Counter Strike : Global Offensive Source Code
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.

623 lines
19 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // List of perforce files and operations
  4. //
  5. //=============================================================================
  6. #include "vgui_controls/perforcefilelistframe.h"
  7. #include "tier1/keyvalues.h"
  8. #include "vgui_controls/Button.h"
  9. #include "vgui_controls/ListPanel.h"
  10. #include "vgui_controls/Splitter.h"
  11. #include "vgui_controls/TextEntry.h"
  12. #include "vgui_controls/MessageBox.h"
  13. #include "tier2/tier2.h"
  14. #include "p4lib/ip4.h"
  15. #include "filesystem.h"
  16. #include "vgui/IVGui.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include "tier0/memdbgon.h"
  19. //-----------------------------------------------------------------------------
  20. // Sort by asset name
  21. //-----------------------------------------------------------------------------
  22. static int __cdecl OperationSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 )
  23. {
  24. const char *string1 = item1.kv->GetString("operation");
  25. const char *string2 = item2.kv->GetString("operation");
  26. int nRetVal = Q_stricmp( string1, string2 );
  27. if ( nRetVal != 0 )
  28. return nRetVal;
  29. string1 = item1.kv->GetString("filename");
  30. string2 = item2.kv->GetString("filename");
  31. return Q_stricmp( string1, string2 );
  32. }
  33. static int __cdecl FileBrowserSortFunc( vgui::ListPanel *pPanel, const vgui::ListPanelItem &item1, const vgui::ListPanelItem &item2 )
  34. {
  35. const char *string1 = item1.kv->GetString("filename");
  36. const char *string2 = item2.kv->GetString("filename");
  37. return Q_stricmp( string1, string2 );
  38. }
  39. //-----------------------------------------------------------------------------
  40. // Constructor
  41. //-----------------------------------------------------------------------------
  42. COperationFileListFrame::COperationFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, bool bShowDescription, bool bShowOkOnly, int nDialogID ) :
  43. BaseClass( pParent, "PerforceFileList" )
  44. {
  45. m_pText = NULL;
  46. vgui::Panel *pBrowserParent = this;
  47. m_pDescription = NULL;
  48. m_pSplitter = NULL;
  49. if ( bShowDescription )
  50. {
  51. m_pSplitter = new vgui::Splitter( this, "Splitter", vgui::SPLITTER_MODE_HORIZONTAL, 1 );
  52. pBrowserParent = m_pSplitter->GetChild( 0 );
  53. vgui::Panel *pDescParent = m_pSplitter->GetChild( 1 );
  54. m_pDescription = new vgui::TextEntry( pDescParent, "Description" );
  55. m_pDescription->SetMultiline( true );
  56. m_pDescription->SetCatchEnterKey( true );
  57. m_pDescription->SetText( "<enter description here>" );
  58. }
  59. // FIXME: Might be nice to have checkboxes per row
  60. m_pFileBrowser = new vgui::ListPanel( pBrowserParent, "Browser" );
  61. m_pFileBrowser->AddColumnHeader( 0, "operation", "Operation", 52, 0 );
  62. m_pFileBrowser->AddColumnHeader( 1, "filename", pColumnHeader, 128, vgui::ListPanel::COLUMN_RESIZEWITHWINDOW );
  63. m_pFileBrowser->SetSelectIndividualCells( false );
  64. m_pFileBrowser->SetMultiselectEnabled( false );
  65. m_pFileBrowser->SetEmptyListText( "No Perforce Operations" );
  66. m_pFileBrowser->SetDragEnabled( true );
  67. m_pFileBrowser->AddActionSignalTarget( this );
  68. m_pFileBrowser->SetSortFunc( 0, OperationSortFunc );
  69. m_pFileBrowser->SetSortFunc( 1, FileBrowserSortFunc );
  70. m_pFileBrowser->SetSortColumn( 0 );
  71. m_pYesButton = new vgui::Button( this, "YesButton", "Yes", this, "Yes" );
  72. m_pNoButton = new vgui::Button( this, "NoButton", "No", this, "No" );
  73. SetBlockDragChaining( true );
  74. SetDeleteSelfOnClose( true );
  75. if ( bShowDescription )
  76. {
  77. LoadControlSettingsAndUserConfig( "resource/perforcefilelistdescription.res", nDialogID );
  78. }
  79. else
  80. {
  81. LoadControlSettingsAndUserConfig( "resource/perforcefilelist.res", nDialogID );
  82. }
  83. if ( bShowOkOnly )
  84. {
  85. m_pYesButton->SetText( "#MessageBox_OK" );
  86. m_pNoButton->SetVisible( false );
  87. }
  88. m_pContextKeyValues = NULL;
  89. SetTitle( pTitle, false );
  90. }
  91. COperationFileListFrame::~COperationFileListFrame()
  92. {
  93. SaveUserConfig();
  94. CleanUpMessage();
  95. if ( m_pText )
  96. {
  97. delete[] m_pText;
  98. }
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Deletes the message
  102. //-----------------------------------------------------------------------------
  103. void COperationFileListFrame::CleanUpMessage()
  104. {
  105. if ( m_pContextKeyValues )
  106. {
  107. m_pContextKeyValues->deleteThis();
  108. m_pContextKeyValues = NULL;
  109. }
  110. }
  111. //-----------------------------------------------------------------------------
  112. // Performs layout
  113. //-----------------------------------------------------------------------------
  114. void COperationFileListFrame::PerformLayout()
  115. {
  116. BaseClass::PerformLayout();
  117. if ( m_pSplitter )
  118. {
  119. int x, y, w, h;
  120. GetClientArea( x, y, w, h );
  121. y += 6;
  122. h -= 36;
  123. m_pSplitter->SetBounds( x, y, w, h );
  124. }
  125. }
  126. //-----------------------------------------------------------------------------
  127. // Adds files to the frame
  128. //-----------------------------------------------------------------------------
  129. void COperationFileListFrame::ClearAllOperations()
  130. {
  131. m_pFileBrowser->RemoveAll();
  132. }
  133. //-----------------------------------------------------------------------------
  134. // Adds the strings to the list panel
  135. //-----------------------------------------------------------------------------
  136. void COperationFileListFrame::AddOperation( const char *pOperation, const char *pFileName )
  137. {
  138. KeyValues *kv = new KeyValues( "node", "filename", pFileName );
  139. kv->SetString( "operation", pOperation );
  140. m_pFileBrowser->AddItem( kv, 0, false, false );
  141. }
  142. void COperationFileListFrame::AddOperation( const char *pOperation, const char *pFileName, const Color& clr )
  143. {
  144. KeyValues *kv = new KeyValues( "node", "filename", pFileName );
  145. kv->SetString( "operation", pOperation );
  146. kv->SetColor( "cellcolor", clr );
  147. m_pFileBrowser->AddItem( kv, 0, false, false );
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Resizes the operation column to fit the operation text
  151. //-----------------------------------------------------------------------------
  152. void COperationFileListFrame::ResizeOperationColumnToContents()
  153. {
  154. m_pFileBrowser->ResizeColumnToContents( 0 );
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Sets the column header for the 'operation' column
  158. //-----------------------------------------------------------------------------
  159. void COperationFileListFrame::SetOperationColumnHeaderText( const char *pText )
  160. {
  161. m_pFileBrowser->SetColumnHeaderText( 0, pText );
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Adds the strings to the list panel
  165. //-----------------------------------------------------------------------------
  166. void COperationFileListFrame::DoModal( KeyValues *pContextKeyValues, const char *pMessage )
  167. {
  168. m_MessageName = pMessage ? pMessage : "OperationConfirmed";
  169. CleanUpMessage();
  170. m_pContextKeyValues = pContextKeyValues;
  171. m_pFileBrowser->SortList();
  172. if ( m_pNoButton->IsVisible() )
  173. {
  174. m_pYesButton->SetEnabled( m_pFileBrowser->GetItemCount() != 0 );
  175. }
  176. BaseClass::DoModal();
  177. }
  178. //-----------------------------------------------------------------------------
  179. // Retrieves the number of files, the file names, and operations
  180. //-----------------------------------------------------------------------------
  181. int COperationFileListFrame::GetOperationCount()
  182. {
  183. return m_pFileBrowser->GetItemCount();
  184. }
  185. const char *COperationFileListFrame::GetFileName( int i )
  186. {
  187. int nItemId = m_pFileBrowser->GetItemIDFromRow( i );
  188. KeyValues *pKeyValues = m_pFileBrowser->GetItem( nItemId );
  189. return pKeyValues->GetString( "filename" );
  190. }
  191. const char *COperationFileListFrame::GetOperation( int i )
  192. {
  193. int nItemId = m_pFileBrowser->GetItemIDFromRow( i );
  194. KeyValues *pKeyValues = m_pFileBrowser->GetItem( nItemId );
  195. return pKeyValues->GetString( "operation" );
  196. }
  197. //-----------------------------------------------------------------------------
  198. // Retreives the description (only if it was shown)
  199. //-----------------------------------------------------------------------------
  200. const char *COperationFileListFrame::GetDescription()
  201. {
  202. return m_pText;
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Returns the message name
  206. //-----------------------------------------------------------------------------
  207. const char *COperationFileListFrame::CompletionMessage()
  208. {
  209. return m_MessageName;
  210. }
  211. //-----------------------------------------------------------------------------
  212. // On command
  213. //-----------------------------------------------------------------------------
  214. void COperationFileListFrame::OnCommand( const char *pCommand )
  215. {
  216. if ( !Q_stricmp( pCommand, "Yes" ) )
  217. {
  218. if ( m_pDescription )
  219. {
  220. int nLen = m_pDescription->GetTextLength() + 1;
  221. m_pText = new char[ nLen ];
  222. m_pDescription->GetText( m_pText, nLen );
  223. }
  224. KeyValues *pActionKeys;
  225. if ( PerformOperation() )
  226. {
  227. pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 1 );
  228. }
  229. else
  230. {
  231. pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 0 );
  232. }
  233. if ( m_pContextKeyValues )
  234. {
  235. pActionKeys->AddSubKey( m_pContextKeyValues );
  236. m_pContextKeyValues = NULL;
  237. }
  238. CloseModal();
  239. PostActionSignal( pActionKeys );
  240. return;
  241. }
  242. if ( !Q_stricmp( pCommand, "No" ) )
  243. {
  244. KeyValues *pActionKeys = new KeyValues( CompletionMessage(), "operationPerformed", 0 );
  245. if ( m_pContextKeyValues )
  246. {
  247. pActionKeys->AddSubKey( m_pContextKeyValues );
  248. m_pContextKeyValues = NULL;
  249. }
  250. CloseModal();
  251. PostActionSignal( pActionKeys );
  252. return;
  253. }
  254. BaseClass::OnCommand( pCommand );
  255. }
  256. //-----------------------------------------------------------------------------
  257. //
  258. // Version that does the work of perforce actions
  259. //
  260. //-----------------------------------------------------------------------------
  261. CPerforceFileListFrame::CPerforceFileListFrame( vgui::Panel *pParent, const char *pTitle, const char *pColumnHeader, PerforceAction_t action ) :
  262. BaseClass( pParent, pTitle, pColumnHeader, (action == PERFORCE_ACTION_FILE_SUBMIT), false, OPERATION_DIALOG_ID_PERFORCE )
  263. {
  264. m_Action = action;
  265. }
  266. CPerforceFileListFrame::~CPerforceFileListFrame()
  267. {
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Activates the modal dialog
  271. //-----------------------------------------------------------------------------
  272. void CPerforceFileListFrame::DoModal( KeyValues *pContextKeys, const char *pMessage )
  273. {
  274. BaseClass::DoModal( pContextKeys, pMessage ? pMessage : "PerforceActionConfirmed" );
  275. }
  276. //-----------------------------------------------------------------------------
  277. // Adds a file for open
  278. //-----------------------------------------------------------------------------
  279. void CPerforceFileListFrame::AddFileForOpen( const char *pFullPath )
  280. {
  281. bool bIsInPerforce = p4->IsFileInPerforce( pFullPath );
  282. bool bIsOpened = ( p4->GetFileState( pFullPath ) != P4FILE_UNOPENED );
  283. switch( m_Action )
  284. {
  285. case PERFORCE_ACTION_FILE_ADD:
  286. if ( !bIsInPerforce && !bIsOpened )
  287. {
  288. AddOperation( "Add", pFullPath );
  289. }
  290. break;
  291. case PERFORCE_ACTION_FILE_EDIT:
  292. if ( bIsInPerforce && !bIsOpened )
  293. {
  294. AddOperation( "Edit", pFullPath );
  295. }
  296. break;
  297. case PERFORCE_ACTION_FILE_DELETE:
  298. if ( bIsInPerforce && !bIsOpened )
  299. {
  300. AddOperation( "Delete", pFullPath );
  301. }
  302. break;
  303. }
  304. }
  305. //-----------------------------------------------------------------------------
  306. // Add files to dialog for submit/revert dialogs
  307. //-----------------------------------------------------------------------------
  308. void CPerforceFileListFrame::AddFileForSubmit( const char *pFullPath, P4FileState_t state )
  309. {
  310. if ( state == P4FILE_UNOPENED )
  311. return;
  312. char pBuf[128];
  313. const char *pPrefix = (m_Action == PERFORCE_ACTION_FILE_REVERT) ? "Revert" : "Submit";
  314. switch( state )
  315. {
  316. case P4FILE_OPENED_FOR_ADD:
  317. Q_snprintf( pBuf, sizeof(pBuf), "%s Add", pPrefix );
  318. AddOperation( pBuf, pFullPath );
  319. break;
  320. case P4FILE_OPENED_FOR_EDIT:
  321. Q_snprintf( pBuf, sizeof(pBuf), "%s Edit", pPrefix );
  322. AddOperation( pBuf, pFullPath );
  323. break;
  324. case P4FILE_OPENED_FOR_DELETE:
  325. Q_snprintf( pBuf, sizeof(pBuf), "%s Delete", pPrefix );
  326. AddOperation( pBuf, pFullPath );
  327. break;
  328. case P4FILE_OPENED_FOR_INTEGRATE:
  329. Q_snprintf( pBuf, sizeof(pBuf), "%s Integrate", pPrefix );
  330. AddOperation( pBuf, pFullPath );
  331. break;
  332. }
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Version of AddFile that accepts full paths
  336. //-----------------------------------------------------------------------------
  337. void CPerforceFileListFrame::AddFile( const char *pFullPath )
  338. {
  339. if ( m_Action < PERFORCE_ACTION_FILE_REVERT )
  340. {
  341. // If the file wasn't found on the disk, then abort
  342. if ( g_pFullFileSystem->FileExists( pFullPath, NULL ) )
  343. {
  344. AddFileForOpen( pFullPath );
  345. }
  346. return;
  347. }
  348. // Deal with submit, revert
  349. bool bFileExists = g_pFullFileSystem->FileExists( pFullPath, NULL );
  350. P4FileState_t state = p4->GetFileState( pFullPath );
  351. if ( bFileExists || (state == P4FILE_OPENED_FOR_DELETE) )
  352. {
  353. AddFileForSubmit( pFullPath, state );
  354. }
  355. }
  356. //-----------------------------------------------------------------------------
  357. // Version of AddFile that accepts relative paths + search path ids
  358. //-----------------------------------------------------------------------------
  359. void CPerforceFileListFrame::AddFile( const char *pRelativePath, const char *pPathId )
  360. {
  361. // Deal with add, open, edit
  362. if ( m_Action < PERFORCE_ACTION_FILE_REVERT )
  363. {
  364. // If the file wasn't found on the disk, then abort
  365. if ( g_pFullFileSystem->FileExists( pRelativePath, pPathId ) )
  366. {
  367. char pFullPath[MAX_PATH];
  368. g_pFullFileSystem->RelativePathToFullPath( pRelativePath, pPathId, pFullPath, sizeof( pFullPath ) );
  369. AddFileForOpen( pFullPath );
  370. }
  371. return;
  372. }
  373. // Deal with submit, revert
  374. // First, handle the case where the file exists on the drive
  375. char pFullPath[MAX_PATH];
  376. if ( g_pFullFileSystem->FileExists( pRelativePath, pPathId ) )
  377. {
  378. g_pFullFileSystem->RelativePathToFullPath( pRelativePath, pPathId, pFullPath, sizeof( pFullPath ) );
  379. P4FileState_t state = p4->GetFileState( pFullPath );
  380. AddFileForSubmit( pFullPath, state );
  381. return;
  382. }
  383. // Get the list of opened files, cache it off so we aren't continually reasking
  384. if ( Q_stricmp( pPathId, m_LastOpenedFilePathId ) )
  385. {
  386. p4->GetOpenedFileListInPath( pPathId, m_OpenedFiles );
  387. m_LastOpenedFilePathId = pPathId;
  388. }
  389. // If the file doesn't exist, it was opened for delete.
  390. // Using the client spec of the path, we need to piece together
  391. // the full path; the full path unfortunately is usually ambiguous:
  392. // you can never exactly know which mod it came from.
  393. char pTemp[MAX_PATH];
  394. char pSearchString[MAX_PATH];
  395. Q_strncpy( pSearchString, pRelativePath, sizeof(pSearchString) );
  396. Q_FixSlashes( pSearchString );
  397. int k;
  398. int nOpenedFileCount = m_OpenedFiles.Count();
  399. for ( k = 0; k < nOpenedFileCount; ++k )
  400. {
  401. if ( m_OpenedFiles[k].m_eOpenState != P4FILE_OPENED_FOR_DELETE )
  402. continue;
  403. // Check to see if the end of the local file matches the file
  404. const char *pLocalFile = p4->String( m_OpenedFiles[k].m_sLocalFile );
  405. // This ensures the full path lies under the search path
  406. if ( !g_pFullFileSystem->FullPathToRelativePathEx( pLocalFile, pPathId, pTemp, sizeof(pTemp) ) )
  407. continue;
  408. // The relative paths had better be the same
  409. if ( Q_stricmp( pTemp, pSearchString ) )
  410. continue;
  411. AddFileForSubmit( pLocalFile, m_OpenedFiles[k].m_eOpenState );
  412. break;
  413. }
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Does the perforce operation
  417. //-----------------------------------------------------------------------------
  418. bool CPerforceFileListFrame::PerformOperation( )
  419. {
  420. int nFileCount = GetOperationCount();
  421. const char **ppFileNames = (const char**)stackalloc( nFileCount * sizeof(char*) );
  422. for ( int i = 0; i < nFileCount; ++i )
  423. {
  424. ppFileNames[i] = GetFileName( i );
  425. }
  426. bool bSuccess = false;
  427. switch ( m_Action )
  428. {
  429. case PERFORCE_ACTION_FILE_ADD:
  430. bSuccess = p4->OpenFilesForAdd( nFileCount, ppFileNames );
  431. break;
  432. case PERFORCE_ACTION_FILE_EDIT:
  433. bSuccess = p4->OpenFilesForEdit( nFileCount, ppFileNames );
  434. break;
  435. case PERFORCE_ACTION_FILE_DELETE:
  436. bSuccess = p4->OpenFilesForDelete( nFileCount, ppFileNames );
  437. break;
  438. case PERFORCE_ACTION_FILE_REVERT:
  439. bSuccess = p4->RevertFiles( nFileCount, ppFileNames );
  440. break;
  441. case PERFORCE_ACTION_FILE_SUBMIT:
  442. {
  443. // Ensure a description was added
  444. const char *pDescription = GetDescription();
  445. if ( !pDescription[0] || !Q_stricmp( pDescription, "<enter description here>" ) )
  446. {
  447. vgui::MessageBox *pError = new vgui::MessageBox( "Submission Error!", "Description required for submission.", GetParent() );
  448. pError->SetSmallCaption( true );
  449. pError->DoModal();
  450. return false;
  451. }
  452. else
  453. {
  454. bSuccess = p4->SubmitFiles( nFileCount, ppFileNames, pDescription );
  455. }
  456. }
  457. break;
  458. }
  459. const char *pErrorString = p4->GetLastError();
  460. if ( !bSuccess )
  461. {
  462. vgui::MessageBox *pError = new vgui::MessageBox( "Perforce Error!", pErrorString, GetParent() );
  463. pError->SetSmallCaption( true );
  464. pError->DoModal();
  465. }
  466. #if 0
  467. if ( *pErrorString )
  468. {
  469. if ( V_strstr( pErrorString, "opened for add" ) )
  470. return bSuccess;
  471. if ( V_strstr( pErrorString, "opened for edit" ) )
  472. return bSuccess;
  473. // TODO - figure out the rest of these...
  474. const char *pPrefix = "Perforce has generated the following message which may or may not be an error.\n"
  475. "Please email joe with the text of the message, whether you think it was an error, and what perforce operation you where performing.\n"
  476. "To copy the message, hit ~ to enter the console, where you will find the message reprinted.\n"
  477. "Select the lines of text in the message, right click, select Copy, and then paste into an email message.\n\n";
  478. static int nPrefixLen = V_strlen( pPrefix );
  479. int nErrorStringLength = V_strlen( pErrorString );
  480. char *pMsg = (char*)_alloca( nPrefixLen + nErrorStringLength + 1 );
  481. V_strcpy( pMsg, pPrefix );
  482. V_strcpy( pMsg + nPrefixLen, pErrorString );
  483. vgui::MessageBox *pError = new vgui::MessageBox( "Dubious Perforce Message", pMsg, GetParent() );
  484. pError->SetSmallCaption( true );
  485. pError->DoModal();
  486. }
  487. #endif
  488. return bSuccess;
  489. }
  490. //-----------------------------------------------------------------------------
  491. // Show the perforce query dialog
  492. //-----------------------------------------------------------------------------
  493. void ShowPerforceQuery( vgui::Panel *pParent, const char *pFileName, vgui::Panel *pActionSignalTarget, KeyValues *pKeyValues, PerforceAction_t actionFilter )
  494. {
  495. // Refresh the current perforce settings
  496. p4->RefreshActiveClient();
  497. PerforceAction_t action = PERFORCE_ACTION_NONE;
  498. const char *pTitle = NULL;
  499. if ( !p4->IsFileInPerforce( pFileName ) )
  500. {
  501. // If the file isn't in perforce, ask to add it
  502. action = PERFORCE_ACTION_FILE_ADD;
  503. pTitle = "Add File to Perforce?";
  504. }
  505. else if ( p4->GetFileState( pFileName ) == P4FILE_UNOPENED )
  506. {
  507. // If the file isn't checked out yet, ask to check it out
  508. action = PERFORCE_ACTION_FILE_EDIT;
  509. pTitle = "Check Out File from Perforce?";
  510. }
  511. if ( ( action == PERFORCE_ACTION_NONE ) || ( ( actionFilter != PERFORCE_ACTION_NONE ) && ( actionFilter != action ) ) )
  512. {
  513. // Spoof a completion event
  514. KeyValues *pSpoofKeys = new KeyValues( "PerforceQueryCompleted", "operationPerformed", 1 );
  515. if ( pKeyValues )
  516. {
  517. pSpoofKeys->AddSubKey( pKeyValues );
  518. }
  519. vgui::ivgui()->PostMessage( pActionSignalTarget->GetVPanel(), pSpoofKeys, 0 );
  520. return;
  521. }
  522. CPerforceFileListFrame *pQuery = new CPerforceFileListFrame( pParent, pTitle, "File", action );
  523. pQuery->AddFile( pFileName );
  524. if ( pActionSignalTarget )
  525. {
  526. pQuery->AddActionSignalTarget( pActionSignalTarget );
  527. }
  528. pQuery->DoModal( pKeyValues, "PerforceQueryCompleted" );
  529. }