|
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 __ATLCTRLX_H__
|
|
|
10 #define __ATLCTRLX_H__
|
|
|
11
|
|
|
12 #pragma once
|
|
|
13
|
|
|
14 #ifndef __ATLAPP_H__
|
|
|
15 #error atlctrlx.h requires atlapp.h to be included first
|
|
|
16 #endif
|
|
|
17
|
|
|
18 #ifndef __ATLCTRLS_H__
|
|
|
19 #error atlctrlx.h requires atlctrls.h to be included first
|
|
|
20 #endif
|
|
|
21
|
|
|
22
|
|
|
23 ///////////////////////////////////////////////////////////////////////////////
|
|
|
24 // Classes in this file:
|
|
|
25 //
|
|
|
26 // CBitmapButtonImpl<T, TBase, TWinTraits>
|
|
|
27 // CBitmapButton
|
|
|
28 // CCheckListViewCtrlImpl<T, TBase, TWinTraits>
|
|
|
29 // CCheckListViewCtrl
|
|
|
30 // CHyperLinkImpl<T, TBase, TWinTraits>
|
|
|
31 // CHyperLink
|
|
|
32 // CWaitCursor
|
|
|
33 // CCustomWaitCursor
|
|
|
34 // CMultiPaneStatusBarCtrlImpl<T, TBase>
|
|
|
35 // CMultiPaneStatusBarCtrl
|
|
|
36 // CPaneContainerImpl<T, TBase, TWinTraits>
|
|
|
37 // CPaneContainer
|
|
|
38 // CSortListViewImpl<T>
|
|
|
39 // CSortListViewCtrlImpl<T, TBase, TWinTraits>
|
|
|
40 // CSortListViewCtrl
|
|
|
41 // CTabViewImpl<T, TBase, TWinTraits>
|
|
|
42 // CTabView
|
|
|
43
|
|
|
44 namespace WTL
|
|
|
45 {
|
|
|
46
|
|
|
47 ///////////////////////////////////////////////////////////////////////////////
|
|
|
48 // CBitmapButton - bitmap button implementation
|
|
|
49
|
|
|
50 // bitmap button extended styles
|
|
|
51 #define BMPBTN_HOVER 0x00000001
|
|
|
52 #define BMPBTN_AUTO3D_SINGLE 0x00000002
|
|
|
53 #define BMPBTN_AUTO3D_DOUBLE 0x00000004
|
|
|
54 #define BMPBTN_AUTOSIZE 0x00000008
|
|
|
55 #define BMPBTN_SHAREIMAGELISTS 0x00000010
|
|
|
56 #define BMPBTN_AUTOFIRE 0x00000020
|
|
|
57 #define BMPBTN_CHECK 0x00000040
|
|
|
58 #define BMPBTN_AUTOCHECK 0x00000080
|
|
|
59
|
|
|
60 // Note: BMPBTN_CHECK/BMPBTN_AUTOCHECK disables BN_DOUBLECLICKED,
|
|
|
61 // BMPBTN_AUTOFIRE doesn't work with BMPBTN_CHECK/BMPBTN_AUTOCHECK
|
|
|
62
|
|
|
63 template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
|
|
|
64 class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
|
|
|
65 {
|
|
|
66 public:
|
|
|
67 DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName())
|
|
|
68
|
|
|
69 enum
|
|
|
70 {
|
|
|
71 _nImageNormal = 0,
|
|
|
72 _nImagePushed,
|
|
|
73 _nImageFocusOrHover,
|
|
|
74 _nImageDisabled,
|
|
|
75
|
|
|
76 _nImageCount = 4,
|
|
|
77 };
|
|
|
78
|
|
|
79 enum
|
|
|
80 {
|
|
|
81 ID_TIMER_FIRST = 1000,
|
|
|
82 ID_TIMER_REPEAT = 1001
|
|
|
83 };
|
|
|
84
|
|
|
85 // Bitmap button specific extended styles
|
|
|
86 DWORD m_dwExtendedStyle;
|
|
|
87
|
|
|
88 CImageList m_ImageList;
|
|
|
89 int m_nImage[_nImageCount];
|
|
|
90
|
|
|
91 CToolTipCtrl m_tip;
|
|
|
92 LPTSTR m_lpstrToolTipText;
|
|
|
93
|
|
|
94 // Internal states
|
|
|
95 unsigned m_fMouseOver:1;
|
|
|
96 unsigned m_fFocus:1;
|
|
|
97 unsigned m_fPressed:1;
|
|
|
98 unsigned m_fChecked:1;
|
|
|
99
|
|
|
100
|
|
|
101 // Constructor/Destructor
|
|
|
102 CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
|
|
|
103 m_dwExtendedStyle(dwExtendedStyle), m_ImageList(hImageList),
|
|
|
104 m_lpstrToolTipText(NULL),
|
|
|
105 m_fMouseOver(0), m_fFocus(0), m_fPressed(0), m_fChecked(0)
|
|
|
106 {
|
|
|
107 m_nImage[_nImageNormal] = -1;
|
|
|
108 m_nImage[_nImagePushed] = -1;
|
|
|
109 m_nImage[_nImageFocusOrHover] = -1;
|
|
|
110 m_nImage[_nImageDisabled] = -1;
|
|
|
111
|
|
|
112 #ifdef _DEBUG
|
|
|
113 if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && IsCheckMode())
|
|
|
114 ATLTRACE2(atlTraceUI, 0, _T("CBitmapButtonImpl - Check mode and BMPBTN_AUTOFIRE cannot be used together, BMPBTN_AUTOFIRE will be ignored.\n"));
|
|
|
115 #endif // _DEBUG
|
|
|
116 }
|
|
|
117
|
|
|
118 ~CBitmapButtonImpl()
|
|
|
119 {
|
|
|
120 if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0)
|
|
|
121 m_ImageList.Destroy();
|
|
|
122 delete [] m_lpstrToolTipText;
|
|
|
123 }
|
|
|
124
|
|
|
125 // overridden to provide proper initialization
|
|
|
126 BOOL SubclassWindow(HWND hWnd)
|
|
|
127 {
|
|
|
128 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
|
|
|
129 if(bRet != FALSE)
|
|
|
130 {
|
|
|
131 T* pT = static_cast<T*>(this);
|
|
|
132 pT->Init();
|
|
|
133 }
|
|
|
134
|
|
|
135 return bRet;
|
|
|
136 }
|
|
|
137
|
|
|
138 // Attributes
|
|
|
139 DWORD GetBitmapButtonExtendedStyle() const
|
|
|
140 {
|
|
|
141 return m_dwExtendedStyle;
|
|
|
142 }
|
|
|
143
|
|
|
144 DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
|
|
|
145 {
|
|
|
146 DWORD dwPrevStyle = m_dwExtendedStyle;
|
|
|
147 if(dwMask == 0)
|
|
|
148 m_dwExtendedStyle = dwExtendedStyle;
|
|
|
149 else
|
|
|
150 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
|
|
|
151
|
|
|
152 #ifdef _DEBUG
|
|
|
153 if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && IsCheckMode())
|
|
|
154 ATLTRACE2(atlTraceUI, 0, _T("CBitmapButtonImpl - Check mode and BMPBTN_AUTOFIRE cannot be used together, BMPBTN_AUTOFIRE will be ignored.\n"));
|
|
|
155 #endif // _DEBUG
|
|
|
156
|
|
|
157 return dwPrevStyle;
|
|
|
158 }
|
|
|
159
|
|
|
160 HIMAGELIST GetImageList() const
|
|
|
161 {
|
|
|
162 return m_ImageList;
|
|
|
163 }
|
|
|
164
|
|
|
165 HIMAGELIST SetImageList(HIMAGELIST hImageList)
|
|
|
166 {
|
|
|
167 HIMAGELIST hImageListPrev = m_ImageList;
|
|
|
168 m_ImageList = hImageList;
|
|
|
169 if(((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0) && ::IsWindow(this->m_hWnd))
|
|
|
170 SizeToImage();
|
|
|
171
|
|
|
172 return hImageListPrev;
|
|
|
173 }
|
|
|
174
|
|
|
175 int GetToolTipTextLength() const
|
|
|
176 {
|
|
|
177 return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText);
|
|
|
178 }
|
|
|
179
|
|
|
180 bool GetToolTipText(LPTSTR lpstrText, int nLength) const
|
|
|
181 {
|
|
|
182 ATLASSERT(lpstrText != NULL);
|
|
|
183 if(m_lpstrToolTipText == NULL)
|
|
|
184 return false;
|
|
|
185
|
|
|
186 errno_t nRet = ATL::Checked::tcsncpy_s(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE);
|
|
|
187
|
|
|
188 return ((nRet == 0) || (nRet == STRUNCATE));
|
|
|
189 }
|
|
|
190
|
|
|
191 bool SetToolTipText(LPCTSTR lpstrText)
|
|
|
192 {
|
|
|
193 if(m_lpstrToolTipText != NULL)
|
|
|
194 {
|
|
|
195 delete [] m_lpstrToolTipText;
|
|
|
196 m_lpstrToolTipText = NULL;
|
|
|
197 }
|
|
|
198
|
|
|
199 if(lpstrText == NULL)
|
|
|
200 {
|
|
|
201 if(m_tip.IsWindow())
|
|
|
202 m_tip.Activate(FALSE);
|
|
|
203 return true;
|
|
|
204 }
|
|
|
205
|
|
|
206 int cchLen = lstrlen(lpstrText) + 1;
|
|
|
207 ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]);
|
|
|
208 if(m_lpstrToolTipText == NULL)
|
|
|
209 return false;
|
|
|
210
|
|
|
211 ATL::Checked::tcscpy_s(m_lpstrToolTipText, cchLen, lpstrText);
|
|
|
212 if(m_tip.IsWindow())
|
|
|
213 {
|
|
|
214 m_tip.Activate(TRUE);
|
|
|
215 m_tip.AddTool(this->m_hWnd, m_lpstrToolTipText);
|
|
|
216 }
|
|
|
217
|
|
|
218 return true;
|
|
|
219 }
|
|
|
220
|
|
|
221 bool GetCheck() const
|
|
|
222 {
|
|
|
223 return (m_fChecked == 1);
|
|
|
224 }
|
|
|
225
|
|
|
226 void SetCheck(bool bCheck, bool bUpdate = true)
|
|
|
227 {
|
|
|
228 m_fChecked = bCheck ? 1 : 0;
|
|
|
229
|
|
|
230 if(bUpdate)
|
|
|
231 {
|
|
|
232 this->Invalidate();
|
|
|
233 this->UpdateWindow();
|
|
|
234 }
|
|
|
235 }
|
|
|
236
|
|
|
237 // Operations
|
|
|
238 void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1)
|
|
|
239 {
|
|
|
240 if(nNormal != -1)
|
|
|
241 m_nImage[_nImageNormal] = nNormal;
|
|
|
242 if(nPushed != -1)
|
|
|
243 m_nImage[_nImagePushed] = nPushed;
|
|
|
244 if(nFocusOrHover != -1)
|
|
|
245 m_nImage[_nImageFocusOrHover] = nFocusOrHover;
|
|
|
246 if(nDisabled != -1)
|
|
|
247 m_nImage[_nImageDisabled] = nDisabled;
|
|
|
248 }
|
|
|
249
|
|
|
250 BOOL SizeToImage()
|
|
|
251 {
|
|
|
252 ATLASSERT(::IsWindow(this->m_hWnd) && (m_ImageList.m_hImageList != NULL));
|
|
|
253 int cx = 0;
|
|
|
254 int cy = 0;
|
|
|
255 if(!m_ImageList.GetIconSize(cx, cy))
|
|
|
256 return FALSE;
|
|
|
257 return this->ResizeClient(cx, cy);
|
|
|
258 }
|
|
|
259
|
|
|
260 // Overrideables
|
|
|
261 void DoPaint(CDCHandle dc)
|
|
|
262 {
|
|
|
263 ATLASSERT(m_ImageList.m_hImageList != NULL); // image list must be set
|
|
|
264 ATLASSERT(m_nImage[0] != -1); // main bitmap must be set
|
|
|
265
|
|
|
266 // set bitmap according to the current button state
|
|
|
267 bool bHover = IsHoverMode();
|
|
|
268 bool bPressed = (m_fPressed == 1) || (IsCheckMode() && (m_fChecked == 1));
|
|
|
269 int nImage = -1;
|
|
|
270 if(!this->IsWindowEnabled())
|
|
|
271 nImage = m_nImage[_nImageDisabled];
|
|
|
272 else if(bPressed)
|
|
|
273 nImage = m_nImage[_nImagePushed];
|
|
|
274 else if((!bHover && (m_fFocus == 1)) || (bHover && (m_fMouseOver == 1)))
|
|
|
275 nImage = m_nImage[_nImageFocusOrHover];
|
|
|
276
|
|
|
277 // if none is set, use default one
|
|
|
278 if(nImage == -1)
|
|
|
279 nImage = m_nImage[_nImageNormal];
|
|
|
280
|
|
|
281 // draw the button image
|
|
|
282 bool bAuto3D = (m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0;
|
|
|
283 int xyPos = (bPressed && bAuto3D && (m_nImage[_nImagePushed] == -1)) ? 1 : 0;
|
|
|
284 m_ImageList.Draw(dc, nImage, xyPos, xyPos, ILD_NORMAL);
|
|
|
285
|
|
|
286 // draw 3D border if required
|
|
|
287 if(bAuto3D)
|
|
|
288 {
|
|
|
289 RECT rect = {};
|
|
|
290 this->GetClientRect(&rect);
|
|
|
291
|
|
|
292 if(bPressed)
|
|
|
293 dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT);
|
|
|
294 else if(!bHover || (m_fMouseOver == 1))
|
|
|
295 dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT);
|
|
|
296
|
|
|
297 if(!bHover && (m_fFocus == 1))
|
|
|
298 {
|
|
|
299 ::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE));
|
|
|
300 dc.DrawFocusRect(&rect);
|
|
|
301 }
|
|
|
302 }
|
|
|
303 }
|
|
|
304
|
|
|
305 // Message map and handlers
|
|
|
306 BEGIN_MSG_MAP(CBitmapButtonImpl)
|
|
|
307 MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
|
|
308 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
|
|
|
309 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
|
|
|
310 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
|
|
|
311 MESSAGE_HANDLER(WM_PAINT, OnPaint)
|
|
|
312 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
|
|
|
313 MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
|
|
|
314 MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
|
|
|
315 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
|
|
|
316 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
|
|
|
317 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
|
|
|
318 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
|
|
|
319 MESSAGE_HANDLER(WM_ENABLE, OnEnable)
|
|
|
320 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
|
|
|
321 MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
|
|
|
322 MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
|
|
|
323 MESSAGE_HANDLER(WM_KEYUP, OnKeyUp)
|
|
|
324 MESSAGE_HANDLER(WM_TIMER, OnTimer)
|
|
|
325 MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
|
|
|
326 END_MSG_MAP()
|
|
|
327
|
|
|
328 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
329 {
|
|
|
330 T* pT = static_cast<T*>(this);
|
|
|
331 pT->Init();
|
|
|
332
|
|
|
333 bHandled = FALSE;
|
|
|
334 return 1;
|
|
|
335 }
|
|
|
336
|
|
|
337 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
338 {
|
|
|
339 if(m_tip.IsWindow())
|
|
|
340 {
|
|
|
341 m_tip.DestroyWindow();
|
|
|
342 m_tip.m_hWnd = NULL;
|
|
|
343 }
|
|
|
344 bHandled = FALSE;
|
|
|
345 return 1;
|
|
|
346 }
|
|
|
347
|
|
|
348 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
349 {
|
|
|
350 MSG msg = { this->m_hWnd, uMsg, wParam, lParam };
|
|
|
351 if(m_tip.IsWindow())
|
|
|
352 m_tip.RelayEvent(&msg);
|
|
|
353 bHandled = FALSE;
|
|
|
354 return 1;
|
|
|
355 }
|
|
|
356
|
|
|
357 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
358 {
|
|
|
359 return 1; // no background needed
|
|
|
360 }
|
|
|
361
|
|
|
362 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
363 {
|
|
|
364 T* pT = static_cast<T*>(this);
|
|
|
365 if(wParam != NULL)
|
|
|
366 {
|
|
|
367 pT->DoPaint((HDC)wParam);
|
|
|
368 }
|
|
|
369 else
|
|
|
370 {
|
|
|
371 CPaintDC dc(this->m_hWnd);
|
|
|
372 pT->DoPaint(dc.m_hDC);
|
|
|
373 }
|
|
|
374 return 0;
|
|
|
375 }
|
|
|
376
|
|
|
377 LRESULT OnFocus(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
378 {
|
|
|
379 m_fFocus = (uMsg == WM_SETFOCUS) ? 1 : 0;
|
|
|
380 this->Invalidate();
|
|
|
381 this->UpdateWindow();
|
|
|
382 bHandled = FALSE;
|
|
|
383 return 1;
|
|
|
384 }
|
|
|
385
|
|
|
386 LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
387 {
|
|
|
388 LRESULT lRet = 0;
|
|
|
389 if(IsHoverMode())
|
|
|
390 this->SetCapture();
|
|
|
391 else
|
|
|
392 lRet = this->DefWindowProc(uMsg, wParam, lParam);
|
|
|
393 if(::GetCapture() == this->m_hWnd)
|
|
|
394 {
|
|
|
395 m_fPressed = 1;
|
|
|
396 this->Invalidate();
|
|
|
397 this->UpdateWindow();
|
|
|
398 }
|
|
|
399 if(((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0) && !IsCheckMode())
|
|
|
400 {
|
|
|
401 int nElapse = 250;
|
|
|
402 int nDelay = 0;
|
|
|
403 if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0))
|
|
|
404 nElapse += nDelay * 250; // all milli-seconds
|
|
|
405 this->SetTimer(ID_TIMER_FIRST, nElapse);
|
|
|
406 }
|
|
|
407 return lRet;
|
|
|
408 }
|
|
|
409
|
|
|
410 LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
411 {
|
|
|
412 LRESULT lRet = 0;
|
|
|
413 if(!IsHoverMode() && !IsCheckMode())
|
|
|
414 lRet = this->DefWindowProc(uMsg, wParam, lParam);
|
|
|
415 if(::GetCapture() != this->m_hWnd)
|
|
|
416 this->SetCapture();
|
|
|
417 if(m_fPressed == 0)
|
|
|
418 {
|
|
|
419 m_fPressed = 1;
|
|
|
420 this->Invalidate();
|
|
|
421 this->UpdateWindow();
|
|
|
422 }
|
|
|
423 return lRet;
|
|
|
424 }
|
|
|
425
|
|
|
426 LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
427 {
|
|
|
428 if(((m_dwExtendedStyle & BMPBTN_AUTOCHECK) != 0) && (m_fPressed == 1))
|
|
|
429 SetCheck(!GetCheck(), false);
|
|
|
430
|
|
|
431 LRESULT lRet = 0;
|
|
|
432 if(!IsHoverMode() && !IsCheckMode())
|
|
|
433 lRet = this->DefWindowProc(uMsg, wParam, lParam);
|
|
|
434 if(::GetCapture() == this->m_hWnd)
|
|
|
435 {
|
|
|
436 if((IsHoverMode() || IsCheckMode()) && (m_fPressed == 1))
|
|
|
437 this->GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(this->GetDlgCtrlID(), BN_CLICKED), (LPARAM)this->m_hWnd);
|
|
|
438 ::ReleaseCapture();
|
|
|
439 }
|
|
|
440 return lRet;
|
|
|
441 }
|
|
|
442
|
|
|
443 LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
444 {
|
|
|
445 if(m_fPressed == 1)
|
|
|
446 {
|
|
|
447 m_fPressed = 0;
|
|
|
448 this->Invalidate();
|
|
|
449 this->UpdateWindow();
|
|
|
450 }
|
|
|
451 bHandled = FALSE;
|
|
|
452 return 1;
|
|
|
453 }
|
|
|
454
|
|
|
455 LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
456 {
|
|
|
457 this->Invalidate();
|
|
|
458 this->UpdateWindow();
|
|
|
459 bHandled = FALSE;
|
|
|
460 return 1;
|
|
|
461 }
|
|
|
462
|
|
|
463 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
|
|
|
464 {
|
|
|
465 if(::GetCapture() == this->m_hWnd)
|
|
|
466 {
|
|
|
467 POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
468 this->ClientToScreen(&ptCursor);
|
|
|
469 RECT rect = {};
|
|
|
470 this->GetWindowRect(&rect);
|
|
|
471 unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0;
|
|
|
472 if(m_fPressed != uPressed)
|
|
|
473 {
|
|
|
474 m_fPressed = uPressed;
|
|
|
475 this->Invalidate();
|
|
|
476 this->UpdateWindow();
|
|
|
477 }
|
|
|
478 }
|
|
|
479 else if(IsHoverMode() && m_fMouseOver == 0)
|
|
|
480 {
|
|
|
481 m_fMouseOver = 1;
|
|
|
482 this->Invalidate();
|
|
|
483 this->UpdateWindow();
|
|
|
484 StartTrackMouseLeave();
|
|
|
485 }
|
|
|
486 bHandled = FALSE;
|
|
|
487 return 1;
|
|
|
488 }
|
|
|
489
|
|
|
490 LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
491 {
|
|
|
492 if(m_fMouseOver == 1)
|
|
|
493 {
|
|
|
494 m_fMouseOver = 0;
|
|
|
495 this->Invalidate();
|
|
|
496 this->UpdateWindow();
|
|
|
497 }
|
|
|
498 return 0;
|
|
|
499 }
|
|
|
500
|
|
|
501 LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
502 {
|
|
|
503 if((wParam == VK_SPACE) && IsHoverMode())
|
|
|
504 return 0; // ignore if in hover mode
|
|
|
505 if((wParam == VK_SPACE) && (m_fPressed == 0))
|
|
|
506 {
|
|
|
507 m_fPressed = 1;
|
|
|
508 this->Invalidate();
|
|
|
509 this->UpdateWindow();
|
|
|
510 }
|
|
|
511 bHandled = FALSE;
|
|
|
512 return 1;
|
|
|
513 }
|
|
|
514
|
|
|
515 LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
516 {
|
|
|
517 if((wParam == VK_SPACE) && IsHoverMode())
|
|
|
518 return 0; // ignore if in hover mode
|
|
|
519 if((wParam == VK_SPACE) && (m_fPressed == 1))
|
|
|
520 {
|
|
|
521 m_fPressed = 0;
|
|
|
522 if((m_dwExtendedStyle & BMPBTN_AUTOCHECK) != 0)
|
|
|
523 SetCheck(!GetCheck(), false);
|
|
|
524 this->Invalidate();
|
|
|
525 this->UpdateWindow();
|
|
|
526 }
|
|
|
527 bHandled = FALSE;
|
|
|
528 return 1;
|
|
|
529 }
|
|
|
530
|
|
|
531 LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
532 {
|
|
|
533 ATLASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0);
|
|
|
534 switch(wParam) // timer ID
|
|
|
535 {
|
|
|
536 case ID_TIMER_FIRST:
|
|
|
537 this->KillTimer(ID_TIMER_FIRST);
|
|
|
538 if(m_fPressed == 1)
|
|
|
539 {
|
|
|
540 this->GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(this->GetDlgCtrlID(), BN_CLICKED), (LPARAM)this->m_hWnd);
|
|
|
541 int nElapse = 250;
|
|
|
542 int nRepeat = 40;
|
|
|
543 if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0))
|
|
|
544 nElapse = 10000 / (10 * nRepeat + 25); // milli-seconds, approximated
|
|
|
545 this->SetTimer(ID_TIMER_REPEAT, nElapse);
|
|
|
546 }
|
|
|
547 break;
|
|
|
548 case ID_TIMER_REPEAT:
|
|
|
549 if(m_fPressed == 1)
|
|
|
550 this->GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(this->GetDlgCtrlID(), BN_CLICKED), (LPARAM)this->m_hWnd);
|
|
|
551 else if(::GetCapture() != this->m_hWnd)
|
|
|
552 this->KillTimer(ID_TIMER_REPEAT);
|
|
|
553 break;
|
|
|
554 default: // not our timer
|
|
|
555 break;
|
|
|
556 }
|
|
|
557 return 0;
|
|
|
558 }
|
|
|
559
|
|
|
560 LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
561 {
|
|
|
562 // If the control is subclassed or superclassed, this message can cause
|
|
|
563 // repainting without WM_PAINT. We don't use this state, so just do nothing.
|
|
|
564 return 0;
|
|
|
565 }
|
|
|
566
|
|
|
567 // Implementation
|
|
|
568 void Init()
|
|
|
569 {
|
|
|
570 // We need this style to prevent Windows from painting the button
|
|
|
571 this->ModifyStyle(0, BS_OWNERDRAW);
|
|
|
572
|
|
|
573 // create a tool tip
|
|
|
574 m_tip.Create(this->m_hWnd);
|
|
|
575 ATLASSERT(m_tip.IsWindow());
|
|
|
576 if(m_tip.IsWindow() && (m_lpstrToolTipText != NULL))
|
|
|
577 {
|
|
|
578 m_tip.Activate(TRUE);
|
|
|
579 m_tip.AddTool(this->m_hWnd, m_lpstrToolTipText);
|
|
|
580 }
|
|
|
581
|
|
|
582 if((m_ImageList.m_hImageList != NULL) && ((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0))
|
|
|
583 SizeToImage();
|
|
|
584 }
|
|
|
585
|
|
|
586 BOOL StartTrackMouseLeave()
|
|
|
587 {
|
|
|
588 TRACKMOUSEEVENT tme = {};
|
|
|
589 tme.cbSize = sizeof(tme);
|
|
|
590 tme.dwFlags = TME_LEAVE;
|
|
|
591 tme.hwndTrack = this->m_hWnd;
|
|
|
592 return ::TrackMouseEvent(&tme);
|
|
|
593 }
|
|
|
594
|
|
|
595 bool IsHoverMode() const
|
|
|
596 {
|
|
|
597 return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0);
|
|
|
598 }
|
|
|
599
|
|
|
600 bool IsCheckMode() const
|
|
|
601 {
|
|
|
602 return ((m_dwExtendedStyle & (BMPBTN_CHECK | BMPBTN_AUTOCHECK)) != 0);
|
|
|
603 }
|
|
|
604 };
|
|
|
605
|
|
|
606 class CBitmapButton : public CBitmapButtonImpl<CBitmapButton>
|
|
|
607 {
|
|
|
608 public:
|
|
|
609 DECLARE_WND_SUPERCLASS(_T("WTL_BitmapButton"), GetWndClassName())
|
|
|
610
|
|
|
611 CBitmapButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) :
|
|
|
612 CBitmapButtonImpl<CBitmapButton>(dwExtendedStyle, hImageList)
|
|
|
613 { }
|
|
|
614 };
|
|
|
615
|
|
|
616
|
|
|
617 ///////////////////////////////////////////////////////////////////////////////
|
|
|
618 // CCheckListCtrlView - list view control with check boxes
|
|
|
619
|
|
|
620 template <DWORD t_dwStyle, DWORD t_dwExStyle, DWORD t_dwExListViewStyle>
|
|
|
621 class CCheckListViewCtrlImplTraits
|
|
|
622 {
|
|
|
623 public:
|
|
|
624 static DWORD GetWndStyle(DWORD dwStyle)
|
|
|
625 {
|
|
|
626 return (dwStyle == 0) ? t_dwStyle : dwStyle;
|
|
|
627 }
|
|
|
628
|
|
|
629 static DWORD GetWndExStyle(DWORD dwExStyle)
|
|
|
630 {
|
|
|
631 return (dwExStyle == 0) ? t_dwExStyle : dwExStyle;
|
|
|
632 }
|
|
|
633
|
|
|
634 static DWORD GetExtendedLVStyle()
|
|
|
635 {
|
|
|
636 return t_dwExListViewStyle;
|
|
|
637 }
|
|
|
638 };
|
|
|
639
|
|
|
640 typedef CCheckListViewCtrlImplTraits<WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT> CCheckListViewCtrlTraits;
|
|
|
641
|
|
|
642 template <class T, class TBase = CListViewCtrl, class TWinTraits = CCheckListViewCtrlTraits>
|
|
|
643 class ATL_NO_VTABLE CCheckListViewCtrlImpl : public ATL::CWindowImpl<T, TBase, TWinTraits >
|
|
|
644 {
|
|
|
645 public:
|
|
|
646 DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName())
|
|
|
647
|
|
|
648 // Attributes
|
|
|
649 static DWORD GetExtendedLVStyle()
|
|
|
650 {
|
|
|
651 return TWinTraits::GetExtendedLVStyle();
|
|
|
652 }
|
|
|
653
|
|
|
654 // Operations
|
|
|
655 BOOL SubclassWindow(HWND hWnd)
|
|
|
656 {
|
|
|
657 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
|
|
|
658 if(bRet != FALSE)
|
|
|
659 {
|
|
|
660 T* pT = static_cast<T*>(this);
|
|
|
661 pT->Init();
|
|
|
662 }
|
|
|
663
|
|
|
664 return bRet;
|
|
|
665 }
|
|
|
666
|
|
|
667 void CheckSelectedItems(int nCurrItem)
|
|
|
668 {
|
|
|
669 // first check if this item is selected
|
|
|
670 LVITEM lvi = {};
|
|
|
671 lvi.iItem = nCurrItem;
|
|
|
672 lvi.iSubItem = 0;
|
|
|
673 lvi.mask = LVIF_STATE;
|
|
|
674 lvi.stateMask = LVIS_SELECTED;
|
|
|
675 this->GetItem(&lvi);
|
|
|
676 // if item is not selected, don't do anything
|
|
|
677 if(!(lvi.state & LVIS_SELECTED))
|
|
|
678 return;
|
|
|
679 // new check state will be reverse of the current state,
|
|
|
680 BOOL bCheck = !this->GetCheckState(nCurrItem);
|
|
|
681 int nItem = -1;
|
|
|
682 int nOldItem = -1;
|
|
|
683 while((nItem = this->GetNextItem(nOldItem, LVNI_SELECTED)) != -1)
|
|
|
684 {
|
|
|
685 if(nItem != nCurrItem)
|
|
|
686 this->SetCheckState(nItem, bCheck);
|
|
|
687 nOldItem = nItem;
|
|
|
688 }
|
|
|
689 }
|
|
|
690
|
|
|
691 // Implementation
|
|
|
692 void Init()
|
|
|
693 {
|
|
|
694 T* pT = static_cast<T*>(this);
|
|
|
695 (void)pT; // avoid level 4 warning
|
|
|
696 ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
|
|
|
697 this->SetExtendedListViewStyle(pT->GetExtendedLVStyle());
|
|
|
698 }
|
|
|
699
|
|
|
700 // Message map and handlers
|
|
|
701 BEGIN_MSG_MAP(CCheckListViewCtrlImpl)
|
|
|
702 MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
|
|
703 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
|
|
|
704 MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
|
|
|
705 MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
|
|
|
706 END_MSG_MAP()
|
|
|
707
|
|
|
708 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
709 {
|
|
|
710 // first let list view control initialize everything
|
|
|
711 LRESULT lRet = this->DefWindowProc(uMsg, wParam, lParam);
|
|
|
712 if(lRet == 0)
|
|
|
713 {
|
|
|
714 T* pT = static_cast<T*>(this);
|
|
|
715 pT->Init();
|
|
|
716 }
|
|
|
717
|
|
|
718 return lRet;
|
|
|
719 }
|
|
|
720
|
|
|
721 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
|
|
|
722 {
|
|
|
723 POINT ptMsg = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
724 LVHITTESTINFO lvh = {};
|
|
|
725 lvh.pt = ptMsg;
|
|
|
726 if((this->HitTest(&lvh) != -1) && (lvh.flags == LVHT_ONITEMSTATEICON) && (::GetKeyState(VK_CONTROL) >= 0))
|
|
|
727 {
|
|
|
728 T* pT = static_cast<T*>(this);
|
|
|
729 pT->CheckSelectedItems(lvh.iItem);
|
|
|
730 }
|
|
|
731 bHandled = FALSE;
|
|
|
732 return 1;
|
|
|
733 }
|
|
|
734
|
|
|
735 LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
736 {
|
|
|
737 if(wParam == VK_SPACE)
|
|
|
738 {
|
|
|
739 int nCurrItem = this->GetNextItem(-1, LVNI_FOCUSED);
|
|
|
740 if((nCurrItem != -1) && (::GetKeyState(VK_CONTROL) >= 0))
|
|
|
741 {
|
|
|
742 T* pT = static_cast<T*>(this);
|
|
|
743 pT->CheckSelectedItems(nCurrItem);
|
|
|
744 }
|
|
|
745 }
|
|
|
746 bHandled = FALSE;
|
|
|
747 return 1;
|
|
|
748 }
|
|
|
749 };
|
|
|
750
|
|
|
751 class CCheckListViewCtrl : public CCheckListViewCtrlImpl<CCheckListViewCtrl>
|
|
|
752 {
|
|
|
753 public:
|
|
|
754 DECLARE_WND_SUPERCLASS(_T("WTL_CheckListView"), GetWndClassName())
|
|
|
755 };
|
|
|
756
|
|
|
757
|
|
|
758 ///////////////////////////////////////////////////////////////////////////////
|
|
|
759 // CHyperLink - hyper link control implementation
|
|
|
760
|
|
|
761 #define HLINK_UNDERLINED 0x00000000
|
|
|
762 #define HLINK_NOTUNDERLINED 0x00000001
|
|
|
763 #define HLINK_UNDERLINEHOVER 0x00000002
|
|
|
764 #define HLINK_COMMANDBUTTON 0x00000004
|
|
|
765 #define HLINK_NOTIFYBUTTON 0x0000000C
|
|
|
766 #define HLINK_USETAGS 0x00000010
|
|
|
767 #define HLINK_USETAGSBOLD 0x00000030
|
|
|
768 #define HLINK_NOTOOLTIP 0x00000040
|
|
|
769 #define HLINK_AUTOCREATELINKFONT 0x00000080
|
|
|
770 #define HLINK_SINGLELINE 0x00000100
|
|
|
771
|
|
|
772 // Notes:
|
|
|
773 // - HLINK_USETAGS and HLINK_USETAGSBOLD are always left-aligned
|
|
|
774 // - When HLINK_USETAGSBOLD is used, the underlined styles will be ignored
|
|
|
775
|
|
|
776 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
|
|
|
777 class ATL_NO_VTABLE CHyperLinkImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
|
|
|
778 {
|
|
|
779 public:
|
|
|
780 LPTSTR m_lpstrLabel;
|
|
|
781 LPTSTR m_lpstrHyperLink;
|
|
|
782
|
|
|
783 HCURSOR m_hCursor;
|
|
|
784 HFONT m_hFontLink;
|
|
|
785 HFONT m_hFontNormal;
|
|
|
786
|
|
|
787 RECT m_rcLink;
|
|
|
788 CToolTipCtrl m_tip;
|
|
|
789
|
|
|
790 COLORREF m_clrLink;
|
|
|
791 COLORREF m_clrVisited;
|
|
|
792
|
|
|
793 DWORD m_dwExtendedStyle; // Hyper Link specific extended styles
|
|
|
794
|
|
|
795 bool m_bPaintLabel:1;
|
|
|
796 bool m_bVisited:1;
|
|
|
797 bool m_bHover:1;
|
|
|
798 bool m_bInternalLinkFont:1;
|
|
|
799 bool m_bInternalNormalFont:1;
|
|
|
800
|
|
|
801
|
|
|
802 // Constructor/Destructor
|
|
|
803 CHyperLinkImpl(DWORD dwExtendedStyle = HLINK_UNDERLINED) :
|
|
|
804 m_lpstrLabel(NULL), m_lpstrHyperLink(NULL),
|
|
|
805 m_hCursor(NULL), m_hFontLink(NULL), m_hFontNormal(NULL),
|
|
|
806 m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128)),
|
|
|
807 m_dwExtendedStyle(dwExtendedStyle),
|
|
|
808 m_bPaintLabel(true), m_bVisited(false),
|
|
|
809 m_bHover(false), m_bInternalLinkFont(false), m_bInternalNormalFont(false)
|
|
|
810 {
|
|
|
811 ::SetRectEmpty(&m_rcLink);
|
|
|
812 }
|
|
|
813
|
|
|
814 ~CHyperLinkImpl()
|
|
|
815 {
|
|
|
816 delete [] m_lpstrLabel;
|
|
|
817 delete [] m_lpstrHyperLink;
|
|
|
818 }
|
|
|
819
|
|
|
820 // Attributes
|
|
|
821 DWORD GetHyperLinkExtendedStyle() const
|
|
|
822 {
|
|
|
823 return m_dwExtendedStyle;
|
|
|
824 }
|
|
|
825
|
|
|
826 DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
|
|
|
827 {
|
|
|
828 DWORD dwPrevStyle = m_dwExtendedStyle;
|
|
|
829 if(dwMask == 0)
|
|
|
830 m_dwExtendedStyle = dwExtendedStyle;
|
|
|
831 else
|
|
|
832 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
|
|
|
833 return dwPrevStyle;
|
|
|
834 }
|
|
|
835
|
|
|
836 bool GetLabel(LPTSTR lpstrBuffer, int nLength) const
|
|
|
837 {
|
|
|
838 if(m_lpstrLabel == NULL)
|
|
|
839 return false;
|
|
|
840 ATLASSERT(lpstrBuffer != NULL);
|
|
|
841 if(nLength <= lstrlen(m_lpstrLabel))
|
|
|
842 return false;
|
|
|
843
|
|
|
844 ATL::Checked::tcscpy_s(lpstrBuffer, nLength, m_lpstrLabel);
|
|
|
845
|
|
|
846 return true;
|
|
|
847 }
|
|
|
848
|
|
|
849 bool SetLabel(LPCTSTR lpstrLabel)
|
|
|
850 {
|
|
|
851 delete [] m_lpstrLabel;
|
|
|
852 m_lpstrLabel = NULL;
|
|
|
853 int cchLen = lstrlen(lpstrLabel) + 1;
|
|
|
854 ATLTRY(m_lpstrLabel = new TCHAR[cchLen]);
|
|
|
855 if(m_lpstrLabel == NULL)
|
|
|
856 return false;
|
|
|
857
|
|
|
858 ATL::Checked::tcscpy_s(m_lpstrLabel, cchLen, lpstrLabel);
|
|
|
859 T* pT = static_cast<T*>(this);
|
|
|
860 pT->CalcLabelRect();
|
|
|
861
|
|
|
862 if(this->m_hWnd != NULL)
|
|
|
863 this->SetWindowText(lpstrLabel); // Set this for accessibility
|
|
|
864
|
|
|
865 return true;
|
|
|
866 }
|
|
|
867
|
|
|
868 bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const
|
|
|
869 {
|
|
|
870 if(m_lpstrHyperLink == NULL)
|
|
|
871 return false;
|
|
|
872 ATLASSERT(lpstrBuffer != NULL);
|
|
|
873 if(nLength <= lstrlen(m_lpstrHyperLink))
|
|
|
874 return false;
|
|
|
875
|
|
|
876 ATL::Checked::tcscpy_s(lpstrBuffer, nLength, m_lpstrHyperLink);
|
|
|
877
|
|
|
878 return true;
|
|
|
879 }
|
|
|
880
|
|
|
881 bool SetHyperLink(LPCTSTR lpstrLink)
|
|
|
882 {
|
|
|
883 delete [] m_lpstrHyperLink;
|
|
|
884 m_lpstrHyperLink = NULL;
|
|
|
885 int cchLen = lstrlen(lpstrLink) + 1;
|
|
|
886 ATLTRY(m_lpstrHyperLink = new TCHAR[cchLen]);
|
|
|
887 if(m_lpstrHyperLink == NULL)
|
|
|
888 return false;
|
|
|
889
|
|
|
890 ATL::Checked::tcscpy_s(m_lpstrHyperLink, cchLen, lpstrLink);
|
|
|
891 if(m_lpstrLabel == NULL)
|
|
|
892 {
|
|
|
893 T* pT = static_cast<T*>(this);
|
|
|
894 pT->CalcLabelRect();
|
|
|
895 }
|
|
|
896
|
|
|
897 if(m_tip.IsWindow())
|
|
|
898 {
|
|
|
899 m_tip.Activate(TRUE);
|
|
|
900 m_tip.AddTool(this->m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
|
|
|
901 }
|
|
|
902
|
|
|
903 return true;
|
|
|
904 }
|
|
|
905
|
|
|
906 HFONT GetLinkFont() const
|
|
|
907 {
|
|
|
908 return m_hFontLink;
|
|
|
909 }
|
|
|
910
|
|
|
911 void SetLinkFont(HFONT hFont)
|
|
|
912 {
|
|
|
913 if(m_bInternalLinkFont)
|
|
|
914 {
|
|
|
915 ::DeleteObject(m_hFontLink);
|
|
|
916 m_bInternalLinkFont = false;
|
|
|
917 }
|
|
|
918
|
|
|
919 m_hFontLink = hFont;
|
|
|
920
|
|
|
921 T* pT = static_cast<T*>(this);
|
|
|
922 pT->CalcLabelRect();
|
|
|
923 }
|
|
|
924
|
|
|
925 int GetIdealHeight() const
|
|
|
926 {
|
|
|
927 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
928 if((m_lpstrLabel == NULL) && (m_lpstrHyperLink == NULL))
|
|
|
929 return -1;
|
|
|
930 if(!m_bPaintLabel)
|
|
|
931 return -1;
|
|
|
932
|
|
|
933 UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
|
|
|
934
|
|
|
935 CClientDC dc(this->m_hWnd);
|
|
|
936 RECT rect = {};
|
|
|
937 this->GetClientRect(&rect);
|
|
|
938 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
|
|
|
939 RECT rcText = rect;
|
|
|
940 dc.DrawText(_T("NS"), -1, &rcText, DT_LEFT | uFormat | DT_CALCRECT);
|
|
|
941 dc.SelectFont(m_hFontLink);
|
|
|
942 RECT rcLink = rect;
|
|
|
943 dc.DrawText(_T("NS"), -1, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
|
|
|
944 dc.SelectFont(hFontOld);
|
|
|
945 return __max(rcText.bottom - rcText.top, rcLink.bottom - rcLink.top);
|
|
|
946 }
|
|
|
947
|
|
|
948 bool GetIdealSize(SIZE& size) const
|
|
|
949 {
|
|
|
950 int cx = 0, cy = 0;
|
|
|
951 bool bRet = GetIdealSize(cx, cy);
|
|
|
952 if(bRet)
|
|
|
953 {
|
|
|
954 size.cx = cx;
|
|
|
955 size.cy = cy;
|
|
|
956 }
|
|
|
957 return bRet;
|
|
|
958 }
|
|
|
959
|
|
|
960 bool GetIdealSize(int& cx, int& cy) const
|
|
|
961 {
|
|
|
962 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
963 if((m_lpstrLabel == NULL) && (m_lpstrHyperLink == NULL))
|
|
|
964 return false;
|
|
|
965 if(!m_bPaintLabel)
|
|
|
966 return false;
|
|
|
967
|
|
|
968 CClientDC dc(this->m_hWnd);
|
|
|
969 RECT rcClient = {};
|
|
|
970 this->GetClientRect(&rcClient);
|
|
|
971 RECT rcAll = rcClient;
|
|
|
972
|
|
|
973 if(IsUsingTags())
|
|
|
974 {
|
|
|
975 // find tags and label parts
|
|
|
976 LPTSTR lpstrLeft = NULL;
|
|
|
977 int cchLeft = 0;
|
|
|
978 LPTSTR lpstrLink = NULL;
|
|
|
979 int cchLink = 0;
|
|
|
980 LPTSTR lpstrRight = NULL;
|
|
|
981 int cchRight = 0;
|
|
|
982
|
|
|
983 const T* pT = static_cast<const T*>(this);
|
|
|
984 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
|
|
|
985
|
|
|
986 // get label part rects
|
|
|
987 UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
|
|
|
988
|
|
|
989 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
|
|
|
990 RECT rcLeft = rcClient;
|
|
|
991 dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
|
|
|
992
|
|
|
993 dc.SelectFont(m_hFontLink);
|
|
|
994 RECT rcLink = { rcLeft.right, rcLeft.top, rcClient.right, rcClient.bottom };
|
|
|
995 dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
|
|
|
996
|
|
|
997 dc.SelectFont(m_hFontNormal);
|
|
|
998 RECT rcRight = { rcLink.right, rcLink.top, rcClient.right, rcClient.bottom };
|
|
|
999 dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat | DT_CALCRECT);
|
|
|
1000
|
|
|
1001 dc.SelectFont(hFontOld);
|
|
|
1002
|
|
|
1003 int cyMax = __max(rcLeft.bottom, __max(rcLink.bottom, rcRight.bottom));
|
|
|
1004 ::SetRect(&rcAll, rcLeft.left, rcLeft.top, rcRight.right, cyMax);
|
|
|
1005 }
|
|
|
1006 else
|
|
|
1007 {
|
|
|
1008 HFONT hOldFont = NULL;
|
|
|
1009 if(m_hFontLink != NULL)
|
|
|
1010 hOldFont = dc.SelectFont(m_hFontLink);
|
|
|
1011 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
|
|
|
1012 DWORD dwStyle = this->GetStyle();
|
|
|
1013 UINT uFormat = DT_LEFT;
|
|
|
1014 if (dwStyle & SS_CENTER)
|
|
|
1015 uFormat = DT_CENTER;
|
|
|
1016 else if (dwStyle & SS_RIGHT)
|
|
|
1017 uFormat = DT_RIGHT;
|
|
|
1018 uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
|
|
|
1019 dc.DrawText(lpstrText, -1, &rcAll, uFormat | DT_CALCRECT);
|
|
|
1020 if(m_hFontLink != NULL)
|
|
|
1021 dc.SelectFont(hOldFont);
|
|
|
1022 if (dwStyle & SS_CENTER)
|
|
|
1023 {
|
|
|
1024 int dx = (rcClient.right - rcAll.right) / 2;
|
|
|
1025 ::OffsetRect(&rcAll, dx, 0);
|
|
|
1026 }
|
|
|
1027 else if (dwStyle & SS_RIGHT)
|
|
|
1028 {
|
|
|
1029 int dx = rcClient.right - rcAll.right;
|
|
|
1030 ::OffsetRect(&rcAll, dx, 0);
|
|
|
1031 }
|
|
|
1032 }
|
|
|
1033
|
|
|
1034 cx = rcAll.right - rcAll.left;
|
|
|
1035 cy = rcAll.bottom - rcAll.top;
|
|
|
1036
|
|
|
1037 return true;
|
|
|
1038 }
|
|
|
1039
|
|
|
1040 // for command buttons only
|
|
|
1041 bool GetToolTipText(LPTSTR lpstrBuffer, int nLength) const
|
|
|
1042 {
|
|
|
1043 ATLASSERT(IsCommandButton());
|
|
|
1044 return GetHyperLink(lpstrBuffer, nLength);
|
|
|
1045 }
|
|
|
1046
|
|
|
1047 bool SetToolTipText(LPCTSTR lpstrToolTipText)
|
|
|
1048 {
|
|
|
1049 ATLASSERT(IsCommandButton());
|
|
|
1050 return SetHyperLink(lpstrToolTipText);
|
|
|
1051 }
|
|
|
1052
|
|
|
1053 // Operations
|
|
|
1054 BOOL SubclassWindow(HWND hWnd)
|
|
|
1055 {
|
|
|
1056 ATLASSERT(this->m_hWnd == NULL);
|
|
|
1057 ATLASSERT(::IsWindow(hWnd));
|
|
|
1058 if(m_hFontNormal == NULL)
|
|
|
1059 m_hFontNormal = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0L);
|
|
|
1060
|
|
|
1061 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
|
|
|
1062 if(bRet != FALSE)
|
|
|
1063 {
|
|
|
1064 T* pT = static_cast<T*>(this);
|
|
|
1065 pT->Init();
|
|
|
1066 }
|
|
|
1067
|
|
|
1068 return bRet;
|
|
|
1069 }
|
|
|
1070
|
|
|
1071 bool Navigate()
|
|
|
1072 {
|
|
|
1073 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
1074 bool bRet = true;
|
|
|
1075 if(IsNotifyButton())
|
|
|
1076 {
|
|
|
1077 NMHDR nmhdr = { this->m_hWnd, (UINT_PTR)this->GetDlgCtrlID(), NM_CLICK };
|
|
|
1078 this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&nmhdr);
|
|
|
1079 }
|
|
|
1080 else if(IsCommandButton())
|
|
|
1081 {
|
|
|
1082 this->GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(this->GetDlgCtrlID(), BN_CLICKED), (LPARAM)this->m_hWnd);
|
|
|
1083 }
|
|
|
1084 else
|
|
|
1085 {
|
|
|
1086 ATLASSERT(m_lpstrHyperLink != NULL);
|
|
|
1087 DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL);
|
|
|
1088 bRet = (dwRet > 32);
|
|
|
1089 ATLASSERT(bRet);
|
|
|
1090 if(bRet)
|
|
|
1091 {
|
|
|
1092 m_bVisited = true;
|
|
|
1093 this->Invalidate();
|
|
|
1094 }
|
|
|
1095 }
|
|
|
1096 return bRet;
|
|
|
1097 }
|
|
|
1098
|
|
|
1099 void CreateLinkFontFromNormal()
|
|
|
1100 {
|
|
|
1101 if(m_bInternalLinkFont)
|
|
|
1102 {
|
|
|
1103 ::DeleteObject(m_hFontLink);
|
|
|
1104 m_bInternalLinkFont = false;
|
|
|
1105 }
|
|
|
1106
|
|
|
1107 CFontHandle font = (m_hFontNormal != NULL) ? m_hFontNormal : (HFONT)::GetStockObject(SYSTEM_FONT);
|
|
|
1108 LOGFONT lf = {};
|
|
|
1109 font.GetLogFont(&lf);
|
|
|
1110
|
|
|
1111 if(IsUsingTagsBold())
|
|
|
1112 lf.lfWeight = FW_BOLD;
|
|
|
1113 else if(!IsNotUnderlined())
|
|
|
1114 lf.lfUnderline = TRUE;
|
|
|
1115
|
|
|
1116 m_hFontLink = ::CreateFontIndirect(&lf);
|
|
|
1117 m_bInternalLinkFont = true;
|
|
|
1118 ATLASSERT(m_hFontLink != NULL);
|
|
|
1119 }
|
|
|
1120
|
|
|
1121 // Message map and handlers
|
|
|
1122 BEGIN_MSG_MAP(CHyperLinkImpl)
|
|
|
1123 MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
|
|
1124 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
|
|
|
1125 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
|
|
|
1126 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
|
|
|
1127 MESSAGE_HANDLER(WM_PAINT, OnPaint)
|
|
|
1128 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
|
|
|
1129 MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
|
|
|
1130 MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
|
|
|
1131 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
|
|
|
1132 MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
|
|
|
1133 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
|
|
|
1134 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
|
|
|
1135 MESSAGE_HANDLER(WM_CHAR, OnChar)
|
|
|
1136 MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
|
|
|
1137 MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
|
|
|
1138 MESSAGE_HANDLER(WM_ENABLE, OnEnable)
|
|
|
1139 MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
|
|
|
1140 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
|
|
|
1141 MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
|
|
|
1142 MESSAGE_HANDLER(WM_SIZE, OnSize)
|
|
|
1143 END_MSG_MAP()
|
|
|
1144
|
|
|
1145 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
1146 {
|
|
|
1147 T* pT = static_cast<T*>(this);
|
|
|
1148 pT->Init();
|
|
|
1149 return 0;
|
|
|
1150 }
|
|
|
1151
|
|
|
1152 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
1153 {
|
|
|
1154 if(m_tip.IsWindow())
|
|
|
1155 {
|
|
|
1156 m_tip.DestroyWindow();
|
|
|
1157 m_tip.m_hWnd = NULL;
|
|
|
1158 }
|
|
|
1159
|
|
|
1160 if(m_bInternalLinkFont)
|
|
|
1161 {
|
|
|
1162 ::DeleteObject(m_hFontLink);
|
|
|
1163 m_hFontLink = NULL;
|
|
|
1164 m_bInternalLinkFont = false;
|
|
|
1165 }
|
|
|
1166
|
|
|
1167 if(m_bInternalNormalFont)
|
|
|
1168 {
|
|
|
1169 ::DeleteObject(m_hFontNormal);
|
|
|
1170 m_hFontNormal = NULL;
|
|
|
1171 m_bInternalNormalFont = false;
|
|
|
1172 }
|
|
|
1173
|
|
|
1174 bHandled = FALSE;
|
|
|
1175 return 1;
|
|
|
1176 }
|
|
|
1177
|
|
|
1178 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
1179 {
|
|
|
1180 MSG msg = { this->m_hWnd, uMsg, wParam, lParam };
|
|
|
1181 if(m_tip.IsWindow() && IsUsingToolTip())
|
|
|
1182 m_tip.RelayEvent(&msg);
|
|
|
1183 bHandled = FALSE;
|
|
|
1184 return 1;
|
|
|
1185 }
|
|
|
1186
|
|
|
1187 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
1188 {
|
|
|
1189 return 1; // no background painting needed (we do it all during WM_PAINT)
|
|
|
1190 }
|
|
|
1191
|
|
|
1192 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
1193 {
|
|
|
1194 if(!m_bPaintLabel)
|
|
|
1195 {
|
|
|
1196 bHandled = FALSE;
|
|
|
1197 return 1;
|
|
|
1198 }
|
|
|
1199
|
|
|
1200 T* pT = static_cast<T*>(this);
|
|
|
1201 if(wParam != NULL)
|
|
|
1202 {
|
|
|
1203 pT->DoEraseBackground((HDC)wParam);
|
|
|
1204 pT->DoPaint((HDC)wParam);
|
|
|
1205 }
|
|
|
1206 else
|
|
|
1207 {
|
|
|
1208 CPaintDC dc(this->m_hWnd);
|
|
|
1209 pT->DoEraseBackground(dc.m_hDC);
|
|
|
1210 pT->DoPaint(dc.m_hDC);
|
|
|
1211 }
|
|
|
1212
|
|
|
1213 return 0;
|
|
|
1214 }
|
|
|
1215
|
|
|
1216 LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
1217 {
|
|
|
1218 if(m_bPaintLabel)
|
|
|
1219 this->Invalidate();
|
|
|
1220 else
|
|
|
1221 bHandled = FALSE;
|
|
|
1222 return 0;
|
|
|
1223 }
|
|
|
1224
|
|
|
1225 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
|
|
|
1226 {
|
|
|
1227 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
1228 if(((m_lpstrHyperLink != NULL) || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
|
|
|
1229 {
|
|
|
1230 ::SetCursor(m_hCursor);
|
|
|
1231 if(IsUnderlineHover())
|
|
|
1232 {
|
|
|
1233 if(!m_bHover)
|
|
|
1234 {
|
|
|
1235 m_bHover = true;
|
|
|
1236 this->InvalidateRect(&m_rcLink);
|
|
|
1237 this->UpdateWindow();
|
|
|
1238 StartTrackMouseLeave();
|
|
|
1239 }
|
|
|
1240 }
|
|
|
1241 }
|
|
|
1242 else
|
|
|
1243 {
|
|
|
1244 if(IsUnderlineHover())
|
|
|
1245 {
|
|
|
1246 if(m_bHover)
|
|
|
1247 {
|
|
|
1248 m_bHover = false;
|
|
|
1249 this->InvalidateRect(&m_rcLink);
|
|
|
1250 this->UpdateWindow();
|
|
|
1251 }
|
|
|
1252 }
|
|
|
1253 bHandled = FALSE;
|
|
|
1254 }
|
|
|
1255 return 0;
|
|
|
1256 }
|
|
|
1257
|
|
|
1258 LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
1259 {
|
|
|
1260 if(IsUnderlineHover() && m_bHover)
|
|
|
1261 {
|
|
|
1262 m_bHover = false;
|
|
|
1263 this->InvalidateRect(&m_rcLink);
|
|
|
1264 this->UpdateWindow();
|
|
|
1265 }
|
|
|
1266 return 0;
|
|
|
1267 }
|
|
|
1268
|
|
|
1269 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
1270 {
|
|
|
1271 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
1272 if(::PtInRect(&m_rcLink, pt))
|
|
|
1273 {
|
|
|
1274 this->SetFocus();
|
|
|
1275 this->SetCapture();
|
|
|
1276 }
|
|
|
1277 return 0;
|
|
|
1278 }
|
|
|
1279
|
|
|
1280 LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
1281 {
|
|
|
1282 if(GetCapture() == this->m_hWnd)
|
|
|
1283 {
|
|
|
1284 ReleaseCapture();
|
|
|
1285 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
1286 if(::PtInRect(&m_rcLink, pt))
|
|
|
1287 {
|
|
|
1288 T* pT = static_cast<T*>(this);
|
|
|
1289 pT->Navigate();
|
|
|
1290 }
|
|
|
1291 }
|
|
|
1292 return 0;
|
|
|
1293 }
|
|
|
1294
|
|
|
1295 LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
1296 {
|
|
|
1297 if((wParam == VK_RETURN) || (wParam == VK_SPACE))
|
|
|
1298 {
|
|
|
1299 T* pT = static_cast<T*>(this);
|
|
|
1300 pT->Navigate();
|
|
|
1301 }
|
|
|
1302 return 0;
|
|
|
1303 }
|
|
|
1304
|
|
|
1305 LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
1306 {
|
|
|
1307 return DLGC_WANTCHARS;
|
|
|
1308 }
|
|
|
1309
|
|
|
1310 LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
1311 {
|
|
|
1312 POINT pt = {};
|
|
|
1313 GetCursorPos(&pt);
|
|
|
1314 this->ScreenToClient(&pt);
|
|
|
1315 if(((m_lpstrHyperLink != NULL) || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
|
|
|
1316 {
|
|
|
1317 return TRUE;
|
|
|
1318 }
|
|
|
1319 bHandled = FALSE;
|
|
|
1320 return FALSE;
|
|
|
1321 }
|
|
|
1322
|
|
|
1323 LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
1324 {
|
|
|
1325 this->Invalidate();
|
|
|
1326 this->UpdateWindow();
|
|
|
1327 return 0;
|
|
|
1328 }
|
|
|
1329
|
|
|
1330 LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
1331 {
|
|
|
1332 return (LRESULT)m_hFontNormal;
|
|
|
1333 }
|
|
|
1334
|
|
|
1335 LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
1336 {
|
|
|
1337 if(m_bInternalNormalFont)
|
|
|
1338 {
|
|
|
1339 ::DeleteObject(m_hFontNormal);
|
|
|
1340 m_bInternalNormalFont = false;
|
|
|
1341 }
|
|
|
1342
|
|
|
1343 bool bCreateLinkFont = m_bInternalLinkFont;
|
|
|
1344
|
|
|
1345 m_hFontNormal = (HFONT)wParam;
|
|
|
1346
|
|
|
1347 if(bCreateLinkFont || IsAutoCreateLinkFont())
|
|
|
1348 CreateLinkFontFromNormal();
|
|
|
1349
|
|
|
1350 T* pT = static_cast<T*>(this);
|
|
|
1351 pT->CalcLabelRect();
|
|
|
1352
|
|
|
1353 if((BOOL)lParam)
|
|
|
1354 {
|
|
|
1355 this->Invalidate();
|
|
|
1356 this->UpdateWindow();
|
|
|
1357 }
|
|
|
1358
|
|
|
1359 return 0;
|
|
|
1360 }
|
|
|
1361
|
|
|
1362 LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
1363 {
|
|
|
1364 // If the control is subclassed or superclassed, this message can cause
|
|
|
1365 // repainting without WM_PAINT. We don't use this state, so just do nothing.
|
|
|
1366 return 0;
|
|
|
1367 }
|
|
|
1368
|
|
|
1369 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
1370 {
|
|
|
1371 T* pT = static_cast<T*>(this);
|
|
|
1372 pT->CalcLabelRect();
|
|
|
1373 pT->Invalidate();
|
|
|
1374 return 0;
|
|
|
1375 }
|
|
|
1376
|
|
|
1377 // Implementation
|
|
|
1378 void Init()
|
|
|
1379 {
|
|
|
1380 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
1381
|
|
|
1382 // Check if we should paint a label
|
|
|
1383 const int cchBuff = 8;
|
|
|
1384 TCHAR szBuffer[cchBuff] = {};
|
|
|
1385 if(::GetClassName(this->m_hWnd, szBuffer, cchBuff))
|
|
|
1386 {
|
|
|
1387 if(lstrcmpi(szBuffer, _T("static")) == 0)
|
|
|
1388 {
|
|
|
1389 this->ModifyStyle(0, SS_NOTIFY); // we need this
|
|
|
1390 DWORD dwStyle = this->GetStyle() & 0x000000FF;
|
|
|
1391 if((dwStyle == SS_ICON) || (dwStyle == SS_BLACKRECT) || (dwStyle == SS_GRAYRECT) ||
|
|
|
1392 (dwStyle == SS_WHITERECT) || (dwStyle == SS_BLACKFRAME) || (dwStyle == SS_GRAYFRAME) ||
|
|
|
1393 (dwStyle == SS_WHITEFRAME) || (dwStyle == SS_OWNERDRAW) ||
|
|
|
1394 (dwStyle == SS_BITMAP) || (dwStyle == SS_ENHMETAFILE))
|
|
|
1395 m_bPaintLabel = false;
|
|
|
1396 }
|
|
|
1397 }
|
|
|
1398
|
|
|
1399 // create or load a cursor
|
|
|
1400 m_hCursor = ::LoadCursor(NULL, IDC_HAND);
|
|
|
1401 ATLASSERT(m_hCursor != NULL);
|
|
|
1402
|
|
|
1403 // set fonts
|
|
|
1404 if(m_bPaintLabel)
|
|
|
1405 {
|
|
|
1406 if(m_hFontNormal == NULL)
|
|
|
1407 {
|
|
|
1408 m_hFontNormal = AtlCreateControlFont();
|
|
|
1409 m_bInternalNormalFont = true;
|
|
|
1410 }
|
|
|
1411
|
|
|
1412 if(m_hFontLink == NULL)
|
|
|
1413 CreateLinkFontFromNormal();
|
|
|
1414 }
|
|
|
1415
|
|
|
1416 // create a tool tip
|
|
|
1417 m_tip.Create(this->m_hWnd);
|
|
|
1418 ATLASSERT(m_tip.IsWindow());
|
|
|
1419
|
|
|
1420 // set label (defaults to window text)
|
|
|
1421 if(m_lpstrLabel == NULL)
|
|
|
1422 {
|
|
|
1423 int nLen = this->GetWindowTextLength();
|
|
|
1424 if(nLen > 0)
|
|
|
1425 {
|
|
|
1426 ATLTRY(m_lpstrLabel = new TCHAR[nLen + 1]);
|
|
|
1427 if(m_lpstrLabel != NULL)
|
|
|
1428 ATLVERIFY(this->GetWindowText(m_lpstrLabel, nLen + 1) > 0);
|
|
|
1429 }
|
|
|
1430 }
|
|
|
1431
|
|
|
1432 T* pT = static_cast<T*>(this);
|
|
|
1433 pT->CalcLabelRect();
|
|
|
1434
|
|
|
1435 // set hyperlink (defaults to label), or just activate tool tip if already set
|
|
|
1436 if((m_lpstrHyperLink == NULL) && !IsCommandButton())
|
|
|
1437 {
|
|
|
1438 if(m_lpstrLabel != NULL)
|
|
|
1439 SetHyperLink(m_lpstrLabel);
|
|
|
1440 }
|
|
|
1441 else
|
|
|
1442 {
|
|
|
1443 m_tip.Activate(TRUE);
|
|
|
1444 m_tip.AddTool(this->m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
|
|
|
1445 }
|
|
|
1446
|
|
|
1447 // set link colors
|
|
|
1448 if(m_bPaintLabel)
|
|
|
1449 {
|
|
|
1450 ATL::CRegKey rk;
|
|
|
1451 LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings"));
|
|
|
1452 if(lRet == ERROR_SUCCESS)
|
|
|
1453 {
|
|
|
1454 const int cchValue = 12;
|
|
|
1455 TCHAR szValue[cchValue] = {};
|
|
|
1456 ULONG ulCount = cchValue;
|
|
|
1457 lRet = rk.QueryStringValue(_T("Anchor Color"), szValue, &ulCount);
|
|
|
1458 if(lRet == ERROR_SUCCESS)
|
|
|
1459 {
|
|
|
1460 COLORREF clr = pT->_ParseColorString(szValue);
|
|
|
1461 ATLASSERT(clr != CLR_INVALID);
|
|
|
1462 if(clr != CLR_INVALID)
|
|
|
1463 m_clrLink = clr;
|
|
|
1464 }
|
|
|
1465
|
|
|
1466 ulCount = cchValue;
|
|
|
1467 lRet = rk.QueryStringValue(_T("Anchor Color Visited"), szValue, &ulCount);
|
|
|
1468 if(lRet == ERROR_SUCCESS)
|
|
|
1469 {
|
|
|
1470 COLORREF clr = pT->_ParseColorString(szValue);
|
|
|
1471 ATLASSERT(clr != CLR_INVALID);
|
|
|
1472 if(clr != CLR_INVALID)
|
|
|
1473 m_clrVisited = clr;
|
|
|
1474 }
|
|
|
1475 }
|
|
|
1476 }
|
|
|
1477 }
|
|
|
1478
|
|
|
1479 static COLORREF _ParseColorString(LPTSTR lpstr)
|
|
|
1480 {
|
|
|
1481 int c[3] = { -1, -1, -1 };
|
|
|
1482 LPTSTR p = NULL;
|
|
|
1483 for(int i = 0; i < 2; i++)
|
|
|
1484 {
|
|
|
1485 for(p = lpstr; *p != _T('\0'); p = ::CharNext(p))
|
|
|
1486 {
|
|
|
1487 if(*p == _T(','))
|
|
|
1488 {
|
|
|
1489 *p = _T('\0');
|
|
|
1490 c[i] = _ttoi(lpstr);
|
|
|
1491 lpstr = &p[1];
|
|
|
1492 break;
|
|
|
1493 }
|
|
|
1494 }
|
|
|
1495 if(c[i] == -1)
|
|
|
1496 return CLR_INVALID;
|
|
|
1497 }
|
|
|
1498 if(*lpstr == _T('\0'))
|
|
|
1499 return CLR_INVALID;
|
|
|
1500 c[2] = _ttoi(lpstr);
|
|
|
1501
|
|
|
1502 return RGB(c[0], c[1], c[2]);
|
|
|
1503 }
|
|
|
1504
|
|
|
1505 bool CalcLabelRect()
|
|
|
1506 {
|
|
|
1507 if(!::IsWindow(this->m_hWnd))
|
|
|
1508 return false;
|
|
|
1509 if((m_lpstrLabel == NULL) && (m_lpstrHyperLink == NULL))
|
|
|
1510 return false;
|
|
|
1511
|
|
|
1512 CClientDC dc(this->m_hWnd);
|
|
|
1513 RECT rcClient = {};
|
|
|
1514 this->GetClientRect(&rcClient);
|
|
|
1515 m_rcLink = rcClient;
|
|
|
1516 if(!m_bPaintLabel)
|
|
|
1517 return true;
|
|
|
1518
|
|
|
1519 if(IsUsingTags())
|
|
|
1520 {
|
|
|
1521 // find tags and label parts
|
|
|
1522 LPTSTR lpstrLeft = NULL;
|
|
|
1523 int cchLeft = 0;
|
|
|
1524 LPTSTR lpstrLink = NULL;
|
|
|
1525 int cchLink = 0;
|
|
|
1526 LPTSTR lpstrRight = NULL;
|
|
|
1527 int cchRight = 0;
|
|
|
1528
|
|
|
1529 T* pT = static_cast<T*>(this);
|
|
|
1530 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
|
|
|
1531 ATLASSERT(lpstrLink != NULL);
|
|
|
1532 ATLASSERT(cchLink > 0);
|
|
|
1533
|
|
|
1534 // get label part rects
|
|
|
1535 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
|
|
|
1536
|
|
|
1537 UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
|
|
|
1538
|
|
|
1539 RECT rcLeft = rcClient;
|
|
|
1540 if(lpstrLeft != NULL)
|
|
|
1541 dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
|
|
|
1542
|
|
|
1543 dc.SelectFont(m_hFontLink);
|
|
|
1544 RECT rcLink = rcClient;
|
|
|
1545 if(lpstrLeft != NULL)
|
|
|
1546 rcLink.left = rcLeft.right;
|
|
|
1547 dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
|
|
|
1548
|
|
|
1549 dc.SelectFont(hFontOld);
|
|
|
1550
|
|
|
1551 m_rcLink = rcLink;
|
|
|
1552 }
|
|
|
1553 else
|
|
|
1554 {
|
|
|
1555 HFONT hOldFont = NULL;
|
|
|
1556 if(m_hFontLink != NULL)
|
|
|
1557 hOldFont = dc.SelectFont(m_hFontLink);
|
|
|
1558 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
|
|
|
1559 DWORD dwStyle = this->GetStyle();
|
|
|
1560 UINT uFormat = DT_LEFT;
|
|
|
1561 if (dwStyle & SS_CENTER)
|
|
|
1562 uFormat = DT_CENTER;
|
|
|
1563 else if (dwStyle & SS_RIGHT)
|
|
|
1564 uFormat = DT_RIGHT;
|
|
|
1565 uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
|
|
|
1566 dc.DrawText(lpstrText, -1, &m_rcLink, uFormat | DT_CALCRECT);
|
|
|
1567 if(m_hFontLink != NULL)
|
|
|
1568 dc.SelectFont(hOldFont);
|
|
|
1569 if (dwStyle & SS_CENTER)
|
|
|
1570 {
|
|
|
1571 int dx = (rcClient.right - m_rcLink.right) / 2;
|
|
|
1572 ::OffsetRect(&m_rcLink, dx, 0);
|
|
|
1573 }
|
|
|
1574 else if (dwStyle & SS_RIGHT)
|
|
|
1575 {
|
|
|
1576 int dx = rcClient.right - m_rcLink.right;
|
|
|
1577 ::OffsetRect(&m_rcLink, dx, 0);
|
|
|
1578 }
|
|
|
1579 }
|
|
|
1580
|
|
|
1581 return true;
|
|
|
1582 }
|
|
|
1583
|
|
|
1584 void CalcLabelParts(LPTSTR& lpstrLeft, int& cchLeft, LPTSTR& lpstrLink, int& cchLink, LPTSTR& lpstrRight, int& cchRight) const
|
|
|
1585 {
|
|
|
1586 lpstrLeft = NULL;
|
|
|
1587 cchLeft = 0;
|
|
|
1588 lpstrLink = NULL;
|
|
|
1589 cchLink = 0;
|
|
|
1590 lpstrRight = NULL;
|
|
|
1591 cchRight = 0;
|
|
|
1592
|
|
|
1593 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
|
|
|
1594 int cchText = lstrlen(lpstrText);
|
|
|
1595 bool bOutsideLink = true;
|
|
|
1596 for(int i = 0; i < cchText; i++)
|
|
|
1597 {
|
|
|
1598 if(lpstrText[i] != _T('<'))
|
|
|
1599 continue;
|
|
|
1600
|
|
|
1601 if(bOutsideLink)
|
|
|
1602 {
|
|
|
1603 if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 3, _T("<A>"), 3) == CSTR_EQUAL)
|
|
|
1604 {
|
|
|
1605 if(i > 0)
|
|
|
1606 {
|
|
|
1607 lpstrLeft = lpstrText;
|
|
|
1608 cchLeft = i;
|
|
|
1609 }
|
|
|
1610 lpstrLink = &lpstrText[i + 3];
|
|
|
1611 bOutsideLink = false;
|
|
|
1612 }
|
|
|
1613 }
|
|
|
1614 else
|
|
|
1615 {
|
|
|
1616 if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 4, _T("</A>"), 4) == CSTR_EQUAL)
|
|
|
1617 {
|
|
|
1618 cchLink = i - 3 - cchLeft;
|
|
|
1619 if(lpstrText[i + 4] != 0)
|
|
|
1620 {
|
|
|
1621 lpstrRight = &lpstrText[i + 4];
|
|
|
1622 cchRight = cchText - (i + 4);
|
|
|
1623 break;
|
|
|
1624 }
|
|
|
1625 }
|
|
|
1626 }
|
|
|
1627 }
|
|
|
1628
|
|
|
1629 }
|
|
|
1630
|
|
|
1631 void DoEraseBackground(CDCHandle dc)
|
|
|
1632 {
|
|
|
1633 HBRUSH hBrush = (HBRUSH)this->GetParent().SendMessage(WM_CTLCOLORSTATIC, (WPARAM)dc.m_hDC, (LPARAM)this->m_hWnd);
|
|
|
1634 if(hBrush != NULL)
|
|
|
1635 {
|
|
|
1636 RECT rect = {};
|
|
|
1637 this->GetClientRect(&rect);
|
|
|
1638 dc.FillRect(&rect, hBrush);
|
|
|
1639 }
|
|
|
1640 }
|
|
|
1641
|
|
|
1642 void DoPaint(CDCHandle dc)
|
|
|
1643 {
|
|
|
1644 if(IsUsingTags())
|
|
|
1645 {
|
|
|
1646 // find tags and label parts
|
|
|
1647 LPTSTR lpstrLeft = NULL;
|
|
|
1648 int cchLeft = 0;
|
|
|
1649 LPTSTR lpstrLink = NULL;
|
|
|
1650 int cchLink = 0;
|
|
|
1651 LPTSTR lpstrRight = NULL;
|
|
|
1652 int cchRight = 0;
|
|
|
1653
|
|
|
1654 T* pT = static_cast<T*>(this);
|
|
|
1655 pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
|
|
|
1656
|
|
|
1657 // get label part rects
|
|
|
1658 RECT rcClient = {};
|
|
|
1659 this->GetClientRect(&rcClient);
|
|
|
1660
|
|
|
1661 dc.SetBkMode(TRANSPARENT);
|
|
|
1662 HFONT hFontOld = dc.SelectFont(m_hFontNormal);
|
|
|
1663
|
|
|
1664 UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
|
|
|
1665
|
|
|
1666 if(lpstrLeft != NULL)
|
|
|
1667 dc.DrawText(lpstrLeft, cchLeft, &rcClient, DT_LEFT | uFormat);
|
|
|
1668
|
|
|
1669 COLORREF clrOld = dc.SetTextColor(this->IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
|
|
|
1670 if((m_hFontLink != NULL) && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
|
|
|
1671 dc.SelectFont(m_hFontLink);
|
|
|
1672 else
|
|
|
1673 dc.SelectFont(m_hFontNormal);
|
|
|
1674
|
|
|
1675 dc.DrawText(lpstrLink, cchLink, &m_rcLink, DT_LEFT | uFormat);
|
|
|
1676
|
|
|
1677 dc.SetTextColor(clrOld);
|
|
|
1678 dc.SelectFont(m_hFontNormal);
|
|
|
1679 if(lpstrRight != NULL)
|
|
|
1680 {
|
|
|
1681 RECT rcRight = { m_rcLink.right, m_rcLink.top, rcClient.right, rcClient.bottom };
|
|
|
1682 dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat);
|
|
|
1683 }
|
|
|
1684
|
|
|
1685 if(GetFocus() == this->m_hWnd)
|
|
|
1686 dc.DrawFocusRect(&m_rcLink);
|
|
|
1687
|
|
|
1688 dc.SelectFont(hFontOld);
|
|
|
1689 }
|
|
|
1690 else
|
|
|
1691 {
|
|
|
1692 dc.SetBkMode(TRANSPARENT);
|
|
|
1693 COLORREF clrOld = dc.SetTextColor(this->IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
|
|
|
1694
|
|
|
1695 HFONT hFontOld = NULL;
|
|
|
1696 if((m_hFontLink != NULL) && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
|
|
|
1697 hFontOld = dc.SelectFont(m_hFontLink);
|
|
|
1698 else
|
|
|
1699 hFontOld = dc.SelectFont(m_hFontNormal);
|
|
|
1700
|
|
|
1701 LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
|
|
|
1702
|
|
|
1703 DWORD dwStyle = this->GetStyle();
|
|
|
1704 UINT uFormat = DT_LEFT;
|
|
|
1705 if (dwStyle & SS_CENTER)
|
|
|
1706 uFormat = DT_CENTER;
|
|
|
1707 else if (dwStyle & SS_RIGHT)
|
|
|
1708 uFormat = DT_RIGHT;
|
|
|
1709 uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
|
|
|
1710
|
|
|
1711 dc.DrawText(lpstrText, -1, &m_rcLink, uFormat);
|
|
|
1712
|
|
|
1713 if(GetFocus() == this->m_hWnd)
|
|
|
1714 dc.DrawFocusRect(&m_rcLink);
|
|
|
1715
|
|
|
1716 dc.SetTextColor(clrOld);
|
|
|
1717 dc.SelectFont(hFontOld);
|
|
|
1718 }
|
|
|
1719 }
|
|
|
1720
|
|
|
1721 BOOL StartTrackMouseLeave()
|
|
|
1722 {
|
|
|
1723 TRACKMOUSEEVENT tme = {};
|
|
|
1724 tme.cbSize = sizeof(tme);
|
|
|
1725 tme.dwFlags = TME_LEAVE;
|
|
|
1726 tme.hwndTrack = this->m_hWnd;
|
|
|
1727 return ::TrackMouseEvent(&tme);
|
|
|
1728 }
|
|
|
1729
|
|
|
1730 // Implementation helpers
|
|
|
1731 bool IsUnderlined() const
|
|
|
1732 {
|
|
|
1733 return ((m_dwExtendedStyle & (HLINK_NOTUNDERLINED | HLINK_UNDERLINEHOVER)) == 0);
|
|
|
1734 }
|
|
|
1735
|
|
|
1736 bool IsNotUnderlined() const
|
|
|
1737 {
|
|
|
1738 return ((m_dwExtendedStyle & HLINK_NOTUNDERLINED) != 0);
|
|
|
1739 }
|
|
|
1740
|
|
|
1741 bool IsUnderlineHover() const
|
|
|
1742 {
|
|
|
1743 return ((m_dwExtendedStyle & HLINK_UNDERLINEHOVER) != 0);
|
|
|
1744 }
|
|
|
1745
|
|
|
1746 bool IsCommandButton() const
|
|
|
1747 {
|
|
|
1748 return ((m_dwExtendedStyle & HLINK_COMMANDBUTTON) != 0);
|
|
|
1749 }
|
|
|
1750
|
|
|
1751 bool IsNotifyButton() const
|
|
|
1752 {
|
|
|
1753 return ((m_dwExtendedStyle & HLINK_NOTIFYBUTTON) == HLINK_NOTIFYBUTTON);
|
|
|
1754 }
|
|
|
1755
|
|
|
1756 bool IsUsingTags() const
|
|
|
1757 {
|
|
|
1758 return ((m_dwExtendedStyle & HLINK_USETAGS) != 0);
|
|
|
1759 }
|
|
|
1760
|
|
|
1761 bool IsUsingTagsBold() const
|
|
|
1762 {
|
|
|
1763 return ((m_dwExtendedStyle & HLINK_USETAGSBOLD) == HLINK_USETAGSBOLD);
|
|
|
1764 }
|
|
|
1765
|
|
|
1766 bool IsUsingToolTip() const
|
|
|
1767 {
|
|
|
1768 return ((m_dwExtendedStyle & HLINK_NOTOOLTIP) == 0);
|
|
|
1769 }
|
|
|
1770
|
|
|
1771 bool IsAutoCreateLinkFont() const
|
|
|
1772 {
|
|
|
1773 return ((m_dwExtendedStyle & HLINK_AUTOCREATELINKFONT) == HLINK_AUTOCREATELINKFONT);
|
|
|
1774 }
|
|
|
1775
|
|
|
1776 bool IsSingleLine() const
|
|
|
1777 {
|
|
|
1778 return ((m_dwExtendedStyle & HLINK_SINGLELINE) == HLINK_SINGLELINE);
|
|
|
1779 }
|
|
|
1780 };
|
|
|
1781
|
|
|
1782 class CHyperLink : public CHyperLinkImpl<CHyperLink>
|
|
|
1783 {
|
|
|
1784 public:
|
|
|
1785 DECLARE_WND_CLASS(_T("WTL_HyperLink"))
|
|
|
1786 };
|
|
|
1787
|
|
|
1788
|
|
|
1789 ///////////////////////////////////////////////////////////////////////////////
|
|
|
1790 // CWaitCursor - displays a wait cursor
|
|
|
1791
|
|
|
1792 class CWaitCursor
|
|
|
1793 {
|
|
|
1794 public:
|
|
|
1795 // Data
|
|
|
1796 HCURSOR m_hWaitCursor;
|
|
|
1797 HCURSOR m_hOldCursor;
|
|
|
1798 bool m_bInUse;
|
|
|
1799
|
|
|
1800 // Constructor/destructor
|
|
|
1801 CWaitCursor(bool bSet = true, LPCTSTR lpstrCursor = IDC_WAIT, bool bSys = true) : m_hOldCursor(NULL), m_bInUse(false)
|
|
|
1802 {
|
|
|
1803 HINSTANCE hInstance = bSys ? NULL : ModuleHelper::GetResourceInstance();
|
|
|
1804 m_hWaitCursor = ::LoadCursor(hInstance, lpstrCursor);
|
|
|
1805 ATLASSERT(m_hWaitCursor != NULL);
|
|
|
1806
|
|
|
1807 if(bSet)
|
|
|
1808 Set();
|
|
|
1809 }
|
|
|
1810
|
|
|
1811 ~CWaitCursor()
|
|
|
1812 {
|
|
|
1813 Restore();
|
|
|
1814 }
|
|
|
1815
|
|
|
1816 // Methods
|
|
|
1817 bool Set()
|
|
|
1818 {
|
|
|
1819 if(m_bInUse)
|
|
|
1820 return false;
|
|
|
1821 m_hOldCursor = ::SetCursor(m_hWaitCursor);
|
|
|
1822 m_bInUse = true;
|
|
|
1823 return true;
|
|
|
1824 }
|
|
|
1825
|
|
|
1826 bool Restore()
|
|
|
1827 {
|
|
|
1828 if(!m_bInUse)
|
|
|
1829 return false;
|
|
|
1830 ::SetCursor(m_hOldCursor);
|
|
|
1831 m_bInUse = false;
|
|
|
1832 return true;
|
|
|
1833 }
|
|
|
1834 };
|
|
|
1835
|
|
|
1836
|
|
|
1837 ///////////////////////////////////////////////////////////////////////////////
|
|
|
1838 // CCustomWaitCursor - for custom and animated cursors
|
|
|
1839
|
|
|
1840 class CCustomWaitCursor : public CWaitCursor
|
|
|
1841 {
|
|
|
1842 public:
|
|
|
1843 // Constructor/destructor
|
|
|
1844 CCustomWaitCursor(ATL::_U_STRINGorID cursor, bool bSet = true, HINSTANCE hInstance = NULL) :
|
|
|
1845 CWaitCursor(false, IDC_WAIT, true)
|
|
|
1846 {
|
|
|
1847 if(hInstance == NULL)
|
|
|
1848 hInstance = ModuleHelper::GetResourceInstance();
|
|
|
1849 m_hWaitCursor = (HCURSOR)::LoadImage(hInstance, cursor.m_lpstr, IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
|
|
|
1850
|
|
|
1851 if(bSet)
|
|
|
1852 Set();
|
|
|
1853 }
|
|
|
1854
|
|
|
1855 ~CCustomWaitCursor()
|
|
|
1856 {
|
|
|
1857 Restore();
|
|
|
1858 ::DestroyCursor(m_hWaitCursor);
|
|
|
1859 }
|
|
|
1860 };
|
|
|
1861
|
|
|
1862
|
|
|
1863 ///////////////////////////////////////////////////////////////////////////////
|
|
|
1864 // CMultiPaneStatusBarCtrl - Status Bar with multiple panes
|
|
|
1865
|
|
|
1866 template <class T, class TBase = CStatusBarCtrl>
|
|
|
1867 class ATL_NO_VTABLE CMultiPaneStatusBarCtrlImpl : public ATL::CWindowImpl< T, TBase >
|
|
|
1868 {
|
|
|
1869 public:
|
|
|
1870 DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName())
|
|
|
1871
|
|
|
1872 // Data
|
|
|
1873 enum { m_cxPaneMargin = 3 };
|
|
|
1874
|
|
|
1875 int m_nPanes;
|
|
|
1876 int* m_pPane;
|
|
|
1877
|
|
|
1878 // Constructor/destructor
|
|
|
1879 CMultiPaneStatusBarCtrlImpl() : m_nPanes(0), m_pPane(NULL)
|
|
|
1880 { }
|
|
|
1881
|
|
|
1882 ~CMultiPaneStatusBarCtrlImpl()
|
|
|
1883 {
|
|
|
1884 delete [] m_pPane;
|
|
|
1885 }
|
|
|
1886
|
|
|
1887 // Methods
|
|
|
1888 HWND Create(HWND hWndParent, LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
|
|
|
1889 {
|
|
|
1890 return ATL::CWindowImpl< T, TBase >::Create(hWndParent, this->rcDefault, lpstrText, dwStyle, 0, nID);
|
|
|
1891 }
|
|
|
1892
|
|
|
1893 HWND Create(HWND hWndParent, UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
|
|
|
1894 {
|
|
|
1895 const int cchMax = 128; // max text length is 127 for status bars (+1 for null)
|
|
|
1896 TCHAR szText[cchMax] = {};
|
|
|
1897 ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
|
|
|
1898 return Create(hWndParent, szText, dwStyle, nID);
|
|
|
1899 }
|
|
|
1900
|
|
|
1901 BOOL SetPanes(int* pPanes, int nPanes, bool bSetText = true)
|
|
|
1902 {
|
|
|
1903 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
1904 ATLASSERT(nPanes > 0);
|
|
|
1905
|
|
|
1906 m_nPanes = nPanes;
|
|
|
1907 delete [] m_pPane;
|
|
|
1908 m_pPane = NULL;
|
|
|
1909
|
|
|
1910 ATLTRY(m_pPane = new int[nPanes]);
|
|
|
1911 ATLASSERT(m_pPane != NULL);
|
|
|
1912 if(m_pPane == NULL)
|
|
|
1913 return FALSE;
|
|
|
1914
|
|
|
1915 ATL::CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
|
|
|
1916 int* pPanesPos = buff.Allocate(nPanes);
|
|
|
1917 ATLASSERT(pPanesPos != NULL);
|
|
|
1918 if(pPanesPos == NULL)
|
|
|
1919 return FALSE;
|
|
|
1920
|
|
|
1921 ATL::Checked::memcpy_s(m_pPane, nPanes * sizeof(int), pPanes, nPanes * sizeof(int));
|
|
|
1922
|
|
|
1923 // get status bar DC and set font
|
|
|
1924 CClientDC dc(this->m_hWnd);
|
|
|
1925 HFONT hOldFont = dc.SelectFont(this->GetFont());
|
|
|
1926
|
|
|
1927 // get status bar borders
|
|
|
1928 int arrBorders[3] = {};
|
|
|
1929 this->GetBorders(arrBorders);
|
|
|
1930
|
|
|
1931 const int cchBuff = 128;
|
|
|
1932 TCHAR szBuff[cchBuff] = {};
|
|
|
1933 int cxLeft = arrBorders[0];
|
|
|
1934
|
|
|
1935 // calculate right edge of each part
|
|
|
1936 for(int i = 0; i < nPanes; i++)
|
|
|
1937 {
|
|
|
1938 if(pPanes[i] == ID_DEFAULT_PANE)
|
|
|
1939 {
|
|
|
1940 // make very large, will be resized later
|
|
|
1941 pPanesPos[i] = INT_MAX / 2;
|
|
|
1942 }
|
|
|
1943 else
|
|
|
1944 {
|
|
|
1945 ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
|
|
|
1946 SIZE size = {};
|
|
|
1947 dc.GetTextExtent(szBuff, lstrlen(szBuff), &size);
|
|
|
1948 T* pT = static_cast<T*>(this);
|
|
|
1949 (void)pT; // avoid level 4 warning
|
|
|
1950 pPanesPos[i] = cxLeft + size.cx + arrBorders[2] + 2 * pT->m_cxPaneMargin;
|
|
|
1951 }
|
|
|
1952 cxLeft = pPanesPos[i];
|
|
|
1953 }
|
|
|
1954
|
|
|
1955 BOOL bRet = this->SetParts(nPanes, pPanesPos);
|
|
|
1956
|
|
|
1957 if(bRet && bSetText)
|
|
|
1958 {
|
|
|
1959 for(int i = 0; i < nPanes; i++)
|
|
|
1960 {
|
|
|
1961 if(pPanes[i] != ID_DEFAULT_PANE)
|
|
|
1962 {
|
|
|
1963 ::LoadString(ModuleHelper::GetResourceInstance(), pPanes[i], szBuff, cchBuff);
|
|
|
1964 SetPaneText(m_pPane[i], szBuff);
|
|
|
1965 }
|
|
|
1966 }
|
|
|
1967 }
|
|
|
1968
|
|
|
1969 dc.SelectFont(hOldFont);
|
|
|
1970 return bRet;
|
|
|
1971 }
|
|
|
1972
|
|
|
1973 bool GetPaneTextLength(int nPaneID, int* pcchLength = NULL, int* pnType = NULL) const
|
|
|
1974 {
|
|
|
1975 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
1976 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
1977 if(nIndex == -1)
|
|
|
1978 return false;
|
|
|
1979
|
|
|
1980 int nLength = this->GetTextLength(nIndex, pnType);
|
|
|
1981 if(pcchLength != NULL)
|
|
|
1982 *pcchLength = nLength;
|
|
|
1983
|
|
|
1984 return true;
|
|
|
1985 }
|
|
|
1986
|
|
|
1987 BOOL GetPaneText(int nPaneID, LPTSTR lpstrText, int* pcchLength = NULL, int* pnType = NULL) const
|
|
|
1988 {
|
|
|
1989 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
1990 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
1991 if(nIndex == -1)
|
|
|
1992 return FALSE;
|
|
|
1993
|
|
|
1994 int nLength = this->GetText(nIndex, lpstrText, pnType);
|
|
|
1995 if(pcchLength != NULL)
|
|
|
1996 *pcchLength = nLength;
|
|
|
1997
|
|
|
1998 return TRUE;
|
|
|
1999 }
|
|
|
2000
|
|
|
2001 #ifdef __ATLSTR_H__
|
|
|
2002 BOOL GetPaneText(int nPaneID, ATL::CString& strText, int* pcchLength = NULL, int* pnType = NULL) const
|
|
|
2003 {
|
|
|
2004 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2005 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
2006 if(nIndex == -1)
|
|
|
2007 return FALSE;
|
|
|
2008
|
|
|
2009 int nLength = this->GetText(nIndex, strText, pnType);
|
|
|
2010 if(pcchLength != NULL)
|
|
|
2011 *pcchLength = nLength;
|
|
|
2012
|
|
|
2013 return TRUE;
|
|
|
2014 }
|
|
|
2015 #endif // __ATLSTR_H__
|
|
|
2016
|
|
|
2017 BOOL SetPaneText(int nPaneID, LPCTSTR lpstrText, int nType = 0)
|
|
|
2018 {
|
|
|
2019 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2020 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
2021 if(nIndex == -1)
|
|
|
2022 return FALSE;
|
|
|
2023
|
|
|
2024 return this->SetText(nIndex, lpstrText, nType);
|
|
|
2025 }
|
|
|
2026
|
|
|
2027 BOOL GetPaneRect(int nPaneID, LPRECT lpRect) const
|
|
|
2028 {
|
|
|
2029 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2030 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
2031 if(nIndex == -1)
|
|
|
2032 return FALSE;
|
|
|
2033
|
|
|
2034 return this->GetRect(nIndex, lpRect);
|
|
|
2035 }
|
|
|
2036
|
|
|
2037 BOOL SetPaneWidth(int nPaneID, int cxWidth)
|
|
|
2038 {
|
|
|
2039 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2040 ATLASSERT(nPaneID != ID_DEFAULT_PANE); // Can't resize this one
|
|
|
2041 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
2042 if(nIndex == -1)
|
|
|
2043 return FALSE;
|
|
|
2044
|
|
|
2045 // get pane positions
|
|
|
2046 ATL::CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
|
|
|
2047 int* pPanesPos = buff.Allocate(m_nPanes);
|
|
|
2048 if(pPanesPos == NULL)
|
|
|
2049 return FALSE;
|
|
|
2050 this->GetParts(m_nPanes, pPanesPos);
|
|
|
2051 // calculate offset
|
|
|
2052 int cxPaneWidth = pPanesPos[nIndex] - ((nIndex == 0) ? 0 : pPanesPos[nIndex - 1]);
|
|
|
2053 int cxOff = cxWidth - cxPaneWidth;
|
|
|
2054 // find variable width pane
|
|
|
2055 int nDef = m_nPanes;
|
|
|
2056 for(int i = 0; i < m_nPanes; i++)
|
|
|
2057 {
|
|
|
2058 if(m_pPane[i] == ID_DEFAULT_PANE)
|
|
|
2059 {
|
|
|
2060 nDef = i;
|
|
|
2061 break;
|
|
|
2062 }
|
|
|
2063 }
|
|
|
2064 // resize
|
|
|
2065 if(nIndex < nDef) // before default pane
|
|
|
2066 {
|
|
|
2067 for(int i = nIndex; i < nDef; i++)
|
|
|
2068 pPanesPos[i] += cxOff;
|
|
|
2069
|
|
|
2070 }
|
|
|
2071 else // after default one
|
|
|
2072 {
|
|
|
2073 for(int i = nDef; i < nIndex; i++)
|
|
|
2074 pPanesPos[i] -= cxOff;
|
|
|
2075 }
|
|
|
2076 // set pane postions
|
|
|
2077 return this->SetParts(m_nPanes, pPanesPos);
|
|
|
2078 }
|
|
|
2079
|
|
|
2080 BOOL GetPaneTipText(int nPaneID, LPTSTR lpstrText, int nSize) const
|
|
|
2081 {
|
|
|
2082 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2083 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
2084 if(nIndex == -1)
|
|
|
2085 return FALSE;
|
|
|
2086
|
|
|
2087 this->GetTipText(nIndex, lpstrText, nSize);
|
|
|
2088 return TRUE;
|
|
|
2089 }
|
|
|
2090
|
|
|
2091 BOOL SetPaneTipText(int nPaneID, LPCTSTR lpstrText)
|
|
|
2092 {
|
|
|
2093 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2094 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
2095 if(nIndex == -1)
|
|
|
2096 return FALSE;
|
|
|
2097
|
|
|
2098 this->SetTipText(nIndex, lpstrText);
|
|
|
2099 return TRUE;
|
|
|
2100 }
|
|
|
2101
|
|
|
2102 BOOL GetPaneIcon(int nPaneID, HICON& hIcon) const
|
|
|
2103 {
|
|
|
2104 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2105 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
2106 if(nIndex == -1)
|
|
|
2107 return FALSE;
|
|
|
2108
|
|
|
2109 hIcon = this->GetIcon(nIndex);
|
|
|
2110 return TRUE;
|
|
|
2111 }
|
|
|
2112
|
|
|
2113 BOOL SetPaneIcon(int nPaneID, HICON hIcon)
|
|
|
2114 {
|
|
|
2115 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2116 int nIndex = GetPaneIndexFromID(nPaneID);
|
|
|
2117 if(nIndex == -1)
|
|
|
2118 return FALSE;
|
|
|
2119
|
|
|
2120 return this->SetIcon(nIndex, hIcon);
|
|
|
2121 }
|
|
|
2122
|
|
|
2123 // Message map and handlers
|
|
|
2124 BEGIN_MSG_MAP(CMultiPaneStatusBarCtrlImpl< T >)
|
|
|
2125 MESSAGE_HANDLER(WM_SIZE, OnSize)
|
|
|
2126 END_MSG_MAP()
|
|
|
2127
|
|
|
2128 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
2129 {
|
|
|
2130 LRESULT lRet = this->DefWindowProc(uMsg, wParam, lParam);
|
|
|
2131 if((wParam != SIZE_MINIMIZED) && (m_nPanes > 0))
|
|
|
2132 {
|
|
|
2133 T* pT = static_cast<T*>(this);
|
|
|
2134 pT->UpdatePanesLayout();
|
|
|
2135 }
|
|
|
2136 return lRet;
|
|
|
2137 }
|
|
|
2138
|
|
|
2139 // Implementation
|
|
|
2140 BOOL UpdatePanesLayout()
|
|
|
2141 {
|
|
|
2142 // get pane positions
|
|
|
2143 ATL::CTempBuffer<int, _WTL_STACK_ALLOC_THRESHOLD> buff;
|
|
|
2144 int* pPanesPos = buff.Allocate(m_nPanes);
|
|
|
2145 ATLASSERT(pPanesPos != NULL);
|
|
|
2146 if(pPanesPos == NULL)
|
|
|
2147 return FALSE;
|
|
|
2148 int nRet = this->GetParts(m_nPanes, pPanesPos);
|
|
|
2149 ATLASSERT(nRet == m_nPanes);
|
|
|
2150 if(nRet != m_nPanes)
|
|
|
2151 return FALSE;
|
|
|
2152 // calculate offset
|
|
|
2153 RECT rcClient = {};
|
|
|
2154 this->GetClientRect(&rcClient);
|
|
|
2155 int cxOff = rcClient.right - pPanesPos[m_nPanes - 1];
|
|
|
2156 // Move panes left if size grip box is present
|
|
|
2157 if((this->GetStyle() & SBARS_SIZEGRIP) != 0)
|
|
|
2158 cxOff -= ::GetSystemMetrics(SM_CXVSCROLL) + ::GetSystemMetrics(SM_CXEDGE);
|
|
|
2159 // find variable width pane
|
|
|
2160 int i;
|
|
|
2161 for(i = 0; i < m_nPanes; i++)
|
|
|
2162 {
|
|
|
2163 if(m_pPane[i] == ID_DEFAULT_PANE)
|
|
|
2164 break;
|
|
|
2165 }
|
|
|
2166 // resize all panes from the variable one to the right
|
|
|
2167 if((i < m_nPanes) && (pPanesPos[i] + cxOff) > ((i == 0) ? 0 : pPanesPos[i - 1]))
|
|
|
2168 {
|
|
|
2169 for(; i < m_nPanes; i++)
|
|
|
2170 pPanesPos[i] += cxOff;
|
|
|
2171 }
|
|
|
2172 // set pane postions
|
|
|
2173 return this->SetParts(m_nPanes, pPanesPos);
|
|
|
2174 }
|
|
|
2175
|
|
|
2176 int GetPaneIndexFromID(int nPaneID) const
|
|
|
2177 {
|
|
|
2178 for(int i = 0; i < m_nPanes; i++)
|
|
|
2179 {
|
|
|
2180 if(m_pPane[i] == nPaneID)
|
|
|
2181 return i;
|
|
|
2182 }
|
|
|
2183
|
|
|
2184 return -1; // not found
|
|
|
2185 }
|
|
|
2186 };
|
|
|
2187
|
|
|
2188 class CMultiPaneStatusBarCtrl : public CMultiPaneStatusBarCtrlImpl<CMultiPaneStatusBarCtrl>
|
|
|
2189 {
|
|
|
2190 public:
|
|
|
2191 DECLARE_WND_SUPERCLASS(_T("WTL_MultiPaneStatusBar"), GetWndClassName())
|
|
|
2192 };
|
|
|
2193
|
|
|
2194
|
|
|
2195 ///////////////////////////////////////////////////////////////////////////////
|
|
|
2196 // CPaneContainer - provides header with title and close button for panes
|
|
|
2197
|
|
|
2198 // pane container extended styles
|
|
|
2199 #define PANECNT_NOCLOSEBUTTON 0x00000001
|
|
|
2200 #define PANECNT_VERTICAL 0x00000002
|
|
|
2201 #define PANECNT_FLATBORDER 0x00000004
|
|
|
2202 #define PANECNT_NOBORDER 0x00000008
|
|
|
2203 #define PANECNT_DIVIDER 0x00000010
|
|
|
2204 #define PANECNT_GRADIENT 0x00000020
|
|
|
2205
|
|
|
2206 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
|
|
|
2207 class ATL_NO_VTABLE CPaneContainerImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CCustomDraw< T >
|
|
|
2208 {
|
|
|
2209 public:
|
|
|
2210 DECLARE_WND_CLASS_EX2(NULL, T, 0, -1)
|
|
|
2211
|
|
|
2212 // Constants
|
|
|
2213 enum
|
|
|
2214 {
|
|
|
2215 m_cxyBorder = 2,
|
|
|
2216 m_cxyTextOffset = 4,
|
|
|
2217 m_cxyBtnOffset = 1,
|
|
|
2218
|
|
|
2219 m_cchTitle = 80,
|
|
|
2220
|
|
|
2221 m_cxImageTB = 13,
|
|
|
2222 m_cyImageTB = 11,
|
|
|
2223 m_cxyBtnAddTB = 7,
|
|
|
2224
|
|
|
2225 m_cxToolBar = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + m_cxyBtnOffset,
|
|
|
2226
|
|
|
2227 m_xBtnImageLeft = 6,
|
|
|
2228 m_yBtnImageTop = 5,
|
|
|
2229 m_xBtnImageRight = 12,
|
|
|
2230 m_yBtnImageBottom = 11,
|
|
|
2231
|
|
|
2232 m_nCloseBtnID = ID_PANE_CLOSE
|
|
|
2233 };
|
|
|
2234
|
|
|
2235 // Data members
|
|
|
2236 CToolBarCtrl m_tb;
|
|
|
2237 ATL::CWindow m_wndClient;
|
|
|
2238 int m_cxyHeader;
|
|
|
2239 TCHAR m_szTitle[m_cchTitle];
|
|
|
2240 DWORD m_dwExtendedStyle; // Pane container specific extended styles
|
|
|
2241 HFONT m_hFont;
|
|
|
2242 bool m_bInternalFont;
|
|
|
2243
|
|
|
2244
|
|
|
2245 // Constructor
|
|
|
2246 CPaneContainerImpl() : m_cxyHeader(0), m_dwExtendedStyle(0), m_hFont(NULL), m_bInternalFont(false)
|
|
|
2247 {
|
|
|
2248 m_szTitle[0] = 0;
|
|
|
2249 }
|
|
|
2250
|
|
|
2251 // Attributes
|
|
|
2252 DWORD GetPaneContainerExtendedStyle() const
|
|
|
2253 {
|
|
|
2254 return m_dwExtendedStyle;
|
|
|
2255 }
|
|
|
2256
|
|
|
2257 DWORD SetPaneContainerExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
|
|
|
2258 {
|
|
|
2259 DWORD dwPrevStyle = m_dwExtendedStyle;
|
|
|
2260 if(dwMask == 0)
|
|
|
2261 m_dwExtendedStyle = dwExtendedStyle;
|
|
|
2262 else
|
|
|
2263 m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
|
|
|
2264 if(this->m_hWnd != NULL)
|
|
|
2265 {
|
|
|
2266 T* pT = static_cast<T*>(this);
|
|
|
2267 bool bUpdate = false;
|
|
|
2268
|
|
|
2269 if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) != 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)) // add close button
|
|
|
2270 {
|
|
|
2271 pT->CreateCloseButton();
|
|
|
2272 bUpdate = true;
|
|
|
2273 }
|
|
|
2274 else if(((dwPrevStyle & PANECNT_NOCLOSEBUTTON) == 0) && ((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) != 0)) // remove close button
|
|
|
2275 {
|
|
|
2276 pT->DestroyCloseButton();
|
|
|
2277 bUpdate = true;
|
|
|
2278 }
|
|
|
2279
|
|
|
2280 if((dwPrevStyle & PANECNT_VERTICAL) != (m_dwExtendedStyle & PANECNT_VERTICAL)) // change orientation
|
|
|
2281 {
|
|
|
2282 pT->CalcSize();
|
|
|
2283 bUpdate = true;
|
|
|
2284 }
|
|
|
2285
|
|
|
2286 if((dwPrevStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER)) !=
|
|
|
2287 (m_dwExtendedStyle & (PANECNT_FLATBORDER | PANECNT_NOBORDER))) // change border
|
|
|
2288 {
|
|
|
2289 bUpdate = true;
|
|
|
2290 }
|
|
|
2291
|
|
|
2292 if((dwPrevStyle & PANECNT_GRADIENT) != (m_dwExtendedStyle & PANECNT_GRADIENT)) // change background
|
|
|
2293 {
|
|
|
2294 bUpdate = true;
|
|
|
2295 }
|
|
|
2296
|
|
|
2297 if(bUpdate)
|
|
|
2298 pT->UpdateLayout();
|
|
|
2299 }
|
|
|
2300 return dwPrevStyle;
|
|
|
2301 }
|
|
|
2302
|
|
|
2303 HWND GetClient() const
|
|
|
2304 {
|
|
|
2305 return m_wndClient;
|
|
|
2306 }
|
|
|
2307
|
|
|
2308 HWND SetClient(HWND hWndClient)
|
|
|
2309 {
|
|
|
2310 HWND hWndOldClient = m_wndClient;
|
|
|
2311 m_wndClient = hWndClient;
|
|
|
2312 if(this->m_hWnd != NULL)
|
|
|
2313 {
|
|
|
2314 T* pT = static_cast<T*>(this);
|
|
|
2315 pT->UpdateLayout();
|
|
|
2316 }
|
|
|
2317 return hWndOldClient;
|
|
|
2318 }
|
|
|
2319
|
|
|
2320 BOOL GetTitle(LPTSTR lpstrTitle, int cchLength) const
|
|
|
2321 {
|
|
|
2322 ATLASSERT(lpstrTitle != NULL);
|
|
|
2323
|
|
|
2324 errno_t nRet = ATL::Checked::tcsncpy_s(lpstrTitle, cchLength, m_szTitle, _TRUNCATE);
|
|
|
2325
|
|
|
2326 return ((nRet == 0) || (nRet == STRUNCATE));
|
|
|
2327 }
|
|
|
2328
|
|
|
2329 BOOL SetTitle(LPCTSTR lpstrTitle)
|
|
|
2330 {
|
|
|
2331 ATLASSERT(lpstrTitle != NULL);
|
|
|
2332
|
|
|
2333 errno_t nRet = ATL::Checked::tcsncpy_s(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
|
|
|
2334 bool bRet = ((nRet == 0) || (nRet == STRUNCATE));
|
|
|
2335 if(bRet && (this->m_hWnd != NULL))
|
|
|
2336 {
|
|
|
2337 T* pT = static_cast<T*>(this);
|
|
|
2338 pT->UpdateLayout();
|
|
|
2339 }
|
|
|
2340
|
|
|
2341 return bRet;
|
|
|
2342 }
|
|
|
2343
|
|
|
2344 int GetTitleLength() const
|
|
|
2345 {
|
|
|
2346 return lstrlen(m_szTitle);
|
|
|
2347 }
|
|
|
2348
|
|
|
2349 // Methods
|
|
|
2350 HWND Create(HWND hWndParent, LPCTSTR lpstrTitle = NULL, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
|
|
2351 DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
|
|
|
2352 {
|
|
|
2353 if(lpstrTitle != NULL)
|
|
|
2354 ATL::Checked::tcsncpy_s(m_szTitle, m_cchTitle, lpstrTitle, _TRUNCATE);
|
|
|
2355 return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, this->rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
|
|
|
2356 }
|
|
|
2357
|
|
|
2358 HWND Create(HWND hWndParent, UINT uTitleID, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
|
|
|
2359 DWORD dwExStyle = 0, UINT nID = 0, LPVOID lpCreateParam = NULL)
|
|
|
2360 {
|
|
|
2361 if(uTitleID != 0U)
|
|
|
2362 ::LoadString(ModuleHelper::GetResourceInstance(), uTitleID, m_szTitle, m_cchTitle);
|
|
|
2363 return ATL::CWindowImpl< T, TBase, TWinTraits >::Create(hWndParent, this->rcDefault, NULL, dwStyle, dwExStyle, nID, lpCreateParam);
|
|
|
2364 }
|
|
|
2365
|
|
|
2366 BOOL SubclassWindow(HWND hWnd)
|
|
|
2367 {
|
|
|
2368 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
|
|
|
2369 if(bRet != FALSE)
|
|
|
2370 {
|
|
|
2371 T* pT = static_cast<T*>(this);
|
|
|
2372 pT->Init();
|
|
|
2373
|
|
|
2374 RECT rect = {};
|
|
|
2375 this->GetClientRect(&rect);
|
|
|
2376 pT->UpdateLayout(rect.right, rect.bottom);
|
|
|
2377 }
|
|
|
2378
|
|
|
2379 return bRet;
|
|
|
2380 }
|
|
|
2381
|
|
|
2382 BOOL EnableCloseButton(BOOL bEnable)
|
|
|
2383 {
|
|
|
2384 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2385 T* pT = static_cast<T*>(this);
|
|
|
2386 (void)pT; // avoid level 4 warning
|
|
|
2387 return (m_tb.m_hWnd != NULL) ? m_tb.EnableButton(pT->m_nCloseBtnID, bEnable) : FALSE;
|
|
|
2388 }
|
|
|
2389
|
|
|
2390 void UpdateLayout()
|
|
|
2391 {
|
|
|
2392 RECT rcClient = {};
|
|
|
2393 this->GetClientRect(&rcClient);
|
|
|
2394 T* pT = static_cast<T*>(this);
|
|
|
2395 pT->UpdateLayout(rcClient.right, rcClient.bottom);
|
|
|
2396 }
|
|
|
2397
|
|
|
2398 // Message map and handlers
|
|
|
2399 BEGIN_MSG_MAP(CPaneContainerImpl)
|
|
|
2400 MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
|
|
2401 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
|
|
|
2402 MESSAGE_HANDLER(WM_SIZE, OnSize)
|
|
|
2403 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
|
|
|
2404 MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
|
|
|
2405 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
|
|
|
2406 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
|
|
|
2407 MESSAGE_HANDLER(WM_PAINT, OnPaint)
|
|
|
2408 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
|
|
|
2409 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
|
|
|
2410 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
|
|
|
2411 FORWARD_NOTIFICATIONS()
|
|
|
2412 END_MSG_MAP()
|
|
|
2413
|
|
|
2414 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
2415 {
|
|
|
2416 T* pT = static_cast<T*>(this);
|
|
|
2417 pT->Init();
|
|
|
2418
|
|
|
2419 return 0;
|
|
|
2420 }
|
|
|
2421
|
|
|
2422 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
2423 {
|
|
|
2424 if(m_bInternalFont)
|
|
|
2425 {
|
|
|
2426 ::DeleteObject(m_hFont);
|
|
|
2427 m_hFont = NULL;
|
|
|
2428 m_bInternalFont = false;
|
|
|
2429 }
|
|
|
2430
|
|
|
2431 return 0;
|
|
|
2432 }
|
|
|
2433
|
|
|
2434 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
2435 {
|
|
|
2436 T* pT = static_cast<T*>(this);
|
|
|
2437 pT->UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
|
|
|
2438 return 0;
|
|
|
2439 }
|
|
|
2440
|
|
|
2441 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
2442 {
|
|
|
2443 if(m_wndClient.m_hWnd != NULL)
|
|
|
2444 m_wndClient.SetFocus();
|
|
|
2445 return 0;
|
|
|
2446 }
|
|
|
2447
|
|
|
2448 LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
2449 {
|
|
|
2450 return (LRESULT)m_hFont;
|
|
|
2451 }
|
|
|
2452
|
|
|
2453 LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
2454 {
|
|
|
2455 if(m_bInternalFont)
|
|
|
2456 {
|
|
|
2457 ::DeleteObject(m_hFont);
|
|
|
2458 m_bInternalFont = false;
|
|
|
2459 }
|
|
|
2460
|
|
|
2461 m_hFont = (HFONT)wParam;
|
|
|
2462
|
|
|
2463 T* pT = static_cast<T*>(this);
|
|
|
2464 pT->CalcSize();
|
|
|
2465
|
|
|
2466 if((BOOL)lParam != FALSE)
|
|
|
2467 pT->UpdateLayout();
|
|
|
2468
|
|
|
2469 return 0;
|
|
|
2470 }
|
|
|
2471
|
|
|
2472 LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
2473 {
|
|
|
2474 T* pT = static_cast<T*>(this);
|
|
|
2475 pT->DrawPaneTitleBackground((HDC)wParam);
|
|
|
2476
|
|
|
2477 return 1;
|
|
|
2478 }
|
|
|
2479
|
|
|
2480 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
2481 {
|
|
|
2482 T* pT = static_cast<T*>(this);
|
|
|
2483 if(wParam != NULL)
|
|
|
2484 {
|
|
|
2485 pT->DrawPaneTitle((HDC)wParam);
|
|
|
2486
|
|
|
2487 if(m_wndClient.m_hWnd == NULL) // no client window
|
|
|
2488 pT->DrawPane((HDC)wParam);
|
|
|
2489 }
|
|
|
2490 else
|
|
|
2491 {
|
|
|
2492 CPaintDC dc(this->m_hWnd);
|
|
|
2493 pT->DrawPaneTitle(dc.m_hDC);
|
|
|
2494
|
|
|
2495 if(m_wndClient.m_hWnd == NULL) // no client window
|
|
|
2496 pT->DrawPane(dc.m_hDC);
|
|
|
2497 }
|
|
|
2498
|
|
|
2499 return 0;
|
|
|
2500 }
|
|
|
2501
|
|
|
2502 LRESULT OnNotify(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
|
|
|
2503 {
|
|
|
2504 if(m_tb.m_hWnd == NULL)
|
|
|
2505 {
|
|
|
2506 bHandled = FALSE;
|
|
|
2507 return 1;
|
|
|
2508 }
|
|
|
2509
|
|
|
2510 T* pT = static_cast<T*>(this);
|
|
|
2511 (void)pT; // avoid level 4 warning
|
|
|
2512 LPNMHDR lpnmh = (LPNMHDR)lParam;
|
|
|
2513 LRESULT lRet = 0;
|
|
|
2514
|
|
|
2515 // pass toolbar custom draw notifications to the base class
|
|
|
2516 if((lpnmh->code == NM_CUSTOMDRAW) && (lpnmh->hwndFrom == m_tb.m_hWnd))
|
|
|
2517 lRet = CCustomDraw< T >::OnCustomDraw(0, lpnmh, bHandled);
|
|
|
2518 // tooltip notifications come with the tooltip window handle and button ID,
|
|
|
2519 // pass them to the parent if we don't handle them
|
|
|
2520 else if((lpnmh->code == TTN_GETDISPINFO) && (lpnmh->idFrom == pT->m_nCloseBtnID))
|
|
|
2521 bHandled = pT->GetToolTipText(lpnmh);
|
|
|
2522 // only let notifications not from the toolbar go to the parent
|
|
|
2523 else if((lpnmh->hwndFrom != m_tb.m_hWnd) && (lpnmh->idFrom != pT->m_nCloseBtnID))
|
|
|
2524 bHandled = FALSE;
|
|
|
2525
|
|
|
2526 return lRet;
|
|
|
2527 }
|
|
|
2528
|
|
|
2529 LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
2530 {
|
|
|
2531 // if command comes from the close button, substitute HWND of the pane container instead
|
|
|
2532 if((m_tb.m_hWnd != NULL) && ((HWND)lParam == m_tb.m_hWnd))
|
|
|
2533 return this->GetParent().SendMessage(WM_COMMAND, wParam, (LPARAM)this->m_hWnd);
|
|
|
2534
|
|
|
2535 bHandled = FALSE;
|
|
|
2536 return 1;
|
|
|
2537 }
|
|
|
2538
|
|
|
2539 // Custom draw overrides
|
|
|
2540 DWORD OnPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
|
|
|
2541 {
|
|
|
2542 return CDRF_NOTIFYITEMDRAW; // we need per-item notifications
|
|
|
2543 }
|
|
|
2544
|
|
|
2545 DWORD OnItemPrePaint(int /*idCtrl*/, LPNMCUSTOMDRAW /*lpNMCustomDraw*/)
|
|
|
2546 {
|
|
|
2547 return CDRF_NOTIFYPOSTPAINT;
|
|
|
2548 }
|
|
|
2549
|
|
|
2550 DWORD OnItemPostPaint(int /*idCtrl*/, LPNMCUSTOMDRAW lpNMCustomDraw)
|
|
|
2551 {
|
|
|
2552 CDCHandle dc = lpNMCustomDraw->hdc;
|
|
|
2553 RECT& rc = lpNMCustomDraw->rc;
|
|
|
2554
|
|
|
2555 RECT rcImage = { m_xBtnImageLeft, m_yBtnImageTop, m_xBtnImageRight + 1, m_yBtnImageBottom + 1 };
|
|
|
2556 ::OffsetRect(&rcImage, rc.left, rc.top);
|
|
|
2557 T* pT = static_cast<T*>(this);
|
|
|
2558
|
|
|
2559 if((lpNMCustomDraw->uItemState & CDIS_DISABLED) != 0)
|
|
|
2560 {
|
|
|
2561 RECT rcShadow = rcImage;
|
|
|
2562 ::OffsetRect(&rcShadow, 1, 1);
|
|
|
2563 CPen pen1;
|
|
|
2564 pen1.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DHILIGHT));
|
|
|
2565 pT->DrawButtonImage(dc, rcShadow, pen1);
|
|
|
2566 CPen pen2;
|
|
|
2567 pen2.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_3DSHADOW));
|
|
|
2568 pT->DrawButtonImage(dc, rcImage, pen2);
|
|
|
2569 }
|
|
|
2570 else
|
|
|
2571 {
|
|
|
2572 if((lpNMCustomDraw->uItemState & CDIS_SELECTED) != 0)
|
|
|
2573 ::OffsetRect(&rcImage, 1, 1);
|
|
|
2574 CPen pen;
|
|
|
2575 pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT));
|
|
|
2576 pT->DrawButtonImage(dc, rcImage, pen);
|
|
|
2577 }
|
|
|
2578
|
|
|
2579 return CDRF_DODEFAULT; // continue with the default item painting
|
|
|
2580 }
|
|
|
2581
|
|
|
2582 // Implementation - overrideable methods
|
|
|
2583 void Init()
|
|
|
2584 {
|
|
|
2585 if(m_hFont == NULL)
|
|
|
2586 {
|
|
|
2587 // The same as AtlCreateControlFont() for horizontal pane
|
|
|
2588 LOGFONT lf = {};
|
|
|
2589 ATLVERIFY(::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lf, 0) != FALSE);
|
|
|
2590 if(IsVertical())
|
|
|
2591 lf.lfEscapement = 900; // 90 degrees
|
|
|
2592 m_hFont = ::CreateFontIndirect(&lf);
|
|
|
2593 m_bInternalFont = true;
|
|
|
2594 }
|
|
|
2595
|
|
|
2596 T* pT = static_cast<T*>(this);
|
|
|
2597 pT->CalcSize();
|
|
|
2598
|
|
|
2599 if((m_dwExtendedStyle & PANECNT_NOCLOSEBUTTON) == 0)
|
|
|
2600 pT->CreateCloseButton();
|
|
|
2601 }
|
|
|
2602
|
|
|
2603 void UpdateLayout(int cxWidth, int cyHeight)
|
|
|
2604 {
|
|
|
2605 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
2606 RECT rect = {};
|
|
|
2607
|
|
|
2608 if(IsVertical())
|
|
|
2609 {
|
|
|
2610 ::SetRect(&rect, 0, 0, m_cxyHeader, cyHeight);
|
|
|
2611 if(m_tb.m_hWnd != NULL)
|
|
|
2612 m_tb.SetWindowPos(NULL, m_cxyBorder, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
|
2613
|
|
|
2614 if(m_wndClient.m_hWnd != NULL)
|
|
|
2615 m_wndClient.SetWindowPos(NULL, m_cxyHeader, 0, cxWidth - m_cxyHeader, cyHeight, SWP_NOZORDER);
|
|
|
2616 else
|
|
|
2617 rect.right = cxWidth;
|
|
|
2618 }
|
|
|
2619 else
|
|
|
2620 {
|
|
|
2621 ::SetRect(&rect, 0, 0, cxWidth, m_cxyHeader);
|
|
|
2622 if(m_tb.m_hWnd != NULL)
|
|
|
2623 m_tb.SetWindowPos(NULL, rect.right - m_cxToolBar, m_cxyBorder + m_cxyBtnOffset, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
|
2624
|
|
|
2625 if(m_wndClient.m_hWnd != NULL)
|
|
|
2626 m_wndClient.SetWindowPos(NULL, 0, m_cxyHeader, cxWidth, cyHeight - m_cxyHeader, SWP_NOZORDER);
|
|
|
2627 else
|
|
|
2628 rect.bottom = cyHeight;
|
|
|
2629 }
|
|
|
2630
|
|
|
2631 this->InvalidateRect(&rect);
|
|
|
2632 }
|
|
|
2633
|
|
|
2634 void CreateCloseButton()
|
|
|
2635 {
|
|
|
2636 ATLASSERT(m_tb.m_hWnd == NULL);
|
|
|
2637 // create toolbar for the "x" button
|
|
|
2638 m_tb.Create(this->m_hWnd, this->rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | CCS_NOMOVEY | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT, 0);
|
|
|
2639 ATLASSERT(m_tb.IsWindow());
|
|
|
2640
|
|
|
2641 if(m_tb.m_hWnd != NULL)
|
|
|
2642 {
|
|
|
2643 T* pT = static_cast<T*>(this);
|
|
|
2644 (void)pT; // avoid level 4 warning
|
|
|
2645
|
|
|
2646 m_tb.SetButtonStructSize();
|
|
|
2647
|
|
|
2648 TBBUTTON tbbtn = {};
|
|
|
2649 tbbtn.idCommand = pT->m_nCloseBtnID;
|
|
|
2650 tbbtn.fsState = TBSTATE_ENABLED;
|
|
|
2651 tbbtn.fsStyle = BTNS_BUTTON;
|
|
|
2652 m_tb.AddButtons(1, &tbbtn);
|
|
|
2653
|
|
|
2654 m_tb.SetBitmapSize(m_cxImageTB, m_cyImageTB);
|
|
|
2655 m_tb.SetButtonSize(m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB);
|
|
|
2656
|
|
|
2657 if(IsVertical())
|
|
|
2658 m_tb.SetWindowPos(NULL, m_cxyBorder + m_cxyBtnOffset, m_cxyBorder + m_cxyBtnOffset, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB + 1, SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
2659 else
|
|
|
2660 m_tb.SetWindowPos(NULL, 0, 0, m_cxImageTB + m_cxyBtnAddTB, m_cyImageTB + m_cxyBtnAddTB + 1, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
|
|
|
2661 }
|
|
|
2662 }
|
|
|
2663
|
|
|
2664 void DestroyCloseButton()
|
|
|
2665 {
|
|
|
2666 if(m_tb.m_hWnd != NULL)
|
|
|
2667 m_tb.DestroyWindow();
|
|
|
2668 }
|
|
|
2669
|
|
|
2670 void CalcSize()
|
|
|
2671 {
|
|
|
2672 T* pT = static_cast<T*>(this);
|
|
|
2673 CFontHandle font = pT->GetTitleFont();
|
|
|
2674 if(font.IsNull())
|
|
|
2675 font = (HFONT)::GetStockObject(SYSTEM_FONT);
|
|
|
2676 LOGFONT lf = {};
|
|
|
2677 font.GetLogFont(lf);
|
|
|
2678 if(IsVertical())
|
|
|
2679 {
|
|
|
2680 m_cxyHeader = m_cxImageTB + m_cxyBtnAddTB + m_cxyBorder + 1;
|
|
|
2681 }
|
|
|
2682 else
|
|
|
2683 {
|
|
|
2684 int cyFont = abs(lf.lfHeight) + m_cxyBorder + 2 * m_cxyTextOffset;
|
|
|
2685 int cyBtn = m_cyImageTB + m_cxyBtnAddTB + m_cxyBorder + 2 * m_cxyBtnOffset + 1;
|
|
|
2686 m_cxyHeader = __max(cyFont, cyBtn);
|
|
|
2687 }
|
|
|
2688 }
|
|
|
2689
|
|
|
2690 HFONT GetTitleFont() const
|
|
|
2691 {
|
|
|
2692 return m_hFont;
|
|
|
2693 }
|
|
|
2694
|
|
|
2695 BOOL GetToolTipText(LPNMHDR /*lpnmh*/)
|
|
|
2696 {
|
|
|
2697 return FALSE;
|
|
|
2698 }
|
|
|
2699
|
|
|
2700 void DrawPaneTitle(CDCHandle dc)
|
|
|
2701 {
|
|
|
2702 RECT rect = {};
|
|
|
2703 this->GetClientRect(&rect);
|
|
|
2704
|
|
|
2705 UINT uBorder = BF_LEFT | BF_TOP | BF_ADJUST;
|
|
|
2706 if(IsVertical())
|
|
|
2707 {
|
|
|
2708 rect.right = rect.left + m_cxyHeader;
|
|
|
2709 uBorder |= BF_BOTTOM;
|
|
|
2710 }
|
|
|
2711 else
|
|
|
2712 {
|
|
|
2713 rect.bottom = rect.top + m_cxyHeader;
|
|
|
2714 uBorder |= BF_RIGHT;
|
|
|
2715 }
|
|
|
2716
|
|
|
2717 if((m_dwExtendedStyle & PANECNT_NOBORDER) == 0)
|
|
|
2718 {
|
|
|
2719 if((m_dwExtendedStyle & PANECNT_FLATBORDER) != 0)
|
|
|
2720 uBorder |= BF_FLAT;
|
|
|
2721 dc.DrawEdge(&rect, EDGE_ETCHED, uBorder);
|
|
|
2722 }
|
|
|
2723
|
|
|
2724 if((m_dwExtendedStyle & PANECNT_DIVIDER) != 0)
|
|
|
2725 {
|
|
|
2726 uBorder = BF_FLAT | BF_ADJUST | (IsVertical() ? BF_RIGHT : BF_BOTTOM);
|
|
|
2727 dc.DrawEdge(&rect, BDR_SUNKENOUTER, uBorder);
|
|
|
2728 }
|
|
|
2729
|
|
|
2730 // draw title text
|
|
|
2731 dc.SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
|
|
|
2732 dc.SetBkMode(TRANSPARENT);
|
|
|
2733 T* pT = static_cast<T*>(this);
|
|
|
2734 HFONT hFontOld = dc.SelectFont(pT->GetTitleFont());
|
|
|
2735
|
|
|
2736 if(IsVertical())
|
|
|
2737 {
|
|
|
2738 rect.top += m_cxyTextOffset;
|
|
|
2739 rect.bottom -= m_cxyTextOffset;
|
|
|
2740 if(m_tb.m_hWnd != NULL)
|
|
|
2741 rect.top += m_cxToolBar;;
|
|
|
2742
|
|
|
2743 RECT rcCalc = { rect.left, rect.bottom, rect.right, rect.top };
|
|
|
2744 int cxFont = dc.DrawText(m_szTitle, -1, &rcCalc, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS | DT_CALCRECT);
|
|
|
2745 RECT rcText = {};
|
|
|
2746 rcText.left = (rect.right - rect.left - cxFont) / 2;
|
|
|
2747 rcText.right = rcText.left + (rect.bottom - rect.top);
|
|
|
2748 rcText.top = rect.bottom;
|
|
|
2749 rcText.bottom = rect.top;
|
|
|
2750 dc.DrawText(m_szTitle, -1, &rcText, DT_TOP | DT_SINGLELINE | DT_END_ELLIPSIS);
|
|
|
2751 }
|
|
|
2752 else
|
|
|
2753 {
|
|
|
2754 rect.left += m_cxyTextOffset;
|
|
|
2755 rect.right -= m_cxyTextOffset;
|
|
|
2756 if(m_tb.m_hWnd != NULL)
|
|
|
2757 rect.right -= m_cxToolBar;;
|
|
|
2758
|
|
|
2759 dc.DrawText(m_szTitle, -1, &rect, DT_LEFT | DT_SINGLELINE | DT_VCENTER | DT_END_ELLIPSIS);
|
|
|
2760 }
|
|
|
2761
|
|
|
2762 dc.SelectFont(hFontOld);
|
|
|
2763 }
|
|
|
2764
|
|
|
2765 void DrawPaneTitleBackground(CDCHandle dc)
|
|
|
2766 {
|
|
|
2767 RECT rect = {};
|
|
|
2768 this->GetClientRect(&rect);
|
|
|
2769 if(IsVertical())
|
|
|
2770 rect.right = m_cxyHeader;
|
|
|
2771 else
|
|
|
2772 rect.bottom = m_cxyHeader;
|
|
|
2773
|
|
|
2774 if((m_dwExtendedStyle & PANECNT_GRADIENT) != 0)
|
|
|
2775 dc.GradientFillRect(rect, ::GetSysColor(COLOR_WINDOW), ::GetSysColor(COLOR_3DFACE), IsVertical());
|
|
|
2776 else
|
|
|
2777 dc.FillRect(&rect, COLOR_3DFACE);
|
|
|
2778 }
|
|
|
2779
|
|
|
2780 // called only if pane is empty
|
|
|
2781 void DrawPane(CDCHandle dc)
|
|
|
2782 {
|
|
|
2783 RECT rect = {};
|
|
|
2784 this->GetClientRect(&rect);
|
|
|
2785 if(IsVertical())
|
|
|
2786 rect.left += m_cxyHeader;
|
|
|
2787 else
|
|
|
2788 rect.top += m_cxyHeader;
|
|
|
2789 if((this->GetExStyle() & WS_EX_CLIENTEDGE) == 0)
|
|
|
2790 dc.DrawEdge(&rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
|
|
|
2791 dc.FillRect(&rect, COLOR_APPWORKSPACE);
|
|
|
2792 }
|
|
|
2793
|
|
|
2794 // drawing helper - draws "x" button image
|
|
|
2795 void DrawButtonImage(CDCHandle dc, RECT& rcImage, HPEN hPen)
|
|
|
2796 {
|
|
|
2797 HPEN hPenOld = dc.SelectPen(hPen);
|
|
|
2798
|
|
|
2799 dc.MoveTo(rcImage.left, rcImage.top);
|
|
|
2800 dc.LineTo(rcImage.right, rcImage.bottom);
|
|
|
2801 dc.MoveTo(rcImage.left + 1, rcImage.top);
|
|
|
2802 dc.LineTo(rcImage.right + 1, rcImage.bottom);
|
|
|
2803
|
|
|
2804 dc.MoveTo(rcImage.left, rcImage.bottom - 1);
|
|
|
2805 dc.LineTo(rcImage.right, rcImage.top - 1);
|
|
|
2806 dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1);
|
|
|
2807 dc.LineTo(rcImage.right + 1, rcImage.top - 1);
|
|
|
2808
|
|
|
2809 dc.SelectPen(hPenOld);
|
|
|
2810 }
|
|
|
2811
|
|
|
2812 bool IsVertical() const
|
|
|
2813 {
|
|
|
2814 return ((m_dwExtendedStyle & PANECNT_VERTICAL) != 0);
|
|
|
2815 }
|
|
|
2816 };
|
|
|
2817
|
|
|
2818 class CPaneContainer : public CPaneContainerImpl<CPaneContainer>
|
|
|
2819 {
|
|
|
2820 public:
|
|
|
2821 DECLARE_WND_CLASS_EX(_T("WTL_PaneContainer"), 0, -1)
|
|
|
2822 };
|
|
|
2823
|
|
|
2824
|
|
|
2825 ///////////////////////////////////////////////////////////////////////////////
|
|
|
2826 // CSortListViewCtrl - implements sorting for a listview control
|
|
|
2827
|
|
|
2828 // sort listview extended styles
|
|
|
2829 #define SORTLV_USESHELLBITMAPS 0x00000001
|
|
|
2830
|
|
|
2831 // Notification sent to parent when sort column is changed by user clicking header.
|
|
|
2832 #define SLVN_SORTCHANGED LVN_LAST
|
|
|
2833
|
|
|
2834 // A LPNMSORTLISTVIEW is sent with the SLVN_SORTCHANGED notification
|
|
|
2835 typedef struct tagNMSORTLISTVIEW
|
|
|
2836 {
|
|
|
2837 NMHDR hdr;
|
|
|
2838 int iNewSortColumn;
|
|
|
2839 int iOldSortColumn;
|
|
|
2840 } NMSORTLISTVIEW, *LPNMSORTLISTVIEW;
|
|
|
2841
|
|
|
2842 // Column sort types. Can be set on a per-column basis with the SetColumnSortType method.
|
|
|
2843 enum
|
|
|
2844 {
|
|
|
2845 LVCOLSORT_NONE,
|
|
|
2846 LVCOLSORT_TEXT, // default
|
|
|
2847 LVCOLSORT_TEXTNOCASE,
|
|
|
2848 LVCOLSORT_LONG,
|
|
|
2849 LVCOLSORT_DOUBLE,
|
|
|
2850 LVCOLSORT_DECIMAL,
|
|
|
2851 LVCOLSORT_DATETIME,
|
|
|
2852 LVCOLSORT_DATE,
|
|
|
2853 LVCOLSORT_TIME,
|
|
|
2854 LVCOLSORT_CUSTOM,
|
|
|
2855 LVCOLSORT_LAST = LVCOLSORT_CUSTOM
|
|
|
2856 };
|
|
|
2857
|
|
|
2858
|
|
|
2859 template <class T>
|
|
|
2860 class CSortListViewImpl
|
|
|
2861 {
|
|
|
2862 public:
|
|
|
2863 enum
|
|
|
2864 {
|
|
|
2865 m_cchCmpTextMax = 32, // overrideable
|
|
|
2866 m_cxSortImage = 16,
|
|
|
2867 m_cySortImage = 15,
|
|
|
2868 m_cxSortArrow = 11,
|
|
|
2869 m_cySortArrow = 6,
|
|
|
2870 m_iSortUp = 0, // index of sort bitmaps
|
|
|
2871 m_iSortDown = 1,
|
|
|
2872 m_nShellSortUpID = 133
|
|
|
2873 };
|
|
|
2874
|
|
|
2875 // passed to LVCompare functions as lParam1 and lParam2
|
|
|
2876 struct LVCompareParam
|
|
|
2877 {
|
|
|
2878 int iItem;
|
|
|
2879 DWORD_PTR dwItemData;
|
|
|
2880 union
|
|
|
2881 {
|
|
|
2882 long lValue;
|
|
|
2883 double dblValue;
|
|
|
2884 DECIMAL decValue;
|
|
|
2885 LPCTSTR pszValue;
|
|
|
2886 };
|
|
|
2887 };
|
|
|
2888
|
|
|
2889 // passed to LVCompare functions as the lParamSort parameter
|
|
|
2890 struct LVSortInfo
|
|
|
2891 {
|
|
|
2892 T* pT;
|
|
|
2893 int iSortCol;
|
|
|
2894 bool bDescending;
|
|
|
2895 };
|
|
|
2896
|
|
|
2897 bool m_bSortDescending;
|
|
|
2898 bool m_bCommCtrl6;
|
|
|
2899 int m_iSortColumn;
|
|
|
2900 CBitmap m_bmSort[2];
|
|
|
2901 int m_fmtOldSortCol;
|
|
|
2902 HBITMAP m_hbmOldSortCol;
|
|
|
2903 DWORD m_dwSortLVExtendedStyle;
|
|
|
2904 ATL::CSimpleArray<WORD> m_arrColSortType;
|
|
|
2905 bool m_bUseWaitCursor;
|
|
|
2906
|
|
|
2907 CSortListViewImpl() :
|
|
|
2908 m_bSortDescending(false),
|
|
|
2909 m_bCommCtrl6(false),
|
|
|
2910 m_iSortColumn(-1),
|
|
|
2911 m_fmtOldSortCol(0),
|
|
|
2912 m_hbmOldSortCol(NULL),
|
|
|
2913 m_dwSortLVExtendedStyle(SORTLV_USESHELLBITMAPS),
|
|
|
2914 m_bUseWaitCursor(true)
|
|
|
2915 {
|
|
|
2916 DWORD dwMajor = 0;
|
|
|
2917 DWORD dwMinor = 0;
|
|
|
2918 HRESULT hRet = ATL::AtlGetCommCtrlVersion(&dwMajor, &dwMinor);
|
|
|
2919 m_bCommCtrl6 = SUCCEEDED(hRet) && (dwMajor >= 6);
|
|
|
2920 }
|
|
|
2921
|
|
|
2922 // Attributes
|
|
|
2923 void SetSortColumn(int iCol)
|
|
|
2924 {
|
|
|
2925 T* pT = static_cast<T*>(this);
|
|
|
2926 ATLASSERT(::IsWindow(pT->m_hWnd));
|
|
|
2927 CHeaderCtrl header = pT->GetHeader();
|
|
|
2928 ATLASSERT(header.m_hWnd != NULL);
|
|
|
2929 ATLASSERT((iCol >= -1) && (iCol < m_arrColSortType.GetSize()));
|
|
|
2930
|
|
|
2931 int iOldSortCol = m_iSortColumn;
|
|
|
2932 m_iSortColumn = iCol;
|
|
|
2933 if(m_bCommCtrl6)
|
|
|
2934 {
|
|
|
2935 const int nMask = HDF_SORTUP | HDF_SORTDOWN;
|
|
|
2936 HDITEM hditem = { HDI_FORMAT };
|
|
|
2937 if((iOldSortCol != iCol) && (iOldSortCol >= 0) && header.GetItem(iOldSortCol, &hditem))
|
|
|
2938 {
|
|
|
2939 hditem.fmt &= ~nMask;
|
|
|
2940 header.SetItem(iOldSortCol, &hditem);
|
|
|
2941 }
|
|
|
2942 if((iCol >= 0) && header.GetItem(iCol, &hditem))
|
|
|
2943 {
|
|
|
2944 hditem.fmt &= ~nMask;
|
|
|
2945 hditem.fmt |= m_bSortDescending ? HDF_SORTDOWN : HDF_SORTUP;
|
|
|
2946 header.SetItem(iCol, &hditem);
|
|
|
2947 }
|
|
|
2948 return;
|
|
|
2949 }
|
|
|
2950
|
|
|
2951 if(m_bmSort[m_iSortUp].IsNull())
|
|
|
2952 pT->CreateSortBitmaps();
|
|
|
2953
|
|
|
2954 // restore previous sort column's bitmap, if any, and format
|
|
|
2955 HDITEM hditem = { HDI_BITMAP | HDI_FORMAT };
|
|
|
2956 if((iOldSortCol != iCol) && (iOldSortCol >= 0))
|
|
|
2957 {
|
|
|
2958 hditem.hbm = m_hbmOldSortCol;
|
|
|
2959 hditem.fmt = m_fmtOldSortCol;
|
|
|
2960 header.SetItem(iOldSortCol, &hditem);
|
|
|
2961 }
|
|
|
2962
|
|
|
2963 // save new sort column's bitmap and format, and add our sort bitmap
|
|
|
2964 if((iCol >= 0) && header.GetItem(iCol, &hditem))
|
|
|
2965 {
|
|
|
2966 if(iOldSortCol != iCol)
|
|
|
2967 {
|
|
|
2968 m_fmtOldSortCol = hditem.fmt;
|
|
|
2969 m_hbmOldSortCol = hditem.hbm;
|
|
|
2970 }
|
|
|
2971 hditem.fmt &= ~HDF_IMAGE;
|
|
|
2972 hditem.fmt |= HDF_BITMAP | HDF_BITMAP_ON_RIGHT;
|
|
|
2973 int i = m_bSortDescending ? m_iSortDown : m_iSortUp;
|
|
|
2974 hditem.hbm = m_bmSort[i];
|
|
|
2975 header.SetItem(iCol, &hditem);
|
|
|
2976 }
|
|
|
2977 }
|
|
|
2978
|
|
|
2979 int GetSortColumn() const
|
|
|
2980 {
|
|
|
2981 return m_iSortColumn;
|
|
|
2982 }
|
|
|
2983
|
|
|
2984 void SetColumnSortType(int iCol, WORD wType)
|
|
|
2985 {
|
|
|
2986 ATLASSERT((iCol >= 0) && (iCol < m_arrColSortType.GetSize()));
|
|
|
2987 ATLASSERT((wType >= LVCOLSORT_NONE) && (wType <= LVCOLSORT_LAST));
|
|
|
2988 m_arrColSortType[iCol] = wType;
|
|
|
2989 }
|
|
|
2990
|
|
|
2991 WORD GetColumnSortType(int iCol) const
|
|
|
2992 {
|
|
|
2993 ATLASSERT((iCol >= 0) && (iCol < m_arrColSortType.GetSize()));
|
|
|
2994 return m_arrColSortType[iCol];
|
|
|
2995 }
|
|
|
2996
|
|
|
2997 int GetColumnCount() const
|
|
|
2998 {
|
|
|
2999 const T* pT = static_cast<const T*>(this);
|
|
|
3000 ATLASSERT(::IsWindow(pT->m_hWnd));
|
|
|
3001 CHeaderCtrl header = pT->GetHeader();
|
|
|
3002 return header.m_hWnd != NULL ? header.GetItemCount() : 0;
|
|
|
3003 }
|
|
|
3004
|
|
|
3005 bool IsSortDescending() const
|
|
|
3006 {
|
|
|
3007 return m_bSortDescending;
|
|
|
3008 }
|
|
|
3009
|
|
|
3010 DWORD GetSortListViewExtendedStyle() const
|
|
|
3011 {
|
|
|
3012 return m_dwSortLVExtendedStyle;
|
|
|
3013 }
|
|
|
3014
|
|
|
3015 DWORD SetSortListViewExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
|
|
|
3016 {
|
|
|
3017 DWORD dwPrevStyle = m_dwSortLVExtendedStyle;
|
|
|
3018 if(dwMask == 0)
|
|
|
3019 m_dwSortLVExtendedStyle = dwExtendedStyle;
|
|
|
3020 else
|
|
|
3021 m_dwSortLVExtendedStyle = (m_dwSortLVExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
|
|
|
3022 return dwPrevStyle;
|
|
|
3023 }
|
|
|
3024
|
|
|
3025 // Operations
|
|
|
3026 bool DoSortItems(int iCol, bool bDescending = false)
|
|
|
3027 {
|
|
|
3028 T* pT = static_cast<T*>(this);
|
|
|
3029 ATLASSERT(::IsWindow(pT->m_hWnd));
|
|
|
3030 ATLASSERT((iCol >= 0) && (iCol < m_arrColSortType.GetSize()));
|
|
|
3031
|
|
|
3032 WORD wType = m_arrColSortType[iCol];
|
|
|
3033 if(wType == LVCOLSORT_NONE)
|
|
|
3034 return false;
|
|
|
3035
|
|
|
3036 int nCount = pT->GetItemCount();
|
|
|
3037 if(nCount < 2)
|
|
|
3038 {
|
|
|
3039 m_bSortDescending = bDescending;
|
|
|
3040 SetSortColumn(iCol);
|
|
|
3041 return true;
|
|
|
3042 }
|
|
|
3043
|
|
|
3044 CWaitCursor waitCursor(false);
|
|
|
3045 if(m_bUseWaitCursor)
|
|
|
3046 waitCursor.Set();
|
|
|
3047
|
|
|
3048 LVCompareParam* pParam = NULL;
|
|
|
3049 ATLTRY(pParam = new LVCompareParam[nCount]);
|
|
|
3050 PFNLVCOMPARE pFunc = NULL;
|
|
|
3051 TCHAR pszTemp[pT->m_cchCmpTextMax] = {};
|
|
|
3052 bool bStrValue = false;
|
|
|
3053
|
|
|
3054 switch(wType)
|
|
|
3055 {
|
|
|
3056 case LVCOLSORT_TEXT:
|
|
|
3057 pFunc = (PFNLVCOMPARE)pT->LVCompareText;
|
|
|
3058 case LVCOLSORT_TEXTNOCASE:
|
|
|
3059 if(pFunc == NULL)
|
|
|
3060 pFunc = (PFNLVCOMPARE)pT->LVCompareTextNoCase;
|
|
|
3061 case LVCOLSORT_CUSTOM:
|
|
|
3062 {
|
|
|
3063 if(pFunc == NULL)
|
|
|
3064 pFunc = (PFNLVCOMPARE)pT->LVCompareCustom;
|
|
|
3065
|
|
|
3066 for(int i = 0; i < nCount; i++)
|
|
|
3067 {
|
|
|
3068 pParam[i].iItem = i;
|
|
|
3069 pParam[i].dwItemData = pT->GetItemData(i);
|
|
|
3070 pParam[i].pszValue = new TCHAR[pT->m_cchCmpTextMax];
|
|
|
3071 pT->GetItemText(i, iCol, (LPTSTR)pParam[i].pszValue, pT->m_cchCmpTextMax);
|
|
|
3072 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
|
|
|
3073 }
|
|
|
3074 bStrValue = true;
|
|
|
3075 }
|
|
|
3076 break;
|
|
|
3077 case LVCOLSORT_LONG:
|
|
|
3078 {
|
|
|
3079 pFunc = (PFNLVCOMPARE)pT->LVCompareLong;
|
|
|
3080 for(int i = 0; i < nCount; i++)
|
|
|
3081 {
|
|
|
3082 pParam[i].iItem = i;
|
|
|
3083 pParam[i].dwItemData = pT->GetItemData(i);
|
|
|
3084 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
|
|
|
3085 pParam[i].lValue = pT->StrToLong(pszTemp);
|
|
|
3086 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
|
|
|
3087 }
|
|
|
3088 }
|
|
|
3089 break;
|
|
|
3090 case LVCOLSORT_DOUBLE:
|
|
|
3091 {
|
|
|
3092 pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
|
|
|
3093 for(int i = 0; i < nCount; i++)
|
|
|
3094 {
|
|
|
3095 pParam[i].iItem = i;
|
|
|
3096 pParam[i].dwItemData = pT->GetItemData(i);
|
|
|
3097 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
|
|
|
3098 pParam[i].dblValue = pT->StrToDouble(pszTemp);
|
|
|
3099 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
|
|
|
3100 }
|
|
|
3101 }
|
|
|
3102 break;
|
|
|
3103 case LVCOLSORT_DECIMAL:
|
|
|
3104 {
|
|
|
3105 pFunc = (PFNLVCOMPARE)pT->LVCompareDecimal;
|
|
|
3106 for(int i = 0; i < nCount; i++)
|
|
|
3107 {
|
|
|
3108 pParam[i].iItem = i;
|
|
|
3109 pParam[i].dwItemData = pT->GetItemData(i);
|
|
|
3110 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
|
|
|
3111 pT->StrToDecimal(pszTemp, &pParam[i].decValue);
|
|
|
3112 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
|
|
|
3113 }
|
|
|
3114 }
|
|
|
3115 break;
|
|
|
3116 case LVCOLSORT_DATETIME:
|
|
|
3117 case LVCOLSORT_DATE:
|
|
|
3118 case LVCOLSORT_TIME:
|
|
|
3119 {
|
|
|
3120 pFunc = (PFNLVCOMPARE)pT->LVCompareDouble;
|
|
|
3121 DWORD dwFlags = LOCALE_NOUSEROVERRIDE;
|
|
|
3122 if(wType == LVCOLSORT_DATE)
|
|
|
3123 dwFlags |= VAR_DATEVALUEONLY;
|
|
|
3124 else if(wType == LVCOLSORT_TIME)
|
|
|
3125 dwFlags |= VAR_TIMEVALUEONLY;
|
|
|
3126 for(int i = 0; i < nCount; i++)
|
|
|
3127 {
|
|
|
3128 pParam[i].iItem = i;
|
|
|
3129 pParam[i].dwItemData = pT->GetItemData(i);
|
|
|
3130 pT->GetItemText(i, iCol, pszTemp, pT->m_cchCmpTextMax);
|
|
|
3131 pParam[i].dblValue = pT->DateStrToDouble(pszTemp, dwFlags);
|
|
|
3132 pT->SetItemData(i, (DWORD_PTR)&pParam[i]);
|
|
|
3133 }
|
|
|
3134 }
|
|
|
3135 break;
|
|
|
3136 default:
|
|
|
3137 ATLTRACE2(atlTraceUI, 0, _T("Unknown value for sort type in CSortListViewImpl::DoSortItems()\n"));
|
|
|
3138 break;
|
|
|
3139 } // switch(wType)
|
|
|
3140
|
|
|
3141 ATLASSERT(pFunc != NULL);
|
|
|
3142 LVSortInfo lvsi = { pT, iCol, bDescending };
|
|
|
3143 bool bRet = ((BOOL)pT->DefWindowProc(LVM_SORTITEMS, (WPARAM)&lvsi, (LPARAM)pFunc) != FALSE);
|
|
|
3144 for(int i = 0; i < nCount; i++)
|
|
|
3145 {
|
|
|
3146 DWORD_PTR dwItemData = pT->GetItemData(i);
|
|
|
3147 LVCompareParam* p = (LVCompareParam*)dwItemData;
|
|
|
3148 ATLASSERT(p != NULL);
|
|
|
3149 if(bStrValue)
|
|
|
3150 delete [] (TCHAR*)p->pszValue;
|
|
|
3151 pT->SetItemData(i, p->dwItemData);
|
|
|
3152 }
|
|
|
3153 delete [] pParam;
|
|
|
3154
|
|
|
3155 if(bRet)
|
|
|
3156 {
|
|
|
3157 m_bSortDescending = bDescending;
|
|
|
3158 SetSortColumn(iCol);
|
|
|
3159 }
|
|
|
3160
|
|
|
3161 if(m_bUseWaitCursor)
|
|
|
3162 waitCursor.Restore();
|
|
|
3163
|
|
|
3164 return bRet;
|
|
|
3165 }
|
|
|
3166
|
|
|
3167 void CreateSortBitmaps()
|
|
|
3168 {
|
|
|
3169 if((m_dwSortLVExtendedStyle & SORTLV_USESHELLBITMAPS) != 0)
|
|
|
3170 {
|
|
|
3171 bool bFree = false;
|
|
|
3172 LPCTSTR pszModule = _T("shell32.dll");
|
|
|
3173 HINSTANCE hShell = ::GetModuleHandle(pszModule);
|
|
|
3174
|
|
|
3175 if (hShell == NULL)
|
|
|
3176 {
|
|
|
3177 hShell = ::LoadLibrary(pszModule);
|
|
|
3178 bFree = true;
|
|
|
3179 }
|
|
|
3180
|
|
|
3181 if (hShell != NULL)
|
|
|
3182 {
|
|
|
3183 bool bSuccess = true;
|
|
|
3184 for(int i = m_iSortUp; i <= m_iSortDown; i++)
|
|
|
3185 {
|
|
|
3186 if(!m_bmSort[i].IsNull())
|
|
|
3187 m_bmSort[i].DeleteObject();
|
|
|
3188 m_bmSort[i] = (HBITMAP)::LoadImage(hShell, MAKEINTRESOURCE(m_nShellSortUpID + i),
|
|
|
3189 IMAGE_BITMAP, 0, 0, LR_LOADMAP3DCOLORS);
|
|
|
3190 if(m_bmSort[i].IsNull())
|
|
|
3191 {
|
|
|
3192 bSuccess = false;
|
|
|
3193 break;
|
|
|
3194 }
|
|
|
3195 }
|
|
|
3196 if(bFree)
|
|
|
3197 ::FreeLibrary(hShell);
|
|
|
3198 if(bSuccess)
|
|
|
3199 return;
|
|
|
3200 }
|
|
|
3201 }
|
|
|
3202
|
|
|
3203 T* pT = static_cast<T*>(this);
|
|
|
3204 for(int i = m_iSortUp; i <= m_iSortDown; i++)
|
|
|
3205 {
|
|
|
3206 if(!m_bmSort[i].IsNull())
|
|
|
3207 m_bmSort[i].DeleteObject();
|
|
|
3208
|
|
|
3209 CDC dcMem;
|
|
|
3210 CClientDC dc(::GetDesktopWindow());
|
|
|
3211 dcMem.CreateCompatibleDC(dc.m_hDC);
|
|
|
3212 m_bmSort[i].CreateCompatibleBitmap(dc.m_hDC, m_cxSortImage, m_cySortImage);
|
|
|
3213 HBITMAP hbmOld = dcMem.SelectBitmap(m_bmSort[i]);
|
|
|
3214 RECT rc = { 0, 0, m_cxSortImage, m_cySortImage };
|
|
|
3215 pT->DrawSortBitmap(dcMem.m_hDC, i, &rc);
|
|
|
3216 dcMem.SelectBitmap(hbmOld);
|
|
|
3217 dcMem.DeleteDC();
|
|
|
3218 }
|
|
|
3219 }
|
|
|
3220
|
|
|
3221 void NotifyParentSortChanged(int iNewSortCol, int iOldSortCol)
|
|
|
3222 {
|
|
|
3223 T* pT = static_cast<T*>(this);
|
|
|
3224 int nID = pT->GetDlgCtrlID();
|
|
|
3225 NMSORTLISTVIEW nm = { { pT->m_hWnd, (UINT_PTR)nID, SLVN_SORTCHANGED }, iNewSortCol, iOldSortCol };
|
|
|
3226 ::SendMessage(pT->GetParent(), WM_NOTIFY, (WPARAM)nID, (LPARAM)&nm);
|
|
|
3227 }
|
|
|
3228
|
|
|
3229 // Overrideables
|
|
|
3230 int CompareItemsCustom(LVCompareParam* /*pItem1*/, LVCompareParam* /*pItem2*/, int /*iSortCol*/)
|
|
|
3231 {
|
|
|
3232 // pItem1 and pItem2 contain valid iItem, dwItemData, and pszValue members.
|
|
|
3233 // If item1 > item2 return 1, if item1 < item2 return -1, else return 0.
|
|
|
3234 return 0;
|
|
|
3235 }
|
|
|
3236
|
|
|
3237 void DrawSortBitmap(CDCHandle dc, int iBitmap, LPRECT prc)
|
|
|
3238 {
|
|
|
3239 dc.FillRect(prc, ::GetSysColorBrush(COLOR_BTNFACE));
|
|
|
3240 HBRUSH hbrOld = dc.SelectBrush(::GetSysColorBrush(COLOR_BTNSHADOW));
|
|
|
3241 CPen pen;
|
|
|
3242 pen.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNSHADOW));
|
|
|
3243 HPEN hpenOld = dc.SelectPen(pen);
|
|
|
3244 POINT ptOrg = { (m_cxSortImage - m_cxSortArrow) / 2, (m_cySortImage - m_cySortArrow) / 2 };
|
|
|
3245 if(iBitmap == m_iSortUp)
|
|
|
3246 {
|
|
|
3247 POINT pts[3] =
|
|
|
3248 {
|
|
|
3249 { ptOrg.x + m_cxSortArrow / 2, ptOrg.y },
|
|
|
3250 { ptOrg.x, ptOrg.y + m_cySortArrow - 1 },
|
|
|
3251 { ptOrg.x + m_cxSortArrow - 1, ptOrg.y + m_cySortArrow - 1 }
|
|
|
3252 };
|
|
|
3253 dc.Polygon(pts, 3);
|
|
|
3254 }
|
|
|
3255 else
|
|
|
3256 {
|
|
|
3257 POINT pts[3] =
|
|
|
3258 {
|
|
|
3259 { ptOrg.x, ptOrg.y },
|
|
|
3260 { ptOrg.x + m_cxSortArrow / 2, ptOrg.y + m_cySortArrow - 1 },
|
|
|
3261 { ptOrg.x + m_cxSortArrow - 1, ptOrg.y }
|
|
|
3262 };
|
|
|
3263 dc.Polygon(pts, 3);
|
|
|
3264 }
|
|
|
3265 dc.SelectBrush(hbrOld);
|
|
|
3266 dc.SelectPen(hpenOld);
|
|
|
3267 }
|
|
|
3268
|
|
|
3269 double DateStrToDouble(LPCTSTR lpstr, DWORD dwFlags)
|
|
|
3270 {
|
|
|
3271 ATLASSERT(lpstr != NULL);
|
|
|
3272 if((lpstr == NULL) || (lpstr[0] == _T('\0')))
|
|
|
3273 return 0;
|
|
|
3274
|
|
|
3275 USES_CONVERSION;
|
|
|
3276 HRESULT hRet = E_FAIL;
|
|
|
3277 DATE dRet = 0;
|
|
|
3278 if (FAILED(hRet = ::VarDateFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, dwFlags, &dRet)))
|
|
|
3279 {
|
|
|
3280 ATLTRACE2(atlTraceUI, 0, _T("VarDateFromStr failed with result of 0x%8.8X\n"), hRet);
|
|
|
3281 dRet = 0;
|
|
|
3282 }
|
|
|
3283 return dRet;
|
|
|
3284 }
|
|
|
3285
|
|
|
3286 long StrToLong(LPCTSTR lpstr)
|
|
|
3287 {
|
|
|
3288 ATLASSERT(lpstr != NULL);
|
|
|
3289 if((lpstr == NULL) || (lpstr[0] == _T('\0')))
|
|
|
3290 return 0;
|
|
|
3291
|
|
|
3292 USES_CONVERSION;
|
|
|
3293 HRESULT hRet = E_FAIL;
|
|
|
3294 long lRet = 0;
|
|
|
3295 if (FAILED(hRet = ::VarI4FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &lRet)))
|
|
|
3296 {
|
|
|
3297 ATLTRACE2(atlTraceUI, 0, _T("VarI4FromStr failed with result of 0x%8.8X\n"), hRet);
|
|
|
3298 lRet = 0;
|
|
|
3299 }
|
|
|
3300 return lRet;
|
|
|
3301 }
|
|
|
3302
|
|
|
3303 double StrToDouble(LPCTSTR lpstr)
|
|
|
3304 {
|
|
|
3305 ATLASSERT(lpstr != NULL);
|
|
|
3306 if((lpstr == NULL) || (lpstr[0] == _T('\0')))
|
|
|
3307 return 0;
|
|
|
3308
|
|
|
3309 USES_CONVERSION;
|
|
|
3310 HRESULT hRet = E_FAIL;
|
|
|
3311 double dblRet = 0;
|
|
|
3312 if (FAILED(hRet = ::VarR8FromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, &dblRet)))
|
|
|
3313 {
|
|
|
3314 ATLTRACE2(atlTraceUI, 0, _T("VarR8FromStr failed with result of 0x%8.8X\n"), hRet);
|
|
|
3315 dblRet = 0;
|
|
|
3316 }
|
|
|
3317 return dblRet;
|
|
|
3318 }
|
|
|
3319
|
|
|
3320 bool StrToDecimal(LPCTSTR lpstr, DECIMAL* pDecimal)
|
|
|
3321 {
|
|
|
3322 ATLASSERT(lpstr != NULL);
|
|
|
3323 ATLASSERT(pDecimal != NULL);
|
|
|
3324 if((lpstr == NULL) || (pDecimal == NULL))
|
|
|
3325 return false;
|
|
|
3326
|
|
|
3327 USES_CONVERSION;
|
|
|
3328 HRESULT hRet = E_FAIL;
|
|
|
3329 if (FAILED(hRet = ::VarDecFromStr((LPOLESTR)T2COLE(lpstr), LANG_USER_DEFAULT, LOCALE_NOUSEROVERRIDE, pDecimal)))
|
|
|
3330 {
|
|
|
3331 ATLTRACE2(atlTraceUI, 0, _T("VarDecFromStr failed with result of 0x%8.8X\n"), hRet);
|
|
|
3332 pDecimal->Lo64 = 0;
|
|
|
3333 pDecimal->Hi32 = 0;
|
|
|
3334 pDecimal->signscale = 0;
|
|
|
3335 return false;
|
|
|
3336 }
|
|
|
3337 return true;
|
|
|
3338 }
|
|
|
3339
|
|
|
3340 // Overrideable PFNLVCOMPARE functions
|
|
|
3341 static int CALLBACK LVCompareText(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
|
|
3342 {
|
|
|
3343 ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
|
|
|
3344
|
|
|
3345 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
|
|
|
3346 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
|
|
|
3347 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
|
|
|
3348
|
|
|
3349 int nRet = lstrcmp(pParam1->pszValue, pParam2->pszValue);
|
|
|
3350 return pInfo->bDescending ? -nRet : nRet;
|
|
|
3351 }
|
|
|
3352
|
|
|
3353 static int CALLBACK LVCompareTextNoCase(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
|
|
3354 {
|
|
|
3355 ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
|
|
|
3356
|
|
|
3357 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
|
|
|
3358 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
|
|
|
3359 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
|
|
|
3360
|
|
|
3361 int nRet = lstrcmpi(pParam1->pszValue, pParam2->pszValue);
|
|
|
3362 return pInfo->bDescending ? -nRet : nRet;
|
|
|
3363 }
|
|
|
3364
|
|
|
3365 static int CALLBACK LVCompareLong(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
|
|
3366 {
|
|
|
3367 ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
|
|
|
3368
|
|
|
3369 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
|
|
|
3370 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
|
|
|
3371 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
|
|
|
3372
|
|
|
3373 int nRet = 0;
|
|
|
3374 if(pParam1->lValue > pParam2->lValue)
|
|
|
3375 nRet = 1;
|
|
|
3376 else if(pParam1->lValue < pParam2->lValue)
|
|
|
3377 nRet = -1;
|
|
|
3378 return pInfo->bDescending ? -nRet : nRet;
|
|
|
3379 }
|
|
|
3380
|
|
|
3381 static int CALLBACK LVCompareDouble(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
|
|
3382 {
|
|
|
3383 ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
|
|
|
3384
|
|
|
3385 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
|
|
|
3386 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
|
|
|
3387 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
|
|
|
3388
|
|
|
3389 int nRet = 0;
|
|
|
3390 if(pParam1->dblValue > pParam2->dblValue)
|
|
|
3391 nRet = 1;
|
|
|
3392 else if(pParam1->dblValue < pParam2->dblValue)
|
|
|
3393 nRet = -1;
|
|
|
3394 return pInfo->bDescending ? -nRet : nRet;
|
|
|
3395 }
|
|
|
3396
|
|
|
3397 static int CALLBACK LVCompareCustom(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
|
|
3398 {
|
|
|
3399 ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
|
|
|
3400
|
|
|
3401 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
|
|
|
3402 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
|
|
|
3403 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
|
|
|
3404
|
|
|
3405 int nRet = pInfo->pT->CompareItemsCustom(pParam1, pParam2, pInfo->iSortCol);
|
|
|
3406 return pInfo->bDescending ? -nRet : nRet;
|
|
|
3407 }
|
|
|
3408
|
|
|
3409 static int CALLBACK LVCompareDecimal(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
|
|
|
3410 {
|
|
|
3411 ATLASSERT((lParam1 != NULL) && (lParam2 != NULL) && (lParamSort != NULL));
|
|
|
3412
|
|
|
3413 LVCompareParam* pParam1 = (LVCompareParam*)lParam1;
|
|
|
3414 LVCompareParam* pParam2 = (LVCompareParam*)lParam2;
|
|
|
3415 LVSortInfo* pInfo = (LVSortInfo*)lParamSort;
|
|
|
3416
|
|
|
3417 int nRet = (int)::VarDecCmp(&pParam1->decValue, &pParam2->decValue);
|
|
|
3418 nRet--;
|
|
|
3419 return pInfo->bDescending ? -nRet : nRet;
|
|
|
3420 }
|
|
|
3421
|
|
|
3422 BEGIN_MSG_MAP(CSortListViewImpl)
|
|
|
3423 MESSAGE_HANDLER(LVM_INSERTCOLUMN, OnInsertColumn)
|
|
|
3424 MESSAGE_HANDLER(LVM_DELETECOLUMN, OnDeleteColumn)
|
|
|
3425 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, OnHeaderItemClick)
|
|
|
3426 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, OnHeaderItemClick)
|
|
|
3427 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
|
|
|
3428 END_MSG_MAP()
|
|
|
3429
|
|
|
3430 LRESULT OnInsertColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
3431 {
|
|
|
3432 T* pT = static_cast<T*>(this);
|
|
|
3433 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
|
|
|
3434 if(lRet == -1)
|
|
|
3435 return -1;
|
|
|
3436
|
|
|
3437 WORD wType = 0;
|
|
|
3438 m_arrColSortType.Add(wType);
|
|
|
3439 int nCount = m_arrColSortType.GetSize();
|
|
|
3440 ATLASSERT(nCount == GetColumnCount());
|
|
|
3441
|
|
|
3442 for(int i = nCount - 1; i > lRet; i--)
|
|
|
3443 m_arrColSortType[i] = m_arrColSortType[i - 1];
|
|
|
3444 m_arrColSortType[(int)lRet] = LVCOLSORT_TEXT;
|
|
|
3445
|
|
|
3446 if(lRet <= m_iSortColumn)
|
|
|
3447 m_iSortColumn++;
|
|
|
3448
|
|
|
3449 return lRet;
|
|
|
3450 }
|
|
|
3451
|
|
|
3452 LRESULT OnDeleteColumn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
3453 {
|
|
|
3454 T* pT = static_cast<T*>(this);
|
|
|
3455 LRESULT lRet = pT->DefWindowProc(uMsg, wParam, lParam);
|
|
|
3456 if(lRet == 0)
|
|
|
3457 return 0;
|
|
|
3458
|
|
|
3459 int iCol = (int)wParam;
|
|
|
3460 if(m_iSortColumn == iCol)
|
|
|
3461 m_iSortColumn = -1;
|
|
|
3462 else if(m_iSortColumn > iCol)
|
|
|
3463 m_iSortColumn--;
|
|
|
3464 m_arrColSortType.RemoveAt(iCol);
|
|
|
3465
|
|
|
3466 return lRet;
|
|
|
3467 }
|
|
|
3468
|
|
|
3469 LRESULT OnHeaderItemClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
|
|
|
3470 {
|
|
|
3471 LPNMHEADER p = (LPNMHEADER)pnmh;
|
|
|
3472 if(p->iButton == 0)
|
|
|
3473 {
|
|
|
3474 int iOld = m_iSortColumn;
|
|
|
3475 bool bDescending = (m_iSortColumn == p->iItem) ? !m_bSortDescending : false;
|
|
|
3476 if(DoSortItems(p->iItem, bDescending))
|
|
|
3477 NotifyParentSortChanged(p->iItem, iOld);
|
|
|
3478 }
|
|
|
3479 bHandled = FALSE;
|
|
|
3480 return 0;
|
|
|
3481 }
|
|
|
3482
|
|
|
3483 LRESULT OnSettingChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
3484 {
|
|
|
3485 if(wParam == SPI_SETNONCLIENTMETRICS)
|
|
|
3486 GetSystemSettings();
|
|
|
3487 bHandled = FALSE;
|
|
|
3488 return 0;
|
|
|
3489 }
|
|
|
3490
|
|
|
3491 void GetSystemSettings()
|
|
|
3492 {
|
|
|
3493 if(!m_bCommCtrl6 && !m_bmSort[m_iSortUp].IsNull())
|
|
|
3494 {
|
|
|
3495 T* pT = static_cast<T*>(this);
|
|
|
3496 pT->CreateSortBitmaps();
|
|
|
3497 if(m_iSortColumn != -1)
|
|
|
3498 SetSortColumn(m_iSortColumn);
|
|
|
3499 }
|
|
|
3500 }
|
|
|
3501
|
|
|
3502 };
|
|
|
3503
|
|
|
3504
|
|
|
3505 typedef ATL::CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | LVS_REPORT | LVS_SHOWSELALWAYS , WS_EX_CLIENTEDGE> CSortListViewCtrlTraits;
|
|
|
3506
|
|
|
3507 template <class T, class TBase = CListViewCtrl, class TWinTraits = CSortListViewCtrlTraits>
|
|
|
3508 class ATL_NO_VTABLE CSortListViewCtrlImpl: public ATL::CWindowImpl<T, TBase, TWinTraits>, public CSortListViewImpl<T>
|
|
|
3509 {
|
|
|
3510 public:
|
|
|
3511 DECLARE_WND_SUPERCLASS2(NULL, T, TBase::GetWndClassName())
|
|
|
3512
|
|
|
3513 bool SortItems(int iCol, bool bDescending = false)
|
|
|
3514 {
|
|
|
3515 return this->DoSortItems(iCol, bDescending);
|
|
|
3516 }
|
|
|
3517
|
|
|
3518 BEGIN_MSG_MAP(CSortListViewCtrlImpl)
|
|
|
3519 MESSAGE_HANDLER(LVM_INSERTCOLUMN, CSortListViewImpl<T>::OnInsertColumn)
|
|
|
3520 MESSAGE_HANDLER(LVM_DELETECOLUMN, CSortListViewImpl<T>::OnDeleteColumn)
|
|
|
3521 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKA, CSortListViewImpl<T>::OnHeaderItemClick)
|
|
|
3522 NOTIFY_CODE_HANDLER(HDN_ITEMCLICKW, CSortListViewImpl<T>::OnHeaderItemClick)
|
|
|
3523 MESSAGE_HANDLER(WM_SETTINGCHANGE, CSortListViewImpl<T>::OnSettingChange)
|
|
|
3524 END_MSG_MAP()
|
|
|
3525 };
|
|
|
3526
|
|
|
3527 class CSortListViewCtrl : public CSortListViewCtrlImpl<CSortListViewCtrl>
|
|
|
3528 {
|
|
|
3529 public:
|
|
|
3530 DECLARE_WND_SUPERCLASS(_T("WTL_SortListViewCtrl"), GetWndClassName())
|
|
|
3531 };
|
|
|
3532
|
|
|
3533
|
|
|
3534 ///////////////////////////////////////////////////////////////////////////////
|
|
|
3535 // CTabView - implements tab view window
|
|
|
3536
|
|
|
3537 // TabView Notifications
|
|
|
3538 #define TBVN_PAGEACTIVATED (0U-741)
|
|
|
3539 #define TBVN_CONTEXTMENU (0U-742)
|
|
|
3540 #define TBVN_TABCLOSEBTN (0U-743) // return 0 to close page, 1 to keep open
|
|
|
3541 // internal
|
|
|
3542 #define TBVN_CLOSEBTNMOUSELEAVE (0U-744)
|
|
|
3543
|
|
|
3544 // Notification data for TBVN_CONTEXTMENU
|
|
|
3545 struct TBVCONTEXTMENUINFO
|
|
|
3546 {
|
|
|
3547 NMHDR hdr;
|
|
|
3548 POINT pt;
|
|
|
3549 };
|
|
|
3550
|
|
|
3551 typedef TBVCONTEXTMENUINFO* LPTBVCONTEXTMENUINFO;
|
|
|
3552
|
|
|
3553
|
|
|
3554 // Helper class for tab item hover close button
|
|
|
3555 class CTabViewCloseBtn : public ATL::CWindowImpl<CTabViewCloseBtn>
|
|
|
3556 {
|
|
|
3557 public:
|
|
|
3558 DECLARE_WND_CLASS_EX(_T("WTL_TabView_CloseBtn"), 0, -1)
|
|
|
3559
|
|
|
3560 enum { _xyBtnImageLeftTop = 3, _xyBtnImageRightBottom = 9 };
|
|
|
3561
|
|
|
3562 bool m_bHover;
|
|
|
3563 bool m_bPressed;
|
|
|
3564 CToolTipCtrl m_tip;
|
|
|
3565
|
|
|
3566 CTabViewCloseBtn() : m_bHover(false), m_bPressed(false)
|
|
|
3567 { }
|
|
|
3568
|
|
|
3569 // Message map and handlers
|
|
|
3570 BEGIN_MSG_MAP(CTabViewCloseBtn)
|
|
|
3571 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
|
|
|
3572 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
|
|
|
3573 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
|
|
|
3574 MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
|
|
|
3575 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
|
|
|
3576 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
|
|
|
3577 MESSAGE_HANDLER(WM_PAINT, OnPaint)
|
|
|
3578 MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
|
|
|
3579 FORWARD_NOTIFICATIONS()
|
|
|
3580 END_MSG_MAP()
|
|
|
3581
|
|
|
3582 LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
3583 {
|
|
|
3584 MSG msg = { m_hWnd, uMsg, wParam, lParam };
|
|
|
3585 if(m_tip.IsWindow() != FALSE)
|
|
|
3586 m_tip.RelayEvent(&msg);
|
|
|
3587
|
|
|
3588 bHandled = FALSE;
|
|
|
3589 return 1;
|
|
|
3590 }
|
|
|
3591
|
|
|
3592 LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
3593 {
|
|
|
3594 SetCapture();
|
|
|
3595 m_bHover = false;
|
|
|
3596 m_bPressed = true;
|
|
|
3597 Invalidate(FALSE);
|
|
|
3598 UpdateWindow();
|
|
|
3599
|
|
|
3600 return 0;
|
|
|
3601 }
|
|
|
3602
|
|
|
3603 LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
3604 {
|
|
|
3605 if(::GetCapture() == m_hWnd)
|
|
|
3606 {
|
|
|
3607 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
3608 ClientToScreen(&pt);
|
|
|
3609 RECT rect = {};
|
|
|
3610 GetWindowRect(&rect);
|
|
|
3611 bool bPressed = (::PtInRect(&rect, pt) != FALSE);
|
|
|
3612 if(m_bPressed != bPressed)
|
|
|
3613 {
|
|
|
3614 m_bPressed = bPressed;
|
|
|
3615 Invalidate(FALSE);
|
|
|
3616 UpdateWindow();
|
|
|
3617 }
|
|
|
3618 }
|
|
|
3619 else
|
|
|
3620 {
|
|
|
3621 if(!m_bHover)
|
|
|
3622 {
|
|
|
3623 m_bHover = true;
|
|
|
3624 Invalidate(FALSE);
|
|
|
3625 UpdateWindow();
|
|
|
3626 }
|
|
|
3627
|
|
|
3628 TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd };
|
|
|
3629 ::TrackMouseEvent(&tme);
|
|
|
3630 }
|
|
|
3631
|
|
|
3632 return 0;
|
|
|
3633 }
|
|
|
3634
|
|
|
3635 LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
3636 {
|
|
|
3637 if(m_bHover)
|
|
|
3638 {
|
|
|
3639 m_bHover = false;
|
|
|
3640 Invalidate(FALSE);
|
|
|
3641 UpdateWindow();
|
|
|
3642 }
|
|
|
3643
|
|
|
3644 NMHDR nmhdr = { m_hWnd, (UINT_PTR)GetDlgCtrlID(), TBVN_CLOSEBTNMOUSELEAVE };
|
|
|
3645 GetParent().SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
|
|
|
3646
|
|
|
3647 return 0;
|
|
|
3648 }
|
|
|
3649
|
|
|
3650 LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
3651 {
|
|
|
3652 if(::GetCapture() == m_hWnd)
|
|
|
3653 {
|
|
|
3654 bool bAction = m_bPressed;
|
|
|
3655 ReleaseCapture();
|
|
|
3656
|
|
|
3657 if(bAction)
|
|
|
3658 GetParent().SendMessage(WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
|
|
|
3659 }
|
|
|
3660
|
|
|
3661 return 0;
|
|
|
3662 }
|
|
|
3663
|
|
|
3664 LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
3665 {
|
|
|
3666 if(m_bPressed)
|
|
|
3667 {
|
|
|
3668 m_bPressed = false;
|
|
|
3669 Invalidate(FALSE);
|
|
|
3670 UpdateWindow();
|
|
|
3671 }
|
|
|
3672
|
|
|
3673 return 0;
|
|
|
3674 }
|
|
|
3675
|
|
|
3676 LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
3677 {
|
|
|
3678 if(wParam != NULL)
|
|
|
3679 {
|
|
|
3680 DoPaint((HDC)wParam);
|
|
|
3681 }
|
|
|
3682 else
|
|
|
3683 {
|
|
|
3684 CPaintDC dc(this->m_hWnd);
|
|
|
3685 DoPaint(dc.m_hDC);
|
|
|
3686 }
|
|
|
3687
|
|
|
3688 return 0;
|
|
|
3689 }
|
|
|
3690
|
|
|
3691 // painting helper
|
|
|
3692 void DoPaint(CDCHandle dc)
|
|
|
3693 {
|
|
|
3694 RECT rect = {};
|
|
|
3695 GetClientRect(&rect);
|
|
|
3696
|
|
|
3697 RECT rcImage = { _xyBtnImageLeftTop, _xyBtnImageLeftTop, _xyBtnImageRightBottom + 1, _xyBtnImageRightBottom + 1 };
|
|
|
3698 ::OffsetRect(&rcImage, rect.left, rect.top);
|
|
|
3699 if(m_bPressed)
|
|
|
3700 ::OffsetRect(&rcImage, 1, 0);
|
|
|
3701
|
|
|
3702 // draw button frame and background
|
|
|
3703 CPen penFrame;
|
|
|
3704 penFrame.CreatePen(PS_SOLID, 0, ::GetSysColor((m_bHover || m_bPressed) ? COLOR_BTNTEXT : COLOR_BTNSHADOW));
|
|
|
3705 HPEN hPenOld = dc.SelectPen(penFrame);
|
|
|
3706
|
|
|
3707 CBrush brush;
|
|
|
3708 brush.CreateSysColorBrush(m_bPressed ? COLOR_BTNSHADOW : COLOR_WINDOW);
|
|
|
3709 HBRUSH hBrushOld = dc.SelectBrush(brush);
|
|
|
3710
|
|
|
3711 dc.Rectangle(&rect);
|
|
|
3712
|
|
|
3713 // draw button "X"
|
|
|
3714 CPen penX;
|
|
|
3715 penX.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_BTNTEXT));
|
|
|
3716 dc.SelectPen(penX);
|
|
|
3717
|
|
|
3718 dc.MoveTo(rcImage.left, rcImage.top);
|
|
|
3719 dc.LineTo(rcImage.right, rcImage.bottom);
|
|
|
3720 dc.MoveTo(rcImage.left + 1, rcImage.top);
|
|
|
3721 dc.LineTo(rcImage.right + 1, rcImage.bottom);
|
|
|
3722
|
|
|
3723 dc.MoveTo(rcImage.left, rcImage.bottom - 1);
|
|
|
3724 dc.LineTo(rcImage.right, rcImage.top - 1);
|
|
|
3725 dc.MoveTo(rcImage.left + 1, rcImage.bottom - 1);
|
|
|
3726 dc.LineTo(rcImage.right + 1, rcImage.top - 1);
|
|
|
3727
|
|
|
3728 dc.SelectPen(hPenOld);
|
|
|
3729 dc.SelectBrush(hBrushOld);
|
|
|
3730 }
|
|
|
3731 };
|
|
|
3732
|
|
|
3733
|
|
|
3734 template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
|
|
|
3735 class ATL_NO_VTABLE CTabViewImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
|
|
|
3736 {
|
|
|
3737 public:
|
|
|
3738 DECLARE_WND_CLASS_EX2(NULL, T, 0, COLOR_APPWORKSPACE)
|
|
|
3739
|
|
|
3740 // Declarations and enums
|
|
|
3741 struct TABVIEWPAGE
|
|
|
3742 {
|
|
|
3743 HWND hWnd;
|
|
|
3744 LPTSTR lpstrTitle;
|
|
|
3745 LPVOID pData;
|
|
|
3746 };
|
|
|
3747
|
|
|
3748 struct TCITEMEXTRA
|
|
|
3749 {
|
|
|
3750 TCITEMHEADER tciheader;
|
|
|
3751 TABVIEWPAGE tvpage;
|
|
|
3752
|
|
|
3753 operator LPTCITEM() { return (LPTCITEM)this; }
|
|
|
3754 };
|
|
|
3755
|
|
|
3756 enum
|
|
|
3757 {
|
|
|
3758 m_nTabID = 1313,
|
|
|
3759 m_cxMoveMark = 6,
|
|
|
3760 m_cyMoveMark = 3,
|
|
|
3761 m_nMenuItemsMax = (ID_WINDOW_TABLAST - ID_WINDOW_TABFIRST + 1)
|
|
|
3762 };
|
|
|
3763
|
|
|
3764 enum { _nAutoScrollTimerID = 4321 };
|
|
|
3765
|
|
|
3766 enum AutoScroll
|
|
|
3767 {
|
|
|
3768 _AUTOSCROLL_NONE = 0,
|
|
|
3769 _AUTOSCROLL_LEFT = -1,
|
|
|
3770 _AUTOSCROLL_RIGHT = 1
|
|
|
3771 };
|
|
|
3772
|
|
|
3773 enum CloseBtn
|
|
|
3774 {
|
|
|
3775 _cxCloseBtn = 14,
|
|
|
3776 _cyCloseBtn = 13,
|
|
|
3777 _cxCloseBtnMargin = 4,
|
|
|
3778 _cxCloseBtnMarginSel = 1,
|
|
|
3779
|
|
|
3780 _nCloseBtnID = ID_PANE_CLOSE
|
|
|
3781 };
|
|
|
3782
|
|
|
3783 // Data members
|
|
|
3784 ATL::CContainedWindowT<CTabCtrl> m_tab;
|
|
|
3785 int m_cyTabHeight;
|
|
|
3786
|
|
|
3787 int m_nActivePage;
|
|
|
3788
|
|
|
3789 int m_nInsertItem;
|
|
|
3790 POINT m_ptStartDrag;
|
|
|
3791
|
|
|
3792 CMenuHandle m_menu;
|
|
|
3793
|
|
|
3794 int m_cchTabTextLength;
|
|
|
3795
|
|
|
3796 int m_nMenuItemsCount;
|
|
|
3797
|
|
|
3798 ATL::CWindow m_wndTitleBar;
|
|
|
3799 LPTSTR m_lpstrTitleBarBase;
|
|
|
3800 int m_cchTitleBarLength;
|
|
|
3801
|
|
|
3802 CImageList m_ilDrag;
|
|
|
3803
|
|
|
3804 AutoScroll m_AutoScroll;
|
|
|
3805 CUpDownCtrl m_ud;
|
|
|
3806
|
|
|
3807 CTabViewCloseBtn m_btnClose;
|
|
|
3808 int m_nCloseItem;
|
|
|
3809
|
|
|
3810 bool m_bDestroyPageOnRemove:1;
|
|
|
3811 bool m_bDestroyImageList:1;
|
|
|
3812 bool m_bActivePageMenuItem:1;
|
|
|
3813 bool m_bActiveAsDefaultMenuItem:1;
|
|
|
3814 bool m_bEmptyMenuItem:1;
|
|
|
3815 bool m_bWindowsMenuItem:1;
|
|
|
3816 bool m_bNoTabDrag:1;
|
|
|
3817 bool m_bNoTabDragAutoScroll:1;
|
|
|
3818 bool m_bTabCloseButton:1;
|
|
|
3819 // internal
|
|
|
3820 bool m_bTabCapture:1;
|
|
|
3821 bool m_bTabDrag:1;
|
|
|
3822 bool m_bInternalFont:1;
|
|
|
3823
|
|
|
3824 // Constructor/destructor
|
|
|
3825 CTabViewImpl() :
|
|
|
3826 m_tab(this, 1),
|
|
|
3827 m_cyTabHeight(0),
|
|
|
3828 m_nActivePage(-1),
|
|
|
3829 m_nInsertItem(-1),
|
|
|
3830 m_cchTabTextLength(30),
|
|
|
3831 m_nMenuItemsCount(10),
|
|
|
3832 m_lpstrTitleBarBase(NULL),
|
|
|
3833 m_cchTitleBarLength(100),
|
|
|
3834 m_AutoScroll(_AUTOSCROLL_NONE),
|
|
|
3835 m_nCloseItem(-1),
|
|
|
3836 m_bDestroyPageOnRemove(true),
|
|
|
3837 m_bDestroyImageList(true),
|
|
|
3838 m_bActivePageMenuItem(true),
|
|
|
3839 m_bActiveAsDefaultMenuItem(false),
|
|
|
3840 m_bEmptyMenuItem(false),
|
|
|
3841 m_bWindowsMenuItem(false),
|
|
|
3842 m_bNoTabDrag(false),
|
|
|
3843 m_bNoTabDragAutoScroll(false),
|
|
|
3844 m_bTabCloseButton(true),
|
|
|
3845 m_bTabCapture(false),
|
|
|
3846 m_bTabDrag(false),
|
|
|
3847 m_bInternalFont(false)
|
|
|
3848 {
|
|
|
3849 m_ptStartDrag.x = 0;
|
|
|
3850 m_ptStartDrag.y = 0;
|
|
|
3851 }
|
|
|
3852
|
|
|
3853 ~CTabViewImpl()
|
|
|
3854 {
|
|
|
3855 delete [] m_lpstrTitleBarBase;
|
|
|
3856 }
|
|
|
3857
|
|
|
3858 // Message filter function - to be called from PreTranslateMessage of the main window
|
|
|
3859 BOOL PreTranslateMessage(MSG* pMsg)
|
|
|
3860 {
|
|
|
3861 if(this->IsWindow() == FALSE)
|
|
|
3862 return FALSE;
|
|
|
3863
|
|
|
3864 BOOL bRet = FALSE;
|
|
|
3865
|
|
|
3866 // Check for TabView built-in accelerators (Ctrl+Tab/Ctrl+Shift+Tab - next/previous page)
|
|
|
3867 int nCount = GetPageCount();
|
|
|
3868 if(nCount > 0)
|
|
|
3869 {
|
|
|
3870 bool bControl = (::GetKeyState(VK_CONTROL) < 0);
|
|
|
3871 if((pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_TAB) && bControl)
|
|
|
3872 {
|
|
|
3873 if(nCount > 1)
|
|
|
3874 {
|
|
|
3875 int nPage = m_nActivePage;
|
|
|
3876 bool bShift = (::GetKeyState(VK_SHIFT) < 0);
|
|
|
3877 if(bShift)
|
|
|
3878 nPage = (nPage > 0) ? (nPage - 1) : (nCount - 1);
|
|
|
3879 else
|
|
|
3880 nPage = ((nPage >= 0) && (nPage < (nCount - 1))) ? (nPage + 1) : 0;
|
|
|
3881
|
|
|
3882 SetActivePage(nPage);
|
|
|
3883 T* pT = static_cast<T*>(this);
|
|
|
3884 pT->OnPageActivated(m_nActivePage);
|
|
|
3885 }
|
|
|
3886
|
|
|
3887 bRet = TRUE;
|
|
|
3888 }
|
|
|
3889 }
|
|
|
3890
|
|
|
3891 // If we are doing drag-drop, check for Escape key that cancels it
|
|
|
3892 if(bRet == FALSE)
|
|
|
3893 {
|
|
|
3894 if(m_bTabCapture && (pMsg->message == WM_KEYDOWN) && (pMsg->wParam == VK_ESCAPE))
|
|
|
3895 {
|
|
|
3896 ::ReleaseCapture();
|
|
|
3897 bRet = TRUE;
|
|
|
3898 }
|
|
|
3899 }
|
|
|
3900
|
|
|
3901 // Pass the message to the active page
|
|
|
3902 if(bRet == FALSE)
|
|
|
3903 {
|
|
|
3904 if(m_nActivePage != -1)
|
|
|
3905 bRet = (BOOL)::SendMessage(GetPageHWND(m_nActivePage), WM_FORWARDMSG, 0, (LPARAM)pMsg);
|
|
|
3906 }
|
|
|
3907
|
|
|
3908 return bRet;
|
|
|
3909 }
|
|
|
3910
|
|
|
3911 // Attributes
|
|
|
3912 int GetPageCount() const
|
|
|
3913 {
|
|
|
3914 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
3915 return m_tab.GetItemCount();
|
|
|
3916 }
|
|
|
3917
|
|
|
3918 int GetActivePage() const
|
|
|
3919 {
|
|
|
3920 return m_nActivePage;
|
|
|
3921 }
|
|
|
3922
|
|
|
3923 void SetActivePage(int nPage)
|
|
|
3924 {
|
|
|
3925 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
3926 ATLASSERT(IsValidPageIndex(nPage));
|
|
|
3927
|
|
|
3928 T* pT = static_cast<T*>(this);
|
|
|
3929
|
|
|
3930 this->SetRedraw(FALSE);
|
|
|
3931
|
|
|
3932 if(m_nActivePage != -1)
|
|
|
3933 ::ShowWindow(GetPageHWND(m_nActivePage), SW_HIDE);
|
|
|
3934 m_nActivePage = nPage;
|
|
|
3935 m_tab.SetCurSel(m_nActivePage);
|
|
|
3936 ::ShowWindow(GetPageHWND(m_nActivePage), SW_SHOW);
|
|
|
3937
|
|
|
3938 pT->UpdateLayout();
|
|
|
3939
|
|
|
3940 this->SetRedraw(TRUE);
|
|
|
3941 this->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
|
|
|
3942
|
|
|
3943 if(::GetFocus() != m_tab.m_hWnd)
|
|
|
3944 ::SetFocus(GetPageHWND(m_nActivePage));
|
|
|
3945
|
|
|
3946 pT->UpdateTitleBar();
|
|
|
3947 pT->UpdateMenu();
|
|
|
3948 }
|
|
|
3949
|
|
|
3950 HIMAGELIST GetImageList() const
|
|
|
3951 {
|
|
|
3952 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
3953 return m_tab.GetImageList();
|
|
|
3954 }
|
|
|
3955
|
|
|
3956 HIMAGELIST SetImageList(HIMAGELIST hImageList)
|
|
|
3957 {
|
|
|
3958 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
3959 return m_tab.SetImageList(hImageList);
|
|
|
3960 }
|
|
|
3961
|
|
|
3962 void SetWindowMenu(HMENU hMenu)
|
|
|
3963 {
|
|
|
3964 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
3965
|
|
|
3966 m_menu = hMenu;
|
|
|
3967
|
|
|
3968 T* pT = static_cast<T*>(this);
|
|
|
3969 pT->UpdateMenu();
|
|
|
3970 }
|
|
|
3971
|
|
|
3972 void SetTitleBarWindow(HWND hWnd)
|
|
|
3973 {
|
|
|
3974 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
3975
|
|
|
3976 delete [] m_lpstrTitleBarBase;
|
|
|
3977 m_lpstrTitleBarBase = NULL;
|
|
|
3978
|
|
|
3979 m_wndTitleBar = hWnd;
|
|
|
3980 if(hWnd == NULL)
|
|
|
3981 return;
|
|
|
3982
|
|
|
3983 int cchLen = m_wndTitleBar.GetWindowTextLength() + 1;
|
|
|
3984 ATLTRY(m_lpstrTitleBarBase = new TCHAR[cchLen]);
|
|
|
3985 if(m_lpstrTitleBarBase != NULL)
|
|
|
3986 {
|
|
|
3987 m_wndTitleBar.GetWindowText(m_lpstrTitleBarBase, cchLen);
|
|
|
3988 T* pT = static_cast<T*>(this);
|
|
|
3989 pT->UpdateTitleBar();
|
|
|
3990 }
|
|
|
3991 }
|
|
|
3992
|
|
|
3993 // Page attributes
|
|
|
3994 HWND GetPageHWND(int nPage) const
|
|
|
3995 {
|
|
|
3996 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
3997 ATLASSERT(IsValidPageIndex(nPage));
|
|
|
3998
|
|
|
3999 TCITEMEXTRA tcix = {};
|
|
|
4000 tcix.tciheader.mask = TCIF_PARAM;
|
|
|
4001 m_tab.GetItem(nPage, tcix);
|
|
|
4002
|
|
|
4003 return tcix.tvpage.hWnd;
|
|
|
4004 }
|
|
|
4005
|
|
|
4006 LPCTSTR GetPageTitle(int nPage) const
|
|
|
4007 {
|
|
|
4008 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4009 ATLASSERT(IsValidPageIndex(nPage));
|
|
|
4010
|
|
|
4011 TCITEMEXTRA tcix = {};
|
|
|
4012 tcix.tciheader.mask = TCIF_PARAM;
|
|
|
4013 if(m_tab.GetItem(nPage, tcix) == FALSE)
|
|
|
4014 return NULL;
|
|
|
4015
|
|
|
4016 return tcix.tvpage.lpstrTitle;
|
|
|
4017 }
|
|
|
4018
|
|
|
4019 bool SetPageTitle(int nPage, LPCTSTR lpstrTitle)
|
|
|
4020 {
|
|
|
4021 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4022 ATLASSERT(IsValidPageIndex(nPage));
|
|
|
4023
|
|
|
4024 T* pT = static_cast<T*>(this);
|
|
|
4025
|
|
|
4026 int cchBuff = lstrlen(lpstrTitle) + 1;
|
|
|
4027 LPTSTR lpstrBuff = NULL;
|
|
|
4028 ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
|
|
|
4029 if(lpstrBuff == NULL)
|
|
|
4030 return false;
|
|
|
4031
|
|
|
4032 ATL::Checked::tcscpy_s(lpstrBuff, cchBuff, lpstrTitle);
|
|
|
4033 TCITEMEXTRA tcix = {};
|
|
|
4034 tcix.tciheader.mask = TCIF_PARAM;
|
|
|
4035 if(m_tab.GetItem(nPage, tcix) == FALSE)
|
|
|
4036 return false;
|
|
|
4037
|
|
|
4038 ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
|
|
|
4039 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
|
|
|
4040 if(lpstrTabText == NULL)
|
|
|
4041 return false;
|
|
|
4042
|
|
|
4043 delete [] tcix.tvpage.lpstrTitle;
|
|
|
4044
|
|
|
4045 pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
|
|
|
4046
|
|
|
4047 tcix.tciheader.mask = TCIF_TEXT | TCIF_PARAM;
|
|
|
4048 tcix.tciheader.pszText = lpstrTabText;
|
|
|
4049 tcix.tvpage.lpstrTitle = lpstrBuff;
|
|
|
4050 if(m_tab.SetItem(nPage, tcix) == FALSE)
|
|
|
4051 return false;
|
|
|
4052
|
|
|
4053 pT->UpdateTitleBar();
|
|
|
4054 pT->UpdateMenu();
|
|
|
4055
|
|
|
4056 return true;
|
|
|
4057 }
|
|
|
4058
|
|
|
4059 LPVOID GetPageData(int nPage) const
|
|
|
4060 {
|
|
|
4061 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4062 ATLASSERT(IsValidPageIndex(nPage));
|
|
|
4063
|
|
|
4064 TCITEMEXTRA tcix = {};
|
|
|
4065 tcix.tciheader.mask = TCIF_PARAM;
|
|
|
4066 m_tab.GetItem(nPage, tcix);
|
|
|
4067
|
|
|
4068 return tcix.tvpage.pData;
|
|
|
4069 }
|
|
|
4070
|
|
|
4071 LPVOID SetPageData(int nPage, LPVOID pData)
|
|
|
4072 {
|
|
|
4073 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4074 ATLASSERT(IsValidPageIndex(nPage));
|
|
|
4075
|
|
|
4076 TCITEMEXTRA tcix = {};
|
|
|
4077 tcix.tciheader.mask = TCIF_PARAM;
|
|
|
4078 m_tab.GetItem(nPage, tcix);
|
|
|
4079 LPVOID pDataOld = tcix.tvpage.pData;
|
|
|
4080
|
|
|
4081 tcix.tvpage.pData = pData;
|
|
|
4082 m_tab.SetItem(nPage, tcix);
|
|
|
4083
|
|
|
4084 return pDataOld;
|
|
|
4085 }
|
|
|
4086
|
|
|
4087 int GetPageImage(int nPage) const
|
|
|
4088 {
|
|
|
4089 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4090 ATLASSERT(IsValidPageIndex(nPage));
|
|
|
4091
|
|
|
4092 TCITEMEXTRA tcix = {};
|
|
|
4093 tcix.tciheader.mask = TCIF_IMAGE;
|
|
|
4094 m_tab.GetItem(nPage, tcix);
|
|
|
4095
|
|
|
4096 return tcix.tciheader.iImage;
|
|
|
4097 }
|
|
|
4098
|
|
|
4099 int SetPageImage(int nPage, int nImage)
|
|
|
4100 {
|
|
|
4101 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4102 ATLASSERT(IsValidPageIndex(nPage));
|
|
|
4103
|
|
|
4104 TCITEMEXTRA tcix = {};
|
|
|
4105 tcix.tciheader.mask = TCIF_IMAGE;
|
|
|
4106 m_tab.GetItem(nPage, tcix);
|
|
|
4107 int nImageOld = tcix.tciheader.iImage;
|
|
|
4108
|
|
|
4109 tcix.tciheader.iImage = nImage;
|
|
|
4110 m_tab.SetItem(nPage, tcix);
|
|
|
4111
|
|
|
4112 return nImageOld;
|
|
|
4113 }
|
|
|
4114
|
|
|
4115 // Operations
|
|
|
4116 bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
|
|
|
4117 {
|
|
|
4118 return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData);
|
|
|
4119 }
|
|
|
4120
|
|
|
4121 bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL)
|
|
|
4122 {
|
|
|
4123 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4124 ATLASSERT((nPage == GetPageCount()) || IsValidPageIndex(nPage));
|
|
|
4125
|
|
|
4126 T* pT = static_cast<T*>(this);
|
|
|
4127
|
|
|
4128 int cchBuff = lstrlen(lpstrTitle) + 1;
|
|
|
4129 LPTSTR lpstrBuff = NULL;
|
|
|
4130 ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
|
|
|
4131 if(lpstrBuff == NULL)
|
|
|
4132 return false;
|
|
|
4133
|
|
|
4134 ATL::Checked::tcscpy_s(lpstrBuff, cchBuff, lpstrTitle);
|
|
|
4135
|
|
|
4136 ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
|
|
|
4137 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
|
|
|
4138 if(lpstrTabText == NULL)
|
|
|
4139 return false;
|
|
|
4140
|
|
|
4141 pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);
|
|
|
4142
|
|
|
4143 this->SetRedraw(FALSE);
|
|
|
4144
|
|
|
4145 TCITEMEXTRA tcix = {};
|
|
|
4146 tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
|
|
|
4147 tcix.tciheader.pszText = lpstrTabText;
|
|
|
4148 tcix.tciheader.iImage = nImage;
|
|
|
4149 tcix.tvpage.hWnd = hWndView;
|
|
|
4150 tcix.tvpage.lpstrTitle = lpstrBuff;
|
|
|
4151 tcix.tvpage.pData = pData;
|
|
|
4152 int nItem = m_tab.InsertItem(nPage, tcix);
|
|
|
4153 if(nItem == -1)
|
|
|
4154 {
|
|
|
4155 delete [] lpstrBuff;
|
|
|
4156 this->SetRedraw(TRUE);
|
|
|
4157 return false;
|
|
|
4158 }
|
|
|
4159
|
|
|
4160 // adjust active page index, if inserted before it
|
|
|
4161 if(nPage <= m_nActivePage)
|
|
|
4162 m_nActivePage++;
|
|
|
4163
|
|
|
4164 SetActivePage(nItem);
|
|
|
4165 pT->OnPageActivated(m_nActivePage);
|
|
|
4166
|
|
|
4167 if(GetPageCount() == 1)
|
|
|
4168 pT->ShowTabControl(true);
|
|
|
4169
|
|
|
4170 pT->UpdateLayout();
|
|
|
4171
|
|
|
4172 this->SetRedraw(TRUE);
|
|
|
4173 this->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
|
|
|
4174
|
|
|
4175 return true;
|
|
|
4176 }
|
|
|
4177
|
|
|
4178 void RemovePage(int nPage)
|
|
|
4179 {
|
|
|
4180 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4181 ATLASSERT(IsValidPageIndex(nPage));
|
|
|
4182
|
|
|
4183 T* pT = static_cast<T*>(this);
|
|
|
4184
|
|
|
4185 this->SetRedraw(FALSE);
|
|
|
4186
|
|
|
4187 if(GetPageCount() == 1)
|
|
|
4188 pT->ShowTabControl(false);
|
|
|
4189
|
|
|
4190 if(m_bDestroyPageOnRemove)
|
|
|
4191 ::DestroyWindow(GetPageHWND(nPage));
|
|
|
4192 else
|
|
|
4193 ::ShowWindow(GetPageHWND(nPage), SW_HIDE);
|
|
|
4194 LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(nPage);
|
|
|
4195 delete [] lpstrTitle;
|
|
|
4196
|
|
|
4197 ATLVERIFY(m_tab.DeleteItem(nPage) != FALSE);
|
|
|
4198
|
|
|
4199 if(m_nActivePage == nPage)
|
|
|
4200 {
|
|
|
4201 m_nActivePage = -1;
|
|
|
4202
|
|
|
4203 if(nPage > 0)
|
|
|
4204 {
|
|
|
4205 SetActivePage(nPage - 1);
|
|
|
4206 }
|
|
|
4207 else if(GetPageCount() > 0)
|
|
|
4208 {
|
|
|
4209 SetActivePage(nPage);
|
|
|
4210 }
|
|
|
4211 else
|
|
|
4212 {
|
|
|
4213 this->SetRedraw(TRUE);
|
|
|
4214 this->Invalidate();
|
|
|
4215 this->UpdateWindow();
|
|
|
4216 pT->UpdateTitleBar();
|
|
|
4217 pT->UpdateMenu();
|
|
|
4218 }
|
|
|
4219 }
|
|
|
4220 else
|
|
|
4221 {
|
|
|
4222 nPage = (nPage < m_nActivePage) ? (m_nActivePage - 1) : m_nActivePage;
|
|
|
4223 m_nActivePage = -1;
|
|
|
4224 SetActivePage(nPage);
|
|
|
4225 }
|
|
|
4226
|
|
|
4227 pT->OnPageActivated(m_nActivePage);
|
|
|
4228 }
|
|
|
4229
|
|
|
4230 void RemoveAllPages()
|
|
|
4231 {
|
|
|
4232 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4233
|
|
|
4234 if(GetPageCount() == 0)
|
|
|
4235 return;
|
|
|
4236
|
|
|
4237 T* pT = static_cast<T*>(this);
|
|
|
4238
|
|
|
4239 this->SetRedraw(FALSE);
|
|
|
4240
|
|
|
4241 pT->ShowTabControl(false);
|
|
|
4242
|
|
|
4243 for(int i = 0; i < GetPageCount(); i++)
|
|
|
4244 {
|
|
|
4245 if(m_bDestroyPageOnRemove)
|
|
|
4246 ::DestroyWindow(GetPageHWND(i));
|
|
|
4247 else
|
|
|
4248 ::ShowWindow(GetPageHWND(i), SW_HIDE);
|
|
|
4249 LPTSTR lpstrTitle = (LPTSTR)GetPageTitle(i);
|
|
|
4250 delete [] lpstrTitle;
|
|
|
4251 }
|
|
|
4252 m_tab.DeleteAllItems();
|
|
|
4253
|
|
|
4254 m_nActivePage = -1;
|
|
|
4255 pT->OnPageActivated(m_nActivePage);
|
|
|
4256
|
|
|
4257 this->SetRedraw(TRUE);
|
|
|
4258 this->Invalidate();
|
|
|
4259 this->UpdateWindow();
|
|
|
4260
|
|
|
4261 pT->UpdateTitleBar();
|
|
|
4262 pT->UpdateMenu();
|
|
|
4263 }
|
|
|
4264
|
|
|
4265 int PageIndexFromHwnd(HWND hWnd) const
|
|
|
4266 {
|
|
|
4267 int nIndex = -1;
|
|
|
4268
|
|
|
4269 for(int i = 0; i < GetPageCount(); i++)
|
|
|
4270 {
|
|
|
4271 if(GetPageHWND(i) == hWnd)
|
|
|
4272 {
|
|
|
4273 nIndex = i;
|
|
|
4274 break;
|
|
|
4275 }
|
|
|
4276 }
|
|
|
4277
|
|
|
4278 return nIndex;
|
|
|
4279 }
|
|
|
4280
|
|
|
4281 void BuildWindowMenu(HMENU hMenu, int nMenuItemsCount = 10, bool bEmptyMenuItem = true, bool bWindowsMenuItem = true, bool bActivePageMenuItem = true, bool bActiveAsDefaultMenuItem = false)
|
|
|
4282 {
|
|
|
4283 ATLASSERT(::IsWindow(this->m_hWnd));
|
|
|
4284
|
|
|
4285 CMenuHandle menu = hMenu;
|
|
|
4286 T* pT = static_cast<T*>(this);
|
|
|
4287 (void)pT; // avoid level 4 warning
|
|
|
4288 int nFirstPos = 0;
|
|
|
4289
|
|
|
4290 // Find first menu item in our range
|
|
|
4291 for(nFirstPos = 0; nFirstPos < menu.GetMenuItemCount(); nFirstPos++)
|
|
|
4292 {
|
|
|
4293 UINT nID = menu.GetMenuItemID(nFirstPos);
|
|
|
4294 if(((nID >= ID_WINDOW_TABFIRST) && (nID <= ID_WINDOW_TABLAST)) || (nID == ID_WINDOW_SHOWTABLIST))
|
|
|
4295 break;
|
|
|
4296 }
|
|
|
4297
|
|
|
4298 // Remove all menu items for tab pages
|
|
|
4299 BOOL bRet = TRUE;
|
|
|
4300 while(bRet != FALSE)
|
|
|
4301 bRet = menu.DeleteMenu(nFirstPos, MF_BYPOSITION);
|
|
|
4302
|
|
|
4303 // Add separator if it's not already there
|
|
|
4304 int nPageCount = GetPageCount();
|
|
|
4305 if((bWindowsMenuItem || (nPageCount > 0)) && (nFirstPos > 0))
|
|
|
4306 {
|
|
|
4307 CMenuItemInfo mii;
|
|
|
4308 mii.fMask = MIIM_TYPE;
|
|
|
4309 menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
|
|
|
4310 if((nFirstPos <= 0) || ((mii.fType & MFT_SEPARATOR) == 0))
|
|
|
4311 {
|
|
|
4312 menu.AppendMenu(MF_SEPARATOR);
|
|
|
4313 nFirstPos++;
|
|
|
4314 }
|
|
|
4315 }
|
|
|
4316
|
|
|
4317 // Add menu items for all pages
|
|
|
4318 if(nPageCount > 0)
|
|
|
4319 {
|
|
|
4320 // Append menu items for all pages
|
|
|
4321 const int cchPrefix = 3; // 2 digits + space
|
|
|
4322 nMenuItemsCount = __min(__min(nPageCount, nMenuItemsCount), (int)m_nMenuItemsMax);
|
|
|
4323 ATLASSERT(nMenuItemsCount < 100); // 2 digits only
|
|
|
4324 if(nMenuItemsCount >= 100)
|
|
|
4325 nMenuItemsCount = 99;
|
|
|
4326
|
|
|
4327 for(int i = 0; i < nMenuItemsCount; i++)
|
|
|
4328 {
|
|
|
4329 LPCTSTR lpstrTitle = GetPageTitle(i);
|
|
|
4330 int nLen = lstrlen(lpstrTitle);
|
|
|
4331 ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
|
|
|
4332 LPTSTR lpstrText = buff.Allocate(cchPrefix + nLen + 1);
|
|
|
4333 ATLASSERT(lpstrText != NULL);
|
|
|
4334 if(lpstrText != NULL)
|
|
|
4335 {
|
|
|
4336 LPCTSTR lpstrFormat = (i < 9) ? _T("&%i %s") : _T("%i %s");
|
|
|
4337 _stprintf_s(lpstrText, cchPrefix + nLen + 1, lpstrFormat, i + 1, lpstrTitle);
|
|
|
4338 menu.AppendMenu(MF_STRING, ID_WINDOW_TABFIRST + i, lpstrText);
|
|
|
4339 }
|
|
|
4340 }
|
|
|
4341
|
|
|
4342 // Mark active page
|
|
|
4343 if(bActivePageMenuItem && (m_nActivePage != -1))
|
|
|
4344 {
|
|
|
4345 if(bActiveAsDefaultMenuItem)
|
|
|
4346 {
|
|
|
4347 menu.SetMenuDefaultItem((UINT)-1, TRUE);
|
|
|
4348 menu.SetMenuDefaultItem(nFirstPos + m_nActivePage, TRUE);
|
|
|
4349 }
|
|
|
4350 else
|
|
|
4351 {
|
|
|
4352 menu.CheckMenuRadioItem(nFirstPos, nFirstPos + nMenuItemsCount, nFirstPos + m_nActivePage, MF_BYPOSITION);
|
|
|
4353 }
|
|
|
4354 }
|
|
|
4355 }
|
|
|
4356 else
|
|
|
4357 {
|
|
|
4358 if(bEmptyMenuItem)
|
|
|
4359 {
|
|
|
4360 menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_TABFIRST, pT->GetEmptyListText());
|
|
|
4361 menu.EnableMenuItem(ID_WINDOW_TABFIRST, MF_GRAYED);
|
|
|
4362 }
|
|
|
4363
|
|
|
4364 // Remove separator if nothing else is there
|
|
|
4365 if(!bEmptyMenuItem && !bWindowsMenuItem && (nFirstPos > 0))
|
|
|
4366 {
|
|
|
4367 CMenuItemInfo mii;
|
|
|
4368 mii.fMask = MIIM_TYPE;
|
|
|
4369 menu.GetMenuItemInfo(nFirstPos - 1, TRUE, &mii);
|
|
|
4370 if((mii.fType & MFT_SEPARATOR) != 0)
|
|
|
4371 menu.DeleteMenu(nFirstPos - 1, MF_BYPOSITION);
|
|
|
4372 }
|
|
|
4373 }
|
|
|
4374
|
|
|
4375 // Add "Windows..." menu item
|
|
|
4376 if(bWindowsMenuItem)
|
|
|
4377 menu.AppendMenu(MF_BYPOSITION | MF_STRING, ID_WINDOW_SHOWTABLIST, pT->GetWindowsMenuItemText());
|
|
|
4378 }
|
|
|
4379
|
|
|
4380 BOOL SubclassWindow(HWND hWnd)
|
|
|
4381 {
|
|
|
4382 BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd);
|
|
|
4383 if(bRet != FALSE)
|
|
|
4384 {
|
|
|
4385 T* pT = static_cast<T*>(this);
|
|
|
4386 pT->CreateTabControl();
|
|
|
4387 pT->UpdateLayout();
|
|
|
4388 }
|
|
|
4389
|
|
|
4390 return bRet;
|
|
|
4391 }
|
|
|
4392
|
|
|
4393 // Message map and handlers
|
|
|
4394 BEGIN_MSG_MAP(CTabViewImpl)
|
|
|
4395 MESSAGE_HANDLER(WM_CREATE, OnCreate)
|
|
|
4396 MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
|
|
|
4397 MESSAGE_HANDLER(WM_SIZE, OnSize)
|
|
|
4398 MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
|
|
|
4399 MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
|
|
|
4400 MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
|
|
|
4401 MESSAGE_HANDLER(WM_TIMER, OnTimer)
|
|
|
4402 MESSAGE_HANDLER(WM_CONTEXTMENU, OnTabContextMenu)
|
|
|
4403 NOTIFY_HANDLER(m_nTabID, TCN_SELCHANGE, OnTabChanged)
|
|
|
4404 NOTIFY_ID_HANDLER(m_nTabID, OnTabNotification)
|
|
|
4405 NOTIFY_CODE_HANDLER(TTN_GETDISPINFO, OnTabGetDispInfo)
|
|
|
4406 FORWARD_NOTIFICATIONS()
|
|
|
4407 ALT_MSG_MAP(1) // tab control
|
|
|
4408 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnTabLButtonDown)
|
|
|
4409 MESSAGE_HANDLER(WM_LBUTTONUP, OnTabLButtonUp)
|
|
|
4410 MESSAGE_HANDLER(WM_CAPTURECHANGED, OnTabCaptureChanged)
|
|
|
4411 MESSAGE_HANDLER(WM_MOUSEMOVE, OnTabMouseMove)
|
|
|
4412 MESSAGE_HANDLER(WM_MOUSELEAVE, OnTabMouseLeave)
|
|
|
4413 NOTIFY_HANDLER(T::_nCloseBtnID, TBVN_CLOSEBTNMOUSELEAVE, OnTabCloseBtnMouseLeave)
|
|
|
4414 COMMAND_HANDLER(T::_nCloseBtnID, BN_CLICKED, OnTabCloseBtnClicked)
|
|
|
4415 END_MSG_MAP()
|
|
|
4416
|
|
|
4417 LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
4418 {
|
|
|
4419 T* pT = static_cast<T*>(this);
|
|
|
4420 pT->CreateTabControl();
|
|
|
4421
|
|
|
4422 return 0;
|
|
|
4423 }
|
|
|
4424
|
|
|
4425 LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
4426 {
|
|
|
4427 RemoveAllPages();
|
|
|
4428
|
|
|
4429 if(m_bDestroyImageList)
|
|
|
4430 {
|
|
|
4431 CImageList il = m_tab.SetImageList(NULL);
|
|
|
4432 if(il.m_hImageList != NULL)
|
|
|
4433 il.Destroy();
|
|
|
4434 }
|
|
|
4435
|
|
|
4436 if(m_bInternalFont)
|
|
|
4437 {
|
|
|
4438 HFONT hFont = m_tab.GetFont();
|
|
|
4439 m_tab.SetFont(NULL, FALSE);
|
|
|
4440 ::DeleteObject(hFont);
|
|
|
4441 m_bInternalFont = false;
|
|
|
4442 }
|
|
|
4443
|
|
|
4444 m_ud.m_hWnd = NULL;
|
|
|
4445
|
|
|
4446 return 0;
|
|
|
4447 }
|
|
|
4448
|
|
|
4449 LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
4450 {
|
|
|
4451 T* pT = static_cast<T*>(this);
|
|
|
4452 pT->UpdateLayout();
|
|
|
4453 return 0;
|
|
|
4454 }
|
|
|
4455
|
|
|
4456 LRESULT OnSetFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
4457 {
|
|
|
4458 if(m_nActivePage != -1)
|
|
|
4459 ::SetFocus(GetPageHWND(m_nActivePage));
|
|
|
4460 return 0;
|
|
|
4461 }
|
|
|
4462
|
|
|
4463 LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
|
|
|
4464 {
|
|
|
4465 return m_tab.SendMessage(WM_GETFONT);
|
|
|
4466 }
|
|
|
4467
|
|
|
4468 LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
|
|
|
4469 {
|
|
|
4470 if(m_bInternalFont)
|
|
|
4471 {
|
|
|
4472 HFONT hFont = m_tab.GetFont();
|
|
|
4473 m_tab.SetFont(NULL, FALSE);
|
|
|
4474 ::DeleteObject(hFont);
|
|
|
4475 m_bInternalFont = false;
|
|
|
4476 }
|
|
|
4477
|
|
|
4478 m_tab.SendMessage(WM_SETFONT, wParam, lParam);
|
|
|
4479
|
|
|
4480 T* pT = static_cast<T*>(this);
|
|
|
4481 m_cyTabHeight = pT->CalcTabHeight();
|
|
|
4482
|
|
|
4483 if((BOOL)lParam != FALSE)
|
|
|
4484 pT->UpdateLayout();
|
|
|
4485
|
|
|
4486 return 0;
|
|
|
4487 }
|
|
|
4488
|
|
|
4489 LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
4490 {
|
|
|
4491 if(wParam == _nAutoScrollTimerID)
|
|
|
4492 {
|
|
|
4493 T* pT = static_cast<T*>(this);
|
|
|
4494 pT->DoAutoScroll();
|
|
|
4495 }
|
|
|
4496 else
|
|
|
4497 {
|
|
|
4498 bHandled = FALSE;
|
|
|
4499 }
|
|
|
4500
|
|
|
4501 return 0;
|
|
|
4502 }
|
|
|
4503
|
|
|
4504 LRESULT OnTabContextMenu(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
|
|
4505 {
|
|
|
4506 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
4507 int nPage = m_nActivePage;
|
|
|
4508 bool bAction = false;
|
|
|
4509 if((HWND)wParam == m_tab.m_hWnd)
|
|
|
4510 {
|
|
|
4511 if((pt.x == -1) && (pt.y == -1)) // keyboard
|
|
|
4512 {
|
|
|
4513 RECT rect = {};
|
|
|
4514 m_tab.GetItemRect(m_nActivePage, &rect);
|
|
|
4515 pt.x = rect.left;
|
|
|
4516 pt.y = rect.bottom;
|
|
|
4517 m_tab.ClientToScreen(&pt);
|
|
|
4518 bAction = true;
|
|
|
4519 }
|
|
|
4520 else if(::WindowFromPoint(pt) == m_tab.m_hWnd)
|
|
|
4521 {
|
|
|
4522 TCHITTESTINFO hti = {};
|
|
|
4523 hti.pt = pt;
|
|
|
4524 this->ScreenToClient(&hti.pt);
|
|
|
4525 nPage = m_tab.HitTest(&hti);
|
|
|
4526
|
|
|
4527 bAction = true;
|
|
|
4528 }
|
|
|
4529 }
|
|
|
4530
|
|
|
4531 if(bAction)
|
|
|
4532 {
|
|
|
4533 T* pT = static_cast<T*>(this);
|
|
|
4534 pT->OnContextMenu(nPage, pt);
|
|
|
4535 }
|
|
|
4536 else
|
|
|
4537 {
|
|
|
4538 bHandled = FALSE;
|
|
|
4539 }
|
|
|
4540
|
|
|
4541 return 0;
|
|
|
4542 }
|
|
|
4543
|
|
|
4544 LRESULT OnTabChanged(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
|
|
|
4545 {
|
|
|
4546 if(m_bTabCloseButton && (m_btnClose.m_hWnd != NULL))
|
|
|
4547 {
|
|
|
4548 T* pT = static_cast<T*>(this);
|
|
|
4549 RECT rcClose = {};
|
|
|
4550 pT->CalcCloseButtonRect(m_nCloseItem, rcClose);
|
|
|
4551 m_btnClose.SetWindowPos(NULL, &rcClose, SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
4552 }
|
|
|
4553
|
|
|
4554 SetActivePage(m_tab.GetCurSel());
|
|
|
4555 T* pT = static_cast<T*>(this);
|
|
|
4556 pT->OnPageActivated(m_nActivePage);
|
|
|
4557
|
|
|
4558 return 0;
|
|
|
4559 }
|
|
|
4560
|
|
|
4561 LRESULT OnTabNotification(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
|
|
|
4562 {
|
|
|
4563 // nothing to do - this just blocks all tab control
|
|
|
4564 // notifications from being propagated further
|
|
|
4565 return 0;
|
|
|
4566 }
|
|
|
4567
|
|
|
4568 LRESULT OnTabGetDispInfo(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
|
|
|
4569 {
|
|
|
4570 LPNMTTDISPINFO pTTDI = (LPNMTTDISPINFO)pnmh;
|
|
|
4571 if(pTTDI->hdr.hwndFrom == m_tab.GetTooltips())
|
|
|
4572 {
|
|
|
4573 T* pT = static_cast<T*>(this);
|
|
|
4574 pT->UpdateTooltipText(pTTDI);
|
|
|
4575 }
|
|
|
4576 else
|
|
|
4577 {
|
|
|
4578 bHandled = FALSE;
|
|
|
4579 }
|
|
|
4580
|
|
|
4581 return 0;
|
|
|
4582 }
|
|
|
4583
|
|
|
4584 // Tab control message handlers
|
|
|
4585 LRESULT OnTabLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
|
|
|
4586 {
|
|
|
4587 if(!m_bNoTabDrag && (m_tab.GetItemCount() > 1))
|
|
|
4588 {
|
|
|
4589 m_bTabCapture = true;
|
|
|
4590 m_tab.SetCapture();
|
|
|
4591
|
|
|
4592 m_ptStartDrag.x = GET_X_LPARAM(lParam);
|
|
|
4593 m_ptStartDrag.y = GET_Y_LPARAM(lParam);
|
|
|
4594 }
|
|
|
4595
|
|
|
4596 bHandled = FALSE;
|
|
|
4597 return 0;
|
|
|
4598 }
|
|
|
4599
|
|
|
4600 LRESULT OnTabLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
|
|
|
4601 {
|
|
|
4602 if(m_bTabCapture)
|
|
|
4603 {
|
|
|
4604 if(m_bTabDrag)
|
|
|
4605 {
|
|
|
4606 T* pT = static_cast<T*>(this);
|
|
|
4607 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
4608 int nItem = pT->DragHitTest(pt);
|
|
|
4609 if(nItem != -1)
|
|
|
4610 MovePage(m_nActivePage, nItem);
|
|
|
4611 }
|
|
|
4612
|
|
|
4613 ::ReleaseCapture();
|
|
|
4614 }
|
|
|
4615
|
|
|
4616 bHandled = FALSE;
|
|
|
4617 return 0;
|
|
|
4618 }
|
|
|
4619
|
|
|
4620 LRESULT OnTabCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
4621 {
|
|
|
4622 if(m_bTabCapture)
|
|
|
4623 {
|
|
|
4624 m_bTabCapture = false;
|
|
|
4625
|
|
|
4626 if(m_bTabDrag)
|
|
|
4627 {
|
|
|
4628 m_bTabDrag = false;
|
|
|
4629
|
|
|
4630 T* pT = static_cast<T*>(this);
|
|
|
4631 if(!m_bNoTabDragAutoScroll)
|
|
|
4632 pT->StartStopAutoScroll(-1);
|
|
|
4633
|
|
|
4634 pT->DrawMoveMark(-1);
|
|
|
4635
|
|
|
4636 m_ilDrag.DragLeave(GetDesktopWindow());
|
|
|
4637 m_ilDrag.EndDrag();
|
|
|
4638
|
|
|
4639 m_ilDrag.Destroy();
|
|
|
4640 m_ilDrag.m_hImageList = NULL;
|
|
|
4641 }
|
|
|
4642 }
|
|
|
4643
|
|
|
4644 bHandled = FALSE;
|
|
|
4645 return 0;
|
|
|
4646 }
|
|
|
4647
|
|
|
4648 LRESULT OnTabMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
|
|
|
4649 {
|
|
|
4650 bHandled = FALSE;
|
|
|
4651
|
|
|
4652 if(m_bTabCapture)
|
|
|
4653 {
|
|
|
4654 POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
|
4655
|
|
|
4656 if(!m_bTabDrag)
|
|
|
4657 {
|
|
|
4658 if((abs(m_ptStartDrag.x - GET_X_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CXDRAG)) ||
|
|
|
4659 (abs(m_ptStartDrag.y - GET_Y_LPARAM(lParam)) >= ::GetSystemMetrics(SM_CYDRAG)))
|
|
|
4660 {
|
|
|
4661 T* pT = static_cast<T*>(this);
|
|
|
4662 pT->GenerateDragImage(m_nActivePage);
|
|
|
4663
|
|
|
4664 int cxCursor = ::GetSystemMetrics(SM_CXCURSOR);
|
|
|
4665 int cyCursor = ::GetSystemMetrics(SM_CYCURSOR);
|
|
|
4666 m_ilDrag.BeginDrag(0, -(cxCursor / 2), -(cyCursor / 2));
|
|
|
4667 POINT ptEnter = m_ptStartDrag;
|
|
|
4668 m_tab.ClientToScreen(&ptEnter);
|
|
|
4669 m_ilDrag.DragEnter(GetDesktopWindow(), ptEnter);
|
|
|
4670
|
|
|
4671 m_bTabDrag = true;
|
|
|
4672 }
|
|
|
4673 }
|
|
|
4674
|
|
|
4675 if(m_bTabDrag)
|
|
|
4676 {
|
|
|
4677 T* pT = static_cast<T*>(this);
|
|
|
4678 int nItem = pT->DragHitTest(pt);
|
|
|
4679
|
|
|
4680 pT->SetMoveCursor(nItem != -1);
|
|
|
4681
|
|
|
4682 if(m_nInsertItem != nItem)
|
|
|
4683 pT->DrawMoveMark(nItem);
|
|
|
4684
|
|
|
4685 if(!m_bNoTabDragAutoScroll)
|
|
|
4686 pT->StartStopAutoScroll(pt.x);
|
|
|
4687
|
|
|
4688 m_ilDrag.DragShowNolock((nItem != -1) ? TRUE : FALSE);
|
|
|
4689 m_tab.ClientToScreen(&pt);
|
|
|
4690 m_ilDrag.DragMove(pt);
|
|
|
4691
|
|
|
4692 bHandled = TRUE;
|
|
|
4693 }
|
|
|
4694 }
|
|
|
4695 else if(m_bTabCloseButton)
|
|
|
4696 {
|
|
|
4697 TCHITTESTINFO thti = {};
|
|
|
4698 thti.pt.x = GET_X_LPARAM(lParam);
|
|
|
4699 thti.pt.y = GET_Y_LPARAM(lParam);
|
|
|
4700
|
|
|
4701 int nItem = m_tab.HitTest(&thti);
|
|
|
4702 if(nItem >= 0)
|
|
|
4703 {
|
|
|
4704 ATLTRACE(_T("+++++ item = %i\n"), nItem);
|
|
|
4705
|
|
|
4706 T* pT = static_cast<T*>(this);
|
|
|
4707 if(m_btnClose.m_hWnd == NULL)
|
|
|
4708 {
|
|
|
4709 pT->CreateCloseButton(nItem);
|
|
|
4710 m_nCloseItem = nItem;
|
|
|
4711 }
|
|
|
4712 else if(m_nCloseItem != nItem)
|
|
|
4713 {
|
|
|
4714 RECT rcClose = {};
|
|
|
4715 pT->CalcCloseButtonRect(nItem, rcClose);
|
|
|
4716 m_btnClose.SetWindowPos(NULL, &rcClose, SWP_NOZORDER | SWP_NOACTIVATE);
|
|
|
4717 m_nCloseItem = nItem;
|
|
|
4718 }
|
|
|
4719
|
|
|
4720 TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_tab.m_hWnd };
|
|
|
4721 ::TrackMouseEvent(&tme);
|
|
|
4722 }
|
|
|
4723 }
|
|
|
4724
|
|
|
4725 return 0;
|
|
|
4726 }
|
|
|
4727
|
|
|
4728 LRESULT OnTabMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
|
|
|
4729 {
|
|
|
4730 bHandled = FALSE;
|
|
|
4731
|
|
|
4732 if(m_btnClose.m_hWnd != NULL)
|
|
|
4733 {
|
|
|
4734 POINT pt = {};
|
|
|
4735 ::GetCursorPos(&pt);
|
|
|
4736 RECT rect = {};
|
|
|
4737 m_btnClose.GetWindowRect(&rect);
|
|
|
4738 if(::PtInRect(&rect, pt) == FALSE)
|
|
|
4739 {
|
|
|
4740 m_nCloseItem = -1;
|
|
|
4741 T* pT = static_cast<T*>(this);
|
|
|
4742 pT->DestroyCloseButton();
|
|
|
4743 }
|
|
|
4744 else
|
|
|
4745 {
|
|
|
4746 bHandled = TRUE;
|
|
|
4747 }
|
|
|
4748 }
|
|
|
4749
|
|
|
4750 return 0;
|
|
|
4751 }
|
|
|
4752
|
|
|
4753 LRESULT OnTabCloseBtnMouseLeave(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
|
|
|
4754 {
|
|
|
4755 TCHITTESTINFO thti = {};
|
|
|
4756 ::GetCursorPos(&thti.pt);
|
|
|
4757 m_tab.ScreenToClient(&thti.pt);
|
|
|
4758 int nItem = m_tab.HitTest(&thti);
|
|
|
4759 if(nItem == -1)
|
|
|
4760 m_tab.SendMessage(WM_MOUSELEAVE);
|
|
|
4761
|
|
|
4762 return 0;
|
|
|
4763 }
|
|
|
4764
|
|
|
4765 LRESULT OnTabCloseBtnClicked(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
|
|
|
4766 {
|
|
|
4767 T* pT = static_cast<T*>(this);
|
|
|
4768 pT->OnTabCloseBtn(m_nCloseItem);
|
|
|
4769
|
|
|
4770 return 0;
|
|
|
4771 }
|
|
|
4772
|
|
|
4773 // Implementation helpers
|
|
|
4774 bool IsValidPageIndex(int nPage) const
|
|
|
4775 {
|
|
|
4776 return ((nPage >= 0) && (nPage < GetPageCount()));
|
|
|
4777 }
|
|
|
4778
|
|
|
4779 bool MovePage(int nMovePage, int nInsertBeforePage)
|
|
|
4780 {
|
|
|
4781 ATLASSERT(IsValidPageIndex(nMovePage));
|
|
|
4782 ATLASSERT(IsValidPageIndex(nInsertBeforePage));
|
|
|
4783
|
|
|
4784 if(!IsValidPageIndex(nMovePage) || !IsValidPageIndex(nInsertBeforePage))
|
|
|
4785 return false;
|
|
|
4786
|
|
|
4787 if(nMovePage == nInsertBeforePage)
|
|
|
4788 return true; // nothing to do
|
|
|
4789
|
|
|
4790 ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
|
|
|
4791 LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
|
|
|
4792 if(lpstrTabText == NULL)
|
|
|
4793 return false;
|
|
|
4794 TCITEMEXTRA tcix = {};
|
|
|
4795 tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
|
|
|
4796 tcix.tciheader.pszText = lpstrTabText;
|
|
|
4797 tcix.tciheader.cchTextMax = m_cchTabTextLength + 1;
|
|
|
4798 BOOL bRet = m_tab.GetItem(nMovePage, tcix);
|
|
|
4799 ATLASSERT(bRet != FALSE);
|
|
|
4800 if(bRet == FALSE)
|
|
|
4801 return false;
|
|
|
4802
|
|
|
4803 int nInsertItem = (nInsertBeforePage > nMovePage) ? nInsertBeforePage + 1 : nInsertBeforePage;
|
|
|
4804 int nNewItem = m_tab.InsertItem(nInsertItem, tcix);
|
|
|
4805 ATLASSERT(nNewItem == nInsertItem);
|
|
|
4806 if(nNewItem != nInsertItem)
|
|
|
4807 {
|
|
|
4808 ATLVERIFY(m_tab.DeleteItem(nNewItem));
|
|
|
4809 return false;
|
|
|
4810 }
|
|
|
4811
|
|
|
4812 if(nMovePage > nInsertBeforePage)
|
|
|
4813 ATLVERIFY(m_tab.DeleteItem(nMovePage + 1) != FALSE);
|
|
|
4814 else if(nMovePage < nInsertBeforePage)
|
|
|
4815 ATLVERIFY(m_tab.DeleteItem(nMovePage) != FALSE);
|
|
|
4816
|
|
|
4817 SetActivePage(nInsertBeforePage);
|
|
|
4818 T* pT = static_cast<T*>(this);
|
|
|
4819 pT->OnPageActivated(m_nActivePage);
|
|
|
4820
|
|
|
4821 return true;
|
|
|
4822 }
|
|
|
4823
|
|
|
4824 // Implementation overrideables
|
|
|
4825 bool CreateTabControl()
|
|
|
4826 {
|
|
|
4827 m_tab.Create(this->m_hWnd, this->rcDefault, NULL, WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | TCS_TOOLTIPS, 0, m_nTabID);
|
|
|
4828 ATLASSERT(m_tab.m_hWnd != NULL);
|
|
|
4829 if(m_tab.m_hWnd == NULL)
|
|
|
4830 return false;
|
|
|
4831
|
|
|
4832 m_tab.SetFont(AtlCreateControlFont());
|
|
|
4833 m_bInternalFont = true;
|
|
|
4834
|
|
|
4835 m_tab.SetItemExtra(sizeof(TABVIEWPAGE));
|
|
|
4836
|
|
|
4837 T* pT = static_cast<T*>(this);
|
|
|
4838 m_cyTabHeight = pT->CalcTabHeight();
|
|
|
4839
|
|
|
4840 return true;
|
|
|
4841 }
|
|
|
4842
|
|
|
4843 int CalcTabHeight()
|
|
|
4844 {
|
|
|
4845 int nCount = m_tab.GetItemCount();
|
|
|
4846 TCHAR szText[] = _T("NS");
|
|
|
4847 TCITEMEXTRA tcix = {};
|
|
|
4848 tcix.tciheader.mask = TCIF_TEXT;
|
|
|
4849 tcix.tciheader.pszText = szText;
|
|
|
4850 int nIndex = m_tab.InsertItem(nCount, tcix);
|
|
|
4851
|
|
|
4852 RECT rect = { 0, 0, 1000, 1000 };
|
|
|
4853 m_tab.AdjustRect(FALSE, &rect);
|
|
|
4854
|
|
|
4855 RECT rcWnd = { 0, 0, 1000, rect.top };
|
|
|
4856 ::AdjustWindowRectEx(&rcWnd, m_tab.GetStyle(), FALSE, m_tab.GetExStyle());
|
|
|
4857
|
|
|
4858 int nHeight = rcWnd.bottom - rcWnd.top;
|
|
|
4859
|
|
|
4860 m_tab.DeleteItem(nIndex);
|
|
|
4861
|
|
|
4862 return nHeight;
|
|
|
4863 }
|
|
|
4864
|
|
|
4865 void ShowTabControl(bool bShow)
|
|
|
4866 {
|
|
|
4867 m_tab.ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE);
|
|
|
4868 T* pT = static_cast<T*>(this);
|
|
|
4869 pT->UpdateLayout();
|
|
|
4870 }
|
|
|
4871
|
|
|
4872 void UpdateLayout()
|
|
|
4873 {
|
|
|
4874 RECT rect = {};
|
|
|
4875 this->GetClientRect(&rect);
|
|
|
4876
|
|
|
4877 int cyOffset = 0;
|
|
|
4878 if(m_tab.IsWindow() && ((m_tab.GetStyle() & WS_VISIBLE) != 0))
|
|
|
4879 {
|
|
|
4880 m_tab.SetWindowPos(NULL, 0, 0, rect.right - rect.left, m_cyTabHeight, SWP_NOZORDER);
|
|
|
4881 cyOffset = m_cyTabHeight;
|
|
|
4882 }
|
|
|
4883
|
|
|
4884 if(m_nActivePage != -1)
|
|
|
4885 ::SetWindowPos(GetPageHWND(m_nActivePage), NULL, 0, cyOffset, rect.right - rect.left, rect.bottom - rect.top - cyOffset, SWP_NOZORDER);
|
|
|
4886 }
|
|
|
4887
|
|
|
4888 void UpdateMenu()
|
|
|
4889 {
|
|
|
4890 if(m_menu.m_hMenu != NULL)
|
|
|
4891 BuildWindowMenu(m_menu, m_nMenuItemsCount, m_bEmptyMenuItem, m_bWindowsMenuItem, m_bActivePageMenuItem, m_bActiveAsDefaultMenuItem);
|
|
|
4892 }
|
|
|
4893
|
|
|
4894 void UpdateTitleBar()
|
|
|
4895 {
|
|
|
4896 if(!m_wndTitleBar.IsWindow() || (m_lpstrTitleBarBase == NULL))
|
|
|
4897 return; // nothing to do
|
|
|
4898
|
|
|
4899 if(m_nActivePage != -1)
|
|
|
4900 {
|
|
|
4901 T* pT = static_cast<T*>(this);
|
|
|
4902 LPCTSTR lpstrTitle = pT->GetPageTitle(m_nActivePage);
|
|
|
4903 LPCTSTR lpstrDivider = pT->GetTitleDividerText();
|
|
|
4904 int cchBuffer = m_cchTitleBarLength + lstrlen(lpstrDivider) + lstrlen(m_lpstrTitleBarBase) + 1;
|
|
|
4905 ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
|
|
|
4906 LPTSTR lpstrPageTitle = buff.Allocate(cchBuffer);
|
|
|
4907 ATLASSERT(lpstrPageTitle != NULL);
|
|
|
4908 if(lpstrPageTitle != NULL)
|
|
|
4909 {
|
|
|
4910 pT->ShortenTitle(lpstrTitle, lpstrPageTitle, m_cchTitleBarLength + 1);
|
|
|
4911 ATL::Checked::tcscat_s(lpstrPageTitle, cchBuffer, lpstrDivider);
|
|
|
4912 ATL::Checked::tcscat_s(lpstrPageTitle, cchBuffer, m_lpstrTitleBarBase);
|
|
|
4913 }
|
|
|
4914 else
|
|
|
4915 {
|
|
|
4916 lpstrPageTitle = m_lpstrTitleBarBase;
|
|
|
4917 }
|
|
|
4918
|
|
|
4919 m_wndTitleBar.SetWindowText(lpstrPageTitle);
|
|
|
4920 }
|
|
|
4921 else
|
|
|
4922 {
|
|
|
4923 m_wndTitleBar.SetWindowText(m_lpstrTitleBarBase);
|
|
|
4924 }
|
|
|
4925 }
|
|
|
4926
|
|
|
4927 void DrawMoveMark(int nItem)
|
|
|
4928 {
|
|
|
4929 T* pT = static_cast<T*>(this);
|
|
|
4930
|
|
|
4931 if(m_nInsertItem != -1)
|
|
|
4932 {
|
|
|
4933 RECT rect = {};
|
|
|
4934 pT->GetMoveMarkRect(rect);
|
|
|
4935 m_tab.InvalidateRect(&rect);
|
|
|
4936 }
|
|
|
4937
|
|
|
4938 m_nInsertItem = nItem;
|
|
|
4939
|
|
|
4940 if(m_nInsertItem != -1)
|
|
|
4941 {
|
|
|
4942 CClientDC dc(m_tab.m_hWnd);
|
|
|
4943
|
|
|
4944 RECT rect = {};
|
|
|
4945 pT->GetMoveMarkRect(rect);
|
|
|
4946
|
|
|
4947 CPen pen;
|
|
|
4948 pen.CreatePen(PS_SOLID, 1, ::GetSysColor(COLOR_WINDOWTEXT));
|
|
|
4949 CBrush brush;
|
|
|
4950 brush.CreateSolidBrush(::GetSysColor(COLOR_WINDOWTEXT));
|
|
|
4951
|
|
|
4952 HPEN hPenOld = dc.SelectPen(pen);
|
|
|
4953 HBRUSH hBrushOld = dc.SelectBrush(brush);
|
|
|
4954
|
|
|
4955 int x = rect.left;
|
|
|
4956 int y = rect.top;
|
|
|
4957 POINT ptsTop[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y + m_cyMoveMark } };
|
|
|
4958 dc.Polygon(ptsTop, 3);
|
|
|
4959
|
|
|
4960 y = rect.bottom - 1;
|
|
|
4961 POINT ptsBottom[3] = { { x, y }, { x + m_cxMoveMark, y }, { x + (m_cxMoveMark / 2), y - m_cyMoveMark } };
|
|
|
4962 dc.Polygon(ptsBottom, 3);
|
|
|
4963
|
|
|
4964 dc.SelectPen(hPenOld);
|
|
|
4965 dc.SelectBrush(hBrushOld);
|
|
|
4966 }
|
|
|
4967 }
|
|
|
4968
|
|
|
4969 void GetMoveMarkRect(RECT& rect) const
|
|
|
4970 {
|
|
|
4971 m_tab.GetClientRect(&rect);
|
|
|
4972
|
|
|
4973 RECT rcItem = {};
|
|
|
4974 m_tab.GetItemRect(m_nInsertItem, &rcItem);
|
|
|
4975
|
|
|
4976 if(m_nInsertItem <= m_nActivePage)
|
|
|
4977 {
|
|
|
4978 rect.left = rcItem.left - m_cxMoveMark / 2 - 1;
|
|
|
4979 rect.right = rcItem.left + m_cxMoveMark / 2;
|
|
|
4980 }
|
|
|
4981 else
|
|
|
4982 {
|
|
|
4983 rect.left = rcItem.right - m_cxMoveMark / 2 - 1;
|
|
|
4984 rect.right = rcItem.right + m_cxMoveMark / 2;
|
|
|
4985 }
|
|
|
4986 }
|
|
|
4987
|
|
|
4988 void SetMoveCursor(bool bCanMove)
|
|
|
4989 {
|
|
|
4990 ::SetCursor(::LoadCursor(NULL, bCanMove ? IDC_ARROW : IDC_NO));
|
|
|
4991 }
|
|
|
4992
|
|
|
4993 void GenerateDragImage(int nItem)
|
|
|
4994 {
|
|
|
4995 ATLASSERT(IsValidPageIndex(nItem));
|
|
|
4996
|
|
|
4997 RECT rcItem = {};
|
|
|
4998 m_tab.GetItemRect(nItem, &rcItem);
|
|
|
4999 ::InflateRect(&rcItem, 2, 2); // make bigger to cover selected item
|
|
|
5000
|
|
|
5001 ATLASSERT(m_ilDrag.m_hImageList == NULL);
|
|
|
5002 m_ilDrag.Create(rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, ILC_COLORDDB | ILC_MASK, 1, 1);
|
|
|
5003
|
|
|
5004 CClientDC dc(this->m_hWnd);
|
|
|
5005 CDC dcMem;
|
|
|
5006 dcMem.CreateCompatibleDC(dc);
|
|
|
5007 ATLASSERT(dcMem.m_hDC != NULL);
|
|
|
5008 dcMem.SetViewportOrg(-rcItem.left, -rcItem.top);
|
|
|
5009
|
|
|
5010 CBitmap bmp;
|
|
|
5011 bmp.CreateCompatibleBitmap(dc, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top);
|
|
|
5012 ATLASSERT(bmp.m_hBitmap != NULL);
|
|
|
5013
|
|
|
5014 HBITMAP hBmpOld = dcMem.SelectBitmap(bmp);
|
|
|
5015 m_tab.SendMessage(WM_PRINTCLIENT, (WPARAM)dcMem.m_hDC);
|
|
|
5016 dcMem.SelectBitmap(hBmpOld);
|
|
|
5017
|
|
|
5018 ATLVERIFY(m_ilDrag.Add(bmp.m_hBitmap, RGB(255, 0, 255)) != -1);
|
|
|
5019 }
|
|
|
5020
|
|
|
5021 void ShortenTitle(LPCTSTR lpstrTitle, LPTSTR lpstrShortTitle, int cchShortTitle)
|
|
|
5022 {
|
|
|
5023 if(lstrlen(lpstrTitle) >= cchShortTitle)
|
|
|
5024 {
|
|
|
5025 LPCTSTR lpstrEllipsis = _T("...");
|
|
|
5026 int cchEllipsis = lstrlen(lpstrEllipsis);
|
|
|
5027 ATL::Checked::tcsncpy_s(lpstrShortTitle, cchShortTitle, lpstrTitle, cchShortTitle - cchEllipsis - 1);
|
|
|
5028 ATL::Checked::tcscat_s(lpstrShortTitle, cchShortTitle, lpstrEllipsis);
|
|
|
5029 }
|
|
|
5030 else
|
|
|
5031 {
|
|
|
5032 ATL::Checked::tcscpy_s(lpstrShortTitle, cchShortTitle, lpstrTitle);
|
|
|
5033 }
|
|
|
5034 }
|
|
|
5035
|
|
|
5036 void UpdateTooltipText(LPNMTTDISPINFO pTTDI)
|
|
|
5037 {
|
|
|
5038 ATLASSERT(pTTDI != NULL);
|
|
|
5039 pTTDI->lpszText = (LPTSTR)GetPageTitle((int)pTTDI->hdr.idFrom);
|
|
|
5040 }
|
|
|
5041
|
|
|
5042 int DragHitTest(POINT pt) const
|
|
|
5043 {
|
|
|
5044 RECT rect = {};
|
|
|
5045 this->GetClientRect(&rect);
|
|
|
5046 if(::PtInRect(&rect, pt) == FALSE)
|
|
|
5047 return -1;
|
|
|
5048
|
|
|
5049 m_tab.GetClientRect(&rect);
|
|
|
5050 TCHITTESTINFO hti = {};
|
|
|
5051 hti.pt.x = pt.x;
|
|
|
5052 hti.pt.y = rect.bottom / 2; // use middle to ignore
|
|
|
5053 int nItem = m_tab.HitTest(&hti);
|
|
|
5054 if(nItem == -1)
|
|
|
5055 {
|
|
|
5056 int nLast = m_tab.GetItemCount() - 1;
|
|
|
5057 RECT rcItem = {};
|
|
|
5058 m_tab.GetItemRect(nLast, &rcItem);
|
|
|
5059 if(pt.x >= rcItem.right)
|
|
|
5060 nItem = nLast;
|
|
|
5061 }
|
|
|
5062
|
|
|
5063 return nItem;
|
|
|
5064 }
|
|
|
5065
|
|
|
5066 void StartStopAutoScroll(int x)
|
|
|
5067 {
|
|
|
5068 AutoScroll scroll = _AUTOSCROLL_NONE;
|
|
|
5069 if(x != -1)
|
|
|
5070 {
|
|
|
5071 RECT rect = {};
|
|
|
5072 m_tab.GetClientRect(&rect);
|
|
|
5073 int dx = ::GetSystemMetrics(SM_CXVSCROLL);
|
|
|
5074 if((x >= 0) && (x < dx))
|
|
|
5075 {
|
|
|
5076 RECT rcItem = {};
|
|
|
5077 m_tab.GetItemRect(0, &rcItem);
|
|
|
5078 if(rcItem.left < rect.left)
|
|
|
5079 scroll = _AUTOSCROLL_LEFT;
|
|
|
5080 }
|
|
|
5081 else if((x >= (rect.right - dx)) && (x < rect.right))
|
|
|
5082 {
|
|
|
5083 RECT rcItem = {};
|
|
|
5084 m_tab.GetItemRect(m_tab.GetItemCount() - 1, &rcItem);
|
|
|
5085 if(rcItem.right > rect.right)
|
|
|
5086 scroll = _AUTOSCROLL_RIGHT;
|
|
|
5087 }
|
|
|
5088 }
|
|
|
5089
|
|
|
5090 if(scroll != _AUTOSCROLL_NONE)
|
|
|
5091 {
|
|
|
5092 if(m_ud.m_hWnd == NULL)
|
|
|
5093 m_ud = m_tab.GetWindow(GW_CHILD);
|
|
|
5094
|
|
|
5095 if(m_AutoScroll != scroll)
|
|
|
5096 {
|
|
|
5097 m_AutoScroll = scroll;
|
|
|
5098 this->SetTimer(_nAutoScrollTimerID, 300);
|
|
|
5099 }
|
|
|
5100 }
|
|
|
5101 else
|
|
|
5102 {
|
|
|
5103 this->KillTimer(_nAutoScrollTimerID);
|
|
|
5104 m_AutoScroll = _AUTOSCROLL_NONE;
|
|
|
5105 }
|
|
|
5106 }
|
|
|
5107
|
|
|
5108 void DoAutoScroll()
|
|
|
5109 {
|
|
|
5110 ATLASSERT(m_AutoScroll != _AUTOSCROLL_NONE);
|
|
|
5111
|
|
|
5112 int nMin = -1, nMax = -1;
|
|
|
5113 m_ud.GetRange(nMin, nMax);
|
|
|
5114 int nPos = m_ud.GetPos();
|
|
|
5115
|
|
|
5116 int nNewPos = -1;
|
|
|
5117 if((m_AutoScroll == _AUTOSCROLL_LEFT) && (nPos > nMin))
|
|
|
5118 nNewPos = nPos - 1;
|
|
|
5119 else if((m_AutoScroll == _AUTOSCROLL_RIGHT) && (nPos < nMax))
|
|
|
5120 nNewPos = nPos + 1;
|
|
|
5121 if(nNewPos != -1)
|
|
|
5122 {
|
|
|
5123 m_tab.SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, nNewPos));
|
|
|
5124 m_tab.SendMessage(WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0));
|
|
|
5125
|
|
|
5126 POINT pt = {};
|
|
|
5127 ::GetCursorPos(&pt);
|
|
|
5128 m_tab.ScreenToClient(&pt);
|
|
|
5129 m_tab.SendMessage(WM_MOUSEMOVE, NULL, MAKELPARAM(pt.x, pt.y));
|
|
|
5130 }
|
|
|
5131 }
|
|
|
5132
|
|
|
5133 // Text for menu items and title bar - override to provide different strings
|
|
|
5134 static LPCTSTR GetEmptyListText()
|
|
|
5135 {
|
|
|
5136 return _T("(Empty)");
|
|
|
5137 }
|
|
|
5138
|
|
|
5139 static LPCTSTR GetWindowsMenuItemText()
|
|
|
5140 {
|
|
|
5141 return _T("&Windows...");
|
|
|
5142 }
|
|
|
5143
|
|
|
5144 static LPCTSTR GetTitleDividerText()
|
|
|
5145 {
|
|
|
5146 return _T(" - ");
|
|
|
5147 }
|
|
|
5148
|
|
|
5149 // Notifications - override to provide different behavior
|
|
|
5150 void OnPageActivated(int nPage)
|
|
|
5151 {
|
|
|
5152 NMHDR nmhdr = {};
|
|
|
5153 nmhdr.hwndFrom = this->m_hWnd;
|
|
|
5154 nmhdr.idFrom = nPage;
|
|
|
5155 nmhdr.code = TBVN_PAGEACTIVATED;
|
|
|
5156 this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&nmhdr);
|
|
|
5157 }
|
|
|
5158
|
|
|
5159 void OnContextMenu(int nPage, POINT pt)
|
|
|
5160 {
|
|
|
5161 TBVCONTEXTMENUINFO cmi = {};
|
|
|
5162 cmi.hdr.hwndFrom = this->m_hWnd;
|
|
|
5163 cmi.hdr.idFrom = nPage;
|
|
|
5164 cmi.hdr.code = TBVN_CONTEXTMENU;
|
|
|
5165 cmi.pt = pt;
|
|
|
5166 this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&cmi);
|
|
|
5167 }
|
|
|
5168
|
|
|
5169 void OnTabCloseBtn(int nPage)
|
|
|
5170 {
|
|
|
5171 NMHDR nmhdr = {};
|
|
|
5172 nmhdr.hwndFrom = this->m_hWnd;
|
|
|
5173 nmhdr.idFrom = nPage;
|
|
|
5174 nmhdr.code = TBVN_TABCLOSEBTN;
|
|
|
5175 LRESULT lRet = this->GetParent().SendMessage(WM_NOTIFY, this->GetDlgCtrlID(), (LPARAM)&nmhdr);
|
|
|
5176 if(lRet == 0) // default - close page
|
|
|
5177 {
|
|
|
5178 T* pT = static_cast<T*>(this);
|
|
|
5179 pT->RemovePage(m_nCloseItem);
|
|
|
5180 m_nCloseItem = -1;
|
|
|
5181 pT->DestroyCloseButton();
|
|
|
5182 }
|
|
|
5183 else
|
|
|
5184 {
|
|
|
5185 m_tab.SendMessage(WM_MOUSELEAVE);
|
|
|
5186 }
|
|
|
5187 }
|
|
|
5188
|
|
|
5189 // Close button overrideables
|
|
|
5190 void CreateCloseButton(int nItem)
|
|
|
5191 {
|
|
|
5192 ATLASSERT(m_btnClose.m_hWnd == NULL);
|
|
|
5193
|
|
|
5194 m_btnClose.m_bPressed = false;
|
|
|
5195
|
|
|
5196 T* pT = static_cast<T*>(this);
|
|
|
5197 RECT rcClose = {};
|
|
|
5198 pT->CalcCloseButtonRect(nItem, rcClose);
|
|
|
5199 m_btnClose.Create(m_tab.m_hWnd, rcClose, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, T::_nCloseBtnID);
|
|
|
5200 ATLASSERT(m_btnClose.IsWindow());
|
|
|
5201
|
|
|
5202 if(m_btnClose.m_hWnd != NULL)
|
|
|
5203 {
|
|
|
5204 // create a tool tip
|
|
|
5205 ATLASSERT(m_btnClose.m_tip.m_hWnd == NULL);
|
|
|
5206 m_btnClose.m_tip.Create(m_btnClose.m_hWnd);
|
|
|
5207 ATLASSERT(m_btnClose.m_tip.IsWindow());
|
|
|
5208
|
|
|
5209 if(m_btnClose.m_tip.IsWindow())
|
|
|
5210 {
|
|
|
5211 m_btnClose.m_tip.Activate(TRUE);
|
|
|
5212
|
|
|
5213 RECT rect = {};
|
|
|
5214 m_btnClose.GetClientRect(&rect);
|
|
|
5215 m_btnClose.m_tip.AddTool(m_btnClose.m_hWnd, LPSTR_TEXTCALLBACK, &rect, T::_nCloseBtnID);
|
|
|
5216 }
|
|
|
5217 }
|
|
|
5218 }
|
|
|
5219
|
|
|
5220 void DestroyCloseButton()
|
|
|
5221 {
|
|
|
5222 ATLASSERT(m_btnClose.m_hWnd != NULL);
|
|
|
5223
|
|
|
5224 if(m_btnClose.m_hWnd != NULL)
|
|
|
5225 {
|
|
|
5226 if(m_btnClose.m_tip.IsWindow())
|
|
|
5227 {
|
|
|
5228 m_btnClose.m_tip.DestroyWindow();
|
|
|
5229 m_btnClose.m_tip.m_hWnd = NULL;
|
|
|
5230 }
|
|
|
5231
|
|
|
5232 m_btnClose.DestroyWindow();
|
|
|
5233 }
|
|
|
5234 }
|
|
|
5235
|
|
|
5236 void CalcCloseButtonRect(int nItem, RECT& rcClose)
|
|
|
5237 {
|
|
|
5238 RECT rcItem = {};
|
|
|
5239 m_tab.GetItemRect(nItem, &rcItem);
|
|
|
5240
|
|
|
5241 int cy = (rcItem.bottom - rcItem.top - _cyCloseBtn) / 2;
|
|
|
5242 int cx = (nItem == m_tab.GetCurSel()) ? _cxCloseBtnMarginSel : _cxCloseBtnMargin;
|
|
|
5243 ::SetRect(&rcClose, rcItem.right - cx - _cxCloseBtn, rcItem.top + cy,
|
|
|
5244 rcItem.right - cx, rcItem.top + cy + _cyCloseBtn);
|
|
|
5245 }
|
|
|
5246 };
|
|
|
5247
|
|
|
5248 class CTabView : public CTabViewImpl<CTabView>
|
|
|
5249 {
|
|
|
5250 public:
|
|
|
5251 DECLARE_WND_CLASS_EX(_T("WTL_TabView"), 0, COLOR_APPWORKSPACE)
|
|
|
5252 };
|
|
|
5253
|
|
|
5254 } // namespace WTL
|
|
|
5255
|
|
|
5256 #endif // __ATLCTRLX_H__
|