diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/vgui2/vgui_controls/consoledialog.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/vgui2/vgui_controls/consoledialog.cpp')
| -rw-r--r-- | mp/src/vgui2/vgui_controls/consoledialog.cpp | 1256 |
1 files changed, 1256 insertions, 0 deletions
diff --git a/mp/src/vgui2/vgui_controls/consoledialog.cpp b/mp/src/vgui2/vgui_controls/consoledialog.cpp new file mode 100644 index 00000000..dba72dd8 --- /dev/null +++ b/mp/src/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 |