_PROGRAMMING WINDOWS USING STATE TABLES_ by Michael A. Bertrand and William R. Welch [LISTING ONE] /***********DRAW.H : header file for DRAW.C **************************/ #define WAITING 0 /* the possible values for variable iState in */ #define DRAWING 1 /* Tool() are WAITING and DRAWING */ /* These constants are the possible values for iMenuChoice, the variable * recording the user's menu choice. The old menu choice must be stored * so the check mark can be removed from the menu when a new menu choice * is made. Do not change. */ #define IDM_RECT 100 #define IDM_ROUND_RECT 101 #define IDM_ELLIPSE 102 #define IDM_LINE 103 #define IDM_ABOUT 104 /* These constants are the possible values for iFigType, the variable * recording the current FIGURE, as chosen through the menu. The value is * also stored in the iType field in faList[] and is used to determine * which drawing function is called upon from DrawFig[], the array of * pointers to functions; since these values are indices into an array, * starting at 0, they may not be changed. */ #define FT_RECT (IDM_RECT - IDM_RECT) #define FT_ROUND_RECT (IDM_ROUND_RECT - IDM_RECT) #define FT_ELLIPSE (IDM_ELLIPSE - IDM_RECT) #define FT_LINE (IDM_LINE - IDM_RECT) /* maximum number of FIGUREs in faList[] */ #define MAX_FIGS 1000 /* FIGUREs in faList[]: rectangle, rounded rectangle, ellipse, line */ typedef struct { int iType; RECT rsCoord; } FIGURE; /* global variables */ FIGURE faList[MAX_FIGS]; /* List of FIGUREs */ int iListSize; /* tally number of displayed FIGUREs */ HANDLE hInst; /* current instance */ RECT rClient; /* client area in scr coords for ClipCursor() */ /* function prototypes */ long FAR PASCAL WndProc(HWND hWnd, unsigned iMessage, WORD wParam, LONG lParam); void NEAR PASCAL Tool(HWND hWnd, unsigned iMessage, LONG lParam,int iFigType); BOOL FAR PASCAL DrawRoundRect(HDC hDC, int x1, int y1, int x2, int y2); BOOL FAR PASCAL DrawLine(HDC hDC, int x1, int y1, int x2, int y2); BOOL FAR PASCAL AboutDraw(HWND hDlg, unsigned message, WORD wParam, LONG lParam); /* DrawFig[] is an array of pointers to FAR PASCAL functions, each with parms * (HDC,int,int,int,int) and returning BOOL. Note Rectangle() and Ellipse() are * MS Windows GDI calls, while DrawRoundRect() and DrawLine() are our calls. */ BOOL (FAR PASCAL *DrawFig[4])(HDC hDC, int x1, int y1, int x2, int y2) = {Rectangle, DrawRoundRect, Ellipse, DrawLine}; [LISTING TWO] /******* DRAW.C by Michael A. Bertrand and William R. Welch. *******/ #include #include "draw.h" int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) /* hInstance : current instance handle * hPrevInstance : previous instance handle * lpszCmdLine : current command line * nCmdShow : display either window or icon */ { static char szAppName [] = "Draw"; static char szIconName[] = "DrawIcon"; static char szMenuName[] = "DrawMenu"; HWND hWnd; /* handle to WinMain's window */ MSG msg; /* message dispached to window */ WNDCLASS wc; /* for registering window */ /* Save instance handle in global var so can use for "About" dialog box. */ hInst = hInstance; if (!hPrevInstance) /* Register application window class. */ { wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; /* function to get window's messages */ wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, szIconName); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = szMenuName; /* menu resource in RC file */ wc.lpszClassName = szAppName; /* name used in call to CreateWindow() */ if (!RegisterClass(&wc)) return(FALSE); } /* Initialize specific instance. */ hWnd = CreateWindow(szAppName, /* window class */ szAppName, /* window caption */ WS_OVERLAPPEDWINDOW, /* normal window style */ CW_USEDEFAULT, /* initial x-position */ CW_USEDEFAULT, /* initial y-position */ CW_USEDEFAULT, /* initial x-size */ CW_USEDEFAULT, /* initial y-size */ NULL, /* parent window handle */ NULL, /* window menu handle */ hInstance, /* program instance handle */ NULL); /* create parameters */ ShowWindow(hWnd, nCmdShow); /* display the window */ UpdateWindow(hWnd); /* update client area; send WM_PAINT */ /* Read msgs from app que and dispatch them to appropriate win function. * Continues until GetMessage() returns NULL when it receives WM_QUIT. */ while (GetMessage(&msg, NULL, NULL, NULL)) { TranslateMessage(&msg); /* process char input from keyboard */ DispatchMessage(&msg); /* pass message to window function */ } return(msg.wParam); } /****************************************************************/ long FAR PASCAL WndProc(HWND hWnd,unsigned iMessage, WORD wParam, LONG lParam) /* IN: hWnd : handle to window * iMessage : message type * wParam : drawing tool selected from menu (when WM_COMMAND msg) * lParam : mouse coords (x == loword, y == hiword) */ { static int iMenuChoice = IDM_RECT; /* default menu choice */ static int iFigType = FT_RECT; /* default figure type */ HDC hDC; /* must generate our own handle to DC to draw */ HMENU hMenu; /* handle for drop down menu */ PAINTSTRUCT ps; /* needed when receive WM_PAINT message */ int ndx; /* to traverse faList[] when draw it */ FARPROC lpProcAbout; /* pointer to "AboutDraw" function */ POINT pt; /* for ClientToScreen() */ switch(iMessage) { case WM_SIZE: /*convert client coords to scrn coords for ClipCursor()*/ pt.x = pt.y = 0; ClientToScreen(hWnd, &pt); rClient.left = pt.x; rClient.top = pt.y; pt.x = LOWORD(lParam); pt.y = HIWORD(lParam); ClientToScreen(hWnd, &pt); rClient.right = pt.x; rClient.bottom = pt.y; break; case WM_COMMAND: switch(wParam) { case IDM_RECT: case IDM_ROUND_RECT: case IDM_ELLIPSE: case IDM_LINE: /* New FIGURE chosen by user : uncheck old choice and check new * choice on menu; reset iMenuChoice according to user choice. */ hMenu = GetMenu(hWnd); CheckMenuItem(hMenu, iMenuChoice, MF_UNCHECKED); CheckMenuItem(hMenu, iMenuChoice = wParam, MF_CHECKED); /* User has chosen new FIGURE : set iFigType accordingly. */ iFigType = iMenuChoice - IDM_RECT; break; /* case IDM_LINE ... */ case IDM_ABOUT: /* "About" chosen by user : call "AboutDraw" function. */ lpProcAbout = MakeProcInstance(AboutDraw, hInst); DialogBox (hInst, "AboutDraw", hWnd, lpProcAbout); FreeProcInstance(lpProcAbout); break; /* IDM_ABOUT */ } /* switch(wParam) */ break; /* WM_COMMAND */ case WM_LBUTTONDOWN: case WM_MOUSEMOVE: case WM_LBUTTONUP: /* Mouse events passed on to Tool() for processing. */ Tool(hWnd, iMessage, lParam, iFigType); break; /* WM_LBUTTONDOWN... */ case WM_PAINT: /* Repaint window when resized. */ hDC = BeginPaint(hWnd, &ps); /* Draw list of FIGUREs. */ for (ndx = 0; ndx < iListSize; ndx++) DrawFig[faList[ndx].iType](hDC, faList[ndx].rsCoord.left, faList[ndx].rsCoord.top, faList[ndx].rsCoord.right, faList[ndx].rsCoord.bottom); EndPaint(hWnd, &ps); break; /* WM_PAINT */ case WM_DESTROY: /* Destroy window when application terminated. */ PostQuitMessage(0); break; /* WM_DESTROY */ default: return(DefWindowProc(hWnd, iMessage, wParam, lParam)); } /* switch(iMessage) */ return(0L); } /****************************************************************/ void NEAR PASCAL Tool(HWND hWnd, unsigned iMessage, LONG lParam, int iFigType) /* Process mouse event and draw. * IN: hWnd : handle to window * iMessage : mouse event (WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP) * lParam : mouse coords (x == loword, y == hiword) */ { static int x1, y1; /* coordinates of button-down point */ static int x2, y2; /* coordinates of mouse */ static int iState; /* WAITING or DRAWING */ HDC hDC; /* must generate our own handle to DC to draw */ switch(iMessage) { case WM_LBUTTONDOWN: /* Protect array from overflow : if array full, notify and out. */ if (iListSize == MAX_FIGS) { MessageBox(hWnd,"Figure array full","Note",MB_ICONEXCLAMATION|MB_OK); break; /* WM_LBUTTONDOWN */ } /* If not drawing, reset iState and store button-down point. */ if (iState == WAITING) { ClipCursor(&rClient); /* restrict cursor */ iState = DRAWING; /* starting drag */ x1 = x2 = LOWORD(lParam); /* store user point in statics */ y1 = y2 = HIWORD(lParam); } break; /* WM_LBUTTONDOWN */ case WM_MOUSEMOVE: /* If drawing, erase old figure and draw new one at mouse. */ if (iState == DRAWING) { hDC = GetDC(hWnd); SetROP2(hDC, R2_NOTXORPEN); /* draw in XOR */ DrawFig[iFigType](hDC, x1, y1, x2, y2); /* erase old */ x2 = LOWORD(lParam); /* get 2nd user pt */ y2 = HIWORD(lParam); DrawFig[iFigType](hDC, x1, y1, x2, y2); /* draw new */ ReleaseDC(hWnd, hDC); } break; /* WM_MOUSEMOVE */ case WM_LBUTTONUP: /* If drawing, write in COPY mode and store in faList[]. */ if (iState == DRAWING) { ClipCursor(NULL); /* no longer restrict cursor */ iState = WAITING; /* ending draw */ hDC = GetDC(hWnd); SetROP2(hDC, R2_COPYPEN); /* COPY pen for final FIGURE */ DrawFig[iFigType](hDC, x1, y1, x2, y2); /* draw FIGURE */ ReleaseDC(hWnd, hDC); faList[iListSize].iType = iFigType; /* put FIGURE in faList[] */ faList[iListSize].rsCoord.left = x1; faList[iListSize].rsCoord.top = y1; faList[iListSize].rsCoord.right = x2; faList[iListSize].rsCoord.bottom = y2; iListSize++; /* bump tally, since just added figure to list */ } break; /* WM_LBUTTONUP */ } /* switch(iMessage) */ } /****************************************************************/ BOOL FAR PASCAL DrawRoundRect(HDC hDC, int x1, int y1, int x2, int y2) /* IN: hDC : display context in which to draw * x1, y1 : coordinates of first corner * x2, y2 : coordinates of second corner * RET: returns BOOL for consistency with GDI's Rectangle() and Ellipse() * NOTE: GDI's RoundRect() is used to draw, but RoundRect() requires x- * and y-diameters of ellipse used for rounding. This routine sets * the common diameter equal to half the smallest side, then calls * RoundRect(). Array DrawFig[] contains a pointer to this function. */ { int dx, dy; /* sides of rectangle, as positive values */ int diameter; /* diameter of circle used for rounding */ dx = (x1 < x2) ? (x2 - x1) : (x1 - x2); /* x-side of rect (positive) */ dy = (y1 < y2) ? (y2 - y1) : (y1 - y2); /* y-side of rect (positive) */ diameter = (dx < dy) ? dx/2 : dy/2; /* half smallest side */ RoundRect(hDC, x1, y1, x2, y2, diameter, diameter); /* call GDI */ return(TRUE); } /****************************************************************/ BOOL FAR PASCAL DrawLine(HDC hDC, int x1, int y1, int x2, int y2) /* * IN: hDC : display context in which to draw * x1, y1 : coordinates of first endpoint * x2, y2 : coordinates of second endpoint * RET: returns BOOL for consistency w/GDI's Rectangle() and Ellipse(). * NOTE: Array DrawFig[] contains a pointer to this function. */ { MoveTo(hDC, x1, y1); /* MoveTo() and LineTo() are GDI calls. */ LineTo(hDC, x2, y2); return(TRUE); } /****************************************************************/ BOOL FAR PASCAL AboutDraw(HWND hDlg,unsigned iMessage,WORD wParam,LONG lParam) /* Application's "About" dialog box function. * IN: hDlg : handle to dialog box * iMessage : message type * wParam : auxiliary message info (act on IDOK, IDCANCEL) * lParam : unused * RET: Return TRUE if processed appropriate message, FALSE otherwise. */ { switch (iMessage) { case WM_INITDIALOG: /* initialize dialog box */ return (TRUE); case WM_COMMAND: /* received a command */ /* IDOK if OK box selected; IDCANCEL if system menu close command */ if (wParam == IDOK || wParam == IDCANCEL) { EndDialog(hDlg, TRUE); /* exit dialog box */ return(TRUE); /* did proccess message */ } break; /* WM_COMMAND */ } /* switch (iMessage) */ return (FALSE); /* did not process message */ } [LISTING THREE] #*************** DRAW : Make file for DRAW.C *************************** # To make program : NMAKE DRAW # Linker and Resource Compiler: draw.exe depends on draw.obj draw.def draw.res # Linker options as follows : # /A:16 : align on paragraphs # /CO : add symbol information to EXE for CodeView # /NOD : don't search default libs (use only those in link response file) draw.exe: draw.obj draw.def draw.res link /A:16 /CO /NOD draw,,, libw slibcew, draw.def rc draw.res # Microsoft C Compiler : draw.obj contingent on draw.c, draw.h # Compiler options as follows : # -c : compile only # -Gs : remove stack probe before function calls # -Gw : for MS Windows # -Od : disable code optimization to help with debugging # -W3 : highest warning level (flags ANSI incompatibilities) # -AS : small model # -Zp : pack structures (required by MS Windows) # -Zi : add symbol information to OBJ for CodeView draw.obj: draw.c draw.h cl -c -Gsw -Od -W3 -AS -Zpi draw.c # Resource Compiler : draw.res contingent on draw.rc, draw.h draw.res: draw.rc draw.h rc -r -v draw.rc [LISTING FOUR] ;;**************DRAW.DEF : Definition file for DRAW.C************** NAME DRAW DESCRIPTION 'MS Windows Draw Program (c) 1990 M. Bertrand & W. Welch' EXETYPE WINDOWS STUB 'WINSTUB.EXE' CODE MOVEABLE PRELOAD DATA MOVEABLE PRELOAD SINGLE HEAPSIZE 1024 STACKSIZE 4096 EXPORTS WndProc AboutDraw [LISTING FIVE] /************ DRAW.RC : resource file for DRAW.C *******************/ #include "windows.h" #include "draw.h" DrawIcon ICON DRAW.ICO DrawMenu MENU BEGIN POPUP "&Tool" BEGIN MENUITEM "&Rectangle", IDM_RECT, CHECKED MENUITEM "R&ounded rectangle", IDM_ROUND_RECT MENUITEM "&Ellipse", IDM_ELLIPSE MENUITEM "&Line", IDM_LINE MENUITEM Separator MENUITEM "&About Draw...", IDM_ABOUT END END /* "AboutDraw" dialog box contains 3 types of controls : * CTEXT to display centered text at x-coordinates 8, 24, 40, 56 * ICON to display DRAW's icon at coords relative (20,20) * DEFPUSHBUTTON to display 32x14 OK push button at coords (60,74) */ AboutDraw DIALOG 30, 30, 150, 94 CAPTION "About Draw" STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU BEGIN CTEXT "Microsoft Windows" -1, 0, 8, 152, 8 CTEXT "Draw" -1, 0, 24, 152, 8 CTEXT "Copyright (c) 1990" -1, 0, 40, 152, 8 CTEXT "Michael A. Bertrand and William R. Welch" -1, 0, 56, 152, 8 ICON "DrawIcon" -1, 20, 20, 19, 26 DEFPUSHBUTTON "&OK" IDOK, 60, 74, 32, 14, WS_GROUP END