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 <gtk/gtkmain.h>
34 #include <gtk/gtkvbox.h>
35 #include <gtk/gtkhbox.h>
36 #include <gtk/gtkframe.h>
37 #include <gtk/gtklabel.h>
38 #include <gtk/gtktogglebutton.h>
39 #include <gtk/gtkspinbutton.h>
40 #include <gtk/gtkscrolledwindow.h>
41 #include <gtk/gtktreemodel.h>
42 #include <gtk/gtktreeview.h>
43 #include <gtk/gtktreestore.h>
44 #include <gtk/gtktreeselection.h>
45 #include <gtk/gtkcellrenderertext.h>
46 #include <gtk/gtknotebook.h>
48 #include "generic/callback.h"
49 #include "math/vector.h"
50 #include "string/string.h"
51 #include "stream/stringstream.h"
55 #include "gtkutil/filechooser.h"
56 #include "gtkutil/messagebox.h"
62 #include "mainframe.h"
68 void Global_constructPreferences( PreferencesPage& page ){
69 page.appendCheckBox( "Console", "Enable Logging", g_Console_enableLogging );
72 void Interface_constructPreferences( PreferencesPage& page ){
74 page.appendCheckBox( "", "Default Text Editor", g_TextEditor_useWin32Editor );
77 GtkWidget* use_custom = page.appendCheckBox( "Text Editor", "Custom", g_TextEditor_useCustomEditor );
78 GtkWidget* custom_editor = page.appendPathEntry( "Text Editor Command", g_TextEditor_editorCommand, true );
79 Widget_connectToggleDependency( custom_editor, use_custom );
84 void Mouse_constructPreferences( PreferencesPage& page ){
86 const char* buttons[] = { "2 button", "3 button", };
87 page.appendRadio( "Mouse Type", g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) );
89 page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick );
91 void Mouse_constructPage( PreferenceGroup& group ){
92 PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) );
93 Mouse_constructPreferences( page );
95 void Mouse_registerPreferencesPage(){
96 PreferencesDialog_addInterfacePage( FreeCaller1<PreferenceGroup&, Mouse_constructPage>() );
101 =========================================================
102 Games selection dialog
103 =========================================================
108 inline const char* xmlAttr_getName( xmlAttrPtr attr ){
109 return reinterpret_cast<const char*>( attr->name );
112 inline const char* xmlAttr_getValue( xmlAttrPtr attr ){
113 return reinterpret_cast<const char*>( attr->children->content );
116 CGameDescription::CGameDescription( xmlDocPtr pDoc, const CopiedString& gameFile ){
117 // read the user-friendly game name
118 xmlNodePtr pNode = pDoc->children;
120 while ( strcmp( (const char*)pNode->name, "game" ) && pNode != 0 )
125 Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL );
128 for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next )
130 m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) );
134 StringOutputStream path( 256 );
135 path << AppPath_get() << gameFile.c_str() << "/";
136 mGameToolsPath = path.c_str();
139 ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) );
141 mGameFile = gameFile;
144 GameDescription::iterator i = m_gameDescription.find( "type" );
145 if ( i == m_gameDescription.end() ) {
146 globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>( pDoc->URL ) << "'\n";
152 mGameType = ( *i ).second.c_str();
157 void CGameDescription::Dump(){
158 globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n";
159 for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i )
161 globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n";
165 CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
168 #include "warnings.h"
169 #include "stream/textfilestream.h"
170 #include "container/array.h"
171 #include "xml/ixml.h"
172 #include "xml/xmlparser.h"
173 #include "xml/xmlwriter.h"
175 #include "preferencedictionary.h"
176 #include "stringio.h"
178 const char* const PREFERENCES_VERSION = "1.0";
180 bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){
182 TextFileInputStream file( filename );
183 if ( !file.failed() ) {
184 XMLStreamParser parser( file );
185 XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION );
186 parser.exportXML( importer );
190 int l = strlen( cmdline_prefix );
191 for ( int i = 1; i < g_argc - 1; ++i )
193 if ( g_argv[i][0] == '-' ) {
194 if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) {
195 if ( g_argv[i][l + 1] == '-' ) {
196 preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] );
206 bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){
207 TextFileOutputStream file( filename );
208 if ( !file.failed() ) {
209 XMLStreamWriter writer( file );
210 XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION );
211 exporter.exportXML( writer );
217 bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
218 Array<char> tmpName( filename, filename + strlen( filename ) + 1 + 3 );
219 *( tmpName.end() - 4 ) = 'T';
220 *( tmpName.end() - 3 ) = 'M';
221 *( tmpName.end() - 2 ) = 'P';
222 *( tmpName.end() - 1 ) = '\0';
224 return Preferences_Save( preferences, tmpName.data() )
225 && ( !file_exists( filename ) || file_remove( filename ) )
226 && file_move( tmpName.data(), filename );
231 void LogConsole_importString( const char* string ){
232 g_Console_enableLogging = string_equal( string, "true" );
233 Sys_LogFile( g_Console_enableLogging );
235 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
238 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
239 preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
240 preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
241 preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
245 PreferenceDictionary g_global_preferences;
247 void GlobalPreferences_Init(){
248 RegisterGlobalPreferences( g_global_preferences );
251 void CGameDialog::LoadPrefs(){
252 // load global .pref file
253 StringOutputStream strGlobalPref( 256 );
254 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
256 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
258 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
259 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
263 void CGameDialog::SavePrefs(){
264 StringOutputStream strGlobalPref( 256 );
265 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
267 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
269 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
270 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
274 void CGameDialog::DoGameDialog(){
278 // we save the prefs file
282 void CGameDialog::GameFileImport( int value ){
283 m_nComboSelect = value;
284 // use value to set m_sGameFile
285 std::list<CGameDescription *>::iterator iGame = mGames.begin();
287 for ( i = 0; i < value; i++ )
291 m_sGameFile = ( *iGame )->mGameFile;
294 void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
295 // use m_sGameFile to set value
296 std::list<CGameDescription *>::const_iterator iGame;
298 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
300 if ( ( *iGame )->mGameFile == m_sGameFile ) {
306 importCallback( m_nComboSelect );
309 void CGameDialog_GameFileImport( CGameDialog& self, int value ){
310 self.GameFileImport( value );
313 void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
314 self.GameFileExport( importCallback );
317 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
318 std::vector<const char*> games;
319 games.reserve( mGames.size() );
320 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
322 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
326 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
327 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
328 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
330 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
333 GtkWindow* CGameDialog::BuildDialog(){
334 GtkFrame* frame = create_dialog_frame( "Game settings", GTK_SHADOW_ETCHED_IN );
336 GtkVBox* vbox2 = create_dialog_vbox( 0, 4 );
337 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
340 PreferencesPage preferencesPage( *this, GTK_WIDGET( vbox2 ) );
341 Global_constructPreferences( preferencesPage );
342 CreateGlobalFrame( preferencesPage );
345 return create_simple_modal_dialog_window( "Global Preferences", m_modal, GTK_WIDGET( frame ) );
350 std::list<CGameDescription*>& mGames;
353 LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
355 void operator()( const char* name ) const {
356 if ( !extension_equal( path_get_extension( name ), "game" ) ) {
359 StringOutputStream strPath( 256 );
360 strPath << mPath << name;
361 globalOutputStream() << strPath.c_str() << '\n';
363 xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
365 mGames.push_front( new CGameDescription( pDoc, name ) );
370 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
375 void CGameDialog::ScanForGames(){
376 StringOutputStream strGamesPath( 256 );
377 strGamesPath << AppPath_get() << "games/";
378 const char *path = strGamesPath.c_str();
380 globalOutputStream() << "Scanning for game description files: " << path << '\n';
384 do we put game description files below AppPath, or in ~/.radiant
385 i.e. read only or read/write?
386 my guess .. readonly cause it's an install
387 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
388 (if that's really needed)
391 Directory_forEach( path, LoadGameFile( mGames, path ) );
394 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
395 std::list<CGameDescription *>::iterator iGame;
397 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
399 if ( i == m_nComboSelect ) {
403 return 0; // not found
406 void CGameDialog::InitGlobalPrefPath(){
407 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
410 void CGameDialog::Reset(){
411 if ( !g_Preferences.m_global_rc_path ) {
412 InitGlobalPrefPath();
414 StringOutputStream strGlobalPref( 256 );
415 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
416 file_remove( strGlobalPref.c_str() );
419 void CGameDialog::Init(){
420 InitGlobalPrefPath();
423 if ( mGames.empty() ) {
424 Error( "Didn't find any valid game file descriptions, aborting\n" );
428 std::list<CGameDescription *>::iterator iGame, iPrevGame;
429 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
431 if ( iPrevGame != mGames.end() ) {
432 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
433 CGameDescription *h = *iGame;
441 CGameDescription* currentGameDescription = 0;
443 if ( !m_bGamePrompt ) {
444 // search by .game name
445 std::list<CGameDescription *>::iterator iGame;
446 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
448 if ( ( *iGame )->mGameFile == m_sGameFile ) {
449 currentGameDescription = ( *iGame );
454 if ( m_bGamePrompt || !currentGameDescription ) {
457 // use m_nComboSelect to identify the game to run as and set the globals
458 currentGameDescription = GameDescriptionForComboItem();
459 ASSERT_NOTNULL( currentGameDescription );
461 g_pGameDescription = currentGameDescription;
463 g_pGameDescription->Dump();
466 CGameDialog::~CGameDialog(){
467 // free all the game descriptions
468 std::list<CGameDescription *>::iterator iGame;
469 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
474 if ( GetWidget() != 0 ) {
479 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
480 const char* identifier = gameDescription.getKeyValue( "index" );
481 if ( string_empty( identifier ) ) {
488 CGameDialog g_GamesDialog;
491 // =============================================================================
492 // Widget callbacks for PrefsDlg
494 static void OnButtonClean( GtkWidget *widget, gpointer data ){
495 // make sure this is what the user wants
496 if ( gtk_MessageBox( GTK_WIDGET( g_Preferences.GetWidget() ), "This will close Radiant and clean the corresponding registry entries.\n"
497 "Next time you start Radiant it will be good as new. Do you wish to continue?",
498 "Reset Registry", eMB_YESNO, eMB_ICONASTERISK ) == eIDYES ) {
499 PrefsDlg *dlg = (PrefsDlg*)data;
500 dlg->EndModal( eIDCANCEL );
502 g_preferences_globals.disable_ini = true;
508 // =============================================================================
514 very first prefs init deals with selecting the game and the game tools path
515 then we can load .ini stuff
517 using prefs / ini settings:
520 look in ~/.radiant/<version>/gamename
524 #define PREFS_LOCAL_FILENAME "local.pref"
526 void PrefsDlg::Init(){
527 // m_global_rc_path has been set above
528 // m_rc_path is for game specific preferences
529 // takes the form: global-pref-path/gamename/prefs-file
531 // this is common to win32 and Linux init now
532 m_rc_path = g_string_new( m_global_rc_path->str );
535 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
536 g_string_append( m_rc_path, "/" );
537 Q_mkdir( m_rc_path->str );
540 m_inipath = g_string_new( m_rc_path->str );
541 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
544 void notebook_set_page( GtkWidget* notebook, GtkWidget* page ){
545 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
546 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
547 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
551 void PrefsDlg::showPrefPage( GtkWidget* prefpage ){
552 notebook_set_page( m_notebook, prefpage );
556 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
557 PrefsDlg *dlg = (PrefsDlg*)data;
560 GtkTreeIter selected;
561 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
563 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
564 dlg->showPrefPage( prefpage );
568 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
570 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
571 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
578 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
579 callbacks.push_back( callback );
582 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
584 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
585 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
591 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
592 callbacks.push_back( callback );
595 PreferencesPageCallbacks g_interfacePreferences;
596 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
597 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
599 PreferenceGroupCallbacks g_interfaceCallbacks;
600 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
601 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
604 PreferencesPageCallbacks g_displayPreferences;
605 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
606 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
608 PreferenceGroupCallbacks g_displayCallbacks;
609 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
610 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
613 PreferencesPageCallbacks g_settingsPreferences;
614 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
615 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
617 PreferenceGroupCallbacks g_settingsCallbacks;
618 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
619 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
622 void Widget_updateDependency( GtkWidget* self, GtkWidget* toggleButton ){
623 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && GTK_WIDGET_IS_SENSITIVE( toggleButton ) );
626 void ToggleButton_toggled_Widget_updateDependency( GtkWidget *toggleButton, GtkWidget* self ){
627 Widget_updateDependency( self, toggleButton );
630 void ToggleButton_state_changed_Widget_updateDependency( GtkWidget* toggleButton, GtkStateType state, GtkWidget* self ){
631 if ( state == GTK_STATE_INSENSITIVE ) {
632 Widget_updateDependency( self, toggleButton );
636 void Widget_connectToggleDependency( GtkWidget* self, GtkWidget* toggleButton ){
637 g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
638 g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
639 Widget_updateDependency( self, toggleButton );
643 inline GtkWidget* getVBox( GtkWidget* page ){
644 return gtk_bin_get_child( GTK_BIN( page ) );
647 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, GtkWidget* page ){
649 gtk_tree_store_append( store, &group, parent );
650 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
654 GtkWidget* PreferencePages_addPage( GtkWidget* notebook, const char* name ){
655 GtkWidget* preflabel = gtk_label_new( name );
656 gtk_widget_show( preflabel );
658 GtkWidget* pageframe = gtk_frame_new( name );
659 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
660 gtk_widget_show( pageframe );
662 GtkWidget* vbox = gtk_vbox_new( FALSE, 4 );
663 gtk_widget_show( vbox );
664 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
665 gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
667 // Add the page to the notebook
668 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
673 class PreferenceTreeGroup : public PreferenceGroup
676 GtkWidget* m_notebook;
677 GtkTreeStore* m_store;
680 PreferenceTreeGroup( Dialog& dialog, GtkWidget* notebook, GtkTreeStore* store, GtkTreeIter group ) :
682 m_notebook( notebook ),
686 PreferencesPage createPage( const char* treeName, const char* frameName ){
687 GtkWidget* page = PreferencePages_addPage( m_notebook, frameName );
688 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
689 return PreferencesPage( m_dialog, getVBox( page ) );
693 GtkWindow* PrefsDlg::BuildDialog(){
694 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
695 Mouse_registerPreferencesPage();
697 GtkWindow* dialog = create_floating_window( "NetRadiant Preferences", m_parent );
700 GtkWidget* mainvbox = gtk_vbox_new( FALSE, 5 );
701 gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
702 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
703 gtk_widget_show( mainvbox );
706 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
707 gtk_widget_show( hbox );
708 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
711 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
712 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
715 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
716 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
719 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
720 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
725 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
726 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
727 gtk_widget_show( hbox );
730 GtkWidget* sc_win = gtk_scrolled_window_new( 0, 0 );
731 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
732 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
733 gtk_widget_show( sc_win );
734 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
736 // prefs pages notebook
737 m_notebook = gtk_notebook_new();
738 // hide the notebook tabs since its not supposed to look like a notebook
739 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
740 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
741 gtk_widget_show( m_notebook );
745 GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
747 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
748 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
751 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
752 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Preferences", renderer, "text", 0, NULL );
753 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
757 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
758 g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
761 gtk_widget_show( view );
763 gtk_container_add( GTK_CONTAINER( sc_win ), view );
766 /********************************************************************/
767 /* Add preference tree options */
768 /********************************************************************/
771 PreferencePages_addPage( m_notebook, "Front Page" );
774 GtkWidget* global = PreferencePages_addPage( m_notebook, "Global Preferences" );
776 PreferencesPage preferencesPage( *this, getVBox( global ) );
777 Global_constructPreferences( preferencesPage );
779 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
781 GtkWidget* game = PreferencePages_addPage( m_notebook, "Game" );
782 PreferencesPage preferencesPage( *this, getVBox( game ) );
783 g_GamesDialog.CreateGlobalFrame( preferencesPage );
785 PreferenceTree_appendPage( store, &group, "Game", game );
790 GtkWidget* interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
792 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
793 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
796 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
797 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
799 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
803 GtkWidget* display = PreferencePages_addPage( m_notebook, "Display Preferences" );
805 PreferencesPage preferencesPage( *this, getVBox( display ) );
806 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
808 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
809 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
811 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
815 GtkWidget* settings = PreferencePages_addPage( m_notebook, "General Settings" );
817 PreferencesPage preferencesPage( *this, getVBox( settings ) );
818 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
821 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
822 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
824 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
828 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
830 g_object_unref( G_OBJECT( store ) );
836 gtk_notebook_set_page( GTK_NOTEBOOK( m_notebook ), 0 );
841 preferences_globals_t g_preferences_globals;
843 PrefsDlg g_Preferences; // global prefs instance
846 void PreferencesDialog_constructWindow( GtkWindow* main_window ){
847 g_Preferences.m_parent = main_window;
848 g_Preferences.Create();
850 void PreferencesDialog_destroyWindow(){
851 g_Preferences.Destroy();
855 PreferenceDictionary g_preferences;
857 PreferenceSystem& GetPreferenceSystem(){
858 return g_preferences;
861 class PreferenceSystemAPI
863 PreferenceSystem* m_preferencesystem;
865 typedef PreferenceSystem Type;
866 STRING_CONSTANT( Name, "*" );
868 PreferenceSystemAPI(){
869 m_preferencesystem = &GetPreferenceSystem();
871 PreferenceSystem* getTable(){
872 return m_preferencesystem;
876 #include "modulesystem/singletonmodule.h"
877 #include "modulesystem/moduleregistry.h"
879 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
880 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
881 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
883 void Preferences_Load(){
884 g_GamesDialog.LoadPrefs();
886 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
888 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
889 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
893 void Preferences_Save(){
894 if ( g_preferences_globals.disable_ini ) {
898 g_GamesDialog.SavePrefs();
900 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
902 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
903 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
907 void Preferences_Reset(){
908 file_remove( g_Preferences.m_inipath->str );
912 void PrefsDlg::PostModal( EMessageBoxReturn code ){
913 if ( code == eIDOK ) {
919 std::vector<const char*> g_restart_required;
921 void PreferencesDialog_restartRequired( const char* staticName ){
922 g_restart_required.push_back( staticName );
925 void PreferencesDialog_showDialog(){
926 if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
927 if ( !g_restart_required.empty() ) {
928 StringOutputStream message( 256 );
929 message << "Preference changes require a restart:\n";
930 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
932 message << ( *i ) << '\n';
934 gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), message.c_str() );
935 g_restart_required.clear();
944 void GameName_importString( const char* value ){
945 gamename_set( value );
947 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
948 void GameName_exportString( const StringImportCallback& importer ){
949 importer( gamename_get() );
951 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
953 void GameMode_importString( const char* value ){
954 gamemode_set( value );
956 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
957 void GameMode_exportString( const StringImportCallback& importer ){
958 importer( gamemode_get() );
960 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
963 void RegisterPreferences( PreferenceSystem& preferences ){
965 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
967 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
968 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
971 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
972 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
975 void Preferences_Init(){
976 RegisterPreferences( GetPreferenceSystem() );