// 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);
}