mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-11 17:37:22 +10:00
1577 lines
52 KiB
C++
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 (GetWinVersion()==WIN_VER_WIN7 && 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;
|
|
}
|