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