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?
311 for (ii = 0; ii < (int)cwl; ii++)
313 if (awl[ii].hWnd == hWnd)
316 awl[ii].awdLast = awd;
323 for (ii = 0; ii < (int)cwl; ii++)
325 if (awl[ii].hWnd == NULL)
330 if (!REALLOC (awl, cwl, ii+1, 1))
335 awl[ii].awdLast = awd;
339 GetWindowRect (hWnd, &awl[ii].rWnd);
340 Subclass_AddHook (hWnd, Resize_DialogProc);
344 awl[ii].rWnd.left = 0;
345 awl[ii].rWnd.right = 0;
346 awl[ii].rWnd.top = 0;
347 awl[ii].rWnd.bottom = 0;
349 awl[ii].cxDeltaCenter = 0;
350 awl[ii].cyDeltaCenter = 0;
356 void rwFindAndRemoveWnd (HWND hWnd)
358 for (size_t ii = 0; ii < (int)cwl; ii++)
360 if (awl[ii].hWnd == hWnd)
362 Subclass_RemoveHook (awl[ii].hWnd, hWnd);
370 DWORD rwFindAction (rwWindowData *awd, int id)
372 DWORD raDefault = raLeaveAlone;
376 for (int ii = 0; awd[ii].id != idENDLIST; ++ii)
378 if (awd[ii].id == id)
380 if (awd[ii].id == idDEFAULT)
381 raDefault = awd[ii].ra;
389 void GetRectInParent (HWND hWnd, RECT *pr)
393 GetWindowRect (hWnd, pr);
395 pr->right -= pr->left;
396 pr->bottom -= pr->top; // right/bottom == width/height for now
401 ScreenToClient (GetParent (hWnd), &pt);
405 pr->right += pr->left;
406 pr->bottom += pr->top;
411 * SPLITTERS __________________________________________________________________
415 static BOOL fRegistered = FALSE; // TRUE If registered class
417 static TCHAR cszSplitterClassX[] = TEXT("SplitterWindowClassX");
418 static TCHAR cszSplitterClassY[] = TEXT("SplitterWindowClassY");
421 HWND CreateSplitter (HWND hWnd, int id1, int id2, int id,
422 LONG *pcd, rwWindowData *awd, BOOL fMovedAlready)
428 if (!WhereShouldSplitterGo (hWnd, id1, id2, &rWnd, &fX))
431 EnsureSplitterRegistered ();
433 psd = (SplitterData *)Allocate (sizeof(SplitterData));
442 psd->fMovedBeforeCreate = fMovedAlready;
445 (fX) ? cszSplitterClassX : cszSplitterClassY,
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
459 void DeleteSplitter (HWND hWnd, int id1)
463 if (hSplit = GetDlgItem (hWnd, id1))
465 DestroyWindow (hSplit);
469 void EnsureSplitterRegistered (void)
477 wc.style = CS_HREDRAW | CS_VREDRAW;
478 wc.lpfnWndProc = SplitterWndProc;
480 wc.cbWndExtra = sizeof(SplitterData *);
481 wc.hInstance = THIS_HINST;
483 wc.hbrBackground = CreateSolidBrush( GetSysColor( COLOR_BTNFACE ));
484 wc.lpszMenuName = NULL;
486 // Register the X-moving class:
489 wc.hCursor = LoadCursor (THIS_HINST, MAKEINTRESOURCE( IDC_HSPLIT ));
491 wc.hCursor = LoadCursor (NULL, IDC_SIZEWE);
494 wc.lpszClassName = cszSplitterClassX;
496 (void)RegisterClass (&wc);
498 // Register the Y-moving class:
501 wc.hCursor = LoadCursor (THIS_HINST, MAKEINTRESOURCE( IDC_VSPLIT ));
503 wc.hCursor = LoadCursor (NULL, IDC_SIZENS);
506 wc.lpszClassName = cszSplitterClassY;
508 (void)RegisterClass (&wc);
513 BOOL WhereShouldSplitterGo (HWND hWnd, int id1, int id2, RECT *prWnd, BOOL *pfX)
518 GetRectInParent (GetDlgItem (hWnd, id1), &r1);
519 GetRectInParent (GetDlgItem (hWnd, id2), &r2);
521 if (r2.left > r1.right) // R1 on left, R2 on right?
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;
529 else if (r2.right < r1.left) // R2 on left, R1 on right?
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;
537 else if (r2.top > r1.bottom) // R1 on top, R2 on bottom?
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;
545 else if (r2.bottom < r1.top) // R2 on top, R1 on bottom?
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;
553 else // Rectangles intersect!
554 { // Don't know where it should go.
562 LONG APIENTRY SplitterWndProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
565 static LONG cdAtStart;
567 if (msg == WM_CREATE)
569 SetWindowLong (hWnd,GWL_USER,(LONG)((LPCREATESTRUCT)lp)->lpCreateParams);
572 if ((psd = (SplitterData *)GetWindowLong (hWnd, GWL_USER)) != NULL)
577 if (!psd->fMovedBeforeCreate)
578 ResizeSplitter (GetParent (hWnd), psd, 0, psd->pcDelta);
579 psd->fDragging = FALSE;
586 psd->fDragging = TRUE;
587 cdAtStart = *psd->pcDelta;
589 GetCursorPos (&psd->ptStart);
590 ScreenToClient (GetParent (hWnd), &psd->ptStart);
602 ScreenToClient (GetParent (hWnd), &pt);
604 cx = (LONG)pt.x - (LONG)psd->ptStart.x;
605 cy = (LONG)pt.y - (LONG)psd->ptStart.y;
612 if (cd != *(psd->pcDelta))
614 ResizeSplitter (GetParent(hWnd), psd, *psd->pcDelta, &cd);
624 psd->fDragging = FALSE;
632 psd->fDragging = FALSE;
635 psd = NULL; // fault blatantly if you use {psd} now
636 SetWindowLong (hWnd, GWL_USER, 0);
640 #if 0 // Enable me to make the splitters draw in black
644 HDC hdc = BeginPaint (hWnd, &ps);
645 FillRect (hdc, &ps.rcPaint, GetStockObject(BLACK_BRUSH));
646 EndPaint (hWnd, &ps);
654 return DefWindowProc (hWnd, msg, wp, lp);
658 void ResizeSplitter (HWND hWnd, SplitterData *psd, LONG cOld, LONG *pcNew)
665 if (psd == NULL || pcNew == NULL || hWnd == NULL)
668 FindSplitterMinMax (hWnd, psd, cOld, &cdMin, &cdMax);
669 *pcNew = limit (cdMin, *pcNew, cdMax);
674 dx = (psd->fX) ? (*pcNew - cOld) : 0;
675 dy = (psd->fX) ? 0 : (*pcNew - cOld);
677 for (hItem = GetWindow (hWnd, GW_CHILD);
679 hItem = GetWindow (hItem, GW_HWNDNEXT))
686 BOOL fRepaint = FALSE;
687 HDWP dwp = BeginDeferWindowPos (nItems);
689 for (hItem = GetWindow (hWnd, GW_CHILD);
691 hItem = GetWindow (hItem, GW_HWNDNEXT))
696 GetRectInParent (hItem, &rItem);
698 ra = rwFindAction (psd->awd, (GetWindowLong (hItem, GWL_ID)));
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);
714 EndDeferWindowPos (dwp);
716 for (hItem = GetWindow (hWnd, GW_CHILD);
718 hItem = GetWindow (hItem, GW_HWNDNEXT))
720 DWORD ra = rwFindAction (psd->awd, (GetWindowLong (hItem, GWL_ID)));
726 GetRectInParent (hItem, &rItem);
728 InvalidateRect (hWnd, &rItem, TRUE);
741 void FindSplitterMinMax (HWND hWnd, SplitterData *psd, LONG cOld, LONG *pcdMin, LONG *pcdMax)
746 for (int ii = 0; psd->awd[ ii ].id != idENDLIST; ++ii)
749 if ((hControl = GetDlgItem (hWnd, psd->awd[ ii ].id)) != NULL)
752 GetRectInParent (hControl, &rControl);
759 if (LOWORD(psd->awd[ ii ].cMinimum)) // X minimum?
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;
769 if (LOWORD(psd->awd[ ii ].cMaximum)) // X maximum?
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;
779 if (cxMin) *pcdMin = (*pcdMin) ? max( *pcdMin, cxMin ) : cxMin;
780 if (cxMax) *pcdMax = (*pcdMax) ? min( *pcdMax, cxMax ) : cxMax;
787 if (HIWORD(psd->awd[ ii ].cMinimum)) // Y minimum?
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;
797 if (HIWORD(psd->awd[ ii ].cMaximum)) // Y maximum?
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;
807 if (cyMin) *pcdMin = (*pcdMin) ? max( *pcdMin, cyMin ) : cyMin;
808 if (cyMax) *pcdMax = (*pcdMax) ? min( *pcdMax, cyMax ) : cyMax;
815 void FindResizeLimits (HWND hWnd, LONG *pcxMin, LONG *pcxMax, LONG *pcyMin, LONG *pcyMax, rwWindowData *awd)
825 if ((iwl = rwFindOrAddWnd (hWnd)) == -1)
828 if ((awd = awl[ iwl ].awdLast) == NULL)
833 GetWindowRect (hWnd, &rNow);
835 for (DWORD ii = 0; awd[ ii ].id != idENDLIST; ++ii)
838 if ((hControl = GetDlgItem (hWnd, awd[ ii ].id)) != NULL)
841 GetRectInParent (hControl, &rControl);
848 if (LOWORD(awd[ ii ].cMinimum)) // X minimum?
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;
858 if (LOWORD(awd[ ii ].cMaximum)) // X maximum?
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;
868 if (HIWORD(awd[ ii ].cMinimum)) // Y minimum?
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;
878 if (HIWORD(awd[ ii ].cMaximum)) // Y maximum?
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;
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;
897 BOOL CALLBACK Resize_DialogProc (HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
899 PVOID fnNext = Subclass_FindNextHook (hWnd, Resize_DialogProc);
903 case WM_GETMINMAXINFO:
908 FindResizeLimits (hWnd, &cxMin, &cxMax, &cyMin, &cyMax);
911 lpmmi = (LPMINMAXINFO)lp;
914 lpmmi->ptMinTrackSize.x = cxMin;
916 lpmmi->ptMinTrackSize.y = cyMin;
918 lpmmi->ptMaxTrackSize.x = cxMax;
920 lpmmi->ptMaxTrackSize.y = cyMax;
924 rwFindAndRemoveWnd (hWnd);
928 return (fnNext) ? CallWindowProc ((WNDPROC)fnNext, hWnd, msg, wp, lp) : FALSE;