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 / select.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  60KB  |  2,028 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: select.c
  14. *
  15. * Contains routines for selecting and positioning controls.
  16. *
  17. * Functions:
  18. *
  19. *    SelectControl()
  20. *    SelectControl2()
  21. *    RedrawSelection()
  22. *    SetAnchorToFirstSel()
  23. *    SelectNext()
  24. *    SelectPrevious()
  25. *    UnSelectControl()
  26. *    CalcSelectedRect()
  27. *    CancelSelection()
  28. *    OutlineSelectBegin()
  29. *    OutlineSelectDraw()
  30. *    OutlineSelectCancel()
  31. *    OutlineSelectEnd()
  32. *    MyFrameRect()
  33. *    MoveControl()
  34. *    PositionControl()
  35. *    RepositionDialog()
  36. *    SaveDlgClientRect()
  37. *    SizeToText()
  38. *    AlignControls()
  39. *    ArrangeSpacing()
  40. *    ArrangeSize()
  41. *    ArrangePushButtons()
  42. *    InvalidateDlgHandles()
  43. *    OutlineSelectHide()
  44. *    OutlineSelectSetRect()
  45. *    PositionControl2()
  46. *    SizeCtrlToText()
  47. *    QueryTextExtent()
  48. *
  49. * Comments:
  50. *
  51. ****************************************************************************/
  52.  
  53. #include "dlgedit.h"
  54. #include "dlgfuncs.h"
  55. #include "dlgextrn.h"
  56.  
  57.  
  58. STATICFN VOID InvalidateDlgHandles(VOID);
  59. STATICFN VOID OutlineSelectHide(VOID);
  60. STATICFN VOID OutlineSelectSetRect(INT x, INT y);
  61. STATICFN HANDLE PositionControl2(NPCTYPE npc, PRECT prc, HANDLE hwpi);
  62. STATICFN BOOL SizeCtrlToText(NPCTYPE npc);
  63. STATICFN INT QueryTextExtent(HWND hwnd, LPTSTR pszText, BOOL fWordBreak);
  64.  
  65. static POINT gptOutlineSelect;
  66. static RECT grcOutlineSelect;
  67. static RECT grcOutlineSelectLimit;
  68. static BOOL gfOutlineSelectShown = FALSE;
  69.  
  70.  
  71.  
  72. /************************************************************************
  73. * SelectControl
  74. *
  75. * This routine selects a control, showing its drag window and handles.
  76. * If fCheckShift is TRUE and the shift key is down, this routine adds
  77. * the control to the existing selection, unless the control is already
  78. * selected, in which case it is removed from the existing selection.
  79. *
  80. * This routine handles the case where a controls is clicked on to select
  81. * it, and this may cause other controls to be unselected.  If it is
  82. * known for sure that a control should be selected or added to the
  83. * existing selection, SelectControl2 can be used instead.
  84. *
  85. * Arguments:
  86. *   NPCTYPE npc      = The control to select.
  87. *   BOOL fCheckShift = TRUE if the state of the shift key should be
  88. *                      taken into consideration.
  89. *
  90. * Returns:
  91. *   The return will be FALSE if the control was just unselected.
  92. *
  93. ************************************************************************/
  94.  
  95. BOOL SelectControl(
  96.     NPCTYPE npc,
  97.     BOOL fCheckShift)
  98. {
  99.     BOOL fShiftDown;
  100.     BOOL fSelectDone = TRUE;
  101.  
  102.     if (npc->pwcd->iType == W_DIALOG) {
  103.         if (gnpcSel == npc)
  104.             return TRUE;
  105.  
  106.         CancelSelection(FALSE);
  107.         SelectControl2(npc, FALSE);
  108.     }
  109.     else {
  110.         if (fCheckShift)
  111.             fShiftDown = (GetKeyState(VK_SHIFT) & 0x8000) ? TRUE : FALSE;
  112.         else
  113.             fShiftDown = FALSE;
  114.  
  115.         if (npc->fSelected) {
  116.             /*
  117.              * If the shift key is down, and they are NOT trying to
  118.              * select the dialog, toggle the selection of this control
  119.              * to off.
  120.              */
  121.             if (fShiftDown && npc->pwcd->iType != W_DIALOG) {
  122.                 UnSelectControl(npc);
  123.                 CalcSelectedRect();
  124.                 fSelectDone = FALSE;
  125.             }
  126.             else {
  127.                 if (gnpcSel == npc)
  128.                     return TRUE;
  129.                 else
  130.                     SelectControl2(npc, FALSE);
  131.             }
  132.         }
  133.         else {
  134.             /*
  135.              * If they are NOT holding the shift key down, or the
  136.              * dialog is selected, cancel the selection first.
  137.              */
  138.             if (!fShiftDown || gcd.npc->fSelected == TRUE)
  139.                 CancelSelection(FALSE);
  140.  
  141.             SelectControl2(npc, FALSE);
  142.         }
  143.     }
  144.  
  145.     StatusUpdate();
  146.     StatusSetEnable();
  147.  
  148.     return fSelectDone;
  149. }
  150.  
  151.  
  152.  
  153. /************************************************************************
  154. * SelectControl2
  155. *
  156. * This routine is the worker for SelectControl.  It does the actual
  157. * work to "select" a control, updating globals and showing the drag
  158. * windows with handles.
  159. *
  160. * This routine handles the special case where we are selecting a
  161. * control that is already selected.  The editor has the concept of
  162. * a control being selected, as well as there being the currently
  163. * selected control (pointed to by gnpcSel).  There can be the case
  164. * where there are multiple controls selected, but only one will be
  165. * the current selection (usually the last one clicked on).  This
  166. * routine will never unselect other controls.  This must be done
  167. * prior to here, if appropriate.
  168. *
  169. * Arguments:
  170. *   NPCTYPE npc      = The control to make the current selection.
  171. *   BOOL fDontUpdate = TRUE if the selection should NOT be redrawn
  172. *                      after the specified control is added to it.
  173. *                      This allows painting to be deferred until
  174. *                      later if a number of controls are being
  175. *                      selected in a loop.  It also does not call
  176. *                      CalcSelectedRect (this MUST be done later
  177. *                      for drags to work, however!).
  178. * Comments:
  179. *
  180. * If fDontUpdate is TRUE, the selection will not be redrawn, and it
  181. * is required that CalcSelectedRect be called before doing any drag
  182. * operations.
  183. *
  184. ************************************************************************/
  185.  
  186. VOID SelectControl2(
  187.     NPCTYPE npc,
  188.     BOOL fDontUpdate)
  189. {
  190.     BOOL fUpdate = FALSE;
  191.  
  192.     /*
  193.      * Is the control already selected?
  194.      */
  195.     if (npc->fSelected) {
  196.         /*
  197.          * It is already selected (hwndDrag is visible).  If it is
  198.          * not the current selection, we want all drag windows to
  199.          * be redrawn in the proper order to update their appearance.
  200.          */
  201.         if (gnpcSel != npc)
  202.             fUpdate = TRUE;
  203.     }
  204.     else {
  205.         /*
  206.          * The control is not yet selected.  If another control is
  207.          * currently selected, we want all drag windows to be
  208.          * updated so that their handle appearance gets updated.
  209.          */
  210.         if (gnpcSel)
  211.             fUpdate = TRUE;
  212.  
  213.         /*
  214.          * Flip its flag and add to the selected count.
  215.          */
  216.         npc->fSelected = TRUE;
  217.         gcSelected++;
  218.     }
  219.  
  220.     gnpcSel = npc;
  221.  
  222.     if (!fDontUpdate)
  223.         CalcSelectedRect();
  224.  
  225.     if (npc->pwcd->iType == W_DIALOG) {
  226.         gfDlgSelected = TRUE;
  227.         InvalidateDlgHandles();
  228.     }
  229.     else {
  230.         gfDlgSelected = FALSE;
  231.         ShowWindow(npc->hwndDrag, SW_SHOW);
  232.  
  233.         if (fUpdate && !fDontUpdate)
  234.             RedrawSelection();
  235.     }
  236. }
  237.  
  238.  
  239.  
  240. /************************************************************************
  241. * RedrawSelection
  242. *
  243. * This function cause all the selected drag windows to be invalidated.
  244. * This is necessary whenever one of them changes, because of the very
  245. * touchy painting order that has to be maintained.  Without invalidating
  246. * all of them as a unit, there are cases where handles do not get
  247. * properly painted.
  248. *
  249. ************************************************************************/
  250.  
  251. VOID RedrawSelection(VOID)
  252. {
  253.     NPCTYPE npc;
  254.  
  255.     if (!gcSelected) {
  256.         return;
  257.     }
  258.     else if (gcSelected == 1) {
  259.         InvalidateRect(gfDlgSelected ? gnpcSel->hwnd : gnpcSel->hwndDrag,
  260.                 NULL, TRUE);
  261.     }
  262.     else {
  263.         for (npc = npcHead; npc; npc = npc->npcNext) {
  264.             if (npc->fSelected)
  265.                 InvalidateRect(npc->hwndDrag, NULL, TRUE);
  266.         }
  267.     }
  268. }
  269.  
  270.  
  271.  
  272. /************************************************************************
  273. * SetAnchorToFirstSel
  274. *
  275. * This function makes the current selection (the anchor) be the
  276. * first selected control.  It is used after making a group selection,
  277. * and ensures that the control that ends up being the anchor is
  278. * consistently the first one in Z-order.
  279. *
  280. * Arguments:
  281. *   BOOL fDontUpdate = TRUE if the selection should NOT be redrawn.
  282. *
  283. ************************************************************************/
  284.  
  285. VOID SetAnchorToFirstSel(
  286.     BOOL fDontUpdate)
  287. {
  288.     NPCTYPE npc;
  289.  
  290.     if (gcSelected) {
  291.         for (npc = npcHead; npc; npc = npc->npcNext) {
  292.             if (npc->fSelected) {
  293.                 SelectControl2(npc, fDontUpdate);
  294.                 break;
  295.             }
  296.         }
  297.     }
  298. }
  299.  
  300.  
  301.  
  302. /************************************************************************
  303. * SelectNext
  304. *
  305. * This selects the next control in the dialog box.  The enumeration
  306. * includes the dialog box itself, and wraps around.
  307. *
  308. ************************************************************************/
  309.  
  310. VOID SelectNext(VOID)
  311. {
  312.     NPCTYPE npcSelect;
  313.  
  314.     /*
  315.      * Disable the tabbing functions if there is no dialog
  316.      * or if the dialog is already selected and there are
  317.      * no controls (the tabs would be a noop in this case).
  318.      */
  319.     if (!gfEditingDlg || (gfDlgSelected && !npcHead))
  320.         return;
  321.  
  322.     /*
  323.      * Is nothing selected?
  324.      */
  325.     if (!gnpcSel) {
  326.         /*
  327.          * Select the first control, unless there are none, in which
  328.          * case select the dialog.
  329.          */
  330.         if (npcHead)
  331.             npcSelect = npcHead;
  332.         else
  333.             npcSelect = gcd.npc;
  334.     }
  335.     else {
  336.         /*
  337.          * Is the dialog selected?
  338.          */
  339.         if (gfDlgSelected) {
  340.             /*
  341.              * Select the first control, unless there are none, in which
  342.              * case do nothing.
  343.              */
  344.             if (npcHead)
  345.                 npcSelect = npcHead;
  346.             else
  347.                 npcSelect = NULL;
  348.         }
  349.         else {
  350.             /*
  351.              * Find the current control.  If there is one after it,
  352.              * select it, otherwise wrap around to the dialog and
  353.              * select it.
  354.              */
  355.             if (gnpcSel->npcNext)
  356.                 npcSelect = gnpcSel->npcNext;
  357.             else
  358.                 npcSelect = gcd.npc;
  359.         }
  360.     }
  361.  
  362.     if (npcSelect)
  363.         SelectControl(npcSelect, FALSE);
  364. }
  365.  
  366.  
  367.  
  368. /************************************************************************
  369. * SelectPrevious
  370. *
  371. * This selects the previous control in the dialog box.  The enumeration
  372. * includes the dialog box itself, and wraps around.
  373. *
  374. ************************************************************************/
  375.  
  376. VOID SelectPrevious(VOID)
  377. {
  378.     NPCTYPE npc;
  379.     NPCTYPE npcSelect;
  380.  
  381.     /*
  382.      * Disable the tabbing functions if there is no dialog
  383.      * or if the dialog is already selected and there are
  384.      * no controls (the tabs would be a noop in this case).
  385.      */
  386.     if (!gfEditingDlg || (gfDlgSelected && !npcHead))
  387.         return;
  388.  
  389.     /*
  390.      * Is nothing selected?
  391.      */
  392.     if (!gnpcSel) {
  393.         /*
  394.          * Select the last control, unless there are none, in which
  395.          * case select the dialog.
  396.          */
  397.         if (npcHead) {
  398.             npc = npcHead;
  399.             while (npc->npcNext)
  400.                 npc = npc->npcNext;
  401.  
  402.             npcSelect = npc;
  403.         }
  404.         else {
  405.             npcSelect = gcd.npc;
  406.         }
  407.     }
  408.     else {
  409.         /*
  410.          * Is the dialog selected?
  411.          */
  412.         if (gfDlgSelected) {
  413.             /*
  414.              * Select the last control, unless there are none, in which
  415.              * case select nothing.
  416.              */
  417.             if (npcHead) {
  418.                 npc = npcHead;
  419.                 while (npc->npcNext)
  420.                     npc = npc->npcNext;
  421.  
  422.                 npcSelect = npc;
  423.             }
  424.             else {
  425.                 npcSelect = NULL;
  426.             }
  427.         }
  428.         else {
  429.             /*
  430.              * If the first control is selected, select the dialog.
  431.              * Otherwise hunt for and select the previous control.
  432.              */
  433.             if (npcHead == gnpcSel) {
  434.                 npcSelect = gcd.npc;
  435.             }
  436.             else {
  437.                 npc = npcHead;
  438.                 while (npc->npcNext != gnpcSel)
  439.                     npc = npc->npcNext;
  440.  
  441.                 npcSelect = npc;
  442.             }
  443.         }
  444.     }
  445.  
  446.     if (npcSelect)
  447.         SelectControl(npcSelect, FALSE);
  448. }
  449.  
  450.  
  451.  
  452. /************************************************************************
  453. * UnSelectControl
  454. *
  455. * This unselects the specified control, hiding its drag window and handles.
  456. *
  457. * Arguments:
  458. *     NPCTYPE npc = The control to deselect.
  459. *
  460. ************************************************************************/
  461.  
  462. VOID UnSelectControl(
  463.     NPCTYPE npc)
  464. {
  465.     npc->fSelected = FALSE;
  466.     gcSelected--;
  467.  
  468.     /*
  469.      * We don't have a current selection if there are no selected
  470.      * windows, or if the control we are unselecting was the current
  471.      * selection.
  472.      */
  473.     if (!gcSelected || npc == gnpcSel)
  474.         gnpcSel = NULL;
  475.  
  476.     if (npc->pwcd->iType == W_DIALOG) {
  477.         gfDlgSelected = FALSE;
  478.         InvalidateDlgHandles();
  479.     }
  480.     else {
  481.         ShowWindow(npc->hwndDrag, SW_HIDE);
  482.     }
  483.  
  484.     /*
  485.      * Are there still some selected controls, and was the control
  486.      * we just unselected the current selection?  If so, we need
  487.      * to set the current selection to something.
  488.      */
  489.     if (gcSelected && !gnpcSel)
  490.         SetAnchorToFirstSel(FALSE);
  491. }
  492.  
  493.  
  494.  
  495. /************************************************************************
  496. * InvalidateDlgHandles
  497. *
  498. * This function invalidates the handles for the dialog.  This is
  499. * used as an optimization so that the entire dialog does not need
  500. * to be invalidated just to force the handles to be drawn.
  501. *
  502. ************************************************************************/
  503.  
  504. STATICFN VOID InvalidateDlgHandles(VOID)
  505. {
  506.     RECT rc;
  507.     RECT rcClient;
  508.     RECT rcFrame;
  509.     POINT pt;
  510.     INT xOffset;
  511.     INT yOffset;
  512.  
  513.     /*
  514.      * Redraw the dialog border.
  515.      */
  516.     SetWindowPos(gcd.npc->hwnd, NULL, 0, 0, 0, 0,
  517.             SWP_DRAWFRAME | SWP_NOACTIVATE |
  518.             SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
  519.  
  520.     /*
  521.      * Get the frame and client rectangles.
  522.      */
  523.     GetWindowRect(gcd.npc->hwnd, &rcFrame);
  524.     GetClientRect(gcd.npc->hwnd, &rcClient);
  525.  
  526.     /*
  527.      * Determine the offset from the frame origin to the client
  528.      * origin.
  529.      */
  530.     pt.x = pt.y = 0;
  531.     ClientToScreen(gcd.npc->hwnd, &pt);
  532.     xOffset = rcFrame.left - pt.x;
  533.     yOffset = rcFrame.top - pt.y;
  534.  
  535.     /*
  536.      * Make the frame rectangle zero based.
  537.      */
  538.     OffsetRect(&rcFrame, -rcFrame.left, -rcFrame.top);
  539.  
  540.     rc.left = 0;
  541.     rc.top = 0;
  542.     rc.right = CHANDLESIZE;
  543.     rc.bottom = CHANDLESIZE;
  544.     OffsetRect(&rc, xOffset, yOffset);
  545.     InvalidateRect(gcd.npc->hwnd, &rc, TRUE);
  546.  
  547.     rc.left = ((rcFrame.right + 1) / 2) - (CHANDLESIZE / 2);
  548.     rc.top = 0;
  549.     rc.right = rc.left + CHANDLESIZE;
  550.     rc.bottom = CHANDLESIZE;
  551.     OffsetRect(&rc, xOffset, yOffset);
  552.     InvalidateRect(gcd.npc->hwnd, &rc, TRUE);
  553.  
  554.     rc.left = rcFrame.right - CHANDLESIZE;
  555.     rc.top = 0;
  556.     rc.right = rcFrame.right;
  557.     rc.bottom = CHANDLESIZE;
  558.     OffsetRect(&rc, xOffset, yOffset);
  559.     InvalidateRect(gcd.npc->hwnd, &rc, TRUE);
  560.  
  561.     rc.left = rcFrame.right - CHANDLESIZE;
  562.     rc.top = ((rcFrame.bottom + 1) / 2) - (CHANDLESIZE / 2);
  563.     rc.right = rcFrame.right;
  564.     rc.bottom = rc.top + CHANDLESIZE;
  565.     OffsetRect(&rc, xOffset, yOffset);
  566.     InvalidateRect(gcd.npc->hwnd, &rc, TRUE);
  567.  
  568.     rc.left = rcFrame.right - CHANDLESIZE;
  569.     rc.top = rcFrame.bottom - CHANDLESIZE;
  570.     rc.right = rcFrame.right;
  571.     rc.bottom = rcFrame.bottom;
  572.     OffsetRect(&rc, xOffset, yOffset);
  573.     InvalidateRect(gcd.npc->hwnd, &rc, TRUE);
  574.  
  575.     rc.left = ((rcFrame.right + 1) / 2) - (CHANDLESIZE / 2);
  576.     rc.top = rcFrame.bottom - CHANDLESIZE;
  577.     rc.right = rc.left + CHANDLESIZE;
  578.     rc.bottom = rcFrame.bottom;
  579.     OffsetRect(&rc, xOffset, yOffset);
  580.     InvalidateRect(gcd.npc->hwnd, &rc, TRUE);
  581.  
  582.     rc.left = 0;
  583.     rc.top = rcFrame.bottom - CHANDLESIZE;
  584.     rc.right = CHANDLESIZE;
  585.     rc.bottom = rcFrame.bottom;
  586.     OffsetRect(&rc, xOffset, yOffset);
  587.     InvalidateRect(gcd.npc->hwnd, &rc, TRUE);
  588.  
  589.     rc.left = 0;
  590.     rc.top = ((rcFrame.bottom + 1) / 2) - (CHANDLESIZE / 2);
  591.     rc.right = CHANDLESIZE;
  592.     rc.bottom = rc.top + CHANDLESIZE;
  593.     OffsetRect(&rc, xOffset, yOffset);
  594.     InvalidateRect(gcd.npc->hwnd, &rc, TRUE);
  595. }
  596.  
  597.  
  598.  
  599. /************************************************************************
  600. * CalcSelectedRect
  601. *
  602. * This routine updates the gwrcSelected rectangle.  This is used during
  603. * dragging operations.  It contains the minimum rectangle that spans
  604. * all the selected controls.  If there are no selected controls, the
  605. * contents of this global are not defined.  This routine must be called
  606. * every time that a control is selected of unselected to keep this
  607. * rectangle updated, or tracking will not work properly.
  608. *
  609. ************************************************************************/
  610.  
  611. VOID CalcSelectedRect(VOID)
  612. {
  613.     NPCTYPE npc;
  614.     INT nBottom;
  615.     INT nBottomLowest;
  616.  
  617.     /*
  618.      * Nothing is selected.  The rectangle values are considered
  619.      * undefined, so we can quit.
  620.      */
  621.     if (!gcSelected)
  622.         return;
  623.  
  624.     if (gcSelected == 1) {
  625.         /*
  626.          * Only one control is selected.  Set the rectangle to its
  627.          * rectangle.  Note that since the dialog cannot be selected
  628.          * along with other controls, this handles the case where the
  629.          * dialog is selected.
  630.          */
  631.         grcSelected = gnpcSel->rc;
  632.         gnOverHang = GetOverHang(gnpcSel->pwcd->iType,
  633.                 gnpcSel->rc.bottom - gnpcSel->rc.top);
  634.     }
  635.     else {
  636.         /*
  637.          * Seed the rectangle with impossible values.
  638.          */
  639.         SetRect(&grcSelected, 32000, 32000, -32000, -32000);
  640.         nBottomLowest = 0;
  641.  
  642.         /*
  643.          * Loop through all the controls, expanding the rectangle to
  644.          * fit around all the selected controls.
  645.          */
  646.         for (npc = npcHead; npc; npc = npc->npcNext) {
  647.             if (npc->fSelected) {
  648.                 if (npc->rc.left < grcSelected.left)
  649.                     grcSelected.left = npc->rc.left;
  650.  
  651.                 if (npc->rc.right > grcSelected.right)
  652.                     grcSelected.right = npc->rc.right;
  653.  
  654.                 if (npc->rc.top < grcSelected.top)
  655.                     grcSelected.top = npc->rc.top;
  656.  
  657.                 nBottom = npc->rc.bottom - GetOverHang(npc->pwcd->iType,
  658.                         npc->rc.bottom - npc->rc.top);
  659.                 if (nBottom > nBottomLowest)
  660.                     nBottomLowest = nBottom;
  661.  
  662.                 if (npc->rc.bottom > grcSelected.bottom)
  663.                     grcSelected.bottom = npc->rc.bottom;
  664.             }
  665.         }
  666.  
  667.         gnOverHang = grcSelected.bottom - nBottomLowest;
  668.     }
  669. }
  670.  
  671.  
  672.  
  673. /************************************************************************
  674. * CancelSelection
  675. *
  676. * This unselects all selected controls.
  677. *
  678. * Arguments:
  679. *   BOOL fUpdate - If TRUE, the status ribbon is updated.
  680. *
  681. ************************************************************************/
  682.  
  683. VOID CancelSelection(
  684.     BOOL fUpdate)
  685. {
  686.     if (gcSelected) {
  687.         while (gcSelected)
  688.             UnSelectControl(gnpcSel);
  689.  
  690.         if (fUpdate) {
  691.             StatusUpdate();
  692.             StatusSetEnable();
  693.         }
  694.     }
  695. }
  696.  
  697.  
  698.  
  699. /************************************************************************
  700. * OutlineSelectBegin
  701. *
  702. * This function begins an Outline Selection operation.  This will
  703. * draw a tracking rectangle on the screen that can be used to enclose
  704. * controls.  When the selection is completed, all the enclosed controls
  705. * will be selected.
  706. *
  707. * The x and y coordinates are relative to the dialog window, not it's
  708. * client.
  709. *
  710. * Arguments:
  711. *   INT x   - Starting X location (window coords).
  712. *   INT y   - Starting Y location (window coords).
  713. *
  714. ************************************************************************/
  715.  
  716. VOID OutlineSelectBegin(
  717.     INT x,
  718.     INT y)
  719. {
  720.     /*
  721.      * Always be sure the focus is where we want it, not on something
  722.      * like the status ribbon or "Esc" won't work to cancel the tracking.
  723.      */
  724.     SetFocus(ghwndMain);
  725.  
  726.     /*
  727.      * Remember the starting point.  It comes in coords relative to the
  728.      * window, and the DC we are getting is one for the dialog's client,
  729.      * so we have to map it from window coords to the client's coords.
  730.      */
  731.     gptOutlineSelect.x = x;
  732.     gptOutlineSelect.y = y;
  733.     MapDlgClientPoint(&gptOutlineSelect, FALSE);
  734.  
  735.     gState = STATE_SELECTING;
  736.     ghwndTrackOver = gcd.npc->hwnd;
  737.     ghDCTrack = GetDC(ghwndTrackOver);
  738.     SetROP2(ghDCTrack, R2_NOT);
  739.  
  740.     /*
  741.      * Get the rectangle for the client area of the dialog.  This is
  742.      * used to limit the tracking.
  743.      */
  744.     GetClientRect(gcd.npc->hwnd, &grcOutlineSelectLimit);
  745.     OutlineSelectDraw(x, y);
  746.  
  747.     /*
  748.      * The mouse messages from now on will go to the dialog window,
  749.      * so that the following routines can know that the mouse movement
  750.      * points are relative to that window.
  751.      */
  752.     SetCapture(gcd.npc->hwnd);
  753.  
  754.     SetCursor(hcurOutSel);
  755. }
  756.  
  757.  
  758.  
  759. /************************************************************************
  760. * OutlineSelectDraw
  761. *
  762. * This routine draws the outline selection rectangle.  It is assumed
  763. * that the window has been locked for update appropriately or this
  764. * could leave garbage around.  The outline selection rectangle is
  765. * drawn from the starting point in gptOutlineSelect to the given
  766. * x,y location.
  767. *
  768. * Arguments:
  769. *   INT x   - Mouse X location (window coords relative to the dialog).
  770. *   INT y   - Mouse Y location (window coords relative to the dialog).
  771. *
  772. ************************************************************************/
  773.  
  774. VOID OutlineSelectDraw(
  775.     INT x,
  776.     INT y)
  777. {
  778.     OutlineSelectHide();
  779.     OutlineSelectSetRect(x, y);
  780.     MyFrameRect(ghDCTrack, &grcOutlineSelect, DSTINVERT);
  781.     gfOutlineSelectShown = TRUE;
  782. }
  783.  
  784.  
  785.  
  786. /************************************************************************
  787. * OutlineSelectHide
  788. *
  789. * This routine hides the current outline selection rectangle.
  790. *
  791. ************************************************************************/
  792.  
  793. STATICFN VOID OutlineSelectHide(VOID)
  794. {
  795.     if (gfOutlineSelectShown) {
  796.         MyFrameRect(ghDCTrack, &grcOutlineSelect, DSTINVERT);
  797.         gfOutlineSelectShown = FALSE;
  798.     }
  799. }
  800.  
  801.  
  802.  
  803. /************************************************************************
  804. * OutlineSelectSetRect
  805. *
  806. * This function takes an x,y point and makes a rectangle that goes
  807. * from this point to the starting outline selection point.  The cases
  808. * are handled where the new point has negative coordinates relative
  809. * to the starting point, and the rectangle produced is limited to
  810. * the rectangle in grcOutlineSelectLimit.
  811. *
  812. * The rectangle is placed into the global grcOutlineSelect.  The
  813. * global point gwptOutlineSelect is assumed to have previously been
  814. * set to the starting point of the outline selection.
  815. *
  816. * The x and y coordinates are relative to the dialog window, not it's
  817. * client.
  818. *
  819. * Arguments:
  820. *   INT x   - Mouse X location (window coords relative to the dialog).
  821. *   INT y   - Mouse Y location (window coords relative to the dialog).
  822. *
  823. ************************************************************************/
  824.  
  825. STATICFN VOID OutlineSelectSetRect(
  826.     INT x,
  827.     INT y)
  828. {
  829.     POINT pt;
  830.  
  831.     /*
  832.      * The point is coming in relative to the upper left of the
  833.      * dialog window.  It needs to be mapped to points relative
  834.      * to the client of the dialog window.
  835.      */
  836.     pt.x = x;
  837.     pt.y = y;
  838.     MapDlgClientPoint(&pt, FALSE);
  839.  
  840.     if (pt.x > gptOutlineSelect.x) {
  841.         grcOutlineSelect.left = gptOutlineSelect.x;
  842.         grcOutlineSelect.right = pt.x;
  843.     }
  844.     else {
  845.         grcOutlineSelect.left = pt.x;
  846.         grcOutlineSelect.right = gptOutlineSelect.x;
  847.     }
  848.  
  849.     if (pt.y > gptOutlineSelect.y) {
  850.         grcOutlineSelect.top = gptOutlineSelect.y;
  851.         grcOutlineSelect.bottom = pt.y;
  852.     }
  853.     else {
  854.         grcOutlineSelect.top = pt.y;
  855.         grcOutlineSelect.bottom = gptOutlineSelect.y;
  856.     }
  857.  
  858.     if (grcOutlineSelect.left < grcOutlineSelectLimit.left)
  859.         grcOutlineSelect.left = grcOutlineSelectLimit.left;
  860.  
  861.     if (grcOutlineSelect.right > grcOutlineSelectLimit.right)
  862.         grcOutlineSelect.right = grcOutlineSelectLimit.right;
  863.  
  864.     if (grcOutlineSelect.top < grcOutlineSelectLimit.top)
  865.         grcOutlineSelect.top = grcOutlineSelectLimit.top;
  866.  
  867.     if (grcOutlineSelect.bottom > grcOutlineSelectLimit.bottom)
  868.         grcOutlineSelect.bottom = grcOutlineSelectLimit.bottom;
  869. }
  870.  
  871.  
  872.  
  873. /************************************************************************
  874. * OutlineSelectCancel
  875. *
  876. * This routine is used to cancel the display of the outline selection
  877. * rectangle.
  878. *
  879. ************************************************************************/
  880.  
  881. VOID OutlineSelectCancel(VOID)
  882. {
  883.     OutlineSelectHide();
  884.     ReleaseDC(ghwndTrackOver, ghDCTrack);
  885.  
  886.     gState = STATE_NORMAL;
  887.     ReleaseCapture();
  888.     SetCursor(hcurArrow);
  889. }
  890.  
  891.  
  892.  
  893. /************************************************************************
  894. * OutlineSelectEnd
  895. *
  896. * This function completes an outline selection operation.  All the
  897. * enclosed controls will be selected.  If the Shift key is down,
  898. * the enclosed controls will be added to the selection, otherwise the
  899. * current selection will be cancelled first.
  900. *
  901. * The current selection will only be cancelled if there is
  902. * at least one new control enclosed.  This is so that simply clicking and
  903. * releasing the mouse without enclosing any controls leaves the current
  904. * selection alone.
  905. *
  906. * Arguments:
  907. *   INT x   - Mouse X location (dialog client coords).
  908. *   INT y   - Mouse Y location (dialog client coords).
  909. *
  910. ************************************************************************/
  911.  
  912. VOID OutlineSelectEnd(
  913.     INT x,
  914.     INT y)
  915. {
  916.     NPCTYPE npc;
  917.     BOOL fFirstOne = TRUE;
  918.  
  919.     OutlineSelectCancel();
  920.     OutlineSelectSetRect(x, y);
  921.  
  922.     /*
  923.      * If the mouse was not moved at all, consider this a request
  924.      * to select the dialog instead of an outline selection operation.
  925.      */
  926.     if (grcOutlineSelect.left == grcOutlineSelect.right &&
  927.             grcOutlineSelect.top == grcOutlineSelect.bottom) {
  928.         SelectControl(gcd.npc, FALSE);
  929.         return;
  930.     }
  931.  
  932.     /*
  933.      * Convert the selected rectangle to dialog units.
  934.      */
  935.     WinToDURect(&grcOutlineSelect);
  936.  
  937.     for (npc = npcHead; npc; npc = npc->npcNext) {
  938.         /*
  939.          * Do the rectangles intersect?
  940.          */
  941.         if (npc->rc.left < grcOutlineSelect.right &&
  942.                 grcOutlineSelect.left < npc->rc.right &&
  943.                 npc->rc.bottom > grcOutlineSelect.top &&
  944.                 grcOutlineSelect.bottom > npc->rc.top) {
  945.             if (fFirstOne) {
  946.                 /*
  947.                  * If the Shift key is not held down, cancel any outstanding
  948.                  * selections.
  949.                  */
  950.                 if (!(GetKeyState(VK_SHIFT) & 0x8000))
  951.                     CancelSelection(FALSE);
  952.  
  953.                 fFirstOne = FALSE;
  954.             }
  955.  
  956.             /*
  957.              * If the control is not selected, select it.
  958.              */
  959.             if (!npc->fSelected)
  960.                 SelectControl2(npc, TRUE);
  961.         }
  962.     }
  963.  
  964.     /*
  965.      * Update some things if there was at least one control enclosed.
  966.      */
  967.     if (!fFirstOne) {
  968.         SetAnchorToFirstSel(TRUE);
  969.         StatusUpdate();
  970.         StatusSetEnable();
  971.         RedrawSelection();
  972.         CalcSelectedRect();
  973.     }
  974. }
  975.  
  976.  
  977.  
  978. /************************************************************************
  979. * MyFrameRect
  980. *
  981. * This function draws a one-pixel width rectangle using the given
  982. * raster operation.
  983. *
  984. * Arguments:
  985. *   HDC hDC     - DC to use.
  986. *   PRECT prc   - Rectangle to draw the frame around.
  987. *   DWORD dwRop - RasterOp to use (DSTINVERT, BLACKNESS, etc.).
  988. *
  989. ************************************************************************/
  990.  
  991. VOID MyFrameRect(
  992.     HDC hDC,
  993.     PRECT prc,
  994.     DWORD dwRop)
  995. {
  996.     INT x;
  997.     INT y;
  998.     POINT pt;
  999.  
  1000.     x = prc->right  - (pt.x = prc->left);
  1001.     y = prc->bottom - (pt.y = prc->top);
  1002.  
  1003.     PatBlt(hDC, pt.x, pt.y, x, 1, dwRop);
  1004.     pt.y = prc->bottom - 1;
  1005.     PatBlt(hDC, pt.x, pt.y, x, 1, dwRop);
  1006.     pt.y = prc->top;
  1007.     PatBlt(hDC, pt.x, pt.y, 1, y, dwRop);
  1008.     pt.x = prc->right - 1;
  1009.     PatBlt(hDC, pt.x, pt.y, 1, y, dwRop);
  1010. }
  1011.  
  1012.  
  1013.  
  1014. /************************************************************************
  1015. * MoveControl
  1016. *
  1017. * This function moves the current control to the next grid boundary in
  1018. * the specified direction.
  1019. *
  1020. * Arguments:
  1021. *   WPARAM vKey - Virtual key code that was pressed.
  1022. *
  1023. ************************************************************************/
  1024.  
  1025. VOID MoveControl(
  1026.     WPARAM vKey)
  1027. {
  1028.     RECT rc;
  1029.     INT dx;
  1030.     INT dy;
  1031.  
  1032.     /*
  1033.      * Nothing is selected.
  1034.      */
  1035.     if (!gcSelected)
  1036.         return;
  1037.  
  1038.     rc = grcSelected;
  1039.  
  1040.     switch (vKey) {
  1041.         case VK_UP:
  1042.             dx = 0;
  1043.             if (!(dy = -(rc.top % gcyGrid)))
  1044.                 dy = -gcyGrid;
  1045.             break;
  1046.  
  1047.         case VK_DOWN:
  1048.             dx = 0;
  1049.             dy = gcyGrid - (rc.top % gcyGrid);
  1050.             break;
  1051.  
  1052.         case VK_RIGHT:
  1053.             dx = gcxGrid - (rc.left % gcxGrid);
  1054.             dy = 0;
  1055.             break;
  1056.  
  1057.         case VK_LEFT:
  1058.             if (!(dx = -(rc.left % gcxGrid)))
  1059.                 dx = -gcxGrid;
  1060.             dy = 0;
  1061.             break;
  1062.     }
  1063.  
  1064.     OffsetRect(&rc, dx, dy);
  1065.     FitRectToBounds(&rc, gnOverHang, DRAG_CENTER, gfDlgSelected);
  1066.     gHandleHit = DRAG_CENTER;
  1067.     PositionControl(&rc);
  1068. }
  1069.  
  1070.  
  1071.  
  1072. /************************************************************************
  1073. * PositionControl
  1074. *
  1075. * This function positions and sizes the current control.  Both the control
  1076. * window and its associated drag window are moved at the same time.  The
  1077. * coordinates in the control, and the status window display are updated.
  1078. * The given rectangle is in dialog units, and it should have already been
  1079. * range limited and grid aligned as appropriate.
  1080. *
  1081. * Arguments:
  1082. *   NPWRECT nprc - The rectangle to size/position the control with.
  1083. *
  1084. ************************************************************************/
  1085.  
  1086. VOID PositionControl(
  1087.     PRECT prc)
  1088. {
  1089.     INT cx;
  1090.     INT cy;
  1091.     RECT rcT;
  1092.     NPCTYPE npcT;
  1093.     HANDLE hwpi;
  1094.  
  1095.     if (gcSelected == 1) {
  1096.         /*
  1097.          * Did nothing change?
  1098.          */
  1099.         if (EqualRect(prc, &gnpcSel->rc))
  1100.             return;
  1101.  
  1102.         /*
  1103.          * Only a single control is selected.  Move it.
  1104.          */
  1105.         PositionControl2(gnpcSel, prc, NULL);
  1106.  
  1107.         /*
  1108.          * Is the dialog selected, being sized (not just moved), and
  1109.          * does it have at least one control?
  1110.          */
  1111.         if (gfDlgSelected && gHandleHit != DRAG_CENTER && npcHead) {
  1112.             cx = prc->left - grcSelected.left;
  1113.             cy = prc->top - grcSelected.top;
  1114.  
  1115.             /*
  1116.              * Did the top or left edge of the dialog move?
  1117.              */
  1118.             if (cx || cy) {
  1119.                 /*
  1120.                  * Loop through all the controls.  Move all of them by
  1121.                  * the dialog movement delta.
  1122.                  */
  1123.                 hwpi = BeginDeferWindowPos(cWindows * 2);
  1124.                 for (npcT = npcHead; npcT; npcT = npcT->npcNext) {
  1125.                     SetRect(&rcT, npcT->rc.left - cx, npcT->rc.top - cy,
  1126.                             npcT->rc.right - cx, npcT->rc.bottom - cy);
  1127.                     hwpi = PositionControl2(npcT, &rcT, hwpi);
  1128.                 }
  1129.                 EndDeferWindowPos(hwpi);
  1130.             }
  1131.         }
  1132.     }
  1133.     else {
  1134.         /*
  1135.          * Did nothing change?
  1136.          */
  1137.         if (EqualRect(prc, &grcSelected))
  1138.             return;
  1139.  
  1140.         /*
  1141.          * There is a group of controls selected.
  1142.          * Calculate how much the group rectangle was moved.
  1143.          * It is assumed that a group of controls cannot be
  1144.          * sized, only moved.
  1145.          */
  1146.         cx = prc->left - grcSelected.left;
  1147.         cy = prc->top - grcSelected.top;
  1148.  
  1149.         /*
  1150.          * Loop through all the controls.  Move all the selected
  1151.          * ones by the group delta.
  1152.          */
  1153.         hwpi = BeginDeferWindowPos(gcSelected * 2);
  1154.         for (npcT = npcHead; npcT; npcT = npcT->npcNext) {
  1155.             if (npcT->fSelected) {
  1156.                 SetRect(&rcT,
  1157.                         npcT->rc.left + cx, npcT->rc.top + cy,
  1158.                         npcT->rc.right + cx, npcT->rc.bottom + cy);
  1159.                 GridizeRect(&rcT,
  1160.                         GRIDIZE_LEFT | GRIDIZE_TOP | GRIDIZE_SAMESIZE);
  1161.                 FitRectToBounds(&rcT,
  1162.                         GetOverHang(npcT->pwcd->iType,
  1163.                         npcT->rc.bottom - npcT->rc.top),
  1164.                         DRAG_CENTER, gfDlgSelected);
  1165.                 hwpi = PositionControl2(npcT, &rcT, hwpi);
  1166.             }
  1167.         }
  1168.         EndDeferWindowPos(hwpi);
  1169.     }
  1170.  
  1171.     CalcSelectedRect();
  1172.     StatusSetCoords(&gnpcSel->rc);
  1173.     gfResChged = gfDlgChanged = TRUE;
  1174.     ShowFileStatus(FALSE);
  1175. }
  1176.  
  1177.  
  1178.  
  1179. /************************************************************************
  1180. * PositionControl2
  1181. *
  1182. * This function positions and sizes a single control.  Both the control
  1183. * window and its associated drag window are moved at the same time.  The
  1184. * coordinates in the control are updated.
  1185. *
  1186. * The given rectangle is in dialog units, and it should have already been
  1187. * range limited and grid aligned as appropriate.
  1188. *
  1189. * Arguments:
  1190. *   NPCTYPE npc - The control to position.
  1191. *   PRECT prc   - The rectangle to size/position the control with.
  1192. *   HANDLE hwpi - Handle that has been returned from a BeginDeferWindowPos
  1193. *                 call.  If this parameter is not NULL, the calls to
  1194. *                 position the control and drag window will use this
  1195. *                 handle.
  1196. *
  1197. * Returns:
  1198. *
  1199. * The return will be the hwpi handle that is currently being used.
  1200. * The variable that the caller is using must be updated with the
  1201. * return value each call, because each call to DeferWindowPos can
  1202. * possibly change this value.
  1203. *
  1204. ************************************************************************/
  1205.  
  1206. STATICFN HANDLE PositionControl2(
  1207.     NPCTYPE npc,
  1208.     PRECT prc,
  1209.     HANDLE hwpi)
  1210. {
  1211.     RECT rc;
  1212.     RECT rcDrag;
  1213.     HANDLE hwpi2;
  1214.  
  1215.     /*
  1216.      * Make a local copy of the rectangle.
  1217.      */
  1218.     rc = *prc;
  1219.  
  1220.     /*
  1221.      * Start calculating the new position.  Begin by mapping the dialog
  1222.      * points to window coordinates.
  1223.      */
  1224.     DUToWinRect(&rc);
  1225.  
  1226.     if (npc->pwcd->iType == W_DIALOG) {
  1227.         InvalidateDlgHandles();
  1228.  
  1229.         AdjustWindowRectEx(&rc, npc->flStyle, FALSE,
  1230.                 (npc->flStyle & DS_MODALFRAME) ?
  1231.                 npc->flExtStyle | WS_EX_DLGMODALFRAME : npc->flExtStyle);
  1232.         ClientToScreenRect(ghwndSubClient, &rc);
  1233.         MoveWindow(npc->hwnd, rc.left, rc.top,
  1234.                 rc.right - rc.left, rc.bottom - rc.top, TRUE);
  1235.  
  1236.         /*
  1237.          * Update the control structure with the new rectangle.
  1238.          */
  1239.         npc->rc = *prc;
  1240.  
  1241.         /*
  1242.          * Since this was the dialog that was just positioned, we need to
  1243.          * recalculate and save the size of its "client" area.
  1244.          */
  1245.         SaveDlgClientRect(npc->hwnd);
  1246.     }
  1247.     else {
  1248.         rcDrag = rc;
  1249.         InflateRect(&rcDrag, CHANDLESIZE / 2, CHANDLESIZE / 2);
  1250.  
  1251.         if (hwpi)
  1252.             hwpi2 = hwpi;
  1253.         else
  1254.             hwpi2 = BeginDeferWindowPos(2);
  1255.  
  1256.         hwpi2 = DeferWindowPos(hwpi2, npc->hwndDrag, NULL,
  1257.                 rcDrag.left, rcDrag.top,
  1258.                 rcDrag.right - rcDrag.left, rcDrag.bottom - rcDrag.top,
  1259.                 SWP_NOACTIVATE | SWP_NOZORDER);
  1260.  
  1261.         hwpi2 = DeferWindowPos(hwpi2, npc->hwnd, NULL, rc.left, rc.top,
  1262.                 rc.right - rc.left, rc.bottom - rc.top,
  1263.                 SWP_NOACTIVATE | SWP_NOZORDER);
  1264.  
  1265.         if (!hwpi)
  1266.             EndDeferWindowPos(hwpi2);
  1267.  
  1268.         /*
  1269.          * Update the control structure with the new rectangle.
  1270.          */
  1271.         npc->rc = *prc;
  1272.  
  1273.         InvalidateRect(npc->hwndDrag, NULL, TRUE);
  1274.     }
  1275.  
  1276.     return hwpi2;
  1277. }
  1278.  
  1279.  
  1280.  
  1281. /************************************************************************
  1282. * RepositionDialog
  1283. *
  1284. * This routine forces the dialog to be moved to the location that
  1285. * is stored in it's npc rectangle.  This is necessary after the
  1286. * main application has been moved, because the dialog is relative
  1287. * to the app's window and does not automatically move with it.
  1288. *
  1289. ************************************************************************/
  1290.  
  1291. VOID RepositionDialog(VOID)
  1292. {
  1293.     PositionControl2(gcd.npc, &gcd.npc->rc, NULL);
  1294. }
  1295.  
  1296.  
  1297.  
  1298. /************************************************************************
  1299. * SaveDlgClientRect
  1300. *
  1301. * This routine saves away a global that will contain the offset, in window
  1302. * coordinates, of the origin of the "client" area for the current dialog.
  1303. *
  1304. * Arguments:
  1305. *   HWND hwndDlg - The dialog window.
  1306. *
  1307. ************************************************************************/
  1308.  
  1309. VOID SaveDlgClientRect(
  1310.     HWND hwndDlg)
  1311. {
  1312.     RECT rcFrame;
  1313.     POINT pt;
  1314.  
  1315.     GetWindowRect(hwndDlg, &rcFrame);
  1316.     GetClientRect(hwndDlg, &grcDlgClient);
  1317.     pt.x = pt.y = 0;
  1318.     ClientToScreen(hwndDlg, &pt);
  1319.     OffsetRect(&grcDlgClient, pt.x - rcFrame.left, pt.y - rcFrame.top);
  1320. }
  1321.  
  1322.  
  1323.  
  1324. /************************************************************************
  1325. * SizeToText
  1326. *
  1327. * This function will size all the selected controls to fit their text.
  1328. * This is to support the "Size to text" command of the "Edit" menu.
  1329. *
  1330. * Globals are updated appropriately if anything actually had to change.
  1331. *
  1332. ************************************************************************/
  1333.  
  1334. VOID SizeToText(VOID)
  1335. {
  1336.     NPCTYPE npc;
  1337.     BOOL fSized = FALSE;
  1338.  
  1339.     /*
  1340.      * Loop through all the controls.
  1341.      */
  1342.     for (npc = npcHead; npc; npc = npc->npcNext) {
  1343.         /*
  1344.          * Is the control selected, and can it be sized to its text?
  1345.          */
  1346.         if (npc->fSelected && npc->pwcd->fSizeToText)
  1347.             fSized |= SizeCtrlToText(npc);
  1348.     }
  1349.  
  1350.     /*
  1351.      * Was anything modified?
  1352.      */
  1353.     if (fSized) {
  1354.         CalcSelectedRect();
  1355.         StatusSetCoords(&gnpcSel->rc);
  1356.         gfResChged = gfDlgChanged = TRUE;
  1357.         ShowFileStatus(FALSE);
  1358.     }
  1359. }
  1360.  
  1361.  
  1362.  
  1363. /************************************************************************
  1364. * SizeCtrlToText
  1365. *
  1366. * This function sizes a single control so that it just fits its text.
  1367. * This does not make sense for all controls (see the fSizeToText flag
  1368. * in the awcd array), and there are different rules for the different
  1369. * types of controls.
  1370. *
  1371. * Arguments:
  1372. *   NPCTYPE npc - The control to size.
  1373. *
  1374. * Returns:
  1375. *
  1376. * The return value is TRUE if the control was modified (sized) or
  1377. * FALSE if it was not.
  1378. *
  1379. ************************************************************************/
  1380.  
  1381. STATICFN BOOL SizeCtrlToText(
  1382.     NPCTYPE npc)
  1383. {
  1384.     RECT rc;
  1385.     INT x;
  1386.     INT cxLowern;
  1387.  
  1388.     switch (npc->pwcd->iType) {
  1389.         case W_CHECKBOX:
  1390.             /*
  1391.              * Take the width of the text, plus some pixels for the square.
  1392.              */
  1393.             x = QueryTextExtent(npc->hwnd, npc->text, TRUE) + 20;
  1394.             x = MulDiv(x, 4, gcd.cxChar) + 1;
  1395.             break;
  1396.  
  1397.         case W_PUSHBUTTON:
  1398.             /*
  1399.              * The UITF definition of the size of a pushbutton says
  1400.              * that the left and right margins should be approximately
  1401.              * the width of a lowercase "n".  In any event, the width
  1402.              * cannot be less than the default size.
  1403.              */
  1404.             cxLowern = QueryTextExtent(npc->hwnd, L"n", FALSE);
  1405.             x = QueryTextExtent(npc->hwnd, npc->text, FALSE) + (2 * cxLowern);
  1406.             x = MulDiv(x, 4, gcd.cxChar);
  1407.  
  1408.             if (x < awcd[W_PUSHBUTTON].cxDefault)
  1409.                 x = awcd[W_PUSHBUTTON].cxDefault;
  1410.  
  1411.             break;
  1412.  
  1413.         case W_RADIOBUTTON:
  1414.             /*
  1415.              * Take the width of the text, plus some for the circle.
  1416.              */
  1417.             x = QueryTextExtent(npc->hwnd, npc->text, TRUE) + 20;
  1418.             x = MulDiv(x, 4, gcd.cxChar) + 1;
  1419.             break;
  1420.  
  1421.         case W_TEXT:
  1422.             /*
  1423.              * Take the width of the text.
  1424.              */
  1425.             x = QueryTextExtent(npc->hwnd, npc->text, TRUE);
  1426.             x = MulDiv(x, 4, gcd.cxChar) + 1;
  1427.             break;
  1428.  
  1429.         case W_CUSTOM:
  1430.             /*
  1431.              * Call out to the custom control and let it decide
  1432.              * how wide the text should be.
  1433.              */
  1434.             x = CallCustomSizeToText(npc);
  1435.             break;
  1436.  
  1437.         default:
  1438.             x = -1;
  1439.             break;
  1440.     }
  1441.  
  1442.     /*
  1443.      * Does it need to be sized?
  1444.      */
  1445.     if (x != -1 && x != npc->rc.right - npc->rc.left) {
  1446.         /*
  1447.          * Now that we know the size we want the control, position
  1448.          * it to change that size.  Note that we do NOT gridize
  1449.          * the left edge here.  The user probably just wants the
  1450.          * right edge of the control to be adjusted to fit the new
  1451.          * text, and probably does not want the left edge shifting
  1452.          * on them.
  1453.          */
  1454.         rc = npc->rc;
  1455.         rc.right = rc.left + x;
  1456.         FitRectToBounds(&rc,
  1457.                 GetOverHang(npc->pwcd->iType, npc->rc.bottom - npc->rc.top),
  1458.                 DRAG_CENTER, gfDlgSelected);
  1459.         PositionControl2(npc, &rc, NULL);
  1460.  
  1461.         return TRUE;
  1462.     }
  1463.  
  1464.     return FALSE;
  1465. }
  1466.  
  1467.  
  1468.  
  1469. /************************************************************************
  1470. * QueryTextExtent
  1471. *
  1472. * This function takes a window handle and text, and will return the
  1473. * number of pixels that the specified text is wide in that window.
  1474. * It is used to determine how wide a control needs to be to display
  1475. * its text.
  1476. *
  1477. * The font set into the current dialog is taken into consideration
  1478. * when calculating the size.
  1479. *
  1480. * Arguments:
  1481. *   HWND hwnd       - The control window handle.
  1482. *   LPTSTR pszText  - The text of the control.
  1483. *   BOOL fWordBreak - TRUE if this text will be drawn with DT_WORDBREAK.
  1484. *
  1485. * Returns:
  1486. *
  1487. * The number of pixels wide the selected text is.
  1488. *
  1489. ************************************************************************/
  1490.  
  1491. STATICFN INT QueryTextExtent(
  1492.     HWND hwnd,
  1493.     LPTSTR pszText,
  1494.     BOOL fWordBreak)
  1495. {
  1496.     HDC hDC;
  1497.     INT iHeight;
  1498.     RECT rc;
  1499.     INT nLen;
  1500.     HFONT hfontOld;
  1501.  
  1502.     if (!pszText || *pszText == CHAR_NULL)
  1503.         return 0;
  1504.  
  1505.     hDC = GetDC(hwnd);
  1506.  
  1507.     /*
  1508.      * If there is a valid font, select it into the DC.  Note that
  1509.      * we look at gcd.hFont instead of gcd.fFontSpecified, because
  1510.      * it is possible to specify a font for the dialog but not have
  1511.      * been able to create it.
  1512.      */
  1513.     if (gcd.hFont)
  1514.         hfontOld = SelectObject(hDC, gcd.hFont);
  1515.  
  1516.     /*
  1517.      * First, calculate the length of the text.
  1518.      */
  1519.     rc.left = rc.top = 0;
  1520.     rc.right = 10000;
  1521.     rc.bottom = 10000;
  1522.     nLen = lstrlen(pszText);
  1523.     DrawText(hDC, pszText, nLen, &rc,
  1524.             DT_CALCRECT | DT_NOCLIP | DT_EXPANDTABS);
  1525.  
  1526.     /*
  1527.      * First save the height of the line.  This works because the
  1528.      * DrawText call above with DT_CALCRECT will always draw on
  1529.      * a single line.  Then we move the upwards the rectangle to draw
  1530.      * in a large amount, so that it is outside the dimensions of
  1531.      * the control.  Finally, we do a real draw of the text,
  1532.      * increasing the width a little each time until we are able
  1533.      * to draw entirely on one line.  This is inefficient, but it does
  1534.      * ensure that the returned width will be enough to actually
  1535.      * draw the string.
  1536.      */
  1537.     if (fWordBreak) {
  1538.         iHeight = rc.bottom - rc.top;
  1539.         rc.top -= 10000;
  1540.         rc.bottom -= 10000;
  1541.         while (TRUE) {
  1542.             /*
  1543.              * Determine if we have enough width to draw on a single
  1544.              * line yet.
  1545.              */
  1546.             if (DrawText(hDC, pszText, nLen, &rc,
  1547.                     DT_NOCLIP | DT_EXPANDTABS | DT_WORDBREAK) == iHeight)
  1548.                 break;
  1549.  
  1550.             /*
  1551.              * Nope, push the right margin out and try again...
  1552.              */
  1553.             rc.right++;
  1554.         }
  1555.     }
  1556.  
  1557.     if (gcd.hFont)
  1558.         SelectObject(hDC, hfontOld);
  1559.  
  1560.     ReleaseDC(hwnd, hDC);
  1561.  
  1562.     return rc.right - rc.left;
  1563. }
  1564.  
  1565.  
  1566.  
  1567. /************************************************************************
  1568. * AlignControls
  1569. *
  1570. * This function will align all the selected controls.  The point to
  1571. * align to is always taken from the currently selected control.
  1572. *
  1573. * In all cases, the resulting desired position of the control will be
  1574. * gridized and limited to the dialogs "client" area.  The size of the
  1575. * controls will not be changed.
  1576. *
  1577. * Arguments:
  1578. *   INT cmd - The alignment menu command.
  1579. *
  1580. *   The following are valid values for cmd:
  1581. *
  1582. *   MENU_ALIGNLEFT      - Align to the left edge.
  1583. *   MENU_ALIGNVERT      - Align to the center vertically.
  1584. *   MENU_ALIGNRIGHT     - Align to the right edge.
  1585. *   MENU_ALIGNTOP       - Align to the top edge.
  1586. *   MENU_ALIGNHORZ      - Align to the center horizontally.
  1587. *   MENU_ALIGNBOTTOM    - Align to the bottom edge.
  1588. *
  1589. ************************************************************************/
  1590.  
  1591. VOID AlignControls(
  1592.     INT cmd)
  1593. {
  1594.     register INT sDelta;
  1595.     NPCTYPE npc;
  1596.     RECT rc;
  1597.     BOOL fMove;
  1598.     BOOL fModified = FALSE;
  1599.  
  1600.     /*
  1601.      * Loop through all the controls.  Align all the selected ones.
  1602.      */
  1603.     for (npc = npcHead; npc; npc = npc->npcNext) {
  1604.         if (npc->fSelected && npc != gnpcSel) {
  1605.             rc = npc->rc;
  1606.             fMove = FALSE;
  1607.  
  1608.             switch (cmd) {
  1609.                 case MENU_ALIGNLEFT:
  1610.                     if (sDelta = gnpcSel->rc.left - rc.left) {
  1611.                         fMove = TRUE;
  1612.                         rc.left += sDelta;
  1613.                         rc.right += sDelta;
  1614.                     }
  1615.  
  1616.                     break;
  1617.  
  1618.                 case MENU_ALIGNVERT:
  1619.                     if (sDelta =
  1620.                             (((gnpcSel->rc.right - gnpcSel->rc.left) / 2)
  1621.                             + gnpcSel->rc.left) -
  1622.                             (((rc.right - rc.left) / 2) +
  1623.                             rc.left)) {
  1624.                         fMove = TRUE;
  1625.                         rc.left += sDelta;
  1626.                         rc.right += sDelta;
  1627.                     }
  1628.  
  1629.                     break;
  1630.  
  1631.                 case MENU_ALIGNRIGHT:
  1632.                     if (sDelta = gnpcSel->rc.right - rc.right) {
  1633.                         fMove = TRUE;
  1634.                         rc.left += sDelta;
  1635.                         rc.right += sDelta;
  1636.                     }
  1637.  
  1638.                     break;
  1639.  
  1640.                 case MENU_ALIGNTOP:
  1641.                     if (sDelta = gnpcSel->rc.top - rc.top) {
  1642.                         fMove = TRUE;
  1643.                         rc.top += sDelta;
  1644.                         rc.bottom += sDelta;
  1645.                     }
  1646.  
  1647.                     break;
  1648.  
  1649.                 case MENU_ALIGNHORZ:
  1650.                     if (sDelta =
  1651.                             (((gnpcSel->rc.bottom - gnpcSel->rc.top) / 2)
  1652.                             + gnpcSel->rc.top) -
  1653.                             (((rc.bottom - rc.top) / 2) +
  1654.                             rc.top)) {
  1655.                         fMove = TRUE;
  1656.                         rc.top += sDelta;
  1657.                         rc.bottom += sDelta;
  1658.                     }
  1659.  
  1660.                     break;
  1661.  
  1662.                 case MENU_ALIGNBOTTOM:
  1663.                     if (sDelta = gnpcSel->rc.bottom - rc.bottom) {
  1664.                         fMove = TRUE;
  1665.                         rc.top += sDelta;
  1666.                         rc.bottom += sDelta;
  1667.                     }
  1668.  
  1669.                     break;
  1670.             }
  1671.  
  1672.             if (fMove) {
  1673.                 GridizeRect(&rc,
  1674.                         GRIDIZE_LEFT | GRIDIZE_TOP | GRIDIZE_SAMESIZE);
  1675.                 FitRectToBounds(&rc, GetOverHang(npc->pwcd->iType,
  1676.                         npc->rc.bottom - npc->rc.top),
  1677.                         DRAG_CENTER, FALSE);
  1678.  
  1679.                 if (!EqualRect(&rc, &npc->rc)) {
  1680.                     PositionControl2(npc, &rc, NULL);
  1681.                     fModified = TRUE;
  1682.                 }
  1683.             }
  1684.         }
  1685.     }
  1686.  
  1687.     if (fModified) {
  1688.         RedrawSelection();
  1689.         CalcSelectedRect();
  1690.         gfResChged = gfDlgChanged = TRUE;
  1691.         ShowFileStatus(FALSE);
  1692.         StatusUpdate();
  1693.     }
  1694. }
  1695.  
  1696.  
  1697.  
  1698. /************************************************************************
  1699. * ArrangeSpacing
  1700. *
  1701. * This function will evenly space all the selected controls.  The
  1702. * currently selected control is not moved (unless it has to be gridized)
  1703. * and any previous controls in Z order will be evenly spaced to the
  1704. * left or above the anchor, and all controls following in Z order will
  1705. * be evenly spaced below or to the right of the anchor.
  1706. *
  1707. * The spacing values used are gxSpace and gySpace.
  1708. *
  1709. * In all cases, the resulting desired position of the control will be
  1710. * gridized and limited to the dialogs "client" area.  The size of the
  1711. * controls is not changed.
  1712. *
  1713. * Arguments:
  1714. *   INT cmd - The Arrange/Even spacing menu command.
  1715. *
  1716. *   The following are valid values for cmd:
  1717. *
  1718. *   MENU_SPACEHORZ - Space the controls left and right.
  1719. *   MENU_SPACEVERT - Space all the controls up and down.
  1720. *
  1721. ************************************************************************/
  1722.  
  1723. VOID ArrangeSpacing(
  1724.     INT cmd)
  1725. {
  1726.     NPCTYPE npc;
  1727.     RECT rc;
  1728.     BOOL fModified = FALSE;
  1729.     INT x;
  1730.     INT y;
  1731.     INT cPreceding;
  1732.     INT xTotalWidth;
  1733.     INT yTotalWidth;
  1734.  
  1735.     cPreceding = 0;
  1736.     xTotalWidth = 0;
  1737.     yTotalWidth = 0;
  1738.     for (npc = npcHead; npc; npc = npc->npcNext) {
  1739.         if (npc->fSelected) {
  1740.             if (npc == gnpcSel)
  1741.                 break;
  1742.  
  1743.             cPreceding++;
  1744.             xTotalWidth += npc->rc.right - npc->rc.left;
  1745.             yTotalWidth += npc->rc.bottom - npc->rc.top;
  1746.         }
  1747.     }
  1748.  
  1749.     x = gnpcSel->rc.left;
  1750.     y = gnpcSel->rc.top;
  1751.  
  1752.     if (cPreceding) {
  1753.         x -= xTotalWidth + (gxSpace * cPreceding);
  1754.         y -= yTotalWidth + (gySpace * cPreceding);
  1755.     }
  1756.  
  1757.     /*
  1758.      * Loop through all the controls.  Space all the selected ones.
  1759.      */
  1760.     for (npc = npcHead; npc; npc = npc->npcNext) {
  1761.         if (npc->fSelected) {
  1762.             rc = npc->rc;
  1763.  
  1764.             switch (cmd) {
  1765.                 case MENU_SPACEVERT:
  1766.                     rc.top = y;
  1767.                     rc.bottom = y + (npc->rc.bottom - npc->rc.top);
  1768.                     y = rc.bottom + gySpace;
  1769.                     break;
  1770.  
  1771.                 case MENU_SPACEHORZ:
  1772.                     rc.left = x;
  1773.                     rc.right = x + (npc->rc.right - npc->rc.left);
  1774.                     x = rc.right + gxSpace;
  1775.                     break;
  1776.             }
  1777.  
  1778.             GridizeRect(&rc, GRIDIZE_LEFT | GRIDIZE_TOP | GRIDIZE_SAMESIZE);
  1779.             FitRectToBounds(&rc, GetOverHang(npc->pwcd->iType,
  1780.                     npc->rc.bottom - npc->rc.top),
  1781.                     DRAG_CENTER, FALSE);
  1782.  
  1783.             if (!EqualRect(&rc, &npc->rc)) {
  1784.                 PositionControl2(npc, &rc, NULL);
  1785.                 fModified = TRUE;
  1786.             }
  1787.         }
  1788.     }
  1789.  
  1790.     if (fModified) {
  1791.         RedrawSelection();
  1792.         CalcSelectedRect();
  1793.         gfResChged = gfDlgChanged = TRUE;
  1794.         ShowFileStatus(FALSE);
  1795.         StatusUpdate();
  1796.     }
  1797. }
  1798.  
  1799.  
  1800.  
  1801. /************************************************************************
  1802. * ArrangeSize
  1803. *
  1804. * This function will evenly size all the selected controls.  The
  1805. * currently selected control determines the size that the other
  1806. * controls will be set to in the given dimension.
  1807. *
  1808. * In all cases, the resulting size of the control will be gridized and
  1809. * limited to the dialogs "client" area.
  1810. *
  1811. * Arguments:
  1812. *   INT cmd - The Arrange/Same size menu command.
  1813. *
  1814. * The following are valid values for cmd:
  1815. *
  1816. *   MENU_ARRSIZEWIDTH  - Size the widths of the controls.
  1817. *   MENU_ARRSIZEHEIGHT - Size the heights of the controls.
  1818. *
  1819. ************************************************************************/
  1820.  
  1821. VOID ArrangeSize(
  1822.     INT cmd)
  1823. {
  1824.     NPCTYPE npc;
  1825.     RECT rc;
  1826.     BOOL fModified = FALSE;
  1827.     INT cx;
  1828.     INT cy;
  1829.  
  1830.     cx = gnpcSel->rc.right - gnpcSel->rc.left;
  1831.     cy = gnpcSel->rc.bottom - gnpcSel->rc.top;
  1832.  
  1833.     /*
  1834.      * Loop through all the controls, operating on the selected ones.
  1835.      */
  1836.     for (npc = npcHead; npc; npc = npc->npcNext) {
  1837.         /*
  1838.          * Is the control selected, and is it sizeable?
  1839.          */
  1840.         if (npc->fSelected && npc->pwcd->fSizeable) {
  1841.             rc = npc->rc;
  1842.  
  1843.             switch (cmd) {
  1844.                 case MENU_ARRSIZEWIDTH:
  1845.                     rc.right = npc->rc.left + cx;
  1846.                     break;
  1847.  
  1848.                 case MENU_ARRSIZEHEIGHT:
  1849.                     rc.top = npc->rc.bottom - cy;
  1850.                     break;
  1851.             }
  1852.  
  1853.             GridizeRect(&rc, GRIDIZE_LEFT | GRIDIZE_TOP |
  1854.                     GRIDIZE_RIGHT | GRIDIZE_BOTTOM);
  1855.             FitRectToBounds(&rc, GetOverHang(npc->pwcd->iType,
  1856.                     npc->rc.bottom - npc->rc.top),
  1857.                     DRAG_CENTER, FALSE);
  1858.  
  1859.             if (!EqualRect(&rc, &npc->rc)) {
  1860.                 PositionControl2(npc, &rc, NULL);
  1861.                 fModified = TRUE;
  1862.             }
  1863.         }
  1864.     }
  1865.  
  1866.     if (fModified) {
  1867.         RedrawSelection();
  1868.         CalcSelectedRect();
  1869.         gfResChged = gfDlgChanged = TRUE;
  1870.         ShowFileStatus(FALSE);
  1871.         StatusUpdate();
  1872.     }
  1873. }
  1874.  
  1875.  
  1876.  
  1877. /************************************************************************
  1878. * ArrangePushButtons
  1879. *
  1880. * This function will arrange push buttons along either the bottom of
  1881. * the dialog or along the right side of the dialog.  It will operate
  1882. * on the selected buttons (which button is currently selected does not
  1883. * matter) but this function can also be used if buttons are not selected
  1884. * in a couple of special cases.  If either the dialog or nothing is
  1885. * selected, ALL the push buttons in the dialog will be arranged.
  1886. *
  1887. * The margin values used are gxMargin and gyMargin, and the spacing
  1888. * between buttons is gxMinPushSpace, gxMaxPushSpace and gyPushSpace.
  1889. *
  1890. * In all cases, the resulting desired position of the buttons will be
  1891. * gridized and limited to the dialogs "client" area.  The size of the
  1892. * push buttons is not changed.
  1893. *
  1894. * Arguments:
  1895. *   INT cmd - The Arrange/Push buttons menu command.
  1896. *
  1897. *   The following are valid values for cmd:
  1898. *
  1899. *   MENU_ARRPUSHBOTTOM - Arrange push buttons along the bottom.
  1900. *   MENU_ARRPUSHRIGHT  - Arrange push buttons along the right side.
  1901. *
  1902. ************************************************************************/
  1903.  
  1904. VOID ArrangePushButtons(
  1905.     INT cmd)
  1906. {
  1907.     NPCTYPE npc;
  1908.     RECT rc;
  1909.     BOOL fModified = FALSE;
  1910.     INT x;                          // Note: These values must be signed.
  1911.     INT y;
  1912.     INT cxDlg;
  1913.     INT cButtons;
  1914.     INT xTotal;
  1915.     INT xInterSpace;
  1916.  
  1917.     switch (cmd) {
  1918.         case MENU_ARRPUSHBOTTOM:
  1919.             cxDlg = gcd.npc->rc.right - gcd.npc->rc.left;
  1920.             y = (gcd.npc->rc.bottom - gcd.npc->rc.top) - gyMargin;
  1921.  
  1922.             for (cButtons = 0, xTotal = 0, npc = npcHead; npc;
  1923.                     npc = npc->npcNext) {
  1924.                 if (npc->pwcd->iType == W_PUSHBUTTON &&
  1925.                         (!gcSelected || gfDlgSelected || npc->fSelected)) {
  1926.                     cButtons++;
  1927.                     xTotal += npc->rc.right - npc->rc.left;
  1928.                 }
  1929.             }
  1930.  
  1931.             if (cButtons == 1) {
  1932.                 x = (cxDlg - xTotal) / 2;
  1933.                 xInterSpace = 0;
  1934.             }
  1935.             else {
  1936.                 xInterSpace = (cxDlg - xTotal) / (cButtons + 1);
  1937.  
  1938.                 if (xInterSpace > gxMaxPushSpace)
  1939.                     xInterSpace = gxMaxPushSpace;
  1940.                 else if (xInterSpace < gxMinPushSpace)
  1941.                     xInterSpace = gxMinPushSpace;
  1942.  
  1943.                 x = (cxDlg - ((cButtons - 1) * xInterSpace) - xTotal) / 2;
  1944.                 if (x < 0)
  1945.                     x = 0;
  1946.  
  1947.                 if (x < gxMargin && xInterSpace > gxMinPushSpace) {
  1948.                     xInterSpace = (cxDlg - xTotal - (2 * gxMargin))
  1949.                             / (cButtons - 1);
  1950.  
  1951.                     if (xInterSpace < gxMinPushSpace)
  1952.                         xInterSpace = gxMinPushSpace;
  1953.  
  1954.                     x = (cxDlg - ((cButtons - 1) * xInterSpace) - xTotal)
  1955.                             / 2;
  1956.                     if (x < 0)
  1957.                         x = 0;
  1958.                 }
  1959.             }
  1960.  
  1961.             break;
  1962.  
  1963.         case MENU_ARRPUSHRIGHT:
  1964.             x = (gcd.npc->rc.right - gcd.npc->rc.left) - gxMargin;
  1965.             y = gyMargin;
  1966.             break;
  1967.     }
  1968.  
  1969.     /*
  1970.      * Loop through all the controls.
  1971.      */
  1972.     for (npc = npcHead; npc; npc = npc->npcNext) {
  1973.         /*
  1974.          * We will arrange this control only if it is a pushbutton,
  1975.          * and only if one of the following is true: there are no
  1976.          * controls selected, or the dialog itself is selected, or
  1977.          * there are some controls selected and this pushbutton is
  1978.          * one of them.
  1979.          */
  1980.         if (npc->pwcd->iType == W_PUSHBUTTON &&
  1981.                 (!gcSelected || gfDlgSelected || npc->fSelected)) {
  1982.             rc = npc->rc;
  1983.  
  1984.             switch (cmd) {
  1985.                 case MENU_ARRPUSHBOTTOM:
  1986.                     rc.left = x;
  1987.                     rc.top = y - (npc->rc.bottom - npc->rc.top);
  1988.                     rc.bottom = y;
  1989.                     rc.right = rc.left + (npc->rc.right - npc->rc.left);
  1990.  
  1991.                     x = rc.right + xInterSpace;
  1992.  
  1993.                     break;
  1994.  
  1995.                 case MENU_ARRPUSHRIGHT:
  1996.                     rc.left = x - (npc->rc.right - npc->rc.left);
  1997.                     rc.bottom = y + (npc->rc.bottom - npc->rc.top);
  1998.                     rc.right = x;
  1999.                     rc.top = y;
  2000.  
  2001.                     y = rc.bottom + gyPushSpace;
  2002.  
  2003.                     break;
  2004.             }
  2005.  
  2006.             GridizeRect(&rc, GRIDIZE_LEFT | GRIDIZE_TOP | GRIDIZE_SAMESIZE);
  2007.             FitRectToBounds(&rc, GetOverHang(npc->pwcd->iType,
  2008.                     npc->rc.bottom - npc->rc.top),
  2009.                     DRAG_CENTER, FALSE);
  2010.  
  2011.             if (!EqualRect(&rc, &npc->rc)) {
  2012.                 PositionControl2(npc, &rc, NULL);
  2013.                 fModified = TRUE;
  2014.             }
  2015.         }
  2016.     }
  2017.  
  2018.     if (fModified) {
  2019.         if (gfDlgSelected || !gcSelected)
  2020.             InvalidateRect(gcd.npc->hwnd, NULL, TRUE);
  2021.  
  2022.         CalcSelectedRect();
  2023.         gfResChged = gfDlgChanged = TRUE;
  2024.         ShowFileStatus(FALSE);
  2025.         StatusUpdate();
  2026.     }
  2027. }
  2028.