From 25f085cc267760a43d2294406d3fdd6d14dc6e7f Mon Sep 17 00:00:00 2001
From: Klaus Schmidinger <vdr@tvdr.de>
Date: Wed, 1 Nov 2000 11:45:05 +0100
Subject: Improved Schedules menus

---
 dvbapi.c    |  20 +++++-
 dvbapi.h    |   4 +-
 dvbosd.c    |   7 +-
 dvbosd.h    |   3 +-
 interface.c |  86 +++++++++++++++++++++--
 interface.h |   6 +-
 menu.c      | 222 +++++++++++++++++++++++++++++++++++++-----------------------
 osd.c       |   5 +-
 osd.h       |   6 +-
 9 files changed, 257 insertions(+), 102 deletions(-)

diff --git a/dvbapi.c b/dvbapi.c
index f7272f76..cc246d2f 100644
--- a/dvbapi.c
+++ b/dvbapi.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbapi.c 1.33 2000/10/29 10:39:57 kls Exp $
+ * $Id: dvbapi.c 1.34 2000/11/01 09:19:27 kls Exp $
  */
 
 #include "dvbapi.h"
@@ -1598,6 +1598,24 @@ void cDvbApi::ClrEol(int x, int y, eDvbColor color)
   Fill(x, y, cols - x, 1, color);
 }
 
+int cDvbApi::CellWidth(void)
+{
+#ifdef DEBUG_OSD
+  return 1;
+#else
+  return charWidth;
+#endif
+}
+
+int cDvbApi::Width(unsigned char c)
+{
+#ifdef DEBUG_OSD
+  return 1;
+#else
+  return osd->Width(c);
+#endif
+}
+
 void cDvbApi::Text(int x, int y, const char *s, eDvbColor colorFg, eDvbColor colorBg)
 {
   if (x < 0) x = cols + x;
diff --git a/dvbapi.h b/dvbapi.h
index 2a67585d..16a2b7a7 100644
--- a/dvbapi.h
+++ b/dvbapi.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbapi.h 1.19 2000/10/29 12:11:16 kls Exp $
+ * $Id: dvbapi.h 1.20 2000/11/01 09:18:50 kls Exp $
  */
 
 #ifndef __DVBAPI_H
@@ -122,6 +122,8 @@ public:
   void Clear(void);
   void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground);
   void ClrEol(int x, int y, eDvbColor color = clrBackground);
+  int CellWidth(void);
+  int Width(unsigned char c);
   void Text(int x, int y, const char *s, eDvbColor colorFg = clrWhite, eDvbColor colorBg = clrBackground);
   void Flush(void);
 
diff --git a/dvbosd.c b/dvbosd.c
index 97c79322..c0150e26 100644
--- a/dvbosd.c
+++ b/dvbosd.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbosd.c 1.3 2000/10/07 14:42:48 kls Exp $
+ * $Id: dvbosd.c 1.4 2000/11/01 09:13:32 kls Exp $
  */
 
 #include "dvbosd.h"
@@ -76,6 +76,11 @@ void cBitmap::SetPixel(int x, int y, eDvbColor Color)
      }
 }
 
+int cBitmap::Width(unsigned char c)
+{
+  return font ? font->Width(c) : -1;
+}
+
 void cBitmap::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor ColorBg)
 {
   if (bitmap) {
diff --git a/dvbosd.h b/dvbosd.h
index f7a53635..149dc6cb 100644
--- a/dvbosd.h
+++ b/dvbosd.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbosd.h 1.1 2000/10/01 15:00:00 kls Exp $
+ * $Id: dvbosd.h 1.2 2000/11/01 09:13:44 kls Exp $
  */
 
 #ifndef __DVBOSD_H
@@ -56,6 +56,7 @@ public:
   void SetFont(eDvbFont Font);
   bool Dirty(void);
   void SetPixel(int x, int y, eDvbColor Color);
+  int Width(unsigned char c);
   void Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground);
   void Fill(int x1, int y1, int x2, int y2, eDvbColor Color);
   void Clear(void);
diff --git a/interface.c b/interface.c
index 3e1232f0..c0554ca3 100644
--- a/interface.c
+++ b/interface.c
@@ -4,10 +4,11 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: interface.c 1.26 2000/10/29 12:53:55 kls Exp $
+ * $Id: interface.c 1.27 2000/11/01 11:25:25 kls Exp $
  */
 
 #include "interface.h"
+#include <ctype.h>
 #include <unistd.h>
 
 cInterface *Interface = NULL;
@@ -16,6 +17,7 @@ cInterface::cInterface(int SVDRPport)
 {
   open = 0;
   cols[0] = 0;
+  width = height = 0;
   keyFromWait = kNone;
   rcIo = NULL;
   SVDRP = NULL;
@@ -40,15 +42,17 @@ cInterface::~cInterface()
 void cInterface::Open(int NumCols, int NumLines)
 {
   if (!open++)
-     cDvbApi::PrimaryDvbApi->Open(NumCols, NumLines);
+     cDvbApi::PrimaryDvbApi->Open(width = NumCols, height = NumLines);
 }
 
 void cInterface::Close(void)
 {
   if (open == 1)
      Clear();
-  if (!--open)
+  if (!--open) {
      cDvbApi::PrimaryDvbApi->Close();
+     width = height = 0;
+     }
 }
 
 unsigned int cInterface::GetCh(bool Wait, bool *Repeat, bool *Release)
@@ -120,6 +124,74 @@ void cInterface::SetCols(int *c)
       }
 }
 
+char *cInterface::WrapText(const char *Text, int Width, int *Height)
+{
+  // Wraps the Text to make it fit into the area defined by the given Width
+  // (which is given in character cells).
+  // The actual number of lines resulting from this operation is returned in
+  // Height.
+  // The returned string is newly created on the heap and the caller
+  // is responsible for deleting it once it is no longer used.
+  // Wrapping is done by inserting the necessary number of newline
+  // characters into the string.
+
+  int Lines = 1;
+  char *t = strdup(Text);
+  char *Blank = NULL;
+  char *Delim = NULL;
+  int w = 0;
+
+  Width *= cDvbApi::PrimaryDvbApi->CellWidth();
+
+  while (*t && t[strlen(t) - 1] == '\n')
+        t[strlen(t) - 1] = 0; // skips trailing newlines
+
+  for (char *p = t; *p; ) {
+      if (*p == '\n') {
+         Lines++;
+         w = 0;
+         Blank = Delim = NULL;
+         p++;
+         continue;
+         }
+      else if (isspace(*p))
+         Blank = p;
+      int cw = cDvbApi::PrimaryDvbApi->Width(*p);
+      if (w + cw > Width) {
+         if (Blank) {
+            *Blank = '\n';
+            p = Blank;
+            continue;
+            }
+         else {
+            // Here's the ugly part, where we don't have any whitespace to
+            // punch in a newline, so we need to make room for it:
+            if (Delim)
+               p = Delim + 1; // let's fall back to the most recent delimiter
+            char *s = new char[strlen(t) + 2]; // The additional '\n' plus the terminating '\0'
+            int l = p - t;
+            strncpy(s, t, l);
+            s[l] = '\n';
+            strcpy(s + l + 1, p);
+            delete t;
+            t = s;
+            p = t + l;
+            continue;
+            }
+         }
+      else
+         w += cw;
+      if (strchr("-.,:;!?_", *p)) {
+         Delim = p;
+         Blank = NULL;
+         }
+      p++;
+      }
+
+  *Height = Lines;
+  return t;
+}
+
 void cInterface::Write(int x, int y, const char *s, eDvbColor FgColor, eDvbColor BgColor)
 {
   if (open)
@@ -154,7 +226,7 @@ void cInterface::WriteText(int x, int y, const char *s, eDvbColor FgColor, eDvbC
 
 void cInterface::Title(const char *s)
 {
-  int x = (MenuColumns - strlen(s)) / 2;
+  int x = (Width() - strlen(s)) / 2;
   if (x < 0)
      x = 0;
   ClearEol(0, 0, clrCyan);
@@ -203,7 +275,7 @@ bool cInterface::Confirm(const char *s)
 void cInterface::HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor)
 {
   if (open && Text) {
-     const int w = MenuColumns / 4;
+     const int w = Width() / 4;
      int l = (w - strlen(Text)) / 2;
      if (l < 0)
         l = 0;
@@ -460,7 +532,7 @@ eKeys cInterface::DisplayDescription(const cEventInfo *EventInfo)
 
 int cInterface::WriteParagraph(int Line, const char *Text)
 {
-  if (Line < MenuLines && Text) {
+  if (Line < Height() && Text) {
      Line++;
      char *s = strdup(Text);
      char *pStart = s, *pEnd;
@@ -479,7 +551,7 @@ int cInterface::WriteParagraph(int Line, const char *Text)
            //XXX need to scroll if text is longer
            *pEnd = 0;
            Write(1, Line++, pStart, clrCyan);
-           if (Line >= MenuLines)
+           if (Line >= Height())
               return Line;
            pStart = pEnd + 1;
            }
diff --git a/interface.h b/interface.h
index 684adfec..2c9344cc 100644
--- a/interface.h
+++ b/interface.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: interface.h 1.17 2000/10/29 12:32:12 kls Exp $
+ * $Id: interface.h 1.18 2000/11/01 11:18:23 kls Exp $
  */
 
 #ifndef __INTERFACE_H
@@ -19,6 +19,7 @@ class cInterface {
 public:
   enum { MaxCols = 5 };
 private:
+  int width, height;
   int open;
   int cols[MaxCols];
   eKeys keyFromWait;
@@ -35,11 +36,14 @@ public:
   ~cInterface();
   void Open(int NumCols = MenuColumns, int NumLines = MenuLines);
   void Close(void);
+  int Width(void) { return width; }
+  int Height(void) { return height; }
   eKeys GetKey(bool Wait = true);
   void PutKey(eKeys Key);
   void Clear(void);
   void ClearEol(int x, int y, eDvbColor Color = clrBackground);
   void SetCols(int *c);
+  char *WrapText(const char *Text, int Width, int *Height);
   void Write(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
   void WriteText(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
   void Title(const char *s);
diff --git a/menu.c b/menu.c
index d5b0a32f..06d3dd4e 100644
--- a/menu.c
+++ b/menu.c
@@ -4,11 +4,10 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menu.c 1.37 2000/10/29 11:23:33 kls Exp $
+ * $Id: menu.c 1.38 2000/11/01 11:45:05 kls Exp $
  */
 
 #include "menu.h"
-#include <ctype.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -727,7 +726,107 @@ eOSState cMenuChannels::ProcessKey(eKeys Key)
   return state;
 }
 
-// --- cMenuSummary --------------------------------------------------------
+// --- cMenuTextItem ---------------------------------------------------------
+
+class cMenuTextItem : public cOsdItem {
+private:
+  char *text;
+  int x, y, w, h, lines, offset;
+  eDvbColor fgColor, bgColor;
+public:
+  cMenuTextItem(const char *Text, int X, int Y, int W, int H = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
+  ~cMenuTextItem();
+  int Height(void) { return h; }
+  void Clear(void);
+  virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
+  bool CanScrollUp(void) { return offset > 0; }
+  bool CanScrollDown(void) { return h + offset < lines; }
+  void ScrollUp(void);
+  void ScrollDown(void);
+  virtual eOSState ProcessKey(eKeys Key);
+  };
+
+cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor)
+{
+  x = X;
+  y = Y;
+  w = W;
+  h = H;
+  fgColor = FgColor;
+  bgColor = BgColor;
+  offset = 0;
+  text = Interface->WrapText(Text, w - 1, &lines);
+  if (h < 0)
+     h = lines;
+}
+
+cMenuTextItem::~cMenuTextItem()
+{
+  delete text;
+}
+
+void cMenuTextItem::Clear(void)
+{
+  cDvbApi::PrimaryDvbApi->Fill(x, y, w, h, bgColor);
+}
+
+void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
+{
+  int l = 0;
+  char *t = text;
+  while (*t) {
+        char *n = strchr(t, '\n');
+        if (l >= offset) {
+           if (n)
+              *n = 0;
+           Interface->Write(x, y + l - offset, t, fgColor, bgColor);
+           if (n)
+              *n = '\n';
+           else
+              break;
+           }
+        if (!n)
+           break;
+        t = n + 1;
+        if (++l >= h + offset)
+           break;
+        }
+  // scroll indicators use inverted color scheme!
+  if (CanScrollUp())   Interface->Write(x + w - 1, y,         "^", bgColor, fgColor);
+  if (CanScrollDown()) Interface->Write(x + w - 1, y + h - 1, "v", bgColor, fgColor);
+}
+
+void cMenuTextItem::ScrollUp(void)
+{
+  if (CanScrollUp()) {
+     Clear();
+     offset--;
+     Display();
+     }
+}
+
+void cMenuTextItem::ScrollDown(void)
+{
+  if (CanScrollDown()) {
+     Clear();
+     offset++;
+     Display();
+     }
+}
+
+eOSState cMenuTextItem::ProcessKey(eKeys Key)
+{
+  switch (Key) {
+    case kUp|k_Repeat:
+    case kUp:            ScrollUp();   break;
+    case kDown|k_Repeat:
+    case kDown:          ScrollDown(); break;
+    default:             return osUnknown;
+    }
+  return osContinue;
+}
+
+// --- cMenuSummary ----------------------------------------------------------
 
 class cMenuSummary : public cOsdMenu {
 public:
@@ -738,29 +837,7 @@ public:
 cMenuSummary::cMenuSummary(const char *Text)
 :cOsdMenu("Summary")
 {
-  while (*Text) {
-        char line[MenuColumns + 1];
-        char *p = line;
-        const char *b = NULL;
-        *p++ = ' ';
-        while (*Text && p - line < MenuColumns - 2) {
-              if (isspace(*Text))
-                 b = Text; // remember the blank
-              if (*Text == '\n')
-                 break;
-              *p++ = *Text++;
-              }
-        if (*Text) {
-           if (b && Text - b > 0) {
-              p -= Text - b;
-              Text = b + 1;
-              }
-           else
-              Text++;
-           }
-        *p = 0;
-        Add(new cOsdItem(line, osBack));
-        }
+  Add(new cMenuTextItem(Text, 1, 2, MenuColumns - 2, MAXOSDITEMS));
 }
 
 eOSState cMenuSummary::ProcessKey(eKeys Key)
@@ -975,84 +1052,55 @@ eOSState cMenuTimers::ProcessKey(eKeys Key)
   return state;
 }
 
-// --- cMenuEventItem --------------------------------------------------------
-
-class cMenuEventItem : public cOsdItem {
-public:
-  cMenuEventItem(const char *Text);
-};
-
-cMenuEventItem::cMenuEventItem(const char *Text)
-:cOsdItem(Text, osBack)
-{
-}
-
 // --- cMenuEvent ------------------------------------------------------------
 
 class cMenuEvent : public cOsdMenu {
 private:
-  void AddParagraph(const char *text);
   const cEventInfo *eventInfo;
 public:
   cMenuEvent(const cEventInfo *EventInfo, bool CanSwitch = false);
+  virtual eOSState ProcessKey(eKeys Key);
 };
 
 cMenuEvent::cMenuEvent(const cEventInfo *EventInfo, bool CanSwitch)
-:cOsdMenu("Event", 1)
+:cOsdMenu("Event")
 {
-  const char *p;
-  char buffer[MenuColumns + 1];
-
   eventInfo = EventInfo;
-
-  cChannel *channel = Channels.GetByServiceID(eventInfo->GetServiceID());
-
-  snprintf(buffer, sizeof(buffer), "\t%-17.*s %.*s  %s - %s", 17, channel->name, 5, eventInfo->GetDate(), eventInfo->GetTimeString(), eventInfo->GetEndTimeString());
-  Add(new cMenuEventItem(buffer));
-  if ((p = eventInfo->GetTitle()) != NULL && *p) {
-     Add(new cMenuEventItem(""));
-     AddParagraph(p);
-     }
-  if ((p = eventInfo->GetSubtitle()) != NULL && *p) {
-     Add(new cMenuEventItem(""));
-     AddParagraph(p);
-     }
-  if ((p = eventInfo->GetExtendedDescription()) != NULL && *p) {
-     Add(new cMenuEventItem(""));
-     AddParagraph(p);
+  if (eventInfo) {
+     cChannel *channel = Channels.GetByServiceID(eventInfo->GetServiceID());
+     if (channel) {
+        const char *p;
+        char *buffer;
+        asprintf(&buffer, "%-17.*s %.*s  %s - %s", 17, channel->name, 5, eventInfo->GetDate(), eventInfo->GetTimeString(), eventInfo->GetEndTimeString());
+        SetTitle(buffer, false);
+        int Line = 2;
+        cMenuTextItem *item;
+        if (!isempty(p = eventInfo->GetTitle())) {
+           Add(item = new cMenuTextItem(p, 1, Line, MenuColumns - 2, -1, clrCyan));
+           Line += item->Height() + 1;
+           }
+        if (!isempty(p = eventInfo->GetSubtitle())) {
+           Add(item = new cMenuTextItem(p, 1, Line, MenuColumns - 2, -1, clrYellow));
+           Line += item->Height() + 1;
+           }
+        if (!isempty(p = eventInfo->GetExtendedDescription()))
+           Add(new cMenuTextItem(p, 1, Line, MenuColumns - 2, Height() - Line - 2, clrCyan), true);
+        SetHelp("Record", NULL, NULL, CanSwitch ? "Switch" : NULL);
+        }
      }
-  SetHelp("Record", NULL, NULL, CanSwitch ? "Switch" : NULL);
 }
 
-void cMenuEvent::AddParagraph(const char *text)
+eOSState cMenuEvent::ProcessKey(eKeys Key)
 {
-  char *ptextsave = strdup(text);
-
-  if (ptextsave) {
-
-     int column = 1;
-     char buffer[MenuColumns + 1];
-     char *pStart = ptextsave;
-     char *pEndText = &ptextsave[strlen(text) - 1];
+  eOSState state = cOsdMenu::ProcessKey(Key);
 
-     while (pStart < pEndText) {
-           char *pEnd;
-           if (strlen(pStart) > (unsigned)(MenuColumns - column - 2))
-              pEnd = &pStart[MenuColumns - column - 2];
-           else
-              pEnd = &pStart[strlen(pStart)];
-               
-           while (*pEnd && *pEnd != ' ' && pEnd > pStart)
-                 pEnd--;
-       
-           *pEnd = 0;
-           sprintf(buffer, "\t%s", pStart);
-           Add(new cMenuEventItem(buffer));
-           pStart = pEnd + 1;
-           }
+  if (state == osUnknown) {
+     switch (Key) {
+       case kOk:     return osBack;
+       default: break;
+       }
      }
-
-  delete ptextsave;
+  return state;
 }
 
 // --- cMenuWhatsOnItem ------------------------------------------------------
@@ -1266,6 +1314,8 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
        default:      break;
        }
      }
+  else if (!HasSubMenu())
+     now = next = false;
   return state;
 }
 
diff --git a/osd.c b/osd.c
index 3225e681..f3458274 100644
--- a/osd.c
+++ b/osd.c
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: osd.c 1.10 2000/10/28 09:33:47 kls Exp $
+ * $Id: osd.c 1.11 2000/11/01 11:21:51 kls Exp $
  */
 
 #include "osd.h"
@@ -170,7 +170,8 @@ void cOsdMenu::Display(void)
             break;
          }
      }
-  Interface->Status(status);
+  if (!isempty(status))
+     Interface->Status(status);
 }
 
 void cOsdMenu::RefreshCurrent(void)
diff --git a/osd.h b/osd.h
index 5e2ad300..2b9359a6 100644
--- a/osd.h
+++ b/osd.h
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: osd.h 1.12 2000/10/28 09:32:59 kls Exp $
+ * $Id: osd.h 1.13 2000/11/01 11:20:15 kls Exp $
  */
 
 #ifndef __OSD_H
@@ -50,7 +50,7 @@ public:
   void SetText(const char *Text, bool Copy = true);
   void SetColor(eDvbColor FgColor, eDvbColor BgColor = clrBackground);
   const char *Text(void) { return text; }
-  void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
+  virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
   virtual void Set(void) {}
   virtual eOSState ProcessKey(eKeys Key);
   };
@@ -90,6 +90,8 @@ protected:
 public:
   cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0);
   virtual ~cOsdMenu();
+  int Width(void) { return Interface->Width(); }
+  int Height(void) { return Interface->Height(); }
   int Current(void) { return current; }
   void Add(cOsdItem *Item, bool Current = false);
   void Display(void);
-- 
cgit v1.2.3