Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

839 lines
22 KiB

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