Merge remote branch 'origin/trunk' into trac-8658
[xbmc:xbmc-antiquated.git] / xbmc / GUIDialogAddonSettings.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 "GUIDialogAddonSettings.h"
23 #include "FileSystem/PluginDirectory.h"
24 #include "addons/IAddon.h"
25 #include "addons/AddonManager.h"
26 #include "GUIDialogNumeric.h"
27 #include "GUIDialogFileBrowser.h"
28 #include "GUIDialogOK.h"
29 #include "GUIControlGroupList.h"
30 #include "GUISettingsSliderControl.h"
31 #include "Util.h"
32 #include "StringUtils.h"
33 #include "MediaManager.h"
34 #include "GUILabelControl.h"
35 #include "GUIRadioButtonControl.h"
36 #include "GUISpinControlEx.h"
37 #include "GUIImage.h"
38 #include "FileSystem/Directory.h"
39 #include "VideoInfoScanner.h"
40 #include "addons/Scraper.h"
41 #include "GUIWindowManager.h"
42 #include "Application.h"
43 #include "GUIDialogKeyboard.h"
44 #include "FileItem.h"
45 #include "Settings.h"
46 #include "GUIInfoManager.h"
47 #include "GUIDialogSelect.h"
48
49 using namespace std;
50 using namespace ADDON;
51 using XFILE::CDirectory;
52
53 #define CONTROL_SETTINGS_AREA           2
54 #define CONTROL_DEFAULT_BUTTON          3
55 #define CONTROL_DEFAULT_RADIOBUTTON     4
56 #define CONTROL_DEFAULT_SPIN            5
57 #define CONTROL_DEFAULT_SEPARATOR       6
58 #define CONTROL_DEFAULT_LABEL_SEPARATOR 7
59 #define CONTROL_DEFAULT_SLIDER          8
60 #define CONTROL_SECTION_AREA            9
61 #define CONTROL_DEFAULT_SECTION_BUTTON  13
62
63 #define ID_BUTTON_OK                    10
64 #define ID_BUTTON_CANCEL                11
65 #define ID_BUTTON_DEFAULT               12
66 #define CONTROL_HEADING_LABEL           20
67
68 #define CONTROL_START_SETTING           100
69 #define CONTROL_START_SECTION           200
70
71 CGUIDialogAddonSettings::CGUIDialogAddonSettings()
72    : CGUIDialogBoxBase(WINDOW_DIALOG_ADDON_SETTINGS, "DialogAddonSettings.xml")
73 {
74   m_currentSection = 0;
75   m_totalSections = 1;
76 }
77
78 CGUIDialogAddonSettings::~CGUIDialogAddonSettings(void)
79 {
80 }
81
82 bool CGUIDialogAddonSettings::OnMessage(CGUIMessage& message)
83 {
84   switch (message.GetMessage())
85   {
86     case GUI_MSG_WINDOW_DEINIT:
87     {
88       FreeControls();
89       FreeSections();
90     }
91     break;
92     case GUI_MSG_CLICKED:
93     {
94       int iControl = message.GetSenderId();
95       bool bCloseDialog = false;
96
97       if (iControl == ID_BUTTON_DEFAULT)
98         SetDefaults();
99       else if (iControl != ID_BUTTON_OK)
100         bCloseDialog = ShowVirtualKeyboard(iControl);
101
102       if (iControl == ID_BUTTON_OK || iControl == ID_BUTTON_CANCEL || bCloseDialog)
103       {
104         if (iControl == ID_BUTTON_OK || bCloseDialog)
105         {
106           m_bConfirmed = true;
107           SaveSettings();
108         }
109         Close();
110         return true;
111       }
112     }
113     break;
114     case GUI_MSG_FOCUSED:
115     {
116       CGUIDialogBoxBase::OnMessage(message);
117       int focusedControl = GetFocusedControlID();
118       if (focusedControl >= CONTROL_START_SECTION && focusedControl < (int)(CONTROL_START_SECTION + m_totalSections) &&
119           focusedControl - CONTROL_START_SECTION != (int)m_currentSection)
120       { // changing section
121         UpdateFromControls();
122         m_currentSection = focusedControl - CONTROL_START_SECTION;
123         CreateControls();
124       }
125       return true;
126     }
127   }
128   return CGUIDialogBoxBase::OnMessage(message);
129 }
130
131 void CGUIDialogAddonSettings::OnInitWindow()
132 {
133   m_currentSection = 0;
134   m_totalSections = 1;
135   CreateSections();
136   CreateControls();
137   CGUIDialogBoxBase::OnInitWindow();
138 }
139
140 // \brief Show CGUIDialogOK dialog, then wait for user to dismiss it.
141 bool CGUIDialogAddonSettings::ShowAndGetInput(const AddonPtr &addon, bool saveToDisk /* = true */)
142 {
143   if (!addon)
144     return false;
145
146   bool ret(false);
147   if (addon->HasSettings())
148   { 
149     // Create the dialog
150     CGUIDialogAddonSettings* pDialog = NULL;
151     pDialog = (CGUIDialogAddonSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_ADDON_SETTINGS);
152     if (!pDialog)
153       return false;
154
155     // Set the heading
156     CStdString heading;
157     heading.Format("$LOCALIZE[10004] - %s", addon->Name().c_str()); // "Settings - AddonName"
158     pDialog->m_strHeading = heading;
159
160     pDialog->m_changed = false;
161     pDialog->m_addon = addon;
162     pDialog->m_saveToDisk = saveToDisk;
163     pDialog->DoModal();
164     ret = true;
165   }
166   else
167   { // addon does not support settings, inform user
168     CGUIDialogOK::ShowAndGetInput(24000,0,24030,0);
169   }
170
171   return ret;
172 }
173
174 bool CGUIDialogAddonSettings::ShowVirtualKeyboard(int iControl)
175 {
176   int controlId = CONTROL_START_SETTING;
177   bool bCloseDialog = false;
178
179   const TiXmlElement *setting = GetFirstSetting();
180   while (setting)
181   {
182     if (controlId == iControl)
183     {
184       const CGUIControl* control = GetControl(controlId);
185       if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
186       {
187         const char *id = setting->Attribute("id");
188         const char *type = setting->Attribute("type");
189         const char *option = setting->Attribute("option");
190         const char *source = setting->Attribute("source");
191         CStdString value = m_buttonValues[id];
192         CStdString label = GetString(setting->Attribute("label"));
193
194         if (strcmp(type, "text") == 0)
195         {
196           // get any options
197           bool bHidden  = false;
198           bool bEncoded = false;
199           if (option)
200           {
201             bHidden = (strstr(option, "hidden") != NULL);
202             bEncoded = (strstr(option, "urlencoded") != NULL);
203           }
204           if (bEncoded)
205             CUtil::URLDecode(value);
206
207           if (CGUIDialogKeyboard::ShowAndGetInput(value, label, true, bHidden))
208           {
209             // if hidden hide input
210             if (bHidden)
211             {
212               CStdString hiddenText;
213               hiddenText.append(value.size(), L'*');
214               ((CGUIButtonControl *)control)->SetLabel2(hiddenText);
215             }
216             else
217               ((CGUIButtonControl*) control)->SetLabel2(value);
218             if (bEncoded)
219               CUtil::URLEncode(value);
220           }
221         }
222         else if (strcmp(type, "number") == 0 && CGUIDialogNumeric::ShowAndGetNumber(value, label))
223         {
224           ((CGUIButtonControl*) control)->SetLabel2(value);
225         }
226         else if (strcmp(type, "ipaddress") == 0 && CGUIDialogNumeric::ShowAndGetIPAddress(value, label))
227         {
228           ((CGUIButtonControl*) control)->SetLabel2(value);
229         }
230         else if (strcmpi(type, "select") == 0)
231         {
232           CGUIDialogSelect *pDlg = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
233           if (pDlg)
234           {
235             pDlg->SetHeading(label.c_str());
236             pDlg->Reset();
237
238             vector<CStdString> valuesVec;
239             if (setting->Attribute("values"))
240               CUtil::Tokenize(setting->Attribute("values"), valuesVec, "|");
241             else if (setting->Attribute("lvalues"))
242             { // localize
243               CUtil::Tokenize(setting->Attribute("lvalues"), valuesVec, "|");
244               for (unsigned int i = 0; i < valuesVec.size(); i++)
245               {
246                 CStdString value = m_addon->GetString(atoi(valuesVec[i]));
247                 if (value.IsEmpty())
248                   value = g_localizeStrings.Get(atoi(valuesVec[i]));
249                 valuesVec[i] = value;
250               }
251             }
252             else if (source)
253             {
254               valuesVec = GetFileEnumValues(source, setting->Attribute("mask"), setting->Attribute("option"));
255             }
256
257             for (unsigned int i = 0; i < valuesVec.size(); i++)
258             {
259               pDlg->Add(valuesVec[i]);
260               if (valuesVec[i].Equals(value))
261                 pDlg->SetSelected(i); // FIXME: the SetSelected() does not select "i", it always defaults to the first position
262             }
263             pDlg->DoModal();
264             int iSelected = pDlg->GetSelectedLabel();
265             if (iSelected >= 0)
266             {
267               value = valuesVec[iSelected];
268               ((CGUIButtonControl*) control)->SetLabel2(value);
269             }
270           }
271         }
272         else if (strcmpi(type, "audio") == 0 || strcmpi(type, "video") == 0 ||
273           strcmpi(type, "image") == 0 || strcmpi(type, "executable") == 0 ||
274           strcmpi(type, "file") == 0 || strcmpi(type, "folder") == 0)
275         {
276           // setup the shares
277           VECSOURCES *shares = NULL;
278           if (!source || strcmpi(source, "") == 0)
279             shares = g_settings.GetSourcesFromType(type);
280           else
281             shares = g_settings.GetSourcesFromType(source);
282
283           VECSOURCES localShares;
284           if (!shares)
285           {
286             VECSOURCES networkShares;
287             g_mediaManager.GetLocalDrives(localShares);
288             if (!source || strcmpi(source, "local") != 0)
289               g_mediaManager.GetNetworkLocations(networkShares);
290             localShares.insert(localShares.end(), networkShares.begin(), networkShares.end());
291             shares = &localShares;
292           }
293
294           if (strcmpi(type, "folder") == 0)
295           {
296             // get any options
297             bool bWriteOnly = false;
298             if (option)
299               bWriteOnly = (strcmpi(option, "writeable") == 0);
300
301             if (CGUIDialogFileBrowser::ShowAndGetDirectory(*shares, label, value, bWriteOnly))
302               ((CGUIButtonControl*) control)->SetLabel2(value);
303           }
304           else if (strcmpi(type, "image") == 0)
305           {
306             if (CGUIDialogFileBrowser::ShowAndGetImage(*shares, label, value))
307               ((CGUIButtonControl*) control)->SetLabel2(value);
308           }
309           else
310           {
311             // set the proper mask
312             CStdString strMask;
313             if (setting->Attribute("mask"))
314             {
315               strMask = setting->Attribute("mask");
316               // convert mask qualifiers
317               strMask.Replace("$AUDIO", g_settings.m_musicExtensions);
318               strMask.Replace("$VIDEO", g_settings.m_videoExtensions);
319               strMask.Replace("$IMAGE", g_settings.m_pictureExtensions);
320 #if defined(_WIN32_WINNT)
321               strMask.Replace("$EXECUTABLE", ".exe|.bat|.cmd|.py");
322 #else
323               strMask.Replace("$EXECUTABLE", "");
324 #endif
325             }
326             else
327             {
328               if (strcmpi(type, "video") == 0)
329                 strMask = g_settings.m_videoExtensions;
330               else if (strcmpi(type, "audio") == 0)
331                 strMask = g_settings.m_musicExtensions;
332               else if (strcmpi(type, "executable") == 0)
333 #if defined(_WIN32_WINNT)
334                 strMask = ".exe|.bat|.cmd|.py";
335 #else
336                 strMask = "";
337 #endif
338             }
339
340             // get any options
341             bool bUseThumbs = false;
342             bool bUseFileDirectories = false;
343             if (option)
344             {
345               vector<CStdString> options;
346               StringUtils::SplitString(option, "|", options);
347               bUseThumbs = find(options.begin(), options.end(), "usethumbs") != options.end();
348               bUseFileDirectories = find(options.begin(), options.end(), "treatasfolder") != options.end();
349             }
350
351             if (CGUIDialogFileBrowser::ShowAndGetFile(*shares, strMask, label, value))
352               ((CGUIButtonControl*) control)->SetLabel2(value);
353           }
354         }
355         else if (strcmpi(type, "action") == 0)
356         {
357           CStdString action = setting->Attribute("action");
358           if (!action.IsEmpty())
359           {
360             // replace $CWD with the url of plugin/script
361             action.Replace("$CWD", m_addon->Path());
362             action.Replace("$ID", m_addon->ID());
363             if (option)
364               bCloseDialog = (strcmpi(option, "close") == 0);
365             g_application.getApplicationMessenger().ExecBuiltIn(action);
366           }
367         }
368         else if (strcmp(type, "date") == 0)
369         {
370           CDateTime date;
371           if (!value.IsEmpty())
372             date.SetFromDBDate(value);
373           SYSTEMTIME timedate;
374           date.GetAsSystemTime(timedate);
375           if(CGUIDialogNumeric::ShowAndGetDate(timedate, label))
376           {
377             date = timedate;
378             value = date.GetAsDBDate();
379             ((CGUIButtonControl*) control)->SetLabel2(value);
380           }
381         }
382         else if (strcmp(type, "time") == 0)
383         {
384           SYSTEMTIME timedate;
385           if (!value.IsEmpty())
386           {
387             // assumes HH:MM
388             timedate.wHour = atoi(value.Left(2));
389             timedate.wMinute = atoi(value.Right(2));
390           }
391           if (CGUIDialogNumeric::ShowAndGetTime(timedate, label))
392           {
393             value.Format("%02d:%02d", timedate.wHour, timedate.wMinute);
394             ((CGUIButtonControl*) control)->SetLabel2(value);
395           }
396         }
397         m_buttonValues[id] = value;
398         break;
399       }
400     }
401     setting = setting->NextSiblingElement("setting");
402     controlId++;
403   }
404   EnableControls();
405   return bCloseDialog;
406 }
407
408 void CGUIDialogAddonSettings::UpdateFromControls()
409 {
410   int controlID = CONTROL_START_SETTING;
411   const TiXmlElement *setting = GetFirstSetting();
412   while (setting)
413   {
414     CStdString id = setting->Attribute("id");
415     const char *type = setting->Attribute("type");
416     const CGUIControl* control = GetControl(controlID++);
417
418     if (control)
419     {
420       CStdString value;
421       switch (control->GetControlType())
422       {
423         case CGUIControl::GUICONTROL_BUTTON:
424           value = m_buttonValues[id];
425           break;
426         case CGUIControl::GUICONTROL_RADIO:
427           value = ((CGUIRadioButtonControl*) control)->IsSelected() ? "true" : "false";
428           break;
429         case CGUIControl::GUICONTROL_SPINEX:
430           if (strcmpi(type, "fileenum") == 0 || strcmpi(type, "labelenum") == 0)
431             value = ((CGUISpinControlEx*) control)->GetLabel();
432           else
433             value.Format("%i", ((CGUISpinControlEx*) control)->GetValue());
434           break;
435         case CGUIControl::GUICONTROL_SETTINGS_SLIDER:
436           value.Format("%f", ((CGUISettingsSliderControl *)control)->GetFloatValue());
437           break;
438         default:
439           break;
440       }
441       m_settings[id] = value;
442     }
443
444     setting = setting->NextSiblingElement("setting");
445   }
446 }
447
448 void CGUIDialogAddonSettings::SaveSettings(void)
449 {
450   UpdateFromControls();
451
452   for (map<CStdString, CStdString>::iterator i = m_settings.begin(); i != m_settings.end(); ++i)
453     m_addon->UpdateSetting(i->first, i->second);
454
455   if (m_saveToDisk)
456     m_addon->SaveSettings();
457 }
458
459 void CGUIDialogAddonSettings::FreeSections()
460 {
461   CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_SETTINGS_AREA);
462   if (group)
463   {
464     group->FreeResources();
465     group->ClearAll();
466   }
467   m_settings.clear();
468   m_buttonValues.clear();
469 }
470
471 void CGUIDialogAddonSettings::FreeControls()
472 {
473   // clear the category group
474   CGUIControlGroupList *control = (CGUIControlGroupList *)GetControl(CONTROL_SETTINGS_AREA);
475   if (control)
476   {
477     control->FreeResources();
478     control->ClearAll();
479   }
480 }
481
482 void CGUIDialogAddonSettings::CreateSections()
483 {
484   CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_SECTION_AREA);
485   CGUIButtonControl *originalButton = (CGUIButtonControl *)GetControl(CONTROL_DEFAULT_SECTION_BUTTON);
486   if (!m_addon)
487     return;
488
489   if (originalButton)
490     originalButton->SetVisible(false);
491
492   // clear the category group
493   FreeSections();
494
495   // grab our categories
496   const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category");
497   if (!category) // add a default one...
498     category = m_addon->GetSettingsXML();
499  
500   int buttonID = CONTROL_START_SECTION;
501   while (category)
502   { // add a category
503     CGUIButtonControl *button = originalButton ? originalButton->Clone() : NULL;
504
505     CStdString label = GetString(category->Attribute("label"));
506     if (label.IsEmpty())
507       label = g_localizeStrings.Get(128);
508
509     // add the category button
510     if (button && group)
511     {
512       button->SetID(buttonID++);
513       button->SetLabel(label);
514       button->SetVisible(true);
515       group->AddControl(button);
516     }
517
518     // grab a local copy of all the settings in this category
519     const TiXmlElement *setting = category->FirstChildElement("setting");
520     while (setting)
521     {
522       const char *id = setting->Attribute("id");
523       if (id)
524         m_settings[id] = m_addon->GetSetting(id);
525       setting = setting->NextSiblingElement("setting");
526     }
527     category = category->NextSiblingElement("category");
528   }
529   m_totalSections = buttonID - CONTROL_START_SECTION;
530 }
531
532 void CGUIDialogAddonSettings::CreateControls()
533 {
534   FreeControls();
535
536   CGUISpinControlEx *pOriginalSpin = (CGUISpinControlEx*)GetControl(CONTROL_DEFAULT_SPIN);
537   CGUIRadioButtonControl *pOriginalRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_DEFAULT_RADIOBUTTON);
538   CGUIButtonControl *pOriginalButton = (CGUIButtonControl *)GetControl(CONTROL_DEFAULT_BUTTON);
539   CGUIImage *pOriginalImage = (CGUIImage *)GetControl(CONTROL_DEFAULT_SEPARATOR);
540   CGUILabelControl *pOriginalLabel = (CGUILabelControl *)GetControl(CONTROL_DEFAULT_LABEL_SEPARATOR);
541   CGUISettingsSliderControl *pOriginalSlider = (CGUISettingsSliderControl *)GetControl(CONTROL_DEFAULT_SLIDER);
542
543   if (!m_addon || !pOriginalSpin || !pOriginalRadioButton || !pOriginalButton || !pOriginalImage
544                || !pOriginalLabel || !pOriginalSlider)
545     return;
546
547   pOriginalSpin->SetVisible(false);
548   pOriginalRadioButton->SetVisible(false);
549   pOriginalButton->SetVisible(false);
550   pOriginalImage->SetVisible(false);
551   pOriginalLabel->SetVisible(false);
552   pOriginalSlider->SetVisible(false);
553
554   // clear the category group
555   CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_SETTINGS_AREA);
556   if (!group)
557     return;
558
559   // set our dialog heading
560   SET_CONTROL_LABEL(CONTROL_HEADING_LABEL, m_strHeading);
561
562   CGUIControl* pControl = NULL;
563   int controlId = CONTROL_START_SETTING;
564   const TiXmlElement *setting = GetFirstSetting();
565   while (setting)
566   {
567     const char *type = setting->Attribute("type");
568     const char *id = setting->Attribute("id");
569     CStdString values;
570     if (setting->Attribute("values"))
571       values = setting->Attribute("values");
572     CStdString lvalues;
573     if (setting->Attribute("lvalues"))
574       lvalues = setting->Attribute("lvalues");
575     CStdString entries;
576     if (setting->Attribute("entries"))
577       entries = setting->Attribute("entries");
578     const char *subsetting = setting->Attribute("subsetting");
579     CStdString label = GetString(setting->Attribute("label"), subsetting && 0 == strcmpi(subsetting, "true"));
580
581     bool bSort=false;
582     const char *sort = setting->Attribute("sort");
583     if (sort && (strcmp(sort, "yes") == 0))
584       bSort=true;
585
586     if (type)
587     {
588       if (strcmpi(type, "text") == 0 || strcmpi(type, "ipaddress") == 0 ||
589         strcmpi(type, "number") == 0 ||strcmpi(type, "video") == 0 ||
590         strcmpi(type, "audio") == 0 || strcmpi(type, "image") == 0 ||
591         strcmpi(type, "folder") == 0 || strcmpi(type, "executable") == 0 ||
592         strcmpi(type, "file") == 0 || strcmpi(type, "action") == 0 ||
593         strcmpi(type, "date") == 0 || strcmpi(type, "time") == 0 ||
594         strcmpi(type, "select") == 0)
595       {
596         pControl = new CGUIButtonControl(*pOriginalButton);
597         if (!pControl) return;
598         ((CGUIButtonControl *)pControl)->SettingsCategorySetTextAlign(XBFONT_CENTER_Y);
599         ((CGUIButtonControl *)pControl)->SetLabel(label);
600         if (id)
601         {
602           CStdString value = m_settings[id];
603           m_buttonValues[id] = value;
604           // get any option to test for hidden
605           const char *option = setting->Attribute("option");
606           if (option && (strstr(option, "urlencoded")))
607             CUtil::URLDecode(value);
608           if (option && (strstr(option, "hidden")))
609           {
610             CStdString hiddenText;
611             hiddenText.append(value.size(), L'*');
612             ((CGUIButtonControl *)pControl)->SetLabel2(hiddenText);
613           }
614           else
615             ((CGUIButtonControl *)pControl)->SetLabel2(value);
616         }
617         else
618           ((CGUIButtonControl *)pControl)->SetLabel2(setting->Attribute("default"));
619       }
620       else if (strcmpi(type, "bool") == 0)
621       {
622         pControl = new CGUIRadioButtonControl(*pOriginalRadioButton);
623         if (!pControl) return;
624         ((CGUIRadioButtonControl *)pControl)->SetLabel(label);
625         ((CGUIRadioButtonControl *)pControl)->SetSelected(m_settings[id] == "true");
626       }
627       else if (strcmpi(type, "enum") == 0 || strcmpi(type, "labelenum") == 0)
628       {
629         vector<CStdString> valuesVec;
630         vector<CStdString> entryVec;
631
632         pControl = new CGUISpinControlEx(*pOriginalSpin);
633         if (!pControl) return;
634         ((CGUISpinControlEx *)pControl)->SetText(label);
635
636         if (!lvalues.IsEmpty())
637           CUtil::Tokenize(lvalues, valuesVec, "|");
638         else if (values.Equals("$HOURS"))
639         {
640           for (unsigned int i = 0; i < 24; i++)
641           {
642             CDateTime time(2000, 1, 1, i, 0, 0);
643             valuesVec.push_back(g_infoManager.LocalizeTime(time, TIME_FORMAT_HH_MM_XX));
644           }
645         }
646         else
647           CUtil::Tokenize(values, valuesVec, "|");
648         if (!entries.IsEmpty())
649           CUtil::Tokenize(entries, entryVec, "|");
650
651         if(bSort && strcmpi(type, "labelenum") == 0)
652           std::sort(valuesVec.begin(), valuesVec.end(), sortstringbyname());
653
654         for (unsigned int i = 0; i < valuesVec.size(); i++)
655         {
656           int iAdd = i;
657           if (entryVec.size() > i)
658             iAdd = atoi(entryVec[i]);
659           if (!lvalues.IsEmpty())
660           {
661             CStdString replace = m_addon->GetString(atoi(valuesVec[i]));
662             if (replace.IsEmpty())
663               replace = g_localizeStrings.Get(atoi(valuesVec[i]));
664             ((CGUISpinControlEx *)pControl)->AddLabel(replace, iAdd);
665           }
666           else
667             ((CGUISpinControlEx *)pControl)->AddLabel(valuesVec[i], iAdd);
668         }
669         if (strcmpi(type, "labelenum") == 0)
670         { // need to run through all our settings and find the one that matches
671           ((CGUISpinControlEx*) pControl)->SetValueFromLabel(m_settings[id]);
672         }
673         else
674           ((CGUISpinControlEx*) pControl)->SetValue(atoi(m_settings[id]));
675
676       }
677       else if (strcmpi(type, "fileenum") == 0)
678       {
679         pControl = new CGUISpinControlEx(*pOriginalSpin);
680         if (!pControl) return;
681         ((CGUISpinControlEx *)pControl)->SetText(label);
682         ((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f);
683
684         vector<CStdString> items = GetFileEnumValues(values, setting->Attribute("mask"), setting->Attribute("option"));
685         for (unsigned int i = 0; i < items.size(); ++i)
686         {
687           ((CGUISpinControlEx *)pControl)->AddLabel(items[i], i);
688           if (items[i].Equals(m_settings[id]))
689             ((CGUISpinControlEx *)pControl)->SetValue(i);
690         }
691       }
692       else if (strcmpi(type, "slider") == 0)
693       {
694         pControl = new CGUISettingsSliderControl(*pOriginalSlider);
695         if (!pControl) return;
696         ((CGUISettingsSliderControl *)pControl)->SetText(label);
697
698         float fMin = 0.0f;
699         float fMax = 100.0f;
700         float fInc = 1.0f;
701         vector<CStdString> range;
702         StringUtils::SplitString(setting->Attribute("range"), ",", range);
703         if (range.size() > 1)
704         {
705           fMin = (float)atof(range[0]);
706           if (range.size() > 2)
707           {
708             fMax = (float)atof(range[2]);
709             fInc = (float)atof(range[1]);
710           }
711           else
712             fMax = (float)atof(range[1]);
713         }
714
715         ((CGUISettingsSliderControl *)pControl)->SetType(SPIN_CONTROL_TYPE_FLOAT);
716         ((CGUISettingsSliderControl *)pControl)->SetFloatRange(fMin, fMax);
717         ((CGUISettingsSliderControl *)pControl)->SetFloatInterval(fInc);
718         ((CGUISettingsSliderControl *)pControl)->SetFloatValue((float)atof(m_settings[id]));
719       }
720       else if (strcmpi(type, "lsep") == 0)
721       {
722         pControl = new CGUILabelControl(*pOriginalLabel);
723         if (pControl)
724           ((CGUILabelControl *)pControl)->SetLabel(label);
725       }
726       else if (strcmpi(type, "sep") == 0)
727         pControl = new CGUIImage(*pOriginalImage);
728     }
729
730     if (pControl)
731     {
732       pControl->SetWidth(group->GetWidth());
733       pControl->SetVisible(true);
734       pControl->SetID(controlId);
735       pControl->AllocResources();
736       group->AddControl(pControl);
737       pControl = NULL;
738     }
739
740     setting = setting->NextSiblingElement("setting");
741     controlId++;
742   }
743   EnableControls();
744 }
745
746 vector<CStdString> CGUIDialogAddonSettings::GetFileEnumValues(const CStdString &path, const CStdString &mask, const CStdString &options) const
747 {
748   // Create our base path, used for type "fileenum" settings
749   // replace $PROFILE with the profile path of the plugin/script
750   CStdString fullPath = path;
751   if (fullPath.Find("$PROFILE") >= 0)
752     fullPath.Replace("$PROFILE", m_addon->Profile());
753   else
754     fullPath = CUtil::AddFileToFolder(m_addon->Path(), path);
755
756   bool hideExtensions = (options.CompareNoCase("hideext") == 0);
757   // fetch directory
758   CFileItemList items;
759   if (!mask.IsEmpty())
760     CDirectory::GetDirectory(fullPath, items, mask);
761   else
762     CDirectory::GetDirectory(fullPath, items);
763
764   vector<CStdString> values;
765   for (int i = 0; i < items.Size(); ++i)
766   {
767     CFileItemPtr pItem = items[i];
768     if ((mask.Equals("/") && pItem->m_bIsFolder) || !pItem->m_bIsFolder)
769     {
770       if (hideExtensions)
771         pItem->RemoveExtension();
772       values.push_back(pItem->GetLabel());
773     }
774   }
775   return values;
776 }
777
778 // Go over all the settings and set their enabled condition according to the values of the enabled attribute
779 void CGUIDialogAddonSettings::EnableControls()
780 {
781   int controlId = CONTROL_START_SETTING;
782   const TiXmlElement *setting = GetFirstSetting();
783   while (setting)
784   {
785     const CGUIControl* control = GetControl(controlId);
786     if (control)
787     {
788       // set enable status
789       if (setting->Attribute("enable"))
790         ((CGUIControl*) control)->SetEnabled(GetCondition(setting->Attribute("enable"), controlId));
791       else
792         ((CGUIControl*) control)->SetEnabled(true);
793       // set visible status
794       if (setting->Attribute("visible"))
795         ((CGUIControl*) control)->SetVisible(GetCondition(setting->Attribute("visible"), controlId));
796       else
797         ((CGUIControl*) control)->SetVisible(true);
798     }
799     setting = setting->NextSiblingElement("setting");
800     controlId++;
801   }
802 }
803
804 bool CGUIDialogAddonSettings::GetCondition(const CStdString &condition, const int controlId)
805 {
806   if (condition.IsEmpty()) return true;
807
808   bool bCondition = true;
809   bool bCompare = true;
810   vector<CStdString> conditionVec;
811   if (condition.Find("+") >= 0)
812     CUtil::Tokenize(condition, conditionVec, "+");
813   else
814   {
815     bCondition = false;
816     bCompare = false;
817     CUtil::Tokenize(condition, conditionVec, "|");
818   }
819
820   for (unsigned int i = 0; i < conditionVec.size(); i++)
821   {
822     vector<CStdString> condVec;
823     if (!TranslateSingleString(conditionVec[i], condVec)) continue;
824
825     const CGUIControl* control2 = GetControl(controlId + atoi(condVec[1]));
826
827     CStdString value;
828     switch (control2->GetControlType())
829     {
830       case CGUIControl::GUICONTROL_BUTTON:
831         value = ((CGUIButtonControl*) control2)->GetLabel2();
832         break;
833       case CGUIControl::GUICONTROL_RADIO:
834         value = ((CGUIRadioButtonControl*) control2)->IsSelected() ? "true" : "false";
835         break;
836       case CGUIControl::GUICONTROL_SPINEX:
837         if (((CGUISpinControlEx*) control2)->GetFloatValue() > 0.0f)
838           value = ((CGUISpinControlEx*) control2)->GetLabel();
839         else
840           value.Format("%i", ((CGUISpinControlEx*) control2)->GetValue());
841         break;
842       default:
843         break;
844     }
845
846     if (condVec[0].Equals("eq"))
847     {
848       if (bCompare)
849         bCondition &= value.Equals(condVec[2]);
850       else
851         bCondition |= value.Equals(condVec[2]);
852     }
853     else if (condVec[0].Equals("!eq"))
854     {
855       if (bCompare)
856         bCondition &= !value.Equals(condVec[2]);
857       else
858         bCondition |= !value.Equals(condVec[2]);
859     }
860     else if (condVec[0].Equals("gt"))
861     {
862       if (bCompare)
863         bCondition &= (atoi(value) > atoi(condVec[2]));
864       else
865         bCondition |= (atoi(value) > atoi(condVec[2]));
866     }
867     else if (condVec[0].Equals("lt"))
868     {
869       if (bCompare)
870         bCondition &= (atoi(value) < atoi(condVec[2]));
871       else
872         bCondition |= (atoi(value) < atoi(condVec[2]));
873     }
874   }
875   return bCondition;
876 }
877
878 bool CGUIDialogAddonSettings::TranslateSingleString(const CStdString &strCondition, vector<CStdString> &condVec)
879 {
880   CStdString strTest = strCondition;
881   strTest.ToLower();
882   strTest.TrimLeft(" ");
883   strTest.TrimRight(" ");
884
885   int pos1 = strTest.Find("(");
886   int pos2 = strTest.Find(",");
887   int pos3 = strTest.Find(")");
888   if (pos1 >= 0 && pos2 > pos1 && pos3 > pos2)
889   {
890     condVec.push_back(strTest.Left(pos1));
891     condVec.push_back(strTest.Mid(pos1 + 1, pos2 - pos1 - 1));
892     condVec.push_back(strTest.Mid(pos2 + 1, pos3 - pos2 - 1));
893     return true;
894   }
895   return false;
896 }
897
898 CStdString CGUIDialogAddonSettings::GetString(const char *value, bool subSetting) const
899 {
900   if (!value)
901     return "";
902   int id = atoi(value);
903   CStdString prefix(subSetting ? "- " : "");
904   if (id > 0)
905     return prefix + m_addon->GetString(id);
906   return prefix + value;
907 }
908
909 // Go over all the settings and set their default values
910 void CGUIDialogAddonSettings::SetDefaults()
911 {
912   if(!m_addon)
913     return;
914
915   const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category");
916   if (!category) // add a default one...
917     category = m_addon->GetSettingsXML();
918
919   while (category)
920   {
921     const TiXmlElement *setting = category->FirstChildElement("setting");
922     while (setting)
923     {
924       const char *id = setting->Attribute("id");
925       const char *type = setting->Attribute("type");
926       const char *value = setting->Attribute("default");
927       if (id)
928       {
929         if (value)
930           m_settings[id] = value;
931         else if (0 == strcmpi(type, "bool"))
932           m_settings[id] = "false";
933         else if (0 == strcmpi(type, "slider") || 0 == strcmpi(type, "enum"))
934           m_settings[id] = "0";
935         else
936           m_settings[id] = "";
937       }
938       setting = setting->NextSiblingElement("setting");
939     }
940     category = category->NextSiblingElement("category");
941   }
942   CreateControls();
943 }
944
945 const TiXmlElement *CGUIDialogAddonSettings::GetFirstSetting() const
946 {
947   const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category");
948   if (!category)
949     category = m_addon->GetSettingsXML();
950   for (unsigned int i = 0; i < m_currentSection && category; i++)
951     category = category->NextSiblingElement("category");
952   if (category)
953     return category->FirstChildElement("setting");
954   return NULL;
955 }
956
957 void CGUIDialogAddonSettings::Render()
958 {
959   // update status of current section button
960   bool alphaFaded = false;
961   CGUIControl *control = GetFirstFocusableControl(CONTROL_START_SECTION + m_currentSection);
962   if (control && !control->HasFocus())
963   {
964     if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
965     {
966       control->SetFocus(true);
967       ((CGUIButtonControl *)control)->SetAlpha(0x80);
968       alphaFaded = true;
969     }
970     else if (control->GetControlType() == CGUIControl::GUICONTROL_TOGGLEBUTTON)
971     {
972       control->SetFocus(true);
973       ((CGUIButtonControl *)control)->SetSelected(true);
974       alphaFaded = true;
975     }
976   }
977   CGUIDialogBoxBase::Render();
978   if (alphaFaded && m_bRunning) // dialog may close during Render()
979   {
980     control->SetFocus(false);
981     if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
982       ((CGUIButtonControl *)control)->SetAlpha(0xFF);
983     else
984       ((CGUIButtonControl *)control)->SetSelected(false);
985   }
986 }