diff options
Diffstat (limited to 'vgui2/vgui_controls/consoledialog.cpp')
| -rw-r--r-- | vgui2/vgui_controls/consoledialog.cpp | 1256 |
1 files changed, 1256 insertions, 0 deletions
diff --git a/vgui2/vgui_controls/consoledialog.cpp b/vgui2/vgui_controls/consoledialog.cpp new file mode 100644 index 0000000..d788abc --- /dev/null +++ b/vgui2/vgui_controls/consoledialog.cpp @@ -0,0 +1,1256 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "vgui_controls/consoledialog.h" + +#include "vgui/IInput.h" +#include "vgui/IScheme.h" +#include "vgui/IVGui.h" +#include "vgui/ISurface.h" +#include "vgui/ILocalize.h" +#include "KeyValues.h" + +#include "vgui_controls/Button.h" +#include "vgui/KeyCode.h" +#include "vgui_controls/Menu.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/RichText.h" +#include "tier1/convar.h" +#include "tier1/convar_serverbounded.h" +#include "icvar.h" +#include "filesystem.h" + +#include <stdlib.h> + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Used by the autocompletion system +//----------------------------------------------------------------------------- +class CNonFocusableMenu : public Menu +{ + DECLARE_CLASS_SIMPLE( CNonFocusableMenu, Menu ); + +public: + CNonFocusableMenu( Panel *parent, const char *panelName ) + : BaseClass( parent, panelName ), + m_pFocus( 0 ) + { + } + + void SetFocusPanel( Panel *panel ) + { + m_pFocus = panel; + } + + VPANEL GetCurrentKeyFocus() + { + if ( !m_pFocus ) + return GetVPanel(); + + return m_pFocus->GetVPanel(); + } + +private: + Panel *m_pFocus; +}; + + +//----------------------------------------------------------------------------- +// Purpose: forwards tab key presses up from the text entry so we can do autocomplete +//----------------------------------------------------------------------------- +class TabCatchingTextEntry : public TextEntry +{ +public: + TabCatchingTextEntry(Panel *parent, const char *name, VPANEL comp) : TextEntry(parent, name), m_pCompletionList( comp ) + { + SetAllowNonAsciiCharacters( true ); + SetDragEnabled( true ); + } + + virtual void OnKeyCodeTyped(KeyCode code) + { + if (code == KEY_TAB) + { + GetParent()->OnKeyCodeTyped(code); + } + else if ( code == KEY_ENTER ) + { + // submit is the default button whose click event will have been called already + } + else + { + TextEntry::OnKeyCodeTyped(code); + } + } + + virtual void OnKillFocus() + { + if ( input()->GetFocus() != m_pCompletionList ) // if its not the completion window trying to steal our focus + { + PostMessage(GetParent(), new KeyValues("CloseCompletionList")); + } + } + +private: + VPANEL m_pCompletionList; +}; + + + +// Things the user typed in and hit submit/return with +CHistoryItem::CHistoryItem( void ) +{ + m_text = NULL; + m_extraText = NULL; + m_bHasExtra = false; +} + +CHistoryItem::CHistoryItem( const char *text, const char *extra ) +{ + Assert( text ); + m_text = NULL; + m_extraText = NULL; + m_bHasExtra = false; + SetText( text , extra ); +} + +CHistoryItem::CHistoryItem( const CHistoryItem& src ) +{ + m_text = NULL; + m_extraText = NULL; + m_bHasExtra = false; + SetText( src.GetText(), src.GetExtra() ); +} + +CHistoryItem::~CHistoryItem( void ) +{ + delete[] m_text; + delete[] m_extraText; + m_text = NULL; +} + +const char *CHistoryItem::GetText() const +{ + if ( m_text ) + { + return m_text; + } + else + { + return ""; + } +} + +const char *CHistoryItem::GetExtra() const +{ + if ( m_extraText ) + { + return m_extraText; + } + else + { + return NULL; + } +} + +void CHistoryItem::SetText( const char *text, const char *extra ) +{ + delete[] m_text; + int len = strlen( text ) + 1; + + m_text = new char[ len ]; + Q_memset( m_text, 0x0, len ); + Q_strncpy( m_text, text, len ); + + if ( extra ) + { + m_bHasExtra = true; + delete[] m_extraText; + int elen = strlen( extra ) + 1; + m_extraText = new char[ elen ]; + Q_memset( m_extraText, 0x0, elen); + Q_strncpy( m_extraText, extra, elen ); + } + else + { + m_bHasExtra = false; + } +} + + +//----------------------------------------------------------------------------- +// +// Console page completion item starts here +// +//----------------------------------------------------------------------------- +CConsolePanel::CompletionItem::CompletionItem( void ) +{ + m_bIsCommand = true; + m_pCommand = NULL; + m_pText = NULL; +} + +CConsolePanel::CompletionItem::CompletionItem( const CompletionItem& src ) +{ + m_bIsCommand = src.m_bIsCommand; + m_pCommand = src.m_pCommand; + if ( src.m_pText ) + { + m_pText = new CHistoryItem( (const CHistoryItem& )src.m_pText ); + } + else + { + m_pText = NULL; + } +} + +CConsolePanel::CompletionItem& CConsolePanel::CompletionItem::operator =( const CompletionItem& src ) +{ + if ( this == &src ) + return *this; + + m_bIsCommand = src.m_bIsCommand; + m_pCommand = src.m_pCommand; + if ( src.m_pText ) + { + m_pText = new CHistoryItem( (const CHistoryItem& )*src.m_pText ); + } + else + { + m_pText = NULL; + } + + return *this; +} + +CConsolePanel::CompletionItem::~CompletionItem( void ) +{ + if ( m_pText ) + { + delete m_pText; + m_pText = NULL; + } +} + +const char *CConsolePanel::CompletionItem::GetName() const +{ + if ( m_bIsCommand ) + return m_pCommand->GetName(); + return m_pCommand ? m_pCommand->GetName() : GetCommand(); +} + +const char *CConsolePanel::CompletionItem::GetItemText( void ) +{ + static char text[256]; + text[0] = 0; + if ( m_pText ) + { + if ( m_pText->HasExtra() ) + { + Q_snprintf( text, sizeof( text ), "%s %s", m_pText->GetText(), m_pText->GetExtra() ); + } + else + { + Q_strncpy( text, m_pText->GetText(), sizeof( text ) ); + } + } + return text; +} + +const char *CConsolePanel::CompletionItem::GetCommand( void ) const +{ + static char text[256]; + text[0] = 0; + if ( m_pText ) + { + Q_strncpy( text, m_pText->GetText(), sizeof( text ) ); + } + return text; +} + + +//----------------------------------------------------------------------------- +// +// Console page starts here +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Purpose: Constructor, destuctor +//----------------------------------------------------------------------------- +CConsolePanel::CConsolePanel( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) : + BaseClass( pParent, pName ), m_bStatusVersion( bStatusVersion ) +{ + SetKeyBoardInputEnabled( true ); + + if ( !m_bStatusVersion ) + { + SetMinimumSize(100,100); + } + + // create controls + m_pHistory = new RichText(this, "ConsoleHistory"); + m_pHistory->SetAllowKeyBindingChainToParent( false ); + SETUP_PANEL( m_pHistory ); + m_pHistory->SetVerticalScrollbar( !m_bStatusVersion ); + if ( m_bStatusVersion ) + { + m_pHistory->SetDrawOffsets( 3, 3 ); + } + m_pHistory->GotoTextEnd(); + + m_pSubmit = new Button(this, "ConsoleSubmit", "#Console_Submit"); + m_pSubmit->SetCommand("submit"); + m_pSubmit->SetVisible( !m_bStatusVersion ); + + CNonFocusableMenu *pCompletionList = new CNonFocusableMenu( this, "CompletionList" ); + m_pCompletionList = pCompletionList; + m_pCompletionList->SetVisible(false); + + m_pEntry = new TabCatchingTextEntry(this, "ConsoleEntry", m_pCompletionList->GetVPanel() ); + m_pEntry->AddActionSignalTarget(this); + m_pEntry->SendNewLine(true); + pCompletionList->SetFocusPanel( m_pEntry ); + + // need to set up default colors, since ApplySchemeSettings won't be called until later + m_PrintColor = Color(216, 222, 211, 255); + m_DPrintColor = Color(196, 181, 80, 255); + + m_pEntry->SetTabPosition(1); + + m_bAutoCompleteMode = false; + m_szPartialText[0] = 0; + m_szPreviousPartialText[0]=0; + + // Add to global console list + g_pCVar->InstallConsoleDisplayFunc( this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CConsolePanel::~CConsolePanel() +{ + ClearCompletionList(); + m_CommandHistory.Purge(); + g_pCVar->RemoveConsoleDisplayFunc( this ); +} + + +//----------------------------------------------------------------------------- +// Updates the completion list +//----------------------------------------------------------------------------- +void CConsolePanel::OnThink() +{ + BaseClass::OnThink(); + + if ( !IsVisible() ) + return; + + if ( !m_pCompletionList->IsVisible() ) + return; + + UpdateCompletionListPosition(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Clears the console +//----------------------------------------------------------------------------- +void CConsolePanel::Clear() +{ + m_pHistory->SetText(""); + m_pHistory->GotoTextEnd(); +} + + +//----------------------------------------------------------------------------- +// Purpose: color text print +//----------------------------------------------------------------------------- +void CConsolePanel::ColorPrint( const Color& clr, const char *msg ) +{ + if ( m_bStatusVersion ) + { + Clear(); + } + + m_pHistory->InsertColorChange( clr ); + m_pHistory->InsertString( msg ); +} + + +//----------------------------------------------------------------------------- +// Purpose: normal text print +//----------------------------------------------------------------------------- +void CConsolePanel::Print(const char *msg) +{ + ColorPrint( m_PrintColor, msg ); +} + + +//----------------------------------------------------------------------------- +// Purpose: debug text print +//----------------------------------------------------------------------------- +void CConsolePanel::DPrint( const char *msg ) +{ + ColorPrint( m_DPrintColor, msg ); +} + + +void CConsolePanel::ClearCompletionList() +{ + int c = m_CompletionList.Count(); + int i; + for ( i = c - 1; i >= 0; i-- ) + { + delete m_CompletionList[ i ]; + } + m_CompletionList.Purge(); +} + + +static ConCommand *FindAutoCompleteCommmandFromPartial( const char *partial ) +{ + char command[ 256 ]; + Q_strncpy( command, partial, sizeof( command ) ); + + char *space = Q_strstr( command, " " ); + if ( space ) + { + *space = 0; + } + + ConCommand *cmd = g_pCVar->FindCommand( command ); + if ( !cmd ) + return NULL; + + if ( !cmd->CanAutoComplete() ) + return NULL; + + return cmd; +} + + +//----------------------------------------------------------------------------- +// Purpose: rebuilds the list of possible completions from the current entered text +//----------------------------------------------------------------------------- +void CConsolePanel::RebuildCompletionList(const char *text) +{ + ClearCompletionList(); + + // we need the length of the text for the partial string compares + int len = Q_strlen(text); + if ( len < 1 ) + { + // Fill the completion list with history instead + for ( int i = 0 ; i < m_CommandHistory.Count(); i++ ) + { + CHistoryItem *item = &m_CommandHistory[ i ]; + CompletionItem *comp = new CompletionItem(); + m_CompletionList.AddToTail( comp ); + comp->m_bIsCommand = false; + comp->m_pCommand = NULL; + comp->m_pText = new CHistoryItem( *item ); + } + return; + } + + bool bNormalBuild = true; + + // if there is a space in the text, and the command isn't of the type to know how to autocomplet, then command completion is over + const char *space = strstr( text, " " ); + if ( space ) + { + ConCommand *pCommand = FindAutoCompleteCommmandFromPartial( text ); + if ( !pCommand ) + return; + + bNormalBuild = false; + + CUtlVector< CUtlString > commands; + int count = pCommand->AutoCompleteSuggest( text, commands ); + Assert( count <= COMMAND_COMPLETION_MAXITEMS ); + int i; + + for ( i = 0; i < count; i++ ) + { + // match found, add to list + CompletionItem *item = new CompletionItem(); + m_CompletionList.AddToTail( item ); + item->m_bIsCommand = false; + item->m_pCommand = NULL; + item->m_pText = new CHistoryItem( commands[ i ].String() ); + } + } + + if ( bNormalBuild ) + { + // look through the command list for all matches + ConCommandBase const *cmd = (ConCommandBase const *)cvar->GetCommands(); + while (cmd) + { + if ( cmd->IsFlagSet( FCVAR_DEVELOPMENTONLY ) || cmd->IsFlagSet( FCVAR_HIDDEN ) ) + { + cmd = cmd->GetNext(); + continue; + } + + if ( !strnicmp(text, cmd->GetName(), len)) + { + // match found, add to list + CompletionItem *item = new CompletionItem(); + m_CompletionList.AddToTail( item ); + item->m_pCommand = (ConCommandBase *)cmd; + const char *tst = cmd->GetName(); + if ( !cmd->IsCommand() ) + { + item->m_bIsCommand = false; + ConVar *var = ( ConVar * )cmd; + ConVar_ServerBounded *pBounded = dynamic_cast<ConVar_ServerBounded*>( var ); + if ( pBounded || var->IsFlagSet( FCVAR_NEVER_AS_STRING ) ) + { + char strValue[512]; + + int intVal = pBounded ? pBounded->GetInt() : var->GetInt(); + float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat(); + + if ( floatVal == intVal ) + Q_snprintf( strValue, sizeof( strValue ), "%d", intVal ); + else + Q_snprintf( strValue, sizeof( strValue ), "%f", floatVal ); + + item->m_pText = new CHistoryItem( var->GetName(), strValue ); + } + else + { + item->m_pText = new CHistoryItem( var->GetName(), var->GetString() ); + } + } + else + { + item->m_bIsCommand = true; + item->m_pText = new CHistoryItem( tst ); + } + } + + cmd = cmd->GetNext(); + } + + // Now sort the list by command name + if ( m_CompletionList.Count() >= 2 ) + { + for ( int i = 0 ; i < m_CompletionList.Count(); i++ ) + { + for ( int j = i + 1; j < m_CompletionList.Count(); j++ ) + { + const CompletionItem *i1, *i2; + i1 = m_CompletionList[ i ]; + i2 = m_CompletionList[ j ]; + + if ( Q_stricmp( i1->GetName(), i2->GetName() ) > 0 ) + { + CompletionItem *temp = m_CompletionList[ i ]; + m_CompletionList[ i ] = m_CompletionList[ j ]; + m_CompletionList[ j ] = temp; + } + } + } + } + } + +} + +//----------------------------------------------------------------------------- +// Purpose: auto completes current text +//----------------------------------------------------------------------------- +void CConsolePanel::OnAutoComplete(bool reverse) +{ + if (!m_bAutoCompleteMode) + { + // we're not in auto-complete mode, Start + m_iNextCompletion = 0; + m_bAutoCompleteMode = true; + } + + // if we're in reverse, move back to before the current + if (reverse) + { + m_iNextCompletion -= 2; + if (m_iNextCompletion < 0) + { + // loop around in reverse + m_iNextCompletion = m_CompletionList.Size() - 1; + } + } + + // get the next completion + if (!m_CompletionList.IsValidIndex(m_iNextCompletion)) + { + // loop completion list + m_iNextCompletion = 0; + } + + // make sure everything is still valid + if (!m_CompletionList.IsValidIndex(m_iNextCompletion)) + return; + + // match found, set text + char completedText[256]; + CompletionItem *item = m_CompletionList[m_iNextCompletion]; + Assert( item ); + + if ( !item->m_bIsCommand && item->m_pCommand ) + { + Q_strncpy(completedText, item->GetCommand(), sizeof(completedText) - 2 ); + } + else + { + Q_strncpy(completedText, item->GetItemText(), sizeof(completedText) - 2 ); + } + + if ( !Q_strstr( completedText, " " ) ) + { + Q_strncat(completedText, " ", sizeof(completedText), COPY_ALL_CHARACTERS ); + } + + m_pEntry->SetText(completedText); + m_pEntry->GotoTextEnd(); + m_pEntry->SelectNone(); + + m_iNextCompletion++; +} + + +//----------------------------------------------------------------------------- +// Purpose: Called whenever the user types text +//----------------------------------------------------------------------------- +void CConsolePanel::OnTextChanged(Panel *panel) +{ + if (panel != m_pEntry) + return; + + Q_strncpy( m_szPreviousPartialText, m_szPartialText, sizeof( m_szPreviousPartialText ) ); + + // get the partial text the user type + m_pEntry->GetText(m_szPartialText, sizeof(m_szPartialText)); + + // see if they've hit the tilde key (which opens & closes the console) + int len = Q_strlen(m_szPartialText); + + bool hitTilde = ( m_szPartialText[len - 1] == '~' || m_szPartialText[len - 1] == '`' ) ? true : false; + + bool altKeyDown = ( vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ) ) ? true : false; + bool ctrlKeyDown = ( vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ) ) ? true : false; + + // Alt-Tilde toggles Japanese IME on/off!!! + if ( ( len > 0 ) && hitTilde ) + { + // Strip the last character (tilde) + m_szPartialText[ len - 1 ] = L'\0'; + + if( !altKeyDown && !ctrlKeyDown ) + { + m_pEntry->SetText( "" ); + + // close the console + PostMessage( this, new KeyValues( "Close" ) ); + PostActionSignal( new KeyValues( "ClosedByHittingTilde" ) ); + } + else + { + m_pEntry->SetText( m_szPartialText ); + } + return; + } + + // clear auto-complete state since the user has typed + m_bAutoCompleteMode = false; + + RebuildCompletionList(m_szPartialText); + + // build the menu + if ( m_CompletionList.Count() < 1 ) + { + m_pCompletionList->SetVisible(false); + } + else + { + m_pCompletionList->SetVisible(true); + m_pCompletionList->DeleteAllItems(); + const int MAX_MENU_ITEMS = 10; + + // add the first ten items to the list + for (int i = 0; i < m_CompletionList.Count() && i < MAX_MENU_ITEMS; i++) + { + char text[256]; + text[0] = 0; + if (i == MAX_MENU_ITEMS - 1) + { + Q_strncpy(text, "...", sizeof( text ) ); + } + else + { + Assert( m_CompletionList[i] ); + Q_strncpy(text, m_CompletionList[i]->GetItemText(), sizeof( text ) ); + } + KeyValues *kv = new KeyValues("CompletionCommand"); + kv->SetString("command",text); + m_pCompletionList->AddMenuItem(text, kv, this); + } + + UpdateCompletionListPosition(); + } + + RequestFocus(); + m_pEntry->RequestFocus(); + +} + +//----------------------------------------------------------------------------- +// Purpose: generic vgui command handler +//----------------------------------------------------------------------------- +void CConsolePanel::OnCommand(const char *command) +{ + if ( !Q_stricmp( command, "Submit" ) ) + { + // submit the entry as a console commmand + char szCommand[256]; + m_pEntry->GetText(szCommand, sizeof(szCommand)); + PostActionSignal( new KeyValues( "CommandSubmitted", "command", szCommand ) ); + + // add to the history + Print("] "); + Print(szCommand); + Print("\n"); + + // clear the field + m_pEntry->SetText(""); + + // clear the completion state + OnTextChanged(m_pEntry); + + // always go the end of the buffer when the user has typed something + m_pHistory->GotoTextEnd(); + + // Add the command to the history + char *extra = strchr(szCommand, ' '); + if ( extra ) + { + *extra = '\0'; + extra++; + } + + if ( Q_strlen( szCommand ) > 0 ) + { + AddToHistory( szCommand, extra ); + } + m_pCompletionList->SetVisible(false); + } + else + { + BaseClass::OnCommand(command); + } +} + + +//----------------------------------------------------------------------------- +// Focus related methods +//----------------------------------------------------------------------------- +bool CConsolePanel::TextEntryHasFocus() const +{ + return ( input()->GetFocus() == m_pEntry->GetVPanel() ); +} + +void CConsolePanel::TextEntryRequestFocus() +{ + m_pEntry->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Purpose: swallows tab key pressed +//----------------------------------------------------------------------------- +void CConsolePanel::OnKeyCodeTyped(KeyCode code) +{ + BaseClass::OnKeyCodeTyped(code); + + // check for processing + if ( TextEntryHasFocus() ) + { + if (code == KEY_TAB) + { + bool reverse = false; + if (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)) + { + reverse = true; + } + + // attempt auto-completion + OnAutoComplete(reverse); + m_pEntry->RequestFocus(); + } + else if (code == KEY_DOWN) + { + OnAutoComplete(false); + // UpdateCompletionListPosition(); + // m_pCompletionList->SetVisible(true); + + m_pEntry->RequestFocus(); + } + else if (code == KEY_UP) + { + OnAutoComplete(true); + m_pEntry->RequestFocus(); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: lays out controls +//----------------------------------------------------------------------------- +void CConsolePanel::PerformLayout() +{ + BaseClass::PerformLayout(); + + // setup tab ordering + GetFocusNavGroup().SetDefaultButton(m_pSubmit); + + IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); + m_pEntry->SetBorder(pScheme->GetBorder("DepressedButtonBorder")); + m_pHistory->SetBorder(pScheme->GetBorder("DepressedButtonBorder")); + + // layout controls + int wide, tall; + GetSize(wide, tall); + + if ( !m_bStatusVersion ) + { + const int inset = 8; + const int entryHeight = 24; + const int topHeight = 4; + const int entryInset = 4; + const int submitWide = 64; + const int submitInset = 7; // x inset to pull the submit button away from the frame grab + + m_pHistory->SetPos(inset, inset + topHeight); + m_pHistory->SetSize(wide - (inset * 2), tall - (entryInset * 2 + inset * 2 + topHeight + entryHeight)); + m_pHistory->InvalidateLayout(); + + int nSubmitXPos = wide - ( inset + submitWide + submitInset ); + m_pSubmit->SetPos( nSubmitXPos, tall - (entryInset * 2 + entryHeight)); + m_pSubmit->SetSize( submitWide, entryHeight); + + m_pEntry->SetPos( inset, tall - (entryInset * 2 + entryHeight) ); + m_pEntry->SetSize( nSubmitXPos - entryInset - 2 * inset, entryHeight); + } + else + { + const int inset = 2; + + int entryWidth = wide / 2; + if ( wide > 400 ) + { + entryWidth = 200; + } + + m_pEntry->SetBounds( inset, inset, entryWidth, tall - 2 * inset ); + + m_pHistory->SetBounds( inset + entryWidth + inset, inset, ( wide - entryWidth ) - inset, tall - 2 * inset ); + } + + UpdateCompletionListPosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets the position of the completion list popup +//----------------------------------------------------------------------------- +void CConsolePanel::UpdateCompletionListPosition() +{ + int ex, ey; + m_pEntry->GetPos(ex, ey); + + if ( !m_bStatusVersion ) + { + // Position below text entry + ey += m_pEntry->GetTall(); + } + else + { + // Position above text entry + int menuwide, menutall; + m_pCompletionList->GetSize( menuwide, menutall ); + ey -= ( menutall + 4 ); + } + + LocalToScreen( ex, ey ); + m_pCompletionList->SetPos( ex, ey ); + + if ( m_pCompletionList->IsVisible() ) + { + m_pEntry->RequestFocus(); + MoveToFront(); + m_pCompletionList->MoveToFront(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Closes the completion list +//----------------------------------------------------------------------------- +void CConsolePanel::CloseCompletionList() +{ + m_pCompletionList->SetVisible(false); +} + +//----------------------------------------------------------------------------- +// Purpose: sets up colors +//----------------------------------------------------------------------------- +void CConsolePanel::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + m_PrintColor = GetSchemeColor("Console.TextColor", pScheme); + m_DPrintColor = GetSchemeColor("Console.DevTextColor", pScheme); + m_pHistory->SetFont( pScheme->GetFont( "ConsoleText", IsProportional() ) ); + m_pCompletionList->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) ); + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: Handles autocompletion menu input +//----------------------------------------------------------------------------- +void CConsolePanel::OnMenuItemSelected(const char *command) +{ + if ( strstr( command, "..." ) ) // stop the menu going away if you click on ... + { + m_pCompletionList->SetVisible( true ); + } + else + { + m_pEntry->SetText(command); + m_pEntry->GotoTextEnd(); + m_pEntry->InsertChar(' '); + m_pEntry->GotoTextEnd(); + } +} + +void CConsolePanel::Hide() +{ + OnClose(); + m_iNextCompletion = 0; + RebuildCompletionList(""); +} + +void CConsolePanel::AddToHistory( const char *commandText, const char *extraText ) +{ + // Newest at end, oldest at head + while ( m_CommandHistory.Count() >= MAX_HISTORY_ITEMS ) + { + // Remove from head until size is reasonable + m_CommandHistory.Remove( 0 ); + } + + // strip the space off the end of the command before adding it to the history + // If this code gets cleaned up then we should remove the redundant calls to strlen, + // the check for whether _alloca succeeded, and should use V_strncpy instead of the + // error prone memset/strncpy sequence. + char *command = static_cast<char *>( _alloca( (strlen( commandText ) + 1 ) * sizeof( char ) )); + if ( command ) + { + memset( command, 0x0, strlen( commandText ) + 1 ); + strncpy( command, commandText, strlen( commandText )); + // There is no actual bug here, just some sloppy/odd code. + // src\vgui2\vgui_controls\consoledialog.cpp(974): warning C6053: The prior call to 'strncpy' might not zero-terminate string 'command' + ANALYZE_SUPPRESS( 6053 ) + if ( command[ strlen( command ) -1 ] == ' ' ) + { + command[ strlen( command ) -1 ] = '\0'; + } + } + + // strip the quotes off the extra text + char *extra = NULL; + + if ( extraText ) + { + extra = static_cast<char *>( malloc( (strlen( extraText ) + 1 ) * sizeof( char ) )); + if ( extra ) + { + memset( extra, 0x0, strlen( extraText ) + 1 ); + strncpy( extra, extraText, strlen( extraText )); // +1 to dodge the starting quote + + // Strip trailing spaces + int i = strlen( extra ) - 1; + while ( i >= 0 && // Check I before referencing i == -1 into the extra array! + extra[ i ] == ' ' ) + { + extra[ i ] = '\0'; + i--; + } + } + } + + // If it's already there, then remove since we'll add it to the end instead + CHistoryItem *item = NULL; + for ( int i = m_CommandHistory.Count() - 1; i >= 0; i-- ) + { + item = &m_CommandHistory[ i ]; + if ( !item ) + continue; + + if ( stricmp( item->GetText(), command ) ) + continue; + + if ( extra || item->GetExtra() ) + { + if ( !extra || !item->GetExtra() ) + continue; + + // stricmp so two commands with the same starting text get added + if ( stricmp( item->GetExtra(), extra ) ) + continue; + } + m_CommandHistory.Remove( i ); + } + + item = &m_CommandHistory[ m_CommandHistory.AddToTail() ]; + Assert( item ); + item->SetText( command, extra ); + + m_iNextCompletion = 0; + RebuildCompletionList( m_szPartialText ); + + free( extra ); +} + +void CConsolePanel::GetConsoleText( char *pchText, size_t bufSize ) const +{ + wchar_t *temp = new wchar_t[ bufSize ]; + m_pHistory->GetText( 0, temp, bufSize * sizeof( wchar_t ) ); + g_pVGuiLocalize->ConvertUnicodeToANSI( temp, pchText, bufSize ); + delete[] temp; +} + +//----------------------------------------------------------------------------- +// Purpose: writes out console to disk +//----------------------------------------------------------------------------- +void CConsolePanel::DumpConsoleTextToFile() +{ + const int CONDUMP_FILES_MAX_NUM = 1000; + + FileHandle_t handle; + bool found = false; + char szfile[ 512 ]; + + // we don't want to overwrite other condump.txt files + for ( int i = 0 ; i < CONDUMP_FILES_MAX_NUM ; ++i ) + { + _snprintf( szfile, sizeof(szfile), "condump%03d.txt", i ); + if ( !g_pFullFileSystem->FileExists(szfile) ) + { + found = true; + break; + } + } + + if ( !found ) + { + Print( "Can't condump! Too many existing condump output files in the gamedir!\n" ); + return; + } + + handle = g_pFullFileSystem->Open( szfile, "wb" ); + if ( handle != FILESYSTEM_INVALID_HANDLE ) + { + int pos = 0; + while (1) + { + wchar_t buf[512]; + m_pHistory->GetText(pos, buf, sizeof(buf)); + pos += sizeof(buf) / sizeof(wchar_t); + + // don't continue if none left + if (buf[0] == 0) + break; + + // convert to ansi + char ansi[512]; + g_pVGuiLocalize->ConvertUnicodeToANSI(buf, ansi, sizeof(ansi)); + + // write to disk + int len = strlen(ansi); + for (int i = 0; i < len; i++) + { + // preceed newlines with a return + if (ansi[i] == '\n') + { + char ret = '\r'; + g_pFullFileSystem->Write( &ret, 1, handle ); + } + + g_pFullFileSystem->Write( ansi + i, 1, handle ); + } + } + + g_pFullFileSystem->Close( handle ); + + Print( "console dumped to " ); + Print( szfile ); + Print( "\n" ); + } + else + { + Print( "Unable to condump to " ); + Print( szfile ); + Print( "\n" ); + } +} + + +//----------------------------------------------------------------------------- +// +// Console dialog starts here +// +//----------------------------------------------------------------------------- +CConsoleDialog::CConsoleDialog( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) : + BaseClass( pParent, pName ) +{ + // initialize dialog + SetVisible( false ); + SetTitle( "#Console_Title", true ); + m_pConsolePanel = new CConsolePanel( this, "ConsolePage", bStatusVersion ); + m_pConsolePanel->AddActionSignalTarget( this ); +} + +void CConsoleDialog::OnScreenSizeChanged( int iOldWide, int iOldTall ) +{ + BaseClass::OnScreenSizeChanged( iOldWide, iOldTall ); + + int sx, sy; + surface()->GetScreenSize( sx, sy ); + + int w, h; + GetSize( w, h ); + if ( w > sx || h > sy ) + { + if ( w > sx ) + { + w = sx; + } + if ( h > sy ) + { + h = sy; + } + + // Try and lower the size to match the screen bounds + SetSize( w, h ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: brings dialog to the fore +//----------------------------------------------------------------------------- +void CConsoleDialog::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, w, h; + GetClientArea( x, y, w, h ); + m_pConsolePanel->SetBounds( x, y, w, h ); +} + + +//----------------------------------------------------------------------------- +// Purpose: brings dialog to the fore +//----------------------------------------------------------------------------- +void CConsoleDialog::Activate() +{ + BaseClass::Activate(); + m_pConsolePanel->m_pEntry->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Hides the dialog +//----------------------------------------------------------------------------- +void CConsoleDialog::Hide() +{ + OnClose(); + m_pConsolePanel->Hide(); +} + + +//----------------------------------------------------------------------------- +// Close just hides the dialog +//----------------------------------------------------------------------------- +void CConsoleDialog::Close() +{ + Hide(); +} + + +//----------------------------------------------------------------------------- +// Submits commands +//----------------------------------------------------------------------------- +void CConsoleDialog::OnCommandSubmitted( const char *pCommand ) +{ + PostActionSignal( new KeyValues( "CommandSubmitted", "command", pCommand ) ); +} + + +//----------------------------------------------------------------------------- +// Chain to the page +//----------------------------------------------------------------------------- +void CConsoleDialog::Print( const char *pMessage ) +{ + m_pConsolePanel->Print( pMessage ); +} + +void CConsoleDialog::DPrint( const char *pMessage ) +{ + m_pConsolePanel->DPrint( pMessage ); +} + +void CConsoleDialog::ColorPrint( const Color& clr, const char *msg ) +{ + m_pConsolePanel->ColorPrint( clr, msg ); +} + +void CConsoleDialog::Clear() +{ + m_pConsolePanel->Clear( ); +} + +void CConsoleDialog::DumpConsoleTextToFile() +{ + m_pConsolePanel->DumpConsoleTextToFile( ); +} + + +void CConsoleDialog::OnKeyCodePressed( vgui::KeyCode code ) +{ + if ( code == KEY_XBUTTON_B ) + { + Hide(); + } + else + { + BaseClass::OnKeyCodePressed(code); + } +}
\ No newline at end of file |