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