Team Fortress 2 Source Code as on 22/4/2020
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.

498 lines
15 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include "vgui_controls/TreeView.h"
  8. #include "vgui_controls/ListPanel.h"
  9. #include "vgui_controls/SectionedListPanel.h"
  10. #include "vgui_controls/ComboBox.h"
  11. #include "vgui_controls/PropertySheet.h"
  12. #include "vgui_controls/PropertyPage.h"
  13. #include <ctype.h>
  14. using namespace vgui;
  15. extern IP4* p4;
  16. // list of all tree view icons
  17. enum
  18. {
  19. IMAGE_FOLDER = 1,
  20. IMAGE_OPENFOLDER,
  21. IMAGE_FILE,
  22. };
  23. //-----------------------------------------------------------------------------
  24. // Purpose: Handles file view
  25. //-----------------------------------------------------------------------------
  26. class CFileTreeView : public TreeView
  27. {
  28. DECLARE_CLASS_SIMPLE( CFileTreeView, TreeView );
  29. public:
  30. CFileTreeView(Panel *parent, const char *name) : BaseClass(parent, name)
  31. {
  32. }
  33. // override to incremental request and show p4 directories
  34. virtual void GenerateChildrenOfNode(int itemIndex)
  35. {
  36. KeyValues *pkv = GetItemData(itemIndex);
  37. if (!pkv->GetInt("dir"))
  38. return;
  39. const char *pFilePath = pkv->GetString("path", "");
  40. if (!pFilePath[0])
  41. return;
  42. surface()->SetCursor(dc_waitarrow);
  43. // get the list of files
  44. CUtlVector<P4File_t> &files = p4->GetFileList(pFilePath);
  45. // generate children
  46. // add all the items
  47. for (int i = 0; i < files.Count(); i++)
  48. {
  49. if (files[i].m_bDeleted)
  50. continue;
  51. KeyValues *kv = new KeyValues("node", "text", p4->String( files[i].m_sName ) );
  52. if (files[i].m_bDir)
  53. {
  54. kv->SetInt("expand", 1);
  55. kv->SetInt("dir", 1);
  56. kv->SetInt("image", IMAGE_FOLDER);
  57. }
  58. else
  59. {
  60. kv->SetInt("image", IMAGE_FILE);
  61. }
  62. // get the files path
  63. char szPath[MAX_PATH];
  64. if (files[i].m_bDir)
  65. {
  66. Q_snprintf(szPath, sizeof(szPath), "%s/%s/%%%%1", p4->String( files[i].m_sPath ), p4->String( files[i].m_sName ) );
  67. }
  68. else
  69. {
  70. Q_snprintf(szPath, sizeof(szPath), "%s/%s", p4->String( files[i].m_sPath ), p4->String( files[i].m_sName ));
  71. }
  72. // translate the files path from a depot path into a local path
  73. char szLocalPath[MAX_PATH];
  74. p4->GetLocalFilePath(szLocalPath, szPath, sizeof(szLocalPath));
  75. kv->SetString("path", szLocalPath);
  76. // now change the items text to match the local paths file name...
  77. char *pLocalName = Q_strrchr(szLocalPath, '\\');
  78. if (pLocalName)
  79. {
  80. *pLocalName = 0;
  81. ++pLocalName;
  82. }
  83. kv->SetString("text", pLocalName);
  84. int itemID = AddItem(kv, itemIndex);
  85. // mark out of date files in red
  86. if (files[i].m_iHaveRevision < files[i].m_iHeadRevision)
  87. {
  88. SetItemFgColor(itemID, Color(255, 0, 0, 255));
  89. }
  90. }
  91. }
  92. // setup a context menu whenever a directory is clicked on
  93. virtual void GenerateContextMenu( int itemIndex, int x, int y )
  94. {
  95. KeyValues *pkv = GetItemData(itemIndex);
  96. const char *pFilePath = pkv->GetString("path", "");
  97. if (!pFilePath[0])
  98. return;
  99. Menu *pContext = new Menu(this, "FileContext");
  100. if (pkv->GetInt("dir"))
  101. {
  102. pContext->AddMenuItem("Cloak folder", new KeyValues("CloakFolder", "item", itemIndex), GetParent(), NULL);
  103. }
  104. else
  105. {
  106. pContext->AddMenuItem("Open for edit", new KeyValues("EditFile", "item", itemIndex), GetParent(), NULL);
  107. pContext->AddMenuItem("Open for delete", new KeyValues("DeleteFile", "item", itemIndex), GetParent(), NULL);
  108. }
  109. // show the context menu
  110. pContext->SetPos(x, y);
  111. pContext->SetVisible(true);
  112. }
  113. // setup a smaller font
  114. virtual void ApplySchemeSettings(IScheme *pScheme)
  115. {
  116. BaseClass::ApplySchemeSettings(pScheme);
  117. SetFont( pScheme->GetFont("DefaultSmall") );
  118. }
  119. };
  120. //-----------------------------------------------------------------------------
  121. // Purpose: Revision list
  122. //-----------------------------------------------------------------------------
  123. class CSmallTextListPanel : public ListPanel
  124. {
  125. DECLARE_CLASS_SIMPLE( CSmallTextListPanel, ListPanel );
  126. public:
  127. CSmallTextListPanel(Panel *parent, const char *name) : BaseClass(parent, name)
  128. {
  129. }
  130. virtual void ApplySchemeSettings(IScheme *pScheme)
  131. {
  132. BaseClass::ApplySchemeSettings(pScheme);
  133. SetFont( pScheme->GetFont("DefaultSmall") );
  134. }
  135. };
  136. //-----------------------------------------------------------------------------
  137. // Purpose: Constructor
  138. //-----------------------------------------------------------------------------
  139. CVP4Dialog::CVP4Dialog() : BaseClass(NULL, "vp4dialog"), m_Images(false)
  140. {
  141. SetSize(1024, 768);
  142. SetTitle("VP4", true);
  143. // clientspec selection
  144. m_pClientCombo = new ComboBox(this, "ClientCombo", 16, false);
  145. // file browser tree controls
  146. m_pFileTree = new CFileTreeView(this, "FileTree");
  147. // build our list of images
  148. m_Images.AddImage(scheme()->GetImage("resource/icon_folder", false));
  149. m_Images.AddImage(scheme()->GetImage("resource/icon_folder_selected", false));
  150. m_Images.AddImage(scheme()->GetImage("resource/icon_file", false));
  151. m_pFileTree->SetImageList(&m_Images, false);
  152. // property sheet - revisions, changes, etc.
  153. m_pViewsSheet = new PropertySheet(this, "ViewsSheet");
  154. // changes
  155. m_pChangesPage = new PropertyPage(m_pViewsSheet, "ChangesPage");
  156. m_pViewsSheet->AddPage(m_pChangesPage, "Changes");
  157. m_pChangesList = new SectionedListPanel(m_pChangesPage, "ChangesList");
  158. // layout changes
  159. int x, y, wide, tall;
  160. m_pChangesPage->GetBounds(x, y, wide, tall);
  161. m_pChangesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -12, -12 );
  162. // revisions
  163. m_pRevisionsPage = new PropertyPage(m_pViewsSheet, "RevisionsPage");
  164. m_pViewsSheet->AddPage(m_pRevisionsPage, "History");
  165. m_pRevisionList = new CSmallTextListPanel(m_pRevisionsPage, "RevisionList");
  166. m_pRevisionList->SetEmptyListText("No file or directory currently selected.");
  167. m_pRevisionList->AddColumnHeader(0, "change", "change", 52, ListPanel::COLUMN_HIDDEN);
  168. m_pRevisionList->AddColumnHeader(1, "date", "date", 52, 0);
  169. m_pRevisionList->AddColumnHeader(2, "time", "time", 52, ListPanel::COLUMN_HIDDEN);
  170. m_pRevisionList->AddColumnHeader(3, "user", "user", 64, 0);
  171. m_pRevisionList->AddColumnHeader(4, "description", "description", 32, ListPanel::COLUMN_RESIZEWITHWINDOW);
  172. m_pRevisionList->SetAllowUserModificationOfColumns(true);
  173. // layout revisions
  174. m_pRevisionsPage->GetBounds(x, y, wide, tall);
  175. m_pRevisionList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -12, -12 );
  176. LoadControlSettingsAndUserConfig("//PLATFORM/resource/vp4dialog.res");
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Purpose: Destructor
  180. //-----------------------------------------------------------------------------
  181. CVP4Dialog::~CVP4Dialog()
  182. {
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: stops app on close
  186. //-----------------------------------------------------------------------------
  187. void CVP4Dialog::OnClose()
  188. {
  189. BaseClass::OnClose();
  190. ivgui()->Stop();
  191. }
  192. //-----------------------------------------------------------------------------
  193. // Purpose: called to open
  194. //-----------------------------------------------------------------------------
  195. void CVP4Dialog::Activate()
  196. {
  197. BaseClass::Activate();
  198. char szTitle[256];
  199. Q_snprintf(szTitle, sizeof(szTitle), "VP4 - %s", p4->String( p4->GetActiveClient().m_sUser ));
  200. SetTitle(szTitle, true);
  201. RefreshFileList();
  202. RefreshClientList();
  203. RefreshChangesList();
  204. p4->GetClientList();
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Purpose: Refreshes the active file list
  208. //-----------------------------------------------------------------------------
  209. void CVP4Dialog::RefreshFileList()
  210. {
  211. m_pFileTree->RemoveAll();
  212. m_pFileTree->SetFgColor(Color(216, 222, 211, 255));
  213. // add the base node
  214. KeyValues *pkv = new KeyValues("root");
  215. pkv->SetString("text", p4->String( p4->GetActiveClient().m_sLocalRoot ));
  216. pkv->SetString("path", p4->String( p4->GetActiveClient().m_sLocalRoot ));
  217. pkv->SetInt("dir", 1);
  218. int iRoot = m_pFileTree->AddItem(pkv, m_pFileTree->GetRootItemIndex());
  219. m_pFileTree->ExpandItem(iRoot, true);
  220. }
  221. //-----------------------------------------------------------------------------
  222. // Purpose: utility function used in qsort
  223. //-----------------------------------------------------------------------------
  224. int __cdecl IntSortFunc(const void *elem1, const void *elem2)
  225. {
  226. if ( *((int *)elem1) < *((int *)elem2) )
  227. return -1;
  228. else if ( *((int *)elem1) > *((int *)elem2) )
  229. return 1;
  230. return 0;
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Purpose: rebuilds the list of changes
  234. //-----------------------------------------------------------------------------
  235. void CVP4Dialog::RefreshChangesList()
  236. {
  237. m_pChangesList->RemoveAll();
  238. m_pChangesList->RemoveAllSections();
  239. CUtlVector<P4File_t> files;
  240. p4->GetOpenedFileList( files );
  241. CUtlVector<int> sections;
  242. // find out all the changelists
  243. for (int i = 0; i < files.Count(); i++)
  244. {
  245. if (!sections.IsValidIndex(sections.Find(files[i].m_iChangelist)))
  246. {
  247. // add a new section
  248. sections.AddToTail(files[i].m_iChangelist);
  249. }
  250. }
  251. // sort the changelists
  252. qsort(sections.Base(), sections.Count(), sizeof(int), &IntSortFunc);
  253. // add the changeslists
  254. {for (int i = 0; i < sections.Count(); i++)
  255. {
  256. m_pChangesList->AddSection(sections[i], "");
  257. char szChangelistName[256];
  258. if (sections[i] > 0)
  259. {
  260. Q_snprintf(szChangelistName, sizeof(szChangelistName), "CHANGE: %d", sections[i]);
  261. }
  262. else
  263. {
  264. Q_snprintf(szChangelistName, sizeof(szChangelistName), "CHANGE: DEFAULT");
  265. }
  266. m_pChangesList->AddColumnToSection(sections[i], "file", szChangelistName, SectionedListPanel::COLUMN_BRIGHT, 512);
  267. }}
  268. // add the files to the changelists
  269. {for (int i = 0; i < files.Count(); i++)
  270. {
  271. char szFile[_MAX_PATH];
  272. Q_snprintf(szFile, sizeof(szFile), "%s/%s", p4->String( files[i].m_sPath ) + p4->GetDepotRootLength(), p4->String( files[i].m_sName ));
  273. KeyValues *pkv = new KeyValues("node", "file", szFile);
  274. m_pChangesList->AddItem(files[i].m_iChangelist, pkv);
  275. }}
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose: Refreshes the client selection combo
  279. //-----------------------------------------------------------------------------
  280. void CVP4Dialog::RefreshClientList()
  281. {
  282. m_pClientCombo->RemoveAll();
  283. CUtlVector<P4Client_t> &clients = p4->GetClientList();
  284. P4Client_t &activeClient = p4->GetActiveClient();
  285. // go through all clients
  286. for (int i = 0; i < clients.Count(); i++)
  287. {
  288. // only add clients that are defined for this machine (host)
  289. if (clients[i].m_sHost != activeClient.m_sHost)
  290. continue;
  291. // add in the new item
  292. char szText[256];
  293. Q_snprintf(szText, sizeof(szText), "%s %s", p4->String( clients[i].m_sName ), p4->String( clients[i].m_sLocalRoot ));
  294. m_pClientCombo->AddItem(szText, new KeyValues("client", "client", p4->String( clients[i].m_sName )));
  295. }
  296. m_pClientCombo->SetText( p4->String( activeClient.m_sName ));
  297. if (m_pClientCombo->GetItemCount() > 1)
  298. {
  299. m_pClientCombo->SetEnabled(true);
  300. }
  301. else
  302. {
  303. m_pClientCombo->SetEnabled(false);
  304. }
  305. }
  306. //-----------------------------------------------------------------------------
  307. // Purpose: refreshes dialog on text changing
  308. //-----------------------------------------------------------------------------
  309. void CVP4Dialog::OnTextChanged()
  310. {
  311. KeyValues *pkv = m_pClientCombo->GetActiveItemUserData();
  312. if (!pkv)
  313. return;
  314. // set the new client
  315. p4->SetActiveClient(pkv->GetString("client"));
  316. // refresh the UI
  317. Activate();
  318. m_pRevisionList->RemoveAll();
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Purpose: Cloaks a folder
  322. //-----------------------------------------------------------------------------
  323. void CVP4Dialog::CloakFolder(int iItem)
  324. {
  325. KeyValues *pkv = m_pFileTree->GetItemData(iItem);
  326. if (!pkv)
  327. return;
  328. // change the clientspec to remove the folder
  329. p4->RemovePathFromActiveClientspec(pkv->GetString("path"));
  330. // remove the folder from the tree view
  331. m_pFileTree->RemoveItem(0-iItem, false);
  332. m_pFileTree->InvalidateLayout();
  333. m_pFileTree->Repaint();
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose: open for edit
  337. //-----------------------------------------------------------------------------
  338. void CVP4Dialog::OpenFileForEdit(int iItem)
  339. {
  340. KeyValues *pkv = m_pFileTree->GetItemData(iItem);
  341. if (!pkv)
  342. return;
  343. p4->OpenFileForEdit(pkv->GetString("path"));
  344. RefreshChangesList();
  345. }
  346. //-----------------------------------------------------------------------------
  347. // Purpose: open for delete
  348. //-----------------------------------------------------------------------------
  349. void CVP4Dialog::OpenFileForDelete(int iItem)
  350. {
  351. KeyValues *pkv = m_pFileTree->GetItemData(iItem);
  352. if (!pkv)
  353. return;
  354. p4->OpenFileForDelete(pkv->GetString("path"));
  355. RefreshChangesList();
  356. }
  357. //-----------------------------------------------------------------------------
  358. // Purpose: updates revision view on a file being selected
  359. //-----------------------------------------------------------------------------
  360. void CVP4Dialog::OnFileSelected()
  361. {
  362. m_pRevisionList->RemoveAll();
  363. // only update if reviews page is visible
  364. if (!m_pRevisionsPage->IsVisible())
  365. return;
  366. // update list
  367. int iItem = m_pFileTree->GetFirstSelectedItem();
  368. if (iItem < 0)
  369. {
  370. m_pRevisionList->SetEmptyListText("No file or directory currently selected.");
  371. return;
  372. }
  373. surface()->SetCursor(dc_waitarrow);
  374. m_pRevisionList->SetEmptyListText("There is no revision history for the selected file.");
  375. KeyValues *pkv = m_pFileTree->GetItemData(iItem);
  376. CUtlVector<P4Revision_t> &revisions = p4->GetRevisionList(pkv->GetString("path"), pkv->GetInt("dir"));
  377. // add all the revisions
  378. for (int i = 0; i < revisions.Count(); i++)
  379. {
  380. KeyValues *pkv = new KeyValues("node");
  381. pkv->SetString("user", p4->String( revisions[i].m_sUser ));
  382. pkv->SetInt("change", revisions[i].m_iChange);
  383. char szDate[256];
  384. Q_snprintf(szDate, sizeof(szDate), "%d/%d/%d", revisions[i].m_nYear, revisions[i].m_nMonth, revisions[i].m_nDay);
  385. pkv->SetString("date", szDate);
  386. char szTime[256];
  387. Q_snprintf(szTime, sizeof(szTime), "%d:%d:%d", revisions[i].m_nHour, revisions[i].m_nMinute, revisions[i].m_nSecond);
  388. pkv->SetString("time", szTime);
  389. // take just the first line of the description
  390. char *pDesc = revisions[i].m_Description.GetForModify();
  391. // move to the first non-whitespace
  392. while (*pDesc && (isspace(*pDesc) || iscntrl(*pDesc)))
  393. {
  394. ++pDesc;
  395. }
  396. char szShortDescription[256];
  397. Q_strncpy(szShortDescription, pDesc, sizeof(szShortDescription));
  398. // truncate to last terminator
  399. char *pTerm = szShortDescription;
  400. while (*pTerm && !iscntrl(*pTerm))
  401. {
  402. ++pTerm;
  403. }
  404. *pTerm = 0;
  405. pkv->SetString("description", szShortDescription);
  406. m_pRevisionList->AddItem(pkv, 0, false, false);
  407. }
  408. }