[EXPLORER-NEW]
[reactos.git] / base / shell / explorer-new / 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,
1349 SmallIcon.cy);
1350
1351 /* Create the bitmap */
1352 hbmp = CreateCompatibleBitmap(hDCScreen,
1353 Size.cx,
1354 Size.cy);
1355 if (hbmp == NULL)
1356 goto Cleanup;
1357
1358 /* Caluclate the button rect */
1359 rcButton.left = 0;
1360 rcButton.top = 0;
1361 rcButton.right = Size.cx;
1362 rcButton.bottom = Size.cy;
1363
1364 /* Draw the button */
1365 hbmpOld = (HBITMAP) SelectObject(hDC, hbmp);
1366
1367 Flags = DC_TEXT | DC_INBUTTON;
1368 if (hIconStart != NULL)
1369 Flags |= DC_ICON;
1370
1371 if (DrawCapTemp != NULL)
1372 {
1373 Ret = DrawCapTemp(NULL,
1374 hDC,
1375 &rcButton,
1376 hStartBtnFont,
1377 hIconStart,
1378 szStartCaption,
1379 Flags);
1380 }
1381
1382 SelectObject(hDC,
1383 hbmpOld);
1384
1385 if (!Ret)
1386 goto Cleanup;
1387
1388 /* We successfully created the bitmap! */
1389 hBitmap = hbmp;
1390 hbmp = NULL;
1391
1392 Cleanup:
1393 if (hDCScreen != NULL)
1394 {
1395 ReleaseDC(NULL,
1396 hDCScreen);
1397 }
1398
1399 if (hbmp != NULL)
1400 DeleteObject(hbmp);
1401
1402 if (hDC != NULL)
1403 DeleteDC(hDC);
1404
1405 return hBitmap;
1406 }
1407
1408 LRESULT OnThemeChanged()
1409 {
1410 if (TaskbarTheme)
1411 CloseThemeData(TaskbarTheme);
1412
1413 if (IsThemeActive())
1414 TaskbarTheme = OpenThemeData(m_hWnd, L"TaskBar");
1415 else
1416 TaskbarTheme = NULL;
1417
1418 if (TaskbarTheme)
1419 {
1420 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, 0);
1421 }
1422 else
1423 {
1424 SetWindowStyle(m_hWnd, WS_THICKFRAME | WS_BORDER, WS_THICKFRAME | WS_BORDER);
1425 }
1426
1427 return TRUE;
1428 }
1429
1430 LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1431 {
1432 return OnThemeChanged();
1433 }
1434
1435 LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1436 {
1437 WCHAR szStartCaption[32];
1438
1439 ((ITrayWindow*)this)->AddRef();
1440
1441 SetWindowTheme(m_hWnd, L"TaskBar", NULL);
1442 OnThemeChanged();
1443
1444 InterlockedIncrement(&TrayWndCount);
1445
1446 if (!LoadString(hExplorerInstance,
1447 IDS_START,
1448 szStartCaption,
1449 sizeof(szStartCaption) / sizeof(szStartCaption[0])))
1450 {
1451 szStartCaption[0] = TEXT('\0');
1452 }
1453
1454 if (hStartBtnFont == NULL || hCaptionFont == NULL)
1455 {
1456 NONCLIENTMETRICS ncm;
1457
1458 /* Get the system fonts, we use the caption font,
1459 always bold, though. */
1460 ncm.cbSize = sizeof(ncm);
1461 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
1462 sizeof(ncm),
1463 &ncm,
1464 FALSE))
1465 {
1466 if (hCaptionFont == NULL)
1467 {
1468 ncm.lfCaptionFont.lfWeight = FW_NORMAL;
1469 hCaptionFont = CreateFontIndirect(&ncm.lfCaptionFont);
1470 }
1471
1472 if (hStartBtnFont == NULL)
1473 {
1474 ncm.lfCaptionFont.lfWeight = FW_BOLD;
1475 hStartBtnFont = CreateFontIndirect(&ncm.lfCaptionFont);
1476 }
1477 }
1478 }
1479
1480 /* Create the Start button */
1481 StartButton.SubclassWindow(CreateWindowEx(
1482 0,
1483 WC_BUTTON,
1484 szStartCaption,
1485 WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS |
1486 BS_PUSHBUTTON | BS_CENTER | BS_VCENTER | BS_BITMAP,
1487 0,
1488 0,
1489 0,
1490 0,
1491 m_hWnd,
1492 (HMENU) IDC_STARTBTN,
1493 hExplorerInstance,
1494 NULL));
1495 if (StartButton.m_hWnd)
1496 {
1497 SetWindowTheme(StartButton.m_hWnd, L"Start", NULL);
1498 StartButton.SendMessage(WM_SETFONT, (WPARAM) hStartBtnFont, FALSE);
1499
1500 if (CreateStartBtnImageList())
1501 {
1502 BUTTON_IMAGELIST bil;
1503
1504 /* Try to set the start button image. this requires the Common
1505 Controls 6.0 to be present (XP and later) */
1506 bil.himl = (HIMAGELIST) himlStartBtn;
1507 bil.margin.left = bil.margin.right = 1;
1508 bil.margin.top = bil.margin.bottom = 1;
1509 bil.uAlign = BUTTON_IMAGELIST_ALIGN_LEFT;
1510
1511 if (!StartButton.SendMessage(BCM_SETIMAGELIST, 0, (LPARAM) &bil))
1512 {
1513 /* Fall back to the deprecated method on older systems that don't
1514 support Common Controls 6.0 */
1515 himlStartBtn->Release();
1516 himlStartBtn = NULL;
1517
1518 goto SetStartBtnImage;
1519 }
1520
1521 /* We're using the image list, remove the BS_BITMAP style and
1522 don't center it horizontally */
1523 SetWindowStyle(StartButton.m_hWnd, BS_BITMAP | BS_RIGHT, 0);
1524
1525 UpdateStartButton(NULL);
1526 }
1527 else
1528 {
1529 HBITMAP hbmStart, hbmOld;
1530
1531 SetStartBtnImage:
1532 hbmStart = CreateStartButtonBitmap();
1533 if (hbmStart != NULL)
1534 {
1535 UpdateStartButton(hbmStart);
1536
1537 hbmOld = (HBITMAP) StartButton.SendMessage(BM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbmStart);
1538
1539 if (hbmOld != NULL)
1540 DeleteObject(hbmOld);
1541 }
1542 }
1543 }
1544
1545 /* Load the saved tray window settings */
1546 RegLoadSettings();
1547
1548 /* Create and initialize the start menu */
1549 hbmStartMenu = LoadBitmap(hExplorerInstance,
1550 MAKEINTRESOURCE(IDB_STARTMENU));
1551 StartMenuPopup = CreateStartMenu(this, &StartMenuBand, hbmStartMenu, 0);
1552
1553 /* Load the tray band site */
1554 if (TrayBandSite != NULL)
1555 {
1556 TrayBandSite.Release();
1557 }
1558
1559 TrayBandSite = CreateTrayBandSite(this, &hwndRebar, &hwndTaskSwitch);
1560 SetWindowTheme(hwndRebar, L"TaskBar", NULL);
1561
1562 /* Create the tray notification window */
1563 hwndTrayNotify = CreateTrayNotifyWnd(this, HideClock);
1564
1565 if (UpdateNonClientMetrics())
1566 {
1567 SetWindowsFont();
1568 }
1569
1570 /* Move the tray window to the right position and resize it if neccessary */
1571 CheckTrayWndPosition();
1572
1573 /* Align all controls on the tray window */
1574 AlignControls(
1575 NULL);
1576
1577 InitShellServices(&(hdpaShellServices));
1578
1579 if (AutoHide)
1580 {
1581 AutoHideState = AUTOHIDE_HIDING;
1582 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
1583 }
1584 return TRUE;
1585 }
1586
1587 HRESULT STDMETHODCALLTYPE Open()
1588 {
1589 RECT rcWnd;
1590
1591 /* Check if there's already a window created and try to show it.
1592 If it was somehow destroyed just create a new tray window. */
1593 if (m_hWnd != NULL && IsWindow())
1594 {
1595 if (!IsWindowVisible(m_hWnd))
1596 {
1597 CheckTrayWndPosition();
1598
1599 ShowWindow(SW_SHOW);
1600 }
1601
1602 return S_OK;
1603 }
1604
1605 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
1606 if (AlwaysOnTop)
1607 dwExStyle |= WS_EX_TOPMOST;
1608
1609 DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
1610 WS_BORDER | WS_THICKFRAME;
1611
1612 ZeroMemory(&rcWnd, sizeof(rcWnd));
1613 if (Position != (DWORD) -1)
1614 rcWnd = rcTrayWnd[Position];
1615
1616 if (!Create(NULL, rcWnd, NULL, dwStyle, dwExStyle))
1617 return E_FAIL;
1618
1619 return S_OK;
1620 }
1621
1622 HRESULT STDMETHODCALLTYPE Close()
1623 {
1624 if (m_hWnd != NULL)
1625 {
1626 SendMessage(m_hWnd,
1627 WM_APP_TRAYDESTROY,
1628 0,
1629 0);
1630 }
1631
1632 return S_OK;
1633 }
1634
1635 HWND STDMETHODCALLTYPE GetHWND()
1636 {
1637 return m_hWnd;
1638 }
1639
1640 BOOL STDMETHODCALLTYPE IsSpecialHWND(IN HWND hWnd)
1641 {
1642 return (m_hWnd == hWnd ||
1643 (hWndDesktop != NULL && m_hWnd == hWndDesktop));
1644 }
1645
1646 BOOL STDMETHODCALLTYPE
1647 IsHorizontal()
1648 {
1649 return IsPosHorizontal();
1650 }
1651
1652 HFONT STDMETHODCALLTYPE GetCaptionFonts(OUT HFONT *phBoldCaption OPTIONAL)
1653 {
1654 if (phBoldCaption != NULL)
1655 *phBoldCaption = hStartBtnFont;
1656
1657 return hCaptionFont;
1658 }
1659
1660 DWORD WINAPI TrayPropertiesThread()
1661 {
1662 HWND hwnd;
1663 RECT posRect;
1664
1665 GetWindowRect(StartButton.m_hWnd, &posRect);
1666 hwnd = CreateWindowEx(0,
1667 WC_STATIC,
1668 NULL,
1669 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
1670 posRect.left,
1671 posRect.top,
1672 posRect.right - posRect.left,
1673 posRect.bottom - posRect.top,
1674 NULL,
1675 NULL,
1676 NULL,
1677 NULL);
1678
1679 hwndTrayPropertiesOwner = hwnd;
1680
1681 DisplayTrayProperties(hwnd);
1682
1683 hwndTrayPropertiesOwner = NULL;
1684 DestroyWindow();
1685
1686 return 0;
1687 }
1688
1689 static DWORD WINAPI s_TrayPropertiesThread(IN OUT PVOID pParam)
1690 {
1691 CTrayWindow *This = (CTrayWindow*) pParam;
1692
1693 return This->TrayPropertiesThread();
1694 }
1695
1696 HWND STDMETHODCALLTYPE DisplayProperties()
1697 {
1698 HWND hTrayProp;
1699
1700 if (hwndTrayPropertiesOwner)
1701 {
1702 hTrayProp = GetLastActivePopup(hwndTrayPropertiesOwner);
1703 if (hTrayProp != NULL &&
1704 hTrayProp != hwndTrayPropertiesOwner)
1705 {
1706 SetForegroundWindow(hTrayProp);
1707 return NULL;
1708 }
1709 }
1710
1711 CloseHandle(CreateThread(NULL, 0, s_TrayPropertiesThread, this, 0, NULL));
1712 return NULL;
1713 }
1714
1715 VOID OpenCommonStartMenuDirectory(IN HWND hWndOwner, IN LPCTSTR lpOperation)
1716 {
1717 WCHAR szDir[MAX_PATH];
1718
1719 if (SHGetSpecialFolderPath(hWndOwner,
1720 szDir,
1721 CSIDL_COMMON_STARTMENU,
1722 FALSE))
1723 {
1724 ShellExecute(hWndOwner,
1725 lpOperation,
1726 NULL,
1727 NULL,
1728 szDir,
1729 SW_SHOWNORMAL);
1730 }
1731 }
1732
1733 VOID OpenTaskManager(IN HWND hWndOwner)
1734 {
1735 ShellExecute(hWndOwner,
1736 TEXT("open"),
1737 TEXT("taskmgr.exe"),
1738 NULL,
1739 NULL,
1740 SW_SHOWNORMAL);
1741 }
1742
1743 BOOL STDMETHODCALLTYPE ExecContextMenuCmd(IN UINT uiCmd)
1744 {
1745 BOOL bHandled = TRUE;
1746
1747 switch (uiCmd)
1748 {
1749 case ID_SHELL_CMD_PROPERTIES:
1750 DisplayProperties();
1751 break;
1752
1753 case ID_SHELL_CMD_OPEN_ALL_USERS:
1754 OpenCommonStartMenuDirectory(m_hWnd,
1755 TEXT("open"));
1756 break;
1757
1758 case ID_SHELL_CMD_EXPLORE_ALL_USERS:
1759 OpenCommonStartMenuDirectory(m_hWnd,
1760 TEXT("explore"));
1761 break;
1762
1763 case ID_LOCKTASKBAR:
1764 if (SHRestricted(REST_CLASSICSHELL) == 0)
1765 {
1766 Lock(!Locked);
1767 }
1768 break;
1769
1770 case ID_SHELL_CMD_OPEN_TASKMGR:
1771 OpenTaskManager(m_hWnd);
1772 break;
1773
1774 case ID_SHELL_CMD_UNDO_ACTION:
1775 break;
1776
1777 case ID_SHELL_CMD_SHOW_DESKTOP:
1778 break;
1779
1780 case ID_SHELL_CMD_TILE_WND_H:
1781 TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL);
1782 break;
1783
1784 case ID_SHELL_CMD_TILE_WND_V:
1785 TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL);
1786 break;
1787
1788 case ID_SHELL_CMD_CASCADE_WND:
1789 CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL);
1790 break;
1791
1792 case ID_SHELL_CMD_CUST_NOTIF:
1793 break;
1794
1795 case ID_SHELL_CMD_ADJUST_DAT:
1796 LaunchCPanel(NULL, TEXT("timedate.cpl"));
1797 break;
1798
1799 default:
1800 TRACE("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd);
1801 bHandled = FALSE;
1802 break;
1803 }
1804
1805 return bHandled;
1806 }
1807
1808 BOOL STDMETHODCALLTYPE Lock(IN BOOL bLock)
1809 {
1810 BOOL bPrevLock;
1811
1812 bPrevLock = Locked;
1813 if (Locked != bLock)
1814 {
1815 Locked = bLock;
1816
1817 if (TrayBandSite != NULL)
1818 {
1819 if (!SUCCEEDED(TrayBandSite->Lock(
1820 bLock)))
1821 {
1822 /* Reset?? */
1823 Locked = bPrevLock;
1824 }
1825 }
1826 }
1827
1828 return bPrevLock;
1829 }
1830
1831
1832 LRESULT DrawBackground(HDC hdc)
1833 {
1834 RECT rect;
1835 int partId;
1836
1837 GetClientRect(&rect);
1838
1839 if (TaskbarTheme)
1840 {
1841 GetClientRect(&rect);
1842 switch (Position)
1843 {
1844 case ABE_LEFT:
1845 partId = TBP_BACKGROUNDLEFT;
1846 break;
1847 case ABE_TOP:
1848 partId = TBP_BACKGROUNDTOP;
1849 break;
1850 case ABE_RIGHT:
1851 partId = TBP_BACKGROUNDRIGHT;
1852 break;
1853 case ABE_BOTTOM:
1854 default:
1855 partId = TBP_BACKGROUNDBOTTOM;
1856 break;
1857 }
1858
1859 if (IsThemeBackgroundPartiallyTransparent(TaskbarTheme, partId, 0))
1860 {
1861 DrawThemeParentBackground(m_hWnd, hdc, &rect);
1862 }
1863
1864 DrawThemeBackground(TaskbarTheme, hdc, partId, 0, &rect, 0);
1865 }
1866
1867 return TRUE;
1868 }
1869
1870 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1871 {
1872 HDC hdc = (HDC) wParam;
1873
1874 if (!TaskbarTheme)
1875 {
1876 bHandled = FALSE;
1877 return 0;
1878 }
1879
1880 return DrawBackground(hdc);
1881 }
1882
1883 int DrawSizer(IN HRGN hRgn)
1884 {
1885 HDC hdc;
1886 RECT rect;
1887 int backoundPart;
1888
1889 GetWindowRect(m_hWnd, &rect);
1890 OffsetRect(&rect, -rect.left, -rect.top);
1891
1892 hdc = GetDCEx(m_hWnd, hRgn, DCX_WINDOW | DCX_INTERSECTRGN | DCX_PARENTCLIP);
1893
1894 switch (Position)
1895 {
1896 case ABE_LEFT:
1897 backoundPart = TBP_SIZINGBARLEFT;
1898 rect.left = rect.right - GetSystemMetrics(SM_CXSIZEFRAME);
1899 break;
1900 case ABE_TOP:
1901 backoundPart = TBP_SIZINGBARTOP;
1902 rect.top = rect.bottom - GetSystemMetrics(SM_CYSIZEFRAME);
1903 break;
1904 case ABE_RIGHT:
1905 backoundPart = TBP_SIZINGBARRIGHT;
1906 rect.right = rect.left + GetSystemMetrics(SM_CXSIZEFRAME);
1907 break;
1908 case ABE_BOTTOM:
1909 default:
1910 backoundPart = TBP_SIZINGBARBOTTOM;
1911 rect.bottom = rect.top + GetSystemMetrics(SM_CYSIZEFRAME);
1912 break;
1913 }
1914
1915 DrawThemeBackground(TaskbarTheme, hdc, backoundPart, 0, &rect, 0);
1916
1917 ReleaseDC(m_hWnd, hdc);
1918 return 0;
1919 }
1920
1921 DWORD WINAPI RunFileDlgThread()
1922 {
1923 HINSTANCE hShell32;
1924 RUNFILEDLG RunFileDlg;
1925 HWND hwnd;
1926 RECT posRect;
1927
1928 GetWindowRect(StartButton.m_hWnd, &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 hwndRunFileDlgOwner = hwnd;
1944
1945 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
1946 RunFileDlg = (RUNFILEDLG) GetProcAddress(hShell32, (LPCSTR) 61);
1947
1948 RunFileDlg(hwnd, NULL, NULL, NULL, NULL, RFF_CALCDIRECTORY);
1949
1950 hwndRunFileDlgOwner = NULL;
1951 ::DestroyWindow(hwnd);
1952
1953 return 0;
1954 }
1955
1956 static DWORD WINAPI s_RunFileDlgThread(IN OUT PVOID pParam)
1957 {
1958 CTrayWindow * This = (CTrayWindow*) pParam;
1959 return This->RunFileDlgThread();
1960 }
1961
1962 void DisplayRunFileDlg()
1963 {
1964 HWND hRunDlg;
1965 if (hwndRunFileDlgOwner)
1966 {
1967 hRunDlg = GetLastActivePopup(hwndRunFileDlgOwner);
1968 if (hRunDlg != NULL &&
1969 hRunDlg != hwndRunFileDlgOwner)
1970 {
1971 SetForegroundWindow(hRunDlg);
1972 return;
1973 }
1974 }
1975
1976 CloseHandle(CreateThread(NULL, 0, s_RunFileDlgThread, this, 0, NULL));
1977 }
1978
1979 void PopupStartMenu()
1980 {
1981 if (StartMenuPopup != NULL)
1982 {
1983 POINTL pt;
1984 RECTL rcExclude;
1985 DWORD dwFlags = 0;
1986
1987 if (GetWindowRect(StartButton.m_hWnd, (RECT*) &rcExclude))
1988 {
1989 switch (Position)
1990 {
1991 case ABE_BOTTOM:
1992 pt.x = rcExclude.left;
1993 pt.y = rcExclude.top;
1994 dwFlags |= MPPF_BOTTOM;
1995 break;
1996 case ABE_TOP:
1997 case ABE_LEFT:
1998 pt.x = rcExclude.left;
1999 pt.y = rcExclude.bottom;
2000 dwFlags |= MPPF_TOP | MPPF_ALIGN_RIGHT;
2001 break;
2002 case ABE_RIGHT:
2003 pt.x = rcExclude.right;
2004 pt.y = rcExclude.bottom;
2005 dwFlags |= MPPF_TOP | MPPF_ALIGN_LEFT;
2006 break;
2007 }
2008
2009 StartMenuPopup->Popup(
2010 &pt,
2011 &rcExclude,
2012 dwFlags);
2013
2014 StartButton.SendMessageW(BM_SETSTATE, TRUE, 0);
2015 }
2016 }
2017 }
2018
2019 void ProcessMouseTracking()
2020 {
2021 RECT rcCurrent;
2022 POINT pt;
2023 BOOL over;
2024 UINT state = AutoHideState;
2025
2026 GetCursorPos(&pt);
2027 GetWindowRect(m_hWnd, &rcCurrent);
2028 over = PtInRect(&rcCurrent, pt);
2029
2030 if (StartButton.SendMessage( BM_GETSTATE, 0, 0) != BST_UNCHECKED)
2031 {
2032 over = TRUE;
2033 }
2034
2035 if (over)
2036 {
2037 if (state == AUTOHIDE_HIDING)
2038 {
2039 TRACE("AutoHide cancelling hide.\n");
2040 AutoHideState = AUTOHIDE_SHOWING;
2041 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2042 }
2043 else if (state == AUTOHIDE_HIDDEN)
2044 {
2045 TRACE("AutoHide starting show.\n");
2046 AutoHideState = AUTOHIDE_SHOWING;
2047 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_SHOW, NULL);
2048 }
2049 }
2050 else
2051 {
2052 if (state == AUTOHIDE_SHOWING)
2053 {
2054 TRACE("AutoHide cancelling show.\n");
2055 AutoHideState = AUTOHIDE_HIDING;
2056 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2057 }
2058 else if (state == AUTOHIDE_SHOWN)
2059 {
2060 TRACE("AutoHide starting hide.\n");
2061 AutoHideState = AUTOHIDE_HIDING;
2062 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
2063 }
2064
2065 KillTimer(TIMER_ID_MOUSETRACK);
2066 }
2067 }
2068
2069 void ProcessAutoHide()
2070 {
2071 RECT rc = rcTrayWnd[Position];
2072 INT w = TraySize.cx - GetSystemMetrics(SM_CXBORDER) * 2 - 1;
2073 INT h = TraySize.cy - GetSystemMetrics(SM_CYBORDER) * 2 - 1;
2074
2075 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);
2076
2077 switch (AutoHideState)
2078 {
2079 case AUTOHIDE_HIDING:
2080 switch (Position)
2081 {
2082 case ABE_LEFT:
2083 AutoHideOffset.cy = 0;
2084 AutoHideOffset.cx -= AUTOHIDE_SPEED_HIDE;
2085 if (AutoHideOffset.cx < -w)
2086 AutoHideOffset.cx = -w;
2087 break;
2088 case ABE_TOP:
2089 AutoHideOffset.cx = 0;
2090 AutoHideOffset.cy -= AUTOHIDE_SPEED_HIDE;
2091 if (AutoHideOffset.cy < -h)
2092 AutoHideOffset.cy = -h;
2093 break;
2094 case ABE_RIGHT:
2095 AutoHideOffset.cy = 0;
2096 AutoHideOffset.cx += AUTOHIDE_SPEED_HIDE;
2097 if (AutoHideOffset.cx > w)
2098 AutoHideOffset.cx = w;
2099 break;
2100 case ABE_BOTTOM:
2101 AutoHideOffset.cx = 0;
2102 AutoHideOffset.cy += AUTOHIDE_SPEED_HIDE;
2103 if (AutoHideOffset.cy > h)
2104 AutoHideOffset.cy = h;
2105 break;
2106 }
2107
2108 if (AutoHideOffset.cx != w && AutoHideOffset.cy != h)
2109 {
2110 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2111 break;
2112 }
2113
2114 /* fallthrough */
2115 case AUTOHIDE_HIDDEN:
2116
2117 switch (Position)
2118 {
2119 case ABE_LEFT:
2120 AutoHideOffset.cx = -w;
2121 AutoHideOffset.cy = 0;
2122 break;
2123 case ABE_TOP:
2124 AutoHideOffset.cx = 0;
2125 AutoHideOffset.cy = -h;
2126 break;
2127 case ABE_RIGHT:
2128 AutoHideOffset.cx = w;
2129 AutoHideOffset.cy = 0;
2130 break;
2131 case ABE_BOTTOM:
2132 AutoHideOffset.cx = 0;
2133 AutoHideOffset.cy = h;
2134 break;
2135 }
2136
2137 KillTimer(TIMER_ID_AUTOHIDE);
2138 AutoHideState = AUTOHIDE_HIDDEN;
2139 break;
2140
2141 case AUTOHIDE_SHOWING:
2142 if (AutoHideOffset.cx >= AUTOHIDE_SPEED_SHOW)
2143 {
2144 AutoHideOffset.cx -= AUTOHIDE_SPEED_SHOW;
2145 }
2146 else if (AutoHideOffset.cx <= -AUTOHIDE_SPEED_SHOW)
2147 {
2148 AutoHideOffset.cx += AUTOHIDE_SPEED_SHOW;
2149 }
2150 else
2151 {
2152 AutoHideOffset.cx = 0;
2153 }
2154
2155 if (AutoHideOffset.cy >= AUTOHIDE_SPEED_SHOW)
2156 {
2157 AutoHideOffset.cy -= AUTOHIDE_SPEED_SHOW;
2158 }
2159 else if (AutoHideOffset.cy <= -AUTOHIDE_SPEED_SHOW)
2160 {
2161 AutoHideOffset.cy += AUTOHIDE_SPEED_SHOW;
2162 }
2163 else
2164 {
2165 AutoHideOffset.cy = 0;
2166 }
2167
2168 if (AutoHideOffset.cx != 0 || AutoHideOffset.cy != 0)
2169 {
2170 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2171 break;
2172 }
2173
2174 /* fallthrough */
2175 case AUTOHIDE_SHOWN:
2176
2177 KillTimer(TIMER_ID_AUTOHIDE);
2178 AutoHideState = AUTOHIDE_SHOWN;
2179 break;
2180 }
2181
2182 rc.left += AutoHideOffset.cx;
2183 rc.right += AutoHideOffset.cx;
2184 rc.top += AutoHideOffset.cy;
2185 rc.bottom += AutoHideOffset.cy;
2186
2187 TRACE("AutoHide Changing position to (%d, %d, %d, %d) and state=%u.\n", rc.left, rc.top, rc.right, rc.bottom, AutoHideState);
2188 SetWindowPos(NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
2189 }
2190
2191 LRESULT OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2192 {
2193 /* Load the saved tray window settings */
2194 RegLoadSettings();
2195
2196 /* Move the tray window to the right position and resize it if neccessary */
2197 CheckTrayWndPosition();
2198
2199 /* Align all controls on the tray window */
2200 AlignControls(NULL);
2201
2202 return TRUE;
2203 }
2204
2205 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2206 {
2207 if (hwndTrayNotify)
2208 {
2209 TrayNotify_NotifyMsg(wParam, lParam);
2210 }
2211 return TRUE;
2212 }
2213
2214 LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2215 {
2216 if (!TaskbarTheme)
2217 {
2218 bHandled = FALSE;
2219 return 0;
2220 }
2221
2222 return DrawSizer((HRGN) wParam);
2223 }
2224
2225 LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2226 {
2227 SetBkMode((HDC) wParam, TRANSPARENT);
2228 return (LRESULT) GetStockObject(HOLLOW_BRUSH);
2229 }
2230
2231 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2232 {
2233 RECT rcClient;
2234 POINT pt;
2235
2236 if (Locked)
2237 {
2238 /* The user may not be able to resize the tray window.
2239 Pretend like the window is not sizeable when the user
2240 clicks on the border. */
2241 return HTBORDER;
2242 }
2243
2244 SetLastError(ERROR_SUCCESS);
2245 if (GetClientRect(&rcClient) &&
2246 (MapWindowPoints(m_hWnd, NULL, (LPPOINT) &rcClient, 2) != 0 || GetLastError() == ERROR_SUCCESS))
2247 {
2248 pt.x = (SHORT) LOWORD(lParam);
2249 pt.y = (SHORT) HIWORD(lParam);
2250
2251 if (PtInRect(&rcClient,
2252 pt))
2253 {
2254 /* The user is trying to drag the tray window */
2255 return HTCAPTION;
2256 }
2257
2258 /* Depending on the position of the tray window, allow only
2259 changing the border next to the monitor working area */
2260 switch (Position)
2261 {
2262 case ABE_TOP:
2263 if (pt.y > rcClient.bottom)
2264 return HTBOTTOM;
2265 break;
2266 case ABE_LEFT:
2267 if (pt.x > rcClient.right)
2268 return HTRIGHT;
2269 break;
2270 case ABE_RIGHT:
2271 if (pt.x < rcClient.left)
2272 return HTLEFT;
2273 break;
2274 case ABE_BOTTOM:
2275 default:
2276 if (pt.y < rcClient.top)
2277 return HTTOP;
2278 break;
2279 }
2280 }
2281 return HTBORDER;
2282 return TRUE;
2283 }
2284
2285 LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2286 {
2287 POINT ptCursor;
2288 PRECT pRect = (PRECT) lParam;
2289
2290 /* We need to ensure that an application can not accidently
2291 move the tray window (using SetWindowPos). However, we still
2292 need to be able to move the window in case the user wants to
2293 drag the tray window to another position or in case the user
2294 wants to resize the tray window. */
2295 if (!Locked && GetCursorPos(&ptCursor))
2296 {
2297 IsDragging = TRUE;
2298 DraggingPosition = GetDraggingRectFromPt(
2299 ptCursor,
2300 pRect,
2301 &DraggingMonitor);
2302 }
2303 else
2304 {
2305 *pRect = rcTrayWnd[Position];
2306
2307 if (AutoHide)
2308 {
2309 pRect->left += AutoHideOffset.cx;
2310 pRect->right += AutoHideOffset.cx;
2311 pRect->top += AutoHideOffset.cy;
2312 pRect->bottom += AutoHideOffset.cy;
2313 }
2314 }
2315 return TRUE;
2316 }
2317
2318 LRESULT OnSizing(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2319 {
2320 PRECT pRect = (PRECT) lParam;
2321
2322 if (!Locked)
2323 {
2324 CalculateValidSize(Position, pRect);
2325 }
2326 else
2327 {
2328 *pRect = rcTrayWnd[Position];
2329
2330 if (AutoHide)
2331 {
2332 pRect->left += AutoHideOffset.cx;
2333 pRect->right += AutoHideOffset.cx;
2334 pRect->top += AutoHideOffset.cy;
2335 pRect->bottom += AutoHideOffset.cy;
2336 }
2337 }
2338 return TRUE;
2339 }
2340
2341 LRESULT OnWindowPosChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2342 {
2343 ChangingWinPos((LPWINDOWPOS) lParam);
2344 return TRUE;
2345 }
2346
2347 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2348 {
2349 RECT rcClient;
2350 InvalidateRect(NULL, TRUE);
2351 if (wParam == SIZE_RESTORED && lParam == 0)
2352 {
2353 ResizeWorkArea();
2354 /* Clip the tray window on multi monitor systems so the edges can't
2355 overlap into another monitor */
2356 ApplyClipping(TRUE);
2357
2358 if (!GetClientRect(&rcClient))
2359 {
2360 return FALSE;
2361 }
2362 }
2363 else
2364 {
2365 rcClient.left = rcClient.top = 0;
2366 rcClient.right = LOWORD(lParam);
2367 rcClient.bottom = HIWORD(lParam);
2368 }
2369
2370 AlignControls(&rcClient);
2371 return TRUE;
2372 }
2373
2374 LRESULT OnEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2375 {
2376 InSizeMove = TRUE;
2377 IsDragging = FALSE;
2378 if (!Locked)
2379 {
2380 /* Remove the clipping on multi monitor systems while dragging around */
2381 ApplyClipping(FALSE);
2382 }
2383 return TRUE;
2384 }
2385
2386 LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2387 {
2388 InSizeMove = FALSE;
2389 if (!Locked)
2390 {
2391 /* Apply clipping */
2392 PostMessage(m_hWnd, WM_SIZE, SIZE_RESTORED, 0);
2393 }
2394 return TRUE;
2395 }
2396
2397 LRESULT OnSysChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2398 {
2399 switch (wParam)
2400 {
2401 case TEXT(' '):
2402 {
2403 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2404 The tray window needs to handle this specially, since it normally doesn't have
2405 a system menu. */
2406
2407 static const UINT uidDisableItem [] = {
2408 SC_RESTORE,
2409 SC_MOVE,
2410 SC_SIZE,
2411 SC_MAXIMIZE,
2412 SC_MINIMIZE,
2413 };
2414 HMENU hSysMenu;
2415 INT i;
2416 UINT uId;
2417
2418 /* temporarily enable the system menu */
2419 SetWindowStyle(m_hWnd, WS_SYSMENU, WS_SYSMENU);
2420
2421 hSysMenu = GetSystemMenu(m_hWnd, FALSE);
2422 if (hSysMenu != NULL)
2423 {
2424 /* Disable all items that are not relevant */
2425 for (i = 0; i != sizeof(uidDisableItem) / sizeof(uidDisableItem[0]); i++)
2426 {
2427 EnableMenuItem(hSysMenu,
2428 uidDisableItem[i],
2429 MF_BYCOMMAND | MF_GRAYED);
2430 }
2431
2432 EnableMenuItem(hSysMenu,
2433 SC_CLOSE,
2434 MF_BYCOMMAND |
2435 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2436
2437 /* Display the system menu */
2438 uId = TrackMenu(
2439 hSysMenu,
2440 NULL,
2441 StartButton.m_hWnd,
2442 Position != ABE_TOP,
2443 FALSE);
2444 if (uId != 0)
2445 {
2446 SendMessage(m_hWnd,
2447 WM_SYSCOMMAND,
2448 (WPARAM) uId,
2449 0);
2450 }
2451 }
2452
2453 /* revert the system menu window style */
2454 SetWindowStyle(m_hWnd, WS_SYSMENU, 0);
2455 break;
2456 }
2457
2458 default:
2459 bHandled = FALSE;
2460 }
2461 return TRUE;
2462 }
2463
2464 LRESULT OnNcRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2465 {
2466 /* We want the user to be able to get a context menu even on the nonclient
2467 area (including the sizing border)! */
2468 uMsg = WM_CONTEXTMENU;
2469 wParam = (WPARAM) m_hWnd;
2470
2471 return OnContextMenu(uMsg, wParam, lParam, bHandled);
2472 }
2473
2474 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2475 {
2476 LRESULT Ret = FALSE;
2477 POINT pt, *ppt = NULL;
2478 HWND hWndExclude = NULL;
2479
2480 /* Check if the administrator has forbidden access to context menus */
2481 if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2482 return FALSE;
2483
2484 pt.x = (SHORT) LOWORD(lParam);
2485 pt.y = (SHORT) HIWORD(lParam);
2486
2487 if (pt.x != -1 || pt.y != -1)
2488 ppt = &pt;
2489 else
2490 hWndExclude = StartButton.m_hWnd;
2491
2492 if ((HWND) wParam == StartButton.m_hWnd)
2493 {
2494 /* Make sure we can't track the context menu if the start
2495 menu is currently being shown */
2496 if (!(StartButton.SendMessage(BM_GETSTATE, 0, 0) & BST_PUSHED))
2497 {
2498 CComPtr<IContextMenu> ctxMenu;
2499 StartMenuBtnCtxMenuCreator(this, m_hWnd, &ctxMenu);
2500 TrackCtxMenu(ctxMenu, ppt, hWndExclude, Position == ABE_BOTTOM, this);
2501 }
2502 }
2503 else
2504 {
2505 /* See if the context menu should be handled by the task band site */
2506 if (ppt != NULL && TrayBandSite != NULL)
2507 {
2508 HWND hWndAtPt;
2509 POINT ptClient = *ppt;
2510
2511 /* Convert the coordinates to client-coordinates */
2512 MapWindowPoints(NULL, m_hWnd, &ptClient, 1);
2513
2514 hWndAtPt = ChildWindowFromPoint(m_hWnd, ptClient);
2515 if (hWndAtPt != NULL &&
2516 (hWndAtPt == hwndRebar || IsChild(hwndRebar,
2517 hWndAtPt)))
2518 {
2519 /* Check if the user clicked on the task switch window */
2520 ptClient = *ppt;
2521 MapWindowPoints(NULL, hwndRebar, &ptClient, 1);
2522
2523 hWndAtPt = ChildWindowFromPointEx(hwndRebar, ptClient, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2524 if (hWndAtPt == hwndTaskSwitch)
2525 goto HandleTrayContextMenu;
2526
2527 /* Forward the message to the task band site */
2528 TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2529 }
2530 else
2531 goto HandleTrayContextMenu;
2532 }
2533 else
2534 {
2535 HandleTrayContextMenu:
2536 /* Tray the default tray window context menu */
2537 CComPtr<IContextMenu> ctxMenu;
2538 TrayWindowCtxMenuCreator(this, m_hWnd, &ctxMenu);
2539 TrackCtxMenu(ctxMenu, ppt, NULL, FALSE, this);
2540 }
2541 }
2542 return Ret;
2543 }
2544
2545 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2546 {
2547 LRESULT Ret = FALSE;
2548 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2549 the rebar control! But we shouldn't forward messages that the band
2550 site doesn't handle, such as other controls (start button, tray window */
2551
2552 HRESULT hr = E_FAIL;
2553
2554 if (TrayBandSite)
2555 {
2556 hr = TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2557 if (SUCCEEDED(hr))
2558 return Ret;
2559 }
2560
2561 if (TrayBandSite == NULL || FAILED(hr))
2562 {
2563 const NMHDR *nmh = (const NMHDR *) lParam;
2564
2565 if (nmh->hwndFrom == hwndTrayNotify)
2566 {
2567 switch (nmh->code)
2568 {
2569 case NTNWM_REALIGN:
2570 /* Cause all controls to be aligned */
2571 PostMessage(m_hWnd, WM_SIZE, SIZE_RESTORED, 0);
2572 break;
2573 }
2574 }
2575 }
2576 return Ret;
2577 }
2578
2579 LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2580 {
2581 /* We "handle" this message so users can't cause a weird maximize/restore
2582 window animation when double-clicking the tray window! */
2583
2584 /* We should forward mouse messages to child windows here.
2585 Right now, this is only clock double-click */
2586 RECT rcClock;
2587 if (TrayNotify_GetClockRect(&rcClock))
2588 {
2589 POINT ptClick;
2590 ptClick.x = MAKEPOINTS(lParam).x;
2591 ptClick.y = MAKEPOINTS(lParam).y;
2592 if (PtInRect(&rcClock, ptClick))
2593 LaunchCPanel(NULL, TEXT("timedate.cpl"));
2594 }
2595 return TRUE;
2596 }
2597
2598 LRESULT OnAppTrayDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2599 {
2600 DestroyWindow();
2601 return TRUE;
2602 }
2603
2604 LRESULT OnOpenStartMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2605 {
2606 HWND hwndStartMenu;
2607 HRESULT hr = IUnknown_GetWindow((IUnknown*) StartMenuPopup, &hwndStartMenu);
2608 if (FAILED_UNEXPECTEDLY(hr))
2609 return FALSE;
2610
2611 if (IsWindowVisible(hwndStartMenu))
2612 {
2613 StartMenuPopup->OnSelect(MPOS_CANCELLEVEL);
2614 }
2615 else
2616 {
2617 PopupStartMenu();
2618 }
2619
2620 return TRUE;
2621 }
2622
2623 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2624 {
2625 LRESULT Ret = FALSE;
2626
2627 if ((HWND) lParam == StartButton.m_hWnd)
2628 {
2629 PopupStartMenu();
2630 return FALSE;
2631 }
2632
2633 if (TrayBandSite == NULL || FAILED_UNEXPECTEDLY(TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret)))
2634 {
2635 switch (LOWORD(wParam))
2636 {
2637 /* FIXME: Handle these commands as well */
2638 case IDM_TASKBARANDSTARTMENU:
2639
2640 DisplayProperties();
2641 break;
2642
2643 case IDM_SEARCH:
2644 break;
2645
2646 case IDM_HELPANDSUPPORT:
2647 {
2648 /* TODO: Implement properly */
2649
2650 LPCWSTR strSite = L"https://www.reactos.org/";
2651
2652 /* TODO: Make localizable */
2653 LPCWSTR strCaption = L"Sorry";
2654 LPCWSTR strMessage = L"ReactOS could not browse to '%s' (error %d). Please make sure there is a web browser installed.";
2655 WCHAR tmpMessage[512];
2656
2657 /* TODO: Read from the registry */
2658 LPCWSTR strVerb = NULL; /* default */
2659 LPCWSTR strPath = strSite;
2660 LPCWSTR strParams = NULL;
2661
2662 /* The return value is defined as HINSTANCE for backwards compatibility only, the cast is needed */
2663 int result = (int) ShellExecuteW(m_hWnd, strVerb, strPath, strParams, NULL, SW_SHOWNORMAL);
2664 if (result <= 32)
2665 {
2666 StringCchPrintfW(tmpMessage, 512, strMessage, strSite, result);
2667 MessageBoxExW(m_hWnd, tmpMessage, strCaption, MB_OK, 0);
2668 }
2669 break;
2670 }
2671
2672 case IDM_RUN:
2673 {
2674 DisplayRunFileDlg();
2675 break;
2676 }
2677
2678 /* FIXME: Handle these commands as well */
2679 case IDM_SYNCHRONIZE:
2680 case IDM_LOGOFF:
2681 case IDM_DISCONNECT:
2682 case IDM_UNDOCKCOMPUTER:
2683 break;
2684
2685 case IDM_SHUTDOWN:
2686 {
2687 HINSTANCE hShell32;
2688 EXITWINDLG ExitWinDlg;
2689
2690 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
2691 ExitWinDlg = (EXITWINDLG) GetProcAddress(hShell32, (LPCSTR) 60);
2692
2693 ExitWinDlg(m_hWnd);
2694 break;
2695 }
2696 }
2697 }
2698 return Ret;
2699 }
2700
2701 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2702 {
2703 if (AutoHide)
2704 {
2705 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2706 }
2707
2708 return TRUE;
2709 }
2710
2711 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2712 {
2713 if (wParam == TIMER_ID_MOUSETRACK)
2714 {
2715 ProcessMouseTracking();
2716 }
2717 else if (wParam == TIMER_ID_AUTOHIDE)
2718 {
2719 ProcessAutoHide();
2720 }
2721
2722 bHandled = FALSE;
2723 return TRUE;
2724 }
2725
2726 LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
2727 {
2728 #if 0
2729 LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr;
2730
2731 if (!as->fChanged)
2732 return 0;
2733
2734 RECT rc;
2735 ::GetWindowRect(m_hWnd, &rc);
2736
2737 SIZE szWindow = {
2738 rc.right - rc.left,
2739 rc.bottom - rc.top };
2740 SIZE szTarget = {
2741 as->rcTarget.right - as->rcTarget.left,
2742 as->rcTarget.bottom - as->rcTarget.top };
2743 SIZE szActual = {
2744 as->rcActual.right - as->rcActual.left,
2745 as->rcActual.bottom - as->rcActual.top };
2746
2747 SIZE borders = {
2748 szWindow.cx - szTarget.cx,
2749 szWindow.cy - szTarget.cx,
2750 };
2751
2752 switch (Position)
2753 {
2754 case ABE_LEFT:
2755 szWindow.cx = szActual.cx + borders.cx;
2756 break;
2757 case ABE_TOP:
2758 szWindow.cy = szActual.cy + borders.cy;
2759 break;
2760 case ABE_RIGHT:
2761 szWindow.cx = szActual.cx + borders.cx;
2762 rc.left = rc.right - szWindow.cy;
2763 break;
2764 case ABE_BOTTOM:
2765 szWindow.cy = szActual.cy + borders.cy;
2766 rc.top = rc.bottom - szWindow.cy;
2767 break;
2768 }
2769
2770 SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER);
2771 #else
2772 bHandled = FALSE;
2773 #endif
2774 return 0;
2775 }
2776
2777 DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE)
2778
2779 BEGIN_MSG_MAP(CTrayWindow)
2780 if (StartMenuBand != NULL)
2781 {
2782 MSG Msg;
2783 LRESULT lRet;
2784
2785 Msg.hwnd = m_hWnd;
2786 Msg.message = uMsg;
2787 Msg.wParam = wParam;
2788 Msg.lParam = lParam;
2789
2790 if (StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK)
2791 {
2792 return lRet;
2793 }
2794
2795 wParam = Msg.wParam;
2796 lParam = Msg.lParam;
2797 }
2798 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
2799 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P
2800 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2801 MESSAGE_HANDLER(WM_SIZE, OnSize)
2802 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2803 /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/
2804 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
2805 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2806 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2807 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
2808 MESSAGE_HANDLER(WM_TIMER, OnTimer)
2809 MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange)
2810 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2811 MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
2812 MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
2813 MESSAGE_HANDLER(WM_MOVING, OnMoving)
2814 MESSAGE_HANDLER(WM_SIZING, OnSizing)
2815 MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChange)
2816 MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove)
2817 MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
2818 MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar)
2819 MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp)
2820 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
2821 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
2822 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
2823 MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
2824 MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
2825 ALT_MSG_MAP(1)
2826 END_MSG_MAP()
2827
2828 /*****************************************************************************/
2829
2830 VOID TrayProcessMessages()
2831 {
2832 MSG Msg;
2833
2834 /* FIXME: We should keep a reference here... */
2835
2836 while (PeekMessage(&Msg,
2837 NULL,
2838 0,
2839 0,
2840 PM_REMOVE))
2841 {
2842 if (Msg.message == WM_QUIT)
2843 break;
2844
2845 if (StartMenuBand == NULL ||
2846 StartMenuBand->IsMenuMessage(
2847 &Msg) != S_OK)
2848 {
2849 TranslateMessage(&Msg);
2850 DispatchMessage(&Msg);
2851 }
2852 }
2853 }
2854
2855 VOID TrayMessageLoop()
2856 {
2857 MSG Msg;
2858 BOOL Ret;
2859
2860 /* FIXME: We should keep a reference here... */
2861
2862 while (1)
2863 {
2864 Ret = GetMessage(&Msg, NULL, 0, 0);
2865
2866 if (!Ret || Ret == -1)
2867 break;
2868
2869 if (Msg.message == WM_HOTKEY)
2870 {
2871 switch (Msg.wParam)
2872 {
2873 case IDHK_RUN: /* Win+R */
2874 DisplayRunFileDlg();
2875 break;
2876 }
2877 }
2878
2879 if (StartMenuBand == NULL ||
2880 StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2881 {
2882 TranslateMessage(&Msg);
2883 DispatchMessage(&Msg);
2884 }
2885 }
2886 }
2887
2888 /*
2889 * IShellDesktopTray
2890 *
2891 * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()!
2892 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2893 * The reason we implement it is because we have to use SHCreateDesktop() so
2894 * that the shell provides the desktop window and all the features that come
2895 * with it (especially positioning of desktop icons)
2896 */
2897
2898 virtual ULONG STDMETHODCALLTYPE GetState()
2899 {
2900 /* FIXME: Return ABS_ flags? */
2901 TRACE("IShellDesktopTray::GetState() unimplemented!\n");
2902 return 0;
2903 }
2904
2905 virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray)
2906 {
2907 TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
2908 *phWndTray = m_hWnd;
2909 return S_OK;
2910 }
2911
2912 virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop)
2913 {
2914 TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
2915
2916 this->hWndDesktop = hWndDesktop;
2917 return S_OK;
2918 }
2919
2920 virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2)
2921 {
2922 TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
2923 return S_OK;
2924 }
2925
2926 virtual HRESULT RaiseStartButton()
2927 {
2928 StartButton.SendMessageW(BM_SETSTATE, FALSE, 0);
2929 return S_OK;
2930 }
2931
2932 void _Init()
2933 {
2934 Position = (DWORD) -1;
2935 }
2936
2937 DECLARE_NOT_AGGREGATABLE(CTrayWindow)
2938
2939 DECLARE_PROTECT_FINAL_CONSTRUCT()
2940 BEGIN_COM_MAP(CTrayWindow)
2941 /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/
2942 COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray)
2943 END_COM_MAP()
2944 };
2945
2946 class CTrayWindowCtxMenu :
2947 public CComCoClass<CTrayWindowCtxMenu>,
2948 public CComObjectRootEx<CComMultiThreadModelNoCS>,
2949 public IContextMenu
2950 {
2951 HWND hWndOwner;
2952 CComPtr<CTrayWindow> TrayWnd;
2953 CComPtr<IContextMenu> pcm;
2954
2955 public:
2956 HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
2957 {
2958 this->TrayWnd = (CTrayWindow *) pTrayWnd;
2959 this->hWndOwner = hWndOwner;
2960 return S_OK;
2961 }
2962
2963 virtual HRESULT STDMETHODCALLTYPE
2964 QueryContextMenu(HMENU hPopup,
2965 UINT indexMenu,
2966 UINT idCmdFirst,
2967 UINT idCmdLast,
2968 UINT uFlags)
2969 {
2970 HMENU menubase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCE(IDM_TRAYWND));
2971
2972 if (menubase)
2973 {
2974 int count = ::GetMenuItemCount(menubase);
2975
2976 for (int i = 0; i < count; i++)
2977 {
2978 WCHAR label[128];
2979
2980 MENUITEMINFOW mii = { 0 };
2981 mii.cbSize = sizeof(mii);
2982 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS
2983 | MIIM_DATA | MIIM_STRING | MIIM_BITMAP | MIIM_FTYPE;
2984 mii.dwTypeData = label;
2985 mii.cch = _countof(label);
2986 ::GetMenuItemInfoW(menubase, i, TRUE, &mii);
2987
2988 TRACE("Adding item %d label %S type %d\n", mii.wID, mii.dwTypeData, mii.fType);
2989
2990 mii.fType |= MFT_RADIOCHECK;
2991
2992 ::InsertMenuItemW(hPopup, i + 1, TRUE, &mii);
2993 }
2994
2995 ::DestroyMenu(menubase);
2996
2997 if (SHRestricted(REST_CLASSICSHELL) != 0)
2998 {
2999 DeleteMenu(hPopup,
3000 ID_LOCKTASKBAR,
3001 MF_BYCOMMAND);
3002 }
3003
3004 CheckMenuItem(hPopup,
3005 ID_LOCKTASKBAR,
3006 MF_BYCOMMAND | (TrayWnd->Locked ? MF_CHECKED : MF_UNCHECKED));
3007
3008 if (TrayWnd->TrayBandSite != NULL)
3009 {
3010 if (SUCCEEDED(TrayWnd->TrayBandSite->AddContextMenus(
3011 hPopup,
3012 0,
3013 ID_SHELL_CMD_FIRST,
3014 ID_SHELL_CMD_LAST,
3015 CMF_NORMAL,
3016 &pcm)))
3017 {
3018 return S_OK;
3019 }
3020 }
3021
3022 }
3023
3024 return E_FAIL;
3025 }
3026
3027 virtual HRESULT STDMETHODCALLTYPE
3028 InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
3029 {
3030 UINT uiCmdId = (UINT) lpici->lpVerb;
3031 if (uiCmdId != 0)
3032 {
3033 if (uiCmdId >= ID_SHELL_CMD_FIRST && uiCmdId <= ID_SHELL_CMD_LAST)
3034 {
3035 CMINVOKECOMMANDINFO cmici = { 0 };
3036
3037 if (pcm != NULL)
3038 {
3039 /* Setup and invoke the shell command */
3040 cmici.cbSize = sizeof(cmici);
3041 cmici.hwnd = hWndOwner;
3042 cmici.lpVerb = (LPCSTR) MAKEINTRESOURCE(uiCmdId - ID_SHELL_CMD_FIRST);
3043 cmici.nShow = SW_NORMAL;
3044
3045 pcm->InvokeCommand(&cmici);
3046 }
3047 }
3048 else
3049 {
3050 TrayWnd->ExecContextMenuCmd(uiCmdId);
3051 }
3052 }
3053
3054 return S_OK;
3055 }
3056
3057 virtual HRESULT STDMETHODCALLTYPE
3058 GetCommandString(UINT_PTR idCmd,
3059 UINT uType,
3060 UINT *pwReserved,
3061 LPSTR pszName,
3062 UINT cchMax)
3063 {
3064 return E_NOTIMPL;
3065 }
3066
3067 CTrayWindowCtxMenu()
3068 {
3069 }
3070
3071 virtual ~CTrayWindowCtxMenu()
3072 {
3073 }
3074
3075 BEGIN_COM_MAP(CTrayWindowCtxMenu)
3076 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3077 END_COM_MAP()
3078 };
3079
3080 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu)
3081 {
3082 CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>();
3083 mnu->Initialize(TrayWnd, hWndOwner);
3084 *ppCtxMenu = mnu;
3085 return S_OK;
3086 }
3087
3088 CTrayWindow * g_TrayWindow;
3089
3090 HRESULT
3091 Tray_OnStartMenuDismissed()
3092 {
3093 return g_TrayWindow->RaiseStartButton();
3094 }
3095
3096
3097 HRESULT CreateTrayWindow(ITrayWindow ** ppTray)
3098 {
3099 CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>();
3100 if (Tray == NULL)
3101 return E_OUTOFMEMORY;
3102
3103 Tray->_Init();
3104 Tray->Open();
3105 g_TrayWindow = Tray;
3106
3107 *ppTray = (ITrayWindow *) Tray;
3108
3109 return S_OK;
3110 }
3111
3112 VOID TrayProcessMessages(ITrayWindow *)
3113 {
3114 g_TrayWindow->TrayProcessMessages();
3115 }
3116
3117 VOID TrayMessageLoop(ITrayWindow *)
3118 {
3119 g_TrayWindow->TrayMessageLoop();
3120 }