1069 lines
29 KiB
C
1069 lines
29 KiB
C
|
/*
|
||
|
* Private headers
|
||
|
*/
|
||
|
|
||
|
#ifndef STRICT
|
||
|
#define STRICT
|
||
|
#endif
|
||
|
|
||
|
#ifndef WIN32_LEAN_AND_MEAN /* build.exe will define it for us on NT */
|
||
|
#define WIN32_LEAN_AND_MEAN
|
||
|
#endif
|
||
|
#undef WINVER /* build process defines this */
|
||
|
#define WINVER 0x0400 /* Windows 95 compatible */
|
||
|
#define _WIN32_WINDOWS 0x0400 /* Windows 95 compatible */
|
||
|
#include <windows.h> /* Everybody's favourite */
|
||
|
#include <commctrl.h>
|
||
|
|
||
|
#ifndef RC_INVOKED
|
||
|
#include <windowsx.h> /* Message crackers */
|
||
|
#include <shlwapi.h>
|
||
|
#include <shellapi.h>
|
||
|
#endif
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Resources
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
/*
|
||
|
* Icons
|
||
|
*/
|
||
|
#define IDI_SDV 0x0001
|
||
|
|
||
|
/*
|
||
|
* Bitmaps
|
||
|
*/
|
||
|
#define IDB_PLUS 0x0001
|
||
|
#define IDB_IMAGES 0x0002
|
||
|
|
||
|
/*
|
||
|
* Strings
|
||
|
*/
|
||
|
#define IDS_TITLE 0x0001
|
||
|
#define IDS_IE4 0x0002
|
||
|
#define IDS_SD_EXEC_ERR 0x0003
|
||
|
#define IDS_DEFAULT_BUGPAGE 0x0004
|
||
|
#define IDS_VIEWBUG_FORMAT 0x0005
|
||
|
#define IDS_VIEWBUG_NONE 0x0006
|
||
|
|
||
|
#define IDS_COL_REV 0x0100
|
||
|
#define IDS_COL_CHANGE 0x0101
|
||
|
#define IDS_COL_OP 0x0102
|
||
|
#define IDS_COL_DATE 0x0103
|
||
|
#define IDS_COL_DEV 0x0104
|
||
|
#define IDS_COL_CHURN 0x0105
|
||
|
#define IDS_COL_COMMENT 0x0106
|
||
|
|
||
|
/*
|
||
|
* Menus
|
||
|
*/
|
||
|
#define IDM_CHANGES 1
|
||
|
#define IDM_CHANGES_POPUP 2
|
||
|
|
||
|
#define IDM_DESCRIBE 3
|
||
|
#define IDM_DESCRIBE_POPUP 4
|
||
|
|
||
|
#define IDM_FILELOG 5
|
||
|
#define IDM_FILELOG_POPUP 6
|
||
|
|
||
|
#define IDM_OPENED 7
|
||
|
#define IDM_OPENED_POPUP 8
|
||
|
|
||
|
|
||
|
#define IDM_EXIT 100
|
||
|
#define IDM_EXITALL 101
|
||
|
|
||
|
#define IDM_COPY 102
|
||
|
#define IDM_COPYALL 103
|
||
|
|
||
|
#define IDM_VIEWDESC 104
|
||
|
#define IDM_VIEWFILEDIFF 105
|
||
|
#define IDM_VIEWWINDIFF 106
|
||
|
#define IDM_VIEWBUG 107
|
||
|
#define IDM_VIEWFILELOG 108
|
||
|
|
||
|
#define IDM_HELP 200
|
||
|
|
||
|
/*
|
||
|
* Accelerators
|
||
|
*/
|
||
|
#define IDA_CHANGES 1
|
||
|
#define IDA_DESCRIBE 2
|
||
|
#define IDA_FILELOG 3
|
||
|
#define IDA_OPENED 4
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Assorted goo
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#ifndef RC_INVOKED
|
||
|
|
||
|
extern HINSTANCE g_hinst;
|
||
|
extern HCURSOR g_hcurWait;
|
||
|
extern HCURSOR g_hcurArrow;
|
||
|
extern HCURSOR g_hcurAppStarting;
|
||
|
extern LONG g_lThreads;
|
||
|
extern TCHAR g_szTitle[MAX_PATH];
|
||
|
extern UINT g_wShowWindow;
|
||
|
|
||
|
DWORD EndThreadTask(DWORD dwExitCode);
|
||
|
|
||
|
#ifndef ARRAYSIZE
|
||
|
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
|
||
|
#endif
|
||
|
|
||
|
#ifndef NO_VTABLE
|
||
|
#define NO_VTABLE __declspec(novtable)
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Because C++ syntax is so ugly.
|
||
|
*/
|
||
|
#define SAFECAST(T, p) static_cast<T>(p)
|
||
|
#define RECAST(T, p) reinterpret_cast<T>(p)
|
||
|
#define CCAST(T, p) const_cast<T>(p)
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Utility goo
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class String;
|
||
|
|
||
|
int ListView_GetCurSel(HWND hwnd);
|
||
|
void ListView_SetCurSel(HWND hwnd, int iIndex);
|
||
|
int ListView_GetSubItemText(HWND hwnd, int iItem, int iSubItem, LPTSTR pszBuf, int cch);
|
||
|
#undef ListView_GetItemText
|
||
|
#define ListView_GetItemText(hwnd, iItem, pszBuf, cch) \
|
||
|
ListView_GetSubItemText(hwnd, iItem, 0, pszBuf, cch)
|
||
|
|
||
|
void ChangeTabsToSpaces(LPTSTR psz);
|
||
|
|
||
|
void PremungeFilespec(LPTSTR psz);
|
||
|
void PostmungeFilespec(LPTSTR psz);
|
||
|
|
||
|
BOOL ContainsWildcards(LPCTSTR psz);
|
||
|
|
||
|
enum MAPTOX {
|
||
|
MAPTOX_DEPOT, // //depot/path
|
||
|
MAPTOX_CLIENT, // //CLIENT/path
|
||
|
MAPTOX_LOCAL, // C:\src\path
|
||
|
};
|
||
|
BOOL MapToXPath(LPCTSTR pszSD, String& strOut, MAPTOX X);
|
||
|
|
||
|
inline BOOL MapToFullDepotPath(LPCTSTR pszSD, String& strOut)
|
||
|
{ return MapToXPath(pszSD, strOut, MAPTOX_DEPOT); }
|
||
|
inline BOOL MapToClientPath(LPCTSTR pszSD, String& strOut)
|
||
|
{ return MapToXPath(pszSD, strOut, MAPTOX_CLIENT); }
|
||
|
BOOL MapToLocalPath(LPCTSTR pszSD, String& strOut);
|
||
|
|
||
|
void Help(HWND hwnd, LPCTSTR pszAnchor);
|
||
|
|
||
|
BOOL SpawnProcess(LPTSTR pszCommand);
|
||
|
void WindiffChangelist(int iChange);
|
||
|
void WindiffOneChange(LPTSTR pszPath);
|
||
|
|
||
|
int ParseBugNumber(LPCTSTR psz);
|
||
|
int ParseBugNumberFromSubItem(HWND hwnd, int iItem, int iSubItem);
|
||
|
void AdjustBugMenu(HMENU hmenu, int iBug, BOOL fContextMenu);
|
||
|
void OpenBugWindow(HWND hwnd, int iBug);
|
||
|
|
||
|
void JiggleMouse();
|
||
|
HMENU LoadPopupMenu(LPCTSTR pszMenu);
|
||
|
void MakeMenuPretty(HMENU hmenu);
|
||
|
void EnableDisableOrRemoveMenuItem(HMENU hmenu, UINT id, BOOL fEnable, BOOL fDelete);
|
||
|
void SetClipboardText(HWND hwnd, LPCTSTR psz);
|
||
|
|
||
|
// SCHAR is the opposite of TCHAR
|
||
|
#ifdef UNICODE
|
||
|
typedef CHAR SCHAR;
|
||
|
#else
|
||
|
typedef WCHAR SCHAR;
|
||
|
#endif
|
||
|
typedef SCHAR *LPSSTR;
|
||
|
typedef const SCHAR *LPCSSTR;
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Change types
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
enum {
|
||
|
OP_UNKNOWN,
|
||
|
OP_EDIT,
|
||
|
OP_DELETE,
|
||
|
OP_ADD,
|
||
|
OP_INTEGRATE,
|
||
|
OP_MERGE,
|
||
|
OP_BRANCH,
|
||
|
OP_COPY,
|
||
|
OP_IGNORED,
|
||
|
OP_MAX,
|
||
|
};
|
||
|
|
||
|
|
||
|
extern struct LogEntryImageMap {
|
||
|
LPCTSTR _pszOp;
|
||
|
int _iImage;
|
||
|
} c_rgleim[];
|
||
|
|
||
|
int ParseOp(LPCTSTR psz);
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Assertion goo
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
void AssertFailed(char *psz, char *pszFile, int iLine);
|
||
|
#define ASSERT(f) ((f) || (AssertFailed(#f, __FILE__, __LINE__), 0))
|
||
|
#else
|
||
|
#define ASSERT sizeof
|
||
|
#endif
|
||
|
|
||
|
#define COMPILETIME_ASSERT(f) switch (0) case 0: case f:
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Downlevel OS support (is this still needed?)
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#undef SUPPORT_DOWNLEVEL
|
||
|
#ifdef SUPPORT_DOWNLEVEL
|
||
|
|
||
|
typedef BOOL (WINAPI *QUEUEUSERWORKITEM)(LPTHREAD_START_ROUTINE, LPVOID, ULONG);
|
||
|
typedef BOOL (WINAPI *ALLOWSETFOREGROUNDWINDOW)(DWORD);
|
||
|
|
||
|
extern QUEUEUSERWORKITEM _QueueUserWorkItem;
|
||
|
extern ALLOWSETFOREGROUNDWINDOW _AllowSetForegroundWindow;
|
||
|
|
||
|
#else
|
||
|
|
||
|
#define _QueueUserWorkItem QueueUserWorkItem
|
||
|
#define _AllowSetForegroundWindow AllowSetForegroundWindow
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Ctype
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#define C_NONE 0x00
|
||
|
#define C_SPACE 0x01
|
||
|
#define C_DIGIT 0x02
|
||
|
#define C_ALPHA 0x04
|
||
|
#define C_DASH 0x08
|
||
|
#define C_BRNCH 0x10
|
||
|
|
||
|
#define B_DEFAULT C_NONE // Characters above 128 are this
|
||
|
|
||
|
extern const BYTE c_rgbCtype[128];
|
||
|
|
||
|
inline BOOL _InOrder(UINT tch1, UINT tch2, UINT tch3)
|
||
|
{
|
||
|
return tch2 - tch1 <= tch3 - tch1;
|
||
|
}
|
||
|
|
||
|
inline BOOL _Ctype(TCHAR tch, BYTE fl)
|
||
|
{
|
||
|
UINT ui = (UINT)tch;
|
||
|
BYTE b;
|
||
|
if (ui < ARRAYSIZE(c_rgbCtype)) {
|
||
|
b = c_rgbCtype[ui];
|
||
|
} else {
|
||
|
b = B_DEFAULT;
|
||
|
}
|
||
|
return b & fl;
|
||
|
}
|
||
|
|
||
|
inline BOOL IsSpace(TCHAR tch)
|
||
|
{
|
||
|
return _Ctype(tch, C_SPACE);
|
||
|
}
|
||
|
|
||
|
inline BOOL IsDigit(TCHAR tch)
|
||
|
{
|
||
|
return _Ctype(tch, C_DIGIT);
|
||
|
}
|
||
|
|
||
|
inline BOOL IsNZDigit(TCHAR tch)
|
||
|
{
|
||
|
return _InOrder(TEXT('1'), tch, TEXT('9'));
|
||
|
}
|
||
|
|
||
|
inline BOOL IsAlpha(TCHAR tch)
|
||
|
{
|
||
|
return _Ctype(tch, C_ALPHA);
|
||
|
}
|
||
|
|
||
|
inline BOOL IsAlias(TCHAR tch)
|
||
|
{
|
||
|
return _Ctype(tch, C_DASH | C_DIGIT | C_ALPHA);
|
||
|
}
|
||
|
|
||
|
inline BOOL IsBranch(TCHAR tch)
|
||
|
{
|
||
|
return _Ctype(tch, C_DASH | C_DIGIT | C_ALPHA | C_BRNCH);
|
||
|
}
|
||
|
|
||
|
inline BOOL _IsPrint(TCHAR tch)
|
||
|
{
|
||
|
return _InOrder(TEXT(' '), tch, TEXT('~'));
|
||
|
}
|
||
|
|
||
|
#define _IsWord(ch) ((UINT)(ch) > TEXT(' '))
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Commands
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
extern DWORD CALLBACK CChanges_ThreadProc(LPVOID lpParameter);
|
||
|
extern DWORD CALLBACK CDescribe_ThreadProc(LPVOID lpParameter);
|
||
|
extern DWORD CALLBACK CFileLog_ThreadProc(LPVOID lpParameter);
|
||
|
extern DWORD CALLBACK CFileOut_ThreadProc(LPVOID lpParameter);
|
||
|
extern DWORD CALLBACK COpened_ThreadProc(LPVOID lpParameter);
|
||
|
|
||
|
BOOL LaunchThreadTask(LPTHREAD_START_ROUTINE pfn, LPCTSTR pszArgs);
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* String, _String, OutputStringBuffer
|
||
|
*
|
||
|
* An extremely lame low-performance string class
|
||
|
* Be careful what you do with it since you can't do much.
|
||
|
*
|
||
|
* _String is the base class that does the heavy lifting and shunts long
|
||
|
* strings into the heap.
|
||
|
*
|
||
|
* String collects strings into a private buffer (which can overflow into
|
||
|
* the heap).
|
||
|
*
|
||
|
* OutputStringBuffer collects strings into the buffer provided
|
||
|
* (which can overflow into the heap). On destruction, copies the result
|
||
|
* if necessary back to the buffer provided.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class _String
|
||
|
{
|
||
|
public:
|
||
|
explicit _String(LPTSTR pszBufOrig, UINT cchBufOrig);
|
||
|
~_String();
|
||
|
void Reset();
|
||
|
operator LPTSTR() const { return _pszBuf; }
|
||
|
LPTSTR Buffer() const { return _pszBuf; }
|
||
|
int BufferLength() const { return _cchBuf; }
|
||
|
_String& operator<<(LPCTSTR psz) { Append(psz); return *this; }
|
||
|
_String& operator<<(const _String& str) { Append(str); return *this; }
|
||
|
_String& operator<<(int i);
|
||
|
_String& operator<<(TCHAR tch);
|
||
|
_String& operator=(LPCTSTR psz) { Reset(); Append(psz); return *this; }
|
||
|
int Length() const { return _cchLen; }
|
||
|
BOOL Append(LPCTSTR psz);
|
||
|
BOOL Append(const _String& str) { return Append(str, str.Length()); }
|
||
|
BOOL Append(LPCTSTR psz, int cchLen);
|
||
|
BOOL Grow(int cchLen) { return Append(NULL, cchLen); }
|
||
|
BOOL Ensure(int cchLen);
|
||
|
void Trim(int cchTrim = 1) { _pszBuf[_cchLen -= cchTrim] = TEXT('\0'); }
|
||
|
void SetLength(int cchLen) { ASSERT(_cchLen < _cchBuf); _cchLen = cchLen; }
|
||
|
void Chomp();
|
||
|
protected:
|
||
|
LPTSTR OriginalBuffer() const { return _pszBufOrig; }
|
||
|
private:
|
||
|
|
||
|
// Disallow the default copy constructor and assignment operator.
|
||
|
// Since our class has pointers, a block copy is never correct.
|
||
|
_String(const _String&); // never defined
|
||
|
_String& operator=(const _String&); // never defined
|
||
|
|
||
|
LPTSTR _pszBuf;
|
||
|
int _cchLen;
|
||
|
int _cchBuf;
|
||
|
LPTSTR _pszBufOrig;
|
||
|
};
|
||
|
|
||
|
class String : public _String
|
||
|
{
|
||
|
public:
|
||
|
explicit String() : _String(_szBuf, ARRAYSIZE(_szBuf)) { }
|
||
|
explicit String(LPCTSTR psz) : _String(_szBuf, ARRAYSIZE(_szBuf)) { Append(psz); }
|
||
|
String& operator=(LPCTSTR psz) { _String::operator=(psz); return *this; }
|
||
|
|
||
|
private:
|
||
|
/* Almost all strings are smaller than this */
|
||
|
TCHAR _szBuf[MAX_PATH];
|
||
|
};
|
||
|
|
||
|
class OutputStringBuffer : public _String
|
||
|
{
|
||
|
public:
|
||
|
OutputStringBuffer(LPTSTR pszBuf, UINT cchBuf)
|
||
|
: _String(pszBuf, cchBuf)
|
||
|
, _cchBufOrig(cchBuf) { }
|
||
|
~OutputStringBuffer();
|
||
|
private:
|
||
|
int _cchBufOrig;
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Custom output formats for "str << blah"
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class _StringFormat {
|
||
|
public:
|
||
|
operator LPCTSTR() const { return _pszDepotPath; }
|
||
|
_StringFormat(LPCTSTR pszDepotPath) : _pszDepotPath(pszDepotPath) { }
|
||
|
protected:
|
||
|
LPCTSTR _pszDepotPath;
|
||
|
};
|
||
|
|
||
|
#define MakeStringFormat(T) \
|
||
|
class T : public _StringFormat { \
|
||
|
public: \
|
||
|
T(LPCTSTR pszDepotPath) : _StringFormat(pszDepotPath) { } \
|
||
|
}; \
|
||
|
_String& operator<<(_String& str, T t); \
|
||
|
|
||
|
MakeStringFormat(QuoteSpaces)
|
||
|
MakeStringFormat(BranchOf)
|
||
|
MakeStringFormat(FilenameOf)
|
||
|
MakeStringFormat(ResolveBranchAndQuoteSpaces)
|
||
|
|
||
|
class StringResource {
|
||
|
public:
|
||
|
operator UINT() const { return _ids; }
|
||
|
StringResource(UINT ids) : _ids(ids) { }
|
||
|
protected:
|
||
|
UINT _ids;
|
||
|
};
|
||
|
_String& operator<<(_String& str, StringResource sr);
|
||
|
|
||
|
#define StringBeginsWith(psz, sz) (StrCmpN(psz, sz, ARRAYSIZE(sz) - 1) == 0)
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* StringCache is a class that babysits a string pointer.
|
||
|
*
|
||
|
* _StringCache is the version that requires explicit
|
||
|
* construction/destruction. It is safe to use in global structures.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
struct _StringCache {
|
||
|
public:
|
||
|
_StringCache& operator=(LPCTSTR psz);
|
||
|
BOOL IsEmpty() const { return _psz == NULL; }
|
||
|
operator LPTSTR() const { return Value(); }
|
||
|
LPTSTR Value() const { return IsEmpty() ? TEXT("") : _psz; }
|
||
|
|
||
|
public:
|
||
|
LPTSTR _psz;
|
||
|
};
|
||
|
|
||
|
class StringCache : public _StringCache
|
||
|
{
|
||
|
public:
|
||
|
StringCache() { _psz = NULL; }
|
||
|
StringCache(LPCTSTR psz) { _psz = NULL; *this = psz; }
|
||
|
~StringCache() { *this = NULL; }
|
||
|
StringCache& operator=(LPCTSTR psz)
|
||
|
{ *SAFECAST(_StringCache*,this) = psz; return *this; }
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* match.cpp - Highly-specialized depot path matching class
|
||
|
*/
|
||
|
class Match {
|
||
|
public:
|
||
|
Match(LPCTSTR pszPattern);
|
||
|
~Match() { delete [] _pszzPats; }
|
||
|
BOOL Matches(LPCTSTR pszPath);
|
||
|
|
||
|
private:
|
||
|
void _AddPattern(LPTSTR pszPat, String& strPats);
|
||
|
BOOL _Matches(LPCTSTR pszPat, LPCTSTR pszPath);
|
||
|
private:
|
||
|
LPTSTR _pszzPats;
|
||
|
LPTSTR _pszEnd;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* pipe.cpp
|
||
|
*/
|
||
|
|
||
|
class ChildProcess
|
||
|
{
|
||
|
public:
|
||
|
explicit ChildProcess() { Construct(); }
|
||
|
explicit ChildProcess(LPCTSTR pszCommand) { Construct(); Start(pszCommand); }
|
||
|
void Start(LPCTSTR pszCommand);
|
||
|
~ChildProcess() { Stop(); }
|
||
|
|
||
|
BOOL IsRunning() const { return _dwPid; }
|
||
|
HANDLE Handle() const { return _hRead; }
|
||
|
void Kill();
|
||
|
void Stop();
|
||
|
|
||
|
private:
|
||
|
void Construct()
|
||
|
{
|
||
|
_hProcess = NULL;
|
||
|
_hRead = NULL;
|
||
|
_dwPid = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
HANDLE _hRead;
|
||
|
HANDLE _hProcess;
|
||
|
DWORD _dwPid;
|
||
|
};
|
||
|
|
||
|
class SDChildProcess : public ChildProcess
|
||
|
{
|
||
|
public:
|
||
|
explicit SDChildProcess(LPCTSTR pszCommand);
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* buffer.cpp
|
||
|
*/
|
||
|
|
||
|
class IOBuffer
|
||
|
{
|
||
|
public:
|
||
|
IOBuffer(HANDLE hRead) { Init(hRead); }
|
||
|
void Init(HANDLE hRead) { _hRead = hRead; _cchBufUsed = 0; }
|
||
|
BOOL NextLine(String &);
|
||
|
|
||
|
private:
|
||
|
enum {
|
||
|
_cchBuf = MAX_PATH,
|
||
|
};
|
||
|
|
||
|
HANDLE _hRead;
|
||
|
TCHAR _rgchBuf[_cchBuf];
|
||
|
int _cchBufUsed; /* Number of bytes already in buffer */
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* LVInfoTip - lvframe.cpp
|
||
|
*
|
||
|
* Special hack class to support extra-long infotips in listview.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class LVInfoTip
|
||
|
{
|
||
|
public:
|
||
|
void Attach(HWND hwnd);
|
||
|
~LVInfoTip() { FreeLastTipAlt(); }
|
||
|
void SetInfoTip(NMLVGETINFOTIP *pgit, LPCTSTR pszTip);
|
||
|
private:
|
||
|
void FreeLastTipAlt();
|
||
|
BOOL ThunkLastTip();
|
||
|
|
||
|
static LRESULT SubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||
|
static LPCTSTR GetSubclassProperty() { return TEXT("LVInfoTip"); }
|
||
|
|
||
|
private:
|
||
|
WNDPROC _wndprocPrev;
|
||
|
BOOL _fGotInfoTip;
|
||
|
LPCTSTR _pszLastTip;
|
||
|
LPSSTR _pszLastTipAlt;
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* TreeList - treelist.cpp
|
||
|
*
|
||
|
* A tree-like listview.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
/*
|
||
|
* We maintain our own tree structure and add/delete items in the
|
||
|
* listview as necessary as tree nodes are expanded/collapsed.
|
||
|
*/
|
||
|
|
||
|
#define PTI_ONDEMAND RECAST(TreeItem*, -1)
|
||
|
#define PTI_APPEND RECAST(TreeItem*, -1)
|
||
|
|
||
|
class TreeItem {
|
||
|
public:
|
||
|
TreeItem * Parent() const { return _ptiParent; }
|
||
|
TreeItem * NextSibling() const { return _ptiNext; }
|
||
|
TreeItem * FirstChild() const { return _ptiChild; }
|
||
|
TreeItem * NextVisible();
|
||
|
BOOL IsExpanded() const { return _cVisKids > 0; }
|
||
|
BOOL IsExpandable() const { return _ptiChild != NULL; }
|
||
|
BOOL IsVisible();
|
||
|
BOOL IsVisibleOrRoot();
|
||
|
void SetExpandable() { SetFirstChild(PTI_ONDEMAND); }
|
||
|
void SetNotExpandable() { SetFirstChild(NULL); }
|
||
|
|
||
|
private:
|
||
|
friend class Tree;
|
||
|
|
||
|
BOOL IsSentinel() const { return this == NULL || this == PTI_ONDEMAND; }
|
||
|
void SetFirstChild(TreeItem *pti) { ASSERT(_ptiChild->IsSentinel()); _ptiChild = pti; }
|
||
|
|
||
|
private:
|
||
|
TreeItem * _ptiParent;
|
||
|
TreeItem * _ptiNext;
|
||
|
TreeItem * _ptiChild;
|
||
|
|
||
|
int _iDepth;
|
||
|
int _iVisIndex;
|
||
|
int _cVisKids;
|
||
|
};
|
||
|
|
||
|
#define TLN_GETDISPINFO 100 // Get pszText/cchTextMax given pti/iSubItem
|
||
|
#define TLN_FILLCHILDREN 101 // Fill children of pti
|
||
|
#define TLN_ITEMACTIVATE 102 // Default action on pti
|
||
|
#define TLN_GETINFOTIP 103 // Get pszText/cchTextMax given pti
|
||
|
#define TLN_DELETEITEM 104 // Destruct the node
|
||
|
#define TLN_GETCONTEXTMENU 105 // Display a context menu
|
||
|
|
||
|
struct NMTREELIST {
|
||
|
NMHDR hdr;
|
||
|
TreeItem *pti;
|
||
|
int iSubItem;
|
||
|
LPTSTR pszText;
|
||
|
int cchTextMax; // Doubles as state
|
||
|
};
|
||
|
|
||
|
class Tree {
|
||
|
public:
|
||
|
Tree(TreeItem *ptiRoot);
|
||
|
~Tree();
|
||
|
TreeItem* GetRoot() { return _ptiRoot; }
|
||
|
BOOL Insert(TreeItem *pti, TreeItem *ptiParent, TreeItem *ptiAfter);
|
||
|
void SetHWND(HWND hwnd);
|
||
|
int Expand(TreeItem *pti);
|
||
|
int Collapse(TreeItem *pti);
|
||
|
int ToggleExpand(TreeItem *pti);
|
||
|
void RedrawItem(TreeItem *pti);
|
||
|
TreeItem *GetCurSel();
|
||
|
void SetCurSel(TreeItem *pti);
|
||
|
HIMAGELIST SetImageList(HIMAGELIST himl);
|
||
|
|
||
|
public: //$$// make these protected someday
|
||
|
LRESULT OnGetDispInfo(NMLVDISPINFO *plvd);
|
||
|
LRESULT OnCacheHint(NMLVCACHEHINT *phint);
|
||
|
LRESULT OnItemActivate(int iItem);
|
||
|
LRESULT OnKeyDown(NMLVKEYDOWN *pkd);
|
||
|
LRESULT OnClick(NMITEMACTIVATE *pia);
|
||
|
LRESULT OnGetInfoTip(NMLVGETINFOTIP *pgit);
|
||
|
LRESULT OnGetContextMenu(int iItem);
|
||
|
LRESULT OnCopyToClipboard(int iMin, int iMax);
|
||
|
|
||
|
private:
|
||
|
void Recalc(TreeItem *pti);
|
||
|
void UpdateCache(TreeItem *pti, int iItem);
|
||
|
LRESULT SendNotify(int code, NMHDR *pnm);
|
||
|
TreeItem* IndexToItem(int iItem);
|
||
|
int InsertListviewItem(int iItem);
|
||
|
void UpdateVisibleCounts(TreeItem *pti, int cDelta);
|
||
|
void DeleteNode(TreeItem *pti);
|
||
|
void SendDeleteNotify(TreeItem *pti);
|
||
|
|
||
|
private:
|
||
|
HWND _hwnd;
|
||
|
TreeItem* _ptiRoot;
|
||
|
|
||
|
int _iHint;
|
||
|
TreeItem* _ptiHint;
|
||
|
};
|
||
|
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* FrameWindow - window.cpp
|
||
|
*
|
||
|
* A window that frames an inner control. The inner control is resized
|
||
|
* to fill the client area.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#define FW_MSG(msg) case msg: return ON_##msg(uiMsg, wParam, lParam)
|
||
|
|
||
|
class NO_VTABLE FrameWindow {
|
||
|
|
||
|
public:
|
||
|
static DWORD RunThread(FrameWindow *self, LPVOID lpParameter);
|
||
|
|
||
|
protected:
|
||
|
void SetAcceleratorTable(LPCTSTR pszAccel)
|
||
|
{
|
||
|
_haccel = LoadAccelerators(g_hinst, pszAccel);
|
||
|
}
|
||
|
|
||
|
BOOL SetWindowMenu(LPCTSTR pszMenu)
|
||
|
{
|
||
|
return SetMenu(_hwnd, LoadMenu(g_hinst, pszMenu));
|
||
|
}
|
||
|
|
||
|
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||
|
virtual ~FrameWindow() { }
|
||
|
|
||
|
//
|
||
|
// For talking back to yourself (typically from a base class back
|
||
|
// to a derived class). Short-circuits the WndProc wrapper for
|
||
|
// perf. Do not use cross-thread!
|
||
|
//
|
||
|
LRESULT SendSelfMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
return HandleMessage(uMsg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
static LRESULT CALLBACK WndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
|
||
|
HWND CreateFrameWindow();
|
||
|
|
||
|
protected:
|
||
|
HWND _hwnd;
|
||
|
HWND _hwndChild;
|
||
|
HACCEL _haccel;
|
||
|
LPTSTR _pszQuery;
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* LVFrame - lvframe.cpp
|
||
|
*
|
||
|
* A FrameWindow that frames a listview in report mode.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
#define LM_ITEMACTIVATE (WM_USER + 100) // wParam = iItem
|
||
|
#define LM_GETINFOTIP (WM_USER + 101) // wParam = iItem, lParam -> NMLVGETINFOTIP
|
||
|
#define LM_GETCONTEXTMENU (WM_USER + 102) // wParam = iItem
|
||
|
#define LM_COPYTOCLIPBOARD (WM_USER + 103) // wParam = iMin, lParam = iMax (exclusive)
|
||
|
#define LM_DELETEITEM (WM_USER + 104) // wParam = iItem, lParam = lParam
|
||
|
|
||
|
typedef struct LVFCOLUMN {
|
||
|
UINT cch;
|
||
|
UINT ids;
|
||
|
UINT fmt;
|
||
|
} LVFCOLUMN;
|
||
|
|
||
|
class NO_VTABLE LVFrame : public FrameWindow {
|
||
|
|
||
|
typedef FrameWindow super;
|
||
|
|
||
|
protected:
|
||
|
enum { IDC_LIST = 1 };
|
||
|
BOOL CreateChild(DWORD dwStyle, DWORD dwExStyle);
|
||
|
BOOL AddColumns(const LVFCOLUMN *rgcol);
|
||
|
|
||
|
void *GetLVItem(int iItem);
|
||
|
int GetCurSel() { return ListView_GetCurSel(_hwndChild); }
|
||
|
|
||
|
LRESULT HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam);
|
||
|
|
||
|
private:
|
||
|
|
||
|
LRESULT ON_WM_NOTIFY(UINT uiMsg, WPARAM wParam, LPARAM lParam);
|
||
|
LRESULT ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam);
|
||
|
LRESULT ON_WM_CONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam);
|
||
|
|
||
|
private:
|
||
|
LVInfoTip _it;
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* TLFrame - tlframe.cpp
|
||
|
*
|
||
|
* Wraps the Tree class. If I were less lazy, I would merge it into
|
||
|
* this class, but right now Tree is a separate class because I stole
|
||
|
* the code from sdflog...
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class NO_VTABLE TLFrame : public LVFrame {
|
||
|
|
||
|
typedef LVFrame super;
|
||
|
|
||
|
protected:
|
||
|
TLFrame(TreeItem *ptiRoot) : _tree(ptiRoot) { }
|
||
|
LRESULT HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam);
|
||
|
BOOL CreateChild(DWORD dwStyle, DWORD dwExStyle);
|
||
|
TreeItem *TLGetCurSel() { return _tree.GetCurSel(); };
|
||
|
|
||
|
private:
|
||
|
LRESULT ON_WM_NOTIFY(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);
|
||
|
|
||
|
protected:
|
||
|
Tree _tree;
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* BGTask
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class BGTask
|
||
|
{
|
||
|
protected:
|
||
|
BGTask() : _hDone(CreateEvent(NULL, TRUE, TRUE, NULL)), _fPending(FALSE) { }
|
||
|
BOOL BGConstructed() const { return _hDone != NULL; }
|
||
|
BOOL BGTaskPending() const { return _fPending; }
|
||
|
~BGTask();
|
||
|
BOOL BGStartTask(LPTHREAD_START_ROUTINE pfn, LPVOID Context);
|
||
|
void BGEndTask();
|
||
|
LRESULT BGFilterSetCursor(LRESULT lres);
|
||
|
private:
|
||
|
BOOL _fPending;
|
||
|
HANDLE _hDone;
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Parser
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class Substring
|
||
|
{
|
||
|
public:
|
||
|
LPTSTR _pszMin; // First character of the substring
|
||
|
LPTSTR _pszMax; // One past the last character of the substring
|
||
|
|
||
|
explicit Substring() { }
|
||
|
explicit Substring(LPCTSTR pszMin, LPCTSTR pszMax)
|
||
|
: _pszMin(CCAST(LPTSTR, pszMin)),
|
||
|
_pszMax(CCAST(LPTSTR, pszMax)) { }
|
||
|
|
||
|
LPTSTR SetStart(LPCTSTR psz)
|
||
|
{
|
||
|
return _pszMin = CCAST(LPTSTR, psz);
|
||
|
}
|
||
|
|
||
|
LPTSTR Start() { return _pszMin; }
|
||
|
SIZE_T Length() const { return _pszMax - _pszMin; }
|
||
|
|
||
|
SIZE_T SetEnd(LPCTSTR psz)
|
||
|
{
|
||
|
_pszMax = CCAST(LPTSTR, psz);
|
||
|
return Length();
|
||
|
}
|
||
|
|
||
|
LPTSTR Finalize() // This method works only on mutable substrings
|
||
|
{
|
||
|
*_pszMax = TEXT('\0');
|
||
|
return _pszMin;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
LPTSTR Parse(LPCTSTR pszFormat, LPCTSTR pszParse, Substring *rgss);
|
||
|
inline _String& operator<<(_String& str, Substring ss)
|
||
|
{
|
||
|
str.Append(ss._pszMin, (int)(ss._pszMax - ss._pszMin));
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* CommentParser - Parses checkin comments
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class NO_VTABLE CommentParser {
|
||
|
|
||
|
public:
|
||
|
|
||
|
virtual void SetDev(LPCTSTR psz) = 0;
|
||
|
virtual void SetComment(LPCTSTR psz) = 0;
|
||
|
|
||
|
void Reset() { _fHaveComment = FALSE; }
|
||
|
void AddComment(LPTSTR psz);
|
||
|
|
||
|
CommentParser() { Reset(); }
|
||
|
|
||
|
private:
|
||
|
BOOL _fHaveComment;
|
||
|
};
|
||
|
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Tokenizer
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class Tokenizer
|
||
|
{
|
||
|
public:
|
||
|
explicit Tokenizer() { }
|
||
|
explicit Tokenizer(LPCTSTR psz) { Restart(psz); }
|
||
|
void Restart(LPCTSTR psz);
|
||
|
LPCTSTR Unparsed() const { return _psz; }
|
||
|
BOOL Token(String& str);
|
||
|
BOOL Finished() const { return !*_psz; }
|
||
|
private:
|
||
|
LPCTSTR _psz;
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* GetOpt
|
||
|
*
|
||
|
* pszParams is the list of switches that take parameters. By default,
|
||
|
* switches do not take parameters.
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class GetOpt
|
||
|
{
|
||
|
public:
|
||
|
GetOpt(LPCTSTR pszParams, LPCTSTR pszArg)
|
||
|
: _pszParams(pszParams), _pszUnparsed(NULL), _tok(pszArg) { }
|
||
|
TCHAR NextSwitch();
|
||
|
BOOL Token() { return _tok.Token(_str); }
|
||
|
BOOL Finished() { return _tok.Finished(); }
|
||
|
LPCTSTR GetValue() const { return _pszValue; }
|
||
|
Tokenizer GetTokenizer() const { return _tok; }
|
||
|
|
||
|
private:
|
||
|
LPCTSTR _pszParams;
|
||
|
LPTSTR _pszUnparsed;
|
||
|
LPTSTR _pszValue;
|
||
|
Tokenizer _tok;
|
||
|
String _str;
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* WaitCursor
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
class WaitCursor
|
||
|
{
|
||
|
public:
|
||
|
explicit WaitCursor() : _hcur(SetCursor(g_hcurWait)) { }
|
||
|
~WaitCursor() { SetCursor(_hcur); }
|
||
|
private:
|
||
|
HCURSOR _hcur;
|
||
|
};
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Annoying version-checking functions
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
inline BOOL VER1GE(UINT A,
|
||
|
UINT a)
|
||
|
{ return A >= a; }
|
||
|
|
||
|
inline BOOL VER2GE(UINT A, UINT B,
|
||
|
UINT a, UINT b)
|
||
|
{ return A > a || (A == a && VER1GE(B,b)); }
|
||
|
|
||
|
inline BOOL VER3GE(UINT A, UINT B, UINT C,
|
||
|
UINT a, UINT b, UINT c)
|
||
|
{ return A > a || (A == a && VER2GE(B,C,b,c)); }
|
||
|
|
||
|
inline BOOL VER4GE(UINT A, UINT B, UINT C, UINT D,
|
||
|
UINT a, UINT b, UINT c, UINT d)
|
||
|
{ return A > a || (A == a && VER3GE(B,C,D,b,c,d)); }
|
||
|
|
||
|
|
||
|
/*****************************************************************************
|
||
|
*
|
||
|
* Globals
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
struct CGlobals
|
||
|
{
|
||
|
public:
|
||
|
void Initialize();
|
||
|
|
||
|
BOOL IsChurnEnabled() const { return _fChurn; }
|
||
|
const _StringCache& GetSdOpts() const { return _pszSdOpts; }
|
||
|
const _StringCache& GetUserName() const { return _rgpszSettings[SETTING_USERNAME]; }
|
||
|
const _StringCache& GetClientName() const { return _rgpszSettings[SETTING_CLIENTNAME]; }
|
||
|
const _StringCache& GetClientRoot() const { return _rgpszSettings[SETTING_CLIENTROOT]; }
|
||
|
const _StringCache& GetFakeDir() const { return _pszFakeDir; }
|
||
|
const _StringCache& GetLocalRoot() const { return _pszLocalRoot; }
|
||
|
LPCTSTR GetSdPath() const { return _szSd; }
|
||
|
String& FormatBugUrl(String& str, int iBug) const
|
||
|
{
|
||
|
str << _pszBugPagePre << iBug << _pszBugPagePost;
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
void SetChurn(BOOL fChurn) { _fChurn = fChurn; }
|
||
|
void SetSdOpts(LPCTSTR psz) { _pszSdOpts = psz; }
|
||
|
BOOL IsVersion(UINT major) { return VER1GE(_Major(), major); }
|
||
|
BOOL IsVersion(UINT major, UINT minor)
|
||
|
{ return VER2GE(_Major(), _Minor(), major, minor); }
|
||
|
|
||
|
|
||
|
private:
|
||
|
void _InitSdPath();
|
||
|
void _InitInfo();
|
||
|
void _InitFakeDir();
|
||
|
void _InitServerVersion();
|
||
|
void _InitBugPage();
|
||
|
|
||
|
UINT _Major() { return _rguiVer[VERSION_MAJOR]; }
|
||
|
UINT _Minor() { return _rguiVer[VERSION_MINOR]; }
|
||
|
|
||
|
enum {
|
||
|
SETTING_USERNAME,
|
||
|
SETTING_CLIENTNAME,
|
||
|
SETTING_CLIENTROOT,
|
||
|
SETTING_LOCALDIR,
|
||
|
SETTING_SERVERVERSION,
|
||
|
SETTING_MAX
|
||
|
};
|
||
|
|
||
|
enum {
|
||
|
VERSION_MAJOR,
|
||
|
VERSION_MINOR,
|
||
|
VERSION_BUILD,
|
||
|
VERSION_QFE,
|
||
|
VERSION_MAX,
|
||
|
};
|
||
|
|
||
|
private:
|
||
|
BOOL _fChurn;
|
||
|
_StringCache _pszSdOpts;
|
||
|
_StringCache _pszLocalRoot;
|
||
|
_StringCache _pszFakeDir;
|
||
|
_StringCache _pszBugPagePre;
|
||
|
_StringCache _pszBugPagePost;
|
||
|
_StringCache _rgpszSettings[SETTING_MAX];
|
||
|
UINT _rguiVer[VERSION_MAX];
|
||
|
TCHAR _szSd[MAX_PATH];
|
||
|
};
|
||
|
|
||
|
extern CGlobals GlobalSettings;
|
||
|
|
||
|
#endif
|