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 CopiedString& 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 Array<char> tmpName( filename, filename + strlen( filename ) + 1 + 3 );
207 *( tmpName.end() - 4 ) = 'T';
208 *( tmpName.end() - 3 ) = 'M';
209 *( tmpName.end() - 2 ) = 'P';
210 *( tmpName.end() - 1 ) = '\0';
212 return Preferences_Save( preferences, tmpName.data() )
213 && ( !file_exists( filename ) || file_remove( filename ) )
214 && file_move( tmpName.data(), filename );
219 void LogConsole_importString( const char* string ){
220 g_Console_enableLogging = string_equal( string, "true" );
221 Sys_LogFile( g_Console_enableLogging );
223 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
226 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
227 preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
228 preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
229 preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
233 PreferenceDictionary g_global_preferences;
235 void GlobalPreferences_Init(){
236 RegisterGlobalPreferences( g_global_preferences );
239 void CGameDialog::LoadPrefs(){
240 // load global .pref file
241 StringOutputStream strGlobalPref( 256 );
242 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
244 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
246 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
247 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
251 void CGameDialog::SavePrefs(){
252 StringOutputStream strGlobalPref( 256 );
253 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
255 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
257 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
258 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
262 void CGameDialog::DoGameDialog(){
266 // we save the prefs file
270 void CGameDialog::GameFileImport( int value ){
271 m_nComboSelect = value;
272 // use value to set m_sGameFile
273 std::list<CGameDescription *>::iterator iGame = mGames.begin();
275 for ( i = 0; i < value; i++ )
279 m_sGameFile = ( *iGame )->mGameFile;
282 void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
283 // use m_sGameFile to set value
284 std::list<CGameDescription *>::const_iterator iGame;
286 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
288 if ( ( *iGame )->mGameFile == m_sGameFile ) {
294 importCallback( m_nComboSelect );
297 void CGameDialog_GameFileImport( CGameDialog& self, int value ){
298 self.GameFileImport( value );
301 void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
302 self.GameFileExport( importCallback );
305 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
306 std::vector<const char*> games;
307 games.reserve( mGames.size() );
308 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
310 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
314 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
315 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
316 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
318 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
321 ui::Window CGameDialog::BuildDialog(){
322 auto frame = create_dialog_frame( "Game settings", GTK_SHADOW_ETCHED_IN );
324 auto vbox2 = create_dialog_vbox( 0, 4 );
325 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
328 PreferencesPage preferencesPage( *this, ui::Widget(GTK_WIDGET( vbox2 )) );
329 Global_constructPreferences( preferencesPage );
330 CreateGlobalFrame( preferencesPage );
333 return create_simple_modal_dialog_window( "Global Preferences", m_modal, frame );
338 std::list<CGameDescription*>& mGames;
341 LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
343 void operator()( const char* name ) const {
344 if ( !extension_equal( path_get_extension( name ), "game" ) ) {
347 StringOutputStream strPath( 256 );
348 strPath << mPath << name;
349 globalOutputStream() << strPath.c_str() << '\n';
351 xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
353 mGames.push_front( new CGameDescription( pDoc, name ) );
358 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
363 void CGameDialog::ScanForGames(){
364 StringOutputStream strGamesPath( 256 );
365 strGamesPath << AppPath_get() << "games/";
366 const char *path = strGamesPath.c_str();
368 globalOutputStream() << "Scanning for game description files: " << path << '\n';
372 do we put game description files below AppPath, or in ~/.radiant
373 i.e. read only or read/write?
374 my guess .. readonly cause it's an install
375 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
376 (if that's really needed)
379 Directory_forEach( path, LoadGameFile( mGames, path ) );
382 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
383 std::list<CGameDescription *>::iterator iGame;
385 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
387 if ( i == m_nComboSelect ) {
391 return 0; // not found
394 void CGameDialog::InitGlobalPrefPath(){
395 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
398 void CGameDialog::Reset(){
399 if ( !g_Preferences.m_global_rc_path ) {
400 InitGlobalPrefPath();
402 StringOutputStream strGlobalPref( 256 );
403 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
404 file_remove( strGlobalPref.c_str() );
407 void CGameDialog::Init(){
408 InitGlobalPrefPath();
411 if ( mGames.empty() ) {
412 Error( "Didn't find any valid game file descriptions, aborting\n" );
416 std::list<CGameDescription *>::iterator iGame, iPrevGame;
417 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
419 if ( iPrevGame != mGames.end() ) {
420 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
421 CGameDescription *h = *iGame;
429 CGameDescription* currentGameDescription = 0;
431 if ( !m_bGamePrompt ) {
432 // search by .game name
433 std::list<CGameDescription *>::iterator iGame;
434 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
436 if ( ( *iGame )->mGameFile == m_sGameFile ) {
437 currentGameDescription = ( *iGame );
442 if ( m_bGamePrompt || !currentGameDescription ) {
445 // use m_nComboSelect to identify the game to run as and set the globals
446 currentGameDescription = GameDescriptionForComboItem();
447 ASSERT_NOTNULL( currentGameDescription );
449 g_pGameDescription = currentGameDescription;
451 g_pGameDescription->Dump();
454 CGameDialog::~CGameDialog(){
455 // free all the game descriptions
456 std::list<CGameDescription *>::iterator iGame;
457 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
467 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
468 const char* identifier = gameDescription.getKeyValue( "index" );
469 if ( string_empty( identifier ) ) {
476 CGameDialog g_GamesDialog;
479 // =============================================================================
480 // Widget callbacks for PrefsDlg
482 static void OnButtonClean( ui::Widget widget, gpointer data ){
483 // make sure this is what the user wants
484 if ( ui::Widget(GTK_WIDGET( g_Preferences.GetWidget() )).alert( "This will close Radiant and clean the corresponding registry entries.\n"
485 "Next time you start Radiant it will be good as new. Do you wish to continue?",
486 "Reset Registry", ui::alert_type::YESNO, ui::alert_icon::Asterisk ) == ui::alert_response::YES ) {
487 PrefsDlg *dlg = (PrefsDlg*)data;
488 dlg->EndModal( eIDCANCEL );
490 g_preferences_globals.disable_ini = true;
496 // =============================================================================
502 very first prefs init deals with selecting the game and the game tools path
503 then we can load .ini stuff
505 using prefs / ini settings:
508 look in ~/.radiant/<version>/gamename
512 #define PREFS_LOCAL_FILENAME "local.pref"
514 void PrefsDlg::Init(){
515 // m_global_rc_path has been set above
516 // m_rc_path is for game specific preferences
517 // takes the form: global-pref-path/gamename/prefs-file
519 // this is common to win32 and Linux init now
520 m_rc_path = g_string_new( m_global_rc_path->str );
523 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
524 g_string_append( m_rc_path, "/" );
525 Q_mkdir( m_rc_path->str );
528 m_inipath = g_string_new( m_rc_path->str );
529 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
532 void notebook_set_page( ui::Widget notebook, ui::Widget page ){
533 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
534 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
535 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
539 void PrefsDlg::showPrefPage( ui::Widget prefpage ){
540 notebook_set_page( m_notebook, prefpage );
544 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
545 PrefsDlg *dlg = (PrefsDlg*)data;
548 GtkTreeIter selected;
549 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
551 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
552 dlg->showPrefPage( prefpage );
556 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
558 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
559 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
566 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
567 callbacks.push_back( callback );
570 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
572 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
573 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
579 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
580 callbacks.push_back( callback );
583 PreferencesPageCallbacks g_interfacePreferences;
584 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
585 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
587 PreferenceGroupCallbacks g_interfaceCallbacks;
588 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
589 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
592 PreferencesPageCallbacks g_displayPreferences;
593 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
594 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
596 PreferenceGroupCallbacks g_displayCallbacks;
597 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
598 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
601 PreferencesPageCallbacks g_settingsPreferences;
602 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
603 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
605 PreferenceGroupCallbacks g_settingsCallbacks;
606 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
607 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
610 void Widget_updateDependency( ui::Widget self, ui::Widget toggleButton ){
611 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && gtk_widget_is_sensitive( toggleButton ) );
614 void ToggleButton_toggled_Widget_updateDependency( ui::Widget toggleButton, ui::Widget self ){
615 Widget_updateDependency( self, toggleButton );
618 void ToggleButton_state_changed_Widget_updateDependency( ui::Widget toggleButton, GtkStateType state, ui::Widget self ){
619 if ( state == GTK_STATE_INSENSITIVE ) {
620 Widget_updateDependency( self, toggleButton );
624 void Widget_connectToggleDependency( ui::Widget self, ui::Widget toggleButton ){
625 g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
626 g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
627 Widget_updateDependency( self, toggleButton );
631 inline ui::Widget getVBox( ui::Widget page ){
632 return ui::Widget(gtk_bin_get_child( GTK_BIN( page ) ));
635 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, ui::Widget page ){
637 gtk_tree_store_append( store, &group, parent );
638 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
642 ui::Widget PreferencePages_addPage( ui::Widget notebook, const char* name ){
643 ui::Widget preflabel = ui::Label( name );
644 gtk_widget_show( preflabel );
646 ui::Widget pageframe = ui::Frame( name );
647 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
648 gtk_widget_show( pageframe );
650 ui::Widget vbox = ui::VBox( FALSE, 4 );
651 gtk_widget_show( vbox );
652 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
653 gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
655 // Add the page to the notebook
656 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
661 class PreferenceTreeGroup : public PreferenceGroup
664 ui::Widget m_notebook;
665 GtkTreeStore* m_store;
668 PreferenceTreeGroup( Dialog& dialog, ui::Widget notebook, GtkTreeStore* store, GtkTreeIter group ) :
670 m_notebook( notebook ),
674 PreferencesPage createPage( const char* treeName, const char* frameName ){
675 ui::Widget page = PreferencePages_addPage( m_notebook, frameName );
676 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
677 return PreferencesPage( m_dialog, getVBox( page ) );
681 ui::Window PrefsDlg::BuildDialog(){
682 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
683 Mouse_registerPreferencesPage();
685 ui::Window dialog = ui::Window(create_floating_window( "NetRadiant Preferences", m_parent ));
688 ui::Widget mainvbox = ui::VBox( FALSE, 5 );
689 gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
690 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
691 gtk_widget_show( mainvbox );
694 ui::Widget hbox = ui::HBox( FALSE, 5 );
695 gtk_widget_show( hbox );
696 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
699 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
700 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
703 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
704 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
707 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
708 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
713 ui::Widget hbox = ui::HBox( FALSE, 5 );
714 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
715 gtk_widget_show( hbox );
718 ui::Widget sc_win = ui::ScrolledWindow();
719 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
720 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
721 gtk_widget_show( sc_win );
722 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
724 // prefs pages notebook
725 m_notebook = ui::Widget(gtk_notebook_new());
726 // hide the notebook tabs since its not supposed to look like a notebook
727 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
728 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
729 gtk_widget_show( m_notebook );
733 GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
735 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
736 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
739 auto renderer = ui::CellRendererText();
740 GtkTreeViewColumn* column = ui::TreeViewColumn( "Preferences", renderer, {{"text", 0}} );
741 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
745 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
746 g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
749 gtk_widget_show( view );
751 gtk_container_add( GTK_CONTAINER( sc_win ), view );
754 /********************************************************************/
755 /* Add preference tree options */
756 /********************************************************************/
759 PreferencePages_addPage( m_notebook, "Front Page" );
762 ui::Widget global = PreferencePages_addPage( m_notebook, "Global Preferences" );
764 PreferencesPage preferencesPage( *this, getVBox( global ) );
765 Global_constructPreferences( preferencesPage );
767 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
769 ui::Widget game = PreferencePages_addPage( m_notebook, "Game" );
770 PreferencesPage preferencesPage( *this, getVBox( game ) );
771 g_GamesDialog.CreateGlobalFrame( preferencesPage );
773 PreferenceTree_appendPage( store, &group, "Game", game );
778 ui::Widget interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
780 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
781 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
784 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
785 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
787 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
791 ui::Widget display = PreferencePages_addPage( m_notebook, "Display Preferences" );
793 PreferencesPage preferencesPage( *this, getVBox( display ) );
794 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
796 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
797 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
799 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
803 ui::Widget settings = PreferencePages_addPage( m_notebook, "General Settings" );
805 PreferencesPage preferencesPage( *this, getVBox( settings ) );
806 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
809 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
810 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
812 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
816 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
818 g_object_unref( G_OBJECT( store ) );
824 gtk_notebook_set_current_page( GTK_NOTEBOOK( m_notebook ), 0 );
829 preferences_globals_t g_preferences_globals;
831 PrefsDlg g_Preferences; // global prefs instance
834 void PreferencesDialog_constructWindow( ui::Window main_window ){
835 g_Preferences.m_parent = main_window;
836 g_Preferences.Create();
838 void PreferencesDialog_destroyWindow(){
839 g_Preferences.Destroy();
843 PreferenceDictionary g_preferences;
845 PreferenceSystem& GetPreferenceSystem(){
846 return g_preferences;
849 class PreferenceSystemAPI
851 PreferenceSystem* m_preferencesystem;
853 typedef PreferenceSystem Type;
854 STRING_CONSTANT( Name, "*" );
856 PreferenceSystemAPI(){
857 m_preferencesystem = &GetPreferenceSystem();
859 PreferenceSystem* getTable(){
860 return m_preferencesystem;
864 #include "modulesystem/singletonmodule.h"
865 #include "modulesystem/moduleregistry.h"
867 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
868 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
869 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
871 void Preferences_Load(){
872 g_GamesDialog.LoadPrefs();
874 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
876 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
877 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
881 void Preferences_Save(){
882 if ( g_preferences_globals.disable_ini ) {
886 g_GamesDialog.SavePrefs();
888 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
890 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
891 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
895 void Preferences_Reset(){
896 file_remove( g_Preferences.m_inipath->str );
900 void PrefsDlg::PostModal( EMessageBoxReturn code ){
901 if ( code == eIDOK ) {
907 std::vector<const char*> g_restart_required;
909 void PreferencesDialog_restartRequired( const char* staticName ){
910 g_restart_required.push_back( staticName );
913 void PreferencesDialog_showDialog(){
914 if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
915 if ( !g_restart_required.empty() ) {
916 StringOutputStream message( 256 );
917 message << "Preference changes require a restart:\n";
918 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
920 message << ( *i ) << '\n';
922 MainFrame_getWindow().alert( message.c_str() );
923 g_restart_required.clear();
932 void GameName_importString( const char* value ){
933 gamename_set( value );
935 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
936 void GameName_exportString( const StringImportCallback& importer ){
937 importer( gamename_get() );
939 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
941 void GameMode_importString( const char* value ){
942 gamemode_set( value );
944 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
945 void GameMode_exportString( const StringImportCallback& importer ){
946 importer( gamemode_get() );
948 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
951 void RegisterPreferences( PreferenceSystem& preferences ){
953 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
955 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
956 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
959 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
960 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
963 void Preferences_Init(){
964 RegisterPreferences( GetPreferenceSystem() );