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