22#include <osl/file.hxx>
23#include <osl/mutex.hxx>
24#include <osl/process.h>
26#include <systools/win32/comtools.hxx>
28#include <com/sun/star/lang/IllegalArgumentException.hpp>
29#include <com/sun/star/lang/XServiceInfo.hpp>
30#include <com/sun/star/system/windows/JumpListItem.hpp>
31#include <com/sun/star/system/windows/XJumpList.hpp>
32#include <com/sun/star/uno/XComponentContext.hpp>
33#include <com/sun/star/util/InvalidStateException.hpp>
38#include <propvarutil.h>
44using namespace css::uno;
45using namespace css::lang;
46using namespace css::system::windows;
47using namespace css::util;
56 COMReference<ICustomDestinationList> m_aDestinationList;
57 COMReference<IObjectArray> m_aRemoved;
61 explicit JumpListImpl(
const Reference<XComponentContext>& xContext);
64 virtual void SAL_CALL beginList(
const OUString& sApplication)
override;
65 virtual void SAL_CALL appendCategory(
const OUString& sCategory,
66 const Sequence<JumpListItem>& aJumpListItems)
override;
67 virtual void SAL_CALL addTasks(
const Sequence<JumpListItem>& aJumpListItems)
override;
68 virtual void SAL_CALL showRecentFiles()
override;
69 virtual void SAL_CALL showFrequentFiles()
override;
70 virtual void SAL_CALL commitList()
override;
71 virtual void SAL_CALL abortList()
override;
72 virtual void SAL_CALL deleteList(
const OUString& sApplication)
override;
73 virtual Sequence<JumpListItem> SAL_CALL getRemovedItems(
const OUString& sApplication)
override;
81JumpListImpl::JumpListImpl(
const Reference<XComponentContext>& xContext)
84 , m_aDestinationList(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER)
90bool lcl_isItemInArray(COMReference<IShellLinkW> pShellLinkItem,
91 COMReference<IObjectArray> poaRemoved)
94 ThrowIfFailed(poaRemoved->GetCount(&nItems),
"GetCount failed.");
96 COMReference<IShellLinkW> pShellLinkItemCompare;
97 for (UINT i = 0;
i < nItems;
i++)
99 if (!SUCCEEDED(poaRemoved->GetAt(i, IID_PPV_ARGS(&pShellLinkItemCompare))))
103 COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW);
104 ThrowIfFailed(pps->GetValue(PKEY_Title, &propvar),
"GetValue failed.");
105 OUString title(o3tl::toU(PropVariantToStringWithDefault(propvar, L
"")));
107 COMReference<IPropertyStore> ppsCompare(pShellLinkItemCompare, COM_QUERY_THROW);
108 ThrowIfFailed(ppsCompare->GetValue(PKEY_Title, &propvar),
"GetValue failed.");
109 OUString titleCompare(o3tl::toU(PropVariantToStringWithDefault(propvar, L
"")));
110 PropVariantClear(&propvar);
112 if (title == titleCompare)
120void SAL_CALL JumpListImpl::beginList(
const OUString& sApplication)
123 throw InvalidStateException(
"There is already a list open. Close it with 'commitList'");
125 if (sApplication !=
"Writer" && sApplication !=
"Calc" && sApplication !=
"Impress"
126 && sApplication !=
"Draw" && sApplication !=
"Math" && sApplication !=
"Base"
127 && sApplication !=
"Startcenter")
129 throw IllegalArgumentException(
130 "Parameter 'application' must be one of 'Writer', 'Calc', 'Impress', 'Draw', "
131 "'Math', 'Base', 'Startcenter'.",
134 OUString sApplicationID(
"TheDocumentFoundation.LibreOffice." + sApplication);
138 m_aDestinationList->SetAppID(o3tl::toW(sApplicationID.getStr()));
142 ThrowIfFailed(m_aDestinationList->BeginList(&min_slots, IID_PPV_ARGS(&m_aRemoved)),
146 catch (
const ComError& e)
148 SAL_WARN(
"shell.jumplist", e.what());
152void SAL_CALL JumpListImpl::appendCategory(
const OUString& sCategory,
153 const Sequence<JumpListItem>& aJumpListItems)
156 throw InvalidStateException(
"No list open. Open it with 'beginList'");
158 if (sCategory.isEmpty())
160 throw IllegalArgumentException(
"Parameter 'category' must not be empty", getXWeak(), 1);
166 OUString sofficePath;
167 oslProcessError
err = osl_getExecutableFile(&sofficeURL.pData);
168 FileBase::getSystemPathFromFileURL(sofficeURL, sofficePath);
169 if (err != osl_Process_E_None)
171 SAL_WARN(
"shell.jumplist",
"osl_getExecutableFile failed");
175 sofficePath = sofficePath.replaceFirst(
"soffice.bin",
"soffice.exe");
177 COMReference<IObjectCollection> aCollection(CLSID_EnumerableObjectCollection,
nullptr,
178 CLSCTX_INPROC_SERVER);
180 for (
auto const& item : aJumpListItems)
182 if (item.name.isEmpty())
186 COMReference<IShellLinkW> pShellLinkItem(CLSID_ShellLink,
nullptr,
187 CLSCTX_INPROC_SERVER);
190 COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW);
194 InitPropVariantFromString(o3tl::toW(item.name.getStr()), &propvar),
195 "InitPropVariantFromString failed.");
197 ThrowIfFailed(pps->SetValue(PKEY_Title, propvar),
"SetValue failed.");
199 ThrowIfFailed(pps->Commit(),
"Commit failed.");
201 PropVariantClear(&propvar);
204 pShellLinkItem->SetDescription(o3tl::toW(item.description.getStr())),
205 Concat2View(
"Setting description '" + item.description.toUtf8() +
"' failed."));
207 ThrowIfFailed(pShellLinkItem->SetPath(o3tl::toW(sofficePath.getStr())),
208 Concat2View(
"Setting path '" + sofficePath.toUtf8() +
"' failed."));
211 pShellLinkItem->SetArguments(o3tl::toW(item.arguments.getStr())),
212 Concat2View(
"Setting arguments '" + item.arguments.toUtf8() +
"' failed."));
215 pShellLinkItem->SetIconLocation(o3tl::toW(item.iconPath.getStr()), 0),
216 Concat2View(
"Setting icon path '" + item.iconPath.toUtf8() +
"' failed."));
218 if (lcl_isItemInArray(pShellLinkItem, m_aRemoved))
220 SAL_INFO(
"shell.jumplist",
"Ignoring item '"
222 <<
"' (was removed by user). See output of "
223 "XJumpList::getRemovedItems().");
226 aCollection->AddObject(pShellLinkItem);
228 catch (
const ComError& e)
230 SAL_WARN(
"shell.jumplist", e.what());
235 COMReference<IObjectArray> pObjectArray(aCollection, COM_QUERY_THROW);
237 ThrowIfFailed(pObjectArray->GetCount(&nItems),
"GetCount failed.");
240 throw IllegalArgumentException(
241 "No valid items given. `jumpListItems` is either empty, or contains only items "
242 "which were removed by the user. See `XJumpList::getRemovedItems()`.",
247 m_aDestinationList->AppendCategory(o3tl::toW(sCategory.getStr()), pObjectArray),
248 "AppendCategory failed.");
250 catch (
const ComError& e)
252 SAL_WARN(
"shell.jumplist", e.what());
256void SAL_CALL JumpListImpl::addTasks(
const Sequence<JumpListItem>& aJumpListItems)
259 throw InvalidStateException(
"No list open. Open it with 'beginList'");
264 OUString sofficePath;
265 oslProcessError
err = osl_getExecutableFile(&sofficeURL.pData);
266 FileBase::getSystemPathFromFileURL(sofficeURL, sofficePath);
267 if (err != osl_Process_E_None)
269 SAL_WARN(
"shell.jumplist",
"osl_getExecutableFile failed");
273 sofficePath = sofficePath.replaceFirst(
"soffice.bin",
"soffice.exe");
275 COMReference<IObjectCollection> aCollection(CLSID_EnumerableObjectCollection,
nullptr,
276 CLSCTX_INPROC_SERVER);
278 for (
auto const& item : aJumpListItems)
280 if (item.name.isEmpty())
284 COMReference<IShellLinkW> pShellLinkItem(CLSID_ShellLink,
nullptr,
285 CLSCTX_INPROC_SERVER);
288 COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW);
292 InitPropVariantFromString(o3tl::toW(item.name.getStr()), &propvar),
293 "InitPropVariantFromString failed.");
295 ThrowIfFailed(pps->SetValue(PKEY_Title, propvar),
"SetValue failed.");
297 ThrowIfFailed(pps->Commit(),
"Commit failed.");
299 PropVariantClear(&propvar);
302 pShellLinkItem->SetDescription(o3tl::toW(item.description.getStr())),
303 Concat2View(
"Setting description '" + item.description.toUtf8() +
"' failed."));
305 ThrowIfFailed(pShellLinkItem->SetPath(o3tl::toW(sofficePath.getStr())),
306 Concat2View(
"Setting path '" + sofficePath.toUtf8() +
"' failed."));
309 pShellLinkItem->SetArguments(o3tl::toW(item.arguments.getStr())),
310 Concat2View(
"Setting arguments '" + item.arguments.toUtf8() +
"' failed."));
313 pShellLinkItem->SetIconLocation(o3tl::toW(item.iconPath.getStr()), 0),
314 Concat2View(
"Setting icon path '" + item.iconPath.toUtf8() +
"' failed."));
316 aCollection->AddObject(pShellLinkItem);
318 catch (
const ComError& e)
320 SAL_WARN(
"shell.jumplist", e.what());
325 COMReference<IObjectArray> pObjectArray(aCollection, COM_QUERY_THROW);
327 ThrowIfFailed(pObjectArray->GetCount(&nItems),
"GetCount failed.");
330 throw IllegalArgumentException(
"No valid items given. `jumpListItems` is empty.",
334 ThrowIfFailed(m_aDestinationList->AddUserTasks(pObjectArray),
"AddUserTasks failed.");
336 catch (
const ComError& e)
338 SAL_WARN(
"shell.jumplist", e.what());
342void SAL_CALL JumpListImpl::showRecentFiles()
345 throw InvalidStateException(
"No list open. Open it with 'beginList'");
349 ThrowIfFailed(m_aDestinationList->AppendKnownCategory(KDC_RECENT),
350 "AppendKnownCategory(KDC_RECENT) failed.");
352 catch (
const ComError& e)
354 SAL_WARN(
"shell.jumplist", e.what());
358void SAL_CALL JumpListImpl::showFrequentFiles()
361 throw InvalidStateException(
"No list open. Open it with 'beginList'");
365 ThrowIfFailed(m_aDestinationList->AppendKnownCategory(KDC_FREQUENT),
366 "AppendKnownCategory(KDC_FREQUENT) failed.");
368 catch (
const ComError& e)
370 SAL_WARN(
"shell.jumplist", e.what());
374void SAL_CALL JumpListImpl::commitList()
377 throw InvalidStateException(
"No list open. Open it with 'beginList'");
381 ThrowIfFailed(m_aDestinationList->CommitList(),
"CommitList failed.");
382 m_isListOpen =
false;
384 catch (
const ComError& e)
386 SAL_WARN(
"shell.jumplist", e.what());
390void SAL_CALL JumpListImpl::abortList()
393 throw InvalidStateException(
"No list open.");
397 ThrowIfFailed(m_aDestinationList->AbortList(),
"AbortList failed.");
398 m_isListOpen =
false;
400 catch (
const ComError& e)
402 SAL_WARN(
"shell.jumplist", e.what());
406void SAL_CALL JumpListImpl::deleteList(
const OUString& sApplication)
409 throw InvalidStateException(
"You are in a list building session. Close it with "
410 "'commitList', or abort with 'abortList'");
412 if (sApplication !=
"Writer" && sApplication !=
"Calc" && sApplication !=
"Impress"
413 && sApplication !=
"Draw" && sApplication !=
"Math" && sApplication !=
"Base"
414 && sApplication !=
"Startcenter")
416 throw IllegalArgumentException(
417 "Parameter 'application' must be one of 'Writer', 'Calc', 'Impress', 'Draw', "
418 "'Math', 'Base', 'Startcenter'.",
421 OUString sApplicationID(
"TheDocumentFoundation.LibreOffice." + sApplication);
425 COMReference<ICustomDestinationList> aDestinationList(CLSID_DestinationList,
nullptr,
426 CLSCTX_INPROC_SERVER);
427 aDestinationList->DeleteList(o3tl::toW(sApplicationID.getStr()));
429 catch (
const ComError& e)
431 SAL_WARN(
"shell.jumplist", e.what());
435Sequence<JumpListItem> SAL_CALL JumpListImpl::getRemovedItems(
const OUString& sApplication)
437 if (sApplication !=
"Writer" && sApplication !=
"Calc" && sApplication !=
"Impress"
438 && sApplication !=
"Draw" && sApplication !=
"Math" && sApplication !=
"Base"
439 && sApplication !=
"Startcenter")
441 throw IllegalArgumentException(
442 "Parameter 'application' must be one of 'Writer', 'Calc', 'Impress', 'Draw', "
443 "'Math', 'Base', 'Startcenter'.",
446 OUString sApplicationID(
"TheDocumentFoundation.LibreOffice." + sApplication);
448 std::vector<JumpListItem> removedItems;
451 COMReference<ICustomDestinationList> aDestinationList(CLSID_DestinationList,
nullptr,
452 CLSCTX_INPROC_SERVER);
454 aDestinationList->SetAppID(o3tl::toW(sApplicationID.getStr()));
456 COMReference<IObjectArray>
removed;
457 ThrowIfFailed(aDestinationList->GetRemovedDestinations(IID_PPV_ARGS(&removed)),
458 "GetRemovedDestinations failed");
461 if (SUCCEEDED(
removed->GetCount(&removed_count) && (removed_count > 0)))
464 COMReference<IShellLinkW> pShellLinkItem;
465 for (UINT i = 0;
i < removed_count; ++
i)
467 if (SUCCEEDED(
removed->GetAt(i, IID_PPV_ARGS(&pShellLinkItem))))
469 COMReference<IPropertyStore> propertyStore(pShellLinkItem, COM_QUERY_THROW);
471 ThrowIfFailed(propertyStore->GetValue(PKEY_Title, &propvar),
473 item.name = o3tl::toU(PropVariantToStringWithDefault(propvar, L
""));
475 ThrowIfFailed(propertyStore->GetValue(PKEY_Link_Arguments, &propvar),
477 item.arguments = o3tl::toU(PropVariantToStringWithDefault(propvar, L
""));
478 PropVariantClear(&propvar);
481 ThrowIfFailed(pShellLinkItem->GetDescription(
482 itemDesc, std::extent<
decltype(itemDesc)>::value),
483 "GetDescription failed.");
484 item.description = o3tl::toU(itemDesc);
488 ThrowIfFailed(pShellLinkItem->GetIconLocation(
489 path, std::extent<
decltype(path)>::value, &icon_index),
490 "GetIconLocation failed.");
491 item.iconPath = o3tl::toU(path);
493 removedItems.emplace_back(item);
498 catch (
const ComError& e)
500 SAL_WARN(
"shell.jumplist", e.what());
508OUString SAL_CALL JumpListImpl::getImplementationName()
510 return "com.sun.star.system.windows.JumpListImpl";
513sal_Bool SAL_CALL JumpListImpl::supportsService(
const OUString& ServiceName)
518Sequence<OUString> SAL_CALL JumpListImpl::getSupportedServiceNames()
520 return {
"com.sun.star.system.windows.JumpList" };
526 return acquire(
new JumpListImpl(context));
SAL_DLLPUBLIC_EXPORT XInterface * shell_JumpListExec_get_implementation(XComponentContext *context, Sequence< Any > const &)
Reference< XComponentContext > m_xContext
void SAL_CALL removed(::sal_Int32 ID) override
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)