home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / winnt / dlgedit / dlgedit.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  38KB  |  1,297 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /****************************** Module Header *******************************
  13. * Module Name: dlgedit.c
  14. *
  15. * Main function and window procedure for the Dialog Box Editor.
  16. *
  17. * Functions:
  18. *
  19. *   MainWndProc()
  20. *   ReadWindowPos()
  21. *   WriteWindowPos()
  22. *   InitApplication()
  23. *   InitInstance()
  24. *   PenWinRegister()
  25. *   GetSystemValues()
  26. *   ReadEnv()
  27. *   WriteEnv()
  28. *   LoadSysColorBitmaps()
  29. *   LoadAlterBitmap()
  30. *   RGBInvertRGB()
  31. *   SizeRibbons()
  32. *   DialogTerminate()
  33. *
  34. * Comments:
  35. *
  36. * Because of the need for a dialog in both work and test mode to be
  37. * shown relative to the client area of its parent, and because the
  38. * editor has a ribbon control along the top of its client area, there
  39. * needed to be another window created that will be the actual parent
  40. * of the dialog being edited.  This window, called the ghwndSubClient
  41. * window, is sized to be the size of the editors client area minus
  42. * the height of the ribbon window at the top.  This makes it so that
  43. * a dialog that has an origin of 0,0 will have the top edge of its
  44. * client area just below the bottom of the ribbon window in the
  45. * editor.  This window does not need any special processing.  It simply
  46. * paints its background with the app workspace color, and is used as
  47. * the basis for coordinate conversion for the dialog.
  48. *
  49. ****************************************************************************/
  50.  
  51. #include "dlgedit.h"
  52. #include "dlgfuncs.h"
  53. #include "dlgextrn.h"
  54. #include "dialogs.h"
  55.  
  56. #include <commdlg.h>
  57.  
  58. #include <stdlib.h>
  59. #include <string.h>
  60. #if defined(DBCS) && !defined(UNICODE)
  61. #define _MBCS
  62. #include <mbstring.h>
  63. #define strtok      _mbstok
  64. #endif
  65.  
  66. STATICFN BOOL InitApplication(HANDLE hInstance);
  67. STATICFN BOOL InitInstance(HANDLE hInstance, INT nCmdShow);
  68. STATICFN VOID PenWinRegister(VOID);
  69. STATICFN VOID GetSystemValues(VOID);
  70. STATICFN VOID ReadEnv(VOID);
  71. STATICFN VOID WriteEnv(VOID);
  72. STATICFN VOID LoadSysColorBitmaps(VOID);
  73. STATICFN HBITMAP LoadAlterBitmap(INT idbm, DWORD rgbNew, DWORD rgbNew2);
  74. STATICFN DWORD RGBInvertRGB(DWORD rgb);
  75. STATICFN VOID SizeRibbons(HWND hwnd);
  76. STATICFN VOID DialogTerminate(VOID);
  77.  
  78. static RECT grcAppPos;              // Saves the app's window pos.
  79. static UINT gmsgHelp;               // Registered help message from commdlg.
  80. static BOOL fStartAsIcon = FALSE;   // TRUE if app is started minimized.
  81.  
  82. /*
  83.  * Contains the address of the Pen Windows callback.
  84.  */
  85. typedef VOID ( APIENTRY *LPFNPENWIN)(WORD, BOOL);
  86. static LPFNPENWIN lpfnRegisterPenApp;
  87.  
  88.  
  89.  
  90. /************************************************************************
  91. * WinMain
  92. *
  93. * This is the main function for the dialog editor.
  94. *
  95. ************************************************************************/
  96.  
  97. INT WINAPI WinMain(
  98.     HINSTANCE hInstance,
  99.     HINSTANCE hPrevInstance,
  100.     LPSTR lpCmdLine,
  101.     INT nCmdShow)
  102. {
  103.     MSG msg;
  104.  
  105.     if (!hPrevInstance) {
  106.         if (!InitApplication(hInstance)) {
  107.             Message(MSG_NOINIT);
  108.             return FALSE;
  109.         }
  110.     }
  111.  
  112.     if (!InitInstance(hInstance, nCmdShow)) {
  113.         Message(MSG_NOINIT);
  114.         return FALSE;
  115.     }
  116.  
  117.     while (GetMessage(&msg, NULL, 0, 0)) {
  118.         if (!ghwndTestDlg || !IsDialogMessage(ghwndTestDlg, &msg)) {
  119.             if (!hwndStatus || !IsDialogMessage(hwndStatus, &msg)) {
  120.                 if (!TranslateAccelerator(ghwndMain, ghAccTable, &msg)) {
  121.                     TranslateMessage(&msg);
  122.                     DispatchMessage(&msg);
  123.                 }
  124.             }
  125.         }
  126.     }
  127.  
  128.     DialogTerminate();
  129.  
  130.     /*
  131.      * Return the value from PostQuitMessage.
  132.      */
  133.     return msg.wParam;
  134. }
  135.  
  136.  
  137.  
  138. /************************************************************************
  139. * InitApplication
  140. *
  141. * Registers the window classes.
  142. *
  143. * Arguments:
  144. *   HANDLE hInstance - Instance handle from WinMain.
  145. *
  146. * Returns:
  147. *   TRUE if all of the window classes were created; otherwise, FALSE.
  148. *
  149. ************************************************************************/
  150.  
  151. STATICFN BOOL InitApplication(
  152.     HANDLE hInstance)
  153. {
  154.     WNDCLASS wc;
  155.  
  156.     wc.style = CS_DBLCLKS;
  157.     wc.lpfnWndProc = MainWndProc;
  158.     wc.cbClsExtra = 0;
  159.     wc.cbWndExtra = sizeof(DWORD);
  160.     wc.hInstance = hInstance;
  161.     wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDICON_DLGEDIT));
  162.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  163.     wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
  164.     wc.lpszMenuName = MAKEINTRESOURCE(IDMENU_MAIN);
  165.     wc.lpszClassName = szMainClass;
  166.     if (!RegisterClass(&wc))
  167.         return FALSE;
  168.  
  169.     wc.style = 0;
  170.     wc.lpfnWndProc = DefWindowProc;
  171.     wc.cbClsExtra = 0;
  172.     wc.cbWndExtra = 0;
  173.     wc.hInstance = hInstance;
  174.     wc.hIcon = NULL;
  175.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  176.     wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
  177.     wc.lpszMenuName = NULL;
  178.     wc.lpszClassName = szSubClientClass;
  179.     if (!RegisterClass(&wc))
  180.         return FALSE;
  181.  
  182.     wc.style = CS_DBLCLKS;
  183.     wc.lpfnWndProc = DragWndProc;
  184.     wc.cbClsExtra = 0;
  185.     wc.cbWndExtra = sizeof(DWORD);
  186.     wc.hInstance = hInstance;
  187.     wc.hIcon = NULL;
  188.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  189.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  190.     wc.lpszMenuName = NULL;
  191.     wc.lpszClassName = szDragClass;
  192.     if (!RegisterClass(&wc))
  193.         return FALSE;
  194.  
  195.     wc.style = 0;
  196.     wc.lpfnWndProc = ToolboxWndProc;
  197.     wc.cbClsExtra = 0;
  198.     wc.cbWndExtra = 0;
  199.     wc.hInstance = hInstance;
  200.     wc.hIcon = NULL;
  201.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  202.     wc.hbrBackground = GetStockObject(LTGRAY_BRUSH);
  203.     wc.lpszMenuName = NULL;
  204.     wc.lpszClassName = szToolboxClass;
  205.     if (!RegisterClass(&wc))
  206.         return FALSE;
  207.  
  208.     wc.style = 0;
  209.     wc.lpfnWndProc = ToolBtnWndProc;
  210.     wc.cbClsExtra = 0;
  211.     wc.cbWndExtra = 0;
  212.     wc.hInstance = hInstance;
  213.     wc.hIcon = NULL;
  214.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  215.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  216.     wc.lpszMenuName = NULL;
  217.     wc.lpszClassName = szToolBtnClass;
  218.     if (!RegisterClass(&wc))
  219.         return FALSE;
  220.  
  221.     wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  222.     wc.lpfnWndProc = CustomWndProc;
  223.     wc.cbClsExtra = 0;
  224.     wc.cbWndExtra = 0;
  225.     wc.hInstance = hInstance;
  226.     wc.hIcon = NULL;
  227.     wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  228.     wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  229.     wc.lpszMenuName = NULL;
  230.     wc.lpszClassName = szCustomClass;
  231.     if (!RegisterClass(&wc))
  232.         return FALSE;
  233.  
  234.     return TRUE;
  235. }
  236.  
  237.  
  238.  
  239. /************************************************************************
  240. * InitInstance
  241. *
  242. * Initializes the dialog editor by loading resources, etc.
  243. *
  244. * Arguments:
  245. *   HANDLE hInstance - Instance handle from WinMain.
  246. *   int nCmdShow     - Show command from WinMain.
  247. *
  248. * Returns:
  249. *   FALSE if any errors occurred during initialization
  250. *
  251. ************************************************************************/
  252.  
  253. STATICFN BOOL InitInstance(
  254.     HANDLE hInstance,
  255.     INT nCmdShow)
  256. {
  257.     HDC hDC;
  258.     TEXTMETRIC tm;
  259.     INT x;
  260.     INT y;
  261.     INT cx;
  262.     INT cy;
  263.     BOOL fMaximized;
  264.     INT i;
  265.     TCHAR szArg1[CCHTEXTMAX];
  266.  
  267.     ghInst = hInstance;
  268.  
  269.     /*
  270.      * We need a mouse - make sure we have one.
  271.      */
  272.     if (!GetSystemMetrics(SM_MOUSEPRESENT)) {
  273.         Message(MSG_NOMOUSE);
  274.         return FALSE;
  275.     }
  276.  
  277.     /*
  278.      * Register for Pen Windows, if it is present.
  279.      */
  280.     PenWinRegister();
  281.  
  282.     ghAccTable = LoadAccelerators(ghInst, MAKEINTRESOURCE(IDACCEL_MAIN));
  283.  
  284.     /*
  285.      * Create a dark gray pen for use in borders later.
  286.      */
  287.     if (!(hpenDarkGray = CreatePen(PS_SOLID, 1, DARKGRAY)))
  288.         return FALSE;
  289.  
  290.     /*
  291.      * Get some system constants.
  292.      */
  293.     GetSystemValues();
  294.  
  295.     /*
  296.      * Note that this must be done instead of using the text metrics,
  297.      * because Windows internally generates a better average value for
  298.      * proportional fonts, and we must match it or our dialogs will
  299.      * be out of proportion.
  300.      */
  301.     gcxSysChar = LOWORD(GetDialogBaseUnits());
  302.     gcySysChar = HIWORD(GetDialogBaseUnits());
  303.  
  304.     /*
  305.      * Because some useful worker routines like WinToDUPoint use
  306.      * the values in gcd.c*Char, set them to be the default font right
  307.      * away.  When a dialog is loaded with a different font, they
  308.      * will be modified.
  309.      */
  310.     gcd.cxChar = gcxSysChar;
  311.     gcd.cyChar = gcySysChar;
  312.  
  313.     /*
  314.      * Build the help file name path.  Assume the help file is in the
  315.      * same directory as the executable.
  316.      */
  317.     GetModuleFileName(ghInst, gszHelpFile, CCHMAXPATH);
  318.     *FileInPath(gszHelpFile) = CHAR_NULL;
  319.     lstrcat(gszHelpFile, ids(IDS_HELPFILE));
  320.  
  321.     /*
  322.      * Register the message for help from the common dialogs.
  323.      */
  324.     gmsgHelp = RegisterWindowMessage(HELPMSGSTRING);
  325.  
  326.     /*
  327.      * Hook the message filter stream so that we can detect F1 keystrokes.
  328.      */
  329.     ghhkMsgFilter = SetWindowsHook(WH_MSGFILTER, (HOOKPROC)MsgFilterHookFunc);
  330.  
  331.     /*
  332.      * Read the last position for the app.
  333.      */
  334.     if (!ReadWindowPos(szAppPos, &x, &y, &cx, &cy, &fMaximized)) {
  335.         x = CW_USEDEFAULT;
  336.         y = CW_USEDEFAULT;
  337.         cx = CW_USEDEFAULT;
  338.         cy = CW_USEDEFAULT;
  339.         fMaximized = FALSE;
  340.     }
  341.  
  342.     /*
  343.      * Create the main window.
  344.      */
  345.     if (!(ghwndMain = CreateWindow(szMainClass, NULL,
  346.             WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  347.             x, y, cx, cy, NULL, NULL, hInstance, NULL)))
  348.         return FALSE;
  349.  
  350.     ShowFileStatus(TRUE);
  351.  
  352.     /*
  353.      * Read the Preferences data.
  354.      */
  355.     ReadEnv();
  356.  
  357.     /*
  358.      * If the app was saved when maximized (and they didn't start it up
  359.      * with some kind of an option to have it minimized or in some
  360.      * other funny initial state from the shell), then cause it to
  361.      * be maximized when shown.
  362.      */
  363.     if (fMaximized && (nCmdShow == SW_SHOWNORMAL || nCmdShow == SW_SHOW))
  364.         nCmdShow = SW_SHOWMAXIMIZED;
  365.  
  366.     ShowWindow(ghwndMain, nCmdShow);
  367.     UpdateWindow(ghwndMain);
  368.  
  369.     /*
  370.      * Did the user start this app minimized from the program manager?
  371.      */
  372.     if (IsIconic(ghwndMain)) {
  373.         /*
  374.          * Set a flag.  The showing of the toolbox will be deferred
  375.          * until the app is restored.
  376.          */
  377.         fStartAsIcon = TRUE;
  378.     }
  379.     else {
  380.         /*
  381.          * If they had the Toolbox before, show it now.
  382.          */
  383.         if (gfShowToolbox)
  384.             ToolboxShow(TRUE);
  385.     }
  386.  
  387.     hcurArrow = LoadCursor(NULL, IDC_ARROW);
  388.     hcurWait = LoadCursor(NULL, IDC_WAIT);
  389.     hcurOutSel = LoadCursor(ghInst, MAKEINTRESOURCE(IDCUR_OUTSEL));
  390.     hcurMove = LoadCursor(ghInst, MAKEINTRESOURCE(IDCUR_MOVE));
  391.     hcurInsert = LoadCursor(ghInst, MAKEINTRESOURCE(IDCUR_INSERT));
  392.     hcurDropTool = LoadCursor(ghInst, MAKEINTRESOURCE(IDCUR_DROPTOOL));
  393.     hcurSizeNESW = LoadCursor(NULL, IDC_SIZENESW);
  394.     hcurSizeNS = LoadCursor(NULL, IDC_SIZENS);
  395.     hcurSizeNWSE = LoadCursor(NULL, IDC_SIZENWSE);
  396.     hcurSizeWE = LoadCursor(NULL, IDC_SIZEWE);
  397.  
  398.     if (!hcurArrow ||
  399.             !hcurWait ||
  400.             !hcurOutSel ||
  401.             !hcurMove ||
  402.             !hcurDropTool ||
  403.             !hcurInsert)
  404.         return FALSE;
  405.  
  406.     if ((hDC = GetDC(ghwndMain)) == NULL)
  407.         return FALSE;
  408.  
  409.     GetTextMetrics(hDC, &tm);
  410.  
  411.     gcyPixelsPerInch = GetDeviceCaps(hDC, LOGPIXELSY);
  412.  
  413.     /*
  414.      * Create a memory DC for drawing bitmaps.
  415.      */
  416.     ghDCMem = CreateCompatibleDC(hDC);
  417.  
  418.     ReleaseDC(ghwndMain, hDC);
  419.  
  420.     /*
  421.      * Load the bitmaps that depend on system colors.
  422.      */
  423.     LoadSysColorBitmaps();
  424.  
  425.     fmtDlg = RegisterClipboardFormat(L"DIALOG");
  426.  
  427.     /*
  428.      * Initialize the icon control ordinal to the icon id from our exe
  429.      * that we will use to show these kind of controls.
  430.      */
  431.     WriteOrd(&gordIcon, IDICON_ICON);
  432.  
  433.     /*
  434.      * Initialize the default text fields in the awcd array.  Because
  435.      * CCONTROLS does not include the dialog type, it has to be done
  436.      * separately.
  437.      */
  438.     awcd[W_DIALOG].pszTextDefault = ids(awcd[W_DIALOG].idsTextDefault);
  439.     for (i = 0; i < CCONTROLS; i++)
  440.         awcd[i].pszTextDefault = ids(awcd[i].idsTextDefault);
  441.  
  442.     /*
  443.      * If there was a command line argument specified, try and open
  444.      * it as the initial file.
  445.      */
  446.     if (__argc > 1) {
  447.         MultiByteToWideChar(CP_ACP, 0, __argv[1], -1, szArg1, CCHTEXTMAX);
  448.         OpenCmdLineFile(szArg1);
  449.     }
  450.  
  451.     /*
  452.      * Be sure the focus is on the main window.  This corrects a
  453.      * problem where the accelerators don't initially work because
  454.      * the focus gets placed on the Properties Bar.
  455.      */
  456.     SetFocus(ghwndMain);
  457.  
  458.     return TRUE;
  459. }
  460.  
  461.  
  462.  
  463. /************************************************************************
  464. * PenWinRegister
  465. *
  466. * This function will register for Pen Windows, if it is present.
  467. *
  468. ************************************************************************/
  469.  
  470. STATICFN VOID PenWinRegister(VOID)
  471. {
  472.     HANDLE hmod;
  473.  
  474.     if (!(hmod = (HANDLE)GetSystemMetrics(SM_PENWINDOWS)))
  475.         return;
  476.  
  477.     if (lpfnRegisterPenApp =
  478.             (LPFNPENWIN)GetProcAddress(hmod, "RegisterPenApp"))
  479.         (*lpfnRegisterPenApp)(1, TRUE);     // Be Pen-Enhanced!
  480. }
  481.  
  482.  
  483.  
  484. /************************************************************************
  485. * GetSystemValues
  486. *
  487. * This function reads various system values.  It is called at init time,
  488. * as well as if we are informed by a WM_SYSVALUECHANGED message that
  489. * some of these values have been changed.
  490. *
  491. ************************************************************************/
  492.  
  493. STATICFN VOID GetSystemValues(VOID)
  494. {
  495.     gcyBorder = GetSystemMetrics(SM_CYBORDER);
  496.  
  497.     /*
  498.      * The distance that the mouse can move during a pre-drag operation
  499.      * before starting to drag the control anyways is based on the
  500.      * mouse double-click movement distances in the system.
  501.      */
  502.     gcxPreDragMax = GetSystemMetrics(SM_CXDOUBLECLK);
  503.     gcyPreDragMax = GetSystemMetrics(SM_CYDOUBLECLK);
  504.  
  505.     /*
  506.      * The number of milliseconds that the pre-drag debounce time lasts.
  507.      */
  508.     gmsecPreDrag = 250;
  509. }
  510.  
  511.  
  512.  
  513. /************************************************************************
  514. * ReadWindowPos
  515. *
  516. * This function retrieves the saved window position for a window and
  517. * returns it in the specified variables.  It is used between sessions
  518. * to restore the application windows to the position they had when
  519. * the editor was last exited.
  520. *
  521. * Arguments:
  522. *   LPTSTR pszKeyName  - KeyName the position was saved under.
  523. *   PINT px            - Saved x position.
  524. *   PINT py            - Saved y position.
  525. *   PINT pcx           - Saved width.
  526. *   PINT pcy           - Saved height.
  527. *   BOOL *pfMaximized  - Set to TRUE if window was maximized when saved.
  528. *
  529. * Returns: 
  530. *   TRUE if the position could be read, or FALSE otherwise.
  531. *   If FALSE is returned, the values in the specified variables are
  532. *   not valid!  The caller must be able to handle a FALSE return and
  533. *   supply a default position for the window.
  534. *
  535. ************************************************************************/
  536.  
  537. BOOL ReadWindowPos(
  538.     LPTSTR pszKeyName,
  539.     PINT px,
  540.     PINT py,
  541.     PINT pcx,
  542.     PINT pcy,
  543.     BOOL *pfMaximized)
  544. {
  545.     static CHAR szSep[] = " ,";
  546.     TCHAR szBuf[CCHTEXTMAX];
  547.     CHAR szBufAnsi[CCHTEXTMAX];
  548.     PSTR psz;
  549.     BOOL fDefCharUsed;
  550.  
  551.     if (!GetPrivateProfileString(ids(IDS_APPNAME),
  552.             pszKeyName, szEmpty, szBuf, CCHTEXTMAX, ids(IDS_DLGEDITINI)))
  553.         return FALSE;
  554.  
  555.     WideCharToMultiByte(CP_ACP, 0, szBuf, -1, szBufAnsi, CCHTEXTMAX,
  556.             NULL, &fDefCharUsed);
  557.  
  558.     if (!(psz = strtok(szBufAnsi, szSep)))
  559.         return FALSE;
  560.  
  561.     *px = atoi(psz);
  562.  
  563.     if (!(psz = strtok(NULL, szSep)))
  564.         return FALSE;
  565.  
  566.     *py = atoi(psz);
  567.  
  568.     if (!(psz = strtok(NULL, szSep)))
  569.         return FALSE;
  570.  
  571.     *pcx = atoi(psz);
  572.  
  573.     if (!(psz = strtok(NULL, szSep)))
  574.         return FALSE;
  575.  
  576.     *pcy = atoi(psz);
  577.  
  578.     /*
  579.      * If there is a "1" following the coordinates, the window was
  580.      * maximized when it was saved.
  581.      */
  582.     *pfMaximized = FALSE;
  583.     if ((psz = strtok(NULL, szSep)) && atoi(psz) == 1)
  584.         *pfMaximized = TRUE;
  585.  
  586.     /*
  587.      * Don't allow a zero sized window.
  588.      */
  589.     if (*pcx == 0 || *pcy == 0)
  590.         return FALSE;
  591.  
  592.     /*
  593.      * Return success.
  594.      */
  595.     return TRUE;
  596.  
  597. }
  598.  
  599.  
  600.  
  601. /************************************************************************
  602. * WriteWindowPos
  603. *
  604. * This function writes the position of a window to the
  605. * editor's profile file under the specified keyname.
  606. * The ReadWindowPos function is the counterpart of this
  607. * function.
  608. *
  609. * Arguments:
  610. *   PRECT prc          - Rectangle for the "restored" window size.
  611. *   BOOL fMaximized    - TRUE if the window is maximized.
  612. *   LPTSTR pszKeyName  - KeyName to save the position under.
  613. *
  614. ************************************************************************/
  615.  
  616. VOID WriteWindowPos(
  617.     PRECT prc,
  618.     BOOL fMaximized,
  619.     LPTSTR pszKeyName)
  620. {
  621.     TCHAR szBuf[CCHTEXTMAX];
  622.  
  623.     wsprintf(szBuf, L"%d %d %d %d", prc->left, prc->top,
  624.             prc->right - prc->left, prc->bottom - prc->top);
  625.  
  626.     if (fMaximized)
  627.         lstrcat(szBuf, L" 1");
  628.  
  629.     WritePrivateProfileString(ids(IDS_APPNAME),
  630.             pszKeyName, szBuf, ids(IDS_DLGEDITINI));
  631. }
  632.  
  633.  
  634.  
  635. /*************************************************************************
  636. * ReadEnv
  637. *
  638. * This function initializes variables from their counterparts
  639. * in the private profile file for DlgEdit.  The application
  640. * merely needs to construct an array of INIENTRY structures
  641. * to describe the variables that must be initialized.
  642. *
  643. * Note that the original value read from the profile is saved when
  644. * it is read.  This allows us to optimize what needs to be written
  645. * out with WriteEnv.
  646. *
  647. *************************************************************************/
  648.  
  649. STATICFN VOID ReadEnv(VOID)
  650. {
  651.     register INT i;
  652.  
  653.     for (i = 0; gaie[i].pszKeyName; i++) {
  654.         *gaie[i].pnVar = gaie[i].nSave =
  655.                 GetPrivateProfileInt(ids(IDS_APPNAME),
  656.                 gaie[i].pszKeyName, gaie[i].nDefault,
  657.                 ids(IDS_DLGEDITINI));
  658.     }
  659.  
  660.     ReadCustomProfile();
  661. }
  662.  
  663.  
  664.  
  665. /*************************************************************************
  666. * WriteEnv
  667. *
  668. * This function is the counterpart to ReadEnv.  It saves values
  669. * in the profile file.
  670. *
  671. *************************************************************************/
  672.  
  673. STATICFN VOID WriteEnv(VOID)
  674. {
  675.     register INT i;
  676.     TCHAR szBuf[17];
  677.  
  678.     for (i = 0; gaie[i].pszKeyName; i++) {
  679.         /*
  680.          * Has the user changed the value since it was read?
  681.          */
  682.         if (gaie[i].nSave != *gaie[i].pnVar) {
  683.             /*
  684.              * If the new value is the same as the default value,
  685.              * erase the entry from the ini file.  Otherwise,
  686.              * write the user-specified value out.
  687.              */
  688.             if (*gaie[i].pnVar == gaie[i].nDefault) {
  689.                 WritePrivateProfileString(ids(IDS_APPNAME),
  690.                         gaie[i].pszKeyName, NULL, ids(IDS_DLGEDITINI));
  691.             }
  692.             else {
  693.                 itoaw(*gaie[i].pnVar, szBuf, 10);
  694.                 WritePrivateProfileString(ids(IDS_APPNAME),
  695.                         gaie[i].pszKeyName, szBuf, ids(IDS_DLGEDITINI));
  696.             }
  697.         }
  698.     }
  699.  
  700.     WriteCustomProfile();
  701. }
  702.  
  703.  
  704.  
  705. /************************************************************************
  706. * MainWndProc
  707. *
  708. * This is the window procedure for the "dlgedit" class.  This is the
  709. * class of the main dialog editor "client" window.
  710. *
  711. ************************************************************************/
  712.  
  713. WINDOWPROC MainWndProc(
  714.     HWND hwnd,
  715.     UINT msg,
  716.     WPARAM wParam,
  717.     LPARAM lParam)
  718. {
  719.     switch (msg) {
  720.         case WM_CREATE:
  721.             {
  722.                 RECT rc;
  723.  
  724.                 /*
  725.                  * Create the status window.
  726.                  */
  727.                 CreateDialog(ghInst, MAKEINTRESOURCE(DID_STATUS),
  728.                         hwnd, StatusDlgProc);
  729.  
  730.                 /*
  731.                  * Save away its height for sizing later (like when
  732.                  * the app is minimized then restored).
  733.                  */
  734.                 GetWindowRect(hwndStatus, &rc);
  735.                 gcyStatus = rc.bottom - rc.top;
  736.  
  737.                 ghwndSubClient = CreateWindow(szSubClientClass, NULL,
  738.                         WS_CHILD | WS_VISIBLE, 0, 0, 0, 0,
  739.                         hwnd, NULL, ghInst, NULL);
  740.  
  741.                 ghMenuMain = GetMenu(hwnd);
  742.                 LoadMenuBitmaps(ghMenuMain);
  743.             }
  744.  
  745.             break;
  746.  
  747.         case WM_ACTIVATE:
  748.             /*
  749.              * If the main window is getting activated, there is no
  750.              * currently active dialog.
  751.              */
  752.             if (LOWORD(wParam))
  753.                 gidCurrentDlg = 0;
  754.  
  755.             goto DoDefault;
  756.  
  757.         case WM_INITMENU:
  758.             if (GetMenu(ghwndMain) == (HMENU)wParam)
  759.                 InitMenu((HMENU)wParam);
  760.  
  761.             break;
  762.  
  763.         case WM_MENUSELECT:
  764.             if (HIWORD(wParam) &
  765.                     (MF_POPUP | MF_SYSMENU))
  766.                 gMenuSelected = 0;
  767.             else
  768.                 gMenuSelected = LOWORD(wParam);
  769.  
  770.             break;
  771.  
  772.         case WM_COMMAND:
  773.             DialogMenu(LOWORD(wParam));
  774.             break;
  775.  
  776.         case WM_KEYDOWN:
  777.             switch (wParam) {
  778.                 case VK_UP:
  779.                 case VK_DOWN:
  780.                 case VK_LEFT:
  781.                 case VK_RIGHT:
  782.                     if ((GetKeyState(VK_SHIFT) & 0x8000) ||
  783.                             (GetKeyState(VK_CONTROL) & 0x8000))
  784.                         break;
  785.  
  786.                     /*
  787.                      * Ignore it if we are not in a normal state
  788.                      * (don't allow when dragging).
  789.                      */
  790.                     if (gState != STATE_NORMAL)
  791.                         break;
  792.  
  793.                     /*
  794.                      * Be sure any outstanding changes get applied
  795.                      * without errors.
  796.                      */
  797.                     if (!StatusApplyChanges())
  798.                         break;
  799.  
  800.                     /*
  801.                      * Move the control in the specified direction.
  802.                      */
  803.                     MoveControl(wParam);
  804.                     break;
  805.  
  806.                 case VK_TAB:
  807.                     if (GetKeyState(VK_CONTROL) & 0x8000)
  808.                         break;
  809.  
  810.                     /*
  811.                      * Ignore it if we are not in a normal state
  812.                      * (don't allow when dragging).
  813.                      */
  814.                     if (gState != STATE_NORMAL)
  815.                         break;
  816.  
  817.                     /*
  818.                      * Be sure any outstanding changes get applied
  819.                      * without errors.
  820.                      */
  821.                     if (!StatusApplyChanges())
  822.                         break;
  823.  
  824.                     /*
  825.                      * Is the shift key pressed also?
  826.                      */
  827.                     if (GetKeyState(VK_SHIFT) & 0x8000)
  828.                         SelectPrevious();
  829.                     else
  830.                         SelectNext();
  831.  
  832.                     break;
  833.  
  834.                 case VK_ESCAPE:
  835.                     if ((GetKeyState(VK_SHIFT) & 0x8000) ||
  836.                             (GetKeyState(VK_CONTROL) & 0x8000))
  837.                         break;
  838.  
  839.                     /*
  840.                      * Be sure any outstanding changes get applied
  841.                      * without errors.
  842.                      */
  843.                     if (!StatusApplyChanges())
  844.                         break;
  845.  
  846.                     if (gState == STATE_SELECTING)
  847.                         OutlineSelectCancel();
  848.  
  849.                     /*
  850.                      * Cancel any drag operation they might have been doing.
  851.                      */
  852.                     if (gState != STATE_NORMAL)
  853.                         DragCancel();
  854.  
  855.                     break;
  856.  
  857.                 case VK_RETURN:
  858.                     if ((GetKeyState(VK_SHIFT) & 0x8000) ||
  859.                             (GetKeyState(VK_CONTROL) & 0x8000))
  860.                         break;
  861.  
  862.                     /*
  863.                      * Be sure any outstanding changes get applied
  864.                      * without errors.
  865.                      */
  866.                     if (!StatusApplyChanges())
  867.                         break;
  868.  
  869.                     switch (gState) {
  870.                         POINTS mpt;
  871.                         POINT pt;
  872.                         DWORD dwPos;
  873.  
  874.                         case STATE_SELECTING:
  875.                             /*
  876.                              * In outline selection mode.  Map the
  877.                              * location of the mouse at the time that
  878.                              * the user pressed Enter into a point
  879.                              * relative to the dialog client and complete
  880.                              * the selection operation.
  881.                              */
  882.                             dwPos = GetMessagePos();
  883.                             mpt = (*((POINTS *)&(dwPos)));
  884.                             ((pt).x = (mpt).x, (pt).y = (mpt).y);
  885.                             ScreenToClient(gcd.npc->hwnd, &pt);
  886.                             OutlineSelectEnd(pt.x, pt.y);
  887.  
  888.                             break;
  889.  
  890.                         case STATE_DRAGGING:
  891.                         case STATE_DRAGGINGNEW:
  892.                             /*
  893.                              * We are dragging something.  Map the
  894.                              * location of the mouse at the time
  895.                              * that the user pressed Enter into a
  896.                              * point relative to the proper window
  897.                              * and complete the drag operation.
  898.                              */
  899.                             dwPos = GetMessagePos();
  900.                             mpt = (*((POINTS *)&(dwPos)));
  901.                             ((pt).x = (mpt).x, (pt).y = (mpt).y);
  902.  
  903.                             /*
  904.                              * The point must be changed to be relative to
  905.                              * the window that the ending mouse up message
  906.                              * would have come through, which will be the
  907.                              * capture window for the drag.  This will be
  908.                              * the dialog if we are adding a new control,
  909.                              * or it will be the selected control if we are
  910.                              * dragging an existing control.
  911.                              */
  912.                             ScreenToClient((gState == STATE_DRAGGING) ?
  913.                                     gnpcSel->hwnd : gcd.npc->hwnd, &pt);
  914.  
  915.                             /*
  916.                              * If the dialog is selected, map the points from
  917.                              * the client area to the window.
  918.                              */
  919.                             if (gfDlgSelected)
  920.                                 MapDlgClientPoint(&pt, TRUE);
  921.  
  922.                             DragEnd(pt.x, pt.y);
  923.  
  924.                             break;
  925.                     }
  926.  
  927.                     break;
  928.             }
  929.  
  930.             break;
  931.  
  932.         case WM_NCCALCSIZE:
  933.             /*
  934.              * Save away what is going to be the new window position.
  935.              */
  936.             if (!IsIconic(hwnd) && !IsZoomed(hwnd))
  937.                 grcAppPos = *((LPRECT)lParam);
  938.  
  939.             /*
  940.              * Now let the DefWindowProc calculate the client area normally.
  941.              */
  942.             goto DoDefault;
  943.  
  944.         case WM_MOVE:
  945.             if (gfEditingDlg)
  946.                 RepositionDialog();
  947.  
  948.             break;
  949.  
  950.         case WM_SIZE:
  951.             SizeRibbons(hwnd);
  952.  
  953.             /*
  954.              * Did the app start minimized and is it being restored
  955.              * for the first time?  If so, show the toolbox if
  956.              * the user has requested it.
  957.              */
  958.             if (fStartAsIcon && !IsIconic(hwnd)) {
  959.                 if (gfShowToolbox)
  960.                     ToolboxShow(TRUE);
  961.  
  962.                 fStartAsIcon = FALSE;
  963.             }
  964.  
  965.             break;
  966.  
  967.         case WM_SYSCOLORCHANGE:
  968.             LoadSysColorBitmaps();
  969.             break;
  970.  
  971.         case WM_CLOSE:
  972.             if (ghwndTestDlg)
  973.                 DestroyTestDialog();
  974.  
  975.             if (DoWeSave(FILE_INCLUDE) == IDCANCEL ||
  976.                     DoWeSave(FILE_RESOURCE) == IDCANCEL)
  977.                 break;
  978.  
  979.             /*
  980.              * First destroy the Properties Bar.
  981.              */
  982.             DestroyWindow(hwndStatus);
  983.             hwndStatus = NULL;
  984.  
  985.             DestroyWindow(hwnd);
  986.             break;
  987.  
  988.         case WM_QUERYENDSESSION:
  989.             if (ghwndTestDlg)
  990.                 DestroyTestDialog();
  991.  
  992.             if (DoWeSave(FILE_INCLUDE) == IDCANCEL ||
  993.                     DoWeSave(FILE_RESOURCE) == IDCANCEL)
  994.                 return FALSE;
  995.             else
  996.                 return TRUE;
  997.  
  998.         case WM_DESTROY:
  999.             /*
  1000.              * Save the position of the app's window.
  1001.              */
  1002.             WriteWindowPos(&grcAppPos, IsZoomed(hwnd), szAppPos);
  1003.  
  1004.             WinHelp(hwnd, gszHelpFile, HELP_QUIT, 0L);
  1005.             FreeMenuBitmaps();
  1006.             PostQuitMessage(0);
  1007.             break;
  1008.  
  1009.         default:
  1010.             /*
  1011.              * Is this the registered help message from one of the common
  1012.              * dialogs?  If so, show the help for it.
  1013.              *
  1014.              * The check to be sure gmsgHelp is non-zero is just in
  1015.              * case the call to register the help message failed
  1016.              * (it will return zero) and there happens to be a zero
  1017.              * message that gets sent to this window somehow.
  1018.              */
  1019.             if (msg == gmsgHelp && gmsgHelp) {
  1020.                 ShowHelp(FALSE);
  1021.                 return 0;
  1022.             }
  1023.  
  1024.         DoDefault:
  1025.             return DefWindowProc(hwnd, msg, wParam, lParam);
  1026.     }
  1027.  
  1028.     return 0;
  1029. }
  1030.  
  1031.  
  1032.  
  1033. /************************************************************************
  1034. * LoadSysColorBitmaps
  1035. *
  1036. * This function loads bitmaps that depend on the system window and
  1037. * highlight colors.  As it loads them, it replaces two special colors
  1038. * in them with some system colors.
  1039. * This is used for the control type bitmaps that appear in lines
  1040. * in the listbox in the Order/Group dialog.
  1041. *
  1042. ************************************************************************/
  1043.  
  1044. STATICFN VOID LoadSysColorBitmaps(VOID)
  1045. {
  1046.     DWORD rgbWindow;
  1047.     DWORD rgbWindowText;
  1048.     DWORD rgbHighlight;
  1049.     DWORD rgbHighlightText;
  1050.     INT i;
  1051.  
  1052.     rgbWindow = GetSysColor(COLOR_WINDOW);
  1053.     rgbWindowText = GetSysColor(COLOR_WINDOWTEXT);
  1054.     rgbHighlight = GetSysColor(COLOR_HIGHLIGHT);
  1055.     rgbHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT);
  1056.  
  1057.     if (hbmTabStop)
  1058.         DeleteObject(hbmTabStop);
  1059.  
  1060.     hbmTabStop = LoadAlterBitmap(IDBM_TABSTOP, rgbWindow, rgbWindowText);
  1061.  
  1062.     if (hbmTabStopSel)
  1063.         DeleteObject(hbmTabStopSel);
  1064.  
  1065.     hbmTabStopSel = LoadAlterBitmap(IDBM_TABSTOP,
  1066.             rgbHighlight, rgbHighlightText);
  1067.  
  1068.     for (i = 0; i < CCONTROLS; i++) {
  1069.         if (awcd[i].hbmCtrlType)
  1070.             DeleteObject(awcd[i].hbmCtrlType);
  1071.  
  1072.         awcd[i].hbmCtrlType = LoadAlterBitmap(
  1073.                 awcd[i].idbmCtrlType, rgbWindow, rgbWindowText);
  1074.  
  1075.         if (awcd[i].hbmCtrlTypeSel)
  1076.             DeleteObject(awcd[i].hbmCtrlTypeSel);
  1077.  
  1078.         awcd[i].hbmCtrlTypeSel = LoadAlterBitmap(
  1079.                 awcd[i].idbmCtrlType, rgbHighlight, rgbHighlightText);
  1080.     }
  1081.  
  1082.     if (ghbmDragHandle)
  1083.         DeleteObject(ghbmDragHandle);
  1084.  
  1085.     ghbmDragHandle = LoadAlterBitmap(IDBM_DRAGHANDLE,
  1086.             rgbWindow, rgbHighlight);
  1087.  
  1088.     if (ghbmDragHandle2)
  1089.         DeleteObject(ghbmDragHandle2);
  1090.  
  1091.     ghbmDragHandle2 = LoadAlterBitmap(IDBM_DRAGHANDLE2,
  1092.             rgbWindow, rgbHighlight);
  1093. }
  1094.  
  1095.  
  1096.  
  1097. /************************************************************************
  1098. * LoadAlterBitmap
  1099. *
  1100. * This function loads a single bitmap.  As it does, it replaces a
  1101. * couple special RGB colors (REPLACECOLOR1 and REPLACECOLOR2) with
  1102. * the passed in RGB colors.
  1103. *
  1104. * Arguments:
  1105. *   INT idbm      - Integer ID of the bitmap to load.
  1106. *   DWORD rgbNew  - Color to replace the special color with.
  1107. *   DWORD rgbNew2 - A second color to replace the second special color with.
  1108. *
  1109. * Returns:
  1110. *   The handle to the bitmap, or NULL if an error occurs.
  1111. *
  1112. ************************************************************************/
  1113.  
  1114. STATICFN HBITMAP LoadAlterBitmap(
  1115.     INT idbm,
  1116.     DWORD rgbNew,
  1117.     DWORD rgbNew2)
  1118. {
  1119.     register INT i;
  1120.     LPBITMAPINFOHEADER lpbihInfo;
  1121.     HDC hdcScreen;
  1122.     HANDLE hresLoad;
  1123.     HANDLE hres;
  1124.     DWORD FAR *qlng;
  1125.     LPBYTE lpbBits;
  1126.     HANDLE hbmp;
  1127.     DWORD rgbReplace1;
  1128.     DWORD rgbReplace2;
  1129.  
  1130.     hresLoad = FindResource(ghInst, MAKEINTRESOURCE(idbm), RT_BITMAP);
  1131.     if (!hresLoad)
  1132.         return NULL;
  1133.  
  1134.     hres = LoadResource(ghInst, hresLoad);
  1135.     if (!hresLoad)
  1136.         return NULL;
  1137.  
  1138.     rgbNew = RGBInvertRGB(rgbNew);
  1139.     rgbNew2 = RGBInvertRGB(rgbNew2);
  1140.     rgbReplace1 = RGBInvertRGB(REPLACECOLOR1);
  1141.     rgbReplace2 = RGBInvertRGB(REPLACECOLOR2);
  1142.     lpbihInfo = (LPBITMAPINFOHEADER)LockResource(hres);
  1143.     qlng = (LPDWORD)((PBYTE)(lpbihInfo) + lpbihInfo->biSize);
  1144.  
  1145.     for (i = 0; i < (1 << lpbihInfo->biBitCount); i++, qlng++) {
  1146.         if (*qlng == rgbReplace1)
  1147.             *qlng = rgbNew;
  1148.         else if (*qlng == rgbReplace2)
  1149.             *qlng = rgbNew2;
  1150.     }
  1151.  
  1152.     /*
  1153.      * First skip over the header structure.
  1154.      */
  1155.     lpbBits = (LPBYTE)(lpbihInfo + 1);
  1156.  
  1157.     /*
  1158.      * Skip the color table entries, if any.
  1159.      */
  1160.     lpbBits += (1 << (lpbihInfo->biBitCount)) * sizeof(RGBQUAD);
  1161.  
  1162.     /*
  1163.      * Create a color bitmap compatible with the display device.
  1164.      */
  1165.     if (hdcScreen = GetDC(NULL)) {
  1166.         hbmp = CreateDIBitmap(hdcScreen, lpbihInfo, (LONG)CBM_INIT,
  1167.                 lpbBits, (LPBITMAPINFO)lpbihInfo, DIB_RGB_COLORS);
  1168.         ReleaseDC(NULL, hdcScreen);
  1169.     }
  1170.  
  1171.     UnlockResource(hres);
  1172.     FreeResource(hres);
  1173.  
  1174.     return hbmp;
  1175. }
  1176.  
  1177.  
  1178.  
  1179. /************************************************************************
  1180. * RGBInvertRGB
  1181. *
  1182. * Reverses the RGB order of a color.  This needs to be done to match
  1183. * the resource file format of the color table.
  1184. *
  1185. ************************************************************************/
  1186.  
  1187. STATICFN DWORD RGBInvertRGB(
  1188.     DWORD rgb)
  1189. {
  1190.     return (DWORD)RGB(GetBValue(rgb), GetGValue(rgb), GetRValue(rgb));
  1191. }
  1192.  
  1193.  
  1194.  
  1195. /************************************************************************
  1196. * SizeRibbons
  1197. *
  1198. * This function positions and sizes the child ribbon and subclient
  1199. * windows in the dialog editor.  It needs to be called any time the
  1200. * size of the main windows changes.
  1201. *
  1202. * Arguments:
  1203. *   HWND hwnd - Parent window handle.
  1204. *
  1205. ************************************************************************/
  1206.  
  1207. STATICFN VOID SizeRibbons(
  1208.     HWND hwnd)
  1209. {
  1210.     RECT rcClient;
  1211.  
  1212.     if (hwndStatus && !IsIconic(hwnd)) {
  1213.         /*
  1214.          * Get the client area.
  1215.          */
  1216.         GetClientRect(hwnd, &rcClient);
  1217.  
  1218.         /*
  1219.          * Size/move the status and subclient window to fit
  1220.          * the new client area.
  1221.          */
  1222.         SetWindowPos(hwndStatus, NULL,
  1223.                 0, 0,
  1224.                 rcClient.right - rcClient.left,
  1225.                 min(rcClient.bottom - rcClient.top, gcyStatus),
  1226.                 SWP_NOACTIVATE | SWP_NOZORDER);
  1227.  
  1228.         SetWindowPos(ghwndSubClient, NULL,
  1229.                 0, gcyStatus,
  1230.                 rcClient.right - rcClient.left,
  1231.                 max((rcClient.bottom - rcClient.top) - gcyStatus, 0),
  1232.                 SWP_NOACTIVATE | SWP_NOZORDER);
  1233.     }
  1234. }
  1235.  
  1236.  
  1237.  
  1238. /****************************************************************************
  1239. * DialogTerminate
  1240. *
  1241. * This undoes what DialogInit does.  It should be called before terminating
  1242. * and after a DialogInit.
  1243. *
  1244. ****************************************************************************/
  1245.  
  1246. STATICFN VOID DialogTerminate(VOID)
  1247. {
  1248.     register INT i;
  1249.  
  1250.     /*
  1251.      * Save the Preferences data.
  1252.      */
  1253.     WriteEnv();
  1254.  
  1255.     if (hbmTabStop)
  1256.         DeleteObject(hbmTabStop);
  1257.  
  1258.     if (hbmTabStopSel)
  1259.         DeleteObject(hbmTabStopSel);
  1260.  
  1261.     if (ghbmDragHandle)
  1262.         DeleteObject(ghbmDragHandle);
  1263.  
  1264.     if (ghbmDragHandle2)
  1265.         DeleteObject(ghbmDragHandle2);
  1266.  
  1267.     if (ghDCMem)
  1268.         DeleteDC(ghDCMem);
  1269.  
  1270.     /*
  1271.      * Free the control type bitmaps.
  1272.      */
  1273.     for (i = 0; i < CCONTROLS; i++) {
  1274.         if (awcd[i].hbmCtrlType)
  1275.             DeleteObject(awcd[i].hbmCtrlType);
  1276.  
  1277.         if (awcd[i].hbmCtrlTypeSel)
  1278.             DeleteObject(awcd[i].hbmCtrlTypeSel);
  1279.     }
  1280.  
  1281.     /*
  1282.      * Free all the custom control links.  This must be done before the
  1283.      * app exits so that any loaded DLL's get unloaded!
  1284.      */
  1285.     while (gpclHead)
  1286.         RemoveCustomLink(gpclHead);
  1287.  
  1288.     if (hpenDarkGray)
  1289.         DeleteObject(hpenDarkGray);
  1290.  
  1291.     if (ghhkMsgFilter)
  1292.         UnhookWindowsHookEx(ghhkMsgFilter);
  1293.  
  1294.     if (lpfnRegisterPenApp)
  1295.         (*lpfnRegisterPenApp)((WORD)1, FALSE);
  1296. }
  1297.