4 * Copyright 2006 - 2007 Thomas Weidenmueller <w3seek@reactos.org>
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.
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.
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
24 typedef unsigned short USHORT
;
29 static const WCHAR szSysPagerWndClass
[] = TEXT("SysPager");
31 // Data comes from shell32/systray.cpp -> TrayNotifyCDS_Dummy
32 typedef struct _SYS_PAGER_COPY_DATA
36 NOTIFYICONDATA nicon_data
;
37 } SYS_PAGER_COPY_DATA
, *PSYS_PAGER_COPY_DATA
;
39 template<typename TItemData
>
41 public CWindowImpl
< CToolbar
<TItemData
>, CWindow
, CControlWinTraits
>
47 return SendMessage(TB_BUTTONCOUNT
);
50 DWORD
GetButton(int index
, TBBUTTON
* btn
)
52 return SendMessage(TB_GETBUTTON
, index
, (LPARAM
)btn
);
55 DWORD
AddButton(TBBUTTON
* btn
)
57 return SendMessage(TB_ADDBUTTONS
, 1, (LPARAM
) btn
);
60 DWORD
AddButtons(int count
, TBBUTTON
* buttons
)
62 return SendMessage(TB_ADDBUTTONS
, count
, (LPARAM
) btn
);
65 DWORD
InsertButton(int insertAt
, TBBUTTON
* btn
)
67 return SendMessage(TB_INSERTBUTTON
, insertAt
, (LPARAM
) btn
);
70 DWORD
MoveButton(int oldIndex
, int newIndex
)
72 return SendMessage(TB_MOVEBUTTON
, oldIndex
, newIndex
);
75 DWORD
DeleteButton(int index
)
77 return SendMessage(TB_DELETEBUTTON
, index
, 0);
80 DWORD
GetButtonInfo(int cmdId
, TBBUTTONINFO
* info
)
82 return SendMessage(TB_GETBUTTONINFO
, cmdId
, (LPARAM
) info
);
85 DWORD
SetButtonInfo(int cmdId
, TBBUTTONINFO
* info
)
87 return SendMessage(TB_SETBUTTONINFO
, cmdId
, (LPARAM
) info
);
91 DWORD
SetButtonSize(int w
, int h
)
93 return SendMessage(TB_SETBUTTONSIZE
, 0, MAKELONG(w
, h
));
99 return SendMessage(TB_AUTOSIZE
);
103 TItemData
* GetItemData(int index
)
106 GetButton(index
, &btn
);
107 return (TItemData
*) btn
.dwData
;
110 DWORD
SetItemData(int index
, TItemData
* data
)
112 TBBUTTONINFO info
= { 0 };
113 info
.cbSize
= sizeof(info
);
114 info
.dwMask
= TBIF_BYINDEX
| TBIF_LPARAM
;
115 info
.lParam
= (DWORD_PTR
) data
;
116 return SetButtonInfo(index
, &info
);
120 class CNotifyToolbar
:
121 public CToolbar
<NOTIFYICONDATA
>
123 const int ICON_SIZE
= 16;
126 int VisibleButtonCount
;
131 VisibleButtonCount(0)
139 int GetVisibleItemCount()
141 return VisibleButtonCount
;
144 int FindItemByIconData(IN CONST NOTIFYICONDATA
*iconData
, NOTIFYICONDATA
** pdata
)
146 int count
= GetItemCount();
148 for (int i
= 0; i
< count
; i
++)
150 NOTIFYICONDATA
* data
;
152 data
= GetItemData(i
);
154 if (data
->hWnd
== iconData
->hWnd
&&
155 data
->uID
== iconData
->uID
)
166 VOID
AddButton(IN CONST NOTIFYICONDATA
*iconData
)
169 NOTIFYICONDATA
* notifyItem
;
170 WCHAR text
[] = TEXT("");
172 notifyItem
= new NOTIFYICONDATA();
173 ZeroMemory(notifyItem
, sizeof(*notifyItem
));
175 notifyItem
->hWnd
= iconData
->hWnd
;
176 notifyItem
->uID
= iconData
->uID
;
178 tbBtn
.fsState
= TBSTATE_ENABLED
;
179 tbBtn
.fsStyle
= BTNS_NOPREFIX
;
180 tbBtn
.dwData
= (DWORD_PTR
)notifyItem
;
181 tbBtn
.iString
= (INT_PTR
) text
;
182 tbBtn
.idCommand
= GetItemCount();
184 if (iconData
->uFlags
& NIF_MESSAGE
)
186 notifyItem
->uCallbackMessage
= iconData
->uCallbackMessage
;
189 if (iconData
->uFlags
& NIF_ICON
)
191 tbBtn
.iBitmap
= ImageList_AddIcon(SysIcons
, iconData
->hIcon
);
194 if (iconData
->uFlags
& NIF_TIP
)
196 StringCchCopy(notifyItem
->szTip
, _countof(notifyItem
->szTip
), iconData
->szTip
);
199 VisibleButtonCount
++;
200 if (iconData
->uFlags
& NIF_STATE
)
202 notifyItem
->dwState
&= ~iconData
->dwStateMask
;
203 notifyItem
->dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
204 if (notifyItem
->dwState
& NIS_HIDDEN
)
206 tbBtn
.fsState
|= TBSTATE_HIDDEN
;
207 VisibleButtonCount
--;
212 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
214 CToolbar::AddButton(&tbBtn
);
215 SetButtonSize(ICON_SIZE
, ICON_SIZE
);
218 VOID
UpdateButton(IN CONST NOTIFYICONDATA
*iconData
)
220 NOTIFYICONDATA
* notifyItem
;
221 TBBUTTONINFO tbbi
= { 0 };
223 int index
= FindItemByIconData(iconData
, ¬ifyItem
);
230 tbbi
.cbSize
= sizeof(tbbi
);
231 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_STATE
;
232 GetButtonInfo(index
, &tbbi
);
234 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
235 tbbi
.idCommand
= index
;
237 if (iconData
->uFlags
& NIF_MESSAGE
)
239 notifyItem
->uCallbackMessage
= iconData
->uCallbackMessage
;
242 if (iconData
->uFlags
& NIF_ICON
)
244 tbbi
.dwMask
|= TBIF_IMAGE
;
245 tbbi
.iImage
= ImageList_AddIcon(SysIcons
, iconData
->hIcon
);
248 if (iconData
->uFlags
& NIF_TIP
)
250 StringCchCopy(notifyItem
->szTip
, _countof(notifyItem
->szTip
), iconData
->szTip
);
253 if (iconData
->uFlags
& NIF_STATE
)
255 if (iconData
->dwStateMask
& NIS_HIDDEN
&&
256 (notifyItem
->dwState
& NIS_HIDDEN
) != (iconData
->dwState
& NIS_HIDDEN
))
258 tbbi
.dwMask
|= TBIF_STATE
;
259 if (iconData
->dwState
& NIS_HIDDEN
)
261 if ((tbbi
.fsState
& TBSTATE_HIDDEN
) == 0)
263 tbbi
.fsState
|= TBSTATE_HIDDEN
;
264 VisibleButtonCount
--;
269 if ((tbbi
.fsState
& TBSTATE_HIDDEN
) != 0)
271 tbbi
.fsState
&= ~TBSTATE_HIDDEN
;
272 VisibleButtonCount
++;
277 notifyItem
->dwState
&= ~iconData
->dwStateMask
;
278 notifyItem
->dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
281 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
283 SetButtonInfo(index
, &tbbi
);
286 VOID
RemoveButton(IN CONST NOTIFYICONDATA
*iconData
)
288 NOTIFYICONDATA
* notifyItem
;
289 TBBUTTONINFO tbbi
= { 0 };
291 int index
= FindItemByIconData(iconData
, ¬ifyItem
);
299 VOID
GetTooltip(int index
, LPTSTR szTip
, DWORD cchTip
)
301 NOTIFYICONDATA
* notifyItem
;
302 notifyItem
= GetItemData(index
);
306 StringCchCopy(szTip
, cchTip
, notifyItem
->szTip
);
312 VOID
SendMouseEvent(IN WORD wIndex
, IN UINT uMsg
, IN WPARAM wParam
)
314 static LPCWSTR eventNames
[] = {
331 NOTIFYICONDATA
* notifyItem
= GetItemData(wIndex
);
333 if (!::IsWindow(notifyItem
->hWnd
))
336 if (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
)
338 DbgPrint("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n",
339 eventNames
[uMsg
- WM_MOUSEFIRST
], wIndex
,
340 notifyItem
->hWnd
, notifyItem
->uCallbackMessage
, notifyItem
->uID
, uMsg
);
344 GetWindowThreadProcessId(notifyItem
->hWnd
, &pid
);
346 if (pid
== GetCurrentProcessId() ||
347 (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
))
349 PostMessage(notifyItem
->hWnd
,
350 notifyItem
->uCallbackMessage
,
356 SendMessage(notifyItem
->hWnd
,
357 notifyItem
->uCallbackMessage
,
363 LRESULT
OnMouseEvent(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
368 pt
.x
= LOWORD(lParam
);
369 pt
.y
= HIWORD(lParam
);
371 iBtn
= (INT
) SendMessageW(TB_HITTEST
, 0, (LPARAM
) &pt
);
375 SendMouseEvent(iBtn
, uMsg
, wParam
);
383 BEGIN_MSG_MAP(CNotifyToolbar
)
384 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST
, WM_MOUSELAST
, OnMouseEvent
)
387 void Initialize(HWND hWndParent
)
390 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
391 TBSTYLE_FLAT
| TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
| TBSTYLE_TRANSPARENT
|
392 CCS_TOP
| CCS_NORESIZE
| CCS_NOPARENTALIGN
| CCS_NODIVIDER
;
393 DWORD exStyles
= WS_EX_TOOLWINDOW
;
395 HWND hWndToolbar
= CreateWindowEx(exStyles
,
407 if (hWndToolbar
!= NULL
)
409 SubclassWindow(hWndToolbar
);
411 SetWindowTheme(hWndToolbar
, L
"TrayNotify", NULL
);
413 /* Identify the version we're using */
414 SendMessageW(TB_BUTTONSTRUCTSIZE
, sizeof(TBBUTTON
), 0);
416 SysIcons
= ImageList_Create(16, 16, ILC_COLOR32
| ILC_MASK
, 0, 1000);
417 SendMessageW(TB_SETIMAGELIST
, 0, (LPARAM
) SysIcons
);
419 SetButtonSize(ICON_SIZE
, ICON_SIZE
);
425 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
426 public CWindowImpl
< CSysPagerWnd
, CWindow
, CControlWinTraits
>
428 CNotifyToolbar
* Toolbar
;
435 virtual ~CSysPagerWnd() { }
437 LRESULT
DrawBackground(HDC hdc
)
441 GetClientRect(&rect
);
442 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
447 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
449 HDC hdc
= (HDC
) wParam
;
457 return DrawBackground(hdc
);
460 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
462 Toolbar
= new CNotifyToolbar();
463 Toolbar
->Initialize(m_hWnd
);
467 LRESULT
NotifyMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
469 PCOPYDATASTRUCT cpData
= (PCOPYDATASTRUCT
) lParam
;
470 if (cpData
->dwData
== 1)
472 SYS_PAGER_COPY_DATA
* data
;
473 NOTIFYICONDATA
*iconData
;
476 parentHWND
= GetParent();
477 parentHWND
= ::GetParent(parentHWND
);
478 ::GetClientRect(parentHWND
, &windowRect
);
480 data
= (PSYS_PAGER_COPY_DATA
) cpData
->lpData
;
481 iconData
= &data
->nicon_data
;
483 switch (data
->notify_code
)
487 Toolbar
->AddButton(iconData
);
492 Toolbar
->UpdateButton(iconData
);
497 Toolbar
->RemoveButton(iconData
);
501 TRACE("NotifyMessage received with unknown code %d.\n", data
->notify_code
);
504 SendMessage(parentHWND
,
507 MAKELONG(windowRect
.right
- windowRect
.left
,
508 windowRect
.bottom
- windowRect
.top
));
514 void GetSize(IN WPARAM wParam
, IN PSIZE size
)
518 int VisibleButtonCount
= Toolbar
->GetVisibleItemCount();
520 if (wParam
) /* horizontal */
522 rows
= size
->cy
/ 24;
525 size
->cx
= (VisibleButtonCount
+ rows
- 1) / rows
* 24;
529 rows
= size
->cx
/ 24;
532 size
->cy
= (VisibleButtonCount
+ rows
- 1) / rows
* 24;
535 tbm
.cbSize
= sizeof(tbm
);
536 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
537 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
538 tbm
.cxButtonSpacing
= 0;
539 tbm
.cyButtonSpacing
= 0;
541 Toolbar
->SendMessageW(TB_SETMETRICS
, 0, (LPARAM
) &tbm
);
544 LRESULT
OnGetInfoTip(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
546 NMTBGETINFOTIPW
* nmtip
= (NMTBGETINFOTIPW
*) hdr
;
547 Toolbar
->GetTooltip(nmtip
->iItem
, nmtip
->pszText
, nmtip
->cchTextMax
);
551 LRESULT
OnCustomDraw(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
553 NMCUSTOMDRAW
* cdraw
= (NMCUSTOMDRAW
*) hdr
;
554 switch (cdraw
->dwDrawStage
)
557 return CDRF_NOTIFYITEMDRAW
;
559 case CDDS_ITEMPREPAINT
:
560 return TBCDRF_NOBACKGROUND
| TBCDRF_NOEDGES
| TBCDRF_NOOFFSET
| TBCDRF_NOMARK
| TBCDRF_NOETCHEDEFFECT
;
565 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
569 szClient
.cx
= LOWORD(lParam
);
570 szClient
.cy
= HIWORD(lParam
);
572 Ret
= DefWindowProc(uMsg
, wParam
, lParam
);
576 Toolbar
->SetWindowPos(NULL
, 0, 0, szClient
.cx
, szClient
.cy
, SWP_NOZORDER
);
581 Toolbar
->GetClientRect(&rc
);
583 SIZE szBar
= { rc
.right
- rc
.left
, rc
.bottom
- rc
.top
};
585 INT xOff
= (szClient
.cx
- szBar
.cx
) / 2;
586 INT yOff
= (szClient
.cy
- szBar
.cy
) / 2;
588 Toolbar
->SetWindowPos(NULL
, xOff
, yOff
, szBar
.cx
, szBar
.cy
, SWP_NOZORDER
);
593 DECLARE_WND_CLASS_EX(szSysPagerWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
595 BEGIN_MSG_MAP(CTaskSwitchWnd
)
596 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
597 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
598 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
599 NOTIFY_CODE_HANDLER(TBN_GETINFOTIPW
, OnGetInfoTip
)
600 NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW
, OnCustomDraw
)
603 HWND
_Init(IN HWND hWndParent
, IN BOOL bVisible
)
607 /* Create the window. The tray window is going to move it to the correct
608 position and resize it as needed. */
609 dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
611 dwStyle
|= WS_VISIBLE
;
613 Create(hWndParent
, 0, NULL
, dwStyle
);
620 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
630 static const WCHAR szTrayClockWndClass
[] = TEXT("TrayClockWClass");
632 #define ID_TRAYCLOCK_TIMER 0
633 #define ID_TRAYCLOCK_TIMER_INIT 1
640 } ClockWndFormats
[] = {
642 { FALSE
, 0, TEXT("dddd") },
643 { FALSE
, DATE_SHORTDATE
, NULL
}
646 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
648 #define TRAY_CLOCK_WND_SPACING_X 0
649 #define TRAY_CLOCK_WND_SPACING_Y 0
651 class CTrayClockWnd
:
652 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
653 public CWindowImpl
< CTrayClockWnd
, CWindow
, CControlWinTraits
>
659 SYSTEMTIME LocalTime
;
666 DWORD IsTimerEnabled
: 1;
667 DWORD IsInitTimerEnabled
: 1;
668 DWORD LinesMeasured
: 1;
669 DWORD IsHorizontal
: 1;
675 SIZE LineSizes
[CLOCKWND_FORMAT_COUNT
];
676 WCHAR szLines
[CLOCKWND_FORMAT_COUNT
][48];
686 ZeroMemory(&textColor
, sizeof(textColor
));
687 ZeroMemory(&rcText
, sizeof(rcText
));
688 ZeroMemory(&LocalTime
, sizeof(LocalTime
));
689 ZeroMemory(&CurrentSize
, sizeof(CurrentSize
));
690 ZeroMemory(LineSizes
, sizeof(LineSizes
));
691 ZeroMemory(szLines
, sizeof(LineSizes
));
693 virtual ~CTrayClockWnd() { }
695 LRESULT
OnThemeChanged()
701 clockTheme
= OpenThemeData(m_hWnd
, L
"Clock");
705 GetThemeFont(clockTheme
,
712 hFont
= CreateFontIndirectW(&clockFont
);
714 GetThemeColor(clockTheme
,
722 NONCLIENTMETRICS ncm
= { 0 };
723 ncm
.cbSize
= sizeof(ncm
);
724 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, FALSE
);
726 hFont
= CreateFontIndirectW(&ncm
.lfMessageFont
);
728 textColor
= RGB(0, 0, 0);
731 SetFont(hFont
, FALSE
);
733 CloseThemeData(clockTheme
);
738 LRESULT
OnThemeChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
740 return OnThemeChanged();
754 hPrevFont
= (HFONT
) SelectObject(hDC
, hFont
);
756 for (i
= 0; i
!= CLOCKWND_FORMAT_COUNT
&& bRet
; i
++)
758 if (szLines
[i
][0] != TEXT('\0') &&
759 !GetTextExtentPoint(hDC
, szLines
[i
], _tcslen(szLines
[i
]),
768 SelectObject(hDC
, hPrevFont
);
770 ReleaseDC(m_hWnd
, hDC
);
776 /* calculate the line spacing */
777 for (i
= 0, c
= 0; i
!= CLOCKWND_FORMAT_COUNT
; i
++)
779 if (LineSizes
[i
].cx
> 0)
781 LineSpacing
+= LineSizes
[i
].cy
;
788 /* We want a spaceing of 1/2 line */
789 LineSpacing
= (LineSpacing
/ c
) / 2;
799 WORD
GetMinimumSize(IN BOOL Horizontal
, IN OUT PSIZE pSize
)
801 WORD iLinesVisible
= 0;
803 SIZE szMax
= { 0, 0 };
806 LinesMeasured
= MeasureLines();
812 i
!= CLOCKWND_FORMAT_COUNT
;
815 if (LineSizes
[i
].cx
!= 0)
817 if (iLinesVisible
> 0)
821 if (szMax
.cy
+ LineSizes
[i
].cy
+ (LONG
) LineSpacing
>
822 pSize
->cy
- (2 * TRAY_CLOCK_WND_SPACING_Y
))
829 if (LineSizes
[i
].cx
> pSize
->cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
833 /* Add line spacing */
834 szMax
.cy
+= LineSpacing
;
839 /* Increase maximum rectangle */
840 szMax
.cy
+= LineSizes
[i
].cy
;
841 if (LineSizes
[i
].cx
> szMax
.cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
842 szMax
.cx
= LineSizes
[i
].cx
+ (2 * TRAY_CLOCK_WND_SPACING_X
);
846 szMax
.cx
+= 2 * TRAY_CLOCK_WND_SPACING_X
;
847 szMax
.cy
+= 2 * TRAY_CLOCK_WND_SPACING_Y
;
851 return iLinesVisible
;
858 INT BufSize
, iRet
, i
;
861 ZeroMemory(LineSizes
,
864 szPrevCurrent
= CurrentSize
;
867 i
!= CLOCKWND_FORMAT_COUNT
;
870 szLines
[i
][0] = TEXT('\0');
871 BufSize
= sizeof(szLines
[0]) / sizeof(szLines
[0][0]);
873 if (ClockWndFormats
[i
].IsTime
)
875 iRet
= GetTimeFormat(LOCALE_USER_DEFAULT
,
876 AdvancedSettings
.bShowSeconds
? ClockWndFormats
[i
].dwFormatFlags
: TIME_NOSECONDS
,
878 ClockWndFormats
[i
].lpFormat
,
884 iRet
= GetDateFormat(LOCALE_USER_DEFAULT
,
885 ClockWndFormats
[i
].dwFormatFlags
,
887 ClockWndFormats
[i
].lpFormat
,
892 if (iRet
!= 0 && i
== 0)
894 /* Set the window text to the time only */
895 SetWindowText(szLines
[i
]);
899 LinesMeasured
= MeasureLines();
902 GetClientRect(&rcClient
))
906 szWnd
.cx
= rcClient
.right
;
907 szWnd
.cy
= rcClient
.bottom
;
909 VisibleLines
= GetMinimumSize(IsHorizontal
, &szWnd
);
913 if (IsWindowVisible(m_hWnd
))
915 InvalidateRect(NULL
, TRUE
);
917 if (hWndNotify
!= NULL
&&
918 (szPrevCurrent
.cx
!= CurrentSize
.cx
||
919 szPrevCurrent
.cy
!= CurrentSize
.cy
))
923 nmh
.hwndFrom
= m_hWnd
;
924 nmh
.idFrom
= GetWindowLongPtr(m_hWnd
, GWLP_ID
);
925 nmh
.code
= NTNWM_REALIGN
;
927 SendMessage(hWndNotify
,
937 GetLocalTime(&LocalTime
);
941 UINT
CalculateDueTime()
945 /* Calculate the due time */
946 GetLocalTime(&LocalTime
);
947 uiDueTime
= 1000 - (UINT
) LocalTime
.wMilliseconds
;
948 if (AdvancedSettings
.bShowSeconds
)
949 uiDueTime
+= (UINT
) LocalTime
.wSecond
* 100;
951 uiDueTime
+= (59 - (UINT
) LocalTime
.wSecond
) * 1000;
953 if (uiDueTime
< USER_TIMER_MINIMUM
|| uiDueTime
> USER_TIMER_MAXIMUM
)
957 /* Add an artificial delay of 0.05 seconds to make sure the timer
958 doesn't fire too early*/
970 /* Disable all timers */
973 KillTimer(ID_TRAYCLOCK_TIMER
);
974 IsTimerEnabled
= FALSE
;
977 if (IsInitTimerEnabled
)
979 KillTimer(ID_TRAYCLOCK_TIMER_INIT
);
982 uiDueTime
= CalculateDueTime();
984 /* Set the new timer */
985 Ret
= SetTimer(ID_TRAYCLOCK_TIMER_INIT
, uiDueTime
, NULL
) != 0;
986 IsInitTimerEnabled
= Ret
;
988 /* Update the time */
994 VOID
CalibrateTimer()
998 UINT uiWait1
, uiWait2
;
1000 /* Kill the initialization timer */
1001 KillTimer(ID_TRAYCLOCK_TIMER_INIT
);
1002 IsInitTimerEnabled
= FALSE
;
1004 uiDueTime
= CalculateDueTime();
1006 if (AdvancedSettings
.bShowSeconds
)
1008 uiWait1
= 1000 - 200;
1013 uiWait1
= 60 * 1000 - 200;
1014 uiWait2
= 60 * 1000;
1017 if (uiDueTime
> uiWait1
)
1019 /* The update of the clock will be up to 200 ms late, but that's
1020 acceptable. We're going to setup a timer that fires depending
1022 Ret
= SetTimer(ID_TRAYCLOCK_TIMER
, uiWait2
, NULL
) != 0;
1023 IsTimerEnabled
= Ret
;
1025 /* Update the time */
1030 /* Recalibrate the timer and recalculate again when the current
1031 minute/second ends. */
1036 LRESULT
OnDestroy(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1038 /* Disable all timers */
1041 KillTimer(ID_TRAYCLOCK_TIMER
);
1044 if (IsInitTimerEnabled
)
1046 KillTimer(ID_TRAYCLOCK_TIMER_INIT
);
1052 LRESULT
OnPaint(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1056 int iPrevBkMode
, i
, line
;
1059 HDC hDC
= (HDC
) wParam
;
1063 hDC
= BeginPaint(&ps
);
1069 if (LinesMeasured
&&
1070 GetClientRect(&rcClient
))
1072 iPrevBkMode
= SetBkMode(hDC
, TRANSPARENT
);
1074 SetTextColor(hDC
, textColor
);
1076 hPrevFont
= (HFONT
) SelectObject(hDC
, hFont
);
1078 rcClient
.left
= (rcClient
.right
/ 2) - (CurrentSize
.cx
/ 2);
1079 rcClient
.top
= (rcClient
.bottom
/ 2) - (CurrentSize
.cy
/ 2);
1080 rcClient
.right
= rcClient
.left
+ CurrentSize
.cx
;
1081 rcClient
.bottom
= rcClient
.top
+ CurrentSize
.cy
;
1083 for (i
= 0, line
= 0;
1084 i
!= CLOCKWND_FORMAT_COUNT
&& line
< VisibleLines
;
1087 if (LineSizes
[i
].cx
!= 0)
1090 rcClient
.left
+ (CurrentSize
.cx
/ 2) - (LineSizes
[i
].cx
/ 2) +
1091 TRAY_CLOCK_WND_SPACING_X
,
1092 rcClient
.top
+ TRAY_CLOCK_WND_SPACING_Y
,
1094 _tcslen(szLines
[i
]));
1096 rcClient
.top
+= LineSizes
[i
].cy
+ LineSpacing
;
1101 SelectObject(hDC
, hPrevFont
);
1103 SetBkMode(hDC
, iPrevBkMode
);
1114 VOID
SetFont(IN HFONT hNewFont
, IN BOOL bRedraw
)
1117 LinesMeasured
= MeasureLines();
1120 InvalidateRect(NULL
, TRUE
);
1124 LRESULT
DrawBackground(HDC hdc
)
1128 GetClientRect(&rect
);
1129 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
1134 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1136 HDC hdc
= (HDC
) wParam
;
1144 return DrawBackground(hdc
);
1147 LRESULT
OnTimer(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1151 case ID_TRAYCLOCK_TIMER
:
1155 case ID_TRAYCLOCK_TIMER_INIT
:
1162 LRESULT
OnGetMinimumSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1164 IsHorizontal
= (BOOL
) wParam
;
1166 return (LRESULT
) GetMinimumSize((BOOL
) wParam
, (PSIZE
) lParam
) != 0;
1169 LRESULT
OnUpdateTime(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1171 return (LRESULT
) ResetTime();
1174 LRESULT
OnNcHitTest(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1176 return HTTRANSPARENT
;
1179 LRESULT
OnSetFont(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1181 SetFont((HFONT
) wParam
, (BOOL
) LOWORD(lParam
));
1185 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1191 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1195 szClient
.cx
= LOWORD(lParam
);
1196 szClient
.cy
= HIWORD(lParam
);
1198 VisibleLines
= GetMinimumSize(IsHorizontal
, &szClient
);
1199 CurrentSize
= szClient
;
1201 InvalidateRect(NULL
, TRUE
);
1205 DECLARE_WND_CLASS_EX(szTrayClockWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
1207 BEGIN_MSG_MAP(CTaskSwitchWnd
)
1208 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
1209 MESSAGE_HANDLER(WM_DESTROY
, OnDestroy
)
1210 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
1211 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
1212 MESSAGE_HANDLER(WM_PAINT
, OnPaint
)
1213 MESSAGE_HANDLER(WM_PRINTCLIENT
, OnPaint
)
1214 MESSAGE_HANDLER(WM_TIMER
, OnTimer
)
1215 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTest
)
1216 MESSAGE_HANDLER(TCWM_GETMINIMUMSIZE
, OnGetMinimumSize
)
1217 MESSAGE_HANDLER(TCWM_UPDATETIME
, OnUpdateTime
)
1221 HWND
_Init(IN HWND hWndParent
, IN BOOL bVisible
)
1223 IsHorizontal
= TRUE
;
1225 hWndNotify
= hWndParent
;
1227 /* Create the window. The tray window is going to move it to the correct
1228 position and resize it as needed. */
1229 DWORD dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
1231 dwStyle
|= WS_VISIBLE
;
1233 Create(hWndParent
, 0, NULL
, dwStyle
);
1237 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
1250 static const WCHAR szTrayNotifyWndClass
[] = TEXT("TrayNotifyWnd");
1252 #define TRAY_NOTIFY_WND_SPACING_X 2
1253 #define TRAY_NOTIFY_WND_SPACING_Y 2
1255 class CTrayNotifyWnd
:
1256 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
1257 public CWindowImpl
< CTrayNotifyWnd
, CWindow
, CControlWinTraits
>
1261 CSysPagerWnd
* m_pager
;
1262 CTrayClockWnd
* m_clock
;
1264 CComPtr
<ITrayWindow
> TrayWindow
;
1267 SIZE szTrayClockMin
;
1269 MARGINS ContentMargin
;
1276 DWORD HideClock
: 1;
1277 DWORD IsHorizontal
: 1;
1290 ZeroMemory(&szTrayClockMin
, sizeof(szTrayClockMin
));
1291 ZeroMemory(&szTrayNotify
, sizeof(szTrayNotify
));
1292 ZeroMemory(&ContentMargin
, sizeof(ContentMargin
));
1294 virtual ~CTrayNotifyWnd() { }
1296 LRESULT
OnThemeChanged()
1299 CloseThemeData(TrayTheme
);
1301 if (IsThemeActive())
1302 TrayTheme
= OpenThemeData(m_hWnd
, L
"TrayNotify");
1308 SetWindowExStyle(m_hWnd
, WS_EX_STATICEDGE
, 0);
1310 GetThemeMargins(TrayTheme
,
1320 SetWindowExStyle(m_hWnd
, WS_EX_STATICEDGE
, WS_EX_STATICEDGE
);
1322 ContentMargin
.cxLeftWidth
= 0;
1323 ContentMargin
.cxRightWidth
= 0;
1324 ContentMargin
.cyTopHeight
= 0;
1325 ContentMargin
.cyBottomHeight
= 0;
1331 LRESULT
OnThemeChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1333 return OnThemeChanged();
1336 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1338 m_clock
= new CTrayClockWnd();
1339 m_clock
->_Init(m_hWnd
, !HideClock
);
1341 m_pager
= new CSysPagerWnd();
1342 m_pager
->_Init(m_hWnd
, !HideClock
);
1349 BOOL
GetMinimumSize(IN BOOL Horizontal
, IN OUT PSIZE pSize
)
1351 SIZE szClock
= { 0, 0 };
1352 SIZE szTray
= { 0, 0 };
1354 IsHorizontal
= Horizontal
;
1356 SetWindowTheme(m_hWnd
, L
"TrayNotifyHoriz", NULL
);
1358 SetWindowTheme(m_hWnd
, L
"TrayNotifyVert", NULL
);
1364 szClock
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1365 if (szClock
.cy
<= 0)
1370 szClock
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1371 if (szClock
.cx
<= 0)
1375 m_clock
->SendMessage(TCWM_GETMINIMUMSIZE
, (WPARAM
) Horizontal
, (LPARAM
) &szClock
);
1377 szTrayClockMin
= szClock
;
1381 szTrayClockMin
= szClock
;
1385 szTray
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1389 szTray
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1392 m_pager
->GetSize(Horizontal
, &szTray
);
1394 szTrayNotify
= szTray
;
1398 pSize
->cx
= 2 * TRAY_NOTIFY_WND_SPACING_X
;
1401 pSize
->cx
+= TRAY_NOTIFY_WND_SPACING_X
+ szTrayClockMin
.cx
;
1403 pSize
->cx
+= szTray
.cx
;
1407 pSize
->cy
= 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1410 pSize
->cy
+= TRAY_NOTIFY_WND_SPACING_Y
+ szTrayClockMin
.cy
;
1412 pSize
->cy
+= szTray
.cy
;
1415 pSize
->cy
+= ContentMargin
.cyTopHeight
+ ContentMargin
.cyBottomHeight
;
1416 pSize
->cx
+= ContentMargin
.cxLeftWidth
+ ContentMargin
.cxRightWidth
;
1421 VOID
Size(IN
const SIZE
*pszClient
)
1430 ptClock
.x
= pszClient
->cx
- TRAY_NOTIFY_WND_SPACING_X
- szTrayClockMin
.cx
;
1431 ptClock
.y
= TRAY_NOTIFY_WND_SPACING_Y
;
1432 szClock
.cx
= szTrayClockMin
.cx
;
1433 szClock
.cy
= pszClient
->cy
- (2 * TRAY_NOTIFY_WND_SPACING_Y
);
1437 ptClock
.x
= TRAY_NOTIFY_WND_SPACING_X
;
1438 ptClock
.y
= pszClient
->cy
- TRAY_NOTIFY_WND_SPACING_Y
- szTrayClockMin
.cy
;
1439 szClock
.cx
= pszClient
->cx
- (2 * TRAY_NOTIFY_WND_SPACING_X
);
1440 szClock
.cy
= szTrayClockMin
.cy
;
1443 m_clock
->SetWindowPos(
1453 ptClock
.x
-= szTrayNotify
.cx
;
1457 ptClock
.y
-= szTrayNotify
.cy
;
1460 m_pager
->SetWindowPos(
1470 LRESULT
DrawBackground(HDC hdc
)
1474 GetClientRect(&rect
);
1478 if (IsThemeBackgroundPartiallyTransparent(TrayTheme
, TNP_BACKGROUND
, 0))
1480 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
1483 DrawThemeBackground(TrayTheme
, hdc
, TNP_BACKGROUND
, 0, &rect
, 0);
1489 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1491 HDC hdc
= (HDC
) wParam
;
1499 return DrawBackground(hdc
);
1502 LRESULT
NotifyMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1506 m_pager
->NotifyMsg(uMsg
, wParam
, lParam
, bHandled
);
1512 BOOL
GetClockRect(OUT PRECT rcClock
)
1514 if (!IsWindowVisible(m_clock
->m_hWnd
))
1517 return GetWindowRect(m_clock
->m_hWnd
, rcClock
);
1520 LRESULT
OnGetMinimumSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1522 return (LRESULT
) GetMinimumSize((BOOL
) wParam
, (PSIZE
) lParam
);
1525 LRESULT
OnUpdateTime(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1527 if (m_clock
!= NULL
)
1529 /* Forward the message to the tray clock window procedure */
1530 return m_clock
->OnUpdateTime(uMsg
, wParam
, lParam
, bHandled
);
1535 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1539 szClient
.cx
= LOWORD(lParam
);
1540 szClient
.cy
= HIWORD(lParam
);
1547 LRESULT
OnNcHitTest(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1549 return HTTRANSPARENT
;
1552 LRESULT
OnShowClock(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1554 BOOL PrevHidden
= HideClock
;
1555 HideClock
= (wParam
== 0);
1557 if (m_clock
!= NULL
&& PrevHidden
!= HideClock
)
1559 m_clock
->ShowWindow(HideClock
? SW_HIDE
: SW_SHOW
);
1562 return (LRESULT
) (!PrevHidden
);
1565 LRESULT
OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1567 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
1569 if (nmh
->hwndFrom
== m_clock
->m_hWnd
)
1571 /* Pass down notifications */
1572 return m_clock
->SendMessage(WM_NOTIFY
, wParam
, lParam
);
1578 LRESULT
OnSetFont(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1580 if (m_clock
!= NULL
)
1582 m_clock
->SendMessageW(WM_SETFONT
, wParam
, lParam
);
1589 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
1591 BEGIN_MSG_MAP(CTaskSwitchWnd
)
1592 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
1593 MESSAGE_HANDLER(WM_THEMECHANGED
, OnThemeChanged
)
1594 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
1595 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
1596 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTest
)
1597 MESSAGE_HANDLER(WM_NOTIFY
, OnNotify
)
1598 MESSAGE_HANDLER(WM_SETFONT
, OnSetFont
)
1599 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE
, OnGetMinimumSize
)
1600 MESSAGE_HANDLER(TNWM_UPDATETIME
, OnUpdateTime
)
1601 MESSAGE_HANDLER(TNWM_SHOWCLOCK
, OnShowClock
)
1604 HWND
_Init(IN OUT ITrayWindow
*TrayWindow
, IN BOOL bHideClock
)
1606 HWND hWndTrayWindow
;
1608 hWndTrayWindow
= TrayWindow
->GetHWND();
1609 if (hWndTrayWindow
== NULL
)
1612 this->TrayWindow
= TrayWindow
;
1613 this->HideClock
= bHideClock
;
1614 this->hWndNotify
= hWndTrayWindow
;
1616 DWORD dwStyle
= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
1617 return Create(hWndTrayWindow
, 0, NULL
, dwStyle
, WS_EX_STATICEDGE
);
1621 static CTrayNotifyWnd
* g_Instance
;
1623 HWND
CreateTrayNotifyWnd(IN OUT ITrayWindow
*Tray
, BOOL bHideClock
)
1625 // TODO: Destroy after the window is destroyed
1626 g_Instance
= new CTrayNotifyWnd();
1628 return g_Instance
->_Init(Tray
, bHideClock
);
1632 TrayNotify_NotifyMsg(WPARAM wParam
, LPARAM lParam
)
1635 g_Instance
->NotifyMsg(0, wParam
, lParam
, bDummy
);
1639 TrayNotify_GetClockRect(OUT PRECT rcClock
)
1641 return g_Instance
->GetClockRect(rcClock
);