[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 #define IDHK_RUN 0x1f4
44 #define IDHK_MINIMIZE_ALL 0x1f5
45 #define IDHK_RESTORE_ALL 0x1f6
46 #define IDHK_HELP 0x1f7
47 #define IDHK_EXPLORE 0x1f8
48 #define IDHK_FIND 0x1f9
49 #define IDHK_FIND_COMPUTER 0x1fa
50 #define IDHK_NEXT_TASK 0x1fb
51 #define IDHK_PREV_TASK 0x1fc
52 #define IDHK_SYS_PROPERTIES 0x1fd
53 #define IDHK_DESKTOP 0x1fe
54 #define IDHK_PAGER 0x1ff
55
56 static LONG TrayWndCount = 0;
57
58 static const WCHAR szTrayWndClass [] = TEXT("Shell_TrayWnd");
59
60 /*
61 * ITrayWindow
62 */
63
64 const GUID IID_IShellDesktopTray = { 0x213e2df9, 0x9a14, 0x4328, { 0x99, 0xb1, 0x69, 0x61, 0xf9, 0x14, 0x3c, 0xe9 } };
65
66 class CTrayWindow :
67 public CComCoClass<CTrayWindow>,
68 public CComObjectRootEx<CComMultiThreadModelNoCS>,
69 public CWindowImpl < CTrayWindow, CWindow, CControlWinTraits >,
70 public ITrayWindow,
71 public IShellDesktopTray
72 {
73 CContainedWindow StartButton;
74
75 HTHEME TaskbarTheme;
76 HWND hWndDesktop;
77
78 IImageList * himlStartBtn;
79 SIZE StartBtnSize;
80 HFONT hStartBtnFont;
81 HFONT hCaptionFont;
82
83 HWND hwndRebar;
84 HWND hwndTaskSwitch;
85 HWND hwndTrayNotify;
86
87 DWORD Position;
88 HMONITOR Monitor;
89 HMONITOR PreviousMonitor;
90 DWORD DraggingPosition;
91 HMONITOR DraggingMonitor;
92
93 RECT rcTrayWnd[4];
94 RECT rcNewPosSize;
95 SIZE TraySize;
96
97 NONCLIENTMETRICS ncm;
98 HFONT hFont;
99
100 CComPtr<IMenuBand> StartMenuBand;
101 CComPtr<IMenuPopup> StartMenuPopup;
102 HBITMAP hbmStartMenu;
103
104 HWND hwndTrayPropertiesOwner;
105 HWND hwndRunFileDlgOwner;
106
107 UINT AutoHideState;
108 SIZE AutoHideOffset;
109 TRACKMOUSEEVENT MouseTrackingInfo;
110
111 HDPA hdpaShellServices;
112
113 public:
114 CComPtr<ITrayBandSite> TrayBandSite;
115 union
116 {
117 DWORD Flags;
118 struct
119 {
120 DWORD AutoHide : 1;
121 DWORD AlwaysOnTop : 1;
122 DWORD SmSmallIcons : 1;
123 DWORD HideClock : 1;
124 DWORD Locked : 1;
125
126 /* UI Status */
127 DWORD InSizeMove : 1;
128 DWORD IsDragging : 1;
129 DWORD NewPosSize : 1;
130 };
131 };
132
133 public:
134 CTrayWindow() :
135 StartButton(this, 1),
136 TaskbarTheme(NULL),
137 hWndDesktop(NULL),
138 himlStartBtn(NULL),
139 hStartBtnFont(NULL),
140 hCaptionFont(NULL),
141 hwndRebar(NULL),
142 hwndTaskSwitch(NULL),
143 hwndTrayNotify(NULL),
144 Position(0),
145 Monitor(NULL),
146 PreviousMonitor(NULL),
147 DraggingPosition(0),
148 DraggingMonitor(NULL),
149 hFont(NULL),
150 hbmStartMenu(NULL),
151 hwndTrayPropertiesOwner(NULL),
152 hwndRunFileDlgOwner(NULL),
153 AutoHideState(NULL),
154 hdpaShellServices(NULL),
155 Flags(0)
156 {
157 ZeroMemory(&StartBtnSize, sizeof(StartBtnSize));
158 ZeroMemory(&rcTrayWnd, sizeof(rcTrayWnd));
159 ZeroMemory(&rcNewPosSize, sizeof(rcNewPosSize));
160 ZeroMemory(&TraySize, sizeof(TraySize));
161 ZeroMemory(&ncm, sizeof(ncm));
162 ZeroMemory(&AutoHideOffset, sizeof(AutoHideOffset));
163 ZeroMemory(&MouseTrackingInfo, sizeof(MouseTrackingInfo));
164 }
165
166 virtual ~CTrayWindow()
167 {
168 (void) InterlockedExchangePointer((PVOID*) &m_hWnd, NULL);
169
170
171 if (hdpaShellServices != NULL)
172 {
173 ShutdownShellServices(hdpaShellServices);
174 hdpaShellServices = NULL;
175 }
176
177 if (himlStartBtn != NULL)
178 {
179 himlStartBtn->Release();
180 himlStartBtn = NULL;
181 }
182
183 if (hCaptionFont != NULL)
184 {
185 DeleteObject(hCaptionFont);
186 hCaptionFont = NULL;
187 }
188
189 if (hStartBtnFont != NULL)
190 {
191 DeleteObject(hStartBtnFont);
192 hStartBtnFont = NULL;
193 }
194
195 if (hFont != NULL)
196 {
197 DeleteObject(hFont);
198 hFont = NULL;
199 }
200
201 if (hbmStartMenu != NULL)
202 {
203 DeleteObject(hbmStartMenu);
204 hbmStartMenu = NULL;
205 }
206
207 if (TaskbarTheme)
208 {
209 CloseThemeData(TaskbarTheme);
210 TaskbarTheme = NULL;
211 }
212
213 if (InterlockedDecrement(&TrayWndCount) == 0)
214 PostQuitMessage(0);
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
1581 RegisterHotKey(m_hWnd, IDHK_RUN, MOD_WIN, 'R');
1582 RegisterHotKey(m_hWnd, IDHK_MINIMIZE_ALL, MOD_WIN, 'M');
1583 RegisterHotKey(m_hWnd, IDHK_RESTORE_ALL, MOD_WIN|MOD_SHIFT, 'M');
1584 RegisterHotKey(m_hWnd, IDHK_HELP, MOD_WIN, VK_F1);
1585 RegisterHotKey(m_hWnd, IDHK_EXPLORE, MOD_WIN, 'E');
1586 RegisterHotKey(m_hWnd, IDHK_FIND, MOD_WIN, 'F');
1587 RegisterHotKey(m_hWnd, IDHK_FIND_COMPUTER, MOD_WIN|MOD_CONTROL, 'F');
1588 RegisterHotKey(m_hWnd, IDHK_NEXT_TASK, MOD_WIN, VK_TAB);
1589 RegisterHotKey(m_hWnd, IDHK_PREV_TASK, MOD_WIN|MOD_SHIFT, VK_TAB);
1590 RegisterHotKey(m_hWnd, IDHK_SYS_PROPERTIES, MOD_WIN, VK_PAUSE);
1591 RegisterHotKey(m_hWnd, IDHK_DESKTOP, MOD_WIN, 'D');
1592 RegisterHotKey(m_hWnd, IDHK_PAGER, MOD_WIN, 'B');
1593
1594 return TRUE;
1595 }
1596
1597 HRESULT STDMETHODCALLTYPE Open()
1598 {
1599 RECT rcWnd;
1600
1601 /* Check if there's already a window created and try to show it.
1602 If it was somehow destroyed just create a new tray window. */
1603 if (m_hWnd != NULL && IsWindow())
1604 {
1605 if (!IsWindowVisible(m_hWnd))
1606 {
1607 CheckTrayWndPosition();
1608
1609 ShowWindow(SW_SHOW);
1610 }
1611
1612 return S_OK;
1613 }
1614
1615 DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE;
1616 if (AlwaysOnTop)
1617 dwExStyle |= WS_EX_TOPMOST;
1618
1619 DWORD dwStyle = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
1620 WS_BORDER | WS_THICKFRAME;
1621
1622 ZeroMemory(&rcWnd, sizeof(rcWnd));
1623 if (Position != (DWORD) -1)
1624 rcWnd = rcTrayWnd[Position];
1625
1626 if (!Create(NULL, rcWnd, NULL, dwStyle, dwExStyle))
1627 return E_FAIL;
1628
1629 return S_OK;
1630 }
1631
1632 HRESULT STDMETHODCALLTYPE Close()
1633 {
1634 if (m_hWnd != NULL)
1635 {
1636 SendMessage(m_hWnd,
1637 WM_APP_TRAYDESTROY,
1638 0,
1639 0);
1640 }
1641
1642 return S_OK;
1643 }
1644
1645 HWND STDMETHODCALLTYPE GetHWND()
1646 {
1647 return m_hWnd;
1648 }
1649
1650 BOOL STDMETHODCALLTYPE IsSpecialHWND(IN HWND hWnd)
1651 {
1652 return (m_hWnd == hWnd ||
1653 (hWndDesktop != NULL && m_hWnd == hWndDesktop));
1654 }
1655
1656 BOOL STDMETHODCALLTYPE
1657 IsHorizontal()
1658 {
1659 return IsPosHorizontal();
1660 }
1661
1662 HFONT STDMETHODCALLTYPE GetCaptionFonts(OUT HFONT *phBoldCaption OPTIONAL)
1663 {
1664 if (phBoldCaption != NULL)
1665 *phBoldCaption = hStartBtnFont;
1666
1667 return hCaptionFont;
1668 }
1669
1670 DWORD WINAPI TrayPropertiesThread()
1671 {
1672 HWND hwnd;
1673 RECT posRect;
1674
1675 GetWindowRect(StartButton.m_hWnd, &posRect);
1676 hwnd = CreateWindowEx(0,
1677 WC_STATIC,
1678 NULL,
1679 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
1680 posRect.left,
1681 posRect.top,
1682 posRect.right - posRect.left,
1683 posRect.bottom - posRect.top,
1684 NULL,
1685 NULL,
1686 NULL,
1687 NULL);
1688
1689 hwndTrayPropertiesOwner = hwnd;
1690
1691 DisplayTrayProperties(hwnd);
1692
1693 hwndTrayPropertiesOwner = NULL;
1694 DestroyWindow();
1695
1696 return 0;
1697 }
1698
1699 static DWORD WINAPI s_TrayPropertiesThread(IN OUT PVOID pParam)
1700 {
1701 CTrayWindow *This = (CTrayWindow*) pParam;
1702
1703 return This->TrayPropertiesThread();
1704 }
1705
1706 HWND STDMETHODCALLTYPE DisplayProperties()
1707 {
1708 HWND hTrayProp;
1709
1710 if (hwndTrayPropertiesOwner)
1711 {
1712 hTrayProp = GetLastActivePopup(hwndTrayPropertiesOwner);
1713 if (hTrayProp != NULL &&
1714 hTrayProp != hwndTrayPropertiesOwner)
1715 {
1716 SetForegroundWindow(hTrayProp);
1717 return NULL;
1718 }
1719 }
1720
1721 CloseHandle(CreateThread(NULL, 0, s_TrayPropertiesThread, this, 0, NULL));
1722 return NULL;
1723 }
1724
1725 VOID OpenCommonStartMenuDirectory(IN HWND hWndOwner, IN LPCTSTR lpOperation)
1726 {
1727 WCHAR szDir[MAX_PATH];
1728
1729 if (SHGetSpecialFolderPath(hWndOwner,
1730 szDir,
1731 CSIDL_COMMON_STARTMENU,
1732 FALSE))
1733 {
1734 ShellExecute(hWndOwner,
1735 lpOperation,
1736 NULL,
1737 NULL,
1738 szDir,
1739 SW_SHOWNORMAL);
1740 }
1741 }
1742
1743 VOID OpenTaskManager(IN HWND hWndOwner)
1744 {
1745 ShellExecute(hWndOwner,
1746 TEXT("open"),
1747 TEXT("taskmgr.exe"),
1748 NULL,
1749 NULL,
1750 SW_SHOWNORMAL);
1751 }
1752
1753 BOOL STDMETHODCALLTYPE ExecContextMenuCmd(IN UINT uiCmd)
1754 {
1755 BOOL bHandled = TRUE;
1756
1757 switch (uiCmd)
1758 {
1759 case ID_SHELL_CMD_PROPERTIES:
1760 DisplayProperties();
1761 break;
1762
1763 case ID_SHELL_CMD_OPEN_ALL_USERS:
1764 OpenCommonStartMenuDirectory(m_hWnd,
1765 TEXT("open"));
1766 break;
1767
1768 case ID_SHELL_CMD_EXPLORE_ALL_USERS:
1769 OpenCommonStartMenuDirectory(m_hWnd,
1770 TEXT("explore"));
1771 break;
1772
1773 case ID_LOCKTASKBAR:
1774 if (SHRestricted(REST_CLASSICSHELL) == 0)
1775 {
1776 Lock(!Locked);
1777 }
1778 break;
1779
1780 case ID_SHELL_CMD_OPEN_TASKMGR:
1781 OpenTaskManager(m_hWnd);
1782 break;
1783
1784 case ID_SHELL_CMD_UNDO_ACTION:
1785 break;
1786
1787 case ID_SHELL_CMD_SHOW_DESKTOP:
1788 break;
1789
1790 case ID_SHELL_CMD_TILE_WND_H:
1791 TileWindows(NULL, MDITILE_HORIZONTAL, NULL, 0, NULL);
1792 break;
1793
1794 case ID_SHELL_CMD_TILE_WND_V:
1795 TileWindows(NULL, MDITILE_VERTICAL, NULL, 0, NULL);
1796 break;
1797
1798 case ID_SHELL_CMD_CASCADE_WND:
1799 CascadeWindows(NULL, MDITILE_SKIPDISABLED, NULL, 0, NULL);
1800 break;
1801
1802 case ID_SHELL_CMD_CUST_NOTIF:
1803 break;
1804
1805 case ID_SHELL_CMD_ADJUST_DAT:
1806 //FIXME: Use SHRunControlPanel
1807 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
1808 break;
1809
1810 default:
1811 TRACE("ITrayWindow::ExecContextMenuCmd(%u): Unhandled Command ID!\n", uiCmd);
1812 bHandled = FALSE;
1813 break;
1814 }
1815
1816 return bHandled;
1817 }
1818
1819 BOOL STDMETHODCALLTYPE Lock(IN BOOL bLock)
1820 {
1821 BOOL bPrevLock;
1822
1823 bPrevLock = Locked;
1824 if (Locked != bLock)
1825 {
1826 Locked = bLock;
1827
1828 if (TrayBandSite != NULL)
1829 {
1830 if (!SUCCEEDED(TrayBandSite->Lock(
1831 bLock)))
1832 {
1833 /* Reset?? */
1834 Locked = bPrevLock;
1835 }
1836 }
1837 }
1838
1839 return bPrevLock;
1840 }
1841
1842
1843 LRESULT DrawBackground(HDC hdc)
1844 {
1845 RECT rect;
1846 int partId;
1847
1848 GetClientRect(&rect);
1849
1850 if (TaskbarTheme)
1851 {
1852 GetClientRect(&rect);
1853 switch (Position)
1854 {
1855 case ABE_LEFT:
1856 partId = TBP_BACKGROUNDLEFT;
1857 break;
1858 case ABE_TOP:
1859 partId = TBP_BACKGROUNDTOP;
1860 break;
1861 case ABE_RIGHT:
1862 partId = TBP_BACKGROUNDRIGHT;
1863 break;
1864 case ABE_BOTTOM:
1865 default:
1866 partId = TBP_BACKGROUNDBOTTOM;
1867 break;
1868 }
1869
1870 DrawThemeBackground(TaskbarTheme, hdc, partId, 0, &rect, 0);
1871 }
1872
1873 return TRUE;
1874 }
1875
1876 LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
1877 {
1878 HDC hdc = (HDC) wParam;
1879
1880 if (!TaskbarTheme)
1881 {
1882 bHandled = FALSE;
1883 return 0;
1884 }
1885
1886 return DrawBackground(hdc);
1887 }
1888
1889 int DrawSizer(IN HRGN hRgn)
1890 {
1891 HDC hdc;
1892 RECT rect;
1893 int backoundPart;
1894
1895 GetWindowRect(m_hWnd, &rect);
1896 OffsetRect(&rect, -rect.left, -rect.top);
1897
1898 hdc = GetDCEx(m_hWnd, hRgn, DCX_WINDOW | DCX_INTERSECTRGN | DCX_PARENTCLIP);
1899
1900 switch (Position)
1901 {
1902 case ABE_LEFT:
1903 backoundPart = TBP_SIZINGBARLEFT;
1904 rect.left = rect.right - GetSystemMetrics(SM_CXSIZEFRAME);
1905 break;
1906 case ABE_TOP:
1907 backoundPart = TBP_SIZINGBARTOP;
1908 rect.top = rect.bottom - GetSystemMetrics(SM_CYSIZEFRAME);
1909 break;
1910 case ABE_RIGHT:
1911 backoundPart = TBP_SIZINGBARRIGHT;
1912 rect.right = rect.left + GetSystemMetrics(SM_CXSIZEFRAME);
1913 break;
1914 case ABE_BOTTOM:
1915 default:
1916 backoundPart = TBP_SIZINGBARBOTTOM;
1917 rect.bottom = rect.top + GetSystemMetrics(SM_CYSIZEFRAME);
1918 break;
1919 }
1920
1921 DrawThemeBackground(TaskbarTheme, hdc, backoundPart, 0, &rect, 0);
1922
1923 ReleaseDC(m_hWnd, hdc);
1924 return 0;
1925 }
1926
1927 DWORD WINAPI RunFileDlgThread()
1928 {
1929 HINSTANCE hShell32;
1930 RUNFILEDLG RunFileDlg;
1931 HWND hwnd;
1932 RECT posRect;
1933
1934 GetWindowRect(StartButton.m_hWnd, &posRect);
1935
1936 hwnd = CreateWindowEx(0,
1937 WC_STATIC,
1938 NULL,
1939 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT,
1940 posRect.left,
1941 posRect.top,
1942 posRect.right - posRect.left,
1943 posRect.bottom - posRect.top,
1944 NULL,
1945 NULL,
1946 NULL,
1947 NULL);
1948
1949 hwndRunFileDlgOwner = hwnd;
1950
1951 hShell32 = GetModuleHandle(TEXT("SHELL32.DLL"));
1952 RunFileDlg = (RUNFILEDLG) GetProcAddress(hShell32, (LPCSTR) 61);
1953
1954 RunFileDlg(hwnd, NULL, NULL, NULL, NULL, RFF_CALCDIRECTORY);
1955
1956 hwndRunFileDlgOwner = NULL;
1957 ::DestroyWindow(hwnd);
1958
1959 return 0;
1960 }
1961
1962 static DWORD WINAPI s_RunFileDlgThread(IN OUT PVOID pParam)
1963 {
1964 CTrayWindow * This = (CTrayWindow*) pParam;
1965 return This->RunFileDlgThread();
1966 }
1967
1968 void DisplayRunFileDlg()
1969 {
1970 HWND hRunDlg;
1971 if (hwndRunFileDlgOwner)
1972 {
1973 hRunDlg = GetLastActivePopup(hwndRunFileDlgOwner);
1974 if (hRunDlg != NULL &&
1975 hRunDlg != hwndRunFileDlgOwner)
1976 {
1977 SetForegroundWindow(hRunDlg);
1978 return;
1979 }
1980 }
1981
1982 CloseHandle(CreateThread(NULL, 0, s_RunFileDlgThread, this, 0, NULL));
1983 }
1984
1985 void PopupStartMenu()
1986 {
1987 if (StartMenuPopup != NULL)
1988 {
1989 POINTL pt;
1990 RECTL rcExclude;
1991 DWORD dwFlags = 0;
1992
1993 if (GetWindowRect(StartButton.m_hWnd, (RECT*) &rcExclude))
1994 {
1995 switch (Position)
1996 {
1997 case ABE_BOTTOM:
1998 pt.x = rcExclude.left;
1999 pt.y = rcExclude.top;
2000 dwFlags |= MPPF_BOTTOM;
2001 break;
2002 case ABE_TOP:
2003 case ABE_LEFT:
2004 pt.x = rcExclude.left;
2005 pt.y = rcExclude.bottom;
2006 dwFlags |= MPPF_TOP | MPPF_ALIGN_RIGHT;
2007 break;
2008 case ABE_RIGHT:
2009 pt.x = rcExclude.right;
2010 pt.y = rcExclude.bottom;
2011 dwFlags |= MPPF_TOP | MPPF_ALIGN_LEFT;
2012 break;
2013 }
2014
2015 StartMenuPopup->Popup(
2016 &pt,
2017 &rcExclude,
2018 dwFlags);
2019
2020 StartButton.SendMessageW(BM_SETSTATE, TRUE, 0);
2021 }
2022 }
2023 }
2024
2025 void ProcessMouseTracking()
2026 {
2027 RECT rcCurrent;
2028 POINT pt;
2029 BOOL over;
2030 UINT state = AutoHideState;
2031
2032 GetCursorPos(&pt);
2033 GetWindowRect(m_hWnd, &rcCurrent);
2034 over = PtInRect(&rcCurrent, pt);
2035
2036 if (StartButton.SendMessage( BM_GETSTATE, 0, 0) != BST_UNCHECKED)
2037 {
2038 over = TRUE;
2039 }
2040
2041 if (over)
2042 {
2043 if (state == AUTOHIDE_HIDING)
2044 {
2045 TRACE("AutoHide cancelling hide.\n");
2046 AutoHideState = AUTOHIDE_SHOWING;
2047 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2048 }
2049 else if (state == AUTOHIDE_HIDDEN)
2050 {
2051 TRACE("AutoHide starting show.\n");
2052 AutoHideState = AUTOHIDE_SHOWING;
2053 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_SHOW, NULL);
2054 }
2055 }
2056 else
2057 {
2058 if (state == AUTOHIDE_SHOWING)
2059 {
2060 TRACE("AutoHide cancelling show.\n");
2061 AutoHideState = AUTOHIDE_HIDING;
2062 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2063 }
2064 else if (state == AUTOHIDE_SHOWN)
2065 {
2066 TRACE("AutoHide starting hide.\n");
2067 AutoHideState = AUTOHIDE_HIDING;
2068 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_DELAY_HIDE, NULL);
2069 }
2070
2071 KillTimer(TIMER_ID_MOUSETRACK);
2072 }
2073 }
2074
2075 void ProcessAutoHide()
2076 {
2077 RECT rc = rcTrayWnd[Position];
2078 INT w = TraySize.cx - GetSystemMetrics(SM_CXBORDER) * 2 - 1;
2079 INT h = TraySize.cy - GetSystemMetrics(SM_CYBORDER) * 2 - 1;
2080
2081 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);
2082
2083 switch (AutoHideState)
2084 {
2085 case AUTOHIDE_HIDING:
2086 switch (Position)
2087 {
2088 case ABE_LEFT:
2089 AutoHideOffset.cy = 0;
2090 AutoHideOffset.cx -= AUTOHIDE_SPEED_HIDE;
2091 if (AutoHideOffset.cx < -w)
2092 AutoHideOffset.cx = -w;
2093 break;
2094 case ABE_TOP:
2095 AutoHideOffset.cx = 0;
2096 AutoHideOffset.cy -= AUTOHIDE_SPEED_HIDE;
2097 if (AutoHideOffset.cy < -h)
2098 AutoHideOffset.cy = -h;
2099 break;
2100 case ABE_RIGHT:
2101 AutoHideOffset.cy = 0;
2102 AutoHideOffset.cx += AUTOHIDE_SPEED_HIDE;
2103 if (AutoHideOffset.cx > w)
2104 AutoHideOffset.cx = w;
2105 break;
2106 case ABE_BOTTOM:
2107 AutoHideOffset.cx = 0;
2108 AutoHideOffset.cy += AUTOHIDE_SPEED_HIDE;
2109 if (AutoHideOffset.cy > h)
2110 AutoHideOffset.cy = h;
2111 break;
2112 }
2113
2114 if (AutoHideOffset.cx != w && AutoHideOffset.cy != h)
2115 {
2116 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2117 break;
2118 }
2119
2120 /* fallthrough */
2121 case AUTOHIDE_HIDDEN:
2122
2123 switch (Position)
2124 {
2125 case ABE_LEFT:
2126 AutoHideOffset.cx = -w;
2127 AutoHideOffset.cy = 0;
2128 break;
2129 case ABE_TOP:
2130 AutoHideOffset.cx = 0;
2131 AutoHideOffset.cy = -h;
2132 break;
2133 case ABE_RIGHT:
2134 AutoHideOffset.cx = w;
2135 AutoHideOffset.cy = 0;
2136 break;
2137 case ABE_BOTTOM:
2138 AutoHideOffset.cx = 0;
2139 AutoHideOffset.cy = h;
2140 break;
2141 }
2142
2143 KillTimer(TIMER_ID_AUTOHIDE);
2144 AutoHideState = AUTOHIDE_HIDDEN;
2145 break;
2146
2147 case AUTOHIDE_SHOWING:
2148 if (AutoHideOffset.cx >= AUTOHIDE_SPEED_SHOW)
2149 {
2150 AutoHideOffset.cx -= AUTOHIDE_SPEED_SHOW;
2151 }
2152 else if (AutoHideOffset.cx <= -AUTOHIDE_SPEED_SHOW)
2153 {
2154 AutoHideOffset.cx += AUTOHIDE_SPEED_SHOW;
2155 }
2156 else
2157 {
2158 AutoHideOffset.cx = 0;
2159 }
2160
2161 if (AutoHideOffset.cy >= AUTOHIDE_SPEED_SHOW)
2162 {
2163 AutoHideOffset.cy -= AUTOHIDE_SPEED_SHOW;
2164 }
2165 else if (AutoHideOffset.cy <= -AUTOHIDE_SPEED_SHOW)
2166 {
2167 AutoHideOffset.cy += AUTOHIDE_SPEED_SHOW;
2168 }
2169 else
2170 {
2171 AutoHideOffset.cy = 0;
2172 }
2173
2174 if (AutoHideOffset.cx != 0 || AutoHideOffset.cy != 0)
2175 {
2176 SetTimer(TIMER_ID_AUTOHIDE, AUTOHIDE_INTERVAL_ANIMATING, NULL);
2177 break;
2178 }
2179
2180 /* fallthrough */
2181 case AUTOHIDE_SHOWN:
2182
2183 KillTimer(TIMER_ID_AUTOHIDE);
2184 AutoHideState = AUTOHIDE_SHOWN;
2185 break;
2186 }
2187
2188 rc.left += AutoHideOffset.cx;
2189 rc.right += AutoHideOffset.cx;
2190 rc.top += AutoHideOffset.cy;
2191 rc.bottom += AutoHideOffset.cy;
2192
2193 TRACE("AutoHide Changing position to (%d, %d, %d, %d) and state=%u.\n", rc.left, rc.top, rc.right, rc.bottom, AutoHideState);
2194 SetWindowPos(NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER);
2195 }
2196
2197 LRESULT OnDisplayChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2198 {
2199 /* Load the saved tray window settings */
2200 RegLoadSettings();
2201
2202 /* Move the tray window to the right position and resize it if neccessary */
2203 CheckTrayWndPosition();
2204
2205 /* Align all controls on the tray window */
2206 AlignControls(NULL);
2207
2208 return TRUE;
2209 }
2210
2211 LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2212 {
2213 if (hwndTrayNotify)
2214 {
2215 TrayNotify_NotifyMsg(wParam, lParam);
2216 }
2217 return TRUE;
2218 }
2219
2220 LRESULT OnNcPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2221 {
2222 if (!TaskbarTheme)
2223 {
2224 bHandled = FALSE;
2225 return 0;
2226 }
2227
2228 return DrawSizer((HRGN) wParam);
2229 }
2230
2231 LRESULT OnCtlColorBtn(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2232 {
2233 SetBkMode((HDC) wParam, TRANSPARENT);
2234 return (LRESULT) GetStockObject(HOLLOW_BRUSH);
2235 }
2236
2237 LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2238 {
2239 RECT rcClient;
2240 POINT pt;
2241
2242 if (Locked)
2243 {
2244 /* The user may not be able to resize the tray window.
2245 Pretend like the window is not sizeable when the user
2246 clicks on the border. */
2247 return HTBORDER;
2248 }
2249
2250 SetLastError(ERROR_SUCCESS);
2251 if (GetClientRect(&rcClient) &&
2252 (MapWindowPoints(m_hWnd, NULL, (LPPOINT) &rcClient, 2) != 0 || GetLastError() == ERROR_SUCCESS))
2253 {
2254 pt.x = (SHORT) LOWORD(lParam);
2255 pt.y = (SHORT) HIWORD(lParam);
2256
2257 if (PtInRect(&rcClient,
2258 pt))
2259 {
2260 /* The user is trying to drag the tray window */
2261 return HTCAPTION;
2262 }
2263
2264 /* Depending on the position of the tray window, allow only
2265 changing the border next to the monitor working area */
2266 switch (Position)
2267 {
2268 case ABE_TOP:
2269 if (pt.y > rcClient.bottom)
2270 return HTBOTTOM;
2271 break;
2272 case ABE_LEFT:
2273 if (pt.x > rcClient.right)
2274 return HTRIGHT;
2275 break;
2276 case ABE_RIGHT:
2277 if (pt.x < rcClient.left)
2278 return HTLEFT;
2279 break;
2280 case ABE_BOTTOM:
2281 default:
2282 if (pt.y < rcClient.top)
2283 return HTTOP;
2284 break;
2285 }
2286 }
2287 return HTBORDER;
2288 return TRUE;
2289 }
2290
2291 LRESULT OnMoving(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2292 {
2293 POINT ptCursor;
2294 PRECT pRect = (PRECT) lParam;
2295
2296 /* We need to ensure that an application can not accidently
2297 move the tray window (using SetWindowPos). However, we still
2298 need to be able to move the window in case the user wants to
2299 drag the tray window to another position or in case the user
2300 wants to resize the tray window. */
2301 if (!Locked && GetCursorPos(&ptCursor))
2302 {
2303 IsDragging = TRUE;
2304 DraggingPosition = GetDraggingRectFromPt(
2305 ptCursor,
2306 pRect,
2307 &DraggingMonitor);
2308 }
2309 else
2310 {
2311 *pRect = rcTrayWnd[Position];
2312
2313 if (AutoHide)
2314 {
2315 pRect->left += AutoHideOffset.cx;
2316 pRect->right += AutoHideOffset.cx;
2317 pRect->top += AutoHideOffset.cy;
2318 pRect->bottom += AutoHideOffset.cy;
2319 }
2320 }
2321 return TRUE;
2322 }
2323
2324 LRESULT OnSizing(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2325 {
2326 PRECT pRect = (PRECT) lParam;
2327
2328 if (!Locked)
2329 {
2330 CalculateValidSize(Position, pRect);
2331 }
2332 else
2333 {
2334 *pRect = rcTrayWnd[Position];
2335
2336 if (AutoHide)
2337 {
2338 pRect->left += AutoHideOffset.cx;
2339 pRect->right += AutoHideOffset.cx;
2340 pRect->top += AutoHideOffset.cy;
2341 pRect->bottom += AutoHideOffset.cy;
2342 }
2343 }
2344 return TRUE;
2345 }
2346
2347 LRESULT OnWindowPosChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2348 {
2349 ChangingWinPos((LPWINDOWPOS) lParam);
2350 return TRUE;
2351 }
2352
2353 LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2354 {
2355 RECT rcClient;
2356 InvalidateRect(NULL, TRUE);
2357 if (wParam == SIZE_RESTORED && lParam == 0)
2358 {
2359 ResizeWorkArea();
2360 /* Clip the tray window on multi monitor systems so the edges can't
2361 overlap into another monitor */
2362 ApplyClipping(TRUE);
2363
2364 if (!GetClientRect(&rcClient))
2365 {
2366 return FALSE;
2367 }
2368 }
2369 else
2370 {
2371 rcClient.left = rcClient.top = 0;
2372 rcClient.right = LOWORD(lParam);
2373 rcClient.bottom = HIWORD(lParam);
2374 }
2375
2376 AlignControls(&rcClient);
2377 return TRUE;
2378 }
2379
2380 LRESULT OnEnterSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2381 {
2382 InSizeMove = TRUE;
2383 IsDragging = FALSE;
2384 if (!Locked)
2385 {
2386 /* Remove the clipping on multi monitor systems while dragging around */
2387 ApplyClipping(FALSE);
2388 }
2389 return TRUE;
2390 }
2391
2392 LRESULT OnExitSizeMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2393 {
2394 InSizeMove = FALSE;
2395 if (!Locked)
2396 {
2397 /* Apply clipping */
2398 PostMessage(m_hWnd, WM_SIZE, SIZE_RESTORED, 0);
2399 }
2400 return TRUE;
2401 }
2402
2403 LRESULT OnSysChar(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2404 {
2405 switch (wParam)
2406 {
2407 case TEXT(' '):
2408 {
2409 /* The user pressed Alt+Space, this usually brings up the system menu of a window.
2410 The tray window needs to handle this specially, since it normally doesn't have
2411 a system menu. */
2412
2413 static const UINT uidDisableItem [] = {
2414 SC_RESTORE,
2415 SC_MOVE,
2416 SC_SIZE,
2417 SC_MAXIMIZE,
2418 SC_MINIMIZE,
2419 };
2420 HMENU hSysMenu;
2421 INT i;
2422 UINT uId;
2423
2424 /* temporarily enable the system menu */
2425 SetWindowStyle(m_hWnd, WS_SYSMENU, WS_SYSMENU);
2426
2427 hSysMenu = GetSystemMenu(m_hWnd, FALSE);
2428 if (hSysMenu != NULL)
2429 {
2430 /* Disable all items that are not relevant */
2431 for (i = 0; i != sizeof(uidDisableItem) / sizeof(uidDisableItem[0]); i++)
2432 {
2433 EnableMenuItem(hSysMenu,
2434 uidDisableItem[i],
2435 MF_BYCOMMAND | MF_GRAYED);
2436 }
2437
2438 EnableMenuItem(hSysMenu,
2439 SC_CLOSE,
2440 MF_BYCOMMAND |
2441 (SHRestricted(REST_NOCLOSE) ? MF_GRAYED : MF_ENABLED));
2442
2443 /* Display the system menu */
2444 uId = TrackMenu(
2445 hSysMenu,
2446 NULL,
2447 StartButton.m_hWnd,
2448 Position != ABE_TOP,
2449 FALSE);
2450 if (uId != 0)
2451 {
2452 SendMessage(m_hWnd,
2453 WM_SYSCOMMAND,
2454 (WPARAM) uId,
2455 0);
2456 }
2457 }
2458
2459 /* revert the system menu window style */
2460 SetWindowStyle(m_hWnd, WS_SYSMENU, 0);
2461 break;
2462 }
2463
2464 default:
2465 bHandled = FALSE;
2466 }
2467 return TRUE;
2468 }
2469
2470 LRESULT OnNcRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2471 {
2472 /* We want the user to be able to get a context menu even on the nonclient
2473 area (including the sizing border)! */
2474 uMsg = WM_CONTEXTMENU;
2475 wParam = (WPARAM) m_hWnd;
2476
2477 return OnContextMenu(uMsg, wParam, lParam, bHandled);
2478 }
2479
2480 LRESULT OnContextMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2481 {
2482 LRESULT Ret = FALSE;
2483 POINT pt, *ppt = NULL;
2484 HWND hWndExclude = NULL;
2485
2486 /* Check if the administrator has forbidden access to context menus */
2487 if (SHRestricted(REST_NOTRAYCONTEXTMENU))
2488 return FALSE;
2489
2490 pt.x = (SHORT) LOWORD(lParam);
2491 pt.y = (SHORT) HIWORD(lParam);
2492
2493 if (pt.x != -1 || pt.y != -1)
2494 ppt = &pt;
2495 else
2496 hWndExclude = StartButton.m_hWnd;
2497
2498 if ((HWND) wParam == StartButton.m_hWnd)
2499 {
2500 /* Make sure we can't track the context menu if the start
2501 menu is currently being shown */
2502 if (!(StartButton.SendMessage(BM_GETSTATE, 0, 0) & BST_PUSHED))
2503 {
2504 CComPtr<IContextMenu> ctxMenu;
2505 StartMenuBtnCtxMenuCreator(this, m_hWnd, &ctxMenu);
2506 TrackCtxMenu(ctxMenu, ppt, hWndExclude, Position == ABE_BOTTOM, this);
2507 }
2508 }
2509 else
2510 {
2511 /* See if the context menu should be handled by the task band site */
2512 if (ppt != NULL && TrayBandSite != NULL)
2513 {
2514 HWND hWndAtPt;
2515 POINT ptClient = *ppt;
2516
2517 /* Convert the coordinates to client-coordinates */
2518 MapWindowPoints(NULL, m_hWnd, &ptClient, 1);
2519
2520 hWndAtPt = ChildWindowFromPoint(m_hWnd, ptClient);
2521 if (hWndAtPt != NULL &&
2522 (hWndAtPt == hwndRebar || IsChild(hwndRebar,
2523 hWndAtPt)))
2524 {
2525 /* Check if the user clicked on the task switch window */
2526 ptClient = *ppt;
2527 MapWindowPoints(NULL, hwndRebar, &ptClient, 1);
2528
2529 hWndAtPt = ChildWindowFromPointEx(hwndRebar, ptClient, CWP_SKIPINVISIBLE | CWP_SKIPDISABLED);
2530 if (hWndAtPt == hwndTaskSwitch)
2531 goto HandleTrayContextMenu;
2532
2533 /* Forward the message to the task band site */
2534 TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2535 }
2536 else
2537 goto HandleTrayContextMenu;
2538 }
2539 else
2540 {
2541 HandleTrayContextMenu:
2542 /* Tray the default tray window context menu */
2543 CComPtr<IContextMenu> ctxMenu;
2544 TrayWindowCtxMenuCreator(this, m_hWnd, &ctxMenu);
2545 TrackCtxMenu(ctxMenu, ppt, NULL, FALSE, this);
2546 }
2547 }
2548 return Ret;
2549 }
2550
2551 LRESULT OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2552 {
2553 LRESULT Ret = FALSE;
2554 /* FIXME: We can't check with IsChild whether the hwnd is somewhere inside
2555 the rebar control! But we shouldn't forward messages that the band
2556 site doesn't handle, such as other controls (start button, tray window */
2557
2558 HRESULT hr = E_FAIL;
2559
2560 if (TrayBandSite)
2561 {
2562 hr = TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret);
2563 if (SUCCEEDED(hr))
2564 return Ret;
2565 }
2566
2567 if (TrayBandSite == NULL || FAILED(hr))
2568 {
2569 const NMHDR *nmh = (const NMHDR *) lParam;
2570
2571 if (nmh->hwndFrom == hwndTrayNotify)
2572 {
2573 switch (nmh->code)
2574 {
2575 case NTNWM_REALIGN:
2576 /* Cause all controls to be aligned */
2577 PostMessage(m_hWnd, WM_SIZE, SIZE_RESTORED, 0);
2578 break;
2579 }
2580 }
2581 }
2582 return Ret;
2583 }
2584
2585 LRESULT OnNcLButtonDblClick(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2586 {
2587 /* We "handle" this message so users can't cause a weird maximize/restore
2588 window animation when double-clicking the tray window! */
2589
2590 /* We should forward mouse messages to child windows here.
2591 Right now, this is only clock double-click */
2592 RECT rcClock;
2593 if (TrayNotify_GetClockRect(&rcClock))
2594 {
2595 POINT ptClick;
2596 ptClick.x = MAKEPOINTS(lParam).x;
2597 ptClick.y = MAKEPOINTS(lParam).y;
2598 if (PtInRect(&rcClock, ptClick))
2599 {
2600 //FIXME: use SHRunControlPanel
2601 ShellExecuteW(m_hWnd, NULL, L"timedate.cpl", NULL, NULL, SW_NORMAL);
2602 }
2603 }
2604 return TRUE;
2605 }
2606
2607 LRESULT OnAppTrayDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2608 {
2609 DestroyWindow();
2610 return TRUE;
2611 }
2612
2613 LRESULT OnOpenStartMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2614 {
2615 HWND hwndStartMenu;
2616 HRESULT hr = IUnknown_GetWindow((IUnknown*) StartMenuPopup, &hwndStartMenu);
2617 if (FAILED_UNEXPECTEDLY(hr))
2618 return FALSE;
2619
2620 if (IsWindowVisible(hwndStartMenu))
2621 {
2622 StartMenuPopup->OnSelect(MPOS_CANCELLEVEL);
2623 }
2624 else
2625 {
2626 PopupStartMenu();
2627 }
2628
2629 return TRUE;
2630 }
2631
2632 LRESULT DoExitWindows()
2633 {
2634 ExitWindowsDialog(m_hWnd);
2635 return 0;
2636 }
2637
2638 LRESULT OnDoExitWindows(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2639 {
2640 /*
2641 * TWM_DOEXITWINDOWS is send by the CDesktopBrowserr to us to
2642 * show the shutdown dialog
2643 */
2644 return DoExitWindows();
2645 }
2646
2647 LRESULT OnSysCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2648 {
2649 if (wParam == SC_CLOSE)
2650 {
2651 return DoExitWindows();
2652 }
2653
2654 bHandled = FALSE;
2655 return TRUE;
2656 }
2657
2658 HRESULT ExecResourceCmd(int id)
2659 {
2660 WCHAR szCommand[256];
2661 WCHAR *pszParameters;
2662
2663 if (!LoadString(hExplorerInstance,
2664 id,
2665 szCommand,
2666 sizeof(szCommand) / sizeof(szCommand[0])))
2667 {
2668 return E_FAIL;
2669 }
2670
2671 pszParameters = wcschr(szCommand, L'>');
2672 if (!pszParameters)
2673 return E_FAIL;
2674
2675 *pszParameters = 0;
2676 pszParameters++;
2677
2678 ShellExecuteW(m_hWnd, NULL, szCommand, pszParameters, NULL, 0);
2679 return S_OK;
2680 }
2681
2682 LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2683 {
2684 switch (wParam)
2685 {
2686 case IDHK_RUN:
2687 DisplayRunFileDlg();
2688 break;
2689 case IDHK_HELP:
2690 ExecResourceCmd(IDS_HELP_COMMAND);
2691 break;
2692 case IDHK_EXPLORE:
2693 ShellExecuteW(0, L"explore", NULL, NULL, NULL, 1);
2694 break;
2695 case IDHK_FIND:
2696 SHFindFiles(NULL, NULL);
2697 break;
2698 case IDHK_FIND_COMPUTER:
2699 SHFindComputer(NULL, NULL);
2700 break;
2701 case IDHK_SYS_PROPERTIES:
2702 //FIXME: Use SHRunControlPanel
2703 ShellExecuteW(m_hWnd, NULL, L"sysdm.cpl", NULL, NULL, SW_NORMAL);
2704 break;
2705 case IDHK_NEXT_TASK:
2706 break;
2707 case IDHK_PREV_TASK:
2708 break;
2709 case IDHK_MINIMIZE_ALL:
2710 break;
2711 case IDHK_RESTORE_ALL:
2712 break;
2713 case IDHK_DESKTOP:
2714 break;
2715 case IDHK_PAGER:
2716 break;
2717 }
2718
2719 return 0;
2720 }
2721
2722 LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2723 {
2724 LRESULT Ret = FALSE;
2725
2726 if ((HWND) lParam == StartButton.m_hWnd)
2727 {
2728 PopupStartMenu();
2729 return FALSE;
2730 }
2731
2732 if (TrayBandSite == NULL || FAILED_UNEXPECTEDLY(TrayBandSite->ProcessMessage(m_hWnd, uMsg, wParam, lParam, &Ret)))
2733 {
2734 switch (LOWORD(wParam))
2735 {
2736 /* FIXME: Handle these commands as well */
2737 case IDM_TASKBARANDSTARTMENU:
2738 DisplayProperties();
2739 break;
2740
2741 case IDM_SEARCH:
2742 SHFindFiles(NULL, NULL);
2743 break;
2744
2745 case IDM_HELPANDSUPPORT:
2746 ExecResourceCmd(IDS_HELP_COMMAND);
2747 break;
2748
2749 case IDM_RUN:
2750 DisplayRunFileDlg();
2751 break;
2752
2753 /* FIXME: Handle these commands as well */
2754 case IDM_SYNCHRONIZE:
2755 case IDM_LOGOFF:
2756 case IDM_DISCONNECT:
2757 case IDM_UNDOCKCOMPUTER:
2758 break;
2759
2760 case IDM_SHUTDOWN:
2761 DoExitWindows();
2762 break;
2763 }
2764 }
2765 return Ret;
2766 }
2767
2768 LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2769 {
2770 if (AutoHide)
2771 {
2772 SetTimer(TIMER_ID_MOUSETRACK, MOUSETRACK_INTERVAL, NULL);
2773 }
2774
2775 return TRUE;
2776 }
2777
2778 LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
2779 {
2780 if (wParam == TIMER_ID_MOUSETRACK)
2781 {
2782 ProcessMouseTracking();
2783 }
2784 else if (wParam == TIMER_ID_AUTOHIDE)
2785 {
2786 ProcessAutoHide();
2787 }
2788
2789 bHandled = FALSE;
2790 return TRUE;
2791 }
2792
2793 LRESULT OnRebarAutoSize(INT code, LPNMHDR nmhdr, BOOL& bHandled)
2794 {
2795 #if 0
2796 LPNMRBAUTOSIZE as = (LPNMRBAUTOSIZE) nmhdr;
2797
2798 if (!as->fChanged)
2799 return 0;
2800
2801 RECT rc;
2802 ::GetWindowRect(m_hWnd, &rc);
2803
2804 SIZE szWindow = {
2805 rc.right - rc.left,
2806 rc.bottom - rc.top };
2807 SIZE szTarget = {
2808 as->rcTarget.right - as->rcTarget.left,
2809 as->rcTarget.bottom - as->rcTarget.top };
2810 SIZE szActual = {
2811 as->rcActual.right - as->rcActual.left,
2812 as->rcActual.bottom - as->rcActual.top };
2813
2814 SIZE borders = {
2815 szWindow.cx - szTarget.cx,
2816 szWindow.cy - szTarget.cx,
2817 };
2818
2819 switch (Position)
2820 {
2821 case ABE_LEFT:
2822 szWindow.cx = szActual.cx + borders.cx;
2823 break;
2824 case ABE_TOP:
2825 szWindow.cy = szActual.cy + borders.cy;
2826 break;
2827 case ABE_RIGHT:
2828 szWindow.cx = szActual.cx + borders.cx;
2829 rc.left = rc.right - szWindow.cy;
2830 break;
2831 case ABE_BOTTOM:
2832 szWindow.cy = szActual.cy + borders.cy;
2833 rc.top = rc.bottom - szWindow.cy;
2834 break;
2835 }
2836
2837 SetWindowPos(NULL, rc.left, rc.top, szWindow.cx, szWindow.cy, SWP_NOACTIVATE | SWP_NOZORDER);
2838 #else
2839 bHandled = FALSE;
2840 #endif
2841 return 0;
2842 }
2843
2844 DECLARE_WND_CLASS_EX(szTrayWndClass, CS_DBLCLKS, COLOR_3DFACE)
2845
2846 BEGIN_MSG_MAP(CTrayWindow)
2847 if (StartMenuBand != NULL)
2848 {
2849 MSG Msg;
2850 LRESULT lRet;
2851
2852 Msg.hwnd = m_hWnd;
2853 Msg.message = uMsg;
2854 Msg.wParam = wParam;
2855 Msg.lParam = lParam;
2856
2857 if (StartMenuBand->TranslateMenuMessage(&Msg, &lRet) == S_OK)
2858 {
2859 return lRet;
2860 }
2861
2862 wParam = Msg.wParam;
2863 lParam = Msg.lParam;
2864 }
2865 MESSAGE_HANDLER(WM_THEMECHANGED, OnThemeChanged)
2866 NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnRebarAutoSize) // Doesn't quite work ;P
2867 MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
2868 MESSAGE_HANDLER(WM_SIZE, OnSize)
2869 MESSAGE_HANDLER(WM_CREATE, OnCreate)
2870 /*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/
2871 MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
2872 MESSAGE_HANDLER(WM_COMMAND, OnCommand)
2873 MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
2874 MESSAGE_HANDLER(WM_NOTIFY, OnNotify)
2875 MESSAGE_HANDLER(WM_CONTEXTMENU, OnContextMenu)
2876 MESSAGE_HANDLER(WM_TIMER, OnTimer)
2877 MESSAGE_HANDLER(WM_DISPLAYCHANGE, OnDisplayChange)
2878 MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
2879 MESSAGE_HANDLER(WM_NCPAINT, OnNcPaint)
2880 MESSAGE_HANDLER(WM_CTLCOLORBTN, OnCtlColorBtn)
2881 MESSAGE_HANDLER(WM_MOVING, OnMoving)
2882 MESSAGE_HANDLER(WM_SIZING, OnSizing)
2883 MESSAGE_HANDLER(WM_WINDOWPOSCHANGING, OnWindowPosChange)
2884 MESSAGE_HANDLER(WM_ENTERSIZEMOVE, OnEnterSizeMove)
2885 MESSAGE_HANDLER(WM_EXITSIZEMOVE, OnExitSizeMove)
2886 MESSAGE_HANDLER(WM_SYSCHAR, OnSysChar)
2887 MESSAGE_HANDLER(WM_NCRBUTTONUP, OnNcRButtonUp)
2888 MESSAGE_HANDLER(WM_NCLBUTTONDBLCLK, OnNcLButtonDblClick)
2889 MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
2890 MESSAGE_HANDLER(WM_NCMOUSEMOVE, OnMouseMove)
2891 MESSAGE_HANDLER(WM_APP_TRAYDESTROY, OnAppTrayDestroy)
2892 MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
2893 MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
2894 MESSAGE_HANDLER(WM_HOTKEY, OnHotkey)
2895 ALT_MSG_MAP(1)
2896 END_MSG_MAP()
2897
2898 /*****************************************************************************/
2899
2900 VOID TrayProcessMessages()
2901 {
2902 MSG Msg;
2903
2904 /* FIXME: We should keep a reference here... */
2905
2906 while (PeekMessage(&Msg,
2907 NULL,
2908 0,
2909 0,
2910 PM_REMOVE))
2911 {
2912 if (Msg.message == WM_QUIT)
2913 break;
2914
2915 if (StartMenuBand == NULL ||
2916 StartMenuBand->IsMenuMessage(
2917 &Msg) != S_OK)
2918 {
2919 TranslateMessage(&Msg);
2920 DispatchMessage(&Msg);
2921 }
2922 }
2923 }
2924
2925 VOID TrayMessageLoop()
2926 {
2927 MSG Msg;
2928 BOOL Ret;
2929
2930 /* FIXME: We should keep a reference here... */
2931
2932 while (1)
2933 {
2934 Ret = GetMessage(&Msg, NULL, 0, 0);
2935
2936 if (!Ret || Ret == -1)
2937 break;
2938
2939 if (StartMenuBand == NULL ||
2940 StartMenuBand->IsMenuMessage(&Msg) != S_OK)
2941 {
2942 TranslateMessage(&Msg);
2943 DispatchMessage(&Msg);
2944 }
2945 }
2946 }
2947
2948 /*
2949 * IShellDesktopTray
2950 *
2951 * NOTE: this is a very windows-specific COM interface used by SHCreateDesktop()!
2952 * These are the calls I observed, it may be wrong/incomplete/buggy!!!
2953 * The reason we implement it is because we have to use SHCreateDesktop() so
2954 * that the shell provides the desktop window and all the features that come
2955 * with it (especially positioning of desktop icons)
2956 */
2957
2958 virtual ULONG STDMETHODCALLTYPE GetState()
2959 {
2960 /* FIXME: Return ABS_ flags? */
2961 TRACE("IShellDesktopTray::GetState() unimplemented!\n");
2962 return 0;
2963 }
2964
2965 virtual HRESULT STDMETHODCALLTYPE GetTrayWindow(OUT HWND *phWndTray)
2966 {
2967 TRACE("IShellDesktopTray::GetTrayWindow(0x%p)\n", phWndTray);
2968 *phWndTray = m_hWnd;
2969 return S_OK;
2970 }
2971
2972 virtual HRESULT STDMETHODCALLTYPE RegisterDesktopWindow(IN HWND hWndDesktop)
2973 {
2974 TRACE("IShellDesktopTray::RegisterDesktopWindow(0x%p)\n", hWndDesktop);
2975
2976 this->hWndDesktop = hWndDesktop;
2977 return S_OK;
2978 }
2979
2980 virtual HRESULT STDMETHODCALLTYPE Unknown(IN DWORD dwUnknown1, IN DWORD dwUnknown2)
2981 {
2982 TRACE("IShellDesktopTray::Unknown(%u,%u) unimplemented!\n", dwUnknown1, dwUnknown2);
2983 return S_OK;
2984 }
2985
2986 virtual HRESULT RaiseStartButton()
2987 {
2988 StartButton.SendMessageW(BM_SETSTATE, FALSE, 0);
2989 return S_OK;
2990 }
2991
2992 void _Init()
2993 {
2994 Position = (DWORD) -1;
2995 }
2996
2997 DECLARE_NOT_AGGREGATABLE(CTrayWindow)
2998
2999 DECLARE_PROTECT_FINAL_CONSTRUCT()
3000 BEGIN_COM_MAP(CTrayWindow)
3001 /*COM_INTERFACE_ENTRY_IID(IID_ITrayWindow, ITrayWindow)*/
3002 COM_INTERFACE_ENTRY_IID(IID_IShellDesktopTray, IShellDesktopTray)
3003 END_COM_MAP()
3004 };
3005
3006 class CTrayWindowCtxMenu :
3007 public CComCoClass<CTrayWindowCtxMenu>,
3008 public CComObjectRootEx<CComMultiThreadModelNoCS>,
3009 public IContextMenu
3010 {
3011 HWND hWndOwner;
3012 CComPtr<CTrayWindow> TrayWnd;
3013 CComPtr<IContextMenu> pcm;
3014
3015 public:
3016 HRESULT Initialize(ITrayWindow * pTrayWnd, IN HWND hWndOwner)
3017 {
3018 this->TrayWnd = (CTrayWindow *) pTrayWnd;
3019 this->hWndOwner = hWndOwner;
3020 return S_OK;
3021 }
3022
3023 virtual HRESULT STDMETHODCALLTYPE
3024 QueryContextMenu(HMENU hPopup,
3025 UINT indexMenu,
3026 UINT idCmdFirst,
3027 UINT idCmdLast,
3028 UINT uFlags)
3029 {
3030 HMENU menubase = LoadPopupMenu(hExplorerInstance, MAKEINTRESOURCE(IDM_TRAYWND));
3031
3032 if (!menubase)
3033 return HRESULT_FROM_WIN32(GetLastError());
3034
3035 int count = ::GetMenuItemCount(menubase);
3036
3037 for (int i = 0; i < count; i++)
3038 {
3039 WCHAR label[128];
3040
3041 MENUITEMINFOW mii = { 0 };
3042 mii.cbSize = sizeof(mii);
3043 mii.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS
3044 | MIIM_DATA | MIIM_STRING | MIIM_BITMAP | MIIM_FTYPE;
3045 mii.dwTypeData = label;
3046 mii.cch = _countof(label);
3047 ::GetMenuItemInfoW(menubase, i, TRUE, &mii);
3048
3049 TRACE("Adding item %d label %S type %d\n", mii.wID, mii.dwTypeData, mii.fType);
3050
3051 mii.fType |= MFT_RADIOCHECK;
3052
3053 ::InsertMenuItemW(hPopup, i + 1, TRUE, &mii);
3054 }
3055
3056 ::DestroyMenu(menubase);
3057
3058 if (SHRestricted(REST_CLASSICSHELL) != 0)
3059 {
3060 DeleteMenu(hPopup,
3061 ID_LOCKTASKBAR,
3062 MF_BYCOMMAND);
3063 }
3064
3065 CheckMenuItem(hPopup,
3066 ID_LOCKTASKBAR,
3067 MF_BYCOMMAND | (TrayWnd->Locked ? MF_CHECKED : MF_UNCHECKED));
3068
3069 if (TrayWnd->TrayBandSite != NULL)
3070 {
3071 if (FAILED(TrayWnd->TrayBandSite->AddContextMenus(
3072 hPopup,
3073 0,
3074 ID_SHELL_CMD_FIRST,
3075 ID_SHELL_CMD_LAST,
3076 CMF_NORMAL,
3077 &pcm)))
3078 {
3079 WARN("AddContextMenus failed.\n");
3080 pcm = NULL;
3081 }
3082 }
3083
3084 return S_OK;
3085 }
3086
3087 virtual HRESULT STDMETHODCALLTYPE
3088 InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
3089 {
3090 UINT uiCmdId = (UINT) lpici->lpVerb;
3091 if (uiCmdId != 0)
3092 {
3093 if (uiCmdId >= ID_SHELL_CMD_FIRST && uiCmdId <= ID_SHELL_CMD_LAST)
3094 {
3095 CMINVOKECOMMANDINFO cmici = { 0 };
3096
3097 if (pcm != NULL)
3098 {
3099 /* Setup and invoke the shell command */
3100 cmici.cbSize = sizeof(cmici);
3101 cmici.hwnd = hWndOwner;
3102 cmici.lpVerb = (LPCSTR) MAKEINTRESOURCE(uiCmdId - ID_SHELL_CMD_FIRST);
3103 cmici.nShow = SW_NORMAL;
3104
3105 pcm->InvokeCommand(&cmici);
3106 }
3107 }
3108 else
3109 {
3110 TrayWnd->ExecContextMenuCmd(uiCmdId);
3111 }
3112 }
3113
3114 return S_OK;
3115 }
3116
3117 virtual HRESULT STDMETHODCALLTYPE
3118 GetCommandString(UINT_PTR idCmd,
3119 UINT uType,
3120 UINT *pwReserved,
3121 LPSTR pszName,
3122 UINT cchMax)
3123 {
3124 return E_NOTIMPL;
3125 }
3126
3127 CTrayWindowCtxMenu()
3128 {
3129 }
3130
3131 virtual ~CTrayWindowCtxMenu()
3132 {
3133 }
3134
3135 BEGIN_COM_MAP(CTrayWindowCtxMenu)
3136 COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
3137 END_COM_MAP()
3138 };
3139
3140 HRESULT TrayWindowCtxMenuCreator(ITrayWindow * TrayWnd, IN HWND hWndOwner, IContextMenu ** ppCtxMenu)
3141 {
3142 CTrayWindowCtxMenu * mnu = new CComObject<CTrayWindowCtxMenu>();
3143 mnu->Initialize(TrayWnd, hWndOwner);
3144 *ppCtxMenu = mnu;
3145 return S_OK;
3146 }
3147
3148 CTrayWindow * g_TrayWindow;
3149
3150 HRESULT
3151 Tray_OnStartMenuDismissed()
3152 {
3153 return g_TrayWindow->RaiseStartButton();
3154 }
3155
3156
3157 HRESULT CreateTrayWindow(ITrayWindow ** ppTray)
3158 {
3159 CComPtr<CTrayWindow> Tray = new CComObject<CTrayWindow>();
3160 if (Tray == NULL)
3161 return E_OUTOFMEMORY;
3162
3163 Tray->_Init();
3164 Tray->Open();
3165 g_TrayWindow = Tray;
3166
3167 *ppTray = (ITrayWindow *) Tray;
3168
3169 return S_OK;
3170 }
3171
3172 VOID TrayProcessMessages(ITrayWindow *)
3173 {
3174 g_TrayWindow->TrayProcessMessages();
3175 }
3176
3177 VOID TrayMessageLoop(ITrayWindow *)
3178 {
3179 g_TrayWindow->TrayMessageLoop();
3180 }