diff options
Diffstat (limited to 'xbmc/pvr/windows/GUIWindowPVRGuide.cpp')
-rw-r--r-- | xbmc/pvr/windows/GUIWindowPVRGuide.cpp | 973 |
1 files changed, 973 insertions, 0 deletions
diff --git a/xbmc/pvr/windows/GUIWindowPVRGuide.cpp b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp new file mode 100644 index 0000000..f4c247f --- /dev/null +++ b/xbmc/pvr/windows/GUIWindowPVRGuide.cpp @@ -0,0 +1,973 @@ +/* + * 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 "GUIWindowPVRGuide.h" + +#include "FileItem.h" +#include "GUIUserMessages.h" +#include "ServiceBroker.h" +#include "addons/Skin.h" +#include "dialogs/GUIDialogBusy.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "dialogs/GUIDialogNumeric.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/DialogHelper.h" +#include "pvr/PVRItem.h" +#include "pvr/PVRManager.h" +#include "pvr/PVRPlaybackState.h" +#include "pvr/channels/PVRChannel.h" +#include "pvr/channels/PVRChannelGroup.h" +#include "pvr/channels/PVRChannelGroupMember.h" +#include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/channels/PVRChannelsPath.h" +#include "pvr/epg/EpgChannelData.h" +#include "pvr/epg/EpgContainer.h" +#include "pvr/epg/EpgInfoTag.h" +#include "pvr/guilib/GUIEPGGridContainer.h" +#include "pvr/guilib/PVRGUIActionsChannels.h" +#include "pvr/guilib/PVRGUIActionsEPG.h" +#include "pvr/guilib/PVRGUIActionsPlayback.h" +#include "pvr/guilib/PVRGUIActionsTimers.h" +#include "pvr/recordings/PVRRecordings.h" +#include "pvr/timers/PVRTimers.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "view/GUIViewState.h" + +#include <functional> +#include <memory> +#include <mutex> +#include <utility> +#include <vector> + +using namespace KODI::MESSAGING; +using namespace PVR; +using namespace std::chrono_literals; + +CGUIWindowPVRGuideBase::CGUIWindowPVRGuideBase(bool bRadio, int id, const std::string& xmlFile) + : CGUIWindowPVRBase(bRadio, id, xmlFile) +{ + CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().RegisterChannelNumberInputHandler(this); +} + +CGUIWindowPVRGuideBase::~CGUIWindowPVRGuideBase() +{ + CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().DeregisterChannelNumberInputHandler( + this); + + m_bRefreshTimelineItems = false; + m_bSyncRefreshTimelineItems = false; + StopRefreshTimelineItemsThread(); +} + +CGUIEPGGridContainer* CGUIWindowPVRGuideBase::GetGridControl() +{ + return dynamic_cast<CGUIEPGGridContainer*>(GetControl(m_viewControl.GetCurrentControl())); +} + +void CGUIWindowPVRGuideBase::InitEpgGridControl() +{ + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) + { + CPVRManager& mgr = CServiceBroker::GetPVRManager(); + + const std::shared_ptr<CPVRChannel> channel = mgr.ChannelGroups()->GetByPath( + mgr.Get<PVR::GUI::Channels>().GetSelectedChannelPath(m_bRadio)); + + if (channel) + { + m_bChannelSelectionRestored = epgGridContainer->SetChannel(channel); + epgGridContainer->JumpToDate( + mgr.PlaybackState()->GetPlaybackTime(channel->ClientID(), channel->UniqueID())); + } + else + { + m_bChannelSelectionRestored = false; + epgGridContainer->JumpToNow(); + } + + if (!epgGridContainer->HasData()) + m_bSyncRefreshTimelineItems = true; // force data update on first window open + } + + StartRefreshTimelineItemsThread(); +} + +void CGUIWindowPVRGuideBase::ClearData() +{ + { + std::unique_lock<CCriticalSection> lock(m_critSection); + m_cachedChannelGroup.reset(); + } + + CGUIWindowPVRBase::ClearData(); +} + +void CGUIWindowPVRGuideBase::OnInitWindow() +{ + if (m_guiState) + m_viewControl.SetCurrentView(m_guiState->GetViewAsControl(), false); + + if (InitChannelGroup()) // no channels -> lazy init + InitEpgGridControl(); + + CGUIWindowPVRBase::OnInitWindow(); +} + +void CGUIWindowPVRGuideBase::OnDeinitWindow(int nextWindowID) +{ + StopRefreshTimelineItemsThread(); + + m_bChannelSelectionRestored = false; + + CGUIDialog* dialog = + CServiceBroker::GetGUI()->GetWindowManager().GetDialog(WINDOW_DIALOG_PVR_GUIDE_CONTROLS); + if (dialog && dialog->IsDialogRunning()) + { + dialog->Close(); + } + + CGUIWindowPVRBase::OnDeinitWindow(nextWindowID); +} + +void CGUIWindowPVRGuideBase::StartRefreshTimelineItemsThread() +{ + StopRefreshTimelineItemsThread(); + m_refreshTimelineItemsThread.reset(new CPVRRefreshTimelineItemsThread(this)); + m_refreshTimelineItemsThread->Create(); +} + +void CGUIWindowPVRGuideBase::StopRefreshTimelineItemsThread() +{ + if (m_refreshTimelineItemsThread) + m_refreshTimelineItemsThread->Stop(); +} + +void CGUIWindowPVRGuideBase::NotifyEvent(const PVREvent& event) +{ + if (event == PVREvent::Epg || event == PVREvent::EpgContainer || + event == PVREvent::ChannelGroupInvalidated || event == PVREvent::ChannelGroup) + { + m_bRefreshTimelineItems = true; + // no base class call => do async refresh + return; + } + else if (event == PVREvent::ChannelPlaybackStopped) + { + if (m_guiState && m_guiState->GetSortMethod().sortBy == SortByLastPlayed) + { + // set dirty to force sync refresh + m_bSyncRefreshTimelineItems = true; + } + } + + // do sync refresh if dirty + CGUIWindowPVRBase::NotifyEvent(event); +} + +void CGUIWindowPVRGuideBase::SetInvalid() +{ + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) + epgGridContainer->SetInvalid(); + + CGUIWindowPVRBase::SetInvalid(); +} + +void CGUIWindowPVRGuideBase::GetContextButtons(int itemNumber, CContextButtons& buttons) +{ + CGUIWindowPVRBase::GetContextButtons(itemNumber, buttons); + + buttons.Add(CONTEXT_BUTTON_NAVIGATE, 19326); // Navigate... +} + +void CGUIWindowPVRGuideBase::UpdateSelectedItemPath() +{ + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) + { + const std::shared_ptr<CPVRChannelGroupMember> groupMember = + epgGridContainer->GetSelectedChannelGroupMember(); + if (groupMember) + CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().SetSelectedChannelPath( + m_bRadio, groupMember->Path()); + } +} + +void CGUIWindowPVRGuideBase::UpdateButtons() +{ + CGUIWindowPVRBase::UpdateButtons(); + + SET_CONTROL_LABEL(CONTROL_LABEL_HEADER1, g_localizeStrings.Get(19032)); + + const std::shared_ptr<CPVRChannelGroup> group = GetChannelGroup(); + SET_CONTROL_LABEL(CONTROL_LABEL_HEADER2, group ? group->GroupName() : ""); +} + +bool CGUIWindowPVRGuideBase::Update(const std::string& strDirectory, + bool updateFilterPath /* = true */) +{ + if (m_bUpdating) + { + // Prevent concurrent updates. Instead, let the timeline items refresh thread pick it up later. + m_bRefreshTimelineItems = true; + return true; + } + + bool bReturn = CGUIWindowPVRBase::Update(strDirectory, updateFilterPath); + + if (bReturn && !m_bChannelSelectionRestored) + { + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) + m_bChannelSelectionRestored = epgGridContainer->SetChannel( + CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().GetSelectedChannelPath( + m_bRadio)); + } + + return bReturn; +} + +bool CGUIWindowPVRGuideBase::GetDirectory(const std::string& strDirectory, CFileItemList& items) +{ + { + std::unique_lock<CCriticalSection> lock(m_critSection); + + if (m_cachedChannelGroup && *m_cachedChannelGroup != *GetChannelGroup()) + { + // channel group change and not very first open of this window. force immediate update. + m_bSyncRefreshTimelineItems = true; + } + } + + if (m_bSyncRefreshTimelineItems) + m_refreshTimelineItemsThread->DoRefresh(true); + + const CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) + { + const std::unique_ptr<CFileItemList> newTimeline = GetGridControl()->GetCurrentTimeLineItems(); + items.RemoveDiscCache(GetID()); + items.Assign(*newTimeline, false); + } + + return true; +} + +void CGUIWindowPVRGuideBase::FormatAndSort(CFileItemList& items) +{ + // Speedup: Nothing to do here as sorting was already done in RefreshTimelineItems + return; +} + +CFileItemPtr CGUIWindowPVRGuideBase::GetCurrentListItem(int offset /*= 0*/) +{ + const CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) + return epgGridContainer->GetSelectedGridItem(offset); + + return {}; +} + +int CGUIWindowPVRGuideBase::GetCurrentListItemIndex(const std::shared_ptr<CFileItem>& item) +{ + return item ? item->GetProperty("TimelineIndex").asInteger() : -1; +} + +bool CGUIWindowPVRGuideBase::ShouldNavigateToGridContainer(int iAction) +{ + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + CGUIControl* control = GetControl(CONTROL_LSTCHANNELGROUPS); + if (epgGridContainer && control && GetFocusedControlID() == control->GetID()) + { + int iNavigationId = control->GetAction(iAction).GetNavigation(); + if (iNavigationId > 0) + { + control = epgGridContainer; + while (control != + this) // navigation target could be the grid control or one of its parent controls. + { + if (iNavigationId == control->GetID()) + { + // channel group selector control's target for the action is the grid control + return true; + } + control = control->GetParentControl(); + } + } + } + return false; +} + +bool CGUIWindowPVRGuideBase::OnAction(const CAction& action) +{ + switch (action.GetID()) + { + case ACTION_MOVE_UP: + case ACTION_MOVE_DOWN: + case ACTION_MOVE_LEFT: + case ACTION_MOVE_RIGHT: + { + // Check whether grid container is configured as channel group selector's navigation target for the given action. + if (ShouldNavigateToGridContainer(action.GetID())) + { + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) + { + CGUIWindowPVRBase::OnAction(action); + + switch (action.GetID()) + { + case ACTION_MOVE_UP: + epgGridContainer->GoToBottom(); + return true; + case ACTION_MOVE_DOWN: + epgGridContainer->GoToTop(); + return true; + case ACTION_MOVE_LEFT: + epgGridContainer->GoToMostRight(); + return true; + case ACTION_MOVE_RIGHT: + epgGridContainer->GoToMostLeft(); + return true; + default: + break; + } + } + } + break; + } + case REMOTE_0: + if (GetCurrentDigitCount() == 0) + { + // single zero input is handled by epg grid container + break; + } + // fall-thru is intended + [[fallthrough]]; + 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; + } + + return CGUIWindowPVRBase::OnAction(action); +} + +void CGUIWindowPVRGuideBase::RefreshView(CGUIMessage& message, bool bInitGridControl) +{ + CGUIWindowPVRBase::OnMessage(message); + + // force grid data update + m_bSyncRefreshTimelineItems = true; + + if (bInitGridControl) + InitEpgGridControl(); + + Refresh(true); +} + +bool CGUIWindowPVRGuideBase::OnMessage(CGUIMessage& message) +{ + bool bReturn = false; + switch (message.GetMessage()) + { + case GUI_MSG_WINDOW_INIT: + { + const CPVRChannelsPath path(message.GetStringParam(0)); + if (path.IsValid() && path.IsChannelGroup()) + { + // if a path to a channel group is given we must init + // that group instead of last played/selected group + m_channelGroupPath = message.GetStringParam(0); + } + break; + } + + case GUI_MSG_ITEM_SELECTED: + message.SetParam1(GetCurrentListItemIndex(GetCurrentListItem())); + bReturn = true; + break; + + case GUI_MSG_CLICKED: + { + if (message.GetSenderId() == m_viewControl.GetCurrentControl()) + { + if (message.GetParam1() == ACTION_SELECT_ITEM || + message.GetParam1() == ACTION_MOUSE_LEFT_CLICK) + { + // If direct channel number input is active, select the entered channel. + if (CServiceBroker::GetPVRManager() + .Get<PVR::GUI::Channels>() + .GetChannelNumberInputHandler() + .CheckInputAndExecuteAction()) + { + bReturn = true; + break; + } + } + + const std::shared_ptr<CFileItem> pItem = GetCurrentListItem(); + if (pItem) + { + switch (message.GetParam1()) + { + case ACTION_SELECT_ITEM: + case ACTION_MOUSE_LEFT_CLICK: + switch (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt( + CSettings::SETTING_EPG_SELECTACTION)) + { + case EPG_SELECT_ACTION_CONTEXT_MENU: + OnPopupMenu(GetCurrentListItemIndex(pItem)); + bReturn = true; + break; + case EPG_SELECT_ACTION_SWITCH: + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel(*pItem, + true); + bReturn = true; + break; + case EPG_SELECT_ACTION_PLAY_RECORDING: + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording(*pItem, + true); + bReturn = true; + break; + case EPG_SELECT_ACTION_INFO: + CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().ShowEPGInfo(*pItem); + bReturn = true; + break; + case EPG_SELECT_ACTION_RECORD: + CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().ToggleTimer(*pItem); + bReturn = true; + break; + case EPG_SELECT_ACTION_SMART_SELECT: + { + const std::shared_ptr<CPVREpgInfoTag> tag(pItem->GetEPGInfoTag()); + if (tag) + { + const CDateTime start(tag->StartAsUTC()); + const CDateTime end(tag->EndAsUTC()); + const CDateTime now(CDateTime::GetUTCDateTime()); + + if (start <= now && now <= end) + { + // current event + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel( + *pItem, true); + } + else if (now < start) + { + // future event + if (CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(tag)) + CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().EditTimer(*pItem); + else + { + bool bCanRecord = true; + const std::shared_ptr<CPVRChannel> channel = CPVRItem(pItem).GetChannel(); + if (channel) + bCanRecord = channel->CanRecord(); + + const int iTextID = + bCanRecord + ? 19302 // "Do you want to record the selected programme or to switch to the current programme?" + : 19344; // "Do you want to set a reminder for the selected programme or to switch to the current programme?" + const int iNoButtonID = bCanRecord ? 264 // No => "Record" + : 826; // "Set reminder" + + HELPERS::DialogResponse ret = + HELPERS::ShowYesNoDialogText(CVariant{19096}, // "Smart select" + CVariant{iTextID}, CVariant{iNoButtonID}, + CVariant{19165}); // Yes => "Switch" + if (ret == HELPERS::DialogResponse::CHOICE_NO) + CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().AddTimer(*pItem, + false); + else if (ret == HELPERS::DialogResponse::CHOICE_YES) + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel( + *pItem, true); + } + } + else + { + // past event + if (CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(tag)) + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayRecording( + *pItem, true); + else if (tag->IsPlayable()) + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayEpgTag( + *pItem); + else + CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().ShowEPGInfo(*pItem); + } + bReturn = true; + } + break; + } + } + break; + case ACTION_SHOW_INFO: + CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().ShowEPGInfo(*pItem); + bReturn = true; + break; + case ACTION_PLAYER_PLAY: + CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel(*pItem, + true); + bReturn = true; + break; + case ACTION_RECORD: + CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().ToggleTimer(*pItem); + bReturn = true; + break; + case ACTION_PVR_SHOW_TIMER_RULE: + CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().AddTimerRule(*pItem, true, + false); + bReturn = true; + break; + case ACTION_CONTEXT_MENU: + case ACTION_MOUSE_RIGHT_CLICK: + OnPopupMenu(GetCurrentListItemIndex(pItem)); + bReturn = true; + break; + } + } + } + else if (message.GetSenderId() == CONTROL_BTNVIEWASICONS || + message.GetSenderId() == CONTROL_BTNSORTBY || + message.GetSenderId() == CONTROL_BTNSORTASC) + { + RefreshView(message, false); + bReturn = true; + } + break; + } + case GUI_MSG_CHANGE_SORT_DIRECTION: + case GUI_MSG_CHANGE_SORT_METHOD: + case GUI_MSG_CHANGE_VIEW_MODE: + { + RefreshView(message, message.GetMessage() == GUI_MSG_CHANGE_VIEW_MODE); + bReturn = true; + break; + } + case GUI_MSG_REFRESH_LIST: + { + switch (static_cast<PVREvent>(message.GetParam1())) + { + case PVREvent::ManagerStarted: + if (InitChannelGroup()) + InitEpgGridControl(); + break; + + case PVREvent::ChannelGroup: + case PVREvent::ChannelGroupInvalidated: + case PVREvent::ClientsInvalidated: + case PVREvent::ChannelPlaybackStopped: + case PVREvent::Epg: + case PVREvent::EpgContainer: + if (InitChannelGroup()) + Refresh(true); + break; + + case PVREvent::Timers: + case PVREvent::TimersInvalidated: + SetInvalid(); + break; + + default: + break; + } + break; + } + case GUI_MSG_SYSTEM_WAKE: + GotoCurrentProgramme(); + bReturn = true; + break; + } + + return bReturn || CGUIWindowPVRBase::OnMessage(message); +} + +bool CGUIWindowPVRGuideBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button) +{ + if (OnContextButtonNavigate(button)) + return true; + + if (itemNumber < 0 || itemNumber >= m_vecItems->Size()) + return false; + + return CGUIMediaWindow::OnContextButton(itemNumber, button); +} + +namespace +{ + +template<typename A> +class CContextMenuFunctions : public CContextButtons +{ +public: + explicit CContextMenuFunctions(A* instance) : m_instance(instance) {} + + void Add(bool (A::*function)(), unsigned int resId) + { + CContextButtons::Add(size(), resId); + m_functions.emplace_back(std::bind(function, m_instance)); + } + + bool Call(int idx) + { + if (idx < 0 || idx >= static_cast<int>(m_functions.size())) + return false; + + return m_functions[idx](); + } + +private: + A* m_instance = nullptr; + std::vector<std::function<bool()>> m_functions; +}; + +} // unnamed namespace + +bool CGUIWindowPVRGuideBase::OnContextButtonNavigate(CONTEXT_BUTTON button) +{ + bool bReturn = false; + + if (button == CONTEXT_BUTTON_NAVIGATE) + { + if (g_SkinInfo->HasSkinFile("DialogPVRGuideControls.xml")) + { + // use controls dialog + CGUIDialog* dialog = + CServiceBroker::GetGUI()->GetWindowManager().GetDialog(WINDOW_DIALOG_PVR_GUIDE_CONTROLS); + if (dialog && !dialog->IsDialogRunning()) + { + dialog->Open(); + } + } + else + { + // use context menu + CContextMenuFunctions<CGUIWindowPVRGuideBase> buttons(this); + buttons.Add(&CGUIWindowPVRGuideBase::GotoBegin, 19063); // First programme + buttons.Add(&CGUIWindowPVRGuideBase::Go12HoursBack, 19317); // 12 hours back + buttons.Add(&CGUIWindowPVRGuideBase::GotoCurrentProgramme, 19070); // Current programme + buttons.Add(&CGUIWindowPVRGuideBase::Go12HoursForward, 19318); // 12 hours forward + buttons.Add(&CGUIWindowPVRGuideBase::GotoEnd, 19064); // Last programme + buttons.Add(&CGUIWindowPVRGuideBase::OpenDateSelectionDialog, 19288); // Date selector + buttons.Add(&CGUIWindowPVRGuideBase::GotoFirstChannel, 19322); // First channel + if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingTV() || + CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRadio() || + CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingEpgTag()) + buttons.Add(&CGUIWindowPVRGuideBase::GotoPlayingChannel, 19323); // Playing channel + buttons.Add(&CGUIWindowPVRGuideBase::GotoLastChannel, 19324); // Last channel + buttons.Add(&CGUIWindowPVRBase::ActivatePreviousChannelGroup, 19319); // Previous group + buttons.Add(&CGUIWindowPVRBase::ActivateNextChannelGroup, 19320); // Next group + buttons.Add(&CGUIWindowPVRBase::OpenChannelGroupSelectionDialog, 19321); // Group selector + + int buttonIdx = 0; + int lastButtonIdx = 2; // initially select "Current programme" + + // loop until canceled + while (buttonIdx >= 0) + { + buttonIdx = CGUIDialogContextMenu::Show(buttons, lastButtonIdx); + lastButtonIdx = buttonIdx; + buttons.Call(buttonIdx); + } + } + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRGuideBase::RefreshTimelineItems() +{ + if (m_bRefreshTimelineItems || m_bSyncRefreshTimelineItems) + { + m_bRefreshTimelineItems = false; + m_bSyncRefreshTimelineItems = false; + + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + if (epgGridContainer) + { + const std::shared_ptr<CPVRChannelGroup> group(GetChannelGroup()); + if (!group) + return false; + + CPVREpgContainer& epgContainer = CServiceBroker::GetPVRManager().EpgContainer(); + + const std::pair<CDateTime, CDateTime> dates = epgContainer.GetFirstAndLastEPGDate(); + CDateTime startDate = dates.first; + CDateTime endDate = dates.second; + const CDateTime currentDate = CDateTime::GetUTCDateTime(); + + if (!startDate.IsValid()) + startDate = currentDate; + + if (!endDate.IsValid() || endDate < startDate) + endDate = startDate; + + // limit start to past days to display + const int iPastDays = epgContainer.GetPastDaysToDisplay(); + const CDateTime maxPastDate(currentDate - CDateTimeSpan(iPastDays, 0, 0, 0)); + if (startDate < maxPastDate) + startDate = maxPastDate; + + // limit end to future days to display + const int iFutureDays = epgContainer.GetFutureDaysToDisplay(); + const CDateTime maxFutureDate(currentDate + CDateTimeSpan(iFutureDays, 0, 0, 0)); + if (endDate > maxFutureDate) + endDate = maxFutureDate; + + std::unique_ptr<CFileItemList> channels(new CFileItemList); + const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers = + group->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE); + + for (const auto& groupMember : groupMembers) + { + channels->Add(std::make_shared<CFileItem>(groupMember)); + } + + if (m_guiState) + channels->Sort(m_guiState->GetSortMethod()); + + epgGridContainer->SetTimelineItems(channels, startDate, endDate); + + { + std::unique_lock<CCriticalSection> lock(m_critSection); + m_cachedChannelGroup = group; + } + return true; + } + } + return false; +} + +bool CGUIWindowPVRGuideBase::GotoBegin() +{ + GetGridControl()->GoToBegin(); + return true; +} + +bool CGUIWindowPVRGuideBase::GotoEnd() +{ + GetGridControl()->GoToEnd(); + return true; +} + +bool CGUIWindowPVRGuideBase::GotoCurrentProgramme() +{ + const CPVRManager& mgr = CServiceBroker::GetPVRManager(); + std::shared_ptr<CPVRChannel> channel = mgr.PlaybackState()->GetPlayingChannel(); + + if (!channel) + { + const std::shared_ptr<CPVREpgInfoTag> playingTag = mgr.PlaybackState()->GetPlayingEpgTag(); + if (playingTag) + channel = mgr.ChannelGroups()->GetChannelForEpgTag(playingTag); + } + + if (channel) + GetGridControl()->GoToDate( + mgr.PlaybackState()->GetPlaybackTime(channel->ClientID(), channel->UniqueID())); + else + GetGridControl()->GoToNow(); + + return true; +} + +bool CGUIWindowPVRGuideBase::OpenDateSelectionDialog() +{ + bool bReturn = false; + + KODI::TIME::SystemTime date; + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + epgGridContainer->GetSelectedDate().GetAsSystemTime(date); + + if (CGUIDialogNumeric::ShowAndGetDate(date, g_localizeStrings.Get(19288))) /* Go to date */ + { + epgGridContainer->GoToDate(CDateTime(date)); + bReturn = true; + } + + return bReturn; +} + +bool CGUIWindowPVRGuideBase::Go12HoursBack() +{ + return GotoDate(-12); +} + +bool CGUIWindowPVRGuideBase::Go12HoursForward() +{ + return GotoDate(+12); +} + +bool CGUIWindowPVRGuideBase::GotoDate(int deltaHours) +{ + CGUIEPGGridContainer* epgGridContainer = GetGridControl(); + epgGridContainer->GoToDate(epgGridContainer->GetSelectedDate() + + CDateTimeSpan(0, deltaHours, 0, 0)); + return true; +} + +bool CGUIWindowPVRGuideBase::GotoFirstChannel() +{ + GetGridControl()->GoToFirstChannel(); + return true; +} + +bool CGUIWindowPVRGuideBase::GotoLastChannel() +{ + GetGridControl()->GoToLastChannel(); + return true; +} + +bool CGUIWindowPVRGuideBase::GotoPlayingChannel() +{ + const CPVRManager& mgr = CServiceBroker::GetPVRManager(); + std::shared_ptr<CPVRChannel> channel = mgr.PlaybackState()->GetPlayingChannel(); + + if (!channel) + { + const std::shared_ptr<CPVREpgInfoTag> playingTag = mgr.PlaybackState()->GetPlayingEpgTag(); + if (playingTag) + channel = mgr.ChannelGroups()->GetChannelForEpgTag(playingTag); + } + + if (channel) + { + GetGridControl()->SetChannel(channel); + return true; + } + return false; +} + +void CGUIWindowPVRGuideBase::OnInputDone() +{ + const CPVRChannelNumber channelNumber = GetChannelNumber(); + if (channelNumber.IsValid()) + { + GetGridControl()->SetChannel(channelNumber); + } +} + +void CGUIWindowPVRGuideBase::GetChannelNumbers(std::vector<std::string>& channelNumbers) +{ + const std::shared_ptr<CPVRChannelGroup> group = GetChannelGroup(); + if (group) + group->GetChannelNumbers(channelNumbers); +} + +CPVRRefreshTimelineItemsThread::CPVRRefreshTimelineItemsThread(CGUIWindowPVRGuideBase* pGuideWindow) + : CThread("epg-grid-refresh-timeline-items"), + m_pGuideWindow(pGuideWindow), + m_ready(true), + m_done(false) +{ +} + +CPVRRefreshTimelineItemsThread::~CPVRRefreshTimelineItemsThread() +{ + // Note: CThread dtor will also call StopThread(true), but if thread worker function exits that + // late, it might access member variables of this which are already destroyed. Thus, stop + // the thread worker here and synchronously, while all members of this are still alive. + StopThread(true); +} + +void CPVRRefreshTimelineItemsThread::Stop() +{ + StopThread(false); + m_ready.Set(); // wake up the worker thread to let it exit +} + +void CPVRRefreshTimelineItemsThread::DoRefresh(bool bWait) +{ + m_ready.Set(); // wake up the worker thread + + if (bWait) + { + m_done.Reset(); + CGUIDialogBusy::WaitOnEvent(m_done, 100, false); + } +} + +void CPVRRefreshTimelineItemsThread::Process() +{ + static const int BOOSTED_SLEEPS_THRESHOLD = 4; + + int iLastEpgItemsCount = 0; + int iUpdatesWithoutChange = 0; + + while (!m_bStop) + { + m_done.Reset(); + + if (m_pGuideWindow->RefreshTimelineItems() && !m_bStop) + { + CGUIMessage m(GUI_MSG_REFRESH_LIST, m_pGuideWindow->GetID(), 0, + static_cast<int>(PVREvent::Epg)); + CServiceBroker::GetAppMessenger()->SendGUIMessage(m); + } + + if (m_bStop) + break; + + m_done.Set(); + + // in order to fill the guide window asap, use a short update interval until we the + // same amount of epg events for BOOSTED_SLEEPS_THRESHOLD + 1 times in a row . + if (iUpdatesWithoutChange < BOOSTED_SLEEPS_THRESHOLD) + { + int iCurrentEpgItemsCount = m_pGuideWindow->CurrentDirectory().Size(); + + if (iCurrentEpgItemsCount == iLastEpgItemsCount) + iUpdatesWithoutChange++; + else + iUpdatesWithoutChange = 0; // reset + + iLastEpgItemsCount = iCurrentEpgItemsCount; + + m_ready.Wait(1000ms); // boosted update cycle + } + else + { + m_ready.Wait(5000ms); // normal update cycle + } + + m_ready.Reset(); + } + + m_ready.Reset(); + m_done.Set(); +} + +std::string CGUIWindowPVRTVGuide::GetRootPath() const +{ + return "pvr://guide/tv/"; +} + +std::string CGUIWindowPVRRadioGuide::GetRootPath() const +{ + return "pvr://guide/radio/"; +} |