/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // // User preferences // // Leonardo Zide (leo@lokigames.com) // #include "preferences.h" #include #include "environment.h" #include "debugging/debugging.h" #include "generic/callback.h" #include "math/vector.h" #include "string/string.h" #include "stream/stringstream.h" #include "os/file.h" #include "os/path.h" #include "os/dir.h" #include "gtkutil/filechooser.h" #include "gtkutil/messagebox.h" #include "cmdlib.h" #include "error.h" #include "console.h" #include "xywindow.h" #include "mainframe.h" #include "qe3.h" #include "gtkdlgs.h" void Global_constructPreferences( PreferencesPage& page ){ page.appendCheckBox( "Console", "Enable Logging", g_Console_enableLogging ); } void Interface_constructPreferences( PreferencesPage& page ){ #ifdef WIN32 page.appendCheckBox( "", "Default Text Editor", g_TextEditor_useWin32Editor ); #else { ui::CheckButton use_custom = page.appendCheckBox( "Text Editor", "Custom", g_TextEditor_useCustomEditor ); ui::Widget custom_editor = page.appendPathEntry( "Text Editor Command", g_TextEditor_editorCommand, true ); Widget_connectToggleDependency( custom_editor, use_custom ); } #endif } void Mouse_constructPreferences( PreferencesPage& page ){ { const char* buttons[] = { "2 button", "3 button", }; page.appendRadio( "Mouse Type", g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE( buttons ) ); } page.appendCheckBox( "Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick ); } void Mouse_constructPage( PreferenceGroup& group ){ PreferencesPage page( group.createPage( "Mouse", "Mouse Preferences" ) ); Mouse_constructPreferences( page ); } void Mouse_registerPreferencesPage(){ PreferencesDialog_addInterfacePage( FreeCaller1() ); } /*! ========================================================= Games selection dialog ========================================================= */ #include #include inline const char* xmlAttr_getName( xmlAttrPtr attr ){ return reinterpret_cast( attr->name ); } inline const char* xmlAttr_getValue( xmlAttrPtr attr ){ return reinterpret_cast( attr->children->content ); } CGameDescription::CGameDescription( xmlDocPtr pDoc, const CopiedString& gameFile ){ // read the user-friendly game name xmlNodePtr pNode = pDoc->children; while ( strcmp( (const char*)pNode->name, "game" ) && pNode != 0 ) { pNode = pNode->next; } if ( !pNode ) { Error( "Didn't find 'game' node in the game description file '%s'\n", pDoc->URL ); } for ( xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next ) { m_gameDescription.insert( GameDescription::value_type( xmlAttr_getName( attr ), xmlAttr_getValue( attr ) ) ); } { StringOutputStream path( 256 ); path << AppPath_get() << gameFile.c_str() << "/"; mGameToolsPath = path.c_str(); } ASSERT_MESSAGE( file_exists( mGameToolsPath.c_str() ), "game directory not found: " << makeQuoted( mGameToolsPath.c_str() ) ); mGameFile = gameFile; { GameDescription::iterator i = m_gameDescription.find( "type" ); if ( i == m_gameDescription.end() ) { globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast( pDoc->URL ) << "'\n"; // default mGameType = "q3"; } else { mGameType = ( *i ).second.c_str(); } } } void CGameDescription::Dump(){ globalOutputStream() << "game description file: " << makeQuoted( mGameFile.c_str() ) << "\n"; for ( GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i ) { globalOutputStream() << ( *i ).first.c_str() << " = " << makeQuoted( ( *i ).second.c_str() ) << "\n"; } } CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription #include "warnings.h" #include "stream/textfilestream.h" #include "container/array.h" #include "xml/ixml.h" #include "xml/xmlparser.h" #include "xml/xmlwriter.h" #include "preferencedictionary.h" #include "stringio.h" const char* const PREFERENCES_VERSION = "1.0"; bool Preferences_Load( PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix ){ bool ret = false; TextFileInputStream file( filename ); if ( !file.failed() ) { XMLStreamParser parser( file ); XMLPreferenceDictionaryImporter importer( preferences, PREFERENCES_VERSION ); parser.exportXML( importer ); ret = true; } int l = strlen( cmdline_prefix ); for ( int i = 1; i < g_argc - 1; ++i ) { if ( g_argv[i][0] == '-' ) { if ( !strncmp( g_argv[i] + 1, cmdline_prefix, l ) ) { if ( g_argv[i][l + 1] == '-' ) { preferences.importPref( g_argv[i] + l + 2, g_argv[i + 1] ); } } ++i; } } return ret; } bool Preferences_Save( PreferenceDictionary& preferences, const char* filename ){ TextFileOutputStream file( filename ); if ( !file.failed() ) { XMLStreamWriter writer( file ); XMLPreferenceDictionaryExporter exporter( preferences, PREFERENCES_VERSION ); exporter.exportXML( writer ); return true; } return false; } bool Preferences_Save_Safe( PreferenceDictionary& preferences, const char* filename ){ Array tmpName( filename, filename + strlen( filename ) + 1 + 3 ); *( tmpName.end() - 4 ) = 'T'; *( tmpName.end() - 3 ) = 'M'; *( tmpName.end() - 2 ) = 'P'; *( tmpName.end() - 1 ) = '\0'; return Preferences_Save( preferences, tmpName.data() ) && ( !file_exists( filename ) || file_remove( filename ) ) && file_move( tmpName.data(), filename ); } void LogConsole_importString( const char* string ){ g_Console_enableLogging = string_equal( string, "true" ); Sys_LogFile( g_Console_enableLogging ); } typedef FreeCaller1 LogConsoleImportStringCaller; void RegisterGlobalPreferences( PreferenceSystem& preferences ){ preferences.registerPreference( "gamefile", CopiedStringImportStringCaller( g_GamesDialog.m_sGameFile ), CopiedStringExportStringCaller( g_GamesDialog.m_sGameFile ) ); preferences.registerPreference( "gamePrompt", BoolImportStringCaller( g_GamesDialog.m_bGamePrompt ), BoolExportStringCaller( g_GamesDialog.m_bGamePrompt ) ); preferences.registerPreference( "log console", LogConsoleImportStringCaller(), BoolExportStringCaller( g_Console_enableLogging ) ); } PreferenceDictionary g_global_preferences; void GlobalPreferences_Init(){ RegisterGlobalPreferences( g_global_preferences ); } void CGameDialog::LoadPrefs(){ // load global .pref file StringOutputStream strGlobalPref( 256 ); strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref"; globalOutputStream() << "loading global preferences from " << makeQuoted( strGlobalPref.c_str() ) << "\n"; if ( !Preferences_Load( g_global_preferences, strGlobalPref.c_str(), "global" ) ) { globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n"; } } void CGameDialog::SavePrefs(){ StringOutputStream strGlobalPref( 256 ); strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref"; globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n"; if ( !Preferences_Save_Safe( g_global_preferences, strGlobalPref.c_str() ) ) { globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n"; } } void CGameDialog::DoGameDialog(){ // show the UI DoModal(); // we save the prefs file SavePrefs(); } void CGameDialog::GameFileImport( int value ){ m_nComboSelect = value; // use value to set m_sGameFile std::list::iterator iGame = mGames.begin(); int i; for ( i = 0; i < value; i++ ) { ++iGame; } m_sGameFile = ( *iGame )->mGameFile; } void CGameDialog::GameFileExport( const IntImportCallback& importCallback ) const { // use m_sGameFile to set value std::list::const_iterator iGame; int i = 0; for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame ) { if ( ( *iGame )->mGameFile == m_sGameFile ) { m_nComboSelect = i; break; } i++; } importCallback( m_nComboSelect ); } void CGameDialog_GameFileImport( CGameDialog& self, int value ){ self.GameFileImport( value ); } void CGameDialog_GameFileExport( CGameDialog& self, const IntImportCallback& importCallback ){ self.GameFileExport( importCallback ); } void CGameDialog::CreateGlobalFrame( PreferencesPage& page ){ std::vector games; games.reserve( mGames.size() ); for ( std::list::iterator i = mGames.begin(); i != mGames.end(); ++i ) { games.push_back( ( *i )->getRequiredKeyValue( "name" ) ); } page.appendCombo( "Select the game", StringArrayRange( &( *games.begin() ), &( *games.end() ) ), ReferenceCaller1( *this ), ReferenceCaller1( *this ) ); page.appendCheckBox( "Startup", "Show Global Preferences", m_bGamePrompt ); } ui::Window CGameDialog::BuildDialog(){ auto frame = create_dialog_frame( "Game settings", ui::Shadow::ETCHED_IN ); auto vbox2 = create_dialog_vbox( 0, 4 ); frame.add(vbox2); { PreferencesPage preferencesPage( *this, ui::Widget(GTK_WIDGET( vbox2 )) ); Global_constructPreferences( preferencesPage ); CreateGlobalFrame( preferencesPage ); } return create_simple_modal_dialog_window( "Global Preferences", m_modal, frame ); } class LoadGameFile { std::list& mGames; const char* mPath; public: LoadGameFile( std::list& games, const char* path ) : mGames( games ), mPath( path ){ } void operator()( const char* name ) const { if ( !extension_equal( path_get_extension( name ), "game" ) ) { return; } StringOutputStream strPath( 256 ); strPath << mPath << name; globalOutputStream() << strPath.c_str() << '\n'; xmlDocPtr pDoc = xmlParseFile( strPath.c_str() ); if ( pDoc ) { mGames.push_front( new CGameDescription( pDoc, name ) ); xmlFreeDoc( pDoc ); } else { globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n"; } } }; void CGameDialog::ScanForGames(){ StringOutputStream strGamesPath( 256 ); strGamesPath << AppPath_get() << "games/"; const char *path = strGamesPath.c_str(); globalOutputStream() << "Scanning for game description files: " << path << '\n'; /*! \todo FIXME LINUX: do we put game description files below AppPath, or in ~/.radiant i.e. read only or read/write? my guess .. readonly cause it's an install we will probably want to add ~/.radiant//games/ scanning on top of that for developers (if that's really needed) */ Directory_forEach( path, LoadGameFile( mGames, path ) ); } CGameDescription* CGameDialog::GameDescriptionForComboItem(){ std::list::iterator iGame; int i = 0; for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame,i++ ) { if ( i == m_nComboSelect ) { return ( *iGame ); } } return 0; // not found } void CGameDialog::InitGlobalPrefPath(){ g_Preferences.m_global_rc_path = g_string_new( SettingsPath_get() ); } void CGameDialog::Reset(){ if ( !g_Preferences.m_global_rc_path ) { InitGlobalPrefPath(); } StringOutputStream strGlobalPref( 256 ); strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref"; file_remove( strGlobalPref.c_str() ); } void CGameDialog::Init(){ InitGlobalPrefPath(); LoadPrefs(); ScanForGames(); if ( mGames.empty() ) { Error( "Didn't find any valid game file descriptions, aborting\n" ); } else { std::list::iterator iGame, iPrevGame; for ( iGame = mGames.begin(), iPrevGame = mGames.end(); iGame != mGames.end(); iPrevGame = iGame, ++iGame ) { if ( iPrevGame != mGames.end() ) { if ( strcmp( ( *iGame )->getRequiredKeyValue( "name" ), ( *iPrevGame )->getRequiredKeyValue( "name" ) ) < 0 ) { CGameDescription *h = *iGame; *iGame = *iPrevGame; *iPrevGame = h; } } } } CGameDescription* currentGameDescription = 0; if ( !m_bGamePrompt ) { // search by .game name std::list::iterator iGame; for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame ) { if ( ( *iGame )->mGameFile == m_sGameFile ) { currentGameDescription = ( *iGame ); break; } } } if ( m_bGamePrompt || !currentGameDescription ) { Create(); DoGameDialog(); // use m_nComboSelect to identify the game to run as and set the globals currentGameDescription = GameDescriptionForComboItem(); ASSERT_NOTNULL( currentGameDescription ); } g_pGameDescription = currentGameDescription; g_pGameDescription->Dump(); } CGameDialog::~CGameDialog(){ // free all the game descriptions std::list::iterator iGame; for ( iGame = mGames.begin(); iGame != mGames.end(); ++iGame ) { delete ( *iGame ); *iGame = 0; } if ( GetWidget() ) { Destroy(); } } inline const char* GameDescription_getIdentifier( const CGameDescription& gameDescription ){ const char* identifier = gameDescription.getKeyValue( "index" ); if ( string_empty( identifier ) ) { identifier = "1"; } return identifier; } CGameDialog g_GamesDialog; // ============================================================================= // Widget callbacks for PrefsDlg static void OnButtonClean( ui::Widget widget, gpointer data ){ // make sure this is what the user wants if ( ui::Widget(GTK_WIDGET( g_Preferences.GetWidget() )).alert( "This will close Radiant and clean the corresponding registry entries.\n" "Next time you start Radiant it will be good as new. Do you wish to continue?", "Reset Registry", ui::alert_type::YESNO, ui::alert_icon::Asterisk ) == ui::alert_response::YES ) { PrefsDlg *dlg = (PrefsDlg*)data; dlg->EndModal( eIDCANCEL ); g_preferences_globals.disable_ini = true; Preferences_Reset(); gtk_main_quit(); } } // ============================================================================= // PrefsDlg class /* ======== very first prefs init deals with selecting the game and the game tools path then we can load .ini stuff using prefs / ini settings: those are per-game look in ~/.radiant//gamename ======== */ #define PREFS_LOCAL_FILENAME "local.pref" void PrefsDlg::Init(){ // m_global_rc_path has been set above // m_rc_path is for game specific preferences // takes the form: global-pref-path/gamename/prefs-file // this is common to win32 and Linux init now m_rc_path = g_string_new( m_global_rc_path->str ); // game sub-dir g_string_append( m_rc_path, g_pGameDescription->mGameFile.c_str() ); g_string_append( m_rc_path, "/" ); Q_mkdir( m_rc_path->str ); // then the ini file m_inipath = g_string_new( m_rc_path->str ); g_string_append( m_inipath, PREFS_LOCAL_FILENAME ); } void notebook_set_page( ui::Widget notebook, ui::Widget page ){ int pagenum = gtk_notebook_page_num( GTK_NOTEBOOK( notebook ), page ); if ( gtk_notebook_get_current_page( GTK_NOTEBOOK( notebook ) ) != pagenum ) { gtk_notebook_set_current_page( GTK_NOTEBOOK( notebook ), pagenum ); } } void PrefsDlg::showPrefPage( ui::Widget prefpage ){ notebook_set_page( m_notebook, prefpage ); return; } static void treeSelection( GtkTreeSelection* selection, gpointer data ){ PrefsDlg *dlg = (PrefsDlg*)data; GtkTreeModel* model; GtkTreeIter selected; if ( gtk_tree_selection_get_selected( selection, &model, &selected ) ) { ui::Widget prefpage; gtk_tree_model_get( model, &selected, 1, (gpointer*)&prefpage, -1 ); dlg->showPrefPage( prefpage ); } } typedef std::list PreferenceGroupCallbacks; inline void PreferenceGroupCallbacks_constructGroup( const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group ){ for ( PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i ) { ( *i )( group ); } } inline void PreferenceGroupCallbacks_pushBack( PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback ){ callbacks.push_back( callback ); } typedef std::list PreferencesPageCallbacks; inline void PreferencesPageCallbacks_constructPage( const PreferencesPageCallbacks& callbacks, PreferencesPage& page ){ for ( PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i ) { ( *i )( page ); } } inline void PreferencesPageCallbacks_pushBack( PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback ){ callbacks.push_back( callback ); } PreferencesPageCallbacks g_interfacePreferences; void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback ){ PreferencesPageCallbacks_pushBack( g_interfacePreferences, callback ); } PreferenceGroupCallbacks g_interfaceCallbacks; void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback ){ PreferenceGroupCallbacks_pushBack( g_interfaceCallbacks, callback ); } PreferencesPageCallbacks g_displayPreferences; void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback ){ PreferencesPageCallbacks_pushBack( g_displayPreferences, callback ); } PreferenceGroupCallbacks g_displayCallbacks; void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback ){ PreferenceGroupCallbacks_pushBack( g_displayCallbacks, callback ); } PreferencesPageCallbacks g_settingsPreferences; void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback ){ PreferencesPageCallbacks_pushBack( g_settingsPreferences, callback ); } PreferenceGroupCallbacks g_settingsCallbacks; void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback ){ PreferenceGroupCallbacks_pushBack( g_settingsCallbacks, callback ); } void Widget_updateDependency( ui::Widget self, ui::Widget toggleButton ){ gtk_widget_set_sensitive( self, gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( toggleButton ) ) && gtk_widget_is_sensitive( toggleButton ) ); } void ToggleButton_toggled_Widget_updateDependency( ui::Widget toggleButton, ui::Widget self ){ Widget_updateDependency( self, toggleButton ); } void ToggleButton_state_changed_Widget_updateDependency( ui::Widget toggleButton, GtkStateType state, ui::Widget self ){ if ( state == GTK_STATE_INSENSITIVE ) { Widget_updateDependency( self, toggleButton ); } } void Widget_connectToggleDependency( ui::Widget self, ui::Widget toggleButton ){ toggleButton.connect( "state_changed", G_CALLBACK( ToggleButton_state_changed_Widget_updateDependency ), self ); toggleButton.connect( "toggled", G_CALLBACK( ToggleButton_toggled_Widget_updateDependency ), self ); Widget_updateDependency( self, toggleButton ); } inline ui::Widget getVBox( ui::Widget page ){ return ui::Widget(gtk_bin_get_child( GTK_BIN( page ) )); } GtkTreeIter PreferenceTree_appendPage( GtkTreeStore* store, GtkTreeIter* parent, const char* name, ui::Widget page ){ GtkTreeIter group; gtk_tree_store_append( store, &group, parent ); gtk_tree_store_set( store, &group, 0, name, 1, page, -1 ); return group; } ui::Widget PreferencePages_addPage( ui::Widget notebook, const char* name ){ ui::Widget preflabel = ui::Label( name ); preflabel.show(); auto pageframe = ui::Frame( name ); gtk_container_set_border_width( GTK_CONTAINER( pageframe ), 4 ); pageframe.show(); ui::Widget vbox = ui::VBox( FALSE, 4 ); vbox.show(); gtk_container_set_border_width( GTK_CONTAINER( vbox ), 4 ); pageframe.add(vbox); // Add the page to the notebook gtk_notebook_append_page( GTK_NOTEBOOK( notebook ), pageframe, preflabel ); return pageframe; } class PreferenceTreeGroup : public PreferenceGroup { Dialog& m_dialog; ui::Widget m_notebook; GtkTreeStore* m_store; GtkTreeIter m_group; public: PreferenceTreeGroup( Dialog& dialog, ui::Widget notebook, GtkTreeStore* store, GtkTreeIter group ) : m_dialog( dialog ), m_notebook( notebook ), m_store( store ), m_group( group ){ } PreferencesPage createPage( const char* treeName, const char* frameName ){ ui::Widget page = PreferencePages_addPage( m_notebook, frameName ); PreferenceTree_appendPage( m_store, &m_group, treeName, page ); return PreferencesPage( m_dialog, getVBox( page ) ); } }; ui::Window PrefsDlg::BuildDialog(){ PreferencesDialog_addInterfacePreferences( FreeCaller1() ); Mouse_registerPreferencesPage(); ui::Window dialog = ui::Window(create_floating_window( "NetRadiant Preferences", m_parent )); { ui::Widget mainvbox = ui::VBox( FALSE, 5 ); dialog.add(mainvbox); gtk_container_set_border_width( GTK_CONTAINER( mainvbox ), 5 ); mainvbox.show(); { ui::Widget hbox = ui::HBox( FALSE, 5 ); hbox.show(); gtk_box_pack_end( GTK_BOX( mainvbox ), hbox, FALSE, TRUE, 0 ); { GtkButton* button = create_dialog_button( "OK", G_CALLBACK( dialog_button_ok ), &m_modal ); gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 ); } { GtkButton* button = create_dialog_button( "Cancel", G_CALLBACK( dialog_button_cancel ), &m_modal ); gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 ); } { GtkButton* button = create_dialog_button( "Clean", G_CALLBACK( OnButtonClean ), this ); gtk_box_pack_end( GTK_BOX( hbox ), GTK_WIDGET( button ), FALSE, FALSE, 0 ); } } { ui::Widget hbox = ui::HBox( FALSE, 5 ); gtk_box_pack_start( GTK_BOX( mainvbox ), hbox, TRUE, TRUE, 0 ); hbox.show(); { auto sc_win = ui::ScrolledWindow(); gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( sc_win ), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC ); gtk_box_pack_start( GTK_BOX( hbox ), sc_win, FALSE, FALSE, 0 ); sc_win.show(); gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW( sc_win ), GTK_SHADOW_IN ); // prefs pages notebook m_notebook = ui::Widget(gtk_notebook_new()); // hide the notebook tabs since its not supposed to look like a notebook gtk_notebook_set_show_tabs( GTK_NOTEBOOK( m_notebook ), FALSE ); gtk_box_pack_start( GTK_BOX( hbox ), m_notebook, TRUE, TRUE, 0 ); m_notebook.show(); { auto store = gtk_tree_store_new( 2, G_TYPE_STRING, G_TYPE_POINTER ); ui::Widget view = ui::TreeView(ui::TreeModel( GTK_TREE_MODEL( store ) )); gtk_tree_view_set_headers_visible( GTK_TREE_VIEW( view ), FALSE ); { auto renderer = ui::CellRendererText(); GtkTreeViewColumn* column = ui::TreeViewColumn( "Preferences", renderer, {{"text", 0}} ); gtk_tree_view_append_column( GTK_TREE_VIEW( view ), column ); } { auto selection = ui::TreeSelection(gtk_tree_view_get_selection( GTK_TREE_VIEW( view ) )); selection.connect( "changed", G_CALLBACK( treeSelection ), this ); } view.show(); sc_win.add(view); { /********************************************************************/ /* Add preference tree options */ /********************************************************************/ // Front page... //GtkWidget* front = PreferencePages_addPage( m_notebook, "Front Page" ); { ui::Widget global = PreferencePages_addPage( m_notebook, "Global Preferences" ); { PreferencesPage preferencesPage( *this, getVBox( global ) ); Global_constructPreferences( preferencesPage ); } GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Global", global ); { ui::Widget game = PreferencePages_addPage( m_notebook, "Game" ); PreferencesPage preferencesPage( *this, getVBox( game ) ); g_GamesDialog.CreateGlobalFrame( preferencesPage ); PreferenceTree_appendPage( store, &group, "Game", game ); } } { ui::Widget interfacePage = PreferencePages_addPage( m_notebook, "Interface Preferences" ); { PreferencesPage preferencesPage( *this, getVBox( interfacePage ) ); PreferencesPageCallbacks_constructPage( g_interfacePreferences, preferencesPage ); } GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Interface", interfacePage ); PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group ); PreferenceGroupCallbacks_constructGroup( g_interfaceCallbacks, preferenceGroup ); } { ui::Widget display = PreferencePages_addPage( m_notebook, "Display Preferences" ); { PreferencesPage preferencesPage( *this, getVBox( display ) ); PreferencesPageCallbacks_constructPage( g_displayPreferences, preferencesPage ); } GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Display", display ); PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group ); PreferenceGroupCallbacks_constructGroup( g_displayCallbacks, preferenceGroup ); } { ui::Widget settings = PreferencePages_addPage( m_notebook, "General Settings" ); { PreferencesPage preferencesPage( *this, getVBox( settings ) ); PreferencesPageCallbacks_constructPage( g_settingsPreferences, preferencesPage ); } GtkTreeIter group = PreferenceTree_appendPage( store, 0, "Settings", settings ); PreferenceTreeGroup preferenceGroup( *this, m_notebook, store, group ); PreferenceGroupCallbacks_constructGroup( g_settingsCallbacks, preferenceGroup ); } } gtk_tree_view_expand_all( GTK_TREE_VIEW( view ) ); g_object_unref( G_OBJECT( store ) ); } } } } gtk_notebook_set_current_page( GTK_NOTEBOOK( m_notebook ), 0 ); return dialog; } preferences_globals_t g_preferences_globals; PrefsDlg g_Preferences; // global prefs instance void PreferencesDialog_constructWindow( ui::Window main_window ){ g_Preferences.m_parent = main_window; g_Preferences.Create(); } void PreferencesDialog_destroyWindow(){ g_Preferences.Destroy(); } PreferenceDictionary g_preferences; PreferenceSystem& GetPreferenceSystem(){ return g_preferences; } class PreferenceSystemAPI { PreferenceSystem* m_preferencesystem; public: typedef PreferenceSystem Type; STRING_CONSTANT( Name, "*" ); PreferenceSystemAPI(){ m_preferencesystem = &GetPreferenceSystem(); } PreferenceSystem* getTable(){ return m_preferencesystem; } }; #include "modulesystem/singletonmodule.h" #include "modulesystem/moduleregistry.h" typedef SingletonModule PreferenceSystemModule; typedef Static StaticPreferenceSystemModule; StaticRegisterModule staticRegisterPreferenceSystem( StaticPreferenceSystemModule::instance() ); void Preferences_Load(){ g_GamesDialog.LoadPrefs(); globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n"; if ( !Preferences_Load( g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str() ) ) { globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n"; } } void Preferences_Save(){ if ( g_preferences_globals.disable_ini ) { return; } g_GamesDialog.SavePrefs(); globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n"; if ( !Preferences_Save_Safe( g_preferences, g_Preferences.m_inipath->str ) ) { globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n"; } } void Preferences_Reset(){ file_remove( g_Preferences.m_inipath->str ); } void PrefsDlg::PostModal( EMessageBoxReturn code ){ if ( code == eIDOK ) { Preferences_Save(); UpdateAllWindows(); } } std::vector g_restart_required; void PreferencesDialog_restartRequired( const char* staticName ){ g_restart_required.push_back( staticName ); } void PreferencesDialog_showDialog(){ if ( ConfirmModified( "Edit Preferences" ) && g_Preferences.DoModal() == eIDOK ) { if ( !g_restart_required.empty() ) { StringOutputStream message( 256 ); message << "Preference changes require a restart:\n"; for ( std::vector::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i ) { message << ( *i ) << '\n'; } MainFrame_getWindow().alert( message.c_str() ); g_restart_required.clear(); } } } void GameName_importString( const char* value ){ gamename_set( value ); } typedef FreeCaller1 GameNameImportStringCaller; void GameName_exportString( const StringImportCallback& importer ){ importer( gamename_get() ); } typedef FreeCaller1 GameNameExportStringCaller; void GameMode_importString( const char* value ){ gamemode_set( value ); } typedef FreeCaller1 GameModeImportStringCaller; void GameMode_exportString( const StringImportCallback& importer ){ importer( gamemode_get() ); } typedef FreeCaller1 GameModeExportStringCaller; void RegisterPreferences( PreferenceSystem& preferences ){ #ifdef WIN32 preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useWin32Editor ), BoolExportStringCaller( g_TextEditor_useWin32Editor ) ); #else preferences.registerPreference( "UseCustomShaderEditor", BoolImportStringCaller( g_TextEditor_useCustomEditor ), BoolExportStringCaller( g_TextEditor_useCustomEditor ) ); preferences.registerPreference( "CustomShaderEditorCommand", CopiedStringImportStringCaller( g_TextEditor_editorCommand ), CopiedStringExportStringCaller( g_TextEditor_editorCommand ) ); #endif preferences.registerPreference( "GameName", GameNameImportStringCaller(), GameNameExportStringCaller() ); preferences.registerPreference( "GameMode", GameModeImportStringCaller(), GameModeExportStringCaller() ); } void Preferences_Init(){ RegisterPreferences( GetPreferenceSystem() ); }