windows-nt/Source/XPSP1/NT/sdktools/sdv/changes.cpp
2020-09-26 16:20:57 +08:00

477 lines
12 KiB
C++

/*****************************************************************************
*
* changes.cpp
*
* View the result of a change query.
*
*****************************************************************************/
#include "sdview.h"
/*****************************************************************************
*
* class CChanges
*
*****************************************************************************/
class CChanges : public LVFrame, public BGTask {
friend DWORD CALLBACK CChanges_ThreadProc(LPVOID lpParameter);
protected:
LRESULT HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam);
private:
typedef LVFrame super;
LRESULT ON_WM_CREATE(UINT uiMsg, WPARAM wParam, LPARAM lParam);
LRESULT ON_WM_SETCURSOR(UINT uiMsg, WPARAM wParam, LPARAM lParam);
LRESULT ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam);
LRESULT ON_WM_INITMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam);
LRESULT ON_LM_ITEMACTIVATE(UINT uiMsg, WPARAM wParam, LPARAM lParam);
LRESULT ON_LM_GETINFOTIP(UINT uiMsg, WPARAM wParam, LPARAM lParam);
LRESULT ON_LM_GETCONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam);
LRESULT ON_LM_COPYTOCLIPBOARD(UINT uiMsg, WPARAM wParam, LPARAM lParam);
LRESULT ON_LM_DELETEITEM(UINT uiMsg, WPARAM wParam, LPARAM lParam);
int GetChangelist(int iItem);
private: /* Helpers */
CChanges()
{
SetAcceleratorTable(MAKEINTRESOURCE(IDA_CHANGES));
}
void _AdjustMenu(HMENU hmenu, int iItem, BOOL fContextMenu);
static DWORD CALLBACK s_BGInvoke(LPVOID lpParam);
DWORD _BGInvoke();
BOOL _BGGetSdCommandLine(String &str);
void _BuildHint();
void _ViewBug();
int _GetBugNumber(int iItem);
private:
StringCache _scQuery;
StringCache _scHint;
StringCache _scUser;
};
int _CChanges_AddError(HWND hwndChild, LPCTSTR psz)
{
LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = MAXLONG;
lvi.iSubItem = 0;
lvi.pszText = CCAST(LPTSTR, psz);
lvi.iItem = ListView_InsertItem(hwndChild, &lvi);
if (lvi.iItem == 0) {
ListView_SetCurSel(hwndChild, 0); /* Select the first item */
}
return lvi.iItem;
}
int CChanges::GetChangelist(int iItem)
{
if (iItem == -1) {
iItem = GetCurSel();
}
if (iItem >= 0) {
LVITEM lvi;
TCHAR sz[64];
if (ListView_GetItemText(_hwndChild, iItem, sz, ARRAYSIZE(sz))) {
Substring ss;
if (Parse(TEXT("$d$e"), sz, &ss)) {
return StrToInt(sz);
}
}
}
return -1;
}
LRESULT CChanges::ON_WM_CREATE(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
static const LVFCOLUMN s_rgcol[] = {
{ 7 ,IDS_COL_CHANGE ,LVCFMT_RIGHT },
{ 15 ,IDS_COL_DATE ,LVCFMT_LEFT },
{ 10 ,IDS_COL_DEV ,LVCFMT_LEFT },
{ 30 ,IDS_COL_COMMENT ,LVCFMT_LEFT },
{ 0 ,0 ,0 },
};
LRESULT lres = super::HandleMessage(uiMsg, wParam, lParam);
if (lres == 0 &&
SetWindowMenu(MAKEINTRESOURCE(IDM_CHANGES)) &&
CreateChild(LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS |
LVS_NOSORTHEADER,
LVS_EX_LABELTIP | LVS_EX_HEADERDRAGDROP |
LVS_EX_INFOTIP | LVS_EX_FULLROWSELECT) &&
AddColumns(s_rgcol) &&
BGStartTask(s_BGInvoke, this)) {
String str;
str << TEXT("sdv changes ") << _pszQuery;
SetWindowText(_hwnd, str);
lres = 0;
} else {
lres = -1;
}
return lres;
}
LRESULT CChanges::ON_WM_SETCURSOR(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
return BGFilterSetCursor(super::HandleMessage(uiMsg, wParam, lParam));
}
int CChanges::_GetBugNumber(int iItem)
{
// 3 = checkin comment
return ParseBugNumberFromSubItem(_hwndChild, iItem, 3);
}
LRESULT CChanges::ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
int iChange, iBug;
switch (GET_WM_COMMAND_ID(wParam, lParam)) {
case IDM_VIEWDESC:
ON_LM_ITEMACTIVATE(LM_ITEMACTIVATE, GetCurSel(), 0);
return 0;
case IDM_VIEWWINDIFF:
WindiffChangelist(GetChangelist(GetCurSel()));
return 0;
case IDM_VIEWBUG:
iBug = _GetBugNumber(GetCurSel());
if (iBug) {
OpenBugWindow(_hwnd, iBug);
}
return 0;
}
return super::HandleMessage(uiMsg, wParam, lParam);
}
void CChanges::_AdjustMenu(HMENU hmenu, int iItem, BOOL fContextMenu)
{
AdjustBugMenu(hmenu, _GetBugNumber(iItem), fContextMenu);
}
LRESULT CChanges::ON_WM_INITMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
_AdjustMenu(RECAST(HMENU, wParam), GetCurSel(), FALSE);
return 0;
}
LRESULT CChanges::ON_LM_GETINFOTIP(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
LPTSTR pszInfoTip = RECAST(LPTSTR, GetLVItem((int)wParam));
if (pszInfoTip) {
NMLVGETINFOTIP *pgit = RECAST(NMLVGETINFOTIP *, lParam);
pgit->pszText = pszInfoTip;
}
return 0;
}
LRESULT CChanges::ON_LM_ITEMACTIVATE(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
int iChange = GetChangelist((int)wParam);
if (iChange > 0) {
if (_scHint.IsEmpty()) {
_BuildHint();
}
String str;
str << iChange << _scHint;
LaunchThreadTask(CDescribe_ThreadProc, str);
}
return 0;
}
LRESULT CChanges::ON_LM_GETCONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
HMENU hmenu = LoadPopupMenu(MAKEINTRESOURCE(IDM_CHANGES_POPUP));
if (hmenu) {
_AdjustMenu(hmenu, (int)wParam, TRUE);
}
return RECAST(LRESULT, hmenu);
}
LRESULT CChanges::ON_LM_COPYTOCLIPBOARD(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
String str;
for (int iItem = (int)wParam; iItem < (int)lParam; iItem++) {
LPTSTR pszInfoTip = RECAST(LPTSTR, GetLVItem(iItem));
if (pszInfoTip) {
str << pszInfoTip << TEXT("\r\n");
}
}
SetClipboardText(_hwnd, str);
return 0;
}
LRESULT CChanges::ON_LM_DELETEITEM(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
if (lParam) {
LocalFree(RECAST(HLOCAL, lParam));
}
return 0;
}
LRESULT
CChanges::HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch (uiMsg) {
FW_MSG(WM_CREATE);
FW_MSG(WM_SETCURSOR);
FW_MSG(WM_COMMAND);
FW_MSG(WM_INITMENU);
FW_MSG(LM_ITEMACTIVATE);
FW_MSG(LM_GETINFOTIP);
FW_MSG(LM_GETCONTEXTMENU);
FW_MSG(LM_COPYTOCLIPBOARD);
FW_MSG(LM_DELETEITEM);
}
return super::HandleMessage(uiMsg, wParam, lParam);
}
//
// We build the hint only on demand since it takes a while and we
// don't want to slow down the initial query.
//
void CChanges::_BuildHint()
{
String str;
if (!_scQuery.IsEmpty()) {
String strTok, strPath;
Tokenizer tok(_scQuery);
while (tok.Token(strTok)) {
Substring ss;
if (Parse(TEXT("$p"), strTok, &ss) && ss.Length() > 0) {
ss.Finalize(); // Strip off the revision specifier
if (MapToFullDepotPath(strTok, strPath)) {
str << TEXT(' ') << QuoteSpaces(strPath);
}
}
}
}
_scHint = str;
}
//
// A private helper class that captures the parsing state machine.
//
class ChangesParseState : public CommentParser
{
public:
ChangesParseState(HWND hwndChild) : _iItem(-1), _hwndChild(hwndChild) {}
void Flush()
{
if (_iItem >= 0) {
_strFullDescription.Chomp();
LVITEM lvi;
lvi.iItem = _iItem;
lvi.iSubItem = 0;
lvi.mask = LVIF_PARAM;
lvi.lParam = RECAST(LPARAM, StrDup(_strFullDescription));
ListView_SetItem(_hwndChild, &lvi);
}
_iItem = -1;
CommentParser::Reset();
_strFullDescription.Reset();
}
void AddLine(LPCTSTR psz)
{
_strFullDescription << psz;
}
void AddError(LPCTSTR psz)
{
_iItem = _CChanges_AddError(_hwndChild, psz);
}
void AddEntry(Substring *rgss)
{
LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = MAXLONG;
lvi.iSubItem = 0;
lvi.pszText = rgss[0].Finalize();
_iItem = lvi.iItem = ListView_InsertItem(_hwndChild, &lvi);
if (lvi.iItem >= 0) {
lvi.iSubItem = 1;
lvi.pszText = rgss[1].Finalize();
ListView_SetItem(_hwndChild, &lvi);
lvi.iSubItem = 2;
lvi.pszText = rgss[2].Finalize();
LPTSTR psz = StrChr(lvi.pszText, TEXT('\\'));
if (psz) {
lvi.pszText = psz+1;
}
ListView_SetItem(_hwndChild, &lvi);
if (lvi.iItem == 0) {
ListView_SetCurSel(_hwndChild, 0); /* Select the first item */
}
}
}
void SetListViewSubItemText(int iSubItem, LPCTSTR psz)
{
if (_iItem >= 0) {
LVITEM lvi;
lvi.mask = LVIF_TEXT;
lvi.iItem = _iItem;
lvi.iSubItem = iSubItem;
lvi.pszText = CCAST(LPTSTR, psz);
ListView_SetItem(_hwndChild, &lvi);
}
}
void SetDev(LPCTSTR psz)
{
SetListViewSubItemText(2, psz);
}
void SetComment(LPCTSTR psz)
{
SetListViewSubItemText(3, psz);
}
private:
HWND _hwndChild;
int _iItem;
BOOL _fHaveComment;
String _strFullDescription;
};
DWORD CALLBACK CChanges::s_BGInvoke(LPVOID lpParam)
{
CChanges *self = RECAST(CChanges *, lpParam);
return self->_BGInvoke();
}
BOOL CChanges::_BGGetSdCommandLine(String &str)
{
str.Reset();
str << TEXT("changes -l -s submitted ");
/*
* Parse the switches as best we can.
*/
BOOL fMSeen = FALSE;
GetOpt opt(TEXT("mu"), _pszQuery);
for (;;) {
switch (opt.NextSwitch()) {
case TEXT('m'):
fMSeen = TRUE;
str << TEXT("-m ") << opt.GetValue() << TEXT(" ");
break;
case TEXT('i'):
str << TEXT("-i ");
break;
case TEXT('u'):
if (GlobalSettings.IsVersion(1, 60)) {
str << TEXT("-u ") << opt.GetValue() << TEXT(" ");
} else {
_scUser = opt.GetValue();
}
break;
case TEXT('\0'):
goto L_switch; // two-level break
default:
Help(_hwnd, TEXT("#chang"));
return FALSE;
}
}
L_switch:;
if (!fMSeen) {
str << TEXT("-m50 ");
}
/*
* If no filename is given, use *
*
* The query string will be useful later, so cache that away, too.
*/
String strQuery;
if (opt.Finished()) {
strQuery << TEXT("*");
} else {
while (opt.Token()) {
if (opt.GetValue()[0]) {
strQuery << ResolveBranchAndQuoteSpaces(opt.GetValue()) << TEXT(" ");
}
}
}
str << strQuery;
_scQuery = strQuery;
return TRUE;
}
DWORD CChanges::_BGInvoke()
{
String str;
if (_BGGetSdCommandLine(str)) {
SDChildProcess proc(str);
IOBuffer buf(proc.Handle());
ChangesParseState state(_hwndChild);
while (buf.NextLine(str)) {
Substring rgss[3]; // changeno, date, userid
if (Parse(TEXT("Change $d on $D by $p"), str, rgss)) {
state.Flush();
state.AddLine(str);
if (!_scUser.IsEmpty() &&
lstrcmpi(rgss[2].Finalize(), _scUser) != 0) {
/* This change is not for us; ignore it */
} else {
state.AddEntry(rgss);
}
} else if (str[0] == TEXT('\r')) {
state.AddLine(str);
} else if (str[0] == TEXT('\t')) {
state.AddLine(str);
str.Chomp();
state.AddComment(str);
} else {
state.Flush();
str.Chomp();
state.AddError(str);
}
}
state.Flush();
} else {
PostMessage(_hwnd, WM_CLOSE, 0, 0);
}
BGEndTask();
return 0;
}
DWORD CALLBACK CChanges_ThreadProc(LPVOID lpParameter)
{
return FrameWindow::RunThread(new CChanges, lpParameter);
}