summaryrefslogtreecommitdiffstats
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 10:16:58 +0200
commitfa9baaece0912561121cd9dd2a0536b3090fcb49 (patch)
treef2f6167128ede1f0a7ae84e8e5dde5a09dc33f1d
parentsw: layout: fix crash when deleting page with section being formatted (diff)
downloadcore-fa9baaece0912561121cd9dd2a0536b3090fcb49.tar.gz
core-fa9baaece0912561121cd9dd2a0536b3090fcb49.zip
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
-rwxr-xr-xoffapi/com/sun/star/system/windows/XJumpList.idl118
-rwxr-xr-xshell/source/win32/jumplist/JumpList.cxx225
2 files changed, 297 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 100755
--- 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 cbc72299b41a..3372ec9fc3bb 100755
--- 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 sal::systools::COMReference;
using sal::systools::COM_QUERY_THROW;
@@ -52,16 +54,24 @@ using sal::systools::ThrowIfFailed;
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);
~JumpListImpl();
// 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
@@ -73,7 +83,11 @@ public:
JumpListImpl::JumpListImpl(const Reference<XComponentContext>& xContext)
: WeakComponentImplHelper(m_aMutex)
, m_xContext(xContext)
+ , m_aDestinationList()
+ , m_isListOpen(false)
{
+ CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&m_aDestinationList));
}
JumpListImpl::~JumpListImpl() {}
@@ -111,15 +125,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")
@@ -133,17 +143,34 @@ void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory,
try
{
- COMReference<ICustomDestinationList> aDestinationList;
- CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER,
- IID_PPV_ARGS(&aDestinationList));
-
- 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);
@@ -199,7 +226,7 @@ void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory,
pShellLinkItem->SetIconLocation(o3tl::toW(item.iconPath.getStr()), 0),
OString("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
@@ -228,10 +255,96 @@ void SAL_CALL JumpListImpl::appendCategory(const OUString& sCategory,
}
ThrowIfFailed(
- aDestinationList->AppendCategory(o3tl::toW(sCategory.getStr()), pObjectArray.get()),
+ m_aDestinationList->AppendCategory(o3tl::toW(sCategory.getStr()), pObjectArray.get()),
"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;
+ CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&aCollection));
- ThrowIfFailed(aDestinationList->CommitList(), "CommitList failed.");
+ for (auto const& item : aJumpListItems)
+ {
+ if (item.name.isEmpty())
+ continue;
+ try
+ {
+ COMReference<IShellLinkW> pShellLinkItem;
+ CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&pShellLinkItem));
+
+ {
+ 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())),
+ OString("Setting description '" + item.description.toUtf8() + "' failed."));
+
+ ThrowIfFailed(pShellLinkItem->SetPath(o3tl::toW(sofficePath.getStr())),
+ OString("Setting path '" + sofficePath.toUtf8() + "' failed."));
+
+ ThrowIfFailed(
+ pShellLinkItem->SetArguments(o3tl::toW(item.arguments.getStr())),
+ OString("Setting arguments '" + item.arguments.toUtf8() + "' failed."));
+
+ ThrowIfFailed(
+ pShellLinkItem->SetIconLocation(o3tl::toW(item.iconPath.getStr()), 0),
+ OString("Setting icon path '" + item.iconPath.toUtf8() + "' failed."));
+
+ aCollection->AddObject(pShellLinkItem.get());
+ }
+ 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.get()), "AddUserTasks failed.");
}
catch (const ComError& e)
{
@@ -239,8 +352,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")