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 "environment.h"
31 #include "debugging/debugging.h"
33 #include "generic/callback.h"
34 #include "math/vector.h"
35 #include "string/string.h"
36 #include "stream/stringstream.h"
40 #include "gtkutil/filechooser.h"
41 #include "gtkutil/messagebox.h"
47 #include "mainframe.h"
53 void Global_constructPreferences( PreferencesPage& page ){
54 page.appendCheckBox( "Console", "Enable Logging", g_Console_enableLogging );
57 void Interface_constructPreferences( PreferencesPage& page ){
59 page.appendCheckBox( "", "Default Text Editor", g_TextEditor_useWin32Editor );
62 ui::CheckButton use_custom = page.appendCheckBox( "Text Editor", "Custom", g_TextEditor_useCustomEditor );
63 ui::Widget custom_editor = page.appendPathEntry( "Text Editor Command", g_TextEditor_editorCommand, true );
64 Widget_connectToggleDependency( custom_editor, use_custom );
69 void Mouse_constructPreferences( PreferencesPage& page ){
71 const char* buttons[] = { "2 button", "3 button", };
72 page.appendRadio( "Mouse Type", g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) );
74 page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick );
76 void Mouse_constructPage( PreferenceGroup& group ){
77 PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) );
78 Mouse_constructPreferences( page );
80 void Mouse_registerPreferencesPage(){
81 PreferencesDialog_addInterfacePage( FreeCaller1<PreferenceGroup&, Mouse_constructPage>() );
86 =========================================================
87 Games selection dialog
88 =========================================================
92 #include <uilib/uilib.h>
94 inline const char* xmlAttr_getName( xmlAttrPtr attr ){
95 return reinterpret_cast<const char*>( attr->name );
98 inline const char* xmlAttr_getValue( xmlAttrPtr attr ){
99 return reinterpret_cast<const char*>( attr->children->content );
102 CGameDescription::CGameDescription( xmlDocPtr pDoc, const CopiedString& gameFile ){
103 // read the user-friendly game name
104 xmlNodePtr pNode = pDoc->children;
106 while ( strcmp( (const char*)pNode->name, "game" ) && pNode != 0 )
111 Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL );
114 for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next )
116 m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) );
120 StringOutputStream path( 256 );
121 path << AppPath_get() << gameFile.c_str() << "/";
122 mGameToolsPath = path.c_str();
125 ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) );
127 mGameFile = gameFile;
130 GameDescription::iterator i = m_gameDescription.find( "type" );
131 if ( i == m_gameDescription.end() ) {
132 globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>( pDoc->URL ) << "'\n";
138 mGameType = ( *i ).second.c_str();
143 void CGameDescription::Dump(){
144 globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n";
145 for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i )
147 globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n";
151 CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
154 #include "warnings.h"
155 #include "stream/textfilestream.h"
156 #include "container/array.h"
157 #include "xml/ixml.h"
158 #include "xml/xmlparser.h"
159 #include "xml/xmlwriter.h"
161 #include "preferencedictionary.h"
162 #include "stringio.h"
164 const char* const PREFERENCES_VERSION = "1.0";
166 bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){
168 TextFileInputStream file( filename );
169 if ( !file.failed() ) {
170 XMLStreamParser parser( file );
171 XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION );
172 parser.exportXML( importer );
176 int l = strlen( cmdline_prefix );
177 for ( int i = 1; i < g_argc - 1; ++i )
179 if ( g_argv[i][0] == '-' ) {
180 if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) {
181 if ( g_argv[i][l + 1] == '-' ) {
182 preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] );
192 bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){
193 TextFileOutputStream file( filename );
194 if ( !file.failed() ) {
195 XMLStreamWriter writer( file );
196 XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION );
197 exporter.exportXML( writer );
203 bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
204 Array<char> tmpName( filename, filename + strlen( filename ) + 1 + 3 );
205 *( tmpName.end() - 4 ) = 'T';
206 *( tmpName.end() - 3 ) = 'M';
207 *( tmpName.end() - 2 ) = 'P';
208 *( tmpName.end() - 1 ) = '\0';
210 return Preferences_Save( preferences, tmpName.data() )
211 && ( !file_exists( filename ) || file_remove( filename ) )
212 && file_move( tmpName.data(), filename );
217 void LogConsole_importString( const char* string ){
218 g_Console_enableLogging = string_equal( string, "true" );
219 Sys_LogFile( g_Console_enableLogging );
221 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
224 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
225 preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
226 preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
227 preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
231 PreferenceDictionary g_global_preferences;
233 void GlobalPreferences_Init(){
234 RegisterGlobalPreferences( g_global_preferences );
237 void CGameDialog::LoadPrefs(){
238 // load global .pref file
239 StringOutputStream strGlobalPref( 256 );
240 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
242 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
244 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
245 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
249 void CGameDialog::SavePrefs(){
250 StringOutputStream strGlobalPref( 256 );
251 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
253 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
255 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
256 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
260 void CGameDialog::DoGameDialog(){
264 // we save the prefs file
268 void CGameDialog::GameFileImport( int value ){
269 m_nComboSelect = value;
270 // use value to set m_sGameFile
271 std::list<CGameDescription *>::iterator iGame = mGames.begin();
273 for ( i = 0; i < value; i++ )
277 m_sGameFile = ( *iGame )->mGameFile;
280 void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
281 // use m_sGameFile to set value
282 std::list<CGameDescription *>::const_iterator iGame;
284 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
286 if ( ( *iGame )->mGameFile == m_sGameFile ) {
292 importCallback( m_nComboSelect );
295 void CGameDialog_GameFileImport( CGameDialog& self, int value ){
296 self.GameFileImport( value );
299 void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
300 self.GameFileExport( importCallback );
303 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
304 std::vector<const char*> games;
305 games.reserve( mGames.size() );
306 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
308 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
312 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
313 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
314 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
316 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
319 ui::Window CGameDialog::BuildDialog(){
320 auto frame = create_dialog_frame( "Game settings", GTK_SHADOW_ETCHED_IN );
322 auto vbox2 = create_dialog_vbox( 0, 4 );
323 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
326 PreferencesPage preferencesPage( *this, ui::Widget(GTK_WIDGET( vbox2 )) );
327 Global_constructPreferences( preferencesPage );
328 CreateGlobalFrame( preferencesPage );
331 return create_simple_modal_dialog_window( "Global Preferences", m_modal, frame );
336 std::list<CGameDescription*>& mGames;
339 LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
341 void operator()( const char* name ) const {
342 if ( !extension_equal( path_get_extension( name ), "game" ) ) {
345 StringOutputStream strPath( 256 );
346 strPath << mPath << name;
347 globalOutputStream() << strPath.c_str() << '\n';
349 xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
351 mGames.push_front( new CGameDescription( pDoc, name ) );
356 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
361 void CGameDialog::ScanForGames(){
362 StringOutputStream strGamesPath( 256 );
363 strGamesPath << AppPath_get() << "games/";
364 const char *path = strGamesPath.c_str();
366 globalOutputStream() << "Scanning for game description files: " << path << '\n';
370 do we put game description files below AppPath, or in ~/.radiant
371 i.e. read only or read/write?
372 my guess .. readonly cause it's an install
373 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
374 (if that's really needed)
377 Directory_forEach( path, LoadGameFile( mGames, path ) );
380 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
381 std::list<CGameDescription *>::iterator iGame;
383 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
385 if ( i == m_nComboSelect ) {
389 return 0; // not found
392 void CGameDialog::InitGlobalPrefPath(){
393 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
396 void CGameDialog::Reset(){
397 if ( !g_Preferences.m_global_rc_path ) {
398 InitGlobalPrefPath();
400 StringOutputStream strGlobalPref( 256 );
401 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
402 file_remove( strGlobalPref.c_str() );
405 void CGameDialog::Init(){
406 InitGlobalPrefPath();
409 if ( mGames.empty() ) {
410 Error( "Didn't find any valid game file descriptions, aborting\n" );
414 std::list<CGameDescription *>::iterator iGame, iPrevGame;
415 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
417 if ( iPrevGame != mGames.end() ) {
418 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
419 CGameDescription *h = *iGame;
427 CGameDescription* currentGameDescription = 0;
429 if ( !m_bGamePrompt ) {
430 // search by .game name
431 std::list<CGameDescription *>::iterator iGame;
432 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
434 if ( ( *iGame )->mGameFile == m_sGameFile ) {
435 currentGameDescription = ( *iGame );
440 if ( m_bGamePrompt || !currentGameDescription ) {
443 // use m_nComboSelect to identify the game to run as and set the globals
444 currentGameDescription = GameDescriptionForComboItem();
445 ASSERT_NOTNULL( currentGameDescription );
447 g_pGameDescription = currentGameDescription;
449 g_pGameDescription->Dump();
452 CGameDialog::~CGameDialog(){
453 // free all the game descriptions
454 std::list<CGameDescription *>::iterator iGame;
455 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
465 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
466 const char* identifier = gameDescription.getKeyValue( "index" );
467 if ( string_empty( identifier ) ) {
474 CGameDialog g_GamesDialog;
477 // =============================================================================
478 // Widget callbacks for PrefsDlg
480 static void OnButtonClean( ui::Widget widget, gpointer data ){
481 // make sure this is what the user wants
482 if ( ui::Widget(GTK_WIDGET( g_Preferences.GetWidget() )).alert( "This will close Radiant and clean the corresponding registry entries.\n"
483 "Next time you start Radiant it will be good as new. Do you wish to continue?",
484 "Reset Registry", ui::alert_type::YESNO, ui::alert_icon::Asterisk ) == ui::alert_response::YES ) {
485 PrefsDlg *dlg = (PrefsDlg*)data;
486 dlg->EndModal( eIDCANCEL );
488 g_preferences_globals.disable_ini = true;
494 // =============================================================================
500 very first prefs init deals with selecting the game and the game tools path
501 then we can load .ini stuff
503 using prefs / ini settings:
506 look in ~/.radiant/<version>/gamename
510 #define PREFS_LOCAL_FILENAME "local.pref"
512 void PrefsDlg::Init(){
513 // m_global_rc_path has been set above
514 // m_rc_path is for game specific preferences
515 // takes the form: global-pref-path/gamename/prefs-file
517 // this is common to win32 and Linux init now
518 m_rc_path = g_string_new( m_global_rc_path->str );
521 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
522 g_string_append( m_rc_path, "/" );
523 Q_mkdir( m_rc_path->str );
526 m_inipath = g_string_new( m_rc_path->str );
527 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
530 void notebook_set_page( ui::Widget notebook, ui::Widget page ){
531 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
532 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
533 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
537 void PrefsDlg::showPrefPage( ui::Widget prefpage ){
538 notebook_set_page( m_notebook, prefpage );
542 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
543 PrefsDlg *dlg = (PrefsDlg*)data;
546 GtkTreeIter selected;
547 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
549 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
550 dlg->showPrefPage( prefpage );
554 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
556 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
557 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
564 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
565 callbacks.push_back( callback );
568 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
570 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
571 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
577 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
578 callbacks.push_back( callback );
581 PreferencesPageCallbacks g_interfacePreferences;
582 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
583 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
585 PreferenceGroupCallbacks g_interfaceCallbacks;
586 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
587 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
590 PreferencesPageCallbacks g_displayPreferences;
591 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
592 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
594 PreferenceGroupCallbacks g_displayCallbacks;
595 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
596 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
599 PreferencesPageCallbacks g_settingsPreferences;
600 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
601 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
603 PreferenceGroupCallbacks g_settingsCallbacks;
604 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
605 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
608 void Widget_updateDependency( ui::Widget self, ui::Widget toggleButton ){
609 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && gtk_widget_is_sensitive( toggleButton ) );
612 void ToggleButton_toggled_Widget_updateDependency( ui::Widget toggleButton, ui::Widget self ){
613 Widget_updateDependency( self, toggleButton );
616 void ToggleButton_state_changed_Widget_updateDependency( ui::Widget toggleButton, GtkStateType state, ui::Widget self ){
617 if ( state == GTK_STATE_INSENSITIVE ) {
618 Widget_updateDependency( self, toggleButton );
622 void Widget_connectToggleDependency( ui::Widget self, ui::Widget toggleButton ){
623 g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
624 g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
625 Widget_updateDependency( self, toggleButton );
629 inline ui::Widget getVBox( ui::Widget page ){
630 return ui::Widget(gtk_bin_get_child( GTK_BIN( page ) ));
633 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, ui::Widget page ){
635 gtk_tree_store_append( store, &group, parent );
636 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
640 ui::Widget PreferencePages_addPage( ui::Widget notebook, const char* name ){
641 ui::Widget preflabel = ui::Label( name );
642 gtk_widget_show( preflabel );
644 ui::Widget pageframe = ui::Frame( name );
645 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
646 gtk_widget_show( pageframe );
648 ui::Widget vbox = ui::VBox( FALSE, 4 );
649 gtk_widget_show( vbox );
650 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
651 gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
653 // Add the page to the notebook
654 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
659 class PreferenceTreeGroup : public PreferenceGroup
662 ui::Widget m_notebook;
663 GtkTreeStore* m_store;
666 PreferenceTreeGroup( Dialog& dialog, ui::Widget notebook, GtkTreeStore* store, GtkTreeIter group ) :
668 m_notebook( notebook ),
672 PreferencesPage createPage( const char* treeName, const char* frameName ){
673 ui::Widget page = PreferencePages_addPage( m_notebook, frameName );
674 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
675 return PreferencesPage( m_dialog, getVBox( page ) );
679 ui::Window PrefsDlg::BuildDialog(){
680 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
681 Mouse_registerPreferencesPage();
683 ui::Window dialog = ui::Window(create_floating_window( "NetRadiant Preferences", m_parent ));
686 ui::Widget mainvbox = ui::VBox( FALSE, 5 );
687 gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
688 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
689 gtk_widget_show( mainvbox );
692 ui::Widget hbox = ui::HBox( FALSE, 5 );
693 gtk_widget_show( hbox );
694 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
697 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
698 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
701 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
702 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
705 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
706 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
711 ui::Widget hbox = ui::HBox( FALSE, 5 );
712 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
713 gtk_widget_show( hbox );
716 ui::Widget sc_win = ui::ScrolledWindow();
717 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
718 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
719 gtk_widget_show( sc_win );
720 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
722 // prefs pages notebook
723 m_notebook = ui::Widget(gtk_notebook_new());
724 // hide the notebook tabs since its not supposed to look like a notebook
725 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
726 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
727 gtk_widget_show( m_notebook );
731 GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
733 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
734 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
737 auto renderer = ui::CellRendererText();
738 GtkTreeViewColumn* column = ui::TreeViewColumn( "Preferences", renderer, {{"text", 0}} );
739 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
743 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
744 g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
747 gtk_widget_show( view );
749 gtk_container_add( GTK_CONTAINER( sc_win ), view );
752 /********************************************************************/
753 /* Add preference tree options */
754 /********************************************************************/
757 PreferencePages_addPage( m_notebook, "Front Page" );
760 ui::Widget global = PreferencePages_addPage( m_notebook, "Global Preferences" );
762 PreferencesPage preferencesPage( *this, getVBox( global ) );
763 Global_constructPreferences( preferencesPage );
765 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
767 ui::Widget game = PreferencePages_addPage( m_notebook, "Game" );
768 PreferencesPage preferencesPage( *this, getVBox( game ) );
769 g_GamesDialog.CreateGlobalFrame( preferencesPage );
771 PreferenceTree_appendPage( store, &group, "Game", game );
776 ui::Widget interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
778 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
779 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
782 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
783 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
785 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
789 ui::Widget display = PreferencePages_addPage( m_notebook, "Display Preferences" );
791 PreferencesPage preferencesPage( *this, getVBox( display ) );
792 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
794 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
795 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
797 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
801 ui::Widget settings = PreferencePages_addPage( m_notebook, "General Settings" );
803 PreferencesPage preferencesPage( *this, getVBox( settings ) );
804 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
807 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
808 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
810 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
814 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
816 g_object_unref( G_OBJECT( store ) );
822 gtk_notebook_set_current_page( GTK_NOTEBOOK( m_notebook ), 0 );
827 preferences_globals_t g_preferences_globals;
829 PrefsDlg g_Preferences; // global prefs instance
832 void PreferencesDialog_constructWindow( ui::Window main_window ){
833 g_Preferences.m_parent = main_window;
834 g_Preferences.Create();
836 void PreferencesDialog_destroyWindow(){
837 g_Preferences.Destroy();
841 PreferenceDictionary g_preferences;
843 PreferenceSystem& GetPreferenceSystem(){
844 return g_preferences;
847 class PreferenceSystemAPI
849 PreferenceSystem* m_preferencesystem;
851 typedef PreferenceSystem Type;
852 STRING_CONSTANT( Name, "*" );
854 PreferenceSystemAPI(){
855 m_preferencesystem = &GetPreferenceSystem();
857 PreferenceSystem* getTable(){
858 return m_preferencesystem;
862 #include "modulesystem/singletonmodule.h"
863 #include "modulesystem/moduleregistry.h"
865 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
866 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
867 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
869 void Preferences_Load(){
870 g_GamesDialog.LoadPrefs();
872 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
874 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
875 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
879 void Preferences_Save(){
880 if ( g_preferences_globals.disable_ini ) {
884 g_GamesDialog.SavePrefs();
886 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
888 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
889 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
893 void Preferences_Reset(){
894 file_remove( g_Preferences.m_inipath->str );
898 void PrefsDlg::PostModal( EMessageBoxReturn code ){
899 if ( code == eIDOK ) {
905 std::vector<const char*> g_restart_required;
907 void PreferencesDialog_restartRequired( const char* staticName ){
908 g_restart_required.push_back( staticName );
911 void PreferencesDialog_showDialog(){
912 if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
913 if ( !g_restart_required.empty() ) {
914 StringOutputStream message( 256 );
915 message << "Preference changes require a restart:\n";
916 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
918 message << ( *i ) << '\n';
920 MainFrame_getWindow().alert( message.c_str() );
921 g_restart_required.clear();
930 void GameName_importString( const char* value ){
931 gamename_set( value );
933 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
934 void GameName_exportString( const StringImportCallback& importer ){
935 importer( gamename_get() );
937 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
939 void GameMode_importString( const char* value ){
940 gamemode_set( value );
942 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
943 void GameMode_exportString( const StringImportCallback& importer ){
944 importer( gamemode_get() );
946 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
949 void RegisterPreferences( PreferenceSystem& preferences ){
951 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
953 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
954 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
957 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
958 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
961 void Preferences_Init(){
962 RegisterPreferences( GetPreferenceSystem() );