From c04dcc2e7d834218ef2d4194331e383402495ae1 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 20:07:22 +0200 Subject: Adding upstream version 2:20.4+dfsg. Signed-off-by: Daniel Baumann --- xbmc/pvr/windows/GUIWindowPVRBase.cpp | 563 ++++++++++++++++++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100644 xbmc/pvr/windows/GUIWindowPVRBase.cpp (limited to 'xbmc/pvr/windows/GUIWindowPVRBase.cpp') diff --git a/xbmc/pvr/windows/GUIWindowPVRBase.cpp b/xbmc/pvr/windows/GUIWindowPVRBase.cpp new file mode 100644 index 0000000..96a14b5 --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRBase.cpp @@ -0,0 +1,563 @@ +/* + * 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 "GUIWindowPVRBase.h" + +#include "FileItem.h" +#include "GUIUserMessages.h" +#include "ServiceBroker.h" +#include "addons/AddonManager.h" +#include "addons/addoninfo/AddonType.h" +#include "dialogs/GUIDialogExtendedProgressBar.h" +#include "dialogs/GUIDialogSelect.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIMessage.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/LocalizeStrings.h" +#include "input/actions/Action.h" +#include "input/actions/ActionIDs.h" +#include "messaging/ApplicationMessenger.h" +#include "messaging/helpers/DialogOKHelper.h" +#include "pvr/PVRManager.h" +#include "pvr/PVRPlaybackState.h" +#include "pvr/channels/PVRChannelGroup.h" +#include "pvr/channels/PVRChannelGroups.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/filesystem/PVRGUIDirectory.h" +#include "pvr/guilib/PVRGUIActionsChannels.h" +#include "utils/Variant.h" +#include "utils/log.h" + +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +#define MAX_INVALIDATION_FREQUENCY 2000ms // limit to one invalidation per X milliseconds + +using namespace PVR; +using namespace KODI::MESSAGING; + +namespace PVR +{ + +class CGUIPVRChannelGroupsSelector +{ +public: + virtual ~CGUIPVRChannelGroupsSelector() = default; + + bool Initialize(CGUIWindow* parent, bool bRadio); + + bool HasFocus() const; + std::shared_ptr GetSelectedChannelGroup() const; + bool SelectChannelGroup(const std::shared_ptr& newGroup); + +private: + CGUIControl* m_control = nullptr; + std::vector> m_channelGroups; +}; + +} // namespace PVR + +bool CGUIPVRChannelGroupsSelector::Initialize(CGUIWindow* parent, bool bRadio) +{ + CGUIControl* control = parent->GetControl(CONTROL_LSTCHANNELGROUPS); + if (control && control->IsContainer()) + { + m_control = control; + m_channelGroups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(bRadio)->GetMembers(true); + + CFileItemList channelGroupItems; + CPVRGUIDirectory::GetChannelGroupsDirectory(bRadio, true, channelGroupItems); + + CGUIMessage msg(GUI_MSG_LABEL_BIND, m_control->GetID(), CONTROL_LSTCHANNELGROUPS, 0, 0, &channelGroupItems); + m_control->OnMessage(msg); + return true; + } + return false; +} + +bool CGUIPVRChannelGroupsSelector::HasFocus() const +{ + return m_control && m_control->HasFocus(); +} + +std::shared_ptr CGUIPVRChannelGroupsSelector::GetSelectedChannelGroup() const +{ + if (m_control) + { + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, m_control->GetID(), CONTROL_LSTCHANNELGROUPS); + m_control->OnMessage(msg); + + const auto it = std::next(m_channelGroups.begin(), msg.GetParam1()); + if (it != m_channelGroups.end()) + { + return *it; + } + } + return std::shared_ptr(); +} + +bool CGUIPVRChannelGroupsSelector::SelectChannelGroup(const std::shared_ptr& newGroup) +{ + if (m_control && newGroup) + { + int iIndex = 0; + for (const auto& group : m_channelGroups) + { + if (*newGroup == *group) + { + CGUIMessage msg(GUI_MSG_ITEM_SELECT, m_control->GetID(), CONTROL_LSTCHANNELGROUPS, iIndex); + m_control->OnMessage(msg); + return true; + } + ++iIndex; + } + } + return false; +} + +CGUIWindowPVRBase::CGUIWindowPVRBase(bool bRadio, int id, const std::string& xmlFile) : + CGUIMediaWindow(id, xmlFile.c_str()), + m_bRadio(bRadio), + m_channelGroupsSelector(new CGUIPVRChannelGroupsSelector), + m_progressHandle(nullptr) +{ + // prevent removable drives to appear in directory listing (base class default behavior). + m_rootDir.AllowNonLocalSources(false); + + CServiceBroker::GetPVRManager().Events().Subscribe(this, &CGUIWindowPVRBase::Notify); +} + +CGUIWindowPVRBase::~CGUIWindowPVRBase() +{ + if (m_channelGroup) + m_channelGroup->Events().Unsubscribe(this); + + CServiceBroker::GetPVRManager().Events().Unsubscribe(this); +} + +void CGUIWindowPVRBase::UpdateSelectedItemPath() +{ + CServiceBroker::GetPVRManager().Get().SetSelectedChannelPath( + m_bRadio, m_viewControl.GetSelectedItemPath()); +} + +void CGUIWindowPVRBase::Notify(const PVREvent& event) +{ + // call virtual event handler function + NotifyEvent(event); +} + +void CGUIWindowPVRBase::NotifyEvent(const PVREvent& event) +{ + if (event == PVREvent::ManagerStopped) + { + ClearData(); + } + else if (m_active) + { + if (event == PVREvent::SystemSleep) + { + CGUIMessage m(GUI_MSG_SYSTEM_SLEEP, GetID(), 0, static_cast(event)); + CServiceBroker::GetAppMessenger()->SendGUIMessage(m); + } + else if (event == PVREvent::SystemWake) + { + CGUIMessage m(GUI_MSG_SYSTEM_WAKE, GetID(), 0, static_cast(event)); + CServiceBroker::GetAppMessenger()->SendGUIMessage(m); + } + else + { + CGUIMessage m(GUI_MSG_REFRESH_LIST, GetID(), 0, static_cast(event)); + CServiceBroker::GetAppMessenger()->SendGUIMessage(m); + } + } +} + +bool CGUIWindowPVRBase::OnAction(const CAction& action) +{ + switch (action.GetID()) + { + case ACTION_PREVIOUS_CHANNELGROUP: + ActivatePreviousChannelGroup(); + return true; + + case ACTION_NEXT_CHANNELGROUP: + ActivateNextChannelGroup(); + return true; + + case ACTION_MOVE_RIGHT: + case ACTION_MOVE_LEFT: + { + if (m_channelGroupsSelector->HasFocus() && CGUIMediaWindow::OnAction(action)) + { + SetChannelGroup(m_channelGroupsSelector->GetSelectedChannelGroup()); + return true; + } + } + } + + return CGUIMediaWindow::OnAction(action); +} + +bool CGUIWindowPVRBase::ActivatePreviousChannelGroup() +{ + const std::shared_ptr channelGroup = GetChannelGroup(); + if (channelGroup) + { + const CPVRChannelGroups* groups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(channelGroup->IsRadio()); + if (groups) + { + SetChannelGroup(groups->GetPreviousGroup(*channelGroup)); + return true; + } + } + return false; +} + +bool CGUIWindowPVRBase::ActivateNextChannelGroup() +{ + const std::shared_ptr channelGroup = GetChannelGroup(); + if (channelGroup) + { + const CPVRChannelGroups* groups = CServiceBroker::GetPVRManager().ChannelGroups()->Get(channelGroup->IsRadio()); + if (groups) + { + SetChannelGroup(groups->GetNextGroup(*channelGroup)); + return true; + } + } + return false; +} + +void CGUIWindowPVRBase::ClearData() +{ + std::unique_lock lock(m_critSection); + m_channelGroup.reset(); + m_channelGroupsSelector.reset(new CGUIPVRChannelGroupsSelector); +} + +void CGUIWindowPVRBase::OnInitWindow() +{ + SetProperty("IsRadio", m_bRadio ? "true" : ""); + + if (InitChannelGroup()) + { + m_channelGroupsSelector->Initialize(this, m_bRadio); + + CGUIMediaWindow::OnInitWindow(); + + // mark item as selected by channel path + m_viewControl.SetSelectedItem( + CServiceBroker::GetPVRManager().Get().GetSelectedChannelPath(m_bRadio)); + + // This has to be done after base class OnInitWindow to restore correct selection + m_channelGroupsSelector->SelectChannelGroup(GetChannelGroup()); + } + else + { + CGUIWindow::OnInitWindow(); // do not call CGUIMediaWindow as it will do a Refresh which in no case works in this state (no channelgroup!) + ShowProgressDialog(g_localizeStrings.Get(19235), 0); // PVR manager is starting up + } +} + +void CGUIWindowPVRBase::OnDeinitWindow(int nextWindowID) +{ + HideProgressDialog(); + UpdateSelectedItemPath(); + CGUIMediaWindow::OnDeinitWindow(nextWindowID); +} + +bool CGUIWindowPVRBase::OnMessage(CGUIMessage& message) +{ + bool bReturn = false; + switch (message.GetMessage()) + { + case GUI_MSG_CLICKED: + { + switch (message.GetSenderId()) + { + case CONTROL_BTNCHANNELGROUPS: + return OpenChannelGroupSelectionDialog(); + + case CONTROL_LSTCHANNELGROUPS: + { + switch (message.GetParam1()) + { + case ACTION_SELECT_ITEM: + case ACTION_MOUSE_LEFT_CLICK: + { + SetChannelGroup(m_channelGroupsSelector->GetSelectedChannelGroup()); + bReturn = true; + break; + } + } + } + } + break; + } + + case GUI_MSG_REFRESH_LIST: + { + switch (static_cast(message.GetParam1())) + { + case PVREvent::ManagerStarted: + case PVREvent::ClientsInvalidated: + case PVREvent::ChannelGroupsInvalidated: + { + if (InitChannelGroup()) + { + m_channelGroupsSelector->Initialize(this, m_bRadio); + m_channelGroupsSelector->SelectChannelGroup(GetChannelGroup()); + HideProgressDialog(); + Refresh(true); + m_viewControl.SetFocused(); + } + break; + } + default: + break; + } + + if (IsActive()) + { + // Only the active window must set the selected item path which is shared + // between all PVR windows, not the last notified window (observer). + UpdateSelectedItemPath(); + } + bReturn = true; + break; + } + + case GUI_MSG_NOTIFY_ALL: + { + switch (message.GetParam1()) + { + case GUI_MSG_UPDATE_SOURCES: + { + // removable drive connected/disconnected. base class triggers a window + // content refresh, which makes no sense for pvr windows. + bReturn = true; + break; + } + } + break; + } + } + + return bReturn || CGUIMediaWindow::OnMessage(message); +} + +void CGUIWindowPVRBase::SetInvalid() +{ + if (m_refreshTimeout.IsTimePast()) + { + for (const auto& item : *m_vecItems) + item->SetInvalid(); + + CGUIMediaWindow::SetInvalid(); + m_refreshTimeout.Set(MAX_INVALIDATION_FREQUENCY); + } +} + +bool CGUIWindowPVRBase::CanBeActivated() const +{ + // check if there is at least one enabled PVR add-on + if (!CServiceBroker::GetAddonMgr().HasAddons(ADDON::AddonType::PVRDLL)) + { + HELPERS::ShowOKDialogText(CVariant{19296}, CVariant{19272}); // No PVR add-on enabled, You need a tuner, backend software... + return false; + } + + return true; +} + +bool CGUIWindowPVRBase::OpenChannelGroupSelectionDialog() +{ + CGUIDialogSelect* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_SELECT); + if (!dialog) + return false; + + CFileItemList options; + CPVRGUIDirectory::GetChannelGroupsDirectory(m_bRadio, true, options); + + dialog->Reset(); + dialog->SetHeading(CVariant{g_localizeStrings.Get(19146)}); + dialog->SetItems(options); + dialog->SetMultiSelection(false); + if (const std::shared_ptr channelGroup = GetChannelGroup()) + { + dialog->SetSelected(channelGroup->GroupName()); + } + dialog->Open(); + + if (!dialog->IsConfirmed()) + return false; + + const CFileItemPtr item = dialog->GetSelectedFileItem(); + if (!item) + return false; + + SetChannelGroup(CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bRadio)->GetByName(item->m_strTitle)); + + return true; +} + +bool CGUIWindowPVRBase::InitChannelGroup() +{ + if (!CServiceBroker::GetPVRManager().IsStarted()) + return false; + + std::shared_ptr group; + if (m_channelGroupPath.empty()) + { + group = CServiceBroker::GetPVRManager().PlaybackState()->GetActiveChannelGroup(m_bRadio); + } + else + { + group = CServiceBroker::GetPVRManager().ChannelGroups()->Get(m_bRadio)->GetGroupByPath(m_channelGroupPath); + if (group) + CServiceBroker::GetPVRManager().PlaybackState()->SetActiveChannelGroup(group); + else + CLog::LogF(LOGERROR, "Found no {} channel group with path '{}'!", m_bRadio ? "radio" : "TV", + m_channelGroupPath); + + m_channelGroupPath.clear(); + } + + if (group) + { + std::unique_lock lock(m_critSection); + if (m_channelGroup != group) + { + m_viewControl.SetSelectedItem(0); + SetChannelGroup(std::move(group), false); + } + // Path might have changed since last init. Set it always, not just on group change. + m_vecItems->SetPath(GetDirectoryPath()); + return true; + } + return false; +} + +std::shared_ptr CGUIWindowPVRBase::GetChannelGroup() +{ + std::unique_lock lock(m_critSection); + return m_channelGroup; +} + +void CGUIWindowPVRBase::SetChannelGroup(std::shared_ptr &&group, bool bUpdate /* = true */) +{ + if (!group) + return; + + std::shared_ptr updateChannelGroup; + { + std::unique_lock lock(m_critSection); + if (m_channelGroup != group) + { + if (m_channelGroup) + m_channelGroup->Events().Unsubscribe(this); + m_channelGroup = std::move(group); + // we need to register the window to receive changes from the new group + m_channelGroup->Events().Subscribe(this, &CGUIWindowPVRBase::Notify); + if (bUpdate) + updateChannelGroup = m_channelGroup; + } + } + + if (updateChannelGroup) + { + CServiceBroker::GetPVRManager().PlaybackState()->SetActiveChannelGroup(updateChannelGroup); + Update(GetDirectoryPath()); + } +} + +bool CGUIWindowPVRBase::Update(const std::string& strDirectory, bool updateFilterPath /*= true*/) +{ + if (m_bUpdating) + { + // no concurrent updates + return false; + } + + CUpdateGuard guard(m_bUpdating); + + if (!GetChannelGroup()) + { + // no updates before fully initialized + return false; + } + + int iOldCount = m_vecItems->Size(); + int iSelectedItem = m_viewControl.GetSelectedItem(); + const std::string oldPath = m_vecItems->GetPath(); + + bool bReturn = CGUIMediaWindow::Update(strDirectory, updateFilterPath); + + if (bReturn && + iSelectedItem != -1) // something must have been selected + { + int iNewCount = m_vecItems->Size(); + if (iOldCount > iNewCount && // at least one item removed by Update() + oldPath == m_vecItems->GetPath()) // update not due changing into another folder + { + // restore selected item if we just deleted one or more items. + if (iSelectedItem >= iNewCount) + iSelectedItem = iNewCount - 1; + + m_viewControl.SetSelectedItem(iSelectedItem); + } + } + + return bReturn; +} + +void CGUIWindowPVRBase::UpdateButtons() +{ + CGUIMediaWindow::UpdateButtons(); + + const std::shared_ptr channelGroup = GetChannelGroup(); + if (channelGroup) + { + SET_CONTROL_LABEL(CONTROL_BTNCHANNELGROUPS, g_localizeStrings.Get(19141) + ": " + channelGroup->GroupName()); + } + + m_channelGroupsSelector->SelectChannelGroup(channelGroup); +} + +void CGUIWindowPVRBase::ShowProgressDialog(const std::string& strText, int iProgress) +{ + if (!m_progressHandle) + { + CGUIDialogExtendedProgressBar* loadingProgressDialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(WINDOW_DIALOG_EXT_PROGRESS); + if (!loadingProgressDialog) + { + CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_EXT_PROGRESS!"); + return; + } + m_progressHandle = loadingProgressDialog->GetHandle(g_localizeStrings.Get(19235)); // PVR manager is starting up + } + + m_progressHandle->SetPercentage(static_cast(iProgress)); + m_progressHandle->SetText(strText); +} + +void CGUIWindowPVRBase::HideProgressDialog() +{ + if (m_progressHandle) + { + m_progressHandle->MarkFinished(); + m_progressHandle = nullptr; + } +} -- cgit v1.2.3