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