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