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 ) ) {
487 void CGameDialog::AddPacksURL( StringOutputStream &URL ){
488 // add the URLs for the list of game packs installed
489 // FIXME: this is kinda hardcoded for now..
490 std::list<CGameDescription *>::iterator iGame;
491 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
493 URL << "&Games_dlup%5B%5D=" << GameDescription_getIdentifier( *( *iGame ) );
497 CGameDialog g_GamesDialog;
500 // =============================================================================
501 // Widget callbacks for PrefsDlg
503 static void OnButtonClean( GtkWidget *widget, gpointer data ){
504 // make sure this is what the user wants
505 if ( gtk_MessageBox( GTK_WIDGET( g_Preferences.GetWidget() ), "This will close Radiant and clean the corresponding registry entries.\n"
506 "Next time you start Radiant it will be good as new. Do you wish to continue?",
507 "Reset Registry", eMB_YESNO, eMB_ICONASTERISK ) == eIDYES ) {
508 PrefsDlg *dlg = (PrefsDlg*)data;
509 dlg->EndModal( eIDCANCEL );
511 g_preferences_globals.disable_ini = true;
517 // =============================================================================
523 very first prefs init deals with selecting the game and the game tools path
524 then we can load .ini stuff
526 using prefs / ini settings:
529 look in ~/.radiant/<version>/gamename
533 #define PREFS_LOCAL_FILENAME "local.pref"
535 void PrefsDlg::Init(){
536 // m_global_rc_path has been set above
537 // m_rc_path is for game specific preferences
538 // takes the form: global-pref-path/gamename/prefs-file
540 // this is common to win32 and Linux init now
541 m_rc_path = g_string_new( m_global_rc_path->str );
544 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
545 g_string_append( m_rc_path, "/" );
546 Q_mkdir( m_rc_path->str );
549 m_inipath = g_string_new( m_rc_path->str );
550 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
553 void notebook_set_page( GtkWidget* notebook, GtkWidget* page ){
554 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
555 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
556 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
560 void PrefsDlg::showPrefPage( GtkWidget* prefpage ){
561 notebook_set_page( m_notebook, prefpage );
565 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
566 PrefsDlg *dlg = (PrefsDlg*)data;
569 GtkTreeIter selected;
570 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
572 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
573 dlg->showPrefPage( prefpage );
577 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
579 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
580 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
587 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
588 callbacks.push_back( callback );
591 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
593 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
594 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
600 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
601 callbacks.push_back( callback );
604 PreferencesPageCallbacks g_interfacePreferences;
605 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
606 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
608 PreferenceGroupCallbacks g_interfaceCallbacks;
609 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
610 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
613 PreferencesPageCallbacks g_displayPreferences;
614 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
615 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
617 PreferenceGroupCallbacks g_displayCallbacks;
618 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
619 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
622 PreferencesPageCallbacks g_settingsPreferences;
623 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
624 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
626 PreferenceGroupCallbacks g_settingsCallbacks;
627 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
628 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
631 void Widget_updateDependency( GtkWidget* self, GtkWidget* toggleButton ){
632 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && GTK_WIDGET_IS_SENSITIVE( toggleButton ) );
635 void ToggleButton_toggled_Widget_updateDependency( GtkWidget *toggleButton, GtkWidget* self ){
636 Widget_updateDependency( self, toggleButton );
639 void ToggleButton_state_changed_Widget_updateDependency( GtkWidget* toggleButton, GtkStateType state, GtkWidget* self ){
640 if ( state == GTK_STATE_INSENSITIVE ) {
641 Widget_updateDependency( self, toggleButton );
645 void Widget_connectToggleDependency( GtkWidget* self, GtkWidget* toggleButton ){
646 g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
647 g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
648 Widget_updateDependency( self, toggleButton );
652 inline GtkWidget* getVBox( GtkWidget* page ){
653 return gtk_bin_get_child( GTK_BIN( page ) );
656 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, GtkWidget* page ){
658 gtk_tree_store_append( store, &group, parent );
659 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
663 GtkWidget* PreferencePages_addPage( GtkWidget* notebook, const char* name ){
664 GtkWidget* preflabel = gtk_label_new( name );
665 gtk_widget_show( preflabel );
667 GtkWidget* pageframe = gtk_frame_new( name );
668 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
669 gtk_widget_show( pageframe );
671 GtkWidget* vbox = gtk_vbox_new( FALSE, 4 );
672 gtk_widget_show( vbox );
673 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
674 gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
676 // Add the page to the notebook
677 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
682 class PreferenceTreeGroup : public PreferenceGroup
685 GtkWidget* m_notebook;
686 GtkTreeStore* m_store;
689 PreferenceTreeGroup( Dialog& dialog, GtkWidget* notebook, GtkTreeStore* store, GtkTreeIter group ) :
691 m_notebook( notebook ),
695 PreferencesPage createPage( const char* treeName, const char* frameName ){
696 GtkWidget* page = PreferencePages_addPage( m_notebook, frameName );
697 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
698 return PreferencesPage( m_dialog, getVBox( page ) );
702 GtkWindow* PrefsDlg::BuildDialog(){
703 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
704 Mouse_registerPreferencesPage();
706 GtkWindow* dialog = create_floating_window( "NetRadiant Preferences", m_parent );
709 GtkWidget* mainvbox = gtk_vbox_new( FALSE, 5 );
710 gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
711 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
712 gtk_widget_show( mainvbox );
715 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
716 gtk_widget_show( hbox );
717 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
720 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
721 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
724 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
725 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
728 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
729 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
734 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
735 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
736 gtk_widget_show( hbox );
739 GtkWidget* sc_win = gtk_scrolled_window_new( 0, 0 );
740 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
741 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
742 gtk_widget_show( sc_win );
743 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
745 // prefs pages notebook
746 m_notebook = gtk_notebook_new();
747 // hide the notebook tabs since its not supposed to look like a notebook
748 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
749 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
750 gtk_widget_show( m_notebook );
754 GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
756 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
757 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
760 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
761 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Preferences", renderer, "text", 0, NULL );
762 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
766 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
767 g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
770 gtk_widget_show( view );
772 gtk_container_add( GTK_CONTAINER( sc_win ), view );
775 /********************************************************************/
776 /* Add preference tree options */
777 /********************************************************************/
780 PreferencePages_addPage( m_notebook, "Front Page" );
783 GtkWidget* global = PreferencePages_addPage( m_notebook, "Global Preferences" );
785 PreferencesPage preferencesPage( *this, getVBox( global ) );
786 Global_constructPreferences( preferencesPage );
788 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
790 GtkWidget* game = PreferencePages_addPage( m_notebook, "Game" );
791 PreferencesPage preferencesPage( *this, getVBox( game ) );
792 g_GamesDialog.CreateGlobalFrame( preferencesPage );
794 PreferenceTree_appendPage( store, &group, "Game", game );
799 GtkWidget* interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
801 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
802 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
805 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
806 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
808 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
812 GtkWidget* display = PreferencePages_addPage( m_notebook, "Display Preferences" );
814 PreferencesPage preferencesPage( *this, getVBox( display ) );
815 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
817 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
818 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
820 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
824 GtkWidget* settings = PreferencePages_addPage( m_notebook, "General Settings" );
826 PreferencesPage preferencesPage( *this, getVBox( settings ) );
827 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
830 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
831 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
833 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
837 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
839 g_object_unref( G_OBJECT( store ) );
845 gtk_notebook_set_page( GTK_NOTEBOOK( m_notebook ), 0 );
850 preferences_globals_t g_preferences_globals;
852 PrefsDlg g_Preferences; // global prefs instance
855 void PreferencesDialog_constructWindow( GtkWindow* main_window ){
856 g_Preferences.m_parent = main_window;
857 g_Preferences.Create();
859 void PreferencesDialog_destroyWindow(){
860 g_Preferences.Destroy();
864 PreferenceDictionary g_preferences;
866 PreferenceSystem& GetPreferenceSystem(){
867 return g_preferences;
870 class PreferenceSystemAPI
872 PreferenceSystem* m_preferencesystem;
874 typedef PreferenceSystem Type;
875 STRING_CONSTANT( Name, "*" );
877 PreferenceSystemAPI(){
878 m_preferencesystem = &GetPreferenceSystem();
880 PreferenceSystem* getTable(){
881 return m_preferencesystem;
885 #include "modulesystem/singletonmodule.h"
886 #include "modulesystem/moduleregistry.h"
888 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
889 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
890 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
892 void Preferences_Load(){
893 g_GamesDialog.LoadPrefs();
895 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
897 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
898 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
902 void Preferences_Save(){
903 if ( g_preferences_globals.disable_ini ) {
907 g_GamesDialog.SavePrefs();
909 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
911 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
912 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
916 void Preferences_Reset(){
917 file_remove( g_Preferences.m_inipath->str );
921 void PrefsDlg::PostModal( EMessageBoxReturn code ){
922 if ( code == eIDOK ) {
928 std::vector<const char*> g_restart_required;
930 void PreferencesDialog_restartRequired( const char* staticName ){
931 g_restart_required.push_back( staticName );
934 void PreferencesDialog_showDialog(){
935 if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
936 if ( !g_restart_required.empty() ) {
937 StringOutputStream message( 256 );
938 message << "Preference changes require a restart:\n";
939 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
941 message << ( *i ) << '\n';
943 gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), message.c_str() );
944 g_restart_required.clear();
953 void GameName_importString( const char* value ){
954 gamename_set( value );
956 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
957 void GameName_exportString( const StringImportCallback& importer ){
958 importer( gamename_get() );
960 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
962 void GameMode_importString( const char* value ){
963 gamemode_set( value );
965 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
966 void GameMode_exportString( const StringImportCallback& importer ){
967 importer( gamemode_get() );
969 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
972 void RegisterPreferences( PreferenceSystem& preferences ){
974 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
976 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
977 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
980 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
981 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
984 void Preferences_Init(){
985 RegisterPreferences( GetPreferenceSystem() );