PageRenderTime 139ms CodeModel.GetById 13ms app.highlight 109ms RepoModel.GetById 1ms app.codeStats 2ms

/thirdparty/wtl/atlctrlx.h

http://crashrpt.googlecode.com/
C++ Header | 4978 lines | 4061 code | 711 blank | 206 comment | 937 complexity | 6edefb128a55dda12e6890062fe322aa MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1// Windows Template Library - WTL version 8.1
   2// Copyright (C) Microsoft Corporation. All rights reserved.
   3//
   4// This file is a part of the Windows Template Library.
   5// The use and distribution terms for this software are covered by the
   6// Common Public License 1.0 (http://opensource.org/licenses/cpl1.0.php)
   7// which can be found in the file CPL.TXT at the root of this distribution.
   8// By using this software in any fashion, you are agreeing to be bound by
   9// the terms of this license. You must not remove this notice, or
  10// any other, from this software.
  11
  12#ifndef __ATLCTRLX_H__
  13#define __ATLCTRLX_H__
  14
  15#pragma once
  16
  17#ifndef __ATLAPP_H__
  18	#error atlctrlx.h requires atlapp.h to be included first
  19#endif
  20
  21#ifndef __ATLCTRLS_H__
  22	#error atlctrlx.h requires atlctrls.h to be included first
  23#endif
  24
  25#ifndef WM_UPDATEUISTATE
  26  #define WM_UPDATEUISTATE                0x0128
  27#endif // !WM_UPDATEUISTATE
  28
  29
  30///////////////////////////////////////////////////////////////////////////////
  31// Classes in this file:
  32//
  33// CBitmapButtonImpl<T, TBase, TWinTraits>
  34// CBitmapButton
  35// CCheckListViewCtrlImpl<T, TBase, TWinTraits>
  36// CCheckListViewCtrl
  37// CHyperLinkImpl<T, TBase, TWinTraits>
  38// CHyperLink
  39// CWaitCursor
  40// CCustomWaitCursor
  41// CMultiPaneStatusBarCtrlImpl<T, TBase>
  42// CMultiPaneStatusBarCtrl
  43// CPaneContainerImpl<T, TBase, TWinTraits>
  44// CPaneContainer
  45// CSortListViewImpl<T>
  46// CSortListViewCtrlImpl<T, TBase, TWinTraits>
  47// CSortListViewCtrl
  48// CTabViewImpl<T, TBase, TWinTraits>
  49// CTabView
  50
  51namespace WTL
  52{
  53
  54///////////////////////////////////////////////////////////////////////////////
  55// CBitmapButton - bitmap button implementation
  56
  57#ifndef _WIN32_WCE
  58
  59// bitmap button extended styles
  60#define BMPBTN_HOVER		0x00000001
  61#define BMPBTN_AUTO3D_SINGLE	0x00000002
  62#define BMPBTN_AUTO3D_DOUBLE	0x00000004
  63#define BMPBTN_AUTOSIZE		0x00000008
  64#define BMPBTN_SHAREIMAGELISTS	0x00000010
  65#define BMPBTN_AUTOFIRE		0x00000020
  66
  67template <class T, class TBase = CButton, class TWinTraits = ATL::CControlWinTraits>
  68class ATL_NO_VTABLE CBitmapButtonImpl : public ATL::CWindowImpl< T, TBase, TWinTraits>
  69{
  70public:
  71	DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
  72
  73	enum
  74	{
  75		_nImageNormal = 0,
  76		_nImagePushed,
  77		_nImageFocusOrHover,
  78		_nImageDisabled,
  79
  80		_nImageCount = 4,
  81	};
  82
  83	enum
  84	{
  85		ID_TIMER_FIRST = 1000,
  86		ID_TIMER_REPEAT = 1001
  87	};
  88
  89	// Bitmap button specific extended styles
  90	DWORD m_dwExtendedStyle;
  91
  92	CImageList m_ImageList;
  93	int m_nImage[_nImageCount];
  94
  95	CToolTipCtrl m_tip;
  96	LPTSTR m_lpstrToolTipText;
  97
  98	// Internal states
  99	unsigned m_fMouseOver:1;
 100	unsigned m_fFocus:1;
 101	unsigned m_fPressed:1;
 102
 103
 104// Constructor/Destructor
 105	CBitmapButtonImpl(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) : 
 106			m_ImageList(hImageList), m_dwExtendedStyle(dwExtendedStyle), 
 107			m_lpstrToolTipText(NULL),
 108			m_fMouseOver(0), m_fFocus(0), m_fPressed(0)
 109	{
 110		m_nImage[_nImageNormal] = -1;
 111		m_nImage[_nImagePushed] = -1;
 112		m_nImage[_nImageFocusOrHover] = -1;
 113		m_nImage[_nImageDisabled] = -1;
 114	}
 115
 116	~CBitmapButtonImpl()
 117	{
 118		if((m_dwExtendedStyle & BMPBTN_SHAREIMAGELISTS) == 0)
 119			m_ImageList.Destroy();
 120		delete [] m_lpstrToolTipText;
 121	}
 122
 123	// overridden to provide proper initialization
 124	BOOL SubclassWindow(HWND hWnd)
 125	{
 126#if (_MSC_VER >= 1300)
 127		BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
 128#else // !(_MSC_VER >= 1300)
 129		typedef ATL::CWindowImpl< T, TBase, TWinTraits>   _baseClass;
 130		BOOL bRet = _baseClass::SubclassWindow(hWnd);
 131#endif // !(_MSC_VER >= 1300)
 132		if(bRet)
 133			Init();
 134		return bRet;
 135	}
 136
 137// Attributes
 138	DWORD GetBitmapButtonExtendedStyle() const
 139	{
 140		return m_dwExtendedStyle;
 141	}
 142
 143	DWORD SetBitmapButtonExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
 144	{
 145		DWORD dwPrevStyle = m_dwExtendedStyle;
 146		if(dwMask == 0)
 147			m_dwExtendedStyle = dwExtendedStyle;
 148		else
 149			m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
 150		return dwPrevStyle;
 151	}
 152
 153	HIMAGELIST GetImageList() const
 154	{
 155		return m_ImageList;
 156	}
 157
 158	HIMAGELIST SetImageList(HIMAGELIST hImageList)
 159	{
 160		HIMAGELIST hImageListPrev = m_ImageList;
 161		m_ImageList = hImageList;
 162		if((m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0 && ::IsWindow(m_hWnd))
 163			SizeToImage();
 164		return hImageListPrev;
 165	}
 166
 167	int GetToolTipTextLength() const
 168	{
 169		return (m_lpstrToolTipText == NULL) ? -1 : lstrlen(m_lpstrToolTipText);
 170	}
 171
 172	bool GetToolTipText(LPTSTR lpstrText, int nLength) const
 173	{
 174		ATLASSERT(lpstrText != NULL);
 175		if(m_lpstrToolTipText == NULL)
 176			return false;
 177
 178		errno_t nRet = SecureHelper::strncpy_x(lpstrText, nLength, m_lpstrToolTipText, _TRUNCATE);
 179
 180		return (nRet == 0 || nRet == STRUNCATE);
 181	}
 182
 183	bool SetToolTipText(LPCTSTR lpstrText)
 184	{
 185		if(m_lpstrToolTipText != NULL)
 186		{
 187			delete [] m_lpstrToolTipText;
 188			m_lpstrToolTipText = NULL;
 189		}
 190
 191		if(lpstrText == NULL)
 192		{
 193			if(m_tip.IsWindow())
 194				m_tip.Activate(FALSE);
 195			return true;
 196		}
 197
 198		int cchLen = lstrlen(lpstrText) + 1;
 199		ATLTRY(m_lpstrToolTipText = new TCHAR[cchLen]);
 200		if(m_lpstrToolTipText == NULL)
 201			return false;
 202
 203		SecureHelper::strcpy_x(m_lpstrToolTipText, cchLen, lpstrText);
 204		if(m_tip.IsWindow())
 205		{
 206			m_tip.Activate(TRUE);
 207			m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
 208		}
 209
 210		return true;
 211	}
 212
 213// Operations
 214	void SetImages(int nNormal, int nPushed = -1, int nFocusOrHover = -1, int nDisabled = -1)
 215	{
 216		if(nNormal != -1)
 217			m_nImage[_nImageNormal] = nNormal;
 218		if(nPushed != -1)
 219			m_nImage[_nImagePushed] = nPushed;
 220		if(nFocusOrHover != -1)
 221			m_nImage[_nImageFocusOrHover] = nFocusOrHover;
 222		if(nDisabled != -1)
 223			m_nImage[_nImageDisabled] = nDisabled;
 224	}
 225
 226	BOOL SizeToImage()
 227	{
 228		ATLASSERT(::IsWindow(m_hWnd) && m_ImageList.m_hImageList != NULL);
 229		int cx = 0;
 230		int cy = 0;
 231		if(!m_ImageList.GetIconSize(cx, cy))
 232			return FALSE;
 233		return ResizeClient(cx, cy);
 234	}
 235
 236// Overrideables
 237	void DoPaint(CDCHandle dc)
 238	{
 239		ATLASSERT(m_ImageList.m_hImageList != NULL);   // image list must be set
 240		ATLASSERT(m_nImage[0] != -1);                  // main bitmap must be set
 241
 242		// set bitmap according to the current button state
 243		int nImage = -1;
 244		bool bHover = IsHoverMode();
 245		if(!IsWindowEnabled())
 246			nImage = m_nImage[_nImageDisabled];
 247		else if(m_fPressed == 1)
 248			nImage = m_nImage[_nImagePushed];
 249		else if((!bHover && m_fFocus == 1) || (bHover && m_fMouseOver == 1))
 250			nImage = m_nImage[_nImageFocusOrHover];
 251		if(nImage == -1)   // not there, use default one
 252			nImage = m_nImage[_nImageNormal];
 253
 254		// draw the button image
 255		int xyPos = 0;
 256		if((m_fPressed == 1) && ((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0) && (m_nImage[_nImagePushed] == -1))
 257			xyPos = 1;
 258		m_ImageList.Draw(dc, nImage, xyPos, xyPos, ILD_NORMAL);
 259
 260		// draw 3D border if required
 261		if((m_dwExtendedStyle & (BMPBTN_AUTO3D_SINGLE | BMPBTN_AUTO3D_DOUBLE)) != 0)
 262		{
 263			RECT rect;
 264			GetClientRect(&rect);
 265
 266			if(m_fPressed == 1)
 267				dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_SUNKENOUTER : EDGE_SUNKEN, BF_RECT);
 268			else if(!bHover || m_fMouseOver == 1)
 269				dc.DrawEdge(&rect, ((m_dwExtendedStyle & BMPBTN_AUTO3D_SINGLE) != 0) ? BDR_RAISEDINNER : EDGE_RAISED, BF_RECT);
 270
 271			if(!bHover && m_fFocus == 1)
 272			{
 273				::InflateRect(&rect, -2 * ::GetSystemMetrics(SM_CXEDGE), -2 * ::GetSystemMetrics(SM_CYEDGE));
 274				dc.DrawFocusRect(&rect);
 275			}
 276		}
 277	}
 278
 279// Message map and handlers
 280	BEGIN_MSG_MAP(CBitmapButtonImpl)
 281		MESSAGE_HANDLER(WM_CREATE, OnCreate)
 282		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
 283		MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
 284		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
 285		MESSAGE_HANDLER(WM_PAINT, OnPaint)
 286		MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
 287		MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
 288		MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
 289		MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
 290		MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDblClk)
 291		MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
 292		MESSAGE_HANDLER(WM_CAPTURECHANGED, OnCaptureChanged)
 293		MESSAGE_HANDLER(WM_ENABLE, OnEnable)
 294		MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
 295		MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
 296		MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
 297		MESSAGE_HANDLER(WM_KEYUP, OnKeyUp)
 298		MESSAGE_HANDLER(WM_TIMER, OnTimer)
 299		MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
 300	END_MSG_MAP()
 301
 302	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
 303	{
 304		Init();
 305		bHandled = FALSE;
 306		return 1;
 307	}
 308
 309	LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
 310	{
 311		if(m_tip.IsWindow())
 312		{
 313			m_tip.DestroyWindow();
 314			m_tip.m_hWnd = NULL;
 315		}
 316		bHandled = FALSE;
 317		return 1;
 318	}
 319
 320	LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 321	{
 322		MSG msg = { m_hWnd, uMsg, wParam, lParam };
 323		if(m_tip.IsWindow())
 324			m_tip.RelayEvent(&msg);
 325		bHandled = FALSE;
 326		return 1;
 327	}
 328
 329	LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
 330	{
 331		return 1;   // no background needed
 332	}
 333
 334	LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
 335	{
 336		T* pT = static_cast<T*>(this);
 337		if(wParam != NULL)
 338		{
 339			pT->DoPaint((HDC)wParam);
 340		}
 341		else
 342		{
 343			CPaintDC dc(m_hWnd);
 344			pT->DoPaint(dc.m_hDC);
 345		}
 346		return 0;
 347	}
 348
 349	LRESULT OnFocus(UINT uMsg, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
 350	{
 351		m_fFocus = (uMsg == WM_SETFOCUS) ? 1 : 0;
 352		Invalidate();
 353		UpdateWindow();
 354		bHandled = FALSE;
 355		return 1;
 356	}
 357
 358	LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
 359	{
 360		LRESULT lRet = 0;
 361		if(IsHoverMode())
 362			SetCapture();
 363		else
 364			lRet = DefWindowProc(uMsg, wParam, lParam);
 365		if(::GetCapture() == m_hWnd)
 366		{
 367			m_fPressed = 1;
 368			Invalidate();
 369			UpdateWindow();
 370		}
 371		if((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0)
 372		{
 373			int nElapse = 250;
 374			int nDelay = 0;
 375			if(::SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &nDelay, 0))
 376				nElapse += nDelay * 250;   // all milli-seconds
 377			SetTimer(ID_TIMER_FIRST, nElapse);
 378		}
 379		return lRet;
 380	}
 381
 382	LRESULT OnLButtonDblClk(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
 383	{
 384		LRESULT lRet = 0;
 385		if(!IsHoverMode())
 386			lRet = DefWindowProc(uMsg, wParam, lParam);
 387		if(::GetCapture() != m_hWnd)
 388			SetCapture();
 389		if(m_fPressed == 0)
 390		{
 391			m_fPressed = 1;
 392			Invalidate();
 393			UpdateWindow();
 394		}
 395		return lRet;
 396	}
 397
 398	LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
 399	{
 400		LRESULT lRet = 0;
 401		bool bHover = IsHoverMode();
 402		if(!bHover)
 403			lRet = DefWindowProc(uMsg, wParam, lParam);
 404		if(::GetCapture() == m_hWnd)
 405		{
 406			if(bHover && m_fPressed == 1)
 407				::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
 408			::ReleaseCapture();
 409		}
 410		return lRet;
 411	}
 412
 413	LRESULT OnCaptureChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
 414	{
 415		if(m_fPressed == 1)
 416		{
 417			m_fPressed = 0;
 418			Invalidate();
 419			UpdateWindow();
 420		}
 421		bHandled = FALSE;
 422		return 1;
 423	}
 424
 425	LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
 426	{
 427		Invalidate();
 428		UpdateWindow();
 429		bHandled = FALSE;
 430		return 1;
 431	}
 432
 433	LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
 434	{
 435		if(::GetCapture() == m_hWnd)
 436		{
 437			POINT ptCursor = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
 438			ClientToScreen(&ptCursor);
 439			RECT rect = { 0 };
 440			GetWindowRect(&rect);
 441			unsigned int uPressed = ::PtInRect(&rect, ptCursor) ? 1 : 0;
 442			if(m_fPressed != uPressed)
 443			{
 444				m_fPressed = uPressed;
 445				Invalidate();
 446				UpdateWindow();
 447			}
 448		}
 449		else if(IsHoverMode() && m_fMouseOver == 0)
 450		{
 451			m_fMouseOver = 1;
 452			Invalidate();
 453			UpdateWindow();
 454			StartTrackMouseLeave();
 455		}
 456		bHandled = FALSE;
 457		return 1;
 458	}
 459
 460	LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
 461	{
 462		if(m_fMouseOver == 1)
 463		{
 464			m_fMouseOver = 0;
 465			Invalidate();
 466			UpdateWindow();
 467		}
 468		return 0;
 469	}
 470
 471	LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
 472	{
 473		if(wParam == VK_SPACE && IsHoverMode())
 474			return 0;   // ignore if in hover mode
 475		if(wParam == VK_SPACE && m_fPressed == 0)
 476		{
 477			m_fPressed = 1;
 478			Invalidate();
 479			UpdateWindow();
 480		}
 481		bHandled = FALSE;
 482		return 1;
 483	}
 484
 485	LRESULT OnKeyUp(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
 486	{
 487		if(wParam == VK_SPACE && IsHoverMode())
 488			return 0;   // ignore if in hover mode
 489		if(wParam == VK_SPACE && m_fPressed == 1)
 490		{
 491			m_fPressed = 0;
 492			Invalidate();
 493			UpdateWindow();
 494		}
 495		bHandled = FALSE;
 496		return 1;
 497	}
 498
 499	LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
 500	{
 501		ATLASSERT((m_dwExtendedStyle & BMPBTN_AUTOFIRE) != 0);
 502		switch(wParam)   // timer ID
 503		{
 504		case ID_TIMER_FIRST:
 505			KillTimer(ID_TIMER_FIRST);
 506			if(m_fPressed == 1)
 507			{
 508				::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
 509				int nElapse = 250;
 510				int nRepeat = 40;
 511				if(::SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &nRepeat, 0))
 512					nElapse = 10000 / (10 * nRepeat + 25);   // milli-seconds, approximated
 513				SetTimer(ID_TIMER_REPEAT, nElapse);
 514			}
 515			break;
 516		case ID_TIMER_REPEAT:
 517			if(m_fPressed == 1)
 518				::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
 519			else if(::GetCapture() != m_hWnd)
 520				KillTimer(ID_TIMER_REPEAT);
 521			break;
 522		default:	// not our timer
 523			break;
 524		}
 525		return 0;
 526	}
 527
 528	LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
 529	{
 530		// If the control is subclassed or superclassed, this message can cause
 531		// repainting without WM_PAINT. We don't use this state, so just do nothing.
 532		return 0;
 533	}
 534
 535// Implementation
 536	void Init()
 537	{
 538		// We need this style to prevent Windows from painting the button
 539		ModifyStyle(0, BS_OWNERDRAW);
 540
 541		// create a tool tip
 542		m_tip.Create(m_hWnd);
 543		ATLASSERT(m_tip.IsWindow());
 544		if(m_tip.IsWindow() && m_lpstrToolTipText != NULL)
 545		{
 546			m_tip.Activate(TRUE);
 547			m_tip.AddTool(m_hWnd, m_lpstrToolTipText);
 548		}
 549
 550		if(m_ImageList.m_hImageList != NULL && (m_dwExtendedStyle & BMPBTN_AUTOSIZE) != 0)
 551			SizeToImage();
 552	}
 553
 554	BOOL StartTrackMouseLeave()
 555	{
 556		TRACKMOUSEEVENT tme = { 0 };
 557		tme.cbSize = sizeof(tme);
 558		tme.dwFlags = TME_LEAVE;
 559		tme.hwndTrack = m_hWnd;
 560		return _TrackMouseEvent(&tme);
 561	}
 562
 563	bool IsHoverMode() const
 564	{
 565		return ((m_dwExtendedStyle & BMPBTN_HOVER) != 0);
 566	}
 567};
 568
 569class CBitmapButton : public CBitmapButtonImpl<CBitmapButton>
 570{
 571public:
 572	DECLARE_WND_SUPERCLASS(_T("WTL_BitmapButton"), GetWndClassName())
 573
 574	CBitmapButton(DWORD dwExtendedStyle = BMPBTN_AUTOSIZE, HIMAGELIST hImageList = NULL) : 
 575		CBitmapButtonImpl<CBitmapButton>(dwExtendedStyle, hImageList)
 576	{ }
 577};
 578
 579#endif // !_WIN32_WCE
 580
 581
 582///////////////////////////////////////////////////////////////////////////////
 583// CCheckListCtrlView - list view control with check boxes
 584
 585template <DWORD t_dwStyle, DWORD t_dwExStyle, DWORD t_dwExListViewStyle>
 586class CCheckListViewCtrlImplTraits
 587{
 588public:
 589	static DWORD GetWndStyle(DWORD dwStyle)
 590	{
 591		return (dwStyle == 0) ? t_dwStyle : dwStyle;
 592	}
 593
 594	static DWORD GetWndExStyle(DWORD dwExStyle)
 595	{
 596		return (dwExStyle == 0) ? t_dwExStyle : dwExStyle;
 597	}
 598
 599	static DWORD GetExtendedLVStyle()
 600	{
 601		return t_dwExListViewStyle;
 602	}
 603};
 604
 605typedef CCheckListViewCtrlImplTraits<WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SHOWSELALWAYS, WS_EX_CLIENTEDGE, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT>   CCheckListViewCtrlTraits;
 606
 607template <class T, class TBase = CListViewCtrl, class TWinTraits = CCheckListViewCtrlTraits>
 608class ATL_NO_VTABLE CCheckListViewCtrlImpl : public ATL::CWindowImpl<T, TBase, TWinTraits>
 609{
 610public:
 611	DECLARE_WND_SUPERCLASS(NULL, TBase::GetWndClassName())
 612
 613// Attributes
 614	static DWORD GetExtendedLVStyle()
 615	{
 616		return TWinTraits::GetExtendedLVStyle();
 617	}
 618
 619// Operations
 620	BOOL SubclassWindow(HWND hWnd)
 621	{
 622#if (_MSC_VER >= 1300)
 623		BOOL bRet = ATL::CWindowImplBaseT< TBase, TWinTraits>::SubclassWindow(hWnd);
 624#else // !(_MSC_VER >= 1300)
 625		typedef ATL::CWindowImplBaseT< TBase, TWinTraits>   _baseClass;
 626		BOOL bRet = _baseClass::SubclassWindow(hWnd);
 627#endif // !(_MSC_VER >= 1300)
 628		if(bRet)
 629		{
 630			T* pT = static_cast<T*>(this);
 631			pT;
 632			ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
 633			SetExtendedListViewStyle(pT->GetExtendedLVStyle());
 634		}
 635		return bRet;
 636	}
 637
 638	void CheckSelectedItems(int nCurrItem)
 639	{
 640		// first check if this item is selected
 641		LVITEM lvi = { 0 };
 642		lvi.iItem = nCurrItem;
 643		lvi.iSubItem = 0;
 644		lvi.mask = LVIF_STATE;
 645		lvi.stateMask = LVIS_SELECTED;
 646		GetItem(&lvi);
 647		// if item is not selected, don't do anything
 648		if(!(lvi.state & LVIS_SELECTED))
 649			return;
 650		// new check state will be reverse of the current state,
 651		BOOL bCheck = !GetCheckState(nCurrItem);
 652		int nItem = -1;
 653		int nOldItem = -1;
 654		while((nItem = GetNextItem(nOldItem, LVNI_SELECTED)) != -1)
 655		{
 656			if(nItem != nCurrItem)
 657				SetCheckState(nItem, bCheck);
 658			nOldItem = nItem;
 659		}
 660	}
 661
 662// Implementation
 663	BEGIN_MSG_MAP(CCheckListViewCtrlImpl)
 664		MESSAGE_HANDLER(WM_CREATE, OnCreate)
 665		MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
 666		MESSAGE_HANDLER(WM_LBUTTONDBLCLK, OnLButtonDown)
 667		MESSAGE_HANDLER(WM_KEYDOWN, OnKeyDown)
 668	END_MSG_MAP()
 669
 670	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
 671	{
 672		// first let list view control initialize everything
 673		LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
 674		T* pT = static_cast<T*>(this);
 675		pT;
 676		ATLASSERT((pT->GetExtendedLVStyle() & LVS_EX_CHECKBOXES) != 0);
 677		SetExtendedListViewStyle(pT->GetExtendedLVStyle());
 678		return lRet;
 679	}
 680
 681	LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
 682	{
 683		POINT ptMsg = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
 684		LVHITTESTINFO lvh = { 0 };
 685		lvh.pt = ptMsg;
 686		if(HitTest(&lvh) != -1 && lvh.flags == LVHT_ONITEMSTATEICON && ::GetKeyState(VK_CONTROL) >= 0)
 687		{
 688			T* pT = static_cast<T*>(this);
 689			pT->CheckSelectedItems(lvh.iItem);
 690		}
 691		bHandled = FALSE;
 692		return 1;
 693	}
 694
 695	LRESULT OnKeyDown(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
 696	{
 697		if(wParam == VK_SPACE)
 698		{
 699			int nCurrItem = GetNextItem(-1, LVNI_FOCUSED);
 700			if(nCurrItem != -1  && ::GetKeyState(VK_CONTROL) >= 0)
 701			{
 702				T* pT = static_cast<T*>(this);
 703				pT->CheckSelectedItems(nCurrItem);
 704			}
 705		}
 706		bHandled = FALSE;
 707		return 1;
 708	}
 709};
 710
 711class CCheckListViewCtrl : public CCheckListViewCtrlImpl<CCheckListViewCtrl>
 712{
 713public:
 714	DECLARE_WND_SUPERCLASS(_T("WTL_CheckListView"), GetWndClassName())
 715};
 716
 717
 718///////////////////////////////////////////////////////////////////////////////
 719// CHyperLink - hyper link control implementation
 720
 721#if (WINVER < 0x0500) && !defined(_WIN32_WCE)
 722__declspec(selectany) struct
 723{
 724	enum { cxWidth = 32, cyHeight = 32 };
 725	int xHotSpot;
 726	int yHotSpot;
 727	unsigned char arrANDPlane[cxWidth * cyHeight / 8];
 728	unsigned char arrXORPlane[cxWidth * cyHeight / 8];
 729} _AtlHyperLink_CursorData = 
 730{
 731	5, 0, 
 732	{
 733		0xF9, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFF, 
 734		0xF0, 0xFF, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0, 0x01, 0xFF, 0xFF, 
 735		0xF0, 0x00, 0xFF, 0xFF, 0x10, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00, 0x7F, 0xFF, 
 736		0x80, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x7F, 0xFF, 
 737		0xE0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 
 738		0xF8, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 739		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
 740		0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
 741	},
 742	{
 743		0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 
 744		0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0xC0, 0x00, 0x00, 0x06, 0xD8, 0x00, 0x00, 
 745		0x06, 0xDA, 0x00, 0x00, 0x06, 0xDB, 0x00, 0x00, 0x67, 0xFB, 0x00, 0x00, 0x77, 0xFF, 0x00, 0x00, 
 746		0x37, 0xFF, 0x00, 0x00, 0x17, 0xFF, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x00, 
 747		0x0F, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x07, 0xFE, 0x00, 0x00, 0x03, 0xFC, 0x00, 0x00, 
 748		0x03, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 749		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
 750		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
 751	}
 752};
 753#endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
 754
 755#define HLINK_UNDERLINED           0x00000000
 756#define HLINK_NOTUNDERLINED        0x00000001
 757#define HLINK_UNDERLINEHOVER       0x00000002
 758#define HLINK_COMMANDBUTTON        0x00000004
 759#define HLINK_NOTIFYBUTTON         0x0000000C
 760#define HLINK_USETAGS              0x00000010
 761#define HLINK_USETAGSBOLD          0x00000030
 762#define HLINK_NOTOOLTIP            0x00000040
 763#define HLINK_AUTOCREATELINKFONT   0x00000080
 764#define HLINK_SINGLELINE           0x00000100
 765
 766// Notes:
 767// - HLINK_USETAGS and HLINK_USETAGSBOLD are always left-aligned
 768// - When HLINK_USETAGSBOLD is used, the underlined styles will be ignored
 769
 770template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
 771class ATL_NO_VTABLE CHyperLinkImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >
 772{
 773public:
 774	LPTSTR m_lpstrLabel;
 775	LPTSTR m_lpstrHyperLink;
 776
 777	HCURSOR m_hCursor;
 778	HFONT m_hFontLink;
 779	HFONT m_hFontNormal;
 780
 781	RECT m_rcLink;
 782#ifndef _WIN32_WCE
 783	CToolTipCtrl m_tip;
 784#endif // !_WIN32_WCE
 785
 786	COLORREF m_clrLink;
 787	COLORREF m_clrVisited;
 788
 789	DWORD m_dwExtendedStyle;   // Hyper Link specific extended styles
 790
 791	bool m_bPaintLabel:1;
 792	bool m_bVisited:1;
 793	bool m_bHover:1;
 794	bool m_bInternalLinkFont:1;
 795	bool m_bInternalNormalFont:1;
 796
 797
 798// Constructor/Destructor
 799	CHyperLinkImpl(DWORD dwExtendedStyle = HLINK_UNDERLINED) : 
 800			m_lpstrLabel(NULL), m_lpstrHyperLink(NULL),
 801			m_hCursor(NULL), m_hFontLink(NULL), m_hFontNormal(NULL),
 802			m_clrLink(RGB(0, 0, 255)), m_clrVisited(RGB(128, 0, 128)),
 803			m_dwExtendedStyle(dwExtendedStyle),
 804			m_bPaintLabel(true), m_bVisited(false),
 805			m_bHover(false), m_bInternalLinkFont(false), m_bInternalNormalFont(false)
 806	{
 807		::SetRectEmpty(&m_rcLink);
 808	}
 809
 810	~CHyperLinkImpl()
 811	{
 812		delete [] m_lpstrLabel;
 813		delete [] m_lpstrHyperLink;
 814#if (WINVER < 0x0500) && !defined(_WIN32_WCE)
 815		// It was created, not loaded, so we have to destroy it
 816		if(m_hCursor != NULL)
 817			::DestroyCursor(m_hCursor);
 818#endif // (WINVER < 0x0500) && !defined(_WIN32_WCE)
 819	}
 820
 821// Attributes
 822	DWORD GetHyperLinkExtendedStyle() const
 823	{
 824		return m_dwExtendedStyle;
 825	}
 826
 827	DWORD SetHyperLinkExtendedStyle(DWORD dwExtendedStyle, DWORD dwMask = 0)
 828	{
 829		DWORD dwPrevStyle = m_dwExtendedStyle;
 830		if(dwMask == 0)
 831			m_dwExtendedStyle = dwExtendedStyle;
 832		else
 833			m_dwExtendedStyle = (m_dwExtendedStyle & ~dwMask) | (dwExtendedStyle & dwMask);
 834		return dwPrevStyle;
 835	}
 836
 837	bool GetLabel(LPTSTR lpstrBuffer, int nLength) const
 838	{
 839		if(m_lpstrLabel == NULL)
 840			return false;
 841		ATLASSERT(lpstrBuffer != NULL);
 842		if(nLength <= lstrlen(m_lpstrLabel))
 843			return false;
 844
 845		SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrLabel);
 846
 847		return true;
 848	}
 849
 850	bool SetLabel(LPCTSTR lpstrLabel)
 851	{
 852		delete [] m_lpstrLabel;
 853		m_lpstrLabel = NULL;
 854		int cchLen = lstrlen(lpstrLabel) + 1;
 855		ATLTRY(m_lpstrLabel = new TCHAR[cchLen]);
 856		if(m_lpstrLabel == NULL)
 857			return false;
 858
 859		SecureHelper::strcpy_x(m_lpstrLabel, cchLen, lpstrLabel);
 860		T* pT = static_cast<T*>(this);
 861		pT->CalcLabelRect();
 862
 863		if(m_hWnd != NULL)
 864			SetWindowText(lpstrLabel);   // Set this for accessibility
 865
 866		return true;
 867	}
 868
 869	bool GetHyperLink(LPTSTR lpstrBuffer, int nLength) const
 870	{
 871		if(m_lpstrHyperLink == NULL)
 872			return false;
 873		ATLASSERT(lpstrBuffer != NULL);
 874		if(nLength <= lstrlen(m_lpstrHyperLink))
 875			return false;
 876
 877		SecureHelper::strcpy_x(lpstrBuffer, nLength, m_lpstrHyperLink);
 878
 879		return true;
 880	}
 881
 882	bool SetHyperLink(LPCTSTR lpstrLink)
 883	{
 884		delete [] m_lpstrHyperLink;
 885		m_lpstrHyperLink = NULL;
 886		int cchLen = lstrlen(lpstrLink) + 1;
 887		ATLTRY(m_lpstrHyperLink = new TCHAR[cchLen]);
 888		if(m_lpstrHyperLink == NULL)
 889			return false;
 890
 891		SecureHelper::strcpy_x(m_lpstrHyperLink, cchLen, lpstrLink);
 892		if(m_lpstrLabel == NULL)
 893		{
 894			T* pT = static_cast<T*>(this);
 895			pT->CalcLabelRect();
 896		}
 897#ifndef _WIN32_WCE
 898		if(m_tip.IsWindow())
 899		{
 900			m_tip.Activate(TRUE);
 901			m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
 902		}
 903#endif // !_WIN32_WCE
 904		return true;
 905	}
 906
 907	HFONT GetLinkFont() const
 908	{
 909		return m_hFontLink;
 910	}
 911
 912	void SetLinkFont(HFONT hFont)
 913	{
 914		if(m_bInternalLinkFont)
 915		{
 916			::DeleteObject(m_hFontLink);
 917			m_bInternalLinkFont = false;
 918		}
 919
 920		m_hFontLink = hFont;
 921
 922		T* pT = static_cast<T*>(this);
 923		pT->CalcLabelRect();
 924	}
 925
 926	int GetIdealHeight() const
 927	{
 928		ATLASSERT(::IsWindow(m_hWnd));
 929		if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
 930			return -1;
 931		if(!m_bPaintLabel)
 932			return -1;
 933
 934		UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
 935
 936		CClientDC dc(m_hWnd);
 937		RECT rect = { 0 };
 938		GetClientRect(&rect);
 939		HFONT hFontOld = dc.SelectFont(m_hFontNormal);
 940		RECT rcText = rect;
 941		dc.DrawText(_T("NS"), -1, &rcText, DT_LEFT | uFormat | DT_CALCRECT);
 942		dc.SelectFont(m_hFontLink);
 943		RECT rcLink = rect;
 944		dc.DrawText(_T("NS"), -1, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
 945		dc.SelectFont(hFontOld);
 946		return max(rcText.bottom - rcText.top, rcLink.bottom - rcLink.top);
 947	}
 948
 949	bool GetIdealSize(SIZE& size) const
 950	{
 951		int cx = 0, cy = 0;
 952		bool bRet = GetIdealSize(cx, cy);
 953		if(bRet)
 954		{
 955			size.cx = cx;
 956			size.cy = cy;
 957		}
 958		return bRet;
 959	}
 960
 961	bool GetIdealSize(int& cx, int& cy) const
 962	{
 963		ATLASSERT(::IsWindow(m_hWnd));
 964		if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
 965			return false;
 966		if(!m_bPaintLabel)
 967			return false;
 968
 969		CClientDC dc(m_hWnd);
 970		RECT rcClient = { 0 };
 971		GetClientRect(&rcClient);
 972		RECT rcAll = rcClient;
 973
 974		if(IsUsingTags())
 975		{
 976			// find tags and label parts
 977			LPTSTR lpstrLeft = NULL;
 978			int cchLeft = 0;
 979			LPTSTR lpstrLink = NULL;
 980			int cchLink = 0;
 981			LPTSTR lpstrRight = NULL;
 982			int cchRight = 0;
 983
 984			const T* pT = static_cast<const T*>(this);
 985			pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
 986
 987			// get label part rects
 988			UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
 989
 990			HFONT hFontOld = dc.SelectFont(m_hFontNormal);
 991			RECT rcLeft = rcClient;
 992			dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
 993
 994			dc.SelectFont(m_hFontLink);
 995			RECT rcLink = { rcLeft.right, rcLeft.top, rcClient.right, rcClient.bottom };
 996			dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
 997
 998			dc.SelectFont(m_hFontNormal);
 999			RECT rcRight = { rcLink.right, rcLink.top, rcClient.right, rcClient.bottom };
1000			dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat | DT_CALCRECT);
1001
1002			dc.SelectFont(hFontOld);
1003
1004			int cyMax = max(rcLeft.bottom, max(rcLink.bottom, rcRight.bottom));
1005			::SetRect(&rcAll, rcLeft.left, rcLeft.top, rcRight.right, cyMax);
1006		}
1007		else
1008		{
1009			HFONT hOldFont = NULL;
1010			if(m_hFontLink != NULL)
1011				hOldFont = dc.SelectFont(m_hFontLink);
1012			LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1013			DWORD dwStyle = GetStyle();
1014			UINT uFormat = DT_LEFT;
1015			if (dwStyle & SS_CENTER)
1016				uFormat = DT_CENTER;
1017			else if (dwStyle & SS_RIGHT)
1018				uFormat = DT_RIGHT;
1019			uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1020			dc.DrawText(lpstrText, -1, &rcAll, uFormat | DT_CALCRECT);
1021			if(m_hFontLink != NULL)
1022				dc.SelectFont(hOldFont);
1023			if (dwStyle & SS_CENTER)
1024			{
1025				int dx = (rcClient.right - rcAll.right) / 2;
1026				::OffsetRect(&rcAll, dx, 0);
1027			}
1028			else if (dwStyle & SS_RIGHT)
1029			{
1030				int dx = rcClient.right - rcAll.right;
1031				::OffsetRect(&rcAll, dx, 0);
1032			}
1033		}
1034
1035		cx = rcAll.right - rcAll.left;
1036		cy = rcAll.bottom - rcAll.top;
1037
1038		return true;
1039	}
1040
1041	// for command buttons only
1042	bool GetToolTipText(LPTSTR lpstrBuffer, int nLength) const
1043	{
1044		ATLASSERT(IsCommandButton());
1045		return GetHyperLink(lpstrBuffer, nLength);
1046	}
1047
1048	bool SetToolTipText(LPCTSTR lpstrToolTipText)
1049	{
1050		ATLASSERT(IsCommandButton());
1051		return SetHyperLink(lpstrToolTipText);
1052	}
1053
1054// Operations
1055	BOOL SubclassWindow(HWND hWnd)
1056	{
1057		ATLASSERT(m_hWnd == NULL);
1058		ATLASSERT(::IsWindow(hWnd));
1059		if(m_hFontNormal == NULL)
1060			m_hFontNormal = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0L);
1061#if (_MSC_VER >= 1300)
1062		BOOL bRet = ATL::CWindowImpl< T, TBase, TWinTraits>::SubclassWindow(hWnd);
1063#else // !(_MSC_VER >= 1300)
1064		typedef ATL::CWindowImpl< T, TBase, TWinTraits>   _baseClass;
1065		BOOL bRet = _baseClass::SubclassWindow(hWnd);
1066#endif // !(_MSC_VER >= 1300)
1067		if(bRet)
1068		{
1069			T* pT = static_cast<T*>(this);
1070			pT->Init();
1071		}
1072		return bRet;
1073	}
1074
1075	bool Navigate()
1076	{
1077		ATLASSERT(::IsWindow(m_hWnd));
1078		bool bRet = true;
1079		if(IsNotifyButton())
1080		{
1081			NMHDR nmhdr = { m_hWnd, GetDlgCtrlID(), NM_CLICK };
1082			::SendMessage(GetParent(), WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&nmhdr);
1083		}
1084		else if(IsCommandButton())
1085		{
1086			::SendMessage(GetParent(), WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(), BN_CLICKED), (LPARAM)m_hWnd);
1087		}
1088		else
1089		{
1090			ATLASSERT(m_lpstrHyperLink != NULL);
1091#ifndef _WIN32_WCE
1092			DWORD_PTR dwRet = (DWORD_PTR)::ShellExecute(0, _T("open"), m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL);
1093			bRet = (dwRet > 32);
1094#else // CE specific
1095			SHELLEXECUTEINFO shExeInfo = { sizeof(SHELLEXECUTEINFO), 0, 0, L"open", m_lpstrHyperLink, 0, 0, SW_SHOWNORMAL, 0, 0, 0, 0, 0, 0, 0 };
1096			::ShellExecuteEx(&shExeInfo);
1097			DWORD_PTR dwRet = (DWORD_PTR)shExeInfo.hInstApp;
1098			bRet = (dwRet == 0) || (dwRet > 32);
1099#endif // _WIN32_WCE
1100			ATLASSERT(bRet);
1101			if(bRet)
1102			{
1103				m_bVisited = true;
1104				Invalidate();
1105			}
1106		}
1107		return bRet;
1108	}
1109
1110	void CreateLinkFontFromNormal()
1111	{
1112		if(m_bInternalLinkFont)
1113		{
1114			::DeleteObject(m_hFontLink);
1115			m_bInternalLinkFont = false;
1116		}
1117
1118		CFontHandle font = (m_hFontNormal != NULL) ? m_hFontNormal : (HFONT)::GetStockObject(SYSTEM_FONT);
1119		LOGFONT lf = { 0 };
1120		font.GetLogFont(&lf);
1121
1122		if(IsUsingTagsBold())
1123			lf.lfWeight = FW_BOLD;
1124		else if(!IsNotUnderlined())
1125			lf.lfUnderline = TRUE;
1126
1127		m_hFontLink = ::CreateFontIndirect(&lf);
1128		m_bInternalLinkFont = true;
1129		ATLASSERT(m_hFontLink != NULL);
1130	}
1131
1132// Message map and handlers
1133	BEGIN_MSG_MAP(CHyperLinkImpl)
1134		MESSAGE_HANDLER(WM_CREATE, OnCreate)
1135#ifndef _WIN32_WCE
1136		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
1137		MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseMessage)
1138#endif // !_WIN32_WCE
1139		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
1140		MESSAGE_HANDLER(WM_PAINT, OnPaint)
1141#ifndef _WIN32_WCE
1142		MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
1143#endif // !_WIN32_WCE
1144		MESSAGE_HANDLER(WM_SETFOCUS, OnFocus)
1145		MESSAGE_HANDLER(WM_KILLFOCUS, OnFocus)
1146		MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
1147#ifndef _WIN32_WCE
1148		MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
1149#endif // !_WIN32_WCE
1150		MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
1151		MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp)
1152		MESSAGE_HANDLER(WM_CHAR, OnChar)
1153		MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode)
1154		MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
1155		MESSAGE_HANDLER(WM_ENABLE, OnEnable)
1156		MESSAGE_HANDLER(WM_GETFONT, OnGetFont)
1157		MESSAGE_HANDLER(WM_SETFONT, OnSetFont)
1158		MESSAGE_HANDLER(WM_UPDATEUISTATE, OnUpdateUiState)
1159		MESSAGE_HANDLER(WM_SIZE, OnSize)
1160	END_MSG_MAP()
1161
1162	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1163	{
1164		T* pT = static_cast<T*>(this);
1165		pT->Init();
1166		return 0;
1167	}
1168
1169#ifndef _WIN32_WCE
1170	LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1171	{
1172		if(m_tip.IsWindow())
1173		{
1174			m_tip.DestroyWindow();
1175			m_tip.m_hWnd = NULL;
1176		}
1177
1178		if(m_bInternalLinkFont)
1179		{
1180			::DeleteObject(m_hFontLink);
1181			m_hFontLink = NULL;
1182			m_bInternalLinkFont = false;
1183		}
1184
1185		if(m_bInternalNormalFont)
1186		{
1187			::DeleteObject(m_hFontNormal);
1188			m_hFontNormal = NULL;
1189			m_bInternalNormalFont = false;
1190		}
1191
1192		bHandled = FALSE;
1193		return 1;
1194	}
1195
1196	LRESULT OnMouseMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1197	{
1198		MSG msg = { m_hWnd, uMsg, wParam, lParam };
1199		if(m_tip.IsWindow() && IsUsingToolTip())
1200			m_tip.RelayEvent(&msg);
1201		bHandled = FALSE;
1202		return 1;
1203	}
1204#endif // !_WIN32_WCE
1205
1206	LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1207	{
1208		return 1;   // no background painting needed (we do it all during WM_PAINT)
1209	}
1210
1211	LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
1212	{
1213		if(!m_bPaintLabel)
1214		{
1215			bHandled = FALSE;
1216			return 1;
1217		}
1218
1219		T* pT = static_cast<T*>(this);
1220		if(wParam != NULL)
1221		{
1222			pT->DoEraseBackground((HDC)wParam);
1223			pT->DoPaint((HDC)wParam);
1224		}
1225		else
1226		{
1227			CPaintDC dc(m_hWnd);
1228			pT->DoEraseBackground(dc.m_hDC);
1229			pT->DoPaint(dc.m_hDC);
1230		}
1231
1232		return 0;
1233	}
1234
1235	LRESULT OnFocus(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1236	{
1237		if(m_bPaintLabel)
1238			Invalidate();
1239		else
1240			bHandled = FALSE;
1241		return 0;
1242	}
1243
1244	LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
1245	{
1246		POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1247		if((m_lpstrHyperLink != NULL  || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
1248		{
1249			::SetCursor(m_hCursor);
1250			if(IsUnderlineHover())
1251			{
1252				if(!m_bHover)
1253				{
1254					m_bHover = true;
1255					InvalidateRect(&m_rcLink);
1256					UpdateWindow();
1257#ifndef _WIN32_WCE
1258					StartTrackMouseLeave();
1259#endif // !_WIN32_WCE
1260				}
1261			}
1262		}
1263		else
1264		{
1265			if(IsUnderlineHover())
1266			{
1267				if(m_bHover)
1268				{
1269					m_bHover = false;
1270					InvalidateRect(&m_rcLink);
1271					UpdateWindow();
1272				}
1273			}
1274			bHandled = FALSE;
1275		}
1276		return 0;
1277	}
1278
1279#ifndef _WIN32_WCE
1280	LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1281	{
1282		if(IsUnderlineHover() && m_bHover)
1283		{
1284			m_bHover = false;
1285			InvalidateRect(&m_rcLink);
1286			UpdateWindow();
1287		}
1288		return 0;
1289	}
1290#endif // !_WIN32_WCE
1291
1292	LRESULT OnLButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
1293	{
1294		POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1295		if(::PtInRect(&m_rcLink, pt))
1296		{
1297			SetFocus();
1298			SetCapture();
1299		}
1300		return 0;
1301	}
1302
1303	LRESULT OnLButtonUp(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
1304	{
1305		if(GetCapture() == m_hWnd)
1306		{
1307			ReleaseCapture();
1308			POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
1309			if(::PtInRect(&m_rcLink, pt))
1310			{
1311				T* pT = static_cast<T*>(this);
1312				pT->Navigate();
1313			}
1314		}
1315		return 0;
1316	}
1317
1318	LRESULT OnChar(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1319	{
1320		if(wParam == VK_RETURN || wParam == VK_SPACE)
1321		{
1322			T* pT = static_cast<T*>(this);
1323			pT->Navigate();
1324		}
1325		return 0;
1326	}
1327
1328	LRESULT OnGetDlgCode(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1329	{
1330		return DLGC_WANTCHARS;
1331	}
1332
1333	LRESULT OnSetCursor(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
1334	{
1335		POINT pt = { 0, 0 };
1336		GetCursorPos(&pt);
1337		ScreenToClient(&pt);
1338		if((m_lpstrHyperLink != NULL  || IsCommandButton()) && ::PtInRect(&m_rcLink, pt))
1339		{
1340			return TRUE;
1341		}
1342		bHandled = FALSE;
1343		return FALSE;
1344	}
1345
1346	LRESULT OnEnable(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1347	{
1348		Invalidate();
1349		UpdateWindow();
1350		return 0;
1351	}
1352
1353	LRESULT OnGetFont(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1354	{
1355		return (LRESULT)m_hFontNormal;
1356	}
1357
1358	LRESULT OnSetFont(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
1359	{
1360		if(m_bInternalNormalFont)
1361		{
1362			::DeleteObject(m_hFontNormal);
1363			m_bInternalNormalFont = false;
1364		}
1365
1366		bool bCreateLinkFont = m_bInternalLinkFont;
1367
1368		m_hFontNormal = (HFONT)wParam;
1369
1370		if(bCreateLinkFont || IsAutoCreateLinkFont())
1371			CreateLinkFontFromNormal();
1372
1373		T* pT = static_cast<T*>(this);
1374		pT->CalcLabelRect();
1375
1376		if((BOOL)lParam)
1377		{
1378			Invalidate();
1379			UpdateWindow();
1380		}
1381
1382		return 0;
1383	}
1384
1385	LRESULT OnUpdateUiState(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1386	{
1387		// If the control is subclassed or superclassed, this message can cause
1388		// repainting without WM_PAINT. We don't use this state, so just do nothing.
1389		return 0;
1390	}
1391
1392	LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
1393	{
1394		T* pT = static_cast<T*>(this);
1395		pT->CalcLabelRect();
1396		pT->Invalidate();
1397		return 0;
1398	}
1399
1400// Implementation
1401	void Init()
1402	{
1403		ATLASSERT(::IsWindow(m_hWnd));
1404
1405		// Check if we should paint a label
1406		const int cchBuff = 8;
1407		TCHAR szBuffer[cchBuff] = { 0 };
1408		if(::GetClassName(m_hWnd, szBuffer, cchBuff))
1409		{
1410			if(lstrcmpi(szBuffer, _T("static")) == 0)
1411			{
1412				ModifyStyle(0, SS_NOTIFY);   // we need this
1413				DWORD dwStyle = GetStyle() & 0x000000FF;
1414#ifndef _WIN32_WCE
1415				if(dwStyle == SS_ICON || dwStyle == SS_BLACKRECT || dwStyle == SS_GRAYRECT || 
1416						dwStyle == SS_WHITERECT || dwStyle == SS_BLACKFRAME || dwStyle == SS_GRAYFRAME || 
1417						dwStyle == SS_WHITEFRAME || dwStyle == SS_OWNERDRAW || 
1418						dwStyle == SS_BITMAP || dwStyle == SS_ENHMETAFILE)
1419#else // CE specific
1420				if(dwStyle == SS_ICON || dwStyle == SS_BITMAP)
1421#endif // _WIN32_WCE
1422					m_bPaintLabel = false;
1423			}
1424		}
1425
1426		// create or load a cursor
1427#if (WINVER >= 0x0500) || defined(_WIN32_WCE)
1428		m_hCursor = ::LoadCursor(NULL, IDC_HAND);
1429#else
1430		m_hCursor = ::CreateCursor(ModuleHelper::GetModuleInstance(), _AtlHyperLink_CursorData.xHotSpot, _AtlHyperLink_CursorData.yHotSpot, _AtlHyperLink_CursorData.cxWidth, _AtlHyperLink_CursorData.cyHeight, _AtlHyperLink_CursorData.arrANDPlane, _AtlHyperLink_CursorData.arrXORPlane);
1431#endif
1432		ATLASSERT(m_hCursor != NULL);
1433
1434		// set fonts
1435		if(m_bPaintLabel)
1436		{
1437			if(m_hFontNormal == NULL)
1438			{
1439				m_hFontNormal = AtlCreateControlFont();
1440				m_bInternalNormalFont = true;
1441			}
1442
1443			if(m_hFontLink == NULL)
1444				CreateLinkFontFromNormal();
1445		}
1446
1447#ifndef _WIN32_WCE
1448		// create a tool tip
1449		m_tip.Create(m_hWnd);
1450		ATLASSERT(m_tip.IsWindow());
1451#endif // !_WIN32_WCE
1452
1453		// set label (defaults to window text)
1454		if(m_lpstrLabel == NULL)
1455		{
1456			int nLen = GetWindowTextLength();
1457			if(nLen > 0)
1458			{
1459				ATLTRY(m_lpstrLabel = new TCHAR[nLen + 1]);
1460				if(m_lpstrLabel != NULL)
1461					ATLVERIFY(GetWindowText(m_lpstrLabel, nLen + 1) > 0);
1462			}
1463		}
1464
1465		T* pT = static_cast<T*>(this);
1466		pT->CalcLabelRect();
1467
1468		// set hyperlink (defaults to label), or just activate tool tip if already set
1469		if(m_lpstrHyperLink == NULL && !IsCommandButton())
1470		{
1471			if(m_lpstrLabel != NULL)
1472				SetHyperLink(m_lpstrLabel);
1473		}
1474#ifndef _WIN32_WCE
1475		else
1476		{
1477			m_tip.Activate(TRUE);
1478			m_tip.AddTool(m_hWnd, m_lpstrHyperLink, &m_rcLink, 1);
1479		}
1480#endif // !_WIN32_WCE
1481
1482		// set link colors
1483		if(m_bPaintLabel)
1484		{
1485			CRegKeyEx rk;
1486			LONG lRet = rk.Open(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Settings"));
1487			if(lRet == ERROR_SUCCESS)
1488			{
1489				const int cchValue = 12;
1490				TCHAR szValue[cchValue] = { 0 };
1491				ULONG ulCount = cchValue;
1492				lRet = rk.QueryStringValue(_T("Anchor Color"), szValue, &ulCount);
1493				if(lRet == ERROR_SUCCESS)
1494				{
1495					COLORREF clr = pT->_ParseColorString(szValue);
1496					ATLASSERT(clr != CLR_INVALID);
1497					if(clr != CLR_INVALID)
1498						m_clrLink = clr;
1499				}
1500
1501				ulCount = cchValue;
1502				lRet = rk.QueryStringValue(_T("Anchor Color Visited"), szValue, &ulCount);
1503				if(lRet == ERROR_SUCCESS)
1504				{
1505					COLORREF clr = pT->_ParseColorString(szValue);
1506					ATLASSERT(clr != CLR_INVALID);
1507					if(clr != CLR_INVALID)
1508						m_clrVisited = clr;
1509				}
1510			}
1511		}
1512	}
1513
1514	static COLORREF _ParseColorString(LPTSTR lpstr)
1515	{
1516		int c[3] = { -1, -1, -1 };
1517		LPTSTR p = NULL;
1518		for(int i = 0; i < 2; i++)
1519		{
1520			for(p = lpstr; *p != _T('\0'); p = ::CharNext(p))
1521			{
1522				if(*p == _T(','))
1523				{
1524					*p = _T('\0');
1525					c[i] = MinCrtHelper::_atoi(lpstr);
1526					lpstr = &p[1];
1527					break;
1528				}
1529			}
1530			if(c[i] == -1)
1531				return CLR_INVALID;
1532		}
1533		if(*lpstr == _T('\0'))
1534			return CLR_INVALID;
1535		c[2] = MinCrtHelper::_atoi(lpstr);
1536
1537		return RGB(c[0], c[1], c[2]);
1538	}
1539
1540	bool CalcLabelRect()
1541	{
1542		if(!::IsWindow(m_hWnd))
1543			return false;
1544		if(m_lpstrLabel == NULL && m_lpstrHyperLink == NULL)
1545			return false;
1546
1547		CClientDC dc(m_hWnd);
1548		RECT rcClient = { 0 };
1549		GetClientRect(&rcClient);
1550		m_rcLink = rcClient;
1551		if(!m_bPaintLabel)
1552			return true;
1553
1554		if(IsUsingTags())
1555		{
1556			// find tags and label parts
1557			LPTSTR lpstrLeft = NULL;
1558			int cchLeft = 0;
1559			LPTSTR lpstrLink = NULL;
1560			int cchLink = 0;
1561			LPTSTR lpstrRight = NULL;
1562			int cchRight = 0;
1563
1564			T* pT = static_cast<T*>(this);
1565			pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
1566			ATLASSERT(lpstrLink != NULL);
1567			ATLASSERT(cchLink > 0);
1568
1569			// get label part rects
1570			HFONT hFontOld = dc.SelectFont(m_hFontNormal);
1571
1572			UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1573
1574			RECT rcLeft = rcClient;
1575			if(lpstrLeft != NULL)
1576				dc.DrawText(lpstrLeft, cchLeft, &rcLeft, DT_LEFT | uFormat | DT_CALCRECT);
1577
1578			dc.SelectFont(m_hFontLink);
1579			RECT rcLink = rcClient;
1580			if(lpstrLeft != NULL)
1581				rcLink.left = rcLeft.right;
1582			dc.DrawText(lpstrLink, cchLink, &rcLink, DT_LEFT | uFormat | DT_CALCRECT);
1583
1584			dc.SelectFont(hFontOld);
1585
1586			m_rcLink = rcLink;
1587		}
1588		else
1589		{
1590			HFONT hOldFont = NULL;
1591			if(m_hFontLink != NULL)
1592				hOldFont = dc.SelectFont(m_hFontLink);
1593			LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1594			DWORD dwStyle = GetStyle();
1595			UINT uFormat = DT_LEFT;
1596			if (dwStyle & SS_CENTER)
1597				uFormat = DT_CENTER;
1598			else if (dwStyle & SS_RIGHT)
1599				uFormat = DT_RIGHT;
1600			uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1601			dc.DrawText(lpstrText, -1, &m_rcLink, uFormat | DT_CALCRECT);
1602			if(m_hFontLink != NULL)
1603				dc.SelectFont(hOldFont);
1604			if (dwStyle & SS_CENTER)
1605			{
1606				int dx = (rcClient.right - m_rcLink.right) / 2;
1607				::OffsetRect(&m_rcLink, dx, 0);
1608			}
1609			else if (dwStyle & SS_RIGHT)
1610			{
1611				int dx = rcClient.right - m_rcLink.right;
1612				::OffsetRect(&m_rcLink, dx, 0);
1613			}
1614		}
1615
1616		return true;
1617	}
1618
1619	void CalcLabelParts(LPTSTR& lpstrLeft, int& cchLeft, LPTSTR& lpstrLink, int& cchLink, LPTSTR& lpstrRight, int& cchRight) const
1620	{
1621		lpstrLeft = NULL;
1622		cchLeft = 0;
1623		lpstrLink = NULL;
1624		cchLink = 0;
1625		lpstrRight = NULL;
1626		cchRight = 0;
1627
1628		LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1629		int cchText = lstrlen(lpstrText);
1630		bool bOutsideLink = true;
1631		for(int i = 0; i < cchText; i++)
1632		{
1633			if(lpstrText[i] != _T('<'))
1634				continue;
1635
1636			if(bOutsideLink)
1637			{
1638				if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 3, _T("<A>"), 3) == CSTR_EQUAL)
1639				{
1640					if(i > 0)
1641					{
1642						lpstrLeft = lpstrText;
1643						cchLeft = i;
1644					}
1645					lpstrLink = &lpstrText[i + 3];
1646					bOutsideLink = false;
1647				}
1648			}
1649			else
1650			{
1651				if(::CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, &lpstrText[i], 4, _T("</A>"), 4) == CSTR_EQUAL)
1652				{
1653					cchLink = i - 3 - cchLeft;
1654					if(lpstrText[i + 4] != 0)
1655					{
1656						lpstrRight = &lpstrText[i + 4];
1657						cchRight = cchText - (i + 4);
1658						break;
1659					}
1660				}
1661			}
1662		}
1663
1664	}
1665
1666	void DoEraseBackground(CDCHandle dc)
1667	{
1668		HBRUSH hBrush = (HBRUSH)::SendMessage(GetParent(), WM_CTLCOLORSTATIC, (WPARAM)dc.m_hDC, (LPARAM)m_hWnd);
1669		if(hBrush != NULL)
1670		{
1671			RECT rect = { 0 };
1672			GetClientRect(&rect);
1673			dc.FillRect(&rect, hBrush);
1674		}
1675	}
1676
1677	void DoPaint(CDCHandle dc)
1678	{
1679		if(IsUsingTags())
1680		{
1681			// find tags and label parts
1682			LPTSTR lpstrLeft = NULL;
1683			int cchLeft = 0;
1684			LPTSTR lpstrLink = NULL;
1685			int cchLink = 0;
1686			LPTSTR lpstrRight = NULL;
1687			int cchRight = 0;
1688
1689			T* pT = static_cast<T*>(this);
1690			pT->CalcLabelParts(lpstrLeft, cchLeft, lpstrLink, cchLink, lpstrRight, cchRight);
1691
1692			// get label part rects
1693			RECT rcClient = { 0 };
1694			GetClientRect(&rcClient);
1695
1696			dc.SetBkMode(TRANSPARENT);
1697			HFONT hFontOld = dc.SelectFont(m_hFontNormal);
1698
1699			UINT uFormat = IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1700
1701			if(lpstrLeft != NULL)
1702				dc.DrawText(lpstrLeft, cchLeft, &rcClient, DT_LEFT | uFormat);
1703
1704			COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
1705			if(m_hFontLink != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
1706				dc.SelectFont(m_hFontLink);
1707			else
1708				dc.SelectFont(m_hFontNormal);
1709
1710			dc.DrawText(lpstrLink, cchLink, &m_rcLink, DT_LEFT | uFormat);
1711
1712			dc.SetTextColor(clrOld);
1713			dc.SelectFont(m_hFontNormal);
1714			if(lpstrRight != NULL)
1715			{
1716				RECT rcRight = { m_rcLink.right, m_rcLink.top, rcClient.right, rcClient.bottom };
1717				dc.DrawText(lpstrRight, cchRight, &rcRight, DT_LEFT | uFormat);
1718			}
1719
1720			if(GetFocus() == m_hWnd)
1721				dc.DrawFocusRect(&m_rcLink);
1722
1723			dc.SelectFont(hFontOld);
1724		}
1725		else
1726		{
1727			dc.SetBkMode(TRANSPARENT);
1728			COLORREF clrOld = dc.SetTextColor(IsWindowEnabled() ? (m_bVisited ? m_clrVisited : m_clrLink) : (::GetSysColor(COLOR_GRAYTEXT)));
1729
1730			HFONT hFontOld = NULL;
1731			if(m_hFontLink != NULL && (!IsUnderlineHover() || (IsUnderlineHover() && m_bHover)))
1732				hFontOld = dc.SelectFont(m_hFontLink);
1733			else
1734				hFontOld = dc.SelectFont(m_hFontNormal);
1735
1736			LPTSTR lpstrText = (m_lpstrLabel != NULL) ? m_lpstrLabel : m_lpstrHyperLink;
1737
1738			DWORD dwStyle = GetStyle();
1739			UINT uFormat = DT_LEFT;
1740			if (dwStyle & SS_CENTER)
1741				uFormat = DT_CENTER;
1742			else if (dwStyle & SS_RIGHT)
1743				uFormat = DT_RIGHT;
1744			uFormat |= IsSingleLine() ? DT_SINGLELINE : DT_WORDBREAK;
1745
1746			dc.DrawText(lpstrText, -1, &m_rcLink, uFormat);
1747
1748			if(GetFocus() == m_hWnd)
1749				dc.DrawFocusRect(&m_rcLink);
1750
1751			dc.SetTextColor(clrOld);
1752			dc.SelectFont(hFontOld);
1753		}
1754	}
1755
1756#ifndef _WIN32_WCE
1757	BOOL StartTrackMouseLeave()
1758	{
1759		TRACKMOUSEEVENT tme = { 0 };
1760		tme.cbSize = sizeof(tme);
1761		tme.dwFlags = TME_LEAVE;
1762		tme.hwndTrack = m_hWnd;
1763		return _TrackMouseEvent(&tme);
1764	}
1765#endif // !_WIN32_WCE
1766
1767// Implementation helpers
1768	bool IsUnderlined() const
1769	{
1770		return ((m_dwExtendedStyle & (HLINK_NOTUNDERLINED | HLINK_UNDERLINEHOVER)) == 0);
1771	}
1772
1773	bool IsNotUnderlined() const
1774	{
1775		return ((m_dwExtendedStyle & HLINK_NOTUNDERLINED) != 0);
1776	}
1777
1778	bool IsUnderlineHover() const
1779	{
1780		return ((m_dwExtendedStyle & HLINK_UNDERLINEHOVER) != 0);
1781	}
1782
1783	bool IsCommandButton() const
1784	{
1785		return ((m_dwExtendedStyle & HLINK_COMMANDBUTTON) != 0);
1786	}
1787
1788	bool IsNotifyButton() const
1789	{
1790		return ((m_dwExtendedStyle & HLINK_NOTIFYBUTTON) == HLINK_NOTIFYBUTTON);
1791	}
1792
1793	bool IsUsingTags() const
1794	{
1795		return ((m_dwExtendedStyle & HLINK_USETAGS) != 0);
1796	}
1797
1798	bool IsUsingTagsBold() const
1799	{
1800		return ((m_dwExtendedStyle & HLINK_USETAGSBOLD) == HLINK_USETAGSBOLD);
1801	}
1802
1803	bool IsUsingToolTip() const
1804	{
1805		return ((m_dwExtendedStyle & HLINK_NOTOOLTIP) == 0);
1806	}
1807
1808	bool IsAutoCreateLinkFont() const
1809	{
1810		return ((m_dwExtendedStyle & HLINK_AUTOCREATELINKFONT) == HLINK_AUTOCREATELINKFONT);
1811	}
1812
1813	bool IsSingleLine() const
1814	{
1815		return ((m_dwExtendedStyle & HLINK_SINGLELINE) == HLINK_SINGLELINE);
1816	}
1817};
1818
1819class CHyperLink : public CHyperLinkImpl<CHyperLink>
1820{
1821public:
1822	DECLARE_WND_CLASS(_T("WTL_HyperLink"))
1823};
1824
1825
1826///////////////////////////////////////////////////////////////////////////////
1827// CWaitCursor - displays a wait cursor
1828
1829class CWaitCursor
1830{
1831public:
1832// Data
1833	HCURSOR m_hWaitCursor;
1834	HCURSOR m_hOldCursor;
1835	bool m_bInUse;
1836
1837// Constructor/destructor
1838	CWaitCursor(bool bSet = true, LPCTSTR lpstrCursor = IDC_WAIT, bool bSys = true) : m_hOldCursor(NULL), m_bInUse(false)
1839	{
1840		HINSTANCE hInstance = bSys ? NULL : ModuleHelper::GetResourceInstance();
1841		m_hWaitCursor = ::LoadCursor(hInstance, lpstrCursor);
1842		ATLASSERT(m_hWaitCursor != NULL);
1843
1844		if(bSet)
1845			Set();
1846	}
1847
1848	~CWaitCursor()
1849	{
1850		Restore();
1851	}
1852
1853// Methods
1854	bool Set()
1855	{
1856		if(m_bInUse)
1857			return false;
1858		m_hOldCursor = ::SetCursor(m_hWaitCursor);
1859		m_bInUse = true;
1860		return true;
1861	}
1862
1863	bool Restore()
1864	{
1865		if(!m_bInUse)
1866			return false;
1867		::SetCursor(m_hOldCursor);
1868		m_bInUse = false;
1869		return true;
1870	}
1871};
1872
1873
1874///////////////////////////////////////////////////////////////////////////////
1875// CCustomWaitCursor - for custom and animated cursors
1876
1877class CCustomWaitCursor : public CWaitCursor
1878{
1879public:
1880// Constructor/destructor
1881	CCustomWaitCursor(ATL::_U_STRINGorID cursor, bool bSet = true, HINSTANCE hInstance = NULL) 

Large files files are truncated, but you can click here to view the full file