winnt-dont-display-ibm-legal-message-20040326
[openafs.git] / src / WINNT / afsapplib / resize.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 }
14
15 #include <windows.h>
16 #include <stdlib.h>
17 #include <WINNT/al_resource.h>  // To see if you have IDC_HSPLIT etc
18 #include <WINNT/resize.h>
19 #include <WINNT/subclass.h>
20 #include <WINNT/TaLocale.h>
21
22
23 /*
24  * DEFINITIONS ________________________________________________________________
25  *
26  */
27
28 #ifndef limit
29 #define limit(_a,_x,_b)    (min( max( (_a),(_x) ), (_b) ))
30 #endif
31 #ifndef inlimit
32 #define inlimit(_a,_x,_b)  ( (_x >= _a) && (_x <= _b) )
33 #endif
34
35 #ifndef THIS_HINST
36 #define THIS_HINST  (GetModuleHandle (NULL))
37 #endif
38
39 #ifndef GWL_USER
40 #define GWL_USER 0
41 #endif
42
43 #ifndef cxRECT
44 #define cxRECT(_r)  ((_r).right - (_r).left)
45 #endif
46 #ifndef cyRECT
47 #define cyRECT(_r)  ((_r).bottom - (_r).top)
48 #endif
49
50 typedef struct  // SplitterData
51    {
52    LONG         *pcDelta;       // pointer to LONG to hold cx/yDelta
53    int           idWnd1;        // first window to split between
54    int           idWnd2;        // second window to split between
55    rwWindowData *awd;   // data list for using splitter
56    BOOL          fX;    // TRUE if moves in X, FALSE if in Y
57    BOOL          fDragging;     // TRUE if dragging with the mouse
58    POINT         ptStart;       // point at which started dragging
59    BOOL          fMovedBeforeCreate;    // TRUE if windows moved before create
60    } SplitterData;
61
62
63 /*
64  * VARIABLES __________________________________________________________________
65  *
66  */
67
68 typedef struct
69    {
70    HWND  hWnd;
71    RECT  rWnd;
72    LONG  cxDeltaCenter;
73    LONG  cyDeltaCenter;
74    rwWindowData *awdLast;
75    } WindowList;
76
77 static WindowList *awl;
78 static size_t      cwl = 0;
79
80
81 /*
82  * PROTOTYPES _________________________________________________________________
83  *
84  */
85
86 int            rwFindOrAddWnd             (HWND, rwWindowData * = 0);
87 void           rwFindAndRemoveWnd         (HWND);
88 DWORD          rwFindAction               (rwWindowData *, int);
89
90 BOOL           WhereShouldSplitterGo      (HWND, int, int, RECT *, BOOL *);
91 void           EnsureSplitterRegistered   (void);
92 LONG APIENTRY  SplitterWndProc            (HWND, UINT, WPARAM, LPARAM);
93 void           ResizeSplitter             (HWND, SplitterData *, LONG, LONG*);
94 void           FindSplitterMinMax         (HWND, SplitterData *, LONG,
95                                            LONG*, LONG*);
96
97 void FindResizeLimits (HWND hWnd, LONG *pcxMin, LONG *pcxMax, LONG *pcyMin, LONG *pcyMax, rwWindowData * = 0);
98
99 BOOL CALLBACK Resize_DialogProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
100
101
102 /*
103  * ROUTINES ___________________________________________________________________
104  *
105  */
106
107 #ifndef REALLOC
108 #define REALLOC(_a,_c,_r,_i) ResizeReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
109 BOOL ResizeReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
110 {
111    LPVOID pNew;
112    size_t cNew;
113
114    if (cReq <= *pcTarget)
115       return TRUE;
116
117    if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
118       return FALSE;
119
120    if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
121       return FALSE;
122    memset (pNew, 0x00, cbElement * cNew);
123
124    if (*pcTarget != 0)
125    {
126       memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
127       Free (*ppTarget);
128    }
129
130    *ppTarget = pNew;
131    *pcTarget = cNew;
132    return TRUE;
133 }
134 #endif
135
136
137 void ResizeWindow (HWND hWnd, rwWindowData *awd, rwAction rwa, RECT *pr)
138 {
139    static BOOL fInHere = FALSE;  // prevent reentrancy during SetWindowPos().
140    int    ii;
141    RECT   rOld, rNew;
142
143    if (fInHere)
144       return;
145
146             // We maintain list of where-was-this-window-last-time; find
147             // This window in that list, or add it.
148             //
149    if ((ii = rwFindOrAddWnd (hWnd, awd)) == -1)
150       goto lblDONE;
151
152    rOld = awl[ii].rWnd; // previous position
153
154             // If the window disappears, then remove its entry from the
155             // list of windows.
156             //
157    if (!IsWindow (hWnd))
158       {
159       awl[ii].hWnd = NULL;
160       goto lblDONE;
161       }
162
163             // If told to, move and/or resize this parent window.
164             //
165    if (rwa == rwaMoveToHere)
166       {
167       if (pr == NULL)
168          goto lblDONE;
169
170       fInHere = TRUE;
171
172       SetWindowPos (hWnd, NULL,
173                     pr->left,
174                     pr->top,
175                     pr->right - pr->left,
176                     pr->bottom - pr->top,
177                     SWP_NOZORDER | SWP_NOACTIVATE);
178
179       fInHere = FALSE;
180
181       rNew = *pr;
182       }
183    else
184       {
185       GetWindowRect (hWnd, &rNew);
186       }
187
188             // Window has moved from {rOld} to {rNew},
189             //    or
190             // Window's client area has been changed by {*pr}
191             //
192    if (rwa == rwaMoveToHere || rwa == rwaFixupGuts || rwa == rwaNewClientArea)
193       {
194       LONG  dx, dy;
195       LONG  dxc, dyc;
196
197       if (rwa == rwaNewClientArea)
198          {
199          dx = pr->right - pr->left;
200          dy = pr->bottom - pr->top;
201          }
202       else
203          {
204          dx = (rNew.right -rNew.left) - (rOld.right -rOld.left);
205          dy = (rNew.bottom -rNew.top) - (rOld.bottom -rOld.top);
206          }
207
208       if (abs(dx) & 1)
209          awl[ii].cxDeltaCenter += (dx > 0) ? 1 : -1;
210       if (abs(dy) & 1)
211          awl[ii].cyDeltaCenter += (dy > 0) ? 1 : -1;
212
213       dxc = dx + awl[ii].cxDeltaCenter/2;
214       dyc = dy + awl[ii].cyDeltaCenter/2;
215
216       awl[ii].cxDeltaCenter %= 2;
217       awl[ii].cyDeltaCenter %= 2;
218
219       if (dx != 0 || dy != 0)
220          {
221          HWND   hItem;
222          size_t nItems = 0;
223
224          for (hItem = GetWindow (hWnd, GW_CHILD);
225               hItem != NULL;
226               hItem = GetWindow (hItem, GW_HWNDNEXT))
227             {
228             nItems++;
229             }
230
231          if (nItems != 0)
232             {
233             DWORD ra;
234             HDWP dwp = BeginDeferWindowPos (nItems);
235             BOOL fRepaint = FALSE;
236
237             for (hItem = GetWindow (hWnd, GW_CHILD);
238                  hItem != NULL;
239                  hItem = GetWindow (hItem, GW_HWNDNEXT))
240                {
241                RECT   rItem;
242
243                GetRectInParent (hItem, &rItem);
244
245                ra = rwFindAction (awd, (GetWindowLong (hItem, GWL_ID)));
246
247                DeferWindowPos (dwp, hItem, NULL,
248                                rItem.left + ((ra & raMoveX)  ? dx :
249                                             (ra & raMoveXB) ? (0-dx) :
250                                             (ra & raMoveXC) ? (dxc/2) : 0),
251                                rItem.top  + ((ra & raMoveY)  ? dy :
252                                             (ra & raMoveYB) ? (0-dy) :
253                                             (ra & raMoveYC) ? (dyc/2) : 0),
254                                rItem.right -rItem.left
255                                          + ((ra & raSizeX)  ? dx :
256                                             (ra & raSizeXB) ? (0-dx) :
257             (ra & raSizeXC) ? (dxc/2) : 0),
258                                rItem.bottom -rItem.top
259                                          + ((ra & raSizeY)  ? dy :
260                                             (ra & raSizeYB) ? (0-dy) :
261             (ra & raSizeYC) ? (dyc/2) : 0),
262                                SWP_NOACTIVATE | SWP_NOZORDER);
263                }
264
265             EndDeferWindowPos (dwp);
266
267             for (hItem = GetWindow (hWnd, GW_CHILD);
268                  hItem != NULL;
269                  hItem = GetWindow (hItem, GW_HWNDNEXT))
270                {
271                ra = rwFindAction (awd, (GetWindowLong (hItem, GWL_ID)));
272
273                if (ra & raRepaint)
274                   {
275                   RECT rItem;
276                   GetRectInParent (hItem, &rItem);
277                   InvalidateRect (hWnd, &rItem, TRUE);
278                   fRepaint = TRUE;
279                   }
280
281                if (ra & raNotify)
282                   {
283                   RECT rClient;
284                   GetClientRect (hItem, &rClient);
285                   SendMessage (hItem, WM_SIZE, SIZE_RESTORED, MAKELPARAM( rClient.right-rClient.left, rClient.bottom-rClient.top ));
286                   }
287                }
288
289             if (fRepaint)
290                {
291                UpdateWindow (hWnd);
292                }
293             }
294          }
295       }
296
297             // Record this window's current position
298             //
299    awl[ii].rWnd = rNew;
300
301 lblDONE:
302    fInHere = FALSE;
303 }
304
305
306 int rwFindOrAddWnd (HWND hWnd, rwWindowData *awd)
307 {
308             // Is the window handle listed in awl[] already?
309             //
310    for (int ii = 0; ii < (int)cwl; ii++)
311       {
312       if (awl[ii].hWnd == hWnd)
313          {
314          if (awd)
315             awl[ii].awdLast = awd;
316          return ii;
317          }
318       }
319
320             // No?  Then add it.
321             //
322    for (ii = 0; ii < (int)cwl; ii++)
323       {
324       if (awl[ii].hWnd == NULL)
325          break;
326       }
327    if (ii == (int)cwl)
328       {
329       if (!REALLOC (awl, cwl, ii+1, 1))
330          return (DWORD)-1;
331       }
332
333    awl[ii].hWnd = hWnd;
334    awl[ii].awdLast = awd;
335
336    if (IsWindow (hWnd))
337       {
338       GetWindowRect (hWnd, &awl[ii].rWnd);
339       Subclass_AddHook (hWnd, Resize_DialogProc);
340       }
341    else
342       {
343       awl[ii].rWnd.left = 0;
344       awl[ii].rWnd.right = 0;
345       awl[ii].rWnd.top = 0;
346       awl[ii].rWnd.bottom = 0;
347       }
348    awl[ii].cxDeltaCenter = 0;
349    awl[ii].cyDeltaCenter = 0;
350
351    return ii;
352 }
353
354
355 void rwFindAndRemoveWnd (HWND hWnd)
356 {
357    for (size_t ii = 0; ii < (int)cwl; ii++)
358       {
359       if (awl[ii].hWnd == hWnd)
360          {
361          Subclass_RemoveHook (awl[ii].hWnd, hWnd);
362          awl[ii].hWnd = NULL;
363          return;
364          }
365       }
366 }
367
368
369 DWORD rwFindAction (rwWindowData *awd, int id)
370 {
371    DWORD raDefault = raLeaveAlone;
372
373    if (awd != NULL)
374       {
375       for (int ii = 0; awd[ii].id != idENDLIST; ++ii)
376          {
377          if (awd[ii].id == id)
378             return awd[ii].ra;
379          if (awd[ii].id == idDEFAULT)
380             raDefault = awd[ii].ra;
381          }
382       }
383
384    return raDefault;
385 }
386
387
388 void GetRectInParent (HWND hWnd, RECT *pr)
389 {
390    POINT pt;
391
392    GetWindowRect (hWnd, pr);
393
394    pr->right -= pr->left;
395    pr->bottom -= pr->top;       // right/bottom == width/height for now
396
397    pt.x = pr->left;
398    pt.y = pr->top;
399
400    ScreenToClient (GetParent (hWnd), &pt);
401
402    pr->left = pt.x;
403    pr->top = pt.y;
404    pr->right += pr->left;
405    pr->bottom += pr->top;
406 }
407
408
409 /*
410  * SPLITTERS __________________________________________________________________
411  *
412  */
413
414 static BOOL fRegistered = FALSE;        // TRUE If registered class
415
416 static TCHAR cszSplitterClassX[] = TEXT("SplitterWindowClassX");
417 static TCHAR cszSplitterClassY[] = TEXT("SplitterWindowClassY");
418
419
420 HWND CreateSplitter (HWND hWnd, int id1, int id2, int id,
421                      LONG *pcd, rwWindowData *awd, BOOL fMovedAlready)
422 {
423    SplitterData *psd;
424    RECT          rWnd;
425    BOOL          fX;
426
427    if (!WhereShouldSplitterGo (hWnd, id1, id2, &rWnd, &fX))
428       return NULL;
429
430    EnsureSplitterRegistered ();
431
432    psd = (SplitterData *)Allocate (sizeof(SplitterData));
433    if (psd == NULL)
434       return NULL;
435
436    psd->pcDelta = pcd;
437    psd->awd = awd;
438    psd->fX = fX;
439    psd->idWnd1 = id1;
440    psd->idWnd2 = id2;
441    psd->fMovedBeforeCreate = fMovedAlready;
442
443    return CreateWindow(
444               (fX) ? cszSplitterClassX : cszSplitterClassY,
445               TEXT(""), // Title
446               WS_CHILD | WS_VISIBLE,    // Window style
447               rWnd.left,        // Default horizontal position
448               rWnd.top, // Default vertical position
449               rWnd.right  -rWnd.left,   // Default width
450               rWnd.bottom -rWnd.top,    // Default height
451               hWnd,     // Parent window
452               (HMENU)id,        // Use ID given us by caller
453               THIS_HINST,       // This instance owns this window
454               (void *)psd       // Pointer not needed
455               );
456 }
457
458 void DeleteSplitter (HWND hWnd, int id1)
459 {
460    HWND hSplit;
461
462    if (hSplit = GetDlgItem (hWnd, id1))
463       {
464       DestroyWindow (hSplit);
465       }
466 }
467
468 void EnsureSplitterRegistered (void)
469 {
470    WNDCLASS  wc;
471
472    if (fRegistered)
473       return;
474    fRegistered = TRUE;
475
476    wc.style         = CS_HREDRAW | CS_VREDRAW;
477    wc.lpfnWndProc   = SplitterWndProc;
478    wc.cbClsExtra    = 0;
479    wc.cbWndExtra    = sizeof(SplitterData *);
480    wc.hInstance     = THIS_HINST;
481    wc.hIcon         = NULL;
482    wc.hbrBackground = CreateSolidBrush( GetSysColor( COLOR_BTNFACE ));
483    wc.lpszMenuName  = NULL;
484
485             // Register the X-moving class:
486             //
487 #ifdef IDC_HSPLIT
488    wc.hCursor       = LoadCursor (THIS_HINST, MAKEINTRESOURCE( IDC_HSPLIT ));
489 #else
490    wc.hCursor       = LoadCursor (NULL, IDC_SIZEWE);
491 #endif
492
493    wc.lpszClassName = cszSplitterClassX;
494
495    (void)RegisterClass (&wc);
496
497             // Register the Y-moving class:
498             //
499 #ifdef IDC_VSPLIT
500    wc.hCursor       = LoadCursor (THIS_HINST, MAKEINTRESOURCE( IDC_VSPLIT ));
501 #else
502    wc.hCursor       = LoadCursor (NULL, IDC_SIZENS);
503 #endif
504
505    wc.lpszClassName = cszSplitterClassY;
506
507    (void)RegisterClass (&wc);
508
509 }
510
511
512 BOOL WhereShouldSplitterGo (HWND hWnd, int id1, int id2, RECT *prWnd, BOOL *pfX)
513 {
514    RECT  r1, r2;
515    BOOL  rc = TRUE;
516
517    GetRectInParent (GetDlgItem (hWnd, id1), &r1);
518    GetRectInParent (GetDlgItem (hWnd, id2), &r2);
519
520    if (r2.left > r1.right)      // R1 on left, R2 on right?
521       {
522       *pfX = TRUE;
523       prWnd->top    = min (r1.top,    r2.top);
524       prWnd->bottom = max (r1.bottom, r2.bottom);
525       prWnd->left   = r1.right;
526       prWnd->right  = r2.left;
527       }
528    else if (r2.right < r1.left) // R2 on left, R1 on right?
529       {
530       *pfX = TRUE;
531       prWnd->top    = min (r1.top,    r2.top);
532       prWnd->bottom = max (r1.bottom, r2.bottom);
533       prWnd->left   = r2.right;
534       prWnd->right  = r1.left;
535       }
536    else if (r2.top > r1.bottom) // R1 on top, R2 on bottom?
537       {
538       *pfX = FALSE;
539       prWnd->left   = min (r1.left,   r2.left);
540       prWnd->right  = max (r1.right,  r2.right);
541       prWnd->top    = r1.bottom;
542       prWnd->bottom = r2.top;
543       }
544    else if (r2.bottom < r1.top) // R2 on top, R1 on bottom?
545       {
546       *pfX = FALSE;
547       prWnd->left   = min (r1.left,   r2.left);
548       prWnd->right  = max (r1.right,  r2.right);
549       prWnd->top    = r2.bottom;
550       prWnd->bottom = r1.top;
551       }
552    else // Rectangles intersect!
553       { // Don't know where it should go.
554       rc = FALSE;
555       }
556
557    return rc;
558 }
559
560
561 LONG APIENTRY SplitterWndProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
562 {
563    SplitterData *psd;
564    static LONG cdAtStart;
565
566    if (msg == WM_CREATE)
567       {
568       SetWindowLong (hWnd,GWL_USER,(LONG)((LPCREATESTRUCT)lp)->lpCreateParams);
569       }
570
571    if ((psd = (SplitterData *)GetWindowLong (hWnd, GWL_USER)) != NULL)
572       {
573       switch (msg)
574          {
575          case WM_CREATE:
576                if (!psd->fMovedBeforeCreate)
577                   ResizeSplitter (GetParent (hWnd), psd, 0, psd->pcDelta);
578                psd->fDragging = FALSE;
579                break;
580
581          case WM_LBUTTONDOWN:
582                if (!psd->fDragging)
583                   {
584                   SetCapture (hWnd);
585                   psd->fDragging = TRUE;
586                   cdAtStart = *psd->pcDelta;
587
588                   GetCursorPos (&psd->ptStart);
589                   ScreenToClient (GetParent (hWnd), &psd->ptStart);
590                   }
591                break;
592
593          case WM_MOUSEMOVE:
594                if (psd->fDragging)
595                   {
596                   POINT  pt;
597                   LONG   cx, cy;
598                   LONG   cd;
599
600                   GetCursorPos (&pt);
601                   ScreenToClient (GetParent (hWnd), &pt);
602
603                   cx = (LONG)pt.x - (LONG)psd->ptStart.x;
604                   cy = (LONG)pt.y - (LONG)psd->ptStart.y;
605
606                   if (psd->fX)
607                      cd = cdAtStart + cx;
608                   else // (!psd->fX)
609                      cd = cdAtStart + cy;
610
611                   if (cd != *(psd->pcDelta))
612                      {
613                      ResizeSplitter (GetParent(hWnd), psd, *psd->pcDelta, &cd);
614                      *psd->pcDelta = cd;
615                      }
616                   }
617                break;
618
619          case WM_LBUTTONUP:
620                if (psd->fDragging)
621                   {
622                   ReleaseCapture ();
623                   psd->fDragging = FALSE;
624                   }
625                break;
626
627          case WM_DESTROY:
628                if (psd->fDragging)
629                   {
630                   ReleaseCapture ();
631                   psd->fDragging = FALSE;
632
633                   Free (psd);
634                   psd = NULL;   // fault blatantly if you use {psd} now
635                   SetWindowLong (hWnd, GWL_USER, 0);
636                   }
637                break;
638
639 #if 0 // Enable me to make the splitters draw in black
640          case WM_PAINT:
641                {
642                PAINTSTRUCT ps;
643                HDC hdc = BeginPaint (hWnd, &ps);
644                FillRect (hdc, &ps.rcPaint, GetStockObject(BLACK_BRUSH));
645                EndPaint (hWnd, &ps);
646                return 0;
647                }
648                break;
649 #endif
650          }
651       }
652
653    return DefWindowProc (hWnd, msg, wp, lp);
654 }
655
656
657 void ResizeSplitter (HWND hWnd, SplitterData *psd, LONG cOld, LONG *pcNew)
658 {
659    LONG   dx, dy;
660    HWND   hItem;
661    size_t nItems = 0;
662    LONG   cdMin, cdMax;
663
664    if (psd == NULL || pcNew == NULL || hWnd == NULL)
665       return;
666
667    FindSplitterMinMax (hWnd, psd, cOld, &cdMin, &cdMax);
668    *pcNew = limit (cdMin, *pcNew, cdMax);
669
670    if (*pcNew == cOld)
671       return;
672
673    dx = (psd->fX) ? (*pcNew - cOld) : 0;
674    dy = (psd->fX) ? 0 : (*pcNew - cOld);
675
676    for (hItem = GetWindow (hWnd, GW_CHILD);
677         hItem != NULL;
678         hItem = GetWindow (hItem, GW_HWNDNEXT))
679       {
680       nItems++;
681       }
682
683    if (nItems != 0)
684       {
685       BOOL fRepaint = FALSE;
686       HDWP dwp = BeginDeferWindowPos (nItems);
687
688       for (hItem = GetWindow (hWnd, GW_CHILD);
689            hItem != NULL;
690            hItem = GetWindow (hItem, GW_HWNDNEXT))
691          {
692          RECT   rItem;
693          DWORD  ra;
694
695          GetRectInParent (hItem, &rItem);
696
697          ra = rwFindAction (psd->awd, (GetWindowLong (hItem, GWL_ID)));
698
699          DeferWindowPos (dwp, hItem, NULL,
700                          rItem.left + ((ra & raMoveX)  ? dx :
701                                       (ra & raMoveXB) ? (0-dx) : 0),
702                          rItem.top  + ((ra & raMoveY)  ? dy :
703                                       (ra & raMoveYB) ? (0-dy) : 0),
704                          rItem.right -rItem.left
705                                    + ((ra & raSizeX)  ? dx :
706                                       (ra & raSizeXB) ? (0-dx) : 0),
707                          rItem.bottom -rItem.top
708                                    + ((ra & raSizeY)  ? dy :
709                                       (ra & raSizeYB) ? (0-dy) : 0),
710                          SWP_NOACTIVATE | SWP_NOZORDER);
711          }
712
713       EndDeferWindowPos (dwp);
714
715       for (hItem = GetWindow (hWnd, GW_CHILD);
716            hItem != NULL;
717            hItem = GetWindow (hItem, GW_HWNDNEXT))
718          {
719          DWORD ra = rwFindAction (psd->awd, (GetWindowLong (hItem, GWL_ID)));
720
721          if (ra & raRepaint)
722             {
723             RECT  rItem;
724
725             GetRectInParent (hItem, &rItem);
726
727             InvalidateRect (hWnd, &rItem, TRUE);
728             fRepaint = TRUE;
729             }
730          }
731
732       if (fRepaint)
733          {
734          UpdateWindow (hWnd);
735          }
736       }
737 }
738
739
740 void FindSplitterMinMax (HWND hWnd, SplitterData *psd, LONG cOld, LONG *pcdMin, LONG *pcdMax)
741 {
742    *pcdMin = 0;
743    *pcdMax = 0;
744
745    for (int ii = 0; psd->awd[ ii ].id != idENDLIST; ++ii)
746       {
747       HWND hControl;
748       if ((hControl = GetDlgItem (hWnd, psd->awd[ ii ].id)) != NULL)
749          {
750          RECT rControl;
751          GetRectInParent (hControl, &rControl);
752
753          if (psd->fX)
754             {
755             LONG cxMin = 0;
756             LONG cxMax = 0;
757
758             if (LOWORD(psd->awd[ ii ].cMinimum)) // X minimum?
759                {
760                if (psd->awd[ ii ].ra & raSizeX)
761                   cxMin = cOld - cxRECT(rControl) + LOWORD(psd->awd[ ii ].cMinimum);
762                else if (psd->awd[ ii ].ra & raSizeXB)
763                   cxMax = cOld + cxRECT(rControl) - LOWORD(psd->awd[ ii ].cMinimum);
764                else if (psd->awd[ ii ].ra & raSizeXC)
765                   cxMin = cOld - (cxRECT(rControl) - LOWORD(psd->awd[ ii ].cMinimum))*2;
766                }
767
768             if (LOWORD(psd->awd[ ii ].cMaximum)) // X maximum?
769                {
770                if (psd->awd[ ii ].ra & raSizeX)
771                   cxMax = cOld - cxRECT(rControl) + LOWORD(psd->awd[ ii ].cMaximum);
772                else if (psd->awd[ ii ].ra & raSizeXB)
773                   cxMin = cOld + cxRECT(rControl) - LOWORD(psd->awd[ ii ].cMaximum);
774                else if (psd->awd[ ii ].ra & raSizeXC)
775                   cxMin = cOld - (cxRECT(rControl) - LOWORD(psd->awd[ ii ].cMaximum))*2;
776                }
777
778             if (cxMin)  *pcdMin = (*pcdMin) ? max( *pcdMin, cxMin ) : cxMin;
779             if (cxMax)  *pcdMax = (*pcdMax) ? min( *pcdMax, cxMax ) : cxMax;
780             }
781          else
782             {
783             LONG cyMin = 0;
784             LONG cyMax = 0;
785
786             if (HIWORD(psd->awd[ ii ].cMinimum)) // Y minimum?
787                {
788                if (psd->awd[ ii ].ra & raSizeY)
789                   cyMin = cOld - cyRECT(rControl) + HIWORD(psd->awd[ ii ].cMinimum);
790                else if (psd->awd[ ii ].ra & raSizeYB)
791                   cyMax = cOld + cyRECT(rControl) - HIWORD(psd->awd[ ii ].cMinimum);
792                else if (psd->awd[ ii ].ra & raSizeYC)
793                   cyMin = cOld - (cyRECT(rControl) - HIWORD(psd->awd[ ii ].cMinimum))*2;
794                }
795
796             if (HIWORD(psd->awd[ ii ].cMaximum)) // Y maximum?
797                {
798                if (psd->awd[ ii ].ra & raSizeY)
799                   cyMax = cOld - cyRECT(rControl) + HIWORD(psd->awd[ ii ].cMaximum);
800                else if (psd->awd[ ii ].ra & raSizeYB)
801                   cyMin = cOld + cyRECT(rControl) - HIWORD(psd->awd[ ii ].cMaximum);
802                else if (psd->awd[ ii ].ra & raSizeYC)
803                   cyMin = cOld - (cyRECT(rControl) - HIWORD(psd->awd[ ii ].cMaximum))*2;
804                }
805
806             if (cyMin)  *pcdMin = (*pcdMin) ? max( *pcdMin, cyMin ) : cyMin;
807             if (cyMax)  *pcdMax = (*pcdMax) ? min( *pcdMax, cyMax ) : cyMax;
808             }
809          }
810       }
811 }
812
813
814 void FindResizeLimits (HWND hWnd, LONG *pcxMin, LONG *pcxMax, LONG *pcyMin, LONG *pcyMax, rwWindowData *awd)
815 {
816    *pcxMin = 0;
817    *pcxMax = 0;
818    *pcyMin = 0;
819    *pcyMax = 0;
820
821    if (awd == NULL)
822       {
823       int iwl;
824       if ((iwl = rwFindOrAddWnd (hWnd)) == -1)
825          return;
826
827       if ((awd = awl[ iwl ].awdLast) == NULL)
828          return;
829       }
830
831    RECT rNow;
832    GetWindowRect (hWnd, &rNow);
833
834    for (DWORD ii = 0; awd[ ii ].id != idENDLIST; ++ii)
835       {
836       HWND hControl;
837       if ((hControl = GetDlgItem (hWnd, awd[ ii ].id)) != NULL)
838          {
839          RECT rControl;
840          GetRectInParent (hControl, &rControl);
841
842          LONG cxMin = 0;
843          LONG cyMin = 0;
844          LONG cxMax = 0;
845          LONG cyMax = 0;
846
847          if (LOWORD(awd[ ii ].cMinimum)) // X minimum?
848             {
849             if (awd[ ii ].ra & raSizeX)
850                cxMin = cxRECT(rNow) - cxRECT(rControl) + LOWORD(awd[ ii ].cMinimum);
851             else if (awd[ ii ].ra & raSizeXB)
852                cxMax = cxRECT(rNow) + cxRECT(rControl) - LOWORD(awd[ ii ].cMinimum);
853             else if (awd[ ii ].ra & raSizeXC)
854                cxMin = cxRECT(rNow) - (cxRECT(rControl) - LOWORD(awd[ ii ].cMinimum))*2;
855             }
856
857          if (LOWORD(awd[ ii ].cMaximum)) // X maximum?
858             {
859             if (awd[ ii ].ra & raSizeX)
860                cxMax = cxRECT(rNow) - cxRECT(rControl) + LOWORD(awd[ ii ].cMaximum);
861             else if (awd[ ii ].ra & raSizeXB)
862                cxMin = cxRECT(rNow) + cxRECT(rControl) - LOWORD(awd[ ii ].cMaximum);
863             else if (awd[ ii ].ra & raSizeXC)
864                cxMax = cxRECT(rNow) - (cxRECT(rControl) - LOWORD(awd[ ii ].cMaximum))*2;
865             }
866
867          if (HIWORD(awd[ ii ].cMinimum)) // Y minimum?
868             {
869             if (awd[ ii ].ra & raSizeY)
870                cyMin = cyRECT(rNow) - cyRECT(rControl) + HIWORD(awd[ ii ].cMinimum);
871             else if (awd[ ii ].ra & raSizeYB)
872                cyMax = cyRECT(rNow) + cyRECT(rControl) - HIWORD(awd[ ii ].cMinimum);
873             else if (awd[ ii ].ra & raSizeYC)
874                cyMin = cyRECT(rNow) - (cyRECT(rControl) - HIWORD(awd[ ii ].cMinimum))*2;
875             }
876
877          if (HIWORD(awd[ ii ].cMaximum)) // Y maximum?
878             {
879             if (awd[ ii ].ra & raSizeY)
880                cyMax = cyRECT(rNow) - cyRECT(rControl) + HIWORD(awd[ ii ].cMaximum);
881             else if (awd[ ii ].ra & raSizeYB)
882                cyMin = cyRECT(rNow) + cyRECT(rControl) - HIWORD(awd[ ii ].cMaximum);
883             else if (awd[ ii ].ra & raSizeYC)
884                cyMax = cyRECT(rNow) - (cyRECT(rControl) - HIWORD(awd[ ii ].cMaximum))*2;
885             }
886
887          if (cxMin)  *pcxMin = (*pcxMin) ? max( *pcxMin, cxMin ) : cxMin;
888          if (cyMin)  *pcyMin = (*pcyMin) ? max( *pcyMin, cyMin ) : cyMin;
889          if (cxMax)  *pcxMax = (*pcxMax) ? min( *pcxMax, cxMax ) : cxMax;
890          if (cyMax)  *pcyMax = (*pcyMax) ? min( *pcyMax, cyMax ) : cyMax;
891          }
892       }
893 }
894
895
896 BOOL CALLBACK Resize_DialogProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
897 {
898    PVOID fnNext = Subclass_FindNextHook (hWnd, Resize_DialogProc);
899
900    switch (msg)
901       {
902       case WM_GETMINMAXINFO:
903          LONG cxMin;
904          LONG cyMin;
905          LONG cxMax;
906          LONG cyMax;
907          FindResizeLimits (hWnd, &cxMin, &cxMax, &cyMin, &cyMax);
908
909          LPMINMAXINFO lpmmi;
910          lpmmi = (LPMINMAXINFO)lp;
911
912          if (cxMin)
913             lpmmi->ptMinTrackSize.x = cxMin;
914          if (cyMin)
915             lpmmi->ptMinTrackSize.y = cyMin;
916          if (cxMax)
917             lpmmi->ptMaxTrackSize.x = cxMax;
918          if (cyMax)
919             lpmmi->ptMaxTrackSize.y = cyMax;
920          return FALSE;
921
922       case WM_DESTROY:
923          rwFindAndRemoveWnd (hWnd);
924          break;
925       }
926
927    return (fnNext) ? CallWindowProc ((WNDPROC)fnNext, hWnd, msg, wp, lp) : FALSE;
928 }
929