Skip to content

Commit

Permalink
Add tasks to Jump List (#1828)
Browse files Browse the repository at this point in the history
  • Loading branch information
sdottaka authored May 6, 2023
1 parent 5e7cd2e commit e4ccd78
Show file tree
Hide file tree
Showing 53 changed files with 1,273 additions and 50 deletions.
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

0 comments on commit e4ccd78

Please sign in to comment.