From 2a7a011055a44516ec981e525776394a8c04dcfe Mon Sep 17 00:00:00 2001 From: louis Date: Tue, 9 Jul 2013 00:17:42 +0200 Subject: Version 0.0.6 --- HISTORY | 3 +- Makefile | 8 +- channelcolumn.c | 17 + channelcolumn.h | 6 +- config.c | 18 +- config.h | 6 + detailview.c | 53 +- detailview.h | 15 +- dummygrid.c | 1 + epggrid.c | 24 +- epggrid.h | 6 +- footer.c | 4 +- grid.h | 7 +- headergrid.c | 4 +- icons/arrow_left.png | Bin 0 -> 428 bytes icons/arrow_right.png | Bin 0 -> 438 bytes icons/delete_active.png | Bin 0 -> 58636 bytes icons/delete_inactive.png | Bin 0 -> 11721 bytes icons/edit_active.png | Bin 0 -> 16127 bytes icons/edit_inactive.png | Bin 0 -> 53216 bytes icons/icon_backspace.png | Bin 0 -> 2012 bytes icons/icon_del_ins.png | Bin 0 -> 1680 bytes icons/icon_shift.png | Bin 0 -> 1484 bytes icons/info_active.png | Bin 0 -> 11487 bytes icons/info_inactive.png | Bin 0 -> 9034 bytes icons/no.png | Bin 0 -> 52583 bytes icons/record_active.png | Bin 0 -> 7098 bytes icons/record_inactive.png | Bin 0 -> 5897 bytes icons/yes.png | Bin 0 -> 35529 bytes imageloader.c | 13 +- imageloader.h | 1 + osdmanager.c | 31 - po/de_DE.po | 344 +++++- recmanager.c | 598 ++++++++++ recmanager.h | 53 + recmenu.c | 525 +++++++++ recmenu.h | 61 + recmenuitem.c | 2016 ++++++++++++++++++++++++++++++++++ recmenuitem.h | 465 ++++++++ recmenumanager.c | 521 +++++++++ recmenumanager.h | 38 + services/epgsearch.h | 61 +- setup.c | 6 +- styledpixmap.c | 8 + styledpixmap.h | 37 +- switchtimer.c | 109 ++ switchtimer.h | 30 + themes/tvguide-darkblue.theme | 12 + themes/tvguide-default.theme | 12 + themes/tvguide-keepitsimple.theme | 14 +- themes/tvguide-nOpacity.theme | 12 + themes/tvguide-nOpacitydarkred.theme | 12 + themes/tvguide-nOpacitygreen.theme | 12 + themes/tvguide-nOpacityiceblue.theme | 15 + timeline.c | 10 +- timer.c | 46 + timer.h | 15 + tools.c | 370 +++++++ tvguide.c | 12 +- tvguideosd.c | 132 ++- tvguideosd.h | 2 + 61 files changed, 5564 insertions(+), 201 deletions(-) create mode 100644 icons/arrow_left.png create mode 100644 icons/arrow_right.png create mode 100644 icons/delete_active.png create mode 100644 icons/delete_inactive.png create mode 100644 icons/edit_active.png create mode 100644 icons/edit_inactive.png create mode 100644 icons/icon_backspace.png create mode 100644 icons/icon_del_ins.png create mode 100644 icons/icon_shift.png create mode 100644 icons/info_active.png create mode 100644 icons/info_inactive.png create mode 100644 icons/no.png create mode 100644 icons/record_active.png create mode 100644 icons/record_inactive.png create mode 100644 icons/yes.png create mode 100644 recmanager.c create mode 100644 recmanager.h create mode 100644 recmenu.c create mode 100644 recmenu.h create mode 100644 recmenuitem.c create mode 100644 recmenuitem.h create mode 100644 recmenumanager.c create mode 100644 recmenumanager.h create mode 100644 switchtimer.c create mode 100644 switchtimer.h create mode 100644 tools.c diff --git a/HISTORY b/HISTORY index 9950b38..4698d9f 100644 --- a/HISTORY +++ b/HISTORY @@ -43,7 +43,8 @@ VDR Plugin 'tvguide' Revision History - Added setup option to switch functionality of keys "Blue" and "OK" - Setup option to hide schedules time display in horizontal EPG grids -Version 0.0.6 +2013-07-08: Version 0.0.6 - added frame around scaled video picture - added theme "keep it simple" (thanks @saman) - display of additional EPG pictures in detailed epg view +- Introduction of "Search & Recording" Menu diff --git a/Makefile b/Makefile index df0ba33..b668830 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ LIBDIR = $(call PKGCFG,libdir) LOCDIR = $(call PKGCFG,locdir) PLGCFG = $(call PKGCFG,plgcfg) VDRCONFDIR= $(call PKGCFG,configdir) +PLGRESDIR = $(call PKGCFG,resdir)/plugins/$(PLUGIN) TMPDIR ?= /tmp ### The compiler options: @@ -100,6 +101,7 @@ i18n: $(I18Nmo) $(I18Npot) install-i18n: $(I18Nmsgs) ### Targets: + $(SOFILE): $(OBJS) $(CXX) $(CXXFLAGS) $(LDFLAGS) -shared $(OBJS) $(LIBS) -o $@ @@ -110,7 +112,11 @@ install-themes: @mkdir -p $(DESTDIR)$(VDRCONFDIR)/themes cp themes/* $(DESTDIR)$(VDRCONFDIR)/themes -install: install-lib install-i18n install-themes +install-icons: + mkdir -p $(DESTDIR)$(PLGRESDIR)/icons + cp -r icons/* $(DESTDIR)$(PLGRESDIR)/icons + +install: install-lib install-i18n install-themes install-icons dist: $(I18Npo) clean @-rm -rf $(TMPDIR)/$(ARCHIVE) diff --git a/channelcolumn.c b/channelcolumn.c index dae36a0..8ed34b0 100644 --- a/channelcolumn.c +++ b/channelcolumn.c @@ -5,6 +5,7 @@ cChannelColumn::cChannelColumn(int num, const cChannel *channel, cMyTime *myTime this->num = num; this->myTime = myTime; hasTimer = channel->HasTimer(); + hasSwitchTimer = SwitchTimers.ChannelInSwitchList(channel); schedulesLock = new cSchedulesLock(false, 100); header = NULL; } @@ -339,6 +340,22 @@ cGrid *cChannelColumn::addDummyGrid(time_t start, time_t end, cGrid *firstGrid, return dummy; } +void cChannelColumn::SetTimers() { + hasTimer = channel->HasTimer(); + hasSwitchTimer = SwitchTimers.ChannelInSwitchList(channel); + for (cGrid *grid = grids.First(); grid; grid = grids.Next(grid)) { + bool gridHadTimer = grid->HasTimer(); + grid->SetTimer(); + if (gridHadTimer != grid->HasTimer()) + grid->SetDirty(); + bool gridHadSwitchTimer = grid->HasSwitchTimer(); + grid->SetSwitchTimer(); + if (gridHadSwitchTimer != grid->HasSwitchTimer()) + grid->SetDirty(); + grid->Draw(); + } +} + void cChannelColumn::dumpGrids() { esyslog("tvguide: ------Channel %s: %d entires ---------", channel->Name(), grids.Count()); int i=1; diff --git a/channelcolumn.h b/channelcolumn.h index a84f9a6..1dbd174 100644 --- a/channelcolumn.h +++ b/channelcolumn.h @@ -16,6 +16,7 @@ private: cSchedulesLock *schedulesLock; const cSchedules *schedules; bool hasTimer; + bool hasSwitchTimer; cGrid *addEpgGrid(const cEvent *event, cGrid *firstGrid, bool color); cGrid *addDummyGrid(time_t start, time_t end, cGrid *firstGrid, bool color); public: @@ -42,8 +43,11 @@ public: void ClearOutdatedEnd(); int GetNum() {return num;}; void SetNum(int num) {this->num = num;}; - void setTimer() {hasTimer = true;}; + void setTimer() {hasTimer = channel->HasTimer();}; bool HasTimer() { return hasTimer; }; + void setSwitchTimer() {hasSwitchTimer = SwitchTimers.ChannelInSwitchList(channel);}; + bool HasSwitchTimer() { return hasSwitchTimer; }; + void SetTimers(); void clearGrids(); void dumpGrids(); }; diff --git a/config.c b/config.c index ac767fa..408374f 100644 --- a/config.c +++ b/config.c @@ -81,6 +81,8 @@ cTvguideConfig::cTvguideConfig() { FontGridHorizontalSmallDelta = 0; FontTimeLineDateHorizontalDelta = 0; FontTimeLineTimeHorizontalDelta = 0; + FontRecMenuItemDelta = 0; + FontRecMenuItemSmallDelta = 0; //Common Fonts FontButton = NULL; FontDetailView = NULL; @@ -102,7 +104,9 @@ cTvguideConfig::cTvguideConfig() { FontGridHorizontalSmall = NULL; FontTimeLineDateHorizontal = NULL; FontTimeLineTimeHorizontal = NULL; - + //Fonts for RecMenu + FontRecMenuItem = NULL; + FontRecMenuItemSmall = NULL; timeFormat = 1; themeIndex = 4; useBlending = 2; @@ -133,6 +137,8 @@ cTvguideConfig::~cTvguideConfig() { delete FontGridHorizontalSmall; delete FontTimeLineDateHorizontal; delete FontTimeLineTimeHorizontal; + delete FontRecMenuItem; + delete FontRecMenuItemSmall; } void cTvguideConfig::setDynamicValues(int width, int height) { @@ -207,7 +213,9 @@ void cTvguideConfig::SetFonts(void){ FontGridHorizontalSmall = cFont::CreateFont(*fontname, rowHeight/4 + FontGridHorizontalSmallDelta); FontTimeLineDateHorizontal = cFont::CreateFont(*fontname, timeLineHeight/2 + 5 + FontTimeLineDateHorizontalDelta); FontTimeLineTimeHorizontal = cFont::CreateFont(*fontname, timeLineHeight/2 + FontTimeLineTimeHorizontalDelta); - + //Fonts for RecMenu + FontRecMenuItem = cFont::CreateFont(*fontname, osdHeight/30 + FontRecMenuItemDelta); + FontRecMenuItemSmall = cFont::CreateFont(*fontname, osdHeight/40 + FontRecMenuItemSmallDelta); } void cTvguideConfig::SetBlending(void) { @@ -228,6 +236,10 @@ void cTvguideConfig::SetImagesPath(cString path) { epgImagePath = path; } +void cTvguideConfig::SetIconsPath(cString path) { + iconPath = path; +} + void cTvguideConfig::loadTheme() { cThemes themes; themes.Load(*cString("tvguide")); @@ -295,6 +307,8 @@ bool cTvguideConfig::SetupParse(const char *Name, const char *Value) { else if (strcmp(Name, "FontGridHorizontalSmallDelta") == 0) FontGridHorizontalSmallDelta = atoi(Value); else if (strcmp(Name, "FontTimeLineDateHorizontalDelta") == 0) FontTimeLineDateHorizontalDelta = atoi(Value); else if (strcmp(Name, "FontTimeLineTimeHorizontalDelta") == 0) FontTimeLineTimeHorizontalDelta = atoi(Value); + else if (strcmp(Name, "FontRecMenuItemDelta") == 0) FontRecMenuItemDelta = atoi(Value); + else if (strcmp(Name, "FontRecMenuItemSmallDelta") == 0) FontRecMenuItemSmallDelta = atoi(Value); else if (strcmp(Name, "displayRerunsDetailEPGView") == 0) displayRerunsDetailEPGView = atoi(Value); else if (strcmp(Name, "numReruns") == 0) numReruns = atoi(Value); else if (strcmp(Name, "useSubtitleRerun") == 0) useSubtitleRerun = atoi(Value); diff --git a/config.h b/config.h index 3269e50..3afd857 100644 --- a/config.h +++ b/config.h @@ -10,6 +10,7 @@ class cTvguideConfig { ~cTvguideConfig(); void SetLogoPath(cString path); void SetImagesPath(cString path); + void SetIconsPath(cString path); void SetBlending(void); int showMainMenuEntry; int osdWidth; @@ -61,6 +62,7 @@ class cTvguideConfig { int epgImageWidthLarge; int epgImageHeightLarge; cString epgImagePath; + cString iconPath; int fontIndex; const char *fontNameDefault; int FontButtonDelta; @@ -83,6 +85,8 @@ class cTvguideConfig { int FontGridHorizontalSmallDelta; int FontTimeLineDateHorizontalDelta; int FontTimeLineTimeHorizontalDelta; + int FontRecMenuItemDelta; + int FontRecMenuItemSmallDelta; const cFont *FontChannelHeader; const cFont *FontChannelHeaderHorizontal; const cFont *FontChannelGroups; @@ -103,6 +107,8 @@ class cTvguideConfig { const cFont *FontDetailHeader; const cFont *FontMessageBox; const cFont *FontMessageBoxLarge; + const cFont *FontRecMenuItem; + const cFont *FontRecMenuItemSmall; int timeFormat; int themeIndex; int useBlending; diff --git a/detailview.c b/detailview.c index f06229f..0ce16f0 100644 --- a/detailview.c +++ b/detailview.c @@ -2,12 +2,9 @@ #include #include "detailview.h" -cDetailView::cDetailView(cGrid *grid) { - this->grid = grid; - this->event = grid->GetEvent(); +cDetailView::cDetailView(const cEvent *event) { + this->event = event; imgScrollBar = NULL; - FrameTime = 40; // ms - FadeTime = 500; // ms borderWidth = 100; //px scrollBarWidth = 40; headerHeight = max (40 + 3 * tvguideConfig.FontDetailHeader->Height(), // border + 3 Lines @@ -48,24 +45,18 @@ bool cDetailView::setContentDrawportHeight() { void cDetailView::createPixmaps() { header = new cStyledPixmap(osdManager.requestPixmap(5, cRect(borderWidth, borderWidth, tvguideConfig.osdWidth - 2*borderWidth, headerHeight), cRect::Null)); - header->SetAlpha(0); headerLogo = osdManager.requestPixmap(6, cRect(borderWidth, borderWidth, tvguideConfig.osdWidth - 2*borderWidth, headerHeight), cRect::Null); headerLogo->Fill(clrTransparent); - headerLogo->SetAlpha(0); headerBack = osdManager.requestPixmap(4, cRect(borderWidth, borderWidth, tvguideConfig.osdWidth - 2*borderWidth, headerHeight), cRect::Null); - headerBack->SetAlpha(0); headerBack->Fill(clrBlack); header->setColor(theme.Color(clrHeader), theme.Color(clrHeaderBlending)); content = osdManager.requestPixmap(5, cRect(borderWidth, borderWidth + headerHeight, tvguideConfig.osdWidth - 2*borderWidth - scrollBarWidth, tvguideConfig.osdHeight-2*borderWidth-headerHeight), cRect(0,0, tvguideConfig.osdWidth - 2*borderWidth - scrollBarWidth, max(heightContent, tvguideConfig.osdHeight-2*borderWidth-headerHeight))); - content->SetAlpha(0); header->setColor(theme.Color(clrHeader), theme.Color(clrHeaderBlending)); scrollBar = osdManager.requestPixmap(5, cRect(tvguideConfig.osdWidth-borderWidth-scrollBarWidth, borderWidth + headerHeight, scrollBarWidth, tvguideConfig.osdHeight-2*borderWidth-headerHeight)); - scrollBar->SetAlpha(0); footer = osdManager.requestPixmap(5, cRect(borderWidth, borderWidth + headerHeight + content->ViewPort().Height(), tvguideConfig.osdWidth - 2*borderWidth, 3)); - footer->SetAlpha(0); footer->Fill(theme.Color(clrBorder)); } @@ -79,7 +70,8 @@ void cDetailView::drawHeader() { cImageLoader imgLoader; bool logoDrawn = false; if (!tvguideConfig.hideChannelLogos) { - if (imgLoader.LoadLogo(grid->column->getChannel()->Name(), logoWidth, logoHeight)) { + cString channelName = Channels.GetByChannelID(event->ChannelID())->Name(); + if (imgLoader.LoadLogo(*channelName, logoWidth, logoHeight)) { cImage logo = imgLoader.GetImage(); headerLogo->DrawImage(cPoint(10, (header->Height() - logoHeight)/2), logo); logoDrawn = true; @@ -293,28 +285,21 @@ void cDetailView::drawEPGPictures(int height) { } } -void cDetailView::Action(void) { - drawHeader(); - drawContent(); - drawScrollbar(); - uint64_t Start = cTimeMs::Now(); - while (true) { - uint64_t Now = cTimeMs::Now(); - cPixmap::Lock(); - double t = min(double(Now - Start) / FadeTime, 1.0); - int Alpha = t * ALPHA_OPAQUE; - header->SetAlpha(Alpha); - headerBack->SetAlpha(Alpha); - headerLogo->SetAlpha(Alpha); - content->SetAlpha(Alpha); - scrollBar->SetAlpha(Alpha); - footer->SetAlpha(Alpha); - osdManager.flush(); - cPixmap::Unlock(); - int Delta = cTimeMs::Now() - Now; - if (Delta < FrameTime) - cCondWait::SleepMs(FrameTime - Delta); - if ((Now - Start) > FadeTime) +eOSState cDetailView::ProcessKey(eKeys Key) { + eOSState state = osContinue; + switch (Key & ~k_Repeat) { + case kUp: + scrollUp(); + osdManager.flush(); + break; + case kDown: + scrollDown(); + osdManager.flush(); + break; + case kOk: + case kBack: + state = osEnd; break; } + return state; } \ No newline at end of file diff --git a/detailview.h b/detailview.h index 3d72795..6b59bbb 100644 --- a/detailview.h +++ b/detailview.h @@ -5,9 +5,8 @@ class cEpgGrid; -class cDetailView : public cThread { +class cDetailView { private: - cGrid *grid; cStyledPixmap *header; cPixmap *headerLogo; cPixmap *headerBack; @@ -16,8 +15,6 @@ private: cPixmap *footer; const cEvent *event; cImage *imgScrollBar; - int FrameTime; - int FadeTime; cTextWrapper description; cTextWrapper reruns; int borderWidth; @@ -29,19 +26,19 @@ private: int numEPGPics; bool contentScrollable; void loadReruns(void); - void drawHeader(); - void drawContent(); - void drawScrollbar(); int heightEPGPics(void); void drawEPGPictures(int height); cImage *createScrollbar(int width, int height, tColor clrBgr, tColor clrBlend); - virtual void Action(void); public: - cDetailView(cGrid *grid); + cDetailView(const cEvent *event); virtual ~cDetailView(void); void createPixmaps(); + void drawHeader(); + void drawContent(); + void drawScrollbar(); void scrollUp(); void scrollDown(); + eOSState ProcessKey(eKeys Key); }; #endif //__TVGUIDE_DETAILVIEW_H \ No newline at end of file diff --git a/dummygrid.c b/dummygrid.c index 97a8d1a..42025e0 100644 --- a/dummygrid.c +++ b/dummygrid.c @@ -5,6 +5,7 @@ cDummyGrid::cDummyGrid(cChannelColumn *c, time_t start, time_t end) : cGrid(c) { this->end = end; strText = tr("No EPG Information available"); dummy = true; + hasTimer = false; } cDummyGrid::~cDummyGrid(void) { diff --git a/epggrid.c b/epggrid.c index 97a57f8..434a612 100644 --- a/epggrid.c +++ b/epggrid.c @@ -7,6 +7,9 @@ cEpgGrid::cEpgGrid(cChannelColumn *c, const cEvent *event) : cGrid(c) { hasTimer = false; if (column->HasTimer()) hasTimer = event->HasTimer(); + hasSwitchTimer = false; + if (column->HasSwitchTimer()) + hasSwitchTimer = SwitchTimers.EventInSwitchList(event); dummy = false; } @@ -89,8 +92,6 @@ void cEpgGrid::drawText() { pixmap->DrawText(cPoint(borderWidth, borderWidth + offset + i*textHeight), extText->GetLine(i), colorText, colorTextBack, tvguideConfig.FontGridSmall); } } - if (hasTimer) - drawRecIcon(); } else if (tvguideConfig.displayMode == eHorizontal) { if (Width()/tvguideConfig.minutePixel < 10) { int titleY = (tvguideConfig.rowHeight - tvguideConfig.FontGridHorizontal->Height())/2; @@ -107,14 +108,21 @@ void cEpgGrid::drawText() { } pixmap->DrawText(cPoint(borderWidth, titleY), *strTitle, colorText, colorTextBack, tvguideConfig.FontGridHorizontal); } + if (hasSwitchTimer) + drawIcon("Switch", theme.Color(clrButtonYellow)); + if (hasTimer) + drawIcon("REC", theme.Color(clrButtonRed)); } -void cEpgGrid::drawRecIcon() { - cString recIconText("REC"); - int width = tvguideConfig.FontGrid->Width(*recIconText)+2*borderWidth; - int height = tvguideConfig.FontGrid->Height()+10; - pixmap->DrawRectangle( cRect(Width() - width - borderWidth, Height() - height - borderWidth, width, height), theme.Color(clrButtonRed)); - pixmap->DrawText(cPoint(Width() - width, Height() - height - borderWidth/2), *recIconText, theme.Color(clrFont), theme.Color(clrButtonRed), tvguideConfig.FontGrid); +void cEpgGrid::drawIcon(cString iconText, tColor color) { + + const cFont *font = (tvguideConfig.displayMode == eVertical) + ?tvguideConfig.FontGrid + :tvguideConfig.FontGridHorizontalSmall; + int textWidth = font->Width(*iconText)+2*borderWidth; + int textHeight = font->Height()+10; + pixmap->DrawRectangle( cRect(Width() - textWidth - borderWidth, Height() - textHeight - borderWidth, textWidth, textHeight), color); + pixmap->DrawText(cPoint(Width() - textWidth, Height() - textHeight - borderWidth/2), *iconText, theme.Color(clrFont), color, font); } cString cEpgGrid::getTimeString(void) { diff --git a/epggrid.h b/epggrid.h index d7cae70..0465d06 100644 --- a/epggrid.h +++ b/epggrid.h @@ -8,9 +8,8 @@ private: const cEvent *event; cTextWrapper *extText; cString timeString; - bool hasTimer; void drawText(); - void drawRecIcon(); + void drawIcon(cString iconText, tColor color); time_t Duration(void) { return event->Duration(); }; public: cEpgGrid(cChannelColumn *c, const cEvent *event); @@ -21,7 +20,8 @@ public: const cEvent *GetEvent() {return event;}; time_t StartTime() { return event->StartTime(); }; time_t EndTime() { return event->EndTime(); }; - void setTimer() {hasTimer = true;}; + void SetTimer() {hasTimer = event->HasTimer();}; + void SetSwitchTimer() {hasSwitchTimer = SwitchTimers.EventInSwitchList(event);}; cString getTimeString(void); void debug(); }; diff --git a/footer.c b/footer.c index 4c26336..7a19053 100644 --- a/footer.c +++ b/footer.c @@ -38,7 +38,7 @@ void cFooter::DrawButton(const char *text, tColor color, tColor borderColor, int } void cFooter::drawRedButton() { - cString text(tr("Set Timer")); + cString text(tr("Search & Rec")); DrawButton(*text, theme.Color(clrButtonRed), theme.Color(clrButtonRedBorder), 0); } @@ -78,4 +78,4 @@ void cFooter::UpdateGroupButtons(const cChannel *channel) { drawGreenButton(channelGroups->GetPrev(group)); drawYellowButton(channelGroups->GetNext(group)); } -} \ No newline at end of file +} diff --git a/grid.h b/grid.h index 42ad41d..d51271b 100644 --- a/grid.h +++ b/grid.h @@ -12,6 +12,8 @@ protected: bool isColor1; bool active; bool dirty; + bool hasTimer; + bool hasSwitchTimer; bool intersects(cGrid *neighbor); virtual time_t Duration(void) {}; virtual void drawText(void) {}; @@ -37,10 +39,13 @@ public: virtual void SetStartTime(time_t start) {}; virtual void SetEndTime(time_t end) {}; int calcOverlap(cGrid *neighbor); - virtual void setTimer() {}; + virtual void SetTimer() {}; + virtual void SetSwitchTimer() {}; virtual cString getText(void) { return cString("");}; virtual cString getTimeString(void) { return cString("");}; bool Active(void) { return active; }; + bool HasTimer() {return hasTimer;}; + bool HasSwitchTimer() {return hasSwitchTimer;}; bool isDummy() { return dummy; }; virtual void debug() {}; }; diff --git a/headergrid.c b/headergrid.c index 280ea46..4bb25c8 100644 --- a/headergrid.c +++ b/headergrid.c @@ -24,8 +24,8 @@ void cHeaderGrid::createBackground(int num) { width = tvguideConfig.channelHeaderWidth; height = tvguideConfig.rowHeight; } - pixmap = osdManager.requestPixmap(2, cRect(x, y, width, height)); - pixmapLogo = osdManager.requestPixmap(3, cRect(x, y, width, height)); + pixmap = osdManager.requestPixmap(1, cRect(x, y, width, height)); + pixmapLogo = osdManager.requestPixmap(2, cRect(x, y, width, height)); if ((!pixmap) || (!pixmapLogo)){ return; } diff --git a/icons/arrow_left.png b/icons/arrow_left.png new file mode 100644 index 0000000..a65e47d Binary files /dev/null and b/icons/arrow_left.png differ diff --git a/icons/arrow_right.png b/icons/arrow_right.png new file mode 100644 index 0000000..97fa123 Binary files /dev/null and b/icons/arrow_right.png differ diff --git a/icons/delete_active.png b/icons/delete_active.png new file mode 100644 index 0000000..b01dc5c Binary files /dev/null and b/icons/delete_active.png differ diff --git a/icons/delete_inactive.png b/icons/delete_inactive.png new file mode 100644 index 0000000..b327f07 Binary files /dev/null and b/icons/delete_inactive.png differ diff --git a/icons/edit_active.png b/icons/edit_active.png new file mode 100644 index 0000000..5b3b667 Binary files /dev/null and b/icons/edit_active.png differ diff --git a/icons/edit_inactive.png b/icons/edit_inactive.png new file mode 100644 index 0000000..d4f59a1 Binary files /dev/null and b/icons/edit_inactive.png differ diff --git a/icons/icon_backspace.png b/icons/icon_backspace.png new file mode 100644 index 0000000..e20b7bd Binary files /dev/null and b/icons/icon_backspace.png differ diff --git a/icons/icon_del_ins.png b/icons/icon_del_ins.png new file mode 100644 index 0000000..3d06162 Binary files /dev/null and b/icons/icon_del_ins.png differ diff --git a/icons/icon_shift.png b/icons/icon_shift.png new file mode 100644 index 0000000..998d7fa Binary files /dev/null and b/icons/icon_shift.png differ diff --git a/icons/info_active.png b/icons/info_active.png new file mode 100644 index 0000000..7f1ccf7 Binary files /dev/null and b/icons/info_active.png differ diff --git a/icons/info_inactive.png b/icons/info_inactive.png new file mode 100644 index 0000000..9a79e95 Binary files /dev/null and b/icons/info_inactive.png differ diff --git a/icons/no.png b/icons/no.png new file mode 100644 index 0000000..3630d29 Binary files /dev/null and b/icons/no.png differ diff --git a/icons/record_active.png b/icons/record_active.png new file mode 100644 index 0000000..8c42156 Binary files /dev/null and b/icons/record_active.png differ diff --git a/icons/record_inactive.png b/icons/record_inactive.png new file mode 100644 index 0000000..6720a6f Binary files /dev/null and b/icons/record_inactive.png differ diff --git a/icons/yes.png b/icons/yes.png new file mode 100644 index 0000000..ec30010 Binary files /dev/null and b/icons/yes.png differ diff --git a/imageloader.c b/imageloader.c index 31e0d0c..292d183 100644 --- a/imageloader.c +++ b/imageloader.c @@ -59,8 +59,19 @@ bool cImageLoader::LoadAdditionalEPGImage(cString name) { return true; } +bool cImageLoader::LoadIcon(const char *cIcon, int size) { + if (size==0) + return false; + bool success = false; + success = LoadImage(cString(cIcon), tvguideConfig.iconPath, "png"); + if (!success) + return false; + buffer.sample(Geometry(size, size)); + return true; +} + bool cImageLoader::DrawBackground(tColor back, tColor blend, int width, int height) { - if ((width < 1) || (height < 1)) + if ((width < 1) || (height < 1) || (width > 1920) || (height > 1080)) return false; Color Back = Argb2Color(back); Color Blend = Argb2Color(blend); diff --git a/imageloader.h b/imageloader.h index a38a5a0..bc2b7dd 100644 --- a/imageloader.h +++ b/imageloader.h @@ -17,6 +17,7 @@ public: bool LoadLogo(const char *logo, int width, int height); bool LoadEPGImage(int eventID); bool LoadAdditionalEPGImage(cString name); + bool LoadIcon(const char *cIcon, int size); bool DrawBackground(tColor back, tColor blend, int width, int height); private: Image buffer; diff --git a/osdmanager.c b/osdmanager.c index 8fdd7f6..c74162d 100644 --- a/osdmanager.c +++ b/osdmanager.c @@ -1,6 +1,3 @@ -#include -#include - #ifndef __TVGUIDE_OSDMANAGER_H #define __TVGUIDE_OSDMANAGER_H @@ -55,32 +52,4 @@ void cOsdManager::releasePixmap(cPixmap *pixmap) { if (!pixmap) return; osd->DestroyPixmap(pixmap); -} - -static std::string CutText(std::string text, int width, const cFont *font) { - if (width <= font->Size()) - return text.c_str(); - if (font->Width(text.c_str()) < width) - return text.c_str(); - cTextWrapper twText; - twText.Set(text.c_str(), font, width); - std::string cuttedTextNative = twText.GetLine(0); - std::stringstream sstrText; - sstrText << cuttedTextNative << "..."; - std::string cuttedText = sstrText.str(); - int actWidth = font->Width(cuttedText.c_str()); - if (actWidth > width) { - int overlap = actWidth - width; - int charWidth = font->Width("."); - if (charWidth == 0) - charWidth = 1; - int cutChars = overlap / charWidth; - if (cutChars > 0) { - cuttedTextNative = cuttedTextNative.substr(0, cuttedTextNative.length() - cutChars); - std::stringstream sstrText2; - sstrText2 << cuttedTextNative << "..."; - cuttedText = sstrText2.str(); - } - } - return cuttedText; } \ No newline at end of file diff --git a/po/de_DE.po b/po/de_DE.po index e075e74..d1a58c6 100755 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: vdr-tvguide 0.0.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-06-03 09:00+0200\n" +"POT-Creation-Date: 2013-07-07 13:43+0200\n" "PO-Revision-Date: 2012-08-25 17:49+0200\n" "Last-Translator: Horst\n" "Language-Team: \n" @@ -21,8 +21,8 @@ msgstr "Wiederholungen dieser Sendung" msgid "No EPG Information available" msgstr "Keine EPG Daten verfügbar" -msgid "Set Timer" -msgstr "Aufnehmen" +msgid "Search & Rec" +msgstr "Search & Rec" msgid "Channels back" msgstr "Kanäle zurück" @@ -36,6 +36,336 @@ msgstr "Umschalten" msgid "Detailed EPG" msgstr "Detailiertes EPG" +msgid "Transp." +msgstr "Transp." + +msgid "Timer Conflict" +msgstr "Timer Konflikt" + +msgid "all Channels" +msgstr "alle Kanäle" + +msgid "unknown channel" +msgstr "unbekannter Kanal" + +msgid "Duration" +msgstr "Dauer" + +msgid "min" +msgstr "min" + +msgid "recorded at" +msgstr "aufgenommen am" + +msgid "from" +msgstr "von" + +msgid "Instant Record" +msgstr "Sofortaufnahme" + +msgid "Delete Timer" +msgstr "Timer löschen" + +msgid "Edit Timer" +msgstr "Timer bearbeiten" + +msgid "Create Series Timer" +msgstr "Serientimer anlegen" + +msgid "Create Search Timer" +msgstr "Suchtimer anlegen" + +msgid "Create Switch Timer" +msgstr "Umschalttimer anlegen" + +msgid "Delete Switch Timer" +msgstr "Umschalttimer löschen" + +msgid "Search" +msgstr "Suchen" + +msgid "Search in Recordings" +msgstr "In Aufnahmen suchen" + +msgid "Check for Timer Conflicts" +msgstr "Auf Timerkoflikte prüfen" + +msgid "Timer created" +msgstr "Timer angelegt" + +msgid "Timer NOT created" +msgstr "Timer NICHT angelegt" + +msgid "OK" +msgstr "OK" + +msgid "Timer deleted" +msgstr "Timer gelöscht" + +msgid "Timer" +msgstr "Timer" + +msgid "still recording - really delete?" +msgstr "Aufzeichnung läuft - wirklich löschen?" + +msgid "Yes" +msgstr "Ja" + +msgid "No" +msgstr "Nein" + +msgid "One" +msgstr "Ein" + +msgid "detected" +msgstr "gefunden" + +msgid "Timer Conflicts" +msgstr "Timerkonflikte" + +msgid "Show conflict" +msgstr "Konflikt zeigen" + +msgid "timers involved" +msgstr "Timer beteiligt" + +msgid "Ignore Conflicts" +msgstr "Konflikte ignorieren" + +msgid "Ignore Conflict" +msgstr "Konflikt ignorieren" + +msgid "No Timer Conflicts found" +msgstr "Keine Timerkonflikte gefunden" + +msgid "Close" +msgstr "Schließen" + +msgid "Timer Active" +msgstr "Timer aktiv" + +msgid "Priority" +msgstr "Priorität" + +msgid "Lifetime" +msgstr "Lebensdauer" + +msgid "Day" +msgstr "Tag" + +msgid "Timer start time" +msgstr "Timer Start Zeit" + +msgid "Timer stop time" +msgstr "Timer Stop Zeit" + +msgid "Save" +msgstr "Speichern" + +msgid "Cancel" +msgstr "Abbrechen" + +msgid "Create Series Timer based on" +msgstr "Serientimer anlegen basierend auf" + +msgid "Channel" +msgstr "Kanal" + +msgid "Series Timer start time" +msgstr "Serientimer Start Zeit" + +msgid "Series Timer stop time" +msgstr "Serientimer Stop Zeit" + +msgid "Days to record" +msgstr "Tage" + +msgid "Day to start" +msgstr "Beginnen am" + +msgid "Create Timer" +msgstr "Timer anlegen" + +msgid "Series Timer created" +msgstr "Serientimer angelegt" + +msgid "Start" +msgstr "Start" + +msgid "Stop" +msgstr "Stop" + +msgid "Configure Search Timer based on" +msgstr "Suchtimer konfigurieren basierend auf" + +msgid "Search Expression:" +msgstr "Suchausdruck:" + +msgid "Continue" +msgstr "Weiter" + +msgid "Configure Search Timer for Search String" +msgstr "Suchtimer konfigurieren für Suchbegriff" + +msgid "Manually configure Options" +msgstr "Optionen manuell konfigurieren" + +msgid "Use Template" +msgstr "Template benutzen" + +msgid "Creating Search Timer" +msgstr "Suchtimer anlegen" + +msgid "Search Term" +msgstr "Suchbegriff" + +msgid "Using Template" +msgstr "Template" + +msgid "Display Results for Search Timer" +msgstr "Ergebnisse für Suchtimer anzeigen" + +msgid "Use other Template" +msgstr "Anderes Template benutzen" + +msgid "Configure Search Timer Options for Search String" +msgstr "Suchtimer Optionen konfigurieren für Suchbegriff" + +msgid "whole term must appear" +msgstr "vollständiger Ausdruck" + +msgid "all terms must exist" +msgstr "alle Worte" + +msgid "one term must exist" +msgstr "ein Wort" + +msgid "exact match" +msgstr "exakt" + +msgid "regular expression" +msgstr "Regulärer Ausdruck" + +msgid "Search Mode" +msgstr "Suchmodus" + +msgid "Use Title" +msgstr "Titel benutzen" + +msgid "Use Subtitle" +msgstr "Untertitel benutzen" + +msgid "Use Description" +msgstr "Beschreibung benutzen" + +msgid "Limit Channels" +msgstr "Kanäle einschränken" + +msgid "Start Channel" +msgstr "Startkanal" + +msgid "Stop Channel" +msgstr "Stopkanal" + +msgid "Use Time" +msgstr "Zeit benutzen" + +msgid "Start after" +msgstr "Beginn nach" + +msgid "Start before" +msgstr "Beginn vor" + +msgid "search results for Search Timer" +msgstr "Treffer für Suchtimer" + +msgid "search result for Search Timer" +msgstr "Treffer für Suchtimer" + +msgid "Nothing found for Search String" +msgstr "Keine Treffer für Suchbegriff" + +msgid "Search Timer sucessfully created." +msgstr "Suchtimer erfolgreich angelegt" + +msgid "Search Timer update initialised" +msgstr "Suchtimer update initialisiert" + +msgid "Search Timer NOT sucessfully created" +msgstr "Suchtimer NICHT erfolgreich angelegt" + +msgid "Configure Options for Switchtimer" +msgstr "Optionen für Umschalttimer konfigurieren" + +msgid "Minutes before switching" +msgstr "Minuten vor umschalten" + +msgid "switch" +msgstr "umschalten" + +msgid "announce only" +msgstr "nur ankündigen" + +msgid "ask for switch" +msgstr "vor umschalten fragen" + +msgid "Switch Mode" +msgstr "Umschaltmodus" + +msgid "Create" +msgstr "Anlegen" + +msgid "Switch Timer sucessfully created" +msgstr "Umschalttimer erfolgreich angelegt" + +msgid "Switch Timer NOT sucessfully created" +msgstr "Umschalttimer NICHT erfolgreich angelegt" + +msgid "Switch Timer deleted" +msgstr "Umschalttimer gelöscht" + +msgid "Show Search Options" +msgstr "Suchoptionen anzeigen" + +msgid "Perform Search" +msgstr "Suche ausführen" + +msgid "Channel to Search" +msgstr "Suche auf Kanal" + +msgid "Search in title" +msgstr "In Titel suchen" + +msgid "Search in Subtitle" +msgstr "In Untertitel suchen" + +msgid "Search in Description" +msgstr "In Beschreibung suchen" + +msgid "search results for" +msgstr "Suchergebnisse für" + +msgid "search result for" +msgstr "Suchergebnis für" + +msgid "Adapt Search" +msgstr "Suche anpassen" + +msgid "Found" +msgstr " " + +msgid "recording" +msgstr "Aufnahme gefunden" + +msgid "recordings" +msgstr "Aufnahmen gefunden" + +msgid "for" +msgstr "für" + +msgid "No recordings found for" +msgstr "Keine Aufnahmen gefunden für" + msgid "General Settings" msgstr "Allgemeine Einstellungen" @@ -234,8 +564,8 @@ msgstr "Zeitleiste Datum Schriftgröße" msgid "Timeline Time Font Size" msgstr "Zeitleiste Zeit Schriftgröße" -msgid "Timer not set! There is already a timer for this item." -msgstr "Timer wurde nicht gesetzt! Es existiert bereits ein Timer für diese Sendung" +msgid "Search & Recording Menu Font Size" +msgstr "Suchen & Aufnehmen Menu Schriftgröße" -msgid "Timer set" -msgstr "Timer gesetzt" +msgid "Search & Recording Menu Small Font Size" +msgstr "Suchen & Aufnehmen Menu kleine Schriftgröße" diff --git a/recmanager.c b/recmanager.c new file mode 100644 index 0000000..df65cc3 --- /dev/null +++ b/recmanager.c @@ -0,0 +1,598 @@ +#include +#include +#include "recmanager.h" + +static int CompareRecording(const void *p1, const void *p2) { + return (int)((*(cRecording **)p1)->Start() - (*(cRecording **)p2)->Start()); +} + +bool TVGuideTimerConflict::timerInvolved(int involvedID) { + int numConflicts = timerIDs.size(); + for (int i=0; iToDescr()); + return timer; +} + +void cRecManager::DeleteTimer(const cEvent *event) { + cTimer *t = Timers.GetMatch(event); + if (!t) + return; + DeleteTimer(t); +} + +void cRecManager::DeleteTimer(int timerID) { + cTimer *t = Timers.Get(timerID); + if (!t) + return; + DeleteTimer(t); +} + +void cRecManager::DeleteTimer(cTimer *timer) { + if (timer->Recording()) { + timer->Skip(); + cRecordControls::Process(time(NULL)); + } + isyslog("timer %s deleted", *timer->ToDescr()); + Timers.Del(timer, true); + Timers.SetModified(); +} + +void cRecManager::SaveTimer(cTimer *timer, cRecMenu *menu) { + if (!timer) + return; + + bool active = menu->GetBoolValue(1); + int prio = menu->GetIntValue(2); + int lifetime = menu->GetIntValue(3); + time_t day = menu->GetTimeValue(4); + int start = menu->GetIntValue(5); + int stop = menu->GetIntValue(6); + + timer->SetDay(day); + timer->SetStart(start); + timer->SetStop(stop); + timer->SetPriority(prio); + timer->SetLifetime(lifetime); + + if (timer->HasFlags(tfActive) && !active) + timer->ClrFlags(tfActive); + else if (!timer->HasFlags(tfActive) && active) + timer->SetFlags(tfActive); + + timer->SetEventFromSchedule(); + Timers.SetModified(); +} + +bool cRecManager::IsRecorded(const cEvent *event) { + cTimer *timer = Timers.GetMatch(event); + if (!timer) + return false; + return timer->Recording(); +} + +std::vector cRecManager::CheckTimerConflict(void) { + /* TIMERCONFLICT FORMAT: + The result list looks like this for example when we have 2 timer conflicts at one time: + 1190232780:152|30|50#152#45:45|10|50#152#45 + '1190232780' is the time of the conflict in seconds since 1970-01-01. + It's followed by list of timers that have a conflict at this time: + '152|30|50#1 int editTimer(cTimer *timer, bool active, int prio, int start, int stop); + 52#45' is the description of the first conflicting timer. Here: + '152' is VDR's timer id of this timer as returned from VDR's LSTT command + '30' is the percentage of recording that would be done (0...100) + '50#152#45' is the list of concurrent timers at this conflict + '45|10|50#152#45' describes the next conflict + */ + std::vector results; + if (!epgSearchAvailable) + return results; + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + std::list conflicts = epgSearch->handler->TimerConflictList(); + int numConflicts = conflicts.size(); + if (numConflicts > 0) { + for (std::list::iterator it=conflicts.begin(); it != conflicts.end(); ++it) { + TVGuideTimerConflict sConflict; + splitstring s(it->c_str()); + std::vector flds = s.split(':'); + if (flds.size() < 2) + continue; + sConflict.time = atoi(flds[0].c_str()); + splitstring s2(flds[1].c_str()); + std::vector flds2 = s2.split('|'); + if (flds2.size() < 3) + continue; + sConflict.timerID = atoi(flds2[0].c_str()); + sConflict.percentPossible = atoi(flds2[1].c_str()); + splitstring s3(flds2[2].c_str()); + std::vector flds3 = s3.split('#'); + std::vector timerIDs; + for (int k = 0; k < flds3.size(); k++) { + timerIDs.push_back(atoi(flds3[k].c_str()) - 1); + } + sConflict.timerIDs = timerIDs; + results.push_back(sConflict); + } + } + } + delete epgSearch; + + int numConflicts = results.size(); + time_t startTime = 0; + time_t endTime = 0; + for (int i=0; i < numConflicts; i++) { + cTimeInterval *unionSet = NULL; + int numTimers = results[i].timerIDs.size(); + for (int j=0; j < numTimers; j++) { + const cTimer *timer = Timers.Get(results[i].timerIDs[j]); + if (timer) { + if (!unionSet) { + unionSet = new cTimeInterval(timer->StartTime(), timer->StopTime()); + } else { + cTimeInterval *timerInterval = new cTimeInterval(timer->StartTime(), timer->StopTime()); + cTimeInterval *newUnion = unionSet->Union(timerInterval); + delete unionSet; + delete timerInterval; + unionSet = newUnion; + } + } + } + results[i].timeStart = unionSet->Start(); + results[i].timeStop = unionSet->Stop(); + delete unionSet; + + cTimeInterval *intersect = NULL; + for (int j=0; j < numTimers; j++) { + const cTimer *timer = Timers.Get(results[i].timerIDs[j]); + if (timer) { + if (!intersect) { + intersect = new cTimeInterval(timer->StartTime(), timer->StopTime()); + } else { + cTimeInterval *timerInterval = new cTimeInterval(timer->StartTime(), timer->StopTime()); + cTimeInterval *newIntersect = intersect->Intersect(timerInterval); + if (newIntersect) { + delete intersect; + intersect = newIntersect; + } + delete timerInterval; + } + } + } + results[i].overlapStart = intersect->Start(); + results[i].overlapStop = intersect->Stop(); + delete intersect; + } + + return results; +} + +cTimer *cRecManager::CreateSeriesTimer(cRecMenu *menu) { + bool active = menu->GetBoolValue(1); + int channelNumber = menu->GetIntValue(2); + int start = menu->GetIntValue(3); + int stop = menu->GetIntValue(4); + int weekdays = menu->GetIntValue(5); + time_t tday = menu->GetTimeValue(6); + int prio = menu->GetIntValue(7); + int lifetime = menu->GetIntValue(8); + + cChannel *channel = Channels.GetByNumber(channelNumber); + cTimer *seriesTimer = new cTimer(false, false, channel); + + seriesTimer->SetDay(tday); + seriesTimer->SetStart(start); + seriesTimer->SetStop(stop); + seriesTimer->SetPriority(prio); + seriesTimer->SetLifetime(lifetime); + seriesTimer->SetWeekDays(weekdays); + seriesTimer->SetFile("TITLE EPISODE"); + if (active) + seriesTimer->SetFlags(tfActive); + else + seriesTimer->SetFlags(tfNone); + seriesTimer->SetEventFromSchedule(); + Timers.Add(seriesTimer); + Timers.SetModified(); + return seriesTimer; +} + +std::vector cRecManager::ReadEPGSearchTemplates(void) { + cString ConfigDir = cPlugin::ConfigDirectory("epgsearch"); + cString epgsearchConf = "epgsearchtemplates.conf"; + cString fileName = AddDirectory(*ConfigDir, *epgsearchConf); + std::vector epgTemplates; + if (access(fileName, F_OK) == 0) { + FILE *f = fopen(fileName, "r"); + if (f) { + char *s; + cReadLine ReadLine; + while ((s = ReadLine.Read(f)) != NULL) { + char *p = strchr(s, '#'); + if (p) + *p = 0; + stripspace(s); + try { + if (!isempty(s)) { + std::string templ = s; + int posID = templ.find_first_of(":"); + int posName = templ.find_first_of(":", posID+1); + std::string name = templ.substr(posID+1, posName - posID - 1); + std::string templValue = templ.substr(posName); + TVGuideEPGSearchTemplate tmp; + tmp.name = name; + tmp.templValue = templValue; + epgTemplates.push_back(tmp); + } + } catch (...){} + } + } + } + return epgTemplates; +} + +std::string cRecManager::BuildEPGSearchString(cString searchString, std::string templValue) { + std::stringstream searchTimerString; + searchTimerString << "0:"; + searchTimerString << *searchString; + searchTimerString << templValue; + return searchTimerString.str(); +} + +std::string cRecManager::BuildEPGSearchString(cString searchString, cRecMenu *menu) { + int searchMode = menu->GetIntValue(0); + bool useTitle = menu->GetBoolValue(1); + bool useSubTitle = menu->GetBoolValue(2); + bool useDescription = menu->GetBoolValue(3); + bool limitChannels = menu->GetBoolValue(4); + int startChannel = -1; + int stopChannel = -1; + if (limitChannels) { + startChannel = menu->GetIntValue(5); + stopChannel = menu->GetIntValue(6); + } + int after = 0; + int before = 0; + bool limitTime = (limitChannels)?menu->GetBoolValue(7):menu->GetBoolValue(5); + if (limitTime) { + after = (limitChannels)?menu->GetIntValue(8):menu->GetIntValue(6); + before = (limitChannels)?menu->GetIntValue(9):menu->GetIntValue(7); + } + + std::stringstream searchTimerString; + //1 - unique search timer id + searchTimerString << "0:"; + //2 - the search term + searchTimerString << *searchString; + //3 - use time? 0/1 + //4 - start time in HHMM + //5 - stop time in HHMM + if (limitTime) { + searchTimerString << ":1:" << after << ":" << before << ":"; + } else { + searchTimerString << ":0:::"; + } + //6 - use channel? 0 = no, 1 = Interval, 2 = Channel group, 3 = FTA only + //7 - if 'use channel' = 1 then channel id[|channel id] in VDR format, + // one entry or min/max entry separated with |, if 'use channel' = 2 + // then the channel group name + if (limitChannels) { + searchTimerString << "1:"; + cChannel *startChan = Channels.GetByNumber(startChannel); + cChannel *stopChan = Channels.GetByNumber(stopChannel); + searchTimerString << *(startChan->GetChannelID().ToString()); + searchTimerString << "|"; + searchTimerString << *(stopChan->GetChannelID().ToString()) << ":"; + } else { + searchTimerString << "0::"; + } + //8 - match case? 0/1 + searchTimerString << ":0"; + /*9 - search mode: + 0 - the whole term must appear as substring + 1 - all single terms (delimiters are blank,',', ';', '|' or '~') + must exist as substrings. + 2 - at least one term (delimiters are blank, ',', ';', '|' or '~') + must exist as substring. + 3 - matches exactly + 4 - regular expression */ + searchTimerString << searchMode << ":"; + //10 - use title? 0/1 + if (useTitle) + searchTimerString << "1:"; + else + searchTimerString << "0:"; + //11 - use subtitle? 0/1 + if (useSubTitle) + searchTimerString << "1:"; + else + searchTimerString << "0:"; + // 12 - use description? 0/1 + if (useDescription) + searchTimerString << "1:"; + else + searchTimerString << "0:"; + //13 - use duration? 0/1 + //14 - min duration in hhmm + //15 - max duration in hhmm + searchTimerString << "0:::"; + //16 - use as search timer? 0/1 + searchTimerString << "1:"; + //17 - use day of week? 0/1 + //18 - day of week (0 = Sunday, 1 = Monday...; + // -1 Sunday, -2 Monday, -4 Tuesday, ...; -7 Sun, Mon, Tue) + searchTimerString << "0::"; + //19 - use series recording? 0/1 + searchTimerString << "1:"; + //20 - directory for recording + searchTimerString << ":"; + //21 - priority of recording + //22 - lifetime of recording + searchTimerString << "99:99:"; + //23 - time margin for start in minutes + //24 - time margin for stop in minutes + searchTimerString << "5:5:"; + //25 - use VPS? 0/1 + searchTimerString << "0:"; + /*26 - action: + 0 = create a timer + 1 = announce only via OSD (no timer) + 2 = switch only (no timer) + 3 = announce via OSD and switch (no timer) + 4 = announce via mail*/ + searchTimerString << "0:"; + /*27 - use extended EPG info? 0/1 + 28 - extended EPG info values. This entry has the following format + (delimiter is '|' for each category, '#' separates id and value): + 1 - the id of the extended EPG info category as specified in + epgsearchcats.conf + 2 - the value of the extended EPG info category + (a ':' will be translated to "!^colon^!", e.g. in "16:9") */ + searchTimerString << "0::"; + /*29 - avoid repeats? 0/1 + 30 - allowed repeats + 31 - compare title when testing for a repeat? 0/1 + 32 - compare subtitle when testing for a repeat? 0/1/2 + 0 - no + 1 - yes + 2 - yes, if present + 33 - compare description when testing for a repeat? 0/1 + 34 - compare extended EPG info when testing for a repeat? + This entry is a bit field of the category IDs. + 35 - accepts repeats only within x days */ + searchTimerString << "1:1:1:2:1:::"; + /*36 - delete a recording automatically after x days + 37 - but keep this number of recordings anyway + 38 - minutes before switch (if action = 2) + 39 - pause if x recordings already exist + 40 - blacklist usage mode (0 none, 1 selection, 2 all) + 41 - selected blacklist IDs separated with '|' + 42 - fuzzy tolerance value for fuzzy searching + 43 - use this search in favorites menu (0 no, 1 yes) + 44 - id of a menu search template + 45 - auto deletion mode (0 don't delete search timer, 1 delete after given + count of recordings, 2 delete after given days after first recording) + 46 - count of recordings after which to delete the search timer + 47 - count of days after the first recording after which to delete the search + timer + 48 - first day where the search timer is active (see parameter 16) + 49 - last day where the search timer is active (see parameter 16) + 50 - ignore missing EPG categories? 0/1 + 51 - unmute sound if off when used as switch timer + 52 - percentage of match when comparing the summary of two events (with 'avoid repeats') + 53 - HEX representation of the content descriptors, each descriptor ID is represented with 2 chars + 54 - compare date when testing for a repeat? (0=no, 1=same day, 2=same week, 3=same month) */ + searchTimerString << "0::::0:::0::0:::::::::0"; + + //esyslog("tvguide: epgsearch String: %s", searchTimerString.str().c_str()); + + return searchTimerString.str(); +} + +const cEvent **cRecManager::PerformSearchTimerSearch(std::string epgSearchString, int &numResults) { + if (!epgSearchAvailable) + return NULL; + const cEvent **searchResults = NULL; + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + std::list results = epgSearch->handler->QuerySearch(epgSearchString); + numResults = results.size(); + if (numResults > 0) { + searchResults = new const cEvent *[numResults]; + cSchedulesLock *schedulesLock; + const cSchedules *schedules; + schedules = cSchedules::Schedules(*schedulesLock); + const cEvent *event = NULL; + int index=0; + for (std::list::iterator it=results.begin(); it != results.end(); ++it) { + try { + splitstring s(it->c_str()); + std::vector flds = s.split(':', 1); + int eventID = atoi(flds[1].c_str()); + std::string channelID = flds[7]; + tChannelID chanID = tChannelID::FromString(channelID.c_str()); + cChannel *channel = Channels.GetByChannelID(chanID); + if (channel) { + const cSchedule *Schedule = NULL; + Schedule = schedules->GetSchedule(channel); + event = Schedule->GetEvent(eventID); + if (event) { + searchResults[index] = event; + } else + return NULL; + } else + return NULL; + index++; + } catch (...){} + } + } + } + return searchResults; +} + +const cEvent **cRecManager::PerformSearch(cRecMenu *menu, bool withOptions, int &numResults) { + if (epgSearchAvailable) { + cString searchString = menu->GetStringValue(1); + Epgsearch_searchresults_v1_0 data; + data.query = (char *)*searchString; + int mode = 0; + int channelNr = 0; + bool useTitle = true; + bool useSubTitle = true; + bool useDescription = false; + if (withOptions) { + mode = menu->GetIntValue(2); + channelNr = menu->GetIntValue(3); + useTitle = menu->GetBoolValue(4); + useSubTitle = menu->GetBoolValue(5); + useDescription = menu->GetBoolValue(6); + } + data.mode = mode; + data.channelNr = channelNr; + data.useTitle = useTitle; + data.useSubTitle = useSubTitle; + data.useDescription = useDescription; + + if (epgSearchPlugin->Service("Epgsearch-searchresults-v1.0", &data)) { + cList *list = data.pResultList; + int numElements = list->Count(); + const cEvent **searchResults = NULL; + if (numElements > 0) { + searchResults = new const cEvent *[numElements]; + numResults = numElements; + int index = 0; + for (Epgsearch_searchresults_v1_0::cServiceSearchResult *r = list->First(); r ; r = list->Next(r)) { + searchResults[index] = r->event; + index++; + } + } + delete list; + return searchResults; + } + } + return NULL; +} + +int cRecManager::CreateSearchTimer(std::string epgSearchString) { + int timerID = -1; + if (!epgSearchAvailable) + return timerID; + Epgsearch_services_v1_1 *epgSearch = new Epgsearch_services_v1_1; + if (epgSearchPlugin->Service("Epgsearch-services-v1.1", epgSearch)) { + timerID = epgSearch->handler->AddSearchTimer(epgSearchString); + } + return timerID; +} + +void cRecManager::UpdateSearchTimers(void) { + if (epgSearchAvailable) { + Epgsearch_updatesearchtimers_v1_0 data; + data.showMessage = false; + epgSearchPlugin->Service("Epgsearch-updatesearchtimers-v1.0", &data); + } +} + +// announceOnly: 0 = switch, 1 = announce only, 2 = ask for switch +bool cRecManager::CreateSwitchTimer(const cEvent *event, cRecMenu *menu) { + int switchMinsBefore = menu->GetIntValue(1); + int announceOnly = menu->GetIntValue(2); + if (epgSearchAvailable) { + Epgsearch_switchtimer_v1_0 data; + data.event = event; + data.mode = 1; + data.switchMinsBefore = switchMinsBefore; + data.announceOnly = announceOnly; + data.success = false; + epgSearchPlugin->Service("Epgsearch-switchtimer-v1.0", &data); + cSwitchTimer *t = new cSwitchTimer(event); + SwitchTimers.Add(t); + return data.success; + } + return false; +} + +void cRecManager::DeleteSwitchTimer(const cEvent *event) { + SwitchTimers.DeleteSwitchTimer(event); + if (epgSearchAvailable) { + Epgsearch_switchtimer_v1_0 data; + data.event = event; + data.mode = 2; + data.switchMinsBefore = 0; + data.announceOnly = 0; + data.success = false; + epgSearchPlugin->Service("Epgsearch-switchtimer-v1.0", &data); + } +} + +cRecording **cRecManager::SearchForRecordings(cString searchString, int &numResults) { + + cRecording **matchingRecordings = NULL; + int num = 0; + numResults = 0; + + for (cRecording *recording = Recordings.First(); recording; recording = Recordings.Next(recording)) { + std::string s1 = recording->Name(); + std::string s2 = *searchString; + if (s1.empty() || s2.empty()) continue; + + // tolerance for fuzzy searching: 90% of the shorter text length, but at least 1 + int tolerance = std::max(1, (int)std::min(s1.size(), s2.size()) / 10); + + bool match = FindIgnoreCase(s1, s2) >= 0 || FindIgnoreCase(s2, s1) >= 0; + + if (!match) { + AFUZZY af = { NULL, NULL, NULL, NULL, NULL, NULL, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 }; + if (s1.size() > 32) s1 = s1.substr(0, 32); + afuzzy_init(s1.c_str(), tolerance, 0, &af); + /* Checking substring */ + int res = afuzzy_checkSUB(s2.c_str(), &af); + afuzzy_free(&af); + match = (res > 0); + } + + if (!match) { + AFUZZY af = { NULL, NULL, NULL, NULL, NULL, NULL, { 0 }, { 0 }, 0, 0, 0, 0, 0, 0 }; + if (s2.size() > 32) s2 = s2.substr(0, 32); + afuzzy_init(s2.c_str(), tolerance, 0, &af); + /* Checking substring */ + int res = afuzzy_checkSUB(s1.c_str(), &af); + afuzzy_free(&af); + match = (res > 0); + } + + if (match) { + matchingRecordings = (cRecording **)realloc(matchingRecordings, (num + 1) * sizeof(cRecording *)); + matchingRecordings[num++] = recording; + } + } + if (num > 0) { + qsort(matchingRecordings, num, sizeof(cRecording *), CompareRecording); + numResults = num; + return matchingRecordings; + } + return NULL; +} \ No newline at end of file diff --git a/recmanager.h b/recmanager.h new file mode 100644 index 0000000..0b010c4 --- /dev/null +++ b/recmanager.h @@ -0,0 +1,53 @@ +#ifndef __TVGUIDE_RECMMANAGER_H +#define __TVGUIDE_RECMMANAGER_H + +class TVGuideTimerConflict { +public: + time_t time; + time_t timeStart; + time_t timeStop; + time_t overlapStart; + time_t overlapStop; + int percentPossible; + int timerID; + std::vector timerIDs; + bool timerInvolved(int involvedID); +}; + +struct TVGuideEPGSearchTemplate { +public: + std::string name; + std::string templValue; +}; + +// --- cRecManager ------------------------------------------------------------- +class cRecManager { +private: + cPlugin *epgSearchPlugin; + bool epgSearchAvailable; + void DeleteTimer(cTimer *timer); +public: + cRecManager (void); + void SetEPGSearchPlugin(void); + bool EpgSearchAvailable(void) {return epgSearchAvailable;}; + cTimer *createTimer(const cEvent *event); + void DeleteTimer(const cEvent *event); + void DeleteTimer(int timerID); + void SaveTimer(cTimer *timer, cRecMenu *menu); + bool IsRecorded(const cEvent *event); + std::vector CheckTimerConflict(void); + cTimer *CreateSeriesTimer(cRecMenu *menu); + std::string BuildEPGSearchString(cString searchString, cRecMenu *menu); + std::string BuildEPGSearchString(cString searchString, std::string templValue); + const cEvent **PerformSearchTimerSearch(std::string epgSearchString, int &numResults); + const cEvent **PerformSearch(cRecMenu *menu, bool withOptions, int &numResults); + std::vector ReadEPGSearchTemplates(void); + int CreateSearchTimer(std::string epgSearchString); + void UpdateSearchTimers(void); + bool CreateSwitchTimer(const cEvent *event, cRecMenu *menu); + void DeleteSwitchTimer(const cEvent *event); + cRecording **SearchForRecordings(cString searchString, int &numResults); + virtual ~cRecManager (void); +}; + +#endif //__TVGUIDE_RECMMANAGER_H \ No newline at end of file diff --git a/recmenu.c b/recmenu.c new file mode 100644 index 0000000..66a5a84 --- /dev/null +++ b/recmenu.c @@ -0,0 +1,525 @@ +#include "recmenu.h" + +// --- cRecMenu ------------------------------------------------------------- + +cRecMenu::cRecMenu(void) { + border = 10; + height = 2*border; + headerHeight = 0; + footerHeight = 0; + scrollHeight = 0; + scrollItemHeight = 0; + scrollable = false; + scrollbarWidth = 3 * border; + pixmapScrollBar = NULL; + imgScrollBar = NULL; + startIndex = 0; + stopIndex = 0; + numItems = 0; + header = NULL; + footer = NULL; +} + +cRecMenu::~cRecMenu(void) { + if (header) + delete header; + menuItems.Clear(); + if (footer) + delete footer; + if (pixmapScrollBar) + osdManager.releasePixmap(pixmapScrollBar); + if (imgScrollBar) + delete imgScrollBar; +} + +void cRecMenu::SetWidthPercent(int percentOSDWidth) { + width = tvguideConfig.osdWidth * percentOSDWidth / 100; + x = (tvguideConfig.osdWidth - width) / 2; +} + +void cRecMenu::SetWidthPixel(int pixel) { + width = pixel; + x = (tvguideConfig.osdWidth - width) / 2; +} + +int cRecMenu::CalculateOptimalWidth(void) { + int optWidth = 0; + for (cRecMenuItem *item = menuItems.First(); item; item = menuItems.Next(item)) { + int itemWidth = item->GetWidth(); + if (itemWidth > optWidth) + optWidth = itemWidth; + } + return optWidth; +} + + +void cRecMenu::AddMenuItem(cRecMenuItem *item, cRecMenuItem *before) { + if (!before) + menuItems.Add(item); + else + menuItems.Ins(item, before); +} + +void cRecMenu::AddMenuItemScroll(cRecMenuItem *item) { + scrollHeight += item->GetHeight(); + stopIndex++; + numItems++; + if (scrollItemHeight == 0) + scrollItemHeight = item->GetHeight(); + menuItems.Add(item); +} + +bool cRecMenu::CheckHeight(void) { + int nextHeight = headerHeight + footerHeight + scrollHeight + 2*border + 150; + if (nextHeight > tvguideConfig.osdHeight) { + scrollable = true; + return false; + } + return true; +} + +void cRecMenu::CalculateHeight(void) { + height = 2*border; + if (header) + height += headerHeight; + for (cRecMenuItem *item = menuItems.First(); item; item = menuItems.Next(item)) { + height += item->GetHeight(); + } + if (footer) + height += footerHeight; + y = (tvguideConfig.osdHeight - height) / 2; + + if (scrollable) { + width += scrollbarWidth + border; + } +} + +void cRecMenu::CreatePixmap(void) { + pixmap = osdManager.requestPixmap(3, cRect(x, y, width, height)); + if (scrollable) { + int scrollBarX = x + width - scrollbarWidth - border; + int scrollBarY = y + border + headerHeight; + int scrollBarHeight = height - headerHeight - footerHeight - 2 * border; + pixmapScrollBar = osdManager.requestPixmap(4, cRect(scrollBarX, scrollBarY, scrollbarWidth, scrollBarHeight)); + } +} + +void cRecMenu::SetFooter(cRecMenuItem *footer) { + this->footer = footer; + footerHeight = footer->GetHeight(); + height += footerHeight; +} + +void cRecMenu::SetHeader(cRecMenuItem *header) { + this->header = header; + headerHeight = header->GetHeight(); + height += headerHeight; +} + +cRecMenuItem *cRecMenu::GetActiveMenuItem(void) { + for (cRecMenuItem *item = menuItems.First(); item; item = menuItems.Next(item)) { + if (item->isActive()) + return item; + } + if (footer && footer->isActive()) + return footer; + return NULL; +} + +int cRecMenu::GetActive(bool withOffset) { + int numActive = withOffset?startIndex:0; + int i = 0; + for (cRecMenuItem *item = menuItems.First(); item; item = menuItems.Next(item)) { + if (item->isActive()) { + numActive += i; + break; + } + i++; + } + return numActive; +} + +bool cRecMenu::ActivatePrev(void) { + cRecMenuItem *activeItem = GetActiveMenuItem(); + if (!scrollable && footer && footer->isActive()) { + Activate(footer, menuItems.Last()); + return true; + } else if (activeItem) { + cRecMenuItem *prev = NULL; + for (cRecMenuItem *item = menuItems.Prev(activeItem); item; item = menuItems.Prev(item)) { + if (item->isSelectable()) { + prev = item; + break; + } + } + if (prev) { + Activate(activeItem , prev); + return true; + } + } + return false; +} + +bool cRecMenu::ActivateNext(void) { + cRecMenuItem *activeItem = GetActiveMenuItem(); + if (activeItem) { + cRecMenuItem *next = NULL; + for (cRecMenuItem *item = menuItems.Next(activeItem); item; item = menuItems.Next(item)) { + if (item->isSelectable()) { + next = item; + break; + } + } + if (next) { + Activate(activeItem , next); + return true; + } else if (!scrollable && footer && footer->isSelectable()) { + Activate(activeItem , footer); + return true; + } + } + return false; +} + +void cRecMenu::Activate(cRecMenuItem *itemOld, cRecMenuItem *item) { + itemOld->setInactive(); + itemOld->setBackground(); + itemOld->Draw(); + item->setActive(); + item->setBackground(); + item->Draw(); +} + +void cRecMenu::ScrollUp(void) { + if (footer && footer->isActive()) { + Activate(footer, menuItems.Last()); + } else { + //get perv x items + int numNewItems = numItems / 2; + int numAdded = 0; + cRecMenuItem *newItem = NULL; + while (newItem = GetMenuItem(startIndex-1)) { + AddMenuItem(newItem, menuItems.First()); + menuItems.Del(menuItems.Last(), true); + stopIndex--; + startIndex--; + numAdded++; + if (numAdded >= numNewItems) + break; + } + if (numAdded != 0) { + Arrange(true); + Display(true); + ActivatePrev(); + } + } +} + +void cRecMenu::ScrollDown(void) { + //get next x items + int numNewItems = numItems / 2; + int numAdded = 0; + cRecMenuItem *newItem = NULL; + while (newItem = GetMenuItem(stopIndex)) { + menuItems.Add(newItem); + menuItems.Del(menuItems.First(), true); + stopIndex++; + startIndex++; + numAdded++; + if (numAdded >= numNewItems) + break; + } + if (numAdded != 0) { + Arrange(true); + Display(true); + ActivateNext(); + } else { + //last item reached, activate footer + if (footer) { + cRecMenuItem *activeItem = GetActiveMenuItem(); + Activate(activeItem , footer); + } + } +} + +void cRecMenu::JumpBegin(void) { + cRecMenuItem *activeItem = GetActiveMenuItem(); + if (!scrollable) { + cRecMenuItem *firstSelectable= NULL; + for (cRecMenuItem *item = menuItems.First(); item; item = menuItems.Next(item)) { + if (item->isSelectable()) { + firstSelectable = item; + break; + } + } + if (activeItem && firstSelectable) { + Activate(activeItem , firstSelectable); + } + } else { + activeItem->setInactive(); + activeItem->setBackground(); + if (footer) + footer->Draw(); + menuItems.Clear(); + int currentItem = 0; + cRecMenuItem *newItem = NULL; + while (newItem = GetMenuItem(currentItem)) { + AddMenuItem(newItem); + currentItem++; + if (currentItem >= numItems) + break; + } + Arrange(true); + startIndex = 0; + stopIndex = numItems-1; + menuItems.First()->setActive(); + menuItems.First()->setBackground(); + menuItems.First()->Draw(); + Display(true); + } +} + +void cRecMenu::JumpEnd(void) { + cRecMenuItem *activeItem = GetActiveMenuItem(); + if (!activeItem) + return; + if (!scrollable) { + cRecMenuItem *lastSelectable= NULL; + if (footer && footer->isSelectable()) { + lastSelectable = footer; + } else { + for (cRecMenuItem *item = menuItems.Last(); item; item = menuItems.Prev(item)) { + if (item->isSelectable()) { + lastSelectable = item; + break; + } + } + } + if (lastSelectable) { + Activate(activeItem , lastSelectable); + } + } else { + activeItem->setInactive(); + activeItem->setBackground(); + menuItems.Clear(); + int totalNumItems = GetTotalNumMenuItems(); + int currentItem = totalNumItems-1; + int itemsAdded = 0; + cRecMenuItem *newItem = NULL; + while (newItem = GetMenuItem(currentItem)) { + AddMenuItem(newItem, menuItems.First()); + currentItem--; + itemsAdded++; + if (itemsAdded >= numItems) + break; + } + Arrange(true); + stopIndex = totalNumItems; + startIndex = stopIndex - numItems; + if (footer) { + footer->setActive(); + footer->setBackground(); + footer->Draw(); + } else { + menuItems.Last()->setActive(); + menuItems.Last()->setBackground(); + menuItems.Last()->Draw(); + } + Display(true); + } +} + +void cRecMenu::Arrange(bool scroll) { + int xElement = x + border; + int yElement = y + border; + int widthElement = width - 2 * border; + if (scrollable) + widthElement -= scrollbarWidth + border; + + if (header) { + if (!scroll) { + header->SetGeometry(xElement, yElement, widthElement); + header->SetPixmaps(); + } + yElement += header->GetHeight(); + } + for (cRecMenuItem *item = menuItems.First(); item; item = menuItems.Next(item)) { + item->SetGeometry(xElement, yElement, widthElement); + item->SetPixmaps(); + yElement += item->GetHeight(); + } + if (footer && !scroll) { + footer->SetGeometry(xElement, yElement, widthElement); + footer->SetPixmaps(); + } +} + +void cRecMenu::Display(bool scroll) { + pixmap->Fill(theme.Color(clrBackground)); + drawBorder(); + if (header && !scroll) { + header->setBackground(); + header->Draw(); + } + for (cRecMenuItem *item = menuItems.First(); item; item = menuItems.Next(item)) { + item->setBackground(); + item->Draw(); + } + if (footer && !scroll) { + footer->setBackground(); + footer->Draw(); + } + if (scrollable) + DrawScrollBar(); +} + +void cRecMenu::Hide(void) { + pixmap->SetLayer(-1); + if (pixmapScrollBar) + pixmapScrollBar->SetLayer(-1); + if (header) + header->Hide(); + if (footer) + footer->Hide(); + for (cRecMenuItem *item = menuItems.First(); item; item = menuItems.Next(item)) { + item->Hide(); + } +} + +void cRecMenu::Show(void) { + pixmap->SetLayer(3); + if (pixmapScrollBar) + pixmapScrollBar->SetLayer(3); + if (header) + header->Show(); + if (footer) + footer->Show(); + for (cRecMenuItem *item = menuItems.First(); item; item = menuItems.Next(item)) { + item->Show(); + } +} + +void cRecMenu::DrawScrollBar(void) { + pixmapScrollBar->Fill(theme.Color(clrBorder)); + pixmapScrollBar->DrawRectangle(cRect(2,2,pixmapScrollBar->ViewPort().Width()-4, pixmapScrollBar->ViewPort().Height() - 4), theme.Color(clrBackground)); + + int totalNumItems = GetTotalNumMenuItems(); + if (imgScrollBar == NULL) { + int scrollBarImgHeight = (pixmapScrollBar->ViewPort().Height() - 8) * numItems / totalNumItems; + imgScrollBar = createScrollbar(pixmapScrollBar->ViewPort().Width()-8, scrollBarImgHeight, theme.Color(clrHighlight), theme.Color(clrHighlightBlending)); + } + int offset = (pixmapScrollBar->ViewPort().Height() - 8) * startIndex / totalNumItems; + pixmapScrollBar->DrawImage(cPoint(4, 2 + offset), *imgScrollBar); +} + +int cRecMenu::GetIntValue(int itemNumber) { + cRecMenuItem *item = NULL; + item = menuItems.Get(itemNumber); + if (item) { + return item->GetIntValue(); + } + return -1; +} + +time_t cRecMenu::GetTimeValue(int itemNumber) { + cRecMenuItem *item = NULL; + item = menuItems.Get(itemNumber); + if (item) { + return item->GetTimeValue(); + } + return 0; +} + +bool cRecMenu::GetBoolValue(int itemNumber) { + cRecMenuItem *item = NULL; + item = menuItems.Get(itemNumber); + if (item) { + return item->GetBoolValue(); + } + return false; +} + +cString cRecMenu::GetStringValue(int itemNumber) { + cRecMenuItem *item = NULL; + item = menuItems.Get(itemNumber); + if (item) { + return item->GetStringValue(); + } + return cString(""); +} + +const cEvent *cRecMenu::GetEventValue(int itemNumber) { + cRecMenuItem *item = NULL; + item = menuItems.Get(itemNumber); + if (item) { + return item->GetEventValue(); + } + return NULL; +} + +eRecMenuState cRecMenu::ProcessKey(eKeys Key) { + cRecMenuItem *activeItem = GetActiveMenuItem(); + eRecMenuState state = rmsContinue; + if (!activeItem) + return state; + + state = activeItem->ProcessKey(Key); + if (state == rmsRefresh) { + CreateMenuItems(); + Display(); + } else if (state == rmsNotConsumed) { + switch (Key & ~k_Repeat) { + case kUp: + if (!ActivatePrev() && scrollable) + ScrollUp(); + state = rmsConsumed; + break; + case kDown: + if (!ActivateNext() && scrollable) + ScrollDown(); + state = rmsConsumed; + break; + case kLeft: + JumpBegin(); + state = rmsConsumed; + break; + case kRight: + JumpEnd(); + state = rmsConsumed; + break; + default: + break; + } + } + return state; +} + +cImage *cRecMenu::createScrollbar(int width, int height, tColor clrBgr, tColor clrBlend) { + cImage *image = new cImage(cSize(width, height)); + image->Fill(clrBgr); + if (tvguideConfig.useBlending) { + int numSteps = 64; + int alphaStep = 0x03; + if (height < 30) + return image; + else if (height < 100) { + numSteps = 32; + alphaStep = 0x06; + } + int stepY = 0.5*height / numSteps; + if (stepY == 0) + stepY = 1; + int alpha = 0x40; + tColor clr; + for (int i = 0; iSetPixel(cPoint(x,y), clr); + } + } + alpha += alphaStep; + } + } + return image; +} diff --git a/recmenu.h b/recmenu.h new file mode 100644 index 0000000..87d3ef5 --- /dev/null +++ b/recmenu.h @@ -0,0 +1,61 @@ +#ifndef __TVGUIDE_RECMENU_H +#define __TVGUIDE_RECMENU_H + +// --- cRecMenu ------------------------------------------------------------- + +class cRecMenu : public cStyledPixmap { +protected: + int x, y; + int width, height; + int headerHeight, footerHeight; + int scrollHeight; + int scrollItemHeight; + int scrollbarWidth; + cPixmap *pixmapScrollBar; + cImage *imgScrollBar; + int border; + bool scrollable; + int numItems; + int startIndex, stopIndex; + cRecMenuItem *header; + cRecMenuItem *footer; + cList menuItems; + void SetWidthPercent(int percentOSDWidth); + void SetWidthPixel(int pixel); + int CalculateOptimalWidth(void); + bool CheckHeight(void); + void CalculateHeight(void); + void CreatePixmap(void); + void SetHeader(cRecMenuItem *header); + void SetFooter(cRecMenuItem *footer); + void AddMenuItemScroll(cRecMenuItem *item); + void AddMenuItem(cRecMenuItem *item, cRecMenuItem *before = NULL); + cRecMenuItem *GetActiveMenuItem(void); + bool ActivateNext(void); + bool ActivatePrev(void); + void Activate(cRecMenuItem *itemOld, cRecMenuItem *item); + void ScrollUp(void); + void ScrollDown(void); + void JumpBegin(void); + void JumpEnd(void); + void DrawScrollBar(void); + cImage *createScrollbar(int width, int height, tColor clrBgr, tColor clrBlend); + void Arrange(bool scroll = false); + virtual cRecMenuItem *GetMenuItem(int number) { return NULL; }; + virtual int GetTotalNumMenuItems(void) { return 0; }; + virtual void CreateMenuItems(void) {}; +public: + cRecMenu(void); + virtual ~cRecMenu(void); + void Display(bool scroll = false); + void Hide(void); + void Show(void); + int GetActive(bool withOffset); + int GetIntValue(int itemNumber); + time_t GetTimeValue(int itemNumber); + bool GetBoolValue(int itemNumber); + cString GetStringValue(int itemNumber); + const cEvent *GetEventValue(int itemNumber); + eRecMenuState ProcessKey(eKeys Key); +}; +#endif //__TVGUIDE_RECMENU_H \ No newline at end of file diff --git a/recmenuitem.c b/recmenuitem.c new file mode 100644 index 0000000..eb931e9 --- /dev/null +++ b/recmenuitem.c @@ -0,0 +1,2016 @@ +#include +#include +#include "recmenuitem.h" + +// --- cRecMenuItem ------------------------------------------------------------- + +cRecMenuItem::cRecMenuItem(void) { + height = 0; + action = rmsNotConsumed; + drawn = false; + font = tvguideConfig.FontRecMenuItem; + fontSmall = tvguideConfig.FontRecMenuItemSmall; +} + +cRecMenuItem::~cRecMenuItem(void) { +} + +void cRecMenuItem::SetGeometry(int x, int y, int width) { + this->x = x; + this->y = y; + this->width = width; + +} + +void cRecMenuItem::SetPixmaps(void) { + if (!pixmap) + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + else + pixmap->SetViewPort(cRect(x, y, width, height)); +} + +void cRecMenuItem::setBackground(void) { + if (active) { + color = theme.Color(clrHighlight); + colorBlending = theme.Color(clrHighlightBlending); + colorText = theme.Color(clrFontActive); + } else { + color = theme.Color(clrGrid1); + colorBlending = theme.Color(clrGrid1Blending); + colorText = theme.Color(clrFont); + } + colorTextBack = (tvguideConfig.useBlending==0)?color:clrTransparent; + drawBackground(); + drawBorder(); +} + +// --- cRecMenuItemButton ------------------------------------------------------- + +cRecMenuItemButton::cRecMenuItemButton(const char *text, eRecMenuState action, bool active, bool halfWidth) { + selectable = true; + this->text = text; + this->action = action; + this->active = active; + height = 3 * font->Height() / 2; + this->halfWidth = halfWidth; +} + +cRecMenuItemButton::~cRecMenuItemButton(void) { +} + +int cRecMenuItemButton::GetWidth(void) { + return font->Width(*text); +} + +void cRecMenuItemButton::SetPixmaps(void) { + if (halfWidth) { + x += width / 4; + width = width / 2; + } + if (!pixmap) + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + else + pixmap->SetViewPort(cRect(x, y, width, height)); +} + +void cRecMenuItemButton::Draw(void) { + int y = (height - font->Height()) / 2; + int x = (width - font->Width(*text)) / 2;; + pixmap->DrawText(cPoint(x, y), *text, colorText, colorTextBack, font); +} + +eRecMenuState cRecMenuItemButton::ProcessKey(eKeys Key) { + switch (Key & ~k_Repeat) { + case kOk: + return action; + break; + default: + break; + } + return rmsNotConsumed; +} + + +// --- cRecMenuItemButtonYesNo ------------------------------------------------------- +cRecMenuItemButtonYesNo::cRecMenuItemButtonYesNo(cString textYes, + cString textNo, + eRecMenuState actionYes, + eRecMenuState actionNo, + bool active) { + selectable = true; + this->textYes = textYes; + this->textNo = textNo; + this->action = actionYes; + this->actionNo = actionNo; + this->active = active; + yesActive = true; + height = 3 * font->Height() / 2; + pixmapNo = NULL; +} + +cRecMenuItemButtonYesNo::~cRecMenuItemButtonYesNo(void) { + if (pixmapNo) + delete pixmapNo; +} + +void cRecMenuItemButtonYesNo::SetPixmaps(void) { + int buttonWidth = 44 * width / 100; + int yesX = x + width / 25; + int noX = x + 52 * width / 100; + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(yesX, y, buttonWidth, height)); + pixmapNo = new cStyledPixmap(osdManager.requestPixmap(4, cRect(noX, y, buttonWidth, height))); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapNo->SetViewPort(cRect(x, y, width, height)); + } +} + +void cRecMenuItemButtonYesNo::Hide(void) { + pixmap->SetLayer(-1); + pixmapNo->SetLayer(-1); +} + +void cRecMenuItemButtonYesNo::Show(void) { + pixmap->SetLayer(4); + pixmapNo->SetLayer(4); +} + +void cRecMenuItemButtonYesNo::setBackground() { + if (active) { + if (yesActive) { + color = theme.Color(clrHighlight); + colorBlending = theme.Color(clrHighlightBlending); + colorText = theme.Color(clrFontActive); + pixmapNo->setColor( theme.Color(clrGrid1), + theme.Color(clrGrid1Blending)); + colorTextNo = theme.Color(clrFont); + } else { + color = theme.Color(clrGrid1); + colorBlending = theme.Color(clrGrid1Blending); + colorText = theme.Color(clrFont); + pixmapNo->setColor( theme.Color(clrHighlight), + theme.Color(clrHighlightBlending)); + colorTextNo = theme.Color(clrFontActive); + } + } else { + color = theme.Color(clrGrid1); + colorBlending = theme.Color(clrGrid1Blending); + colorText = theme.Color(clrFont); + pixmapNo->setColor( theme.Color(clrGrid1), + theme.Color(clrGrid1Blending)); + colorTextNo = theme.Color(clrFont); + } + colorTextBack = (tvguideConfig.useBlending==0)?color:clrTransparent; + drawBackground(); + drawBorder(); + pixmapNo->drawBackground(); + pixmapNo->drawBorder(); +} + +void cRecMenuItemButtonYesNo::Draw(void) { + int textYesX = (pixmap->ViewPort().Width() - font->Width(*textYes)) / 2; + int textNoX = (pixmapNo->Width() - font->Width(*textNo)) / 2; + int textY = (height - font->Height()) / 2; + pixmap->DrawText(cPoint(textYesX, textY), *textYes, colorText, clrTransparent, font); + pixmapNo->DrawText(cPoint(textNoX, textY), *textNo, colorTextNo, clrTransparent, font); +} + +eRecMenuState cRecMenuItemButtonYesNo::ProcessKey(eKeys Key) { + switch (Key & ~k_Repeat) { + case kLeft: + if (!yesActive) { + yesActive = true; + setBackground(); + Draw(); + return rmsConsumed; + } else + return rmsNotConsumed; + break; + case kRight: + if (yesActive) { + yesActive = false; + setBackground(); + Draw(); + return rmsConsumed; + } else + return rmsNotConsumed; + break; + case kOk: + if (yesActive) + return action; + return actionNo; + break; + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemInfo ------------------------------------------------------- +cRecMenuItemInfo::cRecMenuItemInfo(const char *text) { + selectable = false; + active = false; + this->text = text; + border = 10; +} + +cRecMenuItemInfo::~cRecMenuItemInfo(void) { +} + +void cRecMenuItemInfo::CalculateHeight(int textWidth) { + wrapper.Set(*text, font, textWidth); + height = font->Height() * wrapper.Lines() + 2*border; +} + +void cRecMenuItemInfo::setBackground(void) { + pixmap->Fill(clrTransparent); +} + +void cRecMenuItemInfo::Draw(void) { + int lines = wrapper.Lines(); + int lineHeight = font->Height(); + int x = 0; + int y = border; + for (int i = 0; i < lines; i++) { + x = (width - font->Width(wrapper.GetLine(i))) / 2; + pixmap->DrawText(cPoint(x,y), wrapper.GetLine(i), theme.Color(clrFont), clrTransparent, font); + y += lineHeight; + } +} + +// --- cRecMenuItemInt ------------------------------------------------------- +cRecMenuItemInt::cRecMenuItemInt(cString text, + int initialVal, + int minVal, + int maxVal, + bool active) { + selectable = true; + this->text = text; + this->currentVal = initialVal; + this->minVal = minVal; + this->maxVal = maxVal; + this->active = active; + height = 3 * font->Height() / 2; + pixmapVal = NULL; + fresh = true; +} + +cRecMenuItemInt::~cRecMenuItemInt(void) { + if (pixmapVal) + osdManager.releasePixmap(pixmapVal); +} + +void cRecMenuItemInt::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapVal = osdManager.requestPixmap(5, cRect(x, y, width, height)); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapVal->SetViewPort(cRect(x, y, width, height)); + } +} + +void cRecMenuItemInt::Hide(void) { + pixmap->SetLayer(-1); + pixmapVal->SetLayer(-1); +} + +void cRecMenuItemInt::Show(void) { + pixmap->SetLayer(4); + pixmapVal->SetLayer(5); +} + +void cRecMenuItemInt::setBackground() { + cRecMenuItem::setBackground(); + fresh = true; +} + +void cRecMenuItemInt::Draw(void) { + int textY = (height - font->Height()) / 2; + pixmap->DrawText(cPoint(10, textY), *text, colorText, colorTextBack, font); + DrawValue(); +} + +void cRecMenuItemInt::DrawValue(void) { + pixmapVal->Fill(clrTransparent); + cString textVal = cString::sprintf("%d", currentVal); + int textX = width - font->Width(*textVal) - 10; + int textY = (height - font->Height()) / 2; + pixmapVal->DrawText(cPoint(textX, textY), *textVal, colorText, clrTransparent, font); +} + +eRecMenuState cRecMenuItemInt::ProcessKey(eKeys Key) { + int oldValue = currentVal; + switch (Key & ~k_Repeat) { + case kLeft: + fresh = true; + if (currentVal > minVal) { + currentVal--; + DrawValue(); + } + return rmsConsumed; + break; + case kRight: + fresh = true; + if (currentVal < maxVal) { + currentVal++; + DrawValue(); + } + return rmsConsumed; + break; + case k0 ... k9: + if (fresh) { + currentVal = 0; + fresh = false; + } + currentVal = currentVal * 10 + (Key - k0); + if (!((currentVal >= minVal) && (currentVal <= maxVal))) + currentVal = oldValue; + DrawValue(); + return rmsConsumed; + break; + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemBool ------------------------------------------------------- +cRecMenuItemBool::cRecMenuItemBool(cString text, + bool initialVal, + bool refresh, + bool active) { + selectable = true; + this->text = text; + this->yes = initialVal; + this->refresh = refresh; + this->active = active; + height = 3 * font->Height() / 2; + pixmapVal = NULL; +} + +cRecMenuItemBool::~cRecMenuItemBool(void) { + if (pixmapVal) + osdManager.releasePixmap(pixmapVal); +} + +void cRecMenuItemBool::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapVal = osdManager.requestPixmap(5, cRect(x, y, width, height)); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapVal->SetViewPort(cRect(x, y, width, height)); + } +} + +void cRecMenuItemBool::Hide(void) { + pixmap->SetLayer(-1); + pixmapVal->SetLayer(-1); +} + +void cRecMenuItemBool::Show(void) { + pixmap->SetLayer(4); + pixmapVal->SetLayer(5); +} + +void cRecMenuItemBool::Draw(void) { + int textY = (height - font->Height()) / 2; + pixmap->DrawText(cPoint(10, textY), *text, colorText, colorTextBack, font); + DrawValue(); +} + +void cRecMenuItemBool::DrawValue(void) { + pixmapVal->Fill(clrTransparent); + cString strIcon = yes?"yes":"no"; + int iconSize = height - 8; + int iconX = width - iconSize - 10; + int iconY = (height - iconSize) / 2; + cImageLoader imgLoader; + if (imgLoader.LoadIcon(strIcon, iconSize)) { + cImage icon = imgLoader.GetImage(); + pixmapVal->DrawImage(cPoint(iconX, iconY), icon); + } +} + +eRecMenuState cRecMenuItemBool::ProcessKey(eKeys Key) { + switch (Key & ~k_Repeat) { + case kLeft: + case kRight: + yes = !yes; + DrawValue(); + if (refresh) + return rmsRefresh; + else + return rmsConsumed; + break; + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemSelect ------------------------------------------------------- +cRecMenuItemSelect::cRecMenuItemSelect(cString text, + const char * const *Strings, + int initialVal, + int numValues, + bool active) { + selectable = true; + this->text = text; + strings = Strings; + this->numValues = numValues; + if ((initialVal < 0) || (initialVal > numValues-1)) + this->currentVal = 0; + else + this->currentVal = initialVal; + this->active = active; + height = 3 * font->Height() / 2; + pixmapVal = NULL; +} + +cRecMenuItemSelect::~cRecMenuItemSelect(void) { + if (pixmapVal) + osdManager.releasePixmap(pixmapVal); +} + +void cRecMenuItemSelect::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapVal = osdManager.requestPixmap(5, cRect(x, y, width, height)); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapVal->SetViewPort(cRect(x, y, width, height)); + } +} + +void cRecMenuItemSelect::Hide(void) { + pixmap->SetLayer(-1); + pixmapVal->SetLayer(-1); +} + +void cRecMenuItemSelect::Show(void) { + pixmap->SetLayer(4); + pixmapVal->SetLayer(5); +} + +void cRecMenuItemSelect::Draw(void) { + int textY = (height - font->Height()) / 2; + pixmap->DrawText(cPoint(10, textY), *text, colorText, colorTextBack, font); + DrawValue(); +} + +void cRecMenuItemSelect::DrawValue(void) { + pixmapVal->Fill(clrTransparent); + const char *textVal = strings[currentVal]; + int iconSize = min(128, height); + int textX = width - font->Width(textVal) - iconSize; + int textY = (height - font->Height()) / 2; + pixmapVal->DrawText(cPoint(textX, textY), textVal, colorText, clrTransparent, font); + int iconLeftX = textX - iconSize; + int iconRightX = width - iconSize; + int iconY = (height - iconSize) / 2; + cImageLoader imgLoader; + if (imgLoader.LoadIcon("arrow_left", iconSize)) { + cImage icon = imgLoader.GetImage(); + pixmapVal->DrawImage(cPoint(iconLeftX, iconY), icon); + } + if (imgLoader.LoadIcon("arrow_right", iconSize)) { + cImage icon = imgLoader.GetImage(); + pixmapVal->DrawImage(cPoint(iconRightX, iconY), icon); + } +} + +eRecMenuState cRecMenuItemSelect::ProcessKey(eKeys Key) { + int oldValue = currentVal; + switch (Key & ~k_Repeat) { + case kLeft: + currentVal--; + if (currentVal<0) + currentVal = numValues - 1; + DrawValue(); + return rmsConsumed; + break; + case kRight: + currentVal = (currentVal+1)%numValues; + DrawValue(); + return rmsConsumed; + break; + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemText ------------------------------------------------------- +cRecMenuItemText::cRecMenuItemText(cString title, + char *initialVal, + int length, + bool active) { + selectable = true; + this->title = title; + value = initialVal; + this->active = active; + height = 3 * font->Height(); + pixmapVal = NULL; + pixmapKeyboard = NULL; + pixmapKeyboardHighlight = NULL; + pixmapKeyboardIcons = NULL; + keyboardWidth = 50; + gridHeight = 3 * fontSmall->Height(); + keyboardHeight = 5 * gridHeight; + + this->length = length; + allowed = trVDR(FileNameChars); + pos = -1; + offset = 0; + insert = uppercase = false; + newchar = true; + lengthUtf8 = 0; + valueUtf8 = NULL; + allowedUtf8 = NULL; + charMapUtf8 = NULL; + currentCharUtf8 = NULL; + lastKey = kNone; + keyboardDrawn = false; +} + +cRecMenuItemText::~cRecMenuItemText(void) { + if (pixmapVal) + osdManager.releasePixmap(pixmapVal); + if (pixmapKeyboard) + delete pixmapKeyboard; + if (pixmapKeyboardHighlight) + osdManager.releasePixmap(pixmapKeyboardHighlight); + if (pixmapKeyboardIcons) + osdManager.releasePixmap(pixmapKeyboardIcons); + delete[] valueUtf8; + delete[] allowedUtf8; + delete[] charMapUtf8; +} + +void cRecMenuItemText::SetPixmaps(void) { + int xPixmapVal = x + 20; + int yPixmapVal = y + height / 2 + 10; + int widthPixmapVal = width - 40; + int heightPixmapVal = height / 2 - 20; + int keyboardX = x + (100 - keyboardWidth)*width / 100; + int keyboardY = y + height; + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapVal = osdManager.requestPixmap(5, cRect(xPixmapVal, yPixmapVal, widthPixmapVal, heightPixmapVal)); + pixmapKeyboard = new cStyledPixmap(osdManager.requestPixmap(-1, cRect(keyboardX, keyboardY, keyboardWidth*width/100, keyboardHeight))); + pixmapKeyboardHighlight = osdManager.requestPixmap(-1, cRect(keyboardX, keyboardY, keyboardWidth*width/100, keyboardHeight)); + pixmapKeyboardIcons = osdManager.requestPixmap(-1, cRect(keyboardX, keyboardY, keyboardWidth*width/100, keyboardHeight)); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapVal->SetViewPort(cRect(xPixmapVal, yPixmapVal, widthPixmapVal, heightPixmapVal)); + pixmapKeyboard->SetViewPort(cRect(keyboardX, keyboardY, keyboardWidth*width/100, keyboardHeight)); + pixmapKeyboardHighlight->SetViewPort(cRect(keyboardX, keyboardY, keyboardWidth*width/100, keyboardHeight)); + pixmapKeyboardIcons->SetViewPort(cRect(keyboardX, keyboardY, keyboardWidth*width/100, keyboardHeight)); + } + pixmapKeyboardIcons->Fill(clrTransparent); +} + +void cRecMenuItemText::Hide(void) { + pixmap->SetLayer(-1); + pixmapVal->SetLayer(-1); + pixmapKeyboard->SetLayer(-1); + pixmapKeyboardHighlight->SetLayer(-1); + pixmapKeyboardIcons->SetLayer(-1); +} + +void cRecMenuItemText::Show(void) { + pixmap->SetLayer(4); + pixmapVal->SetLayer(5); +} + +void cRecMenuItemText::setBackground() { + if (!active) { + DeactivateKeyboard(); + } + cRecMenuItem::setBackground(); +} + +void cRecMenuItemText::Draw(void) { + int textY = (height/2 - font->Height()) / 2; + pixmap->DrawText(cPoint(10, textY), *title, colorText, colorTextBack, font); + DrawValue(value); +} + +void cRecMenuItemText::DrawValue(char *newValue) { + tColor clrBack = InEditMode()?theme.Color(clrRecMenuTextActiveBack):theme.Color(clrRecMenuTextBack); + pixmapVal->Fill(clrBack); + int textX = pixmapVal->DrawPort().Width() - font->Width(newValue) - 10; + int textY = (pixmapVal->DrawPort().Height() - font->Height()) / 2; + pixmapVal->DrawText(cPoint(textX, textY), newValue, colorText, clrTransparent, font); +} + +void cRecMenuItemText::ActivateKeyboard(void) { + pixmapKeyboard->SetLayer(6); + pixmapKeyboardHighlight->SetLayer(6); + pixmapKeyboardIcons->SetLayer(6); + pixmapKeyboardHighlight->Fill(clrTransparent); + + if (keyboardDrawn) + return; + + pixmapKeyboard->setColor(theme.Color(clrRecMenuKeyboardBack), theme.Color(clrRecMenuKeyboardBack)); + pixmapKeyboard->drawBackground(); + + int widthKeyBoard = pixmapKeyboard->Width(); + gridWidth = widthKeyBoard / 3; + + pixmapKeyboard->DrawRectangle(cRect(0, 0, widthKeyBoard, keyboardHeight), theme.Color(clrRecMenuKeyboardBorder)); + int num = 1; + for (int row = 0; row < 5; row++) { + for (int col = 0; col < 3; col++) { + int X = col*gridWidth; + int Y = row*gridHeight; + tColor clrBack = theme.Color(clrRecMenuKeyboardBack); + if (num==13) + clrBack = theme.Color(clrButtonRedKeyboard); + else if (num==14) + clrBack = theme.Color(clrButtonGreenKeyboard); + else if (num==15) + clrBack = theme.Color(clrButtonYellowKeyboard); + pixmapKeyboard->DrawRectangle(cRect(X+2, Y+2, gridWidth-4, gridHeight-4), clrBack); + pixmapKeyboard->DrawEllipse(cRect(X, Y, 20, 20), theme.Color(clrRecMenuKeyboardBorder),-2); + pixmapKeyboard->DrawEllipse(cRect(X, Y+gridHeight-20, 20, 20), theme.Color(clrRecMenuKeyboardBorder),-3); + pixmapKeyboard->DrawEllipse(cRect(X+gridWidth-20, Y+gridHeight-20, 20, 20), theme.Color(clrRecMenuKeyboardBorder),-4); + pixmapKeyboard->DrawEllipse(cRect(X+gridWidth-20, Y, 20, 20), theme.Color(clrRecMenuKeyboardBorder),-1); + bool draw = false; + bool drawIcon = false; + cString strNum; + cString strIcon; + cImageLoader imgLoader; + if (num<10) { + strNum = *cString::sprintf("%d", num); + draw = true; + } else if (num == 11) { + strNum = *cString::sprintf("%d", 0); + draw = true; + } else if (num==13) { + strIcon = "icon_shift"; + drawIcon = true; + } else if (num==14) { + strIcon = "icon_del_ins"; + drawIcon = true; + } else if (num==15) { + strIcon = "icon_backspace"; + drawIcon = true; + } + if (draw) { + int numX = X + (gridWidth - font->Width(*strNum))/2; + int numY = Y + font->Height() / 4; + pixmapKeyboard->DrawText(cPoint(numX, numY), *strNum, colorText, colorTextBack, font); + char *smsKeys = GetSMSKeys(num); + int smsKeysX = X + (gridWidth - fontSmall->Width(smsKeys))/2; + int smsKeysY = Y + gridHeight - fontSmall->Height() - 10; + pixmapKeyboard->DrawText(cPoint(smsKeysX, smsKeysY), smsKeys, theme.Color(clrRecMenuKeyboardBorder), colorTextBack, fontSmall); + delete[] smsKeys; + } + if (drawIcon) { + int iconSize = gridHeight - 10; + if (imgLoader.LoadIcon(strIcon, iconSize)) { + cImage icon = imgLoader.GetImage(); + int iconX = X + (gridWidth - iconSize) / 2; + pixmapKeyboardIcons->DrawImage(cPoint(iconX, Y + 5), icon); + } + } + num++; + } + } + keyboardDrawn = true; +} + +void cRecMenuItemText::DeactivateKeyboard(void) { + pixmapKeyboard->SetLayer(-1); + pixmapKeyboardHighlight->SetLayer(-1); + pixmapKeyboardIcons->SetLayer(-1); +} + +void cRecMenuItemText::HighlightSMSKey(int num) { + int x = 0; + int y = 0; + if (num == 0) { + x = gridWidth; + y = 3 * gridHeight; + } else { + x = (num-1)%3 * gridWidth; + y = (num-1)/3 * gridHeight; + } + pixmapKeyboardHighlight->DrawRectangle(cRect(x, y, gridWidth, gridHeight), theme.Color(clrRecMenuKeyboardHigh)); +} + +void cRecMenuItemText::ClearSMSKey(void) { + pixmapKeyboardHighlight->Fill(clrTransparent); +} + + +char *cRecMenuItemText::GetSMSKeys(int num) { + if (num == 11) + num = 0; + if (num > 9) + return NULL; + + currentCharUtf8 = charMapUtf8; + int pos = num; + while (pos > 0 && *currentCharUtf8) { + if (*currentCharUtf8++ == '\t') + pos--; + } + while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8)) { + currentCharUtf8++; + } + uint *startCharUtf8 = currentCharUtf8; + int numChars = 0; + bool specialChar = false; + while(*currentCharUtf8 && *currentCharUtf8 != '\t' && IsAllowed(*currentCharUtf8)) { + currentCharUtf8++; + if (Utf8CharSet(*currentCharUtf8) > 1) + specialChar = true; + numChars++; + } + char buf[100]; + char *p = buf; + int addition = 0; + if (specialChar) + addition = 1; + Utf8FromArray(startCharUtf8, p, numChars+addition); + int maxChars = min(numChars+1+addition, 8); + char *smskey = new char[maxChars]; + Utf8Strn0Cpy(smskey, p, maxChars); + return smskey; +} + +void cRecMenuItemText::EnterEditMode(void) { + if (!valueUtf8) { + valueUtf8 = new uint[length]; + lengthUtf8 = Utf8ToArray(value, valueUtf8, length); + int l = strlen(allowed) + 1; + allowedUtf8 = new uint[l]; + Utf8ToArray(allowed, allowedUtf8, l); + const char *charMap = trVDR("CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9"); + l = strlen(charMap) + 1; + charMapUtf8 = new uint[l]; + Utf8ToArray(charMap, charMapUtf8, l); + currentCharUtf8 = charMapUtf8; + AdvancePos(); + } +} + +void cRecMenuItemText::LeaveEditMode(bool SaveValue) { + if (valueUtf8) { + if (SaveValue) { + Utf8FromArray(valueUtf8, value, length); + stripspace(value); + } + lengthUtf8 = 0; + delete[] valueUtf8; + valueUtf8 = NULL; + delete[] allowedUtf8; + allowedUtf8 = NULL; + delete[] charMapUtf8; + charMapUtf8 = NULL; + pos = -1; + offset = 0; + newchar = true; + } +} + +void cRecMenuItemText::AdvancePos(void) { + if (pos < length - 2 && pos < lengthUtf8) { + if (++pos >= lengthUtf8) { + if (pos >= 2 && valueUtf8[pos - 1] == ' ' && valueUtf8[pos - 2] == ' ') + pos--; // allow only two blanks at the end + else { + valueUtf8[pos] = ' '; + valueUtf8[pos + 1] = 0; + lengthUtf8++; + } + } + } + newchar = true; + if (!insert && Utf8is(alpha, valueUtf8[pos])) + uppercase = Utf8is(upper, valueUtf8[pos]); +} + +uint cRecMenuItemText::Inc(uint c, bool Up) { + uint *p = IsAllowed(c); + if (!p) + p = allowedUtf8; + if (Up) { + if (!*++p) + p = allowedUtf8; + } else if (--p < allowedUtf8) { + p = allowedUtf8; + while (*p && *(p + 1)) + p++; + } + return *p; +} + +void cRecMenuItemText::Type(uint c) { + if (insert && lengthUtf8 < length - 1) + Insert(); + valueUtf8[pos] = c; + if (pos < length - 2) + pos++; + if (pos >= lengthUtf8) { + valueUtf8[pos] = ' '; + valueUtf8[pos + 1] = 0; + lengthUtf8 = pos + 1; + } +} + +void cRecMenuItemText::Insert(void) { + memmove(valueUtf8 + pos + 1, valueUtf8 + pos, (lengthUtf8 - pos + 1) * sizeof(*valueUtf8)); + lengthUtf8++; + valueUtf8[pos] = ' '; +} + +void cRecMenuItemText::Delete(void) { + memmove(valueUtf8 + pos, valueUtf8 + pos + 1, (lengthUtf8 - pos) * sizeof(*valueUtf8)); + lengthUtf8--; +} + +uint *cRecMenuItemText::IsAllowed(uint c) { + if (allowedUtf8) { + for (uint *a = allowedUtf8; *a; a++) { + if (c == *a) + return a; + } + } + return NULL; +} + +void cRecMenuItemText::SetText(void) { + if (InEditMode()) { + int textAreaWidth = width - 20; + textAreaWidth -= font->Width("[]"); + textAreaWidth -= font->Width("<>"); // reserving this anyway make the whole thing simpler + if (pos < offset) + offset = pos; + int WidthFromOffset = 0; + int EndPos = lengthUtf8; + for (int i = offset; i < lengthUtf8; i++) { + WidthFromOffset += font->Width(valueUtf8[i]); + if (WidthFromOffset > textAreaWidth) { + if (pos >= i) { + do { + WidthFromOffset -= font->Width(valueUtf8[offset]); + offset++; + } while (WidthFromOffset > textAreaWidth && offset < pos); + EndPos = pos + 1; + } else { + EndPos = i; + break; + } + } + } + char buf[1000]; + char *p = buf; + if (offset) + *p++ = '<'; + p += Utf8FromArray(valueUtf8 + offset, p, sizeof(buf) - (p - buf), pos - offset); + *p++ = '['; + if (insert && newchar) + *p++ = ']'; + p += Utf8FromArray(&valueUtf8[pos], p, sizeof(buf) - (p - buf), 1); + if (!(insert && newchar)) + *p++ = ']'; + p += Utf8FromArray(&valueUtf8[pos + 1], p, sizeof(buf) - (p - buf), EndPos - pos - 1); + if (EndPos != lengthUtf8) + *p++ = '>'; + *p = 0; + DrawValue(buf); + } else { + DrawValue(value); + } +} + +eRecMenuState cRecMenuItemText::ProcessKey(eKeys Key) { + bool consumed = false; + bool SameKey = NORMALKEY(Key) == lastKey; + ClearSMSKey(); + if (Key != kNone) { + lastKey = NORMALKEY(Key); + } else if (!newchar && k0 <= lastKey && lastKey <= k9 && autoAdvanceTimeout.TimedOut()) { + AdvancePos(); + newchar = true; + currentCharUtf8 = NULL; + SetText(); + return rmsConsumed; + } + + switch ((int)Key) { + case kRed: // Switch between upper- and lowercase characters + if (InEditMode()) { + if (!insert || !newchar) { + uppercase = !uppercase; + valueUtf8[pos] = uppercase ? Utf8to(upper, valueUtf8[pos]) : Utf8to(lower, valueUtf8[pos]); + } + consumed = true; + } + break; + case kGreen: // Toggle insert/overwrite modes + if (InEditMode()) { + insert = !insert; + newchar = true; + consumed = true; + } + break; + case kYellow|k_Repeat: + case kYellow: // Remove the character at the current position; in insert mode it is the character to the right of the cursor + if (InEditMode()) { + if (lengthUtf8 > 1) { + if (!insert || pos < lengthUtf8 - 1) + Delete(); + else if (insert && pos == lengthUtf8 - 1) + valueUtf8[pos] = ' '; // in insert mode, deleting the last character replaces it with a blank to keep the cursor position + // reduce position, if we removed the last character + if (pos == lengthUtf8) + pos--; + } else if (lengthUtf8 == 1) + valueUtf8[0] = ' '; // This is the last character in the string, replace it with a blank + if (Utf8is(alpha, valueUtf8[pos])) + uppercase = Utf8is(upper, valueUtf8[pos]); + newchar = true; + consumed = true; + } + break; + case kLeft|k_Repeat: + case kLeft: + + if (pos > 0) { + if (!insert || newchar) + pos--; + newchar = true; + if (!insert && Utf8is(alpha, valueUtf8[pos])) + uppercase = Utf8is(upper, valueUtf8[pos]); + } + consumed = true; + break; + case kRight|k_Repeat: + case kRight: + if (InEditMode()) { + AdvancePos(); + } else { + EnterEditMode(); + ActivateKeyboard(); + } + consumed = true; + break; + case kUp|k_Repeat: + case kUp: + case kDown|k_Repeat: + case kDown: + if (InEditMode()) { + if (insert && newchar) { + // create a new character in insert mode + if (lengthUtf8 < length - 1) + Insert(); + } + if (uppercase) + valueUtf8[pos] = Utf8to(upper, Inc(Utf8to(lower, valueUtf8[pos]), NORMALKEY(Key) == kUp)); + else + valueUtf8[pos] = Inc( valueUtf8[pos], NORMALKEY(Key) == kUp); + newchar = false; + consumed = true; + } + break; + case k0|k_Repeat ... k9|k_Repeat: + case k0 ... k9: { + if (InEditMode()) { + if (Setup.NumberKeysForChars) { + HighlightSMSKey(NORMALKEY(Key) - k0); + if (!SameKey) { + if (!newchar) + AdvancePos(); + currentCharUtf8 = NULL; + } + if (!currentCharUtf8 || !*currentCharUtf8 || *currentCharUtf8 == '\t') { + // find the beginning of the character map entry for Key + int n = NORMALKEY(Key) - k0; + currentCharUtf8 = charMapUtf8; + while (n > 0 && *currentCharUtf8) { + if (*currentCharUtf8++ == '\t') + n--; + } + // find first allowed character + while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8)) + currentCharUtf8++; + } + if (*currentCharUtf8 && *currentCharUtf8 != '\t') { + if (insert && newchar) { + // create a new character in insert mode + if (lengthUtf8 < length - 1) + Insert(); + } + valueUtf8[pos] = *currentCharUtf8; + if (uppercase) + valueUtf8[pos] = Utf8to(upper, valueUtf8[pos]); + // find next allowed character + do { + currentCharUtf8++; + } while (*currentCharUtf8 && *currentCharUtf8 != '\t' && !IsAllowed(*currentCharUtf8)); + newchar = false; + autoAdvanceTimeout.Set(AUTO_ADVANCE_TIMEOUT); + } + } else { + Type('0' + NORMALKEY(Key) - k0); + } + consumed = true; + } + break; } + case kBack: + case kOk: + if (InEditMode()) { + LeaveEditMode(Key == kOk); + DeactivateKeyboard(); + } else { + EnterEditMode(); + ActivateKeyboard(); + } + consumed = true; + break; + default: + if (InEditMode() && BASICKEY(Key) == kKbd) { + int c = KEYKBD(Key); + if (c <= 0xFF) { + if (IsAllowed(Utf8to(lower, c))) + Type(c); + else { + switch (c) { + case 0x7F: // backspace + if (pos > 0) { + pos--; + ProcessKey(kYellow); + } + break; + default: ; + } + } + } else { + switch (c) { + case kfHome: pos = 0; break; + case kfEnd: pos = lengthUtf8 - 1; break; + case kfIns: ProcessKey(kGreen); + case kfDel: ProcessKey(kYellow); + default: ; + } + } + consumed = true; + } + break; + } + SetText(); + if (consumed) + return rmsConsumed; + return rmsNotConsumed; +} + +// --- cRecMenuItemTime ------------------------------------------------------- +cRecMenuItemTime::cRecMenuItemTime(cString text, + int initialVal, + bool active) { + selectable = true; + this->text = text; + this->value = initialVal; + hh = value / 100; + mm = value % 100; + pos = 0; + fresh = true; + this->active = active; + height = 3 * font->Height() / 2; + pixmapVal = NULL; +} + +cRecMenuItemTime::~cRecMenuItemTime(void) { + if (pixmapVal) + osdManager.releasePixmap(pixmapVal); +} + +void cRecMenuItemTime::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapVal = osdManager.requestPixmap(5, cRect(x, y, width, height)); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapVal->SetViewPort(cRect(x, y, width, height)); + } +} + +void cRecMenuItemTime::Hide(void) { + pixmap->SetLayer(-1); + pixmapVal->SetLayer(-1); +} + +void cRecMenuItemTime::Show(void) { + pixmap->SetLayer(4); + pixmapVal->SetLayer(5); +} + +void cRecMenuItemTime::Draw(void) { + int textY = (height - font->Height()) / 2; + pixmap->DrawText(cPoint(10, textY), *text, colorText, colorTextBack, font); + DrawValue(); +} + +void cRecMenuItemTime::DrawValue(void) { + pixmapVal->Fill(clrTransparent); + char buf[10]; + switch (pos) { + case 1: snprintf(buf, sizeof(buf), "%01d-:--", hh / 10); break; + case 2: snprintf(buf, sizeof(buf), "%02d:--", hh); break; + case 3: snprintf(buf, sizeof(buf), "%02d:%01d-", hh, mm / 10); break; + default: snprintf(buf, sizeof(buf), "%02d:%02d", hh, mm); + } + int textX = width - font->Width(buf) - 10; + int textY = (height - font->Height()) / 2; + pixmapVal->DrawText(cPoint(textX, textY), buf, colorText, clrTransparent, font); +} + +eRecMenuState cRecMenuItemTime::ProcessKey(eKeys Key) { + switch (Key & ~k_Repeat) { + case kLeft|k_Repeat: + case kLeft: { + if (--mm < 0) { + mm = 59; + if (--hh < 0) + hh = 23; + } + fresh = true; + value = hh * 100 + mm; + DrawValue(); + return rmsConsumed; + break; } + case kRight|k_Repeat: + case kRight: { + if (++mm > 59) { + mm = 0; + if (++hh > 23) + hh = 0; + } + fresh = true; + value = hh * 100 + mm; + DrawValue(); + return rmsConsumed; + break; } + case k0|k_Repeat ... k9|k_Repeat: + case k0 ... k9: { + if (fresh || pos > 3) { + pos = 0; + fresh = false; + } + int n = Key - k0; + switch (pos) { + case 0: + if (n <= 2) { + hh = n * 10; + mm = 0; + pos++; + } + break; + case 1: + if (hh + n <= 23) { + hh += n; + pos++; + } + break; + case 2: + if (n <= 5) { + mm += n * 10; + pos++; + } + break; + case 3: + if (mm + n <= 59) { + mm += n; + pos++; + } + break; + default: ; + } + value = hh * 100 + mm; + DrawValue(); + return rmsConsumed; + break; } + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemDay ------------------------------------------------------- +cRecMenuItemDay::cRecMenuItemDay(cString text, + time_t initialVal, + bool active) { + selectable = true; + this->text = text; + this->currentVal = cTimer::SetTime(initialVal, 0); + this->active = active; + height = 3 * font->Height() / 2; + pixmapVal = NULL; +} + +cRecMenuItemDay::~cRecMenuItemDay(void) { + if (pixmapVal) + osdManager.releasePixmap(pixmapVal); +} + +void cRecMenuItemDay::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapVal = osdManager.requestPixmap(5, cRect(x, y, width, height)); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapVal->SetViewPort(cRect(x, y, width, height)); + } +} + +void cRecMenuItemDay::Hide(void) { + pixmap->SetLayer(-1); + pixmapVal->SetLayer(-1); +} + +void cRecMenuItemDay::Show(void) { + pixmap->SetLayer(4); + pixmapVal->SetLayer(5); +} + +void cRecMenuItemDay::Draw(void) { + int textY = (height - font->Height()) / 2; + pixmap->DrawText(cPoint(10, textY), *text, colorText, colorTextBack, font); + DrawValue(); +} + +void cRecMenuItemDay::DrawValue(void) { + pixmapVal->Fill(clrTransparent); + cString textVal = DateString(currentVal); + int textX = width - font->Width(*textVal) - 10; + int textY = (height - font->Height()) / 2; + pixmapVal->DrawText(cPoint(textX, textY), *textVal, colorText, clrTransparent, font); +} + +eRecMenuState cRecMenuItemDay::ProcessKey(eKeys Key) { + switch (Key & ~k_Repeat) { + case kLeft: + currentVal -= 60*60*24; + DrawValue(); + return rmsConsumed; + break; + case kRight: + currentVal += 60*60*24; + DrawValue(); + return rmsConsumed; + break; + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemTimer ------------------------------------------------------- +cRecMenuItemTimer::cRecMenuItemTimer(const cTimer *timer, + eRecMenuState action1, + eRecMenuState action2, + eRecMenuState action3, + time_t conflictStart, + time_t conflictStop, + time_t overlapStart, + time_t overlapStop, + bool active) { + selectable = true; + this->timer = timer; + this->action = action1; + this->action2 = action2; + this->action3 = action3; + iconActive = 0; + this->conflictStart = conflictStart; + this->conflictStop = conflictStop; + this->overlapStart = overlapStart; + this->overlapStop = overlapStop; + this->active = active; + height = 3 * font->Height(); + pixmapIcons = NULL; +} + +cRecMenuItemTimer::~cRecMenuItemTimer(void) { + if (pixmapIcons) + osdManager.releasePixmap(pixmapIcons); + if (pixmapStatus) + osdManager.releasePixmap(pixmapStatus); +} + +void cRecMenuItemTimer::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapStatus = osdManager.requestPixmap(5, cRect(x, y, width, height)); + pixmapIcons = osdManager.requestPixmap(6, cRect(x, y, width, height)); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapStatus->SetViewPort(cRect(x, y, width, height)); + pixmapIcons->SetViewPort(cRect(x, y, width, height)); + } + pixmapStatus->Fill(clrTransparent); + pixmapIcons->Fill(clrTransparent); +} + +void cRecMenuItemTimer::Hide(void) { + pixmap->SetLayer(-1); + pixmapStatus->SetLayer(-1); + pixmapIcons->SetLayer(-1); +} + +void cRecMenuItemTimer::Show(void) { + pixmap->SetLayer(4); + pixmapStatus->SetLayer(5); + pixmapIcons->SetLayer(6); +} + +void cRecMenuItemTimer::Draw(void) { + const cChannel *channel = timer->Channel(); + cString channelName(""); + int channelTransponder = 0; + if (channel) { + channelName = channel->Name(); + channelTransponder = channel->Transponder(); + } + int logoX = DrawIcons(); + int logoWidth = height * tvguideConfig.logoWidthRatio / tvguideConfig.logoHeightRatio; + cImageLoader imgLoader; + if (!tvguideConfig.hideChannelLogos) { + if (imgLoader.LoadLogo(*channelName, logoWidth, height)) { + cImage logo = imgLoader.GetImage(); + pixmapIcons->DrawImage(cPoint(logoX, 0), logo); + logoX += logoWidth + 5; + } + } + int textX = logoX; + int textHeightLine1 = (height/2 - font->Height()) / 2; + int textHeightLine2 = height/2 - 5 + (height/4 - fontSmall->Height()) / 2; + int textHeightLine3 = 3*height/4 - 5 + (height/4 - fontSmall->Height()) / 2; + const cEvent *event = timer->Event(); + cString timerTitle(""); + if (event) + timerTitle = event->Title(); + cString timeStart = DayDateTime(timer->StartTime()); + cString timeEnd = TimeString(timer->StopTime()); + cString timerTime = cString::sprintf("%s - %s", *timeStart, *timeEnd); + cString channelInfo = cString::sprintf("%s, %s %d", *channelName, tr("Transp."), channelTransponder); + pixmap->DrawText(cPoint(textX, textHeightLine1), *timerTitle, theme.Color(clrFont), clrTransparent, font); + pixmap->DrawText(cPoint(textX, textHeightLine2), *timerTime, theme.Color(clrFont), clrTransparent, fontSmall); + pixmap->DrawText(cPoint(textX, textHeightLine3), *channelInfo, theme.Color(clrFont), clrTransparent, fontSmall); + + DrawTimerConflict(); +} + +int cRecMenuItemTimer::DrawIcons(void) { + int iconsX = 10; + int iconSize = 64; + int iconY = (height - iconSize) / 2; + cString iconInfo, iconDelete, iconEdit; + if (active) { + iconInfo = (iconActive==0)?"info_active":"info_inactive"; + iconDelete = (iconActive==1)?"delete_active":"delete_inactive"; + iconEdit = (iconActive==2)?"edit_active":"edit_inactive"; + } else { + iconInfo = "info_inactive"; + iconDelete = "delete_inactive"; + iconEdit = "edit_inactive"; + } + cImageLoader imgLoader; + if (imgLoader.LoadIcon(iconInfo, iconSize)) { + cImage icon = imgLoader.GetImage(); + pixmapIcons->DrawImage(cPoint(iconsX, iconY), icon); + iconsX += iconSize + 5; + } + if (imgLoader.LoadIcon(iconDelete, iconSize)) { + cImage icon = imgLoader.GetImage(); + pixmapIcons->DrawImage(cPoint(iconsX, iconY), icon); + iconsX += iconSize + 5; + } + if (imgLoader.LoadIcon(iconEdit, iconSize)) { + cImage icon = imgLoader.GetImage(); + pixmapIcons->DrawImage(cPoint(iconsX, iconY), icon); + iconsX += iconSize + 5; + } + return iconsX; +} + +void cRecMenuItemTimer::DrawTimerConflict(void) { + int widthConfl = 30 * width / 100; + int xConfl = width - widthConfl; + int heightConflBar = height / 4; + int yConflBar = (height - heightConflBar) / 2; + pixmapStatus->DrawRectangle(cRect(xConfl, 0, widthConfl, height), theme.Color(clrRecMenuTimerConflictBackground)); + + int completeWidthSecs = conflictStop - conflictStart; + int xConfBar = xConfl + (timer->StartTime() - conflictStart) * widthConfl / completeWidthSecs; + int widthConfBar = (timer->StopTime() - timer->StartTime()) * widthConfl / completeWidthSecs; + pixmapStatus->DrawRectangle(cRect(xConfBar, yConflBar, widthConfBar, heightConflBar), theme.Color(clrRecMenuTimerConflictBar)); + + int xOverlap = xConfl + (overlapStart - conflictStart) * widthConfl / completeWidthSecs; + int widthOverlap = (overlapStop - overlapStart) * widthConfl / completeWidthSecs; + + pixmapIcons->DrawRectangle(cRect(xOverlap, 0, widthOverlap, height), theme.Color(clrRecMenuTimerConflictOverlap)); + + pixmapStatus->DrawRectangle(cRect(xConfl-3,2,1,height-4), theme.Color(clrBorder)); + pixmapStatus->DrawRectangle(cRect(xConfl-2,0,2,height), theme.Color(clrBackground)); +} + +eRecMenuState cRecMenuItemTimer::ProcessKey(eKeys Key) { + switch (Key & ~k_Repeat) { + case kLeft: + if (iconActive > 0) { + iconActive--; + DrawIcons(); + return rmsConsumed; + } else + return rmsNotConsumed; + break; + case kRight: + if (iconActive < 2) { + iconActive++; + DrawIcons(); + return rmsConsumed; + } else + return rmsNotConsumed; + break; + case kOk: + if (iconActive == 0) + return action; + else if (iconActive == 1) + return action2; + else if (iconActive == 2) + return action3; + break; + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemTimerConflictHeader ------------------------------------------------------- + +cRecMenuItemTimerConflictHeader::cRecMenuItemTimerConflictHeader(time_t conflictStart, + time_t conflictStop, + time_t overlapStart, + time_t overlapStop) { + selectable = false; + active = false; + this->conflictStart = conflictStart; + this->conflictStop = conflictStop; + this->overlapStart = overlapStart; + this->overlapStop = overlapStop; + height = 3*font->Height()/2; + pixmapStatus = NULL; +} + +cRecMenuItemTimerConflictHeader::~cRecMenuItemTimerConflictHeader(void) { + if (pixmapStatus) + osdManager.releasePixmap(pixmapStatus); +} + +void cRecMenuItemTimerConflictHeader::SetPixmaps(void) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapStatus = osdManager.requestPixmap(5, cRect(x, y, width, height)); + pixmapStatus->Fill(clrTransparent); +} + +void cRecMenuItemTimerConflictHeader::Hide(void) { + pixmap->SetLayer(-1); + pixmapStatus->SetLayer(-1); +} + +void cRecMenuItemTimerConflictHeader::Show(void) { + pixmap->SetLayer(4); + pixmapStatus->SetLayer(5); +} + +void cRecMenuItemTimerConflictHeader::setBackground(void) { + pixmap->Fill(clrTransparent); +} + +void cRecMenuItemTimerConflictHeader::Draw(void) { + int widthConfl = 30 * width / 100; + int xConfl = width - widthConfl; + cString headerText = tr("Timer Conflict"); + int xHeader = (xConfl - font->Width(*headerText)) / 2; + int yHeader = (height - font->Height()) / 2; + pixmap->DrawText(cPoint(xHeader, yHeader), *headerText, theme.Color(clrFont), clrTransparent, font); + + pixmap->DrawRectangle(cRect(xConfl, 0, widthConfl, height), theme.Color(clrRecMenuTimerConflictBackground)); + + int completeWidthSecs = conflictStop - conflictStart; + int xOverlap = xConfl + (overlapStart - conflictStart) * widthConfl / completeWidthSecs; + int yOverlap = height - fontSmall->Height(); + int widthOverlap = (overlapStop - overlapStart) * widthConfl / completeWidthSecs; + + pixmapStatus->DrawRectangle(cRect(xOverlap, yOverlap, widthOverlap, height), theme.Color(clrRecMenuTimerConflictOverlap)); + + cString strConflStart = TimeString(conflictStart); + cString strConflStop = TimeString(conflictStop); + cString strOverlapStart = TimeString(overlapStart); + cString strOverlapStop = TimeString(overlapStop); + int y1 = 5; + int y2 = yOverlap; + int xConflStart = xConfl + 2; + int xConflStop = width - fontSmall->Width(*strConflStop) - 2; + int xOverlapStart = xOverlap - fontSmall->Width(*strOverlapStart) - 2; + int xOverlapStop = xOverlap + widthOverlap + 2; + pixmap->DrawText(cPoint(xConflStart, y1), *strConflStart, theme.Color(clrRecMenuTimerConflictBar), clrTransparent, fontSmall); + pixmap->DrawText(cPoint(xConflStop, y1), *strConflStop, theme.Color(clrRecMenuTimerConflictBar), clrTransparent, fontSmall); + pixmap->DrawText(cPoint(xOverlapStart, y2), *strOverlapStart, theme.Color(clrRecMenuTimerConflictOverlap), clrTransparent, fontSmall); + pixmap->DrawText(cPoint(xOverlapStop, y2), *strOverlapStop, theme.Color(clrRecMenuTimerConflictOverlap), clrTransparent, fontSmall); +} + +// --- cRecMenuItemEvent ------------------------------------------------------- +cRecMenuItemEvent::cRecMenuItemEvent(const cEvent *event, + eRecMenuState action1, + eRecMenuState action2, + bool active) { + selectable = true; + this->event = event; + this->action = action1; + this->action2 = action2; + iconActive = 0; + this->active = active; + height = font->Height() + 2*fontSmall->Height() + 10; + pixmapText = NULL; + pixmapIcons = NULL; +} + +cRecMenuItemEvent::~cRecMenuItemEvent(void) { + if (pixmapIcons) + osdManager.releasePixmap(pixmapIcons); + if (pixmapText) + osdManager.releasePixmap(pixmapText); +} + +void cRecMenuItemEvent::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapText = osdManager.requestPixmap(5, cRect(x, y, width, height)); + pixmapText->Fill(clrTransparent); + pixmapIcons = osdManager.requestPixmap(6, cRect(x, y, width, height)); + pixmapIcons->Fill(clrTransparent); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapText->SetViewPort(cRect(x, y, width, height)); + pixmapIcons->SetViewPort(cRect(x, y, width, height)); + } +} + +void cRecMenuItemEvent::Draw(void) { + if (!event) + return; + int logoX = DrawIcons(); + const cChannel *channel = Channels.GetByChannelID(event->ChannelID()); + cString channelName(""); + if (channel) { + channelName = channel->Name(); + } + int logoWidth = height * tvguideConfig.logoWidthRatio / tvguideConfig.logoHeightRatio; + cImageLoader imgLoader; + if (!tvguideConfig.hideChannelLogos) { + if (imgLoader.LoadLogo(*channelName, logoWidth, height)) { + cImage logo = imgLoader.GetImage(); + pixmapText->DrawImage(cPoint(logoX, 0), logo); + logoX += logoWidth + 5; + } + } + + int textX = logoX; + int textHeightLine1 = 5; + int textHeightLine2 = 5 + fontSmall->Height(); + int textHeightLine3 = height - fontSmall->Height() - 5; + + cString title = event->Title(); + cString desc = event->ShortText(); + cString start = DayDateTime(event->StartTime()); + cString end = event->GetEndTimeString(); + + colorText = active?theme.Color(clrFontActive):theme.Color(clrFont); + cString info = cString::sprintf("%s - %s, %s", *start, *end, *channelName); + pixmapText->DrawText(cPoint(textX, textHeightLine1), *info, colorText, clrTransparent, fontSmall); + pixmapText->DrawText(cPoint(textX, textHeightLine2), *title, colorText, clrTransparent, font); + pixmapText->DrawText(cPoint(textX, textHeightLine3), *desc, colorText, clrTransparent, fontSmall); +} + +int cRecMenuItemEvent::DrawIcons(void) { + pixmapIcons->Fill(clrTransparent); + int iconsX = 10; + int iconSize = 64; + int iconY = (height - iconSize) / 2; + cString iconInfo, iconRecord; + if (active) { + iconInfo = (iconActive==0)?"info_active":"info_inactive"; + if (action2 != rmsDisabled) + iconRecord = (iconActive==1)?"record_active":"record_inactive"; + } else { + iconInfo = "info_inactive"; + if (action2 != rmsDisabled) + iconRecord = "record_inactive"; + } + cImageLoader imgLoader; + if (imgLoader.LoadIcon(iconInfo, iconSize)) { + cImage icon = imgLoader.GetImage(); + pixmapIcons->DrawImage(cPoint(iconsX, iconY), icon); + iconsX += iconSize + 5; + } + if ((action2 != rmsDisabled) && imgLoader.LoadIcon(iconRecord, iconSize)) { + cImage icon = imgLoader.GetImage(); + pixmapIcons->DrawImage(cPoint(iconsX, iconY), icon); + iconsX += iconSize + 5; + } + return iconsX; +} + +void cRecMenuItemEvent::Hide(void) { + pixmap->SetLayer(-1); + pixmapText->SetLayer(-1); + pixmapIcons->SetLayer(-1); +} + +void cRecMenuItemEvent::Show(void) { + pixmap->SetLayer(4); + pixmapText->SetLayer(5); + pixmapIcons->SetLayer(6); +} + +eRecMenuState cRecMenuItemEvent::ProcessKey(eKeys Key) { + bool consumed = false; + switch (Key & ~k_Repeat) { + case kLeft: + if (action2 == rmsDisabled) + return rmsNotConsumed; + if (iconActive == 1) { + iconActive = 0; + consumed = true; + } + DrawIcons(); + if (consumed) + return rmsConsumed; + else + return rmsNotConsumed; + break; + case kRight: { + if (action2 == rmsDisabled) + return rmsNotConsumed; + if (iconActive == 0) { + iconActive = 1; + consumed = true; + } + DrawIcons(); + if (consumed) + return rmsConsumed; + else + return rmsNotConsumed; + break; } + case kOk: + if (iconActive == 0) + return action; + else if (iconActive == 1) + return action2; + break; + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemChannelChooser ------------------------------------------------------- +cRecMenuItemChannelChooser::cRecMenuItemChannelChooser(cString text, + cChannel *initialChannel, + bool active) { + selectable = true; + this->text = text; + this->channel = initialChannel; + if (initialChannel) + initialChannelSet = true; + else + initialChannelSet = false; + channelNumber = 0; + fresh = true; + this->active = active; + height = 2 * font->Height(); + pixmapChannel = NULL; +} + +cRecMenuItemChannelChooser::~cRecMenuItemChannelChooser(void) { + if (pixmapChannel) + osdManager.releasePixmap(pixmapChannel); +} + +void cRecMenuItemChannelChooser::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapChannel = osdManager.requestPixmap(5, cRect(x, y, width, height)); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapChannel->SetViewPort(cRect(x, y, width, height)); + } +} + +void cRecMenuItemChannelChooser::Hide(void) { + pixmap->SetLayer(-1); + pixmapChannel->SetLayer(-1); +} + +void cRecMenuItemChannelChooser::Show(void) { + pixmap->SetLayer(4); + pixmapChannel->SetLayer(5); +} + +void cRecMenuItemChannelChooser::Draw(void) { + int textY = (height - font->Height()) / 2; + pixmap->DrawText(cPoint(10, textY), *text, colorText, colorTextBack, font); + DrawValue(); +} + +void cRecMenuItemChannelChooser::DrawValue(void) { + pixmapChannel->Fill(clrTransparent); + int textY = (height - font->Height()) / 2; + if (channel) { + cString textVal = cString::sprintf("%d - %s", channel->Number(), channel->Name()); + int textX = width - font->Width(*textVal) - 10; + pixmapChannel->DrawText(cPoint(textX, textY), *textVal, colorText, clrTransparent, font); + int logoWidth = height * tvguideConfig.logoWidthRatio / tvguideConfig.logoHeightRatio; + int logoX = textX - logoWidth - 10; + cImageLoader imgLoader; + if (imgLoader.LoadLogo(channel->Name(), logoWidth, height)) { + cImage logo = imgLoader.GetImage(); + pixmapChannel->DrawImage(cPoint(logoX, 0), logo); + } + } else { + cString textVal = tr("all Channels"); + int textX = width - font->Width(*textVal) - 10; + pixmapChannel->DrawText(cPoint(textX, textY), *textVal, colorText, clrTransparent, font); + } +} + +int cRecMenuItemChannelChooser::GetIntValue(void) { + if (channel) + return channel->Number(); + return 0; +} + + +eRecMenuState cRecMenuItemChannelChooser::ProcessKey(eKeys Key) { + switch (Key & ~k_Repeat) { + case kLeft: { + fresh = true; + if (!channel) + return rmsConsumed; + cChannel *prev = channel; + cChannel *firstChannel = Channels.First(); + if(firstChannel->GroupSep()) + firstChannel = Channels.Next(firstChannel); + if (prev == firstChannel) { + if (!initialChannelSet) + channel = NULL; + } else { + while (prev = Channels.Prev(prev)) { + if(!prev->GroupSep()) { + channel = prev; + break; + } + } + } + DrawValue(); + return rmsConsumed; + break; } + case kRight: { + fresh = true; + if (!channel) { + channel = Channels.First(); + if(channel->GroupSep()) + channel = Channels.Next(channel); + } else { + cChannel *next = channel; + while (next = Channels.Next(next)) { + if(!next->GroupSep()) { + channel = next; + break; + } + } + } + DrawValue(); + return rmsConsumed; + break; } + case k0 ... k9: { + if (fresh) { + channelNumber = 0; + fresh = false; + } + channelNumber = channelNumber * 10 + (Key - k0); + cChannel *chanNew = Channels.GetByNumber(channelNumber); + if (chanNew) { + channel = chanNew; + DrawValue(); + } + return rmsConsumed; + break; } + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemDayChooser ------------------------------------------------------- +cRecMenuItemDayChooser::cRecMenuItemDayChooser(cString text, + int weekdays, + bool active) { + selectable = true; + this->text = text; + this->weekdays = weekdays; + this->active = active; + height = 2 * font->Height(); + selectedDay = 0; + pixmapWeekdays = NULL; + pixmapWeekdaysSelect = NULL; +} + +cRecMenuItemDayChooser::~cRecMenuItemDayChooser(void) { + if (pixmapWeekdays) + osdManager.releasePixmap(pixmapWeekdays); + if (pixmapWeekdaysSelect) + osdManager.releasePixmap(pixmapWeekdaysSelect); +} + +void cRecMenuItemDayChooser::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapWeekdays = osdManager.requestPixmap(5, cRect(x, y, width, height)); + pixmapWeekdaysSelect = osdManager.requestPixmap(6, cRect(x, y, width, height)); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapWeekdays->SetViewPort(cRect(x, y, width, height)); + pixmapWeekdaysSelect->SetViewPort(cRect(x, y, width, height)); + } + SetSizes(); +} + +void cRecMenuItemDayChooser::Hide(void) { + pixmap->SetLayer(-1); + pixmapWeekdays->SetLayer(-1); + pixmapWeekdaysSelect->SetLayer(-1); +} + +void cRecMenuItemDayChooser::Show(void) { + pixmap->SetLayer(4); + pixmapWeekdays->SetLayer(5); + pixmapWeekdaysSelect->SetLayer(6); +} + +void cRecMenuItemDayChooser::SetSizes(void) { + days = trVDR("MTWTFSS"); + int maxWidth = 0; + for (unsigned i=0; iWidth(days.at(i)); + if (charWidth > maxWidth) + maxWidth = charWidth; + } + daysSize = min(maxWidth + 15, height-4); + daysX = width - 10 - 7*daysSize; + daysY = (height - daysSize) / 2; +} + +void cRecMenuItemDayChooser::setBackground() { + cRecMenuItem::setBackground(); + if (active) { + DrawHighlight(selectedDay); + } else { + DrawHighlight(-1); + } +} + +void cRecMenuItemDayChooser::Draw(void) { + int textY = (height - font->Height()) / 2; + pixmap->DrawText(cPoint(10, textY), *text, colorText, colorTextBack, font); + DrawDays(); +} + +void cRecMenuItemDayChooser::DrawDays(void) { + pixmapWeekdays->Fill(clrTransparent); + int textY = (height - font->Height()) / 2; + pixmapWeekdays->DrawRectangle(cRect(daysX, daysY, 7*daysSize, daysSize), theme.Color(clrBorder)); + int currentX = daysX; + for (unsigned day=0; dayDrawRectangle(cRect(currentX+2, daysY+2, daysSize-4, daysSize-4), theme.Color(clrBackground)); + tColor colorDay = WeekDaySet(day)?theme.Color(clrRecMenuDayActive) + :theme.Color(clrRecMenuDayInactive); + int textX = currentX + (daysSize - font->Width(*strDay)) / 2; + pixmapWeekdays->DrawText(cPoint(textX, textY), *strDay, colorDay, clrTransparent, font); + currentX += daysSize; + } +} + +void cRecMenuItemDayChooser::DrawHighlight(int day) { + pixmapWeekdaysSelect->Fill(clrTransparent); + if (day > -1) { + int currentX = daysX + day*daysSize; + pixmapWeekdaysSelect->DrawRectangle(cRect(currentX, daysY, daysSize, daysSize), theme.Color(clrRecMenuDayHighlight)); + } +} + +bool cRecMenuItemDayChooser::WeekDaySet(unsigned day) { + return weekdays & (1 << day); +} + +void cRecMenuItemDayChooser::ToggleDay(void) { + bool dayActive = WeekDaySet(selectedDay); + int dayBit = pow(2, selectedDay); + if (dayActive) { + weekdays -= dayBit; + } else { + weekdays += dayBit; + } +} + +eRecMenuState cRecMenuItemDayChooser::ProcessKey(eKeys Key) { + switch (Key & ~k_Repeat) { + case kLeft: { + selectedDay--; + if (selectedDay<0) + selectedDay += 7; + DrawHighlight(selectedDay); + return rmsConsumed; + break; } + case kRight: { + selectedDay = (selectedDay+1)%7; + DrawHighlight(selectedDay); + return rmsConsumed; + break; } + case kOk: + ToggleDay(); + DrawDays(); + return rmsConsumed; + break; + default: + break; + } + return rmsNotConsumed; +} + +// --- cRecMenuItemRecording ------------------------------------------------------- +cRecMenuItemRecording::cRecMenuItemRecording(cRecording *recording, bool active) { + selectable = true; + this->recording = recording; + this->active = active; + height = font->Height() + 2*fontSmall->Height() + 10; + pixmapText = NULL; +} + +cRecMenuItemRecording::~cRecMenuItemRecording(void) { + if (pixmapText) + osdManager.releasePixmap(pixmapText); +} + +void cRecMenuItemRecording::SetPixmaps(void) { + if (!pixmap) { + pixmap = osdManager.requestPixmap(4, cRect(x, y, width, height)); + pixmapText = osdManager.requestPixmap(5, cRect(x, y, width, height)); + pixmapText->Fill(clrTransparent); + } else { + pixmap->SetViewPort(cRect(x, y, width, height)); + pixmapText->SetViewPort(cRect(x, y, width, height)); + } +} + +void cRecMenuItemRecording::Draw(void) { + if (!recording) + return; + const cRecordingInfo *recInfo = recording->Info(); + cChannel *channel = Channels.GetByChannelID(recInfo->ChannelID()); + cString channelName = tr("unknown channel"); + if (channel) + channelName = channel->Name(); + cString name = recording->Name(); + cString dateTime = cString::sprintf("%s, %s", *DateString(recording->Start()), *TimeString(recording->Start())); + + int recDuration = recording->LengthInSeconds() / 60; + + cString recDetails = cString::sprintf("%s: %d %s, %s %s %s \"%s\"", tr("Duration"), recDuration, tr("min"), tr("recorded at"), *dateTime, tr("from"), *channelName); + recDetails = CutText(*recDetails, width - 40, fontSmall).c_str(); + int text1Y = (height/2 - font->Height()) / 2 + 5; + int text2Y = height/2 + (height/2 - fontSmall->Height())/2 - 5; + colorText = active?theme.Color(clrFontActive):theme.Color(clrFont); + pixmapText->DrawText(cPoint(10, text1Y), *name, colorText, clrTransparent, font); + pixmapText->DrawText(cPoint(10, text2Y), *recDetails, colorText, clrTransparent, fontSmall); +} + +void cRecMenuItemRecording::Hide(void) { + pixmap->SetLayer(-1); + pixmapText->SetLayer(-1); +} + +void cRecMenuItemRecording::Show(void) { + pixmap->SetLayer(4); + pixmapText->SetLayer(5); +} diff --git a/recmenuitem.h b/recmenuitem.h new file mode 100644 index 0000000..780eac9 --- /dev/null +++ b/recmenuitem.h @@ -0,0 +1,465 @@ +#ifndef __TVGUIDE_RECMENUITEM_H +#define __TVGUIDE_RECMENUITEM_H + +#define AUTO_ADVANCE_TIMEOUT 1500 + +enum eRecMenuState { + rmsConsumed, + rmsNotConsumed, + rmsRefresh, + rmsContinue, + rmsClose, + rmsInstantRecord, + rmsIgnoreTimerConflict, + rmsDeleteTimerConflictMenu, + rmsEditTimerConflictMenu, + rmsSaveTimerConflictMenu, + rmsTimerConflictShowInfo, + rmsDeleteTimer, + rmsDeleteTimerConfirmation, + rmsEditTimer, + rmsSaveTimer, + rmsSearch, + rmsSearchWithOptions, + rmsSearchPerform, + rmsSearchShowInfo, + rmsSearchRecord, + rmsSearchRecordConfirm, + rmsSearchNothingFoundConfirm, + rmsSeriesTimer, + rmsSeriesTimerCreate, + rmsSearchTimer, + rmsSearchTimerOptions, + rmsSearchTimerOptionsReload, + rmsSearchTimerUseTemplate, + rmsSearchTimerOptionsManually, + rmsSearchTimerTestManually, + rmsSearchTimerTestTemplate, + rmsSearchTimerNothingFoundConfirm, + rmsSearchTimerCreateManually, + rmsSearchTimerCreateTemplate, + rmsSwitchTimer, + rmsSwitchTimerCreate, + rmsSwitchTimerDelete, + rmsRecordingSearch, + rmsRecordingSearchResult, + rmsTimerConflict, + rmsTimerConflicts, + rmsDisabled, +}; + +enum eDependend { + eGreater, + eLower, +}; +// --- cRecMenuItem ------------------------------------------------------------- +class cRecMenuItem : public cListObject, public cStyledPixmap { +protected: + int x, y; + int width, height; + bool selectable; + bool active; + bool drawn; + eRecMenuState action; + tColor colorText; + tColor colorTextBack; + const cFont *font; + const cFont *fontSmall; +public: + cRecMenuItem(void); + virtual ~cRecMenuItem(void); + void SetGeometry(int x, int y, int width); + virtual void SetPixmaps(void); + virtual int GetHeight(void) { return height; }; + virtual int GetWidth(void) { return 0; }; + virtual void CalculateHeight(int textWidth) {}; + void setActive(void) { this->active = true; } + void setInactive(void) { this->active = false; } + bool isSelectable(void) { return selectable; } + bool isActive(void) { return active; } + virtual void setBackground(void); + virtual void Draw(void) {}; + virtual void Hide(void) { pixmap->SetLayer(-1);}; + virtual void Show(void) { pixmap->SetLayer(4);}; + virtual int GetIntValue(void) { return -1; }; + virtual time_t GetTimeValue(void) { return 0; }; + virtual bool GetBoolValue(void) { return false; }; + virtual cString GetStringValue(void) { return cString(""); }; + virtual const cEvent *GetEventValue(void) { return NULL; }; + virtual eRecMenuState ProcessKey(eKeys Key) { return rmsNotConsumed; }; + +}; + +// --- cRecMenuItemButton ------------------------------------------------------- +class cRecMenuItemButton : public cRecMenuItem { +private: + cString text; + bool halfWidth; +public: + cRecMenuItemButton(const char *text, eRecMenuState action, bool active, bool halfWidth = false); + virtual ~cRecMenuItemButton(void); + int GetWidth(void); + void SetPixmaps(void); + void Draw(void); + eRecMenuState ProcessKey(eKeys Key); +}; + +// --- cRecMenuItemButtonYesNo ------------------------------------------------------- +class cRecMenuItemButtonYesNo : public cRecMenuItem { +private: + cString textYes; + cString textNo; + eRecMenuState actionNo; + bool yesActive; + cStyledPixmap *pixmapNo; + tColor colorTextNo; +public: + cRecMenuItemButtonYesNo(cString textYes, + cString textNo, + eRecMenuState actionYes, + eRecMenuState actionNo, + bool active); + virtual ~cRecMenuItemButtonYesNo(void); + void SetPixmaps(void); + void setBackground(void); + void Hide(void); + void Show(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); +}; + +// --- cRecMenuItemInfo ------------------------------------------------------- +class cRecMenuItemInfo : public cRecMenuItem { +private: + cString text; + cTextWrapper wrapper; + int border; +public: + cRecMenuItemInfo(const char *text); + virtual ~cRecMenuItemInfo(void); + void setBackground(void); + void CalculateHeight(int textWidth); + void Draw(void); +}; + +// --- cRecMenuItemInt ------------------------------------------------------- +class cRecMenuItemInt : public cRecMenuItem { +private: + cString text; + int currentVal; + int minVal; + int maxVal; + cPixmap *pixmapVal; + bool fresh; + void DrawValue(void); +public: + cRecMenuItemInt(cString text, + int initialVal, + int minVal, + int maxVal, + bool active); + virtual ~cRecMenuItemInt(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + void setBackground(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); + int GetIntValue(void) { return currentVal; }; +}; + +// --- cRecMenuItemBool ------------------------------------------------------- +class cRecMenuItemBool : public cRecMenuItem { +private: + cString text; + bool yes; + cPixmap *pixmapVal; + bool refresh; + void DrawValue(void); +public: + cRecMenuItemBool(cString text, + bool initialVal, + bool refresh, + bool active); + virtual ~cRecMenuItemBool(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); + bool GetBoolValue(void) { return yes; }; +}; + +// --- cRecMenuItemSelect ------------------------------------------------------- +class cRecMenuItemSelect : public cRecMenuItem { +private: + cString text; + int currentVal; + const char * const *strings; + int numValues; + cPixmap *pixmapVal; + void DrawValue(void); +public: + cRecMenuItemSelect(cString text, + const char * const *Strings, + int initialVal, + int numValues, + bool active); + virtual ~cRecMenuItemSelect(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); + int GetIntValue(void) { return currentVal; }; + cString GetStringValue(void) { return strings[currentVal]; }; +}; + +// --- cRecMenuItemText ------------------------------------------------------- +class cRecMenuItemText : public cRecMenuItem { +private: + cString title; + char *value; + int length; + const char *allowed; + int pos, offset; + bool insert, newchar, uppercase; + int lengthUtf8; + uint *valueUtf8; + uint *allowedUtf8; + uint *charMapUtf8; + uint *currentCharUtf8; + eKeys lastKey; + cTimeMs autoAdvanceTimeout; + cPixmap *pixmapVal; + cStyledPixmap *pixmapKeyboard; + cPixmap *pixmapKeyboardHighlight; + cPixmap *pixmapKeyboardIcons; + int keyboardWidth; + int gridWidth; + int gridHeight; + int keyboardHeight; + bool keyboardDrawn; + uint *IsAllowed(uint c); + void AdvancePos(void); + uint Inc(uint c, bool Up); + void Type(uint c); + void Insert(void); + void Delete(void); + void EnterEditMode(void); + void LeaveEditMode(bool SaveValue = false); + bool InEditMode(void) { return valueUtf8 != NULL; }; + void SetText(void); + void ActivateKeyboard(void); + void DeactivateKeyboard(void); + void HighlightSMSKey(int num); + void ClearSMSKey(void); + char *GetSMSKeys(int num); + void DrawValue(char *newValue); +public: + cRecMenuItemText(cString title, + char *initialVal, + int length, + bool active); + virtual ~cRecMenuItemText(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + void setBackground(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); + cString GetStringValue(void) { return value; }; +}; + + +// --- cRecMenuItemTime ------------------------------------------------------- +class cRecMenuItemTime : public cRecMenuItem { +private: + cString text; + int value; + int mm; + int hh; + int pos; + bool fresh; + cPixmap *pixmapVal; + void DrawValue(void); +public: + cRecMenuItemTime(cString text, + int initialVal, + bool active); + virtual ~cRecMenuItemTime(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); + int GetIntValue(void) { return value; }; +}; + +// --- cRecMenuItemDay ------------------------------------------------------- +class cRecMenuItemDay : public cRecMenuItem { +private: + cString text; + time_t currentVal; + cPixmap *pixmapVal; + void DrawValue(void); +public: + cRecMenuItemDay(cString text, + time_t initialVal, + bool active); + virtual ~cRecMenuItemDay(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); + time_t GetTimeValue(void) { return currentVal; }; +}; + +// --- cRecMenuItemTimer ------------------------------------------------------- +class cRecMenuItemTimer : public cRecMenuItem { +private: + const cTimer *timer; + eRecMenuState action2; + eRecMenuState action3; + int iconActive; + cPixmap *pixmapIcons; + cPixmap *pixmapStatus; + time_t conflictStart; + time_t conflictStop; + time_t overlapStart; + time_t overlapStop; + int DrawIcons(void); + void DrawTimerConflict(void); +public: + cRecMenuItemTimer(const cTimer *timer, + eRecMenuState action1, + eRecMenuState action2, + eRecMenuState action3, + time_t conflictStart, + time_t conflictStop, + time_t overlapStart, + time_t overlapStop, + bool active); + virtual ~cRecMenuItemTimer(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); +}; + +// --- cRecMenuItemTimerConflictHeader ------------------------------------------------------- + +class cRecMenuItemTimerConflictHeader: public cRecMenuItem { +private: + cPixmap *pixmapStatus; + time_t conflictStart; + time_t conflictStop; + time_t overlapStart; + time_t overlapStop; +public: + cRecMenuItemTimerConflictHeader(time_t conflictStart, + time_t conflictStop, + time_t overlapStart, + time_t overlapStop); + virtual ~cRecMenuItemTimerConflictHeader(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + void setBackground(void); + void Draw(void); +}; + +// --- cRecMenuItemEvent ------------------------------------------------------- +class cRecMenuItemEvent : public cRecMenuItem { +private: + const cEvent *event; + eRecMenuState action2; + int iconActive; + cPixmap *pixmapText; + cPixmap *pixmapIcons; + int DrawIcons(void); +public: + cRecMenuItemEvent(const cEvent *event, + eRecMenuState action1, + eRecMenuState action2, + bool active); + virtual ~cRecMenuItemEvent(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + const cEvent *GetEventValue(void) { return event; }; + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); +}; + +// --- cRecMenuItemChannelChooser ------------------------------------------------------- +class cRecMenuItemChannelChooser : public cRecMenuItem { +private: + cString text; + cChannel *channel; + int channelNumber; + bool initialChannelSet; + bool fresh; + cPixmap *pixmapChannel; + void DrawValue(void); +public: + cRecMenuItemChannelChooser (cString text, + cChannel *initialChannel, + bool active); + virtual ~cRecMenuItemChannelChooser(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); + int GetIntValue(void); +}; + +// --- cRecMenuItemDayChooser ------------------------------------------------------- +class cRecMenuItemDayChooser : public cRecMenuItem { +private: + cString text; + int weekdays; + std::string days; + int daysX; + int daysY; + int daysSize; + int selectedDay; + cPixmap *pixmapWeekdays; + cPixmap *pixmapWeekdaysSelect; + void SetSizes(void); + void DrawDays(void); + void DrawHighlight(int day); + void ToggleDay(void); + bool WeekDaySet(unsigned day); +public: + cRecMenuItemDayChooser (cString text, + int weekdays, + bool active); + virtual ~cRecMenuItemDayChooser(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + void setBackground(void); + eRecMenuState ProcessKey(eKeys Key); + void Draw(void); + int GetIntValue(void) { return weekdays;} ; +}; + +// --- cRecMenuItemRecording ------------------------------------------------------- +class cRecMenuItemRecording : public cRecMenuItem { +private: + cRecording *recording; + cPixmap *pixmapText; +public: + cRecMenuItemRecording(cRecording *recording, bool active); + virtual ~cRecMenuItemRecording(void); + void SetPixmaps(void); + void Hide(void); + void Show(void); + void Draw(void); +}; + +#endif //__TVGUIDE_RECMENUITEM_H \ No newline at end of file diff --git a/recmenumanager.c b/recmenumanager.c new file mode 100644 index 0000000..cd34c12 --- /dev/null +++ b/recmenumanager.c @@ -0,0 +1,521 @@ +#include "recmenumanager.h" + +cRecMenuManager::cRecMenuManager(void) { + active = false; + activeMenu = NULL; + activeMenuBuffer = NULL; + recManager = new cRecManager(); + recManager->SetEPGSearchPlugin(); + instantRecord = false; + currentConflict = -1; + templateID = -1; + timer = NULL; + searchWithOptions = false; + detailViewActive = false; +} + +cRecMenuManager::~cRecMenuManager(void) { + if (activeMenu) { + active = false; + delete activeMenu; + activeMenu = NULL; + } + delete recManager; +} + +void cRecMenuManager::Start(const cEvent *event) { + active = true; + activeMenuBuffer = NULL; + instantRecord = false; + currentConflict = -1; + templateID = -1; + timer = NULL; + searchWithOptions = false; + detailViewActive = false; + SetBackground(); + this->event = event; + activeMenu = new cRecMenuMain(recManager->EpgSearchAvailable(), event->HasTimer(), SwitchTimers.EventInSwitchList(event)); + activeMenu->Display(); + osdManager.flush(); +} + +void cRecMenuManager::Close(void) { + event = NULL; + active = false; + if (activeMenu) { + delete activeMenu; + activeMenu = NULL; + } + DeleteBackground(); +} + +void cRecMenuManager::SetBackground(void) { + int backgroundWidth = tvguideConfig.osdWidth; + int backgroundHeight = tvguideConfig.osdHeight; + pixmapBackground = osdManager.requestPixmap(3, cRect(0, 0, backgroundWidth, backgroundHeight)); + pixmapBackground->Fill(theme.Color(clrRecMenuBackground)); + if (tvguideConfig.scaleVideo) { + int tvHeight = tvguideConfig.statusHeaderHeight; + int tvWidth = tvHeight * 16 / 9; + int tvX = tvguideConfig.osdWidth - tvWidth; + pixmapBackground->DrawRectangle(cRect(tvX, 0, tvWidth, tvHeight), clrTransparent); + } +} + +void cRecMenuManager::DeleteBackground(void) { + osdManager.releasePixmap(pixmapBackground); +} + +eOSState cRecMenuManager::StateMachine(eRecMenuState nextState) { + eOSState state = osContinue; + switch (nextState) { + /* + * --------- INSTANT RECORDING --------------------------- + */ + case rmsInstantRecord: { + //Creating timer for active Event + //if no conflict, confirm and exit + instantRecord = true; + delete activeMenu; + cTimer *timer = recManager->createTimer(event); + if (!displayTimerConflict(timer)) { + activeMenu = new cRecMenuConfirmTimer(event); + activeMenu->Display(); + } + break; } + case rmsIgnoreTimerConflict: + //Confirming created Timer + if (instantRecord) { + delete activeMenu; + activeMenu = new cRecMenuConfirmTimer(event); + activeMenu->Display(); + } else { + state = osEnd; + Close(); + } + break; + case rmsTimerConflictShowInfo: { + int timerIndex = activeMenu->GetActive(true); + int timerID = conflictList[currentConflict].timerIDs[timerIndex]; + cTimer *t = Timers.Get(timerID); + if (t) { + const cEvent *ev = t->Event(); + if (ev) { + activeMenu->Hide(); + detailView = new cDetailView(ev); + detailView->drawHeader(); + detailView->drawContent(); + detailView->drawScrollbar(); + detailViewActive = true; + } + } + break;} + case rmsDeleteTimerConflictMenu: { + //delete timer out of current timer conflict + //active menu: cRecMenuTimerConflict + int timerIndex = activeMenu->GetActive(true); + int timerID = conflictList[currentConflict].timerIDs[timerIndex]; + recManager->DeleteTimer(timerID); + delete activeMenu; + if (!displayTimerConflict(timerID)) { + activeMenu = new cRecMenuConfirmTimer(event); + activeMenu->Display(); + } + break; } + case rmsEditTimerConflictMenu: { + //edit timer out of current timer conflict + //active menu: cRecMenuTimerConflict + int activeItem = activeMenu->GetActive(true); + int timerID = conflictList[currentConflict].timerIDs[activeItem]; + timer = Timers.Get(timerID); + if (timer) { + delete activeMenu; + activeMenu = new cRecMenuEditTimer(timer, rmsSaveTimerConflictMenu); + activeMenu->Display(); + } + break; } + case rmsSaveTimerConflictMenu: { + //save timer from current timer conflict + recManager->SaveTimer(timer, activeMenu); + delete activeMenu; + if (!displayTimerConflict(timer)) { + activeMenu = new cRecMenuConfirmTimer(event); + activeMenu->Display(); + } + break; } + case rmsDeleteTimer: + //delete timer for active event + delete activeMenu; + if (recManager->IsRecorded(event)) { + activeMenu = new cRecMenuAskDeleteTimer(event); + } else { + recManager->DeleteTimer(event); + activeMenu = new cRecMenuConfirmDeleteTimer(event); + } + activeMenu->Display(); + break; + case rmsDeleteTimerConfirmation: + //delete running timer for active event + recManager->DeleteTimer(event); + delete activeMenu; + activeMenu = new cRecMenuConfirmDeleteTimer(event); + activeMenu->Display(); + break; + case rmsEditTimer: { + //edit timer for active event + timer = Timers.GetMatch(event); + if (timer) { + delete activeMenu; + activeMenu = new cRecMenuEditTimer(timer, rmsSaveTimer); + activeMenu->Display(); + } + break; } + case rmsSaveTimer: { + //save timer for active event + recManager->SaveTimer(timer, activeMenu); + state = osEnd; + Close(); + break; } + /* + * --------- SERIES TIMER --------------------------------- + */ + case rmsSeriesTimer: { + delete activeMenu; + cChannel *channel = Channels.GetByChannelID(event->ChannelID()); + activeMenu = new cRecMenuSeriesTimer(channel, event); + activeMenu->Display(); + break; } + case rmsSeriesTimerCreate: { + cTimer *seriesTimer = recManager->CreateSeriesTimer(activeMenu); + delete activeMenu; + activeMenu = new cRecMenuConfirmSeriesTimer(seriesTimer); + activeMenu->Display(); + break; } + /* + * --------- SEARCH TIMER --------------------------------- + */ + case rmsSearchTimer: + delete activeMenu; + activeMenu = new cRecMenuSearchTimer(event); + activeMenu->Display(); + break; + case rmsSearchTimerOptions: { + searchString = *activeMenu->GetStringValue(1); + delete activeMenu; + if (isempty(*searchString)) { + activeMenu = new cRecMenuSearchTimer(event); + } else { + epgSearchTemplates = recManager->ReadEPGSearchTemplates(); + int numTemplates = epgSearchTemplates.size(); + if (numTemplates > 0) { + activeMenu = new cRecMenuSearchTimerTemplates(searchString, epgSearchTemplates); + } else { + activeMenu = new cRecMenuSearchTimerOptions(searchString); + } + } + activeMenu->Display(); + break; } + case rmsSearchTimerOptionsReload: { + int numTemplates = epgSearchTemplates.size(); + delete activeMenu; + activeMenu = new cRecMenuSearchTimerTemplates(searchString, epgSearchTemplates); + activeMenu->Display(); + break; } + case rmsSearchTimerUseTemplate: { + templateID = activeMenu->GetActive(true) - 1; + delete activeMenu; + activeMenu = new cRecMenuSearchTimerTemplatesCreate(searchString, epgSearchTemplates[templateID].name.c_str()); + activeMenu->Display(); + break; } + case rmsSearchTimerOptionsManually: + delete activeMenu; + activeMenu = new cRecMenuSearchTimerOptions(searchString); + activeMenu->Display(); + break; + case rmsSearchTimerTestTemplate: { + std::string epgSearchString = recManager->BuildEPGSearchString(searchString, epgSearchTemplates[templateID].templValue); + int numSearchResults = 0; + const cEvent **searchResult = recManager->PerformSearchTimerSearch(epgSearchString, numSearchResults); + if (searchResult) { + activeMenuBuffer = activeMenu; + activeMenuBuffer->Hide(); + activeMenu = new cRecMenuSearchTimerResults(searchString, searchResult, numSearchResults, epgSearchTemplates[templateID].name); + activeMenu->Display(); + } else { + activeMenuBuffer = activeMenu; + activeMenuBuffer->Hide(); + activeMenu = new cRecMenuSearchTimerNothingFound(searchString, epgSearchTemplates[templateID].name); + activeMenu->Display(); + } + break; } + case rmsSearchTimerTestManually: { + std::string epgSearchString = recManager->BuildEPGSearchString(searchString, activeMenu); + int numSearchResults = 0; + const cEvent **searchResult = recManager->PerformSearchTimerSearch(epgSearchString, numSearchResults); + if (searchResult) { + activeMenuBuffer = activeMenu; + activeMenuBuffer->Hide(); + activeMenu = new cRecMenuSearchTimerResults(searchString, searchResult, numSearchResults, ""); + activeMenu->Display(); + } else { + activeMenuBuffer = activeMenu; + activeMenuBuffer->Hide(); + activeMenu = new cRecMenuSearchTimerNothingFound(searchString, ""); + activeMenu->Display(); + } + break; } + case rmsSearchTimerNothingFoundConfirm: + delete activeMenu; + activeMenu = activeMenuBuffer; + activeMenuBuffer = NULL; + activeMenu->Show(); + break; + case rmsSearchTimerCreateManually: + case rmsSearchTimerCreateTemplate: { + std::string epgSearchString; + if (nextState == rmsSearchTimerCreateManually) { + epgSearchString = recManager->BuildEPGSearchString(searchString, activeMenu); + } else if (nextState = rmsSearchTimerCreateTemplate) { + epgSearchString = recManager->BuildEPGSearchString(searchString, epgSearchTemplates[templateID].templValue); + } + bool success = createSearchTimer(epgSearchString); + delete activeMenu; + activeMenu = new cRecMenuSearchTimerCreateConfirm(success); + activeMenu->Display(); + break; } + /* + * --------- SWITCH TIMER --------------------------------- + */ + case rmsSwitchTimer: + delete activeMenu; + activeMenu = new cRecMenuSwitchTimer(); + activeMenu->Display(); + break; + case rmsSwitchTimerCreate: { + bool success = recManager->CreateSwitchTimer(event, activeMenu); + delete activeMenu; + activeMenu = new cRecMenuSwitchTimerConfirm(success); + activeMenu->Display(); + break; } + case rmsSwitchTimerDelete: + recManager->DeleteSwitchTimer(event); + delete activeMenu; + activeMenu = new cRecMenuSwitchTimerDelete(); + activeMenu->Display(); + break; + /* + * --------- RECORDINGS SEARCH --------------------------------- + */ + case rmsRecordingSearch: + delete activeMenu; + activeMenu = new cRecMenuRecordingSearch(event); + activeMenu->Display(); + break; + case rmsRecordingSearchResult: { + searchString = activeMenu->GetStringValue(1); + delete activeMenu; + if (isempty(*searchString)) { + activeMenu = new cRecMenuRecordingSearch(event); + } else { + int numSearchResults = 0; + cRecording **searchResult = recManager->SearchForRecordings(searchString, numSearchResults); + if (numSearchResults == 0) { + activeMenu = new cRecMenuRecordingSearchNotFound(searchString); + } else { + activeMenu = new cRecMenuRecordingSearchResults(searchString, searchResult, numSearchResults); + } + } + activeMenu->Display(); + break; } + /* + * --------- SEARCH --------------------------------- + */ + case rmsSearch: + delete activeMenu; + activeMenu = new cRecMenuSearch(event); + activeMenu->Display(); + searchWithOptions = false; + break; + case rmsSearchWithOptions: { + cString searchString = activeMenu->GetStringValue(1); + delete activeMenu; + if (isempty(*searchString)) { + activeMenu = new cRecMenuSearch(event); + } else { + activeMenu = new cRecMenuSearch(event, *searchString); + searchWithOptions = true; + } + activeMenu->Display(); + break; } + case rmsSearchPerform: { + cString searchString = activeMenu->GetStringValue(1); + if (isempty(*searchString)) { + delete activeMenu; + activeMenu = new cRecMenuSearch(event); + } else { + int numSearchResults = 0; + const cEvent **searchResult = recManager->PerformSearch(activeMenu, searchWithOptions, numSearchResults); + if (searchResult) { + delete activeMenu; + activeMenu = new cRecMenuSearchResults(searchString, searchResult, numSearchResults); + } else { + activeMenuBuffer = activeMenu; + activeMenuBuffer->Hide(); + activeMenu = new cRecMenuSearchNothingFound(searchString); + } + } + activeMenu->Display(); + break; } + case rmsSearchNothingFoundConfirm: + delete activeMenu; + activeMenu = activeMenuBuffer; + activeMenuBuffer = NULL; + activeMenu->Show(); + break; + case rmsSearchShowInfo: { + const cEvent *ev = activeMenu->GetEventValue(activeMenu->GetActive(false)); + if (ev) { + activeMenu->Hide(); + detailView = new cDetailView(ev); + detailView->drawHeader(); + detailView->drawContent(); + detailView->drawScrollbar(); + detailViewActive = true; + } + break;} + case rmsSearchRecord: { + const cEvent *ev = activeMenu->GetEventValue(activeMenu->GetActive(false)); + cTimer *timer = recManager->createTimer(ev); + activeMenuBuffer = activeMenu; + activeMenuBuffer->Hide(); + activeMenu = new cRecMenuSearchConfirmTimer(ev); + activeMenu->Display(); + break;} + case rmsSearchRecordConfirm: + delete activeMenu; + activeMenu = activeMenuBuffer; + activeMenuBuffer = NULL; + activeMenu->Show(); + break; + /* + * --------- CHECK FOR TIMER CONFLICTS --------------------------------- + */ + case rmsTimerConflicts: { + //Show timer conflict + //active menu: cRecMenuTimerConflicts + conflictList = recManager->CheckTimerConflict(); + delete activeMenu; + int numConflicts = conflictList.size(); + if (numConflicts > 0) { + activeMenu = new cRecMenuTimerConflicts(conflictList); + } else { + activeMenu = new cRecMenuNoTimerConflict(); + } + activeMenu->Display(); + break; } + case rmsTimerConflict: + //Show timer conflict + //active menu: cRecMenuTimerConflicts + currentConflict = activeMenu->GetActive(true); + delete activeMenu; + activeMenu = new cRecMenuTimerConflict(conflictList[currentConflict]); + activeMenu->Display(); + break; + + /* + * --------- COMMON --------------------------------- + */ + case rmsClose: { + if (activeMenuBuffer == NULL) { + state = osEnd; + Close(); + } else { + delete activeMenu; + activeMenu = activeMenuBuffer; + activeMenuBuffer = NULL; + activeMenu->Show(); + state = osContinue; + } + break; } + default: + break; + } + return state; +} + +bool cRecMenuManager::displayTimerConflict(cTimer *timer) { + int timerID = 0; + for (cTimer *t = Timers.First(); t; t = Timers.Next(t)) { + if (t == timer) + return displayTimerConflict(timerID); + timerID++; + } + return false; +} + +bool cRecMenuManager::displayTimerConflict(int timerID) { + conflictList = recManager->CheckTimerConflict(); + int numConflicts = conflictList.size(); + int showTimerConflict = -1; + if (numConflicts > 0) { + for (int i=0; i -1) { + currentConflict = showTimerConflict; + activeMenu = new cRecMenuTimerConflict(conflictList[currentConflict]); + activeMenu->Display(); + return true; + } + return false; +} + +bool cRecMenuManager::createSearchTimer(std::string epgSearchString) { + int newTimerID = recManager->CreateSearchTimer(epgSearchString); + bool success = false; + if (newTimerID > -1) { + recManager->UpdateSearchTimers(); + success = true; + } + return success; +} + +eOSState cRecMenuManager::ProcessKey(eKeys Key) { + eOSState state = osContinue; + eRecMenuState nextState = rmsContinue; + if (!activeMenu) + return state; + if (detailViewActive) { + state = detailView->ProcessKey(Key); + if (state == osEnd) { + delete detailView; + detailView = NULL; + detailViewActive = false; + activeMenu->Show(); + state = osContinue; + } + } else { + nextState = activeMenu->ProcessKey(Key); + if ((nextState == rmsClose) || ((nextState == rmsNotConsumed)&&(Key == kBack))){ + if (activeMenuBuffer == NULL) { + state = osEnd; + Close(); + } else { + delete activeMenu; + activeMenu = activeMenuBuffer; + activeMenuBuffer = NULL; + activeMenu->Show(); + state = osContinue; + osdManager.flush(); + } + return state; + } + state = StateMachine(nextState); + } + osdManager.flush(); + return state; +} \ No newline at end of file diff --git a/recmenumanager.h b/recmenumanager.h new file mode 100644 index 0000000..cc9ac2c --- /dev/null +++ b/recmenumanager.h @@ -0,0 +1,38 @@ +#ifndef __TVGUIDE_RECMENUMANAGER_H +#define __TVGUIDE_RECMENUMANAGER_H + +// --- cRecMenuManager ------------------------------------------------------------- +class cRecMenuManager { +private: + bool active; + cRecMenu *activeMenu; + cRecMenu *activeMenuBuffer; + const cEvent *event; + cRecManager *recManager; + std::vector conflictList; + std::vector epgSearchTemplates; + bool instantRecord; + int currentConflict; + int templateID; + bool searchWithOptions; + cTimer *timer; + cString searchString; + cDetailView *detailView; + cPixmap *pixmapBackground; + bool detailViewActive; + void SetBackground(void); + void DeleteBackground(void); + bool displayTimerConflict(cTimer *timer); + bool displayTimerConflict(int timerID); + bool createSearchTimer(std::string epgSearchString); +public: + cRecMenuManager(void); + virtual ~cRecMenuManager(void); + bool isActive(void) { return active; }; + void Start(const cEvent *event); + void Close(void); + eOSState StateMachine(eRecMenuState nextState); + eOSState ProcessKey(eKeys Key); +}; + +#endif //__TVGUIDE_RECMENUMANAGER_H \ No newline at end of file diff --git a/services/epgsearch.h b/services/epgsearch.h index de29299..2669da4 100644 --- a/services/epgsearch.h +++ b/services/epgsearch.h @@ -1,5 +1,5 @@ -/* -Copyright (C) 2004-2007 Christian Wieninger +/* -*- c++ -*- +Copyright (C) 2004-2013 Christian Wieninger This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -24,10 +24,6 @@ The project's page is at http://winni.vdr-developer.org/epgsearch #ifndef EPGSEARCHSERVICES_INC #define EPGSEARCHSERVICES_INC -// Added by Andreas Mair (mail _AT_ andreas _DOT_ vdr-developer _DOT_ org) -#define EPGSEARCH_SEARCHRESULTS_SERVICE_STRING_ID "Epgsearch-searchresults-v1.0" -#define EPGSEARCH_LASTCONFLICTINFO_SERVICE_STRING_ID "Epgsearch-lastconflictinfo-v1.0" - #include #include #include @@ -59,6 +55,13 @@ struct Epgsearch_exttimeredit_v1_0 cOsdMenu* pTimerMenu; // pointer to the menu of results }; +// Data structure for service "Epgsearch-enablesearchtimers-v1.0" +struct Epgsearch_enablesearchtimers_v1_0 +{ +// in + bool enable; // enable search timer thread? +}; + // Data structure for service "Epgsearch-updatesearchtimers-v1.0" struct Epgsearch_updatesearchtimers_v1_0 { @@ -104,7 +107,7 @@ struct Epgsearch_searchresults_v1_0 bool useDescription; // search in description // out - class cServiceSearchResult : public cListObject + class cServiceSearchResult : public cListObject { public: const cEvent* event; @@ -119,11 +122,11 @@ struct Epgsearch_switchtimer_v1_0 { // in const cEvent* event; - int mode; // mode (0=query existance, 1=add/modify, 2=delete) + int mode; // mode (0=query existence, 1=add/modify, 2=delete) // in/out int switchMinsBefore; int announceOnly; -// out +// out bool success; // result }; @@ -132,7 +135,7 @@ class cServiceHandler { public: virtual std::list SearchTimerList() = 0; - // returns a list of search timer entries in the same format as used in epgsearch.conf + // returns a list of search timer entries in the same format as used in epgsearch.conf virtual int AddSearchTimer(const std::string&) = 0; // adds a new search timer and returns its ID (-1 on error) virtual bool ModSearchTimer(const std::string&) = 0; @@ -140,11 +143,11 @@ class cServiceHandler virtual bool DelSearchTimer(int) = 0; // deletes search timer with given ID and returns success virtual std::list QuerySearchTimer(int) = 0; - // returns the search result of the searchtimer with given ID in the same format as used in SVDRP command 'QRYS' (->MANUAL) + // returns the search result of the searchtimer with given ID in the same format as used in SVDRP command 'QRYS' (->MANUAL) virtual std::list QuerySearch(std::string) = 0; - // returns the search result of the searchtimer with given settings in the same format as used in SVDRP command 'QRYS' (->MANUAL) + // returns the search result of the searchtimer with given settings in the same format as used in SVDRP command 'QRYS' (->MANUAL) virtual std::list ExtEPGInfoList() = 0; - // returns a list of extended EPG categories in the same format as used in epgsearchcats.conf + // returns a list of extended EPG categories in the same format as used in epgsearchcats.conf virtual std::list ChanGrpList() = 0; // returns a list of channel groups maintained by epgsearch virtual std::list BlackList() = 0; @@ -164,4 +167,36 @@ struct Epgsearch_services_v1_0 std::auto_ptr handler; }; +// Data structures for service "Epgsearch-services-v1.1" +class cServiceHandler_v1_1 : public cServiceHandler +{ + public: + // Get timer conflicts + virtual std::list TimerConflictList(bool relOnly=false) = 0; + // Check if a conflict check is advised + virtual bool IsConflictCheckAdvised() = 0; +}; + +struct Epgsearch_services_v1_1 +{ +// in/out + std::auto_ptr handler; +}; + +// Data structures for service "Epgsearch-services-v1.2" +class cServiceHandler_v1_2 : public cServiceHandler_v1_1 +{ + public: + // List of all recording directories used in recordings, timers (and optionally search timers or in epgsearchdirs.conf) + virtual std::set ShortDirectoryList() = 0; + // Evaluate an expression against an event + virtual std::string Evaluate(const std::string& expr, const cEvent* event) = 0; +}; + +struct Epgsearch_services_v1_2 +{ +// in/out + std::auto_ptr handler; +}; + #endif diff --git a/setup.c b/setup.c index a49106e..7368bb7 100644 --- a/setup.c +++ b/setup.c @@ -102,6 +102,8 @@ void cTvguideSetup::Store(void) { SetupStore("FontGridHorizontalSmallDelta", tvguideConfig.FontGridHorizontalSmallDelta); SetupStore("FontTimeLineDateHorizontalDelta", tvguideConfig.FontTimeLineDateHorizontalDelta); SetupStore("FontTimeLineTimeHorizontalDelta", tvguideConfig.FontTimeLineTimeHorizontalDelta); + SetupStore("FontRecMenuItemDelta", tvguideConfig.FontRecMenuItemDelta); + SetupStore("FontRecMenuItemSmallDelta", tvguideConfig.FontRecMenuItemSmallDelta); SetupStore("displayRerunsDetailEPGView", tvguideConfig.displayRerunsDetailEPGView); SetupStore("numReruns", tvguideConfig.numReruns); SetupStore("useSubtitleRerun", tvguideConfig.useSubtitleRerun); @@ -301,7 +303,9 @@ void cMenuSetupFont::Set(void) { Add(new cMenuEditIntItem(tr("Timeline Date Font Size"), &tmpTvguideConfig->FontTimeLineDateHorizontalDelta, -30, 30)); Add(new cMenuEditIntItem(tr("Timeline Time Font Size"), &tmpTvguideConfig->FontTimeLineTimeHorizontalDelta, -30, 30)); } - + + Add(new cMenuEditIntItem(tr("Search & Recording Menu Font Size"), &tmpTvguideConfig->FontRecMenuItemDelta, -30, 30)); + Add(new cMenuEditIntItem(tr("Search & Recording Menu Small Font Size"), &tmpTvguideConfig->FontRecMenuItemSmallDelta, -30, 30)); SetCurrent(Get(currentItem)); Display(); diff --git a/styledpixmap.c b/styledpixmap.c index 77e6450..d57ee54 100644 --- a/styledpixmap.c +++ b/styledpixmap.c @@ -132,3 +132,11 @@ void cStyledPixmap::DrawImage(const cPoint &Point, const cImage &Image) { void cStyledPixmap::DrawRectangle(const cRect &Rect, tColor Color) { pixmap->DrawRectangle(Rect,Color); } + +void cStyledPixmap::DrawEllipse(const cRect &Rect, tColor Color, int Quadrant) { + pixmap->DrawEllipse(Rect,Color,Quadrant); +} + +void cStyledPixmap::SetViewPort(const cRect &Rect) { + pixmap->SetViewPort(Rect); +} \ No newline at end of file diff --git a/styledpixmap.h b/styledpixmap.h index ac974be..af09994 100644 --- a/styledpixmap.h +++ b/styledpixmap.h @@ -13,23 +13,26 @@ protected: tColor colorBlending; void setPixmap(cPixmap *pixmap); public: - cStyledPixmap(void); - cStyledPixmap(cPixmap *pixmap); - virtual ~cStyledPixmap(void); - void drawBackground(); - void drawBlendedBackground(); - void drawSparsedBackground(); - void drawBorder(); - void drawBoldBorder(); - void drawDefaultBorder(int width, int height); - void drawRoundedCorners(int width, int height, int radius); - void setColor(tColor color, tColor colorBlending) {this->color = color; this->colorBlending = colorBlending;}; - void SetAlpha(int alpha) {pixmap->SetAlpha(alpha);}; - void DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font); - void DrawImage(const cPoint &Point, const cImage &Image); - void DrawRectangle(const cRect &Rect, tColor Color); - int Width() {return pixmap->ViewPort().Width();}; - int Height() {return pixmap->ViewPort().Height();}; + cStyledPixmap(void); + cStyledPixmap(cPixmap *pixmap); + virtual ~cStyledPixmap(void); + void drawBackground(); + void drawBlendedBackground(); + void drawSparsedBackground(); + void drawBorder(); + void drawBoldBorder(); + void drawDefaultBorder(int width, int height); + void drawRoundedCorners(int width, int height, int radius); + void setColor(tColor color, tColor colorBlending) {this->color = color; this->colorBlending = colorBlending;}; + void SetAlpha(int alpha) {pixmap->SetAlpha(alpha);}; + void SetLayer(int layer) {pixmap->SetLayer(layer);}; + void DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font); + void DrawImage(const cPoint &Point, const cImage &Image); + void DrawRectangle(const cRect &Rect, tColor Color); + void DrawEllipse(const cRect &Rect, tColor Color, int Quadrant); + void SetViewPort(const cRect &Rect); + int Width() {return pixmap->ViewPort().Width();}; + int Height() {return pixmap->ViewPort().Height();}; }; #endif //__TVGUIDE_STYLEDPIXMAP_H \ No newline at end of file diff --git a/switchtimer.c b/switchtimer.c new file mode 100644 index 0000000..4ba7b45 --- /dev/null +++ b/switchtimer.c @@ -0,0 +1,109 @@ +#include "switchtimer.h" + +cSwitchTimers SwitchTimers; + +// -- cSwitchTimer ----------------------------------------------------------------- +cSwitchTimer::cSwitchTimer(void) { +} + +cSwitchTimer::cSwitchTimer(const cEvent* Event) { + eventID = 0; + startTime = 0; + if (Event) { + eventID = Event->EventID(); + channelID = Event->ChannelID(); + startTime = Event->StartTime(); + } +} + +bool cSwitchTimer::Parse(const char *s) { + char *line; + char *pos; + char *pos_next; + int parameter = 1; + int valuelen; +#define MAXVALUELEN (10 * MaxFileName) + + char value[MAXVALUELEN]; + startTime=0; + + pos = line = strdup(s); + pos_next = pos + strlen(pos); + if (*pos_next == '\n') *pos_next = 0; + while (*pos) { + while (*pos == ' ') + pos++; + if (*pos) { + if (*pos != ':') { + pos_next = strchr(pos, ':'); + if (!pos_next) + pos_next = pos + strlen(pos); + valuelen = pos_next - pos + 1; + if (valuelen > MAXVALUELEN) + valuelen = MAXVALUELEN; + strn0cpy(value, pos, valuelen); + pos = pos_next; + switch (parameter) { + case 1: + channelID = tChannelID::FromString(value); + break; + case 2: + eventID = atoi(value); + break; + case 3: + startTime = atol(value); + break; + default: + break; + } + } + parameter++; + } + if (*pos) + pos++; + } + free(line); + return (parameter >= 3) ? true : false; +} + +bool cSwitchTimers::EventInSwitchList(const cEvent* event) { + if (!event) return false; + cMutexLock SwitchTimersLock(this); + cSwitchTimer* switchTimer = SwitchTimers.First(); + while (switchTimer) { + if (switchTimer->eventID == event->EventID()) + return true; + switchTimer = SwitchTimers.Next(switchTimer); + } + return false; +} + +bool cSwitchTimers::ChannelInSwitchList(const cChannel* channel) { + if (!channel) return false; + cMutexLock SwitchTimersLock(this); + cSwitchTimer* switchTimer = SwitchTimers.First(); + while (switchTimer) { + if (switchTimer->channelID == channel->GetChannelID()) + return true; + switchTimer = SwitchTimers.Next(switchTimer); + } + return false; +} + +void cSwitchTimers::DeleteSwitchTimer(const cEvent *event) { + if (!event) return; + cMutexLock SwitchTimersLock(this); + cSwitchTimer* switchTimer = SwitchTimers.First(); + cSwitchTimer* delTimer = NULL; + + while (switchTimer) { + if (switchTimer->eventID == event->EventID()) { + delTimer = switchTimer; + break; + } + switchTimer = SwitchTimers.Next(switchTimer); + } + if (delTimer) { + SwitchTimers.Del(delTimer, true); + } +} diff --git a/switchtimer.h b/switchtimer.h new file mode 100644 index 0000000..8958b44 --- /dev/null +++ b/switchtimer.h @@ -0,0 +1,30 @@ +#ifndef __TVGUIDE_SWITCHTIMER_H +#define __TVGUIDE_SWITCHTIMER_H + +#include + +class cSwitchTimer : public cListObject +{ +public: + tEventID eventID; + time_t startTime; + tChannelID channelID; + + cSwitchTimer(void); + cSwitchTimer(const cEvent* Event); + bool Parse(const char *s); +}; + +class cSwitchTimers : public cConfig, public cMutex +{ +public: + cSwitchTimers(void) {} + ~cSwitchTimers(void) {} + bool EventInSwitchList(const cEvent* event); + bool ChannelInSwitchList(const cChannel* channel); + void DeleteSwitchTimer(const cEvent *event); +}; + +extern cSwitchTimers SwitchTimers; + +#endif //__TVGUIDE_SWITCHTIMER_H diff --git a/themes/tvguide-darkblue.theme b/themes/tvguide-darkblue.theme index 90285ce..ec0a1b1 100644 --- a/themes/tvguide-darkblue.theme +++ b/themes/tvguide-darkblue.theme @@ -30,3 +30,15 @@ clrButtonYellowBorder = FFBBBB00 clrButtonBlue = 990000BB clrButtonBlueBorder = FF0000BB clrButtonBlend = DD000000 +clrRecMenuBackground = AA000000 +clrRecMenuTimerConflictBackground = FFCCCCCC +clrRecMenuTimerConflictBar = FF222222 +clrRecMenuTimerConflictOverlap = AAFF0000 +clrRecMenuDayActive = FF00FF00 +clrRecMenuDayInactive = FFFF0000 +clrRecMenuDayHighlight = 44FFFFFF +clrRecMenuTextBack = FF000000 +clrRecMenuTextActiveBack = FF404749 +clrRecMenuKeyboardBack = FF000000 +clrRecMenuKeyboardBorder = FFFFFFFF +clrRecMenuKeyboardHigh = 40BB0000 \ No newline at end of file diff --git a/themes/tvguide-default.theme b/themes/tvguide-default.theme index 9704e65..05b2e47 100644 --- a/themes/tvguide-default.theme +++ b/themes/tvguide-default.theme @@ -30,3 +30,15 @@ clrButtonYellowBorder = FFBBBB00 clrButtonBlue = 990000BB clrButtonBlueBorder = FF0000BB clrButtonBlend = DD000000 +clrRecMenuBackground = AA000000 +clrRecMenuTimerConflictBackground = FFCCCCCC +clrRecMenuTimerConflictBar = FF222222 +clrRecMenuTimerConflictOverlap = AAFF0000 +clrRecMenuDayActive = FF00FF00 +clrRecMenuDayInactive = FFFF0000 +clrRecMenuDayHighlight = 44FFFFFF +clrRecMenuTextBack = FF000000 +clrRecMenuTextActiveBack = FF404749 +clrRecMenuKeyboardBack = FF000000 +clrRecMenuKeyboardBorder = FFFFFFFF +clrRecMenuKeyboardHigh = 40BB0000 \ No newline at end of file diff --git a/themes/tvguide-keepitsimple.theme b/themes/tvguide-keepitsimple.theme index f88aa92..852d7c3 100644 --- a/themes/tvguide-keepitsimple.theme +++ b/themes/tvguide-keepitsimple.theme @@ -25,4 +25,16 @@ clrButtonYellow = 99BBBB00 clrButtonYellowBorder = FFBBBB00 clrButtonBlue = 990000BB clrButtonBlueBorder = FF0000BB -clrButtonBlend = DD000000 \ No newline at end of file +clrButtonBlend = DD000000 +clrRecMenuBackground = AA000000 +clrRecMenuTimerConflictBackground = FFCCCCCC +clrRecMenuTimerConflictBar = FF222222 +clrRecMenuTimerConflictOverlap = AAFF0000 +clrRecMenuDayActive = FF00FF00 +clrRecMenuDayInactive = FFFF0000 +clrRecMenuDayHighlight = 44FFFFFF +clrRecMenuTextBack = FF000000 +clrRecMenuTextActiveBack = FF404749 +clrRecMenuKeyboardBack = FF000000 +clrRecMenuKeyboardBorder = BB555555 +clrRecMenuKeyboardHigh = 40BB0000 \ No newline at end of file diff --git a/themes/tvguide-nOpacity.theme b/themes/tvguide-nOpacity.theme index f492c2d..345d13a 100644 --- a/themes/tvguide-nOpacity.theme +++ b/themes/tvguide-nOpacity.theme @@ -30,3 +30,15 @@ clrButtonYellowBorder = FFBBBB00 clrButtonBlue = 990000BB clrButtonBlueBorder = FF0000BB clrButtonBlend = DD000000 +clrRecMenuBackground = AA000000 +clrRecMenuTimerConflictBackground = FFCCCCCC +clrRecMenuTimerConflictBar = FF222222 +clrRecMenuTimerConflictOverlap = AAFF0000 +clrRecMenuDayActive = FF00FF00 +clrRecMenuDayInactive = FFFF0000 +clrRecMenuDayHighlight = 44FFFFFF +clrRecMenuTextBack = FF000000 +clrRecMenuTextActiveBack = FF404749 +clrRecMenuKeyboardBack = FF000000 +clrRecMenuKeyboardBorder = FF003DF5 +clrRecMenuKeyboardHigh = 40BB0000 \ No newline at end of file diff --git a/themes/tvguide-nOpacitydarkred.theme b/themes/tvguide-nOpacitydarkred.theme index d0c73a0..b14f6ac 100644 --- a/themes/tvguide-nOpacitydarkred.theme +++ b/themes/tvguide-nOpacitydarkred.theme @@ -30,3 +30,15 @@ clrButtonYellowBorder = FFBBBB00 clrButtonBlue = 990000BB clrButtonBlueBorder = FF0000BB clrButtonBlend = DD000000 +clrRecMenuBackground = AA000000 +clrRecMenuTimerConflictBackground = FFCCCCCC +clrRecMenuTimerConflictBar = FF222222 +clrRecMenuTimerConflictOverlap = AAFF0000 +clrRecMenuDayActive = FF00FF00 +clrRecMenuDayInactive = FFFF0000 +clrRecMenuDayHighlight = 44FFFFFF +clrRecMenuTextBack = FF000000 +clrRecMenuTextActiveBack = FF404749 +clrRecMenuKeyboardBack = FF000000 +clrRecMenuKeyboardBorder = FF660000 +clrRecMenuKeyboardHigh = 40BB0000 \ No newline at end of file diff --git a/themes/tvguide-nOpacitygreen.theme b/themes/tvguide-nOpacitygreen.theme index e787d15..3042a84 100644 --- a/themes/tvguide-nOpacitygreen.theme +++ b/themes/tvguide-nOpacitygreen.theme @@ -30,3 +30,15 @@ clrButtonYellowBorder = FFBBBB00 clrButtonBlue = 990000BB clrButtonBlueBorder = FF0000BB clrButtonBlend = DD000000 +clrRecMenuBackground = AA000000 +clrRecMenuTimerConflictBackground = FFCCCCCC +clrRecMenuTimerConflictBar = FF222222 +clrRecMenuTimerConflictOverlap = AAFF0000 +clrRecMenuDayActive = FF00FF00 +clrRecMenuDayInactive = FFFF0000 +clrRecMenuDayHighlight = 44FFFFFF +clrRecMenuTextBack = FF000000 +clrRecMenuTextActiveBack = FF404749 +clrRecMenuKeyboardBack = FF000000 +clrRecMenuKeyboardBorder = EE006600 +clrRecMenuKeyboardHigh = 40BB0000 \ No newline at end of file diff --git a/themes/tvguide-nOpacityiceblue.theme b/themes/tvguide-nOpacityiceblue.theme index 2112974..ce5cb2c 100644 --- a/themes/tvguide-nOpacityiceblue.theme +++ b/themes/tvguide-nOpacityiceblue.theme @@ -25,3 +25,18 @@ clrButtonYellow = FFBBBB00 clrButtonYellowBorder = FFBBBB00 clrButtonBlue = FF0000BB clrButtonBlueBorder = FF0000BB +clrRecMenuBackground = AA000000 +clrRecMenuTimerConflictBackground = FFCCCCCC +clrRecMenuTimerConflictBar = FF222222 +clrRecMenuTimerConflictOverlap = AAFF0000 +clrRecMenuDayActive = FF00FF00 +clrRecMenuDayInactive = FFFF0000 +clrRecMenuDayHighlight = 77000000 +clrRecMenuTextBack = FF3C3C3C +clrRecMenuTextActiveBack = FF404749 +clrRecMenuKeyboardBack = FF000044 +clrRecMenuKeyboardBorder = FF3C3C3C +clrRecMenuKeyboardHigh = 55FFFFFF +clrButtonRedKeyboard = FFBB0000 +clrButtonGreenKeyboard = FF00BB00 +clrButtonYellowKeyboard = FFBBBB00 diff --git a/timeline.c b/timeline.c index 454aa9c..4355352 100644 --- a/timeline.c +++ b/timeline.c @@ -3,11 +3,11 @@ cTimeLine::cTimeLine(cMyTime *myTime) { this->myTime = myTime; if (tvguideConfig.displayMode == eVertical) { - dateViewer = new cStyledPixmap(osdManager.requestPixmap(3, cRect(0, + dateViewer = new cStyledPixmap(osdManager.requestPixmap(1, cRect(0, tvguideConfig.statusHeaderHeight, tvguideConfig.timeLineWidth, tvguideConfig.channelHeaderHeight + tvguideConfig.channelGroupsHeight))); - timeline = osdManager.requestPixmap(2, cRect(0, + timeline = osdManager.requestPixmap(1, cRect(0, tvguideConfig.statusHeaderHeight + tvguideConfig.channelHeaderHeight + tvguideConfig.channelGroupsHeight, tvguideConfig.timeLineWidth, tvguideConfig.osdHeight - tvguideConfig.statusHeaderHeight - tvguideConfig.channelHeaderHeight - tvguideConfig.channelGroupsHeight - tvguideConfig.footerHeight) @@ -16,11 +16,11 @@ cTimeLine::cTimeLine(cMyTime *myTime) { tvguideConfig.timeLineWidth, 1440*tvguideConfig.minutePixel)); } else if (tvguideConfig.displayMode == eHorizontal) { - dateViewer = new cStyledPixmap(osdManager.requestPixmap(3, cRect(0, + dateViewer = new cStyledPixmap(osdManager.requestPixmap(1, cRect(0, tvguideConfig.statusHeaderHeight, tvguideConfig.channelHeaderWidth + tvguideConfig.channelGroupsWidth, tvguideConfig.timeLineHeight))); - timeline = osdManager.requestPixmap(2, cRect(tvguideConfig.channelHeaderWidth + tvguideConfig.channelGroupsWidth, + timeline = osdManager.requestPixmap(1, cRect(tvguideConfig.channelHeaderWidth + tvguideConfig.channelGroupsWidth, tvguideConfig.statusHeaderHeight, tvguideConfig.osdWidth - tvguideConfig.channelHeaderWidth - tvguideConfig.channelGroupsWidth, tvguideConfig.timeLineHeight) @@ -29,7 +29,7 @@ cTimeLine::cTimeLine(cMyTime *myTime) { 1440*tvguideConfig.minutePixel, tvguideConfig.timeLineWidth)); } - clock = new cStyledPixmap(osdManager.requestPixmap(3, cRect(0, + clock = new cStyledPixmap(osdManager.requestPixmap(1, cRect(0, tvguideConfig.osdHeight- tvguideConfig.footerHeight, tvguideConfig.timeLineWidth, tvguideConfig.footerHeight-9))); diff --git a/timer.c b/timer.c index 8e60c0e..f6d9925 100644 --- a/timer.c +++ b/timer.c @@ -120,3 +120,49 @@ time_t cMyTime::GetRounded() { void cMyTime::debug() { esyslog("t: %s, tStart: %s, tEnd: %s", *TimeString(t), *TimeString(tStart), *TimeString(tEnd)); } + +// --- cTimeInterval ------------------------------------------------------------- + +cTimeInterval::cTimeInterval(time_t start, time_t stop) { + this->start = start; + this->stop = stop; +} + +cTimeInterval::~cTimeInterval(void) { +} + +cTimeInterval *cTimeInterval::Intersect(cTimeInterval *interval) { + time_t startIntersect, stopIntersect; + + if ((stop <= interval->Start()) || (interval->Stop() <= start)) { + return NULL; + } + + if (start <= interval->Start()) { + startIntersect = interval->Start(); + } else { + startIntersect = start; + } + if (stop <= interval->Stop()) { + stopIntersect = stop; + } else { + stopIntersect = interval->Stop(); + } + return new cTimeInterval(startIntersect, stopIntersect); +} + +cTimeInterval *cTimeInterval::Union(cTimeInterval *interval) { + time_t startUnion, stopUnion; + + if (start <= interval->Start()) { + startUnion = start; + } else { + startUnion = interval->Start(); + } + if (stop <= interval->Stop()) { + stopUnion = interval->Stop(); + } else { + stopUnion = stop; + } + return new cTimeInterval(startUnion, stopUnion); +} \ No newline at end of file diff --git a/timer.h b/timer.h index e84f74c..2ab5045 100644 --- a/timer.h +++ b/timer.h @@ -30,4 +30,19 @@ class cMyTime { void debug(); }; +// --- cTimeInterval ------------------------------------------------------------- + +class cTimeInterval { + private: + time_t start; + time_t stop; + public: + cTimeInterval(time_t start, time_t stop); + virtual ~cTimeInterval(void); + time_t Start(void) { return start; }; + time_t Stop(void) { return stop; }; + cTimeInterval *Intersect(cTimeInterval *interval); + cTimeInterval *Union(cTimeInterval *interval); +}; + #endif //__TVGUIDE_TIMER_H \ No newline at end of file diff --git a/tools.c b/tools.c new file mode 100644 index 0000000..2a3815e --- /dev/null +++ b/tools.c @@ -0,0 +1,370 @@ +#include +#include +#include + +/**************************************************************************************** +* CUTTEXT +****************************************************************************************/ +static std::string CutText(std::string text, int width, const cFont *font) { + if (width <= font->Size()) + return text.c_str(); + if (font->Width(text.c_str()) < width) + return text.c_str(); + cTextWrapper twText; + twText.Set(text.c_str(), font, width); + std::string cuttedTextNative = twText.GetLine(0); + std::stringstream sstrText; + sstrText << cuttedTextNative << "..."; + std::string cuttedText = sstrText.str(); + int actWidth = font->Width(cuttedText.c_str()); + if (actWidth > width) { + int overlap = actWidth - width; + int charWidth = font->Width("."); + if (charWidth == 0) + charWidth = 1; + int cutChars = overlap / charWidth; + if (cutChars > 0) { + cuttedTextNative = cuttedTextNative.substr(0, cuttedTextNative.length() - cutChars); + std::stringstream sstrText2; + sstrText2 << cuttedTextNative << "..."; + cuttedText = sstrText2.str(); + } + } + return cuttedText; +} +/**************************************************************************************** +* SPLTSTRING +****************************************************************************************/ +class splitstring : public std::string { + std::vector flds; +public: + splitstring(const char *s) : std::string(s) { }; + std::vector& split(char delim, int rep=0); +}; + +// split: receives a char delimiter; returns a vector of strings +// By default ignores repeated delimiters, unless argument rep == 1. +std::vector& splitstring::split(char delim, int rep) { + if (!flds.empty()) flds.clear(); // empty vector if necessary + std::string work = data(); + std::string buf = ""; + int i = 0; + while (i < work.length()) { + if (work[i] != delim) + buf += work[i]; + else if (rep == 1) { + flds.push_back(buf); + buf = ""; + } else if (buf.length() > 0) { + flds.push_back(buf); + buf = ""; + } + i++; + } + if (!buf.empty()) + flds.push_back(buf); + return flds; +} + +/**************************************************************************************** +* FINDIGNORECASE +****************************************************************************************/ +int FindIgnoreCase(const std::string& expr, const std::string& query) +{ + const char *p = expr.c_str(); + const char *r = strcasestr(p, query.c_str()); + + if (!r) + return -1; + return r - p; +} + +/**************************************************************************************** +* FUZZYSEARCH +****************************************************************************************/ +#include +#include +#include +#include + +#ifndef _AFUZZY_H +#define _AFUZZY_H + +// source from: +/* + Leonid Boitsov 2002. (itman@narod.ru) + C version of Stas Namin. + This code is a GPL software and is distributed under GNU + public licence without any warranty. +*/ + +typedef unsigned int Uint; + +#define MaxPatSize (sizeof(Uint) * 8) + +typedef struct +{ + Uint *R, + *R1, + *RP, + *S, + *RI; + Uint *FilterS; + + int Map[256]; + int FilterMap[256]; + int k; + Uint mask_ok; + Uint filter_ok; + Uint filter_shift; + int r_size; + int FilterSet; +} AFUZZY; + +void afuzzy_init(const char *p, int kerr, int UseFilter, AFUZZY *fuzzy); +void afuzzy_free(AFUZZY *fuzzy); +int afuzzy_checkSUB(const char *t, AFUZZY *fuzzy); + +#endif + + +static int afuzzy_checkFLT(const char *t, AFUZZY *fuzzy); + +/****************************************************************************** +FUNCTION afuzzy_init() + Initialization of the fuzzy search routine. This applies to the consequent + calls of the afuzzy_CheckRTR (whole string matching) and afuzzy_CheckSUB + (substring match) routines. afuzzy_init() should be called for each + new pattern or error length. The search is case sensitive + +ARGUMENTS: + p Pattern + kerr Number of possible errors. Shouldn't exceed pattern length + UseFilter Use agrep filter algorithm that speeds up search. + fuzzy pointer to the structure that will be later passes to Check* + (the first 6 elements should be NULLs for the first call) + +RETURN VALUE: + none + +ALGORITHM + see. the article on agrep algorithms. + The only change is accounting transpositions as one edit operation . +******************************************************************************/ +void afuzzy_init(const char *p, int kerr, int UseFilter, AFUZZY *fuzzy) +{ + int cnt, p_len, i, j, l, d, m, dd; + char PatFilter[sizeof(Uint)*8 + 1]; + + fuzzy->k = kerr; + m = strlen(p); + fuzzy->FilterSet = 0; + memset(fuzzy->Map, 0 , sizeof(fuzzy->Map) ); + + if (fuzzy->S) + free(fuzzy->S); + if (fuzzy->R) + free(fuzzy->R); + if (fuzzy->R1) + free(fuzzy->R1); + if (fuzzy->RP) + free(fuzzy->RP); + if (fuzzy->RI) + free(fuzzy->RI); + if (fuzzy->FilterS) + free(fuzzy->FilterS); + + fuzzy->FilterS = NULL; + fuzzy->S = (Uint *)calloc(m + 1, sizeof(Uint)); + fuzzy->R = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); + fuzzy->R1 = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); + fuzzy->RI = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); + fuzzy->RP = (Uint *)calloc(fuzzy->k + 1, sizeof(Uint)); + + for (i = 0, cnt = 0; i < m; i++) + { + l = fuzzy->Map[(unsigned char)p[i]]; + if (!l) + { + l = fuzzy->Map[(unsigned char)p[i]] = ++cnt; + fuzzy->S[l] = 0; + } + fuzzy->S[l] |= 1 << i; + } + + + for (d = 0; d <= fuzzy->k; d++) + fuzzy->RI[d] = (1 << d) - 1; + + fuzzy->mask_ok = (1 << (m - 1)); + fuzzy->r_size = sizeof(Uint) * (fuzzy->k + 1); + p_len = m; + + if (p_len > (int) sizeof(Uint)*8) + p_len = (int) sizeof(Uint)*8; + + /* If k is zero then no filter is needed! */ + if (fuzzy->k && (p_len >= 2*(fuzzy->k + 1)) ) + { + if (UseFilter) + { + fuzzy->FilterSet = 1; + memset(fuzzy->FilterMap, 0 , sizeof(fuzzy->FilterMap) ); + fuzzy->FilterS = (Uint *)calloc(m + 1, sizeof(Uint)); + + /* Not let's fill the interleaved pattern */ + dd = p_len / (fuzzy->k + 1); + p_len = dd * (fuzzy->k + 1); + + for (i = 0, cnt = 0; i < dd; i++) + for (j = 0; j < fuzzy->k + 1; j++, cnt++) + PatFilter[cnt] = (unsigned char)p[j*dd + i]; + PatFilter[p_len] = 0; + + for (i = 0, cnt = 0; i < p_len; i++) + { + l = fuzzy->FilterMap[(unsigned char)PatFilter[i]]; + if (!l) + { + l = fuzzy->FilterMap[(unsigned char)PatFilter[i]] = ++cnt; + fuzzy->FilterS[l] = 0; + } + fuzzy->FilterS[l] |= 1 << i; + } + fuzzy->filter_ok = 0; + for (i = p_len - fuzzy->k - 1; i <= p_len - 1; i++) /* k+1 times */ + fuzzy->filter_ok |= 1 << i; + + /* k+1 first bits set to 1 */ + fuzzy->filter_shift = (1 << (fuzzy->k + 2)) - 1; + } + } +} + +/****************************************************************************** +FUNCTION afuzzy_free() + Cleaning up after previous afuzzy_init() call. + +ARGUMENTS: + fuzzy pointer to the afuzzy parameters structure + +RETURN VALUE: + none +******************************************************************************/ +void afuzzy_free(AFUZZY *fuzzy) +{ + if (fuzzy->S) + { + free(fuzzy->S); + fuzzy->S = NULL; + } + if (fuzzy->R) + { + free(fuzzy->R); + fuzzy->R = NULL; + } + if (fuzzy->R1) + { + free(fuzzy->R1); + fuzzy->R1 = NULL; + } + if (fuzzy->RP) + { + free(fuzzy->RP); + fuzzy->RP = NULL; + } + if (fuzzy->RI) + { + free(fuzzy->RI); + fuzzy->RI = NULL; + } + if (fuzzy->FilterS) + { + free(fuzzy->FilterS); + fuzzy->FilterS = NULL; + } +} + + +/****************************************************************************** +FUNCTION afuzzy_CheckSUB() + Perform a fuzzy pattern substring matching. afuzzy_init() should be + called previously to initialize the pattern and error length. + Positive result means that some part of the string given matches the + pattern with no more than afuzzy->k errors (1 error = 1 letter + replacement or transposition) + +ARGUMENTS: + t the string to test + fuzzy pointer to the afuzzy parameters structure + +RETURN VALUE: + 0 - no match + > 0 - strings match + +ALGORITHM + ???????????????? +******************************************************************************/ +int afuzzy_checkSUB(const char *t, AFUZZY *fuzzy) +{ + register char c; + register int j, d; + + /* For eficciency this case should be little bit optimized */ + if (!fuzzy->k) + { + Uint R = 0, R1; + + for (j = 0; (c = t[j]) != '\0'; j++) + { + R1 = ( ((R<<1) | 1) & fuzzy->S[fuzzy->Map[(unsigned char)c]]); + R = R1; + + if (R1 & fuzzy->mask_ok) + return 1; + } /* end for (register int j = 0 ... */ + return 0; + } + + if (fuzzy->FilterSet && !afuzzy_checkFLT(t, fuzzy)) + return 0; + + memcpy(fuzzy->R, fuzzy->RI, fuzzy->r_size); /* R = RI */ + + for (j = 0; (c = t[j]); j++) + { + for (d = 0; d <= fuzzy->k; d++) + { + fuzzy->R1[d] = (((fuzzy->R[d]<<1) | 1) & + fuzzy->S[fuzzy->Map[(unsigned char)c]]); + if (d > 0) + fuzzy->R1[d] |= ((fuzzy->R[d-1] | fuzzy->R1[d-1])<<1) | 1 | + fuzzy->R[d-1]; + } + if (fuzzy->R1[fuzzy->k] & fuzzy->mask_ok) + return j; + + memcpy(fuzzy->R, fuzzy->R1, fuzzy->r_size); + + } /* end for (register int j = 0 ... */ + + return 0; +} + +static int afuzzy_checkFLT(const char *t, AFUZZY *fuzzy) +{ + register Uint FilterR = 0; + register Uint FilterR1; + register int j; + + for (j = 0; t[j] != '\0'; j++) + { + FilterR1 = ( ((FilterR<<(fuzzy->k+1)) | fuzzy->filter_shift) & + fuzzy->FilterS[fuzzy->FilterMap[(unsigned char)t[j]]]); + if (FilterR1 & fuzzy->filter_ok) + return 1; + FilterR = FilterR1; + } /* end for (register int j = 0 ... */ + + return 0; +} diff --git a/tvguide.c b/tvguide.c index 806868b..f33c1e9 100644 --- a/tvguide.c +++ b/tvguide.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "tvguideosd.c" @@ -20,7 +21,7 @@ -static const char *VERSION = "0.0.5"; +static const char *VERSION = "0.0.6"; static const char *DESCRIPTION = "A fancy 2d EPG Viewer"; static const char *MAINMENUENTRY = "Tvguide"; @@ -28,6 +29,7 @@ class cPluginTvguide : public cPlugin { private: bool logoPathSet; bool imagesPathSet; + bool iconsPathSet; public: cPluginTvguide(void); virtual ~cPluginTvguide(); @@ -58,6 +60,7 @@ cPluginTvguide::cPluginTvguide(void) // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! logoPathSet = false; imagesPathSet = false; + iconsPathSet = false; } cPluginTvguide::~cPluginTvguide() @@ -124,6 +127,13 @@ bool cPluginTvguide::Start(void) tvguideConfig.SetImagesPath(path); logoPathSet = true; } + + if (!iconsPathSet) { + cString path = cString::sprintf("%s/icons/", cPlugin::ConfigDirectory(PLUGIN_NAME_I18N)); + tvguideConfig.SetIconsPath(path); + iconsPathSet = true; + } + return true; } diff --git a/tvguideosd.c b/tvguideosd.c index 5aaa128..01fcbe6 100644 --- a/tvguideosd.c +++ b/tvguideosd.c @@ -37,6 +37,21 @@ THEME_CLR(theme, clrButtonYellowBorder, 0xFFBBBB00); THEME_CLR(theme, clrButtonBlue, 0x990000BB); THEME_CLR(theme, clrButtonBlueBorder, 0xFF0000BB); THEME_CLR(theme, clrButtonBlend, 0xDD000000); +THEME_CLR(theme, clrRecMenuBackground, 0xB0000000); +THEME_CLR(theme, clrRecMenuTimerConflictBackground, 0xFFCCCCCC); +THEME_CLR(theme, clrRecMenuTimerConflictBar, 0xFF222222); +THEME_CLR(theme, clrRecMenuTimerConflictOverlap, 0xAAFF0000); +THEME_CLR(theme, clrRecMenuDayActive, 0xFF00FF00); +THEME_CLR(theme, clrRecMenuDayInactive, 0xFFFF0000); +THEME_CLR(theme, clrRecMenuDayHighlight, 0x44FFFFFF); +THEME_CLR(theme, clrRecMenuTextBack, 0xFF000000); +THEME_CLR(theme, clrRecMenuTextActiveBack, 0xFF404749); +THEME_CLR(theme, clrRecMenuKeyboardBack, 0xFF000000); +THEME_CLR(theme, clrRecMenuKeyboardBorder, clrWhite); +THEME_CLR(theme, clrRecMenuKeyboardHigh, 0x55FFFFFF); +THEME_CLR(theme, clrButtonRedKeyboard, 0xFFBB0000); +THEME_CLR(theme, clrButtonGreenKeyboard, 0xFF00BB00); +THEME_CLR(theme, clrButtonYellowKeyboard, 0xFFBBBB00); #include "config.c" cTvguideConfig tvguideConfig; @@ -44,11 +59,12 @@ cTvguideConfig tvguideConfig; #include "osdmanager.c" cOsdManager osdManager; +#include "tools.c" +#include "switchtimer.c" #include "setup.c" #include "imageloader.c" #include "styledpixmap.c" #include "timer.c" -#include "messagebox.c" #include "timeline.c" #include "grid.c" #include "headergrid.c" @@ -60,6 +76,11 @@ cOsdManager osdManager; #include "channelgroup.c" #include "channelgroups.c" #include "footer.c" +#include "recmenuitem.c" +#include "recmenu.c" +#include "recmanager.c" +#include "recmenus.c" +#include "recmenumanager.c" #include "tvguideosd.h" #include @@ -69,6 +90,7 @@ cTvGuideOsd::cTvGuideOsd(void) { detailViewActive = false; activeGrid = NULL; timeLine = NULL; + recMenuManager = NULL; } cTvGuideOsd::~cTvGuideOsd() { @@ -82,7 +104,7 @@ cTvGuideOsd::~cTvGuideOsd() { delete timeLine; delete channelGroups; delete footer; - cMessageBox::Destroy(); + delete recMenuManager; osdManager.deleteOsd(); } @@ -97,6 +119,12 @@ void cTvGuideOsd::Show(void) { osdManager.setBackground(); myTime = new cMyTime(); myTime->Now(); + SwitchTimers.Load(AddDirectory(cPlugin::ConfigDirectory("epgsearch"), "epgsearchswitchtimers.conf")); + cSwitchTimer *st = NULL; + for (st = SwitchTimers.First(); st; st = SwitchTimers.Next(st)) { + esyslog("tvguide: switchtimer eventID %d time %ld", st->eventID, st->startTime); + } + recMenuManager = new cRecMenuManager(); drawOsd(); } esyslog("tvguide: Rendering took %d ms", int(cTimeMs::Now()-start)); @@ -392,15 +420,10 @@ void cTvGuideOsd::processKeyUp() { if (!activeGrid) { return; } - if (detailViewActive) { - detailView->scrollUp(); - osdManager.flush(); - } else { - if (tvguideConfig.displayMode == eVertical) { - timeBack(); - } else if (tvguideConfig.displayMode == eHorizontal) { - channelBack(); - } + if (tvguideConfig.displayMode == eVertical) { + timeBack(); + } else if (tvguideConfig.displayMode == eHorizontal) { + channelBack(); } } @@ -408,21 +431,14 @@ void cTvGuideOsd::processKeyDown() { if (!activeGrid) { return; } - if (detailViewActive) { - detailView->scrollDown(); - osdManager.flush(); - } else { - if (tvguideConfig.displayMode == eVertical) { - timeForward(); - } else if (tvguideConfig.displayMode == eHorizontal) { - channelForward(); - } + if (tvguideConfig.displayMode == eVertical) { + timeForward(); + } else if (tvguideConfig.displayMode == eHorizontal) { + channelForward(); } } void cTvGuideOsd::processKeyLeft() { - if (detailViewActive) - return; if (activeGrid == NULL) return; if (tvguideConfig.displayMode == eVertical) { @@ -433,8 +449,6 @@ void cTvGuideOsd::processKeyLeft() { } void cTvGuideOsd::processKeyRight() { - if (detailViewActive) - return; if (activeGrid == NULL) return; if (tvguideConfig.displayMode == eVertical) { @@ -447,26 +461,7 @@ void cTvGuideOsd::processKeyRight() { void cTvGuideOsd::processKeyRed() { if ((activeGrid == NULL) || activeGrid->isDummy()) return; - cTimer *timer = new cTimer(activeGrid->GetEvent()); - cTimer *t = Timers.GetTimer(timer); - cString msg; - if (t) { - isyslog("timer %s already exists", *timer->ToDescr()); - delete timer; - msg = cString::sprintf(tr("Timer not set! There is already a timer for this item.")); - } else { - Timers.Add(timer); - Timers.SetModified(); - msg = cString::sprintf("%s:\n%s (%s) %s - %s", tr("Timer set"), activeGrid->GetEvent()->Title(), timer->Channel()->Name(), *DayDateTime(timer->StartTime()), *TimeString(timer->StopTime())); - timer->SetEvent(activeGrid->GetEvent()); - activeGrid->setTimer(); - activeGrid->column->setTimer(); - activeGrid->SetDirty(); - activeGrid->Draw(); - osdManager.flush(); - isyslog("timer %s added (active)", *timer->ToDescr()); - } - cMessageBox::Start(4000, msg); + recMenuManager->Start(activeGrid->GetEvent()); } void cTvGuideOsd::processKeyGreen() { @@ -547,7 +542,7 @@ eOSState cTvGuideOsd::processKeyBlue() { } eOSState cTvGuideOsd::processKeyOk() { - if ((tvguideConfig.blueKeyMode == 0) || detailViewActive ) { + if (tvguideConfig.blueKeyMode == 0) { DetailedEPG(); } else if (tvguideConfig.blueKeyMode == 1) { return ChannelSwitch(); @@ -567,17 +562,13 @@ eOSState cTvGuideOsd::ChannelSwitch() { } void cTvGuideOsd::DetailedEPG() { - if (detailViewActive) { - delete detailView; - detailView = NULL; - detailViewActive = false; + if (!activeGrid->isDummy()) { + detailViewActive = true; + detailView = new cDetailView(activeGrid->GetEvent()); + detailView->drawHeader(); + detailView->drawContent(); + detailView->drawScrollbar(); osdManager.flush(); - } else { - if (!activeGrid->isDummy()) { - detailViewActive = true; - detailView = new cDetailView(activeGrid); - detailView->Start(); - } } } @@ -646,11 +637,32 @@ void cTvGuideOsd::processKey9() { osdManager.flush(); } +void cTvGuideOsd::SetTimers() { + for (cChannelColumn *column = columns.First(); column; column = columns.Next(column)) { + column->SetTimers(); + } +} + eOSState cTvGuideOsd::ProcessKey(eKeys Key) { - eOSState state = cOsdObject::ProcessKey(Key); - if (state == osUnknown) { - cPixmap::Lock(); + eOSState state = osContinue; + cPixmap::Lock(); + if (recMenuManager->isActive()) { + state = recMenuManager->ProcessKey(Key); + if (state == osEnd) { + SetTimers(); + osdManager.flush(); + } state = osContinue; + } else if (detailViewActive) { + state = detailView->ProcessKey(Key); + if (state == osEnd) { + delete detailView; + detailView = NULL; + detailViewActive = false; + osdManager.flush(); + state = osContinue; + } + } else { switch (Key & ~k_Repeat) { case kUp: processKeyUp(); break; case kDown: processKeyDown(); break; @@ -661,7 +673,7 @@ eOSState cTvGuideOsd::ProcessKey(eKeys Key) { case kYellow: processKeyYellow(); break; case kBlue: state = processKeyBlue(); break; case kOk: state = processKeyOk(); break; - case kBack: state=osEnd; break; + case kBack: state=osEnd; break; case k1: processKey1(); break; case k3: processKey3(); break; case k4: processKey4(); break; @@ -670,8 +682,8 @@ eOSState cTvGuideOsd::ProcessKey(eKeys Key) { case k9: processKey9(); break; default: break; } - cPixmap::Unlock(); } + cPixmap::Unlock(); return state; } diff --git a/tvguideosd.h b/tvguideosd.h index bc46728..717ce10 100644 --- a/tvguideosd.h +++ b/tvguideosd.h @@ -13,6 +13,7 @@ private: cTimeLine *timeLine; cChannelGroups *channelGroups; cFooter *footer; + cRecMenuManager *recMenuManager; bool detailViewActive; void drawOsd(); void readChannels(const cChannel *channelStart); @@ -42,6 +43,7 @@ private: void ScrollBack(); eOSState ChannelSwitch(); void DetailedEPG(); + void SetTimers(); void dump(); public: cTvGuideOsd(void); -- cgit v1.2.3