///////////////////////////////////////////////////////////////////////////// // Name: src/msw/taskbarbutton.cpp // Purpose: Implements wxTaskBarButtonImpl class for manipulating buttons on // the Windows taskbar. // Author: Chaobin Zhang // Created: 2014-06-01 // Copyright: (c) 2014 wxWidgets development team // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/icon.h" #include "wx/toplevel.h" #endif #if wxUSE_TASKBARBUTTON #ifdef _MSC_VER #pragma comment( lib, "shlwapi" ) #endif #include "wx/msw/private.h" #include "wx/msw/taskbarbutton.h" #include "wx/scopedptr.h" #include "wx/msw/private/comptr.h" #include "wx/msw/private/cotaskmemptr.h" #include #include #if wxUSE_DYNLIB_CLASS #include "wx/dynlib.h" #endif // wxUSE_DYNLIB_CLASS // ---------------------------------------------------------------------------- // Redefine the interfaces: ITaskbarList3, IObjectCollection, // ICustomDestinationList, IShellLink, IShellItem, IApplicationDocumentLists // etc. // ---------------------------------------------------------------------------- WINOLEAPI PropVariantClear(PROPVARIANT* pvar); #ifndef PropVariantInit #define PropVariantInit(pvar) memset ( (pvar), 0, sizeof(PROPVARIANT) ) #endif #ifndef INFOTIPSIZE #define INFOTIPSIZE 1024 #endif namespace { // The maximum number of thumbnail toolbar buttons allowed on windows is 7. static const int MAX_BUTTON_COUNT = 7; DEFINE_GUID(wxCLSID_TaskbarList, 0x56fdf344, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x0, 0x60, 0x97, 0xc9, 0xa0, 0x90); DEFINE_GUID(wxCLSID_DestinationList, 0x77f10cf0, 0x3db5, 0x4966, 0xb5, 0x20, 0xb7, 0xc5, 0x4f, 0xd3,0x5e, 0xd6); DEFINE_GUID(wxCLSID_EnumerableObjectCollection, 0x2d3468c1, 0x36a7, 0x43b6, 0xac, 0x24, 0xd3, 0xf0, 0x2f, 0xd9, 0x60, 0x7a); DEFINE_GUID(wxCLSID_ShellLink, 0x00021401, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); DEFINE_GUID(wxIID_ICustomDestinationList, 0x6332debf, 0x87b5, 0x4670, 0x90, 0xc0, 0x5e, 0x57, 0xb4, 0x08, 0xa4, 0x9e); DEFINE_GUID(wxIID_ITaskbarList3, 0xea1afb91, 0x9e28, 0x4b86, 0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf); DEFINE_GUID(wxIID_IPropertyStore, 0x886d8eeb, 0x8cf2, 0x4446, 0x8d, 0x02, 0xcd, 0xba, 0x1d, 0xbd, 0xcf, 0x99); DEFINE_GUID(wxIID_IObjectArray, 0x92ca9dcd, 0x5622, 0x4bba, 0xa8, 0x05, 0x5e, 0x9f, 0x54, 0x1b, 0xd8, 0xc9); DEFINE_GUID(wxIID_IObjectCollection, 0x5632b1a4, 0xe38a, 0x400a, 0x92, 0x8a, 0xd4, 0xcd, 0x63, 0x23, 0x02, 0x95); DEFINE_GUID(wxIID_IApplicationDocumentLists, 0x3c594f9f, 0x9f30, 0x47a1, 0x97, 0x9a, 0xc9, 0xe8, 0x3d, 0x3d, 0x0a, 0x06); DEFINE_GUID(wxCLSID_ApplicationDocumentLists, 0x86bec222, 0x30f2, 0x47e0, 0x9f, 0x25, 0x60, 0xd1, 0x1c, 0xd7, 0x5c, 0x28); DEFINE_GUID(wxIID_IUnknown, 0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); DEFINE_GUID(wxIID_IShellItem, 0x43826d1e, 0xe718, 0x42ee, 0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe); typedef IUnknown *HIMAGELIST; typedef enum THUMBBUTTONFLAGS { THBF_ENABLED = 0, THBF_DISABLED = 0x1, THBF_DISMISSONCLICK = 0x2, THBF_NOBACKGROUND = 0x4, THBF_HIDDEN = 0x8, THBF_NONINTERACTIVE = 0x10 } THUMBBUTTONFLAGS; typedef enum THUMBBUTTONMASK { THB_BITMAP = 0x1, THB_ICON = 0x2, THB_TOOLTIP = 0x4, THB_FLAGS = 0x8 } THUMBBUTTONMASK; typedef struct THUMBBUTTON { THUMBBUTTONMASK dwMask; UINT iId; UINT iBitmap; HICON hIcon; WCHAR szTip[260]; THUMBBUTTONFLAGS dwFlags; } THUMBBUTTON; typedef struct THUMBBUTTON *LPTHUMBBUTTON; typedef enum TBPFLAG { TBPF_NOPROGRESS = 0, TBPF_INDETERMINATE = 0x1, TBPF_NORMAL = 0x2, TBPF_ERROR = 0x4, TBPF_PAUSED = 0x8 } TBPFLAG; #ifndef PROPERTYKEY_DEFINED typedef struct _tagpropertykey { GUID fmtid; DWORD pid; } PROPERTYKEY; #endif // !PROPERTYKEY_DEFINED #define REFPROPERTYKEY const PROPERTYKEY & #define DEFINE_PROPERTYKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) \ const PROPERTYKEY name = \ { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid } DEFINE_PROPERTYKEY(PKEY_Title, 0xf29f85e0, 0x4ff9, 0x1068, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9, 2); DEFINE_PROPERTYKEY(PKEY_AppUserModel_IsDestListSeparator, 0x9f4c2855, 0x9f79, 0x4b39, 0xa8, 0xd0, 0xe1, 0xd4, 0x2d, 0xe1, 0xd5, 0xf3, 6); DEFINE_PROPERTYKEY(PKEY_Link_Arguments, 0x436f2667, 0x14e2, 0x4feb, 0xb3, 0x0a, 0x14, 0x6c, 0x53, 0xb5, 0xb6, 0x74, 100); #ifdef wxUSE_UNICODE #define IShellLink wxIShellLinkW DEFINE_GUID(wxIID_IShellLink, 0x000214F9, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); #else #define IShellLink wxIShellLinkA DEFINE_GUID(wxIID_IShellLink, 0x000214EE, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); #endif // wxUSE_UNICODE typedef enum _SIGDN { SIGDN_NORMALDISPLAY = 0, SIGDN_PARENTRELATIVEPARSING = (int)0x80018001, SIGDN_DESKTOPABSOLUTEPARSING = (int)0x80028000, SIGDN_PARENTRELATIVEEDITING = (int)0x80031001, SIGDN_DESKTOPABSOLUTEEDITING = (int)0x8004c000, SIGDN_FILESYSPATH = (int)0x80058000, SIGDN_URL = (int)0x80068000, SIGDN_PARENTRELATIVEFORADDRESSBAR = (int)0x8007c001, SIGDN_PARENTRELATIVE = (int)0x80080001 } SIGDN; enum _SICHINTF { SICHINT_DISPLAY = 0, SICHINT_ALLFIELDS = (int)0x80000000, SICHINT_CANONICAL = 0x10000000, SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL = 0x20000000 }; typedef DWORD SICHINTF; typedef ULONG SFGAOF; typedef enum KNOWNDESTCATEGORY { KDC_FREQUENT = 1, KDC_RECENT = ( KDC_FREQUENT + 1 ) } KNOWNDESTCATEGORY; typedef enum APPDOCLISTTYPE { ADLT_RECENT = 0, ADLT_FREQUENT = ( ADLT_RECENT + 1 ) } APPDOCLISTTYPE; } // anonymous namespace class wxITaskbarList : public IUnknown { public: virtual HRESULT wxSTDCALL HrInit() = 0; virtual HRESULT wxSTDCALL AddTab(HWND) = 0; virtual HRESULT wxSTDCALL DeleteTab(HWND) = 0; virtual HRESULT wxSTDCALL ActivateTab(HWND) = 0; virtual HRESULT wxSTDCALL SetActiveAlt(HWND) = 0; }; class wxITaskbarList2 : public wxITaskbarList { public: virtual HRESULT wxSTDCALL MarkFullscreenWindow(HWND, BOOL) = 0; }; class wxIShellLinkA : public IUnknown { public: virtual HRESULT wxSTDCALL GetPath(LPSTR, int, WIN32_FIND_DATAA*, DWORD) = 0; virtual HRESULT wxSTDCALL GetIDList(LPITEMIDLIST *ppidl) = 0; virtual HRESULT wxSTDCALL SetIDList(LPCITEMIDLIST pidl) = 0; virtual HRESULT wxSTDCALL GetDescription(LPSTR, int) = 0; virtual HRESULT wxSTDCALL SetDescription(LPCSTR) = 0; virtual HRESULT wxSTDCALL GetWorkingDirectory(LPSTR, int) = 0; virtual HRESULT wxSTDCALL SetWorkingDirectory(LPCSTR) = 0; virtual HRESULT wxSTDCALL GetArguments(LPSTR, int) = 0; virtual HRESULT wxSTDCALL SetArguments(LPCSTR) = 0; virtual HRESULT wxSTDCALL GetHotkey(WORD*) = 0; virtual HRESULT wxSTDCALL SetHotkey(WORD) = 0; virtual HRESULT wxSTDCALL GetShowCmd(int*) = 0; virtual HRESULT wxSTDCALL SetShowCmd(int) = 0; virtual HRESULT wxSTDCALL GetIconLocation(LPSTR, int, int*) = 0; virtual HRESULT wxSTDCALL SetIconLocation(LPCSTR, int) = 0; virtual HRESULT wxSTDCALL SetRelativePath(LPCSTR, DWORD) = 0; virtual HRESULT wxSTDCALL Resolve(HWND, DWORD) = 0; virtual HRESULT wxSTDCALL SetPath(LPCSTR) = 0; }; class wxIShellLinkW : public IUnknown { public: virtual HRESULT wxSTDCALL GetPath(LPWSTR, int, WIN32_FIND_DATAW*, DWORD) = 0; virtual HRESULT wxSTDCALL GetIDList(LPITEMIDLIST *ppidl) = 0; virtual HRESULT wxSTDCALL SetIDList(LPCITEMIDLIST pidl) = 0; virtual HRESULT wxSTDCALL GetDescription(LPWSTR, int) = 0; virtual HRESULT wxSTDCALL SetDescription(LPCWSTR) = 0; virtual HRESULT wxSTDCALL GetWorkingDirectory(LPWSTR, int) = 0; virtual HRESULT wxSTDCALL SetWorkingDirectory(LPCWSTR) = 0; virtual HRESULT wxSTDCALL GetArguments(LPWSTR, int) = 0; virtual HRESULT wxSTDCALL SetArguments(LPCWSTR) = 0; virtual HRESULT wxSTDCALL GetHotkey(WORD*) = 0; virtual HRESULT wxSTDCALL SetHotkey(WORD) = 0; virtual HRESULT wxSTDCALL GetShowCmd(int*) = 0; virtual HRESULT wxSTDCALL SetShowCmd(int) = 0; virtual HRESULT wxSTDCALL GetIconLocation(LPWSTR, int, int*) = 0; virtual HRESULT wxSTDCALL SetIconLocation(LPCWSTR, int) = 0; virtual HRESULT wxSTDCALL SetRelativePath(LPCWSTR, DWORD) = 0; virtual HRESULT wxSTDCALL Resolve(HWND, DWORD) = 0; virtual HRESULT wxSTDCALL SetPath(LPCWSTR) = 0; }; class IShellItem : public IUnknown { public: virtual HRESULT wxSTDCALL BindToHandler(IBindCtx*, REFGUID, REFIID, void **) = 0; virtual HRESULT wxSTDCALL GetParent(IShellItem **) = 0; virtual HRESULT wxSTDCALL GetDisplayName(SIGDN, LPWSTR*) = 0; virtual HRESULT wxSTDCALL GetAttributes(SFGAOF, SFGAOF*) = 0; virtual HRESULT wxSTDCALL Compare(IShellItem *, SICHINTF, int *) = 0; }; class IObjectArray : public IUnknown { public: virtual HRESULT wxSTDCALL GetCount(UINT*) = 0; virtual HRESULT wxSTDCALL GetAt(UINT, REFIID, void **) = 0; }; class IObjectCollection : public IObjectArray { public: virtual HRESULT wxSTDCALL AddObject(IUnknown *) = 0; virtual HRESULT wxSTDCALL AddFromArray(IObjectArray *) = 0; virtual HRESULT wxSTDCALL RemoveObjectAt(UINT) = 0; virtual HRESULT wxSTDCALL Clear() = 0; }; class IPropertyStore : public IUnknown { public: virtual HRESULT wxSTDCALL GetCount(DWORD *) = 0; virtual HRESULT wxSTDCALL GetAt(DWORD, PROPERTYKEY *) = 0; virtual HRESULT wxSTDCALL GetValue(REFPROPERTYKEY, PROPVARIANT *) = 0; virtual HRESULT wxSTDCALL SetValue(REFPROPERTYKEY, const PROPVARIANT&) = 0; virtual HRESULT wxSTDCALL Commit() = 0; }; class ICustomDestinationList : public IUnknown { public: virtual HRESULT wxSTDCALL SetAppID(LPCWSTR) = 0; virtual HRESULT wxSTDCALL BeginList(UINT*, REFIID, void**) = 0; virtual HRESULT wxSTDCALL AppendCategory(LPCWSTR, IObjectArray *) = 0; virtual HRESULT wxSTDCALL AppendKnownCategory(KNOWNDESTCATEGORY) = 0; virtual HRESULT wxSTDCALL AddUserTasks(IObjectArray *) = 0; virtual HRESULT wxSTDCALL CommitList() = 0; virtual HRESULT wxSTDCALL GetRemovedDestinations(REFIID, void**) = 0; virtual HRESULT wxSTDCALL DeleteList(LPCWSTR) = 0; virtual HRESULT wxSTDCALL AbortList() = 0; }; class IApplicationDocumentLists : public IUnknown { public: virtual HRESULT wxSTDCALL SetAppID(LPCWSTR) = 0; virtual HRESULT wxSTDCALL GetList(APPDOCLISTTYPE, UINT, REFIID, void**) = 0; }; namespace { inline HRESULT InitPropVariantFromBoolean(BOOL fVal, PROPVARIANT *ppropvar) { ppropvar->vt = VT_BOOL; ppropvar->boolVal = fVal ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } inline HRESULT InitPropVariantFromString(PCWSTR psz, PROPVARIANT *ppropvar) { HRESULT hr = E_FAIL; ppropvar->vt = VT_LPWSTR; #if wxUSE_DYNLIB_CLASS typedef HRESULT (WINAPI *SHStrDupW_t)(LPCWSTR, LPWSTR*); static SHStrDupW_t s_pfnSHStrDupW = NULL; if ( !s_pfnSHStrDupW ) { wxDynamicLibrary dll(wxT("shlwapi.dll")); if ( dll.IsLoaded() ) { s_pfnSHStrDupW = (SHStrDupW_t)dll.GetSymbol(wxT("SHStrDupW")); } } if ( s_pfnSHStrDupW ) { hr = s_pfnSHStrDupW(psz, &ppropvar->pwszVal); } #elif defined (_MSC_VER) hr = SHStrDupW(psz, &ppropvar->pwszVal); #else wxUnusedVar(psz); #endif if ( FAILED(hr) ) { PropVariantInit(ppropvar); } return hr; } THUMBBUTTONFLAGS GetNativeThumbButtonFlags(const wxThumbBarButton& button) { WXUINT flags = 0; flags |= (button.IsEnable() ? THBF_ENABLED : THBF_DISABLED); if ( button.IsDismissOnClick() ) flags |= THBF_DISMISSONCLICK; if ( !button.HasBackground() ) flags |= THBF_NOBACKGROUND; if ( !button.IsShown() ) flags |= THBF_HIDDEN; if ( !button.IsInteractive() ) flags |= THBF_NONINTERACTIVE; return static_cast(flags); } bool AddShellLink(IObjectCollection *collection, const wxTaskBarJumpListItem& item) { wxCOMPtr shellLink; wxCOMPtr propertyStore; HRESULT hr = CoCreateInstance ( wxCLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, wxIID_IShellLink, reinterpret_cast (&(shellLink)) ); if ( FAILED(hr) ) { wxLogApiError("CoCreateInstance(wxCLSID_ShellLink)", hr); return false; } if ( item.GetType() == wxTASKBAR_JUMP_LIST_TASK || item.GetType() == wxTASKBAR_JUMP_LIST_DESTINATION ) { if ( !item.GetFilePath().IsEmpty() ) shellLink->SetPath(item.GetFilePath().wc_str()); if ( !item.GetArguments().IsEmpty() ) shellLink->SetArguments(item.GetArguments().wc_str()); if ( !item.GetIconPath().IsEmpty() ) { shellLink->SetIconLocation(item.GetIconPath().wc_str(), item.GetIconIndex()); } if ( !item.GetTooltip().IsEmpty() ) shellLink->SetDescription(item.GetTooltip().wc_str()); } hr = shellLink->QueryInterface(wxIID_IPropertyStore, reinterpret_cast(&(propertyStore))); if ( FAILED(hr) ) { wxLogApiError("IShellLink(QueryInterface)", hr); return false; } PROPVARIANT pv; if ( item.GetType() == wxTASKBAR_JUMP_LIST_TASK || item.GetType() == wxTASKBAR_JUMP_LIST_DESTINATION ) { hr = InitPropVariantFromString(item.GetTitle().wc_str(), &pv); if ( SUCCEEDED(hr) ) { hr = propertyStore->SetValue(PKEY_Title, pv); } } else if ( item.GetType() == wxTASKBAR_JUMP_LIST_SEPARATOR ) { hr = InitPropVariantFromBoolean(TRUE, &pv); if ( SUCCEEDED(hr) ) { hr = propertyStore->SetValue(PKEY_AppUserModel_IsDestListSeparator, pv); } } // Save the changes we made to the property store. propertyStore->Commit(); PropVariantClear(&pv); // Add this IShellLink object to the given collection. hr = collection->AddObject(shellLink); return SUCCEEDED(hr); } wxTaskBarJumpListItem* GetItemFromIShellLink(IShellLink* link) { if ( !link ) return NULL; wxTaskBarJumpListItem* item = new wxTaskBarJumpListItem(NULL, wxTASKBAR_JUMP_LIST_DESTINATION); wxCOMPtr linkProps; HRESULT hr = link->QueryInterface ( wxIID_IPropertyStore, reinterpret_cast(&linkProps) ); if ( FAILED(hr) ) { wxLogApiError("IShellLink::QueryInterface", hr); return NULL; } PROPVARIANT var; linkProps->GetValue(PKEY_Link_Arguments, &var); item->SetArguments(wxString(var.pwszVal)); PropVariantClear(&var); const int bufferSize = 2048; wchar_t buffer[bufferSize]; link->GetDescription(buffer, INFOTIPSIZE); item->SetTooltip(wxString(buffer)); int dummyIndex; link->GetIconLocation(buffer, bufferSize - 1, &dummyIndex); item->SetIconPath(wxString(buffer)); link->GetPath(buffer, bufferSize - 1, NULL, 0x1); item->SetFilePath(wxString(buffer)); return item; } wxTaskBarJumpListItem* GetItemFromIShellItem(IShellItem *shellItem) { if ( !shellItem ) return NULL; wxTaskBarJumpListItem *item = new wxTaskBarJumpListItem(NULL, wxTASKBAR_JUMP_LIST_DESTINATION); wxCoTaskMemPtr name; shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name); item->SetFilePath(wxString(name)); return item; } IObjectCollection* CreateObjectCollection() { IObjectCollection* collection; HRESULT hr; hr = CoCreateInstance ( wxCLSID_EnumerableObjectCollection, NULL, CLSCTX_INPROC, wxIID_IObjectCollection, reinterpret_cast(&(collection)) ); if ( FAILED(hr) ) { wxLogApiError("CoCreateInstance(wxCLSID_EnumerableObjectCollection)", hr); return NULL; } return collection; } } // namespace class wxITaskbarList3 : public wxITaskbarList2 { public: virtual HRESULT wxSTDCALL SetProgressValue(HWND, ULONGLONG, ULONGLONG) = 0; virtual HRESULT wxSTDCALL SetProgressState(HWND, TBPFLAG) = 0; virtual HRESULT wxSTDCALL RegisterTab(HWND, HWND) = 0; virtual HRESULT wxSTDCALL UnregisterTab(HWND) = 0; virtual HRESULT wxSTDCALL SetTabOrder(HWND, HWND) = 0; virtual HRESULT wxSTDCALL SetTabActive(HWND, HWND, DWORD) = 0; virtual HRESULT wxSTDCALL ThumbBarAddButtons(HWND, UINT, LPTHUMBBUTTON) = 0; virtual HRESULT wxSTDCALL ThumbBarUpdateButtons(HWND, UINT, LPTHUMBBUTTON) = 0; virtual HRESULT wxSTDCALL ThumbBarSetImageList(HWND, ::HIMAGELIST) = 0; virtual HRESULT wxSTDCALL SetOverlayIcon(HWND, HICON, LPCWSTR) = 0; virtual HRESULT wxSTDCALL SetThumbnailTooltip(HWND, LPCWSTR pszTip) = 0; virtual HRESULT wxSTDCALL SetThumbnailClip(HWND, RECT *) = 0; }; // ---------------------------------------------------------------------------- // wxTaskBarJumpListImpl: definition of class for internal taskbar jump list // implementation. // ---------------------------------------------------------------------------- class wxTaskBarJumpListImpl { public: wxTaskBarJumpListImpl(wxTaskBarJumpList *jumpList = NULL, const wxString& appID = wxEmptyString); virtual ~wxTaskBarJumpListImpl(); void ShowRecentCategory(bool shown = true); void HideRecentCategory(); void ShowFrequentCategory(bool shown = true); void HideFrequentCategory(); wxTaskBarJumpListCategory& GetTasks(); const wxTaskBarJumpListCategory& GetFrequentCategory(); const wxTaskBarJumpListCategory& GetRecentCategory(); const wxTaskBarJumpListCategories& GetCustomCategories() const; void AddCustomCategory(wxTaskBarJumpListCategory* category); wxTaskBarJumpListCategory* RemoveCustomCategory(const wxString& title); void DeleteCustomCategory(const wxString& title); void Update(); private: bool BeginUpdate(); bool CommitUpdate(); void AddTasksToDestinationList(); void AddCustomCategoriesToDestinationList(); void LoadKnownCategory(const wxString& title); wxTaskBarJumpList *m_jumpList; wxCOMPtr m_destinationList; wxCOMPtr m_objectArray; wxScopedPtr m_tasks; wxScopedPtr m_frequent; wxScopedPtr m_recent; wxTaskBarJumpListCategories m_customCategories; bool m_recent_visible; bool m_frequent_visible; // Application User Model ID. wxString m_appID; }; // ---------------------------------------------------------------------------- // wxThumbBarButton Implementation. // ---------------------------------------------------------------------------- wxIMPLEMENT_DYNAMIC_CLASS(wxThumbBarButton, wxObject); wxThumbBarButton::wxThumbBarButton(int id, const wxIcon& icon, const wxString& tooltip, bool enable, bool dismissOnClick, bool hasBackground, bool shown, bool interactive) : m_id(id), m_icon(icon), m_tooltip(tooltip), m_enable(enable), m_dismissOnClick(dismissOnClick), m_hasBackground(hasBackground), m_shown(shown), m_interactive(interactive), m_taskBarButtonParent(NULL) { } bool wxThumbBarButton::Create(int id, const wxIcon& icon, const wxString& tooltip, bool enable, bool dismissOnClick, bool hasBackground, bool shown, bool interactive) { m_id = id; m_icon = icon; m_tooltip = tooltip; m_enable = enable; m_dismissOnClick = dismissOnClick; m_hasBackground = hasBackground; m_shown = shown; m_interactive = interactive; return true; } void wxThumbBarButton::Enable(bool enable) { if ( m_enable != enable ) { m_enable = enable; UpdateParentTaskBarButton(); } } void wxThumbBarButton::SetHasBackground(bool has) { if ( m_hasBackground != has ) { m_hasBackground = has; UpdateParentTaskBarButton(); } } void wxThumbBarButton::EnableDismissOnClick(bool enable) { if ( m_dismissOnClick != enable ) { m_dismissOnClick = enable; UpdateParentTaskBarButton(); } } void wxThumbBarButton::Show(bool shown) { if ( m_shown != shown ) { m_shown = shown; UpdateParentTaskBarButton(); } } void wxThumbBarButton::SetInteractive(bool interactive) { if ( m_interactive != interactive ) { m_interactive = interactive; UpdateParentTaskBarButton(); } } bool wxThumbBarButton::UpdateParentTaskBarButton() { if ( !m_taskBarButtonParent ) return false; return static_cast( m_taskBarButtonParent)->InitOrUpdateThumbBarButtons(); } // ---------------------------------------------------------------------------- // wxTaskBarButtonImpl Implementation. // ---------------------------------------------------------------------------- /* static */ wxTaskBarButton* wxTaskBarButton::New(wxWindow* parent) { wxITaskbarList3* taskbarList = NULL; HRESULT hr = CoCreateInstance ( wxCLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, wxIID_ITaskbarList3, reinterpret_cast(&taskbarList) ); if ( FAILED(hr) ) { // Don't log this error, it may be normal when running under XP. return NULL; } hr = taskbarList->HrInit(); if ( FAILED(hr) ) { // This is however unexpected. wxLogApiError(wxT("ITaskbarList3::Init"), hr); taskbarList->Release(); return NULL; } return new wxTaskBarButtonImpl(taskbarList, parent); } wxTaskBarButtonImpl::wxTaskBarButtonImpl(wxITaskbarList3* taskbarList, wxWindow* parent) : m_parent(parent), m_taskbarList(taskbarList), m_progressRange(0), m_progressValue(0), m_progressState(wxTASKBAR_BUTTON_NO_PROGRESS), m_hasInitThumbnailToolbar(false) { } wxTaskBarButtonImpl::~wxTaskBarButtonImpl() { if ( m_taskbarList ) m_taskbarList->Release(); for ( wxThumbBarButtons::iterator iter = m_thumbBarButtons.begin(); iter != m_thumbBarButtons.end(); ++iter) { delete (*iter); } m_thumbBarButtons.clear(); } void wxTaskBarButtonImpl::Realize() { // (Re-)apply all settings: this is needed if settings were made before the // create message was sent, taskbar icon is hidden and shown again or // explorer is restarted SetProgressRange(m_progressRange); SetProgressState(m_progressState); if ( m_progressValue > 0 ) SetProgressValue(m_progressValue); SetThumbnailTooltip(m_thumbnailTooltip); SetOverlayIcon(m_overlayIcon, m_overlayIconDescription); if ( !m_thumbnailClipRect.IsEmpty() ) SetThumbnailClip(m_thumbnailClipRect); m_hasInitThumbnailToolbar = false; InitOrUpdateThumbBarButtons(); } void wxTaskBarButtonImpl::SetProgressRange(int range) { m_progressRange = range; if ( m_progressRange == 0 ) SetProgressState(wxTASKBAR_BUTTON_NO_PROGRESS); } void wxTaskBarButtonImpl::SetProgressValue(int value) { m_progressValue = value; m_taskbarList->SetProgressValue(m_parent->GetHWND(), value, m_progressRange); } void wxTaskBarButtonImpl::PulseProgress() { SetProgressState(wxTASKBAR_BUTTON_INDETERMINATE); } void wxTaskBarButtonImpl::Show(bool show) { if ( show ) m_taskbarList->AddTab(m_parent->GetHWND()); else m_taskbarList->DeleteTab(m_parent->GetHWND()); } void wxTaskBarButtonImpl::Hide() { Show(false); } void wxTaskBarButtonImpl::SetThumbnailTooltip(const wxString& tooltip) { m_thumbnailTooltip = tooltip; m_taskbarList->SetThumbnailTooltip(m_parent->GetHWND(), tooltip.wc_str()); } void wxTaskBarButtonImpl::SetProgressState(wxTaskBarButtonState state) { m_progressState = state; m_taskbarList->SetProgressState(m_parent->GetHWND(), static_cast(state)); } void wxTaskBarButtonImpl::SetOverlayIcon(const wxIcon& icon, const wxString& description) { m_overlayIcon = icon; m_overlayIconDescription = description; m_taskbarList->SetOverlayIcon(m_parent->GetHWND(), GetHiconOf(icon), description.wc_str()); } void wxTaskBarButtonImpl::SetThumbnailClip(const wxRect& rect) { m_thumbnailClipRect = rect; RECT rc; wxCopyRectToRECT(rect, rc); m_taskbarList->SetThumbnailClip(m_parent->GetHWND(), rect.IsEmpty() ? NULL : &rc); } void wxTaskBarButtonImpl::SetThumbnailContents(const wxWindow *child) { SetThumbnailClip(child->GetRect()); } bool wxTaskBarButtonImpl::AppendThumbBarButton(wxThumbBarButton *button) { wxASSERT_MSG( m_thumbBarButtons.size() < MAX_BUTTON_COUNT, "Number of ThumbBarButtons and separators is limited to 7" ); button->SetParent(this); m_thumbBarButtons.push_back(button); return InitOrUpdateThumbBarButtons(); } bool wxTaskBarButtonImpl::AppendSeparatorInThumbBar() { wxASSERT_MSG( m_thumbBarButtons.size() < MAX_BUTTON_COUNT, "Number of ThumbBarButtons and separators is limited to 7" ); // Append a disable ThumbBarButton without background can simulate the // behavior of appending a separator. wxThumbBarButton *separator = new wxThumbBarButton(wxID_ANY, wxNullIcon, wxEmptyString, false, false, false); m_thumbBarButtons.push_back(separator); return InitOrUpdateThumbBarButtons(); } bool wxTaskBarButtonImpl::InsertThumbBarButton(size_t pos, wxThumbBarButton *button) { wxASSERT_MSG( m_thumbBarButtons.size() < MAX_BUTTON_COUNT, "Number of ThumbBarButtons and separators is limited to 7" ); wxASSERT_MSG( pos <= m_thumbBarButtons.size(), "Invalid index when inserting the button" ); button->SetParent(this); m_thumbBarButtons.insert(m_thumbBarButtons.begin() + pos, button); return InitOrUpdateThumbBarButtons(); } wxThumbBarButton* wxTaskBarButtonImpl::RemoveThumbBarButton( wxThumbBarButton *button) { return RemoveThumbBarButton(button->GetID()); } wxThumbBarButton* wxTaskBarButtonImpl::RemoveThumbBarButton(int id) { for ( wxThumbBarButtons::iterator iter = m_thumbBarButtons.begin(); iter != m_thumbBarButtons.end(); ++iter ) { wxThumbBarButton* button = *iter; if ( id == button->GetID() ) { m_thumbBarButtons.erase(iter); button->SetParent(NULL); InitOrUpdateThumbBarButtons(); return button; } } return NULL; } bool wxTaskBarButtonImpl::InitOrUpdateThumbBarButtons() { THUMBBUTTON buttons[MAX_BUTTON_COUNT]; HRESULT hr; for ( size_t i = 0; i < MAX_BUTTON_COUNT; ++i ) { memset(&buttons[i], 0, sizeof buttons[i]); buttons[i].iId = i; buttons[i].dwFlags = THBF_HIDDEN; buttons[i].dwMask = static_cast(THB_FLAGS); } for ( size_t i = 0; i < m_thumbBarButtons.size(); ++i ) { buttons[i].hIcon = GetHiconOf(m_thumbBarButtons[i]->GetIcon()); buttons[i].dwFlags = GetNativeThumbButtonFlags(*m_thumbBarButtons[i]); buttons[i].dwMask = static_cast(THB_ICON | THB_FLAGS); wxString tooltip = m_thumbBarButtons[i]->GetTooltip(); if ( tooltip.empty() ) continue; // Truncate the tooltip if its length longer than szTip(THUMBBUTTON) // allowed length (260). tooltip.Truncate(260); wxStrlcpy(buttons[i].szTip, tooltip.wc_str(), tooltip.length()); buttons[i].dwMask = static_cast(buttons[i].dwMask | THB_TOOLTIP); } if ( !m_hasInitThumbnailToolbar ) { hr = m_taskbarList->ThumbBarAddButtons(m_parent->GetHWND(), MAX_BUTTON_COUNT, buttons); if ( FAILED(hr) ) { wxLogApiError(wxT("ITaskbarList3::ThumbBarAddButtons"), hr); } m_hasInitThumbnailToolbar = true; } else { hr = m_taskbarList->ThumbBarUpdateButtons(m_parent->GetHWND(), MAX_BUTTON_COUNT, buttons); if ( FAILED(hr) ) { wxLogApiError(wxT("ITaskbarList3::ThumbBarUpdateButtons"), hr); } } return SUCCEEDED(hr); } wxThumbBarButton* wxTaskBarButtonImpl::GetThumbBarButtonByIndex(size_t index) { if ( index >= m_thumbBarButtons.size() ) return NULL; return m_thumbBarButtons[index]; } // ---------------------------------------------------------------------------- // wxTaskBarJumpListItem Implementation. // ---------------------------------------------------------------------------- wxTaskBarJumpListItem::wxTaskBarJumpListItem(wxTaskBarJumpListCategory *parent, wxTaskBarJumpListItemType type, const wxString& title, const wxString& filePath, const wxString& arguments, const wxString& tooltip, const wxString& iconPath, int iconIndex) : m_parentCategory(parent), m_type(type), m_title(title), m_filePath(filePath), m_arguments(arguments), m_tooltip(tooltip), m_iconPath(iconPath), m_iconIndex(iconIndex) { } wxTaskBarJumpListItemType wxTaskBarJumpListItem::GetType() const { return m_type; } void wxTaskBarJumpListItem::SetType(wxTaskBarJumpListItemType type) { m_type = type; if ( m_parentCategory ) m_parentCategory->Update(); } const wxString& wxTaskBarJumpListItem::GetTitle() const { return m_title; } void wxTaskBarJumpListItem::SetTitle(const wxString& title) { m_title = title; if ( m_parentCategory ) m_parentCategory->Update(); } const wxString& wxTaskBarJumpListItem::GetFilePath() const { return m_filePath; } void wxTaskBarJumpListItem::SetFilePath(const wxString& filePath) { m_filePath = filePath; if ( m_parentCategory ) m_parentCategory->Update(); } const wxString& wxTaskBarJumpListItem::GetArguments() const { return m_arguments; } void wxTaskBarJumpListItem::SetArguments(const wxString& arguments) { m_arguments = arguments; if ( m_parentCategory ) m_parentCategory->Update(); } const wxString& wxTaskBarJumpListItem::GetTooltip() const { return m_tooltip; } void wxTaskBarJumpListItem::SetTooltip(const wxString& tooltip) { m_tooltip = tooltip; if ( m_parentCategory ) m_parentCategory->Update(); } const wxString& wxTaskBarJumpListItem::GetIconPath() const { return m_iconPath; } void wxTaskBarJumpListItem::SetIconPath(const wxString& iconPath) { m_iconPath = iconPath; if ( m_parentCategory ) m_parentCategory->Update(); } int wxTaskBarJumpListItem::GetIconIndex() const { return m_iconIndex; } void wxTaskBarJumpListItem::SetIconIndex(int iconIndex) { m_iconIndex = iconIndex; if ( m_parentCategory ) m_parentCategory->Update(); } wxTaskBarJumpListCategory* wxTaskBarJumpListItem::GetCategory() const { return m_parentCategory; } void wxTaskBarJumpListItem::SetCategory(wxTaskBarJumpListCategory *category) { m_parentCategory = category; } // ---------------------------------------------------------------------------- // wxTaskBarJumpListCategory Implementation. // ---------------------------------------------------------------------------- wxTaskBarJumpListCategory::wxTaskBarJumpListCategory(wxTaskBarJumpList *parent, const wxString& title) : m_parent(parent), m_title(title) { } wxTaskBarJumpListCategory::~wxTaskBarJumpListCategory() { for ( wxTaskBarJumpListItems::iterator it = m_items.begin(); it != m_items.end(); ++it ) { delete *it; } } wxTaskBarJumpListItem* wxTaskBarJumpListCategory::Append(wxTaskBarJumpListItem *item) { m_items.push_back(item); item->SetCategory(this); Update(); return item; } void wxTaskBarJumpListCategory::Delete(wxTaskBarJumpListItem *item) { item = Remove(item); item->SetCategory(NULL); Update(); if ( item ) delete item; } wxTaskBarJumpListItem* wxTaskBarJumpListCategory::Remove(wxTaskBarJumpListItem *item) { for (wxTaskBarJumpListItems::iterator it = m_items.begin(); it != m_items.end(); ++it) { if ( *it == item ) { m_items.erase(it); item->SetCategory(NULL); Update(); return item; } } return NULL; } wxTaskBarJumpListItem* wxTaskBarJumpListCategory::FindItemByPosition(size_t pos) const { wxASSERT_MSG( pos < m_items.size(), "invalid pos." ); return m_items[pos]; } wxTaskBarJumpListItem* wxTaskBarJumpListCategory::Insert(size_t pos, wxTaskBarJumpListItem *item) { wxASSERT_MSG( pos <= m_items.size(), "invalid pos." ); m_items.insert(m_items.begin() + pos, item); item->SetCategory(this); Update(); return item; } wxTaskBarJumpListItem* wxTaskBarJumpListCategory::Prepend(wxTaskBarJumpListItem *item) { return Insert(0, item); } void wxTaskBarJumpListCategory::SetTitle(const wxString& title) { m_title = title; Update(); } const wxString& wxTaskBarJumpListCategory::GetTitle() const { return m_title; } const wxTaskBarJumpListItems& wxTaskBarJumpListCategory::GetItems() const { return m_items; } void wxTaskBarJumpListCategory::Update() { if ( m_parent ) m_parent->Update(); } // ---------------------------------------------------------------------------- // wxTaskBarJumpList Implementation. // ---------------------------------------------------------------------------- wxTaskBarJumpList::wxTaskBarJumpList(const wxString& appID) : m_jumpListImpl(new wxTaskBarJumpListImpl(this, appID)) { } wxTaskBarJumpList::~wxTaskBarJumpList() { delete m_jumpListImpl; } wxTaskBarJumpListCategory& wxTaskBarJumpList::GetTasks() const { return m_jumpListImpl->GetTasks(); } void wxTaskBarJumpList::ShowRecentCategory(bool shown) { m_jumpListImpl->ShowRecentCategory(shown); } void wxTaskBarJumpList::HideRecentCategory() { m_jumpListImpl->HideRecentCategory(); } void wxTaskBarJumpList::ShowFrequentCategory(bool shown) { m_jumpListImpl->ShowFrequentCategory(shown); } void wxTaskBarJumpList::HideFrequentCategory() { m_jumpListImpl->HideFrequentCategory(); } const wxTaskBarJumpListCategory& wxTaskBarJumpList::GetFrequentCategory() const { return m_jumpListImpl->GetFrequentCategory(); } const wxTaskBarJumpListCategory& wxTaskBarJumpList::GetRecentCategory() const { return m_jumpListImpl->GetRecentCategory(); } const wxTaskBarJumpListCategories& wxTaskBarJumpList::GetCustomCategories() const { return m_jumpListImpl->GetCustomCategories(); } void wxTaskBarJumpList::AddCustomCategory(wxTaskBarJumpListCategory* category) { m_jumpListImpl->AddCustomCategory(category); } wxTaskBarJumpListCategory* wxTaskBarJumpList::RemoveCustomCategory( const wxString& title) { return m_jumpListImpl->RemoveCustomCategory(title); } void wxTaskBarJumpList::DeleteCustomCategory(const wxString& title) { m_jumpListImpl->DeleteCustomCategory(title); } void wxTaskBarJumpList::Update() { m_jumpListImpl->Update(); } // ---------------------------------------------------------------------------- // wxTaskBarJumpListImpl Implementation. // ---------------------------------------------------------------------------- wxTaskBarJumpListImpl::wxTaskBarJumpListImpl(wxTaskBarJumpList *jumpList, const wxString& appID) : m_jumpList(jumpList), m_destinationList(NULL) { m_appID = appID; HRESULT hr = CoCreateInstance ( wxCLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, wxIID_ICustomDestinationList, reinterpret_cast (&(m_destinationList)) ); if ( FAILED(hr) ) { wxLogApiError(wxT("CoCreateInstance(wxCLSID_DestinationList)"), hr); return; } } wxTaskBarJumpListImpl::~wxTaskBarJumpListImpl() { for ( wxTaskBarJumpListCategories::iterator it = m_customCategories.begin(); it != m_customCategories.end(); ++it ) { delete *it; } } void wxTaskBarJumpListImpl::Update() { if ( !BeginUpdate() ) return; AddTasksToDestinationList(); AddCustomCategoriesToDestinationList(); if ( m_recent_visible ) m_destinationList->AppendKnownCategory(KDC_RECENT); if ( m_frequent_visible ) m_destinationList->AppendKnownCategory(KDC_FREQUENT); CommitUpdate(); } wxTaskBarJumpListCategory& wxTaskBarJumpListImpl::GetTasks() { if ( m_tasks.get() == NULL ) m_tasks.reset(new wxTaskBarJumpListCategory(m_jumpList, wxT("Tasks"))); return *(m_tasks.get()); } void wxTaskBarJumpListImpl::ShowRecentCategory(bool shown) { m_recent_visible = shown; } void wxTaskBarJumpListImpl::HideRecentCategory() { ShowRecentCategory(false); } void wxTaskBarJumpListImpl::ShowFrequentCategory(bool shown) { m_frequent_visible = shown; } void wxTaskBarJumpListImpl::HideFrequentCategory() { ShowFrequentCategory(false); } const wxTaskBarJumpListCategory& wxTaskBarJumpListImpl::GetFrequentCategory() { wxString title = wxT("Frequent"); if ( m_frequent.get() == NULL ) m_frequent.reset(new wxTaskBarJumpListCategory(m_jumpList, title)); LoadKnownCategory(title); return *m_frequent.get(); } const wxTaskBarJumpListCategory& wxTaskBarJumpListImpl::GetRecentCategory() { wxString title = wxT("Recent"); if ( m_recent.get() == NULL ) m_recent.reset(new wxTaskBarJumpListCategory(m_jumpList, title)); LoadKnownCategory(title); return *m_recent.get(); } const wxTaskBarJumpListCategories& wxTaskBarJumpListImpl::GetCustomCategories() const { return m_customCategories; } void wxTaskBarJumpListImpl::AddCustomCategory(wxTaskBarJumpListCategory *category) { wxASSERT_MSG( category != NULL, "Invalid category." ); m_customCategories.push_back(category); } wxTaskBarJumpListCategory* wxTaskBarJumpListImpl::RemoveCustomCategory(const wxString& title) { for ( wxTaskBarJumpListCategories::iterator it = m_customCategories.begin(); it != m_customCategories.end(); ++it ) { wxTaskBarJumpListCategory* tbJlCat = *it; if ( tbJlCat->GetTitle() == title ) { m_customCategories.erase(it); return tbJlCat; } } return NULL; } void wxTaskBarJumpListImpl::DeleteCustomCategory(const wxString& title) { wxTaskBarJumpListCategory* category = RemoveCustomCategory(title); if ( category ) delete category; } bool wxTaskBarJumpListImpl::BeginUpdate() { if ( m_destinationList == NULL ) return false; unsigned int max_count = 0; m_objectArray = NULL; HRESULT hr = m_destinationList->BeginList(&max_count, wxIID_IObjectArray, reinterpret_cast(&m_objectArray)); if ( !m_appID.empty() ) m_destinationList->SetAppID(m_appID.wc_str()); return SUCCEEDED(hr); } bool wxTaskBarJumpListImpl::CommitUpdate() { return SUCCEEDED(m_destinationList->CommitList()); } void wxTaskBarJumpListImpl::AddTasksToDestinationList() { if ( !m_tasks.get() ) return; wxCOMPtr collection(CreateObjectCollection()); if ( !collection ) return; const wxTaskBarJumpListItems& tasks = m_tasks->GetItems(); for ( wxTaskBarJumpListItems::const_iterator it = tasks.begin(); it != tasks.end(); ++it ) { wxASSERT_MSG( ((*it)->GetType() == wxTASKBAR_JUMP_LIST_TASK || (*it)->GetType() == wxTASKBAR_JUMP_LIST_SEPARATOR), "Invalid task Item." ); AddShellLink(collection, *(*it)); } m_destinationList->AddUserTasks(collection); } void wxTaskBarJumpListImpl::AddCustomCategoriesToDestinationList() { for ( wxTaskBarJumpListCategories::iterator it = m_customCategories.begin(); it != m_customCategories.end(); ++it ) { wxCOMPtr collection(CreateObjectCollection()); if ( !collection ) continue; const wxTaskBarJumpListItems& tasks = (*it)->GetItems(); for ( wxTaskBarJumpListItems::const_iterator iter = tasks.begin(); iter != tasks.end(); ++iter ) { wxASSERT_MSG( (*iter)->GetType() == wxTASKBAR_JUMP_LIST_DESTINATION, "Invalid category item." ); AddShellLink(collection, *(*iter)); } m_destinationList->AppendCategory((*it)->GetTitle().wc_str(), collection); } } void wxTaskBarJumpListImpl::LoadKnownCategory(const wxString& title) { wxCOMPtr docList; HRESULT hr = CoCreateInstance ( wxCLSID_ApplicationDocumentLists, NULL, CLSCTX_INPROC_SERVER, wxIID_IApplicationDocumentLists, reinterpret_cast(&docList) ); if ( FAILED(hr) ) { wxLogApiError("CoCreateInstance(wxCLSID_ApplicationDocumentLists)", hr); return; } if ( !m_appID.empty() ) docList->SetAppID(m_appID.wc_str()); wxCOMPtr array; wxASSERT_MSG( title == "Recent" || title == "Frequent", "Invalid title." ); hr = docList->GetList ( title == "Recent" ? ADLT_RECENT : ADLT_FREQUENT, 0, wxIID_IObjectArray, reinterpret_cast(&array) ); if ( FAILED(hr) ) { wxLogApiError("IApplicationDocumentLists::GetList", hr); return; } UINT count = 0; array->GetCount(&count); for (UINT i = 0; i < count; ++i) { wxCOMPtr collectionItem; hr = array->GetAt(i, wxIID_IUnknown, reinterpret_cast(&collectionItem)); if ( FAILED(hr) ) { wxLogApiError("IObjectArray::GetAt", hr); continue; } wxCOMPtr shellLink; wxCOMPtr shellItem; wxTaskBarJumpListItem* item = NULL; if ( SUCCEEDED(collectionItem->QueryInterface( wxIID_IShellLink, reinterpret_cast(&shellLink))) ) { item = GetItemFromIShellLink(shellLink); } else if ( SUCCEEDED(collectionItem->QueryInterface( wxIID_IShellItem, reinterpret_cast(&shellItem))) ) { item = GetItemFromIShellItem(shellItem); } else { wxLogError("Can not query interfaces: IShellLink or IShellItem."); } if ( item ) { if ( title == wxT("Frequent") ) m_frequent->Append(item); else m_recent->Append(item); } } } #endif // wxUSE_TASKBARBUTTON