/* * Copyright (C) 2005-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #include "Dialog.h" #include "LanguageHook.h" #include "ListItem.h" #include "ModuleXbmcgui.h" #include "ServiceBroker.h" #include "WindowException.h" #include "dialogs/GUIDialogColorPicker.h" #include "dialogs/GUIDialogContextMenu.h" #include "dialogs/GUIDialogFileBrowser.h" #include "dialogs/GUIDialogKaiToast.h" #include "dialogs/GUIDialogNumeric.h" #include "dialogs/GUIDialogSelect.h" #include "dialogs/GUIDialogTextViewer.h" #include "dialogs/GUIDialogYesNo.h" #include "guilib/GUIComponent.h" #include "guilib/GUIKeyboardFactory.h" #include "guilib/GUIWindowManager.h" #include "messaging/helpers/DialogOKHelper.h" #include "music/dialogs/GUIDialogMusicInfo.h" #include "settings/MediaSourceSettings.h" #include "storage/MediaManager.h" #include "utils/StringUtils.h" #include "utils/Variant.h" #include "utils/XTimeUtils.h" #include "utils/log.h" #include "video/dialogs/GUIDialogVideoInfo.h" using namespace KODI::MESSAGING; #define ACTIVE_WINDOW CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow() namespace XBMCAddon { namespace xbmcgui { Dialog::~Dialog() = default; bool Dialog::yesno(const String& heading, const String& message, const String& nolabel, const String& yeslabel, int autoclose, int defaultbutton) { return yesNoCustomInternal(heading, message, nolabel, yeslabel, emptyString, autoclose, defaultbutton) == 1; } int Dialog::yesnocustom(const String& heading, const String& message, const String& customlabel, const String& nolabel, const String& yeslabel, int autoclose, int defaultbutton) { return yesNoCustomInternal(heading, message, nolabel, yeslabel, customlabel, autoclose, defaultbutton); } int Dialog::yesNoCustomInternal(const String& heading, const String& message, const String& nolabel, const String& yeslabel, const String& customlabel, int autoclose, int defaultbutton) { DelayedCallGuard dcguard(languageHook); CGUIDialogYesNo* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow( WINDOW_DIALOG_YES_NO); if (pDialog == nullptr) throw WindowException("Error: Window is null"); return pDialog->ShowAndGetInput(CVariant{heading}, CVariant{message}, CVariant{nolabel}, CVariant{yeslabel}, CVariant{customlabel}, autoclose, defaultbutton); } bool Dialog::info(const ListItem* item) { DelayedCallGuard dcguard(languageHook); const AddonClass::Ref listitem(item); if (listitem->item->HasVideoInfoTag()) { CGUIDialogVideoInfo::ShowFor(*listitem->item); return true; } else if (listitem->item->HasMusicInfoTag()) { CGUIDialogMusicInfo::ShowFor(listitem->item.get()); return true; } return false; } int Dialog::contextmenu(const std::vector& list) { DelayedCallGuard dcguard(languageHook); CGUIDialogContextMenu* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_CONTEXT_MENU); if (pDialog == NULL) throw WindowException("Error: Window is NULL, this is not possible :-)"); CContextButtons choices; for(unsigned int i = 0; i < list.size(); i++) { choices.Add(i, list[i]); } return pDialog->Show(choices); } int Dialog::select(const String& heading, const std::vector > & list, int autoclose, int preselect, bool useDetails) { DelayedCallGuard dcguard(languageHook); CGUIDialogSelect* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_SELECT); if (pDialog == NULL) throw WindowException("Error: Window is NULL, this is not possible :-)"); pDialog->Reset(); if (!heading.empty()) pDialog->SetHeading(CVariant{heading}); for (const auto& item : list) { AddonClass::Ref ritem = item.which() == XBMCAddon::first ? ListItem::fromString(item.former()) : AddonClass::Ref(item.later()); CFileItemPtr& fileItem = ritem->item; pDialog->Add(*fileItem); } if (preselect > -1) pDialog->SetSelected(preselect); if (autoclose > 0) pDialog->SetAutoClose(autoclose); pDialog->SetUseDetails(useDetails); pDialog->Open(); return pDialog->GetSelectedItem(); } std::unique_ptr> Dialog::multiselect(const String& heading, const std::vector > & options, int autoclose, const std::vector& preselect, bool useDetails) { DelayedCallGuard dcguard(languageHook); CGUIDialogSelect* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_SELECT); if (pDialog == nullptr) throw WindowException("Error: Window is NULL"); pDialog->Reset(); pDialog->SetMultiSelection(true); pDialog->SetHeading(CVariant{heading}); for (const auto& item : options) { AddonClass::Ref ritem = item.which() == XBMCAddon::first ? ListItem::fromString(item.former()) : AddonClass::Ref(item.later()); CFileItemPtr& fileItem = ritem->item; pDialog->Add(*fileItem); } if (autoclose > 0) pDialog->SetAutoClose(autoclose); pDialog->SetUseDetails(useDetails); pDialog->SetSelected(preselect); pDialog->Open(); if (pDialog->IsConfirmed()) return std::unique_ptr>(new std::vector(pDialog->GetSelectedItems())); else return std::unique_ptr>(); } bool Dialog::ok(const String& heading, const String& message) { DelayedCallGuard dcguard(languageHook); return HELPERS::ShowOKDialogText(CVariant{heading}, CVariant{message}); } void Dialog::textviewer(const String& heading, const String& text, bool usemono) { DelayedCallGuard dcguard(languageHook); CGUIDialogTextViewer* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_TEXT_VIEWER); if (pDialog == NULL) throw WindowException("Error: Window is NULL, this is not possible :-)"); if (!heading.empty()) pDialog->SetHeading(heading); if (!text.empty()) pDialog->SetText(text); pDialog->UseMonoFont(usemono); pDialog->Open(); } Alternative > Dialog::browse(int type, const String& heading, const String& s_shares, const String& maskparam, bool useThumbs, bool useFileDirectories, const String& defaultt, bool enableMultiple) { Alternative > ret; if (enableMultiple) ret.later() = browseMultiple(type,heading,s_shares,maskparam,useThumbs,useFileDirectories,defaultt); else ret.former() = browseSingle(type,heading,s_shares,maskparam,useThumbs,useFileDirectories,defaultt); return ret; } String Dialog::browseSingle(int type, const String& heading, const String& s_shares, const String& maskparam, bool useThumbs, bool useFileDirectories, const String& defaultt ) { DelayedCallGuard dcguard(languageHook); std::string value; std::string mask = maskparam; VECSOURCES *shares = CMediaSourceSettings::GetInstance().GetSources(s_shares); VECSOURCES localShares; if (!shares) { CServiceBroker::GetMediaManager().GetLocalDrives(localShares); if (StringUtils::CompareNoCase(s_shares, "local") != 0) CServiceBroker::GetMediaManager().GetNetworkLocations(localShares); } else // always append local drives { localShares = *shares; CServiceBroker::GetMediaManager().GetLocalDrives(localShares); } if (useFileDirectories && !maskparam.empty()) mask += "|.rar|.zip"; value = defaultt; if (type == 1) CGUIDialogFileBrowser::ShowAndGetFile(localShares, mask, heading, value, useThumbs, useFileDirectories); else if (type == 2) CGUIDialogFileBrowser::ShowAndGetImage(localShares, heading, value); else CGUIDialogFileBrowser::ShowAndGetDirectory(localShares, heading, value, type != 0); return value; } std::vector Dialog::browseMultiple(int type, const String& heading, const String& s_shares, const String& mask, bool useThumbs, bool useFileDirectories, const String& defaultt ) { DelayedCallGuard dcguard(languageHook); VECSOURCES *shares = CMediaSourceSettings::GetInstance().GetSources(s_shares); std::vector valuelist; String lmask = mask; VECSOURCES localShares; if (!shares) { CServiceBroker::GetMediaManager().GetLocalDrives(localShares); if (StringUtils::CompareNoCase(s_shares, "local") != 0) CServiceBroker::GetMediaManager().GetNetworkLocations(localShares); } else // always append local drives { localShares = *shares; CServiceBroker::GetMediaManager().GetLocalDrives(localShares); } if (useFileDirectories && !lmask.empty()) lmask += "|.rar|.zip"; if (type == 1) CGUIDialogFileBrowser::ShowAndGetFileList(localShares, lmask, heading, valuelist, useThumbs, useFileDirectories); else if (type == 2) CGUIDialogFileBrowser::ShowAndGetImageList(localShares, heading, valuelist); else throw WindowException("Error: Cannot retrieve multiple directories using browse %s is NULL.",s_shares.c_str()); return valuelist; } String Dialog::numeric(int inputtype, const String& heading, const String& defaultt, bool bHiddenInput) { DelayedCallGuard dcguard(languageHook); std::string value; KODI::TIME::SystemTime timedate; KODI::TIME::GetLocalTime(&timedate); if (!heading.empty()) { if (inputtype == 1) { if (!defaultt.empty() && defaultt.size() == 10) { const std::string& sDefault = defaultt; timedate.day = atoi(sDefault.substr(0, 2).c_str()); timedate.month = atoi(sDefault.substr(3, 4).c_str()); timedate.year = atoi(sDefault.substr(sDefault.size() - 4).c_str()); } if (CGUIDialogNumeric::ShowAndGetDate(timedate, heading)) value = StringUtils::Format("{:2}/{:2}/{:4}", timedate.day, timedate.month, timedate.year); else return emptyString; } else if (inputtype == 2) { if (!defaultt.empty() && defaultt.size() == 5) { const std::string& sDefault = defaultt; timedate.hour = atoi(sDefault.substr(0, 2).c_str()); timedate.minute = atoi(sDefault.substr(3, 2).c_str()); } if (CGUIDialogNumeric::ShowAndGetTime(timedate, heading)) value = StringUtils::Format("{:2}:{:02}", timedate.hour, timedate.minute); else return emptyString; } else if (inputtype == 3) { value = defaultt; if (!CGUIDialogNumeric::ShowAndGetIPAddress(value, heading)) return emptyString; } else if (inputtype == 4) { value = defaultt; if (!CGUIDialogNumeric::ShowAndVerifyNewPassword(value)) return emptyString; } else { value = defaultt; if (!CGUIDialogNumeric::ShowAndGetNumber(value, heading, 0, bHiddenInput)) return emptyString; } } return value; } void Dialog::notification(const String& heading, const String& message, const String& icon, int time, bool sound) { DelayedCallGuard dcguard(languageHook); std::string strIcon = getNOTIFICATION_INFO(); int iTime = TOAST_DISPLAY_TIME; if (time > 0) iTime = time; if (!icon.empty()) strIcon = icon; if (strIcon == getNOTIFICATION_INFO()) CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, heading, message, iTime, sound); else if (strIcon == getNOTIFICATION_WARNING()) CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning, heading, message, iTime, sound); else if (strIcon == getNOTIFICATION_ERROR()) CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, heading, message, iTime, sound); else CGUIDialogKaiToast::QueueNotification(strIcon, heading, message, iTime, sound); } String Dialog::input(const String& heading, const String& defaultt, int type, int option, int autoclose) { DelayedCallGuard dcguard(languageHook); std::string value(defaultt); KODI::TIME::SystemTime timedate; KODI::TIME::GetLocalTime(&timedate); switch (type) { case INPUT_ALPHANUM: { bool bHiddenInput = (option & ALPHANUM_HIDE_INPUT) == ALPHANUM_HIDE_INPUT; if (!CGUIKeyboardFactory::ShowAndGetInput(value, CVariant{heading}, true, bHiddenInput, autoclose)) value = emptyString; } break; case INPUT_NUMERIC: { if (!CGUIDialogNumeric::ShowAndGetNumber(value, heading, autoclose)) value = emptyString; } break; case INPUT_DATE: { if (!defaultt.empty() && defaultt.size() == 10) { const std::string& sDefault = defaultt; timedate.day = atoi(sDefault.substr(0, 2).c_str()); timedate.month = atoi(sDefault.substr(3, 4).c_str()); timedate.year = atoi(sDefault.substr(sDefault.size() - 4).c_str()); } if (CGUIDialogNumeric::ShowAndGetDate(timedate, heading)) value = StringUtils::Format("{:2}/{:2}/{:4}", timedate.day, timedate.month, timedate.year); else value = emptyString; } break; case INPUT_TIME: { if (!defaultt.empty() && defaultt.size() == 5) { const std::string& sDefault = defaultt; timedate.hour = atoi(sDefault.substr(0, 2).c_str()); timedate.minute = atoi(sDefault.substr(3, 2).c_str()); } if (CGUIDialogNumeric::ShowAndGetTime(timedate, heading)) value = StringUtils::Format("{:2}:{:02}", timedate.hour, timedate.minute); else value = emptyString; } break; case INPUT_IPADDRESS: { if (!CGUIDialogNumeric::ShowAndGetIPAddress(value, heading)) value = emptyString; } break; case INPUT_PASSWORD: { bool bResult = false; if (option & PASSWORD_VERIFY) bResult = CGUIKeyboardFactory::ShowAndVerifyPassword(value, heading, 0, autoclose) == 0 ? true : false; else bResult = CGUIKeyboardFactory::ShowAndVerifyNewPassword(value, heading, true, autoclose); if (!bResult) value = emptyString; } break; default: value = emptyString; break; } return value; } String Dialog::colorpicker(const String& heading, const String& selectedcolor, const String& colorfile, const std::vector& colorlist) { DelayedCallGuard dcguard(languageHook); std::string value = emptyString; CGUIDialogColorPicker* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow( WINDOW_DIALOG_COLOR_PICKER); if (pDialog == nullptr) throw WindowException("Error: Window is NULL, this is not possible :-)"); pDialog->Reset(); if (!heading.empty()) pDialog->SetHeading(CVariant{heading}); if (!colorlist.empty()) { CFileItemList items; for (const auto& coloritem : colorlist) { items.Add(coloritem->item); } pDialog->SetItems(items); } else if (!colorfile.empty()) pDialog->LoadColors(colorfile); else pDialog->LoadColors(); if (!selectedcolor.empty()) pDialog->SetSelectedColor(selectedcolor); pDialog->Open(); if (pDialog->IsConfirmed()) value = pDialog->GetSelectedColor(); return value; } DialogProgress::~DialogProgress() { XBMC_TRACE; deallocating(); } void DialogProgress::deallocating() { XBMC_TRACE; if (dlg && open) { DelayedCallGuard dg; dlg->Close(); } } void DialogProgress::create(const String& heading, const String& message) { DelayedCallGuard dcguard(languageHook); CGUIDialogProgress* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_PROGRESS); if (pDialog == NULL) throw WindowException("Error: Window is NULL, this is not possible :-)"); dlg = pDialog; open = true; pDialog->SetHeading(CVariant{heading}); if (!message.empty()) pDialog->SetText(CVariant{message}); pDialog->Open(); } void DialogProgress::update(int percent, const String& message) { DelayedCallGuard dcguard(languageHook); CGUIDialogProgress* pDialog = dlg; if (pDialog == NULL) throw WindowException("Dialog not created."); if (percent >= 0 && percent <= 100) { pDialog->SetPercentage(percent); pDialog->ShowProgressBar(true); } else { pDialog->ShowProgressBar(false); } if (!message.empty()) pDialog->SetText(CVariant{message}); } void DialogProgress::close() { DelayedCallGuard dcguard(languageHook); if (dlg == NULL) throw WindowException("Dialog not created."); dlg->Close(); open = false; } bool DialogProgress::iscanceled() { if (dlg == NULL) throw WindowException("Dialog not created."); return dlg->IsCanceled(); } DialogProgressBG::~DialogProgressBG() { XBMC_TRACE; deallocating(); } void DialogProgressBG::deallocating() { XBMC_TRACE; if (dlg && open) { DelayedCallGuard dg; dlg->Close(); } } void DialogProgressBG::create(const String& heading, const String& message) { DelayedCallGuard dcguard(languageHook); CGUIDialogExtendedProgressBar* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_EXT_PROGRESS); if (pDialog == NULL) throw WindowException("Error: Window is NULL, this is not possible :-)"); CGUIDialogProgressBarHandle* pHandle = pDialog->GetHandle(heading); dlg = pDialog; handle = pHandle; open = true; pHandle->SetTitle(heading); if (!message.empty()) pHandle->SetText(message); } void DialogProgressBG::update(int percent, const String& heading, const String& message) { DelayedCallGuard dcguard(languageHook); CGUIDialogProgressBarHandle* pHandle = handle; if (pHandle == NULL) throw WindowException("Dialog not created."); if (percent >= 0 && percent <= 100) pHandle->SetPercentage((float)percent); if (!heading.empty()) pHandle->SetTitle(heading); if (!message.empty()) pHandle->SetText(message); } void DialogProgressBG::close() { DelayedCallGuard dcguard(languageHook); if (handle == NULL) throw WindowException("Dialog not created."); handle->MarkFinished(); open = false; } bool DialogProgressBG::isFinished() { if (handle == NULL) throw WindowException("Dialog not created."); return handle->IsFinished(); } } }