|
|
//===== Copyright � 1996-2005, 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 <ctype.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 ) { PostMessage( GetParent()->GetVPanel(), new KeyValues( "Command", "command", "submit" ) ); } 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: depending on our input mode will match the command or substrings in the command.
//-----------------------------------------------------------------------------
bool CConsolePanel::CommandMatchesText(const char *command, const char *text, bool bCheckSubstrings) { if (bCheckSubstrings) { int textLeft = Q_strlen( text ); int length = 0; char uprCommand[ 256 ]; char substring[ 256 ]; Q_strncpy( substring, text, sizeof( substring ) ); Q_strncpy( uprCommand, command, sizeof( uprCommand ) );
Q_strupr( uprCommand ); Q_strupr( substring );
char *strStart = substring; char *space; // split the search string based on spaces, keep searching for the substrings until we are out of text
do { space = Q_strstr( strStart, " " ); if ( space ) { *space = 0; // replace the space with an end of string char
}
if( !Q_strstr(uprCommand, strStart) ) { return false; } length = Q_strlen(strStart) + 1; // need to do an extra to account for the space
if( textLeft > length ) { textLeft -= length; strStart += length; } else // we hit the end of our substrings - abort
{ space = NULL; }
} while (space);
return true; } else if ( !strnicmp(text, command, Q_strlen(text))) // just try to match the whole string.
{ return true; } return false; }
//-----------------------------------------------------------------------------
// 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; bool bCheckSubstrings = false;
const char *space = strstr( text, " " ); if ( space ) { ConCommand *pCommand = FindAutoCompleteCommmandFromPartial( text ); if ( pCommand ) { 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() ); } } else { bCheckSubstrings = true; } } if ( bNormalBuild ) { // look through the command list for all matches
ICvar::Iterator iter( g_pCVar ); for ( iter.SetFirst() ; iter.IsValid() ; iter.Next() ) { ConCommandBase *cmd = iter.Get(); if ( cmd->IsFlagSet( FCVAR_DEVELOPMENTONLY ) || cmd->IsFlagSet( FCVAR_HIDDEN ) ) { continue; } if (CommandMatchesText(cmd->GetName(), text, bCheckSubstrings )) { // 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 ); } } }
// Now sort the list by command name
if ( m_CompletionList.Count() >= 2 ) { m_CompletionList.Sort( &CompletionItemCompare ); } } }
bool CConsolePanel::GetCompletionItemText(char *pDest, int completionIndex, int maxLen) { pDest[0] = 0;
if (m_CompletionList.IsValidIndex(completionIndex)) { CompletionItem *item = m_CompletionList[completionIndex]; Assert(item);
if ( !item->m_bIsCommand && item->m_pCommand ) { Q_strncpy(pDest, item->GetCommand(), maxLen ); } else { Q_strncpy(pDest, item->GetItemText(), maxLen ); } return true; } return false; }
//-----------------------------------------------------------------------------
// Purpose: auto completes current text
//-----------------------------------------------------------------------------
void CConsolePanel::OnAutoComplete(eCompletionType completionType) { 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 (completionType == COMPLETE_TYPE_REVERSE) { m_iNextCompletion -= 2; if (m_iNextCompletion < 0) { // loop around in reverse
m_iNextCompletion = m_CompletionList.Count() - 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;
char completedText[256];
// are we trying to tab-complete our command? We need to have at least two valid entries for that
if (completionType == COMPLETE_TYPE_COMMON_STRING && m_CompletionList.Count() > 1 ) { // see how many of our characters match between the first and last items in our list.
// this should be all of the common characters for all items in our list.
char lastMatchText[256]; GetCompletionItemText(completedText, 0, sizeof(completedText) - 2 ); GetCompletionItemText(lastMatchText, m_CompletionList.Count() - 1, sizeof(lastMatchText) - 2 );
unsigned int i = 0; // make sure that we aren't doing a sub-string match, we won't be able to tab-complete in that case.
if( !Q_strncasecmp(m_szPartialText, completedText, strlen(m_szPartialText)) && !Q_strncasecmp(m_szPartialText, lastMatchText, strlen(m_szPartialText)) ) { for( ; i < strlen(completedText); i++ ) { if( toupper( completedText[i] ) != toupper( lastMatchText[i] ) ) { break; } } }
// terminate where we differ
completedText[i] = 0; } else { GetCompletionItemText(completedText, m_iNextCompletion, sizeof(completedText) - 2 );
if ( !Q_strstr( completedText, " " ) ) { Q_strncat(completedText, " ", sizeof(completedText), COPY_ALL_CHARACTERS ); }
m_iNextCompletion++; }
// Only set our text if we actually changed something
if( strlen(completedText) > strlen(m_szPartialText) ) { m_pEntry->SetText(completedText); // only do this for tab completion, we don't want to mess up our cycling by rebuilding the completion list.
if( completionType == COMPLETE_TYPE_COMMON_STRING ) { OnTextChanged( m_pEntry ); } }
m_pEntry->GotoTextEnd(); m_pEntry->SelectNone(); }
//-----------------------------------------------------------------------------
// 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) { if (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)) { OnAutoComplete( COMPLETE_TYPE_REVERSE ); } else { OnAutoComplete( COMPLETE_TYPE_FORWARD ); } m_pEntry->RequestFocus(); } else if (code == KEY_DOWN) { OnAutoComplete( COMPLETE_TYPE_FORWARD ); m_pEntry->RequestFocus(); } else if (code == KEY_UP) { OnAutoComplete( COMPLETE_TYPE_REVERSE ); m_pEntry->RequestFocus(); } } }
//-----------------------------------------------------------------------------
// Purpose: modifies the style of our text entry if we are in substring mode.
//-----------------------------------------------------------------------------
void CConsolePanel::UpdateEntryStyle() { IScheme *pScheme = scheme()->GetIScheme( GetScheme() ); m_pEntry->SetBorder( pScheme->GetBorder("DepressedButtonBorder")); }
//-----------------------------------------------------------------------------
// 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_pEntry->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) ); m_pCompletionList->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) ); m_pSubmit->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) );
if ( true ) // make the console opaque
{ Color bgColor( 64,64,64, 255 ); SetBgColor( bgColor ); m_pHistory->SetBgColor( bgColor ); m_pEntry->SetBgColor( bgColor ); m_pCompletionList->SetBgColor( bgColor ); }
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
char *command = static_cast<char *>( stackalloc( (strlen( commandText ) + 1 ) * sizeof( char ) )); if ( command ) { memset( command, 0x0, strlen( commandText ) + 1 ); strncpy( command, commandText, strlen( commandText )); if ( command[ strlen( commandText ) -1 ] == ' ' ) { command[ strlen( commandText ) -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)) - 1; //-1 to compensate for null terminator
// 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();
static ConVarRef cv_vguipanel_active( "vgui_panel_active" ); static ConVarRef cv_console_window_open( "console_window_open" );
if ( !cv_console_window_open.GetBool() ) cv_vguipanel_active.SetValue( cv_vguipanel_active.GetInt() + 1 );
cv_console_window_open.SetValue( true ); }
//-----------------------------------------------------------------------------
// Hides the dialog
//-----------------------------------------------------------------------------
void CConsoleDialog::Hide() { static ConVarRef cv_vguipanel_active( "vgui_panel_active" ); static ConVarRef cv_console_window_open( "console_window_open" );
if ( cv_console_window_open.GetBool() ) cv_vguipanel_active.SetValue( cv_vguipanel_active.GetInt() - 1 );
cv_console_window_open.SetValue( false );
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( ); }
|