Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

554 lines
15 KiB

  1. /*****************************************************************************
  2. *
  3. * describe.cpp
  4. *
  5. * View a changelist a description.
  6. *
  7. *****************************************************************************/
  8. #include "sdview.h"
  9. /*****************************************************************************
  10. *
  11. * class CDescribe
  12. *
  13. *****************************************************************************/
  14. //
  15. // The LPARAM of the listview item has the following form:
  16. //
  17. // HIWORD = enum CATEGORY
  18. // LOWORD = original index (to break ties during sorting)
  19. //
  20. enum CATEGORY {
  21. CAT_HEADER, // changelist header
  22. CAT_MATCHED, // file that matches the pattern
  23. CAT_BLANK1, // separates matched from unmatched
  24. CAT_UNMATCHED, // files that don't match the pattern
  25. CAT_BLANK2, // separates unmatched from unchanged
  26. CAT_UNCHANGED, // unmatched files that weren't change
  27. };
  28. class CDescribe : public LVFrame, public BGTask {
  29. friend DWORD CALLBACK CDescribe_ThreadProc(LPVOID lpParameter);
  30. protected:
  31. LRESULT HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  32. private:
  33. enum {
  34. DM_RECALC = WM_APP
  35. };
  36. typedef LVFrame super;
  37. LRESULT ON_WM_CREATE(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  38. LRESULT ON_WM_SIZE(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  39. LRESULT ON_WM_SETCURSOR(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  40. LRESULT ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  41. LRESULT ON_WM_INITMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  42. LRESULT ON_LM_ITEMACTIVATE(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  43. LRESULT ON_LM_GETCONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  44. LRESULT ON_LM_COPYTOCLIPBOARD(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  45. LRESULT ON_DM_RECALC(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  46. private: /* Helpers */
  47. CDescribe()
  48. {
  49. SetAcceleratorTable(MAKEINTRESOURCE(IDA_DESCRIBE));
  50. }
  51. void _ResetChildWidth();
  52. void _AdjustMenu(HMENU hmenu, int iItem, BOOL fContextMenu);
  53. LPTSTR _GetSanitizedLine(int iItem, LPTSTR pszBuf, UINT cch);
  54. void ViewOneFile();
  55. void ViewFileLog();
  56. int _GetBugNumber(int iItem, BOOL fContextMenu);
  57. static LPTSTR _SanitizeClipboardText(LPTSTR psz);
  58. static DWORD CALLBACK s_BGInvoke(LPVOID lpParam);
  59. DWORD _BGInvoke();
  60. private:
  61. int _cxMax;
  62. int _iBug;
  63. Substring _ssChange;
  64. };
  65. LRESULT CDescribe::ON_WM_CREATE(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  66. {
  67. static const LVFCOLUMN s_rgcol[] = {
  68. { 30 ,IDS_COL_COMMENT ,LVCFMT_LEFT },
  69. { 0 ,0 ,0 },
  70. };
  71. LRESULT lres;
  72. if (Parse(TEXT("$d"), _pszQuery, &_ssChange)) {
  73. String str;
  74. str << TEXT("sdv describe ") << _ssChange;
  75. SetWindowText(_hwnd, str);
  76. lres = super::HandleMessage(uiMsg, wParam, lParam);
  77. if (lres == 0 &&
  78. SetWindowMenu(MAKEINTRESOURCE(IDM_DESCRIBE)) &&
  79. CreateChild(LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS |
  80. LVS_NOCOLUMNHEADER,
  81. LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT) &&
  82. AddColumns(s_rgcol) &&
  83. (SetWindowRedraw(_hwndChild, FALSE), TRUE) &&
  84. BGStartTask(s_BGInvoke, this)) {
  85. } else {
  86. lres = -1;
  87. }
  88. } else {
  89. Help(_hwnd, TEXT("#descr"));
  90. lres = -1;
  91. }
  92. return lres;
  93. }
  94. LRESULT CDescribe::ON_WM_SETCURSOR(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  95. {
  96. return BGFilterSetCursor(super::HandleMessage(uiMsg, wParam, lParam));
  97. }
  98. LPTSTR CDescribe::_GetSanitizedLine(int iItem, LPTSTR pszBuf, UINT cch)
  99. {
  100. LPTSTR pszPath = NULL;
  101. if (iItem >= 0 &&
  102. ListView_GetItemText(_hwndChild, iItem, pszBuf, cch)) {
  103. LPTSTR psz =_SanitizeClipboardText(pszBuf);
  104. if (psz != pszBuf) {
  105. pszPath = psz;
  106. }
  107. }
  108. return pszPath;
  109. }
  110. void CDescribe::ViewOneFile()
  111. {
  112. TCHAR sz[MAX_PATH];
  113. LPTSTR pszPath = _GetSanitizedLine(GetCurSel(), sz, ARRAYSIZE(sz));
  114. if (pszPath) {
  115. WindiffOneChange(pszPath);
  116. }
  117. }
  118. void CDescribe::ViewFileLog()
  119. {
  120. TCHAR sz[MAX_PATH];
  121. LPTSTR pszPath = _GetSanitizedLine(GetCurSel(), sz, ARRAYSIZE(sz));
  122. if (pszPath) {
  123. String str;
  124. LPTSTR pszSharp = StrChr(pszPath, TEXT('#'));
  125. if (pszSharp) {
  126. *pszSharp++ = TEXT('\0');
  127. str << TEXT("-#") << pszSharp << TEXT(' ');
  128. }
  129. str << pszPath;
  130. LaunchThreadTask(CFileLog_ThreadProc, str);
  131. }
  132. }
  133. LRESULT CDescribe::ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  134. {
  135. int iChange, iBug;
  136. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  137. case IDM_VIEWFILEDIFF:
  138. ViewOneFile();
  139. return 0;
  140. case IDM_VIEWWINDIFF:
  141. WindiffChangelist(StrToInt(_ssChange._pszMin));
  142. return 0;
  143. case IDM_VIEWBUG:
  144. iBug = _GetBugNumber(GetCurSel(), FALSE);
  145. if (iBug) {
  146. OpenBugWindow(_hwnd, iBug);
  147. }
  148. break;
  149. case IDM_VIEWFILELOG:
  150. ViewFileLog();
  151. break;
  152. }
  153. return super::HandleMessage(uiMsg, wParam, lParam);
  154. }
  155. //
  156. // Execute the default context menu item.
  157. //
  158. LRESULT CDescribe::ON_LM_ITEMACTIVATE(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  159. {
  160. HMENU hmenu = RECAST(HMENU, ON_LM_GETCONTEXTMENU(LM_GETCONTEXTMENU, wParam, 0));
  161. if (hmenu) {
  162. FORWARD_WM_COMMAND(_hwnd, GetMenuItemID(hmenu, 0), NULL, 0, SendMessage);
  163. DestroyMenu(hmenu);
  164. }
  165. return 0;
  166. }
  167. int CDescribe::_GetBugNumber(int iItem, BOOL fContextMenu)
  168. {
  169. LPARAM lParam = RECAST(LPARAM, GetLVItem(iItem));
  170. int iBug = 0;
  171. if (HIWORD(lParam) == CAT_HEADER && iItem != 0) {
  172. iBug = ParseBugNumberFromSubItem(_hwndChild, iItem, 0);
  173. }
  174. // If no bug number on the selection, use the default bug number
  175. // for this changelist.
  176. if (iBug == 0 && !fContextMenu) {
  177. iBug = _iBug;
  178. }
  179. return iBug;
  180. }
  181. void CDescribe::_AdjustMenu(HMENU hmenu, int iItem, BOOL fContextMenu)
  182. {
  183. TCHAR sz[MAX_PATH];
  184. sz[0] = TEXT('\0');
  185. if (iItem >= 0) {
  186. ListView_GetItemText(_hwndChild, iItem, sz, ARRAYSIZE(sz));
  187. }
  188. //
  189. // Disable IDM_VIEWFILEDIFF and IDM_VIEWFILELOG
  190. // if this is not a "..." item.
  191. //
  192. BOOL fEnable = (Parse(TEXT("... "), sz, NULL) != NULL);
  193. EnableDisableOrRemoveMenuItem(hmenu, IDM_VIEWFILEDIFF, fEnable, fContextMenu);
  194. EnableDisableOrRemoveMenuItem(hmenu, IDM_VIEWFILELOG, fEnable, fContextMenu);
  195. //
  196. // If a context menu, then nuke IDM_VIEWWINDIFF if this is not
  197. // the "Change" item.
  198. //
  199. if (fContextMenu && iItem != 0) {
  200. DeleteMenu(hmenu, IDM_VIEWWINDIFF, MF_BYCOMMAND);
  201. }
  202. AdjustBugMenu(hmenu, _GetBugNumber(iItem, fContextMenu), fContextMenu);
  203. MakeMenuPretty(hmenu);
  204. }
  205. LRESULT CDescribe::ON_WM_INITMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  206. {
  207. _AdjustMenu(RECAST(HMENU, wParam), GetCurSel(), FALSE);
  208. return 0;
  209. }
  210. LRESULT CDescribe::ON_LM_GETCONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  211. {
  212. HMENU hmenu = LoadPopupMenu(MAKEINTRESOURCE(IDM_DESCRIBE_POPUP));
  213. if (hmenu) {
  214. _AdjustMenu(hmenu, (int)wParam, TRUE);
  215. }
  216. return RECAST(LRESULT, hmenu);
  217. }
  218. //
  219. // If the line begins "...", then strip off everything except for the
  220. // depot specification.
  221. //
  222. LPTSTR CDescribe::_SanitizeClipboardText(LPTSTR psz)
  223. {
  224. Substring rgss[2];
  225. if (Parse(TEXT("... $P#$d"), psz, rgss)) {
  226. *(rgss[1]._pszMax) = TEXT('\0');
  227. return rgss[0].Start();
  228. } else {
  229. return psz;
  230. }
  231. }
  232. LRESULT CDescribe::ON_LM_COPYTOCLIPBOARD(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  233. {
  234. String str;
  235. TCHAR sz[MAX_PATH];
  236. int iMin = (int)wParam;
  237. int iMax = (int)lParam;
  238. // If a single-line copy, then special rules apply
  239. if (iMin + 1 == iMax) {
  240. if (ListView_GetItemText(_hwndChild, iMin, sz, ARRAYSIZE(sz))) {
  241. str << _SanitizeClipboardText(sz);
  242. }
  243. } else {
  244. for (int iItem = iMin; iItem < iMax; iItem++) {
  245. if (ListView_GetItemText(_hwndChild, iItem, sz, ARRAYSIZE(sz))) {
  246. str << sz;
  247. }
  248. str << TEXT("\r\n");
  249. }
  250. }
  251. SetClipboardText(_hwnd, str);
  252. return 0;
  253. }
  254. LRESULT CDescribe::ON_DM_RECALC(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  255. {
  256. ListView_SetColumnWidth(_hwndChild, 0, LVSCW_AUTOSIZE);
  257. _cxMax = ListView_GetColumnWidth(_hwndChild, 0);
  258. _ResetChildWidth();
  259. LVFINDINFO lvfi;
  260. lvfi.flags = LVFI_PARTIAL;
  261. lvfi.psz = TEXT("...");
  262. int iFirst = ListView_FindItem(_hwndChild, -1, &lvfi);
  263. if (iFirst >= 0) {
  264. ListView_SetCurSel(_hwndChild, iFirst);
  265. }
  266. SetWindowRedraw(_hwndChild, TRUE);
  267. return 0;
  268. }
  269. void CDescribe::_ResetChildWidth()
  270. {
  271. RECT rc;
  272. GetClientRect(_hwndChild, &rc);
  273. int cxMargins = GetSystemMetrics(SM_CXEDGE) * 2;
  274. int cxCol = max(_cxMax + cxMargins, rc.right);
  275. ListView_SetColumnWidth(_hwndChild, 0, cxCol);
  276. }
  277. LRESULT CDescribe::ON_WM_SIZE(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  278. {
  279. super::HandleMessage(uiMsg, wParam, lParam);
  280. if (_cxMax) {
  281. _ResetChildWidth();
  282. }
  283. return 0;
  284. }
  285. LRESULT
  286. CDescribe::HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  287. {
  288. switch (uiMsg) {
  289. FW_MSG(WM_CREATE);
  290. FW_MSG(WM_SETCURSOR);
  291. FW_MSG(WM_COMMAND);
  292. FW_MSG(WM_INITMENU);
  293. FW_MSG(WM_SIZE);
  294. FW_MSG(LM_ITEMACTIVATE);
  295. FW_MSG(LM_GETCONTEXTMENU);
  296. FW_MSG(LM_COPYTOCLIPBOARD);
  297. FW_MSG(DM_RECALC);
  298. }
  299. return super::HandleMessage(uiMsg, wParam, lParam);
  300. }
  301. //
  302. // A private helper class that captures the parsing state machine.
  303. //
  304. class DescribeParseState
  305. {
  306. enum PHASE {
  307. PHASE_HEADERS, // collecting the header
  308. PHASE_FILES, // collecting the files
  309. PHASE_DIFFS, // collecting the diffs
  310. };
  311. public:
  312. DescribeParseState(HWND hwndChild, LPCTSTR pszPattern)
  313. : _m(pszPattern)
  314. , _hwndChild(hwndChild)
  315. , _iPhase(PHASE_HEADERS)
  316. , _iLine(0)
  317. , _iMatch(-1)
  318. , _iBug(0)
  319. , _fAnyMatch(FALSE) { }
  320. void AddLine(LPTSTR psz, int iCat)
  321. {
  322. LVITEM lvi;
  323. lvi.mask = LVIF_TEXT | LVIF_PARAM;
  324. lvi.iItem = _iLine;
  325. lvi.iSubItem = 0;
  326. lvi.pszText = psz;
  327. lvi.lParam = MAKELONG(_iLine, iCat);
  328. _iLine++;
  329. ChangeTabsToSpaces(psz);
  330. ListView_InsertItem(_hwndChild, &lvi);
  331. }
  332. void SetMatchLine(LPTSTR psz)
  333. {
  334. // Turn the "====" into "..." so we can search for it
  335. LPTSTR pszDots = psz+1;
  336. pszDots[0] = TEXT('.');
  337. pszDots[1] = TEXT('.');
  338. pszDots[2] = TEXT('.');
  339. LPTSTR pszSharp = StrChr(pszDots, TEXT('#'));
  340. if (!pszSharp) return;
  341. pszSharp[1] = TEXT('\0'); // this wipes out the thing after the '#'
  342. LVFINDINFO lvfi;
  343. lvfi.flags = LVFI_PARTIAL;
  344. lvfi.psz = pszDots;
  345. _iMatch = ListView_FindItem(_hwndChild, 0, &lvfi);
  346. if (_iMatch >= 0) {
  347. _cAdded = _cDeleted = 0;
  348. }
  349. }
  350. void FlushMatch()
  351. {
  352. if (_iMatch >= 0) {
  353. String str;
  354. str.Grow(MAX_PATH-1);
  355. LVITEM lvi;
  356. lvi.iItem = _iMatch;
  357. lvi.mask = LVIF_TEXT | LVIF_PARAM;
  358. lvi.iSubItem = 0;
  359. lvi.pszText = str;
  360. lvi.cchTextMax = str.BufferLength();
  361. if (ListView_GetItem(_hwndChild, &lvi)) {
  362. str.SetLength(lstrlen(str));
  363. str << TEXT(" (") << _cDeleted << TEXT('/') << _cAdded << TEXT(")");
  364. lvi.pszText = str;
  365. if (_cDeleted + _cAdded == 0) {
  366. lvi.lParam = MAKELONG(LOWORD(lvi.lParam), CAT_UNCHANGED);
  367. }
  368. ListView_SetItem(_hwndChild, &lvi);
  369. }
  370. _iMatch = -1;
  371. }
  372. }
  373. void ParseLine(String& str)
  374. {
  375. Substring rgss[3];
  376. switch (_iPhase) {
  377. case PHASE_HEADERS:
  378. if (Parse(TEXT("... "), str, NULL)) {
  379. _iPhase = PHASE_FILES;
  380. goto L_PHASE_FILES;
  381. }
  382. if (_iBug == 0 && str[0] == TEXT('\t')) {
  383. _iBug = ParseBugNumber(str);
  384. }
  385. AddLine(str, CAT_HEADER);
  386. break;
  387. case PHASE_FILES:
  388. if (Parse(TEXT("... "), str, NULL)) {
  389. L_PHASE_FILES:
  390. int iCat;
  391. if (_m.Matches(str + 4)) {
  392. _fAnyMatch = TRUE;
  393. iCat = CAT_MATCHED;
  394. } else {
  395. iCat = CAT_UNMATCHED;
  396. }
  397. AddLine(str, iCat);
  398. } else {
  399. _iPhase = PHASE_DIFFS;
  400. }
  401. break;
  402. case PHASE_DIFFS:
  403. if (Parse(TEXT("==== "), str, NULL)) {
  404. SetMatchLine(str);
  405. } else if (Parse(TEXT("add $d chunks $d lines"), str, rgss)) {
  406. _cAdded += StrToInt(rgss[1].Finalize());
  407. } else if (Parse(TEXT("deleted $d chunks $d lines"), str, rgss)) {
  408. _cDeleted += StrToInt(rgss[1].Finalize());
  409. } else if (Parse(TEXT("changed $d chunks $d / $d lines"), str, rgss)) {
  410. _cDeleted += StrToInt(rgss[1].Finalize());
  411. _cAdded += StrToInt(rgss[2].Finalize());
  412. FlushMatch();
  413. }
  414. break;
  415. }
  416. }
  417. int Finish()
  418. {
  419. if (_fAnyMatch) {
  420. AddLine(TEXT(""), CAT_BLANK1);
  421. }
  422. AddLine(TEXT(""), CAT_BLANK2);
  423. ListView_SortItems(_hwndChild, s_Compare, 0);
  424. return _iBug;
  425. }
  426. static int CALLBACK s_Compare(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
  427. {
  428. return (int)(lParam1 - lParam2);
  429. }
  430. private:
  431. Match _m;
  432. HWND _hwndChild;
  433. int _iPhase;
  434. int _iLine;
  435. int _iMatch;
  436. int _iBug;
  437. int _cAdded;
  438. int _cDeleted;
  439. BOOL _fAnyMatch;
  440. };
  441. DWORD CALLBACK CDescribe::s_BGInvoke(LPVOID lpParam)
  442. {
  443. CDescribe *self = RECAST(CDescribe *, lpParam);
  444. return self->_BGInvoke();
  445. }
  446. DWORD CDescribe::_BGInvoke()
  447. {
  448. DescribeParseState state(_hwndChild, _ssChange._pszMax);
  449. String str;
  450. str << TEXT("describe ");
  451. if (GlobalSettings.IsChurnEnabled()) {
  452. str << TEXT("-ds ");
  453. } else {
  454. str << TEXT("-s ");
  455. }
  456. str << _ssChange;
  457. SDChildProcess proc(str);
  458. IOBuffer buf(proc.Handle());
  459. while (buf.NextLine(str)) {
  460. str.Chomp();
  461. state.ParseLine(str);
  462. }
  463. _iBug = state.Finish();
  464. PostMessage(_hwnd, DM_RECALC, 0, 0);
  465. BGEndTask();
  466. return 0;
  467. }
  468. DWORD CALLBACK CDescribe_ThreadProc(LPVOID lpParameter)
  469. {
  470. return FrameWindow::RunThread(new CDescribe, lpParameter);
  471. }