Remove LiveTV menu.
[vuplus_xbmc] / xbmc / addons / GUIDialogAddonSettings.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "GUIDialogAddonSettings.h"
22 #include "filesystem/PluginDirectory.h"
23 #include "addons/IAddon.h"
24 #include "addons/AddonManager.h"
25 #include "dialogs/GUIDialogNumeric.h"
26 #include "dialogs/GUIDialogFileBrowser.h"
27 #include "dialogs/GUIDialogOK.h"
28 #include "guilib/GUIControlGroupList.h"
29 #include "guilib/GUISettingsSliderControl.h"
30 #include "utils/URIUtils.h"
31 #include "utils/StringUtils.h"
32 #include "storage/MediaManager.h"
33 #include "guilib/GUILabelControl.h"
34 #include "guilib/GUIRadioButtonControl.h"
35 #include "guilib/GUISpinControlEx.h"
36 #include "guilib/GUIImage.h"
37 #include "guilib/Key.h"
38 #include "filesystem/Directory.h"
39 #include "video/VideoInfoScanner.h"
40 #include "addons/Scraper.h"
41 #include "guilib/GUIWindowManager.h"
42 #include "ApplicationMessenger.h"
43 #include "guilib/GUIKeyboardFactory.h"
44 #include "FileItem.h"
45 #include "settings/AdvancedSettings.h"
46 #include "settings/MediaSourceSettings.h"
47 #include "GUIInfoManager.h"
48 #include "GUIUserMessages.h"
49 #include "dialogs/GUIDialogSelect.h"
50 #include "GUIWindowAddonBrowser.h"
51 #include "utils/log.h"
52 #include "Util.h"
53 #include "URL.h"
54
55 using namespace std;
56 using namespace ADDON;
57 using XFILE::CDirectory;
58
59 #define CONTROL_SETTINGS_AREA           2
60 #define CONTROL_DEFAULT_BUTTON          3
61 #define CONTROL_DEFAULT_RADIOBUTTON     4
62 #define CONTROL_DEFAULT_SPIN            5
63 #define CONTROL_DEFAULT_SEPARATOR       6
64 #define CONTROL_DEFAULT_LABEL_SEPARATOR 7
65 #define CONTROL_DEFAULT_SLIDER          8
66 #define CONTROL_SECTION_AREA            9
67 #define CONTROL_DEFAULT_SECTION_BUTTON  13
68
69 #define ID_BUTTON_OK                    10
70 #define ID_BUTTON_CANCEL                11
71 #define ID_BUTTON_DEFAULT               12
72 #define CONTROL_HEADING_LABEL           20
73
74 #define CONTROL_START_SETTING           100
75 #define CONTROL_START_SECTION           200
76
77 CGUIDialogAddonSettings::CGUIDialogAddonSettings()
78    : CGUIDialogBoxBase(WINDOW_DIALOG_ADDON_SETTINGS, "DialogAddonSettings.xml")
79 {
80   m_currentSection = 0;
81   m_totalSections = 1;
82 }
83
84 CGUIDialogAddonSettings::~CGUIDialogAddonSettings(void)
85 {
86 }
87
88 bool CGUIDialogAddonSettings::OnMessage(CGUIMessage& message)
89 {
90   switch (message.GetMessage())
91   {
92     case GUI_MSG_WINDOW_DEINIT:
93     {
94       FreeSections();
95     }
96     break;
97     case GUI_MSG_CLICKED:
98     {
99       int iControl = message.GetSenderId();
100       bool bCloseDialog = false;
101
102       if (iControl == ID_BUTTON_DEFAULT)
103         SetDefaultSettings();
104       else if (iControl != ID_BUTTON_OK)
105         bCloseDialog = ShowVirtualKeyboard(iControl);
106
107       if (iControl == ID_BUTTON_OK || iControl == ID_BUTTON_CANCEL || bCloseDialog)
108       {
109         if (iControl == ID_BUTTON_OK || bCloseDialog)
110         {
111           m_bConfirmed = true;
112           SaveSettings();
113         }
114         Close();
115         return true;
116       }
117     }
118     break;
119     case GUI_MSG_FOCUSED:
120     {
121       CGUIDialogBoxBase::OnMessage(message);
122       int focusedControl = GetFocusedControlID();
123       if (focusedControl >= CONTROL_START_SECTION && focusedControl < (int)(CONTROL_START_SECTION + m_totalSections) &&
124           focusedControl - CONTROL_START_SECTION != (int)m_currentSection)
125       { // changing section
126         UpdateFromControls();
127         m_currentSection = focusedControl - CONTROL_START_SECTION;
128         CreateControls();
129       }
130       return true;
131     }
132     case GUI_MSG_SETTING_UPDATED:
133     {
134       CStdString      id = message.GetStringParam(0);
135       CStdString value   = message.GetStringParam(1);
136       m_settings[id] = value;
137       if (GetFocusedControl())
138       {
139         int iControl = GetFocusedControl()->GetID();
140         CreateControls();
141         CGUIMessage msg(GUI_MSG_SETFOCUS,GetID(),iControl);
142         OnMessage(msg);
143       }
144       return true;
145     }
146   }
147   return CGUIDialogBoxBase::OnMessage(message);
148 }
149
150 bool CGUIDialogAddonSettings::OnAction(const CAction& action)
151 {
152   if (action.GetID() == ACTION_DELETE_ITEM)
153   {
154     CGUIControl* pControl = GetFocusedControl();
155     if (pControl)
156     {
157       int iControl = pControl->GetID();
158       int controlId = CONTROL_START_SETTING;
159       const TiXmlElement* setting = GetFirstSetting();
160       UpdateFromControls();
161       while (setting)
162       {
163         if (controlId == iControl)
164         {
165           const char* id = setting->Attribute("id");
166           const char* value = setting->Attribute("default");
167           m_settings[id] = value;
168           CreateControls();
169           CGUIMessage msg(GUI_MSG_SETFOCUS,GetID(),iControl);
170           OnMessage(msg);
171           return true;
172         }
173         setting = setting->NextSiblingElement("setting");
174         controlId++;
175       }
176     }
177   }
178   return CGUIDialogBoxBase::OnAction(action);
179 }
180
181 void CGUIDialogAddonSettings::OnInitWindow()
182 {
183   m_currentSection = 0;
184   m_totalSections = 1;
185   CreateSections();
186   CreateControls();
187   CGUIDialogBoxBase::OnInitWindow();
188 }
189
190 // \brief Show CGUIDialogOK dialog, then wait for user to dismiss it.
191 bool CGUIDialogAddonSettings::ShowAndGetInput(const AddonPtr &addon, bool saveToDisk /* = true */)
192 {
193   if (!addon)
194     return false;
195
196   if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
197     return false;
198
199   bool ret(false);
200   if (addon->HasSettings())
201   { 
202     // Create the dialog
203     CGUIDialogAddonSettings* pDialog = NULL;
204     pDialog = (CGUIDialogAddonSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_ADDON_SETTINGS);
205     if (!pDialog)
206       return false;
207
208     // Set the heading
209     CStdString heading = StringUtils::Format("$LOCALIZE[10004] - %s", addon->Name().c_str()); // "Settings - AddonName"
210     pDialog->m_strHeading = heading;
211
212     pDialog->m_changed = false;
213     pDialog->m_addon = addon;
214     pDialog->m_saveToDisk = saveToDisk;
215     pDialog->DoModal();
216     ret = true;
217   }
218   else
219   { // addon does not support settings, inform user
220     CGUIDialogOK::ShowAndGetInput(24000,0,24030,0);
221   }
222
223   return ret;
224 }
225
226 bool CGUIDialogAddonSettings::ShowVirtualKeyboard(int iControl)
227 {
228   int controlId = CONTROL_START_SETTING;
229   bool bCloseDialog = false;
230
231   const TiXmlElement *setting = GetFirstSetting();
232   while (setting)
233   {
234     if (controlId == iControl)
235     {
236       const CGUIControl* control = GetControl(controlId);
237       if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
238       {
239         const char *id = setting->Attribute("id");
240         const char *type = setting->Attribute("type");
241         const char *option = setting->Attribute("option");
242         const char *source = setting->Attribute("source");
243         CStdString value = m_buttonValues[id];
244         CStdString label = GetString(setting->Attribute("label"));
245
246         if (strcmp(type, "text") == 0)
247         {
248           // get any options
249           bool bHidden  = false;
250           bool bEncoded = false;
251           if (option)
252           {
253             bHidden = (strstr(option, "hidden") != NULL);
254             bEncoded = (strstr(option, "urlencoded") != NULL);
255           }
256           if (bEncoded)
257             value = CURL::Decode(value);
258
259           if (CGUIKeyboardFactory::ShowAndGetInput(value, label, true, bHidden))
260           {
261             // if hidden hide input
262             if (bHidden)
263             {
264               CStdString hiddenText;
265               hiddenText.append(value.size(), L'*');
266               ((CGUIButtonControl *)control)->SetLabel2(hiddenText);
267             }
268             else
269               ((CGUIButtonControl*) control)->SetLabel2(value);
270             if (bEncoded)
271               value = CURL::Encode(value);
272           }
273         }
274         else if (strcmp(type, "number") == 0 && CGUIDialogNumeric::ShowAndGetNumber(value, label))
275         {
276           ((CGUIButtonControl*) control)->SetLabel2(value);
277         }
278         else if (strcmp(type, "ipaddress") == 0 && CGUIDialogNumeric::ShowAndGetIPAddress(value, label))
279         {
280           ((CGUIButtonControl*) control)->SetLabel2(value);
281         }
282         else if (strcmpi(type, "select") == 0)
283         {
284           CGUIDialogSelect *pDlg = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
285           if (pDlg)
286           {
287             pDlg->SetHeading(label.c_str());
288             pDlg->Reset();
289
290             int selected = -1;
291             vector<std::string> valuesVec;
292             if (setting->Attribute("values"))
293               StringUtils::Tokenize(setting->Attribute("values"), valuesVec, "|");
294             else if (setting->Attribute("lvalues"))
295             { // localize
296               StringUtils::Tokenize(setting->Attribute("lvalues"), valuesVec, "|");
297               for (unsigned int i = 0; i < valuesVec.size(); i++)
298               {
299                 if (i == (unsigned int)atoi(value))
300                   selected = i;
301                 CStdString localized = m_addon->GetString(atoi(valuesVec[i].c_str()));
302                 if (localized.empty())
303                   localized = g_localizeStrings.Get(atoi(valuesVec[i].c_str()));
304                 valuesVec[i] = localized;
305               }
306             }
307             else if (source)
308             {
309               valuesVec = GetFileEnumValues(source, setting->Attribute("mask"), setting->Attribute("option"));
310             }
311
312             for (unsigned int i = 0; i < valuesVec.size(); i++)
313             {
314               pDlg->Add(valuesVec[i]);
315               if (selected == (int)i || (selected < 0 && StringUtils::EqualsNoCase(valuesVec[i], value)))
316                 pDlg->SetSelected(i); // FIXME: the SetSelected() does not select "i", it always defaults to the first position
317             }
318             pDlg->DoModal();
319             int iSelected = pDlg->GetSelectedLabel();
320             if (iSelected >= 0)
321             {
322               if (setting->Attribute("lvalues"))
323                 value = StringUtils::Format("%i", iSelected);
324               else
325                 value = valuesVec[iSelected];
326               ((CGUIButtonControl*) control)->SetLabel2(valuesVec[iSelected]);
327             }
328           }
329         }
330         else if (strcmpi(type, "audio") == 0 || strcmpi(type, "video") == 0 ||
331           strcmpi(type, "image") == 0 || strcmpi(type, "executable") == 0 ||
332           strcmpi(type, "file") == 0 || strcmpi(type, "folder") == 0)
333         {
334           // setup the shares
335           VECSOURCES *shares = NULL;
336           if (source && strcmpi(source, "") != 0)
337             shares = CMediaSourceSettings::Get().GetSources(source);
338
339           VECSOURCES localShares;
340           if (!shares)
341           {
342             VECSOURCES networkShares;
343             g_mediaManager.GetLocalDrives(localShares);
344             if (!source || strcmpi(source, "local") != 0)
345               g_mediaManager.GetNetworkLocations(localShares);
346           }
347           else // always append local drives
348           {
349             localShares = *shares;
350             g_mediaManager.GetLocalDrives(localShares);
351           }
352
353           if (strcmpi(type, "folder") == 0)
354           {
355             // get any options
356             bool bWriteOnly = false;
357             if (option)
358               bWriteOnly = (strcmpi(option, "writeable") == 0);
359
360             if (CGUIDialogFileBrowser::ShowAndGetDirectory(localShares, label, value, bWriteOnly))
361               ((CGUIButtonControl*) control)->SetLabel2(value);
362           }
363           else if (strcmpi(type, "image") == 0)
364           {
365             if (CGUIDialogFileBrowser::ShowAndGetImage(localShares, label, value))
366               ((CGUIButtonControl*) control)->SetLabel2(value);
367           }
368           else
369           {
370             // set the proper mask
371             CStdString strMask;
372             if (setting->Attribute("mask"))
373             {
374               strMask = setting->Attribute("mask");
375               // convert mask qualifiers
376               StringUtils::Replace(strMask, "$AUDIO", g_advancedSettings.m_musicExtensions);
377               StringUtils::Replace(strMask, "$VIDEO", g_advancedSettings.m_videoExtensions);
378               StringUtils::Replace(strMask, "$IMAGE", g_advancedSettings.m_pictureExtensions);
379 #if defined(_WIN32_WINNT)
380               StringUtils::Replace(strMask, "$EXECUTABLE", ".exe|.bat|.cmd|.py");
381 #else
382               StringUtils::Replace(strMask, "$EXECUTABLE", "");
383 #endif
384             }
385             else
386             {
387               if (strcmpi(type, "video") == 0)
388                 strMask = g_advancedSettings.m_videoExtensions;
389               else if (strcmpi(type, "audio") == 0)
390                 strMask = g_advancedSettings.m_musicExtensions;
391               else if (strcmpi(type, "executable") == 0)
392 #if defined(_WIN32_WINNT)
393                 strMask = ".exe|.bat|.cmd|.py";
394 #else
395                 strMask = "";
396 #endif
397             }
398
399             // get any options
400             bool bUseThumbs = false;
401             bool bUseFileDirectories = false;
402             if (option)
403             {
404               vector<CStdString> options;
405               StringUtils::SplitString(option, "|", options);
406               bUseThumbs = find(options.begin(), options.end(), "usethumbs") != options.end();
407               bUseFileDirectories = find(options.begin(), options.end(), "treatasfolder") != options.end();
408             }
409
410             if (CGUIDialogFileBrowser::ShowAndGetFile(localShares, strMask, label, value, bUseThumbs, bUseFileDirectories))
411               ((CGUIButtonControl*) control)->SetLabel2(value);
412           }
413         }
414         else if (strcmpi(type, "action") == 0)
415         {
416           CStdString action = setting->Attribute("action");
417           if (!action.empty())
418           {
419             // replace $CWD with the url of plugin/script
420             StringUtils::Replace(action, "$CWD", m_addon->Path());
421             StringUtils::Replace(action, "$ID", m_addon->ID());
422             if (option)
423               bCloseDialog = (strcmpi(option, "close") == 0);
424             CApplicationMessenger::Get().ExecBuiltIn(action);
425           }
426         }
427         else if (strcmp(type, "date") == 0)
428         {
429           CDateTime date;
430           if (!value.empty())
431             date.SetFromDBDate(value);
432           SYSTEMTIME timedate;
433           date.GetAsSystemTime(timedate);
434           if(CGUIDialogNumeric::ShowAndGetDate(timedate, label))
435           {
436             date = timedate;
437             value = date.GetAsDBDate();
438             ((CGUIButtonControl*) control)->SetLabel2(value);
439           }
440         }
441         else if (strcmp(type, "time") == 0)
442         {
443           SYSTEMTIME timedate;
444           if (value.size() >= 5)
445           {
446             // assumes HH:MM
447             timedate.wHour = atoi(value.substr(0, 2).c_str());
448             timedate.wMinute = atoi(value.substr(3, 2).c_str());
449           }
450           if (CGUIDialogNumeric::ShowAndGetTime(timedate, label))
451           {
452             value = StringUtils::Format("%02d:%02d", timedate.wHour, timedate.wMinute);
453             ((CGUIButtonControl*) control)->SetLabel2(value);
454           }
455         }
456         else if (strcmp(type, "addon") == 0)
457         {
458           const char *strType = setting->Attribute("addontype");
459           if (strType)
460           {
461             CStdStringArray addonTypes;
462             StringUtils::SplitString(strType, ",", addonTypes);
463             vector<ADDON::TYPE> types;
464             for (unsigned int i = 0 ; i < addonTypes.size() ; i++)
465             {
466               StringUtils::Trim(addonTypes[i]);
467               ADDON::TYPE type = TranslateType(addonTypes[i]);
468               if (type != ADDON_UNKNOWN)
469                 types.push_back(type);
470             }
471             if (types.size() > 0)
472             {
473               const char *strMultiselect = setting->Attribute("multiselect");
474               bool multiSelect = strMultiselect && strcmpi(strMultiselect, "true") == 0;
475               if (multiSelect)
476               {
477                 // construct vector of addon IDs (IDs are comma seperated in single string)
478                 CStdStringArray addonIDs;
479                 StringUtils::SplitString(value, ",", addonIDs);
480                 if (CGUIWindowAddonBrowser::SelectAddonID(types, addonIDs, false) == 1)
481                 {
482                   StringUtils::JoinString(addonIDs, ",", value);
483                   ((CGUIButtonControl*) control)->SetLabel2(GetAddonNames(value));
484                 }
485               }
486               else // no need of string splitting/joining if we select only 1 addon
487                 if (CGUIWindowAddonBrowser::SelectAddonID(types, value, false) == 1)
488                   ((CGUIButtonControl*) control)->SetLabel2(GetAddonNames(value));
489             }
490           }
491         }
492         m_buttonValues[id] = value;
493         break;
494       }
495     }
496     setting = setting->NextSiblingElement("setting");
497     controlId++;
498   }
499   EnableControls();
500   return bCloseDialog;
501 }
502
503 void CGUIDialogAddonSettings::UpdateFromControls()
504 {
505   int controlID = CONTROL_START_SETTING;
506   const TiXmlElement *setting = GetFirstSetting();
507   while (setting)
508   {
509     CStdString id = setting->Attribute("id");
510     const char *type = setting->Attribute("type");
511     const CGUIControl* control = GetControl(controlID++);
512
513     if (control)
514     {
515       CStdString value;
516       switch (control->GetControlType())
517       {
518         case CGUIControl::GUICONTROL_BUTTON:
519           value = m_buttonValues[id];
520           break;
521         case CGUIControl::GUICONTROL_RADIO:
522           value = ((CGUIRadioButtonControl*) control)->IsSelected() ? "true" : "false";
523           break;
524         case CGUIControl::GUICONTROL_SPINEX:
525           if (strcmpi(type, "fileenum") == 0 || strcmpi(type, "labelenum") == 0)
526             value = ((CGUISpinControlEx*) control)->GetLabel();
527           else
528             value = StringUtils::Format("%i", ((CGUISpinControlEx*) control)->GetValue());
529           break;
530         case CGUIControl::GUICONTROL_SETTINGS_SLIDER:
531           {
532             CStdString option = setting->Attribute("option");
533             if (option.size() == 0 || StringUtils::EqualsNoCase(option, "float"))
534               value = StringUtils::Format("%f", ((CGUISettingsSliderControl *)control)->GetFloatValue());
535             else
536               value = StringUtils::Format("%i", ((CGUISettingsSliderControl *)control)->GetIntValue());
537           }
538           break;
539         default:
540           break;
541       }
542       m_settings[id] = value;
543     }
544
545     setting = setting->NextSiblingElement("setting");
546   }
547 }
548
549 void CGUIDialogAddonSettings::SaveSettings(void)
550 {
551   UpdateFromControls();
552
553   for (map<CStdString, CStdString>::iterator i = m_settings.begin(); i != m_settings.end(); ++i)
554     m_addon->UpdateSetting(i->first, i->second);
555
556   if (m_saveToDisk)
557   { 
558     m_addon->SaveSettings();
559   } 
560 }
561
562 void CGUIDialogAddonSettings::FreeSections()
563 {
564   CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_SECTION_AREA);
565   if (group)
566   {
567     group->FreeResources();
568     group->ClearAll();
569   }
570   m_settings.clear();
571   m_buttonValues.clear();
572   FreeControls();
573 }
574
575 void CGUIDialogAddonSettings::FreeControls()
576 {
577   // clear the category group
578   CGUIControlGroupList *control = (CGUIControlGroupList *)GetControl(CONTROL_SETTINGS_AREA);
579   if (control)
580   {
581     control->FreeResources();
582     control->ClearAll();
583   }
584 }
585
586 void CGUIDialogAddonSettings::CreateSections()
587 {
588   CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_SECTION_AREA);
589   CGUIButtonControl *originalButton = (CGUIButtonControl *)GetControl(CONTROL_DEFAULT_SECTION_BUTTON);
590   if (!m_addon)
591     return;
592
593   if (originalButton)
594     originalButton->SetVisible(false);
595
596   // clear the category group
597   FreeSections();
598
599   // grab our categories
600   const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category");
601   if (!category) // add a default one...
602     category = m_addon->GetSettingsXML();
603  
604   int buttonID = CONTROL_START_SECTION;
605   while (category)
606   { // add a category
607     CGUIButtonControl *button = originalButton ? originalButton->Clone() : NULL;
608
609     CStdString label = GetString(category->Attribute("label"));
610     if (label.empty())
611       label = g_localizeStrings.Get(128);
612
613     // add the category button
614     if (button && group)
615     {
616       button->SetID(buttonID++);
617       button->SetLabel(label);
618       button->SetVisible(true);
619       group->AddControl(button);
620     }
621
622     // grab a local copy of all the settings in this category
623     const TiXmlElement *setting = category->FirstChildElement("setting");
624     while (setting)
625     {
626       const char *id = setting->Attribute("id");
627       if (id)
628         m_settings[id] = m_addon->GetSetting(id);
629       setting = setting->NextSiblingElement("setting");
630     }
631     category = category->NextSiblingElement("category");
632   }
633   m_totalSections = buttonID - CONTROL_START_SECTION;
634 }
635
636 void CGUIDialogAddonSettings::CreateControls()
637 {
638   FreeControls();
639
640   CGUISpinControlEx *pOriginalSpin = (CGUISpinControlEx*)GetControl(CONTROL_DEFAULT_SPIN);
641   CGUIRadioButtonControl *pOriginalRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_DEFAULT_RADIOBUTTON);
642   CGUIButtonControl *pOriginalButton = (CGUIButtonControl *)GetControl(CONTROL_DEFAULT_BUTTON);
643   CGUIImage *pOriginalImage = (CGUIImage *)GetControl(CONTROL_DEFAULT_SEPARATOR);
644   CGUILabelControl *pOriginalLabel = (CGUILabelControl *)GetControl(CONTROL_DEFAULT_LABEL_SEPARATOR);
645   CGUISettingsSliderControl *pOriginalSlider = (CGUISettingsSliderControl *)GetControl(CONTROL_DEFAULT_SLIDER);
646
647   if (!m_addon || !pOriginalSpin || !pOriginalRadioButton || !pOriginalButton || !pOriginalImage
648                || !pOriginalLabel || !pOriginalSlider)
649     return;
650
651   pOriginalSpin->SetVisible(false);
652   pOriginalRadioButton->SetVisible(false);
653   pOriginalButton->SetVisible(false);
654   pOriginalImage->SetVisible(false);
655   pOriginalLabel->SetVisible(false);
656   pOriginalSlider->SetVisible(false);
657
658   // clear the category group
659   CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_SETTINGS_AREA);
660   if (!group)
661     return;
662
663   // set our dialog heading
664   SET_CONTROL_LABEL(CONTROL_HEADING_LABEL, m_strHeading);
665
666   CGUIControl* pControl = NULL;
667   int controlId = CONTROL_START_SETTING;
668   const TiXmlElement *setting = GetFirstSetting();
669   while (setting)
670   {
671     const char *type = setting->Attribute("type");
672     const char *id = setting->Attribute("id");
673     CStdString values;
674     if (setting->Attribute("values"))
675       values = setting->Attribute("values");
676     CStdString lvalues;
677     if (setting->Attribute("lvalues"))
678       lvalues = setting->Attribute("lvalues");
679     CStdString entries;
680     if (setting->Attribute("entries"))
681       entries = setting->Attribute("entries");
682     CStdString defaultValue;
683     if (setting->Attribute("default"))
684       defaultValue= setting->Attribute("default");
685     const char *subsetting = setting->Attribute("subsetting");
686     CStdString label = GetString(setting->Attribute("label"), subsetting && 0 == strcmpi(subsetting, "true"));
687
688     bool bSort=false;
689     const char *sort = setting->Attribute("sort");
690     if (sort && (strcmp(sort, "yes") == 0))
691       bSort=true;
692
693     if (type)
694     {
695       bool isAddonSetting = false;
696       if (strcmpi(type, "text") == 0 || strcmpi(type, "ipaddress") == 0 ||
697         strcmpi(type, "number") == 0 ||strcmpi(type, "video") == 0 ||
698         strcmpi(type, "audio") == 0 || strcmpi(type, "image") == 0 ||
699         strcmpi(type, "folder") == 0 || strcmpi(type, "executable") == 0 ||
700         strcmpi(type, "file") == 0 || strcmpi(type, "action") == 0 ||
701         strcmpi(type, "date") == 0 || strcmpi(type, "time") == 0 ||
702         strcmpi(type, "select") == 0 || (isAddonSetting = strcmpi(type, "addon") == 0))
703       {
704         pControl = new CGUIButtonControl(*pOriginalButton);
705         if (!pControl) return;
706         ((CGUIButtonControl *)pControl)->SetLabel(label);
707         if (id)
708         {
709           CStdString value = m_settings[id];
710           m_buttonValues[id] = value;
711           // get any option to test for hidden
712           const char *option = setting->Attribute("option");
713           if (option && (strstr(option, "urlencoded")))
714             value = CURL::Decode(value);
715           if (option && (strstr(option, "hidden")))
716           {
717             CStdString hiddenText;
718             hiddenText.append(value.size(), L'*');
719             ((CGUIButtonControl *)pControl)->SetLabel2(hiddenText);
720           }
721           else
722           {
723             if (isAddonSetting)
724               ((CGUIButtonControl *)pControl)->SetLabel2(GetAddonNames(value));
725             else if (strcmpi(type, "select") == 0 && !lvalues.empty())
726             {
727               vector<string> valuesVec = StringUtils::Split(lvalues, "|");
728               int selected = atoi(value.c_str());
729               if (selected >= 0 && selected < (int)valuesVec.size())
730               {
731                 CStdString label = m_addon->GetString(atoi(valuesVec[selected].c_str()));
732                 if (label.empty())
733                   label = g_localizeStrings.Get(atoi(valuesVec[selected].c_str()));
734                 ((CGUIButtonControl *)pControl)->SetLabel2(label);
735               }
736             }
737             else
738               ((CGUIButtonControl *)pControl)->SetLabel2(value);
739           }
740         }
741         else
742           ((CGUIButtonControl *)pControl)->SetLabel2(defaultValue);
743       }
744       else if (strcmpi(type, "bool") == 0)
745       {
746         pControl = new CGUIRadioButtonControl(*pOriginalRadioButton);
747         if (!pControl) return;
748         ((CGUIRadioButtonControl *)pControl)->SetLabel(label);
749         ((CGUIRadioButtonControl *)pControl)->SetSelected(m_settings[id] == "true");
750       }
751       else if (strcmpi(type, "enum") == 0 || strcmpi(type, "labelenum") == 0)
752       {
753         vector<std::string> valuesVec;
754         vector<std::string> entryVec;
755
756         pControl = new CGUISpinControlEx(*pOriginalSpin);
757         if (!pControl) return;
758         ((CGUISpinControlEx *)pControl)->SetText(label);
759
760        if (!lvalues.empty())
761           StringUtils::Tokenize(lvalues, valuesVec, "|");
762         else if (values.Equals("$HOURS"))
763         {
764           for (unsigned int i = 0; i < 24; i++)
765           {
766             CDateTime time(2000, 1, 1, i, 0, 0);
767             valuesVec.push_back(g_infoManager.LocalizeTime(time, TIME_FORMAT_HH_MM_XX));
768           }
769         }
770         else
771           StringUtils::Tokenize(values, valuesVec, "|");
772         if (!entries.empty())
773           StringUtils::Tokenize(entries, entryVec, "|");
774
775         if(bSort && strcmpi(type, "labelenum") == 0)
776           std::sort(valuesVec.begin(), valuesVec.end(), sortstringbyname());
777
778         for (unsigned int i = 0; i < valuesVec.size(); i++)
779         {
780           int iAdd = i;
781           if (entryVec.size() > i)
782             iAdd = atoi(entryVec[i].c_str());
783           if (!lvalues.empty())
784           {
785             CStdString replace = m_addon->GetString(atoi(valuesVec[i].c_str()));
786             if (replace.empty())
787               replace = g_localizeStrings.Get(atoi(valuesVec[i].c_str()));
788             ((CGUISpinControlEx *)pControl)->AddLabel(replace, iAdd);
789           }
790           else
791             ((CGUISpinControlEx *)pControl)->AddLabel(valuesVec[i], iAdd);
792         }
793         if (strcmpi(type, "labelenum") == 0)
794         { // need to run through all our settings and find the one that matches
795           ((CGUISpinControlEx*) pControl)->SetValueFromLabel(m_settings[id]);
796         }
797         else
798           ((CGUISpinControlEx*) pControl)->SetValue(atoi(m_settings[id]));
799
800       }
801       else if (strcmpi(type, "fileenum") == 0)
802       {
803         pControl = new CGUISpinControlEx(*pOriginalSpin);
804         if (!pControl) return;
805         ((CGUISpinControlEx *)pControl)->SetText(label);
806         ((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f);
807
808         vector<std::string> items = GetFileEnumValues(values, setting->Attribute("mask"), setting->Attribute("option"));
809         for (unsigned int i = 0; i < items.size(); ++i)
810         {
811           ((CGUISpinControlEx *)pControl)->AddLabel(items[i], i);
812           if (StringUtils::EqualsNoCase(items[i], m_settings[id]))
813             ((CGUISpinControlEx *)pControl)->SetValue(i);
814         }
815       }
816       // Sample: <setting id="mysettingname" type="rangeofnum" label="30000" rangestart="0" rangeend="100" elements="11" valueformat="30001" default="0" />
817       // in strings.xml: <string id="30001">%2.0f mp</string>
818       // creates 11 piece, text formated number labels from 0 to 100
819       else if (strcmpi(type, "rangeofnum") == 0)
820       {
821         pControl = new CGUISpinControlEx(*pOriginalSpin);
822         if (!pControl)
823           return;
824         ((CGUISpinControlEx *)pControl)->SetText(label);
825         ((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f);
826
827         double rangestart = 0;
828         if (setting->Attribute("rangestart"))
829           rangestart = atof(setting->Attribute("rangestart"));
830         double rangeend = 1;
831         if (setting->Attribute("rangeend"))
832           rangeend = atof(setting->Attribute("rangeend"));
833         int elements = 2;
834         if (setting->Attribute("elements"))
835           elements = atoi(setting->Attribute("elements"));
836         CStdString valueformat;
837         if (setting->Attribute("valueformat"))
838           valueformat = m_addon->GetString(atoi(setting->Attribute("valueformat")));
839         for (int i = 0; i < elements; i++)
840         {
841           CStdString valuestring;
842           if (elements < 2)
843             valuestring = StringUtils::Format(valueformat.c_str(), rangestart);
844           else
845             valuestring = StringUtils::Format(valueformat.c_str(), rangestart+(rangeend-rangestart)/(elements-1)*i);
846           ((CGUISpinControlEx *)pControl)->AddLabel(valuestring, i);
847         }
848         ((CGUISpinControlEx *)pControl)->SetValue(atoi(m_settings[id]));
849       }
850       // Sample: <setting id="mysettingname" type="slider" label="30000" range="5,5,60" option="int" default="5"/>
851       // to make ints from 5-60 with 5 steps
852       else if (strcmpi(type, "slider") == 0)
853       {
854         pControl = new CGUISettingsSliderControl(*pOriginalSlider);
855         if (!pControl) return;
856         ((CGUISettingsSliderControl *)pControl)->SetText(label);
857
858         float fMin = 0.0f;
859         float fMax = 100.0f;
860         float fInc = 1.0f;
861         vector<CStdString> range;
862         StringUtils::SplitString(setting->Attribute("range"), ",", range);
863         if (range.size() > 1)
864         {
865           fMin = (float)atof(range[0]);
866           if (range.size() > 2)
867           {
868             fMax = (float)atof(range[2]);
869             fInc = (float)atof(range[1]);
870           }
871           else
872             fMax = (float)atof(range[1]);
873         }
874
875         CStdString option = setting->Attribute("option");
876         int iType=0;
877
878         if (option.size() == 0 || StringUtils::EqualsNoCase(option, "float"))
879           iType = SPIN_CONTROL_TYPE_FLOAT;
880         else if (StringUtils::EqualsNoCase(option, "int"))
881           iType = SPIN_CONTROL_TYPE_INT;
882         else if (StringUtils::EqualsNoCase(option, "percent"))
883           iType = 0;
884
885         ((CGUISettingsSliderControl *)pControl)->SetType(iType);
886         ((CGUISettingsSliderControl *)pControl)->SetFloatRange(fMin, fMax);
887         ((CGUISettingsSliderControl *)pControl)->SetFloatInterval(fInc);
888         ((CGUISettingsSliderControl *)pControl)->SetFloatValue((float)atof(m_settings[id]));
889       }
890       else if (strcmpi(type, "lsep") == 0)
891       {
892         pControl = new CGUILabelControl(*pOriginalLabel);
893         if (pControl)
894           ((CGUILabelControl *)pControl)->SetLabel(label);
895       }
896       else if (strcmpi(type, "sep") == 0)
897         pControl = new CGUIImage(*pOriginalImage);
898     }
899
900     if (pControl)
901     {
902       pControl->SetWidth(group->GetWidth());
903       pControl->SetVisible(true);
904       pControl->SetID(controlId);
905       pControl->AllocResources();
906       group->AddControl(pControl);
907       pControl = NULL;
908     }
909
910     setting = setting->NextSiblingElement("setting");
911     controlId++;
912     if (controlId >= CONTROL_START_SECTION)
913     {
914       CLog::Log(LOGERROR, "%s - cannot have more than %d controls per category - simplify your addon!", __FUNCTION__, CONTROL_START_SECTION - CONTROL_START_SETTING);
915       break;
916     }
917   }
918   EnableControls();
919 }
920
921 CStdString CGUIDialogAddonSettings::GetAddonNames(const CStdString& addonIDslist) const
922 {
923   CStdString retVal;
924   CStdStringArray addons;
925   StringUtils::SplitString(addonIDslist, ",", addons);
926   for (CStdStringArray::const_iterator it = addons.begin(); it != addons.end() ; it ++)
927   {
928     if (!retVal.empty())
929       retVal += ", ";
930     AddonPtr addon;
931     if (CAddonMgr::Get().GetAddon(*it ,addon))
932       retVal += addon->Name();
933     else
934       retVal += *it;
935   }
936   return retVal;
937 }
938
939 vector<std::string> CGUIDialogAddonSettings::GetFileEnumValues(const CStdString &path, const CStdString &mask, const CStdString &options) const
940 {
941   // Create our base path, used for type "fileenum" settings
942   // replace $PROFILE with the profile path of the plugin/script
943   CStdString fullPath = path;
944   if (fullPath.find("$PROFILE") != std::string::npos)
945     StringUtils::Replace(fullPath, "$PROFILE", m_addon->Profile());
946   else
947     fullPath = URIUtils::AddFileToFolder(m_addon->Path(), path);
948
949   bool hideExtensions = StringUtils::EqualsNoCase(options, "hideext");
950   // fetch directory
951   CFileItemList items;
952   if (!mask.empty())
953     CDirectory::GetDirectory(fullPath, items, mask, XFILE::DIR_FLAG_NO_FILE_DIRS);
954   else
955     CDirectory::GetDirectory(fullPath, items, "", XFILE::DIR_FLAG_NO_FILE_DIRS);
956
957   vector<std::string> values;
958   for (int i = 0; i < items.Size(); ++i)
959   {
960     CFileItemPtr pItem = items[i];
961     if ((mask.Equals("/") && pItem->m_bIsFolder) || !pItem->m_bIsFolder)
962     {
963       if (hideExtensions)
964         pItem->RemoveExtension();
965       values.push_back(pItem->GetLabel());
966     }
967   }
968   return values;
969 }
970
971 // Go over all the settings and set their enabled condition according to the values of the enabled attribute
972 void CGUIDialogAddonSettings::EnableControls()
973 {
974   int controlId = CONTROL_START_SETTING;
975   const TiXmlElement *setting = GetFirstSetting();
976   while (setting)
977   {
978     const CGUIControl* control = GetControl(controlId);
979     if (control)
980     {
981       // set enable status
982       if (setting->Attribute("enable"))
983         ((CGUIControl*) control)->SetEnabled(GetCondition(setting->Attribute("enable"), controlId));
984       else
985         ((CGUIControl*) control)->SetEnabled(true);
986       // set visible status
987       if (setting->Attribute("visible"))
988         ((CGUIControl*) control)->SetVisible(GetCondition(setting->Attribute("visible"), controlId));
989       else
990         ((CGUIControl*) control)->SetVisible(true);
991     }
992     setting = setting->NextSiblingElement("setting");
993     controlId++;
994   }
995 }
996
997 bool CGUIDialogAddonSettings::GetCondition(const CStdString &condition, const int controlId)
998 {
999   if (condition.empty()) return true;
1000
1001   bool bCondition = true;
1002   bool bCompare = true;
1003   bool bControlDependend = false;//flag if the condition depends on another control
1004   vector<std::string> conditionVec;
1005
1006   if (condition.find("+") != std::string::npos)
1007     StringUtils::Tokenize(condition, conditionVec, "+");
1008   else
1009   {
1010     bCondition = false;
1011     bCompare = false;
1012     StringUtils::Tokenize(condition, conditionVec, "|");
1013   }
1014
1015   for (unsigned int i = 0; i < conditionVec.size(); i++)
1016   {
1017     vector<CStdString> condVec;
1018     if (!TranslateSingleString(conditionVec[i], condVec)) continue;
1019
1020     const CGUIControl* control2 = GetControl(controlId + atoi(condVec[1]));
1021     if (!control2)
1022       continue;
1023       
1024     bControlDependend = true; //once we are here - this condition depends on another control
1025
1026     CStdString value;
1027     switch (control2->GetControlType())
1028     {
1029       case CGUIControl::GUICONTROL_BUTTON:
1030         value = ((CGUIButtonControl*) control2)->GetLabel2();
1031         break;
1032       case CGUIControl::GUICONTROL_RADIO:
1033         value = ((CGUIRadioButtonControl*) control2)->IsSelected() ? "true" : "false";
1034         break;
1035       case CGUIControl::GUICONTROL_SPINEX:
1036         if (((CGUISpinControlEx*) control2)->GetFloatValue() > 0.0f)
1037           value = ((CGUISpinControlEx*) control2)->GetLabel();
1038         else
1039           value = StringUtils::Format("%i", ((CGUISpinControlEx*) control2)->GetValue());
1040         break;
1041       default:
1042         break;
1043     }
1044
1045     if (condVec[0].Equals("eq"))
1046     {
1047       if (bCompare)
1048         bCondition &= value.Equals(condVec[2]);
1049       else
1050         bCondition |= value.Equals(condVec[2]);
1051     }
1052     else if (condVec[0].Equals("!eq"))
1053     {
1054       if (bCompare)
1055         bCondition &= !value.Equals(condVec[2]);
1056       else
1057         bCondition |= !value.Equals(condVec[2]);
1058     }
1059     else if (condVec[0].Equals("gt"))
1060     {
1061       if (bCompare)
1062         bCondition &= (atoi(value) > atoi(condVec[2]));
1063       else
1064         bCondition |= (atoi(value) > atoi(condVec[2]));
1065     }
1066     else if (condVec[0].Equals("lt"))
1067     {
1068       if (bCompare)
1069         bCondition &= (atoi(value) < atoi(condVec[2]));
1070       else
1071         bCondition |= (atoi(value) < atoi(condVec[2]));
1072     }
1073   }
1074   
1075   if (!bControlDependend)//if condition doesn't depend on another control - try if its an infobool expression
1076   {
1077     bCondition = g_infoManager.EvaluateBool(condition);
1078   }
1079   
1080   return bCondition;
1081 }
1082
1083 bool CGUIDialogAddonSettings::TranslateSingleString(const CStdString &strCondition, vector<CStdString> &condVec)
1084 {
1085   CStdString strTest = strCondition;
1086   StringUtils::ToLower(strTest);
1087   StringUtils::Trim(strTest);
1088
1089   size_t pos1 = strTest.find("(");
1090   size_t pos2 = strTest.find(",", pos1);
1091   size_t pos3 = strTest.find(")", pos2);
1092   if (pos1 != std::string::npos &&
1093       pos2 != std::string::npos &&
1094       pos3 != std::string::npos)
1095   {
1096     condVec.push_back(strTest.substr(0, pos1));
1097     condVec.push_back(strTest.substr(pos1 + 1, pos2 - pos1 - 1));
1098     condVec.push_back(strTest.substr(pos2 + 1, pos3 - pos2 - 1));
1099     return true;
1100   }
1101   return false;
1102 }
1103
1104 CStdString CGUIDialogAddonSettings::GetString(const char *value, bool subSetting) const
1105 {
1106   if (!value)
1107     return "";
1108   CStdString prefix(subSetting ? "- " : "");
1109   if (StringUtils::IsNaturalNumber(value))
1110     return prefix + m_addon->GetString(atoi(value));
1111   return prefix + value;
1112 }
1113
1114 // Go over all the settings and set their default values
1115 void CGUIDialogAddonSettings::SetDefaultSettings()
1116 {
1117   if(!m_addon)
1118     return;
1119
1120   const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category");
1121   if (!category) // add a default one...
1122     category = m_addon->GetSettingsXML();
1123
1124   while (category)
1125   {
1126     const TiXmlElement *setting = category->FirstChildElement("setting");
1127     while (setting)
1128     {
1129       const char *id = setting->Attribute("id");
1130       const char *type = setting->Attribute("type");
1131       const char *value = setting->Attribute("default");
1132       if (id)
1133       {
1134         if (value)
1135           m_settings[id] = value;
1136         else if (type && 0 == strcmpi(type, "bool"))
1137           m_settings[id] = "false";
1138         else if (type && (0 == strcmpi(type, "slider") || 0 == strcmpi(type, "enum")))
1139           m_settings[id] = "0";
1140         else
1141           m_settings[id] = "";
1142       }
1143       setting = setting->NextSiblingElement("setting");
1144     }
1145     category = category->NextSiblingElement("category");
1146   }
1147   CreateControls();
1148 }
1149
1150 const TiXmlElement *CGUIDialogAddonSettings::GetFirstSetting() const
1151 {
1152   const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category");
1153   if (!category)
1154     category = m_addon->GetSettingsXML();
1155   for (unsigned int i = 0; i < m_currentSection && category; i++)
1156     category = category->NextSiblingElement("category");
1157   if (category)
1158     return category->FirstChildElement("setting");
1159   return NULL;
1160 }
1161
1162 void CGUIDialogAddonSettings::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
1163 {
1164   // update status of current section button
1165   bool alphaFaded = false;
1166   CGUIControl *control = GetFirstFocusableControl(CONTROL_START_SECTION + m_currentSection);
1167   if (control && !control->HasFocus())
1168   {
1169     if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
1170     {
1171       control->SetFocus(true);
1172       ((CGUIButtonControl *)control)->SetAlpha(0x80);
1173       alphaFaded = true;
1174     }
1175     else if (control->GetControlType() == CGUIControl::GUICONTROL_TOGGLEBUTTON)
1176     {
1177       control->SetFocus(true);
1178       ((CGUIButtonControl *)control)->SetSelected(true);
1179       alphaFaded = true;
1180     }
1181   }
1182   CGUIDialogBoxBase::DoProcess(currentTime, dirtyregions);
1183   if (alphaFaded && m_active) // dialog may close
1184   {
1185     control->SetFocus(false);
1186     if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
1187       ((CGUIButtonControl *)control)->SetAlpha(0xFF);
1188     else
1189       ((CGUIButtonControl *)control)->SetSelected(false);
1190   }
1191 }
1192
1193 CStdString CGUIDialogAddonSettings::GetCurrentID() const
1194 {
1195   if (m_addon)
1196     return m_addon->ID();
1197   return "";
1198 }
1199
1200 int CGUIDialogAddonSettings::GetDefaultLabelID(int controlId) const
1201 {
1202   if (controlId == ID_BUTTON_OK)
1203     return 186;
1204   else if (controlId == ID_BUTTON_CANCEL)
1205     return 222;
1206   else if (controlId == ID_BUTTON_DEFAULT)
1207     return 409;
1208
1209   return CGUIDialogBoxBase::GetDefaultLabelID(controlId);
1210 }