1 /*
2  *  Copyright (C) 2016-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "PVRContextMenus.h"
10 
11 #include "ContextMenuItem.h"
12 #include "ServiceBroker.h"
13 #include "guilib/LocalizeStrings.h"
14 #include "pvr/PVRManager.h"
15 #include "pvr/addons/PVRClient.h"
16 #include "pvr/addons/PVRClientMenuHooks.h"
17 #include "pvr/channels/PVRChannel.h"
18 #include "pvr/channels/PVRChannelGroupsContainer.h"
19 #include "pvr/epg/EpgInfoTag.h"
20 #include "pvr/guilib/PVRGUIActions.h"
21 #include "pvr/recordings/PVRRecording.h"
22 #include "pvr/recordings/PVRRecordings.h"
23 #include "pvr/recordings/PVRRecordingsPath.h"
24 #include "pvr/timers/PVRTimerInfoTag.h"
25 #include "pvr/timers/PVRTimers.h"
26 #include "pvr/timers/PVRTimersPath.h"
27 #include "utils/URIUtils.h"
28 
29 #include <memory>
30 #include <string>
31 
32 namespace PVR
33 {
34   namespace CONTEXTMENUITEM
35   {
36     #define DECL_STATICCONTEXTMENUITEM(clazz) \
37     class clazz : public CStaticContextMenuAction \
38     { \
39     public: \
40       explicit clazz(uint32_t label) : CStaticContextMenuAction(label) {} \
41       bool IsVisible(const CFileItem& item) const override; \
42       bool Execute(const CFileItemPtr& item) const override; \
43     };
44 
45     #define DECL_CONTEXTMENUITEM(clazz) \
46     class clazz : public IContextMenuItem \
47     { \
48     public: \
49       std::string GetLabel(const CFileItem& item) const override; \
50       bool IsVisible(const CFileItem& item) const override; \
51       bool Execute(const CFileItemPtr& item) const override; \
52     };
53 
54     DECL_STATICCONTEXTMENUITEM(PlayEpgTag);
55     DECL_STATICCONTEXTMENUITEM(PlayRecording);
56     DECL_CONTEXTMENUITEM(ShowInformation);
57     DECL_STATICCONTEXTMENUITEM(ShowChannelGuide);
58     DECL_STATICCONTEXTMENUITEM(FindSimilar);
59     DECL_STATICCONTEXTMENUITEM(StartRecording);
60     DECL_STATICCONTEXTMENUITEM(StopRecording);
61     DECL_STATICCONTEXTMENUITEM(AddTimerRule);
62     DECL_CONTEXTMENUITEM(EditTimerRule);
63     DECL_STATICCONTEXTMENUITEM(DeleteTimerRule);
64     DECL_CONTEXTMENUITEM(EditTimer);
65     DECL_CONTEXTMENUITEM(DeleteTimer);
66     DECL_STATICCONTEXTMENUITEM(EditRecording);
67     DECL_CONTEXTMENUITEM(DeleteRecording);
68     DECL_STATICCONTEXTMENUITEM(UndeleteRecording);
69     DECL_STATICCONTEXTMENUITEM(DeleteWatchedRecordings);
70     DECL_CONTEXTMENUITEM(ToggleTimerState);
71     DECL_STATICCONTEXTMENUITEM(AddReminder);
72 
73     class PVRClientMenuHook : public IContextMenuItem
74     {
75     public:
PVRClientMenuHook(const CPVRClientMenuHook & hook)76       PVRClientMenuHook(const CPVRClientMenuHook& hook) : m_hook(hook) {};
77 
78       std::string GetLabel(const CFileItem& item) const override;
79       bool IsVisible(const CFileItem& item) const override;
80       bool Execute(const CFileItemPtr& item) const override;
81 
GetHook() const82       const CPVRClientMenuHook& GetHook() const { return m_hook; }
83 
84     private:
85       const CPVRClientMenuHook m_hook;
86     };
87 
GetTimerInfoTagFromItem(const CFileItem & item)88     std::shared_ptr<CPVRTimerInfoTag> GetTimerInfoTagFromItem(const CFileItem& item)
89     {
90       std::shared_ptr<CPVRTimerInfoTag> timer;
91 
92       const std::shared_ptr<CPVREpgInfoTag> epg(item.GetEPGInfoTag());
93       if (epg)
94         timer = CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg);
95 
96       if (!timer)
97         timer = item.GetPVRTimerInfoTag();
98 
99       return timer;
100     }
101 
102     ///////////////////////////////////////////////////////////////////////////////
103     // Play epg tag
104 
IsVisible(const CFileItem & item) const105     bool PlayEpgTag::IsVisible(const CFileItem& item) const
106     {
107       const std::shared_ptr<CPVREpgInfoTag> epg(item.GetEPGInfoTag());
108       if (epg)
109         return epg->IsPlayable();
110 
111       return false;
112     }
113 
Execute(const CFileItemPtr & item) const114     bool PlayEpgTag::Execute(const CFileItemPtr& item) const
115     {
116       return CServiceBroker::GetPVRManager().GUIActions()->PlayEpgTag(item);
117     }
118 
119     ///////////////////////////////////////////////////////////////////////////////
120     // Play recording
121 
IsVisible(const CFileItem & item) const122     bool PlayRecording::IsVisible(const CFileItem& item) const
123     {
124       const std::shared_ptr<CPVRRecording> recording = CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(item.GetEPGInfoTag());
125       if (recording)
126         return !recording->IsDeleted();
127 
128       return false;
129     }
130 
Execute(const CFileItemPtr & item) const131     bool PlayRecording::Execute(const CFileItemPtr& item) const
132     {
133       return CServiceBroker::GetPVRManager().GUIActions()->PlayRecording(item, true /* bCheckResume */);
134     }
135 
136     ///////////////////////////////////////////////////////////////////////////////
137     // Show information (epg, recording)
138 
GetLabel(const CFileItem & item) const139     std::string ShowInformation::GetLabel(const CFileItem& item) const
140     {
141       if (item.GetPVRRecordingInfoTag())
142         return g_localizeStrings.Get(19053); /* Recording Information */
143 
144       return g_localizeStrings.Get(19047); /* Programme information */
145     }
146 
IsVisible(const CFileItem & item) const147     bool ShowInformation::IsVisible(const CFileItem& item) const
148     {
149       const std::shared_ptr<CPVRChannel> channel(item.GetPVRChannelInfoTag());
150       if (channel)
151         return channel->GetEPGNow().get() != nullptr;
152 
153       if (item.HasEPGInfoTag())
154         return !item.GetEPGInfoTag()->IsGapTag();
155 
156       const std::shared_ptr<CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
157       if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
158         return timer->GetEpgInfoTag().get() != nullptr;
159 
160       if (item.GetPVRRecordingInfoTag())
161         return true;
162 
163       return false;
164     }
165 
Execute(const CFileItemPtr & item) const166     bool ShowInformation::Execute(const CFileItemPtr& item) const
167     {
168       if (item->GetPVRRecordingInfoTag())
169         return CServiceBroker::GetPVRManager().GUIActions()->ShowRecordingInfo(item);
170 
171       return CServiceBroker::GetPVRManager().GUIActions()->ShowEPGInfo(item);
172     }
173 
174     ///////////////////////////////////////////////////////////////////////////////
175     // Show channel guide
176 
IsVisible(const CFileItem & item) const177     bool ShowChannelGuide::IsVisible(const CFileItem& item) const
178     {
179       const std::shared_ptr<CPVRChannel> channel(item.GetPVRChannelInfoTag());
180       if (channel)
181         return channel->GetEPGNow().get() != nullptr;
182 
183       return false;
184     }
185 
Execute(const CFileItemPtr & item) const186     bool ShowChannelGuide::Execute(const CFileItemPtr& item) const
187     {
188       return CServiceBroker::GetPVRManager().GUIActions()->ShowChannelEPG(item);
189     }
190 
191     ///////////////////////////////////////////////////////////////////////////////
192     // Find similar
193 
IsVisible(const CFileItem & item) const194     bool FindSimilar::IsVisible(const CFileItem& item) const
195     {
196       const std::shared_ptr<CPVRChannel> channel(item.GetPVRChannelInfoTag());
197       if (channel)
198         return channel->GetEPGNow().get() != nullptr;
199 
200       if (item.HasEPGInfoTag())
201         return !item.GetEPGInfoTag()->IsGapTag();
202 
203       const std::shared_ptr<CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
204       if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
205         return timer->GetEpgInfoTag().get() != nullptr;
206 
207       const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
208       if (recording)
209         return !recording->IsDeleted();
210 
211       return false;
212     }
213 
Execute(const CFileItemPtr & item) const214     bool FindSimilar::Execute(const CFileItemPtr& item) const
215     {
216       return CServiceBroker::GetPVRManager().GUIActions()->FindSimilar(item);
217     }
218 
219     ///////////////////////////////////////////////////////////////////////////////
220     // Start recording
221 
IsVisible(const CFileItem & item) const222     bool StartRecording::IsVisible(const CFileItem& item) const
223     {
224       const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(item);
225 
226       std::shared_ptr<CPVRChannel> channel = item.GetPVRChannelInfoTag();
227       if (channel)
228         return client && client->GetClientCapabilities().SupportsTimers() &&
229                !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel);
230 
231       const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
232       if (epg && epg->IsRecordable())
233       {
234         if (epg->IsGapTag())
235         {
236           channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epg);
237           if (channel)
238           {
239             return client && client->GetClientCapabilities().SupportsTimers() &&
240                 !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel);
241           }
242         }
243         else
244         {
245           return client && client->GetClientCapabilities().SupportsTimers() &&
246               !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg);
247         }
248       }
249       return false;
250     }
251 
Execute(const CFileItemPtr & item) const252     bool StartRecording::Execute(const CFileItemPtr& item) const
253     {
254       const std::shared_ptr<CPVREpgInfoTag> epgTag = item->GetEPGInfoTag();
255       if (!epgTag || epgTag->IsActive())
256       {
257         // instant recording
258         std::shared_ptr<CPVRChannel> channel;
259         if (epgTag)
260           channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epgTag);
261 
262         if (!channel)
263           channel = item->GetPVRChannelInfoTag();
264 
265         if (channel)
266           return CServiceBroker::GetPVRManager().GUIActions()->SetRecordingOnChannel(channel, true);
267       }
268 
269       return CServiceBroker::GetPVRManager().GUIActions()->AddTimer(item, false);
270     }
271 
272     ///////////////////////////////////////////////////////////////////////////////
273     // Stop recording
274 
IsVisible(const CFileItem & item) const275     bool StopRecording::IsVisible(const CFileItem& item) const
276     {
277       const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
278       if (recording && recording->IsInProgress())
279         return true;
280 
281       std::shared_ptr<CPVRChannel> channel = item.GetPVRChannelInfoTag();
282       if (channel)
283         return CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel);
284 
285       const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
286       if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
287         return timer->IsRecording();
288 
289       const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
290       if (epg && epg->IsGapTag())
291       {
292         channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epg);
293         if (channel)
294           return CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel);
295       }
296 
297       return false;
298     }
299 
Execute(const CFileItemPtr & item) const300     bool StopRecording::Execute(const CFileItemPtr& item) const
301     {
302       const std::shared_ptr<CPVREpgInfoTag> epgTag = item->GetEPGInfoTag();
303       if (epgTag && epgTag->IsGapTag())
304       {
305         // instance recording
306         const std::shared_ptr<CPVRChannel> channel =
307             CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag(epgTag);
308         if (channel)
309           return CServiceBroker::GetPVRManager().GUIActions()->SetRecordingOnChannel(channel,
310                                                                                      false);
311       }
312 
313       return CServiceBroker::GetPVRManager().GUIActions()->StopRecording(item);
314     }
315 
316     ///////////////////////////////////////////////////////////////////////////////
317     // Edit recording
318 
IsVisible(const CFileItem & item) const319     bool EditRecording::IsVisible(const CFileItem& item) const
320     {
321       const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
322       if (recording && !recording->IsDeleted() && !recording->IsInProgress())
323       {
324         return CServiceBroker::GetPVRManager().GUIActions()->CanEditRecording(item);
325       }
326       return false;
327     }
328 
Execute(const CFileItemPtr & item) const329     bool EditRecording::Execute(const CFileItemPtr& item) const
330     {
331       return CServiceBroker::GetPVRManager().GUIActions()->EditRecording(item);
332     }
333 
334     ///////////////////////////////////////////////////////////////////////////////
335     // Delete recording
336 
GetLabel(const CFileItem & item) const337     std::string DeleteRecording::GetLabel(const CFileItem& item) const
338     {
339       const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
340       if (recording && recording->IsDeleted())
341         return g_localizeStrings.Get(19291); /* Delete permanently */
342 
343       return g_localizeStrings.Get(117); /* Delete */
344     }
345 
IsVisible(const CFileItem & item) const346     bool DeleteRecording::IsVisible(const CFileItem& item) const
347     {
348       const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
349       if (recording && !recording->IsInProgress())
350         return true;
351 
352       // recordings folder?
353       if (item.m_bIsFolder)
354       {
355         const CPVRRecordingsPath path(item.GetPath());
356         return path.IsValid() && !path.IsRecordingsRoot();
357       }
358 
359       return false;
360     }
361 
Execute(const CFileItemPtr & item) const362     bool DeleteRecording::Execute(const CFileItemPtr& item) const
363     {
364       return CServiceBroker::GetPVRManager().GUIActions()->DeleteRecording(item);
365     }
366 
367     ///////////////////////////////////////////////////////////////////////////////
368     // Undelete recording
369 
IsVisible(const CFileItem & item) const370     bool UndeleteRecording::IsVisible(const CFileItem& item) const
371     {
372       const std::shared_ptr<CPVRRecording> recording(item.GetPVRRecordingInfoTag());
373       if (recording && recording->IsDeleted())
374         return true;
375 
376       return false;
377     }
378 
Execute(const CFileItemPtr & item) const379     bool UndeleteRecording::Execute(const CFileItemPtr& item) const
380     {
381       return CServiceBroker::GetPVRManager().GUIActions()->UndeleteRecording(item);
382     }
383 
384     ///////////////////////////////////////////////////////////////////////////////
385     // Delete watched recordings
386 
IsVisible(const CFileItem & item) const387     bool DeleteWatchedRecordings::IsVisible(const CFileItem& item) const
388     {
389       // recordings folder?
390       if (item.m_bIsFolder && !item.IsParentFolder())
391         return CPVRRecordingsPath(item.GetPath()).IsValid();
392 
393       return false;
394     }
395 
Execute(const std::shared_ptr<CFileItem> & item) const396     bool DeleteWatchedRecordings::Execute(const std::shared_ptr<CFileItem>& item) const
397     {
398       return CServiceBroker::GetPVRManager().GUIActions()->DeleteWatchedRecordings(item);
399     }
400 
401     ///////////////////////////////////////////////////////////////////////////////
402     // Add reminder
403 
IsVisible(const CFileItem & item) const404     bool AddReminder::IsVisible(const CFileItem& item) const
405     {
406       const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
407       if (epg &&
408           !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg) &&
409           epg->StartAsLocalTime() > CDateTime::GetCurrentDateTime())
410         return true;
411 
412       return false;
413     }
414 
Execute(const std::shared_ptr<CFileItem> & item) const415     bool AddReminder::Execute(const std::shared_ptr<CFileItem>& item) const
416     {
417       return CServiceBroker::GetPVRManager().GUIActions()->AddReminder(item);
418     }
419 
420     ///////////////////////////////////////////////////////////////////////////////
421     // Activate / deactivate timer or timer rule
422 
GetLabel(const CFileItem & item) const423     std::string ToggleTimerState::GetLabel(const CFileItem& item) const
424     {
425       const std::shared_ptr<CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
426       if (timer && timer->m_state != PVR_TIMER_STATE_DISABLED)
427         return g_localizeStrings.Get(844); /* Deactivate */
428 
429       return g_localizeStrings.Get(843); /* Activate */
430     }
431 
IsVisible(const CFileItem & item) const432     bool ToggleTimerState::IsVisible(const CFileItem& item) const
433     {
434       const std::shared_ptr<CPVRTimerInfoTag> timer(item.GetPVRTimerInfoTag());
435       if (!timer || URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER) || timer->IsBroken())
436         return false;
437 
438       const std::shared_ptr<CPVRTimerType> timerType(timer->GetTimerType());
439       return timerType && timerType->SupportsEnableDisable();
440     }
441 
Execute(const CFileItemPtr & item) const442     bool ToggleTimerState::Execute(const CFileItemPtr& item) const
443     {
444       return CServiceBroker::GetPVRManager().GUIActions()->ToggleTimerState(item);
445     }
446 
447     ///////////////////////////////////////////////////////////////////////////////
448     // Add timer rule
449 
IsVisible(const CFileItem & item) const450     bool AddTimerRule::IsVisible(const CFileItem& item) const
451     {
452       const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
453       return (epg && !epg->IsGapTag() &&
454               !CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg));
455     }
456 
Execute(const CFileItemPtr & item) const457     bool AddTimerRule::Execute(const CFileItemPtr& item) const
458     {
459       return CServiceBroker::GetPVRManager().GUIActions()->AddTimerRule(item, true, true);
460     }
461 
462     ///////////////////////////////////////////////////////////////////////////////
463     // Edit timer rule
464 
GetLabel(const CFileItem & item) const465     std::string EditTimerRule::GetLabel(const CFileItem& item) const
466     {
467       const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
468       if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
469       {
470         const std::shared_ptr<CPVRTimerInfoTag> parentTimer(CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer));
471         if (parentTimer)
472         {
473           const std::shared_ptr<CPVRTimerType> parentTimerType(parentTimer->GetTimerType());
474           if (parentTimerType && !parentTimerType->IsReadOnly())
475             return g_localizeStrings.Get(19243); /* Edit timer rule */
476         }
477       }
478 
479       return g_localizeStrings.Get(19304); /* View timer rule */
480     }
481 
IsVisible(const CFileItem & item) const482     bool EditTimerRule::IsVisible(const CFileItem& item) const
483     {
484       const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
485       if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
486         return timer->GetTimerRuleId() != PVR_TIMER_NO_PARENT;
487 
488       return false;
489     }
490 
Execute(const CFileItemPtr & item) const491     bool EditTimerRule::Execute(const CFileItemPtr& item) const
492     {
493       return CServiceBroker::GetPVRManager().GUIActions()->EditTimerRule(item);
494     }
495 
496     ///////////////////////////////////////////////////////////////////////////////
497     // Delete timer rule
498 
IsVisible(const CFileItem & item) const499     bool DeleteTimerRule::IsVisible(const CFileItem& item) const
500     {
501       const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
502       if (timer && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER))
503       {
504         const std::shared_ptr<CPVRTimerInfoTag> parentTimer(CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer));
505         if (parentTimer)
506         {
507           const std::shared_ptr<CPVRTimerType> parentTimerType(parentTimer->GetTimerType());
508           return parentTimerType && parentTimerType->AllowsDelete();
509         }
510       }
511 
512       return false;
513     }
514 
Execute(const CFileItemPtr & item) const515     bool DeleteTimerRule::Execute(const CFileItemPtr& item) const
516     {
517       const std::shared_ptr<CFileItem> parentTimer = CServiceBroker::GetPVRManager().GUIActions()->GetTimerRule(item);
518       if (parentTimer)
519         return CServiceBroker::GetPVRManager().GUIActions()->DeleteTimerRule(parentTimer);
520 
521       return false;
522     }
523 
524     ///////////////////////////////////////////////////////////////////////////////
525     // Edit / View timer
526 
GetLabel(const CFileItem & item) const527     std::string EditTimer::GetLabel(const CFileItem& item) const
528     {
529       const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
530       if (timer)
531       {
532         const std::shared_ptr<CPVRTimerType> timerType(timer->GetTimerType());
533         if (timerType)
534         {
535           if (item.GetEPGInfoTag())
536           {
537             if (timerType->IsReminder())
538               return g_localizeStrings.Get(timerType->IsReadOnly() ? 829 : 830); /* View/Edit reminder */
539             else
540               return g_localizeStrings.Get(timerType->IsReadOnly() ? 19241 : 19242); /* View/Edit timer */
541           }
542           else
543             return g_localizeStrings.Get(timerType->IsReadOnly() ? 21483 : 21450); /* View/Edit */
544         }
545       }
546       return g_localizeStrings.Get(19241); /* View timer */
547     }
548 
IsVisible(const CFileItem & item) const549     bool EditTimer::IsVisible(const CFileItem& item) const
550     {
551       const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
552       return timer && (!item.GetEPGInfoTag() || !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER));
553     }
554 
Execute(const CFileItemPtr & item) const555     bool EditTimer::Execute(const CFileItemPtr& item) const
556     {
557       return CServiceBroker::GetPVRManager().GUIActions()->EditTimer(item);
558     }
559 
560     ///////////////////////////////////////////////////////////////////////////////
561     // Delete timer
562 
GetLabel(const CFileItem & item) const563     std::string DeleteTimer::GetLabel(const CFileItem& item) const
564     {
565       if (item.GetPVRTimerInfoTag())
566         return g_localizeStrings.Get(117); /* Delete */
567 
568       const std::shared_ptr<CPVREpgInfoTag> epg = item.GetEPGInfoTag();
569       if (epg)
570       {
571         const std::shared_ptr<CPVRTimerInfoTag> timer = CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epg);
572         if (timer && timer->IsReminder())
573           return g_localizeStrings.Get(827); /* Delete reminder */
574       }
575       return g_localizeStrings.Get(19060); /* Delete timer */
576     }
577 
IsVisible(const CFileItem & item) const578     bool DeleteTimer::IsVisible(const CFileItem& item) const
579     {
580       const std::shared_ptr<CPVRTimerInfoTag> timer(GetTimerInfoTagFromItem(item));
581       if (timer && (!item.GetEPGInfoTag() || !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER)) && !timer->IsRecording())
582       {
583         const std::shared_ptr<CPVRTimerType> timerType(timer->GetTimerType());
584         return timerType && timerType->AllowsDelete();
585       }
586 
587       return false;
588     }
589 
Execute(const CFileItemPtr & item) const590     bool DeleteTimer::Execute(const CFileItemPtr& item) const
591     {
592       return CServiceBroker::GetPVRManager().GUIActions()->DeleteTimer(item);
593     }
594 
595     ///////////////////////////////////////////////////////////////////////////////
596     // PVR Client menu hook
597 
GetLabel(const CFileItem & item) const598     std::string PVRClientMenuHook::GetLabel(const CFileItem& item) const
599     {
600       return m_hook.GetLabel();
601     }
602 
IsVisible(const CFileItem & item) const603     bool PVRClientMenuHook::IsVisible(const CFileItem& item) const
604     {
605       const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(item);
606       if (!client || m_hook.GetAddonId() != client->ID())
607         return false;
608 
609       if (m_hook.IsAllHook())
610         return !item.m_bIsFolder && !URIUtils::PathEquals(item.GetPath(), CPVRTimersPath::PATH_ADDTIMER);
611       else if (m_hook.IsEpgHook())
612         return item.IsEPG();
613       else if (m_hook.IsChannelHook())
614         return item.IsPVRChannel();
615       else if (m_hook.IsDeletedRecordingHook())
616         return item.IsDeletedPVRRecording();
617       else if (m_hook.IsRecordingHook())
618         return item.IsUsablePVRRecording();
619       else if (m_hook.IsTimerHook())
620         return item.IsPVRTimer();
621       else
622         return false;
623     }
624 
Execute(const CFileItemPtr & item) const625     bool PVRClientMenuHook::Execute(const CFileItemPtr& item) const
626     {
627       const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item);
628       if (!client)
629         return false;
630 
631       if (item->IsEPG())
632         return client->CallEpgTagMenuHook(m_hook, item->GetEPGInfoTag()) == PVR_ERROR_NO_ERROR;
633       else if (item->IsPVRChannel())
634         return client->CallChannelMenuHook(m_hook, item->GetPVRChannelInfoTag()) == PVR_ERROR_NO_ERROR;
635       else if (item->IsDeletedPVRRecording())
636         return client->CallRecordingMenuHook(m_hook, item->GetPVRRecordingInfoTag(), true) == PVR_ERROR_NO_ERROR;
637       else if (item->IsUsablePVRRecording())
638         return client->CallRecordingMenuHook(m_hook, item->GetPVRRecordingInfoTag(), false) == PVR_ERROR_NO_ERROR;
639       else if (item->IsPVRTimer())
640         return client->CallTimerMenuHook(m_hook, item->GetPVRTimerInfoTag()) == PVR_ERROR_NO_ERROR;
641       else
642         return false;
643     }
644 
645   } // namespace CONEXTMENUITEM
646 
GetInstance()647   CPVRContextMenuManager& CPVRContextMenuManager::GetInstance()
648   {
649     static CPVRContextMenuManager instance;
650     return instance;
651   }
652 
CPVRContextMenuManager()653   CPVRContextMenuManager::CPVRContextMenuManager()
654   {
655     m_items = {
656         std::make_shared<CONTEXTMENUITEM::PlayEpgTag>(19190), /* Play programme */
657         std::make_shared<CONTEXTMENUITEM::PlayRecording>(19687), /* Play recording */
658         std::make_shared<CONTEXTMENUITEM::ShowInformation>(),
659         std::make_shared<CONTEXTMENUITEM::ShowChannelGuide>(19686), /* Channel guide */
660         std::make_shared<CONTEXTMENUITEM::FindSimilar>(19003), /* Find similar */
661         std::make_shared<CONTEXTMENUITEM::ToggleTimerState>(),
662         std::make_shared<CONTEXTMENUITEM::AddTimerRule>(19061), /* Add timer */
663         std::make_shared<CONTEXTMENUITEM::EditTimerRule>(),
664         std::make_shared<CONTEXTMENUITEM::DeleteTimerRule>(19295), /* Delete timer rule */
665         std::make_shared<CONTEXTMENUITEM::EditTimer>(),
666         std::make_shared<CONTEXTMENUITEM::DeleteTimer>(),
667         std::make_shared<CONTEXTMENUITEM::StartRecording>(264), /* Record */
668         std::make_shared<CONTEXTMENUITEM::StopRecording>(19059), /* Stop recording */
669         std::make_shared<CONTEXTMENUITEM::EditRecording>(21450), /* Edit */
670         std::make_shared<CONTEXTMENUITEM::DeleteRecording>(),
671         std::make_shared<CONTEXTMENUITEM::UndeleteRecording>(19290), /* Undelete */
672         std::make_shared<CONTEXTMENUITEM::DeleteWatchedRecordings>(19327), /* Delete watched */
673         std::make_shared<CONTEXTMENUITEM::AddReminder>(826), /* Set reminder */
674     };
675   }
676 
AddMenuHook(const CPVRClientMenuHook & hook)677   void CPVRContextMenuManager::AddMenuHook(const CPVRClientMenuHook& hook)
678   {
679     if (hook.IsSettingsHook())
680       return; // settings hooks are not handled using context menus
681 
682     const auto item = std::make_shared<CONTEXTMENUITEM::PVRClientMenuHook>(hook);
683     m_items.emplace_back(item);
684     m_events.Publish(PVRContextMenuEvent(PVRContextMenuEventAction::ADD_ITEM, item));
685   }
686 
RemoveMenuHook(const CPVRClientMenuHook & hook)687   void CPVRContextMenuManager::RemoveMenuHook(const CPVRClientMenuHook& hook)
688   {
689     if (hook.IsSettingsHook())
690       return; // settings hooks are not handled using context menus
691 
692     for (auto it = m_items.begin(); it < m_items.end(); ++it)
693     {
694       const CONTEXTMENUITEM::PVRClientMenuHook* cmh = dynamic_cast<const CONTEXTMENUITEM::PVRClientMenuHook*>((*it).get());
695       if (cmh && cmh->GetHook() == hook)
696       {
697         m_events.Publish(PVRContextMenuEvent(PVRContextMenuEventAction::REMOVE_ITEM, *it));
698         m_items.erase(it);
699         return;
700       }
701     }
702   }
703 
704 } // namespace PVR
705