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.

898 lines
26 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. char *UTIL_va(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. FileHandle_t fh = g_pFullFileSystem->Open( "cfg/config_default.cfg", "rb" );
  504. if (fh == FILESYSTEM_INVALID_HANDLE)
  505. return;
  506. // L4D: also unbind other keys
  507. engine->ClientCmd_Unrestricted( "unbindall\n" );
  508. int size = g_pFullFileSystem->Size(fh);
  509. CUtlBuffer buf( 0, size, CUtlBuffer::TEXT_BUFFER );
  510. g_pFullFileSystem->Read( buf.Base(), size, fh );
  511. g_pFullFileSystem->Close(fh);
  512. // Clear out all current bindings
  513. ClearBindItems();
  514. const char *data = (const char*)buf.Base();
  515. // loop through all the binding
  516. while ( data != NULL )
  517. {
  518. char cmd[64];
  519. data = UTIL_Parse( data, cmd, sizeof(cmd) );
  520. if ( cmd[ 0 ] == '\0' )
  521. break;
  522. if ( !Q_stricmp(cmd, "bind") ||
  523. !Q_stricmp(cmd, "cmd2 bind") )
  524. {
  525. // FIXME: If we ever support > 2 player splitscreen this will need to be reworked.
  526. int nJoyStick = 0;
  527. if ( !stricmp(cmd, "cmd2 bind") )
  528. {
  529. nJoyStick = 1;
  530. }
  531. // Key name
  532. char szKeyName[256];
  533. data = UTIL_Parse( data, szKeyName, sizeof(szKeyName) );
  534. if ( szKeyName[ 0 ] == '\0' )
  535. break; // Error
  536. char szBinding[256];
  537. data = UTIL_Parse( data, szBinding, sizeof(szBinding) );
  538. if ( szKeyName[ 0 ] == '\0' )
  539. break; // Error
  540. // Skip it if it's a bind for the other slit
  541. if ( nJoyStick != m_nSplitScreenUser )
  542. continue;
  543. // Find item
  544. KeyValues *item = GetItemForBinding( szBinding );
  545. if ( item )
  546. {
  547. // Bind it
  548. AddBinding( item, szKeyName );
  549. }
  550. }
  551. else
  552. {
  553. // L4D: Use Defaults also resets cvars listed in config_default.cfg
  554. CGameUIConVarRef var( cmd );
  555. if ( var.IsValid() )
  556. {
  557. char szValue[256] = "";
  558. data = UTIL_Parse( data, szValue, sizeof(szValue) );
  559. var.SetValue( szValue );
  560. }
  561. }
  562. }
  563. PostActionSignal(new KeyValues("ApplyButtonEnable"));
  564. // Make sure console and escape key are always valid
  565. KeyValues *item = GetItemForBinding( "toggleconsole" );
  566. if ( item )
  567. {
  568. // Bind it
  569. AddBinding( item, "`" );
  570. }
  571. item = GetItemForBinding( "cancelselect" );
  572. if ( item )
  573. {
  574. // Bind it
  575. AddBinding( item, "ESCAPE" );
  576. }
  577. }
  578. //-----------------------------------------------------------------------------
  579. // Purpose: User clicked on item: remember where last active row/column was
  580. //-----------------------------------------------------------------------------
  581. void COptionsSubKeyboard::ItemSelected(int itemID)
  582. {
  583. m_pKeyBindList->SetItemOfInterest(itemID);
  584. if (m_pKeyBindList->IsItemIDValid(itemID))
  585. {
  586. // find the details, see if we should be enabled/clear/whatever
  587. m_pSetBindingButton->SetEnabled(true);
  588. KeyValues *kv = m_pKeyBindList->GetItemData(itemID);
  589. if (kv)
  590. {
  591. const char *key = kv->GetString("Key", NULL);
  592. if (key && *key)
  593. {
  594. m_pClearBindingButton->SetEnabled(true);
  595. }
  596. else
  597. {
  598. m_pClearBindingButton->SetEnabled(false);
  599. }
  600. if (kv->GetInt("Header"))
  601. {
  602. m_pSetBindingButton->SetEnabled(false);
  603. }
  604. }
  605. }
  606. else
  607. {
  608. m_pSetBindingButton->SetEnabled(false);
  609. m_pClearBindingButton->SetEnabled(false);
  610. }
  611. }
  612. //-----------------------------------------------------------------------------
  613. // Purpose: called when the capture has finished
  614. //-----------------------------------------------------------------------------
  615. void COptionsSubKeyboard::Finish( ButtonCode_t code )
  616. {
  617. int r = m_pKeyBindList->GetItemOfInterest();
  618. // Retrieve clicked row and column
  619. m_pKeyBindList->EndCaptureMode( dc_arrow );
  620. // Find item for this row
  621. KeyValues *item = m_pKeyBindList->GetItemData(r);
  622. if ( item )
  623. {
  624. // Handle keys: but never rebind the escape key
  625. // Esc just exits bind mode silently
  626. if ( code != BUTTON_CODE_NONE && code != KEY_ESCAPE && code != BUTTON_CODE_INVALID )
  627. {
  628. // Bind the named key
  629. AddBinding( item, g_pInputSystem->ButtonCodeToString( code ) );
  630. PostActionSignal( new KeyValues( "ApplyButtonEnable" ) );
  631. }
  632. m_pKeyBindList->InvalidateItem(r);
  633. }
  634. m_pSetBindingButton->SetEnabled(true);
  635. m_pClearBindingButton->SetEnabled(true);
  636. }
  637. //-----------------------------------------------------------------------------
  638. // Purpose: Scans for captured key presses
  639. //-----------------------------------------------------------------------------
  640. void COptionsSubKeyboard::OnThink()
  641. {
  642. BaseClass::OnThink();
  643. if ( m_pKeyBindList->IsCapturing() )
  644. {
  645. ButtonCode_t code = BUTTON_CODE_INVALID;
  646. if ( engine->CheckDoneKeyTrapping( code ) )
  647. {
  648. Finish( code );
  649. }
  650. }
  651. }
  652. //-----------------------------------------------------------------------------
  653. // Purpose: Check for enter key and go into keybinding mode if it was pressed
  654. //-----------------------------------------------------------------------------
  655. void COptionsSubKeyboard::OnKeyCodePressed(vgui::KeyCode code)
  656. {
  657. // Enter key pressed and not already trapping next key/button press
  658. if ( !m_pKeyBindList->IsCapturing() )
  659. {
  660. // Grab which item was set as interesting
  661. int r = m_pKeyBindList->GetItemOfInterest();
  662. // Check that it's visible
  663. int x, y, w, h;
  664. bool visible = m_pKeyBindList->GetCellBounds(r, 1, x, y, w, h);
  665. if (visible)
  666. {
  667. if ( KEY_DELETE == code )
  668. {
  669. // find the current binding and remove it
  670. KeyValues *kv = m_pKeyBindList->GetItemData(r);
  671. const char *key = kv->GetString("Key", NULL);
  672. if (key && *key)
  673. {
  674. RemoveKeyFromBindItems(NULL, key);
  675. }
  676. m_pClearBindingButton->SetEnabled(false);
  677. m_pKeyBindList->InvalidateItem(r);
  678. PostActionSignal(new KeyValues("ApplyButtonEnable"));
  679. // message handled, don't pass on
  680. return;
  681. }
  682. }
  683. }
  684. // Allow base class to process message instead
  685. BaseClass::OnKeyCodePressed( code );
  686. }
  687. //-----------------------------------------------------------------------------
  688. // Purpose: advanced keyboard settings dialog
  689. //-----------------------------------------------------------------------------
  690. class COptionsSubKeyboardAdvancedDlg : public vgui::Frame
  691. {
  692. DECLARE_CLASS_SIMPLE( COptionsSubKeyboardAdvancedDlg, vgui::Frame );
  693. public:
  694. COptionsSubKeyboardAdvancedDlg( vgui::VPANEL hParent ) : BaseClass( NULL, NULL )
  695. {
  696. // parent is ignored, since we want look like we're steal focus from the parent (we'll become modal below)
  697. SetTitle("#GameUI_KeyboardAdvanced_Title", true);
  698. SetSize( 280, 140 );
  699. LoadControlSettings( "resource/OptionsSubKeyboardAdvancedDlg.res" );
  700. MoveToCenterOfScreen();
  701. SetSizeable( false );
  702. SetDeleteSelfOnClose( true );
  703. }
  704. virtual void Activate()
  705. {
  706. BaseClass::Activate();
  707. input()->SetAppModalSurface(GetVPanel());
  708. // reset the data
  709. CGameUIConVarRef con_enable( "con_enable" );
  710. if ( con_enable.IsValid() )
  711. {
  712. SetControlInt("ConsoleCheck", con_enable.GetInt() ? 1 : 0);
  713. }
  714. CGameUIConVarRef hud_fastswitch( "hud_fastswitch", true );
  715. if ( hud_fastswitch.IsValid() )
  716. {
  717. SetControlInt("FastSwitchCheck", hud_fastswitch.GetInt() ? 1 : 0);
  718. }
  719. }
  720. virtual void OnApplyData()
  721. {
  722. // apply data
  723. CGameUIConVarRef con_enable( "con_enable" );
  724. con_enable.SetValue( GetControlInt( "ConsoleCheck", 0 ) );
  725. CGameUIConVarRef hud_fastswitch( "hud_fastswitch", true );
  726. hud_fastswitch.SetValue( GetControlInt( "FastSwitchCheck", 0 ) );
  727. }
  728. virtual void OnCommand( const char *command )
  729. {
  730. if ( !stricmp(command, "OK") )
  731. {
  732. // apply the data
  733. OnApplyData();
  734. Close();
  735. }
  736. else
  737. {
  738. BaseClass::OnCommand( command );
  739. }
  740. }
  741. void OnKeyCodeTyped(KeyCode code)
  742. {
  743. // force ourselves to be closed if the escape key it pressed
  744. if (code == KEY_ESCAPE)
  745. {
  746. Close();
  747. }
  748. else
  749. {
  750. BaseClass::OnKeyCodeTyped(code);
  751. }
  752. }
  753. };
  754. //-----------------------------------------------------------------------------
  755. // Purpose: Open advanced keyboard options
  756. //-----------------------------------------------------------------------------
  757. void COptionsSubKeyboard::OpenKeyboardAdvancedDialog()
  758. {
  759. if (!m_OptionsSubKeyboardAdvancedDlg.Get())
  760. {
  761. m_OptionsSubKeyboardAdvancedDlg = new COptionsSubKeyboardAdvancedDlg(GetVParent());
  762. }
  763. m_OptionsSubKeyboardAdvancedDlg->Activate();
  764. }