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