Home

Dokumentation

Impressum

Dokumentation VDR
 

Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

menu.c

Go to the documentation of this file.
00001 /*
00002  * menu.c: The actual menu implementations
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: menu.c 1.228 2002/12/01 10:31:55 kls Exp $
00008  */
00009 
00010 #include "menu.h"
00011 #include <ctype.h>
00012 #include <limits.h>
00013 #include <stdio.h>
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #include "channels.h"
00017 #include "config.h"
00018 #include "cutter.h"
00019 #include "eit.h"
00020 #include "i18n.h"
00021 #include "menuitems.h"
00022 #include "plugin.h"
00023 #include "recording.h"
00024 #include "remote.h"
00025 #include "sources.h"
00026 #include "status.h"
00027 #include "timers.h"
00028 #include "videodir.h"
00029 
00030 #define MENUTIMEOUT     120 // seconds
00031 #define MAXWAIT4EPGINFO  10 // seconds
00032 #define MODETIMEOUT       3 // seconds
00033 
00034 #define MAXRECORDCONTROLS (MAXDEVICES * MAXRECEIVERS)
00035 #define MAXINSTANTRECTIME (24 * 60 - 1) // 23:59 hours
00036 
00037 #define CHNUMWIDTH  (numdigits(Channels.MaxNumber()) + 1)
00038 
00039 // --- cMenuEditChanItem -----------------------------------------------------
00040 
00041 class cMenuEditChanItem : public cMenuEditIntItem {
00042 protected:
00043   virtual void Set(void);
00044 public:
00045   cMenuEditChanItem(const char *Name, int *Value);
00046   virtual eOSState ProcessKey(eKeys Key);
00047   };
00048 
00049 cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value)
00050 :cMenuEditIntItem(Name, Value, 1, Channels.MaxNumber())
00051 {
00052   Set();
00053 }
00054 
00055 void cMenuEditChanItem::Set(void)
00056 {
00057   char buf[255];
00058   cChannel *channel = Channels.GetByNumber(*value);
00059   snprintf(buf, sizeof(buf), "%d %s", *value, channel ? channel->Name() : "");
00060   SetValue(buf);
00061 }
00062 
00063 eOSState cMenuEditChanItem::ProcessKey(eKeys Key)
00064 {
00065   int delta = 1;
00066 
00067   switch (Key) {
00068     case kLeft|k_Repeat:
00069     case kLeft:  delta = -1;
00070     case kRight|k_Repeat:
00071     case kRight: 
00072                  {
00073                    cChannel *channel = Channels.GetByNumber(*value + delta, delta);
00074                    if (channel) {
00075                       *value = channel->Number();
00076                       Set();
00077                       }
00078                  }
00079                  break;
00080     default : return cMenuEditIntItem::ProcessKey(Key);
00081     }
00082   return osContinue;
00083 }
00084 
00085 // --- cMenuEditTranItem -----------------------------------------------------
00086 
00087 class cMenuEditTranItem : public cMenuEditChanItem {
00088 private:
00089   int number;
00090   int transponder;
00091 public:
00092   cMenuEditTranItem(const char *Name, int *Value);
00093   virtual eOSState ProcessKey(eKeys Key);
00094   };
00095 
00096 cMenuEditTranItem::cMenuEditTranItem(const char *Name, int *Value)
00097 :cMenuEditChanItem(Name, Value)
00098 {
00099   number = 0;
00100   transponder = *Value;
00101   cChannel *channel = Channels.First();
00102   while (channel) {
00103         if (!channel->GroupSep() && ISTRANSPONDER(channel->Frequency(), *Value)) {
00104            number = channel->Number();
00105            break;
00106            }
00107         channel = (cChannel *)channel->Next();
00108         }
00109   *Value = number;
00110   Set();
00111   *Value = transponder;
00112 }
00113 
00114 eOSState cMenuEditTranItem::ProcessKey(eKeys Key)
00115 {
00116   *value = number;
00117   eOSState state = cMenuEditChanItem::ProcessKey(Key);
00118   number = *value;
00119   cChannel *channel = Channels.GetByNumber(*value);
00120   if (channel)
00121      transponder = channel->Frequency();
00122   *value = transponder;
00123   return state;
00124 }
00125 
00126 // --- cMenuEditDayItem ------------------------------------------------------
00127 
00128 class cMenuEditDayItem : public cMenuEditIntItem {
00129 protected:
00130   static int days[];
00131   int d;
00132   virtual void Set(void);
00133 public:
00134   cMenuEditDayItem(const char *Name, int *Value);
00135   virtual eOSState ProcessKey(eKeys Key);
00136   };
00137 
00138 int cMenuEditDayItem::days[] ={ cTimer::ParseDay("M------"),
00139                                 cTimer::ParseDay("-T-----"),
00140                                 cTimer::ParseDay("--W----"),
00141                                 cTimer::ParseDay("---T---"),
00142                                 cTimer::ParseDay("----F--"),
00143                                 cTimer::ParseDay("-----S-"),
00144                                 cTimer::ParseDay("------S"),
00145                                 cTimer::ParseDay("MTWTF--"),
00146                                 cTimer::ParseDay("MTWTFS-"),
00147                                 cTimer::ParseDay("MTWTFSS"),
00148                                 cTimer::ParseDay("-----SS"),
00149                                 0 };
00150 
00151 cMenuEditDayItem::cMenuEditDayItem(const char *Name, int *Value)
00152 :cMenuEditIntItem(Name, Value, -INT_MAX, 31)
00153 {
00154   d = -1;
00155   if (*value < 0) {
00156      int n = 0;
00157      while (days[n]) {
00158            if (days[n] == *value) {
00159               d = n;
00160               break;
00161               }
00162            n++;
00163            }
00164      }
00165   Set();
00166 }
00167 
00168 void cMenuEditDayItem::Set(void)
00169 {
00170   SetValue(cTimer::PrintDay(*value));
00171 }
00172 
00173 eOSState cMenuEditDayItem::ProcessKey(eKeys Key)
00174 {
00175   switch (Key) {
00176     case kLeft|k_Repeat:
00177     case kLeft:  if (d > 0)
00178                     *value = days[--d];
00179                  else if (d == 0) {
00180                     *value = 31;
00181                     d = -1;
00182                     }
00183                  else if (*value == 1) {
00184                     d = sizeof(days) / sizeof(int) - 2;
00185                     *value = days[d];
00186                     }
00187                  else
00188                     return cMenuEditIntItem::ProcessKey(Key);
00189                  Set();
00190                  break;
00191     case kRight|k_Repeat:
00192     case kRight: if (d >= 0) {
00193                     *value = days[++d];
00194                     if (*value == 0) {
00195                        *value = 1;
00196                        d = -1;
00197                        }
00198                     }
00199                  else if (*value == 31) {
00200                     d = 0;
00201                     *value = days[d];
00202                     }
00203                  else
00204                     return cMenuEditIntItem::ProcessKey(Key);
00205                  Set();
00206                  break;
00207     default : return cMenuEditIntItem::ProcessKey(Key);
00208     }
00209   return osContinue;
00210 }
00211 
00212 // --- cMenuEditDateItem -----------------------------------------------------
00213 
00214 class cMenuEditDateItem : public cMenuEditItem {
00215 protected:
00216   time_t *value;
00217   virtual void Set(void);
00218 public:
00219   cMenuEditDateItem(const char *Name, time_t *Value);
00220   virtual eOSState ProcessKey(eKeys Key);
00221   };
00222 
00223 cMenuEditDateItem::cMenuEditDateItem(const char *Name, time_t *Value)
00224 :cMenuEditItem(Name)
00225 {
00226   value = Value;
00227   Set();
00228 }
00229 
00230 void cMenuEditDateItem::Set(void)
00231 {
00232 #define DATEBUFFERSIZE 32
00233   char buf[DATEBUFFERSIZE];
00234   if (*value) {
00235      struct tm tm_r;
00236      localtime_r(value, &tm_r);
00237      strftime(buf, DATEBUFFERSIZE, "%Y-%m-%d ", &tm_r);
00238      strcat(buf, WeekDayName(tm_r.tm_wday));
00239      }
00240   else
00241      *buf = 0;
00242   SetValue(buf);
00243 }
00244 
00245 eOSState cMenuEditDateItem::ProcessKey(eKeys Key)
00246 {
00247   eOSState state = cMenuEditItem::ProcessKey(Key);
00248 
00249   if (state == osUnknown) {
00250      if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
00251         *value -= SECSINDAY;
00252         if (*value < time(NULL))
00253            *value = 0;
00254         }
00255      else if (NORMALKEY(Key) == kRight) {
00256         if (!*value)
00257            *value = cTimer::SetTime(time(NULL), 0);
00258         *value += SECSINDAY;
00259         }
00260      else
00261         return state;
00262      Set();
00263      state = osContinue;
00264      }
00265   return state;
00266 }
00267 
00268 // --- cMenuEditTimeItem -----------------------------------------------------
00269 
00270 class cMenuEditTimeItem : public cMenuEditItem {
00271 protected:
00272   int *value;
00273   int hh, mm;
00274   int pos;
00275   virtual void Set(void);
00276 public:
00277   cMenuEditTimeItem(const char *Name, int *Value);
00278   virtual eOSState ProcessKey(eKeys Key);
00279   };
00280 
00281 cMenuEditTimeItem::cMenuEditTimeItem(const char *Name, int *Value)
00282 :cMenuEditItem(Name)
00283 {
00284   value = Value;
00285   hh = *value / 100;
00286   mm = *value % 100;
00287   pos = 0;
00288   Set();
00289 }
00290 
00291 void cMenuEditTimeItem::Set(void)
00292 {
00293   char buf[10];
00294   switch (pos) {
00295     case 1:  snprintf(buf, sizeof(buf), "%01d-:--", hh / 10); break;
00296     case 2:  snprintf(buf, sizeof(buf), "%02d:--", hh); break;
00297     case 3:  snprintf(buf, sizeof(buf), "%02d:%01d-", hh, mm / 10); break;
00298     default: snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm);
00299     }
00300   SetValue(buf);
00301 }
00302 
00303 eOSState cMenuEditTimeItem::ProcessKey(eKeys Key)
00304 {
00305   eOSState state = cMenuEditItem::ProcessKey(Key);
00306 
00307   if (state == osUnknown) {
00308      if (k0 <= Key && Key <= k9) {
00309         if (fresh || pos > 3) {
00310            pos = 0;
00311            fresh = false;
00312            }
00313         int n = Key - k0;
00314         switch (pos) {
00315           case 0: if (n <= 2) {
00316                      hh = n * 10;
00317                      mm = 0;
00318                      pos++;
00319                      }
00320                   break;
00321           case 1: if (hh + n <= 23) {
00322                      hh += n;
00323                      pos++;
00324                      }
00325                   break;
00326           case 2: if (n <= 5) {
00327                      mm += n * 10;
00328                      pos++;
00329                      }
00330                   break;
00331           case 3: if (mm + n <= 59) {
00332                      mm += n;
00333                      pos++;
00334                      }
00335                   break;
00336           }
00337         }
00338      else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
00339         if (--mm < 0) {
00340            mm = 59;
00341            if (--hh < 0)
00342               hh = 23;
00343            }
00344         fresh = true;
00345         }
00346      else if (NORMALKEY(Key) == kRight) {
00347         if (++mm > 59) {
00348            mm = 0;
00349            if (++hh > 23)
00350               hh = 0;
00351            }
00352         fresh = true;
00353         }
00354      else
00355         return state;
00356      *value = hh * 100 + mm;
00357      Set();
00358      state = osContinue;
00359      }
00360   return state;
00361 }
00362 
00363 // --- cMenuEditCaItem -------------------------------------------------------
00364 
00365 class cMenuEditCaItem : public cMenuEditIntItem {
00366 private:
00367   const cCaDefinition *ca;
00368   bool allowCardNr;
00369 protected:
00370   virtual void Set(void);
00371 public:
00372   cMenuEditCaItem(const char *Name, int *Value, bool AllowCardNr = false);
00373   eOSState ProcessKey(eKeys Key);
00374   };
00375 
00376 cMenuEditCaItem::cMenuEditCaItem(const char *Name, int *Value, bool AllowCardNr)
00377 :cMenuEditIntItem(Name, Value, 0)
00378 {
00379   ca = CaDefinitions.Get(*Value);
00380   allowCardNr = AllowCardNr;
00381   Set();
00382 }
00383 
00384 void cMenuEditCaItem::Set(void)
00385 {
00386   if (ca)
00387      SetValue(ca->Description());
00388   else
00389      cMenuEditIntItem::Set();
00390 }
00391 
00392 eOSState cMenuEditCaItem::ProcessKey(eKeys Key)
00393 {
00394   eOSState state = cMenuEditItem::ProcessKey(Key);
00395 
00396   if (state == osUnknown) {
00397      if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
00398         if (ca && ca->Prev()) {
00399            ca = (cCaDefinition *)ca->Prev();
00400            *value = ca->Number();
00401            }
00402         }
00403      else if (NORMALKEY(Key) == kRight) {
00404         if (ca && ca->Next() && (allowCardNr || ((cCaDefinition *)ca->Next())->Number() > MAXDEVICES)) {
00405            ca = (cCaDefinition *)ca->Next();
00406            *value = ca->Number();
00407            }
00408         }
00409      else
00410         return cMenuEditIntItem::ProcessKey(Key);
00411      Set();
00412      state = osContinue;
00413      }
00414   return state;
00415 }
00416 
00417 // --- cMenuEditSrcItem ------------------------------------------------------
00418 
00419 class cMenuEditSrcItem : public cMenuEditIntItem {
00420 private:
00421   const cSource *source;
00422 protected:
00423   virtual void Set(void);
00424 public:
00425   cMenuEditSrcItem(const char *Name, int *Value);
00426   eOSState ProcessKey(eKeys Key);
00427   };
00428 
00429 cMenuEditSrcItem::cMenuEditSrcItem(const char *Name, int *Value)
00430 :cMenuEditIntItem(Name, Value, 0)
00431 {
00432   source = Sources.Get(*Value);
00433   Set();
00434 }
00435 
00436 void cMenuEditSrcItem::Set(void)
00437 {
00438   if (source) {
00439      char *buffer = NULL;
00440      asprintf(&buffer, "%s - %s", cSource::ToString(source->Code()), source->Description());
00441      SetValue(buffer);
00442      free(buffer);
00443      }
00444   else
00445      cMenuEditIntItem::Set();
00446 }
00447 
00448 eOSState cMenuEditSrcItem::ProcessKey(eKeys Key)
00449 {
00450   eOSState state = cMenuEditItem::ProcessKey(Key);
00451 
00452   if (state == osUnknown) {
00453      if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
00454         if (source && source->Prev()) {
00455            source = (cSource *)source->Prev();
00456            *value = source->Code();
00457            }
00458         }
00459      else if (NORMALKEY(Key) == kRight) {
00460         if (source) { 
00461            if (source->Next())
00462               source = (cSource *)source->Next();
00463            }
00464         else
00465            source = Sources.First();
00466         if (source)
00467            *value = source->Code();
00468         }
00469      else
00470         return state; // we don't call cMenuEditIntItem::ProcessKey(Key) here since we don't accept numerical input
00471      Set();
00472      state = osContinue;
00473      }
00474   return state;
00475 }
00476 
00477 // --- cMenuEditMapItem ------------------------------------------------------
00478 
00479 class cMenuEditMapItem : public cMenuEditItem {
00480 protected:
00481   int *value;
00482   const tChannelParameterMap *map;
00483   const char *zeroString;
00484   virtual void Set(void);
00485 public:
00486   cMenuEditMapItem(const char *Name, int *Value, const tChannelParameterMap *Map, const char *ZeroString = NULL);
00487   virtual eOSState ProcessKey(eKeys Key);
00488   };
00489 
00490 cMenuEditMapItem::cMenuEditMapItem(const char *Name, int *Value, const tChannelParameterMap *Map, const char *ZeroString)
00491 :cMenuEditItem(Name)
00492 {
00493   value = Value;
00494   map = Map;
00495   zeroString = ZeroString;
00496   Set();
00497 }
00498 
00499 void cMenuEditMapItem::Set(void)
00500 {
00501   int n = MapToUser(*value, map);
00502   if (n == 999)
00503      SetValue(tr("auto"));
00504   else if (n == 0 && zeroString)
00505      SetValue(zeroString);
00506   else if (n >= 0) {
00507      char buf[16];
00508      snprintf(buf, sizeof(buf), "%d", n);
00509      SetValue(buf);
00510      }
00511   else
00512      SetValue("???");
00513 }
00514 
00515 eOSState cMenuEditMapItem::ProcessKey(eKeys Key)
00516 {
00517   eOSState state = cMenuEditItem::ProcessKey(Key);
00518 
00519   if (state == osUnknown) {
00520      int newValue = *value;
00521      int n = DriverIndex(*value, map);
00522      if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
00523         if (n-- > 0)
00524            newValue = map[n].driverValue;
00525         }
00526      else if (NORMALKEY(Key) == kRight) {
00527         if (map[++n].userValue >= 0)
00528            newValue = map[n].driverValue;
00529         }
00530      else
00531         return state;
00532      if (newValue != *value) {
00533         *value = newValue;
00534         Set();
00535         }
00536      state = osContinue;
00537      }
00538   return state;
00539 }
00540 
00541 // --- cMenuEditChannel ------------------------------------------------------
00542 
00543 class cMenuEditChannel : public cOsdMenu {
00544 private:
00545   cChannel *channel;
00546   cChannel data;
00547   void Setup(void);
00548 public:
00549   cMenuEditChannel(cChannel *Channel, bool New = false);
00550   virtual eOSState ProcessKey(eKeys Key);
00551   };
00552 
00553 cMenuEditChannel::cMenuEditChannel(cChannel *Channel, bool New)
00554 :cOsdMenu(tr("Edit channel"), 14)
00555 {
00556   channel = Channel;
00557   if (channel) {
00558      data = *channel;
00559      if (New)
00560         channel = NULL;
00561      Setup();
00562      }
00563 }
00564 
00565 void cMenuEditChannel::Setup(void)
00566 {
00567   int current = Current();
00568   char type = *cSource::ToString(data.source);
00569 #define ST(s) if (strchr(s, type))
00570 
00571   Clear();
00572 
00573   // Parameters for all types of sources:
00574   Add(new cMenuEditStrItem( tr("Name"),          data.name, sizeof(data.name), tr(FileNameChars)));
00575   Add(new cMenuEditSrcItem( tr("Source"),       &data.source));
00576   Add(new cMenuEditIntItem( tr("Frequency"),    &data.frequency));
00577   Add(new cMenuEditIntItem( tr("Vpid"),         &data.vpid,  0, 0x1FFF));
00578   Add(new cMenuEditIntItem( tr("Apid1"),        &data.apid1, 0, 0x1FFF));
00579   Add(new cMenuEditIntItem( tr("Apid2"),        &data.apid2, 0, 0x1FFF));
00580   Add(new cMenuEditIntItem( tr("Dpid1"),        &data.dpid1, 0, 0x1FFF));
00581   Add(new cMenuEditIntItem( tr("Dpid2"),        &data.dpid2, 0, 0x1FFF));
00582   Add(new cMenuEditIntItem( tr("Tpid"),         &data.tpid,  0, 0x1FFF));
00583   Add(new cMenuEditCaItem(  tr("CA"),           &data.ca, true));
00584   Add(new cMenuEditIntItem( tr("Sid"),          &data.sid, 0));
00585   /* XXX not yet used
00586   Add(new cMenuEditIntItem( tr("Nid"),          &data.nid, 0));
00587   Add(new cMenuEditIntItem( tr("Tid"),          &data.tid, 0));
00588   Add(new cMenuEditIntItem( tr("Rid"),          &data.rid, 0));
00589   XXX*/
00590   // Parameters for specific types of sources:
00591   ST(" S ")  Add(new cMenuEditChrItem( tr("Polarization"), &data.polarization, "hv"));
00592   ST("CS ")  Add(new cMenuEditIntItem( tr("Srate"),        &data.srate));
00593   ST("CST")  Add(new cMenuEditMapItem( tr("Inversion"),    &data.inversion,    InversionValues, tr("off")));
00594   ST("CST")  Add(new cMenuEditMapItem( tr("CoderateH"),    &data.coderateH,    CoderateValues, tr("none")));
00595   ST("  T")  Add(new cMenuEditMapItem( tr("CoderateL"),    &data.coderateL,    CoderateValues, tr("none")));
00596   ST("C T")  Add(new cMenuEditMapItem( tr("Modulation"),   &data.modulation,   ModulationValues, "QPSK"));
00597   ST("  T")  Add(new cMenuEditMapItem( tr("Bandwidth"),    &data.bandwidth,    BandwidthValues));
00598   ST("  T")  Add(new cMenuEditMapItem( tr("Transmission"), &data.transmission, TransmissionValues));
00599   ST("  T")  Add(new cMenuEditMapItem( tr("Guard"),        &data.guard,        GuardValues));
00600   ST("  T")  Add(new cMenuEditMapItem( tr("Hierarchy"),    &data.hierarchy,    HierarchyValues, tr("none")));
00601 
00602   SetCurrent(Get(current));
00603   Display();
00604 }
00605 
00606 eOSState cMenuEditChannel::ProcessKey(eKeys Key)
00607 {
00608   int oldSource = data.source;
00609   eOSState state = cOsdMenu::ProcessKey(Key);
00610 
00611   if (state == osUnknown) {
00612      if (Key == kOk) {
00613         if (Channels.HasUniqueChannelID(&data, channel)) {
00614            if (channel) {
00615               *channel = data;
00616               isyslog("edited channel %d %s", channel->Number(), data.ToText());
00617               state = osBack;
00618               }
00619            else {
00620               channel = new cChannel;
00621               *channel = data;
00622               Channels.Add(channel);
00623               Channels.ReNumber();
00624               isyslog("added channel %d %s", channel->Number(), data.ToText());
00625               state = osUser1;
00626               }
00627            Channels.Save();
00628            }
00629         else {
00630            Interface->Error(tr("Channel settings are not unique!"));
00631            state = osContinue;
00632            }
00633         }
00634      }
00635   if (Key != kNone && (data.source & cSource::st_Mask) != (oldSource & cSource::st_Mask))
00636      Setup();
00637   return state;
00638 }
00639 
00640 // --- cMenuChannelItem ------------------------------------------------------
00641 
00642 class cMenuChannelItem : public cOsdItem {
00643 private:
00644   cChannel *channel;
00645 public:
00646   cMenuChannelItem(cChannel *Channel);
00647   virtual void Set(void);
00648   };
00649 
00650 cMenuChannelItem::cMenuChannelItem(cChannel *Channel)
00651 {
00652   channel = Channel;
00653   if (channel->GroupSep())
00654      SetColor(clrCyan, clrBackground);
00655   Set();
00656 }
00657 
00658 void cMenuChannelItem::Set(void)
00659 {
00660   char *buffer = NULL;
00661   if (!channel->GroupSep())
00662      asprintf(&buffer, "%d\t%s", channel->Number(), channel->Name());
00663   else
00664      asprintf(&buffer, "---\t%s ----------------------------------------------------------------", channel->Name());
00665   SetText(buffer, false);
00666 }
00667 
00668 // --- cMenuChannels ---------------------------------------------------------
00669 
00670 class cMenuChannels : public cOsdMenu {
00671 private:
00672   void Propagate(void);
00673 protected:
00674   eOSState Switch(void);
00675   eOSState Edit(void);
00676   eOSState New(void);
00677   eOSState Delete(void);
00678   virtual void Move(int From, int To);
00679 public:
00680   cMenuChannels(void);
00681   virtual eOSState ProcessKey(eKeys Key);
00682   };
00683 
00684 cMenuChannels::cMenuChannels(void)
00685 :cOsdMenu(tr("Channels"), CHNUMWIDTH)
00686 {
00687   for (cChannel *channel = Channels.First(); channel; channel = Channels.Next(channel)) {
00688       if (!channel->GroupSep() || *channel->Name())
00689          Add(new cMenuChannelItem(channel), channel->Number() == cDevice::CurrentChannel());
00690       }
00691   SetHelp(tr("Edit"), tr("New"), tr("Delete"), tr("Mark"));
00692 }
00693 
00694 void cMenuChannels::Propagate(void)
00695 {
00696   Channels.ReNumber();
00697   Channels.Save();
00698   for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next())
00699       ci->Set();
00700   Timers.Save(); // channel numbering has changed!
00701   Display();
00702 }
00703 
00704 eOSState cMenuChannels::Switch(void)
00705 {
00706   cChannel *ch = Channels.Get(Current());
00707   if (ch)
00708      cDevice::PrimaryDevice()->SwitchChannel(ch, true);
00709   return osEnd;
00710 }
00711 
00712 eOSState cMenuChannels::Edit(void)
00713 {
00714   if (HasSubMenu() || Count() == 0)
00715      return osContinue;
00716   cChannel *ch = Channels.Get(Current());
00717   if (ch)
00718      return AddSubMenu(new cMenuEditChannel(ch));
00719   return osContinue;
00720 }
00721 
00722 eOSState cMenuChannels::New(void)
00723 {
00724   if (HasSubMenu())
00725      return osContinue;
00726   return AddSubMenu(new cMenuEditChannel(Channels.Get(Current()), true));
00727 }
00728 
00729 eOSState cMenuChannels::Delete(void)
00730 {
00731   if (Count() > 0) {
00732      int Index = Current();
00733      cChannel *channel = Channels.Get(Index);
00734      int DeletedChannel = channel->Number();
00735      // Check if there is a timer using this channel:
00736      for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti)) {
00737          if (ti->Channel() == channel) {
00738             Interface->Error(tr("Channel is being used by a timer!"));
00739             return osContinue;
00740             }
00741          }
00742      if (Interface->Confirm(tr("Delete channel?"))) {
00743         Channels.Del(channel);
00744         cOsdMenu::Del(Index);
00745         Propagate();
00746         isyslog("channel %d deleted", DeletedChannel);
00747         }
00748      }
00749   return osContinue;
00750 }
00751 
00752 void cMenuChannels::Move(int From, int To)
00753 {
00754   int FromNumber = Channels.Get(From)->Number();
00755   int ToNumber = Channels.Get(To)->Number();
00756   Channels.Move(From, To);
00757   cOsdMenu::Move(From, To);
00758   Propagate();
00759   isyslog("channel %d moved to %d", FromNumber, ToNumber);
00760 }
00761 
00762 eOSState cMenuChannels::ProcessKey(eKeys Key)
00763 {
00764   eOSState state = cOsdMenu::ProcessKey(Key);
00765 
00766   switch (state) {
00767     case osUser1: {
00768          cChannel *channel = Channels.Last();
00769          if (channel) {
00770             Add(new cMenuChannelItem(channel), true);
00771             return CloseSubMenu();
00772             }
00773          }
00774          break;
00775     default:
00776          if (state == osUnknown) {
00777             switch (Key) {
00778               case kOk:     return Switch();
00779               case kRed:    return Edit();
00780               case kGreen:  return New();
00781               case kYellow: return Delete();
00782               case kBlue:   Mark(); break;
00783               default: break;
00784               }
00785             }
00786     }
00787   return state;
00788 }
00789 
00790 // --- cMenuText -------------------------------------------------------------
00791 
00792 class cMenuText : public cOsdMenu {
00793 public:
00794   cMenuText(const char *Title, const char *Text, eDvbFont Font = fontOsd);
00795   virtual eOSState ProcessKey(eKeys Key);
00796   };
00797 
00798 cMenuText::cMenuText(const char *Title, const char *Text, eDvbFont Font)
00799 :cOsdMenu(Title)
00800 {
00801   Add(new cMenuTextItem(Text, 1, 2, Setup.OSDwidth - 2, MAXOSDITEMS, clrWhite, clrBackground, Font));
00802 }
00803 
00804 eOSState cMenuText::ProcessKey(eKeys Key)
00805 {
00806   eOSState state = cOsdMenu::ProcessKey(Key);
00807 
00808   if (state == osUnknown) {
00809      switch (Key) {
00810        case kOk: return osBack;
00811        default:  state = osContinue;
00812        }
00813      }
00814   return state;
00815 }
00816 
00817 // --- cMenuEditTimer --------------------------------------------------------
00818 
00819 class cMenuEditTimer : public cOsdMenu {
00820 private:
00821   cTimer *timer;
00822   cTimer data;
00823   int channel;
00824   cMenuEditDateItem *firstday;
00825   void SetFirstDayItem(void);
00826 public:
00827   cMenuEditTimer(int Index, bool New = false);
00828   virtual eOSState ProcessKey(eKeys Key);
00829   };
00830 
00831 cMenuEditTimer::cMenuEditTimer(int Index, bool New)
00832 :cOsdMenu(tr("Edit timer"), 12)
00833 {
00834   firstday = NULL;
00835   timer = Timers.Get(Index);
00836   if (timer) {
00837      data = *timer;
00838      if (New)
00839         data.active = 1;
00840      channel = data.Channel()->Number();
00841      Add(new cMenuEditBoolItem(tr("Active"),       &data.active));
00842      Add(new cMenuEditChanItem(tr("Channel"),      &channel));
00843      Add(new cMenuEditDayItem( tr("Day"),          &data.day));
00844      Add(new cMenuEditTimeItem(tr("Start"),        &data.start));
00845      Add(new cMenuEditTimeItem(tr("Stop"),         &data.stop));
00846      Add(new cMenuEditIntItem( tr("Priority"),     &data.priority, 0, MAXPRIORITY));
00847      Add(new cMenuEditIntItem( tr("Lifetime"),     &data.lifetime, 0, MAXLIFETIME));
00848      Add(new cMenuEditStrItem( tr("File"),          data.file, sizeof(data.file), tr(FileNameChars)));
00849      SetFirstDayItem();
00850      }
00851 }
00852 
00853 void cMenuEditTimer::SetFirstDayItem(void)
00854 {
00855   if (!firstday && !data.IsSingleEvent()) {
00856      Add(firstday = new cMenuEditDateItem(tr("First day"), &data.firstday));
00857      Display();
00858      }
00859   else if (firstday && data.IsSingleEvent()) {
00860      Del(firstday->Index());
00861      firstday = NULL;
00862      data.firstday = 0;
00863      Display();
00864      }
00865 }
00866 
00867 eOSState cMenuEditTimer::ProcessKey(eKeys Key)
00868 {
00869   eOSState state = cOsdMenu::ProcessKey(Key);
00870 
00871   if (state == osUnknown) {
00872      switch (Key) {
00873        case kOk:     {  
00874                        cChannel *ch = Channels.GetByNumber(channel);
00875                        if (ch)
00876                           data.channel = ch;
00877                        else {
00878                           Interface->Error(tr("*** Invalid Channel ***"));
00879                           break;
00880                           }
00881                        if (!*data.file)
00882                           strcpy(data.file, data.Channel()->Name());
00883                        if (timer && memcmp(timer, &data, sizeof(data)) != 0) {
00884                           *timer = data;
00885                           if (timer->active)
00886                              timer->active = 1; // allows external programs to mark active timers with values > 1 and recognize if the user has modified them
00887                           Timers.Save();
00888                           isyslog("timer %d modified (%s)", timer->Index() + 1, timer->active ? "active" : "inactive");
00889                           }
00890                      }
00891                      return osBack;
00892        case kRed:
00893        case kGreen:
00894        case kYellow:
00895        case kBlue:   return osContinue;
00896        default: break;
00897        }
00898      }
00899   if (Key != kNone)
00900      SetFirstDayItem();
00901   return state;
00902 }
00903 
00904 // --- cMenuTimerItem --------------------------------------------------------
00905 
00906 class cMenuTimerItem : public cOsdItem {
00907 private:
00908   cTimer *timer;
00909 public:
00910   cMenuTimerItem(cTimer *Timer);
00911   virtual bool operator< (const cListObject &ListObject);
00912   virtual void Set(void);
00913   cTimer *Timer(void) { return timer; }
00914   };
00915 
00916 cMenuTimerItem::cMenuTimerItem(cTimer *Timer)
00917 {
00918   timer = Timer;
00919   Set();
00920 }
00921 
00922 bool cMenuTimerItem::operator< (const cListObject &ListObject)
00923 {
00924   return *timer < *((cMenuTimerItem *)&ListObject)->timer;
00925 }
00926 
00927 void cMenuTimerItem::Set(void)
00928 {
00929   char *buffer = NULL;
00930   asprintf(&buffer, "%c\t%d\t%s\t%02d:%02d\t%02d:%02d\t%s",
00931                     !timer->Active() ? ' ' : timer->FirstDay() ? '!' : timer->Recording() ? '#' : '>',
00932                     timer->Channel()->Number(),
00933                     timer->PrintDay(timer->Day()),
00934                     timer->Start() / 100,
00935                     timer->Start() % 100,
00936                     timer->Stop() / 100,
00937                     timer->Stop() % 100,
00938                     timer->File());
00939   SetText(buffer, false);
00940 }
00941 
00942 // --- cMenuTimers -----------------------------------------------------------
00943 
00944 class cMenuTimers : public cOsdMenu {
00945 private:
00946   eOSState Edit(void);
00947   eOSState New(void);
00948   eOSState Delete(void);
00949   eOSState OnOff(void);
00950   virtual void Move(int From, int To);
00951   eOSState Summary(void);
00952   cTimer *CurrentTimer(void);
00953 public:
00954   cMenuTimers(void);
00955   virtual eOSState ProcessKey(eKeys Key);
00956   };
00957 
00958 cMenuTimers::cMenuTimers(void)
00959 :cOsdMenu(tr("Timers"), 2, CHNUMWIDTH, 10, 6, 6)
00960 {
00961   int i = 0;
00962   cTimer *timer;
00963 
00964   while ((timer = Timers.Get(i)) != NULL) {
00965         Add(new cMenuTimerItem(timer));
00966         i++;
00967         }
00968   if (Setup.SortTimers)
00969      Sort();
00970   SetHelp(tr("Edit"), tr("New"), tr("Delete"), Setup.SortTimers ? tr("On/Off") : tr("Mark"));
00971 }
00972 
00973 cTimer *cMenuTimers::CurrentTimer(void)
00974 {
00975   cMenuTimerItem *item = (cMenuTimerItem *)Get(Current());
00976   return item ? item->Timer() : NULL;
00977 }
00978 
00979 eOSState cMenuTimers::OnOff(void)
00980 {
00981   cTimer *timer = CurrentTimer();
00982   if (timer) {
00983      timer->OnOff();
00984      RefreshCurrent();
00985      DisplayCurrent(true);
00986      if (timer->FirstDay())
00987         isyslog("timer %d first day set to %s", timer->Index() + 1, timer->PrintFirstDay());
00988      else
00989         isyslog("timer %d %sactivated", timer->Index() + 1, timer->Active() ? "" : "de");
00990      Timers.Save();
00991      }
00992   return osContinue;
00993 }
00994 
00995 eOSState cMenuTimers::Edit(void)
00996 {
00997   if (HasSubMenu() || Count() == 0)
00998      return osContinue;
00999   isyslog("editing timer %d", CurrentTimer()->Index() + 1);
01000   return AddSubMenu(new cMenuEditTimer(CurrentTimer()->Index()));
01001 }
01002 
01003 eOSState cMenuTimers::New(void)
01004 {
01005   if (HasSubMenu())
01006      return osContinue;
01007   cTimer *timer = new cTimer;
01008   Timers.Add(timer);
01009   Add(new cMenuTimerItem(timer), true);
01010   Timers.Save();
01011   isyslog("timer %d added", timer->Index() + 1);
01012   return AddSubMenu(new cMenuEditTimer(timer->Index(), true));
01013 }
01014 
01015 eOSState cMenuTimers::Delete(void)
01016 {
01017   // Check if this timer is active:
01018   cTimer *ti = CurrentTimer();
01019   if (ti) {
01020      if (!ti->Recording()) {
01021         if (Interface->Confirm(tr("Delete timer?"))) {
01022            int Index = ti->Index();
01023            Timers.Del(ti);
01024            cOsdMenu::Del(Current());
01025            Timers.Save();
01026            Display();
01027            isyslog("timer %d deleted", Index + 1);
01028            }
01029         }
01030      else
01031         Interface->Error(tr("Timer is recording!"));
01032      }
01033   return osContinue;
01034 }
01035 
01036 void cMenuTimers::Move(int From, int To)
01037 {
01038   Timers.Move(From, To);
01039   cOsdMenu::Move(From, To);
01040   Timers.Save();
01041   Display();
01042   isyslog("timer %d moved to %d", From + 1, To + 1);
01043 }
01044 
01045 eOSState cMenuTimers::Summary(void)
01046 {
01047   if (HasSubMenu() || Count() == 0)
01048      return osContinue;
01049   cTimer *ti = CurrentTimer();
01050   if (ti && !isempty(ti->Summary()))
01051      return AddSubMenu(new cMenuText(tr("Summary"), ti->Summary()));
01052   return Edit(); // convenience for people not using the Summary feature ;-)
01053 }
01054 
01055 eOSState cMenuTimers::ProcessKey(eKeys Key)
01056 {
01057   eOSState state = cOsdMenu::ProcessKey(Key);
01058 
01059   if (state == osUnknown) {
01060      switch (Key) {
01061        case kOk:     return Summary();
01062        case kRed:    return Edit();
01063        case kGreen:  return New();
01064        case kYellow: return Delete();
01065        case kBlue:   if (Setup.SortTimers)
01066                         OnOff();
01067                      else
01068                         Mark();
01069                      break;
01070        default: break;
01071        }
01072      }
01073   return state;
01074 }
01075 
01076 // --- cMenuEvent ------------------------------------------------------------
01077 
01078 class cMenuEvent : public cOsdMenu {
01079 private:
01080   const cEventInfo *eventInfo;
01081 public:
01082   cMenuEvent(const cEventInfo *EventInfo, bool CanSwitch = false);
01083   cMenuEvent(bool Now);
01084   virtual eOSState ProcessKey(eKeys Key);
01085 };
01086 
01087 cMenuEvent::cMenuEvent(const cEventInfo *EventInfo, bool CanSwitch)
01088 :cOsdMenu(tr("Event"))
01089 {
01090   eventInfo = EventInfo;
01091   if (eventInfo) {
01092      cChannel *channel = Channels.GetByChannelID(eventInfo->GetChannelID(), true);
01093      if (channel) {
01094         char *buffer;
01095         asprintf(&buffer, "%-17.*s\t%.*s  %s - %s", 17, channel->Name(), 5, eventInfo->GetDate(), eventInfo->GetTimeString(), eventInfo->GetEndTimeString());
01096         SetTitle(buffer, false);
01097         free(buffer);
01098         int Line = 2;
01099         cMenuTextItem *item;
01100         const char *Title = eventInfo->GetTitle();
01101         const char *Subtitle = eventInfo->GetSubtitle();
01102         const char *ExtendedDescription = eventInfo->GetExtendedDescription();
01103         if (!isempty(Title)) {
01104            Add(item = new cMenuTextItem(Title, 1, Line, Setup.OSDwidth - 2, -1, clrCyan));
01105            Line += item->Height() + 1;
01106            }
01107         if (!isempty(Subtitle)) {
01108            Add(item = new cMenuTextItem(Subtitle, 1, Line, Setup.OSDwidth - 2, -1, clrYellow));
01109            Line += item->Height() + 1;
01110            }
01111         if (!isempty(ExtendedDescription))
01112            Add(new cMenuTextItem(ExtendedDescription, 1, Line, Setup.OSDwidth - 2, Height() - Line - 2, clrCyan), true);
01113         SetHelp(tr("Record"), NULL, NULL, CanSwitch ? tr("Switch") : NULL);
01114         }
01115      }
01116 }
01117 
01118 eOSState cMenuEvent::ProcessKey(eKeys Key)
01119 {
01120   eOSState state = cOsdMenu::ProcessKey(Key);
01121 
01122   if (state == osUnknown) {
01123      switch (Key) {
01124        case kGreen:
01125        case kYellow: return osContinue;
01126        case kOk:     return osBack;
01127        default: break;
01128        }
01129      }
01130   return state;
01131 }
01132 
01133 // --- cMenuWhatsOnItem ------------------------------------------------------
01134 
01135 class cMenuWhatsOnItem : public cOsdItem {
01136 public:
01137   const cEventInfo *eventInfo;
01138   cMenuWhatsOnItem(const cEventInfo *EventInfo);
01139 };
01140 
01141 cMenuWhatsOnItem::cMenuWhatsOnItem(const cEventInfo *EventInfo)
01142 {
01143   eventInfo = EventInfo;
01144   char *buffer = NULL;
01145   cChannel *channel = Channels.GetByNumber(eventInfo->GetChannelNumber());
01146   asprintf(&buffer, "%d\t%.*s\t%.*s\t%s", eventInfo->GetChannelNumber(), 6, channel ? channel->Name() : "???", 5, eventInfo->GetTimeString(), eventInfo->GetTitle());
01147   SetText(buffer, false);
01148 }
01149 
01150 // --- cMenuWhatsOn ----------------------------------------------------------
01151 
01152 class cMenuWhatsOn : public cOsdMenu {
01153 private:
01154   eOSState Record(void);
01155   eOSState Switch(void);
01156   static int currentChannel;
01157   static const cEventInfo *scheduleEventInfo;
01158 public:
01159   cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr);
01160   static int CurrentChannel(void) { return currentChannel; }
01161   static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; }
01162   static const cEventInfo *ScheduleEventInfo(void);
01163   virtual eOSState ProcessKey(eKeys Key);
01164   };
01165 
01166 int cMenuWhatsOn::currentChannel = 0;
01167 const cEventInfo *cMenuWhatsOn::scheduleEventInfo = NULL;
01168 
01169 static int CompareEventChannel(const void *p1, const void *p2)
01170 {
01171   return (int)( (*(const cEventInfo **)p1)->GetChannelNumber() - (*(const cEventInfo **)p2)->GetChannelNumber());
01172 }
01173 
01174 cMenuWhatsOn::cMenuWhatsOn(const cSchedules *Schedules, bool Now, int CurrentChannelNr)
01175 :cOsdMenu(Now ? tr("What's on now?") : tr("What's on next?"), CHNUMWIDTH, 7, 6)
01176 {
01177   const cSchedule *Schedule = Schedules->First();
01178   const cEventInfo **pArray = NULL;
01179   int num = 0;
01180 
01181   while (Schedule) {
01182         pArray = (const cEventInfo **)realloc(pArray, (num + 1) * sizeof(cEventInfo *));
01183 
01184         pArray[num] = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
01185         if (pArray[num]) {
01186            cChannel *channel = Channels.GetByChannelID(pArray[num]->GetChannelID(), true);
01187            if (channel) {
01188               pArray[num]->SetChannelNumber(channel->Number());
01189               num++;
01190               }
01191            }
01192         Schedule = (const cSchedule *)Schedules->Next(Schedule);
01193         }
01194 
01195   qsort(pArray, num, sizeof(cEventInfo *), CompareEventChannel);
01196 
01197   for (int a = 0; a < num; a++)
01198       Add(new cMenuWhatsOnItem(pArray[a]), pArray[a]->GetChannelNumber() == CurrentChannelNr);
01199 
01200   currentChannel = CurrentChannelNr;
01201   free(pArray);
01202   SetHelp(tr("Record"), Now ? tr("Next") : tr("Now"), tr("Button$Schedule"), tr("Switch"));
01203 }
01204 
01205 const cEventInfo *cMenuWhatsOn::ScheduleEventInfo(void)
01206 {
01207   const cEventInfo *ei = scheduleEventInfo;
01208   scheduleEventInfo = NULL;
01209   return ei;
01210 }
01211 
01212 eOSState cMenuWhatsOn::Switch(void)
01213 {
01214   cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current());
01215   if (item) {
01216      cChannel *channel = Channels.GetByChannelID(item->eventInfo->GetChannelID(), true);
01217      if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true))
01218         return osEnd;
01219      }
01220   Interface->Error(tr("Can't switch channel!"));
01221   return osContinue;
01222 }
01223 
01224 eOSState cMenuWhatsOn::Record(void)
01225 {
01226   cMenuWhatsOnItem *item = (cMenuWhatsOnItem *)Get(Current());
01227   if (item) {
01228      cTimer *timer = new cTimer(item->eventInfo);
01229      cTimer *t = Timers.GetTimer(timer);
01230      if (!t) {
01231         Timers.Add(timer);
01232         Timers.Save();
01233         isyslog("timer %d added", timer->Index() + 1);
01234         }
01235      else {
01236         delete timer;
01237         timer = t;
01238         }
01239      return AddSubMenu(new cMenuEditTimer(timer->Index(), true));
01240      }
01241   return osContinue;
01242 }
01243 
01244 eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
01245 {
01246   eOSState state = cOsdMenu::ProcessKey(Key);
01247 
01248   if (state == osUnknown) {
01249      switch (Key) {
01250        case kRecord:
01251        case kRed:    return Record();
01252        case kYellow: state = osBack;
01253                      // continue with kGreen
01254        case kGreen:  {
01255                        cMenuWhatsOnItem *mi = (cMenuWhatsOnItem *)Get(Current());
01256                        if (mi) {
01257                           scheduleEventInfo = mi->eventInfo;
01258                           currentChannel = mi->eventInfo->GetChannelNumber();
01259                           }
01260                      }
01261                      break;
01262        case kBlue:   return Switch();
01263        case kOk:     if (Count())
01264                         return AddSubMenu(new cMenuEvent(((cMenuWhatsOnItem *)Get(Current()))->eventInfo, true));
01265                      break;
01266        default:      break;
01267        }
01268      }
01269   return state;
01270 }
01271 
01272 // --- cMenuScheduleItem -----------------------------------------------------
01273 
01274 class cMenuScheduleItem : public cOsdItem {
01275 public:
01276   const cEventInfo *eventInfo;
01277   cMenuScheduleItem(const cEventInfo *EventInfo);
01278 };
01279 
01280 cMenuScheduleItem::cMenuScheduleItem(const cEventInfo *EventInfo)
01281 {
01282   eventInfo = EventInfo;
01283   char *buffer = NULL;
01284   asprintf(&buffer, "%.*s\t%.*s\t%s", 5, eventInfo->GetDate(), 5, eventInfo->GetTimeString(), eventInfo->GetTitle());
01285   SetText(buffer, false);
01286 }
01287 
01288 // --- cMenuSchedule ---------------------------------------------------------
01289 
01290 class cMenuSchedule : public cOsdMenu {
01291 private:
01292   cMutexLock mutexLock;
01293   const cSchedules *schedules;
01294   bool now, next;
01295   int otherChannel;
01296   eOSState Record(void);
01297   eOSState Switch(void);
01298   void PrepareSchedule(cChannel *Channel);
01299 public:
01300   cMenuSchedule(void);
01301   virtual ~cMenuSchedule();
01302   virtual eOSState ProcessKey(eKeys Key);
01303   };
01304 
01305 cMenuSchedule::cMenuSchedule(void)
01306 :cOsdMenu("", 6, 6)
01307 {
01308   now = next = false;
01309   otherChannel = 0;
01310   cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
01311   if (channel) {
01312      cMenuWhatsOn::SetCurrentChannel(channel->Number());
01313      schedules = cSIProcessor::Schedules(mutexLock);
01314      PrepareSchedule(channel);
01315      SetHelp(tr("Record"), tr("Now"), tr("Next"));
01316      }
01317 }
01318 
01319 cMenuSchedule::~cMenuSchedule()
01320 {
01321   cMenuWhatsOn::ScheduleEventInfo(); // makes sure any posted data is cleared
01322 }
01323 
01324 static int CompareEventTime(const void *p1, const void *p2)
01325 {
01326   return (int)((*(cEventInfo **)p1)->GetTime() - (*(cEventInfo **)p2)->GetTime());
01327 }
01328 
01329 void cMenuSchedule::PrepareSchedule(cChannel *Channel)
01330 {
01331   Clear();
01332   char *buffer = NULL;
01333   asprintf(&buffer, tr("Schedule - %s"), Channel->Name());
01334   SetTitle(buffer);
01335   free(buffer);
01336   if (schedules) {
01337      const cSchedule *Schedule = schedules->GetSchedule(Channel->GetChannelID());
01338      int num = Schedule->NumEvents();
01339      const cEventInfo **pArray = MALLOC(const cEventInfo *, num);
01340      if (pArray) {
01341         time_t now = time(NULL);
01342         int numreal = 0;
01343         for (int a = 0; a < num; a++) {
01344             const cEventInfo *EventInfo = Schedule->GetEventNumber(a);
01345             if (EventInfo->GetTime() + EventInfo->GetDuration() > now)
01346                pArray[numreal++] = EventInfo;
01347             }
01348 
01349         qsort(pArray, numreal, sizeof(cEventInfo *), CompareEventTime);
01350 
01351         for (int a = 0; a < numreal; a++)
01352             Add(new cMenuScheduleItem(pArray[a]));
01353         free(pArray);
01354         }
01355      }
01356 }
01357 
01358 eOSState cMenuSchedule::Record(void)
01359 {
01360   cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current());
01361   if (item) {
01362      cTimer *timer = new cTimer(item->eventInfo);
01363      cTimer *t = Timers.GetTimer(timer);
01364      if (!t) {
01365         Timers.Add(timer);
01366         Timers.Save();
01367         isyslog("timer %d added", timer->Index() + 1);
01368         }
01369      else {
01370         delete timer;
01371         timer = t;
01372         }
01373      return AddSubMenu(new cMenuEditTimer(timer->Index(), true));
01374      }
01375   return osContinue;
01376 }
01377 
01378 eOSState cMenuSchedule::Switch(void)
01379 {
01380   if (otherChannel) {
01381      if (Channels.SwitchTo(otherChannel))
01382         return osEnd;
01383      }
01384   Interface->Error(tr("Can't switch channel!"));
01385   return osContinue;
01386 }
01387 
01388 eOSState cMenuSchedule::ProcessKey(eKeys Key)
01389 {
01390   eOSState state = cOsdMenu::ProcessKey(Key);
01391 
01392   if (state == osUnknown) {
01393      switch (Key) {
01394        case kRecord:
01395        case kRed:    return Record();
01396        case kGreen:  if (schedules) {
01397                         if (!now && !next) {
01398                            int ChannelNr = 0;
01399                            if (Count()) {
01400                               cChannel *channel = Channels.GetByChannelID(((cMenuScheduleItem *)Get(Current()))->eventInfo->GetChannelID(), true);
01401                               if (channel)
01402                                  ChannelNr = channel->Number();
01403                               }
01404                            now = true;
01405                            return AddSubMenu(new cMenuWhatsOn(schedules, true, ChannelNr));
01406                            }
01407                         now = !now;
01408                         next = !next;
01409                         return AddSubMenu(new cMenuWhatsOn(schedules, now, cMenuWhatsOn::CurrentChannel()));
01410                         }
01411        case kYellow: if (schedules)
01412                         return AddSubMenu(new cMenuWhatsOn(schedules, false, cMenuWhatsOn::CurrentChannel()));
01413                      break;
01414        case kBlue:   if (Count())
01415                         return Switch();
01416                      break;
01417        case kOk:     if (Count())
01418                         return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->eventInfo, otherChannel));
01419                      break;
01420        default:      break;
01421        }
01422      }
01423   else if (!HasSubMenu()) {
01424      now = next = false;
01425      const cEventInfo *ei = cMenuWhatsOn::ScheduleEventInfo();
01426      if (ei) {
01427         cChannel *channel = Channels.GetByChannelID(ei->GetChannelID(), true);
01428         if (channel) {
01429            PrepareSchedule(channel);
01430            if (channel->Number() != cDevice::CurrentChannel()) {
01431               otherChannel = channel->Number();
01432               SetHelp(tr("Record"), tr("Now"), tr("Next"), tr("Switch"));
01433               }
01434            Display();
01435            }
01436         }
01437      }
01438   return state;
01439 }
01440 
01441 // --- cMenuCommands ---------------------------------------------------------
01442 
01443 class cMenuCommands : public cOsdMenu {
01444 private:
01445   cCommands *commands;
01446   char *parameters;
01447   eOSState Execute(void);
01448 public:
01449   cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters = NULL);
01450   virtual ~cMenuCommands();
01451   virtual eOSState ProcessKey(eKeys Key);
01452   };
01453 
01454 cMenuCommands::cMenuCommands(const char *Title, cCommands *Commands, const char *Parameters)
01455 :cOsdMenu(Title)
01456 {
01457   SetHasHotkeys();
01458   commands = Commands;
01459   parameters = Parameters ? strdup(Parameters) : NULL;
01460   int i = 0;
01461   cCommand *command;
01462 
01463   while ((command = commands->Get(i)) != NULL) {
01464         Add(new cOsdItem(hk(command->Title())));
01465         i++;
01466         }
01467 }
01468 
01469 cMenuCommands::~cMenuCommands()
01470 {
01471   free(parameters);
01472 }
01473 
01474 eOSState cMenuCommands::Execute(void)
01475 {
01476   cCommand *command = commands->Get(Current());
01477   if (command) {
01478      char *buffer = NULL;
01479      bool confirmed = true;
01480      if (command->Confirm()) {
01481         asprintf(&buffer, "%s?", command->Title());
01482         confirmed = Interface->Confirm(buffer);
01483         free(buffer);
01484         }
01485      if (confirmed) {
01486         asprintf(&buffer, "%s...", command->Title());
01487         Interface->Status(buffer);
01488         Interface->Flush();
01489         free(buffer);
01490         const char *Result = command->Execute(parameters);
01491         if (Result)
01492            return AddSubMenu(new cMenuText(command->Title(), Result, fontFix));
01493         return osEnd;
01494         }
01495      }
01496   return osContinue;
01497 }
01498 
01499 eOSState cMenuCommands::ProcessKey(eKeys Key)
01500 {
01501   eOSState state = cOsdMenu::ProcessKey(Key);
01502 
01503   if (state == osUnknown) {
01504      switch (Key) {
01505        case kOk:  return Execute();
01506        default:   break;
01507        }
01508      }
01509   return state;
01510 }
01511 
01512 // --- cMenuRecordingItem ----------------------------------------------------
01513 
01514 class cMenuRecordingItem : public cOsdItem {
01515 private:
01516   char *fileName;
01517   char *name;
01518   int totalEntries, newEntries;
01519 public:
01520   cMenuRecordingItem(cRecording *Recording, int Level);
01521   ~cMenuRecordingItem();
01522   void IncrementCounter(bool New);
01523   const char *Name(void) { return name; }
01524   const char *FileName(void) { return fileName; }
01525   bool IsDirectory(void) { return name != NULL; }
01526   };
01527 
01528 cMenuRecordingItem::cMenuRecordingItem(cRecording *Recording, int Level)
01529 {
01530   fileName = strdup(Recording->FileName());
01531   name = NULL;
01532   totalEntries = newEntries = 0;
01533   SetText(Recording->Title('\t', true, Level));
01534   if (*Text() == '\t')
01535      name = strdup(Text() + 2); // 'Text() + 2' to skip the two '\t'
01536 }
01537 
01538 cMenuRecordingItem::~cMenuRecordingItem()
01539 {
01540   free(fileName);
01541   free(name);
01542 }
01543 
01544 void cMenuRecordingItem::IncrementCounter(bool New)
01545 {
01546   totalEntries++;
01547   if (New)
01548      newEntries++;
01549   char *buffer = NULL;
01550   asprintf(&buffer, "%d\t%d\t%s", totalEntries, newEntries, name);
01551   SetText(buffer, false);
01552 }
01553 
01554 // --- cMenuRecordings -------------------------------------------------------
01555 
01556 cRecordings cMenuRecordings::Recordings;
01557 int cMenuRecordings::helpKeys = -1;
01558 
01559 cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus)
01560 :cOsdMenu(Base ? Base : tr("Recordings"), 6, 6)
01561 {
01562   base = Base ? strdup(Base) : NULL;
01563   level = Setup.RecordingDirs ? Level : -1;
01564   if (!Base) {
01565      Interface->Status(tr("scanning recordings..."));
01566      Interface->Flush();
01567      }
01568   if (Base || Recordings.Load()) {
01569      const char *LastReplayed = cReplayControl::LastReplayed();
01570      cMenuRecordingItem *LastItem = NULL;
01571      char *LastItemText = NULL;
01572      for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) {
01573          if (!Base || (strstr(recording->Name(), Base) == recording->Name() && recording->Name()[strlen(Base)] == '~')) {
01574             cMenuRecordingItem *Item = new cMenuRecordingItem(recording, level);
01575             if (*Item->Text() && (!LastItem || strcmp(Item->Text(), LastItemText) != 0)) {
01576                Add(Item);
01577                LastItem = Item;
01578                free(LastItemText);
01579                LastItemText = strdup(LastItem->Text()); // must use a copy because of the counters!
01580                }
01581             else
01582                delete Item;
01583             if (LastItem) {
01584                if (LastReplayed && strcmp(LastReplayed, recording->FileName()) == 0)
01585                   SetCurrent(LastItem);
01586                if (LastItem->IsDirectory())
01587                   LastItem->IncrementCounter(recording->IsNew());
01588                }
01589             }
01590          }
01591      free(LastItemText);
01592      if (Current() < 0)
01593         SetCurrent(First());
01594      else if (OpenSubMenus && Open(true))
01595         return;
01596      }
01597   Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay
01598   SetHelpKeys();
01599 }
01600 
01601 cMenuRecordings::~cMenuRecordings()
01602 {
01603   helpKeys = -1;
01604   free(base);
01605 }
01606 
01607 void cMenuRecordings::SetHelpKeys(void)
01608 {
01609   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
01610   int NewHelpKeys = helpKeys;
01611   if (ri) {
01612      if (ri->IsDirectory())
01613         NewHelpKeys = 1;
01614      else {
01615         NewHelpKeys = 2;
01616         cRecording *recording = GetRecording(ri);
01617         if (recording && recording->Summary())
01618            NewHelpKeys = 3;
01619         }
01620      }
01621   if (NewHelpKeys != helpKeys) {
01622      switch (NewHelpKeys) {
01623        case 0: SetHelp(NULL); break;
01624        case 1: SetHelp(tr("Open")); break;
01625        case 2:
01626        case 3: SetHelp(RecordingCommands.Count() ? tr("Commands") : tr("Play"), tr("Rewind"), tr("Delete"), NewHelpKeys == 3 ? tr("Summary") : NULL);
01627        }
01628      helpKeys = NewHelpKeys;
01629      }
01630 }
01631 
01632 cRecording *cMenuRecordings::GetRecording(cMenuRecordingItem *Item)
01633 {
01634   cRecording *recording = Recordings.GetByName(Item->FileName());
01635   if (!recording)
01636      Interface->Error(tr("Error while accessing recording!"));
01637   return recording;
01638 }
01639 
01640 bool cMenuRecordings::Open(bool OpenSubMenus)
01641 {
01642   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
01643   if (ri && ri->IsDirectory()) {
01644      const char *t = ri->Name();
01645      char *buffer = NULL;
01646      if (base) {
01647         asprintf(&buffer, "%s~%s", base, t);
01648         t = buffer;
01649         }
01650      AddSubMenu(new cMenuRecordings(t, level + 1, OpenSubMenus));
01651      free(buffer);
01652      return true;
01653      }
01654   return false;
01655 }
01656 
01657 eOSState cMenuRecordings::Play(void)
01658 {
01659   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
01660   if (ri) {
01661      if (ri->IsDirectory())
01662         Open();
01663      else {
01664         cRecording *recording = GetRecording(ri);
01665         if (recording) {
01666            cReplayControl::SetRecording(recording->FileName(), recording->Title());
01667            return osReplay;
01668            }
01669         }
01670      }
01671   return osContinue;
01672 }
01673 
01674 eOSState cMenuRecordings::Rewind(void)
01675 {
01676   if (HasSubMenu() || Count() == 0)
01677      return osContinue;
01678   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
01679   if (ri && !ri->IsDirectory()) {
01680      cDevice::PrimaryDevice()->StopReplay(); // must do this first to be able to rewind the currently replayed recording
01681      cResumeFile ResumeFile(ri->FileName());
01682      ResumeFile.Delete();
01683      return Play();
01684      }
01685   return osContinue;
01686 }
01687 
01688 eOSState cMenuRecordings::Delete(void)
01689 {
01690   if (HasSubMenu() || Count() == 0)
01691      return osContinue;
01692   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
01693   if (ri && !ri->IsDirectory()) {
01694      if (Interface->Confirm(tr("Delete recording?"))) {
01695         cRecordControl *rc = cRecordControls::GetRecordControl(ri->FileName());
01696         if (rc) {
01697            if (Interface->Confirm(tr("Timer still recording - really delete?"))) {
01698               cTimer *timer = rc->Timer();
01699               if (timer) {
01700                  timer->Skip();
01701                  cRecordControls::Process(time(NULL));
01702                  Timers.Save();
01703                  }
01704               }
01705            else
01706               return osContinue;
01707            }
01708         cRecording *recording = GetRecording(ri);
01709         if (recording) {
01710            if (recording->Delete()) {
01711               cReplayControl::ClearLastReplayed(ri->FileName());
01712               cOsdMenu::Del(Current());
01713               Recordings.Del(recording);
01714               Display();
01715               if (!Count())
01716                  return osBack;
01717               }
01718            else
01719               Interface->Error(tr("Error while deleting recording!"));
01720            }
01721         }
01722      }
01723   return osContinue;
01724 }
01725 
01726 eOSState cMenuRecordings::Summary(void)
01727 {
01728   if (HasSubMenu() || Count() == 0)
01729      return osContinue;
01730   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
01731   if (ri && !ri->IsDirectory()) {
01732      cRecording *recording = GetRecording(ri);
01733      if (recording && recording->Summary() && *recording->Summary())
01734         return AddSubMenu(new cMenuText(tr("Summary"), recording->Summary()));
01735      }
01736   return osContinue;
01737 }
01738 
01739 eOSState cMenuRecordings::Commands(eKeys Key)
01740 {
01741   if (HasSubMenu() || Count() == 0)
01742      return osContinue;
01743   cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
01744   if (ri && !ri->IsDirectory()) {
01745      cRecording *recording = GetRecording(ri);
01746      if (recording) {
01747         char *parameter = NULL;
01748         asprintf(&parameter, "'%s'", recording->FileName());
01749         cMenuCommands *menu;
01750         eOSState state = AddSubMenu(menu = new cMenuCommands(tr("Recording commands"), &RecordingCommands, parameter));
01751         free(parameter);
01752         if (Key != kNone)
01753            state = menu->ProcessKey(Key);
01754         return state;
01755         }
01756      }
01757   return osContinue;
01758 }
01759 
01760 eOSState cMenuRecordings::ProcessKey(eKeys Key)
01761 {
01762   bool HadSubMenu = HasSubMenu();
01763   eOSState state = cOsdMenu::ProcessKey(Key);
01764 
01765   if (state == osUnknown) {
01766      switch (Key) {
01767        case kOk:     return Play();
01768        case kRed:    return (helpKeys > 1 && RecordingCommands.Count()) ? Commands() : Play();
01769        case kGreen:  return Rewind();
01770        case kYellow: return Delete();
01771        case kBlue:   return Summary();
01772        case k1...k9: return Commands(Key);
01773        default: break;
01774        }
01775      }
01776   if (Key == kYellow && HadSubMenu && !HasSubMenu()) {
01777      // the last recording in a subdirectory was deleted, so let's go back up
01778      cOsdMenu::Del(Current());
01779      if (!Count())
01780         return osBack;
01781      Display();
01782      }
01783   if (!HasSubMenu() && Key != kNone)
01784      SetHelpKeys();
01785   return state;
01786 }
01787 
01788 // --- cMenuSetupBase --------------------------------------------------------
01789 
01790 class cMenuSetupBase : public cMenuSetupPage {
01791 protected:
01792   cSetup data;
01793   virtual void Store(void);
01794 public:
01795   cMenuSetupBase(void);
01796   };
01797 
01798 cMenuSetupBase::cMenuSetupBase(void)
01799 {
01800   data = Setup;
01801 }
01802 
01803 void cMenuSetupBase::Store(void)
01804 {
01805   Setup = data;
01806   Setup.Save();
01807 }
01808 
01809 // --- cMenuSetupOSD ---------------------------------------------------------
01810 
01811 class cMenuSetupOSD : public cMenuSetupBase {
01812 private:
01813   virtual void Set(void);
01814 public:
01815   cMenuSetupOSD(void) { Set(); }
01816   virtual eOSState ProcessKey(eKeys Key);
01817   };
01818 
01819 void cMenuSetupOSD::Set(void)
01820 {
01821   Clear();
01822   SetSection(tr("OSD"));
01823   Add(new cMenuEditStraItem(tr("Setup.OSD$Language"),               &data.OSDLanguage, I18nNumLanguages, I18nLanguages()));
01824   Add(new cMenuEditIntItem( tr("Setup.OSD$Width"),                  &data.OSDwidth, MINOSDWIDTH, MAXOSDWIDTH));
01825   Add(new cMenuEditIntItem( tr("Setup.OSD$Height"),                 &data.OSDheight, MINOSDHEIGHT, MAXOSDHEIGHT));
01826   Add(new cMenuEditIntItem( tr("Setup.OSD$Message time (s)"),       &data.OSDMessageTime, 1, 60));
01827   Add(new cMenuEditBoolItem(tr("Setup.OSD$Channel info position"),  &data.ChannelInfoPos, tr("bottom"), tr("top")));
01828   Add(new cMenuEditBoolItem(tr("Setup.OSD$Info on channel switch"), &data.ShowInfoOnChSwitch));
01829   Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll pages"),           &data.MenuScrollPage));
01830   Add(new cMenuEditBoolItem(tr("Setup.OSD$Sort timers"),            &data.SortTimers));
01831   Add(new cMenuEditBoolItem(tr("Setup.OSD$Recording directories"),  &data.RecordingDirs));
01832 }
01833 
01834 eOSState cMenuSetupOSD::ProcessKey(eKeys Key)
01835 {
01836   int osdLanguage = data.OSDLanguage;
01837   eOSState state = cMenuSetupBase::ProcessKey(Key);
01838 
01839   if (data.OSDLanguage != osdLanguage) {
01840      int OriginalOSDLanguage = Setup.OSDLanguage;
01841      Setup.OSDLanguage = data.OSDLanguage;
01842      Set();
01843      Display();
01844      Setup.OSDLanguage = OriginalOSDLanguage;
01845      }
01846   return state;
01847 }
01848 
01849 // --- cMenuSetupEPG ---------------------------------------------------------
01850 
01851 class cMenuSetupEPG : public cMenuSetupBase {
01852 public:
01853   cMenuSetupEPG(void);
01854   };
01855 
01856 cMenuSetupEPG::cMenuSetupEPG(void)
01857 {
01858   SetSection(tr("EPG"));
01859   Add(new cMenuEditIntItem( tr("Setup.EPG$EPG scan timeout (h)"),      &data.EPGScanTimeout));
01860   Add(new cMenuEditIntItem( tr("Setup.EPG$EPG bugfix level"),          &data.EPGBugfixLevel, 0, MAXEPGBUGFIXLEVEL));
01861   Add(new cMenuEditBoolItem(tr("Setup.EPG$Set system time"),           &data.SetSystemTime));
01862   Add(new cMenuEditTranItem(tr("Setup.EPG$Use time from transponder"), &data.TimeTransponder));
01863 }
01864 
01865 // --- cMenuSetupDVB ---------------------------------------------------------
01866 
01867 class cMenuSetupDVB : public cMenuSetupBase {
01868 public:
01869   cMenuSetupDVB(void);
01870   virtual eOSState ProcessKey(eKeys Key);
01871   };
01872 
01873 cMenuSetupDVB::cMenuSetupDVB(void)
01874 {
01875   SetSection(tr("DVB"));
01876   Add(new cMenuEditIntItem( tr("Setup.DVB$Primary DVB interface"), &data.PrimaryDVB, 1, cDevice::NumDevices()));
01877   Add(new cMenuEditBoolItem(tr("Setup.DVB$Video format"),          &data.VideoFormat, "4:3", "16:9"));
01878 }
01879 
01880 eOSState cMenuSetupDVB::ProcessKey(eKeys Key)
01881 {
01882   int oldPrimaryDVB = Setup.PrimaryDVB;
01883   bool oldVideoFormat = Setup.VideoFormat;
01884   eOSState state = cMenuSetupBase::ProcessKey(Key);
01885 
01886   if (state == osBack && Key == kOk) {
01887      if (Setup.PrimaryDVB != oldPrimaryDVB)
01888         state = osSwitchDvb;
01889      if (Setup.VideoFormat != oldVideoFormat)
01890         cDevice::PrimaryDevice()->SetVideoFormat(Setup.VideoFormat);
01891      }
01892   return state;
01893 }
01894 
01895 // --- cMenuSetupLNB ---------------------------------------------------------
01896 
01897 class cMenuSetupLNB : public cMenuSetupBase {
01898 private:
01899   void Setup(void);
01900 public:
01901   cMenuSetupLNB(void);
01902   virtual eOSState ProcessKey(eKeys Key);
01903   };
01904 
01905 cMenuSetupLNB::cMenuSetupLNB(void)
01906 {
01907   SetSection(tr("LNB"));
01908   Setup();
01909 }
01910 
01911 void cMenuSetupLNB::Setup(void)
01912 {
01913   int current = Current();
01914 
01915   Clear();
01916 
01917   Add(new cMenuEditBoolItem(tr("Setup.LNB$Use DiSEqC"),               &data.DiSEqC));
01918   if (!data.DiSEqC) {
01919      Add(new cMenuEditIntItem( tr("Setup.LNB$SLOF (MHz)"),               &data.LnbSLOF));
01920      Add(new cMenuEditIntItem( tr("Setup.LNB$Low LNB frequency (MHz)"),  &data.LnbFrequLo));
01921      Add(new cMenuEditIntItem( tr("Setup.LNB$High LNB frequency (MHz)"), &data.LnbFrequHi));
01922      }
01923 
01924   SetCurrent(Get(current));
01925   Display();
01926 }
01927 
01928 eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
01929 {
01930   int oldDiSEqC = data.DiSEqC;
01931   eOSState state = cMenuSetupBase::ProcessKey(Key);
01932 
01933   if (Key != kNone && data.DiSEqC != oldDiSEqC)
01934      Setup();
01935   return state;
01936 }
01937 
01938 // --- cMenuSetupCICAM -------------------------------------------------------
01939 
01940 class cMenuSetupCICAM : public cMenuSetupBase {
01941 public:
01942   cMenuSetupCICAM(void);
01943   virtual eOSState ProcessKey(eKeys Key);
01944   };
01945 
01946 cMenuSetupCICAM::cMenuSetupCICAM(void)
01947 {
01948   SetSection(tr("CICAM"));
01949   for (int d = 0; d < cDevice::NumDevices(); d++) {
01950       for (int i = 0; i < 2; i++) {
01951           char buffer[32];
01952           snprintf(buffer, sizeof(buffer), "%s%d %d", tr("Setup.CICAM$CICAM DVB"), d + 1, i + 1);
01953           Add(new cMenuEditCaItem(buffer, &data.CaCaps[d][i]));
01954           }
01955       }
01956 }
01957 
01958 eOSState cMenuSetupCICAM::ProcessKey(eKeys Key)
01959 {
01960   eOSState state = cMenuSetupBase::ProcessKey(Key);
01961 
01962   if (state == osBack && Key == kOk)
01963      cDevice::SetCaCaps();
01964   return state;
01965 }
01966 
01967 // --- cMenuSetupRecord ------------------------------------------------------
01968 
01969 class cMenuSetupRecord : public cMenuSetupBase {
01970 public:
01971   cMenuSetupRecord(void);
01972   };
01973 
01974 cMenuSetupRecord::cMenuSetupRecord(void)
01975 {
01976   SetSection(tr("Recording"));
01977   Add(new cMenuEditIntItem( tr("Setup.Recording$Margin at start (min)"),     &data.MarginStart));
01978   Add(new cMenuEditIntItem( tr("Setup.Recording$Margin at stop (min)"),      &data.MarginStop));
01979   Add(new cMenuEditIntItem( tr("Setup.Recording$Primary limit"),             &data.PrimaryLimit, 0, MAXPRIORITY));
01980   Add(new cMenuEditIntItem( tr("Setup.Recording$Default priority"),          &data.DefaultPriority, 0, MAXPRIORITY));
01981   Add(new cMenuEditIntItem( tr("Setup.Recording$Default lifetime (d)"),      &data.DefaultLifetime, 0, MAXLIFETIME));
01982   Add(new cMenuEditBoolItem(tr("Setup.Recording$Use episode name"),          &data.UseSubtitle));
01983   Add(new cMenuEditBoolItem(tr("Setup.Recording$Mark instant recording"),    &data.MarkInstantRecord));
01984   Add(new cMenuEditStrItem( tr("Setup.Recording$Name instant recording"),     data.NameInstantRecord, sizeof(data.NameInstantRecord), tr(FileNameChars)));
01985   Add(new cMenuEditIntItem( tr("Setup.Recording$Instant rec. time (min)"),   &data.InstantRecordTime, 1, MAXINSTANTRECTIME));
01986   Add(new cMenuEditBoolItem(tr("Setup.Recording$Record Dolby Digital"),      &data.RecordDolbyDigital));
01987   Add(new cMenuEditIntItem( tr("Setup.Recording$Max. video file size (MB)"), &data.MaxVideoFileSize, MINVIDEOFILESIZE, MAXVIDEOFILESIZE));
01988   Add(new cMenuEditBoolItem(tr("Setup.Recording$Split edited files"),        &data.SplitEditedFiles));
01989 }
01990 
01991 // --- cMenuSetupReplay ------------------------------------------------------
01992 
01993 class cMenuSetupReplay : public cMenuSetupBase {
01994 public:
01995   cMenuSetupReplay(void);
01996   };
01997 
01998 cMenuSetupReplay::cMenuSetupReplay(void)
01999 {
02000   SetSection(tr("Replay"));
02001   Add(new cMenuEditBoolItem(tr("Setup.Replay$Multi speed mode"), &data.MultiSpeedMode));
02002   Add(new cMenuEditBoolItem(tr("Setup.Replay$Show replay mode"), &data.ShowReplayMode));
02003 }
02004 
02005 // --- cMenuSetupMisc --------------------------------------------------------
02006 
02007 class cMenuSetupMisc : public cMenuSetupBase {
02008 public:
02009   cMenuSetupMisc(void);
02010   };
02011 
02012 cMenuSetupMisc::cMenuSetupMisc(void)
02013 {
02014   SetSection(tr("Miscellaneous"));
02015   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. event timeout (min)"),   &data.MinEventTimeout));
02016   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$Min. user inactivity (min)"), &data.MinUserInactivity));
02017   Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"),          &data.SVDRPTimeout));
02018 }
02019 
02020 // --- cMenuSetupPluginItem --------------------------------------------------
02021 
02022 class cMenuSetupPluginItem : public cOsdItem {
02023 private:
02024   int pluginIndex;
02025 public:
02026   cMenuSetupPluginItem(const char *Name, int Index);
02027   int PluginIndex(void) { return pluginIndex; }
02028   };
02029 
02030 cMenuSetupPluginItem::cMenuSetupPluginItem(const char *Name, int Index)
02031 :cOsdItem(Name)
02032 {
02033   pluginIndex = Index;
02034 }
02035 
02036 // --- cMenuSetupPlugins -----------------------------------------------------
02037 
02038 class cMenuSetupPlugins : public cMenuSetupBase {
02039 public:
02040   cMenuSetupPlugins(void);
02041   virtual eOSState ProcessKey(eKeys Key);
02042   };
02043 
02044 cMenuSetupPlugins::cMenuSetupPlugins(void)
02045 {
02046   SetSection(tr("Plugins"));
02047   SetHasHotkeys();
02048   for (int i = 0; ; i++) {
02049       cPlugin *p = cPluginManager::GetPlugin(i);
02050       if (p) {
02051          char *buffer = NULL;
02052          asprintf(&buffer, "%s (%s) - %s", p->Name(), p->Version(), p->Description());
02053          Add(new cMenuSetupPluginItem(hk(buffer), i));
02054          free(buffer);
02055          }
02056       else
02057          break;
02058       }
02059 }
02060 
02061 eOSState cMenuSetupPlugins::ProcessKey(eKeys Key)
02062 {
02063   eOSState state = HasSubMenu() ? cMenuSetupBase::ProcessKey(Key) : cOsdMenu::ProcessKey(Key);
02064 
02065   if (Key == kOk) {
02066      if (state == osUnknown) {
02067         cMenuSetupPluginItem *item = (cMenuSetupPluginItem *)Get(Current());
02068         if (item) {
02069            cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
02070            if (p) {
02071               cMenuSetupPage *menu = p->SetupMenu();
02072               if (menu) {
02073                  menu->SetPlugin(p);
02074                  return AddSubMenu(menu);
02075                  }
02076               Interface->Info(tr("This plugin has no setup parameters!"));
02077               }
02078            }
02079         }
02080      else if (state == osContinue)
02081         Store();
02082      }
02083   return state;
02084 }
02085 
02086 // --- cMenuSetup ------------------------------------------------------------
02087 
02088 class cMenuSetup : public cOsdMenu {
02089 private:
02090   virtual void Set(void);
02091   eOSState Restart(void);
02092 public:
02093   cMenuSetup(void);
02094   virtual eOSState ProcessKey(eKeys Key);
02095   };
02096 
02097 cMenuSetup::cMenuSetup(void)
02098 :cOsdMenu("")
02099 {
02100   Set();
02101 }
02102 
02103 void cMenuSetup::Set(void)
02104 {
02105   Clear();
02106   char buffer[64];
02107   snprintf(buffer, sizeof(buffer), "%s - VDR %s", tr("Setup"), VDRVERSION);
02108   SetTitle(buffer);
02109   SetHasHotkeys();
02110   Add(new cOsdItem(hk(tr("OSD")),           osUser1));
02111   Add(new cOsdItem(hk(tr("EPG")),           osUser2));
02112   Add(new cOsdItem(hk(tr("DVB")),           osUser3));
02113   Add(new cOsdItem(hk(tr("LNB")),           osUser4));
02114   Add(new cOsdItem(hk(tr("CICAM")),         osUser5));
02115   Add(new cOsdItem(hk(tr("Recording")),     osUser6));
02116   Add(new cOsdItem(hk(tr("Replay")),        osUser7));
02117   Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
02118   if (cPluginManager::HasPlugins())
02119   Add(new cOsdItem(hk(tr("Plugins")),       osUser9));
02120   Add(new cOsdItem(hk(tr("Restart")),       osUser10));
02121 }
02122 
02123 eOSState cMenuSetup::Restart(void)
02124 {
02125   if (Interface->Confirm(cRecordControls::Active() ? tr("Recording - restart anyway?") : tr("Really restart?"))) {
02126      cThread::EmergencyExit(true);
02127      return osEnd;
02128      }
02129   return osContinue;
02130 }
02131 
02132 eOSState cMenuSetup::ProcessKey(eKeys Key)
02133 {
02134   int osdLanguage = Setup.OSDLanguage;
02135   eOSState state = cOsdMenu::ProcessKey(Key);
02136 
02137   switch (state) {
02138     case osUser1: return AddSubMenu(new cMenuSetupOSD);
02139     case osUser2: return AddSubMenu(new cMenuSetupEPG);
02140     case osUser3: return AddSubMenu(new cMenuSetupDVB);
02141     case osUser4: return AddSubMenu(new cMenuSetupLNB);
02142     case osUser5: return AddSubMenu(new cMenuSetupCICAM);
02143     case osUser6: return AddSubMenu(new cMenuSetupRecord);
02144     case osUser7: return AddSubMenu(new cMenuSetupReplay);
02145     case osUser8: return AddSubMenu(new cMenuSetupMisc);
02146     case osUser9: return AddSubMenu(new cMenuSetupPlugins);
02147     case osUser10: return Restart();
02148     default: ;
02149     }
02150   if (Setup.OSDLanguage != osdLanguage) {
02151      Set();
02152      if (!HasSubMenu())
02153         Display();
02154      }
02155   return state;
02156 }
02157 
02158 // --- cMenuPluginItem -------------------------------------------------------
02159 
02160 class cMenuPluginItem : public cOsdItem {
02161 private:
02162   int pluginIndex;
02163 public:
02164   cMenuPluginItem(const char *Name, int Index);
02165   int PluginIndex(void) { return pluginIndex; }
02166   };
02167 
02168 cMenuPluginItem::cMenuPluginItem(const char *Name, int Index)
02169 :cOsdItem(Name, osPlugin)
02170 {
02171   pluginIndex = Index;
02172 }
02173 
02174 // --- cMenuMain -------------------------------------------------------------
02175 
02176 #define STOP_RECORDING tr(" Stop recording ")
02177 #define ON_PRIMARY_INTERFACE tr("on primary interface")
02178 
02179 cOsdObject *cMenuMain::pluginOsdObject = NULL;
02180 
02181 cMenuMain::cMenuMain(bool Replaying, eOSState State, const char *Plugin)
02182 :cOsdMenu("")
02183 {
02184   replaying = Replaying;
02185   Set(Plugin);
02186 
02187   // Initial submenus:
02188 
02189   switch (State) {
02190     case osSchedule:   AddSubMenu(new cMenuSchedule); break;
02191     case osChannels:   AddSubMenu(new cMenuChannels); break;
02192     case osTimers:     AddSubMenu(new cMenuTimers); break;
02193     case osRecordings: AddSubMenu(new cMenuRecordings(NULL, 0, true)); break;
02194     case osSetup:      AddSubMenu(new cMenuSetup); break;
02195     case osCommands:   AddSubMenu(new cMenuCommands(tr("Commands"), &Commands)); break;
02196     case osPlugin:     break; // the actual work is done in Set()
02197     default: break;
02198     }
02199 }
02200 
02201 cOsdObject *cMenuMain::PluginOsdObject(void)
02202 {
02203   cOsdObject *o = pluginOsdObject;
02204   pluginOsdObject = NULL;
02205   return o;
02206 }
02207 
02208 void cMenuMain::Set(const char *Plugin)
02209 {
02210   Clear();
02211   //SetTitle("VDR"); // this is done below, including disk usage
02212   SetHasHotkeys();
02213 
02214   // Title with disk usage:
02215 
02216 #define MB_PER_MINUTE 25.75 // this is just an estimate!
02217 
02218   char buffer[40];
02219   int FreeMB;
02220   int Percent = VideoDiskSpace(&FreeMB);
02221   int Minutes = int(double(FreeMB) / MB_PER_MINUTE);
02222   int Hours = Minutes / 60;
02223   Minutes %= 60;
02224   snprintf(buffer, sizeof(buffer), "%s  -  %s %d%%  -  %2d:%02d %s", tr("VDR"), tr("Disk"), Percent, Hours, Minutes, tr("free"));
02225   SetTitle(buffer);
02226 
02227   // Basic menu items:
02228 
02229   Add(new cOsdItem(hk(tr("Schedule")),   osSchedule));
02230   Add(new cOsdItem(hk(tr("Channels")),   osChannels));
02231   Add(new cOsdItem(hk(tr("Timers")),     osTimers));
02232   Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
02233 
02234   // Plugins:
02235 
02236   for (int i = 0; ; i++) {
02237       cPlugin *p = cPluginManager::GetPlugin(i);
02238       if (p) {
02239          const char *item = p->MainMenuEntry();
02240          if (item)
02241             Add(new cMenuPluginItem(hk(item), i), Plugin && strcmp(Plugin, p->Name()) == 0);
02242          }
02243       else
02244          break;
02245       }
02246 
02247   // More basic menu items:
02248 
02249   Add(new cOsdItem(hk(tr("Setup")),      osSetup));
02250   if (Commands.Count())
02251      Add(new cOsdItem(hk(tr("Commands")),  osCommands));
02252 
02253   // Replay control:
02254 
02255   if (replaying)
02256      Add(new cOsdItem(tr(" Stop replaying"), osStopReplay));
02257 
02258   // Record control:
02259 
02260   if (cRecordControls::StopPrimary()) {
02261      char *buffer = NULL;
02262      asprintf(&buffer, "%s%s", STOP_RECORDING, ON_PRIMARY_INTERFACE);
02263      Add(new cOsdItem(buffer, osStopRecord));
02264      free(buffer);
02265      }
02266 
02267   const char *s = NULL;
02268   while ((s = cRecordControls::GetInstantId(s)) != NULL) {
02269         char *buffer = NULL;
02270         asprintf(&buffer, "%s%s", STOP_RECORDING, s);
02271         Add(new cOsdItem(buffer, osStopRecord));
02272         free(buffer);
02273         }
02274 
02275   // Editing control:
02276 
02277   if (cCutter::Active())
02278      Add(new cOsdItem(tr(" Cancel editing"), osCancelEdit));
02279 
02280   // Color buttons:
02281 
02282   SetHelp(tr("Record"), cDevice::PrimaryDevice()->NumAudioTracks() > 1 ? tr("Language") : NULL, NULL, replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Resume") : NULL);
02283   Display();
02284   lastActivity = time(NULL);
02285 }
02286 
02287 eOSState cMenuMain::ProcessKey(eKeys Key)
02288 {
02289   int osdLanguage = Setup.OSDLanguage;
02290   eOSState state = cOsdMenu::ProcessKey(Key);
02291 
02292   switch (state) {
02293     case osSchedule:   return AddSubMenu(new cMenuSchedule);
02294     case osChannels:   return AddSubMenu(new cMenuChannels);
02295     case osTimers:     return AddSubMenu(new cMenuTimers);
02296     case osRecordings: return AddSubMenu(new cMenuRecordings);
02297     case osSetup:      return AddSubMenu(new cMenuSetup);
02298     case osCommands:   return AddSubMenu(new cMenuCommands(tr("Commands"), &Commands));
02299     case osStopRecord: if (Interface->Confirm(tr("Stop recording?"))) {
02300                           cOsdItem *item = Get(Current());
02301                           if (item) {
02302                              const char *s = item->Text() + strlen(STOP_RECORDING);
02303                              if (strcmp(s, ON_PRIMARY_INTERFACE) == 0)
02304                                 cRecordControls::StopPrimary(true);
02305                              else
02306                                 cRecordControls::Stop(item->Text() + strlen(STOP_RECORDING));
02307                              return osEnd;
02308                              }
02309                           }
02310                        break;
02311     case osCancelEdit: if (Interface->Confirm(tr("Cancel editing?"))) {
02312                           cCutter::Stop();
02313                           return osEnd;
02314                           }
02315                        break;
02316     case osPlugin:     {
02317                          cMenuPluginItem *item = (cMenuPluginItem *)Get(Current());
02318                          if (item) {
02319                             cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
02320                             if (p) {
02321                                cOsdObject *menu = p->MainMenuAction();
02322                                if (menu) {
02323                                   if (menu->IsMenu())
02324                                      return AddSubMenu((cOsdMenu *)menu);
02325                                   else {
02326                                      pluginOsdObject = menu;
02327                                      return osPlugin;
02328                                      }
02329                                   }
02330                                }
02331                             }
02332                          state = osEnd;
02333                        }
02334                        break;
02335     default: switch (Key) {
02336                case kRecord:
02337                case kRed:    if (!HasSubMenu())
02338                                 state = osRecord;
02339                              break;
02340                case kGreen:  if (!HasSubMenu()) {
02341                                 int CurrentAudioTrack = -1;
02342                                 const char **AudioTracks = cDevice::PrimaryDevice()->GetAudioTracks(&CurrentAudioTrack);
02343                                 if (AudioTracks) {
02344                                    const char **at = &AudioTracks[CurrentAudioTrack];
02345                                    if (!*++at)
02346                                       at = AudioTracks;
02347                                    Interface->Clear();
02348                                    cDevice::PrimaryDevice()->SetAudioTrack(at - AudioTracks);
02349                                    //XXX Interface->Info(*at);
02350                                    state = osEnd;
02351                                    }
02352                                 }
02353                              break;
02354                case kBlue:   if (!HasSubMenu())
02355                                 state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osContinue;
02356                              break;
02357                default:      break;
02358                }
02359     }
02360   if (Key != kNone) {
02361      lastActivity = time(NULL);
02362      if (Setup.OSDLanguage != osdLanguage) {
02363         Set();
02364         if (!HasSubMenu())
02365            Display();
02366         }
02367      }
02368   else if (time(NULL) - lastActivity > MENUTIMEOUT)
02369      state = osEnd;
02370   return state;
02371 }
02372 
02373 // --- cDisplayChannel -------------------------------------------------------
02374 
02375 #define DIRECTCHANNELTIMEOUT 1000 //ms
02376 #define INFOTIMEOUT          5000 //ms
02377 
02378 cDisplayChannel::cDisplayChannel(int Number, bool Switched)
02379 :cOsdObject(true)
02380 {
02381   group = -1;
02382   withInfo = !Switched || Setup.ShowInfoOnChSwitch;
02383   int EpgLines = withInfo ? 5 : 1;
02384   lines = 0;
02385   number = 0;
02386   cChannel *channel = Channels.GetByNumber(Number);
02387   Interface->Open(Setup.OSDwidth, Setup.ChannelInfoPos ? EpgLines : -EpgLines);
02388   if (channel) {
02389      DisplayChannel(channel);
02390      DisplayInfo();
02391      }
02392   lastTime = time_ms();
02393 }
02394 
02395 cDisplayChannel::cDisplayChannel(eKeys FirstKey)
02396 :cOsdObject(true)
02397 {
02398   group = -1;
02399   lines = 0;
02400   number = 0;
02401   lastTime = time_ms();
02402   int EpgLines = Setup.ShowInfoOnChSwitch ? 5 : 1;
02403   Interface->Open(Setup.OSDwidth, Setup.ChannelInfoPos ? EpgLines : -EpgLines);
02404   ProcessKey(FirstKey);
02405 }
02406 
02407 cDisplayChannel::~cDisplayChannel()
02408 {
02409   Interface->Close();
02410 }
02411 
02412 void cDisplayChannel::DisplayChannel(const cChannel *Channel)
02413 {
02414   int BufSize = Width() + 1;
02415   char buffer[BufSize];
02416   *buffer = 0;
02417   if (Channel) {
02418      if (Channel->GroupSep())
02419         snprintf(buffer, BufSize, "%s", Channel->Name());
02420      else
02421         snprintf(buffer, BufSize, "%d%s  %s", Channel->Number(), number ? "-" : "", Channel->Name());
02422      }
02423   else if (number)
02424      snprintf(buffer, BufSize, "%d-", number);
02425   else
02426      snprintf(buffer, BufSize, "%s", tr("*** Invalid Channel ***"));
02427   Interface->Fill(0, 0, Setup.OSDwidth, 1, clrBackground);
02428   Interface->Write(0, 0, buffer);
02429   const char *date = DayDateTime();
02430   Interface->Write(-strlen(date), 0, date);
02431   cStatus::MsgOsdChannel(buffer);
02432 }
02433 
02434 void cDisplayChannel::DisplayInfo(void)
02435 {
02436   if (withInfo) {
02437      const cEventInfo *Present = NULL, *Following = NULL;
02438      cMutexLock MutexLock;
02439      const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
02440      if (Schedules) {
02441         const cSchedule *Schedule = Schedules->GetSchedule();
02442         if (Schedule) {
02443            const char *PresentTitle = NULL, *PresentSubtitle = NULL, *FollowingTitle = NULL, *FollowingSubtitle = NULL;
02444            int Lines = 0;
02445            if ((Present = Schedule->GetPresentEvent()) != NULL) {
02446               PresentTitle = Present->GetTitle();
02447               if (!isempty(PresentTitle))
02448                  Lines++;
02449               PresentSubtitle = Present->GetSubtitle();
02450               if (!isempty(PresentSubtitle))
02451                  Lines++;
02452               }
02453            if ((Following = Schedule->GetFollowingEvent()) != NULL) {
02454               FollowingTitle = Following->GetTitle();
02455               if (!isempty(FollowingTitle))
02456                  Lines++;
02457               FollowingSubtitle = Following->GetSubtitle();
02458               if (!isempty(FollowingSubtitle))
02459                  Lines++;
02460               }
02461            if (Lines > lines) {
02462               const int t = 6;
02463               int l = 1;
02464               Interface->Fill(0, 1, Setup.OSDwidth, Lines, clrBackground);
02465               if (!isempty(PresentTitle)) {
02466                  Interface->Write(0, l, Present->GetTimeString(), clrYellow, clrBackground);
02467                  Interface->Write(t, l, PresentTitle, clrCyan, clrBackground);
02468                  l++;
02469                  }
02470               if (!isempty(PresentSubtitle)) {
02471                  Interface->Write(t, l, PresentSubtitle, clrCyan, clrBackground);
02472                  l++;
02473                  }
02474               if (!isempty(FollowingTitle)) {
02475                  Interface->Write(0, l, Following->GetTimeString(), clrYellow, clrBackground);
02476                  Interface->Write(t, l, FollowingTitle, clrCyan, clrBackground);
02477                  l++;
02478                  }
02479               if (!isempty(FollowingSubtitle)) {
02480                  Interface->Write(t, l, FollowingSubtitle, clrCyan, clrBackground);
02481                  }
02482               Interface->Flush();
02483               lines = Lines;
02484               lastTime = time_ms();
02485               cStatus::MsgOsdProgramme(Present ? Present->GetTime() : 0, PresentTitle, PresentSubtitle, Following ? Following->GetTime() : 0, FollowingTitle, FollowingSubtitle);
02486               }
02487            }
02488         }
02489      }
02490 }
02491 
02492 void cDisplayChannel::Refresh(void)
02493 {
02494   Interface->Clear();
02495   DisplayChannel(Channels.GetByNumber(cDevice::CurrentChannel()));
02496   lastTime = time_ms();
02497   lines = 0;
02498 }
02499 
02500 eOSState cDisplayChannel::ProcessKey(eKeys Key)
02501 {
02502   switch (Key) {
02503     case k0:
02504          if (number == 0) {
02505             // keep the "Toggle channels" function working
02506             cRemote::Put(Key);
02507             return osEnd;
02508             }
02509     case k1 ... k9:
02510          if (number >= 0) {
02511             number = number * 10 + Key - k0;
02512             if (number > 0) {
02513                cChannel *channel = Channels.GetByNumber(number);
02514                DisplayChannel(channel);
02515                lastTime = time_ms();
02516                }
02517             }
02518          break;
02519     case kLeft|k_Repeat:
02520     case kLeft:
02521     case kRight|k_Repeat:
02522     case kRight:
02523          withInfo = false;
02524          if (group < 0) {
02525             cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
02526             if (channel)
02527                group = channel->Index();
02528             }
02529          if (group >= 0) {
02530             int SaveGroup = group;
02531             if (NORMALKEY(Key) == kRight)
02532                group = Channels.GetNextGroup(group) ;
02533             else
02534                group = Channels.GetPrevGroup(group < 1 ? 1 : group);
02535             if (group < 0)
02536                group = SaveGroup;
02537             cChannel *channel = Channels.Get(group);
02538             if (channel) {
02539                Interface->Clear();
02540                DisplayChannel(channel);
02541                if (!channel->GroupSep())
02542                   group = -1;
02543                }
02544             }
02545          lastTime = time_ms();
02546          break;
02547     case kUp|k_Repeat:
02548     case kUp:
02549     case kDown|k_Repeat:
02550     case kDown:
02551          cDevice::SwitchChannel(NORMALKEY(Key) == kUp ? 1 : -1);
02552          // no break here
02553     case kChanUp|k_Repeat:
02554     case kChanUp:
02555     case kChanDn|k_Repeat:
02556     case kChanDn:
02557          Refresh();
02558          break;
02559     case kNone:
02560          if (number && time_ms() - lastTime > DIRECTCHANNELTIMEOUT) {
02561             if (Channels.GetByNumber(number))
02562                Channels.SwitchTo(number);
02563             else {
02564                number = 0;
02565                DisplayChannel(NULL);
02566                lastTime = time_ms();
02567                return osContinue;
02568                }
02569             return osEnd;
02570             }
02571          break;
02572     //TODO
02573     //XXX case kGreen:  return osEventNow;
02574     //XXX case kYellow: return osEventNext;
02575     case kOk:     if (group >= 0)
02576                      Channels.SwitchTo(Channels.Get(Channels.GetNextNormal(group))->Number());
02577                   return osEnd;
02578     default:      if ((Key & (k_Repeat | k_Release)) == 0) {
02579                      cRemote::Put(Key);
02580                      return osEnd;
02581                      }
02582     };
02583   if (time_ms() - lastTime < INFOTIMEOUT) {
02584      DisplayInfo();
02585      return osContinue;
02586      }
02587   return osEnd;
02588 }
02589 
02590 // --- cVolumeBar ------------------------------------------------------------
02591 
02592 class cVolumeBar : public cBitmap {
02593 public:
02594   cVolumeBar(int Width, int Height, int Current, int Total, const char *Prompt = NULL);
02595   };
02596 
02597 cVolumeBar::cVolumeBar(int Width, int Height, int Current, int Total, const char *Prompt)
02598 :cBitmap(Width, Height, 2)
02599 {
02600   int l = Prompt ? cBitmap::Width(Prompt) : 0;
02601   int p = (Width - l) * Current / Total;
02602   Text(0, 0, Prompt, clrGreen);
02603   Fill(l, 0, p, Height - 1, clrGreen);
02604   Fill(l + p, 0, Width - 1, Height - 1, clrWhite);
02605 }
02606 
02607 // --- cDisplayVolume --------------------------------------------------------
02608 
02609 #define VOLUMETIMEOUT 1000 //ms
02610 #define MUTETIMEOUT   5000 //ms
02611 
02612 cDisplayVolume *cDisplayVolume::displayVolume = NULL;
02613 
02614 cDisplayVolume::cDisplayVolume(void)
02615 :cOsdObject(true)
02616 {
02617   displayVolume = this;
02618   timeout = time_ms() + (cDevice::PrimaryDevice()->IsMute() ? MUTETIMEOUT : VOLUMETIMEOUT);
02619   Interface->Open(Setup.OSDwidth, -1);
02620   Show();
02621 }
02622 
02623 cDisplayVolume::~cDisplayVolume()
02624 {
02625   Interface->Close();
02626   displayVolume = NULL;
02627 }
02628 
02629 void cDisplayVolume::Show(void)
02630 {
02631   cDevice *device = cDevice::PrimaryDevice();
02632   if (device->IsMute()) {
02633      Interface->Fill(0, 0, Width(), 1, clrTransparent);
02634      Interface->Write(0, 0, tr("Mute"), clrGreen);
02635      }
02636   else {
02637      int Current = cDevice::CurrentVolume();
02638      int Total = MAXVOLUME;
02639      const char *Prompt = tr("Volume ");
02640 #ifdef DEBUG_OSD
02641      int l = strlen(Prompt);
02642      int p = int(double(Width() - l) * Current / Total + 0.5);
02643      Interface->Write(0, 0, Prompt, clrGreen);
02644      Interface->Fill(l, 0, p, 1, clrGreen);
02645      Interface->Fill(l + p, 0, Width() - l - p, 1, clrWhite);
02646 #else
02647      cVolumeBar VolumeBar(Width() * cOsd::CellWidth(), cOsd::LineHeight(), Current, Total, Prompt);
02648      Interface->SetBitmap(0, 0, VolumeBar);
02649 #endif
02650      }
02651 }
02652 
02653 cDisplayVolume *cDisplayVolume::Create(void)
02654 {
02655   if (!displayVolume)
02656      new cDisplayVolume;
02657   return displayVolume;
02658 }
02659 
02660 void cDisplayVolume::Process(eKeys Key)
02661 {
02662   if (displayVolume)
02663      displayVolume->ProcessKey(Key);
02664 }
02665 
02666 eOSState cDisplayVolume::ProcessKey(eKeys Key)
02667 {
02668   switch (Key) {
02669     case kVolUp|k_Repeat:
02670     case kVolUp:
02671     case kVolDn|k_Repeat:
02672     case kVolDn:
02673          Show();
02674          timeout = time_ms() + VOLUMETIMEOUT;
02675          break;
02676     case kMute:
02677          if (cDevice::PrimaryDevice()->IsMute()) {
02678             Show();
02679             timeout = time_ms() + MUTETIMEOUT;
02680             }
02681          else
02682             timeout = 0;
02683          break;
02684     case kNone: break;
02685     default: if ((Key & k_Release) == 0) {
02686                 cRemote::Put(Key);
02687                 return osEnd;
02688                 }
02689     }
02690   return time_ms() < timeout ? osContinue : osEnd;
02691 }
02692 
02693 // --- cRecordControl --------------------------------------------------------
02694 
02695 cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
02696 {
02697   eventInfo = NULL;
02698   instantId = NULL;
02699   fileName = NULL;
02700   recorder = NULL;
02701   device = Device;
02702   if (!device) device = cDevice::PrimaryDevice();//XXX
02703   timer = Timer;
02704   if (!timer) {
02705      timer = new cTimer(true);
02706      Timers.Add(timer);
02707      Timers.Save();
02708      asprintf(&instantId, cDevice::NumDevices() > 1 ? "%s - %d" : "%s", timer->Channel()->Name(), device->CardIndex() + 1);
02709      }
02710   timer->SetPending(true);
02711   timer->SetRecording(true);
02712 
02713   const char *Title = NULL;
02714   const char *Subtitle = NULL;
02715   const char *Summary = NULL;
02716   if (GetEventInfo()) {
02717      Title = eventInfo->GetTitle();
02718      Subtitle = eventInfo->GetSubtitle();
02719      Summary = eventInfo->GetExtendedDescription();
02720      dsyslog("Title: '%s' Subtitle: '%s'", Title, Subtitle);
02721      }
02722   cRecording Recording(timer, Title, Subtitle, Summary);
02723   fileName = strdup(Recording.FileName());
02724   cRecordingUserCommand::InvokeCommand(RUC_BEFORERECORDING, fileName);
02725   const cChannel *ch = timer->Channel();
02726   recorder = new cRecorder(fileName, ch->Ca(), timer->Priority(), ch->Vpid(), ch->Apid1(), ch->Apid2(), ch->Dpid1(), ch->Dpid2());
02727   if (device->AttachReceiver(recorder)) {
02728      Recording.WriteSummary();
02729      cStatus::MsgRecording(device, Recording.Name());
02730      }
02731   else
02732      DELETENULL(recorder);
02733 }
02734 
02735 cRecordControl::~cRecordControl()
02736 {
02737   Stop(true);
02738   free(instantId);
02739   free(fileName);
02740 }
02741 
02742 #define INSTANT_REC_EPG_LOOKAHEAD 300 // seconds to look into the EPG data for an instant recording
02743 
02744 bool cRecordControl::GetEventInfo(void)
02745 {
02746   const cChannel *channel = timer->Channel();
02747   time_t Time = timer->Active() == taActInst ? timer->StartTime() + INSTANT_REC_EPG_LOOKAHEAD : timer->StartTime() + (timer->StopTime() - timer->StartTime()) / 2;
02748   for (int seconds = 0; seconds <= MAXWAIT4EPGINFO; seconds++) {
02749       {
02750         cMutexLock MutexLock;
02751         const cSchedules *Schedules = cSIProcessor::Schedules(MutexLock);
02752         if (Schedules) {
02753            const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
02754            if (Schedule) {
02755               eventInfo = Schedule->GetEventAround(Time);
02756               if (eventInfo) {
02757                  if (seconds > 0)
02758                     dsyslog("got EPG info after %d seconds", seconds);
02759                  return true;
02760                  }
02761               }
02762            }
02763       }
02764       if (seconds == 0)
02765          dsyslog("waiting for EPG info...");
02766       sleep(1);
02767       }
02768   dsyslog("no EPG info available");
02769   return false;
02770 }
02771 
02772 void cRecordControl::Stop(bool KeepInstant)
02773 {
02774   if (timer) {
02775      DELETENULL(recorder);
02776      timer->SetRecording(false);
02777      if ((IsInstant() && !KeepInstant) || (timer->IsSingleEvent() && timer->StopTime() <= time(NULL))) {
02778         isyslog("deleting timer %d", timer->Index() + 1);
02779         Timers.Del(timer);
02780         Timers.Save();
02781         }
02782      timer = NULL;
02783      cStatus::MsgRecording(device, NULL);
02784      cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);
02785      }
02786 }
02787 
02788 bool cRecordControl::Process(time_t t)
02789 {
02790   if (!recorder || !timer || !timer->Matches(t))
02791      return false;
02792   AssertFreeDiskSpace(timer->Priority());
02793   return true;
02794 }
02795 
02796 // --- cRecordControls -------------------------------------------------------
02797 
02798 cRecordControl *cRecordControls::RecordControls[MAXRECORDCONTROLS] = { NULL };
02799 
02800 bool cRecordControls::Start(cTimer *Timer)
02801 {
02802   int ch = Timer ? Timer->Channel()->Number() : cDevice::CurrentChannel();
02803   cChannel *channel = Channels.GetByNumber(ch);
02804 
02805   if (channel) {
02806      bool NeedsDetachReceivers = false;
02807      cDevice *device = cDevice::GetDevice(channel, Timer ? Timer->Priority() : Setup.DefaultPriority, &NeedsDetachReceivers);
02808      if (device) {
02809         if (NeedsDetachReceivers)
02810            Stop(device);
02811         if (!device->SwitchChannel(channel, false)) {
02812            cThread::EmergencyExit(true);
02813            return false;
02814            }
02815         for (int i = 0; i < MAXRECORDCONTROLS; i++) {
02816             if (!RecordControls[i]) {
02817                RecordControls[i] = new cRecordControl(device, Timer);
02818                return true;
02819                }
02820             }
02821         }
02822      else if (!Timer || (Timer->Priority() >= Setup.PrimaryLimit && !Timer->Pending()))
02823         isyslog("no free DVB device to record channel %d!", ch);
02824      }
02825   else
02826      esyslog("ERROR: channel %d not defined!", ch);
02827   return false;
02828 }
02829 
02830 void cRecordControls::Stop(const char *InstantId)
02831 {
02832   for (int i = 0; i < MAXRECORDCONTROLS; i++) {
02833       if (RecordControls[i]) {
02834          const char *id = RecordControls[i]->InstantId();
02835          if (id && strcmp(id, InstantId) == 0)
02836             RecordControls[i]->Stop();
02837          }
02838       }
02839 }
02840 
02841 void cRecordControls::Stop(cDevice *Device)
02842 {
02843   for (int i = 0; i < MAXRECORDCONTROLS; i++) {
02844       if (RecordControls[i]) {
02845          if (RecordControls[i]->Uses(Device)) {
02846             isyslog("stopping recording on DVB device %d due to higher priority", Device->CardIndex() + 1);
02847             RecordControls[i]->Stop(true);
02848             }
02849          }
02850       }
02851 }
02852 
02853 bool cRecordControls::StopPrimary(bool DoIt)
02854 {
02855   if (cDevice::PrimaryDevice()->Receiving()) {
02856      //XXX+ disabled for the moment - might become obsolete with DVB_DRIVER_VERSION >= 2002090101
02857      cDevice *device = NULL;//XXX cDevice::GetDevice(cDevice::PrimaryDevice()->Ca(), 0);
02858      if (device) {
02859         if (DoIt)
02860            Stop(cDevice::PrimaryDevice());
02861         return true;
02862         }
02863      }
02864   return false;
02865 }
02866 
02867 const char *cRecordControls::GetInstantId(const char *LastInstantId)
02868 {
02869   for (int i = 0; i < MAXRECORDCONTROLS; i++) {
02870       if (RecordControls[i]) {
02871          if (!LastInstantId && RecordControls[i]->InstantId())
02872             return RecordControls[i]->InstantId();
02873          if (LastInstantId && LastInstantId == RecordControls[i]->InstantId())
02874             LastInstantId = NULL;
02875          }
02876       }
02877   return NULL;
02878 }
02879 
02880 cRecordControl *cRecordControls::GetRecordControl(const char *FileName)
02881 {
02882   for (int i = 0; i < MAXRECORDCONTROLS; i++) {
02883       if (RecordControls[i] && strcmp(RecordControls[i]->FileName(), FileName) == 0)
02884          return RecordControls[i];
02885       }
02886   return NULL;
02887 }
02888 
02889 void cRecordControls::Process(time_t t)
02890 {
02891   for (int i = 0; i < MAXRECORDCONTROLS; i++) {
02892       if (RecordControls[i]) {
02893          if (!RecordControls[i]->Process(t))
02894             DELETENULL(RecordControls[i]);
02895          }
02896       }
02897 }
02898 
02899 bool cRecordControls::Active(void)
02900 {
02901   for (int i = 0; i < MAXRECORDCONTROLS; i++) {
02902       if (RecordControls[i])
02903          return true;
02904       }
02905   return false;
02906 }
02907 
02908 void cRecordControls::Shutdown(void)
02909 {
02910   for (int i = 0; i < MAXRECORDCONTROLS; i++)
02911       DELETENULL(RecordControls[i]);
02912 }
02913 
02914 // --- cProgressBar ----------------------------------------------------------
02915 
02916 class cProgressBar : public cBitmap {
02917 protected:
02918   int total;
02919   int Pos(int p) { return p * width / total; }
02920   void Mark(int x, bool Start, bool Current);
02921 public:
02922   cProgressBar(int Width, int Height, int Current, int Total, const cMarks &Marks);
02923   };
02924 
02925 cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks &Marks)
02926 :cBitmap(Width, Height, 2)
02927 {
02928   total = Total;
02929   if (total > 0) {
02930      int p = Pos(Current);
02931      Fill(0, 0, p, Height - 1, clrGreen);
02932      Fill(p + 1, 0, Width - 1, Height - 1, clrWhite);
02933      bool Start = true;
02934      for (const cMark *m = Marks.First(); m; m = Marks.Next(m)) {
02935          int p1 = Pos(m->position);
02936          if (Start) {
02937             const cMark *m2 = Marks.Next(m);
02938             int p2 = Pos(m2 ? m2->position : total);
02939             int h = Height / 3;
02940             Fill(p1, h, p2, Height - h, clrRed);
02941             }
02942          Mark(p1, Start, m->position == Current);
02943          Start = !Start;
02944          }
02945      }
02946 }
02947 
02948 void cProgressBar::Mark(int x, bool Start, bool Current)
02949 {
02950   Fill(x, 0, x, height - 1, clrBlack);
02951   const int d = height / (Current ? 3 : 9);
02952   for (int i = 0; i < d; i++) {
02953       int h = Start ? i : height - 1 - i;
02954       Fill(x - d + i, h, x + d - i, h, Current ? clrRed : clrBlack);
02955       }
02956 }
02957 
02958 // --- cReplayControl --------------------------------------------------------
02959 
02960 char *cReplayControl::fileName = NULL;
02961 char *cReplayControl::title = NULL;
02962 
02963 cReplayControl::cReplayControl(void)
02964 :cDvbPlayerControl(fileName)
02965 {
02966   visible = modeOnly = shown = displayFrames = false;
02967   lastCurrent = lastTotal = -1;
02968   timeoutShow = 0;
02969   timeSearchActive = false;
02970   marks.Load(fileName);
02971   cRecording Recording(fileName);
02972   cStatus::MsgReplaying(this, Recording.Name());
02973 }
02974 
02975 cReplayControl::~cReplayControl()
02976 {
02977   Hide();
02978   cStatus::MsgReplaying(this, NULL);
02979   Stop();
02980 }
02981 
02982 void cReplayControl::SetRecording(const char *FileName, const char *Title)
02983 {
02984   free(fileName);
02985   free(title);
02986   fileName = FileName ? strdup(FileName) : NULL;
02987   title = Title ? strdup(Title) : NULL;
02988 }
02989 
02990 const char *cReplayControl::LastReplayed(void)
02991 {
02992   return fileName;
02993 }
02994 
02995 void cReplayControl::ClearLastReplayed(const char *FileName)
02996 {
02997   if (fileName && FileName && strcmp(fileName, FileName) == 0) {
02998      free(fileName);
02999      fileName = NULL;
03000      }
03001 }
03002 
03003 void cReplayControl::Show(int Seconds)
03004 {
03005   if (modeOnly)
03006      Hide();
03007   if (!visible) {
03008      shown = ShowProgress(true);
03009      timeoutShow = (shown && Seconds > 0) ? time(NULL) + Seconds : 0;
03010      }
03011 }
03012 
03013 void cReplayControl::Hide(void)
03014 {
03015   if (visible) {
03016      Interface->Close();
03017      needsFastResponse = visible = false;
03018      modeOnly = false;
03019      }
03020 }
03021 
03022 void cReplayControl::DisplayAtBottom(const char *s)
03023 {
03024   if (s) {
03025      int w = cOsd::WidthInCells(s);
03026      int d = max(Width() - w, 0) / 2;
03027      if (modeOnly) //XXX remove when displaying replay mode differently
03028         Interface->Fill(0, -1, Interface->Width(), 1, clrTransparent); //XXX remove when displaying replay mode differently
03029      Interface->Write(d, -1, s);
03030      Interface->Flush();
03031      }
03032   else
03033      Interface->Fill(12, 2, Width() - 22, 1, clrBackground);
03034 }
03035 
03036 void cReplayControl::ShowMode(void)
03037 {
03038   if (Setup.ShowReplayMode && !timeSearchActive) {
03039      bool Play, Forward;
03040      int Speed;
03041      if (GetReplayMode(Play, Forward, Speed)) {
03042         bool NormalPlay = (Play && Speed == -1);
03043 
03044         if (!visible) {
03045            if (NormalPlay)
03046               return; // no need to do indicate ">" unless there was a different mode displayed before
03047            // open small display
03048            /*XXX change when displaying replay mode differently
03049            Interface->Open(9, -1);
03050            Interface->Clear();
03051            XXX*/
03052            Interface->Open(0, -1); //XXX remove when displaying replay mode differently
03053            visible = modeOnly = true;
03054            }
03055 
03056         if (modeOnly && !timeoutShow && NormalPlay)
03057            timeoutShow = time(NULL) + MODETIMEOUT;
03058         const char *Mode;
03059         if (Speed == -1) Mode = Play    ? "  >  " : " ||  ";
03060         else if (Play)   Mode = Forward ? " X>> " : " <<X ";
03061         else             Mode = Forward ? " X|> " : " <|X ";
03062         char buf[16];
03063         strn0cpy(buf, Mode, sizeof(buf));
03064         char *p = strchr(buf, 'X');
03065         if (p)
03066            *p = Speed > 0 ? '1' + Speed - 1 : ' ';
03067 
03068         eDvbFont OldFont = Interface->SetFont(fontFix);
03069         DisplayAtBottom(buf);
03070         Interface->SetFont(OldFont);
03071         }
03072      }
03073 }
03074 
03075 bool cReplayControl::ShowProgress(bool Initial)
03076 {
03077   int Current, Total;
03078 
03079   if (GetIndex(Current, Total) && Total > 0) {
03080      if (!visible) {
03081         Interface->Open(Setup.OSDwidth, -3);
03082         needsFastResponse = visible = true;
03083         }
03084      if (Initial) {
03085         Interface->Clear();
03086         if (title)
03087            Interface->Write(0, 0, title);
03088         lastCurrent = lastTotal = -1;
03089         }
03090      if (Total != lastTotal) {
03091         Interface->Write(-7, 2, IndexToHMSF(Total));
03092         if (!Initial)
03093            Interface->Flush();
03094         }
03095      if (Current != lastCurrent || Total != lastTotal) {
03096 #ifdef DEBUG_OSD
03097         int p = Width() * Current / Total;
03098         Interface->Fill(0, 1, p, 1, clrGreen);
03099         Interface->Fill(p, 1, Width() - p, 1, clrWhite);
03100 #else
03101         cProgressBar ProgressBar(Width() * cOsd::CellWidth(), cOsd::LineHeight(), Current, Total, marks);
03102         Interface->SetBitmap(0, cOsd::LineHeight(), ProgressBar);
03103         if (!Initial)
03104            Interface->Flush();
03105 #endif
03106         Interface->Write(0, 2, IndexToHMSF(Current, displayFrames));
03107         Interface->Flush();
03108         lastCurrent = Current;
03109         }
03110      lastTotal = Total;
03111      ShowMode();
03112      return true;
03113      }
03114   return false;
03115 }
03116 
03117 void cReplayControl::TimeSearchDisplay(void)
03118 {
03119   char buf[64];
03120   strcpy(buf, tr("Jump: "));
03121   int len = strlen(buf);
03122   char h10 = '0' + (timeSearchTime >> 24);
03123   char h1  = '0' + ((timeSearchTime & 0x00FF0000) >> 16);
03124   char m10 = '0' + ((timeSearchTime & 0x0000FF00) >> 8);
03125   char m1  = '0' + (timeSearchTime & 0x000000FF);
03126   char ch10 = timeSearchPos > 3 ? h10 : '-';
03127   char ch1  = timeSearchPos > 2 ? h1  : '-';
03128   char cm10 = timeSearchPos > 1 ? m10 : '-';
03129   char cm1  = timeSearchPos > 0 ? m1  : '-';
03130   sprintf(buf + len, "%c%c:%c%c", ch10, ch1, cm10, cm1);
03131   DisplayAtBottom(buf);
03132 }
03133 
03134 void cReplayControl::TimeSearchProcess(eKeys Key)
03135 {
03136 #define STAY_SECONDS_OFF_END 10
03137   int Seconds = (timeSearchTime >> 24) * 36000 + ((timeSearchTime & 0x00FF0000) >> 16) * 3600 + ((timeSearchTime & 0x0000FF00) >> 8) * 600 + (timeSearchTime & 0x000000FF) * 60;
03138   int Current = (lastCurrent / FRAMESPERSEC);
03139   int Total = (lastTotal / FRAMESPERSEC);
03140   switch (Key) {
03141     case k0 ... k9:
03142          if (timeSearchPos < 4) {
03143             timeSearchTime <<= 8;
03144             timeSearchTime |= Key - k0;
03145             timeSearchPos++;
03146             TimeSearchDisplay();
03147             }
03148          break;
03149     case kFastRew:
03150     case kLeft:
03151     case kFastFwd:
03152     case kRight: {
03153          int dir = ((Key == kRight || Key == kFastFwd) ? 1 : -1);
03154          if (dir > 0)
03155             Seconds = min(Total - Current - STAY_SECONDS_OFF_END, Seconds);
03156          SkipSeconds(Seconds * dir);
03157          timeSearchActive = false;
03158          }
03159          break;
03160     case kPlay:
03161     case kUp:
03162     case kPause:
03163     case kDown:
03164          Seconds = min(Total - STAY_SECONDS_OFF_END, Seconds);
03165          Goto(Seconds * FRAMESPERSEC, Key == kDown || Key == kPause);
03166          timeSearchActive = false;
03167          break;
03168     default:
03169          timeSearchActive = false;
03170          break;
03171     }
03172 
03173   if (!timeSearchActive) {
03174      if (timeSearchHide)
03175         Hide();
03176      else
03177         DisplayAtBottom();
03178      ShowMode();
03179      }
03180 }
03181 
03182 void cReplayControl::TimeSearch(void)
03183 {
03184   timeSearchTime = timeSearchPos = 0;
03185   timeSearchHide = false;
03186   if (modeOnly)
03187      Hide();
03188   if (!visible) {
03189      Show();
03190      if (visible)
03191         timeSearchHide = true;
03192      else
03193         return;
03194      }
03195   timeoutShow = 0;
03196   TimeSearchDisplay();
03197   timeSearchActive = true;
03198 }
03199 
03200 void cReplayControl::MarkToggle(void)
03201 {
03202   int Current, Total;
03203   if (GetIndex(Current, Total, true)) {
03204      cMark *m = marks.Get(Current);
03205      lastCurrent = -1; // triggers redisplay
03206      if (m)
03207         marks.Del(m);
03208      else {
03209         marks.Add(Current);
03210         Show(2);
03211         }
03212      marks.Save();
03213      }
03214 }
03215 
03216 void cReplayControl::MarkJump(bool Forward)
03217 {
03218   if (marks.Count()) {
03219      int Current, Total;
03220      if (GetIndex(Current, Total)) {
03221         cMark *m = Forward ? marks.GetNext(Current) : marks.GetPrev(Current);
03222         if (m)
03223            Goto(m->position, true);
03224         }
03225      displayFrames = true;
03226      }
03227 }
03228 
03229 void cReplayControl::MarkMove(bool Forward)
03230 {
03231   int Current, Total;
03232   if (GetIndex(Current, Total)) {
03233      cMark *m = marks.Get(Current);
03234      if (m) {
03235         displayFrames = true;
03236         int p = SkipFrames(Forward ? 1 : -1);
03237         cMark *m2;
03238         if (Forward) {
03239            if ((m2 = marks.Next(m)) != NULL && m2->position <= p)
03240               return;
03241            }
03242         else {
03243            if ((m2 = marks.Prev(m)) != NULL && m2->position >= p)
03244               return;
03245            }
03246         Goto(m->position = p, true);
03247         marks.Save();
03248         }
03249      }
03250 }
03251 
03252 void cReplayControl::EditCut(void)
03253 {
03254   if (fileName) {
03255      Hide();
03256      if (!cCutter::Active()) {
03257         if (!cCutter::Start(fileName))
03258            Interface->Error(tr("Can't start editing process!"));
03259         else
03260            Interface->Info(tr("Editing process started"));
03261         }
03262      else
03263         Interface->Error(tr("Editing process already active!"));
03264      ShowMode();
03265      }
03266 }
03267 
03268 void cReplayControl::EditTest(void)
03269 {
03270   int Current, Total;
03271   if (GetIndex(Current, Total)) {
03272      cMark *m = marks.Get(Current);
03273      if (!m)
03274         m = marks.GetNext(Current);
03275      if (m) {
03276         if ((m->Index() & 0x01) != 0)
03277            m = marks.Next(m);
03278         if (m) {
03279            Goto(m->position - SecondsToFrames(3));
03280            Play();
03281            }
03282         }
03283      }
03284 }
03285 
03286 eOSState cReplayControl::ProcessKey(eKeys Key)
03287 {
03288   if (!Active())
03289      return osEnd;
03290   if (visible) {
03291      if (timeoutShow && time(NULL) > timeoutShow) {
03292         Hide();
03293         ShowMode();
03294         timeoutShow = 0;
03295         }
03296      else if (modeOnly)
03297         ShowMode();
03298      else
03299         shown = ShowProgress(!shown) || shown;
03300      }
03301   bool DisplayedFrames = displayFrames;
03302   displayFrames = false;
03303   if (timeSearchActive && Key != kNone) {
03304      TimeSearchProcess(Key);
03305      return osContinue;
03306      }
03307   bool DoShowMode = true;
03308   switch (Key) {
03309     // Positioning:
03310     case kPlay:
03311     case kUp:      Play(); break;
03312     case kPause:
03313     case kDown:    Pause(); break;
03314     case kFastRew|k_Release:
03315     case kLeft|k_Release:
03316                    if (Setup.MultiSpeedMode) break;
03317     case kFastRew:
03318     case kLeft:    Backward(); break;
03319     case kFastFwd|k_Release:
03320     case kRight|k_Release:
03321                    if (Setup.MultiSpeedMode) break;
03322     case kFastFwd:
03323     case kRight:   Forward(); break;
03324     case kRed:     TimeSearch(); break;
03325     case kGreen|k_Repeat:
03326     case kGreen:   SkipSeconds(-60); break;
03327     case kYellow|k_Repeat:
03328     case kYellow:  SkipSeconds( 60); break;
03329     case kStop:
03330     case kBlue:    Hide();
03331                    Stop();
03332                    return osEnd;
03333     default: {
03334       DoShowMode = false;
03335       switch (Key) {
03336         // Editing:
03337         //XXX should we do this only when the ProgressDisplay is on???
03338         case kMarkToggle:      MarkToggle(); break;
03339         case kMarkJumpBack:    MarkJump(false); break;
03340         case kMarkJumpForward: MarkJump(true); break;
03341         case kMarkMoveBack|k_Repeat:
03342         case kMarkMoveBack:    MarkMove(false); break;
03343         case kMarkMoveForward|k_Repeat:
03344         case kMarkMoveForward: MarkMove(true); break;
03345         case kEditCut:         EditCut(); break;
03346         case kEditTest:        EditTest(); break;
03347         default: {
03348           displayFrames = DisplayedFrames;
03349           switch (Key) {
03350             // Menu control:
03351             case kOk:      if (visible && !modeOnly) {
03352                               Hide();
03353                               DoShowMode = true;
03354                               }
03355                            else
03356                               Show();
03357                            break;
03358             case kBack:    return osRecordings;
03359             default:       return osUnknown;
03360             }
03361           }
03362         }
03363       }
03364     }
03365   if (DoShowMode)
03366      ShowMode();
03367   if (DisplayedFrames && !displayFrames)
03368      Interface->Fill(0, 2, 11, 1, clrBackground);
03369   return osContinue;
03370 }
03371 

Generated on Wed Feb 5 23:30:10 2003 for VDR by doxygen1.3-rc2