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