diff options
author | mrwastl <mrwastl@users.sourceforge.net> | 2011-05-01 22:22:32 +0200 |
---|---|---|
committer | mrwastl <mrwastl@users.sourceforge.net> | 2011-05-01 22:22:32 +0200 |
commit | 46e597df44402086edd010b69702c2de52b75fc8 (patch) | |
tree | fa9528f19f951b765b071c239b09547cf69bd169 /glcdskin | |
parent | 57729cf285b058d192a60bd7fce1b2d29bdd9650 (diff) | |
download | graphlcd-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.c | 11 | ||||
-rw-r--r-- | glcdskin/config.h | 6 | ||||
-rw-r--r-- | glcdskin/display.c | 26 | ||||
-rw-r--r-- | glcdskin/display.h | 4 | ||||
-rw-r--r-- | glcdskin/function.c | 14 | ||||
-rw-r--r-- | glcdskin/function.h | 4 | ||||
-rw-r--r-- | glcdskin/object.c | 502 | ||||
-rw-r--r-- | glcdskin/object.h | 56 | ||||
-rw-r--r-- | glcdskin/parser.c | 185 | ||||
-rw-r--r-- | glcdskin/parser.h | 11 | ||||
-rw-r--r-- | glcdskin/skin.c | 12 | ||||
-rw-r--r-- | glcdskin/skin.h | 10 | ||||
-rw-r--r-- | glcdskin/string.c | 72 |
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 |