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