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
23 #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
24 #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
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 class CNotifyToolbar
:
40 public CToolbar
<NOTIFYICONDATA
>
42 static const int ICON_SIZE
= 16;
45 int VisibleButtonCount
;
58 int GetVisibleButtonCount()
60 return VisibleButtonCount
;
63 int FindItemByIconData(IN CONST NOTIFYICONDATA
*iconData
, NOTIFYICONDATA
** pdata
)
65 int count
= GetButtonCount();
67 for (int i
= 0; i
< count
; i
++)
69 NOTIFYICONDATA
* data
;
71 data
= GetItemData(i
);
73 if (data
->hWnd
== iconData
->hWnd
&&
74 data
->uID
== iconData
->uID
)
85 VOID
AddButton(IN CONST NOTIFYICONDATA
*iconData
)
88 NOTIFYICONDATA
* notifyItem
;
89 WCHAR text
[] = TEXT("");
91 int index
= FindItemByIconData(iconData
, ¬ifyItem
);
94 UpdateButton(iconData
);
98 notifyItem
= new NOTIFYICONDATA();
99 ZeroMemory(notifyItem
, sizeof(*notifyItem
));
101 notifyItem
->hWnd
= iconData
->hWnd
;
102 notifyItem
->uID
= iconData
->uID
;
104 tbBtn
.fsState
= TBSTATE_ENABLED
;
105 tbBtn
.fsStyle
= BTNS_NOPREFIX
;
106 tbBtn
.dwData
= (DWORD_PTR
)notifyItem
;
107 tbBtn
.iString
= (INT_PTR
) text
;
108 tbBtn
.idCommand
= GetButtonCount();
110 if (iconData
->uFlags
& NIF_MESSAGE
)
112 notifyItem
->uCallbackMessage
= iconData
->uCallbackMessage
;
115 if (iconData
->uFlags
& NIF_ICON
)
117 tbBtn
.iBitmap
= ImageList_AddIcon(SysIcons
, iconData
->hIcon
);
120 if (iconData
->uFlags
& NIF_TIP
)
122 StringCchCopy(notifyItem
->szTip
, _countof(notifyItem
->szTip
), iconData
->szTip
);
125 VisibleButtonCount
++;
126 if (iconData
->uFlags
& NIF_STATE
)
128 notifyItem
->dwState
&= ~iconData
->dwStateMask
;
129 notifyItem
->dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
130 if (notifyItem
->dwState
& NIS_HIDDEN
)
132 tbBtn
.fsState
|= TBSTATE_HIDDEN
;
133 VisibleButtonCount
--;
138 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
140 CToolbar::AddButton(&tbBtn
);
141 SetButtonSize(ICON_SIZE
, ICON_SIZE
);
144 VOID
UpdateButton(IN CONST NOTIFYICONDATA
*iconData
)
146 NOTIFYICONDATA
* notifyItem
;
147 TBBUTTONINFO tbbi
= { 0 };
149 int index
= FindItemByIconData(iconData
, ¬ifyItem
);
156 tbbi
.cbSize
= sizeof(tbbi
);
157 tbbi
.dwMask
= TBIF_BYINDEX
| TBIF_COMMAND
;
158 tbbi
.idCommand
= index
;
160 if (iconData
->uFlags
& NIF_MESSAGE
)
162 notifyItem
->uCallbackMessage
= iconData
->uCallbackMessage
;
165 if (iconData
->uFlags
& NIF_ICON
)
167 tbbi
.dwMask
|= TBIF_IMAGE
;
168 tbbi
.iImage
= ImageList_AddIcon(SysIcons
, iconData
->hIcon
);
171 if (iconData
->uFlags
& NIF_TIP
)
173 StringCchCopy(notifyItem
->szTip
, _countof(notifyItem
->szTip
), iconData
->szTip
);
176 if (iconData
->uFlags
& NIF_STATE
)
178 if (iconData
->dwStateMask
& NIS_HIDDEN
&&
179 (notifyItem
->dwState
& NIS_HIDDEN
) != (iconData
->dwState
& NIS_HIDDEN
))
181 tbbi
.dwMask
|= TBIF_STATE
;
182 if (iconData
->dwState
& NIS_HIDDEN
)
184 tbbi
.fsState
|= TBSTATE_HIDDEN
;
185 VisibleButtonCount
--;
189 tbbi
.fsState
&= ~TBSTATE_HIDDEN
;
190 VisibleButtonCount
++;
194 notifyItem
->dwState
&= ~iconData
->dwStateMask
;
195 notifyItem
->dwState
|= (iconData
->dwState
& iconData
->dwStateMask
);
198 /* TODO: support NIF_INFO, NIF_GUID, NIF_REALTIME, NIF_SHOWTIP */
200 SetButtonInfo(index
, &tbbi
);
203 VOID
RemoveButton(IN CONST NOTIFYICONDATA
*iconData
)
205 NOTIFYICONDATA
* notifyItem
;
207 int index
= FindItemByIconData(iconData
, ¬ifyItem
);
215 VOID
GetTooltipText(int index
, LPTSTR szTip
, DWORD cchTip
)
217 NOTIFYICONDATA
* notifyItem
;
218 notifyItem
= GetItemData(index
);
222 StringCchCopy(szTip
, cchTip
, notifyItem
->szTip
);
228 VOID
SendMouseEvent(IN WORD wIndex
, IN UINT uMsg
, IN WPARAM wParam
)
230 static LPCWSTR eventNames
[] = {
247 NOTIFYICONDATA
* notifyItem
= GetItemData(wIndex
);
249 if (!::IsWindow(notifyItem
->hWnd
))
252 if (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
)
254 TRACE("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n",
255 eventNames
[uMsg
- WM_MOUSEFIRST
], wIndex
,
256 notifyItem
->hWnd
, notifyItem
->uCallbackMessage
, notifyItem
->uID
, uMsg
);
260 GetWindowThreadProcessId(notifyItem
->hWnd
, &pid
);
262 if (pid
== GetCurrentProcessId() ||
263 (uMsg
>= WM_MOUSEFIRST
&& uMsg
<= WM_MOUSELAST
))
265 PostMessage(notifyItem
->hWnd
,
266 notifyItem
->uCallbackMessage
,
272 SendMessage(notifyItem
->hWnd
,
273 notifyItem
->uCallbackMessage
,
279 LRESULT
OnMouseEvent(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
281 POINT pt
= { GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
) };
283 INT iBtn
= HitTest(&pt
);
287 SendMouseEvent(iBtn
, uMsg
, wParam
);
294 LRESULT
OnTooltipShow(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
297 GetWindowRect(hdr
->hwndFrom
, &rcTip
);
299 SIZE szTip
= { rcTip
.right
- rcTip
.left
, rcTip
.bottom
- rcTip
.top
};
301 INT iBtn
= GetHotItem();
305 MONITORINFO monInfo
= { 0 };
306 HMONITOR hMon
= MonitorFromWindow(m_hWnd
, MONITOR_DEFAULTTONEAREST
);
308 monInfo
.cbSize
= sizeof(monInfo
);
311 GetMonitorInfo(hMon
, &monInfo
);
313 GetWindowRect(GetDesktopWindow(), &monInfo
.rcMonitor
);
315 GetItemRect(iBtn
, &rcItem
);
317 POINT ptItem
= { rcItem
.left
, rcItem
.top
};
318 SIZE szItem
= { rcItem
.right
- rcItem
.left
, rcItem
.bottom
- rcItem
.top
};
319 ClientToScreen(m_hWnd
, &ptItem
);
321 ptItem
.x
+= szItem
.cx
/ 2;
322 ptItem
.y
-= szTip
.cy
;
324 if (ptItem
.x
+ szTip
.cx
> monInfo
.rcMonitor
.right
)
325 ptItem
.x
= monInfo
.rcMonitor
.right
- szTip
.cx
;
327 if (ptItem
.y
+ szTip
.cy
> monInfo
.rcMonitor
.bottom
)
328 ptItem
.y
= monInfo
.rcMonitor
.bottom
- szTip
.cy
;
330 if (ptItem
.x
< monInfo
.rcMonitor
.left
)
331 ptItem
.x
= monInfo
.rcMonitor
.left
;
333 if (ptItem
.y
< monInfo
.rcMonitor
.top
)
334 ptItem
.y
= monInfo
.rcMonitor
.top
;
336 TRACE("ptItem { %d, %d }\n", ptItem
.x
, ptItem
.y
);
338 ::SetWindowPos(hdr
->hwndFrom
, NULL
, ptItem
.x
, ptItem
.y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
349 BEGIN_MSG_MAP(CNotifyToolbar
)
350 MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST
, WM_MOUSELAST
, OnMouseEvent
)
351 NOTIFY_CODE_HANDLER(TTN_SHOW
, OnTooltipShow
)
354 void Initialize(HWND hWndParent
)
357 WS_CHILD
| WS_VISIBLE
| WS_CLIPCHILDREN
|
358 TBSTYLE_FLAT
| TBSTYLE_TOOLTIPS
| TBSTYLE_WRAPABLE
| TBSTYLE_TRANSPARENT
|
359 CCS_TOP
| CCS_NORESIZE
| CCS_NOPARENTALIGN
| CCS_NODIVIDER
;
361 SubclassWindow(Create(hWndParent
, styles
));
363 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
365 SysIcons
= ImageList_Create(16, 16, ILC_COLOR32
| ILC_MASK
, 0, 1000);
366 SetImageList(SysIcons
);
368 SetButtonSize(ICON_SIZE
, ICON_SIZE
);
373 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
374 public CWindowImpl
< CSysPagerWnd
, CWindow
, CControlWinTraits
>
376 CNotifyToolbar Toolbar
;
380 virtual ~CSysPagerWnd() {}
382 LRESULT
DrawBackground(HDC hdc
)
386 GetClientRect(&rect
);
387 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
392 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
394 HDC hdc
= (HDC
) wParam
;
402 return DrawBackground(hdc
);
405 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
407 Toolbar
.Initialize(m_hWnd
);
411 LRESULT
NotifyMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
413 PCOPYDATASTRUCT cpData
= (PCOPYDATASTRUCT
) lParam
;
414 if (cpData
->dwData
== 1)
416 SYS_PAGER_COPY_DATA
* data
;
417 NOTIFYICONDATA
*iconData
;
420 parentHWND
= GetParent();
421 parentHWND
= ::GetParent(parentHWND
);
422 ::GetClientRect(parentHWND
, &windowRect
);
424 data
= (PSYS_PAGER_COPY_DATA
) cpData
->lpData
;
425 iconData
= &data
->nicon_data
;
427 switch (data
->notify_code
)
431 Toolbar
.AddButton(iconData
);
436 Toolbar
.UpdateButton(iconData
);
441 Toolbar
.RemoveButton(iconData
);
445 TRACE("NotifyMessage received with unknown code %d.\n", data
->notify_code
);
448 SendMessage(parentHWND
,
451 MAKELONG(windowRect
.right
- windowRect
.left
,
452 windowRect
.bottom
- windowRect
.top
));
458 void GetSize(IN WPARAM wParam
, IN PSIZE size
)
461 int VisibleButtonCount
= Toolbar
.GetVisibleButtonCount();
463 if (wParam
) /* horizontal */
465 rows
= size
->cy
/ 24;
468 size
->cx
= (VisibleButtonCount
+ rows
- 1) / rows
* 24;
472 rows
= size
->cx
/ 24;
475 size
->cy
= (VisibleButtonCount
+ rows
- 1) / rows
* 24;
479 LRESULT
OnGetInfoTip(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
481 NMTBGETINFOTIPW
* nmtip
= (NMTBGETINFOTIPW
*) hdr
;
482 Toolbar
.GetTooltipText(nmtip
->iItem
, nmtip
->pszText
, nmtip
->cchTextMax
);
486 LRESULT
OnCustomDraw(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
488 NMCUSTOMDRAW
* cdraw
= (NMCUSTOMDRAW
*) hdr
;
489 switch (cdraw
->dwDrawStage
)
492 return CDRF_NOTIFYITEMDRAW
;
494 case CDDS_ITEMPREPAINT
:
495 return TBCDRF_NOBACKGROUND
| TBCDRF_NOEDGES
| TBCDRF_NOOFFSET
| TBCDRF_NOMARK
| TBCDRF_NOETCHEDEFFECT
;
500 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
504 szClient
.cx
= LOWORD(lParam
);
505 szClient
.cy
= HIWORD(lParam
);
507 Ret
= DefWindowProc(uMsg
, wParam
, lParam
);
512 tbm
.cbSize
= sizeof(tbm
);
513 tbm
.dwMask
= TBMF_BARPAD
| TBMF_BUTTONSPACING
;
514 tbm
.cxBarPad
= tbm
.cyBarPad
= 0;
515 tbm
.cxButtonSpacing
= 0;
516 tbm
.cyButtonSpacing
= 0;
518 Toolbar
.SetMetrics(&tbm
);
520 Toolbar
.SetWindowPos(NULL
, 0, 0, szClient
.cx
, szClient
.cy
, SWP_NOZORDER
);
524 Toolbar
.GetClientRect(&rc
);
526 SIZE szBar
= { rc
.right
- rc
.left
, rc
.bottom
- rc
.top
};
528 INT xOff
= (szClient
.cx
- szBar
.cx
) / 2;
529 INT yOff
= (szClient
.cy
- szBar
.cy
) / 2;
531 Toolbar
.SetWindowPos(NULL
, xOff
, yOff
, szBar
.cx
, szBar
.cy
, SWP_NOZORDER
);
536 DECLARE_WND_CLASS_EX(szSysPagerWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
538 BEGIN_MSG_MAP(CTaskSwitchWnd
)
539 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
540 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
541 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
542 NOTIFY_CODE_HANDLER(TBN_GETINFOTIPW
, OnGetInfoTip
)
543 NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW
, OnCustomDraw
)
546 HWND
_Init(IN HWND hWndParent
, IN BOOL bVisible
)
550 /* Create the window. The tray window is going to move it to the correct
551 position and resize it as needed. */
552 dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
554 dwStyle
|= WS_VISIBLE
;
556 Create(hWndParent
, 0, NULL
, dwStyle
);
563 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
573 static const WCHAR szTrayClockWndClass
[] = TEXT("TrayClockWClass");
575 #define ID_TRAYCLOCK_TIMER 0
576 #define ID_TRAYCLOCK_TIMER_INIT 1
583 } ClockWndFormats
[] = {
585 { FALSE
, 0, TEXT("dddd") },
586 { FALSE
, DATE_SHORTDATE
, NULL
}
589 #define CLOCKWND_FORMAT_COUNT (sizeof(ClockWndFormats) / sizeof(ClockWndFormats[0]))
591 #define TRAY_CLOCK_WND_SPACING_X 0
592 #define TRAY_CLOCK_WND_SPACING_Y 0
594 class CTrayClockWnd
:
595 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
596 public CWindowImpl
< CTrayClockWnd
, CWindow
, CControlWinTraits
>
602 SYSTEMTIME LocalTime
;
609 DWORD IsTimerEnabled
: 1;
610 DWORD IsInitTimerEnabled
: 1;
611 DWORD LinesMeasured
: 1;
612 DWORD IsHorizontal
: 1;
618 SIZE LineSizes
[CLOCKWND_FORMAT_COUNT
];
619 WCHAR szLines
[CLOCKWND_FORMAT_COUNT
][48];
629 ZeroMemory(&textColor
, sizeof(textColor
));
630 ZeroMemory(&rcText
, sizeof(rcText
));
631 ZeroMemory(&LocalTime
, sizeof(LocalTime
));
632 ZeroMemory(&CurrentSize
, sizeof(CurrentSize
));
633 ZeroMemory(LineSizes
, sizeof(LineSizes
));
634 ZeroMemory(szLines
, sizeof(LineSizes
));
636 virtual ~CTrayClockWnd() { }
638 LRESULT
OnThemeChanged()
644 clockTheme
= OpenThemeData(m_hWnd
, L
"Clock");
648 GetThemeFont(clockTheme
,
655 hFont
= CreateFontIndirectW(&clockFont
);
657 GetThemeColor(clockTheme
,
665 NONCLIENTMETRICS ncm
= { 0 };
666 ncm
.cbSize
= sizeof(ncm
);
667 SystemParametersInfo(SPI_GETNONCLIENTMETRICS
, sizeof(ncm
), &ncm
, FALSE
);
669 hFont
= CreateFontIndirectW(&ncm
.lfMessageFont
);
671 textColor
= RGB(0, 0, 0);
674 SetFont(hFont
, FALSE
);
676 CloseThemeData(clockTheme
);
681 LRESULT
OnThemeChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
683 return OnThemeChanged();
697 hPrevFont
= (HFONT
) SelectObject(hDC
, hFont
);
699 for (i
= 0; i
!= CLOCKWND_FORMAT_COUNT
&& bRet
; i
++)
701 if (szLines
[i
][0] != TEXT('\0') &&
702 !GetTextExtentPoint(hDC
, szLines
[i
], _tcslen(szLines
[i
]),
711 SelectObject(hDC
, hPrevFont
);
713 ReleaseDC(m_hWnd
, hDC
);
719 /* calculate the line spacing */
720 for (i
= 0, c
= 0; i
!= CLOCKWND_FORMAT_COUNT
; i
++)
722 if (LineSizes
[i
].cx
> 0)
724 LineSpacing
+= LineSizes
[i
].cy
;
731 /* We want a spaceing of 1/2 line */
732 LineSpacing
= (LineSpacing
/ c
) / 2;
742 WORD
GetMinimumSize(IN BOOL Horizontal
, IN OUT PSIZE pSize
)
744 WORD iLinesVisible
= 0;
746 SIZE szMax
= { 0, 0 };
749 LinesMeasured
= MeasureLines();
755 i
!= CLOCKWND_FORMAT_COUNT
;
758 if (LineSizes
[i
].cx
!= 0)
760 if (iLinesVisible
> 0)
764 if (szMax
.cy
+ LineSizes
[i
].cy
+ (LONG
) LineSpacing
>
765 pSize
->cy
- (2 * TRAY_CLOCK_WND_SPACING_Y
))
772 if (LineSizes
[i
].cx
> pSize
->cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
776 /* Add line spacing */
777 szMax
.cy
+= LineSpacing
;
782 /* Increase maximum rectangle */
783 szMax
.cy
+= LineSizes
[i
].cy
;
784 if (LineSizes
[i
].cx
> szMax
.cx
- (2 * TRAY_CLOCK_WND_SPACING_X
))
785 szMax
.cx
= LineSizes
[i
].cx
+ (2 * TRAY_CLOCK_WND_SPACING_X
);
789 szMax
.cx
+= 2 * TRAY_CLOCK_WND_SPACING_X
;
790 szMax
.cy
+= 2 * TRAY_CLOCK_WND_SPACING_Y
;
794 return iLinesVisible
;
801 INT BufSize
, iRet
, i
;
804 ZeroMemory(LineSizes
,
807 szPrevCurrent
= CurrentSize
;
810 i
!= CLOCKWND_FORMAT_COUNT
;
813 szLines
[i
][0] = TEXT('\0');
814 BufSize
= sizeof(szLines
[0]) / sizeof(szLines
[0][0]);
816 if (ClockWndFormats
[i
].IsTime
)
818 iRet
= GetTimeFormat(LOCALE_USER_DEFAULT
,
819 AdvancedSettings
.bShowSeconds
? ClockWndFormats
[i
].dwFormatFlags
: TIME_NOSECONDS
,
821 ClockWndFormats
[i
].lpFormat
,
827 iRet
= GetDateFormat(LOCALE_USER_DEFAULT
,
828 ClockWndFormats
[i
].dwFormatFlags
,
830 ClockWndFormats
[i
].lpFormat
,
835 if (iRet
!= 0 && i
== 0)
837 /* Set the window text to the time only */
838 SetWindowText(szLines
[i
]);
842 LinesMeasured
= MeasureLines();
845 GetClientRect(&rcClient
))
849 szWnd
.cx
= rcClient
.right
;
850 szWnd
.cy
= rcClient
.bottom
;
852 VisibleLines
= GetMinimumSize(IsHorizontal
, &szWnd
);
856 if (IsWindowVisible(m_hWnd
))
858 InvalidateRect(NULL
, TRUE
);
860 if (hWndNotify
!= NULL
&&
861 (szPrevCurrent
.cx
!= CurrentSize
.cx
||
862 szPrevCurrent
.cy
!= CurrentSize
.cy
))
866 nmh
.hwndFrom
= m_hWnd
;
867 nmh
.idFrom
= GetWindowLongPtr(m_hWnd
, GWLP_ID
);
868 nmh
.code
= NTNWM_REALIGN
;
870 SendMessage(hWndNotify
,
880 GetLocalTime(&LocalTime
);
884 UINT
CalculateDueTime()
888 /* Calculate the due time */
889 GetLocalTime(&LocalTime
);
890 uiDueTime
= 1000 - (UINT
) LocalTime
.wMilliseconds
;
891 if (AdvancedSettings
.bShowSeconds
)
892 uiDueTime
+= (UINT
) LocalTime
.wSecond
* 100;
894 uiDueTime
+= (59 - (UINT
) LocalTime
.wSecond
) * 1000;
896 if (uiDueTime
< USER_TIMER_MINIMUM
|| uiDueTime
> USER_TIMER_MAXIMUM
)
900 /* Add an artificial delay of 0.05 seconds to make sure the timer
901 doesn't fire too early*/
913 /* Disable all timers */
916 KillTimer(ID_TRAYCLOCK_TIMER
);
917 IsTimerEnabled
= FALSE
;
920 if (IsInitTimerEnabled
)
922 KillTimer(ID_TRAYCLOCK_TIMER_INIT
);
925 uiDueTime
= CalculateDueTime();
927 /* Set the new timer */
928 Ret
= SetTimer(ID_TRAYCLOCK_TIMER_INIT
, uiDueTime
, NULL
) != 0;
929 IsInitTimerEnabled
= Ret
;
931 /* Update the time */
937 VOID
CalibrateTimer()
941 UINT uiWait1
, uiWait2
;
943 /* Kill the initialization timer */
944 KillTimer(ID_TRAYCLOCK_TIMER_INIT
);
945 IsInitTimerEnabled
= FALSE
;
947 uiDueTime
= CalculateDueTime();
949 if (AdvancedSettings
.bShowSeconds
)
951 uiWait1
= 1000 - 200;
956 uiWait1
= 60 * 1000 - 200;
960 if (uiDueTime
> uiWait1
)
962 /* The update of the clock will be up to 200 ms late, but that's
963 acceptable. We're going to setup a timer that fires depending
965 Ret
= SetTimer(ID_TRAYCLOCK_TIMER
, uiWait2
, NULL
) != 0;
966 IsTimerEnabled
= Ret
;
968 /* Update the time */
973 /* Recalibrate the timer and recalculate again when the current
974 minute/second ends. */
979 LRESULT
OnDestroy(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
981 /* Disable all timers */
984 KillTimer(ID_TRAYCLOCK_TIMER
);
987 if (IsInitTimerEnabled
)
989 KillTimer(ID_TRAYCLOCK_TIMER_INIT
);
995 LRESULT
OnPaint(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
999 int iPrevBkMode
, i
, line
;
1002 HDC hDC
= (HDC
) wParam
;
1006 hDC
= BeginPaint(&ps
);
1012 if (LinesMeasured
&&
1013 GetClientRect(&rcClient
))
1015 iPrevBkMode
= SetBkMode(hDC
, TRANSPARENT
);
1017 SetTextColor(hDC
, textColor
);
1019 hPrevFont
= (HFONT
) SelectObject(hDC
, hFont
);
1021 rcClient
.left
= (rcClient
.right
/ 2) - (CurrentSize
.cx
/ 2);
1022 rcClient
.top
= (rcClient
.bottom
/ 2) - (CurrentSize
.cy
/ 2);
1023 rcClient
.right
= rcClient
.left
+ CurrentSize
.cx
;
1024 rcClient
.bottom
= rcClient
.top
+ CurrentSize
.cy
;
1026 for (i
= 0, line
= 0;
1027 i
!= CLOCKWND_FORMAT_COUNT
&& line
< VisibleLines
;
1030 if (LineSizes
[i
].cx
!= 0)
1033 rcClient
.left
+ (CurrentSize
.cx
/ 2) - (LineSizes
[i
].cx
/ 2) +
1034 TRAY_CLOCK_WND_SPACING_X
,
1035 rcClient
.top
+ TRAY_CLOCK_WND_SPACING_Y
,
1037 _tcslen(szLines
[i
]));
1039 rcClient
.top
+= LineSizes
[i
].cy
+ LineSpacing
;
1044 SelectObject(hDC
, hPrevFont
);
1046 SetBkMode(hDC
, iPrevBkMode
);
1057 VOID
SetFont(IN HFONT hNewFont
, IN BOOL bRedraw
)
1060 LinesMeasured
= MeasureLines();
1063 InvalidateRect(NULL
, TRUE
);
1067 LRESULT
DrawBackground(HDC hdc
)
1071 GetClientRect(&rect
);
1072 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
1077 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1079 HDC hdc
= (HDC
) wParam
;
1087 return DrawBackground(hdc
);
1090 LRESULT
OnTimer(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1094 case ID_TRAYCLOCK_TIMER
:
1098 case ID_TRAYCLOCK_TIMER_INIT
:
1105 LRESULT
OnGetMinimumSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1107 IsHorizontal
= (BOOL
) wParam
;
1109 return (LRESULT
) GetMinimumSize((BOOL
) wParam
, (PSIZE
) lParam
) != 0;
1112 LRESULT
OnUpdateTime(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1114 return (LRESULT
) ResetTime();
1117 LRESULT
OnNcHitTest(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1119 return HTTRANSPARENT
;
1122 LRESULT
OnSetFont(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1124 SetFont((HFONT
) wParam
, (BOOL
) LOWORD(lParam
));
1128 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1134 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1138 szClient
.cx
= LOWORD(lParam
);
1139 szClient
.cy
= HIWORD(lParam
);
1141 VisibleLines
= GetMinimumSize(IsHorizontal
, &szClient
);
1142 CurrentSize
= szClient
;
1144 InvalidateRect(NULL
, TRUE
);
1148 DECLARE_WND_CLASS_EX(szTrayClockWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
1150 BEGIN_MSG_MAP(CTaskSwitchWnd
)
1151 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
1152 MESSAGE_HANDLER(WM_DESTROY
, OnDestroy
)
1153 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
1154 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
1155 MESSAGE_HANDLER(WM_PAINT
, OnPaint
)
1156 MESSAGE_HANDLER(WM_PRINTCLIENT
, OnPaint
)
1157 MESSAGE_HANDLER(WM_TIMER
, OnTimer
)
1158 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTest
)
1159 MESSAGE_HANDLER(TCWM_GETMINIMUMSIZE
, OnGetMinimumSize
)
1160 MESSAGE_HANDLER(TCWM_UPDATETIME
, OnUpdateTime
)
1164 HWND
_Init(IN HWND hWndParent
, IN BOOL bVisible
)
1166 IsHorizontal
= TRUE
;
1168 hWndNotify
= hWndParent
;
1170 /* Create the window. The tray window is going to move it to the correct
1171 position and resize it as needed. */
1172 DWORD dwStyle
= WS_CHILD
| WS_CLIPSIBLINGS
;
1174 dwStyle
|= WS_VISIBLE
;
1176 Create(hWndParent
, 0, NULL
, dwStyle
);
1180 SetWindowTheme(m_hWnd
, L
"TrayNotify", NULL
);
1193 static const WCHAR szTrayNotifyWndClass
[] = TEXT("TrayNotifyWnd");
1195 #define TRAY_NOTIFY_WND_SPACING_X 2
1196 #define TRAY_NOTIFY_WND_SPACING_Y 2
1198 class CTrayNotifyWnd
:
1199 public CComObjectRootEx
<CComMultiThreadModelNoCS
>,
1200 public CWindowImpl
< CTrayNotifyWnd
, CWindow
, CControlWinTraits
>
1204 CSysPagerWnd
* m_pager
;
1205 CTrayClockWnd
* m_clock
;
1207 CComPtr
<ITrayWindow
> TrayWindow
;
1210 SIZE szTrayClockMin
;
1212 MARGINS ContentMargin
;
1219 DWORD HideClock
: 1;
1220 DWORD IsHorizontal
: 1;
1233 ZeroMemory(&szTrayClockMin
, sizeof(szTrayClockMin
));
1234 ZeroMemory(&szTrayNotify
, sizeof(szTrayNotify
));
1235 ZeroMemory(&ContentMargin
, sizeof(ContentMargin
));
1237 virtual ~CTrayNotifyWnd() { }
1239 LRESULT
OnThemeChanged()
1242 CloseThemeData(TrayTheme
);
1244 if (IsThemeActive())
1245 TrayTheme
= OpenThemeData(m_hWnd
, L
"TrayNotify");
1251 SetWindowExStyle(m_hWnd
, WS_EX_STATICEDGE
, 0);
1253 GetThemeMargins(TrayTheme
,
1263 SetWindowExStyle(m_hWnd
, WS_EX_STATICEDGE
, WS_EX_STATICEDGE
);
1265 ContentMargin
.cxLeftWidth
= 0;
1266 ContentMargin
.cxRightWidth
= 0;
1267 ContentMargin
.cyTopHeight
= 0;
1268 ContentMargin
.cyBottomHeight
= 0;
1274 LRESULT
OnThemeChanged(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1276 return OnThemeChanged();
1279 LRESULT
OnCreate(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1281 m_clock
= new CTrayClockWnd();
1282 m_clock
->_Init(m_hWnd
, !HideClock
);
1284 m_pager
= new CSysPagerWnd();
1285 m_pager
->_Init(m_hWnd
, !HideClock
);
1292 BOOL
GetMinimumSize(IN BOOL Horizontal
, IN OUT PSIZE pSize
)
1294 SIZE szClock
= { 0, 0 };
1295 SIZE szTray
= { 0, 0 };
1297 IsHorizontal
= Horizontal
;
1299 SetWindowTheme(m_hWnd
, L
"TrayNotifyHoriz", NULL
);
1301 SetWindowTheme(m_hWnd
, L
"TrayNotifyVert", NULL
);
1307 szClock
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1308 if (szClock
.cy
<= 0)
1313 szClock
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1314 if (szClock
.cx
<= 0)
1318 m_clock
->SendMessage(TCWM_GETMINIMUMSIZE
, (WPARAM
) Horizontal
, (LPARAM
) &szClock
);
1320 szTrayClockMin
= szClock
;
1324 szTrayClockMin
= szClock
;
1328 szTray
.cy
= pSize
->cy
- 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1332 szTray
.cx
= pSize
->cx
- 2 * TRAY_NOTIFY_WND_SPACING_X
;
1335 m_pager
->GetSize(Horizontal
, &szTray
);
1337 szTrayNotify
= szTray
;
1341 pSize
->cx
= 2 * TRAY_NOTIFY_WND_SPACING_X
;
1344 pSize
->cx
+= TRAY_NOTIFY_WND_SPACING_X
+ szTrayClockMin
.cx
;
1346 pSize
->cx
+= szTray
.cx
;
1350 pSize
->cy
= 2 * TRAY_NOTIFY_WND_SPACING_Y
;
1353 pSize
->cy
+= TRAY_NOTIFY_WND_SPACING_Y
+ szTrayClockMin
.cy
;
1355 pSize
->cy
+= szTray
.cy
;
1358 pSize
->cy
+= ContentMargin
.cyTopHeight
+ ContentMargin
.cyBottomHeight
;
1359 pSize
->cx
+= ContentMargin
.cxLeftWidth
+ ContentMargin
.cxRightWidth
;
1364 VOID
Size(IN
const SIZE
*pszClient
)
1373 ptClock
.x
= pszClient
->cx
- TRAY_NOTIFY_WND_SPACING_X
- szTrayClockMin
.cx
;
1374 ptClock
.y
= TRAY_NOTIFY_WND_SPACING_Y
;
1375 szClock
.cx
= szTrayClockMin
.cx
;
1376 szClock
.cy
= pszClient
->cy
- (2 * TRAY_NOTIFY_WND_SPACING_Y
);
1380 ptClock
.x
= TRAY_NOTIFY_WND_SPACING_X
;
1381 ptClock
.y
= pszClient
->cy
- TRAY_NOTIFY_WND_SPACING_Y
- szTrayClockMin
.cy
;
1382 szClock
.cx
= pszClient
->cx
- (2 * TRAY_NOTIFY_WND_SPACING_X
);
1383 szClock
.cy
= szTrayClockMin
.cy
;
1386 m_clock
->SetWindowPos(
1396 ptClock
.x
-= szTrayNotify
.cx
;
1400 ptClock
.y
-= szTrayNotify
.cy
;
1403 m_pager
->SetWindowPos(
1413 LRESULT
DrawBackground(HDC hdc
)
1417 GetClientRect(&rect
);
1421 if (IsThemeBackgroundPartiallyTransparent(TrayTheme
, TNP_BACKGROUND
, 0))
1423 DrawThemeParentBackground(m_hWnd
, hdc
, &rect
);
1426 DrawThemeBackground(TrayTheme
, hdc
, TNP_BACKGROUND
, 0, &rect
, 0);
1432 LRESULT
OnEraseBackground(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1434 HDC hdc
= (HDC
) wParam
;
1442 return DrawBackground(hdc
);
1445 LRESULT
NotifyMsg(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1449 m_pager
->NotifyMsg(uMsg
, wParam
, lParam
, bHandled
);
1455 BOOL
GetClockRect(OUT PRECT rcClock
)
1457 if (!IsWindowVisible(m_clock
->m_hWnd
))
1460 return GetWindowRect(m_clock
->m_hWnd
, rcClock
);
1463 LRESULT
OnGetMinimumSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1465 return (LRESULT
) GetMinimumSize((BOOL
) wParam
, (PSIZE
) lParam
);
1468 LRESULT
OnUpdateTime(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1470 if (m_clock
!= NULL
)
1472 /* Forward the message to the tray clock window procedure */
1473 return m_clock
->OnUpdateTime(uMsg
, wParam
, lParam
, bHandled
);
1478 LRESULT
OnSize(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1482 szClient
.cx
= LOWORD(lParam
);
1483 szClient
.cy
= HIWORD(lParam
);
1490 LRESULT
OnNcHitTest(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1492 return HTTRANSPARENT
;
1495 LRESULT
OnShowClock(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1497 BOOL PrevHidden
= HideClock
;
1498 HideClock
= (wParam
== 0);
1500 if (m_clock
!= NULL
&& PrevHidden
!= HideClock
)
1502 m_clock
->ShowWindow(HideClock
? SW_HIDE
: SW_SHOW
);
1505 return (LRESULT
) (!PrevHidden
);
1508 LRESULT
OnNotify(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1510 const NMHDR
*nmh
= (const NMHDR
*) lParam
;
1512 if (nmh
->hwndFrom
== m_clock
->m_hWnd
)
1514 /* Pass down notifications */
1515 return m_clock
->SendMessage(WM_NOTIFY
, wParam
, lParam
);
1521 LRESULT
OnSetFont(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
& bHandled
)
1523 if (m_clock
!= NULL
)
1525 m_clock
->SendMessageW(WM_SETFONT
, wParam
, lParam
);
1532 DECLARE_WND_CLASS_EX(szTrayNotifyWndClass
, CS_DBLCLKS
, COLOR_3DFACE
)
1534 BEGIN_MSG_MAP(CTaskSwitchWnd
)
1535 MESSAGE_HANDLER(WM_CREATE
, OnCreate
)
1536 MESSAGE_HANDLER(WM_THEMECHANGED
, OnThemeChanged
)
1537 MESSAGE_HANDLER(WM_ERASEBKGND
, OnEraseBackground
)
1538 MESSAGE_HANDLER(WM_SIZE
, OnSize
)
1539 MESSAGE_HANDLER(WM_NCHITTEST
, OnNcHitTest
)
1540 MESSAGE_HANDLER(WM_NOTIFY
, OnNotify
)
1541 MESSAGE_HANDLER(WM_SETFONT
, OnSetFont
)
1542 MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE
, OnGetMinimumSize
)
1543 MESSAGE_HANDLER(TNWM_UPDATETIME
, OnUpdateTime
)
1544 MESSAGE_HANDLER(TNWM_SHOWCLOCK
, OnShowClock
)
1547 HWND
_Init(IN OUT ITrayWindow
*TrayWindow
, IN BOOL bHideClock
)
1549 HWND hWndTrayWindow
;
1551 hWndTrayWindow
= TrayWindow
->GetHWND();
1552 if (hWndTrayWindow
== NULL
)
1555 this->TrayWindow
= TrayWindow
;
1556 this->HideClock
= bHideClock
;
1557 this->hWndNotify
= hWndTrayWindow
;
1559 DWORD dwStyle
= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
1560 return Create(hWndTrayWindow
, 0, NULL
, dwStyle
, WS_EX_STATICEDGE
);
1564 static CTrayNotifyWnd
* g_Instance
;
1566 HWND
CreateTrayNotifyWnd(IN OUT ITrayWindow
*Tray
, BOOL bHideClock
)
1568 // TODO: Destroy after the window is destroyed
1569 g_Instance
= new CTrayNotifyWnd();
1571 return g_Instance
->_Init(Tray
, bHideClock
);
1575 TrayNotify_NotifyMsg(WPARAM wParam
, LPARAM lParam
)
1578 g_Instance
->NotifyMsg(0, wParam
, lParam
, bDummy
);
1582 TrayNotify_GetClockRect(OUT PRECT rcClock
)
1584 return g_Instance
->GetClockRect(rcClock
);