diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/pvr/dialogs | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/pvr/dialogs')
27 files changed, 5846 insertions, 0 deletions
diff --git a/xbmc/pvr/dialogs/CMakeLists.txt b/xbmc/pvr/dialogs/CMakeLists.txt new file mode 100644 index 0000000..39b88ed --- /dev/null +++ b/xbmc/pvr/dialogs/CMakeLists.txt @@ -0,0 +1,29 @@ +set(SOURCES GUIDialogPVRChannelManager.cpp + GUIDialogPVRChannelsOSD.cpp + GUIDialogPVRGroupManager.cpp + GUIDialogPVRGuideInfo.cpp + GUIDialogPVRChannelGuide.cpp + GUIDialogPVRGuideControls.cpp + GUIDialogPVRGuideSearch.cpp + GUIDialogPVRRadioRDSInfo.cpp + GUIDialogPVRRecordingInfo.cpp + GUIDialogPVRRecordingSettings.cpp + GUIDialogPVRTimerSettings.cpp + GUIDialogPVRClientPriorities.cpp + GUIDialogPVRItemsViewBase.cpp) + +set(HEADERS GUIDialogPVRChannelManager.h + GUIDialogPVRChannelsOSD.h + GUIDialogPVRGroupManager.h + GUIDialogPVRGuideInfo.h + GUIDialogPVRChannelGuide.h + GUIDialogPVRGuideControls.h + GUIDialogPVRGuideSearch.h + GUIDialogPVRRadioRDSInfo.h + GUIDialogPVRRecordingInfo.h + GUIDialogPVRRecordingSettings.h + GUIDialogPVRTimerSettings.h + GUIDialogPVRClientPriorities.h + GUIDialogPVRItemsViewBase.h) + +core_add_library(pvr_dialogs) diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp new file mode 100644 index 0000000..10b2623 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRChannelGuide.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "pvr/PVRManager.h" +#include "pvr/PVRPlaybackState.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/epg/EpgInfoTag.h" + +#include <memory> +#include <vector> + +using namespace PVR; + +CGUIDialogPVRChannelGuide::CGUIDialogPVRChannelGuide() + : CGUIDialogPVRItemsViewBase(WINDOW_DIALOG_PVR_CHANNEL_GUIDE, "DialogPVRChannelGuide.xml") +{ +} + +void CGUIDialogPVRChannelGuide::Open(const std::shared_ptr<CPVRChannel>& channel) +{ + m_channel = channel; + CGUIDialogPVRItemsViewBase::Open(); +} + +void CGUIDialogPVRChannelGuide::OnInitWindow() +{ + // no user-specific channel is set; use current playing channel + if (!m_channel) + m_channel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel(); + + if (!m_channel) + { + Close(); + return; + } + + Init(); + + const std::vector<std::shared_ptr<CPVREpgInfoTag>> tags = m_channel->GetEpgTags(); + for (const auto& tag : tags) + { + m_vecItems->Add(std::make_shared<CFileItem>(tag)); + } + + m_viewControl.SetItems(*m_vecItems); + + CGUIDialogPVRItemsViewBase::OnInitWindow(); + + // select the active entry + unsigned int iSelectedItem = 0; + for (int iEpgPtr = 0; iEpgPtr < m_vecItems->Size(); ++iEpgPtr) + { + const CFileItemPtr entry = m_vecItems->Get(iEpgPtr); + if (entry->HasEPGInfoTag() && entry->GetEPGInfoTag()->IsActive()) + { + iSelectedItem = iEpgPtr; + break; + } + } + m_viewControl.SetSelectedItem(iSelectedItem); +} + +void CGUIDialogPVRChannelGuide::OnDeinitWindow(int nextWindowID) +{ + CGUIDialogPVRItemsViewBase::OnDeinitWindow(nextWindowID); + m_channel.reset(); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.h b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.h new file mode 100644 index 0000000..b348a9e --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "pvr/dialogs/GUIDialogPVRItemsViewBase.h" + +#include <memory> + +namespace PVR +{ + class CPVRChannel; + + class CGUIDialogPVRChannelGuide : public CGUIDialogPVRItemsViewBase + { + public: + CGUIDialogPVRChannelGuide(); + ~CGUIDialogPVRChannelGuide() override = default; + + void Open(const std::shared_ptr<CPVRChannel>& channel); + + protected: + void OnInitWindow() override; + void OnDeinitWindow(int nextWindowID) override; + + private: + std::shared_ptr<CPVRChannel> m_channel; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp new file mode 100644 index 0000000..267b3c6 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp @@ -0,0 +1,1076 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRChannelManager.h" + +#include "FileItem.h" +#include "GUIPassword.h" +#include "ServiceBroker.h" +#include "TextureCache.h" +#include "dialogs/GUIDialogFileBrowser.h" +#include "dialogs/GUIDialogProgress.h" +#include "dialogs/GUIDialogSelect.h" +#include "dialogs/GUIDialogYesNo.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUIMessage.h" +#include "guilib/GUISpinControlEx.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "input/actions/Action.h" +#include "input/actions/ActionIDs.h" +#include "messaging/helpers/DialogOKHelper.h" +#include "profiles/ProfileManager.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClient.h" +#include "pvr/addons/PVRClients.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRChannelGroupMember.h" +#include "pvr/channels/PVRChannelGroups.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/dialogs/GUIDialogPVRGroupManager.h" +#include "pvr/guilib/PVRGUIActionsParentalControl.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "storage/MediaManager.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" + +#include <algorithm> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#define BUTTON_OK 4 +#define BUTTON_APPLY 5 +#define BUTTON_CANCEL 6 +#define RADIOBUTTON_ACTIVE 7 +#define EDIT_NAME 8 +#define BUTTON_CHANNEL_LOGO 9 +#define IMAGE_CHANNEL_LOGO 10 +#define RADIOBUTTON_USEEPG 12 +#define SPIN_EPGSOURCE_SELECTION 13 +#define RADIOBUTTON_PARENTAL_LOCK 14 +#define CONTROL_LIST_CHANNELS 20 +#define BUTTON_GROUP_MANAGER 30 +#define BUTTON_NEW_CHANNEL 31 +#define BUTTON_RADIO_TV 34 +#define BUTTON_REFRESH_LOGOS 35 + +namespace +{ +constexpr const char* LABEL_CHANNEL_DISABLED = "0"; + +// Note: strings must not be changed; they are part of the public skinning API for this dialog. +constexpr const char* PROPERTY_CHANNEL_NUMBER = "Number"; +constexpr const char* PROPERTY_CHANNEL_ENABLED = "ActiveChannel"; +constexpr const char* PROPERTY_CHANNEL_USER_SET_HIDDEN = "UserSetHidden"; +constexpr const char* PROPERTY_CHANNEL_LOCKED = "ParentalLocked"; +constexpr const char* PROPERTY_CHANNEL_ICON = "Icon"; +constexpr const char* PROPERTY_CHANNEL_CUSTOM_ICON = "UserSetIcon"; +constexpr const char* PROPERTY_CHANNEL_NAME = "Name"; +constexpr const char* PROPERTY_CHANNEL_EPG_ENABLED = "UseEPG"; +constexpr const char* PROPERTY_CHANNEL_EPG_SOURCE = "EPGSource"; +constexpr const char* PROPERTY_CLIENT_SUPPORTS_SETTINGS = "SupportsSettings"; +constexpr const char* PROPERTY_CLIENT_NAME = "ClientName"; +constexpr const char* PROPERTY_ITEM_CHANGED = "Changed"; + +} // namespace + +using namespace PVR; +using namespace KODI::MESSAGING; + +CGUIDialogPVRChannelManager::CGUIDialogPVRChannelManager() : + CGUIDialog(WINDOW_DIALOG_PVR_CHANNEL_MANAGER, "DialogPVRChannelManager.xml"), + m_channelItems(new CFileItemList) +{ + SetRadio(false); +} + +CGUIDialogPVRChannelManager::~CGUIDialogPVRChannelManager() +{ + delete m_channelItems; +} + +bool CGUIDialogPVRChannelManager::OnActionMove(const CAction& action) +{ + bool bReturn(false); + int iActionId = action.GetID(); + + if (GetFocusedControlID() == CONTROL_LIST_CHANNELS) + { + if (iActionId == ACTION_MOUSE_MOVE) + { + int iSelected = m_viewControl.GetSelectedItem(); + if (m_iSelected < iSelected) + { + iActionId = ACTION_MOVE_DOWN; + } + else if (m_iSelected > iSelected) + { + iActionId = ACTION_MOVE_UP; + } + else + { + return bReturn; + } + } + + if (iActionId == ACTION_MOVE_DOWN || iActionId == ACTION_MOVE_UP || + iActionId == ACTION_PAGE_DOWN || iActionId == ACTION_PAGE_UP || + iActionId == ACTION_FIRST_PAGE || iActionId == ACTION_LAST_PAGE) + { + CGUIDialog::OnAction(action); + int iSelected = m_viewControl.GetSelectedItem(); + + bReturn = true; + if (!m_bMovingMode) + { + if (iSelected != m_iSelected) + { + m_iSelected = iSelected; + SetData(m_iSelected); + } + } + else + { + bool bMoveUp = iActionId == ACTION_PAGE_UP || iActionId == ACTION_MOVE_UP || iActionId == ACTION_FIRST_PAGE; + unsigned int iLines = bMoveUp ? abs(m_iSelected - iSelected) : 1; + bool bOutOfBounds = bMoveUp ? m_iSelected <= 0 : m_iSelected >= m_channelItems->Size() - 1; + if (bOutOfBounds) + { + bMoveUp = !bMoveUp; + iLines = m_channelItems->Size() - 1; + } + for (unsigned int iLine = 0; iLine < iLines; ++iLine) + { + unsigned int iNewSelect = bMoveUp ? m_iSelected - 1 : m_iSelected + 1; + + const CFileItemPtr newItem = m_channelItems->Get(iNewSelect); + const std::string number = newItem->GetProperty(PROPERTY_CHANNEL_NUMBER).asString(); + if (number != LABEL_CHANNEL_DISABLED) + { + // Swap channel numbers + const CFileItemPtr item = m_channelItems->Get(m_iSelected); + newItem->SetProperty(PROPERTY_CHANNEL_NUMBER, + item->GetProperty(PROPERTY_CHANNEL_NUMBER)); + SetItemChanged(newItem); + item->SetProperty(PROPERTY_CHANNEL_NUMBER, number); + SetItemChanged(item); + } + + // swap items + m_channelItems->Swap(iNewSelect, m_iSelected); + m_iSelected = iNewSelect; + } + + m_viewControl.SetItems(*m_channelItems); + m_viewControl.SetSelectedItem(m_iSelected); + } + } + } + + return bReturn; +} + +bool CGUIDialogPVRChannelManager::OnAction(const CAction& action) +{ + return OnActionMove(action) || + CGUIDialog::OnAction(action); +} + +void CGUIDialogPVRChannelManager::OnInitWindow() +{ + CGUIDialog::OnInitWindow(); + + m_iSelected = 0; + m_bMovingMode = false; + m_bAllowNewChannel = false; + + EnableChannelOptions(false); + CONTROL_DISABLE(BUTTON_APPLY); + + // prevent resorting channels if backend channel numbers or backend channel order shall be used + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + m_bAllowRenumber = !settings->GetBool(CSettings::SETTING_PVRMANAGER_USEBACKENDCHANNELNUMBERS); + m_bAllowReorder = + m_bAllowRenumber && !settings->GetBool(CSettings::SETTING_PVRMANAGER_BACKENDCHANNELORDER); + + Update(); + + if (m_initialSelection) + { + // set initial selection + const std::shared_ptr<CPVRChannel> channel = m_initialSelection->GetPVRChannelInfoTag(); + for (int i = 0; i < m_channelItems->Size(); ++i) + { + if (m_channelItems->Get(i)->GetPVRChannelInfoTag() == channel) + { + m_iSelected = i; + m_viewControl.SetSelectedItem(m_iSelected); + break; + } + } + m_initialSelection.reset(); + } + SetData(m_iSelected); +} + +void CGUIDialogPVRChannelManager::OnDeinitWindow(int nextWindowID) +{ + Clear(); + + CGUIDialog::OnDeinitWindow(nextWindowID); +} + +void CGUIDialogPVRChannelManager::SetRadio(bool bIsRadio) +{ + m_bIsRadio = bIsRadio; + SetProperty("IsRadio", m_bIsRadio ? "true" : ""); +} + +void CGUIDialogPVRChannelManager::Open(const std::shared_ptr<CFileItem>& initialSelection) +{ + m_initialSelection = initialSelection; + CGUIDialog::Open(); +} + +bool CGUIDialogPVRChannelManager::OnClickListChannels(const CGUIMessage& message) +{ + if (!m_bMovingMode) + { + int iAction = message.GetParam1(); + int iItem = m_viewControl.GetSelectedItem(); + + /* Check file item is in list range and get his pointer */ + if (iItem < 0 || iItem >= m_channelItems->Size()) return true; + + /* Process actions */ + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK) + { + /* Show Contextmenu */ + OnPopupMenu(iItem); + + return true; + } + } + else + { + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + pItem->Select(false); + m_bMovingMode = false; + SetItemChanged(pItem); + return true; + } + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonOK() +{ + SaveList(); + Close(); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonApply() +{ + SaveList(); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonCancel() +{ + Close(); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonRadioTV() +{ + PromptAndSaveList(); + + m_iSelected = 0; + m_bMovingMode = false; + m_bAllowNewChannel = false; + m_bIsRadio = !m_bIsRadio; + SetProperty("IsRadio", m_bIsRadio ? "true" : ""); + Update(); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonRadioActive() +{ + CGUIMessage msg(GUI_MSG_IS_SELECTED, GetID(), RADIOBUTTON_ACTIVE); + if (OnMessage(msg)) + { + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + const bool selected = (msg.GetParam1() == 1); + if (pItem->GetProperty(PROPERTY_CHANNEL_ENABLED).asBoolean() != selected) + { + pItem->SetProperty(PROPERTY_CHANNEL_ENABLED, selected); + pItem->SetProperty(PROPERTY_CHANNEL_USER_SET_HIDDEN, true); + SetItemChanged(pItem); + Renumber(); + } + return true; + } + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonRadioParentalLocked() +{ + CGUIMessage msg(GUI_MSG_IS_SELECTED, GetID(), RADIOBUTTON_PARENTAL_LOCK); + if (!OnMessage(msg)) + return false; + + bool selected(msg.GetParam1() == 1); + + // ask for PIN first + if (CServiceBroker::GetPVRManager().Get<PVR::GUI::Parental>().CheckParentalPIN() != + ParentalCheckResult::SUCCESS) + { // failed - reset to previous + SET_CONTROL_SELECTED(GetID(), RADIOBUTTON_PARENTAL_LOCK, !selected); + return false; + } + + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + if (pItem->GetProperty(PROPERTY_CHANNEL_LOCKED).asBoolean() != selected) + { + pItem->SetProperty(PROPERTY_CHANNEL_LOCKED, selected); + SetItemChanged(pItem); + Renumber(); + } + return true; + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonEditName() +{ + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), EDIT_NAME); + if (OnMessage(msg)) + { + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + if (pItem->GetProperty(PROPERTY_CHANNEL_NAME).asString() != msg.GetLabel()) + { + pItem->SetProperty(PROPERTY_CHANNEL_NAME, msg.GetLabel()); + SetItemChanged(pItem); + } + return true; + } + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonChannelLogo() +{ + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (!pItem) + return false; + + const std::shared_ptr<CProfileManager> profileManager = CServiceBroker::GetSettingsComponent()->GetProfileManager(); + + if (profileManager->GetCurrentProfile().canWriteSources() && !g_passwordManager.IsProfileLockUnlocked()) + return false; + + // setup our thumb list + CFileItemList items; + + // add the current thumb, if available + if (!pItem->GetProperty(PROPERTY_CHANNEL_ICON).asString().empty()) + { + CFileItemPtr current(new CFileItem("thumb://Current", false)); + current->SetArt("thumb", pItem->GetPVRChannelInfoTag()->IconPath()); + current->SetLabel(g_localizeStrings.Get(19282)); + items.Add(current); + } + else if (pItem->HasArt("thumb")) + { // already have a thumb that the share doesn't know about - must be a local one, so we mayaswell reuse it. + CFileItemPtr current(new CFileItem("thumb://Current", false)); + current->SetArt("thumb", pItem->GetArt("thumb")); + current->SetLabel(g_localizeStrings.Get(19282)); + items.Add(current); + } + + // and add a "no thumb" entry as well + CFileItemPtr nothumb(new CFileItem("thumb://None", false)); + nothumb->SetArt("icon", pItem->GetArt("icon")); + nothumb->SetLabel(g_localizeStrings.Get(19283)); + items.Add(nothumb); + + std::string strThumb; + VECSOURCES shares; + const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + if (settings->GetString(CSettings::SETTING_PVRMENU_ICONPATH) != "") + { + CMediaSource share1; + share1.strPath = settings->GetString(CSettings::SETTING_PVRMENU_ICONPATH); + share1.strName = g_localizeStrings.Get(19066); + shares.push_back(share1); + } + CServiceBroker::GetMediaManager().GetLocalDrives(shares); + if (!CGUIDialogFileBrowser::ShowAndGetImage(items, shares, g_localizeStrings.Get(19285), strThumb, NULL, 19285)) + return false; + + if (strThumb == "thumb://Current") + return true; + + if (strThumb == "thumb://None") + strThumb = ""; + + if (pItem->GetProperty(PROPERTY_CHANNEL_ICON).asString() != strThumb) + { + pItem->SetProperty(PROPERTY_CHANNEL_ICON, strThumb); + pItem->SetProperty(PROPERTY_CHANNEL_CUSTOM_ICON, true); + SetItemChanged(pItem); + } + + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonUseEPG() +{ + CGUIMessage msg(GUI_MSG_IS_SELECTED, GetID(), RADIOBUTTON_USEEPG); + if (OnMessage(msg)) + { + CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + if (pItem) + { + const bool selected = (msg.GetParam1() == 1); + if (pItem->GetProperty(PROPERTY_CHANNEL_EPG_ENABLED).asBoolean() != selected) + { + pItem->SetProperty(PROPERTY_CHANNEL_EPG_ENABLED, selected); + SetItemChanged(pItem); + } + return true; + } + } + + return false; +} + +bool CGUIDialogPVRChannelManager::OnClickEPGSourceSpin() +{ + //! @todo Add EPG scraper support + return true; + // CGUISpinControlEx* pSpin = static_cast<CGUISpinControlEx*>(GetControl(SPIN_EPGSOURCE_SELECTION)); + // if (pSpin) + // { + // CFileItemPtr pItem = m_channelItems->Get(m_iSelected); + // if (pItem) + // { + // if (pItem->GetProperty(PROPERTY_CHANNEL_EPG_SOURCE).asInteger() != 0) + // { + // pItem->SetProperty(PROPERTY_CHANNEL_EPG_SOURCE, static_cast<int>(0)); + // SetItemChanged(pItem); + // } + // return true; + // } + // } +} + +bool CGUIDialogPVRChannelManager::OnClickButtonGroupManager() +{ + PromptAndSaveList(); + + /* Load group manager dialog */ + CGUIDialogPVRGroupManager* pDlgInfo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPVRGroupManager>(WINDOW_DIALOG_PVR_GROUP_MANAGER); + if (!pDlgInfo) + return false; + + pDlgInfo->SetRadio(m_bIsRadio); + + /* Open dialog window */ + pDlgInfo->Open(); + + Update(); + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonNewChannel() +{ + PromptAndSaveList(); + + int iSelection = 0; + if (m_clientsWithSettingsList.size() > 1) + { + CGUIDialogSelect* pDlgSelect = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); + if (!pDlgSelect) + return false; + + pDlgSelect->SetHeading(CVariant{19213}); // Select Client + + for (const auto& client : m_clientsWithSettingsList) + pDlgSelect->Add(client->Name()); + pDlgSelect->Open(); + + iSelection = pDlgSelect->GetSelectedItem(); + } + + if (iSelection >= 0 && iSelection < static_cast<int>(m_clientsWithSettingsList.size())) + { + int iClientID = m_clientsWithSettingsList[iSelection]->GetID(); + + std::shared_ptr<CPVRChannel> channel(new CPVRChannel(m_bIsRadio)); + channel->SetChannelName(g_localizeStrings.Get(19204)); // New channel + channel->SetClientID(iClientID); + + PVR_ERROR ret = PVR_ERROR_UNKNOWN; + const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(iClientID); + if (client) + { + channel->SetEPGEnabled(client->GetClientCapabilities().SupportsEPG()); + ret = client->OpenDialogChannelAdd(channel); + } + + if (ret == PVR_ERROR_NO_ERROR) + { + CFileItemList prevChannelItems; + prevChannelItems.Assign(*m_channelItems); + + Update(); + + for (int index = 0; index < m_channelItems->Size(); ++index) + { + if (!prevChannelItems.Contains(m_channelItems->Get(index)->GetPath())) + { + m_iSelected = index; + m_viewControl.SetSelectedItem(m_iSelected); + SetData(m_iSelected); + break; + } + } + } + else if (ret == PVR_ERROR_NOT_IMPLEMENTED) + HELPERS::ShowOKDialogText(CVariant{19033}, CVariant{19038}); // "Information", "Not supported by the PVR backend." + else + HELPERS::ShowOKDialogText(CVariant{2103}, CVariant{16029}); // "Add-on error", "Check the log for more information about this message." + } + return true; +} + +bool CGUIDialogPVRChannelManager::OnClickButtonRefreshChannelLogos() +{ + for (const auto& item : *m_channelItems) + { + const std::string thumb = item->GetArt("thumb"); + if (!thumb.empty()) + { + // clear current cached image + CServiceBroker::GetTextureCache()->ClearCachedImage(thumb); + item->SetArt("thumb", ""); + } + } + + m_iSelected = 0; + Update(); + + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS); + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); + + return true; +} + +bool CGUIDialogPVRChannelManager::OnMessageClick(const CGUIMessage& message) +{ + int iControl = message.GetSenderId(); + switch(iControl) + { + case CONTROL_LIST_CHANNELS: + return OnClickListChannels(message); + case BUTTON_OK: + return OnClickButtonOK(); + case BUTTON_APPLY: + return OnClickButtonApply(); + case BUTTON_CANCEL: + return OnClickButtonCancel(); + case BUTTON_RADIO_TV: + return OnClickButtonRadioTV(); + case RADIOBUTTON_ACTIVE: + return OnClickButtonRadioActive(); + case RADIOBUTTON_PARENTAL_LOCK: + return OnClickButtonRadioParentalLocked(); + case EDIT_NAME: + return OnClickButtonEditName(); + case BUTTON_CHANNEL_LOGO: + return OnClickButtonChannelLogo(); + case RADIOBUTTON_USEEPG: + return OnClickButtonUseEPG(); + case SPIN_EPGSOURCE_SELECTION: + return OnClickEPGSourceSpin(); + case BUTTON_GROUP_MANAGER: + return OnClickButtonGroupManager(); + case BUTTON_NEW_CHANNEL: + return OnClickButtonNewChannel(); + case BUTTON_REFRESH_LOGOS: + return OnClickButtonRefreshChannelLogos(); + default: + return false; + } +} + +bool CGUIDialogPVRChannelManager::OnMessage(CGUIMessage& message) +{ + unsigned int iMessage = message.GetMessage(); + + switch (iMessage) + { + case GUI_MSG_CLICKED: + return OnMessageClick(message); + } + + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRChannelManager::OnWindowLoaded() +{ + CGUIDialog::OnWindowLoaded(); + + m_viewControl.Reset(); + m_viewControl.SetParentWindow(GetID()); + m_viewControl.AddView(GetControl(CONTROL_LIST_CHANNELS)); +} + +void CGUIDialogPVRChannelManager::OnWindowUnload() +{ + CGUIDialog::OnWindowUnload(); + m_viewControl.Reset(); +} + +CFileItemPtr CGUIDialogPVRChannelManager::GetCurrentListItem(int offset) +{ + return m_channelItems->Get(m_iSelected); +} + +bool CGUIDialogPVRChannelManager::OnPopupMenu(int iItem) +{ + // popup the context menu + // grab our context menu + CContextButtons buttons; + + // mark the item + if (iItem >= 0 && iItem < m_channelItems->Size()) + m_channelItems->Get(iItem)->Select(true); + else + return false; + + CFileItemPtr pItem = m_channelItems->Get(iItem); + if (!pItem) + return false; + + if (m_bAllowReorder && + pItem->GetProperty(PROPERTY_CHANNEL_NUMBER).asString() != LABEL_CHANNEL_DISABLED) + buttons.Add(CONTEXT_BUTTON_MOVE, 116); /* Move channel up or down */ + + if (pItem->GetProperty(PROPERTY_CLIENT_SUPPORTS_SETTINGS).asBoolean()) + { + buttons.Add(CONTEXT_BUTTON_SETTINGS, 10004); /* Open add-on channel settings dialog */ + buttons.Add(CONTEXT_BUTTON_DELETE, 117); /* Delete add-on channel */ + } + + int choice = CGUIDialogContextMenu::ShowAndGetChoice(buttons); + + // deselect our item + if (iItem >= 0 && iItem < m_channelItems->Size()) + m_channelItems->Get(iItem)->Select(false); + + if (choice < 0) + return false; + + return OnContextButton(iItem, (CONTEXT_BUTTON)choice); +} + +bool CGUIDialogPVRChannelManager::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + /* Check file item is in list range and get his pointer */ + if (itemNumber < 0 || itemNumber >= m_channelItems->Size()) return false; + + CFileItemPtr pItem = m_channelItems->Get(itemNumber); + if (!pItem) + return false; + + if (button == CONTEXT_BUTTON_MOVE) + { + m_bMovingMode = true; + pItem->Select(true); + } + else if (button == CONTEXT_BUTTON_SETTINGS) + { + PromptAndSaveList(); + + const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*pItem); + PVR_ERROR ret = PVR_ERROR_UNKNOWN; + if (client) + ret = client->OpenDialogChannelSettings(pItem->GetPVRChannelInfoTag()); + + if (ret == PVR_ERROR_NO_ERROR) + { + Update(); + SetData(m_iSelected); + } + else if (ret == PVR_ERROR_NOT_IMPLEMENTED) + HELPERS::ShowOKDialogText(CVariant{19033}, CVariant{19038}); // "Information", "Not supported by the PVR backend." + else + HELPERS::ShowOKDialogText(CVariant{2103}, CVariant{16029}); // "Add-on error", "Check the log for more information about this message." + } + else if (button == CONTEXT_BUTTON_DELETE) + { + CGUIDialogYesNo* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return true; + + pDialog->SetHeading(CVariant{19211}); // Delete channel + pDialog->SetText(CVariant{750}); // Are you sure? + pDialog->Open(); + + if (pDialog->IsConfirmed()) + { + const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*pItem); + if (client) + { + const std::shared_ptr<CPVRChannel> channel = pItem->GetPVRChannelInfoTag(); + PVR_ERROR ret = client->DeleteChannel(channel); + if (ret == PVR_ERROR_NO_ERROR) + { + CPVRChannelGroups* groups = + CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bIsRadio); + if (groups) + { + groups->UpdateFromClients({}); + Update(); + } + } + else if (ret == PVR_ERROR_NOT_IMPLEMENTED) + HELPERS::ShowOKDialogText(CVariant{19033}, CVariant{19038}); // "Information", "Not supported by the PVR backend." + else + HELPERS::ShowOKDialogText(CVariant{2103}, CVariant{16029}); // "Add-on error", "Check the log for more information about this message." + } + } + } + return true; +} + +void CGUIDialogPVRChannelManager::SetData(int iItem) +{ + if (iItem < 0 || iItem >= m_channelItems->Size()) + { + ClearChannelOptions(); + EnableChannelOptions(false); + return; + } + + CFileItemPtr pItem = m_channelItems->Get(iItem); + if (!pItem) + return; + + SET_CONTROL_LABEL2(EDIT_NAME, pItem->GetProperty(PROPERTY_CHANNEL_NAME).asString()); + CGUIMessage msg(GUI_MSG_SET_TYPE, GetID(), EDIT_NAME, CGUIEditControl::INPUT_TYPE_TEXT, 19208); + OnMessage(msg); + + SET_CONTROL_SELECTED(GetID(), RADIOBUTTON_ACTIVE, + pItem->GetProperty(PROPERTY_CHANNEL_ENABLED).asBoolean()); + SET_CONTROL_SELECTED(GetID(), RADIOBUTTON_USEEPG, + pItem->GetProperty(PROPERTY_CHANNEL_EPG_ENABLED).asBoolean()); + SET_CONTROL_SELECTED(GetID(), RADIOBUTTON_PARENTAL_LOCK, + pItem->GetProperty(PROPERTY_CHANNEL_LOCKED).asBoolean()); + + EnableChannelOptions(true); +} + +void CGUIDialogPVRChannelManager::Update() +{ + m_viewControl.SetCurrentView(CONTROL_LIST_CHANNELS); + + // empty the lists ready for population + Clear(); + + std::shared_ptr<CPVRChannelGroup> channels = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bIsRadio); + + // No channels available, nothing to do. + if (!channels) + return; + + channels->UpdateFromClients({}); + + const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers = channels->GetMembers(); + std::shared_ptr<CFileItem> channelFile; + for (const auto& member : groupMembers) + { + channelFile = std::make_shared<CFileItem>(member); + if (!channelFile) + continue; + const std::shared_ptr<CPVRChannel> channel(channelFile->GetPVRChannelInfoTag()); + + channelFile->SetProperty(PROPERTY_CHANNEL_ENABLED, !channel->IsHidden()); + channelFile->SetProperty(PROPERTY_CHANNEL_USER_SET_HIDDEN, channel->IsUserSetHidden()); + channelFile->SetProperty(PROPERTY_CHANNEL_NAME, channel->ChannelName()); + channelFile->SetProperty(PROPERTY_CHANNEL_EPG_ENABLED, channel->EPGEnabled()); + channelFile->SetProperty(PROPERTY_CHANNEL_ICON, channel->ClientIconPath()); + channelFile->SetProperty(PROPERTY_CHANNEL_CUSTOM_ICON, channel->IsUserSetIcon()); + channelFile->SetProperty(PROPERTY_CHANNEL_EPG_SOURCE, 0); + channelFile->SetProperty(PROPERTY_CHANNEL_LOCKED, channel->IsLocked()); + channelFile->SetProperty(PROPERTY_CHANNEL_NUMBER, + member->ChannelNumber().FormattedChannelNumber()); + + const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*channelFile); + if (client) + { + channelFile->SetProperty(PROPERTY_CLIENT_NAME, client->GetFriendlyName()); + channelFile->SetProperty(PROPERTY_CLIENT_SUPPORTS_SETTINGS, + client->GetClientCapabilities().SupportsChannelSettings()); + } + + m_channelItems->Add(channelFile); + } + + { + std::vector< std::pair<std::string, int> > labels; + labels.emplace_back(g_localizeStrings.Get(19210), 0); + //! @todo Add Labels for EPG scrapers here + SET_CONTROL_LABELS(SPIN_EPGSOURCE_SELECTION, 0, &labels); + } + + m_clientsWithSettingsList = CServiceBroker::GetPVRManager().Clients()->GetClientsSupportingChannelSettings(m_bIsRadio); + if (!m_clientsWithSettingsList.empty()) + m_bAllowNewChannel = true; + + if (m_bAllowNewChannel) + CONTROL_ENABLE(BUTTON_NEW_CHANNEL); + else + CONTROL_DISABLE(BUTTON_NEW_CHANNEL); + + Renumber(); + m_viewControl.SetItems(*m_channelItems); + if (m_iSelected >= m_channelItems->Size()) + m_iSelected = m_channelItems->Size() - 1; + m_viewControl.SetSelectedItem(m_iSelected); + SetData(m_iSelected); +} + +void CGUIDialogPVRChannelManager::Clear() +{ + m_viewControl.Clear(); + m_channelItems->Clear(); + + ClearChannelOptions(); + EnableChannelOptions(false); + + CONTROL_DISABLE(BUTTON_APPLY); +} + +void CGUIDialogPVRChannelManager::ClearChannelOptions() +{ + CONTROL_DESELECT(RADIOBUTTON_ACTIVE); + SET_CONTROL_LABEL2(EDIT_NAME, ""); + SET_CONTROL_FILENAME(BUTTON_CHANNEL_LOGO, ""); + CONTROL_DESELECT(RADIOBUTTON_USEEPG); + + std::vector<std::pair<std::string, int>> labels = {{g_localizeStrings.Get(19210), 0}}; + SET_CONTROL_LABELS(SPIN_EPGSOURCE_SELECTION, 0, &labels); + + CONTROL_DESELECT(RADIOBUTTON_PARENTAL_LOCK); +} + +void CGUIDialogPVRChannelManager::EnableChannelOptions(bool bEnable) +{ + if (bEnable) + { + CONTROL_ENABLE(RADIOBUTTON_ACTIVE); + CONTROL_ENABLE(EDIT_NAME); + CONTROL_ENABLE(BUTTON_CHANNEL_LOGO); + CONTROL_ENABLE(IMAGE_CHANNEL_LOGO); + CONTROL_ENABLE(RADIOBUTTON_USEEPG); + CONTROL_ENABLE(SPIN_EPGSOURCE_SELECTION); + CONTROL_ENABLE(RADIOBUTTON_PARENTAL_LOCK); + } + else + { + CONTROL_DISABLE(RADIOBUTTON_ACTIVE); + CONTROL_DISABLE(EDIT_NAME); + CONTROL_DISABLE(BUTTON_CHANNEL_LOGO); + CONTROL_DISABLE(IMAGE_CHANNEL_LOGO); + CONTROL_DISABLE(RADIOBUTTON_USEEPG); + CONTROL_DISABLE(SPIN_EPGSOURCE_SELECTION); + CONTROL_DISABLE(RADIOBUTTON_PARENTAL_LOCK); + } +} + +void CGUIDialogPVRChannelManager::RenameChannel(const CFileItemPtr& pItem) +{ + std::string strChannelName = pItem->GetProperty(PROPERTY_CHANNEL_NAME).asString(); + if (strChannelName != pItem->GetPVRChannelInfoTag()->ChannelName()) + { + std::shared_ptr<CPVRChannel> channel = pItem->GetPVRChannelInfoTag(); + channel->SetChannelName(strChannelName); + + const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*pItem); + if (!client || (client->RenameChannel(channel) != PVR_ERROR_NO_ERROR)) + HELPERS::ShowOKDialogText(CVariant{2103}, CVariant{16029}); // Add-on error;Check the log file for details. + } +} + +bool CGUIDialogPVRChannelManager::PersistChannel(const CFileItemPtr& pItem, + const std::shared_ptr<CPVRChannelGroup>& group) +{ + if (!pItem || !group) + return false; + + return group->UpdateChannel( + pItem->GetPVRChannelInfoTag()->StorageId(), + pItem->GetProperty(PROPERTY_CHANNEL_NAME).asString(), + pItem->GetProperty(PROPERTY_CHANNEL_ICON).asString(), + static_cast<int>(pItem->GetProperty(PROPERTY_CHANNEL_EPG_SOURCE).asInteger()), + m_bAllowRenumber ? pItem->GetProperty(PROPERTY_CHANNEL_NUMBER).asInteger() : 0, + !pItem->GetProperty(PROPERTY_CHANNEL_ENABLED).asBoolean(), // hidden + pItem->GetProperty(PROPERTY_CHANNEL_EPG_ENABLED).asBoolean(), + pItem->GetProperty(PROPERTY_CHANNEL_LOCKED).asBoolean(), + pItem->GetProperty(PROPERTY_CHANNEL_CUSTOM_ICON).asBoolean(), + pItem->GetProperty(PROPERTY_CHANNEL_USER_SET_HIDDEN).asBoolean()); +} + +void CGUIDialogPVRChannelManager::PromptAndSaveList() +{ + if (!HasChangedItems()) + return; + + CGUIDialogYesNo* pDialogYesNo = + CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>(WINDOW_DIALOG_YES_NO); + if (pDialogYesNo) + { + pDialogYesNo->SetHeading(CVariant{20052}); + pDialogYesNo->SetLine(0, CVariant{""}); + pDialogYesNo->SetLine(1, CVariant{19212}); + pDialogYesNo->SetLine(2, CVariant{20103}); + pDialogYesNo->Open(); + + if (pDialogYesNo->IsConfirmed()) + SaveList(); + else + Update(); + } +} + +void CGUIDialogPVRChannelManager::SaveList() +{ + if (!HasChangedItems()) + return; + + /* display the progress dialog */ + CGUIDialogProgress* pDlgProgress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS); + pDlgProgress->SetHeading(CVariant{190}); + pDlgProgress->SetLine(0, CVariant{""}); + pDlgProgress->SetLine(1, CVariant{328}); + pDlgProgress->SetLine(2, CVariant{""}); + pDlgProgress->Open(); + pDlgProgress->Progress(); + pDlgProgress->SetPercentage(0); + + /* persist all channels */ + std::shared_ptr<CPVRChannelGroup> group = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bIsRadio); + if (!group) + return; + + for (int iListPtr = 0; iListPtr < m_channelItems->Size(); ++iListPtr) + { + CFileItemPtr pItem = m_channelItems->Get(iListPtr); + if (pItem && pItem->GetProperty(PROPERTY_ITEM_CHANGED).asBoolean()) + { + if (pItem->GetProperty(PROPERTY_CLIENT_SUPPORTS_SETTINGS).asBoolean()) + RenameChannel(pItem); + + if (PersistChannel(pItem, group)) + pItem->SetProperty(PROPERTY_ITEM_CHANGED, false); + } + + pDlgProgress->SetPercentage(iListPtr * 100 / m_channelItems->Size()); + } + + group->SortAndRenumber(); + + auto channelGroups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bIsRadio); + channelGroups->UpdateChannelNumbersFromAllChannelsGroup(); + channelGroups->PersistAll(); + pDlgProgress->Close(); + + CONTROL_DISABLE(BUTTON_APPLY); +} + +bool CGUIDialogPVRChannelManager::HasChangedItems() const +{ + return std::any_of(m_channelItems->cbegin(), m_channelItems->cend(), [](const auto& item) { + return item && item->GetProperty(PROPERTY_ITEM_CHANGED).asBoolean(); + }); +} + +namespace +{ + +bool IsItemChanged(const std::shared_ptr<CFileItem>& item) +{ + const std::shared_ptr<CPVRChannelGroupMember> member = item->GetPVRChannelGroupMemberInfoTag(); + const std::shared_ptr<CPVRChannel> channel = member->Channel(); + + return item->GetProperty(PROPERTY_CHANNEL_ENABLED).asBoolean() == channel->IsHidden() || + item->GetProperty(PROPERTY_CHANNEL_USER_SET_HIDDEN).asBoolean() != + channel->IsUserSetHidden() || + item->GetProperty(PROPERTY_CHANNEL_NAME).asString() != channel->ChannelName() || + item->GetProperty(PROPERTY_CHANNEL_EPG_ENABLED).asBoolean() != channel->EPGEnabled() || + item->GetProperty(PROPERTY_CHANNEL_ICON).asString() != channel->ClientIconPath() || + item->GetProperty(PROPERTY_CHANNEL_CUSTOM_ICON).asBoolean() != channel->IsUserSetIcon() || + item->GetProperty(PROPERTY_CHANNEL_EPG_SOURCE).asInteger() != 0 || + item->GetProperty(PROPERTY_CHANNEL_LOCKED).asBoolean() != channel->IsLocked() || + item->GetProperty(PROPERTY_CHANNEL_NUMBER).asString() != + member->ChannelNumber().FormattedChannelNumber(); +} + +} // namespace + +void CGUIDialogPVRChannelManager::SetItemChanged(const CFileItemPtr& pItem) +{ + const bool changed = IsItemChanged(pItem); + pItem->SetProperty(PROPERTY_ITEM_CHANGED, changed); + + if (changed || HasChangedItems()) + CONTROL_ENABLE(BUTTON_APPLY); + else + CONTROL_DISABLE(BUTTON_APPLY); +} + +void CGUIDialogPVRChannelManager::Renumber() +{ + if (!m_bAllowRenumber) + return; + + int iNextChannelNumber = 0; + for (const auto& item : *m_channelItems) + { + const std::string number = item->GetProperty(PROPERTY_CHANNEL_ENABLED).asBoolean() + ? std::to_string(++iNextChannelNumber) + : LABEL_CHANNEL_DISABLED; + + if (item->GetProperty(PROPERTY_CHANNEL_NUMBER).asString() != number) + { + item->SetProperty(PROPERTY_CHANNEL_NUMBER, number); + SetItemChanged(item); + } + } +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h new file mode 100644 index 0000000..0716436 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "dialogs/GUIDialogContextMenu.h" +#include "guilib/GUIDialog.h" +#include "view/GUIViewControl.h" + +#include <memory> +#include <vector> + +class CAction; +class CFileItemList; +class CGUIMessage; + +namespace PVR +{ + class CPVRChannelGroup; + class CPVRClient; + + class CGUIDialogPVRChannelManager : public CGUIDialog + { + public: + CGUIDialogPVRChannelManager(); + ~CGUIDialogPVRChannelManager() override; + bool OnMessage(CGUIMessage& message) override; + bool OnAction(const CAction& action) override; + void OnWindowLoaded() override; + void OnWindowUnload() override; + bool HasListItems() const override{ return true; } + CFileItemPtr GetCurrentListItem(int offset = 0) override; + + void Open(const std::shared_ptr<CFileItem>& initialSelection); + void SetRadio(bool bIsRadio); + + protected: + void OnInitWindow() override; + void OnDeinitWindow(int nextWindowID) override; + + private: + void Clear(); + void Update(); + void PromptAndSaveList(); + void SaveList(); + void Renumber(); + void SetData(int iItem); + void RenameChannel(const CFileItemPtr& pItem); + + void ClearChannelOptions(); + void EnableChannelOptions(bool bEnable); + + bool OnPopupMenu(int iItem); + bool OnContextButton(int itemNumber, CONTEXT_BUTTON button); + bool OnActionMove(const CAction& action); + bool OnMessageClick(const CGUIMessage& message); + bool OnClickListChannels(const CGUIMessage& message); + bool OnClickButtonOK(); + bool OnClickButtonApply(); + bool OnClickButtonCancel(); + bool OnClickButtonRadioTV(); + bool OnClickButtonRadioActive(); + bool OnClickButtonRadioParentalLocked(); + bool OnClickButtonEditName(); + bool OnClickButtonChannelLogo(); + bool OnClickButtonUseEPG(); + bool OnClickEPGSourceSpin(); + bool OnClickButtonGroupManager(); + bool OnClickButtonNewChannel(); + bool OnClickButtonRefreshChannelLogos(); + + bool PersistChannel(const CFileItemPtr& pItem, const std::shared_ptr<CPVRChannelGroup>& group); + + bool HasChangedItems() const; + void SetItemChanged(const CFileItemPtr& pItem); + + bool m_bIsRadio = false; + bool m_bMovingMode = false; + bool m_bAllowNewChannel = false; + bool m_bAllowRenumber = false; + bool m_bAllowReorder = false; + + std::shared_ptr<CFileItem> m_initialSelection; + int m_iSelected = 0; + CFileItemList* m_channelItems; + CGUIViewControl m_viewControl; + + std::vector<std::shared_ptr<CPVRClient>> m_clientsWithSettingsList; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp new file mode 100644 index 0000000..b5830d4 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRChannelsOSD.h" + +#include "FileItem.h" +#include "GUIInfoManager.h" +#include "ServiceBroker.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIMessage.h" +#include "input/actions/Action.h" +#include "input/actions/ActionIDs.h" +#include "messaging/ApplicationMessenger.h" +#include "pvr/PVRManager.h" +#include "pvr/PVRPlaybackState.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRChannelGroupMember.h" +#include "pvr/channels/PVRChannelGroups.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/epg/EpgContainer.h" +#include "pvr/guilib/PVRGUIActionsChannels.h" +#include "pvr/guilib/PVRGUIActionsPlayback.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" + +#include <memory> +#include <string> +#include <vector> + +using namespace PVR; + +using namespace std::chrono_literals; + +#define MAX_INVALIDATION_FREQUENCY 2000ms // limit to one invalidation per X milliseconds + +CGUIDialogPVRChannelsOSD::CGUIDialogPVRChannelsOSD() + : CGUIDialogPVRItemsViewBase(WINDOW_DIALOG_PVR_OSD_CHANNELS, "DialogPVRChannelsOSD.xml") +{ + CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().RegisterChannelNumberInputHandler(this); +} + +CGUIDialogPVRChannelsOSD::~CGUIDialogPVRChannelsOSD() +{ + auto& mgr = CServiceBroker::GetPVRManager(); + mgr.Events().Unsubscribe(this); + mgr.Get<PVR::GUI::Channels>().DeregisterChannelNumberInputHandler(this); +} + +bool CGUIDialogPVRChannelsOSD::OnMessage(CGUIMessage& message) +{ + if (message.GetMessage() == GUI_MSG_REFRESH_LIST) + { + switch (static_cast<PVREvent>(message.GetParam1())) + { + case PVREvent::CurrentItem: + m_viewControl.SetItems(*m_vecItems); + return true; + + case PVREvent::Epg: + case PVREvent::EpgContainer: + case PVREvent::EpgActiveItem: + if (IsActive()) + SetInvalid(); + return true; + + default: + break; + } + } + return CGUIDialogPVRItemsViewBase::OnMessage(message); +} + +void CGUIDialogPVRChannelsOSD::OnInitWindow() +{ + if (!CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingTV() && + !CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRadio()) + { + Close(); + return; + } + + Init(); + Update(); + CGUIDialogPVRItemsViewBase::OnInitWindow(); +} + +void CGUIDialogPVRChannelsOSD::OnDeinitWindow(int nextWindowID) +{ + if (m_group) + { + CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().SetSelectedChannelPath( + m_group->IsRadio(), m_viewControl.GetSelectedItemPath()); + + // next OnInitWindow will set the group which is then selected + m_group.reset(); + } + + CGUIDialogPVRItemsViewBase::OnDeinitWindow(nextWindowID); +} + +bool CGUIDialogPVRChannelsOSD::OnAction(const CAction& action) +{ + switch (action.GetID()) + { + case ACTION_SELECT_ITEM: + case ACTION_MOUSE_LEFT_CLICK: + { + // If direct channel number input is active, select the entered channel. + if (CServiceBroker::GetPVRManager() + .Get<PVR::GUI::Channels>() + .GetChannelNumberInputHandler() + .CheckInputAndExecuteAction()) + return true; + + if (m_viewControl.HasControl(GetFocusedControlID())) + { + // Switch to channel + GotoChannel(m_viewControl.GetSelectedItem()); + return true; + } + break; + } + case ACTION_PREVIOUS_CHANNELGROUP: + case ACTION_NEXT_CHANNELGROUP: + { + // save control states and currently selected item of group + SaveControlStates(); + + // switch to next or previous group + const CPVRChannelGroups* groups = + CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_group->IsRadio()); + const std::shared_ptr<CPVRChannelGroup> nextGroup = action.GetID() == ACTION_NEXT_CHANNELGROUP + ? groups->GetNextGroup(*m_group) + : groups->GetPreviousGroup(*m_group); + CServiceBroker::GetPVRManager().PlaybackState()->SetActiveChannelGroup(nextGroup); + m_group = nextGroup; + Init(); + Update(); + + // restore control states and previously selected item of group + RestoreControlStates(); + return true; + } + case REMOTE_0: + case REMOTE_1: + case REMOTE_2: + case REMOTE_3: + case REMOTE_4: + case REMOTE_5: + case REMOTE_6: + case REMOTE_7: + case REMOTE_8: + case REMOTE_9: + { + AppendChannelNumberCharacter(static_cast<char>(action.GetID() - REMOTE_0) + '0'); + return true; + } + case ACTION_CHANNEL_NUMBER_SEP: + { + AppendChannelNumberCharacter(CPVRChannelNumber::SEPARATOR); + return true; + } + default: + break; + } + + return CGUIDialogPVRItemsViewBase::OnAction(action); +} + +void CGUIDialogPVRChannelsOSD::Update() +{ + CPVRManager& pvrMgr = CServiceBroker::GetPVRManager(); + pvrMgr.Events().Subscribe(this, &CGUIDialogPVRChannelsOSD::Notify); + + const std::shared_ptr<CPVRChannel> channel = pvrMgr.PlaybackState()->GetPlayingChannel(); + if (channel) + { + const std::shared_ptr<CPVRChannelGroup> group = + pvrMgr.PlaybackState()->GetActiveChannelGroup(channel->IsRadio()); + if (group) + { + const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers = + group->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE); + for (const auto& groupMember : groupMembers) + { + m_vecItems->Add(std::make_shared<CFileItem>(groupMember)); + } + + m_viewControl.SetItems(*m_vecItems); + + if (!m_group) + { + m_group = group; + m_viewControl.SetSelectedItem( + pvrMgr.Get<PVR::GUI::Channels>().GetSelectedChannelPath(channel->IsRadio())); + SaveSelectedItemPath(group->GroupID()); + } + } + } +} + +void CGUIDialogPVRChannelsOSD::SetInvalid() +{ + if (m_refreshTimeout.IsTimePast()) + { + for (const auto& item : *m_vecItems) + item->SetInvalid(); + + CGUIDialogPVRItemsViewBase::SetInvalid(); + m_refreshTimeout.Set(MAX_INVALIDATION_FREQUENCY); + } +} + +void CGUIDialogPVRChannelsOSD::SaveControlStates() +{ + CGUIDialogPVRItemsViewBase::SaveControlStates(); + + if (m_group) + SaveSelectedItemPath(m_group->GroupID()); +} + +void CGUIDialogPVRChannelsOSD::RestoreControlStates() +{ + CGUIDialogPVRItemsViewBase::RestoreControlStates(); + + if (m_group) + { + const std::string path = GetLastSelectedItemPath(m_group->GroupID()); + if (path.empty()) + m_viewControl.SetSelectedItem(0); + else + m_viewControl.SetSelectedItem(path); + } +} + +void CGUIDialogPVRChannelsOSD::GotoChannel(int iItem) +{ + if (iItem < 0 || iItem >= m_vecItems->Size()) + return; + + // Preserve the item before closing self, because this will clear m_vecItems + const std::shared_ptr<CFileItem> item = m_vecItems->Get(iItem); + + if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool( + CSettings::SETTING_PVRMENU_CLOSECHANNELOSDONSWITCH)) + Close(); + + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel( + *item, true /* bCheckResume */); +} + +void CGUIDialogPVRChannelsOSD::Notify(const PVREvent& event) +{ + const CGUIMessage m(GUI_MSG_REFRESH_LIST, GetID(), 0, static_cast<int>(event)); + CServiceBroker::GetAppMessenger()->SendGUIMessage(m); +} + +void CGUIDialogPVRChannelsOSD::SaveSelectedItemPath(int iGroupID) +{ + m_groupSelectedItemPaths[iGroupID] = m_viewControl.GetSelectedItemPath(); +} + +std::string CGUIDialogPVRChannelsOSD::GetLastSelectedItemPath(int iGroupID) const +{ + const auto it = m_groupSelectedItemPaths.find(iGroupID); + if (it != m_groupSelectedItemPaths.end()) + return it->second; + + return std::string(); +} + +void CGUIDialogPVRChannelsOSD::GetChannelNumbers(std::vector<std::string>& channelNumbers) +{ + if (m_group) + m_group->GetChannelNumbers(channelNumbers); +} + +void CGUIDialogPVRChannelsOSD::OnInputDone() +{ + const CPVRChannelNumber channelNumber = GetChannelNumber(); + if (channelNumber.IsValid()) + { + int itemIndex = 0; + for (const CFileItemPtr& channel : *m_vecItems) + { + if (channel->GetPVRChannelGroupMemberInfoTag()->ChannelNumber() == channelNumber) + { + m_viewControl.SetSelectedItem(itemIndex); + return; + } + ++itemIndex; + } + } +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h new file mode 100644 index 0000000..c23fb92 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "pvr/PVRChannelNumberInputHandler.h" +#include "pvr/dialogs/GUIDialogPVRItemsViewBase.h" +#include "threads/SystemClock.h" + +#include <map> +#include <memory> +#include <string> + +namespace PVR +{ +enum class PVREvent; + +class CPVRChannelGroup; + +class CGUIDialogPVRChannelsOSD : public CGUIDialogPVRItemsViewBase, + public CPVRChannelNumberInputHandler +{ +public: + CGUIDialogPVRChannelsOSD(); + ~CGUIDialogPVRChannelsOSD() override; + bool OnMessage(CGUIMessage& message) override; + bool OnAction(const CAction& action) override; + + /*! + * @brief CEventStream callback for PVR events. + * @param event The event. + */ + void Notify(const PVREvent& event); + + // CPVRChannelNumberInputHandler implementation + void GetChannelNumbers(std::vector<std::string>& channelNumbers) override; + void OnInputDone() override; + +protected: + void OnInitWindow() override; + void OnDeinitWindow(int nextWindowID) override; + void RestoreControlStates() override; + void SaveControlStates() override; + void SetInvalid() override; + +private: + void GotoChannel(int iItem); + void Update(); + void SaveSelectedItemPath(int iGroupID); + std::string GetLastSelectedItemPath(int iGroupID) const; + + std::shared_ptr<CPVRChannelGroup> m_group; + std::map<int, std::string> m_groupSelectedItemPaths; + XbmcThreads::EndTime<> m_refreshTimeout; +}; +} // namespace PVR diff --git a/xbmc/pvr/dialogs/GUIDialogPVRClientPriorities.cpp b/xbmc/pvr/dialogs/GUIDialogPVRClientPriorities.cpp new file mode 100644 index 0000000..ca83ba1 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRClientPriorities.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017-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 "GUIDialogPVRClientPriorities.h" + +#include "ServiceBroker.h" +#include "guilib/GUIMessage.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClient.h" +#include "settings/lib/Setting.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <cstdlib> +#include <memory> + +using namespace PVR; + +CGUIDialogPVRClientPriorities::CGUIDialogPVRClientPriorities() : + CGUIDialogSettingsManualBase(WINDOW_DIALOG_PVR_CLIENT_PRIORITIES, "DialogSettings.xml") +{ + m_loadType = LOAD_EVERY_TIME; +} + +void CGUIDialogPVRClientPriorities::SetupView() +{ + CGUIDialogSettingsManualBase::SetupView(); + + SetHeading(19240); // Client priorities + SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON); + SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186); // OK + SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222); // Cancel +} + +std::string CGUIDialogPVRClientPriorities::GetSettingsLabel( + const std::shared_ptr<ISetting>& pSetting) +{ + int iClientId = std::atoi(pSetting->GetId().c_str()); + auto clientEntry = m_clients.find(iClientId); + if (clientEntry != m_clients.end()) + return clientEntry->second->GetFriendlyName(); + + CLog::LogF(LOGERROR, "Unable to obtain pvr client with id '{}'", iClientId); + return CGUIDialogSettingsManualBase::GetLocalizedString(13205); // Unknown +} + +void CGUIDialogPVRClientPriorities::InitializeSettings() +{ + CGUIDialogSettingsManualBase::InitializeSettings(); + + const std::shared_ptr<CSettingCategory> category = AddCategory("pvrclientpriorities", -1); + if (category == nullptr) + { + CLog::LogF(LOGERROR, "Unable to add settings category"); + return; + } + + const std::shared_ptr<CSettingGroup> group = AddGroup(category); + if (group == nullptr) + { + CLog::LogF(LOGERROR, "Unable to add settings group"); + return; + } + + m_clients = CServiceBroker::GetPVRManager().Clients()->GetCreatedClients(); + for (const auto& client : m_clients) + { + AddEdit(group, std::to_string(client.second->GetID()), 13205 /* Unknown */, SettingLevel::Basic, + client.second->GetPriority()); + } +} + +void CGUIDialogPVRClientPriorities::OnSettingChanged(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == nullptr) + { + CLog::LogF(LOGERROR, "No setting"); + return; + } + + CGUIDialogSettingsManualBase::OnSettingChanged(setting); + + m_changedValues[setting->GetId()] = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); +} + +bool CGUIDialogPVRClientPriorities::Save() +{ + for (const auto& changedClient : m_changedValues) + { + int iClientId = std::atoi(changedClient.first.c_str()); + auto clientEntry = m_clients.find(iClientId); + if (clientEntry != m_clients.end()) + clientEntry->second->SetPriority(changedClient.second); + } + + return true; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRClientPriorities.h b/xbmc/pvr/dialogs/GUIDialogPVRClientPriorities.h new file mode 100644 index 0000000..a6ca62d --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRClientPriorities.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017-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. + */ + +#pragma once + +#include "pvr/addons/PVRClients.h" +#include "settings/dialogs/GUIDialogSettingsManualBase.h" + +#include <map> +#include <string> + +namespace PVR +{ + class CGUIDialogPVRClientPriorities : public CGUIDialogSettingsManualBase + { + public: + CGUIDialogPVRClientPriorities(); + + protected: + // implementation of ISettingCallback + void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override; + + // specialization of CGUIDialogSettingsBase + std::string GetSettingsLabel(const std::shared_ptr<ISetting>& pSetting) override; + bool AllowResettingSettings() const override { return false; } + bool Save() override; + void SetupView() override; + + // specialization of CGUIDialogSettingsManualBase + void InitializeSettings() override; + + private: + CPVRClientMap m_clients; + std::map<std::string, int> m_changedValues; + }; +} // namespace PVR diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp new file mode 100644 index 0000000..dbc37f3 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp @@ -0,0 +1,546 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRGroupManager.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "dialogs/GUIDialogYesNo.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIMessage.h" +#include "guilib/GUIRadioButtonControl.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "input/actions/Action.h" +#include "input/actions/ActionIDs.h" +#include "messaging/helpers//DialogOKHelper.h" +#include "pvr/PVRManager.h" +#include "pvr/PVRPlaybackState.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRChannelGroupMember.h" +#include "pvr/channels/PVRChannelGroups.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/filesystem/PVRGUIDirectory.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" + +#include <memory> +#include <string> +#include <vector> + +using namespace KODI::MESSAGING; +using namespace PVR; + +#define CONTROL_LIST_CHANNELS_LEFT 11 +#define CONTROL_LIST_CHANNELS_RIGHT 12 +#define CONTROL_LIST_CHANNEL_GROUPS 13 +#define CONTROL_CURRENT_GROUP_LABEL 20 +#define CONTROL_UNGROUPED_LABEL 21 +#define CONTROL_IN_GROUP_LABEL 22 +#define BUTTON_HIDE_GROUP 25 +#define BUTTON_NEWGROUP 26 +#define BUTTON_RENAMEGROUP 27 +#define BUTTON_DELGROUP 28 +#define BUTTON_OK 29 +#define BUTTON_TOGGLE_RADIO_TV 34 +#define BUTTON_RECREATE_GROUP_THUMB 35 + +CGUIDialogPVRGroupManager::CGUIDialogPVRGroupManager() : + CGUIDialog(WINDOW_DIALOG_PVR_GROUP_MANAGER, "DialogPVRGroupManager.xml") +{ + m_ungroupedChannels = new CFileItemList; + m_groupMembers = new CFileItemList; + m_channelGroups = new CFileItemList; + + SetRadio(false); +} + +CGUIDialogPVRGroupManager::~CGUIDialogPVRGroupManager() +{ + delete m_ungroupedChannels; + delete m_groupMembers; + delete m_channelGroups; +} + +void CGUIDialogPVRGroupManager::SetRadio(bool bIsRadio) +{ + m_bIsRadio = bIsRadio; + SetProperty("IsRadio", m_bIsRadio ? "true" : ""); +} + +bool CGUIDialogPVRGroupManager::PersistChanges() +{ + return CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bIsRadio)->PersistAll(); +} + +bool CGUIDialogPVRGroupManager::ActionButtonOk(const CGUIMessage& message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (iControl == BUTTON_OK) + { + PersistChanges(); + Close(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonNewGroup(const CGUIMessage& message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (iControl == BUTTON_NEWGROUP) + { + std::string strGroupName = ""; + /* prompt for a group name */ + if (CGUIKeyboardFactory::ShowAndGetInput(strGroupName, CVariant{g_localizeStrings.Get(19139)}, false)) + { + if (strGroupName != "") + { + /* add the group if it doesn't already exist */ + CPVRChannelGroups* groups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bIsRadio); + if (groups->AddGroup(strGroupName)) + { + CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bIsRadio)->GetByName(strGroupName)->SetGroupType(PVR_GROUP_TYPE_USER_DEFINED); + m_iSelectedChannelGroup = groups->Size() - 1; + Update(); + } + } + } + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonDeleteGroup(const CGUIMessage& message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (iControl == BUTTON_DELGROUP) + { + if (!m_selectedGroup) + return bReturn; + + CGUIDialogYesNo* pDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogYesNo>(WINDOW_DIALOG_YES_NO); + if (!pDialog) + return bReturn; + + pDialog->SetHeading(CVariant{117}); + pDialog->SetLine(0, CVariant{""}); + pDialog->SetLine(1, CVariant{m_selectedGroup->GroupName()}); + pDialog->SetLine(2, CVariant{""}); + pDialog->Open(); + + if (pDialog->IsConfirmed()) + { + ClearSelectedGroupsThumbnail(); + if (CServiceBroker::GetPVRManager() + .ChannelGroups() + ->Get(m_bIsRadio) + ->DeleteGroup(m_selectedGroup)) + Update(); + } + + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonRenameGroup(const CGUIMessage& message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (iControl == BUTTON_RENAMEGROUP) + { + if (!m_selectedGroup) + return bReturn; + + std::string strGroupName(m_selectedGroup->GroupName()); + if (CGUIKeyboardFactory::ShowAndGetInput(strGroupName, CVariant{g_localizeStrings.Get(19139)}, false)) + { + if (!strGroupName.empty()) + { + ClearSelectedGroupsThumbnail(); + m_selectedGroup->SetGroupName(strGroupName); + Update(); + } + } + + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonUngroupedChannels(const CGUIMessage& message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (m_viewUngroupedChannels.HasControl(iControl)) // list/thumb control + { + m_iSelectedUngroupedChannel = m_viewUngroupedChannels.GetSelectedItem(); + int iAction = message.GetParam1(); + + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + if (m_channelGroups->GetFolderCount() == 0) + { + HELPERS::ShowOKDialogText(CVariant{19033}, CVariant{19137}); + } + else if (m_ungroupedChannels->GetFileCount() > 0) + { + CFileItemPtr pItemChannel = m_ungroupedChannels->Get(m_iSelectedUngroupedChannel); + + if (m_selectedGroup->AppendToGroup(pItemChannel->GetPVRChannelInfoTag())) + { + ClearSelectedGroupsThumbnail(); + Update(); + } + } + } + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonGroupMembers(const CGUIMessage& message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (m_viewGroupMembers.HasControl(iControl)) // list/thumb control + { + m_iSelectedGroupMember = m_viewGroupMembers.GetSelectedItem(); + int iAction = message.GetParam1(); + + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + if (m_selectedGroup && m_groupMembers->GetFileCount() > 0) + { + CFileItemPtr pItemChannel = m_groupMembers->Get(m_iSelectedGroupMember); + m_selectedGroup->RemoveFromGroup(pItemChannel->GetPVRChannelInfoTag()); + ClearSelectedGroupsThumbnail(); + Update(); + } + } + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonChannelGroups(const CGUIMessage& message) +{ + bool bReturn = false; + unsigned int iControl = message.GetSenderId(); + + if (m_viewChannelGroups.HasControl(iControl)) // list/thumb control + { + int iAction = message.GetParam1(); + + if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK) + { + m_iSelectedChannelGroup = m_viewChannelGroups.GetSelectedItem(); + Update(); + } + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonHideGroup(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == BUTTON_HIDE_GROUP && m_selectedGroup) + { + CGUIRadioButtonControl* button = static_cast<CGUIRadioButtonControl*>(GetControl(message.GetSenderId())); + if (button) + { + CServiceBroker::GetPVRManager() + .ChannelGroups() + ->Get(m_bIsRadio) + ->HideGroup(m_selectedGroup, button->IsSelected()); + Update(); + } + + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonToggleRadioTV(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == BUTTON_TOGGLE_RADIO_TV) + { + PersistChanges(); + SetRadio(!m_bIsRadio); + Update(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::ActionButtonRecreateThumbnail(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == BUTTON_RECREATE_GROUP_THUMB) + { + m_thumbLoader.ClearCachedImages(*m_channelGroups); + Update(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::OnMessageClick(const CGUIMessage& message) +{ + return ActionButtonOk(message) || + ActionButtonNewGroup(message) || + ActionButtonDeleteGroup(message) || + ActionButtonRenameGroup(message) || + ActionButtonUngroupedChannels(message) || + ActionButtonGroupMembers(message) || + ActionButtonChannelGroups(message) || + ActionButtonHideGroup(message) || + ActionButtonToggleRadioTV(message) || + ActionButtonRecreateThumbnail(message); +} + +bool CGUIDialogPVRGroupManager::OnMessage(CGUIMessage& message) +{ + unsigned int iMessage = message.GetMessage(); + + switch (iMessage) + { + case GUI_MSG_CLICKED: + { + OnMessageClick(message); + } + break; + } + + return CGUIDialog::OnMessage(message); +} + +bool CGUIDialogPVRGroupManager::OnActionMove(const CAction& action) +{ + bool bReturn = false; + int iActionId = action.GetID(); + + if (GetFocusedControlID() == CONTROL_LIST_CHANNEL_GROUPS) + { + if (iActionId == ACTION_MOUSE_MOVE) + { + int iSelected = m_viewChannelGroups.GetSelectedItem(); + if (m_iSelectedChannelGroup < iSelected) + { + iActionId = ACTION_MOVE_DOWN; + } + else if (m_iSelectedChannelGroup > iSelected) + { + iActionId = ACTION_MOVE_UP; + } + else + { + return bReturn; + } + } + + if (iActionId == ACTION_MOVE_DOWN || iActionId == ACTION_MOVE_UP || + iActionId == ACTION_PAGE_DOWN || iActionId == ACTION_PAGE_UP || + iActionId == ACTION_FIRST_PAGE || iActionId == ACTION_LAST_PAGE) + { + CGUIDialog::OnAction(action); + int iSelected = m_viewChannelGroups.GetSelectedItem(); + + bReturn = true; + if (iSelected != m_iSelectedChannelGroup) + { + m_iSelectedChannelGroup = iSelected; + Update(); + } + } + } + + return bReturn; +} + +bool CGUIDialogPVRGroupManager::OnAction(const CAction& action) +{ + return OnActionMove(action) || + CGUIDialog::OnAction(action); +} + +void CGUIDialogPVRGroupManager::OnInitWindow() +{ + CGUIDialog::OnInitWindow(); + m_iSelectedUngroupedChannel = 0; + m_iSelectedGroupMember = 0; + m_iSelectedChannelGroup = 0; + Update(); +} + +void CGUIDialogPVRGroupManager::OnDeinitWindow(int nextWindowID) +{ + Clear(); + CGUIDialog::OnDeinitWindow(nextWindowID); +} + +void CGUIDialogPVRGroupManager::OnWindowLoaded() +{ + CGUIDialog::OnWindowLoaded(); + + m_viewUngroupedChannels.Reset(); + m_viewUngroupedChannels.SetParentWindow(GetID()); + m_viewUngroupedChannels.AddView(GetControl(CONTROL_LIST_CHANNELS_LEFT)); + + m_viewGroupMembers.Reset(); + m_viewGroupMembers.SetParentWindow(GetID()); + m_viewGroupMembers.AddView(GetControl(CONTROL_LIST_CHANNELS_RIGHT)); + + m_viewChannelGroups.Reset(); + m_viewChannelGroups.SetParentWindow(GetID()); + m_viewChannelGroups.AddView(GetControl(CONTROL_LIST_CHANNEL_GROUPS)); +} + +void CGUIDialogPVRGroupManager::OnWindowUnload() +{ + CGUIDialog::OnWindowUnload(); + m_viewUngroupedChannels.Reset(); + m_viewGroupMembers.Reset(); + m_viewChannelGroups.Reset(); +} + +void CGUIDialogPVRGroupManager::Update() +{ + m_viewUngroupedChannels.SetCurrentView(CONTROL_LIST_CHANNELS_LEFT); + m_viewGroupMembers.SetCurrentView(CONTROL_LIST_CHANNELS_RIGHT); + m_viewChannelGroups.SetCurrentView(CONTROL_LIST_CHANNEL_GROUPS); + + Clear(); + + // get the groups list + CPVRGUIDirectory::GetChannelGroupsDirectory(m_bIsRadio, false, *m_channelGroups); + + // Load group thumbnails + m_thumbLoader.Load(*m_channelGroups); + + m_viewChannelGroups.SetItems(*m_channelGroups); + m_viewChannelGroups.SetSelectedItem(m_iSelectedChannelGroup); + + /* select a group or select the default group if no group was selected */ + CFileItemPtr pItem = m_channelGroups->Get(m_viewChannelGroups.GetSelectedItem()); + m_selectedGroup = CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bIsRadio)->GetByName(pItem->m_strTitle); + if (m_selectedGroup) + { + /* set this group in the pvrmanager, so it becomes the selected group in other dialogs too */ + CServiceBroker::GetPVRManager().PlaybackState()->SetActiveChannelGroup(m_selectedGroup); + SET_CONTROL_LABEL(CONTROL_CURRENT_GROUP_LABEL, m_selectedGroup->GroupName()); + SET_CONTROL_SELECTED(GetID(), BUTTON_HIDE_GROUP, m_selectedGroup->IsHidden()); + + CONTROL_ENABLE_ON_CONDITION(BUTTON_DELGROUP, !m_selectedGroup->IsInternalGroup()); + + if (m_selectedGroup->IsInternalGroup()) + { + std::string strNewLabel = StringUtils::Format("{} {}", g_localizeStrings.Get(19022), + m_bIsRadio ? g_localizeStrings.Get(19024) + : g_localizeStrings.Get(19023)); + SET_CONTROL_LABEL(CONTROL_UNGROUPED_LABEL, strNewLabel); + + strNewLabel = StringUtils::Format("{} {}", g_localizeStrings.Get(19218), + m_bIsRadio ? g_localizeStrings.Get(19024) + : g_localizeStrings.Get(19023)); + SET_CONTROL_LABEL(CONTROL_IN_GROUP_LABEL, strNewLabel); + } + else + { + std::string strNewLabel = g_localizeStrings.Get(19219); + SET_CONTROL_LABEL(CONTROL_UNGROUPED_LABEL, strNewLabel); + + strNewLabel = + StringUtils::Format("{} {}", g_localizeStrings.Get(19220), m_selectedGroup->GroupName()); + SET_CONTROL_LABEL(CONTROL_IN_GROUP_LABEL, strNewLabel); + } + + // Slightly different handling for "all" group... + if (m_selectedGroup->IsInternalGroup()) + { + const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers = + m_selectedGroup->GetMembers(CPVRChannelGroup::Include::ALL); + for (const auto& groupMember : groupMembers) + { + if (groupMember->Channel()->IsHidden()) + m_ungroupedChannels->Add(std::make_shared<CFileItem>(groupMember)); + else + m_groupMembers->Add(std::make_shared<CFileItem>(groupMember)); + } + } + else + { + const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers = + m_selectedGroup->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE); + for (const auto& groupMember : groupMembers) + { + m_groupMembers->Add(std::make_shared<CFileItem>(groupMember)); + } + + /* for the center part, get all channels of the "all" channels group that are not in this group */ + const std::shared_ptr<CPVRChannelGroup> allGroup = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bIsRadio); + const std::vector<std::shared_ptr<CPVRChannelGroupMember>> allGroupMembers = + allGroup->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE); + for (const auto& groupMember : allGroupMembers) + { + if (!m_selectedGroup->IsGroupMember(groupMember->Channel())) + m_ungroupedChannels->Add(std::make_shared<CFileItem>(groupMember)); + } + } + m_viewGroupMembers.SetItems(*m_groupMembers); + m_viewGroupMembers.SetSelectedItem(m_iSelectedGroupMember); + + m_viewUngroupedChannels.SetItems(*m_ungroupedChannels); + m_viewUngroupedChannels.SetSelectedItem(m_iSelectedUngroupedChannel); + } +} + +void CGUIDialogPVRGroupManager::Clear() +{ + if (m_thumbLoader.IsLoading()) + m_thumbLoader.StopThread(); + + m_viewUngroupedChannels.Clear(); + m_viewGroupMembers.Clear(); + m_viewChannelGroups.Clear(); + + m_ungroupedChannels->Clear(); + m_groupMembers->Clear(); + m_channelGroups->Clear(); +} + +void CGUIDialogPVRGroupManager::ClearSelectedGroupsThumbnail() +{ + m_thumbLoader.ClearCachedImage(*m_channelGroups->Get(m_iSelectedChannelGroup)); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h new file mode 100644 index 0000000..524205a --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "guilib/GUIDialog.h" +#include "pvr/PVRThumbLoader.h" +#include "view/GUIViewControl.h" + +#include <memory> + +class CFileItemList; +class CGUIMessage; + +namespace PVR +{ + class CPVRChannelGroup; + + class CGUIDialogPVRGroupManager : public CGUIDialog + { + public: + CGUIDialogPVRGroupManager(); + ~CGUIDialogPVRGroupManager() override; + bool OnMessage(CGUIMessage& message) override; + bool OnAction(const CAction& action) override; + void OnWindowLoaded() override; + void OnWindowUnload() override; + + void SetRadio(bool bIsRadio); + + protected: + void OnInitWindow() override; + void OnDeinitWindow(int nextWindowID) override; + + private: + void Clear(); + void ClearSelectedGroupsThumbnail(); + void Update(); + bool PersistChanges(); + bool ActionButtonOk(const CGUIMessage& message); + bool ActionButtonNewGroup(const CGUIMessage& message); + bool ActionButtonDeleteGroup(const CGUIMessage& message); + bool ActionButtonRenameGroup(const CGUIMessage& message); + bool ActionButtonUngroupedChannels(const CGUIMessage& message); + bool ActionButtonGroupMembers(const CGUIMessage& message); + bool ActionButtonChannelGroups(const CGUIMessage& message); + bool ActionButtonHideGroup(const CGUIMessage& message); + bool ActionButtonToggleRadioTV(const CGUIMessage& message); + bool ActionButtonRecreateThumbnail(const CGUIMessage& message); + bool OnMessageClick(const CGUIMessage& message); + bool OnActionMove(const CAction& action); + + std::shared_ptr<CPVRChannelGroup> m_selectedGroup; + bool m_bIsRadio; + + int m_iSelectedUngroupedChannel = 0; + int m_iSelectedGroupMember = 0; + int m_iSelectedChannelGroup = 0; + + CFileItemList * m_ungroupedChannels; + CFileItemList * m_groupMembers; + CFileItemList * m_channelGroups; + + CGUIViewControl m_viewUngroupedChannels; + CGUIViewControl m_viewGroupMembers; + CGUIViewControl m_viewChannelGroups; + + CPVRThumbLoader m_thumbLoader; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideControls.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideControls.cpp new file mode 100644 index 0000000..2f41af5 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideControls.cpp @@ -0,0 +1,19 @@ +/* + * 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 "GUIDialogPVRGuideControls.h" + +using namespace PVR; + +CGUIDialogPVRGuideControls::CGUIDialogPVRGuideControls() + : CGUIDialog(WINDOW_DIALOG_PVR_GUIDE_CONTROLS, "DialogPVRGuideControls.xml") +{ + m_loadType = KEEP_IN_MEMORY; +} + +CGUIDialogPVRGuideControls::~CGUIDialogPVRGuideControls() = default; diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideControls.h b/xbmc/pvr/dialogs/GUIDialogPVRGuideControls.h new file mode 100644 index 0000000..9b81f2b --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideControls.h @@ -0,0 +1,21 @@ +/* + * 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. + */ + +#pragma once + +#include "guilib/GUIDialog.h" + +namespace PVR +{ +class CGUIDialogPVRGuideControls : public CGUIDialog +{ +public: + CGUIDialogPVRGuideControls(); + ~CGUIDialogPVRGuideControls() override; +}; +} // namespace PVR diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp new file mode 100644 index 0000000..9bce21e --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRGuideInfo.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "guilib/GUIMessage.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClient.h" +#include "pvr/epg/EpgInfoTag.h" +#include "pvr/guilib/PVRGUIActionsEPG.h" +#include "pvr/guilib/PVRGUIActionsPlayback.h" +#include "pvr/guilib/PVRGUIActionsTimers.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimerInfoTag.h" +#include "pvr/timers/PVRTimers.h" + +#include <memory> + +using namespace PVR; + +#define CONTROL_BTN_FIND 4 +#define CONTROL_BTN_SWITCH 5 +#define CONTROL_BTN_RECORD 6 +#define CONTROL_BTN_OK 7 +#define CONTROL_BTN_PLAY_RECORDING 8 +#define CONTROL_BTN_ADD_TIMER 9 +#define CONTROL_BTN_PLAY_EPGTAG 10 +#define CONTROL_BTN_SET_REMINDER 11 + +CGUIDialogPVRGuideInfo::CGUIDialogPVRGuideInfo() + : CGUIDialog(WINDOW_DIALOG_PVR_GUIDE_INFO, "DialogPVRInfo.xml") +{ +} + +CGUIDialogPVRGuideInfo::~CGUIDialogPVRGuideInfo() = default; + +bool CGUIDialogPVRGuideInfo::OnClickButtonOK(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_OK) + { + Close(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnClickButtonRecord(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_RECORD) + { + auto& mgr = CServiceBroker::GetPVRManager(); + + const std::shared_ptr<CPVRTimerInfoTag> timerTag = + mgr.Timers()->GetTimerForEpgTag(m_progItem->GetEPGInfoTag()); + if (timerTag) + { + if (timerTag->IsRecording()) + bReturn = mgr.Get<PVR::GUI::Timers>().StopRecording(CFileItem(timerTag)); + else + bReturn = mgr.Get<PVR::GUI::Timers>().DeleteTimer(CFileItem(timerTag)); + } + else + { + bReturn = mgr.Get<PVR::GUI::Timers>().AddTimer(*m_progItem, false); + } + } + + if (bReturn) + Close(); + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnClickButtonAddTimer(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_ADD_TIMER) + { + auto& mgr = CServiceBroker::GetPVRManager(); + if (m_progItem && !mgr.Timers()->GetTimerForEpgTag(m_progItem->GetEPGInfoTag())) + { + bReturn = mgr.Get<PVR::GUI::Timers>().AddTimerRule(*m_progItem, true, true); + } + } + + if (bReturn) + Close(); + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnClickButtonSetReminder(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_SET_REMINDER) + { + auto& mgr = CServiceBroker::GetPVRManager(); + if (m_progItem && !mgr.Timers()->GetTimerForEpgTag(m_progItem->GetEPGInfoTag())) + { + bReturn = mgr.Get<PVR::GUI::Timers>().AddReminder(*m_progItem); + } + } + + if (bReturn) + Close(); + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnClickButtonPlay(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_SWITCH || + message.GetSenderId() == CONTROL_BTN_PLAY_RECORDING || + message.GetSenderId() == CONTROL_BTN_PLAY_EPGTAG) + { + Close(); + + if (m_progItem) + { + if (message.GetSenderId() == CONTROL_BTN_PLAY_RECORDING) + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording( + *m_progItem, true /* bCheckResume */); + else if (message.GetSenderId() == CONTROL_BTN_PLAY_EPGTAG && + m_progItem->GetEPGInfoTag()->IsPlayable()) + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayEpgTag(*m_progItem); + else + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel( + *m_progItem, true /* bCheckResume */); + + bReturn = true; + } + } + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnClickButtonFind(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_FIND) + { + Close(); + if (m_progItem) + return CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().FindSimilar(*m_progItem); + } + + return bReturn; +} + +bool CGUIDialogPVRGuideInfo::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_CLICKED: + return OnClickButtonOK(message) || OnClickButtonRecord(message) || + OnClickButtonPlay(message) || OnClickButtonFind(message) || + OnClickButtonAddTimer(message) || OnClickButtonSetReminder(message); + } + + return CGUIDialog::OnMessage(message); +} + +bool CGUIDialogPVRGuideInfo::OnInfo(int actionID) +{ + Close(); + return true; +} + +void CGUIDialogPVRGuideInfo::SetProgInfo(const std::shared_ptr<CFileItem>& item) +{ + m_progItem = item; +} + +CFileItemPtr CGUIDialogPVRGuideInfo::GetCurrentListItem(int offset) +{ + return m_progItem; +} + +void CGUIDialogPVRGuideInfo::OnInitWindow() +{ + CGUIDialog::OnInitWindow(); + + if (!m_progItem) + { + /* no epg event selected */ + return; + } + + auto& mgr = CServiceBroker::GetPVRManager(); + const auto epgTag = m_progItem->GetEPGInfoTag(); + + if (!mgr.Recordings()->GetRecordingForEpgTag(epgTag)) + { + /* not recording. hide the play recording button */ + SET_CONTROL_HIDDEN(CONTROL_BTN_PLAY_RECORDING); + } + + bool bHideRecord = true; + bool bHideAddTimer = true; + const std::shared_ptr<CPVRTimerInfoTag> timer = mgr.Timers()->GetTimerForEpgTag(epgTag); + bool bHideSetReminder = timer || (epgTag->StartAsLocalTime() <= CDateTime::GetCurrentDateTime()); + + if (timer) + { + if (timer->IsRecording()) + { + SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19059); /* Stop recording */ + bHideRecord = false; + } + else if (!timer->GetTimerType()->IsReadOnly()) + { + SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 19060); /* Delete timer */ + bHideRecord = false; + } + } + else if (epgTag->IsRecordable()) + { + const std::shared_ptr<CPVRClient> client = mgr.GetClient(epgTag->ClientID()); + if (client && client->GetClientCapabilities().SupportsTimers()) + { + SET_CONTROL_LABEL(CONTROL_BTN_RECORD, 264); /* Record */ + bHideRecord = false; + bHideAddTimer = false; + } + } + + if (!epgTag->IsPlayable()) + SET_CONTROL_HIDDEN(CONTROL_BTN_PLAY_EPGTAG); + + if (bHideRecord) + SET_CONTROL_HIDDEN(CONTROL_BTN_RECORD); + + if (bHideAddTimer) + SET_CONTROL_HIDDEN(CONTROL_BTN_ADD_TIMER); + + if (bHideSetReminder) + SET_CONTROL_HIDDEN(CONTROL_BTN_SET_REMINDER); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h new file mode 100644 index 0000000..8d9bda5 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "guilib/GUIDialog.h" + +#include <memory> + +class CGUIMessage; + +namespace PVR +{ +class CGUIDialogPVRGuideInfo : public CGUIDialog +{ +public: + CGUIDialogPVRGuideInfo(); + ~CGUIDialogPVRGuideInfo() override; + bool OnMessage(CGUIMessage& message) override; + bool OnInfo(int actionID) override; + bool HasListItems() const override { return true; } + CFileItemPtr GetCurrentListItem(int offset = 0) override; + + void SetProgInfo(const std::shared_ptr<CFileItem>& item); + +protected: + void OnInitWindow() override; + +private: + bool OnClickButtonOK(const CGUIMessage& message); + bool OnClickButtonRecord(const CGUIMessage& message); + bool OnClickButtonPlay(const CGUIMessage& message); + bool OnClickButtonFind(const CGUIMessage& message); + bool OnClickButtonAddTimer(const CGUIMessage& message); + bool OnClickButtonSetReminder(const CGUIMessage& message); + + std::shared_ptr<CFileItem> m_progItem; +}; +} // namespace PVR diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp new file mode 100644 index 0000000..4cf7f2f --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRGuideSearch.h" + +#include "ServiceBroker.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUIKeyboardFactory.h" +#include "guilib/GUIMessage.h" +#include "guilib/LocalizeStrings.h" +#include "pvr/PVRManager.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRChannelGroupMember.h" +#include "pvr/channels/PVRChannelGroups.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/epg/EpgContainer.h" +#include "pvr/epg/EpgSearchFilter.h" +#include "utils/StringUtils.h" + +#include <string> +#include <utility> +#include <vector> + +using namespace PVR; + +#define CONTROL_EDIT_SEARCH 9 +#define CONTROL_BTN_INC_DESC 10 +#define CONTROL_BTN_CASE_SENS 11 +#define CONTROL_SPIN_MIN_DURATION 12 +#define CONTROL_SPIN_MAX_DURATION 13 +#define CONTROL_EDIT_START_DATE 14 +#define CONTROL_EDIT_STOP_DATE 15 +#define CONTROL_EDIT_START_TIME 16 +#define CONTROL_EDIT_STOP_TIME 17 +#define CONTROL_SPIN_GENRE 18 +#define CONTROL_SPIN_NO_REPEATS 19 +#define CONTROL_BTN_UNK_GENRE 20 +#define CONTROL_SPIN_GROUPS 21 +#define CONTROL_BTN_FTA_ONLY 22 +#define CONTROL_SPIN_CHANNELS 23 +#define CONTROL_BTN_IGNORE_TMR 24 +#define CONTROL_BTN_CANCEL 25 +#define CONTROL_BTN_SEARCH 26 +#define CONTROL_BTN_IGNORE_REC 27 +#define CONTROL_BTN_DEFAULTS 28 +static constexpr int CONTROL_BTN_SAVE = 29; +static constexpr int CONTROL_BTN_IGNORE_FINISHED = 30; +static constexpr int CONTROL_BTN_IGNORE_FUTURE = 31; + +CGUIDialogPVRGuideSearch::CGUIDialogPVRGuideSearch() + : CGUIDialog(WINDOW_DIALOG_PVR_GUIDE_SEARCH, "DialogPVRGuideSearch.xml") +{ +} + +void CGUIDialogPVRGuideSearch::SetFilterData( + const std::shared_ptr<CPVREpgSearchFilter>& searchFilter) +{ + m_searchFilter = searchFilter; +} + +void CGUIDialogPVRGuideSearch::UpdateChannelSpin() +{ + int iChannelGroup = GetSpinValue(CONTROL_SPIN_GROUPS); + + std::vector< std::pair<std::string, int> > labels; + if (m_searchFilter->IsRadio()) + labels.emplace_back(g_localizeStrings.Get(19216), EPG_SEARCH_UNSET); // All radio channels + else + labels.emplace_back(g_localizeStrings.Get(19217), EPG_SEARCH_UNSET); // All TV channels + + std::shared_ptr<CPVRChannelGroup> group; + if (iChannelGroup != EPG_SEARCH_UNSET) + group = CServiceBroker::GetPVRManager().ChannelGroups()->GetByIdFromAll(iChannelGroup); + + if (!group) + group = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_searchFilter->IsRadio()); + + m_channelsMap.clear(); + const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers = + group->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE); + int iIndex = 0; + int iSelectedChannel = EPG_SEARCH_UNSET; + for (const auto& groupMember : groupMembers) + { + labels.emplace_back(std::make_pair(groupMember->Channel()->ChannelName(), iIndex)); + m_channelsMap.insert(std::make_pair(iIndex, groupMember)); + + if (iSelectedChannel == EPG_SEARCH_UNSET && + groupMember->ChannelUID() == m_searchFilter->GetChannelUID() && + groupMember->ClientID() == m_searchFilter->GetClientID()) + iSelectedChannel = iIndex; + + ++iIndex; + } + + SET_CONTROL_LABELS(CONTROL_SPIN_CHANNELS, iSelectedChannel, &labels); +} + +void CGUIDialogPVRGuideSearch::UpdateGroupsSpin() +{ + std::vector<std::pair<std::string, int>> labels; + const std::vector<std::shared_ptr<CPVRChannelGroup>> groups = + CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_searchFilter->IsRadio())->GetMembers(); + int selectedGroup = EPG_SEARCH_UNSET; + for (const auto& group : groups) + { + labels.emplace_back(group->GroupName(), group->GroupID()); + + if (selectedGroup == EPG_SEARCH_UNSET && + group->GroupID() == m_searchFilter->GetChannelGroupID()) + selectedGroup = group->GroupID(); + } + + SET_CONTROL_LABELS(CONTROL_SPIN_GROUPS, selectedGroup, &labels); +} + +void CGUIDialogPVRGuideSearch::UpdateGenreSpin() +{ + std::vector< std::pair<std::string, int> > labels; + labels.emplace_back(g_localizeStrings.Get(593), EPG_SEARCH_UNSET); + labels.emplace_back(g_localizeStrings.Get(19500), EPG_EVENT_CONTENTMASK_MOVIEDRAMA); + labels.emplace_back(g_localizeStrings.Get(19516), EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS); + labels.emplace_back(g_localizeStrings.Get(19532), EPG_EVENT_CONTENTMASK_SHOW); + labels.emplace_back(g_localizeStrings.Get(19548), EPG_EVENT_CONTENTMASK_SPORTS); + labels.emplace_back(g_localizeStrings.Get(19564), EPG_EVENT_CONTENTMASK_CHILDRENYOUTH); + labels.emplace_back(g_localizeStrings.Get(19580), EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE); + labels.emplace_back(g_localizeStrings.Get(19596), EPG_EVENT_CONTENTMASK_ARTSCULTURE); + labels.emplace_back(g_localizeStrings.Get(19612), EPG_EVENT_CONTENTMASK_SOCIALPOLITICALECONOMICS); + labels.emplace_back(g_localizeStrings.Get(19628), EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE); + labels.emplace_back(g_localizeStrings.Get(19644), EPG_EVENT_CONTENTMASK_LEISUREHOBBIES); + labels.emplace_back(g_localizeStrings.Get(19660), EPG_EVENT_CONTENTMASK_SPECIAL); + labels.emplace_back(g_localizeStrings.Get(19499), EPG_EVENT_CONTENTMASK_USERDEFINED); + + SET_CONTROL_LABELS(CONTROL_SPIN_GENRE, m_searchFilter->GetGenreType(), &labels); +} + +void CGUIDialogPVRGuideSearch::UpdateDurationSpin() +{ + /* minimum duration */ + std::vector< std::pair<std::string, int> > labels; + + labels.emplace_back("-", EPG_SEARCH_UNSET); + for (int i = 1; i < 12*60/5; ++i) + labels.emplace_back(StringUtils::Format(g_localizeStrings.Get(14044), i * 5), i * 5); + + SET_CONTROL_LABELS(CONTROL_SPIN_MIN_DURATION, m_searchFilter->GetMinimumDuration(), &labels); + + /* maximum duration */ + labels.clear(); + + labels.emplace_back("-", EPG_SEARCH_UNSET); + for (int i = 1; i < 12*60/5; ++i) + labels.emplace_back(StringUtils::Format(g_localizeStrings.Get(14044), i * 5), i * 5); + + SET_CONTROL_LABELS(CONTROL_SPIN_MAX_DURATION, m_searchFilter->GetMaximumDuration(), &labels); +} + +bool CGUIDialogPVRGuideSearch::OnMessage(CGUIMessage& message) +{ + CGUIDialog::OnMessage(message); + + switch (message.GetMessage()) + { + case GUI_MSG_CLICKED: + { + int iControl = message.GetSenderId(); + if (iControl == CONTROL_BTN_SEARCH) + { + // Read data from controls, update m_searchfilter accordingly + UpdateSearchFilter(); + + m_result = Result::SEARCH; + Close(); + return true; + } + else if (iControl == CONTROL_BTN_CANCEL) + { + m_result = Result::CANCEL; + Close(); + return true; + } + else if (iControl == CONTROL_BTN_DEFAULTS) + { + if (m_searchFilter) + { + m_searchFilter->Reset(); + Update(); + } + return true; + } + else if (iControl == CONTROL_BTN_SAVE) + { + // Read data from controls, update m_searchfilter accordingly + UpdateSearchFilter(); + + std::string title = m_searchFilter->GetTitle(); + if (title.empty()) + { + title = m_searchFilter->GetSearchTerm(); + if (title.empty()) + title = g_localizeStrings.Get(137); // "Search" + else + StringUtils::Trim(title, "\""); + + if (!CGUIKeyboardFactory::ShowAndGetInput( + title, CVariant{g_localizeStrings.Get(528)}, // "Enter title" + false)) + { + return false; + } + m_searchFilter->SetTitle(title); + } + + m_result = Result::SAVE; + Close(); + return true; + } + else if (iControl == CONTROL_SPIN_GROUPS) + { + UpdateChannelSpin(); + return true; + } + } + break; + } + + return false; +} + +void CGUIDialogPVRGuideSearch::OnInitWindow() +{ + CGUIDialog::OnInitWindow(); + + m_result = Result::CANCEL; +} + +void CGUIDialogPVRGuideSearch::OnWindowLoaded() +{ + Update(); + return CGUIDialog::OnWindowLoaded(); +} + +CDateTime CGUIDialogPVRGuideSearch::ReadDateTime(const std::string& strDate, const std::string& strTime) const +{ + CDateTime dateTime; + int iHours, iMinutes; + sscanf(strTime.c_str(), "%d:%d", &iHours, &iMinutes); + dateTime.SetFromDBDate(strDate); + dateTime.SetDateTime(dateTime.GetYear(), dateTime.GetMonth(), dateTime.GetDay(), iHours, iMinutes, 0); + return dateTime.GetAsUTCDateTime(); +} + +bool CGUIDialogPVRGuideSearch::IsRadioSelected(int controlID) +{ + CGUIMessage msg(GUI_MSG_IS_SELECTED, GetID(), controlID); + OnMessage(msg); + return (msg.GetParam1() == 1); +} + +int CGUIDialogPVRGuideSearch::GetSpinValue(int controlID) +{ + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), controlID); + OnMessage(msg); + return msg.GetParam1(); +} + +std::string CGUIDialogPVRGuideSearch::GetEditValue(int controlID) +{ + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), controlID); + OnMessage(msg); + return msg.GetLabel(); +} + +void CGUIDialogPVRGuideSearch::UpdateSearchFilter() +{ + if (!m_searchFilter) + return; + + m_searchFilter->SetSearchTerm(GetEditValue(CONTROL_EDIT_SEARCH)); + + m_searchFilter->SetSearchInDescription(IsRadioSelected(CONTROL_BTN_INC_DESC)); + m_searchFilter->SetCaseSensitive(IsRadioSelected(CONTROL_BTN_CASE_SENS)); + m_searchFilter->SetFreeToAirOnly(IsRadioSelected(CONTROL_BTN_FTA_ONLY)); + m_searchFilter->SetIncludeUnknownGenres(IsRadioSelected(CONTROL_BTN_UNK_GENRE)); + m_searchFilter->SetIgnorePresentRecordings(IsRadioSelected(CONTROL_BTN_IGNORE_REC)); + m_searchFilter->SetIgnorePresentTimers(IsRadioSelected(CONTROL_BTN_IGNORE_TMR)); + m_searchFilter->SetRemoveDuplicates(IsRadioSelected(CONTROL_SPIN_NO_REPEATS)); + m_searchFilter->SetIgnoreFinishedBroadcasts(IsRadioSelected(CONTROL_BTN_IGNORE_FINISHED)); + m_searchFilter->SetIgnoreFutureBroadcasts(IsRadioSelected(CONTROL_BTN_IGNORE_FUTURE)); + m_searchFilter->SetGenreType(GetSpinValue(CONTROL_SPIN_GENRE)); + m_searchFilter->SetMinimumDuration(GetSpinValue(CONTROL_SPIN_MIN_DURATION)); + m_searchFilter->SetMaximumDuration(GetSpinValue(CONTROL_SPIN_MAX_DURATION)); + + auto it = m_channelsMap.find(GetSpinValue(CONTROL_SPIN_CHANNELS)); + m_searchFilter->SetClientID(it == m_channelsMap.end() ? -1 : (*it).second->ClientID()); + m_searchFilter->SetChannelUID(it == m_channelsMap.end() ? -1 : (*it).second->ChannelUID()); + m_searchFilter->SetChannelGroupID(GetSpinValue(CONTROL_SPIN_GROUPS)); + + const CDateTime start = + ReadDateTime(GetEditValue(CONTROL_EDIT_START_DATE), GetEditValue(CONTROL_EDIT_START_TIME)); + if (start != m_startDateTime) + { + m_searchFilter->SetStartDateTime(start); + m_startDateTime = start; + } + const CDateTime end = + ReadDateTime(GetEditValue(CONTROL_EDIT_STOP_DATE), GetEditValue(CONTROL_EDIT_STOP_TIME)); + if (end != m_endDateTime) + { + m_searchFilter->SetEndDateTime(end); + m_endDateTime = end; + } +} + +void CGUIDialogPVRGuideSearch::Update() +{ + if (!m_searchFilter) + return; + + SET_CONTROL_LABEL2(CONTROL_EDIT_SEARCH, m_searchFilter->GetSearchTerm()); + { + CGUIMessage msg(GUI_MSG_SET_TYPE, GetID(), CONTROL_EDIT_SEARCH, CGUIEditControl::INPUT_TYPE_TEXT, 16017); + OnMessage(msg); + } + + SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_CASE_SENS, m_searchFilter->IsCaseSensitive()); + SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_INC_DESC, m_searchFilter->ShouldSearchInDescription()); + SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_FTA_ONLY, m_searchFilter->IsFreeToAirOnly()); + SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_UNK_GENRE, m_searchFilter->ShouldIncludeUnknownGenres()); + SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_IGNORE_REC, m_searchFilter->ShouldIgnorePresentRecordings()); + SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_IGNORE_TMR, m_searchFilter->ShouldIgnorePresentTimers()); + SET_CONTROL_SELECTED(GetID(), CONTROL_SPIN_NO_REPEATS, m_searchFilter->ShouldRemoveDuplicates()); + SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_IGNORE_FINISHED, + m_searchFilter->ShouldIgnoreFinishedBroadcasts()); + SET_CONTROL_SELECTED(GetID(), CONTROL_BTN_IGNORE_FUTURE, + m_searchFilter->ShouldIgnoreFutureBroadcasts()); + + // Set start/end datetime fields + m_startDateTime = m_searchFilter->GetStartDateTime(); + m_endDateTime = m_searchFilter->GetEndDateTime(); + if (!m_startDateTime.IsValid() || !m_endDateTime.IsValid()) + { + const auto dates = CServiceBroker::GetPVRManager().EpgContainer().GetFirstAndLastEPGDate(); + if (!m_startDateTime.IsValid()) + m_startDateTime = dates.first; + if (!m_endDateTime.IsValid()) + m_endDateTime = dates.second; + } + + if (!m_startDateTime.IsValid()) + m_startDateTime = CDateTime::GetUTCDateTime(); + + if (!m_endDateTime.IsValid()) + m_endDateTime = m_startDateTime + CDateTimeSpan(10, 0, 0, 0); // default to start + 10 days + + CDateTime startLocal; + startLocal.SetFromUTCDateTime(m_startDateTime); + CDateTime endLocal; + endLocal.SetFromUTCDateTime(m_endDateTime); + + SET_CONTROL_LABEL2(CONTROL_EDIT_START_TIME, startLocal.GetAsLocalizedTime("", false)); + { + CGUIMessage msg(GUI_MSG_SET_TYPE, GetID(), CONTROL_EDIT_START_TIME, CGUIEditControl::INPUT_TYPE_TIME, 14066); + OnMessage(msg); + } + SET_CONTROL_LABEL2(CONTROL_EDIT_STOP_TIME, endLocal.GetAsLocalizedTime("", false)); + { + CGUIMessage msg(GUI_MSG_SET_TYPE, GetID(), CONTROL_EDIT_STOP_TIME, CGUIEditControl::INPUT_TYPE_TIME, 14066); + OnMessage(msg); + } + SET_CONTROL_LABEL2(CONTROL_EDIT_START_DATE, startLocal.GetAsDBDate()); + { + CGUIMessage msg(GUI_MSG_SET_TYPE, GetID(), CONTROL_EDIT_START_DATE, CGUIEditControl::INPUT_TYPE_DATE, 14067); + OnMessage(msg); + } + SET_CONTROL_LABEL2(CONTROL_EDIT_STOP_DATE, endLocal.GetAsDBDate()); + { + CGUIMessage msg(GUI_MSG_SET_TYPE, GetID(), CONTROL_EDIT_STOP_DATE, CGUIEditControl::INPUT_TYPE_DATE, 14067); + OnMessage(msg); + } + + UpdateDurationSpin(); + UpdateGroupsSpin(); + UpdateChannelSpin(); + UpdateGenreSpin(); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h new file mode 100644 index 0000000..4d02167 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "XBDateTime.h" +#include "guilib/GUIDialog.h" + +#include <map> +#include <memory> +#include <string> + +namespace PVR +{ + class CPVREpgSearchFilter; + class CPVRChannelGroupMember; + + class CGUIDialogPVRGuideSearch : public CGUIDialog + { + public: + CGUIDialogPVRGuideSearch(); + ~CGUIDialogPVRGuideSearch() override = default; + bool OnMessage(CGUIMessage& message) override; + void OnWindowLoaded() override; + + void SetFilterData(const std::shared_ptr<CPVREpgSearchFilter>& searchFilter); + + enum class Result + { + SEARCH, + SAVE, + CANCEL + }; + Result GetResult() const { return m_result; } + + protected: + void OnInitWindow() override; + + private: + void UpdateSearchFilter(); + void UpdateChannelSpin(); + void UpdateGroupsSpin(); + void UpdateGenreSpin(); + void UpdateDurationSpin(); + CDateTime ReadDateTime(const std::string& strDate, const std::string& strTime) const; + void Update(); + + bool IsRadioSelected(int controlID); + int GetSpinValue(int controlID); + std::string GetEditValue(int controlID); + + Result m_result = Result::CANCEL; + std::shared_ptr<CPVREpgSearchFilter> m_searchFilter; + std::map<int, std::shared_ptr<CPVRChannelGroupMember>> m_channelsMap; + + CDateTime m_startDateTime; + CDateTime m_endDateTime; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRItemsViewBase.cpp b/xbmc/pvr/dialogs/GUIDialogPVRItemsViewBase.cpp new file mode 100644 index 0000000..2add783 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRItemsViewBase.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRItemsViewBase.h" + +#include "ContextMenuManager.h" +#include "FileItem.h" +#include "ServiceBroker.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "input/actions/Action.h" +#include "input/actions/ActionIDs.h" +#include "pvr/PVRManager.h" +#include "pvr/guilib/PVRGUIActionsEPG.h" +#include "view/ViewState.h" + +#include <utility> + +#define CONTROL_LIST 11 + +using namespace PVR; + +CGUIDialogPVRItemsViewBase::CGUIDialogPVRItemsViewBase(int id, const std::string& xmlFile) + : CGUIDialog(id, xmlFile), m_vecItems(new CFileItemList) +{ +} + +void CGUIDialogPVRItemsViewBase::OnWindowLoaded() +{ + CGUIDialog::OnWindowLoaded(); + m_viewControl.Reset(); + m_viewControl.SetParentWindow(GetID()); + m_viewControl.AddView(GetControl(CONTROL_LIST)); +} + +void CGUIDialogPVRItemsViewBase::OnWindowUnload() +{ + CGUIDialog::OnWindowUnload(); + m_viewControl.Reset(); +} + +void CGUIDialogPVRItemsViewBase::OnInitWindow() +{ + CGUIDialog::OnInitWindow(); +} + +void CGUIDialogPVRItemsViewBase::OnDeinitWindow(int nextWindowID) +{ + CGUIDialog::OnDeinitWindow(nextWindowID); + Clear(); +} + +bool CGUIDialogPVRItemsViewBase::OnAction(const CAction& action) +{ + if (m_viewControl.HasControl(GetFocusedControlID())) + { + switch (action.GetID()) + { + case ACTION_SHOW_INFO: + case ACTION_SELECT_ITEM: + case ACTION_MOUSE_LEFT_CLICK: + ShowInfo(m_viewControl.GetSelectedItem()); + return true; + + case ACTION_CONTEXT_MENU: + case ACTION_MOUSE_RIGHT_CLICK: + return ContextMenu(m_viewControl.GetSelectedItem()); + + default: + break; + } + } + return CGUIDialog::OnAction(action); +} + +CGUIControl* CGUIDialogPVRItemsViewBase::GetFirstFocusableControl(int id) +{ + if (m_viewControl.HasControl(id)) + id = m_viewControl.GetCurrentControl(); + + return CGUIDialog::GetFirstFocusableControl(id); +} + +void CGUIDialogPVRItemsViewBase::ShowInfo(int itemIdx) +{ + if (itemIdx < 0 || itemIdx >= m_vecItems->Size()) + return; + + const std::shared_ptr<CFileItem> item = m_vecItems->Get(itemIdx); + if (!item) + return; + + CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().ShowEPGInfo(*item); +} + +bool CGUIDialogPVRItemsViewBase::ContextMenu(int itemIdx) +{ + auto InRange = [](size_t i, std::pair<size_t, size_t> range) { + return i >= range.first && i < range.second; + }; + + if (itemIdx < 0 || itemIdx >= m_vecItems->Size()) + return false; + + const CFileItemPtr item = m_vecItems->Get(itemIdx); + if (!item) + return false; + + CContextButtons buttons; + + // Add the global menu + const ContextMenuView globalMenu = + CServiceBroker::GetContextMenuManager().GetItems(*item, CContextMenuManager::MAIN); + auto globalMenuRange = std::make_pair(buttons.size(), buttons.size() + globalMenu.size()); + for (const auto& menu : globalMenu) + buttons.emplace_back(~buttons.size(), menu->GetLabel(*item)); + + // Add addon menus + const ContextMenuView addonMenu = + CServiceBroker::GetContextMenuManager().GetAddonItems(*item, CContextMenuManager::MAIN); + auto addonMenuRange = std::make_pair(buttons.size(), buttons.size() + addonMenu.size()); + for (const auto& menu : addonMenu) + buttons.emplace_back(~buttons.size(), menu->GetLabel(*item)); + + if (buttons.empty()) + return true; + + int idx = CGUIDialogContextMenu::Show(buttons); + if (idx < 0 || idx >= static_cast<int>(buttons.size())) + return false; + + Close(); + + if (InRange(idx, globalMenuRange)) + return CONTEXTMENU::LoopFrom(*globalMenu[idx - globalMenuRange.first], item); + + return CONTEXTMENU::LoopFrom(*addonMenu[idx - addonMenuRange.first], item); +} + +void CGUIDialogPVRItemsViewBase::Init() +{ + m_viewControl.SetCurrentView(DEFAULT_VIEW_LIST); + Clear(); +} + +void CGUIDialogPVRItemsViewBase::Clear() +{ + m_viewControl.Clear(); + m_vecItems->Clear(); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRItemsViewBase.h b/xbmc/pvr/dialogs/GUIDialogPVRItemsViewBase.h new file mode 100644 index 0000000..66530e4 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRItemsViewBase.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "guilib/GUIDialog.h" +#include "view/GUIViewControl.h" + +#include <memory> +#include <string> + +class CFileItemList; + +namespace PVR +{ +class CGUIDialogPVRItemsViewBase : public CGUIDialog +{ +public: + CGUIDialogPVRItemsViewBase() = delete; + CGUIDialogPVRItemsViewBase(int id, const std::string& xmlFile); + ~CGUIDialogPVRItemsViewBase() override = default; + + void OnWindowLoaded() override; + void OnWindowUnload() override; + bool OnAction(const CAction& action) override; + +protected: + void Init(); + + void OnInitWindow() override; + void OnDeinitWindow(int nextWindowID) override; + CGUIControl* GetFirstFocusableControl(int id) override; + + std::unique_ptr<CFileItemList> m_vecItems; + CGUIViewControl m_viewControl; + +private: + void Clear(); + void ShowInfo(int itemIdx); + bool ContextMenu(int iItemIdx); +}; +} // namespace PVR diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp new file mode 100644 index 0000000..ead92d2 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRRadioRDSInfo.h" + +#include "GUIUserMessages.h" +#include "ServiceBroker.h" +#include "guilib/GUIMessage.h" +#include "guilib/GUISpinControl.h" +#include "guilib/GUITextBox.h" +#include "guilib/LocalizeStrings.h" +#include "pvr/PVRManager.h" +#include "pvr/PVRPlaybackState.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRRadioRDSInfoTag.h" + +using namespace PVR; + +#define CONTROL_BTN_OK 10 +#define SPIN_CONTROL_INFO 21 +#define TEXT_INFO 22 +#define CONTROL_NEXT_PAGE 60 +#define CONTROL_INFO_LIST 70 + +#define INFO_NEWS 1 +#define INFO_NEWS_LOCAL 2 +#define INFO_SPORT 3 +#define INFO_WEATHER 4 +#define INFO_LOTTERY 5 +#define INFO_STOCK 6 +#define INFO_OTHER 7 +#define INFO_CINEMA 8 +#define INFO_HOROSCOPE 9 + +CGUIDialogPVRRadioRDSInfo::CGUIDialogPVRRadioRDSInfo() + : CGUIDialog(WINDOW_DIALOG_PVR_RADIO_RDS_INFO, "DialogPVRRadioRDSInfo.xml") + , m_InfoNews(29916, INFO_NEWS) + , m_InfoNewsLocal(29917, INFO_NEWS_LOCAL) + , m_InfoSport(29918, INFO_SPORT) + , m_InfoWeather(400, INFO_WEATHER) + , m_InfoLottery(29919, INFO_LOTTERY) + , m_InfoStock(29920, INFO_STOCK) + , m_InfoOther(29921, INFO_OTHER) + , m_InfoCinema(19602, INFO_CINEMA) + , m_InfoHoroscope(29922, INFO_HOROSCOPE) +{ +} + +bool CGUIDialogPVRRadioRDSInfo::OnMessage(CGUIMessage& message) +{ + if (message.GetMessage() == GUI_MSG_CLICKED) + { + int iControl = message.GetSenderId(); + + if (iControl == CONTROL_BTN_OK) + { + Close(); + return true; + } + else if (iControl == SPIN_CONTROL_INFO) + { + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel(); + if (!channel) + return false; + + const std::shared_ptr<CPVRRadioRDSInfoTag> currentRDS = channel->GetRadioRDSInfoTag(); + if (!currentRDS) + return false; + + const CGUISpinControl* spin = static_cast<CGUISpinControl*>(GetControl(SPIN_CONTROL_INFO)); + if (!spin) + return false; + + CGUITextBox* textbox = static_cast<CGUITextBox*>(GetControl(TEXT_INFO)); + if (!textbox) + return false; + + switch (spin->GetValue()) + { + case INFO_NEWS: + textbox->SetInfo(currentRDS->GetInfoNews()); + break; + case INFO_NEWS_LOCAL: + textbox->SetInfo(currentRDS->GetInfoNewsLocal()); + break; + case INFO_SPORT: + textbox->SetInfo(currentRDS->GetInfoSport()); + break; + case INFO_WEATHER: + textbox->SetInfo(currentRDS->GetInfoWeather()); + break; + case INFO_LOTTERY: + textbox->SetInfo(currentRDS->GetInfoLottery()); + break; + case INFO_STOCK: + textbox->SetInfo(currentRDS->GetInfoStock()); + break; + case INFO_OTHER: + textbox->SetInfo(currentRDS->GetInfoOther()); + break; + case INFO_CINEMA: + textbox->SetInfo(currentRDS->GetInfoCinema()); + break; + case INFO_HOROSCOPE: + textbox->SetInfo(currentRDS->GetInfoHoroscope()); + break; + } + + SET_CONTROL_VISIBLE(CONTROL_INFO_LIST); + } + } + else if (message.GetMessage() == GUI_MSG_NOTIFY_ALL) + { + if (message.GetParam1() == GUI_MSG_UPDATE_RADIOTEXT && IsActive()) + { + UpdateInfoControls(); + } + } + + return CGUIDialog::OnMessage(message); +} + +void CGUIDialogPVRRadioRDSInfo::OnInitWindow() +{ + CGUIDialog::OnInitWindow(); + + InitInfoControls(); +} + +void CGUIDialogPVRRadioRDSInfo::InitInfoControls() +{ + SET_CONTROL_HIDDEN(CONTROL_INFO_LIST); + + CGUISpinControl* spin = static_cast<CGUISpinControl*>(GetControl(SPIN_CONTROL_INFO)); + if (spin) + spin->Clear(); + + CGUITextBox* textbox = static_cast<CGUITextBox*>(GetControl(TEXT_INFO)); + + m_InfoNews.Init(spin, textbox); + m_InfoNewsLocal.Init(spin, textbox); + m_InfoSport.Init(spin, textbox); + m_InfoWeather.Init(spin, textbox); + m_InfoLottery.Init(spin, textbox); + m_InfoStock.Init(spin, textbox); + m_InfoOther.Init(spin, textbox); + m_InfoCinema.Init(spin, textbox); + m_InfoHoroscope.Init(spin, textbox); + + if (spin && textbox) + UpdateInfoControls(); +} + +void CGUIDialogPVRRadioRDSInfo::UpdateInfoControls() +{ + const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel(); + if (!channel) + return; + + const std::shared_ptr<CPVRRadioRDSInfoTag> currentRDS = channel->GetRadioRDSInfoTag(); + if (!currentRDS) + return; + + bool bInfoPresent = m_InfoNews.Update(currentRDS->GetInfoNews()); + bInfoPresent |= m_InfoNewsLocal.Update(currentRDS->GetInfoNewsLocal()); + bInfoPresent |= m_InfoSport.Update(currentRDS->GetInfoSport()); + bInfoPresent |= m_InfoWeather.Update(currentRDS->GetInfoWeather()); + bInfoPresent |= m_InfoLottery.Update(currentRDS->GetInfoLottery()); + bInfoPresent |= m_InfoStock.Update(currentRDS->GetInfoStock()); + bInfoPresent |= m_InfoOther.Update(currentRDS->GetInfoOther()); + bInfoPresent |= m_InfoCinema.Update(currentRDS->GetInfoCinema()); + bInfoPresent |= m_InfoHoroscope.Update(currentRDS->GetInfoHoroscope()); + + if (bInfoPresent) + SET_CONTROL_VISIBLE(CONTROL_INFO_LIST); +} + +CGUIDialogPVRRadioRDSInfo::InfoControl::InfoControl(uint32_t iSpinLabelId, uint32_t iSpinControlId) +: m_iSpinLabelId(iSpinLabelId), + m_iSpinControlId(iSpinControlId) +{ +} + +void CGUIDialogPVRRadioRDSInfo::InfoControl::Init(CGUISpinControl* spin, CGUITextBox* textbox) +{ + m_spinControl = spin; + m_textbox = textbox; + m_bSpinLabelPresent = false; + m_textboxValue.clear(); +} + +bool CGUIDialogPVRRadioRDSInfo::InfoControl::Update(const std::string& textboxValue) +{ + if (m_spinControl && m_textbox && !textboxValue.empty()) + { + if (!m_bSpinLabelPresent) + { + m_spinControl->AddLabel(g_localizeStrings.Get(m_iSpinLabelId), m_iSpinControlId); + m_bSpinLabelPresent = true; + } + + if (m_textboxValue != textboxValue) + { + m_spinControl->SetValue(m_iSpinControlId); + m_textboxValue = textboxValue; + m_textbox->SetInfo(textboxValue); + return true; + } + } + return false; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.h b/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.h new file mode 100644 index 0000000..4ae1b0b --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "guilib/GUIDialog.h" + +#include <string> + +class CGUISpinControl; +class CGUITextBox; + +namespace PVR +{ + class CGUIDialogPVRRadioRDSInfo : public CGUIDialog + { + public: + CGUIDialogPVRRadioRDSInfo(); + ~CGUIDialogPVRRadioRDSInfo() override = default; + bool OnMessage(CGUIMessage& message) override; + + protected: + void OnInitWindow() override; + + private: + class InfoControl + { + public: + InfoControl(uint32_t iSpinLabelId, uint32_t iSpinControlId); + void Init(CGUISpinControl* spin, CGUITextBox* textbox); + bool Update(const std::string& textboxValue); + + private: + CGUISpinControl* m_spinControl = nullptr; + uint32_t m_iSpinLabelId = 0; + uint32_t m_iSpinControlId = 0; + CGUITextBox* m_textbox = nullptr; + bool m_bSpinLabelPresent = false; + std::string m_textboxValue; + }; + + void InitInfoControls(); + void UpdateInfoControls(); + + InfoControl m_InfoNews; + InfoControl m_InfoNewsLocal; + InfoControl m_InfoSport; + InfoControl m_InfoWeather; + InfoControl m_InfoLottery; + InfoControl m_InfoStock; + InfoControl m_InfoOther; + InfoControl m_InfoCinema; + InfoControl m_InfoHoroscope; + }; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp new file mode 100644 index 0000000..9396083 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRRecordingInfo.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "guilib/GUIMessage.h" +#include "pvr/PVRManager.h" +#include "pvr/guilib/PVRGUIActionsEPG.h" +#include "pvr/guilib/PVRGUIActionsPlayback.h" + +using namespace PVR; + +#define CONTROL_BTN_FIND 4 +#define CONTROL_BTN_OK 7 +#define CONTROL_BTN_PLAY_RECORDING 8 + +CGUIDialogPVRRecordingInfo::CGUIDialogPVRRecordingInfo() + : CGUIDialog(WINDOW_DIALOG_PVR_RECORDING_INFO, "DialogPVRInfo.xml"), m_recordItem(new CFileItem) +{ +} + +bool CGUIDialogPVRRecordingInfo::OnMessage(CGUIMessage& message) +{ + switch (message.GetMessage()) + { + case GUI_MSG_CLICKED: + return OnClickButtonOK(message) || OnClickButtonPlay(message) || OnClickButtonFind(message); + } + + return CGUIDialog::OnMessage(message); +} + +bool CGUIDialogPVRRecordingInfo::OnClickButtonOK(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_OK) + { + Close(); + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRRecordingInfo::OnClickButtonPlay(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_PLAY_RECORDING) + { + Close(); + + if (m_recordItem) + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording( + *m_recordItem, true /* check resume */); + + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRRecordingInfo::OnClickButtonFind(const CGUIMessage& message) +{ + bool bReturn = false; + + if (message.GetSenderId() == CONTROL_BTN_FIND) + { + Close(); + + if (m_recordItem) + CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().FindSimilar(*m_recordItem); + + bReturn = true; + } + + return bReturn; +} + +bool CGUIDialogPVRRecordingInfo::OnInfo(int actionID) +{ + Close(); + return true; +} + +void CGUIDialogPVRRecordingInfo::SetRecording(const CFileItem& item) +{ + m_recordItem = std::make_shared<CFileItem>(item); +} + +CFileItemPtr CGUIDialogPVRRecordingInfo::GetCurrentListItem(int offset) +{ + return m_recordItem; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h new file mode 100644 index 0000000..b22e0d7 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "guilib/GUIDialog.h" + +class CFileItem; +class CGUIMessage; + +namespace PVR +{ +class CGUIDialogPVRRecordingInfo : public CGUIDialog +{ +public: + CGUIDialogPVRRecordingInfo(); + ~CGUIDialogPVRRecordingInfo() override = default; + bool OnMessage(CGUIMessage& message) override; + bool OnInfo(int actionID) override; + bool HasListItems() const override { return true; } + CFileItemPtr GetCurrentListItem(int offset = 0) override; + + void SetRecording(const CFileItem& item); + +private: + bool OnClickButtonFind(const CGUIMessage& message); + bool OnClickButtonOK(const CGUIMessage& message); + bool OnClickButtonPlay(const CGUIMessage& message); + + CFileItemPtr m_recordItem; +}; +} // namespace PVR diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp b/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp new file mode 100644 index 0000000..ed8cd7b --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2017-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 "GUIDialogPVRRecordingSettings.h" + +#include "FileItem.h" +#include "ServiceBroker.h" +#include "guilib/GUIMessage.h" +#include "guilib/LocalizeStrings.h" +#include "messaging/helpers/DialogHelper.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClient.h" +#include "pvr/recordings/PVRRecording.h" +#include "settings/dialogs/GUIDialogSettingsBase.h" +#include "settings/lib/Setting.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" +#include "utils/log.h" + +#include <algorithm> +#include <iterator> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +using namespace PVR; +using namespace KODI::MESSAGING; + +#define SETTING_RECORDING_NAME "recording.name" +#define SETTING_RECORDING_PLAYCOUNT "recording.playcount" +#define SETTING_RECORDING_LIFETIME "recording.lifetime" + +CGUIDialogPVRRecordingSettings::CGUIDialogPVRRecordingSettings() + : CGUIDialogSettingsManualBase(WINDOW_DIALOG_PVR_RECORDING_SETTING, "DialogSettings.xml") +{ + m_loadType = LOAD_EVERY_TIME; +} + +void CGUIDialogPVRRecordingSettings::SetRecording(const std::shared_ptr<CPVRRecording>& recording) +{ + if (!recording) + { + CLog::LogF(LOGERROR, "No recording given"); + return; + } + + m_recording = recording; + + // Copy data we need from tag. Do not modify the tag itself until Save()! + m_strTitle = m_recording->m_strTitle; + m_iPlayCount = m_recording->GetLocalPlayCount(); + m_iLifetime = m_recording->LifeTime(); +} + +void CGUIDialogPVRRecordingSettings::SetupView() +{ + CGUIDialogSettingsManualBase::SetupView(); + SetHeading(19068); // Recording settings + SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON); + SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186); // OK + SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222); // Cancel +} + +void CGUIDialogPVRRecordingSettings::InitializeSettings() +{ + CGUIDialogSettingsManualBase::InitializeSettings(); + + const std::shared_ptr<CSettingCategory> category = AddCategory("pvrrecordingsettings", -1); + if (category == nullptr) + { + CLog::LogF(LOGERROR, "Unable to add settings category"); + return; + } + + const std::shared_ptr<CSettingGroup> group = AddGroup(category); + if (group == nullptr) + { + CLog::LogF(LOGERROR, "Unable to add settings group"); + return; + } + + std::shared_ptr<CSetting> setting = nullptr; + const std::shared_ptr<CPVRClient> client = + CServiceBroker::GetPVRManager().GetClient(m_recording->ClientID()); + + // Name + setting = AddEdit(group, SETTING_RECORDING_NAME, 19075, SettingLevel::Basic, m_strTitle); + setting->SetEnabled(client && client->GetClientCapabilities().SupportsRecordingsRename()); + + // Play count + if (client && client->GetClientCapabilities().SupportsRecordingsPlayCount()) + setting = AddEdit(group, SETTING_RECORDING_PLAYCOUNT, 567, SettingLevel::Basic, + m_recording->GetLocalPlayCount()); + + // Lifetime + if (client && client->GetClientCapabilities().SupportsRecordingsLifetimeChange()) + setting = AddList(group, SETTING_RECORDING_LIFETIME, 19083, SettingLevel::Basic, m_iLifetime, + LifetimesFiller, 19083); +} + +bool CGUIDialogPVRRecordingSettings::CanEditRecording(const CFileItem& item) +{ + if (!item.HasPVRRecordingInfoTag()) + return false; + + const std::shared_ptr<CPVRClient> client = + CServiceBroker::GetPVRManager().GetClient(item.GetPVRRecordingInfoTag()->ClientID()); + + if (!client) + return false; + + const CPVRClientCapabilities& capabilities = client->GetClientCapabilities(); + + return capabilities.SupportsRecordingsRename() || capabilities.SupportsRecordingsPlayCount() || + capabilities.SupportsRecordingsLifetimeChange(); +} + +bool CGUIDialogPVRRecordingSettings::OnSettingChanging( + const std::shared_ptr<const CSetting>& setting) +{ + if (setting == nullptr) + { + CLog::LogF(LOGERROR, "No setting"); + return false; + } + + const std::string& settingId = setting->GetId(); + + if (settingId == SETTING_RECORDING_LIFETIME) + { + int iNewLifetime = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + if (m_recording->WillBeExpiredWithNewLifetime(iNewLifetime)) + { + if (HELPERS::ShowYesNoDialogText( + CVariant{19068}, // "Recording settings" + StringUtils::Format(g_localizeStrings.Get(19147), + iNewLifetime)) // "Setting the lifetime..." + != HELPERS::DialogResponse::CHOICE_YES) + return false; + } + } + + return CGUIDialogSettingsManualBase::OnSettingChanging(setting); +} + +void CGUIDialogPVRRecordingSettings::OnSettingChanged( + const std::shared_ptr<const CSetting>& setting) +{ + if (setting == nullptr) + { + CLog::LogF(LOGERROR, "No setting"); + return; + } + + CGUIDialogSettingsManualBase::OnSettingChanged(setting); + + const std::string& settingId = setting->GetId(); + + if (settingId == SETTING_RECORDING_NAME) + { + m_strTitle = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + } + else if (settingId == SETTING_RECORDING_PLAYCOUNT) + { + m_iPlayCount = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + } + else if (settingId == SETTING_RECORDING_LIFETIME) + { + m_iLifetime = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + } +} + +bool CGUIDialogPVRRecordingSettings::Save() +{ + // Name + m_recording->m_strTitle = m_strTitle; + + // Play count + m_recording->SetLocalPlayCount(m_iPlayCount); + + // Lifetime + m_recording->SetLifeTime(m_iLifetime); + + return true; +} + +void CGUIDialogPVRRecordingSettings::LifetimesFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRRecordingSettings* pThis = static_cast<CGUIDialogPVRRecordingSettings*>(data); + if (pThis) + { + list.clear(); + + const std::shared_ptr<CPVRClient> client = + CServiceBroker::GetPVRManager().GetClient(pThis->m_recording->ClientID()); + if (client) + { + std::vector<std::pair<std::string, int>> values; + client->GetClientCapabilities().GetRecordingsLifetimeValues(values); + std::transform( + values.cbegin(), values.cend(), std::back_inserter(list), + [](const auto& value) { return IntegerSettingOption(value.first, value.second); }); + } + + current = pThis->m_iLifetime; + + auto it = list.begin(); + while (it != list.end()) + { + if (it->value == current) + break; // value already in list + + ++it; + } + + if (it == list.end()) + { + // PVR backend supplied value is not in the list of predefined values. Insert it. + list.insert(it, IntegerSettingOption( + StringUtils::Format(g_localizeStrings.Get(17999), current) /* {} days */, + current)); + } + } + else + CLog::LogF(LOGERROR, "No dialog"); +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.h b/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.h new file mode 100644 index 0000000..f0de299 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017-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. + */ + +#pragma once + +#include "settings/dialogs/GUIDialogSettingsManualBase.h" + +#include <memory> +#include <string> +#include <vector> + +class CFileItem; +class CSetting; + +struct IntegerSettingOption; + +namespace PVR +{ +class CPVRRecording; + +class CGUIDialogPVRRecordingSettings : public CGUIDialogSettingsManualBase +{ +public: + CGUIDialogPVRRecordingSettings(); + + void SetRecording(const std::shared_ptr<CPVRRecording>& recording); + static bool CanEditRecording(const CFileItem& item); + +protected: + // implementation of ISettingCallback + bool OnSettingChanging(const std::shared_ptr<const CSetting>& setting) override; + void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override; + + // specialization of CGUIDialogSettingsBase + bool AllowResettingSettings() const override { return false; } + bool Save() override; + void SetupView() override; + + // specialization of CGUIDialogSettingsManualBase + void InitializeSettings() override; + +private: + static void LifetimesFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + + std::shared_ptr<CPVRRecording> m_recording; + std::string m_strTitle; + int m_iPlayCount = 0; + int m_iLifetime = 0; +}; +} // namespace PVR diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp new file mode 100644 index 0000000..f786627 --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp @@ -0,0 +1,1513 @@ +/* + * Copyright (C) 2012-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 "GUIDialogPVRTimerSettings.h" + +#include "ServiceBroker.h" +#include "dialogs/GUIDialogNumeric.h" +#include "guilib/GUIMessage.h" +#include "guilib/LocalizeStrings.h" +#include "messaging/helpers/DialogOKHelper.h" +#include "pvr/PVRManager.h" +#include "pvr/addons/PVRClient.h" +#include "pvr/addons/PVRClients.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRChannelGroup.h" +#include "pvr/channels/PVRChannelGroupMember.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/epg/EpgInfoTag.h" +#include "pvr/timers/PVRTimerInfoTag.h" +#include "pvr/timers/PVRTimerType.h" +#include "settings/SettingUtils.h" +#include "settings/dialogs/GUIDialogSettingsBase.h" +#include "settings/lib/Setting.h" +#include "settings/lib/SettingsManager.h" +#include "settings/windows/GUIControlSettings.h" +#include "utils/StringUtils.h" +#include "utils/Variant.h" +#include "utils/log.h" + +#include <algorithm> +#include <iterator> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +using namespace PVR; +using namespace KODI::MESSAGING; + +#define SETTING_TMR_TYPE "timer.type" +#define SETTING_TMR_ACTIVE "timer.active" +#define SETTING_TMR_NAME "timer.name" +#define SETTING_TMR_EPGSEARCH "timer.epgsearch" +#define SETTING_TMR_FULLTEXT "timer.fulltext" +#define SETTING_TMR_CHANNEL "timer.channel" +#define SETTING_TMR_START_ANYTIME "timer.startanytime" +#define SETTING_TMR_END_ANYTIME "timer.endanytime" +#define SETTING_TMR_START_DAY "timer.startday" +#define SETTING_TMR_END_DAY "timer.endday" +#define SETTING_TMR_BEGIN "timer.begin" +#define SETTING_TMR_END "timer.end" +#define SETTING_TMR_WEEKDAYS "timer.weekdays" +#define SETTING_TMR_FIRST_DAY "timer.firstday" +#define SETTING_TMR_NEW_EPISODES "timer.newepisodes" +#define SETTING_TMR_BEGIN_PRE "timer.startmargin" +#define SETTING_TMR_END_POST "timer.endmargin" +#define SETTING_TMR_PRIORITY "timer.priority" +#define SETTING_TMR_LIFETIME "timer.lifetime" +#define SETTING_TMR_MAX_REC "timer.maxrecordings" +#define SETTING_TMR_DIR "timer.directory" +#define SETTING_TMR_REC_GROUP "timer.recgroup" + +#define TYPE_DEP_VISIBI_COND_ID_POSTFIX "visibi.typedep" +#define TYPE_DEP_ENABLE_COND_ID_POSTFIX "enable.typedep" +#define CHANNEL_DEP_VISIBI_COND_ID_POSTFIX "visibi.channeldep" +#define START_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX "visibi.startanytimedep" +#define END_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX "visibi.endanytimedep" + +CGUIDialogPVRTimerSettings::CGUIDialogPVRTimerSettings() + : CGUIDialogSettingsManualBase(WINDOW_DIALOG_PVR_TIMER_SETTING, "DialogSettings.xml"), + m_iWeekdays(PVR_WEEKDAY_NONE) +{ + m_loadType = LOAD_EVERY_TIME; +} + +CGUIDialogPVRTimerSettings::~CGUIDialogPVRTimerSettings() = default; + +bool CGUIDialogPVRTimerSettings::CanBeActivated() const +{ + if (!m_timerInfoTag) + { + CLog::LogF(LOGERROR, "No timer info tag"); + return false; + } + return true; +} + +void CGUIDialogPVRTimerSettings::SetTimer(const std::shared_ptr<CPVRTimerInfoTag>& timer) +{ + if (!timer) + { + CLog::LogF(LOGERROR, "No timer given"); + return; + } + + m_timerInfoTag = timer; + + // Copy data we need from tag. Do not modify the tag itself until Save()! + m_timerType = m_timerInfoTag->GetTimerType(); + m_bIsRadio = m_timerInfoTag->m_bIsRadio; + m_bIsNewTimer = m_timerInfoTag->m_iClientIndex == PVR_TIMER_NO_CLIENT_INDEX; + m_bTimerActive = m_bIsNewTimer || !m_timerType->SupportsEnableDisable() || + !(m_timerInfoTag->m_state == PVR_TIMER_STATE_DISABLED); + m_bStartAnyTime = + m_bIsNewTimer || !m_timerType->SupportsStartAnyTime() || m_timerInfoTag->m_bStartAnyTime; + m_bEndAnyTime = + m_bIsNewTimer || !m_timerType->SupportsEndAnyTime() || m_timerInfoTag->m_bEndAnyTime; + m_strTitle = m_timerInfoTag->m_strTitle; + + m_startLocalTime = m_timerInfoTag->StartAsLocalTime(); + m_endLocalTime = m_timerInfoTag->EndAsLocalTime(); + + m_timerStartTimeStr = m_startLocalTime.GetAsLocalizedTime("", false); + m_timerEndTimeStr = m_endLocalTime.GetAsLocalizedTime("", false); + m_firstDayLocalTime = m_timerInfoTag->FirstDayAsLocalTime(); + + m_strEpgSearchString = m_timerInfoTag->m_strEpgSearchString; + if ((m_bIsNewTimer || !m_timerType->SupportsEpgTitleMatch()) && m_strEpgSearchString.empty()) + m_strEpgSearchString = m_strTitle; + + m_bFullTextEpgSearch = m_timerInfoTag->m_bFullTextEpgSearch; + + m_iWeekdays = m_timerInfoTag->m_iWeekdays; + if ((m_bIsNewTimer || !m_timerType->SupportsWeekdays()) && m_iWeekdays == PVR_WEEKDAY_NONE) + m_iWeekdays = PVR_WEEKDAY_ALLDAYS; + + m_iPreventDupEpisodes = m_timerInfoTag->m_iPreventDupEpisodes; + m_iMarginStart = m_timerInfoTag->m_iMarginStart; + m_iMarginEnd = m_timerInfoTag->m_iMarginEnd; + m_iPriority = m_timerInfoTag->m_iPriority; + m_iLifetime = m_timerInfoTag->m_iLifetime; + m_iMaxRecordings = m_timerInfoTag->m_iMaxRecordings; + + if (m_bIsNewTimer && m_timerInfoTag->m_strDirectory.empty() && + m_timerType->SupportsRecordingFolders()) + m_strDirectory = m_strTitle; + else + m_strDirectory = m_timerInfoTag->m_strDirectory; + + m_iRecordingGroup = m_timerInfoTag->m_iRecordingGroup; + + InitializeChannelsList(); + InitializeTypesList(); + + // Channel + m_channel = ChannelDescriptor(); + + if (m_timerInfoTag->m_iClientChannelUid == PVR_CHANNEL_INVALID_UID) + { + if (m_timerType->SupportsAnyChannel()) + { + // Select first matching "Any channel" entry. + const auto it = std::find_if(m_channelEntries.cbegin(), m_channelEntries.cend(), + [this](const auto& channel) { + return channel.second.channelUid == PVR_CHANNEL_INVALID_UID && + channel.second.clientId == m_timerInfoTag->m_iClientId; + }); + + if (it != m_channelEntries.cend()) + { + m_channel = (*it).second; + } + else + { + CLog::LogF(LOGERROR, "Unable to map PVR_CHANNEL_INVALID_UID to channel entry!"); + } + } + else if (m_bIsNewTimer) + { + // Select first matching regular (not "Any channel") entry. + const auto it = std::find_if(m_channelEntries.cbegin(), m_channelEntries.cend(), + [this](const auto& channel) { + return channel.second.channelUid != PVR_CHANNEL_INVALID_UID && + channel.second.clientId == m_timerInfoTag->m_iClientId; + }); + + if (it != m_channelEntries.cend()) + { + m_channel = (*it).second; + } + else + { + CLog::LogF(LOGERROR, "Unable to map PVR_CHANNEL_INVALID_UID to channel entry!"); + } + } + } + else + { + // Find matching channel entry + const auto it = std::find_if( + m_channelEntries.cbegin(), m_channelEntries.cend(), [this](const auto& channel) { + return channel.second.channelUid == m_timerInfoTag->m_iClientChannelUid && + channel.second.clientId == m_timerInfoTag->m_iClientId; + }); + + if (it != m_channelEntries.cend()) + { + m_channel = (*it).second; + } + else + { + CLog::LogF(LOGERROR, "Unable to map channel uid to channel entry!"); + } + } +} + +void CGUIDialogPVRTimerSettings::SetupView() +{ + CGUIDialogSettingsManualBase::SetupView(); + SetHeading(19065); + SET_CONTROL_HIDDEN(CONTROL_SETTINGS_CUSTOM_BUTTON); + SET_CONTROL_LABEL(CONTROL_SETTINGS_OKAY_BUTTON, 186); + SET_CONTROL_LABEL(CONTROL_SETTINGS_CANCEL_BUTTON, 222); + SetButtonLabels(); +} + +void CGUIDialogPVRTimerSettings::InitializeSettings() +{ + CGUIDialogSettingsManualBase::InitializeSettings(); + + const std::shared_ptr<CSettingCategory> category = AddCategory("pvrtimersettings", -1); + if (category == NULL) + { + CLog::LogF(LOGERROR, "Unable to add settings category"); + return; + } + + const std::shared_ptr<CSettingGroup> group = AddGroup(category); + if (group == NULL) + { + CLog::LogF(LOGERROR, "Unable to add settings group"); + return; + } + + std::shared_ptr<CSetting> setting = NULL; + + // Timer type + bool useDetails = false; + bool foundClientSupportingTimers = false; + + const CPVRClientMap clients = CServiceBroker::GetPVRManager().Clients()->GetCreatedClients(); + for (const auto& client : clients) + { + if (client.second->GetClientCapabilities().SupportsTimers()) + { + if (foundClientSupportingTimers) + { + // found second client supporting timers, use detailed timer type list layout + useDetails = true; + break; + } + foundClientSupportingTimers = true; + } + } + + setting = AddList(group, SETTING_TMR_TYPE, 803, SettingLevel::Basic, 0, TypesFiller, 803, true, + -1, useDetails); + AddTypeDependentEnableCondition(setting, SETTING_TMR_TYPE); + + // Timer enabled/disabled + setting = AddToggle(group, SETTING_TMR_ACTIVE, 305, SettingLevel::Basic, m_bTimerActive); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_ACTIVE); + AddTypeDependentEnableCondition(setting, SETTING_TMR_ACTIVE); + + // Name + setting = + AddEdit(group, SETTING_TMR_NAME, 19075, SettingLevel::Basic, m_strTitle, true, false, 19097); + AddTypeDependentEnableCondition(setting, SETTING_TMR_NAME); + + // epg search string (only for epg-based timer rules) + setting = AddEdit(group, SETTING_TMR_EPGSEARCH, 804, SettingLevel::Basic, m_strEpgSearchString, + true, false, 805); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_EPGSEARCH); + AddTypeDependentEnableCondition(setting, SETTING_TMR_EPGSEARCH); + + // epg fulltext search (only for epg-based timer rules) + setting = AddToggle(group, SETTING_TMR_FULLTEXT, 806, SettingLevel::Basic, m_bFullTextEpgSearch); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_FULLTEXT); + AddTypeDependentEnableCondition(setting, SETTING_TMR_FULLTEXT); + + // Channel + setting = + AddList(group, SETTING_TMR_CHANNEL, 19078, SettingLevel::Basic, 0, ChannelsFiller, 19078); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_CHANNEL); + AddTypeDependentEnableCondition(setting, SETTING_TMR_CHANNEL); + + // Days of week (only for timer rules) + std::vector<int> weekdaysPreselect; + if (m_iWeekdays & PVR_WEEKDAY_MONDAY) + weekdaysPreselect.push_back(PVR_WEEKDAY_MONDAY); + if (m_iWeekdays & PVR_WEEKDAY_TUESDAY) + weekdaysPreselect.push_back(PVR_WEEKDAY_TUESDAY); + if (m_iWeekdays & PVR_WEEKDAY_WEDNESDAY) + weekdaysPreselect.push_back(PVR_WEEKDAY_WEDNESDAY); + if (m_iWeekdays & PVR_WEEKDAY_THURSDAY) + weekdaysPreselect.push_back(PVR_WEEKDAY_THURSDAY); + if (m_iWeekdays & PVR_WEEKDAY_FRIDAY) + weekdaysPreselect.push_back(PVR_WEEKDAY_FRIDAY); + if (m_iWeekdays & PVR_WEEKDAY_SATURDAY) + weekdaysPreselect.push_back(PVR_WEEKDAY_SATURDAY); + if (m_iWeekdays & PVR_WEEKDAY_SUNDAY) + weekdaysPreselect.push_back(PVR_WEEKDAY_SUNDAY); + + setting = AddList(group, SETTING_TMR_WEEKDAYS, 19079, SettingLevel::Basic, weekdaysPreselect, + WeekdaysFiller, 19079, 1, -1, true, -1, WeekdaysValueFormatter); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_WEEKDAYS); + AddTypeDependentEnableCondition(setting, SETTING_TMR_WEEKDAYS); + + // "Start any time" (only for timer rules) + setting = AddToggle(group, SETTING_TMR_START_ANYTIME, 810, SettingLevel::Basic, m_bStartAnyTime); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_START_ANYTIME); + AddTypeDependentEnableCondition(setting, SETTING_TMR_START_ANYTIME); + + // Start day (day + month + year only, no hours, minutes) + setting = AddSpinner(group, SETTING_TMR_START_DAY, 19128, SettingLevel::Basic, + GetDateAsIndex(m_startLocalTime), DaysFiller); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_START_DAY); + AddTypeDependentEnableCondition(setting, SETTING_TMR_START_DAY); + AddStartAnytimeDependentVisibilityCondition(setting, SETTING_TMR_START_DAY); + + // Start time (hours + minutes only, no day, month, year) + setting = AddButton(group, SETTING_TMR_BEGIN, 19126, SettingLevel::Basic); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_BEGIN); + AddTypeDependentEnableCondition(setting, SETTING_TMR_BEGIN); + AddStartAnytimeDependentVisibilityCondition(setting, SETTING_TMR_BEGIN); + + // "End any time" (only for timer rules) + setting = AddToggle(group, SETTING_TMR_END_ANYTIME, 817, SettingLevel::Basic, m_bEndAnyTime); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END_ANYTIME); + AddTypeDependentEnableCondition(setting, SETTING_TMR_END_ANYTIME); + + // End day (day + month + year only, no hours, minutes) + setting = AddSpinner(group, SETTING_TMR_END_DAY, 19129, SettingLevel::Basic, + GetDateAsIndex(m_endLocalTime), DaysFiller); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END_DAY); + AddTypeDependentEnableCondition(setting, SETTING_TMR_END_DAY); + AddEndAnytimeDependentVisibilityCondition(setting, SETTING_TMR_END_DAY); + + // End time (hours + minutes only, no day, month, year) + setting = AddButton(group, SETTING_TMR_END, 19127, SettingLevel::Basic); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END); + AddTypeDependentEnableCondition(setting, SETTING_TMR_END); + AddEndAnytimeDependentVisibilityCondition(setting, SETTING_TMR_END); + + // First day (only for timer rules) + setting = AddSpinner(group, SETTING_TMR_FIRST_DAY, 19084, SettingLevel::Basic, + GetDateAsIndex(m_firstDayLocalTime), DaysFiller); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_FIRST_DAY); + AddTypeDependentEnableCondition(setting, SETTING_TMR_FIRST_DAY); + + // "Prevent duplicate episodes" (only for timer rules) + setting = AddList(group, SETTING_TMR_NEW_EPISODES, 812, SettingLevel::Basic, + m_iPreventDupEpisodes, DupEpisodesFiller, 812); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_NEW_EPISODES); + AddTypeDependentEnableCondition(setting, SETTING_TMR_NEW_EPISODES); + + // Pre and post record time + setting = AddList(group, SETTING_TMR_BEGIN_PRE, 813, SettingLevel::Basic, m_iMarginStart, + MarginTimeFiller, 813); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_BEGIN_PRE); + AddTypeDependentEnableCondition(setting, SETTING_TMR_BEGIN_PRE); + + setting = AddList(group, SETTING_TMR_END_POST, 814, SettingLevel::Basic, m_iMarginEnd, + MarginTimeFiller, 814); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_END_POST); + AddTypeDependentEnableCondition(setting, SETTING_TMR_END_POST); + + // Priority + setting = AddList(group, SETTING_TMR_PRIORITY, 19082, SettingLevel::Basic, m_iPriority, + PrioritiesFiller, 19082); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_PRIORITY); + AddTypeDependentEnableCondition(setting, SETTING_TMR_PRIORITY); + + // Lifetime + setting = AddList(group, SETTING_TMR_LIFETIME, 19083, SettingLevel::Basic, m_iLifetime, + LifetimesFiller, 19083); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_LIFETIME); + AddTypeDependentEnableCondition(setting, SETTING_TMR_LIFETIME); + + // MaxRecordings + setting = AddList(group, SETTING_TMR_MAX_REC, 818, SettingLevel::Basic, m_iMaxRecordings, + MaxRecordingsFiller, 818); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_MAX_REC); + AddTypeDependentEnableCondition(setting, SETTING_TMR_MAX_REC); + + // Recording folder + setting = AddEdit(group, SETTING_TMR_DIR, 19076, SettingLevel::Basic, m_strDirectory, true, false, + 19104); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_DIR); + AddTypeDependentEnableCondition(setting, SETTING_TMR_DIR); + + // Recording group + setting = AddList(group, SETTING_TMR_REC_GROUP, 811, SettingLevel::Basic, m_iRecordingGroup, + RecordingGroupFiller, 811); + AddTypeDependentVisibilityCondition(setting, SETTING_TMR_REC_GROUP); + AddTypeDependentEnableCondition(setting, SETTING_TMR_REC_GROUP); +} + +int CGUIDialogPVRTimerSettings::GetWeekdaysFromSetting(const SettingConstPtr& setting) +{ + std::shared_ptr<const CSettingList> settingList = + std::static_pointer_cast<const CSettingList>(setting); + if (settingList->GetElementType() != SettingType::Integer) + { + CLog::LogF(LOGERROR, "Wrong weekdays element type"); + return 0; + } + int weekdays = 0; + std::vector<CVariant> list = CSettingUtils::GetList(settingList); + for (const auto& value : list) + { + if (!value.isInteger()) + { + CLog::LogF(LOGERROR, "Wrong weekdays value type"); + return 0; + } + weekdays += static_cast<int>(value.asInteger()); + } + + return weekdays; +} + +void CGUIDialogPVRTimerSettings::OnSettingChanged(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + { + CLog::LogF(LOGERROR, "No setting"); + return; + } + + CGUIDialogSettingsManualBase::OnSettingChanged(setting); + + const std::string& settingId = setting->GetId(); + + if (settingId == SETTING_TMR_TYPE) + { + int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + const auto it = m_typeEntries.find(idx); + if (it != m_typeEntries.end()) + { + m_timerType = it->second; + + // reset certain settings to the defaults of the new timer type + + if (m_timerType->SupportsPriority()) + m_iPriority = m_timerType->GetPriorityDefault(); + + if (m_timerType->SupportsLifetime()) + m_iLifetime = m_timerType->GetLifetimeDefault(); + + if (m_timerType->SupportsMaxRecordings()) + m_iMaxRecordings = m_timerType->GetMaxRecordingsDefault(); + + if (m_timerType->SupportsRecordingGroup()) + m_iRecordingGroup = m_timerType->GetRecordingGroupDefault(); + + if (m_timerType->SupportsRecordOnlyNewEpisodes()) + m_iPreventDupEpisodes = m_timerType->GetPreventDuplicateEpisodesDefault(); + + if (m_timerType->IsTimerRule() && (m_iWeekdays == PVR_WEEKDAY_ALLDAYS)) + SetButtonLabels(); // update "Any day" vs. "Every day" + } + else + { + CLog::LogF(LOGERROR, "Unable to get 'type' value"); + } + } + else if (settingId == SETTING_TMR_ACTIVE) + { + m_bTimerActive = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_NAME) + { + m_strTitle = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_EPGSEARCH) + { + m_strEpgSearchString = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_FULLTEXT) + { + m_bFullTextEpgSearch = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_CHANNEL) + { + int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + const auto it = m_channelEntries.find(idx); + if (it != m_channelEntries.end()) + { + m_channel = it->second; + } + else + { + CLog::LogF(LOGERROR, "Unable to get 'type' value"); + } + } + else if (settingId == SETTING_TMR_WEEKDAYS) + { + m_iWeekdays = GetWeekdaysFromSetting(setting); + } + else if (settingId == SETTING_TMR_START_ANYTIME) + { + m_bStartAnyTime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_END_ANYTIME) + { + m_bEndAnyTime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_START_DAY) + { + SetDateFromIndex(m_startLocalTime, + std::static_pointer_cast<const CSettingInt>(setting)->GetValue()); + } + else if (settingId == SETTING_TMR_END_DAY) + { + SetDateFromIndex(m_endLocalTime, + std::static_pointer_cast<const CSettingInt>(setting)->GetValue()); + } + else if (settingId == SETTING_TMR_FIRST_DAY) + { + SetDateFromIndex(m_firstDayLocalTime, + std::static_pointer_cast<const CSettingInt>(setting)->GetValue()); + } + else if (settingId == SETTING_TMR_NEW_EPISODES) + { + m_iPreventDupEpisodes = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_BEGIN_PRE) + { + m_iMarginStart = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_END_POST) + { + m_iMarginEnd = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_PRIORITY) + { + m_iPriority = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_LIFETIME) + { + m_iLifetime = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_MAX_REC) + { + m_iMaxRecordings = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_DIR) + { + m_strDirectory = std::static_pointer_cast<const CSettingString>(setting)->GetValue(); + } + else if (settingId == SETTING_TMR_REC_GROUP) + { + m_iRecordingGroup = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + } +} + +void CGUIDialogPVRTimerSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting) +{ + if (setting == NULL) + { + CLog::LogF(LOGERROR, "No setting"); + return; + } + + CGUIDialogSettingsManualBase::OnSettingAction(setting); + + const std::string& settingId = setting->GetId(); + if (settingId == SETTING_TMR_BEGIN) + { + KODI::TIME::SystemTime timerStartTime; + m_startLocalTime.GetAsSystemTime(timerStartTime); + if (CGUIDialogNumeric::ShowAndGetTime(timerStartTime, g_localizeStrings.Get(14066))) + { + SetTimeFromSystemTime(m_startLocalTime, timerStartTime); + m_timerStartTimeStr = m_startLocalTime.GetAsLocalizedTime("", false); + SetButtonLabels(); + } + } + else if (settingId == SETTING_TMR_END) + { + KODI::TIME::SystemTime timerEndTime; + m_endLocalTime.GetAsSystemTime(timerEndTime); + if (CGUIDialogNumeric::ShowAndGetTime(timerEndTime, g_localizeStrings.Get(14066))) + { + SetTimeFromSystemTime(m_endLocalTime, timerEndTime); + m_timerEndTimeStr = m_endLocalTime.GetAsLocalizedTime("", false); + SetButtonLabels(); + } + } +} + +bool CGUIDialogPVRTimerSettings::Validate() +{ + // @todo: Timer rules may have no date (time-only), so we can't check those for now. + // We need to extend the api with additional attributes to properly fix this + if (m_timerType->IsTimerRule()) + return true; + + bool bStartAnyTime = m_bStartAnyTime; + bool bEndAnyTime = m_bEndAnyTime; + + if (!m_timerType->SupportsStartAnyTime() || + !m_timerType->IsEpgBased()) // Start anytime toggle is not displayed + bStartAnyTime = false; // Assume start time change needs checking for + + if (!m_timerType->SupportsEndAnyTime() || + !m_timerType->IsEpgBased()) // End anytime toggle is not displayed + bEndAnyTime = false; // Assume end time change needs checking for + + // Begin and end time + if (!bStartAnyTime && !bEndAnyTime) + { + if (m_timerType->SupportsStartTime() && m_timerType->SupportsEndTime() && + m_endLocalTime < m_startLocalTime) + { + HELPERS::ShowOKDialogText(CVariant{19065}, // "Timer settings" + CVariant{19072}); // In order to add/update a timer + return false; + } + } + + return true; +} + +bool CGUIDialogPVRTimerSettings::Save() +{ + if (!Validate()) + return false; + + // Timer type + m_timerInfoTag->SetTimerType(m_timerType); + + // Timer active/inactive + m_timerInfoTag->m_state = m_bTimerActive ? PVR_TIMER_STATE_SCHEDULED : PVR_TIMER_STATE_DISABLED; + + // Name + m_timerInfoTag->m_strTitle = m_strTitle; + + // epg search string (only for epg-based timer rules) + m_timerInfoTag->m_strEpgSearchString = m_strEpgSearchString; + + // epg fulltext search, instead of just title match. (only for epg-based timer rules) + m_timerInfoTag->m_bFullTextEpgSearch = m_bFullTextEpgSearch; + + // Channel + m_timerInfoTag->m_iClientChannelUid = m_channel.channelUid; + m_timerInfoTag->m_iClientId = m_channel.clientId; + m_timerInfoTag->m_bIsRadio = m_bIsRadio; + m_timerInfoTag->UpdateChannel(); + + if (!m_timerType->SupportsStartAnyTime() || + !m_timerType->IsEpgBased()) // Start anytime toggle is not displayed + m_bStartAnyTime = false; // Assume start time change needs checking for + m_timerInfoTag->m_bStartAnyTime = m_bStartAnyTime; + + if (!m_timerType->SupportsEndAnyTime() || + !m_timerType->IsEpgBased()) // End anytime toggle is not displayed + m_bEndAnyTime = false; // Assume end time change needs checking for + m_timerInfoTag->m_bEndAnyTime = m_bEndAnyTime; + + // Begin and end time + if (!m_bStartAnyTime && !m_bEndAnyTime) + { + if (m_timerType->SupportsStartTime() && // has start clock entry + m_timerType->SupportsEndTime() && // and end clock entry + m_timerType->IsTimerRule()) // but no associated start/end day spinners + { + if (m_endLocalTime < m_startLocalTime) // And the end clock is earlier than the start clock + { + CLog::LogFC(LOGDEBUG, LOGPVR, "End before start, adding a day."); + m_endLocalTime += CDateTimeSpan(1, 0, 0, 0); + if (m_endLocalTime < m_startLocalTime) + { + CLog::Log(LOGWARNING, + "Timer settings dialog: End before start. Setting end time to start time."); + m_endLocalTime = m_startLocalTime; + } + } + else if (m_endLocalTime > + (m_startLocalTime + CDateTimeSpan(1, 0, 0, 0))) // Or the duration is more than a day + { + CLog::LogFC(LOGDEBUG, LOGPVR, "End > 1 day after start, removing a day."); + m_endLocalTime -= CDateTimeSpan(1, 0, 0, 0); + if (m_endLocalTime > (m_startLocalTime + CDateTimeSpan(1, 0, 0, 0))) + { + CLog::Log( + LOGWARNING, + "Timer settings dialog: End > 1 day after start. Setting end time to start time."); + m_endLocalTime = m_startLocalTime; + } + } + } + else if (m_endLocalTime < m_startLocalTime) + { + // this case will fail validation so this can't be reached. + } + m_timerInfoTag->SetStartFromLocalTime(m_startLocalTime); + m_timerInfoTag->SetEndFromLocalTime(m_endLocalTime); + } + else if (!m_bStartAnyTime) + m_timerInfoTag->SetStartFromLocalTime(m_startLocalTime); + else if (!m_bEndAnyTime) + m_timerInfoTag->SetEndFromLocalTime(m_endLocalTime); + + // Days of week (only for timer rules) + if (m_timerType->IsTimerRule()) + m_timerInfoTag->m_iWeekdays = m_iWeekdays; + else + m_timerInfoTag->m_iWeekdays = PVR_WEEKDAY_NONE; + + // First day (only for timer rules) + m_timerInfoTag->SetFirstDayFromLocalTime(m_firstDayLocalTime); + + // "New episodes only" (only for timer rules) + m_timerInfoTag->m_iPreventDupEpisodes = m_iPreventDupEpisodes; + + // Pre and post record time + m_timerInfoTag->m_iMarginStart = m_iMarginStart; + m_timerInfoTag->m_iMarginEnd = m_iMarginEnd; + + // Priority + m_timerInfoTag->m_iPriority = m_iPriority; + + // Lifetime + m_timerInfoTag->m_iLifetime = m_iLifetime; + + // MaxRecordings + m_timerInfoTag->m_iMaxRecordings = m_iMaxRecordings; + + // Recording folder + m_timerInfoTag->m_strDirectory = m_strDirectory; + + // Recording group + m_timerInfoTag->m_iRecordingGroup = m_iRecordingGroup; + + // Set the timer's title to the channel name if it's empty or 'New Timer' + if (m_strTitle.empty() || m_strTitle == g_localizeStrings.Get(19056)) + { + const std::string channelName = m_timerInfoTag->ChannelName(); + if (!channelName.empty()) + m_timerInfoTag->m_strTitle = channelName; + } + + // Update summary + m_timerInfoTag->UpdateSummary(); + + return true; +} + +void CGUIDialogPVRTimerSettings::SetButtonLabels() +{ + // timer start time + BaseSettingControlPtr settingControl = GetSettingControl(SETTING_TMR_BEGIN); + if (settingControl != NULL && settingControl->GetControl() != NULL) + { + SET_CONTROL_LABEL2(settingControl->GetID(), m_timerStartTimeStr); + } + + // timer end time + settingControl = GetSettingControl(SETTING_TMR_END); + if (settingControl != NULL && settingControl->GetControl() != NULL) + { + SET_CONTROL_LABEL2(settingControl->GetID(), m_timerEndTimeStr); + } +} + +void CGUIDialogPVRTimerSettings::AddCondition(const std::shared_ptr<CSetting>& setting, + const std::string& identifier, + SettingConditionCheck condition, + SettingDependencyType depType, + const std::string& settingId) +{ + GetSettingsManager()->AddDynamicCondition(identifier, condition, this); + CSettingDependency dep(depType, GetSettingsManager()); + dep.And()->Add(CSettingDependencyConditionPtr( + new CSettingDependencyCondition(identifier, "true", settingId, false, GetSettingsManager()))); + SettingDependencies deps(setting->GetDependencies()); + deps.push_back(dep); + setting->SetDependencies(deps); +} + +int CGUIDialogPVRTimerSettings::GetDateAsIndex(const CDateTime& datetime) +{ + const CDateTime date(datetime.GetYear(), datetime.GetMonth(), datetime.GetDay(), 0, 0, 0); + time_t t(0); + date.GetAsTime(t); + return static_cast<int>(t); +} + +void CGUIDialogPVRTimerSettings::SetDateFromIndex(CDateTime& datetime, int date) +{ + const CDateTime newDate(static_cast<time_t>(date)); + datetime.SetDateTime(newDate.GetYear(), newDate.GetMonth(), newDate.GetDay(), datetime.GetHour(), + datetime.GetMinute(), datetime.GetSecond()); +} + +void CGUIDialogPVRTimerSettings::SetTimeFromSystemTime(CDateTime& datetime, + const KODI::TIME::SystemTime& time) +{ + const CDateTime newTime(time); + datetime.SetDateTime(datetime.GetYear(), datetime.GetMonth(), datetime.GetDay(), + newTime.GetHour(), newTime.GetMinute(), newTime.GetSecond()); +} + +void CGUIDialogPVRTimerSettings::InitializeTypesList() +{ + m_typeEntries.clear(); + + // If timer is read-only or was created by a timer rule, only add current type, for information. Type can't be changed. + if (m_timerType->IsReadOnly() || m_timerInfoTag->HasParent()) + { + m_typeEntries.insert(std::make_pair(0, m_timerType)); + return; + } + + bool bFoundThisType(false); + int idx(0); + const std::vector<std::shared_ptr<CPVRTimerType>> types(CPVRTimerType::GetAllTypes()); + for (const auto& type : types) + { + // Type definition prohibits created of new instances. + // But the dialog can act as a viewer for these types. + if (type->ForbidsNewInstances()) + continue; + + // Read-only timers cannot be created using this dialog. + // But the dialog can act as a viewer for read-only types. + if (type->IsReadOnly()) + continue; + + // Drop TimerTypes that require EPGInfo, if none is populated + if (type->RequiresEpgTagOnCreate() && !m_timerInfoTag->GetEpgInfoTag()) + continue; + + // Drop TimerTypes without 'Series' EPG attributes if none are set + if (type->RequiresEpgSeriesOnCreate()) + { + const std::shared_ptr<CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag()); + if (epgTag && !epgTag->IsSeries()) + continue; + } + + // Drop TimerTypes which need series link if none is set + if (type->RequiresEpgSeriesLinkOnCreate()) + { + const std::shared_ptr<CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag()); + if (!epgTag || epgTag->SeriesLink().empty()) + continue; + } + + // Drop TimerTypes that forbid EPGInfo, if it is populated + if (type->ForbidsEpgTagOnCreate() && m_timerInfoTag->GetEpgInfoTag()) + continue; + + // Drop TimerTypes that aren't rules and cannot be recorded + if (!type->IsTimerRule()) + { + const std::shared_ptr<CPVREpgInfoTag> epgTag(m_timerInfoTag->GetEpgInfoTag()); + bool bCanRecord = epgTag ? epgTag->IsRecordable() + : m_timerInfoTag->EndAsLocalTime() > CDateTime::GetCurrentDateTime(); + if (!bCanRecord) + continue; + } + + if (!bFoundThisType && *type == *m_timerType) + bFoundThisType = true; + + m_typeEntries.insert(std::make_pair(idx++, type)); + } + + if (!bFoundThisType) + m_typeEntries.insert(std::make_pair(idx++, m_timerType)); +} + +void CGUIDialogPVRTimerSettings::InitializeChannelsList() +{ + m_channelEntries.clear(); + + int index = 0; + + // Add special "any channel" entries - one for every client (used for epg-based timer rules). + const CPVRClientMap clients = CServiceBroker::GetPVRManager().Clients()->GetCreatedClients(); + for (const auto& client : clients) + { + m_channelEntries.insert( + {index, ChannelDescriptor(PVR_CHANNEL_INVALID_UID, client.second->GetID(), + g_localizeStrings.Get(809))}); // "Any channel" + ++index; + } + + // Add regular channels + const std::shared_ptr<CPVRChannelGroup> allGroup = + CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(m_bIsRadio); + const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers = + allGroup->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE); + for (const auto& groupMember : groupMembers) + { + const std::shared_ptr<CPVRChannel> channel = groupMember->Channel(); + const std::string channelDescription = StringUtils::Format( + "{} {}", groupMember->ChannelNumber().FormattedChannelNumber(), channel->ChannelName()); + m_channelEntries.insert( + {index, ChannelDescriptor(channel->UniqueID(), channel->ClientID(), channelDescription)}); + ++index; + } +} + +void CGUIDialogPVRTimerSettings::TypesFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + current = 0; + + static const std::vector<std::pair<std::string, CVariant>> reminderTimerProps{ + std::make_pair("PVR.IsRemindingTimer", CVariant{true})}; + static const std::vector<std::pair<std::string, CVariant>> recordingTimerProps{ + std::make_pair("PVR.IsRecordingTimer", CVariant{true})}; + + const auto clients = CServiceBroker::GetPVRManager().Clients(); + + bool foundCurrent(false); + for (const auto& typeEntry : pThis->m_typeEntries) + { + std::string clientName; + + const auto client = clients->GetCreatedClient(typeEntry.second->GetClientId()); + if (client) + clientName = client->GetFriendlyName(); + + list.emplace_back(typeEntry.second->GetDescription(), clientName, typeEntry.first, + typeEntry.second->IsReminder() ? reminderTimerProps : recordingTimerProps); + + if (!foundCurrent && (*(pThis->m_timerType) == *(typeEntry.second))) + { + current = typeEntry.first; + foundCurrent = true; + } + } + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +void CGUIDialogPVRTimerSettings::ChannelsFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + current = 0; + + bool foundCurrent(false); + for (const auto& channelEntry : pThis->m_channelEntries) + { + // Only include channels for the currently selected timer type or all channels if type is client-independent. + if (pThis->m_timerType->GetClientId() == -1 || // client-independent + pThis->m_timerType->GetClientId() == channelEntry.second.clientId) + { + // Do not add "any channel" entry if not supported by selected timer type. + if (channelEntry.second.channelUid == PVR_CHANNEL_INVALID_UID && + !pThis->m_timerType->SupportsAnyChannel()) + continue; + + list.emplace_back( + IntegerSettingOption(channelEntry.second.description, channelEntry.first)); + } + + if (!foundCurrent && (pThis->m_channel == channelEntry.second)) + { + current = channelEntry.first; + foundCurrent = true; + } + } + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +void CGUIDialogPVRTimerSettings::DaysFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + current = 0; + + // Data range: "today" until "yesterday next year" + const CDateTime now(CDateTime::GetCurrentDateTime()); + CDateTime time(now.GetYear(), now.GetMonth(), now.GetDay(), 0, 0, 0); + const CDateTime yesterdayPlusOneYear(CDateTime(time.GetYear() + 1, time.GetMonth(), + time.GetDay(), time.GetHour(), time.GetMinute(), + time.GetSecond()) - + CDateTimeSpan(1, 0, 0, 0)); + + CDateTime oldCDateTime; + if (setting->GetId() == SETTING_TMR_FIRST_DAY) + oldCDateTime = pThis->m_timerInfoTag->FirstDayAsLocalTime(); + else if (setting->GetId() == SETTING_TMR_START_DAY) + oldCDateTime = pThis->m_timerInfoTag->StartAsLocalTime(); + else + oldCDateTime = pThis->m_timerInfoTag->EndAsLocalTime(); + const CDateTime oldCDate(oldCDateTime.GetYear(), oldCDateTime.GetMonth(), oldCDateTime.GetDay(), + 0, 0, 0); + + if ((oldCDate < time) || (oldCDate > yesterdayPlusOneYear)) + list.emplace_back(oldCDate.GetAsLocalizedDate(true /*long date*/), GetDateAsIndex(oldCDate)); + + while (time <= yesterdayPlusOneYear) + { + list.emplace_back(time.GetAsLocalizedDate(true /*long date*/), GetDateAsIndex(time)); + time += CDateTimeSpan(1, 0, 0, 0); + } + + if (setting->GetId() == SETTING_TMR_FIRST_DAY) + current = GetDateAsIndex(pThis->m_firstDayLocalTime); + else if (setting->GetId() == SETTING_TMR_START_DAY) + current = GetDateAsIndex(pThis->m_startLocalTime); + else + current = GetDateAsIndex(pThis->m_endLocalTime); + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +void CGUIDialogPVRTimerSettings::DupEpisodesFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + + std::vector<std::pair<std::string, int>> values; + pThis->m_timerType->GetPreventDuplicateEpisodesValues(values); + std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) { + return IntegerSettingOption(value.first, value.second); + }); + + current = pThis->m_iPreventDupEpisodes; + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +void CGUIDialogPVRTimerSettings::WeekdaysFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + list.emplace_back(g_localizeStrings.Get(831), PVR_WEEKDAY_MONDAY); // "Mondays" + list.emplace_back(g_localizeStrings.Get(832), PVR_WEEKDAY_TUESDAY); // "Tuesdays" + list.emplace_back(g_localizeStrings.Get(833), PVR_WEEKDAY_WEDNESDAY); // "Wednesdays" + list.emplace_back(g_localizeStrings.Get(834), PVR_WEEKDAY_THURSDAY); // "Thursdays" + list.emplace_back(g_localizeStrings.Get(835), PVR_WEEKDAY_FRIDAY); // "Fridays" + list.emplace_back(g_localizeStrings.Get(836), PVR_WEEKDAY_SATURDAY); // "Saturdays" + list.emplace_back(g_localizeStrings.Get(837), PVR_WEEKDAY_SUNDAY); // "Sundays" + + current = pThis->m_iWeekdays; + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +void CGUIDialogPVRTimerSettings::PrioritiesFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + + std::vector<std::pair<std::string, int>> values; + pThis->m_timerType->GetPriorityValues(values); + std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) { + return IntegerSettingOption(value.first, value.second); + }); + + current = pThis->m_iPriority; + + auto it = list.begin(); + while (it != list.end()) + { + if (it->value == current) + break; // value already in list + + ++it; + } + + if (it == list.end()) + { + // PVR backend supplied value is not in the list of predefined values. Insert it. + list.insert(it, IntegerSettingOption(std::to_string(current), current)); + } + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +void CGUIDialogPVRTimerSettings::LifetimesFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + + std::vector<std::pair<std::string, int>> values; + pThis->m_timerType->GetLifetimeValues(values); + std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) { + return IntegerSettingOption(value.first, value.second); + }); + + current = pThis->m_iLifetime; + + auto it = list.begin(); + while (it != list.end()) + { + if (it->value == current) + break; // value already in list + + ++it; + } + + if (it == list.end()) + { + // PVR backend supplied value is not in the list of predefined values. Insert it. + list.insert(it, IntegerSettingOption( + StringUtils::Format(g_localizeStrings.Get(17999), current) /* {} days */, + current)); + } + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +void CGUIDialogPVRTimerSettings::MaxRecordingsFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + + std::vector<std::pair<std::string, int>> values; + pThis->m_timerType->GetMaxRecordingsValues(values); + std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) { + return IntegerSettingOption(value.first, value.second); + }); + + current = pThis->m_iMaxRecordings; + + auto it = list.begin(); + while (it != list.end()) + { + if (it->value == current) + break; // value already in list + + ++it; + } + + if (it == list.end()) + { + // PVR backend supplied value is not in the list of predefined values. Insert it. + list.insert(it, IntegerSettingOption(std::to_string(current), current)); + } + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +void CGUIDialogPVRTimerSettings::RecordingGroupFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + + std::vector<std::pair<std::string, int>> values; + pThis->m_timerType->GetRecordingGroupValues(values); + std::transform(values.cbegin(), values.cend(), std::back_inserter(list), [](const auto& value) { + return IntegerSettingOption(value.first, value.second); + }); + + current = pThis->m_iRecordingGroup; + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +void CGUIDialogPVRTimerSettings::MarginTimeFiller(const SettingConstPtr& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data) +{ + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis) + { + list.clear(); + + // Get global settings values + CPVRSettings::MarginTimeFiller(setting, list, current, data); + + if (setting->GetId() == SETTING_TMR_BEGIN_PRE) + current = pThis->m_iMarginStart; + else + current = pThis->m_iMarginEnd; + + bool bInsertValue = true; + auto it = list.begin(); + while (it != list.end()) + { + if (it->value == current) + { + bInsertValue = false; + break; // value already in list + } + + if (it->value > current) + break; + + ++it; + } + + if (bInsertValue) + { + // PVR backend supplied value is not in the list of predefined values. Insert it. + list.insert(it, IntegerSettingOption( + StringUtils::Format(g_localizeStrings.Get(14044), current) /* {} min */, + current)); + } + } + else + CLog::LogF(LOGERROR, "No dialog"); +} + +std::string CGUIDialogPVRTimerSettings::WeekdaysValueFormatter(const SettingConstPtr& setting) +{ + return CPVRTimerInfoTag::GetWeekdaysString(GetWeekdaysFromSetting(setting), true, true); +} + +void CGUIDialogPVRTimerSettings::AddTypeDependentEnableCondition( + const std::shared_ptr<CSetting>& setting, const std::string& identifier) +{ + // Enable setting depending on read-only attribute of the selected timer type + std::string id(identifier); + id.append(TYPE_DEP_ENABLE_COND_ID_POSTFIX); + AddCondition(setting, id, TypeReadOnlyCondition, SettingDependencyType::Enable, SETTING_TMR_TYPE); +} + +bool CGUIDialogPVRTimerSettings::TypeReadOnlyCondition(const std::string& condition, + const std::string& value, + const SettingConstPtr& setting, + void* data) +{ + if (setting == NULL) + return false; + + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis == NULL) + { + CLog::LogF(LOGERROR, "No dialog"); + return false; + } + + if (!StringUtils::EqualsNoCase(value, "true")) + return false; + + std::string cond(condition); + cond.erase(cond.find(TYPE_DEP_ENABLE_COND_ID_POSTFIX)); + + // If only one type is available, disable type selector. + if (pThis->m_typeEntries.size() == 1) + { + if (cond == SETTING_TMR_TYPE) + return false; + } + + // For existing one time epg-based timers, disable editing of epg-filled data. + if (!pThis->m_bIsNewTimer && pThis->m_timerType->IsEpgBasedOnetime()) + { + if ((cond == SETTING_TMR_NAME) || (cond == SETTING_TMR_CHANNEL) || + (cond == SETTING_TMR_START_DAY) || (cond == SETTING_TMR_END_DAY) || + (cond == SETTING_TMR_BEGIN) || (cond == SETTING_TMR_END)) + return false; + } + + /* Always enable enable/disable, if supported by the timer type. */ + if (pThis->m_timerType->SupportsEnableDisable() && !pThis->m_timerInfoTag->IsBroken()) + { + if (cond == SETTING_TMR_ACTIVE) + return true; + } + + // Let the PVR client decide... + int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + const auto entry = pThis->m_typeEntries.find(idx); + if (entry != pThis->m_typeEntries.end()) + return !entry->second->IsReadOnly(); + else + CLog::LogF(LOGERROR, "No type entry"); + + return false; +} + +void CGUIDialogPVRTimerSettings::AddTypeDependentVisibilityCondition( + const std::shared_ptr<CSetting>& setting, const std::string& identifier) +{ + // Show or hide setting depending on attributes of the selected timer type + std::string id(identifier); + id.append(TYPE_DEP_VISIBI_COND_ID_POSTFIX); + AddCondition(setting, id, TypeSupportsCondition, SettingDependencyType::Visible, + SETTING_TMR_TYPE); +} + +bool CGUIDialogPVRTimerSettings::TypeSupportsCondition(const std::string& condition, + const std::string& value, + const SettingConstPtr& setting, + void* data) +{ + if (setting == NULL) + return false; + + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis == NULL) + { + CLog::LogF(LOGERROR, "No dialog"); + return false; + } + + if (!StringUtils::EqualsNoCase(value, "true")) + return false; + + int idx = std::static_pointer_cast<const CSettingInt>(setting)->GetValue(); + const auto entry = pThis->m_typeEntries.find(idx); + if (entry != pThis->m_typeEntries.end()) + { + std::string cond(condition); + cond.erase(cond.find(TYPE_DEP_VISIBI_COND_ID_POSTFIX)); + + if (cond == SETTING_TMR_EPGSEARCH) + return entry->second->SupportsEpgTitleMatch() || entry->second->SupportsEpgFulltextMatch(); + else if (cond == SETTING_TMR_FULLTEXT) + return entry->second->SupportsEpgFulltextMatch(); + else if (cond == SETTING_TMR_ACTIVE) + return entry->second->SupportsEnableDisable(); + else if (cond == SETTING_TMR_CHANNEL) + return entry->second->SupportsChannels(); + else if (cond == SETTING_TMR_START_ANYTIME) + return entry->second->SupportsStartAnyTime() && entry->second->IsEpgBased(); + else if (cond == SETTING_TMR_END_ANYTIME) + return entry->second->SupportsEndAnyTime() && entry->second->IsEpgBased(); + else if (cond == SETTING_TMR_START_DAY) + return entry->second->SupportsStartTime() && entry->second->IsOnetime(); + else if (cond == SETTING_TMR_END_DAY) + return entry->second->SupportsEndTime() && entry->second->IsOnetime(); + else if (cond == SETTING_TMR_BEGIN) + return entry->second->SupportsStartTime(); + else if (cond == SETTING_TMR_END) + return entry->second->SupportsEndTime(); + else if (cond == SETTING_TMR_WEEKDAYS) + return entry->second->SupportsWeekdays(); + else if (cond == SETTING_TMR_FIRST_DAY) + return entry->second->SupportsFirstDay(); + else if (cond == SETTING_TMR_NEW_EPISODES) + return entry->second->SupportsRecordOnlyNewEpisodes(); + else if (cond == SETTING_TMR_BEGIN_PRE) + return entry->second->SupportsStartMargin(); + else if (cond == SETTING_TMR_END_POST) + return entry->second->SupportsEndMargin(); + else if (cond == SETTING_TMR_PRIORITY) + return entry->second->SupportsPriority(); + else if (cond == SETTING_TMR_LIFETIME) + return entry->second->SupportsLifetime(); + else if (cond == SETTING_TMR_MAX_REC) + return entry->second->SupportsMaxRecordings(); + else if (cond == SETTING_TMR_DIR) + return entry->second->SupportsRecordingFolders(); + else if (cond == SETTING_TMR_REC_GROUP) + return entry->second->SupportsRecordingGroup(); + else + CLog::LogF(LOGERROR, "Unknown condition"); + } + else + { + CLog::LogF(LOGERROR, "No type entry"); + } + return false; +} + +void CGUIDialogPVRTimerSettings::AddStartAnytimeDependentVisibilityCondition( + const std::shared_ptr<CSetting>& setting, const std::string& identifier) +{ + // Show or hide setting depending on value of setting "any time" + std::string id(identifier); + id.append(START_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX); + AddCondition(setting, id, StartAnytimeSetCondition, SettingDependencyType::Visible, + SETTING_TMR_START_ANYTIME); +} + +bool CGUIDialogPVRTimerSettings::StartAnytimeSetCondition(const std::string& condition, + const std::string& value, + const SettingConstPtr& setting, + void* data) +{ + if (setting == NULL) + return false; + + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis == NULL) + { + CLog::LogF(LOGERROR, "No dialog"); + return false; + } + + if (!StringUtils::EqualsNoCase(value, "true")) + return false; + + // "any time" setting is only relevant for epg-based timers. + if (!pThis->m_timerType->IsEpgBased()) + return true; + + // If 'Start anytime' option isn't supported, don't hide start time + if (!pThis->m_timerType->SupportsStartAnyTime()) + return true; + + std::string cond(condition); + cond.erase(cond.find(START_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX)); + + if ((cond == SETTING_TMR_START_DAY) || (cond == SETTING_TMR_BEGIN)) + { + bool bAnytime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + return !bAnytime; + } + return false; +} + +void CGUIDialogPVRTimerSettings::AddEndAnytimeDependentVisibilityCondition( + const std::shared_ptr<CSetting>& setting, const std::string& identifier) +{ + // Show or hide setting depending on value of setting "any time" + std::string id(identifier); + id.append(END_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX); + AddCondition(setting, id, EndAnytimeSetCondition, SettingDependencyType::Visible, + SETTING_TMR_END_ANYTIME); +} + +bool CGUIDialogPVRTimerSettings::EndAnytimeSetCondition(const std::string& condition, + const std::string& value, + const SettingConstPtr& setting, + void* data) +{ + if (setting == NULL) + return false; + + CGUIDialogPVRTimerSettings* pThis = static_cast<CGUIDialogPVRTimerSettings*>(data); + if (pThis == NULL) + { + CLog::LogF(LOGERROR, "No dialog"); + return false; + } + + if (!StringUtils::EqualsNoCase(value, "true")) + return false; + + // "any time" setting is only relevant for epg-based timers. + if (!pThis->m_timerType->IsEpgBased()) + return true; + + // If 'End anytime' option isn't supported, don't hide end time + if (!pThis->m_timerType->SupportsEndAnyTime()) + return true; + + std::string cond(condition); + cond.erase(cond.find(END_ANYTIME_DEP_VISIBI_COND_ID_POSTFIX)); + + if ((cond == SETTING_TMR_END_DAY) || (cond == SETTING_TMR_END)) + { + bool bAnytime = std::static_pointer_cast<const CSettingBool>(setting)->GetValue(); + return !bAnytime; + } + return false; +} diff --git a/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h new file mode 100644 index 0000000..d3e9e3e --- /dev/null +++ b/xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2012-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. + */ + +#pragma once + +#include "XBDateTime.h" +#include "addons/kodi-dev-kit/include/kodi/c-api/addon-instance/pvr/pvr_channels.h" // PVR_CHANNEL_INVALID_UID +#include "settings/SettingConditions.h" +#include "settings/dialogs/GUIDialogSettingsManualBase.h" +#include "settings/lib/SettingDependency.h" + +#include <map> +#include <memory> +#include <string> +#include <vector> + +class CSetting; + +struct IntegerSettingOption; + +namespace PVR +{ +class CPVRTimerInfoTag; +class CPVRTimerType; + +class CGUIDialogPVRTimerSettings : public CGUIDialogSettingsManualBase +{ +public: + CGUIDialogPVRTimerSettings(); + ~CGUIDialogPVRTimerSettings() override; + + bool CanBeActivated() const override; + + void SetTimer(const std::shared_ptr<CPVRTimerInfoTag>& timer); + +protected: + // implementation of ISettingCallback + void OnSettingChanged(const std::shared_ptr<const CSetting>& setting) override; + void OnSettingAction(const std::shared_ptr<const CSetting>& setting) override; + + // specialization of CGUIDialogSettingsBase + bool AllowResettingSettings() const override { return false; } + bool Save() override; + void SetupView() override; + + // specialization of CGUIDialogSettingsManualBase + void InitializeSettings() override; + +private: + bool Validate(); + void InitializeTypesList(); + void InitializeChannelsList(); + void SetButtonLabels(); + + static int GetDateAsIndex(const CDateTime& datetime); + static void SetDateFromIndex(CDateTime& datetime, int date); + static void SetTimeFromSystemTime(CDateTime& datetime, const KODI::TIME::SystemTime& time); + + static int GetWeekdaysFromSetting(const std::shared_ptr<const CSetting>& setting); + + static void TypesFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + static void ChannelsFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + static void DaysFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + static void DupEpisodesFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + static void WeekdaysFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + static void PrioritiesFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + static void LifetimesFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + static void MaxRecordingsFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + static void RecordingGroupFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + static void MarginTimeFiller(const std::shared_ptr<const CSetting>& setting, + std::vector<IntegerSettingOption>& list, + int& current, + void* data); + + static std::string WeekdaysValueFormatter(const std::shared_ptr<const CSetting>& setting); + + void AddCondition(const std::shared_ptr<CSetting>& setting, + const std::string& identifier, + SettingConditionCheck condition, + SettingDependencyType depType, + const std::string& settingId); + + void AddTypeDependentEnableCondition(const std::shared_ptr<CSetting>& setting, + const std::string& identifier); + static bool TypeReadOnlyCondition(const std::string& condition, + const std::string& value, + const std::shared_ptr<const CSetting>& setting, + void* data); + + void AddTypeDependentVisibilityCondition(const std::shared_ptr<CSetting>& setting, + const std::string& identifier); + static bool TypeSupportsCondition(const std::string& condition, + const std::string& value, + const std::shared_ptr<const CSetting>& setting, + void* data); + + void AddStartAnytimeDependentVisibilityCondition(const std::shared_ptr<CSetting>& setting, + const std::string& identifier); + static bool StartAnytimeSetCondition(const std::string& condition, + const std::string& value, + const std::shared_ptr<const CSetting>& setting, + void* data); + void AddEndAnytimeDependentVisibilityCondition(const std::shared_ptr<CSetting>& setting, + const std::string& identifier); + static bool EndAnytimeSetCondition(const std::string& condition, + const std::string& value, + const std::shared_ptr<const CSetting>& setting, + void* data); + + typedef std::map<int, std::shared_ptr<CPVRTimerType>> TypeEntriesMap; + + typedef struct ChannelDescriptor + { + int channelUid; + int clientId; + std::string description; + + ChannelDescriptor(int _channelUid = PVR_CHANNEL_INVALID_UID, + int _clientId = -1, + const std::string& _description = "") + : channelUid(_channelUid), clientId(_clientId), description(_description) + { + } + + inline bool operator==(const ChannelDescriptor& right) const + { + return (channelUid == right.channelUid && clientId == right.clientId && + description == right.description); + } + + } ChannelDescriptor; + + typedef std::map<int, ChannelDescriptor> ChannelEntriesMap; + + std::shared_ptr<CPVRTimerInfoTag> m_timerInfoTag; + TypeEntriesMap m_typeEntries; + ChannelEntriesMap m_channelEntries; + std::string m_timerStartTimeStr; + std::string m_timerEndTimeStr; + + std::shared_ptr<CPVRTimerType> m_timerType; + bool m_bIsRadio = false; + bool m_bIsNewTimer = true; + bool m_bTimerActive = false; + std::string m_strTitle; + std::string m_strEpgSearchString; + bool m_bFullTextEpgSearch = true; + ChannelDescriptor m_channel; + CDateTime m_startLocalTime; + CDateTime m_endLocalTime; + bool m_bStartAnyTime = false; + bool m_bEndAnyTime = false; + unsigned int m_iWeekdays; + CDateTime m_firstDayLocalTime; + unsigned int m_iPreventDupEpisodes = 0; + unsigned int m_iMarginStart = 0; + unsigned int m_iMarginEnd = 0; + int m_iPriority = 0; + int m_iLifetime = 0; + int m_iMaxRecordings = 0; + std::string m_strDirectory; + unsigned int m_iRecordingGroup = 0; +}; +} // namespace PVR |