1 /* 2 * ReactOS Explorer 3 * 4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org> 5 * Copyright 2018-2022 Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com> 6 * 7 * this library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * this library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22 #include "precomp.h" 23 #include <commoncontrols.h> 24 25 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu); 26 LRESULT appbar_message(COPYDATASTRUCT* cds); 27 void appbar_notify_all(HMONITOR hMon, UINT uMsg, HWND hwndExclude, LPARAM lParam); 28 29 #define WM_APP_TRAYDESTROY (WM_APP + 0x100) 30 31 #define TIMER_ID_AUTOHIDE 1 32 #define TIMER_ID_MOUSETRACK 2 33 #define MOUSETRACK_INTERVAL 100 34 #define AUTOHIDE_DELAY_HIDE 2000 35 #define AUTOHIDE_DELAY_SHOW 50 36 #define AUTOHIDE_INTERVAL_ANIMATING 10 37 38 #define AUTOHIDE_SPEED_SHOW 10 39 #define AUTOHIDE_SPEED_HIDE 1 40 41 #define AUTOHIDE_HIDDEN 0 42 #define AUTOHIDE_SHOWING 1 43 #define AUTOHIDE_SHOWN 2 44 #define AUTOHIDE_HIDING 3 45 46 #define IDHK_RUN 0x1f4 47 #define IDHK_MINIMIZE_ALL 0x1f5 48 #define IDHK_RESTORE_ALL 0x1f6 49 #define IDHK_HELP 0x1f7 50 #define IDHK_EXPLORE 0x1f8 51 #define IDHK_FIND 0x1f9 52 #define IDHK_FIND_COMPUTER 0x1fa 53 #define IDHK_NEXT_TASK 0x1fb 54 #define IDHK_PREV_TASK 0x1fc 55 #define IDHK_SYS_PROPERTIES 0x1fd 56 #define IDHK_DESKTOP 0x1fe 57 #define IDHK_PAGER 0x1ff 58 59 static const WCHAR szTrayWndClass[] = L"Shell_TrayWnd"; 60 61 enum { NONE, TILED, CASCADED } g_Arrangement = NONE; 62 63 struct WINDOWPOSBACKUPDATA 64 { 65 HWND hwnd; 66 WINDOWPLACEMENT wplt; 67 }; 68 CSimpleArray<WINDOWPOSBACKUPDATA> g_WindowPosBackup; 69 70 static BOOL CALLBACK BackupWindowsPosProc(HWND hwnd, LPARAM lParam) 71 { 72 WINDOWPOSBACKUPDATA wposdata; 73 HWND hDesk = GetDesktopWindow(); 74 if (IsWindowVisible(hwnd) && !IsIconic(hwnd) && (hwnd != hDesk)) 75 { 76 wposdata.hwnd = hwnd; 77 wposdata.wplt.length = sizeof(wposdata.wplt); 78 GetWindowPlacement(hwnd, &(wposdata.wplt)); 79 g_WindowPosBackup.Add(wposdata); 80 } 81 82 return TRUE; 83 } 84 85 VOID BackupWindowPos() 86 { 87 EnumWindows(BackupWindowsPosProc, NULL); 88 } 89 90 VOID RestoreWindowPos() 91 { 92 g_Arrangement = NONE; 93 94 for (INT i = g_WindowPosBackup.GetSize() - 1; i >= 0; --i) 95 { 96 SetWindowPlacement(g_WindowPosBackup[i].hwnd, &(g_WindowPosBackup[i].wplt)); 97 } 98 99 g_WindowPosBackup.RemoveAll(); 100 } 101 102 struct EFFECTIVE_INFO 103 { 104 HWND hwndFound; 105 HWND hwndDesktop; 106 HWND hwndProgman; 107 HWND hTrayWnd; 108 BOOL bMustBeInMonitor; 109 }; 110 111 static BOOL CALLBACK 112 FindEffectiveProc(HWND hwnd, LPARAM lParam) 113 { 114 EFFECTIVE_INFO *pei = (EFFECTIVE_INFO *)lParam; 115 116 if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) 117 return TRUE; // continue 118 119 if (pei->hTrayWnd == hwnd || pei->hwndDesktop == hwnd || 120 pei->hwndProgman == hwnd) 121 { 122 return TRUE; // continue 123 } 124 125 if (pei->bMustBeInMonitor) 126 { 127 // is the window in the nearest monitor? 128 HMONITOR hMon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); 129 if (hMon) 130 { 131 MONITORINFO info; 132 ZeroMemory(&info, sizeof(info)); 133 info.cbSize = sizeof(info); 134 if (GetMonitorInfoW(hMon, &info)) 135 { 136 RECT rcWindow, rcMonitor, rcIntersect; 137 rcMonitor = info.rcMonitor; 138 139 GetWindowRect(hwnd, &rcWindow); 140 141 if (!IntersectRect(&rcIntersect, &rcMonitor, &rcWindow)) 142 return TRUE; // continue 143 } 144 } 145 } 146 147 pei->hwndFound = hwnd; 148 return FALSE; // stop if found 149 } 150 151 static BOOL 152 IsThereAnyEffectiveWindow(BOOL bMustBeInMonitor) 153 { 154 EFFECTIVE_INFO ei; 155 ei.hwndFound = NULL; 156 ei.hwndDesktop = GetDesktopWindow(); 157 ei.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL); 158 ei.hwndProgman = FindWindowW(L"Progman", NULL); 159 ei.bMustBeInMonitor = bMustBeInMonitor; 160 161 EnumWindows(FindEffectiveProc, (LPARAM)&ei); 162 if (ei.hwndFound && FALSE) 163 { 164 WCHAR szClass[64], szText[64]; 165 GetClassNameW(ei.hwndFound, szClass, _countof(szClass)); 166 GetWindowTextW(ei.hwndFound, szText, _countof(szText)); 167 MessageBoxW(NULL, szText, szClass, 0); 168 } 169 return ei.hwndFound != NULL; 170 } 171 172 CSimpleArray<HWND> g_MinimizedAll; 173 174 /* 175 * ITrayWindow 176 */ 177 178 const GUID IID_IShellDesktopTray = { 0x213e2df9, 0x9a14, 0x4328, { 0x99, 0xb1, 0x69, 0x61, 0xf9, 0x14, 0x3c, 0xe9 } }; 179 180 class CStartButton 181 : public CWindowImpl<CStartButton> 182 { 183 HIMAGELIST m_ImageList; 184 SIZE m_Size; 185 HFONT m_Font; 186 187 public: 188 CStartButton() 189 : m_ImageList(NULL), 190 m_Font(NULL) 191 { 192 m_Size.cx = 0; 193 m_Size.cy = 0; 194 } 195 196 virtual ~CStartButton() 197 { 198 if (m_ImageList != NULL) 199 ImageList_Destroy(m_ImageList); 200 201 if (m_Font != NULL) 202 DeleteObject(m_Font); 203 } 204 205 SIZE GetSize() 206 { 207 return m_Size; 208 } 209 210 VOID UpdateSize() 211 { 212 SIZE Size = { 0, 0 }; 213 214 if (m_ImageList == NULL || 215 !SendMessageW(BCM_GETIDEALSIZE, 0, (LPARAM) &Size)) 216 { 217 Size.cx = 2 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYCAPTION) * 3; 218 } 219 220 Size.cy = max(Size.cy, GetSystemMetrics(SM_CYCAPTION)); 221 222 /* Save the size of the start button */ 223 m_Size = Size; 224 } 225 226 VOID UpdateFont() 227 { 228 /* Get the system fonts, we use the caption font, always bold, though. */ 229 NONCLIENTMETRICS ncm = {sizeof(ncm)}; 230 if (!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE)) 231 return; 232 233 if (m_Font) 234 DeleteObject(m_Font); 235 236 ncm.lfCaptionFont.lfWeight = FW_BOLD; 237 m_Font = CreateFontIndirect(&ncm.lfCaptionFont); 238 239 SetFont(m_Font, FALSE); 240 } 241 242 VOID Initialize() 243 { 244 // HACK & FIXME: CORE-18016 245 HWND hWnd = m_hWnd; 246 m_hWnd = NULL; 247 SubclassWindow(hWnd); 248 249 SetWindowTheme(m_hWnd, L"Start", NULL); 250 251 m_ImageList = ImageList_LoadImageW(hExplorerInstance, 252 MAKEINTRESOURCEW(IDB_START), 253 0, 0, 0, 254 IMAGE_BITMAP, 255 LR_LOADTRANSPARENT | LR_CREATEDIBSECTION); 256 257 BUTTON_IMAGELIST bil = {m_ImageList, {1,1,1,1}, BUTTON_IMAGELIST_ALIGN_LEFT}; 258 SendMessageW(BCM_SETIMAGELIST, 0, (LPARAM) &bil); 259 UpdateSize(); 260 } 261 262 HWND Create(HWND hwndParent) 263 { 264 WCHAR szStartCaption[32]; 265 if (!LoadStringW(hExplorerInstance, 266 IDS_START, 267 szStartCaption, 268 _countof(szStartCaption))) 269 { 270 wcscpy(szStartCaption, L"Start"); 271 } 272 273 DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | BS_PUSHBUTTON | BS_LEFT | BS_VCENTER; 274 275 // HACK & FIXME: CORE-18016 276 m_hWnd = CreateWindowEx( 277 0, 278 WC_BUTTON, 279 szStartCaption, 280 dwStyle, 281 0, 0, 0, 0, 282 hwndParent, 283 (HMENU) IDC_STARTBTN, 284 hExplorerInstance, 285 NULL); 286 287 if (m_hWnd) 288 Initialize(); 289 290 return m_hWnd; 291 } 292 293 LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 294 { 295 if (uMsg == WM_KEYUP && wParam != VK_SPACE) 296 return 0; 297 298 GetParent().PostMessage(TWM_OPENSTARTMENU); 299 return 0; 300 } 301 302 BEGIN_MSG_MAP(CStartButton) 303 MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown) 304 END_MSG_MAP() 305 306 }; 307 308 // This window class name is CONFIRMED on Win10 by WinHier. 309 static const WCHAR szTrayShowDesktopButton[] = L"TrayShowDesktopButtonWClass"; 310 311 // The 'Show Desktop' button at edge of taskbar 312 class CTrayShowDesktopButton : 313 public CWindowImpl<CTrayShowDesktopButton, CWindow, CControlWinTraits> 314 { 315 LONG m_nClickedTime; 316 BOOL m_bHovering; 317 HTHEME m_hTheme; 318 319 public: 320 DECLARE_WND_CLASS_EX(szTrayShowDesktopButton, CS_HREDRAW | CS_VREDRAW, COLOR_3DFACE) 321 322 CTrayShowDesktopButton() : m_nClickedTime(0), m_bHovering(FALSE) 323 { 324 } 325 326 INT WidthOrHeight() const 327 { 328 #define SHOW_DESKTOP_MINIMUM_WIDTH 3 329 INT cxy = 2 * ::GetSystemMetrics(SM_CXEDGE); 330 return max(cxy, SHOW_DESKTOP_MINIMUM_WIDTH); 331 } 332 333 HRESULT DoCreate(HWND hwndParent) 334 { 335 DWORD style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS; 336 Create(hwndParent, NULL, NULL, style); 337 if (!m_hWnd) 338 return E_FAIL; 339 340 ::SetWindowTheme(m_hWnd, L"TaskBar", NULL); 341 return S_OK; 342 } 343 344 LRESULT OnClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 345 { 346 // The actual action can be delayed as an expected behaviour. 347 // But a too late action is an unexpected behaviour. 348 LONG nTime0 = m_nClickedTime; 349 LONG nTime1 = ::GetMessageTime(); 350 if (nTime1 - nTime0 >= 600) // Ignore after 0.6 sec 351 return 0; 352 353 // Show/Hide Desktop 354 GetParent().SendMessage(WM_COMMAND, TRAYCMD_TOGGLE_DESKTOP, 0); 355 return 0; 356 } 357 358 #define TSDB_CLICK (WM_USER + 100) 359 360 // This function is called from OnLButtonDown and parent. 361 VOID Click() 362 { 363 // The actual action can be delayed as an expected behaviour. 364 m_nClickedTime = ::GetMessageTime(); 365 PostMessage(TSDB_CLICK, 0, 0); 366 } 367 368 LRESULT OnLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 369 { 370 Click(); // Left-click 371 return 0; 372 } 373 374 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 375 { 376 if (m_hTheme) 377 ::CloseThemeData(m_hTheme); 378 379 m_hTheme = ::OpenThemeData(m_hWnd, L"TaskBar"); 380 InvalidateRect(NULL, TRUE); 381 return 0; 382 } 383 384 // This function is called from OnPaint and parent. 385 VOID OnDraw(HDC hdc, LPRECT prc); 386 387 LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 388 { 389 RECT rc; 390 GetClientRect(&rc); 391 392 PAINTSTRUCT ps; 393 HDC hdc = BeginPaint(&ps); 394 OnDraw(hdc, &rc); 395 EndPaint(&ps); 396 return 0; 397 } 398 399 BOOL PtInButton(POINT pt) 400 { 401 if (!IsWindow()) 402 return FALSE; 403 RECT rc; 404 GetWindowRect(&rc); 405 INT cxEdge = ::GetSystemMetrics(SM_CXEDGE), cyEdge = ::GetSystemMetrics(SM_CYEDGE); 406 ::InflateRect(&rc, max(cxEdge, 1), max(cyEdge, 1)); 407 return ::PtInRect(&rc, pt); 408 } 409 410 #define SHOW_DESKTOP_TIMER_ID 999 411 #define SHOW_DESKTOP_TIMER_INTERVAL 200 412 413 VOID StartHovering() 414 { 415 if (m_bHovering) 416 return; 417 418 m_bHovering = TRUE; 419 SetTimer(SHOW_DESKTOP_TIMER_ID, SHOW_DESKTOP_TIMER_INTERVAL, NULL); 420 InvalidateRect(NULL, TRUE); 421 GetParent().PostMessage(WM_NCPAINT, 0, 0); 422 } 423 424 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 425 { 426 StartHovering(); 427 return 0; 428 } 429 430 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 431 { 432 if (wParam != SHOW_DESKTOP_TIMER_ID || !m_bHovering) 433 return 0; 434 435 POINT pt; 436 ::GetCursorPos(&pt); 437 if (!PtInButton(pt)) // The end of hovering? 438 { 439 m_bHovering = FALSE; 440 KillTimer(SHOW_DESKTOP_TIMER_ID); 441 InvalidateRect(NULL, TRUE); 442 GetParent().PostMessage(WM_NCPAINT, 0, 0); 443 } 444 445 return 0; 446 } 447 448 LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 449 { 450 if (m_hTheme) 451 { 452 CloseThemeData(m_hTheme); 453 m_hTheme = NULL; 454 } 455 return 0; 456 } 457 458 BEGIN_MSG_MAP(CTrayShowDesktopButton) 459 MESSAGE_HANDLER(WM_LBUTTONUP, OnLButtonUp) 460 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged) 461 MESSAGE_HANDLER(WM_THEMECHANGED, OnSettingChanged) 462 MESSAGE_HANDLER(WM_PAINT, OnPaint) 463 MESSAGE_HANDLER(WM_TIMER, OnTimer) 464 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) 465 MESSAGE_HANDLER(WM_DESTROY, OnDestroy) 466 MESSAGE_HANDLER(TSDB_CLICK, OnClick) 467 END_MSG_MAP() 468 }; 469 470 VOID CTrayShowDesktopButton::OnDraw(HDC hdc, LPRECT prc) 471 { 472 if (m_hTheme) 473 { 474 if (m_bHovering) // Draw a hot button 475 { 476 HTHEME hButtonTheme = ::OpenThemeData(m_hWnd, L"Button"); 477 ::DrawThemeBackground(hButtonTheme, hdc, BP_PUSHBUTTON, PBS_NORMAL, prc, prc); 478 ::CloseThemeData(hButtonTheme); 479 } 480 else // Draw a taskbar background 481 { 482 ::DrawThemeBackground(m_hTheme, hdc, TBP_BACKGROUNDTOP, 0, prc, prc); 483 } 484 } 485 else 486 { 487 RECT rc = *prc; 488 if (m_bHovering) // Draw a hot button 489 { 490 ::DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_ADJUSTRECT); 491 HBRUSH hbrHot = ::CreateSolidBrush(RGB(255, 255, 191)); 492 ::FillRect(hdc, &rc, hbrHot); 493 ::DeleteObject(hbrHot); 494 } 495 else // Draw a flattish button 496 { 497 ::DrawFrameControl(hdc, &rc, DFC_BUTTON, DFCS_BUTTONPUSH); 498 ::InflateRect(&rc, -1, -1); 499 ::FillRect(hdc, &rc, ::GetSysColorBrush(COLOR_3DFACE)); 500 } 501 } 502 } 503 504 class CTrayWindow : 505 public CComCoClass<CTrayWindow>, 506 public CComObjectRootEx<CComMultiThreadModelNoCS>, 507 public CWindowImpl < CTrayWindow, CWindow, CControlWinTraits >, 508 public ITrayWindow, 509 public IShellDesktopTray, 510 public IOleWindow, 511 public IContextMenu 512 { 513 CStartButton m_StartButton; 514 CTrayShowDesktopButton m_ShowDesktopButton; 515 516 CComPtr<IMenuBand> m_StartMenuBand; 517 CComPtr<IMenuPopup> m_StartMenuPopup; 518 519 CComPtr<IDeskBand> m_TaskBand; 520 CComPtr<IContextMenu> m_ContextMenu; 521 HTHEME m_Theme; 522 523 HFONT m_Font; 524 525 HWND m_DesktopWnd; 526 HWND m_Rebar; 527 HWND m_TaskSwitch; 528 HWND m_TrayNotify; 529 530 CComPtr<IUnknown> m_TrayNotifyInstance; 531 532 DWORD m_Position; 533 HMONITOR m_Monitor; 534 HMONITOR m_PreviousMonitor; 535 DWORD m_DraggingPosition; 536 HMONITOR m_DraggingMonitor; 537 538 RECT m_TrayRects[4]; 539 SIZE m_TraySize; 540 541 HWND m_TrayPropertiesOwner; 542 HWND m_RunFileDlgOwner; 543 544 UINT m_AutoHideState; 545 SIZE m_AutoHideOffset; 546 TRACKMOUSEEVENT m_MouseTrackingInfo; 547 548 HDPA m_ShellServices; 549 550 public: 551 CComPtr<ITrayBandSite> m_TrayBandSite; 552 553 union 554 { 555 DWORD Flags; 556 struct 557 { 558 /* UI Status */ 559 DWORD InSizeMove : 1; 560 DWORD IsDragging : 1; 561 DWORD NewPosSize : 1; 562 }; 563 }; 564 565 public: 566 CTrayWindow() : 567 m_StartButton(), 568 m_ShowDesktopButton(), 569 m_Theme(NULL), 570 m_Font(NULL), 571 m_DesktopWnd(NULL), 572 m_Rebar(NULL), 573 m_TaskSwitch(NULL), 574 m_TrayNotify(NULL), 575 m_Position(0), 576 m_Monitor(NULL), 577 m_PreviousMonitor(NULL), 578 m_DraggingPosition(0), 579 m_DraggingMonitor(NULL), 580 m_TrayPropertiesOwner(NULL), 581 m_RunFileDlgOwner(NULL), 582 m_AutoHideState(NULL), 583 m_ShellServices(NULL), 584 Flags(0) 585 { 586 ZeroMemory(&m_TrayRects, sizeof(m_TrayRects)); 587 ZeroMemory(&m_TraySize, sizeof(m_TraySize)); 588 ZeroMemory(&m_AutoHideOffset, sizeof(m_AutoHideOffset)); 589 ZeroMemory(&m_MouseTrackingInfo, sizeof(m_MouseTrackingInfo)); 590 } 591 592 virtual ~CTrayWindow() 593 { 594 if (m_ShellServices != NULL) 595 { 596 ShutdownShellServices(m_ShellServices); 597 m_ShellServices = NULL; 598 } 599 600 if (m_Font != NULL) 601 { 602 DeleteObject(m_Font); 603 m_Font = NULL; 604 } 605 606 if (m_Theme) 607 { 608 CloseThemeData(m_Theme); 609 m_Theme = NULL; 610 } 611 612 PostQuitMessage(0); 613 } 614 615 616 617 618 619 /********************************************************** 620 * ##### command handling ##### 621 */ 622 623 HRESULT ExecResourceCmd(int id) 624 { 625 WCHAR szCommand[256]; 626 WCHAR *pszParameters; 627 628 if (!LoadStringW(hExplorerInstance, 629 id, 630 szCommand, 631 _countof(szCommand))) 632 { 633 return E_FAIL; 634 } 635 636 pszParameters = wcschr(szCommand, L'>'); 637 if (pszParameters) 638 { 639 *pszParameters = 0; 640 pszParameters++; 641 } 642 643 ShellExecuteW(m_hWnd, NULL, szCommand, pszParameters, NULL, SW_SHOWNORMAL); 644 return S_OK; 645 } 646 647 LRESULT DoExitWindows() 648 { 649 /* Display the ReactOS Shutdown Dialog */ 650 ExitWindowsDialog(m_hWnd); 651 652 /* 653 * If the user presses CTRL+ALT+SHIFT while exiting 654 * the shutdown dialog, exit the shell cleanly. 655 */ 656 if ((GetKeyState(VK_CONTROL) & 0x8000) && 657 (GetKeyState(VK_SHIFT) & 0x8000) && 658 (GetKeyState(VK_MENU) & 0x8000)) 659 { 660 PostMessage(WM_QUIT, 0, 0); 661 } 662 return 0; 663 } 664 665 DWORD WINAPI RunFileDlgThread() 666 { 667 HWND hwnd; 668 RECT posRect; 669 670 m_StartButton.GetWindowRect(&posRect); 671 672 hwnd = CreateWindowEx(0, 673 WC_STATIC, 674 NULL, 675 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT, 676 posRect.left, 677 posRect.top, 678 posRect.right - posRect.left, 679 posRect.bottom - posRect.top, 680 NULL, 681 NULL, 682 NULL, 683 NULL); 684 685 m_RunFileDlgOwner = hwnd; 686 687 // build the default directory from two environment variables 688 CStringW strDefaultDir, strHomePath; 689 strDefaultDir.GetEnvironmentVariable(L"HOMEDRIVE"); 690 strHomePath.GetEnvironmentVariable(L"HOMEPATH"); 691 strDefaultDir += strHomePath; 692 693 RunFileDlg(hwnd, NULL, (LPCWSTR)strDefaultDir, NULL, NULL, RFF_CALCDIRECTORY); 694 695 m_RunFileDlgOwner = NULL; 696 ::DestroyWindow(hwnd); 697 698 return 0; 699 } 700 701 static DWORD WINAPI s_RunFileDlgThread(IN OUT PVOID pParam) 702 { 703 CTrayWindow * This = (CTrayWindow*) pParam; 704 return This->RunFileDlgThread(); 705 } 706 707 void DisplayRunFileDlg() 708 { 709 HWND hRunDlg; 710 if (m_RunFileDlgOwner) 711 { 712 hRunDlg = ::GetLastActivePopup(m_RunFileDlgOwner); 713 if (hRunDlg != NULL && 714 hRunDlg != m_RunFileDlgOwner) 715 { 716 SetForegroundWindow(hRunDlg); 717 return; 718 } 719 } 720 721 CloseHandle(CreateThread(NULL, 0, s_RunFileDlgThread, this, 0, NULL)); 722 } 723 724 DWORD WINAPI TrayPropertiesThread() 725 { 726 HWND hwnd; 727 RECT posRect; 728 729 m_StartButton.GetWindowRect(&posRect); 730 hwnd = CreateWindowEx(0, 731 WC_STATIC, 732 NULL, 733 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT, 734 posRect.left, 735 posRect.top, 736 posRect.right - posRect.left, 737 posRect.bottom - posRect.top, 738 NULL, 739 NULL, 740 NULL, 741 NULL); 742 743 m_TrayPropertiesOwner = hwnd; 744 745 DisplayTrayProperties(hwnd, m_hWnd); 746 747 m_TrayPropertiesOwner = NULL; 748 ::DestroyWindow(hwnd); 749 750 return 0; 751 } 752 753 static DWORD WINAPI s_TrayPropertiesThread(IN OUT PVOID pParam) 754 { 755 CTrayWindow *This = (CTrayWindow*) pParam; 756 757 return This->TrayPropertiesThread(); 758 } 759 760 HWND STDMETHODCALLTYPE DisplayProperties() 761 { 762 HWND hTrayProp; 763 764 if (m_TrayPropertiesOwner) 765 { 766 hTrayProp = ::GetLastActivePopup(m_TrayPropertiesOwner); 767 if (hTrayProp != NULL && 768 hTrayProp != m_TrayPropertiesOwner) 769 { 770 SetForegroundWindow(hTrayProp); 771 return NULL; 772 } 773 } 774 775 CloseHandle(CreateThread(NULL, 0, s_TrayPropertiesThread, this, 0, NULL)); 776 return NULL; 777 } 778 779 VOID OpenCommonStartMenuDirectory(IN HWND hWndOwner, IN LPCTSTR lpOperation) 780 { 781 WCHAR szDir[MAX_PATH]; 782 783 if (SHGetSpecialFolderPath(hWndOwner, 784 szDir, 785 CSIDL_COMMON_STARTMENU, 786 FALSE)) 787 { 788 ShellExecute(hWndOwner, 789 lpOperation, 790 szDir, 791 NULL, 792 NULL, 793 SW_SHOWNORMAL); 794 } 795 } 796 797 VOID OpenTaskManager(IN HWND hWndOwner) 798 { 799 ShellExecute(hWndOwner, 800 TEXT("open"), 801 TEXT("taskmgr.exe"), 802 NULL, 803 NULL, 804 SW_SHOWNORMAL); 805 } 806 807 VOID ToggleDesktop() 808 { 809 if (::IsThereAnyEffectiveWindow(TRUE)) 810 { 811 ShowDesktop(); 812 } 813 else 814 { 815 RestoreAll(); 816 } 817 } 818 819 BOOL STDMETHODCALLTYPE ExecContextMenuCmd(IN UINT uiCmd) 820 { 821 switch (uiCmd) 822 { 823 case ID_SHELL_CMD_PROPERTIES: 824 DisplayProperties(); 825 break; 826 827 case ID_SHELL_CMD_OPEN_ALL_USERS: 828 OpenCommonStartMenuDirectory(m_hWnd, 829 TEXT("open")); 830 break; 831 832 case ID_SHELL_CMD_EXPLORE_ALL_USERS: 833 OpenCommonStartMenuDirectory(m_hWnd, 834 TEXT("explore")); 835 break; 836 837 case ID_LOCKTASKBAR: 838 if (SHRestricted(REST_CLASSICSHELL) == 0) 839 { 840 Lock(!g_TaskbarSettings.bLock); 841 } 842 break; 843 844 case ID_SHELL_CMD_OPEN_TASKMGR: 845 OpenTaskManager(m_hWnd); 846 break; 847 848 case ID_SHELL_CMD_UNDO_ACTION: 849 RestoreWindowPos(); 850 break; 851 852 case ID_SHELL_CMD_SHOW_DESKTOP: 853 ShowDesktop(); 854 break; 855 856 case ID_SHELL_CMD_TILE_WND_H: 857 appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, TRUE); 858 if (g_Arrangement == NONE) 859 { 860 BackupWindowPos(); 861 } 862 TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL); 863 appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, FALSE); 864 g_Arrangement = TILED; 865 break; 866 867 case ID_SHELL_CMD_TILE_WND_V: 868 appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, TRUE); 869 if (g_Arrangement == NONE) 870 { 871 BackupWindowPos(); 872 } 873 TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL); 874 appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, FALSE); 875 g_Arrangement = TILED; 876 break; 877 878 case ID_SHELL_CMD_CASCADE_WND: 879 appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, TRUE); 880 if (g_Arrangement == NONE) 881 { 882 BackupWindowPos(); 883 } 884 CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL); 885 appbar_notify_all(NULL, ABN_WINDOWARRANGE, NULL, FALSE); 886 g_Arrangement = CASCADED; 887 break; 888 889 case ID_SHELL_CMD_CUST_NOTIF: 890 ShowCustomizeNotifyIcons(hExplorerInstance, m_hWnd); 891 break; 892 893 case ID_SHELL_CMD_ADJUST_DAT: 894 //FIXME: Use SHRunControlPanel 895 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL); 896 break; 897 898 case ID_SHELL_CMD_RESTORE_ALL: 899 RestoreAll(); 900 break; 901 902 default: 903 TRACE("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd); 904 return FALSE; 905 } 906 907 return TRUE; 908 } 909 910 VOID HideStartMenu() 911 { 912 m_StartMenuPopup->OnSelect(MPOS_CANCELLEVEL); 913 } 914 915 LRESULT HandleHotKey(DWORD id) 916 { 917 switch (id) 918 { 919 case IDHK_RUN: 920 HideStartMenu(); 921 DisplayRunFileDlg(); 922 break; 923 case IDHK_HELP: 924 ExecResourceCmd(IDS_HELP_COMMAND); 925 break; 926 case IDHK_EXPLORE: 927 //FIXME: We don't support this yet: 928 //ShellExecuteW(0, L"explore", NULL, NULL, NULL, 1); 929 ShellExecuteW(0, NULL, L"explorer.exe", L"/e ,", NULL, 1); 930 break; 931 case IDHK_FIND: 932 SHFindFiles(NULL, NULL); 933 break; 934 case IDHK_FIND_COMPUTER: 935 SHFindComputer(NULL, NULL); 936 break; 937 case IDHK_SYS_PROPERTIES: 938 //FIXME: Use SHRunControlPanel 939 ShellExecuteW(m_hWnd, NULL, L"sysdm.cpl", NULL, NULL, SW_NORMAL); 940 break; 941 case IDHK_NEXT_TASK: 942 break; 943 case IDHK_PREV_TASK: 944 break; 945 case IDHK_MINIMIZE_ALL: 946 MinimizeAll(); 947 break; 948 case IDHK_RESTORE_ALL: 949 RestoreAll(); 950 break; 951 case IDHK_DESKTOP: 952 ToggleDesktop(); 953 break; 954 case IDHK_PAGER: 955 break; 956 } 957 958 return 0; 959 } 960 961 LRESULT HandleCommand(UINT uCommand) 962 { 963 switch (uCommand) 964 { 965 case TRAYCMD_STARTMENU: 966 // TODO: 967 break; 968 case TRAYCMD_RUN_DIALOG: 969 HideStartMenu(); 970 DisplayRunFileDlg(); 971 break; 972 case TRAYCMD_LOGOFF_DIALOG: 973 LogoffWindowsDialog(m_hWnd); // FIXME: Maybe handle it in a similar way as DoExitWindows? 974 break; 975 case TRAYCMD_CASCADE: 976 CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL); 977 break; 978 case TRAYCMD_TILE_H: 979 TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL); 980 break; 981 case TRAYCMD_TILE_V: 982 TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL); 983 break; 984 case TRAYCMD_TOGGLE_DESKTOP: 985 ToggleDesktop(); 986 break; 987 case TRAYCMD_DATE_AND_TIME: 988 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL); 989 break; 990 case TRAYCMD_TASKBAR_PROPERTIES: 991 DisplayProperties(); 992 break; 993 case TRAYCMD_MINIMIZE_ALL: 994 MinimizeAll(); 995 break; 996 case TRAYCMD_RESTORE_ALL: 997 RestoreAll(); 998 break; 999 case TRAYCMD_SHOW_DESKTOP: 1000 ShowDesktop(); 1001 break; 1002 case TRAYCMD_SHOW_TASK_MGR: 1003 OpenTaskManager(m_hWnd); 1004 break; 1005 case TRAYCMD_CUSTOMIZE_TASKBAR: 1006 break; 1007 case TRAYCMD_LOCK_TASKBAR: 1008 if (SHRestricted(REST_CLASSICSHELL) == 0) 1009 { 1010 Lock(!g_TaskbarSettings.bLock); 1011 } 1012 break; 1013 case TRAYCMD_HELP_AND_SUPPORT: 1014 ExecResourceCmd(IDS_HELP_COMMAND); 1015 break; 1016 case TRAYCMD_CONTROL_PANEL: 1017 // TODO: 1018 break; 1019 case TRAYCMD_SHUTDOWN_DIALOG: 1020 DoExitWindows(); 1021 break; 1022 case TRAYCMD_PRINTERS_AND_FAXES: 1023 // TODO: 1024 break; 1025 case TRAYCMD_LOCK_DESKTOP: 1026 // TODO: 1027 break; 1028 case TRAYCMD_SWITCH_USER_DIALOG: 1029 // TODO: 1030 break; 1031 case IDM_SEARCH: 1032 case TRAYCMD_SEARCH_FILES: 1033 SHFindFiles(NULL, NULL); 1034 break; 1035 case TRAYCMD_SEARCH_COMPUTERS: 1036 SHFindComputer(NULL, NULL); 1037 break; 1038 1039 default: 1040 break; 1041 } 1042 1043 return FALSE; 1044 } 1045 1046 1047 UINT TrackMenu( 1048 IN HMENU hMenu, 1049 IN POINT *ppt OPTIONAL, 1050 IN HWND hwndExclude OPTIONAL, 1051 IN BOOL TrackUp, 1052 IN BOOL IsContextMenu) 1053 { 1054 TPMPARAMS tmp, *ptmp = NULL; 1055 POINT pt; 1056 UINT cmdId; 1057 UINT fuFlags; 1058 1059 if (hwndExclude != NULL) 1060 { 1061 /* Get the client rectangle and map it to screen coordinates */ 1062 if (::GetClientRect(hwndExclude, 1063 &tmp.rcExclude) && 1064 ::MapWindowPoints(hwndExclude, 1065 NULL, 1066 (LPPOINT) &tmp.rcExclude, 1067 2) != 0) 1068 { 1069 ptmp = &tmp; 1070 } 1071 } 1072 1073 if (ppt == NULL) 1074 { 1075 if (ptmp == NULL && 1076 GetClientRect(&tmp.rcExclude) && 1077 MapWindowPoints( 1078 NULL, 1079 (LPPOINT) &tmp.rcExclude, 1080 2) != 0) 1081 { 1082 ptmp = &tmp; 1083 } 1084 1085 if (ptmp != NULL) 1086 { 1087 /* NOTE: TrackPopupMenuEx will eventually align the track position 1088 for us, no need to take care of it here as long as the 1089 coordinates are somewhere within the exclusion rectangle */ 1090 pt.x = ptmp->rcExclude.left; 1091 pt.y = ptmp->rcExclude.top; 1092 } 1093 else 1094 pt.x = pt.y = 0; 1095 } 1096 else 1097 pt = *ppt; 1098 1099 tmp.cbSize = sizeof(tmp); 1100 1101 fuFlags = TPM_RETURNCMD | TPM_VERTICAL; 1102 fuFlags |= (TrackUp ? TPM_BOTTOMALIGN : TPM_TOPALIGN); 1103 if (IsContextMenu) 1104 fuFlags |= TPM_RIGHTBUTTON; 1105 else 1106 fuFlags |= (TrackUp ? TPM_VERNEGANIMATION : TPM_VERPOSANIMATION); 1107 1108 cmdId = TrackPopupMenuEx(hMenu, 1109 fuFlags, 1110 pt.x, 1111 pt.y, 1112 m_hWnd, 1113 ptmp); 1114 1115 return cmdId; 1116 } 1117 1118 HRESULT TrackCtxMenu( 1119 IN IContextMenu * contextMenu, 1120 IN POINT *ppt OPTIONAL, 1121 IN HWND hwndExclude OPTIONAL, 1122 IN BOOL TrackUp, 1123 IN PVOID Context OPTIONAL) 1124 { 1125 POINT pt; 1126 TPMPARAMS params; 1127 RECT rc; 1128 HRESULT hr; 1129 UINT uCommand; 1130 HMENU popup = CreatePopupMenu(); 1131 1132 if (popup == NULL) 1133 return E_FAIL; 1134 1135 if (ppt) 1136 { 1137 pt = *ppt; 1138 } 1139 else 1140 { 1141 ::GetWindowRect(m_hWnd, &rc); 1142 pt.x = rc.left; 1143 pt.y = rc.top; 1144 } 1145 1146 TRACE("Before Query\n"); 1147 hr = contextMenu->QueryContextMenu(popup, 0, 0, UINT_MAX, CMF_NORMAL); 1148 if (FAILED_UNEXPECTEDLY(hr)) 1149 { 1150 TRACE("Query failed\n"); 1151 DestroyMenu(popup); 1152 return hr; 1153 } 1154 1155 TRACE("Before Tracking\n"); 1156 ::SetForegroundWindow(m_hWnd); 1157 if (hwndExclude) 1158 { 1159 ::GetWindowRect(hwndExclude, &rc); 1160 ZeroMemory(¶ms, sizeof(params)); 1161 params.cbSize = sizeof(params); 1162 params.rcExclude = rc; 1163 uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, pt.x, pt.y, m_hWnd, ¶ms); 1164 } 1165 else 1166 { 1167 uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, pt.x, pt.y, m_hWnd, NULL); 1168 } 1169 ::PostMessage(m_hWnd, WM_NULL, 0, 0); 1170 1171 if (uCommand != 0) 1172 { 1173 TRACE("Before InvokeCommand\n"); 1174 CMINVOKECOMMANDINFO cmi = { 0 }; 1175 cmi.cbSize = sizeof(cmi); 1176 cmi.lpVerb = MAKEINTRESOURCEA(uCommand); 1177 cmi.hwnd = m_hWnd; 1178 hr = contextMenu->InvokeCommand(&cmi); 1179 } 1180 else 1181 { 1182 TRACE("TrackPopupMenu failed. Code=%d, LastError=%d\n", uCommand, GetLastError()); 1183 hr = S_FALSE; 1184 } 1185 1186 DestroyMenu(popup); 1187 return hr; 1188 } 1189 1190 1191 1192 1193 1194 /********************************************************** 1195 * ##### moving and sizing handling ##### 1196 */ 1197 1198 void UpdateFonts() 1199 { 1200 /* There is nothing to do if themes are enabled */ 1201 if (m_Theme) 1202 return; 1203 1204 m_StartButton.UpdateFont(); 1205 1206 NONCLIENTMETRICS ncm = {sizeof(ncm)}; 1207 if (!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE)) 1208 { 1209 ERR("SPI_GETNONCLIENTMETRICS failed\n"); 1210 return; 1211 } 1212 1213 if (m_Font != NULL) 1214 DeleteObject(m_Font); 1215 1216 ncm.lfCaptionFont.lfWeight = FW_NORMAL; 1217 m_Font = CreateFontIndirect(&ncm.lfCaptionFont); 1218 if (!m_Font) 1219 { 1220 ERR("CreateFontIndirect failed\n"); 1221 return; 1222 } 1223 1224 SendMessage(m_Rebar, WM_SETFONT, (WPARAM) m_Font, TRUE); 1225 SendMessage(m_TaskSwitch, WM_SETFONT, (WPARAM) m_Font, TRUE); 1226 SendMessage(m_TrayNotify, WM_SETFONT, (WPARAM) m_Font, TRUE); 1227 } 1228 1229 HMONITOR GetScreenRectFromRect( 1230 IN OUT RECT *pRect, 1231 IN DWORD dwFlags) 1232 { 1233 MONITORINFO mi; 1234 HMONITOR hMon; 1235 1236 mi.cbSize = sizeof(mi); 1237 hMon = MonitorFromRect(pRect, dwFlags); 1238 if (hMon != NULL && 1239 GetMonitorInfo(hMon, &mi)) 1240 { 1241 *pRect = mi.rcMonitor; 1242 } 1243 else 1244 { 1245 pRect->left = 0; 1246 pRect->top = 0; 1247 pRect->right = GetSystemMetrics(SM_CXSCREEN); 1248 pRect->bottom = GetSystemMetrics(SM_CYSCREEN); 1249 1250 hMon = NULL; 1251 } 1252 1253 return hMon; 1254 } 1255 1256 HMONITOR GetMonitorFromRect( 1257 IN const RECT *pRect) 1258 { 1259 HMONITOR hMon; 1260 1261 /* In case the monitor sizes or saved sizes differ a bit (probably 1262 not a lot, only so the tray window overlaps into another monitor 1263 now), minimize the risk that we determine a wrong monitor by 1264 using the center point of the tray window if we can't determine 1265 it using the rectangle. */ 1266 hMon = MonitorFromRect(pRect, MONITOR_DEFAULTTONULL); 1267 if (hMon == NULL) 1268 { 1269 POINT pt; 1270 1271 pt.x = pRect->left + ((pRect->right - pRect->left) / 2); 1272 pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2); 1273 1274 /* be less error-prone, find the nearest monitor */ 1275 hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); 1276 } 1277 1278 return hMon; 1279 } 1280 1281 HMONITOR GetScreenRect( 1282 IN HMONITOR hMonitor, 1283 IN OUT RECT *pRect) 1284 { 1285 HMONITOR hMon = NULL; 1286 1287 if (hMonitor != NULL) 1288 { 1289 MONITORINFO mi; 1290 1291 mi.cbSize = sizeof(mi); 1292 if (!GetMonitorInfo(hMonitor, &mi)) 1293 { 1294 /* Hm, the monitor is gone? Try to find a monitor where it 1295 could be located now */ 1296 hMon = GetMonitorFromRect(pRect); 1297 if (hMon == NULL || 1298 !GetMonitorInfo(hMon, &mi)) 1299 { 1300 hMon = NULL; 1301 goto GetPrimaryRect; 1302 } 1303 } 1304 1305 *pRect = mi.rcMonitor; 1306 } 1307 else 1308 { 1309 GetPrimaryRect: 1310 pRect->left = 0; 1311 pRect->top = 0; 1312 pRect->right = GetSystemMetrics(SM_CXSCREEN); 1313 pRect->bottom = GetSystemMetrics(SM_CYSCREEN); 1314 } 1315 1316 return hMon; 1317 } 1318 1319 VOID AdjustSizerRect(RECT *rc, DWORD pos) 1320 { 1321 int iSizerPart[4] = {TBP_SIZINGBARLEFT, TBP_SIZINGBARTOP, TBP_SIZINGBARRIGHT, TBP_SIZINGBARBOTTOM}; 1322 SIZE size; 1323 1324 if (pos > ABE_BOTTOM) 1325 pos = ABE_BOTTOM; 1326 1327 HRESULT hr = GetThemePartSize(m_Theme, NULL, iSizerPart[pos], 0, NULL, TS_TRUE, &size); 1328 if (FAILED_UNEXPECTEDLY(hr)) 1329 return; 1330 1331 switch (pos) 1332 { 1333 case ABE_TOP: 1334 rc->bottom -= size.cy; 1335 break; 1336 case ABE_BOTTOM: 1337 rc->top += size.cy; 1338 break; 1339 case ABE_LEFT: 1340 rc->right -= size.cx; 1341 break; 1342 case ABE_RIGHT: 1343 rc->left += size.cx; 1344 break; 1345 } 1346 } 1347 1348 VOID MakeTrayRectWithSize(IN DWORD Position, 1349 IN const SIZE *pTraySize, 1350 IN OUT RECT *pRect) 1351 { 1352 switch (Position) 1353 { 1354 case ABE_LEFT: 1355 pRect->right = pRect->left + pTraySize->cx; 1356 break; 1357 1358 case ABE_TOP: 1359 pRect->bottom = pRect->top + pTraySize->cy; 1360 break; 1361 1362 case ABE_RIGHT: 1363 pRect->left = pRect->right - pTraySize->cx; 1364 break; 1365 1366 case ABE_BOTTOM: 1367 default: 1368 pRect->top = pRect->bottom - pTraySize->cy; 1369 break; 1370 } 1371 } 1372 1373 VOID GetTrayRectFromScreenRect(IN DWORD Position, 1374 IN const RECT *pScreen, 1375 IN const SIZE *pTraySize OPTIONAL, 1376 OUT RECT *pRect) 1377 { 1378 if (pTraySize == NULL) 1379 pTraySize = &m_TraySize; 1380 1381 *pRect = *pScreen; 1382 1383 if(!m_Theme) 1384 { 1385 /* Move the border outside of the screen */ 1386 InflateRect(pRect, 1387 GetSystemMetrics(SM_CXEDGE), 1388 GetSystemMetrics(SM_CYEDGE)); 1389 } 1390 1391 MakeTrayRectWithSize(Position, pTraySize, pRect); 1392 } 1393 1394 BOOL IsPosHorizontal() 1395 { 1396 return m_Position == ABE_TOP || m_Position == ABE_BOTTOM; 1397 } 1398 1399 HMONITOR CalculateValidSize( 1400 IN DWORD Position, 1401 IN OUT RECT *pRect) 1402 { 1403 RECT rcScreen; 1404 //BOOL Horizontal; 1405 HMONITOR hMon; 1406 SIZE szMax, szWnd; 1407 1408 //Horizontal = IsPosHorizontal(); 1409 1410 szWnd.cx = pRect->right - pRect->left; 1411 szWnd.cy = pRect->bottom - pRect->top; 1412 1413 rcScreen = *pRect; 1414 hMon = GetScreenRectFromRect( 1415 &rcScreen, 1416 MONITOR_DEFAULTTONEAREST); 1417 1418 /* Calculate the maximum size of the tray window and limit the window 1419 size to half of the screen's size. */ 1420 szMax.cx = (rcScreen.right - rcScreen.left) / 2; 1421 szMax.cy = (rcScreen.bottom - rcScreen.top) / 2; 1422 if (szWnd.cx > szMax.cx) 1423 szWnd.cx = szMax.cx; 1424 if (szWnd.cy > szMax.cy) 1425 szWnd.cy = szMax.cy; 1426 1427 /* FIXME - calculate */ 1428 1429 GetTrayRectFromScreenRect(Position, 1430 &rcScreen, 1431 &szWnd, 1432 pRect); 1433 1434 return hMon; 1435 } 1436 1437 #if 0 1438 VOID 1439 GetMinimumWindowSize( 1440 OUT RECT *pRect) 1441 { 1442 RECT rcMin = {0}; 1443 1444 AdjustWindowRectEx(&rcMin, 1445 GetWindowLong(m_hWnd, 1446 GWL_STYLE), 1447 FALSE, 1448 GetWindowLong(m_hWnd, 1449 GWL_EXSTYLE)); 1450 1451 *pRect = rcMin; 1452 } 1453 #endif 1454 1455 1456 DWORD GetDraggingRectFromPt( 1457 IN POINT pt, 1458 OUT RECT *pRect, 1459 OUT HMONITOR *phMonitor) 1460 { 1461 HMONITOR hMon, hMonNew; 1462 DWORD PosH, PosV, Pos; 1463 SIZE DeltaPt, ScreenOffset; 1464 RECT rcScreen; 1465 1466 rcScreen.left = 0; 1467 rcScreen.top = 0; 1468 1469 /* Determine the screen rectangle */ 1470 hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL); 1471 if (hMon != NULL) 1472 { 1473 MONITORINFO mi; 1474 1475 mi.cbSize = sizeof(mi); 1476 if (!GetMonitorInfo(hMon, &mi)) 1477 { 1478 hMon = NULL; 1479 goto GetPrimaryScreenRect; 1480 } 1481 1482 /* make left top corner of the screen zero based to 1483 make calculations easier */ 1484 pt.x -= mi.rcMonitor.left; 1485 pt.y -= mi.rcMonitor.top; 1486 1487 ScreenOffset.cx = mi.rcMonitor.left; 1488 ScreenOffset.cy = mi.rcMonitor.top; 1489 rcScreen.right = mi.rcMonitor.right - mi.rcMonitor.left; 1490 rcScreen.bottom = mi.rcMonitor.bottom - mi.rcMonitor.top; 1491 } 1492 else 1493 { 1494 GetPrimaryScreenRect: 1495 ScreenOffset.cx = 0; 1496 ScreenOffset.cy = 0; 1497 rcScreen.right = GetSystemMetrics(SM_CXSCREEN); 1498 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN); 1499 } 1500 1501 /* Calculate the nearest screen border */ 1502 if (pt.x < rcScreen.right / 2) 1503 { 1504 DeltaPt.cx = pt.x; 1505 PosH = ABE_LEFT; 1506 } 1507 else 1508 { 1509 DeltaPt.cx = rcScreen.right - pt.x; 1510 PosH = ABE_RIGHT; 1511 } 1512 1513 if (pt.y < rcScreen.bottom / 2) 1514 { 1515 DeltaPt.cy = pt.y; 1516 PosV = ABE_TOP; 1517 } 1518 else 1519 { 1520 DeltaPt.cy = rcScreen.bottom - pt.y; 1521 PosV = ABE_BOTTOM; 1522 } 1523 1524 Pos = (DeltaPt.cx * rcScreen.bottom < DeltaPt.cy * rcScreen.right) ? PosH : PosV; 1525 1526 /* Fix the screen origin to be relative to the primary monitor again */ 1527 OffsetRect(&rcScreen, 1528 ScreenOffset.cx, 1529 ScreenOffset.cy); 1530 1531 RECT rcPos = m_TrayRects[Pos]; 1532 1533 hMonNew = GetMonitorFromRect(&rcPos); 1534 if (hMon != hMonNew) 1535 { 1536 SIZE szTray; 1537 1538 /* Recalculate the rectangle, we're dragging to another monitor. 1539 We don't need to recalculate the rect on single monitor systems. */ 1540 szTray.cx = rcPos.right - rcPos.left; 1541 szTray.cy = rcPos.bottom - rcPos.top; 1542 1543 GetTrayRectFromScreenRect(Pos, &rcScreen, &szTray, pRect); 1544 hMon = hMonNew; 1545 } 1546 else 1547 { 1548 /* The user is dragging the tray window on the same monitor. We don't need 1549 to recalculate the rectangle */ 1550 *pRect = rcPos; 1551 } 1552 1553 *phMonitor = hMon; 1554 1555 return Pos; 1556 } 1557 1558 DWORD GetDraggingRectFromRect( 1559 IN OUT RECT *pRect, 1560 OUT HMONITOR *phMonitor) 1561 { 1562 POINT pt; 1563 1564 /* Calculate the center of the rectangle. We call 1565 GetDraggingRectFromPt to calculate a valid 1566 dragging rectangle */ 1567 pt.x = pRect->left + ((pRect->right - pRect->left) / 2); 1568 pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2); 1569 1570 return GetDraggingRectFromPt( 1571 pt, 1572 pRect, 1573 phMonitor); 1574 } 1575 1576 VOID ChangingWinPos(IN OUT LPWINDOWPOS pwp) 1577 { 1578 RECT rcTray; 1579 1580 if (IsDragging) 1581 { 1582 rcTray.left = pwp->x; 1583 rcTray.top = pwp->y; 1584 rcTray.right = rcTray.left + pwp->cx; 1585 rcTray.bottom = rcTray.top + pwp->cy; 1586 1587 if (!EqualRect(&rcTray, 1588 &m_TrayRects[m_DraggingPosition])) 1589 { 1590 /* Recalculate the rectangle, the user dragged the tray 1591 window to another monitor or the window was somehow else 1592 moved or resized */ 1593 m_DraggingPosition = GetDraggingRectFromRect( 1594 &rcTray, 1595 &m_DraggingMonitor); 1596 //m_TrayRects[DraggingPosition] = rcTray; 1597 } 1598 1599 //Monitor = CalculateValidSize(DraggingPosition, 1600 // &rcTray); 1601 1602 m_Monitor = m_DraggingMonitor; 1603 m_Position = m_DraggingPosition; 1604 IsDragging = FALSE; 1605 1606 m_TrayRects[m_Position] = rcTray; 1607 goto ChangePos; 1608 } 1609 else if (GetWindowRect(&rcTray)) 1610 { 1611 if (InSizeMove) 1612 { 1613 if (!(pwp->flags & SWP_NOMOVE)) 1614 { 1615 rcTray.left = pwp->x; 1616 rcTray.top = pwp->y; 1617 } 1618 1619 if (!(pwp->flags & SWP_NOSIZE)) 1620 { 1621 rcTray.right = rcTray.left + pwp->cx; 1622 rcTray.bottom = rcTray.top + pwp->cy; 1623 } 1624 1625 m_Position = GetDraggingRectFromRect(&rcTray, &m_Monitor); 1626 1627 if (!(pwp->flags & (SWP_NOMOVE | SWP_NOSIZE))) 1628 { 1629 SIZE szWnd; 1630 1631 szWnd.cx = pwp->cx; 1632 szWnd.cy = pwp->cy; 1633 1634 MakeTrayRectWithSize(m_Position, &szWnd, &rcTray); 1635 } 1636 1637 m_TrayRects[m_Position] = rcTray; 1638 } 1639 else if (m_Position != (DWORD)-1) 1640 { 1641 /* If the user isn't resizing the tray window we need to make sure the 1642 new size or position is valid. this is to prevent changes to the window 1643 without user interaction. */ 1644 rcTray = m_TrayRects[m_Position]; 1645 1646 if (g_TaskbarSettings.sr.AutoHide) 1647 { 1648 rcTray.left += m_AutoHideOffset.cx; 1649 rcTray.right += m_AutoHideOffset.cx; 1650 rcTray.top += m_AutoHideOffset.cy; 1651 rcTray.bottom += m_AutoHideOffset.cy; 1652 } 1653 1654 } 1655 1656 ChangePos: 1657 m_TraySize.cx = rcTray.right - rcTray.left; 1658 m_TraySize.cy = rcTray.bottom - rcTray.top; 1659 1660 pwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE); 1661 pwp->x = rcTray.left; 1662 pwp->y = rcTray.top; 1663 pwp->cx = m_TraySize.cx; 1664 pwp->cy = m_TraySize.cy; 1665 } 1666 } 1667 1668 VOID ApplyClipping(IN BOOL Clip) 1669 { 1670 RECT rcClip, rcWindow; 1671 HRGN hClipRgn; 1672 1673 if (GetWindowRect(&rcWindow)) 1674 { 1675 /* Disable clipping on systems with only one monitor */ 1676 if (GetSystemMetrics(SM_CMONITORS) <= 1) 1677 Clip = FALSE; 1678 1679 if (Clip) 1680 { 1681 rcClip = rcWindow; 1682 1683 GetScreenRect(m_Monitor, &rcClip); 1684 1685 if (!IntersectRect(&rcClip, &rcClip, &rcWindow)) 1686 { 1687 rcClip = rcWindow; 1688 } 1689 1690 OffsetRect(&rcClip, 1691 -rcWindow.left, 1692 -rcWindow.top); 1693 1694 hClipRgn = CreateRectRgnIndirect(&rcClip); 1695 } 1696 else 1697 hClipRgn = NULL; 1698 1699 /* Set the clipping region or make sure the window isn't clipped 1700 by disabling it explicitly. */ 1701 SetWindowRgn(hClipRgn, TRUE); 1702 } 1703 } 1704 1705 VOID ResizeWorkArea() 1706 { 1707 #if !WIN7_DEBUG_MODE 1708 RECT rcTray, rcWorkArea; 1709 1710 /* If monitor has changed then fix the previous monitors work area */ 1711 if (m_PreviousMonitor != m_Monitor) 1712 { 1713 GetScreenRect(m_PreviousMonitor, &rcWorkArea); 1714 SystemParametersInfoW(SPI_SETWORKAREA, 1715 1, 1716 &rcWorkArea, 1717 SPIF_SENDCHANGE); 1718 } 1719 1720 rcTray = m_TrayRects[m_Position]; 1721 1722 GetScreenRect(m_Monitor, &rcWorkArea); 1723 m_PreviousMonitor = m_Monitor; 1724 1725 /* If AutoHide is false then change the workarea to exclude 1726 the area that the taskbar covers. */ 1727 if (!g_TaskbarSettings.sr.AutoHide) 1728 { 1729 switch (m_Position) 1730 { 1731 case ABE_TOP: 1732 rcWorkArea.top = rcTray.bottom; 1733 break; 1734 case ABE_LEFT: 1735 rcWorkArea.left = rcTray.right; 1736 break; 1737 case ABE_RIGHT: 1738 rcWorkArea.right = rcTray.left; 1739 break; 1740 case ABE_BOTTOM: 1741 rcWorkArea.bottom = rcTray.top; 1742 break; 1743 } 1744 } 1745 1746 /* 1747 * Resize the current monitor work area. Win32k will also send 1748 * a WM_SIZE message to automatically resize the desktop. 1749 */ 1750 SystemParametersInfoW(SPI_SETWORKAREA, 1751 1, 1752 &rcWorkArea, 1753 SPIF_SENDCHANGE); 1754 #endif 1755 } 1756 1757 VOID CheckTrayWndPosition() 1758 { 1759 /* Force the rebar bands to resize */ 1760 IUnknown_Exec(m_TrayBandSite, 1761 IID_IDeskBand, 1762 DBID_BANDINFOCHANGED, 1763 0, 1764 NULL, 1765 NULL); 1766 1767 /* Calculate the size of the taskbar based on the rebar */ 1768 FitToRebar(&m_TrayRects[m_Position]); 1769 1770 /* Move the tray window */ 1771 /* The handler of WM_WINDOWPOSCHANGING will override whatever size 1772 * and position we use here with m_TrayRects */ 1773 SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE); 1774 ResizeWorkArea(); 1775 ApplyClipping(TRUE); 1776 } 1777 1778 VOID RegLoadSettings() 1779 { 1780 DWORD Pos; 1781 RECT rcScreen; 1782 SIZE WndSize, EdgeSize, DlgFrameSize; 1783 SIZE StartBtnSize = m_StartButton.GetSize(); 1784 1785 EdgeSize.cx = GetSystemMetrics(SM_CXEDGE); 1786 EdgeSize.cy = GetSystemMetrics(SM_CYEDGE); 1787 DlgFrameSize.cx = GetSystemMetrics(SM_CXDLGFRAME); 1788 DlgFrameSize.cy = GetSystemMetrics(SM_CYDLGFRAME); 1789 1790 m_Position = g_TaskbarSettings.sr.Position; 1791 rcScreen = g_TaskbarSettings.sr.Rect; 1792 GetScreenRectFromRect(&rcScreen, MONITOR_DEFAULTTONEAREST); 1793 1794 if (!g_TaskbarSettings.sr.Size.cx || !g_TaskbarSettings.sr.Size.cy) 1795 { 1796 /* Use the minimum size of the taskbar, we'll use the start 1797 button as a minimum for now. Make sure we calculate the 1798 entire window size, not just the client size. However, we 1799 use a thinner border than a standard thick border, so that 1800 the start button and bands are not stuck to the screen border. */ 1801 if(!m_Theme) 1802 { 1803 g_TaskbarSettings.sr.Size.cx = StartBtnSize.cx + (2 * (EdgeSize.cx + DlgFrameSize.cx)); 1804 g_TaskbarSettings.sr.Size.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy)); 1805 } 1806 else 1807 { 1808 g_TaskbarSettings.sr.Size.cx = StartBtnSize.cx - EdgeSize.cx; 1809 g_TaskbarSettings.sr.Size.cy = StartBtnSize.cy - EdgeSize.cy; 1810 if(!g_TaskbarSettings.bLock) 1811 g_TaskbarSettings.sr.Size.cy += GetSystemMetrics(SM_CYSIZEFRAME); 1812 } 1813 } 1814 /* Determine a minimum tray window rectangle. The "client" height is 1815 zero here since we cannot determine an optimal minimum width when 1816 loaded as a vertical tray window. We just need to make sure the values 1817 loaded from the registry are at least. The windows explorer behaves 1818 the same way, it allows the user to save a zero width vertical tray 1819 window, but not a zero height horizontal tray window. */ 1820 if(!m_Theme) 1821 { 1822 WndSize.cx = 2 * (EdgeSize.cx + DlgFrameSize.cx); 1823 WndSize.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy)); 1824 } 1825 else 1826 { 1827 WndSize.cx = StartBtnSize.cx; 1828 WndSize.cy = StartBtnSize.cy - EdgeSize.cy; 1829 } 1830 1831 if (WndSize.cx < g_TaskbarSettings.sr.Size.cx) 1832 WndSize.cx = g_TaskbarSettings.sr.Size.cx; 1833 if (WndSize.cy < g_TaskbarSettings.sr.Size.cy) 1834 WndSize.cy = g_TaskbarSettings.sr.Size.cy; 1835 1836 /* Save the calculated size */ 1837 m_TraySize = WndSize; 1838 1839 /* Calculate all docking rectangles. We need to do this here so they're 1840 initialized and dragging the tray window to another position gives 1841 usable results */ 1842 for (Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++) 1843 { 1844 GetTrayRectFromScreenRect(Pos, 1845 &rcScreen, 1846 &m_TraySize, 1847 &m_TrayRects[Pos]); 1848 // TRACE("m_TrayRects[%d(%d)]: %d,%d,%d,%d\n", Pos, Position, m_TrayRects[Pos].left, m_TrayRects[Pos].top, m_TrayRects[Pos].right, m_TrayRects[Pos].bottom); 1849 } 1850 1851 /* Determine which monitor we are on. It shouldn't matter which docked 1852 position rectangle we use */ 1853 m_Monitor = GetMonitorFromRect(&m_TrayRects[ABE_LEFT]); 1854 } 1855 1856 VOID AlignControls(IN PRECT prcClient OPTIONAL) 1857 { 1858 RECT rcClient; 1859 SIZE TraySize, StartSize; 1860 POINT ptTrayNotify = { 0, 0 }; 1861 BOOL Horizontal; 1862 HDWP dwp; 1863 1864 m_StartButton.UpdateSize(); 1865 if (prcClient != NULL) 1866 { 1867 rcClient = *prcClient; 1868 } 1869 else 1870 { 1871 if (!GetClientRect(&rcClient)) 1872 { 1873 ERR("Could not get client rect lastErr=%d\n", GetLastError()); 1874 return; 1875 } 1876 } 1877 1878 Horizontal = IsPosHorizontal(); 1879 1880 /* We're about to resize/move the start button, the rebar control and 1881 the tray notification control */ 1882 dwp = BeginDeferWindowPos(4); 1883 if (dwp == NULL) 1884 { 1885 ERR("BeginDeferWindowPos failed. lastErr=%d\n", GetLastError()); 1886 return; 1887 } 1888 1889 /* Limit the Start button width to the client width, if necessary */ 1890 StartSize = m_StartButton.GetSize(); 1891 if (StartSize.cx > rcClient.right) 1892 StartSize.cx = rcClient.right; 1893 1894 HWND hwndTaskToolbar = ::GetWindow(m_TaskSwitch, GW_CHILD); 1895 if (hwndTaskToolbar) 1896 { 1897 DWORD size = SendMessageW(hwndTaskToolbar, TB_GETBUTTONSIZE, 0, 0); 1898 1899 /* Themed button covers Edge area as well */ 1900 StartSize.cy = HIWORD(size) + (m_Theme ? GetSystemMetrics(SM_CYEDGE) : 0); 1901 } 1902 1903 if (m_StartButton.m_hWnd != NULL) 1904 { 1905 /* Resize and reposition the button */ 1906 dwp = m_StartButton.DeferWindowPos(dwp, 1907 NULL, 1908 0, 1909 0, 1910 StartSize.cx, 1911 StartSize.cy, 1912 SWP_NOZORDER | SWP_NOACTIVATE); 1913 if (dwp == NULL) 1914 { 1915 ERR("DeferWindowPos for start button failed. lastErr=%d\n", GetLastError()); 1916 return; 1917 } 1918 } 1919 1920 if (m_ShowDesktopButton.m_hWnd) 1921 { 1922 // Get rectangle from rcClient 1923 RECT rc = rcClient; 1924 INT cxyShowDesktop = m_ShowDesktopButton.WidthOrHeight(); 1925 if (Horizontal) 1926 { 1927 rc.left = rc.right - cxyShowDesktop; 1928 rc.right += 5; // excessive 1929 } 1930 else 1931 { 1932 rc.top = rc.bottom - cxyShowDesktop; 1933 rc.bottom += 5; // excessive 1934 } 1935 1936 /* Resize and reposition the button */ 1937 dwp = m_ShowDesktopButton.DeferWindowPos(dwp, NULL, 1938 rc.left, rc.top, 1939 rc.right - rc.left, rc.bottom - rc.top, 1940 SWP_NOZORDER | SWP_NOACTIVATE); 1941 1942 // Adjust rcClient 1943 if (Horizontal) 1944 rcClient.right -= cxyShowDesktop + ::GetSystemMetrics(SM_CXEDGE); 1945 else 1946 rcClient.bottom -= cxyShowDesktop + ::GetSystemMetrics(SM_CYEDGE); 1947 } 1948 1949 /* Determine the size that the tray notification window needs */ 1950 if (Horizontal) 1951 { 1952 TraySize.cx = 0; 1953 TraySize.cy = rcClient.bottom; 1954 } 1955 else 1956 { 1957 TraySize.cx = rcClient.right; 1958 TraySize.cy = 0; 1959 } 1960 1961 if (m_TrayNotify != NULL && 1962 SendMessage(m_TrayNotify, 1963 TNWM_GETMINIMUMSIZE, 1964 (WPARAM)Horizontal, 1965 (LPARAM)&TraySize)) 1966 { 1967 /* Move the tray notification window to the desired location */ 1968 if (Horizontal) 1969 ptTrayNotify.x = rcClient.right - TraySize.cx; 1970 else 1971 ptTrayNotify.y = rcClient.bottom - TraySize.cy; 1972 1973 dwp = ::DeferWindowPos(dwp, 1974 m_TrayNotify, 1975 NULL, 1976 ptTrayNotify.x, 1977 ptTrayNotify.y, 1978 TraySize.cx, 1979 TraySize.cy, 1980 SWP_NOZORDER | SWP_NOACTIVATE); 1981 if (dwp == NULL) 1982 { 1983 ERR("DeferWindowPos for notification area failed. lastErr=%d\n", GetLastError()); 1984 return; 1985 } 1986 } 1987 1988 /* Resize/Move the rebar control */ 1989 if (m_Rebar != NULL) 1990 { 1991 POINT ptRebar = { 0, 0 }; 1992 SIZE szRebar; 1993 1994 SetWindowStyle(m_Rebar, CCS_VERT, Horizontal ? 0 : CCS_VERT); 1995 1996 if (Horizontal) 1997 { 1998 ptRebar.x = StartSize.cx + GetSystemMetrics(SM_CXSIZEFRAME); 1999 szRebar.cx = ptTrayNotify.x - ptRebar.x; 2000 szRebar.cy = rcClient.bottom; 2001 } 2002 else 2003 { 2004 ptRebar.y = StartSize.cy + GetSystemMetrics(SM_CYSIZEFRAME); 2005 szRebar.cx = rcClient.right; 2006 szRebar.cy = ptTrayNotify.y - ptRebar.y; 2007 } 2008 2009 dwp = ::DeferWindowPos(dwp, 2010 m_Rebar, 2011 NULL, 2012 ptRebar.x, 2013 ptRebar.y, 2014 szRebar.cx, 2015 szRebar.cy, 2016 SWP_NOZORDER | SWP_NOACTIVATE); 2017 } 2018 2019 if (dwp != NULL) 2020 EndDeferWindowPos(dwp); 2021 2022 if (m_TaskSwitch != NULL) 2023 { 2024 /* Update the task switch window configuration */ 2025 SendMessage(m_TaskSwitch, TSWM_UPDATETASKBARPOS, 0, 0); 2026 } 2027 } 2028 2029 void FitToRebar(PRECT pRect) 2030 { 2031 /* Get the rect of the rebar */ 2032 RECT rebarRect, taskbarRect, clientRect; 2033 ::GetWindowRect(m_Rebar, &rebarRect); 2034 ::GetWindowRect(m_hWnd, &taskbarRect); 2035 ::GetClientRect(m_hWnd, &clientRect); 2036 OffsetRect(&rebarRect, -taskbarRect.left, -taskbarRect.top); 2037 2038 /* Calculate the difference of size of the taskbar and the rebar */ 2039 SIZE margins; 2040 margins.cx = taskbarRect.right - taskbarRect.left - clientRect.right + clientRect.left; 2041 margins.cy = taskbarRect.bottom - taskbarRect.top - clientRect.bottom + clientRect.top; 2042 2043 /* Calculate the new size of the rebar and make it resize, then change the new taskbar size */ 2044 switch (m_Position) 2045 { 2046 case ABE_TOP: 2047 rebarRect.bottom = rebarRect.top + pRect->bottom - pRect->top - margins.cy; 2048 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect); 2049 pRect->bottom = pRect->top + rebarRect.bottom - rebarRect.top + margins.cy; 2050 break; 2051 case ABE_BOTTOM: 2052 rebarRect.top = rebarRect.bottom - (pRect->bottom - pRect->top - margins.cy); 2053 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect); 2054 pRect->top = pRect->bottom - (rebarRect.bottom - rebarRect.top + margins.cy); 2055 break; 2056 case ABE_LEFT: 2057 rebarRect.right = rebarRect.left + (pRect->right - pRect->left - margins.cx); 2058 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect); 2059 pRect->right = pRect->left + (rebarRect.right - rebarRect.left + margins.cx); 2060 break; 2061 case ABE_RIGHT: 2062 rebarRect.left = rebarRect.right - (pRect->right - pRect->left - margins.cx); 2063 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect); 2064 pRect->left = pRect->right - (rebarRect.right - rebarRect.left + margins.cx); 2065 break; 2066 } 2067 2068 CalculateValidSize(m_Position, pRect); 2069 } 2070 2071 void PopupStartMenu() 2072 { 2073 if (m_StartMenuPopup != NULL) 2074 { 2075 POINTL pt; 2076 RECTL rcExclude; 2077 DWORD dwFlags = 0; 2078 2079 if (m_StartButton.GetWindowRect((RECT*) &rcExclude)) 2080 { 2081 switch (m_Position) 2082 { 2083 case ABE_BOTTOM: 2084 pt.x = rcExclude.left; 2085 pt.y = rcExclude.top; 2086 dwFlags |= MPPF_TOP; 2087 break; 2088 case ABE_TOP: 2089 pt.x = rcExclude.left; 2090 pt.y = rcExclude.bottom; 2091 dwFlags |= MPPF_BOTTOM; 2092 break; 2093 case ABE_LEFT: 2094 pt.x = rcExclude.right; 2095 pt.y = rcExclude.top; 2096 dwFlags |= MPPF_RIGHT; 2097 break; 2098 case ABE_RIGHT: 2099 pt.x = rcExclude.left; 2100 pt.y = rcExclude.top; 2101 dwFlags |= MPPF_LEFT; 2102 break; 2103 } 2104 2105 m_StartMenuPopup->Popup(&pt, &rcExclude, dwFlags); 2106 2107 m_StartButton.SendMessageW(BM_SETSTATE, TRUE, 0); 2108 } 2109 } 2110 } 2111 2112 void ProcessMouseTracking() 2113 { 2114 RECT rcCurrent; 2115 POINT pt; 2116 BOOL over; 2117 UINT state = m_AutoHideState; 2118 2119 GetCursorPos(&pt); 2120 GetWindowRect(&rcCurrent); 2121 over = PtInRect(&rcCurrent, pt); 2122 2123 if (m_StartButton.SendMessage( BM_GETSTATE, 0, 0) != BST_UNCHECKED) 2124 { 2125 over = TRUE; 2126 } 2127 2128 if (over) 2129 { 2130 if (state == AUTOHIDE_HIDING) 2131 { 2132 TRACE("AutoHide cancelling hide.\n"); 2133 m_AutoHideState = AUTOHIDE_SHOWING; 2134 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL); 2135 } 2136 else if (state == AUTOHIDE_HIDDEN) 2137 { 2138 TRACE("AutoHide starting show.\n"); 2139 m_AutoHideState = AUTOHIDE_SHOWING; 2140 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_SHOW, NULL); 2141 } 2142 } 2143 else 2144 { 2145 if (state == AUTOHIDE_SHOWING) 2146 { 2147 TRACE("AutoHide cancelling show.\n"); 2148 m_AutoHideState = AUTOHIDE_HIDING; 2149 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL); 2150 } 2151 else if (state == AUTOHIDE_SHOWN) 2152 { 2153 TRACE("AutoHide starting hide.\n"); 2154 m_AutoHideState = AUTOHIDE_HIDING; 2155 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL); 2156 } 2157 2158 KillTimer(TIMER_ID_MOUSETRACK); 2159 } 2160 } 2161 2162 void ProcessAutoHide() 2163 { 2164 INT w = m_TraySize.cx - GetSystemMetrics(SM_CXSIZEFRAME); 2165 INT h = m_TraySize.cy - GetSystemMetrics(SM_CYSIZEFRAME); 2166 2167 switch (m_AutoHideState) 2168 { 2169 case AUTOHIDE_HIDING: 2170 switch (m_Position) 2171 { 2172 case ABE_LEFT: 2173 m_AutoHideOffset.cy = 0; 2174 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_HIDE; 2175 if (m_AutoHideOffset.cx < -w) 2176 m_AutoHideOffset.cx = w; 2177 break; 2178 case ABE_TOP: 2179 m_AutoHideOffset.cx = 0; 2180 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_HIDE; 2181 if (m_AutoHideOffset.cy < -h) 2182 m_AutoHideOffset.cy = h; 2183 break; 2184 case ABE_RIGHT: 2185 m_AutoHideOffset.cy = 0; 2186 m_AutoHideOffset.cx += AUTOHIDE_SPEED_HIDE; 2187 if (m_AutoHideOffset.cx > w) 2188 m_AutoHideOffset.cx = w; 2189 break; 2190 case ABE_BOTTOM: 2191 m_AutoHideOffset.cx = 0; 2192 m_AutoHideOffset.cy += AUTOHIDE_SPEED_HIDE; 2193 if (m_AutoHideOffset.cy > h) 2194 m_AutoHideOffset.cy = h; 2195 break; 2196 } 2197 2198 if (m_AutoHideOffset.cx != w && m_AutoHideOffset.cy != h) 2199 { 2200 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL); 2201 break; 2202 } 2203 2204 /* fallthrough */ 2205 case AUTOHIDE_HIDDEN: 2206 2207 switch (m_Position) 2208 { 2209 case ABE_LEFT: 2210 m_AutoHideOffset.cx = -w; 2211 m_AutoHideOffset.cy = 0; 2212 break; 2213 case ABE_TOP: 2214 m_AutoHideOffset.cx = 0; 2215 m_AutoHideOffset.cy = -h; 2216 break; 2217 case ABE_RIGHT: 2218 m_AutoHideOffset.cx = w; 2219 m_AutoHideOffset.cy = 0; 2220 break; 2221 case ABE_BOTTOM: 2222 m_AutoHideOffset.cx = 0; 2223 m_AutoHideOffset.cy = h; 2224 break; 2225 } 2226 2227 KillTimer(TIMER_ID_AUTOHIDE); 2228 m_AutoHideState = AUTOHIDE_HIDDEN; 2229 break; 2230 2231 case AUTOHIDE_SHOWING: 2232 if (m_AutoHideOffset.cx >= AUTOHIDE_SPEED_SHOW) 2233 { 2234 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_SHOW; 2235 } 2236 else if (m_AutoHideOffset.cx <= -AUTOHIDE_SPEED_SHOW) 2237 { 2238 m_AutoHideOffset.cx += AUTOHIDE_SPEED_SHOW; 2239 } 2240 else 2241 { 2242 m_AutoHideOffset.cx = 0; 2243 } 2244 2245 if (m_AutoHideOffset.cy >= AUTOHIDE_SPEED_SHOW) 2246 { 2247 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_SHOW; 2248 } 2249 else if (m_AutoHideOffset.cy <= -AUTOHIDE_SPEED_SHOW) 2250 { 2251 m_AutoHideOffset.cy += AUTOHIDE_SPEED_SHOW; 2252 } 2253 else 2254 { 2255 m_AutoHideOffset.cy = 0; 2256 } 2257 2258 if (m_AutoHideOffset.cx != 0 || m_AutoHideOffset.cy != 0) 2259 { 2260 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL); 2261 break; 2262 } 2263 2264 /* fallthrough */ 2265 case AUTOHIDE_SHOWN: 2266 2267 KillTimer(TIMER_ID_AUTOHIDE); 2268 m_AutoHideState = AUTOHIDE_SHOWN; 2269 break; 2270 } 2271 2272 SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER); 2273 } 2274 2275 2276 2277 2278 2279 /********************************************************** 2280 * ##### taskbar drawing ##### 2281 */ 2282 2283 LRESULT EraseBackgroundWithTheme(HDC hdc) 2284 { 2285 RECT rect; 2286 int iSBkgndPart[4] = {TBP_BACKGROUNDLEFT, TBP_BACKGROUNDTOP, TBP_BACKGROUNDRIGHT, TBP_BACKGROUNDBOTTOM}; 2287 2288 ASSERT(m_Position <= ABE_BOTTOM); 2289 2290 if (m_Theme) 2291 { 2292 GetClientRect(&rect); 2293 DrawThemeBackground(m_Theme, hdc, iSBkgndPart[m_Position], 0, &rect, 0); 2294 } 2295 2296 return 0; 2297 } 2298 2299 int DrawSizerWithTheme(IN HRGN hRgn) 2300 { 2301 HDC hdc; 2302 RECT rect; 2303 int iSizerPart[4] = {TBP_SIZINGBARLEFT, TBP_SIZINGBARTOP, TBP_SIZINGBARRIGHT, TBP_SIZINGBARBOTTOM}; 2304 SIZE size; 2305 2306 ASSERT(m_Position <= ABE_BOTTOM); 2307 2308 HRESULT hr = GetThemePartSize(m_Theme, NULL, iSizerPart[m_Position], 0, NULL, TS_TRUE, &size); 2309 if (FAILED_UNEXPECTEDLY(hr)) 2310 return 0; 2311 2312 GetWindowRect(&rect); 2313 OffsetRect(&rect, -rect.left, -rect.top); 2314 2315 hdc = GetWindowDC(); 2316 2317 switch (m_Position) 2318 { 2319 case ABE_LEFT: 2320 rect.left = rect.right - size.cx; 2321 break; 2322 case ABE_TOP: 2323 rect.top = rect.bottom - size.cy; 2324 break; 2325 case ABE_RIGHT: 2326 rect.right = rect.left + size.cx; 2327 break; 2328 case ABE_BOTTOM: 2329 default: 2330 rect.bottom = rect.top + size.cy; 2331 break; 2332 } 2333 2334 DrawThemeBackground(m_Theme, hdc, iSizerPart[m_Position], 0, &rect, 0); 2335 2336 ReleaseDC(hdc); 2337 return 0; 2338 } 2339 2340 2341 2342 2343 2344 /* 2345 * ITrayWindow 2346 */ 2347 HRESULT STDMETHODCALLTYPE Open() 2348 { 2349 RECT rcWnd; 2350 2351 /* Check if there's already a window created and try to show it. 2352 If it was somehow destroyed just create a new tray window. */ 2353 if (m_hWnd != NULL && IsWindow()) 2354 { 2355 return S_OK; 2356 } 2357 2358 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE; 2359 if (g_TaskbarSettings.sr.AlwaysOnTop) 2360 dwExStyle |= WS_EX_TOPMOST; 2361 2362 DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; 2363 if(!m_Theme) 2364 { 2365 dwStyle |= WS_THICKFRAME | WS_BORDER; 2366 } 2367 2368 ZeroMemory(&rcWnd, sizeof(rcWnd)); 2369 if (m_Position != (DWORD) -1) 2370 rcWnd = m_TrayRects[m_Position]; 2371 2372 if (!Create(NULL, rcWnd, NULL, dwStyle, dwExStyle)) 2373 return E_FAIL; 2374 2375 /* Align all controls on the tray window */ 2376 AlignControls(NULL); 2377 2378 /* Move the tray window to the right position and resize it if necessary */ 2379 CheckTrayWndPosition(); 2380 2381 return S_OK; 2382 } 2383 2384 HRESULT STDMETHODCALLTYPE Close() 2385 { 2386 if (m_hWnd != NULL) 2387 { 2388 SendMessage(m_hWnd, 2389 WM_APP_TRAYDESTROY, 2390 0, 2391 0); 2392 } 2393 2394 return S_OK; 2395 } 2396 2397 HWND STDMETHODCALLTYPE GetHWND() 2398 { 2399 return m_hWnd; 2400 } 2401 2402 BOOL STDMETHODCALLTYPE IsSpecialHWND(IN HWND hWnd) 2403 { 2404 return (m_hWnd == hWnd || 2405 (m_DesktopWnd != NULL && m_hWnd == m_DesktopWnd)); 2406 } 2407 2408 BOOL STDMETHODCALLTYPE IsHorizontal() 2409 { 2410 return IsPosHorizontal(); 2411 } 2412 2413 BOOL STDMETHODCALLTYPE Lock(IN BOOL bLock) 2414 { 2415 BOOL bPrevLock = g_TaskbarSettings.bLock; 2416 2417 if (g_TaskbarSettings.bLock != bLock) 2418 { 2419 g_TaskbarSettings.bLock = bLock; 2420 2421 if (m_TrayBandSite != NULL) 2422 { 2423 if (!SUCCEEDED(m_TrayBandSite->Lock(bLock))) 2424 { 2425 /* Reset?? */ 2426 g_TaskbarSettings.bLock = bPrevLock; 2427 return bPrevLock; 2428 } 2429 } 2430 2431 if (m_Theme) 2432 { 2433 /* Update cached tray sizes */ 2434 for(DWORD Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++) 2435 { 2436 RECT rcGripper = {0}; 2437 AdjustSizerRect(&rcGripper, Pos); 2438 2439 if(g_TaskbarSettings.bLock) 2440 { 2441 m_TrayRects[Pos].top += rcGripper.top; 2442 m_TrayRects[Pos].left += rcGripper.left; 2443 m_TrayRects[Pos].bottom += rcGripper.bottom; 2444 m_TrayRects[Pos].right += rcGripper.right; 2445 } 2446 else 2447 { 2448 m_TrayRects[Pos].top -= rcGripper.top; 2449 m_TrayRects[Pos].left -= rcGripper.left; 2450 m_TrayRects[Pos].bottom -= rcGripper.bottom; 2451 m_TrayRects[Pos].right -= rcGripper.right; 2452 } 2453 } 2454 } 2455 SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER); 2456 ResizeWorkArea(); 2457 ApplyClipping(TRUE); 2458 } 2459 2460 return bPrevLock; 2461 } 2462 2463 2464 /* 2465 * IContextMenu 2466 */ 2467 HRESULT STDMETHODCALLTYPE QueryContextMenu(HMENU hPopup, 2468 UINT indexMenu, 2469 UINT idCmdFirst, 2470 UINT idCmdLast, 2471 UINT uFlags) 2472 { 2473 if (!m_ContextMenu) 2474 { 2475 HRESULT hr = TrayWindowCtxMenuCreator(this, m_hWnd, &m_ContextMenu); 2476 if (FAILED_UNEXPECTEDLY(hr)) 2477 return hr; 2478 } 2479 2480 return m_ContextMenu->QueryContextMenu(hPopup, indexMenu, idCmdFirst, idCmdLast, uFlags); 2481 } 2482 2483 HRESULT STDMETHODCALLTYPE InvokeCommand(LPCMINVOKECOMMANDINFO lpici) 2484 { 2485 if (!m_ContextMenu) 2486 return E_INVALIDARG; 2487 2488 return m_ContextMenu->InvokeCommand(lpici); 2489 } 2490 2491 HRESULT STDMETHODCALLTYPE GetCommandString(UINT_PTR idCmd, 2492 UINT uType, 2493 UINT *pwReserved, 2494 LPSTR pszName, 2495 UINT cchMax) 2496 { 2497 if (!m_ContextMenu) 2498 return E_INVALIDARG; 2499 2500 return m_ContextMenu->GetCommandString(idCmd, uType, pwReserved, pszName, cchMax); 2501 } 2502 2503 BOOL IsShowDesktopButtonNeeded() // Read the registry value 2504 { 2505 return SHRegGetBoolUSValueW( 2506 L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced", 2507 L"TaskbarSd", 2508 FALSE, 2509 TRUE); 2510 } 2511 2512 /********************************************************** 2513 * ##### message handling ##### 2514 */ 2515 2516 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2517 { 2518 HRESULT hRet; 2519 2520 ((ITrayWindow*)this)->AddRef(); 2521 2522 SetWindowTheme(m_hWnd, L"TaskBar", NULL); 2523 2524 /* Create the Start button */ 2525 m_StartButton.Create(m_hWnd); 2526 2527 /* Create the 'Show Desktop' button if necessary */ 2528 if (IsShowDesktopButtonNeeded()) 2529 m_ShowDesktopButton.DoCreate(m_hWnd); 2530 2531 /* Load the saved tray window settings */ 2532 RegLoadSettings(); 2533 2534 /* Create and initialize the start menu */ 2535 HBITMAP hbmBanner = LoadBitmapW(hExplorerInstance, MAKEINTRESOURCEW(IDB_STARTMENU)); 2536 m_StartMenuPopup = CreateStartMenu(this, &m_StartMenuBand, hbmBanner, 0); 2537 2538 /* Create the task band */ 2539 hRet = CTaskBand_CreateInstance(this, m_StartButton.m_hWnd, IID_PPV_ARG(IDeskBand, &m_TaskBand)); 2540 if (FAILED_UNEXPECTEDLY(hRet)) 2541 return FALSE; 2542 2543 /* Create the rebar band site. This actually creates the rebar and the tasks toolbar. */ 2544 hRet = CTrayBandSite_CreateInstance(this, m_TaskBand, &m_TrayBandSite); 2545 if (FAILED_UNEXPECTEDLY(hRet)) 2546 return FALSE; 2547 2548 /* Create the tray notification window */ 2549 hRet = CTrayNotifyWnd_CreateInstance(m_hWnd, IID_PPV_ARG(IUnknown, &m_TrayNotifyInstance)); 2550 if (FAILED_UNEXPECTEDLY(hRet)) 2551 return FALSE; 2552 2553 /* Get the hwnd of the rebar */ 2554 hRet = IUnknown_GetWindow(m_TrayBandSite, &m_Rebar); 2555 if (FAILED_UNEXPECTEDLY(hRet)) 2556 return FALSE; 2557 2558 /* Get the hwnd of the tasks toolbar */ 2559 hRet = IUnknown_GetWindow(m_TaskBand, &m_TaskSwitch); 2560 if (FAILED_UNEXPECTEDLY(hRet)) 2561 return FALSE; 2562 2563 /* Get the hwnd of the tray notification window */ 2564 hRet = IUnknown_GetWindow(m_TrayNotifyInstance, &m_TrayNotify); 2565 if (FAILED_UNEXPECTEDLY(hRet)) 2566 return FALSE; 2567 2568 SetWindowTheme(m_Rebar, L"TaskBar", NULL); 2569 2570 UpdateFonts(); 2571 2572 InitShellServices(&m_ShellServices); 2573 2574 if (g_TaskbarSettings.sr.AutoHide) 2575 { 2576 m_AutoHideState = AUTOHIDE_HIDING; 2577 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL); 2578 } 2579 2580 /* Set the initial lock state in the band site */ 2581 m_TrayBandSite->Lock(g_TaskbarSettings.bLock); 2582 2583 RegisterHotKey(m_hWnd, IDHK_RUN, MOD_WIN, 'R'); 2584 RegisterHotKey(m_hWnd, IDHK_MINIMIZE_ALL, MOD_WIN, 'M'); 2585 RegisterHotKey(m_hWnd, IDHK_RESTORE_ALL, MOD_WIN|MOD_SHIFT, 'M'); 2586 RegisterHotKey(m_hWnd, IDHK_HELP, MOD_WIN, VK_F1); 2587 RegisterHotKey(m_hWnd, IDHK_EXPLORE, MOD_WIN, 'E'); 2588 RegisterHotKey(m_hWnd, IDHK_FIND, MOD_WIN, 'F'); 2589 RegisterHotKey(m_hWnd, IDHK_FIND_COMPUTER, MOD_WIN|MOD_CONTROL, 'F'); 2590 RegisterHotKey(m_hWnd, IDHK_NEXT_TASK, MOD_WIN, VK_TAB); 2591 RegisterHotKey(m_hWnd, IDHK_PREV_TASK, MOD_WIN|MOD_SHIFT, VK_TAB); 2592 RegisterHotKey(m_hWnd, IDHK_SYS_PROPERTIES, MOD_WIN, VK_PAUSE); 2593 RegisterHotKey(m_hWnd, IDHK_DESKTOP, MOD_WIN, 'D'); 2594 RegisterHotKey(m_hWnd, IDHK_PAGER, MOD_WIN, 'B'); 2595 2596 return TRUE; 2597 } 2598 2599 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2600 { 2601 if (m_Theme) 2602 CloseThemeData(m_Theme); 2603 2604 m_Theme = OpenThemeData(m_hWnd, L"TaskBar"); 2605 2606 if (m_Theme) 2607 { 2608 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, 0); 2609 } 2610 else 2611 { 2612 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, WS_THICKFRAME | WS_BORDER); 2613 } 2614 SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER); 2615 2616 return TRUE; 2617 } 2618 2619 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2620 { 2621 if (wParam == SPI_SETNONCLIENTMETRICS) 2622 { 2623 SendMessage(m_TrayNotify, uMsg, wParam, lParam); 2624 SendMessage(m_TaskSwitch, uMsg, wParam, lParam); 2625 UpdateFonts(); 2626 AlignControls(NULL); 2627 CheckTrayWndPosition(); 2628 } 2629 2630 return 0; 2631 } 2632 2633 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2634 { 2635 HDC hdc = (HDC) wParam; 2636 2637 if (!m_Theme) 2638 { 2639 bHandled = FALSE; 2640 return 0; 2641 } 2642 2643 return EraseBackgroundWithTheme(hdc); 2644 } 2645 2646 LRESULT OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2647 { 2648 /* Load the saved tray window settings */ 2649 RegLoadSettings(); 2650 2651 /* Move the tray window to the right position and resize it if necessary */ 2652 CheckTrayWndPosition(); 2653 2654 return TRUE; 2655 } 2656 2657 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2658 { 2659 COPYDATASTRUCT *pCopyData = reinterpret_cast<COPYDATASTRUCT *>(lParam); 2660 switch (pCopyData->dwData) 2661 { 2662 case TABDMC_APPBAR: 2663 return appbar_message(pCopyData); 2664 case TABDMC_NOTIFY: 2665 case TABDMC_LOADINPROC: 2666 return ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam); 2667 } 2668 return FALSE; 2669 } 2670 2671 // We have to draw non-client area because the 'Show Desktop' button is beyond client area. 2672 void DrawShowDesktopButton() 2673 { 2674 if (!m_ShowDesktopButton.IsWindow()) 2675 return; 2676 // Get the rectangle in window coordinates 2677 RECT rcButton, rcWnd; 2678 GetWindowRect(&rcWnd); 2679 m_ShowDesktopButton.GetWindowRect(&rcButton); 2680 ::OffsetRect(&rcButton, -rcWnd.left, -rcWnd.top); 2681 2682 HDC hdc = GetDCEx(NULL, DCX_WINDOW | DCX_CACHE); 2683 m_ShowDesktopButton.OnDraw(hdc, &rcButton); // Draw the button 2684 ReleaseDC(hdc); 2685 } 2686 2687 LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2688 { 2689 DefWindowProc(uMsg, wParam, lParam); 2690 bHandled = TRUE; 2691 2692 if (!m_Theme || g_TaskbarSettings.bLock) 2693 { 2694 DrawShowDesktopButton(); // We have to draw non-client area 2695 return 0; 2696 } 2697 2698 DrawSizerWithTheme((HRGN) wParam); 2699 DrawShowDesktopButton(); // We have to draw non-client area 2700 return 0; 2701 } 2702 2703 LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2704 { 2705 SetBkMode((HDC) wParam, TRANSPARENT); 2706 return (LRESULT) GetStockObject(HOLLOW_BRUSH); 2707 } 2708 2709 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2710 { 2711 RECT rcClient; 2712 POINT pt; 2713 2714 if (g_TaskbarSettings.bLock) 2715 { 2716 /* The user may not be able to resize the tray window. 2717 Pretend like the window is not sizeable when the user 2718 clicks on the border. */ 2719 return HTBORDER; 2720 } 2721 2722 SetLastError(ERROR_SUCCESS); 2723 if (GetClientRect(&rcClient) && 2724 (MapWindowPoints(NULL, (LPPOINT) &rcClient, 2) != 0 || GetLastError() == ERROR_SUCCESS)) 2725 { 2726 pt.x = (SHORT) LOWORD(lParam); 2727 pt.y = (SHORT) HIWORD(lParam); 2728 2729 if (PtInRect(&rcClient, pt)) 2730 { 2731 /* The user is trying to drag the tray window */ 2732 return HTCAPTION; 2733 } 2734 2735 /* Depending on the position of the tray window, allow only 2736 changing the border next to the monitor working area */ 2737 switch (m_Position) 2738 { 2739 case ABE_TOP: 2740 if (pt.y > rcClient.bottom) 2741 return HTBOTTOM; 2742 break; 2743 case ABE_LEFT: 2744 if (pt.x > rcClient.right) 2745 return HTRIGHT; 2746 break; 2747 case ABE_RIGHT: 2748 if (pt.x < rcClient.left) 2749 return HTLEFT; 2750 break; 2751 case ABE_BOTTOM: 2752 default: 2753 if (pt.y < rcClient.top) 2754 return HTTOP; 2755 break; 2756 } 2757 } 2758 return HTBORDER; 2759 } 2760 2761 LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2762 { 2763 POINT ptCursor; 2764 PRECT pRect = (PRECT) lParam; 2765 2766 /* We need to ensure that an application can not accidently 2767 move the tray window (using SetWindowPos). However, we still 2768 need to be able to move the window in case the user wants to 2769 drag the tray window to another position or in case the user 2770 wants to resize the tray window. */ 2771 if (!g_TaskbarSettings.bLock && GetCursorPos(&ptCursor)) 2772 { 2773 IsDragging = TRUE; 2774 m_DraggingPosition = GetDraggingRectFromPt(ptCursor, pRect, &m_DraggingMonitor); 2775 } 2776 else 2777 { 2778 *pRect = m_TrayRects[m_Position]; 2779 } 2780 return TRUE; 2781 } 2782 2783 LRESULT OnSizing(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2784 { 2785 PRECT pRect = (PRECT) lParam; 2786 2787 if (!g_TaskbarSettings.bLock) 2788 { 2789 FitToRebar(pRect); 2790 } 2791 else 2792 { 2793 *pRect = m_TrayRects[m_Position]; 2794 } 2795 return TRUE; 2796 } 2797 2798 LRESULT OnWindowPosChanging(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2799 { 2800 ChangingWinPos((LPWINDOWPOS) lParam); 2801 return TRUE; 2802 } 2803 2804 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2805 { 2806 RECT rcClient; 2807 if (wParam == SIZE_RESTORED && lParam == 0) 2808 { 2809 ResizeWorkArea(); 2810 /* Clip the tray window on multi monitor systems so the edges can't 2811 overlap into another monitor */ 2812 ApplyClipping(TRUE); 2813 2814 if (!GetClientRect(&rcClient)) 2815 { 2816 return FALSE; 2817 } 2818 } 2819 else 2820 { 2821 rcClient.left = rcClient.top = 0; 2822 rcClient.right = LOWORD(lParam); 2823 rcClient.bottom = HIWORD(lParam); 2824 } 2825 2826 AlignControls(&rcClient); 2827 return TRUE; 2828 } 2829 2830 LRESULT OnEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2831 { 2832 InSizeMove = TRUE; 2833 IsDragging = FALSE; 2834 if (!g_TaskbarSettings.bLock) 2835 { 2836 /* Remove the clipping on multi monitor systems while dragging around */ 2837 ApplyClipping(FALSE); 2838 } 2839 return TRUE; 2840 } 2841 2842 LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2843 { 2844 InSizeMove = FALSE; 2845 if (!g_TaskbarSettings.bLock) 2846 { 2847 FitToRebar(&m_TrayRects[m_Position]); 2848 2849 /* Apply clipping */ 2850 PostMessage(WM_SIZE, SIZE_RESTORED, 0); 2851 } 2852 return TRUE; 2853 } 2854 2855 LRESULT OnSysChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2856 { 2857 switch (wParam) 2858 { 2859 case TEXT(' '): 2860 { 2861 /* The user pressed Alt+Space, this usually brings up the system menu of a window. 2862 The tray window needs to handle this specially, since it normally doesn't have 2863 a system menu. */ 2864 2865 static const UINT uidDisableItem [] = { 2866 SC_RESTORE, 2867 SC_MOVE, 2868 SC_SIZE, 2869 SC_MAXIMIZE, 2870 SC_MINIMIZE, 2871 }; 2872 HMENU hSysMenu; 2873 UINT i, uId; 2874 2875 /* temporarily enable the system menu */ 2876 SetWindowStyle(m_hWnd, WS_SYSMENU, WS_SYSMENU); 2877 2878 hSysMenu = GetSystemMenu(FALSE); 2879 if (hSysMenu != NULL) 2880 { 2881 /* Disable all items that are not relevant */ 2882 for (i = 0; i < _countof(uidDisableItem); i++) 2883 { 2884 EnableMenuItem(hSysMenu, 2885 uidDisableItem[i], 2886 MF_BYCOMMAND | MF_GRAYED); 2887 } 2888 2889 EnableMenuItem(hSysMenu, 2890 SC_CLOSE, 2891 MF_BYCOMMAND | 2892 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED)); 2893 2894 /* Display the system menu */ 2895 uId = TrackMenu( 2896 hSysMenu, 2897 NULL, 2898 m_StartButton.m_hWnd, 2899 m_Position != ABE_TOP, 2900 FALSE); 2901 if (uId != 0) 2902 { 2903 SendMessage(m_hWnd, WM_SYSCOMMAND, (WPARAM) uId, 0); 2904 } 2905 } 2906 2907 /* revert the system menu window style */ 2908 SetWindowStyle(m_hWnd, WS_SYSMENU, 0); 2909 break; 2910 } 2911 2912 default: 2913 bHandled = FALSE; 2914 } 2915 return TRUE; 2916 } 2917 2918 LRESULT OnNcLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2919 { 2920 /* This handler implements the trick that makes the start button to 2921 get pressed when the user clicked left or below the button */ 2922 2923 POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; 2924 WINDOWINFO wi = {sizeof(WINDOWINFO)}; 2925 2926 bHandled = FALSE; 2927 2928 RECT rcStartBtn; 2929 m_StartButton.GetWindowRect(&rcStartBtn); 2930 2931 GetWindowInfo(m_hWnd, &wi); 2932 2933 switch (m_Position) 2934 { 2935 case ABE_TOP: 2936 case ABE_LEFT: 2937 { 2938 if (pt.x > rcStartBtn.right || pt.y > rcStartBtn.bottom) 2939 return 0; 2940 break; 2941 } 2942 case ABE_RIGHT: 2943 { 2944 if (pt.x < rcStartBtn.left || pt.y > rcStartBtn.bottom) 2945 return 0; 2946 2947 if (rcStartBtn.right + (int)wi.cxWindowBorders * 2 + 1 < wi.rcWindow.right && 2948 pt.x > rcStartBtn.right) 2949 { 2950 return 0; 2951 } 2952 break; 2953 } 2954 case ABE_BOTTOM: 2955 { 2956 if (pt.x > rcStartBtn.right || pt.y < rcStartBtn.top) 2957 return 0; 2958 2959 if (rcStartBtn.bottom + (int)wi.cyWindowBorders * 2 + 1 < wi.rcWindow.bottom && 2960 pt.y > rcStartBtn.bottom) 2961 { 2962 return 0; 2963 } 2964 2965 break; 2966 } 2967 } 2968 2969 bHandled = TRUE; 2970 PopupStartMenu(); 2971 return 0; 2972 } 2973 2974 LRESULT OnNcRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2975 { 2976 /* We want the user to be able to get a context menu even on the nonclient 2977 area (including the sizing border)! */ 2978 uMsg = WM_CONTEXTMENU; 2979 wParam = (WPARAM) m_hWnd; 2980 2981 return OnContextMenu(uMsg, wParam, lParam, bHandled); 2982 } 2983 2984 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 2985 { 2986 LRESULT Ret = FALSE; 2987 POINT pt, *ppt = NULL; 2988 HWND hWndExclude = NULL; 2989 2990 /* Check if the administrator has forbidden access to context menus */ 2991 if (SHRestricted(REST_NOTRAYCONTEXTMENU)) 2992 return FALSE; 2993 2994 pt.x = (SHORT) LOWORD(lParam); 2995 pt.y = (SHORT) HIWORD(lParam); 2996 2997 if (pt.x != -1 || pt.y != -1) 2998 ppt = &pt; 2999 else 3000 hWndExclude = m_StartButton.m_hWnd; 3001 3002 if ((HWND) wParam == m_StartButton.m_hWnd) 3003 { 3004 /* Make sure we can't track the context menu if the start 3005 menu is currently being shown */ 3006 if (!(m_StartButton.SendMessage(BM_GETSTATE, 0, 0) & BST_PUSHED)) 3007 { 3008 CComPtr<IContextMenu> ctxMenu; 3009 CStartMenuBtnCtxMenu_CreateInstance(this, m_hWnd, &ctxMenu); 3010 TrackCtxMenu(ctxMenu, ppt, hWndExclude, m_Position == ABE_BOTTOM, this); 3011 } 3012 } 3013 else 3014 { 3015 /* See if the context menu should be handled by the task band site */ 3016 if (ppt != NULL && m_TrayBandSite != NULL) 3017 { 3018 HWND hWndAtPt; 3019 POINT ptClient = *ppt; 3020 3021 /* Convert the coordinates to client-coordinates */ 3022 ::MapWindowPoints(NULL, m_hWnd, &ptClient, 1); 3023 3024 hWndAtPt = ChildWindowFromPoint(ptClient); 3025 if (hWndAtPt != NULL && 3026 (hWndAtPt == m_Rebar || ::IsChild(m_Rebar, hWndAtPt))) 3027 { 3028 /* Check if the user clicked on the task switch window */ 3029 ptClient = *ppt; 3030 ::MapWindowPoints(NULL, m_Rebar, &ptClient, 1); 3031 3032 hWndAtPt = ::ChildWindowFromPointEx(m_Rebar, ptClient, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED); 3033 if (hWndAtPt == m_TaskSwitch) 3034 goto HandleTrayContextMenu; 3035 3036 /* Forward the message to the task band site */ 3037 m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret); 3038 } 3039 else 3040 goto HandleTrayContextMenu; 3041 } 3042 else 3043 { 3044 HandleTrayContextMenu: 3045 /* Tray the default tray window context menu */ 3046 TrackCtxMenu(this, ppt, NULL, FALSE, this); 3047 } 3048 } 3049 return Ret; 3050 } 3051 3052 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3053 { 3054 LRESULT Ret = FALSE; 3055 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside 3056 the rebar control! But we shouldn't forward messages that the band 3057 site doesn't handle, such as other controls (start button, tray window */ 3058 3059 HRESULT hr = E_FAIL; 3060 3061 if (m_TrayBandSite) 3062 { 3063 hr = m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret); 3064 if (SUCCEEDED(hr)) 3065 return Ret; 3066 } 3067 3068 if (m_TrayBandSite == NULL || FAILED(hr)) 3069 { 3070 const NMHDR *nmh = (const NMHDR *) lParam; 3071 3072 if (nmh->hwndFrom == m_TrayNotify) 3073 { 3074 switch (nmh->code) 3075 { 3076 case NTNWM_REALIGN: 3077 /* Cause all controls to be aligned */ 3078 PostMessage(WM_SIZE, SIZE_RESTORED, 0); 3079 break; 3080 } 3081 } 3082 } 3083 return Ret; 3084 } 3085 3086 BOOL CheckShowDesktopButtonClick(LPARAM lParam, BOOL& bHandled) 3087 { 3088 POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; 3089 if (m_ShowDesktopButton.PtInButton(pt)) // Did you click the button? 3090 { 3091 m_ShowDesktopButton.Click(); 3092 bHandled = TRUE; 3093 return TRUE; 3094 } 3095 3096 return FALSE; 3097 } 3098 3099 LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3100 { 3101 /* Let the clock handle the double click */ 3102 ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam); 3103 3104 /* We "handle" this message so users can't cause a weird maximize/restore 3105 window animation when double-clicking the tray window! */ 3106 return TRUE; 3107 } 3108 3109 LRESULT OnNcLButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3110 { 3111 CheckShowDesktopButtonClick(lParam, bHandled); 3112 return FALSE; 3113 } 3114 3115 LRESULT OnAppTrayDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3116 { 3117 DestroyWindow(); 3118 return TRUE; 3119 } 3120 3121 LRESULT OnOpenStartMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3122 { 3123 HWND hwndStartMenu; 3124 HRESULT hr = IUnknown_GetWindow(m_StartMenuPopup, &hwndStartMenu); 3125 if (FAILED_UNEXPECTEDLY(hr)) 3126 return FALSE; 3127 3128 if (::IsWindowVisible(hwndStartMenu)) 3129 HideStartMenu(); 3130 else 3131 PopupStartMenu(); 3132 3133 return TRUE; 3134 } 3135 3136 LRESULT OnDoExitWindows(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3137 { 3138 /* 3139 * TWM_DOEXITWINDOWS is send by the CDesktopBrowser to us 3140 * to show the shutdown dialog. Also a WM_CLOSE message sent 3141 * by apps should show the dialog. 3142 */ 3143 return DoExitWindows(); 3144 } 3145 3146 LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3147 { 3148 if (wParam == SC_CLOSE) 3149 { 3150 return DoExitWindows(); 3151 } 3152 3153 bHandled = FALSE; 3154 return TRUE; 3155 } 3156 3157 LRESULT OnGetTaskSwitch(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3158 { 3159 bHandled = TRUE; 3160 return (LRESULT)m_TaskSwitch; 3161 } 3162 3163 LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3164 { 3165 return HandleHotKey(wParam); 3166 } 3167 3168 struct MINIMIZE_INFO 3169 { 3170 HWND hwndDesktop; 3171 HWND hTrayWnd; 3172 HWND hwndProgman; 3173 BOOL bRet; 3174 CSimpleArray<HWND> *pMinimizedAll; 3175 BOOL bShowDesktop; 3176 }; 3177 3178 static BOOL IsDialog(HWND hwnd) 3179 { 3180 WCHAR szClass[32]; 3181 GetClassNameW(hwnd, szClass, _countof(szClass)); 3182 return wcscmp(szClass, L"#32770") == 0; 3183 } 3184 3185 static BOOL CALLBACK MinimizeWindowsProc(HWND hwnd, LPARAM lParam) 3186 { 3187 MINIMIZE_INFO *info = (MINIMIZE_INFO *)lParam; 3188 if (hwnd == info->hwndDesktop || hwnd == info->hTrayWnd || 3189 hwnd == info->hwndProgman) 3190 { 3191 return TRUE; 3192 } 3193 if (!info->bShowDesktop) 3194 { 3195 if (!::IsWindowEnabled(hwnd) || IsDialog(hwnd)) 3196 return TRUE; 3197 HWND hwndOwner = ::GetWindow(hwnd, GW_OWNER); 3198 if (hwndOwner && !::IsWindowEnabled(hwndOwner)) 3199 return TRUE; 3200 } 3201 if (::IsWindowVisible(hwnd) && !::IsIconic(hwnd)) 3202 { 3203 ::ShowWindowAsync(hwnd, SW_MINIMIZE); 3204 info->bRet = TRUE; 3205 info->pMinimizedAll->Add(hwnd); 3206 } 3207 return TRUE; 3208 } 3209 3210 VOID MinimizeAll(BOOL bShowDesktop = FALSE) 3211 { 3212 MINIMIZE_INFO info; 3213 info.hwndDesktop = GetDesktopWindow();; 3214 info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL); 3215 info.hwndProgman = FindWindowW(L"Progman", NULL); 3216 info.bRet = FALSE; 3217 info.pMinimizedAll = &g_MinimizedAll; 3218 info.bShowDesktop = bShowDesktop; 3219 EnumWindows(MinimizeWindowsProc, (LPARAM)&info); 3220 3221 // invalid handles should be cleared to avoid mismatch of handles 3222 for (INT i = 0; i < g_MinimizedAll.GetSize(); ++i) 3223 { 3224 if (!::IsWindow(g_MinimizedAll[i])) 3225 g_MinimizedAll[i] = NULL; 3226 } 3227 3228 ::SetForegroundWindow(m_DesktopWnd); 3229 ::SetFocus(m_DesktopWnd); 3230 } 3231 3232 VOID ShowDesktop() 3233 { 3234 MinimizeAll(TRUE); 3235 } 3236 3237 VOID RestoreAll() 3238 { 3239 for (INT i = g_MinimizedAll.GetSize() - 1; i >= 0; --i) 3240 { 3241 HWND hwnd = g_MinimizedAll[i]; 3242 if (::IsWindowVisible(hwnd) && ::IsIconic(hwnd)) 3243 { 3244 ::ShowWindowAsync(hwnd, SW_RESTORE); 3245 } 3246 } 3247 g_MinimizedAll.RemoveAll(); 3248 } 3249 3250 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3251 { 3252 LRESULT Ret = FALSE; 3253 3254 if ((HWND) lParam == m_StartButton.m_hWnd) 3255 { 3256 return FALSE; 3257 } 3258 3259 if (m_TrayBandSite == NULL || FAILED_UNEXPECTEDLY(m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret))) 3260 { 3261 return HandleCommand(LOWORD(wParam)); 3262 } 3263 return Ret; 3264 } 3265 3266 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3267 { 3268 POINT pt; 3269 ::GetCursorPos(&pt); 3270 if (m_ShowDesktopButton.PtInButton(pt)) 3271 m_ShowDesktopButton.StartHovering(); 3272 3273 if (g_TaskbarSettings.sr.AutoHide) 3274 { 3275 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL); 3276 } 3277 3278 return TRUE; 3279 } 3280 3281 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3282 { 3283 if (wParam == TIMER_ID_MOUSETRACK) 3284 { 3285 ProcessMouseTracking(); 3286 } 3287 else if (wParam == TIMER_ID_AUTOHIDE) 3288 { 3289 ProcessAutoHide(); 3290 } 3291 3292 bHandled = FALSE; 3293 return TRUE; 3294 } 3295 3296 LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3297 { 3298 LRESULT ret = DefWindowProc(uMsg, wParam, lParam); 3299 DrawShowDesktopButton(); // We have to draw non-client area 3300 bHandled = TRUE; 3301 return ret; 3302 } 3303 3304 LRESULT OnNcCalcSize(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3305 { 3306 RECT *rc = NULL; 3307 /* Ignore WM_NCCALCSIZE if we are not themed or locked */ 3308 if(!m_Theme || g_TaskbarSettings.bLock) 3309 { 3310 bHandled = FALSE; 3311 return 0; 3312 } 3313 if(!wParam) 3314 { 3315 rc = (RECT*)wParam; 3316 } 3317 else 3318 { 3319 NCCALCSIZE_PARAMS *prms = (NCCALCSIZE_PARAMS*)lParam; 3320 if(prms->lppos->flags & SWP_NOSENDCHANGING) 3321 { 3322 bHandled = FALSE; 3323 return 0; 3324 } 3325 rc = &prms->rgrc[0]; 3326 } 3327 3328 AdjustSizerRect(rc, m_Position); 3329 3330 return 0; 3331 } 3332 3333 LRESULT OnInitMenuPopup(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3334 { 3335 HMENU hMenu = (HMENU)wParam; 3336 if (::IsThereAnyEffectiveWindow(FALSE)) 3337 { 3338 ::EnableMenuItem(hMenu, ID_SHELL_CMD_CASCADE_WND, MF_BYCOMMAND | MF_ENABLED); 3339 ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_H, MF_BYCOMMAND | MF_ENABLED); 3340 ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_V, MF_BYCOMMAND | MF_ENABLED); 3341 if (g_Arrangement != NONE) 3342 { 3343 CStringW strCaption((g_Arrangement == TILED) ? MAKEINTRESOURCEW(IDS_TRAYWND_UNDO_TILE) 3344 : MAKEINTRESOURCEW(IDS_TRAYWND_UNDO_CASCADE)); 3345 MENUITEMINFOW mii = { sizeof(mii) }; 3346 ::GetMenuItemInfoW(hMenu, ID_SHELL_CMD_UNDO_ACTION, FALSE, &mii); 3347 mii.fMask = MIIM_TYPE; 3348 mii.fType = MFT_STRING; 3349 mii.dwTypeData = const_cast<LPWSTR>(&strCaption[0]); 3350 ::SetMenuItemInfoW(hMenu, ID_SHELL_CMD_UNDO_ACTION, FALSE, &mii); 3351 } 3352 else 3353 { 3354 ::DeleteMenu(hMenu, ID_SHELL_CMD_UNDO_ACTION, MF_BYCOMMAND); 3355 } 3356 } 3357 else 3358 { 3359 ::EnableMenuItem(hMenu, ID_SHELL_CMD_CASCADE_WND, MF_BYCOMMAND | MF_GRAYED); 3360 ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_H, MF_BYCOMMAND | MF_GRAYED); 3361 ::EnableMenuItem(hMenu, ID_SHELL_CMD_TILE_WND_V, MF_BYCOMMAND | MF_GRAYED); 3362 ::DeleteMenu(hMenu, ID_SHELL_CMD_UNDO_ACTION, MF_BYCOMMAND); 3363 g_Arrangement = NONE; 3364 g_WindowPosBackup.RemoveAll(); 3365 } 3366 return 0; 3367 } 3368 3369 LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled) 3370 { 3371 #if 0 3372 LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr; 3373 3374 if (!as->fChanged) 3375 return 0; 3376 3377 RECT rc; 3378 ::GetWindowRect(m_hWnd, &rc); 3379 3380 SIZE szWindow = { 3381 rc.right - rc.left, 3382 rc.bottom - rc.top }; 3383 SIZE szTarget = { 3384 as->rcTarget.right - as->rcTarget.left, 3385 as->rcTarget.bottom - as->rcTarget.top }; 3386 SIZE szActual = { 3387 as->rcActual.right - as->rcActual.left, 3388 as->rcActual.bottom - as->rcActual.top }; 3389 3390 SIZE borders = { 3391 szWindow.cx - szTarget.cx, 3392 szWindow.cy - szTarget.cx, 3393 }; 3394 3395 switch (m_Position) 3396 { 3397 case ABE_LEFT: 3398 szWindow.cx = szActual.cx + borders.cx; 3399 break; 3400 case ABE_TOP: 3401 szWindow.cy = szActual.cy + borders.cy; 3402 break; 3403 case ABE_RIGHT: 3404 szWindow.cx = szActual.cx + borders.cx; 3405 rc.left = rc.right - szWindow.cy; 3406 break; 3407 case ABE_BOTTOM: 3408 szWindow.cy = szActual.cy + borders.cy; 3409 rc.top = rc.bottom - szWindow.cy; 3410 break; 3411 } 3412 3413 SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER); 3414 #else 3415 bHandled = FALSE; 3416 #endif 3417 return 0; 3418 } 3419 3420 LRESULT OnTaskbarSettingsChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) 3421 { 3422 TaskbarSettings* newSettings = (TaskbarSettings*)lParam; 3423 3424 /* Propagate the new settings to the children */ 3425 ::SendMessageW(m_TaskSwitch, uMsg, wParam, lParam); 3426 ::SendMessageW(m_TrayNotify, uMsg, wParam, lParam); 3427 3428 /* Toggle autohide */ 3429 if (newSettings->sr.AutoHide != g_TaskbarSettings.sr.AutoHide) 3430 { 3431 g_TaskbarSettings.sr.AutoHide = newSettings->sr.AutoHide; 3432 memset(&m_AutoHideOffset, 0, sizeof(m_AutoHideOffset)); 3433 m_AutoHideState = AUTOHIDE_SHOWN; 3434 if (!newSettings->sr.AutoHide) 3435 SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER); 3436 else 3437 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL); 3438 } 3439 3440 /* Toggle lock state */ 3441 Lock(newSettings->bLock); 3442 3443 /* Toggle OnTop state */ 3444 if (newSettings->sr.AlwaysOnTop != g_TaskbarSettings.sr.AlwaysOnTop) 3445 { 3446 g_TaskbarSettings.sr.AlwaysOnTop = newSettings->sr.AlwaysOnTop; 3447 HWND hWndInsertAfter = newSettings->sr.AlwaysOnTop ? HWND_TOPMOST : HWND_BOTTOM; 3448 SetWindowPos(hWndInsertAfter, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); 3449 } 3450 3451 g_TaskbarSettings.Save(); 3452 return 0; 3453 } 3454 3455 DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE) 3456 3457 BEGIN_MSG_MAP(CTrayWindow) 3458 if (m_StartMenuBand != NULL) 3459 { 3460 MSG Msg; 3461 LRESULT lRet; 3462 3463 Msg.hwnd = m_hWnd; 3464 Msg.message = uMsg; 3465 Msg.wParam = wParam; 3466 Msg.lParam = lParam; 3467 3468 if (m_StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK) 3469 { 3470 return lRet; 3471 } 3472 3473 wParam = Msg.wParam; 3474 lParam = Msg.lParam; 3475 } 3476 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged) 3477 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged) 3478 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P 3479 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) 3480 MESSAGE_HANDLER(WM_SIZE, OnSize) 3481 MESSAGE_HANDLER(WM_CREATE, OnCreate) 3482 /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/ 3483 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest) 3484 MESSAGE_HANDLER(WM_COMMAND, OnCommand) 3485 MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand) 3486 MESSAGE_HANDLER(WM_NOTIFY, OnNotify) 3487 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu) 3488 MESSAGE_HANDLER(WM_TIMER, OnTimer) 3489 MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange) 3490 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData) 3491 MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint) 3492 MESSAGE_HANDLER(WM_NCACTIVATE, OnNcActivate) 3493 MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn) 3494 MESSAGE_HANDLER(WM_MOVING, OnMoving) 3495 MESSAGE_HANDLER(WM_SIZING, OnSizing) 3496 MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChanging) 3497 MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove) 3498 MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove) 3499 MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNcLButtonDown) 3500 MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar) 3501 MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp) 3502 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick) 3503 MESSAGE_HANDLER(WM_NCLBUTTONUP, OnNcLButtonUp) 3504 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove) 3505 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove) 3506 MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy) 3507 MESSAGE_HANDLER(WM_CLOSE, OnDoExitWindows) 3508 MESSAGE_HANDLER(WM_HOTKEY, OnHotkey) 3509 MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize) 3510 MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup) 3511 MESSAGE_HANDLER(TWM_SETTINGSCHANGED, OnTaskbarSettingsChanged) 3512 MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu) 3513 MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows) 3514 MESSAGE_HANDLER(TWM_GETTASKSWITCH, OnGetTaskSwitch) 3515 ALT_MSG_MAP(1) 3516 END_MSG_MAP() 3517 3518 /*****************************************************************************/ 3519 3520 VOID TrayProcessMessages() 3521 { 3522 MSG Msg; 3523 3524 /* FIXME: We should keep a reference here... */ 3525 3526 while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) 3527 { 3528 if (Msg.message == WM_QUIT) 3529 break; 3530 3531 if (m_StartMenuBand == NULL || 3532 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK) 3533 { 3534 TranslateMessage(&Msg); 3535 DispatchMessage(&Msg); 3536 } 3537 } 3538 } 3539 3540 VOID TrayMessageLoop() 3541 { 3542 MSG Msg; 3543 BOOL Ret; 3544 3545 /* FIXME: We should keep a reference here... */ 3546 3547 while (true) 3548 { 3549 Ret = GetMessage(&Msg, NULL, 0, 0); 3550 3551 if (!Ret || Ret == -1) 3552 break; 3553 3554 if (m_StartMenuBand == NULL || 3555 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK) 3556 { 3557 TranslateMessage(&Msg); 3558 DispatchMessage(&Msg); 3559 } 3560 } 3561 } 3562 3563 /* 3564 * IShellDesktopTray 3565 * 3566 * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()! 3567 * These are the calls I observed, it may be wrong/incomplete/buggy!!! 3568 * The reason we implement it is because we have to use SHCreateDesktop() so 3569 * that the shell provides the desktop window and all the features that come 3570 * with it (especially positioning of desktop icons) 3571 */ 3572 3573 virtual ULONG STDMETHODCALLTYPE GetState() 3574 { 3575 /* FIXME: Return ABS_ flags? */ 3576 TRACE("IShellDesktopTray::GetState() unimplemented!\n"); 3577 return 0; 3578 } 3579 3580 virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray) 3581 { 3582 TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray); 3583 *phWndTray = m_hWnd; 3584 return S_OK; 3585 } 3586 3587 virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop) 3588 { 3589 TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop); 3590 3591 m_DesktopWnd = hWndDesktop; 3592 return S_OK; 3593 } 3594 3595 virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2) 3596 { 3597 TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2); 3598 return S_OK; 3599 } 3600 3601 virtual HRESULT RaiseStartButton() 3602 { 3603 m_StartButton.SendMessageW(BM_SETSTATE, FALSE, 0); 3604 return S_OK; 3605 } 3606 3607 HRESULT WINAPI GetWindow(HWND* phwnd) 3608 { 3609 if (!phwnd) 3610 return E_INVALIDARG; 3611 *phwnd = m_hWnd; 3612 return S_OK; 3613 } 3614 3615 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode) 3616 { 3617 return E_NOTIMPL; 3618 } 3619 3620 void _Init() 3621 { 3622 m_Position = (DWORD) -1; 3623 } 3624 3625 DECLARE_NOT_AGGREGATABLE(CTrayWindow) 3626 3627 DECLARE_PROTECT_FINAL_CONSTRUCT() 3628 BEGIN_COM_MAP(CTrayWindow) 3629 /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/ 3630 COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray) 3631 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow) 3632 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) 3633 END_COM_MAP() 3634 }; 3635 3636 class CTrayWindowCtxMenu : 3637 public CComCoClass<CTrayWindowCtxMenu>, 3638 public CComObjectRootEx<CComMultiThreadModelNoCS>, 3639 public IContextMenu 3640 { 3641 HWND hWndOwner; 3642 CComPtr<CTrayWindow> TrayWnd; 3643 CComPtr<IContextMenu> pcm; 3644 UINT m_idCmdCmFirst; 3645 3646 public: 3647 HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner) 3648 { 3649 this->TrayWnd = (CTrayWindow *) pTrayWnd; 3650 this->hWndOwner = hWndOwner; 3651 this->m_idCmdCmFirst = 0; 3652 return S_OK; 3653 } 3654 3655 virtual HRESULT STDMETHODCALLTYPE 3656 QueryContextMenu(HMENU hPopup, 3657 UINT indexMenu, 3658 UINT idCmdFirst, 3659 UINT idCmdLast, 3660 UINT uFlags) 3661 { 3662 HMENU hMenuBase; 3663 3664 hMenuBase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCEW(IDM_TRAYWND)); 3665 3666 if (g_MinimizedAll.GetSize() != 0 && !::IsThereAnyEffectiveWindow(TRUE)) 3667 { 3668 CStringW strRestoreAll(MAKEINTRESOURCEW(IDS_RESTORE_ALL)); 3669 MENUITEMINFOW mii = { sizeof(mii) }; 3670 mii.fMask = MIIM_ID | MIIM_TYPE; 3671 mii.wID = ID_SHELL_CMD_RESTORE_ALL; 3672 mii.fType = MFT_STRING; 3673 mii.dwTypeData = const_cast<LPWSTR>(&strRestoreAll[0]); 3674 SetMenuItemInfoW(hMenuBase, ID_SHELL_CMD_SHOW_DESKTOP, FALSE, &mii); 3675 } 3676 3677 if (!hMenuBase) 3678 return HRESULT_FROM_WIN32(GetLastError()); 3679 3680 if (SHRestricted(REST_CLASSICSHELL) != 0) 3681 { 3682 DeleteMenu(hPopup, 3683 ID_LOCKTASKBAR, 3684 MF_BYCOMMAND); 3685 } 3686 3687 CheckMenuItem(hMenuBase, 3688 ID_LOCKTASKBAR, 3689 MF_BYCOMMAND | (g_TaskbarSettings.bLock ? MF_CHECKED : MF_UNCHECKED)); 3690 3691 UINT idCmdNext; 3692 idCmdNext = Shell_MergeMenus(hPopup, hMenuBase, indexMenu, idCmdFirst, idCmdLast, MM_SUBMENUSHAVEIDS | MM_ADDSEPARATOR); 3693 m_idCmdCmFirst = idCmdNext - idCmdFirst; 3694 3695 ::DestroyMenu(hMenuBase); 3696 3697 if (TrayWnd->m_TrayBandSite != NULL) 3698 { 3699 pcm.Release(); 3700 if (FAILED(TrayWnd->m_TrayBandSite->AddContextMenus( 3701 hPopup, 3702 indexMenu, 3703 idCmdNext, 3704 idCmdLast, 3705 CMF_NORMAL, 3706 &pcm))) 3707 { 3708 WARN("AddContextMenus failed.\n"); 3709 pcm.Release(); 3710 } 3711 } 3712 3713 return S_OK; 3714 } 3715 3716 virtual HRESULT STDMETHODCALLTYPE 3717 InvokeCommand(LPCMINVOKECOMMANDINFO lpici) 3718 { 3719 UINT uiCmdId = PtrToUlong(lpici->lpVerb); 3720 if (uiCmdId != 0) 3721 { 3722 if (uiCmdId >= m_idCmdCmFirst) 3723 { 3724 CMINVOKECOMMANDINFO cmici = { 0 }; 3725 3726 if (pcm != NULL) 3727 { 3728 /* Setup and invoke the shell command */ 3729 cmici.cbSize = sizeof(cmici); 3730 cmici.hwnd = hWndOwner; 3731 cmici.lpVerb = (LPCSTR) MAKEINTRESOURCEW(uiCmdId - m_idCmdCmFirst); 3732 cmici.nShow = SW_NORMAL; 3733 3734 pcm->InvokeCommand(&cmici); 3735 } 3736 } 3737 else 3738 { 3739 TrayWnd->ExecContextMenuCmd(uiCmdId); 3740 } 3741 } 3742 3743 return S_OK; 3744 } 3745 3746 virtual HRESULT STDMETHODCALLTYPE 3747 GetCommandString(UINT_PTR idCmd, 3748 UINT uType, 3749 UINT *pwReserved, 3750 LPSTR pszName, 3751 UINT cchMax) 3752 { 3753 return E_NOTIMPL; 3754 } 3755 3756 CTrayWindowCtxMenu() 3757 { 3758 } 3759 3760 virtual ~CTrayWindowCtxMenu() 3761 { 3762 } 3763 3764 BEGIN_COM_MAP(CTrayWindowCtxMenu) 3765 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu) 3766 END_COM_MAP() 3767 }; 3768 3769 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu) 3770 { 3771 CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>(); 3772 mnu->Initialize(TrayWnd, hWndOwner); 3773 *ppCtxMenu = mnu; 3774 return S_OK; 3775 } 3776 3777 HRESULT CreateTrayWindow(ITrayWindow ** ppTray) 3778 { 3779 CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>(); 3780 if (Tray == NULL) 3781 return E_OUTOFMEMORY; 3782 3783 Tray->_Init(); 3784 Tray->Open(); 3785 3786 *ppTray = (ITrayWindow *) Tray; 3787 3788 return S_OK; 3789 } 3790 3791 HRESULT 3792 Tray_OnStartMenuDismissed(ITrayWindow* Tray) 3793 { 3794 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray); 3795 return TrayWindow->RaiseStartButton(); 3796 } 3797 3798 VOID TrayProcessMessages(ITrayWindow *Tray) 3799 { 3800 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray); 3801 TrayWindow->TrayProcessMessages(); 3802 } 3803 3804 VOID TrayMessageLoop(ITrayWindow *Tray) 3805 { 3806 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray); 3807 TrayWindow->TrayMessageLoop(); 3808 } 3809