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