Listing 1 A demo program that implements spin controls

//  **************************************************
//  * NAME = SPIN.C                                  *
//  * DEV. PLATFORM= Turbo C++,v3.1 for Windows      *
//  * MEMORY MODEL = SMALL                           *
//  * Demo. program for creating/using spin controls.*
//  * Keith E. Bugg, TRISTAR SYSTEMS, Inc            *
//  **************************************************
#define OEMRESOURCE // MUST define BEFORE <windows.h>
#define DAY_CTRL 100// Ctrl IDs for day editbox ctrl.
#define MONTH_CTRL 200  // ID for month combobox ctrl.
#define YEAR_CTRL 300   // ID for year editbox ctrl.
#include <windows.h>// must have Windows header file
#include <stdio.h>  // get basic prototypes
#include <stdlib.h> // for 'itoa()', etc.

       /* declare prototypes here */
long FAR PASCAL _export SpinProc(HWND, unsigned,
   WORD, LONG);
BOOL FAR PASCAL DateDialog(HWND ,WORD,WORD ,LONG );
//
//      GLOBAL VARIABLES HERE
//
HDC hDC;        // handle to output Display Context
int day,month,year;
char *months[]= {"January","Febuary","March","April",
   "May","June","July","August","September","October",
   "November" ,"December"};
int max_days[]= {31,28,31,30,31,30,31,31.30,31,30,31};
char dayval[3],yearval[5];
//  **************************************************
//  end var. decl., etc.  Now do message handler for
//  the main window of the spin control demo program
//  **************************************************
//
long FAR PASCAL_export SpinProc(HWND hWnd,
   unsigned message,WORD wParam, LONG lParam)
{
   PAINTSTRUCT ps; // Paint Struct for BeginPaint call
   int i;                  // general purpose variable
   HINSTANCE hInstance;
   FARPROC lpfnDlgProc;
   // --------- end local variables ---------------
   //
   switch (message)    // process messages here
   {

      case WM_CLOSE:     // Exit via system menu
         MessageBeep(0);    // Warning beep
         i= MessageBox(hWnd,
         "Are you sure you want to Exit?",
         "EXIT",MB_OKCANCEL | MB_ICONEXCLAMATION);
         if(i == IDOK)      // really wants to exit
         {                  //queue up a QUIT msg
            PostMessage(hWnd,WM_QUIT,0,0); 
            return 0L;
         }
         break;

      case WM_COMMAND:    // check for system message
         switch(wParam)
         {
            case SC_MINIMIZE:   // on minimize
               ShowWindow(hWnd,SW_SHOWMINIMIZED);
               break;

            case SC_MAXIMIZE:   // on maximize
               MessageBeep(0); //stub
               ShowWindow(hWnd,SW_SHOWMAXIMIZED);
               break;

            case SC_RESTORE:    // on restore
               ShowWindow(hWnd,SW_SHOW);
               break;
            //
            //  here is user clicks the sub-menu
            //  option "Run Demo" from drop-down
            case 100:   // Run Demo menu option
               hInstance = GetWindowWord(hWnd, GWW_HINSTANCE);
               lpfnDlgProc= MakeProcInstance(DateDialog,hInstance);
               DialogBox(hInstance,"DIALOG_1", hWnd,lpfnDlgProc);
               break;

            default:
               break;
            }
            break;

      case WM_QUIT:       // QUIT & DESTROY messages
      case WM_DESTROY:
         return (0L);

      default:          // message is of no interest
         return (DefWindowProc(hWnd, message,
            wParam, lParam));
   }
   return (NULL);
}       /* end SpinProc()    */

#pragma argsused        // TC++ compiler directive
//
//  next comes WinMain, which builds demo window
//
// ****************************************************
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE
   hPrevInstance,LPSTR lpCmdLine, int nCmdShow)
{
   MSG msg;            // message
   WNDCLASS wc;
   static HWND hWnd;
   //
   //------------------------------------------------
   //
   
   if (!hPrevInstance) // Other instances running?
   {
      // Fill in window class structure with
      // parameters that describe the main window.
      //
      wc.style = CS_HREDRAW | CS_VREDRAW;
      wc.lpfnWndProc=(long(FAR PASCAL*)())SpinProc;
      wc.cbClsExtra = 0; // No per-class extra data.
      wc.cbWndExtra = 0; // No per-window extra data.
      wc.hInstance = hInstance;
      wc.hIcon = LoadIcon(hInstance, "spin");;
      wc.hCursor = LoadCursor(NULL, IDC_ARROW);
      wc.hbrBackground = CreateSolidBrush(
         RGB(100,255,255)); // cyan background
      wc.lpszMenuName = "MAIN_MENU";
      wc.lpszClassName = "SPIN";

      // Register the window class and return
      // success/failure code.

      if(!RegisterClass(&wc))
         return 0;

      // Create main window for application instance.

      hWnd = CreateWindow(
         "SPIN",       // See RegisterClass() call.
         "Spin Controls",// Text for title bar.
         WS_MAXIMIZE | WS_SYSMENU | WS_MINIMIZEBOX |
         WS_MAXIMIZEBOX | WS_THICKFRAME |
         WS_OVERLAPPEDWINDOW,// Window's styles..
         0,                  // horizontal position.
         0,                  // vertical position.
         600,                // Default width.
         400,                // Default height.
         NULL,               // no parent.
         NULL,               // No menu bar (later)
         hInstance,          // inst. owns window.
         NULL                // Pointer not needed.
         );
      //
      // If CreateWindow failed, return "failure" */
      if (!hWnd)
      {
         MessageBeep(0);
         MessageBox(hWnd,"Could not create window!",
            "ERROR",MB_OK);
         return 0;
      }

      // Make window visible; update its client area

      nCmdShow= SW_SHOWNORMAL;    // Show normal size
      ShowWindow(hWnd,nCmdShow ); // Show the window
      UpdateWindow(hWnd);         // Send WM_PAINT

   }
   // Acquire and dispatch messages until a
   // WM_QUIT message is received.

   while (GetMessage(&msg, // message structure
      NULL,               // window handle rec. msg
      NULL,               // lowest msg. to examine
      NULL))              // highest msg. to examine
   {
      TranslateMessage(&msg); // Translates msgs.
      DispatchMessage(&msg);  // Dispatches msgs.
   }
   return (msg.wParam);     // Returns the value
                        // from PostQuitMessage
}               // end WinMain()

BOOL FAR PASCAL DateDialog(HWND hDlg,WORD wMessage,
   WORD wParam, LONG lParam)
{
   static HWND hDay,hCombo,hYear;
   HANDLE hInstance;   // app. instance
   //
   //  decl. rectangles for bit maps
   //
   static RECT day_up,day_down,year_up, year_down;
   static HDC hMemDC;  // handle to memory DC
   static BITMAP bm;   // for loading bit maps
   static HBITMAP  hUp,hDown;   // bit map handles
   MSG msg;        // message struct. for slewing
   int k, mon_max; // mon_max is max # days in month
   static int bmWidth, bmHeight; // bit map size
   POINT pt;   // for finding mouse click in rects.
   //
   switch(wMessage)
   {
      // initialize dialog box here
      case WM_INITDIALOG:
         hInstance = GetWindowWord(hDlg,
            GWW_HINSTANCE);
         //
         //  create Day edit box as child window
         //
         hDay = CreateWindow("EDIT", NULL, 
                   ES_LEFT | ES_READONLY | WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP,
                   40, 55, 50, 25, hDlg, DAY_CTRL, hInstance, NULL);
         //
         //  create Month combobox as child window
         //
         hCombo = CreateWindow("COMBOBOX",NULL,
                 WS_CHILD | WS_VSCROLL | WS_TABSTOP
                 | CBS_DROPDOWN | CBS_HASSTRINGS,
                 175, 55, 120, 150,hDlg,MONTH_CTRL,
                 hInstance,NULL);
         //
         //  create Year edit box as child window
         //
         hYear = CreateWindow("EDIT",NULL,ES_LEFT |
                ES_READONLY | WS_CHILD | WS_VISIBLE
                | WS_BORDER | WS_TABSTOP,
                310,55,70,25,hDlg,YEAR_CTRL,
                hInstance,NULL);
         ShowWindow(hYear,SW_SHOWNORMAL);
         //
         // init. combobox with names of months
         //
         for(k=0; k < 12; k++)
            SendMessage(hCombo,CB_ADDSTRING,0,(DWORD)(LPSTR)months[k]);

         ShowWindow(hCombo,SW_SHOWNORMAL);
         //
         // Load the Microsoft-supplied arrows here
         //
         hUp = LoadBitmap(NULL,OBM_UPARROW);
         hDown = LoadBitmap(NULL,OBM_DNARROW);
         //
         //  Get the height & width of the up/down
         //  arrows supplied by Microsoft. Use
         //  this to set up rectangles upon which
         //  the arrows will be "pasted". When
         //  user clicks mouse inside one of these
         //  rectangles, we change the day/year
         //
         GetObject(hUp, sizeof(BITMAP), &bm);
         // get size of up/down arrow (same)
         bmWidth = bm.bmWidth;
         bmHeight = bm.bmHeight;
         //
         // memory handle to DC; !** IMPORTANT **!
         //
         hMemDC = CreateCompatibleDC(hDC);
         //
         //  init. the "Day" and "Year" edit boxes
         //  set to date 15-Jan-1993
         //
         day= 15;        // set day to 15
         SetDlgItemText(hDlg,DAY_CTRL,"15");
         //
         //  do month here
         //
         month= 0;       // January
         SendMessage(hCombo,CB_SELECTSTRING,0,
            (DWORD)(LPSTR)months[month]);
         //
         //
         year= 1993;     // set year to 1993

         SetDlgItemText(hDlg,YEAR_CTRL,"1993");
         //
         //  define rect. for the "Day" up arrow
         //  ***********************************
         day_up.left=105;
         day_up.top= 56;
         day_up.right= 105+bmWidth;
         day_up.bottom= day_up.top+bmHeight;
         //
         // now do down arrow for "Day"
         //
         day_down.left=105;
         day_down.top= 74;
         day_down.right= 105+bmWidth;
         day_down.bottom= day_down.top+bmHeight;
         //
         //  define rect. for the "Year" up arrow
         //
         year_up.left= 395;
         year_up.top= 56;
         year_up.right= 395+bmWidth;
         year_up.bottom= year_up.top+bmHeight;
         //
         // do down arrow for "Year"
         //
         year_down.left= 395;
         year_down.top= 74;
         year_down.right= 395+bmWidth;
         year_down.bottom= year_down.top+bmHeight;

         return (TRUE);
         //
         //  do paint message here
         //
      case WM_PAINT:
         //
         // get handle to display context
         //
         hDC= GetDC(hDlg);
         //
         // now "paste" the up/down arrow bitmap
         // into position. Note: the coordinates
         // are hard-coded, not as parameters
         //
         SelectObject(hMemDC,hUp);
         BitBlt(hDC,105,56,bmWidth,bmHeight,hMemDC,
            0,0,SRCCOPY);  // day up
         BitBlt(hDC,395,56,bmWidth,bmHeight,hMemDC,
            0,0,SRCCOPY);  // year up
         //
         SelectObject(hMemDC,hDown);
         BitBlt(hDC,105,74,bmWidth,bmHeight,hMemDC,
            0,0,SRCCOPY);  // day down
         BitBlt(hDC,395,74,bmWidth,bmHeight,hMemDC,
            0,0,SRCCOPY);  // year down

         ReleaseDC(hDlg,hDC);    // release DC
         break;
         //
         // end paint message
         //
      case WM_CLOSE:
         //
         // *** Release memory back to Windows ***
         //
         DeleteDC(hMemDC);
         DeleteObject(hUp);
         DeleteObject(hDown);
         EndDialog(hDlg,0);  // close dialog box
         return (TRUE);
         //
         // user clicked left button here
         //
      case WM_LBUTTONDOWN:
         pt.x = LOWORD(lParam);
         pt.y = HIWORD(lParam);
         //
         //  Was click in day up bit map?
         //
         if(PtInRect(&day_up,pt))
         {
            ++day;  // user wants to increase day
            //
            //  get month, then max days for that
            //  month.check for leap year, too
            //
            month = (WORD) SendMessage(hCombo,
                    CB_GETCURSEL,0,0L);
            mon_max= max_days[month];
            //
            //  check for leap year here
            //
            if(month == 1)  // 0=Jan, 1= Feb, etc
            {
               // need to get year
               if((year % 4 == 0 && year % 100 != 0) || year % 400 ==0)
                  max_days[1]= 29;// lp yr.
            }
            if(day > mon_max)
               day = 1;
            itoa(day,dayval,10);
            //  Update edit box with new day
            SetDlgItemText(hDlg,DAY_CTRL,dayval);
         }
         //
         //  see if click in day down arrow
         //
         if(PtInRect(&day_down,pt))
         {
            --day;  // user wants to decrease day
            if(day < 1)
               day = 1;
            itoa(day,dayval,10);
            //  Update edit box with new day
            SetDlgItemText(hDlg,DAY_CTRL,dayval);
         }
         //
         //  see if click in year up arrow
         //
         if(PtInRect(&year_up,pt))
         {
            ++year; // incr. year & check range
            if(year > 2200)
               year = 2200;
            itoa(year,yearval,10);
            //  Update edit box with new year
            SetDlgItemText(hDlg,YEAR_CTRL,yearval);
         }
         //
         //  see if click in year down arrow
         //
         if(PtInRect(&year_down,pt))
         {
            --year; // decr. year & check range
            if(year < 1800)
               year = 1800;
            itoa(year,yearval,10);
            //  Update edit box with new year
            SetDlgItemText(hDlg,YEAR_CTRL,yearval);
         }
         break;

   case WM_RBUTTONDOWN:   // right button slewing
         pt.x = LOWORD(lParam);
         pt.y = HIWORD(lParam);
         //
         //  Is cursor in year up rectangle?
         //
         if(PtInRect(&year_up,pt))
         {
            while(1)
            {
               if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
               {
                  pt = msg.pt;
                  ScreenToClient(hDlg,&pt);
                  if(!PtInRect(&year_up,pt))
                  {   // user moved out of box
                     break;
                  }
                  if(msg.message== WM_RBUTTONUP)
                     break;
                  else
                  {
                     TranslateMessage(&msg);
                     DispatchMessage(&msg);
                  }
               }
               ++year; // increment year
               if(year > 2200) // check range
                  year = 1993;// default to 1993
               itoa(year,yearval,10);
               //  Update edit box with new year
               SetDlgItemText(hDlg, YEAR_CTRL, yearval);

            }   // end while loop for slewing

         }   // end if slewed year up
         //
         //  now check for slewing year down
         //
         if(PtInRect(&year_down,pt))
         {
            while(1)
            {
               if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
               {
                  pt = msg.pt;
                  ScreenToClient(hDlg,&pt);
                  if(!PtInRect(&year_down,pt))
                  {   // user moved out of box
                     break;
                  }
                  if(msg.message== WN_RBUTTONUP)
                     break;
                  else
                  {
                     TranslateMessage(&msg);
                     DispatchMessage(&msg);
                  }
               }
               --year;     // decrement year
               if(year < 1800) // check range
                  year = 1800;
               itoa(year,yearval,10);
               // Update edit box with new year
               SetDlgItemText(hDlg, YEAR_CTRL, yearval);
            }   // end while loop for slewing down
         }   // end if year down was slewed

         break;
   }           // end switch on messages
   return (FALSE);
}               // end DateDialog()
//
//  end spin.c
//
// End of File