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