/* 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 "debugging/debugging.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 { GtkWidget* use_custom = page.appendCheckBox("Text Editor", "Custom", g_TextEditor_useCustomEditor); GtkWidget* 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 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) { TextFileInputStream file(filename); if(!file.failed()) { XMLStreamParser parser(file); XMLPreferenceDictionaryImporter importer(preferences, PREFERENCES_VERSION); parser.exportXML(importer); return true; } return false; } 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())) { 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; imGameFile; } 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); } GtkWindow* CGameDialog::BuildDialog() { GtkFrame* frame = create_dialog_frame("Game settings", GTK_SHADOW_ETCHED_IN); GtkVBox* vbox2 = create_dialog_vbox(0, 4); gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox2)); { PreferencesPage preferencesPage(*this, GTK_WIDGET(vbox2)); Global_constructPreferences(preferencesPage); CreateGlobalFrame(preferencesPage); } return create_simple_modal_dialog_window("Global Preferences", m_modal, GTK_WIDGET(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"); } 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() != 0) { Destroy(); } } inline const char* GameDescription_getIdentifier(const CGameDescription& gameDescription) { const char* identifier = gameDescription.getKeyValue("index"); if(string_empty(identifier)) { identifier = "1"; } return identifier; } void CGameDialog::AddPacksURL(StringOutputStream &URL) { // add the URLs for the list of game packs installed // FIXME: this is kinda hardcoded for now.. std::list::iterator iGame; for(iGame=mGames.begin(); iGame!=mGames.end(); ++iGame) { URL << "&Games_dlup%5B%5D=" << GameDescription_getIdentifier(*(*iGame)); } } CGameDialog g_GamesDialog; // ============================================================================= // Widget callbacks for PrefsDlg static void OnButtonClean (GtkWidget *widget, gpointer data) { // make sure this is what the user wants if (gtk_MessageBox(GTK_WIDGET(g_Preferences.GetWidget()), "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", eMB_YESNO, eMB_ICONASTERISK) == eIDYES) { 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(GtkWidget* notebook, GtkWidget* 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(GtkWidget* 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)) { GtkWidget* 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(GtkWidget* self, GtkWidget* 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(GtkWidget *toggleButton, GtkWidget* self) { Widget_updateDependency(self, toggleButton); } void ToggleButton_state_changed_Widget_updateDependency(GtkWidget* toggleButton, GtkStateType state, GtkWidget* self) { if(state == GTK_STATE_INSENSITIVE) { Widget_updateDependency(self, toggleButton); } } void Widget_connectToggleDependency(GtkWidget* self, GtkWidget* toggleButton) { g_signal_connect(G_OBJECT(toggleButton), "state_changed", G_CALLBACK(ToggleButton_state_changed_Widget_updateDependency), self); g_signal_connect(G_OBJECT(toggleButton), "toggled", G_CALLBACK(ToggleButton_toggled_Widget_updateDependency), self); Widget_updateDependency(self, toggleButton); } inline GtkWidget* getVBox(GtkWidget* page) { return gtk_bin_get_child(GTK_BIN(page)); } GtkTreeIter PreferenceTree_appendPage(GtkTreeStore* store, GtkTreeIter* parent, const char* name, GtkWidget* page) { GtkTreeIter group; gtk_tree_store_append(store, &group, parent); gtk_tree_store_set(store, &group, 0, name, 1, page, -1); return group; } GtkWidget* PreferencePages_addPage(GtkWidget* notebook, const char* name) { GtkWidget* preflabel = gtk_label_new(name); gtk_widget_show(preflabel); GtkWidget* pageframe = gtk_frame_new(name); gtk_container_set_border_width(GTK_CONTAINER(pageframe), 4); gtk_widget_show(pageframe); GtkWidget* vbox = gtk_vbox_new(FALSE, 4); gtk_widget_show(vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), 4); gtk_container_add(GTK_CONTAINER(pageframe), 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; GtkWidget* m_notebook; GtkTreeStore* m_store; GtkTreeIter m_group; public: PreferenceTreeGroup(Dialog& dialog, GtkWidget* 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) { GtkWidget* page = PreferencePages_addPage(m_notebook, frameName); PreferenceTree_appendPage(m_store, &m_group, treeName, page); return PreferencesPage(m_dialog, getVBox(page)); } }; GtkWindow* PrefsDlg::BuildDialog() { PreferencesDialog_addInterfacePreferences(FreeCaller1()); Mouse_registerPreferencesPage(); GtkWindow* dialog = create_floating_window("NetRadiant Preferences", m_parent); { GtkWidget* mainvbox = gtk_vbox_new(FALSE, 5); gtk_container_add(GTK_CONTAINER(dialog), mainvbox); gtk_container_set_border_width(GTK_CONTAINER(mainvbox), 5); gtk_widget_show(mainvbox); { GtkWidget* hbox = gtk_hbox_new(FALSE, 5); gtk_widget_show(hbox); 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); } } { GtkWidget* hbox = gtk_hbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(mainvbox), hbox, TRUE, TRUE, 0); gtk_widget_show(hbox); { GtkWidget* sc_win = gtk_scrolled_window_new(0, 0); 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); gtk_widget_show(sc_win); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sc_win), GTK_SHADOW_IN); // prefs pages notebook m_notebook = 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); gtk_widget_show(m_notebook); { GtkTreeStore* store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER); GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); { GtkCellRenderer* renderer = gtk_cell_renderer_text_new(); GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Preferences", renderer, "text", 0, 0); gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); } { GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(treeSelection), this); } gtk_widget_show(view); gtk_container_add(GTK_CONTAINER (sc_win), view); { /********************************************************************/ /* Add preference tree options */ /********************************************************************/ // Front page... //GtkWidget* front = PreferencePages_addPage(m_notebook, "Front Page"); { GtkWidget* global = PreferencePages_addPage(m_notebook, "Global Preferences"); { PreferencesPage preferencesPage(*this, getVBox(global)); Global_constructPreferences(preferencesPage); } GtkTreeIter group = PreferenceTree_appendPage(store, 0, "Global", global); { GtkWidget* game = PreferencePages_addPage(m_notebook, "Game"); PreferencesPage preferencesPage(*this, getVBox(game)); g_GamesDialog.CreateGlobalFrame(preferencesPage); PreferenceTree_appendPage(store, &group, "Game", game); } } { GtkWidget* 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); } { GtkWidget* 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); } { GtkWidget* 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_page(GTK_NOTEBOOK(m_notebook), 0); return dialog; } preferences_globals_t g_preferences_globals; PrefsDlg g_Preferences; // global prefs instance void PreferencesDialog_constructWindow(GtkWindow* 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)) { 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'; } gtk_MessageBox(GTK_WIDGET(MainFrame_getWindow()), 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()); }