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