|
1
|
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__
|