d92c2f5c4b1ef0bf9fefd60419becb316f92f4be
[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    int ii;
311    for (ii = 0; ii < (int)cwl; ii++)
312       {
313       if (awl[ii].hWnd == hWnd)
314          {
315          if (awd)
316             awl[ii].awdLast = awd;
317          return ii;
318          }
319       }
320
321             // No?  Then add it.
322             //
323    for (ii = 0; ii < (int)cwl; ii++)
324       {
325       if (awl[ii].hWnd == NULL)
326          break;
327       }
328    if (ii == (int)cwl)
329       {
330       if (!REALLOC (awl, cwl, ii+1, 1))
331          return (DWORD)-1;
332       }
333
334    awl[ii].hWnd = hWnd;
335    awl[ii].awdLast = awd;
336
337    if (IsWindow (hWnd))
338       {
339       GetWindowRect (hWnd, &awl[ii].rWnd);
340       Subclass_AddHook (hWnd, Resize_DialogProc);
341       }
342    else
343       {
344       awl[ii].rWnd.left = 0;
345       awl[ii].rWnd.right = 0;
346       awl[ii].rWnd.top = 0;
347       awl[ii].rWnd.bottom = 0;
348       }
349    awl[ii].cxDeltaCenter = 0;
350    awl[ii].cyDeltaCenter = 0;
351
352    return ii;
353 }
354
355
356 void rwFindAndRemoveWnd (HWND hWnd)
357 {
358    for (size_t ii = 0; ii < (int)cwl; ii++)
359       {
360       if (awl[ii].hWnd == hWnd)
361          {
362          Subclass_RemoveHook (awl[ii].hWnd, hWnd);
363          awl[ii].hWnd = NULL;
364          return;
365          }
366       }
367 }
368
369
370 DWORD rwFindAction (rwWindowData *awd, int id)
371 {
372    DWORD raDefault = raLeaveAlone;
373
374    if (awd != NULL)
375       {
376       for (int ii = 0; awd[ii].id != idENDLIST; ++ii)
377          {
378          if (awd[ii].id == id)
379             return awd[ii].ra;
380          if (awd[ii].id == idDEFAULT)
381             raDefault = awd[ii].ra;
382          }
383       }
384
385    return raDefault;
386 }
387
388
389 void GetRectInParent (HWND hWnd, RECT *pr)
390 {
391    POINT pt;
392
393    GetWindowRect (hWnd, pr);
394
395    pr->right -= pr->left;
396    pr->bottom -= pr->top;       // right/bottom == width/height for now
397
398    pt.x = pr->left;
399    pt.y = pr->top;
400
401    ScreenToClient (GetParent (hWnd), &pt);
402
403    pr->left = pt.x;
404    pr->top = pt.y;
405    pr->right += pr->left;
406    pr->bottom += pr->top;
407 }
408
409
410 /*
411  * SPLITTERS __________________________________________________________________
412  *
413  */
414
415 static BOOL fRegistered = FALSE;        // TRUE If registered class
416
417 static TCHAR cszSplitterClassX[] = TEXT("SplitterWindowClassX");
418 static TCHAR cszSplitterClassY[] = TEXT("SplitterWindowClassY");
419
420
421 HWND CreateSplitter (HWND hWnd, int id1, int id2, int id,
422                      LONG *pcd, rwWindowData *awd, BOOL fMovedAlready)
423 {
424    SplitterData *psd;
425    RECT          rWnd;
426    BOOL          fX;
427
428    if (!WhereShouldSplitterGo (hWnd, id1, id2, &rWnd, &fX))
429       return NULL;
430
431    EnsureSplitterRegistered ();
432
433    psd = (SplitterData *)Allocate (sizeof(SplitterData));
434    if (psd == NULL)
435       return NULL;
436
437    psd->pcDelta = pcd;
438    psd->awd = awd;
439    psd->fX = fX;
440    psd->idWnd1 = id1;
441    psd->idWnd2 = id2;
442    psd->fMovedBeforeCreate = fMovedAlready;
443
444    return CreateWindow(
445               (fX) ? cszSplitterClassX : cszSplitterClassY,
446               TEXT(""), // Title
447               WS_CHILD | WS_VISIBLE,    // Window style
448               rWnd.left,        // Default horizontal position
449               rWnd.top, // Default vertical position
450               rWnd.right  -rWnd.left,   // Default width
451               rWnd.bottom -rWnd.top,    // Default height
452               hWnd,     // Parent window
453               (HMENU)id,        // Use ID given us by caller
454               THIS_HINST,       // This instance owns this window
455               (void *)psd       // Pointer not needed
456               );
457 }
458
459 void DeleteSplitter (HWND hWnd, int id1)
460 {
461    HWND hSplit;
462
463    if (hSplit = GetDlgItem (hWnd, id1))
464       {
465       DestroyWindow (hSplit);
466       }
467 }
468
469 void EnsureSplitterRegistered (void)
470 {
471    WNDCLASS  wc;
472
473    if (fRegistered)
474       return;
475    fRegistered = TRUE;
476
477    wc.style         = CS_HREDRAW | CS_VREDRAW;
478    wc.lpfnWndProc   = SplitterWndProc;
479    wc.cbClsExtra    = 0;
480    wc.cbWndExtra    = sizeof(SplitterData *);
481    wc.hInstance     = THIS_HINST;
482    wc.hIcon         = NULL;
483    wc.hbrBackground = CreateSolidBrush( GetSysColor( COLOR_BTNFACE ));
484    wc.lpszMenuName  = NULL;
485
486             // Register the X-moving class:
487             //
488 #ifdef IDC_HSPLIT
489    wc.hCursor       = LoadCursor (THIS_HINST, MAKEINTRESOURCE( IDC_HSPLIT ));
490 #else
491    wc.hCursor       = LoadCursor (NULL, IDC_SIZEWE);
492 #endif
493
494    wc.lpszClassName = cszSplitterClassX;
495
496    (void)RegisterClass (&wc);
497
498             // Register the Y-moving class:
499             //
500 #ifdef IDC_VSPLIT
501    wc.hCursor       = LoadCursor (THIS_HINST, MAKEINTRESOURCE( IDC_VSPLIT ));
502 #else
503    wc.hCursor       = LoadCursor (NULL, IDC_SIZENS);
504 #endif
505
506    wc.lpszClassName = cszSplitterClassY;
507
508    (void)RegisterClass (&wc);
509
510 }
511
512
513 BOOL WhereShouldSplitterGo (HWND hWnd, int id1, int id2, RECT *prWnd, BOOL *pfX)
514 {
515    RECT  r1, r2;
516    BOOL  rc = TRUE;
517
518    GetRectInParent (GetDlgItem (hWnd, id1), &r1);
519    GetRectInParent (GetDlgItem (hWnd, id2), &r2);
520
521    if (r2.left > r1.right)      // R1 on left, R2 on right?
522       {
523       *pfX = TRUE;
524       prWnd->top    = min (r1.top,    r2.top);
525       prWnd->bottom = max (r1.bottom, r2.bottom);
526       prWnd->left   = r1.right;
527       prWnd->right  = r2.left;
528       }
529    else if (r2.right < r1.left) // R2 on left, R1 on right?
530       {
531       *pfX = TRUE;
532       prWnd->top    = min (r1.top,    r2.top);
533       prWnd->bottom = max (r1.bottom, r2.bottom);
534       prWnd->left   = r2.right;
535       prWnd->right  = r1.left;
536       }
537    else if (r2.top > r1.bottom) // R1 on top, R2 on bottom?
538       {
539       *pfX = FALSE;
540       prWnd->left   = min (r1.left,   r2.left);
541       prWnd->right  = max (r1.right,  r2.right);
542       prWnd->top    = r1.bottom;
543       prWnd->bottom = r2.top;
544       }
545    else if (r2.bottom < r1.top) // R2 on top, R1 on bottom?
546       {
547       *pfX = FALSE;
548       prWnd->left   = min (r1.left,   r2.left);
549       prWnd->right  = max (r1.right,  r2.right);
550       prWnd->top    = r2.bottom;
551       prWnd->bottom = r1.top;
552       }
553    else // Rectangles intersect!
554       { // Don't know where it should go.
555       rc = FALSE;
556       }
557
558    return rc;
559 }
560
561
562 LONG APIENTRY SplitterWndProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
563 {
564    SplitterData *psd;
565    static LONG cdAtStart;
566
567    if (msg == WM_CREATE)
568       {
569       SetWindowLong (hWnd,GWL_USER,(LONG)((LPCREATESTRUCT)lp)->lpCreateParams);
570       }
571
572    if ((psd = (SplitterData *)GetWindowLong (hWnd, GWL_USER)) != NULL)
573       {
574       switch (msg)
575          {
576          case WM_CREATE:
577                if (!psd->fMovedBeforeCreate)
578                   ResizeSplitter (GetParent (hWnd), psd, 0, psd->pcDelta);
579                psd->fDragging = FALSE;
580                break;
581
582          case WM_LBUTTONDOWN:
583                if (!psd->fDragging)
584                   {
585                   SetCapture (hWnd);
586                   psd->fDragging = TRUE;
587                   cdAtStart = *psd->pcDelta;
588
589                   GetCursorPos (&psd->ptStart);
590                   ScreenToClient (GetParent (hWnd), &psd->ptStart);
591                   }
592                break;
593
594          case WM_MOUSEMOVE:
595                if (psd->fDragging)
596                   {
597                   POINT  pt;
598                   LONG   cx, cy;
599                   LONG   cd;
600
601                   GetCursorPos (&pt);
602                   ScreenToClient (GetParent (hWnd), &pt);
603
604                   cx = (LONG)pt.x - (LONG)psd->ptStart.x;
605                   cy = (LONG)pt.y - (LONG)psd->ptStart.y;
606
607                   if (psd->fX)
608                      cd = cdAtStart + cx;
609                   else // (!psd->fX)
610                      cd = cdAtStart + cy;
611
612                   if (cd != *(psd->pcDelta))
613                      {
614                      ResizeSplitter (GetParent(hWnd), psd, *psd->pcDelta, &cd);
615                      *psd->pcDelta = cd;
616                      }
617                   }
618                break;
619
620          case WM_LBUTTONUP:
621                if (psd->fDragging)
622                   {
623                   ReleaseCapture ();
624                   psd->fDragging = FALSE;
625                   }
626                break;
627
628          case WM_DESTROY:
629                if (psd->fDragging)
630                   {
631                   ReleaseCapture ();
632                   psd->fDragging = FALSE;
633
634                   Free (psd);
635                   psd = NULL;   // fault blatantly if you use {psd} now
636                   SetWindowLong (hWnd, GWL_USER, 0);
637                   }
638                break;
639
640 #if 0 // Enable me to make the splitters draw in black
641          case WM_PAINT:
642                {
643                PAINTSTRUCT ps;
644                HDC hdc = BeginPaint (hWnd, &ps);
645                FillRect (hdc, &ps.rcPaint, GetStockObject(BLACK_BRUSH));
646                EndPaint (hWnd, &ps);
647                return 0;
648                }
649                break;
650 #endif
651          }
652       }
653
654    return DefWindowProc (hWnd, msg, wp, lp);
655 }
656
657
658 void ResizeSplitter (HWND hWnd, SplitterData *psd, LONG cOld, LONG *pcNew)
659 {
660    LONG   dx, dy;
661    HWND   hItem;
662    size_t nItems = 0;
663    LONG   cdMin, cdMax;
664
665    if (psd == NULL || pcNew == NULL || hWnd == NULL)
666       return;
667
668    FindSplitterMinMax (hWnd, psd, cOld, &cdMin, &cdMax);
669    *pcNew = limit (cdMin, *pcNew, cdMax);
670
671    if (*pcNew == cOld)
672       return;
673
674    dx = (psd->fX) ? (*pcNew - cOld) : 0;
675    dy = (psd->fX) ? 0 : (*pcNew - cOld);
676
677    for (hItem = GetWindow (hWnd, GW_CHILD);
678         hItem != NULL;
679         hItem = GetWindow (hItem, GW_HWNDNEXT))
680       {
681       nItems++;
682       }
683
684    if (nItems != 0)
685       {
686       BOOL fRepaint = FALSE;
687       HDWP dwp = BeginDeferWindowPos (nItems);
688
689       for (hItem = GetWindow (hWnd, GW_CHILD);
690            hItem != NULL;
691            hItem = GetWindow (hItem, GW_HWNDNEXT))
692          {
693          RECT   rItem;
694          DWORD  ra;
695
696          GetRectInParent (hItem, &rItem);
697
698          ra = rwFindAction (psd->awd, (GetWindowLong (hItem, GWL_ID)));
699
700          DeferWindowPos (dwp, hItem, NULL,
701                          rItem.left + ((ra & raMoveX)  ? dx :
702                                       (ra & raMoveXB) ? (0-dx) : 0),
703                          rItem.top  + ((ra & raMoveY)  ? dy :
704                                       (ra & raMoveYB) ? (0-dy) : 0),
705                          rItem.right -rItem.left
706                                    + ((ra & raSizeX)  ? dx :
707                                       (ra & raSizeXB) ? (0-dx) : 0),
708                          rItem.bottom -rItem.top
709                                    + ((ra & raSizeY)  ? dy :
710                                       (ra & raSizeYB) ? (0-dy) : 0),
711                          SWP_NOACTIVATE | SWP_NOZORDER);
712          }
713
714       EndDeferWindowPos (dwp);
715
716       for (hItem = GetWindow (hWnd, GW_CHILD);
717            hItem != NULL;
718            hItem = GetWindow (hItem, GW_HWNDNEXT))
719          {
720          DWORD ra = rwFindAction (psd->awd, (GetWindowLong (hItem, GWL_ID)));
721
722          if (ra & raRepaint)
723             {
724             RECT  rItem;
725
726             GetRectInParent (hItem, &rItem);
727
728             InvalidateRect (hWnd, &rItem, TRUE);
729             fRepaint = TRUE;
730             }
731          }
732
733       if (fRepaint)
734          {
735          UpdateWindow (hWnd);
736          }
737       }
738 }
739
740
741 void FindSplitterMinMax (HWND hWnd, SplitterData *psd, LONG cOld, LONG *pcdMin, LONG *pcdMax)
742 {
743    *pcdMin = 0;
744    *pcdMax = 0;
745
746    for (int ii = 0; psd->awd[ ii ].id != idENDLIST; ++ii)
747       {
748       HWND hControl;
749       if ((hControl = GetDlgItem (hWnd, psd->awd[ ii ].id)) != NULL)
750          {
751          RECT rControl;
752          GetRectInParent (hControl, &rControl);
753
754          if (psd->fX)
755             {
756             LONG cxMin = 0;
757             LONG cxMax = 0;
758
759             if (LOWORD(psd->awd[ ii ].cMinimum)) // X minimum?
760                {
761                if (psd->awd[ ii ].ra & raSizeX)
762                   cxMin = cOld - cxRECT(rControl) + LOWORD(psd->awd[ ii ].cMinimum);
763                else if (psd->awd[ ii ].ra & raSizeXB)
764                   cxMax = cOld + cxRECT(rControl) - LOWORD(psd->awd[ ii ].cMinimum);
765                else if (psd->awd[ ii ].ra & raSizeXC)
766                   cxMin = cOld - (cxRECT(rControl) - LOWORD(psd->awd[ ii ].cMinimum))*2;
767                }
768
769             if (LOWORD(psd->awd[ ii ].cMaximum)) // X maximum?
770                {
771                if (psd->awd[ ii ].ra & raSizeX)
772                   cxMax = cOld - cxRECT(rControl) + LOWORD(psd->awd[ ii ].cMaximum);
773                else if (psd->awd[ ii ].ra & raSizeXB)
774                   cxMin = cOld + cxRECT(rControl) - LOWORD(psd->awd[ ii ].cMaximum);
775                else if (psd->awd[ ii ].ra & raSizeXC)
776                   cxMin = cOld - (cxRECT(rControl) - LOWORD(psd->awd[ ii ].cMaximum))*2;
777                }
778
779             if (cxMin)  *pcdMin = (*pcdMin) ? max( *pcdMin, cxMin ) : cxMin;
780             if (cxMax)  *pcdMax = (*pcdMax) ? min( *pcdMax, cxMax ) : cxMax;
781             }
782          else
783             {
784             LONG cyMin = 0;
785             LONG cyMax = 0;
786
787             if (HIWORD(psd->awd[ ii ].cMinimum)) // Y minimum?
788                {
789                if (psd->awd[ ii ].ra & raSizeY)
790                   cyMin = cOld - cyRECT(rControl) + HIWORD(psd->awd[ ii ].cMinimum);
791                else if (psd->awd[ ii ].ra & raSizeYB)
792                   cyMax = cOld + cyRECT(rControl) - HIWORD(psd->awd[ ii ].cMinimum);
793                else if (psd->awd[ ii ].ra & raSizeYC)
794                   cyMin = cOld - (cyRECT(rControl) - HIWORD(psd->awd[ ii ].cMinimum))*2;
795                }
796
797             if (HIWORD(psd->awd[ ii ].cMaximum)) // Y maximum?
798                {
799                if (psd->awd[ ii ].ra & raSizeY)
800                   cyMax = cOld - cyRECT(rControl) + HIWORD(psd->awd[ ii ].cMaximum);
801                else if (psd->awd[ ii ].ra & raSizeYB)
802                   cyMin = cOld + cyRECT(rControl) - HIWORD(psd->awd[ ii ].cMaximum);
803                else if (psd->awd[ ii ].ra & raSizeYC)
804                   cyMin = cOld - (cyRECT(rControl) - HIWORD(psd->awd[ ii ].cMaximum))*2;
805                }
806
807             if (cyMin)  *pcdMin = (*pcdMin) ? max( *pcdMin, cyMin ) : cyMin;
808             if (cyMax)  *pcdMax = (*pcdMax) ? min( *pcdMax, cyMax ) : cyMax;
809             }
810          }
811       }
812 }
813
814
815 void FindResizeLimits (HWND hWnd, LONG *pcxMin, LONG *pcxMax, LONG *pcyMin, LONG *pcyMax, rwWindowData *awd)
816 {
817    *pcxMin = 0;
818    *pcxMax = 0;
819    *pcyMin = 0;
820    *pcyMax = 0;
821
822    if (awd == NULL)
823       {
824       int iwl;
825       if ((iwl = rwFindOrAddWnd (hWnd)) == -1)
826          return;
827
828       if ((awd = awl[ iwl ].awdLast) == NULL)
829          return;
830       }
831
832    RECT rNow;
833    GetWindowRect (hWnd, &rNow);
834
835    for (DWORD ii = 0; awd[ ii ].id != idENDLIST; ++ii)
836       {
837       HWND hControl;
838       if ((hControl = GetDlgItem (hWnd, awd[ ii ].id)) != NULL)
839          {
840          RECT rControl;
841          GetRectInParent (hControl, &rControl);
842
843          LONG cxMin = 0;
844          LONG cyMin = 0;
845          LONG cxMax = 0;
846          LONG cyMax = 0;
847
848          if (LOWORD(awd[ ii ].cMinimum)) // X minimum?
849             {
850             if (awd[ ii ].ra & raSizeX)
851                cxMin = cxRECT(rNow) - cxRECT(rControl) + LOWORD(awd[ ii ].cMinimum);
852             else if (awd[ ii ].ra & raSizeXB)
853                cxMax = cxRECT(rNow) + cxRECT(rControl) - LOWORD(awd[ ii ].cMinimum);
854             else if (awd[ ii ].ra & raSizeXC)
855                cxMin = cxRECT(rNow) - (cxRECT(rControl) - LOWORD(awd[ ii ].cMinimum))*2;
856             }
857
858          if (LOWORD(awd[ ii ].cMaximum)) // X maximum?
859             {
860             if (awd[ ii ].ra & raSizeX)
861                cxMax = cxRECT(rNow) - cxRECT(rControl) + LOWORD(awd[ ii ].cMaximum);
862             else if (awd[ ii ].ra & raSizeXB)
863                cxMin = cxRECT(rNow) + cxRECT(rControl) - LOWORD(awd[ ii ].cMaximum);
864             else if (awd[ ii ].ra & raSizeXC)
865                cxMax = cxRECT(rNow) - (cxRECT(rControl) - LOWORD(awd[ ii ].cMaximum))*2;
866             }
867
868          if (HIWORD(awd[ ii ].cMinimum)) // Y minimum?
869             {
870             if (awd[ ii ].ra & raSizeY)
871                cyMin = cyRECT(rNow) - cyRECT(rControl) + HIWORD(awd[ ii ].cMinimum);
872             else if (awd[ ii ].ra & raSizeYB)
873                cyMax = cyRECT(rNow) + cyRECT(rControl) - HIWORD(awd[ ii ].cMinimum);
874             else if (awd[ ii ].ra & raSizeYC)
875                cyMin = cyRECT(rNow) - (cyRECT(rControl) - HIWORD(awd[ ii ].cMinimum))*2;
876             }
877
878          if (HIWORD(awd[ ii ].cMaximum)) // Y maximum?
879             {
880             if (awd[ ii ].ra & raSizeY)
881                cyMax = cyRECT(rNow) - cyRECT(rControl) + HIWORD(awd[ ii ].cMaximum);
882             else if (awd[ ii ].ra & raSizeYB)
883                cyMin = cyRECT(rNow) + cyRECT(rControl) - HIWORD(awd[ ii ].cMaximum);
884             else if (awd[ ii ].ra & raSizeYC)
885                cyMax = cyRECT(rNow) - (cyRECT(rControl) - HIWORD(awd[ ii ].cMaximum))*2;
886             }
887
888          if (cxMin)  *pcxMin = (*pcxMin) ? max( *pcxMin, cxMin ) : cxMin;
889          if (cyMin)  *pcyMin = (*pcyMin) ? max( *pcyMin, cyMin ) : cyMin;
890          if (cxMax)  *pcxMax = (*pcxMax) ? min( *pcxMax, cxMax ) : cxMax;
891          if (cyMax)  *pcyMax = (*pcyMax) ? min( *pcyMax, cyMax ) : cyMax;
892          }
893       }
894 }
895
896
897 BOOL CALLBACK Resize_DialogProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
898 {
899    PVOID fnNext = Subclass_FindNextHook (hWnd, Resize_DialogProc);
900
901    switch (msg)
902       {
903       case WM_GETMINMAXINFO:
904          LONG cxMin;
905          LONG cyMin;
906          LONG cxMax;
907          LONG cyMax;
908          FindResizeLimits (hWnd, &cxMin, &cxMax, &cyMin, &cyMax);
909
910          LPMINMAXINFO lpmmi;
911          lpmmi = (LPMINMAXINFO)lp;
912
913          if (cxMin)
914             lpmmi->ptMinTrackSize.x = cxMin;
915          if (cyMin)
916             lpmmi->ptMinTrackSize.y = cyMin;
917          if (cxMax)
918             lpmmi->ptMaxTrackSize.x = cxMax;
919          if (cyMax)
920             lpmmi->ptMaxTrackSize.y = cyMax;
921          return FALSE;
922
923       case WM_DESTROY:
924          rwFindAndRemoveWnd (hWnd);
925          break;
926       }
927
928    return (fnNext) ? CallWindowProc ((WNDPROC)fnNext, hWnd, msg, wp, lp) : FALSE;
929 }
930