Reactos
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(¶ms, sizeof(params));
979 params.cbSize = sizeof(params);
980 params.rcExclude = rc;
981 uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, pt.x, pt.y, m_hWnd, ¶ms);
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}