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