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