Files
Open-Shell-Menu/Src/StartMenu/StartMenuDLL/JumpLists.cpp
ge0rdi 7d59f5ebfb Support for modern app jump-list tasks
Task jump-list items for modern apps require special handling.

First of all they don't provide icon directly.
Icon location is stored in `PKEY_AppUserModel_DestListLogoUri` property.
And then has to be resolved for given application package.

Next, context menu of provided `IShellLink` item doesn't seem to be able
to start actual link target.
Thus we need to obtain context menu of target item.

Fixes #375.
2020-09-23 11:50:32 +02:00

656 lines
20 KiB
C++

// Classic Shell (c) 2009-2017, Ivo Beltchev
// Open-Shell (c) 2017-2018, The Open-Shell Team
// Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author
#include "stdafx.h"
#include "JumpLists.h"
#include "ItemManager.h"
#include "ResourceHelper.h"
#include "Translations.h"
#include "FNVHash.h"
#include "LogManager.h"
#include <propkey.h>
#include <StrSafe.h>
static const CLSID CLSID_AutomaticDestinationList={0xf0ae1542, 0xf497, 0x484b, {0xa1, 0x75, 0xa2, 0x0d, 0xb0, 0x91, 0x44, 0xba}};
struct APPDESTCATEGORY
{
int type;
union
{
wchar_t *name;
int subType;
};
int count;
int pad[10]; // just in case
};
static const GUID IID_IDestinationList={0x03f1eed2, 0x8676, 0x430b, {0xab, 0xe1, 0x76, 0x5c, 0x1d, 0x8f, 0xe1, 0x47}};
static const GUID IID_IDestinationList10a={0xfebd543d, 0x1f7b, 0x4b38, {0x94, 0x0b, 0x59, 0x33, 0xbd, 0x2c, 0xb2, 0x1b}}; // 10240
static const GUID IID_IDestinationList10b={0x507101cd, 0xf6ad, 0x46c8, {0x8e, 0x20, 0xee, 0xb9, 0xe6, 0xba, 0xc4, 0x7f}}; // 10547
interface IDestinationList: public IUnknown
{
public:
STDMETHOD(SetMinItems)();
virtual HRESULT STDMETHODCALLTYPE SetApplicationID( LPCWSTR appUserModelId ) = 0;
STDMETHOD(GetSlotCount)();
virtual HRESULT STDMETHODCALLTYPE GetCategoryCount( UINT *pCount ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetCategory( UINT index, int getCatFlags, APPDESTCATEGORY *pCategory ) = 0;
STDMETHOD(DeleteCategory)();
virtual HRESULT STDMETHODCALLTYPE EnumerateCategoryDestinations( UINT index, REFIID riid, void **ppvObject ) = 0;
STDMETHOD(RemoveDestination)( IUnknown *pItem );
STDMETHOD(ResolveDestination)();
};
static const GUID IID_IAutomaticDestinationList={0xbc10dce3, 0x62f2, 0x4bc6, {0xaf, 0x37, 0xdb, 0x46, 0xed, 0x78, 0x73, 0xc4}};
static const GUID IID_IAutomaticDestinationList10b={0xe9c5ef8d, 0xfd41, 0x4f72, {0xba, 0x87, 0xeb, 0x03 ,0xba, 0xd5, 0x81, 0x7c}}; // 10547
interface IAutomaticDestinationList: public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE Initialize( LPCWSTR appUserModelId, LPCWSTR lnkPath, LPCWSTR ) = 0;
virtual HRESULT STDMETHODCALLTYPE HasList( BOOL *pHasList ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetList( int listType, unsigned int maxCount, REFIID riid, void **ppvObject ) = 0;
STDMETHOD(AddUsagePoint)();
virtual HRESULT STDMETHODCALLTYPE PinItem( IUnknown *pItem, int pinIndex ) = 0; // -1 - pin, -2 - unpin
STDMETHOD(IsPinned)();
virtual HRESULT STDMETHODCALLTYPE RemoveDestination( IUnknown *pItem ) = 0;
STDMETHOD(SetUsageData)();
STDMETHOD(GetUsageData)();
STDMETHOD(ResolveDestination)();
virtual HRESULT STDMETHODCALLTYPE ClearList( int listType ) = 0;
};
interface IAutomaticDestinationList10b: public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE Initialize( LPCWSTR appUserModelId, LPCWSTR lnkPath, LPCWSTR ) = 0;
virtual HRESULT STDMETHODCALLTYPE HasList( BOOL *pHasList ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetList( int listType, unsigned int maxCount, unsigned int flags, REFIID riid, void **ppvObject ) = 0;
STDMETHOD(AddUsagePoint)();
virtual HRESULT STDMETHODCALLTYPE PinItem( IUnknown *pItem, int pinIndex ) = 0; // -1 - pin, -2 - unpin
STDMETHOD(IsPinned)();
virtual HRESULT STDMETHODCALLTYPE RemoveDestination( IUnknown *pItem ) = 0;
STDMETHOD(SetUsageData)();
STDMETHOD(GetUsageData)();
STDMETHOD(ResolveDestination)();
virtual HRESULT STDMETHODCALLTYPE ClearList( int listType ) = 0;
};
class CAutomaticList
{
public:
CAutomaticList( const wchar_t *appid );
bool HasList( void );
CComPtr<IObjectCollection> GetList( int listType, unsigned int maxCount );
void PinItem( IUnknown *pItem, int pinIndex );
bool RemoveDestination( IUnknown *pItem );
private:
CComPtr<IAutomaticDestinationList> m_pAutoList;
CComPtr<IAutomaticDestinationList10b> m_pAutoList10b;
};
CAutomaticList::CAutomaticList( const wchar_t *appid )
{
CComPtr<IUnknown> pAutoListUnk;
if (SUCCEEDED(pAutoListUnk.CoCreateInstance(CLSID_AutomaticDestinationList)))
{
pAutoListUnk->QueryInterface(IID_IAutomaticDestinationList,(void**)&m_pAutoList);
if (m_pAutoList)
{
if (FAILED(m_pAutoList->Initialize(appid,NULL,NULL)))
m_pAutoList=NULL;
}
else if (GetWinVersion()>=WIN_VER_WIN10)
{
pAutoListUnk->QueryInterface(IID_IAutomaticDestinationList10b,(void**)&m_pAutoList10b);
if (m_pAutoList10b)
{
if (FAILED(m_pAutoList10b->Initialize(appid,NULL,NULL)))
m_pAutoList10b=NULL;
}
}
}
}
bool CAutomaticList::HasList( void )
{
BOOL hasList;
if (m_pAutoList)
{
if (FAILED(m_pAutoList->HasList(&hasList)) || !hasList)
return false;
}
else if (m_pAutoList10b)
{
if (FAILED(m_pAutoList10b->HasList(&hasList)) || !hasList)
return false;
}
else
return false;
CComPtr<IObjectCollection> pCollection;
UINT count;
pCollection=GetList(1,1);
if (pCollection && SUCCEEDED(pCollection->GetCount(&count)) && count>0)
return true;
pCollection=GetList(0,1);
if (pCollection && SUCCEEDED(pCollection->GetCount(&count)) && count>0)
return true;
return false;
}
CComPtr<IObjectCollection> CAutomaticList::GetList( int listType, unsigned int maxCount )
{
CComPtr<IObjectCollection> pCollection;
if (m_pAutoList)
m_pAutoList->GetList(listType,maxCount,IID_IObjectCollection,(void**)&pCollection);
else if (m_pAutoList10b)
m_pAutoList10b->GetList(listType,maxCount,1,IID_IObjectCollection,(void**)&pCollection);
return pCollection;
}
void CAutomaticList::PinItem( IUnknown *pItem, int pinIndex )
{
if (m_pAutoList)
m_pAutoList->PinItem(pItem,pinIndex);
else if (m_pAutoList10b)
m_pAutoList10b->PinItem(pItem,pinIndex);
}
bool CAutomaticList::RemoveDestination( IUnknown *pItem )
{
if (m_pAutoList)
return SUCCEEDED(m_pAutoList->RemoveDestination(pItem));
else if (m_pAutoList10b)
return SUCCEEDED(m_pAutoList10b->RemoveDestination(pItem));
return false;
}
static CComPtr<IDestinationList> GetCustomList( const wchar_t *appid )
{
CComPtr<IUnknown> pCustomListUnk;
if (SUCCEEDED(pCustomListUnk.CoCreateInstance(CLSID_DestinationList)))
{
CComPtr<IDestinationList> pCustomList;
if (GetWinVersion()<WIN_VER_WIN10)
pCustomListUnk->QueryInterface(IID_IDestinationList,(void**)&pCustomList);
else
{
if (FAILED(pCustomListUnk->QueryInterface(IID_IDestinationList10a,(void**)&pCustomList)))
pCustomListUnk->QueryInterface(IID_IDestinationList10b,(void**)&pCustomList);
}
if (pCustomList && SUCCEEDED(pCustomList->SetApplicationID(appid)))
return pCustomList;
}
return CComPtr<IDestinationList>();
}
// Returns true if the given app has a non-empty jumplist
bool HasJumplist( const wchar_t *appid )
{
Assert(GetWinVersion()>=WIN_VER_WIN7);
CComPtr<IDestinationList> pCustomList=GetCustomList(appid);
if (pCustomList)
{
UINT count;
if (SUCCEEDED(pCustomList->GetCategoryCount(&count)) && count>0)
return true;
}
if (CAutomaticList(appid).HasList())
return true;
return false;
}
static unsigned int CalcLinkHash( IShellLink *pLink )
{
CAbsolutePidl pidl;
if (FAILED(pLink->GetIDList(&pidl)))
return 0;
unsigned int hash=FNV_HASH0;
CComString pName;
if (SUCCEEDED(SHGetNameFromIDList(pidl,SIGDN_DESKTOPABSOLUTEPARSING,&pName)))
{
pName.MakeUpper();
hash=CalcFNVHash(pName);
}
CComQIPtr<IPropertyStore> pStore=pLink;
if (pStore)
hash=CalcFNVHash(GetPropertyStoreString(pStore,PKEY_Link_Arguments),hash);
return hash;
}
static void AddJumpItem( CJumpGroup &group, IUnknown *pUnknown, std::vector<CComPtr<IShellItem>> &ignoreItems, std::vector<unsigned int> &ignoreLinks )
{
CJumpItem item;
item.type=CJumpItem::TYPE_UNKNOWN;
item.pItem=pUnknown;
item.hash=0;
item.bHidden=false;
item.bHasArguments=false;
CComQIPtr<IShellItem> pItem=pUnknown;
if (pItem)
{
for (std::vector<CComPtr<IShellItem>>::const_iterator it=ignoreItems.begin();it!=ignoreItems.end();++it)
{
int order;
if (SUCCEEDED(pItem->Compare(*it,SICHINT_CANONICAL|SICHINT_TEST_FILESYSPATH_IF_NOT_EQUAL,&order)) && order==0)
return;
}
item.type=CJumpItem::TYPE_ITEM;
CComString pName;
if (FAILED(pItem->GetDisplayName(SIGDN_NORMALDISPLAY,&pName)))
return;
item.name=pName;
pName.Clear();
if (SUCCEEDED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName)))
{
LOG_MENU(LOG_OPEN,L"Jumplist Item Path: %s",(const wchar_t*)pName);
pName.MakeUpper();
item.hash=CalcFNVHash(pName);
}
LOG_MENU(LOG_OPEN,L"Jumplist Item Name: %s",item.name);
group.items.push_back(item);
return;
}
CComQIPtr<IShellLink> pLink=pUnknown;
if (pLink)
{
unsigned int hash=CalcLinkHash(pLink);
for (std::vector<unsigned int>::const_iterator it=ignoreLinks.begin();it!=ignoreLinks.end();++it)
{
if (hash==*it)
return;
}
item.type=CJumpItem::TYPE_LINK;
CComQIPtr<IPropertyStore> pStore(pLink);
if (pStore)
{
PROPVARIANT val;
PropVariantInit(&val);
if (group.type==CJumpGroup::TYPE_TASKS && SUCCEEDED(pStore->GetValue(PKEY_AppUserModel_IsDestListSeparator,&val)) && val.vt==VT_BOOL && val.boolVal)
{
item.type=CJumpItem::TYPE_SEPARATOR;
PropVariantClear(&val);
}
else
{
CString str=GetPropertyStoreString(pStore,PKEY_Title);
if (!str.IsEmpty())
{
wchar_t name[256];
SHLoadIndirectString(str,name,_countof(name),NULL);
item.name=name;
}
}
}
CAbsolutePidl pidl;
if (SUCCEEDED(pLink->GetIDList(&pidl)))
{
CComString pName;
if (item.name.IsEmpty())
{
if (SUCCEEDED(SHGetNameFromIDList(pidl,SIGDN_NORMALDISPLAY,&pName)))
{
item.name=pName;
}
}
pName.Clear();
if (SUCCEEDED(SHGetNameFromIDList(pidl,SIGDN_DESKTOPABSOLUTEPARSING,&pName)))
{
LOG_MENU(LOG_OPEN,L"Jumplist Link Path: %s",(const wchar_t*)pName);
pName.MakeUpper();
item.hash=CalcFNVHash(pName);
}
CComQIPtr<IPropertyStore> pStore(pLink);
if (pStore)
{
CString args=GetPropertyStoreString(pStore,PKEY_Link_Arguments);
if (!args.IsEmpty())
{
LOG_MENU(LOG_OPEN,L"Jumplist Link Args: %s",args);
item.hash=CalcFNVHash(args,item.hash);
item.bHasArguments=true;
}
}
}
LOG_MENU(LOG_OPEN,L"Jumplist Link Name: %s",item.name);
#ifdef _DEBUG
LogPropertyStore(LOG_OPEN, pStore);
#endif
if (!item.name.IsEmpty())
group.items.push_back(item);
return;
}
}
static void AddJumpCollection( CJumpGroup &group, IObjectCollection *pCollection, std::vector<CComPtr<IShellItem>> &ignoreItems, std::vector<unsigned int> &ignoreLinks )
{
UINT count;
if (SUCCEEDED(pCollection->GetCount(&count)))
{
for (UINT i=0;i<count;i++)
{
CComPtr<IUnknown> pUnknown;
if (SUCCEEDED(pCollection->GetAt(i,IID_IUnknown,(void**)&pUnknown)) && pUnknown)
AddJumpItem(group,pUnknown,ignoreItems,ignoreLinks);
}
}
}
// Returns the jumplist for the given shortcut
bool GetJumplist( const wchar_t *appid, CJumpList &list, int maxCount, int maxHeight, int sepHeight, int itemHeight )
{
Assert(GetWinVersion()>=WIN_VER_WIN7);
list.Clear();
UINT categoryCount=0;
CComPtr<IDestinationList> pCustomList=GetCustomList(appid);
if (pCustomList)
{
if (FAILED(pCustomList->GetCategoryCount(&categoryCount)))
categoryCount=0;
}
list.groups.reserve(categoryCount+2);
std::vector<CComPtr<IShellItem>> ignoreItems;
std::vector<unsigned int> ignoreLinks;
CAutomaticList autoList(appid);
{
// add pinned
CComPtr<IObjectCollection> pPinnedList=autoList.GetList(0,maxCount);
if (pPinnedList)
{
Assert(list.groups.empty());
list.groups.resize(list.groups.size()+1);
CJumpGroup &group=*list.groups.rbegin();
group.type=CJumpGroup::TYPE_PINNED;
group.name=FindTranslation(L"JumpList.Pinned",L"Pinned");
AddJumpCollection(group,pPinnedList,ignoreItems,ignoreLinks);
for (std::vector<CJumpItem>::const_iterator it=group.items.begin();it!=group.items.end();++it)
{
CComQIPtr<IShellItem> pShellItem(it->pItem);
if (pShellItem)
ignoreItems.push_back(pShellItem);
else
{
CComQIPtr<IShellLink> pLink(it->pItem);
if (pLink)
{
unsigned int hash=CalcLinkHash(pLink);
if (hash)
ignoreLinks.push_back(hash);
}
}
}
}
}
int taskIndex=-1;
for (UINT catIndex=0;catIndex<categoryCount;catIndex++)
{
APPDESTCATEGORY category={0};
if (SUCCEEDED(pCustomList->GetCategory(catIndex,1,&category)))
{
if (category.type==0)
{
// custom group
if (category.name)
{
wchar_t name[256];
SHLoadIndirectString(category.name,name,_countof(name),NULL);
CoTaskMemFree(category.name);
CComPtr<IObjectCollection> pCollection;
if (SUCCEEDED(pCustomList->EnumerateCategoryDestinations(catIndex,IID_IObjectCollection,(void**)&pCollection)))
{
list.groups.resize(list.groups.size()+1);
CJumpGroup &group=*list.groups.rbegin();
group.name=name;
group.type=CJumpGroup::TYPE_CUSTOM;
AddJumpCollection(group,pCollection,ignoreItems,ignoreLinks);
}
}
}
else if (category.type==1)
{
// standard group
if (category.subType==1 || category.subType==2)
{
CComPtr<IObjectCollection> pCollection=autoList.GetList(3-category.subType,maxCount);
if (pCollection)
{
list.groups.resize(list.groups.size()+1);
CJumpGroup &group=*list.groups.rbegin();
if (category.subType==1)
{
group.type=CJumpGroup::TYPE_FREQUENT;
group.name=FindTranslation(L"JumpList.Frequent",L"Frequent");
}
else
{
group.type=CJumpGroup::TYPE_RECENT;
group.name=FindTranslation(L"JumpList.Recent",L"Recent");
}
AddJumpCollection(group,pCollection,ignoreItems,ignoreLinks);
}
}
}
else if (category.type==2 && taskIndex==-1)
{
taskIndex=catIndex;
}
}
}
if (taskIndex!=-1)
{
// add tasks
CComPtr<IObjectCollection> pCollection;
if (SUCCEEDED(pCustomList->EnumerateCategoryDestinations(taskIndex,IID_IObjectCollection,(void**)&pCollection)))
{
list.groups.resize(list.groups.size()+1);
CJumpGroup &group=*list.groups.rbegin();
group.name=FindTranslation(L"JumpList.Tasks",L"Tasks");
group.type=CJumpGroup::TYPE_TASKS;
AddJumpCollection(group,pCollection,ignoreItems,ignoreLinks);
}
}
if (categoryCount==0)
{
// add recent
CComPtr<IObjectCollection> pRecentList=autoList.GetList(1,maxCount);
if (pRecentList)
{
list.groups.resize(list.groups.size()+1);
CJumpGroup &group=*list.groups.rbegin();
group.type=CJumpGroup::TYPE_RECENT;
group.name=FindTranslation(L"JumpList.Recent",L"Recent");
AddJumpCollection(group,pRecentList,ignoreItems,ignoreLinks);
}
}
// limit the item count (not tasks or pinned)
for (std::vector<CJumpGroup>::iterator it=list.groups.begin();it!=list.groups.end();++it)
{
CJumpGroup &group=*it;
if (group.type==CJumpGroup::TYPE_TASKS || group.type==CJumpGroup::TYPE_PINNED)
maxHeight-=sepHeight+(int)group.items.size()*itemHeight;
}
for (std::vector<CJumpGroup>::iterator it=list.groups.begin();it!=list.groups.end();++it)
{
CJumpGroup &group=*it;
if (group.type!=CJumpGroup::TYPE_TASKS && group.type!=CJumpGroup::TYPE_PINNED)
{
maxHeight-=sepHeight;
for (std::vector<CJumpItem>::iterator it2=group.items.begin();it2!=group.items.end();++it2)
if (!it2->bHidden)
{
it2->bHidden=(maxCount<=0 || maxHeight<itemHeight);
maxCount--;
maxHeight-=itemHeight;
}
}
}
// hide empty groups
for (std::vector<CJumpGroup>::iterator it=list.groups.begin();it!=list.groups.end();++it)
{
CJumpGroup &group=*it;
group.bHidden=true;
for (std::vector<CJumpItem>::const_iterator it2=group.items.begin();it2!=group.items.end();++it2)
if (!it2->bHidden)
{
group.bHidden=false;
break;
}
}
return true;
}
// Executes the given item using the correct application
bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &item, HWND hwnd )
{
Assert(GetWinVersion()>=WIN_VER_WIN7);
if (!item.pItem)
return false;
if (item.type==CJumpItem::TYPE_ITEM)
{
CComQIPtr<IShellItem> pItem(item.pItem);
if (!pItem)
return false;
SHELLEXECUTEINFO execute={sizeof(execute),SEE_MASK_IDLIST|SEE_MASK_FLAG_LOG_USAGE};
execute.nShow=SW_SHOWNORMAL;
CAbsolutePidl pidl;
if (SUCCEEDED(SHGetIDListFromObject(pItem,&pidl)))
{
execute.lpIDList=pidl;
ShellExecuteEx(&execute);
}
return true;
}
if (item.type==CJumpItem::TYPE_LINK)
{
// Name: System.AppUserModel.HostEnvironment -- PKEY_AppUserModel_HostEnvironment
// Type: UInt32 -- VT_UI4
// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 14
static const PROPERTYKEY PKEY_AppUserModel_HostEnvironment = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 14 };
// Name: System.AppUserModel.ActivationContext -- PKEY_AppUserModel_ActivationContext
// Type: String -- VT_LPWSTR
// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 20
static const PROPERTYKEY PKEY_AppUserModel_ActivationContext = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 20 };
CComQIPtr<IContextMenu> pMenu(item.pItem);
CStringA params;
CComQIPtr<IShellLink> pLink(item.pItem);
if (pLink)
{
CComQIPtr<IPropertyStore> store(pLink);
if (store)
{
auto appId = GetPropertyStoreString(store, PKEY_AppUserModel_ID);
if (!appId.IsEmpty())
{
CComPtr<IShellItem2> target;
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder, 0, appId, IID_PPV_ARGS(&target))))
{
ULONG modern = 0;
if (SUCCEEDED(target->GetUInt32(PKEY_AppUserModel_HostEnvironment, &modern)) && modern)
{
CComQIPtr<IContextMenu> targetMenu;
if (SUCCEEDED(target->BindToHandler(nullptr, BHID_SFUIObject, IID_PPV_ARGS(&targetMenu))))
{
pMenu = targetMenu;
params = CT2CA(GetPropertyStoreString(store, PKEY_AppUserModel_ActivationContext));
}
}
}
}
}
}
// invoke the link through its context menu
if (!pMenu)
return false;
HRESULT hr;
HMENU menu=CreatePopupMenu();
hr=pMenu->QueryContextMenu(menu,0,1,1000,CMF_DEFAULTONLY);
if (FAILED(hr))
{
DestroyMenu(menu);
return false;
}
int id=GetMenuDefaultItem(menu,FALSE,0);
if (id>0)
{
CMINVOKECOMMANDINFO command={sizeof(command),CMIC_MASK_FLAG_LOG_USAGE};
command.lpVerb=MAKEINTRESOURCEA(id-1);
if (!params.IsEmpty())
command.lpParameters = params;
wchar_t path[_MAX_PATH];
GetModuleFileName(NULL,path,_countof(path));
if (_wcsicmp(PathFindFileName(path),L"explorer.exe")==0)
command.fMask|=CMIC_MASK_ASYNCOK;
command.hwnd=hwnd;
command.nShow=SW_SHOWNORMAL;
hr=pMenu->InvokeCommand(&command);
}
DestroyMenu(menu);
}
return true;
}
// Removes the given item from the jumplist
void RemoveJumpItem( const CItemManager::ItemInfo *pAppInfo, CJumpList &list, int groupIdx, int itemIdx )
{
CString appid;
{
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
appid=pAppInfo->GetAppid();
}
CJumpGroup &group=list.groups[groupIdx];
if (group.type==CJumpGroup::TYPE_FREQUENT || group.type==CJumpGroup::TYPE_RECENT)
{
if (CAutomaticList(appid).RemoveDestination(group.items[itemIdx].pItem))
group.items.erase(group.items.begin()+itemIdx);
}
else
{
CComPtr<IDestinationList> pCustomList=GetCustomList(appid);
if (pCustomList)
{
if (SUCCEEDED(pCustomList->RemoveDestination(group.items[itemIdx].pItem)))
group.items.erase(group.items.begin()+itemIdx);
}
}
}
// Pins or unpins the given item from the jumplist
void PinJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpList &list, int groupIdx, int itemIdx, bool bPin, int pinIndex )
{
CString appid;
{
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
appid=pAppInfo->GetAppid();
}
const CJumpGroup &group=list.groups[groupIdx];
CAutomaticList(appid).PinItem(group.items[itemIdx].pItem,bPin?pinIndex:-2);
}