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