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