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"
35 #include "generic/callback.h"
36 #include "math/vector.h"
37 #include "string/string.h"
38 #include "stream/stringstream.h"
42 #include "gtkutil/filechooser.h"
43 #include "gtkutil/messagebox.h"
49 #include "mainframe.h"
55 void Global_constructPreferences( PreferencesPage& page ){
56 page.appendCheckBox( "Console", "Enable Logging", g_Console_enableLogging );
59 void Interface_constructPreferences( PreferencesPage& page ){
61 page.appendCheckBox( "", "Default Text Editor", g_TextEditor_useWin32Editor );
64 ui::CheckButton use_custom = page.appendCheckBox( "Text Editor", "Custom", g_TextEditor_useCustomEditor );
65 ui::Widget custom_editor = page.appendPathEntry( "Text Editor Command", g_TextEditor_editorCommand, true );
66 Widget_connectToggleDependency( custom_editor, use_custom );
71 void Mouse_constructPreferences( PreferencesPage& page ){
73 const char* buttons[] = { "2 button", "3 button", };
74 page.appendRadio( "Mouse Type", g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) );
76 page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick );
78 void Mouse_constructPage( PreferenceGroup& group ){
79 PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) );
80 Mouse_constructPreferences( page );
82 void Mouse_registerPreferencesPage(){
83 PreferencesDialog_addInterfacePage( FreeCaller1<PreferenceGroup&, Mouse_constructPage>() );
88 =========================================================
89 Games selection dialog
90 =========================================================
94 #include <uilib/uilib.h>
96 inline const char* xmlAttr_getName( xmlAttrPtr attr ){
97 return reinterpret_cast<const char*>( attr->name );
100 inline const char* xmlAttr_getValue( xmlAttrPtr attr ){
101 return reinterpret_cast<const char*>( attr->children->content );
104 CGameDescription::CGameDescription( xmlDocPtr pDoc, const std::string& gameFile ){
105 // read the user-friendly game name
106 xmlNodePtr pNode = pDoc->children;
108 while ( strcmp( (const char*)pNode->name, "game" ) && pNode != 0 )
113 Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL );
116 for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next )
118 m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) );
122 StringOutputStream path( 256 );
123 path << AppPath_get() << gameFile.c_str() << "/";
124 mGameToolsPath = path.c_str();
127 ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) );
129 mGameFile = gameFile;
132 GameDescription::iterator i = m_gameDescription.find( "type" );
133 if ( i == m_gameDescription.end() ) {
134 globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>( pDoc->URL ) << "'\n";
140 mGameType = ( *i ).second.c_str();
145 void CGameDescription::Dump(){
146 globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n";
147 for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i )
149 globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n";
153 CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
156 #include "warnings.h"
157 #include "stream/textfilestream.h"
158 #include "container/array.h"
159 #include "xml/ixml.h"
160 #include "xml/xmlparser.h"
161 #include "xml/xmlwriter.h"
163 #include "preferencedictionary.h"
164 #include "stringio.h"
166 const char* const PREFERENCES_VERSION = "1.0";
168 bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){
170 TextFileInputStream file( filename );
171 if ( !file.failed() ) {
172 XMLStreamParser parser( file );
173 XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION );
174 parser.exportXML( importer );
178 int l = strlen( cmdline_prefix );
179 for ( int i = 1; i < g_argc - 1; ++i )
181 if ( g_argv[i][0] == '-' ) {
182 if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) {
183 if ( g_argv[i][l + 1] == '-' ) {
184 preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] );
194 bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){
195 TextFileOutputStream file( filename );
196 if ( !file.failed() ) {
197 XMLStreamWriter writer( file );
198 XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION );
199 exporter.exportXML( writer );
205 bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
206 std::string tmpName( filename );
209 return Preferences_Save( preferences, tmpName.c_str() )
210 && ( !file_exists( filename ) || file_remove( filename ) )
211 && file_move( tmpName.c_str(), filename );
216 void LogConsole_importString( const char* string ){
217 g_Console_enableLogging = string_equal( string, "true" );
218 Sys_LogFile( g_Console_enableLogging );
220 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
223 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
224 preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
225 preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
226 preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
230 PreferenceDictionary g_global_preferences;
232 void GlobalPreferences_Init(){
233 RegisterGlobalPreferences( g_global_preferences );
236 void CGameDialog::LoadPrefs(){
237 // load global .pref file
238 StringOutputStream strGlobalPref( 256 );
239 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
241 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
243 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
244 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
248 void CGameDialog::SavePrefs(){
249 StringOutputStream strGlobalPref( 256 );
250 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
252 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
254 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
255 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
259 void CGameDialog::DoGameDialog(){
263 // we save the prefs file
267 void CGameDialog::GameFileImport( int value ){
268 m_nComboSelect = value;
269 // use value to set m_sGameFile
270 std::list<CGameDescription *>::iterator iGame = mGames.begin();
272 for ( i = 0; i < value; i++ )
276 m_sGameFile = ( *iGame )->mGameFile;
279 void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
280 // use m_sGameFile to set value
281 std::list<CGameDescription *>::const_iterator iGame;
283 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
285 if ( ( *iGame )->mGameFile == m_sGameFile ) {
291 importCallback( m_nComboSelect );
294 void CGameDialog_GameFileImport( CGameDialog& self, int value ){
295 self.GameFileImport( value );
298 void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
299 self.GameFileExport( importCallback );
302 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
303 std::vector<const char*> games;
304 games.reserve( mGames.size() );
305 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
307 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
311 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
312 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
313 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
315 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
318 ui::Window CGameDialog::BuildDialog(){
319 GtkFrame* frame = create_dialog_frame( "Game settings", GTK_SHADOW_ETCHED_IN );
321 GtkVBox* vbox2 = create_dialog_vbox( 0, 4 );
322 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
325 PreferencesPage preferencesPage( *this, ui::Widget(GTK_WIDGET( vbox2 )) );
326 Global_constructPreferences( preferencesPage );
327 CreateGlobalFrame( preferencesPage );
330 return ui::Window(create_simple_modal_dialog_window( "Global Preferences", m_modal, GTK_WIDGET( frame ) ));
335 std::list<CGameDescription*>& mGames;
338 LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
340 void operator()( const char* name ) const {
341 if ( !extension_equal( path_get_extension( name ), "game" ) ) {
344 StringOutputStream strPath( 256 );
345 strPath << mPath << name;
346 globalOutputStream() << strPath.c_str() << '\n';
348 xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
350 mGames.push_front( new CGameDescription( pDoc, name ) );
355 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
360 void CGameDialog::ScanForGames(){
361 StringOutputStream strGamesPath( 256 );
362 strGamesPath << AppPath_get() << "games/";
363 const char *path = strGamesPath.c_str();
365 globalOutputStream() << "Scanning for game description files: " << path << '\n';
369 do we put game description files below AppPath, or in ~/.radiant
370 i.e. read only or read/write?
371 my guess .. readonly cause it's an install
372 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
373 (if that's really needed)
376 Directory_forEach( path, LoadGameFile( mGames, path ) );
379 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
380 std::list<CGameDescription *>::iterator iGame;
382 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
384 if ( i == m_nComboSelect ) {
388 return 0; // not found
391 void CGameDialog::InitGlobalPrefPath(){
392 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
395 void CGameDialog::Reset(){
396 if ( !g_Preferences.m_global_rc_path ) {
397 InitGlobalPrefPath();
399 StringOutputStream strGlobalPref( 256 );
400 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
401 file_remove( strGlobalPref.c_str() );
404 void CGameDialog::Init(){
405 InitGlobalPrefPath();
408 if ( mGames.empty() ) {
409 Error( "Didn't find any valid game file descriptions, aborting\n" );
413 std::list<CGameDescription *>::iterator iGame, iPrevGame;
414 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
416 if ( iPrevGame != mGames.end() ) {
417 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
418 CGameDescription *h = *iGame;
426 CGameDescription* currentGameDescription = 0;
428 if ( !m_bGamePrompt ) {
429 // search by .game name
430 std::list<CGameDescription *>::iterator iGame;
431 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
433 if ( ( *iGame )->mGameFile == m_sGameFile ) {
434 currentGameDescription = ( *iGame );
439 if ( m_bGamePrompt || !currentGameDescription ) {
442 // use m_nComboSelect to identify the game to run as and set the globals
443 currentGameDescription = GameDescriptionForComboItem();
444 ASSERT_NOTNULL( currentGameDescription );
446 g_pGameDescription = currentGameDescription;
448 g_pGameDescription->Dump();
451 CGameDialog::~CGameDialog(){
452 // free all the game descriptions
453 std::list<CGameDescription *>::iterator iGame;
454 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
464 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
465 const char* identifier = gameDescription.getKeyValue( "index" );
466 if ( string_empty( identifier ) ) {
472 void CGameDialog::AddPacksURL( StringOutputStream &URL ){
473 // add the URLs for the list of game packs installed
474 // FIXME: this is kinda hardcoded for now..
475 std::list<CGameDescription *>::iterator iGame;
476 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
478 URL << "&Games_dlup%5B%5D=" << GameDescription_getIdentifier( *( *iGame ) );
482 CGameDialog g_GamesDialog;
485 // =============================================================================
486 // Widget callbacks for PrefsDlg
488 static void OnButtonClean( ui::Widget widget, gpointer data ){
489 // make sure this is what the user wants
490 if ( ui::Widget(GTK_WIDGET( g_Preferences.GetWidget() )).alert( "This will close Radiant and clean the corresponding registry entries.\n"
491 "Next time you start Radiant it will be good as new. Do you wish to continue?",
492 "Reset Registry", ui::alert_type::YESNO, ui::alert_icon::ASTERISK ) == ui::alert_response::YES ) {
493 PrefsDlg *dlg = (PrefsDlg*)data;
494 dlg->EndModal( eIDCANCEL );
496 g_preferences_globals.disable_ini = true;
502 // =============================================================================
508 very first prefs init deals with selecting the game and the game tools path
509 then we can load .ini stuff
511 using prefs / ini settings:
514 look in ~/.radiant/<version>/gamename
518 #define PREFS_LOCAL_FILENAME "local.pref"
520 void PrefsDlg::Init(){
521 // m_global_rc_path has been set above
522 // m_rc_path is for game specific preferences
523 // takes the form: global-pref-path/gamename/prefs-file
525 // this is common to win32 and Linux init now
526 m_rc_path = g_string_new( m_global_rc_path->str );
529 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
530 g_string_append( m_rc_path, "/" );
531 Q_mkdir( m_rc_path->str );
534 m_inipath = g_string_new( m_rc_path->str );
535 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
538 void notebook_set_page( ui::Widget notebook, ui::Widget page ){
539 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
540 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
541 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
545 void PrefsDlg::showPrefPage( ui::Widget prefpage ){
546 notebook_set_page( m_notebook, prefpage );
550 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
551 PrefsDlg *dlg = (PrefsDlg*)data;
554 GtkTreeIter selected;
555 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
557 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
558 dlg->showPrefPage( prefpage );
562 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
564 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
565 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
572 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
573 callbacks.push_back( callback );
576 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
578 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
579 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
585 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
586 callbacks.push_back( callback );
589 PreferencesPageCallbacks g_interfacePreferences;
590 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
591 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
593 PreferenceGroupCallbacks g_interfaceCallbacks;
594 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
595 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
598 PreferencesPageCallbacks g_displayPreferences;
599 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
600 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
602 PreferenceGroupCallbacks g_displayCallbacks;
603 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
604 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
607 PreferencesPageCallbacks g_settingsPreferences;
608 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
609 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
611 PreferenceGroupCallbacks g_settingsCallbacks;
612 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
613 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
616 void Widget_updateDependency( ui::Widget self, ui::Widget toggleButton ){
617 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && gtk_widget_is_sensitive( toggleButton ) );
620 void ToggleButton_toggled_Widget_updateDependency( ui::Widget toggleButton, ui::Widget self ){
621 Widget_updateDependency( self, toggleButton );
624 void ToggleButton_state_changed_Widget_updateDependency( ui::Widget toggleButton, GtkStateType state, ui::Widget self ){
625 if ( state == GTK_STATE_INSENSITIVE ) {
626 Widget_updateDependency( self, toggleButton );
630 void Widget_connectToggleDependency( ui::Widget self, ui::Widget toggleButton ){
631 g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
632 g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
633 Widget_updateDependency( self, toggleButton );
637 inline ui::Widget getVBox( ui::Widget page ){
638 return ui::Widget(gtk_bin_get_child( GTK_BIN( page ) ));
641 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, ui::Widget page ){
643 gtk_tree_store_append( store, &group, parent );
644 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
648 ui::Widget PreferencePages_addPage( ui::Widget notebook, const char* name ){
649 ui::Widget preflabel = ui::Label( name );
650 gtk_widget_show( preflabel );
652 ui::Widget pageframe = ui::Frame( name );
653 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
654 gtk_widget_show( pageframe );
656 ui::Widget vbox = ui::VBox( FALSE, 4 );
657 gtk_widget_show( vbox );
658 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
659 gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
661 // Add the page to the notebook
662 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
667 class PreferenceTreeGroup : public PreferenceGroup
670 ui::Widget m_notebook;
671 GtkTreeStore* m_store;
674 PreferenceTreeGroup( Dialog& dialog, ui::Widget notebook, GtkTreeStore* store, GtkTreeIter group ) :
676 m_notebook( notebook ),
680 PreferencesPage createPage( const char* treeName, const char* frameName ){
681 ui::Widget page = PreferencePages_addPage( m_notebook, frameName );
682 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
683 return PreferencesPage( m_dialog, getVBox( page ) );
687 ui::Window PrefsDlg::BuildDialog(){
688 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
689 Mouse_registerPreferencesPage();
691 ui::Window dialog = ui::Window(create_floating_window( "NetRadiant Preferences", m_parent ));
694 ui::Widget mainvbox = ui::VBox( FALSE, 5 );
695 gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
696 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
697 gtk_widget_show( mainvbox );
700 ui::Widget hbox = ui::HBox( FALSE, 5 );
701 gtk_widget_show( hbox );
702 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
705 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
706 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
709 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
710 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
713 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
714 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
719 ui::Widget hbox = ui::HBox( FALSE, 5 );
720 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
721 gtk_widget_show( hbox );
724 ui::Widget sc_win = ui::ScrolledWindow();
725 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
726 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
727 gtk_widget_show( sc_win );
728 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
730 // prefs pages notebook
731 m_notebook = ui::Widget(gtk_notebook_new());
732 // hide the notebook tabs since its not supposed to look like a notebook
733 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
734 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
735 gtk_widget_show( m_notebook );
739 GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
741 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
742 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
745 auto renderer = ui::CellRendererText();
746 GtkTreeViewColumn* column = ui::TreeViewColumn( "Preferences", renderer, {{"text", 0}} );
747 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
751 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
752 g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
755 gtk_widget_show( view );
757 gtk_container_add( GTK_CONTAINER( sc_win ), view );
760 /********************************************************************/
761 /* Add preference tree options */
762 /********************************************************************/
765 PreferencePages_addPage( m_notebook, "Front Page" );
768 ui::Widget global = PreferencePages_addPage( m_notebook, "Global Preferences" );
770 PreferencesPage preferencesPage( *this, getVBox( global ) );
771 Global_constructPreferences( preferencesPage );
773 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
775 ui::Widget game = PreferencePages_addPage( m_notebook, "Game" );
776 PreferencesPage preferencesPage( *this, getVBox( game ) );
777 g_GamesDialog.CreateGlobalFrame( preferencesPage );
779 PreferenceTree_appendPage( store, &group, "Game", game );
784 ui::Widget interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
786 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
787 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
790 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
791 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
793 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
797 ui::Widget display = PreferencePages_addPage( m_notebook, "Display Preferences" );
799 PreferencesPage preferencesPage( *this, getVBox( display ) );
800 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
802 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
803 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
805 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
809 ui::Widget settings = PreferencePages_addPage( m_notebook, "General Settings" );
811 PreferencesPage preferencesPage( *this, getVBox( settings ) );
812 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
815 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
816 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
818 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
822 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
824 g_object_unref( G_OBJECT( store ) );
830 gtk_notebook_set_current_page( GTK_NOTEBOOK( m_notebook ), 0 );
835 preferences_globals_t g_preferences_globals;
837 PrefsDlg g_Preferences; // global prefs instance
840 void PreferencesDialog_constructWindow( ui::Window main_window ){
841 g_Preferences.m_parent = main_window;
842 g_Preferences.Create();
844 void PreferencesDialog_destroyWindow(){
845 g_Preferences.Destroy();
849 PreferenceDictionary g_preferences;
851 PreferenceSystem& GetPreferenceSystem(){
852 return g_preferences;
855 class PreferenceSystemAPI
857 PreferenceSystem* m_preferencesystem;
859 typedef PreferenceSystem Type;
860 STRING_CONSTANT( Name, "*" );
862 PreferenceSystemAPI(){
863 m_preferencesystem = &GetPreferenceSystem();
865 PreferenceSystem* getTable(){
866 return m_preferencesystem;
870 #include "modulesystem/singletonmodule.h"
871 #include "modulesystem/moduleregistry.h"
873 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
874 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
875 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
877 void Preferences_Load(){
878 g_GamesDialog.LoadPrefs();
880 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
882 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
883 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
887 void Preferences_Save(){
888 if ( g_preferences_globals.disable_ini ) {
892 g_GamesDialog.SavePrefs();
894 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
896 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
897 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
901 void Preferences_Reset(){
902 file_remove( g_Preferences.m_inipath->str );
906 void PrefsDlg::PostModal( EMessageBoxReturn code ){
907 if ( code == eIDOK ) {
913 std::vector<const char*> g_restart_required;
915 void PreferencesDialog_restartRequired( const char* staticName ){
916 g_restart_required.push_back( staticName );
919 void PreferencesDialog_showDialog(){
920 if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
921 if ( !g_restart_required.empty() ) {
922 StringOutputStream message( 256 );
923 message << "Preference changes require a restart:\n";
924 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
926 message << ( *i ) << '\n';
928 MainFrame_getWindow().alert( message.c_str() );
929 g_restart_required.clear();
938 void GameName_importString( const char* value ){
939 gamename_set( value );
941 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
942 void GameName_exportString( const StringImportCallback& importer ){
943 importer( gamename_get() );
945 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
947 void GameMode_importString( const char* value ){
948 gamemode_set( value );
950 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
951 void GameMode_exportString( const StringImportCallback& importer ){
952 importer( gamemode_get() );
954 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
957 void RegisterPreferences( PreferenceSystem& preferences ){
959 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
961 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
962 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
965 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
966 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
969 void Preferences_Init(){
970 RegisterPreferences( GetPreferenceSystem() );