vdr  1.7.27
menuitems.c
Go to the documentation of this file.
00001 /*
00002  * menuitems.c: General purpose menu items
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: menuitems.c 2.13 2012/03/13 11:21:57 kls Exp $
00008  */
00009 
00010 #include "menuitems.h"
00011 #include <ctype.h>
00012 #include <math.h>
00013 #include <wctype.h>
00014 #include "i18n.h"
00015 #include "plugin.h"
00016 #include "remote.h"
00017 #include "skins.h"
00018 #include "status.h"
00019 
00020 #define AUTO_ADVANCE_TIMEOUT  1500 // ms before auto advance when entering characters via numeric keys
00021 
00022 const char *FileNameChars = trNOOP("FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&");
00023 
00024 // --- cMenuEditItem ---------------------------------------------------------
00025 
00026 cMenuEditItem::cMenuEditItem(const char *Name)
00027 {
00028   name = strdup(Name ? Name : "???");
00029   SetHelp(NULL);
00030 }
00031 
00032 cMenuEditItem::~cMenuEditItem()
00033 {
00034   free(name);
00035 }
00036 
00037 void cMenuEditItem::SetValue(const char *Value)
00038 {
00039   cString buffer = cString::sprintf("%s:\t%s", name, Value);
00040   SetText(buffer);
00041   cStatus::MsgOsdCurrentItem(buffer);
00042 }
00043 
00044 void cMenuEditItem::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
00045 {
00046   // strings are NOT copied - must be constants!!!
00047   helpRed    = Red;
00048   helpGreen  = Green;
00049   helpYellow = Yellow;
00050   helpBlue   = Blue;
00051   helpDisplayed = false;
00052 }
00053 
00054 bool cMenuEditItem::DisplayHelp(void)
00055 {
00056   bool HasHelp = helpRed || helpGreen || helpYellow || helpBlue;
00057   if (HasHelp && !helpDisplayed) {
00058      cSkinDisplay::Current()->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
00059      cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
00060      helpDisplayed = true;
00061      }
00062   return HasHelp;
00063 }
00064 
00065 // --- cMenuEditIntItem ------------------------------------------------------
00066 
00067 cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max, const char *MinString, const char *MaxString)
00068 :cMenuEditItem(Name)
00069 {
00070   value = Value;
00071   min = Min;
00072   max = Max;
00073   minString = MinString;
00074   maxString = MaxString;
00075   if (*value < min)
00076      *value = min;
00077   else if (*value > max)
00078      *value = max;
00079   Set();
00080 }
00081 
00082 void cMenuEditIntItem::Set(void)
00083 {
00084   if (minString && *value == min)
00085      SetValue(minString);
00086   else if (maxString && *value == max)
00087      SetValue(maxString);
00088   else {
00089      char buf[16];
00090      snprintf(buf, sizeof(buf), "%d", *value);
00091      SetValue(buf);
00092      }
00093 }
00094 
00095 eOSState cMenuEditIntItem::ProcessKey(eKeys Key)
00096 {
00097   eOSState state = cMenuEditItem::ProcessKey(Key);
00098 
00099   if (state == osUnknown) {
00100      int newValue = *value;
00101      bool IsRepeat = Key & k_Repeat;
00102      Key = NORMALKEY(Key);
00103      switch (Key) {
00104        case kNone: break;
00105        case k0 ... k9:
00106             if (fresh) {
00107                newValue = 0;
00108                fresh = false;
00109                }
00110             newValue = newValue * 10 + (Key - k0);
00111             break;
00112        case kLeft: // TODO might want to increase the delta if repeated quickly?
00113             newValue = *value - 1;
00114             fresh = true;
00115             if (!IsRepeat && newValue < min && max != INT_MAX)
00116                newValue = max;
00117             break;
00118        case kRight:
00119             newValue = *value + 1;
00120             fresh = true;
00121             if (!IsRepeat && newValue > max && min != INT_MIN)
00122                newValue = min;
00123             break;
00124        default:
00125             if (*value < min) { *value = min; Set(); }
00126             if (*value > max) { *value = max; Set(); }
00127             return state;
00128        }
00129      if (newValue != *value && (!fresh || min <= newValue) && newValue <= max) {
00130         *value = newValue;
00131         Set();
00132         }
00133      state = osContinue;
00134      }
00135   return state;
00136 }
00137 
00138 // --- cMenuEditBoolItem -----------------------------------------------------
00139 
00140 cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString)
00141 :cMenuEditIntItem(Name, Value, 0, 1)
00142 {
00143   falseString = FalseString ? FalseString : tr("no");
00144   trueString = TrueString ? TrueString : tr("yes");
00145   Set();
00146 }
00147 
00148 void cMenuEditBoolItem::Set(void)
00149 {
00150   char buf[16];
00151   snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
00152   SetValue(buf);
00153 }
00154 
00155 // --- cMenuEditBitItem ------------------------------------------------------
00156 
00157 cMenuEditBitItem::cMenuEditBitItem(const char *Name, uint *Value, uint Mask, const char *FalseString, const char *TrueString)
00158 :cMenuEditBoolItem(Name, &bit, FalseString, TrueString)
00159 {
00160   value = Value;
00161   bit = (*value & Mask) != 0;
00162   mask = Mask;
00163   Set();
00164 }
00165 
00166 void cMenuEditBitItem::Set(void)
00167 {
00168   *value = bit ? *value | mask : *value & ~mask;
00169   cMenuEditBoolItem::Set();
00170 }
00171 
00172 // --- cMenuEditNumItem ------------------------------------------------------
00173 
00174 cMenuEditNumItem::cMenuEditNumItem(const char *Name, char *Value, int Length, bool Blind)
00175 :cMenuEditItem(Name)
00176 {
00177   value = Value;
00178   length = Length;
00179   blind = Blind;
00180   Set();
00181 }
00182 
00183 void cMenuEditNumItem::Set(void)
00184 {
00185   if (blind) {
00186      char buf[length + 1];
00187      int i;
00188      for (i = 0; i < length && value[i]; i++)
00189          buf[i] = '*';
00190      buf[i] = 0;
00191      SetValue(buf);
00192      }
00193   else
00194      SetValue(value);
00195 }
00196 
00197 eOSState cMenuEditNumItem::ProcessKey(eKeys Key)
00198 {
00199   eOSState state = cMenuEditItem::ProcessKey(Key);
00200 
00201   if (state == osUnknown) {
00202      Key = NORMALKEY(Key);
00203      switch (Key) {
00204        case kLeft: {
00205             int l = strlen(value);
00206             if (l > 0)
00207                value[l - 1] = 0;
00208             }
00209             break;
00210        case k0 ... k9: {
00211             int l = strlen(value);
00212             if (l < length) {
00213                value[l] = Key - k0 + '0';
00214                value[l + 1] = 0;
00215                }
00216             }
00217             break;
00218        default: return state;
00219        }
00220      Set();
00221      state = osContinue;
00222      }
00223   return state;
00224 }
00225 
00226 // --- cMenuEditPrcItem ------------------------------------------------------
00227 
00228 cMenuEditPrcItem::cMenuEditPrcItem(const char *Name, double *Value, double Min, double Max, int Decimals)
00229 :cMenuEditItem(Name)
00230 {
00231   value = Value;
00232   min = Min;
00233   max = Max;
00234   decimals = Decimals;
00235   factor = 100;
00236   while (Decimals-- > 0)
00237         factor *= 10;
00238   if (*value < min)
00239      *value = min;
00240   else if (*value > max)
00241      *value = max;
00242   Set();
00243 }
00244 
00245 void cMenuEditPrcItem::Set(void)
00246 {
00247   char buf[16];
00248   snprintf(buf, sizeof(buf), "%.*f", decimals, *value * 100);
00249   SetValue(buf);
00250 }
00251 
00252 eOSState cMenuEditPrcItem::ProcessKey(eKeys Key)
00253 {
00254   eOSState state = cMenuEditItem::ProcessKey(Key);
00255 
00256   if (state == osUnknown) {
00257      double newValue = round(*value * factor); // avoids precision problems
00258      Key = NORMALKEY(Key);
00259      switch (Key) {
00260        case kNone: break;
00261        case k0 ... k9:
00262             if (fresh) {
00263                newValue = 0;
00264                fresh = false;
00265                }
00266             newValue = newValue * 10 + (Key - k0);
00267             break;
00268        case kLeft: // TODO might want to increase the delta if repeated quickly?
00269             newValue--;
00270             fresh = true;
00271             break;
00272        case kRight:
00273             newValue++;
00274             fresh = true;
00275             break;
00276        default:
00277             if (*value < min) { *value = min; Set(); }
00278             if (*value > max) { *value = max; Set(); }
00279             return state;
00280        }
00281      newValue /= factor;
00282      if (!DoubleEqual(newValue, *value) && (!fresh || min <= newValue) && newValue <= max) {
00283         *value = newValue;
00284         Set();
00285         }
00286      state = osContinue;
00287      }
00288   return state;
00289 }
00290 
00291 // --- cMenuEditChrItem ------------------------------------------------------
00292 
00293 cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
00294 :cMenuEditItem(Name)
00295 {
00296   value = Value;
00297   allowed = strdup(Allowed ? Allowed : "");
00298   current = strchr(allowed, *Value);
00299   if (!current)
00300      current = allowed;
00301   Set();
00302 }
00303 
00304 cMenuEditChrItem::~cMenuEditChrItem()
00305 {
00306   free(allowed);
00307 }
00308 
00309 void cMenuEditChrItem::Set(void)
00310 {
00311   char buf[2];
00312   buf[0] = *value;
00313   buf[1] = '\0';
00314   SetValue(buf);
00315 }
00316 
00317 eOSState cMenuEditChrItem::ProcessKey(eKeys Key)
00318 {
00319   eOSState state = cMenuEditItem::ProcessKey(Key);
00320 
00321   if (state == osUnknown) {
00322      if (NORMALKEY(Key) == kLeft) {
00323         if (current > allowed)
00324            current--;
00325         }
00326      else if (NORMALKEY(Key) == kRight) {
00327         if (*(current + 1))
00328            current++;
00329         }
00330      else
00331         return state;
00332      *value = *current;
00333      Set();
00334      state = osContinue;
00335      }
00336   return state;
00337 }
00338 
00339 // --- cMenuEditStrItem ------------------------------------------------------
00340 
00341 cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed)
00342 :cMenuEditItem(Name)
00343 {
00344   value = Value;
00345   length = Length;
00346   allowed = Allowed ? Allowed : tr(FileNameChars);
00347   pos = -1;
00348   offset = 0;
00349   insert = uppercase = false;
00350   newchar = true;
00351   lengthUtf8 = 0;
00352   valueUtf8 = NULL;
00353   allowedUtf8 = NULL;
00354   charMapUtf8 = NULL;
00355   currentCharUtf8 = NULL;
00356   lastKey = kNone;
00357   Set();
00358 }
00359 
00360 cMenuEditStrItem::~cMenuEditStrItem()
00361 {
00362   delete[] valueUtf8;
00363   delete[] allowedUtf8;
00364   delete[] charMapUtf8;
00365 }
00366 
00367 void cMenuEditStrItem::EnterEditMode(void)
00368 {
00369   if (!valueUtf8) {
00370      valueUtf8 = new uint[length];
00371      lengthUtf8 = Utf8ToArray(value, valueUtf8, length);
00372      int l = strlen(allowed) + 1;
00373      allowedUtf8 = new uint[l];
00374      Utf8ToArray(allowed, allowedUtf8, l);
00375      const char *charMap = tr("CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9");
00376      l = strlen(charMap) + 1;
00377      charMapUtf8 = new uint[l];
00378      Utf8ToArray(charMap, charMapUtf8, l);
00379      currentCharUtf8 = charMapUtf8;
00380      AdvancePos();
00381      }
00382 }
00383 
00384 void cMenuEditStrItem::LeaveEditMode(bool SaveValue)
00385 {
00386   if (valueUtf8) {
00387      if (SaveValue) {
00388         Utf8FromArray(valueUtf8, value, length);
00389         stripspace(value);
00390         }
00391      lengthUtf8 = 0;
00392      delete[] valueUtf8;
00393      valueUtf8 = NULL;
00394      delete[] allowedUtf8;
00395      allowedUtf8 = NULL;
00396      delete[] charMapUtf8;
00397      charMapUtf8 = NULL;
00398      pos = -1;
00399      offset = 0;
00400      newchar = true;
00401      }
00402 }
00403 
00404 void cMenuEditStrItem::SetHelpKeys(void)
00405 {
00406   if (InEditMode())
00407      SetHelp(tr("Button$ABC/abc"), insert ? tr("Button$Overwrite") : tr("Button$Insert"), tr("Button$Delete"));
00408   else
00409      SetHelp(NULL);
00410 }
00411 
00412 uint *cMenuEditStrItem::IsAllowed(uint c)
00413 {
00414   if (allowedUtf8) {
00415      for (uint *a = allowedUtf8; *a; a++) {
00416          if (c == *a)
00417             return a;
00418          }
00419      }
00420   return NULL;
00421 }
00422 
00423 void cMenuEditStrItem::AdvancePos(void)
00424 {
00425   if (pos < length - 2 && pos < lengthUtf8) {
00426      if (++pos >= lengthUtf8) {
00427         if (pos >= 2 && valueUtf8[pos - 1] == ' ' && valueUtf8[pos - 2] == ' ')
00428            pos--; // allow only two blanks at the end
00429         else {
00430            valueUtf8[pos] = ' ';
00431            valueUtf8[pos + 1] = 0;
00432            lengthUtf8++;
00433            }
00434         }
00435      }
00436   newchar = true;
00437   if (!insert && Utf8is(alpha, valueUtf8[pos]))
00438      uppercase = Utf8is(upper, valueUtf8[pos]);
00439 }
00440 
00441 void cMenuEditStrItem::Set(void)
00442 {
00443   if (InEditMode()) {
00444      // This is an ugly hack to make editing strings work with the 'skincurses' plugin.
00445      const cFont *font = dynamic_cast<cSkinDisplayMenu *>(cSkinDisplay::Current())->GetTextAreaFont(false);
00446      if (!font || font->Width("W") != 1) // all characters have with == 1 in the font used by 'skincurses'
00447         font = cFont::GetFont(fontOsd);
00448 
00449      int width = cSkinDisplay::Current()->EditableWidth();
00450      width -= font->Width("[]");
00451      width -= font->Width("<>"); // reserving this anyway make the whole thing simpler
00452 
00453      if (pos < offset)
00454         offset = pos;
00455      int WidthFromOffset = 0;
00456      int EndPos = lengthUtf8;
00457      for (int i = offset; i < lengthUtf8; i++) {
00458          WidthFromOffset += font->Width(valueUtf8[i]);
00459          if (WidthFromOffset > width) {
00460             if (pos >= i) {
00461                do {
00462                   WidthFromOffset -= font->Width(valueUtf8[offset]);
00463                   offset++;
00464                   } while (WidthFromOffset > width && offset < pos);
00465                EndPos = pos + 1;
00466                }
00467             else {
00468                EndPos = i;
00469                break;
00470                }
00471             }
00472          }
00473 
00474      char buf[1000];
00475      char *p = buf;
00476      if (offset)
00477         *p++ = '<';
00478      p += Utf8FromArray(valueUtf8 + offset, p, sizeof(buf) - (p - buf), pos - offset);
00479      *p++ = '[';
00480      if (insert && newchar)
00481         *p++ = ']';
00482      p += Utf8FromArray(&valueUtf8[pos], p, sizeof(buf) - (p - buf), 1);
00483      if (!(insert && newchar))
00484         *p++ = ']';
00485      p += Utf8FromArray(&valueUtf8[pos + 1], p, sizeof(buf) - (p - buf), EndPos - pos - 1);
00486      if (EndPos != lengthUtf8)
00487         *p++ = '>';
00488      *p = 0;
00489 
00490      SetValue(buf);
00491      }
00492   else
00493      SetValue(value);
00494 }
00495 
00496 uint cMenuEditStrItem::Inc(uint c, bool Up)
00497 {
00498   uint *p = IsAllowed(c);
00499   if (!p)
00500      p = allowedUtf8;
00501   if (Up) {
00502      if (!*++p)
00503         p = allowedUtf8;
00504      }
00505   else if (--p < allowedUtf8) {
00506      p = allowedUtf8;
00507      while (*p && *(p + 1))
00508            p++;
00509      }
00510   return *p;
00511 }
00512 
00513 void cMenuEditStrItem::Type(uint c)
00514 {
00515   if (insert && lengthUtf8 < length - 1)
00516      Insert();
00517   valueUtf8[pos] = c;
00518   if (pos < length - 2)
00519      pos++;
00520   if (pos >= lengthUtf8) {
00521      valueUtf8[pos] = ' ';
00522      valueUtf8[pos + 1] = 0;
00523      lengthUtf8 = pos + 1;
00524      }
00525 }
00526 
00527 void cMenuEditStrItem::Insert(void)
00528 {
00529   memmove(valueUtf8 + pos + 1, valueUtf8 + pos, (lengthUtf8 - pos + 1) * sizeof(*valueUtf8));
00530   lengthUtf8++;
00531   valueUtf8[pos] = ' ';
00532 }
00533 
00534 void cMenuEditStrItem::Delete(void)
00535 {
00536   memmove(valueUtf8 + pos, valueUtf8 + pos + 1, (lengthUtf8 - pos) * sizeof(*valueUtf8));
00537   lengthUtf8--;
00538 }
00539 
00540 eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
00541 {
00542   bool SameKey = NORMALKEY(Key) == lastKey;
00543   if (Key != kNone)
00544      lastKey = NORMALKEY(Key);
00545   else if (!newchar && k0 <= lastKey && lastKey <= k9 && autoAdvanceTimeout.TimedOut()) {
00546      AdvancePos();
00547      newchar = true;
00548      currentCharUtf8 = NULL;
00549      Set();
00550      return osContinue;
00551      }
00552   switch (int(Key)) {
00553     case kRed:   // Switch between upper- and lowercase characters
00554                  if (InEditMode()) {
00555                     if (!insert || !newchar) {
00556                        uppercase = !uppercase;
00557                        valueUtf8[pos] = uppercase ? Utf8to(upper, valueUtf8[pos]) : Utf8to(lower, valueUtf8[pos]);
00558                        }
00559                     }
00560                  else
00561                     return osUnknown;
00562                  break;
00563     case kGreen: // Toggle insert/overwrite modes
00564                  if (InEditMode()) {
00565                     insert = !insert;
00566                     newchar = true;
00567                     SetHelpKeys();
00568                     }
00569                  else
00570                     return osUnknown;
00571                  break;
00572     case kYellow|k_Repeat:
00573     case kYellow: // Remove the character at the current position; in insert mode it is the character to the right of the cursor
00574                  if (InEditMode()) {
00575                     if (lengthUtf8 > 1) {
00576                        if (!insert || pos < lengthUtf8 - 1)
00577                           Delete();
00578                        else if (insert && pos == lengthUtf8 - 1)
00579                           valueUtf8[pos] = ' '; // in insert mode, deleting the last character replaces it with a blank to keep the cursor position
00580                        // reduce position, if we removed the last character
00581                        if (pos == lengthUtf8)
00582                           pos--;
00583                        }
00584                     else if (lengthUtf8 == 1)
00585                        valueUtf8[0] = ' '; // This is the last character in the string, replace it with a blank
00586                     if (Utf8is(alpha, valueUtf8[pos]))
00587                        uppercase = Utf8is(upper, valueUtf8[pos]);
00588                     newchar = true;
00589                     }
00590                  else
00591                     return osUnknown;
00592                  break;
00593     case kBlue|k_Repeat:
00594     case kBlue:  // consume the key only if in edit-mode
00595                  if (!InEditMode())
00596                     return osUnknown;
00597                  break;
00598     case kLeft|k_Repeat:
00599     case kLeft:  if (pos > 0) {
00600                     if (!insert || newchar)
00601                        pos--;
00602                     newchar = true;
00603                     if (!insert && Utf8is(alpha, valueUtf8[pos]))
00604                        uppercase = Utf8is(upper, valueUtf8[pos]);
00605                     }
00606                  break;
00607     case kRight|k_Repeat:
00608     case kRight: if (InEditMode())
00609                     AdvancePos();
00610                  else {
00611                     EnterEditMode();
00612                     SetHelpKeys();
00613                     }
00614                  break;
00615     case kUp|k_Repeat:
00616     case kUp:
00617     case kDown|k_Repeat:
00618     case kDown:  if (InEditMode()) {
00619                     if (insert && newchar) {
00620                        // create a new character in insert mode
00621                        if (lengthUtf8 < length - 1)
00622                           Insert();
00623                        }
00624                     if (uppercase)
00625                        valueUtf8[pos] = Utf8to(upper, Inc(Utf8to(lower, valueUtf8[pos]), NORMALKEY(Key) == kUp));
00626                     else
00627                        valueUtf8[pos] =               Inc(              valueUtf8[pos],  NORMALKEY(Key) == kUp);
00628                     newchar = false;
00629                     }
00630                  else
00631                     return cMenuEditItem::ProcessKey(Key);
00632                  break;
00633     case k0|k_Repeat ... k9|k_Repeat:
00634     case k0 ... k9: {
00635                  if (InEditMode()) {
00636                     if (Setup.NumberKeysForChars) {
00637                        if (!SameKey) {
00638                           if (!newchar)
00639                              AdvancePos();
00640                           currentCharUtf8 = NULL;
00641                           }
00642                        if (!currentCharUtf8 || !*currentCharUtf8 || *currentCharUtf8 == '\t') {
00643                           // find the beginning of the character map entry for Key
00644                           int n = NORMALKEY(Key) - k0;
00645                           currentCharUtf8 = charMapUtf8;
00646                           while (n > 0 && *currentCharUtf8) {
00647                                 if (*currentCharUtf8++ == '\t')
00648                                    n--;
00649                                 }
00650                           // find first allowed character
00651                           while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8))
00652                                 currentCharUtf8++;
00653                           }
00654                        if (*currentCharUtf8 && *currentCharUtf8 != '\t') {
00655                           if (insert && newchar) {
00656                              // create a new character in insert mode
00657                              if (lengthUtf8 < length - 1)
00658                                 Insert();
00659                              }
00660                           valueUtf8[pos] = *currentCharUtf8;
00661                           if (uppercase)
00662                              valueUtf8[pos] = Utf8to(upper, valueUtf8[pos]);
00663                           // find next allowed character
00664                           do {
00665                              currentCharUtf8++;
00666                              } while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8));
00667                           newchar = false;
00668                           autoAdvanceTimeout.Set(AUTO_ADVANCE_TIMEOUT);
00669                           }
00670                        }
00671                     else
00672                        Type('0' + NORMALKEY(Key) - k0);
00673                     }
00674                  else
00675                     return cMenuEditItem::ProcessKey(Key);
00676                  }
00677                  break;
00678     case kBack:
00679     case kOk:    if (InEditMode()) {
00680                     LeaveEditMode(Key == kOk);
00681                     SetHelpKeys();
00682                     break;
00683                     }
00684                  // run into default
00685     default:     if (InEditMode() && BASICKEY(Key) == kKbd) {
00686                     int c = KEYKBD(Key);
00687                     if (c <= 0xFF) { // FIXME what about other UTF-8 characters?
00688                        if (IsAllowed(Utf8to(lower, c)))
00689                           Type(c);
00690                        else {
00691                           switch (c) {
00692                             case 0x7F: // backspace
00693                                        if (pos > 0) {
00694                                           pos--;
00695                                           return ProcessKey(kYellow);
00696                                           }
00697                                        break;
00698                             default: ;
00699                             }
00700                           }
00701                        }
00702                     else {
00703                        switch (c) {
00704                          case kfHome: pos = 0; break;
00705                          case kfEnd:  pos = lengthUtf8 - 1; break;
00706                          case kfIns:  return ProcessKey(kGreen);
00707                          case kfDel:  return ProcessKey(kYellow);
00708                          default: ;
00709                          }
00710                        }
00711                     }
00712                  else
00713                     return cMenuEditItem::ProcessKey(Key);
00714     }
00715   Set();
00716   return osContinue;
00717 }
00718 
00719 // --- cMenuEditStraItem -----------------------------------------------------
00720 
00721 cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
00722 :cMenuEditIntItem(Name, Value, 0, NumStrings - 1)
00723 {
00724   strings = Strings;
00725   Set();
00726 }
00727 
00728 void cMenuEditStraItem::Set(void)
00729 {
00730   SetValue(strings[*value]);
00731 }
00732 
00733 // --- cMenuEditChanItem -----------------------------------------------------
00734 
00735 cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value, const char *NoneString)
00736 :cMenuEditIntItem(Name, Value, NoneString ? 0 : 1, Channels.MaxNumber())
00737 {
00738   channelID = NULL;
00739   noneString = NoneString;
00740   dummyValue = 0;
00741   Set();
00742 }
00743 
00744 cMenuEditChanItem::cMenuEditChanItem(const char *Name, cString *ChannelID, const char *NoneString)
00745 :cMenuEditIntItem(Name, &dummyValue, NoneString ? 0 : 1, Channels.MaxNumber())
00746 {
00747   channelID = ChannelID;
00748   noneString = NoneString;
00749   cChannel *channel = Channels.GetByChannelID(tChannelID::FromString(*ChannelID));
00750   dummyValue = channel ? channel->Number() : 0;
00751   Set();
00752 }
00753 
00754 void cMenuEditChanItem::Set(void)
00755 {
00756   if (*value > 0) {
00757      char buf[255];
00758      cChannel *channel = Channels.GetByNumber(*value);
00759      snprintf(buf, sizeof(buf), "%d %s", *value, channel ? channel->Name() : "");
00760      SetValue(buf);
00761      if (channelID)
00762         *channelID = channel->GetChannelID().ToString();
00763      }
00764   else if (noneString) {
00765      SetValue(noneString);
00766      if (channelID)
00767         *channelID = "";
00768      }
00769 }
00770 
00771 eOSState cMenuEditChanItem::ProcessKey(eKeys Key)
00772 {
00773   int delta = 1;
00774 
00775   switch (int(Key)) {
00776     case kLeft|k_Repeat:
00777     case kLeft:  delta = -1;
00778     case kRight|k_Repeat:
00779     case kRight:
00780                  {
00781                    cChannel *channel = Channels.GetByNumber(*value + delta, delta);
00782                    if (channel)
00783                       *value = channel->Number();
00784                    else if (delta < 0 && noneString)
00785                       *value = 0;
00786                    if (channelID)
00787                       *channelID = channel ? channel->GetChannelID().ToString() : "";
00788                    Set();
00789                  }
00790                  break;
00791     default: return cMenuEditIntItem::ProcessKey(Key);
00792     }
00793   return osContinue;
00794 }
00795 
00796 // --- cMenuEditTranItem -----------------------------------------------------
00797 
00798 cMenuEditTranItem::cMenuEditTranItem(const char *Name, int *Value, int *Source)
00799 :cMenuEditChanItem(Name, &number, "-")
00800 {
00801   number = 0;
00802   source = Source;
00803   transponder = Value;
00804   cChannel *channel = Channels.First();
00805   while (channel) {
00806         if (!channel->GroupSep() && *source == channel->Source() && ISTRANSPONDER(channel->Transponder(), *Value)) {
00807            number = channel->Number();
00808            break;
00809            }
00810         channel = (cChannel *)channel->Next();
00811         }
00812   Set();
00813 }
00814 
00815 eOSState cMenuEditTranItem::ProcessKey(eKeys Key)
00816 {
00817   eOSState state = cMenuEditChanItem::ProcessKey(Key);
00818   cChannel *channel = Channels.GetByNumber(number);
00819   if (channel) {
00820      *source = channel->Source();
00821      *transponder = channel->Transponder();
00822      }
00823   else {
00824      *source = 0;
00825      *transponder = 0;
00826      }
00827   return state;
00828 }
00829 
00830 // --- cMenuEditDateItem -----------------------------------------------------
00831 
00832 static int ParseWeekDays(const char *s)
00833 {
00834   time_t day;
00835   int weekdays;
00836   return cTimer::ParseDay(s, day, weekdays) ? weekdays : 0;
00837 }
00838 
00839 int cMenuEditDateItem::days[] = { ParseWeekDays("M------"),
00840                                   ParseWeekDays("-T-----"),
00841                                   ParseWeekDays("--W----"),
00842                                   ParseWeekDays("---T---"),
00843                                   ParseWeekDays("----F--"),
00844                                   ParseWeekDays("-----S-"),
00845                                   ParseWeekDays("------S"),
00846                                   ParseWeekDays("MTWTF--"),
00847                                   ParseWeekDays("MTWTFS-"),
00848                                   ParseWeekDays("MTWTFSS"),
00849                                   ParseWeekDays("-----SS"),
00850                                   0 };
00851 
00852 cMenuEditDateItem::cMenuEditDateItem(const char *Name, time_t *Value, int *WeekDays)
00853 :cMenuEditItem(Name)
00854 {
00855   value = Value;
00856   weekdays = WeekDays;
00857   oldvalue = 0;
00858   oldweekdays = 0;
00859   dayindex = weekdays ? FindDayIndex(*weekdays) : 0;
00860   Set();
00861 }
00862 
00863 int cMenuEditDateItem::FindDayIndex(int WeekDays)
00864 {
00865   for (unsigned int i = 0; i < sizeof(days) / sizeof(int); i++)
00866       if (WeekDays == days[i])
00867          return i;
00868   return 0;
00869 }
00870 
00871 void cMenuEditDateItem::Set(void)
00872 {
00873 #define DATEBUFFERSIZE 32
00874   char buf[DATEBUFFERSIZE];
00875   if (weekdays && *weekdays) {
00876      SetValue(cTimer::PrintDay(0, *weekdays, false));
00877      return;
00878      }
00879   else if (*value) {
00880      struct tm tm_r;
00881      localtime_r(value, &tm_r);
00882      strftime(buf, DATEBUFFERSIZE, "%Y-%m-%d ", &tm_r);
00883      strcat(buf, WeekDayName(tm_r.tm_wday));
00884      }
00885   else
00886      *buf = 0;
00887   SetValue(buf);
00888 }
00889 
00890 void cMenuEditDateItem::ToggleRepeating(void)
00891 {
00892   if (weekdays) {
00893      if (*weekdays) {
00894         *value = cTimer::SetTime(oldvalue ? oldvalue : time(NULL), 0);
00895         oldvalue = 0;
00896         oldweekdays = *weekdays;
00897         *weekdays = 0;
00898         }
00899      else {
00900         *weekdays = oldweekdays ? oldweekdays : days[cTimer::GetWDay(*value)];
00901         oldweekdays = 0;
00902         dayindex = FindDayIndex(*weekdays);
00903         oldvalue = *value;
00904         *value = 0;
00905         }
00906      Set();
00907      }
00908 }
00909 
00910 eOSState cMenuEditDateItem::ProcessKey(eKeys Key)
00911 {
00912   eOSState state = cMenuEditItem::ProcessKey(Key);
00913 
00914   if (state == osUnknown) {
00915      time_t now = time(NULL);
00916      if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
00917         if (!weekdays || !*weekdays) {
00918            // Decrement single day:
00919            time_t v = *value;
00920            v -= SECSINDAY;
00921            if (v < now) {
00922               if (now <= v + SECSINDAY) { // switched from tomorrow to today
00923                  if (!weekdays)
00924                     v = 0;
00925                  }
00926               else if (weekdays) { // switched from today to yesterday, so enter weekdays mode
00927                  v = 0;
00928                  dayindex = sizeof(days) / sizeof(int) - 2;
00929                  *weekdays = days[dayindex];
00930                  }
00931               else // don't go before today
00932                  v = *value;
00933               }
00934            *value = v;
00935            }
00936         else {
00937            // Decrement weekday index:
00938            if (dayindex > 0)
00939               *weekdays = days[--dayindex];
00940            }
00941         }
00942      else if (NORMALKEY(Key) == kRight) {
00943         if (!weekdays || !*weekdays) {
00944            // Increment single day:
00945            if (!*value)
00946               *value = cTimer::SetTime(now, 0);
00947            *value += SECSINDAY;
00948            }
00949         else {
00950            // Increment weekday index:
00951            *weekdays = days[++dayindex];
00952            if (!*weekdays) { // was last weekday entry, so switch to today
00953               *value = cTimer::SetTime(now, 0);
00954               dayindex = 0;
00955               }
00956            }
00957         }
00958      else if (weekdays) {
00959         if (Key == k0) {
00960            // Toggle between weekdays and single day:
00961            ToggleRepeating();
00962            return osContinue; // ToggleRepeating) has already called Set()
00963            }
00964         else if (k1 <= Key && Key <= k7) {
00965            // Toggle individual weekdays:
00966            if (*weekdays) {
00967               int v = *weekdays ^ (1 << (Key - k1));
00968               if (v != 0)
00969                  *weekdays = v; // can't let this become all 0
00970               }
00971            }
00972         else
00973            return state;
00974         }
00975      else
00976         return state;
00977      Set();
00978      state = osContinue;
00979      }
00980   return state;
00981 }
00982 
00983 // --- cMenuEditTimeItem -----------------------------------------------------
00984 
00985 cMenuEditTimeItem::cMenuEditTimeItem(const char *Name, int *Value)
00986 :cMenuEditItem(Name)
00987 {
00988   value = Value;
00989   hh = *value / 100;
00990   mm = *value % 100;
00991   pos = 0;
00992   Set();
00993 }
00994 
00995 void cMenuEditTimeItem::Set(void)
00996 {
00997   char buf[10];
00998   switch (pos) {
00999     case 1:  snprintf(buf, sizeof(buf), "%01d-:--", hh / 10); break;
01000     case 2:  snprintf(buf, sizeof(buf), "%02d:--", hh); break;
01001     case 3:  snprintf(buf, sizeof(buf), "%02d:%01d-", hh, mm / 10); break;
01002     default: snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm);
01003     }
01004   SetValue(buf);
01005 }
01006 
01007 eOSState cMenuEditTimeItem::ProcessKey(eKeys Key)
01008 {
01009   eOSState state = cMenuEditItem::ProcessKey(Key);
01010 
01011   if (state == osUnknown) {
01012      if (k0 <= Key && Key <= k9) {
01013         if (fresh || pos > 3) {
01014            pos = 0;
01015            fresh = false;
01016            }
01017         int n = Key - k0;
01018         switch (pos) {
01019           case 0: if (n <= 2) {
01020                      hh = n * 10;
01021                      mm = 0;
01022                      pos++;
01023                      }
01024                   break;
01025           case 1: if (hh + n <= 23) {
01026                      hh += n;
01027                      pos++;
01028                      }
01029                   break;
01030           case 2: if (n <= 5) {
01031                      mm += n * 10;
01032                      pos++;
01033                      }
01034                   break;
01035           case 3: if (mm + n <= 59) {
01036                      mm += n;
01037                      pos++;
01038                      }
01039                   break;
01040           default: ;
01041           }
01042         }
01043      else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
01044         if (--mm < 0) {
01045            mm = 59;
01046            if (--hh < 0)
01047               hh = 23;
01048            }
01049         fresh = true;
01050         }
01051      else if (NORMALKEY(Key) == kRight) {
01052         if (++mm > 59) {
01053            mm = 0;
01054            if (++hh > 23)
01055               hh = 0;
01056            }
01057         fresh = true;
01058         }
01059      else
01060         return state;
01061      *value = hh * 100 + mm;
01062      Set();
01063      state = osContinue;
01064      }
01065   return state;
01066 }
01067 
01068 // --- cMenuEditMapItem ------------------------------------------------------
01069 
01070 cMenuEditMapItem::cMenuEditMapItem(const char *Name, int *Value, const tDvbParameterMap *Map, const char *ZeroString)
01071 :cMenuEditItem(Name)
01072 {
01073   value = Value;
01074   map = Map;
01075   zeroString = ZeroString;
01076   Set();
01077 }
01078 
01079 void cMenuEditMapItem::Set(void)
01080 {
01081   const char *s = NULL;
01082   int n = MapToUser(*value, map, &s);
01083   if (n == 0 && zeroString)
01084      SetValue(zeroString);
01085   else if (n >= 0) {
01086      if (s)
01087         SetValue(s);
01088      else {
01089         char buf[16];
01090         snprintf(buf, sizeof(buf), "%d", n);
01091         SetValue(buf);
01092         }
01093      }
01094   else
01095      SetValue("???");
01096 }
01097 
01098 eOSState cMenuEditMapItem::ProcessKey(eKeys Key)
01099 {
01100   eOSState state = cMenuEditItem::ProcessKey(Key);
01101 
01102   if (state == osUnknown) {
01103      int newValue = *value;
01104      int n = DriverIndex(*value, map);
01105      if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
01106         if (n-- > 0)
01107            newValue = map[n].driverValue;
01108         }
01109      else if (NORMALKEY(Key) == kRight) {
01110         if (map[++n].userValue >= 0)
01111            newValue = map[n].driverValue;
01112         }
01113      else
01114         return state;
01115      if (newValue != *value) {
01116         *value = newValue;
01117         Set();
01118         }
01119      state = osContinue;
01120      }
01121   return state;
01122 }
01123 
01124 // --- cMenuSetupPage --------------------------------------------------------
01125 
01126 cMenuSetupPage::cMenuSetupPage(void)
01127 :cOsdMenu("", 33)
01128 {
01129   plugin = NULL;
01130 }
01131 
01132 void cMenuSetupPage::SetSection(const char *Section)
01133 {
01134   SetTitle(cString::sprintf("%s - %s", tr("Setup"), Section));
01135 }
01136 
01137 eOSState cMenuSetupPage::ProcessKey(eKeys Key)
01138 {
01139   eOSState state = cOsdMenu::ProcessKey(Key);
01140 
01141   if (state == osUnknown) {
01142      switch (Key) {
01143        case kOk: Store();
01144                  state = osBack;
01145                  break;
01146        default: break;
01147        }
01148      }
01149   return state;
01150 }
01151 
01152 void cMenuSetupPage::SetPlugin(cPlugin *Plugin)
01153 {
01154   plugin = Plugin;
01155   SetSection(cString::sprintf("%s '%s'", tr("Plugin"), plugin->Name()));
01156 }
01157 
01158 void cMenuSetupPage::SetupStore(const char *Name, const char *Value)
01159 {
01160   if (plugin)
01161      plugin->SetupStore(Name, Value);
01162 }
01163 
01164 void cMenuSetupPage::SetupStore(const char *Name, int Value)
01165 {
01166   if (plugin)
01167      plugin->SetupStore(Name, Value);
01168 }