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