fixed: Debug logging enabled in the UI would reset itself on reboot.
[xbmc:xbmc-antiquated.git] / xbmc / GUISettings.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "GUISettings.h"
23 #include <limits.h>
24 #include <float.h>
25 #include "Settings.h"
26 #include "GUIDialogFileBrowser.h"
27 #include "XBAudioConfig.h"
28 #include "MediaManager.h"
29 #ifdef _LINUX
30 #include "LinuxTimezone.h"
31 #endif
32 #include "utils/Network.h"
33 #include "Application.h"
34 #include "FileSystem/SpecialProtocol.h"
35 #include "AdvancedSettings.h"
36 #include "LocalizeStrings.h"
37 #include "StringUtils.h"
38 #include "SystemInfo.h"
39 #include "utils/log.h"
40 #include "tinyXML/tinyxml.h"
41 #include "visualizations/Visualisation.h"
42 #include "WindowingFactory.h"
43
44 using namespace std;
45
46 // String id's of the masks
47 #define MASK_MINS   14044
48 #define MASK_SECS   14045
49 #define MASK_MS    14046
50 #define MASK_PERCENT 14047
51 #define MASK_KBPS   14048
52 #define MASK_KB    14049
53 #define MASK_DB    14050
54
55 #define MAX_RESOLUTIONS 128
56
57 #define TEXT_OFF  351
58 #define TEXT_NONE 231
59
60 class CGUISettings g_guiSettings;
61
62 #ifdef _LINUX
63 #define DEFAULT_VISUALISATION "opengl_spectrum.vis"
64 #elif defined(_WIN32)
65 #ifdef HAS_DX
66 #define DEFAULT_VISUALISATION "MilkDrop_win32dx.vis"
67 #else
68 #define DEFAULT_VISUALISATION "opengl_spectrum_win32.vis"
69 #endif
70 #endif
71
72 struct sortsettings
73 {
74   bool operator()(const CSetting* pSetting1, const CSetting* pSetting2)
75   {
76     return pSetting1->GetOrder() < pSetting2->GetOrder();
77   }
78 };
79
80 void CSettingBool::FromString(const CStdString &strValue)
81 {
82   m_bData = (strValue == "true");
83 }
84
85 CStdString CSettingBool::ToString()
86 {
87   return m_bData ? "true" : "false";
88 }
89
90 CSettingSeparator::CSettingSeparator(int iOrder, const char *strSetting)
91     : CSetting(iOrder, strSetting, 0, SEPARATOR_CONTROL)
92 {
93 }
94
95 CSettingFloat::CSettingFloat(int iOrder, const char *strSetting, int iLabel, float fData, float fMin, float fStep, float fMax, int iControlType)
96     : CSetting(iOrder, strSetting, iLabel, iControlType)
97 {
98   m_fData = fData;
99   m_fMin = fMin;
100   m_fStep = fStep;
101   m_fMax = fMax;
102 }
103
104 void CSettingFloat::FromString(const CStdString &strValue)
105 {
106   SetData((float)atof(strValue.c_str()));
107 }
108
109 CStdString CSettingFloat::ToString()
110 {
111   CStdString strValue;
112   strValue.Format("%f", m_fData);
113   return strValue;
114 }
115
116 CSettingInt::CSettingInt(int iOrder, const char *strSetting, int iLabel, int iData, int iMin, int iStep, int iMax, int iControlType, const char *strFormat)
117     : CSetting(iOrder, strSetting, iLabel, iControlType)
118 {
119   m_iData = iData;
120   m_iMin = iMin;
121   m_iMax = iMax;
122   m_iStep = iStep;
123   m_iFormat = -1;
124   m_iLabelMin = -1;
125   if (strFormat)
126     m_strFormat = strFormat;
127   else
128     m_strFormat = "%i";
129 }
130
131 CSettingInt::CSettingInt(int iOrder, const char *strSetting, int iLabel, int iData, int iMin, int iStep, int iMax, int iControlType, int iFormat, int iLabelMin)
132     : CSetting(iOrder, strSetting, iLabel, iControlType)
133 {
134   m_iData = iData;
135   m_iMin = iMin;
136   m_iMax = iMax;
137   m_iStep = iStep;
138   m_iLabelMin = iLabelMin;
139   m_iFormat = iFormat;
140   if (m_iFormat < 0)
141     m_strFormat = "%i";
142 }
143
144 void CSettingInt::FromString(const CStdString &strValue)
145 {
146   SetData(atoi(strValue.c_str()));
147 }
148
149 CStdString CSettingInt::ToString()
150 {
151   CStdString strValue;
152   strValue.Format("%i", m_iData);
153   return strValue;
154 }
155
156 void CSettingHex::FromString(const CStdString &strValue)
157 {
158   int iHexValue;
159   if (sscanf(strValue, "%x", (unsigned int *)&iHexValue))
160     SetData(iHexValue);
161 }
162
163 CStdString CSettingHex::ToString()
164 {
165   CStdString strValue;
166   strValue.Format("%x", m_iData);
167   return strValue;
168 }
169
170 CSettingString::CSettingString(int iOrder, const char *strSetting, int iLabel, const char *strData, int iControlType, bool bAllowEmpty, int iHeadingString)
171     : CSetting(iOrder, strSetting, iLabel, iControlType)
172 {
173   m_strData = strData;
174   m_bAllowEmpty = bAllowEmpty;
175   m_iHeadingString = iHeadingString;
176 }
177
178 void CSettingString::FromString(const CStdString &strValue)
179 {
180   m_strData = strValue;
181 }
182
183 CStdString CSettingString::ToString()
184 {
185   return m_strData;
186 }
187
188 CSettingPath::CSettingPath(int iOrder, const char *strSetting, int iLabel, const char *strData, int iControlType, bool bAllowEmpty, int iHeadingString)
189     : CSettingString(iOrder, strSetting, iLabel, strData, iControlType, bAllowEmpty, iHeadingString)
190 {
191 }
192
193 void CSettingsGroup::GetCategories(vecSettingsCategory &vecCategories)
194 {
195   vecCategories.clear();
196   for (unsigned int i = 0; i < m_vecCategories.size(); i++)
197   {
198     vecSettings settings;
199     // check whether we actually have these settings available.
200     g_guiSettings.GetSettingsGroup(m_vecCategories[i]->m_strCategory, settings);
201     if (settings.size())
202       vecCategories.push_back(m_vecCategories[i]);
203   }
204 }
205
206 // Settings are case sensitive
207 CGUISettings::CGUISettings(void)
208 {
209 }
210
211 void CGUISettings::Initialize()
212 {
213   ZeroMemory(&m_replayGain, sizeof(ReplayGainSettings));
214
215   // Pictures settings
216   AddGroup(0, 1);
217   AddCategory(0, "pictures", 14081);
218   AddBool(1, "pictures.usetags", 14082, true);
219   AddBool(2,"pictures.generatethumbs",13360,true);
220   AddBool(3, "pictures.useexifrotation", 20184, true);
221   AddBool(4, "pictures.showvideos", 22022, false);
222   // FIXME: hide this setting until it is properly respected. In the meanwhile, default to AUTO.
223   AddInt(0, "pictures.displayresolution", 169, (int)RES_AUTORES, (int)RES_AUTORES, 1, (int)RES_AUTORES, SPIN_CONTROL_TEXT);
224
225   AddCategory(0, "slideshow", 108);
226   AddInt(1, "slideshow.staytime", 12378, 5, 1, 1, 100, SPIN_CONTROL_INT_PLUS, MASK_SECS);
227   AddBool(2, "slideshow.displayeffects", 12379, true);
228   AddBool(0, "slideshow.shuffle", 13319, false);
229
230   // Programs settings
231 //  AddGroup(1, 0);
232
233   // My Weather settings
234   AddGroup(2, 8);
235   AddCategory(2, "weather", 16000);
236   AddString(1, "weather.areacode1", 14019, "USNY0996 - New York, NY", BUTTON_CONTROL_STANDARD);
237   AddString(2, "weather.areacode2", 14020, "UKXX0085 - London, United Kingdom", BUTTON_CONTROL_STANDARD);
238   AddString(3, "weather.areacode3", 14021, "JAXX0085 - Tokyo, Japan", BUTTON_CONTROL_STANDARD);
239   AddSeparator(4, "weather.sep1");
240   AddString(5, "weather.plugin", 23000, "", SPIN_CONTROL_TEXT, true);
241   AddString(6, "weather.pluginsettings", 23001, "", BUTTON_CONTROL_STANDARD, true);
242
243   // My Music Settings
244   AddGroup(3, 2);
245   AddCategory(3,"musiclibrary",14022);
246   AddBool(0, "musiclibrary.enabled", 418, true);
247   AddBool(2, "musiclibrary.showcompilationartists", 13414, true);
248   AddSeparator(3,"musiclibrary.sep1");
249   AddBool(4,"musiclibrary.downloadinfo", 20192, false);
250   AddString(6, "musiclibrary.scraper", 20194, "allmusic.xml", SPIN_CONTROL_TEXT);
251   AddString(7, "musiclibrary.scrapersettings", 21417, "", BUTTON_CONTROL_STANDARD);
252   AddBool(8, "musiclibrary.updateonstartup", 22000, false);
253   AddBool(0, "musiclibrary.backgroundupdate", 22001, false);
254   AddSeparator(9,"musiclibrary.sep2");
255   AddString(10, "musiclibrary.cleanup", 334, "", BUTTON_CONTROL_STANDARD);
256   AddString(11, "musiclibrary.export", 20196, "", BUTTON_CONTROL_STANDARD);
257   AddString(12, "musiclibrary.import", 20197, "", BUTTON_CONTROL_STANDARD);
258
259   AddCategory(3, "musicplayer", 14086);
260   AddBool(1, "musicplayer.autoplaynextitem", 489, true);
261   AddBool(2, "musicplayer.queuebydefault", 14084, false);
262   AddSeparator(3, "musicplayer.sep1");
263   AddInt(4, "musicplayer.replaygaintype", 638, REPLAY_GAIN_ALBUM, REPLAY_GAIN_NONE, 1, REPLAY_GAIN_TRACK, SPIN_CONTROL_TEXT);
264   AddInt(0, "musicplayer.replaygainpreamp", 641, 89, 77, 1, 101, SPIN_CONTROL_INT_PLUS, MASK_DB);
265   AddInt(0, "musicplayer.replaygainnogainpreamp", 642, 89, 77, 1, 101, SPIN_CONTROL_INT_PLUS, MASK_DB);
266   AddBool(0, "musicplayer.replaygainavoidclipping", 643, false);
267   AddInt(5, "musicplayer.crossfade", 13314, 0, 0, 1, 15, SPIN_CONTROL_INT_PLUS, MASK_SECS, TEXT_OFF);
268   AddBool(6, "musicplayer.crossfadealbumtracks", 13400, true);
269   AddSeparator(7, "musicplayer.sep3");
270   AddString(8, "musicplayer.visualisation", 250, DEFAULT_VISUALISATION, SPIN_CONTROL_TEXT);
271
272   AddCategory(3, "musicfiles", 14081);
273   AddBool(1, "musicfiles.usetags", 258, true);
274   AddString(2, "musicfiles.trackformat", 13307, "[%N. ]%A - %T", EDIT_CONTROL_INPUT, false, 16016);
275   AddString(3, "musicfiles.trackformatright", 13387, "%D", EDIT_CONTROL_INPUT, false, 16016);
276   // advanced per-view trackformats.
277   AddString(0, "musicfiles.nowplayingtrackformat", 13307, "", EDIT_CONTROL_INPUT, false, 16016);
278   AddString(0, "musicfiles.nowplayingtrackformatright", 13387, "", EDIT_CONTROL_INPUT, false, 16016);
279   AddString(0, "musicfiles.librarytrackformat", 13307, "", EDIT_CONTROL_INPUT, false, 16016);
280   AddString(0, "musicfiles.librarytrackformatright", 13387, "", EDIT_CONTROL_INPUT, false, 16016);
281   AddBool(4, "musicfiles.findremotethumbs", 14059, true);
282
283   AddCategory(3, "scrobbler", 15221);
284   AddBool(1, "scrobbler.lastfmsubmit", 15201, false);
285   AddBool(2, "scrobbler.lastfmsubmitradio", 15250, false);
286   AddString(3,"scrobbler.lastfmusername", 15202, "", EDIT_CONTROL_INPUT, false, 15202);
287   AddString(4,"scrobbler.lastfmpassword", 15203, "", EDIT_CONTROL_HIDDEN_INPUT, false, 15203);
288   AddSeparator(5, "scrobbler.sep1");
289   AddBool(6, "scrobbler.librefmsubmit", 15217, false);
290   AddString(7, "scrobbler.librefmusername", 15218, "", EDIT_CONTROL_INPUT, false, 15218);
291   AddString(8, "scrobbler.librefmpassword", 15219, "", EDIT_CONTROL_HIDDEN_INPUT, false, 15219);
292
293   AddCategory(3, "audiocds", 620);
294   AddBool(1, "audiocds.autorun", 14085, false);
295   AddBool(2, "audiocds.usecddb", 227, true);
296   AddSeparator(3, "audiocds.sep1");
297   AddPath(4,"audiocds.recordingpath",20000,"select writable folder",BUTTON_CONTROL_PATH_INPUT,false,657);
298   AddString(5, "audiocds.trackformat", 13307, "[%N. ]%T - %A", EDIT_CONTROL_INPUT, false, 16016);
299   AddInt(6, "audiocds.encoder", 621, CDDARIP_ENCODER_LAME, CDDARIP_ENCODER_LAME, 1, CDDARIP_ENCODER_WAV, SPIN_CONTROL_TEXT);
300   AddInt(7, "audiocds.quality", 622, CDDARIP_QUALITY_CBR, CDDARIP_QUALITY_CBR, 1, CDDARIP_QUALITY_EXTREME, SPIN_CONTROL_TEXT);
301   AddInt(8, "audiocds.bitrate", 623, 192, 128, 32, 320, SPIN_CONTROL_INT_PLUS, MASK_KBPS);
302
303 #ifdef HAS_KARAOKE
304   AddCategory(3, "karaoke", 13327);
305   AddBool(1, "karaoke.enabled", 13323, false);
306   // auto-popup the song selector dialog when the karaoke song was just finished and playlist is empty.
307   AddBool(2, "karaoke.autopopupselector", 22037, false);
308   AddSeparator(3, "karaoke.sep1");
309   AddString(4, "karaoke.font", 22030, "arial.ttf", SPIN_CONTROL_TEXT);
310   AddInt(5, "karaoke.fontheight", 22031, 36, 16, 2, 74, SPIN_CONTROL_TEXT); // use text as there is a disk based lookup needed
311   AddInt(6, "karaoke.fontcolors", 22032, KARAOKE_COLOR_START, KARAOKE_COLOR_START, 1, KARAOKE_COLOR_END, SPIN_CONTROL_TEXT);
312   AddString(7, "karaoke.charset", 22033, "DEFAULT", SPIN_CONTROL_TEXT);
313   AddSeparator(8,"karaoke.sep2");
314   AddString(10, "karaoke.export", 22038, "", BUTTON_CONTROL_STANDARD);
315   AddString(11, "karaoke.importcsv", 22036, "", BUTTON_CONTROL_STANDARD);
316 #endif
317
318   // System settings
319   AddGroup(4, 13000);
320   AddCategory(4, "videoscreen", 21373);
321 #if defined(_WIN32) || defined (__APPLE__)
322   AddString(1, "videoscreen.screenmode", 131, "DESKTOP", SPIN_CONTROL_TEXT);
323 #else
324   AddString(1, "videoscreen.screenmode", 169, "DESKTOP", SPIN_CONTROL_TEXT);
325 #endif
326 #if defined(_WIN32) || defined (__APPLE__)
327   // We prefer a fake fullscreen mode (window covering the screen rather than dedicated fullscreen)
328   // as it works nicer with switching to other applications. However on some systems vsync is broken
329   // when we do this (eg non-Aero on ATI in particular) and on others (AppleTV) we can't get XBMC to
330   // the front
331   bool fakeFullScreen = true;
332   bool showSetting = true;
333   if (g_sysinfo.IsAeroDisabled())
334     fakeFullScreen = false;
335 #if defined (__APPLE__)
336   if (g_sysinfo.IsAppleTV())
337   {
338     fakeFullScreen = false;
339   }
340   showSetting = false;
341 #endif
342   AddBool(showSetting ? 2 : 0, "videoscreen.fakefullscreen", 14083, fakeFullScreen);
343   AddBool(3, "videoscreen.blankdisplays", 13130, false);
344   AddSeparator(4, "videoscreen.sep1");
345 #endif
346 #if defined(__APPLE__) || defined(_WIN32)
347   // OSX does not use a driver set vsync
348   AddInt(5, "videoscreen.vsync", 13105, DEFAULT_VSYNC, VSYNC_DISABLED, 1, VSYNC_ALWAYS, SPIN_CONTROL_TEXT);
349 #else
350   AddInt(5, "videoscreen.vsync", 13105, DEFAULT_VSYNC, VSYNC_DISABLED, 1, VSYNC_DRIVER, SPIN_CONTROL_TEXT);
351 #endif
352   AddString(6, "videoscreen.guicalibration",214,"", BUTTON_CONTROL_STANDARD);
353 #ifndef HAS_DX
354   // Todo: Implement test pattern for DX
355   AddString(7, "videoscreen.testpattern",226,"", BUTTON_CONTROL_STANDARD);
356 #endif
357 #if defined(_LINUX) && !defined(__APPLE__)
358   AddSeparator(8, "videoscreen.sep2");
359   AddBool(9, "videoscreen.haslcd", 4501, false);
360 #endif
361
362   AddCategory(4, "audiooutput", 772);
363   AddInt(3, "audiooutput.mode", 337, AUDIO_ANALOG, AUDIO_ANALOG, 1, AUDIO_DIGITAL, SPIN_CONTROL_TEXT);
364   AddBool(4, "audiooutput.ac3passthrough", 364, true);
365   AddBool(5, "audiooutput.dtspassthrough", 254, true);
366 #ifdef __APPLE__
367   AddString(6, "audiooutput.audiodevice", 545, "Default", SPIN_CONTROL_TEXT);
368   //AddString(7, "audiooutput.passthroughdevice", 546, "S/PDIF", BUTTON_CONTROL_INPUT);
369   AddBool(7, "audiooutput.downmixmultichannel", 548, true);
370 #elif defined(_LINUX)
371   AddSeparator(6, "audiooutput.sep1");
372   AddString(7, "audiooutput.audiodevice", 545, "default", SPIN_CONTROL_TEXT);
373   AddString(8, "audiooutput.customdevice", 1300, "", EDIT_CONTROL_INPUT);
374   AddSeparator(9, "audiooutput.sep2");
375   AddString(10, "audiooutput.passthroughdevice", 546, "iec958", SPIN_CONTROL_TEXT);
376   AddString(11, "audiooutput.custompassthrough", 1301, "", EDIT_CONTROL_INPUT);
377   AddSeparator(12, "audiooutput.sep3");
378   AddBool(13, "audiooutput.downmixmultichannel", 548, true);
379 #elif defined(_WIN32)
380   AddString(6, "audiooutput.audiodevice", 545, "Default", SPIN_CONTROL_TEXT);
381   AddBool(7, "audiooutput.downmixmultichannel", 548, true);
382 #endif
383
384   AddCategory(4, "input", 14094);
385 #ifdef __APPLE__
386   AddInt(1, "input.appleremotemode", 13600, APPLE_REMOTE_STANDARD, APPLE_REMOTE_DISABLED, 1, APPLE_REMOTE_MULTIREMOTE, SPIN_CONTROL_TEXT);
387   AddBool(2, "input.appleremotealwayson", 13602, false);
388   AddInt(0, "input.appleremotesequencetime", 13603, 500, 50, 50, 1000, SPIN_CONTROL_INT_PLUS, MASK_MS, TEXT_OFF);
389   AddSeparator(3, "input.sep1");
390 #endif
391   AddBool(4, "input.remoteaskeyboard", 21449, false);
392   AddBool(5, "input.enablemouse", 21369, true);
393
394   AddCategory(4, "powermanagement", 14095);
395   // Note: Application.cpp might hide powersaving settings if not supported.
396   AddInt(1, "powermanagement.displaysoff", 1450, 0, 0, 5, 120, SPIN_CONTROL_INT_PLUS, MASK_MINS, TEXT_OFF);
397   AddInt(2, "powermanagement.shutdowntime", 357, 0, 0, 5, 120, SPIN_CONTROL_INT_PLUS, MASK_MINS, TEXT_OFF);
398   // In standalone mode we default to another.
399   if (g_application.IsStandAlone())
400     AddInt(3, "powermanagement.shutdownstate", 13008, 0, 1, 1, 5, SPIN_CONTROL_TEXT);
401   else
402     AddInt(3, "powermanagement.shutdownstate", 13008, POWERSTATE_QUIT, 0, 1, 5, SPIN_CONTROL_TEXT);
403
404   AddCategory(4, "debug", 14092);
405   AddBool(1, "debug.showloginfo", 20191, false);
406   AddPath(2, "debug.screenshotpath",20004,"select writable folder",BUTTON_CONTROL_PATH_INPUT,false,657);
407
408   AddCategory(4, "masterlock", 12360);
409   AddString(1, "masterlock.lockcode"       , 20100, "-", BUTTON_CONTROL_STANDARD);
410   AddBool(4, "masterlock.startuplock"      , 20076,false);
411   // hidden masterlock settings
412   AddInt(0,"masterlock.maxretries", 12364, 3, 3, 1, 100, SPIN_CONTROL_TEXT);
413
414   AddCategory(4, "cache", 439);
415   AddInt(0, "cache.harddisk", 14025, 256, 0, 256, 4096, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
416   AddSeparator(0, "cache.sep1");
417   AddInt(0, "cachevideo.dvdrom", 14026, 2048, 0, 256, 16384, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
418   AddInt(0, "cachevideo.lan", 14027, 2048, 0, 256, 16384, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
419   AddInt(0, "cachevideo.internet", 14028, 4096, 0, 256, 16384, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
420   AddSeparator(0, "cache.sep2");
421   AddInt(0, "cacheaudio.dvdrom", 14030, 256, 0, 256, 4096, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
422   AddInt(0, "cacheaudio.lan", 14031, 256, 0, 256, 4096, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
423   AddInt(0, "cacheaudio.internet", 14032, 256, 0, 256, 4096, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
424   AddSeparator(0, "cache.sep3");
425   AddInt(0, "cachedvd.dvdrom", 14034, 2048, 0, 256, 16384, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
426   AddInt(0, "cachedvd.lan", 14035, 2048, 0, 256, 16384, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
427   AddSeparator(0, "cache.sep4");
428   AddInt(0, "cacheunknown.internet", 14060, 4096, 0, 256, 16384, SPIN_CONTROL_INT_PLUS, MASK_KB, TEXT_OFF);
429
430   // video settings
431   AddGroup(5, 3);
432   AddCategory(5, "videolibrary", 14022);
433   AddBool(0, "videolibrary.enabled", 418, true);
434   AddBool(3, "videolibrary.showunwatchedplots", 20369, true);
435   AddBool(0, "videolibrary.seasonthumbs", 20382, true);
436   AddBool(5, "videolibrary.actorthumbs", 20402, true);
437   AddInt(0, "videolibrary.flattentvshows", 20412, 1, 0, 1, 2, SPIN_CONTROL_TEXT);
438   AddBool(8, "videolibrary.updateonstartup", 22000, false);
439   AddBool(0, "videolibrary.backgroundupdate", 22001, false);
440   AddSeparator(10, "videolibrary.sep3");
441   AddString(11, "videolibrary.cleanup", 334, "", BUTTON_CONTROL_STANDARD);
442   AddString(12, "videolibrary.export", 647, "", BUTTON_CONTROL_STANDARD);
443   AddString(13, "videolibrary.import", 648, "", BUTTON_CONTROL_STANDARD);
444
445   AddCategory(5, "videoplayer", 14086);
446   AddInt(1, "videoplayer.resumeautomatically", 12017, RESUME_ASK, RESUME_NO, 1, RESUME_ASK, SPIN_CONTROL_TEXT);
447   AddSeparator(2, "videoplayer.sep1");
448 #ifdef HAVE_LIBVDPAU
449   AddInt(3, "videoplayer.rendermethod", 13415, RENDER_METHOD_AUTO, RENDER_METHOD_AUTO, 1, RENDER_METHOD_VDPAU, SPIN_CONTROL_TEXT);
450 #elif defined(_WIN32) && defined(HAS_DX)
451   // No render methods other than AUTO on win32 DirectX
452   AddInt(0, "videoplayer.rendermethod", 13415, RENDER_METHOD_AUTO, RENDER_METHOD_AUTO, 1, RENDER_METHOD_AUTO, SPIN_CONTROL_TEXT);
453 #else
454   AddInt(3, "videoplayer.rendermethod", 13415, RENDER_METHOD_AUTO, RENDER_METHOD_AUTO, 1, RENDER_METHOD_SOFTWARE, SPIN_CONTROL_TEXT);
455 #endif
456   // FIXME: hide this setting until it is properly respected. In the meanwhile, default to AUTO.
457   //AddInt(5, "videoplayer.displayresolution", 169, (int)RES_AUTORES, (int)RES_AUTORES, 1, (int)CUSTOM+MAX_RESOLUTIONS, SPIN_CONTROL_TEXT);
458   AddInt(0, "videoplayer.displayresolution", 169, (int)RES_AUTORES, (int)RES_AUTORES, 1, (int)RES_AUTORES, SPIN_CONTROL_TEXT);
459   AddBool(4, "videoplayer.adjustrefreshrate", 170, false);
460   //sync settings not available on windows gl build
461 #if defined(_WIN32) && defined(HAS_GL)
462   #define SYNCSETTINGS 0
463 #else
464   #define SYNCSETTINGS 1
465 #endif
466   AddBool(SYNCSETTINGS ? 5 : 0, "videoplayer.usedisplayasclock", 13510, false);
467   AddInt(SYNCSETTINGS ? 6 : 0, "videoplayer.synctype", 13500, SYNC_DISCON, SYNC_DISCON, 1, SYNC_RESAMPLE, SPIN_CONTROL_TEXT);
468   AddFloat(0, "videoplayer.maxspeedadjust", 13504, 5.0f, 0.0f, 0.1f, 10.0f);
469   AddInt(0, "videoplayer.resamplequality", 13505, RESAMPLE_MID, RESAMPLE_LOW, 1, RESAMPLE_REALLYHIGH, SPIN_CONTROL_TEXT);
470   AddInt(7, "videoplayer.errorinaspect", 22021, 0, 0, 1, 20, SPIN_CONTROL_INT_PLUS, MASK_PERCENT, TEXT_NONE);
471 #ifdef HAVE_LIBVDPAU
472   AddBool(0, "videoplayer.strictbinding", 13120, false);
473   AddBool(0, "videoplayer.vdpau_allow_xrandr", 13122, false);
474 #endif
475 #ifdef HAS_GL
476   AddSeparator(8, "videoplayer.sep1.5");
477   AddInt(0, "videoplayer.highqualityupscaling", 13112, SOFTWARE_UPSCALING_DISABLED, SOFTWARE_UPSCALING_DISABLED, 1, SOFTWARE_UPSCALING_ALWAYS, SPIN_CONTROL_TEXT);
478   AddInt(0, "videoplayer.upscalingalgorithm", 13116, VS_SCALINGMETHOD_BICUBIC_SOFTWARE, VS_SCALINGMETHOD_BICUBIC_SOFTWARE, 1, VS_SCALINGMETHOD_VDPAU_HARDWARE, SPIN_CONTROL_TEXT);
479 #ifdef HAVE_LIBVDPAU
480   AddBool(0, "videoplayer.vdpauUpscalingLevel", 13121, false);
481   AddBool(11, "videoplayer.vdpaustudiolevel", 13122, false);
482 #endif
483 #endif
484   AddSeparator(12, "videoplayer.sep5");
485   AddBool(13, "videoplayer.teletextenabled", 23050, true);
486
487   AddCategory(5, "myvideos", 14081);
488   AddBool(0, "myvideos.treatstackasfile", 20051, true);
489   AddBool(2, "myvideos.extractflags",20433, true);
490   AddBool(3, "myvideos.cleanstrings", 20418, false);
491   AddBool(0, "myvideos.extractthumb",20433, true);
492
493   AddCategory(5, "subtitles", 287);
494   AddString(1, "subtitles.font", 14089, "arial.ttf", SPIN_CONTROL_TEXT);
495   AddInt(2, "subtitles.height", 289, 28, 16, 2, 74, SPIN_CONTROL_TEXT); // use text as there is a disk based lookup needed
496   AddInt(3, "subtitles.style", 736, FONT_STYLE_BOLD, FONT_STYLE_NORMAL, 1, FONT_STYLE_BOLD_ITALICS, SPIN_CONTROL_TEXT);
497   AddInt(4, "subtitles.color", 737, SUBTITLE_COLOR_START + 1, SUBTITLE_COLOR_START, 1, SUBTITLE_COLOR_END, SPIN_CONTROL_TEXT);
498   AddString(5, "subtitles.charset", 735, "DEFAULT", SPIN_CONTROL_TEXT);
499   AddSeparator(7, "subtitles.sep1");
500   AddPath(11, "subtitles.custompath", 21366, "", BUTTON_CONTROL_PATH_INPUT, false, 657);
501
502   AddCategory(5, "dvds", 14087);
503   AddBool(1, "dvds.autorun", 14088, false);
504   AddInt(2, "dvds.playerregion", 21372, 0, 0, 1, 8, SPIN_CONTROL_INT_PLUS, -1, TEXT_OFF);
505   AddBool(3, "dvds.automenu", 21882, false);
506
507   // Don't add the category - makes them hidden in the GUI
508   //AddCategory(5, "postprocessing", 14041);
509   AddBool(2, "postprocessing.enable", 286, false);
510   AddBool(3, "postprocessing.auto", 307, true); // only has effect if PostProcessing.Enable is on.
511   AddBool(4, "postprocessing.verticaldeblocking", 308, false);
512   AddInt(5, "postprocessing.verticaldeblocklevel", 308, 0, 0, 1, 100, SPIN_CONTROL_INT);
513   AddBool(6, "postprocessing.horizontaldeblocking", 309, false);
514   AddInt(7, "postprocessing.horizontaldeblocklevel", 309, 0, 0, 1, 100, SPIN_CONTROL_INT);
515   AddBool(8, "postprocessing.autobrightnesscontrastlevels", 310, false);
516   AddBool(9, "postprocessing.dering", 311, false);
517
518   AddCategory(5, "scrapers", 21412);
519   AddString(1, "scrapers.moviedefault", 21413, "tmdb.xml", SPIN_CONTROL_TEXT);
520   AddString(2, "scrapers.tvshowdefault", 21414, "tvdb.xml", SPIN_CONTROL_TEXT);
521   AddString(3, "scrapers.musicvideodefault", 21415, "mtv.xml", SPIN_CONTROL_TEXT);
522   AddSeparator(4,"scrapers.sep2");
523   AddBool(5, "scrapers.langfallback", 21416, false);
524
525   // network settings
526   AddGroup(6, 705);
527
528   AddCategory(6, "services", 14036);
529   AddBool(1, "services.upnpserver", 21360, false);
530   AddBool(2, "services.upnprenderer", 21881, false);
531   AddSeparator(3,"services.sep3");
532 #ifdef HAS_WEB_SERVER
533   AddBool(4,  "services.webserver",        263, false);
534 #ifdef _LINUX
535   AddString(5,"services.webserverport",    730, (geteuid()==0)?"80":"8080", EDIT_CONTROL_NUMBER_INPUT, false, 730);
536 #else
537   AddString(5,"services.webserverport",    730, "80", EDIT_CONTROL_NUMBER_INPUT, false, 730);
538 #endif
539   AddString(6,"services.webserverusername",1048, "xbmc", EDIT_CONTROL_INPUT);
540   AddString(7,"services.webserverpassword",733, "", EDIT_CONTROL_HIDDEN_INPUT, true, 733);
541 #endif
542 #ifdef HAS_EVENT_SERVER
543   AddSeparator(8,"services.sep1");
544   AddBool(9,  "services.esenabled",         791, true);
545   AddString(0,"services.esport",            792, "9777", EDIT_CONTROL_NUMBER_INPUT, false, 792);
546   AddInt(0,   "services.esportrange",       793, 10, 1, 1, 100, SPIN_CONTROL_INT);
547   AddInt(0,   "services.esmaxclients",      797, 20, 1, 1, 100, SPIN_CONTROL_INT);
548   AddBool(10,  "services.esallinterfaces",   794, false);
549   AddInt(0,   "services.esinitialdelay",    795, 750, 5, 5, 10000, SPIN_CONTROL_INT);
550   AddInt(0,   "services.escontinuousdelay", 796, 25, 5, 5, 10000, SPIN_CONTROL_INT);
551 #endif
552 #ifdef HAS_ZEROCONF
553   AddSeparator(11, "services.sep2");
554   AddBool(12, "services.zeroconf", 1260, true);
555 #endif
556
557 #ifndef _WIN32
558   AddCategory(6, "smb", 1200);
559   AddString(3, "smb.winsserver",  1207,   "",  EDIT_CONTROL_IP_INPUT);
560   AddString(4, "smb.workgroup",   1202,   "WORKGROUP", EDIT_CONTROL_INPUT, false, 1202);
561 #endif
562
563   AddCategory(6, "network", 798);
564   if (g_application.IsStandAlone())
565   {
566 #ifndef __APPLE__
567     AddString(0, "network.interface",775,"", SPIN_CONTROL_TEXT);
568     AddInt(0, "network.assignment", 715, NETWORK_DHCP, NETWORK_DHCP, 1, NETWORK_DISABLED, SPIN_CONTROL_TEXT);
569     AddString(0, "network.ipaddress", 719, "0.0.0.0", EDIT_CONTROL_IP_INPUT);
570     AddString(0, "network.subnet", 720, "255.255.255.0", EDIT_CONTROL_IP_INPUT);
571     AddString(0, "network.gateway", 721, "0.0.0.0", EDIT_CONTROL_IP_INPUT);
572     AddString(0, "network.dns", 722, "0.0.0.0", EDIT_CONTROL_IP_INPUT);
573     AddString(0, "network.dnssuffix", 22002, "", EDIT_CONTROL_INPUT, true);
574     AddString(0, "network.essid", 776, "0.0.0.0", BUTTON_CONTROL_STANDARD);
575     AddInt(0, "network.enc", 778, ENC_NONE, ENC_NONE, 1, ENC_WPA2, SPIN_CONTROL_TEXT);
576     AddString(0, "network.key", 777, "0.0.0.0", EDIT_CONTROL_INPUT);
577 #ifndef _WIN32
578     AddString(0, "network.save", 779, "", BUTTON_CONTROL_STANDARD);
579 #endif
580     AddSeparator(0, "network.sep1");
581 #endif
582   }
583   AddBool(13, "network.usehttpproxy", 708, false);
584   AddString(14, "network.httpproxyserver", 706, "", EDIT_CONTROL_INPUT);
585   AddString(15, "network.httpproxyport", 730, "8080", EDIT_CONTROL_NUMBER_INPUT, false, 707);
586   AddString(16, "network.httpproxyusername", 1048, "", EDIT_CONTROL_INPUT);
587   AddString(17, "network.httpproxypassword", 733, "", EDIT_CONTROL_HIDDEN_INPUT,true,733);
588
589   // appearance settings
590   AddGroup(7, 480);
591   AddCategory(7,"lookandfeel", 166);
592   AddString(1, "lookandfeel.skin",166,DEFAULT_SKIN, SPIN_CONTROL_TEXT);
593   AddString(2, "lookandfeel.skintheme",15111,"SKINDEFAULT", SPIN_CONTROL_TEXT);
594   AddString(3, "lookandfeel.skincolors",14078, "SKINDEFAULT", SPIN_CONTROL_TEXT);
595   AddString(4, "lookandfeel.font",13303,"Default", SPIN_CONTROL_TEXT);
596   AddInt(5, "lookandfeel.skinzoom",20109, 0, -20, 2, 20, SPIN_CONTROL_INT, MASK_PERCENT);
597   AddInt(6, "lookandfeel.startupwindow",512,1, WINDOW_HOME, 1, WINDOW_PYTHON_END, SPIN_CONTROL_TEXT);
598   AddString(7, "lookandfeel.soundskin",15108,"SKINDEFAULT", SPIN_CONTROL_TEXT);
599   AddSeparator(8, "lookandfeel.sep2");
600   AddBool(9, "lookandfeel.enablerssfeeds",13305,  true);
601   AddString(10, "lookandfeel.rssedit", 21435, "", BUTTON_CONTROL_STANDARD);
602
603   AddCategory(7, "locale", 14090);
604   AddString(1, "locale.language",248,"english", SPIN_CONTROL_TEXT);
605   AddString(2, "locale.country", 20026, "USA", SPIN_CONTROL_TEXT);
606   AddString(3, "locale.charset", 14091, "DEFAULT", SPIN_CONTROL_TEXT); // charset is set by the language file
607 #if defined(_LINUX) && !defined(__APPLE__)
608   AddSeparator(4, "locale.sep1");
609   AddString(7, "locale.timezonecountry", 14079, g_timezone.GetCountryByTimezone(g_timezone.GetOSConfiguredTimezone()), SPIN_CONTROL_TEXT);
610   AddString(8, "locale.timezone", 14080, g_timezone.GetOSConfiguredTimezone(), SPIN_CONTROL_TEXT);
611 #endif
612 #ifdef HAS_TIME_SERVER
613   AddSeparator(9, "locale.sep2");
614   AddBool(10, "locale.timeserver", 168, false);
615   AddString(11, "locale.timeserveraddress", 731, "pool.ntp.org", EDIT_CONTROL_INPUT);
616 #endif
617
618   AddCategory(7, "filelists", 14081);
619   AddBool(1, "filelists.showparentdiritems", 13306, true);
620   AddBool(2, "filelists.showextensions", 497, true);
621   AddBool(3, "filelists.ignorethewhensorting", 13399, true);
622   AddBool(4, "filelists.allowfiledeletion", 14071, false);
623   AddBool(5, "filelists.showaddsourcebuttons", 21382,  true);
624   AddBool(6, "filelists.showhidden", 21330, false);
625
626   AddCategory(7, "screensaver", 360);
627   AddInt(1, "screensaver.time", 355, 3, 1, 1, 60, SPIN_CONTROL_INT_PLUS, MASK_MINS);
628   AddString(2, "screensaver.mode", 356, "Dim", SPIN_CONTROL_TEXT);
629   AddBool(3, "screensaver.usemusicvisinstead", 13392, true);
630   AddBool(4, "screensaver.usedimonpause", 22014, true);
631   AddSeparator(5, "screensaver.sep1");
632   AddInt(6, "screensaver.dimlevel", 362, 20, 0, 10, 80, SPIN_CONTROL_INT_PLUS, MASK_PERCENT);
633   AddPath(7, "screensaver.slideshowpath", 774, "", BUTTON_CONTROL_PATH_INPUT, false, 657);
634   AddSeparator(8, "screensaver.sep2");
635   AddString(9, "screensaver.preview", 1000, "", BUTTON_CONTROL_STANDARD);
636
637   AddCategory(7, "window", 0);
638   AddInt(0, "window.width",  0, 720, 10, 1, INT_MAX, SPIN_CONTROL_INT);
639   AddInt(0, "window.height", 0, 480, 10, 1, INT_MAX, SPIN_CONTROL_INT);
640
641   AddPath(0,"system.playlistspath",20006,"set default",BUTTON_CONTROL_PATH_INPUT,false);
642 }
643
644 CGUISettings::~CGUISettings(void)
645 {
646   Clear();
647 }
648
649 void CGUISettings::AddGroup(int groupID, int labelID)
650 {
651   CSettingsGroup *pGroup = new CSettingsGroup(groupID, labelID);
652   if (pGroup)
653     settingsGroups.push_back(pGroup);
654 }
655
656 void CGUISettings::AddCategory(int groupID, const char *strSetting, int labelID)
657 {
658   for (unsigned int i = 0; i < settingsGroups.size(); i++)
659   {
660     if (settingsGroups[i]->GetGroupID() == groupID)
661       settingsGroups[i]->AddCategory(CStdString(strSetting).ToLower(), labelID);
662   }
663 }
664
665 CSettingsGroup *CGUISettings::GetGroup(int groupID)
666 {
667   for (unsigned int i = 0; i < settingsGroups.size(); i++)
668   {
669     if (settingsGroups[i]->GetGroupID() == groupID)
670       return settingsGroups[i];
671   }
672   CLog::Log(LOGDEBUG, "Error: Requested setting group (%i) was not found.  "
673                       "It must be case-sensitive",
674             groupID);
675   return NULL;
676 }
677
678 void CGUISettings::AddSeparator(int iOrder, const char *strSetting)
679 {
680   CSettingSeparator *pSetting = new CSettingSeparator(iOrder, CStdString(strSetting).ToLower());
681   if (!pSetting) return;
682   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
683 }
684
685 void CGUISettings::AddBool(int iOrder, const char *strSetting, int iLabel, bool bData, int iControlType)
686 {
687   CSettingBool* pSetting = new CSettingBool(iOrder, CStdString(strSetting).ToLower(), iLabel, bData, iControlType);
688   if (!pSetting) return ;
689   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
690 }
691 bool CGUISettings::GetBool(const char *strSetting) const
692 {
693   ASSERT(settingsMap.size());
694   CStdString lower(strSetting);
695   lower.ToLower();
696   constMapIter it = settingsMap.find(lower);
697   if (it != settingsMap.end())
698   { // old category
699     return ((CSettingBool*)(*it).second)->GetData();
700   }
701   // Backward compatibility (skins use this setting)
702   if (lower == "lookandfeel.enablemouse")
703     return GetBool("input.enablemouse");
704   // Assert here and write debug output
705   CLog::Log(LOGDEBUG,"Error: Requested setting (%s) was not found.  It must be case-sensitive", strSetting);
706   return false;
707 }
708
709 void CGUISettings::SetBool(const char *strSetting, bool bSetting)
710 {
711   ASSERT(settingsMap.size());
712   mapIter it = settingsMap.find(CStdString(strSetting).ToLower());
713   if (it != settingsMap.end())
714   { // old category
715     ((CSettingBool*)(*it).second)->SetData(bSetting);
716     return ;
717   }
718   // Assert here and write debug output
719   CLog::Log(LOGDEBUG,"Error: Requested setting (%s) was not found.  It must be case-sensitive", strSetting);
720 }
721
722 void CGUISettings::ToggleBool(const char *strSetting)
723 {
724   ASSERT(settingsMap.size());
725   mapIter it = settingsMap.find(CStdString(strSetting).ToLower());
726   if (it != settingsMap.end())
727   { // old category
728     ((CSettingBool*)(*it).second)->SetData(!((CSettingBool *)(*it).second)->GetData());
729     return ;
730   }
731   // Assert here and write debug output
732   CLog::Log(LOGDEBUG,"Error: Requested setting (%s) was not found.  It must be case-sensitive", strSetting);
733 }
734
735 void CGUISettings::AddFloat(int iOrder, const char *strSetting, int iLabel, float fData, float fMin, float fStep, float fMax, int iControlType)
736 {
737   CSettingFloat* pSetting = new CSettingFloat(iOrder, CStdString(strSetting).ToLower(), iLabel, fData, fMin, fStep, fMax, iControlType);
738   if (!pSetting) return ;
739   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
740 }
741
742 float CGUISettings::GetFloat(const char *strSetting) const
743 {
744   ASSERT(settingsMap.size());
745   constMapIter it = settingsMap.find(CStdString(strSetting).ToLower());
746   if (it != settingsMap.end())
747   {
748     return ((CSettingFloat *)(*it).second)->GetData();
749   }
750   // Assert here and write debug output
751   //ASSERT(false);
752   CLog::Log(LOGDEBUG,"Error: Requested setting (%s) was not found.  It must be case-sensitive", strSetting);
753   return 0.0f;
754 }
755
756 void CGUISettings::SetFloat(const char *strSetting, float fSetting)
757 {
758   ASSERT(settingsMap.size());
759   mapIter it = settingsMap.find(CStdString(strSetting).ToLower());
760   if (it != settingsMap.end())
761   {
762     ((CSettingFloat *)(*it).second)->SetData(fSetting);
763     return ;
764   }
765   // Assert here and write debug output
766   ASSERT(false);
767   CLog::Log(LOGDEBUG,"Error: Requested setting (%s) was not found.  It must be case-sensitive", strSetting);
768 }
769
770 void CGUISettings::LoadMasterLock(TiXmlElement *pRootElement)
771 {
772   std::map<CStdString,CSetting*>::iterator it = settingsMap.find("masterlock.maxretries");
773   if (it != settingsMap.end())
774     LoadFromXML(pRootElement, it);
775   it = settingsMap.find("masterlock.startuplock");
776   if (it != settingsMap.end())
777     LoadFromXML(pRootElement, it);
778     it = settingsMap.find("autodetect.nickname");
779   if (it != settingsMap.end())
780     LoadFromXML(pRootElement, it);
781 }
782
783
784 void CGUISettings::AddInt(int iOrder, const char *strSetting, int iLabel, int iData, int iMin, int iStep, int iMax, int iControlType, const char *strFormat)
785 {
786   CSettingInt* pSetting = new CSettingInt(iOrder, CStdString(strSetting).ToLower(), iLabel, iData, iMin, iStep, iMax, iControlType, strFormat);
787   if (!pSetting) return ;
788   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
789 }
790
791 void CGUISettings::AddInt(int iOrder, const char *strSetting, int iLabel, int iData, int iMin, int iStep, int iMax, int iControlType, int iFormat, int iLabelMin/*=-1*/)
792 {
793   CSettingInt* pSetting = new CSettingInt(iOrder, CStdString(strSetting).ToLower(), iLabel, iData, iMin, iStep, iMax, iControlType, iFormat, iLabelMin);
794   if (!pSetting) return ;
795   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
796 }
797
798 void CGUISettings::AddHex(int iOrder, const char *strSetting, int iLabel, int iData, int iMin, int iStep, int iMax, int iControlType, const char *strFormat)
799 {
800   CSettingHex* pSetting = new CSettingHex(iOrder, CStdString(strSetting).ToLower(), iLabel, iData, iMin, iStep, iMax, iControlType, strFormat);
801   if (!pSetting) return ;
802   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
803 }
804
805 int CGUISettings::GetInt(const char *strSetting) const
806 {
807   ASSERT(settingsMap.size());
808   constMapIter it = settingsMap.find(CStdString(strSetting).ToLower());
809   if (it != settingsMap.end())
810   {
811     return ((CSettingInt *)(*it).second)->GetData();
812   }
813   // Assert here and write debug output
814   CLog::Log(LOGERROR,"Error: Requested setting (%s) was not found.  It must be case-sensitive", strSetting);
815   //ASSERT(false);
816   return 0;
817 }
818
819 void CGUISettings::SetInt(const char *strSetting, int iSetting)
820 {
821   ASSERT(settingsMap.size());
822   mapIter it = settingsMap.find(CStdString(strSetting).ToLower());
823   if (it != settingsMap.end())
824   {
825     ((CSettingInt *)(*it).second)->SetData(iSetting);
826     return ;
827   }
828   // Assert here and write debug output
829   ASSERT(false);
830 }
831
832 void CGUISettings::AddString(int iOrder, const char *strSetting, int iLabel, const char *strData, int iControlType, bool bAllowEmpty, int iHeadingString)
833 {
834   CSettingString* pSetting = new CSettingString(iOrder, CStdString(strSetting).ToLower(), iLabel, strData, iControlType, bAllowEmpty, iHeadingString);
835   if (!pSetting) return ;
836   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
837 }
838
839 void CGUISettings::AddPath(int iOrder, const char *strSetting, int iLabel, const char *strData, int iControlType, bool bAllowEmpty, int iHeadingString)
840 {
841   CSettingPath* pSetting = new CSettingPath(iOrder, CStdString(strSetting).ToLower(), iLabel, strData, iControlType, bAllowEmpty, iHeadingString);
842   if (!pSetting) return ;
843   settingsMap.insert(pair<CStdString, CSetting*>(CStdString(strSetting).ToLower(), pSetting));
844 }
845
846 const CStdString &CGUISettings::GetString(const char *strSetting, bool bPrompt) const
847 {
848   ASSERT(settingsMap.size());
849   constMapIter it = settingsMap.find(CStdString(strSetting).ToLower());
850   if (it != settingsMap.end())
851   {
852     CSettingString* result = ((CSettingString *)(*it).second);
853     if (result->GetData() == "select folder" || result->GetData() == "select writable folder")
854     {
855       CStdString strData = "";
856       if (bPrompt)
857       {
858         VECSOURCES shares;
859         g_mediaManager.GetLocalDrives(shares);
860         if (CGUIDialogFileBrowser::ShowAndGetDirectory(shares,g_localizeStrings.Get(result->GetLabel()),strData,result->GetData() == "select writable folder"))
861         {
862           result->SetData(strData);
863           g_settings.Save();
864         }
865         else
866           return StringUtils::EmptyString;
867       }
868       else
869         return StringUtils::EmptyString;
870     }
871     return result->GetData();
872   }
873   // Assert here and write debug output
874   CLog::Log(LOGDEBUG,"Error: Requested setting (%s) was not found.  It must be case-sensitive", strSetting);
875   //ASSERT(false);
876   // hardcoded return value so that compiler is happy
877   return StringUtils::EmptyString;
878 }
879
880 void CGUISettings::SetString(const char *strSetting, const char *strData)
881 {
882   ASSERT(settingsMap.size());
883   mapIter it = settingsMap.find(CStdString(strSetting).ToLower());
884   if (it != settingsMap.end())
885   {
886     ((CSettingString *)(*it).second)->SetData(strData);
887     return ;
888   }
889   // Assert here and write debug output
890   ASSERT(false);
891   CLog::Log(LOGDEBUG,"Error: Requested setting (%s) was not found.  It must be case-sensitive", strSetting);
892 }
893
894 CSetting *CGUISettings::GetSetting(const char *strSetting)
895 {
896   ASSERT(settingsMap.size());
897   mapIter it = settingsMap.find(CStdString(strSetting).ToLower());
898   if (it != settingsMap.end())
899     return (*it).second;
900   else
901     return NULL;
902 }
903
904 // get all the settings beginning with the term "strGroup"
905 void CGUISettings::GetSettingsGroup(const char *strGroup, vecSettings &settings)
906 {
907   vecSettings unorderedSettings;
908   for (mapIter it = settingsMap.begin(); it != settingsMap.end(); it++)
909   {
910     if ((*it).first.Left(strlen(strGroup)).Equals(strGroup) && (*it).second->GetOrder() > 0 && !(*it).second->IsAdvanced())
911       unorderedSettings.push_back((*it).second);
912   }
913   // now order them...
914   sort(unorderedSettings.begin(), unorderedSettings.end(), sortsettings());
915
916   // remove any instances of 2 separators in a row
917   bool lastWasSeparator(false);
918   for (vecSettings::iterator it = unorderedSettings.begin(); it != unorderedSettings.end(); it++)
919   {
920     CSetting *setting = *it;
921     // only add separators if we don't have 2 in a row
922     if (setting->GetType() == SETTINGS_TYPE_SEPARATOR)
923     {
924       if (!lastWasSeparator)
925         settings.push_back(setting);
926       lastWasSeparator = true;
927     }
928     else
929     {
930       lastWasSeparator = false;
931       settings.push_back(setting);
932     }
933   }
934 }
935
936 void CGUISettings::LoadXML(TiXmlElement *pRootElement, bool hideSettings /* = false */)
937 { // load our stuff...
938   for (mapIter it = settingsMap.begin(); it != settingsMap.end(); it++)
939   {
940     LoadFromXML(pRootElement, it, hideSettings);
941   }
942   // Get hardware based stuff...
943   CLog::Log(LOGNOTICE, "Getting hardware information now...");
944   if (GetInt("audiooutput.mode") == AUDIO_DIGITAL && !g_audioConfig.HasDigitalOutput())
945     SetInt("audiooutput.mode", AUDIO_ANALOG);
946   // FIXME: Check if the hardware supports it (if possible ;)
947   //SetBool("audiooutput.ac3passthrough", g_audioConfig.GetAC3Enabled());
948   //SetBool("audiooutput.dtspassthrough", g_audioConfig.GetDTSEnabled());
949   CLog::Log(LOGINFO, "Using %s output", GetInt("audiooutput.mode") == AUDIO_ANALOG ? "analog" : "digital");
950   CLog::Log(LOGINFO, "AC3 pass through is %s", GetBool("audiooutput.ac3passthrough") ? "enabled" : "disabled");
951   CLog::Log(LOGINFO, "DTS pass through is %s", GetBool("audiooutput.dtspassthrough") ? "enabled" : "disabled");
952
953   g_guiSettings.m_LookAndFeelResolution = GetResolution();
954 #ifdef __APPLE__
955   // trap any previous vsync by driver setting, does not exist on OSX
956   if (GetInt("videoscreen.vsync") == VSYNC_DRIVER)
957   {
958     SetInt("videoscreen.vsync", VSYNC_ALWAYS);
959   }
960 #endif
961   // if AppleTV, trap any previous highqualityupscaling setting and set to zero
962   if (g_sysinfo.IsAppleTV())
963   {
964     if (GetInt("videoplayer.highqualityupscaling") != SOFTWARE_UPSCALING_DISABLED)
965     {
966       SetInt("videoplayer.highqualityupscaling", SOFTWARE_UPSCALING_DISABLED);
967     }
968   }
969  // DXMERGE: This might have been useful?
970  // g_videoConfig.SetVSyncMode((VSYNC)GetInt("videoscreen.vsync"));
971   CLog::Log(LOGNOTICE, "Checking resolution %i", g_guiSettings.m_LookAndFeelResolution);
972   if (!g_graphicsContext.IsValidResolution(g_guiSettings.m_LookAndFeelResolution))
973   {
974     CLog::Log(LOGNOTICE, "Setting safe mode %i", RES_DESKTOP);
975     SetResolution(RES_DESKTOP);
976   }
977
978   // Move replaygain settings into our struct
979   m_replayGain.iPreAmp = GetInt("musicplayer.replaygainpreamp");
980   m_replayGain.iNoGainPreAmp = GetInt("musicplayer.replaygainnogainpreamp");
981   m_replayGain.iType = GetInt("musicplayer.replaygaintype");
982   m_replayGain.bAvoidClipping = GetBool("musicplayer.replaygainavoidclipping");
983
984   // check if we load the right vis
985   if(!CVisualisation::IsValidVisualisation(g_guiSettings.GetString("musicplayer.visualisation")))
986     g_guiSettings.SetString("musicplayer.visualisation", DEFAULT_VISUALISATION);
987
988 #if defined(_LINUX) && !defined(__APPLE__)
989   CStdString timezone = GetString("locale.timezone");
990   if(timezone == "0" || timezone.IsEmpty())
991   {
992     timezone = g_timezone.GetOSConfiguredTimezone();
993     SetString("locale.timezone", timezone);
994   }
995   g_timezone.SetTimezone(timezone);
996 #endif
997 }
998
999 void CGUISettings::LoadFromXML(TiXmlElement *pRootElement, mapIter &it, bool advanced /* = false */)
1000 {
1001   CStdStringArray strSplit;
1002   StringUtils::SplitString((*it).first, ".", strSplit);
1003   if (strSplit.size() > 1)
1004   {
1005     const TiXmlNode *pChild = pRootElement->FirstChild(strSplit[0].c_str());
1006     if (pChild)
1007     {
1008       const TiXmlElement *pGrandChild = pChild->FirstChildElement(strSplit[1].c_str());
1009       if (pGrandChild && pGrandChild->FirstChild())
1010       {
1011         CStdString strValue = pGrandChild->FirstChild()->Value();
1012         if (strValue.size() )
1013         {
1014           if (strValue != "-")
1015           { // update our item
1016             if ((*it).second->GetType() == SETTINGS_TYPE_PATH)
1017             { // check our path
1018               int pathVersion = 0;
1019               pGrandChild->Attribute("pathversion", &pathVersion);
1020               strValue = CSpecialProtocol::ReplaceOldPath(strValue, pathVersion);
1021             }
1022             (*it).second->FromString(strValue);
1023             if (advanced)
1024               (*it).second->SetAdvanced();
1025           }
1026         }
1027       }
1028     }
1029   }
1030 }
1031
1032 void CGUISettings::SaveXML(TiXmlNode *pRootNode)
1033 {
1034   for (mapIter it = settingsMap.begin(); it != settingsMap.end(); it++)
1035   {
1036     // don't save advanced settings
1037     CStdString first = (*it).first;
1038     if ((*it).second->IsAdvanced())
1039       continue;
1040
1041     CStdStringArray strSplit;
1042     StringUtils::SplitString((*it).first, ".", strSplit);
1043     if (strSplit.size() > 1)
1044     {
1045       TiXmlNode *pChild = pRootNode->FirstChild(strSplit[0].c_str());
1046       if (!pChild)
1047       { // add our group tag
1048         TiXmlElement newElement(strSplit[0].c_str());
1049         pChild = pRootNode->InsertEndChild(newElement);
1050       }
1051
1052       if (pChild)
1053       { // successfully added (or found) our group
1054         TiXmlElement newElement(strSplit[1]);
1055         if ((*it).second->GetControlType() == SETTINGS_TYPE_PATH)
1056           newElement.SetAttribute("pathversion", CSpecialProtocol::path_version);
1057         TiXmlNode *pNewNode = pChild->InsertEndChild(newElement);
1058         if (pNewNode)
1059         {
1060           TiXmlText value((*it).second->ToString());
1061           pNewNode->InsertEndChild(value);
1062         }
1063       }
1064     }
1065   }
1066 }
1067
1068 void CGUISettings::Clear()
1069 {
1070   for (mapIter it = settingsMap.begin(); it != settingsMap.end(); it++)
1071     delete (*it).second;
1072   settingsMap.clear();
1073   for (unsigned int i = 0; i < settingsGroups.size(); i++)
1074     delete settingsGroups[i];
1075   settingsGroups.clear();
1076 }
1077
1078 float square_error(float x, float y)
1079 {
1080   float yonx = (x > 0) ? y / x : 0;
1081   float xony = (y > 0) ? x / y : 0;
1082   return std::max(yonx, xony);
1083 }
1084
1085 RESOLUTION CGUISettings::GetResolution() const
1086 {
1087   return GetResFromString(GetString("videoscreen.screenmode"));
1088 }
1089
1090 RESOLUTION CGUISettings::GetResFromString(const CStdString &res)
1091 {
1092   if (res == "DESKTOP")
1093     return RES_DESKTOP;
1094   else if (res == "WINDOW")
1095     return RES_WINDOW;
1096   else if (res.GetLength()==20)
1097   {
1098     // format: SWWWWWHHHHHRRR.RRRRR, where S = screen, W = width, H = height, R = refresh
1099     int screen = atol(res.Mid(0,1).c_str());
1100     int width = atol(res.Mid(1,5).c_str());
1101     int height = atol(res.Mid(6,5).c_str());
1102     float refresh = (float)atof(res.Mid(11).c_str());
1103     // find the closest match to these in our res vector.  If we have the screen, we score the res
1104     RESOLUTION bestRes = RES_DESKTOP;
1105     float bestScore = FLT_MAX;
1106     size_t maxRes = g_settings.m_ResInfo.size();
1107     if (g_Windowing.GetNumScreens())
1108       maxRes = std::min(maxRes, (size_t)RES_DESKTOP + g_Windowing.GetNumScreens());
1109     for (unsigned int i = RES_DESKTOP; i < maxRes; ++i)
1110     {
1111       const RESOLUTION_INFO &info = g_settings.m_ResInfo[i];
1112       if (info.iScreen != screen)
1113         continue;
1114       float score = 10*(square_error((float)info.iWidth, (float)width) + square_error((float)info.iHeight, (float)height)) + square_error(info.fRefreshRate, refresh);
1115       if (score < bestScore)
1116       {
1117         bestScore = score;
1118         bestRes = (RESOLUTION)i;
1119       }
1120     }
1121     return bestRes;
1122   }
1123   return RES_DESKTOP;
1124 }
1125
1126 void CGUISettings::SetResolution(RESOLUTION res)
1127 {
1128   CStdString mode;
1129   if (res == RES_DESKTOP)
1130     mode = "DESKTOP";
1131   else if (res == RES_WINDOW)
1132     mode = "WINDOW";
1133   else if (res >= RES_CUSTOM && res < (RESOLUTION)g_settings.m_ResInfo.size())
1134   {
1135     const RESOLUTION_INFO &info = g_settings.m_ResInfo[res];
1136     mode.Format("%1i%05i%05i%09.5f", info.iScreen, info.iWidth, info.iHeight, info.fRefreshRate);
1137   }
1138   else
1139   {
1140     CLog::Log(LOGWARNING, "%s, setting invalid resolution %i", __FUNCTION__, res);
1141     mode = "DESKTOP";
1142   }
1143   SetString("videoscreen.screenmode", mode);
1144   m_LookAndFeelResolution = res;
1145 }