mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-12 01:47:24 +10:00
685 lines
20 KiB
C++
685 lines
20 KiB
C++
// Classic Shell (c) 2009-2016, Ivo Beltchev
|
|
// 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);
|
|
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)
|
|
{
|
|
/* CString appid;
|
|
{
|
|
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
|
|
appid=pAppInfo->GetAppid();
|
|
}
|
|
LOG_MENU(LOG_OPEN,L"Execute Item: name=%s, appid=%s",item.name,appid);*/
|
|
CComQIPtr<IShellItem> pItem=item.pItem;
|
|
if (!pItem)
|
|
return false;
|
|
/* CComString pName;
|
|
if (FAILED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName)))
|
|
return false;
|
|
wchar_t ext[_MAX_EXT];
|
|
Strcpy(ext,_countof(ext),PathFindExtension(pName));
|
|
|
|
// find the correct association handler by appid and invoke it on the item
|
|
CComPtr<IEnumAssocHandlers> pEnumHandlers;
|
|
if (ext[0] && SUCCEEDED(SHAssocEnumHandlers(ext,ASSOC_FILTER_RECOMMENDED,&pEnumHandlers)))
|
|
{
|
|
CComPtr<IAssocHandler> pHandler;
|
|
ULONG count;
|
|
while (SUCCEEDED(pEnumHandlers->Next(1,&pHandler,&count)) && count==1)
|
|
{
|
|
CComQIPtr<IObjectWithAppUserModelID> pObject=pHandler;
|
|
if (pObject)
|
|
{
|
|
CComString pID;
|
|
if (SUCCEEDED(pObject->GetAppID(&pID)))
|
|
{
|
|
// found explicit appid
|
|
if (_wcsicmp(appid,pID)==0)
|
|
{
|
|
LOG_MENU(LOG_OPEN,L"Found handler appid");
|
|
CComPtr<IDataObject> pDataObject;
|
|
if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_DataObject,IID_IDataObject,(void**)&pDataObject)) && SUCCEEDED(pHandler->Invoke(pDataObject)))
|
|
return true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pHandler=NULL;
|
|
}
|
|
pEnumHandlers=NULL;
|
|
|
|
// find the correct association handler by exe name and invoke it on the item
|
|
wchar_t targetPath[_MAX_PATH];
|
|
targetPath[0]=0;
|
|
{
|
|
CComPtr<IShellItem> pItem;
|
|
SHCreateItemFromIDList(pAppInfo->GetPidl(),IID_IShellItem,(void**)&pItem);
|
|
CComPtr<IShellLink> pLink;
|
|
if (pItem)
|
|
pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IShellLink,(void**)&pLink);
|
|
CAbsolutePidl target;
|
|
if (pLink && SUCCEEDED(pLink->Resolve(NULL,SLR_INVOKE_MSI|SLR_NO_UI|SLR_NOUPDATE)) && SUCCEEDED(pLink->GetIDList(&target)))
|
|
{
|
|
if (FAILED(SHGetPathFromIDList(target,targetPath)))
|
|
targetPath[0]=0;
|
|
}
|
|
}
|
|
if (targetPath[0] && SUCCEEDED(SHAssocEnumHandlers(ext,ASSOC_FILTER_RECOMMENDED,&pEnumHandlers)))
|
|
{
|
|
while (SUCCEEDED(pEnumHandlers->Next(1,&pHandler,&count)) && count==1)
|
|
{
|
|
CComString pExe;
|
|
if (SUCCEEDED(pHandler->GetName(&pExe)))
|
|
{
|
|
if (_wcsicmp(targetPath,pExe)==0)
|
|
{
|
|
LOG_MENU(LOG_OPEN,L"Found handler appexe %s",targetPath);
|
|
CComPtr<IDataObject> pDataObject;
|
|
if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_DataObject,IID_IDataObject,(void**)&pDataObject)) && SUCCEEDED(pHandler->Invoke(pDataObject)))
|
|
return true;
|
|
break;
|
|
}
|
|
}
|
|
pHandler=NULL;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
// couldn't find a handler, execute the old way
|
|
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)
|
|
{
|
|
// invoke the link through its context menu
|
|
CComQIPtr<IContextMenu> pMenu=item.pItem;
|
|
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);
|
|
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);
|
|
}
|