[EXPLORER] Add missing 'break' statements. Spotted by mudhead. CORE-14518
[reactos.git] / base / shell / explorer / syspager.cpp
index 7a55360..5340ba4 100644 (file)
 
 #include "precomp.h"
 
-// Data comes from shell32/systray.cpp -> TrayNotifyCDS_Dummy
-typedef struct _SYS_PAGER_COPY_DATA
+struct InternalIconData : NOTIFYICONDATA
 {
-    DWORD           cookie;
-    DWORD           notify_code;
-    NOTIFYICONDATA  nicon_data;
-} SYS_PAGER_COPY_DATA, *PSYS_PAGER_COPY_DATA;
+    // 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 CNotifyToolbar;
+
+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;
+            StringCchCopy(szInfo, _countof(szInfo), source->szInfo);
+            StringCchCopy(szInfoTitle, _countof(szInfoTitle), source->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;
+
+    CNotifyToolbar * m_toolbar;
+
+    InternalIconData * m_current;
+    bool m_currentClosed;
+
+    int m_timer;
+
+public:
+    CBalloonQueue();
+
+    void Init(HWND hwndParent, CNotifyToolbar * 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, IN UINT uReason);
+};
+
+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();
+    bool SendNotifyCallback(InternalIconData* notifyItem, UINT uMsg);
+
+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 dwMessage, _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),
@@ -93,7 +318,7 @@ void CIconWatcher::Uninitialize()
     LeaveCriticalSection(&m_ListLock);
 }
 
-bool CIconWatcher::AddIconToWatcher(_In_ NOTIFYICONDATA *iconData)
+bool CIconWatcher::AddIconToWatcher(_In_ CONST NOTIFYICONDATA *iconData)
 {
     DWORD ProcessId;
     (void)GetWindowThreadProcessId(iconData->hWnd, &ProcessId);
@@ -107,7 +332,7 @@ bool CIconWatcher::AddIconToWatcher(_In_ NOTIFYICONDATA *iconData)
 
     IconWatcherData *Icon = new IconWatcherData(iconData);
     Icon->hProcess = hProcess;
-    Icon->ProcessId;
+    Icon->ProcessId = ProcessId;
 
     bool Added = false;
     EnterCriticalSection(&m_ListLock);
@@ -132,7 +357,7 @@ bool CIconWatcher::AddIconToWatcher(_In_ NOTIFYICONDATA *iconData)
     return Added;
 }
 
-bool CIconWatcher::RemoveIconFromWatcher(_In_ NOTIFYICONDATA *iconData)
+bool CIconWatcher::RemoveIconFromWatcher(_In_ CONST NOTIFYICONDATA *iconData)
 {
     EnterCriticalSection(&m_ListLock);
         
@@ -146,7 +371,7 @@ bool CIconWatcher::RemoveIconFromWatcher(_In_ NOTIFYICONDATA *iconData)
     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();
@@ -188,7 +413,7 @@ UINT WINAPI CIconWatcher::WatcherThread(_In_opt_ LPVOID lpParam)
         ASSERT(Size <= MAXIMUM_WAIT_OBJECTS);
 
         if (WatchList)
-            delete WatchList;
+            delete[] WatchList;
         WatchList = new HANDLE[Size];
         WatchList[0] = This->m_WakeUpEvent;
 
@@ -224,24 +449,18 @@ UINT WINAPI CIconWatcher::WatcherThread(_In_opt_ LPVOID lpParam)
 
             TRACE("Pid %lu owns a notification icon and has stopped without deleting it. We'll cleanup on its behalf", Icon->ProcessId);
 
-            int len = FIELD_OFFSET(SYS_PAGER_COPY_DATA, nicon_data) + Icon->IconData.cbSize;
-            PSYS_PAGER_COPY_DATA pnotify_data = (PSYS_PAGER_COPY_DATA)new BYTE[len];
-            pnotify_data->cookie = 1;
-            pnotify_data->notify_code = NIM_DELETE;
-            memcpy(&pnotify_data->nicon_data, &Icon->IconData, Icon->IconData.cbSize);
+            TRAYNOTIFYDATAW tnid = {0};
+            tnid.dwSignature = NI_NOTIFY_SIG;
+            tnid.dwMessage   = NIM_DELETE;
+            CopyMemory(&tnid.nid, &Icon->IconData, Icon->IconData.cbSize);
 
             COPYDATASTRUCT data;
             data.dwData = 1;
-            data.cbData = len;
-            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);
-
-            delete pnotify_data;
+            data.cbData = sizeof(tnid);
+            data.lpData = &tnid;
 
+            BOOL Success = ::SendMessage(This->m_hwndSysTray, WM_COPYDATA,
+                                         (WPARAM)&Icon->IconData, (LPARAM)&data);
             if (!Success)
             {
                 // If we failed to handle the delete message, forcibly remove it
@@ -260,7 +479,7 @@ UINT WINAPI CIconWatcher::WatcherThread(_In_opt_ LPVOID lpParam)
     }
 
     if (WatchList)
-        delete WatchList;
+        delete[] WatchList;
 
     return 0;
 }
@@ -279,7 +498,7 @@ CBalloonQueue::CBalloonQueue() :
 {
 }
 
-void CBalloonQueue::Init(HWND hwndParent, CToolbar<InternalIconData> * toolbar, CTooltips * balloons)
+void CBalloonQueue::Init(HWND hwndParent, CNotifyToolbar * toolbar, CTooltips * balloons)
 {
     m_hwndParent = hwndParent;
     m_toolbar = toolbar;
@@ -304,7 +523,7 @@ bool CBalloonQueue::OnTimer(int timerId)
 
     if (m_current && !m_currentClosed)
     {
-        Close(m_current);
+        Close(m_current, NIN_BALLOONTIMEOUT);
     }
     else
     {
@@ -340,13 +559,13 @@ void CBalloonQueue::UpdateInfo(InternalIconData * notifyItem)
     }
     else
     {
-        Close(notifyItem);
+        Close(notifyItem, NIN_BALLOONHIDE);
     }
 }
 
 void CBalloonQueue::RemoveInfo(InternalIconData * notifyItem)
 {
-    Close(notifyItem);
+    Close(notifyItem, NIN_BALLOONHIDE);
 
     POSITION position = m_queue.GetHeadPosition();
     while(position != NULL)
@@ -362,7 +581,9 @@ void CBalloonQueue::RemoveInfo(InternalIconData * notifyItem)
 void CBalloonQueue::CloseCurrent()
 {
     if (m_current != NULL)
-        Close(m_current);
+    {
+        Close(m_current, NIN_BALLOONTIMEOUT);
+    }
 }
 
 int CBalloonQueue::IndexOf(InternalIconData * pdata)
@@ -405,14 +626,18 @@ void CBalloonQueue::Show(Info& info)
     if (timeout > MaxTimeout) timeout = MaxTimeout;
 
     SetTimer(timeout);
+
+    m_toolbar->SendNotifyCallback(m_current, NIN_BALLOONSHOW);
 }
 
-void CBalloonQueue::Close(IN OUT InternalIconData * notifyItem)
+void CBalloonQueue::Close(IN OUT InternalIconData * notifyItem, IN UINT uReason)
 {
     TRACE("HideBalloonTip called\n");
 
     if (m_current == notifyItem && !m_currentClosed)
     {
+        m_toolbar->SendNotifyCallback(m_current, uReason);
+
         // Prevent Re-entry
         m_currentClosed = true;
         m_tooltips->TrackDeactivate();
@@ -477,9 +702,9 @@ int CNotifyToolbar::FindExistingSharedIcon(HICON handle)
     return -1;
 }
 
-BOOL CNotifyToolbar::AddButton(IN CONST NOTIFYICONDATA *iconData)
+BOOL CNotifyToolbar::AddButton(_In_ CONST NOTIFYICONDATA *iconData)
 {
-    TBBUTTON tbBtn;
+    TBBUTTON tbBtn = { 0 };
     InternalIconData * notifyItem;
     WCHAR text[] = L"";
 
@@ -547,8 +772,8 @@ BOOL CNotifyToolbar::AddButton(IN CONST NOTIFYICONDATA *iconData)
     if (iconData->uFlags & NIF_INFO)
     {
         // NOTE: In Vista+, the uTimeout value is disregarded, and the accessibility settings are used always.
-        StrNCpy(notifyItem->szInfo, iconData->szInfo, _countof(notifyItem->szInfo));
-        StrNCpy(notifyItem->szInfoTitle, iconData->szInfoTitle, _countof(notifyItem->szInfo));
+        StringCchCopy(notifyItem->szInfo, _countof(notifyItem->szInfo), iconData->szInfo);
+        StringCchCopy(notifyItem->szInfoTitle, _countof(notifyItem->szInfoTitle), iconData->szInfoTitle);
         notifyItem->dwInfoFlags = iconData->dwInfoFlags;
         notifyItem->uTimeout = iconData->uTimeout;
     }
@@ -575,7 +800,7 @@ BOOL CNotifyToolbar::AddButton(IN CONST NOTIFYICONDATA *iconData)
     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, &notifyItem);
@@ -598,7 +823,7 @@ BOOL CNotifyToolbar::SwitchVersion(IN CONST NOTIFYICONDATA *iconData)
     return TRUE;
 }
 
-BOOL CNotifyToolbar::UpdateButton(IN CONST NOTIFYICONDATA *iconData)
+BOOL CNotifyToolbar::UpdateButton(_In_ CONST NOTIFYICONDATA *iconData)
 {
     InternalIconData * notifyItem;
     TBBUTTONINFO tbbi = { 0 };
@@ -685,8 +910,8 @@ BOOL CNotifyToolbar::UpdateButton(IN CONST NOTIFYICONDATA *iconData)
     if (iconData->uFlags & NIF_INFO)
     {
         // NOTE: In Vista+, the uTimeout value is disregarded, and the accessibility settings are used always.
-        StrNCpy(notifyItem->szInfo, iconData->szInfo, _countof(notifyItem->szInfo));
-        StrNCpy(notifyItem->szInfoTitle, iconData->szInfoTitle, _countof(notifyItem->szInfo));
+        StringCchCopy(notifyItem->szInfo, _countof(notifyItem->szInfo), iconData->szInfo);
+        StringCchCopy(notifyItem->szInfoTitle, _countof(notifyItem->szInfoTitle), iconData->szInfoTitle);
         notifyItem->dwInfoFlags = iconData->dwInfoFlags;
         notifyItem->uTimeout = iconData->uTimeout;
     }
@@ -703,7 +928,7 @@ BOOL CNotifyToolbar::UpdateButton(IN CONST NOTIFYICONDATA *iconData)
     return TRUE;
 }
 
-BOOL CNotifyToolbar::RemoveButton(IN CONST NOTIFYICONDATA *iconData)
+BOOL CNotifyToolbar::RemoveButton(_In_ CONST NOTIFYICONDATA *iconData)
 {
     InternalIconData * notifyItem;
 
@@ -790,27 +1015,8 @@ VOID CNotifyToolbar::ResizeImagelist()
     SetButtonSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
 }
 
-VOID CNotifyToolbar::SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam)
+bool CNotifyToolbar::SendNotifyCallback(InternalIconData* notifyItem, UINT uMsg)
 {
-    static LPCWSTR eventNames [] = {
-        L"WM_MOUSEMOVE",
-        L"WM_LBUTTONDOWN",
-        L"WM_LBUTTONUP",
-        L"WM_LBUTTONDBLCLK",
-        L"WM_RBUTTONDOWN",
-        L"WM_RBUTTONUP",
-        L"WM_RBUTTONDBLCLK",
-        L"WM_MBUTTONDOWN",
-        L"WM_MBUTTONUP",
-        L"WM_MBUTTONDBLCLK",
-        L"WM_MOUSEWHEEL",
-        L"WM_XBUTTONDOWN",
-        L"WM_XBUTTONUP",
-        L"WM_XBUTTONDBLCLK"
-    };
-
-    InternalIconData * notifyItem = GetItemData(wIndex);
-
     if (!::IsWindow(notifyItem->hWnd))
     {
         // We detect and destroy icons with invalid handles only on mouse move over systray, same as MS does.
@@ -819,17 +1025,11 @@ VOID CNotifyToolbar::SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wPar
 
         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;
-    }
-
-    if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
-    {
-        TRACE("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n",
-                    eventNames[uMsg - WM_MOUSEFIRST], wIndex,
-                    notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, uMsg);
+        return true;
     }
 
     DWORD pid;
@@ -839,9 +1039,9 @@ VOID CNotifyToolbar::SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wPar
         (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST))
     {
         ::PostMessage(notifyItem->hWnd,
-                        notifyItem->uCallbackMessage,
-                        notifyItem->uID,
-                        uMsg);
+                      notifyItem->uCallbackMessage,
+                      notifyItem->uID,
+                      uMsg);
     }
     else
     {
@@ -850,6 +1050,38 @@ VOID CNotifyToolbar::SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wPar
                     notifyItem->uID,
                     uMsg);
     }
+    return false;
+}
+
+VOID CNotifyToolbar::SendMouseEvent(IN WORD wIndex, IN UINT uMsg, IN WPARAM wParam)
+{
+    static LPCWSTR eventNames [] = {
+        L"WM_MOUSEMOVE",
+        L"WM_LBUTTONDOWN",
+        L"WM_LBUTTONUP",
+        L"WM_LBUTTONDBLCLK",
+        L"WM_RBUTTONDOWN",
+        L"WM_RBUTTONUP",
+        L"WM_RBUTTONDBLCLK",
+        L"WM_MBUTTONDOWN",
+        L"WM_MBUTTONUP",
+        L"WM_MBUTTONDBLCLK",
+        L"WM_MOUSEWHEEL",
+        L"WM_XBUTTONDOWN",
+        L"WM_XBUTTONUP",
+        L"WM_XBUTTONDBLCLK"
+    };
+
+    InternalIconData * notifyItem = GetItemData(wIndex);
+
+    if (uMsg >= WM_MOUSEFIRST && uMsg <= WM_MOUSELAST)
+    {
+        TRACE("Sending message %S from button %d to %p (msg=%x, w=%x, l=%x)...\n",
+            eventNames[uMsg - WM_MOUSEFIRST], wIndex,
+            notifyItem->hWnd, notifyItem->uCallbackMessage, notifyItem->uID, uMsg);
+    }
+
+    SendNotifyCallback(notifyItem, uMsg);
 }
 
 LRESULT CNotifyToolbar::OnMouseEvent(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
@@ -960,6 +1192,8 @@ void CNotifyToolbar::Initialize(HWND hWndParent, CBalloonQueue * queue)
     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);
@@ -973,17 +1207,8 @@ void CNotifyToolbar::Initialize(HWND hWndParent, CBalloonQueue * queue)
 const WCHAR szSysPagerWndClass[] = L"SysPager";
 
 CSysPagerWnd::CSysPagerWnd() {}
-CSysPagerWnd::~CSysPagerWnd() {}
 
-LRESULT CSysPagerWnd::DrawBackground(HDC hdc)
-{
-    RECT rect;
-
-    GetClientRect(&rect);
-    DrawThemeParentBackground(m_hWnd, hdc, &rect);
-
-    return TRUE;
-}
+CSysPagerWnd::~CSysPagerWnd() {}
 
 LRESULT CSysPagerWnd::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 {
@@ -995,7 +1220,11 @@ LRESULT CSysPagerWnd::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam,
         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)
@@ -1038,60 +1267,57 @@ LRESULT CSysPagerWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& b
     return TRUE;
 }
 
-BOOL CSysPagerWnd::NotifyIconCmd(WPARAM wParam, LPARAM lParam)
+BOOL CSysPagerWnd::NotifyIcon(DWORD dwMessage, _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", dwMessage);
+    switch (dwMessage)
+    {
+    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);
         }
+        break;
+
+    case NIM_MODIFY:
+        ret = Toolbar.UpdateButton(iconData);
+        break;
 
-        if (VisibleButtonCount != Toolbar.GetVisibleButtonCount())
+    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;
+        break;
+
+    case NIM_SETVERSION:
+        ret = Toolbar.SwitchVersion(iconData);
+        break;
+
+    default:
+        TRACE("NotifyIcon received with unknown code %d.\n", dwMessage);
+        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)
@@ -1200,29 +1426,51 @@ LRESULT CSysPagerWnd::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHa
     return 0;
 }
 
-void CSysPagerWnd::ResizeImagelist()
+LRESULT CSysPagerWnd::OnCopyData(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 {
-    Toolbar.ResizeImagelist();
+    PCOPYDATASTRUCT cpData = (PCOPYDATASTRUCT)lParam;
+    if (cpData->dwData == 1)
+    {
+        /* A taskbar NotifyIcon notification */
+        PTRAYNOTIFYDATAW pData = (PTRAYNOTIFYDATAW)cpData->lpData;
+        if (pData->dwSignature == NI_NOTIFY_SIG)
+            return NotifyIcon(pData->dwMessage, &pData->nid);
+    }
+    // TODO: Handle other types of taskbar notifications
+
+    return FALSE;
+}
+
+LRESULT CSysPagerWnd::OnSettingChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
+{
+    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);
 }