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.

893 lines
25 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "optionssubkeyboard.h"
  7. #include "engineinterface.h"
  8. #include "vcontrolslistpanel.h"
  9. #include "vgui_controls/Button.h"
  10. #include "vgui_controls/Label.h"
  11. #include "vgui_controls/ListPanel.h"
  12. #include "vgui_controls/QueryBox.h"
  13. #include "vgui/Cursor.h"
  14. #include "vgui/IVGui.h"
  15. #include "vgui/ISurface.h"
  16. #include "tier1/keyvalues.h"
  17. #include "tier1/convar.h"
  18. #include "vgui/KeyCode.h"
  19. #include "vgui/MouseCode.h"
  20. #include "vgui/ISystem.h"
  21. #include "vgui/IInput.h"
  22. #include "filesystem.h"
  23. #include "tier1/utlbuffer.h"
  24. #include "IGameUIFuncs.h"
  25. #include "vstdlib/ikeyvaluessystem.h"
  26. #include "tier2/tier2.h"
  27. #include "inputsystem/iinputsystem.h"
  28. #include "gameui_util.h"
  29. // memdbgon must be the last include file in a .cpp file!!!
  30. #include "tier0/memdbgon.h"
  31. using namespace vgui;
  32. //-----------------------------------------------------------------------------
  33. // Purpose: Constructor
  34. //-----------------------------------------------------------------------------
  35. COptionsSubKeyboard::COptionsSubKeyboard(vgui::Panel *parent) : PropertyPage(parent, NULL)
  36. {
  37. Q_memset( m_Bindings, 0, sizeof( m_Bindings ));
  38. m_nSplitScreenUser = 0;
  39. // For joystick buttons, controls which user are binding/unbinding
  40. if ( !IsGameConsole() )
  41. {
  42. //HACK HACK: Probably the entire gameui needs to have a splitscrene context for which player the settings apply to, but this is only
  43. // on the PC...
  44. static CGameUIConVarRef in_forceuser( "in_forceuser" );
  45. if ( in_forceuser.IsValid() )
  46. {
  47. m_nSplitScreenUser = clamp( in_forceuser.GetInt(), 0, 1 );
  48. }
  49. else
  50. {
  51. m_nSplitScreenUser = MAX( 0, engine->GetActiveSplitScreenPlayerSlot() );
  52. }
  53. }
  54. // create the key bindings list
  55. CreateKeyBindingList();
  56. // Store all current key bindings
  57. SaveCurrentBindings();
  58. // Parse default descriptions
  59. ParseActionDescriptions();
  60. m_pSetBindingButton = new Button(this, "ChangeKeyButton", "");
  61. m_pClearBindingButton = new Button(this, "ClearKeyButton", "");
  62. LoadControlSettings("Resource/OptionsSubKeyboard.res");
  63. m_pSetBindingButton->SetEnabled(false);
  64. m_pClearBindingButton->SetEnabled(false);
  65. }
  66. //-----------------------------------------------------------------------------
  67. // Purpose: Destructor
  68. //-----------------------------------------------------------------------------
  69. COptionsSubKeyboard::~COptionsSubKeyboard()
  70. {
  71. DeleteSavedBindings();
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose: reloads current keybinding
  75. //-----------------------------------------------------------------------------
  76. void COptionsSubKeyboard::OnResetData()
  77. {
  78. // Populate list based on current settings
  79. FillInCurrentBindings();
  80. if ( IsVisible() )
  81. {
  82. m_pKeyBindList->SetSelectedItem(0);
  83. }
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose: saves out keybinding changes
  87. //-----------------------------------------------------------------------------
  88. void COptionsSubKeyboard::OnApplyChanges()
  89. {
  90. ApplyAllBindings();
  91. }
  92. //-----------------------------------------------------------------------------
  93. // Purpose: Create key bindings list control
  94. //-----------------------------------------------------------------------------
  95. void COptionsSubKeyboard::CreateKeyBindingList()
  96. {
  97. // Create the control
  98. m_pKeyBindList = new VControlsListPanel(this, "listpanel_keybindlist");
  99. }
  100. //-----------------------------------------------------------------------------
  101. // Purpose: binds double-clicking or hitting enter in the keybind list to changing the key
  102. //-----------------------------------------------------------------------------
  103. void COptionsSubKeyboard::OnKeyCodeTyped(vgui::KeyCode code)
  104. {
  105. if (code == KEY_ENTER)
  106. {
  107. OnCommand("ChangeKey");
  108. }
  109. else
  110. {
  111. BaseClass::OnKeyCodeTyped(code);
  112. }
  113. }
  114. //-----------------------------------------------------------------------------
  115. // Purpose: command handler
  116. //-----------------------------------------------------------------------------
  117. void COptionsSubKeyboard::OnCommand( const char *command )
  118. {
  119. if ( !stricmp( command, "Defaults" ) )
  120. {
  121. // open a box asking if we want to restore defaults
  122. QueryBox *box = new QueryBox("#GameUI_KeyboardSettings", "#GameUI_KeyboardSettingsText");
  123. box->AddActionSignalTarget(this);
  124. box->SetOKCommand(new KeyValues("Command", "command", "DefaultsOK"));
  125. box->DoModal();
  126. }
  127. else if ( !stricmp(command, "DefaultsOK"))
  128. {
  129. // Restore defaults from default keybindings file
  130. FillInDefaultBindings();
  131. m_pKeyBindList->RequestFocus();
  132. }
  133. else if ( !m_pKeyBindList->IsCapturing() && !stricmp( command, "ChangeKey" ) )
  134. {
  135. m_pKeyBindList->StartCaptureMode(dc_blank);
  136. }
  137. else if ( !m_pKeyBindList->IsCapturing() && !stricmp( command, "ClearKey" ) )
  138. {
  139. // OnKeyCodePressed( ButtonCodeToJoystickButtonCode( KEY_DELETE, CL4DBasePanel::GetSingleton().GetLastActiveUserId() ) );
  140. OnKeyCodePressed( KEY_DELETE ); // <<< PC only code, no need for joystick management
  141. m_pKeyBindList->RequestFocus();
  142. }
  143. else if ( !stricmp(command, "Advanced") )
  144. {
  145. OpenKeyboardAdvancedDialog();
  146. }
  147. else
  148. {
  149. BaseClass::OnCommand( command );
  150. }
  151. }
  152. const char *UTIL_Parse( const char *data, char *token, int sizeofToken )
  153. {
  154. data = engine->ParseFile( data, token, sizeofToken );
  155. return data;
  156. }
  157. static char *UTIL_CopyString( const char *in )
  158. {
  159. int len = strlen( in ) + 1;
  160. char *out = new char[ len ];
  161. Q_strncpy( out, in, len );
  162. return out;
  163. }
  164. const char *UTIL_va(PRINTF_FORMAT_STRING const char *format, ...)
  165. {
  166. va_list argptr;
  167. static char string[4][1024];
  168. static int curstring = 0;
  169. curstring = ( curstring + 1 ) % 4;
  170. va_start (argptr, format);
  171. Q_vsnprintf( string[curstring], 1024, format, argptr );
  172. va_end (argptr);
  173. return string[curstring];
  174. }
  175. //-----------------------------------------------------------------------------
  176. // Purpose:
  177. //-----------------------------------------------------------------------------
  178. void COptionsSubKeyboard::ParseActionDescriptions( void )
  179. {
  180. char szBinding[256];
  181. char szDescription[256];
  182. KeyValues *item;
  183. // Load the default keys list
  184. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  185. if ( !g_pFullFileSystem->ReadFile( "scripts/kb_act.lst", NULL, buf ) )
  186. return;
  187. const char *data = (const char*)buf.Base();
  188. int sectionIndex = 0;
  189. char token[512];
  190. while ( 1 )
  191. {
  192. data = UTIL_Parse( data, token, sizeof(token) );
  193. // Done.
  194. if ( strlen( token ) <= 0 )
  195. break;
  196. Q_strncpy( szBinding, token, sizeof( szBinding ) );
  197. data = UTIL_Parse( data, token, sizeof(token) );
  198. if ( strlen(token) <= 0 )
  199. {
  200. break;
  201. }
  202. Q_strncpy(szDescription, token, sizeof( szDescription ) );
  203. // Skip '======' rows
  204. if ( szDescription[ 0 ] != '=' )
  205. {
  206. // Flag as special header row if binding is "blank"
  207. if (!stricmp(szBinding, "blank"))
  208. {
  209. // add header item
  210. int nColumn1 = 286;
  211. int nColumn2 = 128;
  212. if ( IsProportional() )
  213. {
  214. nColumn1 = vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), nColumn1 );
  215. nColumn2 = vgui::scheme()->GetProportionalScaledValueEx( GetScheme(), nColumn2 );
  216. }
  217. m_pKeyBindList->AddSection(++sectionIndex, szDescription);
  218. m_pKeyBindList->AddColumnToSection(sectionIndex, "Action", szDescription, SectionedListPanel::COLUMN_BRIGHT, nColumn1 );
  219. m_pKeyBindList->AddColumnToSection(sectionIndex, "Key", "#GameUI_KeyButton", SectionedListPanel::COLUMN_BRIGHT, nColumn2 );
  220. }
  221. else
  222. {
  223. // Create a new: blank item
  224. item = new KeyValues( "Item" );
  225. // fill in data
  226. item->SetString("Action", szDescription);
  227. item->SetString("Binding", szBinding);
  228. item->SetString("Key", "");
  229. // Add to list
  230. m_pKeyBindList->AddItem(sectionIndex, item);
  231. item->deleteThis();
  232. }
  233. }
  234. }
  235. }
  236. //-----------------------------------------------------------------------------
  237. // Purpose: Search current data set for item which has the specified binding string
  238. // Input : *binding - string to find
  239. // Output : KeyValues or NULL on failure
  240. //-----------------------------------------------------------------------------
  241. KeyValues *COptionsSubKeyboard::GetItemForBinding( const char *binding )
  242. {
  243. static int bindingSymbol = KeyValuesSystem()->GetSymbolForString("Binding");
  244. // Loop through all items
  245. for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++)
  246. {
  247. KeyValues *item = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i));
  248. if ( !item )
  249. continue;
  250. KeyValues *bindingItem = item->FindKey(bindingSymbol);
  251. const char *bindString = bindingItem->GetString();
  252. // Check the "Binding" key
  253. if (!stricmp(bindString, binding))
  254. return item;
  255. }
  256. // Didn't find it
  257. return NULL;
  258. }
  259. //-----------------------------------------------------------------------------
  260. // Purpose: Bind the specified keyname to the specified item
  261. // Input : *item - Item to which to add the key
  262. // *keyname - The key to be added
  263. //-----------------------------------------------------------------------------
  264. void COptionsSubKeyboard::AddBinding( KeyValues *item, const char *keyname )
  265. {
  266. // See if it's already there as a binding
  267. if ( !stricmp( item->GetString( "Key", "" ), keyname ) )
  268. return;
  269. // Make sure it doesn't live anywhere
  270. RemoveKeyFromBindItems( item, keyname );
  271. const char *binding = item->GetString( "Binding", "" );
  272. // Loop through all the key bindings and set all entries that have
  273. // the same binding. This allows us to have multiple entries pointing
  274. // to the same binding.
  275. for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++)
  276. {
  277. KeyValues *curitem = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i));
  278. if ( !curitem )
  279. continue;
  280. const char *curbinding = curitem->GetString( "Binding", "" );
  281. if (!stricmp(curbinding, binding))
  282. {
  283. curitem->SetString( "Key", keyname );
  284. m_pKeyBindList->InvalidateItem(i);
  285. }
  286. }
  287. }
  288. //-----------------------------------------------------------------------------
  289. // Purpose: Remove all keys from list
  290. //-----------------------------------------------------------------------------
  291. void COptionsSubKeyboard::ClearBindItems( void )
  292. {
  293. for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++)
  294. {
  295. KeyValues *item = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i));
  296. if ( !item )
  297. continue;
  298. // Reset keys
  299. item->SetString( "Key", "" );
  300. m_pKeyBindList->InvalidateItem(i);
  301. }
  302. m_pKeyBindList->InvalidateLayout();
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose: Remove all instances of the specified key from bindings
  306. //-----------------------------------------------------------------------------
  307. void COptionsSubKeyboard::RemoveKeyFromBindItems( KeyValues *org_item, const char *key )
  308. {
  309. Assert( key && key[ 0 ] );
  310. if ( !key || !key[ 0 ] )
  311. return;
  312. int len = Q_strlen( key );
  313. char *pszKey = new char[len + 1];
  314. if ( !pszKey )
  315. return;
  316. Q_memcpy( pszKey, key, len+1 );
  317. for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++)
  318. {
  319. KeyValues *item = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i));
  320. if ( !item )
  321. continue;
  322. // If it's bound to the primary: then remove it
  323. if ( !stricmp( pszKey, item->GetString( "Key", "" ) ) )
  324. {
  325. bool bClearEntry = true;
  326. if ( org_item )
  327. {
  328. // Only clear it out if the actual binding isn't the same. This allows
  329. // us to have the same key bound to multiple entries in the keybinding list
  330. // if they point to the same command.
  331. const char *org_binding = org_item->GetString( "Binding", "" );
  332. const char *binding = item->GetString( "Binding", "" );
  333. if ( !stricmp( org_binding, binding ) )
  334. {
  335. bClearEntry = false;
  336. }
  337. }
  338. if ( bClearEntry )
  339. {
  340. item->SetString( "Key", "" );
  341. m_pKeyBindList->InvalidateItem(i);
  342. }
  343. }
  344. }
  345. delete [] pszKey;
  346. // Make sure the display is up to date
  347. m_pKeyBindList->InvalidateLayout();
  348. }
  349. //-----------------------------------------------------------------------------
  350. // Purpose: Ask the engine for all bindings and set up the list
  351. //-----------------------------------------------------------------------------
  352. void COptionsSubKeyboard::FillInCurrentBindings( void )
  353. {
  354. // reset the unbind list
  355. // we only unbind keys used by the normal config items (not custom binds)
  356. m_KeysToUnbind.RemoveAll();
  357. // Clear any current settings
  358. ClearBindItems();
  359. bool bJoystick = false;
  360. CGameUIConVarRef var( "joystick" );
  361. if ( var.IsValid() )
  362. {
  363. bJoystick = var.GetBool();
  364. }
  365. for ( int i = 0; i < BUTTON_CODE_LAST; i++ )
  366. {
  367. ButtonCode_t bc = ( ButtonCode_t )i;
  368. bool bIsJoystickCode = IsJoystickCode( bc );
  369. // Skip Joystick buttons for the "other" user
  370. if ( bIsJoystickCode && GetJoystickForCode( bc ) != m_nSplitScreenUser )
  371. continue;
  372. // Look up binding
  373. const char *binding = gameuifuncs->GetBindingForButtonCode( bc );
  374. if ( !binding )
  375. continue;
  376. // See if there is an item for this one?
  377. KeyValues *item = GetItemForBinding( binding );
  378. if ( item )
  379. {
  380. // Bind it by name
  381. const char *keyName = g_pInputSystem->ButtonCodeToString( bc );
  382. // Already in list, means user had two keys bound to this item. We'll only note the first one we encounter
  383. char const *currentKey = item->GetString( "Key", "" );
  384. if ( currentKey && currentKey[ 0 ] )
  385. {
  386. ButtonCode_t currentBC = (ButtonCode_t)gameuifuncs->GetButtonCodeForBind( currentKey );
  387. // If we're using a joystick, joystick bindings override keyboard ones
  388. bool bShouldOverride = bJoystick && bIsJoystickCode && !IsJoystickCode(currentBC);
  389. if ( !bShouldOverride )
  390. continue;
  391. // Remove the key we're about to override from the unbinding list
  392. m_KeysToUnbind.FindAndRemove( currentBC );
  393. }
  394. AddBinding( item, keyName );
  395. // remember to apply unbinding of this key when we apply
  396. m_KeysToUnbind.AddToTail( bc );
  397. }
  398. }
  399. }
  400. //-----------------------------------------------------------------------------
  401. // Purpose: Clean up memory used by saved bindings
  402. //-----------------------------------------------------------------------------
  403. void COptionsSubKeyboard::DeleteSavedBindings( void )
  404. {
  405. for ( int i = 0; i < BUTTON_CODE_LAST; i++ )
  406. {
  407. if ( m_Bindings[ i ].binding )
  408. {
  409. delete[] m_Bindings[ i ].binding;
  410. m_Bindings[ i ].binding = NULL;
  411. }
  412. }
  413. }
  414. //-----------------------------------------------------------------------------
  415. // Purpose: Copy all bindings into save array
  416. //-----------------------------------------------------------------------------
  417. void COptionsSubKeyboard::SaveCurrentBindings( void )
  418. {
  419. DeleteSavedBindings();
  420. for (int i = 0; i < BUTTON_CODE_LAST; i++)
  421. {
  422. const char *binding = gameuifuncs->GetBindingForButtonCode( (ButtonCode_t)i );
  423. if ( !binding || !binding[0])
  424. continue;
  425. // Copy the binding string
  426. m_Bindings[ i ].binding = UTIL_CopyString( binding );
  427. }
  428. }
  429. //-----------------------------------------------------------------------------
  430. // Purpose: Tells the engine to bind a key
  431. //-----------------------------------------------------------------------------
  432. void COptionsSubKeyboard::BindKey( ButtonCode_t bc, const char *binding )
  433. {
  434. char const *pszKeyName = g_pInputSystem->ButtonCodeToString( bc );
  435. Assert( pszKeyName );
  436. if ( !pszKeyName || !*pszKeyName )
  437. return;
  438. int nSlot = GetJoystickForCode( bc );
  439. engine->ClientCmd_Unrestricted( UTIL_va( "cmd%d bind \"%s\" \"%s\"\n", nSlot + 1, pszKeyName, binding ) );
  440. }
  441. //-----------------------------------------------------------------------------
  442. // Purpose: Tells the engine to unbind a key
  443. //-----------------------------------------------------------------------------
  444. void COptionsSubKeyboard::UnbindKey( ButtonCode_t bc )
  445. {
  446. char const *pszKeyName = g_pInputSystem->ButtonCodeToString( bc );
  447. Assert( pszKeyName );
  448. if ( !pszKeyName || !*pszKeyName )
  449. return;
  450. int nSlot = GetJoystickForCode( bc );
  451. engine->ClientCmd_Unrestricted( UTIL_va( "cmd%d unbind \"%s\"\n", nSlot + 1, pszKeyName ) );
  452. }
  453. //-----------------------------------------------------------------------------
  454. // Purpose: Go through list and bind specified keys to actions
  455. //-----------------------------------------------------------------------------
  456. void COptionsSubKeyboard::ApplyAllBindings( void )
  457. {
  458. // unbind everything that the user unbound
  459. for (int i = 0; i < m_KeysToUnbind.Count(); i++)
  460. {
  461. ButtonCode_t bc = m_KeysToUnbind[ i ];
  462. UnbindKey( bc );
  463. }
  464. m_KeysToUnbind.RemoveAll();
  465. // free binding memory
  466. DeleteSavedBindings();
  467. for (int i = 0; i < m_pKeyBindList->GetItemCount(); i++)
  468. {
  469. KeyValues *item = m_pKeyBindList->GetItemData(m_pKeyBindList->GetItemIDFromRow(i));
  470. if ( !item )
  471. continue;
  472. // See if it has a binding
  473. const char *binding = item->GetString( "Binding", "" );
  474. if ( !binding || !binding[ 0 ] )
  475. continue;
  476. const char *keyname;
  477. // Check main binding
  478. keyname = item->GetString( "Key", "" );
  479. if ( keyname && keyname[ 0 ] )
  480. {
  481. ButtonCode_t code = g_pInputSystem->StringToButtonCode( keyname );
  482. if ( IsJoystickCode( code ) )
  483. {
  484. code = ButtonCodeToJoystickButtonCode( code, m_nSplitScreenUser );
  485. }
  486. // Tell the engine
  487. BindKey( code, binding );
  488. if ( code != BUTTON_CODE_INVALID )
  489. {
  490. m_Bindings[ code ].binding = UTIL_CopyString( binding );
  491. }
  492. }
  493. }
  494. // Now exec their custom bindings
  495. engine->ClientCmd_Unrestricted( "exec userconfig.cfg\nhost_writeconfig\n" );
  496. }
  497. //-----------------------------------------------------------------------------
  498. // Purpose: Read in defaults from game's default config file and populate list
  499. // using those defaults
  500. //-----------------------------------------------------------------------------
  501. void COptionsSubKeyboard::FillInDefaultBindings( void )
  502. {
  503. CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
  504. if ( !g_pFullFileSystem->ReadFile( "cfg/config_default.cfg", NULL, buf ) )
  505. return;
  506. // L4D: also unbind other keys
  507. engine->ClientCmd_Unrestricted( "unbindall\n" );
  508. // Clear out all current bindings
  509. ClearBindItems();
  510. const char *data = (const char*)buf.Base();
  511. // loop through all the binding
  512. while ( data != NULL )
  513. {
  514. char cmd[64];
  515. data = UTIL_Parse( data, cmd, sizeof(cmd) );
  516. if ( cmd[ 0 ] == '\0' )
  517. break;
  518. if ( !Q_stricmp(cmd, "bind") ||
  519. !Q_stricmp(cmd, "cmd2 bind") )
  520. {
  521. // FIXME: If we ever support > 2 player splitscreen this will need to be reworked.
  522. int nJoyStick = 0;
  523. if ( !stricmp(cmd, "cmd2 bind") )
  524. {
  525. nJoyStick = 1;
  526. }
  527. // Key name
  528. char szKeyName[256];
  529. data = UTIL_Parse( data, szKeyName, sizeof(szKeyName) );
  530. if ( szKeyName[ 0 ] == '\0' )
  531. break; // Error
  532. char szBinding[256];
  533. data = UTIL_Parse( data, szBinding, sizeof(szBinding) );
  534. if ( szKeyName[ 0 ] == '\0' )
  535. break; // Error
  536. // Skip it if it's a bind for the other slit
  537. if ( nJoyStick != m_nSplitScreenUser )
  538. continue;
  539. // Find item
  540. KeyValues *item = GetItemForBinding( szBinding );
  541. if ( item )
  542. {
  543. // Bind it
  544. AddBinding( item, szKeyName );
  545. }
  546. }
  547. else
  548. {
  549. // L4D: Use Defaults also resets cvars listed in config_default.cfg
  550. CGameUIConVarRef var( cmd );
  551. if ( var.IsValid() )
  552. {
  553. char szValue[256] = "";
  554. data = UTIL_Parse( data, szValue, sizeof(szValue) );
  555. var.SetValue( szValue );
  556. }
  557. }
  558. }
  559. PostActionSignal(new KeyValues("ApplyButtonEnable"));
  560. // Make sure console and escape key are always valid
  561. KeyValues *item = GetItemForBinding( "toggleconsole" );
  562. if ( item )
  563. {
  564. // Bind it
  565. AddBinding( item, "`" );
  566. }
  567. item = GetItemForBinding( "cancelselect" );
  568. if ( item )
  569. {
  570. // Bind it
  571. AddBinding( item, "ESCAPE" );
  572. }
  573. }
  574. //-----------------------------------------------------------------------------
  575. // Purpose: User clicked on item: remember where last active row/column was
  576. //-----------------------------------------------------------------------------
  577. void COptionsSubKeyboard::ItemSelected(int itemID)
  578. {
  579. m_pKeyBindList->SetItemOfInterest(itemID);
  580. if (m_pKeyBindList->IsItemIDValid(itemID))
  581. {
  582. // find the details, see if we should be enabled/clear/whatever
  583. m_pSetBindingButton->SetEnabled(true);
  584. KeyValues *kv = m_pKeyBindList->GetItemData(itemID);
  585. if (kv)
  586. {
  587. const char *key = kv->GetString("Key", NULL);
  588. if (key && *key)
  589. {
  590. m_pClearBindingButton->SetEnabled(true);
  591. }
  592. else
  593. {
  594. m_pClearBindingButton->SetEnabled(false);
  595. }
  596. if (kv->GetInt("Header"))
  597. {
  598. m_pSetBindingButton->SetEnabled(false);
  599. }
  600. }
  601. }
  602. else
  603. {
  604. m_pSetBindingButton->SetEnabled(false);
  605. m_pClearBindingButton->SetEnabled(false);
  606. }
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Purpose: called when the capture has finished
  610. //-----------------------------------------------------------------------------
  611. void COptionsSubKeyboard::Finish( ButtonCode_t code )
  612. {
  613. int r = m_pKeyBindList->GetItemOfInterest();
  614. // Retrieve clicked row and column
  615. m_pKeyBindList->EndCaptureMode( dc_arrow );
  616. // Find item for this row
  617. KeyValues *item = m_pKeyBindList->GetItemData(r);
  618. if ( item )
  619. {
  620. // Handle keys: but never rebind the escape key
  621. // Esc just exits bind mode silently
  622. if ( code != BUTTON_CODE_NONE && code != KEY_ESCAPE && code != BUTTON_CODE_INVALID )
  623. {
  624. // Bind the named key
  625. AddBinding( item, g_pInputSystem->ButtonCodeToString( code ) );
  626. PostActionSignal( new KeyValues( "ApplyButtonEnable" ) );
  627. }
  628. m_pKeyBindList->InvalidateItem(r);
  629. }
  630. m_pSetBindingButton->SetEnabled(true);
  631. m_pClearBindingButton->SetEnabled(true);
  632. }
  633. //-----------------------------------------------------------------------------
  634. // Purpose: Scans for captured key presses
  635. //-----------------------------------------------------------------------------
  636. void COptionsSubKeyboard::OnThink()
  637. {
  638. BaseClass::OnThink();
  639. if ( m_pKeyBindList->IsCapturing() )
  640. {
  641. ButtonCode_t code = BUTTON_CODE_INVALID;
  642. if ( engine->CheckDoneKeyTrapping( code ) )
  643. {
  644. Finish( code );
  645. }
  646. }
  647. }
  648. //-----------------------------------------------------------------------------
  649. // Purpose: Check for enter key and go into keybinding mode if it was pressed
  650. //-----------------------------------------------------------------------------
  651. void COptionsSubKeyboard::OnKeyCodePressed(vgui::KeyCode code)
  652. {
  653. // Enter key pressed and not already trapping next key/button press
  654. if ( !m_pKeyBindList->IsCapturing() )
  655. {
  656. // Grab which item was set as interesting
  657. int r = m_pKeyBindList->GetItemOfInterest();
  658. // Check that it's visible
  659. int x, y, w, h;
  660. bool visible = m_pKeyBindList->GetCellBounds(r, 1, x, y, w, h);
  661. if (visible)
  662. {
  663. if ( KEY_DELETE == code )
  664. {
  665. // find the current binding and remove it
  666. KeyValues *kv = m_pKeyBindList->GetItemData(r);
  667. const char *key = kv->GetString("Key", NULL);
  668. if (key && *key)
  669. {
  670. RemoveKeyFromBindItems(NULL, key);
  671. }
  672. m_pClearBindingButton->SetEnabled(false);
  673. m_pKeyBindList->InvalidateItem(r);
  674. PostActionSignal(new KeyValues("ApplyButtonEnable"));
  675. // message handled, don't pass on
  676. return;
  677. }
  678. }
  679. }
  680. // Allow base class to process message instead
  681. BaseClass::OnKeyCodePressed( code );
  682. }
  683. //-----------------------------------------------------------------------------
  684. // Purpose: advanced keyboard settings dialog
  685. //-----------------------------------------------------------------------------
  686. class COptionsSubKeyboardAdvancedDlg : public vgui::Frame
  687. {
  688. DECLARE_CLASS_SIMPLE( COptionsSubKeyboardAdvancedDlg, vgui::Frame );
  689. public:
  690. explicit COptionsSubKeyboardAdvancedDlg( vgui::VPANEL hParent ) : BaseClass( NULL, NULL )
  691. {
  692. // parent is ignored, since we want look like we're steal focus from the parent (we'll become modal below)
  693. SetTitle("#GameUI_KeyboardAdvanced_Title", true);
  694. SetSize( 280, 140 );
  695. LoadControlSettings( "resource/OptionsSubKeyboardAdvancedDlg.res" );
  696. MoveToCenterOfScreen();
  697. SetSizeable( false );
  698. SetDeleteSelfOnClose( true );
  699. }
  700. virtual void Activate()
  701. {
  702. BaseClass::Activate();
  703. input()->SetAppModalSurface(GetVPanel());
  704. // reset the data
  705. CGameUIConVarRef con_enable( "con_enable" );
  706. if ( con_enable.IsValid() )
  707. {
  708. SetControlInt("ConsoleCheck", con_enable.GetInt() ? 1 : 0);
  709. }
  710. CGameUIConVarRef hud_fastswitch( "hud_fastswitch", true );
  711. if ( hud_fastswitch.IsValid() )
  712. {
  713. SetControlInt("FastSwitchCheck", hud_fastswitch.GetInt() ? 1 : 0);
  714. }
  715. }
  716. virtual void OnApplyData()
  717. {
  718. // apply data
  719. CGameUIConVarRef con_enable( "con_enable" );
  720. con_enable.SetValue( GetControlInt( "ConsoleCheck", 0 ) );
  721. CGameUIConVarRef hud_fastswitch( "hud_fastswitch", true );
  722. hud_fastswitch.SetValue( GetControlInt( "FastSwitchCheck", 0 ) );
  723. }
  724. virtual void OnCommand( const char *command )
  725. {
  726. if ( !stricmp(command, "OK") )
  727. {
  728. // apply the data
  729. OnApplyData();
  730. Close();
  731. }
  732. else
  733. {
  734. BaseClass::OnCommand( command );
  735. }
  736. }
  737. void OnKeyCodeTyped(KeyCode code)
  738. {
  739. // force ourselves to be closed if the escape key it pressed
  740. if (code == KEY_ESCAPE)
  741. {
  742. Close();
  743. }
  744. else
  745. {
  746. BaseClass::OnKeyCodeTyped(code);
  747. }
  748. }
  749. };
  750. //-----------------------------------------------------------------------------
  751. // Purpose: Open advanced keyboard options
  752. //-----------------------------------------------------------------------------
  753. void COptionsSubKeyboard::OpenKeyboardAdvancedDialog()
  754. {
  755. if (!m_OptionsSubKeyboardAdvancedDlg.Get())
  756. {
  757. m_OptionsSubKeyboardAdvancedDlg = new COptionsSubKeyboardAdvancedDlg(GetVParent());
  758. }
  759. m_OptionsSubKeyboardAdvancedDlg->Activate();
  760. }