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