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