/* * dspitems.c: A plugin for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * (c) 2007-2011 Jörg Wendel * * Date: 14.04.13 */ //*************************************************************************** // Includes //*************************************************************************** #include #include #include #include #include #include const int maxGranularity = 500; //*************************************************************************** // Init Statics //*************************************************************************** Renderer* cDisplayItem::render = 0; cGraphTFTDisplay* cDisplayItem::vdrStatus = 0; int cDisplayItem::forceDraw = yes; uint64_t cDisplayItem::nextForce = 0; cDisplayItem* cDisplayItem::selectedItem = 0; //*************************************************************************** // cDisplayItem //*************************************************************************** void cDisplayItem::scheduleForce(uint64_t aTime) { if (!nextForce || nextForce > aTime) { nextForce = aTime; tell(1, "schedule force in (%ldms)", nextForce - msNow()); } } void cDisplayItem::scheduleDrawAt(uint64_t aTime) { if (!nextDraw || nextDraw > aTime) { nextDraw = aTime; tell(1, "schedule next draw of '%s'[%s] in (%ldms)", nameOf(), Debug().c_str(), nextDraw - msNow()); } } void cDisplayItem::scheduleDrawIn(int aTime) { uint64_t at = aTime + msNow(); // the maximal redraw granularity is 500ms (maxGranularity), due to this // adjust to next full granularity step at = round(at / maxGranularity) * maxGranularity; scheduleDrawAt(at); } void cDisplayItem::scheduleDrawNextFullMinute() { uint64_t ms = SECONDS(((time(0)/60 +1) * 60) - time(0)); scheduleDrawAt(ms+msNow()); } //*************************************************************************** // Object //*************************************************************************** cDisplayItem::cDisplayItem() : cThemeItem() { nextDraw = 0; section = 0; marquee_active = no; backgroundItem = 0; visible = yes; animation = na; nextAnimationAt = msNow(); animationImage = ""; actLineCount = na; lastConditionState = true; } cDisplayItem::~cDisplayItem() { } //*************************************************************************** // Evaluate Path //*************************************************************************** string cDisplayItem::evaluatePath() { string p; // iterate over path for (int i = 0; i < pathCount; i++) { if (evaluate(p, pathList[i].c_str()) != success) continue; tell(5, "check path '%s'", p.c_str()); if (p == "") { tell(1, "path '%s' empty, skipping", pathList[i].c_str()); continue; } // append plugin-conf-path (if required) if (p[0] != '/') { p = string(GraphTFTSetup.PluginConfPath) + "/graphTFT/themes/" + string(Thms::theTheme->getDir()) + "/" + p; } tell(4, "%d path [%s] converted to '%s'", i, pathList[i].c_str(), p.c_str()); if (fileExists(p.c_str())) return p; // check for wildcard if (p.find("?") != string::npos) { int actualAnimation = animation; // to next int maxNum = 9; string p1, p2, path; if (p.find("??") != string::npos) maxNum = 99; // reset animation loop if (actualAnimation > maxNum || actualAnimation < 0) actualAnimation = 0; // animated image if (nextAnimationAt > msNow()) return animationImage; p1 = p.substr(0, p.find_first_of('?')); p2 = p.substr(p.find_last_of('?')+1); animation = actualAnimation+1; path = p1 + Str::toStr(animation) + p2; tell(5, "Checking for '%s'", path.c_str()); while (!fileExists(path.c_str()) && animation != actualAnimation) { if (++animation > maxNum) animation = 0; path = p1 + Str::toStr(animation) + p2; tell(5, "Checking for '%s'", path.c_str()); } if (fileExists(path.c_str())) { tell(4, "Animated image '%s'", path.c_str()); animationImage = path; nextAnimationAt = msNow() + _delay; return animationImage; } } } tell(1, "Image '%s' not found :(", p.c_str()); return ""; } //*************************************************************************** // //*************************************************************************** int cDisplayItem::replayModeValue(ReplayMode rm) { bool play, forward; int speed; if (!vdrStatus->_replay.control || !vdrStatus->_replay.control->GetReplayMode(play, forward, speed)) return -1; switch (rm) { case rmSpeed: return speed; case rmForward: return forward; case rmPlay: return play; default: return -1; } return -1; } //*************************************************************************** // Variable of Group //*************************************************************************** const char* variableGroup(const char* value, const char* group, const char*& var) { if (!group) return 0; if (strncasecmp(value, group, strlen(group)) == 0) { var = value + strlen(group); return group; } return 0; } //*************************************************************************** // Lookup Variable //*************************************************************************** int cDisplayItem::lookupVariable(const char* name, string& value, const char* fmt) { const char* p; int status; value = ""; p = variable(name, fmt, status); if (p) { value = p; return success; } if (status == success) return fail; // nothing found ... -> // check theme for global variable if (cThemeItem::lookupVariable(name, value, fmt) == success) return success; // variable unknown :( tell(0, "Unexpected variable '%s'", name); return fail; } const char* cDisplayItem::variable(const char* name, const char* fmt, int& status) { static char buf[1000]; int total, current; const char* var; const char* group; status = success; if ((group = variableGroup(name, "music", var))) { if (strcasecmp(var, "Track") == 0) return vdrStatus->_music.nextTrack(); else if (strcasecmp(var, "Artist") == 0) return vdrStatus->_music.artist.c_str(); else if (strcasecmp(var, "Album") == 0) return vdrStatus->_music.album.c_str(); else if (strcasecmp(var, "Genre") == 0) return vdrStatus->_music.genre.c_str(); else if (strcasecmp(var, "Year") == 0) return vdrStatus->_music.year.c_str(); else if (strcasecmp(var, "Filename") == 0) return vdrStatus->_music.filename.c_str(); else if (strcasecmp(var, "Comment") == 0) return vdrStatus->_music.comment.c_str(); else if (strcasecmp(var, "Frequence") == 0) return Str::toStr(vdrStatus->_music.frequence/1000); else if (strcasecmp(var, "Bitrate") == 0) return Str::toStr(vdrStatus->_music.bitrate/1000); else if (strcasecmp(var, "StereoMode") == 0) return vdrStatus->_music.smode.c_str(); else if (strcasecmp(var, "Index") == 0) return Str::toStr(vdrStatus->_music.index); else if (strcasecmp(var, "Count") == 0) return Str::toStr(vdrStatus->_music.count); else if (strcasecmp(var, "CurrentTrack") == 0) return vdrStatus->_music.currentTrack.c_str(); else if (strcasecmp(var, "PlayStatus") == 0) return vdrStatus->_music.status.c_str(); if (strcasecmp(var, "CoverName") == 0) return vdrStatus->_coverPath.c_str(); else if (strcasecmp(var, "Rating") == 0) return Str::toStr(vdrStatus->_music.rating); else if (strcasecmp(var, "Loop") == 0) return Str::toStr(vdrStatus->_music.loop); else if (strcasecmp(var, "Timer") == 0) return Str::toStr(vdrStatus->_music.timer); else if (strcasecmp(var, "Copy") == 0) return Str::toStr(vdrStatus->_music.copy); else if (strcasecmp(var, "Lyrics") == 0) return Str::toStr(vdrStatus->_music.lyrics); else if (strcasecmp(var, "Shuffle") == 0) return Str::toStr(vdrStatus->_music.shuffle); else if (strcasecmp(var, "Shutdown") == 0) return Str::toStr(vdrStatus->_music.shutdown); else if (strcasecmp(var, "Recording") == 0) return Str::toStr(vdrStatus->_music.recording); else if (strcasecmp(var, "ButtonRed") == 0) return vdrStatus->_music.red.c_str(); else if (strcasecmp(var, "ButtonGreen") == 0) return vdrStatus->_music.green.c_str(); else if (strcasecmp(var, "ButtonYellow") == 0) return vdrStatus->_music.yellow.c_str(); else if (strcasecmp(var, "ButtonBlue") == 0) return vdrStatus->_music.blue.c_str(); } else if ((group = variableGroup(name, "recording", var)) || (group = variableGroup(name, "replay", var)) || (group = variableGroup(name, "rowRecording", var)) || (group = variableGroup(name, "selectedRowRecording", var))) { const cRecording* recording = 0; if (strcmp(group, "recording") == 0) recording = Recordings.GetByName(vdrStatus->_recording.c_str()); else if (strcmp(group, "replay") == 0) recording = Recordings.GetByName(vdrStatus->_replay.fileName.c_str()); else if (strcmp(group, "rowRecording") == 0) vdrStatus->_menu.drawingRow == na ? 0 : recording = vdrStatus->_menu.items[vdrStatus->_menu.drawingRow].recording; else if (strcmp(group, "selectedRowRecording") == 0) recording = vdrStatus->_menu.currentRow == na ? 0 : vdrStatus->_menu.items[vdrStatus->_menu.currentRow].recording; // recording variables if (!recording && strcmp(group, "replay") != 0) { // tell(0, "current row is (%s)", vdrStatus->_menu.currentRow); tell(0, "Missing '%s' info, can't lookup variable", group, name); return 0; } if (vdrStatus->_replay.control) { if (strcasecmp(var, "Speed") == 0) return Str::toStr(replayModeValue(rmSpeed)); else if (strcasecmp(var, "Play") == 0) return Str::toStr(replayModeValue(rmPlay)); else if (strcasecmp(var, "Forward") == 0) return Str::toStr(replayModeValue(rmForward)); else if (strcasecmp(var, "Current") == 0) { vdrStatus->_replay.control->GetIndex(current, total); #if APIVERSNUM < 10704 return formatDateTime((current ? current : 1) / FRAMESPERSEC, fmt, buf, sizeof(buf), yes); #else return formatDateTime((current ? current : 1) / (int)vdrStatus->_replay.control->FramesPerSecond(), fmt, buf, sizeof(buf), yes); #endif } else if (strcasecmp(var, "Total") == 0) { vdrStatus->_replay.control->GetIndex(current, total); #if APIVERSNUM < 10704 return formatDateTime((total ? total : 1) / FRAMESPERSEC, fmt, buf, sizeof(buf), yes); #else return formatDateTime((total ? total : 1) / (int)vdrStatus->_replay.control->FramesPerSecond(), fmt, buf, sizeof(buf), yes); #endif } else if (strcasecmp(var, "RawCurrent") == 0) { vdrStatus->_replay.control->GetIndex(current, total); return Str::toStr(current); } else if (strcasecmp(var, "RawTotal") == 0) { vdrStatus->_replay.control->GetIndex(current, total); return Str::toStr(total); } } if (recording) { if (strcasecmp(var, "Title") == 0) return Str::notNull(recording->Info()->Title()); else if (strcasecmp(var, "Path") == 0) return Str::notNull(recording->FileName()); else if (strcasecmp(var, "Time") == 0) return formatDateTime(recording->Start(), fmt, buf, sizeof(buf)); else if (strcasecmp(var, "EventID") == 0) return Str::toStr(!recording->Info() ? na : (int)recording->Info()->EventID()); // return Str::toStr(!recording->Info() ? na : na); else if (strcasecmp(var, "SubTitle") == 0) return Str::notNull(recording->Info()->ShortText()); else if (strcasecmp(var, "Description") == 0) return Str::notNull(recording->Info()->Description()); # if APIVERSNUM >= 10500 else if (strcasecmp(var, "Channel") == 0) return Str::notNull(recording->Info()->ChannelName()); # endif } else { if (strcasecmp(var, "Title") == 0) return Str::notNull(vdrStatus->_replay.name.c_str()); else if (strcasecmp(var, "EventID") == 0) return Str::toStr(na); else if (strcasecmp(var, "SubTitle") == 0) return ""; else if (strcasecmp(var, "Channel") == 0) return ""; else if (strcasecmp(var, "Path") == 0) return Str::notNull(vdrStatus->_replay.fileName.c_str()); else if (strcasecmp(var, "Time") == 0) return ""; else if (strcasecmp(var, "Description") == 0) return "no details available"; } } else if ((group = variableGroup(name, "volume", var))) { if (strcasecmp(var, "Mute") == 0) return Str::toStr(vdrStatus->_mute); else if (strcasecmp(var, "Level") == 0) return Str::toStr(vdrStatus->_volume); } else if ((group = variableGroup(name, "event", var)) || (group = variableGroup(name, "following", var)) || (group = variableGroup(name, "present", var)) || (group = variableGroup(name, "rowEvent", var)) || (group = variableGroup(name, "selectedRowEvent", var))) { tell(5, "lookup variable '%s' of group '%s'", var, group); #if VDRVERSNUM >= 10733 eTimerMatch timerMatch = tmNone; #else int timerMatch = 0; #endif const cEvent* event = 0; const cChannel* channel = 0; if (strcmp(group, "event") == 0) { if (vdrStatus->_event) { event = vdrStatus->_event; channel = Channels.GetByChannelID(event->ChannelID()); } } else if (strcmp(group, "present") == 0) { event = vdrStatus->_presentEvent; channel = vdrStatus->_presentChannel; } else if (strcmp(group, "following") == 0) { event = vdrStatus->_followingEvent; channel = vdrStatus->_presentChannel; } else if (strcmp(group, "rowEvent") == 0) { event = vdrStatus->_menu.drawingRow == na ? 0 : vdrStatus->_menu.items[vdrStatus->_menu.drawingRow].event; channel = vdrStatus->_menu.drawingRow == na ? 0 : vdrStatus->_menu.items[vdrStatus->_menu.drawingRow].channel; } else if (strcmp(group, "selectedRowEvent") == 0) { event = vdrStatus->_menu.currentRow == na ? 0 : vdrStatus->_menu.items[vdrStatus->_menu.currentRow].event; channel = vdrStatus->_menu.currentRow == na ? 0 : vdrStatus->_menu.items[vdrStatus->_menu.currentRow].channel; } // this items don't need a channel strcpy(buf, tr("No EPG data available.")); if (strcasecmp(var, "Title") == 0) { if (!event) { // No EPG Data available // RDS is available only for presentTitle if ((vdrStatus->_rds.text.empty()) || (strcmp(group, "present") != 0)) { if (!channel) return buf; return channel->Name(); } return vdrStatus->_rds.text.c_str(); } return Str::notNull(event->Title()); } else if (strcasecmp(var, "SubTitle") == 0) { if (!event) { // No EPG Data available // RDS is available only for presentSubTitle if (((vdrStatus->_rds.title.empty() && vdrStatus->_rds.artist.empty())) || (strcmp(group, "present") != 0)) { return buf; } sprintf(buf, "%s : %s\n%s : %s", tr("Title"), vdrStatus->_rds.title.c_str(), tr("Artist"), vdrStatus->_rds.artist.c_str()); return buf; } return Str::notNull(event->ShortText()); } else if (strcasecmp(var, "Description") == 0) { if (!event) return buf; // No EPG Data available return Str::notNull(event->Description()); } if (!channel || !channel->Number()) { tell(1, "Info: Can't find channel for '%s' in '%s' of row %d", group, var, vdrStatus->_menu.drawingRow); return 0; } // this items don't need a event if (strcasecmp(var, "ChannelName") == 0) return channel ? channel->Name() : ""; else if (strcasecmp(var, "ChannelNumber") == 0) return Str::toStr(channel ? channel->Number() : 0); // check the event if (!event) { tell(1, "Info: Missing event for '%s', can't lookup variable in '%s'", group, var); return 0; } // following items need a event cTimer* timer = Timers.GetMatch(event, &timerMatch); if (strcasecmp(var, "ID") == 0) return Str::toStr((int)event->EventID()); else if (strcasecmp(var, "StartTime") == 0) return formatDateTime(event->StartTime(), fmt, buf, sizeof(buf)); else if (strcasecmp(var, "EndTime") == 0) return formatDateTime(event->EndTime(), fmt, buf, sizeof(buf)); else if (strcasecmp(var, "Duration") == 0) return Str::toStr(event->Duration() / 60); else if (strcasecmp(var, "HasTimer") == 0) return timer && timerMatch == tmFull ? "1" : "0"; else if (strcasecmp(var, "HasPartialTimer") == 0) return timer && timerMatch == tmPartial ? "1" : "0"; else if (strcasecmp(var, "HasPartialTimerBefore") == 0) return timer && timerMatch == tmPartial && timer->StartTime() >= event->StartTime() ? "1" : "0"; else if (strcasecmp(var, "HasPartialTimerAfter") == 0) return timer && timerMatch == tmPartial && timer->StartTime() < event->StartTime() ? "1" : "0"; else if (strcasecmp(var, "IsRunning") == 0) return event->SeenWithin(30) && event->IsRunning() ? "1" : "0"; else if (strcasecmp(var, "Progress") == 0) { if (time(0) > event->StartTime()) return Str::toStr((int)(100.0 / ((double)event->Duration() / (double)(time(0)-event->StartTime())))); else return Str::toStr((int)(100.0 / ((double)event->Duration() / (double)(event->StartTime()-time(0))))); } else if (strcasecmp(var, "IsRecording") == 0) return timerMatch == tmFull && timer && timer->Recording() ? "1" : "0"; } else { if (strcasecmp(name, "menuTitle") == 0) return vdrStatus->_menu.title.c_str(); if (strcasecmp(name, "textWidth") == 0) return Str::toStr(textWidth); if (strcasecmp(name, "colCount") == 0) return Str::toStr(vdrStatus->_menu.items[vdrStatus->_menu.drawingRow].tabCount); if (strcasecmp(name, "rowCount") == 0) return Str::toStr((int)vdrStatus->_menu.items.size()); if (strcasecmp(name, "visibleRows") == 0) return Str::toStr(vdrStatus->_menu.visibleRows); if (strcasecmp(name, "currentRow") == 0) return Str::toStr(vdrStatus->_menu.currentRow); if (strcasecmp(name, "touchMenu") == 0) return Str::toStr(vdrStatus->touchMenu); if (strcasecmp(name, "themeVersion") == 0) return Thms::theTheme->getThemeVersion().c_str(); if (strcasecmp(name, "syntaxVersion") == 0) return Thms::theTheme->getSyntaxVersion().c_str(); if (strcasecmp(name, "themeName") == 0) return Thms::theTheme->getName().c_str(); if (strcasecmp(name, "mouseX") == 0) return Str::toStr(vdrStatus->mouseX); if (strcasecmp(name, "mouseY") == 0) return Str::toStr(vdrStatus->mouseY); if (strcasecmp(name, "mouseKey") == 0) return Str::toStr(vdrStatus->mouseKey); if (strcasecmp(name, "calibrationInstruction") == 0) return vdrStatus->calibration.instruction.c_str(); if (strcasecmp(name, "calibrationInfo") == 0) return vdrStatus->calibration.info.c_str(); if (strcasecmp(name, "calibrationCursorX") == 0) return Str::toStr(vdrStatus->calibration.cursorX); if (strcasecmp(name, "calibrationCursorY") == 0) return Str::toStr(vdrStatus->calibration.cursorY); if (strcasecmp(name, "calibrationTouchedX") == 0) return Str::toStr(vdrStatus->calibration.lastX); if (strcasecmp(name, "calibrationTouchedY") == 0) return Str::toStr(vdrStatus->calibration.lastY); if (strcasecmp(name, "calibrationOffsetX") == 0) return Str::toStr(vdrStatus->calibration.settings.offsetX); if (strcasecmp(name, "calibrationOffsetY") == 0) return Str::toStr(vdrStatus->calibration.settings.offsetY); if (strcasecmp(name, "calibrationScaleX") == 0) return Str::toStr(vdrStatus->calibration.settings.scaleX, 4); if (strcasecmp(name, "calibrationScaleY") == 0) return Str::toStr(vdrStatus->calibration.settings.scaleY, 4); if (strcasecmp(name, "actRecordingCount") == 0) return Str::toStr((int)vdrStatus->_timers.countRunning()); if (strcasecmp(name, "actRecordingName") == 0) { if (vdrStatus->_showRecording >= 0 && vdrStatus->_showRecording < (int)vdrStatus->_timers.countRunning()) return Str::notNull(vdrStatus->_timers.getTimer(vdrStatus->_showRecording)->title.c_str()); } if (strcasecmp(name, "actRecordings") == 0) return vdrStatus->_timers.next(); if (strcasecmp(name, "actRunningRecordings") == 0) return vdrStatus->_timers.nextRunning(); if (strcasecmp(name, "actPendingRecordings") == 0) return vdrStatus->_timers.nextPending(); if (strcasecmp(name, "menuText") == 0) return vdrStatus->_menu.text.c_str(); if (strcasecmp(name, "STR") == 0) return Str::toStr(getFrontendSTR()); if (strcasecmp(name, "SNR") == 0) return Str::toStr(getFrontendSNR()); if (strcasecmp(name, "time") == 0) { if (strstr(fmt, "%s") || strstr(fmt, "%S") || strstr(fmt, "%T")) { // refresh every 1 second if (!_delay) scheduleDrawIn(1000); } else { // refresh at full minute scheduleDrawNextFullMinute(); } return formatDateTime(time(0), fmt, buf, sizeof(buf)); } } status = fail; return 0; } //*************************************************************************** // Evaluate Condition //*************************************************************************** int cDisplayItem::evaluateCondition(int recurse) { static Scan* scan = 0; int result; int state; int rightType = catUnknown; int leftType = catUnknown; int leftInt = 0; int rightInt = 0; string leftStr = ""; string rightStr = ""; char op[100]; *op = 0; char logicalOp[100]; *logicalOp = 0; string expression; if (!recurse || !scan) { if (_condition.size() <= 0) return yes; // beim Fehler erst mal 'no' ... ? if (evaluate(expression, _condition.c_str()) != success) return no; tell(4, "evaluating condition '%s' with expression '%s'", _condition.c_str(), expression.c_str()); // ... scan = new Scan(expression.c_str()); } // left expression scan->eat(); if (scan->isNum()) { leftInt = scan->lastInt(); leftType = catInteger; } else if (scan->isString()) { leftStr = scan->lastString(); leftType = catString; } else { tell(0, "Error: Invalid left '%s' expression in '%s'", scan->lastIdent(), expression.c_str()); return no; } // operator ? if ((state = scan->eat()) == success && scan->isOperator() && !scan->isLogical()) { strcpy(op, scan->lastIdent()); // right expression scan->eat(); if (scan->isNum()) { rightInt = scan->lastInt(); rightType = catInteger; } else if (scan->isString()) { rightStr = scan->lastString(); rightType = catString; } else { tell(0, "Error: Invalid right '%s' expression in '%s'", scan->lastIdent(), expression.c_str()); return no; } // check the condition if (leftType != rightType) { tell(0, "Error: Argument types of left and right " "agrument don't match in (%d/%d) '%s'", leftType, rightType, expression.c_str()); return no; } if (leftType == catInteger) result = condition(leftInt, rightInt, op); else result = condition(&leftStr, &rightStr, op); state = scan->eat(); } else if (leftType == catInteger) { result = leftInt ? true : false; } else { result = leftStr != "" ? true : false; } // any more expressions in here? tell(4, "check for further condition at '%s'", Str::notNull(scan->next())); if (state == success) { tell(4, "further condition found"); if (!scan->isLogical()) { tell(0, "Error: Invalid logical operator '%s' expression in '%s'", scan->lastIdent(), expression.c_str()); return no; } strcpy(logicalOp, scan->lastIdent()); // start a recursion ... if (strncmp(logicalOp, "&", 1) == 0) result = result && evaluateCondition(yes); else if (strncmp(logicalOp, "|", 1) == 0) result = result || evaluateCondition(yes); } delete scan; scan = 0; tell(4, "condition is '%s'; evaluated condition is '%s'; result is '%s'", _condition.c_str(), expression.c_str(), result ? "match" : "don't match"); return result; } //*************************************************************************** // evaluate the condition (prepare for string operation later ..) //*************************************************************************** int cDisplayItem::condition(int left, int right, const char* op) { tell(4, "evaluate condition '%d' '%s' '%d'", left, op, right); if (strcmp(op, ">") == 0) return left > right; if (strcmp(op, "<") == 0) return left < right; if (strcmp(op, ">=") == 0) return left >= right; if (strcmp(op, "<=") == 0) return left <= right; if (strcmp(op, "=") == 0 || strcmp(op, "==") == 0) return left == right; if (strcmp(op, "!=") == 0 || strcmp(op, "<>") == 0) return left != right; tell(0, "Unexpected operator '%s'", op); return no; } int cDisplayItem::condition(string* left, string* right, const char* op) { tell(4, "evaluate condition '%s' '%s' '%s'", left->c_str(), op, right->c_str()); if (strcmp(op, ">") == 0) return *left > *right; if (strcmp(op, "<") == 0) return *left < *right; if (strcmp(op, ">=") == 0) return *left >= *right; if (strcmp(op, "<=") == 0) return *left <= *right; if (strcmp(op, "=") == 0 || strcmp(op, "==") == 0) return *left == *right; if (strcmp(op, "!=") == 0 || strcmp(op, "<>") == 0) return *left != *right; tell(0, "Unexpected operator '%s'", op); return no; } //*************************************************************************** // Interface //*************************************************************************** int cDisplayItem::reset() { if (_scroll) { marquee_active = yes; marquee_left = no; marquee_idx = na; marquee_count = 0; marquee_strip = 0; scheduleDrawIn(0); } animation = na; nextAnimationAt = msNow(); animationImage = ""; textWidth = 0; return done; } int cDisplayItem::clear() { if (render) render->clear(); return done; } int cDisplayItem::draw() { int status = success; if (!render) { tell(0, "Fatal: Missing renderer object !"); return fail; } // check condition int cond = evaluateCondition(); if (groupOf() != groupMenu && groupOf() != groupTextList) { if (!cond) { tell(4, "Ignore drawing of '%s' due to condition '%s'", nameOf(), _condition.c_str()); status = ignore; } } // schedule due to the configured delay .. if (cond && _delay > 0 && visible) scheduleDrawIn(_delay); // condition state changed force immediate redraw ! if (lastConditionState != cond) { tell(4, "Condition '%s' of '%s' [%s] changed from (%d) to (%d), force draw", _condition.c_str(), nameOf(), Text() != "" ? Text().c_str() : Path().c_str(), lastConditionState, cond); lastConditionState = cond; scheduleForce(msNow() + 10); } return status; } int cDisplayItem::refresh() { tell(6, "timeMs::Now() (%ldms);", msNow()); tell(6, "nextForce at (%ldms)", nextForce); tell(6, "forceDraw '%s', nextDraw (%ldms), isForegroundItem(%d), Foreground(%d)", forceDraw ? "yes" : "no", nextDraw, isForegroundItem(), Foreground()); LogDuration ld("cDisplayItem::refresh()"); // force required ? (volume, animating, osd-message, ...) if (nextForce && msNow() >= nextForce) { forceDraw = yes; nextForce = 0; } // respect the maximal redraw granularity if ((nextDraw && (msNow() >= nextDraw-(maxGranularity/2-1))) || isForegroundItem() || forceDraw || Foreground()) { nextDraw = 0; int res = draw() == success ? 1 : 0; tell(1, "draw '%s', %s", nameOf(), res ? "done" : "skipped due to condition"); if (res > 0 && logLevel >= 3) { if (isForegroundItem() || forceDraw || Foreground()) { tell(3, "forceDraw(%d), isForegroundItem(%d), Foreground(%d)", forceDraw, isForegroundItem(), Foreground()); tell(3, "'%s' - '%s'", nameOf(), Text().size() ? Text().c_str() : Path().c_str()); } } return res; } return 0; } //*************************************************************************** // Painters //*************************************************************************** int cDisplayItem::drawText(const char* aText, int y, int height, int clear, int skipLines) { int width; unsigned int viewLength; string text = Str::notNull(aText); y = y ? y : _y; width = _width ? _width : Thms::theTheme->getWidth() - _x; height = height ? height : _height; height = height ? height : Thms::theTheme->getHeight() - _y; _scroll = _lines > 1 ? no : _scroll; viewLength = width / render->charWidthOf(_font.c_str(), _size); skipLines = skipLines ? skipLines : StartLine(); // draw background if (clear) drawBackRect(y, _bg_height ? _bg_height : height); if (!text.length()) return done; tell(5, "drawing text '%s' at %d/%d (%d/%d), '%s'(%d) lines (%d)!", text.c_str(), _x, y, width, height, _font.c_str(), _size, _lines); tell(5, "scroll is (%d) and marquee_active is (%d) for test '%s'", _scroll, marquee_active, text.c_str()); // get line count of the actual text actLineCount = render->lineCount(text.c_str(), _font.c_str(), _size, width); // ... if (!_scroll || text.length() <= viewLength) { // normal and 'dots' mode render->text(text.c_str(), _font.c_str(), _size, _align, _x, y, _red, _green, _blue, width, height, _lines, _dots, skipLines); } else { // marquee and ticker mode tell(5, "drawing text in scroll mode '%s'", text.c_str()); if (_scroll == 1 && marquee_idx + viewLength >= text.length()) marquee_left = yes; else if (_scroll == 2 && marquee_idx >= text.length()) marquee_idx = na; if (marquee_left) marquee_idx--; else marquee_idx++; if (marquee_idx == 0) { marquee_left = no; marquee_count++; } if (_scroll_count && marquee_count > _scroll_count) { marquee_active = no; marquee_idx = 0; } render->text(text.substr(marquee_idx, text.length()).c_str(), _font.c_str(), _size, _align, _x, y, _red, _green, _blue, width, height, _lines, marquee_active ? no : _dots); if (marquee_active) { if (marquee_idx == 0) scheduleDrawIn(1000); else scheduleDrawIn(500); } } return done; } int cDisplayItem::drawRectangle() { render->rectangle(_x, _y, _width, _height, _red, _green, _blue, _transparent); return done; } int cDisplayItem::drawBackRect(int y, int height) { int x = _bg_x != na ? _bg_x : _x; y = y ? y : _bg_y != na ? _bg_y : _y; int width = _bg_width > 0 ? _bg_width : _width; height = height ? height : _bg_height > 0 ? _bg_height : _height; if (backgroundItem && backgroundItem->Path() != "") { // fill with part of the backround image tell(3, "Drawing backround area of '%s' for '%s'", backgroundItem->Path().c_str(), _text.c_str()); render->imagePart(backgroundItem->Path().c_str(), x, y, width, height); } //else if (_bg_transparent != 0) if (_bg_transparent != 0 || !backgroundItem || backgroundItem->Path() == "") { // fill with color render->rectangle(x, y, width, height, _bg_red, _bg_green, _bg_blue, _bg_transparent); } return done; } int cDisplayItem::drawImage(const char* path, int fit, int aspectRatio) { if (!path) path = _path.c_str(); if (fit == na) fit = _fit; if (aspectRatio == na) aspectRatio = _aspect_ratio; tell(3, "drawing image '%s' at %d/%d (%d/%d); fit = %d; aspectRatio = %d)", path, _x, _y, _width, _height, _fit, aspectRatio); if (BgWidth()) drawBackRect(); render->image(path, _x, _y, _width, _height, fit, aspectRatio); return done; } //*************************************************************************** // Format Data Time //*************************************************************************** const char* cDisplayItem::formatDateTime(time_t theTime, const char* fmt, char* date, int len, int absolut) { tm* tmp; int res; *date = 0; string format = fmt && *fmt ? fmt : (_format.length() ? _format : "%a %d.%m %H:%M"); // %s seems to be absolut as default ... if (absolut && format.find("%s") == string::npos) { localtime(&theTime); theTime += timezone; } tmp = localtime(&theTime); if (!tmp) { tell(0, "Error: Can't get localtime!"); return 0; } res = strftime(date, len, format.c_str(), tmp); if (!res) { tell(0, "Error: Can't convert time, maybe " "invalid format string '%s'!", format.c_str()); return 0; } if (format.find("%s") != string::npos || format.find("%S") != string::npos || format.find("%T") != string::npos) { // refresh in 1 second if (!_delay) scheduleDrawIn(1000); } else { // refresh at next full minute scheduleDrawNextFullMinute(); } return date; } //*************************************************************************** // Draw Image on Background Coordinates //*************************************************************************** int cDisplayItem::drawImageOnBack(const char* path, int fit) { if (!path) path = _path2.c_str(); tell(5, "drawing image '%s' at %d/%d (%d/%d)", path, _bg_x, _bg_y, _bg_width, _bg_height); render->image(path, _bg_x, _bg_y, _bg_width, _bg_height, fit); return done; } int cDisplayItem::drawProgressBar(double current, double total, string path, int y, int height, int withFrame, int clear) { int xDone; char tmp[50]; int bgX = _bg_x != na ? _bg_x : _x; int bgWidth = _bg_width ? _bg_width : _width; int bgY = y != na ? y : _bg_y != na ? _bg_y : _y; int bgHeight = height != na ? height : _bg_height ? _bg_height : _height; int red = _red; int green = _green; int blue = _blue; current = current < 0 ? 0 : current ; bgHeight = bgHeight ? bgHeight : _height; height = height == na ? _height : height; y = y == na ? _y : y; if (!total) total = 1; xDone = (int)((current/total) * (float)_width); tell(4, "bar, %f/%f xDone=%d", current, total, xDone); // background if (clear) drawBackRect(y, height); if (_bg_x && withFrame) render->rectangle(bgX, bgY, bgWidth, bgHeight, _bg_red, _bg_green, _bg_blue, _bg_transparent); if (path != "") render->image(path.c_str(), _x, y, _width, height, true); else // if (_bg_x <= 0) render->rectangle(_x, y, _width, height, _bg_red, _bg_green, _bg_blue, _bg_transparent); // foreground if (path != "") { // with image render->rectangle(_x+xDone, y, _width-xDone, height, _bg_red, _bg_green, _bg_blue, _bg_transparent); } else { // without image if (_switch) { // colorchanging bar red = green = 255; blue = 0; double percent = current / total; if (percent < 0.5f) red = (int)(255.0f * percent * 2.0f); else green = (int)(255.0f * (1-percent) * 2.0f); } render->rectangle(_x, y, xDone, height, red, green, blue, _transparent); } // draw optional text if (_text == "percent") { int textHeight = _size * 5/3; sprintf(tmp, "%3.0f%%", current / (total/100)); render->text(tmp, _font.c_str(), _size, _align, _x, y + (height-textHeight)/2, 255-red, 255-green, 255-blue, bgWidth, height, 1, no); } else if (_text == "value") { int textHeight = _size * 5/3; sprintf(tmp, "%d%s / %d%s", (int)current, _unit.c_str(), (int)total, _unit.c_str()); render->text(tmp, _font.c_str(), _size, _align, _x, y + (height-textHeight)/2, 255-red, 255-green, 255-blue, bgWidth, height, 1, no); } return success; } int cDisplayItem::drawPartingLine(string text, int y, int height) { tell(5, "drawing parting line at %d/%d (%d/%d)", _x, y, _width, height); if (_path != "" && text != "") render->image(_path.c_str(), _x, y, _width, height); if (_path2 != "" && text == "") render->image(_path2.c_str(), _x, y, _width, height); if (text != "") render->text(text.c_str(), _font.c_str(), _size, _align, _x, y, _red, _green, _blue, _width, height, 1); return done; } //*************************************************************************** // Draw Classes //*************************************************************************** int cDisplayText::draw() { string p; if (evaluate(p, _text.c_str()) == success) { if (p != lastText || StartLine() < 0) { lastText = p; StartLine(0); } int vLines = Height() / (_size * 5/3); if (StartLine() >= lineCount()-vLines) StartLine(lineCount()-vLines); // first ... cal text width textWidth = render->charWidthOf(_font.c_str(), _size) * p.length(); // second ... check condition if (cDisplayItem::draw() != success) return fail; // third ... draw text return drawText(p.c_str()); } return fail; } int cDisplayRectangle::draw() { if (cDisplayItem::draw() != success) return fail; return drawRectangle(); } int cDisplayImage::draw() { if (cDisplayItem::draw() != success) return fail; string path = evaluatePath().c_str(); tell(3, "Looking for file '%s'", path.c_str()); if (!Str::isEmpty(path.c_str())) return drawImage(path.c_str()); drawBackRect(); return done; } int cDisplayImageFile::draw() { if (cDisplayItem::draw() != success) return fail; FILE* fp; char line[1000+TB]; *line = 0; char* c; fp = fopen(_path.c_str(), "r"); if (fp) { c = fgets(line, 1000, fp); if (c) line[strlen(line)] = 0; Str::allTrim(line); fclose(fp); } if (!_fit || _aspect_ratio) drawBackRect(); // info tell(5, "Looking for file '%s'", line); if (!Str::isEmpty(line) && fileExists(line)) { drawImage(line); } else { tell(4, "Info: Image '%s' not found, falling back to '%s'", line, _path2.c_str()); drawImage(_path2.c_str()); } return success; } int cDisplayCalibrationCursor::draw() { if (cDisplayItem::draw() != success) return fail; int width = _width ? _width : 20; int height = _height ? _height : 20; if (_path != "") render->image(_path.c_str(), vdrStatus->calibration.cursorX - width/2, vdrStatus->calibration.cursorY - height/2, width, height, yes); else render->rectangle(vdrStatus->calibration.cursorX - width/2, vdrStatus->calibration.cursorY - height/2, width, height, _red, _green, _blue, _transparent); return success; } int cDisplayMenuButton::draw() { if (cDisplayItem::draw() != success) return fail; int index = _item - itemMenuButton; if (index >= 0 && index <= 3) return drawText(vdrStatus->_menu.buttons[index].c_str(), 0, 0, no /*clear*/); return fail; } int cDisplayMenuButtonBackground::draw() { if (cDisplayItem::draw() != success) return fail; int index = _item - itemMenuButtonBackground; if (index < 0 || index > 3) return fail; if (vdrStatus->_menu.buttons[index].length()) { if (_path != "") return drawImage(_path.c_str()); } else if (_path2 != "") { return drawImage(_path2.c_str()); } return done; } int cDisplayMessage::draw() { static uint64_t showUntil = msNow(); static string lastMessage = ""; if (cDisplayItem::draw() != success) return fail; visible = no; tell(3, "draw message '%s', vdrStatus->_message", vdrStatus->_message.c_str()); if (lastMessage != vdrStatus->_message) { lastMessage = vdrStatus->_message; if (vdrStatus->_message == "") showUntil = msNow(); else showUntil = msNow() + SECONDS(Setup.OSDMessageTime); scheduleForce(showUntil); } if (msNow() < showUntil && vdrStatus->_message != "") { visible = yes; drawBackRect(); if (_path != "") drawImageOnBack(_path.c_str()); tell(3, "draw message '%s'", vdrStatus->_message.c_str()); if (vdrStatus->_message.length()) drawText(vdrStatus->_message.c_str(), 0, 0, no /*clean*/); scheduleForce(showUntil); return success; } return ignore; } int cDisplayVolumeMuteSymbol::draw() { static int lastMute = vdrStatus->_mute; static uint64_t showUntil = msNow(); if (cDisplayItem::draw() != success) return fail; visible = no; if (!_permanent && lastMute != vdrStatus->_mute) { lastMute = vdrStatus->_mute; showUntil = msNow() + _delay; scheduleForce(showUntil); } if (_permanent || msNow() < showUntil) { visible = yes; if (vdrStatus->_mute) return drawImage(_path.c_str()); // is muted else if (_path2 != "") return drawImage(_path2.c_str()); } return ignore; } int cDisplayVolumebar::draw() { static int lastVolume = vdrStatus->_volume; static uint64_t showUntil = msNow(); if (cDisplayItem::draw() != success) return fail; visible = no; if (!_permanent && lastVolume != vdrStatus->_volume) { lastVolume = vdrStatus->_volume; showUntil = msNow() + _delay; scheduleForce(showUntil); } if (_permanent || msNow() < showUntil) // ) && !vdrStatus->_mute) { visible = yes; if (_path2 != "") drawImageOnBack(_path2.c_str()); return drawProgressBar(vdrStatus->_volume, 255, _path, _y, _height, no /*withFrame*/, no /*clear*/); } return ignore; } int cDisplayTimebar::draw() { if (cDisplayItem::draw() != success) return fail; if (vdrStatus->_presentEvent && vdrStatus->_followingEvent) { string path = evaluatePath().c_str(); drawProgressBar(time(0) - vdrStatus->_presentEvent->StartTime(), vdrStatus->_followingEvent->StartTime() - vdrStatus->_presentEvent->StartTime(), path); } else drawBackRect(); if (!_delay) scheduleDrawIn(SECONDS(30)); return done; } int cDisplayProgressBar::draw() { if (cDisplayItem::draw() != success) return fail; string cur; string tot; if (evaluate(cur, _value.c_str()) == success && evaluate(tot, _total.c_str()) == success) { tell(3, "Progress: '%s'/'%s' %d/%d", cur.c_str(), tot.c_str(), atoi(cur.c_str()), atoi(tot.c_str())); string path = evaluatePath().c_str(); return drawProgressBar(atoi(cur.c_str()), atoi(tot.c_str()), path); } return ignore; } int cDisplaySysinfo::draw() { int status = ignore; if (cDisplayItem::draw() != success) return fail; string path = evaluatePath().c_str(); if (_type.find("cpu") == 0) { int load = Sysinfo::cpuLoad(); // if (forceDraw || abs(load - lastCpuLoad) > 2) { lastCpuLoad = load; if (_type == "cpuload") status = drawProgressBar(load, 100, path); else if (_type == "cpuidle") status = drawProgressBar(100-load, 100, path); } } else if (_type.find("mem") == 0) { unsigned long total, used, free, cached; Sysinfo::memInfoMb(total, used, free, cached); if (forceDraw || used != lastUsedMem) { int f = _factor / (1024*1024); // due to memInfoMb already return MB lastUsedMem = used; // scale to given factor total /= f; used /= f; free /= f; if (_type == "memused") status = drawProgressBar(used, total, path); else if (_type == "memfree") status = drawProgressBar(free, total, path); else if (_type == "memcached") status = drawProgressBar(cached, total, path); } } else if (_type == "disk" && _reference != "") { unsigned long freeM = 0, usedM = 0; char* dir = strdup(_reference.c_str()); char* c; if ((c = strchr(dir, '?'))) { int u; for (int i = 0; i < 10; i++) { *c = '0' + i; if (fileExists(dir)) { tell(6, "adding size of '%s'", dir); freeM += FreeDiskSpaceMB(dir, &u); usedM += u; } } } else { freeM = FreeDiskSpaceMB(dir, (int*)&usedM); } free(dir); // trick, at least 1 due to divide by zero error usedM = usedM ? usedM : 1; freeM = freeM ? freeM : 1; if (forceDraw || usedM != lastUsedDisk) { int f = _factor / (1024*1024); // due to FreeDiskSpaceMB return MB lastUsedDisk = usedM; // scale to given factor usedM /= f; freeM /= f; status = drawProgressBar(usedM, freeM + usedM, path); } } else { // return without scheduling next draw ! tell(0, "Ignoring sysinfo item of unexpected type '%s'", _type.c_str()); return fail; } return status; } int cDisplaySymbol::draw() { if (cDisplayItem::draw() != success) return fail; drawBackRect(); int status = ignore; const char* image = 0; cChannel* chan; int mailFlag = no; if (_item == itemMailSymbol) { if (cPluginManager::CallFirstService("MailBox-HasNewMail-1.0", &mailFlag)) { if (!_delay) scheduleDrawIn(SECONDS(30)); image = mailFlag ? _path.c_str() : _path2.c_str(); tell(5, "mailbox: status (%d)", mailFlag); } } if (vdrStatus->isModeNormal(vdrStatus->_mode) && (chan = Channels.GetByNumber(vdrStatus->_channel))) { switch (_item) { case itemSym2ch: image = chan->Apid(1) ? _path.c_str() : _path2.c_str(); break; case itemSymDD: image = chan->Dpid(0) ? _path.c_str() : _path2.c_str(); break; case itemSymVTX: image = chan->Tpid() ? _path.c_str() : _path2.c_str(); break; case itemSymCrypt: image = chan->Ca() ? _path.c_str() : _path2.c_str(); break; } } if (image && *image) status = drawImage(image); return status; } int cDisplayMailCount::draw() { if (cDisplayItem::draw() != success) return fail; int res = ignore; unsigned long mailCount = 0; if (cPluginManager::CallFirstService("MailBox-GetTotalUnseen-1.0", &mailCount)) { if (!_delay) scheduleDrawIn(SECONDS(30)); if (mailCount) res = drawText(itoa(mailCount)); tell(5, "mailbox: (%d) pending mails", mailCount); } return res; } int cDisplayBackground::draw() { if (cDisplayItem::draw() != success) return fail; // todo // implement a force of all other items !? if (_path != "") return cDisplayImage::draw(); return drawRectangle(); } int cDisplayTextList::draw() { if (cDisplayItem::draw() != success) return fail; string p; int lineHeight = _size * 5/3; int y = 0; int rows = _height / lineHeight; drawBackRect(); vdrStatus->_music.iter = 0; vdrStatus->_timers.iter = 0; for (int i = 0; i < rows; i++) { y = Y() + lineHeight * i; if (evaluate(p, _text.c_str()) == success) { if (!p.length()) break; tell(5, "TextList, drawing '%s' at (%d/%d)", p.c_str(), _x, y); drawText(p.c_str(), y, lineHeight); } } return success; } int cDisplayMenuSelected::draw() { if (cDisplayItem::draw() != success) return fail; selectedItem = this; return done; } int cDisplayMenuColumnSelected::draw() { if (cDisplayItem::draw() != success) return fail; selectedItem = this; return done; } int cDisplayMenuEventColumnSelected::draw() { if (cDisplayItem::draw() != success) return fail; selectedItem = this; return done; } //*************************************************************************** // Menu - old style //*************************************************************************** int cDisplayMenu::draw() { if (cDisplayItem::draw() != success) return fail; if (!selectedItem) return fail; int total; int tabPercent[cGraphTFTService::MaxTabs] = {0, 0, 0, 0, 0, 0}; int afterSelect = 0; int lineHeight = _size * 5/3; int lineHeightSelect = selectedItem->Size() * 5/3; int count = ((_height-lineHeightSelect) / lineHeight) + 1; int step = vdrStatus->_menu.currentRow - vdrStatus->_menu.currentRowLast; // step since last refresh vdrStatus->_menu.visibleRows = count; vdrStatus->_menu.lineHeight = lineHeight; vdrStatus->_menu.lineHeightSelected = lineHeightSelect; if (vdrStatus->_menu.topRow < 0) vdrStatus->_menu.topRow = max(0, vdrStatus->_menu.currentRow - count/2); // initial else if (vdrStatus->_menu.currentRow == vdrStatus->_menu.topRow-1) vdrStatus->_menu.topRow = vdrStatus->_menu.currentRow; // up else if (vdrStatus->_menu.currentRow == vdrStatus->_menu.topRow+count) vdrStatus->_menu.topRow++; // down else if (vdrStatus->_menu.currentRow < vdrStatus->_menu.topRow || vdrStatus->_menu.currentRow > vdrStatus->_menu.topRow+count) vdrStatus->_menu.topRow += step; // page up / page down if (vdrStatus->_menu.topRow > (int)vdrStatus->_menu.items.size()-count) vdrStatus->_menu.topRow = vdrStatus->_menu.items.size()-count; if (vdrStatus->_menu.topRow < 0) vdrStatus->_menu.topRow = 0; vdrStatus->_menu.currentRowLast = vdrStatus->_menu.currentRow; // calculate column width total = vdrStatus->_menu.charInTabs[0] + vdrStatus->_menu.charInTabs[1] + vdrStatus->_menu.charInTabs[2] + vdrStatus->_menu.charInTabs[3] + vdrStatus->_menu.charInTabs[4] + vdrStatus->_menu.charInTabs[5]; if (!total) return done; for (int i = 0; vdrStatus->_menu.charInTabs[i] != 0; i++) tabPercent[i] = (int)((100L / (double)total) * (double)vdrStatus->_menu.charInTabs[i]); tell(4, "Graphtft: tabs set to - %d:%d:%d:%d:%d", tabPercent[0], tabPercent[1], tabPercent[2], tabPercent[3], tabPercent[4]); // loop over visible rows ... for (int i = vdrStatus->_menu.topRow; i < min((int)vdrStatus->_menu.items.size(), vdrStatus->_menu.topRow + count); ++i) { cDisplayItem* p = i == vdrStatus->_menu.currentRow ? selectedItem : this; int y = p->Y() + ((i - vdrStatus->_menu.topRow) * lineHeight); if (i == vdrStatus->_menu.currentRow) { afterSelect = lineHeightSelect - lineHeight; // draw the selected backround if (selectedItem->Focus() != "") render->image(selectedItem->Focus().c_str(), _x, _y + (i - vdrStatus->_menu.topRow) * lineHeight, _width, _height); // draw the columns int x = selectedItem->X(); for (int t = 0; t < vdrStatus->_menu.items[i].tabCount; ++t) { if (vdrStatus->_menu.items[i].tabs[t] != "") { int width = (_width * tabPercent[t]) / 100; render->text(vdrStatus->_menu.items[i].tabs[t].c_str(), selectedItem->Font().c_str(), selectedItem->Size(), selectedItem->Align(), x, _y + ((i - vdrStatus->_menu.topRow) * lineHeight), selectedItem->Red(), selectedItem->Green(), selectedItem->Blue(), width, lineHeightSelect, 1); x += width; } } if (selectedItem->Path2() != "") render->image(selectedItem->Path2().c_str(), _x, _y + (i - vdrStatus->_menu.topRow) * lineHeight, _width, _height); if (p->StaticPicture()) { // for the selected item we optionaly draw a additional picture (ImageMap) // as image name use the text after the number in the last column ... string path = Thms::theTheme->getPathFromImageMap(vdrStatus->_menu.items[i].tabs[vdrStatus->_menu.items[i].tabCount-1].c_str()); if (path != "") { if (p->StaticX() && p->StaticY()) render->image(path.c_str(), p->StaticX(), p->StaticY(), p->StaticWidth(), p->StaticHeight(), yes, yes); else if (p->StaticX()) render->image(path.c_str(), p->StaticX(), _y + (i - vdrStatus->_menu.topRow) * lineHeight - ((p->StaticHeight() - lineHeightSelect) /2), p->StaticWidth(), p->StaticHeight(), yes, yes); } } } else { if (_path != "") render->image(_path.c_str(), _x, y + afterSelect, _width, _height); int x = _x; for (int t = 0; t < vdrStatus->_menu.items[i].tabCount; ++t) { if (vdrStatus->_menu.items[i].tabs[t] != "") { int width = (_width * tabPercent[t]) / 100; render->text(vdrStatus->_menu.items[i].tabs[t].c_str(), // test, _font.c_str(), _size, _align, // font, font-size, align x, y + afterSelect, _red, _green, _blue, width, lineHeight, 1); x += width; } } } } return done; } //*************************************************************************** // Menu Column - new 'column' style //*************************************************************************** int cDisplayMenuColumn::draw() { if (cDisplayItem::draw() != success) return fail; cGraphTFTDisplay::MenuInfo* _menu = &vdrStatus->_menu; int count; int lineHeight; int lineHeightSelect; int step = _menu->currentRow - _menu->currentRowLast; // step since last refresh int afterSelect = 0; // calc height and row count ... lineHeight = Height() ? Height() : Size() * 5/3; if (selectedItem) lineHeightSelect = selectedItem->Height() ? selectedItem->Height() : selectedItem->Size() * 5/3; else lineHeightSelect = lineHeight; count = ((_menu_height-lineHeightSelect) / lineHeight) + 1; // tell(0, "_menu->items.size(%d), _menu->topRow(%d), _menu->currentRow(%d)," // "_menu->currentRowLast(%d), count(%d)", // _menu->items.size(), _menu->topRow, _menu->currentRow, _menu->currentRowLast, count); _menu->visibleRows = count; _menu->lineHeight = lineHeight; _menu->lineHeightSelected = lineHeightSelect; if (_menu->topRow < 0) _menu->topRow = max(0, _menu->currentRow - count/2); // initial else if (_menu->currentRow == _menu->topRow-1) _menu->topRow = _menu->currentRow; // up else if (_menu->currentRow == _menu->topRow+count) _menu->topRow++; // down else if (_menu->currentRow < _menu->topRow || _menu->currentRow > _menu->topRow+count) _menu->topRow += step; // page up / page down if (_menu->topRow > (int)_menu->items.size()-count) _menu->topRow = _menu->items.size()-count; if (_menu->topRow < _menu->currentRow-count) _menu->topRow = _menu->currentRow-count; if (_menu->topRow < 0) _menu->topRow = 0; if (step) marquee_active = no; _menu->currentRowLast = _menu->currentRow; // paint the visible rows for this column for (int i = _menu->topRow; i < min((int)_menu->items.size(), _menu->topRow + count); ++i) { cDisplayItem* p = this; if (i == _menu->currentRow) { afterSelect = lineHeightSelect - lineHeight; if (!selectedItem || selectedItem->Number() == na) continue; p = selectedItem; } int y = p->MenuY() + ((i - _menu->topRow) * lineHeight) + (p == selectedItem ? 0 : afterSelect); _menu->drawingRow = i; // the row tell(4, "colcount of row (%d) is (%d)", i, _menu->items[i].tabCount); if (!p->evaluateCondition()) continue; // calc pos an width ... p->X(_menu->items[i].nextX); if (!p->Width()) p->Width(Thms::theTheme->getWidth()-p->X()); _menu->items[i].nextX = p->X() + p->Width() + p->Spacing(); if (i == _menu->currentRow) { if (p->ImageMap() && Number()) { // for the selected item we optionaly draw a additional picture (ImageMap) // as image name use the text after the number in the last column ... string path = Thms::theTheme->getPathFromImageMap(_menu->items[i].tabs[p->Number()-1].c_str()); if (path != "") { if (p->StaticX() && p->StaticY()) render->image(path.c_str(), p->StaticX(), p->StaticY(), p->StaticWidth(), p->StaticHeight(), yes, yes); else if (p->StaticX()) render->image(path.c_str(), p->StaticX(), y - ((p->StaticHeight() - lineHeightSelect) /2), p->StaticWidth(), p->StaticHeight(), yes, yes); } } if (p->StaticPicture() && Number()) { // Image with static position for this column if (_menu->items[i].tabs[p->Number()-1].c_str()[0]) { if (p->StaticX() && p->StaticY()) render->image(p->channelLogoPath(_menu->items[i].tabs[p->Number()-1].c_str(), Format().c_str()).c_str(), p->StaticX(), p->StaticY(), 0, 0); else if (p->StaticX()) render->image(p->channelLogoPath(_menu->items[i].tabs[p->Number()-1].c_str(), Format().c_str()).c_str(), p->StaticX(), y, 0, 0); } } if (p->StaticText() && Number()) { // Text with static position for this column if (_menu->items[i].tabs[p->Number()-1].c_str()[0]) { render->text(_menu->items[i].tabs[p->Number()-1].c_str(), // text p->Font().c_str(), p->Size(), p->Align(), // font, font-size, align p->StaticX(), // x-pos p->StaticY(), // y-pos p->Red(), p->Green(), p->Blue(), // RGB p->StaticWidth(), // width lineHeight, 1); // height, line-count } } } if (p->Width() == na) continue; if (_menu->items[i].type == itPartingLine) { cDisplayItem* partingLine = section->getItem(itemPartingLine); if (partingLine) partingLine->drawPartingLine(_menu->items[i].tabs[0], y, lineHeight); continue; } // draw image if (p->Focus() != "") render->image(p->Focus().c_str(), p->X(), y, p->Width(), lineHeight); if (p->Type() == "progress") { int current = 0; int n = 0; int barHeight = p->BarHeight(); if (p->BarHeightUnit() == iuPercent) // BarHeight in % of row barHeight = (int)(((double)lineHeight/100) * (double)p->BarHeight()); // calc progress while (_menu->items[i].tabs[p->Number()-1][n]) { if (_menu->items[i].tabs[p->Number()-1][n] == '|') current++; n++; } // draw column's progress-bar tell(5, "progress [%d] of (%d%%) at position y = %d [%s]", i, current, y, _menu->items[i].tabs[p->Number()-1].c_str()); if (current) p->drawProgressBar(current, 8, p->Path2(), y + (lineHeight-barHeight)/2, barHeight); } else if (p->Type() == "image") { string path; if (p->Path() != "") path = p->evaluatePath(); else path = p->channelLogoPath(_menu->items[i].tabs[p->Number()-1].c_str(), Format().c_str()); // draw column's image if (path.length()) { if (p != selectedItem) p->drawBackRect(y, lineHeight); int barHeight = p->BarHeight(); if (p->BarHeightUnit() == iuPercent) // BarHeight in % of row barHeight = (int)(((double)lineHeight/100) * (double)p->BarHeight()); int offset = (lineHeight - barHeight) / 2; render->image(path.c_str(), p->X(), y+offset, p->Width(), lineHeight-(2*offset), p->Fit(), p->AspectRatio()); } } else { // draw column's text string text; if (p->Text() != "") { if (evaluate(text, p->Text().c_str()) != success) text = ""; } else text = _menu->items[i].tabs[p->Number()-1].c_str(); tell(5, "draw '%s'", text.c_str()); p->drawText(text.c_str(), y, lineHeight, no); } } selectedItem = 0; return done; } //*************************************************************************** // Menu Event Column //*************************************************************************** int cDisplayMenuEventColumn::draw() { // static int selectedOffset = 0; if (cDisplayItem::draw() != success) return fail; if (!vdrStatus->_eventsReady) return fail; cGraphTFTDisplay::MenuInfo* _menu = &vdrStatus->_menu; string path; int count; int lineHeight; int lineHeightSelect = 0; int step = _menu->currentRow - _menu->currentRowLast; // step since last refresh int afterSelect = 0; // calc height ans row count ... lineHeight = Height() ? Height() : Size() * 5/3; if (selectedItem) lineHeightSelect = selectedItem->Height() ? selectedItem->Height() : selectedItem->Size() * 5/3; else lineHeightSelect = lineHeight; // assume normal hight this time count = ((_menu_height-lineHeightSelect) / lineHeight) + 1; _menu->visibleRows = count; _menu->lineHeight = lineHeight; _menu->lineHeightSelected = lineHeightSelect; if (_menu->topRow < 0) _menu->topRow = max(0, _menu->currentRow - count/2); // initial else if (_menu->currentRow == _menu->topRow-1) _menu->topRow = _menu->currentRow; // up else if (_menu->currentRow == _menu->topRow+count) _menu->topRow++; // down else if (_menu->currentRow < _menu->topRow || _menu->currentRow > _menu->topRow+count) _menu->topRow += step; // page up / page down if (_menu->topRow > (int)_menu->items.size()-count) _menu->topRow = _menu->items.size()-count; if (_menu->topRow < _menu->currentRow-count) _menu->topRow = _menu->currentRow-count; if (_menu->topRow < 0) _menu->topRow = 0; if (step) marquee_active = no; _menu->currentRowLast = _menu->currentRow; // paint the visible rows for this column for (int i = _menu->topRow; vdrStatus->_eventsReady && i < min((int)_menu->items.size(), _menu->topRow + count); ++i) { if (i == na) tell(0, "XXXXXXXXXXXXXXXXXXXXXXXXX"); _menu->drawingRow = i; // the row cDisplayItem* p = i == _menu->currentRow ? selectedItem : this; if (!p) continue; int y = p->MenuY() + ((i - _menu->topRow) * lineHeight) + afterSelect; tell(4, "colcount of row (%d) is (%d)", i, _menu->items[i].tabCount); if (i == _menu->currentRow) afterSelect = lineHeightSelect - lineHeight; if (_menu->items[i].type == itPartingLine) { cDisplayItem* partingLine = section->getItem(itemPartingLine); // nur für die erste Spalte zeichnen, partingLine // soll alle Spalten abdecken! if (partingLine && Number() == 0) partingLine->drawPartingLine(_menu->items[i].tabs[0], y, lineHeight); continue; } // don't check condition for itPartingLine if (!p->evaluateCondition()) continue; // draw focus image if (i == _menu->currentRow && p->Focus() != "") { render->image(p->Focus().c_str(), p->X(), y, // + selectedOffset, p->Width(), lineHeight); } if (p->Type() == "progress") { int barHeight = p->BarHeight(); string value; int current; lookupVariable("rowEventProgress", value); current = atoi(value.c_str()); if (p->BarHeightUnit() == iuPercent) // BarHeight in % of row barHeight = (int)(((double)lineHeight/100) * (double)p->BarHeight()); // draw column's progress-bar tell(5, "progress [%d] of (%d%%)", i, current); string path; if (p->Path() != "") path = evaluatePath().c_str(); else path = p->Path2(); if (current) p->drawProgressBar(current, 100, path, y + (lineHeight-barHeight)/2, barHeight); } else if (p->Type() == "image") { path = p->evaluatePath(); // draw column's image if (path.length()) { if (p != selectedItem) p->drawBackRect(y, lineHeight); int barHeight = p->BarHeight(); if (p->BarHeightUnit() == iuPercent) // BarHeight in % of row barHeight = (int)(((double)lineHeight/100) * (double)p->BarHeight()); int offset = (lineHeight - barHeight) / 2; render->image(path.c_str(), p->X(), y+offset, p->Width(), lineHeight-(2*offset), p->Fit(), p->AspectRatio()); } } else { // draw column's text string text; int textHeight = p->Size() * 5/3; textHeight -= textHeight/10; // 10% weniger if (evaluate(text, p->Text().c_str()) != success) text = ""; tell(5, "draw '%s' of item (%d)", text.c_str()); if (!p->Line()) p->drawText(text.c_str(), y + (p->AlignV() ? (lineHeight-textHeight)/2 : 0), lineHeight, no); else p->drawText(text.c_str(), y + ((p->Line()-1)*textHeight), textHeight, no); } } selectedItem = 0; return done; } //*************************************************************************** // Spectrum Analyzer //*************************************************************************** int cDisplaySpectrumAnalyzer::draw() { if (cDisplayItem::draw() != success) return fail; if (cPluginManager::CallFirstService(SPAN_GET_BAR_HEIGHTS_ID, 0)) { // tell(0, "draw SpectrumAnalyzer II"); cSpanService::Span_GetBarHeights_v1_0 GetBarHeights; int bandsSA = 20; int falloffSA = 8; unsigned int* barHeights = new unsigned int[bandsSA]; unsigned int* barHeightsLeftChannel = new unsigned int[bandsSA]; unsigned int* barHeightsRightChannel = new unsigned int[bandsSA]; unsigned int* barPeaksBothChannels = new unsigned int[bandsSA]; unsigned int* barPeaksLeftChannel = new unsigned int[bandsSA]; unsigned int* barPeaksRightChannel = new unsigned int[bandsSA]; unsigned int volumeLeftChannel; unsigned int volumeRightChannel; unsigned int volumeBothChannels; GetBarHeights.bands = bandsSA; GetBarHeights.barHeights = barHeights; GetBarHeights.barHeightsLeftChannel = barHeightsLeftChannel; GetBarHeights.barHeightsRightChannel = barHeightsRightChannel; GetBarHeights.volumeLeftChannel = &volumeLeftChannel; GetBarHeights.volumeRightChannel = &volumeRightChannel; GetBarHeights.volumeBothChannels = &volumeBothChannels; GetBarHeights.name = "graphtft"; GetBarHeights.falloff = falloffSA; GetBarHeights.barPeaksBothChannels = barPeaksBothChannels; GetBarHeights.barPeaksLeftChannel = barPeaksLeftChannel; GetBarHeights.barPeaksRightChannel = barPeaksRightChannel; if (cPluginManager::CallFirstService(SPAN_GET_BAR_HEIGHTS_ID, &GetBarHeights)) { int i; int barWidth = _width / (2 * bandsSA); int width = barWidth * 2 * bandsSA; tell(4, "width = %d; barWidth = %d; ", width, barWidth); if (_path != "") render->image(_path.c_str(), _x, _y, width, _height, true); else render->rectangle(_x, _y, width, _height, _red, _green, _blue, _transparent); for (i = 0; i < bandsSA; i++) { render->rectangle(_x + 2 * barWidth * i, _y, barWidth, _height - (barHeightsLeftChannel[i] * _height / 100), 0, 0, 0, _transparent); render->rectangle(_x + 2 * barWidth * i + barWidth, _y, barWidth, _height - (barHeightsRightChannel[i] * _height / 100), 0, 0, 0, _transparent); // the peak // height = barPeaksBothChannels[i] * item->Height() / 100; // if (height > 0) // { // render->rectangle(item->X() + barWidth*2*i+ barWidth + 1, // item->Y(), // item->X() + barWidth*2*i + barWidth+ barWidth + 1, // height, // item->Red(), item->Green(), item->Blue(), // item->Transparent()); // } } } delete[] barHeights; delete[] barHeightsLeftChannel; delete[] barHeightsRightChannel; delete[] barPeaksBothChannels; delete[] barPeaksLeftChannel; delete[] barPeaksRightChannel; } return done; }