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