Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1371 lines
34 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "vgui_controls/consoledialog.h"
  8. #include "vgui/IInput.h"
  9. #include "vgui/IScheme.h"
  10. #include "vgui/IVGui.h"
  11. #include "vgui/ISurface.h"
  12. #include "vgui/ILocalize.h"
  13. #include "keyvalues.h"
  14. #include "vgui_controls/Button.h"
  15. #include "vgui/KeyCode.h"
  16. #include "vgui_controls/Menu.h"
  17. #include "vgui_controls/TextEntry.h"
  18. #include "vgui_controls/RichText.h"
  19. #include "tier1/convar.h"
  20. #include "tier1/convar_serverbounded.h"
  21. #include "icvar.h"
  22. #include "filesystem.h"
  23. #include <ctype.h>
  24. #include <stdlib.h>
  25. #if defined( _X360 )
  26. #include "xbox/xbox_win32stubs.h"
  27. #endif
  28. // memdbgon must be the last include file in a .cpp file!!!
  29. #include "tier0/memdbgon.h"
  30. using namespace vgui;
  31. //-----------------------------------------------------------------------------
  32. // Used by the autocompletion system
  33. //-----------------------------------------------------------------------------
  34. class CNonFocusableMenu : public Menu
  35. {
  36. DECLARE_CLASS_SIMPLE( CNonFocusableMenu, Menu );
  37. public:
  38. CNonFocusableMenu( Panel *parent, const char *panelName )
  39. : BaseClass( parent, panelName ),
  40. m_pFocus( 0 )
  41. {
  42. }
  43. void SetFocusPanel( Panel *panel )
  44. {
  45. m_pFocus = panel;
  46. }
  47. VPANEL GetCurrentKeyFocus()
  48. {
  49. if ( !m_pFocus )
  50. return GetVPanel();
  51. return m_pFocus->GetVPanel();
  52. }
  53. private:
  54. Panel *m_pFocus;
  55. };
  56. //-----------------------------------------------------------------------------
  57. // Purpose: forwards tab key presses up from the text entry so we can do autocomplete
  58. //-----------------------------------------------------------------------------
  59. class TabCatchingTextEntry : public TextEntry
  60. {
  61. public:
  62. TabCatchingTextEntry(Panel *parent, const char *name, VPANEL comp) : TextEntry(parent, name), m_pCompletionList( comp )
  63. {
  64. SetAllowNonAsciiCharacters( true );
  65. SetDragEnabled( true );
  66. }
  67. virtual void OnKeyCodeTyped(KeyCode code)
  68. {
  69. if (code == KEY_TAB)
  70. {
  71. GetParent()->OnKeyCodeTyped(code);
  72. }
  73. else if ( code == KEY_ENTER )
  74. {
  75. PostMessage( GetParent()->GetVPanel(), new KeyValues( "Command", "command", "submit" ) );
  76. }
  77. else
  78. {
  79. TextEntry::OnKeyCodeTyped(code);
  80. }
  81. }
  82. virtual void OnKillFocus()
  83. {
  84. if ( input()->GetFocus() != m_pCompletionList ) // if its not the completion window trying to steal our focus
  85. {
  86. PostMessage(GetParent(), new KeyValues("CloseCompletionList"));
  87. }
  88. }
  89. private:
  90. VPANEL m_pCompletionList;
  91. };
  92. // Things the user typed in and hit submit/return with
  93. CHistoryItem::CHistoryItem( void )
  94. {
  95. m_text = NULL;
  96. m_extraText = NULL;
  97. m_bHasExtra = false;
  98. }
  99. CHistoryItem::CHistoryItem( const char *text, const char *extra )
  100. {
  101. Assert( text );
  102. m_text = NULL;
  103. m_extraText = NULL;
  104. m_bHasExtra = false;
  105. SetText( text , extra );
  106. }
  107. CHistoryItem::CHistoryItem( const CHistoryItem& src )
  108. {
  109. m_text = NULL;
  110. m_extraText = NULL;
  111. m_bHasExtra = false;
  112. SetText( src.GetText(), src.GetExtra() );
  113. }
  114. CHistoryItem::~CHistoryItem( void )
  115. {
  116. delete[] m_text;
  117. delete[] m_extraText;
  118. m_text = NULL;
  119. }
  120. const char *CHistoryItem::GetText() const
  121. {
  122. if ( m_text )
  123. {
  124. return m_text;
  125. }
  126. else
  127. {
  128. return "";
  129. }
  130. }
  131. const char *CHistoryItem::GetExtra() const
  132. {
  133. if ( m_extraText )
  134. {
  135. return m_extraText;
  136. }
  137. else
  138. {
  139. return NULL;
  140. }
  141. }
  142. void CHistoryItem::SetText( const char *text, const char *extra )
  143. {
  144. delete[] m_text;
  145. int len = strlen( text ) + 1;
  146. m_text = new char[ len ];
  147. Q_memset( m_text, 0x0, len );
  148. Q_strncpy( m_text, text, len );
  149. if ( extra )
  150. {
  151. m_bHasExtra = true;
  152. delete[] m_extraText;
  153. int elen = strlen( extra ) + 1;
  154. m_extraText = new char[ elen ];
  155. Q_memset( m_extraText, 0x0, elen);
  156. Q_strncpy( m_extraText, extra, elen );
  157. }
  158. else
  159. {
  160. m_bHasExtra = false;
  161. }
  162. }
  163. //-----------------------------------------------------------------------------
  164. //
  165. // Console page completion item starts here
  166. //
  167. //-----------------------------------------------------------------------------
  168. CConsolePanel::CompletionItem::CompletionItem( void )
  169. {
  170. m_bIsCommand = true;
  171. m_pCommand = NULL;
  172. m_pText = NULL;
  173. }
  174. CConsolePanel::CompletionItem::CompletionItem( const CompletionItem& src )
  175. {
  176. m_bIsCommand = src.m_bIsCommand;
  177. m_pCommand = src.m_pCommand;
  178. if ( src.m_pText )
  179. {
  180. m_pText = new CHistoryItem( (const CHistoryItem& )src.m_pText );
  181. }
  182. else
  183. {
  184. m_pText = NULL;
  185. }
  186. }
  187. CConsolePanel::CompletionItem& CConsolePanel::CompletionItem::operator =( const CompletionItem& src )
  188. {
  189. if ( this == &src )
  190. return *this;
  191. m_bIsCommand = src.m_bIsCommand;
  192. m_pCommand = src.m_pCommand;
  193. if ( src.m_pText )
  194. {
  195. m_pText = new CHistoryItem( (const CHistoryItem& )*src.m_pText );
  196. }
  197. else
  198. {
  199. m_pText = NULL;
  200. }
  201. return *this;
  202. }
  203. CConsolePanel::CompletionItem::~CompletionItem( void )
  204. {
  205. if ( m_pText )
  206. {
  207. delete m_pText;
  208. m_pText = NULL;
  209. }
  210. }
  211. const char *CConsolePanel::CompletionItem::GetName() const
  212. {
  213. if ( m_bIsCommand )
  214. return m_pCommand->GetName();
  215. return m_pCommand ? m_pCommand->GetName() : GetCommand();
  216. }
  217. const char *CConsolePanel::CompletionItem::GetItemText( void )
  218. {
  219. static char text[256];
  220. text[0] = 0;
  221. if ( m_pText )
  222. {
  223. if ( m_pText->HasExtra() )
  224. {
  225. Q_snprintf( text, sizeof( text ), "%s %s", m_pText->GetText(), m_pText->GetExtra() );
  226. }
  227. else
  228. {
  229. Q_strncpy( text, m_pText->GetText(), sizeof( text ) );
  230. }
  231. }
  232. return text;
  233. }
  234. const char *CConsolePanel::CompletionItem::GetCommand( void ) const
  235. {
  236. static char text[256];
  237. text[0] = 0;
  238. if ( m_pText )
  239. {
  240. Q_strncpy( text, m_pText->GetText(), sizeof( text ) );
  241. }
  242. return text;
  243. }
  244. //-----------------------------------------------------------------------------
  245. //
  246. // Console page starts here
  247. //
  248. //-----------------------------------------------------------------------------
  249. //-----------------------------------------------------------------------------
  250. // Purpose: Constructor, destuctor
  251. //-----------------------------------------------------------------------------
  252. CConsolePanel::CConsolePanel( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) :
  253. BaseClass( pParent, pName ), m_bStatusVersion( bStatusVersion )
  254. {
  255. SetKeyBoardInputEnabled( true );
  256. if ( !m_bStatusVersion )
  257. {
  258. SetMinimumSize(100,100);
  259. }
  260. // create controls
  261. m_pHistory = new RichText(this, "ConsoleHistory");
  262. m_pHistory->SetAllowKeyBindingChainToParent( false );
  263. SETUP_PANEL( m_pHistory );
  264. m_pHistory->SetVerticalScrollbar( !m_bStatusVersion );
  265. if ( m_bStatusVersion )
  266. {
  267. m_pHistory->SetDrawOffsets( 3, 3 );
  268. }
  269. m_pHistory->GotoTextEnd();
  270. m_pSubmit = new Button(this, "ConsoleSubmit", "#Console_Submit");
  271. m_pSubmit->SetCommand("submit");
  272. m_pSubmit->SetVisible( !m_bStatusVersion );
  273. CNonFocusableMenu *pCompletionList = new CNonFocusableMenu( this, "CompletionList" );
  274. m_pCompletionList = pCompletionList;
  275. m_pCompletionList->SetVisible(false);
  276. m_pEntry = new TabCatchingTextEntry(this, "ConsoleEntry", m_pCompletionList->GetVPanel() );
  277. m_pEntry->AddActionSignalTarget(this);
  278. m_pEntry->SendNewLine(true);
  279. pCompletionList->SetFocusPanel( m_pEntry );
  280. // need to set up default colors, since ApplySchemeSettings won't be called until later
  281. m_PrintColor = Color(216, 222, 211, 255);
  282. m_DPrintColor = Color(196, 181, 80, 255);
  283. m_pEntry->SetTabPosition(1);
  284. m_bAutoCompleteMode = false;
  285. m_szPartialText[0] = 0;
  286. m_szPreviousPartialText[0]=0;
  287. // Add to global console list
  288. g_pCVar->InstallConsoleDisplayFunc( this );
  289. }
  290. //-----------------------------------------------------------------------------
  291. // Purpose: Destructor
  292. //-----------------------------------------------------------------------------
  293. CConsolePanel::~CConsolePanel()
  294. {
  295. ClearCompletionList();
  296. m_CommandHistory.Purge();
  297. g_pCVar->RemoveConsoleDisplayFunc( this );
  298. }
  299. //-----------------------------------------------------------------------------
  300. // Updates the completion list
  301. //-----------------------------------------------------------------------------
  302. void CConsolePanel::OnThink()
  303. {
  304. BaseClass::OnThink();
  305. if ( !IsVisible() )
  306. return;
  307. if ( !m_pCompletionList->IsVisible() )
  308. return;
  309. UpdateCompletionListPosition();
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Purpose: Clears the console
  313. //-----------------------------------------------------------------------------
  314. void CConsolePanel::Clear()
  315. {
  316. m_pHistory->SetText("");
  317. m_pHistory->GotoTextEnd();
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Purpose: color text print
  321. //-----------------------------------------------------------------------------
  322. void CConsolePanel::ColorPrint( const Color& clr, const char *msg )
  323. {
  324. if ( m_bStatusVersion )
  325. {
  326. Clear();
  327. }
  328. m_pHistory->InsertColorChange( clr );
  329. m_pHistory->InsertString( msg );
  330. }
  331. //-----------------------------------------------------------------------------
  332. // Purpose: normal text print
  333. //-----------------------------------------------------------------------------
  334. void CConsolePanel::Print(const char *msg)
  335. {
  336. ColorPrint( m_PrintColor, msg );
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Purpose: debug text print
  340. //-----------------------------------------------------------------------------
  341. void CConsolePanel::DPrint( const char *msg )
  342. {
  343. ColorPrint( m_DPrintColor, msg );
  344. }
  345. void CConsolePanel::ClearCompletionList()
  346. {
  347. int c = m_CompletionList.Count();
  348. int i;
  349. for ( i = c - 1; i >= 0; i-- )
  350. {
  351. delete m_CompletionList[ i ];
  352. }
  353. m_CompletionList.Purge();
  354. }
  355. static ConCommand *FindAutoCompleteCommmandFromPartial( const char *partial )
  356. {
  357. char command[ 256 ];
  358. Q_strncpy( command, partial, sizeof( command ) );
  359. char *space = Q_strstr( command, " " );
  360. if ( space )
  361. {
  362. *space = 0;
  363. }
  364. ConCommand *cmd = g_pCVar->FindCommand( command );
  365. if ( !cmd )
  366. return NULL;
  367. if ( !cmd->CanAutoComplete() )
  368. return NULL;
  369. return cmd;
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose: depending on our input mode will match the command or substrings in the command.
  373. //-----------------------------------------------------------------------------
  374. bool CConsolePanel::CommandMatchesText(const char *command, const char *text, bool bCheckSubstrings)
  375. {
  376. if (bCheckSubstrings)
  377. {
  378. int textLeft = Q_strlen( text );
  379. int length = 0;
  380. char uprCommand[ 256 ];
  381. char substring[ 256 ];
  382. Q_strncpy( substring, text, sizeof( substring ) );
  383. Q_strncpy( uprCommand, command, sizeof( uprCommand ) );
  384. Q_strupr( uprCommand );
  385. Q_strupr( substring );
  386. char *strStart = substring;
  387. char *space;
  388. // split the search string based on spaces, keep searching for the substrings until we are out of text
  389. do
  390. {
  391. space = Q_strstr( strStart, " " );
  392. if ( space )
  393. {
  394. *space = 0; // replace the space with an end of string char
  395. }
  396. if( !Q_strstr(uprCommand, strStart) )
  397. {
  398. return false;
  399. }
  400. length = Q_strlen(strStart) + 1; // need to do an extra to account for the space
  401. if( textLeft > length )
  402. {
  403. textLeft -= length;
  404. strStart += length;
  405. }
  406. else // we hit the end of our substrings - abort
  407. {
  408. space = NULL;
  409. }
  410. } while (space);
  411. return true;
  412. }
  413. else if ( !strnicmp(text, command, Q_strlen(text))) // just try to match the whole string.
  414. {
  415. return true;
  416. }
  417. return false;
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose: rebuilds the list of possible completions from the current entered text
  421. //-----------------------------------------------------------------------------
  422. void CConsolePanel::RebuildCompletionList(const char *text)
  423. {
  424. ClearCompletionList();
  425. // we need the length of the text for the partial string compares
  426. int len = Q_strlen(text);
  427. if ( len < 1 )
  428. {
  429. // Fill the completion list with history instead
  430. for ( int i = 0 ; i < m_CommandHistory.Count(); i++ )
  431. {
  432. CHistoryItem *item = &m_CommandHistory[ i ];
  433. CompletionItem *comp = new CompletionItem();
  434. m_CompletionList.AddToTail( comp );
  435. comp->m_bIsCommand = false;
  436. comp->m_pCommand = NULL;
  437. comp->m_pText = new CHistoryItem( *item );
  438. }
  439. return;
  440. }
  441. bool bNormalBuild = true;
  442. bool bCheckSubstrings = false;
  443. const char *space = strstr( text, " " );
  444. if ( space )
  445. {
  446. ConCommand *pCommand = FindAutoCompleteCommmandFromPartial( text );
  447. if ( pCommand )
  448. {
  449. bNormalBuild = false;
  450. CUtlVector< CUtlString > commands;
  451. int count = pCommand->AutoCompleteSuggest( text, commands );
  452. Assert( count <= COMMAND_COMPLETION_MAXITEMS );
  453. int i;
  454. for ( i = 0; i < count; i++ )
  455. {
  456. // match found, add to list
  457. CompletionItem *item = new CompletionItem();
  458. m_CompletionList.AddToTail( item );
  459. item->m_bIsCommand = false;
  460. item->m_pCommand = NULL;
  461. item->m_pText = new CHistoryItem( commands[ i ].String() );
  462. }
  463. }
  464. else
  465. {
  466. bCheckSubstrings = true;
  467. }
  468. }
  469. if ( bNormalBuild )
  470. {
  471. // look through the command list for all matches
  472. ICvar::Iterator iter( g_pCVar );
  473. for ( iter.SetFirst() ; iter.IsValid() ; iter.Next() )
  474. {
  475. ConCommandBase *cmd = iter.Get();
  476. if ( cmd->IsFlagSet( FCVAR_DEVELOPMENTONLY ) || cmd->IsFlagSet( FCVAR_HIDDEN ) )
  477. {
  478. continue;
  479. }
  480. if (CommandMatchesText(cmd->GetName(), text, bCheckSubstrings ))
  481. {
  482. // match found, add to list
  483. CompletionItem *item = new CompletionItem();
  484. m_CompletionList.AddToTail( item );
  485. item->m_pCommand = (ConCommandBase *)cmd;
  486. const char *tst = cmd->GetName();
  487. if ( !cmd->IsCommand() )
  488. {
  489. item->m_bIsCommand = false;
  490. ConVar *var = ( ConVar * )cmd;
  491. ConVar_ServerBounded *pBounded = dynamic_cast<ConVar_ServerBounded*>( var );
  492. if ( pBounded || var->IsFlagSet( FCVAR_NEVER_AS_STRING ) )
  493. {
  494. char strValue[512];
  495. int intVal = pBounded ? pBounded->GetInt() : var->GetInt();
  496. float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat();
  497. if ( floatVal == intVal )
  498. Q_snprintf( strValue, sizeof( strValue ), "%d", intVal );
  499. else
  500. Q_snprintf( strValue, sizeof( strValue ), "%f", floatVal );
  501. item->m_pText = new CHistoryItem( var->GetName(), strValue );
  502. }
  503. else
  504. {
  505. item->m_pText = new CHistoryItem( var->GetName(), var->GetString() );
  506. }
  507. }
  508. else
  509. {
  510. item->m_bIsCommand = true;
  511. item->m_pText = new CHistoryItem( tst );
  512. }
  513. }
  514. }
  515. // Now sort the list by command name
  516. if ( m_CompletionList.Count() >= 2 )
  517. {
  518. m_CompletionList.Sort( &CompletionItemCompare );
  519. }
  520. }
  521. }
  522. bool CConsolePanel::GetCompletionItemText(char *pDest, int completionIndex, int maxLen)
  523. {
  524. pDest[0] = 0;
  525. if (m_CompletionList.IsValidIndex(completionIndex))
  526. {
  527. CompletionItem *item = m_CompletionList[completionIndex];
  528. Assert(item);
  529. if ( !item->m_bIsCommand && item->m_pCommand )
  530. {
  531. Q_strncpy(pDest, item->GetCommand(), maxLen );
  532. }
  533. else
  534. {
  535. Q_strncpy(pDest, item->GetItemText(), maxLen );
  536. }
  537. return true;
  538. }
  539. return false;
  540. }
  541. //-----------------------------------------------------------------------------
  542. // Purpose: auto completes current text
  543. //-----------------------------------------------------------------------------
  544. void CConsolePanel::OnAutoComplete(eCompletionType completionType)
  545. {
  546. if (!m_bAutoCompleteMode)
  547. {
  548. // we're not in auto-complete mode, Start
  549. m_iNextCompletion = 0;
  550. m_bAutoCompleteMode = true;
  551. }
  552. // if we're in reverse, move back to before the current
  553. if (completionType == COMPLETE_TYPE_REVERSE)
  554. {
  555. m_iNextCompletion -= 2;
  556. if (m_iNextCompletion < 0)
  557. {
  558. // loop around in reverse
  559. m_iNextCompletion = m_CompletionList.Count() - 1;
  560. }
  561. }
  562. // get the next completion
  563. if (!m_CompletionList.IsValidIndex(m_iNextCompletion))
  564. {
  565. // loop completion list
  566. m_iNextCompletion = 0;
  567. }
  568. // make sure everything is still valid
  569. if (!m_CompletionList.IsValidIndex(m_iNextCompletion))
  570. return;
  571. char completedText[256];
  572. // are we trying to tab-complete our command? We need to have at least two valid entries for that
  573. if (completionType == COMPLETE_TYPE_COMMON_STRING &&
  574. m_CompletionList.Count() > 1 )
  575. {
  576. // see how many of our characters match between the first and last items in our list.
  577. // this should be all of the common characters for all items in our list.
  578. char lastMatchText[256];
  579. GetCompletionItemText(completedText, 0, sizeof(completedText) - 2 );
  580. GetCompletionItemText(lastMatchText, m_CompletionList.Count() - 1, sizeof(lastMatchText) - 2 );
  581. unsigned int i = 0;
  582. // make sure that we aren't doing a sub-string match, we won't be able to tab-complete in that case.
  583. if( !Q_strncasecmp(m_szPartialText, completedText, strlen(m_szPartialText)) && !Q_strncasecmp(m_szPartialText, lastMatchText, strlen(m_szPartialText)) )
  584. {
  585. for( ; i < strlen(completedText); i++ )
  586. {
  587. if( toupper( completedText[i] ) != toupper( lastMatchText[i] ) )
  588. {
  589. break;
  590. }
  591. }
  592. }
  593. // terminate where we differ
  594. completedText[i] = 0;
  595. }
  596. else
  597. {
  598. GetCompletionItemText(completedText, m_iNextCompletion, sizeof(completedText) - 2 );
  599. if ( !Q_strstr( completedText, " " ) )
  600. {
  601. Q_strncat(completedText, " ", sizeof(completedText), COPY_ALL_CHARACTERS );
  602. }
  603. m_iNextCompletion++;
  604. }
  605. // Only set our text if we actually changed something
  606. if( strlen(completedText) > strlen(m_szPartialText) )
  607. {
  608. m_pEntry->SetText(completedText);
  609. // only do this for tab completion, we don't want to mess up our cycling by rebuilding the completion list.
  610. if( completionType == COMPLETE_TYPE_COMMON_STRING )
  611. {
  612. OnTextChanged( m_pEntry );
  613. }
  614. }
  615. m_pEntry->GotoTextEnd();
  616. m_pEntry->SelectNone();
  617. }
  618. //-----------------------------------------------------------------------------
  619. // Purpose: Called whenever the user types text
  620. //-----------------------------------------------------------------------------
  621. void CConsolePanel::OnTextChanged(Panel *panel)
  622. {
  623. if (panel != m_pEntry)
  624. return;
  625. Q_strncpy( m_szPreviousPartialText, m_szPartialText, sizeof( m_szPreviousPartialText ) );
  626. // get the partial text the user type
  627. m_pEntry->GetText(m_szPartialText, sizeof(m_szPartialText));
  628. // see if they've hit the tilde key (which opens & closes the console)
  629. int len = Q_strlen(m_szPartialText);
  630. bool hitTilde = ( m_szPartialText[len - 1] == '~' || m_szPartialText[len - 1] == '`' ) ? true : false;
  631. bool altKeyDown = ( vgui::input()->IsKeyDown( KEY_LALT ) || vgui::input()->IsKeyDown( KEY_RALT ) ) ? true : false;
  632. bool ctrlKeyDown = ( vgui::input()->IsKeyDown( KEY_LCONTROL ) || vgui::input()->IsKeyDown( KEY_RCONTROL ) ) ? true : false;
  633. // Alt-Tilde toggles Japanese IME on/off!!!
  634. if ( ( len > 0 ) && hitTilde )
  635. {
  636. // Strip the last character (tilde)
  637. m_szPartialText[ len - 1 ] = L'\0';
  638. if( !altKeyDown && !ctrlKeyDown )
  639. {
  640. m_pEntry->SetText( "" );
  641. // close the console
  642. PostMessage( this, new KeyValues( "Close" ) );
  643. PostActionSignal( new KeyValues( "ClosedByHittingTilde" ) );
  644. }
  645. else
  646. {
  647. m_pEntry->SetText( m_szPartialText );
  648. }
  649. return;
  650. }
  651. // clear auto-complete state since the user has typed
  652. m_bAutoCompleteMode = false;
  653. RebuildCompletionList(m_szPartialText);
  654. // build the menu
  655. if ( m_CompletionList.Count() < 1 )
  656. {
  657. m_pCompletionList->SetVisible(false);
  658. }
  659. else
  660. {
  661. m_pCompletionList->SetVisible(true);
  662. m_pCompletionList->DeleteAllItems();
  663. const int MAX_MENU_ITEMS = 10;
  664. // add the first ten items to the list
  665. for (int i = 0; i < m_CompletionList.Count() && i < MAX_MENU_ITEMS; i++)
  666. {
  667. char text[256];
  668. text[0] = 0;
  669. if (i == MAX_MENU_ITEMS - 1)
  670. {
  671. Q_strncpy(text, "...", sizeof( text ) );
  672. }
  673. else
  674. {
  675. Assert( m_CompletionList[i] );
  676. Q_strncpy(text, m_CompletionList[i]->GetItemText(), sizeof( text ) );
  677. }
  678. KeyValues *kv = new KeyValues("CompletionCommand");
  679. kv->SetString("command",text);
  680. m_pCompletionList->AddMenuItem(text, kv, this);
  681. }
  682. UpdateCompletionListPosition();
  683. }
  684. RequestFocus();
  685. m_pEntry->RequestFocus();
  686. }
  687. //-----------------------------------------------------------------------------
  688. // Purpose: generic vgui command handler
  689. //-----------------------------------------------------------------------------
  690. void CConsolePanel::OnCommand(const char *command)
  691. {
  692. if ( !Q_stricmp( command, "Submit" ) )
  693. {
  694. // submit the entry as a console commmand
  695. char szCommand[256];
  696. m_pEntry->GetText(szCommand, sizeof(szCommand));
  697. PostActionSignal( new KeyValues( "CommandSubmitted", "command", szCommand ) );
  698. // add to the history
  699. Print("] ");
  700. Print(szCommand);
  701. Print("\n");
  702. // clear the field
  703. m_pEntry->SetText("");
  704. // clear the completion state
  705. OnTextChanged(m_pEntry);
  706. // always go the end of the buffer when the user has typed something
  707. m_pHistory->GotoTextEnd();
  708. // Add the command to the history
  709. char *extra = strchr(szCommand, ' ');
  710. if ( extra )
  711. {
  712. *extra = '\0';
  713. extra++;
  714. }
  715. if ( Q_strlen( szCommand ) > 0 )
  716. {
  717. AddToHistory( szCommand, extra );
  718. }
  719. m_pCompletionList->SetVisible(false);
  720. }
  721. else
  722. {
  723. BaseClass::OnCommand(command);
  724. }
  725. }
  726. //-----------------------------------------------------------------------------
  727. // Focus related methods
  728. //-----------------------------------------------------------------------------
  729. bool CConsolePanel::TextEntryHasFocus() const
  730. {
  731. return ( input()->GetFocus() == m_pEntry->GetVPanel() );
  732. }
  733. void CConsolePanel::TextEntryRequestFocus()
  734. {
  735. m_pEntry->RequestFocus();
  736. }
  737. //-----------------------------------------------------------------------------
  738. // Purpose: swallows tab key pressed
  739. //-----------------------------------------------------------------------------
  740. void CConsolePanel::OnKeyCodeTyped(KeyCode code)
  741. {
  742. BaseClass::OnKeyCodeTyped(code);
  743. // check for processing
  744. if ( TextEntryHasFocus() )
  745. {
  746. if (code == KEY_TAB)
  747. {
  748. if (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT))
  749. {
  750. OnAutoComplete( COMPLETE_TYPE_REVERSE );
  751. }
  752. else
  753. {
  754. OnAutoComplete( COMPLETE_TYPE_FORWARD );
  755. }
  756. m_pEntry->RequestFocus();
  757. }
  758. else if (code == KEY_DOWN)
  759. {
  760. OnAutoComplete( COMPLETE_TYPE_FORWARD );
  761. m_pEntry->RequestFocus();
  762. }
  763. else if (code == KEY_UP)
  764. {
  765. OnAutoComplete( COMPLETE_TYPE_REVERSE );
  766. m_pEntry->RequestFocus();
  767. }
  768. }
  769. }
  770. //-----------------------------------------------------------------------------
  771. // Purpose: modifies the style of our text entry if we are in substring mode.
  772. //-----------------------------------------------------------------------------
  773. void CConsolePanel::UpdateEntryStyle()
  774. {
  775. IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
  776. m_pEntry->SetBorder( pScheme->GetBorder("DepressedButtonBorder"));
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Purpose: lays out controls
  780. //-----------------------------------------------------------------------------
  781. void CConsolePanel::PerformLayout()
  782. {
  783. BaseClass::PerformLayout();
  784. // setup tab ordering
  785. GetFocusNavGroup().SetDefaultButton(m_pSubmit);
  786. IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
  787. m_pEntry->SetBorder(pScheme->GetBorder("DepressedButtonBorder"));
  788. m_pHistory->SetBorder(pScheme->GetBorder("DepressedButtonBorder"));
  789. // layout controls
  790. int wide, tall;
  791. GetSize(wide, tall);
  792. if ( !m_bStatusVersion )
  793. {
  794. const int inset = 8;
  795. const int entryHeight = 24;
  796. const int topHeight = 4;
  797. const int entryInset = 4;
  798. const int submitWide = 64;
  799. const int submitInset = 7; // x inset to pull the submit button away from the frame grab
  800. m_pHistory->SetPos(inset, inset + topHeight);
  801. m_pHistory->SetSize(wide - (inset * 2), tall - (entryInset * 2 + inset * 2 + topHeight + entryHeight));
  802. m_pHistory->InvalidateLayout();
  803. int nSubmitXPos = wide - ( inset + submitWide + submitInset );
  804. m_pSubmit->SetPos( nSubmitXPos, tall - (entryInset * 2 + entryHeight));
  805. m_pSubmit->SetSize( submitWide, entryHeight);
  806. m_pEntry->SetPos( inset, tall - (entryInset * 2 + entryHeight) );
  807. m_pEntry->SetSize( nSubmitXPos - entryInset - 2 * inset, entryHeight);
  808. }
  809. else
  810. {
  811. const int inset = 2;
  812. int entryWidth = wide / 2;
  813. if ( wide > 400 )
  814. {
  815. entryWidth = 200;
  816. }
  817. m_pEntry->SetBounds( inset, inset, entryWidth, tall - 2 * inset );
  818. m_pHistory->SetBounds( inset + entryWidth + inset, inset, ( wide - entryWidth ) - inset, tall - 2 * inset );
  819. }
  820. UpdateCompletionListPosition();
  821. }
  822. //-----------------------------------------------------------------------------
  823. // Purpose: Sets the position of the completion list popup
  824. //-----------------------------------------------------------------------------
  825. void CConsolePanel::UpdateCompletionListPosition()
  826. {
  827. int ex, ey;
  828. m_pEntry->GetPos(ex, ey);
  829. if ( !m_bStatusVersion )
  830. {
  831. // Position below text entry
  832. ey += m_pEntry->GetTall();
  833. }
  834. else
  835. {
  836. // Position above text entry
  837. int menuwide, menutall;
  838. m_pCompletionList->GetSize( menuwide, menutall );
  839. ey -= ( menutall + 4 );
  840. }
  841. LocalToScreen( ex, ey );
  842. m_pCompletionList->SetPos( ex, ey );
  843. if ( m_pCompletionList->IsVisible() )
  844. {
  845. m_pEntry->RequestFocus();
  846. MoveToFront();
  847. m_pCompletionList->MoveToFront();
  848. }
  849. }
  850. //-----------------------------------------------------------------------------
  851. // Purpose: Closes the completion list
  852. //-----------------------------------------------------------------------------
  853. void CConsolePanel::CloseCompletionList()
  854. {
  855. m_pCompletionList->SetVisible(false);
  856. }
  857. //-----------------------------------------------------------------------------
  858. // Purpose: sets up colors
  859. //-----------------------------------------------------------------------------
  860. void CConsolePanel::ApplySchemeSettings(IScheme *pScheme)
  861. {
  862. BaseClass::ApplySchemeSettings(pScheme);
  863. m_PrintColor = GetSchemeColor("Console.TextColor", pScheme);
  864. m_DPrintColor = GetSchemeColor("Console.DevTextColor", pScheme);
  865. m_pHistory->SetFont( pScheme->GetFont( "ConsoleText", IsProportional() ) );
  866. m_pEntry->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) );
  867. m_pCompletionList->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) );
  868. m_pSubmit->SetFont( pScheme->GetFont( "DefaultSmall", IsProportional() ) );
  869. if ( true ) // make the console opaque
  870. {
  871. Color bgColor( 64,64,64, 255 );
  872. SetBgColor( bgColor );
  873. m_pHistory->SetBgColor( bgColor );
  874. m_pEntry->SetBgColor( bgColor );
  875. m_pCompletionList->SetBgColor( bgColor );
  876. }
  877. InvalidateLayout();
  878. }
  879. //-----------------------------------------------------------------------------
  880. // Purpose: Handles autocompletion menu input
  881. //-----------------------------------------------------------------------------
  882. void CConsolePanel::OnMenuItemSelected(const char *command)
  883. {
  884. if ( strstr( command, "..." ) ) // stop the menu going away if you click on ...
  885. {
  886. m_pCompletionList->SetVisible( true );
  887. }
  888. else
  889. {
  890. m_pEntry->SetText(command);
  891. m_pEntry->GotoTextEnd();
  892. m_pEntry->InsertChar(' ');
  893. m_pEntry->GotoTextEnd();
  894. }
  895. }
  896. void CConsolePanel::Hide()
  897. {
  898. OnClose();
  899. m_iNextCompletion = 0;
  900. RebuildCompletionList("");
  901. }
  902. void CConsolePanel::AddToHistory( const char *commandText, const char *extraText )
  903. {
  904. // Newest at end, oldest at head
  905. while ( m_CommandHistory.Count() >= MAX_HISTORY_ITEMS )
  906. {
  907. // Remove from head until size is reasonable
  908. m_CommandHistory.Remove( 0 );
  909. }
  910. // strip the space off the end of the command before adding it to the history
  911. char *command = static_cast<char *>( stackalloc( (strlen( commandText ) + 1 ) * sizeof( char ) ));
  912. if ( command )
  913. {
  914. memset( command, 0x0, strlen( commandText ) + 1 );
  915. strncpy( command, commandText, strlen( commandText ));
  916. if ( command[ strlen( commandText ) -1 ] == ' ' )
  917. {
  918. command[ strlen( commandText ) -1 ] = '\0';
  919. }
  920. }
  921. // strip the quotes off the extra text
  922. char *extra = NULL;
  923. if ( extraText )
  924. {
  925. extra = static_cast<char *>( malloc( (strlen( extraText ) + 1 ) * sizeof( char ) ));
  926. if ( extra )
  927. {
  928. memset( extra, 0x0, strlen( extraText ) + 1 );
  929. strncpy( extra, extraText, strlen( extraText )); // +1 to dodge the starting quote
  930. // Strip trailing spaces
  931. int i = strlen( extra ) - 1;
  932. while ( i >= 0 && // Check I before referencing i == -1 into the extra array!
  933. extra[ i ] == ' ' )
  934. {
  935. extra[ i ] = '\0';
  936. i--;
  937. }
  938. }
  939. }
  940. // If it's already there, then remove since we'll add it to the end instead
  941. CHistoryItem *item = NULL;
  942. for ( int i = m_CommandHistory.Count() - 1; i >= 0; i-- )
  943. {
  944. item = &m_CommandHistory[ i ];
  945. if ( !item )
  946. continue;
  947. if ( stricmp( item->GetText(), command ) )
  948. continue;
  949. if ( extra || item->GetExtra() )
  950. {
  951. if ( !extra || !item->GetExtra() )
  952. continue;
  953. // stricmp so two commands with the same starting text get added
  954. if ( stricmp( item->GetExtra(), extra ) )
  955. continue;
  956. }
  957. m_CommandHistory.Remove( i );
  958. }
  959. item = &m_CommandHistory[ m_CommandHistory.AddToTail() ];
  960. Assert( item );
  961. item->SetText( command, extra );
  962. m_iNextCompletion = 0;
  963. RebuildCompletionList( m_szPartialText );
  964. free( extra );
  965. }
  966. void CConsolePanel::GetConsoleText( char *pchText, size_t bufSize ) const
  967. {
  968. wchar_t *temp = new wchar_t[ bufSize ];
  969. m_pHistory->GetText( 0, temp, bufSize * sizeof( wchar_t ) );
  970. g_pVGuiLocalize->ConvertUnicodeToANSI( temp, pchText, bufSize );
  971. delete[] temp;
  972. }
  973. //-----------------------------------------------------------------------------
  974. // Purpose: writes out console to disk
  975. //-----------------------------------------------------------------------------
  976. void CConsolePanel::DumpConsoleTextToFile()
  977. {
  978. const int CONDUMP_FILES_MAX_NUM = 1000;
  979. FileHandle_t handle;
  980. bool found = false;
  981. char szfile[ 512 ];
  982. // we don't want to overwrite other condump.txt files
  983. for ( int i = 0 ; i < CONDUMP_FILES_MAX_NUM ; ++i )
  984. {
  985. _snprintf( szfile, sizeof(szfile), "condump%03d.txt", i );
  986. if ( !g_pFullFileSystem->FileExists(szfile) )
  987. {
  988. found = true;
  989. break;
  990. }
  991. }
  992. if ( !found )
  993. {
  994. Print( "Can't condump! Too many existing condump output files in the gamedir!\n" );
  995. return;
  996. }
  997. handle = g_pFullFileSystem->Open( szfile, "wb" );
  998. if ( handle != FILESYSTEM_INVALID_HANDLE )
  999. {
  1000. int pos = 0;
  1001. while (1)
  1002. {
  1003. wchar_t buf[512];
  1004. m_pHistory->GetText(pos, buf, sizeof(buf));
  1005. pos += (sizeof(buf) / sizeof(wchar_t)) - 1; //-1 to compensate for null terminator
  1006. // don't continue if none left
  1007. if (buf[0] == 0)
  1008. break;
  1009. // convert to ansi
  1010. char ansi[512];
  1011. g_pVGuiLocalize->ConvertUnicodeToANSI(buf, ansi, sizeof(ansi));
  1012. // write to disk
  1013. int len = strlen(ansi);
  1014. for (int i = 0; i < len; i++)
  1015. {
  1016. // preceed newlines with a return
  1017. if (ansi[i] == '\n')
  1018. {
  1019. char ret = '\r';
  1020. g_pFullFileSystem->Write( &ret, 1, handle );
  1021. }
  1022. g_pFullFileSystem->Write( ansi + i, 1, handle );
  1023. }
  1024. }
  1025. g_pFullFileSystem->Close( handle );
  1026. Print( "console dumped to " );
  1027. Print( szfile );
  1028. Print( "\n" );
  1029. }
  1030. else
  1031. {
  1032. Print( "Unable to condump to " );
  1033. Print( szfile );
  1034. Print( "\n" );
  1035. }
  1036. }
  1037. //-----------------------------------------------------------------------------
  1038. //
  1039. // Console dialog starts here
  1040. //
  1041. //-----------------------------------------------------------------------------
  1042. CConsoleDialog::CConsoleDialog( vgui::Panel *pParent, const char *pName, bool bStatusVersion ) :
  1043. BaseClass( pParent, pName )
  1044. {
  1045. // initialize dialog
  1046. SetVisible( false );
  1047. SetTitle( "#Console_Title", true );
  1048. m_pConsolePanel = new CConsolePanel( this, "ConsolePage", bStatusVersion );
  1049. m_pConsolePanel->AddActionSignalTarget( this );
  1050. }
  1051. void CConsoleDialog::OnScreenSizeChanged( int iOldWide, int iOldTall )
  1052. {
  1053. BaseClass::OnScreenSizeChanged( iOldWide, iOldTall );
  1054. int sx, sy;
  1055. surface()->GetScreenSize( sx, sy );
  1056. int w, h;
  1057. GetSize( w, h );
  1058. if ( w > sx || h > sy )
  1059. {
  1060. if ( w > sx )
  1061. {
  1062. w = sx;
  1063. }
  1064. if ( h > sy )
  1065. {
  1066. h = sy;
  1067. }
  1068. // Try and lower the size to match the screen bounds
  1069. SetSize( w, h );
  1070. }
  1071. }
  1072. //-----------------------------------------------------------------------------
  1073. // Purpose: brings dialog to the fore
  1074. //-----------------------------------------------------------------------------
  1075. void CConsoleDialog::PerformLayout()
  1076. {
  1077. BaseClass::PerformLayout();
  1078. int x, y, w, h;
  1079. GetClientArea( x, y, w, h );
  1080. m_pConsolePanel->SetBounds( x, y, w, h );
  1081. }
  1082. //-----------------------------------------------------------------------------
  1083. // Purpose: brings dialog to the fore
  1084. //-----------------------------------------------------------------------------
  1085. void CConsoleDialog::Activate()
  1086. {
  1087. BaseClass::Activate();
  1088. m_pConsolePanel->m_pEntry->RequestFocus();
  1089. static ConVarRef cv_vguipanel_active( "vgui_panel_active" );
  1090. static ConVarRef cv_console_window_open( "console_window_open" );
  1091. if ( !cv_console_window_open.GetBool() )
  1092. cv_vguipanel_active.SetValue( cv_vguipanel_active.GetInt() + 1 );
  1093. cv_console_window_open.SetValue( true );
  1094. }
  1095. //-----------------------------------------------------------------------------
  1096. // Hides the dialog
  1097. //-----------------------------------------------------------------------------
  1098. void CConsoleDialog::Hide()
  1099. {
  1100. static ConVarRef cv_vguipanel_active( "vgui_panel_active" );
  1101. static ConVarRef cv_console_window_open( "console_window_open" );
  1102. if ( cv_console_window_open.GetBool() )
  1103. cv_vguipanel_active.SetValue( cv_vguipanel_active.GetInt() - 1 );
  1104. cv_console_window_open.SetValue( false );
  1105. OnClose();
  1106. m_pConsolePanel->Hide();
  1107. }
  1108. //-----------------------------------------------------------------------------
  1109. // Close just hides the dialog
  1110. //-----------------------------------------------------------------------------
  1111. void CConsoleDialog::Close()
  1112. {
  1113. Hide();
  1114. }
  1115. //-----------------------------------------------------------------------------
  1116. // Submits commands
  1117. //-----------------------------------------------------------------------------
  1118. void CConsoleDialog::OnCommandSubmitted( const char *pCommand )
  1119. {
  1120. PostActionSignal( new KeyValues( "CommandSubmitted", "command", pCommand ) );
  1121. }
  1122. //-----------------------------------------------------------------------------
  1123. // Chain to the page
  1124. //-----------------------------------------------------------------------------
  1125. void CConsoleDialog::Print( const char *pMessage )
  1126. {
  1127. m_pConsolePanel->Print( pMessage );
  1128. }
  1129. void CConsoleDialog::DPrint( const char *pMessage )
  1130. {
  1131. m_pConsolePanel->DPrint( pMessage );
  1132. }
  1133. void CConsoleDialog::ColorPrint( const Color& clr, const char *msg )
  1134. {
  1135. m_pConsolePanel->ColorPrint( clr, msg );
  1136. }
  1137. void CConsoleDialog::Clear()
  1138. {
  1139. m_pConsolePanel->Clear( );
  1140. }
  1141. void CConsoleDialog::DumpConsoleTextToFile()
  1142. {
  1143. m_pConsolePanel->DumpConsoleTextToFile( );
  1144. }