diff options
author | Andreas Mair <amair.sob@googlemail.com> | 2007-02-14 12:24:25 +0100 |
---|---|---|
committer | Andreas Mair <amair.sob@googlemail.com> | 2007-02-14 12:24:25 +0100 |
commit | 2ced4d35c533969fdb0ee6fa963a2fef26c50b79 (patch) | |
tree | b49facd42724fda2ad7c294479373d8f0f08a527 /enigma.c | |
download | vdr-plugin-skinenigmang-2ced4d35c533969fdb0ee6fa963a2fef26c50b79.tar.gz vdr-plugin-skinenigmang-2ced4d35c533969fdb0ee6fa963a2fef26c50b79.tar.bz2 |
2007-02-14: Version 0.0.1v0.0.1
- Initial release.
Diffstat (limited to 'enigma.c')
-rw-r--r-- | enigma.c | 2573 |
1 files changed, 2573 insertions, 0 deletions
diff --git a/enigma.c b/enigma.c new file mode 100644 index 0000000..ee977f7 --- /dev/null +++ b/enigma.c @@ -0,0 +1,2573 @@ +/* + * enigma.c: The 'EnigmaNG' VDR skin + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include <math.h> +#include <ctype.h> +#include <sstream> +#include <iomanip> + +#ifndef __STL_CONFIG_H +#define __STL_CONFIG_H +#endif + +#include "common.h" +#include "tools.h" +#include "config.h" +#include "logo.h" +#include "i18n.h" +#include "enigma.h" +#include "status.h" +#include <vdr/device.h> +#include <vdr/timers.h> +#include <vdr/menu.h> +#include <vdr/font.h> +#include <vdr/osd.h> +#include <vdr/themes.h> +#include <vdr/plugin.h> + +#ifdef HAVE_EPGSEARCH +#include "../epgsearch/services.h" +#endif + +using namespace std; + +#define MAX_AUDIO_BITMAPS 3 +#define MAX_SPEED_BITMAPS 10 +#define MAX_TRICKSPEED_BITMAPS 4 + + +#include "symbols/small/eventparttimer.xpm" +#include "symbols/small/eventtimer.xpm" +#include "symbols/small/eventvps.xpm" +#include "symbols/small/eventrunning.xpm" +#include "symbols/small/eventrecording.xpm" +#include "symbols/small/recordingnew.xpm" + +// small symbols (e.g. for channelinfo) +#include "symbols/small/audio.xpm" +#include "symbols/small/audioleft.xpm" +#include "symbols/small/audioright.xpm" +#include "symbols/small/dolbydigital.xpm" +#include "symbols/small/encrypted.xpm" +#include "symbols/small/teletext.xpm" +#include "symbols/small/vps.xpm" +#include "symbols/small/radio.xpm" +#include "symbols/small/recording.xpm" +#include "symbols/small/timer.xpm" +#include "symbols/small/run.xpm" + +static cBitmap bmEventPartTimer(eventparttimer_xpm); +static cBitmap bmEventTimer(eventtimer_xpm); +static cBitmap bmEventVPS(eventvps_xpm); +static cBitmap bmEventRunning(eventrunning_xpm); +static cBitmap bmEventRecording(eventrecording_xpm); +static cBitmap bmRecordingNew(recordingnew_xpm); + +// channel info +static cBitmap bmAudio[MAX_AUDIO_BITMAPS] = { + cBitmap(audio_xpm), + cBitmap(audioleft_xpm), + cBitmap(audioright_xpm) +}; +static cBitmap bmDolbyDigital(dolbydigital_xpm); +static cBitmap bmEncrypted(encrypted_xpm); +static cBitmap bmRadio(radio_xpm); +static cBitmap bmRecording(recording_xpm); +static cBitmap bmTeletext(teletext_xpm); +static cBitmap bmVPS(vps_xpm); +static cBitmap bmRun(run_xpm); +static cBitmap bmTimer(timer_xpm); + +// audio menu +static const char *strAudio_large[MAX_AUDIO_BITMAPS] = { + "icons/audio/audioStereo", + "icons/audio/audioLeft", + "icons/audio/audioRight" +}; + +// replay +static const char *strSlowForward_large[MAX_TRICKSPEED_BITMAPS] = { + "icons/replay/slowForward", + "icons/replay/slowForward1", + "icons/replay/slowForward2", + "icons/replay/slowForward3" +}; +static const char *strFastForward_large[MAX_SPEED_BITMAPS] = { + "icons/replay/fastForward", + "icons/replay/fastForward1", + "icons/replay/fastForward2", + "icons/replay/fastForward3", + "icons/replay/fastForward4", + "icons/replay/fastForward5", + "icons/replay/fastForward6", + "icons/replay/fastForward7", + "icons/replay/fastForward8", + "icons/replay/fastForward9" +}; +static const char *strSlowRewind_large[MAX_TRICKSPEED_BITMAPS] = { + "icons/replay/slowRewind", + "icons/replay/slowRewind1", + "icons/replay/slowRewind2", + "icons/replay/slowRewind3" +}; +static const char *strFastRewind_large[MAX_SPEED_BITMAPS] = { + "icons/replay/fastRewind", + "icons/replay/fastRewind1", + "icons/replay/fastRewind2", + "icons/replay/fastRewind3", + "icons/replay/fastRewind4", + "icons/replay/fastRewind5", + "icons/replay/fastRewind6", + "icons/replay/fastRewind7", + "icons/replay/fastRewind8", + "icons/replay/fastRewind9" +}; + +static cTheme Theme; + +// Background colors +THEME_CLR(Theme, clrBackground, 0xE5DEE5FA); +THEME_CLR(Theme, clrAltBackground, 0xE5B2BBD6); +THEME_CLR(Theme, clrTitleBg, 0xE54158BC); +THEME_CLR(Theme, clrLogoBg, 0xE58EA4E3); +THEME_CLR(Theme, clrBottomBg, 0xE51D2F7D); +THEME_CLR(Theme, clrBotProgBarBg, 0xFF808080); +THEME_CLR(Theme, clrBotProgBarFg, 0xFFFFFFFF); +// Text colors +THEME_CLR(Theme, clrMenuTxtFg, 0xFF000000); +THEME_CLR(Theme, clrTitleFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrTitleShadow, 0xFF000000); +// Symbols +THEME_CLR(Theme, clrSymbolActive, 0xFFC4C400); +THEME_CLR(Theme, clrSymbolInactive, 0xFF808080); +THEME_CLR(Theme, clrSymbolRecord, 0xFFC40000); +THEME_CLR(Theme, clrSymbolTimerActive, 0xFF0000C4); +THEME_CLR(Theme, clrSymbolVpsActive, 0xFFC4C400); +THEME_CLR(Theme, clrSymbolRecActive, 0xFFC40000); +THEME_CLR(Theme, clrSymbolRunActive, 0xFF00C400); +// Help buttons +THEME_CLR(Theme, clrButtonRedFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrButtonRedBg, 0xE5C40000); +THEME_CLR(Theme, clrButtonGreenFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrButtonGreenBg, 0xE500C400); +THEME_CLR(Theme, clrButtonYellowFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrButtonYellowBg, 0xE5C4C400); +THEME_CLR(Theme, clrButtonBlueFg, 0xFFFFFFFF); +THEME_CLR(Theme, clrButtonBlueBg, 0xE50000C4); +// Messages +THEME_CLR(Theme, clrMessageBorder, 0xE5FF0000); +THEME_CLR(Theme, clrMessageStatusFg, 0xFF000000); +THEME_CLR(Theme, clrMessageStatusBg, 0xE5D7FFD1); +THEME_CLR(Theme, clrMessageInfoFg, 0xFF000000); +THEME_CLR(Theme, clrMessageInfoBg, 0xE5D1DDFF); +THEME_CLR(Theme, clrMessageWarningFg, 0xFF000000); +THEME_CLR(Theme, clrMessageWarningBg, 0xE5F9FFD1); +THEME_CLR(Theme, clrMessageErrorFg, 0xFF000000); +THEME_CLR(Theme, clrMessageErrorBg, 0xE5FFD1D1); +// Volume bar +THEME_CLR(Theme, clrVolumeBar, 0xFF33CC33); +THEME_CLR(Theme, clrVolumeBarMute, 0xFFFF0000); +// Menu list items +THEME_CLR(Theme, clrMenuItemCurrentFg, 0xFF000000); +THEME_CLR(Theme, clrMenuHighlight, 0xE5B2BBD6); +THEME_CLR(Theme, clrMenuItemSelectableFg, 0xFF000000); +THEME_CLR(Theme, clrMenuItemNotSelectableFg, 0xE54158BC); +// title/subtitle +THEME_CLR(Theme, clrMenuEventTitle, 0xFF000000); +THEME_CLR(Theme, clrMenuEventShortText, 0xFF1D2F7D); +// Scrollbar +THEME_CLR(Theme, clrMenuScrollbarTotal, 0xE5B2BBD6); +THEME_CLR(Theme, clrMenuScrollbarShown, 0xFF000000); +// Replay OSD +THEME_CLR(Theme, clrReplayCurrent, 0xFF1D2F7D); +THEME_CLR(Theme, clrReplayTotal, 0xFF1D2F7D); +THEME_CLR(Theme, clrReplayModeJump, 0xFF1D2F7D); +THEME_CLR(Theme, clrReplayBarAreaBg, 0xE5DEE5FA); +THEME_CLR(Theme, clrReplayProgressSeen, 0xFF8EA4E3); +THEME_CLR(Theme, clrReplayProgressRest, 0xE5DEE5FA); +THEME_CLR(Theme, clrReplayProgressSelected, 0xFF4158BC); +THEME_CLR(Theme, clrReplayProgressMark, 0xFF4158BC); +THEME_CLR(Theme, clrReplayProgressCurrent, 0xFFFF0000); + +#define TinyGap 1 +#define SmallGap 2 +#define Gap 4 +#define BigGap 8 +#define Roundness 10 +#define MessageHeight (2 * SmallGap + cFont::GetFont(fontOsd)->Height() + 2 * SmallGap) + +#define TitleDecoGap SmallGap +#define TitleDecoGap2 SmallGap +#define TitleDecoHeight SmallGap +#define TitleDeco (TitleDecoGap + TitleDecoHeight + TitleDecoGap2) +#define LogoDecoGap SmallGap +#define LogoDecoGap2 Gap +#define LogoDecoWidth SmallGap +#define MarkerGap 6 + +/* LogoHeight must be maximum of + * - the height needed for titlebar, event title & subtitle (see cSkinEnigmaDisplayMenu::SetEvent() & cSkinEnigmaDisplayMenu::SetRecording()) + * - the height of the message OSD + */ +#define LogoHeight max(cFont::GetFont(fontOsd)->Height() * 3 + TitleDeco + SmallGap, cFont::GetFont(fontOsd)->Height() * 2 + TitleDeco + SmallGap * 2 + MessageHeight) +#define LogoWidth LogoHeight + +// --- cSkinEnigmaDisplayChannel -------------------------------------------- + +class cSkinEnigmaDisplayChannel:public cSkinDisplayChannel { +private: + cOsd *osd; + bool fShowLogo; + + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom, xLogoDecoLeft, xLogoDecoRight, xLogoPos, yLogoPos; + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xEventNowLeft, xEventNowRight, yEventNowTop, yEventNowBottom; + int xEventNextLeft, xEventNextRight, yEventNextTop, yEventNextBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + int xMessageLeft, xMessageRight, yMessageTop, yMessageBottom; + + int lineHeightSml; + int lineHeightOsd; + + bool HasChannelTimerRecording(const cChannel *Channel); + void DrawAreas(void); + void DrawGroupInfo(const cChannel *Channel, int Number); + void DrawChannelInfo(const cChannel *Channel, int Number); + cString GetChannelName(const cChannel *Channel); + cString GetChannelNumber(const cChannel *Channel, int Number); +public: + cSkinEnigmaDisplayChannel(bool WithInfo); + virtual ~ cSkinEnigmaDisplayChannel(); + virtual void SetChannel(const cChannel *Channel, int Number); + virtual void SetEvents(const cEvent *Present, const cEvent *Following); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayChannel::cSkinEnigmaDisplayChannel(bool WithInfo) +{ + fShowLogo = EnigmaConfig.showLogo; + + lineHeightSml = cFont::GetFont(fontSml)->Height(); + lineHeightOsd = cFont::GetFont(fontOsd)->Height(); + + int LogoSize = max(lineHeightSml * 2 + lineHeightOsd * 2 + SmallGap, LogoHeight); + // title bar & logo area + xLogoLeft = 0; + xLogoRight = xLogoLeft + LogoSize; + xLogoDecoLeft = xLogoRight + LogoDecoGap; + xLogoDecoRight = xLogoDecoLeft + LogoDecoWidth; + xTitleLeft = (fShowLogo ? xLogoDecoRight + LogoDecoGap2 : 0); + xTitleRight = xTitleLeft + ((Setup.OSDWidth - xTitleLeft) & ~0x07); // width must be multiple of 8 + yTitleTop = 0; + yTitleBottom = yTitleTop + lineHeightOsd; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + yLogoTop = yTitleDecoBottom + TitleDecoGap2; + yLogoBottom = yLogoTop + LogoSize; + xLogoPos = xLogoLeft + (LogoSize - LogoHeight) / 2; + yLogoPos = yLogoTop + (LogoSize - LogoHeight) / 2; + // current event area + xEventNowLeft = xTitleLeft; + xEventNowRight = xTitleRight; + yEventNowTop = yLogoTop; + yEventNowBottom = yEventNowTop + lineHeightOsd + lineHeightSml; + // next event area + xEventNextLeft = xEventNowLeft; + xEventNextRight = xEventNowRight; + yEventNextTop = yEventNowBottom + SmallGap; + yEventNextBottom = yEventNextTop + lineHeightOsd + lineHeightSml; + // progress bar area + xBottomLeft = xTitleLeft; + xBottomRight = xTitleRight; + yBottomTop = yEventNextBottom + SmallGap; + yBottomBottom = yBottomTop + lineHeightOsd; + // message area + xMessageLeft = xTitleLeft; + xMessageRight = xTitleRight; + yMessageTop = yLogoTop + (LogoSize - 2 * SmallGap - lineHeightSml - 2 * SmallGap) / 2; + yMessageBottom = yMessageTop + MessageHeight; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {0, 0, xBottomRight - 1, yBottomBottom - 1, fShowLogo ? 8 : 4} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea) == oeOk)) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + } else { + if (fShowLogo) { + tArea Areas[] = { {xLogoLeft, yLogoTop, xLogoDecoRight - 1, yLogoBottom - 1, 4}, + {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom - 1, 2}, + {xEventNowLeft, yEventNowTop, xEventNowRight - 1, yEventNextBottom - 1, 4}, + {xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, 4} + }; + eOsdError rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayChannel: CanHandleAreas() returned %d\n", rc); + } else { + tArea Areas[] = { {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom - 1, 2}, + {xEventNowLeft, yEventNowTop, xEventNowRight - 1, yEventNextBottom - 1, 4}, + {xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, 4} + }; + eOsdError rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayChannel: CanHandleAreas() returned %d\n", rc); + } + } +} + +cSkinEnigmaDisplayChannel::~cSkinEnigmaDisplayChannel() +{ + delete osd; +} + +bool cSkinEnigmaDisplayChannel::HasChannelTimerRecording(const cChannel *Channel) +{ + // try to find current channel from timers + for (cTimer * t = Timers.First(); t; t = Timers.Next(t)) { + if ((t->Channel() == Channel) && t->Recording()) + return true; + } + return false; +} + +void cSkinEnigmaDisplayChannel::DrawAreas(void) +{ + // draw titlebar + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, + Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, + yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + // draw rounded left corner of title bar + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, + yTitleTop + Roundness - 1, clrTransparent, -2); + // draw rounded right corner of title bar + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight - 1, + yTitleTop + Roundness - 1, clrTransparent, -1); + // draw current event area + osd->DrawRectangle(xEventNowLeft, yEventNowTop, xEventNowRight - 1, + yEventNowBottom - 1, Theme.Color(clrBackground)); + // draw next event area + osd->DrawRectangle(xEventNextLeft, yEventNextTop, xEventNextRight - 1, + yEventNextBottom - 1, Theme.Color(clrAltBackground)); + // draw progress bar area + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, + yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomLeft, yBottomBottom - Roundness, + xBottomLeft + Roundness, yBottomBottom - 1, clrTransparent, + -3); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, + xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); +} + +void cSkinEnigmaDisplayChannel::DrawGroupInfo(const cChannel *Channel, int Number) +{ + DrawAreas(); + + const cFont *font = cFont::GetFont(fontOsd); + int xName = xTitleLeft + Roundness + font->Width("0000-") + Gap; + // draw channel group name + osd->DrawText(xName + 3, yTitleTop + 3, GetChannelName(Channel), + Theme.Color(clrTitleShadow), clrTransparent, font, + xTitleRight - xTitleLeft - 3, lineHeightOsd - 3); + osd->DrawText(xName, yTitleTop, GetChannelName(Channel), + Theme.Color(clrTitleFg), clrTransparent, font, + xTitleRight - xTitleLeft, lineHeightOsd); +} + +void cSkinEnigmaDisplayChannel::DrawChannelInfo(const cChannel *Channel, int Number) +{ + DrawAreas(); + + const cFont *font = cFont::GetFont(fontOsd); + int xNumber = xTitleLeft + Roundness; + int xName = xNumber + font->Width("0000-") + Gap; + + // draw channel number + osd->DrawText(xNumber + 3, yTitleTop + 3, GetChannelNumber(Channel, Number), + Theme.Color(clrTitleShadow), clrTransparent, font, + xName - xNumber - Gap - 3, yTitleBottom - yTitleTop - 3); + osd->DrawText(xNumber, yTitleTop, GetChannelNumber(Channel, Number), + Theme.Color(clrTitleFg), clrTransparent, font, + xName - xNumber - Gap, yTitleBottom - yTitleTop); + // draw channel name + osd->DrawText(xName + 3, yTitleTop + 3, GetChannelName(Channel), + Theme.Color(clrTitleShadow), clrTransparent, font, + xTitleRight - Roundness - xName - 3, + yTitleBottom - yTitleTop - 3); + osd->DrawText(xName, yTitleTop, GetChannelName(Channel), + Theme.Color(clrTitleFg), clrTransparent, font, + xTitleRight - Roundness - xName, yTitleBottom - yTitleTop); + // draw symbols + // right edge of logo + int xs = xBottomRight - Roundness; + // bottom edge of logo + int ys = yBottomBottom - SmallGap; + bool isvps = false; + if (EnigmaConfig.showVps) { + // check if vps + // get schedule + cSchedulesLock SchedulesLock; + const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock); + if (Schedules) { + const cSchedule *Schedule = Schedules->GetSchedule(Channel); + if (Schedule) { + // get present event + const cEvent *Event = Schedule->GetPresentEvent(); + // check if present event has vps + if (Event && Event->Vps()) { + isvps = true; + } + } + } + } + char *strFile = NULL; + asprintf(&strFile, "flags/%s", Channel->Alang(0)); + if (EnigmaLogoCache.Load(strFile)) { + xs -= (EnigmaLogoCache.Get().Width() + SmallGap); + osd->DrawBitmap(xs, ys - EnigmaLogoCache.Get().Height(), EnigmaLogoCache.Get()); + } + free(strFile); + // draw audio symbol according to current audio channel + int AudioMode = cDevice::PrimaryDevice()->GetAudioChannel(); + if (!(AudioMode >= 0 && AudioMode < MAX_AUDIO_BITMAPS)) + AudioMode = 0; + xs -= (bmAudio[AudioMode].Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmAudio[AudioMode].Height(), bmAudio[AudioMode], + Theme.Color(clrBottomBg), Theme.Color(Channel->Apid(1) ? clrSymbolActive : clrSymbolInactive)); + // draw dolby digital symbol + xs -= (bmDolbyDigital.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmDolbyDigital.Height(), bmDolbyDigital, + Theme.Color(clrBottomBg), Theme.Color(Channel->Dpid(0) ? clrSymbolActive : clrSymbolInactive)); + if (EnigmaConfig.showVps) { + // draw vps symbol + xs -= (bmVPS.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmVPS.Height(), bmVPS, + Theme.Color(clrBottomBg), Theme.Color(isvps ? clrSymbolActive : clrSymbolInactive)); + } + // draw radio symbol if no video PID; otherwise draw teletext symbol + if (Channel->Vpid()) { + xs -= (bmTeletext.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmTeletext.Height(), bmTeletext, + Theme.Color(clrBottomBg), Theme.Color(Channel->Tpid() ? clrSymbolActive : clrSymbolInactive)); + } else { + xs -= (bmRadio.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmRadio.Height(), bmRadio, + Theme.Color(clrBottomBg), Theme.Color(Channel->Apid(0) ? clrSymbolActive : clrSymbolInactive)); + } + // draw encryption symbol + xs -= (bmEncrypted.Width() + SmallGap); + osd->DrawBitmap(xs, ys - bmEncrypted.Height(), bmEncrypted, + Theme.Color(clrBottomBg), Theme.Color(Channel->Ca() ? clrSymbolActive : clrSymbolInactive)); + // draw recording symbol + xs -= (bmRecording.Width() + SmallGap); + if (cRecordControls::Active()) + osd->DrawBitmap(xs, ys - bmRecording.Height(), bmRecording, + Theme.Color(clrBottomBg), + Theme.Color(HasChannelTimerRecording(Channel) ? clrSymbolRecord : clrSymbolActive)); + else + osd->DrawBitmap(xs, ys - bmRecording.Height(), bmRecording, + Theme.Color(clrBottomBg), Theme.Color(clrSymbolInactive)); +} + +cString cSkinEnigmaDisplayChannel::GetChannelName(const cChannel *Channel) +{ + char buffer[256]; + // check if channel exists + if (Channel) { + snprintf(buffer, sizeof(buffer), "%s", Channel->Name()); + } else { + snprintf(buffer, sizeof(buffer), "%s", tr("*** Invalid Channel ***")); + } + return buffer; +} + +cString cSkinEnigmaDisplayChannel::GetChannelNumber(const cChannel *Channel, int Number) +{ + char buffer[256]; + // check if real channel exists + if (Channel && !Channel->GroupSep()) { + snprintf(buffer, sizeof(buffer), "%d%s", Channel->Number(), Number ? "-" : ""); + } else if (Number) { + // no channel but number + snprintf(buffer, sizeof(buffer), "%d-", Number); + } else { + // no channel and no number + snprintf(buffer, sizeof(buffer), " "); + } + return buffer; +} + +void cSkinEnigmaDisplayChannel::SetChannel(const cChannel *Channel, int Number) +{ + if (Channel) { + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + + if (fShowLogo) { + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + osd->DrawRectangle(xLogoDecoLeft, yLogoTop, xLogoDecoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + const char *logoname = EnigmaConfig.useChannelId ? *Channel->GetChannelID().ToString() : Channel->Name(); + char *filename = (char *)malloc(strlen(logoname) + 20 /* should be enough for folder */); + if (filename == NULL) return; + strcpy(filename, "hqlogos/"); + strcat(filename, logoname); + bool fFoundLogo = false; + if (!(fFoundLogo = EnigmaLogoCache.Load(filename))) { + strcpy(filename, "logos/"); + strcat(filename, logoname); + if (!(fFoundLogo = EnigmaLogoCache.Load(filename))) { + fFoundLogo = EnigmaLogoCache.Load("hqlogos/no_logo"); //TODO? different default logo for channel/group? + } + } + free(filename); + + if (fFoundLogo) { + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - ChannelLogoWidth) / 2, + yLogoTop + (yLogoBottom - yLogoTop - ChannelLogoHeight) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg), true); + } + } + + if (Channel->GroupSep()) + DrawGroupInfo(Channel, Number); + else + DrawChannelInfo(Channel, Number); + } +} + +void cSkinEnigmaDisplayChannel::SetEvents(const cEvent *Present, + const cEvent *Following) +{ + const cFont *lFontOsd = cFont::GetFont(fontOsd); + const cFont *lFontSml = cFont::GetFont(fontSml); + + int xTimeLeft = xEventNowLeft + Gap; + int xTimeWidth = lFontOsd->Width("00:00"); + int xDurationLeft = xEventNowRight - Gap - lFontOsd->Width("000'"); + int xDurationWidth = xEventNowRight - Gap - xDurationLeft; + int xTextLeft = xTimeLeft + xTimeWidth + BigGap; + int xTextWidth = xDurationLeft - xTextLeft - BigGap; + + // check epg datas + const cEvent *e = Present; // Current event + if (e) { + char sLen[4]; + int total = e->Duration(); + snprintf(sLen, sizeof(sLen), "%d'", total / 60); + // draw start time + osd->DrawText(xTimeLeft, yEventNowTop, e->GetTimeString(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground), + lFontOsd, xTimeWidth, lineHeightOsd); + // draw title + osd->DrawText(xTextLeft, yEventNowTop, e->Title(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground), + lFontOsd, xTextWidth, lineHeightOsd); + // draw duration + osd->DrawText(xDurationLeft, yEventNowTop, sLen, + Theme.Color(clrMenuEventTitle), Theme.Color(clrBackground), + lFontOsd, xDurationWidth, lineHeightOsd, taRight); + if (e->HasTimer()) { + if (e->IsRunning()) + osd->DrawBitmap(xTimeLeft + (xTimeWidth - bmRecording.Width()) / 2, + yEventNowTop + lineHeightOsd, bmRecording, + Theme.Color(clrSymbolRecActive), + Theme.Color(clrBackground)); + else + osd->DrawBitmap(xTimeLeft + (xTimeWidth - bmTimer.Width()) / 2, + yEventNowTop + lineHeightOsd, bmTimer, + Theme.Color(clrSymbolTimerActive), + Theme.Color(clrBackground)); + } + // draw shorttext + osd->DrawText(xTextLeft, yEventNowTop + lineHeightOsd, e->ShortText(), + Theme.Color(clrMenuEventShortText), + Theme.Color(clrBackground), lFontSml, xTextWidth, lineHeightSml); + int now = (time(NULL) - e->StartTime()); + if ((now < total) && ((now / 60) > 0)) { + char sNow[5]; + if (EnigmaConfig.showRemaining) { + snprintf(sNow, sizeof(sNow), "-%d'", (total - now) / 60); + } else { + snprintf(sNow, sizeof(sNow), "+%d'", now / 60); + } + osd->DrawText(xDurationLeft, yEventNowTop + lineHeightOsd, sNow, + Theme.Color(clrMenuEventShortText), + Theme.Color(clrBackground), lFontSml, xDurationWidth, + lineHeightSml, taRight); + } + // draw timebar + int xBarWidth = 124; + int xBarLeft = xBottomLeft + Roundness; + int x = xBarLeft + SmallGap + (int)(roundf((float)(now) / (float)(total) * (float)(xBarWidth - Gap - SmallGap))); + x = min(x, xBottomLeft + Gap + xBarWidth - SmallGap - 1); + osd->DrawRectangle(xBarLeft, yBottomTop + SmallGap + SmallGap, + xBottomLeft + Gap + xBarWidth - 1, + yBottomBottom - SmallGap - 1, + Theme.Color(clrBotProgBarBg)); + osd->DrawRectangle(xBarLeft + SmallGap, + yBottomTop + SmallGap + SmallGap + SmallGap, x, + yBottomBottom - SmallGap - SmallGap - 1, + Theme.Color(clrBotProgBarFg)); + } + + e = Following; // Next event + if (e) { + char sLen[5]; + snprintf(sLen, 5, "%d'", e->Duration() / 60); + // draw start time + osd->DrawText(xTimeLeft, yEventNextTop, e->GetTimeString(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrAltBackground), + lFontOsd, xTimeWidth, lineHeightOsd); + // draw title + osd->DrawText(xTextLeft, yEventNextTop, e->Title(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrAltBackground), + lFontOsd, xTextWidth, lineHeightOsd); + // draw duration + osd->DrawText(xDurationLeft, yEventNextTop, sLen, + Theme.Color(clrMenuEventTitle), Theme.Color(clrAltBackground), + lFontOsd, xDurationWidth, lineHeightOsd, taRight); + if (e->HasTimer()) + osd->DrawBitmap(xTimeLeft + (xTimeWidth - bmTimer.Width()) / 2, + yEventNextTop + lineHeightOsd, bmTimer, + Theme.Color(clrSymbolTimerActive), Theme.Color(clrAltBackground)); + // draw shorttext + osd->DrawText(xTextLeft, yEventNextTop + lineHeightOsd, e->ShortText(), + Theme.Color(clrMenuEventShortText), + Theme.Color(clrAltBackground), lFontSml, xTextWidth, lineHeightSml); + } +} + +void cSkinEnigmaDisplayChannel::SetMessage(eMessageType Type, const char *Text) +{ + // check if message + if (Text) { + // save osd region + osd->SaveRegion(xMessageLeft, yMessageTop, xMessageRight - 1, yMessageBottom - 1); + // draw message + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, + yMessageBottom - 1, clrTransparent); + osd->DrawRectangle(xMessageLeft, yMessageTop + SmallGap, xMessageRight - 1, + yMessageBottom - SmallGap - 1, + Theme.Color(clrMessageBorder)); + osd->DrawText(xMessageLeft + Gap, yMessageTop + 2 * SmallGap, Text, + Theme.Color(clrMessageStatusFg + 2 * Type), + Theme.Color(clrMessageStatusBg + 2 * Type), + cFont::GetFont(fontOsd), + xMessageRight - Gap - xMessageLeft - Gap - 1, + yMessageBottom - 2 * SmallGap - yMessageTop - 2 * SmallGap, + taCenter); + } else { + // restore saved osd region + osd->RestoreRegion(); + } +} + +void cSkinEnigmaDisplayChannel::Flush(void) +{ + // update date string + cString date = DayDateTime(); + const cFont *font = cFont::GetFont(fontOsd); + int w = font->Width(date); + osd->DrawText(xTitleRight - Roundness - w, yTitleTop, date, + Theme.Color(clrTitleFg), Theme.Color(clrTitleBg), + font, w, yTitleBottom - yTitleTop, taCenter); + osd->Flush(); +} + +// --- cSkinEnigmaDisplayMenu ----------------------------------------------- + +class cSkinEnigmaDisplayMenu:public cSkinDisplayMenu { +private: + cOsd *osd; + + char *strTitle; + bool isMainMenu; + bool fShowLogo; + bool fShowInfo; + + int xBodyLeft, xBodyRight, yBodyTop, yBodyBottom; + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xButtonsLeft, xButtonsRight, yButtonsTop, yButtonsBottom; + int xMessageLeft, xMessageRight, yMessageTop, yMessageBottom; + int xDateLeft, xDateRight, yDateTop, yDateBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom; + int xInfoLeft, xInfoRight, yInfoTop, yInfoBottom; + + int lineHeightOsd; + int lineHeightSml; + + void SetScrollbar(void); + void SetupAreas(void); + void DrawTitle(const char *Title); + int DrawFlag(int x, int y, const tComponent *p); + const char *GetPluginMainMenuName(const char *plugin); +public: + cSkinEnigmaDisplayMenu(); + virtual ~cSkinEnigmaDisplayMenu(); + 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 int GetTextAreaWidth(void) const; + virtual const cFont *GetTextAreaFont(bool FixedFont) const; + virtual void Flush(void); +}; + +cSkinEnigmaDisplayMenu::cSkinEnigmaDisplayMenu(void) +{ + osd = NULL; + strTitle = NULL; + isMainMenu = true; +#ifdef NO_MENULOGO + fShowLogo = false; +#else + fShowLogo = EnigmaConfig.showSymbols; +#endif + fShowInfo = EnigmaConfig.showInfo; + + lineHeightSml = cFont::GetFont(fontSml)->Height(); + lineHeightOsd = cFont::GetFont(fontOsd)->Height(); + + int RightColWidth = (144 + LogoWidth) & ~0x07; // must be multiple of 8 + + // title bar + xTitleLeft = 0; + xTitleRight = Setup.OSDWidth - RightColWidth; + yTitleTop = 0; + yTitleBottom = lineHeightOsd; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + // help buttons + xButtonsLeft = xTitleLeft; + xButtonsRight = Setup.OSDWidth; + yButtonsTop = Setup.OSDHeight - lineHeightOsd; + yButtonsBottom = Setup.OSDHeight; + // content area with items + xBodyLeft = xTitleLeft; + xBodyRight = xTitleRight; + yBodyTop = yTitleDecoBottom + TitleDecoGap2; + yBodyBottom = yButtonsTop - SmallGap; + // message area + xMessageLeft = xBodyLeft; + xMessageRight = Setup.OSDWidth; + yMessageBottom = yButtonsTop - SmallGap; + yMessageTop = yMessageBottom - MessageHeight + SmallGap; // No extra SmallGap at bottom as there's already the Gap above the buttons + // logo box + xLogoLeft = Setup.OSDWidth - LogoWidth; + xLogoRight = Setup.OSDWidth; + yLogoTop = yTitleTop; + yLogoBottom = yLogoTop + LogoHeight + SmallGap; + // info box + xInfoLeft = Setup.OSDWidth - RightColWidth; + xInfoRight = Setup.OSDWidth; + yInfoTop = yLogoBottom; + yInfoBottom = yBodyBottom; + // date box + xDateLeft = xTitleRight; + xDateRight = Setup.OSDWidth; + yDateTop = yTitleTop; + yDateBottom = yLogoBottom; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop); + + tArea Areas[] = { {xTitleLeft, yTitleTop, xMessageRight - 1, yButtonsBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { //TODO? single body area (-> no symbols in event/recording info) + tArea Areas[] = { {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom - 1, 4}, //title area + {xBodyLeft, yBodyTop, xBodyRight - 1, yLogoBottom - 1, 2}, //body area (beside date/logo area) +#ifdef NO_MENULOGO + {xDateLeft, yDateTop, xDateRight - 1, yDateBottom - 1, 2}, //date +#else + {xDateLeft, yDateTop, xDateRight - 1, yInfoTop - 1, 4}, //date/logo area +#endif + {xBodyLeft, yInfoTop, xInfoRight - 1, yInfoTop + lineHeightOsd - 1, 4}, //area for symbols in event/recording info + {xBodyLeft, yInfoTop + lineHeightOsd, xInfoRight - 1, yMessageTop - 1, 2}, //body area (below date/logo area) + {xMessageLeft, yMessageTop, xMessageRight - 1, yButtonsBottom - 1, 4} //message/buttons area + }; + + eOsdError rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else { + error("cSkinEnigmaDisplayMenu: CanHandleAreas() [1] returned %d\n", rc); + return; + } + +#ifndef NO_MENULOGO + // set colors for info area + osd->GetBitmap(4)->Reset(); + osd->GetBitmap(4)->SetColor(0, Theme.Color(clrTransparent)); + osd->GetBitmap(4)->SetColor(1, Theme.Color(clrBackground)); + osd->GetBitmap(4)->SetColor(2, Theme.Color(clrAltBackground)); +#endif + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + + SetupAreas(); +} + +void cSkinEnigmaDisplayMenu::SetupAreas(void) +{ + debug("cSkinEnigmaDisplayMenu::SetupAreas\n"); + + DrawTitle(strTitle); + + // draw date area + osd->DrawRectangle(xDateLeft, yDateTop, xDateRight - 1, yDateBottom - 1, + fShowLogo ? Theme.Color(clrLogoBg) : Theme.Color(clrBackground)); + + // draw info area + if (fShowInfo) { + osd->DrawRectangle(xInfoLeft, fShowLogo ? yInfoTop : yBodyTop, xInfoRight - 1, yInfoBottom - 1, Theme.Color(clrAltBackground)); + + const cFont *font = cFont::GetFont(fontOsd); + int h = font->Height(); + int x = xInfoLeft + Gap; + int y = fShowLogo ? yInfoTop : yBodyTop; + int w = xInfoRight - x; + int yMaxHeight = yInfoBottom; + +#ifdef HAVE_EPGSEARCH + cPlugin *p = cPluginManager::GetPlugin("epgsearch"); + if (p) { + Epgsearch_lastconflictinfo_v1_0 *serviceData = new Epgsearch_lastconflictinfo_v1_0; + if (serviceData) { + serviceData->nextConflict = 0; + serviceData->relevantConflicts = 0; + serviceData->totalConflicts = 0; + + p->Service("Epgsearch-lastconflictinfo-v1.0", serviceData); + if (serviceData->relevantConflicts > 0) { + int yWarning = yMaxHeight - 3 * h; // Needed space for warning + yMaxHeight = yWarning; + osd->DrawRectangle(xInfoLeft, yWarning, xInfoRight - 1, yWarning + SmallGap + 1, Theme.Color(clrBackground)); + yWarning += 1 + h / 2; + osd->DrawText(xInfoLeft, yWarning, tr("WARNING"), Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h, taCenter); + yWarning += h + SmallGap; + + char *info; + asprintf(&info, "%d %s", serviceData->relevantConflicts, (serviceData->relevantConflicts == 1 ? tr("Timer conflict") : tr("Timer conflicts"))); + osd->DrawText(x, yWarning, info, Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h, taCenter); + yWarning += h; + free(info); + } + delete serviceData; + } + } +#endif //HAVE_EPGSEARCH + + if (Timers.GetNextActiveTimer()) { + // Show next active timers + y += h / 2; + osd->DrawText(x, y, tr("TIMERS"), Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h, taCenter); + y += h + h / 2; + EnigmaStatus.UpdateActiveTimers(); + + for (tTimer *timer = EnigmaStatus.mTimers.First(); timer; timer = EnigmaStatus.mTimers.Next(timer)) { + if (y + 2 * h + SmallGap + 1 + h / 2 > yMaxHeight) + break; // don't overwrite warning or help buttons + + if (timer->isRecording) { + osd->DrawBitmap(x, y + (lineHeightOsd - bmRecording.Height()) / 2, bmRecording, Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground)); //TODO: draw red + } + osd->DrawText(x + (timer->isRecording ? (bmRecording.Width() + Gap) : 0), y, timer->title.c_str(), Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h); + y += h; + char* info = NULL; + asprintf(&info, "%d. %s / %s", timer->startDay, *TimeString(timer->startTime), timer->channelName.c_str()); + osd->DrawText(x, y, info, Theme.Color(clrMenuItemSelectableFg), Theme.Color(clrAltBackground), font, w, h); + free(info); + y += h; + osd->DrawRectangle(x, y + SmallGap, xInfoRight - Gap - 1, y + SmallGap + 1, Theme.Color(clrMenuItemSelectableFg)); + y += SmallGap + 1 + h / 2; + } + } + + } else { // !fShowInfo + osd->DrawRectangle(xInfoLeft, fShowLogo ? yInfoTop : yBodyTop, xInfoRight - 1, yInfoBottom - 1, Theme.Color(clrBackground)); + } +} + +cSkinEnigmaDisplayMenu::~cSkinEnigmaDisplayMenu() +{ + free(strTitle); + delete osd; +} + +void cSkinEnigmaDisplayMenu::SetScrollbar(void) +{ + // check if scrollbar is needed + if (textScroller.CanScroll()) { + int h = lineHeightOsd; + int w = lineHeightOsd; + 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(); + int xl = textScroller.Width() + SmallGap; + // arrow up + osd->DrawRectangle(xl, yt, xl + w, yt + SmallGap, + textScroller.CanScrollUp() ? Theme.Color(clrMenuScrollbarShown) : Theme.Color(clrAltBackground)); + osd->DrawRectangle(xl + w - SmallGap, yt + SmallGap, xl + w, yt + h, + textScroller.CanScrollUp() ? Theme.Color(clrMenuScrollbarShown) : Theme.Color(clrAltBackground)); + // draw background of scrollbar + osd->DrawRectangle(xl + w - SmallGap, st, xl + w, sb, Theme.Color(clrMenuScrollbarTotal)); + // draw visible area of scrollbar + osd->DrawRectangle(xl + w - SmallGap, tt, xl + w, tb, Theme.Color(clrMenuScrollbarShown)); + // arrow down + osd->DrawRectangle(xl + w - SmallGap, yb - h, xl + w, yb - SmallGap, + textScroller.CanScrollDown() ? Theme.Color(clrMenuScrollbarShown) : Theme.Color(clrAltBackground)); + osd->DrawRectangle(xl, yb - SmallGap, xl + w, yb, + textScroller.CanScrollDown() ? Theme.Color(clrMenuScrollbarShown) : Theme.Color(clrAltBackground)); + } +} + +void cSkinEnigmaDisplayMenu::Scroll(bool Up, bool Page) +{ + cSkinDisplayMenu::Scroll(Up, Page); + SetScrollbar(); +} + +int cSkinEnigmaDisplayMenu::MaxItems(void) +{ + // max number of items + return (yBodyBottom - yBodyTop) / lineHeightOsd; +} + +void cSkinEnigmaDisplayMenu::Clear(void) +{ + debug("cSkinEnigmaDisplayMenu::Clear\n"); + textScroller.Reset(); + // clear items area + osd->DrawRectangle(xBodyLeft, yBodyTop, (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - 1, yBodyBottom - 1, Theme.Color(clrBackground)); +} + +void cSkinEnigmaDisplayMenu::SetTitle(const char *Title) +{ + debug("cSkinEnigmaDisplayMenu::SetTitle(%s)\n", Title); + + if (Title && strTitle) { + if (strcmp(Title, strTitle) != 0) { + free(strTitle); + strTitle = strdup(Title); + } + } else { + free(strTitle); + if (Title) + strTitle = strdup(Title); + else + strTitle = NULL; + } + + if (!(isMainMenu && Title && strncmp(tr("VDR"), Title, strlen(tr("VDR"))) == 0)) { + bool old_isMainMenu = isMainMenu; + bool old_fShowLogo = fShowLogo; + bool old_fShowInfo = fShowInfo; + + if (strTitle == NULL || strncmp(tr("VDR"), strTitle, strlen(tr("VDR"))) == 0) { + isMainMenu = true; +#ifdef NO_MENULOGO + fShowLogo = false; +#else + fShowLogo = EnigmaConfig.showSymbols; +#endif + fShowInfo = EnigmaConfig.showInfo; + } else { + isMainMenu = false; + fShowLogo = false; + fShowInfo = false; + } + + if (old_isMainMenu != isMainMenu + || old_fShowLogo != fShowLogo + || old_fShowInfo != fShowInfo) { + SetupAreas(); + } else { + DrawTitle(Title); + } + } else { + DrawTitle(Title); + } +} + +void cSkinEnigmaDisplayMenu::DrawTitle(const char *Title) +{ + // draw titlebar + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + // draw rounded left corner of title bar + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, yTitleTop + Roundness - 1, clrTransparent, -2); + +// debug("DrawTitle(%s)\n", Title); + if (Title) { + // draw title with shadow + osd->DrawText(xTitleLeft + Roundness + 3, yTitleTop + 3, Title, + Theme.Color(clrTitleShadow), clrTransparent, + cFont::GetFont(fontOsd), + xTitleRight - xTitleLeft - Roundness - 3, + yTitleBottom - yTitleTop - 3); + osd->DrawText(xTitleLeft + Roundness, yTitleTop, Title, + Theme.Color(clrTitleFg), clrTransparent, + cFont::GetFont(fontOsd), + xTitleRight - xTitleLeft - Roundness, + yTitleBottom - yTitleTop); + } +} + +void cSkinEnigmaDisplayMenu::SetButtons(const char *Red, const char *Green, const char *Yellow, const char *Blue) +{ + debug("cSkinEnigmaDisplayMenu::SetButtons(%s, %s, %s, %s)\n", Red, Green, Yellow, Blue); + const cFont *font = cFont::GetFont(fontOsd); + int w = (xButtonsRight - xButtonsLeft) / 4; + int t3 = xButtonsLeft + xButtonsRight - xButtonsLeft - w; + int t2 = t3 - w; + int t1 = t2 - w; + int t0 = xButtonsLeft; + // clear background + osd->DrawRectangle(xButtonsLeft, yButtonsTop, xButtonsRight, yButtonsBottom, clrTransparent); + // draw color buttons + if (Red) + osd->DrawText(t0, yButtonsTop, Red, Theme.Color(clrButtonRedFg), + Theme.Color(clrButtonRedBg), font, t1 - t0 + 1, + yButtonsBottom - yButtonsTop, taCenter); + else { + osd->DrawRectangle(t0, yButtonsTop, Green ? t1 - 1 : t1, yButtonsBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(t0, yButtonsBottom - Roundness, t0 + Roundness - 1, yButtonsBottom - 1, clrTransparent, -3); + } + if (Green) + osd->DrawText(t1, yButtonsTop, Green, Theme.Color(clrButtonGreenFg), + Theme.Color(clrButtonGreenBg), font, w, + yButtonsBottom - yButtonsTop, taCenter); + else + osd->DrawRectangle(t1, yButtonsTop, Yellow ? t1 + w : t2 - 1, yButtonsBottom - 1, Theme.Color(clrBottomBg)); + + if (Yellow) + osd->DrawText(t2, yButtonsTop, Yellow, Theme.Color(clrButtonYellowFg), + Theme.Color(clrButtonYellowBg), font, w, + yButtonsBottom - yButtonsTop, taCenter); + else + osd->DrawRectangle(t2, yButtonsTop, Blue ? t2 + w : t3 - 1, yButtonsBottom - 1, Theme.Color(clrBottomBg)); + if (Blue) + osd->DrawText(t3, yButtonsTop, Blue, Theme.Color(clrButtonBlueFg), + Theme.Color(clrButtonBlueBg), font, w, + yButtonsBottom - yButtonsTop, taCenter); + else { + osd->DrawRectangle(t3, yButtonsTop, xButtonsRight, yButtonsBottom, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xButtonsRight - Roundness, yButtonsBottom - Roundness, + xButtonsRight - 1, yButtonsBottom - 1, clrTransparent, -4); + } +} + +void cSkinEnigmaDisplayMenu::SetMessage(eMessageType Type, const char *Text) +{ + // check if message + if (Text) { + // save osd region + osd->SaveRegion(xMessageLeft, yMessageTop, xMessageRight - 1, yMessageBottom - 1); + // draw message + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, + yMessageBottom - 1, clrTransparent); + osd->DrawRectangle(xMessageLeft, yMessageTop + SmallGap, xMessageRight - 1, + yMessageBottom - 1, Theme.Color(clrMessageBorder)); + osd->DrawText(xMessageLeft + Gap, yMessageTop + 2 * SmallGap, Text, + Theme.Color(clrMessageStatusFg + 2 * Type), + Theme.Color(clrMessageStatusBg + 2 * Type), + cFont::GetFont(fontOsd), + xMessageRight - Gap - xMessageLeft - Gap - 1, + yMessageBottom - SmallGap - yMessageTop - 2 * SmallGap, + taCenter); + } else { + // restore saved osd region + osd->RestoreRegion(); + } +} + +void cSkinEnigmaDisplayMenu::SetItem(const char *Text, int Index, bool Current, bool Selectable) +{ + int xItemLeft = xBodyLeft + (EnigmaConfig.showMarker ? lineHeightOsd : SmallGap); + int xItemRight = (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - (EnigmaConfig.showMarker ? lineHeightOsd : SmallGap); + int numItems = (yBodyBottom - yBodyTop) / lineHeightOsd; + int top = yBodyTop + (yBodyBottom - yBodyTop - numItems * lineHeightOsd) / 2; +// debug("top=%d yBodyTop=%d yBodyBottom=%d lineHeightOsd=%d\n", top, yBodyTop, yBodyBottom, lineHeightOsd); + int y = top + Index * lineHeightOsd; + tColor ColorFg, ColorBg; + // select colors + if (Current) { + ColorFg = Theme.Color(clrMenuItemCurrentFg); + ColorBg = Theme.Color(clrMenuHighlight); + } else { + if (Selectable) { + ColorFg = Theme.Color(clrMenuItemSelectableFg); + ColorBg = Theme.Color(clrBackground); + } else { + ColorFg = Theme.Color(clrMenuItemNotSelectableFg); + ColorBg = Theme.Color(clrBackground); + } + } + const cFont *font = cFont::GetFont(fontOsd); + // this should prevent menu flickering + osd->DrawRectangle(xItemLeft, y + lineHeightOsd / 2, xItemLeft + 1, y + lineHeightOsd / 2 + 1, Theme.Color(clrBackground)); + if (EnigmaConfig.showMarker) { + osd->DrawRectangle(xBodyLeft, y, xItemLeft - 1, y + lineHeightOsd - 1, ColorBg); + osd->DrawEllipse(xBodyLeft + MarkerGap, y + MarkerGap, xBodyLeft + lineHeightOsd - MarkerGap, y + lineHeightOsd - MarkerGap, Current ? ColorFg : ColorBg); + osd->DrawRectangle(xItemRight, y, (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - 1, y + lineHeightOsd - 1, ColorBg); + } + // draw item + for (int i = 0; i < MaxTabs; i++) { + const char *s = GetTabbedText(Text, i); + if (s) { + char buffer[9]; + int xt = xItemLeft + Tab(i); + bool iseventinfo = false; + bool isnewrecording = false; + bool isprogressbar = false; + int now = 0, total = 0; + + // check if event info symbol: "tTV*" "R" + if (EnigmaConfig.showListSymbols) { + // check if event info characters + if (strlen(s) == 3 && strchr(" tTR", s[0]) + && strchr(" V", s[1]) && strchr(" *", s[2])) { + // update status + iseventinfo = true; + } + + // check if new recording: "01.01.06*", "10:10*" + if (!iseventinfo && + (strlen(s) == 6 && s[5] == '*' && s[2] == ':' && isdigit(*s) + && isdigit(*(s + 1)) && isdigit(*(s + 3)) && isdigit(*(s + 4))) + || (strlen(s) == 9 && s[8] == '*' && s[5] == '.' && s[2] == '.' + && isdigit(*s) && isdigit(*(s + 1)) && isdigit(*(s + 3)) + && isdigit(*(s + 4)) && isdigit(*(s + 6)) + && isdigit(*(s + 7)))) { + // update status + isnewrecording = true; + // make a copy + strncpy(buffer, s, strlen(s)); + // remove the '*' character + buffer[strlen(s) - 1] = '\0'; + } + } + + // check if progress bar: "[||||||| ]" + if (!iseventinfo && !isnewrecording && EnigmaConfig.showProgressbar && + (strlen(s) > 5 && s[0] == '[' && s[strlen(s) - 1] == ']')) { + const char *p = s + 1; + // update status + isprogressbar = true; + for (; *p != ']'; ++p) { + // check if progressbar characters + if (*p == ' ' || *p == '|') { + // update counters + ++total; + if (*p == '|') + ++now; + } else { + // wrong character detected; not a progressbar + isprogressbar = false; + break; + } + } + } + + if (iseventinfo) { + int evx = xt + Gap; + const char *p = s; + // draw background + osd->DrawRectangle(xt, y, xItemRight, y + lineHeightOsd - 1, ColorBg); + // draw symbols + for (; *p; ++p) { + switch (*p) { + case 't': + // partial timer event + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventPartTimer.Height()) / 2, bmEventPartTimer, ColorFg, ColorBg); + evx += bmEventPartTimer.Width(); + break; + case 'T': + // timer event + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventTimer.Height()) / 2, bmEventTimer, ColorFg, ColorBg); + evx += bmEventTimer.Width(); + break; + case 'R': + // recording event (epgsearch) + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventRecording.Height()) / 2, bmEventRecording, ColorFg, ColorBg); + evx += bmEventRecording.Width(); + break; + case 'V': + // vps event + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventVPS.Height()) / 2, bmEventVPS, ColorFg, ColorBg); + evx += bmEventVPS.Width(); + break; + case '*': + // running event + osd->DrawBitmap(evx, y + (lineHeightOsd - bmEventRunning.Height()) / 2, bmEventRunning, ColorFg, ColorBg); + evx += bmEventRunning.Width(); + break; + case ' ': + default: + // let's ignore space character + break; + } + } + } else if (isnewrecording) { + // draw text + osd->DrawText(xt, y, buffer, ColorFg, ColorBg, font, xItemRight - xt); + // draw symbol + osd->DrawBitmap(xt + font->Width(buffer), y + (lineHeightOsd - bmRecordingNew.Height()) / 2, bmRecordingNew, ColorFg, ColorBg); + } else if (isprogressbar) { + // define x coordinates of progressbar + int px0 = xt; + int px1 = (Selectable ? (Tab(i + 1) ? Tab(i + 1) : xItemRight) : xItemRight) - 1; + int px = px0 + (int) ((float) now * (float) (px1 - px0) / (float) total); + // define y coordinates of progressbar + int py0 = y + Gap; + int py1 = y + lineHeightOsd - Gap; + // draw background + osd->DrawRectangle(px0, y, px1, y + lineHeightOsd - 1, ColorBg); + // draw progressbar + osd->DrawRectangle(px0, py0, px, py1, ColorFg); + osd->DrawRectangle(px + 1, py0, px1, py0, ColorFg); + osd->DrawRectangle(px + 1, py1, px1, py1, ColorFg); + osd->DrawRectangle(px1, py0, px1, py1, ColorFg); + } else { + // draw text + osd->DrawText(xt, y, s, ColorFg, ColorBg, font, xItemRight - xt); + } + } + if (!Tab(i + 1)) + break; + } + //set editable width + SetEditableWidth(xItemRight - Tab(1)); + +#ifndef NO_MENULOGO +// debug("SetItem: (%s) %d %d %d\n", Text, Index, Current, Selectable); + if (Current && isMainMenu && fShowLogo && Text) { + char *ItemText, *ItemText2; + int n = strtoul(Text, &ItemText, 10); + if (n != 0) + ItemText2 = ItemText = skipspace(ItemText); + else + ItemText2 = skipspace(ItemText); + + //debug("SetItem: %d (%s) %d %d %d\n", n, ItemText, Index, Current, Selectable); + bool fFoundLogo = false; + if (strcmp(ItemText, tr("Schedule")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/schedule"); + else if (strcmp(ItemText, tr("Channels")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/channels"); + else if (strcmp(ItemText, tr("Timers")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("timerinfo")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/timers"); + else if (strcmp(ItemText, tr("Recordings")) == 0 + || strcmp(ItemText, tr("Recording info")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("extrecmenu")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/recordings"); + else if (strcmp(ItemText, tr("Setup")) == 0 + || strcmp(ItemText2, tr("Setup")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/setup"); + else if (strcmp(ItemText, tr("Commands")) == 0 + || strcmp(ItemText2, tr("Commands")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/commands"); + else if (strcmp(ItemText, tr(" Stop replaying")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/stop"); + else if (strcmp(ItemText, tr(" Cancel editing")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/cancel"); + else if (strcmp(ItemText, tr("Summary")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/summary"); + else if (strcmp(ItemText2, GetPluginMainMenuName("audiorecorder")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/audiorecorder"); + else if (strcmp(ItemText2, GetPluginMainMenuName("burn")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/burn"); + else if (strcmp(ItemText2, GetPluginMainMenuName("cdda")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/cdda"); + else if (strcmp(ItemText2, GetPluginMainMenuName("chanorg")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/chanorg"); + else if (strcmp(ItemText2, GetPluginMainMenuName("channelscan")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/channelscan"); + else if (strcmp(ItemText2, GetPluginMainMenuName("digicam")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/digicam"); + else if (strcmp(ItemText2, GetPluginMainMenuName("director")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/director"); + else if (strcmp(ItemText2, GetPluginMainMenuName("dvd")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/dvd"); + else if (strcmp(ItemText2, GetPluginMainMenuName("dvdselect")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("dvdswitch")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/dvdselect"); + else if (strcmp(ItemText2, GetPluginMainMenuName("dxr3")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("softdevice")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/device"); + else if (strcmp(ItemText2, GetPluginMainMenuName("epgsearch")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("epgsearchonly")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("conflictcheckonly")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("nordlichtsepg")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/epgsearch"); + else if (strcmp(ItemText2, GetPluginMainMenuName("externalplayer")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/externalplayer"); + else if (strcmp(ItemText2, GetPluginMainMenuName("femon")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/femon"); + else if (strcmp(ItemText2, GetPluginMainMenuName("filebrowser")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/filebrowser"); + else if (strcmp(ItemText2, GetPluginMainMenuName("fussball")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/fussball"); + else if (strcmp(ItemText2, GetPluginMainMenuName("games")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/games"); + else if (strcmp(ItemText2, GetPluginMainMenuName("image")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("osdimage")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/image"); + else if (strcmp(ItemText2, GetPluginMainMenuName("mp3")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("mp3ng")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("muggle")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/mp3"); + else if (strcmp(ItemText2, GetPluginMainMenuName("mplayer")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/mplayer"); + else if (strcmp(ItemText2, GetPluginMainMenuName("newsticker")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/newsticker"); + else if (strcmp(ItemText2, GetPluginMainMenuName("osdpip")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/osdpip"); + else if (strcmp(ItemText2, GetPluginMainMenuName("pin")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/pin"); + else if (strcmp(ItemText2, GetPluginMainMenuName("radio")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/radio"); + else if (strcmp(ItemText2, GetPluginMainMenuName("rotor")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/rotor"); + else if (strcmp(ItemText2, GetPluginMainMenuName("solitaire")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/solitaire"); + else if (strcmp(ItemText2, GetPluginMainMenuName("streamdev-client")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("streamdev-server")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/streamdev"); + else if (strcmp(ItemText2, GetPluginMainMenuName("sudoku")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/sudoku"); + else if (strcmp(ItemText2, GetPluginMainMenuName("teletext")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("osdteletext")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/teletext"); + else if (strcmp(ItemText2, GetPluginMainMenuName("tvonscreen")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/tvonscreen"); + else if (strcmp(ItemText2, GetPluginMainMenuName("vcd")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vcd"); + else if (strcmp(ItemText2, GetPluginMainMenuName("vdrc")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vdrc"); + else if (strcmp(ItemText2, GetPluginMainMenuName("vdrcd")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("mediad")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vdrcd"); + else if (strcmp(ItemText2, GetPluginMainMenuName("vdrrip")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vdrrip"); + else if (strcmp(ItemText2, GetPluginMainMenuName("weather")) == 0 + || strcmp(ItemText2, GetPluginMainMenuName("weatherng")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/weather"); + else if (strcmp(ItemText2, GetPluginMainMenuName("webepg")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/webepg"); + else if (strcmp(ItemText2, GetPluginMainMenuName("xineliboutput")) == 0) + fFoundLogo = EnigmaLogoCache.Load("icons/menu/xineliboutput"); + else + fFoundLogo = EnigmaLogoCache.Load("icons/menu/vdr"); + + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoTop - 1, Theme.Color(clrLogoBg)); + if (fFoundLogo) { + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg), false, true); + } + } +#endif +} + +const char *cSkinEnigmaDisplayMenu::GetPluginMainMenuName(const char *plugin) +{ + cPlugin *p = cPluginManager::GetPlugin(plugin); + if (p) { + const char *entry = p->MainMenuEntry(); + if (entry) + return entry; + } + return plugin; +} + +int cSkinEnigmaDisplayMenu::DrawFlag(int x, int y, const tComponent *p) +{ + if (p == NULL) + return 0; + + if ((p->stream == 2) && p->language) { + string flag("flags/"); + flag += p->language; + /*TODO + if (p->description) { + + } + */ + if (EnigmaLogoCache.Load(flag.c_str())) { + osd->DrawBitmap(x, y - EnigmaLogoCache.Get().Height(), EnigmaLogoCache.Get()); + return EnigmaLogoCache.Get().Width(); + } + } + + return 0; +} + +void cSkinEnigmaDisplayMenu::SetEvent(const cEvent *Event) +{ + // check if event + if (!Event) + return; + + isMainMenu = false; + fShowInfo = false; +#ifdef NO_MENULOGO + fShowLogo = false; +#else + fShowLogo = EnigmaConfig.showSymbols; +#endif + + const cFont *font = cFont::GetFont(fontOsd); + const cFont *smlfont = cFont::GetFont(fontSml); + int wsb = lineHeightOsd + 2 * SmallGap; //scrollbar width + + time_t now = time(NULL); + // draw recording date string + stringstream sstrDate; + sstrDate << *Event->GetDateString() + << " " << *Event->GetTimeString() + << " - " << *Event->GetEndTimeString() + << " ("; + if (now > Event->StartTime()) + sstrDate << (now - Event->StartTime()) / 60 << '/'; + sstrDate << Event->Duration() / 60 << tr("min") << ")"; + + int y = yDateBottom + (smlfont->Height() - bmVPS.Height()) / 2; + int xs = xDateRight - Gap; + // check if event has VPS + if (EnigmaConfig.showVps && Event->Vps()) { + // draw VPS symbol + if (Event->Vps() != Event->StartTime()) { + char vps[6]; + struct tm tm_r; + time_t t_vps = Event->Vps(); + strftime(vps, sizeof(vps), "%H:%M", localtime_r(&t_vps, &tm_r)); + xs -= smlfont->Width(vps); + osd->DrawText(xs, yDateBottom, vps, + Theme.Color(clrMenuTxtFg), Theme.Color(clrBackground), + smlfont, smlfont->Width(vps), smlfont->Height()); + xs -= TinyGap; + } + xs -= bmVPS.Width(); + osd->DrawBitmap(xs, y, bmVPS, Theme.Color(clrSymbolVpsActive), Theme.Color(clrSymbolInactive)); + xs -= Gap; + } + // check if event is running + if (Event->IsRunning()) { + // draw running symbol + xs -= (bmRun.Width() + Gap); + osd->DrawBitmap(xs, y, bmRun, Theme.Color(clrSymbolRunActive), Theme.Color(clrBackground)); + } + // check if event has timer + if (Event->HasTimer()) { + if (Event->IsRunning()) { + // draw recording symbol + xs -= (bmRecording.Width() + Gap); + osd->DrawBitmap(xs, y, bmRecording, Theme.Color(clrSymbolRecActive), Theme.Color(clrBackground)); + } else { + // draw timer symbol + xs -= (bmTimer.Width() + Gap); + osd->DrawBitmap(xs, y, bmTimer, Theme.Color(clrSymbolTimerActive), Theme.Color(clrBackground)); + } + } + string stringInfo; + const cComponents *Components = Event->Components(); + if (Components) { + stringstream sstrInfo; + for (int i = 0; i < Components->NumComponents(); i++) { + const tComponent *p = Components->Component(i); + if ((p->stream == 2) && p->language) { + if (p->description) { + sstrInfo << p->description + << " (" << p->language << "), "; + } else { + sstrInfo << p->language << ", "; + } +// DrawFlag(p); //TODO + } + } + // strip out the last delimiter + if (!sstrInfo.str().empty()) + stringInfo = tr("Languages"); + stringInfo += ": "; + stringInfo += sstrInfo.str().substr(0, sstrInfo.str().length() - 2); + } + int yHeadlineBottom = yDateBottom; + int xHeadlineRight = fShowLogo ? xDateLeft : xInfoRight; + // draw recording title + osd->DrawRectangle(xBodyLeft, yBodyTop, xHeadlineRight - 1, + yHeadlineBottom - 1, Theme.Color(clrAltBackground)); + int th = font->Height() + (Event->Description() && Event->ShortText() ? Gap + smlfont->Height() : 0); + y = yBodyTop + (yHeadlineBottom - yBodyTop - th) / 2; + osd->DrawText(xBodyLeft + Gap, y, Event->Title(), + Theme.Color(clrMenuEventTitle), Theme.Color(clrAltBackground), + font, xHeadlineRight - xBodyLeft - Gap - 1, font->Height()); + osd->DrawText(xBodyLeft + Gap, yHeadlineBottom, sstrDate.str().c_str(), + Theme.Color(clrMenuTxtFg), Theme.Color(clrBackground), + smlfont, xDateLeft - xBodyLeft - Gap - 1, smlfont->Height()); + + // draw recording short text and description + const char *strDescr = NULL; + if (isempty(Event->Description())) { + // check if short text + if (!isempty(Event->ShortText())) { + // draw short text as description, if no description available + strDescr = Event->ShortText(); + } + } else { + // check if short text + if (!isempty(Event->ShortText())) { + // draw below Event->Title + y += lineHeightOsd + Gap; + // draw short text + osd->DrawText(xBodyLeft + Gap, y, Event->ShortText(), + Theme.Color(clrMenuEventShortText), Theme.Color(clrAltBackground), + smlfont, xHeadlineRight - xBodyLeft - Gap - 1, lineHeightSml); + } + // draw description + strDescr = Event->Description(); + } + + string stringReruns; +#ifdef HAVE_EPGSEARCH + // try to find a rerun of the show using epgsearch-service + if (!isempty(Event->Title())) { + stringstream sstrReruns; + Epgsearch_searchresults_v1_0 data; + data.query = (char *)Event->Title(); + data.mode = 0; + data.channelNr = 0; + data.useTitle = true; + data.useSubTitle = false; + data.useDescription = false; + if (cPluginManager::CallFirstService("Epgsearch-searchresults-v1.0", &data)) { + cList<Epgsearch_searchresults_v1_0::cServiceSearchResult>* list = data.pResultList; + if (list) { + //TODO: current event is shown as rerun + sstrReruns << tr("RERUNS OF THIS SHOW") << ':' << endl; + int i = 0; + for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r && i < 5; r = list->Next(r)) { + i++; + sstrReruns << "- " + << *DayDateTime(r->event->StartTime()) + << " " << Channels.GetByChannelID(r->event->ChannelID())->ShortName(true) + << ": " << r->event->Title(); + if (!isempty(r->event->ShortText())) + sstrReruns << "~" << r->event->ShortText(); + sstrReruns << endl; + } + delete list; + } + } + stringReruns = sstrReruns.str(); + } +#endif // HAVE_EPGSEARCH + + const char *strFirst = NULL; + const char *strSecond = NULL; + const char *strThird = stringReruns.empty() ? NULL : stringReruns.c_str(); + if (EnigmaConfig.showAuxInfo) { + strFirst = strDescr; + strSecond = stringInfo.empty() ? NULL : stringInfo.c_str(); + } else { + strFirst = stringInfo.empty() ? NULL : stringInfo.c_str(); + strSecond = strDescr; + } + if (strFirst || strSecond || strSecond) { + y = yHeadlineBottom + SmallGap + 2 * smlfont->Height(); + char *mytext; + asprintf(&mytext, "%s%s%s%s%s", strFirst ? strFirst : "", + strSecond ? "\n\n" : "", strSecond ? strSecond : "", + (strFirst || strSecond) && strThird ? "\n\n" : "", strThird ? strThird : ""); + textScroller.Set(osd, xBodyLeft + Gap, y, + xInfoRight - xBodyLeft - wsb - Gap, yBodyBottom - Gap - y, + mytext, smlfont, Theme.Color(clrMenuTxtFg), + Theme.Color(clrBackground)); + SetScrollbar(); +// free(mytext); + } + +#ifndef NO_MENULOGO + if (fShowLogo) { + // draw logo + osd->DrawRectangle(xDateLeft + SmallGap, yDateTop, xDateRight - 1, yDateBottom - SmallGap - 1, Theme.Color(clrLogoBg)); + if (EnigmaLogoCache.Load("icons/menu/schedule")) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), Theme.Color(clrLogoBg)); + // draw borders + osd->DrawRectangle(xDateLeft, yDateTop, xDateLeft + SmallGap - 1, yDateBottom - 1, clrTransparent); + osd->DrawRectangle(xDateLeft, yLogoBottom - SmallGap, xDateRight, yLogoBottom - 1, clrTransparent); + } +#endif +} + +void cSkinEnigmaDisplayMenu::SetRecording(const cRecording *Recording) +{ + // check if recording + if (!Recording) + return; + + isMainMenu = false; + fShowInfo = false; +#ifdef NO_MENULOGO + fShowLogo = false; +#else + fShowLogo = EnigmaConfig.showSymbols; +#endif + + const cRecordingInfo *Info = Recording->Info(); + const cFont *font = cFont::GetFont(fontOsd); + const cFont *smlfont = cFont::GetFont(fontSml); + int wsb = lineHeightOsd + 2 * SmallGap; //scrollbar width + + // draw recording date string + stringstream sstrDate; + sstrDate << *DateString(Recording->start) + << " " << *TimeString(Recording->start); + + // draw additional information + stringstream sstrInfo; + int dirSize = DirSizeMB(Recording->FileName()); + cChannel *channel = Channels.GetByChannelID(((cRecordingInfo *)Info)->ChannelID()); + sstrInfo << tr("Channel") << ": " << (channel ? channel->Name() : "???") << endl + << tr("Size") << ": " << setprecision(3) << (dirSize > 1023 ? dirSize / 1024.0 : dirSize) << (dirSize > 1023 ? "GB\n" : "MB\n") + << tr("Priority") << ": " << Recording->priority << endl + << tr("Lifetime") << ": " << Recording->lifetime << endl; + if (Info->Aux()) { + sstrInfo << endl << tr("Auxiliary information") << ":\n" + << parseaux(Info->Aux()); + } + const cComponents *Components = Info->Components(); + if (Components) { + //TODO: show flaggs? + stringstream info; + for (int i = 0; i < Components->NumComponents(); i++) { + const tComponent *p = Components->Component(i); + if ((p->stream == 2) && p->language) { + if (p->description) { + info << p->description + << " (" << p->language << "), "; + } else { + info << p->language << ", "; + } + } + } + // strip out the last delimiter + if (!info.str().empty()) { + sstrInfo << tr("Languages") << ": " + << info.str().substr(0, info.str().length() - 2); + } + } + + int yHeadlineBottom = yDateBottom; + int xHeadlineRight = fShowLogo ? xDateLeft : xInfoRight; + // draw recording title + const char *Title = Info->Title(); + if (isempty(Title)) + Title = Recording->Name(); + osd->DrawRectangle(xBodyLeft, yBodyTop, xHeadlineRight - 1, yHeadlineBottom - 1, Theme.Color(clrAltBackground)); + int th = font->Height() + (Info->Description() && Info->ShortText() ? Gap + smlfont->Height() : 0); + int y = yBodyTop + (yHeadlineBottom - yBodyTop - th) / 2; + osd->DrawText(xBodyLeft + Gap, y, Title, + Theme.Color(clrMenuTxtFg), Theme.Color(clrAltBackground), + font, xHeadlineRight - xBodyLeft - Gap - 1, font->Height()); + osd->DrawText(xBodyLeft + Gap, yHeadlineBottom, sstrDate.str().c_str(), + Theme.Color(clrMenuTxtFg), Theme.Color(clrBackground), + smlfont, xHeadlineRight - xBodyLeft - Gap - 1, smlfont->Height()); + // draw recording short text and description + const char* strDescr = NULL; + if (isempty(Info->Description())) { + // check if short text + if (!isempty(Info->ShortText())) { + // draw short text as description, if no description available + strDescr = Info->ShortText(); + } + } else { + // check if short text + if (!isempty(Info->ShortText())) { + // draw below Title + y += lineHeightOsd + Gap; + // draw short text + osd->DrawText(xBodyLeft + Gap, y, Info->ShortText(), + Theme.Color(clrMenuEventShortText), Theme.Color(clrAltBackground), + smlfont, xHeadlineRight - xBodyLeft - Gap - 1, lineHeightSml); + } + // draw description + strDescr = Info->Description(); + } + + string stringInfo = sstrInfo.str(); + const char *strInfo = stringInfo.empty() ? NULL : stringInfo.c_str(); + if (strDescr || strInfo) { + y = yHeadlineBottom + 2 * smlfont->Height(); + char *mytext; + if (EnigmaConfig.showAuxInfo) + asprintf(&mytext, "%s%s%s", strDescr ? strDescr : "", strInfo && strDescr ? "\n\n" : "", strInfo ? strInfo : ""); + else + asprintf(&mytext, "%s%s%s", strInfo ? strInfo : "", strInfo && strDescr ? "\n\n" : "", strDescr ? strDescr : ""); + textScroller.Set(osd, xBodyLeft + Gap, y, + xInfoRight - xBodyLeft - Gap - wsb, + yBodyBottom - Gap - y, mytext, smlfont, + Theme.Color(clrMenuTxtFg), + Theme.Color(clrBackground)); + SetScrollbar(); + free(mytext); + } + +#ifndef NO_MENULOGO + if (fShowLogo) { + // draw logo + osd->DrawRectangle(xDateLeft + SmallGap, yDateTop, xDateRight - 1, yDateBottom - SmallGap - 1, Theme.Color(clrLogoBg)); + if (EnigmaLogoCache.Load("icons/menu/recordings")) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), Theme.Color(clrLogoBg)); + // draw borders + osd->DrawRectangle(xDateLeft, yDateTop, xDateLeft + SmallGap - 1, yDateBottom - 1, clrTransparent); + osd->DrawRectangle(xDateLeft, yLogoBottom - SmallGap, xDateRight, yLogoBottom - 1, clrTransparent); + } +#endif +} + +void cSkinEnigmaDisplayMenu::SetText(const char *Text, bool FixedFont) +{ + int wsb = lineHeightOsd + 2 * SmallGap; //scrollbar width + // draw text + textScroller.Set(osd, xBodyLeft + Gap, yBodyTop + Gap, + (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - 2 * Gap - wsb, + yBodyBottom - yBodyTop - 2 * Gap, Text, + GetTextAreaFont(FixedFont), + Theme.Color(clrMenuTxtFg), Theme.Color(clrBackground)); + SetScrollbar(); +} + +int cSkinEnigmaDisplayMenu::GetTextAreaWidth(void) const +{ + // max text area width + return (fShowLogo || fShowInfo ? xBodyRight : xInfoRight) - xBodyLeft; +} + +const cFont *cSkinEnigmaDisplayMenu::GetTextAreaFont(bool FixedFont) const +{ + // text area font + return cFont::GetFont(FixedFont ? fontFix : fontOsd); +} + +void cSkinEnigmaDisplayMenu::Flush(void) +{ +//debug("cSkinEnigmaDisplayMenu::Flush\n"); + if (fShowLogo) { + int x = xDateLeft + SmallGap; + int w = xLogoLeft - x; + const cFont *font = cFont::GetFont(fontOsd); + int ys = yDateTop + (yDateBottom - yDateTop - 3 * lineHeightOsd) / 2; + + char temp[32]; + struct tm tm_r; + time_t t = time(NULL); + tm *tm = localtime_r(&t, &tm_r); + + strftime(temp, sizeof(temp), "%A", tm); + osd->DrawText(x, ys, temp, Theme.Color(clrMenuTxtFg), + Theme.Color(clrLogoBg), font, w, + lineHeightOsd, taCenter); + ys += lineHeightOsd; + + strftime(temp, sizeof(temp), "%d.%m.%Y", tm); + osd->DrawText(x, ys, temp, Theme.Color(clrMenuTxtFg), + Theme.Color(clrLogoBg), font, w, + lineHeightOsd, taCenter); + ys += lineHeightOsd; + + cString time = TimeString(t); + osd->DrawText(x, ys, time, Theme.Color(clrMenuTxtFg), + Theme.Color(clrLogoBg), font, w, + lineHeightOsd, taCenter); + //draw borders + osd->DrawRectangle(xDateLeft, yDateTop, xDateLeft + SmallGap - 1, yLogoBottom - SmallGap - 1, clrTransparent); + osd->DrawRectangle(xDateLeft, yLogoBottom - SmallGap, xDateRight, yLogoBottom - 1, clrTransparent); + } else { + cString date = DayDateTime(); + const cFont *font = cFont::GetFont(fontOsd); + osd->DrawText(xDateLeft + SmallGap, yDateTop, date, Theme.Color(clrMenuTxtFg), + Theme.Color(clrLogoBg), font, xDateRight - xDateLeft - SmallGap, + yTitleDecoBottom - yDateTop, taCenter); + //draw borders + osd->DrawRectangle(xDateLeft, yDateTop, xDateLeft + SmallGap - 1, yTitleDecoBottom - 1, clrTransparent); + osd->DrawRectangle(xDateLeft, yTitleDecoBottom, xDateRight, yBodyTop - 1, clrTransparent); + } + osd->Flush(); +} + +// --- cSkinEnigmaDisplayReplay --------------------------------------------- + +class cSkinEnigmaDisplayReplay:public cSkinDisplayReplay { +private: + cOsd *osd; + + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom; + int xProgressLeft, xProgressRight, yProgressTop, yProgressBottom; + int xTimeLeft, xTimeRight, yTimeTop, yTimeBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + int xMessageLeft, xMessageRight, yMessageTop, yMessageBottom; + + int lineHeight; + bool modeonly; + int nJumpWidth; +public: + cSkinEnigmaDisplayReplay(bool ModeOnly); + virtual ~ cSkinEnigmaDisplayReplay(); + 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 SetMessage(eMessageType Type, const char *Text); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayReplay::cSkinEnigmaDisplayReplay(bool ModeOnly) +{ + modeonly = ModeOnly; + nJumpWidth = 0; + lineHeight = cFont::GetFont(fontOsd)->Height(); + + xTitleLeft = 0; + xTitleRight = Setup.OSDWidth; + yTitleTop = 0; + yTitleBottom = yTitleTop + lineHeight; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + xLogoLeft = xTitleLeft; + xLogoRight = xLogoLeft + LogoWidth; + yLogoTop = yTitleDecoBottom + TitleDecoGap2; + yLogoBottom = yLogoTop + LogoHeight; + xTimeLeft = xLogoRight + LogoDecoGap2; + xTimeRight = xTitleRight; + xTimeLeft += (xTimeRight - xTimeLeft) - ((xTimeRight - xTimeLeft) & ~0x07); + yTimeTop = yLogoBottom - SmallGap - lineHeight - SmallGap; + yTimeBottom = yLogoBottom; + xProgressLeft = xTimeLeft; + xProgressRight = xTitleRight; + yProgressTop = yLogoTop; + yProgressBottom = yTimeTop - SmallGap; + xBottomLeft = xTitleLeft; + xBottomRight = xTitleRight; + yBottomTop = yLogoBottom + SmallGap; + yBottomBottom = yBottomTop + lineHeight; + xMessageLeft = xProgressLeft; + xMessageRight = xProgressRight; + yMessageTop = yLogoTop + (LogoHeight - MessageHeight) / 2; + yMessageBottom = yMessageTop + MessageHeight; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {xTitleLeft, yTitleTop, xBottomRight - 1, yBottomBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { + tArea Areas[] = { {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom - 1, 2}, + {xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, 4}, + {xProgressLeft, yProgressTop, xTimeRight - 1, yTimeBottom - 1, 4}, + {xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, 2} + }; + int rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayReplay: CanHandleAreas() returned %d\n", rc); + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + if (modeonly) { + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, EnigmaConfig.showSymbols ? Theme.Color(clrLogoBg) : clrTransparent); + } else { + // draw title area + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, + yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, + yTitleTop + Roundness - 1, clrTransparent, -2); + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight, + yTitleTop + Roundness, clrTransparent, -1); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, + yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + // draw progress bar area + osd->DrawRectangle(xProgressLeft, yProgressTop, xProgressRight - 1, + yProgressBottom - 1, Theme.Color(clrReplayBarAreaBg)); + // draw time area + osd->DrawRectangle(xTimeLeft, yTimeTop, xTimeRight - 1, yTimeBottom - 1, Theme.Color(clrAltBackground)); + // draw bottom area + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, + yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomLeft, yBottomBottom - Roundness, + xBottomLeft + Roundness - 1, yBottomBottom - 1, + clrTransparent, -3); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, + xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); + } +} + +cSkinEnigmaDisplayReplay::~cSkinEnigmaDisplayReplay() +{ + delete osd; +} + +void cSkinEnigmaDisplayReplay::SetTitle(const char *Title) +{ + // draw title area + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, + yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, + yTitleTop + Roundness - 1, clrTransparent, -2); + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight, + yTitleTop + Roundness, clrTransparent, -1); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, + yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + + if (Title) { + debug("REPLAY TITLE: %s\n", Title); + // draw titlebar + osd->DrawText(xTitleLeft + Roundness + 3, yTitleTop + 3, Title, + Theme.Color(clrTitleShadow), clrTransparent, + cFont::GetFont(fontOsd), + xTitleRight - Roundness - xTitleLeft - Roundness - 3, + yTitleBottom - yTitleTop - 3); + osd->DrawText(xTitleLeft + Roundness, yTitleTop, Title, + Theme.Color(clrTitleFg), clrTransparent, + cFont::GetFont(fontOsd), + xTitleRight - Roundness - xTitleLeft - Roundness, + yTitleBottom - yTitleTop); + } +} + +void cSkinEnigmaDisplayReplay::SetMode(bool Play, bool Forward, int Speed) +{ + bool fFoundLogo = false; + if (Speed < -1) + Speed = -1; + + if (EnigmaConfig.showSymbols) { + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + + char *logo = NULL; + asprintf(&logo, "icons/menu/%s", EnigmaStatus.ReplayModeName()); + if (EnigmaLogoCache.Load(logo)) { + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg), true); + } + free(logo); + } else { + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, modeonly ? clrTransparent : Theme.Color(clrLogoBg)); + } + + if (Speed == -1) + fFoundLogo = EnigmaLogoCache.Load(Play ? "icons/replay/play" : "icons/replay/pause"); + else if (Play) { + if (Speed > MAX_SPEED_BITMAPS - 1) { + error("MAX SPEED %d > 9\n", Speed); + Speed = MAX_SPEED_BITMAPS - 1; + } + fFoundLogo = EnigmaLogoCache.Load(Forward ? strFastForward_large[Speed] : strFastRewind_large[Speed]); + } else { // trick speed + if (Speed > MAX_TRICKSPEED_BITMAPS - 1) { + error("MAX SPEED %d > 3\n", Speed); + Speed = MAX_TRICKSPEED_BITMAPS - 1; + } + fFoundLogo = EnigmaLogoCache.Load(Forward ? strSlowForward_large[Speed] : strSlowRewind_large[Speed]); + } + if (fFoundLogo) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(), EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg), false, true); +} + +void cSkinEnigmaDisplayReplay::SetProgress(int Current, int Total) +{ + // create progressbar + cProgressBar pb(xProgressRight - xProgressLeft - 2 * BigGap, + yProgressBottom - yProgressTop - 2 * BigGap, Current, Total, + marks, Theme.Color(clrReplayProgressSeen), + Theme.Color(clrReplayProgressRest), + Theme.Color(clrReplayProgressSelected), + Theme.Color(clrReplayProgressMark), + Theme.Color(clrReplayProgressCurrent)); + // draw progressbar + osd->DrawBitmap(xProgressLeft + BigGap, yProgressTop + BigGap, pb); +} + +void cSkinEnigmaDisplayReplay::SetCurrent(const char *Current) +{ + if (!Current) + return; + + // draw current time + const cFont *font = cFont::GetFont(fontSml); + int w = font->Width(Current); + osd->DrawText(xTimeLeft + BigGap, yTimeTop, Current, + Theme.Color(clrReplayCurrent), Theme.Color(clrAltBackground), font, + w, yTimeBottom - yTimeTop, taLeft); +} + +void cSkinEnigmaDisplayReplay::SetTotal(const char *Total) +{ + if (!Total) + return; + + // draw total time + const cFont *font = cFont::GetFont(fontSml); + int w = font->Width(Total); + osd->DrawText(xTimeRight - BigGap - w, yTimeTop, Total, + Theme.Color(clrReplayTotal), Theme.Color(clrAltBackground), font, w, + yTimeBottom - yTimeTop, taRight); +} + +void cSkinEnigmaDisplayReplay::SetJump(const char *Jump) +{ + if (Jump) { + // draw jump prompt + const cFont *font = cFont::GetFont(fontSml); + nJumpWidth = font->Width(Jump); + osd->DrawText(xTimeLeft + (xTimeRight - xTimeLeft - nJumpWidth) / 2, + yTimeTop, Jump, Theme.Color(clrReplayModeJump), + Theme.Color(clrAltBackground), font, nJumpWidth, + yTimeBottom - yTimeTop, taLeft); + } else { + // erase old prompt + osd->DrawRectangle(xTimeLeft + (xTimeRight - xTimeLeft - nJumpWidth) / 2, + yTimeTop, + xTimeLeft + (xTimeRight - xTimeLeft - nJumpWidth) / 2 + + nJumpWidth - 1, yTimeBottom - 1, Theme.Color(clrAltBackground)); + } +} + +void cSkinEnigmaDisplayReplay::SetMessage(eMessageType Type, const char *Text) +{ + if (Text) { + // save current osd + osd->SaveRegion(xMessageLeft, yMessageTop, xMessageRight, yMessageBottom); + // draw message + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, + yMessageBottom - 1, clrTransparent); + osd->DrawRectangle(xMessageLeft, yMessageTop + SmallGap, xMessageRight - 1, + yMessageBottom - SmallGap - 1, + Theme.Color(clrMessageBorder)); + osd->DrawText(xMessageLeft + Gap, yMessageTop + 2 * SmallGap, Text, + Theme.Color(clrMessageStatusFg + 2 * Type), + Theme.Color(clrMessageStatusBg + 2 * Type), + cFont::GetFont(fontOsd), + xMessageRight - Gap - xMessageLeft - Gap - 1, + yMessageBottom - 2 * SmallGap - yMessageTop - 2 * SmallGap, + taCenter); + } else { + // restore saved osd + osd->RestoreRegion(); + } +} + +void cSkinEnigmaDisplayReplay::Flush(void) +{ + // update date + if (!modeonly) { + cString date = DayDateTime(); + osd->DrawText(xBottomLeft + Roundness, yBottomTop, date, + Theme.Color(clrTitleFg), Theme.Color(clrBottomBg), + cFont::GetFont(fontOsd), + xBottomRight - Roundness - xBottomLeft - Roundness - 1, + yBottomBottom - yBottomTop - 1, taRight); + } + osd->Flush(); +} + +// --- cSkinEnigmaDisplayVolume --------------------------------------------- + +class cSkinEnigmaDisplayVolume:public cSkinDisplayVolume { +private: + cOsd *osd; + + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom, xLogoDecoLeft, xLogoDecoRight; + int xBodyLeft, xBodyRight, yBodyTop, yBodyBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + + int lineHeight; +public: + cSkinEnigmaDisplayVolume(); + virtual ~ cSkinEnigmaDisplayVolume(); + virtual void SetVolume(int Current, int Total, bool Mute); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayVolume::cSkinEnigmaDisplayVolume() +{ + lineHeight = cFont::GetFont(fontOsd)->Height(); + + xLogoLeft = 0; + xLogoRight = LogoWidth; + xLogoDecoLeft = xLogoRight + LogoDecoGap; + xLogoDecoRight = xLogoDecoLeft + LogoDecoWidth; + yLogoTop = 0; + yLogoBottom = yLogoTop + LogoHeight; + xTitleLeft = xLogoDecoRight + LogoDecoGap2; + xTitleRight = Setup.OSDWidth; + yTitleTop = yLogoTop; + yTitleBottom = yTitleTop + lineHeight; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + xBottomLeft = xTitleLeft; + xBottomRight = Setup.OSDWidth; + yBottomTop = yLogoBottom - lineHeight; + yBottomBottom = yLogoBottom; + xBodyLeft = xTitleLeft; + xBodyRight = xTitleRight; + yBodyTop = yTitleDecoBottom + TitleDecoGap2; + yBodyBottom = yBottomTop - SmallGap; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {xLogoLeft, yLogoTop, xTitleRight - 1, yBottomBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { + tArea Areas[] = { {xLogoLeft, yLogoTop, xLogoRight + LogoDecoGap + LogoDecoWidth - 1, yLogoBottom - 1, 4}, + {xTitleLeft, yTitleTop, xTitleRight - 1, yBottomBottom - 1, 4} + }; + int rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayVolume: CanHandleAreas() returned %d (%d, %d)\n", rc, oeWrongAlignment, oeOutOfMemory); + // set colors + osd->GetBitmap(1)->Reset(); + osd->GetBitmap(1)->SetColor(0, Theme.Color(clrTransparent)); + osd->GetBitmap(1)->SetColor(1, Theme.Color(clrBackground)); + osd->GetBitmap(1)->SetColor(2, Theme.Color(clrTitleBg)); + osd->GetBitmap(1)->SetColor(3, Theme.Color(clrBottomBg)); + osd->GetBitmap(1)->SetColor(4, Theme.Color(clrVolumeBar)); + osd->GetBitmap(1)->SetColor(5, Theme.Color(clrVolumeBarMute)); + osd->GetBitmap(1)->SetColor(6, Theme.Color(clrTitleFg)); + osd->GetBitmap(1)->SetColor(7, Theme.Color(clrTitleShadow)); + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + osd->DrawRectangle(xLogoDecoLeft, yLogoTop, xLogoDecoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + // draw title + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight - 1, yTitleTop + Roundness - 1, clrTransparent, -1); + // draw body area + osd->DrawRectangle(xBodyLeft, yBodyTop, xBodyRight - 1, yBodyBottom - 1, Theme.Color(clrBackground)); + // draw bottom area + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); +} + +cSkinEnigmaDisplayVolume::~cSkinEnigmaDisplayVolume() +{ + delete osd; +} + +void cSkinEnigmaDisplayVolume::SetVolume(int Current, int Total, bool Mute) +{ + tColor ColorBar; + const char *Prompt; + const cFont *font = cFont::GetFont(fontOsd); + bool fFoundLogo = false; + + // select behaviour + if (Mute) { + ColorBar = Theme.Color(clrVolumeBarMute); + Prompt = tr("Mute"); + fFoundLogo = EnigmaLogoCache.Load("icons/volume/muteOn"); + } else { + ColorBar = Theme.Color(clrVolumeBar); + Prompt = tr("Volume"); + fFoundLogo = EnigmaLogoCache.Load("icons/volume/muteOff"); + } + // logo + if (fFoundLogo) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(),EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg)); + // current volume + int vol = xBodyLeft + Gap + (xBodyRight - Gap - xBodyLeft - Gap) * Current / Total; + // draw titlebar + osd->DrawRectangle(xTitleLeft + Gap, yTitleTop, xTitleRight - Roundness - 1, + yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawText(xTitleLeft + Gap + 3, yTitleTop + 3, Prompt, + Theme.Color(clrTitleShadow), clrTransparent, font, + xTitleRight - xTitleLeft, lineHeight, taCenter); + osd->DrawText(xTitleLeft + Gap, yTitleTop, Prompt, + Theme.Color(clrTitleFg), clrTransparent, font, + xTitleRight - xTitleLeft, lineHeight, taCenter); + // draw volumebar + osd->DrawRectangle(xBodyLeft + Gap, yBodyTop + Gap, xBodyRight - Gap - 1, + yBodyBottom - Gap - 1, Theme.Color(clrBackground)); + osd->DrawRectangle(xBodyLeft + Gap, yBodyTop + Gap, vol - 1, + yBodyBottom - Gap - 1, ColorBar); +} + +void cSkinEnigmaDisplayVolume::Flush(void) +{ + cString date = DayDateTime(); + osd->DrawText(xBottomLeft, yBottomTop, date, Theme.Color(clrTitleFg), + Theme.Color(clrBottomBg), cFont::GetFont(fontOsd), + xBottomRight - Gap - xBottomLeft - 1, + yBottomBottom - yBottomTop - 1, taRight); + osd->Flush(); +} + +// --- cSkinEnigmaDisplayTracks --------------------------------------------- + +class cSkinEnigmaDisplayTracks:public cSkinDisplayTracks { +private: + cOsd *osd; + + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom; + int xListLeft, xListRight, yListTop, yListBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + + int lineHeight; + int currentIndex; + + void SetItem(const char *Text, int Index, bool Current); +public: + cSkinEnigmaDisplayTracks(const char *Title, int NumTracks, + const char *const *Tracks); + virtual ~ cSkinEnigmaDisplayTracks(); + virtual void SetTrack(int Index, const char *const *Tracks); + virtual void SetAudioChannel(int AudioChannel); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayTracks::cSkinEnigmaDisplayTracks(const char *Title, int NumTracks, const char *const *Tracks) +{ //TODO: honour enable/disable option "show symbols" + const cFont *font = cFont::GetFont(fontOsd); + lineHeight = font->Height(); + currentIndex = -1; + int ItemsWidth = 0; + for (int i = 0; i < NumTracks; i++) + ItemsWidth = max(ItemsWidth, font->Width(Tracks[i])); + ItemsWidth += (EnigmaConfig.showMarker ? lineHeight : SmallGap); + ItemsWidth = max(ItemsWidth, LogoWidth); + int width = LogoWidth + LogoDecoGap2 + ItemsWidth; + width = max(width, font->Width(DayDateTime()) + 2 * Roundness); + width = max(width, font->Width(Title) + 2 * Roundness); + + xTitleLeft = 0; + xTitleRight = Setup.OSDWidth; + int d = xTitleRight - xTitleLeft; + if (d > width) { + d = (d - width) & ~0x07; // must be multiple of 8 + xTitleRight -= d; + } + + yTitleTop = 0; + yTitleBottom = lineHeight; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + xLogoLeft = xTitleLeft; + xLogoRight = xLogoLeft + LogoWidth; + yLogoTop = yTitleDecoBottom + TitleDecoGap2; + yLogoBottom = yLogoTop + LogoHeight; + xListLeft = xLogoRight + LogoDecoGap2; + xListRight = xTitleRight; + yListTop = yLogoTop; + yListBottom = yLogoBottom; + xBottomLeft = xTitleLeft; + xBottomRight = xTitleRight; + yBottomTop = yListBottom + SmallGap; + yBottomBottom = yBottomTop + lineHeight; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {xTitleLeft, yTitleTop, xBottomRight - 1, yBottomBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { + tArea Areas[] = { {xTitleLeft, yTitleTop, xTitleRight - 1, yTitleDecoBottom- 1, 2}, + {xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, 4}, + {xListLeft, yListTop, xListRight - 1, yListBottom - 1, 4}, + {xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, 2} + }; + int rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayTracks: CanHandleAreas() returned %d\n", rc); + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); + // draw titlebar + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawText(xTitleLeft + Roundness + 3, yTitleTop + 3, Title, + Theme.Color(clrTitleShadow), clrTransparent, font, + xTitleRight - Roundness - xTitleLeft - Roundness, + lineHeight - 3, taCenter); + osd->DrawText(xTitleLeft + Roundness, yTitleTop, Title, + Theme.Color(clrTitleFg), clrTransparent, font, + xTitleRight - Roundness - xTitleLeft - Roundness, lineHeight, + taCenter); + // draw rounded left corner of titlebar + osd->DrawEllipse(xTitleLeft, yTitleTop, xTitleLeft + Roundness - 1, + yTitleTop + Roundness - 1, clrTransparent, -2); + // draw rounded right corner of titlebar + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight - 1, + yTitleTop + Roundness - 1, clrTransparent, -1); + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + // draw list area + osd->DrawRectangle(xListLeft, yListTop, xListRight - 1, yListBottom - 1, Theme.Color(clrBackground)); + // draw bottom + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomLeft, yBottomBottom - Roundness, + xBottomLeft + Roundness, yBottomBottom - 1, clrTransparent, -3); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, + xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); + // fill up audio tracks + SetAudioChannel(cDevice::PrimaryDevice()->GetAudioChannel()); + for (int i = 0; i < NumTracks; i++) + SetItem(Tracks[i], i, false); +} + +cSkinEnigmaDisplayTracks::~cSkinEnigmaDisplayTracks() +{ + delete osd; +} + +void cSkinEnigmaDisplayTracks::SetItem(const char *Text, int Index, bool Current) +{ + int xItemLeft = xListLeft + (EnigmaConfig.showMarker ? lineHeight : SmallGap); + const cFont *font = cFont::GetFont(fontOsd); + int y = yListTop + Index * lineHeight; + tColor ColorFg, ColorBg; + if (Current) { + ColorFg = Theme.Color(clrMenuItemCurrentFg); + ColorBg = Theme.Color(clrMenuHighlight); + currentIndex = Index; + } else { + ColorFg = Theme.Color(clrMenuItemSelectableFg); + ColorBg = Theme.Color(clrBackground); + } + // draw track id + osd->DrawRectangle(xListLeft, y, xListRight, y + lineHeight, ColorBg); + if (EnigmaConfig.showMarker) { + osd->DrawEllipse(xListLeft + MarkerGap, y + MarkerGap, + xListLeft + lineHeight - MarkerGap, + y + lineHeight - MarkerGap, + Current ? ColorFg : ColorBg); + } + osd->DrawText(xItemLeft, y, Text, ColorFg, ColorBg, font, xListRight - xItemLeft - SmallGap, lineHeight); +} + +void cSkinEnigmaDisplayTracks::SetAudioChannel(int AudioChannel) +{ + // draw logo area + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + if (!(AudioChannel >= 0 && AudioChannel < MAX_AUDIO_BITMAPS)) + AudioChannel = 0; + if (EnigmaLogoCache.Load(strAudio_large[AudioChannel])) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, + EnigmaLogoCache.Get(),EnigmaLogoCache.Get().Color(1), + Theme.Color(clrLogoBg)); +} + +void cSkinEnigmaDisplayTracks::SetTrack(int Index, const char *const *Tracks) +{ + if (currentIndex >= 0) + SetItem(Tracks[currentIndex], currentIndex, false); + SetItem(Tracks[Index], Index, true); +} + +void cSkinEnigmaDisplayTracks::Flush(void) +{ + cString date = DayDateTime(); + osd->DrawText(xBottomLeft + Roundness, yBottomTop, date, + Theme.Color(clrTitleFg), Theme.Color(clrBottomBg), + cFont::GetFont(fontOsd), + xBottomRight - Roundness - xBottomLeft - Roundness - 1, + yBottomBottom - yBottomTop - 1, taRight); + osd->Flush(); +} + +// --- cSkinEnigmaDisplayMessage -------------------------------------------- + +class cSkinEnigmaDisplayMessage:public cSkinDisplayMessage { +private: + cOsd *osd; + + int xTitleLeft, xTitleRight, yTitleTop, yTitleBottom, yTitleDecoTop, yTitleDecoBottom; + int xLogoLeft, xLogoRight, yLogoTop, yLogoBottom, xLogoDecoLeft, xLogoDecoRight; + int xMessageLeft, xMessageRight, yMessageTop, yMessageBottom; + int xBottomLeft, xBottomRight, yBottomTop, yBottomBottom; + + int lineHeight; +public: + cSkinEnigmaDisplayMessage(); + virtual ~ cSkinEnigmaDisplayMessage(); + virtual void SetMessage(eMessageType Type, const char *Text); + virtual void Flush(void); +}; + +cSkinEnigmaDisplayMessage::cSkinEnigmaDisplayMessage() +{ + lineHeight = cFont::GetFont(fontOsd)->Height(); + + xLogoLeft = 0; + xLogoRight = xLogoLeft + LogoWidth; + xLogoDecoLeft = xLogoRight + LogoDecoGap; + xLogoDecoRight = xLogoDecoLeft + LogoDecoWidth; + yLogoTop = 0; + yLogoBottom = yLogoTop + LogoHeight; + xTitleLeft = xLogoDecoRight + LogoDecoGap2; + xTitleRight = Setup.OSDWidth; + yTitleTop = yLogoTop; + yTitleBottom = yTitleTop + lineHeight; + yTitleDecoTop = yTitleBottom + TitleDecoGap; + yTitleDecoBottom = yTitleDecoTop + TitleDecoHeight; + xBottomLeft = xTitleLeft; + xBottomRight = xTitleRight; + yBottomTop = yLogoBottom - lineHeight; + yBottomBottom = yLogoBottom; + xMessageLeft = xTitleLeft; + xMessageRight = xTitleRight; + yMessageTop = yTitleDecoBottom + TitleDecoGap2; + yMessageBottom = yBottomTop - SmallGap; + + // create osd + osd = cOsdProvider::NewOsd(Setup.OSDLeft, Setup.OSDTop + Setup.OSDHeight - yBottomBottom); + tArea Areas[] = { {xLogoLeft, yLogoTop, xBottomRight - 1, yBottomBottom - 1, 8} }; + if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk) { + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + } else { + tArea Areas[] = { {xLogoLeft, yLogoTop, xLogoRight + LogoDecoGap + LogoDecoWidth - 1, yLogoBottom - 1, 4}, + {xTitleLeft, yTitleTop, xBottomRight - 1, yBottomBottom - 1, 4} }; + int rc = osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)); + if (rc == oeOk) + osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea)); + else + error("cSkinEnigmaDisplayMessage: CanHandleAreas() returned %d\n", rc); + } + // clear all + osd->DrawRectangle(0, 0, osd->Width(), osd->Height(), clrTransparent); +} + +cSkinEnigmaDisplayMessage::~cSkinEnigmaDisplayMessage() +{ + delete osd; +} + +void cSkinEnigmaDisplayMessage::SetMessage(eMessageType Type, const char *Text) +{ + // draw logo + osd->DrawRectangle(xLogoLeft, yLogoTop, xLogoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + osd->DrawRectangle(xLogoDecoLeft, yLogoTop, xLogoDecoRight - 1, yLogoBottom - 1, Theme.Color(clrLogoBg)); + if (EnigmaLogoCache.Load("icons/message/info")) + osd->DrawBitmap(xLogoLeft + (xLogoRight - xLogoLeft - EnigmaLogoCache.Get().Width()) / 2, + yLogoTop + (yLogoBottom - yLogoTop - EnigmaLogoCache.Get().Height()) / 2, EnigmaLogoCache.Get(), + EnigmaLogoCache.Get().Color(1), Theme.Color(clrLogoBg)); + // draw title + osd->DrawRectangle(xTitleLeft, yTitleTop, xTitleRight - 1, yTitleBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawRectangle(xTitleLeft, yTitleDecoTop, xTitleRight - 1, yTitleDecoBottom - 1, Theme.Color(clrTitleBg)); + osd->DrawEllipse(xTitleRight - Roundness, yTitleTop, xTitleRight - 1, yTitleTop + Roundness - 1, clrTransparent, -1); + // draw centered message text + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, yMessageBottom - 1, clrTransparent); + osd->DrawRectangle(xMessageLeft, yMessageTop, xMessageRight - 1, + yMessageBottom - 1, Theme.Color(clrMessageBorder)); + osd->DrawText(xMessageLeft, yMessageTop + 2 * SmallGap, Text, + Theme.Color(clrMessageStatusFg + 2 * Type), + Theme.Color(clrMessageStatusBg + 2 * Type), + cFont::GetFont(fontOsd), + xMessageRight - xMessageLeft, + yMessageBottom - 2 * SmallGap - yMessageTop - 2 * SmallGap, taCenter); + // draw bottom + osd->DrawRectangle(xBottomLeft, yBottomTop, xBottomRight - 1, yBottomBottom - 1, Theme.Color(clrBottomBg)); + osd->DrawEllipse(xBottomRight - Roundness, yBottomBottom - Roundness, + xBottomRight - 1, yBottomBottom - 1, clrTransparent, -4); +} + +void cSkinEnigmaDisplayMessage::Flush(void) +{ + osd->Flush(); +} + +// --- cSkinEnigma ---------------------------------------------------------- + +cSkinEnigma::cSkinEnigma() : cSkin("EnigmaNG", &::Theme) +{} + +const char *cSkinEnigma::Description(void) +{ + return tr("EnigmaNG"); +} + +cSkinDisplayChannel *cSkinEnigma::DisplayChannel(bool WithInfo) +{ + return new cSkinEnigmaDisplayChannel(WithInfo); +} + +cSkinDisplayMenu *cSkinEnigma::DisplayMenu(void) +{ + return new cSkinEnigmaDisplayMenu; +} + +cSkinDisplayReplay *cSkinEnigma::DisplayReplay(bool ModeOnly) +{ + return new cSkinEnigmaDisplayReplay(ModeOnly); +} + +cSkinDisplayVolume *cSkinEnigma::DisplayVolume(void) +{ + return new cSkinEnigmaDisplayVolume; +} + +cSkinDisplayTracks *cSkinEnigma::DisplayTracks(const char *Title, int NumTracks, const char *const *Tracks) +{ + return new cSkinEnigmaDisplayTracks(Title, NumTracks, Tracks); +} + +cSkinDisplayMessage *cSkinEnigma::DisplayMessage(void) +{ + return new cSkinEnigmaDisplayMessage; +} + +// vim:et:sw=2:ts=2: |