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