Click here to Skip to main content
15,892,005 members
Articles / Desktop Programming / ATL

Classic Shell

Rate me:
Please Sign up or sign in to vote.
4.99/5 (135 votes)
23 Feb 2010MIT33 min read 969.4K   10K   195  
Classic Start menu and other shell features for Windows 7 and Vista.
// Classic Shell (c) 2009-2010, Ivo Beltchev
// The sources for Classic Shell are distributed under the MIT open source license

// ExplorerBand.cpp : Implementation of CExplorerBand

#include "stdafx.h"
#include "ExplorerBand.h"
#include "resource.h"
#include "ExplorerBHO.h"
#include "ParseSettings.h"
#include "GlobalSettings.h"
#include "TranslationSettings.h"
#include "Settings.h"
#include "dllmain.h"

///////////////////////////////////////////////////////////////////////////////

// CBandWindow - the parent window of the toolbar

const CBandWindow::StdToolbarItem CBandWindow::s_StdItems[]={
	{ID_GOUP,"Toolbar.GoUp",L"Up One Level",46,NULL,NULL,L",2",L",3"},
	{ID_CUT,"Toolbar.Cut",L"Cut",16762},
	{ID_COPY,"Toolbar.Copy",L"Copy",243},
	{ID_PASTE,"Toolbar.Paste",L"Paste",16763},
	{ID_DELETE,"Toolbar.Delete",L"Delete",240},
	{ID_PROPERTIES,"Toolbar.Properties",L"Properties",253},
	{ID_EMAIL,"Toolbar.Email",L"E-mail the selected items",265},
	{ID_SETTINGS,"Toolbar.Settings",L"Classic Explorer Settings",210,NULL,NULL,L",1"},
};

static struct
{
	const wchar_t *name;
	int id;
} g_StdCommands[]={
	{L"up",CBandWindow::ID_GOUP},
	{L"cut",CBandWindow::ID_CUT},
	{L"copy",CBandWindow::ID_COPY},
	{L"paste",CBandWindow::ID_PASTE},
	{L"delete",CBandWindow::ID_DELETE},
	{L"properties",CBandWindow::ID_PROPERTIES},
	{L"email",CBandWindow::ID_EMAIL},
	{L"settings",CBandWindow::ID_SETTINGS},
	{L"moveto",CBandWindow::ID_MOVETO},
	{L"copyto",CBandWindow::ID_COPYTO},
	{L"undo",CBandWindow::ID_UNDO},
	{L"redo",CBandWindow::ID_REDO},
	{L"selectall",CBandWindow::ID_SELECTALL},
	{L"invertselection",CBandWindow::ID_INVERT},
	{L"back",CBandWindow::ID_GOBACK},
	{L"forward",CBandWindow::ID_GOFORWARD},
	{L"refresh",CBandWindow::ID_REFRESH},
};

bool CBandWindow::ParseToolbarItem( const wchar_t *name, StdToolbarItem &item )
{
	wchar_t text[256];
	swprintf_s(text,L"%s.Command",name);
	const wchar_t *str=FindSetting(text);
	if (!str) return false;

	item.id=ID_SEPARATOR;
	for (int i=0;i<_countof(g_StdCommands);i++)
		if (_wcsicmp(str,g_StdCommands[i].name)==0)
		{
			item.id=g_StdCommands[i].id;
			break;
		}
	if (item.id==ID_SEPARATOR)
	{
		item.id=ID_CUSTOM;
		item.command=str;
	}

	swprintf_s(text,L"%s.Icon",name);
	str=FindSetting(text);
	if (!str) return false;
	item.iconPath=str;

	swprintf_s(text,L"%s.IconDisabled",name);
	item.iconPathD=FindSetting(text);

	swprintf_s(text,L"%s.Tip",name);
	str=FindSetting(text);
	if (str)
	{
		if (str[0]=='$')
			item.tip=FindTranslation(str+1,NULL);
		else
			item.tip=str;
	}

	swprintf_s(text,L"%s.Name",name);
	str=FindSetting(text);
	if (str)
	{
		if (str[0]=='$')
			item.name=FindTranslation(str+1,NULL);
		else
			item.name=str;
	}

	return true;
}

void CBandWindow::ParseToolbar( DWORD enabled )
{
	m_Items.clear();
	const wchar_t *str=FindSetting("ToolbarItems");
	if (str)
	{
		// custom toolbar
		while (*str)
		{
			wchar_t token[256];
			str=GetToken(str,token,_countof(token),L", \t");
			StdToolbarItem item={ID_SEPARATOR};
			if (_wcsicmp(token,L"SEPARATOR")!=0)
			{
				if (!ParseToolbarItem(token,item))
					continue;
				if (item.id==ID_CUSTOM)
				{
					item.id=ID_CUSTOM+(int)m_Items.size();
					item.regName=token;
				}
			}
			m_Items.push_back(item);
		}
	}
	else
	{
		// standard toolbar
		for (int i=0;i<_countof(s_StdItems);i++)
		{
			if (enabled&(1<<s_StdItems[i].id))
			{
				m_Items.push_back(s_StdItems[i]);
				StdToolbarItem &item=m_Items[m_Items.size()-1];
				item.tip=FindTranslation(item.tipKey,item.tip);
			}
		}
	}
	if (m_Items.empty())
	{
		// make sure there is at least one button
		m_Items.push_back(s_StdItems[_countof(s_StdItems)-1]);
		m_Items[0].tip=FindTranslation(m_Items[0].tipKey,m_Items[0].tip);
	}
}

LRESULT CBandWindow::OnCreate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
	CRegKey regSettings;
	if (regSettings.Open(HKEY_CURRENT_USER,L"Software\\IvoSoft\\ClassicExplorer")!=ERROR_SUCCESS)
		regSettings.Create(HKEY_CURRENT_USER,L"Software\\IvoSoft\\ClassicExplorer");

	DWORD BigButtons;
	if (regSettings.QueryDWORDValue(L"BigButtons",BigButtons)!=ERROR_SUCCESS)
		BigButtons=0;

	if (FindSetting("ToolbarItems"))
	{
		ParseToolbar(0);
	}
	else
	{
		DWORD ToolbarButtons=0;
		if (regSettings.QueryDWORDValue(L"ToolbarButtons",ToolbarButtons)!=ERROR_SUCCESS)
			ToolbarButtons=DEFAULT_BUTTONS|((ID_LAST-1)<<24);
		if (!(ToolbarButtons&0xFF000000)) ToolbarButtons|=0x07000002; // for backwards compatibility (when there were 7 buttons the button count was not saved)
		unsigned int mask1=(((2<<(ToolbarButtons>>24))-1)&~1); // bits to keep
		unsigned int mask2=(((2<<ID_LAST)-1)&~1)&~mask1; // bits to replace with defaults
		ToolbarButtons=(ToolbarButtons&mask1)|(DEFAULT_BUTTONS&mask2)|((ID_LAST-1)<<24);
		if ((ToolbarButtons&0xFFFFFF)==0)
			ToolbarButtons|=1<<DEFAULT_ONLY_BUTTON;
		ParseToolbar(ToolbarButtons);
	}

	bool bName=false;
	bool bList=FindSettingBool("ToolbarListMode",false);
	for (std::vector<StdToolbarItem>::const_iterator it=m_Items.begin();it!=m_Items.end();++it)
		if (it->name)
		{
			bName=true;
			break;
		}

	// create the toolbar
	if (bName && !bList)
		m_Toolbar=CreateWindow(TOOLBARCLASSNAME,L"",WS_CHILD|TBSTYLE_TOOLTIPS|TBSTYLE_FLAT|CCS_NODIVIDER|CCS_NOPARENTALIGN|CCS_NORESIZE,0,0,10,10,m_hWnd,(HMENU)101,g_Instance,NULL);
	else
		m_Toolbar=CreateWindow(TOOLBARCLASSNAME,L"",WS_CHILD|TBSTYLE_TOOLTIPS|TBSTYLE_FLAT|TBSTYLE_LIST|CCS_NODIVIDER|CCS_NOPARENTALIGN|CCS_NORESIZE,0,0,10,10,m_hWnd,(HMENU)101,g_Instance,NULL);

	m_Toolbar.SendMessage(TB_SETEXTENDEDSTYLE,0,TBSTYLE_EX_MIXEDBUTTONS);
	m_Toolbar.SendMessage(TB_BUTTONSTRUCTSIZE,sizeof(TBBUTTON));
	m_Toolbar.SendMessage(TB_SETMAXTEXTROWS,1);

	int iconSize=0;
	if (BigButtons)
	{
		const wchar_t *str=FindSetting("LargeIconSize");
		if (str) iconSize=_wtol(str);
	}
	else
	{
		const wchar_t *str=FindSetting("SmallIconSize");
		if (str) iconSize=_wtol(str);
	}

	if (iconSize==0)
	{
		// pick icon size based on the DPI setting
		HDC hdc=::GetDC(NULL);
		int dpi=GetDeviceCaps(hdc,LOGPIXELSY);
		::ReleaseDC(NULL,hdc);
		if (dpi>=120)
			iconSize=BigButtons?32:24;
		else
			iconSize=BigButtons?24:16;
	}
	else if (iconSize<8) iconSize=8;
	else if (iconSize>128) iconSize=128;

	m_Enabled=ImageList_Create(iconSize,iconSize,ILC_COLOR32|ILC_MASK|(IsLanguageRTL()?ILC_MIRROR:0),(int)m_Items.size(),2);
	m_Disabled=ImageList_Create(iconSize,iconSize,ILC_COLOR32|ILC_MASK|(IsLanguageRTL()?ILC_MIRROR:0),(int)m_Items.size(),2);

	HMODULE hShell32=GetModuleHandle(L"Shell32.dll");
	std::vector<HMODULE> modules;

	bool bSame=FindSettingBool("ToolbarSameSize",false);

	// create buttons
	std::vector<TBBUTTON> buttons(m_Items.size());
	for (int i=0;i<(int)m_Items.size();i++)
	{
		const StdToolbarItem &item=m_Items[i];
		TBBUTTON &button=buttons[i];

		button.idCommand=item.id;
		button.dwData=i;

		if (item.id==ID_SEPARATOR)
			button.fsStyle=BTNS_SEP;
		else
		{
			button.iBitmap=I_IMAGENONE;
			if (!item.iconPath || _wcsicmp(item.iconPath,L"NONE")!=0)
			{
				HICON hIcon=LoadIcon(iconSize,item.iconPath,item.icon,modules,hShell32);
				if (!hIcon)
					hIcon=(HICON)LoadImage(hShell32,MAKEINTRESOURCE(1),IMAGE_ICON,iconSize,iconSize,LR_DEFAULTCOLOR);
				if (hIcon)
				{
					button.iBitmap=ImageList_AddIcon(m_Enabled,hIcon);
					HICON hIcon2=item.iconPathD?LoadIcon(iconSize,item.iconPathD,0,modules,hShell32):NULL;
					if (!hIcon2)
						hIcon2=CreateDisabledIcon(hIcon,iconSize);
					int idx=ImageList_AddIcon(m_Disabled,hIcon2);
					ATLASSERT(button.iBitmap==idx);
					DestroyIcon(hIcon);
					DestroyIcon(hIcon2);
				}
			}

			button.fsState=(item.id!=ID_SETTINGS || FindSettingBool("EnableSettings",true))?TBSTATE_ENABLED:0;
			button.fsStyle=BTNS_BUTTON|BTNS_NOPREFIX;
			if (!bSame)
				button.fsStyle|=BTNS_AUTOSIZE;
			if (item.name)
			{
				button.fsStyle|=BTNS_SHOWTEXT;
				button.iString=(INT_PTR)item.name;
			}
		}
	}

	for (std::vector<HMODULE>::const_iterator it=modules.begin();it!=modules.end();++it)
		FreeLibrary(*it);

	// add buttons
	HIMAGELIST old=(HIMAGELIST)m_Toolbar.SendMessage(TB_SETIMAGELIST,0,(LPARAM)m_Enabled);
	if (old) ImageList_Destroy(old);
	old=(HIMAGELIST)m_Toolbar.SendMessage(TB_SETDISABLEDIMAGELIST,0,(LPARAM)m_Disabled);
	if (old) ImageList_Destroy(old);
	m_Toolbar.SendMessage(TB_ADDBUTTONS,buttons.size(),(LPARAM)&buttons[0]);
	SendMessage(WM_CLEAR);
	return 0;
}

LRESULT CBandWindow::OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
	ImageList_Destroy(m_Enabled);
	ImageList_Destroy(m_Disabled);
	bHandled=FALSE;
	return 0;
}

LRESULT CBandWindow::OnUpdateUI( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
	// update the state of the custom buttons based on the registry settings
	CRegKey regSettings;
	if (regSettings.Open(HKEY_CURRENT_USER,L"Software\\IvoSoft\\ClassicExplorer")==ERROR_SUCCESS)
	{
		for (std::vector<StdToolbarItem>::const_iterator it=m_Items.begin();it!=m_Items.end();++it)
		{
			if (!it->regName.empty())
			{
				DWORD val;
				if (regSettings.QueryDWORDValue(it->regName.c_str(),val)!=ERROR_SUCCESS)
					val=0;
				m_Toolbar.SendMessage(TB_ENABLEBUTTON,it->id,(val&1)?0:1);
				m_Toolbar.SendMessage(TB_CHECKBUTTON,it->id,(val&2)?1:0);
			}
		}
	}
	return 0;
}

// Go to the parent folder
LRESULT CBandWindow::OnNavigate( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
	if (m_pBrowser)
	{
		UINT flags=(GetKeyState(VK_CONTROL)<0?SBSP_NEWBROWSER:SBSP_SAMEBROWSER);
		if (wID==ID_GOUP)
			m_pBrowser->BrowseObject(NULL,flags|SBSP_PARENT);
		if (wID==ID_GOBACK)
			m_pBrowser->BrowseObject(NULL,flags|SBSP_NAVIGATEBACK);
		if (wID==ID_GOFORWARD)
			m_pBrowser->BrowseObject(NULL,flags|SBSP_NAVIGATEFORWARD);
	}

	return TRUE;
}

void CBandWindow::SendShellTabCommand( int command )
{
	// sends a command to the ShellTabWindowClass window
	for (CWindow parent=GetParent();parent.m_hWnd;parent=parent.GetParent())
	{
		// find a parent window with class ShellTabWindowClass
		wchar_t name[256];
		GetClassName(parent.m_hWnd,name,_countof(name));
		if (_wcsicmp(name,L"ShellTabWindowClass")==0)
		{
			parent.SendMessage(WM_COMMAND,command);
			break;
		}
	}
}

// Executes a cut/copy/paste/delete command
LRESULT CBandWindow::OnToolbarCommand( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
	if (wID>=ID_CUSTOM)
	{
		int idx=wID-ID_CUSTOM;
		wchar_t buf[2048];
		wcscpy_s(buf,m_Items[idx].command);
		DoEnvironmentSubst(buf,_countof(buf));
		wchar_t *pBuf=buf;
		bool bArg1=wcsstr(buf,L"%1")!=NULL;
		bool bArg2=wcsstr(buf,L"%2")!=NULL;
		wchar_t path[_MAX_PATH];
		wchar_t file[_MAX_PATH];
		path[0]=file[0]=0;

		CComPtr<IShellView> pView;
		if (SUCCEEDED(m_pBrowser->QueryActiveShellView(&pView)))
		{
			CComPtr<IPersistFolder2> pFolder;
			LPITEMIDLIST pidl;
			CComQIPtr<IFolderView> pView2=pView;
			if (pView2 && SUCCEEDED(pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder)) && SUCCEEDED(pFolder->GetCurFolder(&pidl)))
			{
				// get current path
				SHGetPathFromIDList(pidl,path);
				if (bArg2)
				{
					CComPtr<IEnumIDList> pEnum;
					int count;
					// if only one file is selected get the file name (%2)
					if (SUCCEEDED(pView2->ItemCount(SVGIO_SELECTION,&count)) && count==1 && SUCCEEDED(pView2->Items(SVGIO_SELECTION,IID_IEnumIDList,(void**)&pEnum)) && pEnum)
					{
						PITEMID_CHILD child;
						if (pEnum->Next(1,&child,NULL)==S_OK)
						{
							LPITEMIDLIST full=ILCombine(pidl,child);
							SHGetPathFromIDList(full,file);
							ILFree(child);
							ILFree(full);
						}
					}
				}
				ILFree(pidl);
			}
		}

		if (bArg1 || bArg2)
		{
			// expand environment variables, %1, %2
			DWORD_PTR args[100]={(DWORD_PTR)path,(DWORD_PTR)file};
			FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_ARGUMENT_ARRAY|FORMAT_MESSAGE_FROM_STRING,buf,0,0,(LPWSTR)&pBuf,0,(va_list*)args);
		}

		wchar_t exe[_MAX_PATH];
		const wchar_t *params=GetToken(pBuf,exe,_countof(exe),L" ");
		ShellExecute(NULL,NULL,exe,params,path,SW_SHOWNORMAL);
		if (pBuf!=buf)
			LocalFree(pBuf);
		return TRUE;
	}
	// check if the focus is on the tree side or on the list side
	CWindow focus=GetFocus();
	wchar_t name[256];
	GetClassName(focus,name,_countof(name));
	CWindow parent=focus.GetParent();
	if (_wcsicmp(name,WC_TREEVIEW)==0)
	{
		// send these commands to the parent of the tree view
		if (wID==ID_CUT)
			parent.SendMessage(WM_COMMAND,41025);
		if (wID==ID_COPY)
			parent.SendMessage(WM_COMMAND,41026);
		if (wID==ID_PASTE)
			parent.SendMessage(WM_COMMAND,41027);
		if (wID==ID_DELETE)
			parent.SendMessage(WM_COMMAND,40995);
		if (wID==ID_PROPERTIES)
			ShowTreeProperties(focus.m_hWnd);
	}
	else
	{
		GetClassName(parent,name,_countof(name));
		if (_wcsicmp(name,L"SHELLDLL_DefView")==0)
		{
			// send these commands to the SHELLDLL_DefView window
			if (wID==ID_CUT)
			{
				parent.SendMessage(WM_COMMAND,28696);
				focus.InvalidateRect(NULL);
			}
			if (wID==ID_COPY)
				parent.SendMessage(WM_COMMAND,28697);
			if (wID==ID_PASTE)
				parent.SendMessage(WM_COMMAND,28698);
			if (wID==ID_DELETE)
				parent.SendMessage(WM_COMMAND,28689);
			if (wID==ID_PROPERTIES)
				parent.SendMessage(WM_COMMAND,28691);
			if (wID==ID_COPYTO)
				parent.SendMessage(WM_COMMAND,28702);
			if (wID==ID_MOVETO)
				parent.SendMessage(WM_COMMAND,28703);
		}
	}

	if (wID==ID_UNDO)
		SendShellTabCommand(28699);
	if (wID==ID_REDO)
		SendShellTabCommand(28704);
	if (wID==ID_SELECTALL)
		SendShellTabCommand(28705);
	if (wID==ID_INVERT)
		SendShellTabCommand(28706);
	if (wID==ID_REFRESH)
		SendShellTabCommand(41504);

	return TRUE;
}

LRESULT CBandWindow::OnEmail( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
	const IID CLSID_SendMail={0x9E56BE60,0xC50F,0x11CF,{0x9A,0x2C,0x00,0xA0,0xC9,0x0A,0x90,0xCE}};

	CComPtr<IShellView> pView;
	if (FAILED(m_pBrowser->QueryActiveShellView(&pView))) return TRUE;

	// check if there is anything selected
	CComQIPtr<IFolderView> pView2=pView;
	int count;
	if (pView2 && SUCCEEDED(pView2->ItemCount(SVGIO_SELECTION,&count)) && count==0) return TRUE;

	// get the data object
	CComPtr<IDataObject> pDataObj;
	if (FAILED(pView->GetItemObject(SVGIO_SELECTION,IID_IDataObject,(void**)&pDataObj))) return TRUE;
	CComQIPtr<IAsyncOperation> pAsync=pDataObj;
	if (pAsync)
		pAsync->SetAsyncMode(FALSE);

	// drop into the SendMail handler
	CComPtr<IDropTarget> pDropTarget;
	if (SUCCEEDED(CoCreateInstance(CLSID_SendMail,NULL,CLSCTX_ALL,IID_IDropTarget,(void **)&pDropTarget)))
	{
		POINTL pt={0,0};
		DWORD dwEffect=0;
		pDropTarget->DragEnter(pDataObj,MK_LBUTTON,pt,&dwEffect);
		pDropTarget->Drop(pDataObj,0,pt,&dwEffect);
	}
	return TRUE;
}

// Show the settings dialog
LRESULT CBandWindow::OnSettings( WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled )
{
#ifndef BUILD_SETUP
	if (GetKeyState(VK_SHIFT)<0)
		*(int*)0=0; // force a crash if Shift is pressed. Makes it easy to restart explorer.exe
#endif
	ShowSettings(m_hWnd);
	return TRUE;
}

LRESULT CBandWindow::OnRClick( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
{
	NMMOUSE *pInfo=(NMMOUSE*)pnmh;
	POINT pt=pInfo->pt;
	{
		RECT rc;
		int count=(int)m_Toolbar.SendMessage(TB_BUTTONCOUNT);
		m_Toolbar.SendMessage(TB_GETITEMRECT,count-1,(LPARAM)&rc);
		if (pt.x>rc.right)
			return 0;
	}
	m_Toolbar.ClientToScreen(&pt);
	ShowSettingsMenu(m_hWnd,pt.x,pt.y);
	return 1;
}

LRESULT CBandWindow::OnGetInfoTip( int idCtrl, LPNMHDR pnmh, BOOL& bHandled )
{
	NMTBGETINFOTIP *pTip=(NMTBGETINFOTIP*)pnmh;
	const StdToolbarItem &item=m_Items[pTip->lParam];
	if (item.tip)
	{
		// show the tip for the standard item
		wcscpy_s(pTip->pszText,pTip->cchTextMax,item.tip);
	}
	return 0;
}

void CBandWindow::UpdateToolbar( void )
{
	// disable the Up button if we are at the top level
	bool bDesktop=false;
	if (m_pBrowser)
	{
		CComPtr<IShellView> pView;
		m_pBrowser->QueryActiveShellView(&pView);
		if (pView)
		{
			CComQIPtr<IFolderView> pView2=pView;
			if (pView2)
			{
				CComPtr<IPersistFolder2> pFolder;
				pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder);
				if (pFolder)
				{
					LPITEMIDLIST pidl;
					pFolder->GetCurFolder(&pidl);
					if (ILIsEmpty(pidl))
						bDesktop=true; // only the top level has empty PIDL
					CoTaskMemFree(pidl);
				}
			}
		}
	}
	m_Toolbar.SendMessage(TB_ENABLEBUTTON,CBandWindow::ID_GOUP,bDesktop?0:1);
}

///////////////////////////////////////////////////////////////////////////////

// CExplorerBand - adds a toolbar band to Windows Explorer with 2 buttons - "Up" and "Settings"

CExplorerBand::CExplorerBand( void )
{
	m_bSubclassRebar=(LOWORD(GetVersion())==0x0106); // Windows 7
	m_bSubclassedRebar=false;
}

// Subclasses the rebar control on Windows 7. Makes sure the RBBS_BREAK style is properly set. Windows 7 has a bug
// that forces RBBS_BREAK for every rebar band
LRESULT CALLBACK CExplorerBand::RebarSubclassProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
	if (uMsg==RB_SETBANDINFO || uMsg==RB_INSERTBAND)
	{
		REBARBANDINFO *pInfo=(REBARBANDINFO*)lParam;
		if ((pInfo->hwndChild==(HWND)dwRefData) && (pInfo->fMask&RBBIM_STYLE))
		{
			if (((CExplorerBand*)uIdSubclass)->m_bBandNewLine)
				pInfo->fStyle|=RBBS_BREAK;
			else
				pInfo->fStyle&=~RBBS_BREAK;
		}
	}
	return DefSubclassProc(hWnd,uMsg,wParam,lParam);
}

// IDeskBand
STDMETHODIMP CExplorerBand::GetBandInfo( DWORD dwBandID, DWORD dwViewMode, DESKBANDINFO* pdbi )
{
	// initializes the band
	if (m_bSubclassRebar && !m_bSubclassedRebar)
	{
		CRegKey regSettings;
		if (regSettings.Open(HKEY_CURRENT_USER,L"Software\\IvoSoft\\ClassicExplorer")!=ERROR_SUCCESS)
			regSettings.Create(HKEY_CURRENT_USER,L"Software\\IvoSoft\\ClassicExplorer");

		DWORD NewLine;
		if (regSettings.QueryDWORDValue(L"NewLine",NewLine)!=ERROR_SUCCESS)
			NewLine=0;
		m_bBandNewLine=NewLine!=0;

		HWND parent=GetParent(m_BandWindow.GetToolbar());
		wchar_t className[256];
		GetClassName(parent,className,_countof(className));
		if (_wcsicmp(className,REBARCLASSNAME)==0)
		{
			SetWindowSubclass(parent,RebarSubclassProc,(UINT_PTR)this,(DWORD_PTR)m_BandWindow.GetToolbar());
			m_bSubclassedRebar=true;
		}
	}
	RECT rc;
	int count=(int)SendMessage(m_BandWindow.GetToolbar(),TB_BUTTONCOUNT,0,0);
	SendMessage(m_BandWindow.GetToolbar(),TB_GETITEMRECT,count-1,(LPARAM)&rc);

	if (pdbi)
	{
		if (pdbi->dwMask&DBIM_MINSIZE)
		{
			pdbi->ptMinSize.x=rc.right;
			pdbi->ptMinSize.y=rc.bottom;
		}
		if (pdbi->dwMask&DBIM_MAXSIZE)
		{
			pdbi->ptMaxSize.x=0; // ignored
			pdbi->ptMaxSize.y=-1;	// unlimited
		}
		if (pdbi->dwMask&DBIM_INTEGRAL)
		{
			pdbi->ptIntegral.x=0; // not sizeable
			pdbi->ptIntegral.y=0; // not sizeable
		}
		if (pdbi->dwMask&DBIM_ACTUAL)
		{
			pdbi->ptActual.x=rc.right;
			pdbi->ptActual.y=rc.bottom;
		}
		if (pdbi->dwMask&DBIM_TITLE)
		{
			*pdbi->wszTitle=0; // no title
		}
		if (pdbi->dwMask&DBIM_BKCOLOR)
		{
			//Use the default background color by removing this flag.
			pdbi->dwMask&=~DBIM_BKCOLOR;
		}
	}
	return S_OK;
}

// IOleWindow
STDMETHODIMP CExplorerBand::GetWindow( HWND* phwnd )
{
	if (!phwnd)
		return E_INVALIDARG;
	*phwnd=m_BandWindow.GetToolbar();
	return S_OK;
}

STDMETHODIMP CExplorerBand::ContextSensitiveHelp( BOOL fEnterMode )
{
	return S_OK;
}

// IDockingWindow
STDMETHODIMP CExplorerBand::CloseDW( unsigned long dwReserved )
{
	ShowDW(FALSE);
	return S_OK;
}

STDMETHODIMP CExplorerBand::ResizeBorderDW( const RECT* prcBorder, IUnknown* punkToolbarSite, BOOL fReserved )
{
	// Not used by any band object.
	return E_NOTIMPL;
}

STDMETHODIMP CExplorerBand::ShowDW( BOOL fShow )
{
	if (m_bSubclassedRebar)
	{
		// on Windows 7 get the current RBBS_BREAK state and save it in the registry to be restored later
		HWND parent=GetParent(m_BandWindow.GetToolbar());
		int n=(int)SendMessage(parent,RB_GETBANDCOUNT,0,0);
		for (int i=0;i<n;i++)
		{
			REBARBANDINFO info={sizeof(info),RBBIM_STYLE|RBBIM_CHILD};
			SendMessage(parent,RB_GETBANDINFO,i,(LPARAM)&info);
			if (info.hwndChild==m_BandWindow.GetToolbar())
			{
				m_bBandNewLine=(info.fStyle&RBBS_BREAK)!=0;
				CRegKey regSettings;
				if (regSettings.Open(HKEY_CURRENT_USER,L"Software\\IvoSoft\\ClassicExplorer")!=ERROR_SUCCESS)
					regSettings.Create(HKEY_CURRENT_USER,L"Software\\IvoSoft\\ClassicExplorer");

				regSettings.SetDWORDValue(L"NewLine",m_bBandNewLine?1:0);
				break;
			}
		}
	}
	ShowWindow(m_BandWindow.GetToolbar(),fShow?SW_SHOW:SW_HIDE);
	return S_OK;
}

// IObjectWithSite
STDMETHODIMP CExplorerBand::SetSite( IUnknown* pUnkSite )
{
	IObjectWithSiteImpl<CExplorerBand>::SetSite(pUnkSite);

	if (m_BandWindow.IsWindow())
		m_BandWindow.DestroyWindow();
	m_BandWindow.SetBrowser(NULL);
	if (m_bSubclassedRebar)
		RemoveWindowSubclass(GetParent(m_BandWindow.GetToolbar()),RebarSubclassProc,(UINT_PTR)this);
	m_bSubclassedRebar=false;

	if (m_pWebBrowser && m_dwEventCookie!=0xFEFEFEFE)
		DispEventUnadvise(m_pWebBrowser,&DIID_DWebBrowserEvents2);
	m_pWebBrowser=NULL;

	//If punkSite is not NULL, a new site is being set.
	if (pUnkSite)
	{
extern void ReadIniFile( bool bStartup );
		ReadIniFile(false);

		//Get the parent window.
		HWND hWndParent=NULL;

		CComQIPtr<IOleWindow> pOleWindow=pUnkSite;
		if (pOleWindow)
			pOleWindow->GetWindow(&hWndParent);

		if (!IsWindow(hWndParent))
			return E_FAIL;

		m_BandWindow.Create(hWndParent,NULL,NULL,WS_CHILD);
		if (!m_BandWindow.IsWindow())
			return E_FAIL;

		CComQIPtr<IServiceProvider> pProvider=pUnkSite;

		if (pProvider)
		{
			CComPtr<IShellBrowser> pBrowser;
			pProvider->QueryService(SID_SShellBrowser,IID_IShellBrowser,(void**)&pBrowser);
			m_BandWindow.SetBrowser(pBrowser);

			// listen for web browser notifications. we only care about DISPID_DOWNLOADCOMPLETE and DISPID_ONQUIT
			pProvider->QueryService(SID_SWebBrowserApp,IID_IWebBrowser2,(void**)&m_pWebBrowser);
			if (m_pWebBrowser)
			{
				if (m_dwEventCookie==0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE when the sink is not advised
					DispEventAdvise(m_pWebBrowser,&DIID_DWebBrowserEvents2);
			}
		}
	}
	return S_OK;
}

STDMETHODIMP CExplorerBand::OnNavigateComplete( IDispatch *pDisp, VARIANT *URL )
{
	// this is called when the current folder changes. disable the Up button if this is the desktop folder
	m_BandWindow.UpdateToolbar();
	return S_OK;
}

STDMETHODIMP CExplorerBand::OnCommandStateChange( long Command, VARIANT_BOOL Enable )
{
	if (Command==CSC_NAVIGATEFORWARD)
	{
		SendMessage(m_BandWindow.GetToolbar(),TB_ENABLEBUTTON,CBandWindow::ID_GOFORWARD,Enable?1:0);
	}
	if (Command==CSC_NAVIGATEBACK)
	{
		SendMessage(m_BandWindow.GetToolbar(),TB_ENABLEBUTTON,CBandWindow::ID_GOBACK,Enable?1:0);
	}
	return S_OK;
}

STDMETHODIMP CExplorerBand::OnQuit( void )
{
	if (m_pWebBrowser && m_dwEventCookie!=0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE, when the sink is not advised
		return DispEventUnadvise(m_pWebBrowser,&DIID_DWebBrowserEvents2);
	return S_OK;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior)
United States United States
Ivo started programming in 1985 on an Apple ][ clone. He graduated from Sofia University, Bulgaria with a MSCS degree. Ivo has been working as a professional programmer for over 12 years, and as a professional game programmer for over 10. He is currently employed in Pandemic Studios, a video game company in Los Angeles, California.

Comments and Discussions