fixed: typo (missing |)
[xbmc:xbmc.git] / xbmc / Settings.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 "system.h"
23 #include "Settings.h"
24 #include "AdvancedSettings.h"
25 #include "Application.h"
26 #include "KeyboardLayoutConfiguration.h"
27 #include "Util.h"
28 #include "URL.h"
29 #include "GUIWindowFileManager.h"
30 #include "GUIDialogButtonMenu.h"
31 #include "GUIFontManager.h"
32 #include "LangCodeExpander.h"
33 #include "ButtonTranslator.h"
34 #include "XMLUtils.h"
35 #include "utils/PasswordManager.h"
36 #include "utils/RegExp.h"
37 #include "GUIPassword.h"
38 #include "GUIAudioManager.h"
39 #include "AudioContext.h"
40 #include "utils/GUIInfoManager.h"
41 #include "utils/Network.h"
42 #include "FileSystem/MultiPathDirectory.h"
43 #include "FileSystem/SpecialProtocol.h"
44 #include "GUIBaseContainer.h" // for VIEW_TYPE enum
45 #include "MediaManager.h"
46 #include "DNSNameCache.h"
47 #include "GUIWindowManager.h"
48 #include "GUIDialogYesNo.h"
49 #include "FileSystem/Directory.h"
50 #include "FileItem.h"
51 #include "LangInfo.h"
52 #include "LocalizeStrings.h"
53 #include "StringUtils.h"
54 #include "SystemInfo.h"
55 #ifdef _WIN32
56 #include "win32/WIN32Util.h"
57 #endif
58 #if defined(_LINUX) && defined(HAS_FILESYSTEM_SMB)
59 #include "FileSystem/SMBDirectory.h"
60 #endif
61 #include "playercorefactory/PlayerCoreFactory.h"
62 #include "utils/FileUtils.h"
63 #include "MouseStat.h"
64
65 using namespace std;
66 using namespace XFILE;
67
68 class CSettings g_settings;
69
70 CSettings::CSettings(void)
71 {
72 }
73
74 void CSettings::Initialize()
75 {
76   RESOLUTION_INFO res;
77   vector<RESOLUTION_INFO>::iterator it = m_ResInfo.begin();
78
79   m_ResInfo.insert(it, RES_CUSTOM, res);
80
81   for (int i = RES_HDTV_1080i; i <= RES_PAL60_16x9; i++)
82   {
83     g_graphicsContext.ResetScreenParameters((RESOLUTION)i);
84     g_graphicsContext.ResetOverscan((RESOLUTION)i, m_ResInfo[i].Overscan);
85   }
86
87   m_iMyVideoStack = STACK_NONE;
88
89   m_bMyMusicSongInfoInVis = true;    // UNUSED - depreciated.
90   m_bMyMusicSongThumbInVis = false;  // used for music info in vis screen
91
92   m_bMyMusicPlaylistRepeat = false;
93   m_bMyMusicPlaylistShuffle = false;
94
95   m_bMyVideoPlaylistRepeat = false;
96   m_bMyVideoPlaylistShuffle = false;
97   m_bMyVideoNavFlatten = false;
98   m_bStartVideoWindowed = false;
99   m_bAddonAutoUpdate = false;
100   m_bAddonNotifications = true;
101
102   m_nVolumeLevel = 0;
103   m_dynamicRangeCompressionLevel = 0;
104   m_iPreMuteVolumeLevel = 0;
105   m_bMute = false;
106   m_fZoomAmount = 1.0f;
107   m_fPixelRatio = 1.0f;
108   m_bNonLinStretch = false;
109
110   m_pictureExtensions = ".png|.jpg|.jpeg|.bmp|.gif|.ico|.tif|.tiff|.tga|.pcx|.cbz|.zip|.cbr|.rar|.m3u|.dng|.nef|.cr2|.crw|.orf|.arw|.erf|.3fr|.dcr|.x3f|.mef|.raf|.mrw|.pef|.sr2|.rss";
111   m_musicExtensions = ".nsv|.m4a|.flac|.aac|.strm|.pls|.rm|.rma|.mpa|.wav|.wma|.ogg|.mp3|.mp2|.m3u|.mod|.amf|.669|.dmf|.dsm|.far|.gdm|.imf|.it|.m15|.med|.okt|.s3m|.stm|.sfx|.ult|.uni|.xm|.sid|.ac3|.dts|.cue|.aif|.aiff|.wpl|.ape|.mac|.mpc|.mp+|.mpp|.shn|.zip|.rar|.wv|.nsf|.spc|.gym|.adx|.dsp|.adp|.ymf|.ast|.afc|.hps|.xsp|.xwav|.waa|.wvs|.wam|.gcm|.idsp|.mpdsp|.mss|.spt|.rsd|.mid|.kar|.sap|.cmc|.cmr|.dmc|.mpt|.mpd|.rmt|.tmc|.tm8|.tm2|.oga|.url|.pxml|.tta|.rss|.cm3|.cms|.dlt|.brstm";
112   m_videoExtensions = ".m4v|.3g2|.3gp|.nsv|.tp|.ts|.ty|.strm|.pls|.rm|.rmvb|.m3u|.ifo|.mov|.qt|.divx|.xvid|.bivx|.vob|.nrg|.img|.iso|.pva|.wmv|.asf|.asx|.ogm|.m2v|.avi|.bin|.dat|.mpg|.mpeg|.mp4|.mkv|.avc|.vp3|.svq3|.nuv|.viv|.dv|.fli|.flv|.rar|.001|.wpl|.zip|.vdr|.dvr-ms|.xsp|.mts|.m2t|.m2ts|.evo|.ogv|.sdp|.avs|.rec|.url|.pxml|.vc1|.h264|.rcv|.rss|.mpls|.webm|.bdmv";
113   // internal music extensions
114   m_musicExtensions += "|.sidstream|.oggstream|.nsfstream|.asapstream|.cdda";
115
116   #ifdef __APPLE__
117     CStdString logDir = getenv("HOME");
118     logDir += "/Library/Logs/";
119     m_logFolder = logDir;
120   #else
121     m_logFolder = "special://home/";              // log file location
122   #endif
123
124   // defaults for scanning
125   m_bMyMusicIsScanning = false;
126
127   iAdditionalSubtitleDirectoryChecked = 0;
128   m_iMyMusicStartWindow = WINDOW_MUSIC_FILES;
129   m_iVideoStartWindow = WINDOW_VIDEO_FILES;
130
131   m_watchMode["movies"] = VIDEO_SHOW_ALL;
132   m_watchMode["tvshows"] = VIDEO_SHOW_ALL;
133   m_watchMode["musicvideos"] = VIDEO_SHOW_ALL;
134
135   m_iSystemTimeTotalUp = 0;
136   m_HttpApiBroadcastLevel = 0;
137   m_HttpApiBroadcastPort = 8278;
138
139   m_userAgent = g_sysinfo.GetUserAgent();
140
141   m_usingLoginScreen = false;
142   m_lastUsedProfile = 0;
143   m_currentProfile = 0;
144 }
145
146 CSettings::~CSettings(void)
147 {
148   m_ResInfo.clear();
149 }
150
151
152 void CSettings::Save() const
153 {
154   if (g_application.m_bStop)
155   {
156     //don't save settings when we're busy stopping the application
157     //a lot of screens try to save settings on deinit and deinit is called
158     //for every screen when the application is stopping.
159     return ;
160   }
161   if (!SaveSettings(GetSettingsFile()))
162   {
163     CLog::Log(LOGERROR, "Unable to save settings to %s", GetSettingsFile().c_str());
164   }
165 }
166
167 bool CSettings::Reset()
168 {
169   CLog::Log(LOGINFO, "Resetting settings");
170   CFile::Delete(GetSettingsFile());
171   Save();
172   return LoadSettings(GetSettingsFile());
173 }
174
175 bool CSettings::Load()
176 {
177   CSpecialProtocol::SetProfilePath(GetProfileUserDataFolder());
178   CLog::Log(LOGNOTICE, "loading %s", GetSettingsFile().c_str());
179   if (!LoadSettings(GetSettingsFile()))
180   {
181     CLog::Log(LOGERROR, "Unable to load %s, creating new %s with default values", GetSettingsFile().c_str(), GetSettingsFile().c_str());
182     if (!Reset())
183       return false;
184   }
185
186   LoadSources();
187   LoadRSSFeeds();
188   LoadUserFolderLayout();
189
190   return true;
191 }
192
193 VECSOURCES *CSettings::GetSourcesFromType(const CStdString &type)
194 {
195   if (type == "programs" || type == "myprograms")
196     return &m_programSources;
197   else if (type == "files")
198     return &m_fileSources;
199   else if (type == "music")
200     return &m_musicSources;
201   else if (type == "video")
202     return &m_videoSources;
203   else if (type == "pictures")
204     return &m_pictureSources;
205
206   return NULL;
207 }
208
209 CStdString CSettings::GetDefaultSourceFromType(const CStdString &type)
210 {
211   CStdString defaultShare;
212   if (type == "programs" || type == "myprograms")
213     defaultShare = m_defaultProgramSource;
214   else if (type == "files")
215     defaultShare = m_defaultFileSource;
216   else if (type == "music")
217     defaultShare = m_defaultMusicSource;
218   else if (type == "video")
219     defaultShare = m_defaultVideoSource;
220   else if (type == "pictures")
221     defaultShare = m_defaultPictureSource;
222   return defaultShare;
223 }
224
225 void CSettings::GetSources(const TiXmlElement* pRootElement, const CStdString& strTagName, VECSOURCES& items, CStdString& strDefault)
226 {
227   //CLog::Log(LOGDEBUG, "  Parsing <%s> tag", strTagName.c_str());
228   strDefault = "";
229
230   items.clear();
231   const TiXmlNode *pChild = pRootElement->FirstChild(strTagName.c_str());
232   if (pChild)
233   {
234     pChild = pChild->FirstChild();
235     while (pChild > 0)
236     {
237       CStdString strValue = pChild->Value();
238       if (strValue == "source" || strValue == "bookmark") // "bookmark" left in for backwards compatibility
239       {
240         CMediaSource share;
241         if (GetSource(strTagName, pChild, share))
242         {
243           items.push_back(share);
244         }
245         else
246         {
247           CLog::Log(LOGERROR, "    Missing or invalid <name> and/or <path> in source");
248         }
249       }
250
251       if (strValue == "default")
252       {
253         const TiXmlNode *pValueNode = pChild->FirstChild();
254         if (pValueNode)
255         {
256           const char* pszText = pChild->FirstChild()->Value();
257           if (strlen(pszText) > 0)
258             strDefault = pszText;
259           CLog::Log(LOGDEBUG, "    Setting <default> source to : %s", strDefault.c_str());
260         }
261       }
262       pChild = pChild->NextSibling();
263     }
264   }
265   else
266   {
267     CLog::Log(LOGDEBUG, "  <%s> tag is missing or sources.xml is malformed", strTagName.c_str());
268   }
269 }
270
271 bool CSettings::GetSource(const CStdString &category, const TiXmlNode *source, CMediaSource &share)
272 {
273   //CLog::Log(LOGDEBUG,"    ---- SOURCE START ----");
274   const TiXmlNode *pNodeName = source->FirstChild("name");
275   CStdString strName;
276   if (pNodeName && pNodeName->FirstChild())
277   {
278     strName = pNodeName->FirstChild()->Value();
279     //CLog::Log(LOGDEBUG,"    Found name: %s", strName.c_str());
280   }
281   // get multiple paths
282   vector<CStdString> vecPaths;
283   const TiXmlElement *pPathName = source->FirstChildElement("path");
284   while (pPathName)
285   {
286     if (pPathName->FirstChild())
287     {
288       int pathVersion = 0;
289       pPathName->Attribute("pathversion", &pathVersion);
290       CStdString strPath = pPathName->FirstChild()->Value();
291       strPath = CSpecialProtocol::ReplaceOldPath(strPath, pathVersion);
292       // make sure there are no virtualpaths or stack paths defined in xboxmediacenter.xml
293       //CLog::Log(LOGDEBUG,"    Found path: %s", strPath.c_str());
294       if (!CUtil::IsVirtualPath(strPath) && !CUtil::IsStack(strPath))
295       {
296         // translate special tags
297         if (!strPath.IsEmpty() && strPath.at(0) == '$')
298         {
299           CStdString strPathOld(strPath);
300           strPath = CUtil::TranslateSpecialSource(strPath);
301           if (!strPath.IsEmpty())
302           {
303             //CLog::Log(LOGDEBUG,"    -> Translated to path: %s", strPath.c_str());
304           }
305           else
306           {
307             //CLog::Log(LOGERROR,"    -> Skipping invalid token: %s", strPathOld.c_str());
308             pPathName = pPathName->NextSiblingElement("path");
309             continue;
310           }
311         }
312         CUtil::AddSlashAtEnd(strPath);
313         vecPaths.push_back(strPath);
314       }
315       else
316         CLog::Log(LOGERROR,"    Invalid path type (%s) in source", strPath.c_str());
317     }
318     pPathName = pPathName->NextSiblingElement("path");
319   }
320
321   const TiXmlNode *pLockMode = source->FirstChild("lockmode");
322   const TiXmlNode *pLockCode = source->FirstChild("lockcode");
323   const TiXmlNode *pBadPwdCount = source->FirstChild("badpwdcount");
324   const TiXmlNode *pThumbnailNode = source->FirstChild("thumbnail");
325
326   if (!strName.IsEmpty() && vecPaths.size() > 0)
327   {
328     vector<CStdString> verifiedPaths;
329     // disallowed for files, or theres only a single path in the vector
330     if ((category.Equals("files")) || (vecPaths.size() == 1))
331       verifiedPaths.push_back(vecPaths[0]);
332
333     // multiple paths?
334     else
335     {
336       // validate the paths
337       for (int j = 0; j < (int)vecPaths.size(); ++j)
338       {
339         CURL url(vecPaths[j]);
340         CStdString protocol = url.GetProtocol();
341         bool bIsInvalid = false;
342
343         // for my programs
344         if (category.Equals("programs") || category.Equals("myprograms"))
345         {
346           // only allow HD and plugins
347           if (url.IsLocal() || protocol.Equals("plugin"))
348             verifiedPaths.push_back(vecPaths[j]);
349           else
350             bIsInvalid = true;
351         }
352
353         // for others allow everything (if the user does something silly, we can't stop them)
354         else
355           verifiedPaths.push_back(vecPaths[j]);
356
357         // error message
358         if (bIsInvalid)
359           CLog::Log(LOGERROR,"    Invalid path type (%s) for multipath source", vecPaths[j].c_str());
360       }
361
362       // no valid paths? skip to next source
363       if (verifiedPaths.size() == 0)
364       {
365         CLog::Log(LOGERROR,"    Missing or invalid <name> and/or <path> in source");
366         return false;
367       }
368     }
369
370     share.FromNameAndPaths(category, strName, verifiedPaths);
371
372 /*    CLog::Log(LOGDEBUG,"      Adding source:");
373     CLog::Log(LOGDEBUG,"        Name: %s", share.strName.c_str());
374     if (CUtil::IsVirtualPath(share.strPath) || CUtil::IsMultiPath(share.strPath))
375     {
376       for (int i = 0; i < (int)share.vecPaths.size(); ++i)
377         CLog::Log(LOGDEBUG,"        Path (%02i): %s", i+1, share.vecPaths.at(i).c_str());
378     }
379     else
380       CLog::Log(LOGDEBUG,"        Path: %s", share.strPath.c_str());
381 */
382     share.m_iBadPwdCount = 0;
383     if (pLockMode)
384     {
385       share.m_iLockMode = LockType(atoi(pLockMode->FirstChild()->Value()));
386       share.m_iHasLock = 2;
387     }
388
389     if (pLockCode)
390     {
391       if (pLockCode->FirstChild())
392         share.m_strLockCode = pLockCode->FirstChild()->Value();
393     }
394
395     if (pBadPwdCount)
396     {
397       if (pBadPwdCount->FirstChild())
398         share.m_iBadPwdCount = atoi( pBadPwdCount->FirstChild()->Value() );
399     }
400
401     if (pThumbnailNode)
402     {
403       if (pThumbnailNode->FirstChild())
404         share.m_strThumbnailImage = pThumbnailNode->FirstChild()->Value();
405     }
406
407     return true;
408   }
409   return false;
410 }
411
412 bool CSettings::GetPath(const TiXmlElement* pRootElement, const char *tagName, CStdString &strValue)
413 {
414   CStdString strDefault = strValue;
415   if (XMLUtils::GetPath(pRootElement, tagName, strValue))
416   { // tag exists
417     // check for "-" for backward compatibility
418     if (!strValue.Equals("-"))
419       return true;
420   }
421   // tag doesn't exist - set default
422   strValue = strDefault;
423   return false;
424 }
425
426 bool CSettings::GetString(const TiXmlElement* pRootElement, const char *tagName, CStdString &strValue, const CStdString& strDefaultValue)
427 {
428   if (XMLUtils::GetString(pRootElement, tagName, strValue))
429   { // tag exists
430     // check for "-" for backward compatibility
431     if (!strValue.Equals("-"))
432       return true;
433   }
434   // tag doesn't exist - set default
435   strValue = strDefaultValue;
436   return false;
437 }
438
439 bool CSettings::GetString(const TiXmlElement* pRootElement, const char *tagName, char *szValue, const CStdString& strDefaultValue)
440 {
441   CStdString strValue;
442   bool ret = GetString(pRootElement, tagName, strValue, strDefaultValue);
443   if (szValue)
444     strcpy(szValue, strValue.c_str());
445   return ret;
446 }
447
448 bool CSettings::GetInteger(const TiXmlElement* pRootElement, const char *tagName, int& iValue, const int iDefault, const int iMin, const int iMax)
449 {
450   if (XMLUtils::GetInt(pRootElement, tagName, iValue, iMin, iMax))
451     return true;
452   // default
453   iValue = iDefault;
454   return false;
455 }
456
457 bool CSettings::GetFloat(const TiXmlElement* pRootElement, const char *tagName, float& fValue, const float fDefault, const float fMin, const float fMax)
458 {
459   if (XMLUtils::GetFloat(pRootElement, tagName, fValue, fMin, fMax))
460     return true;
461   // default
462   fValue = fDefault;
463   return false;
464 }
465
466 void CSettings::GetViewState(const TiXmlElement *pRootElement, const CStdString &strTagName, CViewState &viewState, SORT_METHOD defaultSort, int defaultView)
467 {
468   const TiXmlElement* pNode = pRootElement->FirstChildElement(strTagName);
469   if (!pNode)
470   {
471     viewState.m_sortMethod = defaultSort;
472     viewState.m_viewMode = defaultView;
473     return;
474   }
475   GetInteger(pNode, "viewmode", viewState.m_viewMode, defaultView, DEFAULT_VIEW_LIST, DEFAULT_VIEW_MAX);
476
477   int sortMethod;
478   GetInteger(pNode, "sortmethod", sortMethod, defaultSort, SORT_METHOD_NONE, SORT_METHOD_MAX);
479   viewState.m_sortMethod = (SORT_METHOD)sortMethod;
480
481   int sortOrder;
482   GetInteger(pNode, "sortorder", sortOrder, SORT_ORDER_ASC, SORT_ORDER_NONE, SORT_ORDER_DESC);
483   viewState.m_sortOrder = (SORT_ORDER)sortOrder;
484 }
485
486 void CSettings::SetViewState(TiXmlNode *pRootNode, const CStdString &strTagName, const CViewState &viewState) const
487 {
488   TiXmlElement newElement(strTagName);
489   TiXmlNode *pNewNode = pRootNode->InsertEndChild(newElement);
490   if (pNewNode)
491   {
492     XMLUtils::SetInt(pNewNode, "viewmode", viewState.m_viewMode);
493     XMLUtils::SetInt(pNewNode, "sortmethod", (int)viewState.m_sortMethod);
494     XMLUtils::SetInt(pNewNode, "sortorder", (int)viewState.m_sortOrder);
495   }
496 }
497
498 bool CSettings::LoadCalibration(const TiXmlElement* pRoot, const CStdString& strSettingsFile)
499 {
500   const TiXmlElement *pElement = pRoot->FirstChildElement("resolutions");
501   if (!pElement)
502   {
503     CLog::Log(LOGERROR, "%s Doesn't contain <resolutions>", strSettingsFile.c_str());
504     return false;
505   }
506   const TiXmlElement *pResolution = pElement->FirstChildElement("resolution");
507   while (pResolution)
508   {
509     // get the data for this resolution
510     CStdString mode;
511     XMLUtils::GetString(pResolution, "description", mode);
512     // find this resolution in our resolution vector
513     for (unsigned int res = 0; res < m_ResInfo.size(); res++)
514     {
515       if (res == RES_WINDOW)
516         continue;
517
518       if (m_ResInfo[res].strMode == mode)
519       { // found, read in the rest of the information for this item
520         const TiXmlElement *pOverscan = pResolution->FirstChildElement("overscan");
521         if (pOverscan)
522         {
523           GetInteger(pOverscan, "left", m_ResInfo[res].Overscan.left, 0, -m_ResInfo[res].iWidth / 4, m_ResInfo[res].iWidth / 4);
524           GetInteger(pOverscan, "top", m_ResInfo[res].Overscan.top, 0, -m_ResInfo[res].iHeight / 4, m_ResInfo[res].iHeight / 4);
525           GetInteger(pOverscan, "right", m_ResInfo[res].Overscan.right, m_ResInfo[res].iWidth, m_ResInfo[res].iWidth / 2, m_ResInfo[res].iWidth*3 / 2);
526           GetInteger(pOverscan, "bottom", m_ResInfo[res].Overscan.bottom, m_ResInfo[res].iHeight, m_ResInfo[res].iHeight / 2, m_ResInfo[res].iHeight*3 / 2);
527         }
528
529         // get the appropriate "safe graphics area" = 10% for 4x3, 3.5% for 16x9
530         float fSafe;
531         if (res == RES_PAL_4x3 || res == RES_NTSC_4x3 || res == RES_PAL60_4x3 || res == RES_HDTV_480p_4x3)
532           fSafe = 0.1f;
533         else
534           fSafe = 0.035f;
535
536         GetInteger(pResolution, "subtitles", m_ResInfo[res].iSubtitles, (int)((1 - fSafe)*m_ResInfo[res].iHeight), m_ResInfo[res].iHeight / 2, m_ResInfo[res].iHeight*5 / 4);
537         GetFloat(pResolution, "pixelratio", m_ResInfo[res].fPixelRatio, 128.0f / 117.0f, 0.5f, 2.0f);
538     /*    CLog::Log(LOGDEBUG, "  calibration for %s %ix%i", m_ResInfo[res].strMode, m_ResInfo[res].iWidth, m_ResInfo[res].iHeight);
539         CLog::Log(LOGDEBUG, "    subtitle yposition:%i pixelratio:%03.3f offsets:(%i,%i)->(%i,%i)",
540                   m_ResInfo[res].iSubtitles, m_ResInfo[res].fPixelRatio,
541                   m_ResInfo[res].Overscan.left, m_ResInfo[res].Overscan.top,
542                   m_ResInfo[res].Overscan.right, m_ResInfo[res].Overscan.bottom);*/
543       }
544     }
545     // iterate around
546     pResolution = pResolution->NextSiblingElement("resolution");
547
548
549 /* Hmm, these stuff shouldn't be releaded, they should be used instead of our internal
550    id counter to select what resolution is affected by this settings
551 #ifdef HAS_XRANDR
552     const CStdString def("");
553     CStdString val;
554     GetString(pResolution, "xrandrid", val, def);
555     strncpy(m_ResInfo[iRes].strId, val.c_str(), sizeof(m_ResInfo[iRes].strId));
556     GetString(pResolution, "output", val, def);
557     strncpy(m_ResInfo[iRes].strOutput, val.c_str(), sizeof(m_ResInfo[iRes].strOutput));
558     GetFloat(pResolution, "refreshrate", m_ResInfo[iRes].fRefreshRate, 0, 0, 200);
559 #endif
560 */
561   }
562   return true;
563 }
564
565 bool CSettings::SaveCalibration(TiXmlNode* pRootNode) const
566 {
567   TiXmlElement xmlRootElement("resolutions");
568   TiXmlNode *pRoot = pRootNode->InsertEndChild(xmlRootElement);
569
570   // save WINDOW, DESKTOP and CUSTOM resolution
571   for (size_t i = RES_WINDOW ; i < m_ResInfo.size() ; i++)
572   {
573     // Write the resolution tag
574     TiXmlElement resElement("resolution");
575     TiXmlNode *pNode = pRoot->InsertEndChild(resElement);
576     // Now write each of the pieces of information we need...
577     XMLUtils::SetString(pNode, "description", m_ResInfo[i].strMode);
578     XMLUtils::SetInt(pNode, "subtitles", m_ResInfo[i].iSubtitles);
579     XMLUtils::SetFloat(pNode, "pixelratio", m_ResInfo[i].fPixelRatio);
580 #ifdef HAS_XRANDR
581     XMLUtils::SetFloat(pNode, "refreshrate", m_ResInfo[i].fRefreshRate);
582     XMLUtils::SetString(pNode, "output", m_ResInfo[i].strOutput);
583     XMLUtils::SetString(pNode, "xrandrid", m_ResInfo[i].strId);
584 #endif
585     // create the overscan child
586     TiXmlElement overscanElement("overscan");
587     TiXmlNode *pOverscanNode = pNode->InsertEndChild(overscanElement);
588     XMLUtils::SetInt(pOverscanNode, "left", m_ResInfo[i].Overscan.left);
589     XMLUtils::SetInt(pOverscanNode, "top", m_ResInfo[i].Overscan.top);
590     XMLUtils::SetInt(pOverscanNode, "right", m_ResInfo[i].Overscan.right);
591     XMLUtils::SetInt(pOverscanNode, "bottom", m_ResInfo[i].Overscan.bottom);
592   }
593   return true;
594 }
595
596 bool CSettings::LoadSettings(const CStdString& strSettingsFile)
597 {
598   // load the xml file
599   TiXmlDocument xmlDoc;
600
601   if (!xmlDoc.LoadFile(strSettingsFile))
602   {
603     CLog::Log(LOGERROR, "%s, Line %d\n%s", strSettingsFile.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
604     return false;
605   }
606
607   TiXmlElement *pRootElement = xmlDoc.RootElement();
608   if (strcmpi(pRootElement->Value(), "settings") != 0)
609   {
610     CLog::Log(LOGERROR, "%s\nDoesn't contain <settings>", strSettingsFile.c_str());
611     return false;
612   }
613
614   // mymusic settings
615   TiXmlElement *pElement = pRootElement->FirstChildElement("mymusic");
616   if (pElement)
617   {
618     TiXmlElement *pChild = pElement->FirstChildElement("playlist");
619     if (pChild)
620     {
621       XMLUtils::GetBoolean(pChild, "repeat", m_bMyMusicPlaylistRepeat);
622       XMLUtils::GetBoolean(pChild, "shuffle", m_bMyMusicPlaylistShuffle);
623     }
624     // if the user happened to reboot in the middle of the scan we save this state
625     pChild = pElement->FirstChildElement("scanning");
626     if (pChild)
627     {
628       XMLUtils::GetBoolean(pChild, "isscanning", m_bMyMusicIsScanning);
629     }
630     GetInteger(pElement, "startwindow", m_iMyMusicStartWindow, WINDOW_MUSIC_FILES, WINDOW_MUSIC_FILES, WINDOW_MUSIC_NAV); //501; view songs
631     XMLUtils::GetBoolean(pElement, "songinfoinvis", m_bMyMusicSongInfoInVis);
632     XMLUtils::GetBoolean(pElement, "songthumbinvis", m_bMyMusicSongThumbInVis);
633     GetPath(pElement, "defaultlibview", m_defaultMusicLibSource);
634   }
635   // myvideos settings
636   pElement = pRootElement->FirstChildElement("myvideos");
637   if (pElement)
638   {
639     GetInteger(pElement, "startwindow", m_iVideoStartWindow, WINDOW_VIDEO_FILES, WINDOW_VIDEO_FILES, WINDOW_VIDEO_NAV);
640     GetInteger(pElement, "stackvideomode", m_iMyVideoStack, STACK_NONE, STACK_NONE, STACK_SIMPLE);
641
642     GetPath(pElement, "defaultlibview", m_defaultVideoLibSource);
643
644     // Read the watchmode settings for the various media views
645     GetInteger(pElement, "watchmodemovies", m_watchMode["movies"], VIDEO_SHOW_ALL, VIDEO_SHOW_ALL, VIDEO_SHOW_WATCHED);
646     GetInteger(pElement, "watchmodetvshows", m_watchMode["tvshows"], VIDEO_SHOW_ALL, VIDEO_SHOW_ALL, VIDEO_SHOW_WATCHED);
647     GetInteger(pElement, "watchmodemusicvideos", m_watchMode["musicvideos"], VIDEO_SHOW_ALL, VIDEO_SHOW_ALL, VIDEO_SHOW_WATCHED);
648
649     XMLUtils::GetBoolean(pElement, "flatten", m_bMyVideoNavFlatten);
650
651     TiXmlElement *pChild = pElement->FirstChildElement("playlist");
652     if (pChild)
653     { // playlist
654       XMLUtils::GetBoolean(pChild, "repeat", m_bMyVideoPlaylistRepeat);
655       XMLUtils::GetBoolean(pChild, "shuffle", m_bMyVideoPlaylistShuffle);
656     }
657   }
658
659   pElement = pRootElement->FirstChildElement("viewstates");
660   if (pElement)
661   {
662     GetViewState(pElement, "musicnavartists", m_viewStateMusicNavArtists);
663     GetViewState(pElement, "musicnavalbums", m_viewStateMusicNavAlbums);
664     GetViewState(pElement, "musicnavsongs", m_viewStateMusicNavSongs);
665     GetViewState(pElement, "musiclastfm", m_viewStateMusicLastFM);
666     GetViewState(pElement, "videonavactors", m_viewStateVideoNavActors);
667     GetViewState(pElement, "videonavyears", m_viewStateVideoNavYears);
668     GetViewState(pElement, "videonavgenres", m_viewStateVideoNavGenres);
669     GetViewState(pElement, "videonavtitles", m_viewStateVideoNavTitles);
670     GetViewState(pElement, "videonavepisodes", m_viewStateVideoNavEpisodes, SORT_METHOD_EPISODE);
671     GetViewState(pElement, "videonavtvshows", m_viewStateVideoNavTvShows);
672     GetViewState(pElement, "videonavseasons", m_viewStateVideoNavSeasons);
673     GetViewState(pElement, "videonavmusicvideos", m_viewStateVideoNavMusicVideos);
674
675     GetViewState(pElement, "programs", m_viewStatePrograms, SORT_METHOD_LABEL, DEFAULT_VIEW_AUTO);
676     GetViewState(pElement, "pictures", m_viewStatePictures, SORT_METHOD_LABEL, DEFAULT_VIEW_AUTO);
677     GetViewState(pElement, "videofiles", m_viewStateVideoFiles, SORT_METHOD_LABEL, DEFAULT_VIEW_AUTO);
678     GetViewState(pElement, "musicfiles", m_viewStateMusicFiles, SORT_METHOD_LABEL, DEFAULT_VIEW_AUTO);
679   }
680
681   // general settings
682   pElement = pRootElement->FirstChildElement("general");
683   if (pElement)
684   {
685     GetInteger(pElement, "systemtotaluptime", m_iSystemTimeTotalUp, 0, 0, INT_MAX);
686     GetInteger(pElement, "httpapibroadcastlevel", m_HttpApiBroadcastLevel, 0, 0, 255);
687     GetInteger(pElement, "httpapibroadcastport", m_HttpApiBroadcastPort, 8278, 1, 65535);
688   }
689
690   pElement = pRootElement->FirstChildElement("defaultvideosettings");
691   if (pElement)
692   {
693     int interlaceMethod;
694     GetInteger(pElement, "interlacemethod", interlaceMethod, VS_INTERLACEMETHOD_NONE, VS_INTERLACEMETHOD_NONE, VS_INTERLACEMETHOD_INVERSE_TELECINE);
695     m_defaultVideoSettings.m_InterlaceMethod = (EINTERLACEMETHOD)interlaceMethod;
696     int scalingMethod;
697     GetInteger(pElement, "scalingmethod", scalingMethod, VS_SCALINGMETHOD_LINEAR, VS_SCALINGMETHOD_NEAREST, VS_SCALINGMETHOD_AUTO);
698     m_defaultVideoSettings.m_ScalingMethod = (ESCALINGMETHOD)scalingMethod;
699
700     GetInteger(pElement, "viewmode", m_defaultVideoSettings.m_ViewMode, VIEW_MODE_NORMAL, VIEW_MODE_NORMAL, VIEW_MODE_CUSTOM);
701     GetFloat(pElement, "zoomamount", m_defaultVideoSettings.m_CustomZoomAmount, 1.0f, 0.5f, 2.0f);
702     GetFloat(pElement, "pixelratio", m_defaultVideoSettings.m_CustomPixelRatio, 1.0f, 0.5f, 2.0f);
703     GetFloat(pElement, "volumeamplification", m_defaultVideoSettings.m_VolumeAmplification, VOLUME_DRC_MINIMUM * 0.01f, VOLUME_DRC_MINIMUM * 0.01f, VOLUME_DRC_MAXIMUM * 0.01f);
704     GetFloat(pElement, "noisereduction", m_defaultVideoSettings.m_NoiseReduction, 0.0f, 0.0f, 1.0f);
705     GetFloat(pElement, "sharpness", m_defaultVideoSettings.m_Sharpness, 0.0f, -1.0f, 1.0f);
706     XMLUtils::GetBoolean(pElement, "outputtoallspeakers", m_defaultVideoSettings.m_OutputToAllSpeakers);
707     XMLUtils::GetBoolean(pElement, "showsubtitles", m_defaultVideoSettings.m_SubtitleOn);
708     GetFloat(pElement, "brightness", m_defaultVideoSettings.m_Brightness, 50, 0, 100);
709     GetFloat(pElement, "contrast", m_defaultVideoSettings.m_Contrast, 50, 0, 100);
710     GetFloat(pElement, "gamma", m_defaultVideoSettings.m_Gamma, 20, 0, 100);
711     GetFloat(pElement, "audiodelay", m_defaultVideoSettings.m_AudioDelay, 0.0f, -10.0f, 10.0f);
712     GetFloat(pElement, "subtitledelay", m_defaultVideoSettings.m_SubtitleDelay, 0.0f, -10.0f, 10.0f);
713     XMLUtils::GetBoolean(pElement, "autocrop", m_defaultVideoSettings.m_Crop);
714     XMLUtils::GetBoolean(pElement, "nonlinstretch", m_defaultVideoSettings.m_CustomNonLinStretch);
715     XMLUtils::GetBoolean(pElement, "addonautoupdate", m_bAddonAutoUpdate);
716     XMLUtils::GetBoolean(pElement, "addonnotifications", m_bAddonNotifications);
717
718     m_defaultVideoSettings.m_SubtitleCached = false;
719   }
720   // audio settings
721   pElement = pRootElement->FirstChildElement("audio");
722   if (pElement)
723   {
724     GetInteger(pElement, "volumelevel", m_nVolumeLevel, VOLUME_MAXIMUM, VOLUME_MINIMUM, VOLUME_MAXIMUM);
725     GetInteger(pElement, "dynamicrangecompression", m_dynamicRangeCompressionLevel, VOLUME_DRC_MINIMUM, VOLUME_DRC_MINIMUM, VOLUME_DRC_MAXIMUM);
726   }
727
728   LoadCalibration(pRootElement, strSettingsFile);
729   g_guiSettings.LoadXML(pRootElement);
730   LoadSkinSettings(pRootElement);
731
732   // Configure the PlayerCoreFactory
733   LoadPlayerCoreFactorySettings("special://xbmc/system/playercorefactory.xml", true);
734   LoadPlayerCoreFactorySettings(GetUserDataItem("playercorefactory.xml"), false);
735
736   // Advanced settings
737   g_advancedSettings.Load();
738
739   // Default players?
740   CLog::Log(LOGNOTICE, "Default DVD Player: %s", g_advancedSettings.m_videoDefaultDVDPlayer.c_str());
741   CLog::Log(LOGNOTICE, "Default Video Player: %s", g_advancedSettings.m_videoDefaultPlayer.c_str());
742   CLog::Log(LOGNOTICE, "Default Audio Player: %s", g_advancedSettings.m_audioDefaultPlayer.c_str());
743
744   // setup any logging...
745   if (g_guiSettings.GetBool("debug.showloginfo"))
746   {
747     g_advancedSettings.m_logLevel = std::max(g_advancedSettings.m_logLevelHint, LOG_LEVEL_DEBUG_FREEMEM);
748     CLog::Log(LOGNOTICE, "Enabled debug logging due to GUI setting (%d)", g_advancedSettings.m_logLevel);
749   }
750   return true;
751 }
752
753 bool CSettings::LoadPlayerCoreFactorySettings(const CStdString& fileStr, bool clear)
754 {
755   CLog::Log(LOGNOTICE, "Loading player core factory settings from %s.", fileStr.c_str());
756   if (!CFile::Exists(fileStr))
757   { // tell the user it doesn't exist
758     CLog::Log(LOGNOTICE, "%s does not exist. Skipping.", fileStr.c_str());
759     return false;
760   }
761
762   TiXmlDocument playerCoreFactoryXML;
763   if (!playerCoreFactoryXML.LoadFile(fileStr))
764   {
765     CLog::Log(LOGERROR, "Error loading %s, Line %d (%s)", fileStr.c_str(), playerCoreFactoryXML.ErrorRow(), playerCoreFactoryXML.ErrorDesc());
766     return false;
767   }
768
769   return CPlayerCoreFactory::LoadConfiguration(playerCoreFactoryXML.RootElement(), clear);
770 }
771
772 bool CSettings::SaveSettings(const CStdString& strSettingsFile, CGUISettings *localSettings /* = NULL */) const
773 {
774   TiXmlDocument xmlDoc;
775   TiXmlElement xmlRootElement("settings");
776   TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement);
777   if (!pRoot) return false;
778   // write our tags one by one - just a big list for now (can be flashed up later)
779
780   // mymusic settings
781   TiXmlElement musicNode("mymusic");
782   TiXmlNode *pNode = pRoot->InsertEndChild(musicNode);
783   if (!pNode) return false;
784   {
785     TiXmlElement childNode("playlist");
786     TiXmlNode *pChild = pNode->InsertEndChild(childNode);
787     if (!pChild) return false;
788     XMLUtils::SetBoolean(pChild, "repeat", m_bMyMusicPlaylistRepeat);
789     XMLUtils::SetBoolean(pChild, "shuffle", m_bMyMusicPlaylistShuffle);
790   }
791   {
792     TiXmlElement childNode("scanning");
793     TiXmlNode *pChild = pNode->InsertEndChild(childNode);
794     if (!pChild) return false;
795     XMLUtils::SetBoolean(pChild, "isscanning", m_bMyMusicIsScanning);
796   }
797
798   XMLUtils::SetInt(pNode, "startwindow", m_iMyMusicStartWindow);
799   XMLUtils::SetBoolean(pNode, "songinfoinvis", m_bMyMusicSongInfoInVis);
800   XMLUtils::SetBoolean(pNode, "songthumbinvis", m_bMyMusicSongThumbInVis);
801   XMLUtils::SetPath(pNode, "defaultlibview", m_defaultMusicLibSource);
802
803   // myvideos settings
804   TiXmlElement videosNode("myvideos");
805   pNode = pRoot->InsertEndChild(videosNode);
806   if (!pNode) return false;
807
808   XMLUtils::SetInt(pNode, "startwindow", m_iVideoStartWindow);
809
810   XMLUtils::SetInt(pNode, "stackvideomode", m_iMyVideoStack);
811
812   XMLUtils::SetPath(pNode, "defaultlibview", m_defaultVideoLibSource);
813
814   XMLUtils::SetInt(pNode, "watchmodemovies", m_watchMode.find("movies")->second);
815   XMLUtils::SetInt(pNode, "watchmodetvshows", m_watchMode.find("tvshows")->second);
816   XMLUtils::SetInt(pNode, "watchmodemusicvideos", m_watchMode.find("musicvideos")->second);
817
818   XMLUtils::SetBoolean(pNode, "flatten", m_bMyVideoNavFlatten);
819
820   { // playlist window
821     TiXmlElement childNode("playlist");
822     TiXmlNode *pChild = pNode->InsertEndChild(childNode);
823     if (!pChild) return false;
824     XMLUtils::SetBoolean(pChild, "repeat", m_bMyVideoPlaylistRepeat);
825     XMLUtils::SetBoolean(pChild, "shuffle", m_bMyVideoPlaylistShuffle);
826   }
827
828   // view states
829   TiXmlElement viewStateNode("viewstates");
830   pNode = pRoot->InsertEndChild(viewStateNode);
831   if (pNode)
832   {
833     SetViewState(pNode, "musicnavartists", m_viewStateMusicNavArtists);
834     SetViewState(pNode, "musicnavalbums", m_viewStateMusicNavAlbums);
835     SetViewState(pNode, "musicnavsongs", m_viewStateMusicNavSongs);
836     SetViewState(pNode, "musiclastfm", m_viewStateMusicLastFM);
837     SetViewState(pNode, "videonavactors", m_viewStateVideoNavActors);
838     SetViewState(pNode, "videonavyears", m_viewStateVideoNavYears);
839     SetViewState(pNode, "videonavgenres", m_viewStateVideoNavGenres);
840     SetViewState(pNode, "videonavtitles", m_viewStateVideoNavTitles);
841     SetViewState(pNode, "videonavepisodes", m_viewStateVideoNavEpisodes);
842     SetViewState(pNode, "videonavseasons", m_viewStateVideoNavSeasons);
843     SetViewState(pNode, "videonavtvshows", m_viewStateVideoNavTvShows);
844     SetViewState(pNode, "videonavmusicvideos", m_viewStateVideoNavMusicVideos);
845
846     SetViewState(pNode, "programs", m_viewStatePrograms);
847     SetViewState(pNode, "pictures", m_viewStatePictures);
848     SetViewState(pNode, "videofiles", m_viewStateVideoFiles);
849     SetViewState(pNode, "musicfiles", m_viewStateMusicFiles);
850   }
851
852   // general settings
853   TiXmlElement generalNode("general");
854   pNode = pRoot->InsertEndChild(generalNode);
855   if (!pNode) return false;
856   XMLUtils::SetInt(pNode, "systemtotaluptime", m_iSystemTimeTotalUp);
857   XMLUtils::SetInt(pNode, "httpapibroadcastport", m_HttpApiBroadcastPort);
858   XMLUtils::SetInt(pNode, "httpapibroadcastlevel", m_HttpApiBroadcastLevel);
859
860   // default video settings
861   TiXmlElement videoSettingsNode("defaultvideosettings");
862   pNode = pRoot->InsertEndChild(videoSettingsNode);
863   if (!pNode) return false;
864   XMLUtils::SetInt(pNode, "interlacemethod", m_defaultVideoSettings.m_InterlaceMethod);
865   XMLUtils::SetInt(pNode, "scalingmethod", m_defaultVideoSettings.m_ScalingMethod);
866   XMLUtils::SetFloat(pNode, "noisereduction", m_defaultVideoSettings.m_NoiseReduction);
867   XMLUtils::SetFloat(pNode, "sharpness", m_defaultVideoSettings.m_Sharpness);
868   XMLUtils::SetInt(pNode, "viewmode", m_defaultVideoSettings.m_ViewMode);
869   XMLUtils::SetFloat(pNode, "zoomamount", m_defaultVideoSettings.m_CustomZoomAmount);
870   XMLUtils::SetFloat(pNode, "pixelratio", m_defaultVideoSettings.m_CustomPixelRatio);
871   XMLUtils::SetFloat(pNode, "volumeamplification", m_defaultVideoSettings.m_VolumeAmplification);
872   XMLUtils::SetBoolean(pNode, "outputtoallspeakers", m_defaultVideoSettings.m_OutputToAllSpeakers);
873   XMLUtils::SetBoolean(pNode, "showsubtitles", m_defaultVideoSettings.m_SubtitleOn);
874   XMLUtils::SetFloat(pNode, "brightness", m_defaultVideoSettings.m_Brightness);
875   XMLUtils::SetFloat(pNode, "contrast", m_defaultVideoSettings.m_Contrast);
876   XMLUtils::SetFloat(pNode, "gamma", m_defaultVideoSettings.m_Gamma);
877   XMLUtils::SetFloat(pNode, "audiodelay", m_defaultVideoSettings.m_AudioDelay);
878   XMLUtils::SetFloat(pNode, "subtitledelay", m_defaultVideoSettings.m_SubtitleDelay);
879   XMLUtils::SetBoolean(pNode, "autocrop", m_defaultVideoSettings.m_Crop); 
880   XMLUtils::SetBoolean(pNode, "nonlinstretch", m_defaultVideoSettings.m_CustomNonLinStretch);
881   XMLUtils::SetBoolean(pNode, "addonautoupdate", m_bAddonAutoUpdate);
882   XMLUtils::SetBoolean(pNode, "addonnotifications", m_bAddonNotifications);
883
884
885   // audio settings
886   TiXmlElement volumeNode("audio");
887   pNode = pRoot->InsertEndChild(volumeNode);
888   if (!pNode) return false;
889   XMLUtils::SetInt(pNode, "volumelevel", m_nVolumeLevel);
890   XMLUtils::SetInt(pNode, "dynamicrangecompression", m_dynamicRangeCompressionLevel);
891
892   SaveCalibration(pRoot);
893
894   if (localSettings) // local settings to save
895     localSettings->SaveXML(pRoot);
896   else // save the global settings
897     g_guiSettings.SaveXML(pRoot);
898
899   SaveSkinSettings(pRoot);
900
901   // For mastercode
902   SaveProfiles( PROFILES_FILE );
903
904   // save the file
905   return xmlDoc.SaveFile(strSettingsFile);
906 }
907
908 bool CSettings::LoadProfile(unsigned int index)
909 {
910   unsigned int oldProfile = m_currentProfile;
911   m_currentProfile = index;
912   CStdString strOldSkin = g_guiSettings.GetString("lookandfeel.skin");
913   CStdString strOldFont = g_guiSettings.GetString("lookandfeel.font");
914   CStdString strOldTheme = g_guiSettings.GetString("lookandfeel.skintheme");
915   CStdString strOldColors = g_guiSettings.GetString("lookandfeel.skincolors");
916   if (Load())
917   {
918     CreateProfileFolders();
919
920     // initialize our charset converter
921     g_charsetConverter.reset();
922
923     // Load the langinfo to have user charset <-> utf-8 conversion
924     CStdString strLanguage = g_guiSettings.GetString("locale.language");
925     strLanguage[0] = toupper(strLanguage[0]);
926
927     CStdString strLangInfoPath;
928     strLangInfoPath.Format("special://xbmc/language/%s/langinfo.xml", strLanguage.c_str());
929     CLog::Log(LOGINFO, "load language info file:%s", strLangInfoPath.c_str());
930     g_langInfo.Load(strLangInfoPath);
931
932     CStdString strLanguagePath;
933     strLanguagePath.Format("special://xbmc/language/%s/strings.xml", strLanguage.c_str());
934
935     CButtonTranslator::GetInstance().Load();
936     g_localizeStrings.Load(strLanguagePath);
937
938     g_Mouse.SetEnabled(g_guiSettings.GetBool("input.enablemouse"));
939
940     g_infoManager.ResetCache();
941     g_infoManager.ResetLibraryBools();
942
943     // always reload the skin - we need it for the new language strings
944     g_application.ReloadSkin();
945
946     if (m_currentProfile != 0)
947     {
948       TiXmlDocument doc;
949       if (doc.LoadFile(CUtil::AddFileToFolder(GetUserDataFolder(),"guisettings.xml")))
950         g_guiSettings.LoadMasterLock(doc.RootElement());
951     }
952
953     CPasswordManager::GetInstance().Clear();
954
955     // to set labels - shares are reloaded
956 #if !defined(_WIN32) && defined(HAS_DVD_DRIVE)
957     MEDIA_DETECT::CDetectDVDMedia::UpdateState();
958 #endif
959     // init windows
960     CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_WINDOW_RESET);
961     g_windowManager.SendMessage(msg);
962
963     CUtil::DeleteMusicDatabaseDirectoryCache();
964     CUtil::DeleteVideoDatabaseDirectoryCache();
965
966     return true;
967   }
968
969   m_currentProfile = oldProfile;
970
971   return false;
972 }
973
974 bool CSettings::DeleteProfile(unsigned int index)
975 {
976   const CProfile *profile = GetProfile(index);
977   if (!profile)
978     return false;
979
980   CGUIDialogYesNo* dlgYesNo = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
981   if (dlgYesNo)
982   {
983     CStdString message;
984     CStdString str = g_localizeStrings.Get(13201);
985     message.Format(str.c_str(), profile->getName());
986     dlgYesNo->SetHeading(13200);
987     dlgYesNo->SetLine(0, message);
988     dlgYesNo->SetLine(1, "");
989     dlgYesNo->SetLine(2, "");
990     dlgYesNo->DoModal();
991
992     if (dlgYesNo->IsConfirmed())
993     {
994       //delete profile
995       CStdString strDirectory = profile->getDirectory();
996       m_vecProfiles.erase(m_vecProfiles.begin()+index);
997       if (index == m_currentProfile)
998       {
999         LoadProfile(0);
1000         Save();
1001       }
1002
1003       CFileItemPtr item = CFileItemPtr(new CFileItem(CUtil::AddFileToFolder(GetUserDataFolder(), strDirectory)));
1004       item->m_strPath = CUtil::AddFileToFolder(GetUserDataFolder(), strDirectory + "\\");
1005       item->m_bIsFolder = true;
1006       item->Select(true);
1007       CFileUtils::DeleteItem(item);
1008     }
1009     else
1010       return false;
1011   }
1012
1013   SaveProfiles( PROFILES_FILE );
1014
1015   return true;
1016 }
1017
1018 void CSettings::LoadProfiles(const CStdString& profilesFile)
1019 {
1020   // clear out our profiles
1021   m_vecProfiles.clear();
1022
1023   TiXmlDocument profilesDoc;
1024   if (CFile::Exists(profilesFile))
1025   {
1026     if (profilesDoc.LoadFile(profilesFile))
1027     {
1028       TiXmlElement *rootElement = profilesDoc.RootElement();
1029       if (rootElement && strcmpi(rootElement->Value(),"profiles") == 0)
1030       {
1031         XMLUtils::GetUInt(rootElement, "lastloaded", m_lastUsedProfile);
1032         XMLUtils::GetBoolean(rootElement, "useloginscreen", m_usingLoginScreen);
1033
1034         TiXmlElement* pProfile = rootElement->FirstChildElement("profile");
1035         
1036         CStdString defaultDir("special://home/userdata");
1037         if (!CDirectory::Exists(defaultDir))
1038           defaultDir = "special://xbmc/userdata";
1039         while (pProfile)
1040         {
1041           CProfile profile(defaultDir);
1042           profile.Load(pProfile);
1043           m_vecProfiles.push_back(profile);
1044           pProfile = pProfile->NextSiblingElement("profile");
1045         }
1046       }
1047       else
1048         CLog::Log(LOGERROR, "Error loading %s, no <profiles> node", profilesFile.c_str());
1049     }
1050     else
1051       CLog::Log(LOGERROR, "Error loading %s, Line %d\n%s", profilesFile.c_str(), profilesDoc.ErrorRow(), profilesDoc.ErrorDesc());
1052   }
1053
1054   if (m_vecProfiles.empty())
1055   { // add the master user
1056     CProfile profile("special://masterprofile/", "Master user");
1057     m_vecProfiles.push_back(profile);
1058   }
1059
1060   // check the validity of the previous profile index
1061   if (m_lastUsedProfile >= m_vecProfiles.size())
1062     m_lastUsedProfile = 0;
1063
1064   m_currentProfile = m_lastUsedProfile;
1065
1066   // the login screen runs as the master profile, so if we're using this, we need to ensure
1067   // we switch to the master profile
1068   if (m_usingLoginScreen)
1069     m_currentProfile = 0;
1070 }
1071
1072 bool CSettings::SaveProfiles(const CStdString& profilesFile) const
1073 {
1074   TiXmlDocument xmlDoc;
1075   TiXmlElement xmlRootElement("profiles");
1076   TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement);
1077   if (!pRoot) return false;
1078   XMLUtils::SetInt(pRoot,"lastloaded", m_currentProfile);
1079   XMLUtils::SetBoolean(pRoot,"useloginscreen",m_usingLoginScreen);
1080   for (unsigned int i = 0; i < m_vecProfiles.size(); ++i)
1081     m_vecProfiles[i].Save(pRoot);
1082
1083   // save the file
1084   return xmlDoc.SaveFile(profilesFile);
1085 }
1086
1087 bool CSettings::LoadUPnPXml(const CStdString& strSettingsFile)
1088 {
1089   TiXmlDocument UPnPDoc;
1090
1091   if (!CFile::Exists(strSettingsFile))
1092   { // set defaults, or assume no rss feeds??
1093     return false;
1094   }
1095   if (!UPnPDoc.LoadFile(strSettingsFile))
1096   {
1097     CLog::Log(LOGERROR, "Error loading %s, Line %d\n%s", strSettingsFile.c_str(), UPnPDoc.ErrorRow(), UPnPDoc.ErrorDesc());
1098     return false;
1099   }
1100
1101   TiXmlElement *pRootElement = UPnPDoc.RootElement();
1102   if (!pRootElement || strcmpi(pRootElement->Value(),"upnpserver") != 0)
1103   {
1104     CLog::Log(LOGERROR, "Error loading %s, no <upnpserver> node", strSettingsFile.c_str());
1105     return false;
1106   }
1107   // load settings
1108
1109   // default values for ports
1110   m_UPnPPortServer = 0;
1111   m_UPnPPortRenderer = 0;
1112   m_UPnPMaxReturnedItems = 0;
1113
1114   XMLUtils::GetString(pRootElement, "UUID", m_UPnPUUIDServer);
1115   XMLUtils::GetInt(pRootElement, "Port", m_UPnPPortServer);
1116   XMLUtils::GetInt(pRootElement, "MaxReturnedItems", m_UPnPMaxReturnedItems);
1117   XMLUtils::GetString(pRootElement, "UUIDRenderer", m_UPnPUUIDRenderer);
1118   XMLUtils::GetInt(pRootElement, "PortRenderer", m_UPnPPortRenderer);
1119
1120   return true;
1121 }
1122
1123 bool CSettings::SaveUPnPXml(const CStdString& strSettingsFile) const
1124 {
1125   TiXmlDocument xmlDoc;
1126   TiXmlElement xmlRootElement("upnpserver");
1127   TiXmlNode *pRoot = xmlDoc.InsertEndChild(xmlRootElement);
1128   if (!pRoot) return false;
1129
1130   // create a new Element for UUID
1131   XMLUtils::SetString(pRoot, "UUID", m_UPnPUUIDServer);
1132   XMLUtils::SetInt(pRoot, "Port", m_UPnPPortServer);
1133   XMLUtils::SetInt(pRoot, "MaxReturnedItems", m_UPnPMaxReturnedItems);
1134   XMLUtils::SetString(pRoot, "UUIDRenderer", m_UPnPUUIDRenderer);
1135   XMLUtils::SetInt(pRoot, "PortRenderer", m_UPnPPortRenderer);
1136
1137   // save the file
1138   return xmlDoc.SaveFile(strSettingsFile);
1139 }
1140
1141 bool CSettings::UpdateShare(const CStdString &type, const CStdString oldName, const CMediaSource &share)
1142 {
1143   VECSOURCES *pShares = GetSourcesFromType(type);
1144
1145   if (!pShares) return false;
1146
1147   // update our current share list
1148   CMediaSource* pShare=NULL;
1149   for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
1150   {
1151     if ((*it).strName == oldName)
1152     {
1153       (*it).strName = share.strName;
1154       (*it).strPath = share.strPath;
1155       (*it).vecPaths = share.vecPaths;
1156       pShare = &(*it);
1157       break;
1158     }
1159   }
1160
1161   if (!pShare)
1162     return false;
1163
1164   // Update our XML file as well
1165   return SaveSources();
1166 }
1167
1168 // NOTE: This function does NOT save the sources.xml file - you need to call SaveSources() separately.
1169 bool CSettings::UpdateSource(const CStdString &strType, const CStdString strOldName, const CStdString &strUpdateElement, const CStdString &strUpdateText)
1170 {
1171   VECSOURCES *pShares = GetSourcesFromType(strType);
1172
1173   if (!pShares) return false;
1174
1175   // disallow virtual paths
1176   if (strUpdateElement.Equals("path") && CUtil::IsVirtualPath(strUpdateText))
1177     return false;
1178
1179   for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
1180   {
1181     if ((*it).strName == strOldName)
1182     {
1183       if ("name" == strUpdateElement)
1184         (*it).strName = strUpdateText;
1185       else if ("lockmode" == strUpdateElement)
1186         (*it).m_iLockMode = LockType(atoi(strUpdateText));
1187       else if ("lockcode" == strUpdateElement)
1188         (*it).m_strLockCode = strUpdateText;
1189       else if ("badpwdcount" == strUpdateElement)
1190         (*it).m_iBadPwdCount = atoi(strUpdateText);
1191       else if ("thumbnail" == strUpdateElement)
1192         (*it).m_strThumbnailImage = strUpdateText;
1193       else if ("path" == strUpdateElement)
1194       {
1195         (*it).vecPaths.clear();
1196         (*it).strPath = strUpdateText;
1197         (*it).vecPaths.push_back(strUpdateText);
1198       }
1199       else
1200         return false;
1201       return true;
1202     }
1203   }
1204   return false;
1205 }
1206
1207 bool CSettings::DeleteSource(const CStdString &strType, const CStdString strName, const CStdString strPath, bool virtualSource)
1208 {
1209   VECSOURCES *pShares = GetSourcesFromType(strType);
1210   if (!pShares) return false;
1211
1212   bool found(false);
1213
1214   for (IVECSOURCES it = pShares->begin(); it != pShares->end(); it++)
1215   {
1216     if ((*it).strName == strName && (*it).strPath == strPath)
1217     {
1218       CLog::Log(LOGDEBUG,"found share, removing!");
1219       pShares->erase(it);
1220       found = true;
1221       break;
1222     }
1223   }
1224
1225   if (virtualSource)
1226     return found;
1227
1228   return SaveSources();
1229 }
1230
1231 bool CSettings::AddShare(const CStdString &type, const CMediaSource &share)
1232 {
1233   VECSOURCES *pShares = GetSourcesFromType(type);
1234   if (!pShares) return false;
1235
1236   // translate dir and add to our current shares
1237   CStdString strPath1 = share.strPath;
1238   strPath1.ToUpper();
1239   if(strPath1.IsEmpty())
1240   {
1241     CLog::Log(LOGERROR, "unable to add empty path");
1242     return false;
1243   }
1244
1245   CMediaSource shareToAdd = share;
1246   if (strPath1.at(0) == '$')
1247   {
1248     shareToAdd.strPath = CUtil::TranslateSpecialSource(strPath1);
1249     if (!share.strPath.IsEmpty())
1250       CLog::Log(LOGDEBUG, "%s Translated (%s) to Path (%s)",__FUNCTION__ ,strPath1.c_str(),shareToAdd.strPath.c_str());
1251     else
1252     {
1253       CLog::Log(LOGDEBUG, "%s Skipping invalid special directory token: %s",__FUNCTION__,strPath1.c_str());
1254       return false;
1255     }
1256   }
1257   pShares->push_back(shareToAdd);
1258
1259   if (!share.m_ignore)
1260   {
1261     return SaveSources();
1262   }
1263   return true;
1264 }
1265
1266 bool CSettings::SaveSources()
1267 {
1268   // TODO: Should we be specifying utf8 here??
1269   TiXmlDocument doc;
1270   TiXmlElement xmlRootElement("sources");
1271   TiXmlNode *pRoot = doc.InsertEndChild(xmlRootElement);
1272   if (!pRoot) return false;
1273
1274   // ok, now run through and save each sources section
1275   SetSources(pRoot, "programs", m_programSources, m_defaultProgramSource);
1276   SetSources(pRoot, "video", m_videoSources, m_defaultVideoSource);
1277   SetSources(pRoot, "music", m_musicSources, m_defaultMusicSource);
1278   SetSources(pRoot, "pictures", m_pictureSources, m_defaultPictureSource);
1279   SetSources(pRoot, "files", m_fileSources, m_defaultFileSource);
1280
1281   return doc.SaveFile(GetSourcesFile());
1282 }
1283
1284 bool CSettings::SetSources(TiXmlNode *root, const char *section, const VECSOURCES &shares, const char *defaultPath)
1285 {
1286   TiXmlElement sectionElement(section);
1287   TiXmlNode *sectionNode = root->InsertEndChild(sectionElement);
1288   if (sectionNode)
1289   {
1290     XMLUtils::SetPath(sectionNode, "default", defaultPath);
1291     for (unsigned int i = 0; i < shares.size(); i++)
1292     {
1293       const CMediaSource &share = shares[i];
1294       if (share.m_ignore)
1295         continue;
1296       TiXmlElement source("source");
1297
1298       XMLUtils::SetString(&source, "name", share.strName);
1299
1300       for (unsigned int i = 0; i < share.vecPaths.size(); i++)
1301         XMLUtils::SetPath(&source, "path", share.vecPaths[i]);
1302
1303       if (share.m_iHasLock)
1304       {
1305         XMLUtils::SetInt(&source, "lockmode", share.m_iLockMode);
1306         XMLUtils::SetString(&source, "lockcode", share.m_strLockCode);
1307         XMLUtils::SetInt(&source, "badpwdcount", share.m_iBadPwdCount);
1308       }
1309       if (!share.m_strThumbnailImage.IsEmpty())
1310         XMLUtils::SetPath(&source, "thumbnail", share.m_strThumbnailImage);
1311
1312       sectionNode->InsertEndChild(source);
1313     }
1314   }
1315   return true;
1316 }
1317
1318 void CSettings::LoadSources()
1319 {
1320   // clear sources
1321   m_fileSources.clear();
1322   m_musicSources.clear();
1323   m_pictureSources.clear();
1324   m_programSources.clear();
1325   m_videoSources.clear();
1326
1327   CStdString strSourcesFile = GetSourcesFile();
1328   CLog::Log(LOGNOTICE, "Loading media sources from %s", strSourcesFile.c_str());
1329
1330   // load xml file
1331   TiXmlDocument xmlDoc;
1332   TiXmlElement *pRootElement = NULL;
1333   if (xmlDoc.LoadFile(strSourcesFile))
1334   {
1335     pRootElement = xmlDoc.RootElement();
1336     if (pRootElement && strcmpi(pRootElement->Value(),"sources") != 0)
1337       CLog::Log(LOGERROR, "%s sources.xml file does not contain <sources>", __FUNCTION__);
1338   }
1339   else if (CFile::Exists(strSourcesFile))
1340     CLog::Log(LOGERROR, "%s Error loading %s: Line %d, %s", __FUNCTION__, strSourcesFile.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
1341
1342   // parse sources
1343   if (pRootElement)
1344   {
1345     GetSources(pRootElement, "programs", m_programSources, m_defaultProgramSource);
1346     GetSources(pRootElement, "pictures", m_pictureSources, m_defaultPictureSource);
1347     GetSources(pRootElement, "files", m_fileSources, m_defaultFileSource);
1348     GetSources(pRootElement, "music", m_musicSources, m_defaultMusicSource);
1349     GetSources(pRootElement, "video", m_videoSources, m_defaultVideoSource);
1350   }
1351 }
1352
1353 void CSettings::LoadSkinSettings(const TiXmlElement* pRootElement)
1354 {
1355   int number = 0;
1356   const TiXmlElement *pElement = pRootElement->FirstChildElement("skinsettings");
1357   if (pElement)
1358   {
1359     m_skinStrings.clear();
1360     m_skinBools.clear();
1361     const TiXmlElement *pChild = pElement->FirstChildElement("setting");
1362     while (pChild)
1363     {
1364       CStdString settingName = pChild->Attribute("name");
1365       if (pChild->Attribute("type") && strcmpi(pChild->Attribute("type"),"string") == 0)
1366       { // string setting
1367         CSkinString string;
1368         string.name = settingName;
1369         string.value = pChild->FirstChild() ? pChild->FirstChild()->Value() : "";
1370         m_skinStrings.insert(pair<int, CSkinString>(number++, string));
1371       }
1372       else
1373       { // bool setting
1374         CSkinBool setting;
1375         setting.name = settingName;
1376         setting.value = pChild->FirstChild() ? strcmpi(pChild->FirstChild()->Value(), "true") == 0 : false;
1377         m_skinBools.insert(pair<int, CSkinBool>(number++, setting));
1378       }
1379       pChild = pChild->NextSiblingElement("setting");
1380     }
1381   }
1382 }
1383
1384 void CSettings::SaveSkinSettings(TiXmlNode *pRootElement) const
1385 {
1386   // add the <skinsettings> tag
1387   TiXmlElement xmlSettingsElement("skinsettings");
1388   TiXmlNode *pSettingsNode = pRootElement->InsertEndChild(xmlSettingsElement);
1389   if (!pSettingsNode) return;
1390   for (map<int, CSkinBool>::const_iterator it = m_skinBools.begin(); it != m_skinBools.end(); ++it)
1391   {
1392     // Add a <setting type="bool" name="name">true/false</setting>
1393     TiXmlElement xmlSetting("setting");
1394     xmlSetting.SetAttribute("type", "bool");
1395     xmlSetting.SetAttribute("name", (*it).second.name.c_str());
1396     TiXmlText xmlBool((*it).second.value ? "true" : "false");
1397     xmlSetting.InsertEndChild(xmlBool);
1398     pSettingsNode->InsertEndChild(xmlSetting);
1399   }
1400   for (map<int, CSkinString>::const_iterator it = m_skinStrings.begin(); it != m_skinStrings.end(); ++it)
1401   {
1402     // Add a <setting type="string" name="name">string</setting>
1403     TiXmlElement xmlSetting("setting");
1404     xmlSetting.SetAttribute("type", "string");
1405     xmlSetting.SetAttribute("name", (*it).second.name.c_str());
1406     TiXmlText xmlLabel((*it).second.value);
1407     xmlSetting.InsertEndChild(xmlLabel);
1408     pSettingsNode->InsertEndChild(xmlSetting);
1409   }
1410 }
1411
1412 void CSettings::Clear()
1413 {
1414   m_programSources.clear();
1415   m_pictureSources.clear();
1416   m_fileSources.clear();
1417   m_musicSources.clear();
1418   m_videoSources.clear();
1419 //  m_vecIcons.clear();
1420   m_vecProfiles.clear();
1421   m_mapRssUrls.clear();
1422   m_skinBools.clear();
1423   m_skinStrings.clear();
1424 }
1425
1426 int CSettings::TranslateSkinString(const CStdString &setting)
1427 {
1428   CStdString settingName;
1429   settingName.Format("%s.%s", g_guiSettings.GetString("lookandfeel.skin").c_str(), setting);
1430   // run through and see if we have this setting
1431   for (map<int, CSkinString>::const_iterator it = m_skinStrings.begin(); it != m_skinStrings.end(); it++)
1432   {
1433     if (settingName.Equals((*it).second.name))
1434       return (*it).first;
1435   }
1436   // didn't find it - insert it
1437   CSkinString skinString;
1438   skinString.name = settingName;
1439   m_skinStrings.insert(pair<int, CSkinString>(m_skinStrings.size() + m_skinBools.size(), skinString));
1440   return m_skinStrings.size() + m_skinBools.size() - 1;
1441 }
1442
1443 const CStdString &CSettings::GetSkinString(int setting) const
1444 {
1445   map<int, CSkinString>::const_iterator it = m_skinStrings.find(setting);
1446   if (it != m_skinStrings.end())
1447   {
1448     return (*it).second.value;
1449   }
1450   return StringUtils::EmptyString;
1451 }
1452
1453 void CSettings::SetSkinString(int setting, const CStdString &label)
1454 {
1455   map<int, CSkinString>::iterator it = m_skinStrings.find(setting);
1456   if (it != m_skinStrings.end())
1457   {
1458     (*it).second.value = label;
1459     return;
1460   }
1461   assert(false);
1462   CLog::Log(LOGFATAL, "%s : Unknown setting requested", __FUNCTION__);
1463 }
1464
1465 void CSettings::ResetSkinSetting(const CStdString &setting)
1466 {
1467   CStdString settingName;
1468   settingName.Format("%s.%s", g_guiSettings.GetString("lookandfeel.skin").c_str(), setting);
1469   // run through and see if we have this setting as a string
1470   for (map<int, CSkinString>::iterator it = m_skinStrings.begin(); it != m_skinStrings.end(); it++)
1471   {
1472     if (settingName.Equals((*it).second.name))
1473     {
1474       (*it).second.value = "";
1475       return;
1476     }
1477   }
1478   // and now check for the skin bool
1479   for (map<int, CSkinBool>::iterator it = m_skinBools.begin(); it != m_skinBools.end(); it++)
1480   {
1481     if (settingName.Equals((*it).second.name))
1482     {
1483       (*it).second.value = false;
1484       return;
1485     }
1486   }
1487 }
1488
1489 int CSettings::TranslateSkinBool(const CStdString &setting)
1490 {
1491   CStdString settingName;
1492   settingName.Format("%s.%s", g_guiSettings.GetString("lookandfeel.skin").c_str(), setting);
1493   // run through and see if we have this setting
1494   for (map<int, CSkinBool>::const_iterator it = m_skinBools.begin(); it != m_skinBools.end(); it++)
1495   {
1496     if (settingName.Equals((*it).second.name))
1497       return (*it).first;
1498   }
1499   // didn't find it - insert it
1500   CSkinBool skinBool;
1501   skinBool.name = settingName;
1502   skinBool.value = false;
1503   m_skinBools.insert(pair<int, CSkinBool>(m_skinBools.size() + m_skinStrings.size(), skinBool));
1504   return m_skinBools.size() + m_skinStrings.size() - 1;
1505 }
1506
1507 bool CSettings::GetSkinBool(int setting) const
1508 {
1509   map<int, CSkinBool>::const_iterator it = m_skinBools.find(setting);
1510   if (it != m_skinBools.end())
1511   {
1512     return (*it).second.value;
1513   }
1514   // default is to return false
1515   return false;
1516 }
1517
1518 void CSettings::SetSkinBool(int setting, bool set)
1519 {
1520   map<int, CSkinBool>::iterator it = m_skinBools.find(setting);
1521   if (it != m_skinBools.end())
1522   {
1523     (*it).second.value = set;
1524     return;
1525   }
1526   assert(false);
1527   CLog::Log(LOGFATAL,"%s : Unknown setting requested", __FUNCTION__);
1528 }
1529
1530 void CSettings::ResetSkinSettings()
1531 {
1532   CStdString currentSkin = g_guiSettings.GetString("lookandfeel.skin") + ".";
1533   // clear all the settings and strings from this skin.
1534   map<int, CSkinBool>::iterator it = m_skinBools.begin();
1535   while (it != m_skinBools.end())
1536   {
1537     CStdString skinName = (*it).second.name;
1538     if (skinName.Left(currentSkin.size()) == currentSkin)
1539       (*it).second.value = false;
1540
1541     it++;
1542   }
1543   map<int, CSkinString>::iterator it2 = m_skinStrings.begin();
1544   while (it2 != m_skinStrings.end())
1545   {
1546     CStdString skinName = (*it2).second.name;
1547     if (skinName.Left(currentSkin.size()) == currentSkin)
1548       (*it2).second.value = "";
1549
1550     it2++;
1551   }
1552   g_infoManager.ResetCache();
1553 }
1554
1555 static CStdString ToWatchContent(const CStdString &content)
1556 {
1557   if (content == "seasons" || content == "episodes")
1558    return "tvshows";
1559   else
1560     return content;
1561 }
1562
1563 int CSettings::GetWatchMode(const CStdString& content) const
1564 {
1565   std::map<CStdString, int>::iterator it = g_settings.m_watchMode.find(ToWatchContent(content));
1566   if (it != g_settings.m_watchMode.end())
1567     return it->second;
1568   return VIDEO_SHOW_ALL;
1569 }
1570
1571 void CSettings::SetWatchMode(const CStdString& content, int value)
1572 {
1573   std::map<CStdString, int>::iterator it = g_settings.m_watchMode.find(ToWatchContent(content));
1574   if (it != g_settings.m_watchMode.end())
1575     it->second = value;
1576 }
1577
1578 void CSettings::CycleWatchMode(const CStdString& content)
1579 {
1580   std::map<CStdString, int>::iterator it = g_settings.m_watchMode.find(ToWatchContent(content));
1581   if (it != g_settings.m_watchMode.end())
1582   {
1583     it->second++;
1584     if (it->second > VIDEO_SHOW_WATCHED)
1585       it->second = VIDEO_SHOW_ALL;
1586   }
1587 }
1588
1589 void CSettings::LoadUserFolderLayout()
1590 {
1591   // check them all
1592   CStdString strDir = g_guiSettings.GetString("system.playlistspath");
1593   if (strDir == "set default")
1594   {
1595     strDir = "special://profile/playlists/";
1596     g_guiSettings.SetString("system.playlistspath",strDir.c_str());
1597   }
1598   CDirectory::Create(strDir);
1599   CDirectory::Create(CUtil::AddFileToFolder(strDir,"music"));
1600   CDirectory::Create(CUtil::AddFileToFolder(strDir,"video"));
1601   CDirectory::Create(CUtil::AddFileToFolder(strDir,"mixed"));
1602 }
1603
1604 CStdString CSettings::GetProfileUserDataFolder() const
1605 {
1606   CStdString folder;
1607   if (m_currentProfile == 0)
1608     return GetUserDataFolder();
1609
1610   CUtil::AddFileToFolder(GetUserDataFolder(),GetCurrentProfile().getDirectory(),folder);
1611
1612   return folder;
1613 }
1614
1615 CStdString CSettings::GetUserDataItem(const CStdString& strFile) const
1616 {
1617   CStdString folder;
1618   folder = "special://profile/"+strFile;
1619   //check if item exists in the profile
1620   //(either for folder or for a file (depending on slashAtEnd of strFile)
1621   //otherwise return path to masterprofile
1622   if ( (CUtil::HasSlashAtEnd(folder) && !CDirectory::Exists(folder)) || !CFile::Exists(folder))
1623     folder = "special://masterprofile/"+strFile;
1624   return folder;
1625 }
1626
1627 CStdString CSettings::GetUserDataFolder() const
1628 {
1629   return GetMasterProfile().getDirectory();
1630 }
1631
1632 CStdString CSettings::GetDatabaseFolder() const
1633 {
1634   CStdString folder;
1635   if (GetCurrentProfile().hasDatabases())
1636     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Database", folder);
1637   else
1638     CUtil::AddFileToFolder(GetUserDataFolder(), "Database", folder);
1639
1640   return folder;
1641 }
1642
1643 CStdString CSettings::GetCDDBFolder() const
1644 {
1645   CStdString folder;
1646   if (GetCurrentProfile().hasDatabases())
1647     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Database/CDDB", folder);
1648   else
1649     CUtil::AddFileToFolder(GetUserDataFolder(), "Database/CDDB", folder);
1650
1651   return folder;
1652 }
1653
1654 CStdString CSettings::GetThumbnailsFolder() const
1655 {
1656   CStdString folder;
1657   if (GetCurrentProfile().hasDatabases())
1658     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails", folder);
1659   else
1660     CUtil::AddFileToFolder(GetUserDataFolder(), "Thumbnails", folder);
1661
1662   return folder;
1663 }
1664
1665 CStdString CSettings::GetMusicThumbFolder() const
1666 {
1667   CStdString folder;
1668   if (GetCurrentProfile().hasDatabases())
1669     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails/Music", folder);
1670   else
1671     CUtil::AddFileToFolder(GetUserDataFolder(), "Thumbnails/Music", folder);
1672
1673   return folder;
1674 }
1675
1676 CStdString CSettings::GetLastFMThumbFolder() const
1677 {
1678   CStdString folder;
1679   if (GetCurrentProfile().hasDatabases())
1680     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails/Music/LastFM", folder);
1681   else
1682     CUtil::AddFileToFolder(GetUserDataFolder(), "Thumbnails/Music/LastFM", folder);
1683
1684   return folder;
1685 }
1686
1687 CStdString CSettings::GetMusicArtistThumbFolder() const
1688 {
1689   CStdString folder;
1690   if (GetCurrentProfile().hasDatabases())
1691     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails/Music/Artists", folder);
1692   else
1693     CUtil::AddFileToFolder(GetUserDataFolder(), "Thumbnails/Music/Artists", folder);
1694
1695   return folder;
1696 }
1697
1698 CStdString CSettings::GetVideoThumbFolder() const
1699 {
1700   CStdString folder;
1701   if (GetCurrentProfile().hasDatabases())
1702     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails/Video", folder);
1703   else
1704     CUtil::AddFileToFolder(GetUserDataFolder(), "Thumbnails/Video", folder);
1705
1706   return folder;
1707 }
1708
1709 CStdString CSettings::GetVideoFanartFolder() const
1710 {
1711   CStdString folder;
1712   if (GetCurrentProfile().hasDatabases())
1713     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails/Video/Fanart", folder);
1714   else
1715     CUtil::AddFileToFolder(GetUserDataFolder(), "Thumbnails/Video/Fanart", folder);
1716
1717   return folder;
1718 }
1719
1720 CStdString CSettings::GetMusicFanartFolder() const
1721 {
1722   CStdString folder;
1723   if (GetCurrentProfile().hasDatabases())
1724     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails/Music/Fanart", folder);
1725   else
1726     CUtil::AddFileToFolder(GetUserDataFolder(), "Thumbnails/Music/Fanart", folder);
1727
1728   return folder;
1729 }
1730
1731 CStdString CSettings::GetBookmarksThumbFolder() const
1732 {
1733   CStdString folder;
1734   if (GetCurrentProfile().hasDatabases())
1735     CUtil::AddFileToFolder(GetProfileUserDataFolder(), "Thumbnails/Video/Bookmarks", folder);
1736   else
1737     CUtil::AddFileToFolder(GetUserDataFolder(), "Thumbnails/Video/Bookmarks", folder);
1738
1739   return folder;
1740 }
1741
1742 CStdString CSettings::GetSourcesFile() const
1743 {
1744   CStdString folder;
1745   if (GetCurrentProfile().hasSources())
1746     CUtil::AddFileToFolder(GetProfileUserDataFolder(),"sources.xml",folder);
1747   else
1748     CUtil::AddFileToFolder(GetUserDataFolder(),"sources.xml",folder);
1749
1750   return folder;
1751 }
1752
1753 void CSettings::LoadRSSFeeds()
1754 {
1755   CStdString rssXML;
1756   rssXML = GetUserDataItem("RssFeeds.xml");
1757   TiXmlDocument rssDoc;
1758   if (!CFile::Exists(rssXML))
1759   { // set defaults, or assume no rss feeds??
1760     return;
1761   }
1762   if (!rssDoc.LoadFile(rssXML))
1763   {
1764     CLog::Log(LOGERROR, "Error loading %s, Line %d\n%s", rssXML.c_str(), rssDoc.ErrorRow(), rssDoc.ErrorDesc());
1765     return;
1766   }
1767
1768   TiXmlElement *pRootElement = rssDoc.RootElement();
1769   if (!pRootElement || strcmpi(pRootElement->Value(),"rssfeeds") != 0)
1770   {
1771     CLog::Log(LOGERROR, "Error loading %s, no <rssfeeds> node", rssXML.c_str());
1772     return;
1773   }
1774
1775   m_mapRssUrls.clear();
1776   TiXmlElement* pSet = pRootElement->FirstChildElement("set");
1777   while (pSet)
1778   {
1779     int iId;
1780     if (pSet->QueryIntAttribute("id", &iId) == TIXML_SUCCESS)
1781     {
1782       RssSet set;
1783       set.rtl = pSet->Attribute("rtl") && strcasecmp(pSet->Attribute("rtl"),"true")==0;
1784       TiXmlElement* pFeed = pSet->FirstChildElement("feed");
1785       while (pFeed)
1786       {
1787         int iInterval;
1788         if ( pFeed->QueryIntAttribute("updateinterval",&iInterval) != TIXML_SUCCESS)
1789         {
1790           iInterval=30; // default to 30 min
1791           CLog::Log(LOGDEBUG,"no interval set, default to 30!");
1792         }
1793         if (pFeed->FirstChild())
1794         {
1795           // TODO: UTF-8: Do these URLs need to be converted to UTF-8?
1796           //              What about the xml encoding?
1797           CStdString strUrl = pFeed->FirstChild()->Value();
1798           set.url.push_back(strUrl);
1799           set.interval.push_back(iInterval);
1800         }
1801         pFeed = pFeed->NextSiblingElement("feed");
1802       }
1803       m_mapRssUrls.insert(make_pair(iId,set));
1804     }
1805     else
1806       CLog::Log(LOGERROR,"found rss url set with no id in RssFeeds.xml, ignored");
1807
1808     pSet = pSet->NextSiblingElement("set");
1809   }
1810 }
1811
1812 CStdString CSettings::GetSettingsFile() const
1813 {
1814   CStdString settings;
1815   if (m_currentProfile == 0)
1816     settings = "special://masterprofile/guisettings.xml";
1817   else
1818     settings = "special://profile/guisettings.xml";
1819   return settings;
1820 }
1821
1822 void CSettings::CreateProfileFolders()
1823 {
1824   CDirectory::Create(GetDatabaseFolder());
1825   CDirectory::Create(GetCDDBFolder());
1826
1827   // Thumbnails/
1828   CDirectory::Create(GetThumbnailsFolder());
1829   CDirectory::Create(GetMusicThumbFolder());
1830   CDirectory::Create(GetMusicArtistThumbFolder());
1831   CDirectory::Create(GetLastFMThumbFolder());
1832   CDirectory::Create(GetVideoThumbFolder());
1833   CDirectory::Create(GetVideoFanartFolder());
1834   CDirectory::Create(GetMusicFanartFolder());
1835   CDirectory::Create(GetBookmarksThumbFolder());
1836   CStdString generatedThumbsFolder = CUtil::AddFileToFolder(GetThumbnailsFolder(), "generated");
1837   CDirectory::Create(generatedThumbsFolder);
1838   CLog::Log(LOGINFO, "thumbnails folder: %s", GetThumbnailsFolder().c_str());
1839   for (unsigned int hex=0; hex < 16; hex++)
1840   {
1841     CStdString strHex;
1842     strHex.Format("%x",hex);
1843     CDirectory::Create(CUtil::AddFileToFolder(GetMusicThumbFolder(), strHex));
1844     CDirectory::Create(CUtil::AddFileToFolder(GetVideoThumbFolder(), strHex));
1845     CDirectory::Create(CUtil::AddFileToFolder(GetThumbnailsFolder(), strHex));
1846     CDirectory::Create(CUtil::AddFileToFolder(generatedThumbsFolder, strHex));
1847   }
1848   CDirectory::Create("special://profile/addon_data");
1849   CDirectory::Create("special://profile/keymaps");
1850 }
1851
1852 static CProfile emptyProfile;
1853
1854 const CProfile &CSettings::GetMasterProfile() const
1855 {
1856   if (GetNumProfiles())
1857     return m_vecProfiles[0];
1858   CLog::Log(LOGERROR, "%s - master profile requested while none exists", __FUNCTION__);
1859   return emptyProfile;
1860 }
1861
1862 const CProfile &CSettings::GetCurrentProfile() const
1863 {
1864   if (m_currentProfile < m_vecProfiles.size())
1865     return m_vecProfiles[m_currentProfile];
1866   CLog::Log(LOGERROR, "%s - last profile index (%u) is outside the valid range (%ld)", __FUNCTION__, m_currentProfile, m_vecProfiles.size());
1867   return emptyProfile;
1868 }
1869
1870 void CSettings::UpdateCurrentProfileDate()
1871 {
1872   if (m_currentProfile < m_vecProfiles.size())
1873     m_vecProfiles[m_currentProfile].setDate();
1874 }
1875
1876 const CProfile *CSettings::GetProfile(unsigned int index) const
1877 {
1878   if (index < GetNumProfiles())
1879     return &m_vecProfiles[index];
1880   return NULL;
1881 }
1882
1883 CProfile *CSettings::GetProfile(unsigned int index)
1884 {
1885   if (index < GetNumProfiles())
1886     return &m_vecProfiles[index];
1887   return NULL;
1888 }
1889
1890 unsigned int CSettings::GetNumProfiles() const
1891 {
1892   return m_vecProfiles.size();
1893 }
1894
1895 int CSettings::GetProfileIndex(const CStdString &name) const
1896 {
1897   for (unsigned int i = 0; i < m_vecProfiles.size(); i++)
1898     if (m_vecProfiles[i].getName().Equals(name))
1899       return i;
1900   return -1;
1901 }
1902
1903 void CSettings::AddProfile(const CProfile &profile)
1904 {
1905   m_vecProfiles.push_back(profile);
1906 }
1907
1908 void CSettings::LoadMasterForLogin()
1909 {
1910   // save the previous user
1911   m_lastUsedProfile = m_currentProfile;
1912   if (m_currentProfile != 0)
1913     LoadProfile(0);
1914 }