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