/*
* Copyright (C) 2005-2013 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XBMC; see the file COPYING. If not, see
* .
*
*/
#include "GUIDialogAddonSettings.h"
#include "filesystem/PluginDirectory.h"
#include "addons/IAddon.h"
#include "addons/AddonManager.h"
#include "dialogs/GUIDialogNumeric.h"
#include "dialogs/GUIDialogFileBrowser.h"
#include "dialogs/GUIDialogOK.h"
#include "guilib/GUIControlGroupList.h"
#include "guilib/GUISettingsSliderControl.h"
#include "utils/URIUtils.h"
#include "utils/StringUtils.h"
#include "storage/MediaManager.h"
#include "guilib/GUILabelControl.h"
#include "guilib/GUIRadioButtonControl.h"
#include "guilib/GUISpinControlEx.h"
#include "guilib/GUIImage.h"
#include "input/Key.h"
#include "filesystem/Directory.h"
#include "video/VideoInfoScanner.h"
#include "addons/Scraper.h"
#include "guilib/GUIWindowManager.h"
#include "ApplicationMessenger.h"
#include "guilib/GUIKeyboardFactory.h"
#include "FileItem.h"
#include "settings/AdvancedSettings.h"
#include "settings/MediaSourceSettings.h"
#include "GUIInfoManager.h"
#include "GUIUserMessages.h"
#include "dialogs/GUIDialogSelect.h"
#include "GUIWindowAddonBrowser.h"
#include "utils/log.h"
#include "Util.h"
#include "URL.h"
#include "utils/XMLUtils.h"
using namespace std;
using namespace ADDON;
using XFILE::CDirectory;
#define CONTROL_SETTINGS_AREA 2
#define CONTROL_DEFAULT_BUTTON 3
#define CONTROL_DEFAULT_RADIOBUTTON 4
#define CONTROL_DEFAULT_SPIN 5
#define CONTROL_DEFAULT_SEPARATOR 6
#define CONTROL_DEFAULT_LABEL_SEPARATOR 7
#define CONTROL_DEFAULT_SLIDER 8
#define CONTROL_SECTION_AREA 9
#define CONTROL_DEFAULT_SECTION_BUTTON 13
#define ID_BUTTON_OK 10
#define ID_BUTTON_CANCEL 11
#define ID_BUTTON_DEFAULT 12
#define CONTROL_HEADING_LABEL 20
#define CONTROL_START_SECTION 100
#define CONTROL_START_SETTING 200
CGUIDialogAddonSettings::CGUIDialogAddonSettings()
: CGUIDialogBoxBase(WINDOW_DIALOG_ADDON_SETTINGS, "DialogAddonSettings.xml")
{
m_currentSection = 0;
m_totalSections = 1;
m_saveToDisk = false;
}
CGUIDialogAddonSettings::~CGUIDialogAddonSettings(void)
{
}
bool CGUIDialogAddonSettings::OnMessage(CGUIMessage& message)
{
switch (message.GetMessage())
{
case GUI_MSG_WINDOW_DEINIT:
{
FreeSections();
}
break;
case GUI_MSG_CLICKED:
{
int iControl = message.GetSenderId();
bool bCloseDialog = false;
if (iControl == ID_BUTTON_DEFAULT)
SetDefaultSettings();
else if (iControl != ID_BUTTON_OK)
bCloseDialog = ShowVirtualKeyboard(iControl);
if (iControl == ID_BUTTON_OK || iControl == ID_BUTTON_CANCEL || bCloseDialog)
{
if (iControl == ID_BUTTON_OK || bCloseDialog)
{
m_bConfirmed = true;
SaveSettings();
}
Close();
return true;
}
}
break;
case GUI_MSG_FOCUSED:
{
CGUIDialogBoxBase::OnMessage(message);
int focusedControl = GetFocusedControlID();
if (focusedControl >= CONTROL_START_SECTION && focusedControl < (int)(CONTROL_START_SECTION + m_totalSections) &&
focusedControl - CONTROL_START_SECTION != (int)m_currentSection)
{ // changing section
UpdateFromControls();
m_currentSection = focusedControl - CONTROL_START_SECTION;
CreateControls();
}
return true;
}
case GUI_MSG_SETTING_UPDATED:
{
std::string id = message.GetStringParam(0);
std::string value = message.GetStringParam(1);
m_settings[id] = value;
if (GetFocusedControl())
{
int iControl = GetFocusedControl()->GetID();
CreateControls();
CGUIMessage msg(GUI_MSG_SETFOCUS,GetID(),iControl);
OnMessage(msg);
}
return true;
}
}
return CGUIDialogBoxBase::OnMessage(message);
}
bool CGUIDialogAddonSettings::OnAction(const CAction& action)
{
if (action.GetID() == ACTION_DELETE_ITEM)
{
CGUIControl* pControl = GetFocusedControl();
if (pControl)
{
int iControl = pControl->GetID();
int controlId = CONTROL_START_SETTING;
const TiXmlElement* setting = GetFirstSetting();
UpdateFromControls();
while (setting)
{
if (controlId == iControl)
{
const char* id = setting->Attribute("id");
const char* value = setting->Attribute("default");
if (id && value)
m_settings[id] = value;
CreateControls();
CGUIMessage msg(GUI_MSG_SETFOCUS,GetID(),iControl);
OnMessage(msg);
return true;
}
setting = setting->NextSiblingElement("setting");
controlId++;
}
}
}
return CGUIDialogBoxBase::OnAction(action);
}
void CGUIDialogAddonSettings::OnInitWindow()
{
m_currentSection = 0;
m_totalSections = 1;
CreateSections();
CreateControls();
CGUIDialogBoxBase::OnInitWindow();
}
// \brief Show CGUIDialogOK dialog, then wait for user to dismiss it.
bool CGUIDialogAddonSettings::ShowAndGetInput(const AddonPtr &addon, bool saveToDisk /* = true */)
{
if (!addon)
return false;
if (!g_passwordManager.CheckMenuLock(WINDOW_ADDON_BROWSER))
return false;
bool ret(false);
if (addon->HasSettings())
{
// Create the dialog
CGUIDialogAddonSettings* pDialog = NULL;
pDialog = (CGUIDialogAddonSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_ADDON_SETTINGS);
if (!pDialog)
return false;
// Set the heading
std::string heading = StringUtils::Format("$LOCALIZE[10004] - %s", addon->Name().c_str()); // "Settings - AddonName"
pDialog->m_strHeading = heading;
pDialog->m_addon = addon;
pDialog->m_saveToDisk = saveToDisk;
pDialog->DoModal();
ret = true;
}
else
{ // addon does not support settings, inform user
CGUIDialogOK::ShowAndGetInput(24000,0,24030,0);
}
return ret;
}
bool CGUIDialogAddonSettings::ShowVirtualKeyboard(int iControl)
{
int controlId = CONTROL_START_SETTING;
bool bCloseDialog = false;
const TiXmlElement *setting = GetFirstSetting();
while (setting)
{
if (controlId == iControl)
{
const CGUIControl* control = GetControl(controlId);
const std::string id = XMLUtils::GetAttribute(setting, "id");
const std::string type = XMLUtils::GetAttribute(setting, "type");
//Special handling for actions: does not require id attribute. TODO: refactor me.
if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON && type == "action")
{
const char *option = setting->Attribute("option");
std::string action = XMLUtils::GetAttribute(setting, "action");
if (!action.empty())
{
// replace $CWD with the url of plugin/script
StringUtils::Replace(action, "$CWD", m_addon->Path());
StringUtils::Replace(action, "$ID", m_addon->ID());
if (option)
bCloseDialog = (strcmpi(option, "close") == 0);
CApplicationMessenger::Get().ExecBuiltIn(action);
}
break;
}
if (control && control->GetControlType() == CGUIControl::GUICONTROL_BUTTON &&
!id.empty() && !type.empty())
{
const char *option = setting->Attribute("option");
const char *source = setting->Attribute("source");
std::string value = m_buttonValues[id];
std::string label = GetString(setting->Attribute("label"));
if (type == "text")
{
// get any options
bool bHidden = false;
bool bEncoded = false;
if (option)
{
bHidden = (strstr(option, "hidden") != NULL);
bEncoded = (strstr(option, "urlencoded") != NULL);
}
if (bEncoded)
value = CURL::Decode(value);
if (CGUIKeyboardFactory::ShowAndGetInput(value, label, true, bHidden))
{
// if hidden hide input
if (bHidden)
{
std::string hiddenText;
hiddenText.append(value.size(), L'*');
((CGUIButtonControl *)control)->SetLabel2(hiddenText);
}
else
((CGUIButtonControl*) control)->SetLabel2(value);
if (bEncoded)
value = CURL::Encode(value);
}
}
else if (type == "number" && CGUIDialogNumeric::ShowAndGetNumber(value, label))
{
((CGUIButtonControl*) control)->SetLabel2(value);
}
else if (type == "ipaddress" && CGUIDialogNumeric::ShowAndGetIPAddress(value, label))
{
((CGUIButtonControl*) control)->SetLabel2(value);
}
else if (type == "select")
{
CGUIDialogSelect *pDlg = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
if (pDlg)
{
pDlg->SetHeading(label.c_str());
pDlg->Reset();
int selected = -1;
vector valuesVec;
if (setting->Attribute("values"))
StringUtils::Tokenize(setting->Attribute("values"), valuesVec, "|");
else if (setting->Attribute("lvalues"))
{ // localize
StringUtils::Tokenize(setting->Attribute("lvalues"), valuesVec, "|");
for (unsigned int i = 0; i < valuesVec.size(); i++)
{
if (i == (unsigned int)atoi(value.c_str()))
selected = i;
std::string localized = m_addon->GetString(atoi(valuesVec[i].c_str()));
if (localized.empty())
localized = g_localizeStrings.Get(atoi(valuesVec[i].c_str()));
valuesVec[i] = localized;
}
}
else if (source)
{
valuesVec = GetFileEnumValues(source, XMLUtils::GetAttribute(setting, "mask"), XMLUtils::GetAttribute(setting, "option"));
}
for (unsigned int i = 0; i < valuesVec.size(); i++)
{
pDlg->Add(valuesVec[i]);
if (selected == (int)i || (selected < 0 && StringUtils::EqualsNoCase(valuesVec[i], value)))
pDlg->SetSelected(i); // FIXME: the SetSelected() does not select "i", it always defaults to the first position
}
pDlg->DoModal();
int iSelected = pDlg->GetSelectedLabel();
if (iSelected >= 0)
{
if (setting->Attribute("lvalues"))
value = StringUtils::Format("%i", iSelected);
else
value = valuesVec[iSelected];
((CGUIButtonControl*) control)->SetLabel2(valuesVec[iSelected]);
}
}
}
else if (type == "audio" || type == "video"
|| type == "image" || type == "executable"
|| type == "file" || type == "folder")
{
// setup the shares
VECSOURCES *shares = NULL;
if (source && strcmpi(source, "") != 0)
shares = CMediaSourceSettings::Get().GetSources(source);
VECSOURCES localShares;
if (!shares)
{
g_mediaManager.GetLocalDrives(localShares);
if (!source || strcmpi(source, "local") != 0)
g_mediaManager.GetNetworkLocations(localShares);
}
else // always append local drives
{
localShares = *shares;
g_mediaManager.GetLocalDrives(localShares);
}
if (type == "folder")
{
// get any options
bool bWriteOnly = false;
if (option)
bWriteOnly = (strcmpi(option, "writeable") == 0);
if (CGUIDialogFileBrowser::ShowAndGetDirectory(localShares, label, value, bWriteOnly))
((CGUIButtonControl*) control)->SetLabel2(value);
}
else if (type == "image")
{
if (CGUIDialogFileBrowser::ShowAndGetImage(localShares, label, value))
((CGUIButtonControl*) control)->SetLabel2(value);
}
else
{
// set the proper mask
std::string strMask;
if (setting->Attribute("mask"))
{
strMask = setting->Attribute("mask");
// convert mask qualifiers
StringUtils::Replace(strMask, "$AUDIO", g_advancedSettings.m_musicExtensions);
StringUtils::Replace(strMask, "$VIDEO", g_advancedSettings.m_videoExtensions);
StringUtils::Replace(strMask, "$IMAGE", g_advancedSettings.m_pictureExtensions);
#if defined(_WIN32_WINNT)
StringUtils::Replace(strMask, "$EXECUTABLE", ".exe|.bat|.cmd|.py");
#else
StringUtils::Replace(strMask, "$EXECUTABLE", "");
#endif
}
else
{
if (type == "video")
strMask = g_advancedSettings.m_videoExtensions;
else if (type == "audio")
strMask = g_advancedSettings.m_musicExtensions;
else if (type == "executable")
#if defined(_WIN32_WINNT)
strMask = ".exe|.bat|.cmd|.py";
#else
strMask = "";
#endif
}
// get any options
bool bUseThumbs = false;
bool bUseFileDirectories = false;
if (option)
{
vector options = StringUtils::Split(option, '|');
bUseThumbs = find(options.begin(), options.end(), "usethumbs") != options.end();
bUseFileDirectories = find(options.begin(), options.end(), "treatasfolder") != options.end();
}
if (CGUIDialogFileBrowser::ShowAndGetFile(localShares, strMask, label, value, bUseThumbs, bUseFileDirectories))
((CGUIButtonControl*) control)->SetLabel2(value);
}
}
else if (type == "date")
{
CDateTime date;
if (!value.empty())
date.SetFromDBDate(value);
SYSTEMTIME timedate;
date.GetAsSystemTime(timedate);
if(CGUIDialogNumeric::ShowAndGetDate(timedate, label))
{
date = timedate;
value = date.GetAsDBDate();
((CGUIButtonControl*) control)->SetLabel2(value);
}
}
else if (type == "time")
{
SYSTEMTIME timedate;
if (value.size() >= 5)
{
// assumes HH:MM
timedate.wHour = atoi(value.substr(0, 2).c_str());
timedate.wMinute = atoi(value.substr(3, 2).c_str());
}
if (CGUIDialogNumeric::ShowAndGetTime(timedate, label))
{
value = StringUtils::Format("%02d:%02d", timedate.wHour, timedate.wMinute);
((CGUIButtonControl*) control)->SetLabel2(value);
}
}
else if (type == "addon")
{
const char *strType = setting->Attribute("addontype");
if (strType)
{
vector addonTypes = StringUtils::Split(strType, ',');
vector types;
for (vector::iterator i = addonTypes.begin(); i != addonTypes.end(); ++i)
{
StringUtils::Trim(*i);
ADDON::TYPE type = TranslateType(*i);
if (type != ADDON_UNKNOWN)
types.push_back(type);
}
if (types.size() > 0)
{
const char *strMultiselect = setting->Attribute("multiselect");
bool multiSelect = strMultiselect && strcmpi(strMultiselect, "true") == 0;
if (multiSelect)
{
// construct vector of addon IDs (IDs are comma seperated in single string)
vector addonIDs = StringUtils::Split(value, ',');
if (CGUIWindowAddonBrowser::SelectAddonID(types, addonIDs, false) == 1)
{
value = StringUtils::Join(addonIDs, ",");
((CGUIButtonControl*) control)->SetLabel2(GetAddonNames(value));
}
}
else // no need of string splitting/joining if we select only 1 addon
if (CGUIWindowAddonBrowser::SelectAddonID(types, value, false) == 1)
((CGUIButtonControl*) control)->SetLabel2(GetAddonNames(value));
}
}
}
m_buttonValues[id] = value;
break;
}
}
setting = setting->NextSiblingElement("setting");
controlId++;
}
EnableControls();
return bCloseDialog;
}
void CGUIDialogAddonSettings::UpdateFromControls()
{
int controlID = CONTROL_START_SETTING;
const TiXmlElement *setting = GetFirstSetting();
while (setting)
{
const std::string id = XMLUtils::GetAttribute(setting, "id");
const std::string type = XMLUtils::GetAttribute(setting, "type");
const CGUIControl* control = GetControl(controlID++);
if (control)
{
std::string value;
switch (control->GetControlType())
{
case CGUIControl::GUICONTROL_BUTTON:
value = m_buttonValues[id];
break;
case CGUIControl::GUICONTROL_RADIO:
value = ((CGUIRadioButtonControl*) control)->IsSelected() ? "true" : "false";
break;
case CGUIControl::GUICONTROL_SPINEX:
if (type == "fileenum" || type == "labelenum")
value = ((CGUISpinControlEx*) control)->GetLabel();
else
value = StringUtils::Format("%i", ((CGUISpinControlEx*) control)->GetValue());
break;
case CGUIControl::GUICONTROL_SETTINGS_SLIDER:
{
std::string option = XMLUtils::GetAttribute(setting, "option");
if (option.size() == 0 || StringUtils::EqualsNoCase(option, "float"))
value = StringUtils::Format("%f", ((CGUISettingsSliderControl *)control)->GetFloatValue());
else
value = StringUtils::Format("%i", ((CGUISettingsSliderControl *)control)->GetIntValue());
}
break;
default:
break;
}
m_settings[id] = value;
}
setting = setting->NextSiblingElement("setting");
}
}
void CGUIDialogAddonSettings::SaveSettings(void)
{
UpdateFromControls();
for (map::iterator i = m_settings.begin(); i != m_settings.end(); ++i)
m_addon->UpdateSetting(i->first, i->second);
if (m_saveToDisk)
{
m_addon->SaveSettings();
}
}
void CGUIDialogAddonSettings::FreeSections()
{
CGUIControlGroupList *group = dynamic_cast(GetControl(CONTROL_SECTION_AREA));
if (group)
{
group->FreeResources();
group->ClearAll();
}
m_settings.clear();
m_buttonValues.clear();
FreeControls();
}
void CGUIDialogAddonSettings::FreeControls()
{
// clear the category group
CGUIControlGroupList *control = dynamic_cast(GetControl(CONTROL_SETTINGS_AREA));
if (control)
{
control->FreeResources();
control->ClearAll();
}
}
void CGUIDialogAddonSettings::CreateSections()
{
CGUIControlGroupList *group = dynamic_cast(GetControl(CONTROL_SECTION_AREA));
CGUIButtonControl *originalButton = dynamic_cast(GetControl(CONTROL_DEFAULT_SECTION_BUTTON));
if (!m_addon)
return;
if (originalButton)
originalButton->SetVisible(false);
// clear the category group
FreeSections();
// grab our categories
const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category");
if (!category) // add a default one...
category = m_addon->GetSettingsXML();
int buttonID = CONTROL_START_SECTION;
while (category)
{ // add a category
CGUIButtonControl *button = originalButton ? originalButton->Clone() : NULL;
std::string label = GetString(category->Attribute("label"));
if (label.empty())
label = g_localizeStrings.Get(128);
if (buttonID >= CONTROL_START_SETTING)
{
CLog::Log(LOGERROR, "%s - cannot have more than %d categories - simplify your addon!", __FUNCTION__, CONTROL_START_SETTING - CONTROL_START_SECTION);
break;
}
// add the category button
if (button && group)
{
button->SetID(buttonID++);
button->SetLabel(label);
button->SetVisible(true);
group->AddControl(button);
}
// grab a local copy of all the settings in this category
const TiXmlElement *setting = category->FirstChildElement("setting");
while (setting)
{
const std::string id = XMLUtils::GetAttribute(setting, "id");
if (!id.empty())
m_settings[id] = m_addon->GetSetting(id);
setting = setting->NextSiblingElement("setting");
}
category = category->NextSiblingElement("category");
}
m_totalSections = buttonID - CONTROL_START_SECTION;
}
void CGUIDialogAddonSettings::CreateControls()
{
FreeControls();
CGUISpinControlEx *pOriginalSpin = dynamic_cast(GetControl(CONTROL_DEFAULT_SPIN));
CGUIRadioButtonControl *pOriginalRadioButton = dynamic_cast(GetControl(CONTROL_DEFAULT_RADIOBUTTON));
CGUIButtonControl *pOriginalButton = dynamic_cast(GetControl(CONTROL_DEFAULT_BUTTON));
CGUIImage *pOriginalImage = dynamic_cast(GetControl(CONTROL_DEFAULT_SEPARATOR));
CGUILabelControl *pOriginalLabel = dynamic_cast(GetControl(CONTROL_DEFAULT_LABEL_SEPARATOR));
CGUISettingsSliderControl *pOriginalSlider = dynamic_cast(GetControl(CONTROL_DEFAULT_SLIDER));
if (!m_addon || !pOriginalSpin || !pOriginalRadioButton || !pOriginalButton || !pOriginalImage
|| !pOriginalLabel || !pOriginalSlider)
return;
pOriginalSpin->SetVisible(false);
pOriginalRadioButton->SetVisible(false);
pOriginalButton->SetVisible(false);
pOriginalImage->SetVisible(false);
pOriginalLabel->SetVisible(false);
pOriginalSlider->SetVisible(false);
CGUIControlGroupList *group = dynamic_cast(GetControl(CONTROL_SETTINGS_AREA));
if (!group)
return;
// set our dialog heading
SET_CONTROL_LABEL(CONTROL_HEADING_LABEL, m_strHeading);
CGUIControl* pControl = NULL;
int controlId = CONTROL_START_SETTING;
const TiXmlElement *setting = GetFirstSetting();
while (setting)
{
const std::string type = XMLUtils::GetAttribute(setting, "type");
const std::string id = XMLUtils::GetAttribute(setting, "id");
const std::string values = XMLUtils::GetAttribute(setting, "values");
const std::string lvalues = XMLUtils::GetAttribute(setting, "lvalues");
const std::string entries = XMLUtils::GetAttribute(setting, "entries");
const std::string defaultVal = XMLUtils::GetAttribute(setting, "default");
const std::string subsetting = XMLUtils::GetAttribute(setting, "subsetting");
const std::string label = GetString(setting->Attribute("label"), subsetting == "true");
bool bSort = XMLUtils::GetAttribute(setting, "sort") == "yes";
if (!type.empty())
{
bool isAddonSetting = false;
if (type == "text" || type == "ipaddress"
|| type == "number" || type == "video"
|| type == "audio" || type == "image"
|| type == "folder" || type == "executable"
|| type == "file" || type == "action"
|| type == "date" || type == "time"
|| type == "select" || (isAddonSetting = type == "addon"))
{
pControl = new CGUIButtonControl(*pOriginalButton);
if (!pControl) return;
((CGUIButtonControl *)pControl)->SetLabel(label);
if (!id.empty())
{
std::string value = m_settings[id];
m_buttonValues[id] = value;
// get any option to test for hidden
const std::string option = XMLUtils::GetAttribute(setting, "option");
if (option == "urlencoded")
value = CURL::Decode(value);
else if (option == "hidden")
{
std::string hiddenText;
hiddenText.append(value.size(), L'*');
((CGUIButtonControl *)pControl)->SetLabel2(hiddenText);
}
else
{
if (isAddonSetting)
((CGUIButtonControl *)pControl)->SetLabel2(GetAddonNames(value));
else if (type == "select" && !lvalues.empty())
{
vector valuesVec = StringUtils::Split(lvalues, '|');
int selected = atoi(value.c_str());
if (selected >= 0 && selected < (int)valuesVec.size())
{
std::string label = m_addon->GetString(atoi(valuesVec[selected].c_str()));
if (label.empty())
label = g_localizeStrings.Get(atoi(valuesVec[selected].c_str()));
((CGUIButtonControl *)pControl)->SetLabel2(label);
}
}
else
((CGUIButtonControl *)pControl)->SetLabel2(value);
}
}
else
((CGUIButtonControl *)pControl)->SetLabel2(defaultVal);
}
else if (type == "bool" && !id.empty())
{
pControl = new CGUIRadioButtonControl(*pOriginalRadioButton);
if (!pControl) return;
((CGUIRadioButtonControl *)pControl)->SetLabel(label);
((CGUIRadioButtonControl *)pControl)->SetSelected(m_settings[id] == "true");
}
else if ((type == "enum" || type == "labelenum") && !id.empty())
{
vector valuesVec;
vector entryVec;
pControl = new CGUISpinControlEx(*pOriginalSpin);
if (!pControl) return;
((CGUISpinControlEx *)pControl)->SetText(label);
if (!lvalues.empty())
StringUtils::Tokenize(lvalues, valuesVec, "|");
else if (values == "$HOURS")
{
for (unsigned int i = 0; i < 24; i++)
{
CDateTime time(2000, 1, 1, i, 0, 0);
valuesVec.push_back(g_infoManager.LocalizeTime(time, TIME_FORMAT_HH_MM_XX));
}
}
else
StringUtils::Tokenize(values, valuesVec, "|");
if (!entries.empty())
StringUtils::Tokenize(entries, entryVec, "|");
if(bSort && type == "labelenum")
std::sort(valuesVec.begin(), valuesVec.end(), sortstringbyname());
for (unsigned int i = 0; i < valuesVec.size(); i++)
{
int iAdd = i;
if (entryVec.size() > i)
iAdd = atoi(entryVec[i].c_str());
if (!lvalues.empty())
{
std::string replace = m_addon->GetString(atoi(valuesVec[i].c_str()));
if (replace.empty())
replace = g_localizeStrings.Get(atoi(valuesVec[i].c_str()));
((CGUISpinControlEx *)pControl)->AddLabel(replace, iAdd);
}
else
((CGUISpinControlEx *)pControl)->AddLabel(valuesVec[i], iAdd);
}
if (type == "labelenum")
{ // need to run through all our settings and find the one that matches
((CGUISpinControlEx*) pControl)->SetValueFromLabel(m_settings[id]);
}
else
((CGUISpinControlEx*) pControl)->SetValue(atoi(m_settings[id].c_str()));
}
else if (type == "fileenum" && !id.empty())
{
pControl = new CGUISpinControlEx(*pOriginalSpin);
if (!pControl) return;
((CGUISpinControlEx *)pControl)->SetText(label);
((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f);
vector items = GetFileEnumValues(values, XMLUtils::GetAttribute(setting, "mask"), XMLUtils::GetAttribute(setting, "option"));
for (unsigned int i = 0; i < items.size(); ++i)
{
((CGUISpinControlEx *)pControl)->AddLabel(items[i], i);
if (StringUtils::EqualsNoCase(items[i], m_settings[id]))
((CGUISpinControlEx *)pControl)->SetValue(i);
}
}
// Sample:
// in strings.xml: %2.0f mp
// creates 11 piece, text formated number labels from 0 to 100
else if (type == "rangeofnum" && !id.empty())
{
pControl = new CGUISpinControlEx(*pOriginalSpin);
if (!pControl)
return;
((CGUISpinControlEx *)pControl)->SetText(label);
((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f);
double rangestart = 0, rangeend = 1;
setting->Attribute("rangestart", &rangestart);
setting->Attribute("rangeend", &rangeend);
int elements = 2;
setting->Attribute("elements", &elements);
std::string valueformat;
if (setting->Attribute("valueformat"))
valueformat = m_addon->GetString(atoi(setting->Attribute("valueformat")));
for (int i = 0; i < elements; i++)
{
std::string valuestring;
if (elements < 2)
valuestring = StringUtils::Format(valueformat.c_str(), rangestart);
else
valuestring = StringUtils::Format(valueformat.c_str(), rangestart+(rangeend-rangestart)/(elements-1)*i);
((CGUISpinControlEx *)pControl)->AddLabel(valuestring, i);
}
((CGUISpinControlEx *)pControl)->SetValue(atoi(m_settings[id].c_str()));
}
// Sample:
// to make ints from 5-60 with 5 steps
else if (type == "slider" && !id.empty())
{
pControl = new CGUISettingsSliderControl(*pOriginalSlider);
if (!pControl) return;
((CGUISettingsSliderControl *)pControl)->SetText(label);
float fMin = 0.0f;
float fMax = 100.0f;
float fInc = 1.0f;
vector range = StringUtils::Split(XMLUtils::GetAttribute(setting, "range"), ',');
if (range.size() > 1)
{
fMin = (float)atof(range[0].c_str());
if (range.size() > 2)
{
fMax = (float)atof(range[2].c_str());
fInc = (float)atof(range[1].c_str());
}
else
fMax = (float)atof(range[1].c_str());
}
std::string option = XMLUtils::GetAttribute(setting, "option");
int iType=0;
if (option.empty() || StringUtils::EqualsNoCase(option, "float"))
iType = SLIDER_CONTROL_TYPE_FLOAT;
else if (StringUtils::EqualsNoCase(option, "int"))
iType = SLIDER_CONTROL_TYPE_INT;
else if (StringUtils::EqualsNoCase(option, "percent"))
iType = SLIDER_CONTROL_TYPE_PERCENTAGE;
((CGUISettingsSliderControl *)pControl)->SetType(iType);
((CGUISettingsSliderControl *)pControl)->SetFloatRange(fMin, fMax);
((CGUISettingsSliderControl *)pControl)->SetFloatInterval(fInc);
((CGUISettingsSliderControl *)pControl)->SetFloatValue((float)atof(m_settings[id].c_str()));
}
else if (type == "lsep")
{
pControl = new CGUILabelControl(*pOriginalLabel);
if (pControl)
((CGUILabelControl *)pControl)->SetLabel(label);
}
else if (type == "sep")
pControl = new CGUIImage(*pOriginalImage);
}
if (pControl)
{
pControl->SetWidth(group->GetWidth());
pControl->SetVisible(true);
pControl->SetID(controlId);
pControl->AllocResources();
group->AddControl(pControl);
pControl = NULL;
}
setting = setting->NextSiblingElement("setting");
controlId++;
}
EnableControls();
}
std::string CGUIDialogAddonSettings::GetAddonNames(const std::string& addonIDslist) const
{
std::string retVal;
vector addons = StringUtils::Split(addonIDslist, ',');
for (vector::const_iterator it = addons.begin(); it != addons.end() ; ++it)
{
if (!retVal.empty())
retVal += ", ";
AddonPtr addon;
if (CAddonMgr::Get().GetAddon(*it ,addon))
retVal += addon->Name();
else
retVal += *it;
}
return retVal;
}
vector CGUIDialogAddonSettings::GetFileEnumValues(const std::string &path, const std::string &mask, const std::string &options) const
{
// Create our base path, used for type "fileenum" settings
// replace $PROFILE with the profile path of the plugin/script
std::string fullPath = path;
if (fullPath.find("$PROFILE") != std::string::npos)
StringUtils::Replace(fullPath, "$PROFILE", m_addon->Profile());
else
fullPath = URIUtils::AddFileToFolder(m_addon->Path(), path);
bool hideExtensions = StringUtils::EqualsNoCase(options, "hideext");
// fetch directory
CFileItemList items;
if (!mask.empty())
CDirectory::GetDirectory(fullPath, items, mask, XFILE::DIR_FLAG_NO_FILE_DIRS);
else
CDirectory::GetDirectory(fullPath, items, "", XFILE::DIR_FLAG_NO_FILE_DIRS);
vector values;
for (int i = 0; i < items.Size(); ++i)
{
CFileItemPtr pItem = items[i];
if ((mask == "/" && pItem->m_bIsFolder) || !pItem->m_bIsFolder)
{
if (hideExtensions)
pItem->RemoveExtension();
values.push_back(pItem->GetLabel());
}
}
return values;
}
// Go over all the settings and set their enabled condition according to the values of the enabled attribute
void CGUIDialogAddonSettings::EnableControls()
{
int controlId = CONTROL_START_SETTING;
const TiXmlElement *setting = GetFirstSetting();
while (setting)
{
const CGUIControl* control = GetControl(controlId);
if (control)
{
// set enable status
const char *enable = setting->Attribute("enable");
if (enable)
((CGUIControl*) control)->SetEnabled(GetCondition(enable, controlId));
else
((CGUIControl*) control)->SetEnabled(true);
// set visible status
const char *visible = setting->Attribute("visible");
if (visible)
((CGUIControl*) control)->SetVisible(GetCondition(visible, controlId));
else
((CGUIControl*) control)->SetVisible(true);
}
setting = setting->NextSiblingElement("setting");
controlId++;
}
}
bool CGUIDialogAddonSettings::GetCondition(const std::string &condition, const int controlId)
{
if (condition.empty()) return true;
bool bCondition = true;
bool bCompare = true;
bool bControlDependend = false;//flag if the condition depends on another control
vector conditionVec;
if (condition.find("+") != std::string::npos)
StringUtils::Tokenize(condition, conditionVec, "+");
else
{
bCondition = false;
bCompare = false;
StringUtils::Tokenize(condition, conditionVec, "|");
}
for (unsigned int i = 0; i < conditionVec.size(); i++)
{
vector condVec;
if (!TranslateSingleString(conditionVec[i], condVec)) continue;
const CGUIControl* control2 = GetControl(controlId + atoi(condVec[1].c_str()));
if (!control2)
continue;
bControlDependend = true; //once we are here - this condition depends on another control
std::string value;
switch (control2->GetControlType())
{
case CGUIControl::GUICONTROL_BUTTON:
value = ((CGUIButtonControl*) control2)->GetLabel2();
break;
case CGUIControl::GUICONTROL_RADIO:
value = ((CGUIRadioButtonControl*) control2)->IsSelected() ? "true" : "false";
break;
case CGUIControl::GUICONTROL_SPINEX:
if (((CGUISpinControlEx*) control2)->GetFloatValue() > 0.0f)
value = ((CGUISpinControlEx*) control2)->GetLabel();
else
value = StringUtils::Format("%i", ((CGUISpinControlEx*) control2)->GetValue());
break;
default:
break;
}
if (condVec[0] == "eq")
{
if (bCompare)
bCondition &= StringUtils::EqualsNoCase(value, condVec[2]);
else
bCondition |= StringUtils::EqualsNoCase(value, condVec[2]);
}
else if (condVec[0] == "!eq")
{
if (bCompare)
bCondition &= !StringUtils::EqualsNoCase(value, condVec[2]);
else
bCondition |= !StringUtils::EqualsNoCase(value, condVec[2]);
}
else if (condVec[0] == "gt")
{
if (bCompare)
bCondition &= (atoi(value.c_str()) > atoi(condVec[2].c_str()));
else
bCondition |= (atoi(value.c_str()) > atoi(condVec[2].c_str()));
}
else if (condVec[0] == "lt")
{
if (bCompare)
bCondition &= (atoi(value.c_str()) < atoi(condVec[2].c_str()));
else
bCondition |= (atoi(value.c_str()) < atoi(condVec[2].c_str()));
}
}
if (!bControlDependend)//if condition doesn't depend on another control - try if its an infobool expression
{
bCondition = g_infoManager.EvaluateBool(condition);
}
return bCondition;
}
bool CGUIDialogAddonSettings::TranslateSingleString(const std::string &strCondition, vector &condVec)
{
std::string strTest = strCondition;
StringUtils::ToLower(strTest);
StringUtils::Trim(strTest);
size_t pos1 = strTest.find("(");
size_t pos2 = strTest.find(",", pos1);
size_t pos3 = strTest.find(")", pos2);
if (pos1 != std::string::npos &&
pos2 != std::string::npos &&
pos3 != std::string::npos)
{
condVec.push_back(strTest.substr(0, pos1));
condVec.push_back(strTest.substr(pos1 + 1, pos2 - pos1 - 1));
condVec.push_back(strTest.substr(pos2 + 1, pos3 - pos2 - 1));
return true;
}
return false;
}
std::string CGUIDialogAddonSettings::GetString(const char *value, bool subSetting) const
{
if (!value)
return "";
std::string prefix(subSetting ? "- " : "");
if (StringUtils::IsNaturalNumber(value))
return prefix + m_addon->GetString(atoi(value));
return prefix + value;
}
// Go over all the settings and set their default values
void CGUIDialogAddonSettings::SetDefaultSettings()
{
if(!m_addon)
return;
const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category");
if (!category) // add a default one...
category = m_addon->GetSettingsXML();
while (category)
{
const TiXmlElement *setting = category->FirstChildElement("setting");
while (setting)
{
const std::string id = XMLUtils::GetAttribute(setting, "id");
const std::string type = XMLUtils::GetAttribute(setting, "type");
const char *value = setting->Attribute("default");
if (!id.empty())
{
if (value)
m_settings[id] = value;
else if (type == "bool")
m_settings[id] = "false";
else if (type == "slider" || type == "enum")
m_settings[id] = "0";
else
m_settings[id] = "";
}
setting = setting->NextSiblingElement("setting");
}
category = category->NextSiblingElement("category");
}
CreateControls();
}
const TiXmlElement *CGUIDialogAddonSettings::GetFirstSetting() const
{
const TiXmlElement *category = m_addon->GetSettingsXML()->FirstChildElement("category");
if (!category)
category = m_addon->GetSettingsXML();
for (unsigned int i = 0; i < m_currentSection && category; i++)
category = category->NextSiblingElement("category");
if (category)
return category->FirstChildElement("setting");
return NULL;
}
void CGUIDialogAddonSettings::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
{
// update status of current section button
bool alphaFaded = false;
CGUIControl *control = GetFirstFocusableControl(CONTROL_START_SECTION + m_currentSection);
if (control && !control->HasFocus())
{
if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
{
control->SetFocus(true);
((CGUIButtonControl *)control)->SetAlpha(0x80);
alphaFaded = true;
}
else if (control->GetControlType() == CGUIControl::GUICONTROL_TOGGLEBUTTON)
{
control->SetFocus(true);
((CGUIButtonControl *)control)->SetSelected(true);
alphaFaded = true;
}
}
CGUIDialogBoxBase::DoProcess(currentTime, dirtyregions);
if (alphaFaded && m_active) // dialog may close
{
control->SetFocus(false);
if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
((CGUIButtonControl *)control)->SetAlpha(0xFF);
else
((CGUIButtonControl *)control)->SetSelected(false);
}
}
std::string CGUIDialogAddonSettings::GetCurrentID() const
{
if (m_addon)
return m_addon->ID();
return "";
}
int CGUIDialogAddonSettings::GetDefaultLabelID(int controlId) const
{
if (controlId == ID_BUTTON_OK)
return 186;
else if (controlId == ID_BUTTON_CANCEL)
return 222;
else if (controlId == ID_BUTTON_DEFAULT)
return 409;
return CGUIDialogBoxBase::GetDefaultLabelID(controlId);
}