mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-12 01:47:24 +10:00
It reports just one category with unsupported type, thus jump-list will be empty. Fixes #487.
662 lines
20 KiB
C++
662 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)
|
|
{
|
|
// skip Settings app (it reports one category with unsupported type, thus jump-list will be empty)
|
|
if (wcscmp(appid, L"windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel") == 0)
|
|
return false;
|
|
|
|
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);
|
|
}
|