Merge pull request #938 from ONLYOFFICE/feature/jumplist_recent_63817

Add feature 63817
This commit is contained in:
Oleg Kozhukharenko
2023-08-30 17:23:58 +03:00
committed by GitHub
9 changed files with 256 additions and 139 deletions

View File

@ -47,6 +47,7 @@ build_xp {
DEFINES += __OS_WIN_XP
} else {
HEADERS += $$PWD/src/jumplist.h
SOURCES += $$PWD/src/jumplist.cpp
LIBS += -lshlwapi \
-lole32
}

View File

@ -0,0 +1,194 @@
/*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#define NTDDI_VERSION NTDDI_WIN7
#define WIN32_LEAN_AND_MEAN
#define STRICT_TYPED_ITEMIDS
#define ICON_OFFSET 14
#include "jumplist.h"
#include <QtGlobal>
#include <Windows.h>
#include <psapi.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <objectarray.h>
#include <shobjidl.h>
#include <propkey.h>
#include <propvarutil.h>
#include <shlobj.h>
HRESULT _CreateShellLink(PCWSTR pszArguments, PCWSTR pszTitle, IShellLink **ppsl, int index)
{
IShellLink *psl;
HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&psl));
if (SUCCEEDED(hr))
{
WCHAR szAppPath[MAX_PATH];
if (GetModuleFileName(NULL, szAppPath, ARRAYSIZE(szAppPath)))
{
hr = psl->SetPath(szAppPath);
if (SUCCEEDED(hr))
{
hr = psl->SetArguments(pszArguments);
if (SUCCEEDED(hr))
{
hr = psl->SetIconLocation(szAppPath, index + ICON_OFFSET);
Q_UNUSED(hr);
IPropertyStore *pps;
hr = psl->QueryInterface(IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr))
{
PROPVARIANT propvar;
hr = InitPropVariantFromString(pszTitle, &propvar);
if (SUCCEEDED(hr))
{
hr = pps->SetValue(PKEY_Title, propvar);
if (SUCCEEDED(hr))
{
hr = pps->Commit();
if (SUCCEEDED(hr))
{
hr = psl->QueryInterface(IID_PPV_ARGS(ppsl));
}
}
PropVariantClear(&propvar);
}
pps->Release();
}
}
}
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
psl->Release();
}
return hr;
}
HRESULT _AddTasksToList(ICustomDestinationList *pcdl, QStringList list)
{
IObjectCollection *poc;
HRESULT hr = CoCreateInstance(CLSID_EnumerableObjectCollection, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&poc));
if (SUCCEEDED(hr))
{
PCWSTR args[] = {
L"--new:word",
L"--new:cell",
L"--new:slide",
L"--new:form"
};
IShellLink * psl;
for (int i = 0; i < MAX_TASK_NUM && i < list.size(); i++) {
hr = _CreateShellLink(args[i], list[i].replace('_', ' ').toStdWString().c_str(), &psl, i);
if (SUCCEEDED(hr))
{
hr = poc->AddObject(psl);
psl->Release();
}
}
if (SUCCEEDED(hr))
{
IObjectArray * poa;
hr = poc->QueryInterface(IID_PPV_ARGS(&poa));
if (SUCCEEDED(hr))
{
hr = pcdl->AddUserTasks(poa);
poa->Release();
}
}
poc->Release();
}
return hr;
}
void CreateJumpList(const QStringList &list)
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
ICustomDestinationList *pcdl;
hr = CoCreateInstance(CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pcdl));
if (SUCCEEDED(hr)) {
UINT cMinSlots;
IObjectArray *poaRemoved;
hr = pcdl->BeginList(&cMinSlots, IID_PPV_ARGS(&poaRemoved));
if (SUCCEEDED(hr)) {
hr = _AddTasksToList(pcdl, list);
if (SUCCEEDED(hr))
hr = pcdl->CommitList();
}
poaRemoved->Release();
pcdl->Release();
}
CoUninitialize();
}
Q_UNUSED(hr);
}
void DeleteJumpList()
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
ICustomDestinationList *pcdl;
hr = CoCreateInstance(CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pcdl));
if (SUCCEEDED(hr)) {
hr = pcdl->DeleteList(NULL);
pcdl->Release();
}
CoUninitialize();
}
Q_UNUSED(hr);
}
void ClearHistory()
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
IApplicationDestinations *pad;
hr = CoCreateInstance(CLSID_ApplicationDestinations, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pad));
if (SUCCEEDED(hr)) {
hr = pad->RemoveAllDestinations();
pad->Release();
}
CoUninitialize();
}
Q_UNUSED(hr);
}

View File

@ -1,146 +1,45 @@
/*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#ifndef JUMPLIST_H
#define JUMPLIST_H
#define NTDDI_VERSION NTDDI_WIN7
#define WIN32_LEAN_AND_MEAN
#define STRICT_TYPED_ITEMIDS
#define ICON_OFFSET 14
#include <QStringList>
#define MIN_TASK_NUM 3
#define MAX_TASK_NUM 4
#include <QtGlobal>
#include <QStringList>
#include <windows.h>
#include <psapi.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <objectarray.h>
#include <shobjidl.h>
#include <propkey.h>
#include <propvarutil.h>
#include <shlobj.h>
HRESULT _CreateShellLink(PCWSTR pszArguments, PCWSTR pszTitle, IShellLink **ppsl, int index)
{
IShellLink *psl;
HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&psl));
if (SUCCEEDED(hr))
{
WCHAR szAppPath[MAX_PATH];
if (GetModuleFileName(NULL, szAppPath, ARRAYSIZE(szAppPath)))
{
hr = psl->SetPath(szAppPath);
if (SUCCEEDED(hr))
{
hr = psl->SetArguments(pszArguments);
if (SUCCEEDED(hr))
{
hr = psl->SetIconLocation(szAppPath, index + ICON_OFFSET);
Q_UNUSED(hr);
IPropertyStore *pps;
hr = psl->QueryInterface(IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr))
{
PROPVARIANT propvar;
hr = InitPropVariantFromString(pszTitle, &propvar);
if (SUCCEEDED(hr))
{
hr = pps->SetValue(PKEY_Title, propvar);
if (SUCCEEDED(hr))
{
hr = pps->Commit();
if (SUCCEEDED(hr))
{
hr = psl->QueryInterface(IID_PPV_ARGS(ppsl));
}
}
PropVariantClear(&propvar);
}
pps->Release();
}
}
}
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
psl->Release();
}
return hr;
}
HRESULT _AddTasksToList(ICustomDestinationList *pcdl, QStringList list)
{
IObjectCollection *poc;
HRESULT hr = CoCreateInstance(CLSID_EnumerableObjectCollection, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&poc));
if (SUCCEEDED(hr))
{
PCWSTR args[] = {
L"--new:word",
L"--new:cell",
L"--new:slide",
L"--new:form"
};
IShellLink * psl;
for (int i = 0; i < MAX_TASK_NUM && i < list.size(); i++) {
hr = _CreateShellLink(args[i], list[i].replace('_', ' ').toStdWString().c_str(), &psl, i);
if (SUCCEEDED(hr))
{
hr = poc->AddObject(psl);
psl->Release();
}
}
if (SUCCEEDED(hr))
{
IObjectArray * poa;
hr = poc->QueryInterface(IID_PPV_ARGS(&poa));
if (SUCCEEDED(hr))
{
hr = pcdl->AddUserTasks(poa);
poa->Release();
}
}
poc->Release();
}
return hr;
}
void CreateJumpList(const QStringList &list)
{
ICustomDestinationList *pcdl;
HRESULT hr = CoCreateInstance(CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pcdl));
if (SUCCEEDED(hr)) {
UINT cMinSlots;
IObjectArray *poaRemoved;
hr = pcdl->BeginList(&cMinSlots, IID_PPV_ARGS(&poaRemoved));
if (SUCCEEDED(hr))
{
hr = _AddTasksToList(pcdl, list);
if (SUCCEEDED(hr))
{
hr = pcdl->CommitList();
}
}
poaRemoved->Release();
pcdl->Release();
}
Q_UNUSED(hr);
}
void DeleteJumpList()
{
ICustomDestinationList *pcdl;
HRESULT hr = CoCreateInstance(CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pcdl));
if (SUCCEEDED(hr)) {
hr = pcdl->DeleteList(NULL);
pcdl->Release();
}
Q_UNUSED(hr);
}
void CreateJumpList(const QStringList &list);
void DeleteJumpList();
void ClearHistory();
#endif // JUMPLIST_H

View File

@ -36,6 +36,7 @@
#include "Windows.h"
#ifndef __OS_WIN_XP
# include "jumplist.h"
# include <shlobj_core.h>
#endif
typedef HRESULT (__stdcall *SetCurrentProcessExplicitAppUserModelIDProc)(PCWSTR AppID);
@ -82,8 +83,14 @@ int main(int argc, char *argv[])
return 0;
} else
if (_cmdArgs.contains("--remove-jump-list")) {
ClearHistory();
DeleteJumpList();
return 0;
} else
if (_cmdArgs.contains("--add-to-recent") && _cmdArgs.size() > 1) {
std::wstring path = _cmdArgs.at(1).toStdWString();
SHAddToRecentDocs(SHARD_PATHW, path.c_str());
return 0;
}
#endif

View File

@ -232,6 +232,9 @@ public:
CMessage::error(m_appmanager.mainWindow()->handle(),
QObject::tr("File %1 cannot be opened or doesn't exists.").arg(_info.fileName()));
}
#ifdef _WIN32
else Utils::addToRecent(file_path);
#endif
}
}

View File

@ -50,6 +50,8 @@
int main( int argc, char *argv[] )
{
#ifdef _WIN32
if (argc > 1 && strcmp(argv[1], "--add-to-recent") == 0)
return 0;
Core_SetProcessDpiAwareness();
Utils::setAppUserModelId(APP_USER_MODEL_ID);
WCHAR * cm_line = GetCommandLine();

View File

@ -44,6 +44,7 @@
#include <QScreen>
#include <QStorageInfo>
#include <QPrinterInfo>
#include <QProcess>
#include "cascapplicationmanagerwrapper.h"
#include "qdpichecker.h"
#include "common/File.h"
@ -55,7 +56,6 @@
#include "lmcons.h"
typedef HRESULT (__stdcall *SetCurrentProcessExplicitAppUserModelIDProc)(PCWSTR AppID);
#else
# include <QProcess>
# include <QEventLoop>
#include <sys/stat.h>
#include <stdlib.h>
@ -728,6 +728,13 @@ Utils::WinVer Utils::getWinVersion()
}
return WinVer::Undef;
}
void Utils::addToRecent(const std::wstring &path)
{
QString _path = QString::fromStdWString(path);
QString appPath = qApp->applicationDirPath();
QProcess::startDetached(appPath + "/" + QString(REG_APP_NAME), {"--add-to-recent", QDir::toNativeSeparators(_path)}, appPath);
}
#endif
QString Utils::replaceBackslash(const QString& path)

View File

@ -122,6 +122,7 @@ public:
Undef, WinXP, WinVista, Win7, Win8, Win8_1, Win10, Win11
};
static WinVer getWinVersion();
static void addToRecent(const std::wstring&);
#endif
};

View File

@ -707,6 +707,9 @@ void CMainWindow::doOpenLocalFile(COpenOptions& opts)
int result = m_pTabs->openLocalDocument(opts, true);
if ( !(result < 0) ) {
toggleButtonMain(false, true);
#ifdef _WIN32
Utils::addToRecent(opts.wurl);
#endif
} else
if (result == -255) {
QTimer::singleShot(0, this, [=] {