//========= Copyright Valve Corporation, All rights reserved. ============// // // This is a helper class designed to help with the chains of modal dialogs // encountered when trying to open or save a particular file // //============================================================================= #include "vgui_controls/FileOpenStateMachine.h" #include "tier1/KeyValues.h" #include "vgui_controls/FileOpenDialog.h" #include "vgui_controls/MessageBox.h" #include "vgui_controls/perforcefilelistframe.h" #include "vgui_controls/savedocumentquery.h" #include "filesystem.h" #include "p4lib/ip4.h" #include "tier2/tier2.h" #include "tier0/icommandline.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" using namespace vgui; //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- FileOpenStateMachine::FileOpenStateMachine( vgui::Panel *pParent, IFileOpenStateMachineClient *pClient ) : BaseClass( pParent, "FileOpenStateMachine" ) { m_pClient = pClient; m_CompletionState = SUCCESSFUL; m_CurrentState = STATE_NONE; m_pContextKeyValues = NULL; SetVisible( false ); } FileOpenStateMachine::~FileOpenStateMachine() { CleanUpContextKeyValues(); } //----------------------------------------------------------------------------- // Cleans up keyvalues //----------------------------------------------------------------------------- void FileOpenStateMachine::CleanUpContextKeyValues() { if ( m_pContextKeyValues ) { m_pContextKeyValues->deleteThis(); m_pContextKeyValues = NULL; } } //----------------------------------------------------------------------------- // Returns the state machine completion state //----------------------------------------------------------------------------- FileOpenStateMachine::CompletionState_t FileOpenStateMachine::GetCompletionState() { return m_CompletionState; } //----------------------------------------------------------------------------- // Utility to set the completion state //----------------------------------------------------------------------------- void FileOpenStateMachine::SetCompletionState( FileOpenStateMachine::CompletionState_t state ) { m_CompletionState = state; if ( m_CompletionState == IN_PROGRESS ) return; m_CurrentState = STATE_NONE; KeyValues *kv = new KeyValues( "FileStateMachineFinished" ); kv->SetInt( "completionState", m_CompletionState ); kv->SetInt( "wroteFile", m_bWroteFile ); kv->SetString( "fullPath", m_FileName.Get() ); kv->SetString( "fileType", m_bIsOpeningFile ? m_OpenFileType.Get() : m_SaveFileType.Get() ); if ( m_pContextKeyValues ) { kv->AddSubKey( m_pContextKeyValues ); m_pContextKeyValues = NULL; } PostActionSignal( kv ); } //----------------------------------------------------------------------------- // Called by the message box in OverwriteFileDialog //----------------------------------------------------------------------------- void FileOpenStateMachine::OnOverwriteFile( ) { CheckOutDialog( ); } void FileOpenStateMachine::OnCancelOverwriteFile( ) { SetCompletionState( FILE_NOT_OVERWRITTEN ); } //----------------------------------------------------------------------------- // Shows the overwrite existing file dialog //----------------------------------------------------------------------------- void FileOpenStateMachine::OverwriteFileDialog( ) { if ( !g_pFullFileSystem->FileExists( m_FileName ) ) { CheckOutDialog( ); return; } m_CurrentState = STATE_SHOWING_OVERWRITE_DIALOG; char pBuf[1024]; Q_snprintf( pBuf, sizeof(pBuf), "File already exists. Overwrite it?\n\n\"%s\"\n", m_FileName.Get() ); vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Overwrite Existing File?", pBuf, GetParent() ); pMessageBox->AddActionSignalTarget( this ); pMessageBox->SetOKButtonVisible( true ); pMessageBox->SetOKButtonText( "Yes" ); pMessageBox->SetCancelButtonVisible( true ); pMessageBox->SetCancelButtonText( "No" ); pMessageBox->SetCloseButtonVisible( false ); pMessageBox->SetCommand( new KeyValues( "OverwriteFile" ) ); pMessageBox->SetCancelCommand( new KeyValues( "CancelOverwriteFile" ) ); pMessageBox->DoModal(); } //----------------------------------------------------------------------------- // Used to open a particular file in perforce, and deal with all the lovely dialogs //----------------------------------------------------------------------------- void FileOpenStateMachine::OnFileSelectionCancelled() { if ( m_CurrentState == STATE_SHOWING_SAVE_DIALOG ) { SetCompletionState( FILE_SAVE_NAME_NOT_SPECIFIED ); return; } if ( m_CurrentState == STATE_SHOWING_OPEN_DIALOG ) { SetCompletionState( FILE_OPEN_NAME_NOT_SPECIFIED ); return; } Assert(0); } //----------------------------------------------------------------------------- // Used to open a particular file in perforce, and deal with all the lovely dialogs //----------------------------------------------------------------------------- void FileOpenStateMachine::OnFileSelected( KeyValues *pKeyValues ) { if ( m_CurrentState == STATE_SHOWING_SAVE_DIALOG ) { m_FileName = pKeyValues->GetString( "fullpath" ); const char *pFilterInfo = pKeyValues->GetString( "filterinfo" ); if ( pFilterInfo ) { m_SaveFileType = pFilterInfo; } OverwriteFileDialog(); return; } if ( m_CurrentState == STATE_SHOWING_OPEN_DIALOG ) { m_FileName = pKeyValues->GetString( "fullpath" ); const char *pFilterInfo = pKeyValues->GetString( "filterinfo" ); if ( pFilterInfo ) { m_OpenFileType = pFilterInfo; } ReadFile( ); return; } Assert(0); } //----------------------------------------------------------------------------- // Writes the file out //----------------------------------------------------------------------------- void FileOpenStateMachine::WriteFile() { m_CurrentState = STATE_WRITING_FILE; if ( !m_pClient->OnWriteFileToDisk( m_FileName, m_SaveFileType, m_pContextKeyValues ) ) { SetCompletionState( ERROR_WRITING_FILE ); return; } m_bWroteFile = true; if ( m_bShowPerforceDialogs ) { m_CurrentState = STATE_SHOWING_PERFORCE_ADD_DIALOG; ShowPerforceQuery( GetParent(), m_FileName, this, NULL, PERFORCE_ACTION_FILE_ADD ); return; } if ( !m_bIsOpeningFile ) { SetCompletionState( SUCCESSFUL ); return; } OpenFileDialog(); } //----------------------------------------------------------------------------- // Called by the message box in MakeFileWriteableDialog //----------------------------------------------------------------------------- void FileOpenStateMachine::OnMakeFileWriteable( ) { if ( !g_pFullFileSystem->SetFileWritable( m_FileName, true ) ) { SetCompletionState( ERROR_MAKING_FILE_WRITEABLE ); return; } WriteFile(); } void FileOpenStateMachine::OnCancelMakeFileWriteable( ) { SetCompletionState( FILE_NOT_MADE_WRITEABLE ); } //----------------------------------------------------------------------------- // Shows the make file writeable dialog //----------------------------------------------------------------------------- void FileOpenStateMachine::MakeFileWriteableDialog( ) { // If the file is writeable, write it! if ( !g_pFullFileSystem->FileExists( m_FileName ) || g_pFullFileSystem->IsFileWritable( m_FileName ) ) { WriteFile(); return; } // If it's in perforce, and not checked out, then we must abort. bool bIsInPerforce = p4->IsFileInPerforce( m_FileName ); bool bIsOpened = ( p4->GetFileState( m_FileName ) != P4FILE_UNOPENED ); if ( bIsInPerforce && !bIsOpened ) { SetCompletionState( FILE_NOT_CHECKED_OUT ); return; } m_CurrentState = STATE_SHOWING_MAKE_FILE_WRITEABLE_DIALOG; char pBuf[1024]; Q_snprintf( pBuf, sizeof(pBuf), "Encountered read-only file. Should it be made writeable?\n\n\"%s\"\n", m_FileName.Get() ); vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Make File Writeable?", pBuf, GetParent() ); pMessageBox->AddActionSignalTarget( this ); pMessageBox->SetOKButtonVisible( true ); pMessageBox->SetOKButtonText( "Yes" ); pMessageBox->SetCancelButtonVisible( true ); pMessageBox->SetCancelButtonText( "No" ); pMessageBox->SetCloseButtonVisible( false ); pMessageBox->SetCommand( new KeyValues( "MakeFileWriteable" ) ); pMessageBox->SetCancelCommand( new KeyValues( "CancelMakeFileWriteable" ) ); pMessageBox->DoModal(); } //----------------------------------------------------------------------------- // Called when ShowPerforceQuery completes //----------------------------------------------------------------------------- void FileOpenStateMachine::OnPerforceQueryCompleted( KeyValues *pKeyValues ) { if ( m_CurrentState == STATE_SHOWING_CHECK_OUT_DIALOG ) { if ( pKeyValues->GetInt( "operationPerformed" ) == 0 ) { SetCompletionState( FILE_NOT_CHECKED_OUT ); return; } MakeFileWriteableDialog(); return; } if ( m_CurrentState == STATE_SHOWING_PERFORCE_ADD_DIALOG ) { if ( !m_bIsOpeningFile ) { SetCompletionState( SUCCESSFUL ); return; } OpenFileDialog(); return; } Assert(0); } //----------------------------------------------------------------------------- // Used to open a particular file in perforce, and deal with all the lovely dialogs //----------------------------------------------------------------------------- void FileOpenStateMachine::CheckOutDialog( ) { if ( m_bShowPerforceDialogs ) { m_CurrentState = STATE_SHOWING_CHECK_OUT_DIALOG; ShowPerforceQuery( GetParent(), m_FileName, this, NULL, PERFORCE_ACTION_FILE_EDIT ); return; } WriteFile(); } //----------------------------------------------------------------------------- // These 3 messages come from the savedocumentquery dialog //----------------------------------------------------------------------------- void FileOpenStateMachine::OnSaveFile() { if ( !m_FileName[0] || !Q_IsAbsolutePath( m_FileName ) ) { m_CurrentState = STATE_SHOWING_SAVE_DIALOG; FileOpenDialog *pDialog = new FileOpenDialog( GetParent(), "Save As", false ); m_pClient->SetupFileOpenDialog( pDialog, false, m_SaveFileType, m_pContextKeyValues ); pDialog->SetDeleteSelfOnClose( true ); pDialog->AddActionSignalTarget( this ); pDialog->DoModal( ); return; } CheckOutDialog( ); } void FileOpenStateMachine::OnMarkNotDirty() { if ( !m_bIsOpeningFile ) { SetCompletionState( SUCCESSFUL ); return; } // Jump right to opening the file OpenFileDialog( ); } void FileOpenStateMachine::OnCancelSaveDocument() { SetCompletionState( FILE_SAVE_CANCELLED ); } //----------------------------------------------------------------------------- // Show the save document query dialog //----------------------------------------------------------------------------- void FileOpenStateMachine::ShowSaveQuery( ) { m_CurrentState = STATE_SHOWING_SAVE_DIRTY_FILE_DIALOG; ShowSaveDocumentQuery( GetParent(), m_FileName, m_SaveFileType, 0, this, NULL ); } //----------------------------------------------------------------------------- // Used to save a specified file, and deal with all the lovely dialogs //----------------------------------------------------------------------------- void FileOpenStateMachine::SaveFile( KeyValues *pContextKeyValues, const char *pFileName, const char *pFileType, int nFlags ) { CleanUpContextKeyValues(); SetCompletionState( IN_PROGRESS ); m_pContextKeyValues = pContextKeyValues; m_FileName = pFileName; m_SaveFileType = pFileType; m_OpenFileType = NULL; m_OpenFileName = NULL; // Clear the P4 dialog flag for SDK users and licensees without Perforce if ( CommandLine()->FindParm( "-nop4" ) ) { nFlags &= ~FOSM_SHOW_PERFORCE_DIALOGS; } m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0; m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0; m_bIsOpeningFile = false; m_bWroteFile = false; if ( m_bShowSaveQuery ) { ShowSaveQuery(); return; } OnSaveFile(); } //----------------------------------------------------------------------------- // Reads the file in //----------------------------------------------------------------------------- void FileOpenStateMachine::ReadFile() { m_CurrentState = STATE_READING_FILE; if ( !m_pClient->OnReadFileFromDisk( m_FileName, m_OpenFileType, m_pContextKeyValues ) ) { SetCompletionState( ERROR_READING_FILE ); return; } SetCompletionState( SUCCESSFUL ); } //----------------------------------------------------------------------------- // Shows the open file dialog //----------------------------------------------------------------------------- void FileOpenStateMachine::OpenFileDialog( ) { m_CurrentState = STATE_SHOWING_OPEN_DIALOG; if ( m_OpenFileName.IsEmpty() ) { FileOpenDialog *pDialog = new FileOpenDialog( GetParent(), "Open", true ); m_pClient->SetupFileOpenDialog( pDialog, true, m_OpenFileType, m_pContextKeyValues ); pDialog->SetDeleteSelfOnClose( true ); pDialog->AddActionSignalTarget( this ); pDialog->DoModal( ); } else { m_FileName = m_OpenFileName; ReadFile(); } } //----------------------------------------------------------------------------- // Opens a file, saves an existing one if necessary //----------------------------------------------------------------------------- void FileOpenStateMachine::OpenFile( const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName, const char *pSaveFileType, int nFlags ) { CleanUpContextKeyValues(); SetCompletionState( IN_PROGRESS ); m_pContextKeyValues = pContextKeyValues; m_FileName = pSaveFileName; m_SaveFileType = pSaveFileType; m_OpenFileType = pOpenFileType; m_OpenFileName = NULL; m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0; m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0; m_bIsOpeningFile = true; m_bWroteFile = false; if ( m_bShowSaveQuery ) { ShowSaveQuery(); return; } OpenFileDialog(); } //----------------------------------------------------------------------------- // Version of OpenFile that skips browsing for a particular file to open //----------------------------------------------------------------------------- void FileOpenStateMachine::OpenFile( const char *pOpenFileName, const char *pOpenFileType, KeyValues *pContextKeyValues, const char *pSaveFileName, const char *pSaveFileType, int nFlags ) { CleanUpContextKeyValues(); SetCompletionState( IN_PROGRESS ); m_pContextKeyValues = pContextKeyValues; m_FileName = pSaveFileName; m_SaveFileType = pSaveFileType; m_OpenFileType = pOpenFileType; m_bShowPerforceDialogs = ( nFlags & FOSM_SHOW_PERFORCE_DIALOGS ) != 0; m_bShowSaveQuery = ( nFlags & FOSM_SHOW_SAVE_QUERY ) != 0; m_bIsOpeningFile = true; m_bWroteFile = false; m_OpenFileName = pOpenFileName; if ( m_bShowSaveQuery ) { ShowSaveQuery(); return; } OpenFileDialog(); }