Source code of Windows XP (NT5)
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.

739 lines
20 KiB

  1. /*****************************************************************************
  2. *
  3. * opened.cpp
  4. *
  5. * View the list of opened files and pending changes.
  6. *
  7. *****************************************************************************/
  8. #include "sdview.h"
  9. /*****************************************************************************
  10. *
  11. * OpenedEntry
  12. *
  13. * We list the changes in descending numerical order, except that
  14. * "default" goes at the top of the list (rather than at the bottom,
  15. * which is what StrToInt would've given us).
  16. *
  17. *****************************************************************************/
  18. MakeStringFormat(ChangeList)
  19. MakeStringFormat(PendingOp)
  20. class OpenedEntry : public TreeItem {
  21. public:
  22. OpenedEntry(ChangeList clChange, LPCTSTR pszComment);
  23. OpenedEntry(PendingOp opOp, LPCTSTR pszFile);
  24. OpenedEntry() { }
  25. void SetComment(LPCTSTR pszComment) { _scComment = pszComment; }
  26. void SetFullDescription(LPCTSTR pszFullDescription) { _scFullDescription = pszFullDescription; }
  27. LRESULT GetDispInfo(NMTREELIST *pdi, int iColumn);
  28. LRESULT GetInfoTip(NMTREELIST *pdi);
  29. LPCTSTR GetChange() const { return _scChange; }
  30. LPCTSTR GetComment() const { return _scComment; }
  31. int GetOp() const { return _iOp; }
  32. UINT GetSortKey() const { return _uiSort; }
  33. BOOL IsAddLike() const { return _iOp == OP_ADD || _iOp == OP_BRANCH; }
  34. BOOL IsDelLike() const { return _iOp == OP_DELETE; }
  35. BOOL HasComment() const { return !_scComment.IsEmpty(); }
  36. static UINT ComputeSortKey(LPCTSTR pszChange)
  37. { return (UINT)StrToInt(pszChange) - 1; }
  38. static UINT SortKey_DefaultChange() { return (UINT)0-1; }
  39. private:
  40. void GetImage(NMTREELIST *ptl);
  41. private:
  42. UINT _uiSort; // Sort key
  43. int _iOp; // Checkin operation
  44. StringCache _scChange; // Change number or operation
  45. StringCache _scComment; // Checkin comment or path
  46. StringCache _scFullDescription; // Full checkin description
  47. };
  48. OpenedEntry::OpenedEntry(ChangeList clChange, LPCTSTR pszComment)
  49. : _scChange(clChange)
  50. , _uiSort(ComputeSortKey(clChange))
  51. , _iOp(OP_EDIT)
  52. , _scComment(pszComment)
  53. {
  54. }
  55. OpenedEntry::OpenedEntry(PendingOp opOp, LPCTSTR pszComment)
  56. : _scChange(opOp)
  57. , _iOp(ParseOp(opOp))
  58. , _scComment(pszComment)
  59. {
  60. }
  61. void OpenedEntry::GetImage(NMTREELIST *ptl)
  62. {
  63. if (_iOp > 0) {
  64. ptl->iSubItem = c_rgleim[_iOp]._iImage;
  65. } else {
  66. ptl->iSubItem = 0;
  67. }
  68. }
  69. LRESULT OpenedEntry::GetDispInfo(NMTREELIST *ptl, int iColumn)
  70. {
  71. switch (iColumn) {
  72. case -1: GetImage(ptl); break;
  73. case 0: ptl->pszText = _scChange; break;
  74. case 1: ptl->pszText = _scComment; break;
  75. }
  76. return 0;
  77. }
  78. LRESULT OpenedEntry::GetInfoTip(NMTREELIST *ptl)
  79. {
  80. ptl->pszText = _scFullDescription;
  81. return 0;
  82. }
  83. /*****************************************************************************
  84. *
  85. * class COpened
  86. *
  87. *****************************************************************************/
  88. class COpened : public TLFrame, public BGTask {
  89. friend DWORD CALLBACK COpened_ThreadProc(LPVOID lpParameter);
  90. protected:
  91. LRESULT HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  92. private:
  93. enum {
  94. OM_INITIALIZED = WM_APP
  95. };
  96. typedef TLFrame super;
  97. LRESULT ON_WM_CREATE(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  98. LRESULT ON_WM_SETCURSOR(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  99. LRESULT ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  100. LRESULT ON_WM_INITMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  101. LRESULT ON_WM_NOTIFY(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  102. LRESULT ON_OM_INITIALIZED(UINT uiMsg, WPARAM wParam, LPARAM lParam);
  103. private: /* Helpers */
  104. COpened() : TLFrame(new OpenedEntry)
  105. {
  106. SetAcceleratorTable(MAKEINTRESOURCE(IDA_OPENED));
  107. }
  108. OpenedEntry *OEGetCurSel() { return SAFECAST(OpenedEntry*, TLGetCurSel()); }
  109. LRESULT _FillChildren(OpenedEntry *pleRoot, LPCTSTR pszRootPath);
  110. LRESULT _OnItemActivate(OpenedEntry *ple);
  111. LRESULT _OnGetContextMenu(OpenedEntry *ple);
  112. BOOL _IsViewFileLogEnabled(OpenedEntry *ple);
  113. LRESULT _ViewFileLog(OpenedEntry *ple);
  114. void _AdjustMenu(HMENU hmenu, OpenedEntry *ple, BOOL fContextMenu);
  115. int _GetChangeNumber(OpenedEntry *ple);
  116. int _GetBugNumber(OpenedEntry *ple);
  117. static DWORD CALLBACK s_BGInvoke(LPVOID lpParam);
  118. DWORD _BGInvoke();
  119. LPCTSTR _BGParse(StringCache *pscUser);
  120. void _BGGetChanges(LPCTSTR pszUser);
  121. void _BGFillInChanges();
  122. OpenedEntry *_BGFindChange(LPCTSTR pszChange, BOOL fCreate);
  123. void _BGGetOpened(LPCTSTR pszArgs, LPCTSTR pszUser);
  124. BOOL _IsChangeHeader(OpenedEntry *ple)
  125. { return ple && ple->Parent() == _tree.GetRoot(); }
  126. BOOL _IsChangeFile(OpenedEntry *ple)
  127. { return ple && ple->Parent() != _tree.GetRoot(); }
  128. private:
  129. };
  130. LRESULT COpened::ON_WM_CREATE(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  131. {
  132. LRESULT lres;
  133. static const LVFCOLUMN c_rgcol[] = {
  134. { 15 ,IDS_COL_CHANGE ,LVCFMT_LEFT },
  135. { 60 ,IDS_COL_COMMENT ,LVCFMT_LEFT },
  136. { 0 ,0 ,0 },
  137. };
  138. lres = super::HandleMessage(uiMsg, wParam, lParam);
  139. if (lres == 0 &&
  140. _tree.GetRoot() &&
  141. SetWindowMenu(MAKEINTRESOURCE(IDM_OPENED)) &&
  142. CreateChild(LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS |
  143. LVS_NOSORTHEADER,
  144. LVS_EX_LABELTIP | LVS_EX_HEADERDRAGDROP |
  145. LVS_EX_INFOTIP | LVS_EX_FULLROWSELECT) &&
  146. AddColumns(c_rgcol) &&
  147. BGStartTask(s_BGInvoke, this)) {
  148. SetWindowText(_hwnd, TEXT("sdv opened"));
  149. } else {
  150. lres = -1;
  151. }
  152. return lres;
  153. }
  154. int COpened::_GetBugNumber(OpenedEntry *ple)
  155. {
  156. if (_IsChangeFile(ple)) {
  157. ple = SAFECAST(OpenedEntry *, ple->Parent());
  158. }
  159. if (ple) {
  160. return ParseBugNumber(ple->GetComment());
  161. } else {
  162. return 0;
  163. }
  164. }
  165. int COpened::_GetChangeNumber(OpenedEntry *ple)
  166. {
  167. if (_IsChangeFile(ple)) {
  168. ple = SAFECAST(OpenedEntry *, ple->Parent());
  169. }
  170. if (ple) {
  171. return StrToInt(ple->GetChange());
  172. } else {
  173. return 0;
  174. }
  175. }
  176. BOOL COpened::_IsViewFileLogEnabled(OpenedEntry *ple)
  177. {
  178. if (!_IsChangeFile(ple)) {
  179. return FALSE; // not even a file!
  180. }
  181. //
  182. // Some of the ops create files so there's nothing to see.
  183. //
  184. if (ple->IsAddLike()) {
  185. return FALSE;
  186. }
  187. return TRUE;
  188. }
  189. LRESULT COpened::_ViewFileLog(OpenedEntry *poe)
  190. {
  191. if (!_IsViewFileLogEnabled(poe)) {
  192. return 0;
  193. }
  194. Substring ss;
  195. if (Parse(TEXT("$P"), poe->GetComment(), &ss)) {
  196. String str;
  197. str << TEXT("-#") << ss._pszMax << TEXT(" ") << ss;
  198. LaunchThreadTask(CFileLog_ThreadProc, str);
  199. }
  200. return 0;
  201. }
  202. LRESULT COpened::ON_WM_SETCURSOR(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  203. {
  204. return BGFilterSetCursor(super::HandleMessage(uiMsg, wParam, lParam));
  205. }
  206. LRESULT COpened::ON_WM_COMMAND(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  207. {
  208. int iChange, iBug;
  209. switch (GET_WM_COMMAND_ID(wParam, lParam)) {
  210. case IDM_VIEWFILEDIFF:
  211. return _OnItemActivate(OEGetCurSel());
  212. case IDM_VIEWBUG:
  213. iBug = _GetBugNumber(OEGetCurSel());
  214. if (iBug) {
  215. OpenBugWindow(_hwnd, iBug);
  216. }
  217. break;
  218. case IDM_VIEWFILELOG:
  219. _ViewFileLog(OEGetCurSel());
  220. break;
  221. }
  222. return super::HandleMessage(uiMsg, wParam, lParam);
  223. }
  224. void COpened::_AdjustMenu(HMENU hmenu, OpenedEntry *ple, BOOL fContextMenu)
  225. {
  226. AdjustBugMenu(hmenu, _GetBugNumber(ple), fContextMenu);
  227. BOOL fEnable = _IsViewFileLogEnabled(ple);
  228. EnableDisableOrRemoveMenuItem(hmenu, IDM_VIEWFILELOG, fEnable, fContextMenu);
  229. fEnable = _IsChangeFile(ple);
  230. EnableDisableOrRemoveMenuItem(hmenu, IDM_VIEWFILEDIFF, fEnable, fContextMenu);
  231. MakeMenuPretty(hmenu);
  232. }
  233. LRESULT COpened::ON_WM_INITMENU(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  234. {
  235. _AdjustMenu(RECAST(HMENU, wParam), OEGetCurSel(), FALSE);
  236. return 0;
  237. }
  238. LRESULT COpened::_OnGetContextMenu(OpenedEntry *ple)
  239. {
  240. HMENU hmenu = LoadPopupMenu(MAKEINTRESOURCE(IDM_OPENED_POPUP));
  241. if (hmenu) {
  242. _AdjustMenu(hmenu, ple, TRUE);
  243. }
  244. return RECAST(LRESULT, hmenu);
  245. }
  246. LRESULT COpened::_OnItemActivate(OpenedEntry *ple)
  247. {
  248. if (_IsChangeFile(ple)) {
  249. //
  250. // Map the full depot path to a local file so we can pass it
  251. // to windiff. We can't use "sd diff" because that will fail
  252. // on a borrowed enlistment.
  253. //
  254. String strLocal;
  255. if (MapToLocalPath(ple->GetComment(), strLocal)) {
  256. Substring ss;
  257. if (Parse(TEXT("$p"), strLocal, &ss)) {
  258. String str;
  259. str << TEXT("windiff ");
  260. if (ple->IsAddLike()) {
  261. str << TEXT("nul ");
  262. } else {
  263. str << QuoteSpaces(ple->GetComment());
  264. }
  265. str << TEXT(" ");
  266. if (ple->IsDelLike()) {
  267. str << TEXT("nul ");
  268. } else {
  269. str << QuoteSpaces(ss.Finalize());
  270. }
  271. SpawnProcess(str);
  272. }
  273. }
  274. }
  275. return 0;
  276. }
  277. LRESULT COpened::ON_WM_NOTIFY(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  278. {
  279. NMTREELIST *ptl = RECAST(NMTREELIST*, lParam);
  280. OpenedEntry *ple;
  281. switch (ptl->hdr.code) {
  282. case TLN_GETDISPINFO:
  283. ple = SAFECAST(OpenedEntry*, ptl->pti);
  284. if (ptl->iSubItem < 0) {
  285. return ple->GetDispInfo(ptl, ptl->iSubItem);
  286. } else if (ptl->iSubItem < 2) {
  287. return ple->GetDispInfo(ptl, ptl->iSubItem);
  288. } else {
  289. ASSERT(0); // invalid column
  290. return 0;
  291. }
  292. case TLN_ITEMACTIVATE:
  293. ple = SAFECAST(OpenedEntry*, ptl->pti);
  294. return _OnItemActivate(ple);
  295. case TLN_GETINFOTIP:
  296. ple = SAFECAST(OpenedEntry*, ptl->pti);
  297. return ple->GetInfoTip(ptl);
  298. case TLN_DELETEITEM:
  299. ple = SAFECAST(OpenedEntry*, ptl->pti);
  300. delete ple;
  301. return 0;
  302. case TLN_GETCONTEXTMENU:
  303. ple = SAFECAST(OpenedEntry*, ptl->pti);
  304. return _OnGetContextMenu(ple);
  305. }
  306. return super::HandleMessage(uiMsg, wParam, lParam);
  307. }
  308. LRESULT COpened::ON_OM_INITIALIZED(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  309. {
  310. _tree.Expand(_tree.GetRoot());
  311. //
  312. // Also expand the first changelist since it's usually what
  313. // you are interested in.
  314. //
  315. TreeItem *pti = _tree.GetRoot()->FirstChild();
  316. if (pti) {
  317. _tree.Expand(pti);
  318. }
  319. return 0;
  320. }
  321. LRESULT
  322. COpened::HandleMessage(UINT uiMsg, WPARAM wParam, LPARAM lParam)
  323. {
  324. switch (uiMsg) {
  325. FW_MSG(WM_CREATE);
  326. FW_MSG(WM_SETCURSOR);
  327. FW_MSG(WM_COMMAND);
  328. FW_MSG(WM_INITMENU);
  329. FW_MSG(WM_NOTIFY);
  330. FW_MSG(OM_INITIALIZED);
  331. }
  332. return super::HandleMessage(uiMsg, wParam, lParam);
  333. }
  334. //
  335. // A private helper class that captures the parsing state machine.
  336. //
  337. class PendingChangesParseState
  338. {
  339. public:
  340. PendingChangesParseState() : _poeCurrent(NULL), _poeInsertAfter(NULL) { }
  341. OpenedEntry *GetCurrent() const { return _poeCurrent; }
  342. void Flush(Tree& tree)
  343. {
  344. if (_poeCurrent) {
  345. //
  346. // Trim the trailing CRLF off the last line of the full
  347. // description.
  348. //
  349. _strFullDescription.Chomp();
  350. _poeCurrent->SetFullDescription(_strFullDescription);
  351. tree.RedrawItem(_poeCurrent);
  352. _poeCurrent = NULL;
  353. }
  354. _fHaveComment = FALSE;
  355. _strFullDescription.Reset();
  356. }
  357. void AddEntry(Tree &tree, String& str, Substring *rgss)
  358. {
  359. OpenedEntry *poe = new OpenedEntry(ChangeList(rgss[0].Finalize()), // Change
  360. NULL); // Comment
  361. if (poe) {
  362. if (tree.Insert(poe, tree.GetRoot(), _poeInsertAfter)) {
  363. _poeInsertAfter = _poeCurrent = poe;
  364. } else {
  365. delete poe;
  366. }
  367. }
  368. }
  369. void SetEntry(OpenedEntry *poe)
  370. {
  371. _poeCurrent = poe;
  372. }
  373. void AddLine(const String& str)
  374. {
  375. _strFullDescription << str;
  376. }
  377. //
  378. // We cannot use the CommentParser because we don't have a Dev
  379. // column; besides, we don't want to handle proxy checkins here.
  380. // Show the real unfiltered checkin comment.
  381. //
  382. void AddComment(LPTSTR psz)
  383. {
  384. if (_fHaveComment) return;
  385. if (!_poeCurrent) return;
  386. //
  387. // Ignore leading spaces.
  388. //
  389. while (*psz == TEXT('\t') || *psz == TEXT(' ')) psz++;
  390. //
  391. // Skip blank description lines.
  392. //
  393. if (*psz == TEXT('\0')) return;
  394. //
  395. // Use the first nonblank comment line as the text and toss the rest.
  396. //
  397. // Change all tabs to spaces because listview doesn't like tabs.
  398. //
  399. ChangeTabsToSpaces(psz);
  400. _poeCurrent->SetComment(psz);
  401. _fHaveComment = TRUE;
  402. }
  403. private:
  404. BOOL _fHaveComment;
  405. OpenedEntry*_poeCurrent;
  406. OpenedEntry*_poeInsertAfter;
  407. String _strFullDescription;
  408. };
  409. DWORD CALLBACK COpened::s_BGInvoke(LPVOID lpParam)
  410. {
  411. COpened *self = RECAST(COpened *, lpParam);
  412. return self->_BGInvoke();
  413. }
  414. //
  415. // Returns unparsed string (or NULL)
  416. // and puts user name in pscUser.
  417. //
  418. //
  419. LPCTSTR COpened::_BGParse(StringCache *pscUser)
  420. {
  421. /*
  422. * Parse the switches as best we can.
  423. *
  424. */
  425. GetOpt opt(TEXT("u"), _pszQuery);
  426. for (;;) {
  427. switch (opt.NextSwitch()) {
  428. case TEXT('u'):
  429. *pscUser = opt.GetValue();
  430. break;
  431. case TEXT('\0'):
  432. goto L_switch; // two-level break
  433. default:
  434. // Caller will display help for us
  435. return NULL;
  436. }
  437. }
  438. L_switch:;
  439. if (pscUser->IsEmpty()) {
  440. *pscUser = GlobalSettings.GetUserName();
  441. }
  442. String str;
  443. str << TEXT("sdv opened -u ") << *pscUser;
  444. SetWindowText(_hwnd, str);
  445. /*
  446. * The rest goes to "sd opened".
  447. */
  448. return opt.GetTokenizer().Unparsed();
  449. }
  450. void COpened::_BGGetChanges(LPCTSTR pszUser)
  451. {
  452. LPCTSTR pszClient = GlobalSettings.GetClientName();
  453. UINT cchClient = lstrlen(pszClient);
  454. String str;
  455. str << TEXT("changes -l -s pending");
  456. if (GlobalSettings.IsVersion(1, 60)) {
  457. str << TEXT(" -u ") << QuoteSpaces(pszUser);
  458. }
  459. SDChildProcess proc(str);
  460. IOBuffer buf(proc.Handle());
  461. PendingChangesParseState state;
  462. while (buf.NextLine(str)) {
  463. Substring rgss[4]; // changeno, date, domain\userid, client
  464. if (Parse(TEXT("Change $d on $D by $u@$w"), str, rgss)) {
  465. state.Flush(_tree);
  466. if (rgss[3].Length() == cchClient &&
  467. StrCmpNI(rgss[3].Start(), pszClient, cchClient) == 0) {
  468. state.AddLine(str);
  469. state.AddEntry(_tree, str, rgss);
  470. }
  471. } else if (state.GetCurrent()) {
  472. state.AddLine(str);
  473. if (str[0] == TEXT('\t')) {
  474. str.Chomp();
  475. state.AddComment(str);
  476. }
  477. }
  478. }
  479. state.Flush(_tree);
  480. }
  481. OpenedEntry *COpened::_BGFindChange(LPCTSTR pszChange, BOOL fCreate)
  482. {
  483. UINT uiKey = OpenedEntry::ComputeSortKey(pszChange);
  484. OpenedEntry *poeInsertAfter = NULL;
  485. OpenedEntry *poe = SAFECAST(OpenedEntry *, _tree.GetRoot()->FirstChild());
  486. if (poe == PTI_ONDEMAND) {
  487. poe = NULL;
  488. }
  489. while (poe) {
  490. if (poe->GetSortKey() == uiKey) {
  491. return poe;
  492. }
  493. if (poe->GetSortKey() < uiKey) {
  494. break;
  495. }
  496. poeInsertAfter = poe;
  497. poe = SAFECAST(OpenedEntry *, poe->NextSibling());
  498. }
  499. //
  500. // Create it if necessary. (We always create "default".)
  501. //
  502. if (fCreate || StrCmp(pszChange, TEXT("default")) == 0) {
  503. poe = new OpenedEntry(ChangeList(pszChange), NULL);
  504. if (poe) {
  505. if (_tree.Insert(poe, _tree.GetRoot(), poeInsertAfter)) {
  506. return poe;
  507. }
  508. delete poe;
  509. }
  510. }
  511. return NULL;
  512. }
  513. void COpened::_BGGetOpened(LPCTSTR pszArgs, LPCTSTR pszUser)
  514. {
  515. String str, strOrig;
  516. str << TEXT("opened ") << pszArgs;
  517. SDChildProcess proc(str);
  518. IOBuffer buf(proc.Handle());
  519. while (buf.NextLine(str)) {
  520. Substring rgss[6]; // path, version, op, changeno, type, user
  521. LPTSTR pszRest = Parse(TEXT("$P#$d - $w "), str, rgss);
  522. if (pszRest) {
  523. strOrig = str;
  524. rgss[1].Finalize(); // End of revision (path#version)
  525. //
  526. // Parsing is such sweet sorrow.
  527. //
  528. // "default change" but "change 1234".
  529. //
  530. LPTSTR pszRest2;
  531. if ((pszRest2 = Parse(TEXT("change $d $w"), pszRest, &rgss[3])) ||
  532. (pszRest2 = Parse(TEXT("$w change $w"), pszRest, &rgss[3]))) {
  533. *pszRest2++ = TEXT('\0'); // relies on the fact that we didn't chomp
  534. if (Parse(TEXT("by $p"), pszRest2, &rgss[5])) {
  535. // Some nondefault user, how about that
  536. rgss[5].Finalize();
  537. } else {
  538. // Default user
  539. rgss[5].SetStart(GlobalSettings.GetUserName());
  540. }
  541. if (lstrcmpi(rgss[5].Start(), pszUser) == 0) {
  542. OpenedEntry *poeParent = _BGFindChange(rgss[3].Finalize(), pszArgs[0]);
  543. if (poeParent) {
  544. OpenedEntry *poe = new OpenedEntry(PendingOp(rgss[2].Finalize()),
  545. rgss[0].Start());
  546. if (poe) {
  547. if (_tree.Insert(poe, poeParent, PTI_APPEND)) {
  548. strOrig.Chomp();
  549. poe->SetFullDescription(strOrig);
  550. } else {
  551. delete poe;
  552. }
  553. }
  554. }
  555. }
  556. }
  557. }
  558. }
  559. }
  560. void COpened::_BGFillInChanges()
  561. {
  562. String str;
  563. str << TEXT("describe -s ");
  564. BOOL fAnyChanges = FALSE;
  565. OpenedEntry *poe = SAFECAST(OpenedEntry *, _tree.GetRoot()->FirstChild());
  566. if (poe == PTI_ONDEMAND) {
  567. poe = NULL;
  568. }
  569. while (poe) {
  570. if (poe->GetSortKey() != OpenedEntry::SortKey_DefaultChange() &&
  571. !poe->HasComment()) {
  572. str << poe->GetChange() << TEXT(" ");
  573. fAnyChanges = TRUE;
  574. }
  575. poe = SAFECAST(OpenedEntry *, poe->NextSibling());
  576. }
  577. if (fAnyChanges) {
  578. SDChildProcess proc(str);
  579. IOBuffer buf(proc.Handle());
  580. PendingChangesParseState state;
  581. while (buf.NextLine(str)) {
  582. Substring rgss[4]; // changeno, domain\userid, client, date
  583. if (Parse(TEXT("Change $d by $u@$w on $D"), str, rgss)) {
  584. state.Flush(_tree);
  585. state.AddLine(str);
  586. OpenedEntry *poe = _BGFindChange(rgss[0].Finalize(), FALSE);
  587. state.SetEntry(poe);
  588. } else if (state.GetCurrent()) {
  589. if (str[0] == TEXT('A')) { // "Affected files"
  590. state.Flush(_tree);
  591. } else {
  592. state.AddLine(str);
  593. if (str[0] == TEXT('\t')) {
  594. str.Chomp();
  595. state.AddComment(str);
  596. }
  597. }
  598. }
  599. }
  600. state.Flush(_tree);
  601. }
  602. }
  603. DWORD COpened::_BGInvoke()
  604. {
  605. StringCache scUser;
  606. LPCTSTR pszUnparsed = _BGParse(&scUser);
  607. if (pszUnparsed) {
  608. // If no parameters, then go hunt down all the changelists
  609. // so we can find the empty ones, too. Otherwise, we will
  610. // figure them out as we see the results of "sd opened".
  611. if (!*pszUnparsed) {
  612. _BGGetChanges(scUser);
  613. }
  614. _BGGetOpened(pszUnparsed, scUser);
  615. PostMessage(_hwnd, OM_INITIALIZED, 0, 0);
  616. _BGFillInChanges();
  617. } else {
  618. Help(_hwnd, TEXT("#opene"));
  619. PostMessage(_hwnd, WM_CLOSE, 0, 0);
  620. }
  621. BGEndTask();
  622. return 0;
  623. }
  624. DWORD CALLBACK COpened_ThreadProc(LPVOID lpParameter)
  625. {
  626. return FrameWindow::RunThread(new COpened, lpParameter);
  627. }