Annotation of mstools/samples/sdktools/dlgedit/select.c, revision 1.1.1.1

1.1       root        1: 
                      2: /******************************************************************************\
                      3: *       This is a part of the Microsoft Source Code Samples. 
                      4: *       Copyright (C) 1993 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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.