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