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)
70 page.appendCheckBox("Console", "Enable Logging", g_Console_enableLogging);
73 void Interface_constructPreferences(PreferencesPage& page)
76 page.appendCheckBox("", "Default Text Editor", g_TextEditor_useWin32Editor);
79 GtkWidget* use_custom = page.appendCheckBox("Text Editor", "Custom", g_TextEditor_useCustomEditor);
80 GtkWidget* custom_editor = page.appendPathEntry("Text Editor Command", g_TextEditor_editorCommand, true);
81 Widget_connectToggleDependency(custom_editor, use_custom);
86 void Mouse_constructPreferences(PreferencesPage& page)
89 const char* buttons[] = { "2 button", "3 button", };
90 page.appendRadio("Mouse Type", g_glwindow_globals.m_nMouseType, STRING_ARRAY_RANGE(buttons));
92 page.appendCheckBox("Right Button", "Activates Context Menu", g_xywindow_globals.m_bRightClick);
94 void Mouse_constructPage(PreferenceGroup& group)
96 PreferencesPage page(group.createPage("Mouse", "Mouse Preferences"));
97 Mouse_constructPreferences(page);
99 void Mouse_registerPreferencesPage()
101 PreferencesDialog_addInterfacePage(FreeCaller1<PreferenceGroup&, Mouse_constructPage>());
106 =========================================================
107 Games selection dialog
108 =========================================================
113 inline const char* xmlAttr_getName(xmlAttrPtr attr)
115 return reinterpret_cast<const char*>(attr->name);
118 inline const char* xmlAttr_getValue(xmlAttrPtr attr)
120 return reinterpret_cast<const char*>(attr->children->content);
123 CGameDescription::CGameDescription(xmlDocPtr pDoc, const CopiedString& gameFile)
125 // read the user-friendly game name
126 xmlNodePtr pNode = pDoc->children;
128 while (strcmp((const char*)pNode->name, "game") && pNode != 0)
134 Error("Didn't find 'game' node in the game description file '%s'\n", pDoc->URL);
137 for(xmlAttrPtr attr = pNode->properties; attr != 0; attr = attr->next)
139 m_gameDescription.insert(GameDescription::value_type(xmlAttr_getName(attr), xmlAttr_getValue(attr)));
143 StringOutputStream path(256);
144 path << AppPath_get() << gameFile.c_str() << "/";
145 mGameToolsPath = path.c_str();
148 ASSERT_MESSAGE(file_exists(mGameToolsPath.c_str()), "game directory not found: " << makeQuoted(mGameToolsPath.c_str()));
150 mGameFile = gameFile;
153 GameDescription::iterator i = m_gameDescription.find("type");
154 if(i == m_gameDescription.end())
156 globalErrorStream() << "Warning, 'type' attribute not found in '" << reinterpret_cast<const char*>(pDoc->URL) << "'\n";
162 mGameType = (*i).second.c_str();
167 void CGameDescription::Dump()
169 globalOutputStream() << "game description file: " << makeQuoted(mGameFile.c_str()) << "\n";
170 for(GameDescription::iterator i = m_gameDescription.begin(); i != m_gameDescription.end(); ++i)
172 globalOutputStream() << (*i).first.c_str() << " = " << makeQuoted((*i).second.c_str()) << "\n";
176 CGameDescription *g_pGameDescription; ///< shortcut to g_GamesDialog.m_pCurrentDescription
179 #include "warnings.h"
180 #include "stream/textfilestream.h"
181 #include "container/array.h"
182 #include "xml/ixml.h"
183 #include "xml/xmlparser.h"
184 #include "xml/xmlwriter.h"
186 #include "preferencedictionary.h"
187 #include "stringio.h"
189 const char* const PREFERENCES_VERSION = "1.0";
191 bool Preferences_Load(PreferenceDictionary& preferences, const char* filename, const char *cmdline_prefix)
194 TextFileInputStream file(filename);
197 XMLStreamParser parser(file);
198 XMLPreferenceDictionaryImporter importer(preferences, PREFERENCES_VERSION);
199 parser.exportXML(importer);
203 int l = strlen(cmdline_prefix);
204 for(int i = 1; i < g_argc - 1; ++i)
206 if(g_argv[i][0] == '-')
208 if(!strncmp(g_argv[i]+1, cmdline_prefix, l))
209 if(g_argv[i][l+1] == '-')
210 preferences.importPref(g_argv[i]+l+2, g_argv[i+1]);
218 bool Preferences_Save(PreferenceDictionary& preferences, const char* filename)
220 TextFileOutputStream file(filename);
223 XMLStreamWriter writer(file);
224 XMLPreferenceDictionaryExporter exporter(preferences, PREFERENCES_VERSION);
225 exporter.exportXML(writer);
231 bool Preferences_Save_Safe(PreferenceDictionary& preferences, const char* filename)
233 Array<char> tmpName(filename, filename + strlen(filename) + 1 + 3);
234 *(tmpName.end() - 4) = 'T';
235 *(tmpName.end() - 3) = 'M';
236 *(tmpName.end() - 2) = 'P';
237 *(tmpName.end() - 1) = '\0';
239 return Preferences_Save(preferences, tmpName.data())
240 && (!file_exists(filename) || file_remove(filename))
241 && file_move(tmpName.data(), filename);
246 void LogConsole_importString(const char* string)
248 g_Console_enableLogging = string_equal(string, "true");
249 Sys_LogFile(g_Console_enableLogging);
251 typedef FreeCaller1<const char*, LogConsole_importString> LogConsoleImportStringCaller;
254 void RegisterGlobalPreferences(PreferenceSystem& preferences)
256 preferences.registerPreference("gamefile", CopiedStringImportStringCaller(g_GamesDialog.m_sGameFile), CopiedStringExportStringCaller(g_GamesDialog.m_sGameFile));
257 preferences.registerPreference("gamePrompt", BoolImportStringCaller(g_GamesDialog.m_bGamePrompt), BoolExportStringCaller(g_GamesDialog.m_bGamePrompt));
258 preferences.registerPreference("log console", LogConsoleImportStringCaller(), BoolExportStringCaller(g_Console_enableLogging));
262 PreferenceDictionary g_global_preferences;
264 void GlobalPreferences_Init()
266 RegisterGlobalPreferences(g_global_preferences);
269 void CGameDialog::LoadPrefs()
271 // load global .pref file
272 StringOutputStream strGlobalPref(256);
273 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
275 globalOutputStream() << "loading global preferences from " << makeQuoted(strGlobalPref.c_str()) << "\n";
277 if(!Preferences_Load(g_global_preferences, strGlobalPref.c_str(), "global"))
279 globalOutputStream() << "failed to load global preferences from " << strGlobalPref.c_str() << "\n";
283 void CGameDialog::SavePrefs()
285 StringOutputStream strGlobalPref(256);
286 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
288 globalOutputStream() << "saving global preferences to " << strGlobalPref.c_str() << "\n";
290 if(!Preferences_Save_Safe(g_global_preferences, strGlobalPref.c_str()))
292 globalOutputStream() << "failed to save global preferences to " << strGlobalPref.c_str() << "\n";
296 void CGameDialog::DoGameDialog()
301 // we save the prefs file
305 void CGameDialog::GameFileImport(int value)
307 m_nComboSelect = value;
308 // use value to set m_sGameFile
309 std::list<CGameDescription *>::iterator iGame = mGames.begin();
311 for(i=0; i<value; i++)
315 m_sGameFile = (*iGame)->mGameFile;
318 void CGameDialog::GameFileExport(const IntImportCallback& importCallback) const
320 // use m_sGameFile to set value
321 std::list<CGameDescription *>::const_iterator iGame;
323 for(iGame=mGames.begin(); iGame!=mGames.end(); ++iGame)
325 if ((*iGame)->mGameFile == m_sGameFile)
332 importCallback(m_nComboSelect);
335 void CGameDialog_GameFileImport(CGameDialog& self, int value)
337 self.GameFileImport(value);
340 void CGameDialog_GameFileExport(CGameDialog& self, const IntImportCallback& importCallback)
342 self.GameFileExport(importCallback);
345 void CGameDialog::CreateGlobalFrame(PreferencesPage& page)
347 std::vector<const char*> games;
348 games.reserve(mGames.size());
349 for(std::list<CGameDescription *>::iterator i = mGames.begin(); i != mGames.end(); ++i)
351 games.push_back((*i)->getRequiredKeyValue("name"));
355 StringArrayRange(&(*games.begin()), &(*games.end())),
356 ReferenceCaller1<CGameDialog, int, CGameDialog_GameFileImport>(*this),
357 ReferenceCaller1<CGameDialog, const IntImportCallback&, CGameDialog_GameFileExport>(*this)
359 page.appendCheckBox("Startup", "Show Global Preferences", m_bGamePrompt);
362 GtkWindow* CGameDialog::BuildDialog()
364 GtkFrame* frame = create_dialog_frame("Game settings", GTK_SHADOW_ETCHED_IN);
366 GtkVBox* vbox2 = create_dialog_vbox(0, 4);
367 gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(vbox2));
370 PreferencesPage preferencesPage(*this, GTK_WIDGET(vbox2));
371 Global_constructPreferences(preferencesPage);
372 CreateGlobalFrame(preferencesPage);
375 return create_simple_modal_dialog_window("Global Preferences", m_modal, GTK_WIDGET(frame));
380 std::list<CGameDescription*>& mGames;
383 LoadGameFile(std::list<CGameDescription*>& games, const char* path) : mGames(games), mPath(path)
386 void operator()(const char* name) const
388 if(!extension_equal(path_get_extension(name), "game"))
392 StringOutputStream strPath(256);
393 strPath << mPath << name;
394 globalOutputStream() << strPath.c_str() << '\n';
396 xmlDocPtr pDoc = xmlParseFile(strPath.c_str());
399 mGames.push_front(new CGameDescription(pDoc, name));
404 globalErrorStream() << "XML parser failed on '" << strPath.c_str() << "'\n";
409 void CGameDialog::ScanForGames()
411 StringOutputStream strGamesPath(256);
412 strGamesPath << AppPath_get() << "games/";
413 const char *path = strGamesPath.c_str();
415 globalOutputStream() << "Scanning for game description files: " << path << '\n';
419 do we put game description files below AppPath, or in ~/.radiant
420 i.e. read only or read/write?
421 my guess .. readonly cause it's an install
422 we will probably want to add ~/.radiant/<version>/games/ scanning on top of that for developers
423 (if that's really needed)
426 Directory_forEach(path, LoadGameFile(mGames, path));
429 CGameDescription* CGameDialog::GameDescriptionForComboItem()
431 std::list<CGameDescription *>::iterator iGame;
433 for(iGame=mGames.begin(); iGame!=mGames.end(); ++iGame,i++)
435 if (i == m_nComboSelect)
440 return 0; // not found
443 void CGameDialog::InitGlobalPrefPath()
445 g_Preferences.m_global_rc_path = g_string_new(SettingsPath_get());
448 void CGameDialog::Reset()
450 if (!g_Preferences.m_global_rc_path)
451 InitGlobalPrefPath();
452 StringOutputStream strGlobalPref(256);
453 strGlobalPref << g_Preferences.m_global_rc_path->str << "global.pref";
454 file_remove(strGlobalPref.c_str());
457 void CGameDialog::Init()
459 InitGlobalPrefPath();
464 Error("Didn't find any valid game file descriptions, aborting\n");
468 std::list<CGameDescription *>::iterator iGame, iPrevGame;
469 for(iGame=mGames.begin(), iPrevGame = mGames.end(); iGame!=mGames.end(); iPrevGame = iGame, ++iGame)
471 if(iPrevGame != mGames.end())
472 if(strcmp((*iGame)->getRequiredKeyValue("name"), (*iPrevGame)->getRequiredKeyValue("name")) < 0)
474 CGameDescription *h = *iGame;
481 CGameDescription* currentGameDescription = 0;
485 // search by .game name
486 std::list<CGameDescription *>::iterator iGame;
487 for(iGame=mGames.begin(); iGame!=mGames.end(); ++iGame)
489 if ((*iGame)->mGameFile == m_sGameFile)
491 currentGameDescription = (*iGame);
496 if (m_bGamePrompt || !currentGameDescription)
500 // use m_nComboSelect to identify the game to run as and set the globals
501 currentGameDescription = GameDescriptionForComboItem();
502 ASSERT_NOTNULL(currentGameDescription);
504 g_pGameDescription = currentGameDescription;
506 g_pGameDescription->Dump();
509 CGameDialog::~CGameDialog()
511 // free all the game descriptions
512 std::list<CGameDescription *>::iterator iGame;
513 for(iGame=mGames.begin(); iGame!=mGames.end(); ++iGame)
524 inline const char* GameDescription_getIdentifier(const CGameDescription& gameDescription)
526 const char* identifier = gameDescription.getKeyValue("index");
527 if(string_empty(identifier))
534 void CGameDialog::AddPacksURL(StringOutputStream &URL)
536 // add the URLs for the list of game packs installed
537 // FIXME: this is kinda hardcoded for now..
538 std::list<CGameDescription *>::iterator iGame;
539 for(iGame=mGames.begin(); iGame!=mGames.end(); ++iGame)
541 URL << "&Games_dlup%5B%5D=" << GameDescription_getIdentifier(*(*iGame));
545 CGameDialog g_GamesDialog;
548 // =============================================================================
549 // Widget callbacks for PrefsDlg
551 static void OnButtonClean (GtkWidget *widget, gpointer data)
553 // make sure this is what the user wants
554 if (gtk_MessageBox(GTK_WIDGET(g_Preferences.GetWidget()), "This will close Radiant and clean the corresponding registry entries.\n"
555 "Next time you start Radiant it will be good as new. Do you wish to continue?",
556 "Reset Registry", eMB_YESNO, eMB_ICONASTERISK) == eIDYES)
558 PrefsDlg *dlg = (PrefsDlg*)data;
559 dlg->EndModal (eIDCANCEL);
561 g_preferences_globals.disable_ini = true;
567 // =============================================================================
573 very first prefs init deals with selecting the game and the game tools path
574 then we can load .ini stuff
576 using prefs / ini settings:
579 look in ~/.radiant/<version>/gamename
583 #define PREFS_LOCAL_FILENAME "local.pref"
585 void PrefsDlg::Init()
587 // m_global_rc_path has been set above
588 // m_rc_path is for game specific preferences
589 // takes the form: global-pref-path/gamename/prefs-file
591 // this is common to win32 and Linux init now
592 m_rc_path = g_string_new (m_global_rc_path->str);
595 g_string_append (m_rc_path, g_pGameDescription->mGameFile.c_str());
596 g_string_append (m_rc_path, "/");
597 Q_mkdir (m_rc_path->str);
600 m_inipath = g_string_new (m_rc_path->str);
601 g_string_append (m_inipath, PREFS_LOCAL_FILENAME);
604 void notebook_set_page(GtkWidget* notebook, GtkWidget* page)
606 int pagenum = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), page);
607 if(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook)) != pagenum)
609 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), pagenum);
613 void PrefsDlg::showPrefPage(GtkWidget* prefpage)
615 notebook_set_page(m_notebook, prefpage);
619 static void treeSelection(GtkTreeSelection* selection, gpointer data)
621 PrefsDlg *dlg = (PrefsDlg*)data;
624 GtkTreeIter selected;
625 if(gtk_tree_selection_get_selected(selection, &model, &selected))
628 gtk_tree_model_get(model, &selected, 1, (gpointer*)&prefpage, -1);
629 dlg->showPrefPage(prefpage);
633 typedef std::list<PreferenceGroupCallback> PreferenceGroupCallbacks;
635 inline void PreferenceGroupCallbacks_constructGroup(const PreferenceGroupCallbacks& callbacks, PreferenceGroup& group)
637 for(PreferenceGroupCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i)
644 inline void PreferenceGroupCallbacks_pushBack(PreferenceGroupCallbacks& callbacks, const PreferenceGroupCallback& callback)
646 callbacks.push_back(callback);
649 typedef std::list<PreferencesPageCallback> PreferencesPageCallbacks;
651 inline void PreferencesPageCallbacks_constructPage(const PreferencesPageCallbacks& callbacks, PreferencesPage& page)
653 for(PreferencesPageCallbacks::const_iterator i = callbacks.begin(); i != callbacks.end(); ++i)
659 inline void PreferencesPageCallbacks_pushBack(PreferencesPageCallbacks& callbacks, const PreferencesPageCallback& callback)
661 callbacks.push_back(callback);
664 PreferencesPageCallbacks g_interfacePreferences;
665 void PreferencesDialog_addInterfacePreferences(const PreferencesPageCallback& callback)
667 PreferencesPageCallbacks_pushBack(g_interfacePreferences, callback);
669 PreferenceGroupCallbacks g_interfaceCallbacks;
670 void PreferencesDialog_addInterfacePage(const PreferenceGroupCallback& callback)
672 PreferenceGroupCallbacks_pushBack(g_interfaceCallbacks, callback);
675 PreferencesPageCallbacks g_displayPreferences;
676 void PreferencesDialog_addDisplayPreferences(const PreferencesPageCallback& callback)
678 PreferencesPageCallbacks_pushBack(g_displayPreferences, callback);
680 PreferenceGroupCallbacks g_displayCallbacks;
681 void PreferencesDialog_addDisplayPage(const PreferenceGroupCallback& callback)
683 PreferenceGroupCallbacks_pushBack(g_displayCallbacks, callback);
686 PreferencesPageCallbacks g_settingsPreferences;
687 void PreferencesDialog_addSettingsPreferences(const PreferencesPageCallback& callback)
689 PreferencesPageCallbacks_pushBack(g_settingsPreferences, callback);
691 PreferenceGroupCallbacks g_settingsCallbacks;
692 void PreferencesDialog_addSettingsPage(const PreferenceGroupCallback& callback)
694 PreferenceGroupCallbacks_pushBack(g_settingsCallbacks, callback);
697 void Widget_updateDependency(GtkWidget* self, GtkWidget* toggleButton)
699 gtk_widget_set_sensitive(self, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggleButton)) && GTK_WIDGET_IS_SENSITIVE(toggleButton));
702 void ToggleButton_toggled_Widget_updateDependency(GtkWidget *toggleButton, GtkWidget* self)
704 Widget_updateDependency(self, toggleButton);
707 void ToggleButton_state_changed_Widget_updateDependency(GtkWidget* toggleButton, GtkStateType state, GtkWidget* self)
709 if(state == GTK_STATE_INSENSITIVE)
711 Widget_updateDependency(self, toggleButton);
715 void Widget_connectToggleDependency(GtkWidget* self, GtkWidget* toggleButton)
717 g_signal_connect(G_OBJECT(toggleButton), "state_changed", G_CALLBACK(ToggleButton_state_changed_Widget_updateDependency), self);
718 g_signal_connect(G_OBJECT(toggleButton), "toggled", G_CALLBACK(ToggleButton_toggled_Widget_updateDependency), self);
719 Widget_updateDependency(self, toggleButton);
723 inline GtkWidget* getVBox(GtkWidget* page)
725 return gtk_bin_get_child(GTK_BIN(page));
728 GtkTreeIter PreferenceTree_appendPage(GtkTreeStore* store, GtkTreeIter* parent, const char* name, GtkWidget* page)
731 gtk_tree_store_append(store, &group, parent);
732 gtk_tree_store_set(store, &group, 0, name, 1, page, -1);
736 GtkWidget* PreferencePages_addPage(GtkWidget* notebook, const char* name)
738 GtkWidget* preflabel = gtk_label_new(name);
739 gtk_widget_show(preflabel);
741 GtkWidget* pageframe = gtk_frame_new(name);
742 gtk_container_set_border_width(GTK_CONTAINER(pageframe), 4);
743 gtk_widget_show(pageframe);
745 GtkWidget* vbox = gtk_vbox_new(FALSE, 4);
746 gtk_widget_show(vbox);
747 gtk_container_set_border_width(GTK_CONTAINER(vbox), 4);
748 gtk_container_add(GTK_CONTAINER(pageframe), vbox);
750 // Add the page to the notebook
751 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), pageframe, preflabel);
756 class PreferenceTreeGroup : public PreferenceGroup
759 GtkWidget* m_notebook;
760 GtkTreeStore* m_store;
763 PreferenceTreeGroup(Dialog& dialog, GtkWidget* notebook, GtkTreeStore* store, GtkTreeIter group) :
765 m_notebook(notebook),
770 PreferencesPage createPage(const char* treeName, const char* frameName)
772 GtkWidget* page = PreferencePages_addPage(m_notebook, frameName);
773 PreferenceTree_appendPage(m_store, &m_group, treeName, page);
774 return PreferencesPage(m_dialog, getVBox(page));
778 GtkWindow* PrefsDlg::BuildDialog()
780 PreferencesDialog_addInterfacePreferences(FreeCaller1<PreferencesPage&, Interface_constructPreferences>());
781 Mouse_registerPreferencesPage();
783 GtkWindow* dialog = create_floating_window("NetRadiant Preferences", m_parent);
786 GtkWidget* mainvbox = gtk_vbox_new(FALSE, 5);
787 gtk_container_add(GTK_CONTAINER(dialog), mainvbox);
788 gtk_container_set_border_width(GTK_CONTAINER(mainvbox), 5);
789 gtk_widget_show(mainvbox);
792 GtkWidget* hbox = gtk_hbox_new(FALSE, 5);
793 gtk_widget_show(hbox);
794 gtk_box_pack_end(GTK_BOX(mainvbox), hbox, FALSE, TRUE, 0);
797 GtkButton* button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &m_modal);
798 gtk_box_pack_end(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
801 GtkButton* button = create_dialog_button("Cancel", G_CALLBACK(dialog_button_cancel), &m_modal);
802 gtk_box_pack_end(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
805 GtkButton* button = create_dialog_button("Clean", G_CALLBACK(OnButtonClean), this);
806 gtk_box_pack_end(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0);
811 GtkWidget* hbox = gtk_hbox_new(FALSE, 5);
812 gtk_box_pack_start(GTK_BOX(mainvbox), hbox, TRUE, TRUE, 0);
813 gtk_widget_show(hbox);
816 GtkWidget* sc_win = gtk_scrolled_window_new(0, 0);
817 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sc_win), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
818 gtk_box_pack_start(GTK_BOX(hbox), sc_win, FALSE, FALSE, 0);
819 gtk_widget_show(sc_win);
820 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sc_win), GTK_SHADOW_IN);
822 // prefs pages notebook
823 m_notebook = gtk_notebook_new();
824 // hide the notebook tabs since its not supposed to look like a notebook
825 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(m_notebook), FALSE);
826 gtk_box_pack_start(GTK_BOX(hbox), m_notebook, TRUE, TRUE, 0);
827 gtk_widget_show(m_notebook);
831 GtkTreeStore* store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
833 GtkWidget* view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
834 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
837 GtkCellRenderer* renderer = gtk_cell_renderer_text_new();
838 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes("Preferences", renderer, "text", 0, NULL);
839 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
843 GtkTreeSelection* selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
844 g_signal_connect(G_OBJECT(selection), "changed", G_CALLBACK(treeSelection), this);
847 gtk_widget_show(view);
849 gtk_container_add(GTK_CONTAINER (sc_win), view);
852 /********************************************************************/
853 /* Add preference tree options */
854 /********************************************************************/
857 PreferencePages_addPage(m_notebook, "Front Page");
860 GtkWidget* global = PreferencePages_addPage(m_notebook, "Global Preferences");
862 PreferencesPage preferencesPage(*this, getVBox(global));
863 Global_constructPreferences(preferencesPage);
865 GtkTreeIter group = PreferenceTree_appendPage(store, 0, "Global", global);
867 GtkWidget* game = PreferencePages_addPage(m_notebook, "Game");
868 PreferencesPage preferencesPage(*this, getVBox(game));
869 g_GamesDialog.CreateGlobalFrame(preferencesPage);
871 PreferenceTree_appendPage(store, &group, "Game", game);
876 GtkWidget* interfacePage = PreferencePages_addPage(m_notebook, "Interface Preferences");
878 PreferencesPage preferencesPage(*this, getVBox(interfacePage));
879 PreferencesPageCallbacks_constructPage(g_interfacePreferences, preferencesPage);
882 GtkTreeIter group = PreferenceTree_appendPage(store, 0, "Interface", interfacePage);
883 PreferenceTreeGroup preferenceGroup(*this, m_notebook, store, group);
885 PreferenceGroupCallbacks_constructGroup(g_interfaceCallbacks, preferenceGroup);
889 GtkWidget* display = PreferencePages_addPage(m_notebook, "Display Preferences");
891 PreferencesPage preferencesPage(*this, getVBox(display));
892 PreferencesPageCallbacks_constructPage(g_displayPreferences, preferencesPage);
894 GtkTreeIter group = PreferenceTree_appendPage(store, 0, "Display", display);
895 PreferenceTreeGroup preferenceGroup(*this, m_notebook, store, group);
897 PreferenceGroupCallbacks_constructGroup(g_displayCallbacks, preferenceGroup);
901 GtkWidget* settings = PreferencePages_addPage(m_notebook, "General Settings");
903 PreferencesPage preferencesPage(*this, getVBox(settings));
904 PreferencesPageCallbacks_constructPage(g_settingsPreferences, preferencesPage);
907 GtkTreeIter group = PreferenceTree_appendPage(store, 0, "Settings", settings);
908 PreferenceTreeGroup preferenceGroup(*this, m_notebook, store, group);
910 PreferenceGroupCallbacks_constructGroup(g_settingsCallbacks, preferenceGroup);
914 gtk_tree_view_expand_all(GTK_TREE_VIEW(view));
916 g_object_unref(G_OBJECT(store));
922 gtk_notebook_set_page(GTK_NOTEBOOK(m_notebook), 0);
927 preferences_globals_t g_preferences_globals;
929 PrefsDlg g_Preferences; // global prefs instance
932 void PreferencesDialog_constructWindow(GtkWindow* main_window)
934 g_Preferences.m_parent = main_window;
935 g_Preferences.Create();
937 void PreferencesDialog_destroyWindow()
939 g_Preferences.Destroy();
943 PreferenceDictionary g_preferences;
945 PreferenceSystem& GetPreferenceSystem()
947 return g_preferences;
950 class PreferenceSystemAPI
952 PreferenceSystem* m_preferencesystem;
954 typedef PreferenceSystem Type;
955 STRING_CONSTANT(Name, "*");
957 PreferenceSystemAPI()
959 m_preferencesystem = &GetPreferenceSystem();
961 PreferenceSystem* getTable()
963 return m_preferencesystem;
967 #include "modulesystem/singletonmodule.h"
968 #include "modulesystem/moduleregistry.h"
970 typedef SingletonModule<PreferenceSystemAPI> PreferenceSystemModule;
971 typedef Static<PreferenceSystemModule> StaticPreferenceSystemModule;
972 StaticRegisterModule staticRegisterPreferenceSystem(StaticPreferenceSystemModule::instance());
974 void Preferences_Load()
976 g_GamesDialog.LoadPrefs();
978 globalOutputStream() << "loading local preferences from " << g_Preferences.m_inipath->str << "\n";
980 if(!Preferences_Load(g_preferences, g_Preferences.m_inipath->str, g_GamesDialog.m_sGameFile.c_str()))
982 globalOutputStream() << "failed to load local preferences from " << g_Preferences.m_inipath->str << "\n";
986 void Preferences_Save()
988 if (g_preferences_globals.disable_ini)
991 g_GamesDialog.SavePrefs();
993 globalOutputStream() << "saving local preferences to " << g_Preferences.m_inipath->str << "\n";
995 if(!Preferences_Save_Safe(g_preferences, g_Preferences.m_inipath->str))
997 globalOutputStream() << "failed to save local preferences to " << g_Preferences.m_inipath->str << "\n";
1001 void Preferences_Reset()
1003 file_remove(g_Preferences.m_inipath->str);
1007 void PrefsDlg::PostModal (EMessageBoxReturn code)
1016 std::vector<const char*> g_restart_required;
1018 void PreferencesDialog_restartRequired(const char* staticName)
1020 g_restart_required.push_back(staticName);
1023 void PreferencesDialog_showDialog()
1025 if(ConfirmModified("Edit Preferences") && g_Preferences.DoModal() == eIDOK)
1027 if(!g_restart_required.empty())
1029 StringOutputStream message(256);
1030 message << "Preference changes require a restart:\n";
1031 for(std::vector<const char*>::iterator i = g_restart_required.begin(); i != g_restart_required.end(); ++i)
1033 message << (*i) << '\n';
1035 gtk_MessageBox(GTK_WIDGET(MainFrame_getWindow()), message.c_str());
1036 g_restart_required.clear();
1045 void GameName_importString(const char* value)
1047 gamename_set(value);
1049 typedef FreeCaller1<const char*, GameName_importString> GameNameImportStringCaller;
1050 void GameName_exportString(const StringImportCallback& importer)
1052 importer(gamename_get());
1054 typedef FreeCaller1<const StringImportCallback&, GameName_exportString> GameNameExportStringCaller;
1056 void GameMode_importString(const char* value)
1058 gamemode_set(value);
1060 typedef FreeCaller1<const char*, GameMode_importString> GameModeImportStringCaller;
1061 void GameMode_exportString(const StringImportCallback& importer)
1063 importer(gamemode_get());
1065 typedef FreeCaller1<const StringImportCallback&, GameMode_exportString> GameModeExportStringCaller;
1068 void RegisterPreferences(PreferenceSystem& preferences)
1071 preferences.registerPreference("UseCustomShaderEditor", BoolImportStringCaller(g_TextEditor_useWin32Editor), BoolExportStringCaller(g_TextEditor_useWin32Editor));
1073 preferences.registerPreference("UseCustomShaderEditor", BoolImportStringCaller(g_TextEditor_useCustomEditor), BoolExportStringCaller(g_TextEditor_useCustomEditor));
1074 preferences.registerPreference("CustomShaderEditorCommand", CopiedStringImportStringCaller(g_TextEditor_editorCommand), CopiedStringExportStringCaller(g_TextEditor_editorCommand));
1077 preferences.registerPreference("GameName", GameNameImportStringCaller(), GameNameExportStringCaller());
1078 preferences.registerPreference("GameMode", GameModeImportStringCaller(), GameModeExportStringCaller());
1081 void Preferences_Init()
1083 RegisterPreferences(GetPreferenceSystem());