| OLD | NEW |
| (Empty) |
| 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_bFullD
rag(true), | |
| 83 m_cxyDragOffset(0), m_nProportionalPos(0), m_bUpdateProp
ortionalPos(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 l
ock 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_cxyMi
n)) | |
| 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 || nP
ane == 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) ? SP
LIT_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) | (dwE
xtendedStyle & dwMask); | |
| 240 #ifdef _DEBUG | |
| 241 if(IsProportional() && IsRightAligned()) | |
| 242 ATLTRACE2(atlTraceUI, 0, _T("CSplitterImpl::SetSplitterE
xtendedStyle - 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 bUpda
te = 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[SPLI
T_PANE_RIGHT] == NULL || m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RI
GHT]); | |
| 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[SPLI
T_PANE_RIGHT] == NULL || m_hWndPane[SPLIT_PANE_LEFT] != m_hWndPane[SPLIT_PANE_RI
GHT]); | |
| 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_RIG
HT; | |
| 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 ? (B
F_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_ADJ
UST); | |
| 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*/, BO
OL& bHandled) | |
| 434 { | |
| 435 GetSystemSettings(false); | |
| 436 bHandled = FALSE; | |
| 437 return 1; | |
| 438 } | |
| 439 | |
| 440 LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOO
L& /*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& b
Handled) | |
| 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& b
Handled) | |
| 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_cxy
DragOffset; | |
| 478 else | |
| 479 xyNewSplitPos = yPos - m_rcSplitter.top - m_cxyD
ragOffset; | |
| 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, tru
e)) | |
| 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, B
OOL& 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_x
ySplitterPos; | |
| 522 else | |
| 523 m_cxyDragOffset = yPos - m_rcSplitter.top - m_xy
SplitterPos; | |
| 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 /*lPar
am*/, 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& bHand
led) | |
| 556 { | |
| 557 if(m_nSinglePane == SPLIT_PANE_NONE) | |
| 558 { | |
| 559 if(m_nDefActivePane == SPLIT_PANE_LEFT || m_nDefActivePa
ne == 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) && ::PtIn
Rect(&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 /*lPara
m*/, 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_xySplitter
Pos; | |
| 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_xySplitter
Pos; | |
| 687 } | |
| 688 } | |
| 689 else if(nPane == SPLIT_PANE_RIGHT) | |
| 690 { | |
| 691 if(t_bVertical) | |
| 692 { | |
| 693 lpRect->left = m_rcSplitter.left + m_xySplitterP
os + 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_xyS
plitterPos + 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 windo
w 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_C
XEDGE : SM_CYEDGE); | |
| 761 m_cxyMin = 0; | |
| 762 } | |
| 763 else | |
| 764 { | |
| 765 m_cxyBarEdge = 0; | |
| 766 m_cxyMin = 2 * ::GetSystemMetrics(t_bVertical ? SM_CXEDG
E : 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_nPropMa
x, 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_h
Cursor = NULL; | |
| 837 | |
| 838 | |
| 839 /////////////////////////////////////////////////////////////////////////////// | |
| 840 // CSplitterWindowImpl - Implements a splitter window | |
| 841 | |
| 842 template <class T, bool t_bVertical = true, class TBase = ATL::CWindow, class TW
inTraits = ATL::CControlWinTraits> | |
| 843 class ATL_NO_VTABLE CSplitterWindowImpl : public ATL::CWindowImpl< T, TBase, TWi
nTraits >, 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 /*lPa
ram*/, BOOL& /*bHandled*/) | |
| 858 { | |
| 859 // handled, no background painting needed | |
| 860 return 1; | |
| 861 } | |
| 862 | |
| 863 LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bH
andled) | |
| 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__ | |
| OLD | NEW |