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 ){
73 page.appendPathEntry( "Shader Editor Command", g_TextEditor_editorCommand, false );
76 void Mouse_constructPreferences( PreferencesPage& page ){
78 // const char* buttons[] = { "2 button", "3 button", };
79 // page.appendRadio( "Mouse Type", g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) );
81 // page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick );
82 page.appendCheckBox( "", "Zoom to mouse pointer", g_xywindow_globals.m_bZoomInToPointer );
84 void Mouse_constructPage( PreferenceGroup& group ){
85 PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) );
86 Mouse_constructPreferences( page );
88 void Mouse_registerPreferencesPage(){
89 PreferencesDialog_addInterfacePage( FreeCaller1<PreferenceGroup&, Mouse_constructPage>() );
94 =========================================================
95 Games selection dialog
96 =========================================================
101 inline const char* xmlAttr_getName( xmlAttrPtr attr ){
102 return reinterpret_cast<const char*>( attr->name );
105 inline const char* xmlAttr_getValue( xmlAttrPtr attr ){
106 return reinterpret_cast<const char*>( attr->children->content );
109 CGameDescription::CGameDescription( xmlDocPtr pDoc, const CopiedString& gameFile ){
110 // read the user-friendly game name
111 xmlNodePtr pNode = pDoc->children;
113 while ( pNode != 0 && strcmp( (const char*)pNode->name, "game" ) )
118 Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL );
121 for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next )
123 m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) );
127 StringOutputStream path( 256 );
128 path << AppPath_get() << gameFile.c_str() << "/";
129 mGameToolsPath = path.c_str();
132 ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) );
134 mGameFile = gameFile;
137 GameDescription::iterator i = m_gameDescription.find( "type" );
138 if ( i == m_gameDescription.end() ) {
139 globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>( pDoc->URL ) << "'\n";
145 mGameType = ( *i ).second.c_str();
150 void CGameDescription::Dump(){
151 globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n";
152 for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i )
154 globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n";
158 CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
161 #include "warnings.h"
162 #include "stream/textfilestream.h"
163 #include "container/array.h"
164 #include "xml/ixml.h"
165 #include "xml/xmlparser.h"
166 #include "xml/xmlwriter.h"
168 #include "preferencedictionary.h"
169 #include "stringio.h"
171 const char* const PREFERENCES_VERSION = "1.0";
173 bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){
175 TextFileInputStream file( filename );
176 if ( !file.failed() ) {
177 XMLStreamParser parser( file );
178 XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION );
179 parser.exportXML( importer );
183 int l = strlen( cmdline_prefix );
184 for ( int i = 1; i < g_argc - 1; ++i )
186 if ( g_argv[i][0] == '-' ) {
187 if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) {
188 if ( g_argv[i][l + 1] == '-' ) {
189 preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] );
199 bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){
200 TextFileOutputStream file( filename );
201 if ( !file.failed() ) {
202 XMLStreamWriter writer( file );
203 XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION );
204 exporter.exportXML( writer );
210 bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){
211 Array<char> tmpName( filename, filename + strlen( filename ) + 1 + 3 );
212 *( tmpName.end() - 4 ) = 'T';
213 *( tmpName.end() - 3 ) = 'M';
214 *( tmpName.end() - 2 ) = 'P';
215 *( tmpName.end() - 1 ) = '\0';
217 return Preferences_Save( preferences, tmpName.data() )
218 && ( !file_exists( filename ) || file_remove( filename ) )
219 && file_move( tmpName.data(), filename );
224 void LogConsole_importString( const char* string ){
225 g_Console_enableLogging = string_equal( string, "true" );
226 Sys_LogFile( g_Console_enableLogging );
228 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
231 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
232 preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
233 preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
234 preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
238 PreferenceDictionary g_global_preferences;
240 void GlobalPreferences_Init(){
241 RegisterGlobalPreferences( g_global_preferences );
244 void CGameDialog::LoadPrefs(){
245 // load global .pref file
246 StringOutputStream strGlobalPref( 256 );
247 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
249 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
251 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
252 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
256 void CGameDialog::SavePrefs(){
257 StringOutputStream strGlobalPref( 256 );
258 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
260 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
262 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
263 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
267 void CGameDialog::DoGameDialog(){
271 // we save the prefs file
275 void CGameDialog::GameFileImport( int value ){
276 m_nComboSelect = value;
277 // use value to set m_sGameFile
278 std::list<CGameDescription *>::iterator iGame = mGames.begin();
280 for ( i = 0; i < value; i++ )
284 m_sGameFile = ( *iGame )->mGameFile;
287 void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
288 // use m_sGameFile to set value
289 std::list<CGameDescription *>::const_iterator iGame;
291 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
293 if ( ( *iGame )->mGameFile == m_sGameFile ) {
299 importCallback( m_nComboSelect );
302 void CGameDialog_GameFileImport( CGameDialog& self, int value ){
303 self.GameFileImport( value );
306 void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
307 self.GameFileExport( importCallback );
310 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
311 std::vector<const char*> games;
312 games.reserve( mGames.size() );
313 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
315 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
319 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
320 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
321 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
323 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
326 GtkWindow* CGameDialog::BuildDialog(){
327 GtkFrame* frame = create_dialog_frame( "Game settings", GTK_SHADOW_ETCHED_IN );
329 GtkVBox* vbox2 = create_dialog_vbox( 0, 4 );
330 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
333 PreferencesPage preferencesPage( *this, GTK_WIDGET( vbox2 ) );
334 Global_constructPreferences( preferencesPage );
335 CreateGlobalFrame( preferencesPage );
338 return create_simple_modal_dialog_window( "Global Preferences", m_modal, GTK_WIDGET( frame ) );
343 std::list<CGameDescription*>& mGames;
346 LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
348 void operator()( const char* name ) const {
349 if ( !extension_equal( path_get_extension( name ), "game" ) ) {
352 StringOutputStream strPath( 256 );
353 strPath << mPath << name;
354 globalOutputStream() << strPath.c_str() << '\n';
356 xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
358 mGames.push_front( new CGameDescription( pDoc, name ) );
363 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
368 void CGameDialog::ScanForGames(){
369 StringOutputStream strGamesPath( 256 );
370 strGamesPath << AppPath_get() << "games/";
371 const char *path = strGamesPath.c_str();
373 globalOutputStream() << "Scanning for game description files: " << path << '\n';
377 do we put game description files below AppPath, or in ~/.radiant
378 i.e. read only or read/write?
379 my guess .. readonly cause it's an install
380 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
381 (if that's really needed)
384 Directory_forEach( path, LoadGameFile( mGames, path ) );
387 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
388 std::list<CGameDescription *>::iterator iGame;
390 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
392 if ( i == m_nComboSelect ) {
396 return 0; // not found
399 void CGameDialog::InitGlobalPrefPath(){
400 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
403 void CGameDialog::Reset(){
404 if ( !g_Preferences.m_global_rc_path ) {
405 InitGlobalPrefPath();
407 StringOutputStream strGlobalPref( 256 );
408 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
409 file_remove( strGlobalPref.c_str() );
412 void CGameDialog::Init(){
413 InitGlobalPrefPath();
416 if ( mGames.empty() ) {
417 Error( "Didn't find any valid game file descriptions, aborting\n" );
421 std::list<CGameDescription *>::iterator iGame, iPrevGame;
422 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
424 if ( iPrevGame != mGames.end() ) {
425 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
426 CGameDescription *h = *iGame;
434 CGameDescription* currentGameDescription = 0;
436 if ( !m_bGamePrompt ) {
437 // search by .game name
438 std::list<CGameDescription *>::iterator iGame;
439 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
441 if ( ( *iGame )->mGameFile == m_sGameFile ) {
442 currentGameDescription = ( *iGame );
447 if ( m_bGamePrompt || !currentGameDescription ) {
450 // use m_nComboSelect to identify the game to run as and set the globals
451 currentGameDescription = GameDescriptionForComboItem();
452 ASSERT_NOTNULL( currentGameDescription );
454 g_pGameDescription = currentGameDescription;
456 g_pGameDescription->Dump();
459 CGameDialog::~CGameDialog(){
460 // free all the game descriptions
461 std::list<CGameDescription *>::iterator iGame;
462 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
467 if ( GetWidget() != 0 ) {
472 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
473 const char* identifier = gameDescription.getKeyValue( "index" );
474 if ( string_empty( identifier ) ) {
480 void CGameDialog::AddPacksURL( StringOutputStream &URL ){
481 // add the URLs for the list of game packs installed
482 // FIXME: this is kinda hardcoded for now..
483 std::list<CGameDescription *>::iterator iGame;
484 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
486 URL << "&Games_dlup%5B%5D=" << GameDescription_getIdentifier( *( *iGame ) );
490 CGameDialog g_GamesDialog;
493 // =============================================================================
494 // Widget callbacks for PrefsDlg
496 static void OnButtonClean( GtkWidget *widget, gpointer data ){
497 // make sure this is what the user wants
498 if ( gtk_MessageBox( GTK_WIDGET( g_Preferences.GetWidget() ), "This will close Radiant and clean the corresponding registry entries.\n"
499 "Next time you start Radiant it will be good as new. Do you wish to continue?",
500 "Reset Registry", eMB_YESNO, eMB_ICONASTERISK ) == eIDYES ) {
501 PrefsDlg *dlg = (PrefsDlg*)data;
502 dlg->EndModal( eIDCANCEL );
504 g_preferences_globals.disable_ini = true;
510 // =============================================================================
516 very first prefs init deals with selecting the game and the game tools path
517 then we can load .ini stuff
519 using prefs / ini settings:
522 look in ~/.radiant/<version>/gamename
526 #define PREFS_LOCAL_FILENAME "local.pref"
528 void PrefsDlg::Init(){
529 // m_global_rc_path has been set above
530 // m_rc_path is for game specific preferences
531 // takes the form: global-pref-path/gamename/prefs-file
533 // this is common to win32 and Linux init now
534 m_rc_path = g_string_new( m_global_rc_path->str );
537 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
538 g_string_append( m_rc_path, "/" );
539 Q_mkdir( m_rc_path->str );
542 m_inipath = g_string_new( m_rc_path->str );
543 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
546 void notebook_set_page( GtkWidget* notebook, GtkWidget* page ){
547 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
548 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
549 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
553 void PrefsDlg::showPrefPage( GtkWidget* prefpage ){
554 notebook_set_page( m_notebook, prefpage );
558 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
559 PrefsDlg *dlg = (PrefsDlg*)data;
562 GtkTreeIter selected;
563 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
565 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
566 dlg->showPrefPage( prefpage );
570 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
572 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
573 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
580 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
581 callbacks.push_back( callback );
584 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
586 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
587 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
593 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
594 callbacks.push_back( callback );
597 PreferencesPageCallbacks g_interfacePreferences;
598 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
599 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
601 PreferenceGroupCallbacks g_interfaceCallbacks;
602 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
603 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
606 PreferencesPageCallbacks g_displayPreferences;
607 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
608 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
610 PreferenceGroupCallbacks g_displayCallbacks;
611 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
612 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
615 PreferencesPageCallbacks g_settingsPreferences;
616 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
617 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
619 PreferenceGroupCallbacks g_settingsCallbacks;
620 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
621 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
624 void Widget_updateDependency( GtkWidget* self, GtkWidget* toggleButton ){
625 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && GTK_WIDGET_IS_SENSITIVE( toggleButton ) );
628 void ToggleButton_toggled_Widget_updateDependency( GtkWidget *toggleButton, GtkWidget* self ){
629 Widget_updateDependency( self, toggleButton );
632 void ToggleButton_state_changed_Widget_updateDependency( GtkWidget* toggleButton, GtkStateType state, GtkWidget* self ){
633 if ( state == GTK_STATE_INSENSITIVE ) {
634 Widget_updateDependency( self, toggleButton );
638 void Widget_connectToggleDependency( GtkWidget* self, GtkWidget* toggleButton ){
639 g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
640 g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
641 Widget_updateDependency( self, toggleButton );
645 inline GtkWidget* getVBox( GtkWidget* page ){
646 return gtk_bin_get_child( GTK_BIN( page ) );
649 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, GtkWidget* page ){
651 gtk_tree_store_append( store, &group, parent );
652 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
656 GtkWidget* PreferencePages_addPage( GtkWidget* notebook, const char* name ){
657 GtkWidget* preflabel = gtk_label_new( name );
658 gtk_widget_show( preflabel );
660 GtkWidget* pageframe = gtk_frame_new( name );
661 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
662 gtk_widget_show( pageframe );
664 GtkWidget* vbox = gtk_vbox_new( FALSE, 4 );
665 gtk_widget_show( vbox );
666 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
667 gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
669 // Add the page to the notebook
670 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
675 class PreferenceTreeGroup : public PreferenceGroup
678 GtkWidget* m_notebook;
679 GtkTreeStore* m_store;
682 PreferenceTreeGroup( Dialog& dialog, GtkWidget* notebook, GtkTreeStore* store, GtkTreeIter group ) :
684 m_notebook( notebook ),
688 PreferencesPage createPage( const char* treeName, const char* frameName ){
689 GtkWidget* page = PreferencePages_addPage( m_notebook, frameName );
690 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
691 return PreferencesPage( m_dialog, getVBox( page ) );
695 GtkWindow* PrefsDlg::BuildDialog(){
696 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
697 //Mouse_registerPreferencesPage();
699 GtkWindow* dialog = create_floating_window( "NetRadiant Preferences", m_parent );
702 GtkWidget* mainvbox = gtk_vbox_new( FALSE, 5 );
703 gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
704 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
705 gtk_widget_show( mainvbox );
708 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
709 gtk_widget_show( hbox );
710 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
713 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
714 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
717 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
718 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
721 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
722 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
727 GtkWidget* hbox = gtk_hbox_new( FALSE, 5 );
728 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
729 gtk_widget_show( hbox );
732 GtkWidget* sc_win = gtk_scrolled_window_new( 0, 0 );
733 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
734 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
735 gtk_widget_show( sc_win );
736 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
738 // prefs pages notebook
739 m_notebook = gtk_notebook_new();
740 // hide the notebook tabs since its not supposed to look like a notebook
741 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
742 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
743 gtk_widget_show( m_notebook );
747 GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
749 GtkWidget* view = gtk_tree_view_new_with_model( GTK_TREE_MODEL( store ) );
750 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
753 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
754 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Preferences", renderer, "text", 0, NULL );
755 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
759 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
760 g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
763 gtk_widget_show( view );
765 gtk_container_add( GTK_CONTAINER( sc_win ), view );
768 /********************************************************************/
769 /* Add preference tree options */
770 /********************************************************************/
773 PreferencePages_addPage( m_notebook, "Front Page" );
776 GtkWidget* global = PreferencePages_addPage( m_notebook, "Global Preferences" );
778 PreferencesPage preferencesPage( *this, getVBox( global ) );
779 Global_constructPreferences( preferencesPage );
781 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
783 GtkWidget* game = PreferencePages_addPage( m_notebook, "Game" );
784 PreferencesPage preferencesPage( *this, getVBox( game ) );
785 g_GamesDialog.CreateGlobalFrame( preferencesPage );
787 PreferenceTree_appendPage( store, &group, "Game", game );
792 GtkWidget* interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
794 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
795 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
798 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
799 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
801 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
805 GtkWidget* display = PreferencePages_addPage( m_notebook, "Display Preferences" );
807 PreferencesPage preferencesPage( *this, getVBox( display ) );
808 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
810 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
811 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
813 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
817 GtkWidget* settings = PreferencePages_addPage( m_notebook, "General Settings" );
819 PreferencesPage preferencesPage( *this, getVBox( settings ) );
820 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
823 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
824 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
826 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
830 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
832 g_object_unref( G_OBJECT( store ) );
838 gtk_notebook_set_page( GTK_NOTEBOOK( m_notebook ), 0 );
843 preferences_globals_t g_preferences_globals;
845 PrefsDlg g_Preferences; // global prefs instance
848 void PreferencesDialog_constructWindow( GtkWindow* main_window ){
849 g_Preferences.m_parent = main_window;
850 g_Preferences.Create();
852 void PreferencesDialog_destroyWindow(){
853 g_Preferences.Destroy();
857 PreferenceDictionary g_preferences;
859 PreferenceSystem& GetPreferenceSystem(){
860 return g_preferences;
863 class PreferenceSystemAPI
865 PreferenceSystem* m_preferencesystem;
867 typedef PreferenceSystem Type;
868 STRING_CONSTANT( Name, "*" );
870 PreferenceSystemAPI(){
871 m_preferencesystem = &GetPreferenceSystem();
873 PreferenceSystem* getTable(){
874 return m_preferencesystem;
878 #include "modulesystem/singletonmodule.h"
879 #include "modulesystem/moduleregistry.h"
881 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
882 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
883 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
885 void Preferences_Load(){
886 g_GamesDialog.LoadPrefs();
888 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
890 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
891 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
895 void Preferences_Save(){
896 if ( g_preferences_globals.disable_ini ) {
900 g_GamesDialog.SavePrefs();
902 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
904 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
905 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
909 void Preferences_Reset(){
910 file_remove( g_Preferences.m_inipath->str );
914 void PrefsDlg::PostModal( EMessageBoxReturn code ){
915 if ( code == eIDOK ) {
921 std::vector<const char*> g_restart_required;
923 void PreferencesDialog_restartRequired( const char* staticName ){
924 g_restart_required.push_back( staticName );
927 void PreferencesDialog_showDialog(){
928 //if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
929 if ( g_Preferences.DoModal() == eIDOK ) {
930 if ( !g_restart_required.empty() ) {
931 StringOutputStream message( 256 );
932 message << "Preference changes require a restart:\n";
933 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
935 message << ( *i ) << '\n';
937 gtk_MessageBox( GTK_WIDGET( MainFrame_getWindow() ), message.c_str() );
938 g_restart_required.clear();
947 void GameName_importString( const char* value ){
948 gamename_set( value );
950 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
951 void GameName_exportString( const StringImportCallback& importer ){
952 importer( gamename_get() );
954 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
956 void GameMode_importString( const char* value ){
957 gamemode_set( value );
959 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
960 void GameMode_exportString( const StringImportCallback& importer ){
961 importer( gamemode_get() );
963 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
966 void RegisterPreferences( PreferenceSystem& preferences ){
967 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
969 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
970 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
973 void Preferences_Init(){
974 RegisterPreferences( GetPreferenceSystem() );