From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/vgui2/vgui_controls/FileOpenDialog.cpp | 1701 +++++++++++++++++++++++++ 1 file changed, 1701 insertions(+) create mode 100644 mp/src/vgui2/vgui_controls/FileOpenDialog.cpp (limited to 'mp/src/vgui2/vgui_controls/FileOpenDialog.cpp') diff --git a/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp b/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp new file mode 100644 index 00000000..8a1af699 --- /dev/null +++ b/mp/src/vgui2/vgui_controls/FileOpenDialog.cpp @@ -0,0 +1,1701 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Implementation of vgui generic open file dialog +// +// $NoKeywords: $ +//===========================================================================// + + +#define PROTECTED_THINGS_DISABLE + +#if !defined( _X360 ) && defined( WIN32 ) +#include "winlite.h" +#include +#elif defined( POSIX ) +#include +#define _stat stat +#define _wcsnicmp wcsncmp +#elif defined( _X360 ) +#else +#error +#endif + +#undef GetCurrentDirectory +#include "filesystem.h" +#include + +#include "tier1/utldict.h" +#include "tier1/utlstring.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#undef GetCurrentDirectory +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +static int s_nLastSortColumn = 0; + +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 ListBaseIntegerSortFunc(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; + } + + int i1 = item1.kv->GetInt(fieldName); + int i2 = item2.kv->GetInt(fieldName); + if ( i1 == i2 ) + { + // Use filename to break ties + return ListFileNameSortFunc( pPanel, item1, item2 ); + } + + return ( i1 < i2 ) ? -1 : 1; +} + +static int ListBaseInteger64SortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2, char const *lowfield, char const *highfield ) +{ + 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; + } + + uint32 l1 = item1.kv->GetInt(lowfield); + uint32 h1 = item1.kv->GetInt(highfield); + uint32 l2 = item2.kv->GetInt(lowfield); + uint32 h2 = item2.kv->GetInt(highfield); + uint64 i1 = (uint64)( (uint64)l1 | ( (uint64)h1 << 32 ) ); + uint64 i2 = (uint64)( (uint64)l2 | ( (uint64)h2 << 32 ) ); + + if ( i1 == i2 ) + { + // Use filename to break ties + return ListFileNameSortFunc( pPanel, item1, item2 ); + } + + return ( i1 < i2 ) ? -1 : 1; +} + + +static int ListFileSizeSortFunc(ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + return ListBaseIntegerSortFunc( 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_low", "modifiedint_high" ); +} +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_low", "createdint_high" ); +} +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 FileCompletionMenu : public Menu +{ +public: + FileCompletionMenu(Panel *parent, const char *panelName) : Menu(parent, panelName) + { + } + + // override it so it doesn't request focus + virtual void SetVisible(bool state) + { + Panel::SetVisible(state); + } + +}; + + +//----------------------------------------------------------------------------- +// File completion edit text entry +//----------------------------------------------------------------------------- +class FileCompletionEdit : public TextEntry +{ + DECLARE_CLASS_SIMPLE( FileCompletionEdit, TextEntry ); + +public: + FileCompletionEdit(Panel *parent); + ~FileCompletionEdit(); + + int AddItem(const char *itemText, KeyValues *userData); + int AddItem(const wchar_t *itemText, KeyValues *userData); + void DeleteAllItems(); + int GetItemCount(); + int GetItemIDFromRow(int row); + int GetRowFromItemID(int itemID); + virtual void PerformLayout(); + void OnSetText(const wchar_t *newtext); + virtual void OnKillFocus(); + void HideMenu(void); + void ShowMenu(void); + virtual void OnKeyCodeTyped(KeyCode code); + MESSAGE_FUNC_INT( OnMenuItemHighlight, "MenuItemHighlight", itemID ); + +private: + FileCompletionMenu *m_pDropDown; +}; + + + +FileCompletionEdit::FileCompletionEdit(Panel *parent) : TextEntry(parent, NULL) +{ + m_pDropDown = new FileCompletionMenu(this, NULL); + m_pDropDown->AddActionSignalTarget(this); +} + +FileCompletionEdit::~FileCompletionEdit() +{ + delete m_pDropDown; +} + +int FileCompletionEdit::AddItem(const char *itemText, KeyValues *userData) +{ + // when the menu item is selected it will send the custom message "SetText" + return m_pDropDown->AddMenuItem(itemText, new KeyValues("SetText", "text", itemText), this, userData); +} +int FileCompletionEdit::AddItem(const wchar_t *itemText, KeyValues *userData) +{ + // add the element to the menu + // when the menu item is selected it will send the custom message "SetText" + KeyValues *kv = new KeyValues("SetText"); + kv->SetWString("text", itemText); + + // get an ansi version for the menuitem name + char ansi[128]; + g_pVGuiLocalize->ConvertUnicodeToANSI(itemText, ansi, sizeof(ansi)); + return m_pDropDown->AddMenuItem(ansi, kv, this, userData); +} + +void FileCompletionEdit::DeleteAllItems() +{ + m_pDropDown->DeleteAllItems(); +} + +int FileCompletionEdit::GetItemCount() +{ + return m_pDropDown->GetItemCount(); +} + +int FileCompletionEdit::GetItemIDFromRow(int row) +{ + // valid from [0, GetItemCount) + return m_pDropDown->GetMenuID(row); +} + +int FileCompletionEdit::GetRowFromItemID(int itemID) +{ + int i; + for (i=0;iGetMenuID(i) == itemID) + return i; + } + return -1; +} + +void FileCompletionEdit::PerformLayout() +{ + BaseClass::PerformLayout(); + + m_pDropDown->PositionRelativeToPanel( this, Menu::DOWN, 0 ); + + // reset the width of the drop down menu to be the width of this edit box + m_pDropDown->SetFixedWidth(GetWide()); + m_pDropDown->ForceCalculateWidth(); +} + +void FileCompletionEdit::OnSetText(const wchar_t *newtext) +{ + // see if the combobox text has changed, and if so, post a message detailing the new text + wchar_t wbuf[255]; + GetText( wbuf, 254 ); + + if ( wcscmp(wbuf, newtext) ) + { + // text has changed + SetText(newtext); + + // fire off that things have changed + PostActionSignal(new KeyValues("TextChanged", "text", newtext)); + Repaint(); + } +} + +void FileCompletionEdit::OnKillFocus() +{ + HideMenu(); + BaseClass::OnKillFocus(); +} + +void FileCompletionEdit::HideMenu(void) +{ + // hide the menu + m_pDropDown->SetVisible(false); +} + +void FileCompletionEdit::ShowMenu(void) +{ + // reset the dropdown's position + m_pDropDown->InvalidateLayout(); + + // make sure we're at the top of the draw order (and therefore our children as well) + // this important to make sure the menu will be drawn in the foreground + MoveToFront(); + + // reset the drop down + m_pDropDown->ClearCurrentlyHighlightedItem(); + + // limit it to only 6 + if (m_pDropDown->GetItemCount() > 6) + { + m_pDropDown->SetNumberOfVisibleItems(6); + } + else + { + m_pDropDown->SetNumberOfVisibleItems(m_pDropDown->GetItemCount()); + } + // show the menu + m_pDropDown->SetVisible(true); + + Repaint(); +} + +void FileCompletionEdit::OnKeyCodeTyped(KeyCode code) +{ + if ( code == KEY_DOWN ) + { + if (m_pDropDown->GetItemCount() > 0) + { + int menuID = m_pDropDown->GetCurrentlyHighlightedItem(); + int row = -1; + if ( menuID == -1 ) + { + row = m_pDropDown->GetItemCount() - 1; + } + else + { + row = GetRowFromItemID(menuID); + } + row++; + if (row == m_pDropDown->GetItemCount()) + { + row = 0; + } + menuID = GetItemIDFromRow(row); + m_pDropDown->SetCurrentlyHighlightedItem(menuID); + return; + } + } + else if ( code == KEY_UP ) + { + if (m_pDropDown->GetItemCount() > 0) + { + int menuID = m_pDropDown->GetCurrentlyHighlightedItem(); + int row = -1; + if ( menuID == -1 ) + { + row = 0; + } + else + { + row = GetRowFromItemID(menuID); + } + row--; + if ( row < 0 ) + { + row = m_pDropDown->GetItemCount() - 1; + } + menuID = GetItemIDFromRow(row); + m_pDropDown->SetCurrentlyHighlightedItem(menuID); + return; + } + } + else if ( code == KEY_ESCAPE ) + { + if ( m_pDropDown->IsVisible() ) + { + HideMenu(); + return; + } + } + BaseClass::OnKeyCodeTyped(code); + return; +} + +void FileCompletionEdit::OnMenuItemHighlight( int itemID ) +{ + char wbuf[80]; + if ( m_pDropDown->IsValidMenuID(itemID) ) + { + m_pDropDown->GetMenuItem(itemID)->GetText(wbuf, 80); + } + else + { + wbuf[0] = 0; + } + SetText(wbuf); + RequestFocus(); + GotoTextEnd(); +} + + +} // 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 ) +{ + m_bFileSelected = false; + SetTitle(title, true); + SetMinimizeButtonVisible(false); + +#ifdef POSIX + Q_strncpy(m_szLastPath, "/", sizeof( m_szLastPath ) ); +#else + Q_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_pFileNameEdit = new FileCompletionEdit(this); + m_pFileNameEdit->AddActionSignalTarget(this); + + 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" ); + + m_pFolderIcon = new ImagePanel(NULL, "FolderIcon"); + + // 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_pFileNameEdit->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_pFileNameEdit->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_pFileNameEdit); + + 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_pFileNameEdit->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 ); + if ( !pLocalPath[0] || ( IsOSX() && V_strlen(pLocalPath) <= 2 ) ) + { + const char *pszHomeDir = getenv( "HOME" ); + V_strcpy_safe( pLocalPath, pszHomeDir ); + } + + 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_pFolderIcon->SetImage(scheme()->GetImage("resource/icon_folder", false)); + m_pFolderUpButton->AddImage(scheme()->GetImage("resource/icon_folderup", false), -3); + m_pNewFolderButton->AddImage( scheme()->GetImage("resource/icon_newfolder", false), -3 ); + m_pOpenInExplorerButton->AddImage( scheme()->GetImage("resource/icon_play_once", false), -3 ); + + 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(); + +#ifdef WIN32 + // 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; + } +#else + m_pFullPathEdit->AddItem("/", NULL); + + char *pData = fullpath; + int indent = 0; + while (*pData) + { + if (*pData == '/' && ( pData[1] != '\0' ) ) + { + 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++; + } +#endif +} + + +//----------------------------------------------------------------------------- +// 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_pFileNameEdit->SetText(""); + m_pFileNameEdit->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( _X360 ) && defined( WIN32 ) + ShellExecute( NULL, NULL, pCurrentDirectory, NULL, NULL, SW_SHOWNORMAL ); +#elif defined( OSX ) + char szCmd[ MAX_PATH * 2]; + Q_snprintf( szCmd, sizeof(szCmd), "/usr/bin/open \"%s\"", pCurrentDirectory ); + ::system( szCmd ); +#elif defined( LINUX ) + char szCmd[ MAX_PATH * 2 ]; + Q_snprintf( szCmd, sizeof(szCmd), "xdg-open \"%s\" &", pCurrentDirectory ); + ::system( szCmd ); +#endif +} + + +//----------------------------------------------------------------------------- +// 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_pFileNameEdit->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_pFileNameEdit->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_pFileNameEdit->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); + + Q_StripLastDir( fullpath, sizeof( fullpath ) ); + // append a trailing slash + Q_AppendSlash( fullpath, sizeof( fullpath ) ); + + 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 + // cleanup the path, we format tabs into the list to make it pretty in the UI + Q_StripPrecedingAndTrailingWhitespace( fullpath ); + + 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); +} + +#ifdef WIN32 +const char *GetAttributesAsString( DWORD dwAttributes ) +{ + static char out[ 256 ]; + out[ 0 ] = 0; + if ( dwAttributes & FILE_ATTRIBUTE_ARCHIVE ) + { + Q_strncat( out, "A", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_COMPRESSED ) + { + Q_strncat( out, "C", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_DIRECTORY ) + { + Q_strncat( out, "D", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_HIDDEN ) + { + Q_strncat( out, "H", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_READONLY ) + { + Q_strncat( out, "R", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_SYSTEM ) + { + Q_strncat( out, "S", sizeof( out ), COPY_ALL_CHARACTERS ); + } + if ( dwAttributes & FILE_ATTRIBUTE_TEMPORARY ) + { + Q_strncat( out, "T", sizeof( out ), COPY_ALL_CHARACTERS ); + } + return out; +} + +const char *GetFileTimetamp( FILETIME ft ) +{ + SYSTEMTIME local; + FILETIME localFileTime; + FileTimeToLocalFileTime( &ft, &localFileTime ); + FileTimeToSystemTime( &localFileTime, &local ); + + static char out[ 256 ]; + + bool am = true; + WORD hour = local.wHour; + if ( hour >= 12 ) + { + am = false; + // 12:42 pm displays as 12:42 pm + // 13:42 pm displays as 1:42 pm + if ( hour > 12 ) + { + hour -= 12; + } + } + Q_snprintf( out, sizeof( out ), "%d/%02d/%04d %d:%02d %s", + local.wMonth, + local.wDay, + local.wYear, + hour, + local.wMinute, + am ? "AM" : "PM" // TODO: Localize this? + ); + return out; +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: Fill the filelist with the names of all the files in the current directory +//----------------------------------------------------------------------------- +#define MAX_FILTER_LENGTH 255 +void FileOpenDialog::PopulateFileList() +{ + // clear the current list + m_pFileList->DeleteAllItems(); + + FileFindHandle_t findHandle; + char pszFileModified[64]; + + // get the current directory + char currentDir[MAX_PATH * 4]; + char dir[MAX_PATH * 4]; + char filterList[MAX_FILTER_LENGTH+1]; + GetCurrentDirectory(currentDir, sizeof(dir)); + + 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 ) + || !IsOSX() + || ( IsOSX() && g_pFullFileSystem->FindIsDirectory( findHandle ) && Q_stristr( pszFileName, ".app" ) ) ) + { + char pFullPath[MAX_PATH]; + Q_snprintf( pFullPath, MAX_PATH, "%s%s", currentDir, pszFileName ); + + // add the file to the list + kv->SetString( "text", pszFileName ); + + kv->SetInt( "image", 1 ); + + IImage *image = surface()->GetIconImageForFullPath( pFullPath ); + + if ( image ) + { + kv->SetPtr( "iconImage", (void *)image ); + } + + kv->SetInt("imageSelected", 1); + kv->SetInt("directory", 0); + + kv->SetString( "filesize", Q_pretifymem( g_pFullFileSystem->Size( pFullPath ), 0, true ) ); + Q_FixSlashes( pFullPath ); + wchar_t fileType[ 80 ]; + g_pFullFileSystem->GetFileTypeForFullPath( pFullPath, fileType, sizeof( fileType ) ); + kv->SetWString( "type", fileType ); + + kv->SetString( "attributes", g_pFullFileSystem->IsFileWritable( pFullPath )? "" : "R" ); + + long fileModified = g_pFullFileSystem->GetFileTime( pFullPath ); + g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified ); + kv->SetString( "modified", pszFileModified ); + +// kv->SetString( "created", GetFileTimetamp( findData.ftCreationTime ) ); + + m_pFileList->AddItem(kv, 0, false, false); + } + + pszFileName = g_pFullFileSystem->FindNext( findHandle ); + } + g_pFullFileSystem->FindClose( findHandle ); + } + } + + // find all the directories + GetCurrentDirectory( dir, sizeof(dir) ); + Q_strncat(dir, "*", sizeof( dir ), COPY_ALL_CHARACTERS); + + const char *pszFileName = g_pFullFileSystem->FindFirst( dir, &findHandle ); + while ( pszFileName ) + { + if ( pszFileName[0] != '.' && g_pFullFileSystem->FindIsDirectory( findHandle ) + && ( !IsOSX() || ( IsOSX() && !Q_stristr( pszFileName, ".app" ) ) ) ) + { + char pFullPath[MAX_PATH]; + Q_snprintf( pFullPath, MAX_PATH, "%s%s", currentDir, pszFileName ); + + kv->SetString("text", pszFileName ); + kv->SetPtr( "iconImage", (void *)NULL ); + kv->SetInt("image", 2); + kv->SetInt("imageSelected", 3); + kv->SetInt("directory", 1); + + kv->SetString( "filesize", "" ); + kv->SetString( "type", "#FileOpenDialog_FileType_Folder" ); + + kv->SetString( "attributes", g_pFullFileSystem->IsFileWritable( pFullPath )? "" : "R" ); + + long fileModified = g_pFullFileSystem->GetFileTime( pFullPath ); + g_pFullFileSystem->FileTimeToString( pszFileModified, sizeof( pszFileModified ), fileModified ); + kv->SetString( "modified", pszFileModified ); + +// kv->SetString( "created", GetFileTimetamp( findData.ftCreationTime ) ); + + m_pFileList->AddItem( kv, 0, false, false ); + } + + pszFileName = g_pFullFileSystem->FindNext( findHandle ); + } + g_pFullFileSystem->FindClose( findHandle ); + + kv->deleteThis(); + m_pFileList->SortList(); +} + + +//----------------------------------------------------------------------------- +// 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_pFileNameEdit->SetText(""); + return; + } + + if ( !stricmp(pFileName, ".") ) + { + // clear the name text + m_pFileNameEdit->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] == '\\' ) && (!IsOSX() || ( IsOSX() && !Q_stristr( pFileName, ".app" ) ) ); + Q_StripTrailingSlash( pFileName ); + + if ( !stricmp(pFileName, "..") ) + { + MoveUpFolder(); + + // clear the name text + m_pFileNameEdit->SetText(""); + return; + } + + if ( !stricmp(pFileName, ".") ) + { + // clear the name text + m_pFileNameEdit->SetText(""); + return; + } + + // Compute the full path + char pFullPath[MAX_PATH * 4]; + if ( !Q_IsAbsolutePath( pFileName ) ) + { + GetCurrentDirectory(pFullPath, sizeof(pFullPath) - MAX_PATH); + Q_AppendSlash( pFullPath, sizeof( pFullPath ) ); + strcat(pFullPath, pFileName); + if ( !pFileName[0] ) + { + Q_StripTrailingSlash( pFullPath ); + } + } + else + { + Q_strncpy( pFullPath, pFileName, sizeof(pFullPath) ); + } + + Q_StripTrailingSlash( pFullPath ); + + // 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( pFullPath ) == 2 ) + { + Q_AppendSlash( pFullPath, Q_ARRAYSIZE( pFullPath ) ); + } +#endif + + + // If the name specified is a directory, then change directory + if ( g_pFullFileSystem->IsDirectory( pFullPath, NULL ) && + ( !IsOSX() || ( IsOSX() && !Q_stristr( pFullPath, ".app" ) ) ) ) + { + // it's a directory; change to the specified directory + if ( !bSpecifiedDirectory ) + { + Q_AppendSlash( pFullPath, Q_ARRAYSIZE( pFullPath ) ); + } + SetStartDirectory( pFullPath ); + + // clear the name text + m_pFileNameEdit->SetText(""); + m_pFileNameEdit->HideMenu(); + + PopulateDriveList(); + PopulateFileList(); + InvalidateLayout(); + return; + } + else if ( bSpecifiedDirectory ) + { + PopulateDriveList(); + PopulateFileList(); + InvalidateLayout(); + 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::PopulateFileNameCompletion() +{ + char buf[80]; + m_pFileNameEdit->GetText(buf, 80); + wchar_t wbuf[80]; + m_pFileNameEdit->GetText(wbuf, 80); + int bufLen = wcslen(wbuf); + + // delete all items before we check if there's even a string + m_pFileNameEdit->DeleteAllItems(); + + // no string at all - don't show even bother showing it + if (bufLen == 0) + { + m_pFileNameEdit->HideMenu(); + return; + } + + // what files use current string as a prefix? + int nCount = m_pFileList->GetItemCount(); + int i; + for ( i = 0 ; i < nCount ; i++ ) + { + KeyValues *kv = m_pFileList->GetItem(m_pFileList->GetItemIDFromRow(i)); + const wchar_t *wszString = kv->GetWString("text"); + if ( !_wcsnicmp(wbuf, wszString, bufLen) ) + { + m_pFileNameEdit->AddItem(wszString, NULL); + } + } + + // if there are any items - show the menu + if ( m_pFileNameEdit->GetItemCount() > 0 ) + { + m_pFileNameEdit->ShowMenu(); + } + else + { + m_pFileNameEdit->HideMenu(); + } + + m_pFileNameEdit->InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// 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_pFileNameEdit->SetText(""); + } + else + { + // put the file name into the text edit box + KeyValues *data = m_pFileList->GetItem(m_pFileList->GetSelectedItem(0)); + m_pFileNameEdit->SetText(data->GetString("text")); + } + + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// 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_pFileNameEdit->HideMenu(); + m_pFileNameEdit->SetText(""); + OnOpen(); + } + else if (pPanel == m_pFileNameEdit) + { + PopulateFileNameCompletion(); + } + else if (pPanel == m_pFileTypeCombo) + { + m_pFileNameEdit->HideMenu(); + PopulateFileList(); + } +} -- cgit v1.2.3