comparison foosdk/wtl/Include/atlsplit.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 // Windows Template Library - WTL version 10.0
2 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
3 //
4 // This file is a part of the Windows Template Library.
5 // The use and distribution terms for this software are covered by the
6 // Microsoft Public License (http://opensource.org/licenses/MS-PL)
7 // which can be found in the file MS-PL.txt at the root folder.
8
9 #ifndef __ATLSPLIT_H__
10 #define __ATLSPLIT_H__
11
12 #pragma once
13
14 #ifndef __ATLAPP_H__
15 #error atlsplit.h requires atlapp.h to be included first
16 #endif
17
18 #ifndef __ATLWIN_H__
19 #error atlsplit.h requires atlwin.h to be included first
20 #endif
21
22
23 ///////////////////////////////////////////////////////////////////////////////
24 // Classes in this file:
25 //
26 // CSplitterImpl<T>
27 // CSplitterWindowImpl<T, TBase, TWinTraits>
28 // CSplitterWindowT<t_bVertical> - CSplitterWindow, CHorSplitterWindow
29
30
31 namespace WTL
32 {
33
34 ///////////////////////////////////////////////////////////////////////////////
35 // CSplitterImpl - Provides splitter support to any window
36
37 // Splitter panes constants
38 #define SPLIT_PANE_LEFT 0
39 #define SPLIT_PANE_RIGHT 1
40 #define SPLIT_PANE_TOP SPLIT_PANE_LEFT
41 #define SPLIT_PANE_BOTTOM SPLIT_PANE_RIGHT
42 #define SPLIT_PANE_NONE -1
43
44 // Splitter extended styles
45 #define SPLIT_PROPORTIONAL 0x00000001
46 #define SPLIT_NONINTERACTIVE 0x00000002
47 #define SPLIT_RIGHTALIGNED 0x00000004
48 #define SPLIT_BOTTOMALIGNED SPLIT_RIGHTALIGNED
49 #define SPLIT_GRADIENTBAR 0x00000008
50 #define SPLIT_FLATBAR 0x00000020
51 #define SPLIT_FIXEDBARSIZE 0x00000010
52
53 // Note: SPLIT_PROPORTIONAL and SPLIT_RIGHTALIGNED/SPLIT_BOTTOMALIGNED are
54 // mutually exclusive. If both are set, splitter defaults to SPLIT_PROPORTIONAL.
55 // Also, SPLIT_FLATBAR overrides SPLIT_GRADIENTBAR if both are set.
56
57
58 template <class T>
59 class CSplitterImpl
60 {
61 public:
62 enum { m_nPanesCount = 2, m_nPropMax = INT_MAX, m_cxyStep = 10 };
63
64 bool m_bVertical;
65 HWND m_hWndPane[m_nPanesCount];
66 RECT m_rcSplitter;
67 int m_xySplitterPos; // splitter bar position
68 int m_xySplitterPosNew; // internal - new position while moving
69 HWND m_hWndFocusSave;
70 int m_nDefActivePane;
71 int m_cxySplitBar; // splitter bar width/height
72 HCURSOR m_hCursor;
73 int m_cxyMin; // minimum pane size
74 int m_cxyBarEdge; // splitter bar edge
75 bool m_bFullDrag;
76 int m_cxyDragOffset; // internal
77 int m_nProportionalPos;
78 bool m_bUpdateProportionalPos;
79 DWORD m_dwExtendedStyle; // splitter specific extended styles
80 int m_nSinglePane; // single pane mode
81 int m_xySplitterDefPos; // default position
82 bool m_bProportionalDefPos; // porportinal def pos
83
84 // Constructor
85 CSplitterImpl(bool bVertical = true) :
86 m_bVertical(bVertical), m_xySplitterPos(-1), m_xySplitterPosNew(-1), m_hWndFocusSave(NULL),
87 m_nDefActivePane(SPLIT_PANE_NONE), m_cxySplitBar(4), m_hCursor(NULL), m_cxyMin(0), m_cxyBarEdge(0),
88 m_bFullDrag(true), m_cxyDragOffset(0), m_nProportionalPos(0), m_bUpdateProportionalPos(true),
89 m_dwExtendedStyle(SPLIT_PROPORTIONAL), m_nSinglePane(SPLIT_PANE_NONE),
90 m_xySplitterDefPos(-1), m_bProportionalDefPos(false)
91 {
92 m_hWndPane[SPLIT_PANE_LEFT] = NULL;
93 m_hWndPane[SPLIT_PANE_RIGHT] = NULL;
94
95 ::SetRectEmpty(&m_rcSplitter);
96 }
97
98 // Attributes
99 void SetSplitterRect(LPRECT lpRect = NULL, bool bUpdate = true)
100 {
101 if(lpRect == NULL)
102 {
103 T* pT = static_cast<T*>(this);
104 pT->GetClientRect(&m_rcSplitter);
105 }
106 else
107 {
108 m_rcSplitter = *lpRect;
109 }
110
111 if(IsProportional())
112 UpdateProportionalPos();
113 else if(IsRightAligned())
114 UpdateRightAlignPos();
115
116 if(bUpdate)
117 UpdateSplitterLayout();
118 }
119
120 void GetSplitterRect(LPRECT lpRect) const
121 {
122 ATLASSERT(lpRect != NULL);
123 *lpRect = m_rcSplitter;
124 }
125
126 bool SetSplitterPos(int xyPos = -1, bool bUpdate = true)
127 {
128 if(xyPos == -1) // -1 == default position
129 {
130 if(m_bProportionalDefPos)
131 {
132 ATLASSERT((m_xySplitterDefPos >= 0) && (m_xySplitterDefPos <= m_nPropMax));
133
134 if(m_bVertical)
135 xyPos = ::MulDiv(m_xySplitterDefPos, m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge, m_nPropMax);
136 else
137 xyPos = ::MulDiv(m_xySplitterDefPos, m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge, m_nPropMax);
138 }
139 else if(m_xySplitterDefPos != -1)
140 {
141 xyPos = m_xySplitterDefPos;
142 }
143 else // not set, use middle position
144 {
145 if(m_bVertical)
146 xyPos = (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) / 2;
147 else
148 xyPos = (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge) / 2;
149 }
150 }
151
152 // Adjust if out of valid range
153 int cxyMax = 0;
154 if(m_bVertical)
155 cxyMax = m_rcSplitter.right - m_rcSplitter.left;
156 else
157 cxyMax = m_rcSplitter.bottom - m_rcSplitter.top;
158
159 if(xyPos < m_cxyMin + m_cxyBarEdge)
160 xyPos = m_cxyMin;
161 else if(xyPos > (cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin))
162 xyPos = cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin;
163
164 // Set new position and update if requested
165 bool bRet = (m_xySplitterPos != xyPos);
166 m_xySplitterPos = xyPos;
167
168 if(m_bUpdateProportionalPos)
169 {
170 if(IsProportional())
171 StoreProportionalPos();
172 else if(IsRightAligned())
173 StoreRightAlignPos();
174 }
175 else
176 {
177 m_bUpdateProportionalPos = true;
178 }
179
180 if(bUpdate && bRet)
181 UpdateSplitterLayout();
182
183 return bRet;
184 }
185
186 int GetSplitterPos() const
187 {
188 return m_xySplitterPos;
189 }
190
191 void SetSplitterPosPct(int nPct, bool bUpdate = true)
192 {
193 ATLASSERT((nPct >= 0) && (nPct <= 100));
194
195 m_nProportionalPos = ::MulDiv(nPct, m_nPropMax, 100);
196 UpdateProportionalPos();
197
198 if(bUpdate)
199 UpdateSplitterLayout();
200 }
201
202 int GetSplitterPosPct() const
203 {
204 int cxyTotal = m_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);
205 return ((cxyTotal > 0) && (m_xySplitterPos >= 0)) ? ::MulDiv(m_xySplitterPos, 100, cxyTotal) : -1;
206 }
207
208 bool SetSinglePaneMode(int nPane = SPLIT_PANE_NONE)
209 {
210 ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT) || (nPane == SPLIT_PANE_NONE));
211 if(!((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT) || (nPane == SPLIT_PANE_NONE)))
212 return false;
213
214 if(nPane != SPLIT_PANE_NONE)
215 {
216 if(::IsWindowVisible(m_hWndPane[nPane]) == FALSE)
217 ::ShowWindow(m_hWndPane[nPane], SW_SHOW);
218 int nOtherPane = (nPane == SPLIT_PANE_LEFT) ? SPLIT_PANE_RIGHT : SPLIT_PANE_LEFT;
219 ::ShowWindow(m_hWndPane[nOtherPane], SW_HIDE);
220 if(m_nDefActivePane != nPane)
221 m_nDefActivePane = nPane;
222 }
223 else if(m_nSinglePane != SPLIT_PANE_NONE)
224 {
225 int nOtherPane = (m_nSinglePane == SPLIT_PANE_LEFT) ? SPLIT_PANE_RIGHT : SPLIT_PANE_LEFT;
226 ::ShowWindow(m_hWndPane[nOtherPane], SW_SHOW);
227 }
228
229 m_nSinglePane = nPane;
230 UpdateSplitterLayout();
231
232 return true;
233 }
234
235 int GetSinglePaneMode() const
236 {
237 return m_nSinglePane;
238 }
239
240 DWORD GetSplitterExtendedStyle() const
241 {
242 return m_dwExtendedStyle;
243 }
244
245 DWORD SetSplitterExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
246 {
247 DWORD dwPrevStyle = m_dwExtendedStyle;
248 if(dwMask == 0)
249 m_dwExtendedStyle = dwExtendedStyle;
250 else
251 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
252
253 #ifdef _DEBUG
254 if(IsProportional() && IsRightAligned())
255 ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::SetSplitterExtendedStyle - SPLIT_PROPORTIONAL and SPLIT_RIGHTALIGNED are mutually exclusive, defaulting to SPLIT_PROPORTIONAL.\n"));
256 #endif // _DEBUG
257
258 return dwPrevStyle;
259 }
260
261 void SetSplitterDefaultPos(int xyPos = -1)
262 {
263 m_xySplitterDefPos = xyPos;
264 m_bProportionalDefPos = false;
265 }
266
267 void SetSplitterDefaultPosPct(int nPct)
268 {
269 ATLASSERT((nPct >= 0) && (nPct <= 100));
270
271 m_xySplitterDefPos = ::MulDiv(nPct, m_nPropMax, 100);
272 m_bProportionalDefPos = true;
273 }
274
275 // Splitter operations
276 void SetSplitterPanes(HWND hWndLeftTop, HWND hWndRightBottom, bool bUpdate = true)
277 {
278 m_hWndPane[SPLIT_PANE_LEFT] = hWndLeftTop;
279 m_hWndPane[SPLIT_PANE_RIGHT] = hWndRightBottom;
280 ATLASSERT((m_hWndPane[SPLIT_PANE_LEFT] == NULL) || (m_hWndPane[SPLIT_PANE_RIGHT] == NULL) || (m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RIGHT]));
281 if(bUpdate)
282 UpdateSplitterLayout();
283 }
284
285 bool SetSplitterPane(int nPane, HWND hWnd, bool bUpdate = true)
286 {
287 ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT));
288 if((nPane != SPLIT_PANE_LEFT) && (nPane != SPLIT_PANE_RIGHT))
289 return false;
290
291 m_hWndPane[nPane] = hWnd;
292 ATLASSERT((m_hWndPane[SPLIT_PANE_LEFT] == NULL) || (m_hWndPane[SPLIT_PANE_RIGHT] == NULL) || (m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RIGHT]));
293 if(bUpdate)
294 UpdateSplitterLayout();
295
296 return true;
297 }
298
299 HWND GetSplitterPane(int nPane) const
300 {
301 ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT));
302 if((nPane != SPLIT_PANE_LEFT) && (nPane != SPLIT_PANE_RIGHT))
303 return NULL;
304
305 return m_hWndPane[nPane];
306 }
307
308 bool SetActivePane(int nPane)
309 {
310 ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT));
311 if((nPane != SPLIT_PANE_LEFT) && (nPane != SPLIT_PANE_RIGHT))
312 return false;
313 if((m_nSinglePane != SPLIT_PANE_NONE) && (nPane != m_nSinglePane))
314 return false;
315
316 ::SetFocus(m_hWndPane[nPane]);
317 m_nDefActivePane = nPane;
318
319 return true;
320 }
321
322 int GetActivePane() const
323 {
324 int nRet = SPLIT_PANE_NONE;
325 HWND hWndFocus = ::GetFocus();
326 if(hWndFocus != NULL)
327 {
328 for(int nPane = 0; nPane < m_nPanesCount; nPane++)
329 {
330 if((hWndFocus == m_hWndPane[nPane]) || (::IsChild(m_hWndPane[nPane], hWndFocus) != FALSE))
331 {
332 nRet = nPane;
333 break;
334 }
335 }
336 }
337
338 return nRet;
339 }
340
341 bool ActivateNextPane(bool bNext = true)
342 {
343 int nPane = m_nSinglePane;
344 if(nPane == SPLIT_PANE_NONE)
345 {
346 switch(GetActivePane())
347 {
348 case SPLIT_PANE_LEFT:
349 nPane = SPLIT_PANE_RIGHT;
350 break;
351 case SPLIT_PANE_RIGHT:
352 nPane = SPLIT_PANE_LEFT;
353 break;
354 default:
355 nPane = bNext ? SPLIT_PANE_LEFT : SPLIT_PANE_RIGHT;
356 break;
357 }
358 }
359
360 return SetActivePane(nPane);
361 }
362
363 bool SetDefaultActivePane(int nPane)
364 {
365 ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT));
366 if((nPane != SPLIT_PANE_LEFT) && (nPane != SPLIT_PANE_RIGHT))
367 return false;
368
369 m_nDefActivePane = nPane;
370
371 return true;
372 }
373
374 bool SetDefaultActivePane(HWND hWnd)
375 {
376 for(int nPane = 0; nPane < m_nPanesCount; nPane++)
377 {
378 if(hWnd == m_hWndPane[nPane])
379 {
380 m_nDefActivePane = nPane;
381 return true;
382 }
383 }
384
385 return false; // not found
386 }
387
388 int GetDefaultActivePane() const
389 {
390 return m_nDefActivePane;
391 }
392
393 void DrawSplitter(CDCHandle dc)
394 {
395 ATLASSERT(dc.m_hDC != NULL);
396 if((m_nSinglePane == SPLIT_PANE_NONE) && (m_xySplitterPos == -1))
397 return;
398
399 T* pT = static_cast<T*>(this);
400 if(m_nSinglePane == SPLIT_PANE_NONE)
401 {
402 pT->DrawSplitterBar(dc);
403
404 for(int nPane = 0; nPane < m_nPanesCount; nPane++)
405 {
406 if(m_hWndPane[nPane] == NULL)
407 pT->DrawSplitterPane(dc, nPane);
408 }
409 }
410 else
411 {
412 if(m_hWndPane[m_nSinglePane] == NULL)
413 pT->DrawSplitterPane(dc, m_nSinglePane);
414 }
415 }
416
417 // call to initiate moving splitter bar with keyboard
418 void MoveSplitterBar()
419 {
420 T* pT = static_cast<T*>(this);
421
422 int x = 0;
423 int y = 0;
424 if(m_bVertical)
425 {
426 x = m_xySplitterPos + (m_cxySplitBar / 2) + m_cxyBarEdge;
427 y = (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge) / 2;
428 }
429 else
430 {
431 x = (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) / 2;
432 y = m_xySplitterPos + (m_cxySplitBar / 2) + m_cxyBarEdge;
433 }
434
435 POINT pt = { x, y };
436 pT->ClientToScreen(&pt);
437 ::SetCursorPos(pt.x, pt.y);
438
439 m_xySplitterPosNew = m_xySplitterPos;
440 pT->SetCapture();
441 m_hWndFocusSave = pT->SetFocus();
442 ::SetCursor(m_hCursor);
443 if(!m_bFullDrag)
444 DrawGhostBar();
445 if(m_bVertical)
446 m_cxyDragOffset = x - m_rcSplitter.left - m_xySplitterPos;
447 else
448 m_cxyDragOffset = y - m_rcSplitter.top - m_xySplitterPos;
449 }
450
451 void SetOrientation(bool bVertical, bool bUpdate = true)
452 {
453 if(m_bVertical != bVertical)
454 {
455 m_bVertical = bVertical;
456
457 m_hCursor = ::LoadCursor(NULL, m_bVertical ? IDC_SIZEWE : IDC_SIZENS);
458
459 T* pT = static_cast<T*>(this);
460 pT->GetSystemSettings(false);
461
462 if(m_bVertical)
463 m_xySplitterPos = ::MulDiv(m_xySplitterPos, m_rcSplitter.right - m_rcSplitter.left, m_rcSplitter.bottom - m_rcSplitter.top);
464 else
465 m_xySplitterPos = ::MulDiv(m_xySplitterPos, m_rcSplitter.bottom - m_rcSplitter.top, m_rcSplitter.right - m_rcSplitter.left);
466 }
467
468 if(bUpdate)
469 UpdateSplitterLayout();
470 }
471
472 // Overrideables
473 void DrawSplitterBar(CDCHandle dc)
474 {
475 RECT rect = {};
476 if(GetSplitterBarRect(&rect))
477 {
478 dc.FillRect(&rect, COLOR_3DFACE);
479
480 if((m_dwExtendedStyle & SPLIT_FLATBAR) != 0)
481 {
482 RECT rect1 = rect;
483 if(m_bVertical)
484 rect1.right = rect1.left + 1;
485 else
486 rect1.bottom = rect1.top + 1;
487 dc.FillRect(&rect1, COLOR_WINDOW);
488
489 rect1 = rect;
490 if(m_bVertical)
491 rect1.left = rect1.right - 1;
492 else
493 rect1.top = rect1.bottom - 1;
494 dc.FillRect(&rect1, COLOR_3DSHADOW);
495 }
496 else if((m_dwExtendedStyle & SPLIT_GRADIENTBAR) != 0)
497 {
498 RECT rect2 = rect;
499 if(m_bVertical)
500 rect2.left = (rect.left + rect.right) / 2 - 1;
501 else
502 rect2.top = (rect.top + rect.bottom) / 2 - 1;
503
504 dc.GradientFillRect(rect2, ::GetSysColor(COLOR_3DFACE), ::GetSysColor(COLOR_3DSHADOW), m_bVertical);
505 }
506
507 // draw 3D edge if needed
508 T* pT = static_cast<T*>(this);
509 if((pT->GetExStyle() & WS_EX_CLIENTEDGE) != 0)
510 dc.DrawEdge(&rect, EDGE_RAISED, m_bVertical ? (BF_LEFT | BF_RIGHT) : (BF_TOP | BF_BOTTOM));
511 }
512 }
513
514 // called only if pane is empty
515 void DrawSplitterPane(CDCHandle dc, int nPane)
516 {
517 RECT rect = {};
518 if(GetSplitterPaneRect(nPane, &rect))
519 {
520 T* pT = static_cast<T*>(this);
521 if((pT->GetExStyle() & WS_EX_CLIENTEDGE) == 0)
522 dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
523 dc.FillRect(&rect, COLOR_APPWORKSPACE);
524 }
525 }
526
527 // Message map and handlers
528 BEGIN_MSG_MAP(CSplitterImpl)
529 MESSAGE_HANDLER(WM_CREATE, OnCreate)
530 MESSAGE_HANDLER(WM_PAINT, OnPaint)
531 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
532 if(IsInteractive())
533 {
534 MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
535 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
536 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
537 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
538 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDoubleClick)
539 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
540 MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
541 }
542 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
543 MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
544 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
545 END_MSG_MAP()
546
547 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
548 {
549 T* pT = static_cast<T*>(this);
550 pT->Init();
551
552 bHandled = FALSE;
553 return 1;
554 }
555
556 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
557 {
558 T* pT = static_cast<T*>(this);
559
560 // try setting position if not set
561 if((m_nSinglePane == SPLIT_PANE_NONE) && (m_xySplitterPos == -1))
562 pT->SetSplitterPos();
563
564 // do painting
565 if(wParam != NULL)
566 {
567 pT->DrawSplitter((HDC)wParam);
568 }
569 else
570 {
571 CPaintDC dc(pT->m_hWnd);
572 pT->DrawSplitter(dc.m_hDC);
573 }
574
575 return 0;
576 }
577
578 LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
579 {
580 T* pT = static_cast<T*>(this);
581 if(((HWND)wParam == pT->m_hWnd) && (LOWORD(lParam) == HTCLIENT))
582 {
583 DWORD dwPos = ::GetMessagePos();
584 POINT ptPos = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };
585 pT->ScreenToClient(&ptPos);
586 if(IsOverSplitterBar(ptPos.x, ptPos.y))
587 return 1;
588 }
589
590 bHandled = FALSE;
591 return 0;
592 }
593
594 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
595 {
596 T* pT = static_cast<T*>(this);
597 int xPos = GET_X_LPARAM(lParam);
598 int yPos = GET_Y_LPARAM(lParam);
599 if(::GetCapture() == pT->m_hWnd)
600 {
601 int xyNewSplitPos = 0;
602 if(m_bVertical)
603 xyNewSplitPos = xPos - m_rcSplitter.left - m_cxyDragOffset;
604 else
605 xyNewSplitPos = yPos - m_rcSplitter.top - m_cxyDragOffset;
606
607 if(xyNewSplitPos == -1) // avoid -1, that means default position
608 xyNewSplitPos = -2;
609
610 if(m_xySplitterPos != xyNewSplitPos)
611 {
612 if(m_bFullDrag)
613 {
614 if(pT->SetSplitterPos(xyNewSplitPos, true))
615 pT->UpdateWindow();
616 }
617 else
618 {
619 DrawGhostBar();
620 pT->SetSplitterPos(xyNewSplitPos, false);
621 DrawGhostBar();
622 }
623 }
624 }
625 else // not dragging, just set cursor
626 {
627 if(IsOverSplitterBar(xPos, yPos))
628 ::SetCursor(m_hCursor);
629 bHandled = FALSE;
630 }
631
632 return 0;
633 }
634
635 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
636 {
637 T* pT = static_cast<T*>(this);
638 int xPos = GET_X_LPARAM(lParam);
639 int yPos = GET_Y_LPARAM(lParam);
640 if((::GetCapture() != pT->m_hWnd) && IsOverSplitterBar(xPos, yPos))
641 {
642 m_xySplitterPosNew = m_xySplitterPos;
643 pT->SetCapture();
644 m_hWndFocusSave = pT->SetFocus();
645 ::SetCursor(m_hCursor);
646 if(!m_bFullDrag)
647 DrawGhostBar();
648 if(m_bVertical)
649 m_cxyDragOffset = xPos - m_rcSplitter.left - m_xySplitterPos;
650 else
651 m_cxyDragOffset = yPos - m_rcSplitter.top - m_xySplitterPos;
652 }
653 else if((::GetCapture() == pT->m_hWnd) && !IsOverSplitterBar(xPos, yPos))
654 {
655 ::ReleaseCapture();
656 }
657
658 bHandled = FALSE;
659 return 1;
660 }
661
662 LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
663 {
664 T* pT = static_cast<T*>(this);
665 if(::GetCapture() == pT->m_hWnd)
666 {
667 m_xySplitterPosNew = m_xySplitterPos;
668 ::ReleaseCapture();
669 }
670
671 bHandled = FALSE;
672 return 1;
673 }
674
675 LRESULT OnLButtonDoubleClick(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
676 {
677 T* pT = static_cast<T*>(this);
678 pT->SetSplitterPos(); // default
679
680 return 0;
681 }
682
683 LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
684 {
685 if(!m_bFullDrag)
686 DrawGhostBar();
687
688 if((m_xySplitterPosNew != -1) && (!m_bFullDrag || (m_xySplitterPos != m_xySplitterPosNew)))
689 {
690 m_xySplitterPos = m_xySplitterPosNew;
691 m_xySplitterPosNew = -1;
692 UpdateSplitterLayout();
693 T* pT = static_cast<T*>(this);
694 pT->UpdateWindow();
695 }
696
697 if(m_hWndFocusSave != NULL)
698 ::SetFocus(m_hWndFocusSave);
699
700 return 0;
701 }
702
703 LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
704 {
705 T* pT = static_cast<T*>(this);
706 if(::GetCapture() == pT->m_hWnd)
707 {
708 switch(wParam)
709 {
710 case VK_RETURN:
711 m_xySplitterPosNew = m_xySplitterPos;
712 // FALLTHROUGH
713 case VK_ESCAPE:
714 ::ReleaseCapture();
715 break;
716 case VK_LEFT:
717 case VK_RIGHT:
718 if(m_bVertical)
719 {
720 POINT pt = {};
721 ::GetCursorPos(&pt);
722 int xyPos = m_xySplitterPos + ((wParam == VK_LEFT) ? -pT->m_cxyStep : pT->m_cxyStep);
723 int cxyMax = m_rcSplitter.right - m_rcSplitter.left;
724 if(xyPos < (m_cxyMin + m_cxyBarEdge))
725 xyPos = m_cxyMin;
726 else if(xyPos > (cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin))
727 xyPos = cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin;
728 pt.x += xyPos - m_xySplitterPos;
729 ::SetCursorPos(pt.x, pt.y);
730 }
731 break;
732 case VK_UP:
733 case VK_DOWN:
734 if(!m_bVertical)
735 {
736 POINT pt = {};
737 ::GetCursorPos(&pt);
738 int xyPos = m_xySplitterPos + ((wParam == VK_UP) ? -pT->m_cxyStep : pT->m_cxyStep);
739 int cxyMax = m_rcSplitter.bottom - m_rcSplitter.top;
740 if(xyPos < (m_cxyMin + m_cxyBarEdge))
741 xyPos = m_cxyMin;
742 else if(xyPos > (cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin))
743 xyPos = cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin;
744 pt.y += xyPos - m_xySplitterPos;
745 ::SetCursorPos(pt.x, pt.y);
746 }
747 break;
748 default:
749 break;
750 }
751 }
752 else
753 {
754 bHandled = FALSE;
755 }
756
757 return 0;
758 }
759
760 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM, BOOL& bHandled)
761 {
762 T* pT = static_cast<T*>(this);
763 if(::GetCapture() != pT->m_hWnd)
764 {
765 if(m_nSinglePane == SPLIT_PANE_NONE)
766 {
767 if((m_nDefActivePane == SPLIT_PANE_LEFT) || (m_nDefActivePane == SPLIT_PANE_RIGHT))
768 ::SetFocus(m_hWndPane[m_nDefActivePane]);
769 }
770 else
771 {
772 ::SetFocus(m_hWndPane[m_nSinglePane]);
773 }
774 }
775
776 bHandled = FALSE;
777 return 1;
778 }
779
780 LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
781 {
782 T* pT = static_cast<T*>(this);
783 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
784 if((lRet == MA_ACTIVATE) || (lRet == MA_ACTIVATEANDEAT))
785 {
786 DWORD dwPos = ::GetMessagePos();
787 POINT pt = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };
788 pT->ScreenToClient(&pt);
789 RECT rcPane = {};
790 for(int nPane = 0; nPane < m_nPanesCount; nPane++)
791 {
792 if(GetSplitterPaneRect(nPane, &rcPane) && (::PtInRect(&rcPane, pt) != FALSE))
793 {
794 m_nDefActivePane = nPane;
795 break;
796 }
797 }
798 }
799
800 return lRet;
801 }
802
803 LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
804 {
805 T* pT = static_cast<T*>(this);
806 pT->GetSystemSettings(true);
807
808 return 0;
809 }
810
811 // Implementation - internal helpers
812 void Init()
813 {
814 m_hCursor = ::LoadCursor(NULL, m_bVertical ? IDC_SIZEWE : IDC_SIZENS);
815
816 T* pT = static_cast<T*>(this);
817 pT->GetSystemSettings(false);
818 }
819
820 void UpdateSplitterLayout()
821 {
822 if((m_nSinglePane == SPLIT_PANE_NONE) && (m_xySplitterPos == -1))
823 return;
824
825 T* pT = static_cast<T*>(this);
826 RECT rect = {};
827 if(m_nSinglePane == SPLIT_PANE_NONE)
828 {
829 if(GetSplitterBarRect(&rect))
830 pT->InvalidateRect(&rect);
831
832 for(int nPane = 0; nPane < m_nPanesCount; nPane++)
833 {
834 if(GetSplitterPaneRect(nPane, &rect))
835 {
836 if(m_hWndPane[nPane] != NULL)
837 ::SetWindowPos(m_hWndPane[nPane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
838 else
839 pT->InvalidateRect(&rect);
840 }
841 }
842 }
843 else
844 {
845 if(GetSplitterPaneRect(m_nSinglePane, &rect))
846 {
847 if(m_hWndPane[m_nSinglePane] != NULL)
848 ::SetWindowPos(m_hWndPane[m_nSinglePane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);
849 else
850 pT->InvalidateRect(&rect);
851 }
852 }
853 }
854
855 bool GetSplitterBarRect(LPRECT lpRect) const
856 {
857 ATLASSERT(lpRect != NULL);
858 if((m_nSinglePane != SPLIT_PANE_NONE) || (m_xySplitterPos == -1))
859 return false;
860
861 if(m_bVertical)
862 {
863 lpRect->left = m_rcSplitter.left + m_xySplitterPos;
864 lpRect->top = m_rcSplitter.top;
865 lpRect->right = m_rcSplitter.left + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;
866 lpRect->bottom = m_rcSplitter.bottom;
867 }
868 else
869 {
870 lpRect->left = m_rcSplitter.left;
871 lpRect->top = m_rcSplitter.top + m_xySplitterPos;
872 lpRect->right = m_rcSplitter.right;
873 lpRect->bottom = m_rcSplitter.top + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;
874 }
875
876 return true;
877 }
878
879 bool GetSplitterPaneRect(int nPane, LPRECT lpRect) const
880 {
881 ATLASSERT((nPane == SPLIT_PANE_LEFT) || (nPane == SPLIT_PANE_RIGHT));
882 ATLASSERT(lpRect != NULL);
883 bool bRet = true;
884 if(m_nSinglePane != SPLIT_PANE_NONE)
885 {
886 if(nPane == m_nSinglePane)
887 *lpRect = m_rcSplitter;
888 else
889 bRet = false;
890 }
891 else if(nPane == SPLIT_PANE_LEFT)
892 {
893 if(m_bVertical)
894 {
895 lpRect->left = m_rcSplitter.left;
896 lpRect->top = m_rcSplitter.top;
897 lpRect->right = m_rcSplitter.left + m_xySplitterPos;
898 lpRect->bottom = m_rcSplitter.bottom;
899 }
900 else
901 {
902 lpRect->left = m_rcSplitter.left;
903 lpRect->top = m_rcSplitter.top;
904 lpRect->right = m_rcSplitter.right;
905 lpRect->bottom = m_rcSplitter.top + m_xySplitterPos;
906 }
907 }
908 else if(nPane == SPLIT_PANE_RIGHT)
909 {
910 if(m_bVertical)
911 {
912 lpRect->left = m_rcSplitter.left + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;
913 lpRect->top = m_rcSplitter.top;
914 lpRect->right = m_rcSplitter.right;
915 lpRect->bottom = m_rcSplitter.bottom;
916 }
917 else
918 {
919 lpRect->left = m_rcSplitter.left;
920 lpRect->top = m_rcSplitter.top + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;
921 lpRect->right = m_rcSplitter.right;
922 lpRect->bottom = m_rcSplitter.bottom;
923 }
924 }
925 else
926 {
927 bRet = false;
928 }
929
930 return bRet;
931 }
932
933 bool IsOverSplitterRect(int x, int y) const
934 {
935 // -1 == don't check
936 return (((x == -1) || ((x >= m_rcSplitter.left) && (x <= m_rcSplitter.right))) &&
937 ((y == -1) || ((y >= m_rcSplitter.top) && (y <= m_rcSplitter.bottom))));
938 }
939
940 bool IsOverSplitterBar(int x, int y) const
941 {
942 if(m_nSinglePane != SPLIT_PANE_NONE)
943 return false;
944 if((m_xySplitterPos == -1) || !IsOverSplitterRect(x, y))
945 return false;
946 int xy = m_bVertical ? x : y;
947 int xyOff = m_bVertical ? m_rcSplitter.left : m_rcSplitter.top;
948
949 return ((xy >= (xyOff + m_xySplitterPos)) && (xy < (xyOff + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge)));
950 }
951
952 void DrawGhostBar()
953 {
954 RECT rect = {};
955 if(GetSplitterBarRect(&rect))
956 {
957 // convert client to window coordinates
958 T* pT = static_cast<T*>(this);
959 RECT rcWnd = {};
960 pT->GetWindowRect(&rcWnd);
961 ::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rcWnd, 2);
962 ::OffsetRect(&rect, -rcWnd.left, -rcWnd.top);
963
964 // invert the brush pattern (looks just like frame window sizing)
965 CWindowDC dc(pT->m_hWnd);
966 CBrush brush(CDCHandle::GetHalftoneBrush());
967 if(brush.m_hBrush != NULL)
968 {
969 CBrushHandle brushOld = dc.SelectBrush(brush);
970 dc.PatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT);
971 dc.SelectBrush(brushOld);
972 }
973 }
974 }
975
976 void GetSystemSettings(bool bUpdate)
977 {
978 if((m_dwExtendedStyle & SPLIT_FIXEDBARSIZE) == 0)
979 {
980 m_cxySplitBar = ::GetSystemMetrics(m_bVertical ? SM_CXSIZEFRAME : SM_CYSIZEFRAME);
981 }
982
983 T* pT = static_cast<T*>(this);
984 if((pT->GetExStyle() & WS_EX_CLIENTEDGE) != 0)
985 {
986 m_cxyBarEdge = 2 * ::GetSystemMetrics(m_bVertical ? SM_CXEDGE : SM_CYEDGE);
987 m_cxyMin = 0;
988 }
989 else
990 {
991 m_cxyBarEdge = 0;
992 m_cxyMin = 2 * ::GetSystemMetrics(m_bVertical ? SM_CXEDGE : SM_CYEDGE);
993 }
994
995 ::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &m_bFullDrag, 0);
996
997 if(bUpdate)
998 UpdateSplitterLayout();
999 }
1000
1001 bool IsProportional() const
1002 {
1003 return ((m_dwExtendedStyle & SPLIT_PROPORTIONAL) != 0);
1004 }
1005
1006 void StoreProportionalPos()
1007 {
1008 int cxyTotal = m_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);
1009 if(cxyTotal > 0)
1010 m_nProportionalPos = ::MulDiv(m_xySplitterPos, m_nPropMax, cxyTotal);
1011 else
1012 m_nProportionalPos = 0;
1013 ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::StoreProportionalPos - %i\n"), m_nProportionalPos);
1014 }
1015
1016 void UpdateProportionalPos()
1017 {
1018 int cxyTotal = m_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);
1019 if(cxyTotal > 0)
1020 {
1021 int xyNewPos = ::MulDiv(m_nProportionalPos, cxyTotal, m_nPropMax);
1022 m_bUpdateProportionalPos = false;
1023 T* pT = static_cast<T*>(this);
1024 pT->SetSplitterPos(xyNewPos, false);
1025 }
1026 }
1027
1028 bool IsRightAligned() const
1029 {
1030 return ((m_dwExtendedStyle & SPLIT_RIGHTALIGNED) != 0);
1031 }
1032
1033 void StoreRightAlignPos()
1034 {
1035 int cxyTotal = m_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);
1036 if(cxyTotal > 0)
1037 m_nProportionalPos = cxyTotal - m_xySplitterPos;
1038 else
1039 m_nProportionalPos = 0;
1040 ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::StoreRightAlignPos - %i\n"), m_nProportionalPos);
1041 }
1042
1043 void UpdateRightAlignPos()
1044 {
1045 int cxyTotal = m_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);
1046 if(cxyTotal > 0)
1047 {
1048 m_bUpdateProportionalPos = false;
1049 T* pT = static_cast<T*>(this);
1050 pT->SetSplitterPos(cxyTotal - m_nProportionalPos, false);
1051 }
1052 }
1053
1054 bool IsInteractive() const
1055 {
1056 return ((m_dwExtendedStyle & SPLIT_NONINTERACTIVE) == 0);
1057 }
1058 };
1059
1060
1061 ///////////////////////////////////////////////////////////////////////////////
1062 // CSplitterWindowImpl - Implements a splitter window
1063
1064 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
1065 class ATL_NO_VTABLE CSplitterWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CSplitterImpl< T >
1066 {
1067 public:
1068 DECLARE_WND_CLASS_EX2(NULL, T, CS_DBLCLKS, COLOR_WINDOW)
1069
1070 CSplitterWindowImpl(bool bVertical = true) : CSplitterImpl< T >(bVertical)
1071 { }
1072
1073 BOOL SubclassWindow(HWND hWnd)
1074 {
1075 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
1076 if(bRet != FALSE)
1077 {
1078 T* pT = static_cast<T*>(this);
1079 pT->Init();
1080
1081 this->SetSplitterRect();
1082 }
1083
1084 return bRet;
1085 }
1086
1087 BEGIN_MSG_MAP(CSplitterWindowImpl)
1088 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
1089 MESSAGE_HANDLER(WM_SIZE, OnSize)
1090 CHAIN_MSG_MAP(CSplitterImpl< T >)
1091 FORWARD_NOTIFICATIONS()
1092 END_MSG_MAP()
1093
1094 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1095 {
1096 // handled, no background painting needed
1097 return 1;
1098 }
1099
1100 LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
1101 {
1102 if(wParam != SIZE_MINIMIZED)
1103 this->SetSplitterRect();
1104
1105 bHandled = FALSE;
1106 return 1;
1107 }
1108 };
1109
1110
1111 ///////////////////////////////////////////////////////////////////////////////
1112 // CSplitterWindow/CHorSplitterWindow - Implements splitter windows to be used as is
1113
1114 template <bool t_bVertical = true>
1115 class CSplitterWindowT : public CSplitterWindowImpl<CSplitterWindowT<t_bVertical> >
1116 {
1117 public:
1118 DECLARE_WND_CLASS_EX2(_T("WTL_SplitterWindow"), CSplitterWindowT<t_bVertical>, CS_DBLCLKS, COLOR_WINDOW)
1119
1120 CSplitterWindowT() : CSplitterWindowImpl<CSplitterWindowT<t_bVertical> >(t_bVertical)
1121 { }
1122 };
1123
1124 typedef CSplitterWindowT<true> CSplitterWindow;
1125 typedef CSplitterWindowT<false> CHorSplitterWindow;
1126
1127 } // namespace WTL
1128
1129 #endif // __ATLSPLIT_H__