From 6094765d94e4caaf0813039dff826b731f277753 Mon Sep 17 00:00:00 2001
From: lordjaxom <lordjaxom>
Date: Sat, 5 Jun 2004 18:06:22 +0000
Subject: - added scrollable texts and "SymbolScrollUp" and "SymbolScrollDown"
 - added "MenuText", "MenuEventTitle", "MenuEventShortText",  
 "MenuEventDescription", "MenuEventTime", "MenuRecording",  
 "SymbolEventRunning", "SymbolEventTimer" and "SymbolEventVPS" - implemented
 image caching - added english and german README - removed some workarounds,
 and added a patch to vdr to the tree (will be   included in 1.3.10) - fixed
 two bugs when displaying replay symbols - implemented tabbed texts in menu

---
 HISTORY                    |  13 +++
 Makefile                   |  27 ++++---
 README                     |  92 +++++++++++++++++++++-
 README.de                  | 101 ++++++++++++++++++++++++
 SKINS                      | 133 +++++++++++++++++++++++++++++++
 bitmap.c                   |  53 +++++++------
 bitmap.h                   |  17 +++-
 cache.c                    |   6 ++
 cache.h                    | 124 +++++++++++++++++++++++++++++
 common.c                   |  69 +---------------
 common.h                   |  10 ++-
 data.c                     | 113 ++++++++------------------
 data.h                     |  16 +++-
 display.c                  |  46 ++++++++++-
 display.h                  |   4 +-
 i18n.c                     |  18 +++--
 loader.c                   |   7 +-
 patches/vdr-1.3.9-osd.diff | 106 +++++++++++++++++++++++++
 render.c                   | 192 ++++++++++++++++++++++++++++++++++-----------
 render.h                   |  23 ++++--
 text2skin.c                |  23 ++----
 text2skin.h                |  27 +++++++
 22 files changed, 940 insertions(+), 280 deletions(-)
 create mode 100644 README.de
 create mode 100644 cache.c
 create mode 100644 cache.h
 create mode 100644 patches/vdr-1.3.9-osd.diff
 create mode 100644 text2skin.h

diff --git a/HISTORY b/HISTORY
index 13b6a63..afa09ac 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,19 @@
 VDR Plugin 'text2skin' Revision History
 ---------------------------------------
 
+2004-06-05: Version 0.0.1
+
+- added scrollable texts and "SymbolScrollUp" and "SymbolScrollDown"
+- added "MenuText", "MenuEventTitle", "MenuEventShortText", 
+  "MenuEventDescription", "MenuEventTime", "MenuRecording", 
+  "SymbolEventRunning", "SymbolEventTimer" and "SymbolEventVPS"
+- implemented image caching
+- added english and german README
+- removed some workarounds, and added a patch to vdr to the tree (will be 
+  included in 1.3.10)
+- fixed two bugs when displaying replay symbols
+- implemented tabbed texts in menu
+
 2004-06-02: Version 0.0.1-rc4
 
 - implemented image loading through ImageMagick (fixes crashes when running 
diff --git a/Makefile b/Makefile
index 3da420d..65e16be 100644
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,16 @@
-#
-# Makefile for a Video Disk Recorder plugin
-#
-# $Id: Makefile,v 1.5 2004/06/02 20:43:05 lordjaxom Exp $
+# exchange the comments on the following to lines if you would like to use
+# Imlib2 for loading images. BEWARE that you can not use GraphTFT together with
+# Text2Skin if you use Imlib2! (That's why I actually implemented ImageMagick)
 
-# disable in case you don't want to install imlib
-# in that case, you will not be able to load other files than simple xpms
+HAVE_IMAGEMAGICK=1
 # HAVE_IMLIB2=1
 
-# disable in case you don't want to install ImageMagick
-# in that case, you will not be able to load other files than simple xpms
-HAVE_IMAGEMAGICK=1
+
+# DO NOT EDIT BELOW THIS LINE UNLESS YOU KNOW WHAT YOU'RE DOING
+# -------------------------------------------------------------
+#
+# $Id: Makefile,v 1.7 2004/06/05 16:52:44 lordjaxom Exp $
+#
 
 # The official name of this plugin.
 # This name will be used in the '-P...' option of VDR to load the plugin.
@@ -19,7 +20,7 @@ PLUGIN = text2skin
 
 ### The version number of this plugin (taken from the main source file):
 
-VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+VERSION = $(shell grep 'const char \*cText2SkinPlugin::VERSION *=' $(PLUGIN).c | awk '{ print $$5 }' | sed -e 's/[";]//g')
 
 ### The C++ compiler and options:
 
@@ -58,6 +59,10 @@ ifdef HAVE_IMAGEMAGICK
 	LIBS += -lMagick++
 endif
 
+ifdef DEBUG
+	DEFINES += -DDEBUG
+endif
+
 INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include
 
 DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
@@ -65,7 +70,7 @@ DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
 ### The object files (add further files here):
 
 OBJS = $(PLUGIN).o loader.o data.o display.o render.o common.o bitmap.o \
-       file.o i18n.o theme.o
+       file.o i18n.o theme.o cache.o
 
 ### Implicit rules:
 
diff --git a/README b/README
index 4e04b83..16c11f3 100644
--- a/README
+++ b/README
@@ -1,11 +1,97 @@
 This is a "plugin" for the Video Disk Recorder (VDR).
 
-Written by:                  Your Name <email@host.dom>
+Written by:                  Sascha Volkenandt <sascha@akv-soft.de>
 
-Project's homepage:          URL
+Project's homepage:          http://www.magoa.net/linux/contrib/
 
-Latest version available at: URL
+Latest version available at: http://www.magoa.net/linux/contrib/
 
 See the file COPYING for license information.
 
+
 Description:
+------------
+
+This plugin is designed to load and interpret a set of files describing the
+layout of the On Screen Display and to make this "Skin" available to VDR via
+Setup -> OSD in the main menu. Of course it is possible to load more than one
+text-based skin this way and to choose between them while running VDR. All 
+skins may be themeable (you can create your own color-theme) and translateable
+as the author of the skin wishes.
+
+
+Prerequisites:
+--------------
+
+For loading images in format other than simple XPM, you will need an image 
+library. You can choose between two supported libraries, ImageMagick or Imlib2,
+from which the first one is the default. You can specify which library to use
+(if any) in the first few lines of the Makefile. Here is an overview of the
+advantages and drawbacks of each solution:
+
+No library
+ - you can only load XPM files
+ - XPMs don't support partial transparency / alpha channels
+
+ImageMagick 
+ + you can load many different image types
+ - is a but slower than Imlib2
+
+Imlib2
+ + you can load many different image types
+ - CRASHES WHEN USED TOGETHER WITH THE GRAPHTFT-PLUGIN!
+
+Using both libraries at the same time doesn't make sense anyway.
+
+HINT: Although the manual of ImageMagick claims that the used library Magick++
+is part of the source distribution, some binary distributions may have to 
+install Magick++ separately.
+
+
+Installation:
+-------------
+
+Install text2skin like any other plugin. In this example I assume that you have
+changed to the folder where the VDR sourcecode is located, and that it is 
+version 0.0.1 of the plugin you wish to install.
+
+root@linux # cd PLUGINS/src
+root@linux # wget http://www.magoa.net/linux/contrib/vdr-text2skin-0.0.1.tgz
+root@linux # tar -xfz vdr-text2skin-0.0.1.tgz
+root@linux # ln -s text2skin-0.0.1 text2skin
+root@linux # cd ../..
+root@linux # make plugins
+root@linux # ./vdr -P text2skin
+
+If you are using VDR 1.3.9, you also have to apply a patch to the sources. This
+patch will be included in VDR 1.3.10.
+
+root@linux # patch -p1 < PLUGINS/src/text2skin/patches/vdr-1.3.9-osd.diff
+root@linux # make vdr
+root@linux # ./vdr -P text2skin
+
+
+Where to put the skins:
+-----------------------
+
+As you might know, VDR has a subfolder "plugins" inside it's configuration 
+folder, where all plugin-related files should reside. If you don't know, where
+this could be, look into the folder you gave to VDR with the -v parameter 
+(or the -c parameter, if that was given). "plugins" should be inside that 
+folder.
+
+Inside that "plugins" folder, create a subfolder called "text2skin". Inside
+"text2skin", create one folder for each skin. These skin-folders must have the
+same names as the skins residing in them.  Each skin must at least have a file
+carrying the same name, but ending in ".skin". Example:
+
+/video0/plugins/text2skin/demo/demo.skin
+/video0/plugins/text2skin/skin2/skin2.skin
+...
+
+The other files inside the skin-folder are additional description files (for
+Themeing and Translation), images, logos and symbols.
+
+If you download a skin, you usually just change to plugins/text2skin and unpack
+it there.
+
diff --git a/README.de b/README.de
new file mode 100644
index 0000000..49a9945
--- /dev/null
+++ b/README.de
@@ -0,0 +1,101 @@
+Dies ist ein "plugin" f�r den Video Disk Recorder (VDR).
+
+Geschrieben von:               Sascha Volkenandt <sascha@akv-soft.de>
+
+Projekthomepage:               http://www.magoa.net/linux/contrib/
+
+Letzte Version verf�gbar auf: http://www.magoa.net/linux/contrib/
+
+Siehe COPYING f�r Linzensierungsinformationen.
+
+
+Beschreibung:
+-------------
+
+Dieses Plugin wurde designed um eine Reihe von Dateien zu laden und zu 
+interpretieren, die das Aussehen des On Screen Display beschreiben, und diese 
+dem VDR via Einstellungen -> OSD zur Verf�gung zu stellen. Nat�rlich ist es
+m�glich mehrere textbasierte Skins auf diesem Wege zu laden, und unter diesen
+bei laufendem VDR auszuw�hlen. Alle Skins k�nnen Themeable (die Farbvarianten
+k�nnen ge�ndert werden) und �bersetzbar sein, wenn der Autor des Skins dies
+m�chte.
+
+
+Voraussetzungen:
+----------------
+
+Um andere Bildformate als einfaches XPM zu laden, brauchen Sie eine 
+Grafikbibliothek. Es werden zwei Bibliotheken unterst�tzt, aus der Sie eine
+zu benutzende w�hlen k�nnen. Dies sind ImageMagick oder Imlib2, wobei erstere
+standardm��ig herangezogen wird. Sie k�nnen beeinflussen, welche Bibliothek
+(wenn �berhaupt eine) genutzt werden soll, indem Sie die ersten paar Zeilen
+der Datei Makefile �ndern. Hier ist eine �bersicht �ber M�glichkeiten und 
+Nachteile jeder m�glichen L�sung:
+
+Keine Bibliothek
+ - Sie k�nnen nur XPM Dateien laden
+ - XPMs haben weder Teiltransparenzen noch Alphakan�le
+ 
+ImageMagick
+ + Sie k�nnen viele verschiedene Bildformate laden
+ - ist aber etwas langsamer als Imlib2
+
+Imlib2
+ + Sie k�nnen viele verschiedene Bildformate laden
+ - ST�RZT AB, WENN ES ZUSAMMEN MIT DEM GRAPHTFT-PLUGIN ZUM EINSATZ KOMMT!
+
+Beide Bibliotheken gleichzeitig zu benutzen macht keinen Sinn.
+
+HINWEIS: Obwohl das Handbuch von ImageMagick behauptet, dass die hier benutzte
+Bibliothek Magick++ integraler Bestandteil der Quellen sind, muss auf manchen
+Distributionen das Paket Magick++ zus�tzlich installiert werden.
+
+
+Installation:
+-------------
+
+Installieren Sie text2skin wie jedes andere Plugin. In diesem Beispiel nehme
+ich an Sie sind in den Ordner gewechselt, in dem VDR's Quelltext liegt, und
+Sie wollen Version 0.0.1 des Plugins installieren.
+
+root@linux # cd PLUGINS/src
+root@linux # wget http://www.magoa.net/linux/contrib/vdr-text2skin-0.0.1.tgz
+root@linux # tar -xfz vdr-text2skin-0.0.1.tgz
+root@linux # ln -s text2skin-0.0.1 text2skin
+root@linux # cd ../..
+root@linux # make plugins
+root@linux # ./vdr -P text2skin
+
+Wenn Sie VDR 1.3.9 benutzen, m�ssen Sie auch noch einen Patch anwenden. Dieser
+Patch wird in VDR 1.3.10 aber enthalten sein.
+
+root@linux # patch -p1 < PLUGINS/src/text2skin/patches/vdr-1.3.9-osd.diff
+root@linux # make vdr
+root@linux # ./vdr -P text2skin
+
+
+Wo die Skins hingeh�ren:
+------------------------
+
+Wie Sie vielleicht wissen, hat VDR einen Unterordner "plugins" innerhalb seines
+Konfigurationsordners, in dem alle Dateien, die zu Plugins geh�ren, enthalten
+sein sollten. Wenn Sie nicht wissen, wo das sein k�nnte, schauen Sie in dem
+Ordner nach, den Sie VDR beim Starten mit -v (oder -c, falls gegeben) �bergeben
+haben. "plugins" sollte in diesem Ordner enthalten sein.
+
+Innerhalb dieses "plugins" Ordners erstellen Sie einen Ordner namens 
+"text2skin". In "text2skin" erstellen Sie einen weiteren Ordner pro Skin. Diese
+Skin-Ordner m�ssen den gleichen Namen tragen wie die Skins in ihnen. Jedes 
+Skin muss mindestens eine Datei enthalten, die denselben Namen tr�gt, aber mit
+der Endung ".skin". Beispiel:
+
+/video0/plugins/text2skin/demo/demo.skin
+/video0/plugins/text2skin/skin2/skin2.skin
+...
+
+Die anderen Dateien in dem Skin-Ordner sind weitere Beschreibungsdateien (f�r
+Farben und �bersetzungen) sowie Bilder, Logos und Symbole.
+
+Wenn Sie ein Skin herunterladen, wechseln Sie normalerweise einfach in den
+plugins/text2skin Ordner und entpacken es dort.
+
diff --git a/SKINS b/SKINS
index cd59fd1..ef2de74 100644
--- a/SKINS
+++ b/SKINS
@@ -261,6 +261,84 @@ Description: Draws the specified image into the specified location if the
              image handling, see Item=Background.
 Parameters:  x, y, width, height, path, altpath, fg, bg
 
+Item:        Item=SymbolPlay
+Description: Draws the specified image into the specified location if the 
+             current replay is playing normally. If that is not the case, the 
+             alternative image (if given) will be displayed. For details on the
+             image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolPause
+Description: Draws the specified image into the specified location if the 
+             current replay is paused. If that is not the case, the 
+             alternative image (if given) will be displayed. For details on the
+             image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolFastFwd
+Description: Draws the specified image into the specified location if the 
+             current replay is fast forwarding. If that is not the case, the 
+             alternative image (if given) will be displayed. For details on the
+             image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolFastRew
+Description: Draws the specified image into the specified location if the 
+             current replay is fast rewinding. If that is not the case, the 
+             alternative image (if given) will be displayed. For details on the
+             image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolSlowFwd
+Description: Draws the specified image into the specified location if the 
+             current replay is slow forwarding. If that is not the case, the 
+             alternative image (if given) will be displayed. For details on the
+             image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolSlowRew
+Description: Draws the specified image into the specified location if the 
+             current replay is slow rewinding. If that is not the case, the 
+             alternative image (if given) will be displayed. For details on the
+             image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolEventRunning
+Description: Draws the specified image into the specified location if the 
+             displayed event (in menu display) is currently running. If that 
+             is not the case, the alternative image (if given) will be 
+             displayed. For details on the image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolEventTimer
+Description: Draws the specified image into the specified location if the 
+             displayed event (in menu display) will be recorded. If that is 
+             not the case, the alternative image (if given) will be displayed. 
+             For details on the image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolEventVPS
+Description: Draws the specified image into the specified location if the 
+             displayed event (in menu display) is VPS. If that is not the 
+             case, the alternative image (if given) will be displayed. For 
+             details on the image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolScrollUp
+Description: Draws the specified image into the specified location if the 
+             current display is scrollable and not at the top of its contents.
+             If that is not the case, the alternative image (if given) will be 
+             displayed. For details on the image handling, see Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
+Item:        Item=SymbolScrollDown
+Description: Draws the specified image into the specified location if the 
+             current display is scrollable and not at the bottom of its 
+             contents. If that is not the case, the alternative image (if 
+             given) will be displayed. For details on the image handling, see 
+             Item=Background.
+Parameters:  x, y, width, height, path, altpath, fg, bg
+
 Item:        Item=Language
 Description: Draws a logo for the current language (currently the only 
              languages VDR knows are "Audio 1" and possibly "Audio 2", 
@@ -317,6 +395,61 @@ Item:        Item=MessageError
 Description: Draws the current error message (if present).
 Parameters:  x, y, width, height, fg, font, align, text
 
+Item:        Item=MenuArea
+Description: Specifies the area in which the menu items will be shown as a
+             list.
+Parameters:  x, y, width, height
+
+Item:        Item=MenuItem
+Description: Specifies how one item is drawn in the list of menu items.
+             list.
+Parameters:  [x,] [y,] width, height, fg, bg, font, align
+
+Item:        Item=MenuCurrent
+Description: Specifies how the currently selected item is drawn in the list of 
+             menu items.
+             list.
+Parameters:  [x,] [y,] width, height, fg, bg, font, align
+
+Item:        Item=MenuTitle
+Description: Draws the title line of the displayed menu.
+Parameters:  x, y, width, height, fg, font, align, text
+
+Item:        Item=MenuRed, Item=MenuGreen, Item=MenuYellow, Item=MenuBlue
+Description: Draws the red, green, yellow or blue (respectively) button.
+Parameters:  x, y, width, height, fg, font, align, text
+
+Item:        Item=MenuText
+Description: Draws the current menu text in a scrollable text-area. If this
+             item is present, SymbolScrollUp and SymbolScrollDown apply, too.
+Parameters:  x, y, width, height, fg, [bg,] font, [align,] text
+
+Item:        Item=MenuEventTitle
+Description: Draws the title of the currently selected event when viewing EPG
+             entries.
+Parameters:  x, y, width, height, fg, font, align, text
+
+Item:        Item=MenuEventShortText
+Description: Draws the short text or episode name of the currently selected 
+             event when viewing EPG entries.
+Parameters:  x, y, width, height, fg, font, align, text
+
+Item:        Item=MenuEventDescription
+Description: Draws the long text of the currently selected event when viewing 
+             EPG entries in a scrollable text-area. If this item is present,
+             SymbolScrollUp and SymbolScrollDown apply, too.
+Parameters:  x, y, width, height, fg, [bg,] font, [align,] text
+
+Item:        Item=MenuEventTime
+Description: Draws the start time of the currently selected event when viewing 
+             EPG entries.
+Parameters:  x, y, width, height, fg, font, align, text
+
+Item:        Item=MenuRecording
+Description: Draws the summary of the currently selected recording when 
+             browsing recordings in a scrollable text-area. If this item is 
+             present, SymbolScrollUp and SymbolScrollDown apply, too.
+Parameters:  x, y, width, height, fg, [bg,] font, [align,] text
 
 Known Parameters
 ----------------
diff --git a/bitmap.c b/bitmap.c
index 185f435..1d9d6f5 100644
--- a/bitmap.c
+++ b/bitmap.c
@@ -1,5 +1,5 @@
 /*
- * $Id: bitmap.c,v 1.9 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: bitmap.c,v 1.11 2004/06/05 16:52:44 lordjaxom Exp $
  */
 
 #include "bitmap.h"
@@ -12,40 +12,46 @@
 #include <Magick++.h>
 #endif
 
-cText2SkinBitmap::cText2SkinBitmap(void): cBitmap(1, 1, 1) {
-#ifdef HAVE_IMLIB2
-  imlib_set_cache_size(4096 * 1024);
-#endif
+template<> 
+void cImageCache::Delete(string &key, cText2SkinBitmap *&value) {
+	delete value;
 }
 
-cText2SkinBitmap::cText2SkinBitmap(const char *Filename): cBitmap(1, 1, 1) {
-#ifdef HAVE_IMLIB2
-  imlib_set_cache_size(4096 * 1024);
-#endif
-	Load(Filename);
+cImageCache cText2SkinBitmap::mCache(10);
+
+cText2SkinBitmap::cText2SkinBitmap(void): cBitmap(1, 1, 1) {
 }
 
 cText2SkinBitmap::~cText2SkinBitmap() {
 }
 
-bool cText2SkinBitmap::Load(const char *Filename) {
-	int len = strlen(Filename);
-	if (len > 4) {
-		if (strcmp(Filename + len - 4, ".xpm") == 0)
-			return LoadXpm(Filename);
+cText2SkinBitmap *cText2SkinBitmap::Load(const char *Filename) {
+	if (mCache.Contains(Filename)) {
+		return mCache[Filename];
+	} else {
+		cText2SkinBitmap *bmp = new cText2SkinBitmap;
+		int len = strlen(Filename);
+		bool result = false;
+		if (len > 4) {
+			if (strcmp(Filename + len - 4, ".xpm") == 0)
+				result = bmp->LoadXpm(Filename);
+			else {
 #ifdef HAVE_IMLIB2
-		else if (strcmp(Filename + len - 4, ".png") == 0)
-			return LoadImlib(Filename);
+				result = bmp->LoadImlib(Filename);
 #else
 #	ifdef HAVE_IMAGEMAGICK
-		else if (strcmp(Filename + len - 4, ".png") == 0)
-			return LoadMagick(Filename);
+				result = bmp->LoadMagick(Filename);
 #	endif
 #endif
-		else
-			esyslog("ERROR: text2skin: unknown file format for %s", Filename);
-	} else
-		esyslog("ERROR: text2skin: filename %s too short to identify format", Filename);
+			}
+			//else
+				//esyslog("ERROR: text2skin: unknown file format for %s", Filename);
+		} else
+			esyslog("ERROR: text2skin: filename %s too short to identify format", Filename);
+	
+		if (result)
+			return (mCache[Filename] = bmp);
+	}
 	return false;
 }
 
@@ -66,7 +72,6 @@ bool cText2SkinBitmap::LoadImlib(const char *Filename) {
 		for (int x = 0; x < Width(); ++x) {
 			tColor col = (data[pos + 3] << 24) | (data[pos + 2] << 16) | (data[pos + 1] << 8) | data[pos + 0];
 			int res = Index(col);
-			//printf("color: r=%d,g=%d,b=%d,a=%d\n", data[pos], data[pos+1], data[pos+2], data[pos+3]);
 			if (pal > 0 && res == 0)
 				;//esyslog("ERROR: text2skin: Too many colors used in palette");
 			else
diff --git a/bitmap.h b/bitmap.h
index b0dc35d..6028f0b 100644
--- a/bitmap.h
+++ b/bitmap.h
@@ -1,20 +1,29 @@
 /*
- * $Id: bitmap.h,v 1.6 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: bitmap.h,v 1.7 2004/06/05 01:39:36 lordjaxom Exp $
  */
 
 #ifndef VDR_TEXT2SKIN_BITMAP_H
 #define VDR_TEXT2SKIN_BITMAP_H
 
 #include "common.h"
+#include "cache.h"
 #include <vdr/osd.h>
 
+class cText2SkinBitmap;
+typedef cText2SkinCache<string,cText2SkinBitmap*> cImageCache;
+
 class cText2SkinBitmap: public cBitmap {
-public:
+private:
+	static cImageCache mCache;
+
+	// disallow direct construction
 	cText2SkinBitmap(void);
-	cText2SkinBitmap(const char *Filename);
+
+public:
+	static cText2SkinBitmap *Load(const char *Filename);
+	
 	virtual ~cText2SkinBitmap();
 
-	bool Load(const char *Filename);
 #ifdef HAVE_IMLIB2
 	bool LoadImlib(const char *Filename);
 #endif
diff --git a/cache.c b/cache.c
new file mode 100644
index 0000000..1182e9e
--- /dev/null
+++ b/cache.c
@@ -0,0 +1,6 @@
+/*
+ * $Id: cache.c,v 1.1 2004/06/05 01:40:13 lordjaxom Exp $
+ */
+
+#include "cache.h"
+
diff --git a/cache.h b/cache.h
new file mode 100644
index 0000000..93aff9f
--- /dev/null
+++ b/cache.h
@@ -0,0 +1,124 @@
+/*
+ * $Id: cache.h,v 1.1 2004/06/05 01:40:13 lordjaxom Exp $
+ */
+
+#ifndef VDR_TEXT2SKIN_CACHE_HPP
+#define VDR_TEXT2SKIN_CACHE_HPP
+
+#include "common.h"
+
+// template class generic cache
+
+template<class K,class D>
+class cText2SkinCache {
+private:
+	struct Item {
+		K      _key;
+		D      _data;
+		time_t _lastUsed;
+
+		Item  *_next;
+		Item  *_prev;
+
+		Item() { _next = _prev = NULL; }
+	};
+
+	typedef map <K,Item*> DataMap;
+
+	DataMap _items;
+	int     _maxItems;
+	Item   *_first;
+	Item   *_last;
+		
+	void Unlink(Item *item);
+	void Update(Item *item);
+	void Delete(Item *item);
+	void Delete(K &key, D &data);
+
+public:
+	cText2SkinCache(int maxItems);
+	~cText2SkinCache();
+
+	bool Contains(const K &key);
+	D &operator[](const K &key);
+};
+
+template<class K,class D>
+inline void cText2SkinCache<K,D>::Unlink(Item *item) {
+	if (item == _first) {
+		_first = item->_next;
+		if (_first)
+			_first->_prev = NULL;
+		else
+			_last = NULL;
+	} else if (item == _last) {
+		_last = item->_prev;
+		_last->_next = NULL;
+	} else {
+		item->_prev->_next = item->_next;
+		item->_next->_prev = item->_prev;
+	}
+}
+
+template<class K,class D>
+inline void cText2SkinCache<K,D>::Delete(Item *item) {
+	Delete(item->_key, item->_data);
+	delete item;
+}
+
+template<class K,class D>
+inline void cText2SkinCache<K,D>::Delete(K &key, D &Data) {
+}
+
+template<class K,class D>
+inline void cText2SkinCache<K,D>::Update(Item *item) {
+	item->_lastUsed = time_ms();
+	if (item->_next != NULL || item->_prev != NULL)
+		Unlink(item);
+
+	item->_next = NULL;
+	item->_prev = _last;
+	if (_last)
+		_last->_next = item;
+	_last = item;
+	if (!_first)
+		_first = item;
+
+	while ((int)_items.size() > _maxItems) {
+		Item *aged = _first;
+		_items.erase(aged->_key);
+		Unlink(aged);
+		Delete(aged);
+	}
+}
+
+template<class K,class D>
+inline bool cText2SkinCache<K,D>::Contains(const K &key) {
+	return (_items.find(key) != _items.end());
+}
+
+template<class K,class D>
+cText2SkinCache<K,D>::cText2SkinCache(int maxItems) {
+	_maxItems = maxItems;
+	_first = _last = NULL;
+}
+
+template<class K,class D>
+cText2SkinCache<K,D>::~cText2SkinCache() {
+}
+
+template<class K,class D>
+D &cText2SkinCache<K,D>::operator[](const K &key) {
+	Item *item;
+	if (Contains(key)) {
+		item = _items[key];
+	} else {
+		item = new Item;
+		item->_key = key;
+		_items[key] = item;
+	}
+	Update(item);
+	return item->_data;
+}
+
+#endif // VDR_TEXT2SKIN_CACHE_HPP
diff --git a/common.c b/common.c
index bd41565..0d203b5 100644
--- a/common.c
+++ b/common.c
@@ -1,5 +1,5 @@
 /*
- * $Id: common.c,v 1.6 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: common.c,v 1.7 2004/06/05 16:52:44 lordjaxom Exp $
  */
 
 #include "data.h"
@@ -10,73 +10,6 @@ const char *SkinPath(void) {
 	return cPlugin::ConfigDirectory(PLUGIN_NAME_I18N);
 }
 
-void DrawTextTransparent(cOsd *Osd, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment) {
-	int w = Font->Width(s);
-	int h = Font->Height();
-	int limit = 0;
-	if (Width || Height) {
-		int cw = Width ? Width : w;
-		limit = x + cw;
-		if (Width) {
-			 if ((Alignment & taLeft) != 0)
-					;
-			 else if ((Alignment & taRight) != 0) {
-					if (w < Width)
-						 x += Width - w;
-					}
-			 else { // taCentered
-					if (w < Width)
-						 x += (Width - w) / 2;
-					}
-			 }
-		if (Height) {
-			 if ((Alignment & taTop) != 0)
-					;
-			 else if ((Alignment & taBottom) != 0) {
-					if (h < Height)
-						 y += Height - h;
-					}
-			 else { // taCentered
-					if (h < Height)
-						 y += (Height - h) / 2;
-					}
-			 }
-		}
-	while (s && *s) {
-			 const cFont::tCharData *CharData = Font->CharData(*s++);
-			 if (limit && int(x + CharData->width) > limit)
-					break; // we don't draw partial characters
-			 if (int(x + CharData->width) > 0) {
-					for (int row = 0; row < h; row++) {
-							cFont::tPixelData PixelData = CharData->lines[row];
-							for (int col = CharData->width; col-- > 0; ) {
-									if (PixelData & 1)
-										Osd->DrawRectangle(x + col, y + row, x + col, y + row, ColorFg);
-									PixelData >>= 1;
-									}
-							}
-					}
-			 x += CharData->width;
-			 }
-}
-
-void DrawBitmap(cOsd *Osd, int x, int y, cBitmap &Bitmap, tColor ColorFg, tColor ColorBg) {
-	if (ColorFg || ColorBg) {
-		Bitmap.SetColor(0, ColorBg);
-		Bitmap.SetColor(1, ColorFg);
-	}
-	tColor fill = Bitmap.Color(*Bitmap.Data(0, 0)); // to be sure to grab a USED color
-	Osd->DrawRectangle(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1, fill); // to be sure the palette is reset, if the Bitmap covers an Area
- 	for (int iy = 0; iy < Bitmap.Height(); iy++) {
-		const tIndex *ptr = Bitmap.Data(0, iy);
-		for (int ix = 0; ix < Bitmap.Width(); ix++, ptr += sizeof(tIndex)) {
-			// DrawPixel is b0rked
-			//Osd->DrawPixel(x + ix, y + iy, Bitmap.Color(*ptr));
-			Osd->DrawRectangle(x + ix, y + iy, x + ix, y + iy, Bitmap.Color(*ptr));
-		}
-	}
-}
-
 const char *ChannelNumber(const cChannel *Channel, int Number) {
 	static char buffer[256];
 	buffer[0] = '\0';
diff --git a/common.h b/common.h
index 340b5e9..24a7f41 100644
--- a/common.h
+++ b/common.h
@@ -1,5 +1,5 @@
 /*
- * $Id: common.h,v 1.6 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: common.h,v 1.7 2004/06/05 16:52:44 lordjaxom Exp $
  */
 
 #ifndef VDR_TEXT2SKIN_COMMON_H
@@ -14,14 +14,16 @@ using std::string;
 using std::vector;
 using std::map;
 
-#define precond(x) if ((x)) { esyslog("ERROR: text2skin: "#x " not given"); return; }
+#ifdef DEBUG
+#	define Dprintf(x...) fprintf(stderr, x);
+#else
+#	define Dprintf(x...)
+#endif
 
 class cChannel;
 class cText2SkinItem;
 
 const char *SkinPath(void);
-void DrawTextTransparent(cOsd *Osd, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment);
-void DrawBitmap(cOsd *Osd, int x, int y, cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0);
 const char *ChannelNumber(const cChannel *Channel, int Number);
 const char *ChannelName(const cChannel *Channel, int Number);
 	
diff --git a/data.c b/data.c
index 9d6cdb2..dd8d097 100644
--- a/data.c
+++ b/data.c
@@ -1,13 +1,30 @@
 /*
- * $Id: data.c,v 1.12 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: data.c,v 1.13 2004/06/05 01:39:36 lordjaxom Exp $
  */
 
 #include "data.h"
 #include "common.h"
 
 static string SectionNames[__SECTION_COUNT__] =
-	{ "Skin", "ChannelSmall", "Channel", "Volume",
-	  "ReplayMode", "Replay", "Message", "Menu" };
+	{ "Skin", "ChannelSmall", "Channel", "Volume", "ReplayMode", "Replay", 
+	  "Message", "Menu" };
+
+static string ItemNames[__ITEM_COUNT__] = 
+	{ "Unknown", "Skin", "Background", "Text", "Image", "Rectangle", "Ellipse",
+	  "Slope", "DateTime", "Date", "Time", "ChannelLogo", "ChannelNumberName",
+	  "ChannelNumber", "ChannelName", "Language", "Timebar", "PresentTime",
+	  "PresentTitle", "PresentShortText", "FollowingTime", "FollowingTitle",
+	  "FollowingShortText", "SymbolTeletext", "SymbolAudio", "SymbolDolby",
+	  "SymbolEncrypted", "SymbolRecording", "SymbolRadio", "Volumebar", "Mute",
+	  "Replaybar", "ReplayTitle", "ReplayCurrent", "ReplayTotal", "ReplayJump",
+	  "SymbolPlay", "SymbolPause", "SymbolFastFwd", "SymbolFastRew",
+	  "SymbolSlowFwd", "SymbolSlowRew", "MessageStatus", "MessageInfo",
+	  "MessageWarning", "MessageError", "MenuArea", "MenuItem", "MenuCurrent",
+	  "MenuTitle", "MenuRed", "MenuGreen", "MenuYellow", "MenuBlue", "MenuText",
+		"SymbolScrollUp", "SymbolScrollDown", "MenuEventTitle", 
+		"MenuEventShortText", "MenuEventDescription", "MenuEventTime", 
+		"SymbolEventRunning", "SymbolEventTimer", "SymbolEventVPS",
+		"MenuRecording" };
 	
 cText2SkinItem::cText2SkinItem(void) {
 	mItem    = itemUnknown;
@@ -17,40 +34,17 @@ cText2SkinItem::cText2SkinItem(void) {
 	mSize.h  = 0;
 	mBpp     = 4;
 	mArc     = 0;
-	//mFg      = NULL;
-	//mBg      = NULL;
 	mFont    = cFont::GetFont(fontOsd);
 	mAlign   = taDefault;
 }
 
 cText2SkinItem::~cText2SkinItem() {
-	//delete mBg;
-	//delete mFg;
 }
 
 bool cText2SkinItem::Parse(const char *Text) {
 	char *text = strdup(Text);
 	char *ptr = text;
 
-	/*ptr = text + strlen(text) - 1;
-	for (; ptr >= text && *ptr == ' '; --ptr)
-		*ptr = '\0';
-	ptr = skipspace(text);
-	if (*ptr == '\0' || *ptr == '#') // empty line or comment
-		return true;
-	else if (*ptr == '[' && ptr[strlen(ptr)-1] == ']') { // section
-		++ptr;
-		ptr[strlen(ptr)-1] = '\0';
-		if      (strcmp(ptr, "Channel")      == 0)  mParseSection = sectionChannel;
-		else if (strcmp(ptr, "ChannelSmall") == 0)  mParseSection = sectionChannelSmall;
-		else if (strcmp(ptr, "Menu")         == 0)  mParseSection = sectionMenu;
-		else if (strcmp(ptr, "Volume")       == 0)  mParseSection = sectionVolume;
-		else if (strcmp(ptr, "ReplayMode")   == 0)  mParseSection = sectionReplayMode;
-		else if (strcmp(ptr, "Replay")       == 0)  mParseSection = sectionReplay;
-		else if (strcmp(ptr, "Message")      == 0)  mParseSection = sectionMessage;
-		return true;
-	}*/
-
 	// check if this is an item
 	string item;
 	if (ParseVar(ptr, "Item", item)) {
@@ -59,62 +53,19 @@ bool cText2SkinItem::Parse(const char *Text) {
 				mItem = itemSkin;
 			else
 				esyslog("ERROR: text2skin: Skin doesn't contain Item=Skin keyphrase");
+		} else {
+			int i;
+			// valid items begin at index two
+			for (i = 2; i < __ITEM_COUNT__; ++i) {
+				if (ItemNames[i] == item) {
+					mItem = (eSkinItem)i;
+					break;
+				}
+			}
+			if (i == __ITEM_COUNT__)
+				esyslog("ERROR: text2skin: %s is not a valid theme item\n", item.c_str());
 		}
-		else if (item == "Background")         mItem = itemBackground;
-		else if (item == "ChannelLogo")        mItem = itemChannelLogo;
-		else if (item == "Language")           mItem = itemLanguage;
-		else if (item == "Text")               mItem = itemText;
-		else if (item == "Image")              mItem = itemImage;
-		else if (item == "DateTime")           mItem = itemDateTime;
-		else if (item == "Date")               mItem = itemDate;
-		else if (item == "Time")               mItem = itemTime;
-		else if (item == "ChannelNumberName")  mItem = itemChannelNumberName;
-		else if (item == "ChannelNumber")      mItem = itemChannelNumber;
-		else if (item == "ChannelName")        mItem = itemChannelName;
-		else if (item == "Rectangle")          mItem = itemRectangle;
-		else if (item == "Ellipse")            mItem = itemEllipse;
-		else if (item == "Slope")              mItem = itemSlope;
-		else if (item == "Timebar")            mItem = itemTimebar;
-		else if (item == "PresentTime")        mItem = itemPresentTime;
-		else if (item == "PresentTitle")       mItem = itemPresentTitle;
-		else if (item == "PresentShortText")   mItem = itemPresentShortText;
-		else if (item == "FollowingTime")      mItem = itemFollowingTime;
-		else if (item == "FollowingTitle")     mItem = itemFollowingTitle;
-		else if (item == "FollowingShortText") mItem = itemFollowingShortText;
-		else if (item == "SymbolTeletext")     mItem = itemSymbolTeletext;
-		else if (item == "SymbolAudio")        mItem = itemSymbolAudio;
-		else if (item == "SymbolDolby")        mItem = itemSymbolDolby;
-		else if (item == "SymbolEncrypted")    mItem = itemSymbolEncrypted;
-		else if (item == "SymbolRecording")    mItem = itemSymbolRecording;
-		else if (item == "SymbolRadio")        mItem = itemSymbolRadio;
-		else if (item == "Volumebar")          mItem = itemVolumebar;
-		else if (item == "Mute")               mItem = itemMute;
-		else if (item == "Replaybar")          mItem = itemReplaybar;
-		else if (item == "ReplayTitle")        mItem = itemReplayTitle;
-		else if (item == "ReplayCurrent")      mItem = itemReplayCurrent;
-		else if (item == "ReplayTotal")        mItem = itemReplayTotal;
-		else if (item == "ReplayJump")         mItem = itemReplayJump;
-		else if (item == "SymbolPlay")         mItem = itemSymbolPlay;
-		else if (item == "SymbolPause")        mItem = itemSymbolPause;
-		else if (item == "SymbolFastFwd")      mItem = itemSymbolFastFwd;
-		else if (item == "SymbolFastRew")      mItem = itemSymbolFastRew;
-		else if (item == "SymbolSlowFwd")      mItem = itemSymbolSlowFwd;
-		else if (item == "SymbolFastFwd")      mItem = itemSymbolFastFwd;
-		else if (item == "MessageStatus")      mItem = itemMessageStatus;
-		else if (item == "MessageInfo")        mItem = itemMessageInfo;
-		else if (item == "MessageWarning")     mItem = itemMessageWarning;
-		else if (item == "MessageError")       mItem = itemMessageError;
-		else if (item == "MenuArea")           mItem = itemMenuArea;
-		else if (item == "MenuItem")           mItem = itemMenuItem;
-		else if (item == "MenuCurrent")        mItem = itemMenuCurrent;
-		else if (item == "MenuTitle")          mItem = itemMenuTitle;
-		else if (item == "MenuRed")            mItem = itemMenuRed;
-		else if (item == "MenuGreen")          mItem = itemMenuGreen;
-		else if (item == "MenuYellow")         mItem = itemMenuYellow;
-		else if (item == "MenuBlue")           mItem = itemMenuBlue;
-		else
-			esyslog("ERROR: text2skin: %s is not a valid theme item\n", item.c_str());
-
+		
 		if (mItem != itemUnknown) {
 			if (mItem != itemSkin)
 				ParseItem(ptr);
diff --git a/data.h b/data.h
index a8dbd43..d3f59a0 100644
--- a/data.h
+++ b/data.h
@@ -1,5 +1,5 @@
 /*
- * $Id: data.h,v 1.10 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: data.h,v 1.11 2004/06/05 01:39:36 lordjaxom Exp $
  */
 
 #ifndef VDR_TEXT2SKIN_DATA_H
@@ -80,6 +80,18 @@ enum eSkinItem {
 	itemMenuGreen,
 	itemMenuYellow,
 	itemMenuBlue,
+	itemMenuText,
+	itemSymbolScrollUp,
+	itemSymbolScrollDown,
+	itemMenuEventTitle,
+	itemMenuEventShortText,
+	itemMenuEventDescription,
+	itemMenuEventTime,
+	itemSymbolEventRunning,
+	itemSymbolEventTimer,
+	itemSymbolEventVPS,
+	itemMenuRecording,
+	__ITEM_COUNT__
 };
 
 struct POINT {
@@ -124,8 +136,6 @@ public:
 	const SIZE     &Size(void)    const { return mSize; }
 	int             Bpp(void)     const { return mBpp; }
 	int             Arc(void)     const { return mArc; }
-	//const tColor   *Fg(void)      const { return mFg; }
-	//const tColor   *Bg(void)      const { return mBg; }
 	const string   &Fg(void)      const { return mFg; }
 	const string   &Bg(void)      const { return mBg; }
 	const cFont    *Font(void)    const { return mFont; }
diff --git a/display.c b/display.c
index 88abfa1..93ccb32 100644
--- a/display.c
+++ b/display.c
@@ -1,5 +1,5 @@
 /*
- * $Id: display.c,v 1.9 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: display.c,v 1.12 2004/06/05 18:04:29 lordjaxom Exp $
  */
 
 #include "render.h"
@@ -81,6 +81,7 @@ void cText2SkinDisplayVolume::Flush(void) {
 // --- cText2SkinDisplayReplay ------------------------------------------------
 
 cText2SkinDisplayReplay::cText2SkinDisplayReplay(cText2SkinData *Data, cText2SkinI18n *I18n, cText2SkinTheme *Theme, bool ModeOnly) {
+	Dprintf("ModeOnly: %d\n", ModeOnly);
 	mRender   = new cText2SkinRender(Data, I18n, Theme, ModeOnly ? sectionReplayMode : sectionReplay);
 	mDirty    = false;
 }
@@ -98,7 +99,7 @@ void cText2SkinDisplayReplay::SetTitle(const char *Title) {
 }
 
 void cText2SkinDisplayReplay::SetMode(bool Play, bool Forward, int Speed) {
-	if (mRender->mReplayPlay != Play || mRender->mReplayPlay != Forward || mRender->mReplaySpeed != Speed) {
+	if (mRender->mReplayPlay != Play || mRender->mReplayForward != Forward || mRender->mReplaySpeed != Speed) {
 		mRender->mReplayPlay = Play;
 		mRender->mReplayForward = Forward;
 		mRender->mReplaySpeed = Speed;
@@ -209,7 +210,15 @@ cText2SkinDisplayMenu::~cText2SkinDisplayMenu() {
 
 void cText2SkinDisplayMenu::Clear(void) {
 	mRender->mMenuItems.clear();
-	mRender->mMenuCurrent = -1;
+	mRender->mMenuTitle     = "";
+	mRender->mMenuCurrent   = -1;
+	mRender->mMenuRed       = "";
+	mRender->mMenuGreen     = "";
+	mRender->mMenuYellow    = "";
+	mRender->mMenuBlue      = "";
+	mRender->mMenuEvent     = NULL;
+	mRender->mMenuRecording = NULL;
+	mRender->mMenuText      = "";
 	mDirty = true;
 }
 
@@ -245,7 +254,17 @@ void cText2SkinDisplayMenu::SetMessage(eMessageType Type, const char *Text) {
 }
 
 void cText2SkinDisplayMenu::SetItem(const char *Text, int Index, bool Current, bool Selectable) {
-	cText2SkinRender::MenuItem item = { Text, Selectable };
+	cText2SkinRender::MenuItem item;
+	item.text = Text;
+	item.sel = Selectable;
+	for (int i = 0; i < MaxTabs; ++i) {
+		const char *tab = GetTabbedText(Text, i);
+		if (tab)
+			item.tabs[i] = tab;
+		if (!Tab(i + 1))
+			break;
+	}
+	SetEditableWidth(mRender->GetEditableWidth(item, Current));
 	if ((int)mRender->mMenuItems.size() <= Index) {
 		mRender->mMenuItems.push_back(item);
 		mDirty = true;
@@ -282,6 +301,25 @@ void cText2SkinDisplayMenu::SetText(const char *Text, bool FixedFont) {
 	}
 }
 
+void cText2SkinDisplayMenu::SetTabs(int Tab1, int Tab2, int Tab3, int Tab4, int Tab5) {
+	cSkinDisplayMenu::SetTabs(Tab1, Tab2, Tab3, Tab4, Tab5);
+	mRender->mMenuTabs[0] = Tab(0);
+	mRender->mMenuTabs[1] = Tab(1);
+	mRender->mMenuTabs[2] = Tab(2);
+	mRender->mMenuTabs[3] = Tab(3);
+	mRender->mMenuTabs[4] = Tab(4);
+	mRender->mMenuTabs[5] = Tab(5);
+}
+
+void cText2SkinDisplayMenu::Scroll(bool Up, bool Page) {
+	if (mRender->mScroller && (Up ? mRender->mScroller->CanScrollUp() : mRender->mScroller->CanScrollDown())) {
+		mRender->mMenuScroll = true;
+		mRender->mMenuScrollUp = Up;
+		mRender->mMenuScrollPage = Page;
+		mDirty = true;
+	}
+}
+
 void cText2SkinDisplayMenu::Flush(void) {
 	if (mDirty) {
 		mRender->Flush();
diff --git a/display.h b/display.h
index e7d7926..afb24db 100644
--- a/display.h
+++ b/display.h
@@ -1,5 +1,5 @@
 /*
- * $Id: display.h,v 1.3 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: display.h,v 1.5 2004/06/05 18:04:29 lordjaxom Exp $
  */
 
 #ifndef VDR_TEXT2SKIN_SKIN_H
@@ -89,6 +89,8 @@ public:
 	virtual void SetEvent(const cEvent *Event);
 	virtual void SetRecording(const cRecording *Recording);
 	virtual void SetText(const char *Text, bool FixedFont);
+	virtual void SetTabs(int Tab1, int Tab2, int Tab3, int Tab4, int Tab5);
+	virtual void Scroll(bool Up, bool Page);
 	virtual void Flush(void);
 };
 
diff --git a/i18n.c b/i18n.c
index 0583ae1..7c28ca6 100644
--- a/i18n.c
+++ b/i18n.c
@@ -1,5 +1,5 @@
 /*
- * $Id: i18n.c,v 1.1 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: i18n.c,v 1.2 2004/06/05 16:52:44 lordjaxom Exp $
  */
 
 #include "i18n.h"
@@ -24,12 +24,20 @@ bool cText2SkinI18n::Parse(const char *Text) {
 			memset(&p, 0, sizeof(tI18nPhrase));
 			Text += 17;
 
+
 			for (i = 0; i < I18nNumLanguages; ++i) {
+				char *langs = strdup(I18nLanguageCode(i));
+				char *ptr = langs, *ep;
 				string text;
-				if (ParseVar(Text, I18nLanguageCode(i), text)) 
-					p[i] = strdup(text.c_str());
-				else
-					p[i] = "";
+				p[i] = "";
+				do {
+					if ((ep = strchr(ptr, ',')) != NULL)
+						*ep = '\0';
+					if (ParseVar(Text, ptr, text))
+						p[i] = strdup(text.c_str());
+					ptr = ep + 1;
+				} while (ep != NULL);
+				free(langs);
 			}
 
 			int idx = mNumPhrases++;
diff --git a/loader.c b/loader.c
index 3ec9052..3523bf9 100644
--- a/loader.c
+++ b/loader.c
@@ -1,5 +1,5 @@
 /*
- * $Id: loader.c,v 1.6 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: loader.c,v 1.8 2004/06/05 16:52:44 lordjaxom Exp $
  */
 
 #include "loader.h"
@@ -34,9 +34,8 @@ void cText2SkinLoader::Load(const char *Skin) {
 	string transfile = (string)SkinPath() + "/" + Skin + "/" + Skin + ".trans";
 	if (access(transfile.c_str(), F_OK) == 0) {
 		translations = new cText2SkinI18n(Skin);
-		if (!translations->Load(transfile)) {
+		if (!translations->Load(transfile))
 			DELETENULL(translations);
-		}
 	}
 
 	cText2SkinTheme *theme = new cText2SkinTheme(Skin);
@@ -74,7 +73,7 @@ cText2SkinLoader::~cText2SkinLoader() {
 }
 
 cSkinDisplayChannel *cText2SkinLoader::DisplayChannel(bool WithInfo) {
-	printf("WithInfo: %d\n", WithInfo);
+	Dprintf("WithInfo: %d\n", WithInfo);
 	return new cText2SkinDisplayChannel(mData, mI18n, mTheme, WithInfo);
 }
 
diff --git a/patches/vdr-1.3.9-osd.diff b/patches/vdr-1.3.9-osd.diff
new file mode 100644
index 0000000..0da295b
--- /dev/null
+++ b/patches/vdr-1.3.9-osd.diff
@@ -0,0 +1,106 @@
+diff -Nru -x PLUGINS o/vdr-1.3.9/osd.c vdr-1.3.9/osd.c
+--- o/vdr-1.3.9/osd.c	2004-05-28 17:33:22.000000000 +0200
++++ vdr-1.3.9/osd.c	2004-06-05 17:25:33.036994648 +0200
+@@ -244,7 +246,7 @@
+   return Result;
+ }
+ 
+-bool cBitmap::SetXpm(char *Xpm[])
++bool cBitmap::SetXpm(char *Xpm[], bool IgnoreNone)
+ {
+   char **p = Xpm;
+   int w, h, n, c;
+@@ -257,10 +259,11 @@
+      return false;
+      }
+   int b = 0;
+-  while (1 << (1 << b) < n)
++  while (1 << (1 << b) < (IgnoreNone ? n - 1 : n))
+         b++;
+   SetBpp(1 << b);
+   SetSize(w, h);
++  int NoneColorIndex = MAXNUMCOLORS;
+   for (int i = 0; i < n; i++) {
+       const char *s = *++p;
+       if (int(strlen(s)) < c) {
+@@ -273,14 +276,18 @@
+          return false;
+          }
+       s = skipspace(s + 1);
+-      if (strcasecmp(s, "none") == 0)
+-         s = "#00000000";
++			if (strcasecmp(s, "none") == 0) {
++				s = "#00000000";
++				NoneColorIndex = i;
++				if (IgnoreNone)
++				   continue;
++				}
+       if (*s != '#') {
+          esyslog("ERROR: unknown color code in XPM: '%c'", *s);
+          return false;
+          }
+       tColor color = strtoul(++s, NULL, 16) | 0xFF000000;
+-      SetColor(i, color);
++			SetColor((IgnoreNone && i > NoneColorIndex) ? i - 1 : i, color);
+       }
+   for (int y = 0; y < h; y++) {
+       const char *s = *++p;
+@@ -295,13 +302,17 @@
+                  return false;
+                  }
+               if (strncmp(Xpm[i + 1], s, c) == 0) {
+-                 SetIndex(x, y, i);
++                 if (i == NoneColorIndex)
++                    NoneColorIndex = MAXNUMCOLORS;
++                 SetIndex(x, y, (IgnoreNone && i > NoneColorIndex) ? i - 1 : i);
+                  break;
+                  }
+               }
+           s += c;
+           }
+       }
++  if (NoneColorIndex < MAXNUMCOLORS && !IgnoreNone)
++     return SetXpm(Xpm, true);
+   return true;
+ }
+ 
+@@ -354,7 +365,8 @@
+         int ch = Height ? Height : h;
+         if (!Intersects(x, y, x + cw - 1, y + ch - 1))
+            return;
+-        DrawRectangle(x, y, x + cw - 1, y + ch - 1, ColorBg);
++				if (ColorBg != clrTransparent)
++				   DrawRectangle(x, y, x + cw - 1, y + ch - 1, ColorBg);
+         limit = x + cw - x0;
+         if (Width) {
+            if ((Alignment & taLeft) != 0)
+@@ -395,7 +408,8 @@
+               for (int row = 0; row < h; row++) {
+                   cFont::tPixelData PixelData = CharData->lines[row];
+                   for (int col = CharData->width; col-- > 0; ) {
+-                      SetIndex(x + col, y + row, (PixelData & 1) ? fg : bg);
++                      if (ColorBg != clrTransparent || (PixelData & 1))
++                         SetIndex(x + col, y + row, (PixelData & 1) ? fg : bg);
+                       PixelData >>= 1;
+                       }
+                   }
+diff -Nru -x PLUGINS o/vdr-1.3.9/osd.h vdr-1.3.9/osd.h
+--- o/vdr-1.3.9/osd.h	2004-05-29 16:02:47.000000000 +0200
++++ vdr-1.3.9/osd.h	2004-06-05 17:15:17.809523440 +0200
+@@ -136,9 +136,15 @@
+   bool LoadXpm(const char *FileName);
+        ///< Calls SetXpm() with the data from the file FileName.
+        ///< Returns true if the operation was successful.
+-  bool SetXpm(char *Xpm[]);
++  bool SetXpm(char *Xpm[], bool IgnoreNone = false);
+        ///< Sets this bitmap to the given XPM data. Any previous bitmap or
+        ///< palette data will be overwritten with the new data.
++       ///< If IgnoreNone is true, a "none" color entry will be ignored.
++       ///< Only set IgnoreNone to true if you know that there is a "none"
++       ///< color entry in the XPM data and that this entry is not used!
++       ///< If SetXpm() is called with IgnoreNone set to false and the XPM
++       ///< data contains an unused "none" entry, it will be automatically
++       ///< called again with IgnoreNone set to true.
+        ///< Returns true if the operation was successful.
+   void SetIndex(int x, int y, tIndex Index);
+        ///< Sets the index at the given coordinates to Index.
diff --git a/render.c b/render.c
index 0437c84..b12b5e9 100644
--- a/render.c
+++ b/render.c
@@ -1,5 +1,5 @@
 /*
- * $Id: render.c,v 1.16 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: render.c,v 1.19 2004/06/05 18:04:29 lordjaxom Exp $
  */
 
 #include "render.h"
@@ -18,6 +18,7 @@ cText2SkinRender::cText2SkinRender(cText2SkinData *Data, cText2SkinI18n *I18n, c
 	mTheme             = Theme;
 	mSection           = Section;
 	mOsd               = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop);
+	mScroller          = NULL;
 	mChannel           = NULL;
 	mChannelNumber     = 0;
 	mVolumeCurrent     = 0;
@@ -36,6 +37,9 @@ cText2SkinRender::cText2SkinRender(cText2SkinData *Data, cText2SkinI18n *I18n, c
 	mMenuEvent         = NULL;
 	mMenuRecording     = NULL;
 	mMenuTextFixedFont = false;
+	mMenuScroll        = false;
+	mMenuScrollUp      = false;
+	mMenuScrollPage    = false;
 
 	cText2SkinData::tIterator it = Data->First(mSection);
 	for (; it != Data->Last(mSection); ++it) {
@@ -81,6 +85,7 @@ cText2SkinRender::cText2SkinRender(cText2SkinData *Data, cText2SkinI18n *I18n, c
 }
 
 cText2SkinRender::~cText2SkinRender() {
+	delete mScroller;
 	delete mOsd; 
 }
 
@@ -143,6 +148,11 @@ void cText2SkinRender::Flush(void) {
 		case itemSymbolFastRew:
 		case itemSymbolSlowFwd:
 		case itemSymbolSlowRew:
+		case itemSymbolScrollUp:
+		case itemSymbolScrollDown:
+		case itemSymbolEventRunning:
+		case itemSymbolEventTimer:
+		case itemSymbolEventVPS:
 			DisplaySymbol(item); break;
 		case itemVolumebar:
 			DisplayVolumebar(item); break;
@@ -172,6 +182,18 @@ void cText2SkinRender::Flush(void) {
 		case itemMenuYellow:
 		case itemMenuBlue:
 			DisplayMenuColorbutton(item); break;
+		case itemMenuText:
+			DisplayMenuText(item); break;
+		case itemMenuEventTitle:
+			DisplayMenuEventTitle(item); break;
+		case itemMenuEventShortText:
+			DisplayMenuEventShortText(item); break;
+		case itemMenuEventDescription:
+			DisplayMenuEventDescription(item); break;
+		case itemMenuEventTime:
+			DisplayMenuEventTime(item); break;
+		case itemMenuRecording:
+			DisplayMenuRecording(item); break;
 		default:
 			break;
 		}
@@ -180,41 +202,39 @@ void cText2SkinRender::Flush(void) {
 }
 
 void cText2SkinRender::DrawBackground(const POINT &Pos, const SIZE &Size, const tColor *Bg, const tColor *Fg, const string &Path) {
-	bool image = false;
-	cText2SkinBitmap bm;
+	cText2SkinBitmap *bmp = NULL;
 	if (Path != "") {
 		char *p;
 		asprintf(&p, "%s/%s/%s", SkinPath(), mData->Skin().c_str(), Path.c_str());
-		if (bm.Load(p)) {
-			if (Bg) bm.SetColor(0, *Bg);
-			if (Fg) bm.SetColor(1, *Fg);
-			image = true;
+		if ((bmp = cText2SkinBitmap::Load(p)) != NULL) {
+			if (Bg) bmp->SetColor(0, *Bg);
+			if (Fg) bmp->SetColor(1, *Fg);
 		}
 		free(p);
 	}
 
-	if (image)
-		DrawBitmap(mOsd, Pos.x, Pos.y, bm);
+	if (bmp)
+		mOsd->DrawBitmap(Pos.x, Pos.y, *bmp);
 	else
 		mOsd->DrawRectangle(Pos.x, Pos.y, Pos.x + Size.w - 1, Pos.y + Size.h - 1, Bg ? *Bg : 0);
 }
 
 void cText2SkinRender::DrawImage(const POINT &Pos, const SIZE &Size, const tColor *Bg, const tColor *Fg, const string &Path) {
-	cText2SkinBitmap bm;
+	cText2SkinBitmap *bmp;
 	char *p;
 	asprintf(&p, "%s/%s/%s", SkinPath(), mData->Skin().c_str(), Path.c_str());
-	printf("Trying to load image: %s\n", p);
-	if (bm.Load(p)) {
-		if (Bg) bm.SetColor(0, *Bg);
-		if (Fg) bm.SetColor(1, *Fg);
+	Dprintf("Trying to load image: %s\n", p);
+	if ((bmp = cText2SkinBitmap::Load(p)) != NULL) {
+		if (Bg) bmp->SetColor(0, *Bg);
+		if (Fg) bmp->SetColor(1, *Fg);
 		//mOsd->DrawRectangle(Pos.x, Pos.y, Pos.x + Size.w - 1, Pos.y + Size.h - 1, bm.Color(0));
-		DrawBitmap(mOsd, Pos.x, Pos.y, bm);
+		mOsd->DrawBitmap(Pos.x, Pos.y, *bmp);
 	}
 	free(p);
 }
 
 void cText2SkinRender::DrawText(const POINT &Pos, const SIZE &Size, const tColor *Fg, const string &Text, const cFont *Font, int Align) {
-	DrawTextTransparent(mOsd, Pos.x, Pos.y, Text.c_str(), Fg ? *Fg : 0, 0, Font, Size.w, Size.h, Align);
+	mOsd->DrawText(Pos.x, Pos.y, Text.c_str(), Fg ? *Fg : 0, 0, Font, Size.w, Size.h, Align);
 }
 
 void cText2SkinRender::DrawRectangle(const POINT &Pos, const SIZE &Size, const tColor *Fg) {
@@ -300,6 +320,15 @@ void cText2SkinRender::DrawMark(const POINT &Pos, const SIZE &Size, bool Start,
 	}
 }
 
+void cText2SkinRender::DrawScrollText(const POINT &Pos, const SIZE &Size, const tColor *Fg, const string &Text, const cFont *Font, int Align) {
+	if (mScroller == NULL)
+		mScroller = new cTextScroller(mOsd, Pos.x, Pos.y, Size.w, Size.h, Text.c_str(), Font, Fg ? *Fg : 0, clrTransparent);
+	else if (mMenuScroll) {
+		mScroller->Scroll(mMenuScrollUp, mMenuScrollPage);
+		mMenuScroll = false;
+	}
+}
+
 void cText2SkinRender::DisplayBackground(cText2SkinItem *Item) {
 	DrawBackground(Item->Pos(), Item->Size(), ItemBg(Item), ItemFg(Item), Item->Path());
 }
@@ -315,13 +344,13 @@ void cText2SkinRender::DisplayLanguage(cText2SkinItem *Item) {
 	int current;
 	const char **tracks = cDevice::PrimaryDevice()->GetAudioTracks(&current);
 	if (Item->Path() != "" && Item->Type() != "" && tracks) {
-		printf("Languages: ");
+		Dprintf("Languages: ");
 		int i = 0;
 		while (tracks[i]) {
-			printf("%s%s, ", tracks[i], i == current ? " (current)" : "");
+			Dprintf("%s%s, ", tracks[i], i == current ? " (current)" : "");
 			++i;
 		}
-		printf("\n");
+		Dprintf("\n");
 		if (current < i) {
 			string path = Item->Path() + "/" + tracks[current] + "." + Item->Type();
 			DrawImage(Item->Pos(), Item->Size(), ItemBg(Item), ItemFg(Item), path);
@@ -444,22 +473,45 @@ void cText2SkinRender::DisplaySymbol(cText2SkinItem *Item) {
 		default:
 			break;
 		}
-	} else if (mSection == sectionReplay) {
+	} else if (mSection == sectionReplay || mSection == sectionReplayMode) {
 		switch (Item->Item()) {
-			case itemSymbolPlay:
-				image = (mReplaySpeed == -1 && mReplayPlay) ? Item->Path() : Item->AltPath(); break;
-			case itemSymbolPause:
-				image = (mReplaySpeed == -1 && !mReplayPlay) ? Item->Path() : Item->AltPath(); break;
-			case itemSymbolFastFwd:
-				image = (mReplayPlay && mReplayForward) ? Item->Path() : Item->AltPath(); break;
-			case itemSymbolFastRew:
-				image = (mReplayPlay && !mReplayForward) ? Item->Path() : Item->AltPath(); break;
-			case itemSymbolSlowFwd:
-				image = (!mReplayPlay && mReplayForward) ? Item->Path() : Item->AltPath(); break;
-			case itemSymbolSlowRew:
-				image = (!mReplayPlay && !mReplayForward) ? Item->Path() : Item->AltPath(); break;
+		case itemSymbolPlay:
+			image = (mReplaySpeed == -1 && mReplayPlay) ? Item->Path() : Item->AltPath(); break;
+		case itemSymbolPause:
+			image = (mReplaySpeed == -1 && !mReplayPlay) ? Item->Path() : Item->AltPath(); break;
+		case itemSymbolFastFwd:
+			image = (mReplaySpeed != -1 && mReplayPlay && mReplayForward) ? Item->Path() : Item->AltPath(); break;
+		case itemSymbolFastRew:
+			image = (mReplaySpeed != -1 && mReplayPlay && !mReplayForward) ? Item->Path() : Item->AltPath(); break;
+		case itemSymbolSlowFwd:
+			image = (mReplaySpeed != -1 && !mReplayPlay && mReplayForward) ? Item->Path() : Item->AltPath(); break;
+		case itemSymbolSlowRew:
+			image = (mReplaySpeed != -1 && !mReplayPlay && !mReplayForward) ? Item->Path() : Item->AltPath(); break;
+		default:
+			break;
+		}
+	} else if (mSection == sectionMenu) {
+		if (mScroller) {
+			switch (Item->Item()) {
+			case itemSymbolScrollUp:
+				image = mScroller->CanScrollUp() ? Item->Path() : Item->AltPath(); break;
+			case itemSymbolScrollDown:
+				image = mScroller->CanScrollDown() ? Item->Path() : Item->AltPath(); break;
+			default:
+				break;
+			}
+		} 
+		if (mMenuEvent) {
+			switch (Item->Item()) {
+			case itemSymbolEventRunning:
+				image = mMenuEvent->IsRunning() ? Item->Path() : Item->AltPath(); break;
+			case itemSymbolEventTimer:
+				image = mMenuEvent->HasTimer() ? Item->Path() : Item->AltPath(); break;
+			case itemSymbolEventVPS:
+				image = mMenuEvent->Vps() > 0 ? Item->Path() : Item->AltPath(); break;
 			default:
 				break;
+			}
 		}
 	}
 	if (image != "")
@@ -543,21 +595,26 @@ void cText2SkinRender::DisplayMenuItems(cText2SkinItem *Item) {
 
 	int index = 0;
 	while (yoffs < area->Pos().y + area->Size().h && index < (int)mMenuItems.size()) {
-		if (index == mMenuCurrent) {
-			POINT pt = { xoffs, yoffs };
-			if (current->Pos().x != -1)
-				pt.x += current->Pos().x;
-			if (current->Pos().y != -1)
-				pt.y += current->Pos().y;
-			SIZE size = { current->Size().w, current->Size().h };
-			if (ItemBg(current))
-				DrawRectangle(pt, size, ItemBg(current));
-			DrawText(pt, size, ItemFg(current), mMenuItems[index].name.c_str(), current->Font(), current->Align());
-		} else {
-			POINT pt = { xoffs + Item->Pos().x, yoffs + Item->Pos().y };
-			SIZE size = { Item->Size().w, Item->Size().h };
-			DrawText(pt, size, ItemFg(Item), mMenuItems[index].name.c_str(), Item->Font(), Item->Align());
+		cText2SkinItem *item = (index == mMenuCurrent) ? current : Item;
+		POINT pt = { xoffs, yoffs };
+		SIZE size = item->Size();
+		if (item->Pos().x != -1)
+			pt.x += item->Pos().x;
+		if (item->Pos().y != -1)
+			pt.y += item->Pos().y;
+		if (ItemBg(item))
+			DrawRectangle(pt, size, ItemBg(item));
+
+		for (int i = 0; i < cSkinDisplayMenu::MaxTabs; ++i) {
+			if (mMenuItems[index].tabs[i] != "") {
+				POINT pt2 = { pt.x + mMenuTabs[i], pt.y };
+				SIZE sz = { size.w - mMenuTabs[i], size.h };
+				DrawText(pt2, sz, ItemFg(item), mMenuItems[index].tabs[i], item->Font(), item->Align());
+			}
+			if (!mMenuTabs[i + 1])
+				break;
 		}
+
 		yoffs += Item->Size().h;
 		++index;
 	}
@@ -585,6 +642,40 @@ void cText2SkinRender::DisplayMenuColorbutton(cText2SkinItem *Item) {
 		DrawText(Item->Pos(), Item->Size(), ItemFg(Item), ItemText(Item, text), Item->Font(), Item->Align());
 }
 
+void cText2SkinRender::DisplayMenuText(cText2SkinItem *Item) {
+	if (mMenuText != "")
+		DrawScrollText(Item->Pos(), Item->Size(), ItemFg(Item), ItemText(Item, mMenuText), Item->Font(), Item->Align());
+	else
+		DELETENULL(mScroller);
+}
+
+void cText2SkinRender::DisplayMenuEventTitle(cText2SkinItem *Item) {
+	if (mMenuEvent && mMenuEvent->Title())
+		DrawText(Item->Pos(), Item->Size(), ItemFg(Item), ItemText(Item, mMenuEvent->Title()), Item->Font(), Item->Align());
+}
+
+void cText2SkinRender::DisplayMenuEventShortText(cText2SkinItem *Item) {
+	if (mMenuEvent && mMenuEvent->ShortText())
+		DrawText(Item->Pos(), Item->Size(), ItemFg(Item), ItemText(Item, mMenuEvent->ShortText()), Item->Font(), Item->Align());
+}
+
+void cText2SkinRender::DisplayMenuEventDescription(cText2SkinItem *Item) {
+	if (mMenuEvent && mMenuEvent->Description())
+		DrawScrollText(Item->Pos(), Item->Size(), ItemFg(Item), ItemText(Item, mMenuEvent->Description()), Item->Font(), Item->Align());
+}
+
+void cText2SkinRender::DisplayMenuEventTime(cText2SkinItem *Item) {
+	if (mMenuEvent) {
+		const char *text = DayDateTime(mMenuEvent->StartTime());
+		DrawText(Item->Pos(), Item->Size(), ItemFg(Item), ItemText(Item, text + 10), Item->Font(), Item->Align());
+	}
+}
+
+void cText2SkinRender::DisplayMenuRecording(cText2SkinItem *Item) {
+	if (mMenuRecording)
+		DrawScrollText(Item->Pos(), Item->Size(), ItemFg(Item), ItemText(Item, mMenuRecording->Summary()), Item->Font(), Item->Align());
+}
+
 string cText2SkinRender::ItemText(cText2SkinItem *Item) {
 	return mI18n ? mI18n->Translate(Item->Text()) : Item->Text();
 }
@@ -624,3 +715,12 @@ tColor *cText2SkinRender::ItemBg(cText2SkinItem *Item) {
 		return NULL;
 	return &Bg;
 }
+
+int cText2SkinRender::GetEditableWidth(MenuItem Item, bool Current) {
+	cText2SkinItem *current;
+	if (Current)
+		current = mData->Get(sectionMenu, itemMenuCurrent);
+	else
+		current = mData->Get(sectionMenu, itemMenuItem);
+	return current->Size().w - mMenuTabs[1];
+}
diff --git a/render.h b/render.h
index e96fd08..e27ac5b 100644
--- a/render.h
+++ b/render.h
@@ -1,5 +1,5 @@
 /*
- * $Id: render.h,v 1.12 2004/06/02 20:43:05 lordjaxom Exp $
+ * $Id: render.h,v 1.15 2004/06/05 18:04:29 lordjaxom Exp $
  */
 
 #ifndef VDR_TEXT2SKIN_RENDER_H
@@ -28,6 +28,7 @@ private:
 	cText2SkinTheme  *mTheme;
 	eSkinSection      mSection;
 	cOsd             *mOsd;
+	cTextScroller    *mScroller;
 
 	// channel display
 	const cChannel   *mChannel;
@@ -58,9 +59,10 @@ private:
 
 	// menu
 	struct MenuItem {
-		string          name;
+		string          text;
+		string          tabs[cSkinDisplayMenu::MaxTabs];
 		bool            sel;
-		bool operator!=(const MenuItem &b) { return b.name != name || b.sel != sel; }
+		bool operator!=(const MenuItem &b) { return b.text != text || b.sel != sel; }
 	};
 	string            mMenuTitle;
 	vector<MenuItem>  mMenuItems;
@@ -73,7 +75,11 @@ private:
 	const cRecording *mMenuRecording;
 	string            mMenuText;
 	bool              mMenuTextFixedFont;
-
+	bool              mMenuScroll;
+	bool              mMenuScrollUp;
+	bool              mMenuScrollPage;
+	int               mMenuTabs[cSkinDisplayMenu::MaxTabs];
+	
 protected:
 	// Basic operations
 	void DrawBackground(const POINT &Pos, const SIZE &Size, const tColor *Bg, const tColor *Fg, const string &Path);
@@ -84,6 +90,7 @@ protected:
 	void DrawSlope(const POINT &Pos, const SIZE &Size, const tColor *Fg, int Arc);
 	void DrawProgressbar(const POINT &Pos, const SIZE &Size, int Current, int Total, const tColor *Fg, const tColor *Bg, const cMarks *Marks = NULL);
  	void DrawMark(const POINT &Pos, const SIZE &Size, bool Start, bool Current, bool Horizontal);
+	void DrawScrollText(const POINT &Pos, const SIZE &Size, const tColor *Fg, const string &Text, const cFont *Font, int Align);
 
 	// High-level operations
 	void DisplayBackground(cText2SkinItem *Item); 
@@ -119,13 +126,19 @@ protected:
 	void DisplayMenuItems(cText2SkinItem *Item);
 	void DisplayMenuTitle(cText2SkinItem *Item);
 	void DisplayMenuColorbutton(cText2SkinItem *Item);
-	void DisplayMenuMessage(cText2SkinItem *Item);
+	void DisplayMenuText(cText2SkinItem *Item);
+	void DisplayMenuEventTitle(cText2SkinItem *Item);
+	void DisplayMenuEventShortText(cText2SkinItem *Item);
+	void DisplayMenuEventDescription(cText2SkinItem *Item);
+	void DisplayMenuEventTime(cText2SkinItem *Item);
+	void DisplayMenuRecording(cText2SkinItem *Item);
 
 	// Helpers
 	string ItemText(cText2SkinItem *Item);
 	string ItemText(cText2SkinItem *Item, const string &Content);
 	tColor *ItemFg(cText2SkinItem *Item);
 	tColor *ItemBg(cText2SkinItem *Item);
+	int GetEditableWidth(MenuItem Item, bool Current);
 
 public:
 	cText2SkinRender(cText2SkinData *Data, cText2SkinI18n *I18n, cText2SkinTheme *Theme, eSkinSection Section);
diff --git a/text2skin.c b/text2skin.c
index 68602d2..bd52294 100644
--- a/text2skin.c
+++ b/text2skin.c
@@ -3,26 +3,15 @@
  *
  * See the README file for copyright information and how to reach the author.
  *
- * $Id: text2skin.c,v 1.9 2004/06/02 19:55:55 lordjaxom Exp $
+ * $Id: text2skin.c,v 1.10 2004/06/05 16:52:44 lordjaxom Exp $
  */
 
+#include "text2skin.h"
 #include "loader.h"
-#include <vdr/plugin.h>
-
-static const char *VERSION        = "0.0.1-rc4";
-static const char *DESCRIPTION    = "Loader for text-based skins";
-
-class cText2SkinPlugin : public cPlugin {
-private:
-public:
-  cText2SkinPlugin(void);
-  virtual ~cText2SkinPlugin();
-  virtual const char *Version(void) { return VERSION; }
-  virtual const char *Description(void) { return DESCRIPTION; }
-  virtual bool Start(void);
-  virtual cMenuSetupPage *SetupMenu(void);
-  virtual bool SetupParse(const char *Name, const char *Value);
-  };
+
+const char *cText2SkinPlugin::VERSION        = "0.0.1";
+const char *cText2SkinPlugin::THEMEVERSION   = "0.0.1";
+const char *cText2SkinPlugin::DESCRIPTION    = "Loader for text-based skins";
 
 cText2SkinPlugin::cText2SkinPlugin(void)
 {
diff --git a/text2skin.h b/text2skin.h
new file mode 100644
index 0000000..a42faf1
--- /dev/null
+++ b/text2skin.h
@@ -0,0 +1,27 @@
+/*
+ * $Id: text2skin.h,v 1.2 2004/06/05 16:53:14 lordjaxom Exp $
+ */
+
+#ifndef VDR_TEXT2SKIN_H
+#define VDR_TEXT2SKIN_H
+
+#include "common.h"
+#include <vdr/plugin.h>
+
+class cText2SkinPlugin : public cPlugin {
+private:
+	static const char *VERSION;
+	static const char *THEMEVERSION;
+	static const char *DESCRIPTION;
+public:
+  cText2SkinPlugin(void);
+  virtual ~cText2SkinPlugin();
+  virtual const char *Version(void) { return VERSION; }
+	virtual const char *ThemeVersion(void) { return THEMEVERSION; }
+  virtual const char *Description(void) { return DESCRIPTION; }
+  virtual bool Start(void);
+  virtual cMenuSetupPage *SetupMenu(void);
+  virtual bool SetupParse(const char *Name, const char *Value);
+};
+
+#endif // VDR_TEXT2SKIN_H
-- 
cgit v1.2.3