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