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.

476 lines
13 KiB

  1. /*****************************************************************************
  2. *
  3. * changes.cpp
  4. *
  5. * View the result of a change query.
  6. *
  7. *****************************************************************************/
  8. #include "sdview.h"
  9. /*****************************************************************************
  10. *
  11. * class CChanges
  12. *
  13. *****************************************************************************/
  14. class CChanges : public LVFrame, public BGTask {
  15. friend DWORD CALLBACK CChanges_ThreadProc(LPVOID lpParameter);
  16. protected:
  17. LRESULT HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  18. private:
  19. typedef LVFrame super;
  20. LRESULT ON_WM_CREATE(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  21. LRESULT ON_WM_SETCURSOR(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  22. LRESULT ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  23. LRESULT ON_WM_INITMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  24. LRESULT ON_LM_ITEMACTIVATE(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  25. LRESULT ON_LM_GETINFOTIP(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  26. LRESULT ON_LM_GETCONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  27. LRESULT ON_LM_COPYTOCLIPBOARD(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  28. LRESULT ON_LM_DELETEITEM(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  29. int GetChangelist(int iItem);
  30. private: /* Helpers */
  31. CChanges()
  32. {
  33. SetAcceleratorTable(MAKEINTRESOURCE(IDA_CHANGES));
  34. }
  35. void _AdjustMenu(HMENU hmenu, int iItem, BOOL fContextMenu);
  36. static DWORD CALLBACK s_BGInvoke(LPVOID lpParam);
  37. DWORD _BGInvoke();
  38. BOOL _BGGetSdCommandLine(String &str);
  39. void _BuildHint();
  40. void _ViewBug();
  41. int _GetBugNumber(int iItem);
  42. private:
  43. StringCache _scQuery;
  44. StringCache _scHint;
  45. StringCache _scUser;
  46. };
  47. int _CChanges_AddError(HWND hwndChild, LPCTSTR psz)
  48. {
  49. LVITEM lvi;
  50. lvi.mask = LVIF_TEXT;
  51. lvi.iItem = MAXLONG;
  52. lvi.iSubItem = 0;
  53. lvi.pszText = CCAST(LPTSTR, psz);
  54. lvi.iItem = ListView_InsertItem(hwndChild, &lvi);
  55. if (lvi.iItem == 0) {
  56. ListView_SetCurSel(hwndChild, 0); /* Select the first item */
  57. }
  58. return lvi.iItem;
  59. }
  60. int CChanges::GetChangelist(int iItem)
  61. {
  62. if (iItem == -1) {
  63. iItem = GetCurSel();
  64. }
  65. if (iItem >= 0) {
  66. LVITEM lvi;
  67. TCHAR sz[64];
  68. if (ListView_GetItemText(_hwndChild, iItem, sz, ARRAYSIZE(sz))) {
  69. Substring ss;
  70. if (Parse(TEXT("$d$e"), sz, &ss)) {
  71. return StrToInt(sz);
  72. }
  73. }
  74. }
  75. return -1;
  76. }
  77. LRESULT CChanges::ON_WM_CREATE(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  78. {
  79. static const LVFCOLUMN s_rgcol[] = {
  80. { 7 ,IDS_COL_CHANGE ,LVCFMT_RIGHT },
  81. { 15 ,IDS_COL_DATE ,LVCFMT_LEFT },
  82. { 10 ,IDS_COL_DEV ,LVCFMT_LEFT },
  83. { 30 ,IDS_COL_COMMENT ,LVCFMT_LEFT },
  84. { 0 ,0 ,0 },
  85. };
  86. LRESULT lres = super::HandleMessage(uiMsg, wParam, lParam);
  87. if (lres == 0 &&
  88. SetWindowMenu(MAKEINTRESOURCE(IDM_CHANGES)) &&
  89. CreateChild(LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS |
  90. LVS_NOSORTHEADER,
  91. LVS_EX_LABELTIP | LVS_EX_HEADERDRAGDROP |
  92. LVS_EX_INFOTIP | LVS_EX_FULLROWSELECT) &&
  93. AddColumns(s_rgcol) &&
  94. BGStartTask(s_BGInvoke, this)) {
  95. String str;
  96. str << TEXT("sdv changes ") << _pszQuery;
  97. SetWindowText(_hwnd, str);
  98. lres = 0;
  99. } else {
  100. lres = -1;
  101. }
  102. return lres;
  103. }
  104. LRESULT CChanges::ON_WM_SETCURSOR(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  105. {
  106. return BGFilterSetCursor(super::HandleMessage(uiMsg, wParam, lParam));
  107. }
  108. int CChanges::_GetBugNumber(int iItem)
  109. {
  110. // 3 = checkin comment
  111. return ParseBugNumberFromSubItem(_hwndChild, iItem, 3);
  112. }
  113. LRESULT CChanges::ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  114. {
  115. int iChange, iBug;
  116. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  117. case IDM_VIEWDESC:
  118. ON_LM_ITEMACTIVATE(LM_ITEMACTIVATE, GetCurSel(), 0);
  119. return 0;
  120. case IDM_VIEWWINDIFF:
  121. WindiffChangelist(GetChangelist(GetCurSel()));
  122. return 0;
  123. case IDM_VIEWBUG:
  124. iBug = _GetBugNumber(GetCurSel());
  125. if (iBug) {
  126. OpenBugWindow(_hwnd, iBug);
  127. }
  128. return 0;
  129. }
  130. return super::HandleMessage(uiMsg, wParam, lParam);
  131. }
  132. void CChanges::_AdjustMenu(HMENU hmenu, int iItem, BOOL fContextMenu)
  133. {
  134. AdjustBugMenu(hmenu, _GetBugNumber(iItem), fContextMenu);
  135. }
  136. LRESULT CChanges::ON_WM_INITMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  137. {
  138. _AdjustMenu(RECAST(HMENU, wParam), GetCurSel(), FALSE);
  139. return 0;
  140. }
  141. LRESULT CChanges::ON_LM_GETINFOTIP(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  142. {
  143. LPTSTR pszInfoTip = RECAST(LPTSTR, GetLVItem((int)wParam));
  144. if (pszInfoTip) {
  145. NMLVGETINFOTIP *pgit = RECAST(NMLVGETINFOTIP *, lParam);
  146. pgit->pszText = pszInfoTip;
  147. }
  148. return 0;
  149. }
  150. LRESULT CChanges::ON_LM_ITEMACTIVATE(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  151. {
  152. int iChange = GetChangelist((int)wParam);
  153. if (iChange > 0) {
  154. if (_scHint.IsEmpty()) {
  155. _BuildHint();
  156. }
  157. String str;
  158. str << iChange << _scHint;
  159. LaunchThreadTask(CDescribe_ThreadProc, str);
  160. }
  161. return 0;
  162. }
  163. LRESULT CChanges::ON_LM_GETCONTEXTMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  164. {
  165. HMENU hmenu = LoadPopupMenu(MAKEINTRESOURCE(IDM_CHANGES_POPUP));
  166. if (hmenu) {
  167. _AdjustMenu(hmenu, (int)wParam, TRUE);
  168. }
  169. return RECAST(LRESULT, hmenu);
  170. }
  171. LRESULT CChanges::ON_LM_COPYTOCLIPBOARD(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  172. {
  173. String str;
  174. for (int iItem = (int)wParam; iItem < (int)lParam; iItem++) {
  175. LPTSTR pszInfoTip = RECAST(LPTSTR, GetLVItem(iItem));
  176. if (pszInfoTip) {
  177. str << pszInfoTip << TEXT("\r\n");
  178. }
  179. }
  180. SetClipboardText(_hwnd, str);
  181. return 0;
  182. }
  183. LRESULT CChanges::ON_LM_DELETEITEM(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  184. {
  185. if (lParam) {
  186. LocalFree(RECAST(HLOCAL, lParam));
  187. }
  188. return 0;
  189. }
  190. LRESULT
  191. CChanges::HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  192. {
  193. switch (uiMsg) {
  194. FW_MSG(WM_CREATE);
  195. FW_MSG(WM_SETCURSOR);
  196. FW_MSG(WM_COMMAND);
  197. FW_MSG(WM_INITMENU);
  198. FW_MSG(LM_ITEMACTIVATE);
  199. FW_MSG(LM_GETINFOTIP);
  200. FW_MSG(LM_GETCONTEXTMENU);
  201. FW_MSG(LM_COPYTOCLIPBOARD);
  202. FW_MSG(LM_DELETEITEM);
  203. }
  204. return super::HandleMessage(uiMsg, wParam, lParam);
  205. }
  206. //
  207. // We build the hint only on demand since it takes a while and we
  208. // don't want to slow down the initial query.
  209. //
  210. void CChanges::_BuildHint()
  211. {
  212. String str;
  213. if (!_scQuery.IsEmpty()) {
  214. String strTok, strPath;
  215. Tokenizer tok(_scQuery);
  216. while (tok.Token(strTok)) {
  217. Substring ss;
  218. if (Parse(TEXT("$p"), strTok, &ss) && ss.Length() > 0) {
  219. ss.Finalize(); // Strip off the revision specifier
  220. if (MapToFullDepotPath(strTok, strPath)) {
  221. str << TEXT(' ') << QuoteSpaces(strPath);
  222. }
  223. }
  224. }
  225. }
  226. _scHint = str;
  227. }
  228. //
  229. // A private helper class that captures the parsing state machine.
  230. //
  231. class ChangesParseState : public CommentParser
  232. {
  233. public:
  234. ChangesParseState(HWND hwndChild) : _iItem(-1), _hwndChild(hwndChild) {}
  235. void Flush()
  236. {
  237. if (_iItem >= 0) {
  238. _strFullDescription.Chomp();
  239. LVITEM lvi;
  240. lvi.iItem = _iItem;
  241. lvi.iSubItem = 0;
  242. lvi.mask = LVIF_PARAM;
  243. lvi.lParam = RECAST(LPARAM, StrDup(_strFullDescription));
  244. ListView_SetItem(_hwndChild, &lvi);
  245. }
  246. _iItem = -1;
  247. CommentParser::Reset();
  248. _strFullDescription.Reset();
  249. }
  250. void AddLine(LPCTSTR psz)
  251. {
  252. _strFullDescription << psz;
  253. }
  254. void AddError(LPCTSTR psz)
  255. {
  256. _iItem = _CChanges_AddError(_hwndChild, psz);
  257. }
  258. void AddEntry(Substring *rgss)
  259. {
  260. LVITEM lvi;
  261. lvi.mask = LVIF_TEXT;
  262. lvi.iItem = MAXLONG;
  263. lvi.iSubItem = 0;
  264. lvi.pszText = rgss[0].Finalize();
  265. _iItem = lvi.iItem = ListView_InsertItem(_hwndChild, &lvi);
  266. if (lvi.iItem >= 0) {
  267. lvi.iSubItem = 1;
  268. lvi.pszText = rgss[1].Finalize();
  269. ListView_SetItem(_hwndChild, &lvi);
  270. lvi.iSubItem = 2;
  271. lvi.pszText = rgss[2].Finalize();
  272. LPTSTR psz = StrChr(lvi.pszText, TEXT('\\'));
  273. if (psz) {
  274. lvi.pszText = psz+1;
  275. }
  276. ListView_SetItem(_hwndChild, &lvi);
  277. if (lvi.iItem == 0) {
  278. ListView_SetCurSel(_hwndChild, 0); /* Select the first item */
  279. }
  280. }
  281. }
  282. void SetListViewSubItemText(int iSubItem, LPCTSTR psz)
  283. {
  284. if (_iItem >= 0) {
  285. LVITEM lvi;
  286. lvi.mask = LVIF_TEXT;
  287. lvi.iItem = _iItem;
  288. lvi.iSubItem = iSubItem;
  289. lvi.pszText = CCAST(LPTSTR, psz);
  290. ListView_SetItem(_hwndChild, &lvi);
  291. }
  292. }
  293. void SetDev(LPCTSTR psz)
  294. {
  295. SetListViewSubItemText(2, psz);
  296. }
  297. void SetComment(LPCTSTR psz)
  298. {
  299. SetListViewSubItemText(3, psz);
  300. }
  301. private:
  302. HWND _hwndChild;
  303. int _iItem;
  304. BOOL _fHaveComment;
  305. String _strFullDescription;
  306. };
  307. DWORD CALLBACK CChanges::s_BGInvoke(LPVOID lpParam)
  308. {
  309. CChanges *self = RECAST(CChanges *, lpParam);
  310. return self->_BGInvoke();
  311. }
  312. BOOL CChanges::_BGGetSdCommandLine(String &str)
  313. {
  314. str.Reset();
  315. str << TEXT("changes -l -s submitted ");
  316. /*
  317. * Parse the switches as best we can.
  318. */
  319. BOOL fMSeen = FALSE;
  320. GetOpt opt(TEXT("mu"), _pszQuery);
  321. for (;;) {
  322. switch (opt.NextSwitch()) {
  323. case TEXT('m'):
  324. fMSeen = TRUE;
  325. str << TEXT("-m ") << opt.GetValue() << TEXT(" ");
  326. break;
  327. case TEXT('i'):
  328. str << TEXT("-i ");
  329. break;
  330. case TEXT('u'):
  331. if (GlobalSettings.IsVersion(1, 60)) {
  332. str << TEXT("-u ") << opt.GetValue() << TEXT(" ");
  333. } else {
  334. _scUser = opt.GetValue();
  335. }
  336. break;
  337. case TEXT('\0'):
  338. goto L_switch; // two-level break
  339. default:
  340. Help(_hwnd, TEXT("#chang"));
  341. return FALSE;
  342. }
  343. }
  344. L_switch:;
  345. if (!fMSeen) {
  346. str << TEXT("-m50 ");
  347. }
  348. /*
  349. * If no filename is given, use *
  350. *
  351. * The query string will be useful later, so cache that away, too.
  352. */
  353. String strQuery;
  354. if (opt.Finished()) {
  355. strQuery << TEXT("*");
  356. } else {
  357. while (opt.Token()) {
  358. if (opt.GetValue()[0]) {
  359. strQuery << ResolveBranchAndQuoteSpaces(opt.GetValue()) << TEXT(" ");
  360. }
  361. }
  362. }
  363. str << strQuery;
  364. _scQuery = strQuery;
  365. return TRUE;
  366. }
  367. DWORD CChanges::_BGInvoke()
  368. {
  369. String str;
  370. if (_BGGetSdCommandLine(str)) {
  371. SDChildProcess proc(str);
  372. IOBuffer buf(proc.Handle());
  373. ChangesParseState state(_hwndChild);
  374. while (buf.NextLine(str)) {
  375. Substring rgss[3]; // changeno, date, userid
  376. if (Parse(TEXT("Change $d on $D by $p"), str, rgss)) {
  377. state.Flush();
  378. state.AddLine(str);
  379. if (!_scUser.IsEmpty() &&
  380. lstrcmpi(rgss[2].Finalize(), _scUser) != 0) {
  381. /* This change is not for us; ignore it */
  382. } else {
  383. state.AddEntry(rgss);
  384. }
  385. } else if (str[0] == TEXT('\r')) {
  386. state.AddLine(str);
  387. } else if (str[0] == TEXT('\t')) {
  388. state.AddLine(str);
  389. str.Chomp();
  390. state.AddComment(str);
  391. } else {
  392. state.Flush();
  393. str.Chomp();
  394. state.AddError(str);
  395. }
  396. }
  397. state.Flush();
  398. } else {
  399. PostMessage(_hwnd, WM_CLOSE, 0, 0);
  400. }
  401. BGEndTask();
  402. return 0;
  403. }
  404. DWORD CALLBACK CChanges_ThreadProc(LPVOID lpParameter)
  405. {
  406. return FrameWindow::RunThread(new CChanges, lpParameter);
  407. }