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