diff options
author | Samuel Mehrbrodt <samuel.mehrbrodt@allotropia.de> | 2022-04-25 10:53:09 +0200 |
---|---|---|
committer | Samuel Mehrbrodt <samuel.mehrbrodt@allotropia.de> | 2022-04-26 11:46:17 +0200 |
commit | 675788b208a7c775f8eaa51cd90528b1bb92ed79 (patch) | |
tree | 35d508928327bcc2b7d557811d7cc8032732ae65 | |
parent | 90057e372cd07b41c8dbba4e7b7f107568a5f451 (diff) |
Extend UNO API for custom jump lists
* Allow to display the recent/frequent files
* Allow adding items to the "Tasks" category
* Allow adding multiple categories
Follow-up to 7efd22c912262f7bf4e4735dae70db0b31ab3d5b
Change-Id: I860d44c1a0d9bc8200529c908b6103741dc37bb5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133367
Tested-by: Jenkins
Reviewed-by: Samuel Mehrbrodt <samuel.mehrbrodt@allotropia.de>
-rw-r--r-- | offapi/com/sun/star/system/windows/XJumpList.idl | 118 | ||||
-rw-r--r-- | shell/source/win32/jumplist/JumpList.cxx | 224 |
2 files changed, 296 insertions, 46 deletions
diff --git a/offapi/com/sun/star/system/windows/XJumpList.idl b/offapi/com/sun/star/system/windows/XJumpList.idl index 80fef03b60aa..ddf9415243c2 100644 --- a/offapi/com/sun/star/system/windows/XJumpList.idl +++ b/offapi/com/sun/star/system/windows/XJumpList.idl @@ -19,16 +19,48 @@ module com { module sun { module star { module system { module windows { /** Specifies an interface for adding custom jump lists to the task bar (Windows only) + To add a new jump list, call + 1. XJumpList::beginList + 2. XJumpList::appendCategory / XJumpList::addTasks / XJumpList::showRecentFiles / XJumpList::showFrequentFiles + 3. XJumpList::commitList + + Use XJumpList::abortList to cancel a current list building session. + Use XJumpList::getRemovedItems to see which items were removed by the user. + @since LibreOffice 7.4 */ interface XJumpList: com::sun::star::uno::XInterface { - /** Add (or update) a jump list category. + /** + Start a new jump list. + + @param application + Used to map the jump list to the correct application. Use one of the following values: + <ul> + <li>Writer</li> + <li>Calc</li> + <li>Impress</li> + <li>Draw</li> + <li>Math</li> + <li>Base</li> + <li>Startcenter</li> + </ul> - Note that it is only possible to have one jump list category per `application`. + "Startcenter" will map to the generic "LibreOffice" icon. + + @throws com::sun::star::lang::IllegalArgumentException + When `application` is invalid - When there is already a jump list for the given `application`, - that jump list will be cleared, and the new `category` and `jumpListItems` will be added. + @throws com::sun::star::util::InvalidStateException + When there is already an open list. + */ + void beginList([in] string application) + raises( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::util::InvalidStateException ); + + /** Add a jump list category. + + Users can pin or remove items added via this method. + Use XJumpList::getRemovedItems to see which items were removed by the user. @param category Specifies the category name. It will appear as the title of the custom jump list. @@ -44,34 +76,72 @@ interface XJumpList: com::sun::star::uno::XInterface If you try to add items which the user removed before, they will be silently ignored and not added to the list. - @param application - Used to map the jump list to the correct application. Use one of the following values: - <ul> - <li>Writer</li> - <li>Calc</li> - <li>Impress</li> - <li>Draw</li> - <li>Math</li> - <li>Base</li> - <li>Startcenter</li> - </ul> - - "Startcenter" will map to the generic "LibreOffice" icon. - @throws com::sun::star::lang::IllegalArgumentException When one of the following applies: <ul> <li>`category` is empty</li> <li>`jumpListItems` is empty or contains only items which were removed by the user</li> - <li>`application` is invalid</li> </ul> + + @throws com::sun::star::util::InvalidStateException + When there is no open list. */ void appendCategory( [in] string category, - [in] sequence<com::sun::star::system::windows::JumpListItem> jumpListItems, - [in] string application ) - raises( ::com::sun::star::lang::IllegalArgumentException ); + [in] sequence<com::sun::star::system::windows::JumpListItem> jumpListItems ) + raises( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::util::InvalidStateException ); + + /** Add items to the "Tasks" category. This category is system-defined and the category title cannot be changed. + Also the user cannot remove or pin items from this category (as he can with items added via XJumpList::appendCategory ). + + @param jumpListItems + Specifies a list of com::sun::star::system::JumpListItem. + Must contain at least one item. + These will be added as entries below the "Tasks" system category. + + @throws com::sun::star::lang::IllegalArgumentException + When `jumpListItems` is empty + + @throws com::sun::star::util::InvalidStateException + When there is no open list. + */ + void addTasks([in] sequence<com::sun::star::system::windows::JumpListItem> jumpListItems) + raises( ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::util::InvalidStateException ); + + /** Display the recently used files (populated by LibreOffice) + + @throws com::sun::star::util::InvalidStateException + When there is no open list. + */ + void showRecentFiles() + raises (::com::sun::star::util::InvalidStateException); + + /** Display the frequently used files (populated by LibreOffice) + + @throws com::sun::star::util::InvalidStateException + When there is no open list. + */ + void showFrequentFiles() + raises (::com::sun::star::util::InvalidStateException); + + /** + Commits the list. + + @throws com::sun::star::util::InvalidStateException + When there is no open list. + */ + void commitList() + raises( ::com::sun::star::util::InvalidStateException ); + + /** + Aborts a list building session started with beginList. + + @throws com::sun::star::util::InvalidStateException + When there is no open list. + */ + void abortList() + raises( ::com::sun::star::util::InvalidStateException ); - /** Delete a jump list category + /** Deletes the Jump List for a certain application @param application Used to map the jump list to the correct application. Use one of the following values: @@ -90,7 +160,7 @@ interface XJumpList: com::sun::star::uno::XInterface @throws com::sun::star::lang::IllegalArgumentException When `application` is invalid */ - void deleteCategory( [in] string application ) + void deleteList( [in] string application ) raises( ::com::sun::star::lang::IllegalArgumentException ); /** Returns items that were removed from the jump list by the user. diff --git a/shell/source/win32/jumplist/JumpList.cxx b/shell/source/win32/jumplist/JumpList.cxx index f5d8ca8532cd..06a62a11038d 100644 --- a/shell/source/win32/jumplist/JumpList.cxx +++ b/shell/source/win32/jumplist/JumpList.cxx @@ -30,6 +30,7 @@ #include <com/sun/star/system/windows/JumpListItem.hpp> #include <com/sun/star/system/windows/XJumpList.hpp> #include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/InvalidStateException.hpp> #include <prewin.h> #include <Shlobj.h> @@ -43,6 +44,7 @@ using namespace css; using namespace css::uno; using namespace css::lang; using namespace css::system::windows; +using namespace css::util; using namespace osl; using namespace sal::systools; @@ -51,15 +53,23 @@ namespace class JumpListImpl : public BaseMutex, public WeakComponentImplHelper<XJumpList, XServiceInfo> { Reference<XComponentContext> m_xContext; + COMReference<ICustomDestinationList> m_aDestinationList; + COMReference<IObjectArray> m_aRemoved; + bool m_isListOpen; public: explicit JumpListImpl(const Reference<XComponentContext>& xContext); // XJumpList + virtual void SAL_CALL beginList(const OUString& sApplication) override; virtual void SAL_CALL appendCategory(const OUString& sCategory, - const Sequence<JumpListItem>& aJumpListItems, - const OUString& sApplication) override; - virtual void SAL_CALL deleteCategory(const OUString& sApplication) override; + const Sequence<JumpListItem>& aJumpListItems) override; + virtual void SAL_CALL addTasks(const Sequence<JumpListItem>& aJumpListItems) override; + virtual void SAL_CALL showRecentFiles() override; + virtual void SAL_CALL showFrequentFiles() override; + virtual void SAL_CALL commitList() override; + virtual void SAL_CALL abortList() override; + virtual void SAL_CALL deleteList(const OUString& sApplication) override; virtual Sequence<JumpListItem> SAL_CALL getRemovedItems(const OUString& sApplication) override; // XServiceInfo @@ -71,6 +81,8 @@ public: JumpListImpl::JumpListImpl(const Reference<XComponentContext>& xContext) : WeakComponentImplHelper(m_aMutex) , m_xContext(xContext) + , m_aDestinationList(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER) + , m_isListOpen(false) { } @@ -105,15 +117,11 @@ bool lcl_isItemInArray(COMReference<IShellLinkW> pShellLinkItem, } } -void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory, - const Sequence<JumpListItem>& aJumpListItems, - const OUString& sApplication) +void SAL_CALL JumpListImpl::beginList(const OUString& sApplication) { - if (sCategory.isEmpty()) - { - throw IllegalArgumentException("Parameter 'category' must not be empty", - static_cast<OWeakObject*>(this), 1); - } + if (m_isListOpen) + throw InvalidStateException("There is already a list open. Close it with 'commitList'"); + if (sApplication != "Writer" && sApplication != "Calc" && sApplication != "Impress" && sApplication != "Draw" && sApplication != "Math" && sApplication != "Base" && sApplication != "Startcenter") @@ -127,16 +135,34 @@ void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory, try { - COMReference<ICustomDestinationList> aDestinationList(CLSID_DestinationList, nullptr, - CLSCTX_INPROC_SERVER); - - aDestinationList->SetAppID(o3tl::toW(sApplicationID.getStr())); + m_aDestinationList->SetAppID(o3tl::toW(sApplicationID.getStr())); UINT min_slots; - COMReference<IObjectArray> removed; - ThrowIfFailed(aDestinationList->BeginList(&min_slots, IID_PPV_ARGS(&removed)), + + ThrowIfFailed(m_aDestinationList->BeginList(&min_slots, IID_PPV_ARGS(&m_aRemoved)), "BeginList failed"); + m_isListOpen = true; + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} +void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory, + const Sequence<JumpListItem>& aJumpListItems) +{ + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + if (sCategory.isEmpty()) + { + throw IllegalArgumentException("Parameter 'category' must not be empty", + static_cast<OWeakObject*>(this), 1); + } + + try + { OUString sofficeURL; OUString sofficePath; oslProcessError err = osl_getExecutableFile(&sofficeURL.pData); @@ -191,7 +217,7 @@ void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory, OStringConcatenation("Setting icon path '" + item.iconPath.toUtf8() + "' failed.")); - if (lcl_isItemInArray(pShellLinkItem, removed)) + if (lcl_isItemInArray(pShellLinkItem, m_aRemoved)) { SAL_INFO("shell.jumplist", "Ignoring item '" << item.name @@ -219,10 +245,96 @@ void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory, static_cast<OWeakObject*>(this), 1); } - ThrowIfFailed(aDestinationList->AppendCategory(o3tl::toW(sCategory.getStr()), pObjectArray), - "AppendCategory failed."); + ThrowIfFailed( + m_aDestinationList->AppendCategory(o3tl::toW(sCategory.getStr()), pObjectArray), + "AppendCategory failed."); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::addTasks(const Sequence<JumpListItem>& aJumpListItems) +{ + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + try + { + OUString sofficeURL; + OUString sofficePath; + oslProcessError err = osl_getExecutableFile(&sofficeURL.pData); + FileBase::getSystemPathFromFileURL(sofficeURL, sofficePath); + if (err != osl_Process_E_None) + { + SAL_WARN("shell.jumplist", "osl_getExecutableFile failed"); + return; + } + // We need to run soffice.exe, not soffice.bin + sofficePath = sofficePath.replaceFirst("soffice.bin", "soffice.exe"); + + COMReference<IObjectCollection> aCollection(CLSID_EnumerableObjectCollection, nullptr, + CLSCTX_INPROC_SERVER); - ThrowIfFailed(aDestinationList->CommitList(), "CommitList failed."); + for (auto const& item : aJumpListItems) + { + if (item.name.isEmpty()) + continue; + try + { + COMReference<IShellLinkW> pShellLinkItem(CLSID_ShellLink, nullptr, + CLSCTX_INPROC_SERVER); + + { + COMReference<IPropertyStore> pps(pShellLinkItem, COM_QUERY_THROW); + + PROPVARIANT propvar; + ThrowIfFailed( + InitPropVariantFromString(o3tl::toW(item.name.getStr()), &propvar), + "InitPropVariantFromString failed."); + + ThrowIfFailed(pps->SetValue(PKEY_Title, propvar), "SetValue failed."); + + ThrowIfFailed(pps->Commit(), "Commit failed."); + + PropVariantClear(&propvar); + } + ThrowIfFailed(pShellLinkItem->SetDescription(o3tl::toW(item.description.getStr())), + OStringConcatenation("Setting description '" + + item.description.toUtf8() + "' failed.")); + + ThrowIfFailed( + pShellLinkItem->SetPath(o3tl::toW(sofficePath.getStr())), + OStringConcatenation("Setting path '" + sofficePath.toUtf8() + "' failed.")); + + ThrowIfFailed(pShellLinkItem->SetArguments(o3tl::toW(item.arguments.getStr())), + OStringConcatenation("Setting arguments '" + item.arguments.toUtf8() + + "' failed.")); + + ThrowIfFailed(pShellLinkItem->SetIconLocation(o3tl::toW(item.iconPath.getStr()), 0), + OStringConcatenation("Setting icon path '" + item.iconPath.toUtf8() + + "' failed.")); + + aCollection->AddObject(pShellLinkItem); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + continue; + } + } + + COMReference<IObjectArray> pObjectArray(aCollection, COM_QUERY_THROW); + UINT nItems; + ThrowIfFailed(pObjectArray->GetCount(&nItems), "GetCount failed."); + if (nItems == 0) + { + throw IllegalArgumentException("No valid items given. `jumpListItems` is empty.", + static_cast<OWeakObject*>(this), 1); + } + + ThrowIfFailed(m_aDestinationList->AddUserTasks(pObjectArray), "AddUserTasks failed."); } catch (const ComError& e) { @@ -230,8 +342,76 @@ void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory, } } -void SAL_CALL JumpListImpl::deleteCategory(const OUString& sApplication) +void SAL_CALL JumpListImpl::showRecentFiles() { + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + try + { + ThrowIfFailed(m_aDestinationList->AppendKnownCategory(KDC_RECENT), + "AppendKnownCategory(KDC_RECENT) failed."); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::showFrequentFiles() +{ + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + try + { + ThrowIfFailed(m_aDestinationList->AppendKnownCategory(KDC_FREQUENT), + "AppendKnownCategory(KDC_FREQUENT) failed."); + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::commitList() +{ + if (!m_isListOpen) + throw InvalidStateException("No list open. Open it with 'beginList'"); + + try + { + ThrowIfFailed(m_aDestinationList->CommitList(), "CommitList failed."); + m_isListOpen = false; + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::abortList() +{ + if (!m_isListOpen) + throw InvalidStateException("No list open."); + + try + { + ThrowIfFailed(m_aDestinationList->AbortList(), "AbortList failed."); + m_isListOpen = false; + } + catch (const ComError& e) + { + SAL_WARN("shell.jumplist", e.what()); + } +} + +void SAL_CALL JumpListImpl::deleteList(const OUString& sApplication) +{ + if (m_isListOpen) + throw InvalidStateException("You are in a list building session. Close it with " + "'commitList', or abort with 'abortList'"); + if (sApplication != "Writer" && sApplication != "Calc" && sApplication != "Impress" && sApplication != "Draw" && sApplication != "Math" && sApplication != "Base" && sApplication != "Startcenter") |