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.

1514 lines
42 KiB

  1. //===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: Implementation of vgui generic open file dialog
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #define PROTECTED_THINGS_DISABLE
  8. #if defined( WIN32 )
  9. #if !defined( _GAMECONSOLE )
  10. #include "winlite.h"
  11. #include <shellapi.h>
  12. #endif
  13. #elif defined( POSIX )
  14. #include <stdlib.h>
  15. #define _stat stat
  16. #define _wcsnicmp wcsncmp
  17. #else
  18. #error
  19. #endif
  20. #undef GetCurrentDirectory
  21. #include "filesystem.h"
  22. #include <sys/stat.h>
  23. #include "tier1/utldict.h"
  24. #include "tier1/utlstring.h"
  25. #include <vgui/IScheme.h>
  26. #include <vgui/ISurface.h>
  27. #include <vgui/ISystem.h>
  28. #include <keyvalues.h>
  29. #include <vgui/IVGui.h>
  30. #include <vgui/ILocalize.h>
  31. #include <vgui/IInput.h>
  32. #include <vgui_controls/FileOpenDialog.h>
  33. #include <vgui_controls/Button.h>
  34. #include <vgui_controls/ComboBox.h>
  35. #include <vgui_controls/InputDialog.h>
  36. #include <vgui_controls/Label.h>
  37. #include <vgui_controls/ListPanel.h>
  38. #include <vgui_controls/TextEntry.h>
  39. #include <vgui_controls/ImageList.h>
  40. #include <vgui_controls/MenuItem.h>
  41. #include <vgui_controls/Tooltip.h>
  42. #if defined( _X360 )
  43. #include "xbox/xbox_win32stubs.h"
  44. #undef GetCurrentDirectory
  45. #endif
  46. #if defined( _PS3 )
  47. #include "ps3/ps3_core.h"
  48. #include "ps3/ps3_win32stubs.h"
  49. #undef GetCurrentDirectory
  50. #endif
  51. #include "tier1/fmtstr.h"
  52. // memdbgon must be the last include file in a .cpp file!!!
  53. #include <tier0/memdbgon.h>
  54. using namespace vgui;
  55. static int s_nLastSortColumn = 0;
  56. static const int MAX_FILTER_LENGTH = 255;
  57. static const int MAX_SEARCH_HISTORY = 8;
  58. static int ListFileNameSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  59. {
  60. NOTE_UNUSED( pPanel );
  61. bool dir1 = item1.kv->GetInt("directory") == 1;
  62. bool dir2 = item2.kv->GetInt("directory") == 1;
  63. // if they're both not directories of files, return if dir1 is a directory (before files)
  64. if (dir1 != dir2)
  65. {
  66. return dir1 ? -1 : 1;
  67. }
  68. const char *string1 = item1.kv->GetString("text");
  69. const char *string2 = item2.kv->GetString("text");
  70. // YWB: Mimic windows behavior where filenames starting with numbers are sorted based on numeric part
  71. int num1 = Q_atoi( string1 );
  72. int num2 = Q_atoi( string2 );
  73. if ( num1 != 0 &&
  74. num2 != 0 )
  75. {
  76. if ( num1 < num2 )
  77. return -1;
  78. else if ( num1 > num2 )
  79. return 1;
  80. }
  81. // Push numbers before everything else
  82. if ( num1 != 0 )
  83. {
  84. return -1;
  85. }
  86. // Push numbers before everything else
  87. if ( num2 != 0 )
  88. {
  89. return 1;
  90. }
  91. return Q_stricmp( string1, string2 );
  92. }
  93. static int ListBaseStringSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
  94. {
  95. bool dir1 = item1.kv->GetInt("directory") == 1;
  96. bool dir2 = item2.kv->GetInt("directory") == 1;
  97. // if they're both not directories of files, return if dir1 is a directory (before files)
  98. if (dir1 != dir2)
  99. {
  100. return -1;
  101. }
  102. const char *string1 = item1.kv->GetString(fieldName);
  103. const char *string2 = item2.kv->GetString(fieldName);
  104. int cval = Q_stricmp(string1, string2);
  105. if ( cval == 0 )
  106. {
  107. // Use filename to break ties
  108. return ListFileNameSortFunc( pPanel, item1, item2 );
  109. }
  110. return cval;
  111. }
  112. static int ListBaseInteger64SortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
  113. {
  114. bool dir1 = item1.kv->GetInt("directory") == 1;
  115. bool dir2 = item2.kv->GetInt("directory") == 1;
  116. // if they're both not directories of files, return if dir1 is a directory (before files)
  117. if ( dir1 != dir2 )
  118. {
  119. return dir1 ? -1 : 1;
  120. }
  121. int64 n1 = (int64)item1.kv->GetUint64( fieldName );
  122. int64 n2 = (int64)item2.kv->GetUint64( fieldName );
  123. if ( n1 == n2 )
  124. {
  125. // Use filename to break ties
  126. return ListFileNameSortFunc( pPanel, item1, item2 );
  127. }
  128. return ( n1 < n2 ) ? -1 : 1;
  129. }
  130. static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  131. {
  132. return ListBaseInteger64SortFunc( pPanel, item1, item2, "filesizeint" );
  133. }
  134. static int ListFileModifiedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  135. {
  136. // NOTE: Backward order to get most recent files first
  137. return ListBaseInteger64SortFunc( pPanel, item2, item1, "modifiedint" );
  138. }
  139. static int ListFileCreatedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  140. {
  141. // NOTE: Backward order to get most recent files first
  142. return ListBaseInteger64SortFunc( pPanel, item2, item1, "createdint" );
  143. }
  144. static int ListFileAttributesSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  145. {
  146. return ListBaseStringSortFunc( pPanel, item1, item2, "attributes" );
  147. }
  148. static int ListFileTypeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
  149. {
  150. return ListBaseStringSortFunc( pPanel, item1, item2, "type" );
  151. }
  152. namespace vgui
  153. {
  154. class FileNameComboBox : public ComboBox
  155. {
  156. DECLARE_CLASS_SIMPLE( FileNameComboBox, ComboBox );
  157. public:
  158. FileNameComboBox(FileOpenDialog *parent, const char *panelName, int numLines, bool allowEdit) :
  159. BaseClass( parent, panelName, numLines, allowEdit )
  160. {
  161. }
  162. virtual void OnKeyCodeTyped( KeyCode code )
  163. {
  164. if ( code == KEY_ENTER && !IsDropdownVisible() )
  165. {
  166. // Post to parent
  167. CallParentFunction(new KeyValues("KeyCodeTyped", "code", code));
  168. return;
  169. }
  170. BaseClass::OnKeyCodeTyped( code );
  171. }
  172. virtual void OnMenuItemSelected()
  173. {
  174. BaseClass::OnMenuItemSelected();
  175. PostMessage( GetVParent(), new KeyValues( "OnMatchStringSelected" ) );
  176. }
  177. };
  178. } // namespace vgui
  179. //-----------------------------------------------------------------------------
  180. // Dictionary of start dir contexts
  181. //-----------------------------------------------------------------------------
  182. static CUtlDict< CUtlString, unsigned short > s_StartDirContexts;
  183. struct ColumnInfo_t
  184. {
  185. char const *columnName;
  186. char const *columnText;
  187. int startingWidth;
  188. int minWidth;
  189. int maxWidth;
  190. int flags;
  191. SortFunc *pfnSort;
  192. Label::Alignment alignment;
  193. };
  194. static ColumnInfo_t g_ColInfo[] =
  195. {
  196. { "text", "#FileOpenDialog_Col_Name", 175, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileNameSortFunc , Label::a_west },
  197. { "filesize", "#FileOpenDialog_Col_Size", 100, 20, 10000, 0, &ListFileSizeSortFunc , Label::a_east },
  198. { "type", "#FileOpenDialog_Col_Type", 150, 20, 10000, 0, &ListFileTypeSortFunc , Label::a_west },
  199. { "modified", "#FileOpenDialog_Col_DateModified", 125, 20, 10000, 0, &ListFileModifiedSortFunc , Label::a_west },
  200. // { "created", "#FileOpenDialog_Col_DateCreated", 125, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileCreatedSortFunc , Label::a_west },
  201. { "attributes", "#FileOpenDialog_Col_Attributes", 50, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileAttributesSortFunc , Label::a_west },
  202. };
  203. //-----------------------------------------------------------------------------
  204. // Purpose: Constructor
  205. //-----------------------------------------------------------------------------
  206. FileOpenDialog::FileOpenDialog(Panel *parent, const char *title, bool bOpenOnly, KeyValues* pContextKeyValues ) :
  207. Frame( parent, "FileOpenDialog" )
  208. {
  209. m_DialogType = bOpenOnly ? FOD_OPEN : FOD_SAVE;
  210. Init( title, pContextKeyValues );
  211. }
  212. FileOpenDialog::FileOpenDialog( Panel *parent, const char *title, FileOpenDialogType_t type, KeyValues *pContextKeyValues ) :
  213. Frame( parent, "FileOpenDialog" )
  214. {
  215. m_DialogType = type;
  216. Init( title, pContextKeyValues );
  217. }
  218. void FileOpenDialog::Init( const char *title, KeyValues *pContextKeyValues )
  219. {
  220. // By default, delete self on close
  221. SetDeleteSelfOnClose( true );
  222. m_bFileSelected = false;
  223. SetTitle(title, true);
  224. SetMinimizeButtonVisible(false);
  225. #ifdef POSIX
  226. V_strncpy(m_szLastPath, "/", sizeof( m_szLastPath ) );
  227. #else
  228. V_strncpy(m_szLastPath, "c:\\", sizeof( m_szLastPath ) );
  229. #endif
  230. m_pContextKeyValues = pContextKeyValues;
  231. // Get the list of available drives and put them in a menu here.
  232. // Start with the directory we are in.
  233. m_pFullPathEdit = new ComboBox(this, "FullPathEdit", 6, false);
  234. m_pFullPathEdit->GetTooltip()->SetTooltipFormatToSingleLine();
  235. // list panel
  236. m_pFileList = new ListPanel(this, "FileList");
  237. for ( int i = 0; i < ARRAYSIZE( g_ColInfo ); ++i )
  238. {
  239. const ColumnInfo_t& info = g_ColInfo[ i ];
  240. m_pFileList->AddColumnHeader( i, info.columnName, info.columnText, info.startingWidth, info.minWidth, info.maxWidth, info.flags );
  241. m_pFileList->SetSortFunc( i, info.pfnSort );
  242. m_pFileList->SetColumnTextAlignment( i, info.alignment );
  243. }
  244. m_pFileList->SetSortColumn( s_nLastSortColumn );
  245. m_pFileList->SetMultiselectEnabled( false );
  246. // file name edit box
  247. m_pFileNameCombo = new FileNameComboBox(this, "FileNameCombo", 6, true );
  248. m_pFileTypeCombo = new ComboBox( this, "FileTypeCombo", 6, false );
  249. switch ( m_DialogType )
  250. {
  251. case FOD_OPEN:
  252. m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this );
  253. break;
  254. case FOD_SAVE:
  255. m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Save", this );
  256. break;
  257. case FOD_SELECT_DIRECTORY:
  258. m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Select", this );
  259. m_pFileTypeCombo->SetVisible( false );
  260. break;
  261. }
  262. m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this );
  263. m_pFolderUpButton = new Button( this, "FolderUpButton", "", this );
  264. m_pFolderUpButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_Up" );
  265. m_pNewFolderButton = new Button( this, "NewFolderButton", "", this );
  266. m_pNewFolderButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_NewFolder" );
  267. m_pOpenInExplorerButton = new Button( this, "OpenInExplorerButton", "", this );
  268. #if defined ( OSX )
  269. m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInFinderButton" );
  270. #elif defined ( POSIX )
  271. m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInDesktopManagerButton" );
  272. #else // Assume Windows / Explorer
  273. m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInExplorerButton" );
  274. #endif
  275. Label *lookIn = new Label( this, "LookInLabel", "#FileOpenDialog_Look_in" );
  276. Label *fileName = new Label( this, "FileNameLabel",
  277. ( m_DialogType != FOD_SELECT_DIRECTORY ) ? "#FileOpenDialog_File_name" : "#FileOpenDialog_Directory_Name" );
  278. // set up the control's initial positions
  279. SetSize( 600, 260 );
  280. int nFileEditLeftSide = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 84 : 100;
  281. int nFileNameWidth = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 72 : 82;
  282. m_pFullPathEdit->SetBounds(67, 32, 310, 24);
  283. m_pFolderUpButton->SetBounds(362, 32, 24, 24);
  284. m_pNewFolderButton->SetBounds(392, 32, 24, 24);
  285. m_pOpenInExplorerButton->SetBounds(332, 32, 24, 24);
  286. m_pFileList->SetBounds(10, 60, 406, 130);
  287. m_pFileNameCombo->SetBounds( nFileEditLeftSide, 194, 238, 24);
  288. m_pFileTypeCombo->SetBounds( nFileEditLeftSide, 224, 238, 24);
  289. m_pOpenButton->SetBounds(336, 194, 74, 24);
  290. m_pCancelButton->SetBounds(336, 224, 74, 24);
  291. lookIn->SetBounds(10, 32, 55, 24);
  292. fileName->SetBounds(10, 194, nFileNameWidth, 24);
  293. // set autolayout parameters
  294. m_pFullPathEdit->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_RIGHT, 67, 32, -100, 0 );
  295. m_pFileNameCombo->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -42, -104, 0 );
  296. m_pFileTypeCombo->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -12, -104, 0 );
  297. m_pFileList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 10, 60, -10, -70 );
  298. m_pFolderUpButton->SetPinCorner( Panel::PIN_TOPRIGHT, -40, 32 );
  299. m_pNewFolderButton->SetPinCorner( Panel::PIN_TOPRIGHT, -10, 32 );
  300. m_pOpenInExplorerButton->SetPinCorner( Panel::PIN_TOPRIGHT, -70, 32 );
  301. m_pOpenButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -42 );
  302. m_pCancelButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -12 );
  303. lookIn->SetPinCorner( Panel::PIN_TOPLEFT, 10, 32 );
  304. fileName->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -42 );
  305. // label settings
  306. lookIn->SetContentAlignment(Label::a_west);
  307. fileName->SetContentAlignment(Label::a_west);
  308. lookIn->SetAssociatedControl(m_pFullPathEdit);
  309. fileName->SetAssociatedControl(m_pFileNameCombo);
  310. if ( m_DialogType != FOD_SELECT_DIRECTORY )
  311. {
  312. Label *fileType = new Label(this, "FileTypeLabel", "#FileOpenDialog_File_type");
  313. fileType->SetBounds(10, 224, 72, 24);
  314. fileType->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -12 );
  315. fileType->SetContentAlignment(Label::a_west);
  316. fileType->SetAssociatedControl( m_pFileTypeCombo );
  317. }
  318. // set tab positions
  319. GetFocusNavGroup().SetDefaultButton(m_pOpenButton);
  320. m_pFileNameCombo->SetTabPosition(1);
  321. m_pFileTypeCombo->SetTabPosition(2);
  322. m_pOpenButton->SetTabPosition(3);
  323. m_pCancelButton->SetTabPosition(4);
  324. m_pFullPathEdit->SetTabPosition(5);
  325. m_pFileList->SetTabPosition(6);
  326. m_pOpenButton->SetCommand( ( m_DialogType != FOD_SELECT_DIRECTORY ) ? new KeyValues( "OnOpen" ) : new KeyValues( "SelectFolder" ) );
  327. m_pCancelButton->SetCommand( "CloseModal" );
  328. m_pFolderUpButton->SetCommand( new KeyValues( "OnFolderUp" ) );
  329. m_pNewFolderButton->SetCommand( new KeyValues( "OnNewFolder" ) );
  330. m_pOpenInExplorerButton->SetCommand( new KeyValues( "OpenInExplorer" ) );
  331. SetSize( 600, 384 );
  332. m_nStartDirContext = s_StartDirContexts.InvalidIndex();
  333. // Set our starting path to the current directory
  334. char pLocalPath[255];
  335. g_pFullFileSystem->GetCurrentDirectory( pLocalPath , 255 );
  336. SetStartDirectory( pLocalPath );
  337. // Because these call through virtual functions, we can't issue them in the constructor, so we post a message to ourselves instead!!
  338. PostMessage( GetVPanel(), new KeyValues( "PopulateFileList" ) );
  339. PostMessage( GetVPanel(), new KeyValues( "PopulateDriveList" ) );
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose: Destructor
  343. //-----------------------------------------------------------------------------
  344. FileOpenDialog::~FileOpenDialog()
  345. {
  346. s_nLastSortColumn = m_pFileList->GetSortColumn();
  347. if ( m_pContextKeyValues )
  348. {
  349. m_pContextKeyValues->deleteThis();
  350. m_pContextKeyValues = NULL;
  351. }
  352. }
  353. //-----------------------------------------------------------------------------
  354. // Purpose: Apply scheme settings
  355. //-----------------------------------------------------------------------------
  356. void FileOpenDialog::ApplySchemeSettings(IScheme *pScheme)
  357. {
  358. BaseClass::ApplySchemeSettings(pScheme);
  359. m_pFolderUpButton->SetImage( scheme()->GetImage("resource/icon_folderup", false), 0 );
  360. m_pFolderUpButton->SetContentAlignment( Label::a_center );
  361. m_pNewFolderButton->SetImage( scheme()->GetImage("resource/icon_newfolder", false), 0 );
  362. m_pNewFolderButton->SetContentAlignment( Label::a_center );
  363. m_pOpenInExplorerButton->SetImage( scheme()->GetImage("resource/icon_explore", false), 0 );
  364. m_pOpenInExplorerButton->SetContentAlignment( Label::a_center );
  365. ImageList *imageList = new ImageList(false);
  366. imageList->AddImage(scheme()->GetImage("resource/icon_file", false));
  367. imageList->AddImage(scheme()->GetImage("resource/icon_folder", false));
  368. imageList->AddImage(scheme()->GetImage("resource/icon_folder_selected", false));
  369. m_pFileList->SetImageList(imageList, true);
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Prevent default button ('select') from getting triggered
  373. // when selecting directories. Instead, open the directory
  374. //-----------------------------------------------------------------------------
  375. void FileOpenDialog::OnKeyCodeTyped(KeyCode code)
  376. {
  377. if ( m_DialogType == FOD_SELECT_DIRECTORY && code == KEY_ENTER )
  378. {
  379. OnOpen();
  380. }
  381. else
  382. {
  383. BaseClass::OnKeyCodeTyped( code );
  384. }
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Purpose:
  388. //-----------------------------------------------------------------------------
  389. void FileOpenDialog::PopulateDriveList()
  390. {
  391. char fullpath[MAX_PATH * 4];
  392. char subDirPath[MAX_PATH * 4];
  393. GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH);
  394. Q_strncpy(subDirPath, fullpath, sizeof( subDirPath ) );
  395. m_pFullPathEdit->DeleteAllItems();
  396. // populate the drive list
  397. char buf[512];
  398. int len = system()->GetAvailableDrives(buf, 512);
  399. char *pBuf = buf;
  400. for (int i=0; i < len / 4; i++)
  401. {
  402. m_pFullPathEdit->AddItem(pBuf, NULL);
  403. // is this our drive - add all subdirectories
  404. if (!_strnicmp(pBuf, fullpath, 2))
  405. {
  406. int indent = 0;
  407. char *pData = fullpath;
  408. while (*pData)
  409. {
  410. if (*pData == CORRECT_PATH_SEPARATOR )
  411. {
  412. if (indent > 0)
  413. {
  414. memset(subDirPath, ' ', indent);
  415. memcpy(subDirPath+indent, fullpath, pData-fullpath);
  416. subDirPath[indent+pData-fullpath] = 0;
  417. m_pFullPathEdit->AddItem(subDirPath, NULL);
  418. }
  419. indent += 2;
  420. }
  421. pData++;
  422. }
  423. }
  424. pBuf += 4;
  425. }
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Purpose: Delete self on close
  429. //-----------------------------------------------------------------------------
  430. void FileOpenDialog::OnClose()
  431. {
  432. s_nLastSortColumn = m_pFileList->GetSortColumn();
  433. if ( !m_bFileSelected )
  434. {
  435. KeyValues *pKeyValues = new KeyValues( "FileSelectionCancelled" );
  436. PostActionSignal( pKeyValues );
  437. m_bFileSelected = true;
  438. }
  439. m_pFileNameCombo->SetText("");
  440. m_pFileNameCombo->HideMenu();
  441. if ( vgui::input()->GetAppModalSurface() == GetVPanel() )
  442. {
  443. input()->SetAppModalSurface(NULL);
  444. }
  445. BaseClass::OnClose();
  446. }
  447. void FileOpenDialog::OnFolderUp()
  448. {
  449. MoveUpFolder();
  450. OnOpen();
  451. }
  452. void FileOpenDialog::OnInputCompleted( KeyValues *data )
  453. {
  454. if ( m_hInputDialog.Get() )
  455. {
  456. delete m_hInputDialog.Get();
  457. }
  458. input()->SetAppModalSurface( m_SaveModal );
  459. m_SaveModal = 0;
  460. NewFolder( data->GetString( "text" ) );
  461. OnOpen();
  462. }
  463. void FileOpenDialog::OnInputCanceled()
  464. {
  465. input()->SetAppModalSurface( m_SaveModal );
  466. m_SaveModal = 0;
  467. }
  468. void FileOpenDialog::OnNewFolder()
  469. {
  470. if ( m_hInputDialog.Get() )
  471. delete m_hInputDialog.Get();
  472. m_hInputDialog = new InputDialog( this, "#FileOpenDialog_NewFolder_InputTitle", "#FileOpenDialog_NewFolderPrompt", "#FileOpenDialog_NewFolder_DefaultName" );
  473. if ( m_hInputDialog.Get() )
  474. {
  475. m_SaveModal = input()->GetAppModalSurface();
  476. KeyValues *pContextKeyValues = new KeyValues( "NewFolder" );
  477. m_hInputDialog->SetSmallCaption( true );
  478. m_hInputDialog->SetMultiline( false );
  479. m_hInputDialog->DoModal( pContextKeyValues );
  480. }
  481. }
  482. //-----------------------------------------------------------------------------
  483. // Opens the current file/folder in explorer
  484. //-----------------------------------------------------------------------------
  485. void FileOpenDialog::OnOpenInExplorer()
  486. {
  487. char pCurrentDirectory[MAX_PATH];
  488. GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) );
  489. #if defined( WIN32 )
  490. #if !defined( _GAMECONSOLE )
  491. ShellExecute( NULL, NULL, pCurrentDirectory, NULL, NULL, SW_SHOWNORMAL );
  492. #endif
  493. #elif defined( OSX )
  494. char szCmd[ MAX_PATH ];
  495. Q_snprintf( szCmd, sizeof(szCmd), "/usr/bin/open \"%s\"", pCurrentDirectory );
  496. ::system( szCmd );
  497. #elif defined( LINUX )
  498. DevMsg( "FileOpenDialog::OnOpenInExplorer unimplemented under LINUX\n" );
  499. #endif
  500. }
  501. void FileOpenDialog::AddSearchHistoryString( char const *str )
  502. {
  503. // See if it's already in list
  504. for ( int i = 0; i < m_SearchHistory.Count(); ++i )
  505. {
  506. if ( !Q_stricmp( str, m_SearchHistory[ i ].String() ) )
  507. return;
  508. }
  509. while ( m_SearchHistory.Count() > MAX_SEARCH_HISTORY )
  510. {
  511. m_SearchHistory.Remove( m_SearchHistory.Count() - 1 );
  512. }
  513. CUtlString string;
  514. string = str;
  515. m_SearchHistory.AddToTail( string );
  516. PopulateFileNameSearchHistory();
  517. }
  518. //-----------------------------------------------------------------------------
  519. // Purpose: Handle for button commands
  520. //-----------------------------------------------------------------------------
  521. void FileOpenDialog::OnCommand(const char *command)
  522. {
  523. if (!stricmp(command, "Cancel"))
  524. {
  525. Close();
  526. }
  527. else
  528. {
  529. BaseClass::OnCommand(command);
  530. }
  531. }
  532. //-----------------------------------------------------------------------------
  533. // Sets the start directory context (and resets the start directory in the process)
  534. //-----------------------------------------------------------------------------
  535. void FileOpenDialog::SetStartDirectoryContext( const char *pStartDirContext, const char *pDefaultDir )
  536. {
  537. bool bUseCurrentDirectory = true;
  538. if ( pStartDirContext )
  539. {
  540. m_nStartDirContext = s_StartDirContexts.Find( pStartDirContext );
  541. if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() )
  542. {
  543. m_nStartDirContext = s_StartDirContexts.Insert( pStartDirContext, pDefaultDir );
  544. bUseCurrentDirectory = ( pDefaultDir == NULL );
  545. }
  546. else
  547. {
  548. bUseCurrentDirectory = false;
  549. }
  550. }
  551. else
  552. {
  553. m_nStartDirContext = s_StartDirContexts.InvalidIndex();
  554. }
  555. if ( !bUseCurrentDirectory )
  556. {
  557. SetStartDirectory( s_StartDirContexts[m_nStartDirContext].Get() );
  558. }
  559. else
  560. {
  561. // Set our starting path to the current directory
  562. char pLocalPath[255];
  563. g_pFullFileSystem->GetCurrentDirectory( pLocalPath, 255 );
  564. SetStartDirectory( pLocalPath );
  565. }
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Purpose: Set the starting directory of the file search.
  569. //-----------------------------------------------------------------------------
  570. void FileOpenDialog::SetStartDirectory( const char *dir )
  571. {
  572. m_pFullPathEdit->SetText(dir);
  573. // ensure it's validity
  574. ValidatePath();
  575. // Store this in the start directory list
  576. if ( m_nStartDirContext != s_StartDirContexts.InvalidIndex() )
  577. {
  578. char pDirBuf[MAX_PATH];
  579. GetCurrentDirectory( pDirBuf, sizeof(pDirBuf) );
  580. s_StartDirContexts[ m_nStartDirContext ] = pDirBuf;
  581. }
  582. PopulateDriveList();
  583. }
  584. //-----------------------------------------------------------------------------
  585. // Purpose: Add filters for the drop down combo box
  586. //-----------------------------------------------------------------------------
  587. void FileOpenDialog::AddFilter( const char *filter, const char *filterName, bool bActive, const char *pFilterInfo )
  588. {
  589. KeyValues *kv = new KeyValues("item");
  590. kv->SetString( "filter", filter );
  591. kv->SetString( "filterinfo", pFilterInfo );
  592. int itemID = m_pFileTypeCombo->AddItem(filterName, kv);
  593. if ( bActive )
  594. {
  595. m_pFileTypeCombo->ActivateItem(itemID);
  596. }
  597. }
  598. //-----------------------------------------------------------------------------
  599. // Purpose: Activate the dialog
  600. //-----------------------------------------------------------------------------
  601. void FileOpenDialog::DoModal( bool bUnused )
  602. {
  603. m_bFileSelected = false;
  604. m_pFileNameCombo->RequestFocus();
  605. BaseClass::DoModal();
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Purpose: Gets the directory this is currently in
  609. //-----------------------------------------------------------------------------
  610. void FileOpenDialog::GetCurrentDirectory(char *buf, int bufSize)
  611. {
  612. // get the text from the text entry
  613. m_pFullPathEdit->GetText(buf, bufSize);
  614. }
  615. //-----------------------------------------------------------------------------
  616. // Purpose: Get the last selected file name
  617. //-----------------------------------------------------------------------------
  618. void FileOpenDialog::GetSelectedFileName(char *buf, int bufSize)
  619. {
  620. m_pFileNameCombo->GetText(buf, bufSize);
  621. }
  622. //-----------------------------------------------------------------------------
  623. // Creates a new folder
  624. //-----------------------------------------------------------------------------
  625. void FileOpenDialog::NewFolder( char const *folderName )
  626. {
  627. char pCurrentDirectory[MAX_PATH];
  628. GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) );
  629. char pFullPath[MAX_PATH];
  630. char pNewFolderName[MAX_PATH];
  631. Q_strncpy( pNewFolderName, folderName, sizeof(pNewFolderName) );
  632. int i = 2;
  633. do
  634. {
  635. Q_MakeAbsolutePath( pFullPath, sizeof(pFullPath), pNewFolderName, pCurrentDirectory );
  636. if ( !g_pFullFileSystem->FileExists( pFullPath, NULL ) &&
  637. !g_pFullFileSystem->IsDirectory( pFullPath, NULL ) )
  638. {
  639. g_pFullFileSystem->CreateDirHierarchy( pFullPath, NULL );
  640. m_pFileNameCombo->SetText( pNewFolderName );
  641. return;
  642. }
  643. Q_snprintf( pNewFolderName, sizeof(pNewFolderName), "%s%d", folderName, i );
  644. ++i;
  645. } while ( i <= 999 );
  646. }
  647. //-----------------------------------------------------------------------------
  648. // Purpose: Move the directory structure up
  649. //-----------------------------------------------------------------------------
  650. void FileOpenDialog::MoveUpFolder()
  651. {
  652. char fullpath[MAX_PATH * 4];
  653. GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH);
  654. // strip it back
  655. char *pos = strrchr(fullpath, CORRECT_PATH_SEPARATOR );
  656. if (pos)
  657. {
  658. *pos = 0;
  659. if (!pos[1])
  660. {
  661. pos = strrchr(fullpath, CORRECT_PATH_SEPARATOR );
  662. if (pos)
  663. {
  664. *pos = 0;
  665. }
  666. }
  667. }
  668. // append a trailing slash
  669. Q_strncat(fullpath, CORRECT_PATH_SEPARATOR_S, sizeof( fullpath ), COPY_ALL_CHARACTERS );
  670. SetStartDirectory(fullpath);
  671. PopulateFileList();
  672. InvalidateLayout();
  673. Repaint();
  674. }
  675. //-----------------------------------------------------------------------------
  676. // Purpose: Validate that the current path is valid
  677. //-----------------------------------------------------------------------------
  678. void FileOpenDialog::ValidatePath()
  679. {
  680. char fullpath[MAX_PATH * 4];
  681. GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH);
  682. Q_RemoveDotSlashes( fullpath );
  683. // when statting a directory on Windows, you want to include
  684. // the terminal slash exactly when you are statting a root
  685. // directory. PKMN.
  686. #ifdef _WIN32
  687. if ( Q_strlen( fullpath ) != 3 )
  688. {
  689. Q_StripTrailingSlash( fullpath );
  690. }
  691. #endif
  692. struct _stat buf;
  693. if ( ( 0 == _stat( fullpath, &buf ) ) &&
  694. ( 0 != ( buf.st_mode & S_IFDIR ) ) )
  695. {
  696. Q_AppendSlash( fullpath, sizeof( fullpath ) );
  697. Q_strncpy(m_szLastPath, fullpath, sizeof(m_szLastPath));
  698. }
  699. else
  700. {
  701. // failed to load file, use the previously successful path
  702. }
  703. m_pFullPathEdit->SetText(m_szLastPath);
  704. m_pFullPathEdit->GetTooltip()->SetText(m_szLastPath);
  705. }
  706. static void InitFileData( bool bDirectory, char const *pszFileName, const char *pchDirectoryName, FileData_t &data )
  707. {
  708. data.m_FileName = V_UnqualifiedFileName( pszFileName );
  709. data.m_FullPath = CFmtStr( "%s%s", pchDirectoryName, pszFileName );
  710. Q_FixSlashes( data.m_FullPath.Get() );
  711. if ( !bDirectory )
  712. {
  713. g_pFullFileSystem->GetFileTypeForFullPath( data.m_FullPath, data.m_FileType, sizeof( data.m_FileType ) );
  714. }
  715. data.m_bDirectory = bDirectory;
  716. data.m_nFileSize = g_pFullFileSystem->Size( data.m_FullPath.Get() );
  717. if ( !g_pFullFileSystem->IsFileWritable( data.m_FullPath.Get() ) )
  718. data.m_FileAttributes = "R";
  719. long fileModified = g_pFullFileSystem->GetFileTime( data.m_FullPath.Get() );
  720. char pszFileModified[64];
  721. g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified );
  722. data.m_LastWriteTime = pszFileModified;
  723. data.m_nLastWriteTime = fileModified;
  724. }
  725. void FileData_t::PrepareKV( KeyValues *kv )
  726. {
  727. // add the file to the list
  728. kv->SetString("text", m_FileName );
  729. kv->SetInt("directory", m_bDirectory ? 1 : 0 );
  730. kv->SetInt("image", m_bDirectory ? 2 : 1 );
  731. kv->SetInt("imageSelected", m_bDirectory ? 3 : 1 );
  732. kv->SetPtr( "iconImage", NULL );
  733. if ( !m_bDirectory )
  734. {
  735. IImage *image = surface()->GetIconImageForFullPath( m_FullPath.String() );
  736. if ( image )
  737. {
  738. kv->SetPtr( "iconImage", (void *)image );
  739. }
  740. kv->SetUint64( "filesizeint", (uint64)m_nFileSize );
  741. kv->SetString( "filesize", Q_pretifymem( (float)m_nFileSize, 0, true ) );
  742. kv->SetWString( "type", m_FileType );
  743. }
  744. else
  745. {
  746. kv->SetUint64( "filesizeint", (uint64)0 );
  747. kv->SetString( "filesize", "" );
  748. kv->SetString( "type", "#FileOpenDialog_FileType_Folder" );
  749. }
  750. kv->SetString( "attributes", m_FileAttributes );
  751. kv->SetString( "modified", m_LastWriteTime );
  752. kv->SetString( "created", m_CreationTime );
  753. kv->SetUint64( "modifiedint", m_nLastWriteTime );
  754. kv->SetUint64( "createdint", m_nCreationTime );
  755. }
  756. void FileOpenDialog::BuildFileList()
  757. {
  758. m_Files.RemoveAll();
  759. m_Filtered.RemoveAll();
  760. #ifndef _GAMECONSOLE
  761. // get the current directory
  762. char currentDir[MAX_PATH * 4];
  763. char dir[MAX_PATH * 4];
  764. char filterList[MAX_FILTER_LENGTH+1];
  765. GetCurrentDirectory(currentDir, sizeof(currentDir));
  766. FileFindHandle_t findHandle;
  767. KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData();
  768. if (combokv)
  769. {
  770. Q_strncpy(filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH);
  771. }
  772. else
  773. {
  774. // add wildcard for search
  775. Q_strncpy(filterList, "*\0", MAX_FILTER_LENGTH);
  776. }
  777. char *filterPtr = filterList;
  778. //KeyValues *kv = new KeyValues("item");
  779. if ( m_DialogType != FOD_SELECT_DIRECTORY )
  780. {
  781. while ((filterPtr != NULL) && (*filterPtr != 0))
  782. {
  783. // parse the next filter in the list.
  784. char curFilter[MAX_FILTER_LENGTH];
  785. curFilter[0] = 0;
  786. int i = 0;
  787. while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' ')))
  788. {
  789. ++filterPtr;
  790. }
  791. while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' '))
  792. {
  793. curFilter[i++] = *(filterPtr++);
  794. }
  795. curFilter[i] = 0;
  796. if (curFilter[0] == 0)
  797. {
  798. break;
  799. }
  800. Q_snprintf( dir, MAX_PATH*4, "%s%s", currentDir, curFilter );
  801. // Open the directory and walk it, loading files
  802. const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle );
  803. while ( pszFileName )
  804. {
  805. if ( !g_pFullFileSystem->FindIsDirectory( findHandle ) )
  806. {
  807. FileData_t &fd = m_Files[ m_Files.AddToTail() ];
  808. InitFileData( false, pszFileName, currentDir, fd );
  809. }
  810. pszFileName = g_pFullFileSystem->FindNext( findHandle );
  811. }
  812. g_pFullFileSystem->FindClose( findHandle );
  813. }
  814. }
  815. // find all the directories
  816. GetCurrentDirectory(currentDir, sizeof(currentDir));
  817. Q_snprintf( dir, MAX_PATH*4, "%s*", currentDir );
  818. const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle );
  819. while ( pszFileName )
  820. {
  821. if ( pszFileName[0] != '.' && g_pFullFileSystem->FindIsDirectory( findHandle ) )
  822. {
  823. FileData_t &fd = m_Files[ m_Files.AddToTail() ];
  824. InitFileData( true, pszFileName, currentDir, fd );
  825. }
  826. pszFileName = g_pFullFileSystem->FindNext( findHandle );
  827. }
  828. g_pFullFileSystem->FindClose( findHandle );
  829. #endif
  830. }
  831. // Static method to do wildcard matching for *, ? and . characters
  832. bool FileOpenDialog::FileNameWildCardMatch( char const *string, char const *pattern )
  833. {
  834. for (;; ++string)
  835. {
  836. char stringc=toupper(*string);
  837. char patternc=toupper(*pattern++);
  838. switch (patternc)
  839. {
  840. case 0:
  841. return(stringc==0);
  842. case '?':
  843. if (stringc == 0)
  844. return(false);
  845. break;
  846. case '*':
  847. if (*pattern==0)
  848. return(true);
  849. if (*pattern=='.')
  850. {
  851. if (pattern[1]=='*' && pattern[2]==0)
  852. return(true);
  853. const char *dot=strchr(string,'.');
  854. if (pattern[1]==0)
  855. return (dot==NULL || dot[1]==0);
  856. if (dot!=NULL)
  857. {
  858. string=dot;
  859. if (strpbrk(pattern,"*?")==NULL && strchr(string+1,'.')==NULL)
  860. return(Q_stricmp(pattern+1,string+1)==0);
  861. }
  862. }
  863. while (*string)
  864. if (FileNameWildCardMatch(string++, pattern))
  865. return(true);
  866. return(false);
  867. default:
  868. if (patternc != stringc)
  869. {
  870. if (patternc=='.' && stringc==0)
  871. return(FileNameWildCardMatch(string, pattern ));
  872. else
  873. return(false);
  874. }
  875. break;
  876. }
  877. }
  878. }
  879. bool FileOpenDialog::PassesFilter( FileData_t *fd )
  880. {
  881. // Do the substring filtering
  882. if ( fd->m_bDirectory )
  883. return true;
  884. // Never filter Save... dialogs
  885. if ( m_DialogType == FOD_SAVE )
  886. return true;
  887. if ( m_CurrentSubstringFilter.Length() <= 0 )
  888. return true;
  889. if ( Q_stristr( fd->m_FileName, m_CurrentSubstringFilter.String() ) )
  890. return true;
  891. if ( FileNameWildCardMatch( fd->m_FileName, m_CurrentSubstringFilter.String() ) )
  892. return true;
  893. return false;
  894. }
  895. void FileOpenDialog::FilterFileList()
  896. {
  897. m_Filtered.RemoveAll();
  898. for ( int i = 0; i < m_Files.Count(); ++i )
  899. {
  900. // Apply filter
  901. FileData_t *pFD = &m_Files[ i ];
  902. if ( PassesFilter( pFD ) )
  903. {
  904. m_Filtered.AddToTail( &m_Files[ i ] );
  905. }
  906. }
  907. // clear the current list
  908. m_pFileList->DeleteAllItems();
  909. KeyValues *kv = new KeyValues("item");
  910. for ( int i = 0; i < m_Filtered.Count(); ++i )
  911. {
  912. FileData_t *fd = m_Filtered[ i ];
  913. fd->PrepareKV( kv );
  914. m_pFileList->AddItem(kv, 0, false, false);
  915. }
  916. kv->deleteThis();
  917. m_pFileList->SortList();
  918. }
  919. int FileOpenDialog::CountSubstringMatches()
  920. {
  921. int nMatches = 0;
  922. for ( int i = 0; i < m_Files.Count(); ++i )
  923. {
  924. // Apply filter
  925. FileData_t *pFD = &m_Files[ i ];
  926. if ( PassesFilter( pFD ) )
  927. {
  928. if ( !pFD->m_bDirectory )
  929. {
  930. ++nMatches;
  931. }
  932. }
  933. }
  934. return nMatches;
  935. }
  936. //-----------------------------------------------------------------------------
  937. // Purpose: Fill the filelist with the names of all the files in the current directory
  938. //-----------------------------------------------------------------------------
  939. void FileOpenDialog::PopulateFileList()
  940. {
  941. BuildFileList();
  942. FilterFileList();
  943. }
  944. //-----------------------------------------------------------------------------
  945. // Does the specified extension match something in the filter list?
  946. //-----------------------------------------------------------------------------
  947. bool FileOpenDialog::ExtensionMatchesFilter( const char *pExt )
  948. {
  949. KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData();
  950. if ( !combokv )
  951. return true;
  952. char filterList[MAX_FILTER_LENGTH+1];
  953. Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH );
  954. char *filterPtr = filterList;
  955. while ((filterPtr != NULL) && (*filterPtr != 0))
  956. {
  957. // parse the next filter in the list.
  958. char curFilter[MAX_FILTER_LENGTH];
  959. curFilter[0] = 0;
  960. int i = 0;
  961. while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' ')))
  962. {
  963. ++filterPtr;
  964. }
  965. while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' '))
  966. {
  967. curFilter[i++] = *(filterPtr++);
  968. }
  969. curFilter[i] = 0;
  970. if (curFilter[0] == 0)
  971. break;
  972. if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) )
  973. return true;
  974. // FIXME: This isn't exactly right, but tough cookies;
  975. // it assumes the first two characters of the filter are *.
  976. Assert( curFilter[0] == '*' && curFilter[1] == '.' );
  977. if ( !Q_stricmp( &curFilter[2], pExt ) )
  978. return true;
  979. }
  980. return false;
  981. }
  982. //-----------------------------------------------------------------------------
  983. // Choose the first non *.* filter in the filter list
  984. //-----------------------------------------------------------------------------
  985. void FileOpenDialog::ChooseExtension( char *pExt, int nBufLen )
  986. {
  987. pExt[0] = 0;
  988. KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData();
  989. if ( !combokv )
  990. return;
  991. char filterList[MAX_FILTER_LENGTH+1];
  992. Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH );
  993. char *filterPtr = filterList;
  994. while ((filterPtr != NULL) && (*filterPtr != 0))
  995. {
  996. // parse the next filter in the list.
  997. char curFilter[MAX_FILTER_LENGTH];
  998. curFilter[0] = 0;
  999. int i = 0;
  1000. while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' ')))
  1001. {
  1002. ++filterPtr;
  1003. }
  1004. while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' '))
  1005. {
  1006. curFilter[i++] = *(filterPtr++);
  1007. }
  1008. curFilter[i] = 0;
  1009. if (curFilter[0] == 0)
  1010. break;
  1011. if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) )
  1012. continue;
  1013. // FIXME: This isn't exactly right, but tough cookies;
  1014. // it assumes the first two characters of the filter are *.
  1015. Assert( curFilter[0] == '*' && curFilter[1] == '.' );
  1016. Q_strncpy( pExt, &curFilter[1], nBufLen );
  1017. break;
  1018. }
  1019. }
  1020. //-----------------------------------------------------------------------------
  1021. // Saves the file to the start dir context
  1022. //-----------------------------------------------------------------------------
  1023. void FileOpenDialog::SaveFileToStartDirContext( const char *pFullPath )
  1024. {
  1025. if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() )
  1026. return;
  1027. char pPath[MAX_PATH];
  1028. pPath[0] = 0;
  1029. Q_ExtractFilePath( pFullPath, pPath, sizeof(pPath) );
  1030. s_StartDirContexts[ m_nStartDirContext ] = pPath;
  1031. }
  1032. //-----------------------------------------------------------------------------
  1033. // Posts a file selected message
  1034. //-----------------------------------------------------------------------------
  1035. void FileOpenDialog::PostFileSelectedMessage( const char *pFileName )
  1036. {
  1037. m_bFileSelected = true;
  1038. // open the file!
  1039. KeyValues *pKeyValues = new KeyValues( "FileSelected", "fullpath", pFileName );
  1040. KeyValues *pFilterKeys = m_pFileTypeCombo->GetActiveItemUserData();
  1041. const char *pFilterInfo = pFilterKeys ? pFilterKeys->GetString( "filterinfo", NULL ) : NULL;
  1042. if ( pFilterInfo )
  1043. {
  1044. pKeyValues->SetString( "filterinfo", pFilterInfo );
  1045. }
  1046. if ( m_pContextKeyValues )
  1047. {
  1048. pKeyValues->AddSubKey( m_pContextKeyValues );
  1049. m_pContextKeyValues = NULL;
  1050. }
  1051. PostActionSignal( pKeyValues );
  1052. CloseModal();
  1053. }
  1054. //-----------------------------------------------------------------------------
  1055. // Selects the current folder
  1056. //-----------------------------------------------------------------------------
  1057. void FileOpenDialog::OnSelectFolder()
  1058. {
  1059. ValidatePath();
  1060. // construct a file path
  1061. char pFileName[MAX_PATH];
  1062. GetSelectedFileName( pFileName, sizeof( pFileName ) );
  1063. Q_StripTrailingSlash( pFileName );
  1064. if ( !stricmp(pFileName, "..") )
  1065. {
  1066. MoveUpFolder();
  1067. // clear the name text
  1068. m_pFileNameCombo->SetText("");
  1069. return;
  1070. }
  1071. if ( !stricmp(pFileName, ".") )
  1072. {
  1073. // clear the name text
  1074. m_pFileNameCombo->SetText("");
  1075. return;
  1076. }
  1077. // Compute the full path
  1078. char pFullPath[MAX_PATH * 4];
  1079. if ( !Q_IsAbsolutePath( pFileName ) )
  1080. {
  1081. GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH);
  1082. strcat( pFullPath, pFileName );
  1083. if ( !pFileName[0] )
  1084. {
  1085. Q_StripTrailingSlash( pFullPath );
  1086. }
  1087. }
  1088. else
  1089. {
  1090. Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) );
  1091. }
  1092. if ( g_pFullFileSystem->FileExists( pFullPath ) )
  1093. {
  1094. // open the file!
  1095. SaveFileToStartDirContext( pFullPath );
  1096. PostFileSelectedMessage( pFullPath );
  1097. return;
  1098. }
  1099. PopulateDriveList();
  1100. PopulateFileList();
  1101. InvalidateLayout();
  1102. }
  1103. //-----------------------------------------------------------------------------
  1104. // Purpose: Handle the open button being pressed
  1105. // checks on what has changed and acts accordingly
  1106. //-----------------------------------------------------------------------------
  1107. void FileOpenDialog::OnOpen()
  1108. {
  1109. ValidatePath();
  1110. // construct a file path
  1111. char pFileName[MAX_PATH];
  1112. GetSelectedFileName( pFileName, sizeof( pFileName ) );
  1113. int nLen = Q_strlen( pFileName );
  1114. bool bSpecifiedDirectory = ( pFileName[nLen-1] == '/' || pFileName[nLen-1] == '\\' );
  1115. Q_StripTrailingSlash( pFileName );
  1116. if ( !stricmp(pFileName, "..") )
  1117. {
  1118. MoveUpFolder();
  1119. // clear the name text
  1120. m_pFileNameCombo->SetText("");
  1121. return;
  1122. }
  1123. if ( !stricmp(pFileName, ".") )
  1124. {
  1125. // clear the name text
  1126. m_pFileNameCombo->SetText("");
  1127. return;
  1128. }
  1129. // Compute the full path
  1130. char pFullPath[MAX_PATH * 4];
  1131. if ( !Q_IsAbsolutePath( pFileName ) )
  1132. {
  1133. GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH);
  1134. strcat(pFullPath, pFileName);
  1135. if ( !pFileName[0] )
  1136. {
  1137. Q_StripTrailingSlash( pFullPath );
  1138. }
  1139. }
  1140. else
  1141. {
  1142. Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) );
  1143. }
  1144. // If the name specified is a directory, then change directory
  1145. if ( g_pFullFileSystem->IsDirectory( pFullPath, NULL ) )
  1146. {
  1147. // it's a directory; change to the specified directory
  1148. if ( !bSpecifiedDirectory )
  1149. {
  1150. strcat( pFullPath , CORRECT_PATH_SEPARATOR_S );
  1151. }
  1152. SetStartDirectory( pFullPath );
  1153. // clear the name text
  1154. m_pFileNameCombo->SetText("");
  1155. m_pFileNameCombo->HideMenu();
  1156. m_CurrentSubstringFilter = "";
  1157. PopulateDriveList();
  1158. PopulateFileList();
  1159. InvalidateLayout();
  1160. return;
  1161. }
  1162. else if ( bSpecifiedDirectory )
  1163. {
  1164. PopulateDriveList();
  1165. PopulateFileList();
  1166. InvalidateLayout();
  1167. return;
  1168. }
  1169. m_CurrentSubstringFilter = pFileName;
  1170. AddSearchHistoryString( pFileName );
  1171. if ( m_DialogType != FOD_SAVE )
  1172. {
  1173. if ( m_CurrentSubstringFilter.Length() > 0 )
  1174. {
  1175. // It's ambiguous
  1176. int nMatches = CountSubstringMatches();
  1177. if ( nMatches >= 2 )
  1178. {
  1179. // Apply filter instead
  1180. FilterFileList();
  1181. return;
  1182. }
  1183. }
  1184. }
  1185. // Append suffix of the first filter that isn't *.*
  1186. char extension[512];
  1187. Q_ExtractFileExtension( pFullPath, extension, sizeof(extension) );
  1188. if ( !ExtensionMatchesFilter( extension ) )
  1189. {
  1190. ChooseExtension( extension, sizeof(extension) );
  1191. Q_SetExtension( pFullPath, extension, sizeof(pFullPath) );
  1192. }
  1193. if ( g_pFullFileSystem->FileExists( pFullPath ) )
  1194. {
  1195. // open the file!
  1196. SaveFileToStartDirContext( pFullPath );
  1197. PostFileSelectedMessage( pFullPath );
  1198. return;
  1199. }
  1200. // file not found
  1201. if ( ( m_DialogType == FOD_SAVE ) && pFileName[0] )
  1202. {
  1203. // open the file!
  1204. SaveFileToStartDirContext( pFullPath );
  1205. PostFileSelectedMessage( pFullPath );
  1206. return;
  1207. }
  1208. PopulateDriveList();
  1209. PopulateFileList();
  1210. InvalidateLayout();
  1211. }
  1212. //-----------------------------------------------------------------------------
  1213. // Purpose: using the file edit box as a prefix, create a menu of all possible files
  1214. //-----------------------------------------------------------------------------
  1215. void FileOpenDialog::PopulateFileNameSearchHistory()
  1216. {
  1217. m_pFileNameCombo->RemoveAll();
  1218. for ( int i = 0; i < m_SearchHistory.Count(); ++i )
  1219. {
  1220. m_pFileNameCombo->AddItem( m_SearchHistory[ i ].String(), 0 );
  1221. }
  1222. }
  1223. //-----------------------------------------------------------------------------
  1224. // Purpose: Handle an item in the list being selected
  1225. //-----------------------------------------------------------------------------
  1226. void FileOpenDialog::OnItemSelected()
  1227. {
  1228. // make sure only one item is selected
  1229. if (m_pFileList->GetSelectedItemsCount() != 1)
  1230. {
  1231. m_pFileNameCombo->SetText("");
  1232. }
  1233. else
  1234. {
  1235. // put the file name into the text edit box
  1236. KeyValues *data = m_pFileList->GetItem(m_pFileList->GetSelectedItem(0));
  1237. m_pFileNameCombo->SetText(data->GetString("text"));
  1238. }
  1239. InvalidateLayout();
  1240. }
  1241. void FileOpenDialog::OnMatchStringSelected()
  1242. {
  1243. char pFileName[MAX_PATH];
  1244. GetSelectedFileName( pFileName, sizeof( pFileName ) );
  1245. m_pFileNameCombo->GetText( pFileName, sizeof( pFileName ) );
  1246. m_CurrentSubstringFilter = pFileName;
  1247. // Redo filter
  1248. FilterFileList();
  1249. }
  1250. //-----------------------------------------------------------------------------
  1251. // Purpose: Handle an item in the Drive combo box being selected
  1252. //-----------------------------------------------------------------------------
  1253. void FileOpenDialog::OnTextChanged(KeyValues *kv)
  1254. {
  1255. Panel *pPanel = (Panel *) kv->GetPtr("panel", NULL);
  1256. // first check which control had its text changed!
  1257. if (pPanel == m_pFullPathEdit)
  1258. {
  1259. m_pFileNameCombo->HideMenu();
  1260. m_pFileNameCombo->SetText("");
  1261. OnOpen();
  1262. }
  1263. else if (pPanel == m_pFileNameCombo)
  1264. {
  1265. // Don't react to text being typed
  1266. }
  1267. else if (pPanel == m_pFileTypeCombo)
  1268. {
  1269. m_pFileNameCombo->HideMenu();
  1270. PopulateFileList();
  1271. }
  1272. }