Index: xbmc/GUIDialogPluginSettings.cpp =================================================================== --- xbmc/GUIDialogPluginSettings.cpp (revision 30503) +++ xbmc/GUIDialogPluginSettings.cpp (working copy) @@ -39,22 +39,35 @@ #include "GUIDialogKeyboard.h" #include "FileItem.h" #include "ScriptSettings.h" +#include "GUIDialogOK.h" +#include "GUIControlGroupList.h" +#include "GUISettingsSliderControl.h" +#include "StringUtils.h" +#include "Settings.h" +#include "GUIInfoManager.h" +#include "GUIDialogSelect.h" using namespace std; using namespace DIRECTORY; -#define CONTROL_AREA 2 +#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_CONTROL 100 +#define CONTROL_START_SETTING 100 +#define CONTROL_START_SECTION 200 + CGUIDialogPluginSettings::CGUIDialogPluginSettings() : CGUIDialogBoxBase(WINDOW_DIALOG_PLUGIN_SETTINGS, "DialogPluginSettings.xml") {} @@ -66,134 +79,169 @@ { switch (message.GetMessage()) { + case GUI_MSG_WINDOW_DEINIT: + { + FreeControls(); + FreeSections(); + } + break; case GUI_MSG_CLICKED: { int iControl = message.GetSenderId(); bool bCloseDialog = false; - if (iControl == ID_BUTTON_OK) - { - m_bConfirmed = true; - SaveSettings(); - } - else if (iControl == ID_BUTTON_DEFAULT) + if (iControl == ID_BUTTON_DEFAULT) SetDefaults(); - else + 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; + } } return CGUIDialogBoxBase::OnMessage(message); } void CGUIDialogPluginSettings::OnInitWindow() { - FreeControls(); + m_currentSection = 0; + m_totalSections = 1; + CreateSections(); CreateControls(); CGUIDialogBoxBase::OnInitWindow(); - m_bConfirmed = false; } // \brief Show CGUIDialogOK dialog, then wait for user to dismiss it. -bool CGUIDialogPluginSettings::ShowAndGetInput(CURL& url) +bool CGUIDialogPluginSettings::ShowAndGetInput(CURL& url, bool saveToDisk /* = true */) { - m_url = url; - - // Load language strings temporarily - DIRECTORY::CPluginDirectory::LoadPluginStrings(url); - - // Create the dialog - CGUIDialogPluginSettings* pDialog = (CGUIDialogPluginSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_PLUGIN_SETTINGS); - - pDialog->m_strHeading = m_url.GetFileName(); - CUtil::RemoveSlashAtEnd(pDialog->m_strHeading); - pDialog->m_strHeading.Format("$LOCALIZE[1045] - %s", pDialog->m_strHeading.c_str()); - CPluginSettings settings; - settings.Load(m_url); - pDialog->m_settings = settings; + if (settings.Load(url)) + { + // Create the dialog + CGUIDialogPluginSettings* pDialog = NULL; + pDialog = (CGUIDialogPluginSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_PLUGIN_SETTINGS); + if (!pDialog) + return false; - pDialog->DoModal(); + // Load language strings temporarily + DIRECTORY::CPluginDirectory::LoadPluginStrings(url); - if(pDialog->m_bConfirmed) - { - settings = pDialog->m_settings; - settings.Save(); + pDialog->m_url = url; + pDialog->m_strHeading = pDialog->m_url.GetFileNameWithoutPath(); + CUtil::RemoveSlashAtEnd(pDialog->m_strHeading); + pDialog->m_profile.Format("special://profile/plugin_data/%s/%s", pDialog->m_url.GetHostName().c_str(), pDialog->m_strHeading.c_str()); + pDialog->m_strHeading.Format("$LOCALIZE[1045] - %s", pDialog->m_strHeading.c_str()); + pDialog->m_addon = settings; + pDialog->m_changed = false; + pDialog->m_saveToDisk = saveToDisk; + pDialog->DoModal(); + if(pDialog->m_bConfirmed) + { + settings = pDialog->m_addon; + settings.Save(); + } + return pDialog->m_bConfirmed; } - - return pDialog->m_bConfirmed; + return false; } // \brief Show CGUIDialogOK dialog, then wait for user to dismiss it. -bool CGUIDialogPluginSettings::ShowAndGetInput(SScraperInfo& info) +bool CGUIDialogPluginSettings::ShowAndGetInput(SScraperInfo& info, bool saveToDisk /* = true */) { // Create the dialog - CGUIDialogPluginSettings* pDialog = (CGUIDialogPluginSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_PLUGIN_SETTINGS); + CGUIDialogPluginSettings* pDialog = NULL; + pDialog = (CGUIDialogPluginSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_PLUGIN_SETTINGS); + if (!pDialog) + return false; - pDialog->m_settings = info.settings; + pDialog->m_addon = info.settings; pDialog->m_strHeading.Format("$LOCALIZE[20407] - %s", info.strTitle.c_str()); - + pDialog->m_profile = ""; + pDialog->m_changed = false; + pDialog->m_saveToDisk = saveToDisk; pDialog->DoModal(); if(pDialog->m_bConfirmed) - info.settings.LoadUserXML(static_cast(pDialog->m_settings).GetSettings()); + info.settings.LoadUserXML(static_cast(pDialog->m_addon).GetSettings()); return pDialog->m_bConfirmed; } // \brief Show CGUIDialogOK dialog, then wait for user to dismiss it. -bool CGUIDialogPluginSettings::ShowAndGetInput(CStdString& path) +bool CGUIDialogPluginSettings::ShowAndGetInput(CStdString& path, bool saveToDisk /* = true */) { CUtil::RemoveSlashAtEnd(path); - m_url = CURL(path); + CScriptSettings settings; + if (settings.Load(path)) + { + // Create the dialog + CGUIDialogPluginSettings* pDialog = NULL; + pDialog = (CGUIDialogPluginSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_PLUGIN_SETTINGS); + if (!pDialog) + return false; - // Path where the language strings reside - CStdString pathToLanguageFile = path; - CStdString pathToFallbackLanguageFile = path; - CUtil::AddFileToFolder(pathToLanguageFile, "resources", pathToLanguageFile); - CUtil::AddFileToFolder(pathToFallbackLanguageFile, "resources", pathToFallbackLanguageFile); - CUtil::AddFileToFolder(pathToLanguageFile, "language", pathToLanguageFile); - CUtil::AddFileToFolder(pathToFallbackLanguageFile, "language", pathToFallbackLanguageFile); - CUtil::AddFileToFolder(pathToLanguageFile, g_guiSettings.GetString("locale.language"), pathToLanguageFile); - CUtil::AddFileToFolder(pathToFallbackLanguageFile, "english", pathToFallbackLanguageFile); - CUtil::AddFileToFolder(pathToLanguageFile, "strings.xml", pathToLanguageFile); - CUtil::AddFileToFolder(pathToFallbackLanguageFile, "strings.xml", pathToFallbackLanguageFile); + pDialog->m_url = CURL(path); - // Load language strings temporarily - g_localizeStringsTemp.Load(pathToLanguageFile, pathToFallbackLanguageFile); + // Path where the language strings reside + CStdString pathToLanguageFile = path; + CStdString pathToFallbackLanguageFile = path; + CUtil::AddFileToFolder(pathToLanguageFile, "resources", pathToLanguageFile); + CUtil::AddFileToFolder(pathToFallbackLanguageFile, "resources", pathToFallbackLanguageFile); + CUtil::AddFileToFolder(pathToLanguageFile, "language", pathToLanguageFile); + CUtil::AddFileToFolder(pathToFallbackLanguageFile, "language", pathToFallbackLanguageFile); + CUtil::AddFileToFolder(pathToLanguageFile, g_guiSettings.GetString("locale.language"), pathToLanguageFile); + CUtil::AddFileToFolder(pathToFallbackLanguageFile, "english", pathToFallbackLanguageFile); + CUtil::AddFileToFolder(pathToLanguageFile, "strings.xml", pathToLanguageFile); + CUtil::AddFileToFolder(pathToFallbackLanguageFile, "strings.xml", pathToFallbackLanguageFile); + // Load language strings temporarily + g_localizeStringsTemp.Load(pathToLanguageFile, pathToFallbackLanguageFile); - // Create the dialog - CGUIDialogPluginSettings* pDialog = (CGUIDialogPluginSettings*) g_windowManager.GetWindow(WINDOW_DIALOG_PLUGIN_SETTINGS); - - pDialog->m_strHeading = CUtil::GetFileName(path); - pDialog->m_strHeading.Format("$LOCALIZE[1049] - %s", pDialog->m_strHeading.c_str()); - - CScriptSettings settings; - settings.Load(path); - pDialog->m_settings = settings; - - pDialog->DoModal(); - - if(pDialog->m_bConfirmed) - { - settings = pDialog->m_settings; - settings.Save(); + pDialog->m_strHeading = pDialog->m_url.GetFileNameWithoutPath(); + CUtil::RemoveSlashAtEnd(pDialog->m_strHeading); + pDialog->m_profile.Format("special://profile/script_data/%s", pDialog->m_strHeading.c_str()); + pDialog->m_strHeading.Format("$LOCALIZE[1049] - %s", pDialog->m_strHeading.c_str()); + pDialog->m_addon = settings; + pDialog->m_changed = false; + pDialog->m_saveToDisk = saveToDisk; + pDialog->DoModal(); + if(pDialog->m_bConfirmed) + { + settings = pDialog->m_addon; + settings.Save(); + } + return pDialog->m_bConfirmed; } - - return pDialog->m_bConfirmed; + return false; } bool CGUIDialogPluginSettings::ShowVirtualKeyboard(int iControl) { - int controlId = CONTROL_START_CONTROL; + int controlId = CONTROL_START_SETTING; bool bCloseDialog = false; - TiXmlElement *setting = m_settings.GetPluginRoot()->FirstChildElement("setting"); + const TiXmlElement *setting = GetFirstSetting(); + while (setting) { if (controlId == iControl) @@ -206,11 +254,12 @@ const char *option = setting->Attribute("option"); const char *source = setting->Attribute("source"); CStdString value = m_buttonValues[id]; + CStdString label = GetString(setting->Attribute("label")); if (strcmp(type, "text") == 0) { // get any options - bool bHidden = false; + bool bHidden = false; bool bEncoded = false; if (option) { @@ -220,7 +269,7 @@ if (bEncoded) CUtil::URLDecode(value); - if (CGUIDialogKeyboard::ShowAndGetInput(value, ((CGUIButtonControl*) control)->GetLabel(), true, bHidden)) + if (CGUIDialogKeyboard::ShowAndGetInput(value, label, true, bHidden)) { // if hidden hide input if (bHidden) @@ -235,18 +284,60 @@ CUtil::URLEncode(value); } } - else if (strcmp(type, "integer") == 0 && CGUIDialogNumeric::ShowAndGetNumber(value, ((CGUIButtonControl*) control)->GetLabel())) + else if (strcmp(type, "number") == 0 && CGUIDialogNumeric::ShowAndGetNumber(value, label)) { ((CGUIButtonControl*) control)->SetLabel2(value); } - else if (strcmp(type, "ipaddress") == 0 && CGUIDialogNumeric::ShowAndGetIPAddress(value, ((CGUIButtonControl*) control)->GetLabel())) + else if (strcmp(type, "ipaddress") == 0 && CGUIDialogNumeric::ShowAndGetIPAddress(value, label)) { ((CGUIButtonControl*) control)->SetLabel2(value); } - else if (strcmpi(type, "video") == 0 || strcmpi(type, "music") == 0 || - strcmpi(type, "pictures") == 0 || strcmpi(type, "programs") == 0 || - strcmpi(type, "folder") == 0 || strcmpi(type, "files") == 0) + else if (strcmpi(type, "select") == 0) { + CGUIDialogSelect *pDlg = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT); + if (pDlg) + { + pDlg->SetHeading(label.c_str()); + pDlg->Reset(); + + vector valuesVec; + if (setting->Attribute("values")) + CUtil::Tokenize(setting->Attribute("values"), valuesVec, "|"); + else if (setting->Attribute("lvalues")) + { // localize + CUtil::Tokenize(setting->Attribute("lvalues"), valuesVec, "|"); + for (unsigned int i = 0; i < valuesVec.size(); i++) + { + CStdString value = g_localizeStringsTemp.Get(atoi(valuesVec[i])); + if (value.IsEmpty()) + value = g_localizeStrings.Get(atoi(valuesVec[i])); + valuesVec[i] = value; + } + } + else if (source) + { + valuesVec = GetFileEnumValues(source, setting->Attribute("mask"), setting->Attribute("option")); + } + + for (unsigned int i = 0; i < valuesVec.size(); i++) + { + pDlg->Add(valuesVec[i]); + if (valuesVec[i].Equals(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) + { + value = valuesVec[iSelected]; + ((CGUIButtonControl*) control)->SetLabel2(value); + } + } + } + else if (strcmpi(type, "audio") == 0 || strcmpi(type, "video") == 0 || + strcmpi(type, "image") == 0 || strcmpi(type, "executable") == 0 || + strcmpi(type, "file") == 0 || strcmpi(type, "folder") == 0) + { // setup the shares VECSOURCES *shares = NULL; if (!source || strcmpi(source, "") == 0) @@ -254,9 +345,10 @@ else shares = g_settings.GetSourcesFromType(source); - VECSOURCES localShares, networkShares; + VECSOURCES localShares; if (!shares) { + VECSOURCES networkShares; g_mediaManager.GetLocalDrives(localShares); if (!source || strcmpi(source, "local") != 0) g_mediaManager.GetNetworkLocations(networkShares); @@ -271,12 +363,12 @@ if (option) bWriteOnly = (strcmpi(option, "writeable") == 0); - if (CGUIDialogFileBrowser::ShowAndGetDirectory(*shares, ((CGUIButtonControl*) control)->GetLabel(), value, bWriteOnly)) + if (CGUIDialogFileBrowser::ShowAndGetDirectory(*shares, label, value, bWriteOnly)) ((CGUIButtonControl*) control)->SetLabel2(value); } - else if (strcmpi(type, "pictures") == 0) + else if (strcmpi(type, "image") == 0) { - if (CGUIDialogFileBrowser::ShowAndGetImage(*shares, ((CGUIButtonControl*) control)->GetLabel(), value)) + if (CGUIDialogFileBrowser::ShowAndGetImage(*shares, label, value)) ((CGUIButtonControl*) control)->SetLabel2(value); } else @@ -284,21 +376,22 @@ // set the proper mask CStdString strMask; if (setting->Attribute("mask")) + { strMask = setting->Attribute("mask"); + // convert mask qualifiers + strMask.Replace("$AUDIO", g_stSettings.m_musicExtensions); + strMask.Replace("$VIDEO", g_stSettings.m_videoExtensions); + strMask.Replace("$IMAGE", g_stSettings.m_pictureExtensions); + strMask.Replace("$EXECUTABLE", ".xbe|.py"); + } else { if (strcmpi(type, "video") == 0) strMask = g_stSettings.m_videoExtensions; - else if (strcmpi(type, "music") == 0) + else if (strcmpi(type, "audio") == 0) strMask = g_stSettings.m_musicExtensions; - else if (strcmpi(type, "programs") == 0) -#if defined(_XBOX) + else if (strcmpi(type, "executable") == 0) strMask = ".xbe|.py"; -#elif defined(_WIN32_WINNT) - strMask = ".exe|.bat|.cmd|.py"; -#else - strMask = ""; -#endif } // get any options @@ -306,27 +399,61 @@ bool bUseFileDirectories = false; if (option) { - bUseThumbs = (strcmpi(option, "usethumbs") == 0 || strcmpi(option, "usethumbs|treatasfolder") == 0); - bUseFileDirectories = (strcmpi(option, "treatasfolder") == 0 || strcmpi(option, "usethumbs|treatasfolder") == 0); + vector options; + StringUtils::SplitString(option, "|", options); + bUseThumbs = find(options.begin(), options.end(), "usethumbs") != options.end(); + bUseFileDirectories = find(options.begin(), options.end(), "treatasfolder") != options.end(); } - if (CGUIDialogFileBrowser::ShowAndGetFile(*shares, strMask, ((CGUIButtonControl*) control)->GetLabel(), value)) + if (CGUIDialogFileBrowser::ShowAndGetFile(*shares, strMask, label, value)) ((CGUIButtonControl*) control)->SetLabel2(value); } } else if (strcmpi(type, "action") == 0) { - if (setting->Attribute("default")) + CStdString action = setting->Attribute("action"); + if (!action.IsEmpty()) { - CStdString action = setting->Attribute("default"); CStdString url = m_url.Get(); - // replace $CWD with the url of plugin + CUtil::RemoveSlashAtEnd(url); + // replace $CWD with the url of plugin/script action.Replace("$CWD", url); + action.Replace("$ID", url); + action.Replace("/", "\\"); if (option) bCloseDialog = (strcmpi(option, "close") == 0); g_applicationMessenger.ExecBuiltIn(action); } } + else if (strcmp(type, "date") == 0) + { + CDateTime date; + if (!value.IsEmpty()) + date.SetFromDBDate(value); + SYSTEMTIME timedate; + date.GetAsSystemTime(timedate); + if(CGUIDialogNumeric::ShowAndGetDate(timedate, label)) + { + date = timedate; + value = date.GetAsDBDate(); + ((CGUIButtonControl*) control)->SetLabel2(value); + } + } + else if (strcmp(type, "time") == 0) + { + SYSTEMTIME timedate; + if (!value.IsEmpty()) + { + // assumes HH:MM + timedate.wHour = atoi(value.Left(2)); + timedate.wMinute = atoi(value.Right(2)); + } + if (CGUIDialogNumeric::ShowAndGetTime(timedate, label)) + { + value.Format("%02d:%02d", timedate.wHour, timedate.wMinute); + ((CGUIButtonControl*) control)->SetLabel2(value); + } + } m_buttonValues[id] = value; break; } @@ -338,24 +465,19 @@ return bCloseDialog; } -// Go over all the settings and set their values according to the values of the GUI components -bool CGUIDialogPluginSettings::SaveSettings(void) +void CGUIDialogPluginSettings::UpdateFromControls() { - // Retrieve all the values from the GUI components and put them in the model - int controlId = CONTROL_START_CONTROL; - TiXmlElement *setting = m_settings.GetPluginRoot()->FirstChildElement("setting"); + int controlID = CONTROL_START_SETTING; + const TiXmlElement *setting = GetFirstSetting(); + while (setting) { - CStdString id; - if (setting->Attribute("id")) - id = setting->Attribute("id"); + CStdString id = setting->Attribute("id"); const char *type = setting->Attribute("type"); + const CGUIControl* control = GetControl(controlID++); - // skip type "lsep", it is not a required control - if (strcmpi(type, "lsep") != 0) + if (control) { - const CGUIControl* control = GetControl(controlId); - CStdString value; switch (control->GetControlType()) { @@ -371,21 +493,46 @@ else value.Format("%i", ((CGUISpinControlEx*) control)->GetValue()); break; + case CGUIControl::GUICONTROL_SETTINGS_SLIDER: + value.Format("%f", ((CGUISettingsSliderControl *)control)->GetFloatValue()); + break; default: break; } - m_settings.Set(id, value); + m_settings[id] = value; } + setting = setting->NextSiblingElement("setting"); - controlId++; } - return true; } +void CGUIDialogPluginSettings::SaveSettings(void) +{ + UpdateFromControls(); + + for (map::iterator i = m_settings.begin(); i != m_settings.end(); ++i) + m_addon.Set(i->first, i->second); + + if (m_saveToDisk) + m_addon.Save(); +} + +void CGUIDialogPluginSettings::FreeSections() +{ + CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_SETTINGS_AREA); + if (group) + { + group->FreeResources(); + group->ClearAll(); + } + m_settings.clear(); + m_buttonValues.clear(); +} + void CGUIDialogPluginSettings::FreeControls() { // clear the category group - CGUIControlGroupList *control = (CGUIControlGroupList *)GetControl(CONTROL_AREA); + CGUIControlGroupList *control = (CGUIControlGroupList *)GetControl(CONTROL_SETTINGS_AREA); if (control) { control->FreeResources(); @@ -393,45 +540,88 @@ } } +void CGUIDialogPluginSettings::CreateSections() +{ + CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_SECTION_AREA); + CGUIButtonControl *originalButton = (CGUIButtonControl *)GetControl(CONTROL_DEFAULT_SECTION_BUTTON); + + if (originalButton) + originalButton->SetVisible(false); + + // clear the category group + FreeSections(); + + // grab our categories + const TiXmlElement *category = m_addon.GetPluginRoot()->FirstChildElement("category"); + if (!category) // add a default one... + category = m_addon.GetPluginRoot(); + + int buttonID = CONTROL_START_SECTION; + while (category) + { // add a category + CGUIButtonControl *button = originalButton ? originalButton->Clone() : NULL; + + CStdString label = GetString(category->Attribute("label")); + if (label.IsEmpty()) + label = g_localizeStrings.Get(128); + + // 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 char *id = setting->Attribute("id"); + if (id) + m_settings[id] = m_addon.Get(id); + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); + } + m_totalSections = buttonID - CONTROL_START_SECTION; +} + void CGUIDialogPluginSettings::CreateControls() { + FreeControls(); + CGUISpinControlEx *pOriginalSpin = (CGUISpinControlEx*)GetControl(CONTROL_DEFAULT_SPIN); CGUIRadioButtonControl *pOriginalRadioButton = (CGUIRadioButtonControl *)GetControl(CONTROL_DEFAULT_RADIOBUTTON); CGUIButtonControl *pOriginalButton = (CGUIButtonControl *)GetControl(CONTROL_DEFAULT_BUTTON); CGUIImage *pOriginalImage = (CGUIImage *)GetControl(CONTROL_DEFAULT_SEPARATOR); CGUILabelControl *pOriginalLabel = (CGUILabelControl *)GetControl(CONTROL_DEFAULT_LABEL_SEPARATOR); + CGUISettingsSliderControl *pOriginalSlider = (CGUISettingsSliderControl *)GetControl(CONTROL_DEFAULT_SLIDER); - if (!pOriginalSpin || !pOriginalRadioButton || !pOriginalButton || !pOriginalImage) + if (!pOriginalSpin || !pOriginalRadioButton || !pOriginalButton || !pOriginalImage + || !pOriginalLabel || !pOriginalSlider) return; pOriginalSpin->SetVisible(false); pOriginalRadioButton->SetVisible(false); pOriginalButton->SetVisible(false); pOriginalImage->SetVisible(false); - if (pOriginalLabel) - pOriginalLabel->SetVisible(false); + pOriginalLabel->SetVisible(false); + pOriginalSlider->SetVisible(false); // clear the category group - CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_AREA); + CGUIControlGroupList *group = (CGUIControlGroupList *)GetControl(CONTROL_SETTINGS_AREA); if (!group) return; // set our dialog heading SET_CONTROL_LABEL(CONTROL_HEADING_LABEL, m_strHeading); - // Create our base path, used for type "fileenum" settings - CStdString basepath; - if (m_url.GetProtocol().Equals("plugin")) - { // plugins need to create path - basepath = CUtil::AddFileToFolder("special://home/plugins/", m_url.GetHostName()); - basepath = CUtil::AddFileToFolder(basepath, m_url.GetFileName()); - } - else - basepath = m_url.Get(); + CGUIControl* pControl = NULL; + int controlId = CONTROL_START_SETTING; + const TiXmlElement *setting = GetFirstSetting(); - CGUIControl* pControl = NULL; - int controlId = CONTROL_START_CONTROL; - TiXmlElement *setting = m_settings.GetPluginRoot()->FirstChildElement("setting"); while (setting) { const char *type = setting->Attribute("type"); @@ -445,11 +635,8 @@ CStdString entries; if (setting->Attribute("entries")) entries = setting->Attribute("entries"); - CStdString label; - if (setting->Attribute("label") && atoi(setting->Attribute("label")) > 0) - label.Format("$LOCALIZE[%s]", setting->Attribute("label")); - else - label = setting->Attribute("label"); + const char *subsetting = setting->Attribute("subsetting"); + CStdString label = GetString(setting->Attribute("label"), subsetting && 0 == strcmpi(subsetting, "true")); bool bSort=false; const char *sort = setting->Attribute("sort"); @@ -459,10 +646,12 @@ if (type) { if (strcmpi(type, "text") == 0 || strcmpi(type, "ipaddress") == 0 || - strcmpi(type, "integer") == 0 || strcmpi(type, "video") == 0 || - strcmpi(type, "music") == 0 || strcmpi(type, "pictures") == 0 || - strcmpi(type, "folder") == 0 || strcmpi(type, "programs") == 0 || - strcmpi(type, "files") == 0 || strcmpi(type, "action") == 0) + strcmpi(type, "number") == 0 ||strcmpi(type, "video") == 0 || + strcmpi(type, "audio") == 0 || strcmpi(type, "image") == 0 || + strcmpi(type, "folder") == 0 || strcmpi(type, "executable") == 0 || + strcmpi(type, "file") == 0 || strcmpi(type, "action") == 0 || + strcmpi(type, "date") == 0 || strcmpi(type, "time") == 0 || + strcmpi(type, "select") == 0) { pControl = new CGUIButtonControl(*pOriginalButton); if (!pControl) return; @@ -470,8 +659,10 @@ ((CGUIButtonControl *)pControl)->SetLabel(label); if (id) { - m_buttonValues[id] = m_settings.Get(id); - CStdString value=m_settings.Get(id); + CStdString tmpS = m_settings[id]; + + CStdString value = m_settings[id]; + m_buttonValues[id] = value; // get any option to test for hidden const char *option = setting->Attribute("option"); if (option && (strstr(option, "urlencoded"))) @@ -485,13 +676,15 @@ else ((CGUIButtonControl *)pControl)->SetLabel2(value); } + else + ((CGUIButtonControl *)pControl)->SetLabel2(GetString(setting->Attribute("default"))); } else if (strcmpi(type, "bool") == 0) { pControl = new CGUIRadioButtonControl(*pOriginalRadioButton); if (!pControl) return; ((CGUIRadioButtonControl *)pControl)->SetLabel(label); - ((CGUIRadioButtonControl *)pControl)->SetSelected(m_settings.Get(id) == "true"); + ((CGUIRadioButtonControl *)pControl)->SetSelected(m_settings[id] == "true"); } else if (strcmpi(type, "enum") == 0 || strcmpi(type, "labelenum") == 0) { @@ -504,6 +697,14 @@ if (!lvalues.IsEmpty()) CUtil::Tokenize(lvalues, valuesVec, "|"); + else if (values.Equals("$HOURS")) + { + for (unsigned int i = 0; i < 24; i++) + { + CDateTime time(0, 0, 0, i, 0, 0); + valuesVec.push_back(g_infoManager.LocalizeTime(time, TIME_FORMAT_HH_MM_XX)); + } + } else CUtil::Tokenize(values, valuesVec, "|"); if (!entries.IsEmpty()) @@ -529,10 +730,10 @@ } if (strcmpi(type, "labelenum") == 0) { // need to run through all our settings and find the one that matches - ((CGUISpinControlEx*) pControl)->SetValueFromLabel(m_settings.Get(id)); + ((CGUISpinControlEx*) pControl)->SetValueFromLabel(m_settings[id]); } else - ((CGUISpinControlEx*) pControl)->SetValue(atoi(m_settings.Get(id))); + ((CGUISpinControlEx*) pControl)->SetValue(atoi(m_settings[id])); } else if (strcmpi(type, "fileenum") == 0) @@ -540,39 +741,51 @@ pControl = new CGUISpinControlEx(*pOriginalSpin); if (!pControl) return; ((CGUISpinControlEx *)pControl)->SetText(label); + ((CGUISpinControlEx *)pControl)->SetFloatValue(1.0f); - //find Folders... - CFileItemList items; - CStdString enumpath; - CUtil::AddFileToFolder(basepath, values, enumpath); - CStdString mask; - if (setting->Attribute("mask")) - mask = setting->Attribute("mask"); - if (!mask.IsEmpty()) - CDirectory::GetDirectory(enumpath, items, mask); - else - CDirectory::GetDirectory(enumpath, items); + vector items = GetFileEnumValues(values, setting->Attribute("mask"), setting->Attribute("option")); + for (unsigned int i = 0; i < items.size(); ++i) + { + ((CGUISpinControlEx *)pControl)->AddLabel(items[i], i); + if (items[i].Equals(m_settings[id])) + ((CGUISpinControlEx *)pControl)->SetValue(i); + } + } + else if (strcmpi(type, "slider") == 0) + { + pControl = new CGUISettingsSliderControl(*pOriginalSlider); + if (!pControl) return; + ((CGUISettingsSliderControl *)pControl)->SetText(label); - int iItem = 0; - for (int i = 0; i < items.Size(); ++i) + float fMin = 0.0f; + float fMax = 100.0f; + float fInc = 1.0f; + vector range; + StringUtils::SplitString(setting->Attribute("range"), ",", range); + if (range.size() > 1) { - CFileItemPtr pItem = items[i]; - if ((mask.Equals("/") && pItem->m_bIsFolder) || !pItem->m_bIsFolder) + fMin = (float)atof(range[0]); + if (range.size() > 2) { - ((CGUISpinControlEx *)pControl)->AddLabel(pItem->GetLabel(), iItem); - if (pItem->GetLabel().Equals(m_settings.Get(id))) - ((CGUISpinControlEx *)pControl)->SetValue(iItem); - iItem++; + fMax = (float)atof(range[2]); + fInc = (float)atof(range[1]); } + else + fMax = (float)atof(range[1]); } + + ((CGUISettingsSliderControl *)pControl)->SetType(SPIN_CONTROL_TYPE_FLOAT); + ((CGUISettingsSliderControl *)pControl)->SetFloatRange(fMin, fMax); + ((CGUISettingsSliderControl *)pControl)->SetFloatInterval(fInc); + ((CGUISettingsSliderControl *)pControl)->SetFloatValue((float)atof(m_settings[id])); } - else if (strcmpi(type, "lsep") == 0 && pOriginalLabel) + else if (strcmpi(type, "lsep") == 0) { pControl = new CGUILabelControl(*pOriginalLabel); if (pControl) ((CGUILabelControl *)pControl)->SetLabel(label); } - else if ((strcmpi(type, "sep") == 0 || strcmpi(type, "lsep") == 0) && pOriginalImage) + else if (strcmpi(type, "sep") == 0) pControl = new CGUIImage(*pOriginalImage); } @@ -592,11 +805,49 @@ EnableControls(); } +vector CGUIDialogPluginSettings::GetFileEnumValues(const CStdString &path, const CStdString &mask, const CStdString &options) const +{ + // Create our base path, used for type "fileenum" settings + // replace $PROFILE with the profile path of the plugin/script + CStdString fullPath = path; + CStdString url = m_url.Get(); + url.Replace("plugin://", "special://home/plugins/"); + + if (fullPath.Find("$PROFILE") >= 0) + { + fullPath.Replace("$PROFILE", m_profile); + } + else + fullPath = CUtil::AddFileToFolder(url, path); + + bool hideExtensions = (options.CompareNoCase("hideext") == 0); + // fetch directory + CFileItemList items; + if (!mask.IsEmpty()) + CDirectory::GetDirectory(fullPath, items, mask); + else + CDirectory::GetDirectory(fullPath, items); + + vector values; + for (int i = 0; i < items.Size(); ++i) + { + CFileItemPtr pItem = items[i]; + if ((mask.Equals("/") && 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 CGUIDialogPluginSettings::EnableControls() { - int controlId = CONTROL_START_CONTROL; - TiXmlElement *setting = m_settings.GetPluginRoot()->FirstChildElement("setting"); + int controlId = CONTROL_START_SETTING; + const TiXmlElement *setting = GetFirstSetting(); + while (setting) { const CGUIControl* control = GetControl(controlId); @@ -651,7 +902,10 @@ value = ((CGUIRadioButtonControl*) control2)->IsSelected() ? "true" : "false"; break; case CGUIControl::GUICONTROL_SPINEX: - value.Format("%i", ((CGUISpinControlEx*) control2)->GetValue()); + if (((CGUISpinControlEx*) control2)->GetFloatValue() > 0.0f) + value = ((CGUISpinControlEx*) control2)->GetLabel(); + else + value.Format("%i", ((CGUISpinControlEx*) control2)->GetValue()); break; default: break; @@ -709,55 +963,124 @@ return false; } +CStdString CGUIDialogPluginSettings::GetString(const char *value, bool subSetting) const +{ + if (!value) + return ""; + // Replace all $ADDON[id number] with the real string + CStdString label = ReplaceAddonStrings(value); + + int id = atoi(label); + CStdString prefix(subSetting ? "- " : ""); + if (id > 0) + return prefix + g_localizeStringsTemp.Get(id); + return prefix + label; +} + +CStdString CGUIDialogPluginSettings::ReplaceAddonStrings(const CStdString &label) +{ + CStdString work(label); + //FIXME why not use RE here? + // Replace all $ADDON[id number] with the real string + int pos1 = work.Find("$ADDON["); + while (pos1 >= 0) + { + int pos2 = StringUtils::FindEndBracket(work, '[', ']', pos1 + 7); + if (pos2 > pos1) + { + CStdString left = work.Left(pos1); + CStdString right = work.Mid(pos2 + 1); + int length = work.Find(" ", pos1 + 7) - (pos1 + 7); + CStdString id = work.substr(pos1+7, length); + int stringid = atoi(work.substr(pos1+7+id.length()+1, 5).c_str()); + CStdString replace = g_localizeStringsTemp.Get(stringid); + work = left + replace + right; + } + else + { + CLog::Log(LOGERROR, "Error parsing label - missing ']'"); + return ""; + } + pos1 = work.Find("$ADDON[", pos1); + } + return work; +} + // Go over all the settings and set their default values void CGUIDialogPluginSettings::SetDefaults() { - int controlId = CONTROL_START_CONTROL; - TiXmlElement *setting = m_settings.GetPluginRoot()->FirstChildElement("setting"); - while (setting) + const TiXmlElement *category = m_addon.GetPluginRoot()->FirstChildElement("category"); + if (!category) // add a default one... + category = m_addon.GetPluginRoot(); + + while (category) { - const CGUIControl* control = GetControl(controlId); - if (control) + const TiXmlElement *setting = category->FirstChildElement("setting"); + while (setting) { - CStdString value; - switch (control->GetControlType()) + const char *id = setting->Attribute("id"); + const char *type = setting->Attribute("type"); + const char *value = setting->Attribute("default"); + if (id) { - case CGUIControl::GUICONTROL_BUTTON: - if (setting->Attribute("default") && setting->Attribute("id")) - ((CGUIButtonControl*) control)->SetLabel2(setting->Attribute("default")); - else - ((CGUIButtonControl*) control)->SetLabel2(""); - break; - case CGUIControl::GUICONTROL_RADIO: - if (setting->Attribute("default")) - ((CGUIRadioButtonControl*) control)->SetSelected(strcmpi(setting->Attribute("default"), "true") == 0); - else - ((CGUIRadioButtonControl*) control)->SetSelected(false); - break; - case CGUIControl::GUICONTROL_SPINEX: - { - if (setting->Attribute("default")) - { - if (strcmpi(setting->Attribute("type"), "fileenum") == 0 || strcmpi(setting->Attribute("type"), "labelenum") == 0) - { // need to run through all our settings and find the one that matches - ((CGUISpinControlEx*) control)->SetValueFromLabel(setting->Attribute("default")); - } - else - ((CGUISpinControlEx*) control)->SetValue(atoi(setting->Attribute("default"))); - } - else - ((CGUISpinControlEx*) control)->SetValue(0); - } - break; - default: - break; + if (value) + m_settings[id] = value; + else if (0 == strcmpi(type, "bool")) + m_settings[id] = "false"; + else if (0 == strcmpi(type, "slider") || 0 == strcmpi(type, "enum")) + m_settings[id] = "0"; + else if (0 != strcmpi(type, "action")) + m_settings[id] = ""; } + setting = setting->NextSiblingElement("setting"); } - setting = setting->NextSiblingElement("setting"); - controlId++; + category = category->NextSiblingElement("category"); } - EnableControls(); + CreateControls(); } +const TiXmlElement *CGUIDialogPluginSettings::GetFirstSetting() +{ + const TiXmlElement *category = m_addon.GetPluginRoot()->FirstChildElement("category"); + if (!category) + category = m_addon.GetPluginRoot(); + for (unsigned int i = 0; i < m_currentSection && category; i++) + category = category->NextSiblingElement("category"); + if (category) + return category->FirstChildElement("setting"); + return NULL; +} + +void CGUIDialogPluginSettings::Render() +{ + // 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::Render(); + if (alphaFaded && m_bRunning) // dialog may close during Render() + { + control->SetFocus(false); + if (control->GetControlType() == CGUIControl::GUICONTROL_BUTTON) + ((CGUIButtonControl *)control)->SetAlpha(0xFF); + else + ((CGUIButtonControl *)control)->SetSelected(false); + } +} + CURL CGUIDialogPluginSettings::m_url; Index: xbmc/GUIDialogPluginSettings.h =================================================================== --- xbmc/GUIDialogPluginSettings.h (revision 30503) +++ xbmc/GUIDialogPluginSettings.h (working copy) @@ -33,27 +33,63 @@ CGUIDialogPluginSettings(void); virtual ~CGUIDialogPluginSettings(void); virtual bool OnMessage(CGUIMessage& message); - static bool ShowAndGetInput(CURL& url); - static bool ShowAndGetInput(SScraperInfo& info); - static bool ShowAndGetInput(CStdString& path); + /*! \brief Show the addon settings dialog, allowing the user to configure an addon + \param addon the addon to configure + \param saveToDisk whether the changes should be saved to disk or just made local to the addon. Defaults to true + \return true if settings were changed and the dialog confirmed, false otherwise. + */ + static bool ShowAndGetInput(CURL& url, bool saveToDisk = true); + static bool ShowAndGetInput(SScraperInfo& info, bool saveToDisk = true); + static bool ShowAndGetInput(CStdString& path, bool saveToDisk = true); + virtual void Render(); + static CStdString ReplaceAddonStrings(const CStdString &label); protected: virtual void OnInitWindow(); private: + /*! \brief return a (localized) addon string. + \param value either a character string (which is used directly) or a number to lookup in the addons strings.xml + \param subsetting whether the character string should be prefixed by "- ", defaults to false + \return the localized addon string + */ + CStdString GetString(const char *value, bool subSetting = false) const; + + /*! \brief return a the values for a fileenum setting + \param path the path to use for files + \param mask the mask to use + \param options any options, such as "hideext" to hide extensions + \return the filenames in the path that match the mask + */ + std::vector GetFileEnumValues(const CStdString &path, const CStdString &mask, const CStdString &options) const; + + void CreateSections(); + void FreeSections(); void CreateControls(); void FreeControls(); + void UpdateFromControls(); void EnableControls(); void SetDefaults(); bool GetCondition(const CStdString &condition, const int controlId); - - bool SaveSettings(void); + + void SaveSettings(void); bool ShowVirtualKeyboard(int iControl); - static CURL m_url; bool TranslateSingleString(const CStdString &strCondition, std::vector &enableVec); - CBasicSettings m_settings; + + const TiXmlElement *GetFirstSetting(); + + CBasicSettings m_addon; CStdString m_strHeading; + CStdString m_profile; std::map m_buttonValues; + bool m_changed; + bool m_saveToDisk; // whether the addon settings should be saved to disk or just stored locally in the addon + + unsigned int m_currentSection; + unsigned int m_totalSections; + + std::map m_settings; // local storage of values + static CURL m_url; }; #endif Index: xbmc/PluginSettings.cpp =================================================================== --- xbmc/PluginSettings.cpp (revision 30503) +++ xbmc/PluginSettings.cpp (working copy) @@ -107,15 +107,24 @@ if (m_pluginXmlDoc.RootElement()) { - // Try to find the setting in the plugin and return its default value - const TiXmlElement* setting = m_pluginXmlDoc.RootElement()->FirstChildElement("setting"); - while (setting) + const TiXmlElement *category = m_pluginXmlDoc.RootElement()->FirstChildElement("category"); + // this makes grouping optional + if (!category) + category = m_pluginXmlDoc.RootElement(); + + while (category) { - const char *id = setting->Attribute("id"); - if (id && strcmpi(id, key) == 0 && setting->Attribute("default")) - return setting->Attribute("default"); + // Try to find the setting in the plugin and return its default value + const TiXmlElement* setting = category->FirstChildElement("setting"); + while (setting) + { + const char *id = setting->Attribute("id"); + if (id && strcmpi(id, key) == 0 && setting->Attribute("default")) + return setting->Attribute("default"); - setting = setting->NextSiblingElement("setting"); + setting = setting->NextSiblingElement("setting"); + } + category = category->NextSiblingElement("category"); } }