Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

VS File Finder

, 8 May 2005
A Visual Studio add-in to help navigate around large projects.
vsfilefindersetup.zip
VSFileFinderSetup.msi
vsfilefindersource.zip
VSFileFinder
docs
back.gif
finder.gif
menu.gif
modified
back.gif
finder.gif
home.gif
menu.gif
monster.gif
next.gif
noback.gif
nonext.gif
solutionexplorer.gif
monster.gif
next.gif
noback.gif
nonext.gif
orig
back.png
finder.png
home.png
menu.png
monster.gif
next.png
noback.png
solutionexplorer.gif
solutionexplorer.gif
VSFileFinder
AddIn.def
AddIn.rgs
VSFileFinderCtrl
icon1.ico
Options.ico
VSFileFinderCtrl.def
VSFileFinderCtrl.rgs
VSFileFinderCtrlCtrl.bmp
VSFileFinderCtrlps.def
VSFileFinderCtrlps.mk
VSFileFinderSetup
Binary
bannrbmp.bmp
completi.ico
custicon.ico
dlgbmp.bmp
exclamic.ico
info.ico
insticon.ico
New.ico
removico.ico
repairic.ico
Up.ico
Product.wxs
Text.wxs
UI.wxs
// VSFileFinderCtrl.cpp : Implementation of the CVSFileFinderCtrl ActiveX Control class.

#include "stdafx.h"
#include "VSFileFinderCtrlApp.h"
#include "VSFileFinderCtrl.h"
#include "VSFileFinderCtrlPropPage.h"
#include "ProjectItemFinder.h"
#include "ColourListCtrl.h"
#include "Options.h"
#include "OptionsDlg.h"

static const int ID_TIMER = 0;

IMPLEMENT_DYNCREATE(CVSFileFinderCtrl, COleControl)

// Message map
BEGIN_MESSAGE_MAP(CVSFileFinderCtrl, COleControl)
	ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
	ON_WM_CREATE()
	ON_WM_SIZE()
	ON_EN_CHANGE(ID_EDIT, OnEditChange)
	ON_NOTIFY(NM_CLICK, ID_LIST, OnListSelChange)
	ON_NOTIFY(NM_RETURN, ID_LIST, OnListEnter)
	ON_NOTIFY(HDN_ITEMCLICK, 0, OnHeaderItemClick)
	ON_NOTIFY(LVN_GETDISPINFO, ID_LIST, OnGetListDispInfo)
	ON_BN_CLICKED(ID_BUTTON, OnButtonClicked)
	ON_WM_TIMER()
	ON_WM_DESTROY()
	ON_WM_SETFOCUS()
END_MESSAGE_MAP()

// Dispatch map
BEGIN_DISPATCH_MAP(CVSFileFinderCtrl, COleControl)
	DISP_FUNCTION_ID(CVSFileFinderCtrl, "SetInterface", dispidSetInterface, SetInterface, VT_EMPTY, VTS_DISPATCH)
END_DISPATCH_MAP()

// Event map
BEGIN_EVENT_MAP(CVSFileFinderCtrl, COleControl)
END_EVENT_MAP()

// Property pages
BEGIN_PROPPAGEIDS(CVSFileFinderCtrl, 1)
	PROPPAGEID(CVSFileFinderCtrlPropPage::guid)
END_PROPPAGEIDS(CVSFileFinderCtrl)

// Initialize class factory and guid
IMPLEMENT_OLECREATE_EX(CVSFileFinderCtrl, "VSFileFinder.VSFileFinderCtrl.1",
	0xf45b6628, 0x6873, 0x43f1, 0xa0, 0x50, 0x84, 0x57, 0x4f, 0xbb, 0x1f, 0xae);

// Type library ID and version
IMPLEMENT_OLETYPELIB(CVSFileFinderCtrl, _tlid, _wVerMajor, _wVerMinor)

// Interface IDs
const IID BASED_CODE IID_DVSFileFinderCtrl =
		{ 0xA65A9F34, 0x1852, 0x4D8A, { 0xA5, 0x37, 0xD9, 0xF2, 0x31, 0x47, 0x75, 0x9 } };
const IID BASED_CODE IID_DVSFileFinderCtrlEvents =
		{ 0xF7D70BB9, 0x72F3, 0x4BED, { 0x92, 0x1C, 0x1E, 0xF4, 0x37, 0xE2, 0x42, 0x25 } };

// Control type information
static const DWORD BASED_CODE _dwVSFileFinderCtrlOleMisc =
	OLEMISC_ACTIVATEWHENVISIBLE |
	OLEMISC_IGNOREACTIVATEWHENVISIBLE |
	OLEMISC_SETCLIENTSITEFIRST |
	OLEMISC_INSIDEOUT |
	OLEMISC_CANTLINKINSIDE |
	OLEMISC_RECOMPOSEONRESIZE;

IMPLEMENT_OLECTLTYPE(CVSFileFinderCtrl, IDS_VSFILEFINDERCTRL, _dwVSFileFinderCtrlOleMisc)

// CVSFileFinderCtrl::CVSFileFinderCtrlFactory::UpdateRegistry -
// Adds or removes system registry entries for CVSFileFinderCtrl
BOOL CVSFileFinderCtrl::CVSFileFinderCtrlFactory::UpdateRegistry(BOOL bRegister)
{
	if (bRegister)
		return AfxOleRegisterControlClass(
			AfxGetInstanceHandle(),
			m_clsid,
			m_lpszProgID,
			IDS_VSFILEFINDERCTRL,
			IDB_VSFILEFINDERCTRL,
			afxRegApartmentThreading,
			_dwVSFileFinderCtrlOleMisc,
			_tlid,
			_wVerMajor,
			_wVerMinor);
	else
		return AfxOleUnregisterClass(m_clsid, m_lpszProgID);
}

CVSFileFinderCtrl::CVSFileFinderCtrl()
	: m_pSolutionEventsSink(NULL)
	, m_pCSharpEventsSink(NULL)
	, m_pJSharpEventsSink(NULL)
	, m_pVBEventsSink(NULL)
	, m_pVCSolutionItemsEventsSink(NULL)
	, m_pOptions(NULL)
{
	InitializeIIDs(&IID_DVSFileFinderCtrl, &IID_DVSFileFinderCtrlEvents);
	m_pOptions = new Options;
	m_pOptions->Load();
	m_pListCtrl = new FileFinderListCtrl(this);
}

CVSFileFinderCtrl::~CVSFileFinderCtrl()
{
	m_pOptions->Save();
	delete m_pOptions;
	delete m_pListCtrl;
}

void CVSFileFinderCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/)
{
	if (!pdc)
		return;

	pdc->FillSolidRect(&rcBounds, GetSysColor(COLOR_3DFACE));
}

// CVSFileFinderCtrl::DoPropExchange - Persistence support
void CVSFileFinderCtrl::DoPropExchange(CPropExchange* pPX)
{
	ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
	COleControl::DoPropExchange(pPX);

	// TODO: Call PX_ functions for each persistent custom property.
}

DWORD CVSFileFinderCtrl::GetControlFlags()
{
	DWORD dwFlags = COleControl::GetControlFlags();

	// The control will not be redrawn when making the transition
	// between the active and inactivate state.
	dwFlags |= noFlickerActivate;

	// The control can receive mouse notifications when inactive.
	dwFlags |= pointerInactive;
	return dwFlags;
}

void CVSFileFinderCtrl::OnResetState()
{
	COleControl::OnResetState();  // Resets defaults found in DoPropExchange
}

// TODO: Without this code, the control appears to draw in the wrong place
// but I am not sure why (needs checking)
HRESULT CVSFileFinderCtrl::OnActivateInPlace(BOOL bUIActivate, LPMSG pMsg)
{
	HRESULT hr = COleControl::OnActivateInPlace(bUIActivate, pMsg);
	DestroyTracker();
	return hr;
}

int CVSFileFinderCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (COleControl::OnCreate(lpCreateStruct) == -1)
		return -1;

	CRect rc(0, 0, 0, 0);

	CFont font;
	font.CreateStockObject(DEFAULT_GUI_FONT);

	m_EditCtrl.Create(WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | WS_TABSTOP , rc, this, ID_EDIT);
	m_EditCtrl.ModifyStyleEx(0, WS_EX_CLIENTEDGE);
	m_EditCtrl.SetFont(&font);

	m_Button.Create(_T("Options"), WS_CHILD | WS_VISIBLE | BS_ICON, rc, this, ID_BUTTON);
	m_Button.SetIcon(LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_OPTIONS)));
	m_Button.SetFont(&font);

	m_pListCtrl->Create(WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_SINGLESEL | LVS_OWNERDATA | WS_TABSTOP, rc, this, ID_LIST);
	m_pListCtrl->ModifyStyleEx(0, WS_EX_CLIENTEDGE);
	m_pListCtrl->SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_UNDERLINEHOT);

	int nNameWidth, nProjectWidth, nFileWidth;
	m_pOptions->GetColumnWidths(&nNameWidth, &nProjectWidth, &nFileWidth);

	m_pListCtrl->InsertColumn(0, _T("Name"), LVCFMT_LEFT, nNameWidth);
	m_pListCtrl->InsertColumn(1, _T("Project"), LVCFMT_LEFT, nProjectWidth);
	m_pListCtrl->InsertColumn(2, _T("File"), LVCFMT_LEFT, nFileWidth);

	m_Sorter.SetSortOrder(ProjectItemSort::Field_Project, ProjectItemSort::Direction_Ascending);
	m_Sorter.AddSortOrder(ProjectItemSort::Field_Name, ProjectItemSort::Direction_Ascending);

	RefreshHeaderCtrl();

	return 0;
}

void CVSFileFinderCtrl::OnSize(UINT /*nType*/, int cx, int cy)
{
	const int nXBorder = 2;
	const int nButtonWidth = 18;
	const int nEditHeight = 18;
	m_EditCtrl.MoveWindow(nXBorder, 1, cx - nXBorder - nButtonWidth, nEditHeight);
	m_Button.MoveWindow(cx - nButtonWidth, 1, nButtonWidth, nEditHeight);
	m_pListCtrl->MoveWindow(nXBorder, 21, cx - nXBorder, cy - 22);
}

void CVSFileFinderCtrl::ConnectSolutionEvents()
{
	CComPtr<EnvDTE::Events> pEvents;
	if (SUCCEEDED(m_pDTE->get_Events(&pEvents)))
	{
		if (!m_pSolutionEvents && SUCCEEDED(pEvents->get_SolutionEvents((EnvDTE::_SolutionEvents**)&m_pSolutionEvents)) && m_pSolutionEvents)
		{
			m_pSolutionEventsSink = new SolutionEventsSink(this);
			m_pSolutionEventsSink->DispEventAdvise((IUnknown*)m_pSolutionEvents.p);
		}
	}
}

void CVSFileFinderCtrl::ConnectCSharpEvents()
{
	CComPtr<EnvDTE::Events> pEvents;
	if (SUCCEEDED(m_pDTE->get_Events(&pEvents)))
	{
		if (!m_CSharpEvents && SUCCEEDED(pEvents->GetObject(CComBSTR("CSharpProjectItemsEvents"), (IDispatch**)&m_CSharpEvents)) && m_CSharpEvents)
		{
			m_pCSharpEventsSink = new ProjectItemsEventsSink(this);
			m_pCSharpEventsSink->DispEventAdvise((IUnknown*)m_CSharpEvents.p);
		}
	}
}

void CVSFileFinderCtrl::ConnectJSharpEvents()
{
	CComPtr<EnvDTE::Events> pEvents;
	if (SUCCEEDED(m_pDTE->get_Events(&pEvents)))
	{
		if (!m_JSharpEvents && SUCCEEDED(pEvents->GetObject(CComBSTR("VJSharpProjectItemsEvents"), (IDispatch**)&m_JSharpEvents)) && m_JSharpEvents)
		{
			m_pJSharpEventsSink = new ProjectItemsEventsSink(this);
			m_pJSharpEventsSink->DispEventAdvise((IUnknown*)m_JSharpEvents.p);
		}
	}
}

void CVSFileFinderCtrl::ConnectVBEvents()
{
	CComPtr<EnvDTE::Events> pEvents;
	if (SUCCEEDED(m_pDTE->get_Events(&pEvents)))
	{
		if (!m_VBEvents && SUCCEEDED(pEvents->GetObject(CComBSTR("VBProjectItemsEvents"), (IDispatch**)&m_VBEvents)) && m_VBEvents)
		{
			m_pVBEventsSink = new ProjectItemsEventsSink(this);
			m_pVBEventsSink->DispEventAdvise((IUnknown*)m_VBEvents.p);
		}
	}
}

void CVSFileFinderCtrl::ConnectVCEvents()
{
	CComPtr<EnvDTE::Events> pEvents;
	if (SUCCEEDED(m_pDTE->get_Events(&pEvents)))
	{
		if (!m_pVCSolutionItemsEvents && SUCCEEDED(pEvents->GetObject(CComBSTR("VCProjectEngineEventsObject"), (IDispatch**)&m_pVCSolutionItemsEvents)) && m_pVCSolutionItemsEvents)
		{
			m_pVCSolutionItemsEventsSink = new VCProjectItemsEventsSink(this);
			m_pVCSolutionItemsEventsSink->DispEventAdvise((IUnknown*)m_pVCSolutionItemsEvents.p);
		}
	}
}

void CVSFileFinderCtrl::DisconnectEvents()
{
	if (m_pSolutionEvents.p)
	{
		m_pSolutionEventsSink->DispEventUnadvise((IUnknown*)m_pSolutionEvents.p);
		m_pSolutionEvents = NULL;
	}
	if (m_CSharpEvents.p)
	{
		m_pCSharpEventsSink->DispEventUnadvise((IUnknown*)m_CSharpEvents.p);
		m_CSharpEvents = NULL;
	}
	if (m_JSharpEvents.p)
	{
		m_pJSharpEventsSink->DispEventUnadvise((IUnknown*)m_JSharpEvents.p);
		m_JSharpEvents = NULL;
	}
	if (m_VBEvents.p)
	{
		m_pVBEventsSink->DispEventUnadvise((IUnknown*)m_VBEvents.p);
		m_VBEvents = NULL;
	}
	if (m_pVCSolutionItemsEvents.p)
	{
		m_pVCSolutionItemsEventsSink->DispEventUnadvise((IUnknown*)m_pVCSolutionItemsEvents.p);
		m_pVCSolutionItemsEvents = NULL;
	}

	delete m_pSolutionEventsSink;			m_pSolutionEventsSink = NULL;
	delete m_pCSharpEventsSink;				m_pCSharpEventsSink = NULL;
	delete m_pJSharpEventsSink;				m_pJSharpEventsSink = NULL;
	delete m_pVBEventsSink;					m_pVBEventsSink = NULL;
	delete m_pVCSolutionItemsEventsSink;	m_pVCSolutionItemsEventsSink= NULL;
}

void CVSFileFinderCtrl::SetInterface(IDispatch* pInterface)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	CComPtr<IDispatch> pDispatch(pInterface);
	if (pDispatch)
	{
		pDispatch->QueryInterface(&m_pDTE);

		ConnectSolutionEvents();

		// TODO: Only connect events when required (avoids loading lots of unneeded components)
		ConnectCSharpEvents();
		ConnectJSharpEvents();
		ConnectVBEvents();
		ConnectVCEvents();
		Update();
	}
	else
	{
		DisconnectEvents();
		m_pDTE = 0;
	}
}

void CVSFileFinderCtrl::RefreshList(CString filter)
{
	m_ListData.clear();
	ProjectItemFinder finder(m_pOptions, &m_ListData, filter);
	finder.VisitSolution(GetSolution());
	std::stable_sort(m_ListData.begin(), m_ListData.end(), m_Sorter);
	m_pListCtrl->SetItemCount(static_cast<int>(m_ListData.size()));
	m_pListCtrl->SetItemState(0, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
}

void CVSFileFinderCtrl::OnEditChange()
{
	CString s;
	m_EditCtrl.GetWindowText(s);
	RefreshList(s);
}

void CVSFileFinderCtrl::OnListSelChange(NMHDR* pNotifyStruct, LRESULT* pResult)
{
	*pResult = 0;

	NMITEMACTIVATE* pData = reinterpret_cast<NMITEMACTIVATE*>(pNotifyStruct);
	if (pData->iItem == -1)
		return;

	OpenFile(m_pListCtrl->GetItemText(pData->iItem, 2));
}

void CVSFileFinderCtrl::OnListEnter(NMHDR*, LRESULT* pResult)
{
	*pResult = 0;

	POSITION pos = m_pListCtrl->GetFirstSelectedItemPosition();
	if (pos == NULL)
		return;

	int nItem = m_pListCtrl->GetNextSelectedItem(pos);

	OpenFile(m_pListCtrl->GetItemText(nItem, 2));
}

void CVSFileFinderCtrl::OpenFile(CString strName)
{
	CComPtr<EnvDTE::ItemOperations> pItemOps;
	m_pDTE->get_ItemOperations(&pItemOps);
	CComPtr<EnvDTE::Window> pWindow;
	pItemOps->OpenFile(CComBSTR(strName), CComBSTR(""), &pWindow);
}

void CVSFileFinderCtrl::Update()
{
	SetTimer(ID_TIMER, 250, NULL);
}

CComPtr<EnvDTE::_Solution> CVSFileFinderCtrl::GetSolution()
{
	if (!m_pDTE)
		return NULL;

	CComPtr<EnvDTE::_Solution> pSolution;
	if (FAILED(m_pDTE->get_Solution(&pSolution)))
		return NULL;

	return pSolution;
}

void CVSFileFinderCtrl::OnHeaderItemClick(NMHDR* pNotifyStruct, LRESULT* pResult)
{
	*pResult = 0;

	NMHEADER* pItem = reinterpret_cast<NMHEADER*>(pNotifyStruct);

	ProjectItemSort::Field f(ProjectItemSort::Field_Name);

	switch(pItem->iItem)
	{
		case 0: f = ProjectItemSort::Field_Name;	break;
		case 1: f = ProjectItemSort::Field_Project;	break;
		case 2: f = ProjectItemSort::Field_Path;	break;
	}

	if (GetAsyncKeyState(VK_CONTROL) || GetAsyncKeyState(VK_SHIFT))
		m_Sorter.AddSortOrder(f, ProjectItemSort::Direction_Ascending);
	else
		m_Sorter.SetSortOrder(f, ProjectItemSort::Direction_Ascending);

	OnEditChange();
}

void CVSFileFinderCtrl::RefreshHeaderCtrl()
{
	// ? Should draw sort arrows here
}

void CVSFileFinderCtrl::OnGetListDispInfo(NMHDR* pNotifyStruct, LRESULT* pResult)
{
	*pResult = 0;

	LV_DISPINFO* pDispInfo = reinterpret_cast<LV_DISPINFO*>(pNotifyStruct);
	LV_ITEM* pItem = &(pDispInfo)->item;

	const int nItemIndex = pItem->iItem;
	ASSERT(nItemIndex >= 0 && nItemIndex < static_cast<int>(m_ListData.size()));

	if (pItem->mask & LVIF_TEXT)
	{
		switch(pItem->iSubItem)
		{
			case 0:	_tcsncpy(pItem->pszText, m_ListData[nItemIndex].m_Name, pItem->cchTextMax);		break;
			case 1:	_tcsncpy(pItem->pszText, m_ListData[nItemIndex].m_Project, pItem->cchTextMax);	break;
			case 2:	_tcsncpy(pItem->pszText, m_ListData[nItemIndex].m_Path, pItem->cchTextMax);		break;
		}
		pItem->pszText[pItem->cchTextMax - 1] = L'\0';
	}
}

COLORREF CVSFileFinderCtrl::GetColour(int nListIndex) const
{
	ASSERT(nListIndex >= 0 && nListIndex < static_cast<int>(m_ListData.size()));
	return m_ListData[nListIndex].m_Colour;
}

void CVSFileFinderCtrl::OnTimer(UINT_PTR /*nIDEvent*/)
{
	KillTimer(ID_TIMER);
	OnEditChange();
}

void CVSFileFinderCtrl::OnSolutionChange()
{
	Update();
}

void CVSFileFinderCtrl::OnButtonClicked()
{
	OptionsDlg dlg(*m_pOptions, this);
	if (dlg.DoModal() == IDOK)
	{
		*m_pOptions = dlg.GetOptions();
		m_pOptions->Save();
		RefreshList();
	}
}

void CVSFileFinderCtrl::OnDestroy()
{
	m_pOptions->SetColumnWidths(
		m_pListCtrl->GetColumnWidth(0),
		m_pListCtrl->GetColumnWidth(1),
		m_pListCtrl->GetColumnWidth(2)
	);
}

BOOL CVSFileFinderCtrl::PreTranslateMessage(MSG* pMsg)
{
	// Call IsDialogMessage to enable tabbing between windows.  Only call it for
	// the tab key so we still get messages like NM_RETURN and don't call it if
	// the control key is down so Ctrl+Tab still switches documents

	bool bTabKey = pMsg && pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_TAB;
	bool bControlDown = GetKeyState(VK_CONTROL) < 0;

	if (bTabKey && !bControlDown && IsDialogMessage(pMsg))
		return TRUE;
	else
		return CWnd::PreTranslateMessage(pMsg);
}

void CVSFileFinderCtrl::OnSetFocus(CWnd*)
{
	m_EditCtrl.SetFocus();
	m_EditCtrl.SetSel(0, -1);
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

J W Payne
Web Developer
United Kingdom United Kingdom
No Biography provided

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 8 May 2005
Article Copyright 2004 by J W Payne
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid