PageRenderTime 101ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/radiant/preferences.cpp

https://gitlab.com/illwieckz/netradiant
C++ | 970 lines | 709 code | 181 blank | 80 comment | 82 complexity | d2dfaf290d0e47863658d313a52d1433 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0
  1. /*
  2. Copyright (C) 1999-2006 Id Software, Inc. and contributors.
  3. For a list of contributors, see the accompanying CONTRIBUTORS file.
  4. This file is part of GtkRadiant.
  5. GtkRadiant is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2 of the License, or
  8. (at your option) any later version.
  9. GtkRadiant is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with GtkRadiant; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. */
  17. //
  18. // User preferences
  19. //
  20. // Leonardo Zide (leo@lokigames.com)
  21. //
  22. #include "preferences.h"
  23. #include "globaldefs.h"
  24. #include <gtk/gtk.h>
  25. #include "environment.h"
  26. #include "debugging/debugging.h"
  27. #include "generic/callback.h"
  28. #include "math/vector.h"
  29. #include "string/string.h"
  30. #include "stream/stringstream.h"
  31. #include "os/file.h"
  32. #include "os/path.h"
  33. #include "os/dir.h"
  34. #include "gtkutil/filechooser.h"
  35. #include "gtkutil/messagebox.h"
  36. #include "cmdlib.h"
  37. #include "error.h"
  38. #include "console.h"
  39. #include "xywindow.h"
  40. #include "mainframe.h"
  41. #include "qe3.h"
  42. #include "gtkdlgs.h"
  43. void Global_constructPreferences( PreferencesPage& page ){
  44. page.appendCheckBox( "Console", "Enable Logging", g_Console_enableLogging );
  45. }
  46. void Interface_constructPreferences( PreferencesPage& page ){
  47. #if GDEF_OS_WINDOWS
  48. page.appendCheckBox( "", "Default Text Editor", g_TextEditor_useWin32Editor );
  49. #else
  50. {
  51. ui::CheckButton use_custom = page.appendCheckBox( "Text Editor", "Custom", g_TextEditor_useCustomEditor );
  52. ui::Widget custom_editor = page.appendPathEntry( "Text Editor Command", g_TextEditor_editorCommand, true );
  53. Widget_connectToggleDependency( custom_editor, use_custom );
  54. }
  55. #endif
  56. }
  57. void Mouse_constructPreferences( PreferencesPage& page ){
  58. {
  59. const char* buttons[] = { "2 button", "3 button", };
  60. page.appendRadio( "Mouse Type", g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) );
  61. }
  62. page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick );
  63. }
  64. void Mouse_constructPage( PreferenceGroup& group ){
  65. PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) );
  66. Mouse_constructPreferences( page );
  67. }
  68. void Mouse_registerPreferencesPage(){
  69. PreferencesDialog_addInterfacePage( makeCallbackF(Mouse_constructPage) );
  70. }
  71. /*!
  72. =========================================================
  73. Games selection dialog
  74. =========================================================
  75. */
  76. #include <map>
  77. #include <uilib/uilib.h>
  78. inline const char* xmlAttr_getName( xmlAttrPtr attr ){
  79. return reinterpret_cast<const char*>( attr->name );
  80. }
  81. inline const char* xmlAttr_getValue( xmlAttrPtr attr ){
  82. return reinterpret_cast<const char*>( attr->children->content );
  83. }
  84. CGameDescription::CGameDescription( xmlDocPtr pDoc, const CopiedString& gameFile ){
  85. // read the user-friendly game name
  86. xmlNodePtr pNode = pDoc->children;
  87. while ( strcmp( (const char*)pNode->name, "game" ) && pNode != 0 )
  88. {
  89. pNode = pNode->next;
  90. }
  91. if ( !pNode ) {
  92. Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL );
  93. }
  94. for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next )
  95. {
  96. m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) );
  97. }
  98. {
  99. StringOutputStream path( 256 );
  100. path << DataPath_get() << "gamepacks/" << gameFile.c_str() << "/";
  101. mGameToolsPath = path.c_str();
  102. }
  103. ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) );
  104. mGameFile = gameFile;
  105. {
  106. GameDescription::iterator i = m_gameDescription.find( "type" );
  107. if ( i == m_gameDescription.end() ) {
  108. globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>( pDoc->URL ) << "'\n";
  109. // default
  110. mGameType = "q3";
  111. }
  112. else
  113. {
  114. mGameType = ( *i ).second.c_str();
  115. }
  116. }
  117. }
  118. void CGameDescription::Dump(){
  119. globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n";
  120. for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i )
  121. {
  122. globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n";
  123. }
  124. }
  125. CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
  126. #include "warnings.h"
  127. #include "stream/textfilestream.h"
  128. #include "container/array.h"
  129. #include "xml/ixml.h"
  130. #include "xml/xmlparser.h"
  131. #include "xml/xmlwriter.h"
  132. #include "preferencedictionary.h"
  133. #include "stringio.h"
  134. const char* const PREFERENCES_VERSION = "1.0";
  135. bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){
  136. bool ret = false;
  137. TextFileInputStream file( filename );
  138. if ( !file.failed() ) {
  139. XMLStreamParser parser( file );
  140. XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION );
  141. parser.exportXML( importer );
  142. ret = true;
  143. }
  144. int l = strlen( cmdline_prefix );
  145. for ( int i = 1; i < g_argc - 1; ++i )
  146. {
  147. if ( g_argv[i][0] == '-' ) {
  148. if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) {
  149. if ( g_argv[i][l + 1] == '-' ) {
  150. preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] );
  151. }
  152. }
  153. ++i;
  154. }
  155. }
  156. return ret;
  157. }
  158. bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){
  159. TextFileOutputStream file( filename );
  160. if ( !file.failed() ) {
  161. XMLStreamWriter writer( file );
  162. XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION );
  163. exporter.exportXML( writer );
  164. return true;
  165. }
  166. return false;
  167. }
  168. bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
  169. Array<char> tmpName( filename, filename + strlen( filename ) + 1 + 3 );
  170. *( tmpName.end() - 4 ) = 'T';
  171. *( tmpName.end() - 3 ) = 'M';
  172. *( tmpName.end() - 2 ) = 'P';
  173. *( tmpName.end() - 1 ) = '\0';
  174. return Preferences_Save( preferences, tmpName.data() )
  175. && ( !file_exists( filename ) || file_remove( filename ) )
  176. && file_move( tmpName.data(), filename );
  177. }
  178. struct LogConsole {
  179. static void Export(const Callback<void(bool)> &returnz) {
  180. returnz(g_Console_enableLogging);
  181. }
  182. static void Import(bool value) {
  183. g_Console_enableLogging = value;
  184. Sys_LogFile(g_Console_enableLogging);
  185. }
  186. };
  187. void RegisterGlobalPreferences( PreferenceSystem& preferences ){
  188. preferences.registerPreference( "gamefile", make_property_string( g_GamesDialog.m_sGameFile ) );
  189. preferences.registerPreference( "gamePrompt", make_property_string( g_GamesDialog.m_bGamePrompt ) );
  190. preferences.registerPreference( "log console", make_property_string<LogConsole>() );
  191. }
  192. PreferenceDictionary g_global_preferences;
  193. void GlobalPreferences_Init(){
  194. RegisterGlobalPreferences( g_global_preferences );
  195. }
  196. void CGameDialog::LoadPrefs(){
  197. // load global .pref file
  198. StringOutputStream strGlobalPref( 256 );
  199. strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
  200. globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
  201. if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
  202. globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
  203. }
  204. }
  205. void CGameDialog::SavePrefs(){
  206. StringOutputStream strGlobalPref( 256 );
  207. strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
  208. globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
  209. if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
  210. globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
  211. }
  212. }
  213. void CGameDialog::DoGameDialog(){
  214. // show the UI
  215. DoModal();
  216. // we save the prefs file
  217. SavePrefs();
  218. }
  219. void CGameDialog::GameFileImport( int value ){
  220. m_nComboSelect = value;
  221. // use value to set m_sGameFile
  222. std::list<CGameDescription *>::iterator iGame = mGames.begin();
  223. int i;
  224. for ( i = 0; i < value; i++ )
  225. {
  226. ++iGame;
  227. }
  228. if ( ( *iGame )->mGameFile != m_sGameFile ) {
  229. m_sGameFile = ( *iGame )->mGameFile;
  230. PreferencesDialog_restartRequired( "Selected Game" );
  231. }
  232. }
  233. void CGameDialog::GameFileExport( const Callback<void(int)> & importCallback ) const {
  234. // use m_sGameFile to set value
  235. std::list<CGameDescription *>::const_iterator iGame;
  236. int i = 0;
  237. for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
  238. {
  239. if ( ( *iGame )->mGameFile == m_sGameFile ) {
  240. m_nComboSelect = i;
  241. break;
  242. }
  243. i++;
  244. }
  245. importCallback( m_nComboSelect );
  246. }
  247. struct CGameDialog_GameFile {
  248. static void Export(const CGameDialog &self, const Callback<void(int)> &returnz) {
  249. self.GameFileExport(returnz);
  250. }
  251. static void Import(CGameDialog &self, int value) {
  252. self.GameFileImport(value);
  253. }
  254. };
  255. void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
  256. std::vector<const char*> games;
  257. games.reserve( mGames.size() );
  258. for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
  259. {
  260. games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
  261. }
  262. page.appendCombo(
  263. "Select the game",
  264. StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
  265. make_property<CGameDialog_GameFile>(*this)
  266. );
  267. page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
  268. }
  269. ui::Window CGameDialog::BuildDialog(){
  270. auto frame = create_dialog_frame( "Game settings", ui::Shadow::ETCHED_IN );
  271. auto vbox2 = create_dialog_vbox( 0, 4 );
  272. frame.add(vbox2);
  273. {
  274. PreferencesPage preferencesPage( *this, vbox2 );
  275. Global_constructPreferences( preferencesPage );
  276. CreateGlobalFrame( preferencesPage );
  277. }
  278. return create_simple_modal_dialog_window( "Global Preferences", m_modal, frame );
  279. }
  280. void CGameDialog::ScanForGames(){
  281. StringOutputStream strGamesPath( 256 );
  282. strGamesPath << DataPath_get() << "gamepacks/games/";
  283. const char *path = strGamesPath.c_str();
  284. globalOutputStream() << "Scanning for game description files: " << path << '\n';
  285. /*!
  286. \todo FIXME LINUX:
  287. do we put game description files below AppPath, or in ~/.radiant
  288. i.e. read only or read/write?
  289. my guess .. readonly cause it's an install
  290. we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
  291. (if that's really needed)
  292. */
  293. Directory_forEach(path, [&](const char *name) {
  294. if (!extension_equal(path_get_extension(name), "game")) {
  295. return;
  296. }
  297. StringOutputStream strPath(256);
  298. strPath << path << name;
  299. globalOutputStream() << strPath.c_str() << '\n';
  300. xmlDocPtr pDoc = xmlParseFile(strPath.c_str());
  301. if (pDoc) {
  302. mGames.push_front(new CGameDescription(pDoc, name));
  303. xmlFreeDoc(pDoc);
  304. } else {
  305. globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
  306. }
  307. });
  308. }
  309. CGameDescription* CGameDialog::GameDescriptionForComboItem(){
  310. std::list<CGameDescription *>::iterator iGame;
  311. int i = 0;
  312. for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
  313. {
  314. if ( i == m_nComboSelect ) {
  315. return ( *iGame );
  316. }
  317. }
  318. return 0; // not found
  319. }
  320. void CGameDialog::InitGlobalPrefPath(){
  321. g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
  322. }
  323. void CGameDialog::Reset(){
  324. if ( !g_Preferences.m_global_rc_path ) {
  325. InitGlobalPrefPath();
  326. }
  327. StringOutputStream strGlobalPref( 256 );
  328. strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
  329. file_remove( strGlobalPref.c_str() );
  330. }
  331. void CGameDialog::Init(){
  332. InitGlobalPrefPath();
  333. LoadPrefs();
  334. ScanForGames();
  335. if ( mGames.empty() ) {
  336. Error( "Didn't find any valid game file descriptions, aborting\n" );
  337. }
  338. else
  339. {
  340. std::list<CGameDescription *>::iterator iGame, iPrevGame;
  341. for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
  342. {
  343. if ( iPrevGame != mGames.end() ) {
  344. if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
  345. CGameDescription *h = *iGame;
  346. *iGame = *iPrevGame;
  347. *iPrevGame = h;
  348. }
  349. }
  350. }
  351. }
  352. CGameDescription* currentGameDescription = 0;
  353. if ( !m_bGamePrompt ) {
  354. // search by .game name
  355. std::list<CGameDescription *>::iterator iGame;
  356. for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
  357. {
  358. if ( ( *iGame )->mGameFile == m_sGameFile ) {
  359. currentGameDescription = ( *iGame );
  360. break;
  361. }
  362. }
  363. }
  364. if ( m_bGamePrompt || !currentGameDescription ) {
  365. Create();
  366. DoGameDialog();
  367. // use m_nComboSelect to identify the game to run as and set the globals
  368. currentGameDescription = GameDescriptionForComboItem();
  369. ASSERT_NOTNULL( currentGameDescription );
  370. }
  371. g_pGameDescription = currentGameDescription;
  372. g_pGameDescription->Dump();
  373. }
  374. CGameDialog::~CGameDialog(){
  375. // free all the game descriptions
  376. std::list<CGameDescription *>::iterator iGame;
  377. for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
  378. {
  379. delete ( *iGame );
  380. *iGame = 0;
  381. }
  382. if ( GetWidget() ) {
  383. Destroy();
  384. }
  385. }
  386. inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
  387. const char* identifier = gameDescription.getKeyValue( "index" );
  388. if ( string_empty( identifier ) ) {
  389. identifier = "1";
  390. }
  391. return identifier;
  392. }
  393. CGameDialog g_GamesDialog;
  394. // =============================================================================
  395. // Widget callbacks for PrefsDlg
  396. static void OnButtonClean( ui::Widget widget, gpointer data ){
  397. // make sure this is what the user wants
  398. if ( ui::alert( g_Preferences.GetWidget(), "This will close " RADIANT_NAME " and clean the corresponding registry entries.\n"
  399. "Next time you start " RADIANT_NAME " it will be good as new. Do you wish to continue?",
  400. "Reset Registry", ui::alert_type::YESNO, ui::alert_icon::Asterisk ) == ui::alert_response::YES ) {
  401. PrefsDlg *dlg = (PrefsDlg*)data;
  402. dlg->EndModal( eIDCANCEL );
  403. g_preferences_globals.disable_ini = true;
  404. Preferences_Reset();
  405. gtk_main_quit();
  406. }
  407. }
  408. // =============================================================================
  409. // PrefsDlg class
  410. /*
  411. ========
  412. very first prefs init deals with selecting the game and the game tools path
  413. then we can load .ini stuff
  414. using prefs / ini settings:
  415. those are per-game
  416. look in ~/.radiant/<version>/gamename
  417. ========
  418. */
  419. const char *PREFS_LOCAL_FILENAME = "local.pref";
  420. void PrefsDlg::Init(){
  421. // m_global_rc_path has been set above
  422. // m_rc_path is for game specific preferences
  423. // takes the form: global-pref-path/gamename/prefs-file
  424. // this is common to win32 and Linux init now
  425. m_rc_path = g_string_new( m_global_rc_path->str );
  426. // game sub-dir
  427. g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
  428. g_string_append( m_rc_path, "/" );
  429. Q_mkdir( m_rc_path->str );
  430. // then the ini file
  431. m_inipath = g_string_new( m_rc_path->str );
  432. g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
  433. }
  434. void notebook_set_page( ui::Widget notebook, ui::Widget page ){
  435. int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
  436. if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
  437. gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
  438. }
  439. }
  440. void PrefsDlg::showPrefPage( ui::Widget prefpage ){
  441. notebook_set_page( m_notebook, prefpage );
  442. return;
  443. }
  444. static void treeSelection( ui::TreeSelection selection, gpointer data ){
  445. PrefsDlg *dlg = (PrefsDlg*)data;
  446. GtkTreeModel* model;
  447. GtkTreeIter selected;
  448. if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
  449. ui::Widget prefpage{ui::null};
  450. gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
  451. dlg->showPrefPage( prefpage );
  452. }
  453. }
  454. typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
  455. inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
  456. for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
  457. {
  458. ( *i )( group );
  459. }
  460. }
  461. inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
  462. callbacks.push_back( callback );
  463. }
  464. typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
  465. inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
  466. for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
  467. {
  468. ( *i )( page );
  469. }
  470. }
  471. inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
  472. callbacks.push_back( callback );
  473. }
  474. PreferencesPageCallbacks g_interfacePreferences;
  475. void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
  476. PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
  477. }
  478. PreferenceGroupCallbacks g_interfaceCallbacks;
  479. void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
  480. PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
  481. }
  482. PreferencesPageCallbacks g_displayPreferences;
  483. void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
  484. PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
  485. }
  486. PreferenceGroupCallbacks g_displayCallbacks;
  487. void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
  488. PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
  489. }
  490. PreferencesPageCallbacks g_settingsPreferences;
  491. void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
  492. PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
  493. }
  494. PreferenceGroupCallbacks g_settingsCallbacks;
  495. void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
  496. PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
  497. }
  498. void Widget_updateDependency( ui::Widget self, ui::Widget toggleButton ){
  499. gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && gtk_widget_is_sensitive( toggleButton ) );
  500. }
  501. void ToggleButton_toggled_Widget_updateDependency( ui::Widget toggleButton, ui::Widget self ){
  502. Widget_updateDependency( self, toggleButton );
  503. }
  504. void ToggleButton_state_changed_Widget_updateDependency( ui::Widget toggleButton, GtkStateType state, ui::Widget self ){
  505. if ( state == GTK_STATE_INSENSITIVE ) {
  506. Widget_updateDependency( self, toggleButton );
  507. }
  508. }
  509. void Widget_connectToggleDependency( ui::Widget self, ui::Widget toggleButton ){
  510. toggleButton.connect( "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
  511. toggleButton.connect( "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
  512. Widget_updateDependency( self, toggleButton );
  513. }
  514. inline ui::VBox getVBox( ui::Bin page ){
  515. return ui::VBox::from(gtk_bin_get_child(page));
  516. }
  517. GtkTreeIter PreferenceTree_appendPage( ui::TreeStore store, GtkTreeIter* parent, const char* name, ui::Widget page ){
  518. GtkTreeIter group;
  519. gtk_tree_store_append( store, &group, parent );
  520. gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
  521. return group;
  522. }
  523. ui::Bin PreferencePages_addPage( ui::Widget notebook, const char* name ){
  524. ui::Widget preflabel = ui::Label( name );
  525. preflabel.show();
  526. auto pageframe = ui::Frame( name );
  527. gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
  528. pageframe.show();
  529. ui::Widget vbox = ui::VBox( FALSE, 4 );
  530. vbox.show();
  531. gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
  532. pageframe.add(vbox);
  533. // Add the page to the notebook
  534. gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
  535. return pageframe;
  536. }
  537. class PreferenceTreeGroup : public PreferenceGroup
  538. {
  539. Dialog& m_dialog;
  540. ui::Widget m_notebook;
  541. ui::TreeStore m_store;
  542. GtkTreeIter m_group;
  543. public:
  544. PreferenceTreeGroup( Dialog& dialog, ui::Widget notebook, ui::TreeStore store, GtkTreeIter group ) :
  545. m_dialog( dialog ),
  546. m_notebook( notebook ),
  547. m_store( store ),
  548. m_group( group ){
  549. }
  550. PreferencesPage createPage( const char* treeName, const char* frameName ){
  551. auto page = PreferencePages_addPage( m_notebook, frameName );
  552. PreferenceTree_appendPage( m_store, &m_group, treeName, page );
  553. return PreferencesPage( m_dialog, getVBox( page ) );
  554. }
  555. };
  556. ui::Window PrefsDlg::BuildDialog(){
  557. PreferencesDialog_addInterfacePreferences( makeCallbackF(Interface_constructPreferences) );
  558. Mouse_registerPreferencesPage();
  559. ui::Window dialog = ui::Window(create_floating_window( RADIANT_NAME " Preferences", m_parent ));
  560. {
  561. auto mainvbox = ui::VBox( FALSE, 5 );
  562. dialog.add(mainvbox);
  563. gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
  564. mainvbox.show();
  565. {
  566. auto hbox = ui::HBox( FALSE, 5 );
  567. hbox.show();
  568. mainvbox.pack_end(hbox, FALSE, TRUE, 0);
  569. {
  570. auto button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
  571. hbox.pack_end(button, FALSE, FALSE, 0);
  572. }
  573. {
  574. auto button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
  575. hbox.pack_end(button, FALSE, FALSE, 0);
  576. }
  577. {
  578. auto button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
  579. hbox.pack_end(button, FALSE, FALSE, 0);
  580. }
  581. }
  582. {
  583. auto hbox = ui::HBox( FALSE, 5 );
  584. mainvbox.pack_start( hbox, TRUE, TRUE, 0 );
  585. hbox.show();
  586. {
  587. auto sc_win = ui::ScrolledWindow(ui::New);
  588. gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
  589. hbox.pack_start( sc_win, FALSE, FALSE, 0 );
  590. sc_win.show();
  591. gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
  592. // prefs pages notebook
  593. m_notebook = ui::Widget::from(gtk_notebook_new());
  594. // hide the notebook tabs since its not supposed to look like a notebook
  595. gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
  596. hbox.pack_start( m_notebook, TRUE, TRUE, 0 );
  597. m_notebook.show();
  598. {
  599. auto store = ui::TreeStore::from(gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER ));
  600. auto view = ui::TreeView(ui::TreeModel::from(store._handle));
  601. gtk_tree_view_set_headers_visible(view, FALSE );
  602. {
  603. auto renderer = ui::CellRendererText(ui::New);
  604. auto column = ui::TreeViewColumn( "Preferences", renderer, {{"text", 0}} );
  605. gtk_tree_view_append_column(view, column );
  606. }
  607. {
  608. auto selection = ui::TreeSelection::from(gtk_tree_view_get_selection(view));
  609. selection.connect( "changed", G_CALLBACK( treeSelection ), this );
  610. }
  611. view.show();
  612. sc_win.add(view);
  613. {
  614. /********************************************************************/
  615. /* Add preference tree options */
  616. /********************************************************************/
  617. // Front page...
  618. //GtkWidget* front =
  619. PreferencePages_addPage( m_notebook, "Front Page" );
  620. {
  621. auto global = PreferencePages_addPage( m_notebook, "Global Preferences" );
  622. {
  623. PreferencesPage preferencesPage( *this, getVBox( global ) );
  624. Global_constructPreferences( preferencesPage );
  625. }
  626. auto group = PreferenceTree_appendPage( store, 0, "Global", global );
  627. {
  628. auto game = PreferencePages_addPage( m_notebook, "Game" );
  629. PreferencesPage preferencesPage( *this, getVBox( game ) );
  630. g_GamesDialog.CreateGlobalFrame( preferencesPage );
  631. PreferenceTree_appendPage( store, &group, "Game", game );
  632. }
  633. }
  634. {
  635. auto interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
  636. {
  637. PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
  638. PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
  639. }
  640. auto group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
  641. PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
  642. PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
  643. }
  644. {
  645. auto display = PreferencePages_addPage( m_notebook, "Display Preferences" );
  646. {
  647. PreferencesPage preferencesPage( *this, getVBox( display ) );
  648. PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
  649. }
  650. auto group = PreferenceTree_appendPage( store, 0, "Display", display );
  651. PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
  652. PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
  653. }
  654. {
  655. auto settings = PreferencePages_addPage( m_notebook, "General Settings" );
  656. {
  657. PreferencesPage preferencesPage( *this, getVBox( settings ) );
  658. PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
  659. }
  660. auto group = PreferenceTree_appendPage( store, 0, "Settings", settings );
  661. PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
  662. PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
  663. }
  664. }
  665. gtk_tree_view_expand_all(view );
  666. g_object_unref( G_OBJECT( store ) );
  667. }
  668. }
  669. }
  670. }
  671. gtk_notebook_set_current_page( GTK_NOTEBOOK( m_notebook ), 0 );
  672. return dialog;
  673. }
  674. preferences_globals_t g_preferences_globals;
  675. PrefsDlg g_Preferences; // global prefs instance
  676. void PreferencesDialog_constructWindow( ui::Window main_window ){
  677. g_Preferences.m_parent = main_window;
  678. g_Preferences.Create();
  679. }
  680. void PreferencesDialog_destroyWindow(){
  681. g_Preferences.Destroy();
  682. }
  683. PreferenceDictionary g_preferences;
  684. PreferenceSystem& GetPreferenceSystem(){
  685. return g_preferences;
  686. }
  687. class PreferenceSystemAPI
  688. {
  689. PreferenceSystem* m_preferencesystem;
  690. public:
  691. typedef PreferenceSystem Type;
  692. STRING_CONSTANT( Name, "*" );
  693. PreferenceSystemAPI(){
  694. m_preferencesystem = &GetPreferenceSystem();
  695. }
  696. PreferenceSystem* getTable(){
  697. return m_preferencesystem;
  698. }
  699. };
  700. #include "modulesystem/singletonmodule.h"
  701. #include "modulesystem/moduleregistry.h"
  702. typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
  703. typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
  704. StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
  705. void Preferences_Load(){
  706. g_GamesDialog.LoadPrefs();
  707. globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
  708. if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
  709. globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
  710. }
  711. }
  712. void Preferences_Save(){
  713. if ( g_preferences_globals.disable_ini ) {
  714. return;
  715. }
  716. // save global preferences
  717. g_GamesDialog.SavePrefs();
  718. globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
  719. if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
  720. globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
  721. }
  722. }
  723. void Preferences_Reset(){
  724. file_remove( g_Preferences.m_inipath->str );
  725. }
  726. void PrefsDlg::PostModal( EMessageBoxReturn code ){
  727. if ( code == eIDOK ) {
  728. Preferences_Save();
  729. UpdateAllWindows();
  730. }
  731. }
  732. std::vector<const char*> g_restart_required;
  733. void PreferencesDialog_restartRequired( const char* staticName ){
  734. g_restart_required.push_back( staticName );
  735. }
  736. void PreferencesDialog_showDialog(){
  737. if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
  738. if ( !g_restart_required.empty() ) {
  739. StringOutputStream message( 256 );
  740. message << "Preference changes require a restart:\n\n";
  741. for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
  742. {
  743. message << ( *i ) << '\n';
  744. }
  745. message << "\nRestart now?";
  746. auto ret = ui::alert( MainFrame_getWindow(), message.c_str(), "Restart " RADIANT_NAME "?", ui::alert_type::YESNO, ui::alert_icon::Question );
  747. g_restart_required.clear();
  748. if ( ret == ui::alert_response::YES ) {
  749. Radiant_Restart();
  750. }
  751. }
  752. }
  753. }
  754. struct GameName {
  755. static void Export(const Callback<void(const char *)> &returnz) {
  756. returnz(gamename_get());
  757. }
  758. static void Import(const char *value) {
  759. gamename_set(value);
  760. }
  761. };
  762. struct GameMode {
  763. static void Export(const Callback<void(const char *)> &returnz) {
  764. returnz(gamemode_get());
  765. }
  766. static void Import(const char *value) {
  767. gamemode_set(value);
  768. }
  769. };
  770. void RegisterPreferences( PreferenceSystem& preferences ){
  771. #if GDEF_OS_WINDOWS
  772. preferences.registerPreference( "UseCustomShaderEditor", make_property_string( g_TextEditor_useWin32Editor ) );
  773. #else
  774. preferences.registerPreference( "UseCustomShaderEditor", make_property_string( g_TextEditor_useCustomEditor ) );
  775. preferences.registerPreference( "CustomShaderEditorCommand", make_property_string( g_TextEditor_editorCommand ) );
  776. #endif
  777. preferences.registerPreference( "GameName", make_property<GameName>() );
  778. preferences.registerPreference( "GameMode", make_property<GameMode>() );
  779. }
  780. void Preferences_Init(){
  781. RegisterPreferences( GetPreferenceSystem() );
  782. }