summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Mehrbrodt <samuel.mehrbrodt@allotropia.de>2022-04-25 10:53:09 +0200
committerSamuel Mehrbrodt <samuel.mehrbrodt@allotropia.de>2022-04-26 11:46:17 +0200
commit675788b208a7c775f8eaa51cd90528b1bb92ed79 (patch)
tree35d508928327bcc2b7d557811d7cc8032732ae65
parent90057e372cd07b41c8dbba4e7b7f107568a5f451 (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.idl118
-rw-r--r--shell/source/win32/jumplist/JumpList.cxx224
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")