Click here to Skip to main content
15,893,564 members
Articles / Desktop Programming / ATL

VS File Finder

Rate me:
Please Sign up or sign in to vote.
4.90/5 (39 votes)
8 May 20053 min read 224.4K   3.3K   37  
A Visual Studio add-in to help navigate around large projects.
// 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


Written By
Web Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions