radiant: ask user to restart the editor when map load switches the brush format
[xonotic/netradiant.git] / radiant / preferences.h
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22 /*
23    The following source code is licensed by Id Software and subject to the terms of
24    its LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with
25    GtkRadiant. If you did not receive a LIMITED USE SOFTWARE LICENSE AGREEMENT,
26    please contact Id Software immediately at info@idsoftware.com.
27  */
28
29 #if !defined( INCLUDED_PREFERENCES_H )
30 #define INCLUDED_PREFERENCES_H
31
32 #include "libxml/parser.h"
33 #include "dialog.h"
34 #include <list>
35 #include <map>
36 #include "property.h"
37
38 void Widget_connectToggleDependency( ui::Widget self, ui::Widget toggleButton );
39
40 class PreferencesPage
41 {
42 Dialog& m_dialog;
43 ui::VBox m_vbox;
44 public:
45 PreferencesPage( Dialog& dialog, ui::VBox vbox ) : m_dialog( dialog ), m_vbox( vbox ){
46 }
47 ui::CheckButton appendCheckBox( const char* name, const char* flag, bool& data ){
48         return m_dialog.addCheckBox( m_vbox, name, flag, data );
49 }
50 ui::CheckButton appendCheckBox( const char* name, const char* flag, Property<bool> const &cb ){
51         return m_dialog.addCheckBox( m_vbox, name, flag, cb );
52 }
53 void appendCombo( const char* name, StringArrayRange values, Property<int> const &cb ){
54         m_dialog.addCombo( m_vbox, name, values, cb );
55 }
56 void appendCombo( const char* name, int& data, StringArrayRange values ){
57         m_dialog.addCombo( m_vbox, name, data, values );
58 }
59 void appendSlider( const char* name, int& data, gboolean draw_value, const char* low, const char* high, double value, double lower, double upper, double step_increment, double page_increment ){
60         m_dialog.addSlider( m_vbox, name, data, draw_value, low, high, value, lower, upper, step_increment, page_increment );
61 }
62 void appendRadio( const char* name, StringArrayRange names, Property<int> const &cb ){
63         m_dialog.addRadio( m_vbox, name, names, cb );
64 }
65 void appendRadio( const char* name, int& data, StringArrayRange names ){
66         m_dialog.addRadio( m_vbox, name, data, names );
67 }
68 void appendRadioIcons( const char* name, StringArrayRange icons, Property<int> const &cb ){
69         m_dialog.addRadioIcons( m_vbox, name, icons, cb );
70 }
71 void appendRadioIcons( const char* name, int& data, StringArrayRange icons ){
72         m_dialog.addRadioIcons( m_vbox, name, data, icons );
73 }
74 ui::Widget appendSpacer( int dimension ){
75         return m_dialog.addSpacer( m_vbox, dimension );
76 }
77 ui::Widget appendLabel( const char* name, const char* text ){
78         return m_dialog.addLabel( m_vbox, name, text );
79 }
80 ui::Widget appendEntry( const char* name, Property<int> const &cb ){
81         return m_dialog.addIntEntry( m_vbox, name, cb );
82 }
83 ui::Widget appendEntry( const char* name, int& data ){
84         return m_dialog.addEntry( m_vbox, name, data );
85 }
86 ui::Widget appendEntry( const char* name, Property<std::size_t> const &cb){
87         return m_dialog.addSizeEntry( m_vbox, name, cb );
88 }
89 ui::Widget appendEntry( const char* name, std::size_t& data ){
90         return m_dialog.addEntry( m_vbox, name, data );
91 }
92 ui::Widget appendEntry( const char* name, Property<float> const &cb ){
93         return m_dialog.addFloatEntry( m_vbox, name, cb );
94 }
95 ui::Widget appendEntry( const char* name, float& data ){
96         return m_dialog.addEntry( m_vbox, name, data );
97 }
98 ui::Widget appendPathEntry( const char* name, bool browse_directory, Property<const char *> const &cb ){
99         return m_dialog.addPathEntry( m_vbox, name, browse_directory, cb );
100 }
101 ui::Widget appendPathEntry( const char* name, CopiedString& data, bool directory ){
102         return m_dialog.addPathEntry( m_vbox, name, data, directory );
103 }
104 ui::SpinButton appendSpinner( const char* name, int& data, double value, double lower, double upper ){
105         return m_dialog.addSpinner( m_vbox, name, data, value, lower, upper );
106 }
107 ui::SpinButton appendSpinner( const char* name, double value, double lower, double upper, Property<int> const &cb ){
108         return m_dialog.addSpinner( m_vbox, name, value, lower, upper, cb );
109 }
110 ui::SpinButton appendSpinner( const char* name, double value, double lower, double upper, Property<float> const &cb ){
111         return m_dialog.addSpinner( m_vbox, name, value, lower, upper, cb );
112 }
113 };
114
115 typedef Callback<void(PreferencesPage&)> PreferencesPageCallback;
116
117 class PreferenceGroup
118 {
119 public:
120 virtual PreferencesPage createPage( const char* treeName, const char* frameName ) = 0;
121 };
122
123 typedef Callback<void(PreferenceGroup&)> PreferenceGroupCallback;
124
125 void PreferencesDialog_addInterfacePreferences( const PreferencesPageCallback& callback );
126 void PreferencesDialog_addInterfacePage( const PreferenceGroupCallback& callback );
127 void PreferencesDialog_addDisplayPreferences( const PreferencesPageCallback& callback );
128 void PreferencesDialog_addDisplayPage( const PreferenceGroupCallback& callback );
129 void PreferencesDialog_addSettingsPreferences( const PreferencesPageCallback& callback );
130 void PreferencesDialog_addSettingsPage( const PreferenceGroupCallback& callback );
131
132 bool PreferencesDialog_isRestartRequired();
133 void PreferencesDialog_restartRequired( const char* staticName );
134
135 template<typename Value>
136 class LatchedValue {
137 public:
138     Value m_value;
139     Value m_latched;
140     const char *m_description;
141
142     LatchedValue(Value value, const char *description) : m_latched(value), m_description(description) {
143     }
144
145     void useLatched() {
146         m_value = m_latched;
147     }
148 };
149
150 template<class T>
151 struct PropertyImpl<LatchedValue<T>, T> {
152         static void Export(const LatchedValue<T> &self, const Callback<void(T)> &returnz) {
153                 returnz(self.m_latched);
154         }
155
156         static void Import(LatchedValue<T> &self, T value) {
157                 self.m_latched = value;
158                 if (value != self.m_value) {
159                         PreferencesDialog_restartRequired(self.m_description);
160                 }
161         }
162 };
163
164 template<class T>
165 Property<T> make_property(LatchedValue<T> &self) {
166         return make_property<LatchedValue<T>, T>(self);
167 }
168
169 /*!
170    holds information for a given game
171    I'm a bit unclear on that still
172    it holds game specific configuration stuff
173    such as base names, engine names, some game specific features to activate in the various modules
174    it is not strictly a prefs thing since the user is not supposed to edit that (unless he is hacking
175    support for a new game)
176
177    what we do now is fully generate the information for this during the setup. We might want to
178    generate a piece that just says "the game pack is there", but put the rest of the config somwhere
179    else (i.e. not generated, copied over during setup .. for instance in the game tools directory)
180  */
181 class CGameDescription
182 {
183 typedef std::map<CopiedString, CopiedString> GameDescription;
184
185 public:
186 CopiedString mGameFile;   ///< the .game file that describes this game
187 GameDescription m_gameDescription;
188
189 CopiedString mGameToolsPath;   ///< the explicit path to the game-dependent modules
190 CopiedString mGameType;   ///< the type of the engine
191
192 const char* getKeyValue( const char* key ) const {
193         GameDescription::const_iterator i = m_gameDescription.find( key );
194         if ( i != m_gameDescription.end() ) {
195                 return ( *i ).second.c_str();
196         }
197         return "";
198 }
199 const char* getRequiredKeyValue( const char* key ) const {
200         GameDescription::const_iterator i = m_gameDescription.find( key );
201         if ( i != m_gameDescription.end() ) {
202                 return ( *i ).second.c_str();
203         }
204         ERROR_MESSAGE( "game attribute " << makeQuoted( key ) << " not found in " << makeQuoted( mGameFile.c_str() ) );
205         return "";
206 }
207
208 CGameDescription( xmlDocPtr pDoc, const CopiedString &GameFile );
209
210 void Dump();
211 };
212
213 extern CGameDescription *g_pGameDescription;
214
215 class PrefsDlg;
216
217 class PreferencesPage;
218
219 class StringOutputStream;
220
221 /*!
222    standalone dialog for games selection, and more generally global settings
223  */
224 class CGameDialog : public Dialog
225 {
226 protected:
227
228 mutable int m_nComboSelect;   ///< intermediate int value for combo in dialog box
229
230 public:
231
232 /*!
233   used to no ask for restart when switching game from Gobal Preferences window displayed on startup
234 */
235
236 bool onStartup;
237 /*!
238    those settings are saved in the global prefs file
239    I'm too lazy to wrap behind protected access, not sure this needs to be public
240    NOTE: those are preference settings. if you change them it is likely that you would
241    have to restart the editor for them to take effect
242  */
243 /*@{*/
244 /*!
245    what game has been selected
246    this is the name of the .game file
247  */
248 CopiedString m_sGameFile;
249 /*!
250    prompt which game to load on startup
251  */
252 bool m_bGamePrompt;
253 /*!
254    when if m_bGamePrompt is true
255    do not prompt at startup which game to load this time, but prompt the next times
256    this is used to not uselessly prompt game after having restarted because user switched game
257  */
258 bool m_bSkipGamePromptOnce;
259 /*!
260    log console to radiant.log
261    m_bForceLogConsole is an obscure forced latching situation
262  */
263 bool m_bForceLogConsole;
264 /*@}*/
265
266 /*!
267    the list of game descriptions we scanned from the game/ dir
268  */
269 std::list<CGameDescription*> mGames;
270
271 CGameDialog() :
272         m_sGameFile( "" ),
273         m_bGamePrompt( true ),
274         m_bSkipGamePromptOnce( false ),
275         m_bForceLogConsole( false ){
276 }
277 virtual ~CGameDialog();
278
279 /*!
280    intialize the game dialog, called at CPrefsDlg::Init
281    will scan for games, load prefs, and do game selection dialog if needed
282  */
283 void Init();
284
285 /*!
286    reset the global settings by removing the file
287  */
288 void Reset();
289
290 /*!
291    run the dialog UI for the list of games
292  */
293 void DoGameDialog();
294
295 /*!
296    Dialog API
297    this is only called when the dialog is built at startup for main engine select
298  */
299 ui::Window BuildDialog();
300
301 void GameFileImport( int value );
302 void GameFileExport( const Callback<void(int)> & importCallback ) const;
303
304 /*!
305    construction of the dialog frame
306    this is the part to be re-used in prefs dialog
307    for the standalone dialog, we include this in a modal box
308    for prefs, we hook the frame in the main notebook
309    build the frame on-demand (only once)
310  */
311 void CreateGlobalFrame( PreferencesPage& page );
312
313 /*!
314    global preferences subsystem
315    XML-based this time, hopefully this will generalize to other prefs
316    LoadPrefs has hardcoded defaults
317    NOTE: it may not be strictly 'CGameDialog' to put the global prefs here
318    could have named the class differently I guess
319  */
320 /*@{*/
321 void LoadPrefs();   ///< load from file into variables
322 void SavePrefs();   ///< save pref variables to file
323 /*@}*/
324
325 private:
326 /*!
327    scan for .game files, load them
328  */
329 void ScanForGames();
330
331 /*!
332    inits g_Preferences.m_global_rc_path
333  */
334 void InitGlobalPrefPath();
335
336 /*!
337    uses m_nComboItem to find the right mGames
338  */
339 CGameDescription *GameDescriptionForComboItem();
340 };
341
342 /*!
343    this holds global level preferences
344  */
345 extern CGameDialog g_GamesDialog;
346
347
348 class texdef_t;
349
350 class PrefsDlg : public Dialog
351 {
352 public:
353 protected:
354 std::list<CGameDescription *> mGames;
355
356 public:
357
358 ui::Widget m_notebook{ui::null};
359
360 virtual ~PrefsDlg(){
361         g_string_free( m_rc_path, true );
362         g_string_free( m_inipath, true );
363 }
364
365 /*!
366    path for global settings
367    win32: AppPath
368    linux: ~/.radiant/[version]/
369  */
370 GString *m_global_rc_path;
371
372 /*!
373    path to per-game settings
374    used for various game dependant storage
375    win32: GameToolsPath
376    linux: ~/.radiant/[version]/[gamename]/
377  */
378 GString *m_rc_path;
379
380 /*!
381    holds per-game settings
382    m_rc_path+"local.pref"
383    \todo FIXME at some point this should become XML property bag code too
384  */
385 GString *m_inipath;
386
387 // initialize the above paths
388 void Init();
389
390 /*! Utility function for swapping notebook pages for tree list selections */
391 void showPrefPage( ui::Widget prefpage );
392
393 protected:
394
395 /*! Dialog API */
396 ui::Window BuildDialog();
397 void PostModal( EMessageBoxReturn code );
398 };
399
400 extern PrefsDlg g_Preferences;
401
402 struct preferences_globals_t
403 {
404         // disabled all INI / registry read write .. used when shutting down after registry cleanup
405         bool disable_ini;
406         preferences_globals_t() : disable_ini( false ){
407         }
408 };
409 extern preferences_globals_t g_preferences_globals;
410
411 void PreferencesDialog_constructWindow( ui::Window main_window );
412 void PreferencesDialog_destroyWindow();
413
414
415 void PreferencesDialog_restartIfRequired();
416 void PreferencesDialog_showDialog();
417
418 void GlobalPreferences_Init();
419 void Preferences_Init();
420
421 void Preferences_Load();
422 void Preferences_Save();
423
424 void Preferences_Reset();
425
426
427 #endif