/* * enigma.c: The 'EnigmaNG' VDR skin * * See the README file for copyright information and how to reach the author. * */ #include #include #include #include #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 #include #include #include #include #include #include #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* 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: