// vssreporterDlg.cpp : implementation file
//
#include "stdafx.h"
#include "vssreporter.h"
#include "vssreporterDlg.h"
#include "ConfigDiffToolDlg.h"
#include "..\shared\regkey.h"
#include "..\shared\folderdialog.h"
#include "..\shared\filemisc.h"
#include "..\shared\deferwndmove.h"
#define COMPILE_MULTIMON_STUBS
#include <multimon.h>
#include <direct.h>
#include <mmsystem.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CVssreporterDlg dialog
enum { UNKNOWN = -1, CREATED, MODIFED, LABELLED, DELETED };
enum { TYPE, FILENAME, FILEPATH, VERSION, LABEL, USER, MODDATE, COMMENT, NUMCOLUMNS };
LPCTSTR COLUMN[] = { "", "Name", "Path", "Ver", "Label", "User", "Date", "Comment" };
#ifdef _VSS5_
# define IVSSVERSION 5
#else
# define IVSSVERSION 6
#endif
CVssreporterDlg::CVssreporterDlg(CWnd* pParent /*=NULL*/)
: CDialog(CVssreporterDlg::IDD, pParent), m_sizeOrg(0, 0),
m_sCreated("CREATED"), m_sCheckedIn("CHECKED IN"), m_sLabeled("LABELED")
{
//{{AFX_DATA_INIT(CVssreporterDlg)
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_bReporting = FALSE;
m_sCurProject.Empty();
m_vssdb = NULL;
m_sUsername = GetUserName();
m_sQuery = CVssQueryDlg::GetLastQuery();
InitLocaleStrings();
m_ilFolders.Create(IDB_FOLDERS, 18, 1, RGB(255, 0, 0));
m_ilSorting.Create(IDB_SORTARROWS, 8, 1, RGB(255, 0, 0));
m_ilResults.Create(IDB_RESULTS, 16, 1, RGB(255, 0, 0));
}
CVssreporterDlg::~CVssreporterDlg()
{
}
void CVssreporterDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CVssreporterDlg)
DDX_Control(pDX, IDC_DATABASELIST, m_cbDatabases);
DDX_Control(pDX, IDC_FILELIST, m_lcResults);
DDX_Control(pDX, IDC_VSSTREE, m_tcVSS);
DDX_CBString(pDX, IDC_DATABASELIST, m_sDbPath);
DDX_Text(pDX, IDC_FILESFOUND, m_sFilesFound);
DDX_Text(pDX, IDC_PASSWORD, m_sPassword);
DDX_Text(pDX, IDC_USERNAME, m_sUsername);
DDX_Text(pDX, IDC_PROGRESS, m_sProgress);
DDX_Text(pDX, IDC_QUERY, m_sQuery);
DDX_Check(pDX, IDC_RECURSIVE, m_bRecursive);
DDX_Check(pDX, IDC_IGNOREDELETED, m_bIgnoreDeleted);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CVssreporterDlg, CDialog)
//{{AFX_MSG_MAP(CVssreporterDlg)
ON_BN_CLICKED(IDC_REPORT, OnReport)
ON_BN_CLICKED(IDC_CANCELREPORT, OnCancelreport)
ON_CBN_SELCHANGE(IDC_DATABASELIST, OnSelchangeDatabaselist)
ON_NOTIFY(TVN_SELCHANGED, IDC_VSSTREE, OnSelchangedVsstree)
ON_BN_CLICKED(IDC_BROWSEDATABASE, OnBrowsedatabase)
ON_WM_CLOSE()
ON_WM_DESTROY()
ON_BN_CLICKED(IDC_COPYRESULTS, OnCopyResults)
ON_WM_CONTEXTMENU()
ON_COMMAND(ID_RESULTS_VISUALSOURCESAFE, OnShowVSS)
ON_EN_CHANGE(IDC_USERNAME, OnChangeUsernamePassword)
ON_COMMAND(ID_COPY_FILESTOFOLDER, OnCopyFilestofolder)
ON_BN_CLICKED(IDC_EDITQUERY, OnEditquery)
ON_NOTIFY(LVN_COLUMNCLICK, IDC_FILELIST, OnColumnclickFilelist)
ON_COMMAND(ID_COPY_TEXTTOCLIPBOARD_TABBED, OnCopyTexttoclipboardTabbed)
ON_COMMAND(ID_COPY_TEXTTOCLIPBOARD_FORMATTED, OnCopyTexttoclipboardFormatted)
ON_COMMAND(ID_COPY_TEXTTOFILE_FORMATTED, OnCopyTexttofileFormatted)
ON_COMMAND(ID_COPY_TEXTTOFILE_TABBED, OnCopyTexttofileTabbed)
ON_NOTIFY(LVN_ITEMCHANGED, IDC_FILELIST, OnSelchangedFilelist)
ON_COMMAND(ID_RESULTS_SELECTALL, OnResultsSelectall)
ON_COMMAND(ID_COPY_TEXTTOFILE_XML, OnCopyTexttofileXml)
ON_COMMAND(ID_COPY_TEXTTOCLIPBOARD_XML, OnCopyTexttoclipboardXml)
ON_COMMAND(ID_RESULTS_DIFF, OnResultsDiff)
ON_EN_CHANGE(IDC_PASSWORD, OnChangeUsernamePassword)
ON_BN_CLICKED(IDC_DIFFTOOL, OnDiffTool)
//}}AFX_MSG_MAP
ON_WM_SIZE()
ON_WM_GETMINMAXINFO()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CVssreporterDlg message handlers
BOOL CVssreporterDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
m_tcVSS.SetImageList(&m_ilFolders, TVSIL_NORMAL);
m_lcResults.SetImageList(&m_ilResults, LVSIL_SMALL);
m_lcResults.GetHeaderCtrl()->SetImageList(&m_ilSorting);
m_lcResults.InsertColumn(TYPE, COLUMN[TYPE], LVCFMT_LEFT, 24);
m_lcResults.InsertColumn(FILENAME, COLUMN[FILENAME], LVCFMT_LEFT, 60);
m_lcResults.InsertColumn(FILEPATH, COLUMN[FILEPATH], LVCFMT_LEFT, 90);
m_lcResults.InsertColumn(VERSION, COLUMN[VERSION], LVCFMT_RIGHT, 40);
m_lcResults.InsertColumn(LABEL, COLUMN[LABEL], LVCFMT_LEFT, 50);
m_lcResults.InsertColumn(USER, COLUMN[USER], LVCFMT_LEFT, 70);
m_lcResults.InsertColumn(MODDATE, COLUMN[MODDATE], LVCFMT_LEFT, 130);
m_lcResults.InsertColumn(COMMENT, COLUMN[COMMENT], LVCFMT_LEFT, 300);
m_lcResults.SetExtendedStyle(m_lcResults.GetExtendedStyle() | LVS_EX_FULLROWSELECT);
// ensure vss is registered and if not allow user to navigate to it
int nVSSver = GetVSSVersion();
if (nVSSver == -1 || nVSSver < IVSSVERSION)
{
CString sMessage;
if (nVSSver != -1) // interface incompatibility
{
sMessage.Format("This version of VSSReporter requires Visual SourceSafe %d to run.", IVSSVERSION);
MessageBox(sMessage, "VSSReporter", MB_OK);
EndDialog(-1);
return TRUE;
}
else
{
if (IDNO == MessageBox("VSSReporter requires the Visual SourceSafe component 'ssapi.dll' which does not appear to be registered.\n\nClick 'Yes' to navigate to this component and have VSS Reporter register it for you, \n or 'No' to quit VSSReporter and register it manually",
"VSSReporter", MB_YESNO))
{
EndDialog(-1);
return TRUE;
}
else
{
CFileDialog dialog(TRUE, "dll", "", OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_NONETWORKBUTTON | OFN_HIDEREADONLY,
"ssapi.dll|ssapi.dll||");
dialog.m_ofn.lpstrTitle = "Locate VSS Component";
if (dialog.DoModal() == IDCANCEL)
EndDialog(-1);
else
{
CString sCmdline;
sCmdline.Format("\"%s\" -s", dialog.GetPathName());
// we need to wait here until it done
SHELLEXECUTEINFO sei;
ZeroMemory(&sei, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.hwnd = *this;
sei.lpFile = "regsvr32.exe";
sei.lpParameters = (LPCTSTR)sCmdline;
sei.nShow = SW_HIDE;
if (ShellExecuteEx(&sei) && sei.hProcess)
{
WaitForSingleObject(sei.hProcess, INFINITE);
CloseHandle(sei.hProcess); // cleanup
}
}
}
}
}
// add interface version to caption
CString sCaption, sVer;
GetWindowText(sCaption);
sVer.Format(" (for VSS %d)", IVSSVERSION);
SetWindowText(sCaption + sVer);
RegLoadSettings();
EnableControls();
// position resize icon as close to bottom left as pos
GetDlgItem(IDC_GRIPPER)->ModifyStyle(0, SBS_SIZEGRIP | SBS_SIZEBOXTOPLEFTALIGN);
CDeferWndMove(1).OffsetCtrl(this, IDC_GRIPPER, 1, 2);
return TRUE; // return TRUE unless you set the focus to a control
}
void CVssreporterDlg::OnReport()
{
UpdateData();
CString sProjPath = m_tcVSS.GetSelectedProject();
if (!sProjPath.IsEmpty())
{
m_bReporting = TRUE;
if (OpenDatabase(m_sDbPath))
{
BOOL bCanReport = FALSE;
CString sError;
// we enable the stop button here so that the label finding can be terminated if nec.
EnableControls();
// get the query
VssQuery query;
CVssQueryDlg::GetLastQuery(query);
switch (query.nFindFilter)
{
case MATCHINGLABEL:
case MATCHINGCOMMENT:
bCanReport = !query.sMatch.IsEmpty();
query.sMatch.MakeUpper();
break;
case LASTMODIFIED:
case MODIFIED:
{
switch (query.nDateFilter)
{
case AFTER:
if (query.nTypeFilter == TYPELABEL)
{
query.dtAfter = FindLabel(sProjPath, query.sAfterLabel, FALSE);
if (query.dtAfter.m_dt == 0)
sError.Format("The label '%s' could not be found in the selected project", query.sAfterLabel);
}
bCanReport = (query.dtAfter.m_dt != 0);
break;
case BEFORE:
if (query.nTypeFilter == TYPELABEL)
{
query.dtBefore = FindLabel(sProjPath, query.sBeforeLabel, TRUE);
if (query.dtBefore.m_dt == 0)
sError.Format("The label '%s' could not be found in the selected project", query.sBeforeLabel);
}
bCanReport = (query.dtBefore.m_dt != 0);
break;
case BETWEEN:
if (query.nTypeFilter == TYPELABEL)
{
query.dtAfter = FindLabel(sProjPath, query.sAfterLabel, TRUE);
query.dtBefore = FindLabel(sProjPath, query.sBeforeLabel, FALSE);
if (query.dtBefore.m_dt == 0 && query.dtAfter.m_dt == 0)
sError.Format("Neither the label '%s' nor the label '%s' could be found in the selected project", query.sBeforeLabel, query.sAfterLabel);
else if (query.dtBefore.m_dt == 0)
sError.Format("The label '%s' could not be found in the selected project", query.sBeforeLabel);
else if (query.dtAfter.m_dt == 0)
sError.Format("The label '%s' could not be found in the selected project", query.sAfterLabel);
}
bCanReport = (query.dtAfter.m_dt != 0 && query.dtBefore.m_dt != 0);
break;
}
}
break;
}
if (bCanReport && Continue(m_bReporting))
{
m_lcResults.DeleteAllItems();
m_sFilesFound = "Files Found: 0";
m_sortInfo.Reset();
m_aColLengths.RemoveAll();
UpdateData(FALSE);
// do the report
ReportOnItem(sProjPath, query);
// sort the results
m_lcResults.SortItems(ResultsSortFunc, (DWORD)&m_sortInfo);
// notify user
PlaySound("SystemAsterisk", NULL, SND_SYNC | SND_ALIAS);
}
else if (!sError.IsEmpty() && Continue(m_bReporting))
{
MessageBox(sError, "VssReporter � AbstractSpoon");
}
}
else
{
MessageBox("Unable to open SourceSafe database", "VssReporter � AbstractSpoon");
}
m_bReporting = FALSE;
EnableControls();
m_sProgress.Empty();
UpdateData(FALSE);
UpdateWindow();
}
}
void CVssreporterDlg::OnCancelreport()
{
m_bReporting = FALSE;
EnableControls();
}
void CVssreporterDlg::OnSelchangeDatabaselist()
{
// cache previous db path
CString sPrevDb = m_sDbPath;
UpdateData();
if (m_tcVSS.OpenDatabase(m_sDbPath, m_sUsername, m_sPassword))
{
// clear results list
m_lcResults.DeleteAllItems();
m_sFilesFound.Empty();
UpdateData(FALSE);
}
else // failed so restore previous db
{
m_cbDatabases.SelectString(-1, sPrevDb);
CString sMessage;
sMessage.Format("The SourceSafe database '%s'\ncould not be found or could not be opened.\n\nThe previously open database has been restored.",
m_sDbPath);
MessageBox(sMessage, "VssReporter � AbstractSpoon");
UpdateData();
}
}
CString CVssreporterDlg::GetUserName()
{
CString sName;
DWORD dwLen = MAX_COMPUTERNAME_LENGTH + 1;
::GetUserName(sName.GetBuffer(dwLen), &dwLen);
sName.ReleaseBuffer();
return sName;
}
BOOL CVssreporterDlg::OpenDatabase(LPCTSTR szDatabasePath)
{
CWaitCursor cursor;
COleVariant var1;
IVSSItemPtr vssi, vssi2p;
IVSSItemsPtr vssis;
try
{
if (m_vssdb)
{
m_vssdb.Release();
m_vssdb = NULL;
}
m_vssdb.CreateInstance(__uuidof(VSSDatabase));
m_vssdb->Open(szDatabasePath, (LPCTSTR)m_sUsername, (LPCTSTR)m_sPassword);
}
catch (...)
{
return FALSE;
}
return TRUE;
}
BOOL CVssreporterDlg::Continue(BOOL& bContinue)
{
MSG msg;
// check messages for the cancel dialog or paint messages
while (::PeekMessage((LPMSG) &msg, NULL, 0, 0, PM_REMOVE))
{
BOOL bDlgMsg = ::IsDialogMessage(*this, &msg);
if (!bDlgMsg)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return bContinue;
}
void CVssreporterDlg::ReportOnProject(IVSSItemPtr& vssi, LPCTSTR szVSSPath, const VssQuery& query)
{
ASSERT (vssi->GetType() == 0l);
m_sProgress.Format("Reporting on project '%s'", szVSSPath);
UpdateData(FALSE);
UpdateWindow();
try // ssapi spits sometimes
{
IVSSItemsPtr vssis = vssi->GetItems(TRUE); // TRUE also gets deleted items
int nCount = vssis->GetCount();
for(int x = 0; x < nCount; x++)
{
COleVariant var1 = (long)(x + 1);
IVSSItemPtr vssi2p = vssis->GetItem(var1);
CString sItem = CString((LPCTSTR)vssi2p->GetSpec());
ReportOnItem(sItem, query);
}
}
catch (...)
{
}
}
void CVssreporterDlg::ReportOnItem(IVSSItemPtr& vssi, LPCTSTR szVSSPath, const VssQuery& query, BOOL bDeleted)
{
ASSERT (vssi->GetType() != 0l);
CString sUser, sDate, sLabel;
CWaitCursor cursor;
COleVariant var1;
COleDateTime date;
IUnknownPtr lpunk;
IVSSVersionsPtr objVersions;
IVSSVersionPtr objVersion;
IEnumVARIANTPtr ppvobj;
ULONG fetched;
VARIANT st;
try // ssapi spits sometimes
{
objVersions = vssi->GetVersions(0L);
// Prepare to loop through the items
lpunk = objVersions->_NewEnum();
lpunk.QueryInterface(IID_IEnumVARIANT, (void **)&ppvobj);
CString sUserFilter = GetUserFilter(query);
BOOL bMatchingLabel = FALSE;
// Loop through the versions
do
{
ppvobj->Next(1UL, &st, &fetched);
if(fetched != 0)
{
// Try to get the item
REFIID riid = __uuidof(IVSSVersion);
HRESULT hr = st.punkVal->QueryInterface(riid, (void**)&objVersion);
if(!FAILED(hr))
{
int nModType = bDeleted ? DELETED : GetModType(objVersion);
// switch on the find filter
switch (query.nFindFilter)
{
case MATCHINGLABEL:
if (!bDeleted) // doesn't make much sense to search labels on deleted items
{
// unfortunately its not as simple as simply testing for the
// a matching label because then we have to find the last
// mod preceding this label
if (!bMatchingLabel) // means we've not yet found the label
{
if (LabelMatches(objVersion, query))
bMatchingLabel = TRUE;
}
// label has been found so now we must check for a (prior) mod
// note: we don't 'else' this with the found label because
// the label may have been attached to the check-in
if (bMatchingLabel &&
(nModType == CREATED || nModType == MODIFIED ||
(nModType == DELETED && !m_bIgnoreDeleted) || nModType == LABELLED))
{
AddResult(szVSSPath, objVersion, nModType);
fetched = 0; // end loop
}
}
break;
case MATCHINGCOMMENT:
if (!bDeleted) // doesn't make much sense to search comments on deleted items
{
CString sComment(GetComment(objVersion));
sComment.TrimLeft();
sComment.TrimRight();
CString sTemp(sComment);
sTemp.MakeUpper();
if (sTemp.Find(query.sMatch) != -1)
{
AddResult(szVSSPath, objVersion, nModType);
fetched = 0; // end loop
}
}
break;
case LASTMODIFIED:
if (nModType == CREATED || nModType == MODIFIED ||
(nModType == DELETED && !m_bIgnoreDeleted))
{
// first check the user matches
CString sItemUser = (LPCTSTR) objVersion->GetUsername();
if (sUserFilter.IsEmpty() || sUserFilter.CompareNoCase(sItemUser) == 0)
{
COleDateTime date;
// note: versions start at the most recent
if (DateMatches(objVersion, query, date))
{
AddResult(szVSSPath, objVersion, nModType);
fetched = 0; // end the loop
}
// if the file has been deleted we're only interested in the last mod
// having been within the dates. so if the first date does not match
// then we drop out regardless
if (bDeleted)
fetched = 0;
}
}
break;
case MODIFIED:
if (nModType == CREATED || nModType == MODIFIED ||
(nModType == DELETED && !m_bIgnoreDeleted))
{
// first check the user matches
CString sItemUser = (LPCTSTR) objVersion->GetUsername();
if (sUserFilter.IsEmpty() || sUserFilter.CompareNoCase(sItemUser) == 0)
{
COleDateTime date;
if (DateMatches(objVersion, query, date))
{
AddResult(szVSSPath, objVersion, nModType);
}
// if the file has been deleted we're only interested in the last mod
// having been within the dates. so if the first date does not match
// or even if it does, we drop out regardless
if (bDeleted)
fetched = 0;
}
}
break;
}
}
st.punkVal->Release();
}
}
while (fetched != 0 && Continue(m_bReporting));
ppvobj.Release();
// something very strange can happen here when the pointers are the same
if (lpunk != ppvobj)
lpunk.Release();
}
catch (...)
{
// do nothing
return;
}
}
CString CVssreporterDlg::GetComment(const IVSSVersionPtr& objVer)
{
CString sComment = (LPCTSTR)objVer->GetComment();
#ifndef _VSS5_
# pragma message("------")
# pragma message("If compiling for use with Visual SourceSafe 5 you will need to ")
# pragma message("#define _VSS5_, else you'll have a problem with GetLabelComment()")
if (sComment.IsEmpty())
sComment = (LPCTSTR)objVer->GetLabelComment();
# pragma message("------")
#endif
return sComment;
}
BOOL CVssreporterDlg::LabelMatches(const IVSSVersionPtr& objVer, const VssQuery& query)
{
CString sLabel;
if (GetLabel(objVer, sLabel))
{
if (query.bRegExp)
{
}
else
{
sLabel.MakeUpper();
if (sLabel.Find(query.sMatch) >= 0)
return TRUE;
}
}
return FALSE;
}
void CVssreporterDlg::AddResult(const CString& sVSSPath, LPCTSTR szUser, int nVersion,
const COleDateTime& date, LPCTSTR szComment, int nModType,
LPCTSTR szLabel)
{
if (date.m_dt <= 0)
return;
// add to list
ResultInfo ri;
ri.sFile = sVSSPath.Mid(sVSSPath.ReverseFind('/') + 1);
ri.sPath = sVSSPath.Left(sVSSPath.ReverseFind('/'));
ri.nVersion = (nModType == DELETED) ? 0 : nVersion;
ri.sUser = (nModType == DELETED) ? "" : szUser;
ri.date = (nModType == DELETED) ? 0 : date;
ri.sComment = (nModType == DELETED) ? "" : szComment;
ri.sLabel = (nModType == DELETED) ? "" : szLabel;
ri.nModType = nModType;
int nIndex = m_lcResults.InsertItem(m_lcResults.GetItemCount(), "", nModType);
if (nIndex >= 0)
{
int nResult = m_sortInfo.aResults.Add(ri);
// permanently tag item with sort data
m_lcResults.SetItemData(nIndex, nResult);
m_lcResults.SetItemText(nIndex, FILENAME, ri.sFile);
m_lcResults.SetItemText(nIndex, FILEPATH, ri.sPath);
CString sVer, sDate;
if (nModType != DELETED)
{
sVer.Format("%d", nVersion);
m_lcResults.SetItemText(nIndex, VERSION, sVer);
m_lcResults.SetItemText(nIndex, USER, szUser);
sDate.Format("%s (%s)", date.Format(VAR_DATEVALUEONLY), date.Format(VAR_TIMEVALUEONLY));
m_lcResults.SetItemText(nIndex, MODDATE, sDate);
m_lcResults.SetItemText(nIndex, COMMENT, szComment);
m_lcResults.SetItemText(nIndex, LABEL, szLabel);
}
m_lcResults.UpdateWindow();
m_sFilesFound.Format("Files Found: %d", m_lcResults.GetItemCount());
UpdateData(FALSE);
// update longest items
if (!m_aColLengths.GetSize()) // first item
{
m_aColLengths.SetSize(NUMCOLUMNS);
m_aColLengths[FILENAME] = ri.sFile.GetLength();
m_aColLengths[FILEPATH] = ri.sPath.GetLength();
m_aColLengths[VERSION] = sVer.GetLength();
m_aColLengths[USER] = ri.sUser.GetLength();
m_aColLengths[MODDATE] = sDate.GetLength();
m_aColLengths[COMMENT] = ri.sComment.GetLength();
m_aColLengths[LABEL] = ri.sLabel.GetLength();
}
else
{
m_aColLengths[FILENAME] = max(m_aColLengths[FILENAME], ri.sFile.GetLength());
m_aColLengths[FILEPATH] = max(m_aColLengths[FILEPATH], ri.sPath.GetLength());
m_aColLengths[VERSION] = max(m_aColLengths[VERSION], sVer.GetLength());
m_aColLengths[USER] = max(m_aColLengths[USER], ri.sUser.GetLength());
m_aColLengths[MODDATE] = max(m_aColLengths[MODDATE], sDate.GetLength());
m_aColLengths[COMMENT] = max(m_aColLengths[COMMENT], ri.sComment.GetLength());
m_aColLengths[LABEL] = max(m_aColLengths[LABEL], ri.sLabel.GetLength());
}
}
}
void CVssreporterDlg::AddResult(const CString& sVSSPath, const IVSSVersionPtr& objVer, int nModType)
{
int nVer = objVer->GetVersionNumber();
DATE date = objVer->GetDate();
CString sUser = (LPCTSTR)objVer->GetUsername();
CString sComment = GetComment(objVer);
CString sLabel = GetLabel(objVer);
AddResult(sVSSPath, sUser, nVer, date, sComment, nModType, sLabel);
}
void CVssreporterDlg::ReportOnItem(CString sVSSPath, const VssQuery& query)
{
CWaitCursor cursor;
IVSSItemPtr vssi; // A vss item
COleVariant varBuf(sVSSPath); // special overloaded variant used to talk to OLE
BOOL bDeleted = FALSE;
try // ssapi spits sometimes
{
vssi = m_vssdb->GetVSSItem(varBuf.bstrVal, 0); // Get the Item passed to us.
}
catch (...)
{
// we'll get here if the item is deleted so we try again
try
{
vssi = m_vssdb->GetVSSItem(varBuf.bstrVal, 1); // Get the Item passed to us.
bDeleted = TRUE; // if we get here its deleted
}
catch (...)
{
// do nothing
return;
}
}
if (!Continue(m_bReporting))
return;
if (vssi->GetType() == 0l) // See if the thing we got was a project
{
if (!bDeleted && (m_bRecursive || sVSSPath == m_sCurProject))
ReportOnProject(vssi, sVSSPath, query);
}
else
ReportOnItem(vssi, sVSSPath, query, bDeleted);
}
COleDateTime CVssreporterDlg::FindLabel(CString sVSSItem, LPCTSTR szLabel, BOOL bFirst)
{
CWaitCursor cursor;
int x; // Counter to run thru project
COleVariant var1; // special overloaded variant used to talk to OLE
IVSSItemPtr vssi, vssi2p; // A vss item
IVSSItemsPtr vssis; // A vss collection
COleDateTime date;
IUnknownPtr lpunk;
IVSSVersionsPtr objVersions;
IVSSVersionPtr objVersion;
IEnumVARIANTPtr ppvobj;
ULONG fetched;
VARIANT st;
m_sProgress.Format("Looking for label '%s' in project '%s'", szLabel, sVSSItem);
UpdateData(FALSE);
UpdateWindow();
try // ssapi spits sometimes
{
COleVariant varBuf(sVSSItem); // special overloaded variant used to talk to OLE
vssi = m_vssdb->GetVSSItem(varBuf.bstrVal, 0); // Get the Item passed to us.
if (!Continue(m_bReporting))
return date;
if (vssi->GetType() == 0L) // See if the thing we got was a project
{
TRACE ("CVssreporterDlg::FindLabel(project = %s)\n", (LPCTSTR)vssi->GetSpec());
vssis = vssi->GetItems(FALSE);
for(x = 0; x < vssis->GetCount(); x++)
{
var1 = (long)(x + 1);
vssi2p = vssis->GetItem(var1);
CString sItem = CString((LPCTSTR)vssi2p->GetSpec());
date = FindLabel(sItem, szLabel, bFirst); // pass it on until it hits an item
if (date.m_dt)
break;
}
}
else // its an item
{
TRACE ("CVssreporterDlg::FindLabel(item = %s)\n", (LPCTSTR)vssi->GetSpec());
objVersions = vssi->GetVersions(0L);
// Prepare to loop through the items
lpunk = objVersions->_NewEnum();
lpunk.QueryInterface(IID_IEnumVARIANT, (void **)&ppvobj);
// Loop through the versions
do
{
ppvobj->Next(1UL, &st, &fetched);
if (fetched != 0)
{
// Try to get the item
if(!FAILED(st.punkVal->QueryInterface(__uuidof(IVSSVersion), (void**)&objVersion)))
{
CString sItemLabel;
if (GetLabel(objVersion, sItemLabel) && sItemLabel.CompareNoCase(szLabel) == 0)
{
TRACE ("CVssreporterDlg::FindLabel(label = %s)\n", sItemLabel);
date = objVersion->GetDate();
// if we are only interested in the first label then stop here
// else we carry on to the end
if (bFirst)
break;
}
}
st.punkVal->Release();
}
}
while (fetched != 0 && Continue(m_bReporting));
ppvobj.Release();
// something very strange can happen here when the pointers are the same
if (lpunk != ppvobj)
lpunk.Release();
}
}
catch (...)
{
// do nothing
}
return date;
}
void CVssreporterDlg::OnOK()
{
m_bReporting = FALSE;
CDialog::OnOK();
RegSaveSettings();
}
void CVssreporterDlg::OnCancel()
{
m_bReporting = FALSE;
// do nothing else
}
void CVssreporterDlg::RegSaveSettings()
{
// pos
WINDOWPLACEMENT wp;
GetWindowPlacement(&wp);
AfxGetApp()->WriteProfileInt("Pos", "TopLeft", MAKELPARAM(wp.rcNormalPosition.left, wp.rcNormalPosition.top));
AfxGetApp()->WriteProfileInt("Pos", "BottomRight", MAKELPARAM(wp.rcNormalPosition.right, wp.rcNormalPosition.bottom));
AfxGetApp()->WriteProfileString("Settings", "VSSPath", m_sVSSPath);
AfxGetApp()->WriteProfileString("Settings", "Database", m_sDbPath);
AfxGetApp()->WriteProfileString("Settings", "Username", m_sUsername);
AfxGetApp()->WriteProfileInt("Settings", "Recursive", m_bRecursive);
AfxGetApp()->WriteProfileInt("Settings", "IgnoreDeleted", m_bIgnoreDeleted);
// save database list
int nCount = m_cbDatabases.GetCount();
AfxGetApp()->WriteProfileInt("Settings", "DatabaseCount", nCount);
for (int nDatabase = 0; nDatabase < nCount; nDatabase++)
{
CString sDatabase;
m_cbDatabases.GetLBText(nDatabase, sDatabase);
if (!sDatabase.IsEmpty())
{
CString sKey;
sKey.Format("Database%02d", nDatabase + 1);
AfxGetApp()->WriteProfileString("Settings", sKey, sDatabase);
}
}
// save column widths
if (m_lcResults.GetHeaderCtrl())
{
int nCol = m_lcResults.GetHeaderCtrl()->GetItemCount();
AfxGetApp()->WriteProfileInt("Settings", "ColumnCount", nCol);
while (nCol--)
{
CString sCol;
sCol.Format("Col%dWidth", nCol);
AfxGetApp()->WriteProfileInt("Settings", sCol, m_lcResults.GetColumnWidth(nCol));
}
}
// save sort info
AfxGetApp()->WriteProfileInt("Settings", "SortColumn", m_sortInfo.nSortColumn);
AfxGetApp()->WriteProfileInt("Settings", "SortAscending", m_sortInfo.bSortAscending);
}
void CVssreporterDlg::RegLoadSettings()
{
// pos
WINDOWPLACEMENT wp;
GetWindowPlacement(&wp);
DWORD dwTopLeft = (DWORD)AfxGetApp()->GetProfileInt("Pos", "TopLeft", -1);
DWORD dwBottomRight = (DWORD)AfxGetApp()->GetProfileInt("Pos", "BottomRight", -1);
if (dwTopLeft != -1 && dwBottomRight != -1)
{
CRect rect(LOWORD(dwTopLeft), HIWORD(dwTopLeft), LOWORD(dwBottomRight), HIWORD(dwBottomRight));
// ensure this intersects with the desktop
if (NULL != MonitorFromRect(rect, MONITOR_DEFAULTTONULL))
{
wp.rcNormalPosition = rect;
wp.showCmd = SW_HIDE;
SetWindowPlacement(&wp);
}
}
m_sVSSPath = AfxGetApp()->GetProfileString("Settings", "VSSPath", "");
m_sDbPath = AfxGetApp()->GetProfileString("Settings", "Database", "");
m_sUsername = AfxGetApp()->GetProfileString("Settings", "Username", "");
m_bRecursive = AfxGetApp()->GetProfileInt("Settings", "Recursive", TRUE);
m_bIgnoreDeleted = AfxGetApp()->GetProfileInt("Settings", "IgnoreDeleted", TRUE);
UpdateData(FALSE);
// load database list
int nCount = AfxGetApp()->GetProfileInt("Settings", "DatabaseCount", 0);
for (int nDatabase = 0; nDatabase < nCount; nDatabase++)
{
CString sKey;
sKey.Format("Database%02d", nDatabase + 1);
CString sDatabase = AfxGetApp()->GetProfileString("Settings", sKey, "");
if (!sDatabase.IsEmpty() && m_cbDatabases.FindString(-1, sDatabase) == CB_ERR)
m_cbDatabases.AddString(sDatabase);
}
if (!m_sDbPath.IsEmpty() && m_tcVSS.OpenDatabase(m_sDbPath, m_sUsername, m_sPassword))
{
// select active db
if (m_cbDatabases.FindString(-1, m_sDbPath) == CB_ERR)
m_cbDatabases.AddString(m_sDbPath);
m_cbDatabases.SelectString(-1, m_sDbPath);
m_tcVSS.SelectItem(m_tcVSS.GetRootItem());
}
else
m_cbDatabases.SetCurSel(-1);
// restore column widths unless column count has changed
if (m_lcResults.GetHeaderCtrl())
{
int nCol = m_lcResults.GetHeaderCtrl()->GetItemCount();
if (nCol == (int)AfxGetApp()->GetProfileInt("Settings", "ColumnCount", -1))
{
while (nCol--)
{
CString sCol;
sCol.Format("Col%dWidth", nCol);
m_lcResults.SetColumnWidth(nCol, AfxGetApp()->GetProfileInt("Settings", sCol, m_lcResults.GetColumnWidth(nCol)));
}
}
}
// restore sort info
SetSortColumn(AfxGetApp()->GetProfileInt("Settings", "SortColumn", 0),
AfxGetApp()->GetProfileInt("Settings", "SortAscending", TRUE));
}
void CVssreporterDlg::EnableControls()
{
UpdateData();
GetDlgItem(IDC_REPORT)->EnableWindow(!m_bReporting && !m_sCurProject.IsEmpty());
GetDlgItem(IDC_CANCELREPORT)->EnableWindow(m_bReporting);
GetDlgItem(IDC_COPYRESULTS)->EnableWindow(!m_bReporting && m_lcResults.GetSelectedCount());
GetDlgItem(IDC_PRINT)->EnableWindow(!m_bReporting && m_lcResults.GetItemCount());
GetDlgItem(IDOK)->EnableWindow(!m_bReporting);
GetDlgItem(IDC_DATABASELIST)->EnableWindow(!m_bReporting);
}
void CVssreporterDlg::OnSelchangedVsstree(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
m_sCurProject = m_tcVSS.GetSelectedProject();
EnableControls();
*pResult = 0;
}
void CVssreporterDlg::MakeValidPath(CString& sPath)
{
sPath.Replace('/', '\\');
}
void CVssreporterDlg::OnBrowsedatabase()
{
CFileDialog dialog(TRUE, NULL, m_sDbPath, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "VSS Databases (srcsafe.ini)|*.ini||");
dialog.m_ofn.lpstrTitle = "Open Database";
if (dialog.DoModal() == IDOK)
{
CString sDatabase = dialog.GetPathName();
if (!m_tcVSS.OpenDatabase(sDatabase, m_sUsername, m_sPassword))
{
MessageBox("Please ensure that the user name and password are correct and \nthat the named user has the correct access rights.",
"Unable to Open Vss Database", MB_OK);
return;
}
// add to database list if new and it could be opened
m_sDbPath = sDatabase;
if (m_cbDatabases.FindString(-1, sDatabase) == CB_ERR)
m_cbDatabases.AddString(sDatabase);
m_cbDatabases.SelectString(-1, sDatabase);
UpdateData(FALSE);
EnableControls();
}
}
void CVssreporterDlg::OnClose()
{
OnOK();
}
void CVssreporterDlg::OnDestroy()
{
CDialog::OnDestroy();
if (m_vssdb)
{
m_vssdb.Release();
m_vssdb = NULL;
}
}
void CVssreporterDlg::OnCopyResults()
{
CRect rButton;
GetDlgItem(IDC_COPYRESULTS)->GetWindowRect(rButton);
CMenu menu;
if (menu.LoadMenu(IDR_COPYPOPUP))
{
CMenu* pPopup = menu.GetSubMenu(0);
if (pPopup)
{
TPMPARAMS tpmp;
tpmp.cbSize = sizeof(TPMPARAMS);
tpmp.rcExclude = rButton;
::TrackPopupMenuEx(*pPopup, TPM_LEFTALIGN, rButton.right, rButton.top, *this, &tpmp);
}
}
}
CString CVssreporterDlg::GetResultsAsText(BOOL bTabbed)
{
int nNumItems = m_lcResults.GetSelectedCount();
if (!nNumItems)
return CString();
CWaitCursor cursor;
// create a single string containing all results
// padding as we go
CString sDate = m_lcResults.GetItemText(0, MODDATE);
// calculate the final string length
// (note: this will be too long for tabbed but we resize at the end)
ASSERT (m_aColLengths.GetSize());
int nLength = 0;
for (int nCol = 0; nCol < NUMCOLUMNS; nCol++)
nLength += nNumItems * (m_aColLengths[nCol] + 3); // 3 for spacing
nLength += 2 * nNumItems; // for '\r\n' on each line
// get the buffer as a raw char* of the required length
CString sResults;
LPTSTR szResults = sResults.GetBuffer(nLength);
// init with spaces (so we don't have to manually pad)
memset(szResults, bTabbed ? '\t' : ' ', nLength);
int nOffset = 0;
POSITION pos = m_lcResults.GetFirstSelectedItemPosition();
while (pos)
{
int nItem = m_lcResults.GetNextSelectedItem(pos);
for (int nCol = 0; nCol < NUMCOLUMNS; nCol++)
{
CString sItemCol = m_lcResults.GetItemText(nItem, nCol);
CopyMemory(szResults + nOffset, sItemCol, sItemCol.GetLength());
if (bTabbed)
nOffset += sItemCol.GetLength() + 1;
else
nOffset += m_aColLengths[nCol] + 3; // 3 for spacing
}
CopyMemory(szResults + nOffset, "\r\n", 2);
nOffset += 2;
}
ASSERT (bTabbed || nOffset == nLength);
sResults.ReleaseBuffer(nLength);
if (bTabbed)
sResults.TrimRight();
return sResults;
}
CString CVssreporterDlg::GetResultsAsXml()
{
int nNumItems = m_lcResults.GetSelectedCount();
if (!nNumItems)
return CString();
CWaitCursor cursor;
// create a single string containing all results
// padding as we go
CString sDate = m_lcResults.GetItemText(0, MODDATE);
// calculate the final string length
ASSERT (m_aColLengths.GetSize());
int nLength = 0;
LPCTSTR szOpenTags[] = { "<FILE>", "<PATH>", "<VERSION>", "<USER>", "<DATE>", "<COMMENT>" };
LPCTSTR szCloseTags[] = { "</FILE>", "</PATH>", "</VERSION>", "</USER>", "</DATE>", "</COMMENT>" };
nLength += nNumItems * lstrlen("</RESULT>\r\n") * 2;
for (int nCol = 0; nCol < NUMCOLUMNS; nCol++)
{
nLength += nNumItems * 2; // '\t\t'
nLength += nNumItems * lstrlen(szOpenTags[nCol]);
nLength += nNumItems * m_aColLengths[nCol];
nLength += nNumItems * lstrlen(szCloseTags[nCol]);
nLength += nNumItems * 2; // '\r\n' on each line
}
// add a bit for the query and toplevel tags
nLength += m_sQuery.GetLength() * 2; // to allow for replacing quotes and such
nLength += lstrlen("</RESULTS>\r\n") * 2;
// get the buffer as a raw char* of the required length
CString sResults;
LPTSTR szResults = sResults.GetBuffer(nLength);
// init with spaces
memset(szResults, ' ', nLength);
// add top level tag
CopyMemory(szResults, "<RESULTS>\r\n", lstrlen("<RESULTS>\r\n"));
szResults += lstrlen("<RESULTS>\r\n");
// add query
CString sQuery;
sQuery.Format("\t<QUERY>%s</QUERY>\r\n", m_sQuery);
// replace quotes and such like
CopyMemory(szResults, (LPCTSTR)sQuery, sQuery.GetLength());
szResults += sQuery.GetLength();
// add results
POSITION pos = m_lcResults.GetFirstSelectedItemPosition();
while (pos)
{
int nItem = m_lcResults.GetNextSelectedItem(pos);
CopyMemory(szResults, "\t<RESULT>\r\n", lstrlen("\t<RESULT>\r\n"));
szResults += lstrlen("\t<RESULT>\r\n");
for (int nCol = 0; nCol < NUMCOLUMNS; nCol++)
{
CString sItemCol = m_lcResults.GetItemText(nItem, nCol);
CString sResult;
sResult.Format("\t\t%s%s%s\r\n", szOpenTags[nCol], sItemCol, szCloseTags[nCol]);
CopyMemory(szResults, sResult, sResult.GetLength());
szResults += sResult.GetLength();
}
CopyMemory(szResults, "\t</RESULT>\r\n", lstrlen("\t</RESULT>\r\n"));
szResults += lstrlen("\t</RESULT>\r\n");
}
// add top level tag
CopyMemory(szResults, "</RESULTS>\r\n", lstrlen("</RESULTS>\r\n"));
szResults += lstrlen("</RESULTS>\r\n");
sResults.ReleaseBuffer(nLength);
return sResults;
}
CString CVssreporterDlg::GetUserFilter(const VssQuery& query)
{
UpdateData();
switch (query.nUserFilter)
{
case ME:
return m_sUsername;
case OTHERUSER:
return query.sOtherUser;
case EVERYONE:
default:
return "";
}
}
void CVssreporterDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if (nType != SIZE_MINIMIZED)
{
if (m_sizeOrg.cx && m_sizeOrg.cy)
{
CDeferWndMove dwm(20);
int nXOffset = cx - m_sizeOrg.cx, nYOffset = cy - m_sizeOrg.cy;
// repos left-hand ctrls up or down only
if (nYOffset)
{
dwm.OffsetCtrl(this, IDC_QUERYLABEL, 0, nYOffset);
dwm.OffsetCtrl(this, IDC_QUERY, 0, nYOffset);
dwm.OffsetCtrl(this, IDC_EDITQUERY, 0, nYOffset);
dwm.OffsetCtrl(this, IDC_QUERYDIVIDER, 0, nYOffset);
dwm.OffsetCtrl(this, IDC_REPORT, 0, nYOffset);
dwm.OffsetCtrl(this, IDC_CANCELREPORT, 0, nYOffset);
dwm.OffsetCtrl(this, IDC_LEFTDIVIDER, 0, nYOffset);
dwm.OffsetCtrl(this, IDC_RECURSIVE, 0, nYOffset);
dwm.OffsetCtrl(this, IDC_IGNOREDELETED, 0, nYOffset);
// resize vss tree
dwm.ResizeCtrl(this, IDC_VSSTREE, 0, nYOffset);
}
// repos right-hand ctrls
if (nXOffset || nYOffset)
{
// gripper
dwm.OffsetCtrl(this, IDC_GRIPPER, nXOffset, nYOffset);
// vert only
dwm.OffsetCtrl(this, IDC_FILESFOUND, 0, nYOffset);
// resize
dwm.ResizeCtrl(this, IDC_VERTDIVIDER, 0, nYOffset);
// vert and/or horz
dwm.OffsetCtrl(this, IDC_COPYTOCLIPBOARD, nXOffset, nYOffset);
dwm.OffsetCtrl(this, IDC_DIFFTOOL, nXOffset, nYOffset);
dwm.OffsetCtrl(this, IDC_PRINT, nXOffset, nYOffset);
dwm.OffsetCtrl(this, IDC_RESIZE, nXOffset, nYOffset);
CRect rOK = dwm.OffsetCtrl(this, IDOK, nXOffset, nYOffset);
// resize progress text to fit
CRect rCtrl = dwm.OffsetCtrl(this, IDC_PROGRESS);
rCtrl.OffsetRect(0, nYOffset);
rCtrl.right = rOK.left - 10;
dwm.MoveWindow(GetDlgItem(IDC_PROGRESS), rCtrl);
// resize results list to suit
rCtrl = dwm.OffsetCtrl(this, IDC_FILELIST); // just gets the current rect
rCtrl.right += (nXOffset);
rCtrl.bottom += (nYOffset);
dwm.MoveWindow(&m_lcResults, rCtrl);
// and base divider
rCtrl = dwm.OffsetCtrl(this, IDC_BASEDIVIDER); // just gets the current rect
rCtrl.OffsetRect(0, nYOffset);
rCtrl.right += (nXOffset);
dwm.MoveWindow(GetDlgItem(IDC_BASEDIVIDER), rCtrl);
}
GetDlgItem(IDC_GRIPPER)->ShowWindow(nType == SIZE_MAXIMIZED ? SW_HIDE : SW_SHOW);
}
m_sizeOrg.cx = cx;
m_sizeOrg.cy = cy;
}
}
void CVssreporterDlg::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
CDialog::OnGetMinMaxInfo(lpMMI);
// original dialog template size
lpMMI->ptMinTrackSize.x = 640;
lpMMI->ptMinTrackSize.y = 480;
}
void CVssreporterDlg::OnContextMenu(CWnd* pWnd, CPoint point)
{
if (pWnd == &m_lcResults)
{
CMenu menu;
if (menu.LoadMenu(IDR_RESULTSPOPUP))
{
CMenu* pPopup = menu.GetSubMenu(0);
if (pPopup)
{
int nNumSel = m_lcResults.GetSelectedCount();
// modify string to show project name if a single item is selected
if (nNumSel == 1)
{
CString sItem, sProject = GetSelectedResult(FILEPATH);
if (!sProject.IsEmpty())
{
sItem.Format("Run Visual SourceSafe (%s)", sProject);
pPopup->ModifyMenu(0, MF_BYPOSITION, ID_RESULTS_VISUALSOURCESAFE, sItem);
}
}
// enable diff item if only one item is selected
if (nNumSel == 1)
pPopup->ModifyMenu(ID_RESULTS_DIFF, MF_BYCOMMAND, ID_RESULTS_DIFF, "Show &Differences with local version");
else if (nNumSel != 2)
pPopup->EnableMenuItem(ID_RESULTS_DIFF, MF_BYCOMMAND | MF_GRAYED);
// enable copy items if any items selected
pPopup->EnableMenuItem(2, MF_BYPOSITION | (nNumSel ? MF_ENABLED : MF_GRAYED));
pPopup->EnableMenuItem(4, MF_BYPOSITION | (nNumSel ? MF_ENABLED : MF_GRAYED));
pPopup->EnableMenuItem(5, MF_BYPOSITION | (nNumSel ? MF_ENABLED : MF_GRAYED));
pPopup->EnableMenuItem(6, MF_BYPOSITION | (nNumSel ? MF_ENABLED : MF_GRAYED));
pPopup->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this);
}
}
}
}
void CVssreporterDlg::OnShowVSS()
{
LPCTSTR szVSSPath = GetVSSExplorerPath();
if (!szVSSPath)
return;
CString sCmdLine, sProject = GetSelectedResult(FILEPATH);
if (!sProject.IsEmpty())
sCmdLine.Format("-p\"%s\" -y%s,%s", sProject, m_sUsername, m_sPassword);
ShellExecute(*this, NULL, szVSSPath, sCmdLine, NULL, SW_NORMAL);
}
CString CVssreporterDlg::GetSelectedResult(int nCol, int nItem)
{
CString sSel;
POSITION pos = m_lcResults.GetFirstSelectedItemPosition();
if (pos)
{
int nSel = -1;
do
{
nSel = m_lcResults.GetNextSelectedItem(pos);
}
while (nItem-- > 0 && pos);
if (nSel != -1)
sSel = m_lcResults.GetItemText(nSel, nCol);
}
return sSel;
}
LPCTSTR CVssreporterDlg::GetVSSExplorerPath()
{
DWORD dwAttr = ::GetFileAttributes(m_sVSSPath);
if (dwAttr == 0xffffffff)
{
// see if we can find it ourselves
m_sVSSPath = GetVSSPath() + "\\ssexp.exe";
if (::GetFileAttributes(m_sVSSPath) == 0xffffffff)
{
UINT uRes = MessageBox("Before the you can use this feature for the first time, \nyou must specify the location of the VSS executable.",
"VSSReporter", MB_ICONINFORMATION | MB_OKCANCEL);
if (uRes == IDCANCEL)
return NULL;
// else
CFileDialog dialog(TRUE, "exe", "", OFN_FILEMUSTEXIST | OFN_EXPLORER | OFN_NONETWORKBUTTON | OFN_HIDEREADONLY,
"Visual SourceSafe (ssexp.exe)|ssexp.exe||");
dialog.m_ofn.lpstrTitle = "Locate VSS Executable";
if (dialog.DoModal() == IDCANCEL)
return NULL;
m_sVSSPath = dialog.GetPathName();
}
}
return (m_sVSSPath.IsEmpty() ? NULL : (LPCTSTR)m_sVSSPath);
}
BOOL CVssreporterDlg::IsVSSRegistered()
{
if (m_vssdb)
return TRUE;
// else
try
{
m_vssdb.CreateInstance(__uuidof(VSSDatabase));
}
catch(...)
{
}
return (m_vssdb != NULL);
}
int CVssreporterDlg::GetVSSVersion()
{
if (IsVSSRegistered())
return IVSSVERSION; // exact version not important
// else
int nVer = -1;
#ifndef _VSS5_ // ie. version 6
// try old getting old db interface
IVSSDatabaseOldPtr vssOld;
try
{
vssOld.CreateInstance(__uuidof(VSSDatabase));
if (vssOld)
nVer = 5;
}
catch(...)
{
}
#endif
return nVer;
}
CString CVssreporterDlg::GetVSSPath()
{
GUID id = __uuidof(VSSDatabase);
unsigned char* pszGuid;
RPC_STATUS rpcs = UuidToString(&id, &pszGuid);
CString sKey;
sKey.Format("CLSID\\{%s}\\InprocServer32", pszGuid);
RpcStringFree(&pszGuid);
CRegKey reg;
CString sPath;
if (reg.Open(HKEY_CLASSES_ROOT, sKey) == ERROR_SUCCESS)
{
reg.Read(NULL, sPath);
// make sure it exists
if (GetFileAttributes(sPath) == 0xffffffff)
sPath.Empty();
else
sPath = sPath.Left(sPath.ReverseFind('\\'));
}
return sPath;
}
void CVssreporterDlg::OnChangeUsernamePassword()
{
UpdateData();
}
void CVssreporterDlg::InitLocaleStrings()
{
char szCountryCode[3];
GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, szCountryCode, 3);
CString sResPath;
sResPath.Format("%s\\ss%s.dll", GetVSSPath(), szCountryCode);
HMODULE hRes = LoadLibrary(sResPath);
if (hRes)
{
char szFormat[64];
if (::LoadString(hRes, 4040, szFormat, 64))
{
m_sCreated.Format(szFormat, ""); // remove %s
m_sCreated.TrimLeft();
m_sCreated.TrimRight();
m_sCreated.MakeUpper();
}
if (::LoadString(hRes, 4053, szFormat, 64))
{
m_sCheckedIn.Format(szFormat, ""); // remove %s
m_sCheckedIn.TrimLeft();
m_sCheckedIn.TrimRight();
m_sCheckedIn.MakeUpper();
}
if (::LoadString(hRes, 4042, szFormat, 64))
{
m_sLabeled.Format(szFormat, ""); // remove %s
m_sLabeled.Replace("''", ""); // remove single quotes
m_sLabeled.TrimLeft();
m_sLabeled.TrimRight();
m_sLabeled.MakeUpper();
}
}
}
BOOL CVssreporterDlg::GetLabel(const IVSSVersionPtr& objVer, CString& sLabel)
{
/* CString vssAction = (LPCTSTR) objVer->GetAction();
vssAction.MakeUpper();
TRACE ("GetLabel: Action = %s\n", vssAction);
// compare action with our locale specific string
if (vssAction.Find(m_sLabeled) != -1)
{
sLabel = vssAction.Mid(m_sLabeled.GetLength() + 2);
sLabel = sLabel.Left(sLabel.GetLength() - 1);
return TRUE;
}
else // see if its got a label
*/ {
sLabel = (LPCTSTR) objVer->GetLabel();
if (sLabel.GetLength())
return TRUE;
}
return FALSE;
}
CString CVssreporterDlg::GetLabel(const IVSSVersionPtr& objVer)
{
CString sLabel = (LPCTSTR)objVer->GetLabel();
return sLabel;
}
int CVssreporterDlg::GetModType(const IVSSVersionPtr& objVer)
{
CString vssAction = (LPCTSTR) objVer->GetAction();
vssAction.MakeUpper();
// compare action with our locale specific strings
if (vssAction.Find(m_sCheckedIn) != -1)
return MODIFIED;
else if (vssAction.Find(m_sCreated) != -1)
return CREATED;
else if (vssAction.Find(m_sLabeled) != -1)
return LABELLED;
// else
return UNKNOWN;
}
BOOL CVssreporterDlg::IsCheckIn(const IVSSVersionPtr& objVer)
{
CString vssAction = (LPCTSTR) objVer->GetAction();
vssAction.MakeUpper();
// compare action with our locale specific string
return (vssAction.Find(m_sCheckedIn) != -1);
}
BOOL CVssreporterDlg::IsCreated(const IVSSVersionPtr& objVer)
{
CString vssAction = (LPCTSTR) objVer->GetAction();
vssAction.MakeUpper();
// compare action with our locale specific string
return (vssAction.Find(m_sCreated) != -1);
}
BOOL CVssreporterDlg::DateMatches(const IVSSVersionPtr& objVer, const VssQuery& query, COleDateTime& date)
{
// check the date
date = objVer->GetDate();
switch (query.nDateFilter)
{
case AFTER:
return (date > query.dtAfter);
case BEFORE:
return (date < query.dtBefore);
case BETWEEN:
return (date > query.dtAfter) && (date < query.dtBefore);
}
return FALSE;
}
void CVssreporterDlg::OnCopyFilestofolder()
{
CFolderDialog dialog("Select the folder to which you want to copy the matching files", NULL, this);
if (dialog.DoModal() == IDOK)
{
int nRes = AfxMessageBox("Do you want to retain the folder structure of the matching files?"
"\n\nSelecting 'No' will flatten the folder structure", MB_YESNOCANCEL);
if (nRes == IDCANCEL)
return;
BOOL bCopyTree = (nRes == IDYES);
CString sDestFolder = dialog.GetFolderPath();
// iterate the files, getting the latest version and copying as we go
POSITION pos = m_lcResults.GetFirstSelectedItemPosition();
while (pos)
{
int nItem = m_lcResults.GetNextSelectedItem(pos);
CString sFile = m_lcResults.GetItemText(nItem, FILENAME);
CString sPath = m_lcResults.GetItemText(nItem, FILEPATH);
long nVer = atoi(m_lcResults.GetItemText(nItem, VERSION));
CString sTempFile = GetVssFile(sFile, sPath, nVer);
if (!sTempFile.IsEmpty())
{
CString sDestPath;
if (bCopyTree)
sDestPath.Format("%s\\%s\\%s", sDestFolder, sPath.Mid(2), sFile);
else
sDestPath.Format("%s\\%s", sDestFolder, sFile);
// make sure the destination folder exist
MakeValidPath(sDestPath);
FileMisc::CreateFolder(sDestPath.Left(sDestPath.ReverseFind('\\')));
if (!CopyFile(sTempFile, sDestPath, FALSE))
{
// notify user
}
// delete the file
// make sure we reset any attributes first
::SetFileAttributes(sTempFile, FILE_ATTRIBUTE_NORMAL);
DeleteFile(sTempFile);
}
else // notify user
{
// TODO
}
}
}
}
void CVssreporterDlg::CopyTexttoclipboard(const CString& sText)
{
if (sText.IsEmpty())
return;
if (!::OpenClipboard(GetSafeHwnd()))
return;
::EmptyClipboard();
// Allocate a global memory object for the text.
HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, (sText.GetLength() + 1) * sizeof(TCHAR));
if (!hglbCopy)
{
CloseClipboard();
return;
}
// Lock the handle and copy the text to the buffer.
LPTSTR lptstrCopy = (LPTSTR)GlobalLock(hglbCopy);
memcpy(lptstrCopy, (LPVOID)(LPCTSTR)sText, sText.GetLength() * sizeof(TCHAR));
lptstrCopy[sText.GetLength()] = (TCHAR) 0; // null character
GlobalUnlock(hglbCopy);
// Place the handle on the clipboard.
::SetClipboardData(CF_TEXT, hglbCopy);
::CloseClipboard();
}
void CVssreporterDlg::CopyTexttofile(const CString& sText, BOOL bXml)
{
if (sText.IsEmpty())
return;
CFileDialog dialog(FALSE, bXml ? "xml" : "txt", NULL, 0,
bXml ? "Xml Files (*.xml)|*.xml||" : "Text Files (*.txt, *.csv)|*.txt;*.csv||", this);
if (dialog.DoModal() == IDOK)
{
CStdioFile file;
if (file.Open(dialog.GetFileName(), CFile::modeCreate | CFile::modeWrite))
{
file.WriteString(sText);
file.Close();
}
}
}
BOOL CVssreporterDlg::PreTranslateMessage(MSG* pMsg)
{
// handle return key ourselves
if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
{
if (pMsg->hwnd == ::GetDlgItem(*this, IDC_PASSWORD) ||
pMsg->hwnd == ::GetDlgItem(*this, IDC_USERNAME) ||
pMsg->hwnd == ::GetDlgItem(*this, IDC_DATABASELIST))
{
UpdateData();
if (!m_sDbPath.IsEmpty())
m_tcVSS.OpenDatabase(m_sDbPath, m_sUsername, m_sPassword);
}
return TRUE; // never pass it on
}
// handle shortcuts
else if (pMsg->message == WM_CHAR)
{
switch (pMsg->wParam)
{
case 1: // Ctrl + A
if (pMsg->hwnd == ::GetDlgItem(*this, IDC_FILELIST))
OnResultsSelectall();
break;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
void CVssreporterDlg::OnEditquery()
{
CVssQueryDlg dialog;
if (dialog.DoModal() == IDOK)
{
m_sQuery = dialog.GetQuery();
UpdateData(FALSE);
}
}
void CVssreporterDlg::OnColumnclickFilelist(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
// update sort column
SetSortColumn(pNMListView->iSubItem);
// do the sort
m_lcResults.SortItems(ResultsSortFunc, (DWORD)&m_sortInfo);
*pResult = 0;
}
void CVssreporterDlg::SetSortColumn(int nCol, BOOL bAscending)
{
if (nCol == m_sortInfo.nSortColumn)
{
if (bAscending == -1)
bAscending = !m_sortInfo.bSortAscending;
m_sortInfo.bSortAscending = bAscending;
// set image
HDITEM hdi;
hdi.mask = HDI_IMAGE | HDI_FORMAT;
m_lcResults.GetHeaderCtrl()->GetItem(nCol, &hdi);
hdi.fmt |= HDF_IMAGE | (nCol == TYPE ? HDF_RIGHT : HDF_BITMAP_ON_RIGHT);
hdi.iImage = m_sortInfo.bSortAscending ? 1 : 0;
m_lcResults.GetHeaderCtrl()->SetItem(nCol, &hdi);
}
else // remove previous image if nec and add the new one
{
HDITEM hdi;
hdi.mask = HDI_IMAGE | HDI_FORMAT;
if (m_sortInfo.nSortColumn != -1)
{
m_lcResults.GetHeaderCtrl()->GetItem(m_sortInfo.nSortColumn, &hdi);
hdi.fmt &= ~HDF_IMAGE;
m_lcResults.GetHeaderCtrl()->SetItem(m_sortInfo.nSortColumn, &hdi);
}
if (bAscending != -1)
m_sortInfo.bSortAscending = bAscending;
m_lcResults.GetHeaderCtrl()->GetItem(nCol, &hdi);
hdi.fmt |= HDF_IMAGE | (nCol == TYPE ? HDF_RIGHT : HDF_BITMAP_ON_RIGHT);
hdi.iImage = m_sortInfo.bSortAscending ? 1 : 0;
m_lcResults.GetHeaderCtrl()->SetItem(nCol, &hdi);
}
m_sortInfo.nSortColumn = nCol;
}
int CALLBACK CVssreporterDlg::ResultsSortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
SortInfo* pSortInfo = (SortInfo*)lParamSort;
switch (pSortInfo->nSortColumn)
{
case TYPE:
{
int nType1 = (int)pSortInfo->aResults[lParam1].nModType;
int nType2 = (int)pSortInfo->aResults[lParam2].nModType;
if (nType1 < nType2)
return pSortInfo->bSortAscending ? -1 : 1;
else if (nType1 > nType2)
return pSortInfo->bSortAscending ? 1 : -1;
else
return 0;
}
case FILENAME:
if (pSortInfo->bSortAscending)
return lstrcmpi(pSortInfo->aResults[lParam1].sFile, pSortInfo->aResults[lParam2].sFile);
else
return lstrcmpi(pSortInfo->aResults[lParam2].sFile, pSortInfo->aResults[lParam1].sFile);
case FILEPATH:
if (pSortInfo->bSortAscending)
return lstrcmpi(pSortInfo->aResults[lParam1].sPath, pSortInfo->aResults[lParam2].sPath);
else
return lstrcmpi(pSortInfo->aResults[lParam2].sPath, pSortInfo->aResults[lParam1].sPath);
case VERSION:
if (pSortInfo->aResults[lParam1].nVersion < pSortInfo->aResults[lParam2].nVersion)
return pSortInfo->bSortAscending ? -1 : 1;
else if (pSortInfo->aResults[lParam1].nVersion > pSortInfo->aResults[lParam2].nVersion)
return pSortInfo->bSortAscending ? 1 : -1;
else
return 0;
case USER:
if (pSortInfo->bSortAscending)
return lstrcmpi(pSortInfo->aResults[lParam1].sUser, pSortInfo->aResults[lParam2].sUser);
else
return lstrcmpi(pSortInfo->aResults[lParam2].sUser, pSortInfo->aResults[lParam1].sUser);
case MODDATE:
if (pSortInfo->aResults[lParam1].date < pSortInfo->aResults[lParam2].date)
return pSortInfo->bSortAscending ? -1 : 1;
else if (pSortInfo->aResults[lParam1].date > pSortInfo->aResults[lParam2].date)
return pSortInfo->bSortAscending ? 1 : -1;
else
return 0;
case COMMENT:
if (pSortInfo->bSortAscending)
return lstrcmpi(pSortInfo->aResults[lParam1].sComment, pSortInfo->aResults[lParam2].sComment);
else
return lstrcmpi(pSortInfo->aResults[lParam2].sComment, pSortInfo->aResults[lParam1].sComment);
case LABEL:
if (pSortInfo->bSortAscending)
return lstrcmpi(pSortInfo->aResults[lParam1].sLabel, pSortInfo->aResults[lParam2].sLabel);
else
return lstrcmpi(pSortInfo->aResults[lParam2].sLabel, pSortInfo->aResults[lParam1].sLabel);
}
return 0;
}
void CVssreporterDlg::OnCopyTexttoclipboardTabbed()
{
CopyTexttoclipboard(GetResultsAsText(TRUE));
}
void CVssreporterDlg::OnCopyTexttoclipboardFormatted()
{
CopyTexttoclipboard(GetResultsAsText(FALSE));
}
void CVssreporterDlg::OnCopyTexttofileFormatted()
{
CopyTexttofile(GetResultsAsText(FALSE), FALSE);
}
void CVssreporterDlg::OnCopyTexttofileTabbed()
{
CopyTexttofile(GetResultsAsText(TRUE), FALSE);
}
void CVssreporterDlg::OnSelchangedFilelist(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (!m_bReporting)
{
GetDlgItem(IDC_COPYRESULTS)->EnableWindow(m_lcResults.GetSelectedCount() > 0);
}
*pResult = 0;
}
void CVssreporterDlg::OnResultsSelectall()
{
int nItem = m_lcResults.GetItemCount();
while (nItem--)
m_lcResults.SetItemState(nItem, LVIS_FOCUSED | LVIS_SELECTED, 0x000F);
}
CString CVssreporterDlg::GetVssFile(LPCTSTR szName, LPCTSTR szPath, int nVersion)
{
CString sItemPath(szPath);
sItemPath.TrimRight();
if (sItemPath.Right(1) != "/")
sItemPath += '/';
sItemPath += szName;
try
{
IVSSItemPtr vssi = m_vssdb->GetVSSItem(COleVariant(sItemPath).bstrVal, 0);
if (vssi)
{
IVSSItemPtr vssiVer = vssi->GetVersion(_variant_t((long)nVersion));
if (vssiVer)
{
CString sTempPath, sPath(szPath);
if (!sPath.Replace("$/", ""))
sPath.Replace("$", "");
// make sure the folders exist
sTempPath.Format("c:\\temp\\vssreporter\\%s", sPath);
FileMisc::CreateFolder(sTempPath);
// ensure we have a unique file path
sTempPath.Format("c:\\temp\\vssreporter\\%s\\%s.%d", sPath, szName, nVersion);
MakeValidPath(sTempPath);
COleVariant sTempDir(sTempPath);
if (SUCCEEDED(vssiVer->Get(&sTempDir.bstrVal, 0))) // get the file
return sTempPath;
}
}
}
catch (...)
{
}
return "";
}
void CVssreporterDlg::OnCopyTexttofileXml()
{
CopyTexttofile(GetResultsAsXml(), TRUE);
}
void CVssreporterDlg::OnCopyTexttoclipboardXml()
{
CopyTexttoclipboard(GetResultsAsXml());
}
void CVssreporterDlg::OnResultsDiff()
{
int nSelCount = m_lcResults.GetSelectedCount();
if (nSelCount == 0 || nSelCount > 2)
return;
CString sFile1, sFile2;
if (nSelCount == 1) // diff with local ver
{
CString sFile = GetSelectedResult(FILENAME);
CString sProject = GetSelectedResult(FILEPATH);
int nVer = atoi(GetSelectedResult(VERSION));
sFile1 = GetVssFile(sFile, sProject, nVer);
sFile2 = GetLocalPath(sProject, sFile);
}
else // diff 2 vss files
{
CString sFile = GetSelectedResult(FILENAME, 0);
CString sProject = GetSelectedResult(FILEPATH, 0);
int nVer = atoi(GetSelectedResult(VERSION, 0));
sFile1 = GetVssFile(sFile, sProject, nVer);
sFile = GetSelectedResult(FILENAME, 1);
sProject = GetSelectedResult(FILEPATH, 1);
nVer = atoi(GetSelectedResult(VERSION, 1));
sFile2 = GetVssFile(sFile, sProject, nVer);
}
if (!sFile1.IsEmpty() && !sFile2.IsEmpty())
{
CConfigDiffToolDlg dialog;
CString sDiffTool = dialog.GetToolPath();
// check it exists
while (GetFileAttributes(sDiffTool) == 0xffffffff)
{
if (dialog.DoModal() == IDOK)
sDiffTool = dialog.GetToolPath();
else
return; // cancelled
}
CString sCommandLine = dialog.GetCommandLine();
// replace %1 and %2 in the command line
sCommandLine.Replace("%1", sFile1);
sCommandLine.Replace("%2", sFile2);
if ((int)ShellExecute(NULL, NULL, sDiffTool, sCommandLine, NULL, SW_SHOWNORMAL) <= 32)
{
}
}
}
CString CVssreporterDlg::GetLocalPath(LPCTSTR szVssProject, LPCTSTR szFileName)
{
CString sVSSPath;
sVSSPath.Format("%s/%s", szVssProject, szFileName);
COleVariant varBuf(sVSSPath); // special overloaded variant used to talk to OLE
IVSSItemPtr vssi;
try
{
vssi = m_vssdb->GetVSSItem(varBuf.bstrVal, 0); // Get the Item passed to us.
}
catch(...)
{
return "";
}
if (vssi->GetType() != 0l) // must be a file
{
return CString((LPCTSTR)vssi->GetLocalSpec());
}
return "";
}
void CVssreporterDlg::OnDiffTool()
{
CConfigDiffToolDlg dialog;
dialog.DoModal();
}