NOTIFYICONDATA nicon_data;
} SYS_PAGER_COPY_DATA, *PSYS_PAGER_COPY_DATA;
+struct InternalIconData : NOTIFYICONDATA
+{
+ // Must keep a separate copy since the original is unioned with uTimeout.
+ UINT uVersionCopy;
+};
+
+struct IconWatcherData
+{
+ HANDLE hProcess;
+ DWORD ProcessId;
+ NOTIFYICONDATA IconData;
+
+ IconWatcherData(CONST NOTIFYICONDATA *iconData) :
+ hProcess(NULL), ProcessId(0)
+ {
+ IconData.cbSize = sizeof(NOTIFYICONDATA);
+ IconData.hWnd = iconData->hWnd;
+ IconData.uID = iconData->uID;
+ IconData.guidItem = iconData->guidItem;
+ }
+
+ ~IconWatcherData()
+ {
+ if (hProcess)
+ {
+ CloseHandle(hProcess);
+ }
+ }
+};
+
+class CIconWatcher
+{
+ CAtlList<IconWatcherData *> m_WatcherList;
+ CRITICAL_SECTION m_ListLock;
+ HANDLE m_hWatcherThread;
+ HANDLE m_WakeUpEvent;
+ HWND m_hwndSysTray;
+ bool m_Loop;
+
+public:
+ CIconWatcher();
+
+ virtual ~CIconWatcher();
+
+ bool Initialize(_In_ HWND hWndParent);
+ void Uninitialize();
+
+ bool AddIconToWatcher(_In_ CONST NOTIFYICONDATA *iconData);
+ bool RemoveIconFromWatcher(_In_ CONST NOTIFYICONDATA *iconData);
+
+ IconWatcherData* GetListEntry(_In_opt_ CONST NOTIFYICONDATA *iconData, _In_opt_ HANDLE hProcess, _In_ bool Remove);
+
+private:
+
+ static UINT WINAPI WatcherThread(_In_opt_ LPVOID lpParam);
+};
+
+class CBalloonQueue
+{
+public:
+ static const int TimerInterval = 2000;
+ static const int BalloonsTimerId = 1;
+ static const int MinTimeout = 10000;
+ static const int MaxTimeout = 30000;
+ static const int CooldownBetweenBalloons = 2000;
+
+private:
+ struct Info
+ {
+ InternalIconData * pSource;
+ WCHAR szInfo[256];
+ WCHAR szInfoTitle[64];
+ WPARAM uIcon;
+ UINT uTimeout;
+
+ Info(InternalIconData * source)
+ {
+ pSource = source;
+ StrNCpy(szInfo, source->szInfo, _countof(szInfo));
+ StrNCpy(szInfoTitle, source->szInfoTitle, _countof(szInfoTitle));
+ uIcon = source->dwInfoFlags & NIIF_ICON_MASK;
+ if (source->dwInfoFlags == NIIF_USER)
+ uIcon = reinterpret_cast<WPARAM>(source->hIcon);
+ uTimeout = source->uTimeout;
+ }
+ };
+
+ HWND m_hwndParent;
+
+ CTooltips * m_tooltips;
+
+ CAtlList<Info> m_queue;
+
+ CToolbar<InternalIconData> * m_toolbar;
+
+ InternalIconData * m_current;
+ bool m_currentClosed;
+
+ int m_timer;
+
+public:
+ CBalloonQueue();
+
+ void Init(HWND hwndParent, CToolbar<InternalIconData> * toolbar, CTooltips * balloons);
+ void Deinit();
+
+ bool OnTimer(int timerId);
+ void UpdateInfo(InternalIconData * notifyItem);
+ void RemoveInfo(InternalIconData * notifyItem);
+ void CloseCurrent();
+
+private:
+
+ int IndexOf(InternalIconData * pdata);
+ void SetTimer(int length);
+ void Show(Info& info);
+ void Close(IN OUT InternalIconData * notifyItem);
+};
+
+class CNotifyToolbar :
+ public CWindowImplBaseT< CToolbar<InternalIconData>, CControlWinTraits >
+{
+ HIMAGELIST m_ImageList;
+ int m_VisibleButtonCount;
+
+ CBalloonQueue * m_BalloonQueue;
+
+public:
+ CNotifyToolbar();
+ virtual ~CNotifyToolbar();
+
+ int GetVisibleButtonCount();
+ int FindItem(IN HWND hWnd, IN UINT uID, InternalIconData ** pdata);
+ int FindExistingSharedIcon(HICON handle);
+ BOOL AddButton(IN CONST NOTIFYICONDATA *iconData);
+ BOOL SwitchVersion(IN CONST NOTIFYICONDATA *iconData);
+ BOOL UpdateButton(IN CONST NOTIFYICONDATA *iconData);
+ BOOL RemoveButton(IN CONST NOTIFYICONDATA *iconData);
+ VOID ResizeImagelist();
+
+private:
+ VOID SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam);
+ LRESULT OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+ LRESULT OnTooltipShow(INT uCode, LPNMHDR hdr, BOOL& bHandled);
+
+public:
+ BEGIN_MSG_MAP(CNotifyToolbar)
+ MESSAGE_RANGE_HANDLER(WM_MOUSEFIRST, WM_MOUSELAST, OnMouseEvent)
+ NOTIFY_CODE_HANDLER(TTN_SHOW, OnTooltipShow)
+ END_MSG_MAP()
+
+ void Initialize(HWND hWndParent, CBalloonQueue * queue);
+};
+
+extern const WCHAR szSysPagerWndClass[];
+
+class CSysPagerWnd :
+ public CComCoClass<CSysPagerWnd>,
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public CWindowImpl < CSysPagerWnd, CWindow, CControlWinTraits >,
+ public IOleWindow,
+ public CIconWatcher
+{
+ CNotifyToolbar Toolbar;
+ CTooltips m_Balloons;
+ CBalloonQueue m_BalloonQueue;
+
+public:
+ CSysPagerWnd();
+ virtual ~CSysPagerWnd();
+
+ LRESULT DrawBackground(HDC hdc);
+ LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+ LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+ LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+ LRESULT OnGetInfoTip(INT uCode, LPNMHDR hdr, BOOL& bHandled);
+ LRESULT OnCustomDraw(INT uCode, LPNMHDR hdr, BOOL& bHandled);
+ LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+ LRESULT OnCtxMenu(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+ LRESULT OnBalloonPop(UINT uCode, LPNMHDR hdr, BOOL& bHandled);
+ LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+ LRESULT OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+ LRESULT OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+ LRESULT OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
+
+public:
+
+ HRESULT WINAPI GetWindow(HWND* phwnd)
+ {
+ if (!phwnd)
+ return E_INVALIDARG;
+ *phwnd = m_hWnd;
+ return S_OK;
+ }
+
+ HRESULT WINAPI ContextSensitiveHelp(BOOL fEnterMode)
+ {
+ return E_NOTIMPL;
+ }
+
+ DECLARE_NOT_AGGREGATABLE(CSysPagerWnd)
+
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+ BEGIN_COM_MAP(CSysPagerWnd)
+ COM_INTERFACE_ENTRY_IID(IID_IOleWindow, IOleWindow)
+ END_COM_MAP()
+
+ BOOL NotifyIcon(DWORD notify_code, _In_ CONST NOTIFYICONDATA *iconData);
+ void GetSize(IN BOOL IsHorizontal, IN PSIZE size);
+
+ DECLARE_WND_CLASS_EX(szSysPagerWndClass, CS_DBLCLKS, COLOR_3DFACE)
+
+ BEGIN_MSG_MAP(CSysPagerWnd)
+ MESSAGE_HANDLER(WM_CREATE, OnCreate)
+ MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+ MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
+ MESSAGE_HANDLER(WM_SIZE, OnSize)
+ MESSAGE_HANDLER(WM_CONTEXTMENU, OnCtxMenu)
+ MESSAGE_HANDLER(WM_TIMER, OnTimer)
+ MESSAGE_HANDLER(WM_COPYDATA, OnCopyData)
+ MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChanged)
+ MESSAGE_HANDLER(TNWM_GETMINIMUMSIZE, OnGetMinimumSize)
+ NOTIFY_CODE_HANDLER(TTN_POP, OnBalloonPop)
+ NOTIFY_CODE_HANDLER(TBN_GETINFOTIPW, OnGetInfoTip)
+ NOTIFY_CODE_HANDLER(NM_CUSTOMDRAW, OnCustomDraw)
+ END_MSG_MAP()
+
+ HRESULT Initialize(IN HWND hWndParent);
+};
+
CIconWatcher::CIconWatcher() :
m_hWatcherThread(NULL),
m_WakeUpEvent(NULL),
LeaveCriticalSection(&m_ListLock);
}
-bool CIconWatcher::AddIconToWatcher(_In_ NOTIFYICONDATA *iconData)
+bool CIconWatcher::AddIconToWatcher(_In_ CONST NOTIFYICONDATA *iconData)
{
DWORD ProcessId;
(void)GetWindowThreadProcessId(iconData->hWnd, &ProcessId);
return Added;
}
-bool CIconWatcher::RemoveIconFromWatcher(_In_ NOTIFYICONDATA *iconData)
+bool CIconWatcher::RemoveIconFromWatcher(_In_ CONST NOTIFYICONDATA *iconData)
{
EnterCriticalSection(&m_ListLock);
return true;
}
-IconWatcherData* CIconWatcher::GetListEntry(_In_opt_ NOTIFYICONDATA *iconData, _In_opt_ HANDLE hProcess, _In_ bool Remove)
+IconWatcherData* CIconWatcher::GetListEntry(_In_opt_ CONST NOTIFYICONDATA *iconData, _In_opt_ HANDLE hProcess, _In_ bool Remove)
{
IconWatcherData *Entry = NULL;
POSITION NextPosition = m_WatcherList.GetHeadPosition();
data.lpData = pnotify_data;
BOOL Success = FALSE;
- HWND parentHWND = ::GetParent(GetParent(This->m_hwndSysTray));
- if (parentHWND)
- Success = ::SendMessage(parentHWND, WM_COPYDATA, (WPARAM)&Icon->IconData, (LPARAM)&data);
+ ::SendMessage(This->m_hwndSysTray, WM_COPYDATA, (WPARAM)&Icon->IconData, (LPARAM)&data);
delete pnotify_data;
return -1;
}
-BOOL CNotifyToolbar::AddButton(IN CONST NOTIFYICONDATA *iconData)
+BOOL CNotifyToolbar::AddButton(_In_ CONST NOTIFYICONDATA *iconData)
{
TBBUTTON tbBtn;
InternalIconData * notifyItem;
return TRUE;
}
-BOOL CNotifyToolbar::SwitchVersion(IN CONST NOTIFYICONDATA *iconData)
+BOOL CNotifyToolbar::SwitchVersion(_In_ CONST NOTIFYICONDATA *iconData)
{
InternalIconData * notifyItem;
int index = FindItem(iconData->hWnd, iconData->uID, ¬ifyItem);
return TRUE;
}
-BOOL CNotifyToolbar::UpdateButton(IN CONST NOTIFYICONDATA *iconData)
+BOOL CNotifyToolbar::UpdateButton(_In_ CONST NOTIFYICONDATA *iconData)
{
InternalIconData * notifyItem;
TBBUTTONINFO tbbi = { 0 };
return TRUE;
}
-BOOL CNotifyToolbar::RemoveButton(IN CONST NOTIFYICONDATA *iconData)
+BOOL CNotifyToolbar::RemoveButton(_In_ CONST NOTIFYICONDATA *iconData)
{
InternalIconData * notifyItem;
RemoveButton(notifyItem);
- HWND parentHWND = ::GetParent(::GetParent(GetParent()));
- ::SendMessage(parentHWND, WM_SIZE, 0, 0);
+ /* Ask the parent to resize */
+ NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN};
+ GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
return;
}
tbm.dwMask = TBMF_BARPAD | TBMF_BUTTONSPACING | TBMF_PAD;
tbm.cxPad = 1;
tbm.cyPad = 1;
+ tbm.cxBarPad = 1;
+ tbm.cyBarPad = 1;
tbm.cxButtonSpacing = 1;
tbm.cyButtonSpacing = 1;
SetMetrics(&tbm);
CSysPagerWnd::CSysPagerWnd() {}
CSysPagerWnd::~CSysPagerWnd() {}
-LRESULT CSysPagerWnd::DrawBackground(HDC hdc)
-{
- RECT rect;
-
- GetClientRect(&rect);
- DrawThemeParentBackground(m_hWnd, hdc, &rect);
-
- return TRUE;
-}
-
LRESULT CSysPagerWnd::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
HDC hdc = (HDC) wParam;
return 0;
}
- return DrawBackground(hdc);
+ RECT rect;
+ GetClientRect(&rect);
+ DrawThemeParentBackground(m_hWnd, hdc, &rect);
+
+ return TRUE;
}
LRESULT CSysPagerWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
return TRUE;
}
-BOOL CSysPagerWnd::NotifyIconCmd(WPARAM wParam, LPARAM lParam)
+BOOL CSysPagerWnd::NotifyIcon(DWORD notify_code, _In_ CONST NOTIFYICONDATA *iconData)
{
- PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT) lParam;
- if (cpData->dwData == 1)
- {
- SYS_PAGER_COPY_DATA * data;
- NOTIFYICONDATA *iconData;
- BOOL ret = FALSE;
-
- int VisibleButtonCount = Toolbar.GetVisibleButtonCount();
+ BOOL ret = FALSE;
- data = (PSYS_PAGER_COPY_DATA) cpData->lpData;
- iconData = &data->nicon_data;
+ int VisibleButtonCount = Toolbar.GetVisibleButtonCount();
- TRACE("NotifyIconCmd received. Code=%d\n", data->notify_code);
- switch (data->notify_code)
+ TRACE("NotifyIcon received. Code=%d\n", notify_code);
+ switch (notify_code)
+ {
+ case NIM_ADD:
+ ret = Toolbar.AddButton(iconData);
+ if (ret == TRUE)
{
- case NIM_ADD:
- ret = Toolbar.AddButton(iconData);
- if (ret == TRUE)
- {
- (void)AddIconToWatcher(iconData);
- }
- break;
- case NIM_MODIFY:
- ret = Toolbar.UpdateButton(iconData);
- break;
- case NIM_DELETE:
- ret = Toolbar.RemoveButton(iconData);
- if (ret == TRUE)
- {
- (void)RemoveIconFromWatcher(iconData);
- }
- break;
- case NIM_SETFOCUS:
- Toolbar.SetFocus();
- ret = TRUE;
- case NIM_SETVERSION:
- ret = Toolbar.SwitchVersion(iconData);
- default:
- TRACE("NotifyIconCmd received with unknown code %d.\n", data->notify_code);
- return FALSE;
+ (void)AddIconToWatcher(iconData);
}
-
- if (VisibleButtonCount != Toolbar.GetVisibleButtonCount())
+ break;
+ case NIM_MODIFY:
+ ret = Toolbar.UpdateButton(iconData);
+ break;
+ case NIM_DELETE:
+ ret = Toolbar.RemoveButton(iconData);
+ if (ret == TRUE)
{
- HWND parentHWND = ::GetParent(GetParent());
- ::SendMessage(parentHWND, WM_SIZE, 0, 0);
+ (void)RemoveIconFromWatcher(iconData);
}
+ break;
+ case NIM_SETFOCUS:
+ Toolbar.SetFocus();
+ ret = TRUE;
+ case NIM_SETVERSION:
+ ret = Toolbar.SwitchVersion(iconData);
+ default:
+ TRACE("NotifyIcon received with unknown code %d.\n", notify_code);
+ return FALSE;
+ }
- return ret;
+ if (VisibleButtonCount != Toolbar.GetVisibleButtonCount())
+ {
+ /* Ask the parent to resize */
+ NMHDR nmh = {GetParent(), 0, NTNWM_REALIGN};
+ GetParent().SendMessage(WM_NOTIFY, 0, (LPARAM) &nmh);
}
- return TRUE;
+ return ret;
}
void CSysPagerWnd::GetSize(IN BOOL IsHorizontal, IN PSIZE size)
return 0;
}
-void CSysPagerWnd::ResizeImagelist()
+LRESULT CSysPagerWnd::OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
+{
+ PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam;
+ if (cpData->dwData == 1)
+ {
+ PSYS_PAGER_COPY_DATA pData = (PSYS_PAGER_COPY_DATA)cpData->lpData;
+ return NotifyIcon(pData->notify_code, &pData->nicon_data);
+ }
+
+ return FALSE;
+}
+
+LRESULT CSysPagerWnd::OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
- Toolbar.ResizeImagelist();
+ if (wParam == SPI_SETNONCLIENTMETRICS)
+ {
+ Toolbar.ResizeImagelist();
+ }
+ return 0;
}
-HWND CSysPagerWnd::_Init(IN HWND hWndParent, IN BOOL bVisible)
+LRESULT CSysPagerWnd::OnGetMinimumSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
- DWORD dwStyle;
+ GetSize((BOOL)wParam, (PSIZE)lParam);
+ return 0;
+}
+HRESULT CSysPagerWnd::Initialize(IN HWND hWndParent)
+{
/* Create the window. The tray window is going to move it to the correct
position and resize it as needed. */
- dwStyle = WS_CHILD | WS_CLIPSIBLINGS;
- if (bVisible)
- dwStyle |= WS_VISIBLE;
-
+ DWORD dwStyle = WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE;
Create(hWndParent, 0, NULL, dwStyle);
-
if (!m_hWnd)
- {
- return NULL;
- }
+ return E_FAIL;
SetWindowTheme(m_hWnd, L"TrayNotify", NULL);
- return m_hWnd;
+ return S_OK;
+}
+
+HRESULT CSysPagerWnd_CreateInstance(HWND hwndParent, REFIID riid, void **ppv)
+{
+ return ShellObjectCreatorInit<CSysPagerWnd>(hwndParent, riid, ppv);
}