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