summaryrefslogtreecommitdiffstats
path: root/xbmc/pvr/dialogs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/pvr/dialogs
parentInitial commit. (diff)
downloadkodi-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')
-rw-r--r--xbmc/pvr/dialogs/CMakeLists.txt29
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.cpp76
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelGuide.h34
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelManager.cpp1076
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelManager.h95
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.cpp299
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRChannelsOSD.h61
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRClientPriorities.cpp102
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRClientPriorities.h41
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGroupManager.cpp546
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGroupManager.h75
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideControls.cpp19
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideControls.h21
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp255
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.h44
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.cpp391
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideSearch.h64
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRItemsViewBase.cpp154
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRItemsViewBase.h47
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.cpp216
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRadioRDSInfo.h60
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.cpp102
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRecordingInfo.h37
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.cpp235
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRRecordingSettings.h58
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.cpp1513
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRTimerSettings.h196
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