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