Merge pull request #26273 from 78andyp/blurayfixes2
[xbmc.git] / xbmc / pvr / windows / GUIWindowPVRGuide.cpp
bloba33c2f3718e33d0a02f514c0078b86f4f5910ba5
1 /*
2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIWindowPVRGuide.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "GUIUserMessages.h"
14 #include "ServiceBroker.h"
15 #include "addons/Skin.h"
16 #include "dialogs/GUIDialogBusy.h"
17 #include "dialogs/GUIDialogContextMenu.h"
18 #include "dialogs/GUIDialogNumeric.h"
19 #include "guilib/GUIMessage.h"
20 #include "guilib/GUIWindowManager.h"
21 #include "guilib/LocalizeStrings.h"
22 #include "input/actions/Action.h"
23 #include "input/actions/ActionIDs.h"
24 #include "messaging/ApplicationMessenger.h"
25 #include "messaging/helpers/DialogHelper.h"
26 #include "pvr/PVRItem.h"
27 #include "pvr/PVRManager.h"
28 #include "pvr/PVRPlaybackState.h"
29 #include "pvr/channels/PVRChannel.h"
30 #include "pvr/channels/PVRChannelGroup.h"
31 #include "pvr/channels/PVRChannelGroupMember.h"
32 #include "pvr/channels/PVRChannelGroupsContainer.h"
33 #include "pvr/channels/PVRChannelsPath.h"
34 #include "pvr/epg/EpgChannelData.h"
35 #include "pvr/epg/EpgContainer.h"
36 #include "pvr/epg/EpgInfoTag.h"
37 #include "pvr/guilib/GUIEPGGridContainer.h"
38 #include "pvr/guilib/PVRGUIActionsChannels.h"
39 #include "pvr/guilib/PVRGUIActionsEPG.h"
40 #include "pvr/guilib/PVRGUIActionsPlayback.h"
41 #include "pvr/guilib/PVRGUIActionsTimers.h"
42 #include "pvr/recordings/PVRRecordings.h"
43 #include "pvr/timers/PVRTimers.h"
44 #include "settings/Settings.h"
45 #include "settings/SettingsComponent.h"
46 #include "video/guilib/VideoPlayActionProcessor.h"
47 #include "view/GUIViewState.h"
49 #include <functional>
50 #include <memory>
51 #include <mutex>
52 #include <utility>
53 #include <vector>
55 using namespace KODI::MESSAGING;
56 using namespace PVR;
57 using namespace std::chrono_literals;
59 CGUIWindowPVRGuideBase::CGUIWindowPVRGuideBase(bool bRadio, int id, const std::string& xmlFile)
60 : CGUIWindowPVRBase(bRadio, id, xmlFile)
62 CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().RegisterChannelNumberInputHandler(this);
65 CGUIWindowPVRGuideBase::~CGUIWindowPVRGuideBase()
67 CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().DeregisterChannelNumberInputHandler(
68 this);
70 m_bRefreshTimelineItems = false;
71 m_bSyncRefreshTimelineItems = false;
72 StopRefreshTimelineItemsThread();
75 CGUIEPGGridContainer* CGUIWindowPVRGuideBase::GetGridControl()
77 return dynamic_cast<CGUIEPGGridContainer*>(GetControl(m_viewControl.GetCurrentControl()));
80 void CGUIWindowPVRGuideBase::InitEpgGridControl()
82 CGUIEPGGridContainer* epgGridContainer = GetGridControl();
83 if (epgGridContainer)
85 CPVRManager& mgr = CServiceBroker::GetPVRManager();
87 const std::shared_ptr<CPVRChannel> channel = mgr.ChannelGroups()->GetByPath(
88 mgr.Get<PVR::GUI::Channels>().GetSelectedChannelPath(m_bRadio));
90 if (channel)
92 m_bChannelSelectionRestored = epgGridContainer->SetChannel(channel);
93 epgGridContainer->JumpToDate(
94 mgr.PlaybackState()->GetPlaybackTime(channel->ClientID(), channel->UniqueID()));
96 else
98 m_bChannelSelectionRestored = false;
99 epgGridContainer->JumpToNow();
102 if (!epgGridContainer->HasData())
103 m_bSyncRefreshTimelineItems = true; // force data update on first window open
106 StartRefreshTimelineItemsThread();
109 void CGUIWindowPVRGuideBase::ClearData()
112 std::unique_lock<CCriticalSection> lock(m_critSection);
113 m_cachedChannelGroup.reset();
116 CGUIWindowPVRBase::ClearData();
119 void CGUIWindowPVRGuideBase::OnInitWindow()
121 if (m_guiState)
122 m_viewControl.SetCurrentView(m_guiState->GetViewAsControl(), false);
124 if (InitChannelGroup()) // no channels -> lazy init
125 InitEpgGridControl();
127 CGUIWindowPVRBase::OnInitWindow();
130 void CGUIWindowPVRGuideBase::OnDeinitWindow(int nextWindowID)
132 StopRefreshTimelineItemsThread();
134 m_bChannelSelectionRestored = false;
136 CGUIDialog* dialog =
137 CServiceBroker::GetGUI()->GetWindowManager().GetDialog(WINDOW_DIALOG_PVR_GUIDE_CONTROLS);
138 if (dialog && dialog->IsDialogRunning())
140 dialog->Close();
143 CGUIWindowPVRBase::OnDeinitWindow(nextWindowID);
146 void CGUIWindowPVRGuideBase::StartRefreshTimelineItemsThread()
148 StopRefreshTimelineItemsThread();
149 m_refreshTimelineItemsThread = std::make_unique<CPVRRefreshTimelineItemsThread>(this);
150 m_refreshTimelineItemsThread->Create();
153 void CGUIWindowPVRGuideBase::StopRefreshTimelineItemsThread()
155 if (m_refreshTimelineItemsThread)
156 m_refreshTimelineItemsThread->Stop();
159 void CGUIWindowPVRGuideBase::NotifyEvent(const PVREvent& event)
161 if (event == PVREvent::Epg || event == PVREvent::EpgContainer ||
162 event == PVREvent::ChannelGroupInvalidated || event == PVREvent::ChannelGroup)
164 m_bRefreshTimelineItems = true;
165 // no base class call => do async refresh
166 return;
168 else if (event == PVREvent::ChannelPlaybackStopped)
170 if (m_guiState && m_guiState->GetSortMethod().sortBy == SortByLastPlayed)
172 // set dirty to force sync refresh
173 m_bSyncRefreshTimelineItems = true;
177 // do sync refresh if dirty
178 CGUIWindowPVRBase::NotifyEvent(event);
181 void CGUIWindowPVRGuideBase::SetInvalid()
183 CGUIEPGGridContainer* epgGridContainer = GetGridControl();
184 if (epgGridContainer)
185 epgGridContainer->SetInvalid();
187 CGUIWindowPVRBase::SetInvalid();
190 void CGUIWindowPVRGuideBase::GetContextButtons(int itemNumber, CContextButtons& buttons)
192 CGUIWindowPVRBase::GetContextButtons(itemNumber, buttons);
194 buttons.Add(CONTEXT_BUTTON_NAVIGATE, 19326); // Navigate...
197 void CGUIWindowPVRGuideBase::UpdateSelectedItemPath()
199 CGUIEPGGridContainer* epgGridContainer = GetGridControl();
200 if (epgGridContainer)
202 const std::shared_ptr<const CPVRChannelGroupMember> groupMember =
203 epgGridContainer->GetSelectedChannelGroupMember();
204 if (groupMember)
205 CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().SetSelectedChannelPath(
206 m_bRadio, groupMember->Path());
210 void CGUIWindowPVRGuideBase::UpdateButtons()
212 CGUIWindowPVRBase::UpdateButtons();
214 SET_CONTROL_LABEL(CONTROL_LABEL_HEADER1, g_localizeStrings.Get(19032));
216 const std::shared_ptr<const CPVRChannelGroup> group = GetChannelGroup();
217 SET_CONTROL_LABEL(CONTROL_LABEL_HEADER2, group ? group->GroupName() : "");
220 bool CGUIWindowPVRGuideBase::Update(const std::string& strDirectory,
221 bool updateFilterPath /* = true */)
223 if (m_bUpdating)
225 // Prevent concurrent updates. Instead, let the timeline items refresh thread pick it up later.
226 m_bRefreshTimelineItems = true;
227 return true;
230 bool bReturn = CGUIWindowPVRBase::Update(strDirectory, updateFilterPath);
232 if (bReturn && !m_bChannelSelectionRestored)
234 CGUIEPGGridContainer* epgGridContainer = GetGridControl();
235 if (epgGridContainer)
236 m_bChannelSelectionRestored = epgGridContainer->SetChannel(
237 CServiceBroker::GetPVRManager().Get<PVR::GUI::Channels>().GetSelectedChannelPath(
238 m_bRadio));
241 return bReturn;
244 bool CGUIWindowPVRGuideBase::GetDirectory(const std::string& strDirectory, CFileItemList& items)
247 std::unique_lock<CCriticalSection> lock(m_critSection);
249 if (m_cachedChannelGroup && *m_cachedChannelGroup != *GetChannelGroup())
251 // channel group change and not very first open of this window. force immediate update.
252 m_bSyncRefreshTimelineItems = true;
256 if (m_bSyncRefreshTimelineItems)
257 m_refreshTimelineItemsThread->DoRefresh(true);
259 const CGUIEPGGridContainer* epgGridContainer = GetGridControl();
260 if (epgGridContainer)
262 const std::unique_ptr<CFileItemList> newTimeline = GetGridControl()->GetCurrentTimeLineItems();
263 items.RemoveDiscCache(GetID());
264 items.Assign(*newTimeline, false);
267 return true;
270 void CGUIWindowPVRGuideBase::FormatAndSort(CFileItemList& items)
272 // Speedup: Nothing to do here as sorting was already done in RefreshTimelineItems
273 return;
276 CFileItemPtr CGUIWindowPVRGuideBase::GetCurrentListItem(int offset /*= 0*/)
278 const CGUIEPGGridContainer* epgGridContainer = GetGridControl();
279 if (epgGridContainer)
280 return epgGridContainer->GetSelectedGridItem(offset);
282 return {};
285 int CGUIWindowPVRGuideBase::GetCurrentListItemIndex(const std::shared_ptr<const CFileItem>& item)
287 return item ? item->GetProperty("TimelineIndex").asInteger32() : -1;
290 bool CGUIWindowPVRGuideBase::ShouldNavigateToGridContainer(int iAction)
292 CGUIEPGGridContainer* epgGridContainer = GetGridControl();
293 CGUIControl* control = GetControl(CONTROL_LSTCHANNELGROUPS);
294 if (epgGridContainer && control && GetFocusedControlID() == control->GetID())
296 int iNavigationId = control->GetAction(iAction).GetNavigation();
297 if (iNavigationId > 0)
299 control = epgGridContainer;
300 while (control !=
301 this) // navigation target could be the grid control or one of its parent controls.
303 if (iNavigationId == control->GetID())
305 // channel group selector control's target for the action is the grid control
306 return true;
308 control = control->GetParentControl();
312 return false;
315 bool CGUIWindowPVRGuideBase::OnAction(const CAction& action)
317 switch (action.GetID())
319 case ACTION_MOVE_UP:
320 case ACTION_MOVE_DOWN:
321 case ACTION_MOVE_LEFT:
322 case ACTION_MOVE_RIGHT:
324 // Check whether grid container is configured as channel group selector's navigation target for the given action.
325 if (ShouldNavigateToGridContainer(action.GetID()))
327 CGUIEPGGridContainer* epgGridContainer = GetGridControl();
328 if (epgGridContainer)
330 CGUIWindowPVRBase::OnAction(action);
332 switch (action.GetID())
334 case ACTION_MOVE_UP:
335 epgGridContainer->GoToBottom();
336 return true;
337 case ACTION_MOVE_DOWN:
338 epgGridContainer->GoToTop();
339 return true;
340 case ACTION_MOVE_LEFT:
341 epgGridContainer->GoToMostRight();
342 return true;
343 case ACTION_MOVE_RIGHT:
344 epgGridContainer->GoToMostLeft();
345 return true;
346 default:
347 break;
351 break;
353 case REMOTE_0:
354 if (GetCurrentDigitCount() == 0)
356 // single zero input is handled by epg grid container
357 break;
359 // fall-thru is intended
360 [[fallthrough]];
361 case REMOTE_1:
362 case REMOTE_2:
363 case REMOTE_3:
364 case REMOTE_4:
365 case REMOTE_5:
366 case REMOTE_6:
367 case REMOTE_7:
368 case REMOTE_8:
369 case REMOTE_9:
370 AppendChannelNumberCharacter(static_cast<char>(action.GetID() - REMOTE_0) + '0');
371 return true;
373 case ACTION_CHANNEL_NUMBER_SEP:
374 AppendChannelNumberCharacter(CPVRChannelNumber::SEPARATOR);
375 return true;
378 return CGUIWindowPVRBase::OnAction(action);
381 void CGUIWindowPVRGuideBase::RefreshView(CGUIMessage& message, bool bInitGridControl)
383 CGUIWindowPVRBase::OnMessage(message);
385 // force grid data update
386 m_bSyncRefreshTimelineItems = true;
388 if (bInitGridControl)
389 InitEpgGridControl();
391 Refresh(true);
394 bool CGUIWindowPVRGuideBase::OnMessage(CGUIMessage& message)
396 bool bReturn = false;
397 switch (message.GetMessage())
399 case GUI_MSG_WINDOW_INIT:
401 const CPVRChannelsPath path(message.GetStringParam(0));
402 if (path.IsValid() && path.IsChannelGroup())
404 // if a path to a channel group is given we must init
405 // that group instead of last played/selected group
406 SetChannelGroupPath(message.GetStringParam(0));
408 break;
411 case GUI_MSG_ITEM_SELECTED:
412 message.SetParam1(GetCurrentListItemIndex(GetCurrentListItem()));
413 bReturn = true;
414 break;
416 case GUI_MSG_CLICKED:
418 if (message.GetSenderId() == m_viewControl.GetCurrentControl())
420 if (message.GetParam1() == ACTION_SELECT_ITEM ||
421 message.GetParam1() == ACTION_MOUSE_LEFT_CLICK)
423 // If direct channel number input is active, select the entered channel.
424 if (CServiceBroker::GetPVRManager()
425 .Get<PVR::GUI::Channels>()
426 .GetChannelNumberInputHandler()
427 .CheckInputAndExecuteAction())
429 bReturn = true;
430 break;
434 const std::shared_ptr<CFileItem> pItem = GetCurrentListItem();
435 if (pItem)
437 switch (message.GetParam1())
439 case ACTION_SELECT_ITEM:
440 case ACTION_MOUSE_LEFT_CLICK:
441 switch (CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(
442 CSettings::SETTING_EPG_SELECTACTION))
444 case EPG_SELECT_ACTION_CONTEXT_MENU:
445 OnPopupMenu(GetCurrentListItemIndex(pItem));
446 bReturn = true;
447 break;
448 case EPG_SELECT_ACTION_SWITCH:
449 CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel(*pItem);
450 bReturn = true;
451 break;
452 case EPG_SELECT_ACTION_PLAY_RECORDING:
454 const std::shared_ptr<CPVRRecording> recording{CPVRItem(pItem).GetRecording()};
455 if (recording)
457 KODI::VIDEO::GUILIB::CVideoPlayActionProcessor proc{
458 std::make_shared<CFileItem>(recording)};
459 proc.ProcessDefaultAction();
460 bReturn = true;
462 break;
464 case EPG_SELECT_ACTION_INFO:
465 CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().ShowEPGInfo(*pItem);
466 bReturn = true;
467 break;
468 case EPG_SELECT_ACTION_RECORD:
469 CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().ToggleTimer(*pItem);
470 bReturn = true;
471 break;
472 case EPG_SELECT_ACTION_SMART_SELECT:
474 const std::shared_ptr<const CPVREpgInfoTag> tag(pItem->GetEPGInfoTag());
475 if (tag)
477 const CDateTime start(tag->StartAsUTC());
478 const CDateTime end(tag->EndAsUTC());
479 const CDateTime now(CDateTime::GetUTCDateTime());
481 if (start <= now && now <= end)
483 // current event
484 CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel(
485 *pItem);
487 else if (now < start)
489 // future event
490 if (CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(tag))
491 CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().EditTimer(*pItem);
492 else
494 bool bCanRecord = true;
495 const std::shared_ptr<const CPVRChannel> channel =
496 CPVRItem(pItem).GetChannel();
497 if (channel)
498 bCanRecord = channel->CanRecord();
500 const int iTextID =
501 bCanRecord
502 ? 19302 // "Do you want to record the selected programme or to switch to the current programme?"
503 : 19344; // "Do you want to set a reminder for the selected programme or to switch to the current programme?"
504 const int iNoButtonID = bCanRecord ? 264 // No => "Record"
505 : 826; // "Set reminder"
507 HELPERS::DialogResponse ret =
508 HELPERS::ShowYesNoDialogText(CVariant{19096}, // "Smart select"
509 CVariant{iTextID}, CVariant{iNoButtonID},
510 CVariant{19165}); // Yes => "Switch"
511 if (ret == HELPERS::DialogResponse::CHOICE_NO)
512 CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().AddTimer(*pItem,
513 false);
514 else if (ret == HELPERS::DialogResponse::CHOICE_YES)
515 CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel(
516 *pItem);
519 else
521 // past event
522 const std::shared_ptr<CPVRRecording> recording{
523 CPVRItem(pItem).GetRecording()};
524 if (recording)
526 KODI::VIDEO::GUILIB::CVideoPlayActionProcessor proc{
527 std::make_shared<CFileItem>(recording)};
528 proc.ProcessDefaultAction();
530 else if (tag->IsPlayable())
531 CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayEpgTag(
532 *pItem);
533 else
534 CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().ShowEPGInfo(*pItem);
536 bReturn = true;
538 break;
541 break;
542 case ACTION_SHOW_INFO:
543 CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().ShowEPGInfo(*pItem);
544 bReturn = true;
545 break;
546 case ACTION_PLAYER_PLAY:
547 CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().SwitchToChannel(*pItem);
548 bReturn = true;
549 break;
550 case ACTION_RECORD:
551 CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().ToggleTimer(*pItem);
552 bReturn = true;
553 break;
554 case ACTION_PVR_SHOW_TIMER_RULE:
555 CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().AddTimerRule(*pItem, true,
556 false);
557 bReturn = true;
558 break;
559 case ACTION_CONTEXT_MENU:
560 case ACTION_MOUSE_RIGHT_CLICK:
561 OnPopupMenu(GetCurrentListItemIndex(pItem));
562 bReturn = true;
563 break;
567 else if (message.GetSenderId() == CONTROL_BTNVIEWASICONS ||
568 message.GetSenderId() == CONTROL_BTNSORTBY ||
569 message.GetSenderId() == CONTROL_BTNSORTASC)
571 RefreshView(message, false);
572 bReturn = true;
574 break;
576 case GUI_MSG_CHANGE_SORT_DIRECTION:
577 case GUI_MSG_CHANGE_SORT_METHOD:
578 case GUI_MSG_CHANGE_VIEW_MODE:
580 RefreshView(message, message.GetMessage() == GUI_MSG_CHANGE_VIEW_MODE);
581 bReturn = true;
582 break;
584 case GUI_MSG_REFRESH_LIST:
586 switch (static_cast<PVREvent>(message.GetParam1()))
588 case PVREvent::ManagerStarted:
589 if (InitChannelGroup())
590 InitEpgGridControl();
591 break;
593 case PVREvent::ChannelGroup:
594 case PVREvent::ChannelGroupInvalidated:
595 case PVREvent::ClientsInvalidated:
596 case PVREvent::ChannelPlaybackStopped:
597 case PVREvent::Epg:
598 case PVREvent::EpgContainer:
599 if (InitChannelGroup())
600 Refresh(true);
601 break;
603 case PVREvent::Timers:
604 case PVREvent::TimersInvalidated:
605 SetInvalid();
606 break;
608 default:
609 break;
611 break;
613 case GUI_MSG_SYSTEM_WAKE:
614 GotoCurrentProgramme();
615 bReturn = true;
616 break;
619 return bReturn || CGUIWindowPVRBase::OnMessage(message);
622 bool CGUIWindowPVRGuideBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
624 if (OnContextButtonNavigate(button))
625 return true;
627 if (itemNumber < 0 || itemNumber >= m_vecItems->Size())
628 return false;
630 return CGUIMediaWindow::OnContextButton(itemNumber, button);
633 namespace
636 template<typename A>
637 class CContextMenuFunctions : public CContextButtons
639 public:
640 explicit CContextMenuFunctions(A* instance) : m_instance(instance) {}
642 void Add(bool (A::*function)(), unsigned int resId)
644 CContextButtons::Add(static_cast<unsigned int>(size()), resId);
645 m_functions.emplace_back(std::bind(function, m_instance));
648 bool Call(int idx)
650 if (idx < 0 || idx >= static_cast<int>(m_functions.size()))
651 return false;
653 return m_functions[idx]();
656 private:
657 A* m_instance = nullptr;
658 std::vector<std::function<bool()>> m_functions;
661 } // unnamed namespace
663 bool CGUIWindowPVRGuideBase::OnContextButtonNavigate(CONTEXT_BUTTON button)
665 bool bReturn = false;
667 if (button == CONTEXT_BUTTON_NAVIGATE)
669 if (g_SkinInfo->HasSkinFile("DialogPVRGuideControls.xml"))
671 // use controls dialog
672 CGUIDialog* dialog =
673 CServiceBroker::GetGUI()->GetWindowManager().GetDialog(WINDOW_DIALOG_PVR_GUIDE_CONTROLS);
674 if (dialog && !dialog->IsDialogRunning())
676 dialog->Open();
679 else
681 // use context menu
682 CContextMenuFunctions<CGUIWindowPVRGuideBase> buttons(this);
683 buttons.Add(&CGUIWindowPVRGuideBase::GotoBegin, 19063); // First programme
684 buttons.Add(&CGUIWindowPVRGuideBase::Go12HoursBack, 19317); // 12 hours back
685 buttons.Add(&CGUIWindowPVRGuideBase::GotoCurrentProgramme, 19070); // Current programme
686 buttons.Add(&CGUIWindowPVRGuideBase::Go12HoursForward, 19318); // 12 hours forward
687 buttons.Add(&CGUIWindowPVRGuideBase::GotoEnd, 19064); // Last programme
688 buttons.Add(&CGUIWindowPVRGuideBase::OpenDateSelectionDialog, 19288); // Date selector
689 buttons.Add(&CGUIWindowPVRGuideBase::GotoFirstChannel, 19322); // First channel
690 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingTV() ||
691 CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRadio() ||
692 CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingEpgTag())
693 buttons.Add(&CGUIWindowPVRGuideBase::GotoPlayingChannel, 19323); // Playing channel
694 buttons.Add(&CGUIWindowPVRGuideBase::GotoLastChannel, 19324); // Last channel
695 buttons.Add(&CGUIWindowPVRBase::ActivatePreviousChannelGroup, 19319); // Previous group
696 buttons.Add(&CGUIWindowPVRBase::ActivateNextChannelGroup, 19320); // Next group
697 buttons.Add(&CGUIWindowPVRBase::OpenChannelGroupSelectionDialog, 19321); // Group selector
699 int buttonIdx = 0;
700 int lastButtonIdx = 2; // initially select "Current programme"
702 // loop until canceled
703 while (buttonIdx >= 0)
705 buttonIdx = CGUIDialogContextMenu::Show(buttons, lastButtonIdx);
706 lastButtonIdx = buttonIdx;
707 buttons.Call(buttonIdx);
710 bReturn = true;
713 return bReturn;
716 bool CGUIWindowPVRGuideBase::RefreshTimelineItems()
718 if (m_bRefreshTimelineItems || m_bSyncRefreshTimelineItems)
720 m_bRefreshTimelineItems = false;
721 m_bSyncRefreshTimelineItems = false;
723 CGUIEPGGridContainer* epgGridContainer = GetGridControl();
724 if (epgGridContainer)
726 const std::shared_ptr<CPVRChannelGroup> group(GetChannelGroup());
727 if (!group)
728 return false;
730 CPVREpgContainer& epgContainer = CServiceBroker::GetPVRManager().EpgContainer();
732 const std::pair<CDateTime, CDateTime> dates = epgContainer.GetFirstAndLastEPGDate();
733 CDateTime startDate = dates.first;
734 CDateTime endDate = dates.second;
735 const CDateTime currentDate = CDateTime::GetUTCDateTime();
737 if (!startDate.IsValid())
738 startDate = currentDate;
740 if (!endDate.IsValid() || endDate < startDate)
741 endDate = startDate;
743 // limit start to past days to display
744 const int iPastDays = epgContainer.GetPastDaysToDisplay();
745 const CDateTime maxPastDate(currentDate - CDateTimeSpan(iPastDays, 0, 0, 0));
746 if (startDate < maxPastDate)
747 startDate = maxPastDate;
749 // limit end to future days to display
750 const int iFutureDays = epgContainer.GetFutureDaysToDisplay();
751 const CDateTime maxFutureDate(currentDate + CDateTimeSpan(iFutureDays, 0, 0, 0));
752 if (endDate > maxFutureDate)
753 endDate = maxFutureDate;
755 std::unique_ptr<CFileItemList> channels(new CFileItemList);
756 const std::vector<std::shared_ptr<CPVRChannelGroupMember>> groupMembers =
757 group->GetMembers(CPVRChannelGroup::Include::ONLY_VISIBLE);
759 for (const auto& groupMember : groupMembers)
761 channels->Add(std::make_shared<CFileItem>(groupMember));
764 if (m_guiState)
765 channels->Sort(m_guiState->GetSortMethod());
767 epgGridContainer->SetTimelineItems(channels, startDate, endDate);
770 std::unique_lock<CCriticalSection> lock(m_critSection);
771 m_cachedChannelGroup = group;
773 return true;
776 return false;
779 bool CGUIWindowPVRGuideBase::GotoBegin()
781 GetGridControl()->GoToBegin();
782 return true;
785 bool CGUIWindowPVRGuideBase::GotoEnd()
787 GetGridControl()->GoToEnd();
788 return true;
791 bool CGUIWindowPVRGuideBase::GotoCurrentProgramme()
793 const CPVRManager& mgr = CServiceBroker::GetPVRManager();
794 std::shared_ptr<CPVRChannel> channel = mgr.PlaybackState()->GetPlayingChannel();
796 if (!channel)
798 const std::shared_ptr<const CPVREpgInfoTag> playingTag =
799 mgr.PlaybackState()->GetPlayingEpgTag();
800 if (playingTag)
801 channel = mgr.ChannelGroups()->GetChannelForEpgTag(playingTag);
804 if (channel)
805 GetGridControl()->GoToDate(
806 mgr.PlaybackState()->GetPlaybackTime(channel->ClientID(), channel->UniqueID()));
807 else
808 GetGridControl()->GoToNow();
810 return true;
813 bool CGUIWindowPVRGuideBase::OpenDateSelectionDialog()
815 bool bReturn = false;
817 KODI::TIME::SystemTime date;
818 CGUIEPGGridContainer* epgGridContainer = GetGridControl();
819 epgGridContainer->GetSelectedDate().GetAsSystemTime(date);
821 if (CGUIDialogNumeric::ShowAndGetDate(date, g_localizeStrings.Get(19288))) /* Go to date */
823 epgGridContainer->GoToDate(CDateTime(date));
824 bReturn = true;
827 return bReturn;
830 bool CGUIWindowPVRGuideBase::Go12HoursBack()
832 return GotoDate(-12);
835 bool CGUIWindowPVRGuideBase::Go12HoursForward()
837 return GotoDate(+12);
840 bool CGUIWindowPVRGuideBase::GotoDate(int deltaHours)
842 CGUIEPGGridContainer* epgGridContainer = GetGridControl();
843 epgGridContainer->GoToDate(epgGridContainer->GetSelectedDate() +
844 CDateTimeSpan(0, deltaHours, 0, 0));
845 return true;
848 bool CGUIWindowPVRGuideBase::GotoFirstChannel()
850 GetGridControl()->GoToFirstChannel();
851 return true;
854 bool CGUIWindowPVRGuideBase::GotoLastChannel()
856 GetGridControl()->GoToLastChannel();
857 return true;
860 bool CGUIWindowPVRGuideBase::GotoPlayingChannel()
862 const CPVRManager& mgr = CServiceBroker::GetPVRManager();
863 std::shared_ptr<CPVRChannel> channel = mgr.PlaybackState()->GetPlayingChannel();
865 if (!channel)
867 const std::shared_ptr<const CPVREpgInfoTag> playingTag =
868 mgr.PlaybackState()->GetPlayingEpgTag();
869 if (playingTag)
870 channel = mgr.ChannelGroups()->GetChannelForEpgTag(playingTag);
873 if (channel)
875 GetGridControl()->SetChannel(channel);
876 return true;
878 return false;
881 void CGUIWindowPVRGuideBase::OnInputDone()
883 const CPVRChannelNumber channelNumber = GetChannelNumber();
884 if (channelNumber.IsValid())
886 GetGridControl()->SetChannel(channelNumber);
890 void CGUIWindowPVRGuideBase::GetChannelNumbers(std::vector<std::string>& channelNumbers)
892 const std::shared_ptr<const CPVRChannelGroup> group = GetChannelGroup();
893 if (group)
894 group->GetChannelNumbers(channelNumbers);
897 CPVRRefreshTimelineItemsThread::CPVRRefreshTimelineItemsThread(CGUIWindowPVRGuideBase* pGuideWindow)
898 : CThread("epg-grid-refresh-timeline-items"),
899 m_pGuideWindow(pGuideWindow),
900 m_ready(true),
901 m_done(false)
905 CPVRRefreshTimelineItemsThread::~CPVRRefreshTimelineItemsThread()
907 // Note: CThread dtor will also call StopThread(true), but if thread worker function exits that
908 // late, it might access member variables of this which are already destroyed. Thus, stop
909 // the thread worker here and synchronously, while all members of this are still alive.
910 StopThread(true);
913 void CPVRRefreshTimelineItemsThread::Stop()
915 StopThread(false);
916 m_ready.Set(); // wake up the worker thread to let it exit
919 void CPVRRefreshTimelineItemsThread::DoRefresh(bool bWait)
921 m_ready.Set(); // wake up the worker thread
923 if (bWait)
925 m_done.Reset();
926 CGUIDialogBusy::WaitOnEvent(m_done, 100, false);
930 void CPVRRefreshTimelineItemsThread::Process()
932 static const int BOOSTED_SLEEPS_THRESHOLD = 4;
934 int iLastEpgItemsCount = 0;
935 int iUpdatesWithoutChange = 0;
937 while (!m_bStop)
939 m_done.Reset();
941 if (m_pGuideWindow->RefreshTimelineItems() && !m_bStop)
943 CGUIMessage m(GUI_MSG_REFRESH_LIST, m_pGuideWindow->GetID(), 0,
944 static_cast<int>(PVREvent::Epg));
945 CServiceBroker::GetAppMessenger()->SendGUIMessage(m);
948 if (m_bStop)
949 break;
951 m_done.Set();
953 // in order to fill the guide window asap, use a short update interval until we the
954 // same amount of epg events for BOOSTED_SLEEPS_THRESHOLD + 1 times in a row .
955 if (iUpdatesWithoutChange < BOOSTED_SLEEPS_THRESHOLD)
957 int iCurrentEpgItemsCount = m_pGuideWindow->CurrentDirectory().Size();
959 if (iCurrentEpgItemsCount == iLastEpgItemsCount)
960 iUpdatesWithoutChange++;
961 else
962 iUpdatesWithoutChange = 0; // reset
964 iLastEpgItemsCount = iCurrentEpgItemsCount;
966 m_ready.Wait(1000ms); // boosted update cycle
968 else
970 m_ready.Wait(5000ms); // normal update cycle
973 m_ready.Reset();
976 m_ready.Reset();
977 m_done.Set();
980 std::string CGUIWindowPVRTVGuide::GetRootPath() const
982 return "pvr://guide/tv/";
985 std::string CGUIWindowPVRRadioGuide::GetRootPath() const
987 return "pvr://guide/radio/";