From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- sp/src/vgui2/vgui_controls/FileOpenDialog.cpp | 3402 ++++++++++++------------- 1 file changed, 1701 insertions(+), 1701 deletions(-) (limited to 'sp/src/vgui2/vgui_controls/FileOpenDialog.cpp') diff --git a/sp/src/vgui2/vgui_controls/FileOpenDialog.cpp b/sp/src/vgui2/vgui_controls/FileOpenDialog.cpp index 8a1af699..ebe5eb0c 100644 --- a/sp/src/vgui2/vgui_controls/FileOpenDialog.cpp +++ b/sp/src/vgui2/vgui_controls/FileOpenDialog.cpp @@ -1,1701 +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(); - } -} +//========= 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