Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tasks to Jump List #1828

Merged
merged 5 commits into from
May 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion Installer/InnoSetup/WinMergeARM64.is6.iss
Original file line number Diff line number Diff line change
Expand Up @@ -960,15 +960,29 @@ begin
end;
end;

procedure RegisterUserTasks();
var
params: string;
UserTasksFlags: DWORD;
ResultCode: Integer;
Begin
UserTasksFlags := 4097; { 4096(Clipboard Compare)+1(New Text Compare) }
RegQueryDWORDValue(HKCU, 'Software\Thingamahoochie\WinMerge', 'UserTasksFlags', UserTasksFlags);
params := '/s- /minimize /noninteractive /set-usertasks-to-jumplist ' + IntToStr(UserTasksFlags);
Exec(ExpandConstant('{app}\WinMergeU.exe'), params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;

{This event procedure is queed each time the user changes pages within the installer}
Procedure CurPageChanged(CurPage: integer);
Begin
{if the installer reaches the file copy page then...}
If CurPage = wpInstalling Then
{Delete the previous start menu group if the location has changed since the last install}
DeletePreviousStartMenu;
If CurPage = wpFinished Then
If CurPage = wpFinished Then Begin
DeleteRenamedFiles;
RegisterUserTasks;
End;
End;

// Checks if context menu is already enabled for shell extension
Expand Down
16 changes: 15 additions & 1 deletion Installer/InnoSetup/WinMergeX64.is6.iss
Original file line number Diff line number Diff line change
Expand Up @@ -959,15 +959,29 @@ begin
end;
end;

procedure RegisterUserTasks();
var
params: string;
UserTasksFlags: DWORD;
ResultCode: Integer;
Begin
UserTasksFlags := 4097; { 4096(Clipboard Compare)+1(New Text Compare) }
RegQueryDWORDValue(HKCU, 'Software\Thingamahoochie\WinMerge', 'UserTasksFlags', UserTasksFlags);
params := '/s- /minimize /noninteractive /set-usertasks-to-jumplist ' + IntToStr(UserTasksFlags);
Exec(ExpandConstant('{app}\WinMergeU.exe'), params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;

{This event procedure is queed each time the user changes pages within the installer}
Procedure CurPageChanged(CurPage: integer);
Begin
{if the installer reaches the file copy page then...}
If CurPage = wpInstalling Then
{Delete the previous start menu group if the location has changed since the last install}
DeletePreviousStartMenu;
If CurPage = wpFinished Then
If CurPage = wpFinished Then Begin
DeleteRenamedFiles;
RegisterUserTasks;
End;
End;

// Checks if context menu is already enabled for shell extension
Expand Down
16 changes: 15 additions & 1 deletion Installer/InnoSetup/WinMergeX64.iss
Original file line number Diff line number Diff line change
Expand Up @@ -940,15 +940,29 @@ begin
end;
end;

procedure RegisterUserTasks();
var
params: string;
UserTasksFlags: DWORD;
ResultCode: Integer;
Begin
UserTasksFlags := 4097; { 4096(Clipboard Compare)+1(New Text Compare) }
RegQueryDWORDValue(HKCU, 'Software\Thingamahoochie\WinMerge', 'UserTasksFlags', UserTasksFlags);
params := '/s- /minimize /noninteractive /set-usertasks-to-jumplist ' + IntToStr(UserTasksFlags);
Exec(ExpandConstant('{app}\WinMergeU.exe'), params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;

{This event procedure is queed each time the user changes pages within the installer}
Procedure CurPageChanged(CurPage: integer);
Begin
{if the installer reaches the file copy page then...}
If CurPage = wpInstalling Then
{Delete the previous start menu group if the location has changed since the last install}
DeletePreviousStartMenu;
If CurPage = wpFinished Then
If CurPage = wpFinished Then Begin
DeleteRenamedFiles;
RegisterUserTasks;
End;
End;

// Checks if context menu is already enabled for shell extension
Expand Down
16 changes: 15 additions & 1 deletion Installer/InnoSetup/WinMergeX64NonAdmin.iss
Original file line number Diff line number Diff line change
Expand Up @@ -938,15 +938,29 @@ begin
end;
end;

procedure RegisterUserTasks();
var
params: string;
UserTasksFlags: DWORD;
ResultCode: Integer;
Begin
UserTasksFlags := 4097; { 4096(Clipboard Compare)+1(New Text Compare) }
RegQueryDWORDValue(HKCU, 'Software\Thingamahoochie\WinMerge', 'UserTasksFlags', UserTasksFlags);
params := '/s- /minimize /noninteractive /set-usertasks-to-jumplist ' + IntToStr(UserTasksFlags);
Exec(ExpandConstant('{app}\WinMergeU.exe'), params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;

{This event procedure is queed each time the user changes pages within the installer}
Procedure CurPageChanged(CurPage: integer);
Begin
{if the installer reaches the file copy page then...}
If CurPage = wpInstalling Then
{Delete the previous start menu group if the location has changed since the last install}
DeletePreviousStartMenu;
If CurPage = wpFinished Then
If CurPage = wpFinished Then Begin
DeleteRenamedFiles;
RegisterUserTasks;
End;
End;

// Checks if context menu is already enabled for shell extension
Expand Down
16 changes: 15 additions & 1 deletion Installer/InnoSetup/WinMergeX86.iss
Original file line number Diff line number Diff line change
Expand Up @@ -960,15 +960,29 @@ begin
end;
end;

procedure RegisterUserTasks();
var
params: string;
UserTasksFlags: DWORD;
ResultCode: Integer;
Begin
UserTasksFlags := 4097; { 4096(Clipboard Compare)+1(New Text Compare) }
RegQueryDWORDValue(HKCU, 'Software\Thingamahoochie\WinMerge', 'UserTasksFlags', UserTasksFlags);
params := '/s- /minimize /noninteractive /set-usertasks-to-jumplist ' + IntToStr(UserTasksFlags);
Exec(ExpandConstant('{app}\WinMergeU.exe'), params, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;

{This event procedure is queed each time the user changes pages within the installer}
Procedure CurPageChanged(CurPage: integer);
Begin
{if the installer reaches the file copy page then...}
If CurPage = wpInstalling Then
{Delete the previous start menu group if the location has changed since the last install}
DeletePreviousStartMenu;
If CurPage = wpFinished Then
If CurPage = wpFinished Then Begin
DeleteRenamedFiles;
RegisterUserTasks;
End;
End;

// Checks if context menu is already enabled for shell extension
Expand Down
192 changes: 155 additions & 37 deletions Src/JumpList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace
std::wstring g_appid;
wchar_t g_exe_path[260];

IShellLinkW *CreateShellLink(const std::wstring& app_path, const std::wstring& params, const std::wstring& title, const std::wstring& desc, int icon_index)
IShellLinkW *CreateShellLink(const std::wstring& app_path, const std::wstring& params, const std::wstring& title, const std::wstring& desc, const std::wstring& icon_path, int icon_index)
{
IShellLinkW *pShellLink = nullptr;
if (FAILED(CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
Expand All @@ -35,8 +35,15 @@ IShellLinkW *CreateShellLink(const std::wstring& app_path, const std::wstring& p
GetModuleFileNameW(nullptr, g_exe_path, sizeof(g_exe_path)/sizeof(g_exe_path[0]));
app_path2 = g_exe_path;
}
std::wstring icon_path2(icon_path);
if (icon_path.empty())
{
if (g_exe_path[0] == '\0')
GetModuleFileNameW(nullptr, g_exe_path, sizeof(g_exe_path)/sizeof(g_exe_path[0]));
icon_path2 = g_exe_path;
}
pShellLink->SetPath(app_path2.c_str());
pShellLink->SetIconLocation(app_path2.c_str(), icon_index);
pShellLink->SetIconLocation(icon_path2.c_str(), icon_index);
pShellLink->SetArguments(params.c_str());
pShellLink->SetDescription(desc.c_str());

Expand All @@ -56,6 +63,91 @@ IShellLinkW *CreateShellLink(const std::wstring& app_path, const std::wstring& p
return pShellLink;
}

static std::vector<JumpList::Item> GetList(IObjectArray *pObjectArray)
{
std::vector<JumpList::Item> list;
UINT nObjects;
if (SUCCEEDED(pObjectArray->GetCount(&nObjects)))
{
for (UINT i = 0; i < nObjects; ++i)
{
IShellLinkW *pShellLink;
if (SUCCEEDED(pObjectArray->GetAt(i, IID_IShellLinkW, (void **)&pShellLink)))
{
wchar_t szPath[MAX_PATH];
wchar_t szPathIcon[MAX_PATH];
wchar_t szDescription[MAX_PATH];
wchar_t szArguments[MAX_PATH * 6];
int icon_index = 0;
pShellLink->GetPath(szPath, sizeof(szPath) / sizeof(szPath[0]), nullptr, SLGP_RAWPATH);
pShellLink->GetDescription(szDescription, sizeof(szDescription) / sizeof(szDescription[0]));
pShellLink->GetArguments(szArguments, sizeof(szArguments) / sizeof(szArguments[0]));
pShellLink->GetIconLocation(szPathIcon, sizeof(szPathIcon) / sizeof(szPathIcon[0]), &icon_index);
IPropertyStore *pPS = nullptr;
if (SUCCEEDED(pShellLink->QueryInterface(IID_IPropertyStore, (void **)&pPS)))
{
PROPVARIANT pv;
PropVariantInit(&pv);
if (SUCCEEDED(pPS->GetValue(PKEY_Title, &pv)))
{
if (pv.vt == VT_LPWSTR && pv.bstrVal)
list.push_back(JumpList::Item(ucr::toTString(szPath), ucr::toTString(szArguments), ucr::toTString(pv.bstrVal), ucr::toTString(szDescription), ucr::toTString(szPathIcon), icon_index));
PropVariantClear(&pv);
}
pPS->Release();
}
pShellLink->Release();
}
}
}
return list;
}

static HRESULT CreateApplicationDocumentLists(IApplicationDocumentLists** ppDocumentLists)
{
HRESULT hr = CoCreateInstance(CLSID_ApplicationDocumentLists, nullptr, CLSCTX_INPROC_SERVER,
IID_IApplicationDocumentLists, (void**)ppDocumentLists);
if (FAILED(hr))
return hr;
hr = (*ppDocumentLists)->SetAppID(g_appid.c_str());
if (FAILED(hr))
{
(*ppDocumentLists)->Release();
return hr;
}
return hr;
}

static HRESULT CreateApplicationDestinations(IApplicationDestinations** ppApplicationDestinations)
{
HRESULT hr = CoCreateInstance(CLSID_ApplicationDestinations, nullptr, CLSCTX_INPROC_SERVER,
IID_IApplicationDestinations, (void**)ppApplicationDestinations);
if (FAILED(hr))
return hr;
hr = (*ppApplicationDestinations)->SetAppID(g_appid.c_str());
if (FAILED(hr))
{
(*ppApplicationDestinations)->Release();
return hr;
}
return hr;
}

static HRESULT CreateCustomDestinationList(ICustomDestinationList** ppCustomDestinationList)
{
HRESULT hr = CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER,
IID_ICustomDestinationList, (void**)ppCustomDestinationList);
if (FAILED(hr))
return hr;
hr = (*ppCustomDestinationList)->SetAppID(g_appid.c_str());
if (FAILED(hr))
{
(*ppCustomDestinationList)->Release();
return hr;
}
return hr;
}

}

namespace JumpList
Expand All @@ -78,11 +170,11 @@ bool SetCurrentProcessExplicitAppUserModelID(const std::wstring& appid)
#endif
}

bool AddToRecentDocs(const String& app_path, const String& params, const String& title, const String& desc, int icon_index)
bool AddToRecentDocs(const String& app_path, const String& params, const String& title, const String& desc, const String& icon_path, int icon_index)
{
SHARDAPPIDINFOLINK saiil;
saiil.pszAppID = g_appid.c_str();
saiil.psl = CreateShellLink(app_path, params, title, desc, icon_index);
saiil.psl = CreateShellLink(app_path, params, title, desc, icon_path, icon_index);
if (saiil.psl == nullptr)
return false;
SHAddToRecentDocs(SHARD_APPIDINFOLINK, &saiil);
Expand All @@ -94,49 +186,75 @@ std::vector<Item> GetRecentDocs(size_t nMaxItems)
{
std::vector<Item> list;
IApplicationDocumentLists *pDocumentLists = nullptr;
if (FAILED(CoCreateInstance(CLSID_ApplicationDocumentLists, nullptr, CLSCTX_INPROC_SERVER,
IID_IApplicationDocumentLists, (void **)&pDocumentLists)))
if (FAILED(CreateApplicationDocumentLists(&pDocumentLists)))
return list;
pDocumentLists->SetAppID(g_appid.c_str());

IObjectArray *pObjectArray;
if (SUCCEEDED(pDocumentLists->GetList(ADLT_RECENT, static_cast<UINT>(nMaxItems), IID_IObjectArray, (void **)&pObjectArray)))
{
UINT nObjects;
if (SUCCEEDED(pObjectArray->GetCount(&nObjects)))
list = GetList(pObjectArray);
pObjectArray->Release();
}
pDocumentLists->Release();
return list;
}

bool RemoveRecentDocs()
{
IApplicationDestinations* pDestinations = nullptr;
if (FAILED(CreateApplicationDestinations(&pDestinations)))
return false;
HRESULT hr = pDestinations->RemoveAllDestinations();
pDestinations->Release();
return SUCCEEDED(hr);
}

bool AddUserTasks(const std::vector<Item>& tasks)
{
ICustomDestinationList* pDestList = nullptr;
HRESULT hr = CreateCustomDestinationList(&pDestList);
if (FAILED(hr))
return false;
if (tasks.empty())
{
hr = pDestList->DeleteList(nullptr);
pDestList->Release();
return SUCCEEDED(hr);
}
IObjectCollection* pObjectCollection = nullptr;
hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pObjectCollection));
if (SUCCEEDED(hr))
{
for (const auto& task : tasks)
{
for (UINT i = 0; i < nObjects; ++i)
IShellLinkW* pShellLink = CreateShellLink(task.path, task.params, task.title, task.desc, task.icon_path, task.icon_index);
if (pShellLink)
{
IShellLinkW *pShellLink;
if (SUCCEEDED(pObjectArray->GetAt(i, IID_IShellLinkW, (void **)&pShellLink)))
{
wchar_t szPath[MAX_PATH];
wchar_t szDescription[MAX_PATH];
wchar_t szArguments[MAX_PATH * 6];
pShellLink->GetPath(szPath, sizeof(szPath) / sizeof(szPath[0]), nullptr, SLGP_RAWPATH);
pShellLink->GetDescription(szDescription, sizeof(szDescription) / sizeof(szDescription[0]));
pShellLink->GetArguments(szArguments, sizeof(szArguments) / sizeof(szArguments[0]));
IPropertyStore *pPS = nullptr;
if (SUCCEEDED(pShellLink->QueryInterface(IID_IPropertyStore, (void **)&pPS)))
{
PROPVARIANT pv;
PropVariantInit(&pv);
if (SUCCEEDED(pPS->GetValue(PKEY_Title, &pv)))
{
if (pv.vt == VT_LPWSTR && pv.bstrVal)
list.push_back(Item(ucr::toTString(szPath), ucr::toTString(szArguments), ucr::toTString(pv.bstrVal), ucr::toTString(szDescription)));
PropVariantClear(&pv);
}
pPS->Release();
}
pShellLink->Release();
}
pObjectCollection->AddObject(pShellLink);
pShellLink->Release();
}
}
pObjectArray->Release();

IObjectArray* pObjectArray = nullptr;
hr = pObjectCollection->QueryInterface(IID_PPV_ARGS(&pObjectArray));
if (SUCCEEDED(hr))
{
IObjectArray* pRemovedItems = nullptr;
UINT minSlots;
hr = pDestList->BeginList(&minSlots, IID_PPV_ARGS(&pRemovedItems));
if (SUCCEEDED(hr))
{
pRemovedItems->Release();
hr = pDestList->AddUserTasks(pObjectArray);
if (SUCCEEDED(hr))
hr = pDestList->CommitList();
}
pObjectArray->Release();
}
pObjectCollection->Release();
}
pDocumentLists->Release();
return list;
pDestList->Release();
return SUCCEEDED(hr);
}

}
Loading