Files
Open-Shell-Menu/Src/ClassicExplorer/ExplorerBand.cpp
Xenhat f4dd56155b Rebrand to Open-Shell (#36) (#58)
* Rebrand to Open-Shell

* Slight installer branding improvement
2018-08-05 15:22:10 -04:00

2286 lines
64 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
// ExplorerBand.cpp : Implementation of CExplorerBand
#include "stdafx.h"
#include "ExplorerBand.h"
#include "resource.h"
#include "ExplorerBHO.h"
#include "ResourceHelper.h"
#include "SettingsUI.h"
#include "Settings.h"
#include "SettingsParser.h"
#include "Translations.h"
#include "dllmain.h"
#include "SettingsUIHelper.h"
#include "FNVHash.h"
#include <shdeprecated.h>
#include <propkey.h>
#include <algorithm>
///////////////////////////////////////////////////////////////////////////////
// CBandWindow - the parent window of the toolbar
static struct
{
const wchar_t *name;
int id;
} g_StdCommands[]={
{L"up",CBandWindow::ID_GOUP},
{L"cut",CBandWindow::ID_CUT},
{L"copy",CBandWindow::ID_COPY},
{L"paste",CBandWindow::ID_PASTE},
{L"paste_shortcut",CBandWindow::ID_PASTE_SHORTCUT},
{L"delete",CBandWindow::ID_DELETE},
{L"properties",CBandWindow::ID_PROPERTIES},
{L"email",CBandWindow::ID_EMAIL},
{L"settings",CBandWindow::ID_SETTINGS},
{L"moveto",CBandWindow::ID_MOVETO},
{L"copyto",CBandWindow::ID_COPYTO},
{L"undo",CBandWindow::ID_UNDO},
{L"redo",CBandWindow::ID_REDO},
{L"selectall",CBandWindow::ID_SELECTALL},
{L"deselect",CBandWindow::ID_DESELECT},
{L"invertselection",CBandWindow::ID_INVERT},
{L"back",CBandWindow::ID_GOBACK},
{L"forward",CBandWindow::ID_GOFORWARD},
{L"refresh",CBandWindow::ID_REFRESH},
{L"stop",CBandWindow::ID_STOP},
{L"rename",CBandWindow::ID_RENAME},
{L"newfolder",CBandWindow::ID_NEWFOLDER},
{L"zipfolder",CBandWindow::ID_ZIPFOLDER},
{L"nav_pane",CBandWindow::ID_NAVPANE},
{L"details_pane",CBandWindow::ID_DETAILSPANE},
{L"preview_pane",CBandWindow::ID_PREVIEWPANE},
{L"mapdrive",CBandWindow::ID_MAP_DRIVE},
{L"disconnect",CBandWindow::ID_DISCONNECT},
{L"customizefolder",CBandWindow::ID_CUSTOMIZEFOLDER},
{L"folderoptions",CBandWindow::ID_FOLDEROPTIONS},
{L"viewtiles",CBandWindow::ID_VIEW_TILES},
{L"viewdetails",CBandWindow::ID_VIEW_DETAILS},
{L"viewlist",CBandWindow::ID_VIEW_LIST},
{L"viewcontent",CBandWindow::ID_VIEW_CONTENT},
{L"viewicons_small",CBandWindow::ID_VIEW_ICONS1},
{L"viewicons_medium",CBandWindow::ID_VIEW_ICONS2},
{L"viewicons_large",CBandWindow::ID_VIEW_ICONS3},
{L"viewicons_extralarge",CBandWindow::ID_VIEW_ICONS4},
{L"show_extensions",CBandWindow::ID_SHOW_EXTENSIONS},
{L"hidden_files",CBandWindow::ID_HIDDEN_FILES},
{L"system_files",CBandWindow::ID_SYSTEM_FILES},
};
static GUID SID_FrameManager={0x31e4fa78,0x02b4,0x419f,{0x94,0x30,0x7b,0x75,0x85,0x23,0x7c,0x77}};
static GUID SID_PerBrowserPropertyBag={0xa3b24a0a,0x7b68,0x448d,{0x99,0x79,0xc7,0x00,0x05,0x9c,0x3a,0xd1}};
const wchar_t *g_NavPaneVisible=L"PageSpaceControlSizer_Visible";
const wchar_t *g_DetailsPaneVisible=L"PreviewPaneSizer_Visible";
const wchar_t *g_DetailsPaneEnabled=L"PreviewPaneSizer_Loaded";
const wchar_t *g_PreviewPaneVisible=L"ReadingPaneSizer_Visible";
const wchar_t *g_PreviewPaneEnabled=L"ReadingPaneSizer_Loaded";
const wchar_t *g_ComboPaneEnabled=L"DetailsContainerSizer_Loaded";
typedef HRESULT (__stdcall *tBagWrite)( IPropertyBag *pThis, LPCOLESTR pszPropName, VARIANT *pVar );
static volatile tBagWrite g_OldBagWrite;
void CBandWindow::ParseToolbarItem( const wchar_t *name, StdToolbarItem &item )
{
wchar_t text[256];
Sprintf(text,_countof(text),L"%s.Command",name);
const wchar_t *str=m_Parser.FindSetting(text);
item.id=ID_CUSTOM;
if (str)
{
for (int i=0;i<_countof(g_StdCommands);i++)
if (_wcsicmp(str,g_StdCommands[i].name)==0)
{
item.id=g_StdCommands[i].id;
break;
}
}
if (item.id==ID_CUSTOM)
{
item.command=str;
item.regName=name;
}
Sprintf(text,_countof(text),L"%s.Link",name);
item.link=m_Parser.FindSetting(text);
if (item.link)
{
const wchar_t *c=wcschr(item.link,'|');
if (c)
{
for (c++;*c==' ';)
c++;
item.link=c;
}
}
Sprintf(text,_countof(text),L"%s.Icon",name);
item.iconPath=m_Parser.FindSetting(text);
Sprintf(text,_countof(text),L"%s.IconDisabled",name);
item.iconPathD=m_Parser.FindSetting(text);
Sprintf(text,_countof(text),L"%s.Label",name);
str=m_Parser.FindSetting(text);
if (str)
{
if (str[0]=='$')
item.label=FindTranslation(str+1,NULL);
else
item.label=str;
}
Sprintf(text,_countof(text),L"%s.Tip",name);
str=m_Parser.FindSetting(text);
if (str)
{
if (str[0]=='$')
item.tip=FindTranslation(str+1,NULL);
else
item.tip=str;
}
}
void CBandWindow::ParseToolbar( void )
{
m_Items.clear();
CString setting=GetSettingString(L"ToolbarItems");
m_Parser.LoadText(setting,setting.GetLength());
m_Parser.ParseText();
std::vector<CSettingsParser::TreeItem> items;
m_Parser.ParseTree(L"Items",items);
m_Items.resize(items.size());
for (int i=0;i<(int)items.size();i++)
{
const wchar_t *name=items[i].name;
StdToolbarItem &item=m_Items[i];
{
// can't use memset here because item is not a POD (there is a CString inside)
item.id=0;
item.tip=NULL;
item.label=NULL;
item.command=NULL;
item.link=NULL;
item.iconPath=NULL;
item.iconPathD=NULL;
item.submenu=NULL;
item.menuIcon=NULL;
item.menuIconD=NULL;
item.bIconLoaded=false;
item.bDisabled=false;
item.bChecked=false;
}
// handle special names
if (!*name)
{
item.id=ID_LAST;
continue;
}
if (_wcsicmp(name,L"SEPARATOR")==0)
{
item.id=ID_SEPARATOR;
continue;
}
ParseToolbarItem(name,item);
int idx=items[i].children;
if (idx>=0)
item.submenu=&m_Items[idx];
}
if (m_Items.size()==1)
{
m_Items.resize(2);
m_Items[1]=m_Items[0];
m_Items[0].id=ID_SETTINGS;
m_Items[0].tip=FindTranslation(L"$Toolbar.Settings",NULL);
m_Items[0].command=L"settings";
m_Items[0].iconPath=L",1";
}
}
LRESULT CALLBACK CBandWindow::ToolbarSubclassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
if (uMsg==WM_SETTINGCHANGE)
{
// HACK: Looks like WM_SETTINGCHANGE breaks the toolbar if it contains a split dropdown button (most likely something to do with metrics or themes)
// So we delete all buttons and post a message recreate them
int count=(int)SendMessage(hWnd,TB_BUTTONCOUNT,0,0);
CBandWindow *pThis=(CBandWindow*)uIdSubclass;
if (count>0)
{
for (int i=count-1;i>=0;i--)
{
TBBUTTON button;
SendMessage(hWnd,TB_GETBUTTON,i,(LPARAM)&button);
pThis->m_Buttons[i].fsState=button.fsState;
SendMessage(hWnd,TB_DELETEBUTTON,i,0);
}
::PostMessage((HWND)dwRefData,CBandWindow::BWM_UPDATEBUTTONS,0,0);
}
// Also refresh the buttons when the folder settings change
if (pThis->HasFolderSettings())
::PostMessage((HWND)dwRefData,CBandWindow::BWM_UPDATETOOLBAR,0,0);
}
if (uMsg==WM_PAINT)
{
CBandWindow *pThis=(CBandWindow*)uIdSubclass;
if (!pThis->m_pBrowserBag && pThis->HasPanes())
pThis->UpdateBag();
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
LRESULT CBandWindow::OnCreate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
ParseToolbar();
bool bLabels=false;
bool bList=GetSettingBool(L"ToolbarListMode");
int mainCount=0; // number of the main items
for (std::vector<StdToolbarItem>::const_iterator it=m_Items.begin();it->id!=ID_LAST;++it,mainCount++)
if (it->label)
bLabels=true;
// create the toolbar
if (bLabels && !bList)
m_Toolbar=CreateWindow(TOOLBARCLASSNAME,L"",WS_CHILD|TBSTYLE_TOOLTIPS|TBSTYLE_FLAT|CCS_NODIVIDER|CCS_NOPARENTALIGN|CCS_NORESIZE,0,0,10,10,m_hWnd,(HMENU)101,g_Instance,NULL);
else
m_Toolbar=CreateWindow(TOOLBARCLASSNAME,L"",WS_CHILD|TBSTYLE_TOOLTIPS|TBSTYLE_FLAT|TBSTYLE_LIST|CCS_NODIVIDER|CCS_NOPARENTALIGN|CCS_NORESIZE,0,0,10,10,m_hWnd,(HMENU)101,g_Instance,NULL);
m_Toolbar.SendMessage(TB_SETEXTENDEDSTYLE,0,TBSTYLE_EX_MIXEDBUTTONS|TBSTYLE_EX_DRAWDDARROWS|TBSTYLE_EX_HIDECLIPPEDBUTTONS);
m_Toolbar.SendMessage(TB_BUTTONSTRUCTSIZE,sizeof(TBBUTTON));
m_Toolbar.SendMessage(TB_SETMAXTEXTROWS,1);
SetWindowSubclass(m_Toolbar,ToolbarSubclassProc,(UINT_PTR)this,(DWORD_PTR)m_hWnd);
int iconSize=GetSettingInt(GetSettingBool(L"UseBigButtons")?L"LargeIconSize":L"SmallIconSize");
if (iconSize<8) iconSize=8;
else if (iconSize>128) iconSize=128;
m_MenuIconSize=GetSettingInt(L"MenuIconSize");
if (m_MenuIconSize<=0) m_MenuIconSize=0;
else if (m_MenuIconSize<8) m_MenuIconSize=8;
if (m_MenuIconSize>32) m_MenuIconSize=32;
m_ImgEnabled=ImageList_Create(iconSize,iconSize,ILC_COLOR32|ILC_MASK|(IsLanguageRTL()?ILC_MIRROR:0),0,mainCount);
m_ImgDisabled=ImageList_Create(iconSize,iconSize,ILC_COLOR32|ILC_MASK|(IsLanguageRTL()?ILC_MIRROR:0),0,mainCount);
HMODULE hShell32=GetModuleHandle(L"Shell32.dll");
std::vector<HMODULE> modules;
bool bSame=GetSettingBool(L"SameSizeButtons");
// create buttons
m_Buttons.resize(mainCount);
for (int i=0;i<mainCount;i++)
{
const StdToolbarItem &item=m_Items[i];
TBBUTTON &button=m_Buttons[i];
button.idCommand=i+1;
button.dwData=i;
if (item.id==ID_SEPARATOR)
button.fsStyle=BTNS_SEP;
else
{
button.iBitmap=I_IMAGENONE;
bool bNoIcon=false;
HICON hIcon=NULL;
HBITMAP hBitmap=NULL;
if (item.iconPath)
{
if (_wcsicmp(item.iconPath,L"NONE")==0)
bNoIcon=true;
else
hIcon=LoadIcon(iconSize,item.iconPath,modules);
}
else if (item.link)
{
CAbsolutePidl pidl;
wchar_t path[_MAX_PATH];
Strcpy(path,_countof(path),item.link);
DoEnvironmentSubst(path,_countof(path));
if (SUCCEEDED(ShParseDisplayName(path,&pidl,0,NULL)))
{
CComPtr<IShellItemImageFactory> pFactory;
if (SUCCEEDED(SHCreateItemFromIDList(pidl,IID_IShellItemImageFactory,(void**)&pFactory)) && pFactory)
{
SIZE size={iconSize,iconSize};
if (FAILED(pFactory->GetImage(size,SIIGBF_ICONONLY,&hBitmap)))
hBitmap=NULL;
}
}
}
if (!hIcon && !hBitmap && !bNoIcon)
hIcon=(HICON)LoadImage(hShell32,MAKEINTRESOURCE(1),IMAGE_ICON,iconSize,iconSize,LR_DEFAULTCOLOR);
if (hIcon)
{
button.iBitmap=ImageList_AddIcon(m_ImgEnabled,hIcon);
HICON hIcon2=item.iconPathD?LoadIcon(iconSize,item.iconPathD,modules):NULL;
if (!hIcon2)
hIcon2=CreateDisabledIcon(hIcon,iconSize);
int idx=ImageList_AddIcon(m_ImgDisabled,hIcon2);
Assert(button.iBitmap==idx);
DestroyIcon(hIcon);
DestroyIcon(hIcon2);
}
else if (hBitmap)
{
button.iBitmap=ImageList_AddMasked(m_ImgEnabled,hBitmap,CLR_NONE);
int idx=ImageList_AddMasked(m_ImgDisabled,hBitmap,CLR_NONE);
Assert(button.iBitmap==idx);
DeleteObject(hBitmap);
}
button.fsState=(item.id!=ID_SETTINGS || GetSettingBool(L"EnableSettings"))?TBSTATE_ENABLED:0;
button.fsStyle=BTNS_BUTTON|BTNS_NOPREFIX;
if (!bSame)
button.fsStyle|=BTNS_AUTOSIZE;
if (item.label)
{
button.fsStyle|=BTNS_SHOWTEXT;
button.iString=(INT_PTR)item.label;
}
bool bFolder=false;
if (item.link)
{
wchar_t path[_MAX_PATH];
Strcpy(path,_countof(path),item.link);
DoEnvironmentSubst(path,_countof(path));
CAbsolutePidl pidl;
SFGAOF flags=0;
if (SUCCEEDED(ShParseDisplayName(path,&pidl,SFGAO_FOLDER,&flags)))
{
if (flags&SFGAO_FOLDER)
bFolder=true;
}
}
if (item.submenu || bFolder)
button.fsStyle|=(item.id<ID_CUSTOM || item.command || (item.link && item.submenu))?BTNS_DROPDOWN:BTNS_WHOLEDROPDOWN;
}
}
for (std::vector<HMODULE>::const_iterator it=modules.begin();it!=modules.end();++it)
FreeLibrary(*it);
// add buttons
HIMAGELIST old=(HIMAGELIST)m_Toolbar.SendMessage(TB_SETIMAGELIST,0,(LPARAM)m_ImgEnabled);
if (old) ImageList_Destroy(old);
old=(HIMAGELIST)m_Toolbar.SendMessage(TB_SETDISABLEDIMAGELIST,0,(LPARAM)m_ImgDisabled);
if (old) ImageList_Destroy(old);
if (!m_Buttons.empty())
m_Toolbar.SendMessage(TB_ADDBUTTONS,(WPARAM)m_Buttons.size(),(LPARAM)&m_Buttons[0]);
SendMessage(WM_CLEAR);
return 0;
}
LRESULT CBandWindow::OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
if (m_ImgEnabled) ImageList_Destroy(m_ImgEnabled); m_ImgEnabled=NULL;
if (m_ImgDisabled) ImageList_Destroy(m_ImgDisabled); m_ImgDisabled=NULL;
for (std::vector<StdToolbarItem>::iterator it=m_Items.begin();it!=m_Items.end();++it)
{
if (it->menuIcon) DeleteObject(it->menuIcon);
if (it->menuIconD) DeleteObject(it->menuIconD);
}
m_Items.clear();
bHandled=FALSE;
return 0;
}
LRESULT CBandWindow::OnUpdateUI( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
// update the state of the custom buttons based on the registry settings
CRegKey regSettings;
if (regSettings.Open(HKEY_CURRENT_USER,GetSettingsRegPath())==ERROR_SUCCESS)
{
bool bMain=true;
for (int idx=0;idx<(int)m_Items.size();idx++)
{
if (m_Items[idx].id==ID_LAST)
bMain=false;
if (!m_Items[idx].regName.IsEmpty())
{
DWORD val;
if (regSettings.QueryDWORDValue(m_Items[idx].regName,val)!=ERROR_SUCCESS)
val=0;
bool bDisabled=(val&1)!=0;
bool bChecked=(val&2)!=0;
if (bMain)
{
if (bDisabled!=m_Items[idx].bDisabled) m_Toolbar.SendMessage(TB_ENABLEBUTTON,idx+1,bDisabled?0:1);
if (bChecked!=m_Items[idx].bChecked) m_Toolbar.SendMessage(TB_CHECKBUTTON,idx+1,bChecked?1:0);
}
m_Items[idx].bDisabled=bDisabled;
m_Items[idx].bChecked=bChecked;
}
}
}
return 0;
}
void CBandWindow::SendEmail( void )
{
const IID CLSID_SendMail={0x9E56BE60,0xC50F,0x11CF,{0x9A,0x2C,0x00,0xA0,0xC9,0x0A,0x90,0xCE}};
CComPtr<IShellView> pView;
if (FAILED(m_pBrowser->QueryActiveShellView(&pView))) return;
// check if there is anything selected
CComQIPtr<IFolderView> pView2=pView;
int count;
if (pView2 && SUCCEEDED(pView2->ItemCount(SVGIO_SELECTION,&count)) && count==0)
return;
// get the data object
CComPtr<IDataObject> pDataObj;
if (FAILED(pView->GetItemObject(SVGIO_SELECTION,IID_IDataObject,(void**)&pDataObj)))
return;
CComQIPtr<IDataObjectAsyncCapability> pAsync=pDataObj;
if (pAsync)
pAsync->SetAsyncMode(FALSE);
// drop into the SendMail handler
CComPtr<IDropTarget> pDropTarget;
if (SUCCEEDED(CoCreateInstance(CLSID_SendMail,NULL,CLSCTX_ALL,IID_IDropTarget,(void **)&pDropTarget)))
{
POINTL pt={0,0};
DWORD dwEffect=DROPEFFECT_COPY;
pDropTarget->DragEnter(pDataObj,MK_LBUTTON,pt,&dwEffect);
pDropTarget->Drop(pDataObj,0,pt,&dwEffect);
}
}
class CSendToZipHelper: public CComObjectRootEx<CComSingleThreadModel>, public IServiceProvider
{
public:
BEGIN_COM_MAP(CSendToZipHelper)
COM_INTERFACE_ENTRY_IID( IID_IServiceProvider, IServiceProvider )
END_COM_MAP()
// from IServiceProvider
virtual HRESULT STDMETHODCALLTYPE QueryService( REFGUID guidService, REFIID riid, void **ppvObject );
CComPtr<IFolderView> m_pFolderView;
};
HRESULT CSendToZipHelper::QueryService( REFGUID guidService, REFIID riid, void **ppvObject )
{
if (guidService==SID_SNewMenuClient)
{
return m_pFolderView->QueryInterface(riid,ppvObject);
}
return E_NOINTERFACE;
}
void CBandWindow::SendToZip( void )
{
const IID CLSID_SendToZip={0x888DCA60, 0xFC0A, 0x11CF, {0x8F, 0x0F, 0x00, 0xC0, 0x4F, 0xD7, 0xD0, 0x62}};
CComPtr<IShellView> pView;
if (FAILED(m_pBrowser->QueryActiveShellView(&pView))) return;
// check if there is anything selected
CComQIPtr<IFolderView> pView2=pView;
CComPtr<IShellFolder> pFolder;
if (FAILED(pView2->GetFolder(IID_IShellFolder,(void**)&pFolder)) || !pFolder) return;
int count;
if (pView2 && SUCCEEDED(pView2->ItemCount(SVGIO_SELECTION,&count)) && count==0)
return;
// get the data object
CComPtr<IDataObject> pDataObj;
if (FAILED(pView->GetItemObject(SVGIO_SELECTION,IID_IDataObject,(void**)&pDataObj)))
return;
CComQIPtr<IDataObjectAsyncCapability> pAsync=pDataObj;
if (pAsync)
pAsync->SetAsyncMode(FALSE);
// drop into the SendMail handler
CComPtr<IDropTarget> pDropTarget;
if (SUCCEEDED(CoCreateInstance(CLSID_SendToZip,NULL,CLSCTX_ALL,IID_IDropTarget,(void **)&pDropTarget)))
{
CComQIPtr<IObjectWithSite> pDropWithSite=pDropTarget;
if (pDropWithSite)
{
CComObject<CSendToZipHelper> *pHelper;
if (SUCCEEDED(CComObject<CSendToZipHelper>::CreateInstance(&pHelper)))
{
pHelper->m_pFolderView=pView2;
pDropWithSite->SetSite(pHelper);
}
else
delete pHelper;
}
POINTL pt={0,0};
DWORD dwEffect=DROPEFFECT_COPY;
pDropTarget->DragEnter(pDataObj,MK_LBUTTON,pt,&dwEffect);
pDropTarget->Drop(pDataObj,0,pt,&dwEffect);
}
}
static bool GetPidlPath( PIDLIST_ABSOLUTE pidl, wchar_t *path )
{
path[0]=0;
if (SHGetPathFromIDList(pidl,path) && *path)
return true;
if (GetWinVersion()>=WIN_VER_WIN7)
{
// maybe it is a library - try the default save folder
CComPtr<IShellItem> pShellItem;
if (SUCCEEDED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pShellItem)))
{
CComPtr<IShellLibrary> pLibrary;
if (SUCCEEDED(pLibrary.CoCreateInstance(CLSID_ShellLibrary)) && SUCCEEDED(pLibrary->LoadLibraryFromItem(pShellItem,STGM_READ)))
{
pShellItem=NULL;
if (SUCCEEDED(pLibrary->GetDefaultSaveFolder(DSFT_DETECT,IID_IShellItem,(void**)&pShellItem)) && pShellItem)
{
CComString pPath;
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH,&pPath)))
{
Strcpy(path,_MAX_PATH,pPath);
return true;
}
}
}
}
}
return false;
}
void CBandWindow::NewFolder( void )
{
CComPtr<IShellView> pView;
if (FAILED(m_pBrowser->QueryActiveShellView(&pView))) return;
CComQIPtr<IFolderView> pView2=pView;
if (!pView2) return;
{
// check if this is a filesystem folder (InvokeCommand may crash for non-folders)
CComPtr<IPersistFolder2> pFolder;
CAbsolutePidl pidl;
if (FAILED(pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder)) || FAILED(pFolder->GetCurFolder(&pidl)))
return;
wchar_t path[_MAX_PATH];
bool bFolder=GetPidlPath(pidl,path); // it is a folder if it has a path
if (!bFolder)
return;
}
CComPtr<IShellFolder> pFolder;
if (FAILED(pView2->GetFolder(IID_IShellFolder,(void**)&pFolder)) || !pFolder) return;
std::vector<unsigned int> items;
{
// remember the old folders
CComPtr<IEnumIDList> pEnum;
if (pFolder->EnumObjects(NULL,SHCONTF_FOLDERS,&pEnum)!=S_OK) pEnum=NULL;
PITEMID_CHILD child;
while (pEnum && pEnum->Next(1,&child,NULL)==S_OK)
{
STRRET str;
if (SUCCEEDED(pFolder->GetDisplayNameOf(child,SHGDN_INFOLDER|SHGDN_FORPARSING,&str)))
{
CComString pName;
StrRetToStr(&str,child,&pName);
items.push_back(CalcFNVHash(pName));
}
ILFree(child);
}
}
CComPtr<IContextMenu> pMenu;
HMENU menu=CreatePopupMenu();
bool bRename=false;
if (SUCCEEDED(pFolder->CreateViewObject(m_hWnd,IID_IContextMenu,(void**)&pMenu)))
{
if (SUCCEEDED(pMenu->QueryContextMenu(menu,0,1,32767,CMF_NORMAL)))
{
CMINVOKECOMMANDINFOEX info={sizeof(info),CMIC_MASK_UNICODE};
info.lpVerb="NewFolder";
info.lpVerbW=L"NewFolder";
info.nShow=SW_SHOWNORMAL;
info.fMask|=CMIC_MASK_NOASYNC;
info.hwnd=GetAncestor(m_hWnd,GA_ROOT);
// Note: InvokeCommand crashes if the item is "Computer". I don't know if this is a bug in Explorer
// or I am not supposed to give unsupported verbs to InvokeCommand. Unfortunately there is no way to
// check if "NewFolder" is a supported verb. It is not present in the menu no matter what I give to
// QueryContextMenu. So we verify if this is a filesystem folder, cross fingers and hope for the best
if (SUCCEEDED(pMenu->InvokeCommand((CMINVOKECOMMANDINFO*)&info)))
bRename=true;
}
}
DestroyMenu(menu);
if (bRename)
{
// look for a new folder and rename it
CComPtr<IEnumIDList> pEnum;
if (pFolder->EnumObjects(NULL,SHCONTF_FOLDERS,&pEnum)!=S_OK) pEnum=NULL;
PITEMID_CHILD child;
while (pEnum && pEnum->Next(1,&child,NULL)==S_OK)
{
STRRET str;
if (SUCCEEDED(pFolder->GetDisplayNameOf(child,SHGDN_INFOLDER|SHGDN_FORPARSING,&str)))
{
CComString pName;
StrRetToStr(&str,child,&pName);
unsigned int hash=CalcFNVHash(pName);
if (std::find(items.begin(),items.end(),hash)==items.end())
{
// found the new folder
pView->SelectItem(child,SVSI_SELECT|SVSI_EDIT|SVSI_DESELECTOTHERS|SVSI_ENSUREVISIBLE|SVSI_FOCUSED);
break;
}
}
ILFree(child);
}
}
}
void CBandWindow::ExecuteCommandFile( const wchar_t *pText )
{
wchar_t command[256];
pText=GetToken(pText,command,_countof(command),L" \t\r\n");
if (_wcsicmp(command,L"open")==0)
{
// navigate to the given folder
wchar_t path[_MAX_PATH];
GetToken(pText,path,_countof(path),L" \t\r\n");
CAbsolutePidl pidl;
if (m_pBrowser && SUCCEEDED(ShParseDisplayName(path,&pidl,0,NULL)))
{
UINT flags=(GetKeyState(VK_CONTROL)<0?SBSP_NEWBROWSER:SBSP_SAMEBROWSER);
m_pBrowser->BrowseObject(pidl,flags|SBSP_ABSOLUTE);
}
}
else if (_wcsicmp(command,L"refresh")==0)
{
// refresh Explorer
SendShellTabCommand(41504);
}
else if (_wcsicmp(command,L"select")==0)
{
// select the given files, deselect all others
std::vector<unsigned int> selected;
wchar_t path[_MAX_PATH];
while (*pText)
{
pText=GetToken(pText,path,_countof(path),L"\t\r\n");
wchar_t *fname=PathFindFileName(path);
CharUpper(fname);
// trim leading spaces
while (*fname==' ')
fname++;
// trim trailing spaces
wchar_t *end=fname+Strlen(fname);
while (end>fname && end[-1]==' ')
end--;
*end=0;
if (*fname)
selected.push_back(CalcFNVHash(fname));
}
CComPtr<IShellView> pView;
if (SUCCEEDED(m_pBrowser->QueryActiveShellView(&pView)))
{
CComQIPtr<IFolderView> pView2=pView;
if (!pView2) return;
CComPtr<IShellFolder> pFolder;
if (FAILED(pView2->GetFolder(IID_IShellFolder,(void**)&pFolder)) || !pFolder) return;
CComPtr<IEnumIDList> pEnum;
if (SUCCEEDED(pView2->Items(SVGIO_ALLVIEW,IID_IEnumIDList,(void**)&pEnum)) && pEnum)
{
PITEMID_CHILD child;
bool bFirst=true;
while (pEnum->Next(1,&child,NULL)==S_OK)
{
STRRET str;
if (SUCCEEDED(pFolder->GetDisplayNameOf(child,SHGDN_FORPARSING|SHGDN_INFOLDER,&str)))
{
CComString pName;
StrRetToStr(&str,child,&pName);
pName.MakeUpper();
unsigned int hash=CalcFNVHash(pName);
UINT flags=SVSI_DESELECT;
if (std::find(selected.begin(),selected.end(),hash)!=selected.end())
{
// the file is in the list
flags=SVSI_SELECT;
if (bFirst)
{
flags|=SVSI_ENSUREVISIBLE|SVSI_FOCUSED;
bFirst=false;
}
}
pView->SelectItem(child,flags);
}
ILFree(child);
}
}
}
}
}
void CBandWindow::SendShellTabCommand( int command )
{
// sends a command to the ShellTabWindowClass window
for (CWindow parent=GetParent();parent.m_hWnd;parent=parent.GetParent())
{
// find a parent window with class ShellTabWindowClass
wchar_t name[256];
GetClassName(parent.m_hWnd,name,_countof(name));
if (_wcsicmp(name,L"ShellTabWindowClass")==0)
{
parent.SendMessage(WM_COMMAND,command);
break;
}
}
}
void CBandWindow::ExecuteCustomCommand( const wchar_t *pCommand )
{
wchar_t buf[2048];
Strcpy(buf,_countof(buf),pCommand);
// expand environment variables
DoEnvironmentSubst(buf,_countof(buf));
wchar_t *pBuf=buf;
bool bArg1=wcsstr(buf,L"%1")!=NULL;
bool bArg2=wcsstr(buf,L"%2")!=NULL;
bool bArg3=wcsstr(buf,L"%3")!=NULL;
bool bArg4=wcsstr(buf,L"%4")!=NULL;
bool bArg5=wcsstr(buf,L"%5")!=NULL;
wchar_t path[_MAX_PATH];
wchar_t file[_MAX_PATH];
wchar_t input[_MAX_PATH];
wchar_t output[_MAX_PATH];
wchar_t temp[_MAX_PATH];
path[0]=file[0]=input[0]=output[0]=temp[0]=0;
CComPtr<IShellView> pView;
if (SUCCEEDED(m_pBrowser->QueryActiveShellView(&pView)))
{
CComPtr<IPersistFolder2> pFolder;
CAbsolutePidl pidl;
CComQIPtr<IFolderView> pView2=pView;
if (pView2 && SUCCEEDED(pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder)) && SUCCEEDED(pFolder->GetCurFolder(&pidl)))
{
// get current path
GetPidlPath(pidl,path);
if (bArg2)
{
CComPtr<IEnumIDList> pEnum;
int count;
// if only one file is selected get the file name (%2)
if (SUCCEEDED(pView2->ItemCount(SVGIO_SELECTION,&count)) && count==1 && SUCCEEDED(pView2->Items(SVGIO_SELECTION,IID_IEnumIDList,(void**)&pEnum)) && pEnum)
{
PITEMID_CHILD child;
if (pEnum->Next(1,&child,NULL)==S_OK)
{
CAbsolutePidl full;
full.Attach(ILCombine(pidl,child));
SHGetPathFromIDList(full,file);
ILFree(child);
}
}
}
if (bArg3 || bArg4)
{
GetTempPath(_countof(temp),temp);
if (GetTempFileName(temp,L"cei",0,input))
{
// create a text file with the selected files
FILE *f;
if (_wfopen_s(&f,input,L"wb")==0 && f)
{
CComPtr<IEnumIDList> pEnum;
if (SUCCEEDED(pView2->Items(SVGIO_SELECTION,IID_IEnumIDList,(void**)&pEnum)) && pEnum)
{
PITEMID_CHILD child;
while (pEnum->Next(1,&child,NULL)==S_OK)
{
wchar_t fname[_MAX_PATH];
CAbsolutePidl full;
full.Attach(ILCombine(pidl,child));
SHGetPathFromIDList(full,fname);
ILFree(child);
if (bArg3)
{
char fnameA[_MAX_PATH];
WcsToMbs(fnameA,_countof(fnameA),fname);
fprintf_s(f,"%s\r\n",fnameA);
}
else
{
fwprintf_s(f,L"%s\r\n",fname);
}
}
}
fclose(f);
}
else
{
input[0]=0;
bArg3=false;
bArg4=false;
}
}
else
{
input[0]=0;
bArg3=false;
bArg4=false;
}
}
}
if (bArg5)
{
if (!temp[0]) GetTempPath(_countof(temp),temp);
if (GetTempFileName(temp,L"ceo",0,output))
{
FILE *f;
if (_wfopen_s(&f,output,L"wb")==0 && f)
{
fclose(f);
}
else
{
output[0]=0;
bArg5=false;
}
}
else
{
output[0]=0;
bArg5=false;
}
}
if (bArg1 || bArg2 || bArg3 || bArg4 || bArg5)
{
// expand %1, %2, %3, %4, %5
DWORD_PTR args[100]={(DWORD_PTR)path,(DWORD_PTR)file,(DWORD_PTR)input,(DWORD_PTR)input,(DWORD_PTR)output};
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_ARGUMENT_ARRAY|FORMAT_MESSAGE_FROM_STRING,buf,0,0,(LPWSTR)&pBuf,0,(va_list*)args);
}
wchar_t exe[_MAX_PATH];
const wchar_t *params=SeparateArguments(pBuf,exe);
if (_wcsicmp(exe,L"open")==0)
{
CAbsolutePidl pidl;
if (m_pBrowser && SUCCEEDED(ShParseDisplayName((LPWSTR)params,&pidl,0,NULL)))
{
UINT flags=(GetKeyState(VK_CONTROL)<0?SBSP_NEWBROWSER:SBSP_SAMEBROWSER);
m_pBrowser->BrowseObject(pidl,flags|SBSP_ABSOLUTE);
}
}
else if (_wcsicmp(exe,L"sortby")==0)
{
CComQIPtr<IFolderView2> pView2=pView;
if (pView2)
ViewByProperty(pView2,params,false);
}
else if (_wcsicmp(exe,L"groupby")==0)
{
CComQIPtr<IFolderView2> pView2=pView;
if (pView2)
ViewByProperty(pView2,params,true);
}
else if (bArg3 || bArg4 || bArg5)
{
// create a process instead of using ShellExecute
STARTUPINFO startupInfo={sizeof(startupInfo)};
PROCESS_INFORMATION processInfo;
memset(&processInfo,0,sizeof(processInfo));
DWORD flags=CREATE_NO_WINDOW;
if (CreateProcess(exe,pBuf,NULL,NULL,TRUE,flags,NULL,*path?path:NULL,&startupInfo,&processInfo))
{
CloseHandle(processInfo.hThread);
if (bArg5)
{
// wait for the process to finish so we can parse the output file
WaitForSingleObject(processInfo.hProcess,INFINITE);
}
CloseHandle(processInfo.hProcess);
}
if (bArg5)
{
// process output
FILE *f;
if (_wfopen_s(&f,output,L"rb")==0 && f)
{
// load output file
fseek(f,0,SEEK_END);
int size=ftell(f);
fseek(f,0,SEEK_SET);
std::vector<unsigned char> text(size+2);
if (fread(&text[0],1,size,f)!=size)
text.clear();
fclose(f);
if (!text.empty())
{
text[size]=0;
text[size+1]=0;
std::vector<wchar_t> textW;
if (size>=2 && text[0]==0xFF && text[1]==0xFE)
ExecuteCommandFile((wchar_t*)&text[2]);
else
{
int len=MbsToWcs(NULL,0,(char*)&text[0]);
textW.resize(len+1);
MbsToWcs(&textW[0],len+1,(char*)&text[0]);
ExecuteCommandFile(&textW[0]);
}
}
}
DeleteFile(output);
}
}
else
{
// simply execute
ShellExecute(NULL,NULL,exe,params,path,SW_SHOWNORMAL);
}
if (pBuf!=buf)
LocalFree(pBuf);
}
}
void CBandWindow::ViewByProperty( IFolderView2 *pView, const wchar_t *pProperty, bool bGroup )
{
SORTCOLUMN column={0};
column.direction=SORT_ASCENDING;
if (pProperty)
{
if (pProperty[0]=='-')
{
column.direction=SORT_DESCENDING;
pProperty++;
}
if (_wcsicmp(pProperty,L"name")==0)
column.propkey=PKEY_ItemNameDisplay;
else if (_wcsicmp(pProperty,L"type")==0)
column.propkey=PKEY_ItemTypeText;
else if (_wcsicmp(pProperty,L"size")==0)
column.propkey=PKEY_Size;
else if (_wcsicmp(pProperty,L"date")==0)
column.propkey=PKEY_DateModified;
else
{
wchar_t token[256];
pProperty=GetToken(pProperty,token,_countof(token),L" ,");
if (IIDFromString(token,&column.propkey.fmtid)!=S_OK)
return;
pProperty=GetToken(pProperty,token,_countof(token),L" ,");
column.propkey.pid=_wtol(token);
}
}
if (bGroup)
{
PROPERTYKEY prop;
BOOL ascending;
if (pProperty && SUCCEEDED(pView->GetGroupBy(&prop,&ascending)))
{
if (prop==column.propkey && ascending)
column.direction=SORT_DESCENDING;
}
pView->SetGroupBy(column.propkey,column.direction==SORT_ASCENDING);
}
else
{
int count;
if (pProperty && SUCCEEDED(pView->GetSortColumnCount(&count)) && count>0)
{
std::vector<SORTCOLUMN> columns(count);
if (SUCCEEDED(pView->GetSortColumns(&columns[0],count)))
{
if (columns[0].propkey==column.propkey)
column.direction=-columns[0].direction;
}
}
pView->SetSortColumns(&column,1);
}
}
LRESULT CBandWindow::OnUpdateButtons( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
if (!m_Buttons.empty() && m_Toolbar.SendMessage(TB_BUTTONCOUNT)==0)
m_Toolbar.SendMessage(TB_ADDBUTTONS,(WPARAM)m_Buttons.size(),(LPARAM)&m_Buttons[0]);
return 0;
}
LRESULT CBandWindow::OnUpdateToolbar( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
UpdateToolbar();
return 0;
}
static BOOL CALLBACK RefreshExplorerWindows( HWND hwnd, LPARAM lParam )
{
wchar_t className[256];
if (GetClassName(hwnd,className,_countof(className)))
{
if (_wcsicmp(className,L"CabinetWClass")==0 || _wcsicmp(className,L"Progman")==0)
PostMessage(hwnd,WM_COMMAND,41504,0); // post refresh command
}
return TRUE;
}
static bool ToggleExplorerSetting( const wchar_t *setting, DWORD off, DWORD on )
{
CRegKey regKey;
if (regKey.Open(HKEY_CURRENT_USER,L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",KEY_READ|KEY_WRITE|KEY_WOW64_64KEY)==ERROR_SUCCESS)
{
DWORD val=off;
if (regKey.QueryDWORDValue(setting,val)!=ERROR_SUCCESS)
val=off;
regKey.SetDWORDValue(setting,val==on?off:on);
EnumWindows(RefreshExplorerWindows,0);
return val==off;
}
return false;
}
// Executes a cut/copy/paste/delete command
LRESULT CBandWindow::OnCommand( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
int idx=LOWORD(wParam)-1;
int id=m_Items[idx].id;
if (id>=ID_CUSTOM)
{
if (m_Items[idx].command)
{
ExecuteCustomCommand(m_Items[idx].command);
}
else if (m_Items[idx].link)
{
wchar_t path[_MAX_PATH];
Strcpy(path,_countof(path),m_Items[idx].link);
DoEnvironmentSubst(path,_countof(path));
CAbsolutePidl pidl;
if (m_pBrowser && SUCCEEDED(ShParseDisplayName(path,&pidl,0,NULL)))
{
UINT flags=(GetKeyState(VK_CONTROL)<0?SBSP_NEWBROWSER:SBSP_SAMEBROWSER);
m_pBrowser->BrowseObject(pidl,flags|SBSP_ABSOLUTE);
}
}
return TRUE;
}
if (id==ID_SETTINGS)
{
#ifndef BUILD_SETUP
if (GetKeyState(VK_SHIFT)<0)
*(int*)0=0; // force a crash if Shift is pressed. Makes it easy to restart explorer.exe
#endif
EditSettings();
return TRUE;
}
if (id==ID_GOUP || id==ID_GOBACK || id==ID_GOFORWARD)
{
if (m_pBrowser)
{
UINT flags=(GetKeyState(VK_CONTROL)<0?SBSP_NEWBROWSER:SBSP_SAMEBROWSER);
if (id==ID_GOUP)
m_pBrowser->BrowseObject(NULL,flags|SBSP_PARENT);
if (id==ID_GOBACK)
m_pBrowser->BrowseObject(NULL,flags|SBSP_NAVIGATEBACK);
if (id==ID_GOFORWARD)
m_pBrowser->BrowseObject(NULL,flags|SBSP_NAVIGATEFORWARD);
}
return TRUE;
}
if (id==ID_EMAIL)
{
SendEmail();
return TRUE;
}
if (id==ID_RENAME)
{
CComPtr<IShellView> pView;
if (SUCCEEDED(m_pBrowser->QueryActiveShellView(&pView)))
{
CComQIPtr<IFolderView2> pView2=pView;
if (pView2) pView2->DoRename();
}
return TRUE;
}
if (id==ID_NEWFOLDER)
{
NewFolder();
return TRUE;
}
if (id==ID_ZIPFOLDER)
{
SendToZip();
return TRUE;
}
if (id==ID_NAVPANE)
{
if (m_pBrowserBag)
{
VARIANT val={VT_EMPTY};
bool bNavPane=SUCCEEDED(m_pBrowserBag->Read(g_NavPaneVisible,&val,NULL)) && val.vt==VT_BOOL && val.boolVal;
VariantClear(&val);
val.vt=VT_BOOL;
val.boolVal=bNavPane?VARIANT_FALSE:VARIANT_TRUE;
m_pBrowserBag->Write(g_NavPaneVisible,&val);
}
return TRUE;
}
if (id==ID_DETAILSPANE)
{
if (m_pBrowserBag)
{
VARIANT val={VT_EMPTY};
bool bNavPane=SUCCEEDED(m_pBrowserBag->Read(g_DetailsPaneVisible,&val,NULL)) && val.vt==VT_BOOL && val.boolVal;
VariantClear(&val);
val.vt=VT_BOOL;
val.boolVal=bNavPane?VARIANT_FALSE:VARIANT_TRUE;
m_pBrowserBag->Write(g_DetailsPaneVisible,&val);
}
return TRUE;
}
if (id==ID_PREVIEWPANE)
{
if (m_pBrowserBag)
{
VARIANT val={VT_EMPTY};
bool bNavPane=SUCCEEDED(m_pBrowserBag->Read(g_PreviewPaneVisible,&val,NULL)) && val.vt==VT_BOOL && val.boolVal;
VariantClear(&val);
val.vt=VT_BOOL;
val.boolVal=bNavPane?VARIANT_FALSE:VARIANT_TRUE;
m_pBrowserBag->Write(g_PreviewPaneVisible,&val);
}
return TRUE;
}
if (id==ID_SHOW_EXTENSIONS)
{
CheckButton(ID_SHOW_EXTENSIONS,ToggleExplorerSetting(L"HideFileExt",1,0));
return TRUE;
}
if (id==ID_HIDDEN_FILES)
{
CheckButton(ID_HIDDEN_FILES,ToggleExplorerSetting(L"Hidden",2,1));
return TRUE;
}
if (id==ID_SYSTEM_FILES)
{
CheckButton(ID_SYSTEM_FILES,ToggleExplorerSetting(L"ShowSuperHidden",0,1));
return TRUE;
}
// check if the focus is on the tree side or on the list side
CWindow focus=GetFocus();
wchar_t name[256];
GetClassName(focus,name,_countof(name));
CWindow parent=focus.GetParent();
if (_wcsicmp(name,WC_TREEVIEW)==0)
{
// send these commands to the parent of the tree view
if (id==ID_CUT)
parent.SendMessage(WM_COMMAND,41025);
if (id==ID_COPY)
parent.SendMessage(WM_COMMAND,41026);
if (id==ID_PASTE)
parent.SendMessage(WM_COMMAND,41027);
if (id==ID_DELETE)
parent.SendMessage(WM_COMMAND,40995);
if (id==ID_PROPERTIES)
ShowTreeProperties(focus.m_hWnd);
}
else
{
GetClassName(parent,name,_countof(name));
if (_wcsicmp(name,L"SHELLDLL_DefView")==0)
{
// send these commands to the SHELLDLL_DefView window
if (id==ID_CUT)
{
parent.SendMessage(WM_COMMAND,28696);
focus.InvalidateRect(NULL);
}
if (id==ID_COPY)
parent.SendMessage(WM_COMMAND,28697);
if (id==ID_PASTE)
parent.SendMessage(WM_COMMAND,28698);
if (id==ID_DELETE)
parent.SendMessage(WM_COMMAND,28689);
if (id==ID_PROPERTIES)
parent.SendMessage(WM_COMMAND,28691);
if (id==ID_COPYTO)
parent.SendMessage(WM_COMMAND,28702);
if (id==ID_MOVETO)
parent.SendMessage(WM_COMMAND,28703);
}
}
if (id==ID_UNDO)
SendShellTabCommand(28699);
if (id==ID_REDO)
SendShellTabCommand(28704);
if (id==ID_PASTE_SHORTCUT)
parent.SendMessage(WM_COMMAND,28700);
if (id==ID_MAP_DRIVE)
SendShellTabCommand(41089);
if (id==ID_DISCONNECT)
SendShellTabCommand(41090);
if (id==ID_CUSTOMIZEFOLDER)
SendShellTabCommand(28722);
if (id==ID_FOLDEROPTIONS)
SendShellTabCommand(41251);
if (id==ID_VIEW_TILES)
SendShellTabCommand(28748);
if (id==ID_VIEW_DETAILS)
SendShellTabCommand(28747);
if (id==ID_VIEW_LIST)
SendShellTabCommand(28753);
if (id==ID_VIEW_CONTENT)
SendShellTabCommand(28754);
if (id==ID_VIEW_ICONS1)
SendShellTabCommand(28752);
if (id==ID_VIEW_ICONS2)
SendShellTabCommand(28750);
if (id==ID_VIEW_ICONS3)
SendShellTabCommand(28751);
if (id==ID_VIEW_ICONS4)
SendShellTabCommand(28749);
if (id==ID_SELECTALL || id==ID_INVERT || id==ID_DESELECT)
{
// handle selection commands the hard way (instead of sending commands with SendShellTabCommand).
// some folders don't support selection and they crash if they get selection commands
CComPtr<IShellView> pView;
if (FAILED(m_pBrowser->QueryActiveShellView(&pView)))
return TRUE;
CComQIPtr<IFolderView2> pView2=pView;
if (!pView2) return TRUE;
// ID_DESELECT
if (id==ID_DESELECT)
{
pView2->SelectItem(-1,SVSI_DESELECTOTHERS);
return TRUE;
}
int count;
if (FAILED(pView2->ItemCount(SVGIO_ALLVIEW,&count)))
return TRUE;
// ID_SELECTALL
if (id==ID_SELECTALL)
{
for (int i=0;i<count;i++)
pView2->SelectItem(i,SVSI_SELECT);
return TRUE;
}
// ID_INVERT
// we can't use IFolderView2::GetSelectedItem to enumerate the selected items. there is a bug in it on Windows 7. when called
// with 0 or 1 it returns 0 for the next item, causing an infinite loop. we have to use Item + GetSelectionState instead.
// it allocates a PIDLs, so it is not ideal, but what can you do. stupid bugs
for (int i=0;i<count;i++)
{
PITEMID_CHILD child;
if (SUCCEEDED(pView2->Item(i,&child)) && child)
{
DWORD state;
if (SUCCEEDED(pView2->GetSelectionState(child,&state)))
pView2->SelectItem(i,(state&SVSI_SELECT)?SVSI_DESELECT:SVSI_SELECT);
ILFree(child);
}
}
return TRUE;
}
if (id==ID_REFRESH)
SendShellTabCommand(41504);
if (id==ID_STOP && m_pWebBrowser)
m_pWebBrowser->Stop();
return TRUE;
}
LRESULT CBandWindow::OnRClick( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
{
NMMOUSE *pInfo=(NMMOUSE*)pnmh;
POINT pt=pInfo->pt;
{
RECT rc;
int count=(int)m_Toolbar.SendMessage(TB_BUTTONCOUNT);
m_Toolbar.SendMessage(TB_GETITEMRECT,count-1,(LPARAM)&rc);
if (pt.x>rc.right)
return 0;
}
m_Toolbar.ClientToScreen(&pt);
ShowSettingsMenu(m_hWnd,pt.x,pt.y);
return 1;
}
LRESULT CBandWindow::OnGetInfoTip( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
{
NMTBGETINFOTIP *pTip=(NMTBGETINFOTIP*)pnmh;
const StdToolbarItem &item=m_Items[pTip->lParam];
if (item.tip && _wcsicmp(item.tip,L"none")!=0)
{
// show the tip for the standard item
Strcpy(pTip->pszText,pTip->cchTextMax,item.tip);
}
return 0;
}
// Callback for IShellMenu. Executes the selected command
class CMenuCallback: public IShellMenuCallback
{
public:
CMenuCallback( IShellBrowser *pBrowser ) { m_bExecuted=false; m_pBrowser=pBrowser; }
virtual HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, void **ppvObject );
virtual ULONG STDMETHODCALLTYPE AddRef( void ) { return 1; }
virtual ULONG STDMETHODCALLTYPE Release( void ) { return 1; }
STDMETHOD(CallbackSM)( LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam );
private:
bool m_bExecuted;
IShellBrowser *m_pBrowser;
};
HRESULT STDMETHODCALLTYPE CMenuCallback::QueryInterface( REFIID riid, void **ppvObject )
{
if (riid==IID_IUnknown || riid==IID_IShellMenuCallback)
{
*ppvObject=this;
return S_OK;
}
else
{
*ppvObject=NULL;
return E_NOINTERFACE;
}
}
HRESULT STDMETHODCALLTYPE CMenuCallback::CallbackSM( LPSMDATA psmd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch(uMsg)
{
case SMC_GETINFO:
{
SMINFO *pSmInfo=(SMINFO*)lParam;
if (pSmInfo->dwMask&SMIM_FLAGS)
pSmInfo->dwFlags|=SMIF_DROPCASCADE|SMIF_TRACKPOPUP;
if (pSmInfo->dwMask&SMIM_ICON)
pSmInfo->iIcon=-1;
}
return S_OK;
case SMC_SFEXEC:
{
if (m_bExecuted)
return S_OK;
m_bExecuted=true;
SFGAOF flags=SFGAO_FOLDER|SFGAO_LINK;
if (SUCCEEDED(psmd->psf->GetAttributesOf(1,&psmd->pidlItem,&flags)))
{
CAbsolutePidl pidl;
if (flags&SFGAO_LINK)
{
flags=0;
// resolve link
CComPtr<IShellLink> pLink;
if (SUCCEEDED(psmd->psf->GetUIObjectOf(NULL,1,&psmd->pidlItem,IID_IShellLink,NULL,(void**)&pLink)) && pLink)
pLink->GetIDList(&pidl);
if (pidl)
{
CComPtr<IShellFolder> pFolder;
PCUITEMID_CHILD child;
SHBindToParent(pidl,IID_IShellFolder,(void**)&pFolder,&child);
SFGAOF flags2=SFGAO_FOLDER;
if (pFolder && SUCCEEDED(pFolder->GetAttributesOf(1,&child,&flags2)) && (flags2&SFGAO_FOLDER))
flags=SFGAO_FOLDER;
else
pidl.Clear();
}
}
if (!pidl)
pidl.Attach(ILCombine(psmd->pidlFolder,psmd->pidlItem));
if (flags&SFGAO_FOLDER)
{
// navigate to folder
if (m_pBrowser)
{
UINT flags=(GetKeyState(VK_CONTROL)<0?SBSP_NEWBROWSER:SBSP_SAMEBROWSER);
m_pBrowser->BrowseObject(pidl,flags);
}
}
else
{
// execute file
SHELLEXECUTEINFO execute={sizeof(execute),SEE_MASK_IDLIST|SEE_MASK_FLAG_LOG_USAGE};
execute.lpIDList=pidl;
execute.nShow=SW_SHOWNORMAL;
ShellExecuteEx(&execute);
}
}
return S_OK;
}
case SMC_PROMOTE:
case SMC_EXITMENU:
case SMC_GETSFINFO:
case SMC_SFSELECTITEM:
case SMC_REFRESH:
case SMC_DEMOTE:
case SMC_DEFAULTICON:
case SMC_NEWITEM:
case SMC_CHEVRONEXPAND:
case SMC_DISPLAYCHEVRONTIP:
case SMC_SETSFOBJECT:
case SMC_SHCHANGENOTIFY:
case SMC_CHEVRONGETTIP:
case SMC_SFDDRESTRICTED:
return S_OK;
default:
return S_FALSE;
}
}
LRESULT CBandWindow::OnDropDown( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
{
NMTOOLBAR *pButton=(NMTOOLBAR*)pnmh;
int idx=0;
const StdToolbarItem *pItem=NULL;
for (std::vector<StdToolbarItem>::const_iterator it=m_Items.begin();it->id!=ID_LAST;++it,idx++)
{
RECT rc;
if (m_Toolbar.SendMessage(TB_GETITEMRECT,idx,(LPARAM)&rc) && memcmp(&rc,&pButton->rcButton,sizeof(RECT))==0)
{
pItem=&*it;
break;
}
}
if (pItem && (pItem->submenu || pItem->link))
{
if (pItem->submenu)
{
TPMPARAMS params={sizeof(params),pButton->rcButton};
m_Toolbar.ClientToScreen(&params.rcExclude); // must not use MapWindowPoints because it produces wrong results in RTL cases
HMENU menu=CreateDropMenu(pItem->submenu);
int res=TrackPopupMenuEx(menu,TPM_RETURNCMD|TPM_VERTICAL,params.rcExclude.left,params.rcExclude.bottom,m_hWnd,&params);
DestroyMenu(menu);
if (res>0)
PostMessage(WM_COMMAND,res);
}
else if (pItem->link)
{
TPMPARAMS params={sizeof(params),pButton->rcButton};
m_Toolbar.MapWindowPoints(NULL,&params.rcExclude); // must use MapWindowPoints to handle RTL correctly
CAbsolutePidl pidl;
CComPtr<IShellFolder> pFolder;
wchar_t buf[1024];
Strcpy(buf,_countof(buf),pItem->link);
DoEnvironmentSubst(buf,_countof(buf));
if (SUCCEEDED(ShParseDisplayName(buf,&pidl,0,NULL)))
SHBindToObject(NULL,pidl,NULL,IID_IShellFolder,(void **)&pFolder);
if (pFolder)
{
HRESULT hr;
CComPtr<ITrackShellMenu> pMenu;
CoCreateInstance(CLSID_TrackShellMenu,NULL,CLSCTX_INPROC_SERVER,IID_ITrackShellMenu,(void**)&pMenu);
if (pMenu)
{
CMenuCallback callback(m_pBrowser);
hr=pMenu->Initialize(&callback,-1,ANCESTORDEFAULT,SMINIT_TOPLEVEL|SMINIT_VERTICAL|SMINIT_RESTRICT_DRAGDROP);
if (SUCCEEDED(hr))
{
CRegKey cRegOrder;
CComString pFavs;
SHGetKnownFolderPath(FOLDERID_Favorites,0,NULL,&pFavs);
if (pFavs && SUCCEEDED(SHGetPathFromIDList(pidl,buf)))
{
// must compare strings and not pidls. sometimes pidls can be different but point to the same folder
pFavs.MakeUpper();
CharUpper(buf);
if (wcscmp(pFavs,buf)==0)
cRegOrder.Open(HKEY_CURRENT_USER,_T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\MenuOrder\\Favorites"));
}
hr=pMenu->SetShellFolder(pFolder,pidl,cRegOrder.m_hKey,SMSET_BOTTOM|0x00000008); // SMSET_USEBKICONEXTRACTION=0x00000008
if (SUCCEEDED(hr))
{
cRegOrder.Detach();
POINTL ptl={params.rcExclude.left,params.rcExclude.bottom};
RECTL rcl={params.rcExclude.left,params.rcExclude.top,params.rcExclude.right,params.rcExclude.bottom};
pMenu->Popup(GetAncestor(m_hWnd,GA_ROOT),&ptl,&rcl,MPPF_SETFOCUS|MPPF_BOTTOM);
}
}
}
}
}
// remove the next mouse click if it is on the same button
MSG msg;
RECT rc=pButton->rcButton;
m_Toolbar.ClientToScreen(&rc);
if (PeekMessage(&msg,NULL,WM_LBUTTONDOWN,WM_LBUTTONDBLCLK,PM_NOREMOVE) && PtInRect(&rc,msg.pt))
PeekMessage(&msg,NULL,WM_LBUTTONDOWN,WM_LBUTTONDBLCLK,PM_REMOVE);
}
return TBDDRET_DEFAULT;
}
LRESULT CBandWindow::OnChevron( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
{
NMREBARCHEVRON *pChevron=(NMREBARCHEVRON*)pnmh;
REBARBANDINFO info={sizeof(info),RBBIM_CHILD};
if (::SendMessage(pnmh->hwndFrom,RB_GETBANDINFO,pChevron->uBand,(LPARAM)&info) && info.hwndChild==m_Toolbar.m_hWnd)
{
RECT clientRect;
m_Toolbar.GetClientRect(&clientRect);
int idx=0;
for (std::vector<StdToolbarItem>::const_iterator it=m_Items.begin();it->id!=ID_LAST;++it,idx++)
{
RECT rc;
m_Toolbar.SendMessage(TB_GETITEMRECT,idx,(LPARAM)&rc);
if (rc.right>clientRect.right)
break;
}
while (m_Items[idx].id==ID_SEPARATOR)
idx++;
if (m_Items[idx].id==ID_LAST) return 1;
HMENU menu=CreateDropMenu(&m_Items[idx]);
TPMPARAMS params={sizeof(params),pChevron->rc};
// use ClientToScreen instead of MapWindowPoints because it works better in RTL mode
::ClientToScreen(pnmh->hwndFrom,(POINT*)&params.rcExclude);
::ClientToScreen(pnmh->hwndFrom,((POINT*)&params.rcExclude)+1);
int res=TrackPopupMenuEx(menu,TPM_RETURNCMD|TPM_VERTICAL,params.rcExclude.left,params.rcExclude.bottom,m_hWnd,&params);
DestroyMenu(menu);
if (res>0)
PostMessage(WM_COMMAND,res);
// remove the next mouse click if it is on the chevron
MSG msg;
if (PeekMessage(&msg,NULL,WM_LBUTTONDOWN,WM_LBUTTONDBLCLK,PM_NOREMOVE) && PtInRect(&params.rcExclude,msg.pt))
PeekMessage(&msg,NULL,WM_LBUTTONDOWN,WM_LBUTTONDBLCLK,PM_REMOVE);
return 1;
}
return 0;
}
HMENU CBandWindow::CreateDropMenu( const StdToolbarItem *pItem )
{
HMODULE hShell32=GetModuleHandle(L"Shell32.dll");
std::vector<HMODULE> modules;
HMENU menu=CreateDropMenuRec(pItem,modules,hShell32);
for (std::vector<HMODULE>::const_iterator it=modules.begin();it!=modules.end();++it)
FreeLibrary(*it);
MENUINFO info={sizeof(info),MIM_STYLE,MNS_CHECKORBMP};
SetMenuInfo(menu,&info);
return menu;
}
HMENU CBandWindow::CreateDropMenuRec( const StdToolbarItem *pItem, std::vector<HMODULE> &modules, HMODULE hShell32 )
{
HMENU menu=CreatePopupMenu();
for (int idx=0;pItem->id!=ID_LAST;pItem++,idx++)
{
if (pItem->id==ID_SEPARATOR)
{
AppendMenu(menu,MF_SEPARATOR,0,0);
continue;
}
const wchar_t *name=pItem->label;
if (!name && pItem->tip && _wcsicmp(pItem->tip,L"none")!=0)
name=pItem->tip;
if (!pItem->bIconLoaded)
{
pItem->bIconLoaded=true;
if (m_MenuIconSize>0)
{
if (pItem->iconPath)
{
if (_wcsicmp(pItem->iconPath,L"NONE")!=0)
{
HICON hIcon=LoadIcon(m_MenuIconSize,pItem->iconPath,modules);
if (!hIcon)
hIcon=(HICON)LoadImage(hShell32,MAKEINTRESOURCE(1),IMAGE_ICON,m_MenuIconSize,m_MenuIconSize,LR_DEFAULTCOLOR);
if (hIcon)
{
HICON hIcon2=pItem->iconPathD?LoadIcon(m_MenuIconSize,pItem->iconPathD,modules):NULL;
if (!hIcon2)
hIcon2=CreateDisabledIcon(hIcon,m_MenuIconSize);
pItem->menuIcon=BitmapFromIcon(hIcon,m_MenuIconSize,NULL,true);
pItem->menuIconD=BitmapFromIcon(hIcon2,m_MenuIconSize,NULL,true);
}
}
}
else if (pItem->link)
{
HICON hIcon=NULL;
CAbsolutePidl pidl;
wchar_t path[_MAX_PATH];
Strcpy(path,_countof(path),pItem->link);
DoEnvironmentSubst(path,_countof(path));
if (SUCCEEDED(ShParseDisplayName(path,&pidl,0,NULL)))
{
if (!name)
{
CComString pName;
if (SUCCEEDED(SHGetNameFromIDList(pidl,SIGDN_PARENTRELATIVEEDITING,&pName)) && pName)
{
pItem->menuText=pName;
}
}
hIcon=LoadIcon(m_MenuIconSize,pidl);
}
if (hIcon)
{
HICON hIcon2=pItem->iconPathD?LoadIcon(m_MenuIconSize,pItem->iconPathD,modules):NULL;
if (!hIcon2)
hIcon2=CreateDisabledIcon(hIcon,m_MenuIconSize);
pItem->menuIcon=BitmapFromIcon(hIcon,m_MenuIconSize,NULL,true);
pItem->menuIconD=BitmapFromIcon(hIcon2,m_MenuIconSize,NULL,true);
}
}
}
}
if (!name) name=pItem->menuText;
if (pItem->submenu)
{
HMENU menu2=CreateDropMenu(pItem->submenu);
AppendMenu(menu,MF_POPUP,(UINT_PTR)menu2,name);
}
else
{
int cmd=(int)(pItem-&m_Items[0]+1);
AppendMenu(menu,MF_STRING,cmd,name);
}
if (pItem->menuIcon)
{
MENUITEMINFO mii={sizeof(mii)};
mii.fMask=MIIM_BITMAP;
mii.hbmpItem=pItem->bDisabled?pItem->menuIconD:pItem->menuIcon;
SetMenuItemInfo(menu,idx,TRUE,&mii);
}
if (pItem->bDisabled)
EnableMenuItem(menu,idx,MF_BYPOSITION|MF_GRAYED);
if (pItem->bChecked)
CheckMenuItem(menu,idx,MF_BYPOSITION|MF_CHECKED);
}
return menu;
}
void CBandWindow::UpdateToolbar( void )
{
// disable the Up button if we are at the top level
bool bDesktop=false;
bool bNavPane=true;
bool bDisableNavPane=false;
bool bDetailsPane=true;
bool bDisableDetailsPane=false;
bool bPreviewPane=true;
bool bDisablePreviewPane=false;
if (m_pBrowser)
{
CComPtr<IShellView> pView;
m_pBrowser->QueryActiveShellView(&pView);
if (pView)
{
CComQIPtr<IFolderView> pView2=pView;
if (pView2)
{
CComPtr<IPersistFolder2> pFolder;
pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder);
if (pFolder)
{
CAbsolutePidl pidl;
if (SUCCEEDED(pFolder->GetCurFolder(&pidl)) && pidl)
{
if (ILIsEmpty(pidl))
bDesktop=true; // only the top level has empty PIDL
}
}
if (m_pBrowserBag)
{
if (GetWinVersion()>=WIN_VER_WIN8)
{
VARIANT val={VT_EMPTY};
if (SUCCEEDED(m_pBrowserBag->Read(g_ComboPaneEnabled,&val,NULL)) && val.vt==VT_BOOL && !val.boolVal)
{
bDisableDetailsPane=true;
bDetailsPane=false;
bDisablePreviewPane=true;
bPreviewPane=false;
}
VariantClear(&val);
}
else
{
VARIANT val={VT_EMPTY};
if (SUCCEEDED(m_pBrowserBag->Read(g_DetailsPaneEnabled,&val,NULL)) && val.vt==VT_BOOL && !val.boolVal)
{
bDisableDetailsPane=true;
bDetailsPane=false;
}
VariantClear(&val);
if (SUCCEEDED(m_pBrowserBag->Read(g_PreviewPaneEnabled,&val,NULL)) && val.vt==VT_BOOL && !val.boolVal)
{
bDisablePreviewPane=true;
bPreviewPane=false;
}
VariantClear(&val);
}
CComPtr<IExplorerPaneVisibility> pVisibility;
if (SUCCEEDED(pView2->GetFolder(IID_IExplorerPaneVisibility,(void**)&pVisibility)) && pVisibility)
{
EXPLORERPANESTATE state=0;
if (SUCCEEDED(pVisibility->GetPaneState(EP_NavPane,&state)))
{
bDisableNavPane=(state&EPS_FORCE)!=0;
if (bDisableNavPane)
bNavPane=!(state&EPS_DEFAULT_OFF);
}
if (!bDisableDetailsPane)
{
state=0;
if (SUCCEEDED(pVisibility->GetPaneState(EP_DetailsPane,&state)))
{
bDisableDetailsPane=(state&EPS_FORCE)!=0;
if (bDisableDetailsPane)
bDetailsPane=!(state&EPS_DEFAULT_OFF);
}
}
if (!bDisablePreviewPane)
{
state=0;
if (SUCCEEDED(pVisibility->GetPaneState(EP_PreviewPane,&state)))
{
bDisablePreviewPane=(state&EPS_FORCE)!=0;
if (bDisablePreviewPane)
bPreviewPane=!(state&EPS_DEFAULT_OFF);
}
}
}
}
}
}
if (m_pBrowserBag)
{
if (!bDisableNavPane)
{
VARIANT val={VT_EMPTY};
bNavPane=SUCCEEDED(m_pBrowserBag->Read(g_NavPaneVisible,&val,NULL)) && val.vt==VT_BOOL && val.boolVal;
VariantClear(&val);
}
if (!bDisableDetailsPane)
{
VARIANT val={VT_EMPTY};
bDetailsPane=SUCCEEDED(m_pBrowserBag->Read(g_DetailsPaneVisible,&val,NULL)) && val.vt==VT_BOOL && val.boolVal;
VariantClear(&val);
}
if (!bDisablePreviewPane)
{
VARIANT val={VT_EMPTY};
bPreviewPane=SUCCEEDED(m_pBrowserBag->Read(g_PreviewPaneVisible,&val,NULL)) && val.vt==VT_BOOL && val.boolVal;
VariantClear(&val);
}
}
}
EnableButton(ID_GOUP,!bDesktop);
if (m_pBrowserBag)
{
EnableButton(ID_NAVPANE,!bDisableNavPane);
CheckButton(ID_NAVPANE,bNavPane);
EnableButton(ID_DETAILSPANE,!bDisableDetailsPane);
CheckButton(ID_DETAILSPANE,bDetailsPane);
EnableButton(ID_PREVIEWPANE,!bDisablePreviewPane);
CheckButton(ID_PREVIEWPANE,bPreviewPane);
}
if (HasFolderSettings())
UpdateFolderSettings();
}
void CBandWindow::UpdateFolderSettings( void )
{
bool bExtensions=false, bHidden=false, bSystem=false;
CRegKey regKey;
if (regKey.Open(HKEY_CURRENT_USER,L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",KEY_READ|KEY_WOW64_64KEY)==ERROR_SUCCESS)
{
DWORD val;
if (regKey.QueryDWORDValue(L"HideFileExt",val)==ERROR_SUCCESS)
bExtensions=(val!=1);
if (regKey.QueryDWORDValue(L"Hidden",val)==ERROR_SUCCESS)
bHidden=(val==1);
if (regKey.QueryDWORDValue(L"ShowSuperHidden",val)==ERROR_SUCCESS)
bSystem=(val==1);
}
CheckButton(ID_SHOW_EXTENSIONS,bExtensions);
CheckButton(ID_HIDDEN_FILES,bHidden);
CheckButton(ID_SYSTEM_FILES,bSystem);
}
void CBandWindow::EnableButton( int cmd, bool bEnable )
{
bool bMain=true;
bool bDisabled=!bEnable;
for (int idx=0;idx<(int)m_Items.size();idx++)
{
if (m_Items[idx].id==ID_LAST)
bMain=false;
if (m_Items[idx].id==cmd && m_Items[idx].bDisabled!=bDisabled)
{
m_Items[idx].bDisabled=bDisabled;
if (bMain)
m_Toolbar.SendMessage(TB_ENABLEBUTTON,idx+1,bDisabled?0:1);
}
}
}
void CBandWindow::CheckButton( int cmd, bool bCheck )
{
bool bMain=true;
for (int idx=0;idx<(int)m_Items.size();idx++)
{
if (m_Items[idx].id==ID_LAST)
bMain=false;
if (m_Items[idx].id==cmd && m_Items[idx].bChecked!=bCheck)
{
m_Items[idx].bChecked=bCheck;
if (bMain)
m_Toolbar.SendMessage(TB_CHECKBUTTON,idx+1,bCheck?1:0);
}
}
}
bool CBandWindow::HasPanes( void ) const
{
for (int idx=0;idx<(int)m_Items.size();idx++)
{
if (m_Items[idx].id==ID_NAVPANE || m_Items[idx].id==ID_DETAILSPANE || m_Items[idx].id==ID_PREVIEWPANE)
return true;
}
return false;
}
bool CBandWindow::HasFolderSettings( void ) const
{
for (int idx=0;idx<(int)m_Items.size();idx++)
{
if (m_Items[idx].id==ID_SHOW_EXTENSIONS || m_Items[idx].id==ID_HIDDEN_FILES || m_Items[idx].id==ID_SYSTEM_FILES)
return true;
}
return false;
}
void CBandWindow::UpdateBag( void )
{
if (!m_pBrowserBag)
{
CComPtr<IUnknown> pFrame;
IUnknown_QueryService(m_pBrowser,SID_FrameManager,IID_IUnknown,(void**)&pFrame);
IUnknown_QueryService(pFrame,SID_PerBrowserPropertyBag,IID_IPropertyBag,(void**)&m_pBrowserBag);
if (m_pBrowserBag)
{
void **vtbl=*(void***)m_pBrowserBag.p+4;
if (InterlockedCompareExchangePointer((void**)&g_OldBagWrite,*vtbl,0)==0)
{
DWORD oldProtect;
VirtualProtect(vtbl,sizeof(void*),PAGE_READWRITE,&oldProtect);
*vtbl=BagWriteHook;
VirtualProtect(vtbl,sizeof(void*),oldProtect,&oldProtect);
// prevent the DLL from being unloaded after we mess with the vtable, otherwise bad things happen
HMODULE q;
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,(const wchar_t*)g_Instance,&q);
}
}
}
PostMessage(BWM_UPDATETOOLBAR);
}
HRESULT __stdcall CBandWindow::BagWriteHook( IPropertyBag *pThis, LPCOLESTR pszPropName, VARIANT *pVar )
{
if (_wcsicmp(pszPropName,g_NavPaneVisible)==0 ||
_wcsicmp(pszPropName,g_DetailsPaneVisible)==0 ||
_wcsicmp(pszPropName,g_PreviewPaneVisible)==0 ||
_wcsicmp(pszPropName,g_DetailsPaneEnabled)==0 ||
_wcsicmp(pszPropName,g_PreviewPaneEnabled)==0 ||
_wcsicmp(pszPropName,g_ComboPaneEnabled)==0
)
{
TlsData *pData=GetTlsData();
if (pData && pData->band)
pData->band->m_BandWindow.PostMessage(BWM_UPDATETOOLBAR);
}
return g_OldBagWrite(pThis,pszPropName,pVar);
}
void CBandWindow::Clear( void )
{
m_TreeParent=NULL;
m_pBrowser=NULL;
m_pWebBrowser=NULL;
m_pBrowserBag=NULL;
}
void CBandWindow::SetBrowsers( IShellBrowser *pBrowser, IWebBrowser2 *pWebBrowser )
{
m_pBrowser=pBrowser;
m_pWebBrowser=pWebBrowser;
m_pBrowserBag=NULL;
}
///////////////////////////////////////////////////////////////////////////////
// CExplorerBand - adds a toolbar band to Windows Explorer with 2 buttons - "Up" and "Settings"
CExplorerBand::CExplorerBand( void )
{
m_bSubclassRebar=GetWinVersion()>=WIN_VER_WIN7;
m_bSubclassedRebar=false;
m_TopWindow=NULL;
}
// Subclasses the rebar control on Windows 7. Makes sure the RBBS_BREAK style is properly set. Windows 7 has a bug
// that forces RBBS_BREAK for every rebar band
LRESULT CALLBACK CExplorerBand::RebarSubclassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
if (uMsg==RB_SETBANDINFO && ((CExplorerBand*)uIdSubclass)->m_bHandleSetInfo)
{
REBARBANDINFO *pInfo=(REBARBANDINFO*)lParam;
if ((pInfo->hwndChild==(HWND)dwRefData) && (pInfo->fMask&RBBIM_STYLE))
{
if (((CExplorerBand*)uIdSubclass)->m_bBandNewLine)
pInfo->fStyle|=RBBS_BREAK;
else
pInfo->fStyle&=~RBBS_BREAK;
}
}
if (uMsg==WM_CLEAR)
((CExplorerBand*)uIdSubclass)->m_bHandleSetInfo=false;
if (uMsg==RB_DELETEBAND)
{
int n=(int)SendMessage(hWnd,RB_GETBANDCOUNT,0,0);
CExplorerBand *pThis=(CExplorerBand*)uIdSubclass;
for (int i=0;i<n;i++)
{
// when ANY band but ours is being deleted, make sure we preserve OUR break style (on Win7 all bands get a break style when any band is deleted)
REBARBANDINFO info={sizeof(info),RBBIM_STYLE|RBBIM_CHILD};
SendMessage(hWnd,RB_GETBANDINFO,i,(LPARAM)&info);
if (info.hwndChild==(HWND)dwRefData)
{
LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam);
if (i!=(int)wParam)
{
bool old=pThis->m_bHandleSetInfo;
pThis->m_bHandleSetInfo=false;
SendMessage(hWnd,RB_SETBANDINFO,i,(LPARAM)&info);
pThis->m_bHandleSetInfo=old;
}
return res;
}
}
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
// Subclasses the rebar's parent to catch RBN_CHEVRONPUSHED
LRESULT CALLBACK CExplorerBand::ParentSubclassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->code==RBN_CHEVRONPUSHED)
{
if (SendMessage((HWND)dwRefData,uMsg,wParam,lParam))
return 0;
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
// IDeskBand
STDMETHODIMP CExplorerBand::GetBandInfo( DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi )
{
// initializes the band
if (!m_bSubclassedRebar)
{
HWND rebar=GetParent(m_BandWindow.GetToolbar());
wchar_t className[256];
GetClassName(rebar,className,_countof(className));
if (_wcsicmp(className,REBARCLASSNAME)==0)
{
if (m_bSubclassRebar)
{
CRegKey regSettings;
m_bBandNewLine=false;
if (regSettings.Open(HKEY_CURRENT_USER,GetSettingsRegPath())==ERROR_SUCCESS)
{
DWORD NewLine;
if (regSettings.QueryDWORDValue(L"NewLine",NewLine)==ERROR_SUCCESS)
m_bBandNewLine=NewLine!=0;
}
SetWindowSubclass(rebar,RebarSubclassProc,(UINT_PTR)this,(DWORD_PTR)m_BandWindow.GetToolbar());
}
SetWindowSubclass(GetParent(rebar),ParentSubclassProc,(UINT_PTR)this,(DWORD_PTR)m_BandWindow.m_hWnd);
m_bSubclassedRebar=true;
}
}
RECT rc;
SendMessage(m_BandWindow.GetToolbar(),TB_GETITEMRECT,0,(LPARAM)&rc);
int minSize=rc.right;
int count=(int)SendMessage(m_BandWindow.GetToolbar(),TB_BUTTONCOUNT,0,0);
SendMessage(m_BandWindow.GetToolbar(),TB_GETITEMRECT,count-1,(LPARAM)&rc);
bool bChevron=GetSettingBool(L"ResizeableToolbar");
if (pdbi)
{
if (pdbi->dwMask&DBIM_MINSIZE)
{
pdbi->ptMinSize.x=bChevron?minSize:rc.right;
pdbi->ptMinSize.y=rc.bottom;
}
if (pdbi->dwMask&DBIM_MAXSIZE)
{
pdbi->ptMaxSize.x=0; // ignored
pdbi->ptMaxSize.y=-1; // unlimited
}
if (pdbi->dwMask&DBIM_INTEGRAL)
{
pdbi->ptIntegral.x=0; // not sizeable
pdbi->ptIntegral.y=0; // not sizeable
}
if (pdbi->dwMask&DBIM_ACTUAL)
{
pdbi->ptActual.x=rc.right;
pdbi->ptActual.y=rc.bottom;
}
if (pdbi->dwMask&DBIM_TITLE)
{
*pdbi->wszTitle=0; // no title
}
if (pdbi->dwMask&DBIM_BKCOLOR)
{
//Use the default background color by removing this flag.
pdbi->dwMask&=~DBIM_BKCOLOR;
}
if (pdbi->dwMask&DBIM_MODEFLAGS)
{
if (bChevron)
pdbi->dwModeFlags|=DBIMF_USECHEVRON;
}
}
return S_OK;
}
// IOleWindow
STDMETHODIMP CExplorerBand::GetWindow( HWND* phwnd )
{
if (!phwnd)
return E_INVALIDARG;
*phwnd=m_BandWindow.GetToolbar();
return S_OK;
}
STDMETHODIMP CExplorerBand::ContextSensitiveHelp( BOOL fEnterMode )
{
return S_OK;
}
// IDockingWindow
STDMETHODIMP CExplorerBand::CloseDW( unsigned long dwReserved )
{
ShowDW(FALSE);
return S_OK;
}
STDMETHODIMP CExplorerBand::ResizeBorderDW( const RECT* prcBorder, IUnknown* punkToolbarSite, BOOL fReserved )
{
// Not used by any band object.
return E_NOTIMPL;
}
STDMETHODIMP CExplorerBand::ShowDW( BOOL fShow )
{
if (m_bSubclassRebar && m_bSubclassedRebar)
{
// on Windows 7 get the current RBBS_BREAK state and save it in the registry to be restored later
HWND rebar=GetParent(m_BandWindow.GetToolbar());
m_bHandleSetInfo=true;
if (!fShow)
{
int n=(int)SendMessage(rebar,RB_GETBANDCOUNT,0,0);
for (int i=0;i<n;i++)
{
REBARBANDINFO info={sizeof(info),RBBIM_STYLE|RBBIM_CHILD};
SendMessage(rebar,RB_GETBANDINFO,i,(LPARAM)&info);
if (info.hwndChild==m_BandWindow.GetToolbar())
{
m_bBandNewLine=(info.fStyle&RBBS_BREAK)!=0;
CRegKey regSettings;
if (regSettings.Open(HKEY_CURRENT_USER,GetSettingsRegPath())!=ERROR_SUCCESS)
regSettings.Create(HKEY_CURRENT_USER,GetSettingsRegPath());
regSettings.SetDWORDValue(L"NewLine",m_bBandNewLine?1:0);
break;
}
}
}
ShowWindow(m_BandWindow.GetToolbar(),fShow?SW_SHOW:SW_HIDE);
PostMessage(rebar,WM_CLEAR,0,0);
}
else
ShowWindow(m_BandWindow.GetToolbar(),fShow?SW_SHOW:SW_HIDE);
return S_OK;
}
// IObjectWithSite
STDMETHODIMP CExplorerBand::SetSite( IUnknown* pUnkSite )
{
IObjectWithSiteImpl<CExplorerBand>::SetSite(pUnkSite);
if (m_BandWindow.IsWindow())
m_BandWindow.DestroyWindow();
m_BandWindow.Clear();
if (m_bSubclassedRebar)
{
HWND hwnd=GetParent(m_BandWindow.GetToolbar());
if (m_bSubclassRebar)
RemoveWindowSubclass(hwnd,RebarSubclassProc,(UINT_PTR)this);
RemoveWindowSubclass(GetParent(hwnd),ParentSubclassProc,(UINT_PTR)this);
}
m_bSubclassedRebar=false;
m_bHandleSetInfo=true;
if (m_pWebBrowser && m_dwEventCookie!=0xFEFEFEFE)
DispEventUnadvise(m_pWebBrowser,&DIID_DWebBrowserEvents2);
m_pWebBrowser=NULL;
//If punkSite is not NULL, a new site is being set.
if (pUnkSite)
{
// hook
GetTlsData()->band=this;
//Get the parent window.
HWND hWndParent=NULL;
CComQIPtr<IOleWindow> pOleWindow=pUnkSite;
if (pOleWindow)
pOleWindow->GetWindow(&hWndParent);
if (!IsWindow(hWndParent))
return E_FAIL;
m_TopWindow=GetAncestor(hWndParent,GA_ROOT);
if (!GetProp(m_TopWindow,g_LoadedSettingsAtom))
{
SetProp(m_TopWindow,g_LoadedSettingsAtom,(HANDLE)1);
LoadSettings();
}
m_BandWindow.Create(hWndParent,NULL,NULL,WS_CHILD);
if (!m_BandWindow.IsWindow())
return E_FAIL;
CComQIPtr<IServiceProvider> pProvider=pUnkSite;
if (pProvider)
{
CComPtr<IShellBrowser> pBrowser;
pProvider->QueryService(SID_SShellBrowser,IID_IShellBrowser,(void**)&pBrowser);
// listen for web browser notifications. we only care about DISPID_NAVIGATECOMPLETE2 and DISPID_ONQUIT
pProvider->QueryService(SID_SWebBrowserApp,IID_IWebBrowser2,(void**)&m_pWebBrowser);
if (m_pWebBrowser)
{
if (m_dwEventCookie==0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE when the sink is not advised
DispEventAdvise(m_pWebBrowser,&DIID_DWebBrowserEvents2);
}
m_BandWindow.SetBrowsers(pBrowser,m_pWebBrowser);
}
}
else
{
// unhook
GetTlsData()->band=NULL;
if (m_TopWindow) RemoveProp(m_TopWindow,g_LoadedSettingsAtom);
m_TopWindow=NULL;
}
return S_OK;
}
STDMETHODIMP CExplorerBand::OnNavigateComplete( IDispatch *pDisp, VARIANT *URL )
{
// this is called when the current folder changes. disable the Up button if this is the desktop folder
m_BandWindow.UpdateToolbar();
return S_OK;
}
STDMETHODIMP CExplorerBand::OnCommandStateChange( long Command, VARIANT_BOOL Enable )
{
if (Command==CSC_NAVIGATEFORWARD)
{
m_BandWindow.EnableButton(CBandWindow::ID_GOFORWARD,Enable?true:false);
}
if (Command==CSC_NAVIGATEBACK)
{
m_BandWindow.EnableButton(CBandWindow::ID_GOBACK,Enable?true:false);
}
return S_OK;
}
STDMETHODIMP CExplorerBand::OnQuit( void )
{
if (m_pWebBrowser && m_dwEventCookie!=0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE, when the sink is not advised
return DispEventUnadvise(m_pWebBrowser,&DIID_DWebBrowserEvents2);
return S_OK;
}