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( "", "Default Text 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 );
79 void Mouse_constructPage( PreferenceGroup& group ){
80 PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) );
81 Mouse_constructPreferences( page );
83 void Mouse_registerPreferencesPage(){
84 PreferencesDialog_addInterfacePage( FreeCaller1<PreferenceGroup&, Mouse_constructPage>() );
89 =========================================================
90 Games selection dialog
91 =========================================================
95 #include <uilib/uilib.h>
97 inline const char* xmlAttr_getName( xmlAttrPtr attr ){
98 return reinterpret_cast<const char*>( attr->name );
101 inline const char* xmlAttr_getValue( xmlAttrPtr attr ){
102 return reinterpret_cast<const char*>( attr->children->content );
105 CGameDescription::CGameDescription( xmlDocPtr pDoc, const CopiedString& gameFile ){
106 // read the user-friendly game name
107 xmlNodePtr pNode = pDoc->children;
109 while ( strcmp( (const char*)pNode->name, "game" ) && pNode != 0 )
114 Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL );
117 for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next )
119 m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) );
123 StringOutputStream path( 256 );
124 path << AppPath_get() << gameFile.c_str() << "/";
125 mGameToolsPath = path.c_str();
128 ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) );
130 mGameFile = gameFile;
133 GameDescription::iterator i = m_gameDescription.find( "type" );
134 if ( i == m_gameDescription.end() ) {
135 globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>( pDoc->URL ) << "'\n";
141 mGameType = ( *i ).second.c_str();
146 void CGameDescription::Dump(){
147 globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n";
148 for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i )
150 globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n";
154 CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
157 #include "warnings.h"
158 #include "stream/textfilestream.h"
159 #include "container/array.h"
160 #include "xml/ixml.h"
161 #include "xml/xmlparser.h"
162 #include "xml/xmlwriter.h"
164 #include "preferencedictionary.h"
165 #include "stringio.h"
167 const char* const PREFERENCES_VERSION = "1.0";
169 bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){
171 TextFileInputStream file( filename );
172 if ( !file.failed() ) {
173 XMLStreamParser parser( file );
174 XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION );
175 parser.exportXML( importer );
179 int l = strlen( cmdline_prefix );
180 for ( int i = 1; i < g_argc - 1; ++i )
182 if ( g_argv[i][0] == '-' ) {
183 if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) {
184 if ( g_argv[i][l + 1] == '-' ) {
185 preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] );
195 bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){
196 TextFileOutputStream file( filename );
197 if ( !file.failed() ) {
198 XMLStreamWriter writer( file );
199 XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION );
200 exporter.exportXML( writer );
206 bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
207 Array<char> tmpName( filename, filename + strlen( filename ) + 1 + 3 );
208 *( tmpName.end() - 4 ) = 'T';
209 *( tmpName.end() - 3 ) = 'M';
210 *( tmpName.end() - 2 ) = 'P';
211 *( tmpName.end() - 1 ) = '\0';
213 return Preferences_Save( preferences, tmpName.data() )
214 && ( !file_exists( filename ) || file_remove( filename ) )
215 && file_move( tmpName.data(), filename );
220 void LogConsole_importString( const char* string ){
221 g_Console_enableLogging = string_equal( string, "true" );
222 Sys_LogFile( g_Console_enableLogging );
224 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
227 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
228 preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
229 preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
230 preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
234 PreferenceDictionary g_global_preferences;
236 void GlobalPreferences_Init(){
237 RegisterGlobalPreferences( g_global_preferences );
240 void CGameDialog::LoadPrefs(){
241 // load global .pref file
242 StringOutputStream strGlobalPref( 256 );
243 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
245 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
247 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
248 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
252 void CGameDialog::SavePrefs(){
253 StringOutputStream strGlobalPref( 256 );
254 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
256 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
258 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
259 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
263 void CGameDialog::DoGameDialog(){
267 // we save the prefs file
271 void CGameDialog::GameFileImport( int value ){
272 m_nComboSelect = value;
273 // use value to set m_sGameFile
274 std::list<CGameDescription *>::iterator iGame = mGames.begin();
276 for ( i = 0; i < value; i++ )
280 m_sGameFile = ( *iGame )->mGameFile;
283 void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
284 // use m_sGameFile to set value
285 std::list<CGameDescription *>::const_iterator iGame;
287 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
289 if ( ( *iGame )->mGameFile == m_sGameFile ) {
295 importCallback( m_nComboSelect );
298 void CGameDialog_GameFileImport( CGameDialog& self, int value ){
299 self.GameFileImport( value );
302 void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
303 self.GameFileExport( importCallback );
306 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
307 std::vector<const char*> games;
308 games.reserve( mGames.size() );
309 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
311 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
315 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
316 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
317 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
319 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
322 ui::Window CGameDialog::BuildDialog(){
323 auto frame = create_dialog_frame( "Game settings", ui::Shadow::ETCHED_IN );
325 auto vbox2 = create_dialog_vbox( 0, 4 );
329 PreferencesPage preferencesPage( *this, ui::Widget(GTK_WIDGET( vbox2 )) );
330 Global_constructPreferences( preferencesPage );
331 CreateGlobalFrame( preferencesPage );
334 return create_simple_modal_dialog_window( "Global Preferences", m_modal, frame );
339 std::list<CGameDescription*>& mGames;
342 LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
344 void operator()( const char* name ) const {
345 if ( !extension_equal( path_get_extension( name ), "game" ) ) {
348 StringOutputStream strPath( 256 );
349 strPath << mPath << name;
350 globalOutputStream() << strPath.c_str() << '\n';
352 xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
354 mGames.push_front( new CGameDescription( pDoc, name ) );
359 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
364 void CGameDialog::ScanForGames(){
365 StringOutputStream strGamesPath( 256 );
366 strGamesPath << AppPath_get() << "games/";
367 const char *path = strGamesPath.c_str();
369 globalOutputStream() << "Scanning for game description files: " << path << '\n';
373 do we put game description files below AppPath, or in ~/.radiant
374 i.e. read only or read/write?
375 my guess .. readonly cause it's an install
376 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
377 (if that's really needed)
380 Directory_forEach( path, LoadGameFile( mGames, path ) );
383 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
384 std::list<CGameDescription *>::iterator iGame;
386 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
388 if ( i == m_nComboSelect ) {
392 return 0; // not found
395 void CGameDialog::InitGlobalPrefPath(){
396 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
399 void CGameDialog::Reset(){
400 if ( !g_Preferences.m_global_rc_path ) {
401 InitGlobalPrefPath();
403 StringOutputStream strGlobalPref( 256 );
404 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
405 file_remove( strGlobalPref.c_str() );
408 void CGameDialog::Init(){
409 InitGlobalPrefPath();
412 if ( mGames.empty() ) {
413 Error( "Didn't find any valid game file descriptions, aborting\n" );
417 std::list<CGameDescription *>::iterator iGame, iPrevGame;
418 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
420 if ( iPrevGame != mGames.end() ) {
421 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
422 CGameDescription *h = *iGame;
430 CGameDescription* currentGameDescription = 0;
432 if ( !m_bGamePrompt ) {
433 // search by .game name
434 std::list<CGameDescription *>::iterator iGame;
435 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
437 if ( ( *iGame )->mGameFile == m_sGameFile ) {
438 currentGameDescription = ( *iGame );
443 if ( m_bGamePrompt || !currentGameDescription ) {
446 // use m_nComboSelect to identify the game to run as and set the globals
447 currentGameDescription = GameDescriptionForComboItem();
448 ASSERT_NOTNULL( currentGameDescription );
450 g_pGameDescription = currentGameDescription;
452 g_pGameDescription->Dump();
455 CGameDialog::~CGameDialog(){
456 // free all the game descriptions
457 std::list<CGameDescription *>::iterator iGame;
458 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
468 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
469 const char* identifier = gameDescription.getKeyValue( "index" );
470 if ( string_empty( identifier ) ) {
477 CGameDialog g_GamesDialog;
480 // =============================================================================
481 // Widget callbacks for PrefsDlg
483 static void OnButtonClean( ui::Widget widget, gpointer data ){
484 // make sure this is what the user wants
485 if ( ui::Widget(GTK_WIDGET( g_Preferences.GetWidget() )).alert( "This will close Radiant and clean the corresponding registry entries.\n"
486 "Next time you start Radiant it will be good as new. Do you wish to continue?",
487 "Reset Registry", ui::alert_type::YESNO, ui::alert_icon::Asterisk ) == ui::alert_response::YES ) {
488 PrefsDlg *dlg = (PrefsDlg*)data;
489 dlg->EndModal( eIDCANCEL );
491 g_preferences_globals.disable_ini = true;
497 // =============================================================================
503 very first prefs init deals with selecting the game and the game tools path
504 then we can load .ini stuff
506 using prefs / ini settings:
509 look in ~/.radiant/<version>/gamename
513 const char *PREFS_LOCAL_FILENAME = "local.pref";
515 void PrefsDlg::Init(){
516 // m_global_rc_path has been set above
517 // m_rc_path is for game specific preferences
518 // takes the form: global-pref-path/gamename/prefs-file
520 // this is common to win32 and Linux init now
521 m_rc_path = g_string_new( m_global_rc_path->str );
524 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
525 g_string_append( m_rc_path, "/" );
526 Q_mkdir( m_rc_path->str );
529 m_inipath = g_string_new( m_rc_path->str );
530 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
533 void notebook_set_page( ui::Widget notebook, ui::Widget page ){
534 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
535 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
536 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
540 void PrefsDlg::showPrefPage( ui::Widget prefpage ){
541 notebook_set_page( m_notebook, prefpage );
545 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
546 PrefsDlg *dlg = (PrefsDlg*)data;
549 GtkTreeIter selected;
550 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
551 ui::Widget prefpage{ui::null};
552 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
553 dlg->showPrefPage( prefpage );
557 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
559 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
560 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
567 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
568 callbacks.push_back( callback );
571 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
573 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
574 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
580 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
581 callbacks.push_back( callback );
584 PreferencesPageCallbacks g_interfacePreferences;
585 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
586 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
588 PreferenceGroupCallbacks g_interfaceCallbacks;
589 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
590 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
593 PreferencesPageCallbacks g_displayPreferences;
594 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
595 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
597 PreferenceGroupCallbacks g_displayCallbacks;
598 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
599 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
602 PreferencesPageCallbacks g_settingsPreferences;
603 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
604 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
606 PreferenceGroupCallbacks g_settingsCallbacks;
607 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
608 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
611 void Widget_updateDependency( ui::Widget self, ui::Widget toggleButton ){
612 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && gtk_widget_is_sensitive( toggleButton ) );
615 void ToggleButton_toggled_Widget_updateDependency( ui::Widget toggleButton, ui::Widget self ){
616 Widget_updateDependency( self, toggleButton );
619 void ToggleButton_state_changed_Widget_updateDependency( ui::Widget toggleButton, GtkStateType state, ui::Widget self ){
620 if ( state == GTK_STATE_INSENSITIVE ) {
621 Widget_updateDependency( self, toggleButton );
625 void Widget_connectToggleDependency( ui::Widget self, ui::Widget toggleButton ){
626 toggleButton.connect( "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
627 toggleButton.connect( "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
628 Widget_updateDependency( self, toggleButton );
632 inline ui::Widget getVBox( ui::Widget page ){
633 return ui::Widget(gtk_bin_get_child( GTK_BIN( page ) ));
636 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, ui::Widget page ){
638 gtk_tree_store_append( store, &group, parent );
639 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
643 ui::Widget PreferencePages_addPage( ui::Widget notebook, const char* name ){
644 ui::Widget preflabel = ui::Label( name );
647 auto pageframe = ui::Frame( name );
648 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
651 ui::Widget vbox = ui::VBox( FALSE, 4 );
653 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
656 // Add the page to the notebook
657 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
662 class PreferenceTreeGroup : public PreferenceGroup
665 ui::Widget m_notebook;
666 GtkTreeStore* m_store;
669 PreferenceTreeGroup( Dialog& dialog, ui::Widget notebook, GtkTreeStore* store, GtkTreeIter group ) :
671 m_notebook( notebook ),
675 PreferencesPage createPage( const char* treeName, const char* frameName ){
676 ui::Widget page = PreferencePages_addPage( m_notebook, frameName );
677 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
678 return PreferencesPage( m_dialog, getVBox( page ) );
682 ui::Window PrefsDlg::BuildDialog(){
683 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
684 Mouse_registerPreferencesPage();
686 ui::Window dialog = ui::Window(create_floating_window( "NetRadiant Preferences", m_parent ));
689 ui::Widget mainvbox = ui::VBox( FALSE, 5 );
690 dialog.add(mainvbox);
691 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
695 ui::Widget hbox = ui::HBox( FALSE, 5 );
697 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
700 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
701 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
704 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
705 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
708 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
709 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
714 ui::Widget hbox = ui::HBox( FALSE, 5 );
715 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
719 auto sc_win = ui::ScrolledWindow(ui::New);
720 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
721 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
723 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
725 // prefs pages notebook
726 m_notebook = ui::Widget(gtk_notebook_new());
727 // hide the notebook tabs since its not supposed to look like a notebook
728 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
729 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
734 auto store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
736 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
737 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
740 auto renderer = ui::CellRendererText(ui::New);
741 GtkTreeViewColumn* column = ui::TreeViewColumn( "Preferences", renderer, {{"text", 0}} );
742 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
746 auto selection = ui::TreeSelection(gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) ));
747 selection.connect( "changed", G_CALLBACK( treeSelection ), this );
755 /********************************************************************/
756 /* Add preference tree options */
757 /********************************************************************/
760 PreferencePages_addPage( m_notebook, "Front Page" );
763 ui::Widget global = PreferencePages_addPage( m_notebook, "Global Preferences" );
765 PreferencesPage preferencesPage( *this, getVBox( global ) );
766 Global_constructPreferences( preferencesPage );
768 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
770 ui::Widget game = PreferencePages_addPage( m_notebook, "Game" );
771 PreferencesPage preferencesPage( *this, getVBox( game ) );
772 g_GamesDialog.CreateGlobalFrame( preferencesPage );
774 PreferenceTree_appendPage( store, &group, "Game", game );
779 ui::Widget interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
781 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
782 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
785 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
786 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
788 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
792 ui::Widget display = PreferencePages_addPage( m_notebook, "Display Preferences" );
794 PreferencesPage preferencesPage( *this, getVBox( display ) );
795 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
797 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
798 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
800 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
804 ui::Widget settings = PreferencePages_addPage( m_notebook, "General Settings" );
806 PreferencesPage preferencesPage( *this, getVBox( settings ) );
807 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
810 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
811 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
813 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
817 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
819 g_object_unref( G_OBJECT( store ) );
825 gtk_notebook_set_current_page( GTK_NOTEBOOK( m_notebook ), 0 );
830 preferences_globals_t g_preferences_globals;
832 PrefsDlg g_Preferences; // global prefs instance
835 void PreferencesDialog_constructWindow( ui::Window main_window ){
836 g_Preferences.m_parent = main_window;
837 g_Preferences.Create();
839 void PreferencesDialog_destroyWindow(){
840 g_Preferences.Destroy();
844 PreferenceDictionary g_preferences;
846 PreferenceSystem& GetPreferenceSystem(){
847 return g_preferences;
850 class PreferenceSystemAPI
852 PreferenceSystem* m_preferencesystem;
854 typedef PreferenceSystem Type;
855 STRING_CONSTANT( Name, "*" );
857 PreferenceSystemAPI(){
858 m_preferencesystem = &GetPreferenceSystem();
860 PreferenceSystem* getTable(){
861 return m_preferencesystem;
865 #include "modulesystem/singletonmodule.h"
866 #include "modulesystem/moduleregistry.h"
868 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
869 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
870 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
872 void Preferences_Load(){
873 g_GamesDialog.LoadPrefs();
875 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
877 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
878 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
882 void Preferences_Save(){
883 if ( g_preferences_globals.disable_ini ) {
887 g_GamesDialog.SavePrefs();
889 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
891 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
892 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
896 void Preferences_Reset(){
897 file_remove( g_Preferences.m_inipath->str );
901 void PrefsDlg::PostModal( EMessageBoxReturn code ){
902 if ( code == eIDOK ) {
908 std::vector<const char*> g_restart_required;
910 void PreferencesDialog_restartRequired( const char* staticName ){
911 g_restart_required.push_back( staticName );
914 void PreferencesDialog_showDialog(){
915 if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
916 if ( !g_restart_required.empty() ) {
917 StringOutputStream message( 256 );
918 message << "Preference changes require a restart:\n";
919 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
921 message << ( *i ) << '\n';
923 MainFrame_getWindow().alert( message.c_str() );
924 g_restart_required.clear();
933 void GameName_importString( const char* value ){
934 gamename_set( value );
936 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
937 void GameName_exportString( const StringImportCallback& importer ){
938 importer( gamename_get() );
940 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
942 void GameMode_importString( const char* value ){
943 gamemode_set( value );
945 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
946 void GameMode_exportString( const StringImportCallback& importer ){
947 importer( gamemode_get() );
949 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
952 void RegisterPreferences( PreferenceSystem& preferences ){
954 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
956 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
957 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
960 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
961 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
964 void Preferences_Init(){
965 RegisterPreferences( GetPreferenceSystem() );