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