summaryrefslogtreecommitdiff
path: root/src/interface
diff options
context:
space:
mode:
authorSimon Robertshaw <simon@hardwired.org.uk>2012-08-17 15:08:03 (GMT)
committer Simon Robertshaw <simon@hardwired.org.uk>2012-08-17 15:08:03 (GMT)
commit614a90cfc677313473d5a6163d93e61fa655dfca (patch)
treee7d5354f57f6956c9e66e589cff21ac4b99cafe2 /src/interface
parent6500923aa574bad86339f231382a7fcc11e64f96 (diff)
downloadpowder-614a90cfc677313473d5a6163d93e61fa655dfca.zip
powder-614a90cfc677313473d5a6163d93e61fa655dfca.tar.gz
RichText label (used for MOTD), fixes #123
Diffstat (limited to 'src/interface')
-rw-r--r--src/interface/RichLabel.cpp183
-rw-r--r--src/interface/RichLabel.h39
2 files changed, 222 insertions, 0 deletions
diff --git a/src/interface/RichLabel.cpp b/src/interface/RichLabel.cpp
new file mode 100644
index 0000000..894cf9e
--- /dev/null
+++ b/src/interface/RichLabel.cpp
@@ -0,0 +1,183 @@
+#include <vector>
+#include <exception>
+
+#include "RichLabel.h"
+#include "Misc.h"
+#include "interface/Point.h"
+#include "interface/Component.h"
+#include "graphics/Graphics.h"
+
+using namespace ui;
+
+struct RichTextParseException: public std::exception {
+ std::string message;
+public:
+ RichTextParseException(std::string message_ = "Parse error"): message(message_) {}
+ const char * what() const throw()
+ {
+ return message.c_str();
+ }
+ ~RichTextParseException() throw() {};
+};
+
+RichLabel::RichLabel(Point position, Point size, std::string labelText):
+ Component(position, size),
+ textSource(labelText)
+{
+ updateRichText();
+}
+
+RichLabel::~RichLabel()
+{
+
+}
+
+void RichLabel::updateRichText()
+{
+ regions.clear();
+
+ enum State { ReadText, ReadData, ReadRegion, ReadDataStart };
+ State state = ReadText;
+
+ int currentDataPos = 0;
+ char * currentData = new char[textSource.length()+1];
+
+ int finalTextPos = 0;
+ char * finalText = new char[textSource.length()+1];
+
+ int originalTextPos = 0;
+ char * originalText = new char[textSource.length()+1];
+ std::copy(textSource.begin(), textSource.end(), originalText);
+
+ int stackPos = -1;
+ RichTextRegion * regionsStack = new RichTextRegion[256];
+
+ try
+ {
+ while(originalText[originalTextPos])
+ {
+ char current = originalText[originalTextPos];
+
+ if(state == ReadText)
+ {
+ if(current == '{')
+ {
+ if(stackPos > 255)
+ throw RichTextParseException("Too many nested regions");
+ stackPos++;
+ regionsStack[stackPos].start = finalTextPos;
+ regionsStack[stackPos].finish = finalTextPos;
+ state = ReadRegion;
+ }
+ else if(current == '}')
+ {
+ if(stackPos >= 0)
+ {
+ currentData[currentDataPos] = 0;
+ regionsStack[stackPos].actionData = std::string(currentData);
+ regions.push_back(regionsStack[stackPos]);
+ stackPos--;
+ }
+ else
+ {
+ throw RichTextParseException("Unexpected '}'");
+ }
+ }
+ else
+ {
+ finalText[finalTextPos++] = current;
+ if(stackPos >= 0)
+ {
+ regionsStack[stackPos].finish = finalTextPos;
+ }
+ }
+ }
+ else if(state == ReadData)
+ {
+ if(current == '|')
+ {
+ state = ReadText;
+ }
+ else
+ {
+ currentData[currentDataPos++] = current;
+ }
+ }
+ else if(state == ReadDataStart)
+ {
+ if(current != ':')
+ {
+ throw RichTextParseException("Expected ':'");
+ }
+ state = ReadData;
+ currentDataPos = 0;
+ }
+ else if(state == ReadRegion)
+ {
+ if(stackPos >= 0)
+ {
+ regionsStack[stackPos].action = current;
+ state = ReadDataStart;
+ }
+ else
+ {
+ throw RichTextParseException();
+ }
+ }
+
+ originalTextPos++;
+ }
+ finalText[finalTextPos] = 0;
+ displayText = std::string(finalText);
+ }
+ catch (const RichTextParseException & e)
+ {
+ displayText = "[Parse exception: " + std::string(e.what()) + "]";
+ }
+ delete[] currentData;
+ delete[] finalText;
+ delete[] originalText;
+ delete[] regionsStack;
+
+ TextPosition(displayText);
+}
+
+void RichLabel::SetText(std::string text)
+{
+ textSource = text;
+ updateRichText();
+}
+
+std::string RichLabel::GetDisplayText()
+{
+ return displayText;
+}
+
+std::string RichLabel::GetText()
+{
+ return textSource;
+}
+
+void RichLabel::Draw(const Point& screenPos)
+{
+ Graphics * g = ui::Engine::Ref().g;
+ ui::Colour textColour = Appearance.TextInactive;
+ g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, textColour.Red, textColour.Green, textColour.Blue, 255);
+}
+
+void RichLabel::OnMouseClick(int x, int y, unsigned button)
+{
+ int cursorPosition = Graphics::CharIndexAtPosition((char*)displayText.c_str(), x-textPosition.X, y-textPosition.Y);
+ for(std::vector<RichTextRegion>::iterator iter = regions.begin(), end = regions.end(); iter != end; ++iter)
+ {
+ if((*iter).start <= cursorPosition && (*iter).finish >= cursorPosition)
+ {
+ switch((*iter).action)
+ {
+ case 'a':
+ OpenURI((*iter).actionData);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/interface/RichLabel.h b/src/interface/RichLabel.h
new file mode 100644
index 0000000..d9682f2
--- /dev/null
+++ b/src/interface/RichLabel.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <string>
+
+#include "Component.h"
+#include "Colour.h"
+
+namespace ui
+{
+ class RichLabel : public Component
+ {
+ public:
+ struct RichTextRegion
+ {
+ int start;
+ int finish;
+ int action;
+ std::string actionData;
+ };
+
+ RichLabel(Point position, Point size, std::string richText);
+
+ virtual ~RichLabel();
+
+ virtual void SetText(std::string text);
+ virtual std::string GetDisplayText();
+ virtual std::string GetText();
+
+ virtual void Draw(const Point& screenPos);
+ virtual void OnMouseClick(int x, int y, unsigned button);
+ protected:
+ std::string textSource;
+ std::string displayText;
+
+ std::vector<RichTextRegion> regions;
+
+ void updateRichText();
+ };
+}