diff options
-rw-r--r-- | HISTORY | 3 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rw-r--r-- | channelcolumn.c | 17 | ||||
-rw-r--r-- | channelcolumn.h | 6 | ||||
-rw-r--r-- | config.c | 18 | ||||
-rw-r--r-- | config.h | 6 | ||||
-rw-r--r-- | detailview.c | 53 | ||||
-rw-r--r-- | detailview.h | 15 | ||||
-rw-r--r-- | dummygrid.c | 1 | ||||
-rw-r--r-- | epggrid.c | 24 | ||||
-rw-r--r-- | epggrid.h | 6 | ||||
-rw-r--r-- | footer.c | 4 | ||||
-rw-r--r-- | grid.h | 7 | ||||
-rw-r--r-- | headergrid.c | 4 | ||||
-rw-r--r-- | icons/arrow_left.png | bin | 0 -> 428 bytes | |||
-rw-r--r-- | icons/arrow_right.png | bin | 0 -> 438 bytes | |||
-rw-r--r-- | icons/delete_active.png | bin | 0 -> 58636 bytes | |||
-rw-r--r-- | icons/delete_inactive.png | bin | 0 -> 11721 bytes | |||
-rw-r--r-- | icons/edit_active.png | bin | 0 -> 16127 bytes | |||
-rw-r--r-- | icons/edit_inactive.png | bin | 0 -> 53216 bytes | |||
-rw-r--r-- | icons/icon_backspace.png | bin | 0 -> 2012 bytes | |||
-rw-r--r-- | icons/icon_del_ins.png | bin | 0 -> 1680 bytes | |||
-rw-r--r-- | icons/icon_shift.png | bin | 0 -> 1484 bytes | |||
-rw-r--r-- | icons/info_active.png | bin | 0 -> 11487 bytes | |||
-rw-r--r-- | icons/info_inactive.png | bin | 0 -> 9034 bytes | |||
-rw-r--r-- | icons/no.png | bin | 0 -> 52583 bytes | |||
-rw-r--r-- | icons/record_active.png | bin | 0 -> 7098 bytes | |||
-rw-r--r-- | icons/record_inactive.png | bin | 0 -> 5897 bytes | |||
-rw-r--r-- | icons/yes.png | bin | 0 -> 35529 bytes | |||
-rw-r--r-- | imageloader.c | 13 | ||||
-rw-r--r-- | imageloader.h | 1 | ||||
-rw-r--r-- | osdmanager.c | 31 | ||||
-rwxr-xr-x | po/de_DE.po | 344 | ||||
-rw-r--r-- | recmanager.c | 598 | ||||
-rw-r--r-- | recmanager.h | 53 | ||||
-rw-r--r-- | recmenu.c | 525 | ||||
-rw-r--r-- | recmenu.h | 61 | ||||
-rw-r--r-- | recmenuitem.c | 2016 | ||||
-rw-r--r-- | recmenuitem.h | 465 | ||||
-rw-r--r-- | recmenumanager.c | 521 | ||||
-rw-r--r-- | recmenumanager.h | 38 | ||||
-rw-r--r-- | services/epgsearch.h | 61 | ||||
-rw-r--r-- | setup.c | 6 | ||||
-rw-r--r-- | styledpixmap.c | 8 | ||||
-rw-r--r-- | styledpixmap.h | 37 | ||||
-rw-r--r-- | switchtimer.c | 109 | ||||
-rw-r--r-- | switchtimer.h | 30 | ||||
-rw-r--r-- | themes/tvguide-darkblue.theme | 12 | ||||
-rw-r--r-- | themes/tvguide-default.theme | 12 | ||||
-rw-r--r-- | themes/tvguide-keepitsimple.theme | 14 | ||||
-rw-r--r-- | themes/tvguide-nOpacity.theme | 12 | ||||
-rw-r--r-- | themes/tvguide-nOpacitydarkred.theme | 12 | ||||
-rw-r--r-- | themes/tvguide-nOpacitygreen.theme | 12 | ||||
-rw-r--r-- | themes/tvguide-nOpacityiceblue.theme | 15 | ||||
-rw-r--r-- | timeline.c | 10 | ||||
-rw-r--r-- | timer.c | 46 | ||||
-rw-r--r-- | timer.h | 15 | ||||
-rw-r--r-- | tools.c | 370 | ||||
-rw-r--r-- | tvguide.c | 12 | ||||
-rw-r--r-- | tvguideosd.c | 132 | ||||
-rw-r--r-- | tvguideosd.h | 2 |
61 files changed, 5564 insertions, 201 deletions
@@ -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 @@ -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(); }; @@ -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);
@@ -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 <sstream> #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) { @@ -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) {
@@ -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();
};
@@ -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 +}
@@ -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 Binary files differnew file mode 100644 index 0000000..a65e47d --- /dev/null +++ b/icons/arrow_left.png diff --git a/icons/arrow_right.png b/icons/arrow_right.png Binary files differnew file mode 100644 index 0000000..97fa123 --- /dev/null +++ b/icons/arrow_right.png diff --git a/icons/delete_active.png b/icons/delete_active.png Binary files differnew file mode 100644 index 0000000..b01dc5c --- /dev/null +++ b/icons/delete_active.png diff --git a/icons/delete_inactive.png b/icons/delete_inactive.png Binary files differnew file mode 100644 index 0000000..b327f07 --- /dev/null +++ b/icons/delete_inactive.png diff --git a/icons/edit_active.png b/icons/edit_active.png Binary files differnew file mode 100644 index 0000000..5b3b667 --- /dev/null +++ b/icons/edit_active.png diff --git a/icons/edit_inactive.png b/icons/edit_inactive.png Binary files differnew file mode 100644 index 0000000..d4f59a1 --- /dev/null +++ b/icons/edit_inactive.png diff --git a/icons/icon_backspace.png b/icons/icon_backspace.png Binary files differnew file mode 100644 index 0000000..e20b7bd --- /dev/null +++ b/icons/icon_backspace.png diff --git a/icons/icon_del_ins.png b/icons/icon_del_ins.png Binary files differnew file mode 100644 index 0000000..3d06162 --- /dev/null +++ b/icons/icon_del_ins.png diff --git a/icons/icon_shift.png b/icons/icon_shift.png Binary files differnew file mode 100644 index 0000000..998d7fa --- /dev/null +++ b/icons/icon_shift.png diff --git a/icons/info_active.png b/icons/info_active.png Binary files differnew file mode 100644 index 0000000..7f1ccf7 --- /dev/null +++ b/icons/info_active.png diff --git a/icons/info_inactive.png b/icons/info_inactive.png Binary files differnew file mode 100644 index 0000000..9a79e95 --- /dev/null +++ b/icons/info_inactive.png diff --git a/icons/no.png b/icons/no.png Binary files differnew file mode 100644 index 0000000..3630d29 --- /dev/null +++ b/icons/no.png diff --git a/icons/record_active.png b/icons/record_active.png Binary files differnew file mode 100644 index 0000000..8c42156 --- /dev/null +++ b/icons/record_active.png diff --git a/icons/record_inactive.png b/icons/record_inactive.png Binary files differnew file mode 100644 index 0000000..6720a6f --- /dev/null +++ b/icons/record_inactive.png diff --git a/icons/yes.png b/icons/yes.png Binary files differnew file mode 100644 index 0000000..ec30010 --- /dev/null +++ b/icons/yes.png 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 <string>
-#include <sstream>
-
#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: <see README>\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 <string> +#include <vector> +#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; i<numConflicts; i++) { + if (timerIDs[i] == involvedID) + return true; + } + return false; +} + +cRecManager::cRecManager(void) { + epgSearchPlugin = NULL; + epgSearchAvailable = false; +} + +cRecManager::~cRecManager(void) { +} + +void cRecManager::SetEPGSearchPlugin(void) { + epgSearchPlugin = cPluginManager::GetPlugin("epgsearch"); + if (epgSearchPlugin) { + epgSearchAvailable = true; + } +} + +cTimer *cRecManager::createTimer(const cEvent *event) { + cTimer *timer = new cTimer(event); + Timers.Add(timer); + Timers.SetModified(); + isyslog("timer %s added (active)", *timer->ToDescr()); + 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<TVGuideTimerConflict> 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<TVGuideTimerConflict> 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<std::string> conflicts = epgSearch->handler->TimerConflictList(); + int numConflicts = conflicts.size(); + if (numConflicts > 0) { + for (std::list<std::string>::iterator it=conflicts.begin(); it != conflicts.end(); ++it) { + TVGuideTimerConflict sConflict; + splitstring s(it->c_str()); + std::vector<std::string> flds = s.split(':'); + if (flds.size() < 2) + continue; + sConflict.time = atoi(flds[0].c_str()); + splitstring s2(flds[1].c_str()); + std::vector<std::string> 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<std::string> flds3 = s3.split('#'); + std::vector<int> 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<TVGuideEPGSearchTemplate> cRecManager::ReadEPGSearchTemplates(void) { + cString ConfigDir = cPlugin::ConfigDirectory("epgsearch"); + cString epgsearchConf = "epgsearchtemplates.conf"; + cString fileName = AddDirectory(*ConfigDir, *epgsearchConf); + std::vector<TVGuideEPGSearchTemplate> 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<std::string> 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<std::string>::iterator it=results.begin(); it != results.end(); ++it) { + try { + splitstring s(it->c_str()); + std::vector<std::string> 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<Epgsearch_searchresults_v1_0::cServiceSearchResult> *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<int> 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<TVGuideTimerConflict> 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<TVGuideEPGSearchTemplate> 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; i<numSteps; i++) { + clr = AlphaBlend(clrBgr, clrBlend, alpha); + for (int y = i*stepY; y < (i+1)*stepY; y++) { + for (int x=0; x<width; x++) { + image->SetPixel(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<cRecMenuItem> 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 <math.h> +#include <vdr/remote.h> +#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; i<days.length(); ++i) { + int charWidth = font->Width(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; day<days.length(); ++day) { + cString strDay = cString::sprintf("%c", days.at(day)); + pixmapWeekdays->DrawRectangle(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<numConflicts; i++) {
+ if (conflictList[i].timerInvolved(timerID)) {
+ showTimerConflict = i;
+ break;
+ }
+ }
+ }
+ if (showTimerConflict > -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<TVGuideTimerConflict> conflictList;
+ std::vector<TVGuideEPGSearchTemplate> 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 <string> #include <list> #include <memory> @@ -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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> ChanGrpList() = 0; // returns a list of channel groups maintained by epgsearch virtual std::list<std::string> BlackList() = 0; @@ -164,4 +167,36 @@ struct Epgsearch_services_v1_0 std::auto_ptr<cServiceHandler> handler; }; +// Data structures for service "Epgsearch-services-v1.1" +class cServiceHandler_v1_1 : public cServiceHandler +{ + public: + // Get timer conflicts + virtual std::list<std::string> 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<cServiceHandler_v1_1> 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<std::string> 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<cServiceHandler_v1_2> handler; +}; + #endif @@ -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 <vdr/plugin.h> + +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<cSwitchTimer>, 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 @@ -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)));
@@ -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 @@ -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 @@ -0,0 +1,370 @@ +#include <string>
+#include <vector>
+#include <sstream>
+
+/****************************************************************************************
+* 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<std::string> flds;
+public:
+ splitstring(const char *s) : std::string(s) { };
+ std::vector<std::string>& 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<std::string>& 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 <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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;
+}
@@ -11,6 +11,7 @@ #include <vdr/osd.h> #include <vdr/plugin.h> #include <vdr/device.h> +#include <vdr/menu.h> #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 <stdlib.h>
@@ -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);
|