2 * Copyright 2000, International Business Machines Corporation and others.
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
11 #include <afs/param.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>
24 * DEFINITIONS ________________________________________________________________
29 #define limit(_a,_x,_b) (min( max( (_a),(_x) ), (_b) ))
32 #define inlimit(_a,_x,_b) ( (_x >= _a) && (_x <= _b) )
36 #define THIS_HINST (GetModuleHandle (NULL))
44 #define cxRECT(_r) ((_r).right - (_r).left)
47 #define cyRECT(_r) ((_r).bottom - (_r).top)
50 typedef struct // SplitterData
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
64 * VARIABLES __________________________________________________________________
74 rwWindowData *awdLast;
77 static WindowList *awl;
78 static size_t cwl = 0;
82 * PROTOTYPES _________________________________________________________________
86 int rwFindOrAddWnd (HWND, rwWindowData * = 0);
87 void rwFindAndRemoveWnd (HWND);
88 DWORD rwFindAction (rwWindowData *, int);
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,
97 void FindResizeLimits (HWND hWnd, LONG *pcxMin, LONG *pcxMax, LONG *pcyMin, LONG *pcyMax, rwWindowData * = 0);
99 BOOL CALLBACK Resize_DialogProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
103 * ROUTINES ___________________________________________________________________
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)
114 if (cReq <= *pcTarget)
117 if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
120 if ((pNew = (LPVOID)Allocate (cbElement * cNew)) == NULL)
122 memset (pNew, 0x00, cbElement * cNew);
126 memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
137 void ResizeWindow (HWND hWnd, rwWindowData *awd, rwAction rwa, RECT *pr)
139 static BOOL fInHere = FALSE; // prevent reentrancy during SetWindowPos().
146 // We maintain list of where-was-this-window-last-time; find
147 // This window in that list, or add it.
149 if ((ii = rwFindOrAddWnd (hWnd, awd)) == -1)
152 rOld = awl[ii].rWnd; // previous position
154 // If the window disappears, then remove its entry from the
157 if (!IsWindow (hWnd))
163 // If told to, move and/or resize this parent window.
165 if (rwa == rwaMoveToHere)
172 SetWindowPos (hWnd, NULL,
175 pr->right - pr->left,
176 pr->bottom - pr->top,
177 SWP_NOZORDER | SWP_NOACTIVATE);
185 GetWindowRect (hWnd, &rNew);
188 // Window has moved from {rOld} to {rNew},
190 // Window's client area has been changed by {*pr}
192 if (rwa == rwaMoveToHere || rwa == rwaFixupGuts || rwa == rwaNewClientArea)
197 if (rwa == rwaNewClientArea)
199 dx = pr->right - pr->left;
200 dy = pr->bottom - pr->top;
204 dx = (rNew.right -rNew.left) - (rOld.right -rOld.left);
205 dy = (rNew.bottom -rNew.top) - (rOld.bottom -rOld.top);
209 awl[ii].cxDeltaCenter += (dx > 0) ? 1 : -1;
211 awl[ii].cyDeltaCenter += (dy > 0) ? 1 : -1;
213 dxc = dx + awl[ii].cxDeltaCenter/2;
214 dyc = dy + awl[ii].cyDeltaCenter/2;
216 awl[ii].cxDeltaCenter %= 2;
217 awl[ii].cyDeltaCenter %= 2;
219 if (dx != 0 || dy != 0)
224 for (hItem = GetWindow (hWnd, GW_CHILD);
226 hItem = GetWindow (hItem, GW_HWNDNEXT))
234 HDWP dwp = BeginDeferWindowPos (nItems);
235 BOOL fRepaint = FALSE;
237 for (hItem = GetWindow (hWnd, GW_CHILD);
239 hItem = GetWindow (hItem, GW_HWNDNEXT))
243 GetRectInParent (hItem, &rItem);
245 ra = rwFindAction (awd, (GetWindowLong (hItem, GWL_ID)));
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);
265 EndDeferWindowPos (dwp);
267 for (hItem = GetWindow (hWnd, GW_CHILD);
269 hItem = GetWindow (hItem, GW_HWNDNEXT))
271 ra = rwFindAction (awd, (GetWindowLong (hItem, GWL_ID)));
276 GetRectInParent (hItem, &rItem);
277 InvalidateRect (hWnd, &rItem, TRUE);
284 GetClientRect (hItem, &rClient);
285 SendMessage (hItem, WM_SIZE, SIZE_RESTORED, MAKELPARAM( rClient.right-rClient.left, rClient.bottom-rClient.top ));
297 // Record this window's current position
306 int rwFindOrAddWnd (HWND hWnd, rwWindowData *awd)
308 // Is the window handle listed in awl[] already?
310 for (int ii = 0; ii < (int)cwl; ii++)
312 if (awl[ii].hWnd == hWnd)
315 awl[ii].awdLast = awd;
322 for (ii = 0; ii < (int)cwl; ii++)
324 if (awl[ii].hWnd == NULL)
329 if (!REALLOC (awl, cwl, ii+1, 1))
334 awl[ii].awdLast = awd;
338 GetWindowRect (hWnd, &awl[ii].rWnd);
339 Subclass_AddHook (hWnd, Resize_DialogProc);
343 awl[ii].rWnd.left = 0;
344 awl[ii].rWnd.right = 0;
345 awl[ii].rWnd.top = 0;
346 awl[ii].rWnd.bottom = 0;
348 awl[ii].cxDeltaCenter = 0;
349 awl[ii].cyDeltaCenter = 0;
355 void rwFindAndRemoveWnd (HWND hWnd)
357 for (size_t ii = 0; ii < (int)cwl; ii++)
359 if (awl[ii].hWnd == hWnd)
361 Subclass_RemoveHook (awl[ii].hWnd, hWnd);
369 DWORD rwFindAction (rwWindowData *awd, int id)
371 DWORD raDefault = raLeaveAlone;
375 for (int ii = 0; awd[ii].id != idENDLIST; ++ii)
377 if (awd[ii].id == id)
379 if (awd[ii].id == idDEFAULT)
380 raDefault = awd[ii].ra;
388 void GetRectInParent (HWND hWnd, RECT *pr)
392 GetWindowRect (hWnd, pr);
394 pr->right -= pr->left;
395 pr->bottom -= pr->top; // right/bottom == width/height for now
400 ScreenToClient (GetParent (hWnd), &pt);
404 pr->right += pr->left;
405 pr->bottom += pr->top;
410 * SPLITTERS __________________________________________________________________
414 static BOOL fRegistered = FALSE; // TRUE If registered class
416 static TCHAR cszSplitterClassX[] = TEXT("SplitterWindowClassX");
417 static TCHAR cszSplitterClassY[] = TEXT("SplitterWindowClassY");
420 HWND CreateSplitter (HWND hWnd, int id1, int id2, int id,
421 LONG *pcd, rwWindowData *awd, BOOL fMovedAlready)
427 if (!WhereShouldSplitterGo (hWnd, id1, id2, &rWnd, &fX))
430 EnsureSplitterRegistered ();
432 psd = (SplitterData *)Allocate (sizeof(SplitterData));
441 psd->fMovedBeforeCreate = fMovedAlready;
444 (fX) ? cszSplitterClassX : cszSplitterClassY,
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
458 void DeleteSplitter (HWND hWnd, int id1)
462 if (hSplit = GetDlgItem (hWnd, id1))
464 DestroyWindow (hSplit);
468 void EnsureSplitterRegistered (void)
476 wc.style = CS_HREDRAW | CS_VREDRAW;
477 wc.lpfnWndProc = SplitterWndProc;
479 wc.cbWndExtra = sizeof(SplitterData *);
480 wc.hInstance = THIS_HINST;
482 wc.hbrBackground = CreateSolidBrush( GetSysColor( COLOR_BTNFACE ));
483 wc.lpszMenuName = NULL;
485 // Register the X-moving class:
488 wc.hCursor = LoadCursor (THIS_HINST, MAKEINTRESOURCE( IDC_HSPLIT ));
490 wc.hCursor = LoadCursor (NULL, IDC_SIZEWE);
493 wc.lpszClassName = cszSplitterClassX;
495 (void)RegisterClass (&wc);
497 // Register the Y-moving class:
500 wc.hCursor = LoadCursor (THIS_HINST, MAKEINTRESOURCE( IDC_VSPLIT ));
502 wc.hCursor = LoadCursor (NULL, IDC_SIZENS);
505 wc.lpszClassName = cszSplitterClassY;
507 (void)RegisterClass (&wc);
512 BOOL WhereShouldSplitterGo (HWND hWnd, int id1, int id2, RECT *prWnd, BOOL *pfX)
517 GetRectInParent (GetDlgItem (hWnd, id1), &r1);
518 GetRectInParent (GetDlgItem (hWnd, id2), &r2);
520 if (r2.left > r1.right) // R1 on left, R2 on right?
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;
528 else if (r2.right < r1.left) // R2 on left, R1 on right?
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;
536 else if (r2.top > r1.bottom) // R1 on top, R2 on bottom?
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;
544 else if (r2.bottom < r1.top) // R2 on top, R1 on bottom?
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;
552 else // Rectangles intersect!
553 { // Don't know where it should go.
561 LONG APIENTRY SplitterWndProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
564 static LONG cdAtStart;
566 if (msg == WM_CREATE)
568 SetWindowLong (hWnd,GWL_USER,(LONG)((LPCREATESTRUCT)lp)->lpCreateParams);
571 if ((psd = (SplitterData *)GetWindowLong (hWnd, GWL_USER)) != NULL)
576 if (!psd->fMovedBeforeCreate)
577 ResizeSplitter (GetParent (hWnd), psd, 0, psd->pcDelta);
578 psd->fDragging = FALSE;
585 psd->fDragging = TRUE;
586 cdAtStart = *psd->pcDelta;
588 GetCursorPos (&psd->ptStart);
589 ScreenToClient (GetParent (hWnd), &psd->ptStart);
601 ScreenToClient (GetParent (hWnd), &pt);
603 cx = (LONG)pt.x - (LONG)psd->ptStart.x;
604 cy = (LONG)pt.y - (LONG)psd->ptStart.y;
611 if (cd != *(psd->pcDelta))
613 ResizeSplitter (GetParent(hWnd), psd, *psd->pcDelta, &cd);
623 psd->fDragging = FALSE;
631 psd->fDragging = FALSE;
634 psd = NULL; // fault blatantly if you use {psd} now
635 SetWindowLong (hWnd, GWL_USER, 0);
639 #if 0 // Enable me to make the splitters draw in black
643 HDC hdc = BeginPaint (hWnd, &ps);
644 FillRect (hdc, &ps.rcPaint, GetStockObject(BLACK_BRUSH));
645 EndPaint (hWnd, &ps);
653 return DefWindowProc (hWnd, msg, wp, lp);
657 void ResizeSplitter (HWND hWnd, SplitterData *psd, LONG cOld, LONG *pcNew)
664 if (psd == NULL || pcNew == NULL || hWnd == NULL)
667 FindSplitterMinMax (hWnd, psd, cOld, &cdMin, &cdMax);
668 *pcNew = limit (cdMin, *pcNew, cdMax);
673 dx = (psd->fX) ? (*pcNew - cOld) : 0;
674 dy = (psd->fX) ? 0 : (*pcNew - cOld);
676 for (hItem = GetWindow (hWnd, GW_CHILD);
678 hItem = GetWindow (hItem, GW_HWNDNEXT))
685 BOOL fRepaint = FALSE;
686 HDWP dwp = BeginDeferWindowPos (nItems);
688 for (hItem = GetWindow (hWnd, GW_CHILD);
690 hItem = GetWindow (hItem, GW_HWNDNEXT))
695 GetRectInParent (hItem, &rItem);
697 ra = rwFindAction (psd->awd, (GetWindowLong (hItem, GWL_ID)));
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);
713 EndDeferWindowPos (dwp);
715 for (hItem = GetWindow (hWnd, GW_CHILD);
717 hItem = GetWindow (hItem, GW_HWNDNEXT))
719 DWORD ra = rwFindAction (psd->awd, (GetWindowLong (hItem, GWL_ID)));
725 GetRectInParent (hItem, &rItem);
727 InvalidateRect (hWnd, &rItem, TRUE);
740 void FindSplitterMinMax (HWND hWnd, SplitterData *psd, LONG cOld, LONG *pcdMin, LONG *pcdMax)
745 for (int ii = 0; psd->awd[ ii ].id != idENDLIST; ++ii)
748 if ((hControl = GetDlgItem (hWnd, psd->awd[ ii ].id)) != NULL)
751 GetRectInParent (hControl, &rControl);
758 if (LOWORD(psd->awd[ ii ].cMinimum)) // X minimum?
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;
768 if (LOWORD(psd->awd[ ii ].cMaximum)) // X maximum?
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;
778 if (cxMin) *pcdMin = (*pcdMin) ? max( *pcdMin, cxMin ) : cxMin;
779 if (cxMax) *pcdMax = (*pcdMax) ? min( *pcdMax, cxMax ) : cxMax;
786 if (HIWORD(psd->awd[ ii ].cMinimum)) // Y minimum?
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;
796 if (HIWORD(psd->awd[ ii ].cMaximum)) // Y maximum?
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;
806 if (cyMin) *pcdMin = (*pcdMin) ? max( *pcdMin, cyMin ) : cyMin;
807 if (cyMax) *pcdMax = (*pcdMax) ? min( *pcdMax, cyMax ) : cyMax;
814 void FindResizeLimits (HWND hWnd, LONG *pcxMin, LONG *pcxMax, LONG *pcyMin, LONG *pcyMax, rwWindowData *awd)
824 if ((iwl = rwFindOrAddWnd (hWnd)) == -1)
827 if ((awd = awl[ iwl ].awdLast) == NULL)
832 GetWindowRect (hWnd, &rNow);
834 for (DWORD ii = 0; awd[ ii ].id != idENDLIST; ++ii)
837 if ((hControl = GetDlgItem (hWnd, awd[ ii ].id)) != NULL)
840 GetRectInParent (hControl, &rControl);
847 if (LOWORD(awd[ ii ].cMinimum)) // X minimum?
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;
857 if (LOWORD(awd[ ii ].cMaximum)) // X maximum?
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;
867 if (HIWORD(awd[ ii ].cMinimum)) // Y minimum?
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;
877 if (HIWORD(awd[ ii ].cMaximum)) // Y maximum?
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;
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;
896 BOOL CALLBACK Resize_DialogProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
898 PVOID fnNext = Subclass_FindNextHook (hWnd, Resize_DialogProc);
902 case WM_GETMINMAXINFO:
907 FindResizeLimits (hWnd, &cxMin, &cxMax, &cyMin, &cyMax);
910 lpmmi = (LPMINMAXINFO)lp;
913 lpmmi->ptMinTrackSize.x = cxMin;
915 lpmmi->ptMinTrackSize.y = cyMin;
917 lpmmi->ptMaxTrackSize.x = cxMax;
919 lpmmi->ptMaxTrackSize.y = cyMax;
923 rwFindAndRemoveWnd (hWnd);
927 return (fnNext) ? CallWindowProc ((WNDPROC)fnNext, hWnd, msg, wp, lp) : FALSE;