1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/taskbarbutton.cpp
3 // Purpose:     Implements wxTaskBarButtonImpl class for manipulating buttons on
4 //              the Windows taskbar.
5 // Author:      Chaobin Zhang <zhchbin@gmail.com>
6 // Created:     2014-06-01
7 // Copyright:   (c) 2014 wxWidgets development team
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 #include "wx/wxprec.h"
12 
13 
14 #ifndef WX_PRECOMP
15     #include "wx/icon.h"
16     #include "wx/toplevel.h"
17 #endif
18 
19 #if wxUSE_TASKBARBUTTON
20 
21 #ifdef _MSC_VER
22     #pragma comment( lib, "shlwapi" )
23 #endif
24 
25 #include "wx/msw/private.h"
26 #include "wx/msw/taskbarbutton.h"
27 #include "wx/scopedptr.h"
28 #include "wx/msw/private/comptr.h"
29 #include "wx/msw/private/cotaskmemptr.h"
30 
31 #include <shlwapi.h>
32 #include <initguid.h>
33 
34 #if wxUSE_DYNLIB_CLASS
35     #include "wx/dynlib.h"
36 #endif // wxUSE_DYNLIB_CLASS
37 
38 // ----------------------------------------------------------------------------
39 // Redefine the interfaces: ITaskbarList3, IObjectCollection,
40 // ICustomDestinationList, IShellLink, IShellItem, IApplicationDocumentLists
41 // etc.
42 // ----------------------------------------------------------------------------
43 
44 WINOLEAPI PropVariantClear(PROPVARIANT* pvar);
45 
46 #ifndef PropVariantInit
47 #define PropVariantInit(pvar) memset ( (pvar), 0, sizeof(PROPVARIANT) )
48 #endif
49 
50 #ifndef INFOTIPSIZE
51 #define INFOTIPSIZE 1024
52 #endif
53 
54 namespace {
55 
56 // The maximum number of thumbnail toolbar buttons allowed on windows is 7.
57 static const int MAX_BUTTON_COUNT = 7;
58 
59 DEFINE_GUID(wxCLSID_TaskbarList,
60     0x56fdf344, 0xfd6d, 0x11d0, 0x95, 0x8a, 0x0, 0x60, 0x97, 0xc9, 0xa0, 0x90);
61 DEFINE_GUID(wxCLSID_DestinationList,
62     0x77f10cf0, 0x3db5, 0x4966, 0xb5, 0x20, 0xb7, 0xc5, 0x4f, 0xd3,0x5e, 0xd6);
63 DEFINE_GUID(wxCLSID_EnumerableObjectCollection,
64     0x2d3468c1, 0x36a7, 0x43b6, 0xac, 0x24, 0xd3, 0xf0, 0x2f, 0xd9, 0x60, 0x7a);
65 DEFINE_GUID(wxCLSID_ShellLink,
66     0x00021401, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
67 DEFINE_GUID(wxIID_ICustomDestinationList,
68     0x6332debf, 0x87b5, 0x4670, 0x90, 0xc0, 0x5e, 0x57, 0xb4, 0x08, 0xa4, 0x9e);
69 DEFINE_GUID(wxIID_ITaskbarList3,
70     0xea1afb91, 0x9e28, 0x4b86, 0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf);
71 DEFINE_GUID(wxIID_IPropertyStore,
72     0x886d8eeb, 0x8cf2, 0x4446, 0x8d, 0x02, 0xcd, 0xba, 0x1d, 0xbd, 0xcf, 0x99);
73 DEFINE_GUID(wxIID_IObjectArray,
74     0x92ca9dcd, 0x5622, 0x4bba, 0xa8, 0x05, 0x5e, 0x9f, 0x54, 0x1b, 0xd8, 0xc9);
75 DEFINE_GUID(wxIID_IObjectCollection,
76     0x5632b1a4, 0xe38a, 0x400a, 0x92, 0x8a, 0xd4, 0xcd, 0x63, 0x23, 0x02, 0x95);
77 DEFINE_GUID(wxIID_IApplicationDocumentLists,
78     0x3c594f9f, 0x9f30, 0x47a1, 0x97, 0x9a, 0xc9, 0xe8, 0x3d, 0x3d, 0x0a, 0x06);
79 DEFINE_GUID(wxCLSID_ApplicationDocumentLists,
80     0x86bec222, 0x30f2, 0x47e0, 0x9f, 0x25, 0x60, 0xd1, 0x1c, 0xd7, 0x5c, 0x28);
81 DEFINE_GUID(wxIID_IUnknown,
82     0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
83 DEFINE_GUID(wxIID_IShellItem,
84     0x43826d1e, 0xe718, 0x42ee, 0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe);
85 
86 typedef IUnknown *HIMAGELIST;
87 
88 typedef enum THUMBBUTTONFLAGS
89 {
90     THBF_ENABLED    = 0,
91     THBF_DISABLED   = 0x1,
92     THBF_DISMISSONCLICK = 0x2,
93     THBF_NOBACKGROUND   = 0x4,
94     THBF_HIDDEN = 0x8,
95     THBF_NONINTERACTIVE = 0x10
96 } THUMBBUTTONFLAGS;
97 
98 typedef enum THUMBBUTTONMASK
99 {
100     THB_BITMAP  = 0x1,
101     THB_ICON    = 0x2,
102     THB_TOOLTIP = 0x4,
103     THB_FLAGS   = 0x8
104 } THUMBBUTTONMASK;
105 
106 typedef struct THUMBBUTTON
107 {
108     THUMBBUTTONMASK dwMask;
109     UINT iId;
110     UINT iBitmap;
111     HICON hIcon;
112     WCHAR szTip[260];
113     THUMBBUTTONFLAGS dwFlags;
114 } THUMBBUTTON;
115 
116 typedef struct THUMBBUTTON *LPTHUMBBUTTON;
117 
118 typedef enum TBPFLAG
119 {
120     TBPF_NOPROGRESS = 0,
121     TBPF_INDETERMINATE  = 0x1,
122     TBPF_NORMAL = 0x2,
123     TBPF_ERROR  = 0x4,
124     TBPF_PAUSED = 0x8
125 } TBPFLAG;
126 
127 #ifndef PROPERTYKEY_DEFINED
128 typedef struct _tagpropertykey
129 {
130     GUID fmtid;
131     DWORD pid;
132 } PROPERTYKEY;
133 #endif // !PROPERTYKEY_DEFINED
134 
135 #define REFPROPERTYKEY const PROPERTYKEY &
136 
137 #define DEFINE_PROPERTYKEY(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8, pid) \
138     const PROPERTYKEY name  = \
139     { { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }, pid }
140 
141 DEFINE_PROPERTYKEY(PKEY_Title,
142     0xf29f85e0, 0x4ff9, 0x1068, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9, 2);
143 DEFINE_PROPERTYKEY(PKEY_AppUserModel_IsDestListSeparator,
144     0x9f4c2855, 0x9f79, 0x4b39, 0xa8, 0xd0, 0xe1, 0xd4, 0x2d, 0xe1, 0xd5, 0xf3, 6);
145 DEFINE_PROPERTYKEY(PKEY_Link_Arguments,
146     0x436f2667, 0x14e2, 0x4feb, 0xb3, 0x0a, 0x14, 0x6c, 0x53, 0xb5, 0xb6, 0x74, 100);
147 
148 #ifdef wxUSE_UNICODE
149 #define IShellLink      wxIShellLinkW
150 
151 DEFINE_GUID(wxIID_IShellLink,
152     0x000214F9, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
153 #else
154 #define IShellLink      wxIShellLinkA
155 
156 DEFINE_GUID(wxIID_IShellLink,
157     0x000214EE, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
158 #endif  // wxUSE_UNICODE
159 
160 typedef enum _SIGDN
161 {
162     SIGDN_NORMALDISPLAY               = 0,
163     SIGDN_PARENTRELATIVEPARSING       = (int)0x80018001,
164     SIGDN_DESKTOPABSOLUTEPARSING      = (int)0x80028000,
165     SIGDN_PARENTRELATIVEEDITING       = (int)0x80031001,
166     SIGDN_DESKTOPABSOLUTEEDITING      = (int)0x8004c000,
167     SIGDN_FILESYSPATH                 = (int)0x80058000,
168     SIGDN_URL                         = (int)0x80068000,
169     SIGDN_PARENTRELATIVEFORADDRESSBAR = (int)0x8007c001,
170     SIGDN_PARENTRELATIVE              = (int)0x80080001
171 } SIGDN;
172 
173 enum _SICHINTF
174 {
175     SICHINT_DISPLAY                       = 0,
176     SICHINT_ALLFIELDS                     = (int)0x80000000,
177     SICHINT_CANONICAL                     = 0x10000000,
178     SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL = 0x20000000
179 };
180 
181 typedef DWORD SICHINTF;
182 typedef ULONG SFGAOF;
183 
184 typedef enum KNOWNDESTCATEGORY
185 {
186     KDC_FREQUENT    = 1,
187     KDC_RECENT  = ( KDC_FREQUENT + 1 )
188 } KNOWNDESTCATEGORY;
189 
190 typedef enum APPDOCLISTTYPE
191 {
192     ADLT_RECENT   = 0,
193     ADLT_FREQUENT = ( ADLT_RECENT + 1 )
194 } APPDOCLISTTYPE;
195 
196 } // anonymous namespace
197 
198 class wxITaskbarList : public IUnknown
199 {
200 public:
201     virtual HRESULT wxSTDCALL HrInit() = 0;
202     virtual HRESULT wxSTDCALL AddTab(HWND) = 0;
203     virtual HRESULT wxSTDCALL DeleteTab(HWND) = 0;
204     virtual HRESULT wxSTDCALL ActivateTab(HWND) = 0;
205     virtual HRESULT wxSTDCALL SetActiveAlt(HWND) = 0;
206 };
207 
208 class wxITaskbarList2 : public wxITaskbarList
209 {
210 public:
211     virtual HRESULT wxSTDCALL MarkFullscreenWindow(HWND, BOOL) = 0;
212 };
213 
214 class wxIShellLinkA : public IUnknown
215 {
216 public:
217     virtual HRESULT wxSTDCALL GetPath(LPSTR, int, WIN32_FIND_DATAA*, DWORD) = 0;
218     virtual HRESULT wxSTDCALL GetIDList(LPITEMIDLIST *ppidl) = 0;
219     virtual HRESULT wxSTDCALL SetIDList(LPCITEMIDLIST pidl) = 0;
220     virtual HRESULT wxSTDCALL GetDescription(LPSTR, int) = 0;
221     virtual HRESULT wxSTDCALL SetDescription(LPCSTR) = 0;
222     virtual HRESULT wxSTDCALL GetWorkingDirectory(LPSTR, int) = 0;
223     virtual HRESULT wxSTDCALL SetWorkingDirectory(LPCSTR) = 0;
224     virtual HRESULT wxSTDCALL GetArguments(LPSTR, int) = 0;
225     virtual HRESULT wxSTDCALL SetArguments(LPCSTR) = 0;
226     virtual HRESULT wxSTDCALL GetHotkey(WORD*) = 0;
227     virtual HRESULT wxSTDCALL SetHotkey(WORD) = 0;
228     virtual HRESULT wxSTDCALL GetShowCmd(int*) = 0;
229     virtual HRESULT wxSTDCALL SetShowCmd(int) = 0;
230     virtual HRESULT wxSTDCALL GetIconLocation(LPSTR, int, int*) = 0;
231     virtual HRESULT wxSTDCALL SetIconLocation(LPCSTR, int) = 0;
232     virtual HRESULT wxSTDCALL SetRelativePath(LPCSTR, DWORD) = 0;
233     virtual HRESULT wxSTDCALL Resolve(HWND, DWORD) = 0;
234     virtual HRESULT wxSTDCALL SetPath(LPCSTR) = 0;
235 };
236 
237 class wxIShellLinkW : public IUnknown
238 {
239 public:
240     virtual HRESULT wxSTDCALL GetPath(LPWSTR, int, WIN32_FIND_DATAW*, DWORD) = 0;
241     virtual HRESULT wxSTDCALL GetIDList(LPITEMIDLIST *ppidl) = 0;
242     virtual HRESULT wxSTDCALL SetIDList(LPCITEMIDLIST pidl) = 0;
243     virtual HRESULT wxSTDCALL GetDescription(LPWSTR, int) = 0;
244     virtual HRESULT wxSTDCALL SetDescription(LPCWSTR) = 0;
245     virtual HRESULT wxSTDCALL GetWorkingDirectory(LPWSTR, int) = 0;
246     virtual HRESULT wxSTDCALL SetWorkingDirectory(LPCWSTR) = 0;
247     virtual HRESULT wxSTDCALL GetArguments(LPWSTR, int) = 0;
248     virtual HRESULT wxSTDCALL SetArguments(LPCWSTR) = 0;
249     virtual HRESULT wxSTDCALL GetHotkey(WORD*) = 0;
250     virtual HRESULT wxSTDCALL SetHotkey(WORD) = 0;
251     virtual HRESULT wxSTDCALL GetShowCmd(int*) = 0;
252     virtual HRESULT wxSTDCALL SetShowCmd(int) = 0;
253     virtual HRESULT wxSTDCALL GetIconLocation(LPWSTR, int, int*) = 0;
254     virtual HRESULT wxSTDCALL SetIconLocation(LPCWSTR, int) = 0;
255     virtual HRESULT wxSTDCALL SetRelativePath(LPCWSTR, DWORD) = 0;
256     virtual HRESULT wxSTDCALL Resolve(HWND, DWORD) = 0;
257     virtual HRESULT wxSTDCALL SetPath(LPCWSTR) = 0;
258 };
259 
260 class IShellItem : public IUnknown
261 {
262 public:
263     virtual HRESULT wxSTDCALL BindToHandler(IBindCtx*, REFGUID, REFIID, void **) = 0;
264     virtual HRESULT wxSTDCALL GetParent(IShellItem **) = 0;
265     virtual HRESULT wxSTDCALL GetDisplayName(SIGDN, LPWSTR*) = 0;
266     virtual HRESULT wxSTDCALL GetAttributes(SFGAOF, SFGAOF*) = 0;
267     virtual HRESULT wxSTDCALL Compare(IShellItem *, SICHINTF, int *) = 0;
268 };
269 
270 class IObjectArray : public IUnknown
271 {
272 public:
273     virtual HRESULT wxSTDCALL GetCount(UINT*) = 0;
274     virtual HRESULT wxSTDCALL GetAt(UINT, REFIID, void **) = 0;
275 };
276 
277 class IObjectCollection : public IObjectArray
278 {
279 public:
280     virtual HRESULT wxSTDCALL AddObject(IUnknown *) = 0;
281     virtual HRESULT wxSTDCALL AddFromArray(IObjectArray *) = 0;
282     virtual HRESULT wxSTDCALL RemoveObjectAt(UINT) = 0;
283     virtual HRESULT wxSTDCALL Clear() = 0;
284 };
285 
286 class IPropertyStore : public IUnknown
287 {
288 public:
289     virtual HRESULT wxSTDCALL GetCount(DWORD *) = 0;
290     virtual HRESULT wxSTDCALL GetAt(DWORD, PROPERTYKEY *) = 0;
291     virtual HRESULT wxSTDCALL GetValue(REFPROPERTYKEY, PROPVARIANT *) = 0;
292     virtual HRESULT wxSTDCALL SetValue(REFPROPERTYKEY, const PROPVARIANT&) = 0;
293     virtual HRESULT wxSTDCALL Commit() = 0;
294 };
295 
296 class ICustomDestinationList : public IUnknown
297 {
298 public:
299     virtual HRESULT wxSTDCALL SetAppID(LPCWSTR) = 0;
300     virtual HRESULT wxSTDCALL BeginList(UINT*, REFIID, void**) = 0;
301     virtual HRESULT wxSTDCALL AppendCategory(LPCWSTR, IObjectArray *) = 0;
302     virtual HRESULT wxSTDCALL AppendKnownCategory(KNOWNDESTCATEGORY) = 0;
303     virtual HRESULT wxSTDCALL AddUserTasks(IObjectArray *) = 0;
304     virtual HRESULT wxSTDCALL CommitList() = 0;
305     virtual HRESULT wxSTDCALL GetRemovedDestinations(REFIID, void**) = 0;
306     virtual HRESULT wxSTDCALL DeleteList(LPCWSTR) = 0;
307     virtual HRESULT wxSTDCALL AbortList() = 0;
308 };
309 
310 class IApplicationDocumentLists : public IUnknown
311 {
312 public:
313     virtual HRESULT wxSTDCALL SetAppID(LPCWSTR) = 0;
314     virtual HRESULT wxSTDCALL GetList(APPDOCLISTTYPE, UINT, REFIID, void**) = 0;
315 };
316 
317 namespace
318 {
319 
InitPropVariantFromBoolean(BOOL fVal,PROPVARIANT * ppropvar)320 inline HRESULT InitPropVariantFromBoolean(BOOL fVal, PROPVARIANT *ppropvar)
321 {
322     ppropvar->vt = VT_BOOL;
323     ppropvar->boolVal = fVal ? VARIANT_TRUE : VARIANT_FALSE;
324     return S_OK;
325 }
326 
InitPropVariantFromString(PCWSTR psz,PROPVARIANT * ppropvar)327 inline HRESULT InitPropVariantFromString(PCWSTR psz, PROPVARIANT *ppropvar)
328 {
329     HRESULT hr = E_FAIL;
330     ppropvar->vt = VT_LPWSTR;
331 
332 #if wxUSE_DYNLIB_CLASS
333     typedef HRESULT (WINAPI *SHStrDupW_t)(LPCWSTR, LPWSTR*);
334     static SHStrDupW_t s_pfnSHStrDupW = NULL;
335     if ( !s_pfnSHStrDupW )
336     {
337         wxDynamicLibrary dll(wxT("shlwapi.dll"));
338         if ( dll.IsLoaded() )
339         {
340             s_pfnSHStrDupW = (SHStrDupW_t)dll.GetSymbol(wxT("SHStrDupW"));
341         }
342     }
343 
344     if ( s_pfnSHStrDupW )
345     {
346         hr = s_pfnSHStrDupW(psz, &ppropvar->pwszVal);
347     }
348 #elif defined (_MSC_VER)
349     hr = SHStrDupW(psz, &ppropvar->pwszVal);
350 #else
351     wxUnusedVar(psz);
352 #endif
353 
354     if ( FAILED(hr) )
355     {
356         PropVariantInit(ppropvar);
357     }
358     return hr;
359 }
360 
GetNativeThumbButtonFlags(const wxThumbBarButton & button)361 THUMBBUTTONFLAGS GetNativeThumbButtonFlags(const wxThumbBarButton& button)
362 {
363     WXUINT flags = 0;
364     flags |= (button.IsEnable() ? THBF_ENABLED : THBF_DISABLED);
365     if ( button.IsDismissOnClick() )
366         flags |= THBF_DISMISSONCLICK;
367     if ( !button.HasBackground() )
368         flags |= THBF_NOBACKGROUND;
369     if ( !button.IsShown() )
370         flags |= THBF_HIDDEN;
371     if ( !button.IsInteractive() )
372         flags |= THBF_NONINTERACTIVE;
373     return static_cast<THUMBBUTTONFLAGS>(flags);
374 }
375 
AddShellLink(IObjectCollection * collection,const wxTaskBarJumpListItem & item)376 bool AddShellLink(IObjectCollection *collection,
377                   const wxTaskBarJumpListItem& item)
378 {
379     wxCOMPtr<IShellLink> shellLink;
380     wxCOMPtr<IPropertyStore> propertyStore;
381 
382     HRESULT hr = CoCreateInstance
383                  (
384                      wxCLSID_ShellLink,
385                      NULL,
386                      CLSCTX_INPROC_SERVER,
387                      wxIID_IShellLink,
388                      reinterpret_cast<void**> (&(shellLink))
389                  );
390     if ( FAILED(hr) )
391     {
392         wxLogApiError("CoCreateInstance(wxCLSID_ShellLink)", hr);
393         return false;
394     }
395 
396     if ( item.GetType() == wxTASKBAR_JUMP_LIST_TASK ||
397          item.GetType() == wxTASKBAR_JUMP_LIST_DESTINATION )
398     {
399         if ( !item.GetFilePath().IsEmpty() )
400             shellLink->SetPath(item.GetFilePath().wc_str());
401         if ( !item.GetArguments().IsEmpty() )
402             shellLink->SetArguments(item.GetArguments().wc_str());
403         if ( !item.GetIconPath().IsEmpty() )
404         {
405             shellLink->SetIconLocation(item.GetIconPath().wc_str(),
406                                         item.GetIconIndex());
407         }
408         if ( !item.GetTooltip().IsEmpty() )
409             shellLink->SetDescription(item.GetTooltip().wc_str());
410     }
411 
412     hr = shellLink->QueryInterface(wxIID_IPropertyStore,
413                                    reinterpret_cast<void**>(&(propertyStore)));
414     if ( FAILED(hr) )
415     {
416         wxLogApiError("IShellLink(QueryInterface)", hr);
417         return false;
418     }
419 
420     PROPVARIANT pv;
421     if ( item.GetType() == wxTASKBAR_JUMP_LIST_TASK ||
422          item.GetType() == wxTASKBAR_JUMP_LIST_DESTINATION )
423     {
424         hr = InitPropVariantFromString(item.GetTitle().wc_str(), &pv);
425         if ( SUCCEEDED(hr) )
426         {
427             hr = propertyStore->SetValue(PKEY_Title, pv);
428         }
429     }
430     else if ( item.GetType() == wxTASKBAR_JUMP_LIST_SEPARATOR )
431     {
432         hr = InitPropVariantFromBoolean(TRUE, &pv);
433         if ( SUCCEEDED(hr) )
434         {
435             hr = propertyStore->SetValue(PKEY_AppUserModel_IsDestListSeparator,
436                                          pv);
437         }
438     }
439 
440     // Save the changes we made to the property store.
441     propertyStore->Commit();
442     PropVariantClear(&pv);
443 
444     // Add this IShellLink object to the given collection.
445     hr = collection->AddObject(shellLink);
446 
447     return SUCCEEDED(hr);
448 }
449 
GetItemFromIShellLink(IShellLink * link)450 wxTaskBarJumpListItem* GetItemFromIShellLink(IShellLink* link)
451 {
452     if ( !link )
453         return NULL;
454 
455     wxTaskBarJumpListItem* item =
456         new wxTaskBarJumpListItem(NULL, wxTASKBAR_JUMP_LIST_DESTINATION);
457 
458     wxCOMPtr<IPropertyStore> linkProps;
459     HRESULT hr = link->QueryInterface
460                  (
461                      wxIID_IPropertyStore,
462                      reinterpret_cast<void **>(&linkProps)
463                  );
464     if ( FAILED(hr) )
465     {
466         wxLogApiError("IShellLink::QueryInterface", hr);
467         return NULL;
468     }
469 
470     PROPVARIANT var;
471     linkProps->GetValue(PKEY_Link_Arguments, &var);
472     item->SetArguments(wxString(var.pwszVal));
473     PropVariantClear(&var);
474 
475     const int bufferSize = 2048;
476     wchar_t buffer[bufferSize];
477 
478     link->GetDescription(buffer, INFOTIPSIZE);
479     item->SetTooltip(wxString(buffer));
480 
481     int dummyIndex;
482     link->GetIconLocation(buffer, bufferSize - 1, &dummyIndex);
483     item->SetIconPath(wxString(buffer));
484 
485     link->GetPath(buffer, bufferSize - 1, NULL, 0x1);
486     item->SetFilePath(wxString(buffer));
487 
488     return item;
489 }
490 
GetItemFromIShellItem(IShellItem * shellItem)491 wxTaskBarJumpListItem* GetItemFromIShellItem(IShellItem *shellItem)
492 {
493     if ( !shellItem )
494         return NULL;
495 
496     wxTaskBarJumpListItem *item =
497         new wxTaskBarJumpListItem(NULL, wxTASKBAR_JUMP_LIST_DESTINATION);
498 
499     wxCoTaskMemPtr<wchar_t> name;
500     shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name);
501     item->SetFilePath(wxString(name));
502     return item;
503 }
504 
CreateObjectCollection()505 IObjectCollection* CreateObjectCollection()
506 {
507     IObjectCollection* collection;
508 
509     HRESULT hr;
510     hr = CoCreateInstance
511          (
512              wxCLSID_EnumerableObjectCollection,
513              NULL,
514              CLSCTX_INPROC,
515              wxIID_IObjectCollection,
516              reinterpret_cast<void**>(&(collection))
517          );
518     if ( FAILED(hr) )
519     {
520         wxLogApiError("CoCreateInstance(wxCLSID_EnumerableObjectCollection)",
521                       hr);
522         return NULL;
523     }
524 
525     return collection;
526 }
527 
528 } // namespace
529 
530 class wxITaskbarList3 : public wxITaskbarList2
531 {
532 public:
533     virtual HRESULT wxSTDCALL SetProgressValue(HWND, ULONGLONG, ULONGLONG) = 0;
534     virtual HRESULT wxSTDCALL SetProgressState(HWND, TBPFLAG) = 0;
535     virtual HRESULT wxSTDCALL RegisterTab(HWND, HWND) = 0;
536     virtual HRESULT wxSTDCALL UnregisterTab(HWND) = 0;
537     virtual HRESULT wxSTDCALL SetTabOrder(HWND, HWND) = 0;
538     virtual HRESULT wxSTDCALL SetTabActive(HWND, HWND, DWORD) = 0;
539     virtual HRESULT wxSTDCALL ThumbBarAddButtons(HWND, UINT, LPTHUMBBUTTON) = 0;
540     virtual
541         HRESULT wxSTDCALL ThumbBarUpdateButtons(HWND, UINT, LPTHUMBBUTTON) = 0;
542     virtual HRESULT wxSTDCALL ThumbBarSetImageList(HWND, ::HIMAGELIST) = 0;
543     virtual HRESULT wxSTDCALL SetOverlayIcon(HWND, HICON, LPCWSTR) = 0;
544     virtual HRESULT wxSTDCALL SetThumbnailTooltip(HWND, LPCWSTR pszTip) = 0;
545     virtual HRESULT wxSTDCALL SetThumbnailClip(HWND, RECT *) = 0;
546 };
547 
548 // ----------------------------------------------------------------------------
549 // wxTaskBarJumpListImpl: definition of class for internal taskbar jump list
550 // implementation.
551 // ----------------------------------------------------------------------------
552 class wxTaskBarJumpListImpl
553 {
554 public:
555     wxTaskBarJumpListImpl(wxTaskBarJumpList *jumpList = NULL,
556                           const wxString& appID = wxEmptyString);
557     virtual ~wxTaskBarJumpListImpl();
558     void ShowRecentCategory(bool shown = true);
559     void HideRecentCategory();
560     void ShowFrequentCategory(bool shown = true);
561     void HideFrequentCategory();
562 
563     wxTaskBarJumpListCategory& GetTasks();
564     const wxTaskBarJumpListCategory& GetFrequentCategory();
565     const wxTaskBarJumpListCategory& GetRecentCategory();
566     const wxTaskBarJumpListCategories& GetCustomCategories() const;
567 
568     void AddCustomCategory(wxTaskBarJumpListCategory* category);
569     wxTaskBarJumpListCategory* RemoveCustomCategory(const wxString& title);
570     void DeleteCustomCategory(const wxString& title);
571     void Update();
572 
573 private:
574     bool BeginUpdate();
575     bool CommitUpdate();
576     void AddTasksToDestinationList();
577     void AddCustomCategoriesToDestinationList();
578     void LoadKnownCategory(const wxString& title);
579 
580     wxTaskBarJumpList *m_jumpList;
581 
582     wxCOMPtr<ICustomDestinationList>    m_destinationList;
583     wxCOMPtr<IObjectArray>              m_objectArray;
584 
585     wxScopedPtr<wxTaskBarJumpListCategory> m_tasks;
586     wxScopedPtr<wxTaskBarJumpListCategory> m_frequent;
587     wxScopedPtr<wxTaskBarJumpListCategory> m_recent;
588     wxTaskBarJumpListCategories m_customCategories;
589     bool m_recent_visible;
590     bool m_frequent_visible;
591 
592     // Application User Model ID.
593     wxString m_appID;
594 };
595 
596 // ----------------------------------------------------------------------------
597 // wxThumbBarButton Implementation.
598 // ----------------------------------------------------------------------------
599 wxIMPLEMENT_DYNAMIC_CLASS(wxThumbBarButton, wxObject);
600 
wxThumbBarButton(int id,const wxIcon & icon,const wxString & tooltip,bool enable,bool dismissOnClick,bool hasBackground,bool shown,bool interactive)601 wxThumbBarButton::wxThumbBarButton(int id,
602                                    const wxIcon& icon,
603                                    const wxString& tooltip,
604                                    bool enable,
605                                    bool dismissOnClick,
606                                    bool hasBackground,
607                                    bool shown,
608                                    bool interactive)
609     : m_id(id),
610       m_icon(icon),
611       m_tooltip(tooltip),
612       m_enable(enable),
613       m_dismissOnClick(dismissOnClick),
614       m_hasBackground(hasBackground),
615       m_shown(shown),
616       m_interactive(interactive),
617       m_taskBarButtonParent(NULL)
618 {
619 }
620 
Create(int id,const wxIcon & icon,const wxString & tooltip,bool enable,bool dismissOnClick,bool hasBackground,bool shown,bool interactive)621 bool wxThumbBarButton::Create(int id,
622                               const wxIcon& icon,
623                               const wxString& tooltip,
624                               bool enable,
625                               bool dismissOnClick,
626                               bool hasBackground,
627                               bool shown,
628                               bool interactive)
629 {
630     m_id = id;
631     m_icon = icon;
632     m_tooltip = tooltip;
633     m_enable = enable;
634     m_dismissOnClick = dismissOnClick;
635     m_hasBackground = hasBackground;
636     m_shown = shown;
637     m_interactive = interactive;
638     return true;
639 }
640 
Enable(bool enable)641 void wxThumbBarButton::Enable(bool enable)
642 {
643     if ( m_enable != enable )
644     {
645         m_enable = enable;
646         UpdateParentTaskBarButton();
647     }
648 }
649 
SetHasBackground(bool has)650 void wxThumbBarButton::SetHasBackground(bool has)
651 {
652     if ( m_hasBackground != has )
653     {
654         m_hasBackground = has;
655         UpdateParentTaskBarButton();
656     }
657 }
658 
EnableDismissOnClick(bool enable)659 void wxThumbBarButton::EnableDismissOnClick(bool enable)
660 {
661     if ( m_dismissOnClick != enable )
662     {
663         m_dismissOnClick = enable;
664         UpdateParentTaskBarButton();
665     }
666 }
667 
Show(bool shown)668 void wxThumbBarButton::Show(bool shown)
669 {
670     if ( m_shown != shown )
671     {
672         m_shown = shown;
673         UpdateParentTaskBarButton();
674     }
675 }
676 
SetInteractive(bool interactive)677 void wxThumbBarButton::SetInteractive(bool interactive)
678 {
679     if ( m_interactive != interactive )
680     {
681         m_interactive = interactive;
682         UpdateParentTaskBarButton();
683     }
684 }
685 
UpdateParentTaskBarButton()686 bool wxThumbBarButton::UpdateParentTaskBarButton()
687 {
688     if ( !m_taskBarButtonParent )
689         return false;
690 
691     return static_cast<wxTaskBarButtonImpl*>(
692                m_taskBarButtonParent)->InitOrUpdateThumbBarButtons();
693 }
694 
695 // ----------------------------------------------------------------------------
696 // wxTaskBarButtonImpl Implementation.
697 // ----------------------------------------------------------------------------
698 
699 /* static */
New(wxWindow * parent)700 wxTaskBarButton* wxTaskBarButton::New(wxWindow* parent)
701 {
702     wxITaskbarList3* taskbarList = NULL;
703 
704     HRESULT hr = CoCreateInstance
705                  (
706                     wxCLSID_TaskbarList,
707                     NULL,
708                     CLSCTX_INPROC_SERVER,
709                     wxIID_ITaskbarList3,
710                     reinterpret_cast<void **>(&taskbarList)
711                  );
712     if ( FAILED(hr) )
713     {
714         // Don't log this error, it may be normal when running under XP.
715         return NULL;
716     }
717 
718     hr = taskbarList->HrInit();
719     if ( FAILED(hr) )
720     {
721         // This is however unexpected.
722         wxLogApiError(wxT("ITaskbarList3::Init"), hr);
723 
724         taskbarList->Release();
725         return NULL;
726     }
727 
728     return new wxTaskBarButtonImpl(taskbarList, parent);
729 }
730 
wxTaskBarButtonImpl(wxITaskbarList3 * taskbarList,wxWindow * parent)731 wxTaskBarButtonImpl::wxTaskBarButtonImpl(wxITaskbarList3* taskbarList,
732                                          wxWindow* parent)
733     : m_parent(parent),
734       m_taskbarList(taskbarList),
735       m_progressRange(0),
736       m_progressValue(0),
737       m_progressState(wxTASKBAR_BUTTON_NO_PROGRESS),
738       m_hasInitThumbnailToolbar(false)
739 {
740 }
741 
~wxTaskBarButtonImpl()742 wxTaskBarButtonImpl::~wxTaskBarButtonImpl()
743 {
744     if ( m_taskbarList )
745       m_taskbarList->Release();
746 
747     for ( wxThumbBarButtons::iterator iter = m_thumbBarButtons.begin();
748           iter != m_thumbBarButtons.end();
749           ++iter)
750     {
751         delete (*iter);
752     }
753     m_thumbBarButtons.clear();
754 }
755 
Realize()756 void wxTaskBarButtonImpl::Realize()
757 {
758     // (Re-)apply all settings: this is needed if settings were made before the
759     // create message was sent, taskbar icon is hidden and shown again or
760     // explorer is restarted
761     SetProgressRange(m_progressRange);
762     SetProgressState(m_progressState);
763     if ( m_progressValue > 0 )
764         SetProgressValue(m_progressValue);
765     SetThumbnailTooltip(m_thumbnailTooltip);
766     SetOverlayIcon(m_overlayIcon, m_overlayIconDescription);
767     if ( !m_thumbnailClipRect.IsEmpty() )
768         SetThumbnailClip(m_thumbnailClipRect);
769     m_hasInitThumbnailToolbar = false;
770     InitOrUpdateThumbBarButtons();
771 }
772 
SetProgressRange(int range)773 void wxTaskBarButtonImpl::SetProgressRange(int range)
774 {
775     m_progressRange = range;
776     if ( m_progressRange == 0 )
777         SetProgressState(wxTASKBAR_BUTTON_NO_PROGRESS);
778 }
779 
SetProgressValue(int value)780 void wxTaskBarButtonImpl::SetProgressValue(int value)
781 {
782     m_progressValue = value;
783     m_taskbarList->SetProgressValue(m_parent->GetHWND(), value, m_progressRange);
784 }
785 
PulseProgress()786 void wxTaskBarButtonImpl::PulseProgress()
787 {
788     SetProgressState(wxTASKBAR_BUTTON_INDETERMINATE);
789 }
790 
Show(bool show)791 void wxTaskBarButtonImpl::Show(bool show)
792 {
793     if ( show )
794         m_taskbarList->AddTab(m_parent->GetHWND());
795     else
796         m_taskbarList->DeleteTab(m_parent->GetHWND());
797 }
798 
Hide()799 void wxTaskBarButtonImpl::Hide()
800 {
801     Show(false);
802 }
803 
SetThumbnailTooltip(const wxString & tooltip)804 void wxTaskBarButtonImpl::SetThumbnailTooltip(const wxString& tooltip)
805 {
806     m_thumbnailTooltip = tooltip;
807     m_taskbarList->SetThumbnailTooltip(m_parent->GetHWND(), tooltip.wc_str());
808 }
809 
SetProgressState(wxTaskBarButtonState state)810 void wxTaskBarButtonImpl::SetProgressState(wxTaskBarButtonState state)
811 {
812     m_progressState = state;
813     m_taskbarList->SetProgressState(m_parent->GetHWND(), static_cast<TBPFLAG>(state));
814 }
815 
SetOverlayIcon(const wxIcon & icon,const wxString & description)816 void wxTaskBarButtonImpl::SetOverlayIcon(const wxIcon& icon,
817                                          const wxString& description)
818 {
819     m_overlayIcon = icon;
820     m_overlayIconDescription = description;
821     m_taskbarList->SetOverlayIcon(m_parent->GetHWND(),
822                                   GetHiconOf(icon),
823                                   description.wc_str());
824 }
825 
SetThumbnailClip(const wxRect & rect)826 void wxTaskBarButtonImpl::SetThumbnailClip(const wxRect& rect)
827 {
828     m_thumbnailClipRect = rect;
829     RECT rc;
830     wxCopyRectToRECT(rect, rc);
831     m_taskbarList->SetThumbnailClip(m_parent->GetHWND(), rect.IsEmpty() ? NULL : &rc);
832 }
833 
SetThumbnailContents(const wxWindow * child)834 void wxTaskBarButtonImpl::SetThumbnailContents(const wxWindow *child)
835 {
836     SetThumbnailClip(child->GetRect());
837 }
838 
AppendThumbBarButton(wxThumbBarButton * button)839 bool wxTaskBarButtonImpl::AppendThumbBarButton(wxThumbBarButton *button)
840 {
841     wxASSERT_MSG( m_thumbBarButtons.size() < MAX_BUTTON_COUNT,
842                   "Number of ThumbBarButtons and separators is limited to 7" );
843 
844     button->SetParent(this);
845     m_thumbBarButtons.push_back(button);
846     return InitOrUpdateThumbBarButtons();
847 }
848 
AppendSeparatorInThumbBar()849 bool wxTaskBarButtonImpl::AppendSeparatorInThumbBar()
850 {
851     wxASSERT_MSG( m_thumbBarButtons.size() < MAX_BUTTON_COUNT,
852                   "Number of ThumbBarButtons and separators is limited to 7" );
853 
854     // Append a disable ThumbBarButton without background can simulate the
855     // behavior of appending a separator.
856     wxThumbBarButton *separator = new wxThumbBarButton(wxID_ANY,
857                                                        wxNullIcon,
858                                                        wxEmptyString,
859                                                        false,
860                                                        false,
861                                                        false);
862     m_thumbBarButtons.push_back(separator);
863     return InitOrUpdateThumbBarButtons();
864 }
865 
InsertThumbBarButton(size_t pos,wxThumbBarButton * button)866 bool wxTaskBarButtonImpl::InsertThumbBarButton(size_t pos,
867                                                wxThumbBarButton *button)
868 {
869     wxASSERT_MSG( m_thumbBarButtons.size() < MAX_BUTTON_COUNT,
870                   "Number of ThumbBarButtons and separators is limited to 7" );
871     wxASSERT_MSG( pos <= m_thumbBarButtons.size(),
872                   "Invalid index when inserting the button" );
873 
874     button->SetParent(this);
875     m_thumbBarButtons.insert(m_thumbBarButtons.begin() + pos, button);
876     return InitOrUpdateThumbBarButtons();
877 }
878 
RemoveThumbBarButton(wxThumbBarButton * button)879 wxThumbBarButton* wxTaskBarButtonImpl::RemoveThumbBarButton(
880     wxThumbBarButton *button)
881 {
882     return RemoveThumbBarButton(button->GetID());
883 }
884 
RemoveThumbBarButton(int id)885 wxThumbBarButton* wxTaskBarButtonImpl::RemoveThumbBarButton(int id)
886 {
887     for ( wxThumbBarButtons::iterator iter = m_thumbBarButtons.begin();
888           iter != m_thumbBarButtons.end();
889           ++iter )
890     {
891         wxThumbBarButton* button = *iter;
892         if ( id == button->GetID() )
893         {
894             m_thumbBarButtons.erase(iter);
895             button->SetParent(NULL);
896             InitOrUpdateThumbBarButtons();
897             return button;
898         }
899     }
900 
901     return NULL;
902 }
903 
InitOrUpdateThumbBarButtons()904 bool wxTaskBarButtonImpl::InitOrUpdateThumbBarButtons()
905 {
906     THUMBBUTTON buttons[MAX_BUTTON_COUNT];
907     HRESULT hr;
908 
909     for ( size_t i = 0; i < MAX_BUTTON_COUNT; ++i )
910     {
911         memset(&buttons[i], 0, sizeof buttons[i]);
912         buttons[i].iId = i;
913         buttons[i].dwFlags = THBF_HIDDEN;
914         buttons[i].dwMask = static_cast<THUMBBUTTONMASK>(THB_FLAGS);
915     }
916 
917     for ( size_t i = 0; i < m_thumbBarButtons.size(); ++i )
918     {
919         buttons[i].hIcon = GetHiconOf(m_thumbBarButtons[i]->GetIcon());
920         buttons[i].dwFlags = GetNativeThumbButtonFlags(*m_thumbBarButtons[i]);
921         buttons[i].dwMask = static_cast<THUMBBUTTONMASK>(THB_ICON | THB_FLAGS);
922         wxString tooltip = m_thumbBarButtons[i]->GetTooltip();
923         if ( tooltip.empty() )
924             continue;
925 
926         // Truncate the tooltip if its length longer than szTip(THUMBBUTTON)
927         // allowed length (260).
928         tooltip.Truncate(260);
929         wxStrlcpy(buttons[i].szTip, tooltip.wc_str(), tooltip.length());
930         buttons[i].dwMask =
931             static_cast<THUMBBUTTONMASK>(buttons[i].dwMask | THB_TOOLTIP);
932     }
933 
934     if ( !m_hasInitThumbnailToolbar )
935     {
936         hr = m_taskbarList->ThumbBarAddButtons(m_parent->GetHWND(),
937                                                MAX_BUTTON_COUNT,
938                                                buttons);
939         if ( FAILED(hr) )
940         {
941             wxLogApiError(wxT("ITaskbarList3::ThumbBarAddButtons"), hr);
942         }
943         m_hasInitThumbnailToolbar = true;
944     }
945     else
946     {
947         hr = m_taskbarList->ThumbBarUpdateButtons(m_parent->GetHWND(),
948                                                   MAX_BUTTON_COUNT,
949                                                   buttons);
950         if ( FAILED(hr) )
951         {
952             wxLogApiError(wxT("ITaskbarList3::ThumbBarUpdateButtons"), hr);
953         }
954     }
955 
956     return SUCCEEDED(hr);
957 }
958 
GetThumbBarButtonByIndex(size_t index)959 wxThumbBarButton* wxTaskBarButtonImpl::GetThumbBarButtonByIndex(size_t index)
960 {
961     if ( index >= m_thumbBarButtons.size() )
962         return NULL;
963 
964     return m_thumbBarButtons[index];
965 }
966 
967 // ----------------------------------------------------------------------------
968 // wxTaskBarJumpListItem Implementation.
969 // ----------------------------------------------------------------------------
wxTaskBarJumpListItem(wxTaskBarJumpListCategory * parent,wxTaskBarJumpListItemType type,const wxString & title,const wxString & filePath,const wxString & arguments,const wxString & tooltip,const wxString & iconPath,int iconIndex)970 wxTaskBarJumpListItem::wxTaskBarJumpListItem(wxTaskBarJumpListCategory *parent,
971                                              wxTaskBarJumpListItemType type,
972                                              const wxString& title,
973                                              const wxString& filePath,
974                                              const wxString& arguments,
975                                              const wxString& tooltip,
976                                              const wxString& iconPath,
977                                              int iconIndex)
978     : m_parentCategory(parent),
979       m_type(type),
980       m_title(title),
981       m_filePath(filePath),
982       m_arguments(arguments),
983       m_tooltip(tooltip),
984       m_iconPath(iconPath),
985       m_iconIndex(iconIndex)
986 {
987 }
988 
GetType() const989 wxTaskBarJumpListItemType wxTaskBarJumpListItem::GetType() const
990 {
991     return m_type;
992 }
993 
SetType(wxTaskBarJumpListItemType type)994 void wxTaskBarJumpListItem::SetType(wxTaskBarJumpListItemType type)
995 {
996     m_type = type;
997     if ( m_parentCategory )
998         m_parentCategory->Update();
999 }
1000 
GetTitle() const1001 const wxString& wxTaskBarJumpListItem::GetTitle() const
1002 {
1003     return m_title;
1004 }
1005 
SetTitle(const wxString & title)1006 void wxTaskBarJumpListItem::SetTitle(const wxString& title)
1007 {
1008     m_title = title;
1009     if ( m_parentCategory )
1010         m_parentCategory->Update();
1011 }
1012 
GetFilePath() const1013 const wxString& wxTaskBarJumpListItem::GetFilePath() const
1014 {
1015     return m_filePath;
1016 }
1017 
SetFilePath(const wxString & filePath)1018 void wxTaskBarJumpListItem::SetFilePath(const wxString& filePath)
1019 {
1020     m_filePath = filePath;
1021     if ( m_parentCategory )
1022         m_parentCategory->Update();
1023 }
1024 
GetArguments() const1025 const wxString& wxTaskBarJumpListItem::GetArguments() const
1026 {
1027     return m_arguments;
1028 }
1029 
SetArguments(const wxString & arguments)1030 void wxTaskBarJumpListItem::SetArguments(const wxString& arguments)
1031 {
1032     m_arguments = arguments;
1033     if ( m_parentCategory )
1034         m_parentCategory->Update();
1035 }
1036 
GetTooltip() const1037 const wxString& wxTaskBarJumpListItem::GetTooltip() const
1038 {
1039     return m_tooltip;
1040 }
1041 
SetTooltip(const wxString & tooltip)1042 void wxTaskBarJumpListItem::SetTooltip(const wxString& tooltip)
1043 {
1044     m_tooltip = tooltip;
1045     if ( m_parentCategory )
1046         m_parentCategory->Update();
1047 }
1048 
GetIconPath() const1049 const wxString& wxTaskBarJumpListItem::GetIconPath() const
1050 {
1051     return m_iconPath;
1052 }
1053 
SetIconPath(const wxString & iconPath)1054 void wxTaskBarJumpListItem::SetIconPath(const wxString& iconPath)
1055 {
1056     m_iconPath = iconPath;
1057     if ( m_parentCategory )
1058         m_parentCategory->Update();
1059 }
1060 
GetIconIndex() const1061 int wxTaskBarJumpListItem::GetIconIndex() const
1062 {
1063     return m_iconIndex;
1064 }
1065 
SetIconIndex(int iconIndex)1066 void wxTaskBarJumpListItem::SetIconIndex(int iconIndex)
1067 {
1068     m_iconIndex = iconIndex;
1069     if ( m_parentCategory )
1070         m_parentCategory->Update();
1071 }
1072 
GetCategory() const1073 wxTaskBarJumpListCategory* wxTaskBarJumpListItem::GetCategory() const
1074 {
1075     return m_parentCategory;
1076 }
1077 
SetCategory(wxTaskBarJumpListCategory * category)1078 void wxTaskBarJumpListItem::SetCategory(wxTaskBarJumpListCategory *category)
1079 {
1080     m_parentCategory = category;
1081 }
1082 
1083 // ----------------------------------------------------------------------------
1084 // wxTaskBarJumpListCategory Implementation.
1085 // ----------------------------------------------------------------------------
wxTaskBarJumpListCategory(wxTaskBarJumpList * parent,const wxString & title)1086 wxTaskBarJumpListCategory::wxTaskBarJumpListCategory(wxTaskBarJumpList *parent,
1087                                                      const wxString& title)
1088     : m_parent(parent),
1089       m_title(title)
1090 {
1091 }
1092 
~wxTaskBarJumpListCategory()1093 wxTaskBarJumpListCategory::~wxTaskBarJumpListCategory()
1094 {
1095     for ( wxTaskBarJumpListItems::iterator it = m_items.begin();
1096           it != m_items.end();
1097           ++it )
1098     {
1099         delete *it;
1100     }
1101 }
1102 
1103 wxTaskBarJumpListItem*
Append(wxTaskBarJumpListItem * item)1104 wxTaskBarJumpListCategory::Append(wxTaskBarJumpListItem *item)
1105 {
1106     m_items.push_back(item);
1107     item->SetCategory(this);
1108     Update();
1109 
1110     return item;
1111 }
1112 
Delete(wxTaskBarJumpListItem * item)1113 void wxTaskBarJumpListCategory::Delete(wxTaskBarJumpListItem *item)
1114 {
1115     item = Remove(item);
1116     item->SetCategory(NULL);
1117     Update();
1118 
1119     if ( item )
1120         delete item;
1121 }
1122 
1123 wxTaskBarJumpListItem*
Remove(wxTaskBarJumpListItem * item)1124 wxTaskBarJumpListCategory::Remove(wxTaskBarJumpListItem *item)
1125 {
1126     for (wxTaskBarJumpListItems::iterator it = m_items.begin();
1127          it != m_items.end();
1128          ++it)
1129     {
1130         if ( *it == item )
1131         {
1132             m_items.erase(it);
1133             item->SetCategory(NULL);
1134             Update();
1135             return item;
1136         }
1137     }
1138 
1139     return NULL;
1140 }
1141 
1142 wxTaskBarJumpListItem*
FindItemByPosition(size_t pos) const1143 wxTaskBarJumpListCategory::FindItemByPosition(size_t pos) const
1144 {
1145     wxASSERT_MSG( pos < m_items.size(), "invalid pos." );
1146     return m_items[pos];
1147 }
1148 
1149 wxTaskBarJumpListItem*
Insert(size_t pos,wxTaskBarJumpListItem * item)1150 wxTaskBarJumpListCategory::Insert(size_t pos, wxTaskBarJumpListItem *item)
1151 {
1152     wxASSERT_MSG( pos <= m_items.size(), "invalid pos." );
1153     m_items.insert(m_items.begin() + pos, item);
1154     item->SetCategory(this);
1155     Update();
1156 
1157     return item;
1158 }
1159 
1160 wxTaskBarJumpListItem*
Prepend(wxTaskBarJumpListItem * item)1161 wxTaskBarJumpListCategory::Prepend(wxTaskBarJumpListItem *item)
1162 {
1163     return Insert(0, item);
1164 }
1165 
SetTitle(const wxString & title)1166 void wxTaskBarJumpListCategory::SetTitle(const wxString& title)
1167 {
1168     m_title = title;
1169     Update();
1170 }
1171 
GetTitle() const1172 const wxString& wxTaskBarJumpListCategory::GetTitle() const
1173 {
1174     return m_title;
1175 }
1176 
GetItems() const1177 const wxTaskBarJumpListItems& wxTaskBarJumpListCategory::GetItems() const
1178 {
1179     return m_items;
1180 }
1181 
Update()1182 void wxTaskBarJumpListCategory::Update()
1183 {
1184     if ( m_parent )
1185         m_parent->Update();
1186 }
1187 
1188 // ----------------------------------------------------------------------------
1189 // wxTaskBarJumpList Implementation.
1190 // ----------------------------------------------------------------------------
wxTaskBarJumpList(const wxString & appID)1191 wxTaskBarJumpList::wxTaskBarJumpList(const wxString& appID)
1192     : m_jumpListImpl(new wxTaskBarJumpListImpl(this, appID))
1193 {
1194 }
1195 
~wxTaskBarJumpList()1196 wxTaskBarJumpList::~wxTaskBarJumpList()
1197 {
1198     delete m_jumpListImpl;
1199 }
1200 
GetTasks() const1201 wxTaskBarJumpListCategory& wxTaskBarJumpList::GetTasks() const
1202 {
1203     return m_jumpListImpl->GetTasks();
1204 }
1205 
ShowRecentCategory(bool shown)1206 void wxTaskBarJumpList::ShowRecentCategory(bool shown)
1207 {
1208     m_jumpListImpl->ShowRecentCategory(shown);
1209 }
1210 
HideRecentCategory()1211 void wxTaskBarJumpList::HideRecentCategory()
1212 {
1213     m_jumpListImpl->HideRecentCategory();
1214 }
1215 
ShowFrequentCategory(bool shown)1216 void wxTaskBarJumpList::ShowFrequentCategory(bool shown)
1217 {
1218     m_jumpListImpl->ShowFrequentCategory(shown);
1219 }
1220 
HideFrequentCategory()1221 void wxTaskBarJumpList::HideFrequentCategory()
1222 {
1223     m_jumpListImpl->HideFrequentCategory();
1224 }
1225 
GetFrequentCategory() const1226 const wxTaskBarJumpListCategory& wxTaskBarJumpList::GetFrequentCategory() const
1227 {
1228     return m_jumpListImpl->GetFrequentCategory();
1229 }
1230 
GetRecentCategory() const1231 const wxTaskBarJumpListCategory& wxTaskBarJumpList::GetRecentCategory() const
1232 {
1233     return m_jumpListImpl->GetRecentCategory();
1234 }
1235 
1236 const wxTaskBarJumpListCategories&
GetCustomCategories() const1237 wxTaskBarJumpList::GetCustomCategories() const
1238 {
1239     return m_jumpListImpl->GetCustomCategories();
1240 }
1241 
AddCustomCategory(wxTaskBarJumpListCategory * category)1242 void wxTaskBarJumpList::AddCustomCategory(wxTaskBarJumpListCategory* category)
1243 {
1244     m_jumpListImpl->AddCustomCategory(category);
1245 }
1246 
RemoveCustomCategory(const wxString & title)1247 wxTaskBarJumpListCategory* wxTaskBarJumpList::RemoveCustomCategory(
1248     const wxString& title)
1249 {
1250     return m_jumpListImpl->RemoveCustomCategory(title);
1251 }
1252 
DeleteCustomCategory(const wxString & title)1253 void wxTaskBarJumpList::DeleteCustomCategory(const wxString& title)
1254 {
1255     m_jumpListImpl->DeleteCustomCategory(title);
1256 }
1257 
Update()1258 void wxTaskBarJumpList::Update()
1259 {
1260     m_jumpListImpl->Update();
1261 }
1262 
1263 // ----------------------------------------------------------------------------
1264 // wxTaskBarJumpListImpl Implementation.
1265 // ----------------------------------------------------------------------------
wxTaskBarJumpListImpl(wxTaskBarJumpList * jumpList,const wxString & appID)1266 wxTaskBarJumpListImpl::wxTaskBarJumpListImpl(wxTaskBarJumpList *jumpList,
1267                                              const wxString& appID)
1268     : m_jumpList(jumpList),
1269       m_destinationList(NULL)
1270 {
1271     m_appID = appID;
1272     HRESULT hr = CoCreateInstance
1273                  (
1274                     wxCLSID_DestinationList,
1275                     NULL,
1276                     CLSCTX_INPROC_SERVER,
1277                     wxIID_ICustomDestinationList,
1278                     reinterpret_cast<void**> (&(m_destinationList))
1279                 );
1280     if ( FAILED(hr) )
1281     {
1282         wxLogApiError(wxT("CoCreateInstance(wxCLSID_DestinationList)"), hr);
1283         return;
1284     }
1285 }
1286 
~wxTaskBarJumpListImpl()1287 wxTaskBarJumpListImpl::~wxTaskBarJumpListImpl()
1288 {
1289     for ( wxTaskBarJumpListCategories::iterator it = m_customCategories.begin();
1290           it != m_customCategories.end();
1291           ++it )
1292     {
1293         delete *it;
1294     }
1295 }
1296 
Update()1297 void wxTaskBarJumpListImpl::Update()
1298 {
1299     if ( !BeginUpdate() )
1300         return;
1301 
1302     AddTasksToDestinationList();
1303     AddCustomCategoriesToDestinationList();
1304     if ( m_recent_visible )
1305         m_destinationList->AppendKnownCategory(KDC_RECENT);
1306     if ( m_frequent_visible )
1307         m_destinationList->AppendKnownCategory(KDC_FREQUENT);
1308     CommitUpdate();
1309 }
1310 
GetTasks()1311 wxTaskBarJumpListCategory& wxTaskBarJumpListImpl::GetTasks()
1312 {
1313     if ( m_tasks.get() == NULL )
1314         m_tasks.reset(new wxTaskBarJumpListCategory(m_jumpList, wxT("Tasks")));
1315 
1316     return *(m_tasks.get());
1317 }
1318 
ShowRecentCategory(bool shown)1319 void wxTaskBarJumpListImpl::ShowRecentCategory(bool shown)
1320 {
1321     m_recent_visible = shown;
1322 }
1323 
HideRecentCategory()1324 void wxTaskBarJumpListImpl::HideRecentCategory()
1325 {
1326     ShowRecentCategory(false);
1327 }
1328 
ShowFrequentCategory(bool shown)1329 void wxTaskBarJumpListImpl::ShowFrequentCategory(bool shown)
1330 {
1331     m_frequent_visible = shown;
1332 }
1333 
HideFrequentCategory()1334 void wxTaskBarJumpListImpl::HideFrequentCategory()
1335 {
1336     ShowFrequentCategory(false);
1337 }
1338 
GetFrequentCategory()1339 const wxTaskBarJumpListCategory& wxTaskBarJumpListImpl::GetFrequentCategory()
1340 {
1341     wxString title = wxT("Frequent");
1342     if ( m_frequent.get() == NULL )
1343         m_frequent.reset(new wxTaskBarJumpListCategory(m_jumpList, title));
1344     LoadKnownCategory(title);
1345 
1346     return *m_frequent.get();
1347 }
1348 
GetRecentCategory()1349 const wxTaskBarJumpListCategory& wxTaskBarJumpListImpl::GetRecentCategory()
1350 {
1351     wxString title = wxT("Recent");
1352     if ( m_recent.get() == NULL )
1353         m_recent.reset(new wxTaskBarJumpListCategory(m_jumpList, title));
1354     LoadKnownCategory(title);
1355 
1356     return *m_recent.get();
1357 }
1358 
1359 const wxTaskBarJumpListCategories&
GetCustomCategories() const1360 wxTaskBarJumpListImpl::GetCustomCategories() const
1361 {
1362     return m_customCategories;
1363 }
1364 
1365 void
AddCustomCategory(wxTaskBarJumpListCategory * category)1366 wxTaskBarJumpListImpl::AddCustomCategory(wxTaskBarJumpListCategory *category)
1367 {
1368     wxASSERT_MSG( category != NULL, "Invalid category." );
1369     m_customCategories.push_back(category);
1370 }
1371 
1372 wxTaskBarJumpListCategory*
RemoveCustomCategory(const wxString & title)1373 wxTaskBarJumpListImpl::RemoveCustomCategory(const wxString& title)
1374 {
1375     for ( wxTaskBarJumpListCategories::iterator it = m_customCategories.begin();
1376           it != m_customCategories.end();
1377           ++it )
1378     {
1379         wxTaskBarJumpListCategory* tbJlCat = *it;
1380         if ( tbJlCat->GetTitle() == title )
1381         {
1382             m_customCategories.erase(it);
1383             return tbJlCat;
1384         }
1385     }
1386 
1387     return NULL;
1388 }
1389 
DeleteCustomCategory(const wxString & title)1390 void wxTaskBarJumpListImpl::DeleteCustomCategory(const wxString& title)
1391 {
1392     wxTaskBarJumpListCategory* category = RemoveCustomCategory(title);
1393     if ( category )
1394         delete category;
1395 }
1396 
BeginUpdate()1397 bool wxTaskBarJumpListImpl::BeginUpdate()
1398 {
1399     if ( m_destinationList == NULL )
1400         return false;
1401 
1402     unsigned int max_count = 0;
1403     m_objectArray = NULL;
1404     HRESULT hr = m_destinationList->BeginList(&max_count,
1405         wxIID_IObjectArray, reinterpret_cast<void**>(&m_objectArray));
1406     if ( !m_appID.empty() )
1407         m_destinationList->SetAppID(m_appID.wc_str());
1408 
1409     return SUCCEEDED(hr);
1410 }
1411 
CommitUpdate()1412 bool wxTaskBarJumpListImpl::CommitUpdate()
1413 {
1414     return SUCCEEDED(m_destinationList->CommitList());
1415 }
1416 
AddTasksToDestinationList()1417 void wxTaskBarJumpListImpl::AddTasksToDestinationList()
1418 {
1419     if ( !m_tasks.get() )
1420         return;
1421 
1422     wxCOMPtr<IObjectCollection> collection(CreateObjectCollection());
1423     if ( !collection )
1424         return;
1425 
1426     const wxTaskBarJumpListItems& tasks = m_tasks->GetItems();
1427     for ( wxTaskBarJumpListItems::const_iterator it = tasks.begin();
1428           it != tasks.end();
1429           ++it )
1430     {
1431         wxASSERT_MSG( ((*it)->GetType() == wxTASKBAR_JUMP_LIST_TASK ||
1432                        (*it)->GetType() == wxTASKBAR_JUMP_LIST_SEPARATOR),
1433                       "Invalid task Item." );
1434         AddShellLink(collection, *(*it));
1435     }
1436     m_destinationList->AddUserTasks(collection);
1437 }
1438 
AddCustomCategoriesToDestinationList()1439 void wxTaskBarJumpListImpl::AddCustomCategoriesToDestinationList()
1440 {
1441     for ( wxTaskBarJumpListCategories::iterator it = m_customCategories.begin();
1442           it != m_customCategories.end();
1443           ++it )
1444     {
1445         wxCOMPtr<IObjectCollection> collection(CreateObjectCollection());
1446         if ( !collection )
1447             continue;
1448 
1449         const wxTaskBarJumpListItems& tasks = (*it)->GetItems();
1450         for ( wxTaskBarJumpListItems::const_iterator iter = tasks.begin();
1451               iter != tasks.end();
1452               ++iter )
1453         {
1454             wxASSERT_MSG(
1455                 (*iter)->GetType() == wxTASKBAR_JUMP_LIST_DESTINATION,
1456                 "Invalid category item." );
1457             AddShellLink(collection, *(*iter));
1458         }
1459         m_destinationList->AppendCategory((*it)->GetTitle().wc_str(),
1460                                           collection);
1461     }
1462 }
1463 
LoadKnownCategory(const wxString & title)1464 void wxTaskBarJumpListImpl::LoadKnownCategory(const wxString& title)
1465 {
1466     wxCOMPtr<IApplicationDocumentLists> docList;
1467     HRESULT hr = CoCreateInstance
1468                  (
1469                     wxCLSID_ApplicationDocumentLists,
1470                     NULL,
1471                     CLSCTX_INPROC_SERVER,
1472                     wxIID_IApplicationDocumentLists,
1473                     reinterpret_cast<void **>(&docList)
1474                  );
1475     if ( FAILED(hr) )
1476     {
1477         wxLogApiError("CoCreateInstance(wxCLSID_ApplicationDocumentLists)", hr);
1478         return;
1479     }
1480     if ( !m_appID.empty() )
1481         docList->SetAppID(m_appID.wc_str());
1482 
1483     wxCOMPtr<IObjectArray> array;
1484     wxASSERT_MSG( title == "Recent" || title == "Frequent", "Invalid title." );
1485     hr = docList->GetList
1486                  (
1487                      title == "Recent" ? ADLT_RECENT : ADLT_FREQUENT,
1488                      0,
1489                      wxIID_IObjectArray,
1490                      reinterpret_cast<void **>(&array)
1491                  );
1492     if ( FAILED(hr) )
1493     {
1494         wxLogApiError("IApplicationDocumentLists::GetList", hr);
1495         return;
1496     }
1497 
1498     UINT count = 0;
1499     array->GetCount(&count);
1500     for (UINT i = 0; i < count; ++i)
1501     {
1502         wxCOMPtr<IUnknown> collectionItem;
1503         hr = array->GetAt(i, wxIID_IUnknown,
1504                           reinterpret_cast<void **>(&collectionItem));
1505         if ( FAILED(hr) )
1506         {
1507             wxLogApiError("IObjectArray::GetAt", hr);
1508             continue;
1509         }
1510 
1511         wxCOMPtr<IShellLink> shellLink;
1512         wxCOMPtr<IShellItem> shellItem;
1513         wxTaskBarJumpListItem* item = NULL;
1514 
1515         if ( SUCCEEDED(collectionItem->QueryInterface(
1516                  wxIID_IShellLink, reinterpret_cast<void**>(&shellLink))) )
1517         {
1518             item = GetItemFromIShellLink(shellLink);
1519         }
1520         else if ( SUCCEEDED(collectionItem->QueryInterface(
1521                       wxIID_IShellItem, reinterpret_cast<void**>(&shellItem))) )
1522         {
1523             item = GetItemFromIShellItem(shellItem);
1524         }
1525         else
1526         {
1527             wxLogError("Can not query interfaces: IShellLink or IShellItem.");
1528         }
1529 
1530         if ( item )
1531         {
1532             if ( title == wxT("Frequent") )
1533                 m_frequent->Append(item);
1534             else
1535                 m_recent->Append(item);
1536         }
1537     }
1538 }
1539 
1540 #endif // wxUSE_TASKBARBUTTON
1541