Feature request: program guide scroll

Message ID 4C1CE454.5020706@gmx.de
State New
Headers

Commit Message

Udo Richter June 19, 2010, 3:37 p.m. UTC
  Am 17.06.2010 19:29, schrieb Udo Richter:
> Am 17.06.2010 17:31, schrieb martinez:
>> Can any kind person write this patch? 
> 
> I'll see if I can come up with something on the weekend.


As always, the more close you look on it, the more complicated it gets.
In other words, it got a little more than 12 lines... ;)

The cMenuEvent can now switch through the list of events of the parent
menu with the green and yellow buttons. Depending on the parent menu,
the buttons either show channel names or event starting times. From
timer menu (blue button), the green and yellow buttons stay empty.

To gain access to the list of events, I've added an abstract
cEventSequence class that is implemented by cMenuWhatsOn and
cMenuSchedule. That way these two and the cMenuScheduleItem can continue
to be private to menu.c.

Since now the cMenuEvent can handle different events, a convenient
shortcut had to be removed: Previously, the red and blue buttons were
handled by the parent menu, not cMenuEvent itself. cMenuEvent now does
this on its own. Also the constructor parameters CanSwitch and Buttons
were dropped and replaced by dynamic code. By this change, the event
info on blue key in timers menu gains the red and blue button for free.

cMenuEvent had its ProcessKey re-arranged, as the original version could
not handle sub-menus. (the parent menu did.)

Timer menu got a bug fix to handle sub-sub-menus without overwriting the
button bar.



The attached patch is against vdr-1.6, but also cleanly applies and
compiles on vdr-1.7.15.

A little testing help and feedback is welcome. Check that the green and
yellow buttons work in program, whats now, whats next and timer info
menu, and keep an eye on button bar text in the event menu and the
parent menu after exiting. Also, the t and T markers on events should
change, and in the timer menu the '>' for active timers, if you change
that. Try adding timers from the event menu for running and future
events. Try whether the blue key switches to the right channel. And try
whatever else you can imagine.


Cheers,

Udo
  

Comments

Torgeir Veimo June 19, 2010, 10:54 p.m. UTC | #1
On 20 June 2010 01:37, Udo Richter <udo_richter@gmx.de> wrote:
> Am 17.06.2010 19:29, schrieb Udo Richter:
>> Am 17.06.2010 17:31, schrieb martinez:
>>> Can any kind person write this patch?
>>
>> I'll see if I can come up with something on the weekend.

While you're at it, what about a configurable option to have the right
and left keys change volume instead of scrolling up and down? A lot of
remotes have the volume on those keys. It could be disable by default
while the menu is displayed of course.
  
VDRU VDRU June 19, 2010, 10:58 p.m. UTC | #2
On Sat, Jun 19, 2010 at 3:54 PM, Torgeir Veimo <torgeir@netenviron.com> wrote:
> While you're at it, what about a configurable option to have the right
> and left keys change volume instead of scrolling up and down? A lot of
> remotes have the volume on those keys. It could be disable by default
> while the menu is displayed of course.

There's tons of remotes with different configurations.  Some use arrow
keys up/down, some use left/right, some use dedicated volume buttons.
You should probably look into editing your remote.conf to properly
reflect the buttons on your remote I guess.
  
VDRU VDRU June 19, 2010, 11:24 p.m. UTC | #3
On Sat, Jun 19, 2010 at 5:20 PM, Tony Houghton <h@realh.co.uk> wrote:
>> There's tons of remotes with different configurations.  Some use arrow
>> keys up/down, some use left/right, some use dedicated volume buttons.
>> You should probably look into editing your remote.conf to properly
>> reflect the buttons on your remote I guess.
>
> But can VDR work correctly if the volume keys are also left & right? Ie
> can it use them as a volume control while playing something but use the
> same keys for navigation in menus etc?

Only takes a few seconds to give it a try!  ;)
  
Torgeir Veimo June 19, 2010, 11:33 p.m. UTC | #4
On 20 June 2010 09:24, VDR User <user.vdr@gmail.com> wrote:
> On Sat, Jun 19, 2010 at 5:20 PM, Tony Houghton <h@realh.co.uk> wrote:
>>> There's tons of remotes with different configurations.  Some use arrow
>>> keys up/down, some use left/right, some use dedicated volume buttons.
>>> You should probably look into editing your remote.conf to properly
>>> reflect the buttons on your remote I guess.
>>
>> But can VDR work correctly if the volume keys are also left & right? Ie
>> can it use them as a volume control while playing something but use the
>> same keys for navigation in menus etc?
>
> Only takes a few seconds to give it a try!  ;)

My volume up/down keys doesn't change options in any option menu, it
only changes the volume. The idea was to have them work as left right
while the menu is active.
  
Tony Houghton June 20, 2010, 12:20 a.m. UTC | #5
On Sat, 19 Jun 2010 15:58:52 -0700
VDR User <user.vdr@gmail.com> wrote:

> On Sat, Jun 19, 2010 at 3:54 PM, Torgeir Veimo
> <torgeir@netenviron.com> wrote:
> > While you're at it, what about a configurable option to have the
> > right and left keys change volume instead of scrolling up and down?
> > A lot of remotes have the volume on those keys. It could be disable
> > by default while the menu is displayed of course.
> 
> There's tons of remotes with different configurations.  Some use arrow
> keys up/down, some use left/right, some use dedicated volume buttons.
> You should probably look into editing your remote.conf to properly
> reflect the buttons on your remote I guess.

But can VDR work correctly if the volume keys are also left & right? Ie
can it use them as a volume control while playing something but use the
same keys for navigation in menus etc?
  
Udo Richter June 20, 2010, 8:18 p.m. UTC | #6
Am 20.06.2010 00:54, schrieb Torgeir Veimo:
> On 20 June 2010 01:37, Udo Richter <udo_richter@gmx.de> wrote:
>> Am 17.06.2010 19:29, schrieb Udo Richter:
>>> Am 17.06.2010 17:31, schrieb martinez:
>>>> Can any kind person write this patch?
>>>
>>> I'll see if I can come up with something on the weekend.
> 
> While you're at it, what about a configurable option to have the right
> and left keys change volume instead of scrolling up and down? A lot of
> remotes have the volume on those keys. It could be disable by default
> while the menu is displayed of course.

What exactly does the EPG event display have to do with the meaning of
left/right key in live TV view?

Also, the left/right key in live TV view is already in use.


Cheers,

Udo
  
Frank Schmirler June 20, 2010, 9:27 p.m. UTC | #7
On Sun, 20 Jun 2010 08:54:23 +1000, Torgeir Veimo wrote
> While you're at it, what about a configurable option to have the 
> right and left keys change volume instead of scrolling up and down? 
> A lot of remotes have the volume on those keys. It could be disable 
> by default while the menu is displayed of course.

I wrote such a patch quite a while ago for VDR 1.4. Just ported it to
VDR-1.7.12. Please see README for an explanation of the options. Patch is
available from http://vdr.schmirler.de/volctrl/

Regards,
Frank
  
Füley István June 21, 2010, 10:47 a.m. UTC | #8
I'd tried your patch last night, but unfortunamtely ... I haven't seen 
any changes.
My vdr 1.7.10 is patched with liemikuutio patch, and your patch applied
with some 20 line offsets, but it said, that all the hunks succeded.
I will try with a plain vdr this evening.

István

Udo Richter wrote:
> Am 17.06.2010 19:29, schrieb Udo Richter:
>> Am 17.06.2010 17:31, schrieb martinez:
>>> Can any kind person write this patch? 
>> I'll see if I can come up with something on the weekend.
> 
> 
> As always, the more close you look on it, the more complicated it gets.
> In other words, it got a little more than 12 lines... ;)
> 
> The cMenuEvent can now switch through the list of events of the parent
> menu with the green and yellow buttons. Depending on the parent menu,
> the buttons either show channel names or event starting times. From
> timer menu (blue button), the green and yellow buttons stay empty.
> 
> To gain access to the list of events, I've added an abstract
> cEventSequence class that is implemented by cMenuWhatsOn and
> cMenuSchedule. That way these two and the cMenuScheduleItem can continue
> to be private to menu.c.
> 
> Since now the cMenuEvent can handle different events, a convenient
> shortcut had to be removed: Previously, the red and blue buttons were
> handled by the parent menu, not cMenuEvent itself. cMenuEvent now does
> this on its own. Also the constructor parameters CanSwitch and Buttons
> were dropped and replaced by dynamic code. By this change, the event
> info on blue key in timers menu gains the red and blue button for free.
> 
> cMenuEvent had its ProcessKey re-arranged, as the original version could
> not handle sub-menus. (the parent menu did.)
> 
> Timer menu got a bug fix to handle sub-sub-menus without overwriting the
> button bar.
> 
> 
> 
> The attached patch is against vdr-1.6, but also cleanly applies and
> compiles on vdr-1.7.15.
> 
> A little testing help and feedback is welcome. Check that the green and
> yellow buttons work in program, whats now, whats next and timer info
> menu, and keep an eye on button bar text in the event menu and the
> parent menu after exiting. Also, the t and T markers on events should
> change, and in the timer menu the '>' for active timers, if you change
> that. Try adding timers from the event menu for running and future
> events. Try whether the blue key switches to the right channel. And try
> whatever else you can imagine.
> 
> 
> Cheers,
> 
> Udo
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> vdr mailing list
> vdr@linuxtv.org
> http://www.linuxtv.org/cgi-bin/mailman/listinfo/vdr
  

Patch

diff -Naur vdr-1.6.0/menu.h vdr-1.6.0-MenuEventNext/menu.h
--- vdr-1.6.0/menu.h	2008-02-10 17:01:53.000000000 +0100
+++ vdr-1.6.0-MenuEventNext/menu.h	2010-06-19 15:33:20.000000000 +0200
@@ -45,12 +45,28 @@ 
   virtual eOSState ProcessKey(eKeys Key);
   };
 
+class cEventSequence {
+public:
+  virtual const cEvent* GetEvent(int Nr) const = 0;
+  virtual int GetEventCount() const = 0;
+};
+
 class cMenuEvent : public cOsdMenu {
 private:
   const cEvent *event;
-public:
-  cMenuEvent(const cEvent *Event, bool CanSwitch = false, bool Buttons = false);
+  const cEventSequence *eventSequence;
+  int eventNr;
+  int otherChannel;
+  char *green;
+  char *yellow;
+  void UpdateEvent();
+public:
+  cMenuEvent(const cEvent *Event);
+  cMenuEvent(const cEventSequence *Events, int EventNr);
+  virtual ~cMenuEvent();
   virtual void Display(void);
+  eOSState Record(void);
+  eOSState Switch(void);
   virtual eOSState ProcessKey(eKeys Key);
   };
 
diff -Naur vdr-1.6.0/menu.c vdr-1.6.0-MenuEventNext/menu.c
--- vdr-1.6.0/menu.c	2008-03-16 12:15:28.000000000 +0100
+++ vdr-1.6.0-MenuEventNext/menu.c	2010-06-19 16:38:35.000000000 +0200
@@ -974,27 +974,99 @@ 
      Add(new cMenuTimerItem(Timers.Get(TimerNumber)), true);
      Display();
      }
-  if (Key != kNone)
+  if (Key != kNone && !HasSubMenu())
      SetHelpKeys();
   return state;
 }
 
 // --- cMenuEvent ------------------------------------------------------------
 
-cMenuEvent::cMenuEvent(const cEvent *Event, bool CanSwitch, bool Buttons)
+cMenuEvent::cMenuEvent(const cEvent *Event)
 :cOsdMenu(tr("Event"))
 {
   event = Event;
+  eventNr = 0;
+  eventSequence = NULL;
+  green = NULL;
+  yellow = NULL;
+  UpdateEvent();
+}
+
+cMenuEvent::cMenuEvent(const cEventSequence *Events, int EventNr)
+:cOsdMenu(tr("Event"))
+{
+  eventSequence = Events;
+  eventNr = EventNr;
+  green = NULL;
+  yellow = NULL;
+  UpdateEvent();
+}
+
+cMenuEvent::~cMenuEvent() {
+  if (green)
+     free(green);
+  if (yellow)
+     free(yellow);
+}
+
+void cMenuEvent::UpdateEvent()
+{
+  const cEvent *eventPrev = NULL;
+  const cEvent *eventNext = NULL;
+  int TimerMatch = tmNone;
+  otherChannel = 0;
+
+  if (eventSequence) {
+     event = eventSequence->GetEvent(eventNr);
+     eventPrev = eventSequence->GetEvent(eventNr-1);
+     eventNext = eventSequence->GetEvent(eventNr+1);
+     }
+
   if (event) {
      cChannel *channel = Channels.GetByChannelID(event->ChannelID(), true);
      if (channel) {
         SetTitle(channel->Name());
-        int TimerMatch = tmNone;
-        Timers.GetMatch(event, &TimerMatch);
-        if (Buttons)
-           SetHelp(TimerMatch == tmFull ? tr("Button$Timer") : tr("Button$Record"), NULL, NULL, CanSwitch ? tr("Button$Switch") : NULL);
+        if (channel->Number() != cDevice::CurrentChannel())
+           otherChannel = channel->Number();
         }
+     Timers.GetMatch(event, &TimerMatch);
      }
+
+  if (green) {
+     free(green);
+     green = NULL;
+     }
+  if (eventPrev) {
+     if (eventPrev->ChannelID() == event->ChannelID()) {
+        time_t tstart = eventPrev->StartTime();
+        struct tm tm_r;
+        struct tm *time = localtime_r(&tstart, &tm_r);
+        asprintf(&green, "%02d:%02d", time->tm_hour, time->tm_min);
+        }
+     else {
+        cChannel *channel = Channels.GetByChannelID(eventPrev->ChannelID(), true);
+        green = strdup(channel->Name());
+        }
+     }
+
+  if (yellow) {
+     free(yellow);
+     yellow = NULL;
+     }
+  if (eventNext) {
+     if (eventNext->ChannelID() == event->ChannelID()) {
+        time_t tstart = eventNext->StartTime();
+        struct tm tm_r;
+        struct tm *time = localtime_r(&tstart, &tm_r);
+        asprintf(&yellow, "%02d:%02d", time->tm_hour, time->tm_min);
+        }
+     else {
+        cChannel *channel = Channels.GetByChannelID(eventNext->ChannelID(), true);
+        yellow = strdup(channel->Name());
+        }
+     }
+
+  SetHelp(TimerMatch == tmFull ? tr("Button$Timer") : tr("Button$Record"), green, yellow, otherChannel ? tr("Button$Switch") : NULL);
 }
 
 void cMenuEvent::Display(void)
@@ -1005,34 +1077,87 @@ 
      cStatus::MsgOsdTextItem(event->Description());
 }
 
-eOSState cMenuEvent::ProcessKey(eKeys Key)
+eOSState cMenuEvent::Record(void)
 {
-  switch (Key) {
-    case kUp|k_Repeat:
-    case kUp:
-    case kDown|k_Repeat:
-    case kDown:
-    case kLeft|k_Repeat:
-    case kLeft:
-    case kRight|k_Repeat:
-    case kRight:
-                  DisplayMenu()->Scroll(NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft, NORMALKEY(Key) == kLeft || NORMALKEY(Key) == kRight);
-                  cStatus::MsgOsdTextItem(NULL, NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft);
-                  return osContinue;
-    case kInfo:   return osBack;
-    default: break;
-    }
+  if (event) {
+     int tm = tmNone;
+     cTimer *timer = Timers.GetMatch(event, &tm);
+     if (timer)
+        return AddSubMenu(new cMenuEditTimer(timer));
 
+     timer = new cTimer(event);
+     cTimer *t = Timers.GetTimer(timer);
+     if (t) {
+        delete timer;
+        timer = t;
+        return AddSubMenu(new cMenuEditTimer(timer));
+        }
+     else {
+        Timers.Add(timer);
+        Timers.SetModified();
+        isyslog("timer %s added (active)", *timer->ToDescr());
+        if (timer->Matches(0, false, NEWTIMERLIMIT))
+           return AddSubMenu(new cMenuEditTimer(timer));
+        UpdateEvent();
+        Display();
+        }
+     }
+  return osContinue;
+}
+
+eOSState cMenuEvent::Switch(void)
+{
+  if (otherChannel) {
+     if (Channels.SwitchTo(otherChannel))
+        return osEnd;
+     }
+  Skins.Message(mtError, tr("Can't switch channel!"));
+  return osContinue;
+}
+
+eOSState cMenuEvent::ProcessKey(eKeys Key)
+{
+  bool HadSubMenu = HasSubMenu();
   eOSState state = cOsdMenu::ProcessKey(Key);
 
   if (state == osUnknown) {
      switch (Key) {
-       case kGreen:
-       case kYellow: return osContinue;
+       case kUp|k_Repeat:
+       case kUp:
+       case kDown|k_Repeat:
+       case kDown:
+       case kLeft|k_Repeat:
+       case kLeft:
+       case kRight|k_Repeat:
+       case kRight:
+                     DisplayMenu()->Scroll(NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft, NORMALKEY(Key) == kLeft || NORMALKEY(Key) == kRight);
+                     cStatus::MsgOsdTextItem(NULL, NORMALKEY(Key) == kUp || NORMALKEY(Key) == kLeft);
+                     return osContinue;
+       case kInfo:   return osBack;
+       case kRecord:
+       case kRed:    return Record();
+       case kGreen:  if (eventSequence && eventNr > 0) {
+                        eventNr--;
+                        UpdateEvent();
+                        Display();
+                        }
+                     return osContinue;
+       case kYellow: if (eventSequence && eventNr < eventSequence->GetEventCount()-1) {
+                        eventNr++;
+                        UpdateEvent();
+                        Display();
+                        }
+                     return osContinue;
+       case kBlue:   return Switch();
        case kOk:     return osBack;
        default: break;
        }
      }
+  else if (!HasSubMenu() && HadSubMenu) {
+     UpdateEvent();
+     Display();
+     }
+
   return state;
 }
 
@@ -1106,7 +1231,7 @@ 
 
 // --- cMenuWhatsOn ----------------------------------------------------------
 
-class cMenuWhatsOn : public cOsdMenu {
+class cMenuWhatsOn : public cOsdMenu, public cEventSequence {
 private:
   bool now;
   int helpKeys;
@@ -1123,6 +1248,8 @@ 
   static void SetCurrentChannel(int ChannelNr) { currentChannel = ChannelNr; }
   static const cEvent *ScheduleEvent(void);
   virtual eOSState ProcessKey(eKeys Key);
+  virtual const cEvent* GetEvent(int Nr) const;
+  virtual int GetEventCount() const;
   };
 
 int cMenuWhatsOn::currentChannel = 0;
@@ -1253,7 +1380,7 @@ 
        case kBlue:   return Switch();
        case kInfo:
        case kOk:     if (Count())
-                        return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, true, true));
+                        return AddSubMenu(new cMenuEvent(this, Current()));
                      break;
        default:      break;
        }
@@ -1267,9 +1394,19 @@ 
   return state;
 }
 
+const cEvent* cMenuWhatsOn::GetEvent(int Nr) const {
+  if (Nr >= 0 && Nr < Count())
+     return ((cMenuScheduleItem *)Get(Nr))->event;
+  return NULL;
+  }
+
+int cMenuWhatsOn::GetEventCount() const {
+  return Count();
+  }
+
 // --- cMenuSchedule ---------------------------------------------------------
 
-class cMenuSchedule : public cOsdMenu {
+class cMenuSchedule : public cOsdMenu, public cEventSequence {
 private:
   cSchedulesLock schedulesLock;
   const cSchedules *schedules;
@@ -1290,6 +1427,8 @@ 
   cMenuSchedule(void);
   virtual ~cMenuSchedule();
   virtual eOSState ProcessKey(eKeys Key);
+  virtual const cEvent* GetEvent(int Nr) const;
+  virtual int GetEventCount() const;
   };
 
 cMenuSchedule::cMenuSchedule(void)
@@ -1518,7 +1657,7 @@ 
                      break;
        case kInfo:
        case kOk:     if (Count())
-                        return AddSubMenu(new cMenuEvent(((cMenuScheduleItem *)Get(Current()))->event, otherChannel, true));
+                        return AddSubMenu(new cMenuEvent(this, Current()));
                      break;
        default:      break;
        }
@@ -1546,6 +1685,16 @@ 
   return state;
 }
 
+const cEvent* cMenuSchedule::GetEvent(int Nr) const {
+  if (Nr >= 0 && Nr < Count())
+     return ((cMenuScheduleItem *)Get(Nr))->event;
+  return NULL;
+  }
+
+int cMenuSchedule::GetEventCount() const {
+  return Count();
+  }
+
 // --- cMenuCommands ---------------------------------------------------------
 
 class cMenuCommands : public cOsdMenu {