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.

570 lines
17 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Contains a list of files, determines their perforce status
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include <vgui_controls/PerforceFileList.h>
  8. #include <vgui_controls/ListPanel.h>
  9. #include <vgui_controls/Label.h>
  10. #include <vgui_controls/ImageList.h>
  11. #include "tier1/KeyValues.h"
  12. #include <vgui/ISurface.h>
  13. #include "filesystem.h"
  14. #include "p4lib/ip4.h"
  15. #include "tier2/tier2.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include <tier0/memdbgon.h>
  18. using namespace vgui;
  19. static int ListFileNameSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  20. {
  21. NOTE_UNUSED( pPanel );
  22. bool dir1 = item1.kv->GetInt("directory") == 1;
  23. bool dir2 = item2.kv->GetInt("directory") == 1;
  24. // if they're both not directories of files, return if dir1 is a directory (before files)
  25. if ( dir1 != dir2 )
  26. {
  27. return dir1 ? -1 : 1;
  28. }
  29. const char *string1 = item1.kv->GetString("text");
  30. const char *string2 = item2.kv->GetString("text");
  31. // YWB: Mimic windows behavior where filenames starting with numbers are sorted based on numeric part
  32. int num1 = Q_atoi( string1 );
  33. int num2 = Q_atoi( string2 );
  34. if ( num1 != 0 &&
  35. num2 != 0 )
  36. {
  37. if ( num1 < num2 )
  38. return -1;
  39. else if ( num1 > num2 )
  40. return 1;
  41. }
  42. // Push numbers before everything else
  43. if ( num1 != 0 )
  44. {
  45. return -1;
  46. }
  47. // Push numbers before everything else
  48. if ( num2 != 0 )
  49. {
  50. return 1;
  51. }
  52. return Q_stricmp( string1, string2 );
  53. }
  54. static int ListBaseStringSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
  55. {
  56. bool dir1 = item1.kv->GetInt("directory") == 1;
  57. bool dir2 = item2.kv->GetInt("directory") == 1;
  58. // if they're both not directories of files, return if dir1 is a directory (before files)
  59. if (dir1 != dir2)
  60. {
  61. return -1;
  62. }
  63. const char *string1 = item1.kv->GetString(fieldName);
  64. const char *string2 = item2.kv->GetString(fieldName);
  65. int cval = Q_stricmp(string1, string2);
  66. if ( cval == 0 )
  67. {
  68. // Use filename to break ties
  69. return ListFileNameSortFunc( pPanel, item1, item2 );
  70. }
  71. return cval;
  72. }
  73. static int ListBaseIntegerSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
  74. {
  75. bool dir1 = item1.kv->GetInt("directory") == 1;
  76. bool dir2 = item2.kv->GetInt("directory") == 1;
  77. // if they're both not directories of files, return if dir1 is a directory (before files)
  78. if (dir1 != dir2)
  79. {
  80. return -1;
  81. }
  82. int i1 = item1.kv->GetInt(fieldName);
  83. int i2 = item2.kv->GetInt(fieldName);
  84. if ( i1 == i2 )
  85. {
  86. // Use filename to break ties
  87. return ListFileNameSortFunc( pPanel, item1, item2 );
  88. }
  89. return ( i1 < i2 ) ? -1 : 1;
  90. }
  91. static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  92. {
  93. return ListBaseIntegerSortFunc( pPanel, item1, item2, "filesizeint" );
  94. }
  95. static int ListFileAttributesSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  96. {
  97. return ListBaseStringSortFunc( pPanel, item1, item2, "attributes" );
  98. }
  99. static int ListFileTypeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  100. {
  101. return ListBaseStringSortFunc( pPanel, item1, item2, "type" );
  102. }
  103. //-----------------------------------------------------------------------------
  104. // Dictionary of start dir contexts
  105. //-----------------------------------------------------------------------------
  106. struct ColumnInfo_t
  107. {
  108. char const *columnName;
  109. char const *columnText;
  110. int startingWidth;
  111. int minWidth;
  112. int maxWidth;
  113. int flags;
  114. SortFunc *pfnSort;
  115. Label::Alignment alignment;
  116. };
  117. static ColumnInfo_t g_ColInfo[] =
  118. {
  119. { "text", "#PerforceFileList_Col_Name", 175, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileNameSortFunc , Label::a_west },
  120. { "type", "#PerforceFileList_Col_Type", 150, 20, 10000, 0, &ListFileTypeSortFunc , Label::a_west },
  121. { "in_perforce", "#PerforceFileList_Col_InPerforce", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west },
  122. { "synched", "#PerforceFileList_Col_Synched", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west },
  123. { "checked_out", "#PerforceFileList_Col_Checked_Out", 50, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileAttributesSortFunc , Label::a_west },
  124. { "attributes", "#PerforceFileList_Col_Attributes", 50, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileAttributesSortFunc , Label::a_west },
  125. };
  126. //-----------------------------------------------------------------------------
  127. // Purpose: Constructor
  128. //-----------------------------------------------------------------------------
  129. PerforceFileList::PerforceFileList( Panel *pParent, const char *pPanelName ) :
  130. BaseClass( pParent, pPanelName )
  131. {
  132. SetMultiselectEnabled( false );
  133. m_bShowDeletedFiles = false;
  134. // list panel
  135. for ( int i = 0; i < ARRAYSIZE( g_ColInfo ); ++i )
  136. {
  137. const ColumnInfo_t& info = g_ColInfo[ i ];
  138. AddColumnHeader( i, info.columnName, info.columnText, info.startingWidth, info.minWidth, info.maxWidth, info.flags );
  139. SetSortFunc( i, info.pfnSort );
  140. SetColumnTextAlignment( i, info.alignment );
  141. }
  142. SetSortColumn( 0 );
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Purpose: Destructor
  146. //-----------------------------------------------------------------------------
  147. PerforceFileList::~PerforceFileList()
  148. {
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Purpose: Apply scheme settings
  152. //-----------------------------------------------------------------------------
  153. void PerforceFileList::ApplySchemeSettings(IScheme *pScheme)
  154. {
  155. BaseClass::ApplySchemeSettings( pScheme );
  156. ImageList *pImageList = new ImageList( false );
  157. pImageList->AddImage( scheme()->GetImage( "resource/icon_file", false ) );
  158. pImageList->AddImage( scheme()->GetImage( "resource/icon_folder", false ) );
  159. pImageList->AddImage( scheme()->GetImage( "resource/icon_folder_selected", false ) );
  160. SetImageList( pImageList, true );
  161. }
  162. //-----------------------------------------------------------------------------
  163. // Toggle showing deleted files or not
  164. //-----------------------------------------------------------------------------
  165. void PerforceFileList::ShowDeletedFiles( bool bShowDeletedFiles )
  166. {
  167. if ( m_bShowDeletedFiles != bShowDeletedFiles )
  168. {
  169. m_bShowDeletedFiles = bShowDeletedFiles;
  170. for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) )
  171. {
  172. KeyValues *pKeyValues = GetItem( i );
  173. if ( !pKeyValues->GetInt( "deleted", 0 ) )
  174. continue;
  175. SetItemVisible( i, m_bShowDeletedFiles );
  176. }
  177. }
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Add a directory to the directory list, returns client spec
  181. //-----------------------------------------------------------------------------
  182. void PerforceFileList::AddItemToDirectoryList( const char *pFullPath, int nItemID, bool bIsDirectory )
  183. {
  184. char pDirectoryBuf[MAX_PATH];
  185. Q_ExtractFilePath( pFullPath, pDirectoryBuf, sizeof(pDirectoryBuf) );
  186. Q_StripTrailingSlash( pDirectoryBuf );
  187. pFullPath = pDirectoryBuf;
  188. DirectoryInfo_t *pInfo;
  189. UtlSymId_t i = m_Directories.Find( pFullPath );
  190. if ( i != m_Directories.InvalidIndex() )
  191. {
  192. pInfo = &m_Directories[i];
  193. }
  194. else
  195. {
  196. char pClientSpec[MAX_PATH];
  197. if ( !p4->GetClientSpecForDirectory( pFullPath, pClientSpec, sizeof(pClientSpec) ) )
  198. {
  199. pClientSpec[0] = 0;
  200. }
  201. pInfo = &m_Directories[ pFullPath ];
  202. pInfo->m_ClientSpec = pClientSpec;
  203. }
  204. pInfo->m_ItemIDs.AddToTail( nItemID );
  205. }
  206. //-----------------------------------------------------------------------------
  207. // Add a file to the file list.
  208. //-----------------------------------------------------------------------------
  209. int PerforceFileList::AddFileToFileList( const char *pFullPath, bool bExistsOnDisk )
  210. {
  211. bool bIsFileWriteable = bExistsOnDisk ? g_pFullFileSystem->IsFileWritable( pFullPath, NULL ) : true;
  212. // add the file to the list
  213. KeyValues *kv = new KeyValues("item");
  214. const char *pRelativePath = Q_UnqualifiedFileName( pFullPath );
  215. kv->SetString( "text", pRelativePath );
  216. kv->SetString( "fullpath", pFullPath );
  217. kv->SetInt( "image", 1 );
  218. IImage *pImage = surface()->GetIconImageForFullPath( pFullPath );
  219. if ( pImage )
  220. {
  221. kv->SetPtr( "iconImage", (void *)pImage );
  222. }
  223. kv->SetInt( "imageSelected", 1 );
  224. kv->SetInt( "directory", 0 );
  225. // These are computed by Refresh
  226. kv->SetInt( "in_perforce", 0 );
  227. kv->SetInt( "synched", 0 );
  228. kv->SetInt( "checked_out", 0 );
  229. kv->SetInt( "deleted", 0 );
  230. wchar_t pFileType[ 80 ];
  231. g_pFullFileSystem->GetFileTypeForFullPath( pFullPath, pFileType, sizeof( pFileType ) );
  232. kv->SetWString( "type", pFileType );
  233. kv->SetString( "attributes", bIsFileWriteable ? "" : "R" );
  234. int nItemID = AddItem( kv, 0, false, false );
  235. kv->deleteThis();
  236. AddItemToDirectoryList( pFullPath, nItemID, false );
  237. return nItemID;
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Add a directory to the file list.
  241. //-----------------------------------------------------------------------------
  242. int PerforceFileList::AddDirectoryToFileList( const char *pFullPath, bool bExistsOnDisk )
  243. {
  244. KeyValues *kv = new KeyValues("item");
  245. const char *pRelativePath = Q_UnqualifiedFileName( pFullPath );
  246. kv->SetString( "text", pRelativePath );
  247. kv->SetString( "fullpath", pFullPath );
  248. kv->SetPtr( "iconImage", (void *)NULL );
  249. kv->SetInt( "image", 2 );
  250. kv->SetInt( "imageSelected", 3 );
  251. kv->SetInt( "directory", 1 );
  252. // These are computed by Refresh
  253. kv->SetInt( "in_perforce", 0 );
  254. kv->SetInt( "synched", 0 );
  255. kv->SetInt( "checked_out", 0 );
  256. kv->SetInt( "deleted", 0 );
  257. kv->SetString( "type", "#PerforceFileList_FileType_Folder" );
  258. kv->SetString( "attributes", "D" );
  259. int nItemID = AddItem(kv, 0, false, false);
  260. kv->deleteThis();
  261. AddItemToDirectoryList( pFullPath, nItemID, true );
  262. return nItemID;
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Add a file or directory to the file list.
  266. //-----------------------------------------------------------------------------
  267. int PerforceFileList::AddFile( const char *pFullPath, int nFileExists, int nIsDirectory )
  268. {
  269. if ( !pFullPath )
  270. return InvalidItemID();
  271. if ( !Q_IsAbsolutePath( pFullPath ) )
  272. {
  273. Warning( "Absolute paths required for PerforceFileList::AddFile!\n"
  274. "\"%s\" is not an abolute path", pFullPath );
  275. return InvalidItemID();
  276. }
  277. char pFixedPath[MAX_PATH];
  278. Q_strncpy( pFixedPath, pFullPath, sizeof(pFixedPath) );
  279. Q_FixSlashes( pFixedPath );
  280. // Check to see if the file is on disk
  281. int nItemID = -1;
  282. bool bFileExists, bIsDirectory;
  283. if ( nFileExists < 0 )
  284. {
  285. bFileExists = g_pFullFileSystem->FileExists( pFixedPath ) ;
  286. }
  287. else
  288. {
  289. bFileExists = ( nFileExists != 0 );
  290. }
  291. if ( nIsDirectory < 0 )
  292. {
  293. if ( bFileExists )
  294. {
  295. bIsDirectory = g_pFullFileSystem->IsDirectory( pFixedPath );
  296. }
  297. else
  298. {
  299. int nLen = Q_strlen( pFixedPath );
  300. bIsDirectory = ( pFixedPath[nLen-1] == CORRECT_PATH_SEPARATOR );
  301. }
  302. }
  303. else
  304. {
  305. bIsDirectory = ( nIsDirectory != 0 );
  306. }
  307. if ( bIsDirectory )
  308. {
  309. nItemID = AddDirectoryToFileList( pFixedPath, bFileExists );
  310. }
  311. else
  312. {
  313. nItemID = AddFileToFileList( pFixedPath, bFileExists );
  314. }
  315. return nItemID;
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Remove all files from the list
  319. //-----------------------------------------------------------------------------
  320. void PerforceFileList::RemoveAllFiles()
  321. {
  322. RemoveAll();
  323. m_Directories.Clear();
  324. }
  325. //-----------------------------------------------------------------------------
  326. // Finds a file in the p4 list
  327. //-----------------------------------------------------------------------------
  328. static P4File_t *FindFileInPerforceList( const char *pFileName, CUtlVector<P4File_t> &fileList, bool *pFound )
  329. {
  330. int nCount = fileList.Count();
  331. for ( int i = 0; i < nCount; ++i )
  332. {
  333. if ( pFound[i] )
  334. continue;
  335. const char *pPerforceFileName = p4->String( fileList[i].m_sLocalFile );
  336. if ( !Q_stricmp( pPerforceFileName, pFileName ) )
  337. {
  338. pFound[i] = true;
  339. return &fileList[i];
  340. }
  341. }
  342. return NULL;
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Refresh perforce information
  346. //-----------------------------------------------------------------------------
  347. void PerforceFileList::RefreshPerforceState( int nItemID, bool bFileExists, P4File_t *pFileInfo )
  348. {
  349. KeyValues *kv = GetItem( nItemID );
  350. bool bIsSynched = false;
  351. bool bIsFileInPerforce = (pFileInfo != NULL);
  352. if ( bIsFileInPerforce )
  353. {
  354. if ( pFileInfo->m_bDeleted != bFileExists )
  355. {
  356. bIsSynched = ( pFileInfo->m_bDeleted || ( pFileInfo->m_iHeadRevision == pFileInfo->m_iHaveRevision ) );
  357. }
  358. }
  359. else
  360. {
  361. bIsSynched = !bFileExists;
  362. }
  363. bool bIsDeleted = bIsFileInPerforce && !bFileExists && pFileInfo->m_bDeleted;
  364. kv->SetInt( "in_perforce", bIsFileInPerforce );
  365. kv->SetInt( "synched", bIsSynched );
  366. kv->SetInt( "checked_out", bIsFileInPerforce && ( pFileInfo->m_eOpenState != P4FILE_UNOPENED ) );
  367. kv->SetInt( "deleted", bIsDeleted );
  368. if ( bIsDeleted )
  369. {
  370. SetItemVisible( nItemID, m_bShowDeletedFiles );
  371. }
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Refresh perforce information
  375. //-----------------------------------------------------------------------------
  376. void PerforceFileList::Refresh()
  377. {
  378. /*
  379. // Slow method.. does too many perforce operations
  380. for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) )
  381. {
  382. const char *pFile = GetFile( i );
  383. P4File_t fileInfo;
  384. bool bIsFileInPerforce = p4->GetFileInfo( pFile, &fileInfo );
  385. bool bFileExists = g_pFullFileSystem->FileExists( pFile );
  386. RefreshPerforceState( i, bFileExists, bIsFileInPerforce ? &fileInfo : NULL );
  387. }
  388. */
  389. // NOTE: Reducing the # of perforce calls is important for performance
  390. int nCount = m_Directories.GetNumStrings();
  391. for ( int i = 0; i < nCount; ++i )
  392. {
  393. const char *pDirectory = m_Directories.String(i);
  394. DirectoryInfo_t *pInfo = &m_Directories[i];
  395. // Retrives files, uses faster method to avoid finding clientspec
  396. CUtlVector<P4File_t> &fileList = p4->GetFileListUsingClientSpec( pDirectory, pInfo->m_ClientSpec );
  397. int nFileCount = fileList.Count();
  398. bool *pFound = (bool*)_alloca( nFileCount * sizeof(bool) );
  399. memset( pFound, 0, nFileCount * sizeof(bool) );
  400. int nItemCount = pInfo->m_ItemIDs.Count();
  401. for ( int j = 0; j < nItemCount; ++j )
  402. {
  403. int nItemID = pInfo->m_ItemIDs[j];
  404. const char *pFileName = GetFile( nItemID );
  405. bool bFileExists = g_pFullFileSystem->FileExists( pFileName );
  406. P4File_t *pFileInfo = FindFileInPerforceList( pFileName, fileList, pFound );
  407. RefreshPerforceState( nItemID, bFileExists, pFileInfo );
  408. }
  409. }
  410. }
  411. //-----------------------------------------------------------------------------
  412. // Is a particular list item a directory?
  413. //-----------------------------------------------------------------------------
  414. bool PerforceFileList::IsDirectoryItem( int nItemID )
  415. {
  416. KeyValues *kv = GetItem( nItemID );
  417. return kv->GetInt( "directory", 0 ) != 0;
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Returns the file associated with a particular item ID
  421. //-----------------------------------------------------------------------------
  422. const char *PerforceFileList::GetFile( int nItemID )
  423. {
  424. KeyValues *kv = GetItem( nItemID );
  425. Assert( kv );
  426. return kv->GetString( "fullpath", "<no file>" );
  427. }
  428. //-----------------------------------------------------------------------------
  429. // Find the item ID associated with a particular file
  430. //-----------------------------------------------------------------------------
  431. int PerforceFileList::FindFile( const char *pFullPath )
  432. {
  433. for ( int i = FirstItem(); i != InvalidItemID(); i = NextItem( i ) )
  434. {
  435. const char *pFile = GetFile( i );
  436. if ( !Q_stricmp( pFile, pFullPath ) )
  437. return i;
  438. }
  439. return InvalidItemID();
  440. }
  441. //-----------------------------------------------------------------------------
  442. // Is a file already in the list?
  443. //-----------------------------------------------------------------------------
  444. bool PerforceFileList::IsFileInList( const char *pFullPath )
  445. {
  446. return ( FindFile( pFullPath ) != InvalidItemID() );
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose: Double-click: expand folders
  450. //-----------------------------------------------------------------------------
  451. void PerforceFileList::OnMouseDoublePressed( MouseCode code )
  452. {
  453. if ( code == MOUSE_LEFT )
  454. {
  455. // select the item
  456. OnMousePressed(code);
  457. // post a special message
  458. if ( GetSelectedItemsCount() > 0 )
  459. {
  460. PostActionSignal( new KeyValues("ItemDoubleClicked" ) );
  461. }
  462. return;
  463. }
  464. BaseClass::OnMouseDoublePressed( code );
  465. }