2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 // Leonardo Zide (leo@lokigames.com)
28 #include "preferences.h"
29 #include "globaldefs.h"
32 #include "environment.h"
34 #include "debugging/debugging.h"
36 #include "generic/callback.h"
37 #include "math/vector.h"
38 #include "string/string.h"
39 #include "stream/stringstream.h"
43 #include "gtkutil/filechooser.h"
44 #include "gtkutil/messagebox.h"
50 #include "mainframe.h"
56 void Global_constructPreferences( PreferencesPage& page ){
57 page.appendCheckBox( "Console", "Enable Logging", g_Console_enableLogging );
60 void Interface_constructPreferences( PreferencesPage& page ){
62 page.appendCheckBox( "", "External Shader Editor", g_TextEditor_useWin32Editor );
65 ui::CheckButton use_custom = page.appendCheckBox( "Text Editor", "Custom", g_TextEditor_useCustomEditor );
66 ui::Widget custom_editor = page.appendPathEntry( "Text Editor Command", g_TextEditor_editorCommand, true );
67 Widget_connectToggleDependency( custom_editor, use_custom );
72 void Mouse_constructPreferences( PreferencesPage& page ){
74 const char* buttons[] = { "2 button", "3 button", };
75 page.appendRadio( "Mouse Type", g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) );
77 page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick );
78 page.appendCheckBox( "", "Improved mousewheel zoom", g_xywindow_globals.m_bImprovedWheelZoom );
80 void Mouse_constructPage( PreferenceGroup& group ){
81 PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) );
82 Mouse_constructPreferences( page );
84 void Mouse_registerPreferencesPage(){
85 PreferencesDialog_addInterfacePage( makeCallbackF(Mouse_constructPage) );
90 =========================================================
91 Games selection dialog
92 =========================================================
96 #include <uilib/uilib.h>
98 inline const char* xmlAttr_getName( xmlAttrPtr attr ){
99 return reinterpret_cast<const char*>( attr->name );
102 inline const char* xmlAttr_getValue( xmlAttrPtr attr ){
103 return reinterpret_cast<const char*>( attr->children->content );
106 CGameDescription::CGameDescription( xmlDocPtr pDoc, const CopiedString& gameFile ){
107 // read the user-friendly game name
108 xmlNodePtr pNode = pDoc->children;
110 while ( strcmp( (const char*)pNode->name, "game" ) && pNode != 0 )
115 Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL );
118 for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next )
120 m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) );
124 StringOutputStream path( 256 );
125 path << DataPath_get() << "gamepacks/" << gameFile.c_str() << "/";
126 mGameToolsPath = path.c_str();
129 ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) );
131 mGameFile = gameFile;
134 GameDescription::iterator i = m_gameDescription.find( "type" );
135 if ( i == m_gameDescription.end() ) {
136 globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>( pDoc->URL ) << "'\n";
142 mGameType = ( *i ).second.c_str();
147 void CGameDescription::Dump(){
148 globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n";
149 for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i )
151 globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n";
155 CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
158 #include "warnings.h"
159 #include "stream/textfilestream.h"
160 #include "container/array.h"
161 #include "xml/ixml.h"
162 #include "xml/xmlparser.h"
163 #include "xml/xmlwriter.h"
165 #include "preferencedictionary.h"
166 #include "stringio.h"
168 const char* const PREFERENCES_VERSION = "1.0";
170 bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){
172 TextFileInputStream file( filename );
173 if ( !file.failed() ) {
174 XMLStreamParser parser( file );
175 XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION );
176 parser.exportXML( importer );
180 int l = strlen( cmdline_prefix );
181 for ( int i = 1; i < g_argc - 1; ++i )
183 if ( g_argv[i][0] == '-' ) {
184 if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) {
185 if ( g_argv[i][l + 1] == '-' ) {
186 preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] );
196 bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){
197 TextFileOutputStream file( filename );
198 if ( !file.failed() ) {
199 XMLStreamWriter writer( file );
200 XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION );
201 exporter.exportXML( writer );
207 bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
208 std::string tmpName( filename );
211 return Preferences_Save( preferences, tmpName.c_str() )
212 && ( !file_exists( filename ) || file_remove( filename ) )
213 && file_move( tmpName.data(), filename );
218 static void Export(const Callback<void(bool)> &returnz) {
219 returnz(g_Console_enableLogging);
222 static void Import(bool value) {
223 g_Console_enableLogging = value;
224 Sys_EnableLogFile(g_Console_enableLogging);
229 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
230 preferences.registerPreference( "gamefile", make_property_string( g_GamesDialog.m_sGameFile ) );
231 preferences.registerPreference( "gamePrompt", make_property_string( g_GamesDialog.m_bGamePrompt ) );
232 preferences.registerPreference( "skipGamePromptOnce", make_property_string( g_GamesDialog.m_bSkipGamePromptOnce ) );
233 preferences.registerPreference( "log console", make_property_string<LogConsole>() );
237 PreferenceDictionary g_global_preferences;
239 void GlobalPreferences_Init(){
240 RegisterGlobalPreferences( g_global_preferences );
243 void CGameDialog::LoadPrefs(){
244 // load global .pref file
245 StringOutputStream strGlobalPref( 256 );
246 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
248 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
250 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
251 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
255 void CGameDialog::SavePrefs(){
256 StringOutputStream strGlobalPref( 256 );
257 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
259 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
261 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
262 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
266 void CGameDialog::DoGameDialog(){
270 // we save the prefs file
274 void CGameDialog::GameFileImport( int value ){
275 m_nComboSelect = value;
276 // use value to set m_sGameFile
277 std::list<CGameDescription *>::iterator iGame = mGames.begin();
279 for ( i = 0; i < value; i++ )
284 if ( ( *iGame )->mGameFile != m_sGameFile ) {
285 m_sGameFile = ( *iGame )->mGameFile;
287 // do not trigger radiant restart when switching game on startup using Global Preferences dialog
289 PreferencesDialog_restartRequired( "Selected Game" );
293 // onStartup can only be true once, when Global Preferences are displayed at startup
297 void CGameDialog::GameFileExport( const Callback<void(int)> & importCallback ) const {
298 // use m_sGameFile to set value
299 std::list<CGameDescription *>::const_iterator iGame;
301 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
303 if ( ( *iGame )->mGameFile == m_sGameFile ) {
309 importCallback( m_nComboSelect );
312 struct CGameDialog_GameFile {
313 static void Export(const CGameDialog &self, const Callback<void(int)> &returnz) {
314 self.GameFileExport(returnz);
317 static void Import(CGameDialog &self, int value) {
318 self.GameFileImport(value);
322 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
323 std::vector<const char*> games;
324 games.reserve( mGames.size() );
325 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
327 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
331 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
332 make_property<CGameDialog_GameFile>(*this)
334 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
337 ui::Window CGameDialog::BuildDialog(){
338 auto frame = create_dialog_frame( "Game settings", ui::Shadow::ETCHED_IN );
340 auto vbox2 = create_dialog_vbox( 0, 4 );
344 PreferencesPage preferencesPage( *this, vbox2 );
345 Global_constructPreferences( preferencesPage );
346 CreateGlobalFrame( preferencesPage );
349 return create_simple_modal_dialog_window( "Global Preferences", m_modal, frame );
352 static void StringReplace( std::string& input, const std::string& first, const std::string& second )
355 while ( ( found = input.find(first, found) ) != std::string::npos )
357 input.replace( found, first.length(), second );
361 // FIXME, for some unknown reason it sorts “Quake 3” after “Quake 4”.
362 static bool CompareGameName( CGameDescription *first, CGameDescription *second )
364 std::string string1( first->getRequiredKeyValue( "name" ) );
365 std::string string2( second->getRequiredKeyValue( "name" ) );
367 // HACK: Replace some roman numerals.
368 StringReplace( string1, " III", " 3" );
369 StringReplace( string2, " III", " 3" );
370 StringReplace( string1, " II", " 2" );
371 StringReplace( string2, " II", " 2" );
373 return string1 < string2;
376 void CGameDialog::ScanForGames(){
377 StringOutputStream strGamesPath( 256 );
378 strGamesPath << DataPath_get() << "gamepacks/games/";
379 const char *path = strGamesPath.c_str();
381 globalOutputStream() << "Scanning for game description files: " << path << '\n';
385 do we put game description files below AppPath, or in ~/.radiant
386 i.e. read only or read/write?
387 my guess .. readonly cause it's an install
388 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
389 (if that's really needed)
392 Directory_forEach(path, [&](const char *name) {
393 if (!extension_equal(path_get_extension(name), "game")) {
396 StringOutputStream strPath(256);
397 strPath << path << name;
398 globalOutputStream() << strPath.c_str() << '\n';
400 xmlDocPtr pDoc = xmlParseFile(strPath.c_str());
402 mGames.push_front(new CGameDescription(pDoc, name));
405 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
408 mGames.sort(CompareGameName);
412 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
413 std::list<CGameDescription *>::iterator iGame;
415 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
417 if ( i == m_nComboSelect ) {
421 return 0; // not found
424 void CGameDialog::InitGlobalPrefPath(){
425 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
428 void CGameDialog::Reset(){
429 if ( !g_Preferences.m_global_rc_path ) {
430 InitGlobalPrefPath();
432 StringOutputStream strGlobalPref( 256 );
433 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
434 file_remove( strGlobalPref.c_str() );
437 void CGameDialog::Init(){
438 bool gamePrompt = false;
440 InitGlobalPrefPath();
444 if ( mGames.empty() ) {
445 Error( "Didn't find any valid game file descriptions, aborting\n" );
449 std::list<CGameDescription *>::iterator iGame, iPrevGame;
450 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
452 if ( iPrevGame != mGames.end() ) {
453 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
454 CGameDescription *h = *iGame;
462 CGameDescription* currentGameDescription = 0;
464 // m_bSkipGamePromptOnce is used to not prompt for game on restart, only on fresh startup
465 if ( m_bGamePrompt && !m_bSkipGamePromptOnce ) {
469 m_bSkipGamePromptOnce = false;
470 g_GamesDialog.SavePrefs();
473 // search by .game name
474 std::list<CGameDescription *>::iterator iGame;
475 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
477 if ( ( *iGame )->mGameFile == m_sGameFile ) {
478 currentGameDescription = ( *iGame );
484 if ( gamePrompt || !currentGameDescription ) {
488 // use m_nComboSelect to identify the game to run as and set the globals
489 currentGameDescription = GameDescriptionForComboItem();
490 ASSERT_NOTNULL( currentGameDescription );
496 g_pGameDescription = currentGameDescription;
498 g_pGameDescription->Dump();
501 CGameDialog::~CGameDialog(){
502 // free all the game descriptions
503 std::list<CGameDescription *>::iterator iGame;
504 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
514 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
515 const char* identifier = gameDescription.getKeyValue( "index" );
516 if ( string_empty( identifier ) ) {
523 CGameDialog g_GamesDialog;
526 // =============================================================================
527 // Widget callbacks for PrefsDlg
529 static void OnButtonClean( ui::Widget widget, gpointer data ){
530 // make sure this is what the user wants
531 if ( ui::alert( g_Preferences.GetWidget(), "This will close " RADIANT_NAME " and clean the corresponding registry entries.\n"
532 "Next time you start " RADIANT_NAME " it will be good as new. Do you wish to continue?",
533 "Reset Registry", ui::alert_type::YESNO, ui::alert_icon::Asterisk ) == ui::alert_response::YES ) {
534 PrefsDlg *dlg = (PrefsDlg*)data;
535 dlg->EndModal( eIDCANCEL );
537 g_preferences_globals.disable_ini = true;
543 // =============================================================================
549 very first prefs init deals with selecting the game and the game tools path
550 then we can load .ini stuff
552 using prefs / ini settings:
555 look in ~/.radiant/<version>/gamename
559 const char *PREFS_LOCAL_FILENAME = "local.pref";
561 void PrefsDlg::Init(){
562 // m_global_rc_path has been set above
563 // m_rc_path is for game specific preferences
564 // takes the form: global-pref-path/gamename/prefs-file
566 // this is common to win32 and Linux init now
567 m_rc_path = g_string_new( m_global_rc_path->str );
570 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
571 g_string_append( m_rc_path, "/" );
572 Q_mkdir( m_rc_path->str );
575 m_inipath = g_string_new( m_rc_path->str );
576 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
579 void notebook_set_page( ui::Widget notebook, ui::Widget page ){
580 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
581 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
582 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
586 void PrefsDlg::showPrefPage( ui::Widget prefpage ){
587 notebook_set_page( m_notebook, prefpage );
591 static void treeSelection( ui::TreeSelection selection, gpointer data ){
592 PrefsDlg *dlg = (PrefsDlg*)data;
595 GtkTreeIter selected;
596 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
597 ui::Widget prefpage{ui::null};
598 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
599 dlg->showPrefPage( prefpage );
603 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
605 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
606 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
613 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
614 callbacks.push_back( callback );
617 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
619 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
620 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
626 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
627 callbacks.push_back( callback );
630 PreferencesPageCallbacks g_interfacePreferences;
631 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
632 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
634 PreferenceGroupCallbacks g_interfaceCallbacks;
635 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
636 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
639 PreferencesPageCallbacks g_displayPreferences;
640 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
641 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
643 PreferenceGroupCallbacks g_displayCallbacks;
644 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
645 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
648 PreferencesPageCallbacks g_settingsPreferences;
649 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
650 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
652 PreferenceGroupCallbacks g_settingsCallbacks;
653 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
654 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
657 void Widget_updateDependency( ui::Widget self, ui::Widget toggleButton ){
658 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && gtk_widget_is_sensitive( toggleButton ) );
661 void ToggleButton_toggled_Widget_updateDependency( ui::Widget toggleButton, ui::Widget self ){
662 Widget_updateDependency( self, toggleButton );
665 void ToggleButton_state_changed_Widget_updateDependency( ui::Widget toggleButton, GtkStateType state, ui::Widget self ){
666 if ( state == GTK_STATE_INSENSITIVE ) {
667 Widget_updateDependency( self, toggleButton );
671 void Widget_connectToggleDependency( ui::Widget self, ui::Widget toggleButton ){
672 toggleButton.connect( "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
673 toggleButton.connect( "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
674 Widget_updateDependency( self, toggleButton );
678 inline ui::VBox getVBox( ui::Bin page ){
679 return ui::VBox::from(gtk_bin_get_child(page));
682 GtkTreeIter PreferenceTree_appendPage( ui::TreeStore store, GtkTreeIter* parent, const char* name, ui::Widget page ){
684 gtk_tree_store_append( store, &group, parent );
685 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
689 ui::Bin PreferencePages_addPage( ui::Widget notebook, const char* name ){
690 ui::Widget preflabel = ui::Label( name );
693 auto pageframe = ui::Frame( name );
694 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
697 ui::Widget vbox = ui::VBox( FALSE, 4 );
699 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
702 // Add the page to the notebook
703 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
708 class PreferenceTreeGroup : public PreferenceGroup
711 ui::Widget m_notebook;
712 ui::TreeStore m_store;
715 PreferenceTreeGroup( Dialog& dialog, ui::Widget notebook, ui::TreeStore store, GtkTreeIter group ) :
717 m_notebook( notebook ),
721 PreferencesPage createPage( const char* treeName, const char* frameName ){
722 auto page = PreferencePages_addPage( m_notebook, frameName );
723 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
724 return PreferencesPage( m_dialog, getVBox( page ) );
728 ui::Window PrefsDlg::BuildDialog(){
729 PreferencesDialog_addInterfacePreferences( makeCallbackF(Interface_constructPreferences) );
730 Mouse_registerPreferencesPage();
732 ui::Window dialog = ui::Window(create_floating_window( RADIANT_NAME " Preferences", m_parent ));
734 gtk_window_set_transient_for( dialog, m_parent );
735 gtk_window_set_position( dialog, GTK_WIN_POS_CENTER_ON_PARENT );
738 auto mainvbox = ui::VBox( FALSE, 5 );
739 dialog.add(mainvbox);
740 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
744 auto hbox = ui::HBox( FALSE, 5 );
746 mainvbox.pack_end(hbox, FALSE, TRUE, 0);
749 auto button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
750 hbox.pack_end(button, FALSE, FALSE, 0);
753 auto button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
754 hbox.pack_end(button, FALSE, FALSE, 0);
757 auto button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
758 hbox.pack_end(button, FALSE, FALSE, 0);
763 auto hbox = ui::HBox( FALSE, 5 );
764 mainvbox.pack_start( hbox, TRUE, TRUE, 0 );
768 auto sc_win = ui::ScrolledWindow(ui::New);
769 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
770 hbox.pack_start( sc_win, FALSE, FALSE, 0 );
772 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
774 // prefs pages notebook
775 m_notebook = ui::Widget::from(gtk_notebook_new());
776 // hide the notebook tabs since its not supposed to look like a notebook
777 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
778 hbox.pack_start( m_notebook, TRUE, TRUE, 0 );
783 auto store = ui::TreeStore::from(gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER ));
785 auto view = ui::TreeView(ui::TreeModel::from(store._handle));
786 gtk_tree_view_set_headers_visible(view, FALSE );
789 auto renderer = ui::CellRendererText(ui::New);
790 auto column = ui::TreeViewColumn( "Preferences", renderer, {{"text", 0}} );
791 gtk_tree_view_append_column(view, column );
795 auto selection = ui::TreeSelection::from(gtk_tree_view_get_selection(view));
796 selection.connect( "changed", G_CALLBACK( treeSelection ), this );
804 /********************************************************************/
805 /* Add preference tree options */
806 /********************************************************************/
809 PreferencePages_addPage( m_notebook, "Front Page" );
812 auto global = PreferencePages_addPage( m_notebook, "Global Preferences" );
814 PreferencesPage preferencesPage( *this, getVBox( global ) );
815 Global_constructPreferences( preferencesPage );
817 auto group = PreferenceTree_appendPage( store, 0, "Global", global );
819 auto game = PreferencePages_addPage( m_notebook, "Game" );
820 PreferencesPage preferencesPage( *this, getVBox( game ) );
821 g_GamesDialog.CreateGlobalFrame( preferencesPage );
823 PreferenceTree_appendPage( store, &group, "Game", game );
828 auto interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
830 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
831 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
834 auto group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
835 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
837 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
841 auto display = PreferencePages_addPage( m_notebook, "Display Preferences" );
843 PreferencesPage preferencesPage( *this, getVBox( display ) );
844 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
846 auto group = PreferenceTree_appendPage( store, 0, "Display", display );
847 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
849 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
853 auto settings = PreferencePages_addPage( m_notebook, "General Settings" );
855 PreferencesPage preferencesPage( *this, getVBox( settings ) );
856 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
859 auto group = PreferenceTree_appendPage( store, 0, "Settings", settings );
860 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
862 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
866 gtk_tree_view_expand_all(view );
868 g_object_unref( G_OBJECT( store ) );
874 gtk_notebook_set_current_page( GTK_NOTEBOOK( m_notebook ), 0 );
879 preferences_globals_t g_preferences_globals;
881 PrefsDlg g_Preferences; // global prefs instance
884 void PreferencesDialog_constructWindow( ui::Window main_window ){
885 g_Preferences.m_parent = main_window;
886 g_Preferences.Create();
888 void PreferencesDialog_destroyWindow(){
889 g_Preferences.Destroy();
893 PreferenceDictionary g_preferences;
895 PreferenceSystem& GetPreferenceSystem(){
896 return g_preferences;
899 class PreferenceSystemAPI
901 PreferenceSystem* m_preferencesystem;
903 typedef PreferenceSystem Type;
904 STRING_CONSTANT( Name, "*" );
906 PreferenceSystemAPI(){
907 m_preferencesystem = &GetPreferenceSystem();
909 PreferenceSystem* getTable(){
910 return m_preferencesystem;
914 #include "modulesystem/singletonmodule.h"
915 #include "modulesystem/moduleregistry.h"
917 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
918 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
919 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
921 void Preferences_Load(){
922 g_GamesDialog.LoadPrefs();
924 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
926 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
927 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
931 void Preferences_Save(){
932 if ( g_preferences_globals.disable_ini ) {
936 // save global preferences
937 g_GamesDialog.SavePrefs();
939 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
941 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
942 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
946 void Preferences_Reset(){
947 file_remove( g_Preferences.m_inipath->str );
951 void PrefsDlg::PostModal( EMessageBoxReturn code ){
952 if ( code == eIDOK ) {
958 std::vector<const char*> g_restart_required;
960 void PreferencesDialog_restartRequired( const char* staticName ){
961 g_restart_required.push_back( staticName );
964 bool PreferencesDialog_isRestartRequired(){
965 return !g_restart_required.empty();
968 void PreferencesDialog_restartIfRequired(){
969 if ( !g_restart_required.empty() ) {
970 StringOutputStream message( 256 );
971 message << "Preference changes require a restart:\n\n";
973 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
975 message << ( *i ) << '\n';
978 message << "\nRestart now?";
980 auto ret = ui::alert( MainFrame_getWindow(), message.c_str(), "Restart " RADIANT_NAME "?", ui::alert_type::YESNO, ui::alert_icon::Question );
982 g_restart_required.clear();
984 if ( ret == ui::alert_response::YES ) {
985 g_GamesDialog.m_bSkipGamePromptOnce = true;
991 void PreferencesDialog_showDialog(){
992 if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
993 PreferencesDialog_restartIfRequired();
998 static void Export(const Callback<void(const char *)> &returnz) {
999 returnz(gamename_get());
1002 static void Import(const char *value) {
1003 gamename_set(value);
1008 static void Export(const Callback<void(const char *)> &returnz) {
1009 returnz(gamemode_get());
1012 static void Import(const char *value) {
1013 gamemode_set(value);
1017 void RegisterPreferences( PreferenceSystem& preferences ){
1019 preferences.registerPreference( "UseCustomShaderEditor", make_property_string( g_TextEditor_useWin32Editor ) );
1021 preferences.registerPreference( "UseCustomShaderEditor", make_property_string( g_TextEditor_useCustomEditor ) );
1022 preferences.registerPreference( "CustomShaderEditorCommand", make_property_string( g_TextEditor_editorCommand ) );
1025 preferences.registerPreference( "GameName", make_property<GameName>() );
1026 preferences.registerPreference( "GameMode", make_property<GameMode>() );
1029 void Preferences_Init(){
1030 RegisterPreferences( GetPreferenceSystem() );