home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / pcmagazi / 1992 / 13 / dlgdemo4.c < prev    next >
Text File  |  1992-04-26  |  38KB  |  991 lines

  1. //
  2. // DLGDEMO4 - Notepad Clone #4 demonstrating use of Win 3.1 Common Dialogs
  3. // Copyright (C) 1992 Ray Duncan
  4. // Ziff Davis Publishing Co. * PC Magazine
  5. //
  6.  
  7. #define WIN31
  8. #define dim(x) (sizeof(x) / sizeof(x[0]))   // returns no. of elements
  9. #define EXENAMESIZE 256                     // max length of path+filename
  10. #define BUFSIZE 65520                       // max length of file data
  11.  
  12. #include "string.h"
  13. #include "windows.h"
  14. #include "commdlg.h"
  15. #include "w31print.h"
  16. #include "dlgdemo4.h"
  17.  
  18. HANDLE hInst;                               // module instance handle
  19. HWND hFrame;                                // handle for frame window
  20. HWND hEdit;                                 // handle for edit window
  21. char szFileName[EXENAMESIZE+1];             // name of current file 
  22. char szTemp[EXENAMESIZE+1];                 // filename scratch buffer
  23. int hFile;                                  // handle for current file
  24. HANDLE hBuff;                               // handle for file I/O buffer
  25. LPSTR lpBuff;                               // far pointer to file buffer
  26. BOOL bCancelPrint = FALSE;                  // TRUE if printing cancelled
  27. BOOL bErrorPrint = FALSE;                   // TRUE if error during print
  28. HWND hCancelPrintDlg = 0;                   // nonmodal dialog window handle
  29. DWORD dwColor = RGB(0, 0, 0);               // user-selected colorref
  30.  
  31.                                             // for FindText & ReplaceText
  32. FINDREPLACE fr;                             // common dialog data structure
  33. HWND hFind = (HWND) 0;                      // nomodal dialog handle
  34. char szFind[256];                           // text to find
  35. char szReplace[256];                        // text to replace
  36.  
  37. char szShortAppName[] = "DlgDemo";          // short application name
  38. char szAppName[] = "Common Dialog Demo #4"; // long application name
  39. char szMenuName[] = "DlgDemoMenu";          // name of menu resource
  40. char szDefName[] = "UNTITLED";              // default filename
  41. char szDefExt[] = "TXT";                    // default extension
  42.  
  43. char *szFilter[] = {                        // filters for Open and
  44.     "ASCII Text (*.TXT)", "*.TXT",          // SaveAs common dialogs
  45.     "All Files (*.*)", "*.*",
  46.     "" };
  47.  
  48. struct decodeWord {                         // structure associates
  49.     WORD Code;                              // messages or menu IDs
  50.     LONG (*Fxn)(HWND, WORD, WORD, LONG); }; // with a function
  51.  
  52. //
  53. // Table of window messages supported by FrameWndProc()
  54. // and the functions which correspond to each message.
  55. //
  56. struct decodeWord messages[] = {
  57.     0, DoFindReplace,
  58.     WM_CREATE, DoCreate,
  59.     WM_INITMENU, DoInitMenu,
  60.     WM_SETFOCUS, DoSetFocus,
  61.     WM_SIZE, DoSize,
  62.     WM_COMMAND, DoCommand,
  63.     WM_DESTROY, DoDestroy, 
  64.     WM_CTLCOLOR, DoSetColor, } ;
  65.  
  66. //
  67. // Table of menubar item IDs and their corresponding functions.
  68. //
  69. struct decodeWord menuitems[] = {
  70.     IDM_NEW, DoMenuNew,
  71.     IDM_OPEN, DoMenuOpen,
  72.     IDM_SAVE, DoMenuSave,
  73.     IDM_SAVEAS, DoMenuSaveAs,
  74.     IDM_PSETUP, DoMenuPrintSetup,
  75.     IDM_PRINT, DoMenuPrint,
  76.     IDM_EXIT, DoMenuExit, 
  77.     IDM_UNDO, DoMenuUndo,
  78.     IDM_CUT, DoMenuCut,
  79.     IDM_COPY, DoMenuCopy,
  80.     IDM_PASTE, DoMenuPaste,
  81.     IDM_DELETE, DoMenuDelete, 
  82.     IDM_FIND, DoMenuFind,
  83.     IDM_REPLACE, DoMenuReplace,
  84.     IDM_FONT, DoMenuFont,
  85.     IDM_COLOR, DoMenuColor, } ;
  86.  
  87. //
  88. // WinMain -- entry point for this application from Windows.
  89. //
  90. int PASCAL WinMain(HANDLE hInstance,
  91.     HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  92. {
  93.     MSG msg;
  94.  
  95.     hInst = hInstance;                      // save this instance handle
  96.  
  97.     if(!hPrevInstance)                      // if first instance,
  98.         if(!InitApplication(hInstance))     // register window class
  99.         {
  100.             MessageBox(hFrame, "Can't initialize application!", szAppName, 
  101.                 MB_ICONSTOP|MB_OK);
  102.             return(FALSE);
  103.         }
  104.  
  105.     if(!InitInstance(hInstance, nCmdShow))  // create this instance's window
  106.     {
  107.         MessageBox(hFrame, "Can't initialize instance!", szAppName, 
  108.             MB_ICONSTOP|MB_OK);
  109.         return(FALSE);
  110.     }
  111.  
  112.     while(GetMessage(&msg, NULL, 0, 0))     // while message != WM_QUIT
  113.     {
  114.         TranslateMessage(&msg);             // translate virtual key codes
  115.         DispatchMessage(&msg);              // dispatch message to window
  116.     }
  117.  
  118.     TermInstance(hInstance);                // clean up for this instance
  119.     return(msg.wParam);                     // return code = WM_QUIT value
  120. }
  121.  
  122. //
  123. // InitApplication --- global initialization code for this application.
  124. //
  125. BOOL InitApplication(HANDLE hInstance)
  126. {
  127.     WNDCLASS  wc;
  128.  
  129.     // set parameters for frame window class
  130.     wc.style = CS_HREDRAW|CS_VREDRAW;       // class style
  131.     wc.lpfnWndProc = FrameWndProc;          // class callback function
  132.     wc.cbClsExtra = 0;                      // extra per-class data
  133.     wc.cbWndExtra = 0;                      // extra per-window data
  134.     wc.hInstance = hInstance;               // handle of class owner
  135.     wc.hIcon = LoadIcon(hInst, "DlgDemoIcon");      // application icon
  136.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);       // default cursor
  137.     wc.hbrBackground = GetStockObject(WHITE_BRUSH); // background color 
  138.     wc.lpszMenuName =  szMenuName;          // name of menu resource
  139.     wc.lpszClassName = szShortAppName;      // name of window class
  140.  
  141.     return(RegisterClass(&wc));             // register class, return status
  142. }
  143.  
  144. //
  145. // InitInstance --- instance initialization code for this application.
  146. //
  147. BOOL InitInstance(HANDLE hInstance, WORD nCmdShow)
  148. {
  149.     hFrame = CreateWindow(                  // create frame window
  150.         szShortAppName,                     // window class name
  151.         szAppName,                          // text for title bar
  152.         WS_OVERLAPPEDWINDOW,                // window style
  153.         CW_USEDEFAULT, CW_USEDEFAULT,       // default position
  154.         CW_USEDEFAULT, CW_USEDEFAULT,       // default size
  155.         NULL,                               // no parent window
  156.         NULL,                               // use class default menu
  157.         hInstance,                          // window owner
  158.         NULL);                              // unused pointer
  159.  
  160.     if(!hFrame) return(FALSE);              // error, can't create window
  161.  
  162.     hBuff = GlobalAlloc(GMEM_MOVEABLE, BUFSIZE);    // allocate memory
  163.     if(!hBuff)                                      // abort if no memory
  164.     {
  165.         MessageBox(hFrame, "Can't allocate memory!", szAppName, 
  166.             MB_ICONSTOP|MB_OK);
  167.         return(0);
  168.     }
  169.  
  170.     lpBuff = GlobalLock(hBuff);             // get far pointer to memory
  171.  
  172.     // register message for FindText() and ReplaceText() common dialogs
  173.     messages[0].Code = RegisterWindowMessage((LPSTR) FINDMSGSTRING);
  174.  
  175.     ShowWindow(hFrame, nCmdShow);           // make frame window visible
  176.     UpdateWindow(hFrame);                   // force WM_PAINT message
  177.     SendMessage(hFrame, WM_COMMAND,         // force new (empty) file by 
  178.         IDM_NEW, 0L);                       // simulating File-New command
  179.     return(TRUE);                           // return success flag
  180. }
  181.  
  182. //
  183. // TermInstance -- instance termination code for this application.
  184. //
  185. BOOL TermInstance(HANDLE hinstance)
  186. {
  187.     GlobalUnlock(hBuff);                    // unlock the memory buffer
  188.     GlobalFree(hBuff);                      // release the buffer
  189.     return(TRUE);                           // return success flag
  190. }
  191.  
  192. //
  193. // FrameWndProc --- callback function for application frame window.
  194. //
  195. LONG FAR PASCAL FrameWndProc(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  196. {
  197.     int i;                                  // scratch variable
  198.  
  199.     for(i = 0; i < dim(messages); i++)      // decode window message and
  200.     {                                       // run corresponding function
  201.         if(wMsg == messages[i].Code)
  202.             return((*messages[i].Fxn)(hWnd, wMsg, wParam, lParam));
  203.     }
  204.  
  205.     return(DefWindowProc(hWnd, wMsg, wParam, lParam));
  206. }
  207.  
  208. //
  209. // DoCreate -- process WM_CREATE message for frame window by
  210. // creating a multiline edit control that will fill the frame window.
  211. // 
  212. LONG DoCreate(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  213. {
  214.     hEdit = CreateWindow("edit",            // class name
  215.                 NULL,                       // text for title bar 
  216.                 WS_CHILD|WS_VISIBLE|WS_HSCROLL|     
  217.                 WS_VSCROLL|WS_BORDER|ES_LEFT|ES_MULTILINE| 
  218.                 ES_AUTOHSCROLL|ES_AUTOVSCROLL,  // window style
  219.                 0, 0,                       // window position
  220.                 0, 0,                       // window size
  221.                 hWnd,                       // parent window
  222.                 IDE_MLE,                    // edit control ID
  223.                 hInst,                      // window owner
  224.                 NULL);                      // unused pointer
  225.     return(0);
  226. }
  227.  
  228. //
  229. // DoSetFocus -- process WM_SETFOCUS message for frame window.
  230. // 
  231. LONG DoSetFocus(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  232. {
  233.     SetFocus(hEdit);                        // toss the focus to 
  234.     return(0);                              // multiline edit control
  235. }
  236.  
  237. //
  238. // DoSize -- process WM_SIZE message for frame window by resizing
  239. // the multiline edit control to completely fill the client area.
  240. // 
  241. LONG DoSize(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  242. {
  243.     MoveWindow(hEdit, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
  244.     return(0);
  245. }
  246.  
  247. //
  248. // DoDestroy -- process WM_DESTROY message for frame window.
  249. // 
  250. LONG DoDestroy(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  251. {
  252.     PostQuitMessage(0);                     // force WM_QUIT message to
  253.     return(0);                              // terminate the event loop
  254. }
  255.  
  256. //
  257. // DoSetColor -- process WM_CTLCOLOR message for edit window.
  258. // 
  259. LONG DoSetColor(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  260. {
  261.     SetTextColor((HDC) wParam, dwColor);    // set text color from results
  262.     return(GetStockObject(WHITE_BRUSH));    // return background brush handle
  263. }
  264.  
  265. //
  266. // DoFindReplace -- process WM_FINDMSGSTRING message from nonmodal
  267. // FindText() AND ReplaceText() common dialogs.  Dispatch DoFind()
  268. // or DoReplace() according to flags in FINDREPLACE structure.
  269. //
  270. LONG DoFindReplace(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  271. {
  272.     if(fr.Flags & FR_DIALOGTERM)            // is dialog being destroyed?
  273.     {
  274.         hFind = 0;                          // yes, reset window handle
  275.         return(0);                          // allowing another dialog
  276.     }                                       // be created
  277.  
  278.     if(fr.Flags & FR_FINDNEXT)              // no, perform find or replace
  279.         return(DoFind(hWnd, wMsg, wParam, lParam));
  280.     else if(fr.Flags & (FR_REPLACE | FR_REPLACEALL))
  281.         return(DoReplace(hWnd, wMsg, wParam, lParam));
  282.     else
  283.         return(DefWindowProc(hWnd, wMsg, wParam, lParam));
  284. }
  285.  
  286. //
  287. // DoFind -- process find command from modeless FindText dialog
  288. //
  289. LONG DoFind(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  290. {
  291.     if(!FindString())                       // search for string
  292.         MessageBox(hFrame,                  // complain if no match
  293.             "Can't find string!", szAppName, MB_ICONSTOP | MB_OK);
  294.     SetFocus(hEdit);                        // restore input focus
  295.     return(0);
  296. }
  297.  
  298. //
  299. // DoReplace -- process replace or replace-all command from 
  300. // modeless FindReplace dialog
  301. //
  302. LONG DoReplace(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  303. {
  304.     int i = 0;                              // replacement counter
  305.     char buff[80];                          // scratch buffer
  306.  
  307.     if(fr.Flags & FR_REPLACEALL)            // replace multiple?
  308.     {
  309.         while(FindString())                 // yes, while another match 
  310.         {                                   // exists, replace it
  311.             SendMessage(hEdit, EM_REPLACESEL, 0, (LPSTR) szReplace);
  312.             i++;                            // and count replacements   
  313.             
  314.         }
  315.  
  316.         wsprintf(buff, "%d replacements made.", i);
  317.         MessageBox(hFrame,                  // show counter to user
  318.                 buff, szAppName, MB_ICONEXCLAMATION | MB_OK);
  319.  
  320.     }
  321.     else                                    // this is single replace
  322.     {
  323.         if(!FindString())                   // search for string
  324.             MessageBox(hFrame,              // complain if no match
  325.                 "Can't find string!", szAppName, MB_ICONSTOP | MB_OK);
  326.         else                                // otherwise replace it
  327.             SendMessage(hEdit, EM_REPLACESEL, 0, (LPSTR) szReplace);
  328.     }
  329.  
  330.     SetFocus(hEdit);                        // restore input focus
  331.     return(0);
  332. }
  333.  
  334. //
  335. // FindString -- search edit control from current selection point for
  336. // the string in szFind[].  If string is present, set selection to include
  337. // match and return TRUE, otherwise return FALSE.
  338. //
  339. BOOL FindString(VOID)
  340. {
  341.     LONG sel;                               // current selection
  342.     int begSel, endSel, cText, cSch;        // scratch variables
  343.     int schDir = 1;                         // search direction
  344.     PSTR pText, pWork;                      // scratch pointers
  345.     HANDLE hText;                           // local heap handle
  346.     int (*matcher)(PSTR, PSTR, WORD);       // pointer to match function
  347.  
  348.     // get handle for memory block, convert to pointer, get text length
  349.     hText = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);
  350.     pText = LocalLock(hText);
  351.     cText = (WORD) SendMessage (hEdit, WM_GETTEXTLENGTH, 0, 0L);
  352.  
  353.     if(cText == 0) return(FALSE);           // bail out if no text at all
  354.  
  355.     // get offset of beginning and end of current selection
  356.     sel = SendMessage(hEdit, EM_GETSEL, 0, 0L);
  357.     begSel = LOWORD(sel);
  358.     endSel = HIWORD(sel);
  359.  
  360.     // set search direction from flags in FINDREPLACE structure
  361.     schDir = fr.Flags & FR_DOWN ? 1 : -1 ;  
  362.  
  363.     // select case-sensitive or case-insensitive match function
  364.     matcher = fr.Flags & FR_MATCHCASE ? strncmp : strnicmp;
  365.  
  366.     // calculate search starting point within text block
  367.     pWork = pText + begSel + schDir;    
  368.  
  369.     // calculate length of text to search
  370.     cSch = schDir<1 ? begSel : max(cText-begSel+1-strlen(szFind), 0);
  371.  
  372.     while(cSch > 0)                         // search until match found
  373.     {                                       // or text is exhausted
  374.         if((*matcher)(pWork, szFind, strlen(szFind)) == 0)
  375.         {
  376.             LocalUnlock(hText);             // found a match, select it
  377.             begSel = pWork - pText;
  378.             endSel = begSel + strlen(szFind);
  379.             SendMessage(hEdit, EM_SETSEL, 0, MAKELONG(begSel, endSel));
  380.             return(TRUE);                   // return success flag
  381.         }
  382.  
  383.         cSch--;                             // no match yet, adjust text
  384.         pWork += schDir;                    // address & length remaining
  385.     }
  386.  
  387.     LocalUnlock(hText);                     // unlock edit control text
  388.     return(FALSE);                          // return match failed flag
  389. }
  390.  
  391. //
  392. // DoInitMenu - initialize the items on menu bar according to the 
  393. // state of the multiline edit control.  If nothing is selected, the 
  394. // edit-cut/copy/delete items are greyed out.  If nothing has 
  395. // been changed since the last file read or write, the file-save item
  396. // is greyed out.  If no text is in the clipboard, the edit-paste
  397. // item is greyed out.
  398. //
  399. LONG DoInitMenu(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  400. {
  401.     LONG selection;
  402.     
  403.     selection = SendMessage(hEdit, EM_GETSEL, 0, 0);
  404.  
  405.     if(HIWORD(selection) != LOWORD(selection))          // set cut/copy/
  406.     {                                                   // delete status
  407.         EnableMenuItem(wParam, IDM_CUT, MF_ENABLED);    
  408.         EnableMenuItem(wParam, IDM_COPY, MF_ENABLED);   
  409.         EnableMenuItem(wParam, IDM_DELETE, MF_ENABLED); 
  410.     }
  411.     else
  412.     {
  413.         EnableMenuItem(wParam, IDM_CUT, MF_GRAYED); 
  414.         EnableMenuItem(wParam, IDM_COPY, MF_GRAYED);    
  415.         EnableMenuItem(wParam, IDM_DELETE, MF_GRAYED);  
  416.     }
  417.  
  418.     if(SendMessage(hEdit, EM_CANUNDO, 0, 0))            // set undo status
  419.         EnableMenuItem(wParam, IDM_UNDO, MF_ENABLED);   
  420.     else
  421.         EnableMenuItem(wParam, IDM_UNDO, MF_GRAYED);    
  422.  
  423.     if(IsClipboardFormatAvailable(CF_TEXT))             // set paste status
  424.         EnableMenuItem(wParam, IDM_PASTE, MF_ENABLED);  
  425.     else
  426.         EnableMenuItem(wParam, IDM_PASTE, MF_GRAYED);   
  427.  
  428.     if(SendMessage(hEdit, EM_GETMODIFY, 0, 0))          // set save status
  429.         EnableMenuItem(wParam, IDM_SAVE, MF_ENABLED);   
  430.     else
  431.         EnableMenuItem(wParam, IDM_SAVE, MF_GRAYED);    
  432.     
  433.     if(hFind)                                           // disable find
  434.     {                                                   // & replace items
  435.         EnableMenuItem(wParam, IDM_FIND, MF_GRAYED);    // if modeless
  436.         EnableMenuItem(wParam, IDM_REPLACE, MF_GRAYED); // dialog is   
  437.     }                                                   // already active
  438.     else
  439.     {                                                   
  440.         EnableMenuItem(wParam, IDM_FIND, MF_ENABLED);    
  441.         EnableMenuItem(wParam, IDM_REPLACE, MF_ENABLED);    
  442.     }
  443.  
  444.     return(0);
  445. }
  446.  
  447. //
  448. // DoCommand -- process WM_COMMAND message for frame window by
  449. // decoding the menubar item with the menuitems[] array, then
  450. // running the corresponding function to process the command.
  451. // 
  452. LONG DoCommand(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  453. {
  454.     int i;                                  // scratch variable
  455.  
  456.     if((wParam == IDE_MLE) && (HIWORD(lParam) == EN_ERRSPACE))
  457.     {
  458.         MessageBox(hWnd, "Out of memory!", "Common Dialog Demo", 
  459.             MB_OK | MB_ICONSTOP);
  460.         return(0);
  461.     }
  462.  
  463.     for(i = 0; i < dim(menuitems); i++)     // decode menu command and
  464.     {                                       // run corresponding function
  465.         if(wParam == menuitems[i].Code)
  466.             return((*menuitems[i].Fxn)(hWnd, wMsg, wParam, lParam));
  467.     }
  468.  
  469.     return(DefWindowProc(hWnd, wMsg, wParam, lParam));
  470. }
  471.  
  472. //
  473. // DoMenuNew -- process File-New command from menu bar.
  474. // 
  475. LONG DoMenuNew(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  476. {
  477.     QueryWriteFile(hEdit);                  // check for dirty buffer
  478.     NewFile(hEdit);                         // empty the text window
  479.     return(0);
  480. }
  481.  
  482. //
  483. // DoMenuOpen -- process File-Open command from menu bar.
  484. //
  485. LONG DoMenuOpen(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  486. {
  487.     OPENFILENAME ofn;                       // used by common dialogs
  488.  
  489.     QueryWriteFile(hEdit);                  // check for dirty buffer
  490.  
  491.     szTemp[0]  = '\0';                      // init filename buffer
  492.  
  493.     ofn.lStructSize = sizeof(OPENFILENAME); // length of structure
  494.     ofn.hwndOwner = hWnd;                   // handle for owner window
  495.     ofn.lpstrFilter = szFilter[0];          // address of filter list
  496.     ofn.lpstrCustomFilter = NULL;           // custom filter buffer address
  497.     ofn.nFilterIndex = 1;                   // use *.TXT filter
  498.     ofn.lpstrFile = szTemp;                 // buffer for path+filename
  499.     ofn.nMaxFile = EXENAMESIZE;             // length of buffer
  500.     ofn.lpstrFileTitle = NULL;              // buffer for filename only
  501.     ofn.lpstrInitialDir = NULL;             // initial directory for dialog
  502.     ofn.lpstrTitle = NULL;                  // title for dialog box
  503.     ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  504.     ofn.lpstrDefExt = NULL;                 // default extension 
  505.  
  506.     if(GetOpenFileName(&ofn))               // display open dialog, 
  507.         ReadFile(hEdit);                    // read data from file
  508.  
  509.     return(0);
  510. }
  511.  
  512. //
  513. // DoMenuSave -- Process File-Save command from menu bar.  If
  514. // filename is default (UNTITLED), ask user for a different one.
  515. //
  516. LONG DoMenuSave(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  517. {
  518.     int hFile;                              // scratch file handle
  519.  
  520.     if(strcmp(szFileName, szDefName) == 0)  // if default name, use SaveAs
  521.         SendMessage(hFrame, WM_COMMAND, IDM_SAVEAS, 0);
  522.     else
  523.         WriteFile(hEdit);                   // otherwise write data to file
  524.  
  525.     return(0);
  526. }
  527.  
  528. //
  529. // DoMenuSaveAs -- process File-SaveAs command from menu bar.
  530. // 
  531. LONG DoMenuSaveAs(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  532. {
  533.     OPENFILENAME ofn;                       // used by common dialogs
  534.  
  535.     strcpy(szTemp, szFileName);             // get default filename
  536.  
  537.     ofn.lStructSize = sizeof(OPENFILENAME); // length of structure
  538.     ofn.hwndOwner = hWnd;                   // handle of owner window
  539.     ofn.lpstrFilter = szFilter[0];          // address of filter list
  540.     ofn.lpstrCustomFilter = NULL;           // custom filter buffer address
  541.     ofn.nFilterIndex = 1L;                  // use *.TXT filter
  542.     ofn.lpstrFile = szTemp;                 // buffer for path+filename
  543.     ofn.nMaxFile = EXENAMESIZE;             // size of buffer
  544.     ofn.lpstrFileTitle = NULL;              // buffer for filename only
  545.     ofn.lpstrInitialDir = NULL;             // initial directory for dialog
  546.     ofn.lpstrTitle = NULL;                  // title for dialog box
  547.     ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_NOTESTFILECREATE;
  548.     ofn.lpstrDefExt = szDefExt;             // default extension
  549.  
  550.     if(GetSaveFileName(&ofn))               // display save-as dialog,
  551.         WriteFile(hEdit);                   // write data to file
  552.  
  553.     return(0);                          
  554. }
  555.  
  556. //
  557. // DoMenuPrintSetup -- process File-PrintSetup command from menu bar.
  558. // 
  559. LONG DoMenuPrintSetup(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  560. {
  561.     PRINTDLG pd;                            // used by common dialog
  562.  
  563.     memset(&pd, 0, sizeof(PRINTDLG));       // zero out entire structure
  564.     pd.lStructSize = sizeof(PRINTDLG);      // length of structure
  565.     pd.hwndOwner = hWnd;                    // owner window
  566.     pd.Flags = PD_PRINTSETUP;               // option flags
  567.  
  568.     PrintDlg(&pd);                          // display print setup
  569.     return(0);                              // common dialog
  570. }
  571.  
  572. //
  573. // DoMenuPrint -- process File-Print command from menu bar.
  574. // 
  575. LONG DoMenuPrint(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  576. {
  577.     PRINTDLG pd;                            // used by common dialog
  578.     FARPROC lpfnAbortPrintProc;             // far pointers to callback
  579.     FARPROC lpfnCancelPrintDlgProc;         // routines
  580.  
  581.  
  582.     memset(&pd, 0, sizeof(PRINTDLG));       // zero out PRINTDLG structure
  583.     pd.lStructSize = sizeof(PRINTDLG);      // length of structure
  584.     pd.hwndOwner = hWnd;                    // owner window
  585.     pd.Flags = PD_RETURNDC | PD_NOPAGENUMS  // option flags
  586.         | PD_NOSELECTION | PD_NOSELECTION | PD_HIDEPRINTTOFILE;
  587.  
  588.     if(PrintDlg(&pd))                       // display print common dialog
  589.     {
  590.         EnableWindow(hFrame, FALSE);        // disable our main window  
  591.  
  592.         lpfnAbortPrintProc =                // create thunks for callbacks
  593.             MakeProcInstance((FARPROC) AbortPrintProc, hInst);
  594.         lpfnCancelPrintDlgProc = 
  595.             MakeProcInstance((FARPROC) CancelPrintDlgProc, hInst);
  596.  
  597.         bCancelPrint = FALSE;               // initialize cancel and
  598.         bErrorPrint = FALSE;                // error printing flags
  599.  
  600.         SetAbortProc(pd.hDC,                // install printer error
  601.             lpfnAbortPrintProc);            // callback routine
  602.  
  603.         hCancelPrintDlg =                   // create modeless cancel dialog
  604.             CreateDialog(hInst, "CancelPrintDlgBox", 
  605.                 hWnd, lpfnCancelPrintDlgProc);
  606.  
  607.         PrintFile(pd.hDC);                  // print text in edit control
  608.  
  609.         if(pd.hDevMode)                     // free DEVMODE memory block
  610.             GlobalFree(pd.hDevMode);        // allocated by common dialog
  611.         if(pd.hDevNames)                    // free DEVNAMES memory block
  612.             GlobalFree(pd.hDevNames);       // allocated by common dialog
  613.  
  614.         DestroyWindow(hCancelPrintDlg);     // destroy modeless dialog
  615.         hCancelPrintDlg = 0;                // and clear its window handle
  616.  
  617.         FreeProcInstance(lpfnAbortPrintProc);   // release callback thunks
  618.         FreeProcInstance(lpfnCancelPrintDlgProc);
  619.  
  620.         DeleteDC(pd.hDC);                   // free printer device context
  621.  
  622.         EnableWindow(hFrame, TRUE);         // re-enable our main window
  623.         SetFocus(hFrame);                   // and give it the focus
  624.     }
  625.  
  626.     return(0);                              
  627. }
  628.  
  629. //
  630. // DoMenuExit -- process File-Exit command from menu bar.
  631. // 
  632. LONG DoMenuExit(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  633. {
  634.     QueryWriteFile(hEdit);                  // check for dirty buffer
  635.     SendMessage (hWnd, WM_CLOSE, 0, 0L);    // send window close message    
  636.     return(0);                              // to shut down the app
  637. }
  638.  
  639. //
  640. // DoMenuUndo -- process Edit-Undo command from menu bar.
  641. // 
  642. LONG DoMenuUndo(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  643. {
  644.     SendMessage(hEdit, WM_UNDO, 0, 0);      // tell the edit control
  645.     return(0);
  646. }
  647.  
  648. //
  649. // DoMenuCut -- process Edit-Cut command from menu bar.
  650. // 
  651. LONG DoMenuCut(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  652. {
  653.     SendMessage(hEdit, WM_CUT, 0, 0);       // tell the edit control
  654.     return(0);
  655. }
  656.  
  657. //
  658. // DoMenuCopy -- process Edit-Copy command from menu bar.
  659. // 
  660. LONG DoMenuCopy(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  661. {
  662.     SendMessage(hEdit, WM_COPY, 0, 0);      // tell the edit control
  663.     return(0);
  664. }
  665.  
  666. //
  667. // DoMenuPaste -- process Edit-Paste command from menu bar.
  668. // 
  669. LONG DoMenuPaste(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  670. {
  671.     SendMessage(hEdit, WM_PASTE, 0, 0);     // tell the edit control
  672.     return(0);
  673. }
  674.  
  675. //
  676. // DoMenuDelete -- process Edit-Delete command from menu bar.
  677. // 
  678. LONG DoMenuDelete(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  679. {
  680.     SendMessage(hEdit, WM_CLEAR, 0, 0);     // tell the edit control
  681.     return(0);
  682. }
  683.  
  684. //
  685. // DoMenuFind -- process Search-Find command from menu bar.
  686. // 
  687. LONG DoMenuFind(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  688. {
  689.     fr.lStructSize = sizeof(FINDREPLACE);   // length of structure
  690.     fr.hwndOwner = hWnd;                    // handle of owner window
  691.     fr.Flags = FR_DOWN | FR_HIDEWHOLEWORD;  // option flags
  692.     fr.lpstrFindWhat = szFind;              // string to find
  693.     fr.wFindWhatLen = sizeof(szFind);       // length of find buffer
  694.  
  695.     hFind = FindText(&fr);                  // activate FindText dialog
  696.     return(0);
  697. }
  698.  
  699. //
  700. // DoMenuReplace -- process Search-Replace command from menu bar.
  701. // 
  702. LONG DoMenuReplace(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  703. {
  704.     fr.lStructSize = sizeof(FINDREPLACE);   // length of structure
  705.     fr.hwndOwner = hWnd;                    // handle of owner window
  706.     fr.Flags = FR_DOWN | FR_HIDEWHOLEWORD;  // option flags
  707.     fr.lpstrFindWhat = szFind;              // string to find
  708.     fr.wFindWhatLen = sizeof(szFind);       // length of find buffer
  709.     fr.lpstrReplaceWith = szReplace;        // replacement string
  710.     fr.wReplaceWithLen = sizeof(szReplace); // length of replace buffer
  711.  
  712.     hFind = ReplaceText(&fr);               // activate ReplaceText dialog
  713.     return(0);
  714. }
  715.  
  716. //
  717. // DoMenuFont -- process Font command from menu bar.
  718. // 
  719. LONG DoMenuFont(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  720. {
  721.     LOGFONT lf;                             // logical font data structure
  722.     CHOOSEFONT chf;                         // font dialog data structure
  723.     HFONT hfont;                            // logical font handle
  724.  
  725.     chf.lStructSize = sizeof(CHOOSEFONT);   // size of structure    
  726.     chf.hwndOwner = hWnd;                   // window handle of owner
  727.     chf.lpLogFont = &lf;                    // logical font structure
  728.     chf.Flags = CF_SCREENFONTS|CF_EFFECTS;  // option flags
  729.     chf.rgbColors = RGB(0,0,0);             // default color = black
  730.  
  731.     if(ChooseFont(&chf))                    // display ChooseFont dialog    
  732.     {
  733.         hfont = CreateFontIndirect(&lf);    // save font handle, then send 
  734.         SendMessage(hEdit, WM_SETFONT, hfont, TRUE);  // it to edit control
  735.     }
  736.  
  737.     return(0);
  738. }
  739.  
  740. //
  741. // DoMenuColor -- process Color command from menu bar.
  742. // 
  743. LONG DoMenuColor(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  744. {
  745.     CHOOSECOLOR chc;                        // color dialog data structure
  746.     DWORD dwCustColors[16];                 // array of custom colorrefs
  747.     int i;                                  // scratch variable
  748.  
  749.     for(i = 0; i < 16; i++)                 // init custom colors to white
  750.         dwCustColors[i] = RGB(255, 255, 255);
  751.  
  752.     chc.lStructSize = sizeof(CHOOSECOLOR);  // size of structure
  753.     chc.hwndOwner = hWnd;                   // window handle of owner
  754.     chc.rgbResult = dwColor;                // initial color to select
  755.     chc.lpCustColors = dwCustColors;        // addr of custom color array
  756.     chc.Flags = CC_FULLOPEN | CC_RGBINIT;   // option flags
  757.  
  758.     if(ChooseColor(&chc))                   // display ChooseColor dialog
  759.     {
  760.         dwColor = chc.rgbResult;            // save new text color
  761.         InvalidateRect(hEdit, NULL, TRUE);  // then force edit window to
  762.         UpdateWindow(hEdit);                // be redrawn with new color
  763.     }
  764.  
  765.     return(0);
  766. }
  767.  
  768. //
  769. // NewFile -- set empty text window with default filename.
  770. //
  771. VOID NewFile(HANDLE hEdit)
  772. {
  773.     lpBuff[0] = '\0';                       // empty the edit control
  774.     SetWindowText(hEdit, lpBuff);           // put text into window
  775.     strcpy(szFileName, szDefName);          // set default filename
  776.     SetWindowCaption(szFileName);           // update title bar
  777.     SendMessage(hEdit, EM_SETMODIFY, 0, 0); // clear change flag
  778. }
  779.  
  780. //
  781. // ReadFile -- read text from specified file to window, close file.
  782. //
  783. VOID ReadFile(HANDLE hEdit)
  784. {
  785.     int length;                             // scratch variable
  786.     int hFile;                              // scratch file handle
  787.  
  788.     hFile = _lopen(szTemp, OF_READ);        // try and open the file
  789.     if(hFile == -1)                         // bail out if no such file
  790.     {
  791.         MessageBox(hFrame, "Can't open file!", szAppName, MB_ICONSTOP|MB_OK);
  792.         return;
  793.     }
  794.  
  795.     strcpy(szFileName, szTemp);             // save new filename
  796.     length = _lread(hFile, lpBuff, BUFSIZE);    // read the file
  797.     lpBuff[length] = '\0';                  // make text ASCIIZ
  798.     SetWindowText(hEdit, lpBuff);           // put text into window
  799.     _lclose(hFile);                         // close the file
  800.     SetWindowCaption(szFileName);           // update title bar
  801.     SendMessage(hEdit, EM_SETMODIFY, 0, 0); // clear change flag
  802. }
  803.  
  804. //
  805. // WriteFile -- write text from specified window to file, close file.
  806. //
  807. VOID WriteFile(HANDLE hEdit)
  808. {
  809.     int length;                             // scratch variable
  810.     int hFile;                              // scratch file handle
  811.  
  812.     hFile = _lcreat(szTemp, 0);             // try and create the file
  813.     if(hFile == -1)                         // bail out if create failed
  814.     {
  815.         MessageBox(hFrame, "Can't create file!", szAppName, 
  816.             MB_ICONSTOP|MB_OK);
  817.         return;
  818.     }
  819.  
  820.     strcpy(szFileName, szTemp);             // save new filename
  821.     length = GetWindowTextLength(hEdit);    // chars in edit control
  822.     GetWindowText(hEdit, lpBuff, length+1); // retrieve text
  823.     _lwrite(hFile, lpBuff, length);         // write text to file
  824.     _lclose(hFile);                         // close the file
  825.     SetWindowCaption(szFileName);           // update title bar
  826.     SendMessage(hEdit, EM_SETMODIFY, 0, 0); // clear change flag
  827. }
  828.  
  829. //
  830. // QueryWriteFile -- check if buffer has been changed, and if
  831. // so prompt the user to write the file to disk.  
  832. //
  833. VOID QueryWriteFile(HANDLE hEdit)
  834. {
  835.     if(SendMessage(hEdit, EM_GETMODIFY, 0, 0))  // was buffer changed?
  836.     {
  837.         if(MessageBox(hFrame, "File has been changed.  Write file?", 
  838.             szAppName, MB_ICONQUESTION|MB_YESNO) == IDYES)
  839.         {
  840.             SendMessage(hFrame, WM_COMMAND, IDM_SAVEAS, 0);
  841.         }
  842.     }
  843. }
  844.  
  845. //
  846. // SetWindowCaption -- concatenate the filename with the application
  847. // name, then update the frame window's title bar.
  848. //
  849. VOID SetWindowCaption(char * szFilename)
  850. {
  851.     char szTemp[EXENAMESIZE+1];             // filename scratch buffer
  852.  
  853.     strcpy(szTemp, szAppName);              // get application name
  854.     strcat(szTemp, " - ");                  // add separator
  855.     strcat(szTemp, szFileName);             // add filename
  856.     SetWindowText(hFrame, szTemp);          // put result into title bar
  857. }
  858.  
  859. //
  860. // PrintFile - print contents of edit window, using printer device
  861. // context supplied by caller.
  862. //
  863. VOID PrintFile(HDC hdc)
  864. {
  865.     WORD cLine, iLine;                      // line length, index
  866.     HANDLE hText;                           // local heap handle
  867.     PSTR pText;                             // scratch pointer
  868.     WORD maxLine, curLine;                  // line counters
  869.     WORD curY;                              // current Y coordinate
  870.     HFONT hFont;                            // non-prop. font handle
  871.     int charY, pageX, pageY;                // char & page Y dimensions
  872.     int horzMarg, vertMarg;                 // page margins
  873.     TEXTMETRIC tm;                          // info about font
  874.     DOCINFO di;                             // used by StartDoc
  875.  
  876.     // initialize document info structure for StartDoc
  877.     di.cbSize = sizeof(DOCINFO);
  878.     di.lpszDocName = szFileName;
  879.     di.lpszOutput = NULL;
  880.  
  881.     // get memory block handle, convert to pointer, get number of lines
  882.     hText = (HANDLE) SendMessage(hEdit, EM_GETHANDLE, 0, 0L);
  883.     pText = LocalLock(hText);
  884.     maxLine = (WORD) SendMessage(hEdit, EM_GETLINECOUNT, 0, 0);
  885.  
  886.     if(maxLine == 0)                        // if there's nothing to
  887.     {                                       // print, unlock memory
  888.         LocalUnlock(hText);                 // block and bail out now
  889.         return;
  890.     }
  891.  
  892.     GetTextMetrics(hdc, &tm);               // get various dimensions
  893.     charY = tm.tmHeight + tm.tmExternalLeading;
  894.     pageY = GetDeviceCaps(hdc, VERTRES);
  895.     horzMarg = GetDeviceCaps(hdc, LOGPIXELSX) / 2;
  896.     curY = vertMarg = GetDeviceCaps(hdc, LOGPIXELSY) / 2;
  897.  
  898.     StartDoc(hdc, &di);                     // begin the print job
  899.     StartPage(hdc);
  900.  
  901.     curLine = 0;
  902.  
  903.     // print contents of edit control, line by line
  904.     while((curLine < maxLine) && (bErrorPrint == 0) && (bCancelPrint == 0))
  905.     {
  906.         // get character index and length for current line
  907.         iLine = (WORD) SendMessage(hEdit, EM_LINEINDEX, curLine, 0);
  908.         cLine = (WORD) SendMessage(hEdit, EM_LINELENGTH, iLine, 0);
  909.  
  910.         // now draw the current line using printer device context
  911.         TextOut(hdc, horzMarg, curY, &pText[iLine], cLine);
  912.         curLine++;                  
  913.  
  914.         curY += charY;                      // advance down page
  915.  
  916.         if(curY >= pageY - vertMarg)        // reached end of page?
  917.         {
  918.             curY = vertMarg;                // yes, reset Y coordinate
  919.             EndPage(hdc);                   // and force new page
  920.             StartPage(hdc);
  921.         }
  922.     }
  923.  
  924.     if(bErrorPrint || bCancelPrint)
  925.         AbortDoc(hdc);
  926.     else
  927.     {
  928.         EndPage(hdc);                       // send final new-page
  929.         EndDoc(hdc);                        // and end the print job
  930.     }
  931.  
  932.     LocalUnlock(hText);                     // unlock edit control 
  933.     return;                                 // memory block & return
  934. }
  935.  
  936. //
  937. // CancelPrintDlgProc - this is the callback routine for the
  938. // modeless Cancel Print dialog box.  We just look for clicks on the
  939. // cancel button and ignore everything else.
  940. //
  941. LONG FAR PASCAL CancelPrintDlgProc(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  942. {
  943.     if(wMsg == WM_COMMAND)                  // button clicked?
  944.     {
  945.         bCancelPrint = TRUE;                // yes, set cancel flag
  946.         return(TRUE);
  947.     }
  948.  
  949.     else if(wMsg == WM_INITDIALOG)          // is this dialog creation?
  950.     {                                       // yes, initialize cancel flag
  951.         bCancelPrint = FALSE;
  952.         SetFocus(GetDlgItem(hWnd, IDD_CANCEL));
  953.         return(TRUE);
  954.     }
  955.  
  956.     else return(FALSE);
  957. }
  958.  
  959. //
  960. // AbortPrintProc - this is the callback routine, installed by DoMenuPrint
  961. // using SetAbortProc, that is entered from Windows at various times during
  962. // printing.  If the ErrorCode parameter is nonzero, a printing error has
  963. // occurred, and the routine sets a flag which is checked by PrintFile.
  964. // The routine also processes messages from the message queue so that the
  965. // user's click of the Cancel button will not go unnoticed.
  966. // 
  967. int FAR PASCAL AbortPrintProc(HDC hdc, int ErrorCode)
  968. {
  969.     MSG msg;
  970.  
  971.     if(ErrorCode)                           // if error code is nonzero,
  972.     {
  973.         bErrorPrint = TRUE;                 // set flag to abort printing
  974.         return(FALSE);                      // abort the print job
  975.     }
  976.  
  977.     // otherwise inspect the queue messages in case something
  978.     // is waiting from the nonmodal Cancel Print dialog
  979.     while(!bCancelPrint && PeekMessage(&msg, NULL, 0, 0, TRUE))
  980.     {
  981.         if(!hCancelPrintDlg || IsDialogMessage(hCancelPrintDlg, &msg))
  982.         {
  983.             TranslateMessage(&msg);
  984.             DispatchMessage(&msg);
  985.         }
  986.     }
  987.  
  988.     return(!bCancelPrint);                  // return TRUE to continue
  989. }                                           // printing, FALSE to abort
  990.  
  991.