[EXPLORER] CNotifyToolbar: Set TBMETRICS::cxBarPad and TBMETRICS::cyBarPad
[reactos.git] / base / shell / explorer / syspager.cpp
index 7a55360..4656d8f 100644 (file)
@@ -29,6 +29,236 @@ typedef struct _SYS_PAGER_COPY_DATA
     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),
@@ -93,7 +323,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);
@@ -132,7 +362,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 +376,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();
@@ -236,9 +466,7 @@ UINT WINAPI CIconWatcher::WatcherThread(_In_opt_ LPVOID lpParam)
             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;
 
@@ -477,7 +705,7 @@ int CNotifyToolbar::FindExistingSharedIcon(HICON handle)
     return -1;
 }
 
-BOOL CNotifyToolbar::AddButton(IN CONST NOTIFYICONDATA *iconData)
+BOOL CNotifyToolbar::AddButton(_In_ CONST NOTIFYICONDATA *iconData)
 {
     TBBUTTON tbBtn;
     InternalIconData * notifyItem;
@@ -575,7 +803,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 +826,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 };
@@ -703,7 +931,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;
 
@@ -819,8 +1047,9 @@ 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;
     }
@@ -960,6 +1189,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);
@@ -975,16 +1206,6 @@ 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;
-}
-
 LRESULT CSysPagerWnd::OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
 {
     HDC hdc = (HDC) wParam;
@@ -995,7 +1216,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 +1263,50 @@ LRESULT CSysPagerWnd::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& b
     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)
@@ -1200,29 +1415,48 @@ 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)
+{
+    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);
 }