Merge pull request #5026 from mkortstiege/fileid
[vuplus_xbmc] / xbmc / pvr / windows / GUIWindowPVRBase.cpp
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "GUIWindowPVRBase.h"
22
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"
40 #include "epg/Epg.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"
47
48 using namespace std;
49 using namespace PVR;
50 using namespace EPG;
51
52 CGUIWindowPVRBase::CGUIWindowPVRBase(bool bRadio, int id, const std::string &xmlFile) :
53   CGUIMediaWindow(id, xmlFile.c_str()),
54   m_bRadio(bRadio)
55 {
56 }
57
58 CGUIWindowPVRBase::~CGUIWindowPVRBase(void)
59 {
60 }
61
62 void CGUIWindowPVRBase::Notify(const Observable &obs, const ObservableMessage msg)
63 {
64   CGUIMessage m(GUI_MSG_REFRESH_LIST, GetID(), msg);
65   CApplicationMessenger::Get().SendGUIMessage(m);
66 }
67
68 bool CGUIWindowPVRBase::OnAction(const CAction &action)
69 {
70   switch (action.GetID())
71   {
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());
76       return true;
77   }
78
79   return CGUIMediaWindow::OnAction(action);
80 }
81
82 bool CGUIWindowPVRBase::OnBack(int actionID)
83 {
84   if (actionID == ACTION_NAV_BACK)
85   {
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);
89     else
90       return CGUIWindow::OnBack(actionID);
91   }
92   return CGUIMediaWindow::OnBack(actionID);
93 }
94
95 void CGUIWindowPVRBase::OnInitWindow(void)
96 {
97   if (!g_PVRManager.IsStarted() || !g_PVRClients->HasConnectedClients())
98   {
99     g_windowManager.PreviousWindow();
100     CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Warning,
101         g_localizeStrings.Get(19045),
102         g_localizeStrings.Get(19044));
103     return;
104   }
105
106   CGUIMediaWindow::OnInitWindow();
107 }
108
109 bool CGUIWindowPVRBase::OnMessage(CGUIMessage& message)
110 {
111   switch (message.GetMessage())
112   {
113     case GUI_MSG_WINDOW_INIT:
114     {
115       m_group = g_PVRManager.GetPlayingGroup(m_bRadio);
116       SetProperty("IsRadio", m_bRadio ? "true" : "");
117     }
118     break;
119       
120     case GUI_MSG_CLICKED:
121     {
122       switch (message.GetSenderId())
123       {
124         case CONTROL_BTNCHANNELGROUPS:
125           return OpenGroupSelectionDialog();
126       }
127     }
128     break;
129   }
130
131   return CGUIMediaWindow::OnMessage(message);
132 }
133
134 bool CGUIWindowPVRBase::OpenGroupSelectionDialog(void)
135 {
136   CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
137   if (!dialog)
138     return false;
139
140   CFileItemList options;
141   g_PVRChannelGroups->Get(m_bRadio)->GetGroupList(&options);
142
143   dialog->Reset();
144   dialog->SetHeading(g_localizeStrings.Get(19146));
145   dialog->SetItems(&options);
146   dialog->SetMultiSelection(false);
147   dialog->SetSelected(m_group->GroupName());
148   dialog->DoModal();
149
150   if (!dialog->IsConfirmed())
151     return false;
152
153   const CFileItemPtr item = dialog->GetSelectedItem();
154   if (!item)
155     return false;
156
157   SetGroup(g_PVRChannelGroups->Get(m_bRadio)->GetByName(item->m_strTitle));
158
159   return true;
160 }
161
162 CPVRChannelGroupPtr CGUIWindowPVRBase::GetGroup(void)
163 {
164   CSingleLock lock(m_critSection);
165   return m_group;
166 }
167
168 void CGUIWindowPVRBase::SetGroup(CPVRChannelGroupPtr group)
169 {
170   CSingleLock lock(m_critSection);
171   if (!group)
172     return;
173
174   if (m_group != group)
175   {
176     if (m_group)
177       m_group->UnregisterObserver(this);
178     m_group = group;
179     // we need to register the window to receive changes from the new group
180     m_group->RegisterObserver(this);
181     g_PVRManager.SetPlayingGroup(m_group);
182     Update();
183   }
184 }
185
186 bool CGUIWindowPVRBase::PlayFile(CFileItem *item, bool bPlayMinimized /* = false */)
187 {
188   if (item->m_bIsFolder)
189   {
190     return false;
191   }
192
193   if (item->GetPath() == g_application.CurrentFile())
194   {
195     CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, GetID());
196     g_windowManager.SendMessage(msg);
197     return true;
198   }
199
200   CMediaSettings::Get().SetVideoStartWindowed(bPlayMinimized);
201
202   if (item->HasPVRRecordingInfoTag())
203   {
204     return PlayRecording(item, bPlayMinimized);
205   }
206   else
207   {
208     bool bSwitchSuccessful(false);
209
210     CPVRChannel *channel = item->HasPVRChannelInfoTag() ? item->GetPVRChannelInfoTag() : NULL;
211
212     if (channel && g_PVRManager.CheckParentalLock(*channel))
213     {
214       /* try a fast switch */
215       if (channel && (g_PVRManager.IsPlayingTV() || g_PVRManager.IsPlayingRadio()) &&
216          (channel->IsRadio() == g_PVRManager.IsPlayingRadio()))
217       {
218         if (channel->StreamURL().empty())
219           bSwitchSuccessful = g_application.m_pPlayer->SwitchChannel(*channel);
220       }
221
222       if (!bSwitchSuccessful)
223       {
224         CApplicationMessenger::Get().PlayFile(*item, false);
225         return true;
226       }
227     }
228
229     if (!bSwitchSuccessful)
230     {
231       CStdString channelName = g_localizeStrings.Get(19029); // Channel
232       if (channel)
233         channelName = channel->ChannelName();
234       CStdString msg = StringUtils::Format(g_localizeStrings.Get(19035).c_str(), channelName.c_str()); // CHANNELNAME could not be played. Check the log for details.
235
236       CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error,
237               g_localizeStrings.Get(19166), // PVR information
238               msg);
239       return false;
240     }
241   }
242
243   return true;
244 }
245
246 bool CGUIWindowPVRBase::StartRecordFile(const CFileItem &item)
247 {
248   if (!item.HasEPGInfoTag())
249     return false;
250
251   const CEpgInfoTag *tag = item.GetEPGInfoTag();
252   CPVRChannelPtr channel;
253   if (tag)
254     channel = tag->ChannelTag();
255
256   if (!channel || !g_PVRManager.CheckParentalLock(*channel))
257     return false;
258
259   CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(&item);
260   if (timer && timer->HasPVRTimerInfoTag())
261   {
262     CGUIDialogOK::ShowAndGetInput(19033,19034,0,0);
263     return false;
264   }
265
266   // ask for confirmation before starting a timer
267   CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
268   if (!pDialog)
269     return false;
270   pDialog->SetHeading(264);
271   pDialog->SetLine(0, tag->PVRChannelName());
272   pDialog->SetLine(1, "");
273   pDialog->SetLine(2, tag->Title());
274   pDialog->DoModal();
275
276   if (!pDialog->IsConfirmed())
277     return false;
278
279   CPVRTimerInfoTag *newTimer = CPVRTimerInfoTag::CreateFromEpg(*tag);
280   bool bReturn(false);
281   if (newTimer)
282   {
283     bReturn = g_PVRTimers->AddTimer(*newTimer);
284     delete newTimer;
285   }
286   return bReturn;
287 }
288
289 bool CGUIWindowPVRBase::StopRecordFile(const CFileItem &item)
290 {
291   if (!item.HasEPGInfoTag())
292     return false;
293
294   const CEpgInfoTag *tag = item.GetEPGInfoTag();
295   if (!tag || !tag->HasPVRChannel())
296     return false;
297
298   CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(&item);
299   if (!timer || !timer->HasPVRTimerInfoTag() || timer->GetPVRTimerInfoTag()->m_bIsRepeating)
300     return false;
301
302   return g_PVRTimers->DeleteTimer(*timer);
303 }
304
305 bool CGUIWindowPVRBase::PlayRecording(CFileItem *item, bool bPlayMinimized /* = false */)
306 {
307   if (!item->HasPVRRecordingInfoTag())
308     return false;
309
310   CStdString stream = item->GetPVRRecordingInfoTag()->m_strStreamURL;
311   if (stream.empty())
312   {
313     CApplicationMessenger::Get().PlayFile(*item, false);
314     return true;
315   }
316
317   /* Isolate the folder from the filename */
318   size_t found = stream.find_last_of("/");
319   if (found == CStdString::npos)
320     found = stream.find_last_of("\\");
321
322   if (found != CStdString::npos)
323   {
324     /* Check here for asterisk at the begin of the filename */
325     if (stream[found+1] == '*')
326     {
327       /* Create a "stack://" url with all files matching the extension */
328       CStdString ext = URIUtils::GetExtension(stream);
329       CStdString dir = stream.substr(0, found).c_str();
330
331       CFileItemList items;
332       XFILE::CDirectory::GetDirectory(dir, items);
333       items.Sort(SortByFile, SortOrderAscending);
334
335       vector<int> stack;
336       for (int i = 0; i < items.Size(); ++i)
337       {
338         if (URIUtils::HasExtension(items[i]->GetPath(), ext))
339           stack.push_back(i);
340       }
341
342       if (stack.empty())
343       {
344         /* If we have a stack change the path of the item to it */
345         XFILE::CStackDirectory dir;
346         CStdString stackPath = dir.ConstructStackPath(items, stack);
347         item->SetPath(stackPath);
348       }
349     }
350     else
351     {
352       /* If no asterisk is present play only the given stream URL */
353       item->SetPath(stream);
354     }
355   }
356   else
357   {
358     CLog::Log(LOGERROR, "CGUIWindowPVRCommon - %s - can't open recording: no valid filename", __FUNCTION__);
359     CGUIDialogOK::ShowAndGetInput(19033,0,19036,0);
360     return false;
361   }
362
363   CApplicationMessenger::Get().PlayFile(*item, false);
364
365   return true;
366 }
367
368 void CGUIWindowPVRBase::ShowRecordingInfo(CFileItem *item)
369 {
370   CGUIDialogPVRRecordingInfo* pDlgInfo = (CGUIDialogPVRRecordingInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_RECORDING_INFO);
371   if (item->IsPVRRecording() && pDlgInfo)
372   {
373     pDlgInfo->SetRecording(item);
374     pDlgInfo->DoModal();
375   }
376 }
377
378 void CGUIWindowPVRBase::ShowEPGInfo(CFileItem *item)
379 {
380   CFileItem *tag = NULL;
381   bool bHasChannel(false);
382   CPVRChannel channel;
383   if (item->IsEPG())
384   {
385     tag = new CFileItem(*item);
386     if (item->GetEPGInfoTag()->HasPVRChannel())
387     {
388       channel = *item->GetEPGInfoTag()->ChannelTag();
389       bHasChannel = true;
390     }
391   }
392   else if (item->IsPVRChannel())
393   {
394     CEpgInfoTag epgnow;
395     channel = *item->GetPVRChannelInfoTag();
396     bHasChannel = true;
397     if (!item->GetPVRChannelInfoTag()->GetEPGNow(epgnow))
398     {
399       CGUIDialogOK::ShowAndGetInput(19033,0,19055,0);
400       return;
401     }
402     tag = new CFileItem(epgnow);
403   }
404
405   CGUIDialogPVRGuideInfo* pDlgInfo = (CGUIDialogPVRGuideInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_PVR_GUIDE_INFO);
406   if (tag && (!bHasChannel || g_PVRManager.CheckParentalLock(channel)) && pDlgInfo)
407   {
408     pDlgInfo->SetProgInfo(tag);
409     pDlgInfo->DoModal();
410   }
411
412   delete tag;
413 }
414
415 bool CGUIWindowPVRBase::ActionInputChannelNumber(int input)
416 {
417   CStdString strInput = StringUtils::Format("%i", input);
418   if (CGUIDialogNumeric::ShowAndGetNumber(strInput, g_localizeStrings.Get(19103)))
419   {
420     int iChannelNumber = atoi(strInput.c_str());
421     if (iChannelNumber >= 0)
422     {
423       int itemIndex = 0;
424       VECFILEITEMS items = m_vecItems->GetList();
425       for (VECFILEITEMS::iterator it = items.begin(); it != items.end(); ++it)
426       {
427         if(((*it)->HasPVRChannelInfoTag() && (*it)->GetPVRChannelInfoTag()->ChannelNumber() == iChannelNumber) ||
428            ((*it)->HasEPGInfoTag() && (*it)->GetEPGInfoTag()->HasPVRChannel() && (*it)->GetEPGInfoTag()->PVRChannelNumber() == iChannelNumber))
429         {
430           // different handling for guide grid
431           if ((GetID() == WINDOW_TV_GUIDE || GetID() == WINDOW_RADIO_GUIDE) &&
432               m_viewControl.GetCurrentControl() == GUIDE_VIEW_TIMELINE)
433           {
434             CGUIEPGGridContainer* epgGridContainer = (CGUIEPGGridContainer*) GetControl(m_viewControl.GetCurrentControl());
435             epgGridContainer->SetChannel((*(*it)->GetEPGInfoTag()->ChannelTag()));
436           }
437           else
438             m_viewControl.SetSelectedItem(itemIndex);
439           return true;
440         }
441         itemIndex++;
442       }
443     }
444   }
445
446   return false;
447 }
448
449 bool CGUIWindowPVRBase::ActionPlayChannel(CFileItem *item)
450 {
451   bool bReturn = false;
452
453   if (item->GetPath() == "pvr://channels/.add.channel")
454   {
455     /* show "add channel" dialog */
456     CGUIDialogOK::ShowAndGetInput(19033,0,19038,0);
457     bReturn = true;
458   }
459   else
460   {
461     /* open channel */
462     bReturn = PlayFile(item, CSettings::Get().GetBool("pvrplayback.playminimized"));
463   }
464
465   return bReturn;
466 }
467
468 bool CGUIWindowPVRBase::ActionPlayEpg(CFileItem *item)
469 {
470   if (!item || !item->HasEPGInfoTag())
471     return false;
472
473   CPVRChannelPtr channel;
474   CEpgInfoTag *epgTag = item->GetEPGInfoTag();
475   if (epgTag->HasPVRChannel())
476     channel = epgTag->ChannelTag();
477
478   if (!channel || !g_PVRManager.CheckParentalLock(*channel))
479     return false;
480
481   CFileItem fileItem;
482   if (epgTag->HasRecording())
483     fileItem = CFileItem(*epgTag->Recording());
484   else
485     fileItem = CFileItem(*channel);
486
487   g_application.SwitchToFullScreen();
488   if (!PlayFile(&fileItem))
489   {
490     // CHANNELNAME could not be played. Check the log for details.
491     CStdString msg = StringUtils::Format(g_localizeStrings.Get(19035).c_str(), channel->ChannelName().c_str());
492     CGUIDialogOK::ShowAndGetInput(19033, 0, msg, 0);
493     return false;
494   }
495
496   return true;
497 }
498
499 bool CGUIWindowPVRBase::ActionDeleteChannel(CFileItem *item)
500 {
501   CPVRChannel *channel = item->GetPVRChannelInfoTag();
502
503   /* check if the channel tag is valid */
504   if (!channel || channel->ChannelNumber() <= 0)
505     return false;
506
507   /* show a confirmation dialog */
508   CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*) g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
509   if (!pDialog)
510     return false;
511   pDialog->SetHeading(19039);
512   pDialog->SetLine(0, "");
513   pDialog->SetLine(1, channel->ChannelName());
514   pDialog->SetLine(2, "");
515   pDialog->DoModal();
516
517   /* prompt for the user's confirmation */
518   if (!pDialog->IsConfirmed())
519     return false;
520
521   g_PVRChannelGroups->GetGroupAll(channel->IsRadio())->RemoveFromGroup(*channel);
522   Update();
523
524   return true;
525 }
526
527 bool CGUIWindowPVRBase::ActionDeleteRecording(CFileItem *item)
528 {
529   bool bReturn = false;
530
531   /* check if the recording tag is valid */
532   CPVRRecording *recTag = (CPVRRecording *) item->GetPVRRecordingInfoTag();
533   if (!recTag || recTag->m_strRecordingId.empty())
534     return bReturn;
535
536   /* show a confirmation dialog */
537   CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
538   if (!pDialog)
539     return bReturn;
540
541   pDialog->SetHeading(122); // Confirm delete
542   pDialog->SetLine(0, item->m_bIsFolder ? 19113 : 19112); // Are you sure?
543   pDialog->SetLine(1, "");
544   pDialog->SetLine(2, item->GetLabel());
545   pDialog->SetChoice(1, 117); // Delete
546
547   /* prompt for the user's confirmation */
548   pDialog->DoModal();
549   if (!pDialog->IsConfirmed())
550     return bReturn;
551
552   /* delete the recording */
553   if (g_PVRRecordings->Delete(*item))
554   {
555     g_PVRManager.TriggerRecordingsUpdate();
556     bReturn = true;
557   }
558
559   return bReturn;
560 }
561
562 bool CGUIWindowPVRBase::ActionRecord(CFileItem *item)
563 {
564   bool bReturn = false;
565
566   CEpgInfoTag *epgTag = item->GetEPGInfoTag();
567   if (!epgTag)
568     return bReturn;
569
570   CPVRChannelPtr channel = epgTag->ChannelTag();
571   if (!channel || !g_PVRManager.CheckParentalLock(*channel))
572     return bReturn;
573
574   if (epgTag->Timer() == NULL)
575   {
576     /* create a confirmation dialog */
577     CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*) g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
578     if (!pDialog)
579       return bReturn;
580
581     pDialog->SetHeading(264);
582     pDialog->SetLine(0, "");
583     pDialog->SetLine(1, epgTag->Title());
584     pDialog->SetLine(2, "");
585     pDialog->DoModal();
586
587     /* prompt for the user's confirmation */
588     if (!pDialog->IsConfirmed())
589       return bReturn;
590
591     CPVRTimerInfoTag *newTimer = CPVRTimerInfoTag::CreateFromEpg(*epgTag);
592     if (newTimer)
593     {
594       bReturn = g_PVRTimers->AddTimer(*newTimer);
595       delete newTimer;
596     }
597     else
598     {
599       bReturn = false;
600     }
601   }
602   else
603   {
604     CGUIDialogOK::ShowAndGetInput(19033,19034,0,0);
605     bReturn = true;
606   }
607
608   return bReturn;
609 }
610
611 bool CGUIWindowPVRBase::UpdateEpgForChannel(CFileItem *item)
612 {
613   CPVRChannel *channel = item->GetPVRChannelInfoTag();
614   CEpg *epg = channel->GetEPG();
615   if (!epg)
616     return false;
617
618   epg->ForceUpdate();
619   return true;
620 }
621
622 bool CGUIWindowPVRBase::Update(const std::string &strDirectory, bool updateFilterPath /* = true */)
623 {
624   return CGUIMediaWindow::Update(strDirectory, updateFilterPath);
625 }
626
627 void CGUIWindowPVRBase::UpdateButtons(void)
628 {
629   CGUIMediaWindow::UpdateButtons();
630   SET_CONTROL_LABEL(CONTROL_BTNCHANNELGROUPS, g_localizeStrings.Get(19141) + ": " + (m_group->GroupType() == PVR_GROUP_TYPE_INTERNAL ? g_localizeStrings.Get(19287) : m_group->GroupName()));
631 }