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