remove assert looking for new compatibilityMode DOCX
[LibreOffice.git] / shell / source / win32 / jumplist / JumpList.cxx
blobd81aa1aa16e48928e21a60eddb2a5c559c8a49ae
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sal/config.h>
12 #include <algorithm>
13 #include <cassert>
15 #include <cppuhelper/basemutex.hxx>
16 #include <cppuhelper/compbase.hxx>
17 #include <comphelper/sequence.hxx>
18 #include <cppuhelper/supportsservice.hxx>
19 #include <o3tl/char16_t2wchar_t.hxx>
20 #include <o3tl/runtimetooustring.hxx>
21 #include <o3tl/safeCoInitUninit.hxx>
22 #include <osl/file.hxx>
23 #include <osl/mutex.hxx>
24 #include <osl/process.h>
25 #include <sal/log.hxx>
26 #include <systools/win32/comtools.hxx>
27 #include <systools/win32/extended_max_path.hxx>
29 #include <com/sun/star/lang/IllegalArgumentException.hpp>
30 #include <com/sun/star/lang/XServiceInfo.hpp>
31 #include <com/sun/star/system/windows/JumpListItem.hpp>
32 #include <com/sun/star/system/windows/XJumpList.hpp>
33 #include <com/sun/star/uno/XComponentContext.hpp>
34 #include <com/sun/star/util/InvalidStateException.hpp>
36 #include <prewin.h>
37 #include <Shlobj.h>
38 #include <propkey.h>
39 #include <propvarutil.h>
40 #include <postwin.h>
42 using namespace comphelper;
43 using namespace cppu;
44 using namespace css;
45 using namespace css::uno;
46 using namespace css::lang;
47 using namespace css::system::windows;
48 using namespace css::util;
49 using namespace osl;
50 using namespace sal::systools;
52 namespace
54 class JumpListImpl : public BaseMutex, public WeakComponentImplHelper<XJumpList, XServiceInfo>
56 Reference<XComponentContext> m_xContext;
57 COMReference<ICustomDestinationList> m_aDestinationList;
58 COMReference<IObjectArray> m_aRemoved;
59 bool m_isListOpen;
61 public:
62 explicit JumpListImpl(const Reference<XComponentContext>& xContext);
64 // XJumpList
65 virtual void SAL_CALL beginList(const OUString& sApplication) override;
66 virtual void SAL_CALL appendCategory(const OUString& sCategory,
67 const Sequence<JumpListItem>& aJumpListItems) override;
68 virtual void SAL_CALL addTasks(const Sequence<JumpListItem>& aJumpListItems) override;
69 virtual void SAL_CALL showRecentFiles() override;
70 virtual void SAL_CALL showFrequentFiles() override;
71 virtual void SAL_CALL commitList() override;
72 virtual void SAL_CALL abortList() override;
73 virtual void SAL_CALL deleteList(const OUString& sApplication) override;
74 virtual Sequence<JumpListItem> SAL_CALL getRemovedItems(const OUString& sApplication) override;
76 // XServiceInfo
77 virtual OUString SAL_CALL getImplementationName() override;
78 virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
79 virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
82 JumpListImpl::JumpListImpl(const Reference<XComponentContext>& xContext)
83 : WeakComponentImplHelper(m_aMutex)
84 , m_xContext(xContext)
85 , m_aDestinationList(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER)
86 , m_isListOpen(false)
90 // Determines if the provided IShellLinkItem is listed in the array of items that the user has removed
91 bool lcl_isItemInArray(COMReference<IShellLinkW> pShellLinkItem,
92 COMReference<IObjectArray> poaRemoved)
94 UINT nItems;
95 ThrowIfFailed(poaRemoved->GetCount(&nItems), "GetCount failed.");
97 COMReference<IShellLinkW> pShellLinkItemCompare;
98 for (UINT i = 0; i < nItems; i++)
100 if (!SUCCEEDED(poaRemoved->GetAt(i, IID_PPV_ARGS(&pShellLinkItemCompare))))
101 continue;
103 PROPVARIANT propvar;
104 COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW);
105 ThrowIfFailed(pps->GetValue(PKEY_Title, &propvar), "GetValue failed.");
106 OUString title(o3tl::toU(PropVariantToStringWithDefault(propvar, L"")));
108 COMReference<IPropertyStore> ppsCompare(pShellLinkItemCompare, COM_QUERY_THROW);
109 ThrowIfFailed(ppsCompare->GetValue(PKEY_Title, &propvar), "GetValue failed.");
110 OUString titleCompare(o3tl::toU(PropVariantToStringWithDefault(propvar, L"")));
111 PropVariantClear(&propvar);
113 if (title == titleCompare)
114 return true;
117 return false;
121 void SAL_CALL JumpListImpl::beginList(const OUString& sApplication)
123 if (m_isListOpen)
124 throw InvalidStateException("There is already a list open. Close it with 'commitList'");
126 if (sApplication != "Writer" && sApplication != "Calc" && sApplication != "Impress"
127 && sApplication != "Draw" && sApplication != "Math" && sApplication != "Base"
128 && sApplication != "Startcenter")
130 throw IllegalArgumentException(
131 "Parameter 'application' must be one of 'Writer', 'Calc', 'Impress', 'Draw', "
132 "'Math', 'Base', 'Startcenter'.",
133 getXWeak(), 1);
135 OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplication);
139 m_aDestinationList->SetAppID(o3tl::toW(sApplicationID.getStr()));
141 UINT min_slots;
143 ThrowIfFailed(m_aDestinationList->BeginList(&min_slots, IID_PPV_ARGS(&m_aRemoved)),
144 "BeginList failed");
145 m_isListOpen = true;
147 catch (const ComError& e)
149 SAL_WARN("shell.jumplist", e.what());
153 void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory,
154 const Sequence<JumpListItem>& aJumpListItems)
156 if (!m_isListOpen)
157 throw InvalidStateException("No list open. Open it with 'beginList'");
159 if (sCategory.isEmpty())
161 throw IllegalArgumentException("Parameter 'category' must not be empty", getXWeak(), 1);
166 OUString sofficeURL;
167 OUString sofficePath;
168 oslProcessError err = osl_getExecutableFile(&sofficeURL.pData);
169 FileBase::getSystemPathFromFileURL(sofficeURL, sofficePath);
170 if (err != osl_Process_E_None)
172 SAL_WARN("shell.jumplist", "osl_getExecutableFile failed");
173 return;
175 // We need to run soffice.exe, not soffice.bin
176 sofficePath = sofficePath.replaceFirst("soffice.bin", "soffice.exe");
178 COMReference<IObjectCollection> aCollection(CLSID_EnumerableObjectCollection, nullptr,
179 CLSCTX_INPROC_SERVER);
181 for (auto const& item : aJumpListItems)
183 if (item.name.isEmpty())
184 continue;
187 COMReference<IShellLinkW> pShellLinkItem(CLSID_ShellLink, nullptr,
188 CLSCTX_INPROC_SERVER);
191 COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW);
193 PROPVARIANT propvar;
194 ThrowIfFailed(
195 InitPropVariantFromString(o3tl::toW(item.name.getStr()), &propvar),
196 "InitPropVariantFromString failed.");
198 ThrowIfFailed(pps->SetValue(PKEY_Title, propvar), "SetValue failed.");
200 ThrowIfFailed(pps->Commit(), "Commit failed.");
202 PropVariantClear(&propvar);
204 ThrowIfFailed(
205 pShellLinkItem->SetDescription(o3tl::toW(item.description.getStr())),
206 Concat2View("Setting description '" + item.description.toUtf8() + "' failed."));
208 ThrowIfFailed(pShellLinkItem->SetPath(o3tl::toW(sofficePath.getStr())),
209 Concat2View("Setting path '" + sofficePath.toUtf8() + "' failed."));
211 ThrowIfFailed(
212 pShellLinkItem->SetArguments(o3tl::toW(item.arguments.getStr())),
213 Concat2View("Setting arguments '" + item.arguments.toUtf8() + "' failed."));
215 ThrowIfFailed(
216 pShellLinkItem->SetIconLocation(o3tl::toW(item.iconPath.getStr()), 0),
217 Concat2View("Setting icon path '" + item.iconPath.toUtf8() + "' failed."));
219 if (lcl_isItemInArray(pShellLinkItem, m_aRemoved))
221 SAL_INFO("shell.jumplist", "Ignoring item '"
222 << item.name
223 << "' (was removed by user). See output of "
224 "XJumpList::getRemovedItems().");
225 continue;
227 aCollection->AddObject(pShellLinkItem);
229 catch (const ComError& e)
231 SAL_WARN("shell.jumplist", e.what());
232 continue;
236 COMReference<IObjectArray> pObjectArray(aCollection, COM_QUERY_THROW);
237 UINT nItems;
238 ThrowIfFailed(pObjectArray->GetCount(&nItems), "GetCount failed.");
239 if (nItems == 0)
241 throw IllegalArgumentException(
242 "No valid items given. `jumpListItems` is either empty, or contains only items "
243 "which were removed by the user. See `XJumpList::getRemovedItems()`.",
244 getXWeak(), 1);
247 ThrowIfFailed(
248 m_aDestinationList->AppendCategory(o3tl::toW(sCategory.getStr()), pObjectArray),
249 "AppendCategory failed.");
251 catch (const ComError& e)
253 SAL_WARN("shell.jumplist", e.what());
257 void SAL_CALL JumpListImpl::addTasks(const Sequence<JumpListItem>& aJumpListItems)
259 if (!m_isListOpen)
260 throw InvalidStateException("No list open. Open it with 'beginList'");
264 OUString sofficeURL;
265 OUString sofficePath;
266 oslProcessError err = osl_getExecutableFile(&sofficeURL.pData);
267 FileBase::getSystemPathFromFileURL(sofficeURL, sofficePath);
268 if (err != osl_Process_E_None)
270 SAL_WARN("shell.jumplist", "osl_getExecutableFile failed");
271 return;
273 // We need to run soffice.exe, not soffice.bin
274 sofficePath = sofficePath.replaceFirst("soffice.bin", "soffice.exe");
276 COMReference<IObjectCollection> aCollection(CLSID_EnumerableObjectCollection, nullptr,
277 CLSCTX_INPROC_SERVER);
279 for (auto const& item : aJumpListItems)
281 if (item.name.isEmpty())
282 continue;
285 COMReference<IShellLinkW> pShellLinkItem(CLSID_ShellLink, nullptr,
286 CLSCTX_INPROC_SERVER);
289 COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW);
291 PROPVARIANT propvar;
292 ThrowIfFailed(
293 InitPropVariantFromString(o3tl::toW(item.name.getStr()), &propvar),
294 "InitPropVariantFromString failed.");
296 ThrowIfFailed(pps->SetValue(PKEY_Title, propvar), "SetValue failed.");
298 ThrowIfFailed(pps->Commit(), "Commit failed.");
300 PropVariantClear(&propvar);
302 ThrowIfFailed(
303 pShellLinkItem->SetDescription(o3tl::toW(item.description.getStr())),
304 Concat2View("Setting description '" + item.description.toUtf8() + "' failed."));
306 ThrowIfFailed(pShellLinkItem->SetPath(o3tl::toW(sofficePath.getStr())),
307 Concat2View("Setting path '" + sofficePath.toUtf8() + "' failed."));
309 ThrowIfFailed(
310 pShellLinkItem->SetArguments(o3tl::toW(item.arguments.getStr())),
311 Concat2View("Setting arguments '" + item.arguments.toUtf8() + "' failed."));
313 ThrowIfFailed(
314 pShellLinkItem->SetIconLocation(o3tl::toW(item.iconPath.getStr()), 0),
315 Concat2View("Setting icon path '" + item.iconPath.toUtf8() + "' failed."));
317 aCollection->AddObject(pShellLinkItem);
319 catch (const ComError& e)
321 SAL_WARN("shell.jumplist", e.what());
322 continue;
326 COMReference<IObjectArray> pObjectArray(aCollection, COM_QUERY_THROW);
327 UINT nItems;
328 ThrowIfFailed(pObjectArray->GetCount(&nItems), "GetCount failed.");
329 if (nItems == 0)
331 throw IllegalArgumentException("No valid items given. `jumpListItems` is empty.",
332 getXWeak(), 1);
335 ThrowIfFailed(m_aDestinationList->AddUserTasks(pObjectArray), "AddUserTasks failed.");
337 catch (const ComError& e)
339 SAL_WARN("shell.jumplist", e.what());
343 void SAL_CALL JumpListImpl::showRecentFiles()
345 if (!m_isListOpen)
346 throw InvalidStateException("No list open. Open it with 'beginList'");
350 ThrowIfFailed(m_aDestinationList->AppendKnownCategory(KDC_RECENT),
351 "AppendKnownCategory(KDC_RECENT) failed.");
353 catch (const ComError& e)
355 SAL_WARN("shell.jumplist", e.what());
359 void SAL_CALL JumpListImpl::showFrequentFiles()
361 if (!m_isListOpen)
362 throw InvalidStateException("No list open. Open it with 'beginList'");
366 ThrowIfFailed(m_aDestinationList->AppendKnownCategory(KDC_FREQUENT),
367 "AppendKnownCategory(KDC_FREQUENT) failed.");
369 catch (const ComError& e)
371 SAL_WARN("shell.jumplist", e.what());
375 void SAL_CALL JumpListImpl::commitList()
377 if (!m_isListOpen)
378 throw InvalidStateException("No list open. Open it with 'beginList'");
382 ThrowIfFailed(m_aDestinationList->CommitList(), "CommitList failed.");
383 m_isListOpen = false;
385 catch (const ComError& e)
387 SAL_WARN("shell.jumplist", e.what());
391 void SAL_CALL JumpListImpl::abortList()
393 if (!m_isListOpen)
394 throw InvalidStateException("No list open.");
398 ThrowIfFailed(m_aDestinationList->AbortList(), "AbortList failed.");
399 m_isListOpen = false;
401 catch (const ComError& e)
403 SAL_WARN("shell.jumplist", e.what());
407 void SAL_CALL JumpListImpl::deleteList(const OUString& sApplication)
409 if (m_isListOpen)
410 throw InvalidStateException("You are in a list building session. Close it with "
411 "'commitList', or abort with 'abortList'");
413 if (sApplication != "Writer" && sApplication != "Calc" && sApplication != "Impress"
414 && sApplication != "Draw" && sApplication != "Math" && sApplication != "Base"
415 && sApplication != "Startcenter")
417 throw IllegalArgumentException(
418 "Parameter 'application' must be one of 'Writer', 'Calc', 'Impress', 'Draw', "
419 "'Math', 'Base', 'Startcenter'.",
420 getXWeak(), 1);
422 OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplication);
426 COMReference<ICustomDestinationList> aDestinationList(CLSID_DestinationList, nullptr,
427 CLSCTX_INPROC_SERVER);
428 aDestinationList->DeleteList(o3tl::toW(sApplicationID.getStr()));
430 catch (const ComError& e)
432 SAL_WARN("shell.jumplist", e.what());
436 Sequence<JumpListItem> SAL_CALL JumpListImpl::getRemovedItems(const OUString& sApplication)
438 if (sApplication != "Writer" && sApplication != "Calc" && sApplication != "Impress"
439 && sApplication != "Draw" && sApplication != "Math" && sApplication != "Base"
440 && sApplication != "Startcenter")
442 throw IllegalArgumentException(
443 "Parameter 'application' must be one of 'Writer', 'Calc', 'Impress', 'Draw', "
444 "'Math', 'Base', 'Startcenter'.",
445 getXWeak(), 1);
447 OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplication);
449 std::vector<JumpListItem> removedItems;
452 COMReference<ICustomDestinationList> aDestinationList(CLSID_DestinationList, nullptr,
453 CLSCTX_INPROC_SERVER);
455 aDestinationList->SetAppID(o3tl::toW(sApplicationID.getStr()));
457 COMReference<IObjectArray> removed;
458 ThrowIfFailed(aDestinationList->GetRemovedDestinations(IID_PPV_ARGS(&removed)),
459 "GetRemovedDestinations failed");
461 UINT removed_count;
462 if (SUCCEEDED(removed->GetCount(&removed_count) && (removed_count > 0)))
464 JumpListItem item;
465 COMReference<IShellLinkW> pShellLinkItem;
466 for (UINT i = 0; i < removed_count; ++i)
468 if (SUCCEEDED(removed->GetAt(i, IID_PPV_ARGS(&pShellLinkItem))))
470 COMReference<IPropertyStore> propertyStore(pShellLinkItem, COM_QUERY_THROW);
471 PROPVARIANT propvar;
472 ThrowIfFailed(propertyStore->GetValue(PKEY_Title, &propvar),
473 "GetValue failed.");
474 item.name = o3tl::toU(PropVariantToStringWithDefault(propvar, L""));
476 ThrowIfFailed(propertyStore->GetValue(PKEY_Link_Arguments, &propvar),
477 "GetValue failed.");
478 item.arguments = o3tl::toU(PropVariantToStringWithDefault(propvar, L""));
479 PropVariantClear(&propvar);
481 wchar_t itemDesc[EXTENDED_MAX_PATH];
482 ThrowIfFailed(pShellLinkItem->GetDescription(
483 itemDesc, std::extent<decltype(itemDesc)>::value),
484 "GetDescription failed.");
485 item.description = o3tl::toU(itemDesc);
487 wchar_t path[EXTENDED_MAX_PATH];
488 int icon_index;
489 ThrowIfFailed(pShellLinkItem->GetIconLocation(
490 path, std::extent<decltype(path)>::value, &icon_index),
491 "GetIconLocation failed.");
492 item.iconPath = o3tl::toU(path);
494 removedItems.emplace_back(item);
499 catch (const ComError& e)
501 SAL_WARN("shell.jumplist", e.what());
504 return containerToSequence(removedItems);
507 // XServiceInfo
509 OUString SAL_CALL JumpListImpl::getImplementationName()
511 return "com.sun.star.system.windows.JumpListImpl";
514 sal_Bool SAL_CALL JumpListImpl::supportsService(const OUString& ServiceName)
516 return cppu::supportsService(this, ServiceName);
519 Sequence<OUString> SAL_CALL JumpListImpl::getSupportedServiceNames()
521 return { "com.sun.star.system.windows.JumpList" };
524 extern "C" SAL_DLLPUBLIC_EXPORT XInterface*
525 shell_JumpListExec_get_implementation(XComponentContext* context, Sequence<Any> const&)
527 return acquire(new JumpListImpl(context));
530 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */