summaryrefslogtreecommitdiff
path: root/glcdskin
diff options
context:
space:
mode:
Diffstat (limited to 'glcdskin')
-rw-r--r--glcdskin/Makefile20
-rw-r--r--glcdskin/cache.c70
-rw-r--r--glcdskin/cache.h16
-rw-r--r--glcdskin/config.h4
-rw-r--r--glcdskin/display.c15
-rw-r--r--glcdskin/display.h2
-rw-r--r--glcdskin/font.c101
-rw-r--r--glcdskin/font.h3
-rw-r--r--glcdskin/function.c87
-rw-r--r--glcdskin/function.h7
-rw-r--r--glcdskin/object.c594
-rw-r--r--glcdskin/object.h90
-rw-r--r--glcdskin/parser.c304
-rw-r--r--glcdskin/parser.h12
-rw-r--r--glcdskin/skin.c14
-rw-r--r--glcdskin/skin.h12
-rw-r--r--glcdskin/string.c155
-rw-r--r--glcdskin/string.h4
-rw-r--r--glcdskin/type.h4
-rw-r--r--glcdskin/variable.c82
-rw-r--r--glcdskin/variable.h19
-rw-r--r--glcdskin/xml.c161
-rw-r--r--glcdskin/xml.h14
23 files changed, 1527 insertions, 263 deletions
diff --git a/glcdskin/Makefile b/glcdskin/Makefile
index 9b3b867..90fe310 100644
--- a/glcdskin/Makefile
+++ b/glcdskin/Makefile
@@ -7,8 +7,8 @@
CXXFLAGS += -fPIC
-VERMAJOR = 1
-VERMINOR = 0
+VERMAJOR = 2
+VERMINOR = 1
VERMICRO = 0
BASENAME = libglcdskin.so
@@ -19,17 +19,23 @@ OBJS = cache.o config.o display.o font.o function.o object.o parser.o skin.o str
HEADERS = cache.h config.h display.h font.h function.h object.h parser.h skin.h string.h type.h variable.h xml.h
+
+### Inner graphlcd-base dependencies
+LIBS += -L../glcdgraphics -lglcdgraphics -L../glcddrivers -lglcddrivers
+
+ifdef HAVE_FONTCONFIG
+ LIBS += -lfontconfig
+ DEFINES += -DHAVE_FONTCONFIG
+endif
+
### Implicit rules:
%.o: %.c
- $(CXX) $(CXXFLAGS) -I.. -c $(DEFINES) $(INCLUDES) $<
+ $(CXX) $(CXXEXTRA) $(CXXFLAGS) -I.. -c $(DEFINES) $(INCLUDES) $<
# Dependencies:
-MAKEDEP = g++ -MM -MG
-DEPFILE = .dependencies
-$(DEPFILE): Makefile
- @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+DEPFILE = $(OBJS:%.o=%.d)
-include $(DEPFILE)
diff --git a/glcdskin/cache.c b/glcdskin/cache.c
index 83629a0..f0543d9 100644
--- a/glcdskin/cache.c
+++ b/glcdskin/cache.c
@@ -12,20 +12,25 @@
#include <glcdgraphics/image.h>
#include <glcdgraphics/glcd.h>
#include <glcdgraphics/pbm.h>
+#include <glcdgraphics/extformats.h>
#include <stdlib.h>
#include <string.h>
+#include <syslog.h>
+
#include "cache.h"
#include "skin.h"
namespace GLCD
{
-cImageItem::cImageItem(const std::string & path, cImage * image)
+cImageItem::cImageItem(const std::string & path, cImage * image, uint16_t scalew, uint16_t scaleh)
: path(path),
counter(0),
- image(image)
+ image(image),
+ scale_width(scalew),
+ scale_height(scaleh)
{
}
@@ -43,25 +48,40 @@ cImageCache::cImageCache(cSkin * Parent, int Size)
cImageCache::~cImageCache()
{
+ Clear();
+}
+
+void cImageCache::Clear(void)
+{
for (unsigned int i = 0; i < images.size(); i++)
{
delete images[i];
}
images.clear();
+ failedpaths.clear();
}
-cImage * cImageCache::Get(const std::string & path)
+cImage * cImageCache::Get(const std::string & path, uint16_t & scalew, uint16_t & scaleh)
{
std::vector <cImageItem *>::iterator it;
cImageItem * item;
uint64_t maxCounter;
std::vector <cImageItem *>::iterator oldest;
+ // test if this path has already been stored as invalid path / invalid/non-existent image
+ for (size_t i = 0; i < failedpaths.size(); i++) {
+ if (failedpaths[i] == path) {
+ return NULL;
+ }
+ }
+
maxCounter = 0;
item = NULL;
for (it = images.begin(); it != images.end(); it++)
{
- if (item == NULL && path == (*it)->Path())
+ uint16_t scw = 0, sch = 0;
+ (*it)->ScalingGeometry(scw, sch);
+ if (item == NULL && path == (*it)->Path() && ( (scw == 0 && sch == 0 && ! (scalew || scaleh)) || (scw == scalew && sch == scaleh)))
{
(*it)->ResetCounter();
item = (*it);
@@ -81,21 +101,25 @@ cImage * cImageCache::Get(const std::string & path)
return item->Image();
}
- item = LoadImage(path);
+ item = LoadImage(path, scalew, scaleh);
if (item)
{
+ syslog(LOG_INFO, "INFO: graphlcd: successfully loaded image '%s'\n", path.c_str());
if (images.size() == size)
{
images.erase(oldest);
}
images.push_back(item);
return item->Image();
+ } else {
+ failedpaths.push_back(path);
}
return NULL;
}
-cImageItem * cImageCache::LoadImage(const std::string & path)
+cImageItem * cImageCache::LoadImage(const std::string & path, uint16_t scalew, uint16_t scaleh)
{
+ //fprintf(stderr, "### loading image %s\n", path.c_str());
cImageItem * item;
cImage * image;
char str[8];
@@ -138,6 +162,28 @@ cImageItem * cImageCache::LoadImage(const std::string & path)
}
file += path;
}
+
+ cImageFile* imgFile = NULL;
+
+ if (strcmp(str, "PBM") == 0) {
+ imgFile = new cPBMFile();
+ } else if (strcmp(str, "GLCD") == 0) {
+ imgFile = new cGLCDFile();
+ } else {
+ imgFile = new cExtFormatFile();
+ }
+
+ uint16_t scale_width = scalew;
+ uint16_t scale_height = scaleh;
+ // scale_width and scale_height are set to 0 if image was NOT scaled
+ if (!imgFile || (imgFile->LoadScaled(*image, file, scalew /*scale_width*/, scaleh /*scale_height*/) == false) ) {
+ delete image;
+ if (imgFile) delete imgFile;
+ return NULL;
+ }
+ delete imgFile;
+
+#if 0
if (strcmp(str, "PBM") == 0)
{
cPBMFile pbm;
@@ -160,11 +206,17 @@ cImageItem * cImageCache::LoadImage(const std::string & path)
}
else
{
- delete image;
- return NULL;
+ cExtFormatFile extformat;
+
+ if (extformat.Load(*image, file) == false)
+ {
+ delete image;
+ return NULL;
+ }
}
+#endif
- item = new cImageItem(path, image);
+ item = new cImageItem(path, image, scale_width, scale_height);
if (!item)
{
delete image;
diff --git a/glcdskin/cache.h b/glcdskin/cache.h
index 925ebcc..533f009 100644
--- a/glcdskin/cache.h
+++ b/glcdskin/cache.h
@@ -29,11 +29,13 @@ private:
std::string path;
uint64_t counter;
cImage * image;
+ uint16_t scale_width, scale_height;
public:
- cImageItem(const std::string & path, cImage * image);
+ cImageItem(const std::string & path, cImage * image, uint16_t scalew, uint16_t scaleh);
~cImageItem();
const std::string & Path() const { return path; }
+ void ScalingGeometry(uint16_t & scalew, uint16_t & scaleh) { scalew = scale_width; scaleh = scale_height; }
uint64_t Counter() const { return counter; }
cImage * Image() { return image; }
void ResetCounter() { counter = 0; }
@@ -46,13 +48,21 @@ private:
cSkin * skin;
size_t size;
std::vector <cImageItem *> images;
+ std::vector <std::string> failedpaths;
- cImageItem * LoadImage(const std::string & path);
+ cImageItem * LoadImage(const std::string & path, uint16_t scalew, uint16_t scaleh);
public:
cImageCache(cSkin * Parent, int Size);
~cImageCache();
- cImage * Get(const std::string & path);
+ cImage * Get(const std::string & path, uint16_t & scalew, uint16_t & scaleh);
+ cImage * Get(const std::string & path) {
+ uint16_t scalew = 0;
+ uint16_t scaleh = 0;
+ return Get(path, scalew, scaleh) ;
+ }
+
+ void Clear(void);
};
} // end of namespace
diff --git a/glcdskin/config.h b/glcdskin/config.h
index ce68513..eede1cc 100644
--- a/glcdskin/config.h
+++ b/glcdskin/config.h
@@ -17,12 +17,15 @@
#include <stdint.h>
+#include "../glcddrivers/driver.h"
+
namespace GLCD
{
class cType;
class cFont;
struct tSkinToken;
+class cDriver;
class cSkinConfig
{
@@ -37,6 +40,7 @@ public:
virtual int GetTokenId(const std::string & Name);
virtual int GetTabPosition(int Index, int MaxWidth, const cFont & Font);
virtual uint64_t Now(void);
+ virtual cDriver * GetDriver(void) const { return NULL; }
};
} // end of namespace
diff --git a/glcdskin/display.c b/glcdskin/display.c
index a926bd9..6f7a673 100644
--- a/glcdskin/display.c
+++ b/glcdskin/display.c
@@ -39,6 +39,21 @@ bool cSkinDisplay::NeedsUpdate(uint64_t CurrentTime)
}
+std::string cSkinDisplay::CheckAction(cGLCDEvent * ev) {
+ std::string rv = "";
+
+ if (!ev)
+ return "";
+
+ for (uint32_t i = 0; i < NumObjects(); ++i) {
+ if ( (rv = GetObject(i)->CheckAction(ev) ) != "" ) {
+ return rv;
+ }
+ }
+ return "";
+}
+
+
cSkinDisplays::cSkinDisplays(void)
{
}
diff --git a/glcdskin/display.h b/glcdskin/display.h
index 3b19b0c..ec0ee8b 100644
--- a/glcdskin/display.h
+++ b/glcdskin/display.h
@@ -45,6 +45,8 @@ public:
void Render(cBitmap * screen);
bool NeedsUpdate(uint64_t CurrentTime);
+
+ std::string CheckAction(cGLCDEvent * ev);
};
class cSkinDisplays: public std::vector<cSkinDisplay *>
diff --git a/glcdskin/font.c b/glcdskin/font.c
index 731b1fa..a8c22c8 100644
--- a/glcdskin/font.c
+++ b/glcdskin/font.c
@@ -6,9 +6,16 @@
#include "skin.h"
#include "function.h"
+#ifdef HAVE_FONTCONFIG
+ #include <fontconfig/fontconfig.h>
+#endif
+
namespace GLCD
{
+int cSkinFont::FcInitCount = 0;
+
+
cSkinFont::cSkinFont(cSkin * Parent)
: mSkin(Parent),
mCondition(NULL),
@@ -17,36 +24,114 @@ cSkinFont::cSkinFont(cSkin * Parent)
{
}
+cSkinFont::~cSkinFont(void) {
+#ifdef HAVE_FONTCONFIG
+ cSkinFont::FcInitCount --;
+ if (cSkinFont::FcInitCount <= 0) {
+ FcFini();
+ }
+#endif
+}
+
bool cSkinFont::ParseUrl(const std::string & url)
{
- std::string::size_type count = std::string::npos;
+ bool isFontconfig = false;
+ std::string rawfont = "";
if (url.find("fnt:") == 0)
{
mType = ftFNT;
+ rawfont = url.substr(4);
mSize = 0;
}
else if (url.find("ft2:") == 0)
{
mType = ftFT2;
- std::string::size_type pos = url.find(":", 4);
+ rawfont = url.substr(4);
+ std::string::size_type pos = rawfont.find(":");
if (pos == std::string::npos)
{
syslog(LOG_ERR, "cFontElement::Load(): No font size specified in %s\n", url.c_str());
return false;
}
- std::string tmp = url.substr(pos + 1);
+ std::string tmp = rawfont.substr(pos + 1);
mSize = atoi(tmp.c_str());
- count = pos - 4;
+ rawfont = rawfont.substr(0,pos);
}
+#ifdef HAVE_FONTCONFIG
+ else if (url.find("fc:") == 0)
+ {
+ mType = ftFT2;
+ isFontconfig = true;
+ rawfont = url.substr(3);
+
+ std::string::size_type pos = rawfont.find(":size=");
+ if (pos == std::string::npos) {
+ syslog(LOG_ERR, "cFontElement::Load(): No font size specified in %s (e.g.: 'size=<fontsize>')\n", url.c_str());
+ return false;
+ }
+ std::string sizeterm = rawfont.substr(pos + 1);
+
+ pos = sizeterm.find(":"); // find terminating :
+ if (pos != std::string::npos) {
+ sizeterm = sizeterm.substr(0, pos);
+ }
+
+ pos = sizeterm.find("=");
+ if (pos == std::string::npos) {
+ syslog(LOG_ERR, "cFontElement::Load(): Invalid size term '%s' (must be 'size=<fontsize>')\n", url.c_str());
+ return false;
+ }
+
+ std::string tmp = sizeterm.substr(pos + 1);
+ mSize = atoi(tmp.c_str());
+ if (mSize <= 0) {
+ syslog(LOG_ERR, "cFontElement::Load(): Invalid font size in '%s'\n", url.c_str());
+ return false;
+ }
+
+ if (cSkinFont::FcInitCount <= 0) {
+ FcInit();
+ }
+ cSkinFont::FcInitCount ++;
+
+ FcPattern *pat = FcNameParse((FcChar8 *) rawfont.c_str() );
+ rawfont = "";
+ FcPatternAddBool(pat, FC_SCALABLE, FcTrue);
+ FcConfigSubstitute(NULL, pat, FcMatchPattern);
+ FcDefaultSubstitute(pat);
+ FcResult result;
+ FcFontSet *fontset = FcFontSort(NULL, pat, FcFalse, NULL, &result);
+ if (fontset) {
+ FcBool scalable;
+ for (int i = 0; i < fontset->nfont; i++) {
+ FcPatternGetBool(fontset->fonts[i], FC_SCALABLE, 0, &scalable);
+ if (scalable) {
+ FcChar8 *s = NULL;
+ FcPatternGetString(fontset->fonts[i], FC_FILE, 0, &s);
+ rawfont = (char *)s; // set font path
+ break;
+ }
+ }
+ FcFontSetDestroy(fontset);
+ }
+ FcPatternDestroy(pat);
+
+ if (rawfont == "") {
+ syslog(LOG_ERR, "cFontElement::Load(): No usable font found for '%s'\n", url.c_str());
+ return false;
+ }
+ }
+#endif
else
{
syslog(LOG_ERR, "cSkinFont::ParseUrl(): Unknown font type in %s\n", url.c_str());
return false;
}
- if (url[4] == '/' || url.find("./") == 4 || url.find("../") == 4)
- mFile = url.substr(4, count);
+ if (isFontconfig || (rawfont[0] == '/' || rawfont.find("./") == 0 || rawfont.find("../") == 0)) {
+ mFile = rawfont;
+ }
else
{
// first try skin's font dir
@@ -57,7 +142,7 @@ bool cSkinFont::ParseUrl(const std::string & url)
mFile += '/';
}
mFile += "fonts/";
- mFile += url.substr(4, count);
+ mFile += rawfont;
#if (__GNUC__ < 3)
std::ifstream f(mFile.c_str(), std::ios::in | std::ios::binary);
#else
@@ -76,7 +161,7 @@ bool cSkinFont::ParseUrl(const std::string & url)
if (mFile[mFile.length() - 1] != '/')
mFile += '/';
}
- mFile += url.substr(4, count);
+ mFile += rawfont;
}
}
diff --git a/glcdskin/font.h b/glcdskin/font.h
index d11fa50..80e5556 100644
--- a/glcdskin/font.h
+++ b/glcdskin/font.h
@@ -49,8 +49,11 @@ private:
cSkinDisplay mDummyDisplay;
cSkinObject mDummyObject;
+ static int FcInitCount;
+
public:
cSkinFont(cSkin * Parent);
+ ~cSkinFont(void);
bool ParseUrl(const std::string & Text);
bool ParseCondition(const std::string & Text);
diff --git a/glcdskin/function.c b/glcdskin/function.c
index 8582ad5..a2d58ca 100644
--- a/glcdskin/function.c
+++ b/glcdskin/function.c
@@ -22,7 +22,7 @@ namespace GLCD
static const char * Internals[] =
{
- "not", "and", "or", "equal", "gt", "lt", "ge", "le", "ne", "file", "trans",
+ "not", "and", "or", "equal", "eq", "gt", "lt", "ge", "le", "ne", "file", "trans",
"add", "sub", "mul", "div",
"FontTotalWidth",
"FontTotalHeight",
@@ -33,6 +33,7 @@ static const char * Internals[] =
"FontTextHeight",
"ImageWidth",
"ImageHeight",
+ "QueryFeature",
NULL
};
@@ -74,7 +75,7 @@ cSkinFunction::~cSkinFunction()
delete mParams[i];
}
-bool cSkinFunction::Parse(const std::string & Text)
+bool cSkinFunction::Parse(const std::string & Text, bool reparse)
{
const char *text = Text.c_str();
const char *ptr = text, *last = text;
@@ -89,7 +90,8 @@ bool cSkinFunction::Parse(const std::string & Text)
|| (*ptr == '\'' && *(ptr + strlen(ptr) - 1) != '\'')
|| (*ptr == '{' && *(ptr + strlen(ptr) - 1) != '}'))
{
- syslog(LOG_ERR, "ERROR: Unmatched string end\n");
+ if (!reparse) // only log this error when not reparsing
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Unmatched string end\n");
return false;
}
@@ -113,7 +115,8 @@ bool cSkinFunction::Parse(const std::string & Text)
// must be a variable id
if (strlen(ptr) < 2)
{
- syslog(LOG_ERR, "ERROR: No variable id given\n");
+ if (!reparse) // only log this error when not reparsing
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/function: No variable id given\n");
return false;
}
@@ -127,7 +130,8 @@ bool cSkinFunction::Parse(const std::string & Text)
int num = strtol(ptr, &end, 10);
if (end == ptr || *end != '\0')
{
- syslog(LOG_ERR, "ERROR: Invalid numeric value\n");
+ // don't log this because when parsing a string starting with a digit (eg: 0%) this may result in a load of false positives
+ //syslog(LOG_ERR, "ERROR: Invalid numeric value (%s)\n", Text.c_str());
return false;
}
@@ -136,9 +140,17 @@ bool cSkinFunction::Parse(const std::string & Text)
}
else
{
+ bool inToken = false;
+
// expression
for (; *ptr; ++ptr)
{
+
+ if (*ptr == '{')
+ inToken = true;
+ else if (inToken && *ptr == '}')
+ inToken = false;
+
if (*ptr == '(')
{
if (inExpr++ == 0)
@@ -156,17 +168,19 @@ bool cSkinFunction::Parse(const std::string & Text)
if (Internals[i] == NULL)
{
- syslog(LOG_ERR, "ERROR: Unknown function %.*s", (int)(ptr - last), last);
+ if (!reparse) // only log this error when not reparsing
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Unknown function %.*s", (int)(ptr - last), last);
return false;
}
last = ptr + 1;
}
}
- else if (*ptr == ',' || *ptr == ')')
+ else if ( ( (!inToken) && (*ptr == ',') ) || *ptr == ')')
{
if (inExpr == 0)
{
- syslog(LOG_ERR, "ERROR: Unmatched '%c' in expression", *ptr);
+ if (!reparse) // only log this error when not reparsing
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Unmatched '%c' in expression (%s)", *ptr, Text.c_str());
return false;
}
@@ -181,8 +195,9 @@ bool cSkinFunction::Parse(const std::string & Text)
if (mNumParams == MAXPARAMETERS)
{
- syslog(LOG_ERR, "ERROR: Too many parameters to function, maximum is %d",
- MAXPARAMETERS);
+ if (!reparse) // only log this error when not reparsing
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Too many parameters to function, maximum is %d",
+ MAXPARAMETERS);
return false;
}
@@ -204,6 +219,7 @@ bool cSkinFunction::Parse(const std::string & Text)
params = -1;
break;
+ case fun_equal:
case fun_eq:
case fun_ne:
case fun_gt:
@@ -247,13 +263,17 @@ bool cSkinFunction::Parse(const std::string & Text)
params = 1;
break;
+ case funQueryFeature:
+ params = 1;
+ break;
+
default:
break;
}
if (params != -1 && mNumParams != (uint32_t) params)
{
- syslog(LOG_ERR, "ERROR: Text2Skin: Wrong number of parameters to %s, "
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Wrong number of parameters to %s, "
"expecting %d", Internals[mType - INTERNAL], params);
return false;
}
@@ -266,7 +286,9 @@ bool cSkinFunction::Parse(const std::string & Text)
if (inExpr > 0)
{
- syslog(LOG_ERR, "ERROR: Expecting ')' in expression");
+ // only log this error when not reparsing
+ if (!reparse)
+ syslog(LOG_ERR, "ERROR: Expecting ')' in expression");
return false;
}
}
@@ -345,8 +367,21 @@ cType cSkinFunction::Evaluate(void) const
case variable:
{
cSkinVariable * variable = mSkin->GetVariable(mVariableId);
- if (variable)
- return variable->Value();
+ if (variable) {
+ cType rv = variable->Value();
+ if (rv.IsString()) {
+ std::string val = rv;
+ if (val.find("{") != std::string::npos || val.find("#") != std::string::npos) {
+ cSkinString *result = new cSkinString(mObject, false);
+ if (result->Parse(val)) {
+ val = (std::string) result->Evaluate();
+ rv = cType(val);
+ }
+ delete result;
+ }
+ }
+ return rv;
+ }
return false;
}
@@ -369,23 +404,24 @@ cType cSkinFunction::Evaluate(void) const
}
return false;
+ case fun_equal:
case fun_eq:
- return mParams[0]->Evaluate() == mParams[1]->Evaluate();
+ return (std::string) mParams[0]->Evaluate() == (std::string) mParams[1]->Evaluate();
case fun_ne:
- return mParams[0]->Evaluate() != mParams[1]->Evaluate();
+ return (std::string) mParams[0]->Evaluate() != (std::string) mParams[1]->Evaluate();
case fun_gt:
- return mParams[0]->Evaluate() > mParams[1]->Evaluate();
+ return (int) mParams[0]->Evaluate() > (int) mParams[1]->Evaluate();
case fun_lt:
- return mParams[0]->Evaluate() < mParams[1]->Evaluate();
+ return (int) mParams[0]->Evaluate() < (int) mParams[1]->Evaluate();
case fun_ge:
- return mParams[0]->Evaluate() >= mParams[1]->Evaluate();
+ return (int) mParams[0]->Evaluate() >= (int) mParams[1]->Evaluate();
case fun_le:
- return mParams[0]->Evaluate() <= mParams[1]->Evaluate();
+ return (int) mParams[0]->Evaluate() <= (int) mParams[1]->Evaluate();
case fun_file:
return FunFile(mParams[0]->Evaluate());
@@ -443,9 +479,18 @@ cType cSkinFunction::Evaluate(void) const
case funImageHeight:
return FunImage(mType, mParams[0]->Evaluate());
+ case funQueryFeature: {
+ int value;
+ if (mSkin->Config().GetDriver()->GetFeature((const std::string)(mParams[0]->Evaluate()), value)) {
+ return (value) ? true : false;
+ } else {
+ return false;
+ }
+ }
+
default:
//Dprintf("unknown function code\n");
- syslog(LOG_ERR, "ERROR: Unknown function code called (this shouldn't happen)");
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/function: Unknown function code called (this shouldn't happen)");
break;
}
return false;
diff --git a/glcdskin/function.h b/glcdskin/function.h
index 4b3732f..16bcfb7 100644
--- a/glcdskin/function.h
+++ b/glcdskin/function.h
@@ -47,6 +47,7 @@ public:
fun_not = INTERNAL,
fun_and,
fun_or,
+ fun_equal,
fun_eq,
fun_gt,
fun_lt,
@@ -70,7 +71,9 @@ public:
funFontTextHeight,
funImageWidth,
- funImageHeight
+ funImageHeight,
+
+ funQueryFeature
};
private:
@@ -94,7 +97,7 @@ public:
cSkinFunction(const cSkinFunction &Src);
~cSkinFunction();
- bool Parse(const std::string &Text);
+ bool Parse(const std::string &Text, bool reparse = false);
cType Evaluate(void) const;
void SetListIndex(int MaxItems, int Index);
diff --git a/glcdskin/object.c b/glcdskin/object.c
index ab00a44..796e436 100644
--- a/glcdskin/object.c
+++ b/glcdskin/object.c
@@ -4,6 +4,8 @@
#include "cache.h"
#include "function.h"
+#include <typeinfo>
+
namespace GLCD
{
@@ -19,18 +21,27 @@ static const std::string ObjectNames[] =
"text",
"scrolltext",
"scrollbar",
+ "button",
"block",
"list",
- "item"
+ "item",
+ "condblock"
};
cSkinObject::cSkinObject(cSkinDisplay * Parent)
: mDisplay(Parent),
mSkin(Parent->Skin()),
mType((eType) __COUNT_OBJECT__),
- mPos1(0, 0),
- mPos2(-1, -1),
- mColor(GLCD::clrBlack),
+ //mPos1(0, 0),
+ //mPos2(-1, -1),
+ mX1(this, false),
+ mY1(this, false),
+ mX2(this, false),
+ mY2(this, false),
+ mWidth(this, false),
+ mHeight(this, false),
+ mColor(this, cColor(cColor::Black)),
+ mBackgroundColor(this, cColor(cColor::Transparent)),
mFilled(false),
mRadius(0),
mArc(0),
@@ -41,13 +52,20 @@ cSkinObject::cSkinObject(cSkinDisplay * Parent)
mPath(this, false),
mCurrent(this, false),
mTotal(this, false),
+ mPeak(this, false),
mFont(this, false),
mText(this, false),
mCondition(NULL),
+ mEffect(tfxNone),
+ mEffectColor(this, cColor(cColor::White)),
+ mPeakGradientColor(this, cColor(cColor::ERRCOL)), // color for peak or gradient; if ERRCOL -> use mColor
+ mGradient(tgrdNone), // default: no gradient
mLastChange(0),
mChangeDelay(-1), // delay between two images frames: -1: not animated / don't care
mStoredImagePath(""),
mImageFrameId(0), // start with 1st frame
+ mOpacity(255), // default: full opacity
+ mScale(tscNone), // scale image: default: don't scale
mScrollLoopMode(-1), // scroll (text) or loop (image) mode: default (-1)
mScrollLoopReached(false), // if scroll/loop == once: already looped once?
mScrollSpeed(0), // scroll speed: default (0)
@@ -56,17 +74,29 @@ cSkinObject::cSkinObject(cSkinDisplay * Parent)
mCurrText(""), // current text (for checks if text has changed)
mAltText(""), // alternative text source for text-objects
mAltCondition(NULL), // condition when alternative sources are used
+ mAction(""), // action (e.g. touchscreen action)
+ mMultilineScrollPosition(0),
+ mMultilineRelScroll(this, false),
mObjects(NULL)
{
+ mColor.SetColor(Parent->Skin()->Config().GetDriver()->GetForegroundColor());
+ mBackgroundColor.SetColor(Parent->Skin()->Config().GetDriver()->GetBackgroundColor());
}
cSkinObject::cSkinObject(const cSkinObject & Src)
: mDisplay(Src.mDisplay),
mSkin(Src.mSkin),
mType(Src.mType),
- mPos1(Src.mPos1),
- mPos2(Src.mPos2),
+ //mPos1(Src.mPos1),
+ //mPos2(Src.mPos2),
+ mX1(Src.mX1),
+ mY1(Src.mY1),
+ mX2(Src.mX2),
+ mY2(Src.mY2),
+ mWidth(Src.mWidth),
+ mHeight(Src.mHeight),
mColor(Src.mColor),
+ mBackgroundColor(Src.mBackgroundColor),
mFilled(Src.mFilled),
mRadius(Src.mRadius),
mArc(Src.mArc),
@@ -77,13 +107,20 @@ cSkinObject::cSkinObject(const cSkinObject & Src)
mPath(Src.mPath),
mCurrent(Src.mCurrent),
mTotal(Src.mTotal),
+ mPeak(Src.mPeak),
mFont(Src.mFont),
mText(Src.mText),
mCondition(Src.mCondition),
+ mEffect(Src.mEffect),
+ mEffectColor(Src.mEffectColor),
+ mPeakGradientColor(Src.mPeakGradientColor),
+ mGradient(Src.mGradient),
mLastChange(0),
mChangeDelay(-1),
mStoredImagePath(Src.mStoredImagePath),
mImageFrameId(0),
+ mOpacity(Src.mOpacity),
+ mScale(Src.mScale),
mScrollLoopMode(Src.mScrollLoopMode),
mScrollLoopReached(Src.mScrollLoopReached),
mScrollSpeed(Src.mScrollSpeed),
@@ -92,6 +129,9 @@ cSkinObject::cSkinObject(const cSkinObject & Src)
mCurrText(Src.mCurrText),
mAltText(Src.mAltText),
mAltCondition(Src.mAltCondition),
+ mAction(Src.mAction),
+ mMultilineScrollPosition(Src.mMultilineScrollPosition),
+ mMultilineRelScroll(Src.mMultilineRelScroll),
mObjects(NULL)
{
if (Src.mObjects)
@@ -116,14 +156,27 @@ bool cSkinObject::ParseType(const std::string & Text)
return false;
}
-bool cSkinObject::ParseColor(const std::string & Text)
+bool cSkinObject::ParseColor(const std::string & Text, cSkinColor & ParamColor)
{
- if (Text == "white")
- mColor = GLCD::clrWhite;
- else if (Text == "black")
- mColor = GLCD::clrBlack;
- else
+ std::string text = (std::string) Text;
+ cColor color = cColor::ERRCOL;
+ if (text[0] == '#') {
+ cSkinVariable * variable = mSkin->GetVariable(text.substr(1));
+ if (variable) {
+ color = cColor::ParseColor(variable->Value().String());
+ if (color == cColor::ERRCOL) {
+ return false;
+ }
+ ParamColor.SetVarId(text.substr(1));
+ return true;
+ }
return false;
+ }
+ color = cColor::ParseColor(text);
+ if (color == cColor::ERRCOL) {
+ return false;
+ }
+ ParamColor.SetColor(color);
return true;
}
@@ -165,6 +218,51 @@ bool cSkinObject::ParseVerticalAlignment(const std::string & Text)
return true;
}
+bool cSkinObject::ParseEffect(const std::string & Text)
+{
+ if (Text == "none")
+ mEffect = tfxNone;
+ else if (Text == "shadow")
+ mEffect = tfxShadow;
+ else if (Text == "outline")
+ mEffect = tfxOutline;
+ else
+ return false;
+ return true;
+}
+
+bool cSkinObject::ParseScale(const std::string & Text)
+{
+ if (Text == "none")
+ mScale = tscNone;
+ else if (Text == "auto")
+ mScale = tscAuto;
+ else if (Text == "autox")
+ mScale = tscAutoX;
+ else if (Text == "autoy")
+ mScale = tscAutoY;
+ else if (Text == "fill")
+ mScale = tscFill;
+ else
+ return false;
+ return true;
+}
+
+bool cSkinObject::ParseGradient(const std::string & Text)
+{
+ if (Text == "none")
+ mGradient = tgrdNone;
+ else if (Text == "total" || Text == "default")
+ mGradient = tgrdTotal;
+ else if (Text == "current" || Text == "currentonly")
+ mGradient = tgrdCurrent;
+ else if (Text == "vertical")
+ mGradient = tgrdVertical;
+ else
+ return false;
+ return true;
+}
+
bool cSkinObject::ParseIntParam(const std::string &Text, int & Param)
{
if (isalpha(Text[0]) || Text[0] == '#')
@@ -189,6 +287,7 @@ bool cSkinObject::ParseIntParam(const std::string &Text, int & Param)
return true;
}
+#if 0
bool cSkinObject::ParseWidth(const std::string &Text)
{
int w;
@@ -214,6 +313,7 @@ bool cSkinObject::ParseHeight(const std::string &Text)
}
return false;
}
+#endif
bool cSkinObject::ParseFontFace(const std::string & Text)
{
@@ -292,16 +392,33 @@ const std::string & cSkinObject::TypeName(void) const
tPoint cSkinObject::Pos(void) const
{
- return tPoint(mPos1.x < 0 ? mSkin->BaseSize().w + mPos1.x : mPos1.x,
- mPos1.y < 0 ? mSkin->BaseSize().h + mPos1.y : mPos1.y);
+ int x1 = mX1.Evaluate();
+ int y1 = mY1.Evaluate();
+ return tPoint(x1 < 0 ? mSkin->BaseSize().w + x1 : x1,
+ y1 < 0 ? mSkin->BaseSize().h + y1 : y1);
}
tSize cSkinObject::Size(void) const
{
- tPoint p1(mPos1.x < 0 ? mSkin->BaseSize().w + mPos1.x : mPos1.x,
- mPos1.y < 0 ? mSkin->BaseSize().h + mPos1.y : mPos1.y);
- tPoint p2(mPos2.x < 0 ? mSkin->BaseSize().w + mPos2.x : mPos2.x,
- mPos2.y < 0 ? mSkin->BaseSize().h + mPos2.y : mPos2.y);
+ int x1 = mX1.Evaluate();
+ int y1 = mY1.Evaluate();
+ tPoint p1(x1 < 0 ? mSkin->BaseSize().w + x1 : x1,
+ y1 < 0 ? mSkin->BaseSize().h + y1 : y1);
+
+ int w = mWidth.Evaluate();
+ int h = mHeight.Evaluate();
+
+ int x2 = mX2.Evaluate();
+ if (w != 0 && x2 == -1) {
+ x2 = x1 + w - 1;
+ }
+ int y2 = mY2.Evaluate();
+ if (h != 0 && y2 == -1) {
+ y2 = y1 + h - 1;
+ }
+
+ tPoint p2((x2 < 0) ? mSkin->BaseSize().w + x2 : x2,
+ (y2 < 0) ? mSkin->BaseSize().h + y2 : y2);
return tSize(p2.x - p1.x + 1, p2.y - p1.y + 1);
}
@@ -332,7 +449,47 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
mChangeDelay = -1;
}
- GLCD::cImage * image = cache->Get(evalPath);
+ uint16_t scalew = 0;
+ uint16_t scaleh = 0;
+
+ switch (mScale) {
+ case tscAuto:
+ {
+ uint16_t w_temp = 0;
+ uint16_t h_temp = 0;
+ // get dimensions of unscaled image
+ GLCD::cImage * image = cache->Get(evalPath, w_temp, h_temp);
+ if (image) {
+ w_temp = image->Width();
+ h_temp = image->Height();
+ if (w_temp != Size().w || h_temp != Size().h) {
+ double fw = (double)Size().w / (double)w_temp;
+ double fh = (double)Size().h / (double)h_temp;
+ if (fw < fh) {
+ scalew = Size().w;
+ } else {
+ scaleh = Size().h;
+ }
+ }
+ }
+ }
+ break;
+ case tscAutoX:
+ scalew = Size().w;
+ break;
+ case tscAutoY:
+ scaleh = Size().h;
+ break;
+ case tscFill:
+ scalew = Size().w;
+ scaleh = Size().h;
+ break;
+ default:
+ scalew = 0;
+ scaleh = 0;
+ }
+
+ GLCD::cImage * image = cache->Get(evalPath, scalew, scaleh);
if (image)
{
int framecount = image->Count();
@@ -341,7 +498,20 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
if (bitmap)
{
- screen->DrawBitmap(Pos().x, Pos().y, *bitmap, mColor);
+ uint16_t xoff = 0;
+ uint16_t yoff = 0;
+ if (scalew || scaleh) {
+ if (image->Width() < (uint16_t)Size().w) {
+ xoff = (Size().w - image->Width() ) / 2;
+ } else if (image->Height() < (uint16_t)Size().h) {
+ yoff = (Size().h - image->Height() ) / 2;
+ }
+ }
+
+ if (mColor == cColor::ERRCOL)
+ screen->DrawBitmap(Pos().x + xoff, Pos().y + yoff, *bitmap);
+ else
+ screen->DrawBitmap(Pos().x + xoff, Pos().y + yoff, *bitmap, mColor, mBackgroundColor, mOpacity);
}
if (mScrollLoopMode != -1) // if == -1: currScrollLoopMode already contains correct value
@@ -350,7 +520,7 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
if (framecount > 1 && currScrollLoopMode > 0 && !mScrollLoopReached) {
mChangeDelay = image->Delay();
- if ( (int)(timestamp - mLastChange) >= mChangeDelay) {
+ if ( (uint32_t)(timestamp - mLastChange) >= (uint32_t)mChangeDelay) {
if (currScrollLoopMode == 1 && mImageFrameId+1 == framecount) {
mScrollLoopReached = true; // stop looping and switch to 1st frame
@@ -405,33 +575,135 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
{
int current = mCurrent.Evaluate();
int total = mTotal.Evaluate();
+ int peak = mPeak.Evaluate();
if (total == 0)
total = 1;
if (current > total)
current = total;
- if (mDirection == 0)
- {
- int w = Size().w * current / total;
- if (w > 0)
- screen->DrawRectangle(Pos().x, Pos().y, Pos().x + w - 1, Pos().y + Size().h - 1, mColor, true);
- }
- else if (mDirection == 1)
- {
- int h = Size().h * current / total;
- if (h > 0)
- screen->DrawRectangle(Pos().x, Pos().y, Pos().x + Size().w - 1, Pos().y + h - 1, mColor, true);
- }
- else if (mDirection == 2)
- {
- int w = Size().w * current / total;
- if (w > 0)
- screen->DrawRectangle(Pos().x + Size().w - w, Pos().y, Pos().x + Size().w - 1, Pos().y + Size().h - 1, mColor, true);
+ if (peak > total)
+ peak = total;
+
+ int maxSize = ( (mDirection % 2) == 0 ) ? Size().w : Size().h;
+ int currSize = maxSize * current / total;
+
+ int peakSize = 0;
+ int peakBarSize = 2;
+ uint32_t peakGradientColor = (mPeakGradientColor == cColor::ERRCOL) ? mColor : mPeakGradientColor;
+
+ bool gradient = false;
+
+ if (peakGradientColor != mColor) {
+ if (mGradient != tgrdNone) {
+ gradient = true;
+ } else if (peak > 0) {
+ peakSize = maxSize * peak / total;
+ if (mRadius <= 0) {
+ peakBarSize = maxSize / 20;
+ if (peakBarSize < 2)
+ peakBarSize = 2;
+ } else {
+ peakBarSize = mRadius;
+ }
+ // at least peakBarSize of empty space between normal progress bar and peak marker. if too small: don't show peak marker
+ if (currSize + peakBarSize + (peakBarSize / 2) >= peakSize)
+ peakSize = 0; // don't show at all
+ }
}
- else if (mDirection == 3)
- {
- int h = Size().h * current / total;
- if (h > 0)
- screen->DrawRectangle(Pos().x, Pos().y + Size().h - h, Pos().x + Size().w - 1, Pos().y + Size().h - 1, mColor, true);
+
+ if (! gradient) {
+ if (mDirection == 0)
+ {
+ if (currSize > 0)
+ screen->DrawRectangle(Pos().x , Pos().y,
+ Pos().x + currSize - 1, Pos().y + Size().h - 1, mColor, true);
+ if (peakSize > 0)
+ screen->DrawRectangle(Pos().x + peakSize-1 , Pos().y,
+ Pos().x + peakSize-1 + peakBarSize-1, Pos().y + Size().h - 1, peakGradientColor, true);
+ }
+ else if (mDirection == 1)
+ {
+ if (currSize > 0)
+ screen->DrawRectangle(Pos().x , Pos().y,
+ Pos().x + Size().w - 1, Pos().y + currSize - 1, mColor, true);
+ if (peakSize > 0)
+ screen->DrawRectangle(Pos().x , Pos().y + peakSize-1,
+ Pos().x + Size().w - 1, Pos().y + peakSize-1 + peakBarSize-1, peakGradientColor, true);
+ }
+ else if (mDirection == 2)
+ {
+ if (currSize > 0)
+ screen->DrawRectangle(Pos().x + Size().w - currSize, Pos().y,
+ Pos().x + Size().w - 1 , Pos().y + Size().h - 1, mColor, true);
+ if (peakSize > 0)
+ screen->DrawRectangle(Pos().x + Size().w + maxSize - peakSize , Pos().y,
+ Pos().x + maxSize - peakSize + peakBarSize-1, Pos().y + Size().h - 1, peakGradientColor, true);
+ }
+ else if (mDirection == 3)
+ {
+ if (currSize > 0)
+ screen->DrawRectangle(Pos().x , Pos().y + Size().h - currSize,
+ Pos().x + Size().w - 1, Pos().y + Size().h - 1 , mColor, true);
+ if (peakSize > 0)
+ screen->DrawRectangle(Pos().x , Pos().y + maxSize - peakSize,
+ Pos().x + Size().w - 1, Pos().y + maxSize - peakSize + peakBarSize-1, peakGradientColor, true);
+ }
+ } else {
+ if (currSize > 0) {
+ int s_a = (mColor & 0xFF000000) >> 24;
+ int s_r = (mColor & 0x00FF0000) >> 16;
+ int s_g = (mColor & 0x0000FF00) >> 8;
+ int s_b = (mColor & 0x000000FF) ;
+ int delta_a = ((peakGradientColor & 0xFF000000) >> 24) - s_a;
+ int delta_r = ((peakGradientColor & 0x00FF0000) >> 16) - s_r;
+ int delta_g = ((peakGradientColor & 0x0000FF00) >> 8) - s_g;
+ int delta_b = ((peakGradientColor & 0x000000FF) ) - s_b;
+ int c_a, c_r, c_g, c_b;
+ double fact;
+ uint32_t currCol;
+ int gradSize = 0;
+ switch (mGradient) {
+ case tgrdCurrent: gradSize = currSize; break;
+ case tgrdVertical: gradSize = (mDirection % 2 == 0) ? Size().h : Size().w ; break;
+ default: gradSize = maxSize; break;
+ }
+
+ for (int i = 0; i < ((mGradient == tgrdVertical) ? gradSize : currSize); i++) {
+ fact = (double)i / (double)(gradSize - 1);
+ c_a = s_a + int( double(delta_a) * fact );
+ c_r = s_r + int( double(delta_r) * fact );
+ c_g = s_g + int( double(delta_g) * fact );
+ c_b = s_b + int( double(delta_b) * fact );
+ currCol = (c_a << 24) | (c_r << 16) | (c_g << 8) | c_b;
+ //fprintf(stderr, "i: %d / %08x -> %08x / currCol: %08x\n", i, (uint32_t)mColor, peakGradientColor, currCol);
+ if (mGradient == tgrdVertical) {
+ if (mDirection == 0)
+ screen->DrawLine(Pos().x, Pos().y + i,
+ Pos().x + currSize - 1, Pos().y + i, currCol);
+ else if (mDirection == 2)
+ screen->DrawLine(Pos().x + Size().w - currSize, Pos().y + i,
+ Pos().x + Size().w - 1, Pos().y + i, currCol);
+ else if (mDirection == 1)
+ screen->DrawLine(Pos().x + Size().w - 1 - i, Pos().y,
+ Pos().x + Size().w - 1 - i, Pos().y + currSize - 1, currCol);
+ else if (mDirection == 3)
+ screen->DrawLine(Pos().x + i, Pos().y + Size().h - currSize,
+ Pos().x + i, Pos().y + Size().h - 1 , currCol);
+ } else {
+ if (mDirection == 0)
+ screen->DrawLine(Pos().x + i, Pos().y,
+ Pos().x + i, Pos().y + Size().h - 1, currCol);
+ else if (mDirection == 2)
+ screen->DrawLine(Pos().x + Size().w - 1 - i, Pos().y,
+ Pos().x + Size().w - 1 - i, Pos().y + Size().h - 1, currCol);
+ else if (mDirection == 1)
+ screen->DrawLine(Pos().x , Pos().y + i,
+ Pos().x + Size().w - 1, Pos().y + i, currCol);
+ else if (mDirection == 3)
+ screen->DrawLine(Pos().x , Pos().y + Size().h - 1 - i,
+ Pos().x + Size().w - 1, Pos().y + Size().h - 1 - i , currCol);
+ }
+ }
+ }
}
break;
}
@@ -462,8 +734,48 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
currScrollTime = (int)(t);
}
+ // amount of loops for effects (no effect: 1 loop)
+ int loop;
+ int loops = 1;
+ int varx[6] = {0, 0, 0, 0, 0, 0};
+ int vary[6] = {0, 0, 0, 0, 0, 0};
+ uint32_t varcol[6] = { mColor, mColor, mColor, mColor, mColor, mColor };
+
+ int fxOff = 1;
+ if (mRadius > 1)
+ fxOff = 2;
+
+ switch (mEffect) {
+ case tfxShadow:
+ loops = 1;
+ for (int fxi = 0; fxi < fxOff; fxi++) {
+ varx[fxi] = fxi + 1; vary[fxi] = fxi + 1;
+ varcol[loops-1] = mEffectColor;
+ loops++;
+ }
+ varcol[loops-1] = cColor::Transparent;
+ loops++;
+ break;
+ case tfxOutline:
+ loops = 6;
+ varx[0] = -fxOff; vary[0] = 0;
+ varx[1] = fxOff; vary[1] = 0;
+ varx[2] = 0; vary[2] = -fxOff;
+ varx[3] = 0; vary[3] = fxOff;
+ varcol[0] = varcol[1] = varcol[2] = varcol[3] = mEffectColor;
+ varcol[4] = cColor::Transparent;
+ break;
+ case tfxNone: // no-one gets forgotten here, so make g++ happy
+ default:
+ loops = 1;
+ }
+
if (skinFont)
{
+
+ cBitmap* pane = new cBitmap(Size().w, Size().h, cColor::Transparent);
+ pane->SetProcessAlpha(false);
+
const cFont * font = skinFont->Font();
std::string text = "";
@@ -484,6 +796,7 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
mCurrText = text;
mScrollLoopReached = false;
mLastChange = timestamp;
+ mMultilineScrollPosition = 0;
}
if (mMultiline)
@@ -492,7 +805,24 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
mScrollLoopReached = true; // avoid check in NeedsUpdate()
std::vector <std::string> lines;
- font->WrapText(Size().w, Size().h, text, lines);
+ font->WrapText(Size().w, 0/*Size().h*/, text, lines);
+
+ size_t amount_lines = Size().h / font->LineHeight();
+
+ if (amount_lines < lines.size()) {
+ int multilineRelScroll = mMultilineRelScroll.Evaluate();
+ if (multilineRelScroll != 0) {
+ if (multilineRelScroll < 0) {
+ mMultilineScrollPosition += multilineRelScroll;
+ if (mMultilineScrollPosition < 0)
+ mMultilineScrollPosition = 0;
+ } else if (multilineRelScroll > 0) {
+ mMultilineScrollPosition += multilineRelScroll;
+ if (mMultilineScrollPosition > (int)((lines.size() - amount_lines)) )
+ mMultilineScrollPosition = lines.size() - amount_lines;
+ }
+ }
+ }
// vertical alignment, calculate y offset
int yoff = 0;
@@ -507,10 +837,14 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
default: yoff = 0;
}
- for (size_t i = 0; i < lines.size(); i++)
+ int end_line = amount_lines;
+ if (amount_lines > lines.size() )
+ end_line = lines.size();
+
+ for (size_t i = 0; i < (size_t)end_line; i++)
{
- int w = font->Width(lines[i]);
- int x = Pos().x;
+ int w = font->Width(lines[i + mMultilineScrollPosition]);
+ int x = 0;
if (w < Size().w)
{
if (mAlign == taRight)
@@ -522,7 +856,12 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
x += (Size().w - w) / 2;
}
}
- screen->DrawText(x, yoff + Pos().y + i * font->LineHeight(), x + Size().w - 1, lines[i], font, mColor);
+ for (loop = 0; loop < loops; loop++) {
+ pane->DrawText(
+ varx[loop] + x, vary[loop] + yoff + i * font->LineHeight(),
+ x + Size().w - 1, lines[i + mMultilineScrollPosition], font, varcol[loop], mBackgroundColor
+ );
+ }
}
}
else
@@ -549,7 +888,7 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
std::string::size_type pos1;
std::string::size_type pos2;
std::string str;
- int x = Pos().x;
+ int x = 0;
int w = Size().w;
int tab = 0;
int tabWidth;
@@ -560,7 +899,9 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
{
str = text.substr(pos1, pos2 - pos1);
tabWidth = mSkin->Config().GetTabPosition(tab, Size().w, *font);
- screen->DrawText(x, yoff + Pos().y, x + tabWidth - 1, str, font, mColor);
+ for (loop = 0; loop < loops; loop++) {
+ pane->DrawText( varx[loop] + x, vary[loop] + yoff, x + tabWidth - 1, str, font, varcol[loop], mBackgroundColor );
+ }
pos1 = pos2 + 1;
pos2 = text.find('\t', pos1);
tabWidth += font->Width(' ');
@@ -569,12 +910,14 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
tab++;
}
str = text.substr(pos1);
- screen->DrawText(x, yoff + Pos().y, x + w - 1, str, font, mColor);
+ for (loop = 0; loop < loops; loop++) {
+ pane->DrawText( varx[loop] + x, vary[loop] + yoff, x + w - 1, str, font, varcol[loop], mBackgroundColor );
+ }
}
else
{
int w = font->Width(text);
- int x = Pos().x;
+ int x = 0;
bool updateScroll = false;
if (w < Size().w)
@@ -601,7 +944,7 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
currScrollTime = mScrollTime;
if (currScrollLoopMode > 0 && (!mScrollLoopReached || mScrollOffset) &&
- ((int)(timestamp-mLastChange) >= currScrollTime)
+ ((uint32_t)(timestamp-mLastChange) >= (uint32_t)currScrollTime)
)
{
if (mScrollLoopReached)
@@ -615,22 +958,31 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
if (mScrollOffset) {
int corr_scrolloffset = mScrollOffset;
/* object update before scrolltime? use previous offset to avoid 'stumbling' scrolling */
- if ((int)(timestamp-mLastChange) < currScrollTime) {
+ if ((uint32_t)(timestamp-mLastChange) < (uint32_t)currScrollTime) {
corr_scrolloffset -= currScrollSpeed;
if (corr_scrolloffset < 0)
corr_scrolloffset = 0;
}
w += font->Width(" ");
std::string textdoubled = text + " " + text;
- screen->DrawText(x, yoff + Pos().y, x + Size().w - 1, textdoubled, font, mColor, true, corr_scrolloffset);
+ for (loop = 0; loop < loops; loop++) {
+ pane->DrawText(
+ varx[loop] + x, vary[loop] + yoff, x + Size().w - 1, textdoubled, font,
+ varcol[loop], mBackgroundColor, true, corr_scrolloffset
+ );
+ }
} else {
- screen->DrawText(x, yoff + Pos().y, x + Size().w - 1, text, font, mColor, true, mScrollOffset);
+ for (loop = 0; loop < loops; loop++) {
+ pane->DrawText(
+ varx[loop] + x, vary[loop] + yoff, x + Size().w - 1, text, font,
+ varcol[loop], mBackgroundColor, true, mScrollOffset
+ );
+ }
}
if (updateScroll) {
- mScrollOffset += currScrollSpeed;
-
- if ( x + Size().w + mScrollOffset >= (w+Size().w - font->Width(" "))) {
+ mScrollOffset += currScrollSpeed;
+ if ( mScrollOffset >= w ) {
if (currScrollLoopMode == 1)
// reset mScrollOffset in next step (else: string not redrawn when scroll done)
mScrollLoopReached = true;
@@ -642,6 +994,8 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
}
}
}
+ screen->DrawBitmap(Pos().x, Pos().y, *pane, cColor::White, cColor::Transparent);
+ delete pane;
}
break;
}
@@ -651,6 +1005,44 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
// Object->Align());
// break;
+ case cSkinObject::button:
+ {
+ cSkinFont * skinFont = mSkin->GetFont(mFont.Evaluate());
+
+ if (mBackgroundColor == mColor || mBackgroundColor == cColor::Transparent)
+ mBackgroundColor.SetColor( (cColor(mColor).Invert()) );
+
+ if (mRadius == 0)
+ screen->DrawRectangle(Pos().x, Pos().y, Pos().x + Size().w - 1, Pos().y + Size().h - 1, mBackgroundColor, true);
+ else
+ screen->DrawRoundRectangle(Pos().x, Pos().y, Pos().x + Size().w - 1, Pos().y + Size().h - 1, mBackgroundColor, true, mRadius);
+
+ if (skinFont)
+ {
+ const cFont * font = skinFont->Font();
+ std::string text = "";
+
+ text = (std::string) mText.Evaluate();
+
+ if (! (text == mCurrText) ) {
+ mCurrText = text;
+ }
+ std::vector <std::string> lines;
+ font->WrapText(Size().w, Size().h, text, lines);
+
+ // always use middle vertical alignment for buttons
+ int diff = Size().h - lines.size() * font->LineHeight();
+ int yoff = (diff > 0) ? diff >> 1 : 0;
+
+ int w = font->Width(text);
+ int x = Pos().x;
+ if (w < Size().w) // always center alignment for buttons
+ x += (Size().w - w) / 2;
+ screen->DrawText(x, yoff + Pos().y, x + Size().w - 1, text, font, mColor, mBackgroundColor);
+ }
+ break;
+ }
+
case cSkinObject::scrollbar:
//DrawScrollbar(Object->Pos(), Object->Size(), Object->Bg(), Object->Fg());
break;
@@ -673,14 +1065,22 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
{
for (int j = 1; j < (int) NumObjects(); j++)
{
+ int px, py, ph,pw;
+ char buf[10];
const cSkinObject * o = GetObject(j);
cSkinObject obj(*o);
obj.SetListIndex(maxitems, i);
if (obj.Condition() != NULL && !obj.Condition()->Evaluate())
continue;
- obj.mPos1.x += mPos1.x;
- obj.mPos1.y += mPos1.y + yoffset;
- obj.mPos2.y += mPos1.y + yoffset;
+ px = obj.Pos().x + Pos().x; // obj.mPos1.x += mPos1.x;
+ py = obj.Pos().y + Pos().y + yoffset; // obj.mPos1.y += mPos1.y + yoffset;
+ ph = o->Size().h; // obj.mPos2.y += mPos1.y + yoffset;
+ pw = o->Size().w;
+ snprintf(buf, 9, "%d", px); obj.mX1.Parse((const char*)buf);
+ snprintf(buf, 9, "%d", py); obj.mY1.Parse((const char*)buf);
+ if (ph > 0)
+ snprintf(buf, 9, "%d", ph); obj.mHeight.Parse((const char*)buf);
+ snprintf(buf, 9, "%d", pw); obj.mWidth.Parse((const char*)buf);
obj.Render(screen);
}
yoffset += itemheight;
@@ -710,7 +1110,7 @@ bool cSkinObject::NeedsUpdate(uint64_t CurrentTime)
currScrollLoopMode = mScrollLoopMode;
if ( mChangeDelay > 0 && currScrollLoopMode > 0 && !mScrollLoopReached &&
- ( (int)(CurrentTime-mLastChange) >= mChangeDelay)
+ ( (uint32_t)(CurrentTime-mLastChange) >= (uint32_t)mChangeDelay)
)
{
return true;
@@ -720,6 +1120,7 @@ bool cSkinObject::NeedsUpdate(uint64_t CurrentTime)
}
case cSkinObject::text:
case cSkinObject::scrolltext:
+ //case cSkinObject::button:
{
int currScrollLoopMode = 1; // default values if no setup default values available
int currScrollTime = 500;
@@ -759,7 +1160,7 @@ bool cSkinObject::NeedsUpdate(uint64_t CurrentTime)
if ( (text != mCurrText) ||
( (currScrollLoopMode > 0) && (!mScrollLoopReached || mScrollOffset) &&
- ((int)(CurrentTime-mLastChange) >= currScrollTime)
+ ((uint32_t)(CurrentTime-mLastChange) >= (uint32_t)currScrollTime)
)
)
{
@@ -788,6 +1189,71 @@ bool cSkinObject::NeedsUpdate(uint64_t CurrentTime)
}
+std::string cSkinObject::CheckAction(cGLCDEvent * ev)
+{
+ if (mCondition != NULL && !mCondition->Evaluate())
+ return "";
+
+ switch (Type())
+ {
+ case cSkinObject::image:
+ case cSkinObject::text:
+ case cSkinObject::scrolltext:
+ case cSkinObject::progress:
+ case cSkinObject::rectangle:
+ case cSkinObject::ellipse:
+ case cSkinObject::slope:
+ case cSkinObject::button:
+ case cSkinObject::item:
+ {
+ if (mAction == "")
+ return "";
+
+ if (ev && (typeid(*ev) == typeid(cTouchEvent))) {
+ cTouchEvent * stev = (cTouchEvent*)ev;
+ // check if touch event is in bounding box of object
+ // uses > and < -1 instead of >= and < -0 for better results
+ if ( (stev->x > Pos().x) && (stev->x < (Pos().x+Size().w -1)) &&
+ (stev->y > Pos().y) && (stev->y < (Pos().y+Size().h -1))
+ )
+ {
+ return mAction;
+ }
+ }
+ return "";
+ break;
+ }
+ case cSkinObject::block:
+ {
+ std::string rv = "";
+ for (uint32_t i = 0; i < NumObjects(); i++) {
+ if ( (rv = GetObject(i)->CheckAction(ev)) != "" ) {
+ return rv;
+ }
+ }
+ return "";
+ break;
+ }
+ default:
+ return "";
+ }
+ return "";
+}
+
+
+
+uint32_t cSkinColor::GetColor(void) {
+ if (mVarId != "") {
+ cSkinVariable * variable = mObject->Skin()->GetVariable(mVarId);
+ if (variable) {
+ return cColor::ParseColor(variable->Value().String());
+ }
+ return cColor::ERRCOL;
+ }
+ return (uint32_t) mColor;
+}
+
+
cSkinObjects::cSkinObjects(void)
{
}
diff --git a/glcdskin/object.h b/glcdskin/object.h
index 0dd996d..cb2f180 100644
--- a/glcdskin/object.h
+++ b/glcdskin/object.h
@@ -24,6 +24,8 @@
#include "type.h"
#include "string.h"
+#include <glcddrivers/driver.h>
+
namespace GLCD
{
@@ -58,6 +60,54 @@ enum eTextVerticalAlignment
tvaBottom
};
+enum eEffect
+{
+ tfxNone,
+ tfxShadow,
+ tfxOutline
+};
+
+enum eScale
+{
+ tscNone,
+ tscAuto,
+ tscAutoX,
+ tscAutoY,
+ tscFill
+};
+
+enum eGradient
+{
+ tgrdNone,
+ tgrdTotal,
+ tgrdCurrent,
+ tgrdVertical
+};
+
+
+
+class cSkinColor
+{
+private:
+ cSkinObject * mObject;
+ uint32_t mColor;
+ std::string mVarId;
+public:
+ cSkinColor(cSkinObject *Parent, uint32_t color):mVarId("") { mObject = Parent; mColor = color; }
+ cSkinColor(cSkinObject *Parent, cColor color):mVarId("") { mObject = Parent; mColor = (uint32_t)color; }
+ cSkinColor(cSkinObject *Parent, const std::string varId) { mObject = Parent; mVarId = varId; }
+ ~cSkinColor() {};
+
+ void SetColor(uint32_t color) { mVarId = ""; mColor = color; }
+ void SetColor(cColor color) { mVarId = ""; mColor = (uint32_t)color; }
+ void SetVarId(const std::string varId) { mVarId = varId; }
+
+ uint32_t GetColor(void);
+
+ operator uint32_t(void) { return GetColor(); }
+};
+
+
class cSkinObject
{
friend bool StartElem(const std::string & name, std::map<std::string,std::string> & attrs);
@@ -77,6 +127,7 @@ public:
text,
scrolltext,
scrollbar,
+ button,
block,
list,
item,
@@ -87,9 +138,16 @@ private:
cSkinDisplay * mDisplay; // parent display
cSkin * mSkin;
eType mType; // type of object, one of enum eType
- tPoint mPos1;
- tPoint mPos2;
- eColor mColor;
+ //tPoint mPos1;
+ //tPoint mPos2;
+ cSkinString mX1; // either mX1 and mWidth or mX1 and mX2 are defined. not all three
+ cSkinString mY1;
+ cSkinString mX2;
+ cSkinString mY2;
+ cSkinString mWidth;
+ cSkinString mHeight;
+ cSkinColor mColor;
+ cSkinColor mBackgroundColor;
bool mFilled;
int mRadius;
int mArc;
@@ -98,11 +156,16 @@ private:
eTextVerticalAlignment mVerticalAlign;
bool mMultiline;
cSkinString mPath;
- cSkinString mCurrent;
- cSkinString mTotal;
+ cSkinString mCurrent; // progress bar: current value
+ cSkinString mTotal; // progress bar: maximum valid value
+ cSkinString mPeak; // progress bar: peak value (<= 0: disabled)
cSkinString mFont;
cSkinString mText;
cSkinFunction * mCondition;
+ eEffect mEffect; // effect: none, shadow, or outline
+ cSkinColor mEffectColor; // effect colour (= shadow colour or colour of outline)
+ cSkinColor mPeakGradientColor; // colour of peak marker or gradient color (mutual exclusive)
+ eGradient mGradient; // use gradient effect for progress bar (overrules peak!)
uint64_t mLastChange; // timestamp: last change in dynamic object (scroll, frame change, ...)
int mChangeDelay; // delay between two changes (frame change, scrolling, ...)
@@ -111,6 +174,8 @@ private:
std::string mStoredImagePath; // stored image path
int mImageFrameId; // frame ID of image
+ int mOpacity; // opacity of an image ([0, 255], default 255)
+ eScale mScale; // image scaling (['none', 'autox', 'autoy', 'fill'], default: none)
int mScrollLoopMode; // scroll (text) or loop (image) mode: -1: default, 0: never, 1: once, 2: always
bool mScrollLoopReached; // if scroll/loop == once: already looped once?
@@ -121,6 +186,10 @@ private:
std::string mAltText; // alternative text source for text-objects
cSkinFunction * mAltCondition; // condition when alternative sources are used
+ std::string mAction; // action attached to this object
+
+ int mMultilineScrollPosition; // current scolling position of mMultiline
+ cSkinString mMultilineRelScroll;// relative scrolling amount of mMultiline (default: 0)
cSkinObjects * mObjects; // used for block objects such as <list>
@@ -130,14 +199,17 @@ public:
~cSkinObject();
bool ParseType(const std::string &Text);
- bool ParseColor(const std::string &Text);
+ bool ParseColor(const std::string &Text, cSkinColor & ParamColor);
bool ParseCondition(const std::string &Text);
bool ParseAlignment(const std::string &Text);
bool ParseVerticalAlignment(const std::string &Text);
+ bool ParseEffect(const std::string &Text);
+ bool ParseScale(const std::string &Text);
+ bool ParseGradient(const std::string &Text);
bool ParseFontFace(const std::string &Text);
bool ParseIntParam(const std::string &Text, int & Param);
- bool ParseWidth(const std::string &Text);
- bool ParseHeight(const std::string &Text);
+ //bool ParseWidth(const std::string &Text);
+ //bool ParseHeight(const std::string &Text);
bool ParseScrollLoopMode(const std::string & Text); // parse scroll mode ([never|once|always])
bool ParseScrollSpeed(const std::string & Text); // parse scroll speed
@@ -164,6 +236,8 @@ public:
// check if update is required for dynamic objects (image, text, progress, pane)
// false: no update required, true: update required
bool NeedsUpdate(uint64_t CurrentTime);
+
+ std::string CheckAction(cGLCDEvent * ev);
};
class cSkinObjects: public std::vector<cSkinObject *>
diff --git a/glcdskin/parser.c b/glcdskin/parser.c
index d42cfb8..5aa5515 100644
--- a/glcdskin/parser.c
+++ b/glcdskin/parser.c
@@ -16,28 +16,33 @@
#include <vector>
#include <string>
+#include <clocale>
+
#include "parser.h"
#include "xml.h"
#include "skin.h"
+/* workaround for thread-safe parsing */
+#include <pthread.h>
+
namespace GLCD
{
#define TAG_ERR_REMAIN(_context) do { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Unexpected tag %s within %s", \
- name.c_str(), _context); \
+ errorDetail = "Unexpected tag "+name+" within "+ _context; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
} while (0)
#define TAG_ERR_CHILD(_context) do { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: No child tag %s expected within %s", \
- name.c_str(), _context); \
+ errorDetail = "No child tag "+name+" expected within "+ _context; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
} while (0)
#define TAG_ERR_END(_context) do { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Unexpected closing tag for %s within %s", \
- name.c_str(), _context); \
+ errorDetail = "Unexpected closing tag for "+name+" within "+ _context; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
} while (0)
@@ -49,8 +54,8 @@ namespace GLCD
#define ATTRIB_MAN_STRING(_attr,_target) \
ATTRIB_OPT_STRING(_attr,_target) \
else { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \
- _attr, name.c_str()); \
+ errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+ name; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
}
@@ -59,8 +64,8 @@ namespace GLCD
char *_e; const char *_t = attrs[_attr].c_str(); \
long _l = strtol(_t, &_e, 10); \
if (_e ==_t || *_e != '\0') { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Invalid numeric value \"%s\" in attribute %s", \
- _t, _attr); \
+ errorDetail = "Invalid numeric value \""+ (std::string)_t +"\" in attribute "+ (std::string)_attr; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
} else \
_target = _l; \
@@ -69,8 +74,8 @@ namespace GLCD
#define ATTRIB_MAN_NUMBER(_attr,_target) \
ATTRIB_OPT_NUMBER(_attr,_target) \
else { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \
- _attr, name.c_str()); \
+ errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+name; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
}
@@ -81,8 +86,8 @@ namespace GLCD
else if (attrs[_attr] == "no") \
_target = false; \
else { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Invalid boolean value \"%s\" in attribute %s", \
- attrs[_attr].c_str(), _attr); \
+ errorDetail = "Invalid boolean value \""+ attrs[_attr] +"\" in attribute "+ _attr; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
} \
}
@@ -90,16 +95,16 @@ namespace GLCD
#define ATTRIB_MAN_BOOL(_attr,_target) \
ATTRIB_OPT_BOOL(_attr,_target) \
else { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \
- _attr, name.c_str()); \
+ errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+name; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
}
#define ATTRIB_OPT_FUNC(_attr,_func) \
if (attrs.find(_attr) != attrs.end()) { \
if (!_func(attrs[_attr])) { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Unexpected value %s for attribute %s", \
- attrs[_attr].c_str(), _attr); \
+ errorDetail = "Unexpected value \""+ attrs[_attr] +"\" for attribute "+ (std::string)_attr; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
} \
}
@@ -107,16 +112,16 @@ namespace GLCD
#define ATTRIB_MAN_FUNC(_attr,_func) \
ATTRIB_OPT_FUNC(_attr,_func) \
else { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \
- _attr, name.c_str()); \
+ errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+name; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
}
#define ATTRIB_OPT_FUNC_PARAM(_attr,_func,_param) \
if (attrs.find(_attr) != attrs.end()) { \
if (!_func(attrs[_attr],_param)) { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Unexpected value %s for attribute %s", \
- attrs[_attr].c_str(), _attr); \
+ errorDetail = "Unexpected value "+ attrs[_attr] +" for attribute "+ (std::string)_attr; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
} \
}
@@ -124,8 +129,8 @@ namespace GLCD
#define ATTRIB_MAN_FUNC_PARAM(_attr,_func,_param) \
ATTRIB_OPT_FUNC_PARAM(_attr,_func,_param) \
else { \
- syslog(LOG_ERR, "ERROR: graphlcd/skin: Mandatory attribute %s missing in tag %s", \
- _attr, name.c_str()); \
+ errorDetail = "Mandatory attribute "+ (std::string)_attr +" missing in tag "+name; \
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str()); \
return false; \
}
@@ -133,31 +138,140 @@ static std::vector<std::string> context;
static cSkin * skin = NULL;
static cSkinFont * font = NULL;
static cSkinVariable * variable = NULL;
+static cSkinVariable * variable_default = NULL;
static cSkinDisplay * display = NULL;
static std::vector <cSkinObject *> parents;
static cSkinObject * object = NULL;
static uint32_t oindex = 0;
+static std::string errorDetail = "";
+static std::string condblock_cond = "";
+
+// support for including files (templates, ...) in the skin definition
+// max. depth supported for file inclusion (-> detect recursive inclusions)
+#define MAX_INCLUDEDEPTH 5
+static int includeDepth = 0;
+static std::string subErrorDetail = "";
+
+bool StartElem(const std::string & name, std::map<std::string,std::string> & attrs);
+bool CharData(const std::string & text);
+bool EndElem(const std::string & name);
+
+
+
+static bool CheckSkinVersion(const std::string & version) {
+ float currv;
+ char* ecptr = NULL;
+ const char* verscstr = version.c_str();
+ // only accept floating point numbers with '.' as separator, no ','
+ char* curr_locale = setlocale(LC_NUMERIC, "C");
+
+ currv = strtof(verscstr, &ecptr);
+ setlocale(LC_NUMERIC, curr_locale);
+
+ if ( (*ecptr != '\0') || (ecptr == NULL) /*|| (ecptr != verscstr)*/ ||
+ ((int)(GLCDSKIN_SKIN_VERSION * 100.0) < (int)(currv * 100.0))
+ )
+ {
+ return false;
+ }
+ return true;
+}
+
+
bool StartElem(const std::string & name, std::map<std::string,std::string> & attrs)
{
//printf("start element: %s\n", name.c_str());
-
+// if (context.size() > 0) fprintf(stderr, "context: %s\n", context[context.size() - 1].c_str());
if (context.size() == 0)
{
if (name == "skin")
{
ATTRIB_MAN_STRING("version", skin->version);
ATTRIB_MAN_STRING("name", skin->title);
+ ATTRIB_OPT_FUNC("enable", skin->ParseEnable);
+
+ if (! CheckSkinVersion(skin->version) ) {
+ errorDetail = "skin version '"+ skin->version +"' not supported.";
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str());
+ return false;
+ }
}
else
TAG_ERR_REMAIN("document");
}
+ else if (name == "include")
+ {
+ if (includeDepth + 1 < MAX_INCLUDEDEPTH) {
+ cSkinObject* tmpobj = new cSkinObject(new cSkinDisplay(skin));
+ cSkinString* path = new cSkinString(tmpobj, false);
+ ATTRIB_MAN_FUNC("path", path->Parse);
+ std::string strpath = path->Evaluate();
+ // is path relative? -> prepend skinpath
+ if (strpath[0] != '/') {
+ strpath = skin->Config().SkinPath() + "/" + strpath;
+ }
+ path = NULL;
+ tmpobj = NULL;
+
+ includeDepth++;
+ cXML incxml(strpath, skin->Config().CharSet());
+ incxml.SetNodeStartCB(StartElem);
+ incxml.SetNodeEndCB(EndElem);
+ incxml.SetCDataCB(CharData);
+ if (incxml.Parse() != 0) {
+ errorDetail = "error when parsing included xml file '"+strpath+"'"+ ( (subErrorDetail == "") ? "" : " ("+subErrorDetail+")");
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str());
+ return false;
+ }
+ includeDepth--;
+ } else {
+ subErrorDetail = "max. include depth reached";
+ return false;
+ }
+ }
+ else if (name == "condblock")
+ {
+ int i = context.size() - 1;
+ while (i >= 0) {
+ if (context[i] == "condblock") {
+ errorDetail = "'condblock' must not be nested in another 'condblock'.";
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str());
+ return false;
+ }
+ i--;
+ }
+ ATTRIB_MAN_STRING("condition", condblock_cond);
+ }
else if (name == "variable")
{
variable = new cSkinVariable(skin);
ATTRIB_MAN_STRING("id", variable->mId);
+ ATTRIB_OPT_FUNC("evaluate", variable->ParseEvalMode);
ATTRIB_MAN_FUNC("value", variable->ParseValue);
- ATTRIB_OPT_FUNC("condition", variable->ParseCondition);
+ if (context[context.size() - 1] == "condblock") {
+ if (attrs.find("condition") != attrs.end()) {
+ errorDetail = "variable \""+variable->mId+"\" must not contain a condition when context = 'condblock'.";
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str());
+ return false;
+ } else {
+ if (! variable->ParseCondition(condblock_cond)) {
+ errorDetail = "Unexpected value \""+ attrs["condition"] +"\" for attribute "+ (std::string)"condition";
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: %s", errorDetail.c_str());
+ return false;
+ }
+ }
+ } else {
+ ATTRIB_OPT_FUNC("condition", variable->ParseCondition);
+ }
+ // if a 'default' value is set, create a second variable w/o condition: will be used if condition is not true
+ // as variables all have global scope (no matter where defined) and will always be sought from the start of the array,
+ // the default variable will be inserted _after_ the variable containing the condition.
+ if (attrs.find("default") != attrs.end()) {
+ variable_default = new cSkinVariable(skin);
+ ATTRIB_MAN_STRING("id", variable_default->mId);
+ ATTRIB_MAN_FUNC("default", variable_default->ParseValue);
+ }
}
else if (context[context.size() - 1] == "skin")
{
@@ -187,83 +301,98 @@ bool StartElem(const std::string & name, std::map<std::string,std::string> & att
}
object = new cSkinObject(display);
+
+ /* default settings */
+ object->ParseColor("transparent", object->mBackgroundColor);
+
if (object->ParseType(name))
{
- ATTRIB_OPT_FUNC_PARAM("x1", object->ParseIntParam, object->mPos1.x);
- ATTRIB_OPT_FUNC_PARAM("y1", object->ParseIntParam, object->mPos1.y);
- ATTRIB_OPT_FUNC_PARAM("x2", object->ParseIntParam, object->mPos2.x);
- ATTRIB_OPT_FUNC_PARAM("y2", object->ParseIntParam, object->mPos2.y);
- ATTRIB_OPT_FUNC("width", object->ParseWidth);
- ATTRIB_OPT_FUNC("height", object->ParseHeight);
+ object->mX1.Parse("0");
+ object->mY1.Parse("0");
+ object->mX2.Parse("-1");
+ object->mY2.Parse("-1");
+ object->mWidth.Parse("0");
+ object->mHeight.Parse("0");
+ ATTRIB_OPT_FUNC("x", object->mX1.Parse);
+ ATTRIB_OPT_FUNC("y", object->mY1.Parse);
+ ATTRIB_OPT_FUNC("x1", object->mX1.Parse);
+ ATTRIB_OPT_FUNC("y1", object->mY1.Parse);
+ ATTRIB_OPT_FUNC("x2", object->mX2.Parse);
+ ATTRIB_OPT_FUNC("y2", object->mY2.Parse);
+ ATTRIB_OPT_FUNC("width", object->mWidth.Parse);
+ ATTRIB_OPT_FUNC("height", object->mHeight.Parse);
ATTRIB_OPT_FUNC("condition", object->ParseCondition);
+ ATTRIB_OPT_STRING("action", object->mAction);
if (name == "image")
{
- ATTRIB_OPT_FUNC_PARAM("x", object->ParseIntParam, object->mPos1.x);
- ATTRIB_OPT_FUNC_PARAM("y", object->ParseIntParam, object->mPos1.y);
- ATTRIB_OPT_FUNC_PARAM("x", object->ParseIntParam, object->mPos2.x);
- ATTRIB_OPT_FUNC_PARAM("y", object->ParseIntParam, object->mPos2.y);
- ATTRIB_OPT_FUNC("color", object->ParseColor);
+ //ATTRIB_OPT_FUNC_PARAM("x", object->ParseIntParam, object->mPos2.x);
+ //ATTRIB_OPT_FUNC_PARAM("y", object->ParseIntParam, object->mPos2.y);
+ ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor);
+ ATTRIB_OPT_FUNC_PARAM("bgcolor", object->ParseColor, object->mBackgroundColor);
ATTRIB_MAN_FUNC("path", object->mPath.Parse);
ATTRIB_OPT_FUNC("loop", object->ParseScrollLoopMode);
+ ATTRIB_OPT_FUNC_PARAM("opacity", object->ParseIntParam, object->mOpacity);
+ ATTRIB_OPT_FUNC("scale", object->ParseScale);
}
else if (name == "text"
|| name == "scrolltext")
{
- ATTRIB_OPT_FUNC("color", object->ParseColor);
+ ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor);
+ ATTRIB_OPT_FUNC_PARAM("bgcolor", object->ParseColor, object->mBackgroundColor);
ATTRIB_OPT_FUNC("align", object->ParseAlignment);
ATTRIB_OPT_FUNC("valign", object->ParseVerticalAlignment);
- ATTRIB_OPT_FUNC("font", object->ParseFontFace);
+ ATTRIB_MAN_FUNC("font", object->ParseFontFace);
ATTRIB_OPT_BOOL("multiline", object->mMultiline);
+ ATTRIB_OPT_FUNC("mlrelscroll", object->mMultilineRelScroll.Parse);
ATTRIB_OPT_FUNC("scrollmode", object->ParseScrollLoopMode);
ATTRIB_OPT_FUNC("scrollspeed", object->ParseScrollSpeed);
ATTRIB_OPT_FUNC("scrolltime", object->ParseScrollTime);
ATTRIB_OPT_STRING("alttext", object->mAltText);
ATTRIB_OPT_FUNC("altcondition", object->ParseAltCondition);
-#if 0
- if (name == "blink")
- {
- ATTRIB_OPT_NUMBER("delay", object->mDelay);
-
- if (object->mDelay == 0)
- object->mDelay = 1000;
- }
- else if (name == "marquee")
- {
- ATTRIB_OPT_NUMBER("delay", object->mDelay);
-
- if (object->mDelay == 0)
- object->mDelay = 500;
- }
-#endif
+ ATTRIB_OPT_FUNC_PARAM("effectcolor", object->ParseColor, object->mEffectColor);
+ ATTRIB_OPT_FUNC("effect", object->ParseEffect);
+ ATTRIB_OPT_NUMBER("radius", object->mRadius);
+ }
+ else if (name == "button")
+ {
+ ATTRIB_OPT_FUNC_PARAM("labelcolor", object->ParseColor, object->mColor);
+ ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mBackgroundColor);
+ ATTRIB_MAN_FUNC("font", object->ParseFontFace);
+ ATTRIB_OPT_NUMBER("radius", object->mRadius);
}
else if (name == "pixel")
{
- ATTRIB_OPT_FUNC("color", object->ParseColor);
+ ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor);
}
else if (name == "line")
{
- ATTRIB_OPT_FUNC("color", object->ParseColor);
+ ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor);
}
else if (name == "rectangle")
{
- ATTRIB_OPT_FUNC("color", object->ParseColor);
+ ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor);
ATTRIB_OPT_BOOL("filled", object->mFilled);
ATTRIB_OPT_NUMBER("radius", object->mRadius);
}
else if (name == "ellipse" || name == "slope")
{
- ATTRIB_OPT_FUNC("color", object->ParseColor);
+ ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mColor);
ATTRIB_OPT_BOOL("filled", object->mFilled);
ATTRIB_OPT_NUMBER("arc", object->mArc);
}
else if (name == "progress"
|| name == "scrollbar")
{
- ATTRIB_OPT_FUNC("color", object->ParseColor);
- ATTRIB_OPT_NUMBER("direction", object->mDirection);
- ATTRIB_OPT_FUNC("current", object->mCurrent.Parse);
- ATTRIB_OPT_FUNC("total", object->mTotal.Parse);
+ ATTRIB_OPT_FUNC_PARAM( "color", object->ParseColor, object->mColor);
+ ATTRIB_OPT_NUMBER( "direction", object->mDirection);
+ ATTRIB_OPT_FUNC( "current", object->mCurrent.Parse);
+ ATTRIB_OPT_FUNC( "total", object->mTotal.Parse);
+ ATTRIB_OPT_FUNC( "peak", object->mPeak.Parse);
+ ATTRIB_OPT_FUNC_PARAM( "peakcolor", object->ParseColor, object->mPeakGradientColor);
+ ATTRIB_OPT_FUNC( "gradient", object->ParseGradient);
+ ATTRIB_OPT_FUNC_PARAM( "gradientcolor", object->ParseColor, object->mPeakGradientColor);
+ ATTRIB_OPT_NUMBER( "radius", object->mRadius);
}
#if 0
else if (name == "item") {
@@ -271,6 +400,12 @@ bool StartElem(const std::string & name, std::map<std::string,std::string> & att
--object->mPos2.y;
}
#endif
+ // range checks
+ if (object->mOpacity < 0)
+ object->mOpacity = 0;
+ else if (object->mOpacity > 255)
+ object->mOpacity = 255;
+
}
else
TAG_ERR_REMAIN(context[context.size() - 1].c_str());
@@ -291,13 +426,14 @@ bool CharData(const std::string & text)
//printf("context: %s\n", context[context.size() - 1].c_str());
if (context[context.size() - 1] == "text"
- || context[context.size() - 1] == "scrolltext")
+ || context[context.size() - 1] == "scrolltext"
+ || context[context.size() - 1] == "button")
{
if (!object->mText.Parse(text))
return false;
}
else
- syslog(LOG_ERR, "ERROR: Bad character data");
+ syslog(LOG_ERR, "ERROR: graphlcd/skin: Bad character data");
return true;
}
@@ -314,7 +450,13 @@ bool EndElem(const std::string & name)
else if (name == "variable")
{
skin->mVariables.push_back(variable);
+//fprintf(stderr, " variable '%s', value: %s\n", variable->mId.c_str(), ((std::string)variable->Value()).c_str());
variable = NULL;
+ if (variable_default != NULL) {
+ skin->mVariables.push_back(variable_default);
+//fprintf(stderr, " variable default '%s', value: %s\n", variable_default->mId.c_str(), ((std::string)variable_default->Value()).c_str());
+ variable_default = NULL;
+ }
}
else if (name == "display")
{
@@ -367,29 +509,57 @@ bool EndElem(const std::string & name)
return true;
}
-cSkin * XmlParse(cSkinConfig & Config, const std::string & Name, const std::string & fileName)
+static pthread_mutex_t parse_mutex; // temp. workaround of thread-safe parsing problem
+
+cSkin * XmlParse(cSkinConfig & Config, const std::string & Name, const std::string & fileName, std::string & errorString)
{
+ pthread_mutex_lock(&parse_mutex); // temp. workaround
+ //fprintf(stderr, ">>>>> XmlParse, Config: %s, Name: %s\n", Config.GetDriver()->ConfigName().c_str(), Name.c_str());
skin = new cSkin(Config, Name);
context.clear();
- cXML xml(fileName);
+ { // temp. workaround for thread-safe parsing
+ font = NULL;
+ variable = NULL;
+ variable_default = NULL;
+ display = NULL;
+ parents.clear();
+ object = NULL;
+ oindex = 0;
+ errorDetail = "";
+ condblock_cond = "";
+ includeDepth = 0;
+ subErrorDetail = "";
+ }
+ cXML xml(fileName, skin->Config().CharSet());
xml.SetNodeStartCB(StartElem);
xml.SetNodeEndCB(EndElem);
xml.SetCDataCB(CharData);
if (xml.Parse() != 0)
{
+ char buff[8];
+ snprintf(buff, 7, "%d", xml.LineNr());
syslog(LOG_ERR, "ERROR: graphlcd/skin: Parse error in %s, line %d", fileName.c_str(), xml.LineNr());
+ // shorter version outgoing errorString (eg. displaying errorString on the display)
+ errorString = "Parse error in skin "+Name+", line "+buff;
+ if (errorDetail != "")
+ errorString += ":\n"+errorDetail;
delete skin;
skin = NULL;
delete display;
display = NULL;
delete object;
object = NULL;
+ //fprintf(stderr, "<<<<< XmlParse ERROR, Config: %s, Name: %s\n", Config.GetDriver()->ConfigName().c_str(), Name.c_str());
+ pthread_mutex_unlock(&parse_mutex);
return NULL;
}
cSkin * result = skin;
skin = NULL;
+ errorString = "";
+ //fprintf(stderr, "<<<<< XmlParse, Config: %s, Name: %s\n", Config.GetDriver()->ConfigName().c_str(), Name.c_str());
+ pthread_mutex_unlock(&parse_mutex);
return result;
}
diff --git a/glcdskin/parser.h b/glcdskin/parser.h
index d7d7f63..855c626 100644
--- a/glcdskin/parser.h
+++ b/glcdskin/parser.h
@@ -16,13 +16,23 @@
#include <string>
+// max. version of skin definitions supported by the parser
+#define GLCDSKIN_SKIN_VERSION 1.2
+
+
namespace GLCD
{
class cSkin;
class cSkinConfig;
-cSkin * XmlParse(cSkinConfig & Config, const std::string & name, const std::string & fileName);
+cSkin * XmlParse(cSkinConfig & Config, const std::string & name, const std::string & fileName, std::string & errorString);
+
+// provide old function for compatibility
+cSkin * XmlParse(cSkinConfig & Config, const std::string & name, const std::string & fileName)
+{ std::string errorString = "";
+ return XmlParse(Config, name, fileName, errorString);
+}
} // end of namespace
diff --git a/glcdskin/skin.c b/glcdskin/skin.c
index a7facdd..122b162 100644
--- a/glcdskin/skin.c
+++ b/glcdskin/skin.c
@@ -21,6 +21,8 @@ cSkin::cSkin(cSkinConfig & Config, const std::string & Name)
name(Name)
{
mImageCache = new cImageCache(this, 100);
+ tsEvalTick = 0;
+ tsEvalSwitch = 0;
}
cSkin::~cSkin(void)
@@ -79,4 +81,16 @@ cSkinVariable * cSkin::GetVariable(const std::string & Id)
}
+bool cSkin::ParseEnable(const std::string & Text)
+{
+ cDriver * driver = config.GetDriver();
+
+ if (!driver)
+ return false;
+
+ driver->SetFeature(Text, 1);
+ return true; // always return true else loading the skin would fail if touchscreen is not available
+}
+
+
} // end of namespace
diff --git a/glcdskin/skin.h b/glcdskin/skin.h
index b2d8496..048c193 100644
--- a/glcdskin/skin.h
+++ b/glcdskin/skin.h
@@ -43,6 +43,8 @@ private:
cSkinDisplays displays;
cSkinVariables mVariables;
cImageCache * mImageCache;
+ uint64_t tsEvalTick;
+ uint64_t tsEvalSwitch;
public:
cSkin(cSkinConfig & Config, const std::string & Name);
@@ -61,6 +63,16 @@ public:
const tSize & BaseSize(void) const { return baseSize; }
cImageCache * ImageCache(void) { return mImageCache; }
+
+ bool ParseEnable(const std::string &Text);
+
+ cColor GetBackgroundColor(void) { return config.GetDriver()->GetBackgroundColor(); }
+ cColor GetForegroundColor(void) { return config.GetDriver()->GetForegroundColor(); }
+
+ void SetTSEvalTick(uint64_t ts) { tsEvalTick = ts; }
+ void SetTSEvalSwitch(uint64_t ts) { tsEvalSwitch = ts; }
+ const uint64_t GetTSEvalTick(void) { return tsEvalTick; }
+ const uint64_t GetTSEvalSwitch(void) { return tsEvalSwitch; }
};
} // end of namespace
diff --git a/glcdskin/string.c b/glcdskin/string.c
index dc9d9dc..3966449 100644
--- a/glcdskin/string.c
+++ b/glcdskin/string.c
@@ -55,18 +55,23 @@ std::string tSkinToken::Token(const tSkinToken & Token)
return result;
}
+#if 0
cSkinString::tStringList cSkinString::mStrings;
+#endif
cSkinString::cSkinString(cSkinObject *Parent, bool Translate)
: mObject(Parent),
mSkin(Parent->Skin()),
mTranslate(Translate)
{
+#if 0
mStrings.push_back(this);
+#endif
}
cSkinString::~cSkinString()
{
+#if 0
tStringList::iterator it = mStrings.begin();
for (; it != mStrings.end(); ++it) {
if ((*it) == this) {
@@ -74,8 +79,10 @@ cSkinString::~cSkinString()
break;
}
}
+#endif
}
+#if 0
void cSkinString::Reparse(void)
{
tStringList::iterator it = mStrings.begin();
@@ -84,6 +91,14 @@ void cSkinString::Reparse(void)
(*it)->Parse((*it)->mOriginal, true);
}
}
+#endif
+
+
+// copied from xml.c (should be valid for parsing variable names too ...)
+static bool IsTokenChar(bool start, int c) {
+ return isalpha(c) || c == '_' || (!start && isdigit(c));
+}
+
bool cSkinString::Parse(const std::string & Text, bool Translate)
{
@@ -93,16 +108,6 @@ bool cSkinString::Parse(const std::string & Text, bool Translate)
bool inAttrib = false;
int offset = 0;
- if (trans[0] == '#')
- {
- cSkinVariable * variable = mSkin->GetVariable(trans.substr(1));
- if (variable)
- {
- trans = (std::string) variable->Value();
- syslog(LOG_ERR, "string variable %s", trans.c_str());
- }
- }
-
//Dprintf("parsing: %s\n", Text.c_str());
mOriginal = Text;
mText = "";
@@ -113,16 +118,39 @@ bool cSkinString::Parse(const std::string & Text, bool Translate)
for (; *ptr; ++ptr) {
if (inToken && *ptr == '\\') {
if (*(ptr + 1) == '\0') {
- syslog(LOG_ERR, "ERROR: Stray \\ in token attribute\n");
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/string: Stray \\ in token attribute\n");
return false;
}
++ptr;
continue;
}
+ else if (*ptr == '#') {
+ if (inToken) {
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/string: Unexpected '#' in token");
+ return false;
+ }
+
+ mText.append(last, ptr - last);
+
+ bool isStartChar = true;
+ const char * varNameStart = ptr;
+ ptr++;
+ while (*ptr && IsTokenChar(isStartChar, *ptr)) {
+ isStartChar = false;
+ ptr++;
+ offset++;
+ }
+ // add #VARNAME#
+ mText.append(varNameStart, (ptr - varNameStart));
+ mText.append("#");
+ offset +=2; // adds two '#' -> fix offset
+ ptr--; // we'd be at the correct position now but the for-loop does a ++ptr -> fix it by stepping back one char
+ last = ptr + 1;
+ }
else if (*ptr == '{') {
if (inToken) {
- syslog(LOG_ERR, "ERROR: Unexpected '{' in token");
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/string: Unexpected '{' in token");
return false;
}
@@ -132,13 +160,13 @@ bool cSkinString::Parse(const std::string & Text, bool Translate)
}
else if (*ptr == '}' || (inToken && *ptr == ':')) {
if (!inToken) {
- syslog(LOG_ERR, "ERROR: Unexpected '}' outside of token");
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/string: Unexpected '}' outside of token");
return false;
}
if (inAttrib) {
if (*ptr == ':') {
- syslog(LOG_ERR, "ERROR: Unexpected ':' inside of token attribute");
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/string: Unexpected ':' inside of token attribute");
return false;
}
@@ -179,12 +207,14 @@ bool cSkinString::Parse(const std::string & Text, bool Translate)
{
std::string tmp;
tmp.assign(last, ptr - last);
- tSkinToken token(mSkin->Config().GetTokenId(tmp), tmp, offset, "");
- mTokens.push_back(token);
+ if (tmp != "") { // ignore empty token
+ tSkinToken token(mSkin->Config().GetTokenId(tmp), tmp, offset, "");
+ mTokens.push_back(token);
+ }
}
else
{
- syslog(LOG_ERR, "ERROR: Unexpected token {%.*s}", (int)(ptr - last), last);
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/string: Unexpected token {%.*s}", (int)(ptr - last), last);
return false;
}
@@ -201,7 +231,7 @@ bool cSkinString::Parse(const std::string & Text, bool Translate)
}
if (inToken) {
- syslog(LOG_ERR, "ERROR: Expecting '}' in token");
+ syslog(LOG_ERR, "ERROR: graphlcd/skin/string: Expecting '}' in token");
return false;
}
@@ -214,19 +244,92 @@ bool cSkinString::Parse(const std::string & Text, bool Translate)
cType cSkinString::Evaluate(void) const
{
- std::string result;
- int offset = 0;
-
if (mText.length() == 0 && mTokens.size() == 1)
return mSkin->Config().GetToken(mTokens[0]);
- for (uint32_t i = 0; i < mTokens.size(); ++i) {
- result.append(mText.c_str() + offset, mTokens[i].Offset - offset);
- result.append(mSkin->Config().GetToken(mTokens[i]));
+ std::string result_raw = "";
+ int offset = 0;
+ for (uint32_t i = 0; i < mTokens.size(); i++) {
+ result_raw.append(mText.substr(offset, mTokens[i].Offset - offset) );
+ result_raw.append(mSkin->Config().GetToken(mTokens[i]));
offset = mTokens[i].Offset;
}
- result.append(mText.c_str() + offset);
- return result;
+ result_raw.append(mText.c_str() + offset);
+
+ // replace variable placeholders (#VARNAME#) with corresponding values
+ std::string result_trans = "";
+ size_t idxstart = 0, idxend = 0;
+ size_t pos = 0;
+ while ( (idxstart=result_raw.find("#", idxstart)) != std::string::npos ) {
+ result_trans.append(result_raw.substr(pos, idxstart-pos));
+ idxend = result_raw.find("#", idxstart+1);
+ if (idxend != std::string::npos) {
+ cSkinVariable * variable = mSkin->GetVariable(result_raw.substr(idxstart+1, idxend-idxstart-1));
+ if (variable) {
+ std::string val = (std::string) variable->Value();
+
+ // if value of variable contains token definions: reparse value
+ if (val.find("{") != std::string::npos) {
+ cSkinString *result = new cSkinString(Object(), false);
+ if (result->Parse(val)) {
+ val = (std::string) result->Evaluate();
+ }
+ delete result;
+ }
+ result_trans.append (val);
+ // syslog(LOG_ERR, "string variable %s", trans.c_str());
+ } else {
+ result_trans.append (result_raw.substr(idxstart, idxend-idxstart)); // no variable found: print as raw text
+ }
+ idxstart = idxend+1;
+ pos = idxstart;
+ } else {
+ result_trans.append ("#"); // no closing '#' -> print # as raw text
+ idxstart ++;
+ pos = idxstart;
+ }
+ }
+ result_trans.append(result_raw.substr(pos));
+
+ // re-evaluate resulting string
+ if ((mText.size() > 0) && mText[0] != '#' && mObject != NULL ) {
+ cSkinFunction *result = new cSkinFunction(mObject);
+ if (result->Parse(result_trans, true)) {
+ std::string result_rescan = (std::string)result->Evaluate();
+ if (result_rescan != "")
+ result_trans = result_rescan;
+ }
+ delete result;
+ }
+
+ // look for $(..)$-expressions
+ result_raw = result_trans;
+ result_trans = "";
+ idxstart = 0;
+ pos = 0;
+ bool err = false;
+ while ( !err && ((idxstart = result_raw.find("$(", idxstart)) != std::string::npos) ) {
+ result_trans.append (result_raw.substr(pos, idxstart - pos) );
+ idxend = result_raw.find(")$", idxstart + 2);
+ if ( idxend == std::string::npos ) { // no $) found: ignore leading $( and pass rest of whole string unparsed
+ result_trans.append(result_raw.substr(pos));
+ err = true;
+ } else {
+ std::string func = result_raw.substr( idxstart + 2, idxend - idxstart - 2 );
+ cSkinFunction *result = new cSkinFunction(mObject);
+ if (result->Parse(func, true)) {
+ std::string result_rescan = (std::string)result->Evaluate();
+ if (result_rescan != "")
+ result_trans.append( result_rescan );
+ }
+ delete result;
+
+ idxstart = idxend + 2;
+ pos = idxstart;
+ }
+ }
+ result_trans.append( result_raw.substr(pos) );
+ return result_trans;
}
} // end of namespace
diff --git a/glcdskin/string.h b/glcdskin/string.h
index b75fa11..20c517c 100644
--- a/glcdskin/string.h
+++ b/glcdskin/string.h
@@ -86,8 +86,10 @@ class cSkin;
class cSkinString
{
private:
+#if 0
typedef std::vector<cSkinString*> tStringList;
static tStringList mStrings;
+#endif
cSkinObject * mObject;
cSkin * mSkin;
@@ -97,7 +99,9 @@ private:
bool mTranslate;
public:
+#if 0
static void Reparse(void);
+#endif
cSkinString(cSkinObject *Parent, bool Translate);
~cSkinString();
diff --git a/glcdskin/type.h b/glcdskin/type.h
index ad02a15..4d54de8 100644
--- a/glcdskin/type.h
+++ b/glcdskin/type.h
@@ -54,6 +54,10 @@ public:
void SetUpdate(uint32_t UpdateIn) { mUpdateIn = UpdateIn; }
uint32_t UpdateIn(void) const { return mUpdateIn; }
+ bool IsString(void) const { return (mType == string); }
+ bool IsNumber(void) const { return (mType == number); }
+ bool IsBoolean(void) const { return (mType == boolean); }
+
operator std::string () const { return String(); }
operator int () const { return Number(); }
operator bool () const;
diff --git a/glcdskin/variable.c b/glcdskin/variable.c
index b07773a..b300afc 100644
--- a/glcdskin/variable.c
+++ b/glcdskin/variable.c
@@ -11,23 +11,63 @@ cSkinVariable::cSkinVariable(cSkin * Parent)
: mSkin(Parent),
mValue(0),
mCondition(NULL),
+ mFunction(NULL),
mDummyDisplay(mSkin),
- mDummyObject(&mDummyDisplay)
+ mDummyObject(&mDummyDisplay),
+ mEvalMode(tevmTick),
+ mEvalInterval(0),
+ mTimestamp(0)
{
}
+bool cSkinVariable::ParseEvalMode(const std::string & Text)
+{
+
+ if (Text == "always") {
+ mEvalMode = tevmAlways;
+ } else if (Text == "tick") {
+ mEvalMode = tevmTick;
+ } else if (Text == "switch") {
+ mEvalMode = tevmSwitch;
+ } else if (Text == "once") {
+ mEvalMode = tevmOnce;
+ } else if (Text.length() > 9 && Text.substr(0,9) == "interval:") {
+ char * e;
+ const char * t = Text.substr(9).c_str();
+ long l = strtol(t, &e, 10);
+ if ( ! (e == t || *e != '\0') && (l >= 100))
+ {
+ mEvalInterval = (int) l;
+ mEvalMode = tevmInterval;
+ return true;
+ }
+ return false;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+
bool cSkinVariable::ParseValue(const std::string & Text)
{
- if (isalpha(Text[0]) || Text[0] == '#')
+ if (isalpha(Text[0]) || Text[0] == '#' || Text[0] == '{')
{
- cSkinFunction * func = new cSkinFunction(&mDummyObject);
- if (func->Parse(Text))
+ //delete mFunction;
+ mFunction = new cSkinFunction(&mDummyObject);
+ if (mFunction->Parse(Text))
{
- mValue = func->Evaluate();
- delete func;
+ if (mEvalMode == tevmOnce) {
+ mValue = mFunction->Evaluate();
+ delete mFunction;
+ mFunction = NULL;
+ }
+ //mValue = func->Evaluate();
+ //delete func;
return true;
}
- delete func;
+ delete mFunction;
+ mFunction = NULL;
}
else if (Text[0] == '\'')
{
@@ -57,6 +97,34 @@ bool cSkinVariable::ParseCondition(const std::string & Text)
return false;
}
+
+const cType & cSkinVariable::Value(void)
+{
+ if ( mTimestamp > 0 &&
+ ( ( mEvalMode == tevmTick && mTimestamp >= mSkin->GetTSEvalTick() ) ||
+ ( mEvalMode == tevmSwitch && mTimestamp >= mSkin->GetTSEvalSwitch() ) ||
+ ( mEvalMode == tevmInterval && (mTimestamp + (uint64_t)mEvalInterval) > mSkin->Config().Now())
+ )
+ )
+ {
+ return mValue;
+ }
+
+ if (mFunction != NULL) {
+ mValue = mFunction->Evaluate();
+ // should've been solved in ParseValue already, just to be sure ...
+ if (mEvalMode == tevmOnce) {
+ delete mFunction;
+ mFunction = NULL;
+ }
+ }
+ if (mEvalMode == tevmTick || mEvalMode == tevmSwitch || mEvalMode == tevmInterval) {
+ mTimestamp = mSkin->Config().Now();
+ }
+ return mValue;
+}
+
+
cSkinVariables::cSkinVariables(void)
{
}
diff --git a/glcdskin/variable.h b/glcdskin/variable.h
index 449f6b0..8c6ef6f 100644
--- a/glcdskin/variable.h
+++ b/glcdskin/variable.h
@@ -18,10 +18,21 @@
#include "display.h"
#include "object.h"
+#include "function.h"
namespace GLCD
{
+enum eEvalMode
+{
+ tevmAlways,
+ tevmTick,
+ tevmSwitch,
+ tevmOnce,
+ tevmInterval
+};
+
+
class cSkin;
class cSkinVariable
@@ -34,18 +45,24 @@ private:
std::string mId;
cType mValue;
cSkinFunction * mCondition;
+ cSkinFunction * mFunction;
cSkinDisplay mDummyDisplay;
cSkinObject mDummyObject;
+ eEvalMode mEvalMode;
+ int mEvalInterval;
+ uint64_t mTimestamp;
public:
cSkinVariable(cSkin * Parent);
+ bool ParseEvalMode(const std::string & Text);
bool ParseValue(const std::string & Text);
bool ParseCondition(const std::string & Text);
cSkin * Skin(void) const { return mSkin; }
const std::string & Id(void) const { return mId; }
- const cType & Value(void) const { return mValue; }
+// const cType & Value(void) const { return mValue; }
+ const cType & Value(void);
cSkinFunction * Condition(void) const { return mCondition; }
};
diff --git a/glcdskin/xml.c b/glcdskin/xml.c
index ed29d11..a3ad976 100644
--- a/glcdskin/xml.c
+++ b/glcdskin/xml.c
@@ -15,32 +15,15 @@
#include <iostream>
#include <fstream>
+#include <string.h>
+#include <stdlib.h>
+
#include "xml.h"
+#include "../glcdgraphics/common.h"
namespace GLCD
{
-std::string trim(const std::string & s)
-{
- std::string::size_type start, end;
-
- start = 0;
- while (start < s.length())
- {
- if (!isspace(s[start]))
- break;
- start++;
- }
- end = s.length() - 1;
- while (end >= 0)
- {
- if (!isspace(s[end]))
- break;
- end--;
- }
- return s.substr(start, end - start + 1);
-}
-
enum {
LOOK4START, // looking for first element start
LOOK4TAG, // looking for element tag
@@ -57,7 +40,7 @@ enum {
INCLOSETAG, // reading closing tag
};
-cXML::cXML(const std::string & file)
+cXML::cXML(const std::string & file, const std::string sysCharset)
: nodestartcb(NULL),
nodeendcb(NULL),
cdatacb(NULL),
@@ -66,6 +49,18 @@ cXML::cXML(const std::string & file)
{
char * buffer;
long size;
+ sysEncoding = sysCharset;
+ sysIsUTF8 = (sysEncoding == "UTF-8");
+ if (!sysIsUTF8) {
+ // convert from utf-8 to system encoding
+ iconv_cd = iconv_open(sysEncoding.c_str(), "UTF-8");
+ if (iconv_cd == (iconv_t) -1) {
+ syslog(LOG_ERR, "ERROR: system encoding %s is not supported\n", sysEncoding.c_str());
+ iconv_cd = NULL;
+ }
+ } else {
+ iconv_cd = NULL;
+ }
#if (__GNUC__ < 3)
std::ifstream f(file.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
@@ -75,20 +70,24 @@ cXML::cXML(const std::string & file)
if (!f.is_open())
{
syslog(LOG_ERR, "ERROR: skin file %s not found\n", file.c_str());
- }
- size = f.tellg();
+ validFile = false;
+ } else {
+ validFile = true;
+ size = f.tellg();
#if (__GNUC__ < 3)
- f.seekg(0, std::ios::beg);
+ f.seekg(0, std::ios::beg);
#else
- f.seekg(0, std::ios_base::beg);
+ f.seekg(0, std::ios_base::beg);
#endif
- buffer = new char[size];
- f.read(buffer, size);
- f.close();
- data.assign(buffer, size);
- delete[] buffer;
+ buffer = new char[size];
+ f.read(buffer, size);
+ f.close();
+ data.assign(buffer, size);
+ delete[] buffer;
+ }
}
+#if 0
cXML::cXML(const char * mem, unsigned int len)
: nodestartcb(NULL),
nodeendcb(NULL),
@@ -98,6 +97,13 @@ cXML::cXML(const char * mem, unsigned int len)
{
data.assign(mem, len);
}
+#endif
+
+cXML::~cXML()
+{
+ if (iconv_cd != NULL)
+ iconv_close(iconv_cd);
+}
void cXML::SetNodeStartCB(XML_NODE_START_CB(cb))
{
@@ -129,14 +135,29 @@ int cXML::Parse(void)
int percent = 0;
int last = 0;
std::string::size_type len;
+ uint32_t c, c_tmp;
+ unsigned int i_old;
+ int l, char_size;
+
+ if (!validFile)
+ return -1;
state = LOOK4START;
linenr = 1;
skipping = false;
len = data.length();
- for (std::string::size_type i = 0; i < len; i++)
+
+ unsigned int i = 0;
+ while (i < (unsigned int)len)
{
- if (ReadChar(data[i]) != 0)
+ i_old = i;
+ encodedCharAdjustCounter(true, data, c_tmp, i);
+ char_size = (i - i_old) + 1;
+ c = 0;
+ for (l = 0 ; l < char_size; l++)
+ c += ( (0xFF & data[i_old + l]) << ( l << 3) );
+
+ if (ReadChar(c /*data[i]*/, char_size) != 0)
return -1;
if (progresscb)
{
@@ -147,6 +168,7 @@ int cXML::Parse(void)
last = percent;
}
}
+ i++;
}
return 0;
}
@@ -156,8 +178,15 @@ bool cXML::IsTokenChar(bool start, int c)
return isalpha(c) || c == '_' || (!start && isdigit(c));
}
-int cXML::ReadChar(int c)
+int cXML::ReadChar(unsigned int c, int char_size)
{
+ // buffer for conversions (when conversion from utf8 to system encoding is required)
+ char convbufin[5];
+ char convbufout[5];
+ char* convbufinp = convbufin;
+ char* convbufoutp = convbufout;
+ size_t bufin_size, bufout_size, bufconverted;
+
// new line?
if (c == '\n')
linenr++;
@@ -179,6 +208,46 @@ int cXML::ReadChar(int c)
cdata.replace(pos, 4, ">");
else if (cdata.substr(pos, 5) == "&amp;")
cdata.replace(pos, 5, "&");
+ else if (cdata.substr(pos, 2) == "&#") {
+ bool ishex = ((cdata.substr(pos+2, 1) == "x") || (cdata.substr(pos+2, 1) == "X") );
+ size_t startpos = pos+2+((ishex)?1:0);
+ size_t endpos = cdata.find(';', startpos );
+ if (endpos != std::string::npos) {
+ char* tempptr;
+ std::string charid = cdata.substr(startpos, endpos-startpos);
+ long val = strtol(charid.c_str(), &tempptr, (ishex) ? 16 : 10);
+
+ if (tempptr != charid.c_str() && *tempptr == '\0') {
+ char encbuf[5]; size_t enclen = 0;
+
+ if ( val <= 0x1F ) {
+ enclen = 0; // ignore control chars
+ } else if ( val <= 0x007F ) {
+ enclen = 1;
+ encbuf[0] = (char)(val & 0x7F);
+ } else if ( val <= 0x07FF ) {
+ enclen = 2;
+ encbuf[1] = (char)(( val & 0x003F) | 0x80);
+ encbuf[0] = (char)(( (val & 0x07C0) >> 6) | 0xC0);
+ } else if ( val <= 0xFFFF ) {
+ enclen = 3;
+ encbuf[2] = (char)(( val & 0x003F) | 0x80);
+ encbuf[1] = (char)(( (val & 0x0FC0) >> 6) | 0x80);
+ encbuf[0] = (char)(( (val & 0xF000) >> 12) | 0xE0);
+ } else if ( val <= 0x10FFFF ) {
+ enclen = 4;
+ encbuf[3] = (char)(( val & 0x003F) | 0x80);
+ encbuf[2] = (char)(( (val & 0x0FC0) >> 6) | 0x80);
+ encbuf[1] = (char)(( (val & 0x03F000 ) >> 12) | 0x80);
+ encbuf[0] = (char)(( (val & 0x1C0000 ) >> 18) | 0xF0);
+ }
+ encbuf[enclen] = '\0';
+ if (enclen > 0) {
+ cdata.replace(pos, endpos-pos+1, encbuf);
+ }
+ }
+ }
+ }
pos++;
}
if (!cdatacb(trim(cdata)))
@@ -190,7 +259,29 @@ int cXML::ReadChar(int c)
state = LOOK4TAG;
}
else
- cdata += c;
+ {
+ int i;
+ //cdata += c;
+ // convert text-data on the fly if system encoding != UTF-8
+ if (iconv_cd != NULL && char_size > 1 /* ((c & 0x80) == 0x80)*/) {
+ for (i = 0; i < char_size; i++)
+ convbufin[i] = ( (char)((c >> ( i << 3) ) & 0xFF) );
+ convbufin[char_size] = '\0';
+ bufin_size = strlen(convbufin);
+ bufout_size = bufin_size;
+ bufconverted = iconv(iconv_cd, &convbufinp, &bufin_size, &convbufoutp, &bufout_size);
+
+ if (bufconverted != (size_t)-1 && strlen(convbufout) != 0) {
+ for (i = 0; i < (int)strlen(convbufout); i++)
+ cdata += convbufout[i];
+ } else {
+ cdata += "?";
+ }
+ } else {
+ for (i = 0; i < char_size; i++)
+ cdata += ( (unsigned char)((c >> ( i << 3) ) & 0xFF) );
+ }
+ }
// silently ignore until resync
break;
diff --git a/glcdskin/xml.h b/glcdskin/xml.h
index 009679c..0978cf4 100644
--- a/glcdskin/xml.h
+++ b/glcdskin/xml.h
@@ -15,6 +15,7 @@
#include <string>
#include <map>
+#include <iconv.h>
namespace GLCD
{
@@ -33,10 +34,14 @@ void (*CB)(int percent)
class cXML
{
private:
+ bool validFile;
bool skipping;
int state;
int linenr;
- int delim;
+ unsigned int delim;
+ std::string sysEncoding;
+ bool sysIsUTF8;
+ iconv_t iconv_cd;
std::string data, cdata, tag, attrn, attrv;
std::map<std::string, std::string> attr;
@@ -49,11 +54,12 @@ private:
protected:
bool IsTokenChar(bool start, int c);
- int ReadChar(int c);
+ int ReadChar(unsigned int c, int char_size);
public:
- cXML(const std::string & file);
- cXML(const char * mem, unsigned int len);
+ cXML(const std::string & file, const std::string sysCharset = "UTF-8");
+ //cXML(const char * mem, unsigned int len);
+ ~cXML();
void SetNodeStartCB(XML_NODE_START_CB(cb));
void SetNodeEndCB(XML_NODE_END_CB(cb));