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