d49205529870718dcf6d08d0a2e1618375347bbb
[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 int iSizerPart[4] = {TBP_SIZINGBARLEFT, TBP_SIZINGBARTOP, TBP_SIZINGBARRIGHT, TBP_SIZINGBARBOTTOM};
854 SIZE size;
855
856 if (pos > ABE_BOTTOM)
857 pos = ABE_BOTTOM;
858
859 HRESULT hr = GetThemePartSize(m_Theme, NULL, iSizerPart[pos], 0, NULL, TS_TRUE, &size);
860 if (FAILED_UNEXPECTEDLY(hr))
861 return;
862
863 switch (pos)
864 {
865 case ABE_TOP:
866 rc->bottom -= size.cy;
867 break;
868 case ABE_BOTTOM:
869 rc->top += size.cy;
870 break;
871 case ABE_LEFT:
872 rc->right -= size.cx;
873 break;
874 case ABE_RIGHT:
875 rc->left += size.cx;
876 break;
877 }
878 }
879
880 VOID MakeTrayRectWithSize(IN DWORD Position,
881 IN const SIZE *pTraySize,
882 IN OUT RECT *pRect)
883 {
884 switch (Position)
885 {
886 case ABE_LEFT:
887 pRect->right = pRect->left + pTraySize->cx;
888 break;
889
890 case ABE_TOP:
891 pRect->bottom = pRect->top + pTraySize->cy;
892 break;
893
894 case ABE_RIGHT:
895 pRect->left = pRect->right - pTraySize->cx;
896 break;
897
898 case ABE_BOTTOM:
899 default:
900 pRect->top = pRect->bottom - pTraySize->cy;
901 break;
902 }
903 }
904
905 VOID GetTrayRectFromScreenRect(IN DWORD Position,
906 IN const RECT *pScreen,
907 IN const SIZE *pTraySize OPTIONAL,
908 OUT RECT *pRect)
909 {
910 if (pTraySize == NULL)
911 pTraySize = &m_TraySize;
912
913 *pRect = *pScreen;
914
915 if(!m_Theme)
916 {
917 /* Move the border outside of the screen */
918 InflateRect(pRect,
919 GetSystemMetrics(SM_CXEDGE),
920 GetSystemMetrics(SM_CYEDGE));
921 }
922
923 MakeTrayRectWithSize(Position, pTraySize, pRect);
924 }
925
926 BOOL IsPosHorizontal()
927 {
928 return m_Position == ABE_TOP || m_Position == ABE_BOTTOM;
929 }
930
931 HMONITOR CalculateValidSize(
932 IN DWORD Position,
933 IN OUT RECT *pRect)
934 {
935 RECT rcScreen;
936 //BOOL Horizontal;
937 HMONITOR hMon;
938 SIZE szMax, szWnd;
939
940 //Horizontal = IsPosHorizontal();
941
942 szWnd.cx = pRect->right - pRect->left;
943 szWnd.cy = pRect->bottom - pRect->top;
944
945 rcScreen = *pRect;
946 hMon = GetScreenRectFromRect(
947 &rcScreen,
948 MONITOR_DEFAULTTONEAREST);
949
950 /* Calculate the maximum size of the tray window and limit the window
951 size to half of the screen's size. */
952 szMax.cx = (rcScreen.right - rcScreen.left) / 2;
953 szMax.cy = (rcScreen.bottom - rcScreen.top) / 2;
954 if (szWnd.cx > szMax.cx)
955 szWnd.cx = szMax.cx;
956 if (szWnd.cy > szMax.cy)
957 szWnd.cy = szMax.cy;
958
959 /* FIXME - calculate */
960
961 GetTrayRectFromScreenRect(Position,
962 &rcScreen,
963 &szWnd,
964 pRect);
965
966 return hMon;
967 }
968
969 #if 0
970 VOID
971 GetMinimumWindowSize(
972 OUT RECT *pRect)
973 {
974 RECT rcMin = {0};
975
976 AdjustWindowRectEx(&rcMin,
977 GetWindowLong(m_hWnd,
978 GWL_STYLE),
979 FALSE,
980 GetWindowLong(m_hWnd,
981 GWL_EXSTYLE));
982
983 *pRect = rcMin;
984 }
985 #endif
986
987
988 DWORD GetDraggingRectFromPt(
989 IN POINT pt,
990 OUT RECT *pRect,
991 OUT HMONITOR *phMonitor)
992 {
993 HMONITOR hMon, hMonNew;
994 DWORD PosH, PosV, Pos;
995 SIZE DeltaPt, ScreenOffset;
996 RECT rcScreen;
997
998 rcScreen.left = 0;
999 rcScreen.top = 0;
1000
1001 /* Determine the screen rectangle */
1002 hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
1003 if (hMon != NULL)
1004 {
1005 MONITORINFO mi;
1006
1007 mi.cbSize = sizeof(mi);
1008 if (!GetMonitorInfo(hMon, &mi))
1009 {
1010 hMon = NULL;
1011 goto GetPrimaryScreenRect;
1012 }
1013
1014 /* make left top corner of the screen zero based to
1015 make calculations easier */
1016 pt.x -= mi.rcMonitor.left;
1017 pt.y -= mi.rcMonitor.top;
1018
1019 ScreenOffset.cx = mi.rcMonitor.left;
1020 ScreenOffset.cy = mi.rcMonitor.top;
1021 rcScreen.right = mi.rcMonitor.right - mi.rcMonitor.left;
1022 rcScreen.bottom = mi.rcMonitor.bottom - mi.rcMonitor.top;
1023 }
1024 else
1025 {
1026 GetPrimaryScreenRect:
1027 ScreenOffset.cx = 0;
1028 ScreenOffset.cy = 0;
1029 rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
1030 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
1031 }
1032
1033 /* Calculate the nearest screen border */
1034 if (pt.x < rcScreen.right / 2)
1035 {
1036 DeltaPt.cx = pt.x;
1037 PosH = ABE_LEFT;
1038 }
1039 else
1040 {
1041 DeltaPt.cx = rcScreen.right - pt.x;
1042 PosH = ABE_RIGHT;
1043 }
1044
1045 if (pt.y < rcScreen.bottom / 2)
1046 {
1047 DeltaPt.cy = pt.y;
1048 PosV = ABE_TOP;
1049 }
1050 else
1051 {
1052 DeltaPt.cy = rcScreen.bottom - pt.y;
1053 PosV = ABE_BOTTOM;
1054 }
1055
1056 Pos = (DeltaPt.cx * rcScreen.bottom < DeltaPt.cy * rcScreen.right) ? PosH : PosV;
1057
1058 /* Fix the screen origin to be relative to the primary monitor again */
1059 OffsetRect(&rcScreen,
1060 ScreenOffset.cx,
1061 ScreenOffset.cy);
1062
1063 RECT rcPos = m_TrayRects[Pos];
1064
1065 hMonNew = GetMonitorFromRect(&rcPos);
1066 if (hMon != hMonNew)
1067 {
1068 SIZE szTray;
1069
1070 /* Recalculate the rectangle, we're dragging to another monitor.
1071 We don't need to recalculate the rect on single monitor systems. */
1072 szTray.cx = rcPos.right - rcPos.left;
1073 szTray.cy = rcPos.bottom - rcPos.top;
1074
1075 GetTrayRectFromScreenRect(Pos, &rcScreen, &szTray, pRect);
1076 if (AutoHide)
1077 {
1078 pRect->left += m_AutoHideOffset.cx;
1079 pRect->right += m_AutoHideOffset.cx;
1080 pRect->top += m_AutoHideOffset.cy;
1081 pRect->bottom += m_AutoHideOffset.cy;
1082 }
1083 hMon = hMonNew;
1084 }
1085 else
1086 {
1087 /* The user is dragging the tray window on the same monitor. We don't need
1088 to recalculate the rectangle */
1089 *pRect = rcPos;
1090 if (AutoHide)
1091 {
1092 pRect->left += m_AutoHideOffset.cx;
1093 pRect->right += m_AutoHideOffset.cx;
1094 pRect->top += m_AutoHideOffset.cy;
1095 pRect->bottom += m_AutoHideOffset.cy;
1096 }
1097 }
1098
1099 *phMonitor = hMon;
1100
1101 return Pos;
1102 }
1103
1104 DWORD GetDraggingRectFromRect(
1105 IN OUT RECT *pRect,
1106 OUT HMONITOR *phMonitor)
1107 {
1108 POINT pt;
1109
1110 /* Calculate the center of the rectangle. We call
1111 GetDraggingRectFromPt to calculate a valid
1112 dragging rectangle */
1113 pt.x = pRect->left + ((pRect->right - pRect->left) / 2);
1114 pt.y = pRect->top + ((pRect->bottom - pRect->top) / 2);
1115
1116 return GetDraggingRectFromPt(
1117 pt,
1118 pRect,
1119 phMonitor);
1120 }
1121
1122 VOID ChangingWinPos(IN OUT LPWINDOWPOS pwp)
1123 {
1124 RECT rcTray;
1125
1126 if (IsDragging)
1127 {
1128 rcTray.left = pwp->x;
1129 rcTray.top = pwp->y;
1130 rcTray.right = rcTray.left + pwp->cx;
1131 rcTray.bottom = rcTray.top + pwp->cy;
1132 if (AutoHide)
1133 {
1134 rcTray.left -= m_AutoHideOffset.cx;
1135 rcTray.right -= m_AutoHideOffset.cx;
1136 rcTray.top -= m_AutoHideOffset.cy;
1137 rcTray.bottom -= m_AutoHideOffset.cy;
1138 }
1139
1140 if (!EqualRect(&rcTray,
1141 &m_TrayRects[m_DraggingPosition]))
1142 {
1143 /* Recalculate the rectangle, the user dragged the tray
1144 window to another monitor or the window was somehow else
1145 moved or resized */
1146 m_DraggingPosition = GetDraggingRectFromRect(
1147 &rcTray,
1148 &m_DraggingMonitor);
1149 //m_TrayRects[DraggingPosition] = rcTray;
1150 }
1151
1152 //Monitor = CalculateValidSize(DraggingPosition,
1153 // &rcTray);
1154
1155 m_Monitor = m_DraggingMonitor;
1156 m_Position = m_DraggingPosition;
1157 IsDragging = FALSE;
1158
1159 m_TrayRects[m_Position] = rcTray;
1160 goto ChangePos;
1161 }
1162 else if (GetWindowRect(&rcTray))
1163 {
1164 if (InSizeMove)
1165 {
1166 if (!(pwp->flags & SWP_NOMOVE))
1167 {
1168 rcTray.left = pwp->x;
1169 rcTray.top = pwp->y;
1170 }
1171
1172 if (!(pwp->flags & SWP_NOSIZE))
1173 {
1174 rcTray.right = rcTray.left + pwp->cx;
1175 rcTray.bottom = rcTray.top + pwp->cy;
1176 }
1177
1178 m_Position = GetDraggingRectFromRect(&rcTray, &m_Monitor);
1179
1180 if (!(pwp->flags & (SWP_NOMOVE | SWP_NOSIZE)))
1181 {
1182 SIZE szWnd;
1183
1184 szWnd.cx = pwp->cx;
1185 szWnd.cy = pwp->cy;
1186
1187 MakeTrayRectWithSize(m_Position, &szWnd, &rcTray);
1188 }
1189
1190 if (AutoHide)
1191 {
1192 rcTray.left -= m_AutoHideOffset.cx;
1193 rcTray.right -= m_AutoHideOffset.cx;
1194 rcTray.top -= m_AutoHideOffset.cy;
1195 rcTray.bottom -= m_AutoHideOffset.cy;
1196 }
1197 m_TrayRects[m_Position] = rcTray;
1198 }
1199 else
1200 {
1201 /* If the user isn't resizing the tray window we need to make sure the
1202 new size or position is valid. this is to prevent changes to the window
1203 without user interaction. */
1204 rcTray = m_TrayRects[m_Position];
1205 }
1206
1207 ChangePos:
1208 m_TraySize.cx = rcTray.right - rcTray.left;
1209 m_TraySize.cy = rcTray.bottom - rcTray.top;
1210
1211 if (AutoHide)
1212 {
1213 rcTray.left += m_AutoHideOffset.cx;
1214 rcTray.right += m_AutoHideOffset.cx;
1215 rcTray.top += m_AutoHideOffset.cy;
1216 rcTray.bottom += m_AutoHideOffset.cy;
1217 }
1218
1219 pwp->flags &= ~(SWP_NOMOVE | SWP_NOSIZE);
1220 pwp->x = rcTray.left;
1221 pwp->y = rcTray.top;
1222 pwp->cx = m_TraySize.cx;
1223 pwp->cy = m_TraySize.cy;
1224 }
1225 }
1226
1227 VOID ApplyClipping(IN BOOL Clip)
1228 {
1229 RECT rcClip, rcWindow;
1230 HRGN hClipRgn;
1231
1232 if (GetWindowRect(&rcWindow))
1233 {
1234 /* Disable clipping on systems with only one monitor */
1235 if (GetSystemMetrics(SM_CMONITORS) <= 1)
1236 Clip = FALSE;
1237
1238 if (Clip)
1239 {
1240 rcClip = rcWindow;
1241
1242 GetScreenRect(m_Monitor, &rcClip);
1243
1244 if (!IntersectRect(&rcClip, &rcClip, &rcWindow))
1245 {
1246 rcClip = rcWindow;
1247 }
1248
1249 OffsetRect(&rcClip,
1250 -rcWindow.left,
1251 -rcWindow.top);
1252
1253 hClipRgn = CreateRectRgnIndirect(&rcClip);
1254 }
1255 else
1256 hClipRgn = NULL;
1257
1258 /* Set the clipping region or make sure the window isn't clipped
1259 by disabling it explicitly. */
1260 SetWindowRgn(hClipRgn, TRUE);
1261 }
1262 }
1263
1264 VOID ResizeWorkArea()
1265 {
1266 #if !WIN7_DEBUG_MODE
1267 RECT rcTray, rcWorkArea;
1268
1269 /* If monitor has changed then fix the previous monitors work area */
1270 if (m_PreviousMonitor != m_Monitor)
1271 {
1272 GetScreenRect(m_PreviousMonitor, &rcWorkArea);
1273 SystemParametersInfoW(SPI_SETWORKAREA,
1274 1,
1275 &rcWorkArea,
1276 SPIF_SENDCHANGE);
1277 }
1278
1279 rcTray = m_TrayRects[m_Position];
1280
1281 GetScreenRect(m_Monitor, &rcWorkArea);
1282 m_PreviousMonitor = m_Monitor;
1283
1284 /* If AutoHide is false then change the workarea to exclude
1285 the area that the taskbar covers. */
1286 if (!AutoHide)
1287 {
1288 switch (m_Position)
1289 {
1290 case ABE_TOP:
1291 rcWorkArea.top = rcTray.bottom;
1292 break;
1293 case ABE_LEFT:
1294 rcWorkArea.left = rcTray.right;
1295 break;
1296 case ABE_RIGHT:
1297 rcWorkArea.right = rcTray.left;
1298 break;
1299 case ABE_BOTTOM:
1300 rcWorkArea.bottom = rcTray.top;
1301 break;
1302 }
1303 }
1304
1305 /*
1306 * Resize the current monitor work area. Win32k will also send
1307 * a WM_SIZE message to automatically resize the desktop.
1308 */
1309 SystemParametersInfoW(SPI_SETWORKAREA,
1310 1,
1311 &rcWorkArea,
1312 SPIF_SENDCHANGE);
1313 #endif
1314 }
1315
1316 VOID CheckTrayWndPosition()
1317 {
1318 RECT rcTray;
1319
1320 rcTray = m_TrayRects[m_Position];
1321
1322 if (AutoHide)
1323 {
1324 rcTray.left += m_AutoHideOffset.cx;
1325 rcTray.right += m_AutoHideOffset.cx;
1326 rcTray.top += m_AutoHideOffset.cy;
1327 rcTray.bottom += m_AutoHideOffset.cy;
1328 }
1329
1330 IUnknown_Exec(m_TrayBandSite,
1331 IID_IDeskBand,
1332 DBID_BANDINFOCHANGED,
1333 0,
1334 NULL,
1335 NULL);
1336
1337 FitToRebar(&rcTray);
1338 m_TrayRects[m_Position] = rcTray;
1339
1340 /* Move the tray window */
1341 SetWindowPos(NULL,
1342 rcTray.left,
1343 rcTray.top,
1344 rcTray.right - rcTray.left,
1345 rcTray.bottom - rcTray.top,
1346 SWP_NOZORDER | SWP_NOACTIVATE);
1347
1348 ResizeWorkArea();
1349
1350 ApplyClipping(TRUE);
1351 }
1352
1353 typedef struct _TW_STUCKRECTS2
1354 {
1355 DWORD cbSize;
1356 LONG Unknown;
1357 DWORD dwFlags;
1358 DWORD Position;
1359 SIZE Size;
1360 RECT Rect;
1361 } TW_STRUCKRECTS2, *PTW_STUCKRECTS2;
1362
1363 VOID RegLoadSettings()
1364 {
1365 DWORD Pos;
1366 TW_STRUCKRECTS2 sr;
1367 RECT rcScreen;
1368 SIZE WndSize, EdgeSize, DlgFrameSize;
1369 DWORD cbSize = sizeof(sr);
1370 SIZE StartBtnSize = m_StartButton.GetSize();
1371
1372 EdgeSize.cx = GetSystemMetrics(SM_CXEDGE);
1373 EdgeSize.cy = GetSystemMetrics(SM_CYEDGE);
1374 DlgFrameSize.cx = GetSystemMetrics(SM_CXDLGFRAME);
1375 DlgFrameSize.cy = GetSystemMetrics(SM_CYDLGFRAME);
1376
1377 if (SHGetValue(hkExplorer,
1378 TEXT("StuckRects2"),
1379 TEXT("Settings"),
1380 NULL,
1381 &sr,
1382 &cbSize) == ERROR_SUCCESS &&
1383 sr.cbSize == sizeof(sr))
1384 {
1385 AutoHide = (sr.dwFlags & ABS_AUTOHIDE) != 0;
1386 AlwaysOnTop = (sr.dwFlags & ABS_ALWAYSONTOP) != 0;
1387 SmSmallIcons = (sr.dwFlags & 0x4) != 0;
1388 HideClock = (sr.dwFlags & 0x8) != 0;
1389
1390 /* FIXME: Are there more flags? */
1391
1392 #if WIN7_DEBUG_MODE
1393 m_Position = ABE_LEFT;
1394 #else
1395 if (sr.Position > ABE_BOTTOM)
1396 m_Position = ABE_BOTTOM;
1397 else
1398 m_Position = sr.Position;
1399 #endif
1400
1401 /* Try to find out which monitor the tray window was located on last.
1402 Here we're only interested in the monitor screen that we think
1403 is the last one used. We're going to determine on which monitor
1404 we really are after calculating the docked position. */
1405 rcScreen = sr.Rect;
1406 GetScreenRectFromRect(
1407 &rcScreen,
1408 MONITOR_DEFAULTTONEAREST);
1409 }
1410 else
1411 {
1412 m_Position = ABE_BOTTOM;
1413 AlwaysOnTop = TRUE;
1414
1415 /* Use the minimum size of the taskbar, we'll use the start
1416 button as a minimum for now. Make sure we calculate the
1417 entire window size, not just the client size. However, we
1418 use a thinner border than a standard thick border, so that
1419 the start button and bands are not stuck to the screen border. */
1420 if(!m_Theme)
1421 {
1422 sr.Size.cx = StartBtnSize.cx + (2 * (EdgeSize.cx + DlgFrameSize.cx));
1423 sr.Size.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy));
1424 }
1425 else
1426 {
1427 sr.Size.cx = StartBtnSize.cx - EdgeSize.cx;
1428 sr.Size.cy = StartBtnSize.cy - EdgeSize.cy;
1429 if(!Locked)
1430 sr.Size.cy += GetSystemMetrics(SM_CYSIZEFRAME);
1431 }
1432
1433 /* Use the primary screen by default */
1434 rcScreen.left = 0;
1435 rcScreen.top = 0;
1436 rcScreen.right = GetSystemMetrics(SM_CXSCREEN);
1437 rcScreen.bottom = GetSystemMetrics(SM_CYSCREEN);
1438 GetScreenRectFromRect(
1439 &rcScreen,
1440 MONITOR_DEFAULTTOPRIMARY);
1441 }
1442
1443 if (m_hWnd != NULL)
1444 SetWindowPos(
1445 AlwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
1446 0,
1447 0,
1448 0,
1449 0,
1450 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1451
1452 /* Determine a minimum tray window rectangle. The "client" height is
1453 zero here since we cannot determine an optimal minimum width when
1454 loaded as a vertical tray window. We just need to make sure the values
1455 loaded from the registry are at least. The windows explorer behaves
1456 the same way, it allows the user to save a zero width vertical tray
1457 window, but not a zero height horizontal tray window. */
1458 if(!m_Theme)
1459 {
1460 WndSize.cx = 2 * (EdgeSize.cx + DlgFrameSize.cx);
1461 WndSize.cy = StartBtnSize.cy + (2 * (EdgeSize.cy + DlgFrameSize.cy));
1462 }
1463 else
1464 {
1465 WndSize.cx = StartBtnSize.cx;
1466 WndSize.cy = StartBtnSize.cy - EdgeSize.cx;
1467 }
1468
1469 if (WndSize.cx < sr.Size.cx)
1470 WndSize.cx = sr.Size.cx;
1471 if (WndSize.cy < sr.Size.cy)
1472 WndSize.cy = sr.Size.cy;
1473
1474 /* Save the calculated size */
1475 m_TraySize = WndSize;
1476
1477 /* Calculate all docking rectangles. We need to do this here so they're
1478 initialized and dragging the tray window to another position gives
1479 usable results */
1480 for (Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++)
1481 {
1482 GetTrayRectFromScreenRect(Pos,
1483 &rcScreen,
1484 &m_TraySize,
1485 &m_TrayRects[Pos]);
1486 // 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);
1487 }
1488
1489 /* Determine which monitor we are on. It shouldn't matter which docked
1490 position rectangle we use */
1491 m_Monitor = GetMonitorFromRect(&m_TrayRects[ABE_LEFT]);
1492 }
1493
1494 VOID AlignControls(IN PRECT prcClient OPTIONAL)
1495 {
1496 RECT rcClient;
1497 SIZE TraySize, StartSize;
1498 POINT ptTrayNotify = { 0, 0 };
1499 BOOL Horizontal;
1500 HDWP dwp;
1501
1502 m_StartButton.UpdateSize();
1503 if (prcClient != NULL)
1504 {
1505 rcClient = *prcClient;
1506 }
1507 else
1508 {
1509 if (!GetClientRect(&rcClient))
1510 {
1511 ERR("Could not get client rect lastErr=%d\n", GetLastError());
1512 return;
1513 }
1514 }
1515
1516 Horizontal = IsPosHorizontal();
1517
1518 /* We're about to resize/move the start button, the rebar control and
1519 the tray notification control */
1520 dwp = BeginDeferWindowPos(3);
1521 if (dwp == NULL)
1522 {
1523 ERR("BeginDeferWindowPos failed. lastErr=%d\n", GetLastError());
1524 return;
1525 }
1526
1527 /* Limit the Start button width to the client width, if necessary */
1528 StartSize = m_StartButton.GetSize();
1529 if (StartSize.cx > rcClient.right)
1530 StartSize.cx = rcClient.right;
1531
1532 if (m_StartButton.m_hWnd != NULL)
1533 {
1534 /* Resize and reposition the button */
1535 dwp = m_StartButton.DeferWindowPos(dwp,
1536 NULL,
1537 0,
1538 0,
1539 StartSize.cx,
1540 StartSize.cy,
1541 SWP_NOZORDER | SWP_NOACTIVATE);
1542 if (dwp == NULL)
1543 {
1544 ERR("DeferWindowPos for start button failed. lastErr=%d\n", GetLastError());
1545 return;
1546 }
1547 }
1548
1549 /* Determine the size that the tray notification window needs */
1550 if (Horizontal)
1551 {
1552 TraySize.cx = 0;
1553 TraySize.cy = rcClient.bottom;
1554 }
1555 else
1556 {
1557 TraySize.cx = rcClient.right;
1558 TraySize.cy = 0;
1559 }
1560
1561 if (m_TrayNotify != NULL &&
1562 SendMessage(m_TrayNotify,
1563 TNWM_GETMINIMUMSIZE,
1564 (WPARAM)Horizontal,
1565 (LPARAM)&TraySize))
1566 {
1567 /* Move the tray notification window to the desired location */
1568 if (Horizontal)
1569 ptTrayNotify.x = rcClient.right - TraySize.cx;
1570 else
1571 ptTrayNotify.y = rcClient.bottom - TraySize.cy;
1572
1573 dwp = ::DeferWindowPos(dwp,
1574 m_TrayNotify,
1575 NULL,
1576 ptTrayNotify.x,
1577 ptTrayNotify.y,
1578 TraySize.cx,
1579 TraySize.cy,
1580 SWP_NOZORDER | SWP_NOACTIVATE);
1581 if (dwp == NULL)
1582 {
1583 ERR("DeferWindowPos for notification area failed. lastErr=%d\n", GetLastError());
1584 return;
1585 }
1586 }
1587
1588 /* Resize/Move the rebar control */
1589 if (m_Rebar != NULL)
1590 {
1591 POINT ptRebar = { 0, 0 };
1592 SIZE szRebar;
1593
1594 SetWindowStyle(m_Rebar, CCS_VERT, Horizontal ? 0 : CCS_VERT);
1595
1596 if (Horizontal)
1597 {
1598 ptRebar.x = StartSize.cx + GetSystemMetrics(SM_CXSIZEFRAME);
1599 szRebar.cx = ptTrayNotify.x - ptRebar.x;
1600 szRebar.cy = rcClient.bottom;
1601 }
1602 else
1603 {
1604 ptRebar.y = StartSize.cy + GetSystemMetrics(SM_CYSIZEFRAME);
1605 szRebar.cx = rcClient.right;
1606 szRebar.cy = ptTrayNotify.y - ptRebar.y;
1607 }
1608
1609 dwp = ::DeferWindowPos(dwp,
1610 m_Rebar,
1611 NULL,
1612 ptRebar.x,
1613 ptRebar.y,
1614 szRebar.cx,
1615 szRebar.cy,
1616 SWP_NOZORDER | SWP_NOACTIVATE);
1617 }
1618
1619 if (dwp != NULL)
1620 EndDeferWindowPos(dwp);
1621
1622 if (m_TaskSwitch != NULL)
1623 {
1624 /* Update the task switch window configuration */
1625 SendMessage(m_TaskSwitch, TSWM_UPDATETASKBARPOS, 0, 0);
1626 }
1627 }
1628
1629 void FitToRebar(PRECT pRect)
1630 {
1631 /* Get the rect of the rebar */
1632 RECT rebarRect, taskbarRect;
1633 ::GetWindowRect(m_Rebar, &rebarRect);
1634 ::GetWindowRect(m_hWnd, &taskbarRect);
1635 OffsetRect(&rebarRect, -taskbarRect.left, -taskbarRect.top);
1636
1637 /* Calculate the difference of size of the taskbar and the rebar */
1638 SIZE margins;
1639 margins.cx = taskbarRect.right - taskbarRect.left - rebarRect.right + rebarRect.left;
1640 margins.cy = taskbarRect.bottom - taskbarRect.top - rebarRect.bottom + rebarRect.top;
1641
1642 /* Calculate the new size of the rebar and make it resize, then change the new taskbar size */
1643 switch (m_Position)
1644 {
1645 case ABE_TOP:
1646 rebarRect.bottom = rebarRect.top + pRect->bottom - pRect->top - margins.cy;
1647 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect);
1648 pRect->bottom = pRect->top + rebarRect.bottom - rebarRect.top + margins.cy;
1649 break;
1650 case ABE_BOTTOM:
1651 rebarRect.top = rebarRect.bottom - (pRect->bottom - pRect->top - margins.cy);
1652 ::SendMessageW(m_Rebar, RB_SIZETORECT, RBSTR_CHANGERECT, (LPARAM)&rebarRect);
1653 pRect->top = pRect->bottom - (rebarRect.bottom - rebarRect.top + margins.cy);
1654 break;
1655 case ABE_LEFT:
1656 case ABE_RIGHT:
1657 /* FIXME: what to do here? */
1658 break;
1659 }
1660
1661 CalculateValidSize(m_Position, pRect);
1662 }
1663
1664 void PopupStartMenu()
1665 {
1666 if (m_StartMenuPopup != NULL)
1667 {
1668 POINTL pt;
1669 RECTL rcExclude;
1670 DWORD dwFlags = 0;
1671
1672 if (m_StartButton.GetWindowRect((RECT*) &rcExclude))
1673 {
1674 switch (m_Position)
1675 {
1676 case ABE_BOTTOM:
1677 pt.x = rcExclude.left;
1678 pt.y = rcExclude.top;
1679 dwFlags |= MPPF_TOP;
1680 break;
1681 case ABE_TOP:
1682 pt.x = rcExclude.left;
1683 pt.y = rcExclude.bottom;
1684 dwFlags |= MPPF_BOTTOM;
1685 break;
1686 case ABE_LEFT:
1687 pt.x = rcExclude.right;
1688 pt.y = rcExclude.top;
1689 dwFlags |= MPPF_RIGHT;
1690 break;
1691 case ABE_RIGHT:
1692 pt.x = rcExclude.left;
1693 pt.y = rcExclude.top;
1694 dwFlags |= MPPF_LEFT;
1695 break;
1696 }
1697
1698 m_StartMenuPopup->Popup(&pt, &rcExclude, dwFlags);
1699
1700 m_StartButton.SendMessageW(BM_SETSTATE, TRUE, 0);
1701 }
1702 }
1703 }
1704
1705 void ProcessMouseTracking()
1706 {
1707 RECT rcCurrent;
1708 POINT pt;
1709 BOOL over;
1710 UINT state = m_AutoHideState;
1711
1712 GetCursorPos(&pt);
1713 GetWindowRect(&rcCurrent);
1714 over = PtInRect(&rcCurrent, pt);
1715
1716 if (m_StartButton.SendMessage( BM_GETSTATE, 0, 0) != BST_UNCHECKED)
1717 {
1718 over = TRUE;
1719 }
1720
1721 if (over)
1722 {
1723 if (state == AUTOHIDE_HIDING)
1724 {
1725 TRACE("AutoHide cancelling hide.\n");
1726 m_AutoHideState = AUTOHIDE_SHOWING;
1727 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1728 }
1729 else if (state == AUTOHIDE_HIDDEN)
1730 {
1731 TRACE("AutoHide starting show.\n");
1732 m_AutoHideState = AUTOHIDE_SHOWING;
1733 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_SHOW, NULL);
1734 }
1735 }
1736 else
1737 {
1738 if (state == AUTOHIDE_SHOWING)
1739 {
1740 TRACE("AutoHide cancelling show.\n");
1741 m_AutoHideState = AUTOHIDE_HIDING;
1742 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1743 }
1744 else if (state == AUTOHIDE_SHOWN)
1745 {
1746 TRACE("AutoHide starting hide.\n");
1747 m_AutoHideState = AUTOHIDE_HIDING;
1748 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
1749 }
1750
1751 KillTimer(TIMER_ID_MOUSETRACK);
1752 }
1753 }
1754
1755 void ProcessAutoHide()
1756 {
1757 RECT rc = m_TrayRects[m_Position];
1758 INT w = m_TraySize.cx - GetSystemMetrics(SM_CXBORDER) * 2 - 1;
1759 INT h = m_TraySize.cy - GetSystemMetrics(SM_CYBORDER) * 2 - 1;
1760
1761 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);
1762
1763 switch (m_AutoHideState)
1764 {
1765 case AUTOHIDE_HIDING:
1766 switch (m_Position)
1767 {
1768 case ABE_LEFT:
1769 m_AutoHideOffset.cy = 0;
1770 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_HIDE;
1771 if (m_AutoHideOffset.cx < -w)
1772 m_AutoHideOffset.cx = -w;
1773 break;
1774 case ABE_TOP:
1775 m_AutoHideOffset.cx = 0;
1776 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_HIDE;
1777 if (m_AutoHideOffset.cy < -h)
1778 m_AutoHideOffset.cy = -h;
1779 break;
1780 case ABE_RIGHT:
1781 m_AutoHideOffset.cy = 0;
1782 m_AutoHideOffset.cx += AUTOHIDE_SPEED_HIDE;
1783 if (m_AutoHideOffset.cx > w)
1784 m_AutoHideOffset.cx = w;
1785 break;
1786 case ABE_BOTTOM:
1787 m_AutoHideOffset.cx = 0;
1788 m_AutoHideOffset.cy += AUTOHIDE_SPEED_HIDE;
1789 if (m_AutoHideOffset.cy > h)
1790 m_AutoHideOffset.cy = h;
1791 break;
1792 }
1793
1794 if (m_AutoHideOffset.cx != w && m_AutoHideOffset.cy != h)
1795 {
1796 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1797 break;
1798 }
1799
1800 /* fallthrough */
1801 case AUTOHIDE_HIDDEN:
1802
1803 switch (m_Position)
1804 {
1805 case ABE_LEFT:
1806 m_AutoHideOffset.cx = -w;
1807 m_AutoHideOffset.cy = 0;
1808 break;
1809 case ABE_TOP:
1810 m_AutoHideOffset.cx = 0;
1811 m_AutoHideOffset.cy = -h;
1812 break;
1813 case ABE_RIGHT:
1814 m_AutoHideOffset.cx = w;
1815 m_AutoHideOffset.cy = 0;
1816 break;
1817 case ABE_BOTTOM:
1818 m_AutoHideOffset.cx = 0;
1819 m_AutoHideOffset.cy = h;
1820 break;
1821 }
1822
1823 KillTimer(TIMER_ID_AUTOHIDE);
1824 m_AutoHideState = AUTOHIDE_HIDDEN;
1825 break;
1826
1827 case AUTOHIDE_SHOWING:
1828 if (m_AutoHideOffset.cx >= AUTOHIDE_SPEED_SHOW)
1829 {
1830 m_AutoHideOffset.cx -= AUTOHIDE_SPEED_SHOW;
1831 }
1832 else if (m_AutoHideOffset.cx <= -AUTOHIDE_SPEED_SHOW)
1833 {
1834 m_AutoHideOffset.cx += AUTOHIDE_SPEED_SHOW;
1835 }
1836 else
1837 {
1838 m_AutoHideOffset.cx = 0;
1839 }
1840
1841 if (m_AutoHideOffset.cy >= AUTOHIDE_SPEED_SHOW)
1842 {
1843 m_AutoHideOffset.cy -= AUTOHIDE_SPEED_SHOW;
1844 }
1845 else if (m_AutoHideOffset.cy <= -AUTOHIDE_SPEED_SHOW)
1846 {
1847 m_AutoHideOffset.cy += AUTOHIDE_SPEED_SHOW;
1848 }
1849 else
1850 {
1851 m_AutoHideOffset.cy = 0;
1852 }
1853
1854 if (m_AutoHideOffset.cx != 0 || m_AutoHideOffset.cy != 0)
1855 {
1856 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
1857 break;
1858 }
1859
1860 /* fallthrough */
1861 case AUTOHIDE_SHOWN:
1862
1863 KillTimer(TIMER_ID_AUTOHIDE);
1864 m_AutoHideState = AUTOHIDE_SHOWN;
1865 break;
1866 }
1867
1868 rc.left += m_AutoHideOffset.cx;
1869 rc.right += m_AutoHideOffset.cx;
1870 rc.top += m_AutoHideOffset.cy;
1871 rc.bottom += m_AutoHideOffset.cy;
1872
1873 TRACE("AutoHide Changing position to (%d, %d, %d, %d) and state=%u.\n", rc.left, rc.top, rc.right, rc.bottom, m_AutoHideState);
1874 SetWindowPos(NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
1875 }
1876
1877
1878
1879
1880
1881 /**********************************************************
1882 * ##### taskbar drawing #####
1883 */
1884
1885 LRESULT EraseBackgroundWithTheme(HDC hdc)
1886 {
1887 RECT rect;
1888 int iSBkgndPart[4] = {TBP_BACKGROUNDLEFT, TBP_BACKGROUNDTOP, TBP_BACKGROUNDRIGHT, TBP_BACKGROUNDBOTTOM};
1889
1890 ASSERT(m_Position <= ABE_BOTTOM);
1891
1892 if (m_Theme)
1893 {
1894 GetClientRect(&rect);
1895 DrawThemeBackground(m_Theme, hdc, iSBkgndPart[m_Position], 0, &rect, 0);
1896 }
1897
1898 return 0;
1899 }
1900
1901 int DrawSizerWithTheme(IN HRGN hRgn)
1902 {
1903 HDC hdc;
1904 RECT rect;
1905 int iSizerPart[4] = {TBP_SIZINGBARLEFT, TBP_SIZINGBARTOP, TBP_SIZINGBARRIGHT, TBP_SIZINGBARBOTTOM};
1906 SIZE size;
1907
1908 ASSERT(m_Position <= ABE_BOTTOM);
1909
1910 HRESULT hr = GetThemePartSize(m_Theme, NULL, iSizerPart[m_Position], 0, NULL, TS_TRUE, &size);
1911 if (FAILED_UNEXPECTEDLY(hr))
1912 return 0;
1913
1914 GetWindowRect(&rect);
1915 OffsetRect(&rect, -rect.left, -rect.top);
1916
1917 hdc = GetWindowDC();
1918
1919 switch (m_Position)
1920 {
1921 case ABE_LEFT:
1922 rect.left = rect.right - size.cx;
1923 break;
1924 case ABE_TOP:
1925 rect.top = rect.bottom - size.cy;
1926 break;
1927 case ABE_RIGHT:
1928 rect.right = rect.left + size.cx;
1929 break;
1930 case ABE_BOTTOM:
1931 default:
1932 rect.bottom = rect.top + size.cy;
1933 break;
1934 }
1935
1936 DrawThemeBackground(m_Theme, hdc, iSizerPart[m_Position], 0, &rect, 0);
1937
1938 ReleaseDC(hdc);
1939 return 0;
1940 }
1941
1942
1943
1944
1945
1946 /*
1947 * ITrayWindow
1948 */
1949 HRESULT STDMETHODCALLTYPE Open()
1950 {
1951 RECT rcWnd;
1952
1953 /* Check if there's already a window created and try to show it.
1954 If it was somehow destroyed just create a new tray window. */
1955 if (m_hWnd != NULL && IsWindow())
1956 {
1957 return S_OK;
1958 }
1959
1960 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
1961 if (AlwaysOnTop)
1962 dwExStyle |= WS_EX_TOPMOST;
1963
1964 DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
1965 if(!m_Theme)
1966 {
1967 dwStyle |= WS_THICKFRAME | WS_BORDER;
1968 }
1969
1970 ZeroMemory(&rcWnd, sizeof(rcWnd));
1971 if (m_Position != (DWORD) -1)
1972 rcWnd = m_TrayRects[m_Position];
1973
1974 if (!Create(NULL, rcWnd, NULL, dwStyle, dwExStyle))
1975 return E_FAIL;
1976
1977 /* Align all controls on the tray window */
1978 AlignControls(NULL);
1979
1980 /* Move the tray window to the right position and resize it if necessary */
1981 CheckTrayWndPosition();
1982
1983 return S_OK;
1984 }
1985
1986 HRESULT STDMETHODCALLTYPE Close()
1987 {
1988 if (m_hWnd != NULL)
1989 {
1990 SendMessage(m_hWnd,
1991 WM_APP_TRAYDESTROY,
1992 0,
1993 0);
1994 }
1995
1996 return S_OK;
1997 }
1998
1999 HWND STDMETHODCALLTYPE GetHWND()
2000 {
2001 return m_hWnd;
2002 }
2003
2004 BOOL STDMETHODCALLTYPE IsSpecialHWND(IN HWND hWnd)
2005 {
2006 return (m_hWnd == hWnd ||
2007 (m_DesktopWnd != NULL && m_hWnd == m_DesktopWnd));
2008 }
2009
2010 BOOL STDMETHODCALLTYPE IsHorizontal()
2011 {
2012 return IsPosHorizontal();
2013 }
2014
2015 BOOL STDMETHODCALLTYPE Lock(IN BOOL bLock)
2016 {
2017 BOOL bPrevLock = Locked;
2018
2019 if (Locked != bLock)
2020 {
2021 Locked = bLock;
2022
2023 if (m_TrayBandSite != NULL)
2024 {
2025 if (!SUCCEEDED(m_TrayBandSite->Lock(bLock)))
2026 {
2027 /* Reset?? */
2028 Locked = bPrevLock;
2029 return bPrevLock;
2030 }
2031 }
2032
2033 if (m_Theme)
2034 {
2035 /* Update cached tray sizes */
2036 for(DWORD Pos = ABE_LEFT; Pos <= ABE_BOTTOM; Pos++)
2037 {
2038 RECT rcGripper = {0};
2039 AdjustSizerRect(&rcGripper, Pos);
2040
2041 if(Locked)
2042 {
2043 m_TrayRects[Pos].top += rcGripper.top;
2044 m_TrayRects[Pos].left += rcGripper.left;
2045 m_TrayRects[Pos].bottom += rcGripper.bottom;
2046 m_TrayRects[Pos].right += rcGripper.right;
2047 }
2048 else
2049 {
2050 m_TrayRects[Pos].top -= rcGripper.top;
2051 m_TrayRects[Pos].left -= rcGripper.left;
2052 m_TrayRects[Pos].bottom -= rcGripper.bottom;
2053 m_TrayRects[Pos].right -= rcGripper.right;
2054 }
2055 }
2056 }
2057 SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER);
2058 ResizeWorkArea();
2059 ApplyClipping(TRUE);
2060 }
2061
2062 return bPrevLock;
2063 }
2064
2065
2066
2067
2068
2069
2070 /**********************************************************
2071 * ##### message handling #####
2072 */
2073
2074 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2075 {
2076 HRESULT hRet;
2077
2078 ((ITrayWindow*)this)->AddRef();
2079
2080 SetWindowTheme(m_hWnd, L"TaskBar", NULL);
2081
2082 /* Create the Start button */
2083 m_StartButton.Create(m_hWnd);
2084
2085 /* Load the saved tray window settings */
2086 RegLoadSettings();
2087
2088 /* Create and initialize the start menu */
2089 HBITMAP hbmBanner = LoadBitmapW(hExplorerInstance, MAKEINTRESOURCEW(IDB_STARTMENU));
2090 m_StartMenuPopup = CreateStartMenu(this, &m_StartMenuBand, hbmBanner, 0);
2091
2092 /* Create the task band */
2093 hRet = CTaskBand_CreateInstance(this, m_StartButton.m_hWnd, IID_PPV_ARG(IDeskBand, &m_TaskBand));
2094 if (FAILED_UNEXPECTEDLY(hRet))
2095 return FALSE;
2096
2097 /* Create the rebar band site. This actually creates the rebar and the tasks toolbar. */
2098 hRet = CTrayBandSite_CreateInstance(this, m_TaskBand, &m_TrayBandSite);
2099 if (FAILED_UNEXPECTEDLY(hRet))
2100 return FALSE;
2101
2102 /* Get the hwnd of the rebar */
2103 hRet = IUnknown_GetWindow(m_TrayBandSite, &m_Rebar);
2104 if (FAILED_UNEXPECTEDLY(hRet))
2105 return FALSE;
2106
2107 /* Get the hwnd of the tasks toolbar */
2108 hRet = IUnknown_GetWindow(m_TaskBand, &m_TaskSwitch);
2109 if (FAILED_UNEXPECTEDLY(hRet))
2110 return FALSE;
2111
2112 SetWindowTheme(m_Rebar, L"TaskBar", NULL);
2113
2114 /* Create the tray notification window */
2115 m_TrayNotify = CreateTrayNotifyWnd(this, HideClock, &m_TrayNotifyInstance);
2116
2117 UpdateFonts();
2118
2119 InitShellServices(&m_ShellServices);
2120
2121 if (AutoHide)
2122 {
2123 m_AutoHideState = AUTOHIDE_HIDING;
2124 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
2125 }
2126
2127 RegisterHotKey(m_hWnd, IDHK_RUN, MOD_WIN, 'R');
2128 RegisterHotKey(m_hWnd, IDHK_MINIMIZE_ALL, MOD_WIN, 'M');
2129 RegisterHotKey(m_hWnd, IDHK_RESTORE_ALL, MOD_WIN|MOD_SHIFT, 'M');
2130 RegisterHotKey(m_hWnd, IDHK_HELP, MOD_WIN, VK_F1);
2131 RegisterHotKey(m_hWnd, IDHK_EXPLORE, MOD_WIN, 'E');
2132 RegisterHotKey(m_hWnd, IDHK_FIND, MOD_WIN, 'F');
2133 RegisterHotKey(m_hWnd, IDHK_FIND_COMPUTER, MOD_WIN|MOD_CONTROL, 'F');
2134 RegisterHotKey(m_hWnd, IDHK_NEXT_TASK, MOD_WIN, VK_TAB);
2135 RegisterHotKey(m_hWnd, IDHK_PREV_TASK, MOD_WIN|MOD_SHIFT, VK_TAB);
2136 RegisterHotKey(m_hWnd, IDHK_SYS_PROPERTIES, MOD_WIN, VK_PAUSE);
2137 RegisterHotKey(m_hWnd, IDHK_DESKTOP, MOD_WIN, 'D');
2138 RegisterHotKey(m_hWnd, IDHK_PAGER, MOD_WIN, 'B');
2139
2140 return TRUE;
2141 }
2142
2143 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2144 {
2145 if (m_Theme)
2146 CloseThemeData(m_Theme);
2147
2148 m_Theme = OpenThemeData(m_hWnd, L"TaskBar");
2149
2150 if (m_Theme)
2151 {
2152 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, 0);
2153 }
2154 else
2155 {
2156 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, WS_THICKFRAME | WS_BORDER);
2157 }
2158 SetWindowPos(NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
2159
2160 return TRUE;
2161 }
2162
2163 LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2164 {
2165 if (wParam == SPI_SETNONCLIENTMETRICS)
2166 {
2167 SendMessage(m_TaskSwitch, uMsg, wParam, lParam);
2168 UpdateFonts();
2169 AlignControls(NULL);
2170 CheckTrayWndPosition();
2171 }
2172
2173 return 0;
2174 }
2175
2176 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2177 {
2178 HDC hdc = (HDC) wParam;
2179
2180 if (!m_Theme)
2181 {
2182 bHandled = FALSE;
2183 return 0;
2184 }
2185
2186 return EraseBackgroundWithTheme(hdc);
2187 }
2188
2189 LRESULT OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2190 {
2191 /* Move the tray window to the right position and resize it if necessary */
2192 CheckTrayWndPosition();
2193
2194 return TRUE;
2195 }
2196
2197 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2198 {
2199 if (m_TrayNotify)
2200 {
2201 TRACE("WM_COPYDATA notify message received. Handling...\n");
2202 return TrayNotify_NotifyIconCmd(m_TrayNotifyInstance, wParam, lParam);
2203 }
2204 return TRUE;
2205 }
2206
2207 LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2208 {
2209 if (!m_Theme)
2210 {
2211 bHandled = FALSE;
2212 return 0;
2213 }
2214
2215 return DrawSizerWithTheme((HRGN) wParam);
2216 }
2217
2218 LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2219 {
2220 SetBkMode((HDC) wParam, TRANSPARENT);
2221 return (LRESULT) GetStockObject(HOLLOW_BRUSH);
2222 }
2223
2224 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2225 {
2226 RECT rcClient;
2227 POINT pt;
2228
2229 if (Locked)
2230 {
2231 /* The user may not be able to resize the tray window.
2232 Pretend like the window is not sizeable when the user
2233 clicks on the border. */
2234 return HTBORDER;
2235 }
2236
2237 SetLastError(ERROR_SUCCESS);
2238 if (GetClientRect(&rcClient) &&
2239 (MapWindowPoints(NULL, (LPPOINT) &rcClient, 2) != 0 || GetLastError() == ERROR_SUCCESS))
2240 {
2241 pt.x = (SHORT) LOWORD(lParam);
2242 pt.y = (SHORT) HIWORD(lParam);
2243
2244 if (PtInRect(&rcClient,
2245 pt))
2246 {
2247 /* The user is trying to drag the tray window */
2248 return HTCAPTION;
2249 }
2250
2251 /* Depending on the position of the tray window, allow only
2252 changing the border next to the monitor working area */
2253 switch (m_Position)
2254 {
2255 case ABE_TOP:
2256 if (pt.y > rcClient.bottom)
2257 return HTBOTTOM;
2258 break;
2259 case ABE_LEFT:
2260 if (pt.x > rcClient.right)
2261 return HTRIGHT;
2262 break;
2263 case ABE_RIGHT:
2264 if (pt.x < rcClient.left)
2265 return HTLEFT;
2266 break;
2267 case ABE_BOTTOM:
2268 default:
2269 if (pt.y < rcClient.top)
2270 return HTTOP;
2271 break;
2272 }
2273 }
2274 return HTBORDER;
2275 return TRUE;
2276 }
2277
2278 LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2279 {
2280 POINT ptCursor;
2281 PRECT pRect = (PRECT) lParam;
2282
2283 /* We need to ensure that an application can not accidently
2284 move the tray window (using SetWindowPos). However, we still
2285 need to be able to move the window in case the user wants to
2286 drag the tray window to another position or in case the user
2287 wants to resize the tray window. */
2288 if (!Locked && GetCursorPos(&ptCursor))
2289 {
2290 IsDragging = TRUE;
2291 m_DraggingPosition = GetDraggingRectFromPt(ptCursor, pRect, &m_DraggingMonitor);
2292 }
2293 else
2294 {
2295 *pRect = m_TrayRects[m_Position];
2296
2297 if (AutoHide)
2298 {
2299 pRect->left += m_AutoHideOffset.cx;
2300 pRect->right += m_AutoHideOffset.cx;
2301 pRect->top += m_AutoHideOffset.cy;
2302 pRect->bottom += m_AutoHideOffset.cy;
2303 }
2304 }
2305 return TRUE;
2306 }
2307
2308 LRESULT OnSizing(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2309 {
2310 PRECT pRect = (PRECT) lParam;
2311
2312 if (!Locked)
2313 {
2314 FitToRebar(pRect);
2315 }
2316 else
2317 {
2318 *pRect = m_TrayRects[m_Position];
2319
2320 if (AutoHide)
2321 {
2322 pRect->left += m_AutoHideOffset.cx;
2323 pRect->right += m_AutoHideOffset.cx;
2324 pRect->top += m_AutoHideOffset.cy;
2325 pRect->bottom += m_AutoHideOffset.cy;
2326 }
2327 }
2328 return TRUE;
2329 }
2330
2331 LRESULT OnWindowPosChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2332 {
2333 ChangingWinPos((LPWINDOWPOS) lParam);
2334 return TRUE;
2335 }
2336
2337 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2338 {
2339 RECT rcClient;
2340 if (wParam == SIZE_RESTORED && lParam == 0)
2341 {
2342 ResizeWorkArea();
2343 /* Clip the tray window on multi monitor systems so the edges can't
2344 overlap into another monitor */
2345 ApplyClipping(TRUE);
2346
2347 if (!GetClientRect(&rcClient))
2348 {
2349 return FALSE;
2350 }
2351 }
2352 else
2353 {
2354 rcClient.left = rcClient.top = 0;
2355 rcClient.right = LOWORD(lParam);
2356 rcClient.bottom = HIWORD(lParam);
2357 }
2358
2359 AlignControls(&rcClient);
2360 return TRUE;
2361 }
2362
2363 LRESULT OnEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2364 {
2365 InSizeMove = TRUE;
2366 IsDragging = FALSE;
2367 if (!Locked)
2368 {
2369 /* Remove the clipping on multi monitor systems while dragging around */
2370 ApplyClipping(FALSE);
2371 }
2372 return TRUE;
2373 }
2374
2375 LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2376 {
2377 InSizeMove = FALSE;
2378 if (!Locked)
2379 {
2380 /* Apply clipping */
2381 PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2382 }
2383 return TRUE;
2384 }
2385
2386 LRESULT OnSysChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2387 {
2388 switch (wParam)
2389 {
2390 case TEXT(' '):
2391 {
2392 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2393 The tray window needs to handle this specially, since it normally doesn't have
2394 a system menu. */
2395
2396 static const UINT uidDisableItem [] = {
2397 SC_RESTORE,
2398 SC_MOVE,
2399 SC_SIZE,
2400 SC_MAXIMIZE,
2401 SC_MINIMIZE,
2402 };
2403 HMENU hSysMenu;
2404 UINT i, uId;
2405
2406 /* temporarily enable the system menu */
2407 SetWindowStyle(m_hWnd, WS_SYSMENU, WS_SYSMENU);
2408
2409 hSysMenu = GetSystemMenu(FALSE);
2410 if (hSysMenu != NULL)
2411 {
2412 /* Disable all items that are not relevant */
2413 for (i = 0; i < _countof(uidDisableItem); i++)
2414 {
2415 EnableMenuItem(hSysMenu,
2416 uidDisableItem[i],
2417 MF_BYCOMMAND | MF_GRAYED);
2418 }
2419
2420 EnableMenuItem(hSysMenu,
2421 SC_CLOSE,
2422 MF_BYCOMMAND |
2423 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2424
2425 /* Display the system menu */
2426 uId = TrackMenu(
2427 hSysMenu,
2428 NULL,
2429 m_StartButton.m_hWnd,
2430 m_Position != ABE_TOP,
2431 FALSE);
2432 if (uId != 0)
2433 {
2434 SendMessage(m_hWnd, WM_SYSCOMMAND, (WPARAM) uId, 0);
2435 }
2436 }
2437
2438 /* revert the system menu window style */
2439 SetWindowStyle(m_hWnd, WS_SYSMENU, 0);
2440 break;
2441 }
2442
2443 default:
2444 bHandled = FALSE;
2445 }
2446 return TRUE;
2447 }
2448
2449 LRESULT OnNcLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2450 {
2451 /* This handler implements the trick that makes the start button to
2452 get pressed when the user clicked left or below the button */
2453
2454 POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
2455 WINDOWINFO wi = {sizeof(WINDOWINFO)};
2456 RECT rcStartBtn;
2457
2458 bHandled = FALSE;
2459
2460 m_StartButton.GetWindowRect(&rcStartBtn);
2461 GetWindowInfo(m_hWnd, &wi);
2462
2463 switch (m_Position)
2464 {
2465 case ABE_TOP:
2466 case ABE_LEFT:
2467 {
2468 if (pt.x > rcStartBtn.right || pt.y > rcStartBtn.bottom)
2469 return 0;
2470 break;
2471 }
2472 case ABE_RIGHT:
2473 {
2474 if (pt.x < rcStartBtn.left || pt.y > rcStartBtn.bottom)
2475 return 0;
2476
2477 if (rcStartBtn.right + (int)wi.cxWindowBorders * 2 + 1 < wi.rcWindow.right &&
2478 pt.x > rcStartBtn.right)
2479 {
2480 return 0;
2481 }
2482 break;
2483 }
2484 case ABE_BOTTOM:
2485 {
2486 if (pt.x > rcStartBtn.right || pt.y < rcStartBtn.top)
2487 {
2488 return 0;
2489 }
2490
2491 if (rcStartBtn.bottom + (int)wi.cyWindowBorders * 2 + 1 < wi.rcWindow.bottom &&
2492 pt.y > rcStartBtn.bottom)
2493 {
2494 return 0;
2495 }
2496
2497 break;
2498 }
2499 }
2500
2501 bHandled = TRUE;
2502 PopupStartMenu();
2503 return 0;
2504 }
2505
2506 LRESULT OnNcRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2507 {
2508 /* We want the user to be able to get a context menu even on the nonclient
2509 area (including the sizing border)! */
2510 uMsg = WM_CONTEXTMENU;
2511 wParam = (WPARAM) m_hWnd;
2512
2513 return OnContextMenu(uMsg, wParam, lParam, bHandled);
2514 }
2515
2516 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2517 {
2518 LRESULT Ret = FALSE;
2519 POINT pt, *ppt = NULL;
2520 HWND hWndExclude = NULL;
2521
2522 /* Check if the administrator has forbidden access to context menus */
2523 if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2524 return FALSE;
2525
2526 pt.x = (SHORT) LOWORD(lParam);
2527 pt.y = (SHORT) HIWORD(lParam);
2528
2529 if (pt.x != -1 || pt.y != -1)
2530 ppt = &pt;
2531 else
2532 hWndExclude = m_StartButton.m_hWnd;
2533
2534 if ((HWND) wParam == m_StartButton.m_hWnd)
2535 {
2536 /* Make sure we can't track the context menu if the start
2537 menu is currently being shown */
2538 if (!(m_StartButton.SendMessage(BM_GETSTATE, 0, 0) & BST_PUSHED))
2539 {
2540 CComPtr<IContextMenu> ctxMenu;
2541 StartMenuBtnCtxMenuCreator(this, m_hWnd, &ctxMenu);
2542 TrackCtxMenu(ctxMenu, ppt, hWndExclude, m_Position == ABE_BOTTOM, this);
2543 }
2544 }
2545 else
2546 {
2547 /* See if the context menu should be handled by the task band site */
2548 if (ppt != NULL && m_TrayBandSite != NULL)
2549 {
2550 HWND hWndAtPt;
2551 POINT ptClient = *ppt;
2552
2553 /* Convert the coordinates to client-coordinates */
2554 ::MapWindowPoints(NULL, m_hWnd, &ptClient, 1);
2555
2556 hWndAtPt = ChildWindowFromPoint(ptClient);
2557 if (hWndAtPt != NULL &&
2558 (hWndAtPt == m_Rebar || ::IsChild(m_Rebar, hWndAtPt)))
2559 {
2560 /* Check if the user clicked on the task switch window */
2561 ptClient = *ppt;
2562 ::MapWindowPoints(NULL, m_Rebar, &ptClient, 1);
2563
2564 hWndAtPt = ::ChildWindowFromPointEx(m_Rebar, ptClient, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2565 if (hWndAtPt == m_TaskSwitch)
2566 goto HandleTrayContextMenu;
2567
2568 /* Forward the message to the task band site */
2569 m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2570 }
2571 else
2572 goto HandleTrayContextMenu;
2573 }
2574 else
2575 {
2576 HandleTrayContextMenu:
2577 /* Tray the default tray window context menu */
2578 CComPtr<IContextMenu> ctxMenu;
2579 TrayWindowCtxMenuCreator(this, m_hWnd, &ctxMenu);
2580 TrackCtxMenu(ctxMenu, ppt, NULL, FALSE, this);
2581 }
2582 }
2583 return Ret;
2584 }
2585
2586 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2587 {
2588 LRESULT Ret = FALSE;
2589 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2590 the rebar control! But we shouldn't forward messages that the band
2591 site doesn't handle, such as other controls (start button, tray window */
2592
2593 HRESULT hr = E_FAIL;
2594
2595 if (m_TrayBandSite)
2596 {
2597 hr = m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2598 if (SUCCEEDED(hr))
2599 return Ret;
2600 }
2601
2602 if (m_TrayBandSite == NULL || FAILED(hr))
2603 {
2604 const NMHDR *nmh = (const NMHDR *) lParam;
2605
2606 if (nmh->hwndFrom == m_TrayNotify)
2607 {
2608 switch (nmh->code)
2609 {
2610 case NTNWM_REALIGN:
2611 /* Cause all controls to be aligned */
2612 PostMessage(WM_SIZE, SIZE_RESTORED, 0);
2613 break;
2614 }
2615 }
2616 }
2617 return Ret;
2618 }
2619
2620 LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2621 {
2622 /* We "handle" this message so users can't cause a weird maximize/restore
2623 window animation when double-clicking the tray window! */
2624
2625 /* We should forward mouse messages to child windows here.
2626 Right now, this is only clock double-click */
2627 RECT rcClock;
2628 if (TrayNotify_GetClockRect(m_TrayNotifyInstance, &rcClock))
2629 {
2630 POINT ptClick;
2631 ptClick.x = MAKEPOINTS(lParam).x;
2632 ptClick.y = MAKEPOINTS(lParam).y;
2633 if (PtInRect(&rcClock, ptClick))
2634 {
2635 //FIXME: use SHRunControlPanel
2636 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
2637 }
2638 }
2639 return TRUE;
2640 }
2641
2642 LRESULT OnAppTrayDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2643 {
2644 DestroyWindow();
2645 return TRUE;
2646 }
2647
2648 LRESULT OnOpenStartMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2649 {
2650 HWND hwndStartMenu;
2651 HRESULT hr = IUnknown_GetWindow(m_StartMenuPopup, &hwndStartMenu);
2652 if (FAILED_UNEXPECTEDLY(hr))
2653 return FALSE;
2654
2655 if (::IsWindowVisible(hwndStartMenu))
2656 {
2657 m_StartMenuPopup->OnSelect(MPOS_CANCELLEVEL);
2658 }
2659 else
2660 {
2661 PopupStartMenu();
2662 }
2663
2664 return TRUE;
2665 }
2666
2667 LRESULT OnDoExitWindows(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2668 {
2669 /*
2670 * TWM_DOEXITWINDOWS is send by the CDesktopBrowser to us
2671 * to show the shutdown dialog. Also a WM_CLOSE message sent
2672 * by apps should show the dialog.
2673 */
2674 return DoExitWindows();
2675 }
2676
2677 LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2678 {
2679 if (wParam == SC_CLOSE)
2680 {
2681 return DoExitWindows();
2682 }
2683
2684 bHandled = FALSE;
2685 return TRUE;
2686 }
2687
2688 LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2689 {
2690 return HandleHotKey(wParam);
2691 }
2692
2693 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2694 {
2695 LRESULT Ret = FALSE;
2696
2697 if ((HWND) lParam == m_StartButton.m_hWnd)
2698 {
2699 PopupStartMenu();
2700 return FALSE;
2701 }
2702
2703 if (m_TrayBandSite == NULL || FAILED_UNEXPECTEDLY(m_TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret)))
2704 {
2705 return HandleCommand(LOWORD(wParam));
2706 }
2707 return Ret;
2708 }
2709
2710 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2711 {
2712 if (AutoHide)
2713 {
2714 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2715 }
2716
2717 return TRUE;
2718 }
2719
2720 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2721 {
2722 if (wParam == TIMER_ID_MOUSETRACK)
2723 {
2724 ProcessMouseTracking();
2725 }
2726 else if (wParam == TIMER_ID_AUTOHIDE)
2727 {
2728 ProcessAutoHide();
2729 }
2730
2731 bHandled = FALSE;
2732 return TRUE;
2733 }
2734
2735 LRESULT OnNcCalcSize(INT code, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2736 {
2737 RECT *rc = NULL;
2738 /* Ignore WM_NCCALCSIZE if we are not themed or locked */
2739 if(!m_Theme || Locked)
2740 {
2741 bHandled = FALSE;
2742 return 0;
2743 }
2744 if(!wParam)
2745 {
2746 rc = (RECT*)wParam;
2747 }
2748 else
2749 {
2750 NCCALCSIZE_PARAMS *prms = (NCCALCSIZE_PARAMS*)lParam;
2751 if(prms->lppos->flags & SWP_NOSENDCHANGING)
2752 {
2753 bHandled = FALSE;
2754 return 0;
2755 }
2756 rc = &prms->rgrc[0];
2757 }
2758
2759 AdjustSizerRect(rc, m_Position);
2760
2761 return 0;
2762 }
2763
2764 LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
2765 {
2766 #if 0
2767 LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr;
2768
2769 if (!as->fChanged)
2770 return 0;
2771
2772 RECT rc;
2773 ::GetWindowRect(m_hWnd, &rc);
2774
2775 SIZE szWindow = {
2776 rc.right - rc.left,
2777 rc.bottom - rc.top };
2778 SIZE szTarget = {
2779 as->rcTarget.right - as->rcTarget.left,
2780 as->rcTarget.bottom - as->rcTarget.top };
2781 SIZE szActual = {
2782 as->rcActual.right - as->rcActual.left,
2783 as->rcActual.bottom - as->rcActual.top };
2784
2785 SIZE borders = {
2786 szWindow.cx - szTarget.cx,
2787 szWindow.cy - szTarget.cx,
2788 };
2789
2790 switch (m_Position)
2791 {
2792 case ABE_LEFT:
2793 szWindow.cx = szActual.cx + borders.cx;
2794 break;
2795 case ABE_TOP:
2796 szWindow.cy = szActual.cy + borders.cy;
2797 break;
2798 case ABE_RIGHT:
2799 szWindow.cx = szActual.cx + borders.cx;
2800 rc.left = rc.right - szWindow.cy;
2801 break;
2802 case ABE_BOTTOM:
2803 szWindow.cy = szActual.cy + borders.cy;
2804 rc.top = rc.bottom - szWindow.cy;
2805 break;
2806 }
2807
2808 SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER);
2809 #else
2810 bHandled = FALSE;
2811 #endif
2812 return 0;
2813 }
2814
2815 DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE)
2816
2817 BEGIN_MSG_MAP(CTrayWindow)
2818 if (m_StartMenuBand != NULL)
2819 {
2820 MSG Msg;
2821 LRESULT lRet;
2822
2823 Msg.hwnd = m_hWnd;
2824 Msg.message = uMsg;
2825 Msg.wParam = wParam;
2826 Msg.lParam = lParam;
2827
2828 if (m_StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK)
2829 {
2830 return lRet;
2831 }
2832
2833 wParam = Msg.wParam;
2834 lParam = Msg.lParam;
2835 }
2836 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
2837 MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
2838 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P
2839 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2840 MESSAGE_HANDLER(WM_SIZE, OnSize)
2841 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2842 /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/
2843 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
2844 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2845 MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
2846 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2847 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
2848 MESSAGE_HANDLER(WM_TIMER, OnTimer)
2849 MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange)
2850 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2851 MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
2852 MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
2853 MESSAGE_HANDLER(WM_MOVING, OnMoving)
2854 MESSAGE_HANDLER(WM_SIZING, OnSizing)
2855 MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChange)
2856 MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove)
2857 MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
2858 MESSAGE_HANDLER(WM_NCLBUTTONDOWN, OnNcLButtonDown)
2859 MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar)
2860 MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp)
2861 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
2862 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
2863 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
2864 MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
2865 MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
2866 MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
2867 MESSAGE_HANDLER(WM_CLOSE, OnDoExitWindows)
2868 MESSAGE_HANDLER(WM_HOTKEY, OnHotkey)
2869 MESSAGE_HANDLER(WM_NCCALCSIZE, OnNcCalcSize)
2870 ALT_MSG_MAP(1)
2871 END_MSG_MAP()
2872
2873 /*****************************************************************************/
2874
2875 VOID TrayProcessMessages()
2876 {
2877 MSG Msg;
2878
2879 /* FIXME: We should keep a reference here... */
2880
2881 while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
2882 {
2883 if (Msg.message == WM_QUIT)
2884 break;
2885
2886 if (m_StartMenuBand == NULL ||
2887 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2888 {
2889 TranslateMessage(&Msg);
2890 DispatchMessage(&Msg);
2891 }
2892 }
2893 }
2894
2895 VOID TrayMessageLoop()
2896 {
2897 MSG Msg;
2898 BOOL Ret;
2899
2900 /* FIXME: We should keep a reference here... */
2901
2902 while (true)
2903 {
2904 Ret = GetMessage(&Msg, NULL, 0, 0);
2905
2906 if (!Ret || Ret == -1)
2907 break;
2908
2909 if (m_StartMenuBand == NULL ||
2910 m_StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2911 {
2912 TranslateMessage(&Msg);
2913 DispatchMessage(&Msg);
2914 }
2915 }
2916 }
2917
2918 /*
2919 * IShellDesktopTray
2920 *
2921 * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()!
2922 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2923 * The reason we implement it is because we have to use SHCreateDesktop() so
2924 * that the shell provides the desktop window and all the features that come
2925 * with it (especially positioning of desktop icons)
2926 */
2927
2928 virtual ULONG STDMETHODCALLTYPE GetState()
2929 {
2930 /* FIXME: Return ABS_ flags? */
2931 TRACE("IShellDesktopTray::GetState() unimplemented!\n");
2932 return 0;
2933 }
2934
2935 virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray)
2936 {
2937 TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
2938 *phWndTray = m_hWnd;
2939 return S_OK;
2940 }
2941
2942 virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop)
2943 {
2944 TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
2945
2946 m_DesktopWnd = hWndDesktop;
2947 return S_OK;
2948 }
2949
2950 virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2)
2951 {
2952 TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
2953 return S_OK;
2954 }
2955
2956 virtual HRESULT RaiseStartButton()
2957 {
2958 m_StartButton.SendMessageW(BM_SETSTATE, FALSE, 0);
2959 return S_OK;
2960 }
2961
2962 HRESULT WINAPI GetWindow(HWND* phwnd)
2963 {
2964 if (!phwnd)
2965 return E_INVALIDARG;
2966 *phwnd = m_hWnd;
2967 return S_OK;
2968 }
2969
2970 HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
2971 {
2972 return E_NOTIMPL;
2973 }
2974
2975 void _Init()
2976 {
2977 m_Position = (DWORD) -1;
2978 }
2979
2980 DECLARE_NOT_AGGREGATABLE(CTrayWindow)
2981
2982 DECLARE_PROTECT_FINAL_CONSTRUCT()
2983 BEGIN_COM_MAP(CTrayWindow)
2984 /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/
2985 COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray)
2986 COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
2987 END_COM_MAP()
2988 };
2989
2990 class CTrayWindowCtxMenu :
2991 public CComCoClass<CTrayWindowCtxMenu>,
2992 public CComObjectRootEx<CComMultiThreadModelNoCS>,
2993 public IContextMenu
2994 {
2995 HWND hWndOwner;
2996 CComPtr<CTrayWindow> TrayWnd;
2997 CComPtr<IContextMenu> pcm;
2998
2999 public:
3000 HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
3001 {
3002 this->TrayWnd = (CTrayWindow *) pTrayWnd;
3003 this->hWndOwner = hWndOwner;
3004 return S_OK;
3005 }
3006
3007 virtual HRESULT STDMETHODCALLTYPE
3008 QueryContextMenu(HMENU hPopup,
3009 UINT indexMenu,
3010 UINT idCmdFirst,
3011 UINT idCmdLast,
3012 UINT uFlags)
3013 {
3014 HMENU menubase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCEW(IDM_TRAYWND));
3015
3016 if (!menubase)
3017 return HRESULT_FROM_WIN32(GetLastError());
3018
3019 int count = ::GetMenuItemCount(menubase);
3020
3021 for (int i = 0; i < count; i++)
3022 {
3023 WCHAR label[128];
3024
3025 MENUITEMINFOW mii = { 0 };
3026 mii.cbSize = sizeof(mii);
3027 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS
3028 | MIIM_DATA | MIIM_STRING | MIIM_BITMAP | MIIM_FTYPE;
3029 mii.dwTypeData = label;
3030 mii.cch = _countof(label);
3031 ::GetMenuItemInfoW(menubase, i, TRUE, &mii);
3032
3033 TRACE("Adding item %d label %S type %d\n", mii.wID, mii.dwTypeData, mii.fType);
3034
3035 ::InsertMenuItemW(hPopup, i + 1, TRUE, &mii);
3036 }
3037
3038 ::DestroyMenu(menubase);
3039
3040 if (SHRestricted(REST_CLASSICSHELL) != 0)
3041 {
3042 DeleteMenu(hPopup,
3043 ID_LOCKTASKBAR,
3044 MF_BYCOMMAND);
3045 }
3046
3047 CheckMenuItem(hPopup,
3048 ID_LOCKTASKBAR,
3049 MF_BYCOMMAND | (TrayWnd->Locked ? MF_CHECKED : MF_UNCHECKED));
3050
3051 if (TrayWnd->m_TrayBandSite != NULL)
3052 {
3053 if (FAILED(TrayWnd->m_TrayBandSite->AddContextMenus(
3054 hPopup,
3055 0,
3056 ID_SHELL_CMD_FIRST,
3057 ID_SHELL_CMD_LAST,
3058 CMF_NORMAL,
3059 &pcm)))
3060 {
3061 WARN("AddContextMenus failed.\n");
3062 pcm = NULL;
3063 }
3064 }
3065
3066 return S_OK;
3067 }
3068
3069 virtual HRESULT STDMETHODCALLTYPE
3070 InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
3071 {
3072 UINT uiCmdId = (UINT) lpici->lpVerb;
3073 if (uiCmdId != 0)
3074 {
3075 if (uiCmdId >= ID_SHELL_CMD_FIRST && uiCmdId <= ID_SHELL_CMD_LAST)
3076 {
3077 CMINVOKECOMMANDINFO cmici = { 0 };
3078
3079 if (pcm != NULL)
3080 {
3081 /* Setup and invoke the shell command */
3082 cmici.cbSize = sizeof(cmici);
3083 cmici.hwnd = hWndOwner;
3084 cmici.lpVerb = (LPCSTR) MAKEINTRESOURCEW(uiCmdId - ID_SHELL_CMD_FIRST);
3085 cmici.nShow = SW_NORMAL;
3086
3087 pcm->InvokeCommand(&cmici);
3088 }
3089 }
3090 else
3091 {
3092 TrayWnd->ExecContextMenuCmd(uiCmdId);
3093 }
3094 }
3095
3096 return S_OK;
3097 }
3098
3099 virtual HRESULT STDMETHODCALLTYPE
3100 GetCommandString(UINT_PTR idCmd,
3101 UINT uType,
3102 UINT *pwReserved,
3103 LPSTR pszName,
3104 UINT cchMax)
3105 {
3106 return E_NOTIMPL;
3107 }
3108
3109 CTrayWindowCtxMenu()
3110 {
3111 }
3112
3113 virtual ~CTrayWindowCtxMenu()
3114 {
3115 }
3116
3117 BEGIN_COM_MAP(CTrayWindowCtxMenu)
3118 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3119 END_COM_MAP()
3120 };
3121
3122 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu)
3123 {
3124 CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>();
3125 mnu->Initialize(TrayWnd, hWndOwner);
3126 *ppCtxMenu = mnu;
3127 return S_OK;
3128 }
3129
3130 HRESULT CreateTrayWindow(ITrayWindow ** ppTray)
3131 {
3132 CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>();
3133 if (Tray == NULL)
3134 return E_OUTOFMEMORY;
3135
3136 Tray->_Init();
3137 Tray->Open();
3138
3139 *ppTray = (ITrayWindow *) Tray;
3140
3141 return S_OK;
3142 }
3143
3144 HRESULT
3145 Tray_OnStartMenuDismissed(ITrayWindow* Tray)
3146 {
3147 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3148 return TrayWindow->RaiseStartButton();
3149 }
3150
3151 VOID TrayProcessMessages(ITrayWindow *Tray)
3152 {
3153 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3154 TrayWindow->TrayProcessMessages();
3155 }
3156
3157 VOID TrayMessageLoop(ITrayWindow *Tray)
3158 {
3159 CTrayWindow * TrayWindow = static_cast<CTrayWindow *>(Tray);
3160 TrayWindow->TrayMessageLoop();
3161 }