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