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( "", "External Shader 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 );
90 page.appendCheckBox( "", "Improved mousewheel zoom", g_xywindow_globals.m_bImprovedWheelZoom );
92 void Mouse_constructPage( PreferenceGroup& group ){
93 PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) );
94 Mouse_constructPreferences( page );
96 void Mouse_registerPreferencesPage(){
97 PreferencesDialog_addInterfacePage( FreeCaller1<PreferenceGroup&, Mouse_constructPage>() );
102 =========================================================
103 Games selection dialog
104 =========================================================
109 inline const char* xmlAttr_getName( xmlAttrPtr attr ){
110 return reinterpret_cast<const char*>( attr->name );
113 inline const char* xmlAttr_getValue( xmlAttrPtr attr ){
114 return reinterpret_cast<const char*>( attr->children->content );
117 CGameDescription::CGameDescription( xmlDocPtr pDoc, const CopiedString& gameFile ){
118 // read the user-friendly game name
119 xmlNodePtr pNode = pDoc->children;
121 while ( pNode != 0 && strcmp( (const char*)pNode->name, "game" ) )
126 Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL );
129 for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next )
131 m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) );
135 StringOutputStream path( 256 );
136 path << AppPath_get() << gameFile.c_str() << "/";
137 mGameToolsPath = path.c_str();
140 ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) );
142 mGameFile = gameFile;
145 GameDescription::iterator i = m_gameDescription.find( "type" );
146 if ( i == m_gameDescription.end() ) {
147 globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>( pDoc->URL ) << "'\n";
153 mGameType = ( *i ).second.c_str();
158 void CGameDescription::Dump(){
159 globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n";
160 for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i )
162 globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n";
166 CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
169 #include "warnings.h"
170 #include "stream/textfilestream.h"
171 #include "container/array.h"
172 #include "xml/ixml.h"
173 #include "xml/xmlparser.h"
174 #include "xml/xmlwriter.h"
176 #include "preferencedictionary.h"
177 #include "stringio.h"
179 const char* const PREFERENCES_VERSION = "1.0";
181 bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){
183 TextFileInputStream file( filename );
184 if ( !file.failed() ) {
185 XMLStreamParser parser( file );
186 XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION );
187 parser.exportXML( importer );
191 int l = strlen( cmdline_prefix );
192 for ( int i = 1; i < g_argc - 1; ++i )
194 if ( g_argv[i][0] == '-' ) {
195 if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) {
196 if ( g_argv[i][l + 1] == '-' ) {
197 preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] );
207 bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){
208 TextFileOutputStream file( filename );
209 if ( !file.failed() ) {
210 XMLStreamWriter writer( file );
211 XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION );
212 exporter.exportXML( writer );
218 bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
219 Array<char> tmpName( filename, filename + strlen( filename ) + 1 + 3 );
220 *( tmpName.end() - 4 ) = 'T';
221 *( tmpName.end() - 3 ) = 'M';
222 *( tmpName.end() - 2 ) = 'P';
223 *( tmpName.end() - 1 ) = '\0';
225 return Preferences_Save( preferences, tmpName.data() )
226 && ( !file_exists( filename ) || file_remove( filename ) )
227 && file_move( tmpName.data(), filename );
232 void LogConsole_importString( const char* string ){
233 g_Console_enableLogging = string_equal( string, "true" );
234 Sys_LogFile( g_Console_enableLogging );
236 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
239 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
240 preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
241 preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
242 preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
246 PreferenceDictionary g_global_preferences;
248 void GlobalPreferences_Init(){
249 RegisterGlobalPreferences( g_global_preferences );
252 void CGameDialog::LoadPrefs(){
253 // load global .pref file
254 StringOutputStream strGlobalPref( 256 );
255 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
257 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
259 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
260 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
264 void CGameDialog::SavePrefs(){
265 StringOutputStream strGlobalPref( 256 );
266 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
268 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
270 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
271 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
275 void CGameDialog::DoGameDialog(){
279 // we save the prefs file
283 void CGameDialog::GameFileImport( int value ){
284 m_nComboSelect = value;
285 // use value to set m_sGameFile
286 std::list<CGameDescription *>::iterator iGame = mGames.begin();
288 for ( i = 0; i < value; i++ )
292 m_sGameFile = ( *iGame )->mGameFile;
295 void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
296 // use m_sGameFile to set value
297 std::list<CGameDescription *>::const_iterator iGame;
299 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
301 if ( ( *iGame )->mGameFile == m_sGameFile ) {
307 importCallback( m_nComboSelect );
310 void CGameDialog_GameFileImport( CGameDialog& self, int value ){
311 self.GameFileImport( value );
314 void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
315 self.GameFileExport( importCallback );
318 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
319 std::vector<const char*> games;
320 games.reserve( mGames.size() );
321 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
323 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
327 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
328 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
329 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
331 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
334 GtkWindow* CGameDialog::BuildDialog(){
335 GtkFrame* frame = create_dialog_frame( "Game settings", GTK_SHADOW_ETCHED_IN );
337 GtkVBox* vbox2 = create_dialog_vbox( 0, 4 );
338 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
341 PreferencesPage preferencesPage( *this, GTK_WIDGET( vbox2 ) );
342 Global_constructPreferences( preferencesPage );
343 CreateGlobalFrame( preferencesPage );
346 return create_simple_modal_dialog_window( "Global Preferences", m_modal, GTK_WIDGET( frame ) );
351 std::list<CGameDescription*>& mGames;
354 LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
356 void operator()( const char* name ) const {
357 if ( !extension_equal( path_get_extension( name ), "game" ) ) {
360 StringOutputStream strPath( 256 );
361 strPath << mPath << name;
362 globalOutputStream() << strPath.c_str() << '\n';
364 xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
366 mGames.push_front( new CGameDescription( pDoc, name ) );
371 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
376 void CGameDialog::ScanForGames(){
377 StringOutputStream strGamesPath( 256 );
378 strGamesPath << AppPath_get() << "games/";
379 const char *path = strGamesPath.c_str();
381 globalOutputStream() << "Scanning for game description files: " << path << '\n';
385 do we put game description files below AppPath, or in ~/.radiant
386 i.e. read only or read/write?
387 my guess .. readonly cause it's an install
388 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
389 (if that's really needed)
392 Directory_forEach( path, LoadGameFile( mGames, path ) );
395 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
396 std::list<CGameDescription *>::iterator iGame;
398 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
400 if ( i == m_nComboSelect ) {
404 return 0; // not found
407 void CGameDialog::InitGlobalPrefPath(){
408 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
411 void CGameDialog::Reset(){
412 if ( !g_Preferences.m_global_rc_path ) {
413 InitGlobalPrefPath();
415 StringOutputStream strGlobalPref( 256 );
416 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
417 file_remove( strGlobalPref.c_str() );
420 void CGameDialog::Init(){
421 InitGlobalPrefPath();
424 if ( mGames.empty() ) {
425 Error( "Didn't find any valid game file descriptions, aborting\n" );
429 std::list<CGameDescription *>::iterator iGame, iPrevGame;
430 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
432 if ( iPrevGame != mGames.end() ) {
433 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
434 CGameDescription *h = *iGame;
442 CGameDescription* currentGameDescription = 0;
444 if ( !m_bGamePrompt ) {
445 // search by .game name
446 std::list<CGameDescription *>::iterator iGame;
447 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
449 if ( ( *iGame )->mGameFile == m_sGameFile ) {
450 currentGameDescription = ( *iGame );
455 if ( m_bGamePrompt || !currentGameDescription ) {
458 // use m_nComboSelect to identify the game to run as and set the globals
459 currentGameDescription = GameDescriptionForComboItem();
460 ASSERT_NOTNULL( currentGameDescription );
462 g_pGameDescription = currentGameDescription;
464 g_pGameDescription->Dump();
467 CGameDialog::~CGameDialog(){
468 // free all the game descriptions
469 std::list<CGameDescription *>::iterator iGame;
470 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
475 if ( GetWidget() != 0 ) {
480 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
481 const char* identifier = gameDescription.getKeyValue( "index" );
482 if ( string_empty( identifier ) ) {
488 void CGameDialog::AddPacksURL( StringOutputStream &URL ){
489 // add the URLs for the list of game packs installed
490 // FIXME: this is kinda hardcoded for now..
491 std::list<CGameDescription *>::iterator iGame;
492 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
494 URL << "&Games_dlup%5B%5D=" << GameDescription_getIdentifier( *( *iGame ) );
498 CGameDialog g_GamesDialog;
501 // =============================================================================
502 // Widget callbacks for PrefsDlg
504 static void OnButtonClean( GtkWidget *widget, gpointer data ){
505 // make sure this is what the user wants
506 if ( gtk_MessageBox( GTK_WIDGET( g_Preferences.GetWidget() ), "This will close Radiant and clean the corresponding registry entries.\n"
507 "Next time you start Radiant it will be good as new. Do you wish to continue?",
508 "Reset Registry", eMB_YESNO, eMB_ICONASTERISK ) == eIDYES ) {
509 PrefsDlg *dlg = (PrefsDlg*)data;
510 dlg->EndModal( eIDCANCEL );
512 g_preferences_globals.disable_ini = true;
518 // =============================================================================
524 very first prefs init deals with selecting the game and the game tools path
525 then we can load .ini stuff
527 using prefs / ini settings:
530 look in ~/.radiant/<version>/gamename
534 #define PREFS_LOCAL_FILENAME "local.pref"
536 void PrefsDlg::Init(){
537 // m_global_rc_path has been set above
538 // m_rc_path is for game specific preferences
539 // takes the form: global-pref-path/gamename/prefs-file
541 // this is common to win32 and Linux init now
542 m_rc_path = g_string_new( m_global_rc_path->str );
545 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
546 g_string_append( m_rc_path, "/" );
547 Q_mkdir( m_rc_path->str );
550 m_inipath = g_string_new( m_rc_path->str );
551 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
554 void notebook_set_page( GtkWidget* notebook, GtkWidget* page ){
555 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
556 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
557 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
561 void PrefsDlg::showPrefPage( GtkWidget* prefpage ){
562 notebook_set_page( m_notebook, prefpage );
566 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
567 PrefsDlg *dlg = (PrefsDlg*)data;
570 GtkTreeIter selected;
571 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
573 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
574 dlg->showPrefPage( prefpage );
578 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
580 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
581 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
588 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
589 callbacks.push_back( callback );
592 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
594 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
595 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
601 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
602 callbacks.push_back( callback );
605 PreferencesPageCallbacks g_interfacePreferences;
606 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
607 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
609 PreferenceGroupCallbacks g_interfaceCallbacks;
610 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
611 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
614 PreferencesPageCallbacks g_displayPreferences;
615 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
616 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
618 PreferenceGroupCallbacks g_displayCallbacks;
619 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
620 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
623 PreferencesPageCallbacks g_settingsPreferences;
624 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
625 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
627 PreferenceGroupCallbacks g_settingsCallbacks;
628 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
629 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
632 void Widget_updateDependency( GtkWidget* self, GtkWidget* toggleButton ){
633 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && GTK_WIDGET_IS_SENSITIVE( toggleButton ) );
636 void ToggleButton_toggled_Widget_updateDependency( GtkWidget *toggleButton, GtkWidget* self ){
637 Widget_updateDependency( self, toggleButton );
640 void ToggleButton_state_changed_Widget_updateDependency( GtkWidget* toggleButton, GtkStateType state, GtkWidget* self ){
641 if ( state == GTK_STATE_INSENSITIVE ) {
642 Widget_updateDependency( self, toggleButton );
646 void Widget_connectToggleDependency( GtkWidget* self, GtkWidget* toggleButton ){
647 g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
648 g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
649 Widget_updateDependency( self, toggleButton );
653 inline GtkWidget* getVBox( GtkWidget* page ){
654 return gtk_bin_get_child( GTK_BIN( page ) );
657 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, GtkWidget* page ){
659 gtk_tree_store_append( store, &group, parent );
660 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
664 GtkWidget* PreferencePages_addPage( GtkWidget* notebook, const char* name ){
665 GtkWidget* preflabel = gtk_label_new( name );
666 gtk_widget_show( preflabel );
668 GtkWidget* pageframe = gtk_frame_new( name );
669 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
670 gtk_widget_show( pageframe );
672 GtkWidget* vbox = gtk_vbox_new( FALSE, 4 );
673 gtk_widget_show( vbox );
674 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
675 gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
677 // Add the page to the notebook
678 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
683 class PreferenceTreeGroup : public PreferenceGroup
686 GtkWidget* m_notebook;
687 GtkTreeStore* m_store;
690 PreferenceTreeGroup( Dialog& dialog, GtkWidget* notebook, GtkTreeStore* store, GtkTreeIter group ) :
692 m_notebook( notebook ),
696 PreferencesPage createPage( const char* treeName, const char* frameName ){
697 GtkWidget* page = PreferencePages_addPage( m_notebook, frameName );
698 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
699 return PreferencesPage( m_dialog, getVBox( page ) );
703 GtkWindow* PrefsDlg::BuildDialog(){
704 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
705 Mouse_registerPreferencesPage();
707 GtkWindow* dialog = create_floating_window( "NetRadiant Preferences", m_parent );
710 GtkWidget* mainvbox = gtk_vbox_new( FALSE, 5 );
711 gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
712 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
713 gtk_widget_show( mainvbox );
716 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
717 gtk_widget_show( hbox );
718 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
721 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
722 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
725 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
726 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
729 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
730 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
735 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
736 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
737 gtk_widget_show( hbox );
740 GtkWidget* sc_win = gtk_scrolled_window_new( 0, 0 );
741 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
742 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
743 gtk_widget_show( sc_win );
744 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
746 // prefs pages notebook
747 m_notebook = gtk_notebook_new();
748 // hide the notebook tabs since its not supposed to look like a notebook
749 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
750 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
751 gtk_widget_show( m_notebook );
755 GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
757 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
758 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
761 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
762 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Preferences", renderer, "text", 0, NULL );
763 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
767 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
768 g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
771 gtk_widget_show( view );
773 gtk_container_add( GTK_CONTAINER( sc_win ), view );
776 /********************************************************************/
777 /* Add preference tree options */
778 /********************************************************************/
781 PreferencePages_addPage( m_notebook, "Front Page" );
784 GtkWidget* global = PreferencePages_addPage( m_notebook, "Global Preferences" );
786 PreferencesPage preferencesPage( *this, getVBox( global ) );
787 Global_constructPreferences( preferencesPage );
789 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
791 GtkWidget* game = PreferencePages_addPage( m_notebook, "Game" );
792 PreferencesPage preferencesPage( *this, getVBox( game ) );
793 g_GamesDialog.CreateGlobalFrame( preferencesPage );
795 PreferenceTree_appendPage( store, &group, "Game", game );
800 GtkWidget* interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
802 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
803 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
806 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
807 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
809 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
813 GtkWidget* display = PreferencePages_addPage( m_notebook, "Display Preferences" );
815 PreferencesPage preferencesPage( *this, getVBox( display ) );
816 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
818 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
819 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
821 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
825 GtkWidget* settings = PreferencePages_addPage( m_notebook, "General Settings" );
827 PreferencesPage preferencesPage( *this, getVBox( settings ) );
828 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
831 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
832 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
834 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
838 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
840 g_object_unref( G_OBJECT( store ) );
846 gtk_notebook_set_page( GTK_NOTEBOOK( m_notebook ), 0 );
851 preferences_globals_t g_preferences_globals;
853 PrefsDlg g_Preferences; // global prefs instance
856 void PreferencesDialog_constructWindow( GtkWindow* main_window ){
857 g_Preferences.m_parent = main_window;
858 g_Preferences.Create();
860 void PreferencesDialog_destroyWindow(){
861 g_Preferences.Destroy();
865 PreferenceDictionary g_preferences;
867 PreferenceSystem& GetPreferenceSystem(){
868 return g_preferences;
871 class PreferenceSystemAPI
873 PreferenceSystem* m_preferencesystem;
875 typedef PreferenceSystem Type;
876 STRING_CONSTANT( Name, "*" );
878 PreferenceSystemAPI(){
879 m_preferencesystem = &GetPreferenceSystem();
881 PreferenceSystem* getTable(){
882 return m_preferencesystem;
886 #include "modulesystem/singletonmodule.h"
887 #include "modulesystem/moduleregistry.h"
889 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
890 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
891 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
893 void Preferences_Load(){
894 g_GamesDialog.LoadPrefs();
896 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
898 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
899 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
903 void Preferences_Save(){
904 if ( g_preferences_globals.disable_ini ) {
908 g_GamesDialog.SavePrefs();
910 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
912 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
913 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
917 void Preferences_Reset(){
918 file_remove( g_Preferences.m_inipath->str );
922 void PrefsDlg::PostModal( EMessageBoxReturn code ){
923 if ( code == eIDOK ) {
929 std::vector<const char*> g_restart_required;
931 void PreferencesDialog_restartRequired( const char* staticName ){
932 g_restart_required.push_back( staticName );
935 void PreferencesDialog_showDialog(){
936 //if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
937 if ( g_Preferences.DoModal() == eIDOK ) {
938 if ( !g_restart_required.empty() ) {
939 StringOutputStream message( 256 );
940 message << "Preference changes require a restart:\n";
941 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
943 message << ( *i ) << '\n';
945 gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), message.c_str() );
946 g_restart_required.clear();
955 void GameName_importString( const char* value ){
956 gamename_set( value );
958 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
959 void GameName_exportString( const StringImportCallback& importer ){
960 importer( gamename_get() );
962 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
964 void GameMode_importString( const char* value ){
965 gamemode_set( value );
967 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
968 void GameMode_exportString( const StringImportCallback& importer ){
969 importer( gamemode_get() );
971 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
974 void RegisterPreferences( PreferenceSystem& preferences ){
976 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
978 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
979 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
982 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
983 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
986 void Preferences_Init(){
987 RegisterPreferences( GetPreferenceSystem() );