vdr  2.7.6
menuitems.c
Go to the documentation of this file.
1 /*
2  * menuitems.c: General purpose menu items
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: menuitems.c 5.4 2025/06/18 20:22:10 kls Exp $
8  */
9 
10 #include "menuitems.h"
11 #include <ctype.h>
12 #include <math.h>
13 #include <wctype.h>
14 #include "i18n.h"
15 #include "plugin.h"
16 #include "remote.h"
17 #include "skins.h"
18 #include "status.h"
19 
20 #define AUTO_ADVANCE_TIMEOUT 1500 // ms before auto advance when entering characters via numeric keys
21 
22 const char *FileNameChars = trNOOP("FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&");
23 
24 // --- cMenuEditItem ---------------------------------------------------------
25 
27 {
28  name = strdup(Name ? Name : "???");
29  SetHelp(NULL);
30 }
31 
33 {
34  free(name);
35 }
36 
37 void cMenuEditItem::SetValue(const char *Value)
38 {
39  cString buffer = cString::sprintf("%s:\t%s", name, Value);
40  SetText(buffer);
41 }
42 
43 void cMenuEditItem::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
44 {
45  // strings are NOT copied - must be constants!!!
46  helpRed = Red;
47  helpGreen = Green;
48  helpYellow = Yellow;
49  helpBlue = Blue;
50  helpDisplayed = false;
51 }
52 
53 bool cMenuEditItem::DisplayHelp(bool Current)
54 {
55  bool HasHelp = helpRed || helpGreen || helpYellow || helpBlue;
56  if (HasHelp && !helpDisplayed && Current) {
59  }
60  helpDisplayed = Current;
61  return HasHelp;
62 }
63 
64 // --- cMenuEditIntItem ------------------------------------------------------
65 
66 cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max, const char *MinString, const char *MaxString)
67 :cMenuEditItem(Name)
68 {
69  value = Value;
70  min = Min;
71  max = Max;
72  minString = MinString;
73  maxString = MaxString;
74  if (*value < min)
75  *value = min;
76  else if (*value > max)
77  *value = max;
78  Set();
79 }
80 
82 {
83  if (minString && *value == min)
85  else if (maxString && *value == max)
87  else {
88  char buf[16];
89  snprintf(buf, sizeof(buf), "%d", *value);
90  SetValue(buf);
91  }
92 }
93 
95 {
97 
98  if (state == osUnknown) {
99  int newValue = *value;
100  bool IsRepeat = Key & k_Repeat;
101  Key = NORMALKEY(Key);
102  switch (Key) {
103  case kNone: return state;
104  case k0 ... k9:
105  if (fresh) {
106  newValue = 0;
107  fresh = false;
108  }
109  newValue = newValue * 10 + (Key - k0);
110  break;
111  case kLeft: // TODO might want to increase the delta if repeated quickly?
112  newValue = *value - 1;
113  fresh = true;
114  if (!IsRepeat && newValue < min && max != INT_MAX)
115  newValue = max;
116  break;
117  case kRight:
118  newValue = *value + 1;
119  fresh = true;
120  if (!IsRepeat && newValue > max && min != INT_MIN)
121  newValue = min;
122  break;
123  default:
124  if (*value < min) { *value = min; Set(); }
125  if (*value > max) { *value = max; Set(); }
126  return state;
127  }
128  if (newValue != *value && (!fresh || min <= newValue) && newValue <= max) {
129  *value = newValue;
130  Set();
131  }
132  state = osContinue;
133  }
134  return state;
135 }
136 
137 // --- cMenuEditBoolItem -----------------------------------------------------
138 
139 cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString)
140 :cMenuEditIntItem(Name, Value, 0, 1)
141 {
142  falseString = FalseString ? FalseString : tr("no");
143  trueString = TrueString ? TrueString : tr("yes");
144  Set();
145 }
146 
148 {
149  char buf[16];
150  snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
151  SetValue(buf);
152 }
153 
154 // --- cMenuEditBitItem ------------------------------------------------------
155 
156 cMenuEditBitItem::cMenuEditBitItem(const char *Name, uint *Value, uint Mask, const char *FalseString, const char *TrueString)
157 :cMenuEditBoolItem(Name, &bit, FalseString, TrueString)
158 {
159  value = Value;
160  bit = (*value & Mask) != 0;
161  mask = Mask;
162  Set();
163 }
164 
166 {
167  *value = bit ? *value | mask : *value & ~mask;
169 }
170 
171 // --- cMenuEditNumItem ------------------------------------------------------
172 
173 cMenuEditNumItem::cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind)
174 :cMenuEditItem(Name)
175 {
176  value = Value;
177  length = Length;
178  blind = Blind;
179  Set();
180 }
181 
183 {
184  if (blind) {
185  char buf[length + 1];
186  int i;
187  for (i = 0; i < length && value[i]; i++)
188  buf[i] = '*';
189  buf[i] = 0;
190  SetValue(buf);
191  }
192  else
193  SetValue(value);
194 }
195 
197 {
199 
200  if (state == osUnknown) {
201  Key = NORMALKEY(Key);
202  switch (Key) {
203  case kLeft: {
204  int l = strlen(value);
205  if (l > 0)
206  value[l - 1] = 0;
207  }
208  break;
209  case k0 ... k9: {
210  int l = strlen(value);
211  if (l < length) {
212  value[l] = Key - k0 + '0';
213  value[l + 1] = 0;
214  }
215  }
216  break;
217  default: return state;
218  }
219  Set();
220  state = osContinue;
221  }
222  return state;
223 }
224 
225 // --- cMenuEditIntxItem -----------------------------------------------------
226 
227 cMenuEditIntxItem::cMenuEditIntxItem(const char *Name, int *Value, int Min, int Max, int Factor, const char *NegString, const char *PosString)
228 :cMenuEditIntItem(Name, Value, Min, Max)
229 {
230  factor = ::max(Factor, 1);
231  negString = NegString;
232  posString = PosString;
233  Set();
234 }
235 
237 {
238  if (negString && posString)
239  SetHelp(NULL, (*value < 0) ? posString : negString);
240 }
241 
243 {
244  const char *s = (*value < 0) ? negString : posString;
245  int v = *value;
246  if (negString && posString)
247  v = abs(v);
248  SetValue(cString::sprintf(s ? "%.*f %s" : "%.*f", factor / 10, double(v) / factor, s));
249  SetHelpKeys();
250 }
251 
253 {
255  if (state == osUnknown) {
256  switch (Key) {
257  case kGreen: if (negString && posString) {
258  *value = -*value;
259  Set();
260  state = osContinue;
261  }
262  break;
263  default: ;
264  }
265  }
266  return state;
267 }
268 
269 // --- cMenuEditPrcItem ------------------------------------------------------
270 
271 cMenuEditPrcItem::cMenuEditPrcItem(const char *Name, double *Value, double Min, double Max, int Decimals)
272 :cMenuEditItem(Name)
273 {
274  value = Value;
275  min = Min;
276  max = Max;
277  decimals = Decimals;
278  factor = 100;
279  while (Decimals-- > 0)
280  factor *= 10;
281  if (*value < min)
282  *value = min;
283  else if (*value > max)
284  *value = max;
285  Set();
286 }
287 
289 {
290  char buf[16];
291  snprintf(buf, sizeof(buf), "%.*f", decimals, *value * 100);
292  SetValue(buf);
293 }
294 
296 {
298 
299  if (state == osUnknown) {
300  double newValue = round(*value * factor); // avoids precision problems
301  Key = NORMALKEY(Key);
302  switch (Key) {
303  case kNone: return state;
304  case k0 ... k9:
305  if (fresh) {
306  newValue = 0;
307  fresh = false;
308  }
309  newValue = newValue * 10 + (Key - k0);
310  break;
311  case kLeft: // TODO might want to increase the delta if repeated quickly?
312  newValue--;
313  fresh = true;
314  break;
315  case kRight:
316  newValue++;
317  fresh = true;
318  break;
319  default:
320  if (*value < min) { *value = min; Set(); }
321  if (*value > max) { *value = max; Set(); }
322  return state;
323  }
324  newValue /= factor;
325  if (!DoubleEqual(newValue, *value) && (!fresh || min <= newValue) && newValue <= max) {
326  *value = newValue;
327  Set();
328  }
329  state = osContinue;
330  }
331  return state;
332 }
333 
334 // --- cMenuEditChrItem ------------------------------------------------------
335 
336 cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
337 :cMenuEditItem(Name)
338 {
339  value = Value;
340  allowed = strdup(Allowed ? Allowed : "");
341  current = strchr(allowed, *Value);
342  if (!current)
343  current = allowed;
344  Set();
345 }
346 
348 {
349  free(allowed);
350 }
351 
353 {
354  char buf[2];
355  buf[0] = *value;
356  buf[1] = '\0';
357  SetValue(buf);
358 }
359 
361 {
363 
364  if (state == osUnknown) {
365  if (NORMALKEY(Key) == kLeft) {
366  if (current > allowed)
367  current--;
368  }
369  else if (NORMALKEY(Key) == kRight) {
370  if (*(current + 1))
371  current++;
372  }
373  else
374  return state;
375  *value = *current;
376  Set();
377  state = osContinue;
378  }
379  return state;
380 }
381 
382 // --- cMenuEditStrItem ------------------------------------------------------
383 
384 cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed)
385 :cMenuEditItem(Name)
386 {
387  value = Value;
388  length = Length;
389  allowed = Allowed ? Allowed : tr(FileNameChars);
390  pos = -1;
391  offset = 0;
392  keepSpace = false;
393  macro = -1;
394  lastMacro = -1;
395  macros = NULL;
396  insert = uppercase = false;
397  newchar = true;
398  lengthUtf8 = 0;
399  valueUtf8 = NULL;
400  allowedUtf8 = NULL;
401  charMapUtf8 = NULL;
402  currentCharUtf8 = NULL;
403  lastKey = kNone;
404  Set();
405 }
406 
408 {
409  delete[] valueUtf8;
410  delete[] allowedUtf8;
411  delete[] charMapUtf8;
412 }
413 
414 void cMenuEditStrItem::SetMacros(const char **Macros)
415 {
416  macros = Macros;
417  macro = 0;
418  lastMacro = -1;
419 }
420 
422 {
423  if (!valueUtf8) {
424  valueUtf8 = new uint[length];
426  int l = strlen(allowed) + 1;
427  allowedUtf8 = new uint[l];
429  const char *charMap = tr("CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9");
430  l = strlen(charMap) + 1;
431  charMapUtf8 = new uint[l];
432  Utf8ToArray(charMap, charMapUtf8, l);
434  AdvancePos();
435  }
436 }
437 
439 {
440  if (valueUtf8) {
441  if (SaveValue) {
443  if (!keepSpace)
444  stripspace(value);
445  }
446  lengthUtf8 = 0;
447  delete[] valueUtf8;
448  valueUtf8 = NULL;
449  delete[] allowedUtf8;
450  allowedUtf8 = NULL;
451  delete[] charMapUtf8;
452  charMapUtf8 = NULL;
453  pos = -1;
454  offset = 0;
455  newchar = true;
456  }
457 }
458 
460 {
461  if (InEditMode())
462  SetHelp(tr("Button$ABC/abc"), insert ? tr("Button$Overwrite") : tr("Button$Insert"), tr("Button$Delete"), macros ? tr("Button$Macro") : NULL);
463  else
464  SetHelp(NULL);
465 }
466 
468 {
469  if (allowedUtf8) {
470  for (uint *a = allowedUtf8; *a; a++) {
471  if (c == *a)
472  return a;
473  }
474  }
475  return NULL;
476 }
477 
479 {
480  if (pos < length - 2 && pos < lengthUtf8) {
481  if (++pos >= lengthUtf8) {
482  if (pos >= 2 && valueUtf8[pos - 1] == ' ' && valueUtf8[pos - 2] == ' ')
483  pos--; // allow only two blanks at the end
484  else {
485  valueUtf8[pos] = ' ';
486  valueUtf8[pos + 1] = 0;
487  lengthUtf8++;
488  }
489  }
490  }
491  newchar = true;
492  if (!insert && Utf8is(alpha, valueUtf8[pos]))
493  uppercase = Utf8is(upper, valueUtf8[pos]);
494 }
495 
497 {
498  if (InEditMode()) {
499  const cFont *font = dynamic_cast<cSkinDisplayMenu *>(cSkinDisplay::Current())->GetTextAreaFont(false);
500 
501  int width = cSkinDisplay::Current()->EditableWidth();
502  width -= font->Width("[]");
503  width -= font->Width("<>"); // reserving this anyway makes the whole thing simpler
504 
505  if (pos < offset)
506  offset = pos;
507  int WidthFromOffset = 0;
508  int EndPos = lengthUtf8;
509  for (int i = offset; i < lengthUtf8; i++) {
510  WidthFromOffset += font->Width(valueUtf8[i]);
511  if (WidthFromOffset > width) {
512  if (pos >= i) {
513  do {
514  WidthFromOffset -= font->Width(valueUtf8[offset]);
515  offset++;
516  } while (WidthFromOffset > width && offset < pos);
517  EndPos = pos + 1;
518  }
519  else {
520  EndPos = i;
521  break;
522  }
523  }
524  }
525 
526  char buf[1000];
527  char *p = buf;
528  if (offset)
529  *p++ = '<';
530  p += Utf8FromArray(valueUtf8 + offset, p, sizeof(buf) - (p - buf), pos - offset);
531  *p++ = '[';
532  if (insert && newchar)
533  *p++ = ']';
534  p += Utf8FromArray(&valueUtf8[pos], p, sizeof(buf) - (p - buf), 1);
535  if (!(insert && newchar))
536  *p++ = ']';
537  p += Utf8FromArray(&valueUtf8[pos + 1], p, sizeof(buf) - (p - buf), EndPos - pos - 1);
538  if (EndPos != lengthUtf8)
539  *p++ = '>';
540  *p = 0;
541 
542  SetValue(buf);
543  }
544  else
545  SetValue(value);
546 }
547 
548 uint cMenuEditStrItem::Inc(uint c, bool Up)
549 {
550  uint *p = IsAllowed(c);
551  if (!p)
552  p = allowedUtf8;
553  if (Up) {
554  if (!*++p)
555  p = allowedUtf8;
556  }
557  else if (--p < allowedUtf8) {
558  p = allowedUtf8;
559  while (*p && *(p + 1))
560  p++;
561  }
562  return *p;
563 }
564 
566 {
567  if (insert && lengthUtf8 < length - 1)
568  Insert();
569  valueUtf8[pos] = c;
570  if (pos < length - 2)
571  pos++;
572  if (pos >= lengthUtf8) {
573  valueUtf8[pos] = ' ';
574  valueUtf8[pos + 1] = 0;
575  lengthUtf8 = pos + 1;
576  }
577 }
578 
580 {
581  memmove(valueUtf8 + pos + 1, valueUtf8 + pos, (lengthUtf8 - pos + 1) * sizeof(*valueUtf8));
582  lengthUtf8++;
583  valueUtf8[pos] = ' ';
584 }
585 
587 {
588  memmove(valueUtf8 + pos, valueUtf8 + pos + 1, (lengthUtf8 - pos) * sizeof(*valueUtf8));
589  lengthUtf8--;
590 }
591 
593 {
594  if (!macros)
595  return;
596  if (lastMacro >= 0) {
597  int l = strlen(macros[lastMacro]);
598  while (l-- > 0)
599  Delete();
600  }
601  const char *p = macros[macro];
602  int oldPos = pos;
603  bool oldInsert = insert;
604  insert = true;
605  newchar = true;
606  while (*p) {
607  Type(*p);
608  p++;
609  }
610  insert = oldInsert;
611  pos = oldPos;
612  lastMacro = macro;
613  if (!macros[++macro])
614  macro = 0;
615 }
616 
618 {
619  bool SameKey = NORMALKEY(Key) == lastKey;
620  if (Key != kNone) {
621  lastKey = NORMALKEY(Key);
622  if (Key != kBlue)
623  lastMacro = -1;
624  }
625  else if (!newchar && k0 <= lastKey && lastKey <= k9 && autoAdvanceTimeout.TimedOut()) {
626  AdvancePos();
627  newchar = true;
628  currentCharUtf8 = NULL;
629  Set();
630  return osContinue;
631  }
632  switch (int(Key)) {
633  case kRed: // Switch between upper- and lowercase characters
634  if (InEditMode()) {
635  if (!insert || !newchar) {
636  uppercase = !uppercase;
637  valueUtf8[pos] = uppercase ? Utf8to(upper, valueUtf8[pos]) : Utf8to(lower, valueUtf8[pos]);
638  }
639  }
640  else
641  return osUnknown;
642  break;
643  case kGreen: // Toggle insert/overwrite modes
644  if (InEditMode()) {
645  insert = !insert;
646  newchar = true;
647  SetHelpKeys();
648  }
649  else
650  return osUnknown;
651  break;
652  case kYellow|k_Repeat:
653  case kYellow: // Remove the character at the current position; in insert mode it is the character to the right of the cursor
654  if (InEditMode()) {
655  if (lengthUtf8 > 1) {
656  if (!insert || pos < lengthUtf8 - 1)
657  Delete();
658  else if (insert && pos == lengthUtf8 - 1)
659  valueUtf8[pos] = ' '; // in insert mode, deleting the last character replaces it with a blank to keep the cursor position
660  // reduce position, if we removed the last character
661  if (pos == lengthUtf8)
662  pos--;
663  }
664  else if (lengthUtf8 == 1)
665  valueUtf8[0] = ' '; // This is the last character in the string, replace it with a blank
666  if (Utf8is(alpha, valueUtf8[pos]))
667  uppercase = Utf8is(upper, valueUtf8[pos]);
668  newchar = true;
669  }
670  else
671  return osUnknown;
672  break;
673  case kBlue|k_Repeat:
674  case kBlue: if (InEditMode())
675  InsertMacro();
676  else
677  return osUnknown;
678  break;
679  case kLeft|k_Repeat:
680  case kLeft: if (pos > 0) {
681  if (!insert || newchar)
682  pos--;
683  newchar = true;
684  if (!insert && Utf8is(alpha, valueUtf8[pos]))
685  uppercase = Utf8is(upper, valueUtf8[pos]);
686  }
687  break;
688  case kRight|k_Repeat:
689  case kRight: if (InEditMode())
690  AdvancePos();
691  else {
692  EnterEditMode();
693  SetHelpKeys();
694  }
695  break;
696  case kUp|k_Repeat:
697  case kUp:
698  case kDown|k_Repeat:
699  case kDown: if (InEditMode()) {
700  if (insert && newchar) {
701  // create a new character in insert mode
702  if (lengthUtf8 < length - 1)
703  Insert();
704  }
705  if (uppercase)
706  valueUtf8[pos] = Utf8to(upper, Inc(Utf8to(lower, valueUtf8[pos]), NORMALKEY(Key) == kUp));
707  else
708  valueUtf8[pos] = Inc( valueUtf8[pos], NORMALKEY(Key) == kUp);
709  newchar = false;
710  }
711  else
712  return cMenuEditItem::ProcessKey(Key);
713  break;
714  case k0|k_Repeat ... k9|k_Repeat:
715  case k0 ... k9: {
716  if (InEditMode()) {
718  if (!SameKey) {
719  if (!newchar)
720  AdvancePos();
721  currentCharUtf8 = NULL;
722  }
723  if (!currentCharUtf8 || !*currentCharUtf8 || *currentCharUtf8 == '\t') {
724  // find the beginning of the character map entry for Key
725  int n = NORMALKEY(Key) - k0;
727  while (n > 0 && *currentCharUtf8) {
728  if (*currentCharUtf8++ == '\t')
729  n--;
730  }
731  // find first allowed character
732  while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8))
733  currentCharUtf8++;
734  }
735  if (*currentCharUtf8 && *currentCharUtf8 != '\t') {
736  if (insert && newchar) {
737  // create a new character in insert mode
738  if (lengthUtf8 < length - 1)
739  Insert();
740  }
742  if (uppercase)
743  valueUtf8[pos] = Utf8to(upper, valueUtf8[pos]);
744  // find next allowed character
745  do {
746  currentCharUtf8++;
747  } while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8));
748  newchar = false;
750  }
751  }
752  else
753  Type('0' + NORMALKEY(Key) - k0);
754  }
755  else
756  return cMenuEditItem::ProcessKey(Key);
757  }
758  break;
759  case kBack:
760  case kOk: if (InEditMode()) {
761  LeaveEditMode(Key == kOk);
762  SetHelpKeys();
763  break;
764  }
765  // run into default
766  default: if (InEditMode() && BASICKEY(Key) == kKbd) {
767  int c = KEYKBD(Key);
768  if (c <= 0xFF) { // FIXME what about other UTF-8 characters?
769  if (IsAllowed(Utf8to(lower, c)))
770  Type(c);
771  else {
772  switch (c) {
773  case 0x7F: // backspace
774  if (pos > 0) {
775  pos--;
776  return ProcessKey(kYellow);
777  }
778  break;
779  default: ;
780  }
781  }
782  }
783  else {
784  switch (c) {
785  case kfHome: pos = 0; break;
786  case kfEnd: pos = lengthUtf8 - 1; break;
787  case kfIns: return ProcessKey(kGreen);
788  case kfDel: return ProcessKey(kYellow);
789  default: ;
790  }
791  }
792  }
793  else
794  return cMenuEditItem::ProcessKey(Key);
795  }
796  Set();
797  return osContinue;
798 }
799 
800 // --- cMenuEditStraItem -----------------------------------------------------
801 
802 cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
803 :cMenuEditIntItem(Name, Value, 0, NumStrings - 1)
804 {
805  strings = Strings;
806  Set();
807 }
808 
810 {
812 }
813 
814 // --- cMenuEditStrlItem -----------------------------------------------------
815 
816 cMenuEditStrlItem::cMenuEditStrlItem(const char *Name, char *Value, int Length, const cStringList *Strings)
817 :cMenuEditIntItem(Name, &index, 0, Strings->Size() - 1)
818 {
819  strings = Strings;
820  value = Value;
821  length = Length;
822  index = strings->Find(value);
823  if (index < 0)
824  index = 0;
825  Set();
826 }
827 
829 {
831  SetValue(value);
832 }
833 
834 // --- cMenuEditChanItem -----------------------------------------------------
835 
836 cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value, const char *NoneString)
837 :cMenuEditIntItem(Name, Value, NoneString ? 0 : 1, cChannels::MaxNumber())
838 {
839  channelID = NULL;
840  noneString = NoneString;
841  dummyValue = 0;
842  Set();
843 }
844 
845 cMenuEditChanItem::cMenuEditChanItem(const char *Name, cString *ChannelID, const char *NoneString)
846 :cMenuEditIntItem(Name, &dummyValue, NoneString ? 0 : 1, cChannels::MaxNumber())
847 {
848  channelID = ChannelID;
849  noneString = NoneString;
851  const cChannel *Channel = Channels->GetByChannelID(tChannelID::FromString(*ChannelID));
852  dummyValue = Channel ? Channel->Number() : 0;
853  Set();
854 }
855 
857 {
858  if (*value > 0) {
859  char buf[255];
861  const cChannel *Channel = Channels->GetByNumber(*value);
862  snprintf(buf, sizeof(buf), "%d %s", *value, Channel ? Channel->Name() : "");
863  SetValue(buf);
864  if (channelID)
865  *channelID = Channel ? Channel->GetChannelID().ToString() : "";
866  }
867  else if (noneString) {
869  if (channelID)
870  *channelID = "";
871  }
872 }
873 
875 {
876  int delta = 1;
877 
878  switch (int(Key)) {
879  case kLeft|k_Repeat:
880  case kLeft: delta = -1;
881  case kRight|k_Repeat:
882  case kRight:
883  {
885  const cChannel *Channel = Channels->GetByNumber(*value + delta, delta);
886  if (Channel)
887  *value = Channel->Number();
888  else if (delta < 0 && noneString)
889  *value = 0;
890  if (channelID)
891  *channelID = Channel ? Channel->GetChannelID().ToString() : "";
892  Set();
893  }
894  break;
895  default: return cMenuEditIntItem::ProcessKey(Key);
896  }
897  return osContinue;
898 }
899 
900 // --- cMenuEditTranItem -----------------------------------------------------
901 
902 cMenuEditTranItem::cMenuEditTranItem(const char *Name, int *Value, int *Source)
903 :cMenuEditChanItem(Name, &number, "-")
904 {
905  number = 0;
906  source = Source;
907  transponder = Value;
909  const cChannel *Channel = Channels->First();
910  while (Channel) {
911  if (!Channel->GroupSep() && *source == Channel->Source() && ISTRANSPONDER(Channel->Transponder(), *Value)) {
912  number = Channel->Number();
913  break;
914  }
915  Channel = Channels->Next(Channel);
916  }
917  Set();
918 }
919 
921 {
924  if (const cChannel *Channel = Channels->GetByNumber(number)) {
925  *source = Channel->Source();
926  *transponder = Channel->Transponder();
927  }
928  else {
929  *source = 0;
930  *transponder = 0;
931  }
932  return state;
933 }
934 
935 // --- cMenuEditDateItem -----------------------------------------------------
936 
937 static int ParseWeekDays(const char *s)
938 {
939  time_t day;
940  int weekdays;
941  return cTimer::ParseDay(s, day, weekdays) ? weekdays : 0;
942 }
943 
944 int cMenuEditDateItem::days[] = { ParseWeekDays("M------"),
945  ParseWeekDays("-T-----"),
946  ParseWeekDays("--W----"),
947  ParseWeekDays("---T---"),
948  ParseWeekDays("----F--"),
949  ParseWeekDays("-----S-"),
950  ParseWeekDays("------S"),
951  ParseWeekDays("MTWTF--"),
952  ParseWeekDays("MTWTFS-"),
953  ParseWeekDays("MTWTFSS"),
954  ParseWeekDays("-----SS"),
955  0 };
956 
957 cMenuEditDateItem::cMenuEditDateItem(const char *Name, time_t *Value, int *WeekDays)
958 :cMenuEditItem(Name)
959 {
960  value = Value;
961  weekdays = WeekDays;
962  oldvalue = 0;
963  oldweekdays = 0;
965  Set();
966 }
967 
969 {
970  for (unsigned int i = 0; i < sizeof(days) / sizeof(int); i++)
971  if (WeekDays == days[i])
972  return i;
973  return 0;
974 }
975 
977 {
978 #define DATEBUFFERSIZE 32
979  char buf[DATEBUFFERSIZE];
980  if (weekdays && *weekdays) {
981  SetValue(cTimer::PrintDay(0, *weekdays, false));
982  return;
983  }
984  else if (*value) {
985  struct tm tm_r;
986  localtime_r(value, &tm_r);
987  strftime(buf, DATEBUFFERSIZE, "%Y-%m-%d ", &tm_r);
988  strcat(buf, WeekDayName(tm_r.tm_wday));
989  }
990  else
991  *buf = 0;
992  SetValue(buf);
993 }
994 
996 {
997  if (weekdays) {
998  if (*weekdays) {
999  *value = cTimer::SetTime(oldvalue ? oldvalue : time(NULL), 0);
1000  oldvalue = 0;
1001  oldweekdays = *weekdays;
1002  *weekdays = 0;
1003  }
1004  else {
1006  oldweekdays = 0;
1008  oldvalue = *value;
1009  *value = 0;
1010  }
1011  Set();
1012  }
1013 }
1014 
1016 {
1018 
1019  if (state == osUnknown) {
1020  time_t now = time(NULL);
1021  if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
1022  if (!weekdays || !*weekdays) {
1023  // Decrement single day:
1024  time_t v = *value;
1025  v -= SECSINDAY;
1026  if (v < now) {
1027  if (now <= v + SECSINDAY) { // switched from tomorrow to today
1028  if (!weekdays)
1029  v = 0;
1030  }
1031  else if (weekdays) { // switched from today to yesterday, so enter weekdays mode
1032  v = 0;
1033  dayindex = sizeof(days) / sizeof(int) - 2;
1034  *weekdays = days[dayindex];
1035  }
1036  else // don't go before today
1037  v = *value;
1038  }
1039  *value = v;
1040  }
1041  else {
1042  // Decrement weekday index:
1043  if (dayindex > 0)
1044  *weekdays = days[--dayindex];
1045  }
1046  }
1047  else if (NORMALKEY(Key) == kRight) {
1048  if (!weekdays || !*weekdays) {
1049  // Increment single day:
1050  if (!*value)
1051  *value = cTimer::SetTime(now, 0);
1052  *value += SECSINDAY;
1053  }
1054  else {
1055  // Increment weekday index:
1056  *weekdays = days[++dayindex];
1057  if (!*weekdays) { // was last weekday entry, so switch to today
1058  *value = cTimer::SetTime(now, 0);
1059  dayindex = 0;
1060  }
1061  }
1062  }
1063  else if (weekdays) {
1064  if (Key == k0) {
1065  // Toggle between weekdays and single day:
1066  ToggleRepeating();
1067  return osContinue; // ToggleRepeating) has already called Set()
1068  }
1069  else if (k1 <= Key && Key <= k7) {
1070  // Toggle individual weekdays:
1071  if (*weekdays) {
1072  int v = *weekdays ^ (1 << (Key - k1));
1073  if (v != 0)
1074  *weekdays = v; // can't let this become all 0
1075  }
1076  }
1077  else
1078  return state;
1079  }
1080  else
1081  return state;
1082  Set();
1083  state = osContinue;
1084  }
1085  return state;
1086 }
1087 
1088 // --- cMenuEditTimeItem -----------------------------------------------------
1089 
1090 cMenuEditTimeItem::cMenuEditTimeItem(const char *Name, int *Value)
1091 :cMenuEditItem(Name)
1092 {
1093  value = Value;
1094  hh = *value / 100;
1095  mm = *value % 100;
1096  pos = 0;
1097  Set();
1098 }
1099 
1101 {
1102  switch (pos) {
1103  case 1: SetValue(cString::sprintf("%01d-:--", hh / 10)); break;
1104  case 2: SetValue(cString::sprintf("%02d:--", hh)); break;
1105  case 3: SetValue(cString::sprintf("%02d:%01d-", hh, mm / 10)); break;
1106  default: SetValue(cString::sprintf("%02d:%02d", hh, mm));
1107  }
1108 }
1109 
1111 {
1113 
1114  if (state == osUnknown) {
1115  if (k0 <= Key && Key <= k9) {
1116  if (fresh || pos > 3) {
1117  pos = 0;
1118  fresh = false;
1119  }
1120  int n = Key - k0;
1121  switch (pos) {
1122  case 0: if (n <= 2) {
1123  hh = n * 10;
1124  mm = 0;
1125  pos++;
1126  }
1127  break;
1128  case 1: if (hh + n <= 23) {
1129  hh += n;
1130  pos++;
1131  }
1132  break;
1133  case 2: if (n <= 5) {
1134  mm += n * 10;
1135  pos++;
1136  }
1137  break;
1138  case 3: if (mm + n <= 59) {
1139  mm += n;
1140  pos++;
1141  }
1142  break;
1143  default: ;
1144  }
1145  }
1146  else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
1147  if (--mm < 0) {
1148  mm = 59;
1149  if (--hh < 0)
1150  hh = 23;
1151  }
1152  fresh = true;
1153  }
1154  else if (NORMALKEY(Key) == kRight) {
1155  if (++mm > 59) {
1156  mm = 0;
1157  if (++hh > 23)
1158  hh = 0;
1159  }
1160  fresh = true;
1161  }
1162  else
1163  return state;
1164  *value = hh * 100 + mm;
1165  Set();
1166  state = osContinue;
1167  }
1168  return state;
1169 }
1170 
1171 // --- cMenuEditMapItem ------------------------------------------------------
1172 
1173 cMenuEditMapItem::cMenuEditMapItem(const char *Name, int *Value, const tDvbParameterMap *Map, const char *ZeroString)
1174 :cMenuEditItem(Name)
1175 {
1176  value = Value;
1177  map = Map;
1178  zeroString = ZeroString;
1179  Set();
1180 }
1181 
1183 {
1184  const char *s = NULL;
1185  int n = MapToUser(*value, map, &s);
1186  if (n == 0 && zeroString)
1188  else if (n >= 0) {
1189  if (s)
1190  SetValue(s);
1191  else {
1192  char buf[16];
1193  snprintf(buf, sizeof(buf), "%d", n);
1194  SetValue(buf);
1195  }
1196  }
1197  else
1198  SetValue("???");
1199 }
1200 
1202 {
1204 
1205  if (state == osUnknown) {
1206  int newValue = *value;
1207  int n = DriverIndex(*value, map);
1208  if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
1209  if (n-- > 0)
1210  newValue = map[n].driverValue;
1211  }
1212  else if (NORMALKEY(Key) == kRight) {
1213  if (map[++n].userValue >= 0)
1214  newValue = map[n].driverValue;
1215  }
1216  else
1217  return state;
1218  if (newValue != *value) {
1219  *value = newValue;
1220  Set();
1221  }
1222  state = osContinue;
1223  }
1224  return state;
1225 }
1226 
1227 // --- cMenuSetupPage --------------------------------------------------------
1228 
1230 :cOsdMenu("", 36)
1231 {
1233  plugin = NULL;
1234 }
1235 
1236 void cMenuSetupPage::SetSection(const char *Section)
1237 {
1238  SetTitle(cString::sprintf("%s - %s", tr("Setup"), Section));
1239 }
1240 
1242 {
1243  eOSState state = cOsdMenu::ProcessKey(Key);
1244 
1245  if (state == osUnknown) {
1246  switch (Key) {
1247  case kOk: Store();
1248  state = osBack;
1249  break;
1250  default: break;
1251  }
1252  }
1253  return state;
1254 }
1255 
1257 {
1259  plugin = Plugin;
1260  SetSection(cString::sprintf("%s '%s'", tr("Plugin"), plugin->Name()));
1261 }
1262 
1263 void cMenuSetupPage::SetupStore(const char *Name, const char *Value)
1264 {
1265  if (plugin)
1266  plugin->SetupStore(Name, Value);
1267 }
1268 
1269 void cMenuSetupPage::SetupStore(const char *Name, int Value)
1270 {
1271  if (plugin)
1272  plugin->SetupStore(Name, Value);
1273 }
#define LOCK_CHANNELS_READ
Definition: channels.h:273
#define ISTRANSPONDER(f1, f2)
Definition: channels.h:18
int Source(void) const
Definition: channels.h:154
int Number(void) const
Definition: channels.h:181
const char * Name(void) const
Definition: channels.c:122
tChannelID GetChannelID(void) const
Definition: channels.h:194
bool GroupSep(void) const
Definition: channels.h:183
int Transponder(void) const
Returns the transponder frequency in MHz, plus the polarization in case of sat.
Definition: channels.c:155
Definition: font.h:37
virtual int Width(void) const =0
Returns the original character width as requested when the font was created, or 0 if the default widt...
cListObject * Next(void) const
Definition: tools.h:547
virtual void Set(void) override
Definition: menuitems.c:165
cMenuEditBitItem(const char *Name, uint *Value, uint Mask, const char *FalseString=NULL, const char *TrueString=NULL)
Definition: menuitems.c:156
virtual void Set(void) override
Definition: menuitems.c:147
cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString=NULL, const char *TrueString=NULL)
Definition: menuitems.c:139
const char * falseString
Definition: menuitems.h:46
const char * trueString
Definition: menuitems.h:46
cString * channelID
Definition: menuitems.h:171
cMenuEditChanItem(const char *Name, int *Value, const char *NoneString=NULL)
Definition: menuitems.c:836
const char * noneString
Definition: menuitems.h:169
virtual void Set(void) override
Definition: menuitems.c:856
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:874
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:360
const char * current
Definition: menuitems.h:100
cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
Definition: menuitems.c:336
char * allowed
Definition: menuitems.h:99
virtual void Set(void) override
Definition: menuitems.c:352
int FindDayIndex(int WeekDays)
Definition: menuitems.c:968
void ToggleRepeating(void)
Definition: menuitems.c:995
static int days[]
Definition: menuitems.h:191
virtual void Set(void) override
Definition: menuitems.c:976
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:1015
time_t * value
Definition: menuitems.h:192
cMenuEditDateItem(const char *Name, time_t *Value, int *WeekDays=NULL)
Definition: menuitems.c:957
cMenuEditIntItem(const char *Name, int *Value, int Min=0, int Max=INT_MAX, const char *MinString=NULL, const char *MaxString=NULL)
Definition: menuitems.c:66
const char * maxString
Definition: menuitems.h:37
virtual void Set(void) override
Definition: menuitems.c:81
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:94
const char * minString
Definition: menuitems.h:37
cMenuEditIntxItem(const char *Name, int *Value, int Min=INT_MIN, int Max=INT_MAX, int Factor=1, const char *NegString=NULL, const char *PosString=NULL)
Definition: menuitems.c:227
void SetHelpKeys(void)
Definition: menuitems.c:236
const char * negString
Definition: menuitems.h:76
const char * posString
Definition: menuitems.h:76
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:252
virtual void Set(void) override
Definition: menuitems.c:242
const char * helpYellow
Definition: menuitems.h:22
bool DisplayHelp(bool Current)
Definition: menuitems.c:53
char * name
Definition: menuitems.h:21
const char * helpRed
Definition: menuitems.h:22
void SetHelp(const char *Red, const char *Green=NULL, const char *Yellow=NULL, const char *Blue=NULL)
Definition: menuitems.c:43
const char * helpGreen
Definition: menuitems.h:22
void SetValue(const char *Value)
Definition: menuitems.c:37
bool helpDisplayed
Definition: menuitems.h:23
cMenuEditItem(const char *Name)
Definition: menuitems.c:26
const char * helpBlue
Definition: menuitems.h:22
const tDvbParameterMap * map
Definition: menuitems.h:219
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:1201
virtual void Set(void) override
Definition: menuitems.c:1182
const char * zeroString
Definition: menuitems.h:220
cMenuEditMapItem(const char *Name, int *Value, const tDvbParameterMap *Map, const char *ZeroString=NULL)
Definition: menuitems.c:1173
virtual void Set(void) override
Definition: menuitems.c:182
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:196
cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind=false)
Definition: menuitems.c:173
virtual void Set(void) override
Definition: menuitems.c:288
cMenuEditPrcItem(const char *Name, double *Value, double Min=0.0, double Max=1.0, int Decimals=0)
Definition: menuitems.c:271
double * value
Definition: menuitems.h:86
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:295
const char * allowed
Definition: menuitems.h:112
uint Inc(uint c, bool Up)
Definition: menuitems.c:548
uint * valueUtf8
Definition: menuitems.h:119
void Insert(void)
Definition: menuitems.c:579
void Delete(void)
Definition: menuitems.c:586
void LeaveEditMode(bool SaveValue=false)
Definition: menuitems.c:438
uint * allowedUtf8
Definition: menuitems.h:120
void Type(uint c)
Definition: menuitems.c:565
cTimeMs autoAdvanceTimeout
Definition: menuitems.h:124
uint * currentCharUtf8
Definition: menuitems.h:122
virtual void Set(void) override
Definition: menuitems.c:496
cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed=NULL)
Definition: menuitems.c:384
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:617
void InsertMacro(void)
Definition: menuitems.c:592
const char ** macros
Definition: menuitems.h:115
bool InEditMode(void)
Definition: menuitems.h:137
void AdvancePos(void)
Definition: menuitems.c:478
void SetMacros(const char **Macros)
Definition: menuitems.c:414
void SetHelpKeys(void)
Definition: menuitems.c:459
void EnterEditMode(void)
Definition: menuitems.c:421
uint * charMapUtf8
Definition: menuitems.h:121
uint * IsAllowed(uint c)
Definition: menuitems.c:467
virtual void Set(void) override
Definition: menuitems.c:809
const char *const * strings
Definition: menuitems.h:148
cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char *const *Strings)
Definition: menuitems.c:802
const cStringList * strings
Definition: menuitems.h:157
virtual void Set(void) override
Definition: menuitems.c:828
cMenuEditStrlItem(const char *Name, char *Value, int Length, const cStringList *Strings)
Definition: menuitems.c:816
cMenuEditTimeItem(const char *Name, int *Value)
Definition: menuitems.c:1090
virtual void Set(void) override
Definition: menuitems.c:1100
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:1110
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:920
cMenuEditTranItem(const char *Name, int *Value, int *Source)
Definition: menuitems.c:902
virtual eOSState ProcessKey(eKeys Key) override
Definition: menuitems.c:1241
virtual void Store(void)=0
cMenuSetupPage(void)
Definition: menuitems.c:1229
void SetSection(const char *Section)
Definition: menuitems.c:1236
void SetupStore(const char *Name, const char *Value=NULL)
Definition: menuitems.c:1263
cPlugin * plugin
Definition: menuitems.h:231
void SetPlugin(cPlugin *Plugin)
Definition: menuitems.c:1256
virtual eOSState ProcessKey(eKeys Key)
Definition: osdbase.c:63
eOSState state
Definition: osdbase.h:51
bool fresh
Definition: osdbase.h:54
void SetText(const char *Text, bool Copy=true)
Definition: osdbase.c:42
virtual eOSState ProcessKey(eKeys Key) override
Definition: osdbase.c:573
void SetTitle(const char *Title)
Definition: osdbase.c:187
void SetMenuCategory(eMenuCategory MenuCategory)
Definition: osdbase.c:125
Definition: plugin.h:22
const char * Name(void)
Definition: plugin.h:36
void SetupStore(const char *Name, const char *Value=NULL)
Definition: plugin.c:111
int NumberKeysForChars
Definition: config.h:331
int EditableWidth(void)
Definition: skins.h:48
static cSkinDisplay * Current(void)
Returns the currently active cSkinDisplay.
Definition: skins.h:61
virtual void SetButtons(const char *Red, const char *Green=NULL, const char *Yellow=NULL, const char *Blue=NULL)
Sets the color buttons to the given strings, provided this cSkinDisplay actually has a color button d...
Definition: skins.h:53
static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
Definition: status.c:111
int Find(const char *s) const
Definition: tools.c:1630
Definition: tools.h:178
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1195
void Set(int Ms=0)
Sets the timer.
Definition: tools.c:808
bool TimedOut(void) const
Definition: tools.c:813
static time_t SetTime(time_t t, int SecondsFromMidnight)
Definition: timers.c:548
static int GetWDay(time_t t)
Definition: timers.c:524
static cString PrintDay(time_t Day, int WeekDays, bool SingleByteChars)
Definition: timers.c:402
static bool ParseDay(const char *s, time_t &Day, int &WeekDays)
Definition: timers.c:343
T & At(int Index) const
Definition: tools.h:731
cSetup Setup
Definition: config.c:372
int DriverIndex(int Value, const tDvbParameterMap *Map)
Definition: dvbdevice.c:167
int MapToUser(int Value, const tDvbParameterMap *Map, const char **String)
Definition: dvbdevice.c:178
#define tr(s)
Definition: i18n.h:85
#define trNOOP(s)
Definition: i18n.h:88
#define BASICKEY(k)
Definition: keys.h:83
#define KEYKBD(k)
Definition: keys.h:85
#define NORMALKEY(k)
Definition: keys.h:79
eKeys
Definition: keys.h:16
@ kRight
Definition: keys.h:23
@ k9
Definition: keys.h:28
@ kRed
Definition: keys.h:24
@ kUp
Definition: keys.h:17
@ kNone
Definition: keys.h:55
@ k7
Definition: keys.h:28
@ kDown
Definition: keys.h:18
@ kGreen
Definition: keys.h:25
@ k1
Definition: keys.h:28
@ kLeft
Definition: keys.h:22
@ kBlue
Definition: keys.h:27
@ kKbd
Definition: keys.h:56
@ k0
Definition: keys.h:28
@ kYellow
Definition: keys.h:26
@ kBack
Definition: keys.h:21
@ k_Repeat
Definition: keys.h:61
@ kOk
Definition: keys.h:20
#define AUTO_ADVANCE_TIMEOUT
Definition: menuitems.c:20
static int ParseWeekDays(const char *s)
Definition: menuitems.c:937
const char * FileNameChars
Definition: menuitems.c:22
#define DATEBUFFERSIZE
eOSState
Definition: osdbase.h:18
@ osContinue
Definition: osdbase.h:19
@ osUnknown
Definition: osdbase.h:18
@ osBack
Definition: osdbase.h:33
@ kfIns
Definition: remote.h:101
@ kfDel
Definition: remote.h:102
@ kfEnd
Definition: remote.h:98
@ kfHome
Definition: remote.h:97
@ mcSetup
Definition: skins.h:120
@ mcPluginSetup
Definition: skins.h:119
static tChannelID FromString(const char *s)
Definition: channels.c:24
cString ToString(void) const
Definition: channels.c:41
int Utf8ToArray(const char *s, uint *a, int Size)
Converts the given character bytes (including the terminating 0) into an array of UTF-8 symbols of th...
Definition: tools.c:938
cString WeekDayName(int WeekDay)
Converts the given WeekDay (0=Sunday, 1=Monday, ...) to a three letter day name.
Definition: tools.c:1218
char * stripspace(char *s)
Definition: tools.c:227
char * strn0cpy(char *dest, const char *src, size_t n)
Definition: tools.c:131
int Utf8FromArray(const uint *a, char *s, int Size, int Max)
Converts the given array of UTF-8 symbols (including the terminating 0) into a sequence of character ...
Definition: tools.c:956
#define SECSINDAY
Definition: tools.h:42
#define Utf8to(conv, c)
Definition: tools.h:148
bool DoubleEqual(double a, double b)
Definition: tools.h:97
#define Utf8is(ccls, c)
Definition: tools.h:149