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