|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.