summaryrefslogtreecommitdiff
path: root/glcdskin
diff options
context:
space:
mode:
authormrwastl <mrwastl@users.sourceforge.net>2011-05-01 22:22:32 +0200
committermrwastl <mrwastl@users.sourceforge.net>2011-05-01 22:22:32 +0200
commit46e597df44402086edd010b69702c2de52b75fc8 (patch)
treefa9528f19f951b765b071c239b09547cf69bd169 /glcdskin
parent57729cf285b058d192a60bd7fce1b2d29bdd9650 (diff)
downloadgraphlcd-base-46e597df44402086edd010b69702c2de52b75fc8.tar.gz
graphlcd-base-46e597df44402086edd010b69702c2de52b75fc8.tar.bz2
initial upload to branch 'touchcol'. see file 'HISTORY' for changes
Diffstat (limited to 'glcdskin')
-rw-r--r--glcdskin/config.c11
-rw-r--r--glcdskin/config.h6
-rw-r--r--glcdskin/display.c26
-rw-r--r--glcdskin/display.h4
-rw-r--r--glcdskin/function.c14
-rw-r--r--glcdskin/function.h4
-rw-r--r--glcdskin/object.c502
-rw-r--r--glcdskin/object.h56
-rw-r--r--glcdskin/parser.c185
-rw-r--r--glcdskin/parser.h11
-rw-r--r--glcdskin/skin.c12
-rw-r--r--glcdskin/skin.h10
-rw-r--r--glcdskin/string.c72
13 files changed, 815 insertions, 98 deletions
diff --git a/glcdskin/config.c b/glcdskin/config.c
index 518218e..c733427 100644
--- a/glcdskin/config.c
+++ b/glcdskin/config.c
@@ -1,6 +1,8 @@
#include "config.h"
#include "type.h"
+#include <sys/time.h>
+
namespace GLCD
{
@@ -39,4 +41,13 @@ int cSkinConfig::GetTabPosition(int Index, int MaxWidth, const cFont & Font)
return 0;
}
+uint64_t cSkinConfig::Now(void)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, 0);
+ return (uint64_t)(tv.tv_sec * 1000 + tv.tv_usec / 1000);
+}
+
+
} // end of namespace
diff --git a/glcdskin/config.h b/glcdskin/config.h
index f6c8af9..eede1cc 100644
--- a/glcdskin/config.h
+++ b/glcdskin/config.h
@@ -15,6 +15,9 @@
#include <string>
+#include <stdint.h>
+
+#include "../glcddrivers/driver.h"
namespace GLCD
{
@@ -22,6 +25,7 @@ namespace GLCD
class cType;
class cFont;
struct tSkinToken;
+class cDriver;
class cSkinConfig
{
@@ -35,6 +39,8 @@ public:
virtual cType GetToken(const tSkinToken & Token);
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 b92fea5..6f7a673 100644
--- a/glcdskin/display.c
+++ b/glcdskin/display.c
@@ -28,6 +28,32 @@ void cSkinDisplay::Render(cBitmap * screen)
}
+bool cSkinDisplay::NeedsUpdate(uint64_t CurrentTime)
+{
+ for (uint32_t i = 0; i < NumObjects(); ++i) {
+ if ( GetObject(i)->NeedsUpdate(CurrentTime) ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+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 af50179..ec0ee8b 100644
--- a/glcdskin/display.h
+++ b/glcdskin/display.h
@@ -43,6 +43,10 @@ public:
cSkinObject * GetObject(uint32_t n) const { return mObjects[n]; }
void Render(cBitmap * screen);
+
+ bool NeedsUpdate(uint64_t CurrentTime);
+
+ std::string CheckAction(cGLCDEvent * ev);
};
class cSkinDisplays: public std::vector<cSkinDisplay *>
diff --git a/glcdskin/function.c b/glcdskin/function.c
index 8582ad5..0fe39b3 100644
--- a/glcdskin/function.c
+++ b/glcdskin/function.c
@@ -33,6 +33,7 @@ static const char * Internals[] =
"FontTextHeight",
"ImageWidth",
"ImageHeight",
+ "QueryFeature",
NULL
};
@@ -247,6 +248,10 @@ bool cSkinFunction::Parse(const std::string & Text)
params = 1;
break;
+ case funQueryFeature:
+ params = 1;
+ break;
+
default:
break;
}
@@ -443,6 +448,15 @@ 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)");
diff --git a/glcdskin/function.h b/glcdskin/function.h
index 4b3732f..890d81f 100644
--- a/glcdskin/function.h
+++ b/glcdskin/function.h
@@ -70,7 +70,9 @@ public:
funFontTextHeight,
funImageWidth,
- funImageHeight
+ funImageHeight,
+
+ funQueryFeature
};
private:
diff --git a/glcdskin/object.c b/glcdskin/object.c
index c79fa59..3a8162e 100644
--- a/glcdskin/object.c
+++ b/glcdskin/object.c
@@ -4,6 +4,8 @@
#include "cache.h"
#include "function.h"
+#include <typeinfo>
+
namespace GLCD
{
@@ -19,9 +21,11 @@ static const std::string ObjectNames[] =
"text",
"scrolltext",
"scrollbar",
+ "button",
"block",
"list",
- "item"
+ "item",
+ "condblock"
};
cSkinObject::cSkinObject(cSkinDisplay * Parent)
@@ -30,12 +34,14 @@ cSkinObject::cSkinObject(cSkinDisplay * Parent)
mType((eType) __COUNT_OBJECT__),
mPos1(0, 0),
mPos2(-1, -1),
- mColor(GLCD::clrBlack),
+ mColor(cColor(cColor::Black)),
+ mBackgroundColor(cColor(cColor::Transparent)),
mFilled(false),
mRadius(0),
mArc(0),
mDirection(0),
mAlign(taLeft),
+ mVerticalAlign(tvaTop),
mMultiline(false),
mPath(this, false),
mCurrent(this, false),
@@ -43,8 +49,23 @@ cSkinObject::cSkinObject(cSkinDisplay * Parent)
mFont(this, false),
mText(this, false),
mCondition(NULL),
+ mLastChange(0),
+ mChangeDelay(-1), // delay between two images frames: -1: not animated / don't care
+ mStoredImagePath(""),
+ mImageFrameId(0), // start with 1st frame
+ 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)
+ mScrollTime(0), // scroll time interval: default (0)
+ mScrollOffset(0), // scroll offset (pixels)
+ 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)
mObjects(NULL)
{
+ mColor = Parent->Skin()->Config().GetDriver()->GetForegroundColor();
+ mBackgroundColor = Parent->Skin()->Config().GetDriver()->GetBackgroundColor();
}
cSkinObject::cSkinObject(const cSkinObject & Src)
@@ -54,11 +75,13 @@ cSkinObject::cSkinObject(const cSkinObject & Src)
mPos1(Src.mPos1),
mPos2(Src.mPos2),
mColor(Src.mColor),
+ mBackgroundColor(Src.mBackgroundColor),
mFilled(Src.mFilled),
mRadius(Src.mRadius),
mArc(Src.mArc),
mDirection(Src.mDirection),
mAlign(Src.mAlign),
+ mVerticalAlign(Src.mVerticalAlign),
mMultiline(Src.mMultiline),
mPath(Src.mPath),
mCurrent(Src.mCurrent),
@@ -66,6 +89,19 @@ cSkinObject::cSkinObject(const cSkinObject & Src)
mFont(Src.mFont),
mText(Src.mText),
mCondition(Src.mCondition),
+ mLastChange(0),
+ mChangeDelay(-1),
+ mStoredImagePath(Src.mStoredImagePath),
+ mImageFrameId(0),
+ mScrollLoopMode(Src.mScrollLoopMode),
+ mScrollLoopReached(Src.mScrollLoopReached),
+ mScrollSpeed(Src.mScrollSpeed),
+ mScrollTime(Src.mScrollTime),
+ mScrollOffset(Src.mScrollOffset),
+ mCurrText(Src.mCurrText),
+ mAltText(Src.mAltText),
+ mAltCondition(Src.mAltCondition),
+ mAction(Src.mAction),
mObjects(NULL)
{
if (Src.mObjects)
@@ -90,15 +126,17 @@ bool cSkinObject::ParseType(const std::string & Text)
return false;
}
-bool cSkinObject::ParseColor(const std::string & Text)
+bool cSkinObject::ParseColor(const std::string & Text, cColor & ParamColor)
{
- if (Text == "white")
- mColor = GLCD::clrWhite;
- else if (Text == "black")
- mColor = GLCD::clrBlack;
- else
- return false;
- return true;
+ std::string text = (std::string) Text;
+ if (text[0] == '#') {
+ cSkinVariable * variable = mSkin->GetVariable(text.substr(1));
+ if (variable) {
+ text = variable->Value().String();
+ }
+ }
+ ParamColor = cColor::ParseColor(text);
+ return (ParamColor == cColor::ERRCOL) ? false : true;
}
bool cSkinObject::ParseCondition(const std::string & Text)
@@ -126,6 +164,19 @@ bool cSkinObject::ParseAlignment(const std::string & Text)
return true;
}
+bool cSkinObject::ParseVerticalAlignment(const std::string & Text)
+{
+ if (Text == "top")
+ mVerticalAlign = tvaTop;
+ else if (Text == "middle")
+ mVerticalAlign = tvaMiddle;
+ else if (Text == "bottom")
+ mVerticalAlign = tvaBottom;
+ else
+ return false;
+ return true;
+}
+
bool cSkinObject::ParseIntParam(const std::string &Text, int & Param)
{
if (isalpha(Text[0]) || Text[0] == '#')
@@ -181,6 +232,63 @@ bool cSkinObject::ParseFontFace(const std::string & Text)
return mFont.Parse(Text);
}
+
+bool cSkinObject::ParseScrollLoopMode(const std::string & Text)
+{
+ if (Text == "never")
+ mScrollLoopMode = 0;
+ else if (Text == "once")
+ mScrollLoopMode = 1;
+ else if (Text == "always")
+ mScrollLoopMode = 2;
+ else
+ return false;
+ return true;
+}
+
+bool cSkinObject::ParseScrollSpeed(const std::string & Text)
+{
+ int val;
+ if (!ParseIntParam(Text, val))
+ return false;
+
+ if (val < 0 || val > 10)
+ return false;
+
+ mScrollSpeed = val;
+ return true;
+}
+
+bool cSkinObject::ParseScrollTime(const std::string & Text)
+{
+ int val;
+ if (!ParseIntParam(Text, val))
+ return false;
+
+ if (val < 0 || val > 2000)
+ return false;
+
+ if (val > 0 && val < 100)
+ val = 100;
+
+ mScrollTime = val;
+ return true;
+}
+
+
+bool cSkinObject::ParseAltCondition(const std::string & Text)
+{
+ cSkinFunction *result = new cSkinFunction(this);
+ if (result->Parse(Text))
+ {
+ delete mAltCondition;
+ mAltCondition = result;
+ return true;
+ }
+ return false;
+}
+
+
void cSkinObject::SetListIndex(int MaxItems, int Index)
{
mText.SetListIndex(MaxItems, Index);
@@ -211,22 +319,65 @@ tSize cSkinObject::Size(void) const
void cSkinObject::Render(GLCD::cBitmap * screen)
{
+ uint64_t timestamp;
+
if (mCondition != NULL && !mCondition->Evaluate())
return;
+ timestamp = mSkin->Config().Now();
+
switch (Type())
{
case cSkinObject::image:
{
cImageCache * cache = mSkin->ImageCache();
- GLCD::cImage * image = cache->Get(mPath.Evaluate());
+ int currScrollLoopMode = 2; //default if not configured in the skin: always
+
+ cType evalPath = mPath.Evaluate();
+ std::string currPath = evalPath;
+
+ if (currPath != mStoredImagePath) {
+ mImageFrameId = 0;
+ mStoredImagePath = currPath;
+ mScrollLoopReached = false;
+ mLastChange = timestamp;
+ mChangeDelay = -1;
+ }
+
+ GLCD::cImage * image = cache->Get(evalPath);
if (image)
{
- const GLCD::cBitmap * bitmap = image->GetBitmap();
+ int framecount = image->Count();
+
+ const GLCD::cBitmap * bitmap = image->GetBitmap(mImageFrameId);
+
if (bitmap)
{
- screen->DrawBitmap(Pos().x, Pos().y, *bitmap, mColor);
+ if (mColor == cColor::ERRCOL)
+ screen->DrawBitmap(Pos().x, Pos().y, *bitmap);
+ else
+ screen->DrawBitmap(Pos().x, Pos().y, *bitmap, mColor, mBackgroundColor);
+ }
+
+ if (mScrollLoopMode != -1) // if == -1: currScrollLoopMode already contains correct value
+ currScrollLoopMode = mScrollLoopMode;
+
+ if (framecount > 1 && currScrollLoopMode > 0 && !mScrollLoopReached) {
+ mChangeDelay = image->Delay();
+
+ if ( (uint32_t)(timestamp - mLastChange) >= (uint32_t)mChangeDelay) {
+
+ if (currScrollLoopMode == 1 && mImageFrameId+1 == framecount) {
+ mScrollLoopReached = true; // stop looping and switch to 1st frame
+ }
+
+ mImageFrameId = (mImageFrameId+1) % framecount;
+ mLastChange = timestamp;
+ }
}
+
+ if (mLastChange == 0)
+ mLastChange = timestamp;
}
break;
}
@@ -301,16 +452,76 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
}
case cSkinObject::text:
+ case cSkinObject::scrolltext:
{
cSkinFont * skinFont = mSkin->GetFont(mFont.Evaluate());
+ int currScrollLoopMode = 1; // default values if no setup default values available
+ int currScrollSpeed = 8;
+ int currScrollTime = 500;
+
+ // get default values from derived config-class if available
+ tSkinToken token = tSkinToken();
+ token.Id = mSkin->Config().GetTokenId("ScrollMode");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollLoopMode = (int)(t);
+ }
+ token.Id = mSkin->Config().GetTokenId("ScrollSpeed");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollSpeed = (int)(t);
+ }
+ token.Id = mSkin->Config().GetTokenId("ScrollTime");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollTime = (int)(t);
+ }
+
if (skinFont)
{
const cFont * font = skinFont->Font();
- std::string text = mText.Evaluate();
+ std::string text = "";
+
+ // is an alternative text defined + alternative condition defined and true?
+ if (mAltCondition != NULL && mAltCondition->Evaluate() && (mAltText.size() != 0)) {
+ cSkinString *result = new cSkinString(this, false);
+
+ if (result->Parse(mAltText)) {
+ text = (std::string) result->Evaluate();
+ }
+ delete result;
+ } else { // nope: use the original text
+ text = (std::string) mText.Evaluate();
+ }
+
+ if (! (text == mCurrText) ) {
+ mScrollOffset = 0;
+ mCurrText = text;
+ mScrollLoopReached = false;
+ mLastChange = timestamp;
+ }
+
if (mMultiline)
{
+ // scrolling in multiline not supported at the moment
+ mScrollLoopReached = true; // avoid check in NeedsUpdate()
+
std::vector <std::string> lines;
font->WrapText(Size().w, Size().h, text, lines);
+
+ // vertical alignment, calculate y offset
+ int yoff = 0;
+ int diff = Size().h - lines.size() * font->LineHeight();
+ switch (mVerticalAlign) {
+ case tvaMiddle:
+ yoff = (diff > 0) ? diff >> 1 : 0;
+ break;
+ case tvaBottom:
+ yoff = (diff > 0) ? diff : 0;
+ break;
+ default: yoff = 0;
+ }
+
for (size_t i = 0; i < lines.size(); i++)
{
int w = font->Width(lines[i]);
@@ -326,14 +537,30 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
x += (Size().w - w) / 2;
}
}
- screen->DrawText(x, Pos().y + i * font->LineHeight(), x + Size().w - 1, lines[i], font, mColor);
+ screen->DrawText(x, yoff + Pos().y + i * font->LineHeight(), x + Size().w - 1, lines[i], font, mColor, mBackgroundColor);
}
}
else
{
+ // vertical alignment, calculate y offset
+ int yoff = 0;
+ int diff = Size().h - font->LineHeight();
+ switch (mVerticalAlign) {
+ case tvaMiddle:
+ yoff = (diff > 0) ? diff >> 1 : 0;
+ break;
+ case tvaBottom:
+ yoff = (diff > 0) ? diff : 0;
+ break;
+ default: yoff = 0;
+ }
+
if (text.find('\t') != std::string::npos
&& mSkin->Config().GetTabPosition(0, Size().w, *font) > 0)
{
+ // scrolling in texts with tabulators not supported at the moment
+ mScrollLoopReached = true; // avoid check in NeedsUpdate()
+
std::string::size_type pos1;
std::string::size_type pos2;
std::string str;
@@ -348,7 +575,7 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
{
str = text.substr(pos1, pos2 - pos1);
tabWidth = mSkin->Config().GetTabPosition(tab, Size().w, *font);
- screen->DrawText(x, Pos().y, x + tabWidth - 1, str, font, mColor);
+ screen->DrawText(x, yoff + Pos().y, x + tabWidth - 1, str, font, mColor, mBackgroundColor);
pos1 = pos2 + 1;
pos2 = text.find('\t', pos1);
tabWidth += font->Width(' ');
@@ -357,14 +584,18 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
tab++;
}
str = text.substr(pos1);
- screen->DrawText(x, Pos().y, x + w - 1, str, font, mColor);
+ screen->DrawText(x, yoff + Pos().y, x + w - 1, str, font, mColor, mBackgroundColor);
}
else
{
int w = font->Width(text);
int x = Pos().x;
+ bool updateScroll = false;
+
if (w < Size().w)
{
+ mScrollLoopReached = true; // avoid check in NeedsUpdate()
+
if (mAlign == taRight)
{
x += Size().w - w;
@@ -373,18 +604,105 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
{
x += (Size().w - w) / 2;
}
+ } else {
+
+ if (mScrollLoopMode != -1) // if == -1: currScrollLoopMode already contains correct value
+ currScrollLoopMode = mScrollLoopMode;
+
+ if (mScrollSpeed > 0)
+ currScrollSpeed = mScrollSpeed;
+
+ if (mScrollTime > 0)
+ currScrollTime = mScrollTime;
+
+ if (currScrollLoopMode > 0 && (!mScrollLoopReached || mScrollOffset) &&
+ ((uint32_t)(timestamp-mLastChange) >= (uint32_t)currScrollTime)
+ )
+ {
+ if (mScrollLoopReached)
+ mScrollOffset = 0;
+ else
+ updateScroll = true;
+ }
+
+ }
+
+ if (mScrollOffset) {
+ int corr_scrolloffset = mScrollOffset;
+ /* object update before scrolltime? use previous offset to avoid 'stumbling' scrolling */
+ 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, mBackgroundColor, true, corr_scrolloffset);
+ } else {
+ screen->DrawText(x, yoff + Pos().y, x + Size().w - 1, text, font, mColor, mBackgroundColor, true, mScrollOffset);
+ }
+
+ if (updateScroll) {
+ mScrollOffset += currScrollSpeed;
+
+ if ( x + Size().w + mScrollOffset >= (w+Size().w - font->Width(" "))) {
+ if (currScrollLoopMode == 1)
+ // reset mScrollOffset in next step (else: string not redrawn when scroll done)
+ mScrollLoopReached = true;
+ else
+ mScrollOffset= 0;
+ }
+ updateScroll = false;
+ mLastChange = timestamp;
}
- screen->DrawText(x, Pos().y, x + Size().w - 1, text, font, mColor);
}
}
}
break;
}
- case cSkinObject::scrolltext:
+// case cSkinObject::scrolltext:
//DrawScrolltext(Object->Pos(), Object->Size(), Object->Fg(), Object->Text(), Object->Font(),
// Object->Align());
+// break;
+
+ case cSkinObject::button:
+ {
+ cSkinFont * skinFont = mSkin->GetFont(mFont.Evaluate());
+
+ if (mBackgroundColor == mColor || mBackgroundColor == cColor::Transparent)
+ mBackgroundColor = 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());
@@ -430,6 +748,152 @@ void cSkinObject::Render(GLCD::cBitmap * screen)
}
}
+bool cSkinObject::NeedsUpdate(uint64_t CurrentTime)
+{
+ if (mCondition != NULL && !mCondition->Evaluate())
+ return false;
+
+ switch (Type())
+ {
+ case cSkinObject::image:
+ {
+ int currScrollLoopMode = 2;
+
+ if (mScrollLoopMode != -1)
+ currScrollLoopMode = mScrollLoopMode;
+
+ if ( mChangeDelay > 0 && currScrollLoopMode > 0 && !mScrollLoopReached &&
+ ( (uint32_t)(CurrentTime-mLastChange) >= (uint32_t)mChangeDelay)
+ )
+ {
+ return true;
+ }
+ return false;
+ break;
+ }
+ case cSkinObject::text:
+ case cSkinObject::scrolltext:
+ //case cSkinObject::button:
+ {
+ int currScrollLoopMode = 1; // default values if no setup default values available
+ int currScrollTime = 500;
+
+ std::string text = "";
+
+ // is an alternative text defined + alternative condition defined and true?
+ if (mAltCondition != NULL && mAltCondition->Evaluate() && (mAltText.size() != 0)) {
+ cSkinString *result = new cSkinString(this, false);
+
+ if (result->Parse(mAltText)) {
+ text = (std::string) result->Evaluate();
+ }
+ delete result;
+ } else { // nope: use the original text
+ text = (std::string) mText.Evaluate();
+ }
+
+ // get default values from derived config-class if available
+ tSkinToken token = tSkinToken();
+ token.Id = mSkin->Config().GetTokenId("ScrollMode");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollLoopMode = (int)(t);
+ }
+ token.Id = mSkin->Config().GetTokenId("ScrollTime");
+ if (token.Id >= 0) {
+ cType t = mSkin->Config().GetToken(token);
+ currScrollTime = (int)(t);
+ }
+
+ if (mScrollLoopMode != -1) // if == -1: currScrollLoopMode already contains correct value
+ currScrollLoopMode = mScrollLoopMode;
+
+ if (mScrollTime > 0)
+ currScrollTime = mScrollTime;
+
+ if ( (text != mCurrText) ||
+ ( (currScrollLoopMode > 0) && (!mScrollLoopReached || mScrollOffset) &&
+ ((uint32_t)(CurrentTime-mLastChange) >= (uint32_t)currScrollTime)
+ )
+ )
+ {
+ return true;
+ }
+ return false;
+ break;
+ }
+ case cSkinObject::progress:
+ return false;
+ break;
+ case cSkinObject::block:
+ {
+ for (uint32_t i = 0; i < NumObjects(); i++) {
+ if ( GetObject(i)->NeedsUpdate(CurrentTime) ) {
+ return true;
+ }
+ }
+ return false;
+ break;
+ }
+ default: // all other elements are static ones
+ return false;
+ }
+ return false;
+}
+
+
+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(cSimpleTouchEvent))) {
+ cSimpleTouchEvent * stev = (cSimpleTouchEvent*)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 "";
+}
+
+
cSkinObjects::cSkinObjects(void)
{
}
diff --git a/glcdskin/object.h b/glcdskin/object.h
index f164747..81fb139 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
{
@@ -51,6 +53,13 @@ enum eTextAlignment
taRight
};
+enum eTextVerticalAlignment
+{
+ tvaTop,
+ tvaMiddle,
+ tvaBottom
+};
+
class cSkinObject
{
friend bool StartElem(const std::string & name, std::map<std::string,std::string> & attrs);
@@ -70,6 +79,7 @@ public:
text,
scrolltext,
scrollbar,
+ button,
block,
list,
item,
@@ -77,17 +87,19 @@ public:
};
private:
- cSkinDisplay * mDisplay;
+ cSkinDisplay * mDisplay; // parent display
cSkin * mSkin;
- eType mType;
+ eType mType; // type of object, one of enum eType
tPoint mPos1;
tPoint mPos2;
- eColor mColor;
+ cColor mColor;
+ cColor mBackgroundColor;
bool mFilled;
int mRadius;
int mArc;
int mDirection;
eTextAlignment mAlign;
+ eTextVerticalAlignment mVerticalAlign;
bool mMultiline;
cSkinString mPath;
cSkinString mCurrent;
@@ -96,7 +108,26 @@ private:
cSkinString mText;
cSkinFunction * mCondition;
- cSkinObjects * mObjects; // used for block objects such as <list>
+ uint64_t mLastChange; // timestamp: last change in dynamic object (scroll, frame change, ...)
+ int mChangeDelay; // delay between two changes (frame change, scrolling, ...)
+ // special values: -2: no further looping (mScrollLoopMode == 'once')
+ // -1: not set (ie: not an animated image)
+
+ std::string mStoredImagePath; // stored image path
+ int mImageFrameId; // frame ID of image
+
+ 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?
+ int mScrollSpeed; // scroll speed: 0: default, [1 - 10]: speed
+ int mScrollTime; // scroll time interval: 0: default, [100 - 2000]: time interval
+ int mScrollOffset; // scroll offset (pixels)
+ std::string mCurrText; // current text (for checks if text has changed)
+
+ 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
+
+ cSkinObjects * mObjects; // used for block objects such as <list>
public:
cSkinObject(cSkinDisplay * parent);
@@ -104,19 +135,26 @@ public:
~cSkinObject();
bool ParseType(const std::string &Text);
- bool ParseColor(const std::string &Text);
+ bool ParseColor(const std::string &Text, cColor & ParamColor);
bool ParseCondition(const std::string &Text);
bool ParseAlignment(const std::string &Text);
+ bool ParseVerticalAlignment(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 ParseScrollLoopMode(const std::string & Text); // parse scroll mode ([never|once|always])
+ bool ParseScrollSpeed(const std::string & Text); // parse scroll speed
+ bool ParseScrollTime(const std::string & Text); // parse scroll time interval
+
+ bool ParseAltCondition(const std::string &Text); // parse condition for alternative use (eg. alternative sources)
+
void SetListIndex(int MaxItems, int Index);
eType Type(void) const { return mType; }
cSkinFunction * Condition(void) const { return mCondition; }
- cSkinDisplay * Display(void) const { return mDisplay; }
+ cSkinDisplay * Display(void) const { return mDisplay; }
cSkin * Skin(void) const { return mSkin; }
const std::string & TypeName(void) const;
@@ -127,6 +165,12 @@ public:
cSkinObject * GetObject(uint32_t Index) const;
void Render(cBitmap * screen);
+
+ // 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 b137a5f..31b3106 100644
--- a/glcdskin/parser.c
+++ b/glcdskin/parser.c
@@ -24,20 +24,20 @@ namespace GLCD
{
#define TAG_ERR_REMAIN(_context) do { \
- syslog(LOG_ERR, "ERROR: Text2Skin: 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: Text2Skin: 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: Text2Skin: 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 +49,8 @@ namespace GLCD
#define ATTRIB_MAN_STRING(_attr,_target) \
ATTRIB_OPT_STRING(_attr,_target) \
else { \
- syslog(LOG_ERR, "ERROR: Text2Skin: 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 +59,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: Text2Skin: 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 +69,8 @@ namespace GLCD
#define ATTRIB_MAN_NUMBER(_attr,_target) \
ATTRIB_OPT_NUMBER(_attr,_target) \
else { \
- syslog(LOG_ERR, "ERROR: Text2Skin: 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 +81,8 @@ namespace GLCD
else if (attrs[_attr] == "no") \
_target = false; \
else { \
- syslog(LOG_ERR, "ERROR: Text2Skin: 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 +90,16 @@ namespace GLCD
#define ATTRIB_MAN_BOOL(_attr,_target) \
ATTRIB_OPT_BOOL(_attr,_target) \
else { \
- syslog(LOG_ERR, "ERROR: Text2Skin: 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: Text2Skin: 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 +107,16 @@ namespace GLCD
#define ATTRIB_MAN_FUNC(_attr,_func) \
ATTRIB_OPT_FUNC(_attr,_func) \
else { \
- syslog(LOG_ERR, "ERROR: Text2Skin: 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: Text2Skin: 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 +124,8 @@ namespace GLCD
#define ATTRIB_MAN_FUNC_PARAM(_attr,_func,_param) \
ATTRIB_OPT_FUNC_PARAM(_attr,_func,_param) \
else { \
- syslog(LOG_ERR, "ERROR: Text2Skin: 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 +133,93 @@ 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 = "";
+
+
+static bool CheckSkinVersion(const std::string & version) {
+ float currv;
+ char* ecptr = NULL;
+ const char* verscstr = version.c_str();
+ currv = strtof(verscstr, &ecptr);
+ 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 == "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_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")
{
@@ -196,6 +258,7 @@ bool StartElem(const std::string & name, std::map<std::string,std::string> & att
ATTRIB_OPT_FUNC("width", object->ParseWidth);
ATTRIB_OPT_FUNC("height", object->ParseHeight);
ATTRIB_OPT_FUNC("condition", object->ParseCondition);
+ ATTRIB_OPT_STRING("action", object->mAction);
if (name == "image")
{
@@ -203,57 +266,57 @@ bool StartElem(const std::string & name, std::map<std::string,std::string> & att
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("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);
}
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_OPT_BOOL("multiline", object->mMultiline);
-#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("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);
+ }
+ else if (name == "button")
+ {
+ ATTRIB_OPT_FUNC_PARAM("labelcolor", object->ParseColor, object->mColor);
+ ATTRIB_OPT_FUNC_PARAM("color", object->ParseColor, object->mBackgroundColor);
+ ATTRIB_OPT_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_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);
@@ -284,7 +347,8 @@ 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;
@@ -307,7 +371,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")
{
@@ -320,7 +390,7 @@ bool EndElem(const std::string & name)
{
if (object == NULL)
{
- printf("rotating parent to object\n");
+ //printf("rotating parent to object\n");
object = parents[parents.size() - 1];
parents.pop_back();
}
@@ -343,7 +413,7 @@ bool EndElem(const std::string & name)
#endif
if (parents.size() > 0)
{
- printf("pushing to parent\n");
+ //printf("pushing to parent\n");
cSkinObject *parent = parents[parents.size() - 1];
if (parent->mObjects == NULL)
parent->mObjects = new cSkinObjects();
@@ -360,7 +430,7 @@ bool EndElem(const std::string & name)
return true;
}
-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)
{
skin = new cSkin(Config, Name);
context.clear();
@@ -371,7 +441,13 @@ cSkin * XmlParse(cSkinConfig & Config, const std::string & Name, const std::stri
xml.SetCDataCB(CharData);
if (xml.Parse() != 0)
{
- syslog(LOG_ERR, "ERROR: Text2Skin: Parse error in %s, line %d", fileName.c_str(), xml.LineNr());
+ 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;
@@ -383,6 +459,7 @@ cSkin * XmlParse(cSkinConfig & Config, const std::string & Name, const std::stri
cSkin * result = skin;
skin = NULL;
+ errorString = "";
return result;
}
diff --git a/glcdskin/parser.h b/glcdskin/parser.h
index d7d7f63..f8b2a2e 100644
--- a/glcdskin/parser.h
+++ b/glcdskin/parser.h
@@ -16,13 +16,22 @@
#include <string>
+#define GLCDSKIN_SKIN_VERSION 1.1
+
+
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..c4e8e4b 100644
--- a/glcdskin/skin.c
+++ b/glcdskin/skin.c
@@ -79,4 +79,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..b13db0a 100644
--- a/glcdskin/skin.h
+++ b/glcdskin/skin.h
@@ -61,6 +61,16 @@ public:
const tSize & BaseSize(void) const { return baseSize; }
cImageCache * ImageCache(void) { return mImageCache; }
+
+ bool ParseEnable(const std::string &Text);
+
+#ifdef GRAPHLCD_CBITMAP_ARGB
+ cColor GetBackgroundColor(void) { return config.GetDriver()->GetBackgroundColor(); }
+ cColor GetForegroundColor(void) { return config.GetDriver()->GetForegroundColor(); }
+#else
+ eColor GetBackgroundColor(void) { return config.GetDriver()->GetBackgroundColor(); }
+ eColor GetForegroundColor(void) { return config.GetDriver()->GetForegroundColor(); }
+#endif
};
} // end of namespace
diff --git a/glcdskin/string.c b/glcdskin/string.c
index dc9d9dc..90a1670 100644
--- a/glcdskin/string.c
+++ b/glcdskin/string.c
@@ -85,6 +85,13 @@ void cSkinString::Reparse(void)
}
}
+
+// 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)
{
std::string trans = Translate ? mSkin->Config().Translate(Text) : Text;
@@ -93,16 +100,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 = "";
@@ -120,6 +117,29 @@ bool cSkinString::Parse(const std::string & Text, bool Translate)
++ptr;
continue;
}
+ else if (*ptr == '#') {
+ if (inToken) {
+ syslog(LOG_ERR, "ERROR: 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 ++; // # adds one character -> 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");
@@ -179,8 +199,10 @@ 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
{
@@ -214,19 +236,35 @@ bool cSkinString::Parse(const std::string & Text, bool Translate)
cType cSkinString::Evaluate(void) const
{
- std::string result;
+ std::string result_raw = "", result_trans = "";
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]));
+ result_raw.append(mText.c_str() + 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
+ 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);
+ cSkinVariable * variable = mSkin->GetVariable(result_raw.substr(idxstart+1, idxend-idxstart-1));
+ if (variable) {
+ result_trans.append ((std::string) variable->Value());
+ // syslog(LOG_ERR, "string variable %s", trans.c_str());
+ }
+ idxstart = idxend+1;
+ pos = idxstart;
+ }
+ result_trans.append(result_raw.substr(pos));
+ return result_trans;
}
} // end of namespace