Files
Open-Shell-Menu/Src/ClassicExplorer/ExplorerBHO.cpp

1577 lines
52 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
// ExplorerBHO.cpp : Implementation of CExplorerBHO
#include "stdafx.h"
#include "ExplorerBHO.h"
#include "Settings.h"
#include "ResourceHelper.h"
#include "resource.h"
#include "SettingsUI.h"
#include "Translations.h"
#include "SettingsUIHelper.h"
#include "DownloadHelper.h"
#include "FNVHash.h"
#include "dllmain.h"
#include <uxtheme.h>
#include <dwmapi.h>
#include <Ntquery.h>
#include <algorithm>
// CExplorerBHO - a browser helper object that implements Alt+Enter for the folder tree
const UINT_PTR TIMER_NAVIGATE='CLSH';
int CExplorerBHO::s_AutoNavDelay;
static void GetTreeItemPidl( HTREEITEM hItem, HWND hwndTree, PIDLIST_ABSOLUTE *result )
{
// find the PIDL of the tree item (combine all child PIDLs from the current item and its parents)
CAbsolutePidl pidl;
while (hItem)
{
TVITEMEX info={TVIF_PARAM,hItem};
TreeView_GetItem(hwndTree,&info);
PIDLIST_RELATIVE **pidl1=(PIDLIST_RELATIVE**)info.lParam;
if (!pidl1 || !*pidl1 || !**pidl1)
{
pidl.Clear();
break;
}
pidl.Attach(pidl?ILCombine((PIDLIST_ABSOLUTE)**pidl1,pidl):(PIDLIST_ABSOLUTE)ILClone(**pidl1));
hItem=TreeView_GetParent(hwndTree,hItem);
}
*result=pidl.Detach();
}
LRESULT CALLBACK CExplorerBHO::SubclassTreeParentProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
// when the tree selection changes start a timer to navigate to the new folder in 100ms
if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->code==TVN_SELCHANGED)
{
if (GetSettingInt(L"AutoNavigate")==2 || ((NMTREEVIEW*)lParam)->action==TVC_BYKEYBOARD)
SetTimer(((NMHDR*)lParam)->hwndFrom,TIMER_NAVIGATE,s_AutoNavDelay,NULL);
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
// Subclass the tree control to:
// - support Alt+Enter
// - navigate to the new folder when you go up/down with the keyboard
// - fix the random scrolling of the tree when a folder is expanded
// - change the tree styles to achieve different looks
LRESULT CALLBACK CExplorerBHO::SubclassTreeProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
if (GetTlsData()->bho == NULL)
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
if (uMsg==TVM_ENSUREVISIBLE && (dwRefData&1))
{
// HACK! there is a bug in Win7 Explorer and when the selected folder is expanded for the first time it sends TVM_ENSUREVISIBLE for
// the root tree item. This causes the navigation pane to scroll up. To work around the bug we ignore TVM_ENSUREVISIBLE if it tries
// to show the root item and it is not selected
HTREEITEM hItem=(HTREEITEM)lParam;
if (!TreeView_GetParent(hWnd,hItem) && !(TreeView_GetItemState(hWnd,hItem,TVIS_SELECTED)&TVIS_SELECTED))
return 0;
}
if (uMsg==WM_TIMER && wParam==TIMER_NAVIGATE)
{
// time to navigate to the selected folder (only if different from the current folder)
KillTimer(hWnd,TIMER_NAVIGATE);
if (GetFocus()!=hWnd)
return 0;
CAbsolutePidl pidl;
GetTreeItemPidl(TreeView_GetSelection(hWnd),hWnd,&pidl);
if (pidl)
{
bool bSameFolder=false;
CExplorerBHO *pThis=GetTlsData()->bho;
CComPtr<IShellView> pView;
if (pThis->m_pBrowser && SUCCEEDED(pThis->m_pBrowser->QueryActiveShellView(&pView)))
{
CComQIPtr<IFolderView> pView2(pView);
CComPtr<IPersistFolder2> pFolder;
CAbsolutePidl pidl2;
if (pView2 && SUCCEEDED(pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder)) && SUCCEEDED(pFolder->GetCurFolder(&pidl2)) && pidl2)
{
if (ILIsEqual(pidl,pidl2))
bSameFolder=true;
}
}
if (!bSameFolder)
SendMessage(hWnd,WM_KEYDOWN,VK_SPACE,0);
}
return 0;
}
// ignore the Alt+Enter syscharacter (to stop the tree view from beeping)
if (uMsg==WM_SYSCHAR && wParam==VK_RETURN)
return 0;
if (uMsg==WM_SYSKEYDOWN && wParam==VK_RETURN)
{
// Alt+Enter is pressed
// if this message was for the folder tree, show the properties of the selected item
if (GetSettingBool(L"AltEnter") && ShowTreeProperties(hWnd))
return 0;
}
if ((uMsg==WM_LBUTTONDOWN || uMsg==WM_LBUTTONDBLCLK) && (wParam&MK_CONTROL))
{
TVHITTESTINFO test;
test.pt.x=(short)LOWORD(lParam);
test.pt.y=(short)HIWORD(lParam);
HTREEITEM hItem=TreeView_HitTest(hWnd,&test);
if (test.flags&TVHT_ONITEM)
{
CAbsolutePidl pidl;
GetTreeItemPidl(hItem,hWnd,&pidl);
if (pidl)
{
CExplorerBHO *pThis=GetTlsData()->bho;
if (pThis->m_pBrowser)
{
pThis->m_pBrowser->BrowseObject(pidl,SBSP_NEWBROWSER|SBSP_ABSOLUTE);
}
}
}
return 0;
}
if (uMsg==TVM_SETEXTENDEDSTYLE && wParam==(TVS_EX_FADEINOUTEXPANDOS|TVS_EX_AUTOHSCROLL|0x80000000) && lParam==0)
{
wParam&=0x7FFFFFFF;
if (GetSettingInt(L"AutoNavigate")>0)
SetWindowSubclass(GetParent(hWnd),SubclassTreeParentProc,'CLSH',0);
if (!GetSettingBool(L"NoFadeButtons"))
wParam&=~TVS_EX_FADEINOUTEXPANDOS;
int indent=-1;
if (GetSettingBool(L"FullIndent"))
indent=0;
int treeStyle=GetSettingInt(L"TreeStyle");
if (treeStyle==STYLE_CLASSIC && GetWinVersion()>=WIN_VER_WIN10)
treeStyle=STYLE_VISTA;
DWORD style=GetWindowLong(hWnd,GWL_STYLE);
if (treeStyle!=STYLE_VISTA)
{
SetWindowTheme(hWnd,NULL,NULL);
if (treeStyle==STYLE_SIMPLE)
{
style|=TVS_SINGLEEXPAND|TVS_TRACKSELECT;
style&=~TVS_HASLINES;
}
else
{
style|=TVS_HASLINES;
style&=~(TVS_SINGLEEXPAND|TVS_TRACKSELECT);
wParam|=TVS_EX_FADEINOUTEXPANDOS;
HIMAGELIST images=TreeView_GetImageList(hWnd,TVSIL_NORMAL);
int cx, cy;
ImageList_GetIconSize(images,&cx,&cy);
indent=cx+3;
}
}
int scroll=GetSettingInt(L"HScrollbar");
if ((scroll==0 && treeStyle==STYLE_VISTA) || scroll==1)
wParam&=~TVS_EX_AUTOHSCROLL;
if ((scroll==0 && treeStyle!=STYLE_VISTA) || scroll==2)
style&=~TVS_NOHSCROLL;
SetWindowLong(hWnd,GWL_STYLE,style);
if (indent>=0)
TreeView_SetIndent(hWnd,indent);
int d=GetSettingInt(L"TreeItemSpacing");
if (d)
{
CExplorerBHO *pThis=GetTlsData()->bho;
pThis->m_TreeItemHeight=TreeView_GetItemHeight(hWnd)+d;
TreeView_SetItemHeight(hWnd,pThis->m_TreeItemHeight);
}
if (wParam==0)
return 0;
}
if (uMsg==WM_SETTINGCHANGE)
{
LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam);
int indent=-1;
if (GetSettingBool(L"FullIndent"))
indent=0;
if (GetSettingInt(L"TreeStyle")==STYLE_CLASSIC && GetWinVersion()<WIN_VER_WIN10)
{
HIMAGELIST images=TreeView_GetImageList(hWnd,TVSIL_NORMAL);
int cx, cy;
ImageList_GetIconSize(images,&cx,&cy);
indent=cx+3;
}
if (indent>=0)
TreeView_SetIndent(hWnd,indent);
return res;
}
if (uMsg==TVM_SETITEMHEIGHT)
{
CExplorerBHO *pThis=GetTlsData()->bho;
if (pThis->m_TreeItemHeight>0)
wParam=pThis->m_TreeItemHeight;
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
LRESULT CALLBACK CExplorerBHO::HookExplorer( int nCode, WPARAM wParam, LPARAM lParam )
{
// wait for the tree control to be created and subclass it
if (nCode==HCBT_CREATEWND)
{
HWND hWnd=(HWND)wParam;
CBT_CREATEWND *pCreate=(CBT_CREATEWND*)lParam;
if (pCreate->lpcs->lpszClass>(LPTSTR)0xFFFF && _wcsicmp(pCreate->lpcs->lpszClass,WC_TREEVIEW)==0)
{
HWND parent=GetAncestor(pCreate->lpcs->hwndParent,GA_ROOT);
wchar_t name[256];
if (GetClassName(parent,name,_countof(name)) && _wcsicmp(name,L"CabinetWClass")==0)
{
DWORD_PTR settings=0;
if (GetSettingBool(L"FixFolderScroll"))
settings|=1;
SetWindowSubclass(hWnd,SubclassTreeProc,'CLSH',settings);
PostMessage(hWnd,TVM_SETEXTENDEDSTYLE,TVS_EX_FADEINOUTEXPANDOS|TVS_EX_AUTOHSCROLL|0x80000000,0);
}
}
else if (pCreate->lpcs->lpszClass>(LPTSTR)0xFFFF && _wcsicmp(pCreate->lpcs->lpszClass,L"DUIViewWndClassName")==0)
{
TlsData *pTlsData=GetTlsData();
if (!pTlsData->bho->m_DUIView)
{
pTlsData->bho->m_DUIView=hWnd;
SetWindowSubclass(pTlsData->bho->m_DUIView,SubclassDUIViewProc,(UINT_PTR)pTlsData->bho,'CLSH');
}
}
}
return CallNextHookEx(NULL,nCode,wParam,lParam);
}
LRESULT CALLBACK CExplorerBHO::HookKeyboard( int nCode, WPARAM wParam, LPARAM lParam )
{
// wait for the tree control to be created and subclass it
if (nCode==HC_ACTION)
{
TlsData *pTlsData=GetTlsData();
if (wParam==pTlsData->bho->m_AltD && (lParam&0x20000000))
{
if (lParam&0x80000000)
{
return 0;
}
else
{
if (IsWindow(pTlsData->bho->m_Breadcrumbs) && IsWindowVisible(pTlsData->bho->m_Breadcrumbs))
{
SetFocus(pTlsData->bho->m_Breadcrumbs);
SendMessage(pTlsData->bho->m_Breadcrumbs,WM_KEYDOWN,VK_SPACE,0);
SendMessage(pTlsData->bho->m_Breadcrumbs,WM_KEYUP,VK_SPACE,0);
return 0;
}
}
}
if (wParam==(pTlsData->bho->m_UpHotkey&255) && !(lParam&0x80000000))
{
// Backspace goes to the parent folder, but only if no window has the caret
GUITHREADINFO info={sizeof(info)};
if (GetGUIThreadInfo(GetCurrentThreadId(),&info) && !info.hwndCaret)
{
bool bShift1=(pTlsData->bho->m_UpHotkey&(HOTKEYF_SHIFT<<8))!=0;
bool bCtrl1=(pTlsData->bho->m_UpHotkey&(HOTKEYF_CONTROL<<8))!=0;
bool bAlt1=(pTlsData->bho->m_UpHotkey&(HOTKEYF_ALT<<8))!=0;
bool bShift2=GetKeyState(VK_SHIFT)<0;
bool bCtrl2=GetKeyState(VK_CONTROL)<0;
bool bAlt2=GetKeyState(VK_MENU)<0;
if (bShift1==bShift2 && bAlt1==bAlt2)
{
if (bCtrl1==bCtrl2)
{
pTlsData->bho->m_pBrowser->BrowseObject(NULL,SBSP_SAMEBROWSER|SBSP_PARENT);
return 1;
}
else if (bCtrl2)
{
pTlsData->bho->m_pBrowser->BrowseObject(NULL,SBSP_NEWBROWSER|SBSP_PARENT);
return 1;
}
}
}
}
}
return CallNextHookEx(NULL,nCode,wParam,lParam);
}
bool CExplorerBHO::GetStatusText( wchar_t *buf, int size, const wchar_t *oldText, bool bShowTip, bool bShowSpace )
{
bool res=false;
CComPtr<IShellView> pView;
if (m_pBrowser && SUCCEEDED(m_pBrowser->QueryActiveShellView(&pView)))
{
CComQIPtr<IFolderView> pView2(pView);
CComPtr<IPersistFolder2> pFolder;
if (pView2 && SUCCEEDED(pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder)))
{
int count;
if (bShowTip && SUCCEEDED(pView2->ItemCount(SVGIO_SELECTION,&count)) && count==1)
{
// if only one item is selected, show its info in the status bar
CComPtr<IEnumIDList> pEnum;
PITEMID_CHILD child;
if (SUCCEEDED(pView2->Items(SVGIO_SELECTION,IID_IEnumIDList,(void**)&pEnum)) && pEnum && pEnum->Next(1,&child,NULL)==S_OK)
{
CComQIPtr<IShellFolder> pFolder2(pFolder);
if (pFolder2)
{
CComPtr<IQueryInfo> pQueryInfo;
if (SUCCEEDED(pFolder2->GetUIObjectOf(NULL,1,(PCUITEMID_CHILD*)&child,IID_IQueryInfo,NULL,(void**)&pQueryInfo)))
{
CComString pTip;
if (SUCCEEDED(pQueryInfo->GetInfoTip(QITIPF_DEFAULT|QITIPF_SINGLELINE,&pTip)) && pTip)
{
Strcpy(buf,size,pTip);
for (wchar_t *p=buf;*p;p++)
if (*p=='\t')
*p=' ';
res=true;
}
}
}
}
}
if (!res)
{
wchar_t buf2[256];
if (!oldText)
{
int count=-1;
const wchar_t *fmt=NULL;
if (SUCCEEDED(pView2->ItemCount(SVGIO_SELECTION,&count)) && count>0)
{
fmt=(count==1?FindTranslation(L"Status.ItemSelected",L"%s item selected"):FindTranslation(L"Status.ItemsSelected",L"%s items selected"));
}
else if (SUCCEEDED(pView2->ItemCount(SVGIO_ALLVIEW,&count)) && count>=0)
{
fmt=(count==1?FindTranslation(L"Status.Item",L"%s item"):FindTranslation(L"Status.Items",L"%s items"));
}
else
buf2[0]=0;
if (fmt)
{
wchar_t str1[100];
wchar_t str2[100];
Sprintf(str1,_countof(str1),L"%d",count);
NUMBERFMT numFmt;
wchar_t sep1[10], sep2[10];
numFmt.NumDigits=0;
GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ILZERO,sep1,10);
numFmt.LeadingZero=_wtol(buf);
GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_INEGNUMBER,sep1,10);
numFmt.NegativeOrder=_wtol(buf);
GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SGROUPING,sep1,10);
numFmt.Grouping=0;
for (const wchar_t *p=sep1;*p;p++)
if (*p>='1' && *p<='9')
numFmt.Grouping=numFmt.Grouping*10+(*p-'0');
GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,sep1,10);
numFmt.lpDecimalSep=sep1;
GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,sep2,10);
numFmt.lpThousandSep=sep2;
if (GetNumberFormat(LOCALE_USER_DEFAULT,0,str1,&numFmt,str2,_countof(str2)))
Sprintf(buf2,_countof(buf2),fmt,str2);
else
Sprintf(buf2,_countof(buf2),fmt,str1);
}
res=true;
}
if (bShowSpace)
{
// show the free space of the drive containing the current folder
// also works for network locations
CAbsolutePidl pidl;
wchar_t path[_MAX_PATH];
ULARGE_INTEGER diskSize;
if (SUCCEEDED(pFolder->GetCurFolder(&pidl)) && SHGetPathFromIDList(pidl,path) && GetDiskFreeSpaceEx(path,NULL,NULL,&diskSize))
{
wchar_t str[100];
StrFormatByteSize64(diskSize.QuadPart,str,_countof(str));
Sprintf(buf,size,FindTranslation(L"Status.FreeSpace",L"%s (Disk free space: %s)"),oldText?oldText:buf2,str);
res=true;
}
else if (!oldText)
Strcpy(buf,size,buf2);
}
else if (!oldText)
Strcpy(buf,size,buf2);
}
}
}
return res;
}
void CExplorerBHO::GetFileSize( wchar_t *buf, int size )
{
__int64 fileSize=-1;
CComPtr<IShellView> pView;
bool bMore=false;
int time0=GetTickCount();
if (m_pBrowser && SUCCEEDED(m_pBrowser->QueryActiveShellView(&pView)))
{
CComQIPtr<IFolderView> pView2(pView);
CComPtr<IPersistFolder2> pFolder;
CAbsolutePidl pidl;
if (pView2 && SUCCEEDED(pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder)) && SUCCEEDED(pFolder->GetCurFolder(&pidl)))
{
CComQIPtr<IShellFolder2> pFolder2(pFolder);
UINT type=SVGIO_SELECTION;
int count, selCount;
if (SUCCEEDED(pView2->ItemCount(SVGIO_ALLVIEW,&count)))
{
if ((FAILED(pView2->ItemCount(SVGIO_SELECTION,&selCount)) || selCount==0))
type=SVGIO_ALLVIEW;
CComPtr<IEnumIDList> pEnum;
if ((count<10000 || selCount<1000) && SUCCEEDED(pView2->Items(type,IID_IEnumIDList,(void**)&pEnum)) && pEnum)
{
PITEMID_CHILD child;
SHCOLUMNID column={PSGUID_STORAGE,PID_STG_SIZE};
int index=0;
while (pEnum->Next(1,&child,NULL)==S_OK)
{
index++;
if ((index%100)==0 && (GetTickCount()-time0)>500)
{
ILFree(child);
bMore=true;
break;
}
CComVariant var;
if (SUCCEEDED(pFolder2->GetDetailsEx(child,&column,&var)) && var.vt==VT_UI8)
{
if (fileSize<0)
fileSize=var.ullVal;
else
fileSize+=var.ullVal;
}
ILFree(child);
}
}
}
}
}
if (fileSize>=0)
{
// format the file size as KB, MB, etc
StrFormatByteSize64(fileSize,buf,size);
if (bMore)
Strcat(buf,size,L"+");
}
else
buf[0]=0;
}
// Subclass the statusbar to:
// - show free disk space
// - show the total size of the selected files
LRESULT CALLBACK CExplorerBHO::SubclassStatusProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
wchar_t buf[1024];
if (uMsg==WM_CLEAR)
{
// recalculate the selection size on a timer. this way if the status text is changed frequently
// the recalculation will not happen every time
SetTimer(hWnd,uIdSubclass,10,NULL);
}
if (uMsg==WM_PAINT && ((CExplorerBHO*)uIdSubclass)->m_bForceRefresh)
{
// sometimes Explorer doesn't fully initialize the status bar on Windows 7 and leaves it with 1 part
// in such case force the view to refresh after the status bar is fully visible
((CExplorerBHO*)uIdSubclass)->m_bForceRefresh=false;
if (SendMessage(hWnd,SB_GETPARTS,0,0)<=1)
PostMessage(GetParent(hWnd),WM_COMMAND,41504,0); // Refresh command
}
if (uMsg==SB_SETTEXT && LOWORD(wParam)==0)
{
// when the text of the first part is changing
if (dwRefData&SHOW_FREE_SPACE)
{
if (((CExplorerBHO*)uIdSubclass)->m_bResetStatus && SendMessage(hWnd,SB_GETPARTS,0,0)<=1)
{
// HACK! there is a bug in Win7 and when the Explorer window is created it doesn't correctly
// initialize the status bar to have 3 parts. as soon as the user resizes the window the
// 3 parts appear. so here we resize the parent of the status bar to create the 3 parts.
HWND parent=GetParent(hWnd);
RECT rc;
GetWindowRect(parent,&rc);
SetWindowPos(parent,NULL,0,0,rc.right-rc.left+1,rc.bottom-rc.top,SWP_NOZORDER|SWP_NOMOVE);
SetWindowPos(parent,NULL,0,0,rc.right-rc.left,rc.bottom-rc.top,SWP_NOZORDER|SWP_NOMOVE);
// the first time the status text is set it is too early. so we do this until we get at lest 2 parts
if (SendMessage(hWnd,SB_GETPARTS,0,0)>1)
((CExplorerBHO*)uIdSubclass)->m_bResetStatus=false;
}
}
if (((CExplorerBHO*)uIdSubclass)->GetStatusText(buf,_countof(buf),(wchar_t*)lParam,(dwRefData&SHOW_INFOTIP)!=0,(dwRefData&SHOW_FREE_SPACE)!=0))
lParam=(LPARAM)buf;
}
if (uMsg==SB_SETTEXT && LOWORD(wParam)==1)
{
return 0;
}
if (uMsg==WM_TIMER && wParam==uIdSubclass)
{
// recalculate the total size of the selected files and show it in part 2 of the status bar
KillTimer(hWnd,wParam);
((CExplorerBHO*)uIdSubclass)->GetFileSize(buf,_countof(buf));
DefSubclassProc(hWnd,SB_SETTEXT,1,(LPARAM)buf);
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
LRESULT CALLBACK CExplorerBHO::SubclassStatusProc8( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
wchar_t buf[1024];
if (uMsg==WM_CLEAR)
{
// recalculate the selection size on a timer. this way if the status text is changed frequently
// the recalculation will not happen every time
SetTimer(hWnd,uIdSubclass,10,NULL);
if (!((CExplorerBHO*)uIdSubclass)->GetStatusText(buf,_countof(buf),NULL,(dwRefData&SHOW_INFOTIP)!=0,(dwRefData&SHOW_FREE_SPACE)!=0))
buf[0]=0;
SendMessage(hWnd,SB_SETTEXT,PART_TEXT,(LPARAM)buf);
return 0;
}
if (uMsg==WM_TIMER && wParam==uIdSubclass)
{
// recalculate the total size of the selected files and show it in part 2 of the status bar
KillTimer(hWnd,wParam);
((CExplorerBHO*)uIdSubclass)->GetFileSize(buf,_countof(buf));
SendMessage(hWnd,SB_SETTEXT,PART_SIZE,(LPARAM)buf);
return 0;
}
if (uMsg==WM_LBUTTONDBLCLK)
{
POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)};
RECT rc;
DefSubclassProc(hWnd,SB_GETRECT,PART_ZONE,(LPARAM)&rc);
if (PtInRect(&rc,pt))
{
CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass;
CComBSTR url;
if (pThis->m_pWebBrowser && SUCCEEDED(pThis->m_pWebBrowser->get_LocationURL(&url)))
{
// use undocumented function 383 from shlwapi
typedef void (WINAPI* FZoneConfigureW)(HWND,LPCWSTR);
FZoneConfigureW ZoneConfigureW;
HMODULE hShlwapi=LoadLibrary(L"shlwapi.dll");
if(hShlwapi)
{
ZoneConfigureW=(FZoneConfigureW)GetProcAddress(hShlwapi,MAKEINTRESOURCEA(383));
if(ZoneConfigureW)
ZoneConfigureW(GetAncestor(hWnd,GA_ROOT),url);
FreeLibrary(hShlwapi);
}
}
return 0;
}
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
// Subclass the DUIView to reduce its size and reposition the taskbar
LRESULT CALLBACK CExplorerBHO::SubclassDUIViewProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass;
if (uMsg==WM_WINDOWPOSCHANGING && pThis->m_Status8)
{
WINDOWPOS *pPos=(WINDOWPOS*)lParam;
if (!(pPos->flags&SWP_NOSIZE))
{
RECT rc;
GetWindowRect(pThis->m_Status8,&rc);
int height=rc.bottom-rc.top;
pPos->cy-=height;
SetWindowPos(pThis->m_Status8,NULL,pPos->x,pPos->y+pPos->cy,pPos->cx,rc.bottom-rc.top,SWP_NOZORDER);
int parts[]={pPos->cx-height-pThis->m_FileSizeWidth-pThis->m_ZoneWidth,pPos->cx-height-pThis->m_ZoneWidth,-1};
SendMessage(pThis->m_Status8,SB_SETPARTS,pThis->m_ZoneWidth?3:2,(LPARAM)parts);
}
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
// Subclass the rebar in the title bar to handle the title bar Up button
LRESULT CALLBACK CExplorerBHO::SubclassRebarProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->hwndFrom==(HWND)dwRefData && ((NMHDR*)lParam)->code==NM_CUSTOMDRAW)
{
// custom-draw the toolbar. just draw the correct icon and nothing else
NMTBCUSTOMDRAW *pDraw=(NMTBCUSTOMDRAW*)lParam;
if (pDraw->nmcd.dwDrawStage==CDDS_PREPAINT)
return CDRF_NOTIFYITEMDRAW;
if (pDraw->nmcd.dwDrawStage==CDDS_ITEMPREPAINT)
{
CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass;
BOOL comp;
if (SUCCEEDED(DwmIsCompositionEnabled(&comp)) && comp)
FillRect(pDraw->nmcd.hdc,&pDraw->nmcd.rc,(HBRUSH)GetStockObject(BLACK_BRUSH));
if (pDraw->nmcd.uItemState&CDIS_DISABLED)
{
if (pThis->m_IconDisabled)
DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconDisabled,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR);
else
DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconNormal,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR);
}
else if (pDraw->nmcd.uItemState&CDIS_SELECTED)
{
if (pThis->m_IconPressed)
DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconPressed,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR);
else
DrawIconEx(pDraw->nmcd.hdc,1,1,pThis->m_IconNormal,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR);
}
else if (pDraw->nmcd.uItemState&CDIS_HOT)
{
if (pThis->m_IconHot)
DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconHot,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR);
else
DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconNormal,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR);
}
else
DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconNormal,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR);
return CDRF_SKIPDEFAULT;
}
}
if (uMsg==WM_THEMECHANGED)
{
// the button size is reset when the theme changes. force the correct size again
HWND toolbar=(HWND)dwRefData;
RECT rc;
GetClientRect(toolbar,&rc);
PostMessage(toolbar,TB_SETBUTTONSIZE,0,MAKELONG(rc.right,rc.bottom));
}
if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->hwndFrom==(HWND)dwRefData && ((NMHDR*)lParam)->code==TBN_GETINFOTIP)
{
// show the tip for the up button
NMTBGETINFOTIP *pTip=(NMTBGETINFOTIP*)lParam;
Strcpy(pTip->pszText,pTip->cchTextMax,FindTranslation(L"Toolbar.GoUp",L"Up One Level"));
return 0;
}
if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->hwndFrom==(HWND)dwRefData && ((NMHDR*)lParam)->code==NM_RCLICK)
{
NMMOUSE *pInfo=(NMMOUSE*)lParam;
POINT pt=pInfo->pt;
ClientToScreen(pInfo->hdr.hwndFrom,&pt);
ShowSettingsMenu(hWnd,pt.x,pt.y);
return TRUE;
}
if (uMsg==WM_COMMAND && wParam==1)
{
UINT flags=(GetKeyState(VK_CONTROL)<0?SBSP_NEWBROWSER:SBSP_SAMEBROWSER);
((CExplorerBHO*)uIdSubclass)->m_pBrowser->BrowseObject(NULL,flags|SBSP_PARENT);
}
CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass;
if (pThis->m_bRemapBands)
{
// HACK! Explorer doesn't use RB_IDTOINDEX every time it needs to access a particular band. Since we insert the Up button in the second
// position, the rest of the bands get offset and comedy ensues (the search box is not sized properly).
// To fix the issue, we renumber the bands so that from the outside ours appears to be last.
static int remapNewOld[2][4]={{3,0,1,2},{0,3,1,2}};
static int remapOldNew[2][4]={{1,2,3,0},{0,2,3,1}};
if (uMsg==RB_IDTOINDEX || uMsg==RB_HITTEST)
{
// remap the result from RB_IDTOINDEX and RB_HITTEST
LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam);
if (res<0 || res>3) return res;
res=remapNewOld[pThis->m_UpButtonIndex-1][res];
if (lParam && uMsg==RB_HITTEST) ((RBHITTESTINFO*)lParam)->iBand=(int)res;
return res;
}
if (uMsg==RB_GETBANDBORDERS || uMsg==RB_GETBANDINFO || uMsg==RB_GETRECT || uMsg==RB_SETBANDINFO || uMsg==RB_SETBANDWIDTH)
{
// remap wParam for all GET/SET messages
if (wParam>=0 && wParam<=3)
wParam=remapOldNew[pThis->m_UpButtonIndex-1][wParam];
}
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
// Subclass the breadcrumbs to make them show the full path
LRESULT CALLBACK CExplorerBHO::SubclassBreadcrumbProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass;
if (*pThis->m_CurPath)
{
if (uMsg==WM_SETFOCUS)
{
if (wParam)
{
// see if the focus comes from the combo box. if so, most likely Escape was pressed, so just focus the main frame
HWND from=(HWND)wParam;
HWND combo=FindChildWindow(GetParent(GetParent(hWnd)),WC_COMBOBOXEX);
if (combo && (combo==from || IsChild(combo,from)))
{
SetFocus(GetAncestor(hWnd,GA_ROOT));
return 0;
}
}
// when the breadcrumbs are focused, switch to the combobox by simulating a mouse click
RECT rc;
GetClientRect(hWnd,&rc);
LPARAM pos=MAKELONG(rc.right-1,rc.bottom/2);
DefSubclassProc(hWnd,WM_LBUTTONDOWN,MK_LBUTTON,pos);
DefSubclassProc(hWnd,WM_LBUTTONUP,0,pos);
return 0;
}
if (uMsg==WM_LBUTTONDOWN || uMsg==WM_LBUTTONDBLCLK || uMsg==WM_LBUTTONUP)
{
// unless the mouse is clicked on the icon, replace the mouse position with a point on the far right.
// this will cause Explorer to switch to the combobox even when a breadcrumb is clicked
int iconSize=GetSystemMetrics(SM_CXSMICON);
if (!pThis->m_CurIcon || (short)LOWORD(lParam)>iconSize+3)
{
RECT rc;
GetClientRect(hWnd,&rc);
lParam=MAKELONG(rc.right-1,rc.bottom/2);
}
}
if (uMsg==WM_PAINT)
{
// make the breadcrumbs control draw the full path like the XP address bar
RECT rc;
GetClientRect(hWnd,&rc);
PAINTSTRUCT ps;
HDC hdc=BeginPaint(hWnd,&ps);
// we need to use buffered painting because DrawThemeTextEx with DTT_COMPOSITED requires it
// on Vista DTT_COMPOSITED is required so that the black text doesn't get transparent. On Windows 7 regular DrawText seems to work fine
BP_PAINTPARAMS paintParams={sizeof(paintParams)};
paintParams.dwFlags=BPPF_ERASE;
HDC hdcPaint=NULL;
HPAINTBUFFER hBufferedPaint=BeginBufferedPaint(hdc,&rc,BPBF_TOPDOWNDIB,&paintParams,&hdcPaint);
if (hdcPaint)
{
rc.top++;
SendMessage(GetParent(GetParent(hWnd)),WM_PRINTCLIENT,(WPARAM)hdcPaint,PRF_CLIENT);
// draw icon
int iconSize=GetSystemMetrics(SM_CXSMICON);
if (pThis->m_CurIcon)
DrawIconEx(hdcPaint,rc.left+3,(rc.top+rc.bottom-iconSize)/2,pThis->m_CurIcon,iconSize,iconSize,0,NULL,DI_NORMAL);
rc.left+=iconSize+8; // Not a good idea to hard-code number of pixels, but seems to work fine for different DPI settings
// draw path
HFONT font=(HFONT)SendMessage(hWnd,WM_GETFONT,0,0);
HGDIOBJ font0=SelectObject(hdcPaint,font);
SetBkMode(hdcPaint,TRANSPARENT);
SetTextColor(hdcPaint,GetSysColor(COLOR_WINDOWTEXT));
HTHEME theme=GetWindowTheme(hWnd);
BOOL dwm;
if (theme && SUCCEEDED(DwmIsCompositionEnabled(&dwm)) && dwm)
{
DTTOPTS opts={sizeof(opts),DTT_COMPOSITED|DTT_COLORPROP};
opts.iColorPropId = COLOR_WINDOWTEXT;
DrawThemeTextEx(theme,hdcPaint,0,0,pThis->m_CurPath,-1,DT_NOPREFIX|DT_VCENTER|DT_SINGLELINE,&rc,&opts);
}
else
{
DrawText(hdcPaint,pThis->m_CurPath,-1,&rc,DT_NOPREFIX|DT_VCENTER|DT_SINGLELINE);
}
SelectObject(hdcPaint,font0);
EndBufferedPaint(hBufferedPaint,TRUE);
}
EndPaint(hWnd,&ps);
return 0;
}
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
// Subclass the progress bar behind the address bar to remove the history and replace it with a list of parent folders
LRESULT CALLBACK CExplorerBHO::SubclassProgressProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass;
if (uMsg==WM_PARENTNOTIFY && LOWORD(wParam)==WM_CREATE && !pThis->m_ComboBox.m_hWnd)
{
// on Windows 7 the combobox is not created at startup. so listen for child windows being created and update pThis->m_ComboBox
HWND combo=(HWND)lParam;
wchar_t className[256];
GetClassName(combo,className,_countof(className));
if (_wcsicmp(className,WC_COMBOBOXEX)==0)
pThis->m_ComboBox=combo;
}
if (uMsg==WM_COMMAND && (HWND)lParam==pThis->m_ComboBox.m_hWnd)
{
if (HIWORD(wParam)==CBN_DROPDOWN)
{
// on drop down refresh the list
pThis->ClearComboItems();
CComPtr<IShellFolder> pDesktop;
SHGetDesktopFolder(&pDesktop);
{
// add desktop
ITEMIDLIST shEmpty={{0}};
CComString pName;
if (SUCCEEDED(SHGetNameFromIDList((PIDLIST_ABSOLUTE)&shEmpty,SIGDN_DESKTOPABSOLUTEEDITING,&pName)))
{
ComboItem item={ILCloneFull((PIDLIST_ABSOLUTE)&shEmpty),0,CString(pName)};
pThis->m_ComboItems.push_back(item);
}
}
if (dwRefData==2)
{
// enumerate all desktop items
CComPtr<IEnumIDList> pEnum;
if (SUCCEEDED(pDesktop->EnumObjects(NULL,SHCONTF_FOLDERS,&pEnum)) && pEnum)
{
PITEMID_CHILD child;
while (pEnum->Next(1,&child,NULL)==S_OK)
{
STRRET str;
if (SUCCEEDED(pDesktop->GetDisplayNameOf(child,SHGDN_INFOLDER|SHGDN_NORMAL,&str)))
{
CComString pName;
StrRetToStr(&str,child,&pName);
ComboItem item={(PIDLIST_ABSOLUTE)child,1,CString(pName)};
item.sortName=item.name;
pThis->m_ComboItems.push_back(item);
}
else
ILFree(child);
}
}
// sort desktop items
std::sort(pThis->m_ComboItems.begin()+1,pThis->m_ComboItems.end());
}
if (dwRefData==2)
{
// enumerate all computer items
CAbsolutePidl pidlComp;
SHGetKnownFolderIDList(FOLDERID_ComputerFolder,0,NULL,&pidlComp);
int index=1;
for (int i=1;i<(int)pThis->m_ComboItems.size();i++)
if (ILIsEqual(pidlComp,pThis->m_ComboItems[i].pidl))
{
index=i+1;
break;
}
int index0=index;
CComPtr<IShellFolder> pComputer;
pDesktop->BindToObject(pidlComp,NULL,IID_IShellFolder,(void**)&pComputer);
CComPtr<IEnumIDList> pEnum;
if (pComputer && SUCCEEDED(pComputer->EnumObjects(NULL,SHCONTF_FOLDERS,&pEnum)) && pEnum)
{
PITEMID_CHILD child;
while (pEnum->Next(1,&child,NULL)==S_OK)
{
STRRET str;
if (SUCCEEDED(pComputer->GetDisplayNameOf(child,SHGDN_INFOLDER|SHGDN_NORMAL,&str)))
{
CComString pName;
StrRetToStr(&str,child,&pName);
ComboItem item={ILCombine(pidlComp,child),2,CString(pName)};
pThis->m_ComboItems.insert(pThis->m_ComboItems.begin()+index,1,item);
index++;
}
ILFree(child);
}
}
//sort computer items
std::sort(pThis->m_ComboItems.begin()+index0,pThis->m_ComboItems.begin()+index);
}
if (pThis->m_CurPidl)
{
// enumerate all parent items
CAbsolutePidl pidl(pThis->m_CurPidl);
PIDLIST_ABSOLUTE pidlStart=pidl;
int index=0;
for (int i=0;i<(int)pThis->m_ComboItems.size();i++)
{
PIDLIST_ABSOLUTE p=(PIDLIST_ABSOLUTE)ILFindChild(pThis->m_ComboItems[i].pidl,pidl);
if (p)
{
index=i;
pidlStart=p;
}
}
int n=0;
for (PUIDLIST_RELATIVE child=pidl;!ILIsEmpty(child);child=ILGetNext(child))
n++;
int start=n;
for (PUIDLIST_RELATIVE child=pidlStart;!ILIsEmpty(child);child=ILGetNext(child))
start--;
for (int i=start;i<n;i++)
{
wchar_t *pName=NULL;
CComPtr<IShellFolder> pFolder;
PCITEMID_CHILD child;
if (SUCCEEDED(SHBindToParent(pidl,IID_IShellFolder,(void**)&pFolder,&child)))
{
STRRET str;
if (SUCCEEDED(pFolder->GetDisplayNameOf(child,SHGDN_INFOLDER|SHGDN_NORMAL,&str)))
{
CComString pName;
StrRetToStr(&str,child,&pName);
ComboItem item={ILCloneFull(pidl),n-(i-start),CString(pName)};
pThis->m_ComboItems.insert(pThis->m_ComboItems.begin()+index+1,1,item);
}
}
ILRemoveLastID(pidl);
}
}
// add all sorted items to the combobox
COMBOBOXEXITEM item={CBEIF_TEXT|CBEIF_IMAGE|CBEIF_SELECTEDIMAGE|CBEIF_INDENT|CBEIF_LPARAM};
item.iItem=-1;
for (std::vector<ComboItem>::const_iterator it=pThis->m_ComboItems.begin();it!=pThis->m_ComboItems.end();++it)
{
item.iImage=item.iSelectedImage=-1;
item.pszText=(LPWSTR)(LPCWSTR)it->name;
item.iIndent=it->indent;
item.lParam=(LPARAM)it->pidl;
SHFILEINFO info;
if (SHGetFileInfo((LPCTSTR)it->pidl,0,&info,sizeof(info),SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON))
item.iImage=item.iSelectedImage=info.iIcon;
int idx=(int)pThis->m_ComboBox.SendMessage(CBEM_INSERTITEM,'CLSH',(LPARAM)&item);
if (pThis->m_CurPidl && ILIsEqual(it->pidl,pThis->m_CurPidl))
pThis->m_ComboBox.SendMessage(CB_SETCURSEL,idx);
}
return 0;
}
if (HIWORD(wParam)==CBN_CLOSEUP || HIWORD(wParam)==CBN_SELENDCANCEL)
{
// on close clear the list
if (!pThis->m_ComboItems.empty())
{
pThis->m_ComboBox.SetWindowText(pThis->m_CurPath);
pThis->ClearComboItems();
}
if (HIWORD(wParam)==CBN_SELENDCANCEL)
PostMessage(hWnd,pThis->m_NavigateMsg,1,0);
return 0;
}
if (HIWORD(wParam)==CBN_SELENDOK)
{
// when an item is selected, go to it and clear the list (selection with mouse)
int index=(int)pThis->m_ComboBox.SendMessage(CB_GETCURSEL);
if (index>=0)
{
COMBOBOXEXITEM item={CBEIF_LPARAM,index};
pThis->m_ComboBox.SendMessage(CBEM_GETITEM,0,(LPARAM)&item);
pThis->m_NavigatePidl.Clone((PIDLIST_ABSOLUTE)item.lParam);
PostMessage(hWnd,pThis->m_NavigateMsg,0,0);
}
pThis->ClearComboItems();
return 0;
}
}
if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->code==CBEN_BEGINEDIT)
{
// ignore this, so Explorer doesn't add its history to the list
return 0;
}
if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->code==CBEN_ENDEDIT)
{
if (pThis->m_ComboBox.SendMessage(CB_GETDROPPEDSTATE))
{
// when an item is selected, go to it and clear the list (selection with keyboard)
NMCBEENDEDIT *pEdit=(NMCBEENDEDIT*)lParam;
int index=pEdit->iNewSelection;
if (index>=0)
{
COMBOBOXEXITEM item={CBEIF_LPARAM,index};
pThis->m_ComboBox.SendMessage(CBEM_GETITEM,0,(LPARAM)&item);
pThis->m_NavigatePidl.Clone((PIDLIST_ABSOLUTE)item.lParam);
pThis->ClearComboItems();
PostMessage(hWnd,pThis->m_NavigateMsg,0,0);
pThis->m_ComboBox.SendMessage(CB_SHOWDROPDOWN,FALSE);
}
return 0;
}
}
if (uMsg==pThis->m_NavigateMsg)
{
// navigate to the selected item
if (wParam==0 && pThis->m_NavigatePidl && !(pThis->m_CurPidl && ILIsEqual(pThis->m_NavigatePidl,pThis->m_CurPidl)))
pThis->m_pBrowser->BrowseObject(pThis->m_NavigatePidl,SBSP_SAMEBROWSER|SBSP_ABSOLUTE);
else
SetFocus(hWnd);
pThis->m_NavigatePidl.Clear();
}
return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}
static void NewVersionCallback( VersionData &data )
{
wchar_t path[_MAX_PATH];
GetModuleFileName(g_Instance,path,_countof(path));
PathRemoveFileSpec(path);
PathAppend(path,L"Update.exe");
wchar_t cmdLine[1024];
Sprintf(cmdLine,_countof(cmdLine),L"\"%s\" -popup",path);
STARTUPINFO startupInfo={sizeof(startupInfo)};
PROCESS_INFORMATION processInfo;
memset(&processInfo,0,sizeof(processInfo));
if (CreateProcess(path,cmdLine,NULL,NULL,TRUE,0,NULL,NULL,&startupInfo,&processInfo))
{
CloseHandle(processInfo.hThread);
CloseHandle(processInfo.hProcess);
}
}
HRESULT STDMETHODCALLTYPE CExplorerBHO::SetSite( IUnknown *pUnkSite )
{
IObjectWithSiteImpl<CExplorerBHO>::SetSite(pUnkSite);
if (pUnkSite)
{
// hook
GetTlsData()->bho=this;
if (!m_Hook)
{
m_Hook=SetWindowsHookEx(WH_CBT,HookExplorer,NULL,GetCurrentThreadId());
}
CComQIPtr<IServiceProvider> pProvider=pUnkSite;
if (pProvider)
{
pProvider->QueryService(SID_SShellBrowser,IID_IShellBrowser,(void**)&m_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 (DispEvent1::m_dwEventCookie==0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE when the sink is not advised
DispEvent1::DispEventAdvise(m_pWebBrowser,&DIID_DWebBrowserEvents2);
CRegKey regKey;
if (regKey.Open(HKEY_CURRENT_USER,GetSettingsRegPath())!=ERROR_SUCCESS)
regKey.Create(HKEY_CURRENT_USER,GetSettingsRegPath());
DWORD val;
if (regKey.QueryDWORDValue(L"ShowedToolbar",val)!=ERROR_SUCCESS || !val)
{
CComVariant name(L"{553891B7-A0D5-4526-BE18-D3CE461D6310}");
CComVariant show(true);
if (GetSettingBool(L"NoInitialToolbar") || SUCCEEDED(m_pWebBrowser->ShowBrowserBar(&name,&show,NULL)))
regKey.SetDWORDValue(L"ShowedToolbar",1);
}
}
HWND status;
if (m_pBrowser && SUCCEEDED(m_pBrowser->GetControlWindow(FCW_STATUS,&status)))
{
m_TopWindow=GetAncestor(status,GA_ROOT);
if (!GetProp(m_TopWindow,g_LoadedSettingsAtom))
{
SetProp(m_TopWindow,g_LoadedSettingsAtom,(HANDLE)1);
LoadSettings();
}
bool bWin8=(GetWinVersion()>=WIN_VER_WIN8);
m_UpButtonIndex=bWin8?0:GetSettingInt(L"ShowUpButton");
bool bShowCaption=!bWin8 && GetSettingBool(L"ShowCaption");
bool bShowIcon=!bWin8 && GetSettingBool(L"ShowIcon");
if (m_TopWindow && (bShowCaption || bShowIcon))
{
// show the title and the icon for the main Explorer window
DWORD flags=WTNCA_NODRAWCAPTION|WTNCA_NODRAWICON;
if (bShowCaption) flags&=~WTNCA_NODRAWCAPTION;
if (bShowIcon) flags&=~WTNCA_NODRAWICON;
SetWindowThemeNonClientAttributes(m_TopWindow,WTNCA_NODRAWCAPTION|WTNCA_NODRAWICON,flags);
}
// find the TravelBand and the rebar
HWND band=NULL, rebar=NULL;
if (m_TopWindow)
band=FindChildWindow(m_TopWindow,L"TravelBand");
if (band)
rebar=GetParent(band);
bool bRedrawRebar=false;
m_Rebar=rebar;
m_AltD=0;
if (rebar && GetSettingBool(L"HideSearch"))
{
// to remove the Search box, first find the band with ID=2 (or 4 for Win8). Then disable the child control and hide the band
int idx=(int)SendMessage(rebar,RB_IDTOINDEX,bWin8?4:2,0);
if (idx>=0)
{
REBARBANDINFO info={sizeof(info),RBBIM_CHILD};
SendMessage(rebar,RB_GETBANDINFO,idx,(LPARAM)&info);
if (info.hwndChild)
EnableWindow(info.hwndChild,FALSE);
SendMessage(rebar,RB_SHOWBAND,idx,FALSE);
bRedrawRebar=true;
}
}
if (rebar && m_UpButtonIndex)
{
// find the toolbar
HWND toolbar=FindWindowEx(band,NULL,TOOLBARCLASSNAME,NULL);
RECT rc;
GetClientRect(toolbar,&rc);
bool bDef;
int size=GetSettingInt(L"UpIconSize",bDef);
if (bDef)
size=rc.bottom;
m_Toolbar.Create(rebar,NULL,NULL,WS_CHILD|TBSTYLE_TOOLTIPS|TBSTYLE_FLAT|TBSTYLE_CUSTOMERASE|CCS_NODIVIDER|CCS_NOPARENTALIGN|CCS_NORESIZE);
m_Toolbar.SendMessage(TB_SETEXTENDEDSTYLE,0,TBSTYLE_EX_MIXEDBUTTONS);
m_Toolbar.SendMessage(TB_BUTTONSTRUCTSIZE,sizeof(TBBUTTON));
m_Toolbar.SendMessage(TB_SETMAXTEXTROWS,1);
std::vector<HMODULE> modules;
CString str=GetSettingString(L"UpIconNormal");
m_IconNormal=str?LoadIcon(size,str,modules):NULL;
if (m_IconNormal)
{
str=GetSettingString(L"UpIconHot");
m_IconHot=LoadIcon(size,str,modules);
str=GetSettingString(L"UpIconPressed");
m_IconPressed=LoadIcon(size,str,modules);
str=GetSettingString(L"UpIconDisabled");
m_IconDisabled=LoadIcon(size,str,modules);
if (!m_IconDisabled)
m_IconDisabled=CreateDisabledIcon(m_IconNormal,size);
}
else
{
m_IconNormal=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_UP2NORMAL),IMAGE_ICON,size,size,LR_DEFAULTCOLOR);
m_IconHot=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_UP2HOT),IMAGE_ICON,size,size,LR_DEFAULTCOLOR);
m_IconPressed=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_UP2PRESSED),IMAGE_ICON,size,size,LR_DEFAULTCOLOR);
m_IconDisabled=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_UP2DISABLED),IMAGE_ICON,size,size,LR_DEFAULTCOLOR);
}
for (std::vector<HMODULE>::const_iterator it=modules.begin();it!=modules.end();++it)
FreeLibrary(*it);
TBBUTTON button={I_IMAGENONE,1,TBSTATE_ENABLED};
m_Toolbar.SendMessage(TB_ADDBUTTONS,1,(LPARAM)&button);
m_Toolbar.SendMessage(TB_SETBUTTONSIZE,0,MAKELONG(size,size));
m_bRemapBands=true;
SetWindowSubclass(rebar,SubclassRebarProc,(UINT_PTR)this,(DWORD_PTR)m_Toolbar.m_hWnd);
REBARBANDINFO info={sizeof(info),RBBIM_CHILD|RBBIM_ID|RBBIM_CHILDSIZE|RBBIM_IDEALSIZE|RBBIM_SIZE|RBBIM_STYLE};
info.fStyle=RBBS_HIDETITLE|RBBS_NOGRIPPER|RBBS_FIXEDSIZE;
info.hwndChild=m_Toolbar.m_hWnd;
info.cxIdeal=info.cx=info.cxMinChild=size;
info.cyMinChild=size;
info.wID='UBTN';
SendMessage(rebar,RB_INSERTBAND,m_UpButtonIndex-1,(LPARAM)&info);
bRedrawRebar=true;
}
if (rebar)
{
int AddressBarHistory=GetSettingInt(L"AddressBarHistory");
HWND progress=NULL;
HWND breadcrumbs=FindChildWindow(rebar,L"Breadcrumb Parent");
if (breadcrumbs)
{
progress=GetParent(breadcrumbs);
breadcrumbs=FindWindowEx(breadcrumbs,NULL,TOOLBARCLASSNAME,NULL);
m_AltD=(char)GetSettingString(L"AddressAltD")[0];
if (m_AltD>='a' && m_AltD<='z')
m_AltD+='A'-'a';
if (m_AltD<'A' || m_AltD>'Z')
m_AltD=0;
}
m_Breadcrumbs=breadcrumbs;
if (GetSettingBool(L"DisableBreadcrumbs"))
{
// "hide" the breadcrumbs. no, not really. instead of hiding the breadcrumbs we just make them show the full path as text
if (breadcrumbs)
{
m_bNoBreadcrumbs=true;
SetWindowSubclass(breadcrumbs,SubclassBreadcrumbProc,(UINT_PTR)this,0);
}
}
if (progress && AddressBarHistory)
{
m_Progress=progress;
m_ComboBox=FindWindowEx(progress,NULL,WC_COMBOBOXEX,NULL);
SetWindowSubclass(progress,SubclassProgressProc,(UINT_PTR)this,AddressBarHistory);
m_NavigateMsg=RegisterWindowMessage(L"OpenShell.Navigate");
}
}
if (bRedrawRebar)
RedrawWindow(rebar,NULL,NULL,RDW_UPDATENOW|RDW_ALLCHILDREN);
m_UpHotkey=GetSettingInt(L"UpHotkey");
if ((m_AltD || m_UpHotkey) && !m_HookKbd)
{
m_HookKbd=SetWindowsHookEx(WH_KEYBOARD,HookKeyboard,NULL,GetCurrentThreadId());
}
if ((bWin8 && GetSettingBool(L"ShowStatusBar")) || (!bWin8 && GetSettingBool(L"ShowFreeSpace")))
{
DWORD flags=(GetSettingBool(L"ShowInfoTip")?SHOW_INFOTIP:0)|(GetSettingBool(L"ShowFreeSpace")?SHOW_FREE_SPACE:0);
if (bWin8)
{
RECT rc;
GetWindowRect(status,&rc);
m_Status8=CreateWindow(STATUSCLASSNAME,NULL,WS_CHILD|WS_VISIBLE|SBARS_SIZEGRIP|SBARS_TOOLTIPS,0,0,rc.right-rc.left,rc.bottom-rc.top,GetParent(status),NULL,g_Instance,NULL);
HDC hdc=CreateCompatibleDC(NULL);
HGDIOBJ font0=SelectObject(hdc,(HFONT)SendMessage(m_Status8,WM_GETFONT,0,0));
HFONT font=CreateFontSetting(GetSettingString(L"StatusBarFont"),GetDeviceCaps(hdc,LOGPIXELSY));
if (font)
{
SendMessage(m_Status8,WM_SETFONT,(WPARAM)font,0);
SelectObject(hdc,font);
TEXTMETRIC met;
GetTextMetrics(hdc,&met);
int height=GetSystemMetrics(SM_CXSMICON);
if (height<met.tmHeight)
height=met.tmHeight;
SendMessage(m_Status8,SB_SETMINHEIGHT,height,0);
}
SIZE size;
if (!GetTextExtentPoint32(hdc,L"1.000 GB",8,&size))
size.cx=0;
m_FileSizeWidth=size.cx+20+(rc.bottom-rc.top);
if (m_FileSizeWidth<80)
m_FileSizeWidth=80;
m_ZoneWidth=0;
if (GetSettingBool(L"ShowZone"))
{
m_pZoneManager.CoCreateInstance(CLSID_InternetZoneManager,NULL,CLSCTX_INPROC_SERVER);
m_pSecurityManager.CoCreateInstance(CLSID_InternetSecurityManager,NULL,CLSCTX_INPROC_SERVER);
if (m_pZoneManager && m_pSecurityManager)
{
for (DWORD zone=URLZONE_LOCAL_MACHINE;zone<=URLZONE_UNTRUSTED;zone++)
{
ZONEATTRIBUTES attributes={sizeof(attributes)};
if (SUCCEEDED(m_pZoneManager->GetZoneAttributes(zone,&attributes)))
{
if (GetTextExtentPoint32(hdc,attributes.szDisplayName,Strlen(attributes.szDisplayName),&size) && m_ZoneWidth<size.cx)
m_ZoneWidth=size.cx;
}
}
m_ZoneWidth+=50;
}
}
SelectObject(hdc,font0);
DeleteDC(hdc);
SendMessage(m_Status8,SB_SIMPLE,FALSE,0);
SetWindowSubclass(m_Status8,SubclassStatusProc8,(UINT_PTR)this,flags);
}
else
{
SetWindowSubclass(status,SubclassStatusProc,(UINT_PTR)this,flags);
m_bForceRefresh=(GetSettingBool(L"ForceRefreshWin7"));
m_Status=status;
}
}
}
s_AutoNavDelay=GetSettingInt(L"AutoNavDelay");
if (m_TopWindow)
CheckForNewVersion(NULL,COMPONENT_EXPLORER,CHECK_AUTO,NewVersionCallback);
}
}
else
{
// unhook
GetTlsData()->bho=NULL;
if (m_Hook)
UnhookWindowsHookEx(m_Hook);
m_Hook=NULL;
if (m_HookKbd)
UnhookWindowsHookEx(m_HookKbd);
m_HookKbd=NULL;
if (m_Status)
RemoveWindowSubclass(m_Status,SubclassStatusProc,(UINT_PTR)this);
m_Status=NULL;
if (m_Status8)
RemoveWindowSubclass(m_Status8,SubclassStatusProc8,(UINT_PTR)this);
m_Status8=NULL;
if (m_DUIView)
RemoveWindowSubclass(m_DUIView,SubclassDUIViewProc,(UINT_PTR)this);
m_DUIView=NULL;
if (m_Progress)
RemoveWindowSubclass(m_Progress,SubclassProgressProc,(UINT_PTR)this);
m_Progress=NULL;
if (m_bNoBreadcrumbs)
RemoveWindowSubclass(m_Breadcrumbs,SubclassBreadcrumbProc,(UINT_PTR)this);
m_Breadcrumbs=NULL;
m_bNoBreadcrumbs=false;
if (m_Rebar)
RemoveWindowSubclass(m_Rebar,SubclassRebarProc,(UINT_PTR)this);
m_pBrowser=NULL;
if (m_pWebBrowser && DispEvent1::m_dwEventCookie!=0xFEFEFEFE)
DispEvent1::DispEventUnadvise(m_pWebBrowser,&DIID_DWebBrowserEvents2);
m_pWebBrowser=NULL;
if (m_pWebDoc && DispEvent2::m_dwEventCookie!=0xFEFEFEFE)
DispEvent2::DispEventUnadvise(m_pWebDoc,&DIID_DShellFolderViewEvents);
m_pWebDoc=NULL;
if (m_Toolbar.m_hWnd)
m_Toolbar.DestroyWindow();
if (m_IconNormal) DestroyIcon(m_IconNormal); m_IconNormal=NULL;
if (m_IconHot) DestroyIcon(m_IconHot); m_IconHot=NULL;
if (m_IconPressed) DestroyIcon(m_IconPressed); m_IconPressed=NULL;
if (m_IconDisabled) DestroyIcon(m_IconDisabled); m_IconDisabled=NULL;
if (m_CurIcon) DestroyIcon(m_CurIcon); m_CurIcon=NULL;
m_CurPidl.Clear();
m_NavigatePidl.Clear();
if (m_TopWindow) RemoveProp(m_TopWindow,g_LoadedSettingsAtom);
m_TopWindow=NULL;
if (m_Balloon)
{
DestroyWindow(m_Balloon);
m_Balloon=NULL;
}
for (std::map<unsigned int,HICON>::iterator it=m_ZoneIconCache.begin();it!=m_ZoneIconCache.end();++it)
DestroyIcon(it->second);
}
ClearComboItems();
return S_OK;
}
void CExplorerBHO::ClearComboItems( void )
{
for (std::vector<ComboItem>::iterator it=m_ComboItems.begin();it!=m_ComboItems.end();++it)
ILFree(it->pidl);
m_ComboItems.clear();
if (m_ComboBox.m_hWnd)
{
for (int i=(int)m_ComboBox.SendMessage(CB_GETCOUNT)-1;i>=0;i--)
m_ComboBox.SendMessage(CBEM_DELETEITEM,i);
}
}
STDMETHODIMP CExplorerBHO::OnDocumentComplete( IDispatch *pDisp, VARIANT *URL )
{
// this is called when the current folder changes. disable the Up button if this is the desktop folder
bool bDesktop=false;
m_CurPath[0]=0;
if (m_CurIcon) DestroyIcon(m_CurIcon);
m_CurIcon=NULL;
m_CurPidl.Clear();
if (m_pWebBrowser && (m_Status || m_Status8))
{
if (m_pWebDoc && DispEvent2::m_dwEventCookie!=0xFEFEFEFE)
DispEvent2::DispEventUnadvise(m_pWebDoc,&DIID_DShellFolderViewEvents);
m_pWebDoc=NULL;
m_pWebBrowser->get_Document(&m_pWebDoc);
if (m_pWebDoc && DispEvent2::m_dwEventCookie==0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE when the sink is not advised
DispEvent2::DispEventAdvise(m_pWebDoc,&DIID_DShellFolderViewEvents);
if (DispEvent2::m_dwEventCookie!=0xFEFEFEFE)
{
if (m_Status)
SendMessage(m_Status,WM_CLEAR,0,0);
if (m_Status8)
SendMessage(m_Status8,WM_CLEAR,0,0);
}
}
if (m_pBrowser)
{
CComPtr<IShellView> pView;
m_pBrowser->QueryActiveShellView(&pView);
if (pView)
{
CComQIPtr<IFolderView> pFolderView(pView);
if (pFolderView)
{
if (GetSettingBool(L"ShowHeaders"))
{
// ***********************************************************************
// The code to turn on the headers is borrowed from the Explorer7Fixes project under the terms of the MIT license:
// http://github.com/ijprest/Explorer7Fixes - Copyright (c) 2010 Ian Prest
CComQIPtr<IFolderView2> pView2(pFolderView);
if (pView2)
{
// Turn on the sort header!
pView2->SetCurrentFolderFlags(FWF_NOHEADERINALLVIEWS,0);
// It seems the ItemsView doesn't respect the FWF_NOHEADERINALLVIEWS flag
// until the view has been refreshed. Rather than call Refresh, we just
// briefly change the view mode and change it back.
FOLDERVIEWMODE viewMode;
int itemSize=0;
pView2->GetViewModeAndIconSize(&viewMode,&itemSize);
if (viewMode!=FVM_DETAILS)
{
pView2->SetViewModeAndIconSize(viewMode==FVM_LIST?FVM_SMALLICON:FVM_LIST,itemSize);
pView2->SetViewModeAndIconSize(viewMode,itemSize);
}
}
// ***********************************************************************
}
CComPtr<IPersistFolder2> pFolder;
pFolderView->GetFolder(IID_IPersistFolder2,(void**)&pFolder);
if (pFolder && SUCCEEDED(pFolder->GetCurFolder(&m_CurPidl)) && m_CurPidl)
{
if (ILIsEmpty(m_CurPidl))
bDesktop=true; // only the top level has empty PIDL
// find path and icon
// it is possible to get the path and icon from the caption of the main window, but there are 2 problems:
// 1) on Vista the icon is wrong. after you navigate to a new folder the icon switches to some default image and doesn't change any more
// 2) if the user has not checked "display full path in title bar", the caption of the main window is just the current folder name and not the full path
// so do it the hard way and grab it from SHGetNameFromIDList and SHGetFileInfo
if (m_bNoBreadcrumbs)
{
CComString pPath;
if (SUCCEEDED(SHGetNameFromIDList(m_CurPidl,SIGDN_DESKTOPABSOLUTEEDITING,&pPath)))
Strcpy(m_CurPath,_countof(m_CurPath),pPath);
else if (SUCCEEDED(SHGetNameFromIDList(m_CurPidl,SIGDN_FILESYSPATH,&pPath)))
Strcpy(m_CurPath,_countof(m_CurPath),pPath); // just in case DESKTOPABSOLUTE fails let's try the FILESYSPATH. probably not needed
SHFILEINFO info;
if (SUCCEEDED(SHGetFileInfo((LPCTSTR)(PIDLIST_ABSOLUTE)m_CurPidl,0,&info,sizeof(info),SHGFI_ICON|SHGFI_SMALLICON|SHGFI_PIDL)))
m_CurIcon=info.hIcon;
}
}
}
}
if (GetSettingBool(L"HideScrollTip"))
{
CComQIPtr<IFolderViewOptions> pOptions(m_pBrowser);
if (pOptions)
pOptions->SetFolderViewOptions(FVO_NOSCROLLTIPS,FVO_NOSCROLLTIPS);
}
}
if (m_Toolbar.m_hWnd)
m_Toolbar.SendMessage(TB_ENABLEBUTTON,1,bDesktop?0:1);
if (m_pZoneManager && m_pSecurityManager)
{
wchar_t text[256];
text[0]=0;
HICON hIcon=NULL;
if (URL && URL->vt==VT_BSTR)
{
DWORD zone;
if (FAILED(m_pSecurityManager->MapUrlToZone(URL->bstrVal,&zone,0)))
zone=URLZONE_LOCAL_MACHINE;
ZONEATTRIBUTES attributes={sizeof(attributes)};
if (SUCCEEDED(m_pZoneManager->GetZoneAttributes(zone,&attributes)))
{
Strcpy(text,_countof(text),attributes.szDisplayName);
unsigned int key=CalcFNVHash(attributes.szIconPath);
std::map<unsigned int,HICON>::const_iterator it=m_ZoneIconCache.find(key);
if (it!=m_ZoneIconCache.end())
hIcon=it->second;
else
{
wchar_t path[_MAX_PATH];
wchar_t *str=wcschr(attributes.szIconPath,'#');
if (!str) str=wcschr(attributes.szIconPath,',');
int index=0;
if (str)
{
index=_wtol(str+1);
*str=0;
}
int iconSize=GetSystemMetrics(SM_CXSMICON);
Strcpy(path,_countof(path),attributes.szIconPath);
if (PathIsRelative(path))
PathFindOnPath(path,NULL);
if (index==0)
hIcon=(HICON)LoadImage(NULL,path,IMAGE_ICON,iconSize,iconSize,LR_LOADFROMFILE);
else
{
HMODULE hModule=LoadLibraryEx(path,NULL,LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if (hModule)
{
hIcon=(HICON)LoadImage(hModule,MAKEINTRESOURCE(index),IMAGE_ICON,iconSize,iconSize,0);
FreeLibrary(hModule);
}
}
m_ZoneIconCache[key]=hIcon;
}
}
}
SendMessage(m_Status8,SB_SETTEXT,PART_ZONE,(LPARAM)text);
SendMessage(m_Status8,SB_SETICON,PART_ZONE,(LPARAM)hIcon);
}
return S_OK;
}
STDMETHODIMP CExplorerBHO::OnQuit( void )
{
if (m_pWebBrowser && DispEvent1::m_dwEventCookie!=0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE, when the sink is not advised
DispEvent1::DispEventUnadvise(m_pWebBrowser,&DIID_DWebBrowserEvents2);
if (m_pWebDoc && DispEvent2::m_dwEventCookie!=0xFEFEFEFE)
DispEvent2::DispEventUnadvise(m_pWebDoc,&DIID_DShellFolderViewEvents);
return S_OK;
}
STDMETHODIMP CExplorerBHO::OnSelChanged( void )
{
if (m_Status)
SendMessage(m_Status,WM_CLEAR,0,0);
if (m_Status8)
SendMessage(m_Status8,WM_CLEAR,0,0);
return S_OK;
}
bool ShowTreeProperties( HWND hwndTree )
{
CAbsolutePidl pidl;
GetTreeItemPidl(TreeView_GetSelection(hwndTree),hwndTree,&pidl);
if (pidl)
{
// show properties
SHELLEXECUTEINFO execute={sizeof(execute),SEE_MASK_IDLIST|SEE_MASK_INVOKEIDLIST,NULL,L"properties"};
execute.lpIDList=pidl;
execute.nShow=SW_SHOWNORMAL;
ShellExecuteEx(&execute);
return true;
}
return false;
}