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 std::string& 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 std::string tmpName( filename );
221 return Preferences_Save( preferences, tmpName.c_str() )
222 && ( !file_exists( filename ) || file_remove( filename ) )
223 && file_move( tmpName.c_str(), filename );
228 void LogConsole_importString( const char* string ){
229 g_Console_enableLogging = string_equal( string, "true" );
230 Sys_LogFile( g_Console_enableLogging );
232 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
235 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
236 preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
237 preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
238 preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
242 PreferenceDictionary g_global_preferences;
244 void GlobalPreferences_Init(){
245 RegisterGlobalPreferences( g_global_preferences );
248 void CGameDialog::LoadPrefs(){
249 // load global .pref file
250 StringOutputStream strGlobalPref( 256 );
251 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
253 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
255 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
256 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
260 void CGameDialog::SavePrefs(){
261 StringOutputStream strGlobalPref( 256 );
262 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
264 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
266 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
267 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
271 void CGameDialog::DoGameDialog(){
275 // we save the prefs file
279 void CGameDialog::GameFileImport( int value ){
280 m_nComboSelect = value;
281 // use value to set m_sGameFile
282 std::list<CGameDescription *>::iterator iGame = mGames.begin();
284 for ( i = 0; i < value; i++ )
288 m_sGameFile = ( *iGame )->mGameFile;
291 void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
292 // use m_sGameFile to set value
293 std::list<CGameDescription *>::const_iterator iGame;
295 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
297 if ( ( *iGame )->mGameFile == m_sGameFile ) {
303 importCallback( m_nComboSelect );
306 void CGameDialog_GameFileImport( CGameDialog& self, int value ){
307 self.GameFileImport( value );
310 void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
311 self.GameFileExport( importCallback );
314 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
315 std::vector<const char*> games;
316 games.reserve( mGames.size() );
317 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
319 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
323 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
324 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
325 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
327 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
330 GtkWindow* CGameDialog::BuildDialog(){
331 GtkFrame* frame = create_dialog_frame( "Game settings", GTK_SHADOW_ETCHED_IN );
333 GtkVBox* vbox2 = create_dialog_vbox( 0, 4 );
334 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
337 PreferencesPage preferencesPage( *this, GTK_WIDGET( vbox2 ) );
338 Global_constructPreferences( preferencesPage );
339 CreateGlobalFrame( preferencesPage );
342 return create_simple_modal_dialog_window( "Global Preferences", m_modal, GTK_WIDGET( frame ) );
347 std::list<CGameDescription*>& mGames;
350 LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
352 void operator()( const char* name ) const {
353 if ( !extension_equal( path_get_extension( name ), "game" ) ) {
356 StringOutputStream strPath( 256 );
357 strPath << mPath << name;
358 globalOutputStream() << strPath.c_str() << '\n';
360 xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
362 mGames.push_front( new CGameDescription( pDoc, name ) );
367 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
372 void CGameDialog::ScanForGames(){
373 StringOutputStream strGamesPath( 256 );
374 strGamesPath << AppPath_get() << "games/";
375 const char *path = strGamesPath.c_str();
377 globalOutputStream() << "Scanning for game description files: " << path << '\n';
381 do we put game description files below AppPath, or in ~/.radiant
382 i.e. read only or read/write?
383 my guess .. readonly cause it's an install
384 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
385 (if that's really needed)
388 Directory_forEach( path, LoadGameFile( mGames, path ) );
391 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
392 std::list<CGameDescription *>::iterator iGame;
394 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
396 if ( i == m_nComboSelect ) {
400 return 0; // not found
403 void CGameDialog::InitGlobalPrefPath(){
404 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
407 void CGameDialog::Reset(){
408 if ( !g_Preferences.m_global_rc_path ) {
409 InitGlobalPrefPath();
411 StringOutputStream strGlobalPref( 256 );
412 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
413 file_remove( strGlobalPref.c_str() );
416 void CGameDialog::Init(){
417 InitGlobalPrefPath();
420 if ( mGames.empty() ) {
421 Error( "Didn't find any valid game file descriptions, aborting\n" );
425 std::list<CGameDescription *>::iterator iGame, iPrevGame;
426 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
428 if ( iPrevGame != mGames.end() ) {
429 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
430 CGameDescription *h = *iGame;
438 CGameDescription* currentGameDescription = 0;
440 if ( !m_bGamePrompt ) {
441 // search by .game name
442 std::list<CGameDescription *>::iterator iGame;
443 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
445 if ( ( *iGame )->mGameFile == m_sGameFile ) {
446 currentGameDescription = ( *iGame );
451 if ( m_bGamePrompt || !currentGameDescription ) {
454 // use m_nComboSelect to identify the game to run as and set the globals
455 currentGameDescription = GameDescriptionForComboItem();
456 ASSERT_NOTNULL( currentGameDescription );
458 g_pGameDescription = currentGameDescription;
460 g_pGameDescription->Dump();
463 CGameDialog::~CGameDialog(){
464 // free all the game descriptions
465 std::list<CGameDescription *>::iterator iGame;
466 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
471 if ( GetWidget() != 0 ) {
476 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
477 const char* identifier = gameDescription.getKeyValue( "index" );
478 if ( string_empty( identifier ) ) {
484 void CGameDialog::AddPacksURL( StringOutputStream &URL ){
485 // add the URLs for the list of game packs installed
486 // FIXME: this is kinda hardcoded for now..
487 std::list<CGameDescription *>::iterator iGame;
488 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
490 URL << "&Games_dlup%5B%5D=" << GameDescription_getIdentifier( *( *iGame ) );
494 CGameDialog g_GamesDialog;
497 // =============================================================================
498 // Widget callbacks for PrefsDlg
500 static void OnButtonClean( GtkWidget *widget, gpointer data ){
501 // make sure this is what the user wants
502 if ( gtk_MessageBox( GTK_WIDGET( g_Preferences.GetWidget() ), "This will close Radiant and clean the corresponding registry entries.\n"
503 "Next time you start Radiant it will be good as new. Do you wish to continue?",
504 "Reset Registry", eMB_YESNO, eMB_ICONASTERISK ) == eIDYES ) {
505 PrefsDlg *dlg = (PrefsDlg*)data;
506 dlg->EndModal( eIDCANCEL );
508 g_preferences_globals.disable_ini = true;
514 // =============================================================================
520 very first prefs init deals with selecting the game and the game tools path
521 then we can load .ini stuff
523 using prefs / ini settings:
526 look in ~/.radiant/<version>/gamename
530 #define PREFS_LOCAL_FILENAME "local.pref"
532 void PrefsDlg::Init(){
533 // m_global_rc_path has been set above
534 // m_rc_path is for game specific preferences
535 // takes the form: global-pref-path/gamename/prefs-file
537 // this is common to win32 and Linux init now
538 m_rc_path = g_string_new( m_global_rc_path->str );
541 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
542 g_string_append( m_rc_path, "/" );
543 Q_mkdir( m_rc_path->str );
546 m_inipath = g_string_new( m_rc_path->str );
547 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
550 void notebook_set_page( GtkWidget* notebook, GtkWidget* page ){
551 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
552 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
553 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
557 void PrefsDlg::showPrefPage( GtkWidget* prefpage ){
558 notebook_set_page( m_notebook, prefpage );
562 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
563 PrefsDlg *dlg = (PrefsDlg*)data;
566 GtkTreeIter selected;
567 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
569 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
570 dlg->showPrefPage( prefpage );
574 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
576 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
577 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
584 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
585 callbacks.push_back( callback );
588 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
590 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
591 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
597 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
598 callbacks.push_back( callback );
601 PreferencesPageCallbacks g_interfacePreferences;
602 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
603 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
605 PreferenceGroupCallbacks g_interfaceCallbacks;
606 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
607 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
610 PreferencesPageCallbacks g_displayPreferences;
611 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
612 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
614 PreferenceGroupCallbacks g_displayCallbacks;
615 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
616 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
619 PreferencesPageCallbacks g_settingsPreferences;
620 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
621 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
623 PreferenceGroupCallbacks g_settingsCallbacks;
624 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
625 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
628 void Widget_updateDependency( GtkWidget* self, GtkWidget* toggleButton ){
629 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && GTK_WIDGET_IS_SENSITIVE( toggleButton ) );
632 void ToggleButton_toggled_Widget_updateDependency( GtkWidget *toggleButton, GtkWidget* self ){
633 Widget_updateDependency( self, toggleButton );
636 void ToggleButton_state_changed_Widget_updateDependency( GtkWidget* toggleButton, GtkStateType state, GtkWidget* self ){
637 if ( state == GTK_STATE_INSENSITIVE ) {
638 Widget_updateDependency( self, toggleButton );
642 void Widget_connectToggleDependency( GtkWidget* self, GtkWidget* toggleButton ){
643 g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
644 g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
645 Widget_updateDependency( self, toggleButton );
649 inline GtkWidget* getVBox( GtkWidget* page ){
650 return gtk_bin_get_child( GTK_BIN( page ) );
653 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, GtkWidget* page ){
655 gtk_tree_store_append( store, &group, parent );
656 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
660 GtkWidget* PreferencePages_addPage( GtkWidget* notebook, const char* name ){
661 GtkWidget* preflabel = gtk_label_new( name );
662 gtk_widget_show( preflabel );
664 GtkWidget* pageframe = gtk_frame_new( name );
665 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
666 gtk_widget_show( pageframe );
668 GtkWidget* vbox = gtk_vbox_new( FALSE, 4 );
669 gtk_widget_show( vbox );
670 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
671 gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
673 // Add the page to the notebook
674 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
679 class PreferenceTreeGroup : public PreferenceGroup
682 GtkWidget* m_notebook;
683 GtkTreeStore* m_store;
686 PreferenceTreeGroup( Dialog& dialog, GtkWidget* notebook, GtkTreeStore* store, GtkTreeIter group ) :
688 m_notebook( notebook ),
692 PreferencesPage createPage( const char* treeName, const char* frameName ){
693 GtkWidget* page = PreferencePages_addPage( m_notebook, frameName );
694 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
695 return PreferencesPage( m_dialog, getVBox( page ) );
699 GtkWindow* PrefsDlg::BuildDialog(){
700 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
701 Mouse_registerPreferencesPage();
703 GtkWindow* dialog = create_floating_window( "NetRadiant Preferences", m_parent );
706 GtkWidget* mainvbox = gtk_vbox_new( FALSE, 5 );
707 gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
708 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
709 gtk_widget_show( mainvbox );
712 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
713 gtk_widget_show( hbox );
714 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
717 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
718 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
721 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
722 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
725 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
726 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
731 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
732 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
733 gtk_widget_show( hbox );
736 GtkWidget* sc_win = gtk_scrolled_window_new( 0, 0 );
737 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
738 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
739 gtk_widget_show( sc_win );
740 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
742 // prefs pages notebook
743 m_notebook = gtk_notebook_new();
744 // hide the notebook tabs since its not supposed to look like a notebook
745 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
746 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
747 gtk_widget_show( m_notebook );
751 GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
753 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
754 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
757 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
758 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Preferences", renderer, "text", 0, NULL );
759 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
763 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
764 g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
767 gtk_widget_show( view );
769 gtk_container_add( GTK_CONTAINER( sc_win ), view );
772 /********************************************************************/
773 /* Add preference tree options */
774 /********************************************************************/
777 PreferencePages_addPage( m_notebook, "Front Page" );
780 GtkWidget* global = PreferencePages_addPage( m_notebook, "Global Preferences" );
782 PreferencesPage preferencesPage( *this, getVBox( global ) );
783 Global_constructPreferences( preferencesPage );
785 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
787 GtkWidget* game = PreferencePages_addPage( m_notebook, "Game" );
788 PreferencesPage preferencesPage( *this, getVBox( game ) );
789 g_GamesDialog.CreateGlobalFrame( preferencesPage );
791 PreferenceTree_appendPage( store, &group, "Game", game );
796 GtkWidget* interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
798 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
799 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
802 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
803 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
805 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
809 GtkWidget* display = PreferencePages_addPage( m_notebook, "Display Preferences" );
811 PreferencesPage preferencesPage( *this, getVBox( display ) );
812 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
814 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
815 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
817 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
821 GtkWidget* settings = PreferencePages_addPage( m_notebook, "General Settings" );
823 PreferencesPage preferencesPage( *this, getVBox( settings ) );
824 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
827 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
828 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
830 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
834 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
836 g_object_unref( G_OBJECT( store ) );
842 gtk_notebook_set_page( GTK_NOTEBOOK( m_notebook ), 0 );
847 preferences_globals_t g_preferences_globals;
849 PrefsDlg g_Preferences; // global prefs instance
852 void PreferencesDialog_constructWindow( GtkWindow* main_window ){
853 g_Preferences.m_parent = main_window;
854 g_Preferences.Create();
856 void PreferencesDialog_destroyWindow(){
857 g_Preferences.Destroy();
861 PreferenceDictionary g_preferences;
863 PreferenceSystem& GetPreferenceSystem(){
864 return g_preferences;
867 class PreferenceSystemAPI
869 PreferenceSystem* m_preferencesystem;
871 typedef PreferenceSystem Type;
872 STRING_CONSTANT( Name, "*" );
874 PreferenceSystemAPI(){
875 m_preferencesystem = &GetPreferenceSystem();
877 PreferenceSystem* getTable(){
878 return m_preferencesystem;
882 #include "modulesystem/singletonmodule.h"
883 #include "modulesystem/moduleregistry.h"
885 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
886 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
887 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
889 void Preferences_Load(){
890 g_GamesDialog.LoadPrefs();
892 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
894 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
895 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
899 void Preferences_Save(){
900 if ( g_preferences_globals.disable_ini ) {
904 g_GamesDialog.SavePrefs();
906 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
908 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
909 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
913 void Preferences_Reset(){
914 file_remove( g_Preferences.m_inipath->str );
918 void PrefsDlg::PostModal( EMessageBoxReturn code ){
919 if ( code == eIDOK ) {
925 std::vector<const char*> g_restart_required;
927 void PreferencesDialog_restartRequired( const char* staticName ){
928 g_restart_required.push_back( staticName );
931 void PreferencesDialog_showDialog(){
932 if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
933 if ( !g_restart_required.empty() ) {
934 StringOutputStream message( 256 );
935 message << "Preference changes require a restart:\n";
936 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
938 message << ( *i ) << '\n';
940 gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), message.c_str() );
941 g_restart_required.clear();
950 void GameName_importString( const char* value ){
951 gamename_set( value );
953 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
954 void GameName_exportString( const StringImportCallback& importer ){
955 importer( gamename_get() );
957 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
959 void GameMode_importString( const char* value ){
960 gamemode_set( value );
962 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
963 void GameMode_exportString( const StringImportCallback& importer ){
964 importer( gamemode_get() );
966 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
969 void RegisterPreferences( PreferenceSystem& preferences ){
971 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
973 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
974 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
977 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
978 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
981 void Preferences_Init(){
982 RegisterPreferences( GetPreferenceSystem() );