]> git.sesse.net Git - casparcg/blob - WTL80/include/atlsplit.h
2.0.2: INFO TEMPLATE works on both compressed and uncompressed templates.
[casparcg] / WTL80 / include / atlsplit.h
1 // Windows Template Library - WTL version 8.0\r
2 // Copyright (C) Microsoft Corporation. All rights reserved.\r
3 //\r
4 // This file is a part of the Windows Template Library.\r
5 // The use and distribution terms for this software are covered by the\r
6 // Common Public License 1.0 (http://opensource.org/osi3.0/licenses/cpl1.0.php)\r
7 // which can be found in the file CPL.TXT at the root of this distribution.\r
8 // By using this software in any fashion, you are agreeing to be bound by\r
9 // the terms of this license. You must not remove this notice, or\r
10 // any other, from this software.\r
11 \r
12 #ifndef __ATLSPLIT_H__\r
13 #define __ATLSPLIT_H__\r
14 \r
15 #pragma once\r
16 \r
17 #ifndef __cplusplus\r
18         #error ATL requires C++ compilation (use a .cpp suffix)\r
19 #endif\r
20 \r
21 #ifndef __ATLAPP_H__\r
22         #error atlsplit.h requires atlapp.h to be included first\r
23 #endif\r
24 \r
25 #ifndef __ATLWIN_H__\r
26         #error atlsplit.h requires atlwin.h to be included first\r
27 #endif\r
28 \r
29 \r
30 ///////////////////////////////////////////////////////////////////////////////\r
31 // Classes in this file:\r
32 //\r
33 // CSplitterImpl<T, t_bVertical>\r
34 // CSplitterWindowImpl<T, t_bVertical, TBase, TWinTraits>\r
35 // CSplitterWindowT<t_bVertical>\r
36 \r
37 \r
38 namespace WTL\r
39 {\r
40 \r
41 ///////////////////////////////////////////////////////////////////////////////\r
42 // CSplitterImpl - Provides splitter support to any window\r
43 \r
44 // Splitter panes constants\r
45 #define SPLIT_PANE_LEFT                  0\r
46 #define SPLIT_PANE_RIGHT                 1\r
47 #define SPLIT_PANE_TOP                   SPLIT_PANE_LEFT\r
48 #define SPLIT_PANE_BOTTOM                SPLIT_PANE_RIGHT\r
49 #define SPLIT_PANE_NONE                 -1\r
50 \r
51 // Splitter extended styles\r
52 #define SPLIT_PROPORTIONAL              0x00000001\r
53 #define SPLIT_NONINTERACTIVE            0x00000002\r
54 #define SPLIT_RIGHTALIGNED              0x00000004\r
55 #define SPLIT_BOTTOMALIGNED             SPLIT_RIGHTALIGNED\r
56 \r
57 // Note: SPLIT_PROPORTIONAL and SPLIT_RIGHTALIGNED/SPLIT_BOTTOMALIGNED are \r
58 // mutually exclusive. If both are set, splitter defaults to SPLIT_PROPORTIONAL\r
59 \r
60 \r
61 template <class T, bool t_bVertical = true>\r
62 class CSplitterImpl\r
63 {\r
64 public:\r
65         enum { m_nPanesCount = 2, m_nPropMax = 10000 };\r
66 \r
67         HWND m_hWndPane[m_nPanesCount];\r
68         RECT m_rcSplitter;\r
69         int m_xySplitterPos;\r
70         int m_nDefActivePane;\r
71         int m_cxySplitBar;              // splitter bar width/height\r
72         static HCURSOR m_hCursor;\r
73         int m_cxyMin;                   // minimum pane size\r
74         int m_cxyBarEdge;               // splitter bar edge\r
75         bool m_bFullDrag;\r
76         int m_cxyDragOffset;\r
77         int m_nProportionalPos;\r
78         bool m_bUpdateProportionalPos;\r
79         DWORD m_dwExtendedStyle;       // splitter specific extended styles\r
80         int m_nSinglePane;             // single pane mode\r
81 \r
82 // Constructor\r
83         CSplitterImpl() :\r
84                         m_xySplitterPos(-1), m_nDefActivePane(SPLIT_PANE_NONE), \r
85                         m_cxySplitBar(0), m_cxyMin(0), m_cxyBarEdge(0), m_bFullDrag(true), \r
86                         m_cxyDragOffset(0), m_nProportionalPos(0), m_bUpdateProportionalPos(true),\r
87                         m_dwExtendedStyle(SPLIT_PROPORTIONAL),\r
88                         m_nSinglePane(SPLIT_PANE_NONE)\r
89         {\r
90                 m_hWndPane[SPLIT_PANE_LEFT] = NULL;\r
91                 m_hWndPane[SPLIT_PANE_RIGHT] = NULL;\r
92 \r
93                 ::SetRectEmpty(&m_rcSplitter);\r
94 \r
95                 if(m_hCursor == NULL)\r
96                 {\r
97                         CStaticDataInitCriticalSectionLock lock;\r
98                         if(FAILED(lock.Lock()))\r
99                         {\r
100                                 ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CSplitterImpl::CSplitterImpl.\n"));\r
101                                 ATLASSERT(FALSE);\r
102                                 return;\r
103                         }\r
104 \r
105                         if(m_hCursor == NULL)\r
106                                 m_hCursor = ::LoadCursor(NULL, t_bVertical ? IDC_SIZEWE : IDC_SIZENS);\r
107 \r
108                         lock.Unlock();\r
109                 }\r
110         }\r
111 \r
112 // Attributes\r
113         void SetSplitterRect(LPRECT lpRect = NULL, bool bUpdate = true)\r
114         {\r
115                 if(lpRect == NULL)\r
116                 {\r
117                         T* pT = static_cast<T*>(this);\r
118                         pT->GetClientRect(&m_rcSplitter);\r
119                 }\r
120                 else\r
121                 {\r
122                         m_rcSplitter = *lpRect;\r
123                 }\r
124 \r
125                 if(IsProportional())\r
126                         UpdateProportionalPos();\r
127                 else if(IsRightAligned())\r
128                         UpdateRightAlignPos();\r
129 \r
130                 if(bUpdate)\r
131                         UpdateSplitterLayout();\r
132         }\r
133 \r
134         void GetSplitterRect(LPRECT lpRect) const\r
135         {\r
136                 ATLASSERT(lpRect != NULL);\r
137                 *lpRect = m_rcSplitter;\r
138         }\r
139 \r
140         bool SetSplitterPos(int xyPos = -1, bool bUpdate = true)\r
141         {\r
142                 if(xyPos == -1)   // -1 == middle\r
143                 {\r
144                         if(t_bVertical)\r
145                                 xyPos = (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) / 2;\r
146                         else\r
147                                 xyPos = (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge) / 2;\r
148                 }\r
149 \r
150                 // Adjust if out of valid range\r
151                 int cxyMax = 0;\r
152                 if(t_bVertical)\r
153                         cxyMax = m_rcSplitter.right - m_rcSplitter.left;\r
154                 else\r
155                         cxyMax = m_rcSplitter.bottom - m_rcSplitter.top;\r
156 \r
157                 if(xyPos < m_cxyMin + m_cxyBarEdge)\r
158                         xyPos = m_cxyMin;\r
159                 else if(xyPos > (cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin))\r
160                         xyPos = cxyMax - m_cxySplitBar - m_cxyBarEdge - m_cxyMin;\r
161 \r
162                 // Set new position and update if requested\r
163                 bool bRet = (m_xySplitterPos != xyPos);\r
164                 m_xySplitterPos = xyPos;\r
165 \r
166                 if(m_bUpdateProportionalPos)\r
167                 {\r
168                         if(IsProportional())\r
169                                 StoreProportionalPos();\r
170                         else if(IsRightAligned())\r
171                                 StoreRightAlignPos();\r
172                 }\r
173                 else\r
174                 {\r
175                         m_bUpdateProportionalPos = true;\r
176                 }\r
177 \r
178                 if(bUpdate && bRet)\r
179                         UpdateSplitterLayout();\r
180 \r
181                 return bRet;\r
182         }\r
183 \r
184         void SetSplitterPosPct(int nPct, bool bUpdate = true)\r
185         {\r
186                 ATLASSERT(nPct >= 0 && nPct <= 100);\r
187 \r
188                 m_nProportionalPos = ::MulDiv(nPct, m_nPropMax, 100);\r
189                 UpdateProportionalPos();\r
190 \r
191                 if(bUpdate)\r
192                         UpdateSplitterLayout();\r
193         }\r
194 \r
195         int GetSplitterPos() const\r
196         {\r
197                 return m_xySplitterPos;\r
198         }\r
199 \r
200         bool SetSinglePaneMode(int nPane = SPLIT_PANE_NONE)\r
201         {\r
202                 ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT || nPane == SPLIT_PANE_NONE);\r
203                 if(!(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT || nPane == SPLIT_PANE_NONE))\r
204                         return false;\r
205 \r
206                 if(nPane != SPLIT_PANE_NONE)\r
207                 {\r
208                         if(!::IsWindowVisible(m_hWndPane[nPane]))\r
209                                 ::ShowWindow(m_hWndPane[nPane], SW_SHOW);\r
210                         int nOtherPane = (nPane == SPLIT_PANE_LEFT) ? SPLIT_PANE_RIGHT : SPLIT_PANE_LEFT;\r
211                         ::ShowWindow(m_hWndPane[nOtherPane], SW_HIDE);\r
212                         if(m_nDefActivePane != nPane)\r
213                                 m_nDefActivePane = nPane;\r
214                 }\r
215                 else if(m_nSinglePane != SPLIT_PANE_NONE)\r
216                 {\r
217                         int nOtherPane = (m_nSinglePane == SPLIT_PANE_LEFT) ? SPLIT_PANE_RIGHT : SPLIT_PANE_LEFT;\r
218                         ::ShowWindow(m_hWndPane[nOtherPane], SW_SHOW);\r
219                 }\r
220 \r
221                 m_nSinglePane = nPane;\r
222                 UpdateSplitterLayout();\r
223                 return true;\r
224         }\r
225 \r
226         int GetSinglePaneMode() const\r
227         {\r
228                 return m_nSinglePane;\r
229         }\r
230 \r
231         DWORD GetSplitterExtendedStyle() const\r
232         {\r
233                 return m_dwExtendedStyle;\r
234         }\r
235 \r
236         DWORD SetSplitterExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)\r
237         {\r
238                 DWORD dwPrevStyle = m_dwExtendedStyle;\r
239                 if(dwMask == 0)\r
240                         m_dwExtendedStyle = dwExtendedStyle;\r
241                 else\r
242                         m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);\r
243 #ifdef _DEBUG\r
244                 if(IsProportional() && IsRightAligned())\r
245                         ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::SetSplitterExtendedStyle - SPLIT_PROPORTIONAL and SPLIT_RIGHTALIGNED are mutually exclusive, defaulting to SPLIT_PROPORTIONAL.\n"));\r
246 #endif // _DEBUG\r
247                 return dwPrevStyle;\r
248         }\r
249 \r
250 // Splitter operations\r
251         void SetSplitterPanes(HWND hWndLeftTop, HWND hWndRightBottom, bool bUpdate = true)\r
252         {\r
253                 m_hWndPane[SPLIT_PANE_LEFT] = hWndLeftTop;\r
254                 m_hWndPane[SPLIT_PANE_RIGHT] = hWndRightBottom;\r
255                 ATLASSERT(m_hWndPane[SPLIT_PANE_LEFT] == NULL || m_hWndPane[SPLIT_PANE_RIGHT] == NULL || m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RIGHT]);\r
256                 if(bUpdate)\r
257                         UpdateSplitterLayout();\r
258         }\r
259 \r
260         bool SetSplitterPane(int nPane, HWND hWnd, bool bUpdate = true)\r
261         {\r
262                 ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);\r
263 \r
264                 if(nPane != SPLIT_PANE_LEFT && nPane != SPLIT_PANE_RIGHT)\r
265                         return false;\r
266                 m_hWndPane[nPane] = hWnd;\r
267                 ATLASSERT(m_hWndPane[SPLIT_PANE_LEFT] == NULL || m_hWndPane[SPLIT_PANE_RIGHT] == NULL || m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RIGHT]);\r
268                 if(bUpdate)\r
269                         UpdateSplitterLayout();\r
270                 return true;\r
271         }\r
272 \r
273         HWND GetSplitterPane(int nPane) const\r
274         {\r
275                 ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);\r
276 \r
277                 if(nPane != SPLIT_PANE_LEFT && nPane != SPLIT_PANE_RIGHT)\r
278                         return false;\r
279                 return m_hWndPane[nPane];\r
280         }\r
281 \r
282         bool SetActivePane(int nPane)\r
283         {\r
284                 ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);\r
285 \r
286                 if(nPane != SPLIT_PANE_LEFT && nPane != SPLIT_PANE_RIGHT)\r
287                         return false;\r
288                 if(m_nSinglePane != SPLIT_PANE_NONE && nPane != m_nSinglePane)\r
289                         return false;\r
290                 ::SetFocus(m_hWndPane[nPane]);\r
291                 m_nDefActivePane = nPane;\r
292                 return true;\r
293         }\r
294 \r
295         int GetActivePane() const\r
296         {\r
297                 int nRet = SPLIT_PANE_NONE;\r
298                 HWND hWndFocus = ::GetFocus();\r
299                 if(hWndFocus != NULL)\r
300                 {\r
301                         for(int nPane = 0; nPane < m_nPanesCount; nPane++)\r
302                         {\r
303                                 if(hWndFocus == m_hWndPane[nPane] || ::IsChild(m_hWndPane[nPane], hWndFocus))\r
304                                 {\r
305                                         nRet = nPane;\r
306                                         break;\r
307                                 }\r
308                         }\r
309                 }\r
310                 return nRet;\r
311         }\r
312 \r
313         bool ActivateNextPane(bool bNext = true)\r
314         {\r
315                 int nPane = m_nSinglePane;\r
316                 if(nPane == SPLIT_PANE_NONE)\r
317                 {\r
318                         switch(GetActivePane())\r
319                         {\r
320                         case SPLIT_PANE_LEFT:\r
321                                 nPane = SPLIT_PANE_RIGHT;\r
322                                 break;\r
323                         case SPLIT_PANE_RIGHT:\r
324                                 nPane = SPLIT_PANE_LEFT;\r
325                                 break;\r
326                         default:\r
327                                 nPane = bNext ? SPLIT_PANE_LEFT : SPLIT_PANE_RIGHT;\r
328                                 break;\r
329                         }\r
330                 }\r
331                 return SetActivePane(nPane);\r
332         }\r
333 \r
334         bool SetDefaultActivePane(int nPane)\r
335         {\r
336                 ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);\r
337 \r
338                 if(nPane != SPLIT_PANE_LEFT && nPane != SPLIT_PANE_RIGHT)\r
339                         return false;\r
340                 m_nDefActivePane = nPane;\r
341                 return true;\r
342         }\r
343 \r
344         bool SetDefaultActivePane(HWND hWnd)\r
345         {\r
346                 for(int nPane = 0; nPane < m_nPanesCount; nPane++)\r
347                 {\r
348                         if(hWnd == m_hWndPane[nPane])\r
349                         {\r
350                                 m_nDefActivePane = nPane;\r
351                                 return true;\r
352                         }\r
353                 }\r
354                 return false;   // not found\r
355         }\r
356 \r
357         int GetDefaultActivePane() const\r
358         {\r
359                 return m_nDefActivePane;\r
360         }\r
361 \r
362         void DrawSplitter(CDCHandle dc)\r
363         {\r
364                 ATLASSERT(dc.m_hDC != NULL);\r
365                 if(m_nSinglePane == SPLIT_PANE_NONE && m_xySplitterPos == -1)\r
366                         return;\r
367 \r
368                 T* pT = static_cast<T*>(this);\r
369                 if(m_nSinglePane == SPLIT_PANE_NONE)\r
370                 {\r
371                         pT->DrawSplitterBar(dc);\r
372 \r
373                         for(int nPane = 0; nPane < m_nPanesCount; nPane++)\r
374                         {\r
375                                 if(m_hWndPane[nPane] == NULL)\r
376                                         pT->DrawSplitterPane(dc, nPane);\r
377                         }\r
378                 }\r
379                 else\r
380                 {\r
381                         if(m_hWndPane[m_nSinglePane] == NULL)\r
382                                 pT->DrawSplitterPane(dc, m_nSinglePane);\r
383                 }\r
384         }\r
385 \r
386 // Overrideables\r
387         void DrawSplitterBar(CDCHandle dc)\r
388         {\r
389                 RECT rect;\r
390                 if(GetSplitterBarRect(&rect))\r
391                 {\r
392                         dc.FillRect(&rect, COLOR_3DFACE);\r
393                         // draw 3D edge if needed\r
394                         T* pT = static_cast<T*>(this);\r
395                         if((pT->GetExStyle() & WS_EX_CLIENTEDGE) != 0)\r
396                                 dc.DrawEdge(&rect, EDGE_RAISED, t_bVertical ? (BF_LEFT | BF_RIGHT) : (BF_TOP | BF_BOTTOM));\r
397                 }\r
398         }\r
399 \r
400         // called only if pane is empty\r
401         void DrawSplitterPane(CDCHandle dc, int nPane)\r
402         {\r
403                 RECT rect;\r
404                 if(GetSplitterPaneRect(nPane, &rect))\r
405                 {\r
406                         T* pT = static_cast<T*>(this);\r
407                         if((pT->GetExStyle() & WS_EX_CLIENTEDGE) == 0)\r
408                                 dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);\r
409                         dc.FillRect(&rect, COLOR_APPWORKSPACE);\r
410                 }\r
411         }\r
412 \r
413 // Message map and handlers\r
414         BEGIN_MSG_MAP(CSplitterImpl)\r
415                 MESSAGE_HANDLER(WM_CREATE, OnCreate)\r
416                 MESSAGE_HANDLER(WM_PAINT, OnPaint)\r
417 #ifndef _WIN32_WCE\r
418                 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)\r
419 #endif // !_WIN32_WCE\r
420                 if(IsInteractive())\r
421                 {\r
422                         MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)\r
423                         MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)\r
424                         MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)\r
425                         MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)\r
426                         MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDoubleClick)\r
427                         MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)\r
428                 }\r
429                 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)\r
430 #ifndef _WIN32_WCE\r
431                 MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)\r
432 #endif // !_WIN32_WCE\r
433                 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)\r
434         END_MSG_MAP()\r
435 \r
436         LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)\r
437         {\r
438                 GetSystemSettings(false);\r
439                 bHandled = FALSE;\r
440                 return 1;\r
441         }\r
442 \r
443         LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r
444         {\r
445                 T* pT = static_cast<T*>(this);\r
446                 // try setting position if not set\r
447                 if(m_nSinglePane == SPLIT_PANE_NONE && m_xySplitterPos == -1)\r
448                         pT->SetSplitterPos();\r
449                 // do painting\r
450                 CPaintDC dc(pT->m_hWnd);\r
451                 pT->DrawSplitter(dc.m_hDC);\r
452                 return 0;\r
453         }\r
454 \r
455         LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)\r
456         {\r
457                 T* pT = static_cast<T*>(this);\r
458                 if((HWND)wParam == pT->m_hWnd && LOWORD(lParam) == HTCLIENT)\r
459                 {\r
460                         DWORD dwPos = ::GetMessagePos();\r
461                         POINT ptPos = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };\r
462                         pT->ScreenToClient(&ptPos);\r
463                         if(IsOverSplitterBar(ptPos.x, ptPos.y))\r
464                                 return 1;\r
465                 }\r
466 \r
467                 bHandled = FALSE;\r
468                 return 0;\r
469         }\r
470 \r
471         LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)\r
472         {\r
473                 T* pT = static_cast<T*>(this);\r
474                 int xPos = GET_X_LPARAM(lParam);\r
475                 int yPos = GET_Y_LPARAM(lParam);\r
476                 if((wParam & MK_LBUTTON) && ::GetCapture() == pT->m_hWnd)\r
477                 {\r
478                         int xyNewSplitPos = 0;\r
479                         if(t_bVertical)\r
480                                 xyNewSplitPos = xPos - m_rcSplitter.left - m_cxyDragOffset;\r
481                         else\r
482                                 xyNewSplitPos = yPos - m_rcSplitter.top - m_cxyDragOffset;\r
483 \r
484                         if(xyNewSplitPos == -1)   // avoid -1, that means middle\r
485                                 xyNewSplitPos = -2;\r
486 \r
487                         if(m_xySplitterPos != xyNewSplitPos)\r
488                         {\r
489                                 if(m_bFullDrag)\r
490                                 {\r
491                                         if(pT->SetSplitterPos(xyNewSplitPos, true))\r
492                                                 pT->UpdateWindow();\r
493                                 }\r
494                                 else\r
495                                 {\r
496                                         DrawGhostBar();\r
497                                         pT->SetSplitterPos(xyNewSplitPos, false);\r
498                                         DrawGhostBar();\r
499                                 }\r
500                         }\r
501                 }\r
502                 else            // not dragging, just set cursor\r
503                 {\r
504                         if(IsOverSplitterBar(xPos, yPos))\r
505                                 ::SetCursor(m_hCursor);\r
506                         bHandled = FALSE;\r
507                 }\r
508 \r
509                 return 0;\r
510         }\r
511 \r
512         LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)\r
513         {\r
514                 int xPos = GET_X_LPARAM(lParam);\r
515                 int yPos = GET_Y_LPARAM(lParam);\r
516                 if(IsOverSplitterBar(xPos, yPos))\r
517                 {\r
518                         T* pT = static_cast<T*>(this);\r
519                         pT->SetCapture();\r
520                         ::SetCursor(m_hCursor);\r
521                         if(!m_bFullDrag)\r
522                                 DrawGhostBar();\r
523                         if(t_bVertical)\r
524                                 m_cxyDragOffset = xPos - m_rcSplitter.left - m_xySplitterPos;\r
525                         else\r
526                                 m_cxyDragOffset = yPos - m_rcSplitter.top - m_xySplitterPos;\r
527                 }\r
528                 bHandled = FALSE;\r
529                 return 1;\r
530         }\r
531 \r
532         LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)\r
533         {\r
534                 ::ReleaseCapture();\r
535                 bHandled = FALSE;\r
536                 return 1;\r
537         }\r
538 \r
539         LRESULT OnLButtonDoubleClick(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r
540         {\r
541                 T* pT = static_cast<T*>(this);\r
542                 pT->SetSplitterPos();   // middle\r
543                 return 0;\r
544         }\r
545 \r
546         LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r
547         {\r
548                 if(!m_bFullDrag)\r
549                 {\r
550                         DrawGhostBar();\r
551                         UpdateSplitterLayout();\r
552                         T* pT = static_cast<T*>(this);\r
553                         pT->UpdateWindow();\r
554                 }\r
555                 return 0;\r
556         }\r
557 \r
558         LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM, BOOL& bHandled)\r
559         {\r
560                 if(m_nSinglePane == SPLIT_PANE_NONE)\r
561                 {\r
562                         if(m_nDefActivePane == SPLIT_PANE_LEFT || m_nDefActivePane == SPLIT_PANE_RIGHT)\r
563                                 ::SetFocus(m_hWndPane[m_nDefActivePane]);\r
564                 }\r
565                 else\r
566                 {\r
567                         ::SetFocus(m_hWndPane[m_nSinglePane]);\r
568                 }\r
569                 bHandled = FALSE;\r
570                 return 1;\r
571         }\r
572 \r
573 #ifndef _WIN32_WCE\r
574         LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)\r
575         {\r
576                 T* pT = static_cast<T*>(this);\r
577                 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);\r
578                 if(lRet == MA_ACTIVATE || lRet == MA_ACTIVATEANDEAT)\r
579                 {\r
580                         DWORD dwPos = ::GetMessagePos();\r
581                         POINT pt = { GET_X_LPARAM(dwPos), GET_Y_LPARAM(dwPos) };\r
582                         pT->ScreenToClient(&pt);\r
583                         RECT rcPane;\r
584                         for(int nPane = 0; nPane < m_nPanesCount; nPane++)\r
585                         {\r
586                                 if(GetSplitterPaneRect(nPane, &rcPane) && ::PtInRect(&rcPane, pt))\r
587                                 {\r
588                                         m_nDefActivePane = nPane;\r
589                                         break;\r
590                                 }\r
591                         }\r
592                 }\r
593                 return lRet;\r
594         }\r
595 #endif // !_WIN32_WCE\r
596 \r
597         LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r
598         {\r
599                 GetSystemSettings(true);\r
600                 return 0;\r
601         }\r
602 \r
603 // Implementation - internal helpers\r
604         void UpdateSplitterLayout()\r
605         {\r
606                 if(m_nSinglePane == SPLIT_PANE_NONE && m_xySplitterPos == -1)\r
607                         return;\r
608 \r
609                 T* pT = static_cast<T*>(this);\r
610                 RECT rect = { 0, 0, 0, 0 };\r
611                 if(m_nSinglePane == SPLIT_PANE_NONE)\r
612                 {\r
613                         if(GetSplitterBarRect(&rect))\r
614                                 pT->InvalidateRect(&rect);\r
615 \r
616                         for(int nPane = 0; nPane < m_nPanesCount; nPane++)\r
617                         {\r
618                                 if(GetSplitterPaneRect(nPane, &rect))\r
619                                 {\r
620                                         if(m_hWndPane[nPane] != NULL)\r
621                                                 ::SetWindowPos(m_hWndPane[nPane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
622                                         else\r
623                                                 pT->InvalidateRect(&rect);\r
624                                 }\r
625                         }\r
626                 }\r
627                 else\r
628                 {\r
629                         if(GetSplitterPaneRect(m_nSinglePane, &rect))\r
630                         {\r
631                                 if(m_hWndPane[m_nSinglePane] != NULL)\r
632                                         ::SetWindowPos(m_hWndPane[m_nSinglePane], NULL, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER);\r
633                                 else\r
634                                         pT->InvalidateRect(&rect);\r
635                         }\r
636                 }\r
637         }\r
638 \r
639         bool GetSplitterBarRect(LPRECT lpRect) const\r
640         {\r
641                 ATLASSERT(lpRect != NULL);\r
642                 if(m_nSinglePane != SPLIT_PANE_NONE || m_xySplitterPos == -1)\r
643                         return false;\r
644 \r
645                 if(t_bVertical)\r
646                 {\r
647                         lpRect->left = m_rcSplitter.left + m_xySplitterPos;\r
648                         lpRect->top = m_rcSplitter.top;\r
649                         lpRect->right = m_rcSplitter.left + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;\r
650                         lpRect->bottom = m_rcSplitter.bottom;\r
651                 }\r
652                 else\r
653                 {\r
654                         lpRect->left = m_rcSplitter.left;\r
655                         lpRect->top = m_rcSplitter.top + m_xySplitterPos;\r
656                         lpRect->right = m_rcSplitter.right;\r
657                         lpRect->bottom = m_rcSplitter.top + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;\r
658                 }\r
659 \r
660                 return true;\r
661         }\r
662 \r
663         bool GetSplitterPaneRect(int nPane, LPRECT lpRect) const\r
664         {\r
665                 ATLASSERT(nPane == SPLIT_PANE_LEFT || nPane == SPLIT_PANE_RIGHT);\r
666                 ATLASSERT(lpRect != NULL);\r
667                 bool bRet = true;\r
668                 if(m_nSinglePane != SPLIT_PANE_NONE)\r
669                 {\r
670                         if(nPane == m_nSinglePane)\r
671                                 *lpRect = m_rcSplitter;\r
672                         else\r
673                                 bRet = false;\r
674                 }\r
675                 else if(nPane == SPLIT_PANE_LEFT)\r
676                 {\r
677                         if(t_bVertical)\r
678                         {\r
679                                 lpRect->left = m_rcSplitter.left;\r
680                                 lpRect->top = m_rcSplitter.top;\r
681                                 lpRect->right = m_rcSplitter.left + m_xySplitterPos;\r
682                                 lpRect->bottom = m_rcSplitter.bottom;\r
683                         }\r
684                         else\r
685                         {\r
686                                 lpRect->left = m_rcSplitter.left;\r
687                                 lpRect->top = m_rcSplitter.top;\r
688                                 lpRect->right = m_rcSplitter.right;\r
689                                 lpRect->bottom = m_rcSplitter.top + m_xySplitterPos;\r
690                         }\r
691                 }\r
692                 else if(nPane == SPLIT_PANE_RIGHT)\r
693                 {\r
694                         if(t_bVertical)\r
695                         {\r
696                                 lpRect->left = m_rcSplitter.left + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;\r
697                                 lpRect->top = m_rcSplitter.top;\r
698                                 lpRect->right = m_rcSplitter.right;\r
699                                 lpRect->bottom = m_rcSplitter.bottom;\r
700                         }\r
701                         else\r
702                         {\r
703                                 lpRect->left = m_rcSplitter.left;\r
704                                 lpRect->top = m_rcSplitter.top + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge;\r
705                                 lpRect->right = m_rcSplitter.right;\r
706                                 lpRect->bottom = m_rcSplitter.bottom;\r
707                         }\r
708                 }\r
709                 else\r
710                 {\r
711                         bRet = false;\r
712                 }\r
713                 return bRet;\r
714         }\r
715 \r
716         bool IsOverSplitterRect(int x, int y) const\r
717         {\r
718                 // -1 == don't check\r
719                 return ((x == -1 || (x >= m_rcSplitter.left && x <= m_rcSplitter.right)) &&\r
720                         (y == -1 || (y >= m_rcSplitter.top && y <= m_rcSplitter.bottom)));\r
721         }\r
722 \r
723         bool IsOverSplitterBar(int x, int y) const\r
724         {\r
725                 if(m_nSinglePane != SPLIT_PANE_NONE)\r
726                         return false;\r
727                 if(m_xySplitterPos == -1 || !IsOverSplitterRect(x, y))\r
728                         return false;\r
729                 int xy = t_bVertical ? x : y;\r
730                 int xyOff = t_bVertical ? m_rcSplitter.left : m_rcSplitter.top;\r
731                 return ((xy >= (xyOff + m_xySplitterPos)) && (xy < xyOff + m_xySplitterPos + m_cxySplitBar + m_cxyBarEdge));\r
732         }\r
733 \r
734         void DrawGhostBar()\r
735         {\r
736                 RECT rect = { 0, 0, 0, 0 };\r
737                 if(GetSplitterBarRect(&rect))\r
738                 {\r
739                         // invert the brush pattern (looks just like frame window sizing)\r
740                         T* pT = static_cast<T*>(this);\r
741                         CWindowDC dc(pT->m_hWnd);\r
742                         CBrush brush = CDCHandle::GetHalftoneBrush();\r
743                         if(brush.m_hBrush != NULL)\r
744                         {\r
745                                 CBrushHandle brushOld = dc.SelectBrush(brush);\r
746                                 dc.PatBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, PATINVERT);\r
747                                 dc.SelectBrush(brushOld);\r
748                         }\r
749                 }\r
750         }\r
751 \r
752         void GetSystemSettings(bool bUpdate)\r
753         {\r
754 #ifndef _WIN32_WCE\r
755                 m_cxySplitBar = ::GetSystemMetrics(t_bVertical ? SM_CXSIZEFRAME : SM_CYSIZEFRAME);\r
756 #else // CE specific\r
757                 m_cxySplitBar = 2 * ::GetSystemMetrics(t_bVertical ? SM_CXEDGE : SM_CYEDGE);\r
758 #endif // _WIN32_WCE\r
759 \r
760                 T* pT = static_cast<T*>(this);\r
761                 if((pT->GetExStyle() & WS_EX_CLIENTEDGE))\r
762                 {\r
763                         m_cxyBarEdge = 2 * ::GetSystemMetrics(t_bVertical ? SM_CXEDGE : SM_CYEDGE);\r
764                         m_cxyMin = 0;\r
765                 }\r
766                 else\r
767                 {\r
768                         m_cxyBarEdge = 0;\r
769                         m_cxyMin = 2 * ::GetSystemMetrics(t_bVertical ? SM_CXEDGE : SM_CYEDGE);\r
770                 }\r
771 \r
772 #ifndef _WIN32_WCE\r
773                 ::SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &m_bFullDrag, 0);\r
774 #endif // !_WIN32_WCE\r
775 \r
776                 if(bUpdate)\r
777                         UpdateSplitterLayout();\r
778         }\r
779 \r
780         bool IsProportional() const\r
781         {\r
782                 return ((m_dwExtendedStyle & SPLIT_PROPORTIONAL) != 0);\r
783         }\r
784 \r
785         void StoreProportionalPos()\r
786         {\r
787                 int cxyTotal = t_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);\r
788                 if(cxyTotal > 0)\r
789                         m_nProportionalPos = ::MulDiv(m_xySplitterPos, m_nPropMax, cxyTotal);\r
790                 else\r
791                         m_nProportionalPos = 0;\r
792                 ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::StoreProportionalPos - %i\n"), m_nProportionalPos);\r
793         }\r
794 \r
795         void UpdateProportionalPos()\r
796         {\r
797                 int cxyTotal = t_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);\r
798                 if(cxyTotal > 0)\r
799                 {\r
800                         int xyNewPos = ::MulDiv(m_nProportionalPos, cxyTotal, m_nPropMax);\r
801                         m_bUpdateProportionalPos = false;\r
802                         T* pT = static_cast<T*>(this);\r
803                         pT->SetSplitterPos(xyNewPos, false);\r
804                 }\r
805         }\r
806 \r
807         bool IsRightAligned() const\r
808         {\r
809                 return ((m_dwExtendedStyle & SPLIT_RIGHTALIGNED) != 0);\r
810         }\r
811 \r
812         void StoreRightAlignPos()\r
813         {\r
814                 int cxyTotal = t_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);\r
815                 if(cxyTotal > 0)\r
816                         m_nProportionalPos = cxyTotal - m_xySplitterPos;\r
817                 else\r
818                         m_nProportionalPos = 0;\r
819                 ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::StoreRightAlignPos - %i\n"), m_nProportionalPos);\r
820         }\r
821 \r
822         void UpdateRightAlignPos()\r
823         {\r
824                 int cxyTotal = t_bVertical ? (m_rcSplitter.right - m_rcSplitter.left - m_cxySplitBar - m_cxyBarEdge) : (m_rcSplitter.bottom - m_rcSplitter.top - m_cxySplitBar - m_cxyBarEdge);\r
825                 if(cxyTotal > 0)\r
826                 {\r
827                         m_bUpdateProportionalPos = false;\r
828                         T* pT = static_cast<T*>(this);\r
829                         pT->SetSplitterPos(cxyTotal - m_nProportionalPos, false);\r
830                 }\r
831         }\r
832 \r
833         bool IsInteractive() const\r
834         {\r
835                 return ((m_dwExtendedStyle & SPLIT_NONINTERACTIVE) == 0);\r
836         }\r
837 };\r
838 \r
839 template <class T, bool t_bVertical> HCURSOR CSplitterImpl< T, t_bVertical>::m_hCursor = NULL;\r
840 \r
841 \r
842 ///////////////////////////////////////////////////////////////////////////////\r
843 // CSplitterWindowImpl - Implements a splitter window\r
844 \r
845 template <class T, bool t_bVertical = true, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>\r
846 class ATL_NO_VTABLE CSplitterWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CSplitterImpl< T , t_bVertical >\r
847 {\r
848 public:\r
849         DECLARE_WND_CLASS_EX(NULL, CS_DBLCLKS, COLOR_WINDOW)\r
850 \r
851         typedef CSplitterImpl< T , t_bVertical >   _baseClass;\r
852 \r
853         BEGIN_MSG_MAP(CSplitterWindowImpl)\r
854                 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)\r
855                 MESSAGE_HANDLER(WM_SIZE, OnSize)\r
856                 CHAIN_MSG_MAP(_baseClass)\r
857                 FORWARD_NOTIFICATIONS()\r
858         END_MSG_MAP()\r
859 \r
860         LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)\r
861         {\r
862                 // handled, no background painting needed\r
863                 return 1;\r
864         }\r
865 \r
866         LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)\r
867         {\r
868                 if(wParam != SIZE_MINIMIZED)\r
869                         SetSplitterRect();\r
870 \r
871                 bHandled = FALSE;\r
872                 return 1;\r
873         }\r
874 };\r
875 \r
876 \r
877 ///////////////////////////////////////////////////////////////////////////////\r
878 // CSplitterWindow - Implements a splitter window to be used as is\r
879 \r
880 template <bool t_bVertical = true>\r
881 class CSplitterWindowT : public CSplitterWindowImpl<CSplitterWindowT<t_bVertical>, t_bVertical>\r
882 {\r
883 public:\r
884         DECLARE_WND_CLASS_EX(_T("WTL_SplitterWindow"), CS_DBLCLKS, COLOR_WINDOW)\r
885 };\r
886 \r
887 typedef CSplitterWindowT<true>    CSplitterWindow;\r
888 typedef CSplitterWindowT<false>   CHorSplitterWindow;\r
889 \r
890 }; // namespace WTL\r
891 \r
892 #endif // __ATLSPLIT_H__\r