From 5d99df7b7760c4a2b497c737a15b609008f6d56d Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 16 May 2004 10:35:36 +0200 Subject: [PATCH] Implemented 'skins' and 'themes' --- CONTRIBUTORS | 4 + HISTORY | 34 +- INSTALL | 5 - MANUAL | 17 +- Makefile | 12 +- PLUGINS.html | 215 +++++- PLUGINS/src/osddemo/HISTORY | 4 + PLUGINS/src/osddemo/osddemo.c | 22 +- channels.c | 18 +- channels.h | 4 +- config.c | 29 +- config.h | 17 +- device.c | 17 +- device.h | 11 +- dvbdevice.c | 9 +- dvbdevice.h | 7 +- dvbosd.c | 168 +++-- dvbosd.h | 29 +- dvbspu.c | 17 +- dvbspu.h | 6 +- eitscan.c | 6 +- font.c | 136 +++- font.h | 32 +- i18n.c | 182 ++++- interface.c | 341 ++------- interface.h | 35 +- menu.c | 487 ++++++------- menu.h | 16 +- menuitems.c | 115 +-- menuitems.h | 23 +- osd.c | 1267 ++++++++++++++++++--------------- osd.h | 474 ++++++++---- osdbase.c | 833 ++++++++++------------ osdbase.h | 296 +++----- player.c | 4 +- player.h | 4 +- plugin.h | 4 +- recording.c | 15 +- recording.h | 26 +- skinclassic.c | 541 ++++++++++++++ skinclassic.h | 26 + skins.c | 201 ++++++ skins.h | 297 ++++++++ skinsttng.c | 879 +++++++++++++++++++++++ skinsttng.h | 26 + symbols/arrowdown.xpm | 17 + symbols/arrowup.xpm | 17 + symbols/audio.xpm | 23 + symbols/dolbydigital.xpm | 23 + symbols/encrypted.xpm | 23 + symbols/ffwd.xpm | 31 + symbols/ffwd1.xpm | 31 + symbols/ffwd2.xpm | 31 + symbols/ffwd3.xpm | 31 + symbols/frew.xpm | 31 + symbols/frew1.xpm | 31 + symbols/frew2.xpm | 31 + symbols/frew3.xpm | 31 + symbols/mute.xpm | 25 + symbols/pause.xpm | 31 + symbols/play.xpm | 31 + symbols/radio.xpm | 23 + symbols/recording.xpm | 23 + symbols/sfwd.xpm | 31 + symbols/sfwd1.xpm | 31 + symbols/sfwd2.xpm | 31 + symbols/sfwd3.xpm | 31 + symbols/srew.xpm | 31 + symbols/srew1.xpm | 31 + symbols/srew2.xpm | 31 + symbols/srew3.xpm | 31 + symbols/teletext.xpm | 23 + symbols/volume.xpm | 19 + themes.c | 305 ++++++++ themes.h | 82 +++ vdr.5 | 44 +- vdr.c | 51 +- 77 files changed, 5789 insertions(+), 2378 deletions(-) create mode 100644 skinclassic.c create mode 100644 skinclassic.h create mode 100644 skins.c create mode 100644 skins.h create mode 100644 skinsttng.c create mode 100644 skinsttng.h create mode 100644 symbols/arrowdown.xpm create mode 100644 symbols/arrowup.xpm create mode 100644 symbols/audio.xpm create mode 100644 symbols/dolbydigital.xpm create mode 100644 symbols/encrypted.xpm create mode 100644 symbols/ffwd.xpm create mode 100644 symbols/ffwd1.xpm create mode 100644 symbols/ffwd2.xpm create mode 100644 symbols/ffwd3.xpm create mode 100644 symbols/frew.xpm create mode 100644 symbols/frew1.xpm create mode 100644 symbols/frew2.xpm create mode 100644 symbols/frew3.xpm create mode 100644 symbols/mute.xpm create mode 100644 symbols/pause.xpm create mode 100644 symbols/play.xpm create mode 100644 symbols/radio.xpm create mode 100644 symbols/recording.xpm create mode 100644 symbols/sfwd.xpm create mode 100644 symbols/sfwd1.xpm create mode 100644 symbols/sfwd2.xpm create mode 100644 symbols/sfwd3.xpm create mode 100644 symbols/srew.xpm create mode 100644 symbols/srew1.xpm create mode 100644 symbols/srew2.xpm create mode 100644 symbols/srew3.xpm create mode 100644 symbols/teletext.xpm create mode 100644 symbols/volume.xpm create mode 100644 themes.c create mode 100644 themes.h diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 74e8639b..f6d4eade 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -962,3 +962,7 @@ Marcus M Pekka Virtanen for adding language code handling to the subtitling descriptor in 'libsi' + +John Kennedy + for publishing "A Fast Bresenham Algorithm For Drawing Ellipses" (found at + http://homepage.smc.edu/kennedy_john/BELIPSE.PDF) diff --git a/HISTORY b/HISTORY index 86570253..2d2399fe 100644 --- a/HISTORY +++ b/HISTORY @@ -2746,7 +2746,7 @@ Video Disk Recorder Revision History - Now explicitly turning on the LNB power at startup, because newer drivers don't do this any more (thanks to Oliver Endriss for pointing this out). -2004-03-28: Version 1.3.7 +2004-05-16: Version 1.3.7 - Fixed a memory leak in thread handling when using NPTL (thanks to Jon Burgess). - Fixed handling Setup.RecordDolbyDigital, which was broken since version 1.1.6. @@ -2759,3 +2759,35 @@ Video Disk Recorder Revision History - Moved several menu item classes from menu.c to menuitems.[hc] to make them available for plugins. - The epg2html.pl script now handles '|' in description texts. +- The new setup option "OSD/Use small font" can be used to control the use of + the small font (see MANUAL for details). +- Swapped osd.[hc] and osdbase.[hc] to have the virtual OSD base class named cOsd. + Plugins may need to adjust their #include statements. +- Colors are now given as AARRGGBB instead of AABBGGRR. The values are mapped to + the driver's (wrong) sequence in dvbosd.c (this should really be fixed in the + driver, together with the endian problem). +- The new OSD setup parameters "Left" and "Top" can be used to define the top left + corner of the OSD. +- The OSD size prameters are now in pixel (as opposed to formerly characters). + When reading a 'setup.conf' file from an older version of VDR, the OSDwidth + and OSDheight values will be converted to pixel automatically. +- The OSD is now fully device independent. See the comments in VDR/osd.h and the + description in PLUGINS.html for information on how a plugin can implement an OSD + display on arbitrary hardware. +- The OSD (actually its cBitmap class) can now handle XPM files. There are several + XPM files in the VDR/symbols directory which can be used by skins (some of these + have been taken from the "elchi" patch). See VDR/skinsttng.c for examples on how + to use these. +- Due to the changes in the OSD handling the DEBUG_OSD option for a textual OSD + has been dropped. There will be a plugin that implements a skin with this + functionality later. +- The entire OSD display can now be implemented via "skins". See VDR/skins.[hc], + VDR/skinclassic.[hc], VDR/skinsttng.[hc] and PLUGINS.html for information on how + a plugin can implement its own skin. By default VDR comes with a "Classic" skin + that implements the OSD display known from previous versions, and the new skin + named "ST:TNG Panels", which is also the default skin now. The actual skin can + be selected through "Setup/OSD/Skin". +- The colors used in a skin can now be configured using "themes". See PLUGINS.html + for information on how a skin can make use of themes, and man vdr(5) for the + structure of a theme file. The actual theme to use can be selected through + "Setup/OSD/Theme". diff --git a/INSTALL b/INSTALL index 88546ea7..ec1906aa 100644 --- a/INSTALL +++ b/INSTALL @@ -40,11 +40,6 @@ following values in the 'make' call to activate the respective control mode: If you want to disable control via the PC keyboard, you can add NO_KBD=1 to the 'make' call. -Adding "DEBUG_OSD=1" will use the PC screen (or current window) -to display texts instead of the DVB card's on-screen display -interface. These modes are useful when testing new menus if you -only have a remote connection to the VDR (which, in my case, is -located in the living room and has neither a monitor nor a keyboard). If your video directory will be on a VFAT partition, add the compile time switch diff --git a/MANUAL b/MANUAL index c4d4391f..ffbc4606 100644 --- a/MANUAL +++ b/MANUAL @@ -425,13 +425,26 @@ Version 1.2 Language = English Defines the language used to display the OSD texts. - Width = 52 The width and height of the OSD . - Height = 18 The valid ranges are width=40...56, height=12...21. + Skin = ST:TNG Panels Defines the "skin" used to display the OSD menus. + + Theme = Default Defines the "theme" to use with the current skin. + + Left = 54 The top and left offset of the OSD. + Top = 45 The valid ranges are left=0...672, top=0...567. + + Width = 624 The width and height of the OSD. + Height = 486 The valid ranges are width=480...672, height=324...567. + The Width must be a multiple of 8. Message time = 1 The time (in seconds) how long an informational message shall be displayed on the OSD. The valid range is 1...60. + Use small font = 1 Defines whether the small font shall be used. 0 means never + use the small font, 1 means use the small font wherever the + current skin wants to, and 2 means always use the small + font. + Channel info position = bottom The position of the channel info window in the OSD (either 'bottom' or 'top'). diff --git a/Makefile b/Makefile index ce38c3db..95d6f876 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Makefile 1.65 2004/01/18 14:16:53 kls Exp $ +# $Id: Makefile 1.66 2004/05/08 09:15:00 kls Exp $ .DELETE_ON_ERROR: @@ -36,8 +36,9 @@ SILIB = $(LSIDIR)/libsi.a OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbosd.o\ dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\ lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\ - receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o sources.o\ - spu.o status.o svdrp.o thread.o timers.o tools.o transfer.o vdr.o videodir.o + receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o\ + skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\ + timers.o tools.o transfer.o vdr.o videodir.o FIXFONT_ISO8859_1 = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1 OSDFONT_ISO8859_1 = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1 @@ -60,11 +61,6 @@ DEFINES += -D_GNU_SOURCE DEFINES += -DVIDEODIR=\"$(VIDEODIR)\" DEFINES += -DPLUGINDIR=\"$(PLUGINLIBDIR)\" -ifdef DEBUG_OSD -DEFINES += -DDEBUG_OSD -NCURSESLIB = -lncurses -endif - ifdef VFAT # for people who want their video directory on a VFAT partition DEFINES += -DVFAT diff --git a/PLUGINS.html b/PLUGINS.html index d13ca4ab..a2cfe9b3 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -9,17 +9,20 @@
Version 1.3

-Copyright © 2003 Klaus Schmidinger
+Copyright © 2004 Klaus Schmidinger
kls@cadsoft.de
www.cadsoft.de/vdr

-
  +
  Important modifications introduced in version 1.2.6 are marked like this.
-
  +
  Important modifications introduced in version 1.3.0 are marked like this.
+
  +Important modifications introduced in version 1.3.7 are marked like this. +

VDR provides an easy to use plugin interface that allows additional functionality to be added to the program by implementing a dynamically loadable library file. @@ -67,10 +70,14 @@ structures and allows it to hook itself into specific areas to perform special a

  • Status monitor
  • Players
  • Receivers -
      +
     
  • Filters
  • The On Screen Display +
      +
  • Skins +
  • Themes +
  • Devices
  • Dolby Digital
  • Remote Control @@ -1207,7 +1214,7 @@ Mode). If the cReceiver isn't needed any more, it may simply be deleted and will automatically detach itself from the cDevice. -
      +
     

    Filters

    A Fistful of Datas

    @@ -1253,35 +1260,166 @@ and will automatically detach itself from the cDevice. See VDR/eit.c or VDR/pat.c to learn how to process filter data.

    +
     

    The On Screen Display

    -
    Express yourself

    +

    Window to the world

    -Most of the time a plugin should be able to access the OSD through the -standard mechanisms also used by VDR itself. However, these set up the OSD in -a manner of textual rows and columns, and automatically set the various -windows and color depths. -

    If a plugin needs to have total control over the OSD, it can call the static function

     #include <vdr/osd.h>
     
    -cOsdBase *MyOsd = cOsd::OpenRaw(x, y);
    +cOsd *MyOsd = cOsdProvider::NewOsd(x, y);
     

    where x and y are the coordinates of the upper left corner -of the OSD area on the screen. Such a "raw" OSD doesn't display anything +of the OSD area on the screen. Such an OSD doesn't display anything yet, so you need to at least call the function

    -MyOsd->Create(...);
    +tArea Area = { 0, 0, 100, 100, 4 };
    +MyOsd->SetAreas(Area, 1);
     

    -to define an actual OSD drawing area (see VDR/osdbase.h for the declarations -of these functions, and VDR/osd.c to see how VDR opens the OSD and sets up +to define an actual OSD drawing area (see VDR/osd.h for the declarations +of these functions, and VDR/skinsttng.c to see how VDR opens the OSD and sets up its windows and color depths). +

    +Theoretically the OSD supports a full screen drawing area, with 32 bit color +depth. However, the actual OSD device in use may not be able to provide the +full area or color depth, maybe because of lack of OSD memory or other restrictions. +A plugin that uses the OSD should therefore test whether the OSD is able to +provide the requested functionality, and should offer alternate color depths +to allow a less powerfull OSD implementation to still work reasonably. +Since it is often not really necessary to have hundreds or thousands of colors +all over the OSD area, a plugin can divide the total drawing area into several +sub-areas with different color depths and separate color palettes, as in + +

    +tArea Area = { 0, 0, 99, 99, 4 };
    +if (osd->CanHandleAreas(Area, 1) == oeOk)
    +   osd->SetAreas(Area, 1);
    +else {
    +   tArea Areas[] = { { 0,  0, 99, 19, 2 },
    +                     { 0, 20, 99, 79, 2 },
    +                     { 0, 80, 99, 99, 4 }
    +                   };
    +   osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
    +   }
    +

    + +In this example an OSD with 100 by 100 pixel and 4 bit color depth shall +be opened, so at first a single area with the full required resolution +is set up and CanHandleAreas() is called with it. If the result indicates +that the OSD will be able to handle this drawing area, a call to SetAreas() +actually sets it. If a single area with that resolution can't be handled, +a second attempt is made in which the total drawing area is divided into +three horizontal stripes, two of which use only 2 bit color depth (because +the objects drawn in there can be displayed with 4 colors) while the third +one still requests 4 bit color depth. +

    +Note that a plugin should always at first request a single drawing area +with the full required resolution. Only if this fails shall it use alternate +areas. Drawing areas are always rectangular and may not overlap (but do not need +to be adjacent). + +


    Skins

    + +
    The emperor's new clothes

    + +The way VDR displays its menus to the user is implemented through skins. +A particular skin provides several functions that return objects to be used +for displaying a specific part of the OSD, like a menu, the channel display +or the volume bar. +

    +By default VDR offers the Classic and the ST:TNG Panels skins, +which can be selected through Setup/OSD/Skin. A plugin can implement an +arbitrary skin of its own by doing something similar to what's done in +VDR/skinclassic.c. +

    +The first step in implementing a new skin is to derive a class from cSkin +that provides the handling objects necessary to do the actual work: + +

    +#include "skins.h"
    +
    +class cMySkin : public cSkin {
    +public:
    +  cMySkin(void);
    +  virtual const char *Description(void);
    +  virtual cSkinDisplayChannel *DisplayChannel(bool WithInfo);
    +  virtual cSkinDisplayMenu *DisplayMenu(void);
    +  virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly);
    +  virtual cSkinDisplayVolume *DisplayVolume(void);
    +  virtual cSkinDisplayMessage *DisplayMessage(void);
    +  };
    +

    + +See the comments in VDR/skins.h for details. VDR/skinclassic.[hc] +can be used as an example for how to implement all the necessary classes and +functions to compose a complete skin. See also the chapter about themes +if you want to make the colors used by your skin configurable. +

    +To add your new skin to the list of skins available to the user in Setup/OSD/Skin, +all you need to do is create a new object of your skin class, as in + +

    +new cMySkin;
    +

    + +in the Start() function of your plugin. +Do not delete this object, it will be automatically deleted when the program ends. + +


    Themes

    + +
    Eye of the beholder...

    + +A theme is a collection of colors that can be used by a skin. +Since every skin most likely has its own idea about what parts of it can be +themed, and different skins may have completely different numbers of +"themeable" parts, a particular theme can only be used with the skin it was designed +for. A particular skin, however, can have any number of themes. Which theme +will be actually used can be defined in Setup/OSD/Theme. +

    +In order to make a skin "themeable" is shall create an object of type cTheme, as in + +

    +static cTheme Theme;
    +

    + +The next step is to define the colors that shall be provided by this theme, as in + +

    +THEME_CLR(Theme, clrTitle,        0xFFBC8024);
    +THEME_CLR(Theme, clrButtonRedFg,  clrWhite);
    +THEME_CLR(Theme, clrButtonRedBg,  clrRed);
    +

    + +THEME_CLR() is a helper macro that adds the given color name +and its default color value to the theme. +

    +Any color names can be used, but they should always start with clr... and +if a given color has a foreground and a background value, the two names shall be +distinguished by appending ...Fg and ...Bg, respectively. +

    +Color values can be either 32 bit hexadecimal numbers in the form 0xAARRGGBB +(where the individual bytes represent Alpha (transparency), Red, Green +and Blue component, respectively), or one of the predefined color names from +VDR/osd.h. +

    +In the actual drawing code of a skin, the color names defined with the THEME_CLR() +macros can be used to fetch the actual color values from the theme, as in + +

    +osd->DrawText(x, y, s, Theme.Color(clrButtonRedFg), Theme.Color(clrButtonRedBg), font);
    +

    + +By default this will use the colors that have been defined in the respective +THEME_CLR() line, but may be overwritten through user supplied theme +files (see man vdr(5) for information about the format of a theme file). +


    Devices

    @@ -1297,7 +1435,7 @@ into a VDR system; or an analog TV receiver card, which does the MPEG encoding "on the fly" - assuming your machine is fast enough), or just a software program that takes an MPEG data stream and displays it, for instance, on an existing graphics adapter.

    -To implement an additional device, a plugin must derive a class from cDevice: +To implement an additional device, a plugin must derive a class from cDevice:

     #include <vdr/device.h>
    @@ -1374,7 +1512,7 @@ The functions to implement replaying capabilites are
     virtual bool HasDecoder(void) const;
     virtual bool CanReplay(void) const;
     virtual bool SetPlayMode(ePlayMode PlayMode);
    -
      +
      virtual int64_t GetSTC(void);
    virtual void TrickSpeed(int Speed); @@ -1396,7 +1534,7 @@ virtual void SetVideoFormat(bool VideoFormat16_9); virtual void SetVolumeDevice(int Volume);

    -
      +
     

    Section Filtering

    @@ -1426,23 +1564,46 @@ handle section data.

    On Screen Display

    +
      If your device provides On Screen Display (OSD) capabilities (which every device -that is supposed to be used as a primary device should do), it can implement -the function +that is supposed to be used as a primary device should do), it shall implement +an "OSD provider" class, derived from cOsdProvider, which, when its CreateOsd() +function is called, returns an object derived from cOsd, which can be used to +access the device's OSD:

    -virtual cOsdBase *NewOsd(int x, int y);
    +class cMyOsdProvider : public cOsdProvider {
    +public:
    +  cMyOsdProvider(void);
    +  virtual cOsd *CreateOsd(int Left, int Top);
    +  };
     

    -which must return a newly created object of a derived cOsdBase class that -implements the functions necessary to display OSD information on your device. -The caller of this function will delete the object as soon as it is no longer -needed. +In its MakePrimaryDevice() function the device shall create an object +of this class, as in + +

    +void cMyDevice::MakePrimaryDevice(bool On)
    +{
    +  new cMyOsdProvider;
    +}
    +

    + +The OSD provider object is allocated on the heap and shall not be deleted +(it will be deleted automatically in case a different device sets up an OSD +provider, or when the program ends). + +Note that an OSD implementation need not be physically linked to the device +in any way. All it needs to make sure is that the OSD will be visible to the +user - whether this goes through OSD facilities of the physical device (like +a "full featured" DVB card) or through a graphics adapter that overlays its +output with the video signal, doesn't matter. +

    Initializing new devices

    -A derived cDevice class shall implement a static function +A derived cDevice class shall implement a static function in which it determines whether the necessary hardware to run this sort of device is actually present in this machine (or whatever other prerequisites might be important), and then creates as many device objects as necessary. diff --git a/PLUGINS/src/osddemo/HISTORY b/PLUGINS/src/osddemo/HISTORY index c7d4ebcf..fd10cc3c 100644 --- a/PLUGINS/src/osddemo/HISTORY +++ b/PLUGINS/src/osddemo/HISTORY @@ -8,3 +8,7 @@ VDR Plugin 'osddemo' Revision History 2002-12-13: Version 0.1.0 - Changed setting of CXX and CXXFLAGS variables in Makefile. + +2004-04-30: Version 0.1.1 + +- New OSD handling. diff --git a/PLUGINS/src/osddemo/osddemo.c b/PLUGINS/src/osddemo/osddemo.c index 3ac8a2d9..08b2d967 100644 --- a/PLUGINS/src/osddemo/osddemo.c +++ b/PLUGINS/src/osddemo/osddemo.c @@ -3,12 +3,12 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: osddemo.c 1.2 2002/12/13 15:02:01 kls Exp $ + * $Id: osddemo.c 1.3 2004/05/16 09:28:51 kls Exp $ */ #include -static const char *VERSION = "0.1.0"; +static const char *VERSION = "0.1.1"; static const char *DESCRIPTION = "Demo of arbitrary OSD setup"; static const char *MAINMENUENTRY = "Osd Demo"; @@ -16,10 +16,10 @@ static const char *MAINMENUENTRY = "Osd Demo"; class cLineGame : public cOsdObject { private: - cOsdBase *osd; + cOsd *osd; int x; int y; - eDvbColor color; + tColor color; public: cLineGame(void); ~cLineGame(); @@ -41,15 +41,11 @@ cLineGame::~cLineGame() void cLineGame::Show(void) { - osd = cOsd::OpenRaw(100, 50); + osd = cOsdProvider::NewOsd(100, 50); if (osd) { - osd->Create(0, 0, 100, 200, 4); - osd->AddColor(clrBackground); - osd->AddColor(clrRed); - osd->AddColor(clrGreen); - osd->AddColor(clrYellow); - osd->AddColor(clrBlue); - osd->Clear(); + tArea Area = { 0, 0, 99, 199, 4 }; + osd->SetAreas(&Area, 1); + osd->DrawRectangle(0, 0, 99, 199, clrGray50); osd->Flush(); } } @@ -70,7 +66,7 @@ eOSState cLineGame::ProcessKey(eKeys Key) case kOk: return osEnd; default: return state; } - osd->Fill(x, y, x + 3, y + 3, color); + osd->DrawRectangle(x, y, x + 3, y + 3, color); osd->Flush(); state = osContinue; } diff --git a/channels.c b/channels.c index 3b0b6161..021b6174 100644 --- a/channels.c +++ b/channels.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: channels.c 1.24 2004/02/13 15:37:42 kls Exp $ + * $Id: channels.c 1.25 2004/04/03 13:42:06 kls Exp $ */ #include "channels.h" @@ -888,3 +888,19 @@ cChannel *cChannels::NewChannel(const cChannel *Transponder, const char *Name, i } return NULL; } + +const char *ChannelString(const cChannel *Channel, int Number) +{ + static char buffer[256]; + if (Channel) { + if (Channel->GroupSep()) + snprintf(buffer, sizeof(buffer), "%s", Channel->Name()); + else + snprintf(buffer, sizeof(buffer), "%d%s %s", Channel->Number(), Number ? "-" : "", Channel->Name()); + } + else if (Number) + snprintf(buffer, sizeof(buffer), "%d-", Number); + else + snprintf(buffer, sizeof(buffer), "%s", tr("*** Invalid Channel ***")); + return buffer; +} diff --git a/channels.h b/channels.h index c797cb28..f5c9fbe1 100644 --- a/channels.h +++ b/channels.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: channels.h 1.16 2004/02/13 15:16:36 kls Exp $ + * $Id: channels.h 1.17 2004/04/03 13:40:47 kls Exp $ */ #ifndef __CHANNELS_H @@ -203,4 +203,6 @@ public: extern cChannels Channels; +const char *ChannelString(const cChannel *Channel, int Number); + #endif //__CHANNELS_H diff --git a/config.c b/config.c index aaf376ab..b89df8b6 100644 --- a/config.c +++ b/config.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.125 2004/02/28 11:12:20 kls Exp $ + * $Id: config.c 1.126 2004/05/16 10:08:09 kls Exp $ */ #include "config.h" @@ -245,6 +245,8 @@ cSetup Setup; cSetup::cSetup(void) { OSDLanguage = 0; + strcpy(OSDSkin, "sttng"); + strcpy(OSDTheme, "default"); PrimaryDVB = 1; ShowInfoOnChSwitch = 1; MenuScrollPage = 1; @@ -279,9 +281,12 @@ cSetup::cSetup(void) UpdateChannels = 4; RecordDolbyDigital = 1; ChannelInfoPos = 0; - OSDwidth = 52; - OSDheight = 18; + OSDLeft = 54; + OSDTop = 45; + OSDWidth = 624; + OSDHeight = 486; OSDMessageTime = 1; + UseSmallFont = 1; MaxVideoFileSize = MAXVIDEOFILESIZE; SplitEditedFiles = 0; MinEventTimeout = 30; @@ -392,6 +397,8 @@ bool cSetup::ParseLanguages(const char *Value, int *Values) bool cSetup::Parse(const char *Name, const char *Value) { if (!strcasecmp(Name, "OSDLanguage")) OSDLanguage = atoi(Value); + else if (!strcasecmp(Name, "OSDSkin")) strn0cpy(OSDSkin, Value, MaxSkinName); + else if (!strcasecmp(Name, "OSDTheme")) strn0cpy(OSDTheme, Value, MaxThemeName); else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value); else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value); else if (!strcasecmp(Name, "MenuScrollPage")) MenuScrollPage = atoi(Value); @@ -426,9 +433,12 @@ bool cSetup::Parse(const char *Name, const char *Value) else if (!strcasecmp(Name, "UpdateChannels")) UpdateChannels = atoi(Value); else if (!strcasecmp(Name, "RecordDolbyDigital")) RecordDolbyDigital = atoi(Value); else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value); - else if (!strcasecmp(Name, "OSDwidth")) OSDwidth = atoi(Value); - else if (!strcasecmp(Name, "OSDheight")) OSDheight = atoi(Value); + else if (!strcasecmp(Name, "OSDLeft")) OSDLeft = atoi(Value); + else if (!strcasecmp(Name, "OSDTop")) OSDTop = atoi(Value); + else if (!strcasecmp(Name, "OSDWidth")) { OSDWidth = atoi(Value); if (OSDWidth < 100) OSDWidth *= 12; OSDWidth &= ~0x07; } // OSD width must be a multiple of 8 + else if (!strcasecmp(Name, "OSDHeight")) { OSDHeight = atoi(Value); if (OSDHeight < 100) OSDHeight *= 27; } else if (!strcasecmp(Name, "OSDMessageTime")) OSDMessageTime = atoi(Value); + else if (!strcasecmp(Name, "UseSmallFont")) UseSmallFont = atoi(Value); else if (!strcasecmp(Name, "MaxVideoFileSize")) MaxVideoFileSize = atoi(Value); else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value); else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value); @@ -446,6 +456,8 @@ bool cSetup::Parse(const char *Name, const char *Value) bool cSetup::Save(void) { Store("OSDLanguage", OSDLanguage); + Store("OSDSkin", OSDSkin); + Store("OSDTheme", OSDTheme); Store("PrimaryDVB", PrimaryDVB); Store("ShowInfoOnChSwitch", ShowInfoOnChSwitch); Store("MenuScrollPage", MenuScrollPage); @@ -480,9 +492,12 @@ bool cSetup::Save(void) Store("UpdateChannels", UpdateChannels); Store("RecordDolbyDigital", RecordDolbyDigital); Store("ChannelInfoPos", ChannelInfoPos); - Store("OSDwidth", OSDwidth); - Store("OSDheight", OSDheight); + Store("OSDLeft", OSDLeft); + Store("OSDTop", OSDTop); + Store("OSDWidth", OSDWidth); + Store("OSDHeight", OSDHeight); Store("OSDMessageTime", OSDMessageTime); + Store("UseSmallFont", UseSmallFont); Store("MaxVideoFileSize", MaxVideoFileSize); Store("SplitEditedFiles", SplitEditedFiles); Store("MinEventTimeout", MinEventTimeout); diff --git a/config.h b/config.h index fdccd417..270728d0 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.191 2004/03/14 16:51:13 kls Exp $ + * $Id: config.h 1.192 2004/05/08 09:18:36 kls Exp $ */ #ifndef __CONFIG_H @@ -26,12 +26,14 @@ #define MAXPRIORITY 99 #define MAXLIFETIME 99 -#define MINOSDWIDTH 40 -#define MAXOSDWIDTH 56 -#define MINOSDHEIGHT 12 -#define MAXOSDHEIGHT 21 +#define MINOSDWIDTH 480 +#define MAXOSDWIDTH 672 +#define MINOSDHEIGHT 324 +#define MAXOSDHEIGHT 567 #define MaxFileName 256 +#define MaxSkinName 16 +#define MaxThemeName 16 class cCommand : public cListObject { private: @@ -204,6 +206,8 @@ public: // Also adjust cMenuSetup (menu.c) when adding parameters here! int __BeginData__; int OSDLanguage; + char OSDSkin[MaxSkinName]; + char OSDTheme[MaxThemeName]; int PrimaryDVB; int ShowInfoOnChSwitch; int MenuScrollPage; @@ -235,8 +239,9 @@ public: int UpdateChannels; int RecordDolbyDigital; int ChannelInfoPos; - int OSDwidth, OSDheight; + int OSDLeft, OSDTop, OSDWidth, OSDHeight; int OSDMessageTime; + int UseSmallFont; int MaxVideoFileSize; int SplitEditedFiles; int MinEventTimeout, MinUserInactivity; diff --git a/device.c b/device.c index 871149e9..9da29579 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 1.53 2004/02/08 15:05:11 kls Exp $ + * $Id: device.c 1.54 2004/04/17 10:07:10 kls Exp $ */ #include "device.h" @@ -126,11 +126,6 @@ bool cDevice::HasDecoder(void) const return false; } -cOsdBase *cDevice::NewOsd(int x, int y) -{ - return NULL; -} - cSpuDecoder *cDevice::GetSpuDecoder(void) { return NULL; @@ -360,11 +355,9 @@ bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView) for (int i = 3; i--;) { switch (SetChannel(Channel, LiveView)) { case scrOk: return true; - case scrNotAvailable: if (Interface) - Interface->Error(tr("Channel not available!")); + case scrNotAvailable: Skins.Message(mtError, tr("Channel not available!")); return false; - case scrNoTransfer: if (Interface) - Interface->Error(tr("Can't start Transfer Mode!")); + case scrNoTransfer: Skins.Message(mtError, tr("Can't start Transfer Mode!")); return false; case scrFailed: break; // loop will retry } @@ -396,8 +389,8 @@ bool cDevice::SwitchChannel(int Direction) if (PrimaryDevice()->SwitchChannel(channel, true)) result = true; } - else if (n != first && Interface) - Interface->Error(tr("Channel not available!")); + else if (n != first) + Skins.Message(mtError, tr("Channel not available!")); } return result; } diff --git a/device.h b/device.h index 5815aca4..a725e4d4 100644 --- a/device.h +++ b/device.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.h 1.41 2004/03/14 10:47:01 kls Exp $ + * $Id: device.h 1.42 2004/04/17 10:15:25 kls Exp $ */ #ifndef __DEVICE_H @@ -54,7 +54,6 @@ enum eVideoSystem { vsPAL, vsNTSC }; -class cOsdBase; class cChannel; class cPlayer; class cReceiver; @@ -139,15 +138,9 @@ public: virtual bool HasDecoder(void) const; ///< Tells whether this device has an MPEG decoder. -// OSD facilities +// SPU facilities public: - virtual cOsdBase *NewOsd(int x, int y); - ///< Creates a new cOsdBase object that can be used by the cOsd class - ///< to display information on the screen, with the upper left corner - ///< of the OSD at the given coordinates. If a derived cDevice doesn't - ///< implement this function, NULL will be returned by default (which - ///< means the device has no OSD capabilities). virtual cSpuDecoder *GetSpuDecoder(void); ///< Returns a pointer to the device's SPU decoder (or NULL, if this ///< device doesn't have an SPU decoder). diff --git a/dvbdevice.c b/dvbdevice.c index 6e1d7f95..afeb9731 100644 --- a/dvbdevice.c +++ b/dvbdevice.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.c 1.83 2004/03/14 14:47:46 kls Exp $ + * $Id: dvbdevice.c 1.84 2004/05/01 13:15:46 kls Exp $ */ #include "dvbdevice.h" @@ -427,7 +427,7 @@ bool cDvbDevice::Initialize(void) void cDvbDevice::MakePrimaryDevice(bool On) { if (HasDecoder()) - cDvbOsd::SetDvbDevice(On ? this : NULL); + new cDvbOsdProvider(fd_osd); } bool cDvbDevice::HasDecoder(void) const @@ -446,11 +446,6 @@ int cDvbDevice::ProvidesCa(const cChannel *Channel) const return cDevice::ProvidesCa(Channel); } -cOsdBase *cDvbDevice::NewOsd(int x, int y) -{ - return new cDvbOsd(x, y); -} - cSpuDecoder *cDvbDevice::GetSpuDecoder(void) { if (!spuDecoder && IsPrimaryDevice()) diff --git a/dvbdevice.h b/dvbdevice.h index f64e6dda..2c5bd8eb 100644 --- a/dvbdevice.h +++ b/dvbdevice.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbdevice.h 1.26 2004/01/03 10:21:50 kls Exp $ + * $Id: dvbdevice.h 1.27 2004/04/17 11:56:22 kls Exp $ */ #ifndef __DVBDEVICE_H @@ -26,7 +26,6 @@ class cDvbTuner; /// The cDvbDevice implements a DVB device which can be accessed through the Linux DVB driver API. class cDvbDevice : public cDevice { - friend class cDvbOsd; private: static bool Probe(const char *FileName); ///< Probes for existing DVB devices. @@ -38,7 +37,6 @@ public: private: fe_type_t frontendType; int fd_osd, fd_audio, fd_video, fd_dvr, fd_stc; - int OsdDeviceHandle(void) const { return fd_osd; } protected: virtual void MakePrimaryDevice(bool On); public: @@ -47,12 +45,11 @@ public: virtual int ProvidesCa(const cChannel *Channel) const; virtual bool HasDecoder(void) const; -// OSD facilities +// SPU facilities private: cDvbSpuDecoder *spuDecoder; public: - cOsdBase *NewOsd(int x, int y); virtual cSpuDecoder *GetSpuDecoder(void); // Channel facilities diff --git a/dvbosd.c b/dvbosd.c index 3d57362d..d21bcefb 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.21 2003/04/12 12:10:12 kls Exp $ + * $Id: dvbosd.c 1.22 2004/05/01 15:10:44 kls Exp $ */ #include "dvbosd.h" @@ -13,40 +13,49 @@ #include #include "tools.h" -const cDvbDevice *cDvbOsd::dvbDevice = NULL; +// --- cDvbOsd --------------------------------------------------------------- -cDvbOsd::cDvbOsd(int x, int y) -:cOsdBase(x, y) +#define MAXNUMWINDOWS 7 // OSD windows are counted 1...7 +#define MAXOSDMEMORY 92000 // number of bytes available to the OSD (depends on firmware version, but there is no way of determining the actual value) + +cDvbOsd::cDvbOsd(int Left, int Top, int OsdDev) +:cOsd(Left, Top) { - osdDev = dvbDevice ? dvbDevice->OsdDeviceHandle() : -1; - if (dvbDevice && osdDev < 0) + osdDev = OsdDev; + shown = false; + if (osdDev < 0) esyslog("ERROR: illegal OSD device handle (%d)!", osdDev); } cDvbOsd::~cDvbOsd() { - for (int i = 0; i < NumWindows(); i++) - CloseWindow(GetWindowNr(i)); -} - -void cDvbOsd::SetDvbDevice(const cDvbDevice *DvbDevice) -{ - dvbDevice = DvbDevice; -} - -bool cDvbOsd::SetWindow(cWindow *Window) -{ - if (Window) { - // Window handles are counted 0...(MAXNUMWINDOWS - 1), but the actual window - // numbers in the driver are used from 1...MAXNUMWINDOWS. - int Handle = Window->Handle(); - if (0 <= Handle && Handle < MAXNUMWINDOWS) { - Cmd(OSD_SetWindow, 0, Handle + 1); - return true; - } - esyslog("ERROR: illegal window handle: %d", Handle); + if (shown) { + cBitmap *Bitmap; + for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { + Cmd(OSD_SetWindow, 0, i + 1); + Cmd(OSD_Close); + } } - return false; +} + +eOsdError cDvbOsd::CanHandleAreas(const tArea *Areas, int NumAreas) +{ + eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas); + if (Result == oeOk) { + if (NumAreas > MAXNUMWINDOWS) + return oeTooManyAreas; + int TotalMemory = 0; + for (int i = 0; i < NumAreas; i++) { + if (Areas[i].bpp != 1 && Areas[i].bpp != 2 && Areas[i].bpp != 4 && Areas[i].bpp != 8) + return oeBppNotSupported; + if ((Areas[i].Width() & (8 / Areas[i].bpp - 1)) != 0) + return oeWrongAlignment; + TotalMemory += Areas[i].Width() * Areas[i].Height() / (8 / Areas[i].bpp); + } + if (TotalMemory > MAXOSDMEMORY) + return oeOutOfMemory; + } + return Result; } void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data) @@ -64,51 +73,78 @@ void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, co } } -bool cDvbOsd::OpenWindow(cWindow *Window) +void cDvbOsd::Flush(void) { - if (SetWindow(Window)) { - Cmd(OSD_Open, Window->Bpp(), X0() + Window->X0(), Y0() + Window->Y0(), X0() + Window->X0() + Window->Width() - 1, Y0() + Window->Y0() + Window->Height() - 1, (void *)1); // initially hidden! - return true; - } - return false; -} - -void cDvbOsd::CommitWindow(cWindow *Window) -{ - if (SetWindow(Window)) { - int x1 = 0, y1 = 0, x2 = 0, y2 = 0; - if (Window->Dirty(x1, y1, x2, y2)) { - // commit colors: - int FirstColor = 0, LastColor = 0; - const eDvbColor *pal; - while ((pal = Window->NewColors(FirstColor, LastColor)) != NULL) - Cmd(OSD_SetPalette, FirstColor, LastColor, 0, 0, 0, pal); - // commit modified data: - Cmd(OSD_SetBlock, Window->Width(), x1, y1, x2, y2, Window->Data(x1, y1)); - } + cBitmap *Bitmap; + for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { + Cmd(OSD_SetWindow, 0, i + 1); + if (!shown) + Cmd(OSD_Open, Bitmap->Bpp(), Left() + Bitmap->X0(), Top() + Bitmap->Y0(), Left() + Bitmap->X0() + Bitmap->Width() - 1, Top() + Bitmap->Y0() + Bitmap->Height() - 1, (void *)1); // initially hidden! + int x1 = 0, y1 = 0, x2 = 0, y2 = 0; + if (Bitmap->Dirty(x1, y1, x2, y2)) { + //TODO Workaround: apparently the bitmap sent to the driver always has to be a multiple + //TODO of 8 bits wide, and (dx * dy) also has to be a multiple of 8. + //TODO Fix driver (should be able to handle any size bitmaps!) + while ((x1 > 0 || x2 < Bitmap->Width() - 1) && ((x2 - x1) & 7) != 7) { + if (x2 < Bitmap->Width() - 1) + x2++; + else if (x1 > 0) + x1--; + } + //TODO "... / 2" <==> Bpp??? + while ((y1 > 0 || y2 < Bitmap->Height() - 1) && (((x2 - x1 + 1) * (y2 - y1 + 1) / 2) & 7) != 0) { + if (y2 < Bitmap->Height() - 1) + y2++; + else if (y1 > 0) + y1--; + } + while ((x1 > 0 || x2 < Bitmap->Width() - 1) && (((x2 - x1 + 1) * (y2 - y1 + 1) / 2) & 7) != 0) { + if (x2 < Bitmap->Width() - 1) + x2++; + else if (x1 > 0) + x1--; + } + // commit colors: + int NumColors; + const tColor *Colors = Bitmap->Colors(NumColors); + if (Colors) { + //TODO this should be fixed in the driver! + tColor colors[NumColors]; + for (int i = 0; i < NumColors; i++) { + // convert AARRGGBB to AABBGGRR (the driver expects the colors the wrong way): + colors[i] = (Colors[i] & 0xFF000000) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16); +#if __BYTE_ORDER == __BIG_ENDIAN + // actually the driver itself should access the bytes according to the current endianness! + colors[i] = ((colors[i] & 0xFF) << 24) | ((colors[i] & 0xFF00) << 8) | ((colors[i] & 0xFF0000) >> 8) | ((colors[i] & 0xFF000000) >> 24); +#endif + } + Colors = colors; + //TODO end of stuff that should be fixed in the driver + Cmd(OSD_SetPalette, 0, NumColors - 1, 0, 0, 0, Colors); + } + // commit modified data: + Cmd(OSD_SetBlock, Bitmap->Width(), x1, y1, x2, y2, Bitmap->Data(x1, y1)); + } + Bitmap->Clean(); + } + if (!shown) { + // Showing the windows in a separate loop to avoid seeing them come up one after another + for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) { + Cmd(OSD_SetWindow, 0, i + 1); + Cmd(OSD_MoveWindow, 0, Left() + Bitmap->X0(), Top() + Bitmap->Y0()); + } + shown = true; } } -void cDvbOsd::ShowWindow(cWindow *Window) +// --- cDvbOsdProvider ------------------------------------------------------- + +cDvbOsdProvider::cDvbOsdProvider(int OsdDev) { - if (SetWindow(Window)) - Cmd(OSD_MoveWindow, 0, X0() + Window->X0(), Y0() + Window->Y0()); + osdDev = OsdDev; } -void cDvbOsd::HideWindow(cWindow *Window, bool Hide) +cOsd *cDvbOsdProvider::CreateOsd(int Left, int Top) { - if (SetWindow(Window)) - Cmd(Hide ? OSD_Hide : OSD_Show, 0); -} - -void cDvbOsd::MoveWindow(cWindow *Window, int x, int y) -{ - if (SetWindow(Window)) - Cmd(OSD_MoveWindow, 0, X0() + x, Y0() + y); -} - -void cDvbOsd::CloseWindow(cWindow *Window) -{ - if (SetWindow(Window)) - Cmd(OSD_Close); + return new cDvbOsd(Left, Top, osdDev); } diff --git a/dvbosd.h b/dvbosd.h index cbc7901f..bac43f51 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.16 2002/11/01 10:05:47 kls Exp $ + * $Id: dvbosd.h 1.17 2004/04/30 13:44:16 kls Exp $ */ #ifndef __DVBOSD_H @@ -12,25 +12,26 @@ #include #include "dvbdevice.h" -#include "osdbase.h" +#include "osd.h" -class cDvbOsd : public cOsdBase { +class cDvbOsd : public cOsd { private: - static const cDvbDevice *dvbDevice; int osdDev; - bool SetWindow(cWindow *Window); + bool shown; void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL); -protected: - virtual bool OpenWindow(cWindow *Window); - virtual void CommitWindow(cWindow *Window); - virtual void ShowWindow(cWindow *Window); - virtual void HideWindow(cWindow *Window, bool Hide); - virtual void MoveWindow(cWindow *Window, int x, int y); - virtual void CloseWindow(cWindow *Window); public: - cDvbOsd(int x, int y); + cDvbOsd(int Left, int Top, int OsdDev); virtual ~cDvbOsd(); - static void SetDvbDevice(const cDvbDevice *DvbDevice); + virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); + virtual void Flush(void); + }; + +class cDvbOsdProvider : public cOsdProvider { +private: + int osdDev; +public: + cDvbOsdProvider(int OsdDev); + virtual cOsd *CreateOsd(int Left, int Top); }; #endif //__DVBOSD_H diff --git a/dvbspu.c b/dvbspu.c index 74ace19a..a7af8ee1 100644 --- a/dvbspu.c +++ b/dvbspu.c @@ -8,7 +8,7 @@ * * parts of this file are derived from the OMS program. * - * $Id: dvbspu.c 1.5 2003/10/12 09:43:18 kls Exp $ + * $Id: dvbspu.c 1.6 2004/04/30 13:45:02 kls Exp $ */ #include @@ -16,8 +16,6 @@ #include #include -#include "osd.h" -#include "osdbase.h" #include "device.h" #include "dvbspu.h" @@ -105,13 +103,13 @@ cBitmap *cDvbSpuBitmap::getBitmap(const aDvbSpuPalDescr paldescr, if (w & 0x03) w += 4 - (w & 0x03); - cBitmap *ret = new cBitmap(w, h, 2, true); + cBitmap *ret = new cBitmap(w, h, 2); // set the palette for (int i = 0; i < 4; i++) { uint32_t color = pal.getColor(paldescr[i].index, paldescr[i].trans); - ret->SetColor(i, (eDvbColor) color); + ret->SetColor(i, (tColor) color); } // set the content @@ -323,8 +321,9 @@ int cDvbSpuDecoder::ScaleYres(int value) void cDvbSpuDecoder::DrawBmp(sDvbSpuRect & size, cBitmap * bmp) { - osd->Create(size.x1, size.y1, size.width(), size.height(), 2, false); - osd->SetBitmap(size.x1, size.y1, *bmp); + tArea Area = { size.x1, size.y1, size.x2, size.y2, 2 }; + osd->SetAreas(&Area, 1); + osd->DrawBitmap(size.x1, size.y1, *bmp); delete bmp; } @@ -360,8 +359,8 @@ void cDvbSpuDecoder::Draw(void) if (bg || fg) { if (osd == NULL) - if ((osd = cOsd::OpenRaw(0, 0)) == NULL) { - dsyslog("OpenRaw failed\n"); + if ((osd = cOsdProvider::NewOsd(0, 0)) == NULL) { + dsyslog("NewOsd failed\n"); return; } diff --git a/dvbspu.h b/dvbspu.h index 11bf7207..4c926df3 100644 --- a/dvbspu.h +++ b/dvbspu.h @@ -8,7 +8,7 @@ * * parts of this file are derived from the OMS program. * - * $Id: dvbspu.h 1.2 2002/09/29 13:49:01 kls Exp $ + * $Id: dvbspu.h 1.3 2004/04/30 13:44:41 kls Exp $ */ #ifndef __DVBSPU_H @@ -16,7 +16,7 @@ #include -#include "osdbase.h" +#include "osd.h" #include "spu.h" typedef struct sDvbSpuPalDescr { @@ -91,7 +91,7 @@ class cDvbSpuBitmap { class cDvbSpuDecoder:public cSpuDecoder { private: - cOsdBase * osd; + cOsd * osd; // processing state uint8_t *spu; diff --git a/eitscan.c b/eitscan.c index 191a00e6..4e2bb426 100644 --- a/eitscan.c +++ b/eitscan.c @@ -4,14 +4,14 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: eitscan.c 1.21 2004/02/14 13:44:31 kls Exp $ + * $Id: eitscan.c 1.22 2004/04/16 13:33:34 kls Exp $ */ #include "eitscan.h" #include #include "channels.h" #include "dvbdevice.h" -#include "interface.h" +#include "skins.h" // --- cScanData ------------------------------------------------------------- @@ -149,7 +149,7 @@ void cEITScanner::Process(void) if ((!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= 0x0100) && Device->ProvidesTransponder(Channel)) { if (Device == cDevice::PrimaryDevice() && !currentChannel) { currentChannel = Device->CurrentChannel(); - Interface->Info("Starting EPG scan"); + Skins.Message(mtInfo, "Starting EPG scan"); } currentDevice = Device;//XXX see also dvbdevice.c!!! Device->SwitchChannel(Channel, false); diff --git a/font.c b/font.c index e933df23..af1f8717 100644 --- a/font.c +++ b/font.c @@ -4,9 +4,11 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: font.c 1.5 2004/01/16 13:17:57 kls Exp $ + * $Id: font.c 1.6 2004/04/24 15:25:48 kls Exp $ */ +#include "config.h" +#include #include "font.h" #include "tools.h" @@ -22,6 +24,8 @@ #include "fontosd-iso8859-7.c" #include "fontsml-iso8859-7.c" +// --- cFont ----------------------------------------------------------------- + static void *FontData[eDvbCodeSize][eDvbFontSize] = { { FontOsd_iso8859_1, FontFix_iso8859_1, FontSml_iso8859_1 }, { FontOsd_iso8859_5, FontFix_iso8859_5, FontSml_iso8859_5 }, @@ -44,9 +48,9 @@ cFont::cFont(void *Data) void cFont::SetData(void *Data) { - int h = ((tCharData *)Data)->height; + height = ((tCharData *)Data)->height; for (int i = 0; i < NUMCHARS; i++) - data[i] = (tCharData *)&((tPixelData *)Data)[(i < 32 ? 0 : i - 32) * (h + 2)]; + data[i] = (tCharData *)&((tPixelData *)Data)[(i < 32 ? 0 : i - 32) * (height + 2)]; } int cFont::Width(const char *s) const @@ -61,7 +65,7 @@ int cFont::Height(const char *s) const { int h = 0; if (s && *s) - h = Height(*s); // all characters have the same height! + h = height; // all characters have the same height! return h; } @@ -95,7 +99,131 @@ void cFont::SetFont(eDvbFont Font, void *Data) const cFont *cFont::GetFont(eDvbFont Font) { + if (Setup.UseSmallFont == 0 && Font == fontSml) + Font = fontOsd; + else if (Setup.UseSmallFont == 2 && Font == fontOsd) + Font = fontSml; if (!fonts[Font]) SetFont(Font); return fonts[Font]; } + +// --- cTextWrapper ---------------------------------------------------------- + +cTextWrapper::cTextWrapper(void) +{ + text = eol = NULL; + lines = 0; + lastLine = -1; +} + +cTextWrapper::cTextWrapper(const char *Text, const cFont *Font, int Width) +{ + text = NULL; + Set(Text, Font, Width); +} + +cTextWrapper::~cTextWrapper() +{ + free(text); +} + +void cTextWrapper::Set(const char *Text, const cFont *Font, int Width) +{ + free(text); + text = Text ? strdup(Text) : NULL; + eol = NULL; + lines = 0; + lastLine = -1; + if (!text) + return; + lines = 1; + if (Width <= 0) + return; + + char *Blank = NULL; + char *Delim = NULL; + int w = 0; + + stripspace(text); // strips trailing newlines + + for (char *p = text; *p; ) { + if (*p == '\n') { + lines++; + w = 0; + Blank = Delim = NULL; + p++; + continue; + } + else if (isspace(*p)) + Blank = p; + int cw = Font->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 = MALLOC(char, strlen(text) + 2); // The additional '\n' plus the terminating '\0' + int l = p - text; + strncpy(s, text, l); + s[l] = '\n'; + strcpy(s + l + 1, p); + free(text); + text = s; + p = text + l; + continue; + } + } + else + w += cw; + if (strchr("-.,:;!?_", *p)) { + Delim = p; + Blank = NULL; + } + p++; + } +} + +const char *cTextWrapper::Text(void) +{ + if (eol) { + *eol = '\n'; + eol = NULL; + } + return text; +} + +const char *cTextWrapper::GetLine(int Line) +{ + char *s = NULL; + if (Line < lines) { + if (eol) { + *eol = '\n'; + if (Line == lastLine + 1) + s = eol + 1; + eol = NULL; + } + if (!s) { + s = text; + for (int i = 0; i < Line; i++) { + s = strchr(s, '\n'); + if (s) + s++; + else + break; + } + } + if (s) { + if ((eol = strchr(s, '\n')) != NULL) + *eol = 0; + } + lastLine = Line; + } + return s; +} diff --git a/font.h b/font.h index 9c363160..aaad4f94 100644 --- a/font.h +++ b/font.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: font.h 1.5 2004/01/16 13:18:28 kls Exp $ + * $Id: font.h 1.6 2004/04/12 10:21:03 kls Exp $ */ #ifndef __FONT_H @@ -38,13 +38,20 @@ private: static eDvbCode code; static cFont *fonts[]; const tCharData *data[NUMCHARS]; + int height; public: cFont(void *Data); void SetData(void *Data); int Width(unsigned char c) const { return data[c]->width; } + ///< Returns the width of the given character. int Width(const char *s) const; + ///< Returns the width of the given string. int Height(unsigned char c) const { return data[c]->height; } + ///< Returns the height of the given character. int Height(const char *s) const; + ///< Returns the height of the given string. + int Height(void) const { return height; } + ///< Returns the height of this font (all characters have the same height). const tCharData *CharData(unsigned char c) const { return data[c]; } static bool SetCode(const char *Code); static void SetCode(eDvbCode Code); @@ -52,4 +59,27 @@ public: static const cFont *GetFont(eDvbFont Font); }; +class cTextWrapper { +private: + char *text; + char *eol; + int lines; + int lastLine; +public: + cTextWrapper(void); + cTextWrapper(const char *Text, const cFont *Font, int Width); + ~cTextWrapper(); + void Set(const char *Text, const cFont *Font, int Width); + ///< Wraps the Text to make it fit into the area defined by the given Width + ///< when displayed with the given Font. + ///< Wrapping is done by inserting the necessary number of newline + ///< characters into the string. + const char *Text(void); + ///< Returns the full wrapped text. + int Lines(void) { return lines; } + ///< Returns the actual number of lines needed to display the full wrapped text. + const char *GetLine(int Line); + ///< Returns the given Line. The first line is numbered 0. + }; + #endif //__FONT_H diff --git a/i18n.c b/i18n.c index 714d98f3..00c96d9a 100644 --- a/i18n.c +++ b/i18n.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 1.153 2004/03/26 15:02:59 kls Exp $ + * $Id: i18n.c 1.154 2004/05/08 09:25:02 kls Exp $ * * Translations provided by: * @@ -2193,6 +2193,78 @@ const tI18nPhrase Phrases[] = { "Idioma", "Ï×ëÚ", }, + { "Setup.OSD$Skin", + "Oberfläche", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Setup.OSD$Theme", + "Thema", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Setup.OSD$Left", + "Links", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "Setup.OSD$Top", + "Oben", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { "Setup.OSD$Width", "Breite", "Sirina", @@ -2247,6 +2319,78 @@ const tI18nPhrase Phrases[] = { "Durada dels missatges (s)", "´ÛØâÕÛìÝÞáâì ßÞÚÐ×Ð áÞÞÑéÕÝØÙ (áÕÚ)", }, + { "Setup.OSD$Use small font", + "Kleine Schrift benutzen", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "never", + "nie", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "skin dependent", + "je nach Oberfläche", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "always", + "immer", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { "Setup.OSD$Channel info position", "Kanal-Info Position", "Pozicija informacije o kanalu", @@ -4214,6 +4358,42 @@ const tI18nPhrase Phrases[] = { "Aquest plugin no admet configuració!", "¼ÞÔãÛì ÝÕ ØÜÕÕâ ßÐàÐÜÕâàÞÒ ÝÐáâàÞÙÚØ!", }, + { "Classic VDR", + "Klassischer VDR", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { "ST:TNG Panels", + "ST:TNG Konsolen", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, { NULL } }; diff --git a/interface.c b/interface.c index 379b7862..06ba331a 100644 --- a/interface.c +++ b/interface.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.c 1.66 2003/10/24 15:48:00 kls Exp $ + * $Id: interface.c 1.67 2004/05/02 10:57:06 kls Exp $ */ #include "interface.h" @@ -12,16 +12,12 @@ #include #include #include "i18n.h" -#include "osd.h" #include "status.h" cInterface *Interface = NULL; cInterface::cInterface(int SVDRPport) { - open = 0; - cols[0] = 0; - width = height = 0; interrupted = false; SVDRP = NULL; if (SVDRPport) @@ -33,38 +29,17 @@ cInterface::~cInterface() delete SVDRP; } -void cInterface::Open(int NumCols, int NumLines) -{ - if (!open++) { - if (NumCols == 0) - NumCols = Setup.OSDwidth; - if (NumLines == 0) - NumLines = Setup.OSDheight; - cOsd::Open(width = NumCols, height = NumLines); - } -} - -void cInterface::Close(void) -{ - if (open == 1) - Clear(); - if (!--open) { - cOsd::Close(); - width = height = 0; - } -} - eKeys cInterface::GetKey(bool Wait) { if (!cRemote::HasKeys()) - Flush(); + Skins.Flush(); if (SVDRP) { if (SVDRP->Process()) Wait = false; - if (!open) { + if (!Skins.IsOpen()) { char *message = SVDRP->GetMessage(); if (message) { - Info(message); + Skins.Message(mtInfo, message); free(message); } } @@ -76,7 +51,7 @@ eKeys cInterface::Wait(int Seconds, bool KeepChar) { if (Seconds == 0) Seconds = Setup.OSDMessageTime; - Flush(); + Skins.Flush(); eKeys Key = kNone; time_t timeout = time(NULL) + Seconds; for (;;) { @@ -90,267 +65,37 @@ eKeys cInterface::Wait(int Seconds, bool KeepChar) return Key; } -void cInterface::Clear(void) -{ - if (open) - cOsd::Clear(); - cStatus::MsgOsdClear(); -} - -void cInterface::ClearEol(int x, int y, eDvbColor Color) -{ - if (open) - cOsd::ClrEol(x, y, Color); -} - -void cInterface::Fill(int x, int y, int w, int h, eDvbColor Color) -{ - if (open) - cOsd::Fill(x, y, w, h, Color); -} - -void cInterface::SetBitmap(int x, int y, const cBitmap &Bitmap) -{ - if (open) - cOsd::SetBitmap(x, y, Bitmap); -} - -void cInterface::Flush(void) -{ - if (open) - cOsd::Flush(); -} - -void cInterface::SetCols(int *c) -{ - for (int i = 0; i < MaxCols; i++) { - cols[i] = *c++; - if (cols[i] == 0) - break; - } -} - -eDvbFont cInterface::SetFont(eDvbFont Font) -{ - return cOsd::SetFont(Font); -} - -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 *= cOsd::CellWidth(); - - while (*t && t[strlen(t) - 1] == '\n') - t[strlen(t) - 1] = 0; // skips trailing newlines - - for (char *p = t; *p; ) { - if (*p == '|') - *p = '\n'; - if (*p == '\n') { - Lines++; - w = 0; - Blank = Delim = NULL; - p++; - continue; - } - else if (isspace(*p)) - Blank = p; - int cw = cOsd::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 = MALLOC(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); - free(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) - cOsd::Text(x, y, s, FgColor, BgColor); -} - -void cInterface::WriteText(int x, int y, const char *s, eDvbColor FgColor, eDvbColor BgColor) -{ - if (open) { - ClearEol(x, y, BgColor); - int col = 0; - for (;;) { - const char *t = strchr(s, '\t'); - const char *p = s; - char buf[1000]; - if (t && col < MaxCols && cols[col] > 0) { - unsigned int n = t - s; - if (n >= sizeof(buf)) - n = sizeof(buf) - 1; - strncpy(buf, s, n); - buf[n] = 0; - p = buf; - s = t + 1; - } - Write(x, y, p, FgColor, BgColor); - if (p == s) - break; - x += cols[col++]; - } - } -} - -void cInterface::Title(const char *s) -{ - ClearEol(0, 0, clrCyan); - const char *t = strchr(s, '\t'); - if (t) { - char buffer[Width() + 1]; - unsigned int n = t - s; - if (n >= sizeof(buffer)) - n = sizeof(buffer) - 1; - strn0cpy(buffer, s, n + 1); - Write(1, 0, buffer, clrBlack, clrCyan); - t++; - Write(-(cOsd::WidthInCells(t) + 1), 0, t, clrBlack, clrCyan); - } - else { - int x = (Width() - strlen(s)) / 2; - if (x < 0) - x = 0; - Write(x, 0, s, clrBlack, clrCyan); - } - cStatus::MsgOsdTitle(s); -} - -void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor) -{ - int Line = (abs(height) == 1) ? 0 : -2; - ClearEol(0, Line, s ? BgColor : clrBackground); - if (s) { - int x = (Width() - int(strlen(s))) / 2; - if (x < 0) - x = 0; - Write(x, Line, s, FgColor, BgColor); - } - cStatus::MsgOsdStatusMessage(s); -} - -void cInterface::Info(const char *s) -{ - Open(Setup.OSDwidth, -1); - isyslog("info: %s", s); - Status(s, clrBlack, clrGreen); - Wait(); - Status(NULL); - Close(); -} - -void cInterface::Error(const char *s) -{ - Open(Setup.OSDwidth, -1); - esyslog("ERROR: %s", s); - Status(s, clrWhite, clrRed); - Wait(); - Status(NULL); - Close(); -} - bool cInterface::Confirm(const char *s, int Seconds, bool WaitForTimeout) { - Open(Setup.OSDwidth, -1); isyslog("confirm: %s", s); - Status(s, clrBlack, clrYellow); - eKeys k = Wait(Seconds); + eKeys k = Skins.Message(mtWarning, s, Seconds); bool result = WaitForTimeout ? k == kNone : k == kOk; - Status(NULL); - Close(); isyslog("%sconfirmed", result ? "" : "not "); return result; } -void cInterface::HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor) +bool cInterface::QueryKeys(cRemote *Remote, cSkinDisplayMenu *DisplayMenu) { - if (open) { - const int w = Width() / 4; - cOsd::Fill(Index * w, -1, w, 1, Text ? BgColor : clrBackground); - if (Text) { - int l = (w - int(strlen(Text))) / 2; - if (l < 0) - l = 0; - cOsd::Text(Index * w + l, -1, Text, FgColor, BgColor); - } - } -} - -void cInterface::Help(const char *Red, const char *Green, const char *Yellow, const char *Blue) -{ - HelpButton(0, Red, clrBlack, clrRed); - HelpButton(1, Green, clrBlack, clrGreen); - HelpButton(2, Yellow, clrBlack, clrYellow); - HelpButton(3, Blue, clrWhite, clrBlue); - cStatus::MsgOsdHelpKeys(Red, Green, Yellow, Blue); -} - -bool cInterface::QueryKeys(cRemote *Remote) -{ - WriteText(1, 3, tr("Phase 1: Detecting RC code type")); - WriteText(1, 5, tr("Press any key on the RC unit")); - Flush(); + DisplayMenu->SetItem(tr("Phase 1: Detecting RC code type"), 1, false, false); + DisplayMenu->SetItem(tr("Press any key on the RC unit"), 3, false, false); + DisplayMenu->Flush(); if (Remote->Initialize()) { - WriteText(1, 5, tr("RC code detected!")); - WriteText(1, 6, tr("Do not press any key...")); - Flush(); + DisplayMenu->SetItem(tr("RC code detected!"), 3, false, false); + DisplayMenu->SetItem(tr("Do not press any key..."), 4, false, false); + DisplayMenu->Flush(); sleep(3); - ClearEol(0, 5); - ClearEol(0, 6); + DisplayMenu->SetItem("", 3, false, false); + DisplayMenu->SetItem("", 4, false, false); - WriteText(1, 3, tr("Phase 2: Learning specific key codes")); + DisplayMenu->SetItem(tr("Phase 2: Learning specific key codes"), 1, false, false); eKeys NewKey = kUp; while (NewKey != kNone) { char *Prompt; asprintf(&Prompt, tr("Press key for '%s'"), tr(cKey::ToString(NewKey))); - WriteText(1, 5, Prompt); + DisplayMenu->SetItem(Prompt, 3, false, false); free(Prompt); cRemote::Clear(); - Flush(); + DisplayMenu->Flush(); for (eKeys k = NewKey; k == NewKey; ) { char *NewCode = NULL; eKeys Key = cRemote::Get(100, &NewCode); @@ -362,20 +107,20 @@ bool cInterface::QueryKeys(cRemote *Remote) Keys.Del(last); } break; - case kDown: WriteText(1, 5, tr("Press 'Up' to confirm")); - WriteText(1, 6, tr("Press 'Down' to continue")); - ClearEol(0, 7); - ClearEol(0, 8); - ClearEol(0, 9); - Flush(); + case kDown: DisplayMenu->SetItem(tr("Press 'Up' to confirm"), 3, false, false); + DisplayMenu->SetItem(tr("Press 'Down' to continue"), 4, false, false); + DisplayMenu->SetItem("", 5, false, false); + DisplayMenu->SetItem("", 6, false, false); + DisplayMenu->SetItem("", 7, false, false); + DisplayMenu->Flush(); for (;;) { Key = cRemote::Get(100); if (Key == kUp) { - Clear(); + DisplayMenu->Clear(); return true; } else if (Key == kDown) { - ClearEol(0, 6); + DisplayMenu->SetItem("", 4, false, false); k = kNone; // breaks the outer for() loop break; } @@ -394,17 +139,17 @@ bool cInterface::QueryKeys(cRemote *Remote) } } if (NewKey > kUp) - WriteText(1, 7, tr("(press 'Up' to go back)")); + DisplayMenu->SetItem(tr("(press 'Up' to go back)"), 5, false, false); else - ClearEol(0, 7); + DisplayMenu->SetItem("", 5, false, false); if (NewKey > kDown) - WriteText(1, 8, tr("(press 'Down' to end key definition)")); + DisplayMenu->SetItem(tr("(press 'Down' to end key definition)"), 6, false, false); else - ClearEol(0, 8); + DisplayMenu->SetItem("", 6, false, false); if (NewKey > kMenu) - WriteText(1, 9, tr("(press 'Menu' to skip this key)")); + DisplayMenu->SetItem(tr("(press 'Menu' to skip this key)"), 7, false, false); else - ClearEol(0, 9); + DisplayMenu->SetItem("", 7, false, false); } return true; } @@ -421,33 +166,31 @@ void cInterface::LearnKeys(void) bool known = Keys.KnowsRemote(Remote->Name()); dsyslog("remote control %s - %s", Remote->Name(), known ? "keys known" : "learning keys"); if (!known) { - Open(); - char Headline[Width()]; + cSkinDisplayMenu *DisplayMenu = Skins.Current()->DisplayMenu(); + char Headline[256]; snprintf(Headline, sizeof(Headline), tr("Learning Remote Control Keys (%s)"), Remote->Name()); - Clear(); cRemote::Clear(); - WriteText(1, 1, Headline); + DisplayMenu->SetTitle(Headline); cRemote::SetLearning(Remote); - bool rc = QueryKeys(Remote); + bool rc = QueryKeys(Remote, DisplayMenu); cRemote::SetLearning(NULL); - Clear(); + DisplayMenu->Clear(); if (!rc) { - Close(); + delete DisplayMenu; continue; } - WriteText(1, 1, Headline); - WriteText(1, 3, tr("Phase 3: Saving key codes")); - WriteText(1, 5, tr("Press 'Up' to save, 'Down' to cancel")); + DisplayMenu->SetItem(tr("Phase 3: Saving key codes"), 1, false, false); + DisplayMenu->SetItem(tr("Press 'Up' to save, 'Down' to cancel"), 3, false, false); for (;;) { eKeys key = GetKey(); if (key == kUp) { Keys.Save(); - Close(); + delete DisplayMenu; break; } else if (key == kDown) { Keys.Load(); - Close(); + delete DisplayMenu; break; } } diff --git a/interface.h b/interface.h index bcc29df0..2b3f979a 100644 --- a/interface.h +++ b/interface.h @@ -4,57 +4,30 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.h 1.30 2003/10/05 09:42:53 kls Exp $ + * $Id: interface.h 1.31 2004/05/01 11:11:13 kls Exp $ */ #ifndef __INTERFACE_H #define __INTERFACE_H #include "config.h" -#include "osdbase.h" #include "remote.h" +#include "skins.h" #include "svdrp.h" class cInterface { -public: - enum { MaxCols = 5 }; private: - int width, height; - int open; - int cols[MaxCols]; bool interrupted; cSVDRP *SVDRP; - bool QueryKeys(cRemote *Remote); - void HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor); - eKeys Wait(int Seconds = 0, bool KeepChar = false); + bool QueryKeys(cRemote *Remote, cSkinDisplayMenu *DisplayMenu); public: cInterface(int SVDRPport = 0); ~cInterface(); - bool IsOpen(void) { return open > 0; } - void Open(int NumCols = 0, int NumLines = 0); - void Close(void); bool HasSVDRPConnection(void) { return SVDRP && SVDRP->HasConnection(); } void Interrupt(void) { interrupted = true; } - int Width(void) { return width; } - int Height(void) { return height; } eKeys GetKey(bool Wait = true); - void Clear(void); - void ClearEol(int x, int y, eDvbColor Color = clrBackground); - void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground); - void SetBitmap(int x, int y, const cBitmap &Bitmap); - void Flush(void); - void SetCols(int *c); - const int *GetCols(void) { return cols; } - eDvbFont SetFont(eDvbFont Font); - 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); - void Status(const char *s, eDvbColor FgColor = clrBlack, eDvbColor BgColor = clrCyan); - void Info(const char *s); - void Error(const char *s); + eKeys Wait(int Seconds = 0, bool KeepChar = false); bool Confirm(const char *s, int Seconds = 10, bool WaitForTimeout = false); - void Help(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); void LearnKeys(void); }; diff --git a/menu.c b/menu.c index 3e29fd67..d6d06174 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.298 2004/03/28 09:17:15 kls Exp $ + * $Id: menu.c 1.299 2004/05/15 14:15:33 kls Exp $ */ #include "menu.h" @@ -18,12 +18,14 @@ #include "cutter.h" #include "eitscan.h" #include "i18n.h" +#include "interface.h" #include "menuitems.h" #include "plugin.h" #include "recording.h" #include "remote.h" #include "sources.h" #include "status.h" +#include "themes.h" #include "timers.h" #include "transfer.h" #include "videodir.h" @@ -309,7 +311,7 @@ eOSState cMenuEditChannel::ProcessKey(eKeys Key) Channels.SetModified(); } else { - Interface->Error(tr("Channel settings are not unique!")); + Skins.Message(mtError, tr("Channel settings are not unique!")); state = osContinue; } } @@ -334,7 +336,7 @@ cMenuChannelItem::cMenuChannelItem(cChannel *Channel) { channel = Channel; if (channel->GroupSep()) - SetColor(clrCyan, clrBackground); + SetSelectable(false); Set(); } @@ -433,7 +435,7 @@ eOSState cMenuChannels::Delete(void) // Check if there is a timer using this channel: for (cTimer *ti = Timers.First(); ti; ti = Timers.Next(ti)) { if (ti->Channel() == channel) { - Interface->Error(tr("Channel is being used by a timer!")); + Skins.Message(mtError, tr("Channel is being used by a timer!")); return osContinue; } } @@ -498,19 +500,42 @@ eOSState cMenuChannels::ProcessKey(eKeys Key) // --- cMenuText ------------------------------------------------------------- class cMenuText : public cOsdMenu { +private: + const char *text; public: cMenuText(const char *Title, const char *Text, eDvbFont Font = fontOsd); + virtual void Display(void); virtual eOSState ProcessKey(eKeys Key); }; cMenuText::cMenuText(const char *Title, const char *Text, eDvbFont Font) :cOsdMenu(Title) { - Add(new cMenuTextItem(Text, 1, 2, Setup.OSDwidth - 2, MAXOSDITEMS, clrWhite, clrBackground, Font)); + text = Text; +} + +void cMenuText::Display(void) +{ + cOsdMenu::Display(); + DisplayMenu()->SetText(text, true);//XXX define control character in text to choose the font??? } eOSState cMenuText::ProcessKey(eKeys Key) { + 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); + return osContinue; + default: break; + } + eOSState state = cOsdMenu::ProcessKey(Key); if (state == osUnknown) { @@ -595,7 +620,7 @@ eOSState cMenuEditTimer::ProcessKey(eKeys Key) if (ch) data.channel = ch; else { - Interface->Error(tr("*** Invalid Channel ***")); + Skins.Message(mtError, tr("*** Invalid Channel ***")); break; } if (!*data.file) @@ -775,6 +800,7 @@ eOSState cMenuTimers::Summary(void) cTimer *ti = CurrentTimer(); if (ti && !isempty(ti->Summary())) return AddSubMenu(new cMenuText(tr("Summary"), ti->Summary())); + //XXX cSkin::SetRecording()??? return Edit(); // convenience for people not using the Summary feature ;-) } @@ -812,7 +838,7 @@ private: const cEvent *event; public: cMenuEvent(const cEvent *Event, bool CanSwitch = false); - cMenuEvent(bool Now); + virtual void Display(void); virtual eOSState ProcessKey(eKeys Key); }; @@ -823,39 +849,34 @@ cMenuEvent::cMenuEvent(const cEvent *Event, bool CanSwitch) if (event) { cChannel *channel = Channels.GetByChannelID(event->ChannelID(), true); if (channel) { - char *buffer; - asprintf(&buffer, "%-17.*s\t%.*s %s - %s", 17, channel->Name(), 5, event->GetDateString(), event->GetTimeString(), event->GetEndTimeString()); - SetTitle(buffer, false); - free(buffer); - int Line = 2; - cMenuTextItem *item; - const char *Title = event->Title(); - const char *Subtitle = event->ShortText(); - const char *ExtendedDescription = event->Description(); - if (!isempty(Title)) { - Add(item = new cMenuTextItem(Title, 1, Line, Setup.OSDwidth - 2, -1, clrCyan)); - Line += item->Height() + 1; - } - if (!isempty(Subtitle)) { - Add(item = new cMenuTextItem(Subtitle, 1, Line, Setup.OSDwidth - 2, -1, clrYellow)); - Line += item->Height() + 1; - } - if (Event->Vps() && Event->Vps() != Event->StartTime()) { - char *buffer; - asprintf(&buffer, "VPS: %s", Event->GetVpsString()); - Add(item = new cMenuTextItem(buffer, 1, Line, Setup.OSDwidth - 2, -1, clrYellow)); - free(buffer); - Line += item->Height() + 1; - } - if (!isempty(ExtendedDescription)) - Add(new cMenuTextItem(ExtendedDescription, 1, Line, Setup.OSDwidth - 2, Height() - Line - 2, clrCyan), true); + SetTitle(channel->Name()); SetHelp(tr("Record"), NULL, NULL, CanSwitch ? tr("Switch") : NULL); } } } +void cMenuEvent::Display(void) +{ + cOsdMenu::Display(); + DisplayMenu()->SetEvent(event); +} + eOSState cMenuEvent::ProcessKey(eKeys Key) { + 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); + return osContinue; + default: break; + } + eOSState state = cOsdMenu::ProcessKey(Key); if (state == osUnknown) { @@ -942,7 +963,7 @@ eOSState cMenuWhatsOn::Switch(void) if (channel && cDevice::PrimaryDevice()->SwitchChannel(channel, true)) return osEnd; } - Interface->Error(tr("Can't switch channel!")); + Skins.Message(mtError, tr("Can't switch channel!")); return osContinue; } @@ -1086,7 +1107,7 @@ eOSState cMenuSchedule::Switch(void) if (Channels.SwitchTo(otherChannel)) return osEnd; } - Interface->Error(tr("Can't switch channel!")); + Skins.Message(mtError, tr("Can't switch channel!")); return osContinue; } @@ -1184,10 +1205,10 @@ eOSState cMenuCommands::Execute(void) } if (confirmed) { asprintf(&buffer, "%s...", command->Title()); - Interface->Status(buffer); - Interface->Flush(); + Skins.Message(mtStatus, buffer); free(buffer); const char *Result = command->Execute(parameters); + Skins.Message(mtStatus, NULL); if (Result) return AddSubMenu(new cMenuText(command->Title(), Result, fontFix)); return osEnd; @@ -1388,11 +1409,11 @@ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus) base = Base ? strdup(Base) : NULL; level = Setup.RecordingDirs ? Level : -1; Display(); // this keeps the higher level menus from showing up briefly when pressing 'Back' during replay - if (!Base) { - Interface->Status(tr("scanning recordings...")); - Interface->Flush(); - } + if (!Base) + Skins.Message(mtStatus, tr("scanning recordings...")); if (Base || Recordings.Load()) { + if (!Base) + Skins.Message(mtStatus, NULL); const char *LastReplayed = cReplayControl::LastReplayed(); cMenuRecordingItem *LastItem = NULL; char *LastItemText = NULL; @@ -1459,7 +1480,7 @@ cRecording *cMenuRecordings::GetRecording(cMenuRecordingItem *Item) { cRecording *recording = Recordings.GetByName(Item->FileName()); if (!recording) - Interface->Error(tr("Error while accessing recording!")); + Skins.Message(mtError, tr("Error while accessing recording!")); return recording; } @@ -1547,7 +1568,7 @@ eOSState cMenuRecordings::Delete(void) return osBack; } else - Interface->Error(tr("Error while deleting recording!")); + Skins.Message(mtError, tr("Error while deleting recording!")); } } } @@ -1641,39 +1662,101 @@ void cMenuSetupBase::Store(void) class cMenuSetupOSD : public cMenuSetupBase { private: + const char *useSmallFontTexts[3]; + int numSkins; + int originalSkinIndex; + int skinIndex; + const char **skinDescriptions; + cThemes themes; + int themeIndex; virtual void Set(void); public: - cMenuSetupOSD(void) { Set(); } - virtual ~cMenuSetupOSD() { cFont::SetCode(I18nCharSets()[Setup.OSDLanguage]); } + cMenuSetupOSD(void); + virtual ~cMenuSetupOSD(); virtual eOSState ProcessKey(eKeys Key); }; +cMenuSetupOSD::cMenuSetupOSD(void) +{ + numSkins = Skins.Count(); + skinIndex = originalSkinIndex = Skins.Current()->Index(); + skinDescriptions = new const char*[numSkins]; + themes.Load(Skins.Current()->Name()); + themeIndex = Skins.Current()->Theme() ? themes.GetThemeIndex(Skins.Current()->Theme()->Description()) : 0; + Set(); +} + +cMenuSetupOSD::~cMenuSetupOSD() +{ + cFont::SetCode(I18nCharSets()[Setup.OSDLanguage]); + delete skinDescriptions; +} + void cMenuSetupOSD::Set(void) { + int current = Current(); + for (cSkin *Skin = Skins.First(); Skin; Skin = Skins.Next(Skin)) + skinDescriptions[Skin->Index()] = Skin->Description(); + useSmallFontTexts[0] = tr("never"); + useSmallFontTexts[1] = tr("skin dependent"); + useSmallFontTexts[2] = tr("always"); Clear(); SetSection(tr("OSD")); Add(new cMenuEditStraItem(tr("Setup.OSD$Language"), &data.OSDLanguage, I18nNumLanguages, I18nLanguages())); - Add(new cMenuEditIntItem( tr("Setup.OSD$Width"), &data.OSDwidth, MINOSDWIDTH, MAXOSDWIDTH)); - Add(new cMenuEditIntItem( tr("Setup.OSD$Height"), &data.OSDheight, MINOSDHEIGHT, MAXOSDHEIGHT)); + Add(new cMenuEditStraItem(tr("Setup.OSD$Skin"), &skinIndex, numSkins, skinDescriptions)); + if (themes.NumThemes()) + Add(new cMenuEditStraItem(tr("Setup.OSD$Theme"), &themeIndex, themes.NumThemes(), themes.Descriptions())); + Add(new cMenuEditIntItem( tr("Setup.OSD$Left"), &data.OSDLeft, 0, MAXOSDWIDTH)); + Add(new cMenuEditIntItem( tr("Setup.OSD$Top"), &data.OSDTop, 0, MAXOSDHEIGHT)); + Add(new cMenuEditIntItem( tr("Setup.OSD$Width"), &data.OSDWidth, MINOSDWIDTH, MAXOSDWIDTH)); + Add(new cMenuEditIntItem( tr("Setup.OSD$Height"), &data.OSDHeight, MINOSDHEIGHT, MAXOSDHEIGHT)); Add(new cMenuEditIntItem( tr("Setup.OSD$Message time (s)"), &data.OSDMessageTime, 1, 60)); + Add(new cMenuEditStraItem(tr("Setup.OSD$Use small font"), &data.UseSmallFont, 3, useSmallFontTexts)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Channel info position"), &data.ChannelInfoPos, tr("bottom"), tr("top"))); Add(new cMenuEditBoolItem(tr("Setup.OSD$Info on channel switch"), &data.ShowInfoOnChSwitch)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll pages"), &data.MenuScrollPage)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Sort timers"), &data.SortTimers)); Add(new cMenuEditBoolItem(tr("Setup.OSD$Recording directories"), &data.RecordingDirs)); + SetCurrent(Get(current)); + Display(); } eOSState cMenuSetupOSD::ProcessKey(eKeys Key) { + if (Key == kOk) { + if (skinIndex != originalSkinIndex) { + cSkin *Skin = Skins.Get(skinIndex); + if (Skin) { + strn0cpy(data.OSDSkin, Skin->Name(), sizeof(data.OSDSkin)); + Skins.SetCurrent(Skin->Name()); + } + } + if (themes.NumThemes() && Skins.Current()->Theme()) { + Skins.Current()->Theme()->Load(themes.FileName(themeIndex)); + strn0cpy(data.OSDTheme, themes.Name(themeIndex), sizeof(data.OSDTheme)); + } + data.OSDWidth &= ~0x07; // OSD width must be a multiple of 8 + } + int osdLanguage = data.OSDLanguage; + int oldSkinIndex = skinIndex; eOSState state = cMenuSetupBase::ProcessKey(Key); - if (data.OSDLanguage != osdLanguage) { + if (data.OSDLanguage != osdLanguage || skinIndex != oldSkinIndex) { int OriginalOSDLanguage = Setup.OSDLanguage; Setup.OSDLanguage = data.OSDLanguage; cFont::SetCode(I18nCharSets()[Setup.OSDLanguage]); + + cSkin *Skin = Skins.Get(skinIndex); + if (Skin) { + char *d = themes.NumThemes() ? strdup(themes.Descriptions()[themeIndex]) : NULL; + themes.Load(Skin->Name()); + if (skinIndex != oldSkinIndex) + themeIndex = d ? themes.GetThemeIndex(d) : 0; + free(d); + } + Set(); - Display(); Setup.OSDLanguage = OriginalOSDLanguage; } return state; @@ -1912,7 +1995,7 @@ eOSState cMenuSetupCICAM::Menu(void) if (CiHandler && CiHandler->EnterMenu(Slot)) return osEnd; // the CAM menu will be executed explicitly from the main loop else - Interface->Error(tr("Can't open CAM menu!")); + Skins.Message(mtError, tr("Can't open CAM menu!")); return osContinue; } @@ -1921,11 +2004,11 @@ eOSState cMenuSetupCICAM::Reset(void) int Slot = 0; cCiHandler *CiHandler = GetCurrentCiHandler(&Slot); if (CiHandler && CiHandler->Reset(Slot)) { - Interface->Info(tr("CAM has been reset")); + Skins.Message(mtInfo, tr("CAM has been reset")); return osEnd; } else - Interface->Error(tr("Can't reset CAM!")); + Skins.Message(mtError, tr("Can't reset CAM!")); return osContinue; } @@ -2064,7 +2147,7 @@ eOSState cMenuSetupPlugins::ProcessKey(eKeys Key) menu->SetPlugin(p); return AddSubMenu(menu); } - Interface->Info(tr("This plugin has no setup parameters!")); + Skins.Message(mtInfo, tr("This plugin has no setup parameters!")); } } } @@ -2199,7 +2282,7 @@ cOsdObject *cMenuMain::PluginOsdObject(void) void cMenuMain::Set(const char *Plugin) { Clear(); - //SetTitle("VDR"); // this is done below, including disk usage + //XXX //SetTitle("VDR"); // this is done below, including disk usage SetHasHotkeys(); // Title with disk usage: @@ -2213,6 +2296,7 @@ void cMenuMain::Set(const char *Plugin) int Hours = Minutes / 60; Minutes %= 60; snprintf(buffer, sizeof(buffer), "%s - %s %d%% - %2d:%02d %s", tr("VDR"), tr("Disk"), Percent, Hours, Minutes, tr("free")); + //XXX -> skin function!!! SetTitle(buffer); // Basic menu items: @@ -2337,9 +2421,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) const char **at = &AudioTracks[CurrentAudioTrack]; if (!*++at) at = AudioTracks; - Interface->Clear(); cDevice::PrimaryDevice()->SetAudioTrack(at - AudioTracks); - //XXX Interface->Info(*at); state = osEnd; } } @@ -2376,14 +2458,14 @@ cDisplayChannel::cDisplayChannel(int Number, bool Switched) { group = -1; withInfo = !Switched || Setup.ShowInfoOnChSwitch; - int EpgLines = withInfo ? 5 : 1; - lines = 0; + displayChannel = Skins.Current()->DisplayChannel(withInfo); number = 0; channel = Channels.GetByNumber(Number); - Interface->Open(Setup.OSDwidth, Setup.ChannelInfoPos ? EpgLines : -EpgLines); + lastPresent = lastFollowing = NULL; if (channel) { DisplayChannel(); DisplayInfo(); + displayChannel->Flush(); } lastTime = time_ms(); } @@ -2392,94 +2474,41 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey) :cOsdObject(true) { group = -1; - lines = 0; number = 0; + lastPresent = lastFollowing = NULL; lastTime = time_ms(); withInfo = Setup.ShowInfoOnChSwitch; - int EpgLines = withInfo ? 5 : 1; - Interface->Open(Setup.OSDwidth, Setup.ChannelInfoPos ? EpgLines : -EpgLines); + displayChannel = Skins.Current()->DisplayChannel(withInfo); ProcessKey(FirstKey); } cDisplayChannel::~cDisplayChannel() { - Interface->Close(); + delete displayChannel; } void cDisplayChannel::DisplayChannel(void) { - int BufSize = Width() + 1; - char buffer[BufSize]; - *buffer = 0; - if (channel) { - if (channel->GroupSep()) - snprintf(buffer, BufSize, "%s", channel->Name()); - else - snprintf(buffer, BufSize, "%d%s %s", channel->Number(), number ? "-" : "", channel->Name()); - } - else if (number) - snprintf(buffer, BufSize, "%d-", number); - else - snprintf(buffer, BufSize, "%s", tr("*** Invalid Channel ***")); - Interface->Fill(0, 0, Setup.OSDwidth, 1, clrBackground); - Interface->Write(0, 0, buffer); - const char *date = DayDateTime(); - Interface->Write(-strlen(date), 0, date); - cStatus::MsgOsdChannel(buffer); + displayChannel->SetChannel(channel, number); + cStatus::MsgOsdChannel(ChannelString(channel, number)); + lastPresent = lastFollowing = NULL; } void cDisplayChannel::DisplayInfo(void) { if (withInfo && channel) { - const cEvent *Present = NULL, *Following = NULL; cSchedulesLock SchedulesLock; const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); if (Schedules) { const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID()); if (Schedule) { - const char *PresentTitle = NULL, *PresentSubtitle = NULL, *FollowingTitle = NULL, *FollowingSubtitle = NULL; - int Lines = 0; - if ((Present = Schedule->GetPresentEvent(true)) != NULL) { - PresentTitle = Present->Title(); - if (!isempty(PresentTitle)) - Lines++; - PresentSubtitle = Present->ShortText(); - if (!isempty(PresentSubtitle)) - Lines++; - } - if ((Following = Schedule->GetFollowingEvent(true)) != NULL) { - FollowingTitle = Following->Title(); - if (!isempty(FollowingTitle)) - Lines++; - FollowingSubtitle = Following->ShortText(); - if (!isempty(FollowingSubtitle)) - Lines++; - } - if (Lines > lines) { - const int t = 6; - int l = 1; - Interface->Fill(0, 1, Setup.OSDwidth, Lines, clrBackground); - if (!isempty(PresentTitle)) { - Interface->Write(0, l, Present->GetTimeString(), clrYellow, clrBackground); - Interface->Write(t, l, PresentTitle, clrCyan, clrBackground); - l++; - } - if (!isempty(PresentSubtitle)) { - Interface->Write(t, l, PresentSubtitle, clrCyan, clrBackground); - l++; - } - if (!isempty(FollowingTitle)) { - Interface->Write(0, l, Following->GetTimeString(), clrYellow, clrBackground); - Interface->Write(t, l, FollowingTitle, clrCyan, clrBackground); - l++; - } - if (!isempty(FollowingSubtitle)) { - Interface->Write(t, l, FollowingSubtitle, clrCyan, clrBackground); - } - Interface->Flush(); - lines = Lines; - lastTime = time_ms(); - cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, PresentTitle, PresentSubtitle, Following ? Following->StartTime() : 0, FollowingTitle, FollowingSubtitle); + const cEvent *Present = Schedule->GetPresentEvent(true); + const cEvent *Following = Schedule->GetFollowingEvent(true); + if (Present != lastPresent || Following != lastFollowing) { + displayChannel->SetEvents(Present, Following); + cStatus::MsgOsdProgramme(Present ? Present->StartTime() : 0, Present ? Present->Title() : NULL, Present ? Present->ShortText() : NULL, Following ? Following->StartTime() : 0, Following ? Following->Title() : NULL, Following ? Following->ShortText() : NULL); + lastPresent = Present; + lastFollowing = Following; } } } @@ -2488,11 +2517,10 @@ void cDisplayChannel::DisplayInfo(void) void cDisplayChannel::Refresh(void) { - Interface->Clear(); channel = Channels.GetByNumber(cDevice::CurrentChannel()); DisplayChannel(); + displayChannel->SetEvents(NULL, NULL); lastTime = time_ms(); - lines = 0; } eOSState cDisplayChannel::ProcessKey(eKeys Key) @@ -2509,7 +2537,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) number = number * 10 + Key - k0; if (number > 0) { channel = Channels.GetByNumber(number); - Interface->Clear(); + displayChannel->SetEvents(NULL, NULL); withInfo = false; DisplayChannel(); lastTime = time_ms(); @@ -2528,7 +2556,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) } if (n > 0) { // This channel is the only one that fits the input, so let's take it right away: - Interface->Flush(); // makes sure the user sees his last input + displayChannel->Flush(); // makes sure the user sees his last input Channels.SwitchTo(number); return osEnd; } @@ -2555,7 +2583,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) group = SaveGroup; channel = Channels.Get(group); if (channel) { - Interface->Clear(); + displayChannel->SetEvents(NULL, NULL); DisplayChannel(); if (!channel->GroupSep()) group = -1; @@ -2604,85 +2632,50 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key) }; if (time_ms() - lastTime < INFOTIMEOUT) { DisplayInfo(); + displayChannel->Flush(); return osContinue; } return osEnd; } -// --- cVolumeBar ------------------------------------------------------------ - -class cVolumeBar : public cBitmap { -public: - cVolumeBar(int Width, int Height, int Current, int Total, const char *Prompt = NULL); - }; - -cVolumeBar::cVolumeBar(int Width, int Height, int Current, int Total, const char *Prompt) -:cBitmap(Width, Height, 2) -{ - int l = Prompt ? cBitmap::Width(Prompt) : 0; - int p = (Width - l) * Current / Total; - Text(0, 0, Prompt, clrGreen); - Fill(l, 0, p, Height - 1, clrGreen); - Fill(l + p, 0, Width - 1, Height - 1, clrWhite); -} - // --- cDisplayVolume -------------------------------------------------------- #define VOLUMETIMEOUT 1000 //ms #define MUTETIMEOUT 5000 //ms -cDisplayVolume *cDisplayVolume::displayVolume = NULL; +cDisplayVolume *cDisplayVolume::currentDisplayVolume = NULL; cDisplayVolume::cDisplayVolume(void) :cOsdObject(true) { - displayVolume = this; + currentDisplayVolume = this; timeout = time_ms() + (cDevice::PrimaryDevice()->IsMute() ? MUTETIMEOUT : VOLUMETIMEOUT); - Interface->Open(Setup.OSDwidth, -1); + displayVolume = Skins.Current()->DisplayVolume(); Show(); } cDisplayVolume::~cDisplayVolume() { - Interface->Close(); - displayVolume = NULL; + delete displayVolume; + currentDisplayVolume = NULL; } void cDisplayVolume::Show(void) { - cDevice *device = cDevice::PrimaryDevice(); - if (device->IsMute()) { - Interface->Fill(0, 0, Width(), 1, clrTransparent); - Interface->Write(0, 0, tr("Mute"), clrGreen); - } - else { - int Current = cDevice::CurrentVolume(); - int Total = MAXVOLUME; - const char *Prompt = tr("Volume "); -#ifdef DEBUG_OSD - int l = strlen(Prompt); - int p = int(double(Width() - l) * Current / Total + 0.5); - Interface->Write(0, 0, Prompt, clrGreen); - Interface->Fill(l, 0, p, 1, clrGreen); - Interface->Fill(l + p, 0, Width() - l - p, 1, clrWhite); -#else - cVolumeBar VolumeBar(Width() * cOsd::CellWidth(), cOsd::LineHeight(), Current, Total, Prompt); - Interface->SetBitmap(0, 0, VolumeBar); -#endif - } + displayVolume->SetVolume(cDevice::CurrentVolume(), MAXVOLUME, cDevice::PrimaryDevice()->IsMute()); } cDisplayVolume *cDisplayVolume::Create(void) { - if (!displayVolume) + if (!currentDisplayVolume) new cDisplayVolume; - return displayVolume; + return currentDisplayVolume; } void cDisplayVolume::Process(eKeys Key) { - if (displayVolume) - displayVolume->ProcessKey(Key); + if (currentDisplayVolume) + currentDisplayVolume->ProcessKey(Key); } eOSState cDisplayVolume::ProcessKey(eKeys Key) @@ -2920,9 +2913,7 @@ bool cRecordControls::StopPrimary(bool DoIt) bool cRecordControls::PauseLiveVideo(void) { - Interface->Open(Setup.OSDwidth, -1); - Interface->Status(tr("Pausing live video...")); - Interface->Flush(); + Skins.Message(mtStatus, tr("Pausing live video...")); cReplayControl::SetRecording(NULL, NULL); // make sure the new cRecordControl will set cReplayControl::LastReplayed() if (Start(NULL, true)) { sleep(2); // allow recorded file to fill up enough to start replaying @@ -2930,11 +2921,11 @@ bool cRecordControls::PauseLiveVideo(void) cControl::Launch(rc); cControl::Attach(); sleep(1); // allow device to replay some frames, so we have a picture - Interface->Close(); + Skins.Message(mtStatus, NULL); rc->ProcessKey(kPause); // pause, allowing replay mode display return true; } - Interface->Close(); + Skins.Message(mtStatus, NULL); return false; } @@ -3001,50 +2992,6 @@ void cRecordControls::Shutdown(void) DELETENULL(RecordControls[i]); } -// --- cProgressBar ---------------------------------------------------------- - -class cProgressBar : public cBitmap { -protected: - int total; - int Pos(int p) { return p * width / total; } - void Mark(int x, bool Start, bool Current); -public: - cProgressBar(int Width, int Height, int Current, int Total, const cMarks &Marks); - }; - -cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks &Marks) -:cBitmap(Width, Height, 2) -{ - total = Total; - if (total > 0) { - int p = Pos(Current); - Fill(0, 0, p, Height - 1, clrGreen); - Fill(p + 1, 0, Width - 1, Height - 1, clrWhite); - bool Start = true; - for (const cMark *m = Marks.First(); m; m = Marks.Next(m)) { - int p1 = Pos(m->position); - if (Start) { - const cMark *m2 = Marks.Next(m); - int p2 = Pos(m2 ? m2->position : total); - int h = Height / 3; - Fill(p1, h, p2, Height - h, clrRed); - } - Mark(p1, Start, m->position == Current); - Start = !Start; - } - } -} - -void cProgressBar::Mark(int x, bool Start, bool Current) -{ - Fill(x, 0, x, height - 1, clrBlack); - const int d = height / (Current ? 3 : 9); - for (int i = 0; i < d; i++) { - int h = Start ? i : height - 1 - i; - Fill(x - d + i, h, x + d - i, h, Current ? clrRed : clrBlack); - } -} - // --- cReplayControl -------------------------------------------------------- char *cReplayControl::fileName = NULL; @@ -3053,8 +3000,11 @@ char *cReplayControl::title = NULL; cReplayControl::cReplayControl(void) :cDvbPlayerControl(fileName) { + displayReplay = NULL; visible = modeOnly = shown = displayFrames = false; lastCurrent = lastTotal = -1; + lastPlay = lastForward = false; + lastSpeed = -1; timeoutShow = 0; timeSearchActive = false; marks.Load(fileName); @@ -3108,61 +3058,36 @@ void cReplayControl::Show(void) void cReplayControl::Hide(void) { if (visible) { - Interface->Close(); + delete displayReplay; + displayReplay = NULL; needsFastResponse = visible = false; modeOnly = false; + lastPlay = lastForward = false; + lastSpeed = -1; } } -void cReplayControl::DisplayAtBottom(const char *s) -{ - if (s) { - int w = cOsd::WidthInCells(s); - int d = max(Width() - w, 0) / 2; - if (modeOnly) //XXX remove when displaying replay mode differently - Interface->Fill(0, -1, Interface->Width(), 1, clrTransparent); //XXX remove when displaying replay mode differently - Interface->Write(d, -1, s); - Interface->Flush(); - } - else - Interface->Fill(12, 2, Width() - 22, 1, clrBackground); -} - void cReplayControl::ShowMode(void) { - if (Setup.ShowReplayMode && !timeSearchActive) { + if (visible || Setup.ShowReplayMode) { bool Play, Forward; int Speed; - if (GetReplayMode(Play, Forward, Speed)) { + if (GetReplayMode(Play, Forward, Speed) && (!visible || Play != lastPlay || Forward != lastForward || Speed != lastSpeed)) { bool NormalPlay = (Play && Speed == -1); if (!visible) { if (NormalPlay) return; // no need to do indicate ">" unless there was a different mode displayed before - // open small display - /*XXX change when displaying replay mode differently - Interface->Open(9, -1); - Interface->Clear(); - XXX*/ - Interface->Open(0, -1); //XXX remove when displaying replay mode differently visible = modeOnly = true; + displayReplay = Skins.Current()->DisplayReplay(modeOnly); } if (modeOnly && !timeoutShow && NormalPlay) timeoutShow = time(NULL) + MODETIMEOUT; - const char *Mode; - if (Speed == -1) Mode = Play ? " > " : " || "; - else if (Play) Mode = Forward ? " X>> " : " < " : " <|X "; - char buf[16]; - strn0cpy(buf, Mode, sizeof(buf)); - char *p = strchr(buf, 'X'); - if (p) - *p = Speed > 0 ? '1' + Speed - 1 : ' '; - - eDvbFont OldFont = Interface->SetFont(fontFix); - DisplayAtBottom(buf); - Interface->SetFont(OldFont); + displayReplay->SetMode(Play, Forward, Speed); + lastPlay = Play; + lastForward = Forward; + lastSpeed = Speed; } } } @@ -3173,33 +3098,26 @@ bool cReplayControl::ShowProgress(bool Initial) if (GetIndex(Current, Total) && Total > 0) { if (!visible) { - Interface->Open(Setup.OSDwidth, -3); + displayReplay = Skins.Current()->DisplayReplay(modeOnly); + displayReplay->SetMarks(&marks); needsFastResponse = visible = true; } if (Initial) { - Interface->Clear(); if (title) - Interface->Write(0, 0, title); + displayReplay->SetTitle(title); lastCurrent = lastTotal = -1; } if (Total != lastTotal) { - Interface->Write(-7, 2, IndexToHMSF(Total)); + displayReplay->SetTotal(IndexToHMSF(Total)); if (!Initial) - Interface->Flush(); + displayReplay->Flush(); } if (Current != lastCurrent || Total != lastTotal) { -#ifdef DEBUG_OSD - int p = Width() * Current / Total; - Interface->Fill(0, 1, p, 1, clrGreen); - Interface->Fill(p, 1, Width() - p, 1, clrWhite); -#else - cProgressBar ProgressBar(Width() * cOsd::CellWidth(), cOsd::LineHeight(), Current, Total, marks); - Interface->SetBitmap(0, cOsd::LineHeight(), ProgressBar); + displayReplay->SetProgress(Current, Total); if (!Initial) - Interface->Flush(); -#endif - Interface->Write(0, 2, IndexToHMSF(Current, displayFrames)); - Interface->Flush(); + displayReplay->Flush(); + displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames)); + displayReplay->Flush(); lastCurrent = Current; } lastTotal = Total; @@ -3223,7 +3141,7 @@ void cReplayControl::TimeSearchDisplay(void) char cm10 = timeSearchPos > 1 ? m10 : '-'; char cm1 = timeSearchPos > 0 ? m1 : '-'; sprintf(buf + len, "%c%c:%c%c", ch10, ch1, cm10, cm1); - DisplayAtBottom(buf); + displayReplay->SetJump(buf); } void cReplayControl::TimeSearchProcess(eKeys Key) @@ -3269,7 +3187,7 @@ void cReplayControl::TimeSearchProcess(eKeys Key) if (timeSearchHide) Hide(); else - DisplayAtBottom(); + displayReplay->SetJump(NULL); ShowMode(); } } @@ -3354,14 +3272,14 @@ void cReplayControl::EditCut(void) Hide(); if (!cCutter::Active()) { if (!marks.Count()) - Interface->Error(tr("No editing marks defined!")); + Skins.Message(mtError, tr("No editing marks defined!")); else if (!cCutter::Start(fileName)) - Interface->Error(tr("Can't start editing process!")); + Skins.Message(mtError, tr("Can't start editing process!")); else - Interface->Info(tr("Editing process started")); + Skins.Message(mtInfo, tr("Editing process started")); } else - Interface->Error(tr("Editing process already active!")); + Skins.Message(mtError, tr("Editing process already active!")); ShowMode(); } } @@ -3466,8 +3384,5 @@ eOSState cReplayControl::ProcessKey(eKeys Key) } if (DoShowMode) ShowMode(); - if (DisplayedFrames && !displayFrames) - Interface->Fill(0, 2, 11, 1, clrBackground); return osContinue; } - diff --git a/menu.h b/menu.h index 6fe95408..d52f6419 100644 --- a/menu.h +++ b/menu.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 1.60 2004/02/15 14:11:28 kls Exp $ + * $Id: menu.h 1.61 2004/04/30 13:45:19 kls Exp $ */ #ifndef __MENU_H @@ -13,10 +13,11 @@ #include "ci.h" #include "device.h" #include "epg.h" -#include "osd.h" +#include "osdbase.h" #include "dvbplayer.h" #include "recorder.h" #include "recording.h" +#include "skins.h" class cMenuMain : public cOsdMenu { private: @@ -32,12 +33,14 @@ public: class cDisplayChannel : public cOsdObject { private: + cSkinDisplayChannel *displayChannel; int group; bool withInfo; - int lines; int lastTime; int number; cChannel *channel; + const cEvent *lastPresent; + const cEvent *lastFollowing; void DisplayChannel(void); void DisplayInfo(void); void Refresh(void); @@ -50,8 +53,9 @@ public: class cDisplayVolume : public cOsdObject { private: + cSkinDisplayVolume *displayVolume; int timeout; - static cDisplayVolume *displayVolume; + static cDisplayVolume *currentDisplayVolume; virtual void Show(void); cDisplayVolume(void); public: @@ -150,9 +154,12 @@ public: class cReplayControl : public cDvbPlayerControl { private: + cSkinDisplayReplay *displayReplay; cMarks marks; bool visible, modeOnly, shown, displayFrames; int lastCurrent, lastTotal; + bool lastPlay, lastForward; + int lastSpeed; time_t timeoutShow; bool timeSearchActive, timeSearchHide; int timeSearchTime, timeSearchPos; @@ -162,7 +169,6 @@ private: void ShowTimed(int Seconds = 0); static char *fileName; static char *title; - void DisplayAtBottom(const char *s = NULL); void ShowMode(void); bool ShowProgress(bool Initial); void MarkToggle(void); diff --git a/menuitems.c b/menuitems.c index 45d2066d..743b3f31 100644 --- a/menuitems.c +++ b/menuitems.c @@ -4,13 +4,15 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menuitems.c 1.16 2004/03/28 09:17:51 kls Exp $ + * $Id: menuitems.c 1.17 2004/05/02 10:37:34 kls Exp $ */ #include "menuitems.h" #include #include "i18n.h" #include "plugin.h" +#include "remote.h" +#include "skins.h" #include "status.h" const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~"; @@ -36,7 +38,6 @@ void cMenuEditItem::SetValue(const char *Value) char *buffer = NULL; asprintf(&buffer, "%s:\t%s", name, value); SetText(buffer, false); - Display(); cStatus::MsgOsdCurrentItem(buffer); } @@ -253,9 +254,9 @@ cMenuEditStrItem::~cMenuEditStrItem() void cMenuEditStrItem::SetHelpKeys(void) { if (pos >= 0) - Interface->Help(tr("ABC/abc"), tr(insert ? "Overwrite" : "Insert"), tr("Delete")); + cSkinDisplay::Current()->SetButtons(tr("ABC/abc"), tr(insert ? "Overwrite" : "Insert"), tr("Delete")); else - Interface->Help(NULL); + cSkinDisplay::Current()->SetButtons(NULL); } void cMenuEditStrItem::Set(void) @@ -264,21 +265,21 @@ void cMenuEditStrItem::Set(void) const char *fmt = insert && newchar ? "[]%c%s" : "[%c]%s"; if (pos >= 0) { + const cFont *font = cFont::GetFont(fontOsd); strncpy(buf, value, pos); snprintf(buf + pos, sizeof(buf) - pos - 2, fmt, *(value + pos), value + pos + 1); - int width = Interface->Width() - Interface->GetCols()[0]; - if (cOsd::WidthInCells(buf) <= width) { + int width = cSkinDisplay::Current()->EditableWidth(); + if (font->Width(buf) <= width) { // the whole buffer fits on the screen SetValue(buf); return; } - width *= cOsd::CellWidth(); - width -= cOsd::Width('>'); // assuming '<' and '>' have the same with + width -= font->Width('>'); // assuming '<' and '>' have the same with int w = 0; int i = 0; int l = strlen(buf); while (i < l && w <= width) - w += cOsd::Width(buf[i++]); + w += font->Width(buf[i++]); if (i >= pos + 4) { // the cursor fits on the screen buf[i - 1] = '>'; @@ -295,7 +296,7 @@ void cMenuEditStrItem::Set(void) else i--; while (i >= 0 && w <= width) - w += cOsd::Width(buf[i--]); + w += font->Width(buf[i--]); buf[++i] = '<'; SetValue(buf + i); } @@ -461,100 +462,6 @@ void cMenuEditStraItem::Set(void) SetValue(strings[*value]); } -// --- cMenuTextItem --------------------------------------------------------- - -cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor, eDvbFont Font) -{ - x = X; - y = Y; - w = W; - h = H; - fgColor = FgColor; - bgColor = BgColor; - font = Font; - offset = 0; - eDvbFont oldFont = Interface->SetFont(font); - text = Interface->WrapText(Text, w - 1, &lines); - Interface->SetFont(oldFont); - if (h < 0) - h = lines; -} - -cMenuTextItem::~cMenuTextItem() -{ - free(text); -} - -void cMenuTextItem::Clear(void) -{ - Interface->Fill(x, y, w, h, bgColor); -} - -void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor) -{ - int l = 0; - char *t = text; - eDvbFont oldFont = Interface->SetFont(font); - 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; - } - Interface->SetFont(oldFont); - // 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); - cStatus::MsgOsdTextItem(text); -} - -void cMenuTextItem::ScrollUp(bool Page) -{ - if (CanScrollUp()) { - Clear(); - offset = max(offset - (Page ? h : 1), 0); - Display(); - } - cStatus::MsgOsdTextItem(NULL, true); -} - -void cMenuTextItem::ScrollDown(bool Page) -{ - if (CanScrollDown()) { - Clear(); - offset = min(offset + (Page ? h : 1), lines - h); - Display(); - } - cStatus::MsgOsdTextItem(NULL, false); -} - -eOSState cMenuTextItem::ProcessKey(eKeys Key) -{ - switch (Key) { - case kLeft|k_Repeat: - case kLeft: - case kUp|k_Repeat: - case kUp: ScrollUp(NORMALKEY(Key) == kLeft); break; - case kRight|k_Repeat: - case kRight: - case kDown|k_Repeat: - case kDown: ScrollDown(NORMALKEY(Key) == kRight); break; - default: return osUnknown; - } - return osContinue; -} - // --- cMenuEditChanItem ----------------------------------------------------- cMenuEditChanItem::cMenuEditChanItem(const char *Name, int *Value) diff --git a/menuitems.h b/menuitems.h index 04779dc0..6e9061b8 100644 --- a/menuitems.h +++ b/menuitems.h @@ -4,13 +4,13 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menuitems.h 1.7 2004/03/28 09:16:24 kls Exp $ + * $Id: menuitems.h 1.8 2004/04/30 13:45:28 kls Exp $ */ #ifndef __MENUITEMS_H #define __MENUITEMS_H -#include "osd.h" +#include "osdbase.h" extern const char *FileNameChars; @@ -100,25 +100,6 @@ public: cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings); }; -class cMenuTextItem : public cOsdItem { -private: - char *text; - int x, y, w, h, lines, offset; - eDvbColor fgColor, bgColor; - eDvbFont font; -public: - cMenuTextItem(const char *Text, int X, int Y, int W, int H = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground, eDvbFont Font = fontOsd); - ~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(bool Page); - void ScrollDown(bool Page); - virtual eOSState ProcessKey(eKeys Key); - }; - class cMenuEditChanItem : public cMenuEditIntItem { protected: virtual void Set(void); diff --git a/osd.c b/osd.c index 7ede0015..94668623 100644 --- a/osd.c +++ b/osd.c @@ -4,651 +4,768 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 1.45 2004/03/14 10:33:20 kls Exp $ + * $Id: osd.c 1.46 2004/05/16 09:25:06 kls Exp $ */ #include "osd.h" -#include -#include "device.h" -#include "i18n.h" -#include "status.h" +#include +#include +#include +#include +#include +#include +#include "tools.h" + +// --- cPalette -------------------------------------------------------------- + +cPalette::cPalette(int Bpp) +{ + SetBpp(Bpp); +} + +void cPalette::Reset(void) +{ + numColors = 0; + modified = false; +} + +int cPalette::Index(tColor Color) +{ + for (int i = 0; i < numColors; i++) { + if (color[i] == Color) + return i; + } + if (numColors < maxColors) { + color[numColors++] = Color; + modified = true; + return numColors - 1; + } + dsyslog("too many different colors used in palette"); + //TODO: return the index of the "closest" color? + return 0; +} + +void cPalette::SetBpp(int Bpp) +{ + bpp = Bpp; + maxColors = 1 << bpp; + Reset(); +} + +void cPalette::SetColor(int Index, tColor Color) +{ + if (Index < maxColors) { + if (numColors <= Index) { + numColors = Index + 1; + modified = true; + } + else + modified |= color[Index] != Color; + color[Index] = Color; + } +} + +const tColor *cPalette::Colors(int &NumColors) +{ + NumColors = numColors; + return numColors ? color : NULL; +} + +void cPalette::Take(const cPalette &Palette, tIndexes *Indexes, tColor ColorFg, tColor ColorBg) +{ + for (int i = 0; i < Palette.numColors; i++) { + tColor Color = Palette.color[i]; + if (ColorFg || ColorBg) { + switch (i) { + case 0: Color = ColorBg; break; + case 1: Color = ColorFg; break; + } + } + int n = Index(Color); + if (Indexes) + (*Indexes)[i] = n; + } +} + +// --- cBitmap --------------------------------------------------------------- + +cBitmap::cBitmap(int Width, int Height, int Bpp, int X0, int Y0) +:cPalette(Bpp) +{ + bitmap = NULL; + x0 = X0; + y0 = Y0; + SetSize(Width, Height); +} + +cBitmap::cBitmap(const char *FileName) +{ + bitmap = NULL; + x0 = 0; + y0 = 0; + LoadXpm(FileName); +} + +cBitmap::cBitmap(char *Xpm[]) +{ + bitmap = NULL; + x0 = 0; + y0 = 0; + SetXpm(Xpm); +} + +cBitmap::~cBitmap() +{ + free(bitmap); +} + +void cBitmap::SetSize(int Width, int Height) +{ + if (bitmap && Width == width && Height == height) + return; + width = Width; + height = Height; + free(bitmap); + bitmap = NULL; + dirtyX1 = 0; + dirtyY1 = 0; + dirtyX2 = width - 1; + dirtyY2 = height - 1; + if (width > 0 && height > 0) { + bitmap = MALLOC(tIndex, width * height); + if (bitmap) + memset(bitmap, 0x00, width * height); + else + esyslog("ERROR: can't allocate bitmap!"); + } + else + esyslog("ERROR: illegal bitmap parameters (%d, %d)!", width, height); +} + +bool cBitmap::Contains(int x, int y) const +{ + x -= x0; + y -= y0; + return 0 <= x && x < width && 0 <= y && y < height; +} + +bool cBitmap::Intersects(int x1, int y1, int x2, int y2) const +{ + x1 -= x0; + y1 -= y0; + x2 -= x0; + y2 -= y0; + return !(x2 < 0 || x1 >= width || y2 < 0 || y1 >= height); +} + +bool cBitmap::Dirty(int &x1, int &y1, int &x2, int &y2) +{ + if (dirtyX2 >= 0) { + x1 = dirtyX1; + y1 = dirtyY1; + x2 = dirtyX2; + y2 = dirtyY2; + return true; + } + return false; +} + +void cBitmap::Clean(void) +{ + dirtyX1 = width; + dirtyY1 = height; + dirtyX2 = -1; + dirtyY2 = -1; +} + +bool cBitmap::LoadXpm(const char *FileName) +{ + bool Result = false; + FILE *f = fopen(FileName, "r"); + if (f) { + char **Xpm = NULL; + bool isXpm = false; + int lines = 0; + int index = 0; + char *s; + while ((s = readline(f)) != NULL) { + s = skipspace(s); + if (!isXpm) { + if (strcmp(s, "/* XPM */") != 0) { + esyslog("ERROR: invalid header in XPM file '%s'", FileName); + break; + } + isXpm = true; + } + else if (*s++ == '"') { + if (!lines) { + int w, h, n, c; + if (4 != sscanf(s, "%d %d %d %d", &w, &h, &n, &c)) { + esyslog("ERROR: faulty 'values' line in XPM file '%s'", FileName); + break; + } + lines = h + n + 1; + Xpm = MALLOC(char *, lines); + } + char *q = strchr(s, '"'); + if (!q) { + esyslog("ERROR: missing quotes in XPM file '%s'", FileName); + break; + } + *q = 0; + if (index < lines) + Xpm[index++] = strdup(s); + else { + esyslog("ERROR: too many lines in XPM file '%s'", FileName); + break; + } + } + } + if (index == lines) + Result = SetXpm(Xpm); + else + esyslog("ERROR: too few lines in XPM file '%s'", FileName); + for (int i = 0; i < index; i++) + free(Xpm[i]); + free(Xpm); + fclose(f); + } + else + esyslog("ERROR: can't open XPM file '%s'", FileName); + return Result; +} + +bool cBitmap::SetXpm(char *Xpm[]) +{ + char **p = Xpm; + int w, h, n, c; + if (4 != sscanf(*p, "%d %d %d %d", &w, &h, &n, &c)) { + esyslog("ERROR: faulty 'values' line in XPM: '%s'", *p); + return false; + } + if (n > MAXNUMCOLORS) { + esyslog("ERROR: too many colors in XPM: %d", n); + return false; + } + int b = 0; + while (1 << (1 << b) < n) + b++; + SetBpp(1 << b); + SetSize(w, h); + for (int i = 0; i < n; i++) { + const char *s = *++p; + if (int(strlen(s)) < c) { + esyslog("ERROR: faulty 'colors' line in XPM: '%s'", s); + return false; + } + s = skipspace(s + c); + if (*s != 'c') { + esyslog("ERROR: unknown color key in XPM: '%c'", *s); + return false; + } + s = skipspace(s + 1); + if (*s != '#') { + esyslog("ERROR: unknown color code in XPM: '%c'", *s); + return false; + } + tColor color = strtoul(++s, NULL, 16) | 0xFF000000; + SetColor(i, color); + } + for (int y = 0; y < h; y++) { + const char *s = *++p; + if (int(strlen(s)) != w * c) { + esyslog("ERROR: faulty pixel line in XPM: %d '%s'", y, s); + return false; + } + for (int x = 0; x < w; x++) { + for (int i = 0; i <= n; i++) { + if (i == n) { + esyslog("ERROR: undefined pixel color in XPM: %d %d '%s'", x, y, s); + return false; + } + if (strncmp(Xpm[i + 1], s, c) == 0) { + SetIndex(x, y, i); + break; + } + } + s += c; + } + } + return true; +} + +void cBitmap::SetIndex(int x, int y, tIndex Index) +{ + if (bitmap) { + if (0 <= x && x < width && 0 <= y && y < height) { + if (bitmap[width * y + x] != Index) { + bitmap[width * y + x] = Index; + if (dirtyX1 > x) dirtyX1 = x; + if (dirtyY1 > y) dirtyY1 = y; + if (dirtyX2 < x) dirtyX2 = x; + if (dirtyY2 < y) dirtyY2 = y; + } + } + } +} + +void cBitmap::DrawPixel(int x, int y, tColor Color) +{ + x -= x0; + y -= y0; + SetIndex(x, y, Index(Color)); +} + +void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg) +{ + if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) { + x -= x0; + y -= y0; + tIndexes Indexes; + if (ColorFg || ColorBg) { + } + Take(Bitmap, &Indexes, ColorFg, ColorBg); + for (int ix = 0; ix < Bitmap.width; ix++) { + for (int iy = 0; iy < Bitmap.height; iy++) + SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]); + } + } +} + +void cBitmap::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment) +{ + if (bitmap) { + int w = Font->Width(s); + int h = Font->Height(); + int limit = 0; + if (Width || Height) { + int cw = Width ? Width : w; + 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); + limit = x + cw - x0; + 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; + } + } + } + else if (!Intersects(x, y, x + w - 1, y + h - 1)) + return; + x -= x0; + y -= y0; + tIndex fg = Index(ColorFg); + tIndex bg = Index(ColorBg); + 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; ) { + SetIndex(x + col, y + row, (PixelData & 1) ? fg : bg); + PixelData >>= 1; + } + } + } + x += CharData->width; + if (x > width - 1) + break; + } + } +} + +void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color) +{ + if (bitmap && Intersects(x1, y1, x2, y2)) { + x1 -= x0; + y1 -= y0; + x2 -= x0; + y2 -= y0; + x1 = max(x1, 0); + y1 = max(y1, 0); + x2 = min(x2, width - 1); + y2 = min(y2, height - 1); + if (x1 == 0 && y1 == 0 && x2 == width - 1 && y2 == height - 1) + Reset(); + tIndex c = Index(Color); + for (int y = y1; y <= y2; y++) + for (int x = x1; x <= x2; x++) + SetIndex(x, y, c); + } +} + +void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants) +{ + if (!Intersects(x1, y1, x2, y2)) + return; + // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF + int rx = x2 - x1; + int ry = y2 - y1; + int cx = (x1 + x2) / 2; + int cy = (y1 + y2) / 2; + switch (abs(Quadrants)) { + case 0: rx /= 2; ry /= 2; break; + case 1: cx = x1; cy = y2; break; + case 2: cx = x2; cy = y2; break; + case 3: cx = x2; cy = y1; break; + case 4: cx = x1; cy = y1; break; + case 5: cx = x1; ry /= 2; break; + case 6: cy = y2; rx /= 2; break; + case 7: cx = x2; ry /= 2; break; + case 8: cy = y1; rx /= 2; break; + } + int TwoASquare = 2 * rx * rx; + int TwoBSquare = 2 * ry * ry; + int x = rx; + int y = 0; + int XChange = ry * ry * (1 - 2 * rx); + int YChange = rx * rx; + int EllipseError = 0; + int StoppingX = TwoBSquare * rx; + int StoppingY = 0; + while (StoppingX >= StoppingY) { + switch (Quadrants) { + case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); // no break + case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, Color); break; + case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); // no break + case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, Color); break; + case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); break; + case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); break; + case 0: + case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break; + case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break; + case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, Color); break; + case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, Color); break; + case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, Color); break; + case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, Color); break; + } + y++; + StoppingY += TwoASquare; + EllipseError += YChange; + YChange += TwoASquare; + if (2 * EllipseError + XChange > 0) { + x--; + StoppingX -= TwoBSquare; + EllipseError += XChange; + XChange += TwoBSquare; + } + } + x = 0; + y = ry; + XChange = ry * ry; + YChange = rx * rx * (1 - 2 * ry); + EllipseError = 0; + StoppingX = 0; + StoppingY = TwoASquare * ry; + while (StoppingX <= StoppingY) { + switch (Quadrants) { + case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); // no break + case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, Color); break; + case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); // no break + case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, Color); break; + case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); break; + case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); break; + case 0: + case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break; + case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break; + case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, Color); break; + case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, Color); break; + case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, Color); break; + case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, Color); break; + } + x++; + StoppingX += TwoBSquare; + EllipseError += XChange; + XChange += TwoBSquare; + if (2 * EllipseError + YChange > 0) { + y--; + StoppingY -= TwoASquare; + EllipseError += YChange; + YChange += TwoASquare; + } + } +} + +void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type) +{ + // TODO This is just a quick and dirty implementation of a slope drawing + // machanism. If somebody can come up with a better solution, let's have it! + if (!Intersects(x1, y1, x2, y2)) + return; + bool upper = Type & 0x01; + bool falling = Type & 0x02; + bool vertical = Type & 0x04; + if (vertical) { + for (int y = y1; y <= y2; y++) { + double c = cos((y - y1) * M_PI / (y2 - y1 + 1)); + if (falling) + c = -c; + int x = int((x2 - x1 + 1) * c / 2); + if (upper && !falling || !upper && falling) + DrawRectangle(x1, y, (x1 + x2) / 2 + x, y, Color); + else + DrawRectangle((x1 + x2) / 2 + x, y, x2, y, Color); + } + } + else { + for (int x = x1; x <= x2; x++) { + double c = cos((x - x1) * M_PI / (x2 - x1 + 1)); + if (falling) + c = -c; + int y = int((y2 - y1 + 1) * c / 2); + if (upper) + DrawRectangle(x, y1, x, (y1 + y2) / 2 + y, Color); + else + DrawRectangle(x, (y1 + y2) / 2 + y, x, y2, Color); + } + } +} + +const tIndex *cBitmap::Data(int x, int y) +{ + return &bitmap[y * width + x]; +} // --- cOsd ------------------------------------------------------------------ -#ifdef DEBUG_OSD - WINDOW *cOsd::window = NULL; - int cOsd::colorPairs[MaxColorPairs] = { 0 }; -#else - cOsdBase *cOsd::osd = NULL; -#endif - int cOsd::cols = 0; - int cOsd::rows = 0; +bool cOsd::isOpen = false; -void cOsd::Initialize(void) +cOsd::cOsd(int Left, int Top) { -#if defined(DEBUG_OSD) - initscr(); - start_color(); - leaveok(stdscr, true); -#endif + if (isOpen) + esyslog("ERROR: OSD opened without closing previous OSD!"); + savedRegion = NULL; + numBitmaps = 0; + left = Left; + top = Top; + width = height = 0; + isOpen = true; } -void cOsd::Shutdown(void) +cOsd::~cOsd() { - Close(); -#if defined(DEBUG_OSD) - endwin(); -#endif + for (int i = 0; i < numBitmaps; i++) + delete bitmaps[i]; + delete savedRegion; + isOpen = false; } -#ifdef DEBUG_OSD -void cOsd::SetColor(eDvbColor colorFg, eDvbColor colorBg) +cBitmap *cOsd::GetBitmap(int Area) { - int color = (colorBg << 16) | colorFg | 0x80000000; - for (int i = 0; i < MaxColorPairs; i++) { - if (!colorPairs[i]) { - colorPairs[i] = color; - init_pair(i + 1, colorFg, colorBg); - wattrset(window, COLOR_PAIR(i + 1)); - break; - } - else if (color == colorPairs[i]) { - wattrset(window, COLOR_PAIR(i + 1)); - break; - } + return Area < numBitmaps ? bitmaps[Area] : NULL; +} + +eOsdError cOsd::CanHandleAreas(const tArea *Areas, int NumAreas) +{ + for (int i = 0; i < NumAreas; i++) { + for (int j = i + 1; j < NumAreas; j++) { + if (Areas[i].Intersects(Areas[j])) + return oeAreasOverlap; + if (Areas[i].x1 > Areas[i].x2 || Areas[i].y1 > Areas[i].y2 || Areas[i].x1 < 0 || Areas[i].y1 < 0) + return oeWrongAlignment; + } } -} -#endif - -cOsdBase *cOsd::OpenRaw(int x, int y) -{ -#ifdef DEBUG_OSD - return NULL; -#else - return osd ? NULL : cDevice::PrimaryDevice()->NewOsd(x, y); -#endif + return oeOk; } -void cOsd::Open(int w, int h) +eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas) { - int d = (h < 0) ? Setup.OSDheight + h : 0; - h = abs(h); - cols = w; - rows = h; -#ifdef DEBUG_OSD - window = subwin(stdscr, h, w, d, (Setup.OSDwidth - w) / 2); - syncok(window, true); - #define B2C(b) (((b) * 1000) / 255) - #define SETCOLOR(n, r, g, b, o) init_color(n, B2C(r), B2C(g), B2C(b)) - //XXX - SETCOLOR(clrBackground, 0x00, 0x00, 0x00, 127); // background 50% gray - SETCOLOR(clrBlack, 0x00, 0x00, 0x00, 255); - SETCOLOR(clrRed, 0xFC, 0x14, 0x14, 255); - SETCOLOR(clrGreen, 0x24, 0xFC, 0x24, 255); - SETCOLOR(clrYellow, 0xFC, 0xC0, 0x24, 255); - SETCOLOR(clrBlue, 0x00, 0x00, 0xFC, 255); - SETCOLOR(clrCyan, 0x00, 0xFC, 0xFC, 255); - SETCOLOR(clrMagenta, 0xB0, 0x00, 0xFC, 255); - SETCOLOR(clrWhite, 0xFC, 0xFC, 0xFC, 255); -#else - w *= charWidth; - h *= lineHeight; - d *= lineHeight; - int x = (720 - w + charWidth) / 2; //TODO PAL vs. NTSC??? - int y = (576 - Setup.OSDheight * lineHeight) / 2 + d; - //XXX - osd = OpenRaw(x, y); - //XXX TODO this should be transferred to the places where the individual windows are requested (there's too much detailed knowledge here!) - if (!osd) - return; - if (h / lineHeight == 5) { //XXX channel display - osd->Create(0, 0, w, h, 4); + eOsdError Result = oeUnknown; + if (numBitmaps == 0) { + Result = CanHandleAreas(Areas, NumAreas); + if (Result == oeOk) { + width = height = 0; + for (int i = 0; i < NumAreas; i++) { + bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1); + width = max(width, Areas[i].x2); + height = max(height, Areas[i].y2); + } + } } - else if (h / lineHeight == 1) { //XXX info display - osd->Create(0, 0, w, h, 4); + if (Result != oeOk) + esyslog("ERROR: cOsd::SetAreas returned %d\n", Result); + return Result; +} + +void cOsd::SaveRegion(int x1, int y1, int x2, int y2) +{ + delete savedRegion; + savedRegion = new cBitmap(x2 - x1 + 1, y2 - y1 + 1, 8, x1, y1); + for (int i = 0; i < numBitmaps; i++) + savedRegion->DrawBitmap(bitmaps[i]->X0(), bitmaps[i]->Y0(), *bitmaps[i]); +} + +void cOsd::RestoreRegion(void) +{ + if (savedRegion) { + DrawBitmap(savedRegion->X0(), savedRegion->Y0(), *savedRegion); + delete savedRegion; + savedRegion = NULL; } - else if (d == 0) { //XXX full menu - osd->Create(0, 0, w, lineHeight, 2); - osd->Create(0, lineHeight, w, (Setup.OSDheight - 3) * lineHeight, 2); - osd->AddColor(clrBackground); - osd->AddColor(clrCyan); - osd->AddColor(clrWhite); - osd->AddColor(clrBlack); - osd->Create(0, (Setup.OSDheight - 2) * lineHeight, w, 2 * lineHeight, 4); - } - else { //XXX progress display - /*XXX - osd->Create(0, 0, w, lineHeight, 1); - osd->Create(0, lineHeight, w, lineHeight, 2, false); - osd->Create(0, 2 * lineHeight, w, lineHeight, 1); - XXX*///XXX some pixels are not drawn correctly with lower bpp values - osd->Create(0, 0, w, h, 4); - } -#endif } -void cOsd::Close(void) +eOsdError cOsd::SetPalette(const cPalette &Palette, int Area) { -#ifdef DEBUG_OSD - if (window) { - delwin(window); - window = 0; - } -#else - delete osd; - osd = NULL; -#endif + if (Area < numBitmaps) + bitmaps[Area]->Take(Palette); + return oeUnknown; } -void cOsd::Clear(void) +void cOsd::DrawPixel(int x, int y, tColor Color) { -#ifdef DEBUG_OSD - SetColor(clrBackground, clrBackground); - Fill(0, 0, cols, rows, clrBackground); - refresh(); -#else - if (osd) - osd->Clear(); -#endif + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawPixel(x, y, Color); } -void cOsd::Fill(int x, int y, int w, int h, eDvbColor color) +void cOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg) { - if (x < 0) x = cols + x; - if (y < 0) y = rows + y; -#ifdef DEBUG_OSD - SetColor(color, color); - for (int r = 0; r < h; r++) { - wmove(window, y + r, x); // ncurses wants 'y' before 'x'! - whline(window, ' ', w); - } - wsyncup(window); // shouldn't be necessary because of 'syncok()', but w/o it doesn't work -#else - if (osd) - osd->Fill(x * charWidth, y * lineHeight, (x + w) * charWidth - 1, (y + h) * lineHeight - 1, color); -#endif + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg); } -void cOsd::SetBitmap(int x, int y, const cBitmap &Bitmap) +void cOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment) { -#ifndef DEBUG_OSD - if (osd) - osd->SetBitmap(x, y, Bitmap); -#endif + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawText(x, y, s, ColorFg, ColorBg, Font, Width, Height, Alignment); } -void cOsd::ClrEol(int x, int y, eDvbColor color) +void cOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color) { - Fill(x, y, cols - x, 1, color); + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawRectangle(x1, y1, x2, y2, Color); } -int cOsd::CellWidth(void) +void cOsd::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants) { -#ifdef DEBUG_OSD - return 1; -#else - return charWidth; -#endif + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawEllipse(x1, y1, x2, y2, Color, Quadrants); } -int cOsd::LineHeight(void) +void cOsd::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type) { -#ifdef DEBUG_OSD - return 1; -#else - return lineHeight; -#endif -} - -int cOsd::Width(unsigned char c) -{ -#ifdef DEBUG_OSD - return 1; -#else - return osd ? osd->Width(c) : 1; -#endif -} - -int cOsd::WidthInCells(const char *s) -{ -#ifdef DEBUG_OSD - return strlen(s); -#else - return osd ? (osd->Width(s) + charWidth - 1) / charWidth : strlen(s); -#endif -} - -eDvbFont cOsd::SetFont(eDvbFont Font) -{ -#ifdef DEBUG_OSD - return Font; -#else - return osd ? osd->SetFont(Font) : Font; -#endif -} - -void cOsd::Text(int x, int y, const char *s, eDvbColor colorFg, eDvbColor colorBg) -{ - if (x < 0) x = cols + x; - if (y < 0) y = rows + y; -#ifdef DEBUG_OSD - SetColor(colorFg, colorBg); - wmove(window, y, x); // ncurses wants 'y' before 'x'! - waddnstr(window, s, cols - x); -#else - if (osd) - osd->Text(x * charWidth, y * lineHeight, s, colorFg, colorBg); -#endif + for (int i = 0; i < numBitmaps; i++) + bitmaps[i]->DrawSlope(x1, y1, x2, y2, Color, Type); } void cOsd::Flush(void) { -#ifdef DEBUG_OSD - refresh(); -#else - if (osd) - osd->Flush(); -#endif } -// --- cOsdItem -------------------------------------------------------------- +// --- cOsdProvider ---------------------------------------------------------- -cOsdItem::cOsdItem(eOSState State) +cOsdProvider *cOsdProvider::osdProvider = NULL; + +cOsdProvider::cOsdProvider(void) { - text = NULL; - offset = -1; - state = State; - fresh = false; - userColor = false; - fgColor = clrWhite; - bgColor = clrBackground; + delete osdProvider; + osdProvider = this; } -cOsdItem::cOsdItem(const char *Text, eOSState State) +cOsdProvider::~cOsdProvider() { - text = NULL; - offset = -1; - state = State; - fresh = false; - userColor = false; - fgColor = clrWhite; - bgColor = clrBackground; - SetText(Text); + osdProvider = NULL; } -cOsdItem::~cOsdItem() +cOsd *cOsdProvider::NewOsd(int Left, int Top) { - free(text); + if (osdProvider) + return osdProvider->CreateOsd(Left, Top); + esyslog("ERROR: no OSD provider available - using dummy OSD!"); + return new cOsd(Left, Top); // create a dummy cOsd, so that access won't result in a segfault } -void cOsdItem::SetText(const char *Text, bool Copy) +void cOsdProvider::Shutdown(void) { - free(text); - text = Copy ? strdup(Text) : (char *)Text; // text assumes ownership! + delete osdProvider; + osdProvider = NULL; } -void cOsdItem::SetColor(eDvbColor FgColor, eDvbColor BgColor) +// --- cTextScroller --------------------------------------------------------- + +cTextScroller::cTextScroller(void) { - userColor = true; - fgColor = FgColor; - bgColor = BgColor; + osd = NULL; + left = top = width = height = 0; + font = NULL; + colorFg = 0; + colorBg = 0; + offset = 0; + shown = 0; } -void cOsdItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor) +cTextScroller::cTextScroller(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg) { - if (Offset < 0) { - FgColor = clrBlack; - BgColor = clrCyan; + Set(Osd, Left, Top, Width, Height, Text, Font, ColorFg, ColorBg); +} + +void cTextScroller::Set(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg) +{ + osd = Osd; + left = Left; + top = Top; + width = Width; + height = Height; + font = Font; + colorFg = ColorFg; + colorBg = ColorBg; + offset = 0; + textWrapper.Set(Text, Font, Width); + shown = min(Total(), height / font->Height()); + height = shown * font->Height(); // sets height to the actually used height, which may be less than Height + DrawText(); +} + +void cTextScroller::Reset(void) +{ + osd = NULL; // just makes sure it won't draw anything +} + +void cTextScroller::DrawText(void) +{ + if (osd) { + for (int i = 0; i < shown; i++) + osd->DrawText(left, top + i * font->Height(), textWrapper.GetLine(offset + i), colorFg, colorBg, font, width); } - fresh |= Offset >= 0; - if (Offset >= 0) - offset = Offset; - if (offset >= 0) - Interface->WriteText(0, offset + 2, text, userColor ? fgColor : FgColor, userColor ? bgColor : BgColor); } -eOSState cOsdItem::ProcessKey(eKeys Key) +void cTextScroller::Scroll(bool Up, bool Page) { - return Key == kOk ? state : osUnknown; -} - -// --- cOsdMenu -------------------------------------------------------------- - -cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) -{ - isMenu = true; - digit = 0; - hasHotkeys = false; - visible = false; - title = NULL; - SetTitle(Title); - cols[0] = c0; - cols[1] = c1; - cols[2] = c2; - cols[3] = c3; - cols[4] = c4; - first = 0; - current = marked = -1; - subMenu = NULL; - helpRed = helpGreen = helpYellow = helpBlue = NULL; - status = NULL; - Interface->Open(); -} - -cOsdMenu::~cOsdMenu() -{ - free(title); - delete subMenu; - free(status); - Interface->Clear(); - Interface->Close(); -} - -const char *cOsdMenu::hk(const char *s) -{ - static char buffer[64]; - if (s && hasHotkeys) { - if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ') - digit = -1; // prevents automatic hotkeys - input already has them - if (digit >= 0) { - digit++; - snprintf(buffer, sizeof(buffer), " %c %s", (digit < 10) ? '0' + digit : ' ' , s); - s = buffer; + if (Up) { + if (CanScrollUp()) { + offset -= Page ? shown : 1; + if (offset < 0) + offset = 0; + DrawText(); } } - return s; -} - -void cOsdMenu::SetHasHotkeys(void) -{ - hasHotkeys = true; - digit = 0; -} - -void cOsdMenu::SetStatus(const char *s) -{ - free(status); - status = s ? strdup(s) : NULL; - if (visible) - Interface->Status(status); -} - -void cOsdMenu::SetTitle(const char *Title, bool ShowDate) -{ - free(title); - if (ShowDate) - asprintf(&title, "%s\t%s", Title, DayDateTime(time(NULL))); - else - title = strdup(Title); -} - -void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue) -{ - // strings are NOT copied - must be constants!!! - helpRed = Red; - helpGreen = Green; - helpYellow = Yellow; - helpBlue = Blue; - if (visible) - Interface->Help(helpRed, helpGreen, helpYellow, helpBlue); -} - -void cOsdMenu::Del(int Index) -{ - cList::Del(Get(Index)); - if (current == Count()) - current--; - if (Index == first && first > 0) - first--; -} - -void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After) -{ - cList::Add(Item, After); - if (Current) - current = Item->Index(); -} - -void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before) -{ - cList::Ins(Item, Before); - if (Current) - current = Item->Index(); -} - -void cOsdMenu::Display(void) -{ - if (subMenu) { - subMenu->Display(); - return; - } - visible = true; - Interface->Clear(); - Interface->SetCols(cols); - Interface->Title(title); - Interface->Help(helpRed, helpGreen, helpYellow, helpBlue); - int count = Count(); - if (count > 0) { - int ni = 0; - for (cOsdItem *item = First(); item; item = Next(item)) - cStatus::MsgOsdItem(item->Text(), ni++); - if (current < 0) - current = 0; // just for safety - there HAS to be a current item! - if (current - first >= MAXOSDITEMS || current < first) { - first = current - MAXOSDITEMS / 2; - if (first + MAXOSDITEMS > count) - first = count - MAXOSDITEMS; - if (first < 0) - first = 0; + else { + if (CanScrollDown()) { + offset += Page ? shown : 1; + if (offset + shown > Total()) + offset = Total() - shown; + DrawText(); } - int i = first; - int n = 0; - for (cOsdItem *item = Get(first); item; item = Next(item)) { - item->Display(i - first, i == current ? clrBlack : clrWhite, i == current ? clrCyan : clrBackground); - if (i == current) - cStatus::MsgOsdCurrentItem(item->Text()); - if (++n == MAXOSDITEMS) //TODO get this from Interface!!! - break; - i++; - } - } - if (!isempty(status)) - Interface->Status(status); -} - -void cOsdMenu::SetCurrent(cOsdItem *Item) -{ - current = Item ? Item->Index() : -1; -} - -void cOsdMenu::RefreshCurrent(void) -{ - cOsdItem *item = Get(current); - if (item) - item->Set(); -} - -void cOsdMenu::DisplayCurrent(bool Current) -{ - cOsdItem *item = Get(current); - if (item) { - item->Display(current - first, Current ? clrBlack : clrWhite, Current ? clrCyan : clrBackground); - if (Current) - cStatus::MsgOsdCurrentItem(item->Text()); } } - -void cOsdMenu::Clear(void) -{ - first = 0; - current = marked = -1; - cList::Clear(); -} - -bool cOsdMenu::SpecialItem(int idx) -{ - cOsdItem *item = Get(idx); - return item && item->HasUserColor(); -} - -void cOsdMenu::CursorUp(void) -{ - if (current > 0) { - int tmpCurrent = current; - while (--tmpCurrent >= 0 && SpecialItem(tmpCurrent)); - if (tmpCurrent < 0) - return; - if (tmpCurrent >= first) - DisplayCurrent(false); - current = tmpCurrent; - if (current < first) { - first = first > MAXOSDITEMS - 1 ? first - (MAXOSDITEMS - 1) : 0; - if (Setup.MenuScrollPage) - current = SpecialItem(first) ? first + 1 : first; - Display(); - } - else - DisplayCurrent(true); - } -} - -void cOsdMenu::CursorDown(void) -{ - int last = Count() - 1; - int lastOnScreen = first + MAXOSDITEMS - 1; - - if (current < last) { - int tmpCurrent = current; - while (++tmpCurrent <= last && SpecialItem(tmpCurrent)); - if (tmpCurrent > last) - return; - if (tmpCurrent <= lastOnScreen) - DisplayCurrent(false); - current = tmpCurrent; - if (current > lastOnScreen) { - first += MAXOSDITEMS - 1; - lastOnScreen = first + MAXOSDITEMS - 1; - if (lastOnScreen > last) { - first = last - (MAXOSDITEMS - 1); - lastOnScreen = last; - } - if (Setup.MenuScrollPage) - current = SpecialItem(lastOnScreen) ? lastOnScreen - 1 : lastOnScreen; - Display(); - } - else - DisplayCurrent(true); - } -} - -void cOsdMenu::PageUp(void) -{ - current -= MAXOSDITEMS; - first -= MAXOSDITEMS; - if (first < 0) - first = current = 0; - if (SpecialItem(current)) { - current -= (current > 0) ? 1 : -1; - first = min(first, current - 1); - } - Display(); - DisplayCurrent(true); -} - -void cOsdMenu::PageDown(void) -{ - current += MAXOSDITEMS; - first += MAXOSDITEMS; - int count = Count(); - if (current > count - 1) { - current = count - 1; - first = max(0, count - MAXOSDITEMS); - } - if (SpecialItem(current)) { - current += (current < count - 1) ? 1 : -1; - first = max(first, current - MAXOSDITEMS); - } - Display(); - DisplayCurrent(true); -} - -void cOsdMenu::Mark(void) -{ - if (Count() && marked < 0) { - marked = current; - SetStatus(tr("Up/Dn for new location - OK to move")); - } -} - -eOSState cOsdMenu::HotKey(eKeys Key) -{ - for (cOsdItem *item = First(); item; item = Next(item)) { - const char *s = item->Text(); - if (s && (s = skipspace(s)) != NULL) { - if (*s == Key - k1 + '1') { - current = item->Index(); - cRemote::Put(kOk, true); - break; - } - } - } - return osContinue; -} - -eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) -{ - delete subMenu; - subMenu = SubMenu; - subMenu->Display(); - return osContinue; // convenience return value -} - -eOSState cOsdMenu::CloseSubMenu() -{ - delete subMenu; - subMenu = NULL; - RefreshCurrent(); - Display(); - return osContinue; // convenience return value -} - -eOSState cOsdMenu::ProcessKey(eKeys Key) -{ - if (subMenu) { - eOSState state = subMenu->ProcessKey(Key); - if (state == osBack) - return CloseSubMenu(); - return state; - } - - cOsdItem *item = Get(current); - if (marked < 0 && item) { - eOSState state = item->ProcessKey(Key); - if (state != osUnknown) - return state; - } - switch (Key) { - case k1...k9: return hasHotkeys ? HotKey(Key) : osUnknown; - case kUp|k_Repeat: - case kUp: CursorUp(); break; - case kDown|k_Repeat: - case kDown: CursorDown(); break; - case kLeft|k_Repeat: - case kLeft: PageUp(); break; - case kRight|k_Repeat: - case kRight: PageDown(); break; - case kBack: return osBack; - case kOk: if (marked >= 0) { - SetStatus(NULL); - if (marked != current) - Move(marked, current); - marked = -1; - break; - } - // else run into default - default: if (marked < 0) - return osUnknown; - } - return osContinue; -} - diff --git a/osd.h b/osd.h index 3835b093..2df875ed 100644 --- a/osd.h +++ b/osd.h @@ -4,169 +4,349 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.h 1.40 2003/09/14 10:59:22 kls Exp $ + * $Id: osd.h 1.41 2004/05/15 14:54:37 kls Exp $ */ #ifndef __OSD_H #define __OSD_H -#if defined(DEBUG_OSD) -#include -#endif -#include "config.h" -#include "osdbase.h" -#include "interface.h" -#include "osdbase.h" -#include "tools.h" +#include +#include +#include "font.h" -#define MAXOSDITEMS (Setup.OSDheight - 4) +#define MAXNUMCOLORS 256 -enum eOSState { osUnknown, - osContinue, - osSchedule, - osChannels, - osTimers, - osRecordings, - osPlugin, - osSetup, - osCommands, - osPause, - osRecord, - osReplay, - osStopRecord, - osStopReplay, - osCancelEdit, - osSwitchDvb, - osBack, - osEnd, - os_User, // the following values can be used locally - osUser1, - osUser2, - osUser3, - osUser4, - osUser5, - osUser6, - osUser7, - osUser8, - osUser9, - osUser10, - }; +enum { + //AARRGGBB + clrTransparent = 0x00000000, + clrGray50 = 0x7F000000, // 50% gray + clrBlack = 0xFF000000, + clrRed = 0xFFFC1414, + clrGreen = 0xFF24FC24, + clrYellow = 0xFFFCC024, + clrMagenta = 0xFFB000FC, + clrBlue = 0xFF0000FC, + clrCyan = 0xFF00FCFC, + clrWhite = 0xFFFCFCFC, + }; + +enum eOsdError { oeOk, + oeTooManyAreas, + oeTooManyColors, + oeBppNotSupported, + oeAreasOverlap, + oeWrongAlignment, + oeOutOfMemory, + oeUnknown, + }; + +typedef uint32_t tColor; +typedef uint8_t tIndex; + +class cPalette { +private: + tColor color[MAXNUMCOLORS]; + int bpp; + int maxColors, numColors; + bool modified; +protected: + typedef tIndex tIndexes[MAXNUMCOLORS]; +public: + cPalette(int Bpp = 8); + ///< Initializes the palette with the given color depth. + int Bpp(void) { return bpp; } + void Reset(void); + ///< Resets the palette, making it contain no colors. + int Index(tColor Color); + ///< Returns the index of the given Color (the first color has index 0). + ///< If Color is not yet contained in this palette, it will be added if + ///< there is a free slot. If the color can't be added to this palette, + ///< 0 will be returned. + tColor Color(int Index) { return Index < maxColors ? color[Index] : 0; } + ///< Returns the color at the given Index. If Index is outside the valid + ///< range, 0 will be returned. + void SetBpp(int Bpp); + ///< Sets the color depth of this palette to the given value. + ///< The palette contents will be reset, so that it contains no colors. + void SetColor(int Index, tColor Color); + ///< Sets the palette entry at Index to Color. If Index is larger than + ///< the number of currently used entries in this palette, the entries + ///< in between will have undefined values. + const tColor *Colors(int &NumColors); + ///< Returns a pointer to the complete color table and stores the + ///< number of valid entries in NumColors. If no colors have been + ///< stored yet, NumColors will be set to 0 and the function will + ///< return NULL. + void Take(const cPalette &Palette, tIndexes *Indexes = NULL, tColor ColorFg = 0, tColor ColorBg = 0); + ///< Takes the colors from the given Palette and adds them to this palette, + ///< using existing entries if possible. If Indexes is given, it will be + ///< filled with the index values that each color of Palette has in this + ///< palette. If either of ColorFg or ColorBg is not zero, the first color + ///< in Palette will be taken as ColorBg, and the second color will become + ///< ColorFg. + }; + +enum eTextAlignment { taCenter = 0x00, + taLeft = 0x01, + taRight = 0x02, + taTop = 0x04, + taBottom = 0x08, + taDefault = taTop | taLeft + }; + +class cBitmap : public cPalette { +private: + tIndex *bitmap; + int x0, y0; + int width, height; + int dirtyX1, dirtyY1, dirtyX2, dirtyY2; +public: + cBitmap(int Width, int Height, int Bpp, int X0 = 0, int Y0 = 0); + ///< Creates a bitmap with the given Width, Height and color depth (Bpp). + ///< X0 and Y0 define the offset at which this bitmap will be located on the OSD. + ///< All coordinates given in the other functions will be relative to + ///< this offset (unless specified otherwise). + cBitmap(const char *FileName); + ///< Creates a bitmap and loads an XPM image from the given file. + cBitmap(char *Xpm[]); + ///< Creates a bitmap from the given XPM data. + virtual ~cBitmap(); + int X0(void) const { return x0; } + int Y0(void) const { return y0; } + int Width(void) const { return width; } + int Height(void) const { return height; } + void SetSize(int Width, int Height); + ///< Sets the size of this bitmap to the given values. Any previous + ///< contents of the bitmap will be lost. If Width and Height are the same + ///< as the current values, nothing will happen and the bitmap remains + ///< unchanged. + bool Contains(int x, int y) const; + ///< Returns true if this bitmap contains the point (x, y). + bool Intersects(int x1, int y1, int x2, int y2) const; + ///< Returns true if this bitmap intersects with the rectangle + ///< defined by the given coordinates. + bool Dirty(int &x1, int &y1, int &x2, int &y2); + ///< Tells whether there is a dirty area and returns the bounding + ///< rectangle of that area (relative to the bitmaps origin). + void Clean(void); + ///< Marks the dirty area as clean. + 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[]); + ///< Sets this bitmap to the given XPM data. Any previous bitmap or + ///< palette data will be overwritten with the new data. + ///< Returns true if the operation was successful. + void SetIndex(int x, int y, tIndex Index); + ///< Sets the index at the given coordinates to Index. + ///< Coordinates are relative to the bitmap's origin. + void DrawPixel(int x, int y, tColor Color); + ///< Sets the pixel at the given coordinates to the given Color, which is + ///< a full 32 bit ARGB value. + ///< If the coordinates are outside the bitmap area, no pixel will be set. + void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0); + ///< Sets the pixels in this bitmap with the data from the given + ///< Bitmap, putting the upper left corner of the Bitmap at (x, y). + ///< If ColorFg or ColorBg is given, the first palette entry of the Bitmap + ///< will be mapped to ColorBg and the second palette entry will be mapped to + ///< ColorFg (palette indexes are defined so that 0 is the background and + ///< 1 is the foreground color). + void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault); + ///< Draws the given string at coordinates (x, y) with the given foreground + ///< and background color and font. If Width and Height are given, the text + ///< will be drawn into a rectangle with the given size and the given + ///< Alignment (default is top-left). + void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color); + ///< Draws a filled rectangle defined by the upper left (x1, y1) and lower right + ///< (x2, y2) corners with the given Color. If the rectangle covers the entire + ///< bitmap area, the color palette will be reset, so that new colors can be + ///< used for drawing. + void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants = 0); + ///< Draws a filled ellipse defined by the upper left (x1, y1) and lower right + ///< (x2, y2) corners with the given Color. Quadrants controls which parts of + ///< the ellipse are actually drawn: + ///< 0 draws the entire ellipse + ///< 1..4 draws only the first, second, third or fourth quadrant, respectively + ///< 5..8 draws the right, top, left or bottom half, respectively + ///< -1..-8 draws the inverted part of the given quadrant(s) + ///< If Quadrants is not 0, the coordinates are those of the actual area, not + ///< the full circle! + void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type); + ///< Draws a "slope" into the rectangle defined by the upper left (x1, y1) and + ///< lower right (x2, y2) corners with the given Color. Type controls the + ///< direction of the slope and which side of it will be drawn: + ///< 0: horizontal, rising, lower + ///< 1: horizontal, rising, upper + ///< 2: horizontal, falling, lower + ///< 3: horizontal, falling, upper + ///< 4: vertical, rising, lower + ///< 5: vertical, rising, upper + ///< 6: vertical, falling, lower + ///< 7: vertical, falling, upper + const tIndex *Data(int x, int y); + ///< Returns the address of the index byte at the given coordinates. + }; + +struct tArea { + int x1, y1, x2, y2; + int bpp; + int Width(void) const { return x2 - x1 + 1; } + int Height(void) const { return y2 - y1 + 1; } + bool Intersects(const tArea &Area) const { return !(x2 < Area.x1 || x1 > Area.x2 || y2 < Area.y1 || y1 > Area.y2); } + }; + +#define MAXOSDAREAS 16 class cOsd { private: - enum { charWidth = 12, // average character width - lineHeight = 27 // smallest text height - }; -#ifdef DEBUG_OSD - static WINDOW *window; - enum { MaxColorPairs = 16 }; - static int colorPairs[MaxColorPairs]; - static void SetColor(eDvbColor colorFg, eDvbColor colorBg = clrBackground); -#else - static cOsdBase *osd; -#endif - static int cols, rows; + static bool isOpen; + cBitmap *savedRegion; + cBitmap *bitmaps[MAXOSDAREAS]; + int numBitmaps; + int left, top, width, height; public: - static void Initialize(void); + cOsd(int Left, int Top); + ///< Initializes the OSD with the given coordinates. + ///< By default it is assumed that the full area will be able to display + ///< full 32 bit graphics (ARGB with eight bit for each color and the alpha + ///< value, repectively). However, the actual hardware in use may not be + ///< able to display such a high resolution OSD, so there is an option to + ///< divide the full OSD area into several sub-areas with lower color depths + ///< and individual palettes. The sub-areas need not necessarily cover the + ///< entire OSD area, but only the OSD area actually covered by sub-areas + ///< will be available for drawing. + ///< At least one area must be defined in order to set the actual width and + ///< height of the OSD. Also, the caller must first try to use an area that + ///< consists of only one sub-area that covers the entire drawing space, + ///< and should require only the minimum necessary color depth. This is + ///< because a derived cOsd class may or may not be able to handle more + ///< than one area. + virtual ~cOsd(); + ///< Shuts down the OSD. + static bool IsOpen(void) { return isOpen; } + int Left(void) { return left; } + int Top(void) { return top; } + int Width(void) { return width; } + int Height(void) { return height; } + cBitmap *GetBitmap(int Area); + ///< Returns a pointer to the bitmap for the given Area, or NULL if no + ///< such bitmap exists. + virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas); + ///< Checks whether the OSD can display the given set of sub-areas. + ///< The return value indicates whether a call to SetAreas() with this + ///< set of areas will succeed. CanHandleAreas() may be called with an + ///< OSD that is already in use with other areas and will not interfere + ///< with the current operation of the OSD. + ///< A derived class must first call the base class CanHandleAreas() + ///< to check the basic conditions, like not overlapping etc. + virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); + ///< Sets the sub-areas to the given areas. + ///< The return value indicates whether the operation was successful. + ///< If an error is reported, nothing will have changed and the previous + ///< OSD (if any) will still be displayed as before. + ///< If the OSD has been divided into several sub-areas, all areas that + ///< are part of the rectangle that surrounds a given drawing operation + ///< will be drawn into, with the proper offsets. + virtual void SaveRegion(int x1, int y1, int x2, int y2); + ///< Saves the region defined by the given coordinates for later restoration + ///< through RestoreRegion(). Only one saved region can be active at any + ///< given time. + virtual void RestoreRegion(void); + ///< Restores the region previously saved by a call to SaveRegion(). + ///< If SaveRegion() has not been called before, nothing will happen. + virtual eOsdError SetPalette(const cPalette &Palette, int Area); + ///< Sets the Palette for the given Area (the first area is numbered 0). + virtual void DrawPixel(int x, int y, tColor Color); + ///< Sets the pixel at the given coordinates to the given Color, which is + ///< a full 32 bit ARGB value. + ///< If the OSD area has been divided into separate sub-areas, and the + ///< given coordinates don't fall into any of these sub-areas, no pixel will + ///< be set. + virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0); + ///< Sets the pixels in the OSD with the data from the given + ///< Bitmap, putting the upper left corner of the Bitmap at (x, y). + ///< If ColorFg or ColorBg is given, the first palette entry of the Bitmap + ///< will be mapped to ColorBg and the second palette entry will be mapped to + ///< ColorFg (palette indexes are defined so that 0 is the background and + ///< 1 is the foreground color). + virtual void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault); + ///< Draws the given string at coordinates (x, y) with the given foreground + ///< and background color and font. If Width and Height are given, the text + ///< will be drawn into a rectangle with the given size and the given + ///< Alignment (default is top-left). + virtual void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color); + ///< Draws a filled rectangle defined by the upper left (x1, y1) and lower right + ///< (x2, y2) corners with the given Color. + virtual void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants = 0); + ///< Draws a filled ellipse defined by the upper left (x1, y1) and lower right + ///< (x2, y2) corners with the given Color. Quadrants controls which parts of + ///< the ellipse are actually drawn: + ///< 0 draws the entire ellipse + ///< 1..4 draws only the first, second, third or fourth quadrant, respectively + ///< 5..8 draws the right, top, left or bottom half, respectively + ///< -1..-8 draws the inverted part of the given quadrant(s) + ///< If Quadrants is not 0, the coordinates are those of the actual area, not + ///< the full circle! + void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type); + ///< Draws a "slope" into the rectangle defined by the upper left (x1, y1) and + ///< lower right (x2, y2) corners with the given Color. Type controls the + ///< direction of the slope and which side of it will be drawn: + ///< 0: horizontal, rising, lower + ///< 1: horizontal, rising, upper + ///< 2: horizontal, falling, lower + ///< 3: horizontal, falling, upper + ///< 4: vertical, rising, lower + ///< 5: vertical, rising, upper + ///< 6: vertical, falling, lower + ///< 7: vertical, falling, upper + virtual void Flush(void); + ///< Actually commits all data to the OSD hardware. + }; + +class cOsdProvider { +private: + static cOsdProvider *osdProvider; +protected: + virtual cOsd *CreateOsd(int Left, int Top) = 0; + ///< Returns a pointer to a newly created cOsd object, which will be located + ///< at the given coordinates. +public: + cOsdProvider(void); + //XXX maybe parameter to make this one "sticky"??? (frame-buffer etc.) + virtual ~cOsdProvider(); + static cOsd *NewOsd(int Left, int Top); + ///< Returns a pointer to a newly created cOsd object, which will be located + ///< at the given coordinates. When the cOsd object is no longer needed, the + ///< caller must delete it. static void Shutdown(void); - static cOsdBase *OpenRaw(int x, int y); - // Returns a raw OSD without any predefined windows or colors. - // If the "normal" OSD is currently in use, NULL will be returned. - // The caller must delete this object before the "normal" OSD is used again! - static void Open(int w, int h); - static void Close(void); - static void Clear(void); - static void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground); - static void SetBitmap(int x, int y, const cBitmap &Bitmap); - static void ClrEol(int x, int y, eDvbColor color = clrBackground); - static int CellWidth(void); - static int LineHeight(void); - static int Width(unsigned char c); - static int WidthInCells(const char *s); - static eDvbFont SetFont(eDvbFont Font); - static void Text(int x, int y, const char *s, eDvbColor colorFg = clrWhite, eDvbColor colorBg = clrBackground); - static void Flush(void); + ///< Shuts down the OSD provider facility by deleting the current OSD provider. }; -class cOsdItem : public cListObject { +class cTextScroller { private: - char *text; - int offset; - eOSState state; -protected: - bool fresh; - bool userColor; - eDvbColor fgColor, bgColor; + cOsd *osd; + int left, top, width, height; + const cFont *font; + tColor colorFg, colorBg; + int offset, shown; + cTextWrapper textWrapper; + void DrawText(void); public: - cOsdItem(eOSState State = osUnknown); - cOsdItem(const char *Text, eOSState State = osUnknown); - virtual ~cOsdItem(); - bool HasUserColor(void) { return userColor; } - void SetText(const char *Text, bool Copy = true); - void SetColor(eDvbColor FgColor, eDvbColor BgColor = clrBackground); - const char *Text(void) { return text; } - virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); - virtual void Set(void) {} - virtual eOSState ProcessKey(eKeys Key); - }; - -class cOsdObject { - friend class cOsdMenu; -private: - bool isMenu; -protected: - bool needsFastResponse; -public: - cOsdObject(bool FastResponse = false) { isMenu = false; needsFastResponse = FastResponse; } - virtual ~cOsdObject() {} - int Width(void) { return Interface->Width(); } - int Height(void) { return Interface->Height(); } - bool NeedsFastResponse(void) { return needsFastResponse; } - bool IsMenu(void) { return isMenu; } - virtual void Show(void) {} - virtual eOSState ProcessKey(eKeys Key) { return osUnknown; } - }; - -class cOsdMenu : public cOsdObject, public cList { -private: - char *title; - int cols[cInterface::MaxCols]; - int first, current, marked; - cOsdMenu *subMenu; - const char *helpRed, *helpGreen, *helpYellow, *helpBlue; - char *status; - int digit; - bool hasHotkeys; -protected: - bool visible; - const char *hk(const char *s); - void SetHasHotkeys(void); - virtual void Clear(void); - bool SpecialItem(int idx); - void SetCurrent(cOsdItem *Item); - void RefreshCurrent(void); - void DisplayCurrent(bool Current); - void CursorUp(void); - void CursorDown(void); - void PageUp(void); - void PageDown(void); - void Mark(void); - eOSState HotKey(eKeys Key); - eOSState AddSubMenu(cOsdMenu *SubMenu); - eOSState CloseSubMenu(); - bool HasSubMenu(void) { return subMenu; } - void SetStatus(const char *s); - void SetTitle(const char *Title, bool ShowDate = true); - void SetHelp(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); - virtual void Del(int Index); -public: - cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); - virtual ~cOsdMenu(); - int Current(void) { return current; } - void Add(cOsdItem *Item, bool Current = false, cOsdItem *After = NULL); - void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL); - virtual void Display(void); - virtual eOSState ProcessKey(eKeys Key); + cTextScroller(void); + cTextScroller(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg); + void Set(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg); + void Reset(void); + int Left(void) { return left; } + int Top(void) { return top; } + int Width(void) { return width; } + int Height(void) { return height; } + int Total(void) { return textWrapper.Lines(); } + int Offset(void) { return offset; } + int Shown(void) { return shown; } + bool CanScroll(void) { return CanScrollUp() || CanScrollDown(); } + bool CanScrollUp(void) { return offset > 0; } + bool CanScrollDown(void) { return offset + shown < Total(); } + void Scroll(bool Up, bool Page); }; #endif //__OSD_H diff --git a/osdbase.c b/osdbase.c index 71794068..08aef965 100644 --- a/osdbase.c +++ b/osdbase.c @@ -4,495 +4,410 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osdbase.c 1.12 2004/01/31 10:31:13 kls Exp $ + * $Id: osdbase.c 1.13 2004/05/01 10:51:43 kls Exp $ */ #include "osdbase.h" -#include -#include -#include -#include -#include -#include "tools.h" +#include +#include "device.h" +#include "i18n.h" +#include "remote.h" +#include "status.h" -// --- cPalette -------------------------------------------------------------- +// --- cOsdItem -------------------------------------------------------------- -cPalette::cPalette(int Bpp) +cOsdItem::cOsdItem(eOSState State) { - maxColors = 1 << Bpp; - numColors = 0; - full = false; + text = NULL; + offset = -1; + state = State; + selectable = true; + fresh = true; } -void cPalette::SetColor(int Index, eDvbColor Color) +cOsdItem::cOsdItem(const char *Text, eOSState State) { - if (Index < maxColors) { - if (numColors < Index) - numColors = Index + 1; - used[Index] = true; - color[Index] = Color; - fetched[Index] = false; + text = NULL; + offset = -1; + state = State; + selectable = true; + fresh = true; + SetText(Text); +} + +cOsdItem::~cOsdItem() +{ + free(text); +} + +void cOsdItem::SetText(const char *Text, bool Copy) +{ + free(text); + text = Copy ? strdup(Text) : (char *)Text; // text assumes ownership! +} + +void cOsdItem::SetSelectable(bool Selectable) +{ + selectable = Selectable; +} + +void cOsdItem::SetFresh(bool Fresh) +{ + fresh = Fresh; +} + +eOSState cOsdItem::ProcessKey(eKeys Key) +{ + return Key == kOk ? state : osUnknown; +} + +// --- cOsdMenu -------------------------------------------------------------- + +cSkinDisplayMenu *cOsdMenu::displayMenu = NULL; +int cOsdMenu::displayMenuCount = 0; +int cOsdMenu::displayMenuItems = 0;//XXX dynamic??? + +cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) +{ + isMenu = true; + digit = 0; + hasHotkeys = false; + title = NULL; + SetTitle(Title); + cols[0] = c0; + cols[1] = c1; + cols[2] = c2; + cols[3] = c3; + cols[4] = c4; + first = 0; + current = marked = -1; + subMenu = NULL; + helpRed = helpGreen = helpYellow = helpBlue = NULL; + status = NULL; + if (!displayMenuCount++) { + displayMenu = Skins.Current()->DisplayMenu(); + displayMenuItems = displayMenu->MaxItems(); } } -int cPalette::Index(eDvbColor Color) +cOsdMenu::~cOsdMenu() { -#if __BYTE_ORDER == __BIG_ENDIAN - Color = eDvbColor(((Color & 0xFF) << 24) | ((Color & 0xFF00) << 8) | ((Color & 0xFF0000) >> 8) | ((Color & 0xFF000000) >> 24)); -#endif - for (int i = 0; i < numColors; i++) { - if (color[i] == Color) { - used[i] = true; - return i; - } - } - if (!full) { - if (numColors < maxColors) { - color[numColors++] = Color; - used[numColors - 1] = true; - fetched[numColors - 1] = false; - return numColors - 1; + free(title); + delete subMenu; + free(status); + displayMenu->Clear(); + cStatus::MsgOsdClear(); + if (!--displayMenuCount) + DELETENULL(displayMenu); +} + +const char *cOsdMenu::hk(const char *s) +{ + static char buffer[64]; + if (s && hasHotkeys) { + if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ') + digit = -1; // prevents automatic hotkeys - input already has them + if (digit >= 0) { + digit++; + snprintf(buffer, sizeof(buffer), " %c %s", (digit < 10) ? '0' + digit : ' ' , s); + s = buffer; } - for (int i = maxColors; --i >= 0; ) { - if (!used[i]) { - color[i] = Color; - used[i] = true; - fetched[i] = false; - return i; + } + return s; +} + +void cOsdMenu::SetHasHotkeys(void) +{ + hasHotkeys = true; + digit = 0; +} + +void cOsdMenu::SetStatus(const char *s) +{ + free(status); + status = s ? strdup(s) : NULL; + displayMenu->SetMessage(mtStatus, s); +} + +void cOsdMenu::SetTitle(const char *Title) +{ + free(title); + title = strdup(Title); +} + +void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue) +{ + // strings are NOT copied - must be constants!!! + helpRed = Red; + helpGreen = Green; + helpYellow = Yellow; + helpBlue = Blue; + displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue); + cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue); +} + +void cOsdMenu::Del(int Index) +{ + cList::Del(Get(Index)); + if (current == Count()) + current--; + if (Index == first && first > 0) + first--; +} + +void cOsdMenu::Add(cOsdItem *Item, bool Current, cOsdItem *After) +{ + cList::Add(Item, After); + if (Current) + current = Item->Index(); +} + +void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before) +{ + cList::Ins(Item, Before); + if (Current) + current = Item->Index(); +} + +void cOsdMenu::Display(void) +{ + if (subMenu) { + subMenu->Display(); + return; + } + displayMenu->Clear(); + cStatus::MsgOsdClear(); + displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX + displayMenu->SetTitle(title); + cStatus::MsgOsdTitle(title); + displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue); + cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue); + int count = Count(); + if (count > 0) { + int ni = 0; + for (cOsdItem *item = First(); item; item = Next(item)) + cStatus::MsgOsdItem(item->Text(), ni++); + if (current < 0) + current = 0; // just for safety - there HAS to be a current item! + if (current - first >= displayMenuItems || current < first) { + first = current - displayMenuItems / 2; + if (first + displayMenuItems > count) + first = count - displayMenuItems; + if (first < 0) + first = 0; + } + int i = first; + int n = 0; + for (cOsdItem *item = Get(first); item; item = Next(item)) { + displayMenu->SetItem(item->Text(), i - first, i == current, item->Selectable()); + if (i == current) + cStatus::MsgOsdCurrentItem(item->Text()); + if (++n == displayMenuItems) + break; + i++; + } + } + if (!isempty(status)) + displayMenu->SetMessage(mtStatus, status); +} + +void cOsdMenu::SetCurrent(cOsdItem *Item) +{ + current = Item ? Item->Index() : -1; +} + +void cOsdMenu::RefreshCurrent(void) +{ + cOsdItem *item = Get(current); + if (item) + item->Set(); +} + +void cOsdMenu::DisplayCurrent(bool Current) +{ + cOsdItem *item = Get(current); + if (item) { + displayMenu->SetItem(item->Text(), current - first, Current, item->Selectable()); + if (Current) + cStatus::MsgOsdCurrentItem(item->Text()); + if (!Current) + item->SetFresh(true); // leaving the current item resets 'fresh' + } +} + +void cOsdMenu::Clear(void) +{ + first = 0; + current = marked = -1; + cList::Clear(); +} + +bool cOsdMenu::SelectableItem(int idx) +{ + cOsdItem *item = Get(idx); + return item && item->Selectable(); +} + +void cOsdMenu::CursorUp(void) +{ + if (current > 0) { + int tmpCurrent = current; + while (--tmpCurrent >= 0 && !SelectableItem(tmpCurrent)); + if (tmpCurrent < 0) + return; + if (tmpCurrent >= first) + DisplayCurrent(false); + current = tmpCurrent; + if (current < first) { + first = first > displayMenuItems - 1 ? first - (displayMenuItems - 1) : 0; + if (Setup.MenuScrollPage) + current = !SelectableItem(first) ? first + 1 : first; + Display(); + } + else + DisplayCurrent(true); + } +} + +void cOsdMenu::CursorDown(void) +{ + int last = Count() - 1; + int lastOnScreen = first + displayMenuItems - 1; + + if (current < last) { + int tmpCurrent = current; + while (++tmpCurrent <= last && !SelectableItem(tmpCurrent)); + if (tmpCurrent > last) + return; + if (tmpCurrent <= lastOnScreen) + DisplayCurrent(false); + current = tmpCurrent; + if (current > lastOnScreen) { + first += displayMenuItems - 1; + lastOnScreen = first + displayMenuItems - 1; + if (lastOnScreen > last) { + first = last - (displayMenuItems - 1); + lastOnScreen = last; + } + if (Setup.MenuScrollPage) + current = !SelectableItem(lastOnScreen) ? lastOnScreen - 1 : lastOnScreen; + Display(); + } + else + DisplayCurrent(true); + } +} + +void cOsdMenu::PageUp(void) +{ + current -= displayMenuItems; + first -= displayMenuItems; + if (first < 0) + first = current = 0; + if (!SelectableItem(current)) { + current -= (current > 0) ? 1 : -1; + first = min(first, current - 1); + } + Display(); + DisplayCurrent(true); +} + +void cOsdMenu::PageDown(void) +{ + current += displayMenuItems; + first += displayMenuItems; + int count = Count(); + if (current > count - 1) { + current = count - 1; + first = max(0, count - displayMenuItems); + } + if (!SelectableItem(current)) { + current += (current < count - 1) ? 1 : -1; + first = max(first, current - displayMenuItems); + } + Display(); + DisplayCurrent(true); +} + +void cOsdMenu::Mark(void) +{ + if (Count() && marked < 0) { + marked = current; + SetStatus(tr("Up/Dn for new location - OK to move")); + } +} + +eOSState cOsdMenu::HotKey(eKeys Key) +{ + for (cOsdItem *item = First(); item; item = Next(item)) { + const char *s = item->Text(); + if (s && (s = skipspace(s)) != NULL) { + if (*s == Key - k1 + '1') { + current = item->Index(); + cRemote::Put(kOk, true); + break; } } - esyslog("ERROR: too many different colors used in palette"); - full = true; - } - return 0; -} - -void cPalette::Reset(void) -{ - for (int i = 0; i < numColors; i++) - used[i] = fetched[i] = false; - full = false; -} - -const eDvbColor *cPalette::NewColors(int &FirstColor, int &LastColor) -{ - for (FirstColor = 0; FirstColor < numColors; FirstColor++) { - if (!fetched[FirstColor]) { - for (LastColor = FirstColor; LastColor < numColors && !fetched[LastColor]; LastColor++) - fetched[LastColor] = true; - LastColor--; // the loop ended one past the last one! - return &color[FirstColor]; - } } - return NULL; + return osContinue; } -const eDvbColor *cPalette::AllColors(int &NumColors) +eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) { - NumColors = numColors; - return numColors ? color : NULL; + delete subMenu; + subMenu = SubMenu; + subMenu->Display(); + return osContinue; // convenience return value } -void cPalette::Take(const cPalette &Palette, tIndexes *Indexes) +eOSState cOsdMenu::CloseSubMenu() { - for (int i = 0; i < Palette.numColors; i++) { - if (Palette.used[i]) { - int n = Index(Palette.color[i]); - if (Indexes) - (*Indexes)[i] = n; - } - } + delete subMenu; + subMenu = NULL; + RefreshCurrent(); + Display(); + return osContinue; // convenience return value } -// --- cBitmap --------------------------------------------------------------- - -cBitmap::cBitmap(int Width, int Height, int Bpp, bool ClearWithBackground) -:cPalette(Bpp) +eOSState cOsdMenu::ProcessKey(eKeys Key) { - width = Width; - height = Height; - clearWithBackground = ClearWithBackground; - bitmap = NULL; - fontType = fontOsd; - font = NULL; - if (width > 0 && height > 0) { - bitmap = MALLOC(u_char, width * height); - if (bitmap) { - Clean(); - memset(bitmap, 0x00, width * height); - SetFont(fontOsd); - } - else - esyslog("ERROR: can't allocate bitmap!"); + if (subMenu) { + eOSState state = subMenu->ProcessKey(Key); + if (state == osBack) + return CloseSubMenu(); + return state; } - else - esyslog("ERROR: illegal bitmap parameters (%d, %d)!", width, height); -} -cBitmap::~cBitmap() -{ - free(bitmap); -} - -eDvbFont cBitmap::SetFont(eDvbFont Font) -{ - eDvbFont oldFont = fontType; - if (fontType != Font || !font) { - font = cFont::GetFont(Font); - fontType = Font; - } - return oldFont; -} - -bool cBitmap::Dirty(int &x1, int &y1, int &x2, int &y2) -{ - if (dirtyX2 >= 0) { - //XXX Workaround: apparently the bitmap sent to the driver always has to be a multiple - //XXX of 8 bits wide, and (dx * dy) also has to be a multiple of 8. - //TODO Fix driver (should be able to handle any size bitmaps!) - while ((dirtyX1 > 0 || dirtyX2 < width - 1) && ((dirtyX2 - dirtyX1) & 7) != 7) { - if (dirtyX2 < width - 1) - dirtyX2++; - else if (dirtyX1 > 0) - dirtyX1--; - } - //XXX "... / 2" <==> Bpp??? - while ((dirtyY1 > 0 || dirtyY2 < height - 1) && (((dirtyX2 - dirtyX1 + 1) * (dirtyY2 - dirtyY1 + 1) / 2) & 7) != 0) { - if (dirtyY2 < height - 1) - dirtyY2++; - else if (dirtyY1 > 0) - dirtyY1--; - } - while ((dirtyX1 > 0 || dirtyX2 < width - 1) && (((dirtyX2 - dirtyX1 + 1) * (dirtyY2 - dirtyY1 + 1) / 2) & 7) != 0) { - if (dirtyX2 < width - 1) - dirtyX2++; - else if (dirtyX1 > 0) - dirtyX1--; - } - x1 = dirtyX1; - y1 = dirtyY1; - x2 = dirtyX2; - y2 = dirtyY2; - return true; - } - return false; -} - -void cBitmap::Clean(void) -{ - dirtyX1 = width; - dirtyY1 = height; - dirtyX2 = -1; - dirtyY2 = -1; -} - -void cBitmap::SetIndex(int x, int y, u_char Index) -{ - if (bitmap) { - if (0 <= x && x < width && 0 <= y && y < height) { - if (bitmap[width * y + x] != Index) { - bitmap[width * y + x] = Index; - if (dirtyX1 > x) dirtyX1 = x; - if (dirtyY1 > y) dirtyY1 = y; - if (dirtyX2 < x) dirtyX2 = x; - if (dirtyY2 < y) dirtyY2 = y; - } + cOsdItem *item = Get(current); + if (marked < 0 && item) { + eOSState state = item->ProcessKey(Key); + if (state != osUnknown) { + DisplayCurrent(true); + return state; } } -} - -void cBitmap::SetPixel(int x, int y, eDvbColor Color) -{ - SetIndex(x, y, Index(Color)); -} - -void cBitmap::SetBitmap(int x, int y, const cBitmap &Bitmap) -{ - if (bitmap && Bitmap.bitmap) { - tIndexes Indexes; - Take(Bitmap, &Indexes); - for (int ix = 0; ix < Bitmap.width; ix++) { - for (int iy = 0; iy < Bitmap.height; iy++) - SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]); - } - } -} - -int cBitmap::Width(unsigned char c) -{ - return font ? font->Width(c) : -1; -} - -int cBitmap::Width(const char *s) -{ - return font ? font->Width(s) : -1; -} - -void cBitmap::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor ColorBg) -{ - if (bitmap) { - u_char fg = Index(ColorFg); - u_char bg = Index(ColorBg); - int h = font->Height(s); - while (s && *s) { - const cFont::tCharData *CharData = font->CharData(*s++); - if (int(x + CharData->width) > width) - break; - 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); - PixelData >>= 1; + switch (Key) { + case k1...k9: return hasHotkeys ? HotKey(Key) : osUnknown; + case kUp|k_Repeat: + case kUp: CursorUp(); break; + case kDown|k_Repeat: + case kDown: CursorDown(); break; + case kLeft|k_Repeat: + case kLeft: PageUp(); break; + case kRight|k_Repeat: + case kRight: PageDown(); break; + case kBack: return osBack; + case kOk: if (marked >= 0) { + SetStatus(NULL); + if (marked != current) + Move(marked, current); + marked = -1; + break; } - } - x += CharData->width; - } - } + // else run into default + default: if (marked < 0) + return osUnknown; + } + return osContinue; } -void cBitmap::Fill(int x1, int y1, int x2, int y2, eDvbColor Color) -{ - if (bitmap) { - u_char c = Index(Color); - for (int y = y1; y <= y2; y++) - for (int x = x1; x <= x2; x++) - SetIndex(x, y, c); - } -} - -void cBitmap::Clear(void) -{ - Reset(); - if (clearWithBackground) - Fill(0, 0, width - 1, height - 1, clrBackground); -} - -const u_char *cBitmap::Data(int x, int y) -{ - return &bitmap[y * width + x]; -} - -// --- cWindow --------------------------------------------------------------- - -cWindow::cWindow(int Handle, int x, int y, int w, int h, int Bpp, bool ClearWithBackground, bool Tiled) -:cBitmap(w, h, Bpp, ClearWithBackground) -{ - handle = Handle; - x0 = x; - y0 = y; - bpp = Bpp; - tiled = Tiled; - shown = false; -} - -bool cWindow::Contains(int x, int y) -{ - x -= x0; - y -= y0; - return x >= 0 && y >= 0 && x < width && y < height; -} - -void cWindow::Relocate(int x, int y) -{ - x0 = x; - y0 = y; -} - -void cWindow::Fill(int x1, int y1, int x2, int y2, eDvbColor Color) -{ - if (tiled) { - x1 -= x0; - y1 -= y0; - x2 -= x0; - y2 -= y0; - } - cBitmap::Fill(x1, y1, x2, y2, Color); -} - -void cWindow::SetBitmap(int x, int y, const cBitmap &Bitmap) -{ - if (tiled) { - x -= x0; - y -= y0; - } - cBitmap::SetBitmap(x, y, Bitmap); -} - -void cWindow::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor ColorBg) -{ - if (tiled) { - x -= x0; - y -= y0; - } - cBitmap::Text(x, y, s, ColorFg, ColorBg); -} - -const u_char *cWindow::Data(int x, int y) -{ - return cBitmap::Data(x, y); -} - -// --- cOsdBase -------------------------------------------------------------- - -cOsdBase::cOsdBase(int x, int y) -{ - numWindows = 0; - x0 = x; - y0 = y; -} - -cOsdBase::~cOsdBase() -{ - for (int i = 0; i < numWindows; i++) - delete window[i]; -} - -tWindowHandle cOsdBase::Create(int x, int y, int w, int h, int Bpp, bool ClearWithBackground, bool Tiled) -{ - if (numWindows < MAXNUMWINDOWS) { - if (x >= 0 && y >= 0 && w > 0 && h > 0 && (Bpp == 1 || Bpp == 2 || Bpp == 4 || Bpp == 8)) { - if ((w & 0x03) != 0) { - w += 4 - (w & 0x03); - dsyslog("OSD window width must be a multiple of 4 - increasing to %d", w); - } - cWindow *win = new cWindow(numWindows, x, y, w, h, Bpp, ClearWithBackground, Tiled); - if (OpenWindow(win)) { - window[win->Handle()] = win; - numWindows++; - return win->Handle(); - } - else - delete win; - } - else - esyslog("ERROR: illegal OSD parameters"); - } - else - esyslog("ERROR: too many OSD windows"); - return -1; -} - -void cOsdBase::AddColor(eDvbColor Color, tWindowHandle Window) -{ - cWindow *w = GetWindow(Window); - if (w) { - w->Index(Color); - w->Reset(); - } -} - -cWindow *cOsdBase::GetWindow(int x, int y) -{ - for (int i = 0; i < numWindows; i++) { - if (window[i]->Tiled() && window[i]->Contains(x, y)) - return window[i]; - } - return NULL; -} - -cWindow *cOsdBase::GetWindow(tWindowHandle Window) -{ - if (0 <= Window && Window < numWindows) - return window[Window]; - if (Window == LAST_CREATED_WINDOW && numWindows > 0) - return window[numWindows - 1]; - return NULL; -} - -void cOsdBase::Flush(void) -{ - for (int i = 0; i < numWindows; i++) { - CommitWindow(window[i]); - window[i]->Clean(); - } - // Showing the windows in a separate loop to avoid seeing them come up one after another - for (int i = 0; i < numWindows; i++) { - if (!window[i]->Shown()) - ShowWindow(window[i]); - } -} - -void cOsdBase::Clear(tWindowHandle Window) -{ - if (Window == ALL_TILED_WINDOWS || Window == ALL_WINDOWS) { - for (int i = 0; i < numWindows; i++) - if (Window == ALL_WINDOWS || window[i]->Tiled()) - window[i]->Clear(); - } - else { - cWindow *w = GetWindow(Window); - if (w) - w->Clear(); - } -} - -void cOsdBase::Fill(int x1, int y1, int x2, int y2, eDvbColor Color, tWindowHandle Window) -{ - cWindow *w = (Window == ALL_TILED_WINDOWS) ? GetWindow(x1, y1) : GetWindow(Window); - if (w) - w->Fill(x1, y1, x2, y2, Color); -} - -void cOsdBase::SetBitmap(int x, int y, const cBitmap &Bitmap, tWindowHandle Window) -{ - cWindow *w = (Window == ALL_TILED_WINDOWS) ? GetWindow(x, y) : GetWindow(Window); - if (w) - w->SetBitmap(x, y, Bitmap); -} - -int cOsdBase::Width(unsigned char c) -{ - return numWindows ? window[0]->Width(c) : 0; -} - -int cOsdBase::Width(const char *s) -{ - return numWindows ? window[0]->Width(s) : 0; -} - -eDvbFont cOsdBase::SetFont(eDvbFont Font) -{ - eDvbFont oldFont = Font; - for (int i = 0; i < numWindows; i++) - oldFont = window[i]->SetFont(Font); - return oldFont; -} - -void cOsdBase::Text(int x, int y, const char *s, eDvbColor ColorFg, eDvbColor ColorBg, tWindowHandle Window) -{ - cWindow *w = (Window == ALL_TILED_WINDOWS) ? GetWindow(x, y) : GetWindow(Window); - if (w) - w->Text(x, y, s, ColorFg, ColorBg); -} - -void cOsdBase::Relocate(tWindowHandle Window, int x, int y, int NewWidth, int NewHeight) -{ - cWindow *w = GetWindow(Window); - if (w) { - if (NewWidth > 0 && NewHeight > 0) { - if ((NewWidth & 0x03) != 0) { - NewWidth += 4 - (NewWidth & 0x03); - dsyslog("OSD window width must be a multiple of 4 - increasing to %d", NewWidth); - } - CloseWindow(w); - cWindow *NewWindow = new cWindow(w->Handle(), x, y, NewWidth, NewHeight, w->Bpp(), w->ClearWithBackground(), w->Tiled()); - window[w->Handle()] = NewWindow; - delete w; - OpenWindow(NewWindow); - } - else { - MoveWindow(w, x, y); - w->Relocate(x, y); - } - } -} - -void cOsdBase::Hide(tWindowHandle Window) -{ - HideWindow(GetWindow(Window), true); -} - -void cOsdBase::Show(tWindowHandle Window) -{ - HideWindow(GetWindow(Window), false); -} diff --git a/osdbase.h b/osdbase.h index 3277d820..53fbf6b8 100644 --- a/osdbase.h +++ b/osdbase.h @@ -4,221 +4,127 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osdbase.h 1.9 2004/01/31 10:28:49 kls Exp $ + * $Id: osdbase.h 1.10 2004/04/30 13:40:11 kls Exp $ */ #ifndef __OSDBASE_H #define __OSDBASE_H -#include -#include "font.h" +#include "config.h" +#include "osd.h" +#include "skins.h" +#include "tools.h" -#define MAXNUMCOLORS 256 +enum eOSState { osUnknown, + osContinue, + osSchedule, + osChannels, + osTimers, + osRecordings, + osPlugin, + osSetup, + osCommands, + osPause, + osRecord, + osReplay, + osStopRecord, + osStopReplay, + osCancelEdit, + osSwitchDvb, + osBack, + osEnd, + os_User, // the following values can be used locally + osUser1, + osUser2, + osUser3, + osUser4, + osUser5, + osUser6, + osUser7, + osUser8, + osUser9, + osUser10, + }; -enum eDvbColor { -#ifdef DEBUG_OSD - clrBackground, - clrTransparent = clrBackground, - clrBlack = clrBackground, - clrRed, - clrGreen, - clrYellow, - clrBlue, - clrMagenta, - clrCyan, - clrWhite, -#else - clrTransparent = 0x00000000, - clrBackground = 0x7F000000, // 50% gray - clrBlack = 0xFF000000, - clrRed = 0xFF1414FC, - clrGreen = 0xFF24FC24, - clrYellow = 0xFF24C0FC, - clrMagenta = 0xFFFC00B0, - clrBlue = 0xFFFC0000, - clrCyan = 0xFFFCFC00, - clrWhite = 0xFFFCFCFC, -#endif - }; - -class cPalette { +class cOsdItem : public cListObject { private: - eDvbColor color[MAXNUMCOLORS]; - int maxColors, numColors; - bool used[MAXNUMCOLORS]; - bool fetched[MAXNUMCOLORS]; - bool full; + char *text; + int offset; + eOSState state; + bool selectable; protected: - typedef u_char tIndexes[MAXNUMCOLORS]; + bool fresh; public: - cPalette(int Bpp); - int Index(eDvbColor Color); - void Reset(void); - void SetColor(int Index, eDvbColor Color); - eDvbColor GetColor(int Index) { return Index < maxColors ? color[Index] : clrBlack; } - const eDvbColor *NewColors(int &FirstColor, int &LastColor); - // With every call this function returns a consecutive range of - // color entries that have been added since the last call. The - // return value is the address of the first new color, and the - // index of the first and last new color are returned in the given - // int parameters. If there are no new color entries, NULL will - // be returned. - const eDvbColor *AllColors(int &NumColors); - // Returns a pointer to the complete color table and stores the - // number of valid entries in NumColors. If no colors have been - // stored yet, NumColors will be set to 0 and the function will - // return NULL. - void Take(const cPalette &Palette, tIndexes *Indexes = NULL); + cOsdItem(eOSState State = osUnknown); + cOsdItem(const char *Text, eOSState State = osUnknown); + virtual ~cOsdItem(); + bool Selectable(void) { return selectable; } + void SetText(const char *Text, bool Copy = true); + void SetSelectable(bool Selectable); + void SetFresh(bool Fresh); + const char *Text(void) { return text; } + virtual void Set(void) {} + virtual eOSState ProcessKey(eKeys Key); }; -class cBitmap : public cPalette { +class cOsdObject { + friend class cOsdMenu; private: - const cFont *font; - eDvbFont fontType; - u_char *bitmap; - bool clearWithBackground; + bool isMenu; protected: - int width, height; - int dirtyX1, dirtyY1, dirtyX2, dirtyY2; + bool needsFastResponse; public: - cBitmap(int Width, int Height, int Bpp, bool ClearWithBackground = true); - virtual ~cBitmap(); - bool ClearWithBackground(void) { return clearWithBackground; } - eDvbFont SetFont(eDvbFont Font); - bool Dirty(int &x1, int &y1, int &x2, int &y2); - void SetIndex(int x, int y, u_char Index); - void SetPixel(int x, int y, eDvbColor Color); - void SetBitmap(int x, int y, const cBitmap &Bitmap); - int Width(void) { return width; } - int Width(unsigned char c); - int Width(const char *s); - int Height(void) { return height; } - 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 Clean(void); - void Clear(void); - const u_char *Data(int x, int y); + cOsdObject(bool FastResponse = false) { isMenu = false; needsFastResponse = FastResponse; } + virtual ~cOsdObject() {} + bool NeedsFastResponse(void) { return needsFastResponse; } + bool IsMenu(void) { return isMenu; } + virtual void Show(void) {} + virtual eOSState ProcessKey(eKeys Key) { return osUnknown; } }; -#define MAXNUMWINDOWS 7 // OSD windows are counted 1...7 - -class cWindow : public cBitmap { +class cOsdMenu : public cOsdObject, public cList { private: - int handle; // the index within the OSD's window array (0...MAXNUMWINDOWS - 1) - int x0, y0; - int bpp; - bool tiled; - bool shown; -public: - cWindow(int Handle, int x, int y, int w, int h, int Bpp, bool ClearWithBackground, bool Tiled); - int X0(void) { return x0; } - int Y0(void) { return y0; } - int Bpp(void) { return bpp; } - bool Tiled(void) { return tiled; } - bool Shown(void) { bool s = shown; shown = true; return s; } - int Handle(void) { return handle; } - bool Contains(int x, int y); - void Relocate(int x, int y); - void Fill(int x1, int y1, int x2, int y2, eDvbColor Color); - void SetBitmap(int x, int y, const cBitmap &Bitmap); - void Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground); - const u_char *Data(int x, int y); - }; - -typedef int tWindowHandle; - -// '-1' is used as an error return value! -#define ALL_WINDOWS (-2) -#define ALL_TILED_WINDOWS (-3) -#define LAST_CREATED_WINDOW (-4) - -class cOsdBase { -private: - int numWindows; - int x0, y0; - cWindow *window[MAXNUMWINDOWS]; - cWindow *GetWindow(int x, int y); - cWindow *GetWindow(tWindowHandle Window); + static cSkinDisplayMenu *displayMenu; + static int displayMenuCount; + static int displayMenuItems; + char *title; + int cols[cSkinDisplayMenu::MaxTabs]; + int first, current, marked; + cOsdMenu *subMenu; + const char *helpRed, *helpGreen, *helpYellow, *helpBlue; + char *status; + int digit; + bool hasHotkeys; protected: - cWindow *GetWindowNr(int i) { return i < MAXNUMWINDOWS ? window[i] : NULL; } - int NumWindows(void) { return numWindows; } - int X0(void) { return x0; } - int Y0(void) { return y0; } - virtual bool OpenWindow(cWindow *Window) = 0; - // Opens the window on the OSD hardware, without actually showing it (the - // initial state shall be "hidden"). - virtual void CommitWindow(cWindow *Window) = 0; - // Write any modified data and color definitions to the OSD hardware. - // Use the window's Dirty() function to find out which area of data - // actually needs to be transferred. If the window has not yet been explicitly - // shown through a call to ShowWindow(), no visible activity shall take place. - virtual void ShowWindow(cWindow *Window) = 0; - // Make the window actually visible on the OSD hardware. - virtual void HideWindow(cWindow *Window, bool Hide) = 0; - // Temporarily hide the window (if 'Hide' is 'true') or make a previously - // hidden window visible again (if 'Hide' is 'false'). - virtual void MoveWindow(cWindow *Window, int x, int y) = 0; - // Move the window to a new location. - virtual void CloseWindow(cWindow *Window) = 0; - // Close the window and release any OSD hardware resources allocated for it. + cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; } + const char *hk(const char *s); + void SetHasHotkeys(void); + virtual void Clear(void); + bool SelectableItem(int idx); + void SetCurrent(cOsdItem *Item); + void RefreshCurrent(void); + void DisplayCurrent(bool Current); + void CursorUp(void); + void CursorDown(void); + void PageUp(void); + void PageDown(void); + void Mark(void); + eOSState HotKey(eKeys Key); + eOSState AddSubMenu(cOsdMenu *SubMenu); + eOSState CloseSubMenu(); + bool HasSubMenu(void) { return subMenu; } + void SetStatus(const char *s); + void SetTitle(const char *Title); + void SetHelp(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); + virtual void Del(int Index); public: - cOsdBase(int x, int y); - // Initializes the OSD, starting at screen coordinates (x, y). - virtual ~cOsdBase(); - // Destroys all windows and shuts down the OSD. - tWindowHandle Create(int x, int y, int w, int h, int Bpp, bool ClearWithBackground = true, bool Tiled = true); - // Creates a window at coordinates (x, y), which are relative to the OSD's - // origin given in the constructor, with the given width, height and color - // depth. ClearWithBackground controls whether the window will be filled with - // clrBackground when it is cleared. Setting this to 'false' may be useful - // for windows that don't need clrBackground but want to save this color - // palette entry for a different color. Tiled controls whether this will - // be part of a multi section OSD (with several windows that all have - // different color depths and palettes and form one large OSD area), or - // whether this is a "standalone" window that will be drawn "in front" - // of any windows defined *after* this one (this can be used for highlighting - // certain parts of the OSD, as would be done in a 'cursor'). - // Returns a handle that can be used to identify this window. - void AddColor(eDvbColor Color, tWindowHandle Window = LAST_CREATED_WINDOW); - // Adds the Color to the color palette of the given window if it is not - // already contained in the palette (and if the palette still has free - // slots for new colors). The default value LAST_CREATED_WINDOW will - // access the most recently created window, without the need of explicitly - // using a window handle. - void Flush(void); - // Actually commits all data of all windows to the OSD. - void Clear(tWindowHandle Window = ALL_TILED_WINDOWS); - // Clears the given window. If ALL_TILED_WINDOWS is given, only the tiled - // windows will be cleared, leaving the standalone windows untouched. If - // ALL_WINDOWS is given, the standalone windows will also be cleared. - void Fill(int x1, int y1, int x2, int y2, eDvbColor Color, tWindowHandle Window = ALL_TILED_WINDOWS); - // Fills the rectangle defined by the upper left (x1, y2) and lower right - // (x2, y2) corners with the given Color. If a specific window is given, - // the coordinates are relative to that window's upper left corner. - // Otherwise they are relative to the upper left corner of the entire OSD. - // If all tiled windows are selected, only that window which contains the - // point (x1, y1) will actually be filled. - void SetBitmap(int x, int y, const cBitmap &Bitmap, tWindowHandle Window = ALL_TILED_WINDOWS); - // Sets the pixels within the given window with the data from the given - // Bitmap. See Fill() for details about the coordinates. - int Width(unsigned char c); - // Returns the width (in pixels) of the given character in the current font. - int Width(const char *s); - // Returns the width (in pixels) of the given string in the current font. - eDvbFont SetFont(eDvbFont Font); - // Sets the current font for subsequent Width() and Text() operations. - void Text(int x, int y, const char *s, eDvbColor ColorFg = clrWhite, eDvbColor ColorBg = clrBackground, tWindowHandle Window = ALL_TILED_WINDOWS); - // Writes the given string at coordinates (x, y) with the given foreground - // and background color into the given window (see Fill() for details - // about the coordinates). - void Relocate(tWindowHandle Window, int x, int y, int NewWidth = -1, int NewHeight = -1); - // Moves the given window to the new location at (x, y). If NewWidth and - // NewHeight are also given, the window will also be resized to the new - // width and height. - void Hide(tWindowHandle Window); - // Hides the given window. - void Show(tWindowHandle Window); - // Shows the given window. + cOsdMenu(const char *Title, int c0 = 0, int c1 = 0, int c2 = 0, int c3 = 0, int c4 = 0); + virtual ~cOsdMenu(); + int Current(void) { return current; } + void Add(cOsdItem *Item, bool Current = false, cOsdItem *After = NULL); + void Ins(cOsdItem *Item, bool Current = false, cOsdItem *Before = NULL); + virtual void Display(void); + virtual eOSState ProcessKey(eKeys Key); }; #endif //__OSDBASE_H diff --git a/player.c b/player.c index a88c5010..c08e4230 100644 --- a/player.c +++ b/player.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: player.c 1.6 2002/11/02 14:55:37 kls Exp $ + * $Id: player.c 1.7 2004/04/16 13:34:11 kls Exp $ */ #include "player.h" @@ -80,7 +80,7 @@ void cControl::Attach(void) if (cDevice::PrimaryDevice()->AttachPlayer(control->player)) control->attached = true; else { - Interface->Error(tr("Channel locked (recording)!")); + Skins.Message(mtError, tr("Channel locked (recording)!")); Shutdown(); } } diff --git a/player.h b/player.h index faeab1a7..85b7bd13 100644 --- a/player.h +++ b/player.h @@ -4,14 +4,14 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: player.h 1.10 2002/11/03 11:27:30 kls Exp $ + * $Id: player.h 1.11 2004/04/30 13:45:59 kls Exp $ */ #ifndef __PLAYER_H #define __PLAYER_H #include "device.h" -#include "osd.h" +#include "osdbase.h" class cPlayer { friend class cDevice; diff --git a/plugin.h b/plugin.h index 0a753faa..2d1cf5d0 100644 --- a/plugin.h +++ b/plugin.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: plugin.h 1.6 2003/05/09 14:57:55 kls Exp $ + * $Id: plugin.h 1.7 2004/04/30 13:46:21 kls Exp $ */ #ifndef __PLUGIN_H @@ -12,7 +12,7 @@ #include "i18n.h" #include "menuitems.h" -#include "osd.h" +#include "osdbase.h" #include "tools.h" #define VDRPLUGINCREATOR(PluginClass) extern "C" void *VDRPluginCreator(void) { return new PluginClass; } diff --git a/recording.c b/recording.c index a181ff45..05efc440 100644 --- a/recording.c +++ b/recording.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.c 1.86 2003/11/08 15:25:35 kls Exp $ + * $Id: recording.c 1.87 2004/05/07 14:24:18 kls Exp $ */ #include "recording.h" @@ -18,6 +18,7 @@ #include "i18n.h" #include "interface.h" #include "remux.h" //XXX+ I_FRAME +#include "skins.h" #include "tools.h" #include "videodir.h" @@ -461,7 +462,7 @@ char *cRecording::SortName(void) return sortBuffer; } -int cRecording::GetResume(void) +int cRecording::GetResume(void) const { if (resume == RESUME_NOT_INITIALIZED) { cResumeFile ResumeFile(FileName()); @@ -476,7 +477,7 @@ bool cRecording::operator< (const cListObject &ListObject) return strcasecmp(SortName(), r->SortName()) < 0; } -const char *cRecording::FileName(void) +const char *cRecording::FileName(void) const { if (!fileName) { struct tm tm_r; @@ -488,7 +489,7 @@ const char *cRecording::FileName(void) return fileName; } -const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level) +const char *cRecording::Title(char Delimiter, bool NewIndicator, int Level) const { char New = NewIndicator && IsNew() ? '*' : ' '; free(titleBuffer); @@ -548,7 +549,7 @@ const char *cRecording::PrefixFileName(char Prefix) return NULL; } -int cRecording::HierarchyLevels(void) +int cRecording::HierarchyLevels(void) const { const char *s = name; int level = 0; @@ -559,7 +560,7 @@ int cRecording::HierarchyLevels(void) return level; } -bool cRecording::IsEdited(void) +bool cRecording::IsEdited(void) const { const char *s = strrchr(name, '~'); s = !s ? name : s + 1; @@ -637,7 +638,7 @@ bool cRecordings::Load(bool Deleted) result = Count() > 0; } else - Interface->Error("Error while opening pipe!"); + Skins.Message(mtError, "Error while opening pipe!"); free(cmd); return result; } diff --git a/recording.h b/recording.h index 59a9dca0..01a25ab4 100644 --- a/recording.h +++ b/recording.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.h 1.28 2003/10/17 14:27:36 kls Exp $ + * $Id: recording.h 1.29 2004/05/07 14:24:22 kls Exp $ */ #ifndef __RECORDING_H @@ -34,15 +34,15 @@ public: class cRecording : public cListObject { private: - int resume; - char *titleBuffer; + mutable int resume; + mutable char *titleBuffer; char *sortBuffer; - char *fileName; - char *name; + mutable char *fileName; + mutable char *name; char *summary; char *StripEpisodeName(char *s); char *SortName(void); - int GetResume(void); + int GetResume(void) const; public: time_t start; int priority; @@ -51,14 +51,14 @@ public: cRecording(const char *FileName); ~cRecording(); virtual bool operator< (const cListObject &ListObject); - const char *Name(void) { return name; } - const char *FileName(void); - const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1); - const char *Summary(void) { return summary; } + const char *Name(void) const { return name; } + const char *FileName(void) const; + const char *Title(char Delimiter = ' ', bool NewIndicator = false, int Level = -1) const; + const char *Summary(void) const { return summary; } const char *PrefixFileName(char Prefix); - int HierarchyLevels(void); - bool IsNew(void) { return GetResume() <= 0; } - bool IsEdited(void); + int HierarchyLevels(void) const; + bool IsNew(void) const { return GetResume() <= 0; } + bool IsEdited(void) const; bool WriteSummary(void); bool Delete(void); // Changes the file name so that it will no longer be visible in the "Recordings" menu diff --git a/skinclassic.c b/skinclassic.c new file mode 100644 index 00000000..2c4b5632 --- /dev/null +++ b/skinclassic.c @@ -0,0 +1,541 @@ +/* + * skinclassic.c: The 'classic' VDR skin + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: skinclassic.c 1.1 2004/05/15 14:51:18 kls Exp $ + */ + +#include "skinclassic.h" +#include "font.h" +#include "i18n.h" +#include "osd.h" +#include "themes.h" + +#define ScrollWidth 5 + +static cTheme Theme; + +THEME_CLR(Theme, clrBackground, clrGray50); +THEME_CLR(Theme, clrButtonRedFg, clrWhite); +THEME_CLR(Theme, clrButtonRedBg, clrRed); +THEME_CLR(Theme, clrButtonGreenFg, clrBlack); +THEME_CLR(Theme, clrButtonGreenBg, clrGreen); +THEME_CLR(Theme, clrButtonYellowFg, clrBlack); +THEME_CLR(Theme, clrButtonYellowBg, clrYellow); +THEME_CLR(Theme, clrButtonBlueFg, clrWhite); +THEME_CLR(Theme, clrButtonBlueBg, clrBlue); +THEME_CLR(Theme, clrMessageStatusFg, clrBlack); +THEME_CLR(Theme, clrMessageStatusBg, clrCyan); +THEME_CLR(Theme, clrMessageInfoFg, clrBlack); +THEME_CLR(Theme, clrMessageInfoBg, clrGreen); +THEME_CLR(Theme, clrMessageWarningFg, clrBlack); +THEME_CLR(Theme, clrMessageWarningBg, clrYellow); +THEME_CLR(Theme, clrMessageErrorFg, clrWhite); +THEME_CLR(Theme, clrMessageErrorBg, clrRed); +THEME_CLR(Theme, clrVolumePrompt, clrGreen); +THEME_CLR(Theme, clrVolumeBarUpper, clrWhite); +THEME_CLR(Theme, clrVolumeBarLower, clrGreen); +THEME_CLR(Theme, clrChannelName, clrWhite); +THEME_CLR(Theme, clrChannelDate, clrWhite); +THEME_CLR(Theme, clrChannelEpgTimeFg, clrWhite); +THEME_CLR(Theme, clrChannelEpgTimeBg, clrRed); +THEME_CLR(Theme, clrChannelEpgTitle, clrCyan); +THEME_CLR(Theme, clrChannelEpgShortText, clrYellow); +THEME_CLR(Theme, clrMenuTitleFg, clrBlack); +THEME_CLR(Theme, clrMenuTitleBg, clrCyan); +THEME_CLR(Theme, clrMenuDate, clrBlack); +THEME_CLR(Theme, clrMenuItemCurrentFg, clrBlack); +THEME_CLR(Theme, clrMenuItemCurrentBg, clrCyan); +THEME_CLR(Theme, clrMenuItemSelectable, clrWhite); +THEME_CLR(Theme, clrMenuItemNonSelectable, clrCyan); +THEME_CLR(Theme, clrMenuEventTime, clrWhite); +THEME_CLR(Theme, clrMenuEventVpsFg, clrBlack); +THEME_CLR(Theme, clrMenuEventVpsBg, clrWhite); +THEME_CLR(Theme, clrMenuEventTitle, clrCyan); +THEME_CLR(Theme, clrMenuEventShortText, clrWhite); +THEME_CLR(Theme, clrMenuEventDescription, clrCyan); +THEME_CLR(Theme, clrMenuScrollbarTotal, clrWhite); +THEME_CLR(Theme, clrMenuScrollbarShown, clrCyan); +THEME_CLR(Theme, clrMenuText, clrWhite); +THEME_CLR(Theme, clrReplayTitle, clrWhite); +THEME_CLR(Theme, clrReplayCurrent, clrWhite); +THEME_CLR(Theme, clrReplayTotal, clrWhite); +THEME_CLR(Theme, clrReplayModeJump, clrWhite); +THEME_CLR(Theme, clrReplayProgressSeen, clrGreen); +THEME_CLR(Theme, clrReplayProgressRest, clrWhite); +THEME_CLR(Theme, clrReplayProgressSelected, clrRed); +THEME_CLR(Theme, clrReplayProgressMark, clrBlack); +THEME_CLR(Theme, clrReplayProgressCurrent, clrRed); + +// --- cSkinClassicDisplayChannel -------------------------------------------- + +class cSkinClassicDisplayChannel : public cSkinDisplayChannel{ +private: + cOsd *osd; + int lineHeight; + int timeWidth; +public: + cSkinClassicDisplayChannel(bool WithInfo); + virtual ~cSkinClassicDisplayChannel(); + virtual void SetChannel(const cChannel *Channel, int Number); + virtual void SetEvents(const cEvent *Present, const cEvent *Following); + virtual void Flush(void); + }; + +cSkinClassicDisplayChannel::cSkinClassicDisplayChannel(bool WithInfo) +{ + int Lines = WithInfo ? 5 : 1; + const cFont *font = cFont::GetFont(fontOsd); + lineHeight = font->Height(); + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + (Setup.ChannelInfoPos ? 0 : Setup.OSDHeight - Lines * lineHeight)); + timeWidth = font->Width("00:00") + 4; + tArea Areas[] = { { 0, 0, Setup.OSDWidth - 1, Lines * lineHeight, 4 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + osd->DrawRectangle(0, 0, osd->Width() - 1, osd->Height() - 1, Theme.Color(clrBackground)); +} + +cSkinClassicDisplayChannel::~cSkinClassicDisplayChannel() +{ + delete osd; +} + +void cSkinClassicDisplayChannel::SetChannel(const cChannel *Channel, int Number) +{ + osd->DrawRectangle(0, 0, osd->Width() - 1, lineHeight - 1, Theme.Color(clrBackground)); + osd->DrawText(2, 0, ChannelString(Channel, Number), Theme.Color(clrChannelName), Theme.Color(clrBackground), cFont::GetFont(fontOsd)); +} + +void cSkinClassicDisplayChannel::SetEvents(const cEvent *Present, const cEvent *Following) +{ + osd->DrawRectangle(0, lineHeight, timeWidth - 1, osd->Height(), Theme.Color(clrChannelEpgTimeBg)); + osd->DrawRectangle(timeWidth, lineHeight, osd->Width() - 1, osd->Height(), Theme.Color(clrBackground)); + for (int i = 0; i < 2; i++) { + const cEvent *e = !i ? Present : Following; + if (e) { + osd->DrawText( 2, (2 * i + 1) * lineHeight, e->GetTimeString(), Theme.Color(clrChannelEpgTimeFg), Theme.Color(clrChannelEpgTimeBg), cFont::GetFont(fontOsd)); + osd->DrawText(timeWidth + 10, (2 * i + 1) * lineHeight, e->Title(), Theme.Color(clrChannelEpgTitle), Theme.Color(clrBackground), cFont::GetFont(fontOsd)); + osd->DrawText(timeWidth + 10, (2 * i + 2) * lineHeight, e->ShortText(), Theme.Color(clrChannelEpgShortText), Theme.Color(clrBackground), cFont::GetFont(fontSml)); + } + } +} + +void cSkinClassicDisplayChannel::Flush(void) +{ + const char *date = DayDateTime(); + osd->DrawText(osd->Width() - cFont::GetFont(fontSml)->Width(date) - 2, 0, date, Theme.Color(clrChannelDate), Theme.Color(clrBackground), cFont::GetFont(fontSml)); + osd->Flush(); +} + +// --- cSkinClassicDisplayMenu ----------------------------------------------- + +class cSkinClassicDisplayMenu : public cSkinDisplayMenu { +private: + cOsd *osd; + int x0, x1; + int y0, y1, y2, y3, y4, y5; + int lineHeight; + void SetScrollbar(void); +public: + cSkinClassicDisplayMenu(void); + virtual ~cSkinClassicDisplayMenu(); + virtual void Scroll(bool Up, bool Page); + virtual int MaxItems(void); + virtual void Clear(void); + virtual void SetTitle(const char *Title); + virtual void SetButtons(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void SetItem(const char *Text, int Index, bool Current, bool Selectable); + virtual void SetEvent(const cEvent *Event); + virtual void SetRecording(const cRecording *Recording); + virtual void SetText(const char *Text, bool FixedFont); + virtual void Flush(void); + }; + +cSkinClassicDisplayMenu::cSkinClassicDisplayMenu(void) +{ + const cFont *font = cFont::GetFont(fontOsd); + lineHeight = font->Height(); + x0 = 0; + x1 = Setup.OSDWidth; + y0 = 0; + y1 = lineHeight; + y2 = y1 + lineHeight; + y5 = Setup.OSDHeight; + y4 = y5 - lineHeight; + y3 = y4 - lineHeight; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop); + tArea Areas[] = { { x0, y0, x1 - 1, y5 - 1, 4 } }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else { + tArea Areas[] = { { x0, y0, x1 - 1, y1 - 1, 2 }, + { x0, y1, x1 - 1, y3 - 1, 2 }, + { x0, y3, x1 - 1, y5 - 1, 4 } + }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } + osd->DrawRectangle(x0, y0, x1 - 1, y5 - 1, Theme.Color(clrBackground)); +} + +cSkinClassicDisplayMenu::~cSkinClassicDisplayMenu() +{ + delete osd; +} + +void cSkinClassicDisplayMenu::SetScrollbar(void) +{ + if (textScroller.CanScroll()) { + int yt = textScroller.Top(); + int yb = yt + textScroller.Height(); + int st = yt; + int sb = yb; + int tt = st + (sb - st) * textScroller.Offset() / textScroller.Total(); + int tb = tt + (sb - st) * textScroller.Shown() / textScroller.Total(); + int xl = x1 - ScrollWidth; + osd->DrawRectangle(xl, st, x1 - 1, sb, Theme.Color(clrMenuScrollbarTotal)); + osd->DrawRectangle(xl, tt, x1 - 1, tb, Theme.Color(clrMenuScrollbarShown)); + } +} + +void cSkinClassicDisplayMenu::Scroll(bool Up, bool Page) +{ + cSkinDisplayMenu::Scroll(Up, Page); + SetScrollbar(); +} + +int cSkinClassicDisplayMenu::MaxItems(void) +{ + return (y3 - y2) / lineHeight; +} + +void cSkinClassicDisplayMenu::Clear(void) +{ + textScroller.Reset(); + osd->DrawRectangle(x0, y1, x1 - 1, y4 - 1, Theme.Color(clrBackground)); +} + +void cSkinClassicDisplayMenu::SetTitle(const char *Title) +{ + const cFont *font = cFont::GetFont(fontOsd); + osd->DrawText(x0, y0, Title, Theme.Color(clrMenuTitleFg), Theme.Color(clrMenuTitleBg), font, x1 - x0); +} + +void cSkinClassicDisplayMenu::SetButtons(const char *Red, const char *Green, const char *Yellow, const char *Blue) +{ + const cFont *font = cFont::GetFont(fontOsd); + int w = x1 - x0; + int t0 = x0; + int t1 = x0 + w / 4; + int t2 = x0 + w / 2; + int t3 = x1 - w / 4; + int t4 = x1; + osd->DrawText(t0, y4, Red, Theme.Color(clrButtonRedFg), Theme.Color(clrButtonRedBg), font, t1 - t0, 0, taCenter); + osd->DrawText(t1, y4, Green, Theme.Color(clrButtonGreenFg), Theme.Color(clrButtonGreenBg), font, t2 - t1, 0, taCenter); + osd->DrawText(t2, y4, Yellow, Theme.Color(clrButtonYellowFg), Theme.Color(clrButtonYellowBg), font, t3 - t2, 0, taCenter); + osd->DrawText(t3, y4, Blue, Theme.Color(clrButtonBlueFg), Theme.Color(clrButtonBlueBg), font, t4 - t3, 0, taCenter); +} + +void cSkinClassicDisplayMenu::SetMessage(eMessageType Type, const char *Text) +{ + const cFont *font = cFont::GetFont(fontOsd); + if (Text) + osd->DrawText(x0, y3, Text, Theme.Color(clrMessageStatusFg + 2 * Type), Theme.Color(clrMessageStatusBg + 2 * Type), font, x1 - x0, 0, taCenter); + else + osd->DrawRectangle(x0, y3, x1 - 1, y4 - 1, Theme.Color(clrBackground)); +} + +void cSkinClassicDisplayMenu::SetItem(const char *Text, int Index, bool Current, bool Selectable) +{ + int y = y2 + Index * lineHeight; + tColor ColorFg, ColorBg; + if (Current) { + ColorFg = Theme.Color(clrMenuItemCurrentFg); + ColorBg = Theme.Color(clrMenuItemCurrentBg); + } + else { + ColorFg = Theme.Color(Selectable ? clrMenuItemSelectable : clrMenuItemNonSelectable); + ColorBg = Theme.Color(clrBackground); + } + const cFont *font = cFont::GetFont(fontOsd); + for (int i = 0; i < MaxTabs; i++) { + const char *s = GetTabbedText(Text, i); + if (s) { + int xt = x0 + Tab(i); + osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x1 - xt); + } + if (!Tab(i + 1)) + break; + } + SetEditableWidth(x1 - x0 - Tab(1)); +} + +void cSkinClassicDisplayMenu::SetEvent(const cEvent *Event) +{ + if (!Event) + return; + const cFont *font = cFont::GetFont(fontOsd); + int xl = x0 + 10; + int y = y2; + cTextScroller ts; + char t[32]; + snprintf(t, sizeof(t), "%s %s - %s", Event->GetDateString(), Event->GetTimeString(), Event->GetEndTimeString());//TODO dayname, no year + ts.Set(osd, xl, y, x1 - xl, y3 - y, t, font, Theme.Color(clrMenuEventTime), Theme.Color(clrBackground)); + if (Event->Vps() && Event->Vps() != Event->StartTime()) { + char *buffer; + asprintf(&buffer, " VPS: %s", Event->GetVpsString()); + const cFont *font = cFont::GetFont(fontSml); + osd->DrawText(x1 - font->Width(buffer), y, buffer, Theme.Color(clrMenuEventVpsFg), Theme.Color(clrMenuEventVpsBg), font); + free(buffer); + } + y += ts.Height(); + y += font->Height(); + ts.Set(osd, xl, y, x1 - xl, y3 - y, Event->Title(), font, Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground)); + y += ts.Height(); + if (!isempty(Event->ShortText())) { + const cFont *font = cFont::GetFont(fontSml); + ts.Set(osd, xl, y, x1 - xl, y3 - y, Event->ShortText(), font, Theme.Color(clrMenuEventShortText), Theme.Color(clrBackground)); + y += ts.Height(); + } + y += font->Height(); + if (!isempty(Event->Description())) { + textScroller.Set(osd, xl, y, x1 - xl - 2 * ScrollWidth, y3 - y, Event->Description(), font, Theme.Color(clrMenuEventDescription), Theme.Color(clrBackground)); + SetScrollbar(); + } +} + +void cSkinClassicDisplayMenu::SetRecording(const cRecording *Recording) +{ + SetText(Recording->Summary(), false); //TODO +} + +void cSkinClassicDisplayMenu::SetText(const char *Text, bool FixedFont) +{ + const cFont *font = cFont::GetFont(FixedFont ? fontFix : fontOsd); + textScroller.Set(osd, x0, y2, x1 - x0 - 2 * ScrollWidth, y3 - y2, Text, font, Theme.Color(clrMenuText), Theme.Color(clrBackground)); + SetScrollbar(); +} + +void cSkinClassicDisplayMenu::Flush(void) +{ + const char *date = DayDateTime(); + const cFont *font = cFont::GetFont(fontOsd); + osd->DrawText(x1 - font->Width(date) - 2, y0, date, Theme.Color(clrMenuDate), Theme.Color(clrMenuTitleBg), font); + osd->Flush(); +} + +// --- cSkinClassicDisplayReplay --------------------------------------------- + +class cSkinClassicDisplayReplay : public cSkinDisplayReplay { +private: + cOsd *osd; + int x0, x1; + int y0, y1, y2, y3; + int lastCurrentWidth; +public: + cSkinClassicDisplayReplay(bool ModeOnly); + virtual ~cSkinClassicDisplayReplay(); + virtual void SetTitle(const char *Title); + virtual void SetMode(bool Play, bool Forward, int Speed); + virtual void SetProgress(int Current, int Total); + virtual void SetCurrent(const char *Current); + virtual void SetTotal(const char *Total); + virtual void SetJump(const char *Jump); + virtual void Flush(void); + }; + +cSkinClassicDisplayReplay::cSkinClassicDisplayReplay(bool ModeOnly) +{ + const cFont *font = cFont::GetFont(fontOsd); + int lineHeight = font->Height(); + lastCurrentWidth = 0; + x0 = 0; + x1 = Setup.OSDWidth; + y0 = 0; + y1 = lineHeight; + y2 = 2 * lineHeight; + y3 = 3 * lineHeight; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - y3); + tArea Areas[] = { { x0, y0, x1 - 1, y3 - 1, 4 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + osd->DrawRectangle(x0, y0, x1 - 1, y3 - 1, ModeOnly ? clrTransparent : Theme.Color(clrBackground)); +} + +cSkinClassicDisplayReplay::~cSkinClassicDisplayReplay() +{ + delete osd; +} + +void cSkinClassicDisplayReplay::SetTitle(const char *Title) +{ + osd->DrawText(x0, y0, Title, Theme.Color(clrReplayTitle), Theme.Color(clrBackground), cFont::GetFont(fontOsd)); +} + +void cSkinClassicDisplayReplay::SetMode(bool Play, bool Forward, int Speed) +{ + if (Setup.ShowReplayMode) { + const char *Mode; + if (Speed == -1) Mode = Play ? " > " : " || "; + else if (Play) Mode = Forward ? " X>> " : " < " : " <|X "; + char buf[16]; + strn0cpy(buf, Mode, sizeof(buf)); + char *p = strchr(buf, 'X'); + if (p) + *p = Speed > 0 ? '1' + Speed - 1 : ' '; + SetJump(buf); + } +} + +void cSkinClassicDisplayReplay::SetProgress(int Current, int Total) +{ + cProgressBar pb(x1 - x0, y2 - y1, Current, Total, marks, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent)); + osd->DrawBitmap(x0, y1, pb); +} + +void cSkinClassicDisplayReplay::SetCurrent(const char *Current) +{ + const cFont *font = cFont::GetFont(fontOsd); + int w = font->Width(Current); + osd->DrawText(x0, y2, Current, Theme.Color(clrReplayCurrent), Theme.Color(clrBackground), font, lastCurrentWidth > w ? lastCurrentWidth : 0); + lastCurrentWidth = w; +} + +void cSkinClassicDisplayReplay::SetTotal(const char *Total) +{ + const cFont *font = cFont::GetFont(fontOsd); + osd->DrawText(x1 - font->Width(Total), y2, Total, Theme.Color(clrReplayTotal), Theme.Color(clrBackground), font); +} + +void cSkinClassicDisplayReplay::SetJump(const char *Jump) +{ + osd->DrawText(x0 + (x1 - x0) / 4, y2, Jump, Theme.Color(clrReplayModeJump), Theme.Color(clrBackground), cFont::GetFont(fontOsd), (x1 - x0) / 2, 0, taCenter); +} + +void cSkinClassicDisplayReplay::Flush(void) +{ + osd->Flush(); +} + +// --- cSkinClassicDisplayVolume --------------------------------------------- + +class cSkinClassicDisplayVolume : public cSkinDisplayVolume { +private: + cOsd *osd; +public: + cSkinClassicDisplayVolume(void); + virtual ~cSkinClassicDisplayVolume(); + virtual void SetVolume(int Current, int Total, bool Mute); + virtual void Flush(void); + }; + +cSkinClassicDisplayVolume::cSkinClassicDisplayVolume(void) +{ + const cFont *font = cFont::GetFont(fontOsd); + int lineHeight = font->Height(); + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - lineHeight); + tArea Areas[] = { { 0, 0, Setup.OSDWidth - 1, lineHeight - 1, 4 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); +} + +cSkinClassicDisplayVolume::~cSkinClassicDisplayVolume() +{ + delete osd; +} + +void cSkinClassicDisplayVolume::SetVolume(int Current, int Total, bool Mute) +{ + const cFont *font = cFont::GetFont(fontOsd); + if (Mute) { + osd->DrawRectangle(0, 0, osd->Width() - 1, osd->Height() - 1, clrTransparent); + osd->DrawText(0, 0, tr("Mute"), Theme.Color(clrVolumePrompt), Theme.Color(clrBackground), font); + } + else { + const char *Prompt = tr("Volume "); + int l = font->Width(Prompt); + int p = (osd->Width() - l) * Current / Total; + osd->DrawText(0, 0, Prompt, Theme.Color(clrVolumePrompt), Theme.Color(clrBackground), font); + osd->DrawRectangle(l, 0, l + p - 1, osd->Height() - 1, Theme.Color(clrVolumeBarLower)); + osd->DrawRectangle(l + p, 0, osd->Width() - 1, osd->Height() - 1, Theme.Color(clrVolumeBarUpper)); + } +} + +void cSkinClassicDisplayVolume::Flush(void) +{ + osd->Flush(); +} + +// --- cSkinClassicDisplayMessage -------------------------------------------- + +class cSkinClassicDisplayMessage : public cSkinDisplayMessage { +private: + cOsd *osd; +public: + cSkinClassicDisplayMessage(void); + virtual ~cSkinClassicDisplayMessage(); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void Flush(void); + }; + +cSkinClassicDisplayMessage::cSkinClassicDisplayMessage(void) +{ + const cFont *font = cFont::GetFont(fontOsd); + int lineHeight = font->Height(); + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - lineHeight); + tArea Areas[] = { { 0, 0, Setup.OSDWidth - 1, lineHeight - 1, 2 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); +} + +cSkinClassicDisplayMessage::~cSkinClassicDisplayMessage() +{ + delete osd; +} + +void cSkinClassicDisplayMessage::SetMessage(eMessageType Type, const char *Text) +{ + const cFont *font = cFont::GetFont(fontOsd); + osd->DrawText(0, 0, Text, Theme.Color(clrMessageStatusFg + 2 * Type), Theme.Color(clrMessageStatusBg + 2 * Type), font, Setup.OSDWidth, 0, taCenter); +} + +void cSkinClassicDisplayMessage::Flush(void) +{ + osd->Flush(); +} + +// --- cSkinClassic ---------------------------------------------------------- + +cSkinClassic::cSkinClassic(void) +:cSkin("classic", &::Theme)//XXX naming problem??? +{ +} + +const char *cSkinClassic::Description(void) +{ + return tr("Classic VDR"); +} + +cSkinDisplayChannel *cSkinClassic::DisplayChannel(bool WithInfo) +{ + return new cSkinClassicDisplayChannel(WithInfo); +} + +cSkinDisplayMenu *cSkinClassic::DisplayMenu(void) +{ + return new cSkinClassicDisplayMenu; +} + +cSkinDisplayReplay *cSkinClassic::DisplayReplay(bool ModeOnly) +{ + return new cSkinClassicDisplayReplay(ModeOnly); +} + +cSkinDisplayVolume *cSkinClassic::DisplayVolume(void) +{ + return new cSkinClassicDisplayVolume; +} + +cSkinDisplayMessage *cSkinClassic::DisplayMessage(void) +{ + return new cSkinClassicDisplayMessage; +} diff --git a/skinclassic.h b/skinclassic.h new file mode 100644 index 00000000..e990b588 --- /dev/null +++ b/skinclassic.h @@ -0,0 +1,26 @@ +/* + * skinclassic.h: The 'classic' VDR skin + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: skinclassic.h 1.1 2004/04/18 09:38:02 kls Exp $ + */ + +#ifndef __SKINCLASSIC_H +#define __SKINCLASSIC_H + +#include "skins.h" + +class cSkinClassic : public cSkin { +public: + cSkinClassic(void); + virtual const char *Description(void); + virtual cSkinDisplayChannel *DisplayChannel(bool WithInfo); + virtual cSkinDisplayMenu *DisplayMenu(void); + virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly); + virtual cSkinDisplayVolume *DisplayVolume(void); + virtual cSkinDisplayMessage *DisplayMessage(void); + }; + +#endif //__SKINCLASSIC_H diff --git a/skins.c b/skins.c new file mode 100644 index 00000000..6872a947 --- /dev/null +++ b/skins.c @@ -0,0 +1,201 @@ +/* + * skins.c: The optical appearance of the OSD + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: skins.c 1.1 2004/05/15 12:34:38 kls Exp $ + */ + +#include "skins.h" +#include "interface.h" +#include "status.h" +#include "tools.h" + +// --- cSkinDisplay ---------------------------------------------------------- + +cSkinDisplay *cSkinDisplay::current = NULL; + +cSkinDisplay::cSkinDisplay(void) +{ + current = this; + editableWidth = 100; //XXX +} + +cSkinDisplay::~cSkinDisplay() +{ + current = NULL; +} + +// --- cSkinDisplayMenu ------------------------------------------------------ + +cSkinDisplayMenu::cSkinDisplayMenu(void) +{ + SetTabs(0); +} + +void cSkinDisplayMenu::SetTabs(int Tab1, int Tab2, int Tab3, int Tab4, int Tab5) +{ + tabs[0] = 0; + tabs[1] = Tab1 ? tabs[0] + Tab1 : 0; + tabs[2] = Tab2 ? tabs[1] + Tab2 : 0; + tabs[3] = Tab3 ? tabs[2] + Tab3 : 0; + tabs[4] = Tab4 ? tabs[3] + Tab4 : 0; + tabs[5] = Tab5 ? tabs[4] + Tab5 : 0; + for (int i = 1; i < MaxTabs; i++) + tabs[i] *= 12;//XXX average character width of font used for items!!! +} + +void cSkinDisplayMenu::Scroll(bool Up, bool Page) +{ + textScroller.Scroll(Up, Page); +} + +const char *cSkinDisplayMenu::GetTabbedText(const char *s, int Tab) +{ + if (!s) + return NULL; + static char buffer[1000]; + const char *a = s; + const char *b = strchrnul(a, '\t'); + while (*b && Tab-- > 0) { + a = b + 1; + b = strchrnul(a, '\t'); + } + if (!*b) + return (Tab <= 0) ? a : NULL; + unsigned int n = b - a; + if (n >= sizeof(buffer)) + n = sizeof(buffer) - 1; + strncpy(buffer, a, n); + buffer[n] = 0; + return buffer; +} + +// --- cSkinDisplayReplay::cProgressBar -------------------------------------- + +cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent) +:cBitmap(Width, Height, 2) +{ + total = Total; + if (total > 0) { + int p = Pos(Current); + DrawRectangle(0, 0, p, Height - 1, ColorSeen); + DrawRectangle(p + 1, 0, Width - 1, Height - 1, ColorRest); + if (Marks) { + bool Start = true; + for (const cMark *m = Marks->First(); m; m = Marks->Next(m)) { + int p1 = Pos(m->position); + if (Start) { + const cMark *m2 = Marks->Next(m); + int p2 = Pos(m2 ? m2->position : total); + int h = Height / 3; + DrawRectangle(p1, h, p2, Height - h, ColorSelected); + } + Mark(p1, Start, m->position == Current, ColorMark, ColorCurrent); + Start = !Start; + } + } + } +} + +void cSkinDisplayReplay::cProgressBar::Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent) +{ + DrawRectangle(x, 0, x, Height() - 1, ColorMark); + const int d = Height() / (Current ? 3 : 9); + for (int i = 0; i < d; i++) { + int h = Start ? i : Height() - 1 - i; + DrawRectangle(x - d + i, h, x + d - i, h, Current ? ColorCurrent : ColorMark); + } +} + +// --- cSkinDisplayReplay ---------------------------------------------------- + +cSkinDisplayReplay::cSkinDisplayReplay(void) +{ + marks = NULL; +} + +void cSkinDisplayReplay::SetMarks(const cMarks *Marks) +{ + marks = Marks; +} + +// --- cSkin ----------------------------------------------------------------- + +cSkin::cSkin(const char *Name, cTheme *Theme) +{ + name = strdup(Name); + theme = Theme; + if (theme) + cThemes::Save(name, theme); + Skins.Add(this); +} + +cSkin::~cSkin() +{ + free(name); +} + +// --- cSkins ---------------------------------------------------------------- + +cSkins Skins; + +cSkins::cSkins(void) +{ + displayMessage = NULL; +} + +cSkins::~cSkins() +{ + delete displayMessage; +} + +bool cSkins::SetCurrent(const char *Name) +{ + if (Name) { + for (cSkin *Skin = First(); Skin; Skin = Next(Skin)) { + if (strcmp(Skin->Name(), Name) == 0) { + current = Skin; + return true; + } + } + } + current = First(); + return current != NULL; +} + +eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds) +{ + switch (Type) { + case mtInfo: isyslog("info: %s", s); break; + case mtError: esyslog("ERROR: %s", s); break; + default: ; + } + if (!cSkinDisplay::Current() && !displayMessage) + displayMessage = Current()->DisplayMessage(); + cSkinDisplay::Current()->SetMessage(Type, s); + cSkinDisplay::Current()->Flush(); + cStatus::MsgOsdStatusMessage(s); + eKeys k = kNone; + if (Type != mtStatus) { + k = Interface->Wait(Seconds); + if (displayMessage) { + delete displayMessage; + displayMessage = NULL; + } + else + cSkinDisplay::Current()->SetMessage(Type, NULL); + } + else if (!s && displayMessage) { + delete displayMessage; + displayMessage = NULL; + } + return k; +} + +void cSkins::Flush(void) +{ + if (cSkinDisplay::Current()) + cSkinDisplay::Current()->Flush(); +} diff --git a/skins.h b/skins.h new file mode 100644 index 00000000..412810f2 --- /dev/null +++ b/skins.h @@ -0,0 +1,297 @@ +/* + * skins.h: The optical appearance of the OSD + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: skins.h 1.1 2004/05/16 09:40:18 kls Exp $ + */ + +#ifndef __SKINS_H +#define __SKINS_H + +#include "channels.h" +#include "epg.h" +#include "keys.h" +#include "osd.h" +#include "recording.h" +#include "themes.h" +#include "tools.h" + +enum eMessageType { mtStatus = 0, mtInfo, mtWarning, mtError }; // will be used to calculate color offsets! + +class cSkinDisplay { +private: + static cSkinDisplay *current; + int editableWidth; //XXX this is not nice, but how else could we know this value? +public: + cSkinDisplay(void); + virtual ~cSkinDisplay(); + int EditableWidth(void) { return editableWidth; } + void SetEditableWidth(int Width) { editableWidth = Width; } + ///< If an item is set through a call to cSkinDisplayMenu::SetItem(), this + ///< function shall be called to set the width of the rightmost tab separated + ///< field. This information will be used for editable items. + virtual void SetButtons(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL) {} + ///< Sets the color buttons to the given strings, provided this cSkinDisplay + ///< actually has a color button display. + virtual void SetMessage(eMessageType Type, const char *Text) {} + ///< Sets a one line message Text, with the given Type. Type can be used + ///< to determine, e.g., the colors for displaying the Text. + virtual void Flush(void) {} + ///< Actually draws the OSD display to the output device. + static cSkinDisplay *Current(void) { return current; } + ///< Returns the currently active cSkinDisplay. + }; + +class cSkinDisplayChannel : public cSkinDisplay { + ///< This class is used to display the current channel, together with + ///< the present and following EPG even. How and to what extent this + ///< is done is totally up to the derived class. +public: + virtual void SetChannel(const cChannel *Channel, int Number) = 0; + ///< Sets the current channel to Channel. If Number is not 0, the + ///< user is in the process of entering a channel number, which must + ///< be displayed accordingly. + virtual void SetEvents(const cEvent *Present, const cEvent *Following) = 0; + ///< Sets the Present and Following EPG events. It either of these + ///< is not available, NULL will be given. + /*TODO + SetButtons + Red = Video options + Green = Info now + Yellow = Info next + Blue = Audio options + AudioOptions + VideoOptions + */ + }; + +class cSkinDisplayMenu : public cSkinDisplay { + ///< This class implements the general purpose menu display, which is + ///< used throughout the program to display information and let the + ///< user interact with items. + ///< A menu consists of the following fields, each of which is explicitly + ///< set by calls to the member functions below: + ///< - Title: a single line of text, indicating what this menu displays + ///< - Color buttons: the red, green, yellow and blue buttons, used for + ///< various functions + ///< - Message: a one line message, indicating a Status, Info, Warning, + ///< or Error condition + ///< - Central area: the main central area of the menu, used to display + ///< one of the following: + ///< - Items: a list of single line items, of which the user may be + ///< able to select one + ///< - Event: the full information about one EPG event + ///< - Text: a multi line, scrollable text +public: + enum { MaxTabs = 6 }; +private: + int tabs[MaxTabs]; +protected: + cTextScroller textScroller; + int Tab(int n) { return (n >= 0 && n < MaxTabs) ? tabs[n] : 0; } + ///< Returns the offset of the given tab from the left border of the + ///< item display area. The value returned is in pixel.//XXX ncurses??? + const char *GetTabbedText(const char *s, int Tab); + ///< Returns the that part of the given string, that follows the given + ///< Tab (where 0 indicates the beginning of the string). If no such + ///< part can be found, NULL will be returned. +public: + cSkinDisplayMenu(void); + virtual void SetTabs(int Tab1, int Tab2 = 0, int Tab3 = 0, int Tab4 = 0, int Tab5 = 0); + ///< Sets the tab columns to the given values, which are the number of + ///< characters in each column.//XXX ncurses??? + virtual void Scroll(bool Up, bool Page); + ///< If this menu contains a text area that can be scrolled, this function + ///< will be called to actually scroll the text. Up indicates whether the + ///< text shall be scrolled up or down, and Page is true if it shall be + ///< scrolled by a full page, rather than a single line. An object of the + ///< cTextScroller class can be used to implement the scrolling text area. + virtual int MaxItems(void) = 0; + ///< Returns the maximum number of items the menu can display. + virtual void Clear(void) = 0; + ///< Clears the entire central area of the menu. + virtual void SetTitle(const char *Title) = 0; + ///< Sets the title of this menu to Title. + virtual void SetButtons(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL) = 0; + ///< Sets the color buttons to the given strings. If any of the values is + ///< NULL, any previous text must be removed from the related button. + virtual void SetMessage(eMessageType Type, const char *Text) = 0; + ///< Sets a one line message Text, with the given Type. Type can be used + ///< to determine, e.g., the colors for displaying the Text. + ///< If Text is NULL, any previously displayed message must be removed, and + ///< any previous contents overwritten by the message must be restored. + virtual void SetItem(const char *Text, int Index, bool Current, bool Selectable) = 0; + ///< Sets the item at the given Index to Text. Index is between 0 and the + ///< value returned by MaxItems(), minus one. Text may contain tab characters ('\t'), + ///< which shall be used to separate the text into several columns, according to the + ///< values set by a prior call to SetTabs(). If Current is true, this item shall + ///< be drawn in a way indicating to the user that it is the currently selected + ///< one. Selectable can be used to display items differently that can't be + ///< selected by the user. + ///< Whenever the current status is moved from one item to another, + ///< this function will be first called for the old current item + ///< with Current set to false, and then for the new current item + ///< with Current set to true. + /*TODO + virtual void SetItem(const cEvent *Event, int Index, bool Current, bool Selectable, bool NowNext???, bool Schedule???); + virtual void SetItem(const cTimer *Timer, int Index, bool Current, bool Selectable); + virtual void SetItem(const cChannel *Channel, int Index, bool Current, bool Selectable); + virtual void SetItem(const cRecording *Recording, int Index, bool Current, bool Selectable); + --> false: call SetItem(text) + */ + virtual void SetEvent(const cEvent *Event) = 0; + ///< Sets the Event that shall be displayed, using the entire central area + ///< of the menu. The Event's 'description' shall be displayed using a + ///< cTextScroller, and the Scroll() function will be called to drive scrolling + ///< that text if necessary. + virtual void SetRecording(const cRecording *Recording) = 0; + ///< Sets the Recording that shall be displayed, using the entire central area + ///< of the menu. The Recording's 'summary' shall be displayed using a + ///< cTextScroller, and the Scroll() function will be called to drive scrolling + ///< that text if necessary. + virtual void SetText(const char *Text, bool FixedFont) = 0; + ///< Sets the Text that shall be displayed, using the entire central area + ///< of the menu. The Text shall be displayed using a cTextScroller, and + ///< the Scroll() function will be called to drive scrolling that text if + ///< necessary. + //XXX ??? virtual void SetHelp(const char *Help) = 0; + }; + +class cSkinDisplayReplay : public cSkinDisplay { + ///< This class implements the progress display used during replay of + ///< a recording. +protected: + const cMarks *marks; + class cProgressBar : public cBitmap { + protected: + int total; + int Pos(int p) { return p * Width() / total; } + void Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent); + public: + cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent); + }; +public: + cSkinDisplayReplay(void); + virtual void SetMarks(const cMarks *Marks); + ///< Sets the editing marks to Marks, which shall be used to display the + ///< progress bar through a cProgressBar object. + virtual void SetTitle(const char *Title) = 0; + ///< Sets the title of the recording. + virtual void SetMode(bool Play, bool Forward, int Speed) = 0; + ///< Sets the current replay mode, which can be used to display some + ///< indicator, showing the user whether we are currently in normal + ///< play mode, fast forward etc. + virtual void SetProgress(int Current, int Total) = 0; + ///< This function will be called whenever the position in of the total + ///< length of the recording has changed. A cProgressBar shall then be + ///< used to display a progress indicator. + virtual void SetCurrent(const char *Current) = 0; + ///< Sets the current position within the recording, as a user readable + ///< string if the form "h:mm:ss.ff". The ".ff" part, indicating the + ///< frame number, is optional and the actual implementation needs to + ///< take care that it is erased from the display when a Current string + ///< _with_ ".ff" is followed by one without it. + virtual void SetTotal(const char *Total) = 0; + ///< Sets the total length of the recording, as a user readable + ///< string if the form "h:mm:ss". + virtual void SetJump(const char *Jump) = 0; + ///< Sets the prompt that allows the user to enter a jump point. + ///< Jump is a string of the form "Jump: mm:ss". The actual implementation + ///< needs to be able to handle variations in the length of this + ///< string, which will occur when the user enters an actual value. + ///< If Jump is NULL, the jump prompt shall be removed from the display. + }; + +class cSkinDisplayVolume : public cSkinDisplay { + ///< This class implements the volume/mute display. +public: + virtual void SetVolume(int Current, int Total, bool Mute) = 0; + ///< Sets the volume to the given Current value, which is in the range + ///< 0...Total. If Mute is true, audio is currently muted and a "mute" + ///< indicator shall be displayed. + }; + +class cSkinDisplayMessage : public cSkinDisplay { + ///< This class implements a simple message display. +public: + virtual void SetMessage(eMessageType Type, const char *Text) = 0; + ///< Sets the message to Text. Type can be used to decide how to display + ///< the message, for instance in which colors. + }; + +class cSkin : public cListObject { +private: + char *name; + cTheme *theme; +public: + cSkin(const char *Name, cTheme *Theme = NULL); + ///< Creates a new skin class, with the given Name and Theme. + ///< Name will be used to identify this skin in the 'setup.conf' + ///< file, and is normally not seen by the user. It should + ///< consist of only lowercase letters and digits. + ///< Theme must be a static object that survives the entire lifetime + ///< of this skin. + ///< The constructor of a derived class shall not set up any data + ///< structures yet, because whether or not this skin will actually + ///< be used is not yet known at this point. All actual work shall + ///< be done in the pure functions below. + ///< A cSkin object must be created on the heap and shall not be + ///< explicitly deleted. + virtual ~cSkin(); + const char *Name(void) { return name; } + cTheme *Theme(void) { return theme; } + virtual const char *Description(void) = 0; + ///< Returns a user visible, single line description of this skin, + ///< which may consist of arbitrary text and can, if the skin + ///< implementation wishes to do so, be internationalized. + ///< The actual text shouldn't be too long, so that it can be + ///< fully displayed in the Setup/OSD menu. + virtual cSkinDisplayChannel *DisplayChannel(bool WithInfo) = 0; + ///< Creates and returns a new object for displaying the current + ///< channel. WithInfo indicates whether it shall display only + ///< the basic channel data, or also information about the present + ///< and following EPG event. + ///< The caller must delete the object after use. + virtual cSkinDisplayMenu *DisplayMenu(void) = 0; + ///< Creates and returns a new object for displaying a menu. + ///< The caller must delete the object after use. + virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly) = 0; + ///< Creates and returns a new object for displaying replay progress. + ///< ModeOnly indicates whether this should be a full featured replay + ///< display, or just a replay mode indicator. + ///< The caller must delete the object after use. + virtual cSkinDisplayVolume *DisplayVolume(void) = 0; + ///< Creates and returns a new object for displaying the current volume. + ///< The caller must delete the object after use. + virtual cSkinDisplayMessage *DisplayMessage(void) = 0; + ///< Creates and returns a new object for displaying a message. + ///< The caller must delete the object after use. + }; + +class cSkins : public cList { +private: + cSkin *current; + cSkinDisplayMessage *displayMessage; +public: + cSkins(void); + ~cSkins(); + bool SetCurrent(const char *Name = NULL); + ///< Sets the current skin to the one indicated by name. + ///< If no such skin can be found, the first one will be used. + cSkin *Current(void) { return current; } + ///< Returns a pointer to the current skin. + bool IsOpen(void) { return cSkinDisplay::Current(); } + ///< Returns true if there is currently a skin display object active. + eKeys Message(eMessageType Type, const char *s, int Seconds = 0); + ///< Displays the given message, either through a currently visible + ///< display object that is capable of doing so, or by creating a + ///< temporary cSkinDisplayMessage object. + void Flush(void); + ///< Flushes the currently active cSkinDisplay, if any. + }; + +extern cSkins Skins; + +#endif //__SKINS_H diff --git a/skinsttng.c b/skinsttng.c new file mode 100644 index 00000000..cb44a3e0 --- /dev/null +++ b/skinsttng.c @@ -0,0 +1,879 @@ +/* + * skinsttng.c: A VDR skin with ST:TNG Panels + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: skinsttng.c 1.1 2004/05/16 09:27:35 kls Exp $ + */ + +// Star Trek: The Next Generation® is a registered trademark of Paramount Pictures +// registered in the United States Patent and Trademark Office. +// No infringement intended. + +#include "skinsttng.h" +#include "font.h" +#include "osd.h" +#include "menu.h" +#include "themes.h" + +#include "symbols/arrowdown.xpm" +#include "symbols/arrowup.xpm" +#include "symbols/audio.xpm" +#include "symbols/dolbydigital.xpm" +#include "symbols/encrypted.xpm" +#include "symbols/ffwd.xpm" +#include "symbols/ffwd1.xpm" +#include "symbols/ffwd2.xpm" +#include "symbols/ffwd3.xpm" +#include "symbols/frew.xpm" +#include "symbols/frew1.xpm" +#include "symbols/frew2.xpm" +#include "symbols/frew3.xpm" +#include "symbols/mute.xpm" +#include "symbols/pause.xpm" +#include "symbols/play.xpm" +#include "symbols/radio.xpm" +#include "symbols/recording.xpm" +#include "symbols/sfwd.xpm" +#include "symbols/sfwd1.xpm" +#include "symbols/sfwd2.xpm" +#include "symbols/sfwd3.xpm" +#include "symbols/srew.xpm" +#include "symbols/srew1.xpm" +#include "symbols/srew2.xpm" +#include "symbols/srew3.xpm" +#include "symbols/teletext.xpm" +#include "symbols/volume.xpm" + +#define Roundness 10 +#define Gap 5 +#define ScrollWidth 5 + +static cTheme Theme; + +THEME_CLR(Theme, clrBackground, clrGray50); +THEME_CLR(Theme, clrButtonRedFg, clrWhite); +THEME_CLR(Theme, clrButtonRedBg, clrRed); +THEME_CLR(Theme, clrButtonGreenFg, clrBlack); +THEME_CLR(Theme, clrButtonGreenBg, clrGreen); +THEME_CLR(Theme, clrButtonYellowFg, clrBlack); +THEME_CLR(Theme, clrButtonYellowBg, clrYellow); +THEME_CLR(Theme, clrButtonBlueFg, clrWhite); +THEME_CLR(Theme, clrButtonBlueBg, clrBlue); +THEME_CLR(Theme, clrMessageFrame, clrYellow); +THEME_CLR(Theme, clrMessageStatusFg, clrBlack); +THEME_CLR(Theme, clrMessageStatusBg, clrCyan); +THEME_CLR(Theme, clrMessageInfoFg, clrBlack); +THEME_CLR(Theme, clrMessageInfoBg, clrGreen); +THEME_CLR(Theme, clrMessageWarningFg, clrBlack); +THEME_CLR(Theme, clrMessageWarningBg, clrYellow); +THEME_CLR(Theme, clrMessageErrorFg, clrWhite); +THEME_CLR(Theme, clrMessageErrorBg, clrRed); +THEME_CLR(Theme, clrVolumeFrame, clrYellow); +THEME_CLR(Theme, clrVolumeSymbol, clrBlack); +THEME_CLR(Theme, clrVolumeBarUpper, 0xFFBC8024); +THEME_CLR(Theme, clrVolumeBarLower, 0xFF248024); +THEME_CLR(Theme, clrChannelFrame, clrYellow); +THEME_CLR(Theme, clrChannelName, clrBlack); +THEME_CLR(Theme, clrChannelDate, clrBlack); +THEME_CLR(Theme, clrChannelSymbolOn, clrBlack); +THEME_CLR(Theme, clrChannelSymbolOff, 0xFFBC8024); +THEME_CLR(Theme, clrChannelSymbolRecFg, clrWhite); +THEME_CLR(Theme, clrChannelSymbolRecBg, clrRed); +THEME_CLR(Theme, clrChannelEpgTime, clrBlack); +THEME_CLR(Theme, clrChannelEpgTitle, clrCyan); +THEME_CLR(Theme, clrChannelEpgShortText, clrYellow); +THEME_CLR(Theme, clrChannelTimebarSeen, clrYellow); +THEME_CLR(Theme, clrChannelTimebarRest, clrGray50); +THEME_CLR(Theme, clrMenuFrame, clrYellow); +THEME_CLR(Theme, clrMenuTitle, clrBlack); +THEME_CLR(Theme, clrMenuDate, clrBlack); +THEME_CLR(Theme, clrMenuItemCurrentFg, clrBlack); +THEME_CLR(Theme, clrMenuItemCurrentBg, clrYellow); +THEME_CLR(Theme, clrMenuItemSelectable, clrYellow); +THEME_CLR(Theme, clrMenuItemNonSelectable, clrCyan); +THEME_CLR(Theme, clrMenuEventTime, clrYellow); +THEME_CLR(Theme, clrMenuEventVps, clrBlack); +THEME_CLR(Theme, clrMenuEventTitle, clrCyan); +THEME_CLR(Theme, clrMenuEventShortText, clrYellow); +THEME_CLR(Theme, clrMenuEventDescription, clrCyan); +THEME_CLR(Theme, clrMenuScrollbarTotal, clrYellow); +THEME_CLR(Theme, clrMenuScrollbarShown, clrCyan); +THEME_CLR(Theme, clrMenuScrollbarArrow, clrBlack); +THEME_CLR(Theme, clrMenuText, clrCyan); +THEME_CLR(Theme, clrReplayFrame, clrYellow); +THEME_CLR(Theme, clrReplayTitle, clrBlack); +THEME_CLR(Theme, clrReplayMode, clrBlack); +THEME_CLR(Theme, clrReplayCurrent, clrBlack); +THEME_CLR(Theme, clrReplayTotal, clrBlack); +THEME_CLR(Theme, clrReplayJump, clrBlack); +THEME_CLR(Theme, clrReplayProgressSeen, clrGreen); +THEME_CLR(Theme, clrReplayProgressRest, clrWhite); +THEME_CLR(Theme, clrReplayProgressSelected, clrRed); +THEME_CLR(Theme, clrReplayProgressMark, clrBlack); +THEME_CLR(Theme, clrReplayProgressCurrent, clrRed); + +// --- cSkinSTTNGDisplayChannel ---------------------------------------------- + +class cSkinSTTNGDisplayChannel : public cSkinDisplayChannel{ +private: + cOsd *osd; + int x0, x1, x2, x3, x4, x5, x6, x7; + int y0, y1, y2, y3, y4, y5, y6, y7; + bool withInfo; + int lineHeight; + tColor frameColor; + const cEvent *present; + int lastSeen; + static cBitmap bmTeletext, bmRadio, bmAudio, bmDolbyDigital, bmEncrypted, bmRecording; +public: + cSkinSTTNGDisplayChannel(bool WithInfo); + virtual ~cSkinSTTNGDisplayChannel(); + virtual void SetChannel(const cChannel *Channel, int Number); + virtual void SetEvents(const cEvent *Present, const cEvent *Following); + virtual void Flush(void); + }; + +cBitmap cSkinSTTNGDisplayChannel::bmTeletext(teletext_xpm); +cBitmap cSkinSTTNGDisplayChannel::bmRadio(radio_xpm); +cBitmap cSkinSTTNGDisplayChannel::bmAudio(audio_xpm); +cBitmap cSkinSTTNGDisplayChannel::bmDolbyDigital(dolbydigital_xpm); +cBitmap cSkinSTTNGDisplayChannel::bmEncrypted(encrypted_xpm); +cBitmap cSkinSTTNGDisplayChannel::bmRecording(recording_xpm); + +cSkinSTTNGDisplayChannel::cSkinSTTNGDisplayChannel(bool WithInfo) +{ + present = NULL; + lastSeen = -1; + const cFont *font = cFont::GetFont(fontOsd); + withInfo = WithInfo; + lineHeight = font->Height(); + frameColor = Theme.Color(clrChannelFrame); + if (withInfo) { + x0 = 0; + x1 = x0 + font->Width("00:00") + 4; + x2 = x1 + Roundness; + x3 = x2 + Gap; + x7 = Setup.OSDWidth; + x6 = x7 - lineHeight / 2; + x5 = x6 - lineHeight / 2; + x4 = x5 - Gap; + y0 = 0; + y1 = lineHeight; + y2 = y1 + Roundness; + y3 = y2 + Gap; + y4 = y3 + 4 * lineHeight; + y5 = y4 + Gap; + y6 = y5 + Roundness; + y7 = y6 + cFont::GetFont(fontSml)->Height(); + int yt = (y0 + y1) / 2; + int yb = (y6 + y7) / 2; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + (Setup.ChannelInfoPos ? 0 : Setup.OSDHeight - y7)); + tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 4 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + osd->DrawRectangle(x0, y0, x7 - 1, y7 - 1, Theme.Color(clrBackground)); + osd->DrawRectangle(x0, y0, x1 - 1, y1 - 1, clrTransparent); + osd->DrawRectangle(x0, y6, x1 - 1, y7 - 1, clrTransparent); + osd->DrawRectangle(x6, y0, x7 - 1, yt - 1, clrTransparent); + osd->DrawRectangle(x6, yb, x7 - 1, y7 - 1, clrTransparent); + osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 2); + osd->DrawRectangle(x1, y0, x4 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x5, y0, x6 - 1, y1 - 1, frameColor); + osd->DrawEllipse (x6, y0, x7 - 1, y1 - 1, frameColor, 5); + osd->DrawRectangle(x0, y1, x1 - 1, y2 - 1, frameColor); + osd->DrawEllipse (x1, y1, x2 - 1, y2 - 1, frameColor, -2); + osd->DrawRectangle(x0, y3, x1 - 1, y4 - 1, frameColor); + osd->DrawRectangle(x0, y5, x1 - 1, y6 - 1, frameColor); + osd->DrawEllipse (x1, y5, x2 - 1, y6 - 1, frameColor, -3); + osd->DrawEllipse (x0, y6, x1 - 1, y7 - 1, frameColor, 3); + osd->DrawRectangle(x1, y6, x4 - 1, y7 - 1, frameColor); + osd->DrawRectangle(x5, y6, x6 - 1, y7 - 1, frameColor); + osd->DrawEllipse (x6, y6, x7 - 1, y7 - 1, frameColor, 5); + } + else { + x0 = 0; + x1 = lineHeight / 2; + x2 = lineHeight; + x3 = x2 + Gap; + x7 = Setup.OSDWidth; + x6 = x7 - lineHeight / 2; + x5 = x6 - lineHeight / 2; + x4 = x5 - Gap; + y0 = 0; + y1 = lineHeight; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + (Setup.ChannelInfoPos ? 0 : Setup.OSDHeight - y1)); + tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 4 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + osd->DrawRectangle(x0, y0, x7 - 1, y1 - 1, clrTransparent); + osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 7); + osd->DrawRectangle(x1, y0, x2 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x5, y0, x6 - 1, y1 - 1, frameColor); + osd->DrawEllipse (x6, y0, x7 - 1, y1 - 1, frameColor, 5); + } +} + +cSkinSTTNGDisplayChannel::~cSkinSTTNGDisplayChannel() +{ + delete osd; +} + +void cSkinSTTNGDisplayChannel::SetChannel(const cChannel *Channel, int Number) +{ + osd->DrawRectangle(x3, y0, x4 - 1, y1 - 1, frameColor); + int x = x4 - 5; + if (Channel && !Channel->GroupSep()) { + int d = 3; + bool rec = cRecordControls::Active(); + x -= bmRecording.Width() + d; + osd->DrawBitmap(x, y0 + (y1 - y0 - bmRecording.Height()) / 2, bmRecording, Theme.Color(rec ? clrChannelSymbolRecFg : clrChannelSymbolOff), rec ? Theme.Color(clrChannelSymbolRecBg) : frameColor); + x -= bmEncrypted.Width() + d; + osd->DrawBitmap(x, y0 + (y1 - y0 - bmEncrypted.Height()) / 2, bmEncrypted, Theme.Color(Channel->Ca() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor); + x -= bmDolbyDigital.Width() + d; + osd->DrawBitmap(x, y0 + (y1 - y0 - bmDolbyDigital.Height()) / 2, bmDolbyDigital, Theme.Color(Channel->Dpid1() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor); + x -= bmAudio.Width() + d; + osd->DrawBitmap(x, y0 + (y1 - y0 - bmAudio.Height()) / 2, bmAudio, Theme.Color(Channel->Apid2() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor); + if (Channel->Vpid()) { + x -= bmTeletext.Width() + d; + osd->DrawBitmap(x, y0 + (y1 - y0 - bmTeletext.Height()) / 2, bmTeletext, Theme.Color(Channel->Tpid() ? clrChannelSymbolOn : clrChannelSymbolOff), frameColor); + } + else { + x -= bmRadio.Width() + d; + osd->DrawBitmap(x, y0 + (y1 - y0 - bmRadio.Height()) / 2, bmRadio, Theme.Color(clrChannelSymbolOn), frameColor); + } + } + osd->DrawText(x3 + 2, y0, ChannelString(Channel, Number), Theme.Color(clrChannelName), frameColor, cFont::GetFont(fontOsd), x - x3 - 2); +} + +void cSkinSTTNGDisplayChannel::SetEvents(const cEvent *Present, const cEvent *Following) +{ + if (!withInfo) + return; + if (present != Present) + lastSeen = -1; + present = Present; + osd->DrawRectangle(x0, y3, x1 - 1, y4 - 1, frameColor); + osd->DrawRectangle(x3, y3, x7 - 1, y4 - 1, Theme.Color(clrBackground)); + for (int i = 0; i < 2; i++) { + const cEvent *e = !i ? Present : Following; + if (e) { + osd->DrawText(x0 + 2, y3 + 2 * i * lineHeight, e->GetTimeString(), Theme.Color(clrChannelEpgTime), frameColor, cFont::GetFont(fontOsd)); + osd->DrawText(x3 + 2, y3 + 2 * i * lineHeight, e->Title(), Theme.Color(clrChannelEpgTitle), Theme.Color(clrBackground), cFont::GetFont(fontOsd), x4 - x3 - 2); + osd->DrawText(x3 + 2, y3 + (2 * i + 1) * lineHeight, e->ShortText(), Theme.Color(clrChannelEpgShortText), Theme.Color(clrBackground), cFont::GetFont(fontSml), x4 - x3 - 2); + } + } +} + +void cSkinSTTNGDisplayChannel::Flush(void) +{ + if (withInfo) { + const char *date = DayDateTime(); + const cFont *font = cFont::GetFont(fontSml); + osd->DrawText(x4 - font->Width(date) - 2, y7 - font->Height(date), date, Theme.Color(clrChannelDate), frameColor, font); + + int seen = 0; + if (present) { + time_t t = time(NULL); + if (t > present->StartTime()) + seen = min(y4 - y3 - 1, int((y4 - y3) * double(t - present->StartTime()) / present->Duration())); + } + if (seen != lastSeen) { + osd->DrawRectangle(x1 + Gap, y3, x1 + Gap + ScrollWidth - 1, y4 - 1, Theme.Color(clrChannelTimebarRest)); + if (seen) + osd->DrawRectangle(x1 + Gap, y3, x1 + Gap + ScrollWidth - 1, y3 + seen, Theme.Color(clrChannelTimebarSeen)); + lastSeen = seen; + } + } + osd->Flush(); +} + +// --- cSkinSTTNGDisplayMenu ------------------------------------------------- + +class cSkinSTTNGDisplayMenu : public cSkinDisplayMenu { +private: + cOsd *osd; + int x0, x1, x2, x3, x4, x5, x6, x7; + int y0, y1, y2, y3, y4, y5, y6, y7; + int lineHeight; + tColor frameColor; + int currentIndex; + bool message; + void SetScrollbar(void); +public: + cSkinSTTNGDisplayMenu(void); + virtual ~cSkinSTTNGDisplayMenu(); + virtual void Scroll(bool Up, bool Page); + virtual int MaxItems(void); + virtual void Clear(void); + virtual void SetTitle(const char *Title); + virtual void SetButtons(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void SetItem(const char *Text, int Index, bool Current, bool Selectable); + virtual void SetEvent(const cEvent *Event); + virtual void SetRecording(const cRecording *Recording); + virtual void SetText(const char *Text, bool FixedFont); + virtual void Flush(void); + }; + +cSkinSTTNGDisplayMenu::cSkinSTTNGDisplayMenu(void) +{ + const cFont *font = cFont::GetFont(fontOsd); + lineHeight = font->Height(); + frameColor = Theme.Color(clrMenuFrame); + currentIndex = -1; + message = false; + x0 = 0; + x1 = lineHeight / 2; + x2 = x1 + Roundness; + x3 = x2 + Gap; + x7 = Setup.OSDWidth; + x6 = x7 - lineHeight / 2; + x5 = x6 - lineHeight / 2; + x4 = x5 - Gap; + y0 = 0; + y1 = lineHeight; + y2 = y1 + Roundness; + y3 = y2 + Gap; + y7 = Setup.OSDHeight; + y6 = y7 - cFont::GetFont(fontSml)->Height(); + y5 = y6 - Roundness; + y4 = y5 - Gap; + int yt = (y0 + y1) / 2; + int yb = (y6 + y7) / 2; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop); + tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 4 } }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else { + tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 2 }, + { x0, y1, x7 - 1, y6 - 1, 2 }, + { x0, y6, x7 - 1, y7 - 1, 4 } + }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + /*TODO + tArea Areas[] = { { x0, y0, x7 - 1, y3 - 1, 2 }, + { x0, y3, x3 - 1, y4 - 1, 1 }, + { x3, y3, x5 - 1, y4 - 1, 2 }, + { x0, y4, x7 - 1, y7 - 1, 4 } + }; + */ + } + osd->DrawRectangle(x0, y0, x7 - 1, y7 - 1, Theme.Color(clrBackground)); + osd->DrawRectangle(x0, y0, x1 - 1, y1 - 1, clrTransparent); + osd->DrawRectangle(x0, y6, x1 - 1, y7 - 1, clrTransparent); + osd->DrawRectangle(x6, y0, x7 - 1, yt - 1, clrTransparent); + osd->DrawRectangle(x6, yb, x7 - 1, y7 - 1, clrTransparent); + osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 2); + osd->DrawRectangle(x1, y0, x2 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x3, y0, x4 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x5, y0, x6 - 1, y1 - 1, frameColor); + osd->DrawEllipse (x6, y0, x7 - 1, y1 - 1, frameColor, 5); + osd->DrawRectangle(x0, y1, x1 - 1, y6 - 1, frameColor); + osd->DrawEllipse (x1, y1, x2 - 1, y2 - 1, frameColor, -2); + osd->DrawEllipse (x1, y5, x2 - 1, y6 - 1, frameColor, -3); + osd->DrawEllipse (x0, y6, x1 - 1, y7 - 1, frameColor, 3); + osd->DrawRectangle(x1, y6, x2 - 1, y7 - 1, frameColor); + osd->DrawRectangle(x3, y6, x4 - 1, y7 - 1, frameColor); + osd->DrawRectangle(x5, y6, x6 - 1, y7 - 1, frameColor); + osd->DrawEllipse (x6, y6, x7 - 1, y7 - 1, frameColor, 5); +} + +cSkinSTTNGDisplayMenu::~cSkinSTTNGDisplayMenu() +{ + delete osd; +} + +void cSkinSTTNGDisplayMenu::SetScrollbar(void) +{ + if (textScroller.CanScroll()) { + int h = lineHeight; + int yt = textScroller.Top(); + int yb = yt + textScroller.Height(); + int st = yt + h + Gap; + int sb = yb - h - Gap; + int tt = st + (sb - st) * textScroller.Offset() / textScroller.Total(); + int tb = tt + (sb - st) * textScroller.Shown() / textScroller.Total(); + osd->DrawRectangle(x5, st, x5 + ScrollWidth - 1, sb, Theme.Color(clrMenuScrollbarTotal)); + osd->DrawRectangle(x5, tt, x5 + ScrollWidth - 1, tb, Theme.Color(clrMenuScrollbarShown)); + osd->DrawRectangle(x5, yt, x6 - 1, yt + h - 1, frameColor); + osd->DrawEllipse (x6, yt, x7 - 1, yt + h - 1, frameColor, 5); + osd->DrawRectangle(x5, yb - h, x6 - 1, yb - 1, frameColor); + osd->DrawEllipse (x6, yb - h, x7 - 1, yb - 1, frameColor, 5); + if (textScroller.CanScrollUp()) { + cBitmap bm(arrowup_xpm); + osd->DrawBitmap(x5 + (x7 - x5 - bm.Width()) / 2 - 2, yt + (h - bm.Height()) / 2, bm, Theme.Color(clrMenuScrollbarArrow), frameColor); + } + if (textScroller.CanScrollDown()) { + cBitmap bm(arrowdown_xpm); + osd->DrawBitmap(x5 + (x7 - x5 - bm.Width()) / 2 - 2, yb - h + (h - bm.Height()) / 2, bm, Theme.Color(clrMenuScrollbarArrow), frameColor); + } + } +} + +void cSkinSTTNGDisplayMenu::Scroll(bool Up, bool Page) +{ + cSkinDisplayMenu::Scroll(Up, Page); + SetScrollbar(); +} + +int cSkinSTTNGDisplayMenu::MaxItems(void) +{ + return (y4 - y3 - 2 * Roundness) / lineHeight; +} + +void cSkinSTTNGDisplayMenu::Clear(void) +{ + textScroller.Reset(); + osd->DrawRectangle(x1, y3, x7 - 1, y4 - 1, Theme.Color(clrBackground)); +} + +void cSkinSTTNGDisplayMenu::SetTitle(const char *Title) +{ + const cFont *font = cFont::GetFont(fontOsd); + const char *VDR = " VDR"; + int w = font->Width(VDR); + osd->DrawText(x3 + 5, y0, Title, Theme.Color(clrMenuTitle), frameColor, font, x4 - w - x3 - 5); + osd->DrawText(x4 - w, y0, VDR, frameColor, clrBlack, font); +} + +void cSkinSTTNGDisplayMenu::SetButtons(const char *Red, const char *Green, const char *Yellow, const char *Blue) +{ + const char *date = DayDateTime(); + const cFont *font = cFont::GetFont(fontSml); + int d = 10; + int d2 = d / 2; + int t4 = x4 - font->Width(date) - 2; + int w = t4 - x3; + int t0 = x3 + d2; + int t1 = x3 + w / 4; + int t2 = x3 + w / 2; + int t3 = t4 - w / 4; + osd->DrawRectangle(t0 + d2, y6, t1 - d2, y7 - 1, clrBlack); + osd->DrawRectangle(t1 + d2, y6, t2 - d2, y7 - 1, clrBlack); + osd->DrawRectangle(t2 + d2, y6, t3 - d2, y7 - 1, clrBlack); + osd->DrawRectangle(t3 + d2, y6, t4 - d2, y7 - 1, clrBlack); + osd->DrawText(t0 + d, y6, Red, Theme.Color(clrButtonRedFg), Theme.Color(clrButtonRedBg), font, t1 - t0 - 2 * d, 0, taCenter); + osd->DrawText(t1 + d, y6, Green, Theme.Color(clrButtonGreenFg), Theme.Color(clrButtonGreenBg), font, t2 - t1 - 2 * d, 0, taCenter); + osd->DrawText(t2 + d, y6, Yellow, Theme.Color(clrButtonYellowFg), Theme.Color(clrButtonYellowBg), font, t3 - t2 - 2 * d, 0, taCenter); + osd->DrawText(t3 + d, y6, Blue, Theme.Color(clrButtonBlueFg), Theme.Color(clrButtonBlueBg), font, t4 - t3 - 2 * d, 0, taCenter); +} + +void cSkinSTTNGDisplayMenu::SetMessage(eMessageType Type, const char *Text) +{ + const cFont *font = cFont::GetFont(fontSml); + if (Text) { + osd->SaveRegion(x3, y6, x4 - 1, y7 - 1); + osd->DrawText(x3, y6, Text, Theme.Color(clrMessageStatusFg + 2 * Type), Theme.Color(clrMessageStatusBg + 2 * Type), font, x4 - x3, 0, taCenter); + message = true; + } + else { + osd->RestoreRegion(); + message = false; + } +} + +void cSkinSTTNGDisplayMenu::SetItem(const char *Text, int Index, bool Current, bool Selectable) +{ + int y = y3 + Roundness + Index * lineHeight; + tColor ColorFg, ColorBg; + if (Current) { + ColorFg = Theme.Color(clrMenuItemCurrentFg); + ColorBg = Theme.Color(clrMenuItemCurrentBg); + osd->DrawEllipse (x1, y - Roundness, x2 - 1, y - 1, frameColor, -3); + osd->DrawRectangle(x1, y, x2 - 1, y + lineHeight - 1, frameColor); + osd->DrawEllipse (x1, y + lineHeight, x2 - 1, y + lineHeight + Roundness - 1, frameColor, -2); + osd->DrawRectangle(x3, y, x4 - 1, y + lineHeight - 1, ColorBg); + currentIndex = Index; + } + else { + ColorFg = Theme.Color(Selectable ? clrMenuItemSelectable : clrMenuItemNonSelectable); + ColorBg = Theme.Color(clrBackground); + if (currentIndex == Index) { + osd->DrawRectangle(x1, y - Roundness, x2 - 1, y + lineHeight + Roundness - 1, Theme.Color(clrBackground)); + osd->DrawRectangle(x3, y, x4 - 1, y + lineHeight - 1, Theme.Color(clrBackground)); + } + } + const cFont *font = cFont::GetFont(fontOsd); + for (int i = 0; i < MaxTabs; i++) { + const char *s = GetTabbedText(Text, i); + if (s) { + int xt = x3 + 5 + Tab(i); + osd->DrawText(xt, y, s, ColorFg, ColorBg, font, x4 - xt); + } + if (!Tab(i + 1)) + break; + } + SetEditableWidth(x4 - x3 - 5 - Tab(1)); +} + +void cSkinSTTNGDisplayMenu::SetEvent(const cEvent *Event) +{ + if (!Event) + return; + const cFont *font = cFont::GetFont(fontOsd); + int xl = x3 + 5; + int y = y3; + cTextScroller ts; + char t[32]; + snprintf(t, sizeof(t), "%s %s - %s", Event->GetDateString(), Event->GetTimeString(), Event->GetEndTimeString());//TODO dayname, no year + ts.Set(osd, xl, y, x4 - xl, y4 - y, t, font, Theme.Color(clrMenuEventTime), Theme.Color(clrBackground)); + if (Event->Vps() && Event->Vps() != Event->StartTime()) { + char *buffer; + asprintf(&buffer, " VPS: %s", Event->GetVpsString()); + const cFont *font = cFont::GetFont(fontSml); + osd->DrawText(x4 - font->Width(buffer), y, buffer, Theme.Color(clrMenuEventVps), frameColor, font); + int yb = y + font->Height(); + osd->DrawRectangle(x5, y, x6 - 1, yb - 1, frameColor); + osd->DrawEllipse (x6, y, x7 - 1, yb - 1, frameColor, 5); + free(buffer); + } + y += ts.Height(); + y += font->Height(); + ts.Set(osd, xl, y, x4 - xl, y4 - y, Event->Title(), font, Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground)); + y += ts.Height(); + if (!isempty(Event->ShortText())) { + const cFont *font = cFont::GetFont(fontSml); + ts.Set(osd, xl, y, x4 - xl, y4 - y, Event->ShortText(), font, Theme.Color(clrMenuEventShortText), Theme.Color(clrBackground)); + y += ts.Height(); + } + y += font->Height(); + if (!isempty(Event->Description())) { + int yt = y; + int yb = y4 - Roundness; + textScroller.Set(osd, xl, yt, x4 - xl, yb - yt, Event->Description(), font, Theme.Color(clrMenuEventDescription), Theme.Color(clrBackground)); + yb = yt + textScroller.Height(); + osd->DrawEllipse (x1, yt - Roundness, x2, yt, frameColor, -3); + osd->DrawRectangle(x1, yt, x2, yb, frameColor); + osd->DrawEllipse (x1, yb, x2, yb + Roundness, frameColor, -2); + SetScrollbar(); + } +} + +void cSkinSTTNGDisplayMenu::SetRecording(const cRecording *Recording) +{ + SetText(Recording->Summary(), false); //XXX +} + +void cSkinSTTNGDisplayMenu::SetText(const char *Text, bool FixedFont) +{ + const cFont *font = cFont::GetFont(FixedFont ? fontFix : fontOsd); + font = cFont::GetFont(fontSml);//XXX -> make a way to let the text define which font to use + textScroller.Set(osd, x3, y3, x4 - x3, y4 - y3, Text, font, Theme.Color(clrMenuText), Theme.Color(clrBackground)); + SetScrollbar(); +} + +void cSkinSTTNGDisplayMenu::Flush(void) +{ + if (!message) { + const char *date = DayDateTime(); + const cFont *font = cFont::GetFont(fontSml); + osd->DrawText(x4 - font->Width(date) - 2, y7 - font->Height(date), date, Theme.Color(clrMenuDate), frameColor, font); + } + osd->Flush(); +} + +// --- cSkinSTTNGDisplayReplay ----------------------------------------------- + +class cSkinSTTNGDisplayReplay : public cSkinDisplayReplay { +private: + cOsd *osd; + int x0, x1, x2, x3, x4, x5, x6, x7; + int y0, y1, y2, y3, y4, y5, y6, y7; + tColor frameColor; + int lastCurrentWidth; +public: + cSkinSTTNGDisplayReplay(bool ModeOnly); + virtual ~cSkinSTTNGDisplayReplay(); + virtual void SetTitle(const char *Title); + virtual void SetMode(bool Play, bool Forward, int Speed); + virtual void SetProgress(int Current, int Total); + virtual void SetCurrent(const char *Current); + virtual void SetTotal(const char *Total); + virtual void SetJump(const char *Jump); + virtual void Flush(void); + }; + +#define SymbolWidth 30 +#define SymbolHeight 30 + +cSkinSTTNGDisplayReplay::cSkinSTTNGDisplayReplay(bool ModeOnly) +{ + const cFont *font = cFont::GetFont(fontSml); + int lineHeight = font->Height(); + frameColor = Theme.Color(clrReplayFrame); + lastCurrentWidth = 0; + cBitmap bm(play_xpm); + x0 = 0; + x1 = max(SymbolWidth, bm.Width()); + x2 = x1 + Roundness; + x3 = x2 + Gap; + x7 = Setup.OSDWidth; + x6 = x7 - lineHeight / 2; + x5 = x6 - lineHeight / 2; + x4 = x5 - Gap; + y0 = 0; + y1 = lineHeight; + y2 = y1 + Roundness; + y3 = y2 + Gap; + y4 = y3 + max(SymbolHeight, bm.Height()); + y5 = y4 + Gap; + y6 = y5 + Roundness; + y7 = y6 + font->Height(); + int yt = (y0 + y1) / 2; + int yb = (y6 + y7) / 2; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - y7); + tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 4 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + osd->DrawRectangle(x0, y0, x7 - 1, y7 - 1, ModeOnly ? clrTransparent : Theme.Color(clrBackground)); + if (!ModeOnly) { + osd->DrawRectangle(x0, y0, x1 - 1, y1 - 1, clrTransparent); + osd->DrawRectangle(x0, y6, x1 - 1, y7 - 1, clrTransparent); + osd->DrawRectangle(x6, y0, x7 - 1, yt - 1, clrTransparent); + osd->DrawRectangle(x6, yb, x7 - 1, y7 - 1, clrTransparent); + osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 2); + osd->DrawRectangle(x1, y0, x4 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x5, y0, x6 - 1, y1 - 1, frameColor); + osd->DrawEllipse (x6, y0, x7 - 1, y1 - 1, frameColor, 5); + osd->DrawRectangle(x0, y1, x1 - 1, y2 - 1, frameColor); + osd->DrawEllipse (x1, y1, x2 - 1, y2 - 1, frameColor, -2); + } + osd->DrawRectangle(x0, y3, x1 - 1, y4 - 1, frameColor); + if (!ModeOnly) { + osd->DrawRectangle(x0, y5, x1 - 1, y6 - 1, frameColor); + osd->DrawEllipse (x1, y5, x2 - 1, y6 - 1, frameColor, -3); + osd->DrawEllipse (x0, y6, x1 - 1, y7 - 1, frameColor, 3); + osd->DrawRectangle(x1, y6, x4 - 1, y7 - 1, frameColor); + osd->DrawRectangle(x5, y6, x6 - 1, y7 - 1, frameColor); + osd->DrawEllipse (x6, y6, x7 - 1, y7 - 1, frameColor, 5); + } +} + +cSkinSTTNGDisplayReplay::~cSkinSTTNGDisplayReplay() +{ + delete osd; +} + +void cSkinSTTNGDisplayReplay::SetTitle(const char *Title) +{ + osd->DrawText(x3 + 5, y0, Title, Theme.Color(clrReplayTitle), frameColor, cFont::GetFont(fontSml), x4 - x3 - 5); +} + +static char **ReplaySymbols[2][2][5] = { + { { pause_xpm, srew_xpm, srew1_xpm, srew2_xpm, srew3_xpm }, + { pause_xpm, sfwd_xpm, sfwd1_xpm, sfwd2_xpm, sfwd3_xpm }, }, + { { play_xpm, frew_xpm, frew1_xpm, frew2_xpm, frew3_xpm }, + { play_xpm, ffwd_xpm, ffwd1_xpm, ffwd2_xpm, ffwd3_xpm } } + }; + +void cSkinSTTNGDisplayReplay::SetMode(bool Play, bool Forward, int Speed) +{ + if (Speed < -1) + Speed = -1; + if (Speed > 3) + Speed = 3; + cBitmap bm(ReplaySymbols[Play][Forward][Speed + 1]); + osd->DrawBitmap(x0 + (x1 - x0 - bm.Width()) / 2, y3 + (y4 - y3 - bm.Height()) / 2, bm, Theme.Color(clrReplayMode), frameColor); +} + +void cSkinSTTNGDisplayReplay::SetProgress(int Current, int Total) +{ + cProgressBar pb(x4 - x3, y4 - y3, Current, Total, marks, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent)); + osd->DrawBitmap(x3, y3, pb); +} + +void cSkinSTTNGDisplayReplay::SetCurrent(const char *Current) +{ + const cFont *font = cFont::GetFont(fontSml); + int w = font->Width(Current); + osd->DrawText(x3, y6, Current, Theme.Color(clrReplayCurrent), frameColor, font, lastCurrentWidth > w ? lastCurrentWidth : 0); + lastCurrentWidth = w; +} + +void cSkinSTTNGDisplayReplay::SetTotal(const char *Total) +{ + const cFont *font = cFont::GetFont(fontSml); + osd->DrawText(x4 - font->Width(Total) - 5, y6, Total, Theme.Color(clrReplayTotal), frameColor, font); +} + +void cSkinSTTNGDisplayReplay::SetJump(const char *Jump) +{ + osd->DrawText(x0 + (x4 - x0) / 4, y6, Jump, Theme.Color(clrReplayJump), frameColor, cFont::GetFont(fontSml), (x4 - x3) / 2, 0, taCenter); +} + +void cSkinSTTNGDisplayReplay::Flush(void) +{ + osd->Flush(); +} + +// --- cSkinSTTNGDisplayVolume ----------------------------------------------- + +class cSkinSTTNGDisplayVolume : public cSkinDisplayVolume { +private: + cOsd *osd; + int x0, x1, x2, x3, x4, x5, x6, x7; + int y0, y1; + tColor frameColor; + int mute; +public: + cSkinSTTNGDisplayVolume(void); + virtual ~cSkinSTTNGDisplayVolume(); + virtual void SetVolume(int Current, int Total, bool Mute); + virtual void Flush(void); + }; + +cSkinSTTNGDisplayVolume::cSkinSTTNGDisplayVolume(void) +{ + const cFont *font = cFont::GetFont(fontOsd); + int lineHeight = font->Height(); + frameColor = Theme.Color(clrVolumeFrame); + mute = -1; + x0 = 0; + x1 = lineHeight / 2; + x2 = lineHeight; + x3 = x2 + Gap; + x7 = Setup.OSDWidth; + x6 = x7 - lineHeight / 2; + x5 = x6 - lineHeight / 2; + x4 = x5 - Gap; + y0 = 0; + y1 = lineHeight; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - y1); + tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 4 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + osd->DrawRectangle(x0, y0, x7 - 1, y1 - 1, clrTransparent); + osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 7); + osd->DrawRectangle(x1, y0, x2 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x3, y0, x4 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x5, y0, x6 - 1, y1 - 1, frameColor); + osd->DrawEllipse (x6, y0, x7 - 1, y1 - 1, frameColor, 5); +} + +cSkinSTTNGDisplayVolume::~cSkinSTTNGDisplayVolume() +{ + delete osd; +} + +void cSkinSTTNGDisplayVolume::SetVolume(int Current, int Total, bool Mute) +{ + int xl = x3 + 5; + int xr = x4 - 5; + int yt = y0 + 3; + int yb = y1 - 3; + if (mute != Mute) { + osd->DrawRectangle(x3, y0, x4 - 1, y1 - 1, frameColor); + mute = Mute; + } + cBitmap bm(Mute ? mute_xpm : volume_xpm); + osd->DrawBitmap(xl, y0 + (y1 - y0 - bm.Height()) / 2, bm, Theme.Color(clrVolumeSymbol), frameColor); + if (!Mute) { + xl += bm.Width() + 5; + int w = (y1 - y0) / 3; + int d = 3; + int n = (xr - xl + d) / (w + d); + int x = xr - n * (w + d); + tColor Color = Theme.Color(clrVolumeBarLower); + for (int i = 0; i < n; i++) { + if (Total * i >= Current * n) + Color = Theme.Color(clrVolumeBarUpper); + osd->DrawRectangle(x, yt, x + w - 1, yb - 1, Color); + x += w + d; + } + } +} + +void cSkinSTTNGDisplayVolume::Flush(void) +{ + osd->Flush(); +} + +// --- cSkinSTTNGDisplayMessage ---------------------------------------------- + +class cSkinSTTNGDisplayMessage : public cSkinDisplayMessage { +private: + cOsd *osd; + int x0, x1, x2, x3, x4, x5, x6, x7; + int y0, y1; +public: + cSkinSTTNGDisplayMessage(void); + virtual ~cSkinSTTNGDisplayMessage(); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void Flush(void); + }; + +cSkinSTTNGDisplayMessage::cSkinSTTNGDisplayMessage(void) +{ + const cFont *font = cFont::GetFont(fontOsd); + int lineHeight = font->Height(); + tColor frameColor = Theme.Color(clrMessageFrame); + x0 = 0; + x1 = lineHeight / 2; + x2 = lineHeight; + x3 = x2 + Gap; + x7 = Setup.OSDWidth; + x6 = x7 - lineHeight / 2; + x5 = x6 - lineHeight / 2; + x4 = x5 - Gap; + y0 = 0; + y1 = lineHeight; + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - y1); + tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 2 } }; + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + osd->DrawRectangle(x0, y0, x7 - 1, y1 - 1, clrTransparent); + osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 7); + osd->DrawRectangle(x1, y0, x2 - 1, y1 - 1, frameColor); + osd->DrawRectangle(x5, y0, x6 - 1, y1 - 1, frameColor); + osd->DrawEllipse (x6, y0, x7 - 1, y1 - 1, frameColor, 5); +} + +cSkinSTTNGDisplayMessage::~cSkinSTTNGDisplayMessage() +{ + delete osd; +} + +void cSkinSTTNGDisplayMessage::SetMessage(eMessageType Type, const char *Text) +{ + const cFont *font = cFont::GetFont(fontOsd); + osd->DrawText(x3, y0, Text, Theme.Color(clrMessageStatusFg + 2 * Type), Theme.Color(clrMessageStatusBg + 2 * Type), font, x4 - x3, 0, taCenter); +} + +void cSkinSTTNGDisplayMessage::Flush(void) +{ + osd->Flush(); +} + +// --- cSkinSTTNG ------------------------------------------------------------ + +cSkinSTTNG::cSkinSTTNG(void) +:cSkin("sttng", &::Theme)//XXX naming problem??? +{ +} + +const char *cSkinSTTNG::Description(void) +{ + return tr("ST:TNG Panels"); +} + +cSkinDisplayChannel *cSkinSTTNG::DisplayChannel(bool WithInfo) +{ + return new cSkinSTTNGDisplayChannel(WithInfo); +} + +cSkinDisplayMenu *cSkinSTTNG::DisplayMenu(void) +{ + return new cSkinSTTNGDisplayMenu; +} + +cSkinDisplayReplay *cSkinSTTNG::DisplayReplay(bool ModeOnly) +{ + return new cSkinSTTNGDisplayReplay(ModeOnly); +} + +cSkinDisplayVolume *cSkinSTTNG::DisplayVolume(void) +{ + return new cSkinSTTNGDisplayVolume; +} + +cSkinDisplayMessage *cSkinSTTNG::DisplayMessage(void) +{ + return new cSkinSTTNGDisplayMessage; +} diff --git a/skinsttng.h b/skinsttng.h new file mode 100644 index 00000000..82b25995 --- /dev/null +++ b/skinsttng.h @@ -0,0 +1,26 @@ +/* + * skinsttng.h: A VDR skin with ST:TNG Panels + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: skinsttng.h 1.1 2004/04/18 09:38:47 kls Exp $ + */ + +#ifndef __SKINSTTNG_H +#define __SKINSTTNG_H + +#include "skins.h" + +class cSkinSTTNG : public cSkin { +public: + cSkinSTTNG(void); + virtual const char *Description(void); + virtual cSkinDisplayChannel *DisplayChannel(bool WithInfo); + virtual cSkinDisplayMenu *DisplayMenu(void); + virtual cSkinDisplayReplay *DisplayReplay(bool ModeOnly); + virtual cSkinDisplayVolume *DisplayVolume(void); + virtual cSkinDisplayMessage *DisplayMessage(void); + }; + +#endif //__SKINSTTNG_H diff --git a/symbols/arrowdown.xpm b/symbols/arrowdown.xpm new file mode 100644 index 00000000..a3f49cdf --- /dev/null +++ b/symbols/arrowdown.xpm @@ -0,0 +1,17 @@ +/* XPM */ +static char * arrowdown_xpm[] = { +"12 12 2 1", +". c #FFFFFF", +"+ c #000000", +"++++++++++++", +"++++++++++++", +".++++++++++.", +".++++++++++.", +"..++++++++..", +"..++++++++..", +"...++++++...", +"...++++++...", +"....++++....", +"....++++....", +".....++.....", +".....++....."}; diff --git a/symbols/arrowup.xpm b/symbols/arrowup.xpm new file mode 100644 index 00000000..a177b4a2 --- /dev/null +++ b/symbols/arrowup.xpm @@ -0,0 +1,17 @@ +/* XPM */ +static char * arrowup_xpm[] = { +"12 12 2 1", +". c #FFFFFF", +"+ c #000000", +".....++.....", +".....++.....", +"....++++....", +"....++++....", +"...++++++...", +"...++++++...", +"..++++++++..", +"..++++++++..", +".++++++++++.", +".++++++++++.", +"++++++++++++", +"++++++++++++"}; diff --git a/symbols/audio.xpm b/symbols/audio.xpm new file mode 100644 index 00000000..26f7cabf --- /dev/null +++ b/symbols/audio.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * audio_xpm[] = { +"27 18 2 1", +". c #FFFFFF", +"+ c #000000", +"...........................", +"...........................", +"..+++++++++++++++++++++++..", +"..+++++++++++++++++++++++..", +"..++...................++..", +"..++...................++..", +"..++...++++.....++++...++..", +"..++..++..++...++..++..++..", +"..++.++....++.++....++.++..", +"..++..++..++...++..++..++..", +"..++...++++.....++++...++..", +"..++...................++..", +"..++...................++..", +"..++...................++..", +"..+++++++++++++++++++++++..", +"..+++++++++++++++++++++++..", +"...........................", +"..........................."}; diff --git a/symbols/dolbydigital.xpm b/symbols/dolbydigital.xpm new file mode 100644 index 00000000..e51709ea --- /dev/null +++ b/symbols/dolbydigital.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * dolbydigital_xpm[] = { +"27 18 2 1", +". c #FFFFFF", +"+ c #000000", +"...........................", +"...........................", +"..+++++++++++.+++++++++++..", +"..++.++++++++.++++++++.++..", +"..++...++++++.++++++...++..", +"..++.....++++.++++.....++..", +"..++......+++.+++......++..", +"..++.......++.++.......++..", +"..++.......++.++.......++..", +"..++.......++.++.......++..", +"..++.......++.++.......++..", +"..++......+++.+++......++..", +"..++.....++++.++++.....++..", +"..++...++++++.++++++...++..", +"..++.++++++++.++++++++.++..", +"..+++++++++++.+++++++++++..", +"...........................", +"..........................."}; diff --git a/symbols/encrypted.xpm b/symbols/encrypted.xpm new file mode 100644 index 00000000..f2ae082a --- /dev/null +++ b/symbols/encrypted.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * encrypted_xpm[] = { +"27 18 2 1", +". c #FFFFFF", +"+ c #000000", +"...........................", +"...........................", +"..+++++++++++++++++++++++..", +"..+++++++++++++++++++++++..", +"..++...................++..", +"..++...................++..", +"..++............++++...++..", +"..++...........++..++..++..", +"..++.+++++++++++....++.++..", +"..++.+++++++++++....++.++..", +"..++..+++......++..++..++..", +"..++..+++.......++++...++..", +"..++...................++..", +"..++...................++..", +"..+++++++++++++++++++++++..", +"..+++++++++++++++++++++++..", +"...........................", +"..........................."}; diff --git a/symbols/ffwd.xpm b/symbols/ffwd.xpm new file mode 100644 index 00000000..7d1edc2c --- /dev/null +++ b/symbols/ffwd.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * ffwd_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"..++......++................", +"..+++.....+++...............", +"..+++++...+++++.............", +"..++++++....++++............", +"..++++++++...+++++..........", +"..+++++++++....++++.........", +"..+++++++++++...+++++.......", +"..++++++++++++....++++......", +"..++++++++++++++...+++++....", +"..+++++++++++++++....++++...", +"..++++++++++++++++....++++..", +"..++++++++++++++++....++++..", +"..+++++++++++++++....++++...", +"..++++++++++++++...+++++....", +"..++++++++++++....++++......", +"..+++++++++++...+++++.......", +"..+++++++++....++++.........", +"..++++++++...+++++..........", +"..++++++....++++............", +"..+++++...+++++.............", +"..+++.....+++...............", +"..++......++................", +"............................", +"............................"}; diff --git a/symbols/ffwd1.xpm b/symbols/ffwd1.xpm new file mode 100644 index 00000000..f78db80c --- /dev/null +++ b/symbols/ffwd1.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * ffwd1_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"..++......++................", +"..+++.....+++...............", +"..+++++...+++++.............", +"..++++++....++++............", +"..++++++++...+++++..........", +"..+++++++++....++++.........", +"..+++++++++++...+++++.......", +"..++++++++++++....++++......", +"..++++++++++++++...+++++....", +"..++++++++++++++.....++++...", +"..++++++++++++++...+++++....", +"..++++++++++++....++++......", +"..+++++++++++...+++++.......", +"..+++++++++....++++.........", +"..++++++++...+++++..........", +"..++++++....++++............", +"..+++++...+++++.............", +"..+++.....+++...............", +"..++......++................", +"............................", +"..++++++....................", +"..++++++....................", +"............................", +"............................"}; diff --git a/symbols/ffwd2.xpm b/symbols/ffwd2.xpm new file mode 100644 index 00000000..f117fc25 --- /dev/null +++ b/symbols/ffwd2.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * ffwd2_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"..++......++................", +"..+++.....+++...............", +"..+++++...+++++.............", +"..++++++....++++............", +"..++++++++...+++++..........", +"..+++++++++....++++.........", +"..+++++++++++...+++++.......", +"..++++++++++++....++++......", +"..++++++++++++++...+++++....", +"..++++++++++++++.....++++...", +"..++++++++++++++...+++++....", +"..++++++++++++....++++......", +"..+++++++++++...+++++.......", +"..+++++++++....++++.........", +"..++++++++...+++++..........", +"..++++++....++++............", +"..+++++...+++++.............", +"..+++.....+++...............", +"..++......++................", +"............................", +"..++++++...++++++...........", +"..++++++...++++++...........", +"............................", +"............................"}; diff --git a/symbols/ffwd3.xpm b/symbols/ffwd3.xpm new file mode 100644 index 00000000..8b9e9b0d --- /dev/null +++ b/symbols/ffwd3.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * ffwd3_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"..++......++................", +"..+++.....+++...............", +"..+++++...+++++.............", +"..++++++....++++............", +"..++++++++...+++++..........", +"..+++++++++....++++.........", +"..+++++++++++...+++++.......", +"..++++++++++++....++++......", +"..++++++++++++++...+++++....", +"..++++++++++++++.....++++...", +"..++++++++++++++...+++++....", +"..++++++++++++....++++......", +"..+++++++++++...+++++.......", +"..+++++++++....++++.........", +"..++++++++...+++++..........", +"..++++++....++++............", +"..+++++...+++++.............", +"..+++.....+++...............", +"..++......++................", +"............................", +"..++++++...++++++...++++++..", +"..++++++...++++++...++++++..", +"............................", +"............................"}; diff --git a/symbols/frew.xpm b/symbols/frew.xpm new file mode 100644 index 00000000..d8a6c474 --- /dev/null +++ b/symbols/frew.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * frew_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"................++......++..", +"...............+++.....+++..", +".............+++++...+++++..", +"............++++....++++++..", +"..........+++++...++++++++..", +".........++++....+++++++++..", +".......+++++...+++++++++++..", +"......++++....++++++++++++..", +"....+++++...++++++++++++++..", +"...++++....+++++++++++++++..", +"..++++....++++++++++++++++..", +"..++++....++++++++++++++++..", +"...++++....+++++++++++++++..", +"....+++++...++++++++++++++..", +"......++++....++++++++++++..", +".......+++++...+++++++++++..", +".........++++....+++++++++..", +"..........+++++...++++++++..", +"............++++....++++++..", +".............+++++...+++++..", +"...............+++.....+++..", +"................++......++..", +"............................", +"............................"}; diff --git a/symbols/frew1.xpm b/symbols/frew1.xpm new file mode 100644 index 00000000..493c229d --- /dev/null +++ b/symbols/frew1.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * frew1_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"................++......++..", +"...............+++.....+++..", +".............+++++...+++++..", +"............++++....++++++..", +"..........+++++...++++++++..", +".........++++....+++++++++..", +".......+++++...+++++++++++..", +"......++++....++++++++++++..", +"....+++++...++++++++++++++..", +"...++++.....++++++++++++++..", +"....+++++...++++++++++++++..", +"......++++....++++++++++++..", +".......+++++...+++++++++++..", +".........++++....+++++++++..", +"..........+++++...++++++++..", +"............++++....++++++..", +".............+++++...+++++..", +"...............+++.....+++..", +"................++......++..", +"............................", +"....................++++++..", +"....................++++++..", +"............................", +"............................"}; diff --git a/symbols/frew2.xpm b/symbols/frew2.xpm new file mode 100644 index 00000000..81fc0ee6 --- /dev/null +++ b/symbols/frew2.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * frew2_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"................++......++..", +"...............+++.....+++..", +".............+++++...+++++..", +"............++++....++++++..", +"..........+++++...++++++++..", +".........++++....+++++++++..", +".......+++++...+++++++++++..", +"......++++....++++++++++++..", +"....+++++...++++++++++++++..", +"...++++.....++++++++++++++..", +"....+++++...++++++++++++++..", +"......++++....++++++++++++..", +".......+++++...+++++++++++..", +".........++++....+++++++++..", +"..........+++++...++++++++..", +"............++++....++++++..", +".............+++++...+++++..", +"...............+++.....+++..", +"................++......++..", +"............................", +"...........++++++...++++++..", +"...........++++++...++++++..", +"............................", +"............................"}; diff --git a/symbols/frew3.xpm b/symbols/frew3.xpm new file mode 100644 index 00000000..19b183cd --- /dev/null +++ b/symbols/frew3.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * frew3_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"................++......++..", +"...............+++.....+++..", +".............+++++...+++++..", +"............++++....++++++..", +"..........+++++...++++++++..", +".........++++....+++++++++..", +".......+++++...+++++++++++..", +"......++++....++++++++++++..", +"....+++++...++++++++++++++..", +"...++++.....++++++++++++++..", +"....+++++...++++++++++++++..", +"......++++....++++++++++++..", +".......+++++...+++++++++++..", +".........++++....+++++++++..", +"..........+++++...++++++++..", +"............++++....++++++..", +".............+++++...+++++..", +"...............+++.....+++..", +"................++......++..", +"............................", +"..++++++...++++++...++++++..", +"..++++++...++++++...++++++..", +"............................", +"............................"}; diff --git a/symbols/mute.xpm b/symbols/mute.xpm new file mode 100644 index 00000000..0f1292f5 --- /dev/null +++ b/symbols/mute.xpm @@ -0,0 +1,25 @@ +/* XPM */ +static char * mute_xpm[] = { +"26 20 2 1", +". c #FFFFFF", +"+ c #000000", +"......++.......++.......++", +"......+++....++++......+++", +".......+++..+++++.....+++.", +"........+++..+.++....+++..", +".......+.+++...++...+++...", +".....++++.+++..++..+++....", +"++++++++...+++....+++.....", +"+++++++.....+++..+++......", +"++...++......++++++.......", +"++...++.......++++........", +"++...++.......++++........", +"++...++......++++++.......", +"+++++++.....+++..+++......", +"++++++++...+++....+++.....", +".....++++.+++..++..+++....", +".......+.+++...++...+++...", +"........+++..+.++....+++..", +".......+++..+++++.....+++.", +"......+++....++++......+++", +"......++.......++.......++"}; diff --git a/symbols/pause.xpm b/symbols/pause.xpm new file mode 100644 index 00000000..c1d82c32 --- /dev/null +++ b/symbols/pause.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * pause_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"......+++++......+++++......", +"............................", +"............................"}; diff --git a/symbols/play.xpm b/symbols/play.xpm new file mode 100644 index 00000000..0e088f86 --- /dev/null +++ b/symbols/play.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * play_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"......++....................", +"......+++...................", +"......+++++.................", +"......++++++................", +"......++++++++..............", +"......+++++++++.............", +"......+++++++++++...........", +"......++++++++++++..........", +"......++++++++++++++........", +"......+++++++++++++++.......", +"......++++++++++++++++......", +"......++++++++++++++++......", +"......+++++++++++++++.......", +"......++++++++++++++........", +"......++++++++++++..........", +"......+++++++++++...........", +"......+++++++++.............", +"......++++++++..............", +"......++++++................", +"......+++++.................", +"......+++...................", +"......++....................", +"............................", +"............................"}; diff --git a/symbols/radio.xpm b/symbols/radio.xpm new file mode 100644 index 00000000..f4d1fc93 --- /dev/null +++ b/symbols/radio.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * radio_xpm[] = { +"27 18 2 1", +". c #FFFFFF", +"+ c #000000", +"...........................", +"............+++++..........", +"............+++++++++......", +"............+++++++++++....", +"............+++...++++++...", +"............+++.....++++...", +"............+++.....+++....", +"............+++.....++.....", +"............+++............", +"............+++............", +"............+++............", +"......+++++++++............", +"...+++++++++++++...........", +"..+++++++++++++++..........", +"..+++++++++++++++..........", +"...+++++++++++++...........", +".....++++++++++............", +"..........................."}; diff --git a/symbols/recording.xpm b/symbols/recording.xpm new file mode 100644 index 00000000..ea7bbd6d --- /dev/null +++ b/symbols/recording.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * recording_xpm[] = { +"27 18 2 1", +". c #FFFFFF", +"+ c #000000", +"...........................", +"...........................", +"..++++++...++++++..++++++..", +"..+++++++.+++++++.+++++++..", +"..++...++.++......++.......", +"..++...++.++......++.......", +"..++...++.++......++.......", +"..++...++.++......++.......", +"..++++++..+++++...++.......", +"..++++....+++++...++.......", +"..++.++...++......++.......", +"..++..++..++......++.......", +"..++...++.++......++.......", +"..++...++.++......++.......", +"..++...++.+++++++.+++++++..", +"..++...++..++++++..++++++..", +"...........................", +"..........................."}; diff --git a/symbols/sfwd.xpm b/symbols/sfwd.xpm new file mode 100644 index 00000000..38468e5d --- /dev/null +++ b/symbols/sfwd.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * sfwd_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"..+++++...++................", +"..+++++...+++...............", +"..+++++...+++++.............", +"..+++++...++++++............", +"..+++++...++++++++..........", +"..+++++...+++++++++.........", +"..+++++...+++++++++++.......", +"..+++++...++++++++++++......", +"..+++++...++++++++++++++....", +"..+++++...+++++++++++++++...", +"..+++++...++++++++++++++++..", +"..+++++...++++++++++++++++..", +"..+++++...+++++++++++++++...", +"..+++++...++++++++++++++....", +"..+++++...++++++++++++......", +"..+++++...+++++++++++.......", +"..+++++...+++++++++.........", +"..+++++...++++++++..........", +"..+++++...++++++............", +"..+++++...+++++.............", +"..+++++...+++...............", +"..+++++...++................", +"............................", +"............................"}; diff --git a/symbols/sfwd1.xpm b/symbols/sfwd1.xpm new file mode 100644 index 00000000..f07801b0 --- /dev/null +++ b/symbols/sfwd1.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * sfwd1_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"..+++++...++................", +"..+++++...+++...............", +"..+++++...+++++.............", +"..+++++...++++++............", +"..+++++...++++++++..........", +"..+++++...+++++++++.........", +"..+++++...+++++++++++.......", +"..+++++...++++++++++++......", +"..+++++...++++++++++++++....", +"..+++++...+++++++++++++++...", +"..+++++...++++++++++++++....", +"..+++++...++++++++++++......", +"..+++++...+++++++++++.......", +"..+++++...+++++++++.........", +"..+++++...++++++++..........", +"..+++++...++++++............", +"..+++++...+++++.............", +"..+++++...+++...............", +"..+++++...++................", +"............................", +"..++++++....................", +"..++++++....................", +"............................", +"............................"}; diff --git a/symbols/sfwd2.xpm b/symbols/sfwd2.xpm new file mode 100644 index 00000000..784744a1 --- /dev/null +++ b/symbols/sfwd2.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * sfwd2_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"..+++++...++................", +"..+++++...+++...............", +"..+++++...+++++.............", +"..+++++...++++++............", +"..+++++...++++++++..........", +"..+++++...+++++++++.........", +"..+++++...+++++++++++.......", +"..+++++...++++++++++++......", +"..+++++...++++++++++++++....", +"..+++++...+++++++++++++++...", +"..+++++...++++++++++++++....", +"..+++++...++++++++++++......", +"..+++++...+++++++++++.......", +"..+++++...+++++++++.........", +"..+++++...++++++++..........", +"..+++++...++++++............", +"..+++++...+++++.............", +"..+++++...+++...............", +"..+++++...++................", +"............................", +"..++++++...++++++...........", +"..++++++...++++++...........", +"............................", +"............................"}; diff --git a/symbols/sfwd3.xpm b/symbols/sfwd3.xpm new file mode 100644 index 00000000..05abde6b --- /dev/null +++ b/symbols/sfwd3.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * sfwd3_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"..+++++...++................", +"..+++++...+++...............", +"..+++++...+++++.............", +"..+++++...++++++............", +"..+++++...++++++++..........", +"..+++++...+++++++++.........", +"..+++++...+++++++++++.......", +"..+++++...++++++++++++......", +"..+++++...++++++++++++++....", +"..+++++...+++++++++++++++...", +"..+++++...++++++++++++++....", +"..+++++...++++++++++++......", +"..+++++...+++++++++++.......", +"..+++++...+++++++++.........", +"..+++++...++++++++..........", +"..+++++...++++++............", +"..+++++...+++++.............", +"..+++++...+++...............", +"..+++++...++................", +"............................", +"..++++++...++++++...++++++..", +"..++++++...++++++...++++++..", +"............................", +"............................"}; diff --git a/symbols/srew.xpm b/symbols/srew.xpm new file mode 100644 index 00000000..4dbae7e5 --- /dev/null +++ b/symbols/srew.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * srew_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"................++...+++++..", +"...............+++...+++++..", +".............+++++...+++++..", +"............++++++...+++++..", +"..........++++++++...+++++..", +".........+++++++++...+++++..", +".......+++++++++++...+++++..", +"......++++++++++++...+++++..", +"....++++++++++++++...+++++..", +"...+++++++++++++++...+++++..", +"..++++++++++++++++...+++++..", +"..++++++++++++++++...+++++..", +"...+++++++++++++++...+++++..", +"....++++++++++++++...+++++..", +"......++++++++++++...+++++..", +".......+++++++++++...+++++..", +".........+++++++++...+++++..", +"..........++++++++...+++++..", +"............++++++...+++++..", +".............+++++...+++++..", +"...............+++...+++++..", +"................++...+++++..", +"............................", +"............................"}; diff --git a/symbols/srew1.xpm b/symbols/srew1.xpm new file mode 100644 index 00000000..101cdf73 --- /dev/null +++ b/symbols/srew1.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * srew1_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"................++...+++++..", +"...............+++...+++++..", +".............+++++...+++++..", +"............++++++...+++++..", +"..........++++++++...+++++..", +".........+++++++++...+++++..", +".......+++++++++++...+++++..", +"......++++++++++++...+++++..", +"....++++++++++++++...+++++..", +"...+++++++++++++++...+++++..", +"....++++++++++++++...+++++..", +"......++++++++++++...+++++..", +".......+++++++++++...+++++..", +".........+++++++++...+++++..", +"..........++++++++...+++++..", +"............++++++...+++++..", +".............+++++...+++++..", +"...............+++...+++++..", +"................++...+++++..", +"............................", +"....................++++++..", +"....................++++++..", +"............................", +"............................"}; diff --git a/symbols/srew2.xpm b/symbols/srew2.xpm new file mode 100644 index 00000000..f664532b --- /dev/null +++ b/symbols/srew2.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * srew2_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"................++...+++++..", +"...............+++...+++++..", +".............+++++...+++++..", +"............++++++...+++++..", +"..........++++++++...+++++..", +".........+++++++++...+++++..", +".......+++++++++++...+++++..", +"......++++++++++++...+++++..", +"....++++++++++++++...+++++..", +"...+++++++++++++++...+++++..", +"....++++++++++++++...+++++..", +"......++++++++++++...+++++..", +".......+++++++++++...+++++..", +".........+++++++++...+++++..", +"..........++++++++...+++++..", +"............++++++...+++++..", +".............+++++...+++++..", +"...............+++...+++++..", +"................++...+++++..", +"............................", +"...........++++++...++++++..", +"...........++++++...++++++..", +"............................", +"............................"}; diff --git a/symbols/srew3.xpm b/symbols/srew3.xpm new file mode 100644 index 00000000..b82a6c72 --- /dev/null +++ b/symbols/srew3.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * srew3_xpm[] = { +"28 26 2 1", +". c #FFFFFF", +"+ c #000000", +"............................", +"............................", +"................++...+++++..", +"...............+++...+++++..", +".............+++++...+++++..", +"............++++++...+++++..", +"..........++++++++...+++++..", +".........+++++++++...+++++..", +".......+++++++++++...+++++..", +"......++++++++++++...+++++..", +"....++++++++++++++...+++++..", +"...+++++++++++++++...+++++..", +"....++++++++++++++...+++++..", +"......++++++++++++...+++++..", +".......+++++++++++...+++++..", +".........+++++++++...+++++..", +"..........++++++++...+++++..", +"............++++++...+++++..", +".............+++++...+++++..", +"...............+++...+++++..", +"................++...+++++..", +"............................", +"..++++++...++++++...++++++..", +"..++++++...++++++...++++++..", +"............................", +"............................"}; diff --git a/symbols/teletext.xpm b/symbols/teletext.xpm new file mode 100644 index 00000000..49fc3c24 --- /dev/null +++ b/symbols/teletext.xpm @@ -0,0 +1,23 @@ +/* XPM */ +static char * teletext_xpm[] = { +"27 18 2 1", +". c #FFFFFF", +"+ c #000000", +"...........................", +"...........................", +"..+++++++++++++++++++++++..", +"..+++++++++++++++++++++++..", +"..++...................++..", +"..++..+++++++++++++++..++..", +"..++..+++++++++++++++..++..", +"..++...................++..", +"..++..+++++++++++++++..++..", +"..++..+++++++++++++++..++..", +"..++...................++..", +"..++..+++++++++++++++..++..", +"..++..+++++++++++++++..++..", +"..++...................++..", +"..+++++++++++++++++++++++..", +"..+++++++++++++++++++++++..", +"...........................", +"..........................."}; diff --git a/symbols/volume.xpm b/symbols/volume.xpm new file mode 100644 index 00000000..9cbc0b25 --- /dev/null +++ b/symbols/volume.xpm @@ -0,0 +1,19 @@ +/* XPM */ +static char * volume_xpm[] = { +"28 14 2 1", +". c #FFFFFF", +"+ c #000000", +"..........................++", +"........................++++", +"......................++++++", +"....................++++++++", +"..................++++++++++", +"................++++++++++++", +"..............++++++++++++++", +"............++++++++++++++++", +"..........++++++++++++++++++", +"........++++++++++++++++++++", +"......++++++++++++++++++++++", +"....++++++++++++++++++++++++", +"..++++++++++++++++++++++++++", +"++++++++++++++++++++++++++++"}; diff --git a/themes.c b/themes.c new file mode 100644 index 00000000..0232822e --- /dev/null +++ b/themes.c @@ -0,0 +1,305 @@ +/* + * themes.c: Color themes used by skins + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: themes.c 1.1 2004/05/16 09:43:14 kls Exp $ + */ + +#include "themes.h" +#include +#include +#include "config.h" +#include "tools.h" + +// --- cTheme ---------------------------------------------------------------- + +cTheme::cTheme(void) +{ + name = strdup("default"); + memset(descriptions, 0, sizeof(descriptions)); + memset(colorNames, 0, sizeof(colorNames)); + memset(colorValues, 0, sizeof(colorValues)); + descriptions[0] = strdup("Default"); +} + +cTheme::~cTheme() +{ + free(name); + for (int i = 0; i < I18nNumLanguages; i++) + free(descriptions[i]); + for (int i = 0; i < MaxThemeColors; i++) + free(colorNames[i]); +} + +bool cTheme::FileNameOk(const char *FileName, bool SetName) +{ + const char *error = NULL; + if (!isempty(FileName)) { + const char *d = strrchr(FileName, '/'); + if (d) + FileName = d + 1; + const char *n = strchr(FileName, '-'); + if (n) { + if (n > FileName) { + if (!strchr(++n, '-')) { + const char *e = strchr(n, '.'); + if (e && strcmp(e, ".theme") == 0) { + if (e - n >= 1) { + // FileName is ok + if (SetName) { + free(name); + name = strndup(n, e - n); + } + } + else + error = "missing theme name"; + } + else + error = "invalid extension"; + } + else + error = "too many '-'"; + } + else + error = "missing skin name"; + } + else + error = "missing '-'"; + } + else + error = "empty"; + if (error) + esyslog("ERROR: invalid theme file name (%s): '%s'", error, FileName); + return !error; +} + +const char *cTheme::Description(void) +{ + char *s = descriptions[Setup.OSDLanguage]; + if (!s) + s = descriptions[0]; + return s ? s : name; +} + +bool cTheme::Load(const char *FileName, bool OnlyDescriptions) +{ + if (!FileNameOk(FileName, true)) + return false; + bool result = false; + if (!OnlyDescriptions) + isyslog("loading %s", FileName); + FILE *f = fopen(FileName, "r"); + if (f) { + int line = 0; + result = true; + char *s; + const char *error = NULL; + while ((s = readline(f)) != NULL) { + line++; + char *p = strchr(s, '#'); + if (p) + *p = 0; + s = stripspace(skipspace(s)); + if (!isempty(s)) { + char *n = s; + char *v = strchr(s, '='); + if (v) { + *v++ = 0; + n = stripspace(skipspace(n)); + v = stripspace(skipspace(v)); + if (strstr(n, "Description") == n) { + int lang = 0; + char *l = strchr(n, '.'); + if (l) + lang = I18nLanguageIndex(++l); + if (lang >= 0) + descriptions[lang] = strdup(v); + else + error = "invalid language code"; + } + else if (!OnlyDescriptions) { + for (int i = 0; i < MaxThemeColors; i++) { + if (colorNames[i]) { + if (strcmp(n, colorNames[i]) == 0) { + char *p = NULL; + errno = 0; + tColor c = strtoul(v, &p, 16); + if (!errno && !*p) + colorValues[i] = c; + else + error = "invalid color value"; + break; + } + } + else { + error = "unknown color name"; + break; + } + } + } + } + else + error = "missing value"; + } + if (error) { + result = false; + break; + } + } + if (!result) + esyslog("ERROR: error in %s, line %d%s%s\n", FileName, line, error ? ": " : "", error ? error : ""); + fclose(f); + } + else + LOG_ERROR_STR(FileName); + return result; +} + +bool cTheme::Save(const char *FileName) +{ + if (!FileNameOk(FileName)) + return false; + bool result = true; + cSafeFile f(FileName); + if (f.Open()) { + for (int i = 0; i < I18nNumLanguages; i++) { + if (descriptions[i]) + fprintf(f, "Description%s%.*s = %s\n", i ? "." : "", 3, i ? I18nLanguageCode(i) : "", descriptions[i]); + } + for (int i = 0; i < MaxThemeColors; i++) { + if (colorNames[i]) + fprintf(f, "%s = %08X\n", colorNames[i], colorValues[i]); + } + if (!f.Close()) + result = false; + } + else + result = false; + return result; +} + +int cTheme::AddColor(const char *Name, tColor Color) +{ + for (int i = 0; i < MaxThemeColors; i++) { + if (colorNames[i]) { + if (strcmp(Name, colorNames[i]) == 0) { + colorValues[i] = Color; + return i; + } + } + else { + colorNames[i] = strdup(Name); + colorValues[i] = Color; + return i; + } + } + return -1; +} + +tColor cTheme::Color(int Subject) +{ + return (Subject >= 0 && Subject < MaxThemeColors) ? colorValues[Subject] : 0; +} + +// --- cThemes --------------------------------------------------------------- + +char *cThemes::themesDirectory = NULL; + +cThemes::cThemes(void) +{ + numThemes = 0; + names = 0; + fileNames = NULL; + descriptions = NULL; +} + +cThemes::~cThemes() +{ + Clear(); +} + +void cThemes::Clear(void) +{ + for (int i = 0; i < numThemes; i++) { + free(names[i]); + free(fileNames[i]); + free(descriptions[i]); + } + free(names); + free(fileNames); + free(descriptions); + numThemes = 0; + names = 0; + fileNames = NULL; + descriptions = NULL; +} + +bool cThemes::Load(const char *SkinName) +{ + Clear(); + if (themesDirectory) { + DIR *d = opendir(themesDirectory); + if (d) { + struct dirent *e; + while ((e = readdir(d)) != NULL) { + if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) { + if (strstr(e->d_name, SkinName) == e->d_name && e->d_name[strlen(SkinName)] == '-') { + const char *FileName = AddDirectory(themesDirectory, e->d_name); + cTheme Theme; + if (Theme.Load(FileName, true)) { + names = (char **)realloc(names, (numThemes + 1) * sizeof(char *)); + names[numThemes] = strdup(Theme.Name()); + fileNames = (char **)realloc(fileNames, (numThemes + 1) * sizeof(char *)); + fileNames[numThemes] = strdup(FileName); + descriptions = (char **)realloc(descriptions, (numThemes + 1) * sizeof(char *)); + descriptions[numThemes] = strdup(Theme.Description()); + numThemes++; + } + } + } + } + closedir(d); + return numThemes > 0; + } + } + return false; +} + +int cThemes::GetThemeIndex(const char *Description) +{ + int index = 0; + for (int i = 0; i < numThemes; i++) { + if (strcmp(descriptions[i], Description) == 0) + return i; + if (strcmp(descriptions[i], "Default") == 0) + index = 1; + } + return index; +} + +void cThemes::SetThemesDirectory(const char *ThemesDirectory) +{ + free(themesDirectory); + themesDirectory = strdup(ThemesDirectory); + MakeDirs(themesDirectory, true); +} + +void cThemes::Load(const char *SkinName, const char *ThemeName, cTheme *Theme) +{ + char *FileName = NULL; + asprintf(&FileName, "%s/%s-%s.theme", themesDirectory, SkinName, ThemeName); + if (access(FileName, F_OK) == 0) // the file exists + Theme->Load(FileName); + free(FileName); +} + +void cThemes::Save(const char *SkinName, cTheme *Theme) +{ + char *FileName = NULL; + asprintf(&FileName, "%s/%s-%s.theme", themesDirectory, SkinName, Theme->Name()); + if (access(FileName, F_OK) != 0) // the file does not exist + Theme->Save(FileName); + free(FileName); +} diff --git a/themes.h b/themes.h new file mode 100644 index 00000000..617f1d2e --- /dev/null +++ b/themes.h @@ -0,0 +1,82 @@ +/* + * themes.h: Color themes used by skins + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: themes.h 1.1 2004/05/15 14:22:16 kls Exp $ + */ + +#ifndef __THEMES_H +#define __THEMES_H + +#include "i18n.h" +#include "osd.h" + +class cTheme { +public: + enum { MaxThemeColors = 128 }; +private: + char *name; + char *descriptions[I18nNumLanguages]; + char *colorNames[MaxThemeColors]; + tColor colorValues[MaxThemeColors]; + bool FileNameOk(const char *FileName, bool SetName = false); +public: + cTheme(void); + ///< Creates a new theme class. + ~cTheme(); + const char *Name(void) { return name; } + const char *Description(void); + ///< Returns a user visible, single line description of this theme. + ///< The actual text shouldn't be too long, so that it can be + ///< fully displayed in the Setup/OSD menu. + bool Load(const char *FileName, bool OnlyDescriptions = false); + ///< Loads the theme data from the given file. + bool Save(const char *FileName); + ///< Saves the theme data to the given file. + ///< FileName must be in the form "-.theme", where + ///< is the name of the skin this theme applies to, and is the + ///< actual theme name, which will be used to identify this theme in the + ///< 'setup.conf', and is normally not seen by the user. It should + ///< consist of only lowercase letters and digits. + int AddColor(const char *Name, tColor Color); + ///< Adds a color with the given Name to this theme, initializes it + ///< with Color and returns an index into the color array that can + ///< be used in a call to Color() later. The index returned from the + ///< first call to AddColor() is 0, and subsequent calls will return + ///< values that are incremented by 1 with every call. + ///< If a color entry with the given Name already exists, its value + ///< will be overwritten with Color and the returned index will be + ///< that of the existing entry. + tColor Color(int Subject); + ///< Returns the color for the given Subject. Subject must be one of + ///< the values returned by a previous call to AddColor(). + }; + +// A helper macro that simplifies defining theme colors. +#define THEME_CLR(Theme, Subject, Color) static const int Subject = Theme.AddColor(#Subject, Color) + +class cThemes { +private: + int numThemes; + char **names; + char **fileNames; + char **descriptions; + static char *themesDirectory; + void Clear(void); +public: + cThemes(void); + ~cThemes(); + bool Load(const char *SkinName); + int NumThemes(void) { return numThemes; } + const char *Name(int Index) { return Index < numThemes ? names[Index] : NULL; } + const char *FileName(int Index) { return Index < numThemes ? fileNames[Index] : NULL; } + const char * const *Descriptions(void) { return descriptions; } + int GetThemeIndex(const char *Description); + static void SetThemesDirectory(const char *ThemesDirectory); + static void Load(const char *SkinName, const char *ThemeName, cTheme *Theme); + static void Save(const char *SkinName, cTheme *Theme); + }; + +#endif //__THEMES_H diff --git a/vdr.5 b/vdr.5 index e54c9f8e..a224a667 100644 --- a/vdr.5 +++ b/vdr.5 @@ -8,7 +8,7 @@ .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.5 1.26 2004/02/24 12:36:35 kls Exp $ +.\" $Id: vdr.5 1.27 2004/05/15 09:24:57 kls Exp $ .\" .TH vdr 5 "1 Jun 2003" "1.2.0" "Video Disk Recorder Files" .SH NAME @@ -505,6 +505,48 @@ Examples: The file \fIsetup.conf\fR contains the basic configuration options for \fBvdr\fR. Each line contains one option in the format "Name = Value". See the MANUAL file for a description of the available options. +.SS THEMES +The files \fIthemes/-.theme\fR in the config directory contain the +color theme definitions for the various skins. In the actual file names \fI\fR +will be replaced by the name if the skin this theme belongs to, and \fI\fR +will be the name of this theme. +Each line in a theme file contains one option in the format "Name = Value". +Anything after (and including) a '#' character is comment. + +The definitions in a theme file are either \fBcolors\fR or a \fBdescription\fR. +.br +\fBColors\fR are in the form + +\fBclrTitle = FF123456\fR + +where the name (clrTitle) is one of the names defined in the source code of +the \fBskin\fR that uses this theme, through the \fBTHEME_CLR()\fR macro. +The value (FF123456) is an eight digit hex number that consist of four bytes, +representing alpha (transparency), red, green and blue component of the color. +An alpha value of 00 means the color will be completely transparent, while FF +means it will be opaque. An RGB value of 000000 results in black, while FFFFFF +is white. + +A \fBdescription\fR can be given as + +\fBDescription = Shades of blue\fR + +and will be used in the Setup/OSD menu to select a theme for a given skin. +The description should give the user an idea what this theme will be like +(for instance, in the given example it would use various shades of blue), +and shouldn't be too long to make sure it fits on the Setup screen. +The default description always should be given in English. If you want, +you can provide language specific descriptions as + +\fBDescription.eng = Shades of blue\fR +.br +\fBDescription.ger = Blautöne\fR + +where the language code (as defined in VDR/i18n.c) is added to the keyword +"Description", separated by a dot. You can enter as many language specific +descriptions as there are languages defined in VDR/i18n.h. +If a theme file doesn't contain a Description, the name of the theme (as +given in the theme's file name) will be used. .SS AUDIO/VIDEO DATA The files \fI001.vdr\fR...\fI255.vdr\fR are the actual recorded MPEG data files. In order to keep the size of an individual file below a given limit, diff --git a/vdr.c b/vdr.c index af95f377..20820549 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * The project's page is at http://www.cadsoft.de/vdr * - * $Id: vdr.c 1.180 2004/03/14 14:25:02 kls Exp $ + * $Id: vdr.c 1.181 2004/05/16 10:12:43 kls Exp $ */ #include @@ -45,11 +45,14 @@ #include "keys.h" #include "lirc.h" #include "menu.h" -#include "osd.h" +#include "osdbase.h" #include "plugin.h" #include "rcu.h" #include "recording.h" +#include "skinclassic.h" +#include "skinsttng.h" #include "sources.h" +#include "themes.h" #include "timers.h" #include "tools.h" #include "transfer.h" @@ -310,7 +313,6 @@ int main(int argc, char *argv[]) // Daemon mode: if (DaemonMode) { -#if !defined(DEBUG_OSD) pid_t pid = fork(); if (pid < 0) { fprintf(stderr, "%m\n"); @@ -322,10 +324,6 @@ int main(int argc, char *argv[]) fclose(stdin); fclose(stdout); fclose(stderr); -#else - fprintf(stderr, "vdr: can't run in daemon mode with DEBUG_OSD on!\n"); - return 2; -#endif } else if (Terminal) { // Claim new controlling terminal @@ -348,6 +346,7 @@ int main(int argc, char *argv[]) ConfigDirectory = VideoDirectory; cPlugin::SetConfigDirectory(ConfigDirectory); + cThemes::SetThemesDirectory(AddDirectory(ConfigDirectory, "themes")); Setup.Load(AddDirectory(ConfigDirectory, "setup.conf")); if (!(Sources.Load(AddDirectory(ConfigDirectory, "sources.conf"), true, true) && @@ -416,10 +415,6 @@ int main(int argc, char *argv[]) } } - // OSD: - - cOsd::Initialize(); - // User interface: Interface = new cInterface(SVDRPport); @@ -429,6 +424,13 @@ int main(int argc, char *argv[]) if (!PluginManager.StartPlugins()) return 2; + // Skins: + + new cSkinClassic; + new cSkinSTTNG; + Skins.SetCurrent(Setup.OSDSkin); + cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme()); + // Remote Controls: #if defined(REMOTE_RCU) new cRcuRemote("/dev/ttyS1"); @@ -586,7 +588,7 @@ int main(int argc, char *argv[]) } } // CAM control: - if (!Menu && !Interface->IsOpen()) + if (!Menu && !cOsd::IsOpen()) Menu = CamControl(); // User Input: cOsdObject *Interact = Menu ? Menu : cControl::Control(); @@ -646,7 +648,7 @@ int main(int argc, char *argv[]) } else cDevice::PrimaryDevice()->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA); - if (!Menu && !Interface->IsOpen()) + if (!Menu && !cOsd::IsOpen()) Menu = Temp = cDisplayVolume::Create(); cDisplayVolume::Process(key); key = kNone; // nobody else needs to see these keys @@ -657,7 +659,7 @@ int main(int argc, char *argv[]) DELETENULL(Menu); Temp = NULL; if (!cRecordControls::PauseLiveVideo()) - Interface->Error(tr("No free DVB device to record!")); + Skins.Message(mtError, tr("No free DVB device to record!")); key = kNone; // nobody else needs to see this key } break; @@ -665,9 +667,9 @@ int main(int argc, char *argv[]) case kRecord: if (!cControl::Control()) { if (cRecordControls::Start()) - ;//XXX Interface->Info(tr("Recording")); + ;//XXX Skins.Message(mtInfo, tr("Recording")); else - Interface->Error(tr("No free DVB device to record!")); + Skins.Message(mtError, tr("No free DVB device to record!")); key = kNone; // nobody else needs to see this key } break; @@ -677,7 +679,7 @@ int main(int argc, char *argv[]) cControl::Shutdown(); Temp = NULL; if (!Shutdown) { - Interface->Error(tr("Can't shutdown - option '-s' not given!")); + Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!")); break; } if (cRecordControls::Active()) { @@ -699,14 +701,14 @@ int main(int argc, char *argv[]) cControl::Shutdown(); // just in case Temp = NULL; if (!cRecordControls::PauseLiveVideo()) - Interface->Error(tr("No free DVB device to record!")); + Skins.Message(mtError, tr("No free DVB device to record!")); break; case osRecord: DELETENULL(Menu); Temp = NULL; if (cRecordControls::Start()) - ;//XXX Interface->Info(tr("Recording")); + ;//XXX Skins.Message(mtInfo, tr("Recording")); else - Interface->Error(tr("No free DVB device to record!")); + Skins.Message(mtError, tr("No free DVB device to record!")); break; case osRecordings: DELETENULL(Menu); @@ -728,7 +730,7 @@ int main(int argc, char *argv[]) DELETENULL(Menu); cControl::Shutdown(); Temp = NULL; - Interface->Info(tr("Switching primary DVB...")); + Skins.Message(mtInfo, tr("Switching primary DVB...")); cDevice::SetPrimaryDevice(Setup.PrimaryDVB); break; case osPlugin: DELETENULL(Menu); @@ -789,9 +791,9 @@ int main(int argc, char *argv[]) EITScanner.Process(); if (!cCutter::Active() && cCutter::Ended()) { if (cCutter::Error()) - Interface->Error(tr("Editing process failed!")); + Skins.Message(mtError, tr("Editing process failed!")); else - Interface->Info(tr("Editing process finished")); + Skins.Message(mtInfo, tr("Editing process finished")); } } if (!Interact && ((!cRecordControls::Active() && !cCutter::Active() && (!Interface->HasSVDRPConnection() || UserShutdown)) || ForceShutdown)) { @@ -862,9 +864,10 @@ int main(int argc, char *argv[]) delete Menu; cControl::Shutdown(); delete Interface; - cOsd::Shutdown(); + cOsdProvider::Shutdown(); Remotes.Clear(); Audios.Clear(); + Skins.Clear(); Setup.CurrentChannel = cDevice::CurrentChannel(); Setup.CurrentVolume = cDevice::CurrentVolume(); Setup.Save();