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
1514 lines
42 KiB
//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose: Implementation of vgui generic open file dialog
|
|
//
|
|
// $NoKeywords: $
|
|
//===========================================================================//
|
|
|
|
|
|
#define PROTECTED_THINGS_DISABLE
|
|
|
|
#if defined( WIN32 )
|
|
#if !defined( _GAMECONSOLE )
|
|
#include "winlite.h"
|
|
#include <shellapi.h>
|
|
#endif
|
|
#elif defined( POSIX )
|
|
#include <stdlib.h>
|
|
#define _stat stat
|
|
#define _wcsnicmp wcsncmp
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
#undef GetCurrentDirectory
|
|
#include "filesystem.h"
|
|
#include <sys/stat.h>
|
|
|
|
#include "tier1/utldict.h"
|
|
#include "tier1/utlstring.h"
|
|
|
|
#include <vgui/IScheme.h>
|
|
#include <vgui/ISurface.h>
|
|
#include <vgui/ISystem.h>
|
|
#include <keyvalues.h>
|
|
#include <vgui/IVGui.h>
|
|
#include <vgui/ILocalize.h>
|
|
#include <vgui/IInput.h>
|
|
|
|
#include <vgui_controls/FileOpenDialog.h>
|
|
|
|
#include <vgui_controls/Button.h>
|
|
#include <vgui_controls/ComboBox.h>
|
|
#include <vgui_controls/InputDialog.h>
|
|
#include <vgui_controls/Label.h>
|
|
#include <vgui_controls/ListPanel.h>
|
|
#include <vgui_controls/TextEntry.h>
|
|
#include <vgui_controls/ImageList.h>
|
|
#include <vgui_controls/MenuItem.h>
|
|
#include <vgui_controls/Tooltip.h>
|
|
|
|
#if defined( _X360 )
|
|
#include "xbox/xbox_win32stubs.h"
|
|
#undef GetCurrentDirectory
|
|
#endif
|
|
|
|
#if defined( _PS3 )
|
|
#include "ps3/ps3_core.h"
|
|
#include "ps3/ps3_win32stubs.h"
|
|
#undef GetCurrentDirectory
|
|
#endif
|
|
|
|
#include "tier1/fmtstr.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include <tier0/memdbgon.h>
|
|
|
|
using namespace vgui;
|
|
|
|
static int s_nLastSortColumn = 0;
|
|
static const int MAX_FILTER_LENGTH = 255;
|
|
static const int MAX_SEARCH_HISTORY = 8;
|
|
|
|
static int ListFileNameSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
|
|
{
|
|
NOTE_UNUSED( pPanel );
|
|
|
|
bool dir1 = item1.kv->GetInt("directory") == 1;
|
|
bool dir2 = item2.kv->GetInt("directory") == 1;
|
|
|
|
// if they're both not directories of files, return if dir1 is a directory (before files)
|
|
if (dir1 != dir2)
|
|
{
|
|
return dir1 ? -1 : 1;
|
|
}
|
|
|
|
const char *string1 = item1.kv->GetString("text");
|
|
const char *string2 = item2.kv->GetString("text");
|
|
|
|
// YWB: Mimic windows behavior where filenames starting with numbers are sorted based on numeric part
|
|
int num1 = Q_atoi( string1 );
|
|
int num2 = Q_atoi( string2 );
|
|
|
|
if ( num1 != 0 &&
|
|
num2 != 0 )
|
|
{
|
|
if ( num1 < num2 )
|
|
return -1;
|
|
else if ( num1 > num2 )
|
|
return 1;
|
|
}
|
|
|
|
// Push numbers before everything else
|
|
if ( num1 != 0 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// Push numbers before everything else
|
|
if ( num2 != 0 )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return Q_stricmp( string1, string2 );
|
|
}
|
|
|
|
static int ListBaseStringSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
|
|
{
|
|
bool dir1 = item1.kv->GetInt("directory") == 1;
|
|
bool dir2 = item2.kv->GetInt("directory") == 1;
|
|
|
|
// if they're both not directories of files, return if dir1 is a directory (before files)
|
|
if (dir1 != dir2)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
const char *string1 = item1.kv->GetString(fieldName);
|
|
const char *string2 = item2.kv->GetString(fieldName);
|
|
int cval = Q_stricmp(string1, string2);
|
|
if ( cval == 0 )
|
|
{
|
|
// Use filename to break ties
|
|
return ListFileNameSortFunc( pPanel, item1, item2 );
|
|
}
|
|
|
|
return cval;
|
|
}
|
|
|
|
static int ListBaseInteger64SortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *fieldName )
|
|
{
|
|
bool dir1 = item1.kv->GetInt("directory") == 1;
|
|
bool dir2 = item2.kv->GetInt("directory") == 1;
|
|
|
|
// if they're both not directories of files, return if dir1 is a directory (before files)
|
|
if ( dir1 != dir2 )
|
|
{
|
|
return dir1 ? -1 : 1;
|
|
}
|
|
|
|
int64 n1 = (int64)item1.kv->GetUint64( fieldName );
|
|
int64 n2 = (int64)item2.kv->GetUint64( fieldName );
|
|
|
|
if ( n1 == n2 )
|
|
{
|
|
// Use filename to break ties
|
|
return ListFileNameSortFunc( pPanel, item1, item2 );
|
|
}
|
|
|
|
return ( n1 < n2 ) ? -1 : 1;
|
|
}
|
|
|
|
static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
|
|
{
|
|
return ListBaseInteger64SortFunc( pPanel, item1, item2, "filesizeint" );
|
|
}
|
|
|
|
static int ListFileModifiedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
|
|
{
|
|
// NOTE: Backward order to get most recent files first
|
|
return ListBaseInteger64SortFunc( pPanel, item2, item1, "modifiedint" );
|
|
}
|
|
static int ListFileCreatedSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
|
|
{
|
|
// NOTE: Backward order to get most recent files first
|
|
return ListBaseInteger64SortFunc( pPanel, item2, item1, "createdint" );
|
|
}
|
|
static int ListFileAttributesSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
|
|
{
|
|
return ListBaseStringSortFunc( pPanel, item1, item2, "attributes" );
|
|
}
|
|
static int ListFileTypeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 )
|
|
{
|
|
return ListBaseStringSortFunc( pPanel, item1, item2, "type" );
|
|
}
|
|
|
|
|
|
|
|
namespace vgui
|
|
{
|
|
|
|
class FileNameComboBox : public ComboBox
|
|
{
|
|
DECLARE_CLASS_SIMPLE( FileNameComboBox, ComboBox );
|
|
|
|
public:
|
|
FileNameComboBox(FileOpenDialog *parent, const char *panelName, int numLines, bool allowEdit) :
|
|
BaseClass( parent, panelName, numLines, allowEdit )
|
|
{
|
|
}
|
|
|
|
virtual void OnKeyCodeTyped( KeyCode code )
|
|
{
|
|
if ( code == KEY_ENTER && !IsDropdownVisible() )
|
|
{
|
|
// Post to parent
|
|
CallParentFunction(new KeyValues("KeyCodeTyped", "code", code));
|
|
return;
|
|
}
|
|
|
|
BaseClass::OnKeyCodeTyped( code );
|
|
}
|
|
|
|
virtual void OnMenuItemSelected()
|
|
{
|
|
BaseClass::OnMenuItemSelected();
|
|
PostMessage( GetVParent(), new KeyValues( "OnMatchStringSelected" ) );
|
|
}
|
|
};
|
|
|
|
} // namespace vgui
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Dictionary of start dir contexts
|
|
//-----------------------------------------------------------------------------
|
|
static CUtlDict< CUtlString, unsigned short > s_StartDirContexts;
|
|
|
|
struct ColumnInfo_t
|
|
{
|
|
char const *columnName;
|
|
char const *columnText;
|
|
int startingWidth;
|
|
int minWidth;
|
|
int maxWidth;
|
|
int flags;
|
|
SortFunc *pfnSort;
|
|
Label::Alignment alignment;
|
|
};
|
|
|
|
static ColumnInfo_t g_ColInfo[] =
|
|
{
|
|
{ "text", "#FileOpenDialog_Col_Name", 175, 20, 10000, ListPanel::COLUMN_UNHIDABLE, &ListFileNameSortFunc , Label::a_west },
|
|
{ "filesize", "#FileOpenDialog_Col_Size", 100, 20, 10000, 0, &ListFileSizeSortFunc , Label::a_east },
|
|
{ "type", "#FileOpenDialog_Col_Type", 150, 20, 10000, 0, &ListFileTypeSortFunc , Label::a_west },
|
|
{ "modified", "#FileOpenDialog_Col_DateModified", 125, 20, 10000, 0, &ListFileModifiedSortFunc , Label::a_west },
|
|
// { "created", "#FileOpenDialog_Col_DateCreated", 125, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileCreatedSortFunc , Label::a_west },
|
|
{ "attributes", "#FileOpenDialog_Col_Attributes", 50, 20, 10000, ListPanel::COLUMN_HIDDEN, &ListFileAttributesSortFunc , Label::a_west },
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
FileOpenDialog::FileOpenDialog(Panel *parent, const char *title, bool bOpenOnly, KeyValues* pContextKeyValues ) :
|
|
Frame( parent, "FileOpenDialog" )
|
|
{
|
|
m_DialogType = bOpenOnly ? FOD_OPEN : FOD_SAVE;
|
|
Init( title, pContextKeyValues );
|
|
}
|
|
|
|
|
|
FileOpenDialog::FileOpenDialog( Panel *parent, const char *title, FileOpenDialogType_t type, KeyValues *pContextKeyValues ) :
|
|
Frame( parent, "FileOpenDialog" )
|
|
{
|
|
m_DialogType = type;
|
|
Init( title, pContextKeyValues );
|
|
}
|
|
|
|
void FileOpenDialog::Init( const char *title, KeyValues *pContextKeyValues )
|
|
{
|
|
// By default, delete self on close
|
|
SetDeleteSelfOnClose( true );
|
|
|
|
m_bFileSelected = false;
|
|
SetTitle(title, true);
|
|
SetMinimizeButtonVisible(false);
|
|
#ifdef POSIX
|
|
V_strncpy(m_szLastPath, "/", sizeof( m_szLastPath ) );
|
|
#else
|
|
V_strncpy(m_szLastPath, "c:\\", sizeof( m_szLastPath ) );
|
|
#endif
|
|
m_pContextKeyValues = pContextKeyValues;
|
|
|
|
// Get the list of available drives and put them in a menu here.
|
|
// Start with the directory we are in.
|
|
m_pFullPathEdit = new ComboBox(this, "FullPathEdit", 6, false);
|
|
m_pFullPathEdit->GetTooltip()->SetTooltipFormatToSingleLine();
|
|
|
|
// list panel
|
|
m_pFileList = new ListPanel(this, "FileList");
|
|
for ( int i = 0; i < ARRAYSIZE( g_ColInfo ); ++i )
|
|
{
|
|
const ColumnInfo_t& info = g_ColInfo[ i ];
|
|
|
|
m_pFileList->AddColumnHeader( i, info.columnName, info.columnText, info.startingWidth, info.minWidth, info.maxWidth, info.flags );
|
|
m_pFileList->SetSortFunc( i, info.pfnSort );
|
|
m_pFileList->SetColumnTextAlignment( i, info.alignment );
|
|
}
|
|
|
|
m_pFileList->SetSortColumn( s_nLastSortColumn );
|
|
m_pFileList->SetMultiselectEnabled( false );
|
|
|
|
// file name edit box
|
|
m_pFileNameCombo = new FileNameComboBox(this, "FileNameCombo", 6, true );
|
|
m_pFileTypeCombo = new ComboBox( this, "FileTypeCombo", 6, false );
|
|
|
|
switch ( m_DialogType )
|
|
{
|
|
case FOD_OPEN:
|
|
m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this );
|
|
break;
|
|
case FOD_SAVE:
|
|
m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Save", this );
|
|
break;
|
|
case FOD_SELECT_DIRECTORY:
|
|
m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Select", this );
|
|
m_pFileTypeCombo->SetVisible( false );
|
|
break;
|
|
}
|
|
|
|
m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this );
|
|
m_pFolderUpButton = new Button( this, "FolderUpButton", "", this );
|
|
m_pFolderUpButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_Up" );
|
|
m_pNewFolderButton = new Button( this, "NewFolderButton", "", this );
|
|
m_pNewFolderButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_NewFolder" );
|
|
m_pOpenInExplorerButton = new Button( this, "OpenInExplorerButton", "", this );
|
|
#if defined ( OSX )
|
|
m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInFinderButton" );
|
|
#elif defined ( POSIX )
|
|
m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInDesktopManagerButton" );
|
|
#else // Assume Windows / Explorer
|
|
m_pOpenInExplorerButton->GetTooltip()->SetText( "#FileOpenDialog_ToolTip_OpenInExplorerButton" );
|
|
#endif
|
|
Label *lookIn = new Label( this, "LookInLabel", "#FileOpenDialog_Look_in" );
|
|
Label *fileName = new Label( this, "FileNameLabel",
|
|
( m_DialogType != FOD_SELECT_DIRECTORY ) ? "#FileOpenDialog_File_name" : "#FileOpenDialog_Directory_Name" );
|
|
|
|
// set up the control's initial positions
|
|
SetSize( 600, 260 );
|
|
|
|
int nFileEditLeftSide = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 84 : 100;
|
|
int nFileNameWidth = ( m_DialogType != FOD_SELECT_DIRECTORY ) ? 72 : 82;
|
|
|
|
m_pFullPathEdit->SetBounds(67, 32, 310, 24);
|
|
m_pFolderUpButton->SetBounds(362, 32, 24, 24);
|
|
m_pNewFolderButton->SetBounds(392, 32, 24, 24);
|
|
m_pOpenInExplorerButton->SetBounds(332, 32, 24, 24);
|
|
m_pFileList->SetBounds(10, 60, 406, 130);
|
|
m_pFileNameCombo->SetBounds( nFileEditLeftSide, 194, 238, 24);
|
|
m_pFileTypeCombo->SetBounds( nFileEditLeftSide, 224, 238, 24);
|
|
m_pOpenButton->SetBounds(336, 194, 74, 24);
|
|
m_pCancelButton->SetBounds(336, 224, 74, 24);
|
|
lookIn->SetBounds(10, 32, 55, 24);
|
|
fileName->SetBounds(10, 194, nFileNameWidth, 24);
|
|
|
|
// set autolayout parameters
|
|
m_pFullPathEdit->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_RIGHT, 67, 32, -100, 0 );
|
|
m_pFileNameCombo->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -42, -104, 0 );
|
|
m_pFileTypeCombo->SetAutoResize( Panel::PIN_BOTTOMLEFT, Panel::AUTORESIZE_RIGHT, nFileEditLeftSide, -12, -104, 0 );
|
|
m_pFileList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 10, 60, -10, -70 );
|
|
|
|
m_pFolderUpButton->SetPinCorner( Panel::PIN_TOPRIGHT, -40, 32 );
|
|
m_pNewFolderButton->SetPinCorner( Panel::PIN_TOPRIGHT, -10, 32 );
|
|
m_pOpenInExplorerButton->SetPinCorner( Panel::PIN_TOPRIGHT, -70, 32 );
|
|
m_pOpenButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -42 );
|
|
m_pCancelButton->SetPinCorner( Panel::PIN_BOTTOMRIGHT, -16, -12 );
|
|
lookIn->SetPinCorner( Panel::PIN_TOPLEFT, 10, 32 );
|
|
fileName->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -42 );
|
|
|
|
// label settings
|
|
lookIn->SetContentAlignment(Label::a_west);
|
|
fileName->SetContentAlignment(Label::a_west);
|
|
|
|
lookIn->SetAssociatedControl(m_pFullPathEdit);
|
|
fileName->SetAssociatedControl(m_pFileNameCombo);
|
|
|
|
if ( m_DialogType != FOD_SELECT_DIRECTORY )
|
|
{
|
|
Label *fileType = new Label(this, "FileTypeLabel", "#FileOpenDialog_File_type");
|
|
fileType->SetBounds(10, 224, 72, 24);
|
|
fileType->SetPinCorner( Panel::PIN_BOTTOMLEFT, 10, -12 );
|
|
fileType->SetContentAlignment(Label::a_west);
|
|
fileType->SetAssociatedControl( m_pFileTypeCombo );
|
|
}
|
|
|
|
// set tab positions
|
|
GetFocusNavGroup().SetDefaultButton(m_pOpenButton);
|
|
|
|
m_pFileNameCombo->SetTabPosition(1);
|
|
m_pFileTypeCombo->SetTabPosition(2);
|
|
m_pOpenButton->SetTabPosition(3);
|
|
m_pCancelButton->SetTabPosition(4);
|
|
m_pFullPathEdit->SetTabPosition(5);
|
|
m_pFileList->SetTabPosition(6);
|
|
|
|
m_pOpenButton->SetCommand( ( m_DialogType != FOD_SELECT_DIRECTORY ) ? new KeyValues( "OnOpen" ) : new KeyValues( "SelectFolder" ) );
|
|
m_pCancelButton->SetCommand( "CloseModal" );
|
|
m_pFolderUpButton->SetCommand( new KeyValues( "OnFolderUp" ) );
|
|
m_pNewFolderButton->SetCommand( new KeyValues( "OnNewFolder" ) );
|
|
m_pOpenInExplorerButton->SetCommand( new KeyValues( "OpenInExplorer" ) );
|
|
|
|
SetSize( 600, 384 );
|
|
|
|
m_nStartDirContext = s_StartDirContexts.InvalidIndex();
|
|
|
|
// Set our starting path to the current directory
|
|
char pLocalPath[255];
|
|
g_pFullFileSystem->GetCurrentDirectory( pLocalPath , 255 );
|
|
SetStartDirectory( pLocalPath );
|
|
|
|
// Because these call through virtual functions, we can't issue them in the constructor, so we post a message to ourselves instead!!
|
|
PostMessage( GetVPanel(), new KeyValues( "PopulateFileList" ) );
|
|
PostMessage( GetVPanel(), new KeyValues( "PopulateDriveList" ) );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Destructor
|
|
//-----------------------------------------------------------------------------
|
|
FileOpenDialog::~FileOpenDialog()
|
|
{
|
|
s_nLastSortColumn = m_pFileList->GetSortColumn();
|
|
if ( m_pContextKeyValues )
|
|
{
|
|
m_pContextKeyValues->deleteThis();
|
|
m_pContextKeyValues = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Apply scheme settings
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::ApplySchemeSettings(IScheme *pScheme)
|
|
{
|
|
BaseClass::ApplySchemeSettings(pScheme);
|
|
|
|
m_pFolderUpButton->SetImage( scheme()->GetImage("resource/icon_folderup", false), 0 );
|
|
m_pFolderUpButton->SetContentAlignment( Label::a_center );
|
|
m_pNewFolderButton->SetImage( scheme()->GetImage("resource/icon_newfolder", false), 0 );
|
|
m_pNewFolderButton->SetContentAlignment( Label::a_center );
|
|
m_pOpenInExplorerButton->SetImage( scheme()->GetImage("resource/icon_explore", false), 0 );
|
|
m_pOpenInExplorerButton->SetContentAlignment( Label::a_center );
|
|
|
|
ImageList *imageList = new ImageList(false);
|
|
imageList->AddImage(scheme()->GetImage("resource/icon_file", false));
|
|
imageList->AddImage(scheme()->GetImage("resource/icon_folder", false));
|
|
imageList->AddImage(scheme()->GetImage("resource/icon_folder_selected", false));
|
|
|
|
m_pFileList->SetImageList(imageList, true);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Prevent default button ('select') from getting triggered
|
|
// when selecting directories. Instead, open the directory
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::OnKeyCodeTyped(KeyCode code)
|
|
{
|
|
if ( m_DialogType == FOD_SELECT_DIRECTORY && code == KEY_ENTER )
|
|
{
|
|
OnOpen();
|
|
}
|
|
else
|
|
{
|
|
BaseClass::OnKeyCodeTyped( code );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::PopulateDriveList()
|
|
{
|
|
char fullpath[MAX_PATH * 4];
|
|
char subDirPath[MAX_PATH * 4];
|
|
GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH);
|
|
Q_strncpy(subDirPath, fullpath, sizeof( subDirPath ) );
|
|
|
|
m_pFullPathEdit->DeleteAllItems();
|
|
|
|
// populate the drive list
|
|
char buf[512];
|
|
int len = system()->GetAvailableDrives(buf, 512);
|
|
char *pBuf = buf;
|
|
for (int i=0; i < len / 4; i++)
|
|
{
|
|
m_pFullPathEdit->AddItem(pBuf, NULL);
|
|
|
|
// is this our drive - add all subdirectories
|
|
if (!_strnicmp(pBuf, fullpath, 2))
|
|
{
|
|
int indent = 0;
|
|
char *pData = fullpath;
|
|
while (*pData)
|
|
{
|
|
if (*pData == CORRECT_PATH_SEPARATOR )
|
|
{
|
|
if (indent > 0)
|
|
{
|
|
memset(subDirPath, ' ', indent);
|
|
memcpy(subDirPath+indent, fullpath, pData-fullpath);
|
|
subDirPath[indent+pData-fullpath] = 0;
|
|
|
|
m_pFullPathEdit->AddItem(subDirPath, NULL);
|
|
}
|
|
indent += 2;
|
|
}
|
|
pData++;
|
|
}
|
|
}
|
|
pBuf += 4;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Delete self on close
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::OnClose()
|
|
{
|
|
s_nLastSortColumn = m_pFileList->GetSortColumn();
|
|
if ( !m_bFileSelected )
|
|
{
|
|
KeyValues *pKeyValues = new KeyValues( "FileSelectionCancelled" );
|
|
PostActionSignal( pKeyValues );
|
|
m_bFileSelected = true;
|
|
}
|
|
|
|
m_pFileNameCombo->SetText("");
|
|
m_pFileNameCombo->HideMenu();
|
|
|
|
if ( vgui::input()->GetAppModalSurface() == GetVPanel() )
|
|
{
|
|
input()->SetAppModalSurface(NULL);
|
|
}
|
|
|
|
BaseClass::OnClose();
|
|
}
|
|
|
|
void FileOpenDialog::OnFolderUp()
|
|
{
|
|
MoveUpFolder();
|
|
OnOpen();
|
|
}
|
|
|
|
void FileOpenDialog::OnInputCompleted( KeyValues *data )
|
|
{
|
|
if ( m_hInputDialog.Get() )
|
|
{
|
|
delete m_hInputDialog.Get();
|
|
}
|
|
|
|
input()->SetAppModalSurface( m_SaveModal );
|
|
m_SaveModal = 0;
|
|
|
|
NewFolder( data->GetString( "text" ) );
|
|
OnOpen();
|
|
}
|
|
|
|
void FileOpenDialog::OnInputCanceled()
|
|
{
|
|
input()->SetAppModalSurface( m_SaveModal );
|
|
m_SaveModal = 0;
|
|
}
|
|
|
|
void FileOpenDialog::OnNewFolder()
|
|
{
|
|
if ( m_hInputDialog.Get() )
|
|
delete m_hInputDialog.Get();
|
|
|
|
m_hInputDialog = new InputDialog( this, "#FileOpenDialog_NewFolder_InputTitle", "#FileOpenDialog_NewFolderPrompt", "#FileOpenDialog_NewFolder_DefaultName" );
|
|
if ( m_hInputDialog.Get() )
|
|
{
|
|
m_SaveModal = input()->GetAppModalSurface();
|
|
|
|
KeyValues *pContextKeyValues = new KeyValues( "NewFolder" );
|
|
m_hInputDialog->SetSmallCaption( true );
|
|
m_hInputDialog->SetMultiline( false );
|
|
m_hInputDialog->DoModal( pContextKeyValues );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Opens the current file/folder in explorer
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::OnOpenInExplorer()
|
|
{
|
|
char pCurrentDirectory[MAX_PATH];
|
|
GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) );
|
|
#if defined( WIN32 )
|
|
#if !defined( _GAMECONSOLE )
|
|
ShellExecute( NULL, NULL, pCurrentDirectory, NULL, NULL, SW_SHOWNORMAL );
|
|
#endif
|
|
#elif defined( OSX )
|
|
char szCmd[ MAX_PATH ];
|
|
Q_snprintf( szCmd, sizeof(szCmd), "/usr/bin/open \"%s\"", pCurrentDirectory );
|
|
::system( szCmd );
|
|
#elif defined( LINUX )
|
|
DevMsg( "FileOpenDialog::OnOpenInExplorer unimplemented under LINUX\n" );
|
|
#endif
|
|
}
|
|
|
|
void FileOpenDialog::AddSearchHistoryString( char const *str )
|
|
{
|
|
// See if it's already in list
|
|
for ( int i = 0; i < m_SearchHistory.Count(); ++i )
|
|
{
|
|
if ( !Q_stricmp( str, m_SearchHistory[ i ].String() ) )
|
|
return;
|
|
}
|
|
|
|
while ( m_SearchHistory.Count() > MAX_SEARCH_HISTORY )
|
|
{
|
|
m_SearchHistory.Remove( m_SearchHistory.Count() - 1 );
|
|
}
|
|
|
|
CUtlString string;
|
|
string = str;
|
|
m_SearchHistory.AddToTail( string );
|
|
|
|
PopulateFileNameSearchHistory();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Handle for button commands
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::OnCommand(const char *command)
|
|
{
|
|
if (!stricmp(command, "Cancel"))
|
|
{
|
|
Close();
|
|
}
|
|
else
|
|
{
|
|
BaseClass::OnCommand(command);
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sets the start directory context (and resets the start directory in the process)
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::SetStartDirectoryContext( const char *pStartDirContext, const char *pDefaultDir )
|
|
{
|
|
bool bUseCurrentDirectory = true;
|
|
if ( pStartDirContext )
|
|
{
|
|
m_nStartDirContext = s_StartDirContexts.Find( pStartDirContext );
|
|
if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() )
|
|
{
|
|
m_nStartDirContext = s_StartDirContexts.Insert( pStartDirContext, pDefaultDir );
|
|
bUseCurrentDirectory = ( pDefaultDir == NULL );
|
|
}
|
|
else
|
|
{
|
|
bUseCurrentDirectory = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_nStartDirContext = s_StartDirContexts.InvalidIndex();
|
|
}
|
|
|
|
if ( !bUseCurrentDirectory )
|
|
{
|
|
SetStartDirectory( s_StartDirContexts[m_nStartDirContext].Get() );
|
|
}
|
|
else
|
|
{
|
|
// Set our starting path to the current directory
|
|
char pLocalPath[255];
|
|
g_pFullFileSystem->GetCurrentDirectory( pLocalPath, 255 );
|
|
SetStartDirectory( pLocalPath );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Set the starting directory of the file search.
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::SetStartDirectory( const char *dir )
|
|
{
|
|
m_pFullPathEdit->SetText(dir);
|
|
|
|
// ensure it's validity
|
|
ValidatePath();
|
|
|
|
// Store this in the start directory list
|
|
if ( m_nStartDirContext != s_StartDirContexts.InvalidIndex() )
|
|
{
|
|
char pDirBuf[MAX_PATH];
|
|
GetCurrentDirectory( pDirBuf, sizeof(pDirBuf) );
|
|
s_StartDirContexts[ m_nStartDirContext ] = pDirBuf;
|
|
}
|
|
|
|
PopulateDriveList();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Add filters for the drop down combo box
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::AddFilter( const char *filter, const char *filterName, bool bActive, const char *pFilterInfo )
|
|
{
|
|
KeyValues *kv = new KeyValues("item");
|
|
kv->SetString( "filter", filter );
|
|
kv->SetString( "filterinfo", pFilterInfo );
|
|
int itemID = m_pFileTypeCombo->AddItem(filterName, kv);
|
|
if ( bActive )
|
|
{
|
|
m_pFileTypeCombo->ActivateItem(itemID);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Activate the dialog
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::DoModal( bool bUnused )
|
|
{
|
|
m_bFileSelected = false;
|
|
m_pFileNameCombo->RequestFocus();
|
|
BaseClass::DoModal();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Gets the directory this is currently in
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::GetCurrentDirectory(char *buf, int bufSize)
|
|
{
|
|
// get the text from the text entry
|
|
m_pFullPathEdit->GetText(buf, bufSize);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Get the last selected file name
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::GetSelectedFileName(char *buf, int bufSize)
|
|
{
|
|
m_pFileNameCombo->GetText(buf, bufSize);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Creates a new folder
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::NewFolder( char const *folderName )
|
|
{
|
|
char pCurrentDirectory[MAX_PATH];
|
|
GetCurrentDirectory( pCurrentDirectory, sizeof(pCurrentDirectory) );
|
|
|
|
char pFullPath[MAX_PATH];
|
|
char pNewFolderName[MAX_PATH];
|
|
Q_strncpy( pNewFolderName, folderName, sizeof(pNewFolderName) );
|
|
int i = 2;
|
|
do
|
|
{
|
|
Q_MakeAbsolutePath( pFullPath, sizeof(pFullPath), pNewFolderName, pCurrentDirectory );
|
|
if ( !g_pFullFileSystem->FileExists( pFullPath, NULL ) &&
|
|
!g_pFullFileSystem->IsDirectory( pFullPath, NULL ) )
|
|
{
|
|
g_pFullFileSystem->CreateDirHierarchy( pFullPath, NULL );
|
|
m_pFileNameCombo->SetText( pNewFolderName );
|
|
return;
|
|
}
|
|
|
|
Q_snprintf( pNewFolderName, sizeof(pNewFolderName), "%s%d", folderName, i );
|
|
++i;
|
|
} while ( i <= 999 );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Move the directory structure up
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::MoveUpFolder()
|
|
{
|
|
char fullpath[MAX_PATH * 4];
|
|
GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH);
|
|
|
|
// strip it back
|
|
char *pos = strrchr(fullpath, CORRECT_PATH_SEPARATOR );
|
|
if (pos)
|
|
{
|
|
*pos = 0;
|
|
|
|
if (!pos[1])
|
|
{
|
|
pos = strrchr(fullpath, CORRECT_PATH_SEPARATOR );
|
|
if (pos)
|
|
{
|
|
*pos = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// append a trailing slash
|
|
Q_strncat(fullpath, CORRECT_PATH_SEPARATOR_S, sizeof( fullpath ), COPY_ALL_CHARACTERS );
|
|
|
|
SetStartDirectory(fullpath);
|
|
PopulateFileList();
|
|
InvalidateLayout();
|
|
Repaint();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Validate that the current path is valid
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::ValidatePath()
|
|
{
|
|
char fullpath[MAX_PATH * 4];
|
|
GetCurrentDirectory(fullpath, sizeof(fullpath) - MAX_PATH);
|
|
Q_RemoveDotSlashes( fullpath );
|
|
|
|
// when statting a directory on Windows, you want to include
|
|
// the terminal slash exactly when you are statting a root
|
|
// directory. PKMN.
|
|
#ifdef _WIN32
|
|
if ( Q_strlen( fullpath ) != 3 )
|
|
{
|
|
Q_StripTrailingSlash( fullpath );
|
|
}
|
|
#endif
|
|
|
|
struct _stat buf;
|
|
if ( ( 0 == _stat( fullpath, &buf ) ) &&
|
|
( 0 != ( buf.st_mode & S_IFDIR ) ) )
|
|
{
|
|
Q_AppendSlash( fullpath, sizeof( fullpath ) );
|
|
Q_strncpy(m_szLastPath, fullpath, sizeof(m_szLastPath));
|
|
}
|
|
else
|
|
{
|
|
// failed to load file, use the previously successful path
|
|
}
|
|
|
|
m_pFullPathEdit->SetText(m_szLastPath);
|
|
m_pFullPathEdit->GetTooltip()->SetText(m_szLastPath);
|
|
}
|
|
|
|
|
|
static void InitFileData( bool bDirectory, char const *pszFileName, const char *pchDirectoryName, FileData_t &data )
|
|
{
|
|
data.m_FileName = V_UnqualifiedFileName( pszFileName );
|
|
data.m_FullPath = CFmtStr( "%s%s", pchDirectoryName, pszFileName );
|
|
Q_FixSlashes( data.m_FullPath.Get() );
|
|
|
|
if ( !bDirectory )
|
|
{
|
|
g_pFullFileSystem->GetFileTypeForFullPath( data.m_FullPath, data.m_FileType, sizeof( data.m_FileType ) );
|
|
}
|
|
|
|
data.m_bDirectory = bDirectory;
|
|
data.m_nFileSize = g_pFullFileSystem->Size( data.m_FullPath.Get() );
|
|
|
|
if ( !g_pFullFileSystem->IsFileWritable( data.m_FullPath.Get() ) )
|
|
data.m_FileAttributes = "R";
|
|
|
|
long fileModified = g_pFullFileSystem->GetFileTime( data.m_FullPath.Get() );
|
|
char pszFileModified[64];
|
|
g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified );
|
|
data.m_LastWriteTime = pszFileModified;
|
|
data.m_nLastWriteTime = fileModified;
|
|
}
|
|
|
|
|
|
void FileData_t::PrepareKV( KeyValues *kv )
|
|
{
|
|
// add the file to the list
|
|
kv->SetString("text", m_FileName );
|
|
|
|
kv->SetInt("directory", m_bDirectory ? 1 : 0 );
|
|
kv->SetInt("image", m_bDirectory ? 2 : 1 );
|
|
kv->SetInt("imageSelected", m_bDirectory ? 3 : 1 );
|
|
|
|
kv->SetPtr( "iconImage", NULL );
|
|
|
|
if ( !m_bDirectory )
|
|
{
|
|
IImage *image = surface()->GetIconImageForFullPath( m_FullPath.String() );
|
|
if ( image )
|
|
{
|
|
kv->SetPtr( "iconImage", (void *)image );
|
|
}
|
|
kv->SetUint64( "filesizeint", (uint64)m_nFileSize );
|
|
kv->SetString( "filesize", Q_pretifymem( (float)m_nFileSize, 0, true ) );
|
|
kv->SetWString( "type", m_FileType );
|
|
|
|
}
|
|
else
|
|
{
|
|
kv->SetUint64( "filesizeint", (uint64)0 );
|
|
kv->SetString( "filesize", "" );
|
|
kv->SetString( "type", "#FileOpenDialog_FileType_Folder" );
|
|
}
|
|
|
|
kv->SetString( "attributes", m_FileAttributes );
|
|
kv->SetString( "modified", m_LastWriteTime );
|
|
kv->SetString( "created", m_CreationTime );
|
|
kv->SetUint64( "modifiedint", m_nLastWriteTime );
|
|
kv->SetUint64( "createdint", m_nCreationTime );
|
|
}
|
|
|
|
void FileOpenDialog::BuildFileList()
|
|
{
|
|
m_Files.RemoveAll();
|
|
m_Filtered.RemoveAll();
|
|
|
|
#ifndef _GAMECONSOLE
|
|
|
|
// get the current directory
|
|
char currentDir[MAX_PATH * 4];
|
|
char dir[MAX_PATH * 4];
|
|
char filterList[MAX_FILTER_LENGTH+1];
|
|
GetCurrentDirectory(currentDir, sizeof(currentDir));
|
|
|
|
FileFindHandle_t findHandle;
|
|
|
|
KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData();
|
|
if (combokv)
|
|
{
|
|
Q_strncpy(filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH);
|
|
}
|
|
else
|
|
{
|
|
// add wildcard for search
|
|
Q_strncpy(filterList, "*\0", MAX_FILTER_LENGTH);
|
|
}
|
|
|
|
char *filterPtr = filterList;
|
|
//KeyValues *kv = new KeyValues("item");
|
|
|
|
if ( m_DialogType != FOD_SELECT_DIRECTORY )
|
|
{
|
|
while ((filterPtr != NULL) && (*filterPtr != 0))
|
|
{
|
|
// parse the next filter in the list.
|
|
char curFilter[MAX_FILTER_LENGTH];
|
|
curFilter[0] = 0;
|
|
int i = 0;
|
|
while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' ')))
|
|
{
|
|
++filterPtr;
|
|
}
|
|
while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' '))
|
|
{
|
|
curFilter[i++] = *(filterPtr++);
|
|
}
|
|
curFilter[i] = 0;
|
|
|
|
if (curFilter[0] == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Q_snprintf( dir, MAX_PATH*4, "%s%s", currentDir, curFilter );
|
|
|
|
// Open the directory and walk it, loading files
|
|
const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle );
|
|
while ( pszFileName )
|
|
{
|
|
if ( !g_pFullFileSystem->FindIsDirectory( findHandle ) )
|
|
{
|
|
FileData_t &fd = m_Files[ m_Files.AddToTail() ];
|
|
InitFileData( false, pszFileName, currentDir, fd );
|
|
}
|
|
|
|
pszFileName = g_pFullFileSystem->FindNext( findHandle );
|
|
}
|
|
g_pFullFileSystem->FindClose( findHandle );
|
|
}
|
|
}
|
|
|
|
|
|
// find all the directories
|
|
GetCurrentDirectory(currentDir, sizeof(currentDir));
|
|
Q_snprintf( dir, MAX_PATH*4, "%s*", currentDir );
|
|
|
|
const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle );
|
|
while ( pszFileName )
|
|
{
|
|
if ( pszFileName[0] != '.' && g_pFullFileSystem->FindIsDirectory( findHandle ) )
|
|
{
|
|
FileData_t &fd = m_Files[ m_Files.AddToTail() ];
|
|
InitFileData( true, pszFileName, currentDir, fd );
|
|
}
|
|
|
|
pszFileName = g_pFullFileSystem->FindNext( findHandle );
|
|
}
|
|
g_pFullFileSystem->FindClose( findHandle );
|
|
#endif
|
|
}
|
|
|
|
// Static method to do wildcard matching for *, ? and . characters
|
|
bool FileOpenDialog::FileNameWildCardMatch( char const *string, char const *pattern )
|
|
{
|
|
for (;; ++string)
|
|
{
|
|
char stringc=toupper(*string);
|
|
char patternc=toupper(*pattern++);
|
|
switch (patternc)
|
|
{
|
|
case 0:
|
|
return(stringc==0);
|
|
case '?':
|
|
if (stringc == 0)
|
|
return(false);
|
|
break;
|
|
case '*':
|
|
if (*pattern==0)
|
|
return(true);
|
|
if (*pattern=='.')
|
|
{
|
|
if (pattern[1]=='*' && pattern[2]==0)
|
|
return(true);
|
|
const char *dot=strchr(string,'.');
|
|
if (pattern[1]==0)
|
|
return (dot==NULL || dot[1]==0);
|
|
if (dot!=NULL)
|
|
{
|
|
string=dot;
|
|
if (strpbrk(pattern,"*?")==NULL && strchr(string+1,'.')==NULL)
|
|
return(Q_stricmp(pattern+1,string+1)==0);
|
|
}
|
|
}
|
|
|
|
while (*string)
|
|
if (FileNameWildCardMatch(string++, pattern))
|
|
return(true);
|
|
return(false);
|
|
default:
|
|
if (patternc != stringc)
|
|
{
|
|
if (patternc=='.' && stringc==0)
|
|
return(FileNameWildCardMatch(string, pattern ));
|
|
else
|
|
return(false);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FileOpenDialog::PassesFilter( FileData_t *fd )
|
|
{
|
|
// Do the substring filtering
|
|
if ( fd->m_bDirectory )
|
|
return true;
|
|
|
|
// Never filter Save... dialogs
|
|
if ( m_DialogType == FOD_SAVE )
|
|
return true;
|
|
|
|
if ( m_CurrentSubstringFilter.Length() <= 0 )
|
|
return true;
|
|
|
|
if ( Q_stristr( fd->m_FileName, m_CurrentSubstringFilter.String() ) )
|
|
return true;
|
|
|
|
if ( FileNameWildCardMatch( fd->m_FileName, m_CurrentSubstringFilter.String() ) )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void FileOpenDialog::FilterFileList()
|
|
{
|
|
m_Filtered.RemoveAll();
|
|
for ( int i = 0; i < m_Files.Count(); ++i )
|
|
{
|
|
// Apply filter
|
|
FileData_t *pFD = &m_Files[ i ];
|
|
if ( PassesFilter( pFD ) )
|
|
{
|
|
m_Filtered.AddToTail( &m_Files[ i ] );
|
|
}
|
|
}
|
|
|
|
// clear the current list
|
|
m_pFileList->DeleteAllItems();
|
|
|
|
KeyValues *kv = new KeyValues("item");
|
|
|
|
for ( int i = 0; i < m_Filtered.Count(); ++i )
|
|
{
|
|
FileData_t *fd = m_Filtered[ i ];
|
|
fd->PrepareKV( kv );
|
|
m_pFileList->AddItem(kv, 0, false, false);
|
|
}
|
|
|
|
kv->deleteThis();
|
|
|
|
m_pFileList->SortList();
|
|
}
|
|
|
|
int FileOpenDialog::CountSubstringMatches()
|
|
{
|
|
int nMatches = 0;
|
|
|
|
for ( int i = 0; i < m_Files.Count(); ++i )
|
|
{
|
|
// Apply filter
|
|
FileData_t *pFD = &m_Files[ i ];
|
|
if ( PassesFilter( pFD ) )
|
|
{
|
|
if ( !pFD->m_bDirectory )
|
|
{
|
|
++nMatches;
|
|
}
|
|
}
|
|
}
|
|
return nMatches;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Fill the filelist with the names of all the files in the current directory
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::PopulateFileList()
|
|
{
|
|
BuildFileList();
|
|
FilterFileList();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Does the specified extension match something in the filter list?
|
|
//-----------------------------------------------------------------------------
|
|
bool FileOpenDialog::ExtensionMatchesFilter( const char *pExt )
|
|
{
|
|
KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData();
|
|
if ( !combokv )
|
|
return true;
|
|
|
|
char filterList[MAX_FILTER_LENGTH+1];
|
|
Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH );
|
|
|
|
char *filterPtr = filterList;
|
|
while ((filterPtr != NULL) && (*filterPtr != 0))
|
|
{
|
|
// parse the next filter in the list.
|
|
char curFilter[MAX_FILTER_LENGTH];
|
|
curFilter[0] = 0;
|
|
int i = 0;
|
|
while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' ')))
|
|
{
|
|
++filterPtr;
|
|
}
|
|
while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' '))
|
|
{
|
|
curFilter[i++] = *(filterPtr++);
|
|
}
|
|
curFilter[i] = 0;
|
|
|
|
if (curFilter[0] == 0)
|
|
break;
|
|
|
|
if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) )
|
|
return true;
|
|
|
|
// FIXME: This isn't exactly right, but tough cookies;
|
|
// it assumes the first two characters of the filter are *.
|
|
Assert( curFilter[0] == '*' && curFilter[1] == '.' );
|
|
if ( !Q_stricmp( &curFilter[2], pExt ) )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Choose the first non *.* filter in the filter list
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::ChooseExtension( char *pExt, int nBufLen )
|
|
{
|
|
pExt[0] = 0;
|
|
|
|
KeyValues *combokv = m_pFileTypeCombo->GetActiveItemUserData();
|
|
if ( !combokv )
|
|
return;
|
|
|
|
char filterList[MAX_FILTER_LENGTH+1];
|
|
Q_strncpy( filterList, combokv->GetString("filter", "*"), MAX_FILTER_LENGTH );
|
|
|
|
char *filterPtr = filterList;
|
|
while ((filterPtr != NULL) && (*filterPtr != 0))
|
|
{
|
|
// parse the next filter in the list.
|
|
char curFilter[MAX_FILTER_LENGTH];
|
|
curFilter[0] = 0;
|
|
int i = 0;
|
|
while ((filterPtr != NULL) && ((*filterPtr == ',') || (*filterPtr == ';') || (*filterPtr <= ' ')))
|
|
{
|
|
++filterPtr;
|
|
}
|
|
while ((filterPtr != NULL) && (*filterPtr != ',') && (*filterPtr != ';') && (*filterPtr > ' '))
|
|
{
|
|
curFilter[i++] = *(filterPtr++);
|
|
}
|
|
curFilter[i] = 0;
|
|
|
|
if (curFilter[0] == 0)
|
|
break;
|
|
|
|
if ( !Q_stricmp( curFilter, "*" ) || !Q_stricmp( curFilter, "*.*" ) )
|
|
continue;
|
|
|
|
// FIXME: This isn't exactly right, but tough cookies;
|
|
// it assumes the first two characters of the filter are *.
|
|
Assert( curFilter[0] == '*' && curFilter[1] == '.' );
|
|
Q_strncpy( pExt, &curFilter[1], nBufLen );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Saves the file to the start dir context
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::SaveFileToStartDirContext( const char *pFullPath )
|
|
{
|
|
if ( m_nStartDirContext == s_StartDirContexts.InvalidIndex() )
|
|
return;
|
|
|
|
char pPath[MAX_PATH];
|
|
pPath[0] = 0;
|
|
Q_ExtractFilePath( pFullPath, pPath, sizeof(pPath) );
|
|
s_StartDirContexts[ m_nStartDirContext ] = pPath;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Posts a file selected message
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::PostFileSelectedMessage( const char *pFileName )
|
|
{
|
|
m_bFileSelected = true;
|
|
|
|
// open the file!
|
|
KeyValues *pKeyValues = new KeyValues( "FileSelected", "fullpath", pFileName );
|
|
KeyValues *pFilterKeys = m_pFileTypeCombo->GetActiveItemUserData();
|
|
const char *pFilterInfo = pFilterKeys ? pFilterKeys->GetString( "filterinfo", NULL ) : NULL;
|
|
if ( pFilterInfo )
|
|
{
|
|
pKeyValues->SetString( "filterinfo", pFilterInfo );
|
|
}
|
|
if ( m_pContextKeyValues )
|
|
{
|
|
pKeyValues->AddSubKey( m_pContextKeyValues );
|
|
m_pContextKeyValues = NULL;
|
|
}
|
|
PostActionSignal( pKeyValues );
|
|
CloseModal();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Selects the current folder
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::OnSelectFolder()
|
|
{
|
|
ValidatePath();
|
|
|
|
// construct a file path
|
|
char pFileName[MAX_PATH];
|
|
GetSelectedFileName( pFileName, sizeof( pFileName ) );
|
|
|
|
Q_StripTrailingSlash( pFileName );
|
|
|
|
if ( !stricmp(pFileName, "..") )
|
|
{
|
|
MoveUpFolder();
|
|
|
|
// clear the name text
|
|
m_pFileNameCombo->SetText("");
|
|
return;
|
|
}
|
|
|
|
if ( !stricmp(pFileName, ".") )
|
|
{
|
|
// clear the name text
|
|
m_pFileNameCombo->SetText("");
|
|
return;
|
|
}
|
|
|
|
// Compute the full path
|
|
char pFullPath[MAX_PATH * 4];
|
|
if ( !Q_IsAbsolutePath( pFileName ) )
|
|
{
|
|
GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH);
|
|
strcat( pFullPath, pFileName );
|
|
if ( !pFileName[0] )
|
|
{
|
|
Q_StripTrailingSlash( pFullPath );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) );
|
|
}
|
|
|
|
if ( g_pFullFileSystem->FileExists( pFullPath ) )
|
|
{
|
|
// open the file!
|
|
SaveFileToStartDirContext( pFullPath );
|
|
PostFileSelectedMessage( pFullPath );
|
|
return;
|
|
}
|
|
|
|
PopulateDriveList();
|
|
PopulateFileList();
|
|
InvalidateLayout();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Handle the open button being pressed
|
|
// checks on what has changed and acts accordingly
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::OnOpen()
|
|
{
|
|
ValidatePath();
|
|
|
|
// construct a file path
|
|
char pFileName[MAX_PATH];
|
|
GetSelectedFileName( pFileName, sizeof( pFileName ) );
|
|
|
|
int nLen = Q_strlen( pFileName );
|
|
bool bSpecifiedDirectory = ( pFileName[nLen-1] == '/' || pFileName[nLen-1] == '\\' );
|
|
Q_StripTrailingSlash( pFileName );
|
|
|
|
if ( !stricmp(pFileName, "..") )
|
|
{
|
|
MoveUpFolder();
|
|
|
|
// clear the name text
|
|
m_pFileNameCombo->SetText("");
|
|
return;
|
|
}
|
|
|
|
if ( !stricmp(pFileName, ".") )
|
|
{
|
|
// clear the name text
|
|
m_pFileNameCombo->SetText("");
|
|
return;
|
|
}
|
|
|
|
// Compute the full path
|
|
char pFullPath[MAX_PATH * 4];
|
|
if ( !Q_IsAbsolutePath( pFileName ) )
|
|
{
|
|
GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH);
|
|
strcat(pFullPath, pFileName);
|
|
if ( !pFileName[0] )
|
|
{
|
|
Q_StripTrailingSlash( pFullPath );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) );
|
|
}
|
|
|
|
// If the name specified is a directory, then change directory
|
|
if ( g_pFullFileSystem->IsDirectory( pFullPath, NULL ) )
|
|
{
|
|
// it's a directory; change to the specified directory
|
|
if ( !bSpecifiedDirectory )
|
|
{
|
|
strcat( pFullPath , CORRECT_PATH_SEPARATOR_S );
|
|
}
|
|
SetStartDirectory( pFullPath );
|
|
|
|
// clear the name text
|
|
m_pFileNameCombo->SetText("");
|
|
m_pFileNameCombo->HideMenu();
|
|
m_CurrentSubstringFilter = "";
|
|
|
|
PopulateDriveList();
|
|
PopulateFileList();
|
|
InvalidateLayout();
|
|
return;
|
|
}
|
|
else if ( bSpecifiedDirectory )
|
|
{
|
|
PopulateDriveList();
|
|
PopulateFileList();
|
|
InvalidateLayout();
|
|
return;
|
|
}
|
|
|
|
m_CurrentSubstringFilter = pFileName;
|
|
AddSearchHistoryString( pFileName );
|
|
|
|
if ( m_DialogType != FOD_SAVE )
|
|
{
|
|
if ( m_CurrentSubstringFilter.Length() > 0 )
|
|
{
|
|
// It's ambiguous
|
|
int nMatches = CountSubstringMatches();
|
|
if ( nMatches >= 2 )
|
|
{
|
|
// Apply filter instead
|
|
FilterFileList();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Append suffix of the first filter that isn't *.*
|
|
char extension[512];
|
|
Q_ExtractFileExtension( pFullPath, extension, sizeof(extension) );
|
|
if ( !ExtensionMatchesFilter( extension ) )
|
|
{
|
|
ChooseExtension( extension, sizeof(extension) );
|
|
Q_SetExtension( pFullPath, extension, sizeof(pFullPath) );
|
|
}
|
|
|
|
if ( g_pFullFileSystem->FileExists( pFullPath ) )
|
|
{
|
|
// open the file!
|
|
SaveFileToStartDirContext( pFullPath );
|
|
PostFileSelectedMessage( pFullPath );
|
|
return;
|
|
}
|
|
|
|
// file not found
|
|
if ( ( m_DialogType == FOD_SAVE ) && pFileName[0] )
|
|
{
|
|
// open the file!
|
|
SaveFileToStartDirContext( pFullPath );
|
|
PostFileSelectedMessage( pFullPath );
|
|
return;
|
|
}
|
|
|
|
PopulateDriveList();
|
|
PopulateFileList();
|
|
InvalidateLayout();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: using the file edit box as a prefix, create a menu of all possible files
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::PopulateFileNameSearchHistory()
|
|
{
|
|
m_pFileNameCombo->RemoveAll();
|
|
for ( int i = 0; i < m_SearchHistory.Count(); ++i )
|
|
{
|
|
m_pFileNameCombo->AddItem( m_SearchHistory[ i ].String(), 0 );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Handle an item in the list being selected
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::OnItemSelected()
|
|
{
|
|
// make sure only one item is selected
|
|
if (m_pFileList->GetSelectedItemsCount() != 1)
|
|
{
|
|
m_pFileNameCombo->SetText("");
|
|
}
|
|
else
|
|
{
|
|
// put the file name into the text edit box
|
|
KeyValues *data = m_pFileList->GetItem(m_pFileList->GetSelectedItem(0));
|
|
m_pFileNameCombo->SetText(data->GetString("text"));
|
|
}
|
|
|
|
InvalidateLayout();
|
|
}
|
|
|
|
void FileOpenDialog::OnMatchStringSelected()
|
|
{
|
|
char pFileName[MAX_PATH];
|
|
GetSelectedFileName( pFileName, sizeof( pFileName ) );
|
|
m_pFileNameCombo->GetText( pFileName, sizeof( pFileName ) );
|
|
|
|
m_CurrentSubstringFilter = pFileName;
|
|
|
|
// Redo filter
|
|
FilterFileList();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Handle an item in the Drive combo box being selected
|
|
//-----------------------------------------------------------------------------
|
|
void FileOpenDialog::OnTextChanged(KeyValues *kv)
|
|
{
|
|
Panel *pPanel = (Panel *) kv->GetPtr("panel", NULL);
|
|
|
|
// first check which control had its text changed!
|
|
if (pPanel == m_pFullPathEdit)
|
|
{
|
|
m_pFileNameCombo->HideMenu();
|
|
m_pFileNameCombo->SetText("");
|
|
OnOpen();
|
|
}
|
|
else if (pPanel == m_pFileNameCombo)
|
|
{
|
|
// Don't react to text being typed
|
|
}
|
|
else if (pPanel == m_pFileTypeCombo)
|
|
{
|
|
m_pFileNameCombo->HideMenu();
|
|
PopulateFileList();
|
|
}
|
|
}
|