/* * 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); }