diff options
Diffstat (limited to 'theme.c')
-rw-r--r-- | theme.c | 2012 |
1 files changed, 2012 insertions, 0 deletions
@@ -0,0 +1,2012 @@ +/** + * GraphTFT plugin for the Video Disk Recorder + * + * theme.c - A plugin for the Video Disk Recorder + * + * (c) 2004 Lars Tegeler, Sascha Volkenandt + * (c) 2006-2013 Jörg Wendel + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + * + **/ + +#include <sys/inotify.h> + +#include <theme.h> +#include <common.h> +#include <scan.h> + +#include "setup.h" + +//*************************************************************************** +// The Theme +//*************************************************************************** + +cGraphTFTTheme* Thms::theTheme = 0; +cGraphTFTThemes themes; + +//*************************************************************************** +// Convert Channel-Name in Logo Path +//*************************************************************************** + +string VariableProvider::channelLogoPath(const char* channel, + const char* format, int classic) +{ + unsigned int s = 0; + unsigned int d = 0; + unsigned int len = strlen(channel); + char* file = (char *)malloc(len+TB); + + // remove '\/:*?"<>| character + + while (channel[s]) + { + if (!strchr("/':*?\"<>\\", channel[s])) + file[d++] = channel[s]; + + s++; + } + + file[d] = 0; + + // special handling fore some channels + + if (!rep(file, "D [0-9] \\- ....")) + { + free(file); + file = strdup("DIREKT"); + } + + string path; + + if (classic) + path = "columnimages/" + string(file) + "." + + ((format && *format) ? string(format) : "png"); + else + path = string(file); + + free(file); + + tell(5, "converted logo name is '%s'", path.c_str()); + + return path; +} + +//*************************************************************************** +// +//*************************************************************************** + +const char* VariableProvider::splitFormatValue(const char* data, + char* value, char* format) +{ + const char* f = strchr(data, '/'); + + if (f) + { + strncpy(value, data, f-data); + value[f-data] = 0; + strcpy(format, f+1); + + return value; + } + + strcpy(value, data); + *format = 0; + + return value; +} + +//*************************************************************************** +// Variable Of +// - returns the first variable name in expession +// "{varTest} xx {varHallo}" -> varTest +//*************************************************************************** + +int VariableProvider::variableOf(string& name, const char* expression, char*& e) +{ + const char* s; + + tell(4, "variableOf '%s'", expression); + + if ((s = strchr(expression, '{'))) + { + s++; + + if (!(e = (char *)strchr(s, '}'))) + { + tell(0, "Parsing of [%s] failed, missing bracket '}'", expression); + return fail; + } + + // found expression + + name = string(expression, s-expression, e-s); + tell(4, "variable '%s' found", name.c_str()); + + return success; + } + + return fail; +} + +//*************************************************************************** +// Evaluate Variable +//*************************************************************************** + +int VariableProvider::evaluate(string& buf, const char* var) +{ + const char* s; + const char* e; + string v; + const char* p = var; + char exp[1000+TB]; + int len; + char tmp[1+TB]; + + buf = ""; + + tell(4, "evaluating '%s'", var); + + while ((s = strchr(p, '{'))) + { + if (s > p) + buf.append(p, s-p); + + if (!(e = strchr(p, '}'))) + { + tell(0, "Parsing of [%s] failed, missing bracket '}'", var); + return fail; + } + + // found expression + + e--; + len = min((int)(e-s), 1000); + + if (len < 1) + { + tell(0, "Parsing of [%s] failed, bracket missmatch", var); + return fail; + } + + strncpy(exp, s+1, len); + exp[len] = 0; + + tell(4, "variable '%s' found", exp); + + if (*exp == '\\' && strlen(exp) > 1) + { + tmp[0] = (char)atoi(exp+1); + tmp[1] = 0; + v = tmp; + + tell(4, "found character expression '%s'", v.c_str()); + } + else + { + char fmt[100+TB]; + char val[255+TB]; + + // split if expression contais format string + // in a expression a '/' always start a format string or special function + + splitFormatValue(exp, val, fmt); + + tell(4, "expression '%s' with variable '%s' and format '%s'", + exp, val, fmt); + + // lookup variable + + if (lookupVariable(val, v, fmt) != success) + return fail; + + tell(4, "I Variable '%s' evaluated, value is '%s'", val, v.c_str()); + + string vv; + + if (strchr(v.c_str(), '{') && evaluate(vv, v.c_str()) == success) + { + tell(4, "II Variable '%s' evaluated, value is '%s'", v.c_str(), vv.c_str()); + v = vv; + } + + // do the format stuff + + if (!Str::isEmpty(fmt) && strcasecmp(fmt, "toLogo") == 0) + v = channelLogoPath(v.c_str(), 0, no); + } + + buf.append(v); + + p = e+2; + } + + buf.append(p); + + return success; +} + +//*************************************************************************** +// Calculate Expression +// calc expressions like: "2 + 4" +//*************************************************************************** + +int VariableProvider::calcExpression(const char* expression) +{ + Scan* scan; + int result = 0; + char op[100]; *op = 0; + + if (!expression || !*expression) + return 0; + + scan = new Scan(expression, no); + + // left expression + + if (scan->eat(yes) != success || !scan->isNum()) + { + delete scan; + return result; + } + + result = scan->lastInt(); + + while (scan->eat() == success && scan->isOperator()) + { + strcpy(op, scan->lastIdent()); + + if (scan->eat(yes) != success || !scan->isNum()) + { + delete scan; + return result; + } + + result = calc(op, result, scan->lastInt()); + } + + delete scan; + + return result; +} + +int VariableProvider::calc(const char* op, int left, int right) +{ + int result = left; + + if (*op == '+') + result += right; + else if (*op == '-') + result -= right; + else if (*op == '*') + result *= right; + else if (*op == ':') + result /= right; + + return result; +} + +//*************************************************************************** +// cThemeService +//*************************************************************************** + +const char* cThemeService::items[] = +{ + "Include", + "Theme", + + "Text", + "Image", + "ImageFile", + "ImageDir", + "Rectangle", + "Timebar", + + "Message", + "VolumeMuteSymbol", + "Volumebar", + + "Menu", + "MenuSelected", + + "MenuButtonRed", + "MenuButtonGreen", + "MenuButtonYellow", + "MenuButtonBlue", + + "MenuButtonBackgroundRed", + "MenuButtonBackgroundGreen", + "MenuButtonBackgroundYellow", + "MenuButtonBackgroundBlue", + + "MenuImageMap", + + "SpectrumAnalyzer", + "PartingLine", + "Sysinfo", + "Background", + "TextList", + "Progressbar", + + "ClickArea", + "MenuNavigationArea", + "CalibrationCursor", + + "Column", + "ColumnSelected", + + "EventColumn", + "EventColumnSelected", + + "Defaults", + "VariableFile", + + 0 +}; + +const char* cThemeService::toName(ItemKind aItem) +{ + if (!isValid(aItem)) + return "unknown"; + + return items[aItem]; +} + +cThemeService::ItemKind cThemeService::toItem(const char* aName) +{ + for (int i = 0; items[i]; i++) + if (strcasecmp(items[i], aName) == 0) + return (ItemKind)i; + + return itemUnknown; +} + +//*************************************************************************** +// Translations +//*************************************************************************** + +cThemeService::Translation cThemeService::alignments[] = +{ + { algLeft, "left" }, + { algCenter, "center" }, + { algRight, "right" }, + { 0, 0 } +}; + +cThemeService::Translation cThemeService::scrollmodes[] = +{ + { smOff, "off" }, + { smMarquee, "marquee" }, + { smTicker, "ticker" }, + { 0, 0 } +}; + +int cThemeService::toDenum(Translation t[], const char* value) +{ + for (int i = 0; t[i].name; i++) + { + if (strcasecmp(t[i].name, value) == 0) + return t[i].denum; + } + + return na; +} + +//*************************************************************************** +// Class cThemeItem +//*************************************************************************** + +cThemeSection* cThemeItem::currentSection = 0; +string cThemeItem::lineBuffer = ""; +string cThemeItem::condition = ""; + +cThemeItem::cThemeItem() +{ + _item = itemUnknown; + + _x = "-1"; + _y = "-1"; + _width = "0"; + _height = "0"; + + _bg_x = na; + _bg_y = na; + _image_map = 0; + _stat_pic = 0; + _stat_text=0; + _stat_width = 0; + _stat_height = 0; + _stat_x = 0; + _stat_y = 0; + _bg_width = 0; + _bg_height = 0; + _overlay = no; + _lines = 0; + _start_line = "0"; + _line = 0; + _size = 32; + _switch = no; + _align = Ts::algLeft; + _align_v = 0; + _delay=0; + _foreground = no; + _count=0; + _spacing = 0; + _yspacing = 5; + _bar_height = 100; + _bar_height_unit = iuPercent; + _scroll = smOff; + _scroll_count = 0; + _dots = no; + _permanent = no; + _factor = 1; + _aspect_ratio = no; + _rotate = 0; // { 0 - off; 1 - auto; } + _fit = no; + _value = ""; + _total = ""; + + _menu_x = 0; + _menu_y = 0; + _menu_width = 0; + _menu_height = 0; + + _color = "255:255:255:255"; + _bg_color = "0:0:0:255"; + _unit = ""; + _reference = ""; + _font = "graphTFT"; + _type = ""; + _format = ""; + _path = ""; + _path2 = ""; + _focus = ""; + _text = ""; + _number = na; + _index = 0; + _whipe_res = 20; + _onClick = ""; + _onDblClick = ""; + _onUp = ""; + _onDown = ""; + _sectionInclude = ""; + _condition = ""; + _debug = "-"; + _id = ""; + _area = ""; + + section = 0; + pathCount = 0; +} + +cThemeItem::~cThemeItem() +{ +} + +//*************************************************************************** +// Parse one line => one Item +//*************************************************************************** + +bool cThemeItem::Parse(const char* s) +{ + cDisplayItem* dspItem; + string tmp; + string::size_type posA; + string::size_type posB; + int value; + string toParse; + + // skip whitespace + + while (*s && (*s == ' ' || *s == '\t')) + s++; + + // skip empty lines + + if (Str::isEmpty(s)) + return true; + + // append + + lineBuffer.append(s); + + // skip line and inline comments + + if ((posA = lineBuffer.find("//")) != string::npos) + { + lineBuffer.erase(posA); + + if (Str::isBlank(lineBuffer.c_str())) + return true; + } + + // parse directives + + if ((posA = lineBuffer.find("#")) == 0) + { + parseDirectives(lineBuffer); + lineBuffer.clear(); + return true; + } + + if (Thms::theTheme->isSkipContent()) + { + lineBuffer.clear(); + tell(2, "Skipping line due to directive [%.50s%s]", + s, strlen(s) > 50 ? ".." : ""); + return true; + } + + // parse variables + + if ((posA = lineBuffer.find("var ")) == 0) + { + string tmp = lineBuffer.substr(posA+4); + parseVariable(tmp, currentSection); + lineBuffer.clear(); + + return true; + } + + // parse condition + + if ((posA = lineBuffer.find("if ")) == 0) + { + condition = lineBuffer.substr(posA+3); + lineBuffer.clear(); + + return true; + } + else if (lineBuffer.find("endif") == 0) + { + condition = ""; + lineBuffer.clear(); + return true; + } + + // check if it is a section start + + posA = lineBuffer.find("["); + posB = lineBuffer.find("]", posA); + + if (posA == 0 && posB > 0 && posB != string::npos) + { + // section ... + + tmp = lineBuffer.substr(posA+1, posB-1); + + // append section + + currentSection = new cThemeSection(tmp); + Thms::theTheme->getSections()->Add(currentSection); + Thms::theTheme->addNormalSection(currentSection->getName()); + lineBuffer.clear(); + + return true; + } + + // line completed ? + + if (lineBuffer.find(";") == string::npos) + { + tell(5, "line buffer now [%s]", lineBuffer.c_str()); + return true; + } + + // ---------------------------- + // + + tell(4, "line completed, processing [%s]", toParse.c_str()); + toParse = lineBuffer; + lineBuffer.clear(); + + if (!currentSection) + return true; + + // include section ... + + if (ParseVar(toParse, "Include", &tmp) == success) + { + if ((dspItem = newDisplayItem(itemSectionInclude))) + { + currentSection->Add(dspItem); + dspItem->_sectionInclude = tmp; + } + + return true; + } + + // item ... + + if ((posA = toParse.find(" ")) == string::npos) + { + tell(0, "Ignoring invalid theme line [%.50s%s]", + s, strlen(s) > 50 ? ".." : ""); + + return true; + } + + // parse item attributes .. + + _item = toItem(toParse.substr(0, posA).c_str()); + + switch (_item) + { + case itemVarFile: + { + if ((dspItem = newDisplayItem(itemVarFile))) + { + dspItem->ParseText(toParse); + dspItem->_item = _item; + dspItem->setSection(currentSection); + currentSection->addVarFile(dspItem); + tell(2, "added variable file '%s', namespace '%s'", + dspItem->Path2().c_str(), dspItem->Path().c_str()); + } + + return true; + } + + case itemTheme: + { + ParseVarExt(toParse, "name", &tmp); + Thms::theTheme->setName(tmp); + ParseVarExt(toParse, "themeVersion", &tmp); + Thms::theTheme->setThemeVersion(tmp); + ParseVarExt(toParse, "syntaxVersion", &tmp); + Thms::theTheme->setSyntaxVersion(tmp); + ParseVarExt(toParse, "dir", &tmp); + Thms::theTheme->setDir(tmp); + ParseVarExt(toParse, "startImage", &tmp); + Thms::theTheme->setStartImage(tmp); + ParseVarExt(toParse, "endImage", &tmp); + Thms::theTheme->setEndImage(tmp); + + if (ParseVarExt(toParse, "width", &value) == success) + Thms::theTheme->setWidth(value); + if (ParseVarExt(toParse, "height", &value) == success) + Thms::theTheme->setHeight(value); + if (ParseVarExt(toParse, "fontPath", &tmp) == success) + Thms::theTheme->setFontPath(tmp); + + return true; + } + + case itemDefaults: + { + currentSection->setDefaultsItem(newDisplayItem(_item)); + currentSection->getDefaultsItem()->ParseText(toParse); + + return true; + } + + case itemUnknown: + { + tell(0, "Warning: Ignoring unknown theme item = [%s]", + toParse.substr(0, posA).c_str()); + + return true; + } + } + + dspItem = newDisplayItem(_item); + + if (dspItem) + { + if (_item == itemMenuImageMap) + Thms::theTheme->AddMapItem(dspItem); + else + currentSection->Add(dspItem); + + // copy default values + + if (currentSection->getDefaultsItem()) + *dspItem = *currentSection->getDefaultsItem(); // copy constructor + else + tell(2, "Info: No defaults for section '%s' defined", + currentSection->getName().c_str()); + + // set/parse item properties + + dspItem->_item = _item; + dspItem->setSection(currentSection); + dspItem->ParseText(toParse); + + if (condition.size()) + { + if (dspItem->_condition.size()) + dspItem->_condition += " && "; + + dspItem->_condition += condition; + + tell(3, "Attach condition '%s' to '%s', condition now '%s'", + condition.c_str(), dspItem->nameOf(), dspItem->_condition.c_str()); + } + } + + return true; +} + +//*************************************************************************** +// Parse Variable +//*************************************************************************** + +int cThemeItem::parseVariable(string& toParse, cThemeSection* section) +{ + int menu = no; + string name, value; + Scan scan(toParse.c_str()); + + scan.eat(); + + if (!scan.isIdent()) + { + tell(0, "Error: Invalid left value '%s' in '%s'", + scan.lastIdent(), toParse.c_str()); + return fail; + } + + // parse optional 'menu' key word + + if (strcmp(scan.lastIdent(), "menu") == 0) + { + menu = yes; + scan.eat(); + + if (!scan.isIdent()) + { + tell(0, "Error: Invalid left value '%s' in '%s'", + scan.lastIdent(), toParse.c_str()); + return fail; + } + } + + // name + + name = scan.lastIdent(); + + scan.eat(); + + // assignment + + if (!scan.isOperator() || scan.lastIdent()[0] != '=') + { + tell(0, "Error: Invalid operator '%s' in '%s', '=' expected", + scan.lastIdent(), toParse.c_str()); + return fail; + } + + scan.eat(); + + if (!scan.isString() && scan.isIdent()) + { + tell(0, "Error: Invalid right value '%s' in '%s'", + scan.lastIdent(), toParse.c_str()); + return fail; + } + +// evaluate(value, scan.lastIdent()); // #jw value = scan.lastIdent(); + value = scan.lastIdent(); + + if (section) + { + tell(3, "adding section variable '%s' with '%s' to '%s'", + name.c_str(), value.c_str(), section->getName().c_str()); + section->variables[name] = value; + } + else + { + tell(3, "adding theme variable '%s' with '%s' %s", + name.c_str(), value.c_str(), menu ? "mode menu" : ""); + + if (menu) + Thms::theTheme->menuVariables[name] = value; + else + Thms::theTheme->variables[name] = value; + } + + return success; +} + +//*************************************************************************** +// Parse Directive +//*************************************************************************** + +int cThemeItem::parseDirectives(string& toParse) +{ + string exeption; + + if (ParseDirective(toParse, "#define", &exeption) == success + && exeption.length()) + { + tell(1, "Adding define '%s'", exeption.c_str()); + Thms::theTheme->defines[Thms::theTheme->defineCount] = exeption; + Thms::theTheme->defineCount++; + + return success; + } + + else if (ParseDirective(toParse, "#endif", &exeption) == success) + { + if (Thms::theTheme->skipContent.empty()) + tell(0, "Warning: Ignoring unexpected '#endif'"); + else + Thms::theTheme->skipContent.pop(); + + tell(2, "Swiching parser '%s' ('#endif' found)", + Thms::theTheme->isSkipContent() ? "off" : "on"); + + return success; + } + + else if (ParseDirective(toParse, "#else", &exeption) == success) + { + int actualSkip = Thms::theTheme->isSkipContent(); + + if (Thms::theTheme->skipContent.empty()) + tell(0, "Warning: Ignoring unexpected '#else'"); + else + Thms::theTheme->skipContent.top() = !actualSkip; + + tell(2, "Swiching parser '%s' [from '%s'] ('#else' found)", + Thms::theTheme->isSkipContent() ? "off" : "on", + actualSkip ? "off" : "on"); + + return success; + } + + else if ((ParseDirective(toParse, "#ifdef", &exeption) == success + || ParseDirective(toParse, "#ifndef", &exeption) == success) + && exeption.length()) + { + int actualSkip = Thms::theTheme->isSkipContent(); + int negate = no; + + if (toParse.find("#ifndef") == 0) + negate = yes; + + tell(4, "Found '%s %s'", negate ? "#ifndef" : "#ifdef", exeption.c_str()); + + int skip = negate ? no : yes; + + for (int i = 0; i < Thms::theTheme->defineCount; i++) + { + if (Thms::theTheme->defines[i] == exeption) + { + tell(4, "Define '%s' is set, switching parser '%s'", + exeption.c_str(), negate ? "off" : "on"); + + skip = negate ? yes : no; + + break; + } + } + + // aus bleibt aus (bei verschachtelten ifdef) + + skip = skip || actualSkip; + + Thms::theTheme->skipContent.push(skip); + + tell(2, "Parser now '%s' due to '%s' '%s'", + Thms::theTheme->isSkipContent() ? "off" : "on", + negate ? "#ifndef" : "#ifdef", + exeption.c_str()); + + return success; + } + + return ignore; +} + +//*************************************************************************** +// +//*************************************************************************** + +cDisplayItem* cThemeItem::newDisplayItem(int item) +{ + cDisplayItem* newItem = 0; + + switch (item) + { + case itemMenuImageMap: + case itemDefaults: + case itemMenuNavigationArea: + case itemSectionInclude: newItem = new cDisplayItem(); break; + + case itemText: newItem = new cDisplayText(); break; + case itemTextList: newItem = new cDisplayTextList(); break; + case itemProgressbar: newItem = new cDisplayProgressBar(); break; + case itemRectangle: newItem = new cDisplayRectangle(); break; + case itemImage: newItem = new cDisplayImage(); break; + case itemImageFile: newItem = new cDisplayImageFile(); break; + case itemImageDir: newItem = new cDisplayImageDir(); break; + case itemCalibrationCursor: newItem = new cDisplayCalibrationCursor(); break; + case itemMessage: newItem = new cDisplayMessage(); break; + + case itemMenu: newItem = new cDisplayMenu(); break; + case itemMenuSelected: newItem = new cDisplayMenuSelected(); break; + case itemColumn: newItem = new cDisplayMenuColumn(); break; + case itemColumnSelected: newItem = new cDisplayMenuColumnSelected(); break; + case itemEventColumn: newItem = new cDisplayMenuEventColumn(); break; + case itemEventColumnSelected: newItem = new cDisplayMenuEventColumnSelected(); break; + + case itemSpectrumAnalyzer: newItem = new cDisplaySpectrumAnalyzer(); break; + case itemPartingLine: newItem = new cDisplayPartingLine(); break; + case itemSysinfo: newItem = new cDisplaySysinfo(); break; + case itemBackground: newItem = new cDisplayBackground(); break; + + case itemVolumeMuteSymbol: newItem = new cDisplayVolumeMuteSymbol(); break; + case itemVolumebar: newItem = new cDisplayVolumebar(); break; + + case itemTimebar: newItem = new cDisplayTimebar(); break; + case itemVarFile: newItem = new cVariableFile(); break; + + case itemMenuButtonRed: + case itemMenuButtonGreen: + case itemMenuButtonYellow: + case itemMenuButtonBlue: + newItem = new cDisplayMenuButton(); + break; + + case itemMenuButtonBackgroundRed: + case itemMenuButtonBackgroundGreen: + case itemMenuButtonBackgroundYellow: + case itemMenuButtonBackgroundBlue: + newItem = new cDisplayMenuButtonBackground(); + break; + }; + + if (newItem) + newItem->_item = item; + + return newItem; +} + +//*************************************************************************** +// Parse Properties +//*************************************************************************** + +int cThemeItem::ParseText(string toParse) +{ + ParseVar(toParse, "id", &_id); + ParseVar(toParse, "area", &_area); + + ParseVarColor(toParse, "color", &_color); + ParseVarColor(toParse, "bg_color", &_bg_color); + + ParseVar(toParse, "x", &_x); + ParseVar(toParse, "y", &_y); + ParseVar(toParse, "width", &_width); + ParseVar(toParse, "height", &_height); + + ParseVar(toParse, "overlay", &_overlay); + ParseVar(toParse, "lines", &_lines); + ParseVar(toParse, "start_line", &_start_line); + ParseVar(toParse, "line", &_line); + ParseVar(toParse, "font", &_font); + ParseVar(toParse, "size", &_size); + ParseVar(toParse, "text", &_text); + ParseVar(toParse, "switch", &_switch); // bool + ParseVar(toParse, "align_v", &_align_v); + ParseVar(toParse, "align", &_align, alignments); + ParseVar(toParse, "focus", &_focus); + ParseVar(toParse, "type", &_type); + ParseVar(toParse, "format", &_format); + ParseVarTime(toParse, "delay", &_delay); + ParseVar(toParse, "foreground", &_foreground); + ParseVar(toParse, "count", &_count); + ParseVar(toParse, "image_map", &_image_map); // bool + ParseVar(toParse, "stat_pic", &_stat_pic); // bool + ParseVar(toParse, "stat_text", &_stat_text); // bool + ParseVar(toParse, "stat_width", &_stat_width); + ParseVar(toParse, "stat_height", &_stat_height); + ParseVar(toParse, "stat_x", &_stat_x); + ParseVar(toParse, "stat_y", &_stat_y); + ParseVar(toParse, "bg_x", &_bg_x); + ParseVar(toParse, "bg_y", &_bg_y); + ParseVar(toParse, "bg_width", &_bg_width); + ParseVar(toParse, "bg_height", &_bg_height); + ParseVar(toParse, "number", &_number); + ParseVar(toParse, "spacing", &_spacing); + ParseVar(toParse, "yspacing", &_yspacing); + ParseVar(toParse, "scroll", &_scroll, scrollmodes); + ParseVar(toParse, "factor", &_factor); + ParseVar(toParse, "unit", &_unit); + ParseVar(toParse, "reference", &_reference); + ParseVar(toParse, "scroll_count", &_scroll_count); + ParseVar(toParse, "dots", &_dots); + ParseVar(toParse, "permanent", &_permanent); + ParseVar(toParse, "value", &_value); + ParseVar(toParse, "total", &_total); + + ParseVar(toParse, "menu_x", &_menu_x); + ParseVar(toParse, "menu_y", &_menu_y); + ParseVar(toParse, "menu_width", &_menu_width); + ParseVar(toParse, "menu_height", &_menu_height); + + ParseVar(toParse, "fit", &_fit); + ParseVar(toParse, "aspect_ratio", &_aspect_ratio); + ParseVar(toParse, "rotate", &_rotate); + + ParseVar(toParse, "whipe_res", &_whipe_res); + ParseVar(toParse, "on_click", &_onClick); + ParseVar(toParse, "on_dblclick", &_onDblClick); + ParseVar(toParse, "on_up", &_onUp); + ParseVar(toParse, "on_down", &_onDown); + + ParseVar(toParse, "name", &_path); + ParseVar(toParse, "file", &_path2); + ParseVar(toParse, "path2", &_path2); + ParseVar(toParse, "pathON", &_path); + ParseVar(toParse, "pathOFF", &_path2); + + ParseVar(toParse, "condition", &_condition); + + ParseVar(toParse, "debug", &_debug); + + // some properties with special handling + + string tmp; + + if (ParseVar(toParse,"bar_height", &tmp) == success) + { + _bar_height = atoi(tmp.c_str()); + + if (strchr(tmp.c_str(), '%')) + _bar_height_unit = iuPercent; + else + _bar_height_unit = iuAbsolute; + } + + if (ParseVar(toParse, "path", &_path) == success) + { + string::size_type e, s; + e = s = 0; + + do + { + e = _path.find(':', s); + + pathList[pathCount].configured = _path.substr(s, e == string::npos ? _path.length()-s : e-s); + pathList[pathCount].curNum = na; + pathList[pathCount].last = ""; + pathCount++; + + s = e+1; + + } while (s > 0 && pathCount < maxPathCount); + } + + _text = replaceChar(_text, '@', '\n'); + + return success; +} + +//*************************************************************************** +// Search via translation list +//*************************************************************************** + +int cThemeItem::ParseVar(string toParse, string name, int* value, Translation* t) +{ + string val; + int status; + int denum; + + if ((status = ParseVar(toParse, name, &val)) == success) + { + denum = cThemeService::toDenum(t, val.c_str()); + + if (denum == na) + { + tell(0, "Error: Unexpected value '%s' for '%s'", + val.c_str(), name.c_str()); + + denum = 0; // 0 -> always the default + } + + *value = denum; + } + + return status; +} + +//*************************************************************************** +// Search the int parameter +//*************************************************************************** + +int cThemeItem::ParseVar(string toParse, string name, int* value) +{ + string val; + int status; + + if ((status = ParseVar(toParse, name, &val)) == success) + { + if (val == "yes" || val == "true") + *value = 1; + else if (val == "no" || val == "false") + *value = 0; + else + *value = atoi(val.c_str()); + } + + return status; +} + +//*************************************************************************** +// Search the time parameter +//*************************************************************************** + +int cThemeItem::ParseVarTime(string toParse, string name, uint64_t* value) +{ + string val; + int status; + + if ((status = ParseVar(toParse, name, &val)) == success) + { + if (val.find("ms") != string::npos) + *value = atoi(val.c_str()); + else + *value = atoi(val.c_str()) * 1000; + } + + return status; +} + +//*************************************************************************** +// Lookup Variable +//*************************************************************************** + +int cThemeItem::lookupVariable(const char* name, string& value, const char* fmt) +{ + int status = fail; + + tell(4, "lookup variable '%s' in '%s'", name, + section ? section->getName().c_str() : "theme"); + + if (section) + status = section->lookupVar(name, value); + + if (status != success) + status = Thms::theTheme->lookupVar(name, value); + + if (status == success) + tell(4, "Found variable '%s' with value '%s' in '%s'", + name, value.c_str(), section ? section->getName().c_str() : "theme"); + + return status; +} + +//*************************************************************************** +// Set Variable +//*************************************************************************** + +int cThemeItem::setVariable(const char* name, int value) +{ + char v[50]; + string tmp; + + sprintf(v, "%d", value); + + if (section && section->lookupVar(name, tmp) == success) + { + section->variables[name] = v; + return success; + } + else if (Thms::theTheme->lookupVar(name, tmp) == success) + { + Thms::theTheme->variables[name] = v; + return success; + } + + return fail; +} + +//*************************************************************************** +// Pase Variables +//*************************************************************************** + +int cThemeItem::ParseVarExt(string toParse, string name, int* value) +{ + string v, p; + + if (ParseVar(toParse, name, &v) != success) + return fail; + + if (evaluate(p, v.c_str()) != success) + return fail; + + *value = atoi(p.c_str()); + + return success; +} + +int cThemeItem::ParseVarExt(string toParse, string name, string* value) +{ + string v; + + if (ParseVar(toParse, name, &v) != success) + return fail; + + if (evaluate(*value, v.c_str()) != success) + return fail; + + tell(4, "found var '%s' with value '%s'", + name.c_str(), value->c_str()); + + return success; +} + +int cThemeItem::ParseVarColor(string toParse, string name, string* value) +{ + char* temp; + int red, green, blue, alpha = na; + + int bg = strncmp(name.c_str(), "bg_", 3) == 0; // background color expected? + + // parse alpha channel + + ParseVar(toParse, bg ? "bg_transparent" : "transparent", &alpha); + ParseVar(toParse, bg ? "bg_alpha" : "alpha", &alpha); + + // parse new style color parameter + + if (ParseVar(toParse, name, value) != success) + { + // parse old style color parameter + + if (ParseVar(toParse, bg ? "bg_red" : "red", &red) + + ParseVar(toParse, bg ? "bg_green" : "green", &green) + + ParseVar(toParse, bg ? "bg_blue" : "blue", &blue) == success) + { + asprintf(&temp, "%d:%d:%d:%d", red, green, blue, alpha); + *value = temp; + free(temp); + + return success; + } + } + + // it's allowed to configure separate alpha value + + if (alpha != na) + { + t_rgba rgba; + + str2rgba(value->c_str(), rgba); + asprintf(&temp, "%d:%d:%d:%d", rgba[rgbR], rgba[rgbG], rgba[rgbB], alpha); + *value = temp; + free(temp); + } + + return success; +} + +//*************************************************************************** +// Search the string parameter +//*************************************************************************** + +int cThemeItem::ParseVar(string toParse, string name, string* value) +{ + string::size_type posA, posB, end = 0; + + name += "="; + + if ((posA = toParse.find("," + name)) == string::npos) + if ((posA = toParse.find(" " + name)) == string::npos) + if ((posA = toParse.find(name)) != 0) + return fail; + + if (posA) + posA++; + + posB = posA; + + while (posB < toParse.length()) + { + if ((end = toParse.find(",", posB)) == string::npos) + if ((end = toParse.find(";", posB)) == string::npos) + return fail; + + // if at first pos or without mask sign, + // then we have found the end of the item + // -> break the loop + + if (end == 0) // wenn "," -> fertig + break; + + if (toParse[end-1] != '\\') // wenn "x," -> fertig + break; + + if (end > 1 && toParse[end-2] == '\\') // wenn "\\," -> fertig + break; + + // => "\," -> nicht fertig + // => search again behind the ',' or ';' sign + + posB = end+1; + } + + *value = toParse.substr(posA + name.size(), end-posA-name.size()); + + // de mask '\' sign + + if ((value->find("\\", 0)) != string::npos) + { + char* buf; char* s; char* d; + + asprintf(&buf, "%s", value->c_str()); + + s = d = buf; + + while (*s) + { + if (*s != '\\' || *(s+1) == '\\') + *d++ = *s; + + s++; + } + + *d = 0; + + tell(5, "got '%s' build '%s'", value->c_str(), buf); + + *value = buf; + free(buf); + } + + return success; +} + +//*************************************************************************** +// Parse Directive +//*************************************************************************** + +int cThemeItem::ParseDirective(string toParse, string name, string* value) +{ + string::size_type posA; + + *value = ""; + + if ((posA = toParse.find(name)) != 0) + return fail; + + if (toParse.length() > posA + name.size() + 1) + *value = toParse.substr(posA + name.size() + 1); + + return success; +} + +//*************************************************************************** +// Class cThemeSections +//*************************************************************************** + +//*************************************************************************** +// Get Section By Name +//*************************************************************************** + +cThemeSection* cThemeSections::getSection(string name) +{ + for (cThemeSection* p = First(); p; p = Next(p)) + if (p->getName() == name) + return p; + + return 0; +} + +//*************************************************************************** +// Class cThemeSection +//*************************************************************************** + +uint64_t cThemeSection::getNextUpdateTime() +{ + uint64_t next = msNow() + SECONDS(300); // 5 minutes + cDisplayItem* pNext = 0; + + // search next (earlyast) drawing time + + for (cDisplayItem* p = First(); p; p = Next(p)) + { + if (p->getNextDraw() && p->getNextDraw() < next) + { + uint64_t drawIn = p->getNextDraw() - msNow(); + + tell(2, "setting next for '%s' in (%ld ms) [%s/%s]", + p->nameOf(), drawIn, p->Debug().c_str(), p->Text().c_str()); + + pNext = p; + next = p->getNextDraw(); + } + } + + if (pNext) + { + int64_t updateIn = next - msNow(); + + if (updateIn < 0) + updateIn = 0; + + int64_t s = updateIn/1000; + int64_t us = updateIn%1000; + + tell(2, "schedule next, nearest item is '%s'[%s] in %ld,%03ld seconds", + pNext->nameOf(), pNext->Debug().c_str(), s, us); + } + + return next < msNow() ? msNow() : next; +} + +int cThemeSection::updateGroup(int group) +{ + tell(3, "update item group (%d)", group); + + for (cDisplayItem* p = First(); p; p = Next(p)) + { + if (p->groupOf() & group) + { + p->reset(); + p->setNextDraw(); + + // schedule all of my area also + + if (p->Area() != "") + { + for (cDisplayItem* pa = First(); pa; pa = Next(pa)) + { + if (pa->Area() == p->Area()) + { + pa->reset(); + pa->setNextDraw(); + } + } + } + } + } + + return done; +} + +int cThemeSection::reset() +{ + tell(3, "reset items"); + + for (cDisplayItem* p = First(); p; p = Next(p)) + p->reset(); + + return done; +} + +int cThemeSection::updateVariables() +{ + map<string,cVariableFile*>::iterator it; + + for (it = varFiles.begin(); it != varFiles.end(); it++) + it->second->parse(); + + return done; +} + +//*************************************************************************** +// Class cDisplayItems +//*************************************************************************** +//*************************************************************************** +// Get Item By Kind +//*************************************************************************** + +cDisplayItem* cDisplayItems::getItemByKind(Ts::ItemKind type) +{ + for (cDisplayItem* p = First(); p; p = Next(p)) + { + if (p->Item() == type) + return p; + } + + return 0; +} + +//*************************************************************************** +// Get Item By ID +// id is configured in theme file, otherwise it is na +//*************************************************************************** + +cDisplayItem* cDisplayItems::getItemById(const char* id) +{ + for (cDisplayItem* p = First(); p; p = Next(p)) + { + if (p->Id() == id) + return p; + } + + return 0; +} + +//*************************************************************************** +// Class GraphTFTTheme +//*************************************************************************** + +cGraphTFTTheme::cGraphTFTTheme() +{ + initialized = no; + + width = 720; + height = 576; + memset(normalModes, 0, sizeof(normalModes)); + normalModesCount = 0; + fontPath = ""; + variables.clear(); + menuVariables.clear(); + + cThemeItem::currentSection = 0; + cThemeItem::lineBuffer = ""; + cThemeItem::condition = ""; + + resetDefines(); +} + +//*************************************************************************** +// Init +//*************************************************************************** + +int cGraphTFTTheme::init() +{ + cThemeSection* s; + cThemeSection* sec; + cDisplayItem* newItem; + cDisplayItem* item; + cDisplayItem* p; + cDisplayItem* background; + + if (initialized) + exit(); + + // loop over sections + + for (s = FirstSection(); s; s = NextSection(s)) + { + // evaluate includes ... + + // loop over sections items + + tell(4, "Section: '%s'", s->getName().c_str()); + + for (p = s->First(); p; p = s->Next(p)) + { + // add items of 'included' sections + + tell(4, "Item: '%s'", Ts::toName((cThemeService::ItemKind)p->Item())); + + if (p->Item() == itemSectionInclude && (sec = getSection(p->SectionInclude()))) + { + tell(4, "Icluding section: '%s'", sec->getName().c_str()); + + map<string,cVariableFile*>::iterator it; + + for (it = sec->getVarFiles()->begin(); it != sec->getVarFiles()->end(); it++) + { + if (!s->hasVarFile(it->second->getName())) + s->addVarFile(it->second); + } + + // include items + + for (item = sec->First(); item; item = sec->Next(item)) + { + tell(4, "Including Item: '%s'", Ts::toName((cThemeService::ItemKind)item->Item())); + + if (item->Item() >= itemBegin + && (newItem = cThemeItem::newDisplayItem(item->Item()))) + { + *newItem = *item; // copy constructor + s->Ins(newItem, p); // s->First()); + } + } + + // include variables + + map<string,string>::iterator iter; + + for (iter = sec->variables.begin(); iter != sec->variables.end(); ++iter) + s->variables[iter->first] = iter->second; + } + } + + // assign background item ... + + background = 0; + + // loop over sections items + + for (p = s->First(); p; p = s->Next(p)) + { + // detect/assign background + // only working properly if background is the first + // item in the theme section! + + if (p->Item() == itemBackground) + { + if (background) + tell(1, "Warning: in theme section '%s' cascading " + "background items detected", s->getName().c_str()); + + background = p; + } + else + p->setBackgroundItem(background); + } + } + + int ex = 0, exs = 0; + int ey = 0; + + // init image map + // init column positions + // init ... + + // loop over all sections + + for (s = FirstSection(); s; s = NextSection(s)) + { + int colNumber = 0; + + // loop over items of this section + + ex = exs = 0; + ey = 0; + + for (p = s->First(); p; p = s->Next(p)) + { + cDisplayItem* dspItem = p; + + if (!dspItem) + continue; + + // calculate y position of the next text item + + if (dspItem->Item() == itemText) + { + if (dspItem->Y() == na) + dspItem->setY(ey); + else + ey = dspItem->Y(); + + if (dspItem->Height()) + ey += dspItem->Height() + dspItem->YSpacing(); + } + + // calculate x position of the column items + + if (dspItem->Item() == itemEventColumn) + // || dspItem->Item() == itemColumn) + { + if (dspItem->Number() == na) + dspItem->Number(colNumber); + + if (dspItem->X() == na) + dspItem->setX(ex); // not configured, take calculated + else if (dspItem->X() < na && ex + dspItem->X() > 0) + dspItem->setX(ex + dspItem->X()); // go back + else + ex = dspItem->X(); // take configured value + + if (dspItem->Width()) + ex += dspItem->Width() + dspItem->Spacing(); // calc next x + else + dspItem->setWidth(Thms::theTheme->getWidth() - dspItem->X()); + } + + if (dspItem->Item() == itemEventColumnSelected) + // || dspItem->Item() == itemColumnSelected) + { + if (dspItem->Number() == na) + dspItem->Number(colNumber); + + if (dspItem->X() == na) + dspItem->setX(exs); // not configured, take calculated + else if (dspItem->X() < na && exs + dspItem->X() > 0) + dspItem->setX(exs + dspItem->X()); // go back + else + exs = dspItem->X(); // take configured value + + if (dspItem->Width()) + exs += dspItem->Width() + dspItem->Spacing(); // calc next x + else + dspItem->setWidth(Thms::theTheme->getWidth() - dspItem->X()); + } + + if (dspItem->Item() == itemEventColumn) + { + // || dspItem->Item() == itemColumn) + colNumber++; + } + + dspItem->init(); + } + } + + // at leat reset all items + + for (s = FirstSection(); s; s = NextSection(s)) + s->reset(); + + initialized = yes; + + return success; +} + +int cGraphTFTTheme::activate(int fdInotify) +{ + cThemeSection* s; + cDisplayItem* p; + + inotifies.clear(); + + if (fdInotify != na) + { + // loop over all sections + + for (s = FirstSection(); s; s = NextSection(s)) + { + // loop over items of this section + + for (p = s->First(); p; p = s->Next(p)) + { + // add inotify watch + + if (p->Item() == itemImageFile) + { + if (p->Path().length()) + { + int wd = inotify_add_watch(fdInotify, p->Path().c_str(), IN_CREATE | IN_MODIFY); + + tell(0, "Adding inotify watch for '%s'", p->Path().c_str()); + + if ((wd = -1)) + { + tell(0, "Adding inotify watch for '%s' failed, %m", p->Path().c_str()); + continue; + } + + inotifies[wd] = p; + } + } + } + } + } + + return success; +} + +int cGraphTFTTheme::deactivate(int fdInotify) +{ + map<int,cDisplayItem*>::iterator iter; + + for (iter = inotifies.begin(); iter != inotifies.end(); iter++) + inotify_rm_watch(fdInotify, iter->first); + + inotifies.clear(); + + return success; +} + +int cGraphTFTTheme::checkViewMode() +{ + // check normal view + + int i = 0; + + while (normalModes[i]) + { + if ((GraphTFTSetup.storeNormalMode && (normalModes[i] == GraphTFTSetup.normalMode)) + || (!GraphTFTSetup.storeNormalMode && (normalModes[i] == GraphTFTSetup.originalNormalMode))) + break; + + i++; + } + + if (!normalModes[i]) + { + GraphTFTSetup.normalMode = "Standard"; + GraphTFTSetup.storeNormalMode = true; + GraphTFTSetup.originalNormalMode = ""; + } + + if (GraphTFTSetup.storeNormalMode) + tell(0, "normal mode now '%s'", GraphTFTSetup.normalMode.c_str()); + else + tell(0, "normal mode now '%s'", GraphTFTSetup.originalNormalMode.c_str()); + + return done; +} + +//*************************************************************************** +// Exit +//*************************************************************************** + +int cGraphTFTTheme::exit() +{ + int i = 0; + cThemeSection* s; + + if (!initialized) + return done; + + cThemeItem::currentSection = 0; + cThemeItem::lineBuffer = ""; + cThemeItem::condition = ""; + + tell(3, "destroy theme '%s'", getName().c_str()); + + for (s = FirstSection(); s; s = NextSection(s)) + s->getItems()->Clear(); + + sections.Clear(); + + while (normalModes[i] && i < 100) + free(normalModes[i++]); + + // delete of this object's is done by + // cConfig before new Load() or in destructor! + + if(Thms::theTheme) + { + Thms::theTheme->resetDefines(); + } + + initialized = no; + + return done; +} + +//*************************************************************************** +// Check +//*************************************************************************** + +int cGraphTFTTheme::check(const char* theVersion) +{ + // check if the theme fit to current version + + if (strcmp(syntaxVersion.c_str(), theVersion) != 0) + { + tell(0, "Warning: Version of themefile syntax " + "does not match, found '%s' instead of '%s'", + syntaxVersion.c_str(), theVersion); + return fail; + } + + return success; +} + +//*************************************************************************** +// Load +//*************************************************************************** + +int cGraphTFTTheme::load(const char* path) +{ + exit(); + Load(path); + + return init(); +} + +//*************************************************************************** +// Add Normal Section +//*************************************************************************** + +void cGraphTFTTheme::addNormalSection(string sectionName) +{ + if (normalModesCount >= 100) + return ; + + if (sectionName.find("Normal") != 0 || sectionName.length() <= strlen("Normal")) + return ; + + if (sectionName == "NormalRadio") + return ; + + if (sectionName == "NormalTV") + asprintf(&normalModes[normalModesCount], "%s", + "Standard"); + else + asprintf(&normalModes[normalModesCount], "%s", + sectionName.substr(strlen("Normal")).c_str()); + + normalModesCount++; +} + +//*************************************************************************** +// Is Normal Mode Section +//*************************************************************************** + +const char* cGraphTFTTheme::getNormalMode(const char* modeName) +{ + for (int i = 0; i < normalModesCount; i++) + { + if (strcasecmp(normalModes[i], modeName) == 0) + return normalModes[i]; + } + + return 0; +} + +const char* cGraphTFTTheme::nextNormalMode(const char* modeName) +{ + int i; + + for (i = 0; i < normalModesCount; i++) + { + if (strcasecmp(normalModes[i], modeName) == 0) + break; + } + + if (++i < normalModesCount) + return normalModes[i]; + + return normalModes[0]; +} + +const char* cGraphTFTTheme::prevNormalMode(const char* modeName) +{ + int i; + + for (i = normalModesCount-1; i >= 0; i--) + { + if (strcasecmp(normalModes[i], modeName) == 0) + break; + } + + if (--i >= 0) + return normalModes[i]; + + return normalModes[normalModesCount-1]; +} + +//*************************************************************************** +// Get Path From ImageMap +//*************************************************************************** + +string cGraphTFTTheme::getPathFromImageMap(const char* aName) +{ + const char* name = aName; + + // skip leading blanks and numbers + + while (*name && (isdigit(*name) || *name == ' ')) + name++; + + tell(3, "checking imagemap for menu entry '%s'", name); + + for (cDisplayItem* p = mapSection.First(); p; p = mapSection.Next(p)) + { + if (p->Item() == itemMenuImageMap) + { + if (strcmp(trVDR(p->Path().c_str()), name) == 0) + { + tell(3, "MenuImageMap: Picture for '%s' is '%s'", aName, p->Path2().c_str()); + return p->Path2(); + } + } + } + + return ""; +} + +//*************************************************************************** +// Calss cGraphTFTThemes +//*************************************************************************** +//*************************************************************************** +// Get Theme +//*************************************************************************** + +cGraphTFTTheme* cGraphTFTThemes::getTheme(string aTheme) +{ + cGraphTFTTheme* t; + + tell(3, "looking for theme '%s'", aTheme.c_str()); + + for (t = First(); t; t = Next(t)) + if (t->getName() == aTheme) + return t; + + return 0; +} |