[EXPLORER] -Remove a debug print that wasn't supposed to be committed.
[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 const WCHAR szTrayWndClass[] = L"Shell_TrayWnd";
57
58 /*
59 * ITrayWindow
60 */
61
62 const GUID IID_IShellDesktopTray = { 0x213e2df9, 0x9a14, 0x4328, { 0x99, 0xb1, 0x69, 0x61, 0xf9, 0x14, 0x3c, 0xe9 } };
63
64 class CStartButton
65 : public CWindow
66 {
67 HIMAGELIST m_ImageList;
68 SIZE m_Size;
69 HFONT m_Font;
70
71 public:
72 CStartButton()
73 : m_ImageList(NULL),
74 m_Font(NULL)
75 {
76 m_Size.cx = 0;
77 m_Size.cy = 0;
78 }
79
80 virtual ~CStartButton()
81 {
82 if (m_ImageList != NULL)
83 ImageList_Destroy(m_ImageList);
84
85 if (m_Font != NULL)
86 DeleteObject(m_Font);
87 }
88
89 SIZE GetSize()
90 {
91 return m_Size;
92 }
93
94 VOID UpdateSize()
95 {
96 SIZE Size = { 0, 0 };
97
98 if (m_ImageList == NULL ||
99 !SendMessageW(BCM_GETIDEALSIZE, 0, (LPARAM) &Size))
100 {
101 Size.cx = 2 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYCAPTION) * 3;
102 }
103
104 Size.cy = max(Size.cy, GetSystemMetrics(SM_CYCAPTION));
105
106 /* Save the size of the start button */
107 m_Size = Size;
108 }
109
110 VOID UpdateFont()
111 {
112 /* Get the system fonts, we use the caption font, always bold, though. */
113 NONCLIENTMETRICS ncm = {sizeof(ncm)};
114 if (!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
115 return;
116
117 if (m_Font)
118 DeleteObject(m_Font);
119
120 ncm.lfCaptionFont.lfWeight = FW_BOLD;
121 m_Font = CreateFontIndirect(&ncm.lfCaptionFont);
122
123 SetFont(m_Font, FALSE);
124 }
125
126 VOID Initialize()
127 {
128 SetWindowTheme(m_hWnd, L"Start", NULL);
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 public IOleWindow
179 {
180 CStartButton m_StartButton;
181
182 CComPtr<IMenuBand> m_StartMenuBand;
183 CComPtr<IMenuPopup> m_StartMenuPopup;
184
185 CComPtr<IDeskBand> m_TaskBand;
186 HTHEME m_Theme;
187
188 HFONT m_Font;
189
190 HWND m_DesktopWnd;
191 HWND m_Rebar;
192 HWND m_TaskSwitch;
193 HWND m_TrayNotify;
194
195 CTrayNotifyWnd* m_TrayNotifyInstance;
196
197 DWORD m_Position;
198 HMONITOR m_Monitor;
199 HMONITOR m_PreviousMonitor;
200 DWORD m_DraggingPosition;
201 HMONITOR m_DraggingMonitor;
202
203 RECT m_TrayRects[4];
204 SIZE m_TraySize;
205
206 HWND m_TrayPropertiesOwner;
207 HWND m_RunFileDlgOwner;
208
209 UINT m_AutoHideState;
210 SIZE m_AutoHideOffset;
211 TRACKMOUSEEVENT m_MouseTrackingInfo;
212
213 HDPA m_ShellServices;
214
215 public:
216 CComPtr<ITrayBandSite> m_TrayBandSite;
217
218 union
219 {
220 DWORD Flags;
221 struct
222 {
223 DWORD AutoHide : 1;
224 DWORD AlwaysOnTop : 1;
225 DWORD SmSmallIcons : 1;
226 DWORD HideClock : 1;
227 DWORD Locked : 1;
228
229 /* UI Status */
230 DWORD InSizeMove : 1;
231 DWORD IsDragging : 1;
232 DWORD NewPosSize : 1;
233 };
234 };
235
236 public:
237 CTrayWindow() :
238 m_StartButton(),
239 m_Theme(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_Font != NULL)
271 {
272 DeleteObject(m_Font);
273 m_Font = NULL;
274 }
275
276 if (m_Theme)
277 {
278 CloseThemeData(m_Theme);
279 m_Theme = NULL;
280 }
281
282 PostQuitMessage(0);
283 }
284
285
286
287
288
289 /**********************************************************
290 * ##### command handling #####
291 */
292
293 HRESULT ExecResourceCmd(int id)
294 {
295 WCHAR szCommand[256];
296 WCHAR *pszParameters;
297
298 if (!LoadStringW(hExplorerInstance,
299 id,
300 szCommand,
301 _countof(szCommand)))
302 {
303 return E_FAIL;
304 }
305
306 pszParameters = wcschr(szCommand, L'>');
307 if (pszParameters)
308 {
309 *pszParameters = 0;
310 pszParameters++;
311 }
312
313 ShellExecuteW(m_hWnd, NULL, szCommand, pszParameters, NULL, SW_SHOWNORMAL);
314 return S_OK;
315 }
316
317 LRESULT DoExitWindows()
318 {
319 ExitWindowsDialog(m_hWnd);
320 return 0;
321 }
322
323 DWORD WINAPI RunFileDlgThread()
324 {
325 HWND hwnd;
326 RECT posRect;
327
328 m_StartButton.GetWindowRect(&posRect);
329
330 hwnd = CreateWindowEx(0,
331 WC_STATIC,
332 NULL,
333 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
334 posRect.left,
335 posRect.top,
336 posRect.right - posRect.left,
337 posRect.bottom - posRect.top,
338 NULL,
339 NULL,
340 NULL,
341 NULL);
342
343 m_RunFileDlgOwner = hwnd;
344
345 RunFileDlg(hwnd, NULL, NULL, NULL, NULL, RFF_CALCDIRECTORY);
346
347 m_RunFileDlgOwner = NULL;
348 ::DestroyWindow(hwnd);
349
350 return 0;
351 }
352
353 static DWORD WINAPI s_RunFileDlgThread(IN OUT PVOID pParam)
354 {
355 CTrayWindow * This = (CTrayWindow*) pParam;
356 return This->RunFileDlgThread();
357 }
358
359 void DisplayRunFileDlg()
360 {
361 HWND hRunDlg;
362 if (m_RunFileDlgOwner)
363 {
364 hRunDlg = ::GetLastActivePopup(m_RunFileDlgOwner);
365 if (hRunDlg != NULL &&
366 hRunDlg != m_RunFileDlgOwner)
367 {
368 SetForegroundWindow(hRunDlg);
369 return;
370 }
371 }
372
373 CloseHandle(CreateThread(NULL, 0, s_RunFileDlgThread, this, 0, NULL));
374 }
375
376 DWORD WINAPI TrayPropertiesThread()
377 {
378 HWND hwnd;
379 RECT posRect;
380
381 m_StartButton.GetWindowRect(&posRect);
382 hwnd = CreateWindowEx(0,
383 WC_STATIC,
384 NULL,
385 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
386 posRect.left,
387 posRect.top,
388 posRect.right - posRect.left,
389 posRect.bottom - posRect.top,
390 NULL,
391 NULL,
392 NULL,
393 NULL);
394
395 m_TrayPropertiesOwner = hwnd;
396
397 DisplayTrayProperties(hwnd);
398
399 m_TrayPropertiesOwner = NULL;
400 ::DestroyWindow(hwnd);
401
402 return 0;
403 }
404
405 static DWORD WINAPI s_TrayPropertiesThread(IN OUT PVOID pParam)
406 {
407 CTrayWindow *This = (CTrayWindow*) pParam;
408
409 return This->TrayPropertiesThread();
410 }
411
412 HWND STDMETHODCALLTYPE DisplayProperties()
413 {
414 HWND hTrayProp;
415
416 if (m_TrayPropertiesOwner)
417 {
418 hTrayProp = ::GetLastActivePopup(m_TrayPropertiesOwner);
419 if (hTrayProp != NULL &&
420 hTrayProp != m_TrayPropertiesOwner)
421 {
422 SetForegroundWindow(hTrayProp);
423 return NULL;
424 }
425 }
426
427 CloseHandle(CreateThread(NULL, 0, s_TrayPropertiesThread, this, 0, NULL));
428 return NULL;
429 }
430
431 VOID OpenCommonStartMenuDirectory(IN HWND hWndOwner, IN LPCTSTR lpOperation)
432 {
433 WCHAR szDir[MAX_PATH];
434
435 if (SHGetSpecialFolderPath(hWndOwner,
436 szDir,
437 CSIDL_COMMON_STARTMENU,
438 FALSE))
439 {
440 ShellExecute(hWndOwner,
441 lpOperation,
442 szDir,
443 NULL,
444 NULL,
445 SW_SHOWNORMAL);
446 }
447 }
448
449 VOID OpenTaskManager(IN HWND hWndOwner)
450 {
451 ShellExecute(hWndOwner,
452 TEXT("open"),
453 TEXT("taskmgr.exe"),
454 NULL,
455 NULL,
456 SW_SHOWNORMAL);
457 }
458
459 BOOL STDMETHODCALLTYPE ExecContextMenuCmd(IN UINT uiCmd)
460 {
461 switch (uiCmd)
462 {
463 case ID_SHELL_CMD_PROPERTIES:
464 DisplayProperties();
465 break;
466
467 case ID_SHELL_CMD_OPEN_ALL_USERS:
468 OpenCommonStartMenuDirectory(m_hWnd,
469 TEXT("open"));
470 break;
471
472 case ID_SHELL_CMD_EXPLORE_ALL_USERS:
473 OpenCommonStartMenuDirectory(m_hWnd,
474 TEXT("explore"));
475 break;
476
477 case ID_LOCKTASKBAR:
478 if (SHRestricted(REST_CLASSICSHELL) == 0)
479 {
480 Lock(!Locked);
481 }
482 break;
483
484 case ID_SHELL_CMD_OPEN_TASKMGR:
485 OpenTaskManager(m_hWnd);
486 break;
487
488 case ID_SHELL_CMD_UNDO_ACTION:
489 break;
490
491 case ID_SHELL_CMD_SHOW_DESKTOP:
492 break;
493
494 case ID_SHELL_CMD_TILE_WND_H:
495 TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL);
496 break;
497
498 case ID_SHELL_CMD_TILE_WND_V:
499 TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL);
500 break;
501
502 case ID_SHELL_CMD_CASCADE_WND:
503 CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL);
504 break;
505
506 case ID_SHELL_CMD_CUST_NOTIF:
507 ShowCustomizeNotifyIcons(hExplorerInstance, m_hWnd);
508 break;
509
510 case ID_SHELL_CMD_ADJUST_DAT:
511 //FIXME: Use SHRunControlPanel
512 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
513 break;
514
515 default:
516 TRACE("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd);
517 return FALSE;
518 }
519
520 return TRUE;
521 }
522
523 LRESULT HandleHotKey(DWORD id)
524 {
525 switch (id)
526 {
527 case IDHK_RUN:
528 DisplayRunFileDlg();
529 break;
530 case IDHK_HELP:
531 ExecResourceCmd(IDS_HELP_COMMAND);
532 break;
533 case IDHK_EXPLORE:
534 //FIXME: We don't support this yet:
535 //ShellExecuteW(0, L"explore", NULL, NULL, NULL, 1);
536 ShellExecuteW(0, NULL, L"explorer.exe", NULL, NULL, 1);
537 break;
538 case IDHK_FIND:
539 SHFindFiles(NULL, NULL);
540 break;
541 case IDHK_FIND_COMPUTER:
542 SHFindComputer(NULL, NULL);
543 break;
544 case IDHK_SYS_PROPERTIES:
545 //FIXME: Use SHRunControlPanel
546 ShellExecuteW(m_hWnd, NULL, L"sysdm.cpl", NULL, NULL, SW_NORMAL);
547 break;
548 case IDHK_NEXT_TASK:
549 break;
550 case IDHK_PREV_TASK:
551 break;
552 case IDHK_MINIMIZE_ALL:
553 break;
554 case IDHK_RESTORE_ALL:
555 break;
556 case IDHK_DESKTOP:
557 break;
558 case IDHK_PAGER:
559 break;
560 }
561
562 return 0;
563 }
564
565 LRESULT HandleCommand(UINT uCommand)
566 {
567 switch (uCommand)
568 {
569 case IDM_TASKBARANDSTARTMENU:
570 DisplayProperties();
571 break;
572
573 case IDM_SEARCH:
574 SHFindFiles(NULL, NULL);
575 break;
576
577 case IDM_HELPANDSUPPORT:
578 ExecResourceCmd(IDS_HELP_COMMAND);
579 break;
580
581 case IDM_RUN:
582 DisplayRunFileDlg();
583 break;
584
585 /* FIXME: Handle these commands as well */
586 case IDM_SYNCHRONIZE:
587 case IDM_DISCONNECT:
588 case IDM_UNDOCKCOMPUTER:
589 break;
590
591 case IDM_LOGOFF:
592 LogoffWindowsDialog(m_hWnd); // FIXME: Maybe handle it in a similar way as DoExitWindows?
593 break;
594
595 case IDM_SHUTDOWN:
596 DoExitWindows();
597 break;
598 }
599
600 return FALSE;
601 }
602
603
604 UINT TrackMenu(
605 IN HMENU hMenu,
606 IN POINT *ppt OPTIONAL,
607 IN HWND hwndExclude OPTIONAL,
608 IN BOOL TrackUp,
609 IN BOOL IsContextMenu)
610 {
611 TPMPARAMS tmp, *ptmp = NULL;
612 POINT pt;
613 UINT cmdId;
614 UINT fuFlags;
615
616 if (hwndExclude != NULL)
617 {
618 /* Get the client rectangle and map it to screen coordinates */
619 if (::GetClientRect(hwndExclude,
620 &tmp.rcExclude) &&
621 ::MapWindowPoints(hwndExclude,
622 NULL,
623 (LPPOINT) &tmp.rcExclude,
624 2) != 0)
625 {
626 ptmp = &tmp;
627 }
628 }
629
630 if (ppt == NULL)
631 {
632 if (ptmp == NULL &&
633 GetClientRect(&tmp.rcExclude) &&
634 MapWindowPoints(
635 NULL,
636 (LPPOINT) &tmp.rcExclude,
637 2) != 0)
638 {
639 ptmp = &tmp;
640 }
641
642 if (ptmp != NULL)
643 {
644 /* NOTE: TrackPopupMenuEx will eventually align the track position
645 for us, no need to take care of it here as long as the
646 coordinates are somewhere within the exclusion rectangle */
647 pt.x = ptmp->rcExclude.left;
648 pt.y = ptmp->rcExclude.top;
649 }
650 else
651 pt.x = pt.y = 0;
652 }
653 else
654 pt = *ppt;
655
656 tmp.cbSize = sizeof(tmp);
657
658 fuFlags = TPM_RETURNCMD | TPM_VERTICAL;
659 fuFlags |= (TrackUp ? TPM_BOTTOMALIGN : TPM_TOPALIGN);
660 if (IsContextMenu)
661 fuFlags |= TPM_RIGHTBUTTON;
662 else
663 fuFlags |= (TrackUp ? TPM_VERNEGANIMATION : TPM_VERPOSANIMATION);
664
665 cmdId = TrackPopupMenuEx(hMenu,
666 fuFlags,
667 pt.x,
668 pt.y,
669 m_hWnd,
670 ptmp);
671
672 return cmdId;
673 }
674
675 HRESULT TrackCtxMenu(
676 IN IContextMenu * contextMenu,
677 IN POINT *ppt OPTIONAL,
678 IN HWND hwndExclude OPTIONAL,
679 IN BOOL TrackUp,
680 IN PVOID Context OPTIONAL)
681 {
682 INT x = ppt->x;
683 INT y = ppt->y;
684 HRESULT hr;
685 UINT uCommand;
686 HMENU popup = CreatePopupMenu();
687
688 if (popup == NULL)
689 return E_FAIL;
690
691 TRACE("Before Query\n");
692 hr = contextMenu->QueryContextMenu(popup, 0, 0, UINT_MAX, CMF_NORMAL);
693 if (FAILED_UNEXPECTEDLY(hr))
694 {
695 TRACE("Query failed\n");
696 DestroyMenu(popup);
697 return hr;
698 }
699
700 TRACE("Before Tracking\n");
701 uCommand = ::TrackPopupMenuEx(popup, TPM_RETURNCMD, x, y, m_hWnd, NULL);
702
703 if (uCommand != 0)
704 {
705 TRACE("Before InvokeCommand\n");
706 CMINVOKECOMMANDINFO cmi = { 0 };
707 cmi.cbSize = sizeof(cmi);
708 cmi.lpVerb = MAKEINTRESOURCEA(uCommand);
709 cmi.hwnd = m_hWnd;
710 hr = contextMenu->InvokeCommand(&cmi);
711 }
712 else
713 {
714 TRACE("TrackPopupMenu failed. Code=%d, LastError=%d\n", uCommand, GetLastError());
715 hr = S_FALSE;
716 }
717
718 DestroyMenu(popup);
719 return hr;
720 }
721
722
723
724
725
726 /**********************************************************
727 * ##### moving and sizing handling #####
728 */
729
730 void UpdateFonts()
731 {
732 /* There is nothing to do if themes are not enabled */
733 if (m_Theme)
734 return;
735
736 m_StartButton.UpdateFont();
737
738 NONCLIENTMETRICS ncm = {sizeof(ncm)};
739 if (!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE))
740 {
741 ERR("SPI_GETNONCLIENTMETRICS failed\n");
742 return;
743 }
744
745 if (m_Font != NULL)
746 DeleteObject(m_Font);
747
748 ncm.lfCaptionFont.lfWeight = FW_NORMAL;
749 m_Font = CreateFontIndirect(&ncm.lfCaptionFont);
750 if (!m_Font)
751 {
752 ERR("CreateFontIndirect failed\n");
753 return;
754 }
755
756 SendMessage(m_Rebar, WM_SETFONT, (WPARAM) m_Font, TRUE);
757 SendMessage(m_TaskSwitch, WM_SETFONT, (WPARAM) m_Font, TRUE);
758 SendMessage(m_TrayNotify, WM_SETFONT, (WPARAM) m_Font, TRUE);
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(!m_Theme)
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 IUnknown_Exec(m_TrayBandSite,
1322 IID_IDeskBand,
1323 DBID_BANDINFOCHANGED,
1324 0,
1325 NULL,
1326 NULL);
1327
1328 FitToRebar(&rcTray);
1329 m_TrayRects[m_Position] = rcTray;
1330
1331 /* Move the tray window */
1332 SetWindowPos(NULL,
1333 rcTray.left,
1334 rcTray.top,
1335 rcTray.right - rcTray.left,
1336 rcTray.bottom - rcTray.top,
1337 SWP_NOZORDER | SWP_NOACTIVATE);
1338
1339 ResizeWorkArea();
1340
1341 ApplyClipping(TRUE);
1342 }
1343
1344 typedef struct _TW_STUCKRECTS2
1345 {
1346 DWORD cbSize;
1347 LONG Unknown;
1348 DWORD dwFlags;
1349 DWORD Position;
1350 SIZE Size;
1351 RECT Rect;
1352 } TW_STRUCKRECTS2, *PTW_STUCKRECTS2;
1353
1354 VOID RegLoadSettings()
1355 {
1356 DWORD Pos;
1357 TW_STRUCKRECTS2 sr;
1358 RECT rcScreen;
1359 SIZE WndSize, EdgeSize, DlgFrameSize;
1360 DWORD cbSize = sizeof(sr);
1361 SIZE StartBtnSize = m_StartButton.GetSize();
1362
1363 EdgeSize.cx = GetSystemMetrics(SM_CXEDGE);
1364 EdgeSize.cy = GetSystemMetrics(SM_CYEDGE);
1365 DlgFrameSize.cx = GetSystemMetrics(SM_CXDLGFRAME);
1366 DlgFrameSize.cy = GetSystemMetrics(SM_CYDLGFRAME);
1367
1368 if (SHGetValue(hkExplorer,
1369 TEXT("StuckRects2"),
1370 TEXT("Settings"),
1371 NULL,
1372 &sr,
1373 &cbSize) == ERROR_SUCCESS &&
1374 sr.cbSize == sizeof(sr))
1375 {
1376 AutoHide = (sr.dwFlags & ABS_AUTOHIDE) != 0;
1377 AlwaysOnTop = (sr.dwFlags & ABS_ALWAYSONTOP) != 0;
1378 SmSmallIcons = (sr.dwFlags & 0x4) != 0;
1379 HideClock = (sr.dwFlags & 0x8) != 0;
1380
1381 /* FIXME: Are there more flags? */
1382
1383 #if WIN7_DEBUG_MODE
1384 m_Position = ABE_LEFT;
1385 #else
1386 if (sr.Position > ABE_BOTTOM)
1387 m_Position = ABE_BOTTOM;
1388 else
1389 m_Position = sr.Position;
1390 #endif
1391
1392 /* Try to find out which monitor the tray window was located on last.
1393 Here we're only interested in the monitor screen that we think
1394 is the last one used. We're going to determine on which monitor
1395 we really are after calculating the docked position. */
1396 rcScreen = sr.Rect;
1397 GetScreenRectFromRect(
1398 &rcScreen,
1399 MONITOR_DEFAULTTONEAREST);
1400 }
1401 else
1402 {
1403 m_Position = ABE_BOTTOM;
1404 AlwaysOnTop = TRUE;
1405
1406 /* Use the minimum size of the taskbar, we'll use the start
1407 button as a minimum for now. Make sure we calculate the
1408 entire window size, not just the client size. However, we
1409 use a thinner border than a standard thick border, so that
1410 the start button and bands are not stuck to the screen border. */
1411 if(!m_Theme)
1412 {
1413 sr.Size.cx = StartBtnSize.cx + (2 * (EdgeSize.cx + DlgFrameSize.cx));
1414 sr.Size.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy));
1415 }
1416 else
1417 {
1418 sr.Size.cx = StartBtnSize.cx - EdgeSize.cx;
1419 sr.Size.cy = StartBtnSize.cy - EdgeSize.cy;
1420 if(!Locked)
1421 sr.Size.cy += GetSystemMetrics(SM_CYSIZEFRAME);
1422 }
1423
1424 /* Use the primary screen by default */
1425 rcScreen.left = 0;
1426 rcScreen.top = 0;
1427 rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
1428 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
1429 GetScreenRectFromRect(
1430 &rcScreen,
1431 MONITOR_DEFAULTTOPRIMARY);
1432 }
1433
1434 if (m_hWnd != NULL)
1435 SetWindowPos(
1436 AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
1437 0,
1438 0,
1439 0,
1440 0,
1441 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1442
1443 /* Determine a minimum tray window rectangle. The "client" height is
1444 zero here since we cannot determine an optimal minimum width when
1445 loaded as a vertical tray window. We just need to make sure the values
1446 loaded from the registry are at least. The windows explorer behaves
1447 the same way, it allows the user to save a zero width vertical tray
1448 window, but not a zero height horizontal tray window. */
1449 if(!m_Theme)
1450 {
1451 WndSize.cx = 2 * (EdgeSize.cx + DlgFrameSize.cx);
1452 WndSize.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy));
1453 }
1454 else
1455 {
1456 WndSize.cx = StartBtnSize.cx;
1457 WndSize.cy = StartBtnSize.cy - EdgeSize.cx;
1458 }
1459
1460 if (WndSize.cx < sr.Size.cx)
1461 WndSize.cx = sr.Size.cx;
1462 if (WndSize.cy < sr.Size.cy)
1463 WndSize.cy = sr.Size.cy;
1464
1465 /* Save the calculated size */
1466 m_TraySize = WndSize;
1467
1468 /* Calculate all docking rectangles. We need to do this here so they're
1469 initialized and dragging the tray window to another position gives
1470 usable results */
1471 for (Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++)
1472 {
1473 GetTrayRectFromScreenRect(Pos,
1474 &rcScreen,
1475 &m_TraySize,
1476 &m_TrayRects[Pos]);
1477 // 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);
1478 }
1479
1480 /* Determine which monitor we are on. It shouldn't matter which docked
1481 position rectangle we use */
1482 m_Monitor = GetMonitorFromRect(&m_TrayRects[ABE_LEFT]);
1483 }
1484
1485 VOID AlignControls(IN PRECT prcClient OPTIONAL)
1486 {
1487 RECT rcClient;
1488 SIZE TraySize, StartSize;
1489 POINT ptTrayNotify = { 0, 0 };
1490 BOOL Horizontal;
1491 HDWP dwp;
1492
1493 m_StartButton.UpdateSize();
1494 if (prcClient != NULL)
1495 {
1496 rcClient = *prcClient;
1497 }
1498 else
1499 {
1500 if (!GetClientRect(&rcClient))
1501 {
1502 ERR("Could not get client rect lastErr=%d\n", GetLastError());
1503 return;
1504 }
1505 }
1506
1507 Horizontal = IsPosHorizontal();
1508
1509 /* We're about to resize/move the start button, the rebar control and
1510 the tray notification control */
1511 dwp = BeginDeferWindowPos(3);
1512 if (dwp == NULL)
1513 {
1514 ERR("BeginDeferWindowPos failed. lastErr=%d\n", GetLastError());
1515 return;
1516 }
1517
1518 /* Limit the Start button width to the client width, if necessary */
1519 StartSize = m_StartButton.GetSize();
1520 if (StartSize.cx > rcClient.right)
1521 StartSize.cx = rcClient.right;
1522
1523 if (m_StartButton.m_hWnd != NULL)
1524 {
1525 /* Resize and reposition the button */
1526 dwp = m_StartButton.DeferWindowPos(dwp,
1527 NULL,
1528 0,
1529 0,
1530 StartSize.cx,
1531 StartSize.cy,
1532 SWP_NOZORDER | SWP_NOACTIVATE);
1533 if (dwp == NULL)
1534 {
1535 ERR("DeferWindowPos for start button failed. lastErr=%d\n", GetLastError());
1536 return;
1537 }
1538 }
1539
1540 /* Determine the size that the tray notification window needs */
1541 if (Horizontal)
1542 {
1543 TraySize.cx = 0;
1544 TraySize.cy = rcClient.bottom;
1545 }
1546 else
1547 {
1548 TraySize.cx = rcClient.right;
1549 TraySize.cy = 0;
1550 }
1551
1552 if (m_TrayNotify != NULL &&
1553 SendMessage(m_TrayNotify,
1554 TNWM_GETMINIMUMSIZE,
1555 (WPARAM)Horizontal,
1556 (LPARAM)&TraySize))
1557 {
1558 /* Move the tray notification window to the desired location */
1559 if (Horizontal)
1560 ptTrayNotify.x = rcClient.right - TraySize.cx;
1561 else
1562 ptTrayNotify.y = rcClient.bottom - TraySize.cy;
1563
1564 dwp = ::DeferWindowPos(dwp,
1565 m_TrayNotify,
1566 NULL,
1567 ptTrayNotify.x,
1568 ptTrayNotify.y,
1569 TraySize.cx,
1570 TraySize.cy,
1571 SWP_NOZORDER | SWP_NOACTIVATE);
1572 if (dwp == NULL)
1573 {
1574 ERR("DeferWindowPos for notification area failed. lastErr=%d\n", GetLastError());
1575 return;
1576 }
1577 }
1578
1579 /* Resize/Move the rebar control */
1580 if (m_Rebar != NULL)
1581 {
1582 POINT ptRebar = { 0, 0 };
1583 SIZE szRebar;
1584
1585 SetWindowStyle(m_Rebar, CCS_VERT, Horizontal ? 0 : CCS_VERT);
1586
1587 if (Horizontal)
1588 {
1589 ptRebar.x = StartSize.cx + GetSystemMetrics(SM_CXSIZEFRAME);
1590 szRebar.cx = ptTrayNotify.x - ptRebar.x;
1591 szRebar.cy = rcClient.bottom;
1592 }
1593 else
1594 {
1595 ptRebar.y = StartSize.cy + GetSystemMetrics(SM_CYSIZEFRAME);
1596 szRebar.cx = rcClient.right;
1597 szRebar.cy = ptTrayNotify.y - ptRebar.y;
1598 }
1599
1600 dwp = ::DeferWindowPos(dwp,
1601 m_Rebar,
1602 NULL,
1603 ptRebar.x,
1604 ptRebar.y,
1605 szRebar.cx,
1606 szRebar.cy,
1607 SWP_NOZORDER | SWP_NOACTIVATE);
1608 }
1609
1610 if (dwp != NULL)
1611 EndDeferWindowPos(dwp);
1612
1613 if (m_TaskSwitch != NULL)
1614 {
1615 /* Update the task switch window configuration */
1616 SendMessage(m_TaskSwitch, TSWM_UPDATETASKBARPOS, 0, 0);
1617 }
1618 }
1619
1620 void FitToRebar(PRECT pRect)
1621 {
1622 /* Get the rect of the rebar */
1623 RECT rebarRect, taskbarRect;
1624 ::GetWindowRect(m_Rebar, &rebarRect);
1625 ::GetWindowRect(m_hWnd, &taskbarRect);
1626 OffsetRect(&rebarRect, -taskbarRect.left, -taskbarRect.top);
1627
1628 /* Calculate the difference of size of the taskbar and the rebar */
1629 SIZE margins;
1630 margins.cx = taskbarRect.right - taskbarRect.left - rebarRect.right + rebarRect.left;
1631 margins.cy = taskbarRect.bottom - taskbarRect.top - rebarRect.bottom + rebarRect.top;
1632
1633 /* Calculate the new size of the rebar and make it resize, then change the new taskbar size */
1634 switch (m_Position)
1635 {
1636 case ABE_TOP:
1637 rebarRect.bottom = rebarRect.top + pRect->bottom - pRect->top - margins.cy;
1638 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect);
1639 pRect->bottom = pRect->top + rebarRect.bottom - rebarRect.top + margins.cy;
1640 break;
1641 case ABE_BOTTOM:
1642 rebarRect.top = rebarRect.bottom - (pRect->bottom - pRect->top - margins.cy);
1643 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect);
1644 pRect->top = pRect->bottom - (rebarRect.bottom - rebarRect.top + margins.cy);
1645 break;
1646 case ABE_LEFT:
1647 case ABE_RIGHT:
1648 /* FIXME: what to do here? */
1649 break;
1650 }
1651
1652 CalculateValidSize(m_Position, pRect);
1653 }
1654
1655 void PopupStartMenu()
1656 {
1657 if (m_StartMenuPopup != NULL)
1658 {
1659 POINTL pt;
1660 RECTL rcExclude;
1661 DWORD dwFlags = 0;
1662
1663 if (m_StartButton.GetWindowRect((RECT*) &rcExclude))
1664 {
1665 switch (m_Position)
1666 {
1667 case ABE_BOTTOM:
1668 pt.x = rcExclude.left;
1669 pt.y = rcExclude.top;
1670 dwFlags |= MPPF_TOP;
1671 break;
1672 case ABE_TOP:
1673 pt.x = rcExclude.left;
1674 pt.y = rcExclude.bottom;
1675 dwFlags |= MPPF_BOTTOM;
1676 break;
1677 case ABE_LEFT:
1678 pt.x = rcExclude.right;
1679 pt.y = rcExclude.top;
1680 dwFlags |= MPPF_RIGHT;
1681 break;
1682 case ABE_RIGHT:
1683 pt.x = rcExclude.left;
1684 pt.y = rcExclude.top;
1685 dwFlags |= MPPF_LEFT;
1686 break;
1687 }
1688
1689 m_StartMenuPopup->Popup(&pt, &rcExclude, dwFlags);
1690
1691 m_StartButton.SendMessageW(BM_SETSTATE, TRUE, 0);
1692 }
1693 }
1694 }
1695
1696 void ProcessMouseTracking()
1697 {
1698 RECT rcCurrent;
1699 POINT pt;
1700 BOOL over;
1701 UINT state = m_AutoHideState;
1702
1703 GetCursorPos(&pt);
1704 GetWindowRect(&rcCurrent);
1705 over = PtInRect(&rcCurrent, pt);
1706
1707 if (m_StartButton.SendMessage( BM_GETSTATE, 0, 0) != BST_UNCHECKED)
1708 {
1709 over = TRUE;
1710 }
1711
1712 if (over)
1713 {
1714 if (state == AUTOHIDE_HIDING)
1715 {
1716 TRACE("AutoHide cancelling hide.\n");
1717 m_AutoHideState = AUTOHIDE_SHOWING;
1718 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1719 }
1720 else if (state == AUTOHIDE_HIDDEN)
1721 {
1722 TRACE("AutoHide starting show.\n");
1723 m_AutoHideState = AUTOHIDE_SHOWING;
1724 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_SHOW, NULL);
1725 }
1726 }
1727 else
1728 {
1729 if (state == AUTOHIDE_SHOWING)
1730 {
1731 TRACE("AutoHide cancelling show.\n");
1732 m_AutoHideState = AUTOHIDE_HIDING;
1733 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1734 }
1735 else if (state == AUTOHIDE_SHOWN)
1736 {
1737 TRACE("AutoHide starting hide.\n");
1738 m_AutoHideState = AUTOHIDE_HIDING;
1739 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
1740 }
1741
1742 KillTimer(TIMER_ID_MOUSETRACK);
1743 }
1744 }
1745
1746 void ProcessAutoHide()
1747 {
1748 RECT rc = m_TrayRects[m_Position];
1749 INT w = m_TraySize.cx - GetSystemMetrics(SM_CXBORDER) * 2 - 1;
1750 INT h = m_TraySize.cy - GetSystemMetrics(SM_CYBORDER) * 2 - 1;
1751
1752 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);
1753
1754 switch (m_AutoHideState)
1755 {
1756 case AUTOHIDE_HIDING:
1757 switch (m_Position)
1758 {
1759 case ABE_LEFT:
1760 m_AutoHideOffset.cy = 0;
1761 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_HIDE;
1762 if (m_AutoHideOffset.cx < -w)
1763 m_AutoHideOffset.cx = -w;
1764 break;
1765 case ABE_TOP:
1766 m_AutoHideOffset.cx = 0;
1767 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_HIDE;
1768 if (m_AutoHideOffset.cy < -h)
1769 m_AutoHideOffset.cy = -h;
1770 break;
1771 case ABE_RIGHT:
1772 m_AutoHideOffset.cy = 0;
1773 m_AutoHideOffset.cx += AUTOHIDE_SPEED_HIDE;
1774 if (m_AutoHideOffset.cx > w)
1775 m_AutoHideOffset.cx = w;
1776 break;
1777 case ABE_BOTTOM:
1778 m_AutoHideOffset.cx = 0;
1779 m_AutoHideOffset.cy += AUTOHIDE_SPEED_HIDE;
1780 if (m_AutoHideOffset.cy > h)
1781 m_AutoHideOffset.cy = h;
1782 break;
1783 }
1784
1785 if (m_AutoHideOffset.cx != w && m_AutoHideOffset.cy != h)
1786 {
1787 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1788 break;
1789 }
1790
1791 /* fallthrough */
1792 case AUTOHIDE_HIDDEN:
1793
1794 switch (m_Position)
1795 {
1796 case ABE_LEFT:
1797 m_AutoHideOffset.cx = -w;
1798 m_AutoHideOffset.cy = 0;
1799 break;
1800 case ABE_TOP:
1801 m_AutoHideOffset.cx = 0;
1802 m_AutoHideOffset.cy = -h;
1803 break;
1804 case ABE_RIGHT:
1805 m_AutoHideOffset.cx = w;
1806 m_AutoHideOffset.cy = 0;
1807 break;
1808 case ABE_BOTTOM:
1809 m_AutoHideOffset.cx = 0;
1810 m_AutoHideOffset.cy = h;
1811 break;
1812 }
1813
1814 KillTimer(TIMER_ID_AUTOHIDE);
1815 m_AutoHideState = AUTOHIDE_HIDDEN;
1816 break;
1817
1818 case AUTOHIDE_SHOWING:
1819 if (m_AutoHideOffset.cx >= AUTOHIDE_SPEED_SHOW)
1820 {
1821 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_SHOW;
1822 }
1823 else if (m_AutoHideOffset.cx <= -AUTOHIDE_SPEED_SHOW)
1824 {
1825 m_AutoHideOffset.cx += AUTOHIDE_SPEED_SHOW;
1826 }
1827 else
1828 {
1829 m_AutoHideOffset.cx = 0;
1830 }
1831
1832 if (m_AutoHideOffset.cy >= AUTOHIDE_SPEED_SHOW)
1833 {
1834 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_SHOW;
1835 }
1836 else if (m_AutoHideOffset.cy <= -AUTOHIDE_SPEED_SHOW)
1837 {
1838 m_AutoHideOffset.cy += AUTOHIDE_SPEED_SHOW;
1839 }
1840 else
1841 {
1842 m_AutoHideOffset.cy = 0;
1843 }
1844
1845 if (m_AutoHideOffset.cx != 0 || m_AutoHideOffset.cy != 0)
1846 {
1847 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1848 break;
1849 }
1850
1851 /* fallthrough */
1852 case AUTOHIDE_SHOWN:
1853
1854 KillTimer(TIMER_ID_AUTOHIDE);
1855 m_AutoHideState = AUTOHIDE_SHOWN;
1856 break;
1857 }
1858
1859 rc.left += m_AutoHideOffset.cx;
1860 rc.right += m_AutoHideOffset.cx;
1861 rc.top += m_AutoHideOffset.cy;
1862 rc.bottom += m_AutoHideOffset.cy;
1863
1864 TRACE("AutoHide Changing position to (%d, %d, %d, %d) and state=%u.\n", rc.left, rc.top, rc.right, rc.bottom, m_AutoHideState);
1865 SetWindowPos(NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
1866 }
1867
1868
1869
1870
1871
1872 /**********************************************************
1873 * ##### taskbar drawing #####
1874 */
1875
1876 LRESULT EraseBackgroundWithTheme(HDC hdc)
1877 {
1878 RECT rect;
1879 int partId;
1880 HRESULT res;
1881
1882 GetClientRect(&rect);
1883
1884 if (m_Theme)
1885 {
1886 GetClientRect(&rect);
1887 switch (m_Position)
1888 {
1889 case ABE_LEFT:
1890 partId = TBP_BACKGROUNDLEFT;
1891 break;
1892 case ABE_TOP:
1893 partId = TBP_BACKGROUNDTOP;
1894 break;
1895 case ABE_RIGHT:
1896 partId = TBP_BACKGROUNDRIGHT;
1897 break;
1898 case ABE_BOTTOM:
1899 default:
1900 partId = TBP_BACKGROUNDBOTTOM;
1901 break;
1902 }
1903 res = DrawThemeBackground(m_Theme, hdc, partId, 0, &rect, 0);
1904 }
1905
1906 return res;
1907 }
1908
1909 int DrawSizerWithTheme(IN HRGN hRgn)
1910 {
1911 HDC hdc;
1912 RECT rect;
1913 int backgroundPart;
1914
1915 GetWindowRect(&rect);
1916 OffsetRect(&rect, -rect.left, -rect.top);
1917
1918 hdc = GetWindowDC();
1919
1920 switch (m_Position)
1921 {
1922 case ABE_LEFT:
1923 backgroundPart = TBP_SIZINGBARLEFT;
1924 rect.left = rect.right - GetSystemMetrics(SM_CXSIZEFRAME);
1925 break;
1926 case ABE_TOP:
1927 backgroundPart = TBP_SIZINGBARTOP;
1928 rect.top = rect.bottom - GetSystemMetrics(SM_CYSIZEFRAME);
1929 break;
1930 case ABE_RIGHT:
1931 backgroundPart = TBP_SIZINGBARRIGHT;
1932 rect.right = rect.left + GetSystemMetrics(SM_CXSIZEFRAME);
1933 break;
1934 case ABE_BOTTOM:
1935 default:
1936 backgroundPart = TBP_SIZINGBARBOTTOM;
1937 rect.bottom = rect.top + GetSystemMetrics(SM_CYSIZEFRAME);
1938 break;
1939 }
1940 if (IsThemeBackgroundPartiallyTransparent(m_Theme, backgroundPart, 0))
1941 {
1942 DrawThemeParentBackground(m_hWnd, hdc, &rect);
1943 }
1944 DrawThemeBackground(m_Theme, hdc, backgroundPart, 0, &rect, 0);
1945
1946 ReleaseDC(hdc);
1947 return 0;
1948 }
1949
1950
1951
1952
1953
1954 /*
1955 * ITrayWindow
1956 */
1957 HRESULT STDMETHODCALLTYPE Open()
1958 {
1959 RECT rcWnd;
1960
1961 /* Check if there's already a window created and try to show it.
1962 If it was somehow destroyed just create a new tray window. */
1963 if (m_hWnd != NULL && IsWindow())
1964 {
1965 return S_OK;
1966 }
1967
1968 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
1969 if (AlwaysOnTop)
1970 dwExStyle |= WS_EX_TOPMOST;
1971
1972 DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
1973 if(!m_Theme)
1974 {
1975 dwStyle |= WS_THICKFRAME | WS_BORDER;
1976 }
1977
1978 ZeroMemory(&rcWnd, sizeof(rcWnd));
1979 if (m_Position != (DWORD) -1)
1980 rcWnd = m_TrayRects[m_Position];
1981
1982 if (!Create(NULL, rcWnd, NULL, dwStyle, dwExStyle))
1983 return E_FAIL;
1984
1985 /* Align all controls on the tray window */
1986 AlignControls(NULL);
1987
1988 /* Move the tray window to the right position and resize it if necessary */
1989 CheckTrayWndPosition();
1990
1991 return S_OK;
1992 }
1993
1994 HRESULT STDMETHODCALLTYPE Close()
1995 {
1996 if (m_hWnd != NULL)
1997 {
1998 SendMessage(m_hWnd,
1999 WM_APP_TRAYDESTROY,
2000 0,
2001 0);
2002 }
2003
2004 return S_OK;
2005 }
2006
2007 HWND STDMETHODCALLTYPE GetHWND()
2008 {
2009 return m_hWnd;
2010 }
2011
2012 BOOL STDMETHODCALLTYPE IsSpecialHWND(IN HWND hWnd)
2013 {
2014 return (m_hWnd == hWnd ||
2015 (m_DesktopWnd != NULL && m_hWnd == m_DesktopWnd));
2016 }
2017
2018 BOOL STDMETHODCALLTYPE IsHorizontal()
2019 {
2020 return IsPosHorizontal();
2021 }
2022
2023 BOOL STDMETHODCALLTYPE Lock(IN BOOL bLock)
2024 {
2025 BOOL bPrevLock = Locked;
2026
2027 if (Locked != bLock)
2028 {
2029 Locked = bLock;
2030
2031 if (m_TrayBandSite != NULL)
2032 {
2033 if (!SUCCEEDED(m_TrayBandSite->Lock(bLock)))
2034 {
2035 /* Reset?? */
2036 Locked = bPrevLock;
2037 return bPrevLock;
2038 }
2039 }
2040
2041 if (m_Theme)
2042 {
2043 /* Update cached tray sizes */
2044 for(DWORD Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++)
2045 {
2046 RECT rcGripper = {0};
2047 AdjustSizerRect(&rcGripper, Pos);
2048
2049 if(Locked)
2050 {
2051 m_TrayRects[Pos].top += rcGripper.top;
2052 m_TrayRects[Pos].left += rcGripper.left;
2053 m_TrayRects[Pos].bottom += rcGripper.bottom;
2054 m_TrayRects[Pos].right += rcGripper.right;
2055 }
2056 else
2057 {
2058 m_TrayRects[Pos].top -= rcGripper.top;
2059 m_TrayRects[Pos].left -= rcGripper.left;
2060 m_TrayRects[Pos].bottom -= rcGripper.bottom;
2061 m_TrayRects[Pos].right -= rcGripper.right;
2062 }
2063 }
2064 }
2065 SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
2066 ResizeWorkArea();
2067 ApplyClipping(TRUE);
2068 }
2069
2070 return bPrevLock;
2071 }
2072
2073
2074
2075
2076
2077
2078 /**********************************************************
2079 * ##### message handling #####
2080 */
2081
2082 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2083 {
2084 HRESULT hRet;
2085
2086 ((ITrayWindow*)this)->AddRef();
2087
2088 SetWindowTheme(m_hWnd, L"TaskBar", NULL);
2089
2090 /* Create the Start button */
2091 m_StartButton.Create(m_hWnd);
2092
2093 /* Load the saved tray window settings */
2094 RegLoadSettings();
2095
2096 /* Create and initialize the start menu */
2097 HBITMAP hbmBanner = LoadBitmapW(hExplorerInstance, MAKEINTRESOURCEW(IDB_STARTMENU));
2098 m_StartMenuPopup = CreateStartMenu(this, &m_StartMenuBand, hbmBanner, 0);
2099
2100 /* Create the task band */
2101 hRet = CTaskBand_CreateInstance(this, m_StartButton.m_hWnd, IID_PPV_ARG(IDeskBand, &m_TaskBand));
2102 if (FAILED_UNEXPECTEDLY(hRet))
2103 return FALSE;
2104
2105 /* Create the rebar band site. This actually creates the rebar and the tasks toolbar. */
2106 hRet = CTrayBandSite_CreateInstance(this, m_TaskBand, &m_TrayBandSite);
2107 if (FAILED_UNEXPECTEDLY(hRet))
2108 return FALSE;
2109
2110 /* Get the hwnd of the rebar */
2111 hRet = IUnknown_GetWindow(m_TrayBandSite, &m_Rebar);
2112 if (FAILED_UNEXPECTEDLY(hRet))
2113 return FALSE;
2114
2115 /* Get the hwnd of the tasks toolbar */
2116 hRet = IUnknown_GetWindow(m_TaskBand, &m_TaskSwitch);
2117 if (FAILED_UNEXPECTEDLY(hRet))
2118 return FALSE;
2119
2120 SetWindowTheme(m_Rebar, L"TaskBar", NULL);
2121
2122 /* Create the tray notification window */
2123 m_TrayNotify = CreateTrayNotifyWnd(this, HideClock, &m_TrayNotifyInstance);
2124
2125 UpdateFonts();
2126
2127 InitShellServices(&m_ShellServices);
2128
2129 if (AutoHide)
2130 {
2131 m_AutoHideState = AUTOHIDE_HIDING;
2132 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
2133 }
2134
2135 RegisterHotKey(m_hWnd, IDHK_RUN, MOD_WIN, 'R');
2136 RegisterHotKey(m_hWnd, IDHK_MINIMIZE_ALL, MOD_WIN, 'M');
2137 RegisterHotKey(m_hWnd, IDHK_RESTORE_ALL, MOD_WIN|MOD_SHIFT, 'M');
2138 RegisterHotKey(m_hWnd, IDHK_HELP, MOD_WIN, VK_F1);
2139 RegisterHotKey(m_hWnd, IDHK_EXPLORE, MOD_WIN, 'E');
2140 RegisterHotKey(m_hWnd, IDHK_FIND, MOD_WIN, 'F');
2141 RegisterHotKey(m_hWnd, IDHK_FIND_COMPUTER, MOD_WIN|MOD_CONTROL, 'F');
2142 RegisterHotKey(m_hWnd, IDHK_NEXT_TASK, MOD_WIN, VK_TAB);
2143 RegisterHotKey(m_hWnd, IDHK_PREV_TASK, MOD_WIN|MOD_SHIFT, VK_TAB);
2144 RegisterHotKey(m_hWnd, IDHK_SYS_PROPERTIES, MOD_WIN, VK_PAUSE);
2145 RegisterHotKey(m_hWnd, IDHK_DESKTOP, MOD_WIN, 'D');
2146 RegisterHotKey(m_hWnd, IDHK_PAGER, MOD_WIN, 'B');
2147
2148 return TRUE;
2149 }
2150
2151 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2152 {
2153 if (m_Theme)
2154 CloseThemeData(m_Theme);
2155
2156 m_Theme = OpenThemeData(m_hWnd, L"TaskBar");
2157
2158 if (m_Theme)
2159 {
2160 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, 0);
2161 }
2162 else
2163 {
2164 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, WS_THICKFRAME | WS_BORDER);
2165 }
2166 SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
2167
2168 return TRUE;
2169 }
2170
2171 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2172 {
2173 if (wParam == SPI_SETNONCLIENTMETRICS)
2174 {
2175 SendMessage(m_TaskSwitch, uMsg, wParam, lParam);
2176 UpdateFonts();
2177 AlignControls(NULL);
2178 CheckTrayWndPosition();
2179 }
2180
2181 return 0;
2182 }
2183
2184 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2185 {
2186 HDC hdc = (HDC) wParam;
2187
2188 if (!m_Theme)
2189 {
2190 bHandled = FALSE;
2191 return 0;
2192 }
2193
2194 return EraseBackgroundWithTheme(hdc);
2195 }
2196
2197 LRESULT OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2198 {
2199 /* Move the tray window to the right position and resize it if necessary */
2200 CheckTrayWndPosition();
2201
2202 return TRUE;
2203 }
2204
2205 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2206 {
2207 if (m_TrayNotify)
2208 {
2209 TRACE("WM_COPYDATA notify message received. Handling...\n");
2210 return TrayNotify_NotifyIconCmd(m_TrayNotifyInstance, wParam, lParam);
2211 }
2212 return TRUE;
2213 }
2214
2215 LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2216 {
2217 if (!m_Theme)
2218 {
2219 bHandled = FALSE;
2220 return 0;
2221 }
2222
2223 return DrawSizerWithTheme((HRGN) wParam);
2224 }
2225
2226 LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2227 {
2228 SetBkMode((HDC) wParam, TRANSPARENT);
2229 return (LRESULT) GetStockObject(HOLLOW_BRUSH);
2230 }
2231
2232 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2233 {
2234 RECT rcClient;
2235 POINT pt;
2236
2237 if (Locked)
2238 {
2239 /* The user may not be able to resize the tray window.
2240 Pretend like the window is not sizeable when the user
2241 clicks on the border. */
2242 return HTBORDER;
2243 }
2244
2245 SetLastError(ERROR_SUCCESS);
2246 if (GetClientRect(&rcClient) &&
2247 (MapWindowPoints(NULL, (LPPOINT) &rcClient, 2) != 0 || GetLastError() == ERROR_SUCCESS))
2248 {
2249 pt.x = (SHORT) LOWORD(lParam);
2250 pt.y = (SHORT) HIWORD(lParam);
2251
2252 if (PtInRect(&rcClient,
2253 pt))
2254 {
2255 /* The user is trying to drag the tray window */
2256 return HTCAPTION;
2257 }
2258
2259 /* Depending on the position of the tray window, allow only
2260 changing the border next to the monitor working area */
2261 switch (m_Position)
2262 {
2263 case ABE_TOP:
2264 if (pt.y > rcClient.bottom)
2265 return HTBOTTOM;
2266 break;
2267 case ABE_LEFT:
2268 if (pt.x > rcClient.right)
2269 return HTRIGHT;
2270 break;
2271 case ABE_RIGHT:
2272 if (pt.x < rcClient.left)
2273 return HTLEFT;
2274 break;
2275 case ABE_BOTTOM:
2276 default:
2277 if (pt.y < rcClient.top)
2278 return HTTOP;
2279 break;
2280 }
2281 }
2282 return HTBORDER;
2283 return TRUE;
2284 }
2285
2286 LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2287 {
2288 POINT ptCursor;
2289 PRECT pRect = (PRECT) lParam;
2290
2291 /* We need to ensure that an application can not accidently
2292 move the tray window (using SetWindowPos). However, we still
2293 need to be able to move the window in case the user wants to
2294 drag the tray window to another position or in case the user
2295 wants to resize the tray window. */
2296 if (!Locked && GetCursorPos(&ptCursor))
2297 {
2298 IsDragging = TRUE;
2299 m_DraggingPosition = GetDraggingRectFromPt(ptCursor, pRect, &m_DraggingMonitor);
2300 }
2301 else
2302 {
2303 *pRect = m_TrayRects[m_Position];
2304
2305 if (AutoHide)
2306 {
2307 pRect->left += m_AutoHideOffset.cx;
2308 pRect->right += m_AutoHideOffset.cx;
2309 pRect->top += m_AutoHideOffset.cy;
2310 pRect->bottom += m_AutoHideOffset.cy;
2311 }
2312 }
2313 return TRUE;
2314 }
2315
2316 LRESULT OnSizing(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2317 {
2318 PRECT pRect = (PRECT) lParam;
2319
2320 if (!Locked)
2321 {
2322 FitToRebar(pRect);
2323 }
2324 else
2325 {
2326 *pRect = m_TrayRects[m_Position];
2327
2328 if (AutoHide)
2329 {
2330 pRect->left += m_AutoHideOffset.cx;
2331 pRect->right += m_AutoHideOffset.cx;
2332 pRect->top += m_AutoHideOffset.cy;
2333 pRect->bottom += m_AutoHideOffset.cy;
2334 }
2335 }
2336 return TRUE;
2337 }
2338
2339 LRESULT OnWindowPosChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2340 {
2341 ChangingWinPos((LPWINDOWPOS) lParam);
2342 return TRUE;
2343 }
2344
2345 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2346 {
2347 RECT rcClient;
2348 if (wParam == SIZE_RESTORED && lParam == 0)
2349 {
2350 ResizeWorkArea();
2351 /* Clip the tray window on multi monitor systems so the edges can't
2352 overlap into another monitor */
2353 ApplyClipping(TRUE);
2354
2355 if (!GetClientRect(&rcClient))
2356 {
2357 return FALSE;
2358 }
2359 }
2360 else
2361 {
2362 rcClient.left = rcClient.top = 0;
2363 rcClient.right = LOWORD(lParam);
2364 rcClient.bottom = HIWORD(lParam);
2365 }
2366
2367 AlignControls(&rcClient);
2368 return TRUE;
2369 }
2370
2371 LRESULT OnEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2372 {
2373 InSizeMove = TRUE;
2374 IsDragging = FALSE;
2375 if (!Locked)
2376 {
2377 /* Remove the clipping on multi monitor systems while dragging around */
2378 ApplyClipping(FALSE);
2379 }
2380 return TRUE;
2381 }
2382
2383 LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2384 {
2385 InSizeMove = FALSE;
2386 if (!Locked)
2387 {
2388 /* Apply clipping */
2389 PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2390 }
2391 return TRUE;
2392 }
2393
2394 LRESULT OnSysChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2395 {
2396 switch (wParam)
2397 {
2398 case TEXT(' '):
2399 {
2400 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2401 The tray window needs to handle this specially, since it normally doesn't have
2402 a system menu. */
2403
2404 static const UINT uidDisableItem [] = {
2405 SC_RESTORE,
2406 SC_MOVE,
2407 SC_SIZE,
2408 SC_MAXIMIZE,
2409 SC_MINIMIZE,
2410 };
2411 HMENU hSysMenu;
2412 UINT i, uId;
2413
2414 /* temporarily enable the system menu */
2415 SetWindowStyle(m_hWnd, WS_SYSMENU, WS_SYSMENU);
2416
2417 hSysMenu = GetSystemMenu(FALSE);
2418 if (hSysMenu != NULL)
2419 {
2420 /* Disable all items that are not relevant */
2421 for (i = 0; i < _countof(uidDisableItem); i++)
2422 {
2423 EnableMenuItem(hSysMenu,
2424 uidDisableItem[i],
2425 MF_BYCOMMAND | MF_GRAYED);
2426 }
2427
2428 EnableMenuItem(hSysMenu,
2429 SC_CLOSE,
2430 MF_BYCOMMAND |
2431 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2432
2433 /* Display the system menu */
2434 uId = TrackMenu(
2435 hSysMenu,
2436 NULL,
2437 m_StartButton.m_hWnd,
2438 m_Position != ABE_TOP,
2439 FALSE);
2440 if (uId != 0)
2441 {
2442 SendMessage(m_hWnd, WM_SYSCOMMAND, (WPARAM) uId, 0);
2443 }
2444 }
2445
2446 /* revert the system menu window style */
2447 SetWindowStyle(m_hWnd, WS_SYSMENU, 0);
2448 break;
2449 }
2450
2451 default:
2452 bHandled = FALSE;
2453 }
2454 return TRUE;
2455 }
2456
2457 LRESULT OnNcRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2458 {
2459 /* We want the user to be able to get a context menu even on the nonclient
2460 area (including the sizing border)! */
2461 uMsg = WM_CONTEXTMENU;
2462 wParam = (WPARAM) m_hWnd;
2463
2464 return OnContextMenu(uMsg, wParam, lParam, bHandled);
2465 }
2466
2467 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2468 {
2469 LRESULT Ret = FALSE;
2470 POINT pt, *ppt = NULL;
2471 HWND hWndExclude = NULL;
2472
2473 /* Check if the administrator has forbidden access to context menus */
2474 if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2475 return FALSE;
2476
2477 pt.x = (SHORT) LOWORD(lParam);
2478 pt.y = (SHORT) HIWORD(lParam);
2479
2480 if (pt.x != -1 || pt.y != -1)
2481 ppt = &pt;
2482 else
2483 hWndExclude = m_StartButton.m_hWnd;
2484
2485 if ((HWND) wParam == m_StartButton.m_hWnd)
2486 {
2487 /* Make sure we can't track the context menu if the start
2488 menu is currently being shown */
2489 if (!(m_StartButton.SendMessage(BM_GETSTATE, 0, 0) & BST_PUSHED))
2490 {
2491 CComPtr<IContextMenu> ctxMenu;
2492 StartMenuBtnCtxMenuCreator(this, m_hWnd, &ctxMenu);
2493 TrackCtxMenu(ctxMenu, ppt, hWndExclude, m_Position == ABE_BOTTOM, this);
2494 }
2495 }
2496 else
2497 {
2498 /* See if the context menu should be handled by the task band site */
2499 if (ppt != NULL && m_TrayBandSite != NULL)
2500 {
2501 HWND hWndAtPt;
2502 POINT ptClient = *ppt;
2503
2504 /* Convert the coordinates to client-coordinates */
2505 ::MapWindowPoints(NULL, m_hWnd, &ptClient, 1);
2506
2507 hWndAtPt = ChildWindowFromPoint(ptClient);
2508 if (hWndAtPt != NULL &&
2509 (hWndAtPt == m_Rebar || ::IsChild(m_Rebar, hWndAtPt)))
2510 {
2511 /* Check if the user clicked on the task switch window */
2512 ptClient = *ppt;
2513 ::MapWindowPoints(NULL, m_Rebar, &ptClient, 1);
2514
2515 hWndAtPt = ::ChildWindowFromPointEx(m_Rebar, ptClient, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2516 if (hWndAtPt == m_TaskSwitch)
2517 goto HandleTrayContextMenu;
2518
2519 /* Forward the message to the task band site */
2520 m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2521 }
2522 else
2523 goto HandleTrayContextMenu;
2524 }
2525 else
2526 {
2527 HandleTrayContextMenu:
2528 /* Tray the default tray window context menu */
2529 CComPtr<IContextMenu> ctxMenu;
2530 TrayWindowCtxMenuCreator(this, m_hWnd, &ctxMenu);
2531 TrackCtxMenu(ctxMenu, ppt, NULL, FALSE, this);
2532 }
2533 }
2534 return Ret;
2535 }
2536
2537 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2538 {
2539 LRESULT Ret = FALSE;
2540 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2541 the rebar control! But we shouldn't forward messages that the band
2542 site doesn't handle, such as other controls (start button, tray window */
2543
2544 HRESULT hr = E_FAIL;
2545
2546 if (m_TrayBandSite)
2547 {
2548 hr = m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2549 if (SUCCEEDED(hr))
2550 return Ret;
2551 }
2552
2553 if (m_TrayBandSite == NULL || FAILED(hr))
2554 {
2555 const NMHDR *nmh = (const NMHDR *) lParam;
2556
2557 if (nmh->hwndFrom == m_TrayNotify)
2558 {
2559 switch (nmh->code)
2560 {
2561 case NTNWM_REALIGN:
2562 /* Cause all controls to be aligned */
2563 PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2564 break;
2565 }
2566 }
2567 }
2568 return Ret;
2569 }
2570
2571 LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2572 {
2573 /* We "handle" this message so users can't cause a weird maximize/restore
2574 window animation when double-clicking the tray window! */
2575
2576 /* We should forward mouse messages to child windows here.
2577 Right now, this is only clock double-click */
2578 RECT rcClock;
2579 if (TrayNotify_GetClockRect(m_TrayNotifyInstance, &rcClock))
2580 {
2581 POINT ptClick;
2582 ptClick.x = MAKEPOINTS(lParam).x;
2583 ptClick.y = MAKEPOINTS(lParam).y;
2584 if (PtInRect(&rcClock, ptClick))
2585 {
2586 //FIXME: use SHRunControlPanel
2587 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
2588 }
2589 }
2590 return TRUE;
2591 }
2592
2593 LRESULT OnAppTrayDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2594 {
2595 DestroyWindow();
2596 return TRUE;
2597 }
2598
2599 LRESULT OnOpenStartMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2600 {
2601 HWND hwndStartMenu;
2602 HRESULT hr = IUnknown_GetWindow(m_StartMenuPopup, &hwndStartMenu);
2603 if (FAILED_UNEXPECTEDLY(hr))
2604 return FALSE;
2605
2606 if (::IsWindowVisible(hwndStartMenu))
2607 {
2608 m_StartMenuPopup->OnSelect(MPOS_CANCELLEVEL);
2609 }
2610 else
2611 {
2612 PopupStartMenu();
2613 }
2614
2615 return TRUE;
2616 }
2617
2618 LRESULT OnDoExitWindows(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2619 {
2620 /*
2621 * TWM_DOEXITWINDOWS is send by the CDesktopBrowser to us
2622 * to show the shutdown dialog. Also a WM_CLOSE message sent
2623 * by apps should show the dialog.
2624 */
2625 return DoExitWindows();
2626 }
2627
2628 LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2629 {
2630 if (wParam == SC_CLOSE)
2631 {
2632 return DoExitWindows();
2633 }
2634
2635 bHandled = FALSE;
2636 return TRUE;
2637 }
2638
2639 LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2640 {
2641 return HandleHotKey(wParam);
2642 }
2643
2644 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2645 {
2646 LRESULT Ret = FALSE;
2647
2648 if ((HWND) lParam == m_StartButton.m_hWnd)
2649 {
2650 PopupStartMenu();
2651 return FALSE;
2652 }
2653
2654 if (m_TrayBandSite == NULL || FAILED_UNEXPECTEDLY(m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret)))
2655 {
2656 return HandleCommand(LOWORD(wParam));
2657 }
2658 return Ret;
2659 }
2660
2661 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2662 {
2663 if (AutoHide)
2664 {
2665 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2666 }
2667
2668 return TRUE;
2669 }
2670
2671 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2672 {
2673 if (wParam == TIMER_ID_MOUSETRACK)
2674 {
2675 ProcessMouseTracking();
2676 }
2677 else if (wParam == TIMER_ID_AUTOHIDE)
2678 {
2679 ProcessAutoHide();
2680 }
2681
2682 bHandled = FALSE;
2683 return TRUE;
2684 }
2685
2686 LRESULT OnNcCalcSize(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2687 {
2688 RECT *rc = NULL;
2689 /* Ignore WM_NCCALCSIZE if we are not themed or locked */
2690 if(!m_Theme || Locked)
2691 {
2692 bHandled = FALSE;
2693 return 0;
2694 }
2695 if(!wParam)
2696 {
2697 rc = (RECT*)wParam;
2698 }
2699 else
2700 {
2701 NCCALCSIZE_PARAMS *prms = (NCCALCSIZE_PARAMS*)lParam;
2702 if(prms->lppos->flags & SWP_NOSENDCHANGING)
2703 {
2704 bHandled = FALSE;
2705 return 0;
2706 }
2707 rc = &prms->rgrc[0];
2708 }
2709
2710 AdjustSizerRect(rc, m_Position);
2711
2712 return 0;
2713 }
2714
2715 LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
2716 {
2717 #if 0
2718 LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr;
2719
2720 if (!as->fChanged)
2721 return 0;
2722
2723 RECT rc;
2724 ::GetWindowRect(m_hWnd, &rc);
2725
2726 SIZE szWindow = {
2727 rc.right - rc.left,
2728 rc.bottom - rc.top };
2729 SIZE szTarget = {
2730 as->rcTarget.right - as->rcTarget.left,
2731 as->rcTarget.bottom - as->rcTarget.top };
2732 SIZE szActual = {
2733 as->rcActual.right - as->rcActual.left,
2734 as->rcActual.bottom - as->rcActual.top };
2735
2736 SIZE borders = {
2737 szWindow.cx - szTarget.cx,
2738 szWindow.cy - szTarget.cx,
2739 };
2740
2741 switch (m_Position)
2742 {
2743 case ABE_LEFT:
2744 szWindow.cx = szActual.cx + borders.cx;
2745 break;
2746 case ABE_TOP:
2747 szWindow.cy = szActual.cy + borders.cy;
2748 break;
2749 case ABE_RIGHT:
2750 szWindow.cx = szActual.cx + borders.cx;
2751 rc.left = rc.right - szWindow.cy;
2752 break;
2753 case ABE_BOTTOM:
2754 szWindow.cy = szActual.cy + borders.cy;
2755 rc.top = rc.bottom - szWindow.cy;
2756 break;
2757 }
2758
2759 SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER);
2760 #else
2761 bHandled = FALSE;
2762 #endif
2763 return 0;
2764 }
2765
2766 DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE)
2767
2768 BEGIN_MSG_MAP(CTrayWindow)
2769 if (m_StartMenuBand != NULL)
2770 {
2771 MSG Msg;
2772 LRESULT lRet;
2773
2774 Msg.hwnd = m_hWnd;
2775 Msg.message = uMsg;
2776 Msg.wParam = wParam;
2777 Msg.lParam = lParam;
2778
2779 if (m_StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK)
2780 {
2781 return lRet;
2782 }
2783
2784 wParam = Msg.wParam;
2785 lParam = Msg.lParam;
2786 }
2787 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
2788 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
2789 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P
2790 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2791 MESSAGE_HANDLER(WM_SIZE, OnSize)
2792 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2793 /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/
2794 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
2795 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2796 MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
2797 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2798 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
2799 MESSAGE_HANDLER(WM_TIMER, OnTimer)
2800 MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange)
2801 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2802 MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
2803 MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
2804 MESSAGE_HANDLER(WM_MOVING, OnMoving)
2805 MESSAGE_HANDLER(WM_SIZING, OnSizing)
2806 MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChange)
2807 MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove)
2808 MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
2809 MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar)
2810 MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp)
2811 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
2812 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
2813 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
2814 MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
2815 MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
2816 MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
2817 MESSAGE_HANDLER(WM_CLOSE, OnDoExitWindows)
2818 MESSAGE_HANDLER(WM_HOTKEY, OnHotkey)
2819 MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize)
2820 ALT_MSG_MAP(1)
2821 END_MSG_MAP()
2822
2823 /*****************************************************************************/
2824
2825 VOID TrayProcessMessages()
2826 {
2827 MSG Msg;
2828
2829 /* FIXME: We should keep a reference here... */
2830
2831 while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
2832 {
2833 if (Msg.message == WM_QUIT)
2834 break;
2835
2836 if (m_StartMenuBand == NULL ||
2837 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2838 {
2839 TranslateMessage(&Msg);
2840 DispatchMessage(&Msg);
2841 }
2842 }
2843 }
2844
2845 VOID TrayMessageLoop()
2846 {
2847 MSG Msg;
2848 BOOL Ret;
2849
2850 /* FIXME: We should keep a reference here... */
2851
2852 while (true)
2853 {
2854 Ret = GetMessage(&Msg, NULL, 0, 0);
2855
2856 if (!Ret || Ret == -1)
2857 break;
2858
2859 if (m_StartMenuBand == NULL ||
2860 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2861 {
2862 TranslateMessage(&Msg);
2863 DispatchMessage(&Msg);
2864 }
2865 }
2866 }
2867
2868 /*
2869 * IShellDesktopTray
2870 *
2871 * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()!
2872 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2873 * The reason we implement it is because we have to use SHCreateDesktop() so
2874 * that the shell provides the desktop window and all the features that come
2875 * with it (especially positioning of desktop icons)
2876 */
2877
2878 virtual ULONG STDMETHODCALLTYPE GetState()
2879 {
2880 /* FIXME: Return ABS_ flags? */
2881 TRACE("IShellDesktopTray::GetState() unimplemented!\n");
2882 return 0;
2883 }
2884
2885 virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray)
2886 {
2887 TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
2888 *phWndTray = m_hWnd;
2889 return S_OK;
2890 }
2891
2892 virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop)
2893 {
2894 TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
2895
2896 m_DesktopWnd = hWndDesktop;
2897 return S_OK;
2898 }
2899
2900 virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2)
2901 {
2902 TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
2903 return S_OK;
2904 }
2905
2906 virtual HRESULT RaiseStartButton()
2907 {
2908 m_StartButton.SendMessageW(BM_SETSTATE, FALSE, 0);
2909 return S_OK;
2910 }
2911
2912 HRESULT WINAPI GetWindow(HWND* phwnd)
2913 {
2914 if (!phwnd)
2915 return E_INVALIDARG;
2916 *phwnd = m_hWnd;
2917 return S_OK;
2918 }
2919
2920 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
2921 {
2922 return E_NOTIMPL;
2923 }
2924
2925 void _Init()
2926 {
2927 m_Position = (DWORD) -1;
2928 }
2929
2930 DECLARE_NOT_AGGREGATABLE(CTrayWindow)
2931
2932 DECLARE_PROTECT_FINAL_CONSTRUCT()
2933 BEGIN_COM_MAP(CTrayWindow)
2934 /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/
2935 COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray)
2936 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
2937 END_COM_MAP()
2938 };
2939
2940 class CTrayWindowCtxMenu :
2941 public CComCoClass<CTrayWindowCtxMenu>,
2942 public CComObjectRootEx<CComMultiThreadModelNoCS>,
2943 public IContextMenu
2944 {
2945 HWND hWndOwner;
2946 CComPtr<CTrayWindow> TrayWnd;
2947 CComPtr<IContextMenu> pcm;
2948
2949 public:
2950 HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
2951 {
2952 this->TrayWnd = (CTrayWindow *) pTrayWnd;
2953 this->hWndOwner = hWndOwner;
2954 return S_OK;
2955 }
2956
2957 virtual HRESULT STDMETHODCALLTYPE
2958 QueryContextMenu(HMENU hPopup,
2959 UINT indexMenu,
2960 UINT idCmdFirst,
2961 UINT idCmdLast,
2962 UINT uFlags)
2963 {
2964 HMENU menubase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCEW(IDM_TRAYWND));
2965
2966 if (!menubase)
2967 return HRESULT_FROM_WIN32(GetLastError());
2968
2969 int count = ::GetMenuItemCount(menubase);
2970
2971 for (int i = 0; i < count; i++)
2972 {
2973 WCHAR label[128];
2974
2975 MENUITEMINFOW mii = { 0 };
2976 mii.cbSize = sizeof(mii);
2977 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS
2978 | MIIM_DATA | MIIM_STRING | MIIM_BITMAP | MIIM_FTYPE;
2979 mii.dwTypeData = label;
2980 mii.cch = _countof(label);
2981 ::GetMenuItemInfoW(menubase, i, TRUE, &mii);
2982
2983 TRACE("Adding item %d label %S type %d\n", mii.wID, mii.dwTypeData, mii.fType);
2984
2985 ::InsertMenuItemW(hPopup, i + 1, TRUE, &mii);
2986 }
2987
2988 ::DestroyMenu(menubase);
2989
2990 if (SHRestricted(REST_CLASSICSHELL) != 0)
2991 {
2992 DeleteMenu(hPopup,
2993 ID_LOCKTASKBAR,
2994 MF_BYCOMMAND);
2995 }
2996
2997 CheckMenuItem(hPopup,
2998 ID_LOCKTASKBAR,
2999 MF_BYCOMMAND | (TrayWnd->Locked ? MF_CHECKED : MF_UNCHECKED));
3000
3001 if (TrayWnd->m_TrayBandSite != NULL)
3002 {
3003 if (FAILED(TrayWnd->m_TrayBandSite->AddContextMenus(
3004 hPopup,
3005 0,
3006 ID_SHELL_CMD_FIRST,
3007 ID_SHELL_CMD_LAST,
3008 CMF_NORMAL,
3009 &pcm)))
3010 {
3011 WARN("AddContextMenus failed.\n");
3012 pcm = NULL;
3013 }
3014 }
3015
3016 return S_OK;
3017 }
3018
3019 virtual HRESULT STDMETHODCALLTYPE
3020 InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
3021 {
3022 UINT uiCmdId = (UINT) lpici->lpVerb;
3023 if (uiCmdId != 0)
3024 {
3025 if (uiCmdId >= ID_SHELL_CMD_FIRST && uiCmdId <= ID_SHELL_CMD_LAST)
3026 {
3027 CMINVOKECOMMANDINFO cmici = { 0 };
3028
3029 if (pcm != NULL)
3030 {
3031 /* Setup and invoke the shell command */
3032 cmici.cbSize = sizeof(cmici);
3033 cmici.hwnd = hWndOwner;
3034 cmici.lpVerb = (LPCSTR) MAKEINTRESOURCEW(uiCmdId - ID_SHELL_CMD_FIRST);
3035 cmici.nShow = SW_NORMAL;
3036
3037 pcm->InvokeCommand(&cmici);
3038 }
3039 }
3040 else
3041 {
3042 TrayWnd->ExecContextMenuCmd(uiCmdId);
3043 }
3044 }
3045
3046 return S_OK;
3047 }
3048
3049 virtual HRESULT STDMETHODCALLTYPE
3050 GetCommandString(UINT_PTR idCmd,
3051 UINT uType,
3052 UINT *pwReserved,
3053 LPSTR pszName,
3054 UINT cchMax)
3055 {
3056 return E_NOTIMPL;
3057 }
3058
3059 CTrayWindowCtxMenu()
3060 {
3061 }
3062
3063 virtual ~CTrayWindowCtxMenu()
3064 {
3065 }
3066
3067 BEGIN_COM_MAP(CTrayWindowCtxMenu)
3068 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3069 END_COM_MAP()
3070 };
3071
3072 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu)
3073 {
3074 CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>();
3075 mnu->Initialize(TrayWnd, hWndOwner);
3076 *ppCtxMenu = mnu;
3077 return S_OK;
3078 }
3079
3080 HRESULT CreateTrayWindow(ITrayWindow ** ppTray)
3081 {
3082 CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>();
3083 if (Tray == NULL)
3084 return E_OUTOFMEMORY;
3085
3086 Tray->_Init();
3087 Tray->Open();
3088
3089 *ppTray = (ITrayWindow *) Tray;
3090
3091 return S_OK;
3092 }
3093
3094 HRESULT
3095 Tray_OnStartMenuDismissed(ITrayWindow* Tray)
3096 {
3097 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3098 return TrayWindow->RaiseStartButton();
3099 }
3100
3101 VOID TrayProcessMessages(ITrayWindow *Tray)
3102 {
3103 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3104 TrayWindow->TrayProcessMessages();
3105 }
3106
3107 VOID TrayMessageLoop(ITrayWindow *Tray)
3108 {
3109 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3110 TrayWindow->TrayMessageLoop();
3111 }