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