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 ui::CheckButton use_custom = page.appendCheckBox( "Text Editor", "Custom", g_TextEditor_useCustomEditor );
78 ui::Widget 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 =========================================================
107 #include <uilib/uilib.h>
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 std::string& gameFile ){
118 // read the user-friendly game name
119 xmlNodePtr pNode = pDoc->children;
121 while ( strcmp( (const char*)pNode->name, "game" ) && pNode != 0 )
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 std::string tmpName( filename );
222 return Preferences_Save( preferences, tmpName.c_str() )
223 && ( !file_exists( filename ) || file_remove( filename ) )
224 && file_move( tmpName.c_str(), filename );
229 void LogConsole_importString( const char* string ){
230 g_Console_enableLogging = string_equal( string, "true" );
231 Sys_LogFile( g_Console_enableLogging );
233 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
236 void RegisterGlobalPreferences( PreferenceSystem& preferences ){
237 preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) );
238 preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) );
239 preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) );
243 PreferenceDictionary g_global_preferences;
245 void GlobalPreferences_Init(){
246 RegisterGlobalPreferences( g_global_preferences );
249 void CGameDialog::LoadPrefs(){
250 // load global .pref file
251 StringOutputStream strGlobalPref( 256 );
252 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
254 globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n";
256 if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) {
257 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
261 void CGameDialog::SavePrefs(){
262 StringOutputStream strGlobalPref( 256 );
263 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
265 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
267 if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) {
268 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
272 void CGameDialog::DoGameDialog(){
276 // we save the prefs file
280 void CGameDialog::GameFileImport( int value ){
281 m_nComboSelect = value;
282 // use value to set m_sGameFile
283 std::list<CGameDescription *>::iterator iGame = mGames.begin();
285 for ( i = 0; i < value; i++ )
289 m_sGameFile = ( *iGame )->mGameFile;
292 void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const {
293 // use m_sGameFile to set value
294 std::list<CGameDescription *>::const_iterator iGame;
296 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
298 if ( ( *iGame )->mGameFile == m_sGameFile ) {
304 importCallback( m_nComboSelect );
307 void CGameDialog_GameFileImport( CGameDialog& self, int value ){
308 self.GameFileImport( value );
311 void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){
312 self.GameFileExport( importCallback );
315 void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){
316 std::vector<const char*> games;
317 games.reserve( mGames.size() );
318 for ( std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i )
320 games.push_back( ( *i )->getRequiredKeyValue( "name" ) );
324 StringArrayRange( &( *games.begin() ), &( *games.end() ) ),
325 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>( *this ),
326 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>( *this )
328 page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt );
331 ui::Window CGameDialog::BuildDialog(){
332 GtkFrame* frame = create_dialog_frame( "Game settings", GTK_SHADOW_ETCHED_IN );
334 GtkVBox* vbox2 = create_dialog_vbox( 0, 4 );
335 gtk_container_add( GTK_CONTAINER( frame ), GTK_WIDGET( vbox2 ) );
338 PreferencesPage preferencesPage( *this, ui::Widget(GTK_WIDGET( vbox2 )) );
339 Global_constructPreferences( preferencesPage );
340 CreateGlobalFrame( preferencesPage );
343 return ui::Window(create_simple_modal_dialog_window( "Global Preferences", m_modal, GTK_WIDGET( frame ) ));
348 std::list<CGameDescription*>& mGames;
351 LoadGameFile( std::list<CGameDescription*>& games, const char* path ) : mGames( games ), mPath( path ){
353 void operator()( const char* name ) const {
354 if ( !extension_equal( path_get_extension( name ), "game" ) ) {
357 StringOutputStream strPath( 256 );
358 strPath << mPath << name;
359 globalOutputStream() << strPath.c_str() << '\n';
361 xmlDocPtr pDoc = xmlParseFile( strPath.c_str() );
363 mGames.push_front( new CGameDescription( pDoc, name ) );
368 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
373 void CGameDialog::ScanForGames(){
374 StringOutputStream strGamesPath( 256 );
375 strGamesPath << AppPath_get() << "games/";
376 const char *path = strGamesPath.c_str();
378 globalOutputStream() << "Scanning for game description files: " << path << '\n';
382 do we put game description files below AppPath, or in ~/.radiant
383 i.e. read only or read/write?
384 my guess .. readonly cause it's an install
385 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
386 (if that's really needed)
389 Directory_forEach( path, LoadGameFile( mGames, path ) );
392 CGameDescription* CGameDialog::GameDescriptionForComboItem(){
393 std::list<CGameDescription *>::iterator iGame;
395 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ )
397 if ( i == m_nComboSelect ) {
401 return 0; // not found
404 void CGameDialog::InitGlobalPrefPath(){
405 g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() );
408 void CGameDialog::Reset(){
409 if ( !g_Preferences.m_global_rc_path ) {
410 InitGlobalPrefPath();
412 StringOutputStream strGlobalPref( 256 );
413 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
414 file_remove( strGlobalPref.c_str() );
417 void CGameDialog::Init(){
418 InitGlobalPrefPath();
421 if ( mGames.empty() ) {
422 Error( "Didn't find any valid game file descriptions, aborting\n" );
426 std::list<CGameDescription *>::iterator iGame, iPrevGame;
427 for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame )
429 if ( iPrevGame != mGames.end() ) {
430 if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) {
431 CGameDescription *h = *iGame;
439 CGameDescription* currentGameDescription = 0;
441 if ( !m_bGamePrompt ) {
442 // search by .game name
443 std::list<CGameDescription *>::iterator iGame;
444 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
446 if ( ( *iGame )->mGameFile == m_sGameFile ) {
447 currentGameDescription = ( *iGame );
452 if ( m_bGamePrompt || !currentGameDescription ) {
455 // use m_nComboSelect to identify the game to run as and set the globals
456 currentGameDescription = GameDescriptionForComboItem();
457 ASSERT_NOTNULL( currentGameDescription );
459 g_pGameDescription = currentGameDescription;
461 g_pGameDescription->Dump();
464 CGameDialog::~CGameDialog(){
465 // free all the game descriptions
466 std::list<CGameDescription *>::iterator iGame;
467 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
477 inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){
478 const char* identifier = gameDescription.getKeyValue( "index" );
479 if ( string_empty( identifier ) ) {
485 void CGameDialog::AddPacksURL( StringOutputStream &URL ){
486 // add the URLs for the list of game packs installed
487 // FIXME: this is kinda hardcoded for now..
488 std::list<CGameDescription *>::iterator iGame;
489 for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame )
491 URL << "&Games_dlup%5B%5D=" << GameDescription_getIdentifier( *( *iGame ) );
495 CGameDialog g_GamesDialog;
498 // =============================================================================
499 // Widget callbacks for PrefsDlg
501 static void OnButtonClean( ui::Widget widget, gpointer data ){
502 // make sure this is what the user wants
503 if ( ui::Widget(GTK_WIDGET( g_Preferences.GetWidget() )).alert( "This will close Radiant and clean the corresponding registry entries.\n"
504 "Next time you start Radiant it will be good as new. Do you wish to continue?",
505 "Reset Registry", ui::alert_type::YESNO, ui::alert_icon::ASTERISK ) == ui::alert_response::YES ) {
506 PrefsDlg *dlg = (PrefsDlg*)data;
507 dlg->EndModal( eIDCANCEL );
509 g_preferences_globals.disable_ini = true;
515 // =============================================================================
521 very first prefs init deals with selecting the game and the game tools path
522 then we can load .ini stuff
524 using prefs / ini settings:
527 look in ~/.radiant/<version>/gamename
531 #define PREFS_LOCAL_FILENAME "local.pref"
533 void PrefsDlg::Init(){
534 // m_global_rc_path has been set above
535 // m_rc_path is for game specific preferences
536 // takes the form: global-pref-path/gamename/prefs-file
538 // this is common to win32 and Linux init now
539 m_rc_path = g_string_new( m_global_rc_path->str );
542 g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() );
543 g_string_append( m_rc_path, "/" );
544 Q_mkdir( m_rc_path->str );
547 m_inipath = g_string_new( m_rc_path->str );
548 g_string_append( m_inipath, PREFS_LOCAL_FILENAME );
551 void notebook_set_page( ui::Widget notebook, ui::Widget page ){
552 int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page );
553 if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) {
554 gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum );
558 void PrefsDlg::showPrefPage( ui::Widget prefpage ){
559 notebook_set_page( m_notebook, prefpage );
563 static void treeSelection( GtkTreeSelection* selection, gpointer data ){
564 PrefsDlg *dlg = (PrefsDlg*)data;
567 GtkTreeIter selected;
568 if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) {
570 gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 );
571 dlg->showPrefPage( prefpage );
575 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
577 inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){
578 for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
585 inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){
586 callbacks.push_back( callback );
589 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
591 inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){
592 for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i )
598 inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){
599 callbacks.push_back( callback );
602 PreferencesPageCallbacks g_interfacePreferences;
603 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){
604 PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback );
606 PreferenceGroupCallbacks g_interfaceCallbacks;
607 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){
608 PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback );
611 PreferencesPageCallbacks g_displayPreferences;
612 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){
613 PreferencesPageCallbacks_pushBack( g_displayPreferences, callback );
615 PreferenceGroupCallbacks g_displayCallbacks;
616 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){
617 PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback );
620 PreferencesPageCallbacks g_settingsPreferences;
621 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){
622 PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback );
624 PreferenceGroupCallbacks g_settingsCallbacks;
625 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){
626 PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback );
629 void Widget_updateDependency( ui::Widget self, ui::Widget toggleButton ){
630 gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && GTK_WIDGET_IS_SENSITIVE( toggleButton ) );
633 void ToggleButton_toggled_Widget_updateDependency( ui::Widget toggleButton, ui::Widget self ){
634 Widget_updateDependency( self, toggleButton );
637 void ToggleButton_state_changed_Widget_updateDependency( ui::Widget toggleButton, GtkStateType state, ui::Widget self ){
638 if ( state == GTK_STATE_INSENSITIVE ) {
639 Widget_updateDependency( self, toggleButton );
643 void Widget_connectToggleDependency( ui::Widget self, ui::Widget toggleButton ){
644 g_signal_connect( G_OBJECT( toggleButton ), "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self );
645 g_signal_connect( G_OBJECT( toggleButton ), "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self );
646 Widget_updateDependency( self, toggleButton );
650 inline ui::Widget getVBox( ui::Widget page ){
651 return ui::Widget(gtk_bin_get_child( GTK_BIN( page ) ));
654 GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, ui::Widget page ){
656 gtk_tree_store_append( store, &group, parent );
657 gtk_tree_store_set( store, &group, 0, name, 1, page, -1 );
661 ui::Widget PreferencePages_addPage( ui::Widget notebook, const char* name ){
662 ui::Widget preflabel = ui::Label( name );
663 gtk_widget_show( preflabel );
665 ui::Widget pageframe = ui::Widget(gtk_frame_new( name ));
666 gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 );
667 gtk_widget_show( pageframe );
669 ui::Widget vbox = ui::VBox( FALSE, 4 );
670 gtk_widget_show( vbox );
671 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 );
672 gtk_container_add( GTK_CONTAINER( pageframe ), vbox );
674 // Add the page to the notebook
675 gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel );
680 class PreferenceTreeGroup : public PreferenceGroup
683 ui::Widget m_notebook;
684 GtkTreeStore* m_store;
687 PreferenceTreeGroup( Dialog& dialog, ui::Widget notebook, GtkTreeStore* store, GtkTreeIter group ) :
689 m_notebook( notebook ),
693 PreferencesPage createPage( const char* treeName, const char* frameName ){
694 ui::Widget page = PreferencePages_addPage( m_notebook, frameName );
695 PreferenceTree_appendPage( m_store, &m_group, treeName, page );
696 return PreferencesPage( m_dialog, getVBox( page ) );
700 ui::Window PrefsDlg::BuildDialog(){
701 PreferencesDialog_addInterfacePreferences( FreeCaller1<PreferencesPage&, Interface_constructPreferences>() );
702 Mouse_registerPreferencesPage();
704 ui::Window dialog = ui::Window(create_floating_window( "NetRadiant Preferences", m_parent ));
707 ui::Widget mainvbox = ui::VBox( FALSE, 5 );
708 gtk_container_add( GTK_CONTAINER( dialog ), mainvbox );
709 gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 );
710 gtk_widget_show( mainvbox );
713 ui::Widget hbox = ui::Widget(gtk_hbox_new( FALSE, 5 ));
714 gtk_widget_show( hbox );
715 gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 );
718 GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal );
719 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
722 GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal );
723 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
726 GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this );
727 gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 );
732 ui::Widget hbox = ui::Widget(gtk_hbox_new( FALSE, 5 ));
733 gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 );
734 gtk_widget_show( hbox );
737 ui::Widget sc_win = ui::ScrolledWindow();
738 gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
739 gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 );
740 gtk_widget_show( sc_win );
741 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN );
743 // prefs pages notebook
744 m_notebook = ui::Widget(gtk_notebook_new());
745 // hide the notebook tabs since its not supposed to look like a notebook
746 gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE );
747 gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 );
748 gtk_widget_show( m_notebook );
752 GtkTreeStore* store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER );
754 ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) ));
755 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE );
758 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
759 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( "Preferences", renderer, "text", 0, NULL );
760 gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column );
764 GtkTreeSelection* selection = gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) );
765 g_signal_connect( G_OBJECT( selection ), "changed", G_CALLBACK( treeSelection ), this );
768 gtk_widget_show( view );
770 gtk_container_add( GTK_CONTAINER( sc_win ), view );
773 /********************************************************************/
774 /* Add preference tree options */
775 /********************************************************************/
778 PreferencePages_addPage( m_notebook, "Front Page" );
781 ui::Widget global = PreferencePages_addPage( m_notebook, "Global Preferences" );
783 PreferencesPage preferencesPage( *this, getVBox( global ) );
784 Global_constructPreferences( preferencesPage );
786 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global );
788 ui::Widget game = PreferencePages_addPage( m_notebook, "Game" );
789 PreferencesPage preferencesPage( *this, getVBox( game ) );
790 g_GamesDialog.CreateGlobalFrame( preferencesPage );
792 PreferenceTree_appendPage( store, &group, "Game", game );
797 ui::Widget interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" );
799 PreferencesPage preferencesPage( *this, getVBox( interfacePage ) );
800 PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage );
803 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage );
804 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
806 PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup );
810 ui::Widget display = PreferencePages_addPage( m_notebook, "Display Preferences" );
812 PreferencesPage preferencesPage( *this, getVBox( display ) );
813 PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage );
815 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display );
816 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
818 PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup );
822 ui::Widget settings = PreferencePages_addPage( m_notebook, "General Settings" );
824 PreferencesPage preferencesPage( *this, getVBox( settings ) );
825 PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage );
828 GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings );
829 PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group );
831 PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup );
835 gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) );
837 g_object_unref( G_OBJECT( store ) );
843 gtk_notebook_set_page( GTK_NOTEBOOK( m_notebook ), 0 );
848 preferences_globals_t g_preferences_globals;
850 PrefsDlg g_Preferences; // global prefs instance
853 void PreferencesDialog_constructWindow( ui::Window main_window ){
854 g_Preferences.m_parent = main_window;
855 g_Preferences.Create();
857 void PreferencesDialog_destroyWindow(){
858 g_Preferences.Destroy();
862 PreferenceDictionary g_preferences;
864 PreferenceSystem& GetPreferenceSystem(){
865 return g_preferences;
868 class PreferenceSystemAPI
870 PreferenceSystem* m_preferencesystem;
872 typedef PreferenceSystem Type;
873 STRING_CONSTANT( Name, "*" );
875 PreferenceSystemAPI(){
876 m_preferencesystem = &GetPreferenceSystem();
878 PreferenceSystem* getTable(){
879 return m_preferencesystem;
883 #include "modulesystem/singletonmodule.h"
884 #include "modulesystem/moduleregistry.h"
886 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
887 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
888 StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() );
890 void Preferences_Load(){
891 g_GamesDialog.LoadPrefs();
893 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
895 if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) {
896 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
900 void Preferences_Save(){
901 if ( g_preferences_globals.disable_ini ) {
905 g_GamesDialog.SavePrefs();
907 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
909 if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) {
910 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
914 void Preferences_Reset(){
915 file_remove( g_Preferences.m_inipath->str );
919 void PrefsDlg::PostModal( EMessageBoxReturn code ){
920 if ( code == eIDOK ) {
926 std::vector<const char*> g_restart_required;
928 void PreferencesDialog_restartRequired( const char* staticName ){
929 g_restart_required.push_back( staticName );
932 void PreferencesDialog_showDialog(){
933 if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) {
934 if ( !g_restart_required.empty() ) {
935 StringOutputStream message( 256 );
936 message << "Preference changes require a restart:\n";
937 for ( std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i )
939 message << ( *i ) << '\n';
941 MainFrame_getWindow().alert( message.c_str() );
942 g_restart_required.clear();
951 void GameName_importString( const char* value ){
952 gamename_set( value );
954 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
955 void GameName_exportString( const StringImportCallback& importer ){
956 importer( gamename_get() );
958 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
960 void GameMode_importString( const char* value ){
961 gamemode_set( value );
963 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
964 void GameMode_exportString( const StringImportCallback& importer ){
965 importer( gamemode_get() );
967 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
970 void RegisterPreferences( PreferenceSystem& preferences ){
972 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) );
974 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) );
975 preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) );
978 preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() );
979 preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() );
982 void Preferences_Init(){
983 RegisterPreferences( GetPreferenceSystem() );