4e042cb7b0209c8fe14f10cc294f06a0d22af41b
[vuplus_xbmc] / xbmc / pvr / windows / GUIWindowPVRGuide.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 "GUIWindowPVRGuide.h"
22
23 #include "Application.h"
24 #include "dialogs/GUIDialogOK.h"
25 #include "guilib/GUIWindowManager.h"
26 #include "guilib/Key.h"
27 #include "pvr/PVRManager.h"
28 #include "pvr/channels/PVRChannelGroupsContainer.h"
29 #include "epg/EpgContainer.h"
30 #include "pvr/windows/GUIWindowPVR.h"
31 #include "settings/AdvancedSettings.h"
32 #include "settings/Settings.h"
33 #include "threads/SingleLock.h"
34 #include "utils/log.h"
35 #include "pvr/addons/PVRClients.h"
36 #include "pvr/timers/PVRTimers.h"
37
38 using namespace PVR;
39 using namespace EPG;
40
41 CGUIWindowPVRGuide::CGUIWindowPVRGuide(CGUIWindowPVR *parent) :
42   CGUIWindowPVRCommon(parent, PVR_WINDOW_EPG, CONTROL_BTNGUIDE, CONTROL_LIST_GUIDE_NOW_NEXT),
43   Observer(),
44   m_iGuideView(CSettings::Get().GetInt("epg.defaultguideview"))
45 {
46   m_cachedTimeline = new CFileItemList;
47   m_cachedChannelGroup = CPVRChannelGroupPtr(new CPVRChannelGroup);
48 }
49
50 CGUIWindowPVRGuide::~CGUIWindowPVRGuide(void)
51 {
52   delete m_cachedTimeline;
53 }
54
55 void CGUIWindowPVRGuide::UnregisterObservers(void)
56 {
57   g_EpgContainer.UnregisterObserver(this);
58 }
59
60 void CGUIWindowPVRGuide::ResetObservers(void)
61 {
62   g_EpgContainer.RegisterObserver(this);
63 }
64
65 void CGUIWindowPVRGuide::Notify(const Observable &obs, const ObservableMessage msg)
66 {
67   if (msg == ObservableMessageEpg || msg == ObservableMessageEpgContainer)
68   {
69     m_bUpdateRequired = true;
70
71     /* update the current window if the EPG timeline view is visible */
72     if (IsFocused() && m_iGuideView == GUIDE_VIEW_TIMELINE)
73       UpdateData(false);
74   }
75   else if (msg == ObservableMessageEpgActiveItem)
76   {
77     if (IsVisible() && m_iGuideView != GUIDE_VIEW_TIMELINE)
78       SetInvalid();
79     else
80       m_bUpdateRequired = true;
81   }
82 }
83
84 void CGUIWindowPVRGuide::GetContextButtons(int itemNumber, CContextButtons &buttons) const
85 {
86   if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
87     return;
88   CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
89
90   CFileItemPtr timer = g_PVRTimers->GetTimerForEpgTag(pItem.get());
91   if (timer && timer->HasPVRTimerInfoTag())
92   {
93     if (timer->GetPVRTimerInfoTag()->IsRecording())
94       buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19059);  /* stop recording */
95     else
96       buttons.Add(CONTEXT_BUTTON_STOP_RECORD, 19060);  /* delete timer */
97   }
98   else if (pItem->GetEPGInfoTag()->EndAsLocalTime() > CDateTime::GetCurrentDateTime())
99   {
100     if (pItem->GetEPGInfoTag()->StartAsLocalTime() < CDateTime::GetCurrentDateTime())
101       buttons.Add(CONTEXT_BUTTON_START_RECORD, 264);   /* record */
102     else
103       buttons.Add(CONTEXT_BUTTON_START_RECORD, 19061); /* add timer */
104   }
105
106   buttons.Add(CONTEXT_BUTTON_INFO, 19047);              /* epg info */
107   buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 19000);         /* switch channel */
108   buttons.Add(CONTEXT_BUTTON_FIND, 19003);              /* find similar program */
109   if (m_iGuideView == GUIDE_VIEW_TIMELINE)
110   {
111     buttons.Add(CONTEXT_BUTTON_BEGIN, 19063);           /* go to begin */
112     buttons.Add(CONTEXT_BUTTON_NOW, 19070);             /* go to now */
113     buttons.Add(CONTEXT_BUTTON_END, 19064);             /* go to end */
114   }
115   if (pItem->GetEPGInfoTag()->HasPVRChannel() &&
116       g_PVRClients->HasMenuHooks(pItem->GetEPGInfoTag()->ChannelTag()->ClientID(), PVR_MENUHOOK_EPG))
117     buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195);      /* PVR client specific action */
118 }
119
120
121 bool CGUIWindowPVRGuide::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
122 {
123   if (itemNumber < 0 || itemNumber >= (int) m_parent->m_vecItems->Size())
124     return false;
125   CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
126
127   return OnContextButtonPlay(pItem.get(), button) ||
128       OnContextButtonInfo(pItem.get(), button) ||
129       OnContextButtonStartRecord(pItem.get(), button) ||
130       OnContextButtonStopRecord(pItem.get(), button) ||
131       OnContextButtonBegin(pItem.get(), button) ||
132       OnContextButtonEnd(pItem.get(), button) ||
133       OnContextButtonNow(pItem.get(), button) ||
134       CGUIWindowPVRCommon::OnContextButton(itemNumber, button);
135 }
136
137 void CGUIWindowPVRGuide::UpdateViewChannel(bool bUpdateSelectedFile)
138 {
139   CPVRChannelPtr CurrentChannel;
140   bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel);
141
142   m_parent->m_guideGrid = NULL;
143   m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_CHANNEL);
144
145   m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19029));
146   if (bGotCurrentChannel && CurrentChannel.get())
147     m_parent->SetLabel(CONTROL_LABELGROUP, CurrentChannel->ChannelName().c_str());
148
149   if ((!bGotCurrentChannel || g_PVRManager.GetCurrentEpg(*m_parent->m_vecItems) == 0) && CurrentChannel.get())
150   {
151     CFileItemPtr item;
152     item.reset(new CFileItem("pvr://guide/" + CurrentChannel->ChannelName() + "/empty.epg", false));
153     item->SetLabel(g_localizeStrings.Get(19028));
154     item->SetLabelPreformated(true);
155     m_parent->m_vecItems->Add(item);
156   }
157   m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
158 }
159
160 void CGUIWindowPVRGuide::UpdateViewNow(bool bUpdateSelectedFile)
161 {
162   CPVRChannelPtr CurrentChannel;
163   bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel);
164   bool bRadio = bGotCurrentChannel ? CurrentChannel->IsRadio() : false;
165
166   m_parent->m_guideGrid = NULL;
167   m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_NOW_NEXT);
168
169   m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19030));
170   m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19030));
171
172   int iEpgItems = g_PVRManager.GetPlayingGroup(bRadio)->GetEPGNow(*m_parent->m_vecItems);
173   if (iEpgItems == 0 && bRadio)
174     // if we didn't get any events for radio, get tv instead
175     iEpgItems = g_PVRManager.GetPlayingGroup(false)->GetEPGNow(*m_parent->m_vecItems);
176
177   if (iEpgItems == 0)
178   {
179     CFileItemPtr item;
180     item.reset(new CFileItem("pvr://guide/now/empty.epg", false));
181     item->SetLabel(g_localizeStrings.Get(19028));
182     item->SetLabelPreformated(true);
183     m_parent->m_vecItems->Add(item);
184   }
185   m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
186 }
187
188 void CGUIWindowPVRGuide::UpdateViewNext(bool bUpdateSelectedFile)
189 {
190   CPVRChannelPtr CurrentChannel;
191   bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel);
192   bool bRadio = bGotCurrentChannel ? CurrentChannel->IsRadio() : false;
193
194   m_parent->m_guideGrid = NULL;
195   m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_GUIDE_NOW_NEXT);
196
197   m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19031));
198   m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19031));
199
200   int iEpgItems = g_PVRManager.GetPlayingGroup(bRadio)->GetEPGNext(*m_parent->m_vecItems);
201   if (iEpgItems == 0 && bRadio)
202     // if we didn't get any events for radio, get tv instead
203     iEpgItems = g_PVRManager.GetPlayingGroup(false)->GetEPGNext(*m_parent->m_vecItems);
204
205   if (iEpgItems)
206   {
207     CFileItemPtr item;
208     item.reset(new CFileItem("pvr://guide/next/empty.epg", false));
209     item->SetLabel(g_localizeStrings.Get(19028));
210     item->SetLabelPreformated(true);
211     m_parent->m_vecItems->Add(item);
212   }
213   m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
214 }
215
216 void CGUIWindowPVRGuide::UpdateViewTimeline(bool bUpdateSelectedFile)
217 {
218   m_parent->m_guideGrid = (CGUIEPGGridContainer*) m_parent->GetControl(CONTROL_LIST_TIMELINE);
219   if (!m_parent->m_guideGrid)
220     return;
221
222   CPVRChannelPtr CurrentChannel;
223   bool bGotCurrentChannel = g_PVRManager.GetCurrentChannel(CurrentChannel);
224   bool bRadio = bGotCurrentChannel ? CurrentChannel->IsRadio() : false;
225
226   if (m_bUpdateRequired || m_cachedTimeline->IsEmpty() ||
227       *m_cachedChannelGroup != *g_PVRManager.GetPlayingGroup(bRadio))
228   {
229     m_bUpdateRequired = false;
230
231     m_cachedTimeline->Clear();
232     m_cachedChannelGroup = g_PVRManager.GetPlayingGroup(bRadio);
233     
234     if (m_cachedChannelGroup->GetEPGAll(*m_cachedTimeline) == 0 && bRadio)
235     {
236       // if we didn't get any events for radio, get tv instead
237       m_cachedChannelGroup = g_PVRManager.GetPlayingGroup(false);
238       m_cachedChannelGroup->GetEPGAll(*m_cachedTimeline);
239     }
240   }
241
242   m_parent->m_vecItems->RemoveDiscCache(m_parent->GetID());
243   m_parent->m_vecItems->Assign(*m_cachedTimeline, false);
244
245   CDateTime startDate(m_cachedChannelGroup->GetFirstEPGDate());
246   CDateTime endDate(m_cachedChannelGroup->GetLastEPGDate());
247   CDateTime currentDate = CDateTime::GetCurrentDateTime().GetAsUTCDateTime();
248   
249   if (!startDate.IsValid())
250     startDate = currentDate;
251   
252   if (!endDate.IsValid() || endDate < startDate)
253     endDate = startDate;
254   
255   // limit start to linger time
256   CDateTime maxPastDate = currentDate - CDateTimeSpan(0, 0, g_advancedSettings.m_iEpgLingerTime, 0);
257   if(startDate < maxPastDate)
258     startDate = maxPastDate;
259   
260   m_parent->m_guideGrid->SetStartEnd(startDate, endDate);
261
262   m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19032));
263   m_parent->SetLabel(CONTROL_LABELGROUP, g_localizeStrings.Get(19032));
264   m_parent->m_viewControl.SetCurrentView(CONTROL_LIST_TIMELINE, true);
265
266   if (bUpdateSelectedFile)
267     SelectPlayingFile();
268 }
269
270 bool CGUIWindowPVRGuide::SelectPlayingFile(void)
271 {
272   if (m_iGuideView == GUIDE_VIEW_TIMELINE)
273   {
274     if (m_parent->m_guideGrid && g_PVRManager.IsPlaying())
275       m_parent->m_guideGrid->SetChannel(g_application.CurrentFile());
276     return true;
277   }
278   return false;
279 }
280
281 void CGUIWindowPVRGuide::UpdateData(bool bUpdateSelectedFile /* = true */)
282 {
283   CSingleLock lock(m_critSection);
284   CLog::Log(LOGDEBUG, "CGUIWindowPVRGuide - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList);
285
286   /* lock the graphics context while updating */
287   CSingleLock graphicsLock(g_graphicsContext);
288   m_parent->m_viewControl.Clear();
289   m_parent->m_vecItems->Clear();
290
291   if (m_iGuideView == GUIDE_VIEW_CHANNEL)
292     UpdateViewChannel(bUpdateSelectedFile);
293   else if (m_iGuideView == GUIDE_VIEW_NOW)
294     UpdateViewNow(bUpdateSelectedFile);
295   else if (m_iGuideView == GUIDE_VIEW_NEXT)
296     UpdateViewNext(bUpdateSelectedFile);
297   else if (m_iGuideView == GUIDE_VIEW_TIMELINE)
298     UpdateViewTimeline(bUpdateSelectedFile);
299
300   m_bUpdateRequired = false;
301   m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(19222));
302   UpdateButtons();
303 }
304
305 bool CGUIWindowPVRGuide::IsSelectedButton(CGUIMessage &message) const
306 {
307   unsigned int iControl = message.GetSenderId();
308   return (iControl == CONTROL_BTNGUIDE ||
309       iControl == CONTROL_BTNGUIDE_CHANNEL ||
310       iControl == CONTROL_BTNGUIDE_NOW ||
311       iControl == CONTROL_BTNGUIDE_NEXT ||
312       iControl == CONTROL_BTNGUIDE_TIMELINE);
313 }
314
315 bool CGUIWindowPVRGuide::IsSelectedList(CGUIMessage &message) const
316 {
317   return ((message.GetSenderId() == CONTROL_LIST_TIMELINE && m_iGuideView == GUIDE_VIEW_TIMELINE) ||
318       (message.GetSenderId() == CONTROL_LIST_GUIDE_CHANNEL && m_iGuideView == GUIDE_VIEW_CHANNEL) ||
319       (message.GetSenderId() == CONTROL_LIST_GUIDE_NOW_NEXT && (m_iGuideView == GUIDE_VIEW_NOW || m_iGuideView == GUIDE_VIEW_NEXT)));
320 }
321
322 bool CGUIWindowPVRGuide::OnClickButton(CGUIMessage &message)
323 {
324   bool bReturn = false;
325
326   if (IsSelectedButton(message))
327   {
328     unsigned int iControl = message.GetSenderId();
329     bReturn = true;
330
331     if (iControl == CONTROL_BTNGUIDE)
332     {
333       if (++m_iGuideView > GUIDE_VIEW_TIMELINE)
334         m_iGuideView = GUIDE_VIEW_CHANNEL;
335     }
336     else if (iControl == CONTROL_BTNGUIDE_CHANNEL)
337       m_iGuideView = GUIDE_VIEW_CHANNEL;
338     else if (iControl == CONTROL_BTNGUIDE_NOW)
339       m_iGuideView = GUIDE_VIEW_NOW;
340     else if (iControl == CONTROL_BTNGUIDE_NEXT)
341       m_iGuideView = GUIDE_VIEW_NEXT;
342     else if (iControl == CONTROL_BTNGUIDE_TIMELINE)
343       m_iGuideView = GUIDE_VIEW_TIMELINE;
344     else
345       bReturn = false;
346
347     if (bReturn)
348       UpdateData();
349   }
350
351   return bReturn;
352 }
353
354 bool CGUIWindowPVRGuide::OnClickList(CGUIMessage &message)
355 {
356   bool bReturn = false;
357
358   if (IsSelectedList(message))
359   {
360     int iAction = message.GetParam1();
361     int iItem = m_parent->m_viewControl.GetSelectedItem();
362
363     /* get the fileitem pointer */
364     if (iItem < 0 || iItem >= (int) m_parent->m_vecItems->Size())
365       return bReturn;
366     CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem);
367
368     /* process actions */
369     switch (iAction)
370     {
371       case ACTION_SELECT_ITEM:
372       case ACTION_MOUSE_LEFT_CLICK:
373         if (!g_advancedSettings.m_bPVRShowEpgInfoOnEpgItemSelect && pItem->GetEPGInfoTag()->IsActive())
374           ActionPlayEpg(pItem.get());
375         else
376           ShowEPGInfo(pItem.get());
377         break;
378       case ACTION_SHOW_INFO:
379         ShowEPGInfo(pItem.get());
380         break;
381       case ACTION_PLAY:
382         ActionPlayEpg(pItem.get());
383         break;
384       case ACTION_RECORD:
385         ActionRecord(pItem.get());
386         break;
387       case ACTION_CONTEXT_MENU:
388       case ACTION_MOUSE_RIGHT_CLICK:
389         m_parent->OnPopupMenu(iItem);
390         break;
391       default:
392         bReturn = false;
393         break;
394     }
395   }
396
397   return bReturn;
398 }
399
400 bool CGUIWindowPVRGuide::OnContextButtonBegin(CFileItem *item, CONTEXT_BUTTON button)
401 {
402   bool bReturn = false;
403
404   if (button == CONTEXT_BUTTON_BEGIN)
405   {
406     CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
407     if (pWindow)
408       pWindow->m_guideGrid->GoToBegin();
409     bReturn = true;
410   }
411
412   return bReturn;
413 }
414
415 bool CGUIWindowPVRGuide::OnContextButtonEnd(CFileItem *item, CONTEXT_BUTTON button)
416 {
417   bool bReturn = false;
418
419   if (button == CONTEXT_BUTTON_END)
420   {
421     CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
422     if (pWindow)
423       pWindow->m_guideGrid->GoToEnd();
424     bReturn = true;
425   }
426
427   return bReturn;
428 }
429
430 bool CGUIWindowPVRGuide::OnContextButtonNow(CFileItem *item, CONTEXT_BUTTON button)
431 {
432   bool bReturn = false;
433   
434   if (button == CONTEXT_BUTTON_NOW)
435   {
436     CGUIWindowPVR *pWindow = (CGUIWindowPVR *) g_windowManager.GetWindow(WINDOW_PVR);
437     if (pWindow)
438       pWindow->m_guideGrid->GoToNow();
439     bReturn = true;
440   }
441   
442   return bReturn;
443 }
444
445 bool CGUIWindowPVRGuide::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button)
446 {
447   bool bReturn = false;
448
449   if (button == CONTEXT_BUTTON_INFO)
450   {
451     ShowEPGInfo(item);
452     bReturn = true;
453   }
454
455   return bReturn;
456 }
457
458 bool CGUIWindowPVRGuide::PlayEpgItem(CFileItem *item)
459 {
460   CPVRChannelPtr channel;
461   if (item && item->HasEPGInfoTag() && item->GetEPGInfoTag()->HasPVRChannel())
462     channel = item->GetEPGInfoTag()->ChannelTag();
463
464   if (!channel || !g_PVRManager.CheckParentalLock(*channel))
465     return false;
466
467   CLog::Log(LOGDEBUG, "play channel '%s'", channel->ChannelName().c_str());
468   CFileItem channelItem = CFileItem(*channel);
469   g_application.SwitchToFullScreen();
470   bool bReturn = PlayFile(&channelItem);
471   if (!bReturn)
472   {
473     CStdString msg = StringUtils::Format(g_localizeStrings.Get(19035).c_str(), channel->ChannelName().c_str()); // CHANNELNAME could not be played. Check the log for details.
474     CGUIDialogOK::ShowAndGetInput(19033, 0, msg, 0);
475   }
476
477   return bReturn;
478 }
479
480 bool CGUIWindowPVRGuide::OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button)
481 {
482   bool bReturn = false;
483
484   if (button == CONTEXT_BUTTON_PLAY_ITEM)
485   {
486     bReturn = PlayEpgItem(item);
487   }
488
489   return bReturn;
490 }
491
492 bool CGUIWindowPVRGuide::OnContextButtonStartRecord(CFileItem *item, CONTEXT_BUTTON button)
493 {
494   bool bReturn = false;
495
496   if (button == CONTEXT_BUTTON_START_RECORD)
497   {
498     StartRecordFile(item);
499     bReturn = true;
500   }
501
502   return bReturn;
503 }
504
505 bool CGUIWindowPVRGuide::OnContextButtonStopRecord(CFileItem *item, CONTEXT_BUTTON button)
506 {
507   bool bReturn = false;
508
509   if (button == CONTEXT_BUTTON_STOP_RECORD)
510   {
511     StopRecordFile(item);
512     bReturn = true;
513   }
514
515   return bReturn;
516 }
517
518 void CGUIWindowPVRGuide::UpdateButtons(void)
519 {
520   if (m_iGuideView == GUIDE_VIEW_CHANNEL)
521     m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19029));
522   else if (m_iGuideView == GUIDE_VIEW_NOW)
523     m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19030));
524   else if (m_iGuideView == GUIDE_VIEW_NEXT)
525     m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19031));
526   else if (m_iGuideView == GUIDE_VIEW_TIMELINE)
527     m_parent->SetLabel(m_iControlButton, g_localizeStrings.Get(19222) + ": " + g_localizeStrings.Get(19032));
528 }
529
530 void CGUIWindowPVRGuide::SettingOptionsEpgGuideViewFiller(const CSetting *setting, std::vector< std::pair<std::string, int> > &list, int &current)
531 {
532   list.push_back(make_pair(g_localizeStrings.Get(19029), PVR::GUIDE_VIEW_CHANNEL));
533   list.push_back(make_pair(g_localizeStrings.Get(19030), PVR::GUIDE_VIEW_NOW));
534   list.push_back(make_pair(g_localizeStrings.Get(19031), PVR::GUIDE_VIEW_NEXT));
535   list.push_back(make_pair(g_localizeStrings.Get(19032), PVR::GUIDE_VIEW_TIMELINE));
536 }