vdr 2.8.1
menu.c
Go to the documentation of this file.
1/*
2 * menu.c: The actual menu implementations
3 *
4 * See the main source file 'vdr.c' for copyright information and
5 * how to reach the author.
6 *
7 * $Id: menu.c 5.53 2026/03/18 19:48:26 kls Exp $
8 */
9
10#include "menu.h"
11#include <ctype.h>
12#include <limits.h>
13#include <math.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include "channels.h"
18#include "config.h"
19#include "cutter.h"
20#include "eitscan.h"
21#include "i18n.h"
22#include "interface.h"
23#include "plugin.h"
24#include "recording.h"
25#include "remote.h"
26#include "shutdown.h"
27#include "sourceparams.h"
28#include "sources.h"
29#include "status.h"
30#include "svdrp.h"
31#include "themes.h"
32#include "timers.h"
33#include "transfer.h"
34#include "videodir.h"
35
36#define MAXWAIT4EPGINFO 3 // seconds
37#define MODETIMEOUT 3 // seconds
38#define NEWTIMERLIMIT 120 // seconds until the start time of a new timer created from the Schedule menu,
39 // within which it will go directly into the "Edit timer" menu to allow
40 // further parameter settings
41#define DEFERTIMER 60 // seconds by which a timer is deferred in case of problems
42
43#define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS)
44#define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours
45#define MAXWAITFORCAMMENU 10 // seconds to wait for the CAM menu to open
46#define CAMMENURETRYTIMEOUT 3 // seconds after which opening the CAM menu is retried
47#define CAMRESPONSETIMEOUT 5 // seconds to wait for a response from a CAM
48#define PROGRESSTIMEOUT 100 // milliseconds to wait before updating the replay progress display
49#define MINFREEDISK 300 // minimum free disk space (in MB) required to start recording
50#define NODISKSPACEDELTA 300 // seconds between "Not enough disk space to start recording!" messages
51#define MAXCHNAMWIDTH 16 // maximum number of characters of channels' short names shown in schedules menus
52
53#define CHNUMWIDTH (numdigits(cChannels::MaxNumber()) + 1)
54#define CHNAMWIDTH (min(MAXCHNAMWIDTH, cChannels::MaxShortChannelNameLength() + 1))
55
56// --- cMenuEditCaItem -------------------------------------------------------
57
59protected:
60 virtual void Set(void) override;
61public:
62 cMenuEditCaItem(const char *Name, int *Value);
64 };
65
66cMenuEditCaItem::cMenuEditCaItem(const char *Name, int *Value)
67:cMenuEditIntItem(Name, Value, 0)
68{
69 Set();
70}
71
73{
74 if (*value == CA_FTA)
75 SetValue(tr("Free To Air"));
76 else if (*value >= CA_ENCRYPTED_MIN)
77 SetValue(tr("encrypted"));
78 else
80}
81
83{
85
86 if (state == osUnknown) {
87 if (NORMALKEY(Key) == kLeft && *value >= CA_ENCRYPTED_MIN)
88 *value = CA_FTA;
89 else
91 Set();
93 }
94 return state;
95}
96
97// --- cMenuEditSrcItem ------------------------------------------------------
98
100private:
102protected:
103 virtual void Set(void) override;
104public:
105 cMenuEditSrcItem(const char *Name, int *Value);
107 };
108
109cMenuEditSrcItem::cMenuEditSrcItem(const char *Name, int *Value)
110:cMenuEditIntItem(Name, Value, 0)
111{
112 source = Sources.Get(*Value);
113 Set();
114}
115
117{
118 if (source)
119 SetValue(cString::sprintf("%s - %s", *cSource::ToString(source->Code()), source->Description()));
120 else
122}
123
125{
127
128 if (state == osUnknown) {
129 bool IsRepeat = Key & k_Repeat;
130 Key = NORMALKEY(Key);
131 if (Key == kLeft) { // TODO might want to increase the delta if repeated quickly?
132 if (source) {
133 if (source->Prev())
134 source = (cSource *)source->Prev();
135 else if (!IsRepeat)
136 source = Sources.Last();
137 *value = source->Code();
138 }
139 }
140 else if (Key == kRight) {
141 if (source) {
142 if (source->Next())
143 source = (cSource *)source->Next();
144 else if (!IsRepeat)
145 source = Sources.First();
146 }
147 else
148 source = Sources.First();
149 if (source)
150 *value = source->Code();
151 }
152 else
153 return state; // we don't call cMenuEditIntItem::ProcessKey(Key) here since we don't accept numerical input
154 Set();
156 }
157 return state;
158}
159
160// --- cMenuEditChannel ------------------------------------------------------
161
163private:
168 char name[256];
169 void Setup(void);
170public:
171 cMenuEditChannel(cStateKey *ChannelsStateKey, cChannel *Channel, bool New = false);
172 cChannel *Channel(void) { return channel; }
173 virtual eOSState ProcessKey(eKeys Key) override;
174 };
175
177:cOsdMenu(tr("Edit channel"), 16)
178{
180 channelsStateKey = ChannelsStateKey;
182 sourceParam = NULL;
183 *name = 0;
184 if (channel) {
185 data = *channel;
186 strn0cpy(name, data.name, sizeof(name));
187 if (New) {
188 channel = NULL;
189 // clear non-editable members:
190 data.nid = 0;
191 data.tid = 0;
192 data.rid = 0;
193 *data.shortName = 0;
194 *data.provider = 0;
195 *data.portalName = 0;
196 }
197 }
198 Setup();
199}
200
202{
203 int current = Current();
204
205 Clear();
206
207 // Parameters for all types of sources:
208 Add(new cMenuEditStrItem( tr("Name"), name, sizeof(name)));
209 Add(new cMenuEditSrcItem( tr("Source"), &data.source));
210 Add(new cMenuEditIntItem( tr("Frequency"), &data.frequency));
211 Add(new cMenuEditIntItem( tr("Vpid"), &data.vpid, 0, 0x1FFF));
212 Add(new cMenuEditIntItem( tr("Ppid"), &data.ppid, 0, 0x1FFF));
213 Add(new cMenuEditIntItem( tr("Apid1"), &data.apids[0], 0, 0x1FFF));
214 Add(new cMenuEditIntItem( tr("Apid2"), &data.apids[1], 0, 0x1FFF));
215 Add(new cMenuEditIntItem( tr("Dpid1"), &data.dpids[0], 0, 0x1FFF));
216 Add(new cMenuEditIntItem( tr("Dpid2"), &data.dpids[1], 0, 0x1FFF));
217 Add(new cMenuEditIntItem( tr("Spid1"), &data.spids[0], 0, 0x1FFF));
218 Add(new cMenuEditIntItem( tr("Spid2"), &data.spids[1], 0, 0x1FFF));
219 Add(new cMenuEditIntItem( tr("Tpid"), &data.tpid, 0, 0x1FFF));
220 Add(new cMenuEditCaItem( tr("CA"), &data.caids[0]));
221 Add(new cMenuEditIntItem( tr("Sid"), &data.sid, 1, 0xFFFF));
222 Add(new cMenuEditIntItem( tr("Nid"), &data.nid, 0));
223 Add(new cMenuEditIntItem( tr("Tid"), &data.tid, 0));
224 /* XXX not yet used
225 Add(new cMenuEditIntItem( tr("Rid"), &data.rid, 0));
226 XXX*/
227 // Parameters for specific types of sources:
229 if (sourceParam) {
230 sourceParam->SetData(&data);
231 cOsdItem *Item;
232 while ((Item = sourceParam->GetOsdItem()) != NULL)
233 Add(Item);
234 }
235
237 Display();
238}
239
241{
242 int oldSource = data.source;
243 eOSState state = cOsdMenu::ProcessKey(Key);
244
245 if (state == osUnknown) {
246 if (Key == kOk) {
248 bool Modified = false;
249 if (sourceParam)
250 sourceParam->GetData(&data);
251 if (Channels->HasUniqueChannelID(&data, channel)) {
252 data.name = strcpyrealloc(data.name, name);
253 if (channel) {
254 *channel = data;
255 isyslog("edited channel %d %s", channel->Number(), *channel->ToText());
256 state = osBack;
257 }
258 else {
259 channel = new cChannel;
260 *channel = data;
261 Channels->Add(channel);
262 Channels->ReNumber();
263 isyslog("added channel %d %s", channel->Number(), *channel->ToText());
264 state = osUser1;
265 }
266 Channels->SetModifiedByUser();
267 Modified = true;
268 }
269 else {
270 Skins.QueueMessage(mtError, tr("Channel settings are not unique!"));
271 state = osContinue;
272 }
273 channelsStateKey->Remove(Modified);
274 }
275 }
276 if (Key != kNone && (data.source & cSource::st_Mask) != (oldSource & cSource::st_Mask)) {
278 if (sourceParam)
279 sourceParam->GetData(&data);
280 Setup();
281 }
282 return state;
283}
284
285// --- cMenuChannelItem ------------------------------------------------------
286
288public:
290private:
293public:
297 static eChannelSortMode SortMode(void) { return sortMode; }
298 virtual int Compare(const cListObject &ListObject) const override;
299 virtual void Set(void) override;
300 const cChannel *Channel(void) { return channel; }
301 virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) override;
302 };
303
305
307{
309 if (channel->GroupSep())
310 SetSelectable(false);
311 Set();
312}
313
314int cMenuChannelItem::Compare(const cListObject &ListObject) const
315{
316 cMenuChannelItem *p = (cMenuChannelItem *)&ListObject;
317 int r = -1;
318 if (sortMode == csmProvider)
319 r = strcoll(channel->Provider(), p->channel->Provider());
320 if (sortMode == csmName || r == 0)
321 r = strcoll(channel->Name(), p->channel->Name());
322 if (sortMode == csmNumber || r == 0)
323 r = channel->Number() - p->channel->Number();
324 return r;
325}
326
328{
329 cString buffer;
330 if (!channel->GroupSep()) {
331 const char *X = *channel->Caids() >= CA_ENCRYPTED_MIN ? "X" : "";
332 const char *R = !channel->Vpid() && (*channel->Apids() || *channel->Dpids()) ? "R" : "";
333 if (sortMode == csmProvider)
334 buffer = cString::sprintf("%d\t%s%s\t%s - %s", channel->Number(), X, R, channel->Provider(), channel->Name());
335 else
336 buffer = cString::sprintf("%d\t%s%s\t%s", channel->Number(), X, R, channel->Name());
337 }
338 else
339 buffer = cString::sprintf("\t\t%s", channel->Name());
340 SetText(buffer);
341}
342
343void cMenuChannelItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
344{
346 if (!DisplayMenu->SetItemChannel(channel, Index, Current, Selectable, sortMode == csmProvider))
347 DisplayMenu->SetItem(Text(), Index, Current, Selectable);
348}
349
350// --- cMenuChannels ---------------------------------------------------------
351
352#define CHANNELNUMBERTIMEOUT 1000 //ms
353
354class cMenuChannels : public cOsdMenu {
355private:
359 void Set(bool Force = false);
360 cChannel *GetChannel(int Index);
361 void Propagate(cChannels *Channels);
362protected:
363 eOSState Number(eKeys Key);
364 eOSState Switch(void);
365 eOSState Edit(void);
366 eOSState New(void);
367 eOSState Delete(void);
368 virtual void Move(int From, int To) override;
369public:
370 cMenuChannels(void);
372 virtual eOSState ProcessKey(eKeys Key) override;
373 };
374
376:cOsdMenu(tr("Channels"), CHNUMWIDTH, 3)
377{
379 number = 0;
380 Set();
381}
382
386
387void cMenuChannels::Set(bool Force)
388{
389 if (Force)
390 channelsStateKey.Reset();
392 const cChannel *CurrentChannel = GetChannel(Current());
393 if (!CurrentChannel)
394 CurrentChannel = Channels->GetByNumber(cDevice::CurrentChannel());
395 cMenuChannelItem *CurrentItem = NULL;
396 Clear();
397 for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) {
398 if (!Channel->GroupSep() || cMenuChannelItem::SortMode() == cMenuChannelItem::csmNumber && *Channel->Name()) {
399 cMenuChannelItem *Item = new cMenuChannelItem(Channel);
400 Add(Item);
401 if (Channel == CurrentChannel)
402 CurrentItem = Item;
403 }
404 }
407 msmNumber);
409 Sort();
410 SetCurrent(CurrentItem);
411 SetHelp(tr("Button$Edit"), tr("Button$New"), tr("Button$Delete"), tr("Button$Mark"));
412 Display();
413 channelsStateKey.Remove();
414 }
415}
416
418{
420 return p ? (cChannel *)p->Channel() : NULL;
421}
422
424{
425 Channels->ReNumber();
426 for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
427 ci->Set();
428 Display();
429 Channels->SetModifiedByUser();
430}
431
433{
434 if (HasSubMenu())
435 return osContinue;
436 if (numberTimer.TimedOut())
437 number = 0;
438 if (!number && Key == k0) {
440 Set(true);
441 }
442 else {
444 number = number * 10 + Key - k0;
445 for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) {
446 if (!ci->Channel()->GroupSep() && ci->Channel()->Number() == number) {
447 DisplayCurrent(false);
448 SetCurrent(ci);
449 DisplayCurrent(true);
450 break;
451 }
452 }
454 }
455 return osContinue;
456}
457
459{
460 if (HasSubMenu())
461 return osContinue;
463 cChannel *ch = GetChannel(Current());
464 if (ch)
466 return osEnd;
467}
468
470{
471 if (HasSubMenu() || Count() == 0)
472 return osContinue;
474 cChannel *ch = GetChannel(Current());
475 if (ch)
477 return osContinue;
478}
479
487
489{
490 if (!HasSubMenu() && Count() > 0) {
491 LOCK_TIMERS_READ; // must lock timers before channels!
493 int Index = Current();
494 cChannel *Channel = GetChannel(Current());
495 if (!Channels->Contains(Channel)) {
496 channelsStateKey.Remove(false);
497 channelsStateKey.Reset(); // makes sure the menu is refreshed
498 return osContinue;
499 }
500 bool Deleted = false;
501 int CurrentChannelNr = cDevice::CurrentChannel();
502 cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
503 int DeletedChannel = Channel->Number();
504 // Check if there is a timer using this channel:
505 if (Timers->UsesChannel(Channel)) {
506 channelsStateKey.Remove(false);
507 Skins.QueueMessage(mtError, tr("Channel is being used by a timer!"));
508 return osContinue;
509 }
510 if (Interface->Confirm(tr("Delete channel?"))) {
511 if (CurrentChannel && Channel == CurrentChannel) {
512 int n = Channels->GetNextNormal(CurrentChannel->Index());
513 if (n < 0)
514 n = Channels->GetPrevNormal(CurrentChannel->Index());
515 CurrentChannel = Channels->Get(n);
516 CurrentChannelNr = 0; // triggers channel switch below
517 }
518 Channels->Del(Channel);
519 cOsdMenu::Del(Index);
520 Propagate(Channels);
521 isyslog("channel %d deleted", DeletedChannel);
522 Deleted = true;
523 if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
524 if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
525 Channels->SwitchTo(CurrentChannel->Number());
526 else
527 cDevice::SetCurrentChannel(CurrentChannel->Number());
528 }
529 }
530 channelsStateKey.Remove(Deleted);
531 }
532 return osContinue;
533}
534
535void cMenuChannels::Move(int From, int To)
536{
538 int CurrentChannelNr = cDevice::CurrentChannel();
539 cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
540 cChannel *FromChannel = GetChannel(From);
541 cChannel *ToChannel = GetChannel(To);
542 if (FromChannel && ToChannel) {
543 int FromNumber = FromChannel->Number();
544 int ToNumber = ToChannel->Number();
545 if (Channels->MoveNeedsDecrement(FromChannel, ToChannel)) {
546 ToChannel = Channels->Prev(ToChannel); // cListBase::Move() doesn't know about the channel list's numbered groups!
547 To--;
548 }
549 Channels->Move(FromChannel, ToChannel);
550 cOsdMenu::Move(From, To);
551 SetCurrent(Get(To));
552 Propagate(Channels);
553 isyslog("channel %d moved to %d", FromNumber, ToNumber);
554 if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
555 if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
556 Channels->SwitchTo(CurrentChannel->Number());
557 else
558 cDevice::SetCurrentChannel(CurrentChannel->Number());
559 }
560 }
561 channelsStateKey.Remove();
562 }
563}
564
566{
567 if (!HasSubMenu())
568 Set(); // react on any changes to the channels list
569 eOSState state = cOsdMenu::ProcessKey(Key);
570
571 switch (state) {
572 case osUser1: {
573 if (cMenuEditChannel *MenuEditChannel = dynamic_cast<cMenuEditChannel *>(SubMenu())) {
574 if (cChannel *Channel = MenuEditChannel->Channel()) {
576 Add(new cMenuChannelItem(Channel), true);
577 return CloseSubMenu();
578 }
579 }
580 }
581 break;
582 default:
583 if (state == osUnknown) {
584 switch (int(Key)) {
585 case k0 ... k9:
586 return Number(Key);
587 case kOk: return Switch();
588 case kRed: return Edit();
589 case kGreen: return New();
590 case kYellow: return Delete();
591 case kBlue: if (!HasSubMenu())
592 Mark();
593 break;
594 case kChanUp|k_Repeat:
595 case kChanUp:
596 case kChanDn|k_Repeat:
597 case kChanDn: {
599 int CurrentChannelNr = cDevice::CurrentChannel();
600 for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) {
601 if (!ci->Channel()->GroupSep() && ci->Channel()->Number() == CurrentChannelNr) {
602 SetCurrent(ci);
603 Display();
604 break;
605 }
606 }
607 }
608 default: break;
609 }
610 }
611 }
612 return state;
613}
614
615// --- cMenuText -------------------------------------------------------------
616
617cMenuText::cMenuText(const char *Title, const char *Text, eDvbFont Font)
619{
621 text = NULL;
622 font = Font;
623 SetText(Text);
624}
625
627{
628 free(text);
629}
630
631void cMenuText::SetText(const char *Text)
632{
633 free(text);
634 text = Text ? strdup(Text) : NULL;
635}
636
638{
640 DisplayMenu()->SetText(text, font == fontFix); //XXX define control character in text to choose the font???
641 if (text)
643}
644
646{
647 switch (int(Key)) {
648 case kUp|k_Repeat:
649 case kUp:
650 case kDown|k_Repeat:
651 case kDown:
652 case kLeft|k_Repeat:
653 case kLeft:
654 case kRight|k_Repeat:
655 case kRight:
656 DisplayMenu()->Scroll(NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft, NORMALKEY(Key) == kLeft || NORMALKEY(Key) == kRight);
657 cStatus::MsgOsdTextItem(NULL, NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft);
658 return osContinue;
659 default: break;
660 }
661
662 eOSState state = cOsdMenu::ProcessKey(Key);
663
664 if (state == osUnknown) {
665 switch (Key) {
666 case kOk: return osBack;
667 default: state = osContinue;
668 }
669 }
670 return state;
671}
672
673// --- cMenuFolderItem -------------------------------------------------------
674
675class cMenuFolderItem : public cOsdItem {
676private:
678public:
679 virtual void Set(void) override;
681 cNestedItem *Folder(void) { return folder; }
682 };
683
690
692{
693 if (folder->SubItems() && folder->SubItems()->Count())
694 SetText(cString::sprintf("%s...", folder->Text()));
695 else
696 SetText(folder->Text());
697}
698
699// --- cMenuEditFolder -------------------------------------------------------
700
701class cMenuEditFolder : public cOsdMenu {
702private:
705 char name[PATH_MAX];
706 eOSState Confirm(void);
707public:
708 cMenuEditFolder(const char *Dir, cList<cNestedItem> *List, cNestedItem *Folder = NULL);
709 cString GetFolder(void);
710 virtual eOSState ProcessKey(eKeys Key) override;
711 };
712
714:cOsdMenu(Folder ? tr("Edit folder") : tr("New folder"), 12)
715{
717 list = List;
718 folder = Folder;
719 if (folder)
720 strn0cpy(name, folder->Text(), sizeof(name));
721 else {
722 *name = 0;
723 cRemote::Put(kRight, true); // go right into string editing mode
724 }
725 if (!isempty(Dir)) {
726 cOsdItem *DirItem = new cOsdItem(Dir);
727 DirItem->SetSelectable(false);
728 Add(DirItem);
729 }
730 Add(new cMenuEditStrItem( tr("Name"), name, sizeof(name)));
731}
732
734{
735 return folder ? folder->Text() : "";
736}
737
739{
740 if (!folder || strcmp(folder->Text(), name) != 0) {
741 // each name may occur only once in a folder list
742 for (cNestedItem *Folder = list->First(); Folder; Folder = list->Next(Folder)) {
743 if (strcmp(Folder->Text(), name) == 0) {
744 Skins.QueueMessage(mtError, tr("Folder name already exists!"));
745 return osContinue;
746 }
747 }
748 char *p = strpbrk(name, "\\{}#~"); // FOLDERDELIMCHAR
749 if (p) {
750 Skins.QueueMessage(mtError, cString::sprintf(tr("Folder name must not contain '%c'!"), *p));
751 return osContinue;
752 }
753 }
754 if (folder)
755 folder->SetText(name);
756 else
757 list->Add(folder = new cNestedItem(name));
758 return osEnd;
759}
760
762{
763 eOSState state = cOsdMenu::ProcessKey(Key);
764
765 if (state == osUnknown) {
766 switch (Key) {
767 case kOk: return Confirm();
768 case kRed:
769 case kGreen:
770 case kYellow:
771 case kBlue: return osContinue;
772 default: break;
773 }
774 }
775 return state;
776}
777
778// --- cMenuFolder -----------------------------------------------------------
779
780cMenuFolder::cMenuFolder(const char *Title, cNestedItemList *NestedItemList, const char *Path)
782{
784 list = nestedItemList = NestedItemList;
785 firstFolder = NULL;
786 editing = false;
787 helpKeys = -1;
788 Set();
789 DescendPath(Path);
790 Display();
791 SetHelpKeys();
792}
793
794cMenuFolder::cMenuFolder(const char *Title, cList<cNestedItem> *List, cNestedItemList *NestedItemList, const char *Dir, const char *Path)
796{
798 list = List;
799 nestedItemList = NestedItemList;
800 dir = Dir;
801 firstFolder = NULL;
802 editing = false;
803 helpKeys = -1;
804 Set();
805 DescendPath(Path);
806 Display();
807 SetHelpKeys();
808}
809
811{
812 if (HasSubMenu())
813 return;
814 int NewHelpKeys = 0;
815 if (firstFolder)
816 NewHelpKeys = 1;
817 if (NewHelpKeys != helpKeys) {
818 helpKeys = NewHelpKeys;
819 SetHelp(NewHelpKeys > 0 ? tr("Button$Open") : NULL, tr("Button$New"), firstFolder ? tr("Button$Delete") : NULL, firstFolder ? tr("Button$Edit") : NULL);
820 }
821}
822
823#define FOLDERDELIMCHARSUBST 0x01
824static void AddRecordingFolders(const cRecordings *Recordings, cList<cNestedItem> *List, char *Path)
825{
826 if (Path) {
827 char *p = strchr(Path, FOLDERDELIMCHARSUBST);
828 if (p)
829 *p++ = 0;
830 cNestedItem *Folder;
831 for (Folder = List->First(); Folder; Folder = List->Next(Folder)) {
832 if (strcmp(Path, Folder->Text()) == 0)
833 break;
834 }
835 if (!Folder)
836 List->Add(Folder = new cNestedItem(Path));
837 if (p) {
838 Folder->SetSubItems(true);
839 AddRecordingFolders(Recordings, Folder->SubItems(), p);
840 }
841 }
842 else {
843 cStringList Dirs;
844 for (const cRecording *Recording = Recordings->First(); Recording; Recording = Recordings->Next(Recording)) {
845 cString Folder = Recording->Folder();
846 strreplace((char *)*Folder, FOLDERDELIMCHAR, FOLDERDELIMCHARSUBST); // makes sure parent folders come before subfolders
847 if (Dirs.Find(Folder) < 0)
848 Dirs.Append(strdup(Folder));
849 }
850 Dirs.Sort();
851 for (int i = 0; i < Dirs.Size(); i++) {
852 if (char *s = Dirs[i])
853 AddRecordingFolders(Recordings, &Folders, s);
854 }
855 }
856}
857
858void cMenuFolder::Set(const char *CurrentFolder)
859{
860 static cStateKey RecordingsStateKey;
861 if (list == &Folders) {
862 if (const cRecordings *Recordings = cRecordings::GetRecordingsRead(RecordingsStateKey)) {
863 AddRecordingFolders(Recordings, &Folders, NULL);
864 RecordingsStateKey.Remove();
865 }
866 }
867 firstFolder = NULL;
868 Clear();
869 if (!isempty(dir)) {
870 cOsdItem *DirItem = new cOsdItem(dir);
871 DirItem->SetSelectable(false);
872 Add(DirItem);
873 }
874 list->Sort();
875 for (cNestedItem *Folder = list->First(); Folder; Folder = list->Next(Folder)) {
876 cOsdItem *FolderItem = new cMenuFolderItem(Folder);
877 Add(FolderItem, CurrentFolder ? strcmp(Folder->Text(), CurrentFolder) == 0 : false);
878 if (!firstFolder)
879 firstFolder = FolderItem;
880 }
881}
882
883void cMenuFolder::DescendPath(const char *Path)
884{
885 if (Path) {
886 const char *p = strchr(Path, FOLDERDELIMCHAR);
887 if (p) {
888 for (cMenuFolderItem *Folder = (cMenuFolderItem *)firstFolder; Folder; Folder = (cMenuFolderItem *)Next(Folder)) {
889 if (strncmp(Folder->Folder()->Text(), Path, p - Path) == 0) {
890 SetCurrent(Folder);
891 if (Folder->Folder()->SubItems() && strchr(p + 1, FOLDERDELIMCHAR))
892 AddSubMenu(new cMenuFolder(Title(), Folder->Folder()->SubItems(), nestedItemList, !isempty(dir) ? *cString::sprintf("%s%c%s", *dir, FOLDERDELIMCHAR, Folder->Folder()->Text()) : Folder->Folder()->Text(), p + 1));
893 break;
894 }
895 }
896 }
897 }
898}
899
901{
902 if (firstFolder) {
904 if (Folder) {
905 if (Open) {
906 Folder->Folder()->SetSubItems(true);
907 return AddSubMenu(new cMenuFolder(Title(), Folder->Folder()->SubItems(), nestedItemList, !isempty(dir) ? *cString::sprintf("%s%c%s", *dir, FOLDERDELIMCHAR, Folder->Folder()->Text()) : Folder->Folder()->Text()));
908 }
909 else
910 return osEnd;
911 }
912 }
913 return osContinue;
914}
915
917{
918 editing = true;
919 return AddSubMenu(new cMenuEditFolder(dir, list));
920}
921
923{
924 if (!HasSubMenu() && firstFolder) {
926 if (Folder && Interface->Confirm(Folder->Folder()->SubItems() ? tr("Delete folder and all sub folders?") : tr("Delete folder?"))) {
927 list->Del(Folder->Folder());
928 Del(Folder->Index());
929 firstFolder = Get(isempty(dir) ? 0 : 1);
930 Display();
931 SetHelpKeys();
932 nestedItemList->Save();
933 }
934 }
935 return osContinue;
936}
937
939{
940 if (!HasSubMenu() && firstFolder) {
942 if (Folder) {
943 editing = true;
944 return AddSubMenu(new cMenuEditFolder(dir, list, Folder->Folder()));
945 }
946 }
947 return osContinue;
948}
949
951{
952 if (cMenuEditFolder *mef = dynamic_cast<cMenuEditFolder *>(SubMenu())) {
953 Set(mef->GetFolder());
954 SetHelpKeys();
955 Display();
956 nestedItemList->Save();
957 }
958 return CloseSubMenu();
959}
960
962{
963 if (firstFolder) {
965 if (Folder) {
966 if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu()))
967 return cString::sprintf("%s%c%s", Folder->Folder()->Text(), FOLDERDELIMCHAR, *mf->GetFolder());
968 return Folder->Folder()->Text();
969 }
970 }
971 return "";
972}
973
975{
976 if (!HasSubMenu())
977 editing = false;
978 eOSState state = cOsdMenu::ProcessKey(Key);
979
980 if (state == osUnknown) {
981 switch (Key) {
982 case kOk: return Select(false);
983 case kRed: return Select(true);
984 case kGreen: return New();
985 case kYellow: return Delete();
986 case kBlue: return Edit();
987 default: state = osContinue;
988 }
989 }
990 else if (state == osEnd && HasSubMenu() && editing)
991 state = SetFolder();
992 SetHelpKeys();
993 return state;
994}
995
996// --- cMenuEditTimer --------------------------------------------------------
997
998static const char *TimerFileMacrosForPattern[] = {
1004 "",
1005 NULL
1006 };
1007
1008static const char *TimerFileMacros[] = {
1011 "",
1012 NULL
1013 };
1014
1016
1018:cOsdMenu(tr("Edit timer"), 12)
1019{
1021 addedTimer = NULL;
1022 pattern = NULL;
1023 file = NULL;
1024 day = firstday = NULL;
1025 timer = Timer;
1026 addIfConfirmed = New;
1027 if (timer) {
1028 data = *timer;
1029 if (New)
1030 data.SetFlags(tfActive);
1031 channel = data.Channel()->Number();
1032 Add(new cMenuEditBitItem( tr("Active"), &data.flags, tfActive));
1033 Add(new cMenuEditChanItem(tr("Channel"), &channel));
1034 Add(day = new cMenuEditDateItem(tr("Day"), &data.day, &data.weekdays));
1035 Add(new cMenuEditTimeItem(tr("Start"), &data.start));
1036 Add(new cMenuEditTimeItem(tr("Stop"), &data.stop));
1037 Add(new cMenuEditBitItem( tr("VPS"), &data.flags, tfVps));
1038 Add(new cMenuEditIntItem( tr("Priority"), &data.priority, 0, MAXPRIORITY));
1039 Add(new cMenuEditIntItem( tr("Lifetime"), &data.lifetime, 0, MAXLIFETIME));
1040 Add(file = new cMenuEditStrItem( tr("File"), data.file, sizeof(data.file)));
1042 SetPatternItem(true);
1043 if (data.remote)
1044 strn0cpy(remote, data.remote, sizeof(remote));
1045 else
1046 *remote = 0;
1048 svdrpServerNames.Sort(true);
1049 svdrpServerNames.Insert(strdup(""));
1050 Add(new cMenuEditStrlItem(tr("Record on"), remote, sizeof(remote), &svdrpServerNames));
1051 }
1052 }
1053 SetHelpKeys();
1054}
1055
1057{
1058 if (timer && addIfConfirmed)
1059 delete timer; // apparently it wasn't confirmed
1060}
1061
1063{
1064 const cTimer *Timer = addedTimer;
1065 addedTimer = NULL;
1066 return Timer;
1067}
1068
1070{
1071 SetHelp(tr("Button$Folder"), data.weekdays ? tr("Button$Single") : tr("Button$Repeating"), *data.pattern ? tr("Button$Regular") : tr("Button$Pattern"));
1072}
1073
1075{
1076 if (!firstday && !data.IsSingleEvent()) {
1077 Add(firstday = new cMenuEditDateItem(tr("First day"), &data.day));
1078 Display();
1079 }
1080 else if (firstday && data.IsSingleEvent()) {
1081 Del(firstday->Index());
1082 firstday = NULL;
1083 Display();
1084 }
1085}
1086
1088{
1089 if (Initial && !*data.pattern) {
1090 file->SetMacros(TimerFileMacros);
1091 return;
1092 }
1093 if (!pattern) {
1094 if (data.HasFlags(tfRecording)) {
1095 Skins.QueueMessage(mtWarning, tr("Timer is recording!"));
1096 return;
1097 }
1098 if (!*data.pattern) {
1099 char *p = strgetlast(data.file, FOLDERDELIMCHAR);
1100 strn0cpy(data.pattern, p, sizeof(data.pattern));
1101 }
1102 Ins(pattern = new cMenuEditStrItem( tr("Pattern"), data.pattern, sizeof(data.pattern)), true, file);
1103 pattern->SetKeepSpace();
1104 file->SetMacros(TimerFileMacrosForPattern);
1105 Display();
1106 }
1107 else {
1108 Del(pattern->Index());
1109 pattern = NULL;
1110 *data.pattern = 0;
1111 file->SetMacros(TimerFileMacros);
1112 Display();
1113 }
1114 SetHelpKeys();
1115}
1116
1118{
1119 if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu())) {
1120 cString Folder = mf->GetFolder();
1121 char *p = strgetlast(data.file, FOLDERDELIMCHAR);
1122 if (!isempty(*Folder))
1123 strn0cpy(data.file, cString::sprintf("%s%c%s", *Folder, FOLDERDELIMCHAR, p), sizeof(data.file));
1124 else if (p != data.file)
1125 memmove(data.file, p, strlen(p) + 1);
1127 Display();
1128 }
1129 return CloseSubMenu();
1130}
1131
1132static bool RemoteTimerError(const cTimer *Timer)
1133{
1134 Skins.QueueMessage(mtError, cString::sprintf("%s %d@%s!", tr("Error while accessing remote timer"), Timer->Id(), Timer->Remote()));
1135 return false; // convenience return code
1136}
1137
1138static bool HandleRemoteModifications(cTimer *NewTimer, cTimer *OldTimer = NULL)
1139{
1140 cString ErrorMessage;
1141 if (!HandleRemoteTimerModifications(NewTimer, OldTimer, &ErrorMessage)) {
1142 Skins.QueueMessage(mtError, ErrorMessage);
1143 return false;
1144 }
1145 return true;
1146}
1147
1149{
1150 eOSState state = cOsdMenu::ProcessKey(Key);
1151
1152 if (state == osUnknown) {
1153 switch (Key) {
1154 case kOk: if (timer) {
1156 if (!addIfConfirmed && !Timers->Contains(timer)) {
1157 if (cTimer *t = Timers->GetById(timer->Id(), timer->Remote()))
1158 timer = t;
1159 else {
1160 Skins.QueueMessage(mtWarning, tr("Timer has been deleted!"));
1161 break;
1162 }
1163 }
1165 if (const cChannel *Channel = Channels->GetByNumber(channel))
1166 data.channel = Channel;
1167 else {
1168 Skins.QueueMessage(mtError, tr("*** Invalid Channel ***"));
1169 break;
1170 }
1171 if (!*data.file)
1172 strcpy(data.file, data.Channel()->ShortName(true));
1173 data.SetRemote(*remote ? remote : NULL);
1174 if (addIfConfirmed) {
1175 *timer = data;
1176 Timers->Add(timer);
1177 addedTimer = timer;
1179 // must add the timer before HandleRemoteModifications to get proper log messages with timer ids
1180 Timers->Del(timer, false);
1181 addedTimer = NULL;
1182 return osContinue;
1183 }
1184 }
1185 else {
1187 return osContinue;
1188 if (timer->Local() && timer->Recording() && data.Remote())
1190 if (timer->Remote() && data.Remote())
1191 Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
1192 if (data.Local() && !timer->IsPatternTimer() && data.IsPatternTimer())
1193 data.SetEvent(NULL);
1194 *timer = data;
1195 }
1196 timer->TriggerRespawn();
1198 timer->SetEventFromSchedule(Schedules);
1199 timer->Matches();
1200 addIfConfirmed = false;
1201 }
1202 return osBack;
1203 case kRed: return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, data.file));
1204 case kGreen: if (day) {
1205 day->ToggleRepeating();
1206 SetCurrent(day);
1208 SetHelpKeys();
1209 Display();
1210 }
1211 return osContinue;
1212 case kYellow: SetPatternItem();
1213 return osContinue;
1214 case kBlue: return osContinue;
1215 default: break;
1216 }
1217 }
1218 else if (state == osEnd && HasSubMenu())
1219 state = SetFolder();
1220 if (Key != kNone)
1222 return state;
1223}
1224
1225// --- cMenuTimerItem --------------------------------------------------------
1226
1227class cMenuTimerItem : public cOsdItem {
1228private:
1230public:
1231 cMenuTimerItem(const cTimer *Timer);
1232 virtual int Compare(const cListObject &ListObject) const override;
1233 virtual void Set(void) override;
1234 const cTimer *Timer(void) { return timer; }
1235 virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) override;
1236 };
1237
1239{
1240 timer = Timer;
1241 Set();
1242}
1243
1244int cMenuTimerItem::Compare(const cListObject &ListObject) const
1245{
1246 return timer->Compare(*((cMenuTimerItem *)&ListObject)->timer);
1247}
1248
1250{
1251 cString day, name("");
1252 if (timer->WeekDays())
1253 day = timer->PrintDay(0, timer->WeekDays(), false);
1254 else if (timer->Day() - time(NULL) < 28 * SECSINDAY) {
1255 day = itoa(timer->GetMDay(timer->Day()));
1256 name = WeekDayName(timer->Day());
1257 }
1258 else {
1259 struct tm tm_r;
1260 time_t Day = timer->Day();
1261 localtime_r(&Day, &tm_r);
1262 char buffer[16];
1263 strftime(buffer, sizeof(buffer), "%Y%m%d", &tm_r);
1264 day = buffer;
1265 }
1266 const char *File = timer->Pattern();
1267 if (!*File) {
1269 if (timer->HasFlags(tfSpawned) && timer->Event() && timer->Event()->Title())
1270 File = timer->Event()->Title();
1271 else {
1272 File = Setup.FoldersInTimerMenu ? NULL : strrchr(timer->File(), FOLDERDELIMCHAR);
1273 if (File && strcmp(File + 1, TIMERMACRO_TITLE) && strcmp(File + 1, TIMERMACRO_EPISODE))
1274 File++;
1275 else
1276 File = timer->File();
1277 }
1278 }
1279 SetText(cString::sprintf("%c\t%d\t%s%s%s\t%02d:%02d\t%02d:%02d\t%s%s%s%s",
1280 !(timer->HasFlags(tfActive)) ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
1281 timer->Channel()->Number(),
1282 *name,
1283 *name && **name ? " " : "",
1284 *day,
1285 timer->Start() / 100,
1286 timer->Start() % 100,
1287 timer->Stop() / 100,
1288 timer->Stop() % 100,
1289 timer->Remote() ? *cString::sprintf("@%s: ", timer->Remote()) : "",
1290 timer->IsPatternTimer() ? "{" : "",
1291 File,
1292 timer->IsPatternTimer() ? "}" : ""));
1293}
1294
1295void cMenuTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
1296{
1298 if (!DisplayMenu->SetItemTimer(timer, Index, Current, Selectable))
1299 DisplayMenu->SetItem(Text(), Index, Current, Selectable);
1300}
1301
1302// --- cMenuTimers -----------------------------------------------------------
1303
1304class cMenuTimers : public cOsdMenu {
1305private:
1309 void Set(void);
1310 eOSState Edit(void);
1311 eOSState New(void);
1312 eOSState Delete(void);
1313 eOSState OnOff(void);
1314 eOSState Info(void);
1315 cTimer *GetTimer(void);
1316 void SetHelpKeys(void);
1317public:
1318 cMenuTimers(void);
1319 virtual ~cMenuTimers() override;
1320 virtual eOSState ProcessKey(eKeys Key) override;
1321 };
1322
1324:cOsdMenu(tr("Timers"), 2, CHNUMWIDTH, 10, 6, 6)
1325{
1327 helpKeys = -1;
1328 showAllTimers = true;
1329 cMenuEditTimer::AddedTimer(); // to clear any leftovers
1330 Set();
1331}
1332
1336
1338{
1339 if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) {
1340 const cTimer *CurrentTimer = GetTimer();
1341 cMenuTimerItem *CurrentItem = NULL;
1342 Clear();
1343 {
1345 for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
1346 cMenuTimerItem *Item = new cMenuTimerItem(Timer);
1347 if (showAllTimers || Timer->HasFlags(tfActive)) {
1348 Add(Item);
1349 if (CurrentTimer && Timer->Id() == CurrentTimer->Id() && (!Timer->Remote() && !CurrentTimer->Remote() || Timer->Remote() && CurrentTimer->Remote() && strcmp(Timer->Remote(), CurrentTimer->Remote()) == 0))
1350 CurrentItem = Item;
1351 }
1352 }
1353 }
1354 Sort();
1355 SetCurrent(CurrentItem ? CurrentItem : First());
1356 SetHelpKeys();
1357 Display();
1358 timersStateKey.Remove();
1359 }
1360}
1361
1363{
1365 return item ? (cTimer *)item->Timer() : NULL;
1366}
1367
1369{
1370 int NewHelpKeys = 0;
1371 if (const cTimer *Timer = GetTimer()) {
1372 if (Timer->Event())
1373 NewHelpKeys = 2;
1374 else
1375 NewHelpKeys = 1;
1376 }
1377 if (NewHelpKeys != helpKeys) {
1378 helpKeys = NewHelpKeys;
1379 SetHelp(helpKeys > 0 ? tr("Button$On/Off") : NULL, tr("Button$New"), helpKeys > 0 ? tr("Button$Delete") : NULL, helpKeys == 2 ? tr("Button$Info") : NULL);
1380 }
1381}
1382
1384{
1385 if (HasSubMenu())
1386 return osContinue;
1387 cStateKey StateKey;
1388 cTimers *Timers = cTimers::GetTimersWrite(StateKey);
1389 cTimer *Timer = GetTimer();
1390 if (Timer) {
1391 Timer->OnOff();
1392 if (Timer->Remote()) {
1394 cStringList Response;
1395 if (!ExecSVDRPCommand(Timer->Remote(), cString::sprintf("MODT %d %s", Timer->Id(), *Timer->ToText(true)), &Response) || SVDRPCode(Response[0]) != 250)
1396 RemoteTimerError(Timer);
1397 }
1398 {
1400 Timer->SetEventFromSchedule(Schedules);
1401 }
1403 DisplayCurrent(true);
1404 if (Timer->FirstDay())
1405 isyslog("set first day of timer %s to %s", *Timer->ToDescr(), *Timer->PrintFirstDay());
1406 else
1407 isyslog("%sactivated timer %s", Timer->HasFlags(tfActive) ? "" : "de", *Timer->ToDescr());
1408 }
1409 StateKey.Remove(Timer != NULL);
1410 return osContinue;
1411}
1412
1414{
1415 if (HasSubMenu() || Count() == 0)
1416 return osContinue;
1417 return AddSubMenu(new cMenuEditTimer(GetTimer()));
1418}
1419
1421{
1422 if (HasSubMenu())
1423 return osContinue;
1424 cTimer *Timer = new cTimer;
1425 if (Setup.SVDRPPeering && *Setup.SVDRPDefaultHost)
1426 Timer->SetRemote(Setup.SVDRPDefaultHost);
1427 return AddSubMenu(new cMenuEditTimer(Timer, true));
1428}
1429
1431{
1433 // Check if this timer is active:
1434 cTimer *Timer = GetTimer();
1435 if (Timer) {
1436 bool TimerRecording = Timer->Recording();
1437 timersStateKey.Remove(false); // must release lock while prompting!
1438 if (Interface->Confirm(tr("Delete timer?")) && (!TimerRecording || Interface->Confirm(tr("Timer still recording - really delete?")))) {
1440 Timer = GetTimer();
1441 if (Timer) {
1442 if (!Timer->Remote()) {
1443 Timer->Skip();
1444 cRecordControls::Process(Timers, time(NULL));
1445 }
1446 if (HandleRemoteModifications(NULL, Timer)) {
1447 if (Timer->Remote())
1449 Timers->Del(Timer);
1451 Display();
1452 }
1453 }
1454 }
1455 else
1456 return osContinue;
1457 }
1458 timersStateKey.Remove(Timer != NULL);
1459 return osContinue;
1460}
1461
1463{
1464 if (HasSubMenu() || Count() == 0)
1465 return osContinue;
1469 cTimer *Timer = GetTimer();
1470 if (Timer && Timer->Event())
1471 return AddSubMenu(new cMenuEvent(Timers, Channels, Timer->Event()));
1472 return osContinue;
1473}
1474
1476{
1477 if (!HasSubMenu())
1478 Set();
1479 eOSState state = cOsdMenu::ProcessKey(Key);
1480 bool oldShowAllTimers = showAllTimers;
1481 if (state == osUnknown) {
1482 switch (Key) {
1483 case k0: showAllTimers = !showAllTimers; break;
1484 case kOk: return Edit();
1485 case kRed: state = OnOff(); break; // must go through SetHelpKeys()!
1486 case kGreen: return New();
1487 case kYellow: state = Delete(); break;
1488 case kInfo:
1489 case kBlue: return Info();
1490 break;
1491 default: break;
1492 }
1493 }
1494 if (const cTimer *Timer = cMenuEditTimer::AddedTimer()) {
1495 // a newly created timer was confirmed with Ok and the proper item needs to be added:
1497 cMenuTimerItem *CurrentItem = new cMenuTimerItem(Timer);
1498 Add(CurrentItem, true);
1499 Sort();
1500 SetCurrent(CurrentItem);
1501 SetHelpKeys();
1502 Display();
1503 }
1504 if (showAllTimers != oldShowAllTimers) {
1505 timersStateKey.Reset();
1506 Set();
1507 }
1508 if (Key != kNone)
1509 SetHelpKeys();
1510 return state;
1511}
1512
1513// --- cMenuEvent ------------------------------------------------------------
1514
1515cMenuEvent::cMenuEvent(const cTimers *Timers, const cChannels *Channels, const cEvent *Event, bool CanSwitch, bool Buttons)
1516:cOsdMenu(tr("Event"))
1517{
1519 event = Event;
1520 if (event) {
1521 if (const cChannel *Channel = Channels->GetByChannelID(event->ChannelID(), true)) {
1522 SetTitle(Channel->Name());
1523 if (Buttons) {
1524 eTimerMatch TimerMatch = tmNone;
1525 Timers->GetMatch(event, &TimerMatch);
1526 SetHelp(TimerMatch == tmFull ? tr("Button$Timer") : tr("Button$Record"), NULL, NULL, CanSwitch ? tr("Button$Switch") : NULL);
1527 }
1528 }
1529 }
1530}
1531
1533{
1537 if (event->Description())
1538 cStatus::MsgOsdTextItem(event->Description());
1539}
1540
1542{
1543 switch (int(Key)) {
1544 case kUp|k_Repeat:
1545 case kUp:
1546 case kDown|k_Repeat:
1547 case kDown:
1548 case kLeft|k_Repeat:
1549 case kLeft:
1550 case kRight|k_Repeat:
1551 case kRight:
1552 DisplayMenu()->Scroll(NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft, NORMALKEY(Key) == kLeft || NORMALKEY(Key) == kRight);
1553 cStatus::MsgOsdTextItem(NULL, NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft);
1554 return osContinue;
1555 case kInfo: return osBack;
1556 default: break;
1557 }
1558
1559 eOSState state = cOsdMenu::ProcessKey(Key);
1560
1561 if (state == osUnknown) {
1562 switch (Key) {
1563 case kGreen:
1564 case kYellow: return osContinue;
1565 case kOk: return osBack;
1566 default: break;
1567 }
1568 }
1569 return state;
1570}
1571
1572// --- cMenuScheduleItem -----------------------------------------------------
1573
1575public:
1576 enum eScheduleSortMode { ssmAllThis, ssmThisThis, ssmThisAll, ssmAllAll }; // "which event(s) on which channel(s)"
1577private:
1579public:
1586 cMenuScheduleItem(const cTimers *Timers, const cEvent *Event, const cChannel *Channel = NULL, bool WithDate = false);
1589 static eScheduleSortMode SortMode(void) { return sortMode; }
1590 virtual int Compare(const cListObject &ListObject) const override;
1591 bool Update(const cTimers *Timers, bool Force = false);
1592 virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) override;
1593 };
1594
1596
1597cMenuScheduleItem::cMenuScheduleItem(const cTimers *Timers, const cEvent *Event, const cChannel *Channel, bool WithDate)
1598{
1599 event = Event;
1600 channel = Channel;
1601 timer = NULL;
1602 withDate = WithDate;
1604 timerActive = false;
1605 Update(Timers, true);
1606}
1607
1608int cMenuScheduleItem::Compare(const cListObject &ListObject) const
1609{
1610 cMenuScheduleItem *p = (cMenuScheduleItem *)&ListObject;
1611 int r = -1;
1612 if (sortMode != ssmAllThis)
1613 r = strcoll(event->Title(), p->event->Title());
1614 if (sortMode == ssmAllThis || r == 0)
1615 r = event->StartTime() - p->event->StartTime();
1616 return r;
1617}
1618
1619static const char *TimerMatchChars = " tT iI";
1620
1621bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force)
1622{
1625 eTimerMatch OldTimerMatch = timerMatch;
1626 bool OldTimerActive = timerActive;
1627 if (event->Schedule() && event->Schedule()->HasTimer())
1628 timer = Timers->GetMatch(event, &timerMatch);
1629 if (event->EndTime() < time(NULL) && !event->IsRunning() && (!timer || !timer->Recording()))
1631 timerActive = timer && timer->HasFlags(tfActive);
1632 if (Force || timerMatch != OldTimerMatch || timerActive != OldTimerActive) {
1633 cString buffer;
1634 char t = TimerMatchChars[timerMatch + (timerActive ? 0 : 3)];
1635 char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' ';
1636 char r = event->SeenWithin(30) && event->IsRunning() ? '*' : ' ';
1637 const char *csn = channel ? channel->ShortName(true) : NULL;
1638 cString eds = event->GetDateString();
1639 if (channel && withDate)
1640 buffer = cString::sprintf("%d\t%.*s\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 999), csn, Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
1641 else if (channel)
1642 buffer = cString::sprintf("%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), Utf8SymChars(csn, 999), csn, *event->GetTimeString(), t, v, r, event->Title());
1643 else
1644 buffer = cString::sprintf("%.*s\t%s\t%c%c%c\t%s", Utf8SymChars(eds, 6), *eds, *event->GetTimeString(), t, v, r, event->Title());
1645 SetText(buffer);
1646 return true;
1647 }
1648 return false;
1649}
1650
1651void cMenuScheduleItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
1652{
1656 if (!DisplayMenu->SetItemEvent(event, Index, Current, Selectable, channel, withDate, timerMatch, timer))
1657 DisplayMenu->SetItem(Text(), Index, Current, Selectable);
1658}
1659
1660// --- cMenuWhatsOn ----------------------------------------------------------
1661
1662class cMenuWhatsOn : public cOsdMenu {
1663private:
1664 bool now;
1668 eOSState Record(void);
1669 eOSState Switch(void);
1670 static int currentChannel;
1671 static const cEvent *scheduleEvent;
1672 bool Update(void);
1673 void SetHelpKeys(const cChannels *Channels);
1674public:
1675 cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, const cSchedules *Schedules, bool Now, int CurrentChannelNr);
1676 static int CurrentChannel(void) { return currentChannel; }
1677 static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; }
1678 static const cEvent *ScheduleEvent(void);
1679 virtual eOSState ProcessKey(eKeys Key) override;
1680 };
1681
1684
1685cMenuWhatsOn::cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, const cSchedules *Schedules, bool Now, int CurrentChannelNr)
1686:cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, CHNAMWIDTH, 6, 4)
1687{
1689 now = Now;
1690 canSwitch = false;
1691 helpKeys = 0;
1692 for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) {
1693 if (!Channel->GroupSep()) {
1694 if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) {
1695 if (const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent())
1696 Add(new cMenuScheduleItem(Timers, Event, Channel), Channel->Number() == CurrentChannelNr);
1697 }
1698 }
1699 }
1700 currentChannel = CurrentChannelNr;
1701 SetHelpKeys(Channels);
1702}
1703
1705{
1706 bool result = false;
1707 if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) {
1708 for (cOsdItem *item = First(); item; item = Next(item)) {
1709 if (((cMenuScheduleItem *)item)->Update(Timers))
1710 result = true;
1711 }
1712 timersStateKey.Remove();
1713 }
1714 return result;
1715}
1716
1718{
1720 canSwitch = false;
1721 int NewHelpKeys = 0;
1722 if (item) {
1723 if (item->timerMatch == tmFull)
1724 NewHelpKeys |= 0x02; // "Timer"
1725 else
1726 NewHelpKeys |= 0x01; // "Record"
1727 if (now)
1728 NewHelpKeys |= 0x04; // "Next"
1729 else
1730 NewHelpKeys |= 0x08; // "Now"
1731 if (const cChannel *Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) {
1732 if (Channel->Number() != cDevice::CurrentChannel()) {
1733 NewHelpKeys |= 0x10; // "Switch"
1734 canSwitch = true;
1735 }
1736 }
1737 }
1738 if (NewHelpKeys != helpKeys) {
1739 const char *Red[] = { NULL, tr("Button$Record"), tr("Button$Timer") };
1740 SetHelp(Red[NewHelpKeys & 0x03], now ? tr("Button$Next") : tr("Button$Now"), tr("Button$Schedule"), canSwitch ? tr("Button$Switch") : NULL);
1741 helpKeys = NewHelpKeys;
1742 }
1743}
1744
1746{
1747 const cEvent *ei = scheduleEvent;
1748 scheduleEvent = NULL;
1749 return ei;
1750}
1751
1753{
1755 if (item) {
1757 const cChannel *Channel = Channels->GetByChannelID(item->event->ChannelID(), true);
1758 if (Channel) {
1759 if (!cDevice::PrimaryDevice()->SwitchChannel(Channel, true))
1760 Channel = NULL;
1761 }
1762 if (Channel)
1763 return osEnd;
1764 }
1765 Skins.QueueMessage(mtError, tr("Can't switch channel!"));
1766 return osContinue;
1767}
1768
1770{
1771 if (cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current())) {
1775 Timers->SetExplicitModify();
1776 if (item->timerMatch == tmFull) {
1777 if (cTimer *Timer = Timers->GetMatch(item->event))
1778 return AddSubMenu(new cMenuEditTimer(Timer));
1779 }
1780 cTimer *Timer = new cTimer(item->event);
1781 if (Setup.SVDRPPeering && *Setup.SVDRPDefaultHost)
1782 Timer->SetRemote(Setup.SVDRPDefaultHost);
1783 if (cTimer *t = Timers->GetTimer(Timer)) {
1784 delete Timer;
1785 Timer = t;
1786 return AddSubMenu(new cMenuEditTimer(Timer));
1787 }
1788 if (Timer->Matches(0, NEWTIMERLIMIT))
1789 return AddSubMenu(new cMenuEditTimer(Timer, true));
1790 Timers->Add(Timer);
1791 Timers->SetModified();
1792 if (!HandleRemoteModifications(Timer)) {
1793 // must add the timer before HandleRemoteModifications to get proper log messages with timer ids
1794 Timers->Del(Timer);
1795 }
1796 else if (Timer->Remote())
1797 Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
1798 if (HasSubMenu())
1799 CloseSubMenu();
1800 }
1801 if (Update())
1802 Display();
1804 SetHelpKeys(Channels);
1805 return osContinue;
1806}
1807
1809{
1810 bool HadSubMenu = HasSubMenu();
1811 eOSState state = cOsdMenu::ProcessKey(Key);
1812
1813 if (state == osUnknown) {
1814 switch (int(Key)) {
1815 case kRecord:
1816 case kRed: return Record();
1817 case kYellow: state = osBack;
1818 // continue with kGreen
1819 case kGreen: {
1821 if (mi) {
1823 scheduleEvent = mi->event;
1824 currentChannel = mi->channel->Number();
1825 }
1826 }
1827 break;
1828 case kBlue: if (canSwitch)
1829 return Switch();
1830 break;
1831 case kChanUp|k_Repeat:
1832 case kChanUp:
1833 case kChanDn|k_Repeat:
1834 case kChanDn: if (!HasSubMenu()) {
1837 for (cOsdItem *item = First(); item; item = Next(item)) {
1838 if (((cMenuScheduleItem *)item)->channel->Number() == cDevice::CurrentChannel()) {
1839 DisplayCurrent(false);
1840 SetCurrent(item);
1841 DisplayCurrent(true);
1842 SetHelpKeys(Channels);
1843 break;
1844 }
1845 }
1846 }
1847 break;
1848 case kInfo:
1849 case kOk: if (Count()) {
1852 return AddSubMenu(new cMenuEvent(Timers, Channels, ((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true));
1853 }
1854 break;
1855 default: break;
1856 }
1857 }
1858 else if (!HasSubMenu()) {
1859 if (HadSubMenu && Update())
1860 Display();
1861 if (Key != kNone) {
1863 SetHelpKeys(Channels);
1864 }
1865 }
1866 return state;
1867}
1868
1869// --- cMenuSchedule ---------------------------------------------------------
1870
1871class cMenuSchedule : public cOsdMenu {
1872private:
1876 bool now, next;
1879 void Set(const cTimers *Timers, const cChannels *Channels, const cChannel *Channel = NULL, bool Force = false);
1880 eOSState Number(void);
1881 eOSState Record(void);
1882 eOSState Switch(void);
1883 bool PrepareScheduleAllThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel);
1884 bool PrepareScheduleThisThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel);
1885 bool PrepareScheduleThisAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel);
1886 bool PrepareScheduleAllAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel);
1887 bool Update(void);
1888 void SetHelpKeys(void);
1889public:
1890 cMenuSchedule(void);
1891 virtual ~cMenuSchedule() override;
1892 virtual eOSState ProcessKey(eKeys Key) override;
1893 };
1894
1896:cOsdMenu(tr("Schedule"))
1897{
1899 scheduleState = -1;
1900 now = next = false;
1901 canSwitch = false;
1902 helpKeys = 0;
1907 Set(Timers, Channels, NULL, true);
1908}
1909
1911{
1912 cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared
1913}
1914
1915void cMenuSchedule::Set(const cTimers *Timers, const cChannels *Channels, const cChannel *Channel, bool Force)
1916{
1917 if (Force) {
1918 schedulesStateKey.Reset();
1919 scheduleState = -1;
1920 }
1922 cMenuScheduleItem *CurrentItem = (cMenuScheduleItem *)Get(Current());
1923 const cEvent *Event = NULL;
1924 if (!Channel) {
1925 if (CurrentItem) {
1926 Event = CurrentItem->event;
1927 Channel = Channels->GetByChannelID(Event->ChannelID(), true);
1928 }
1929 else
1930 Channel = Channels->GetByNumber(cDevice::CurrentChannel());
1931 }
1932 bool Refresh = false;
1933 switch (cMenuScheduleItem::SortMode()) {
1934 case cMenuScheduleItem::ssmAllThis: Refresh = PrepareScheduleAllThis(Timers, Schedules, Event, Channel); break;
1935 case cMenuScheduleItem::ssmThisThis: Refresh = PrepareScheduleThisThis(Timers, Schedules, Event, Channel); break;
1936 case cMenuScheduleItem::ssmThisAll: Refresh = Force && PrepareScheduleThisAll(Timers, Schedules, Event, Channel); break;
1937 case cMenuScheduleItem::ssmAllAll: Refresh = Force && PrepareScheduleAllAll(Timers, Schedules, Event, Channel); break;
1938 default: esyslog("ERROR: unknown SortMode %d (%s %d)", cMenuScheduleItem::SortMode(), __FUNCTION__, __LINE__);
1939 }
1940 if (Refresh) {
1941 CurrentItem = (cMenuScheduleItem *)Get(Current());
1942 Sort();
1943 SetCurrent(CurrentItem);
1944 SetHelpKeys();
1945 Display();
1946 }
1947 schedulesStateKey.Remove();
1948 }
1949}
1950
1951bool cMenuSchedule::PrepareScheduleAllThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel)
1952{
1953 if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) {
1954 if (Schedule->Modified(scheduleState)) {
1955 Clear();
1956 SetCols(7, 6, 4);
1957 SetTitle(cString::sprintf(tr("Schedule - %s"), Channel->Name()));
1958 const cEvent *PresentEvent = Event ? Event : Schedule->GetPresentEvent();
1959 time_t now = time(NULL) - Setup.EPGLinger * 60;
1960 for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) {
1961 if (ev->EndTime() > now || ev == PresentEvent)
1962 Add(new cMenuScheduleItem(Timers, ev), ev == PresentEvent);
1963 }
1964 return true;
1965 }
1966 }
1967 return false;
1968}
1969
1970bool cMenuSchedule::PrepareScheduleThisThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel)
1971{
1972 if (Event) {
1973 if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) {
1974 if (Schedule->Modified(scheduleState)) {
1975 Clear();
1976 SetCols(7, 6, 4);
1977 SetTitle(cString::sprintf(tr("This event - %s"), Channel->Name()));
1978 time_t now = time(NULL) - Setup.EPGLinger * 60;
1979 for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) {
1980 if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title()))
1981 Add(new cMenuScheduleItem(Timers, ev), ev == Event);
1982 }
1983 return true;
1984 }
1985 }
1986 }
1987 return false;
1988}
1989
1990bool cMenuSchedule::PrepareScheduleThisAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel)
1991{
1992 Clear();
1993 SetCols(CHNUMWIDTH, CHNAMWIDTH, 7, 6, 4);
1994 SetTitle(tr("This event - all channels"));
1995 if (Event) {
1997 for (const cChannel *ch = Channels->First(); ch; ch = Channels->Next(ch)) {
1998 if (const cSchedule *Schedule = Schedules->GetSchedule(ch)) {
1999 time_t now = time(NULL) - Setup.EPGLinger * 60;
2000 for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) {
2001 if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title()))
2002 Add(new cMenuScheduleItem(Timers, ev, ch, true), ev == Event && ch == Channel);
2003 }
2004 }
2005 }
2006 }
2007 return true;
2008}
2009
2010bool cMenuSchedule::PrepareScheduleAllAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel)
2011{
2012 Clear();
2013 SetCols(CHNUMWIDTH, CHNAMWIDTH, 7, 6, 4);
2014 SetTitle(tr("All events - all channels"));
2016 cStateKey StateKey;
2017 for (const cChannel *ch = Channels->First(); ch; ch = Channels->Next(ch)) {
2018 if (const cSchedule *Schedule = Schedules->GetSchedule(ch)) {
2019 time_t now = time(NULL) - Setup.EPGLinger * 60;
2020 for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) {
2021 if (ev->EndTime() > now || ev == Event)
2022 Add(new cMenuScheduleItem(Timers, ev, ch, true), ev == Event && ch == Channel);
2023 }
2024 }
2025 }
2026 return true;
2027}
2028
2030{
2031 bool result = false;
2032 if (const cTimers *Timers = cTimers::GetTimersRead(timersStateKey)) {
2033 for (cOsdItem *item = First(); item; item = Next(item)) {
2034 if (((cMenuScheduleItem *)item)->Update(Timers))
2035 result = true;
2036 }
2037 timersStateKey.Remove();
2038 }
2039 return result;
2040}
2041
2043{
2045 canSwitch = false;
2046 int NewHelpKeys = 0;
2047 if (item) {
2048 if (item->timerMatch == tmFull)
2049 NewHelpKeys |= 0x02; // "Timer"
2050 else
2051 NewHelpKeys |= 0x01; // "Record"
2053 if (const cChannel *Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) {
2054 if (Channel->Number() != cDevice::CurrentChannel()) {
2055 NewHelpKeys |= 0x10; // "Switch"
2056 canSwitch = true;
2057 }
2058 }
2059 }
2060 if (NewHelpKeys != helpKeys) {
2061 const char *Red[] = { NULL, tr("Button$Record"), tr("Button$Timer") };
2062 SetHelp(Red[NewHelpKeys & 0x03], tr("Button$Now"), tr("Button$Next"), canSwitch ? tr("Button$Switch") : NULL);
2063 helpKeys = NewHelpKeys;
2064 }
2065}
2066
2068{
2072 Set(Timers, Channels, NULL, true);
2073 return osContinue;
2074}
2075
2077{
2078 if (cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current())) {
2082 Timers->SetExplicitModify();
2083 if (item->timerMatch == tmFull) {
2084 if (cTimer *Timer = Timers->GetMatch(item->event))
2085 return AddSubMenu(new cMenuEditTimer(Timer));
2086 }
2087 cTimer *Timer = new cTimer(item->event);
2088 if (Setup.SVDRPPeering && *Setup.SVDRPDefaultHost)
2089 Timer->SetRemote(Setup.SVDRPDefaultHost);
2090 if (cTimer *t = Timers->GetTimer(Timer)) {
2091 delete Timer;
2092 Timer = t;
2093 return AddSubMenu(new cMenuEditTimer(Timer));
2094 }
2095 if (Timer->Matches(0, NEWTIMERLIMIT))
2096 return AddSubMenu(new cMenuEditTimer(Timer, true));
2097 Timers->Add(Timer);
2098 Timers->SetModified();
2099 if (!HandleRemoteModifications(Timer)) {
2100 // must add the timer before HandleRemoteModifications to get proper log messages with timer ids
2101 Timers->Del(Timer);
2102 }
2103 else if (Timer->Remote())
2104 Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
2105 if (HasSubMenu())
2106 CloseSubMenu();
2107 }
2108 if (Update())
2109 Display();
2110 SetHelpKeys();
2111 return osContinue;
2112}
2113
2115{
2117 if (item) {
2119 const cChannel *Channel = NULL;
2120 if ((Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) != NULL) {
2121 if (!Channels->SwitchTo(Channel->Number()))
2122 Channel = NULL;
2123 }
2124 if (Channel)
2125 return osEnd;
2126 }
2127 Skins.QueueMessage(mtError, tr("Can't switch channel!"));
2128 return osContinue;
2129}
2130
2132{
2133 if (!HasSubMenu()) {
2136 Set(Timers, Channels); // react on any changes to the schedules list
2137 }
2138 bool HadSubMenu = HasSubMenu();
2139 eOSState state = cOsdMenu::ProcessKey(Key);
2140
2141 if (state == osUnknown) {
2142 switch (int(Key)) {
2143 case k0: return Number();
2144 case kRecord:
2145 case kRed: return Record();
2146 case kGreen: {
2150 if (!now && !next) {
2151 int ChannelNr = 0;
2152 if (Count()) {
2153 if (const cChannel *Channel = Channels->GetByChannelID(((cMenuScheduleItem *)Get(Current()))->event->ChannelID(), true))
2154 ChannelNr = Channel->Number();
2155 }
2156 now = true;
2157 return AddSubMenu(new cMenuWhatsOn(Timers, Channels, Schedules, now, ChannelNr));
2158 }
2159 now = !now;
2160 next = !next;
2161 return AddSubMenu(new cMenuWhatsOn(Timers, Channels, Schedules, now, cMenuWhatsOn::CurrentChannel()));
2162 }
2163 case kYellow: {
2167 return AddSubMenu(new cMenuWhatsOn(Timers, Channels, Schedules, false, cMenuWhatsOn::CurrentChannel()));
2168 }
2169 case kBlue: if (canSwitch)
2170 return Switch();
2171 break;
2172 case kChanUp|k_Repeat:
2173 case kChanUp:
2174 case kChanDn|k_Repeat:
2175 case kChanDn: if (!HasSubMenu()) {
2178 if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel()))
2179 Set(Timers, Channels, Channel, true);
2180 }
2181 break;
2182 case kInfo:
2183 case kOk: if (Count()) {
2187 return AddSubMenu(new cMenuEvent(Timers, Channels, ((cMenuScheduleItem *)Get(Current()))->event, canSwitch, true));
2188 }
2189 break;
2190 default: break;
2191 }
2192 }
2193 else if (!HasSubMenu()) {
2194 now = next = false;
2195 if (const cEvent *ei = cMenuWhatsOn::ScheduleEvent()) {
2198 if (const cChannel *Channel = Channels->GetByChannelID(ei->ChannelID(), true)) {
2200 Set(Timers, Channels, Channel, true);
2201 }
2202 }
2203 else if (HadSubMenu && Update())
2204 Display();
2205 if (Key != kNone)
2206 SetHelpKeys();
2207 }
2208 return state;
2209}
2210
2211// --- cMenuCommands ---------------------------------------------------------
2212
2215{
2217 result = NULL;
2218 SetHasHotkeys();
2220 parameters = Parameters;
2221 for (cNestedItem *Command = commands->First(); Command; Command = commands->Next(Command)) {
2222 const char *s = Command->Text();
2223 if (Command->SubItems())
2224 Add(new cOsdItem(hk(cString::sprintf("%s...", s))));
2225 else if (Parse(s))
2226 Add(new cOsdItem(hk(title)));
2227 }
2228}
2229
2231{
2232 free(result);
2233}
2234
2235bool cMenuCommands::Parse(const char *s)
2236{
2237 const char *p = strchr(s, ':');
2238 if (p) {
2239 int l = p - s;
2240 if (l > 0) {
2241 char t[l + 1];
2242 stripspace(strn0cpy(t, s, l + 1));
2243 l = strlen(t);
2244 if (l > 1 && t[l - 1] == '?') {
2245 t[l - 1] = 0;
2246 confirm = true;
2247 }
2248 else
2249 confirm = false;
2250 title = t;
2251 command = skipspace(p + 1);
2252 return true;
2253 }
2254 }
2255 return false;
2256}
2257
2259{
2260 cNestedItem *Command = commands->Get(Current());
2261 if (Command) {
2262 if (Command->SubItems())
2263 return AddSubMenu(new cMenuCommands(Title(), Command->SubItems(), parameters));
2264 if (Parse(Command->Text())) {
2265 if (!confirm || Interface->Confirm(cString::sprintf("%s?", *title))) {
2266 Skins.Message(mtStatus, cString::sprintf("%s...", *title));
2267 free(result);
2268 result = NULL;
2269 cString cmdbuf;
2270 if (!isempty(parameters))
2271 cmdbuf = cString::sprintf("%s %s", *command, *parameters);
2272 const char *cmd = *cmdbuf ? *cmdbuf : *command;
2273 dsyslog("executing command '%s'", cmd);
2274 cPipe p;
2275 if (p.Open(cmd, "r")) {
2276 int l = 0;
2277 int c;
2278 while ((c = fgetc(p)) != EOF) {
2279 if (l % 20 == 0) {
2280 if (char *NewBuffer = (char *)realloc(result, l + 21))
2281 result = NewBuffer;
2282 else {
2283 esyslog("ERROR: out of memory");
2284 break;
2285 }
2286 }
2287 result[l++] = char(c);
2288 }
2289 if (result)
2290 result[l] = 0;
2291 p.Close();
2292 }
2293 else
2294 esyslog("ERROR: can't open pipe for command '%s'", cmd);
2295 Skins.Message(mtStatus, NULL);
2296 if (result)
2297 return AddSubMenu(new cMenuText(title, result, fontFix));
2298 return osEnd;
2299 }
2300 }
2301 }
2302 return osContinue;
2303}
2304
2306{
2307 eOSState state = cOsdMenu::ProcessKey(Key);
2308
2309 if (state == osUnknown) {
2310 switch (Key) {
2311 case kRed:
2312 case kGreen:
2313 case kYellow:
2314 case kBlue: return osContinue;
2315 case kOk: return Execute();
2316 default: break;
2317 }
2318 }
2319 return state;
2320}
2321
2322// --- cMenuCam --------------------------------------------------------------
2323
2324static bool CamMenuIsOpen = false;
2325
2326class cMenuCam : public cOsdMenu {
2327private:
2331 char *input;
2334 void GenerateTitle(const char *s = NULL);
2335 void QueryCam(void);
2336 void AddMultiLineItem(const char *s);
2337 void Set(void);
2338 eOSState Select(void);
2339public:
2340 cMenuCam(cCamSlot *CamSlot);
2341 virtual ~cMenuCam() override;
2342 virtual eOSState ProcessKey(eKeys Key) override;
2343 };
2344
2346:cOsdMenu("", 1) // tab necessary for enquiry!
2347{
2349 camSlot = CamSlot;
2350 ciMenu = NULL;
2351 ciEnquiry = NULL;
2352 input = NULL;
2353 offset = 0;
2354 lastCamExchange = time(NULL);
2356 QueryCam();
2357 CamMenuIsOpen = true;
2358}
2359
2361{
2362 if (ciMenu)
2363 ciMenu->Abort();
2364 delete ciMenu;
2365 if (ciEnquiry)
2366 ciEnquiry->Abort();
2367 delete ciEnquiry;
2368 free(input);
2369 CamMenuIsOpen = false;
2370}
2371
2372void cMenuCam::GenerateTitle(const char *s)
2373{
2374 SetTitle(cString::sprintf("CAM %d - %s", camSlot->SlotNumber(), (s && *s) ? s : camSlot->GetCamName()));
2375}
2376
2378{
2379 delete ciMenu;
2380 ciMenu = NULL;
2381 delete ciEnquiry;
2382 ciEnquiry = NULL;
2383 if (camSlot->HasUserIO()) {
2384 ciMenu = camSlot->GetMenu();
2385 ciEnquiry = camSlot->GetEnquiry();
2386 }
2387 Set();
2388}
2389
2391{
2392 if (ciMenu) {
2393 Clear();
2394 free(input);
2395 input = NULL;
2396 dsyslog("CAM %d: Menu ------------------", camSlot->SlotNumber());
2397 offset = 0;
2398 SetHasHotkeys(ciMenu->Selectable());
2399 GenerateTitle(ciMenu->TitleText());
2400 dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->TitleText());
2401 if (!isempty(ciMenu->SubTitleText())) {
2402 dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->SubTitleText());
2403 AddMultiLineItem(ciMenu->SubTitleText());
2404 offset = Count();
2405 }
2406 for (int i = 0; i < ciMenu->NumEntries(); i++) {
2407 Add(new cOsdItem(hk(ciMenu->Entry(i)), osUnknown, ciMenu->Selectable()));
2408 dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->Entry(i));
2409 }
2410 if (!isempty(ciMenu->BottomText())) {
2411 AddMultiLineItem(ciMenu->BottomText());
2412 dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciMenu->BottomText());
2413 }
2415 }
2416 else if (ciEnquiry) {
2417 Clear();
2418 int Length = ciEnquiry->ExpectedLength();
2419 free(input);
2420 input = MALLOC(char, Length + 1);
2421 *input = 0;
2422 dsyslog("CAM %d: Enquiry ------------------", camSlot->SlotNumber());
2423 GenerateTitle();
2424 Add(new cOsdItem(ciEnquiry->Text(), osUnknown, false));
2425 dsyslog("CAM %d: '%s'", camSlot->SlotNumber(), ciEnquiry->Text());
2426 Add(new cOsdItem("", osUnknown, false));
2427 Add(new cMenuEditNumItem("", input, Length, ciEnquiry->Blind()));
2428 }
2429 Display();
2430}
2431
2433{
2434 while (s && *s) {
2435 const char *p = strchr(s, '\n');
2436 int l = p ? p - s : strlen(s);
2437 cOsdItem *item = new cOsdItem;
2438 item->SetSelectable(false);
2439 item->SetText(strndup(s, l), false);
2440 Add(item);
2441 s = p ? p + 1 : p;
2442 }
2443}
2444
2446{
2447 if (ciMenu) {
2448 if (ciMenu->Selectable()) {
2449 ciMenu->Select(Current() - offset);
2450 dsyslog("CAM %d: select %d", camSlot->SlotNumber(), Current() - offset);
2451 }
2452 else
2453 ciMenu->Cancel();
2454 }
2455 else if (ciEnquiry) {
2456 if (ciEnquiry->ExpectedLength() < 0xFF && int(strlen(input)) != ciEnquiry->ExpectedLength()) {
2457 char buffer[64];
2458 snprintf(buffer, sizeof(buffer), tr("Please enter %d digits!"), ciEnquiry->ExpectedLength());
2459 Skins.QueueMessage(mtError, buffer);
2460 return osContinue;
2461 }
2462 ciEnquiry->Reply(input);
2463 dsyslog("CAM %d: entered '%s'", camSlot->SlotNumber(), ciEnquiry->Blind() ? "****" : input);
2464 }
2465 QueryCam();
2466 return osContinue;
2467}
2468
2470{
2471 if (!camSlot->HasMMI())
2472 return osBack;
2473
2474 eOSState state = cOsdMenu::ProcessKey(Key);
2475
2476 if (ciMenu || ciEnquiry) {
2477 lastCamExchange = time(NULL);
2478 if (state == osUnknown) {
2479 switch (Key) {
2480 case kOk: return Select();
2481 default: break;
2482 }
2483 }
2484 else if (state == osBack) {
2485 if (ciMenu)
2486 ciMenu->Cancel();
2487 if (ciEnquiry)
2488 ciEnquiry->Cancel();
2489 QueryCam();
2490 return osContinue;
2491 }
2492 if (ciMenu && ciMenu->HasUpdate()) {
2493 QueryCam();
2494 return osContinue;
2495 }
2496 }
2497 else if (time(NULL) - lastCamExchange < CAMRESPONSETIMEOUT)
2498 QueryCam();
2499 else {
2500 Skins.QueueMessage(mtError, tr("CAM not responding!"));
2501 return osBack;
2502 }
2503 return state;
2504}
2505
2506// --- CamControl ------------------------------------------------------------
2507
2509{
2510 for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
2511 if (CamSlot->HasUserIO())
2512 return new cMenuCam(CamSlot);
2513 }
2514 return NULL;
2515}
2516
2518{
2519 return CamMenuIsOpen;
2520}
2521
2522// --- cMenuPathEdit ---------------------------------------------------------
2523
2524#define osUserRecRenamed osUser1
2525#define osUserRecMoved osUser2
2526#define osUserRecRemoved osUser3
2527#define osUserRecEmpty osUser4
2528
2529class cMenuPathEdit : public cOsdMenu {
2530private:
2533 char folder[PATH_MAX];
2534 char name[NAME_MAX];
2537 eOSState SetFolder(void);
2538 eOSState Folder(void);
2539 eOSState ApplyChanges(void);
2540public:
2541 cMenuPathEdit(const char *Path);
2542 virtual eOSState ProcessKey(eKeys Key) override;
2543 };
2544
2546:cOsdMenu(tr("Edit path"), 12)
2547{
2549 path = Path;
2550 *folder = 0;
2551 *name = 0;
2552 const char *s = strrchr(path, FOLDERDELIMCHAR);
2553 if (s) {
2554 strn0cpy(folder, cString(path, s), sizeof(folder));
2555 s++;
2556 }
2557 else
2558 s = path;
2559 strn0cpy(name, s, sizeof(name));
2560 {
2562 pathIsInUse = Recordings->PathIsInUse(path);
2563 }
2564 oldFolder = folder;
2565 cOsdItem *p;
2566 Add(p = folderItem = new cMenuEditStrItem(tr("Folder"), folder, sizeof(folder)));
2568 Add(p = new cMenuEditStrItem(tr("Name"), name, sizeof(name)));
2570 if (*path) {
2571 int DirSize = 0;
2572 {
2574 for (const cRecording *Recording = Recordings->First(); Recording; Recording = Recordings->Next(Recording)) {
2575 if (Recording->IsInPath(path)) {
2576 int FileSizeMB = Recording->FileSizeMB();
2577 if (FileSizeMB > 0 )
2578 DirSize += FileSizeMB;
2579 }
2580 }
2581 }
2582 if (DirSize > 1023)
2583 Add(new cOsdItem(cString::sprintf("%s:\t%.2f GB", tr("Size"), DirSize / 1024.), osUnknown, false));
2584 else
2585 Add(new cOsdItem(cString::sprintf("%s:\t%d MB", tr("Size"), DirSize), osUnknown, false));
2586 }
2587 if (pathIsInUse) {
2588 Add(new cOsdItem("", osUnknown, false));
2589 Add(new cOsdItem(tr("This folder is currently in use - no changes are possible!"), osUnknown, false));
2590 }
2591 Display();
2592 if (!pathIsInUse)
2593 SetHelp(tr("Button$Folder"));
2594}
2595
2597{
2598 if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu())) {
2599 strn0cpy(folder, mf->GetFolder(), sizeof(folder));
2601 Display();
2602 }
2603 return CloseSubMenu();
2604}
2605
2607{
2608 return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, path));
2609}
2610
2612{
2613 if (!*name) {
2614 *name = ' '; // name must not be empty!
2615 name[1] = 0;
2616 }
2617 cString NewPath = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name;
2619 if (strcmp(NewPath, path)) {
2620 int NumRecordings = 0;
2621 {
2623 NumRecordings = Recordings->GetNumRecordingsInPath(path);
2624 }
2625 if (NumRecordings > 1 && !Interface->Confirm(cString::sprintf(tr("Move entire folder containing %d recordings?"), NumRecordings)))
2626 return osContinue;
2627 bool Error = false;
2628 {
2630 Recordings->SetExplicitModify();
2631 Error = !Recordings->MoveRecordings(path, NewPath);
2632 if (!Error)
2633 Recordings->SetModified();
2634 }
2635 if (Error) {
2636 Skins.QueueMessage(mtError, tr("Error while moving folder!"));
2637 return osContinue;
2638 }
2639 if (strcmp(folder, oldFolder))
2640 return osUserRecMoved;
2641 return osUserRecRenamed;
2642 }
2643 return osBack;
2644}
2645
2647{
2648 eOSState state = cOsdMenu::ProcessKey(Key);
2649 if (state == osUnknown) {
2650 if (!pathIsInUse) {
2651 switch (Key) {
2652 case kRed: return Folder();
2653 case kOk: return ApplyChanges();
2654 default: break;
2655 }
2656 }
2657 else if (Key == kOk)
2658 return osBack;
2659 }
2660 else if (state == osEnd && HasSubMenu())
2661 state = SetFolder();
2662 return state;
2663}
2664
2665// --- cMenuRecordingEdit ----------------------------------------------------
2666
2668private:
2672 char folder[PATH_MAX];
2673 char name[NAME_MAX];
2679 const char *buttonFolder;
2680 const char *buttonAction;
2682 const char *actionCancel;
2683 const char *doCut;
2685 void Set(void);
2686 void SetHelpKeys(void);
2687 bool RefreshRecording(void);
2688 eOSState SetFolder(void);
2689 eOSState Folder(void);
2690 eOSState Action(void);
2691 eOSState RemoveName(void);
2692 eOSState DeleteMarks(void);
2693 eOSState ApplyChanges(void);
2694public:
2695 cMenuRecordingEdit(const cRecording *Recording);
2696 virtual eOSState ProcessKey(eKeys Key) override;
2697 };
2698
2700:cOsdMenu(tr("Edit recording"), 15)
2701{
2703 recording = Recording;
2704 originalFileName = recording->FileName();
2705 strn0cpy(folder, recording->Folder(), sizeof(folder));
2706 strn0cpy(name, recording->BaseName(), sizeof(name));
2707 priority = recording->Priority();
2708 lifetime = recording->Lifetime();
2709 parentalRating = recording->Info()->ParentalRating();
2710 folderItem = NULL;
2711 nameItem = NULL;
2712 buttonFolder = NULL;
2713 buttonAction = NULL;
2714 buttonDeleteMarks = NULL;
2715 actionCancel = NULL;
2716 doCut = NULL;
2718 Set();
2719}
2720
2722{
2723 int current = Current();
2724 Clear();
2725 recordingIsInUse = recording->IsInUse();
2726 cOsdItem *p;
2727 Add(p = folderItem = new cMenuEditStrItem(tr("Folder"), folder, sizeof(folder)));
2729 Add(p = nameItem = new cMenuEditStrItem(tr("Name"), name, sizeof(name)));
2731 Add(p = new cMenuEditIntItem(tr("Priority"), &priority, 0, MAXPRIORITY));
2733 Add(p = new cMenuEditIntItem(tr("Lifetime"), &lifetime, 0, MAXLIFETIME));
2735 Add(p = new cMenuEditIntItem(tr("Parental rating"), &parentalRating, 0, MAXPARENTALRATING));
2737 if (recordingIsInUse) {
2738 Add(new cOsdItem("", osUnknown, false));
2739 Add(new cOsdItem(tr("This recording is currently in use - no changes are possible!"), osUnknown, false));
2740 }
2742 Display();
2743 SetHelpKeys();
2744}
2745
2747{
2748 buttonFolder = !recordingIsInUse ? tr("Button$Folder") : NULL;
2749 buttonAction = NULL;
2750 buttonDeleteMarks = NULL;
2751 actionCancel = NULL;
2752 doCut = NULL;
2753 if ((recordingIsInUse & ruCut) != 0)
2754 buttonAction = actionCancel = ((recordingIsInUse & ruPending) != 0) ? tr("Button$Cancel cutting") : tr("Button$Stop cutting");
2755 else if ((recordingIsInUse & ruMove) != 0)
2756 buttonAction = actionCancel = ((recordingIsInUse & ruPending) != 0) ? tr("Button$Cancel moving") : tr("Button$Stop moving");
2757 else if ((recordingIsInUse & ruCopy) != 0)
2758 buttonAction = actionCancel = ((recordingIsInUse & ruPending) != 0) ? tr("Button$Cancel copying") : tr("Button$Stop copying");
2759 else if (recording->HasMarks()) {
2760 buttonAction = doCut = tr("Button$Cut");
2761 buttonDeleteMarks = tr("Button$Delete marks");
2762 }
2764}
2765
2767{
2769 if ((recording = Recordings->GetByName(originalFileName)) != NULL)
2770 Set();
2771 else {
2772 recordingsStateKey.Remove();
2773 Skins.QueueMessage(mtWarning, tr("Recording vanished!"));
2774 return false;
2775 }
2776 recordingsStateKey.Remove();
2777 }
2778 return true;
2779}
2780
2782{
2783 if (cMenuFolder *mf = dynamic_cast<cMenuFolder *>(SubMenu())) {
2784 strn0cpy(folder, mf->GetFolder(), sizeof(folder));
2786 Display();
2787 }
2788 return CloseSubMenu();
2789}
2790
2792{
2793 return AddSubMenu(new cMenuFolder(tr("Select folder"), &Folders, recording->Name()));
2794}
2795
2797{
2798 if (actionCancel)
2799 RecordingsHandler.Del(recording->FileName());
2800 else if (doCut) {
2801 if (access(cCutter::EditedFileName(recording->FileName()), F_OK) != 0 || Interface->Confirm(tr("Edited version already exists - overwrite?"))) {
2802 if (!EnoughFreeDiskSpaceForEdit(recording->FileName()))
2803 Skins.QueueMessage(mtError, tr("Not enough free disk space to start editing process!"));
2804 else if (!RecordingsHandler.Add(ruCut, recording->FileName()))
2805 Skins.QueueMessage(mtError, tr("Error while queueing recording for cutting!"));
2806 }
2807 }
2808 recordingIsInUse = recording->IsInUse();
2810 SetHelpKeys();
2811 return osContinue;
2812}
2813
2815{
2816 if (Get(Current()) == nameItem) {
2817 if (Interface->Confirm(tr("Rename recording to folder name?"))) {
2818 char *s = strrchr(folder, FOLDERDELIMCHAR);
2819 if (s)
2820 *s++ = 0;
2821 else
2822 s = folder;
2823 strn0cpy(name, s, sizeof(name));
2824 if (s == folder)
2825 *s = 0;
2826 Set();
2827 }
2828 }
2829 return osContinue;
2830}
2831
2833{
2834 if (buttonDeleteMarks && Interface->Confirm(tr("Delete editing marks for this recording?"))) {
2836 SetHelpKeys();
2837 cMutexLock ControlMutexLock;
2838 if (cControl *Control = cControl::Control(ControlMutexLock, true)) {
2839 if (const cRecording *Recording = Control->GetRecording()) {
2840 if (strcmp(recording->FileName(), Recording->FileName()) == 0)
2841 Control->ClearEditingMarks();
2842 }
2843 }
2844 }
2845 else
2846 Skins.QueueMessage(mtError, tr("Error while deleting editing marks!"));
2847 }
2848 return osContinue;
2849}
2850
2852{
2853 cStateKey StateKey;
2854 cRecordings *Recordings = cRecordings::GetRecordingsWrite(StateKey);
2855 cRecording *Recording = Recordings->GetByName(recording->FileName());
2856 if (!Recording) {
2857 StateKey.Remove(false);
2858 Skins.QueueMessage(mtWarning, tr("Recording vanished!"));
2859 return osBack;
2860 }
2861 bool Modified = false;
2862 if (priority != recording->Priority() || lifetime != recording->Lifetime()) {
2863 if (!Recording->ChangePriorityLifetime(priority, lifetime)) {
2864 StateKey.Remove(Modified);
2865 Skins.QueueMessage(mtError, tr("Error while changing priority/lifetime!"));
2866 return osContinue;
2867 }
2868 Modified = true;
2869 }
2870 if (parentalRating != recording->Info()->ParentalRating()) {
2871 Recording->Info()->SetParentalRating(parentalRating);
2872 if (!Recording->WriteInfo()) {
2873 StateKey.Remove(Modified);
2874 Skins.QueueMessage(mtError, tr("Error while changing parental rating!"));
2875 return osContinue;
2876 }
2877 Modified = true;
2878 }
2879 if (!*name) {
2880 *name = ' '; // name must not be empty!
2881 name[1] = 0;
2882 }
2883 cString OldFolder = Recording->Folder();
2884 cString NewName = *folder ? cString::sprintf("%s%c%s", folder, FOLDERDELIMCHAR, name) : name;
2886 if (strcmp(NewName, Recording->Name())) {
2887 if (!Recording->ChangeName(NewName)) {
2888 StateKey.Remove(Modified);
2889 Skins.QueueMessage(mtError, tr("Error while changing folder/name!"));
2890 return osContinue;
2891 }
2892 Modified = true;
2893 }
2894 if (Modified) {
2895 eOSState state = osUserRecRenamed;
2896 if (strcmp(Recording->Folder(), OldFolder))
2897 state = osUserRecMoved;
2899 StateKey.Remove(Modified);
2900 return state;
2901 }
2902 StateKey.Remove(Modified);
2903 return osBack;
2904}
2905
2907{
2908 if (!HasSubMenu()) {
2909 if (!RefreshRecording())
2910 return osBack; // the recording has vanished, so close this menu
2911 }
2912 eOSState state = cOsdMenu::ProcessKey(Key);
2913 if (state == osUnknown) {
2914 switch (Key) {
2915 case k0: return RemoveName();
2916 case kRed: return buttonFolder ? Folder() : osContinue;
2917 case kGreen: return buttonAction ? Action() : osContinue;
2919 case kOk: return !recordingIsInUse ? ApplyChanges() : osBack;
2920 default: break;
2921 }
2922 }
2923 else if (state == osEnd && HasSubMenu())
2924 state = SetFolder();
2925 return state;
2926}
2927
2928// --- cMenuRecording --------------------------------------------------------
2929
2930class cMenuRecording : public cOsdMenu {
2931private:
2936 bool RefreshRecording(void);
2937public:
2938 cMenuRecording(const cRecording *Recording, bool WithButtons = false);
2939 virtual void Display(void) override;
2940 virtual eOSState ProcessKey(eKeys Key) override;
2941};
2942
2943cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons)
2944:cOsdMenu(tr("Recording info"))
2945{
2947 if (cRecordings::GetRecordingsRead(recordingsStateKey)) // initializes recordingsStateKey, so we don't call Display() unnecessarily
2948 recordingsStateKey.Remove();
2949 recording = Recording;
2950 originalFileName = recording->FileName();
2951 withButtons = WithButtons;
2952 if (withButtons) {
2953 if (recording->Deleted())
2954 SetHelp(tr("Button$Recordings"), tr("Button$Restore"), tr("Button$Permanently delete"));
2955 else
2956 SetHelp(tr("Button$Play"), tr("Button$Rewind"), NULL, tr("Button$Edit"));
2957 }
2958 if (recording->Deleted()) {
2959 int t = recording->Deleted() + Setup.DeleteRetention * SECSINDAY - time(NULL);
2960 cString buffer;
2961 // Note how '2 * ...' is used to avoid the plural/singular problem ;-).
2962 if (t > 2 * SECSINDAY) buffer = cString::sprintf(tr("Deleted recording (%d days left)"), int(t / SECSINDAY));
2963 else if (t > 2 * 3600) buffer = cString::sprintf(tr("Deleted recording (%d hours left)"), int(t / 3600));
2964 else if (t > 2 * 60) buffer = cString::sprintf(tr("Deleted recording (%d minutes left)"), int(t / 60));
2965 else buffer = cString::sprintf(tr("Deleted recording (expired)"));
2966 SetTitle(*buffer);
2967 }
2968}
2969
2971{
2973 if ((recording = Recordings->GetByName(originalFileName)) != NULL)
2974 Display();
2975 else {
2976 recordingsStateKey.Remove();
2977 Skins.QueueMessage(mtWarning, tr("Recording vanished!"));
2978 return false;
2979 }
2980 recordingsStateKey.Remove();
2981 }
2982 return true;
2983}
2984
2986{
2987 if (HasSubMenu()) {
2988 SubMenu()->Display();
2989 return;
2990 }
2993 if (recording->Info()->Description())
2994 cStatus::MsgOsdTextItem(recording->Info()->Description());
2995}
2996
2998{
2999 if (HasSubMenu())
3000 return cOsdMenu::ProcessKey(Key);
3001 else if (!RefreshRecording())
3002 return osBack; // the recording has vanished, so close this menu
3003 switch (int(Key)) {
3004 case kUp|k_Repeat:
3005 case kUp:
3006 case kDown|k_Repeat:
3007 case kDown:
3008 case kLeft|k_Repeat:
3009 case kLeft:
3010 case kRight|k_Repeat:
3011 case kRight:
3012 DisplayMenu()->Scroll(NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft, NORMALKEY(Key) == kLeft || NORMALKEY(Key) == kRight);
3013 cStatus::MsgOsdTextItem(NULL, NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft);
3014 return osContinue;
3015 case kInfo: return osBack;
3016 default: break;
3017 }
3018
3019 eOSState state = cOsdMenu::ProcessKey(Key);
3020
3021 if (state == osUnknown) {
3022 if (recording->Deleted()) {
3023 switch (Key) {
3024 case kRed:
3025 case kGreen:
3026 case kYellow: cRemote::Put(Key, true);
3027 // continue with osBack to close the info menu and process the key
3028 case kOk: return osBack;
3029 default: break;
3030 }
3031 }
3032 else if (withButtons) {
3033 switch (Key) {
3034 case kRed: Key = kOk; // will play the recording, even if recording commands are defined
3035 case kGreen: cRemote::Put(Key, true);
3036 // continue with osBack to close the info menu and process the key
3037 case kOk: return osBack;
3038 case kBlue: return AddSubMenu(new cMenuRecordingEdit(recording));
3039 default: break;
3040 }
3041 }
3042 }
3043 return state;
3044}
3045
3046// --- cMenuRecordingItem ----------------------------------------------------
3047
3049private:
3052 char *name;
3055public:
3058 void IncrementCounter(bool New);
3059 const char *Name(void) const { return name; }
3060 int Level(void) const { return level; }
3061 int Usage(void) { return usage; }
3062 const cRecording *Recording(void) const { return recording; }
3063 bool IsDirectory(void) const { return name != NULL; }
3065 void Update(cSkinDisplayMenu *DisplayMenu, int Index, bool Current);
3066 virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) override;
3067 };
3068
3070{
3072 level = Level;
3073 name = NULL;
3075 usage = ruNone;
3076 SetText(Recording->Title('\t', true, Level));
3077 if (*Text() == '\t') // this is a folder
3078 name = strdup(Text() + 2); // 'Text() + 2' to skip the two '\t'
3079 else { // this is an actual recording
3080 usage = Recording->IsInUse();
3081 if ((usage & ruDst) != 0 && (usage & (ruMove | ruCopy)) != 0)
3082 SetSelectable(false);
3083 }
3084}
3085
3090
3092{
3093 totalEntries++;
3094 if (New)
3095 newEntries++;
3097}
3098
3099void cMenuRecordingItem::Update(cSkinDisplayMenu *DisplayMenu, int Index, bool Current)
3100{
3101 SetText(recording->Title('\t', true, level));
3102 SetMenuItem(DisplayMenu, Index, Current, Selectable());
3103}
3104
3105void cMenuRecordingItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
3106{
3108 if (!DisplayMenu->SetItemRecording(recording, Index, Current, Selectable, level, totalEntries, newEntries))
3109 DisplayMenu->SetItem(Text(), Index, Current, Selectable);
3110}
3111
3112// --- cMenuRecordings -------------------------------------------------------
3113
3117
3118cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus, const cRecordingFilter *Filter, bool DelRecMenu)
3119:cOsdMenu(Base ? (DelRecMenu ? *cString::sprintf("%s %s", tr("Deleted in"), Base) : Base)
3120 : (DelRecMenu ? tr("Deleted recordings") : tr("Recordings")), 9, 6, 6)
3121{
3123 base = Base ? strdup(Base) : NULL;
3124 level = Setup.RecordingDirs ? Level : -1;
3125 filter = Filter;
3126 helpKeys = -1;
3127 delRecMenu = DelRecMenu;
3128 autoRefresh = false;
3129 if (Level == 0 && time(NULL) - toggleDelRec > 2)
3130 toggleDelRec = 0;
3131 Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay
3132 Set();
3133 if (Current() < 0)
3134 SetCurrent(First());
3135 else if (OpenSubMenus && (cReplayControl::LastReplayed() || (delRecMenu ? *deletedName : *fileName))) {
3136 if (Open(true))
3137 return;
3138 }
3139 SetHelpKeys();
3140}
3141
3143{
3145 if (!ri->IsDirectory()) {
3146 if (delRecMenu)
3147 SetDeleted(ri->Recording()->FileName());
3148 else
3149 SetRecording(ri->Recording()->FileName());
3150 }
3151 }
3152 free(base);
3153}
3154
3156{
3158 int NewHelpKeys = 0;
3159 if (ri) {
3160 if (ri->IsDirectory())
3161 NewHelpKeys = toggleDelRec ? 1 : 2;
3162 else if (delRecMenu)
3163 NewHelpKeys = 3;
3164 else if (toggleDelRec)
3165 NewHelpKeys = 4;
3166 else
3167 NewHelpKeys = 5;
3168 }
3169 else
3170 NewHelpKeys = 6;
3171 if (NewHelpKeys != helpKeys) {
3172 switch (NewHelpKeys) {
3173 case 0: SetHelp(NULL); break;
3174 case 1: SetHelp(delRecMenu ? tr("Button$Recordings") : tr("Button$Deleted recordings"), NULL, NULL, delRecMenu ? NULL : tr("Button$Edit")); break;
3175 case 2: SetHelp(delRecMenu ? tr("Button$Recordings") : tr("Button$Open"), NULL, NULL, delRecMenu ? NULL : tr("Button$Edit")); break;
3176 case 3: SetHelp(tr("Button$Recordings"), tr("Button$Restore"), tr("Button$Permanently delete"), tr("Button$Info")); break;
3177 case 4: SetHelp(tr("Button$Deleted recordings"), tr("Button$Rewind"), tr("Button$Delete"), tr("Button$Info")); break;
3178 case 5: SetHelp(RecordingCommands.Count() ? tr("Button$Commands") : tr("Button$Play"), tr("Button$Rewind"), tr("Button$Delete"), tr("Button$Info")); break;
3179 case 6: SetHelp(delRecMenu ? tr("Button$Recordings") : tr("Button$Deleted recordings")); break;
3180 default: ;
3181 }
3182 helpKeys = NewHelpKeys;
3183 }
3184}
3185
3186void cMenuRecordings::Set(bool Refresh)
3187{
3189 recordingsStateKey.Remove();
3191 const char *CurrentRecording = NULL;
3193 CurrentRecording = ri->Recording()->FileName();
3194 if (!CurrentRecording) {
3195 if (delRecMenu)
3196 CurrentRecording = *deletedName;
3197 else if (!Setup.OpenRecMenuAtLastReplayed)
3198 CurrentRecording = *fileName ? *fileName : cReplayControl::LastReplayed();
3199 else if (*fileName && strstr(fileName, DirectoryName()))
3200 CurrentRecording = *fileName;
3201 }
3202 autoRefresh = false;
3203 int current = Current();
3204 Clear();
3206 Recordings->Sort();
3207 cMenuRecordingItem *CurrentItem = NULL;
3208 cMenuRecordingItem *LastItem = NULL;
3209 time_t LastReplayTime = 0;
3210 for (const cRecording *Recording = Recordings->First(); Recording; Recording = Recordings->Next(Recording)) {
3211 if ((!filter || filter->Filter(Recording)) && (!base || (strstr(Recording->Name(), base) == Recording->Name() && Recording->Name()[strlen(base)] == FOLDERDELIMCHAR))) {
3212 cMenuRecordingItem *Item = new cMenuRecordingItem(Recording, level);
3213 cMenuRecordingItem *LastDir = NULL;
3214 if (Item->IsDirectory()) {
3215 // Sorting may ignore non-alphanumeric characters, so we need to explicitly handle directories in case they only differ in such characters:
3216 for (cMenuRecordingItem *p = LastItem; p; p = dynamic_cast<cMenuRecordingItem *>(p->Prev())) {
3217 if (p->Name() && strcmp(p->Name(), Item->Name()) == 0) {
3218 LastDir = p;
3219 break;
3220 }
3221 }
3222 }
3223 if (*Item->Text() && !LastDir) {
3224 autoRefresh |= Item->Usage();
3225 Add(Item);
3226 LastItem = Item;
3227 if (Item->IsDirectory())
3228 LastDir = Item;
3229 }
3230 else
3231 delete Item;
3232 if (LastItem || LastDir) {
3233 if (CurrentRecording) {
3234 if (strcmp(CurrentRecording, Recording->FileName()) == 0)
3235 CurrentItem = LastDir ? LastDir : LastItem;
3236 }
3237 else if (!delRecMenu && Setup.OpenRecMenuAtLastReplayed) {
3238 time_t t = Recording->GetLastReplayTime();
3239 if (t > LastReplayTime) {
3240 LastReplayTime = t;
3241 CurrentItem = LastDir ? LastDir : LastItem;
3242 }
3243 }
3244 }
3245 if (LastDir)
3246 LastDir->IncrementCounter(Recording->IsNew());
3247 }
3248 }
3249 SetCurrent(CurrentItem);
3250 if (Current() < 0)
3251 SetCurrent(Get(current)); // last resort, in case the recording was deleted
3253 recordingsStateKey.Remove(false); // sorting doesn't count as a real modification
3254 AdjustTitle(Count() == 0 ? osUserRecEmpty : osContinue, false);
3255 SetHelpKeys();
3256 if (Refresh)
3257 Display();
3258 }
3259 else if (autoRefresh) {
3260 int current = Current();
3261 for (cOsdItem *Item = First(); Item; Item = Next(Item)) {
3262 int Index = VisibleItem(Item->Index());
3263 if (Index >= 0) {
3265 if (!ri->IsDirectory() && ri->Usage())
3266 ri->Update(DisplayMenu(), Index, Item->Index() == current);
3267 }
3268 }
3269 }
3270}
3271
3272void cMenuRecordings::SetDeleted(const char *FileName)
3273{
3274 deletedName = FileName;
3275}
3276
3277void cMenuRecordings::SetRecording(const char *FileName)
3278{
3279 fileName = FileName;
3280}
3281
3283{
3284 if (base) {
3285 char *s = ExchangeChars(strdup(base), true);
3287 free(s);
3288 return d;
3289 }
3290 return cVideoDirectory::Name();
3291}
3292
3293bool cMenuRecordings::Open(bool OpenSubMenus)
3294{
3296 if (ri && ri->IsDirectory()) {
3297 const char *t = ri->Name();
3298 cString buffer;
3299 if (base) {
3300 buffer = cString::sprintf("%s%c%s", base, FOLDERDELIMCHAR, t);
3301 t = buffer;
3302 }
3303 AddSubMenu(new cMenuRecordings(t, level + 1, OpenSubMenus, filter, delRecMenu));
3304 return true;
3305 }
3306 return false;
3307}
3308
3310{
3311 if (!base) {
3312 if (State == osUserRecEmpty)
3313 SetTitle(delRecMenu ? tr("No deleted recordings") : tr("No recordings"));
3314 else
3315 SetTitle(delRecMenu ? tr("Deleted recordings") : tr("Recordings"));
3316 if (Redisplay)
3317 Display();
3318 }
3319 return State;
3320}
3321
3323{
3325 if (ri) {
3326 if (ri->IsDirectory())
3327 Open();
3328 else {
3330 return osReplay;
3331 }
3332 }
3333 return osContinue;
3334}
3335
3337{
3338 if (HasSubMenu() || Count() == 0)
3339 return osContinue;
3341 if (ri && !ri->IsDirectory()) {
3342 cDevice::PrimaryDevice()->StopReplay(); // must do this first to be able to rewind the currently replayed recording
3343 ri->Recording()->DeleteResume();
3344 return Play();
3345 }
3346 return osContinue;
3347}
3348
3349static bool TimerStillRecording(const char *FileName)
3350{
3352 // local timer
3353 if (Interface->Confirm(tr("Timer still recording - really delete?"))) {
3355 if (cTimer *Timer = rc->Timer()) {
3356 Timer->Skip();
3357 cRecordControls::Process(Timers, time(NULL));
3358 if (Timer->IsSingleEvent()) {
3359 Timers->Del(Timer);
3360 isyslog("deleted timer %s", *Timer->ToDescr());
3361 }
3362 }
3363 }
3364 else
3365 return true; // user didn't confirm deletion
3366 }
3367 else {
3368 // remote timer
3369 cString TimerId = GetRecordingTimerId(FileName);
3370 if (*TimerId) {
3371 int Id;
3372 char *RemoteBuf = NULL;
3373 cString Remote;
3374 if (2 == sscanf(TimerId, "%d@%m[^ \n]", &Id, &RemoteBuf) && Id != 0) {
3375 Remote = RemoteBuf;
3376 free(RemoteBuf);
3377 if (Interface->Confirm(tr("Timer still recording - really delete?"))) {
3379 if (cTimer *Timer = Timers->GetById(Id, Remote)) {
3380 cTimer OldTimer = *Timer;
3381 Timer->Skip();
3382 Timers->SetSyncStateKey(StateKeySVDRPRemoteTimersPoll);
3383 if (Timer->IsSingleEvent()) {
3384 if (HandleRemoteModifications(NULL, Timer))
3385 Timers->Del(Timer);
3386 else
3387 return true; // error while deleting remote timer
3388 }
3389 else if (!HandleRemoteModifications(Timer, &OldTimer))
3390 return true; // error while modifying remote timer
3391 }
3392 }
3393 else
3394 return true; // user didn't confirm deletion
3395 }
3396 }
3397 }
3398 return false;
3399}
3400
3402{
3403 if (HasSubMenu() || Count() == 0)
3404 return osContinue;
3406 if (ri && !ri->IsDirectory()) {
3407 if (Interface->Confirm(tr("Delete recording?"))) {
3409 return osContinue;
3410 cString FileName;
3411 {
3413 if (const cRecording *Recording = Recordings->GetByName(ri->Recording()->FileName()))
3414 FileName = Recording->FileName();
3415 else
3416 return osContinue; // recording has already been deleted
3417 }
3418 if (RecordingsHandler.GetUsage(FileName)) {
3419 if (!Interface->Confirm(tr("Recording is being edited - really delete?")))
3420 return osContinue;
3421 SetNeedsFastResponse(true); // makes sure the edited version is removed from the menu ASAP
3422 }
3423 RecordingsHandler.Del(FileName); // must do this w/o holding a lock, because the cleanup section in cDirCopier::Action() might request one!
3424 if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), FileName) == 0)
3427 cStateKey StateKey;
3428 cRecordings *DeletedRecordings = cRecordings::GetDeletedRecordingsWrite(StateKey);
3429 cRecording *Recording = Recordings->GetByName(FileName);
3430 if (!Recording || Recording->Delete()) {
3432 Recordings->Del(Recording, false);
3433 DeletedRecordings->Add(Recording);
3435 SetHelpKeys();
3437 StateKey.Remove();
3438 recordingsStateKey.Remove();
3439 SetDeleted(Recording->FileName());
3440 Display();
3442 if (!Count())
3443 return osUserRecEmpty;
3444 return osUserRecRemoved;
3445 }
3446 else
3447 Skins.QueueMessage(mtError, tr("Error while deleting recording!"));
3448 StateKey.Remove();
3449 recordingsStateKey.Remove();
3450 }
3451 }
3452 return osContinue;
3453}
3454
3456{
3457 if (HasSubMenu() || Count() == 0)
3458 return osContinue;
3460 if (ri && !ri->IsDirectory()) {
3461 if (Interface->Confirm(tr("Restore recording?"))) {
3462 if (cRecording *Recording = ((cRecording *)ri->Recording())) {
3463 cStateKey StateKey;
3464 cRecordings *Recordings = cRecordings::GetRecordingsWrite(StateKey);
3466 if (Recording->Undelete()) {
3467 DeletedRecordings->Del(Recording, false);
3468 Recordings->Add(Recording);
3470 SetHelpKeys();
3472 StateKey.Remove();
3473 recordingsStateKey.Remove();
3474 SetRecording(Recording->FileName());
3475 Display();
3477 if (!Count())
3478 return osUserRecEmpty;
3479 return osUserRecRemoved;
3480 }
3481 else
3482 Skins.QueueMessage(mtError, tr("Error while restoring recording!"));
3483 StateKey.Remove();
3484 recordingsStateKey.Remove();
3485 }
3486 }
3487 }
3488 return osContinue;
3489}
3490
3492{
3493 if (HasSubMenu() || Count() == 0)
3494 return osContinue;
3496 if (ri && !ri->IsDirectory()) {
3497 if (Interface->Confirm(tr("Permanently delete recording?")) && Interface->Confirm(tr("Are you sure? This can't be undone!"))) {
3498 if (cRecording *Recording = ((cRecording *)ri->Recording())) {
3500 if (Recording->Remove()) {
3501 DeletedRecordings->Del(Recording);
3503 SetHelpKeys();
3505 recordingsStateKey.Remove();
3506 Display();
3508 if (!Count())
3509 return osUserRecEmpty;
3510 return osUserRecRemoved;
3511 }
3512 else
3513 Skins.QueueMessage(mtError, tr("Error while permanently deleting recording!"));
3514 recordingsStateKey.Remove();
3515 }
3516 }
3517 }
3518 return osContinue;
3519}
3520
3522{
3523 if (HasSubMenu() || Count() == 0)
3524 return osContinue;
3526 if (ri->IsDirectory()) {
3527 if (!delRecMenu)
3528 return AddSubMenu(new cMenuPathEdit(cString(ri->Recording()->Name(), strchrn(ri->Recording()->Name(), FOLDERDELIMCHAR, ri->Level() + 1))));
3529 }
3530 else
3531 return AddSubMenu(new cMenuRecording(ri->Recording(), true));
3532 }
3533 return osContinue;
3534}
3535
3537{
3538 if (HasSubMenu() || Count() == 0)
3539 return osContinue;
3541 if (ri && !ri->IsDirectory()) {
3542 cMenuCommands *menu;
3543 eOSState state = AddSubMenu(menu = new cMenuCommands(tr("Recording commands"), &RecordingCommands, cString::sprintf("\"%s\"", *strescape(ri->Recording()->FileName(), "\\\"$"))));
3544 if (Key != kNone)
3545 state = menu->ProcessKey(Key);
3546 return state;
3547 }
3548 return osContinue;
3549}
3550
3552{
3553 if (HasSubMenu())
3554 return osContinue;
3555 if (const cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current()))
3556 SetRecording(ri->Recording()->FileName()); // makes sure the Recordings menu will reposition to the current recording
3558 recordingsStateKey.Reset();
3559 Set(true);
3560 return osContinue;
3561}
3562
3564{
3565 eOSState state = cOsdMenu::ProcessKey(Key);
3566
3567 if (state == osUnknown) {
3568 switch (Key) {
3569 case kPlayPause:
3570 case kPlay:
3571 case kOk: if (delRecMenu) {
3572 Open();
3573 return osContinue;
3574 }
3575 else
3576 return Play();
3577 case kRed: if (delRecMenu)
3578 toggleDelRec = time(NULL);
3579 switch (helpKeys) {
3580 case 1: return delRecMenu ? osRecsOpen : osRecsDel;
3581 case 2: return delRecMenu ? osRecsOpen : Play();
3582 case 3: return osRecsOpen;
3583 case 4: return osRecsDel;
3584 case 5: return RecordingCommands.Count() ? Commands() : Play();
3585 case 6: return delRecMenu ? osRecsOpen : osRecsDel;
3586 default: ;
3587 }
3588 case kGreen: return AdjustTitle(delRecMenu ? Restore() : Rewind());
3589 case kYellow: return AdjustTitle(delRecMenu ? Purge() : Delete());
3590 case kInfo:
3591 case kBlue: return Info();
3592 case k0: return Sort();
3593 case k1...k9: return delRecMenu ? osContinue : Commands(Key);
3594 default: break;
3595 }
3596 }
3597 else if (state == osUserRecRenamed) {
3598 // a recording was renamed (within the same folder), so let's refresh the menu
3599 CloseSubMenu(false); // this is the cMenuRecordingEdit/cMenuPathEdit
3600 fileName = NULL;
3601 state = osContinue;
3602 }
3603 else if (state == osUserRecMoved) {
3604 // a recording was moved to a different folder, so let's delete the old item
3605 CloseSubMenu(false); // this is the cMenuRecordingEdit/cMenuPathEdit
3606 fileName = NULL;
3608 Set(); // the recording might have been moved into a new subfolder of this folder
3609 if (!Count())
3610 return osUserRecEmpty;
3611 Display();
3612 state = osUserRecRemoved;
3613 }
3614 else if (state == osUserRecRemoved) {
3615 // a recording was removed from a sub folder, so update the current item
3616 if (cOsdMenu *m = SubMenu()) {
3618 if (cMenuRecordingItem *riSub = (cMenuRecordingItem *)m->Get(m->Current()))
3619 ri->SetRecording(riSub->Recording());
3620 }
3621 }
3622 // no state change here, this report goes upstream!
3623 }
3624 else if (state == osUserRecEmpty) {
3625 // a subfolder became empty, so let's go back up
3626 CloseSubMenu(false); // this is the now empty submenu
3627 cOsdMenu::Del(Current()); // the menu entry of the now empty subfolder
3628 Set(); // in case a recording was moved into a new subfolder of this folder
3629 SetHelpKeys();
3630 if (!Count()) {
3631 if (base) // don't go up beyond the top level Recordings menu
3632 return state;
3633 else {
3634 AdjustTitle(state);
3635 return osContinue; // AdjustTitle() already does the Display()
3636 }
3637 }
3638 Display();
3639 state = osContinue;
3640 }
3641 if (!HasSubMenu() && state != osContinue) {
3642 Set(true);
3643 if (Key != kNone)
3644 SetHelpKeys();
3645 }
3646 return state;
3647}
3648
3649// --- cMenuSetupBase --------------------------------------------------------
3650
3652protected:
3654 virtual void Store(void) override;
3655public:
3656 cMenuSetupBase(void);
3657 };
3658
3663
3665{
3666 Setup = data;
3668 Setup.Save();
3669}
3670
3671// --- cMenuSetupOSD ---------------------------------------------------------
3672
3674private:
3675 const char *useSmallFontTexts[3];
3676 const char *recSortModeTexts[2];
3677 const char *recSortDirTexts[2];
3678 const char *keyColorTexts[4];
3683 const char **skinDescriptions;
3689 virtual void Set(void);
3690public:
3691 cMenuSetupOSD(void);
3692 virtual ~cMenuSetupOSD() override;
3693 virtual eOSState ProcessKey(eKeys Key) override;
3694 };
3695
3697{
3700 numSkins = Skins.Count();
3701 skinIndex = originalSkinIndex = Skins.Current()->Index();
3702 skinDescriptions = new const char*[numSkins];
3703 themes.Load(Skins.Current()->Name());
3704 themeIndex = originalThemeIndex = Skins.Current()->Theme() ? themes.GetThemeIndex(Skins.Current()->Theme()->Description()) : 0;
3708 fontOsdNames.Insert(strdup(DefaultFontOsd));
3709 fontSmlNames.Insert(strdup(DefaultFontSml));
3710 fontFixNames.Insert(strdup(DefaultFontFix));
3711 fontOsdIndex = max(0, fontOsdNames.Find(Setup.FontOsd));
3712 fontSmlIndex = max(0, fontSmlNames.Find(Setup.FontSml));
3713 fontFixIndex = max(0, fontFixNames.Find(Setup.FontFix));
3714 Set();
3715}
3716
3721
3723{
3724 int current = Current();
3725 for (cSkin *Skin = Skins.First(); Skin; Skin = Skins.Next(Skin))
3726 skinDescriptions[Skin->Index()] = Skin->Description();
3727 useSmallFontTexts[0] = tr("never");
3728 useSmallFontTexts[1] = tr("skin dependent");
3729 useSmallFontTexts[2] = tr("always");
3730 recSortModeTexts[0] = tr("by name");
3731 recSortModeTexts[1] = tr("by time");
3732 recSortDirTexts[0] = tr("ascending");
3733 recSortDirTexts[1] = tr("descending");
3734 keyColorTexts[0] = tr("Key$Red");
3735 keyColorTexts[1] = tr("Key$Green");
3736 keyColorTexts[2] = tr("Key$Yellow");
3737 keyColorTexts[3] = tr("Key$Blue");
3738 Clear();
3739 SetSection(tr("OSD"));
3740 Add(new cMenuEditStraItem(tr("Setup.OSD$Language"), &osdLanguageIndex, I18nNumLanguagesWithLocale(), &I18nLanguages()->At(0)));
3741 Add(new cMenuEditStraItem(tr("Setup.OSD$Skin"), &skinIndex, numSkins, skinDescriptions));
3742 if (themes.NumThemes())
3743 Add(new cMenuEditStraItem(tr("Setup.OSD$Theme"), &themeIndex, themes.NumThemes(), themes.Descriptions()));
3744 Add(new cMenuEditPrcItem( tr("Setup.OSD$Left (%)"), &data.OSDLeftP, 0.0, 0.5));
3745 Add(new cMenuEditPrcItem( tr("Setup.OSD$Top (%)"), &data.OSDTopP, 0.0, 0.5));
3746 Add(new cMenuEditPrcItem( tr("Setup.OSD$Width (%)"), &data.OSDWidthP, 0.5, 1.0));
3747 Add(new cMenuEditPrcItem( tr("Setup.OSD$Height (%)"), &data.OSDHeightP, 0.5, 1.0));
3748 Add(new cMenuEditIntItem( tr("Setup.OSD$Message time (s)"), &data.OSDMessageTime, 1, 60));
3749 Add(new cMenuEditStraItem(tr("Setup.OSD$Use small font"), &data.UseSmallFont, 3, useSmallFontTexts));
3750 Add(new cMenuEditBoolItem(tr("Setup.OSD$Anti-alias"), &data.AntiAlias));
3751 Add(new cMenuEditStraItem(tr("Setup.OSD$Default font"), &fontOsdIndex, fontOsdNames.Size(), &fontOsdNames[0]));
3752 Add(new cMenuEditStraItem(tr("Setup.OSD$Small font"), &fontSmlIndex, fontSmlNames.Size(), &fontSmlNames[0]));
3753 Add(new cMenuEditStraItem(tr("Setup.OSD$Fixed font"), &fontFixIndex, fontFixNames.Size(), &fontFixNames[0]));
3754 Add(new cMenuEditPrcItem( tr("Setup.OSD$Default font size (%)"), &data.FontOsdSizeP, 0.01, 0.1, 1));
3755 Add(new cMenuEditPrcItem( tr("Setup.OSD$Small font size (%)"), &data.FontSmlSizeP, 0.01, 0.1, 1));
3756 Add(new cMenuEditPrcItem( tr("Setup.OSD$Fixed font size (%)"), &data.FontFixSizeP, 0.01, 0.1, 1));
3757 Add(new cMenuEditBoolItem(tr("Setup.OSD$Channel info position"), &data.ChannelInfoPos, tr("bottom"), tr("top")));
3758 Add(new cMenuEditIntItem( tr("Setup.OSD$Channel info time (s)"), &data.ChannelInfoTime, 1, 60));
3759 Add(new cMenuEditBoolItem(tr("Setup.OSD$Info on channel switch"), &data.ShowInfoOnChSwitch));
3760 Add(new cMenuEditBoolItem(tr("Setup.OSD$Timeout requested channel info"), &data.TimeoutRequChInfo));
3761 Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll pages"), &data.MenuScrollPage));
3762 Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll wraps"), &data.MenuScrollWrap));
3763 Add(new cMenuEditBoolItem(tr("Setup.OSD$Menu key closes"), &data.MenuKeyCloses));
3764 Add(new cMenuEditBoolItem(tr("Setup.OSD$Recording directories"), &data.RecordingDirs));
3765 Add(new cMenuEditBoolItem(tr("Setup.OSD$Folders in timer menu"), &data.FoldersInTimerMenu));
3766 Add(new cMenuEditBoolItem(tr("Setup.OSD$Always sort folders first"), &data.AlwaysSortFoldersFirst));
3767 Add(new cMenuEditStraItem(tr("Setup.OSD$Default sort mode for recordings"), &data.DefaultSortModeRec, 2, recSortModeTexts));
3768 Add(new cMenuEditStraItem(tr("Setup.OSD$Sorting direction for recordings"), &data.RecSortingDirection, 2, recSortDirTexts));
3769 Add(new cMenuEditBoolItem(tr("Setup.OSD$Number keys for characters"), &data.NumberKeysForChars));
3770 Add(new cMenuEditStraItem(tr("Setup.OSD$Color key 0"), &data.ColorKey0, 4, keyColorTexts));
3771 Add(new cMenuEditStraItem(tr("Setup.OSD$Color key 1"), &data.ColorKey1, 4, keyColorTexts));
3772 Add(new cMenuEditStraItem(tr("Setup.OSD$Color key 2"), &data.ColorKey2, 4, keyColorTexts));
3773 Add(new cMenuEditStraItem(tr("Setup.OSD$Color key 3"), &data.ColorKey3, 4, keyColorTexts));
3775 Display();
3776}
3777
3779{
3780 bool ModifiedAppearance = false;
3781
3782 if (Key == kOk) {
3783 I18nSetLocale(data.OSDLanguage);
3785 cSkin *Skin = Skins.Get(skinIndex);
3786 if (Skin) {
3787 Utf8Strn0Cpy(data.OSDSkin, Skin->Name(), sizeof(data.OSDSkin));
3788 Skins.SetCurrent(Skin->Name());
3789 ModifiedAppearance = true;
3790 }
3791 }
3792 if (themes.NumThemes() && Skins.Current()->Theme()) {
3793 Skins.Current()->Theme()->Load(themes.FileName(themeIndex));
3794 Utf8Strn0Cpy(data.OSDTheme, themes.Name(themeIndex), sizeof(data.OSDTheme));
3795 ModifiedAppearance |= themeIndex != originalThemeIndex;
3796 }
3797 if (!(DoubleEqual(data.OSDLeftP, Setup.OSDLeftP) && DoubleEqual(data.OSDTopP, Setup.OSDTopP) && DoubleEqual(data.OSDWidthP, Setup.OSDWidthP) && DoubleEqual(data.OSDHeightP, Setup.OSDHeightP)))
3798 ModifiedAppearance = true;
3799 if (data.UseSmallFont != Setup.UseSmallFont || data.AntiAlias != Setup.AntiAlias)
3800 ModifiedAppearance = true;
3801 Utf8Strn0Cpy(data.FontOsd, fontOsdNames[fontOsdIndex], sizeof(data.FontOsd));
3802 Utf8Strn0Cpy(data.FontSml, fontSmlNames[fontSmlIndex], sizeof(data.FontSml));
3803 Utf8Strn0Cpy(data.FontFix, fontFixNames[fontFixIndex], sizeof(data.FontFix));
3804 if (strcmp(data.FontOsd, Setup.FontOsd) || !DoubleEqual(data.FontOsdSizeP, Setup.FontOsdSizeP))
3805 ModifiedAppearance = true;
3806 if (strcmp(data.FontSml, Setup.FontSml) || !DoubleEqual(data.FontSmlSizeP, Setup.FontSmlSizeP))
3807 ModifiedAppearance = true;
3808 if (strcmp(data.FontFix, Setup.FontFix) || !DoubleEqual(data.FontFixSizeP, Setup.FontFixSizeP))
3809 ModifiedAppearance = true;
3810 if (data.AlwaysSortFoldersFirst != Setup.AlwaysSortFoldersFirst || data.RecordingDirs != Setup.RecordingDirs || data.RecSortingDirection != Setup.RecSortingDirection) {
3812 Recordings->ClearSortNames();
3813 }
3814 }
3815
3816 int oldSkinIndex = skinIndex;
3817 int oldOsdLanguageIndex = osdLanguageIndex;
3819
3820 if (ModifiedAppearance)
3822
3823 if (osdLanguageIndex != oldOsdLanguageIndex || skinIndex != oldSkinIndex) {
3824 strn0cpy(data.OSDLanguage, I18nLocale(osdLanguageIndex), sizeof(data.OSDLanguage));
3825 int OriginalOSDLanguage = I18nCurrentLanguage();
3827
3828 cSkin *Skin = Skins.Get(skinIndex);
3829 if (Skin) {
3830 char *d = themes.NumThemes() ? strdup(themes.Descriptions()[themeIndex]) : NULL;
3831 themes.Load(Skin->Name());
3832 if (skinIndex != oldSkinIndex)
3833 themeIndex = d ? themes.GetThemeIndex(d) : 0;
3834 free(d);
3835 }
3836
3837 Set();
3838 I18nSetLanguage(OriginalOSDLanguage);
3839 }
3840 return state;
3841}
3842
3843// --- cMenuSetupEPG ---------------------------------------------------------
3844
3846private:
3849 void Setup(void);
3850public:
3851 cMenuSetupEPG(void);
3852 virtual eOSState ProcessKey(eKeys Key) override;
3853 };
3854
3856{
3858 for (numLanguages = 0; numLanguages < I18nLanguages()->Size() && data.EPGLanguages[numLanguages] >= 0; numLanguages++)
3859 ;
3861 SetSection(tr("EPG"));
3862 SetHelp(tr("Button$Scan"));
3863 Setup();
3864}
3865
3867{
3868 int current = Current();
3869
3870 Clear();
3871
3872 Add(new cMenuEditIntItem( tr("Setup.EPG$EPG scan timeout (h)"), &data.EPGScanTimeout));
3873 Add(new cMenuEditIntItem( tr("Setup.EPG$EPG scan max. channel number (0=all)"), &data.EPGScanMaxChannel));
3874 Add(new cMenuEditBoolItem(tr("Setup.EPG$EPG pause after scan"), &data.EPGPauseAfterScan));
3875 Add(new cMenuEditIntItem( tr("Setup.EPG$EPG bugfix level"), &data.EPGBugfixLevel, 0, MAXEPGBUGFIXLEVEL));
3876 Add(new cMenuEditIntItem( tr("Setup.EPG$EPG linger time (min)"), &data.EPGLinger, 0));
3877 Add(new cMenuEditBoolItem(tr("Setup.EPG$Set system time"), &data.SetSystemTime));
3878 if (data.SetSystemTime)
3879 Add(new cMenuEditTranItem(Indent(2, tr("Setup.EPG$Use time from transponder")), &data.TimeTransponder, &data.TimeSource));
3880 // TRANSLATORS: note the plural!
3881 Add(new cMenuEditIntItem( tr("Setup.EPG$Preferred languages"), &numLanguages, 0, I18nLanguages()->Size()));
3882 for (int i = 0; i < numLanguages; i++)
3883 // TRANSLATORS: note the singular!
3884 Add(new cMenuEditStraItem(Indent(2, tr("Setup.EPG$Preferred language")), &data.EPGLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
3885
3887 Display();
3888}
3889
3891{
3892 if (Key == kOk) {
3893 bool Modified = numLanguages != originalNumLanguages;
3894 if (!Modified) {
3895 for (int i = 0; i < numLanguages; i++) {
3896 if (data.EPGLanguages[i] != ::Setup.EPGLanguages[i]) {
3897 Modified = true;
3898 break;
3899 }
3900 }
3901 }
3902 if (Modified)
3904 }
3905
3906 int oldnumLanguages = numLanguages;
3907 int oldSetSystemTime = data.SetSystemTime;
3908
3910 if (Key != kNone) {
3911 if (numLanguages != oldnumLanguages || data.SetSystemTime != oldSetSystemTime) {
3912 for (int i = oldnumLanguages; i < numLanguages; i++) {
3913 data.EPGLanguages[i] = 0;
3914 for (int l = 0; l < I18nLanguages()->Size(); l++) {
3915 int k;
3916 for (k = 0; k < oldnumLanguages; k++) {
3917 if (data.EPGLanguages[k] == l)
3918 break;
3919 }
3920 if (k >= oldnumLanguages) {
3921 data.EPGLanguages[i] = l;
3922 break;
3923 }
3924 }
3925 }
3926 data.EPGLanguages[numLanguages] = -1;
3927 Setup();
3928 }
3929 if (Key == kRed) {
3930 EITScanner.ForceScan();
3931 return osEnd;
3932 }
3933 }
3934 return state;
3935}
3936
3937// --- cMenuSetupDVB ---------------------------------------------------------
3938
3940private:
3945 void Setup(void);
3947 const char *updateChannelsTexts[6];
3950public:
3951 cMenuSetupDVB(void);
3952 virtual eOSState ProcessKey(eKeys Key) override;
3953 };
3954
3956{
3959 ;
3961 ;
3964 videoDisplayFormatTexts[0] = tr("pan&scan");
3965 videoDisplayFormatTexts[1] = tr("letterbox");
3966 videoDisplayFormatTexts[2] = tr("center cut out");
3967 updateChannelsTexts[0] = tr("no");
3968 updateChannelsTexts[1] = tr("names only");
3969 updateChannelsTexts[2] = tr("PIDs only");
3970 updateChannelsTexts[3] = tr("names and PIDs");
3971 updateChannelsTexts[4] = tr("add new channels");
3972 updateChannelsTexts[5] = tr("add new transponders");
3973 displaySubtitlesTexts[0] = tr("no");
3974 displaySubtitlesTexts[1] = tr("always");
3975 displaySubtitlesTexts[2] = tr("after rewind");
3976 standardComplianceTexts[0] = "DVB";
3977 standardComplianceTexts[1] = "ANSI/SCTE";
3978 standardComplianceTexts[2] = "NORDIG";
3979
3980 SetSection(tr("DVB"));
3981 SetHelp(NULL, tr("Button$Audio"), tr("Button$Subtitles"), NULL);
3982 Setup();
3983}
3984
3986{
3987 int current = Current();
3988
3989 Clear();
3990
3991 Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
3992 Add(new cMenuEditStraItem(tr("Setup.DVB$Standard compliance"), &data.StandardCompliance, 3, standardComplianceTexts));
3993 Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"), &data.VideoFormat, "4:3", "16:9"));
3994 if (data.VideoFormat == 0)
3995 Add(new cMenuEditStraItem(tr("Setup.DVB$Video display format"), &data.VideoDisplayFormat, 3, videoDisplayFormatTexts));
3996 Add(new cMenuEditBoolItem(tr("Setup.DVB$Use Dolby Digital"), &data.UseDolbyDigital));
3997 Add(new cMenuEditStraItem(tr("Setup.DVB$Update channels"), &data.UpdateChannels, 6, updateChannelsTexts));
3998 Add(new cMenuEditIntItem( tr("Setup.DVB$Audio languages"), &numAudioLanguages, 0, I18nLanguages()->Size()));
3999 for (int i = 0; i < numAudioLanguages; i++)
4000 Add(new cMenuEditStraItem(Indent(2, tr("Setup.DVB$Audio language")), &data.AudioLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
4001 Add(new cMenuEditStraItem(tr("Setup.DVB$Display subtitles"), &data.DisplaySubtitles, 3, displaySubtitlesTexts));
4002 if (data.DisplaySubtitles != SUBTITLES_NO) {
4003 Add(new cMenuEditIntItem(Indent(2, tr("Setup.DVB$Subtitle languages")), &numSubtitleLanguages, 0, I18nLanguages()->Size()));
4004 for (int i = 0; i < numSubtitleLanguages; i++)
4005 Add(new cMenuEditStraItem(Indent(4, tr("Setup.DVB$Subtitle language")), &data.SubtitleLanguages[i], I18nLanguages()->Size(), &I18nLanguages()->At(0)));
4006 Add(new cMenuEditIntItem(Indent(2, tr("Setup.DVB$Subtitle offset")), &data.SubtitleOffset, -100, 100));
4007 Add(new cMenuEditIntItem(Indent(2, tr("Setup.DVB$Subtitle foreground transparency")), &data.SubtitleFgTransparency, 0, 9));
4008 Add(new cMenuEditIntItem(Indent(2, tr("Setup.DVB$Subtitle background transparency")), &data.SubtitleBgTransparency, 0, 10));
4009 }
4010
4012 Display();
4013}
4014
4016{
4017 int oldVideoDisplayFormat = ::Setup.VideoDisplayFormat;
4018 bool oldVideoFormat = ::Setup.VideoFormat;
4019 bool newVideoFormat = data.VideoFormat;
4020 int oldDisplaySubtitles = ::Setup.DisplaySubtitles;
4021 int newDisplaySubtitles = data.DisplaySubtitles;
4022 int oldnumAudioLanguages = numAudioLanguages;
4023 int oldnumSubtitleLanguages = numSubtitleLanguages;
4025
4026 if (Key != kNone) {
4027 switch (Key) {
4028 case kGreen: cRemote::Put(kAudio, true);
4029 state = osEnd;
4030 break;
4031 case kYellow: cRemote::Put(kSubtitles, true);
4032 state = osEnd;
4033 break;
4034 default: {
4035 bool DoSetup = data.VideoFormat != newVideoFormat;
4036 DoSetup |= data.DisplaySubtitles != newDisplaySubtitles;
4037 if (numAudioLanguages != oldnumAudioLanguages) {
4038 for (int i = oldnumAudioLanguages; i < numAudioLanguages; i++) {
4039 data.AudioLanguages[i] = 0;
4040 for (int l = 0; l < I18nLanguages()->Size(); l++) {
4041 int k;
4042 for (k = 0; k < oldnumAudioLanguages; k++) {
4043 if (data.AudioLanguages[k] == l)
4044 break;
4045 }
4046 if (k >= oldnumAudioLanguages) {
4047 data.AudioLanguages[i] = l;
4048 break;
4049 }
4050 }
4051 }
4052 data.AudioLanguages[numAudioLanguages] = -1;
4053 DoSetup = true;
4054 }
4055 if (numSubtitleLanguages != oldnumSubtitleLanguages) {
4056 for (int i = oldnumSubtitleLanguages; i < numSubtitleLanguages; i++) {
4057 data.SubtitleLanguages[i] = 0;
4058 for (int l = 0; l < I18nLanguages()->Size(); l++) {
4059 int k;
4060 for (k = 0; k < oldnumSubtitleLanguages; k++) {
4061 if (data.SubtitleLanguages[k] == l)
4062 break;
4063 }
4064 if (k >= oldnumSubtitleLanguages) {
4065 data.SubtitleLanguages[i] = l;
4066 break;
4067 }
4068 }
4069 }
4070 data.SubtitleLanguages[numSubtitleLanguages] = -1;
4071 DoSetup = true;
4072 }
4073 if (DoSetup)
4074 Setup();
4075 }
4076 }
4077 }
4078 if (state == osBack && Key == kOk) {
4079 if (::Setup.VideoDisplayFormat != oldVideoDisplayFormat)
4081 if (::Setup.VideoFormat != oldVideoFormat)
4083 if (::Setup.DisplaySubtitles != oldDisplaySubtitles)
4086 }
4087 return state;
4088}
4089
4090// --- cMenuSetupLNB ---------------------------------------------------------
4091
4093private:
4095 void Setup(void);
4096public:
4097 cMenuSetupLNB(void);
4098 virtual eOSState ProcessKey(eKeys Key) override;
4099 };
4100
4103{
4105 satCableNumbers.FromString(data.DeviceBondings);
4106 SetSection(tr("LNB"));
4107 Setup();
4108}
4109
4111{
4112 int current = Current();
4113
4114 Clear();
4115
4116 Add(new cMenuEditBoolItem(tr("Setup.LNB$Use DiSEqC"), &data.DiSEqC));
4117 if (!data.DiSEqC) {
4118 Add(new cMenuEditIntItem( tr("Setup.LNB$SLOF (MHz)"), &data.LnbSLOF));
4119 Add(new cMenuEditIntItem( tr("Setup.LNB$Low LNB frequency (MHz)"), &data.LnbFrequLo));
4120 Add(new cMenuEditIntItem( tr("Setup.LNB$High LNB frequency (MHz)"), &data.LnbFrequHi));
4121 }
4122
4123 int NumSatDevices = 0;
4124 for (int i = 0; i < cDevice::NumDevices(); i++) {
4125 if (cDevice::GetDevice(i)->ProvidesSource(cSource::stSat))
4126 NumSatDevices++;
4127 }
4128 if (NumSatDevices > 1) {
4129 for (int i = 0; i < cDevice::NumDevices(); i++) {
4130 if (cDevice::GetDevice(i)->ProvidesSource(cSource::stSat))
4131 Add(new cMenuEditIntItem(cString::sprintf(tr("Setup.LNB$Device %d connected to sat cable"), i + 1), &satCableNumbers.Array()[i], 0, NumSatDevices, tr("Setup.LNB$own")));
4132 else
4133 satCableNumbers.Array()[i] = 0;
4134 }
4135 }
4136
4137 Add(new cMenuEditBoolItem(tr("Setup.LNB$Use dish positioner"), &data.UsePositioner));
4138 if (data.UsePositioner) {
4139 Add(new cMenuEditIntxItem(tr("Setup.LNB$Site latitude (degrees)"), &data.SiteLat, -900, 900, 10, tr("South"), tr("North")));
4140 Add(new cMenuEditIntxItem(tr("Setup.LNB$Site longitude (degrees)"), &data.SiteLon, -1800, 1800, 10, tr("West"), tr("East")));
4141 Add(new cMenuEditIntxItem(tr("Setup.LNB$Max. positioner swing (degrees)"), &data.PositionerSwing, 0, 900, 10));
4142 Add(new cMenuEditIntxItem(tr("Setup.LNB$Positioner speed (degrees/s)"), &data.PositionerSpeed, 1, 1800, 10));
4143 }
4144
4146 Display();
4147}
4148
4150{
4151 int oldDiSEqC = data.DiSEqC;
4152 int oldUsePositioner = data.UsePositioner;
4153 bool DeviceBondingsChanged = false;
4154 if (Key == kOk) {
4155 cString NewDeviceBondings = satCableNumbers.ToString();
4156 DeviceBondingsChanged = strcmp(data.DeviceBondings, NewDeviceBondings) != 0;
4157 data.DeviceBondings = NewDeviceBondings;
4158 }
4160
4161 if (Key != kNone && (data.DiSEqC != oldDiSEqC || data.UsePositioner != oldUsePositioner))
4162 Setup();
4163 else if (DeviceBondingsChanged)
4164 cDvbDevice::BondDevices(data.DeviceBondings);
4165 return state;
4166}
4167
4168// --- cMenuSetupCAM ---------------------------------------------------------
4169
4171private:
4173public:
4175 cCamSlot *CamSlot(void) { return camSlot; }
4176 bool Changed(void);
4177 };
4178
4185
4187{
4188 cString AssignedDevice("");
4189 const char *Activating = "";
4190 const char *CamName = camSlot->GetCamName();
4191 if (!CamName) {
4192 switch (camSlot->ModuleStatus()) {
4193 case msReset: CamName = tr("CAM reset"); break;
4194 case msPresent: CamName = tr("CAM present"); break;
4195 case msReady: CamName = tr("CAM ready"); break;
4196 default: CamName = "-"; break;
4197 }
4198 }
4199 else if (camSlot->IsActivating())
4200 // TRANSLATORS: note the leading blank!
4201 Activating = tr(" (activating)");
4202 cVector<int> DeviceNumbers;
4203 for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
4204 if (CamSlot == camSlot || CamSlot->MasterSlot() == camSlot)
4205 CamSlot->Devices(DeviceNumbers);
4206 }
4207 if (DeviceNumbers.Size() > 0) {
4208 AssignedDevice = cString::sprintf(" %s", tr("@ device"));
4209 DeviceNumbers.Sort(CompareInts);
4210 for (int i = 0; i < DeviceNumbers.Size(); i++)
4211 AssignedDevice = cString::sprintf("%s %d", *AssignedDevice, DeviceNumbers[i]);
4212 }
4213
4214 cString buffer = cString::sprintf(" %d %s%s%s", camSlot->SlotNumber(), CamName, *AssignedDevice, Activating);
4215 if (strcmp(buffer, Text()) != 0) {
4216 SetText(buffer);
4217 return true;
4218 }
4219 return false;
4220}
4221
4223private:
4225 const char *activationHelp;
4226 eOSState Menu(void);
4227 eOSState Reset(void);
4228 eOSState Activate(void);
4229 void SetHelpKeys(void);
4230public:
4231 cMenuSetupCAM(void);
4232 virtual eOSState ProcessKey(eKeys Key) override;
4233 };
4234
4236{
4238 activationHelp = NULL;
4240 SetSection(tr("CAM"));
4241 SetCols(15);
4242 SetHasHotkeys();
4243 for (cCamSlot *CamSlot = CamSlots.First(); CamSlot; CamSlot = CamSlots.Next(CamSlot)) {
4244 if (CamSlot->IsMasterSlot()) // we only list master CAM slots
4245 Add(new cMenuSetupCAMItem(CamSlot));
4246 }
4247 SetHelpKeys();
4248}
4249
4251{
4252 if (HasSubMenu())
4253 return;
4255 const char *NewActivationHelp = "";
4256 if (item) {
4257 cCamSlot *CamSlot = item->CamSlot();
4258 if (CamSlot->IsActivating())
4259 NewActivationHelp = tr("Button$Cancel activation");
4260 else if (CamSlot->CanActivate())
4261 NewActivationHelp = tr("Button$Activate");
4262 }
4263 if (NewActivationHelp != activationHelp) {
4264 activationHelp = NewActivationHelp;
4265 SetHelp(tr("Button$Menu"), tr("Button$Reset"), activationHelp);
4266 }
4267}
4268
4270{
4272 if (item) {
4273 if (item->CamSlot()->EnterMenu()) {
4274 Skins.Message(mtStatus, tr("Opening CAM menu..."));
4275 time_t t0 = time(NULL);
4276 time_t t1 = t0;
4277 while (time(NULL) - t0 <= MAXWAITFORCAMMENU) {
4278 if (item->CamSlot()->HasUserIO())
4279 break;
4280 if (time(NULL) - t1 >= CAMMENURETRYTIMEOUT) {
4281 dsyslog("CAM %d: retrying to enter CAM menu...", item->CamSlot()->SlotNumber());
4282 item->CamSlot()->EnterMenu();
4283 t1 = time(NULL);
4284 }
4285 cCondWait::SleepMs(100);
4286 }
4287 Skins.Message(mtStatus, NULL);
4288 if (item->CamSlot()->HasUserIO())
4289 return AddSubMenu(new cMenuCam(item->CamSlot()));
4290 }
4291 Skins.QueueMessage(mtError, tr("Can't open CAM menu!"));
4292 }
4293 return osContinue;
4294}
4295
4297{
4299 if (item) {
4300 cCamSlot *CamSlot = item->CamSlot();
4301 if (CamSlot->IsActivating())
4302 CamSlot->CancelActivation();
4303 else if (CamSlot->CanActivate()) {
4304 if (CamSlot->Priority() < LIVEPRIORITY) { // don't interrupt recordings
4306 if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel())) {
4307 for (int i = 0; i < cDevice::NumDevices(); i++) {
4308 if (cDevice *Device = cDevice::GetDevice(i)) {
4309 if (Device->ProvidesChannel(Channel)) {
4310 if (Device->Priority() < LIVEPRIORITY) { // don't interrupt recordings
4311 if (CamSlot->Assign(Device, true)) { // query
4312 cControl::Shutdown(); // must end transfer mode before assigning CAM, otherwise it might be unassigned again
4313 CamSlot = CamSlot->MtdSpawn();
4314 if (CamSlot->Assign(Device)) {
4315 if (Device->SwitchChannel(Channel, true)) {
4316 CamSlot->StartActivation();
4317 return osContinue;
4318 }
4319 }
4320 }
4321 }
4322 }
4323 }
4324 }
4325 }
4326 }
4327 Skins.QueueMessage(mtError, tr("Can't activate CAM!"));
4328 }
4329 }
4330 return osContinue;
4331}
4332
4334{
4336 if (item) {
4337 if (!item->CamSlot()->Device() || Interface->Confirm(tr("CAM is in use - really reset?"))) {
4338 if (!item->CamSlot()->Reset())
4339 Skins.QueueMessage(mtError, tr("Can't reset CAM!"));
4340 }
4341 }
4342 return osContinue;
4343}
4344
4346{
4348
4349 if (!HasSubMenu()) {
4350 switch (Key) {
4351 case kOk:
4352 case kRed: return Menu();
4353 case kGreen: state = Reset(); break;
4354 case kYellow: state = Activate(); break;
4355 default: break;
4356 }
4357 for (cMenuSetupCAMItem *ci = (cMenuSetupCAMItem *)First(); ci; ci = (cMenuSetupCAMItem *)ci->Next()) {
4358 if (ci->Changed())
4359 DisplayItem(ci);
4360 }
4361 SetHelpKeys();
4362 }
4364 state = osEnd;
4365 return state;
4366}
4367
4368// --- cMenuSetupRecord ------------------------------------------------------
4369
4371private:
4374 const char *delTimeshiftRecTexts[3];
4375public:
4376 cMenuSetupRecord(void);
4377 };
4378
4380{
4382 recordKeyHandlingTexts[0] = tr("no instant recording");
4383 recordKeyHandlingTexts[1] = tr("confirm instant recording");
4384 recordKeyHandlingTexts[2] = tr("record instantly");
4385 pauseKeyHandlingTexts[0] = tr("do not pause live video");
4386 pauseKeyHandlingTexts[1] = tr("confirm pause live video");
4387 pauseKeyHandlingTexts[2] = tr("pause live video");
4388 delTimeshiftRecTexts[0] = tr("no");
4389 delTimeshiftRecTexts[1] = tr("confirm");
4390 delTimeshiftRecTexts[2] = tr("yes");
4391 SetSection(tr("Recording"));
4392 Add(new cMenuEditIntItem( tr("Setup.Recording$Margin at start (min)"), &data.MarginStart));
4393 Add(new cMenuEditIntItem( tr("Setup.Recording$Margin at stop (min)"), &data.MarginStop));
4394 Add(new cMenuEditIntItem( tr("Setup.Recording$Default priority"), &data.DefaultPriority, 0, MAXPRIORITY));
4395 Add(new cMenuEditIntItem( tr("Setup.Recording$Default lifetime (d)"), &data.DefaultLifetime, 0, MAXLIFETIME));
4396 Add(new cMenuEditStraItem(tr("Setup.Recording$Record key handling"), &data.RecordKeyHandling, 3, recordKeyHandlingTexts));
4397 Add(new cMenuEditStraItem(tr("Setup.Recording$Pause key handling"), &data.PauseKeyHandling, 3, pauseKeyHandlingTexts));
4398 Add(new cMenuEditIntItem( tr("Setup.Recording$Pause priority"), &data.PausePriority, 0, MAXPRIORITY));
4399 Add(new cMenuEditIntItem( tr("Setup.Recording$Pause lifetime (d)"), &data.PauseLifetime, 0, MAXLIFETIME));
4400 Add(new cMenuEditBoolItem(tr("Setup.Recording$Use episode name"), &data.UseSubtitle));
4401 Add(new cMenuEditBoolItem(tr("Setup.Recording$Use VPS"), &data.UseVps));
4402 Add(new cMenuEditIntItem( tr("Setup.Recording$VPS margin (s)"), &data.VpsMargin, 0));
4403 Add(new cMenuEditBoolItem(tr("Setup.Recording$Mark instant recording"), &data.MarkInstantRecord));
4404 Add(new cMenuEditStrItem( tr("Setup.Recording$Name instant recording"), data.NameInstantRecord, sizeof(data.NameInstantRecord)));
4405 Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"), &data.InstantRecordTime, 0, MAXINSTANTRECTIME, tr("Setup.Recording$present event")));
4406 Add(new cMenuEditIntItem( tr("Setup.Recording$Max. video file size (MB)"), &data.MaxVideoFileSize, MINVIDEOFILESIZE, MAXVIDEOFILESIZETS));
4407 Add(new cMenuEditBoolItem(tr("Setup.Recording$Split edited files"), &data.SplitEditedFiles));
4408 Add(new cMenuEditStraItem(tr("Setup.Recording$Delete timeshift recording"),&data.DelTimeshiftRec, 3, delTimeshiftRecTexts));
4409}
4410
4411// --- cMenuSetupReplay ------------------------------------------------------
4412
4414protected:
4415 virtual void Store(void) override;
4416public:
4417 cMenuSetupReplay(void);
4418 };
4419
4421{
4423 SetSection(tr("Replay"));
4424 Add(new cMenuEditBoolItem(tr("Setup.Replay$Multi speed mode"), &data.MultiSpeedMode));
4425 Add(new cMenuEditBoolItem(tr("Setup.Replay$Show replay mode"), &data.ShowReplayMode));
4426 Add(new cMenuEditBoolItem(tr("Setup.Replay$Show remaining time"), &data.ShowRemainingTime));
4427 Add(new cMenuEditIntItem( tr("Setup.Replay$Progress display time (s)"), &data.ProgressDisplayTime, 0, 60));
4428 Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause replay when setting mark"), &data.PauseOnMarkSet));
4429 Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause replay when jumping to a mark"), &data.PauseOnMarkJump));
4430 Add(new cMenuEditBoolItem(tr("Setup.Replay$Skip edited parts"), &data.SkipEdited));
4431 Add(new cMenuEditBoolItem(tr("Setup.Replay$Pause replay at last mark"), &data.PauseAtLastMark));
4432 Add(new cMenuEditIntItem( tr("Setup.Replay$Initial duration for adaptive skipping (s)"), &data.AdaptiveSkipInitial, 10, 600));
4433 Add(new cMenuEditIntItem( tr("Setup.Replay$Reset timeout for adaptive skipping (s)"), &data.AdaptiveSkipTimeout, 0, 10));
4434 Add(new cMenuEditBoolItem(tr("Setup.Replay$Alternate behavior for adaptive skipping"), &data.AdaptiveSkipAlternate));
4435 Add(new cMenuEditBoolItem(tr("Setup.Replay$Use Prev/Next keys for adaptive skipping"), &data.AdaptiveSkipPrevNext));
4436 Add(new cMenuEditIntItem( tr("Setup.Replay$Skip distance with Green/Yellow keys (s)"), &data.SkipSeconds, 5, 600));
4437 Add(new cMenuEditIntItem( tr("Setup.Replay$Skip distance with Green/Yellow keys in repeat (s)"), &data.SkipSecondsRepeat, 5, 600));
4438 Add(new cMenuEditIntItem(tr("Setup.Replay$Resume ID"), &data.ResumeID, 0, 99));
4439}
4440
4442{
4443 if (Setup.ResumeID != data.ResumeID) {
4445 Recordings->ResetResume();
4446 }
4448}
4449
4450// --- cMenuSetupMisc --------------------------------------------------------
4451
4453private:
4457 void Set(void);
4458public:
4459 cMenuSetupMisc(void);
4460 virtual eOSState ProcessKey(eKeys Key) override;
4461 };
4462
4464{
4466 svdrpPeeringModeTexts[0] = tr("off");
4467 svdrpPeeringModeTexts[1] = tr("any hosts");
4468 svdrpPeeringModeTexts[2] = tr("only default host");
4472 SetSection(tr("Miscellaneous"));
4473 Set();
4474}
4475
4477{
4478 int current = Current();
4479 Clear();
4480 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. event timeout (min)"), &data.MinEventTimeout));
4481 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. user inactivity (min)"), &data.MinUserInactivity));
4482 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"), &data.SVDRPTimeout));
4483 Add(new cMenuEditStraItem(tr("Setup.Miscellaneous$SVDRP peering"), &data.SVDRPPeering, 3, svdrpPeeringModeTexts));
4484 if (data.SVDRPPeering) {
4485 Add(new cMenuEditStrItem(Indent(2, tr("Setup.Miscellaneous$SVDRP host name")), data.SVDRPHostName, sizeof(data.SVDRPHostName)));
4487 svdrpServerNames.Sort(true);
4488 svdrpServerNames.Insert(strdup(""));
4489 Add(new cMenuEditStrlItem(Indent(2, tr("Setup.Miscellaneous$SVDRP default host")), data.SVDRPDefaultHost, sizeof(data.SVDRPDefaultHost), &svdrpServerNames));
4490 }
4491 }
4492 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Zap timeout (s)"), &data.ZapTimeout));
4493 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Channel entry timeout (ms)"), &data.ChannelEntryTimeout, 0));
4494 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Remote control repeat delay (ms)"), &data.RcRepeatDelay, 0));
4495 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Remote control repeat delta (ms)"), &data.RcRepeatDelta, 0));
4496 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Deleted recordings retention (d)"), &data.DeleteRetention, 0));
4497 Add(new cMenuEditChanItem(tr("Setup.Miscellaneous$Initial channel"), &data.InitialChannel, tr("Setup.Miscellaneous$as before")));
4498 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Initial volume"), &data.InitialVolume, -1, 255, tr("Setup.Miscellaneous$as before")));
4499 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Volume steps"), &data.VolumeSteps, 5, 255));
4500 Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Volume linearize"), &data.VolumeLinearize, -20, 20));
4501 Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Channels wrap"), &data.ChannelsWrap));
4502 Add(new cMenuEditStraItem(tr("Setup.Miscellaneous$Show channel names with source"), &data.ShowChannelNamesWithSource, 3, showChannelNamesWithSourceTexts));
4503 Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Open Recordings menu at last replayed"), &data.OpenRecMenuAtLastReplayed));
4504 Add(new cMenuEditBoolItem(tr("Setup.Miscellaneous$Emergency exit"), &data.EmergencyExit));
4506 Display();
4507}
4508
4510{
4511 bool OldSVDRPPeering = data.SVDRPPeering;
4512 bool ModifiedSVDRPSettings = false;
4513 bool ModifiedShowChannelNamesWithSource = false;
4514 if (Key == kOk) {
4515 ModifiedSVDRPSettings = data.SVDRPPeering != Setup.SVDRPPeering || strcmp(data.SVDRPHostName, Setup.SVDRPHostName);
4516 ModifiedShowChannelNamesWithSource = data.ShowChannelNamesWithSource != Setup.ShowChannelNamesWithSource;
4517 }
4519 if (ModifiedShowChannelNamesWithSource) {
4521 for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel))
4522 Channel->UpdateNameSource();
4523 }
4524 if (data.SVDRPPeering != OldSVDRPPeering)
4525 Set();
4526 if (ModifiedSVDRPSettings) {
4528 {
4530 Timers->SetExplicitModify();
4531 if (Timers->StoreRemoteTimers(NULL, NULL))
4532 Timers->SetModified();
4533 }
4535 }
4536 return state;
4537}
4538
4539// --- cMenuSetupPluginItem --------------------------------------------------
4540
4542private:
4544public:
4545 cMenuSetupPluginItem(const char *Name, int Index);
4546 int PluginIndex(void) { return pluginIndex; }
4547 };
4548
4550:cOsdItem(Name)
4551{
4553}
4554
4555// --- cMenuSetupPlugins -----------------------------------------------------
4556
4558public:
4559 cMenuSetupPlugins(void);
4560 virtual eOSState ProcessKey(eKeys Key) override;
4561 };
4562
4564{
4566 SetSection(tr("Plugins"));
4567 SetHasHotkeys();
4568 for (int i = 0; ; i++) {
4570 if (p)
4571 Add(new cMenuSetupPluginItem(hk(cString::sprintf("%s (%s) - %s", p->Name(), p->Version(), p->Description())), i));
4572 else
4573 break;
4574 }
4575}
4576
4578{
4580
4581 if (Key == kOk) {
4582 if (state == osUnknown) {
4584 if (item) {
4586 if (p) {
4587 cMenuSetupPage *menu = p->SetupMenu();
4588 if (menu) {
4589 menu->SetPlugin(p);
4590 return AddSubMenu(menu);
4591 }
4592 Skins.QueueMessage(mtInfo, tr("This plugin has no setup parameters!"));
4593 }
4594 }
4595 }
4596 else if (state == osContinue) {
4597 Store();
4598 // Reinitialize OSD and skin, in case any plugin setup change has an influence on these:
4600 Display();
4601 }
4602 }
4603 return state;
4604}
4605
4606// --- cMenuSetup ------------------------------------------------------------
4607
4608class cMenuSetup : public cOsdMenu {
4609private:
4610 virtual void Set(void);
4611 eOSState Restart(void);
4612public:
4613 cMenuSetup(void);
4614 virtual eOSState ProcessKey(eKeys Key) override;
4615 };
4616
4618:cOsdMenu("")
4619{
4621 Set();
4622}
4623
4625{
4626 Clear();
4627 char buffer[64];
4628 snprintf(buffer, sizeof(buffer), "%s - VDR %s", tr("Setup"), VDRVERSION);
4629 SetTitle(buffer);
4630 SetHasHotkeys();
4631 Add(new cOsdItem(hk(tr("OSD")), osUser1));
4632 Add(new cOsdItem(hk(tr("EPG")), osUser2));
4633 Add(new cOsdItem(hk(tr("DVB")), osUser3));
4634 Add(new cOsdItem(hk(tr("LNB")), osUser4));
4635 Add(new cOsdItem(hk(tr("CAM")), osUser5));
4636 Add(new cOsdItem(hk(tr("Recording")), osUser6));
4637 Add(new cOsdItem(hk(tr("Replay")), osUser7));
4638 Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
4640 Add(new cOsdItem(hk(tr("Plugins")), osUser9));
4641 Add(new cOsdItem(hk(tr("Restart")), osUser10));
4642}
4643
4645{
4646 if (Interface->Confirm(tr("Really restart?")) && ShutdownHandler.ConfirmRestart(true)) {
4647 ShutdownHandler.Exit(1);
4648 return osEnd;
4649 }
4650 return osContinue;
4651}
4652
4654{
4655 int osdLanguage = I18nCurrentLanguage();
4656 eOSState state = cOsdMenu::ProcessKey(Key);
4657
4658 switch (state) {
4659 case osUser1: return AddSubMenu(new cMenuSetupOSD);
4660 case osUser2: return AddSubMenu(new cMenuSetupEPG);
4661 case osUser3: return AddSubMenu(new cMenuSetupDVB);
4662 case osUser4: return AddSubMenu(new cMenuSetupLNB);
4663 case osUser5: return AddSubMenu(new cMenuSetupCAM);
4664 case osUser6: return AddSubMenu(new cMenuSetupRecord);
4665 case osUser7: return AddSubMenu(new cMenuSetupReplay);
4666 case osUser8: return AddSubMenu(new cMenuSetupMisc);
4667 case osUser9: return AddSubMenu(new cMenuSetupPlugins);
4668 case osUser10: return Restart();
4669 default: ;
4670 }
4671 if (I18nCurrentLanguage() != osdLanguage) {
4672 Set();
4673 if (!HasSubMenu())
4674 Display();
4675 }
4676 return state;
4677}
4678
4679// --- cMenuPluginItem -------------------------------------------------------
4680
4682private:
4684public:
4685 cMenuPluginItem(const char *Name, int Index);
4686 int PluginIndex(void) { return pluginIndex; }
4687 };
4688
4690:cOsdItem(Name, osPlugin)
4691{
4693}
4694
4695// --- cMenuMain -------------------------------------------------------------
4696
4697// TRANSLATORS: note the leading and trailing blanks!
4698#define STOP_RECORDING trNOOP(" Stop recording ")
4699
4701
4702cMenuMain::cMenuMain(eOSState State, bool OpenSubMenus)
4703:cOsdMenu("")
4704{
4706 replaying = false;
4707 deletedRecordingsItem = NULL;
4708 stopReplayItem = NULL;
4709 cancelEditingItem = NULL;
4710 stopRecordingItem = NULL;
4712 Set();
4713
4714 // Initial submenus:
4715
4716 cOsdObject *menu = NULL;
4717 switch (State) {
4718 case osSchedule:
4719 if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu))
4720 menu = new cMenuSchedule;
4721 break;
4722 case osChannels:
4723 if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu))
4724 menu = new cMenuChannels;
4725 break;
4726 case osTimers:
4727 if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu))
4728 menu = new cMenuTimers;
4729 break;
4730 case osRecordings:
4731 if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu))
4732 menu = new cMenuRecordings(NULL, 0, OpenSubMenus);
4733 break;
4734 case osSetup: menu = new cMenuSetup; break;
4735 case osCommands: menu = new cMenuCommands(tr("Button$Commands"), &Commands); break;
4736 default: break;
4737 }
4738 if (menu)
4739 if (menu->IsMenu())
4740 AddSubMenu((cOsdMenu *) menu);
4741}
4742
4744{
4746 pluginOsdObject = NULL;
4747 return o;
4748}
4749
4751{
4752 Clear();
4753 SetTitle("VDR");
4754 SetHasHotkeys();
4755
4756 // Basic menu items:
4757
4758 Add(new cOsdItem(hk(tr("Schedule")), osSchedule));
4759 Add(new cOsdItem(hk(tr("Channels")), osChannels));
4760 Add(new cOsdItem(hk(tr("Timers")), osTimers));
4761 Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
4762
4763 // Plugins:
4764
4765 for (int i = 0; ; i++) {
4767 if (p) {
4768 const char *item = p->MainMenuEntry();
4769 if (item)
4770 Add(new cMenuPluginItem(hk(item), i));
4771 }
4772 else
4773 break;
4774 }
4775
4776 // More basic menu items:
4777
4778 Add(new cOsdItem(hk(tr("Setup")), osSetup));
4779 if (Commands.Count())
4780 Add(new cOsdItem(hk(tr("Button$Commands")), osCommands));
4781
4782 Update(true);
4783
4784 Display();
4785}
4786
4787bool cMenuMain::Update(bool Force)
4788{
4789 bool result = false;
4790
4791 // Deleted recordings:
4792 {
4794 bool HasDeletedRecordings = DeletedRecordings->Count();
4795 if (HasDeletedRecordings && !deletedRecordingsItem) {
4796 // TRANSLATORS: note the leading blank!
4797 Add(deletedRecordingsItem = new cOsdItem(tr(" Deleted recordings"), osRecsDel));
4798 result = true;
4799 }
4800 else if (deletedRecordingsItem && !HasDeletedRecordings) {
4801 Del(deletedRecordingsItem->Index());
4802 deletedRecordingsItem = NULL;
4803 result = true;
4804 }
4805 }
4806
4807 // Replay control:
4808 bool NewReplaying = false;
4809 {
4810 cMutexLock ControlMutexLock;
4811 NewReplaying = cControl::Control(ControlMutexLock) != NULL;
4812 }
4813 if (Force || NewReplaying != replaying) {
4814 replaying = NewReplaying;
4815 // Replay control:
4816 if (replaying && !stopReplayItem)
4817 // TRANSLATORS: note the leading blank!
4818 Add(stopReplayItem = new cOsdItem(tr(" Stop replaying"), osStopReplay));
4819 else if (stopReplayItem && !replaying) {
4820 Del(stopReplayItem->Index());
4821 stopReplayItem = NULL;
4822 }
4823 // Color buttons:
4824 SetHelp(!replaying && Setup.RecordKeyHandling ? tr("Button$Record") : NULL, tr("Button$Audio"), replaying || !Setup.PauseKeyHandling ? NULL : tr("Button$Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Button$Resume") : tr("Button$Play"));
4825 result = true;
4826 }
4827
4828 // Editing control:
4829 bool EditingActive = RecordingsHandler.Active();
4830 if (EditingActive && !cancelEditingItem) {
4831 // TRANSLATORS: note the leading blank!
4832 Add(cancelEditingItem = new cOsdItem(tr(" Cancel editing"), osCancelEdit));
4833 result = true;
4834 }
4835 else if (cancelEditingItem && !EditingActive) {
4836 Del(cancelEditingItem->Index());
4837 cancelEditingItem = NULL;
4838 result = true;
4839 }
4840
4841 // Record control:
4843 while (stopRecordingItem) {
4845 Del(stopRecordingItem->Index());
4846 stopRecordingItem = it;
4847 }
4848 const char *s = NULL;
4849 while ((s = cRecordControls::GetInstantId(s)) != NULL) {
4850 cOsdItem *item = new cOsdItem(osStopRecord);
4851 item->SetText(cString::sprintf("%s%s", tr(STOP_RECORDING), s));
4852 Add(item);
4853 if (!stopRecordingItem)
4854 stopRecordingItem = item;
4855 }
4856 result = true;
4857 }
4858
4859 return result;
4860}
4861
4863{
4864 bool HadSubMenu = HasSubMenu();
4865 int osdLanguage = I18nCurrentLanguage();
4866 eOSState state = cOsdMenu::ProcessKey(Key);
4867 HadSubMenu |= HasSubMenu();
4868
4869 cOsdObject *menu = NULL;
4870 switch (state) {
4871 case osSchedule:
4872 if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osSchedule", &menu))
4873 menu = new cMenuSchedule;
4874 else
4875 state = osContinue;
4876 break;
4877 case osChannels:
4878 if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osChannels", &menu))
4879 menu = new cMenuChannels;
4880 else
4881 state = osContinue;
4882 break;
4883 case osTimers:
4884 if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osTimers", &menu))
4885 menu = new cMenuTimers;
4886 else
4887 state = osContinue;
4888 break;
4889 case osRecordings:
4890 if (!cPluginManager::CallFirstService("MainMenuHooksPatch-v1.0::osRecordings", &menu))
4891 menu = new cMenuRecordings;
4892 else
4893 state = osContinue;
4894 break;
4895 case osRecsOpen: menu = new cMenuRecordings(NULL, 0, true); break;
4896 case osRecsDel: menu = new cMenuRecordings(NULL, 0, true, NULL, true); break;
4897 case osSetup: menu = new cMenuSetup; break;
4898 case osCommands: menu = new cMenuCommands(tr("Button$Commands"), &Commands); break;
4899 case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
4900 if (cOsdItem *item = Get(Current())) {
4901 cRecordControls::Stop(item->Text() + strlen(tr(STOP_RECORDING)));
4902 return osEnd;
4903 }
4904 }
4905 break;
4906 case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) {
4907 RecordingsHandler.DelAll();
4908 return osEnd;
4909 }
4910 break;
4911 case osPlugin: {
4913 if (item) {
4915 if (p) {
4916 cOsdObject *menu = p->MainMenuAction();
4917 if (menu) {
4918 if (menu->IsMenu())
4919 return AddSubMenu((cOsdMenu *)menu);
4920 else {
4921 pluginOsdObject = menu;
4922 return osPlugin;
4923 }
4924 }
4925 }
4926 }
4927 state = osEnd;
4928 }
4929 break;
4930 default: switch (Key) {
4931 case kRecord:
4932 case kRed: if (!HadSubMenu)
4933 state = replaying || !Setup.RecordKeyHandling ? osContinue : osRecord;
4934 break;
4935 case kGreen: if (!HadSubMenu) {
4936 cRemote::Put(kAudio, true);
4937 state = osEnd;
4938 }
4939 break;
4940 case kYellow: if (!HadSubMenu)
4941 state = replaying || !Setup.PauseKeyHandling ? osContinue : osPause;
4942 break;
4943 case kBlue: if (!HadSubMenu)
4945 break;
4946 default: break;
4947 }
4948 }
4949 if (menu) {
4950 if (menu->IsMenu())
4951 return AddSubMenu((cOsdMenu *) menu);
4952 pluginOsdObject = menu;
4953 return osPlugin;
4954 }
4955 bool DoDisplay = Update();
4956 if (Key != kNone) {
4957 if (I18nCurrentLanguage() != osdLanguage) {
4958 Set();
4959 if (!HasSubMenu())
4960 DoDisplay = true;
4961 }
4962 }
4963 if (DoDisplay)
4964 Display();
4965 return state;
4966}
4967
4968// --- SetTrackDescriptions --------------------------------------------------
4969
4970void SetTrackDescriptions(int LiveChannel)
4971{
4973 const cComponents *Components = NULL;
4974 if (LiveChannel) {
4976 if (const cChannel *Channel = Channels->GetByNumber(LiveChannel)) {
4978 if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) {
4979 const cEvent *Present = Schedule->GetPresentEvent();
4980 if (Present)
4981 Components = Present->Components();
4982 }
4983 }
4984 }
4985 else if (cReplayControl::NowReplaying()) {
4987 if (const cRecording *Recording = Recordings->GetByName(cReplayControl::NowReplaying()))
4988 Components = Recording->Info()->Components();
4989 }
4990 if (Components) {
4991 int indexAudio = 0;
4992 int indexDolby = 0;
4993 int indexSubtitle = 0;
4994 for (int i = 0; i < Components->NumComponents(); i++) {
4995 const tComponent *p = Components->Component(i);
4996 switch (p->stream) {
4997 case 2: if (p->type == 0x05)
4998 cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, indexDolby++, 0, LiveChannel ? NULL : p->language, p->description);
4999 else
5000 cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, indexAudio++, 0, LiveChannel ? NULL : p->language, p->description);
5001 break;
5002 case 3: cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, indexSubtitle++, 0, LiveChannel ? NULL : p->language, p->description);
5003 break;
5004 case 4: cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, indexDolby++, 0, LiveChannel ? NULL : p->language, p->description);
5005 break;
5006 default: ;
5007 }
5008 }
5009 }
5010}
5011
5012// --- cDisplayChannel -------------------------------------------------------
5013
5015
5016cDisplayChannel::cDisplayChannel(int Number, bool Switched)
5017:cOsdObject(true)
5018{
5019 currentDisplayChannel = this;
5020 group = -1;
5021 withInfo = !Switched || Setup.ShowInfoOnChSwitch;
5023 number = 0;
5024 timeout = Switched || Setup.TimeoutRequChInfo;
5025 cOsdProvider::OsdSizeChanged(osdState); // just to get the current state
5026 positioner = NULL;
5027 channel = NULL;
5028 {
5030 channel = Channels->GetByNumber(Number);
5031 lastPresent = lastFollowing = NULL;
5032 if (channel) {
5034 DisplayInfo();
5035 }
5036 }
5037 if (channel)
5038 displayChannel->Flush();
5039 lastTime.Set();
5040}
5041
5043:cOsdObject(true)
5044{
5045 currentDisplayChannel = this;
5046 group = -1;
5047 number = 0;
5048 timeout = true;
5049 lastPresent = lastFollowing = NULL;
5050 cOsdProvider::OsdSizeChanged(osdState); // just to get the current state
5051 lastTime.Set();
5052 withInfo = Setup.ShowInfoOnChSwitch;
5053 displayChannel = Skins.Current()->DisplayChannel(withInfo);
5054 positioner = NULL;
5055 channel = NULL;
5056 {
5058 channel = Channels->GetByNumber(cDevice::CurrentChannel());
5059 }
5060 ProcessKey(FirstKey);
5061}
5062
5069
5077
5079{
5080 if (withInfo && channel) {
5082 if (const cSchedule *Schedule = Schedules->GetSchedule(channel)) {
5083 const cEvent *Present = Schedule->GetPresentEvent();
5084 const cEvent *Following = Schedule->GetFollowingEvent();
5085 if (Present != lastPresent || Following != lastFollowing) {
5086 SetTrackDescriptions(channel->Number());
5087 displayChannel->SetEvents(Present, Following);
5088 cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, Present ? Present->Title() : NULL, Present ? Present->ShortText() : NULL, Following ? Following->StartTime() : 0, Following ? Following->Title() : NULL, Following ? Following->ShortText() : NULL);
5089 lastPresent = Present;
5090 lastFollowing = Following;
5091 lastTime.Set();
5092 }
5093 }
5094 }
5095}
5096
5098{
5100 displayChannel->SetEvents(NULL, NULL);
5101}
5102
5103const cChannel *cDisplayChannel::NextAvailableChannel(const cChannel *Channel, int Direction)
5104{
5105 if (Direction) {
5106 cControl::Shutdown(); // prevents old channel from being shown too long if GetDevice() takes longer
5107 // and, if decrypted, this removes the now superfluous PIDs from the CAM, too
5109 while (Channel) {
5110 Channel = Direction > 0 ? Channels->Next(Channel) : Channels->Prev(Channel);
5111 if (!Channel && Setup.ChannelsWrap)
5112 Channel = Direction > 0 ? Channels->First() : Channels->Last();
5113 if (Channel && !Channel->GroupSep() && cDevice::GetDevice(Channel, LIVEPRIORITY, true, true))
5114 return Channel;
5115 }
5116 }
5117 return NULL;
5118}
5119
5121{
5123 delete displayChannel;
5124 displayChannel = Skins.Current()->DisplayChannel(withInfo);
5125 }
5126 const cChannel *NewChannel = NULL;
5127 if (Key != kNone)
5128 lastTime.Set();
5129 switch (int(Key)) {
5130 case k0:
5131 if (number == 0) {
5132 // keep the "Toggle channels" function working
5133 cRemote::Put(Key);
5134 return osEnd;
5135 }
5136 case k1 ... k9:
5137 group = -1;
5138 if (number >= 0) {
5140 number = Key - k0;
5141 else
5142 number = number * 10 + Key - k0;
5144 channel = Channels->GetByNumber(number);
5145 Refresh();
5146 withInfo = false;
5147 // Lets see if there can be any useful further input:
5148 int n = channel ? number * 10 : 0;
5149 int m = 10;
5150 const cChannel *ch = channel;
5151 while (ch && (ch = Channels->Next(ch)) != NULL) {
5152 if (!ch->GroupSep()) {
5153 if (n <= ch->Number() && ch->Number() < n + m) {
5154 n = 0;
5155 break;
5156 }
5157 if (ch->Number() > n) {
5158 n *= 10;
5159 m *= 10;
5160 }
5161 }
5162 }
5163 if (n > 0) {
5164 // This channel is the only one that fits the input, so let's take it right away:
5165 NewChannel = channel;
5166 withInfo = true;
5167 number = 0;
5168 Refresh();
5169 }
5170 }
5171 break;
5172 case kLeft|k_Repeat:
5173 case kLeft:
5174 case kRight|k_Repeat:
5175 case kRight:
5176 case kNext|k_Repeat:
5177 case kNext:
5178 case kPrev|k_Repeat:
5179 case kPrev: {
5180 withInfo = false;
5181 number = 0;
5183 if (group < 0) {
5184 if (const cChannel *Channel = Channels->GetByNumber(cDevice::CurrentChannel()))
5185 group = Channel->Index();
5186 }
5187 if (group >= 0) {
5188 int SaveGroup = group;
5189 if (NORMALKEY(Key) == kRight || NORMALKEY(Key) == kNext)
5190 group = Channels->GetNextGroup(group) ;
5191 else
5192 group = Channels->GetPrevGroup(group < 1 ? 1 : group);
5193 if (group < 0)
5194 group = SaveGroup;
5195 channel = Channels->Get(group);
5196 if (channel) {
5197 Refresh();
5198 if (!channel->GroupSep())
5199 group = -1;
5200 }
5201 }
5202 break;
5203 }
5204 case kUp|k_Repeat:
5205 case kUp:
5206 case kDown|k_Repeat:
5207 case kDown:
5208 case kChanUp|k_Repeat:
5209 case kChanUp:
5210 case kChanDn|k_Repeat:
5211 case kChanDn: {
5212 eKeys k = NORMALKEY(Key);
5213 if (const cChannel *Channel = NextAvailableChannel(channel, (k == kUp || k == kChanUp) ? 1 : -1))
5214 channel = Channel;
5215 else if (channel && channel->Number() != cDevice::CurrentChannel())
5216 Key = k; // immediately switches channel when hitting the beginning/end of the channel list with k_Repeat
5217 }
5218 // no break here
5219 case kUp|k_Release:
5220 case kDown|k_Release:
5221 case kChanUp|k_Release:
5222 case kChanDn|k_Release:
5223 case kNext|k_Release:
5224 case kPrev|k_Release:
5225 if (!(Key & k_Repeat) && channel && channel->Number() != cDevice::CurrentChannel())
5226 NewChannel = channel;
5227 withInfo = true;
5228 group = -1;
5229 number = 0;
5230 Refresh();
5231 break;
5232 case kNone:
5233 if (number && Setup.ChannelEntryTimeout && int(lastTime.Elapsed()) > Setup.ChannelEntryTimeout) {
5235 channel = Channels->GetByNumber(number);
5236 if (channel)
5237 NewChannel = channel;
5238 withInfo = true;
5239 number = 0;
5240 Refresh();
5241 lastTime.Set();
5242 }
5243 break;
5244 //TODO
5245 //XXX case kGreen: return osEventNow;
5246 //XXX case kYellow: return osEventNext;
5247 case kOk: {
5249 if (group >= 0) {
5250 channel = Channels->Get(Channels->GetNextNormal(group));
5251 if (channel)
5252 NewChannel = channel;
5253 withInfo = true;
5254 group = -1;
5255 Refresh();
5256 }
5257 else if (number > 0) {
5258 channel = Channels->GetByNumber(number);
5259 if (channel)
5260 NewChannel = channel;
5261 withInfo = true;
5262 number = 0;
5263 Refresh();
5264 }
5265 else {
5266 return osEnd;
5267 }
5268 }
5269 break;
5270 default:
5271 if ((Key & (k_Repeat | k_Release)) == 0) {
5272 cRemote::Put(Key);
5273 return osEnd;
5274 }
5275 };
5276 if (positioner || !timeout || lastTime.Elapsed() < (uint64_t)(Setup.ChannelInfoTime * 1000)) {
5277 {
5280 // makes sure a channel switch through the SVDRP CHAN command is displayed
5281 channel = Channels->GetByNumber(cDevice::CurrentChannel());
5282 Refresh();
5283 lastTime.Set();
5284 }
5285 DisplayInfo();
5286 if (NewChannel) {
5287 SetTrackDescriptions(NewChannel->Number()); // to make them immediately visible in the channel display
5288 Channels->SwitchTo(NewChannel->Number());
5289 SetTrackDescriptions(NewChannel->Number()); // switching the channel has cleared them
5290 channel = NewChannel;
5291 }
5292 const cPositioner *Positioner = cDevice::ActualDevice()->Positioner();
5293 bool PositionerMoving = Positioner && Positioner->IsMoving();
5294 SetNeedsFastResponse(PositionerMoving);
5295 if (!PositionerMoving) {
5296 if (positioner)
5297 lastTime.Set(); // to keep the channel display up a few seconds after the target position has been reached
5298 Positioner = NULL;
5299 }
5300 if (Positioner || positioner) // making sure we call SetPositioner(NULL) if there is a switch from "with" to "without" positioner
5301 displayChannel->SetPositioner(Positioner);
5302 positioner = Positioner;
5303 }
5304 displayChannel->Flush();
5305 return osContinue;
5306 }
5307 return osEnd;
5308}
5309
5310// --- cDisplayVolume --------------------------------------------------------
5311
5312#define VOLUMETIMEOUT 1000 //ms
5313#define MUTETIMEOUT 5000 //ms
5314
5316
5318:cOsdObject(true)
5319{
5320 currentDisplayVolume = this;
5322 displayVolume = Skins.Current()->DisplayVolume();
5323 Show();
5324}
5325
5331
5333{
5335}
5336
5343
5345{
5347 currentDisplayVolume->ProcessKey(Key);
5348}
5349
5351{
5352 switch (int(Key)) {
5353 case kVolUp|k_Repeat:
5354 case kVolUp:
5355 case kVolDn|k_Repeat:
5356 case kVolDn:
5357 Show();
5359 break;
5360 case kMute:
5361 if (cDevice::PrimaryDevice()->IsMute()) {
5362 Show();
5363 timeout.Set(MUTETIMEOUT);
5364 }
5365 else
5366 timeout.Set();
5367 break;
5368 case kNone: break;
5369 default: if ((Key & k_Release) == 0) {
5370 cRemote::Put(Key);
5371 return osEnd;
5372 }
5373 }
5374 return timeout.TimedOut() ? osEnd : osContinue;
5375}
5376
5377// --- cDisplayTracks --------------------------------------------------------
5378
5379#define TRACKTIMEOUT 5000 //ms
5380
5382
5384:cOsdObject(true)
5385{
5388 currentDisplayTracks = this;
5389 numTracks = track = 0;
5392 for (int i = ttAudioFirst; i <= ttDolbyLast; i++) {
5393 const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(i));
5394 if (TrackId && TrackId->id) {
5396 descriptions[numTracks] = strdup(*TrackId->description ? TrackId->description : *TrackId->language ? TrackId->language : *itoa(i));
5397 if (i == CurrentAudioTrack)
5398 track = numTracks;
5399 numTracks++;
5400 }
5401 }
5402 descriptions[numTracks] = NULL;
5403 timeout.Set(TRACKTIMEOUT);
5404 displayTracks = Skins.Current()->DisplayTracks(tr("Button$Audio"), numTracks, descriptions);
5405 Show();
5406}
5407
5409{
5410 delete displayTracks;
5411 currentDisplayTracks = NULL;
5412 for (int i = 0; i < numTracks; i++)
5413 free(descriptions[i]);
5415}
5416
5418{
5419 int ac = IS_AUDIO_TRACK(types[track]) ? audioChannel : -1;
5420 displayTracks->SetTrack(track, descriptions);
5421 displayTracks->SetAudioChannel(ac);
5422 displayTracks->Flush();
5425}
5426
5428{
5429 if (cDevice::PrimaryDevice()->NumAudioTracks() > 0) {
5431 new cDisplayTracks;
5432 return currentDisplayTracks;
5433 }
5434 Skins.QueueMessage(mtWarning, tr("No audio available!"));
5435 return NULL;
5436}
5437
5439{
5441 currentDisplayTracks->ProcessKey(Key);
5442}
5443
5445{
5446 int oldTrack = track;
5447 int oldAudioChannel = audioChannel;
5448 switch (int(Key)) {
5449 case kUp|k_Repeat:
5450 case kUp:
5451 case kDown|k_Repeat:
5452 case kDown:
5453 if (NORMALKEY(Key) == kUp && track > 0)
5454 track--;
5455 else if (NORMALKEY(Key) == kDown && track < numTracks - 1)
5456 track++;
5457 timeout.Set(TRACKTIMEOUT);
5458 break;
5459 case kLeft|k_Repeat:
5460 case kLeft:
5461 case kRight|k_Repeat:
5462 case kRight: if (IS_AUDIO_TRACK(types[track])) {
5463 static int ac[] = { 1, 0, 2 };
5465 if (NORMALKEY(Key) == kLeft && audioChannel > 0)
5466 audioChannel--;
5467 else if (NORMALKEY(Key) == kRight && audioChannel < 2)
5468 audioChannel++;
5470 timeout.Set(TRACKTIMEOUT);
5471 }
5472 break;
5473 case kAudio|k_Repeat:
5474 case kAudio:
5475 if (++track >= numTracks)
5476 track = 0;
5477 timeout.Set(TRACKTIMEOUT);
5478 break;
5479 case kOk:
5480 if (types[track] != cDevice::PrimaryDevice()->GetCurrentAudioTrack())
5481 oldTrack = -1; // make sure we explicitly switch to that track
5482 timeout.Set();
5483 break;
5484 case kNone: break;
5485 default: if ((Key & k_Release) == 0)
5486 return osEnd;
5487 }
5488 if (track != oldTrack || audioChannel != oldAudioChannel)
5489 Show();
5490 if (track != oldTrack) {
5492 Setup.CurrentDolby = IS_DOLBY_TRACK(types[track]);
5493 }
5494 if (audioChannel != oldAudioChannel)
5496 return timeout.TimedOut() ? osEnd : osContinue;
5497}
5498
5499// --- cDisplaySubtitleTracks ------------------------------------------------
5500
5502
5504:cOsdObject(true)
5505{
5507 currentDisplayTracks = this;
5508 numTracks = track = 0;
5510 descriptions[numTracks] = strdup(tr("No subtitles"));
5511 numTracks++;
5512 eTrackType CurrentSubtitleTrack = cDevice::PrimaryDevice()->GetCurrentSubtitleTrack();
5513 for (int i = ttSubtitleFirst; i <= ttSubtitleLast; i++) {
5514 const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(i));
5515 if (TrackId && TrackId->id) {
5517 descriptions[numTracks] = strdup(*TrackId->description ? TrackId->description : *TrackId->language ? TrackId->language : *itoa(i));
5518 if (i == CurrentSubtitleTrack)
5519 track = numTracks;
5520 numTracks++;
5521 }
5522 }
5523 descriptions[numTracks] = NULL;
5524 timeout.Set(TRACKTIMEOUT);
5525 displayTracks = Skins.Current()->DisplayTracks(tr("Button$Subtitles"), numTracks, descriptions);
5526 Show();
5527}
5528
5530{
5531 delete displayTracks;
5532 currentDisplayTracks = NULL;
5533 for (int i = 0; i < numTracks; i++)
5534 free(descriptions[i]);
5536}
5537
5544
5546{
5547 if (cDevice::PrimaryDevice()->NumSubtitleTracks() > 0) {
5550 return currentDisplayTracks;
5551 }
5552 Skins.QueueMessage(mtWarning, tr("No subtitles available!"));
5553 return NULL;
5554}
5555
5557{
5559 currentDisplayTracks->ProcessKey(Key);
5560}
5561
5563{
5564 int oldTrack = track;
5565 switch (int(Key)) {
5566 case kUp|k_Repeat:
5567 case kUp:
5568 case kDown|k_Repeat:
5569 case kDown:
5570 if (NORMALKEY(Key) == kUp && track > 0)
5571 track--;
5572 else if (NORMALKEY(Key) == kDown && track < numTracks - 1)
5573 track++;
5574 timeout.Set(TRACKTIMEOUT);
5575 break;
5576 case kSubtitles|k_Repeat:
5577 case kSubtitles:
5578 if (++track >= numTracks)
5579 track = 0;
5580 timeout.Set(TRACKTIMEOUT);
5581 break;
5582 case kOk:
5583 oldTrack = -1; // make sure we explicitly switch to that track
5584 timeout.Set();
5585 break;
5586 case kNone: break;
5587 default: if ((Key & k_Release) == 0)
5588 return osEnd;
5589 }
5590 if (track != oldTrack) {
5591 Show();
5593 if (track == ttNone && Setup.DisplaySubtitles == SUBTITLES_REWIND) {
5596 }
5597 }
5598 return timeout.TimedOut() ? osEnd : osContinue;
5599}
5600
5601// --- cRecordControl --------------------------------------------------------
5602
5604{
5605 const char *LastReplayed = cReplayControl::LastReplayed(); // must do this before locking schedules!
5606 // Whatever happens here, the timers will be modified in some way...
5607 Timers->SetModified();
5608 cStateKey ChannelsStateKey;
5609 // To create a new timer, we need to make shure there is
5610 // a lock on Channels prior to the Schedules locking below
5611 if (!Timer)
5612 cChannels::GetChannelsRead(ChannelsStateKey);
5613 // We're going to work with an event here, so we need to prevent
5614 // others from modifying any EPG data:
5615 cStateKey SchedulesStateKey;
5616 cSchedules::GetSchedulesRead(SchedulesStateKey);
5617
5618 event = NULL;
5619 fileName = NULL;
5620 recorder = NULL;
5621 device = Device;
5622 if (!device) device = cDevice::PrimaryDevice();//XXX
5623 timer = Timer;
5624 if (!timer) {
5625 timer = new cTimer(true, Pause);
5626 Timers->Add(timer);
5627 instantId = cString::sprintf(cDevice::NumDevices() > 1 ? "%s - %d" : "%s", timer->Channel()->Name(), device->DeviceNumber() + 1);
5628 ChannelsStateKey.Remove();
5629 }
5630 timer->SetPending(true);
5631 timer->SetRecording(true);
5632 event = timer->Event();
5633
5634 if (event || GetEvent())
5635 dsyslog("Title: '%s' Subtitle: '%s'", event->Title(), event->ShortText());
5636 cRecording Recording(timer, event);
5637 fileName = strdup(Recording.FileName());
5638
5639 // crude attempt to avoid duplicate recordings:
5641 isyslog("already recording: '%s'", fileName);
5642 if (Timer) {
5643 timer->SetPending(false);
5644 timer->SetRecording(false);
5645 timer->OnOff();
5646 }
5647 else {
5648 Timers->Del(timer);
5649 if (!LastReplayed) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
5651 }
5652 timer = NULL;
5653 SchedulesStateKey.Remove();
5654 return;
5655 }
5656
5658 isyslog("record %s", fileName);
5659 if (MakeDirs(fileName, true)) {
5660 Recording.WriteInfo(); // we write this *before* attaching the recorder to the device, to make sure the info file is present when the recorder needs to update the fps value!
5661 const cChannel *ch = timer->Channel();
5662 recorder = new cRecorder(fileName, ch, timer->Priority());
5663 if (device->AttachReceiver(recorder)) {
5664 cStatus::MsgRecording(device, Recording.Name(), Recording.FileName(), true);
5665 if (!Timer && !LastReplayed) // an instant recording, maybe from cRecordControls::PauseLiveVideo()
5667 SchedulesStateKey.Remove();
5669 SetRecordingTimerId(fileName, cString::sprintf("%d@%s", timer->Id(), Setup.SVDRPHostName));
5670 Recordings->AddByName(fileName);
5671 return;
5672 }
5673 else
5675 }
5676 else
5677 timer->SetDeferred(DEFERTIMER);
5678 if (!Timer) {
5679 Timers->Del(timer);
5680 timer = NULL;
5681 }
5682 SchedulesStateKey.Remove();
5683}
5684
5686{
5687 Stop();
5688 free(fileName);
5689}
5690
5691#define INSTANT_REC_EPG_LOOKAHEAD 300 // seconds to look into the EPG data for an instant recording
5692
5694{
5695 const cChannel *Channel = timer->Channel();
5696 time_t Time = timer->HasFlags(tfInstant) ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2;
5697 for (int seconds = 0; seconds <= MAXWAIT4EPGINFO; seconds++) {
5698 {
5700 if (const cSchedule *Schedule = Schedules->GetSchedule(Channel)) {
5701 event = Schedule->GetEventAround(Time);
5702 if (event) {
5703 if (seconds > 0)
5704 dsyslog("got EPG info after %d seconds", seconds);
5705 return true;
5706 }
5707 }
5708 }
5709 if (seconds == 0)
5710 dsyslog("waiting for EPG info...");
5711 cCondWait::SleepMs(1000);
5712 }
5713 dsyslog("no EPG info available");
5714 return false;
5715}
5716
5717void cRecordControl::Stop(bool ExecuteUserCommand)
5718{
5719 if (timer) {
5720 bool Finished = timer->HasFlags(tfActive) && !timer->Matches();
5721 if (recorder) {
5722 recorder->Stop();
5723 int Errors = recorder->Errors();
5724 isyslog("timer %s %s with %d error%s", *timer->ToDescr(), Finished ? "finished" : "stopped", Errors, Errors != 1 ? "s" : "");
5725 if (timer->HasFlags(tfAvoid) && Errors == 0 && Finished) {
5726 const char *p = strgetlast(timer->File(), FOLDERDELIMCHAR);
5727 DoneRecordingsPattern.Append(p);
5728 }
5729 }
5731 timer->SetRecording(false);
5732 timer = NULL;
5734 cStatus::MsgRecording(device, NULL, fileName, false);
5735 if (ExecuteUserCommand && Finished)
5737 }
5738}
5739
5741{
5742 if (!recorder || !recorder->IsAttached() || !timer || !timer->Matches(t)) {
5743 if (timer)
5744 timer->SetPending(false);
5745 return false;
5746 }
5747 return true;
5748}
5749
5750// --- cRecordControls -------------------------------------------------------
5751
5754
5755bool cRecordControls::Start(cTimers *Timers, cTimer *Timer, bool Pause)
5756{
5757 static time_t LastNoDiskSpaceMessage = 0;
5758 int FreeMB = 0;
5759 if (Timer) {
5760 AssertFreeDiskSpace(Timer->Priority(), !Timer->Pending());
5761 Timer->SetPending(true);
5762 }
5764 if (FreeMB < MINFREEDISK) {
5765 if (!Timer || time(NULL) - LastNoDiskSpaceMessage > NODISKSPACEDELTA) {
5766 isyslog("not enough disk space to start recording%s%s", Timer ? " timer " : "", Timer ? *Timer->ToDescr() : "");
5767 Skins.QueueMessage(mtWarning, tr("Not enough disk space to start recording!"));
5768 LastNoDiskSpaceMessage = time(NULL);
5769 }
5770 return false;
5771 }
5772 LastNoDiskSpaceMessage = 0;
5773
5774 ChangeState();
5775 cStateKey StateKey;
5776 const cChannels *Channels = cChannels::GetChannelsRead(StateKey);
5777 int ch = Timer ? Timer->Channel()->Number() : cDevice::CurrentChannel();
5778 if (const cChannel *Channel = Channels->GetByNumber(ch)) {
5779 int Priority = Timer ? Timer->Priority() : Pause ? Setup.PausePriority : Setup.DefaultPriority;
5780 cDevice *device = cDevice::GetDevice(Channel, Priority, false);
5781 if (device) {
5782 dsyslog("switching device %d to channel %d %s (%s)", device->DeviceNumber() + 1, Channel->Number(), *Channel->GetChannelID().ToString(), Channel->Name());
5783 if (!device->SwitchChannel(Channel, false)) {
5784 StateKey.Remove();
5785 ShutdownHandler.RequestEmergencyExit();
5786 return false;
5787 }
5788 StateKey.Remove();
5789 Channels = NULL;
5790 if (!Timer || Timer->Matches()) {
5791 for (int i = 0; i < MAXRECORDCONTROLS; i++) {
5792 if (!RecordControls[i]) {
5793 RecordControls[i] = new cRecordControl(device, Timers, Timer, Pause);
5794 return RecordControls[i]->Process(time(NULL));
5795 }
5796 }
5797 }
5798 }
5799 else if (!Timer || !Timer->Pending()) {
5800 isyslog("no free DVB device to record channel %d (%s)!", ch, Channel->Name());
5801 Skins.QueueMessage(mtError, tr("No free DVB device to record!"));
5802 }
5803 }
5804 else
5805 esyslog("ERROR: channel %d not defined!", ch);
5806 if (Channels)
5807 StateKey.Remove();
5808 return false;
5809}
5810
5812{
5814 return Start(Timers, NULL, Pause);
5815}
5816
5817void cRecordControls::Stop(const char *InstantId)
5818{
5820 ChangeState();
5821 for (int i = 0; i < MAXRECORDCONTROLS; i++) {
5822 if (RecordControls[i]) {
5823 const char *id = RecordControls[i]->InstantId();
5824 if (id && strcmp(id, InstantId) == 0) {
5825 cTimer *Timer = RecordControls[i]->Timer();
5826 RecordControls[i]->Stop();
5827 if (Timer) {
5828 Timers->Del(Timer);
5829 isyslog("deleted timer %s", *Timer->ToDescr());
5830 }
5831 break;
5832 }
5833 }
5834 }
5835}
5836
5838{
5839 for (int i = 0; i < MAXRECORDCONTROLS; i++) {
5840 if (RecordControls[i]) {
5841 if (RecordControls[i]->Timer() == Timer) {
5843 ChangeState();
5844 break;
5845 }
5846 }
5847 }
5848}
5849
5851{
5852 Skins.Message(mtStatus, tr("Pausing live video..."));
5853 cReplayControl::SetRecording(NULL); // make sure the new cRecordControl will set cReplayControl::LastReplayed()
5854 if (Start(true)) {
5855 cReplayControl *rc = new cReplayControl(true);
5856 cControl::Launch(rc);
5858 Skins.Message(mtStatus, NULL);
5859 return true;
5860 }
5861 Skins.Message(mtStatus, NULL);
5862 return false;
5863}
5864
5865const char *cRecordControls::GetInstantId(const char *LastInstantId)
5866{
5867 for (int i = 0; i < MAXRECORDCONTROLS; i++) {
5868 if (RecordControls[i]) {
5869 if (!LastInstantId && RecordControls[i]->InstantId())
5870 return RecordControls[i]->InstantId();
5871 if (LastInstantId && LastInstantId == RecordControls[i]->InstantId())
5872 LastInstantId = NULL;
5873 }
5874 }
5875 return NULL;
5876}
5877
5879{
5880 if (FileName) {
5881 for (int i = 0; i < MAXRECORDCONTROLS; i++) {
5882 if (RecordControls[i] && strcmp(RecordControls[i]->FileName(), FileName) == 0)
5883 return RecordControls[i];
5884 }
5885 }
5886 return NULL;
5887}
5888
5890{
5891 for (int i = 0; i < MAXRECORDCONTROLS; i++) {
5892 if (RecordControls[i] && RecordControls[i]->Timer() == Timer)
5893 return RecordControls[i];
5894 }
5895 return NULL;
5896}
5897
5898bool cRecordControls::Process(cTimers *Timers, time_t t)
5899{
5900 bool Result = false;
5901 for (int i = 0; i < MAXRECORDCONTROLS; i++) {
5902 if (RecordControls[i]) {
5903 if (!RecordControls[i]->Process(t)) {
5905 ChangeState();
5906 Result = true;
5907 }
5908 }
5909 }
5910 return Result;
5911}
5912
5914{
5915 for (int i = 0; i < MAXRECORDCONTROLS; i++) {
5916 if (RecordControls[i]) {
5917 if (RecordControls[i]->Timer() && RecordControls[i]->Timer()->Channel() == Channel) {
5918 if (RecordControls[i]->Device()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder
5919 isyslog("stopping recording due to modification of channel %d (%s)", Channel->Number(), Channel->Name());
5920 RecordControls[i]->Stop();
5921 // This will restart the recording, maybe even from a different
5922 // device in case conditional access has changed.
5923 ChangeState();
5924 }
5925 }
5926 }
5927 }
5928}
5929
5931{
5932 for (int i = 0; i < MAXRECORDCONTROLS; i++) {
5933 if (RecordControls[i])
5934 return true;
5935 }
5936 return false;
5937}
5938
5940{
5941 for (int i = 0; i < MAXRECORDCONTROLS; i++)
5943 ChangeState();
5944}
5945
5947{
5948 int NewState = state;
5949 bool Result = State != NewState;
5950 State = state;
5951 return Result;
5952}
5953
5954// --- cAdaptiveSkipper ------------------------------------------------------
5955
5957{
5958 initialValue = NULL;
5959 currentValue = 0;
5960 framesPerSecond = 0;
5961 lastKey = kNone;
5962}
5963
5964void cAdaptiveSkipper::Initialize(int *InitialValue, double FramesPerSecond)
5965{
5966 initialValue = InitialValue;
5967 framesPerSecond = FramesPerSecond;
5968 currentValue = 0;
5969}
5970
5972{
5973 if (!initialValue)
5974 return 0;
5975 if (timeout.TimedOut()) {
5976 currentValue = int(round(*initialValue * framesPerSecond));
5977 lastKey = Key;
5978 }
5979 else if (Key != lastKey) {
5980 currentValue /= 2;
5981 if (Setup.AdaptiveSkipAlternate)
5982 lastKey = Key; // only halve the value when the direction is changed
5983 else
5984 lastKey = kNone; // once the direction has changed, every further call halves the value
5985 }
5986 timeout.Set(Setup.AdaptiveSkipTimeout * 1000);
5987 return max(currentValue, 1);
5988}
5989
5990// --- cReplayControl --------------------------------------------------------
5991
5995
5997:cDvbPlayerControl(fileName, PauseLive)
5998{
6000 currentReplayControl = this;
6001 displayReplay = NULL;
6002 marksModified = false;
6003 visible = modeOnly = shown = displayFrames = false;
6004 lastErrors = 0;
6005 lastCurrent = lastTotal = -1;
6006 lastPlay = lastForward = false;
6007 lastSpeed = -2; // an invalid value
6008 timeoutShow = 0;
6009 timeSearchActive = false;
6010 cRecording Recording(fileName);
6011 cStatus::MsgReplaying(this, Recording.Name(), Recording.FileName(), true);
6012 marks.Load(fileName, Recording.FramesPerSecond(), Recording.IsPesRecording());
6013 SetMarks(&marks);
6014 adaptiveSkipper.Initialize(&Setup.AdaptiveSkipInitial, Recording.FramesPerSecond());
6015 SetTrackDescriptions(false);
6016 if (Setup.ProgressDisplayTime)
6017 ShowTimed(Setup.ProgressDisplayTime);
6018}
6019
6027
6029{
6030 if (timeshiftTimer) {
6032 Timers->SetExplicitModify();
6033 Timers->Del(timeshiftTimer);
6034 Timers->SetModified();
6035 isyslog("deleted timer %s", *timeshiftTimer->ToDescr());
6036 timeshiftTimer = NULL;
6037 }
6038}
6039
6041{
6042 Hide();
6043 cStatus::MsgReplaying(this, NULL, fileName, false);
6044 if (Setup.DelTimeshiftRec && *fileName) {
6046 if (rc && rc->InstantId()) {
6047 if (Active()) {
6048 if (Setup.DelTimeshiftRec == 2 || Interface->Confirm(tr("Delete timeshift recording?"))) {
6049 // At this point somewhere up the call stack there may be a lock on the Channels, so we can't
6050 // lock the Timers here and have to delete this timer later:
6051 timeshiftTimer = rc->Timer();
6052 rc->Stop(false); // don't execute user command
6054 bool Error = false;
6055 {
6057 Recordings->SetExplicitModify();
6058 if (cRecording *Recording = Recordings->GetByName(fileName)) {
6059 if (Recording->Delete()) {
6061 Recordings->Del(Recording, false);
6062 DeletedRecordings->Add(Recording);
6064 Recordings->SetModified();
6065 }
6066 else
6067 Error = true;
6068 }
6069 }
6070 if (Error)
6071 Skins.QueueMessage(mtError, tr("Error while deleting recording!"));
6072 return;
6073 }
6074 }
6075 }
6076 }
6078 cMenuRecordings::SetRecording(NULL); // make sure opening the Recordings menu navigates to the last replayed recording
6079}
6080
6082{
6083 cStateKey StateKey;
6084 marks.Lock(StateKey);
6085 while (cMark *m = marks.First())
6086 marks.Del(m);
6087 StateKey.Remove();
6089}
6090
6091void cReplayControl::SetRecording(const char *FileName)
6092{
6093 fileName = FileName;
6094}
6095
6097{
6098 return currentReplayControl ? *fileName : NULL;
6099}
6100
6102{
6104 if (!Recordings->GetByName(fileName))
6105 fileName = NULL;
6106 return fileName;
6107}
6108
6109void cReplayControl::ClearLastReplayed(const char *FileName)
6110{
6111 if (*fileName && FileName && strcmp(fileName, FileName) == 0)
6112 fileName = NULL;
6113}
6114
6116{
6117 if (modeOnly)
6118 Hide();
6119 if (!visible) {
6120 shown = ShowProgress(true);
6121 timeoutShow = (shown && Seconds > 0) ? time(NULL) + Seconds : 0;
6122 }
6123 else if (timeoutShow && Seconds > 0)
6124 timeoutShow = time(NULL) + Seconds;
6125}
6126
6128{
6129 ShowTimed();
6130}
6131
6133{
6134 if (visible) {
6135 delete displayReplay;
6136 displayReplay = NULL;
6137 SetNeedsFastResponse(false);
6138 visible = false;
6139 modeOnly = false;
6140 lastPlay = lastForward = false;
6141 lastSpeed = -2; // an invalid value
6142 timeSearchActive = false;
6143 timeoutShow = 0;
6144 }
6145 if (marksModified) {
6146 marks.Save();
6147 marksModified = false;
6148 }
6149}
6150
6152{
6153 if (visible || Setup.ShowReplayMode && !cOsd::IsOpen()) {
6154 bool Play, Forward;
6155 int Speed;
6156 if (GetReplayMode(Play, Forward, Speed) && (!visible || Play != lastPlay || Forward != lastForward || Speed != lastSpeed)) {
6157 bool NormalPlay = (Play && Speed == -1);
6158
6159 if (!visible) {
6160 if (NormalPlay)
6161 return; // no need to do indicate ">" unless there was a different mode displayed before
6162 visible = modeOnly = true;
6163 displayReplay = Skins.Current()->DisplayReplay(modeOnly);
6164 }
6165
6166 if (modeOnly && !timeoutShow && NormalPlay)
6167 timeoutShow = time(NULL) + MODETIMEOUT;
6168 displayReplay->SetMode(Play, Forward, Speed);
6169 lastPlay = Play;
6171 lastSpeed = Speed;
6172 }
6173 }
6174}
6175
6177{
6178 int Current, Total;
6179 if (!(Initial || updateTimer.TimedOut()))
6180 return visible;
6181 if (GetFrameNumber(Current, Total) && Total > 0) {
6182 if (!visible) {
6183 displayReplay = Skins.Current()->DisplayReplay(modeOnly);
6184 displayReplay->SetMarks(&marks);
6185 displayReplay->SetErrors(GetErrors());
6187 visible = true;
6188 }
6189 if (Initial) {
6190 if (*fileName) {
6192 if (const cRecording *Recording = Recordings->GetByName(fileName))
6193 displayReplay->SetRecording(Recording);
6194 }
6195 lastCurrent = lastTotal = -1;
6196 }
6197 const cErrors *Errors = GetErrors();
6198 int NumErrors = Errors ? Errors->Size() : 0;
6199 if (Current != lastCurrent || Total != lastTotal || NumErrors != lastErrors) {
6200 if (Setup.ShowRemainingTime || Total != lastTotal) {
6201 int Index = Total;
6202 if (Setup.ShowRemainingTime)
6203 Index = Current - Index;
6204 displayReplay->SetTotal(IndexToHMSF(Index, false, FramesPerSecond()));
6205 }
6206 displayReplay->SetProgress(Current, Total);
6207 displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, FramesPerSecond()));
6208 displayReplay->SetErrors(Errors);
6209 displayReplay->Flush();
6210 lastCurrent = Current;
6211 lastTotal = Total;
6212 lastErrors = NumErrors;
6213 }
6214 ShowMode();
6216 return true;
6217 }
6218 return false;
6219}
6220
6222{
6223 char buf[64];
6224 // TRANSLATORS: note the trailing blank!
6225 strcpy(buf, tr("Jump: "));
6226 int len = strlen(buf);
6227 char h10 = '0' + (timeSearchTime >> 24);
6228 char h1 = '0' + ((timeSearchTime & 0x00FF0000) >> 16);
6229 char m10 = '0' + ((timeSearchTime & 0x0000FF00) >> 8);
6230 char m1 = '0' + (timeSearchTime & 0x000000FF);
6231 char ch10 = timeSearchPos > 3 ? h10 : '-';
6232 char ch1 = timeSearchPos > 2 ? h1 : '-';
6233 char cm10 = timeSearchPos > 1 ? m10 : '-';
6234 char cm1 = timeSearchPos > 0 ? m1 : '-';
6235 sprintf(buf + len, "%c%c:%c%c", ch10, ch1, cm10, cm1);
6236 displayReplay->SetJump(buf);
6237}
6238
6240{
6241#define STAY_SECONDS_OFF_END 10
6242 int Seconds = (timeSearchTime >> 24) * 36000 + ((timeSearchTime & 0x00FF0000) >> 16) * 3600 + ((timeSearchTime & 0x0000FF00) >> 8) * 600 + (timeSearchTime & 0x000000FF) * 60;
6243 int Current = int(round(lastCurrent / FramesPerSecond()));
6244 int Total = int(round(lastTotal / FramesPerSecond()));
6245 switch (Key) {
6246 case k0 ... k9:
6247 if (timeSearchPos < 4) {
6248 timeSearchTime <<= 8;
6249 timeSearchTime |= Key - k0;
6250 timeSearchPos++;
6252 }
6253 break;
6254 case kFastRew:
6255 case kLeft:
6256 case kFastFwd:
6257 case kRight: {
6258 int dir = ((Key == kRight || Key == kFastFwd) ? 1 : -1);
6259 if (dir > 0)
6260 Seconds = min(Total - Current - STAY_SECONDS_OFF_END, Seconds);
6261 SkipSeconds(Seconds * dir);
6262 timeSearchActive = false;
6263 }
6264 break;
6265 case kPlayPause:
6266 case kPlay:
6267 case kUp:
6268 case kPause:
6269 case kDown:
6270 case kOk:
6271 if (timeSearchPos > 0) {
6272 Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds);
6273 bool Still = Key == kDown || Key == kPause || Key == kOk;
6274 Goto(SecondsToFrames(Seconds, FramesPerSecond()), Still);
6275 }
6276 timeSearchActive = false;
6277 break;
6278 default:
6279 if (!(Key & k_Flags)) // ignore repeat/release keys
6280 timeSearchActive = false;
6281 break;
6282 }
6283
6284 if (!timeSearchActive) {
6285 if (timeSearchHide)
6286 Hide();
6287 else
6288 displayReplay->SetJump(NULL);
6289 ShowMode();
6290 }
6291}
6292
6294{
6296 timeSearchHide = false;
6297 if (modeOnly)
6298 Hide();
6299 if (!visible) {
6300 Show();
6301 if (visible)
6302 timeSearchHide = true;
6303 else
6304 return;
6305 }
6306 timeoutShow = 0;
6308 timeSearchActive = true;
6309}
6310
6312{
6313 int Current, Total;
6314 if (GetIndex(Current, Total, true)) {
6315 lastCurrent = -1; // triggers redisplay
6316 cStateKey StateKey;
6317 marks.Lock(StateKey);
6318 if (cMark *m = marks.Get(Current))
6319 marks.Del(m);
6320 else {
6321 marks.Add(Current);
6322 bool Play, Forward;
6323 int Speed;
6324 if (Setup.PauseOnMarkSet || GetReplayMode(Play, Forward, Speed) && !Play) {
6325 Goto(Current, true);
6326 displayFrames = true;
6327 }
6328 }
6329 StateKey.Remove();
6330 ShowTimed(2);
6331 marksModified = true;
6333 }
6334}
6335
6337{
6338 int Current, Total;
6339 if (GetIndex(Current, Total)) {
6340 if (marks.Count()) {
6341 if (cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current)) {
6342 if (!Setup.PauseOnMarkJump) {
6343 bool Playing, Fwd;
6344 int Speed;
6345 if (GetReplayMode(Playing, Fwd, Speed) && Playing && Forward && m->Position() < Total - SecondsToFrames(3, FramesPerSecond())) {
6346 Goto(m->Position());
6347 return;
6348 }
6349 }
6350 Goto(m->Position(), true);
6351 displayFrames = true;
6352 return;
6353 }
6354 }
6355 // There are either no marks at all, or we already were at the first or last one,
6356 // so jump to the very beginning or end:
6357 Goto(Forward ? Total : 0, true);
6358 }
6359}
6360
6361void cReplayControl::MarkMove(int Frames, bool MarkRequired)
6362{
6363 int Current, Total;
6364 if (GetIndex(Current, Total)) {
6365 bool Play, Forward;
6366 int Speed;
6367 GetReplayMode(Play, Forward, Speed);
6368 cMark *m = marks.Get(Current);
6369 if (!Play && m) {
6370 displayFrames = true;
6371 cMark *m2;
6372 if (Frames > 0) {
6373 // Handle marks at the same offset:
6374 while ((m2 = marks.Next(m)) != NULL && m2->Position() == m->Position())
6375 m = m2;
6376 // Don't skip the next mark:
6377 if ((m2 = marks.Next(m)) != NULL)
6378 Frames = min(Frames, m2->Position() - m->Position() - 1);
6379 }
6380 else {
6381 // Handle marks at the same offset:
6382 while ((m2 = marks.Prev(m)) != NULL && m2->Position() == m->Position())
6383 m = m2;
6384 // Don't skip the next mark:
6385 if ((m2 = marks.Prev(m)) != NULL)
6386 Frames = -min(-Frames, m->Position() - m2->Position() - 1);
6387 }
6388 int p = SkipFrames(Frames);
6389 m->SetPosition(p);
6390 Goto(m->Position(), true);
6391 marksModified = true;
6393 }
6394 else if (!MarkRequired)
6395 Goto(SkipFrames(Frames), !Play);
6396 }
6397}
6398
6400{
6401 const cErrors *Errors = GetErrors();
6402 int NumErrors = Errors ? Errors->Size() : 0;
6403 if (NumErrors > 0) {
6404 int Current, Total;
6405 if (GetIndex(Current, Total)) {
6406 if (Forward) {
6407 int Offset = 0;
6408 for (int i = 0; i < NumErrors; i++) {
6409 int Position = Errors->At(i);
6410 if (Position > Current + Offset) {
6411 int NextIFrame = SkipFrames(Position - Current) + Offset; // this takes us to the I-frame at or right after Position
6412 if (NextIFrame > Position) {
6413 if (SkipFrames(Offset + 1) == NextIFrame) { // means Current is the I-frame right before Position
6414 Offset = NextIFrame - Current;
6415 continue;
6416 }
6417 }
6418 Goto(Position, true); // this takes us to the I-frame at or right before Position
6419 return;
6420 }
6421 }
6422 if (Current < Total)
6423 Goto(Total, true);
6424 }
6425 else {
6426 for (int i = NumErrors - 1; i >= 0; i--) {
6427 if (Errors->At(i) < Current) {
6428 int Position = Errors->At(i);
6429 Goto(Position, true); // this takes us to the I-frame at or right before Position
6430 return;
6431 }
6432 }
6433 if (Current > 0)
6434 Goto(0, true);
6435 }
6436 }
6437 }
6438}
6439
6441{
6442 if (*fileName) {
6443 Hide();
6444 if (!RecordingsHandler.GetUsage(fileName)) {
6445 if (!marks.Count())
6446 Skins.QueueMessage(mtError, tr("No editing marks defined!"));
6447 else if (!marks.GetNumSequences())
6448 Skins.QueueMessage(mtError, tr("No editing sequences defined!"));
6449 else if (access(cCutter::EditedFileName(fileName), F_OK) == 0 && !Interface->Confirm(tr("Edited version already exists - overwrite?")))
6450 ;
6452 Skins.QueueMessage(mtError, tr("Not enough free disk space to start editing process!"));
6453 else if (!RecordingsHandler.Add(ruCut, fileName))
6454 Skins.QueueMessage(mtError, tr("Can't start editing process!"));
6455 else
6456 Skins.QueueMessage(mtInfo, tr("Editing process started"));
6457 }
6458 else
6459 Skins.QueueMessage(mtError, tr("Editing process already active!"));
6460 ShowMode();
6461 }
6462}
6463
6465{
6466 int Current, Total;
6467 if (GetIndex(Current, Total)) {
6468 cMark *m = marks.Get(Current);
6469 if (!m)
6470 m = marks.GetNext(Current);
6471 if (m) {
6472 if ((m->Index() & 0x01) != 0 && !Setup.SkipEdited) // when skipping edited parts we also need to jump to end marks
6473 m = marks.Next(m);
6474 if (m)
6476 }
6477 }
6478}
6479
6481{
6483 if (const cRecording *Recording = Recordings->GetByName(cReplayControl::LastReplayed()))
6484 return new cMenuRecording(Recording, false);
6485 return NULL;
6486}
6487
6489{
6491 if (const cRecording *Recording = Recordings->GetByName(LastReplayed()))
6492 return Recording;
6493 return NULL;
6494}
6495
6497{
6498 if (!Active())
6499 return osRecordings;
6500 if (Key == kNone && !marksModified)
6501 marks.Update();
6502 if (visible) {
6503 if (timeoutShow && time(NULL) > timeoutShow) {
6504 Hide();
6505 ShowMode();
6506 timeoutShow = 0;
6507 }
6508 else if (modeOnly)
6509 ShowMode();
6510 else
6512 }
6513 bool DisplayedFrames = displayFrames;
6514 displayFrames = false;
6515 if (timeSearchActive && Key != kNone) {
6516 TimeSearchProcess(Key);
6517 return osContinue;
6518 }
6519 if (Key == kPlayPause) {
6520 bool Play, Forward;
6521 int Speed;
6522 GetReplayMode(Play, Forward, Speed);
6523 if (Speed >= 0)
6524 Key = Play ? kPlay : kPause;
6525 else
6526 Key = Play ? kPause : kPlay;
6527 }
6528 bool DoShowMode = true;
6529 switch (int(Key)) {
6530 // Positioning:
6531 case kPlay:
6532 case kUp: Play(); break;
6533 case kPause:
6534 case kDown: Pause(); break;
6535 case kFastRew|k_Release:
6536 case kLeft|k_Release:
6537 if (Setup.MultiSpeedMode) break;
6538 case kFastRew:
6539 case kLeft: Backward(); break;
6540 case kFastFwd|k_Release:
6541 case kRight|k_Release:
6542 if (Setup.MultiSpeedMode) break;
6543 case kFastFwd:
6544 case kRight: Forward(); break;
6545 case kRed: TimeSearch(); break;
6546 case kGreen|k_Repeat:
6547 SkipSeconds(-Setup.SkipSecondsRepeat); break;
6548 case kGreen: SkipSeconds(-Setup.SkipSeconds); break;
6549 case kYellow|k_Repeat:
6550 SkipSeconds(Setup.SkipSecondsRepeat); break;
6551 case kYellow: SkipSeconds(Setup.SkipSeconds); break;
6552 case kStop:
6553 case kBlue: Stop();
6554 return osEnd;
6555 default: {
6556 DoShowMode = false;
6557 switch (int(Key)) {
6558 // Editing:
6559 case kMarkToggle: MarkToggle(); break;
6560 case kPrev|k_Repeat:
6561 case kPrev: if (Setup.AdaptiveSkipPrevNext) {
6562 MarkMove(-adaptiveSkipper.GetValue(RAWKEY(Key)), false);
6563 break;
6564 }
6565 // fall through...
6567 case kMarkJumpBack: MarkJump(false); break;
6568 case kNext|k_Repeat:
6569 case kNext: if (Setup.AdaptiveSkipPrevNext) {
6570 MarkMove(+adaptiveSkipper.GetValue(RAWKEY(Key)), false);
6571 break;
6572 }
6573 // fall through...
6575 case kMarkJumpForward: MarkJump(true); break;
6577 case kMarkMoveBack: MarkMove(-1, true); break;
6579 case kMarkMoveForward: MarkMove(+1, true); break;
6581 case kMarkSkipBack: MarkMove(-adaptiveSkipper.GetValue(RAWKEY(Key)), false); break;
6583 case kMarkSkipForward: MarkMove(+adaptiveSkipper.GetValue(RAWKEY(Key)), false); break;
6584 case kChanUp|k_Repeat:
6585 case kChanUp: ErrorJump(true); break;
6586 case kChanDn|k_Repeat:
6587 case kChanDn: ErrorJump(false); break;
6588 case kEditCut: EditCut(); break;
6589 case kEditTest: EditTest(); break;
6590 default: {
6591 displayFrames = DisplayedFrames;
6592 switch (Key) {
6593 // Menu control:
6594 case kOk: if (visible && !modeOnly) {
6595 Hide();
6596 DoShowMode = true;
6597 }
6598 else
6599 Show();
6600 break;
6601 case kBack: Stop();
6602 return osRecordings;
6603 default: return osUnknown;
6604 }
6605 }
6606 }
6607 }
6608 }
6609 if (DoShowMode)
6610 ShowMode();
6611 return osContinue;
6612}
cString ChannelString(const cChannel *Channel, int Number)
Definition channels.c:1140
#define CA_ENCRYPTED_MIN
Definition channels.h:44
#define CA_FTA
Definition channels.h:39
#define LOCK_CHANNELS_READ
Definition channels.h:270
#define LOCK_CHANNELS_WRITE
Definition channels.h:271
cCamSlots CamSlots
Definition ci.c:2838
@ msReady
Definition ci.h:170
@ msPresent
Definition ci.h:170
@ msReset
Definition ci.h:170
double framesPerSecond
Definition menu.h:292
cTimeMs timeout
Definition menu.h:294
cAdaptiveSkipper(void)
Definition menu.c:5956
eKeys lastKey
Definition menu.h:293
void Initialize(int *InitialValue, double FramesPerSecond)
Definition menu.c:5964
int * initialValue
Definition menu.h:290
int currentValue
Definition menu.h:291
int GetValue(eKeys Key)
Definition menu.c:5971
Definition ci.h:232
int Priority(void)
Returns the priority of the device this slot is currently assigned to, or IDLEPRIORITY if it is not a...
Definition ci.c:2656
virtual bool EnterMenu(void)
Requests the CAM in this slot to start its menu.
Definition ci.c:2468
virtual bool HasUserIO(void)
Returns true if there is a pending user interaction, which shall be retrieved via GetMenu() or GetEnq...
Definition ci.c:2462
virtual bool Assign(cDevice *Device, bool Query=false)
Assigns this CAM slot to the given Device, if this is possible.
Definition ci.c:2221
cDevice * Device(void)
Returns the device this CAM slot is currently assigned to.
Definition ci.h:332
virtual bool Reset(void)
Resets the CAM in this slot.
Definition ci.c:2375
virtual void StartActivation(void)
Puts the CAM in this slot into a mode where an inserted smart card can be activated.
Definition ci.c:2398
virtual bool IsActivating(void)
Returns true if this CAM slot is currently activating a smart card.
Definition ci.c:2424
virtual bool CanActivate(void)
Returns true if there is a CAM in this slot that can be put into activation mode.
Definition ci.c:2393
cCamSlot * MtdSpawn(void)
If this CAM slot can do MTD ("Multi Transponder Decryption"), a call to this function returns a cMtdC...
Definition ci.c:2213
int SlotNumber(void)
Returns the number of this CAM slot within the whole system.
Definition ci.h:344
virtual void CancelActivation(void)
Cancels a previously started activation (if any).
Definition ci.c:2413
int Number(void) const
Definition channels.h:179
const char * Name(void) const
Definition channels.c:121
bool GroupSep(void) const
Definition channels.h:181
const char * Provider(void) const
Definition channels.h:147
static cChannels * GetChannelsWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of channels for write access.
Definition channels.c:861
bool HasUniqueChannelID(const cChannel *NewChannel, const cChannel *OldChannel=NULL) const
Definition channels.c:1053
static int MaxNumber(void)
Definition channels.h:249
int GetPrevNormal(int Idx) const
Get previous normal channel (not group).
Definition channels.c:930
static const cChannels * GetChannelsRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of channels for read access.
Definition channels.c:856
void ReNumber(void)
Recalculate 'number' based on channel type.
Definition channels.c:938
const cChannel * GetByNumber(int Number, int SkipGap=0) const
Definition channels.c:983
void SetModifiedByUser(void)
Definition channels.c:1093
const cChannel * GetByChannelID(tChannelID ChannelID, bool TryWithoutRid=false, bool TryWithoutPolarization=false) const
Definition channels.c:1011
bool SwitchTo(int Number) const
Definition channels.c:1063
void Del(cChannel *Channel)
Delete the given Channel from the list.
Definition channels.c:975
int GetNextNormal(int Idx) const
Get next normal channel (not group).
Definition channels.c:922
Definition ci.h:119
tComponent * Component(int Index) const
Definition epg.h:65
int NumComponents(void) const
Definition epg.h:62
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
Definition thread.c:73
static cControl * Control(cMutexLock &MutexLock, bool Hidden=false)
Returns the current replay control (if any) in case it is currently visible.
Definition player.c:73
double FramesPerSecond(void) const
Definition player.h:114
static void Shutdown(void)
Definition player.c:99
static void Attach(void)
Definition player.c:86
static void Launch(cControl *Control)
Definition player.c:79
static cString EditedFileName(const char *FileName)
Returns the full path name of the edited version of the recording with the given FileName.
Definition cutter.c:715
void SetKeepTracks(bool KeepTracks)
Controls whether the current audio and subtitle track settings shall be kept as they currently are,...
Definition device.h:613
bool SetCurrentSubtitleTrack(eTrackType Type, bool Manual=false)
Sets the current subtitle track to the given Type.
Definition device.c:1186
virtual const cPositioner * Positioner(void) const
Returns a pointer to the positioner (if any) this device has used to move the satellite dish to the r...
Definition device.c:780
static cDevice * ActualDevice(void)
Returns the actual receiving device in case of Transfer Mode, or the primary device otherwise.
Definition device.c:222
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition device.h:148
eTrackType GetCurrentSubtitleTrack(void) const
Definition device.h:597
static cDevice * GetDevice(int Index)
Gets the device with the given Index.
Definition device.c:230
eTrackType GetCurrentAudioTrack(void) const
Definition device.h:593
bool SwitchChannel(const cChannel *Channel, bool LiveView)
Switches the device to the given Channel, initiating transfer mode if necessary.
Definition device.c:825
int DeviceNumber(void) const
Returns the number of this device (0 ... numDevices - 1).
Definition device.c:167
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
Definition device.h:371
void StopReplay(void)
Stops the current replay session (if any).
Definition device.c:1434
int GetAudioChannel(void)
Gets the current audio channel, which is stereo (0), mono left (1) or mono right (2).
Definition device.c:1064
void EnsureAudioTrack(bool Force=false)
Makes sure an audio track is selected that is actually available.
Definition device.c:1227
const tTrackId * GetTrack(eTrackType Type)
Returns a pointer to the given track id, or NULL if Type is not less than ttMaxTrackTypes.
Definition device.c:1143
static void SetCurrentChannel(int ChannelNumber)
Sets the number of the current channel on the primary device, without actually switching to it.
Definition device.h:373
void SetAudioChannel(int AudioChannel)
Sets the audio channel to stereo (0), mono left (1) or mono right (2).
Definition device.c:1070
static int NumDevices(void)
Returns the total number of devices.
Definition device.h:129
virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat)
Sets the video display format to the given one (only useful if this device has an MPEG decoder).
Definition device.c:506
virtual void SetVideoFormat(bool VideoFormat16_9)
Sets the output video format to either 16:9 or 4:3 (only useful if this device has an MPEG decoder).
Definition device.c:529
void ClrAvailableTracks(bool DescriptionsOnly=false, bool IdsOnly=false)
Clears the list of currently available tracks.
Definition device.c:1091
void EnsureSubtitleTrack(void)
Makes sure one of the preferred language subtitle tracks is selected.
Definition device.c:1260
static int CurrentVolume(void)
Definition device.h:648
bool SetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language=NULL, const char *Description=NULL)
Sets the track of the given Type and Index to the given values.
Definition device.c:1114
bool SetCurrentAudioTrack(eTrackType Type)
Sets the current audio track to the given Type.
Definition device.c:1168
cTimeMs lastTime
Definition menu.h:128
const cChannel * channel
Definition menu.h:133
const cEvent * lastPresent
Definition menu.h:134
void DisplayChannel(void)
Definition menu.c:5070
const cEvent * lastFollowing
Definition menu.h:135
bool timeout
Definition menu.h:130
cSkinDisplayChannel * displayChannel
Definition menu.h:125
bool withInfo
Definition menu.h:127
virtual ~cDisplayChannel() override
Definition menu.c:5063
void Refresh(void)
Definition menu.c:5097
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:5120
const cPositioner * positioner
Definition menu.h:132
static cDisplayChannel * currentDisplayChannel
Definition menu.h:136
void DisplayInfo(void)
Definition menu.c:5078
cDisplayChannel(int Number, bool Switched)
Definition menu.c:5016
const cChannel * NextAvailableChannel(const cChannel *Channel, int Direction)
Definition menu.c:5103
virtual void Show(void) override
Definition menu.c:5538
cSkinDisplayTracks * displayTracks
Definition menu.h:183
cDisplaySubtitleTracks(void)
Definition menu.c:5503
static void Process(eKeys Key)
Definition menu.c:5556
char * descriptions[ttMaxTrackTypes+1]
Definition menu.h:186
eTrackType types[ttMaxTrackTypes]
Definition menu.h:185
static cDisplaySubtitleTracks * currentDisplayTracks
Definition menu.h:188
static cDisplaySubtitleTracks * Create(void)
Definition menu.c:5545
virtual ~cDisplaySubtitleTracks() override
Definition menu.c:5529
eOSState ProcessKey(eKeys Key)
Definition menu.c:5562
char * descriptions[ttMaxTrackTypes+1]
Definition menu.h:168
static cDisplayTracks * Create(void)
Definition menu.c:5427
cDisplayTracks(void)
Definition menu.c:5383
cTimeMs timeout
Definition menu.h:166
eOSState ProcessKey(eKeys Key)
Definition menu.c:5444
static cDisplayTracks * currentDisplayTracks
Definition menu.h:170
virtual void Show(void) override
Definition menu.c:5417
cSkinDisplayTracks * displayTracks
Definition menu.h:165
int numTracks
Definition menu.h:169
int audioChannel
Definition menu.h:169
static void Process(eKeys Key)
Definition menu.c:5438
virtual ~cDisplayTracks() override
Definition menu.c:5408
eTrackType types[ttMaxTrackTypes]
Definition menu.h:167
static cDisplayVolume * Create(void)
Definition menu.c:5337
cSkinDisplayVolume * displayVolume
Definition menu.h:151
virtual void Show(void) override
Definition menu.c:5332
eOSState ProcessKey(eKeys Key)
Definition menu.c:5350
cTimeMs timeout
Definition menu.h:152
static void Process(eKeys Key)
Definition menu.c:5344
cDisplayVolume(void)
Definition menu.c:5317
static cDisplayVolume * currentDisplayVolume
Definition menu.h:153
virtual ~cDisplayVolume() override
Definition menu.c:5326
static bool BondDevices(const char *Bondings)
Bonds the devices as defined in the given Bondings string.
Definition dvbdevice.c:2042
void SetMarks(const cMarks *Marks)
Definition dvbplayer.c:1016
bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false)
Definition dvbplayer.c:1078
const cErrors * GetErrors(void)
Definition dvbplayer.c:1071
void SkipSeconds(int Seconds)
Definition dvbplayer.c:1058
cDvbPlayerControl(const char *FileName, bool PauseLive=false)
Definition dvbplayer.c:1004
bool GetReplayMode(bool &Play, bool &Forward, int &Speed)
Definition dvbplayer.c:1096
void Pause(void)
Definition dvbplayer.c:1034
int SkipFrames(int Frames)
Definition dvbplayer.c:1064
void Goto(int Index, bool Still=false)
Definition dvbplayer.c:1101
void Stop(void)
Definition dvbplayer.c:1027
void Forward(void)
Definition dvbplayer.c:1046
bool Active(void)
Definition dvbplayer.c:1022
bool GetFrameNumber(int &Current, int &Total)
Definition dvbplayer.c:1087
void Play(void)
Definition dvbplayer.c:1040
void Backward(void)
Definition dvbplayer.c:1052
static void SetupChanged(void)
Definition epg.h:74
const char * ShortText(void) const
Definition epg.h:109
const cComponents * Components(void) const
Definition epg.h:111
time_t StartTime(void) const
Definition epg.h:114
tChannelID ChannelID(void) const
Definition epg.c:155
const char * Title(void) const
Definition epg.h:108
static bool GetAvailableFontNames(cStringList *FontNames, bool Monospaced=false)
Queries the font configuration for a list of available font names, which is returned in FontNames.
Definition font.c:440
bool Contains(const cListObject *Object) const
If a pointer to an object contained in this list has been obtained while holding a lock,...
Definition tools.c:2282
void Del(cListObject *Object, bool DeleteObject=true)
Definition tools.c:2226
virtual void Move(int From, int To)
Definition tools.c:2242
void SetModified(void)
Unconditionally marks this list as modified.
Definition tools.c:2296
void SetSyncStateKey(cStateKey &StateKey)
When making changes to this list (while holding a write lock) that shall not affect some other code t...
Definition tools.h:612
int Count(void) const
Definition tools.h:640
void Add(cListObject *Object, cListObject *After=NULL)
Definition tools.c:2194
void Sort(void)
Definition tools.c:2318
cListObject(const cListObject &ListObject)
Definition tools.h:547
cListObject * Prev(void) const
Definition tools.h:559
int Index(void) const
Definition tools.c:2114
cListObject * Next(void) const
Definition tools.h:560
Definition tools.h:644
const cOsdItem * First(void) const
Definition tools.h:656
cList(const char *NeedsLocking=NULL)
Definition tools.h:646
const T * Next(const T *Object) const
< Returns the element immediately before Object in this list, or NULL if Object is the first element ...
Definition tools.h:663
const cOsdItem * Get(int Index) const
Definition tools.h:653
void SetPosition(int Position)
Definition recording.h:414
int Position(void) const
Definition recording.h:412
static bool DeleteMarksFile(const cRecording *Recording)
Definition recording.c:2441
cCamSlot * camSlot
Definition menu.c:2328
void Set(void)
Definition menu.c:2390
eOSState Select(void)
Definition menu.c:2445
char * input
Definition menu.c:2331
void AddMultiLineItem(const char *s)
Definition menu.c:2432
void GenerateTitle(const char *s=NULL)
Definition menu.c:2372
void QueryCam(void)
Definition menu.c:2377
virtual ~cMenuCam() override
Definition menu.c:2360
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:2469
cMenuCam(cCamSlot *CamSlot)
Definition menu.c:2345
time_t lastCamExchange
Definition menu.c:2333
cCiEnquiry * ciEnquiry
Definition menu.c:2330
cCiMenu * ciMenu
Definition menu.c:2329
int offset
Definition menu.c:2332
static eChannelSortMode sortMode
Definition menu.c:291
cMenuChannelItem(const cChannel *Channel)
Definition menu.c:306
const cChannel * Channel(void)
Definition menu.c:300
virtual int Compare(const cListObject &ListObject) const override
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
Definition menu.c:314
static void SetSortMode(eChannelSortMode SortMode)
Definition menu.c:295
virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) override
Definition menu.c:343
virtual void Set(void) override
Definition menu.c:327
static eChannelSortMode SortMode(void)
Definition menu.c:297
const cChannel * channel
Definition menu.c:292
static void IncSortMode(void)
Definition menu.c:296
cStateKey channelsStateKey
Definition menu.c:356
eOSState Number(eKeys Key)
Definition menu.c:432
cChannel * GetChannel(int Index)
Definition menu.c:417
void Set(bool Force=false)
Definition menu.c:387
int number
Definition menu.c:357
eOSState Delete(void)
Definition menu.c:488
void Propagate(cChannels *Channels)
Definition menu.c:423
eOSState New(void)
Definition menu.c:480
cMenuChannels(void)
Definition menu.c:375
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:565
virtual void Move(int From, int To) override
Definition menu.c:535
cTimeMs numberTimer
Definition menu.c:358
eOSState Edit(void)
Definition menu.c:469
~cMenuChannels()
Definition menu.c:383
eOSState Switch(void)
Definition menu.c:458
eOSState Execute(void)
Definition menu.c:2258
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:2305
cList< cNestedItem > * commands
Definition menu.h:59
virtual ~cMenuCommands() override
Definition menu.c:2230
bool confirm
Definition menu.h:63
cMenuCommands(const char *Title, cList< cNestedItem > *Commands, const char *Parameters=NULL)
Definition menu.c:2213
cString title
Definition menu.h:61
cString command
Definition menu.h:62
bool Parse(const char *s)
Definition menu.c:2235
char * result
Definition menu.h:64
cString parameters
Definition menu.h:60
cMenuEditCaItem(const char *Name, int *Value)
Definition menu.c:66
eOSState ProcessKey(eKeys Key)
Definition menu.c:82
virtual void Set(void) override
Definition menu.c:72
cStateKey * channelsStateKey
Definition menu.c:164
cChannel data
Definition menu.c:166
void Setup(void)
Definition menu.c:201
cChannel * channel
Definition menu.c:165
cSourceParam * sourceParam
Definition menu.c:167
char name[256]
Definition menu.c:168
cChannel * Channel(void)
Definition menu.c:172
cMenuEditChannel(cStateKey *ChannelsStateKey, cChannel *Channel, bool New=false)
Definition menu.c:176
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:240
cNestedItem * folder
Definition menu.c:704
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:761
cMenuEditFolder(const char *Dir, cList< cNestedItem > *List, cNestedItem *Folder=NULL)
Definition menu.c:713
char name[PATH_MAX]
Definition menu.c:705
eOSState Confirm(void)
Definition menu.c:738
cString GetFolder(void)
Definition menu.c:733
cList< cNestedItem > * list
Definition menu.c:703
cMenuEditIntItem(const char *Name, int *Value, int Min=0, int Max=INT_MAX, const char *MinString=NULL, const char *MaxString=NULL)
Definition menuitems.c:66
virtual void Set(void) override
Definition menuitems.c:81
virtual eOSState ProcessKey(eKeys Key) override
Definition menuitems.c:94
void SetValue(const char *Value)
Definition menuitems.c:37
eOSState ProcessKey(eKeys Key)
Definition menu.c:124
cMenuEditSrcItem(const char *Name, int *Value)
Definition menu.c:109
const cSource * source
Definition menu.c:101
virtual void Set(void) override
Definition menu.c:116
bool addIfConfirmed
Definition menu.h:79
eOSState SetFolder(void)
Definition menu.c:1117
cMenuEditDateItem * firstday
Definition menu.h:85
static const cTimer * addedTimer
Definition menu.h:75
int channel
Definition menu.h:78
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:1148
cTimer data
Definition menu.h:77
void SetHelpKeys(void)
Definition menu.c:1069
cMenuEditStrItem * file
Definition menu.h:83
virtual ~cMenuEditTimer() override
Definition menu.c:1056
cMenuEditStrItem * pattern
Definition menu.h:82
cMenuEditDateItem * day
Definition menu.h:84
static const cTimer * AddedTimer(void)
Definition menu.c:1062
cTimer * timer
Definition menu.h:76
cMenuEditTimer(cTimer *Timer, bool New=false)
Definition menu.c:1017
void SetFirstDayItem(void)
Definition menu.c:1074
char remote[HOST_NAME_MAX]
Definition menu.h:81
cStringList svdrpServerNames
Definition menu.h:80
void SetPatternItem(bool Initial=false)
Definition menu.c:1087
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:1541
const cEvent * event
Definition menu.h:99
virtual void Display(void) override
Definition menu.c:1532
cMenuEvent(const cTimers *Timers, const cChannels *Channels, const cEvent *Event, bool CanSwitch=false, bool Buttons=false)
Definition menu.c:1515
cMenuFolderItem(cNestedItem *Folder)
Definition menu.c:684
cNestedItem * Folder(void)
Definition menu.c:681
cNestedItem * folder
Definition menu.c:677
virtual void Set(void) override
Definition menu.c:691
cMenuFolder(const char *Title, cList< cNestedItem > *List, cNestedItemList *NestedItemList, const char *Dir, const char *Path=NULL)
Definition menu.c:794
eOSState Delete(void)
Definition menu.c:922
int helpKeys
Definition menu.h:41
eOSState New(void)
Definition menu.c:916
cNestedItemList * nestedItemList
Definition menu.h:36
cList< cNestedItem > * list
Definition menu.h:37
eOSState SetFolder(void)
Definition menu.c:950
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:974
cString dir
Definition menu.h:38
void Set(const char *CurrentFolder=NULL)
Definition menu.c:858
void DescendPath(const char *Path)
Definition menu.c:883
cOsdItem * firstFolder
Definition menu.h:39
eOSState Edit(void)
Definition menu.c:938
eOSState Select(bool Open)
Definition menu.c:900
bool editing
Definition menu.h:40
cString GetFolder(void)
Definition menu.c:961
void SetHelpKeys(void)
Definition menu.c:810
cOsdItem * cancelEditingItem
Definition menu.h:111
cOsdItem * stopRecordingItem
Definition menu.h:112
void Set(void)
Definition menu.c:4750
int recordControlsState
Definition menu.h:113
bool replaying
Definition menu.h:108
cOsdItem * stopReplayItem
Definition menu.h:110
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:4862
cOsdItem * deletedRecordingsItem
Definition menu.h:109
bool Update(bool Force=false)
Definition menu.c:4787
cMenuMain(eOSState State=osUnknown, bool OpenSubMenus=false)
Definition menu.c:4702
static cOsdObject * pluginOsdObject
Definition menu.h:114
static cOsdObject * PluginOsdObject(void)
Definition menu.c:4743
eOSState ApplyChanges(void)
Definition menu.c:2611
int pathIsInUse
Definition menu.c:2536
cString oldFolder
Definition menu.c:2532
eOSState Folder(void)
Definition menu.c:2606
cMenuEditStrItem * folderItem
Definition menu.c:2535
eOSState SetFolder(void)
Definition menu.c:2596
cString path
Definition menu.c:2531
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:2646
cMenuPathEdit(const char *Path)
Definition menu.c:2545
char name[NAME_MAX]
Definition menu.c:2534
char folder[PATH_MAX]
Definition menu.c:2533
cMenuPluginItem(const char *Name, int Index)
Definition menu.c:4689
int PluginIndex(void)
Definition menu.c:4686
int pluginIndex
Definition menu.c:4683
bool RefreshRecording(void)
Definition menu.c:2766
cMenuEditStrItem * nameItem
Definition menu.c:2678
const char * actionCancel
Definition menu.c:2682
cString originalFileName
Definition menu.c:2670
eOSState ApplyChanges(void)
Definition menu.c:2851
eOSState Action(void)
Definition menu.c:2796
cMenuEditStrItem * folderItem
Definition menu.c:2677
eOSState SetFolder(void)
Definition menu.c:2781
void Set(void)
Definition menu.c:2721
char name[NAME_MAX]
Definition menu.c:2673
const char * buttonAction
Definition menu.c:2680
cStateKey recordingsStateKey
Definition menu.c:2671
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:2906
const char * doCut
Definition menu.c:2683
eOSState RemoveName(void)
Definition menu.c:2814
void SetHelpKeys(void)
Definition menu.c:2746
cMenuRecordingEdit(const cRecording *Recording)
Definition menu.c:2699
const char * buttonFolder
Definition menu.c:2679
const char * buttonDeleteMarks
Definition menu.c:2681
eOSState DeleteMarks(void)
Definition menu.c:2832
const cRecording * recording
Definition menu.c:2669
eOSState Folder(void)
Definition menu.c:2791
char folder[PATH_MAX]
Definition menu.c:2672
const cRecording * recording
Definition menu.c:3050
int Level(void) const
Definition menu.c:3060
const cRecording * Recording(void) const
Definition menu.c:3062
const char * Name(void) const
Definition menu.c:3059
int Usage(void)
Definition menu.c:3061
void IncrementCounter(bool New)
Definition menu.c:3091
bool IsDirectory(void) const
Definition menu.c:3063
virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) override
Definition menu.c:3105
void Update(cSkinDisplayMenu *DisplayMenu, int Index, bool Current)
Definition menu.c:3099
void SetRecording(const cRecording *Recording)
Definition menu.c:3064
cMenuRecordingItem(const cRecording *Recording, int Level)
Definition menu.c:3069
const cRecording * recording
Definition menu.c:2932
bool withButtons
Definition menu.c:2935
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:2997
bool RefreshRecording(void)
Definition menu.c:2970
cString originalFileName
Definition menu.c:2933
cStateKey recordingsStateKey
Definition menu.c:2934
virtual void Display(void) override
Definition menu.c:2985
cMenuRecording(const cRecording *Recording, bool WithButtons=false)
Definition menu.c:2943
cMenuRecordings(const char *Base=NULL, int Level=0, bool OpenSubMenus=false, const cRecordingFilter *Filter=NULL, bool DelRecMenu=false)
Definition menu.c:3118
bool delRecMenu
Definition menu.h:217
void SetDeleted(const char *FileName)
Definition menu.c:3272
void Set(bool Refresh=false)
Definition menu.c:3186
bool Open(bool OpenSubMenus=false)
Definition menu.c:3293
static cString deletedName
Definition menu.h:221
eOSState Purge(void)
Definition menu.c:3491
eOSState Sort(void)
Definition menu.c:3551
static void SetRecording(const char *FileName)
Definition menu.c:3277
eOSState Info(void)
Definition menu.c:3521
eOSState Play(void)
Definition menu.c:3322
const cRecordingFilter * filter
Definition menu.h:219
static cString fileName
Definition menu.h:220
char * base
Definition menu.h:213
eOSState Rewind(void)
Definition menu.c:3336
cStateKey recordingsStateKey
Definition menu.h:215
eOSState AdjustTitle(eOSState State, bool Redisplay=true)
Definition menu.c:3309
eOSState Restore(void)
Definition menu.c:3455
eOSState Commands(eKeys Key=kNone)
Definition menu.c:3536
cString DirectoryName(void)
Definition menu.c:3282
bool autoRefresh
Definition menu.h:218
static time_t toggleDelRec
Definition menu.h:222
void SetHelpKeys(void)
Definition menu.c:3155
eOSState Delete(void)
Definition menu.c:3401
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:3563
static void SetSortMode(eScheduleSortMode SortMode)
Definition menu.c:1587
const cChannel * channel
Definition menu.c:1581
cMenuScheduleItem(const cTimers *Timers, const cEvent *Event, const cChannel *Channel=NULL, bool WithDate=false)
Definition menu.c:1597
const cTimer * timer
Definition menu.c:1582
const cEvent * event
Definition menu.c:1580
static void IncSortMode(void)
Definition menu.c:1588
virtual int Compare(const cListObject &ListObject) const override
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
Definition menu.c:1608
bool Update(const cTimers *Timers, bool Force=false)
Definition menu.c:1621
static eScheduleSortMode sortMode
Definition menu.c:1578
static eScheduleSortMode SortMode(void)
Definition menu.c:1589
eTimerMatch timerMatch
Definition menu.c:1584
virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) override
Definition menu.c:1651
virtual ~cMenuSchedule() override
Definition menu.c:1910
bool PrepareScheduleAllAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel)
Definition menu.c:2010
bool canSwitch
Definition menu.c:1877
bool PrepareScheduleAllThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel)
Definition menu.c:1951
void SetHelpKeys(void)
Definition menu.c:2042
int helpKeys
Definition menu.c:1878
cStateKey timersStateKey
Definition menu.c:1873
bool next
Definition menu.c:1876
void Set(const cTimers *Timers, const cChannels *Channels, const cChannel *Channel=NULL, bool Force=false)
Definition menu.c:1915
bool Update(void)
Definition menu.c:2029
eOSState Record(void)
Definition menu.c:2076
cMenuSchedule(void)
Definition menu.c:1895
cStateKey schedulesStateKey
Definition menu.c:1874
eOSState Number(void)
Definition menu.c:2067
int scheduleState
Definition menu.c:1875
eOSState Switch(void)
Definition menu.c:2114
bool PrepareScheduleThisThis(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel)
Definition menu.c:1970
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:2131
bool PrepareScheduleThisAll(const cTimers *Timers, const cSchedules *Schedules, const cEvent *Event, const cChannel *Channel)
Definition menu.c:1990
cMenuSetupBase(void)
Definition menu.c:3659
cSetup data
Definition menu.c:3653
virtual void Store(void) override
Definition menu.c:3664
cMenuSetupCAMItem(cCamSlot *CamSlot)
Definition menu.c:4179
bool Changed(void)
Definition menu.c:4186
cCamSlot * camSlot
Definition menu.c:4172
cCamSlot * CamSlot(void)
Definition menu.c:4175
void SetHelpKeys(void)
Definition menu.c:4250
cMenuSetupCAM(void)
Definition menu.c:4235
eOSState Menu(void)
Definition menu.c:4269
eOSState Reset(void)
Definition menu.c:4333
eOSState Activate(void)
Definition menu.c:4296
const char * activationHelp
Definition menu.c:4225
int currentChannel
Definition menu.c:4224
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:4345
const char * updateChannelsTexts[6]
Definition menu.c:3947
int numAudioLanguages
Definition menu.c:3942
cMenuSetupDVB(void)
Definition menu.c:3955
int originalNumSubtitleLanguages
Definition menu.c:3943
void Setup(void)
Definition menu.c:3985
int numSubtitleLanguages
Definition menu.c:3944
const char * standardComplianceTexts[3]
Definition menu.c:3949
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:4015
int originalNumAudioLanguages
Definition menu.c:3941
const char * videoDisplayFormatTexts[3]
Definition menu.c:3946
const char * displaySubtitlesTexts[3]
Definition menu.c:3948
int originalNumLanguages
Definition menu.c:3847
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:3890
int numLanguages
Definition menu.c:3848
void Setup(void)
Definition menu.c:3866
cMenuSetupEPG(void)
Definition menu.c:3855
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:4149
cSatCableNumbers satCableNumbers
Definition menu.c:4094
cMenuSetupLNB(void)
Definition menu.c:4101
void Setup(void)
Definition menu.c:4110
void Set(void)
Definition menu.c:4476
cMenuSetupMisc(void)
Definition menu.c:4463
const char * svdrpPeeringModeTexts[3]
Definition menu.c:4454
cStringList svdrpServerNames
Definition menu.c:4456
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:4509
const char * showChannelNamesWithSourceTexts[3]
Definition menu.c:4455
virtual void Set(void)
Definition menu.c:3722
cStringList fontSmlNames
Definition menu.c:3687
cStringList fontOsdNames
Definition menu.c:3687
const char * recSortDirTexts[2]
Definition menu.c:3677
const char * useSmallFontTexts[3]
Definition menu.c:3675
int numSkins
Definition menu.c:3680
cStringList fontFixNames
Definition menu.c:3687
const char * recSortModeTexts[2]
Definition menu.c:3676
int fontOsdIndex
Definition menu.c:3688
const char * keyColorTexts[4]
Definition menu.c:3678
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:3778
int skinIndex
Definition menu.c:3682
int originalSkinIndex
Definition menu.c:3681
int originalThemeIndex
Definition menu.c:3685
int fontFixIndex
Definition menu.c:3688
const char ** skinDescriptions
Definition menu.c:3683
cThemes themes
Definition menu.c:3684
int themeIndex
Definition menu.c:3686
virtual ~cMenuSetupOSD() override
Definition menu.c:3717
int fontSmlIndex
Definition menu.c:3688
cMenuSetupOSD(void)
Definition menu.c:3696
int osdLanguageIndex
Definition menu.c:3679
virtual eOSState ProcessKey(eKeys Key) override
Definition menuitems.c:1241
cMenuSetupPage(void)
Definition menuitems.c:1229
void SetSection(const char *Section)
Definition menuitems.c:1236
void SetPlugin(cPlugin *Plugin)
Definition menuitems.c:1256
cMenuSetupPluginItem(const char *Name, int Index)
Definition menu.c:4549
int PluginIndex(void)
Definition menu.c:4546
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:4577
cMenuSetupPlugins(void)
Definition menu.c:4563
const char * pauseKeyHandlingTexts[3]
Definition menu.c:4373
const char * recordKeyHandlingTexts[3]
Definition menu.c:4372
cMenuSetupRecord(void)
Definition menu.c:4379
const char * delTimeshiftRecTexts[3]
Definition menu.c:4374
virtual void Store(void) override
Definition menu.c:4441
cMenuSetupReplay(void)
Definition menu.c:4420
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:4653
virtual void Set(void)
Definition menu.c:4624
cMenuSetup(void)
Definition menu.c:4617
eOSState Restart(void)
Definition menu.c:4644
eDvbFont font
Definition menu.h:25
void SetText(const char *Text)
Definition menu.c:631
cMenuText(const char *Title, const char *Text, eDvbFont Font=fontOsd)
Definition menu.c:617
virtual void Display(void) override
Definition menu.c:637
char * text
Definition menu.h:24
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:645
virtual ~cMenuText() override
Definition menu.c:626
virtual void SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) override
Definition menu.c:1295
virtual void Set(void) override
Definition menu.c:1249
virtual int Compare(const cListObject &ListObject) const override
Must return 0 if this object is equal to ListObject, a positive value if it is "greater",...
Definition menu.c:1244
cMenuTimerItem(const cTimer *Timer)
Definition menu.c:1238
const cTimer * timer
Definition menu.c:1229
const cTimer * Timer(void)
Definition menu.c:1234
eOSState New(void)
Definition menu.c:1420
cMenuTimers(void)
Definition menu.c:1323
void Set(void)
Definition menu.c:1337
void SetHelpKeys(void)
Definition menu.c:1368
eOSState Info(void)
Definition menu.c:1462
eOSState Edit(void)
Definition menu.c:1413
cTimer * GetTimer(void)
Definition menu.c:1362
cStateKey timersStateKey
Definition menu.c:1306
bool showAllTimers
Definition menu.c:1308
eOSState Delete(void)
Definition menu.c:1430
int helpKeys
Definition menu.c:1307
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:1475
eOSState OnOff(void)
Definition menu.c:1383
virtual ~cMenuTimers() override
Definition menu.c:1333
void SetHelpKeys(const cChannels *Channels)
Definition menu.c:1717
static const cEvent * scheduleEvent
Definition menu.c:1671
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:1808
static const cEvent * ScheduleEvent(void)
Definition menu.c:1745
eOSState Record(void)
Definition menu.c:1769
cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, const cSchedules *Schedules, bool Now, int CurrentChannelNr)
Definition menu.c:1685
static void SetCurrentChannel(int ChannelNr)
Definition menu.c:1677
cStateKey timersStateKey
Definition menu.c:1667
static int CurrentChannel(void)
Definition menu.c:1676
eOSState Switch(void)
Definition menu.c:1752
bool canSwitch
Definition menu.c:1665
bool Update(void)
Definition menu.c:1704
static int currentChannel
Definition menu.c:1670
bool now
Definition menu.c:1664
int helpKeys
Definition menu.c:1666
void SetSubItems(bool On)
Definition config.c:162
cList< cNestedItem > * SubItems(void)
Definition config.h:214
const char * Text(void) const
Definition config.h:213
const char * Text(void) const
Definition osdbase.h:65
void SetSelectable(bool Selectable)
Definition osdbase.c:48
virtual eOSState ProcessKey(eKeys Key)
Definition osdbase.c:63
eOSState state
Definition osdbase.h:53
bool Selectable(void) const
Definition osdbase.h:61
void SetText(const char *Text, bool Copy=true)
Definition osdbase.c:42
cOsdItem(eOSState State=osUnknown)
Definition osdbase.c:20
void Ins(cOsdItem *Item, bool Current=false, cOsdItem *Before=NULL)
Definition osdbase.c:234
virtual eOSState ProcessKey(eKeys Key) override
Definition osdbase.c:581
eOSState CloseSubMenu(bool ReDisplay=true)
Definition osdbase.c:569
void SetTitle(const char *Title)
Definition osdbase.c:187
void DisplayCurrent(bool Current)
Definition osdbase.c:326
int Current(void) const
Definition osdbase.h:146
const char * hk(const char *s)
Definition osdbase.c:149
void Mark(void)
Definition osdbase.c:533
cOsdMenu * SubMenu(void)
Definition osdbase.h:135
void DisplayItem(cOsdItem *Item)
Definition osdbase.c:348
eOSState AddSubMenu(cOsdMenu *SubMenu)
Definition osdbase.c:559
void Add(cOsdItem *Item, bool Current=false, cOsdItem *After=NULL)
Definition osdbase.c:227
void SetHasHotkeys(bool HasHotkeys=true)
Definition osdbase.c:173
void SetCols(int c0, int c1=0, int c2=0, int c3=0, int c4=0)
Definition osdbase.c:164
void SetCurrent(cOsdItem *Item)
Definition osdbase.c:311
cOsdMenu(const char *Title, int c0=0, int c1=0, int c2=0, int c3=0, int c4=0)
Definition osdbase.c:83
void SetMenuCategory(eMenuCategory MenuCategory)
Definition osdbase.c:125
void RefreshCurrent(void)
Definition osdbase.c:319
void SetHelp(const char *Red, const char *Green=NULL, const char *Yellow=NULL, const char *Blue=NULL)
Definition osdbase.c:203
cSkinDisplayMenu * DisplayMenu(void)
Definition osdbase.h:114
virtual void Display(void)
Definition osdbase.c:248
bool HasSubMenu(void)
Definition osdbase.h:134
int VisibleItem(int Idx)
Returns the index in the menu if the item is currently visible, -1 otherwise.
Definition osdbase.c:373
virtual void Del(int Index)
Definition osdbase.c:213
const char * Title(void)
Definition osdbase.h:119
virtual void Clear(void)
Definition osdbase.c:362
void SetMenuSortMode(eMenuSortMode MenuSortMode)
Definition osdbase.c:130
int current
Definition osdbase.h:96
void SetNeedsFastResponse(bool NeedsFastResponse)
Definition osdbase.h:77
cOsdObject(bool FastResponse=false)
Definition osdbase.h:79
bool IsMenu(void) const
Definition osdbase.h:82
static bool OsdSizeChanged(int &State)
Checks if the OSD size has changed and a currently displayed OSD needs to be redrawn.
Definition osd.c:2337
static void UpdateOsdSize(bool Force=false)
Inquires the actual size of the video display and adjusts the OSD and font sizes accordingly.
Definition osd.c:2310
static int IsOpen(void)
Returns true if there is currently a level 0 OSD open.
Definition osd.h:840
int Close(void)
Definition thread.c:1004
bool Open(const char *Command, const char *Mode)
Definition thread.c:950
static bool HasPlugins(void)
Definition plugin.c:465
static cPlugin * CallFirstService(const char *Id, void *Data=NULL)
Definition plugin.c:488
static cPlugin * GetPlugin(int Index)
Definition plugin.c:470
virtual cMenuSetupPage * SetupMenu(void)
Definition plugin.c:101
virtual const char * Version(void)=0
const char * Name(void)
Definition plugin.h:36
virtual const char * MainMenuEntry(void)
Definition plugin.c:91
virtual cOsdObject * MainMenuAction(void)
Definition plugin.c:96
virtual const char * Description(void)=0
A steerable satellite dish generally points to the south on the northern hemisphere,...
Definition positioner.h:31
virtual bool IsMoving(void) const
Returns true if the dish is currently moving as a result of a call to GotoPosition() or GotoAngle().
Definition positioner.c:127
virtual ~cRecordControl()
Definition menu.c:5685
const char * InstantId(void)
Definition menu.h:260
void Stop(bool ExecuteUserCommand=true)
Definition menu.c:5717
cDevice * device
Definition menu.h:247
cTimer * timer
Definition menu.h:248
bool GetEvent(void)
Definition menu.c:5693
char * fileName
Definition menu.h:252
cTimer * Timer(void)
Definition menu.h:262
cRecorder * recorder
Definition menu.h:249
cRecordControl(cDevice *Device, cTimers *Timers, cTimer *Timer=NULL, bool Pause=false)
Definition menu.c:5603
cDevice * Device(void)
Definition menu.h:258
const cEvent * event
Definition menu.h:250
bool Process(time_t t)
Definition menu.c:5740
cString instantId
Definition menu.h:251
static bool StateChanged(int &State)
Definition menu.c:5946
static const char * GetInstantId(const char *LastInstantId)
Definition menu.c:5865
static void ChannelDataModified(const cChannel *Channel)
Definition menu.c:5913
static bool Process(cTimers *Timers, time_t t)
Definition menu.c:5898
static bool PauseLiveVideo(void)
Definition menu.c:5850
static void Shutdown(void)
Definition menu.c:5939
static bool Start(cTimers *Timers, cTimer *Timer, bool Pause=false)
Definition menu.c:5755
static cRecordControl * RecordControls[]
Definition menu.h:5752
static bool Active(void)
Definition menu.c:5930
static void Stop(const char *InstantId)
Definition menu.c:5817
static cRecordControl * GetRecordControl(const char *FileName)
Definition menu.c:5878
static int state
Definition menu.h:268
static void ChangeState(void)
Definition menu.h:284
void SetParentalRating(int ParentalRating)
Definition recording.c:531
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
Definition recording.c:2648
bool ChangePriorityLifetime(int NewPriority, int NewLifetime)
Changes the priority and lifetime of this recording to the given values.
Definition recording.c:1398
bool WriteInfo(const char *OtherFileName=NULL)
Writes the info file of this recording.
Definition recording.c:1370
bool ChangeName(const char *NewName)
Changes the name of this recording to the given value.
Definition recording.c:1422
bool Delete(void)
Changes the file name (both internally and on disk) to make this a "deleted" recording.
Definition recording.c:1451
cString Folder(void) const
Returns the name of the folder this recording is stored in (without the video directory).
Definition recording.c:1227
void DeleteResume(void) const
Definition recording.c:1546
const char * Name(void) const
Returns the full name of the recording (without the video directory).
Definition recording.h:179
const char * FileName(void) const
Returns the full path name to the recording directory, including the video directory and the actual '...
Definition recording.c:1239
cRecordingInfo * Info(void) const
Definition recording.h:186
double FramesPerSecond(void) const
Definition recording.h:191
bool IsPesRecording(void) const
Definition recording.h:215
static const cRecordings * GetRecordingsRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of recordings for read access.
Definition recording.h:285
static const cRecordings * GetDeletedRecordingsRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of deleted recordings for read access.
Definition recording.h:291
static cRecordings * GetDeletedRecordingsWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of deleted recordings for write access.
Definition recording.h:294
static cRecordings * GetRecordingsWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of recordings for write access.
Definition recording.h:288
static void TouchUpdate(void)
Touches the '.update' file in the video directory, so that other instances of VDR that access the sam...
Definition recording.c:1758
void Add(cRecording *Recording)
Definition recording.c:1806
const cRecording * GetByName(const char *FileName) const
Definition recording.c:1795
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
Definition remote.c:124
static void TriggerLastActivity(void)
Simulates user activity, for instance to keep the current menu open even if no remote control key has...
Definition remote.c:204
static void SetRecording(const char *FileName)
Definition menu.c:6091
static const char * LastReplayed(void)
Definition menu.c:6101
void MarkToggle(void)
Definition menu.c:6311
virtual void Show(void) override
Definition menu.c:6127
static cString fileName
Definition menu.h:322
void TimeSearchDisplay(void)
Definition menu.c:6221
static void ClearLastReplayed(const char *FileName)
Definition menu.c:6109
int lastTotal
Definition menu.h:310
virtual eOSState ProcessKey(eKeys Key) override
Definition menu.c:6496
void MarkMove(int Frames, bool MarkRequired)
Definition menu.c:6361
static cReplayControl * currentReplayControl
Definition menu.h:321
bool timeSearchHide
Definition menu.h:315
virtual const cRecording * GetRecording(void) override
Returns the cRecording that is currently being replayed, or NULL if this player is not playing a cRec...
Definition menu.c:6488
void TimeSearchProcess(eKeys Key)
Definition menu.c:6239
void MarkJump(bool Forward)
Definition menu.c:6336
cMarks marks
Definition menu.h:306
void EditCut(void)
Definition menu.c:6440
int timeSearchTime
Definition menu.h:316
cSkinDisplayReplay * displayReplay
Definition menu.h:304
void Stop(void)
Definition menu.c:6040
void ShowTimed(int Seconds=0)
Definition menu.c:6115
static cTimer * timeshiftTimer
Definition menu.h:303
bool lastForward
Definition menu.h:311
virtual cOsdObject * GetInfo(void) override
Returns an OSD object that displays information about the currently played programme.
Definition menu.c:6480
bool displayFrames
Definition menu.h:308
bool shown
Definition menu.h:308
virtual void Hide(void) override
Definition menu.c:6132
time_t timeoutShow
Definition menu.h:313
void EditTest(void)
Definition menu.c:6464
bool timeSearchActive
Definition menu.h:315
bool ShowProgress(bool Initial)
Definition menu.c:6176
static void DelTimeshiftTimer(void)
Definition menu.c:6028
bool marksModified
Definition menu.h:307
int lastSpeed
Definition menu.h:312
cAdaptiveSkipper adaptiveSkipper
Definition menu.h:305
bool lastPlay
Definition menu.h:311
int timeSearchPos
Definition menu.h:316
virtual void ClearEditingMarks(void) override
Clears any editing marks this player might be showing.
Definition menu.c:6081
int lastErrors
Definition menu.h:309
virtual ~cReplayControl() override
Definition menu.c:6020
void ShowMode(void)
Definition menu.c:6151
void ErrorJump(bool Forward)
Definition menu.c:6399
cTimeMs updateTimer
Definition menu.h:314
void TimeSearch(void)
Definition menu.c:6293
bool visible
Definition menu.h:308
bool modeOnly
Definition menu.h:308
static const char * NowReplaying(void)
Definition menu.c:6096
cReplayControl(bool PauseLive=false)
Definition menu.c:5996
int lastCurrent
Definition menu.h:310
const cSchedule * GetSchedule(tChannelID ChannelID) const
Definition epg.c:1396
static const cSchedules * GetSchedulesRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of schedules for read access.
Definition epg.c:1291
static void ResetVersions(void)
Definition epg.c:1322
virtual void SetRecording(const cRecording *Recording)=0
Sets the Recording that shall be displayed, using the entire central area of the menu.
virtual bool SetItemRecording(const cRecording *Recording, int Index, bool Current, bool Selectable, int Level, int Total, int New)
Sets the item at the given Index to Recording.
Definition skins.h:268
virtual void Scroll(bool Up, bool Page)
If this menu contains a text area that can be scrolled, this function will be called to actually scro...
Definition skins.c:107
virtual void SetItem(const char *Text, int Index, bool Current, bool Selectable)=0
Sets the item at the given Index to Text.
virtual void SetEvent(const cEvent *Event)=0
Sets the Event that shall be displayed, using the entire central area of the menu.
virtual bool SetItemEvent(const cEvent *Event, int Index, bool Current, bool Selectable, const cChannel *Channel, bool WithDate, eTimerMatch TimerMatch, bool TimerActive)
Sets the item at the given Index to Event.
Definition skins.h:237
virtual bool SetItemChannel(const cChannel *Channel, int Index, bool Current, bool Selectable, bool WithProvider)
Sets the item at the given Index to Channel.
Definition skins.h:259
virtual bool SetItemTimer(const cTimer *Timer, int Index, bool Current, bool Selectable)
Sets the item at the given Index to Timer.
Definition skins.h:252
virtual void SetText(const char *Text, bool FixedFont)=0
Sets the Text that shall be displayed, using the entire central area of the menu.
Definition skins.h:403
const char * Name(void)
Definition skins.h:422
static cString ToString(int Code)
Definition sources.c:52
@ st_Mask
Definition sources.h:23
@ stSat
Definition sources.h:21
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
Definition thread.c:870
static void MsgMarksModified(const cMarks *Marks)
Definition status.c:63
static void MsgOsdChannel(const char *Text)
Definition status.c:135
static void MsgSetAudioChannel(int AudioChannel)
Definition status.c:81
static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle)
Definition status.c:141
static void MsgReplaying(const cControl *Control, const char *Name, const char *FileName, bool On)
Definition status.c:57
static void MsgRecording(const cDevice *Device, const char *Name, const char *FileName, bool On)
Definition status.c:51
static void MsgOsdClear(void)
Definition status.c:93
static void MsgSetAudioTrack(int Index, const char *const *Tracks)
Definition status.c:75
static void MsgOsdTextItem(const char *Text, bool Scroll=false)
Definition status.c:129
static void MsgSetSubtitleTrack(int Index, const char *const *Tracks)
Definition status.c:87
void Sort(bool IgnoreCase=false)
Definition tools.h:859
int Find(const char *s) const
Definition tools.c:1649
cString & CompactChars(char c)
Compact any sequence of characters 'c' to a single character, and strip all of them from the beginnin...
Definition tools.c:1206
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1212
void OnOff(void)
Definition timers.c:1148
cString PrintFirstDay(void) const
Definition timers.c:436
void SetPending(bool Pending)
Definition timers.c:1060
time_t FirstDay(void) const
Definition timers.h:79
bool Recording(void) const
Definition timers.h:66
void SetRemote(const char *Remote)
Definition timers.c:1108
const cEvent * Event(void) const
Definition timers.h:87
void Skip(void)
Definition timers.c:1140
const cChannel * Channel(void) const
Definition timers.h:70
bool Pending(void) const
Definition timers.h:67
cString ToDescr(void) const
Definition timers.c:333
bool SetEventFromSchedule(const cSchedules *Schedules)
Definition timers.c:981
int Priority(void) const
Definition timers.h:75
bool Matches(void) const
Definition timers.h:109
bool HasFlags(uint Flags) const
Definition timers.c:1135
const char * Remote(void) const
Definition timers.h:81
int Id(void) const
Definition timers.h:65
cString ToText(bool UseChannelID=false) const
Definition timers.c:323
void Add(cTimer *Timer, cTimer *After=NULL)
Definition timers.c:1305
static cTimers * GetTimersWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for write access.
Definition timers.c:1300
void Del(cTimer *Timer, bool DeleteObject=true)
Definition timers.c:1319
static const cTimers * GetTimersRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for read access.
Definition timers.c:1295
const cTimer * GetMatch(time_t t) const
Definition timers.c:1221
int Size(void) const
Definition tools.h:767
void Sort(__compar_fn_t Compare)
Definition tools.h:827
virtual void Append(T Data)
Definition tools.h:787
T & At(int Index) const
Definition tools.h:744
static const char * Name(void)
Definition videodir.c:60
static int VideoDiskSpace(int *FreeMB=NULL, int *UsedMB=NULL)
Definition videodir.c:152
static void ForceCheck(void)
To avoid unnecessary load, the video disk usage is only actually checked every DISKSPACECHEK seconds.
Definition videodir.h:101
cNestedItemList Commands
Definition config.c:275
cSetup Setup
Definition config.c:372
cNestedItemList Folders
Definition config.c:274
cNestedItemList RecordingCommands
Definition config.c:276
#define SUBTITLES_NO
Definition config.h:86
#define TIMERMACRO_MATCH
Definition config.h:57
#define TIMERMACRO_AFTER
Definition config.h:58
#define MAXLIFETIME
Definition config.h:50
#define MAXPRIORITY
Definition config.h:45
#define VDRVERSION
Definition config.h:25
#define TIMERMACRO_BEFORE
Definition config.h:56
#define TIMERMACRO_EPISODE
Definition config.h:55
#define SUBTITLES_REWIND
Definition config.h:88
#define TIMERMACRO_TITLE
Definition config.h:54
#define LIVEPRIORITY
Definition config.h:47
#define MAXVOLUME
Definition device.h:32
eVideoDisplayFormat
Definition device.h:58
#define MAXDEVICES
Definition device.h:29
#define IS_AUDIO_TRACK(t)
Definition device.h:76
eTrackType
Definition device.h:63
@ ttSubtitle
Definition device.h:70
@ ttDolbyLast
Definition device.h:69
@ ttDolby
Definition device.h:67
@ ttAudioFirst
Definition device.h:65
@ ttSubtitleLast
Definition device.h:72
@ ttSubtitleFirst
Definition device.h:71
@ ttAudio
Definition device.h:64
@ ttNone
Definition device.h:63
#define IS_DOLBY_TRACK(t)
Definition device.h:77
cEITScanner EITScanner
Definition eitscan.c:104
#define MAXPARENTALRATING
Definition epg.h:22
#define LOCK_SCHEDULES_READ
Definition epg.h:232
#define MAXEPGBUGFIXLEVEL
Definition epg.h:21
const char * DefaultFontOsd
Definition font.c:24
const char * DefaultFontSml
Definition font.c:25
const char * DefaultFontFix
Definition font.c:26
eDvbFont
Definition font.h:21
@ fontFix
Definition font.h:23
const char * I18nLocale(int Language)
Returns the locale code of the given Language (which is an index as returned by I18nCurrentLanguage()...
Definition i18n.c:280
const cStringList * I18nLanguages(void)
Returns the list of available languages.
Definition i18n.c:263
int I18nNumLanguagesWithLocale(void)
Returns the number of entries in the list returned by I18nLanguages() that actually have a locale.
Definition i18n.c:258
int I18nCurrentLanguage(void)
Returns the index of the current language.
Definition i18n.c:245
void I18nSetLocale(const char *Locale)
Sets the current locale to Locale.
Definition i18n.c:231
void I18nSetLanguage(int Language)
Sets the current language index to Language.
Definition i18n.c:250
#define tr(s)
Definition i18n.h:85
cInterface * Interface
Definition interface.c:20
#define kMarkMoveForward
Definition keys.h:71
#define kMarkSkipForward
Definition keys.h:69
#define kMarkJumpBack
Definition keys.h:72
#define kEditCut
Definition keys.h:74
#define kMarkToggle
Definition keys.h:67
#define kMarkJumpForward
Definition keys.h:73
#define RAWKEY(k)
Definition keys.h:77
#define kEditTest
Definition keys.h:75
#define kMarkSkipBack
Definition keys.h:68
#define kMarkMoveBack
Definition keys.h:70
#define NORMALKEY(k)
Definition keys.h:79
eKeys
Definition keys.h:16
@ kRecord
Definition keys.h:34
@ kPlayPause
Definition keys.h:30
@ kRight
Definition keys.h:23
@ k_Flags
Definition keys.h:63
@ kPause
Definition keys.h:32
@ k9
Definition keys.h:28
@ kRed
Definition keys.h:24
@ kUp
Definition keys.h:17
@ kChanUp
Definition keys.h:40
@ kNone
Definition keys.h:55
@ kPlay
Definition keys.h:31
@ kFastFwd
Definition keys.h:35
@ k_Release
Definition keys.h:62
@ kDown
Definition keys.h:18
@ kGreen
Definition keys.h:25
@ k1
Definition keys.h:28
@ kStop
Definition keys.h:33
@ kSubtitles
Definition keys.h:47
@ kLeft
Definition keys.h:22
@ kBlue
Definition keys.h:27
@ kAudio
Definition keys.h:46
@ kMute
Definition keys.h:45
@ kPrev
Definition keys.h:38
@ k0
Definition keys.h:28
@ kYellow
Definition keys.h:26
@ kBack
Definition keys.h:21
@ k_Repeat
Definition keys.h:61
@ kFastRew
Definition keys.h:36
@ kChanDn
Definition keys.h:41
@ kVolDn
Definition keys.h:44
@ kNext
Definition keys.h:37
@ kOk
Definition keys.h:20
@ kVolUp
Definition keys.h:43
@ kInfo
Definition keys.h:29
static const char * TimerMatchChars
Definition menu.c:1619
static const char * TimerFileMacrosForPattern[]
Definition menu.c:998
#define NEWTIMERLIMIT
Definition menu.c:38
#define osUserRecRenamed
Definition menu.c:2524
#define MAXINSTANTRECTIME
Definition menu.c:44
#define NODISKSPACEDELTA
Definition menu.c:50
#define CAMRESPONSETIMEOUT
Definition menu.c:47
#define MAXRECORDCONTROLS
Definition menu.c:43
static bool RemoteTimerError(const cTimer *Timer)
Definition menu.c:1132
static void AddRecordingFolders(const cRecordings *Recordings, cList< cNestedItem > *List, char *Path)
Definition menu.c:824
#define CAMMENURETRYTIMEOUT
Definition menu.c:46
#define osUserRecEmpty
Definition menu.c:2527
cOsdObject * CamControl(void)
Definition menu.c:2508
bool CamMenuActive(void)
Definition menu.c:2517
#define STAY_SECONDS_OFF_END
#define osUserRecMoved
Definition menu.c:2525
#define MUTETIMEOUT
Definition menu.c:5313
void SetTrackDescriptions(int LiveChannel)
Definition menu.c:4970
#define MODETIMEOUT
Definition menu.c:37
#define TRACKTIMEOUT
Definition menu.c:5379
static bool CamMenuIsOpen
Definition menu.c:2324
#define CHANNELNUMBERTIMEOUT
Definition menu.c:352
static const char * TimerFileMacros[]
Definition menu.c:1008
#define INSTANT_REC_EPG_LOOKAHEAD
Definition menu.c:5691
#define FOLDERDELIMCHARSUBST
Definition menu.c:823
#define CHNAMWIDTH
Definition menu.c:54
#define CHNUMWIDTH
Definition menu.c:53
#define osUserRecRemoved
Definition menu.c:2526
#define MAXWAITFORCAMMENU
Definition menu.c:45
#define VOLUMETIMEOUT
Definition menu.c:5312
#define DEFERTIMER
Definition menu.c:41
#define PROGRESSTIMEOUT
Definition menu.c:48
#define MINFREEDISK
Definition menu.c:49
static bool TimerStillRecording(const char *FileName)
Definition menu.c:3349
static bool HandleRemoteModifications(cTimer *NewTimer, cTimer *OldTimer=NULL)
Definition menu.c:1138
#define MAXWAIT4EPGINFO
Definition menu.c:36
#define STOP_RECORDING
Definition menu.c:4698
void SetTrackDescriptions(int LiveChannel)
Definition menu.c:4970
eOSState
Definition osdbase.h:18
@ osUser5
Definition osdbase.h:42
@ osRecordings
Definition osdbase.h:23
@ osCancelEdit
Definition osdbase.h:34
@ osPause
Definition osdbase.h:29
@ osPlugin
Definition osdbase.h:26
@ osChannels
Definition osdbase.h:21
@ osStopReplay
Definition osdbase.h:33
@ osUser1
Definition osdbase.h:38
@ osUser8
Definition osdbase.h:45
@ osUser10
Definition osdbase.h:47
@ osRecord
Definition osdbase.h:30
@ osEnd
Definition osdbase.h:36
@ osSetup
Definition osdbase.h:27
@ osUser4
Definition osdbase.h:41
@ osStopRecord
Definition osdbase.h:32
@ osContinue
Definition osdbase.h:19
@ osUser6
Definition osdbase.h:43
@ osTimers
Definition osdbase.h:22
@ osReplay
Definition osdbase.h:31
@ osUser3
Definition osdbase.h:40
@ osRecsOpen
Definition osdbase.h:24
@ osUser2
Definition osdbase.h:39
@ osRecsDel
Definition osdbase.h:25
@ osUnknown
Definition osdbase.h:18
@ osUser9
Definition osdbase.h:46
@ osSchedule
Definition osdbase.h:20
@ osCommands
Definition osdbase.h:28
@ osBack
Definition osdbase.h:35
@ osUser7
Definition osdbase.h:44
cString GetRecordingTimerId(const char *Directory)
Definition recording.c:3629
cString IndexToHMSF(int Index, bool WithFrame, double FramesPerSecond)
Definition recording.c:3524
void AssertFreeDiskSpace(int Priority, bool Force)
The special Priority value -1 means that we shall get rid of any deleted recordings faster than norma...
Definition recording.c:152
void GetRecordingsSortMode(const char *Directory)
Definition recording.c:3581
int SecondsToFrames(int Seconds, double FramesPerSecond)
Definition recording.c:3551
eRecordingsSortMode RecordingsSortMode
Definition recording.c:3574
bool EnoughFreeDiskSpaceForEdit(const char *FileName)
Definition recording.c:3661
char * ExchangeChars(char *s, bool ToFileSystem)
Definition recording.c:759
void IncRecordingsSortMode(const char *Directory)
Definition recording.c:3600
cDoneRecordings DoneRecordingsPattern
Definition recording.c:3431
cRecordingsHandler RecordingsHandler
Definition recording.c:2259
void SetRecordingTimerId(const char *Directory, const char *TimerId)
Definition recording.c:3611
@ ruCut
Definition recording.h:34
@ ruCopy
Definition recording.h:36
@ ruDst
Definition recording.h:39
@ ruNone
Definition recording.h:30
@ ruMove
Definition recording.h:35
@ ruPending
Definition recording.h:41
#define RUC_BEFORERECORDING
Definition recording.h:477
@ rsmName
Definition recording.h:614
#define RUC_AFTERRECORDING
Definition recording.h:479
#define LOCK_RECORDINGS_READ
Definition recording.h:352
#define MAXVIDEOFILESIZETS
Definition recording.h:504
#define LOCK_DELETEDRECORDINGS_WRITE
Definition recording.h:355
#define FOLDERDELIMCHAR
Definition recording.h:22
#define LOCK_DELETEDRECORDINGS_READ
Definition recording.h:354
#define LOCK_RECORDINGS_WRITE
Definition recording.h:353
#define MINVIDEOFILESIZE
Definition recording.h:506
cShutdownHandler ShutdownHandler
Definition shutdown.c:27
static const cCursesFont Font
Definition skincurses.c:31
cSkins Skins
Definition skins.c:253
@ mcSetupMisc
Definition skins.h:129
@ mcSetupOsd
Definition skins.h:122
@ mcSetupLnb
Definition skins.h:125
@ mcMain
Definition skins.h:107
@ mcSetup
Definition skins.h:121
@ mcChannel
Definition skins.h:111
@ mcRecordingInfo
Definition skins.h:117
@ mcSetupDvb
Definition skins.h:124
@ mcSetupRecord
Definition skins.h:127
@ mcCam
Definition skins.h:135
@ mcSetupReplay
Definition skins.h:128
@ mcRecordingDel
Definition skins.h:116
@ mcChannelEdit
Definition skins.h:112
@ mcCommand
Definition skins.h:131
@ mcEvent
Definition skins.h:132
@ mcSetupCam
Definition skins.h:126
@ mcSchedule
Definition skins.h:108
@ mcText
Definition skins.h:133
@ mcRecording
Definition skins.h:115
@ mcRecordingEdit
Definition skins.h:118
@ mcTimerEdit
Definition skins.h:114
@ mcScheduleNow
Definition skins.h:109
@ mcSetupPlugins
Definition skins.h:130
@ mcFolder
Definition skins.h:134
@ mcSetupEpg
Definition skins.h:123
@ mcTimer
Definition skins.h:113
@ mcScheduleNext
Definition skins.h:110
@ mtWarning
Definition skins.h:37
@ mtInfo
Definition skins.h:37
@ mtError
Definition skins.h:37
@ mtStatus
Definition skins.h:37
@ msmProvider
Definition skins.h:143
@ msmTime
Definition skins.h:142
@ msmName
Definition skins.h:141
@ msmNumber
Definition skins.h:140
cSourceParams SourceParams
cSources Sources
Definition sources.c:114
Definition runvdr.c:107
char language[MAXLANGCODE2]
Definition epg.h:48
uchar stream
Definition epg.h:46
uchar type
Definition epg.h:47
char * description
Definition epg.h:49
char language[MAXLANGCODE2]
Definition device.h:82
char description[32]
Definition device.h:83
uint16_t id
Definition device.h:81
void StopSVDRPHandler(void)
Definition svdrp.c:2873
bool GetSVDRPServerNames(cStringList *ServerNames)
Gets a list of all available VDRs this VDR is connected to via SVDRP, and stores it in the given Serv...
Definition svdrp.c:2882
bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response)
Sends the given SVDRP Command string to the remote VDR identified by ServerName and collects all of t...
Definition svdrp.c:2891
void StartSVDRPHandler(void)
Definition svdrp.c:2857
int SVDRPCode(const char *s)
Returns the value of the three digit reply code of the given SVDRP response string.
Definition svdrp.h:47
cStateKey StateKeySVDRPRemoteTimersPoll
Controls whether a change to the local list of timers needs to result in sending a POLL to the remote...
bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer, cString *Msg)
Performs any operations necessary to synchronize changes to a timer between peer VDR machines.
Definition timers.c:1509
#define LOCK_TIMERS_READ
Definition timers.h:275
#define LOCK_TIMERS_WRITE
Definition timers.h:276
@ tfAvoid
Definition timers.h:24
@ tfInstant
Definition timers.h:20
@ tfActive
Definition timers.h:19
@ tfVps
Definition timers.h:21
@ tfRecording
Definition timers.h:22
@ tfSpawned
Definition timers.h:23
eTimerMatch
Definition timers.h:27
@ tmFull
Definition timers.h:27
@ tmNone
Definition timers.h:27
char * strcpyrealloc(char *dest, const char *src)
Definition tools.c:114
const char * strgetlast(const char *s, char c)
Definition tools.c:221
char * Utf8Strn0Cpy(char *Dest, const char *Src, int n)
Copies at most n character bytes from Src to Dest, making sure that the resulting copy ends with a co...
Definition tools.c:932
bool isempty(const char *s)
Definition tools.c:357
char * strreplace(char *s, char c1, char c2)
Definition tools.c:142
cString strescape(const char *s, const char *chars)
Definition tools.c:280
bool MakeDirs(const char *FileName, bool IsDirectory)
Definition tools.c:512
cString WeekDayName(int WeekDay)
Converts the given WeekDay (0=Sunday, 1=Monday, ...) to a three letter day name.
Definition tools.c:1235
cString Indent(int n, const char *s)
Returns the given string s, preceeded with n blanks for indentation.
Definition tools.c:410
char * stripspace(char *s)
Definition tools.c:227
int Utf8SymChars(const char *s, int Symbols)
Returns the number of character bytes at the beginning of the given string that form at most the give...
Definition tools.c:907
char * strn0cpy(char *dest, const char *src, size_t n)
Definition tools.c:131
cString itoa(int n)
Definition tools.c:455
const char * strchrn(const char *s, char c, size_t n)
returns a pointer to the n'th occurrence (counting from 1) of c in s, or NULL if no such character wa...
Definition tools.c:186
#define SECSINDAY
Definition tools.h:42
#define dsyslog(a...)
Definition tools.h:37
int CompareInts(const void *a, const void *b)
Definition tools.h:833
#define MALLOC(type, size)
Definition tools.h:47
char * skipspace(const char *s)
Definition tools.h:244
void DELETENULL(T *&p)
Definition tools.h:49
bool DoubleEqual(double a, double b)
Definition tools.h:97
T min(T a, T b)
Definition tools.h:63
T max(T a, T b)
Definition tools.h:64
#define esyslog(a...)
Definition tools.h:35
#define isyslog(a...)
Definition tools.h:36