477 lines
12 KiB
C++
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);
|
|
}
|