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