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