calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / shell / source / win32 / jumplist / JumpList.cxx
blob114c6f4e95b501f226dd89a7539ebe9607ae960b
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>
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>
35 #include <prewin.h>
36 #include <Shlobj.h>
37 #include <propkey.h>
38 #include <propvarutil.h>
39 #include <postwin.h>
41 using namespace comphelper;
42 using namespace cppu;
43 using namespace css;
44 using namespace css::uno;
45 using namespace css::lang;
46 using namespace css::system::windows;
47 using namespace css::util;
48 using namespace osl;
49 using namespace sal::systools;
51 namespace
53 class JumpListImpl : public BaseMutex, public WeakComponentImplHelper<XJumpList, XServiceInfo>
55 Reference<XComponentContext> m_xContext;
56 COMReference<ICustomDestinationList> m_aDestinationList;
57 COMReference<IObjectArray> m_aRemoved;
58 bool m_isListOpen;
60 public:
61 explicit JumpListImpl(const Reference<XComponentContext>& xContext);
63 // XJumpList
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;
75 // XServiceInfo
76 virtual OUString SAL_CALL getImplementationName() override;
77 virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
78 virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
81 JumpListImpl::JumpListImpl(const Reference<XComponentContext>& xContext)
82 : WeakComponentImplHelper(m_aMutex)
83 , m_xContext(xContext)
84 , m_aDestinationList(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER)
85 , m_isListOpen(false)
89 // Determines if the provided IShellLinkItem is listed in the array of items that the user has removed
90 bool lcl_isItemInArray(COMReference<IShellLinkW> pShellLinkItem,
91 COMReference<IObjectArray> poaRemoved)
93 UINT nItems;
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))))
100 continue;
102 PROPVARIANT propvar;
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)
113 return true;
116 return false;
120 void SAL_CALL JumpListImpl::beginList(const OUString& sApplication)
122 if (m_isListOpen)
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'.",
132 static_cast<OWeakObject*>(this), 1);
134 OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplication);
138 m_aDestinationList->SetAppID(o3tl::toW(sApplicationID.getStr()));
140 UINT min_slots;
142 ThrowIfFailed(m_aDestinationList->BeginList(&min_slots, IID_PPV_ARGS(&m_aRemoved)),
143 "BeginList failed");
144 m_isListOpen = true;
146 catch (const ComError& e)
148 SAL_WARN("shell.jumplist", e.what());
152 void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory,
153 const Sequence<JumpListItem>& aJumpListItems)
155 if (!m_isListOpen)
156 throw InvalidStateException("No list open. Open it with 'beginList'");
158 if (sCategory.isEmpty())
160 throw IllegalArgumentException("Parameter 'category' must not be empty",
161 static_cast<OWeakObject*>(this), 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 static_cast<OWeakObject*>(this), 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 static_cast<OWeakObject*>(this), 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 static_cast<OWeakObject*>(this), 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 static_cast<OWeakObject*>(this), 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[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[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: */