2 * Copyright (C) 2012-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "GUIWindowPVRBase.h"
23 #include "Application.h"
24 #include "ApplicationMessenger.h"
25 #include "dialogs/GUIDialogNumeric.h"
26 #include "dialogs/GUIDialogKaiToast.h"
27 #include "dialogs/GUIDialogOK.h"
28 #include "dialogs/GUIDialogYesNo.h"
29 #include "filesystem/StackDirectory.h"
30 #include "guilib/Key.h"
31 #include "guilib/GUIMessage.h"
32 #include "guilib/GUIWindowManager.h"
33 #include "dialogs/GUIDialogKaiToast.h"
34 #include "dialogs/GUIDialogSelect.h"
35 #include "pvr/PVRManager.h"
36 #include "pvr/addons/PVRClients.h"
37 #include "pvr/dialogs/GUIDialogPVRGuideInfo.h"
38 #include "pvr/dialogs/GUIDialogPVRRecordingInfo.h"
39 #include "pvr/timers/PVRTimers.h"
41 #include "epg/GUIEPGGridContainer.h"
42 #include "settings/MediaSettings.h"
43 #include "settings/Settings.h"
44 #include "threads/SingleLock.h"
45 #include "utils/StringUtils.h"
46 #include "utils/Observer.h"
52 CGUIWindowPVRBase::CGUIWindowPVRBase(bool bRadio, int id, const std::string &xmlFile) :
53 CGUIMediaWindow(id, xmlFile.c_str()),
58 CGUIWindowPVRBase::~CGUIWindowPVRBase(void)
62 void CGUIWindowPVRBase::Notify(const Observable &obs, const ObservableMessage msg)
64 CGUIMessage m(GUI_MSG_REFRESH_LIST, GetID(), 0, msg);
65 CApplicationMessenger::Get().SendGUIMessage(m);
68 bool CGUIWindowPVRBase::OnAction(const CAction &action)
70 switch (action.GetID())
72 case ACTION_PREVIOUS_CHANNELGROUP:
73 case ACTION_NEXT_CHANNELGROUP:
74 // switch to next or previous group
75 SetGroup(ACTION_NEXT_CHANNELGROUP ? m_group->GetNextGroup() : m_group->GetPreviousGroup());
79 return CGUIMediaWindow::OnAction(action);
82 bool CGUIWindowPVRBase::OnBack(int actionID)
84 if (actionID == ACTION_NAV_BACK)
86 // don't call CGUIMediaWindow as it will attempt to go to the parent folder which we don't want.
87 if (GetPreviousWindow() != WINDOW_FULLSCREEN_LIVETV)
88 g_windowManager.ActivateWindow(WINDOW_HOME);
90 return CGUIWindow::OnBack(actionID);
92 return CGUIMediaWindow::OnBack(actionID);
95 void CGUIWindowPVRBase::OnInitWindow(void)
97 if (!g_PVRManager.IsStarted() || !g_PVRClients->HasConnectedClients())
99 g_windowManager.PreviousWindow();
100 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning,
101 g_localizeStrings.Get(19045),
102 g_localizeStrings.Get(19044));
106 m_vecItems->SetPath(GetDirectoryPath());
108 CGUIMediaWindow::OnInitWindow();
111 bool CGUIWindowPVRBase::OnMessage(CGUIMessage& message)
113 switch (message.GetMessage())
115 case GUI_MSG_WINDOW_INIT:
117 m_group = g_PVRManager.GetPlayingGroup(m_bRadio);
118 SetProperty("IsRadio", m_bRadio ? "true" : "");
122 case GUI_MSG_CLICKED:
124 switch (message.GetSenderId())
126 case CONTROL_BTNCHANNELGROUPS:
127 return OpenGroupSelectionDialog();
133 return CGUIMediaWindow::OnMessage(message);
136 bool CGUIWindowPVRBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
138 bool bReturn = false;
142 case CONTEXT_BUTTON_MENU_HOOKS:
143 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
145 CFileItemPtr item = m_vecItems->Get(itemNumber);
147 if (item->IsEPG() && item->GetEPGInfoTag()->HasPVRChannel())
148 g_PVRClients->ProcessMenuHooks(item->GetEPGInfoTag()->ChannelTag()->ClientID(), PVR_MENUHOOK_EPG, item.get());
149 else if (item->IsPVRChannel())
150 g_PVRClients->ProcessMenuHooks(item->GetPVRChannelInfoTag()->ClientID(), PVR_MENUHOOK_CHANNEL, item.get());
151 else if (item->IsPVRRecording())
152 g_PVRClients->ProcessMenuHooks(item->GetPVRRecordingInfoTag()->m_iClientId, PVR_MENUHOOK_RECORDING, item.get());
153 else if (item->IsPVRTimer())
154 g_PVRClients->ProcessMenuHooks(item->GetPVRTimerInfoTag()->m_iClientId, PVR_MENUHOOK_TIMER, item.get());
159 case CONTEXT_BUTTON_FIND:
161 int windowSearchId = m_bRadio ? WINDOW_RADIO_SEARCH : WINDOW_TV_SEARCH;
162 CGUIWindowPVRBase *windowSearch = (CGUIWindowPVRBase*) g_windowManager.GetWindow(windowSearchId);
163 if (windowSearch && itemNumber >= 0 && itemNumber < m_vecItems->Size())
165 CFileItemPtr item = m_vecItems->Get(itemNumber);
166 g_windowManager.ActivateWindow(windowSearchId);
167 bReturn = windowSearch->OnContextButton(*item.get(), button);
175 return bReturn || CGUIMediaWindow::OnContextButton(itemNumber, button);
178 void CGUIWindowPVRBase::SetInvalid()
180 VECFILEITEMS items = m_vecItems->GetList();
181 for (VECFILEITEMS::iterator it = items.begin(); it != items.end(); ++it)
183 CGUIMediaWindow::SetInvalid();
186 bool CGUIWindowPVRBase::OpenGroupSelectionDialog(void)
188 CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
192 CFileItemList options;
193 g_PVRChannelGroups->Get(m_bRadio)->GetGroupList(&options);
196 dialog->SetHeading(g_localizeStrings.Get(19146));
197 dialog->SetItems(&options);
198 dialog->SetMultiSelection(false);
199 dialog->SetSelected(m_group->GroupName());
202 if (!dialog->IsConfirmed())
205 const CFileItemPtr item = dialog->GetSelectedItem();
209 SetGroup(g_PVRChannelGroups->Get(m_bRadio)->GetByName(item->m_strTitle));
214 CPVRChannelGroupPtr CGUIWindowPVRBase::GetGroup(void)
216 CSingleLock lock(m_critSection);
220 void CGUIWindowPVRBase::SetGroup(CPVRChannelGroupPtr group)
222 CSingleLock lock(m_critSection);
226 if (m_group != group)
229 m_group->UnregisterObserver(this);
231 // we need to register the window to receive changes from the new group
232 m_group->RegisterObserver(this);
233 g_PVRManager.SetPlayingGroup(m_group);
234 Update(GetDirectoryPath());
238 bool CGUIWindowPVRBase::PlayFile(CFileItem *item, bool bPlayMinimized /* = false */)
240 if (item->m_bIsFolder)
245 if (item->GetPath() == g_application.CurrentFile())
247 CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, GetID());
248 g_windowManager.SendMessage(msg);
252 CMediaSettings::Get().SetVideoStartWindowed(bPlayMinimized);
254 if (item->HasPVRRecordingInfoTag())
256 return PlayRecording(item, bPlayMinimized);
260 bool bSwitchSuccessful(false);
262 CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
264 if (channel && g_PVRManager.CheckParentalLock(*channel))
266 /* try a fast switch */
267 if (channel && (g_PVRManager.IsPlayingTV() || g_PVRManager.IsPlayingRadio()) &&
268 (channel->IsRadio() == g_PVRManager.IsPlayingRadio()))
270 if (channel->StreamURL().empty())
271 bSwitchSuccessful = g_application.m_pPlayer->SwitchChannel(*channel);
274 if (!bSwitchSuccessful)
276 CApplicationMessenger::Get().PlayFile(*item, false);
281 if (!bSwitchSuccessful)
283 CStdString channelName = g_localizeStrings.Get(19029); // Channel
285 channelName = channel->ChannelName();
286 CStdString msg = StringUtils::Format(g_localizeStrings.Get(19035).c_str(), channelName.c_str()); // CHANNELNAME could not be played. Check the log for details.
288 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
289 g_localizeStrings.Get(19166), // PVR information
298 bool CGUIWindowPVRBase::StartRecordFile(const CFileItem &item)
300 if (!item.HasEPGInfoTag())
303 const CEpgInfoTag *tag = item.GetEPGInfoTag();
304 CPVRChannelPtr channel;
306 channel = tag->ChannelTag();
308 if (!channel || !g_PVRManager.CheckParentalLock(*channel))
311 CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(&item);
312 if (timer && timer->HasPVRTimerInfoTag())
314 CGUIDialogOK::ShowAndGetInput(19033,19034,0,0);
318 // ask for confirmation before starting a timer
319 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
322 pDialog->SetHeading(264);
323 pDialog->SetLine(0, tag->PVRChannelName());
324 pDialog->SetLine(1, "");
325 pDialog->SetLine(2, tag->Title());
328 if (!pDialog->IsConfirmed())
331 CPVRTimerInfoTag *newTimer = CPVRTimerInfoTag::CreateFromEpg(*tag);
335 bReturn = g_PVRTimers->AddTimer(*newTimer);
341 bool CGUIWindowPVRBase::StopRecordFile(const CFileItem &item)
343 if (!item.HasEPGInfoTag())
346 const CEpgInfoTag *tag = item.GetEPGInfoTag();
347 if (!tag || !tag->HasPVRChannel())
350 CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(&item);
351 if (!timer || !timer->HasPVRTimerInfoTag() || timer->GetPVRTimerInfoTag()->m_bIsRepeating)
354 return g_PVRTimers->DeleteTimer(*timer);
357 bool CGUIWindowPVRBase::PlayRecording(CFileItem *item, bool bPlayMinimized /* = false */)
359 if (!item->HasPVRRecordingInfoTag())
362 CStdString stream = item->GetPVRRecordingInfoTag()->m_strStreamURL;
365 CApplicationMessenger::Get().PlayFile(*item, false);
369 /* Isolate the folder from the filename */
370 size_t found = stream.find_last_of("/");
371 if (found == CStdString::npos)
372 found = stream.find_last_of("\\");
374 if (found != CStdString::npos)
376 /* Check here for asterisk at the begin of the filename */
377 if (stream[found+1] == '*')
379 /* Create a "stack://" url with all files matching the extension */
380 CStdString ext = URIUtils::GetExtension(stream);
381 CStdString dir = stream.substr(0, found).c_str();
384 XFILE::CDirectory::GetDirectory(dir, items);
385 items.Sort(SortByFile, SortOrderAscending);
388 for (int i = 0; i < items.Size(); ++i)
390 if (URIUtils::HasExtension(items[i]->GetPath(), ext))
396 /* If we have a stack change the path of the item to it */
397 XFILE::CStackDirectory dir;
398 CStdString stackPath = dir.ConstructStackPath(items, stack);
399 item->SetPath(stackPath);
404 /* If no asterisk is present play only the given stream URL */
405 item->SetPath(stream);
410 CLog::Log(LOGERROR, "CGUIWindowPVRCommon - %s - can't open recording: no valid filename", __FUNCTION__);
411 CGUIDialogOK::ShowAndGetInput(19033,0,19036,0);
415 CApplicationMessenger::Get().PlayFile(*item, false);
420 void CGUIWindowPVRBase::ShowRecordingInfo(CFileItem *item)
422 CGUIDialogPVRRecordingInfo* pDlgInfo = (CGUIDialogPVRRecordingInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_RECORDING_INFO);
423 if (item->IsPVRRecording() && pDlgInfo)
425 pDlgInfo->SetRecording(item);
430 void CGUIWindowPVRBase::ShowEPGInfo(CFileItem *item)
432 CFileItem *tag = NULL;
433 bool bHasChannel(false);
437 tag = new CFileItem(*item);
438 if (item->GetEPGInfoTag()->HasPVRChannel())
440 channel = *item->GetEPGInfoTag()->ChannelTag();
444 else if (item->IsPVRChannel())
447 channel = *item->GetPVRChannelInfoTag();
449 if (!item->GetPVRChannelInfoTag()->GetEPGNow(epgnow))
451 CGUIDialogOK::ShowAndGetInput(19033,0,19055,0);
454 tag = new CFileItem(epgnow);
457 CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO);
458 if (tag && (!bHasChannel || g_PVRManager.CheckParentalLock(channel)) && pDlgInfo)
460 pDlgInfo->SetProgInfo(tag);
467 bool CGUIWindowPVRBase::ActionInputChannelNumber(int input)
469 CStdString strInput = StringUtils::Format("%i", input);
470 if (CGUIDialogNumeric::ShowAndGetNumber(strInput, g_localizeStrings.Get(19103)))
472 int iChannelNumber = atoi(strInput.c_str());
473 if (iChannelNumber >= 0)
476 VECFILEITEMS items = m_vecItems->GetList();
477 for (VECFILEITEMS::iterator it = items.begin(); it != items.end(); ++it)
479 if(((*it)->HasPVRChannelInfoTag() && (*it)->GetPVRChannelInfoTag()->ChannelNumber() == iChannelNumber) ||
480 ((*it)->HasEPGInfoTag() && (*it)->GetEPGInfoTag()->HasPVRChannel() && (*it)->GetEPGInfoTag()->PVRChannelNumber() == iChannelNumber))
482 // different handling for guide grid
483 if ((GetID() == WINDOW_TV_GUIDE || GetID() == WINDOW_RADIO_GUIDE) &&
484 m_viewControl.GetCurrentControl() == GUIDE_VIEW_TIMELINE)
486 CGUIEPGGridContainer* epgGridContainer = (CGUIEPGGridContainer*) GetControl(m_viewControl.GetCurrentControl());
487 epgGridContainer->SetChannel((*(*it)->GetEPGInfoTag()->ChannelTag()));
490 m_viewControl.SetSelectedItem(itemIndex);
501 bool CGUIWindowPVRBase::ActionPlayChannel(CFileItem *item)
503 bool bReturn = false;
505 if (item->GetPath() == "pvr://channels/.add.channel")
507 /* show "add channel" dialog */
508 CGUIDialogOK::ShowAndGetInput(19033,0,19038,0);
514 bReturn = PlayFile(item, CSettings::Get().GetBool("pvrplayback.playminimized"));
520 bool CGUIWindowPVRBase::ActionPlayEpg(CFileItem *item)
522 if (!item || !item->HasEPGInfoTag())
525 CPVRChannelPtr channel;
526 CEpgInfoTag *epgTag = item->GetEPGInfoTag();
527 if (epgTag->HasPVRChannel())
528 channel = epgTag->ChannelTag();
530 if (!channel || !g_PVRManager.CheckParentalLock(*channel))
534 if (epgTag->HasRecording())
535 fileItem = CFileItem(*epgTag->Recording());
537 fileItem = CFileItem(*channel);
539 g_application.SwitchToFullScreen();
540 if (!PlayFile(&fileItem))
542 // CHANNELNAME could not be played. Check the log for details.
543 CStdString msg = StringUtils::Format(g_localizeStrings.Get(19035).c_str(), channel->ChannelName().c_str());
544 CGUIDialogOK::ShowAndGetInput(19033, 0, msg, 0);
551 bool CGUIWindowPVRBase::ActionDeleteChannel(CFileItem *item)
553 CPVRChannel *channel = item->GetPVRChannelInfoTag();
555 /* check if the channel tag is valid */
556 if (!channel || channel->ChannelNumber() <= 0)
559 /* show a confirmation dialog */
560 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*) g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
563 pDialog->SetHeading(19039);
564 pDialog->SetLine(0, "");
565 pDialog->SetLine(1, channel->ChannelName());
566 pDialog->SetLine(2, "");
569 /* prompt for the user's confirmation */
570 if (!pDialog->IsConfirmed())
573 g_PVRChannelGroups->GetGroupAll(channel->IsRadio())->RemoveFromGroup(*channel);
579 bool CGUIWindowPVRBase::ActionDeleteRecording(CFileItem *item)
581 bool bReturn = false;
583 /* check if the recording tag is valid */
584 CPVRRecording *recTag = (CPVRRecording *) item->GetPVRRecordingInfoTag();
585 if (!recTag || recTag->m_strRecordingId.empty())
588 /* show a confirmation dialog */
589 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
593 pDialog->SetHeading(122); // Confirm delete
594 pDialog->SetLine(0, item->m_bIsFolder ? 19113 : 19112); // Are you sure?
595 pDialog->SetLine(1, "");
596 pDialog->SetLine(2, item->GetLabel());
597 pDialog->SetChoice(1, 117); // Delete
599 /* prompt for the user's confirmation */
601 if (!pDialog->IsConfirmed())
604 /* delete the recording */
605 if (g_PVRRecordings->Delete(*item))
607 g_PVRManager.TriggerRecordingsUpdate();
614 bool CGUIWindowPVRBase::ActionRecord(CFileItem *item)
616 bool bReturn = false;
618 CEpgInfoTag *epgTag = item->GetEPGInfoTag();
622 CPVRChannelPtr channel = epgTag->ChannelTag();
623 if (!channel || !g_PVRManager.CheckParentalLock(*channel))
626 if (epgTag->Timer() == NULL)
628 /* create a confirmation dialog */
629 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*) g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
633 pDialog->SetHeading(264);
634 pDialog->SetLine(0, "");
635 pDialog->SetLine(1, epgTag->Title());
636 pDialog->SetLine(2, "");
639 /* prompt for the user's confirmation */
640 if (!pDialog->IsConfirmed())
643 CPVRTimerInfoTag *newTimer = CPVRTimerInfoTag::CreateFromEpg(*epgTag);
646 bReturn = g_PVRTimers->AddTimer(*newTimer);
656 CGUIDialogOK::ShowAndGetInput(19033,19034,0,0);
663 bool CGUIWindowPVRBase::UpdateEpgForChannel(CFileItem *item)
665 CPVRChannel *channel = item->GetPVRChannelInfoTag();
666 CEpg *epg = channel->GetEPG();
674 bool CGUIWindowPVRBase::Update(const std::string &strDirectory, bool updateFilterPath /* = true */)
676 return CGUIMediaWindow::Update(strDirectory, updateFilterPath);
679 void CGUIWindowPVRBase::UpdateButtons(void)
681 CGUIMediaWindow::UpdateButtons();
682 SET_CONTROL_LABEL(CONTROL_BTNCHANNELGROUPS, g_localizeStrings.Get(19141) + ": " + (m_group->GroupType() == PVR_GROUP_TYPE_INTERNAL ? g_localizeStrings.Get(19287) : m_group->GroupName()));