summaryrefslogtreecommitdiff
path: root/src/gui_htmlattrtext.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@yahoo.com>2017-06-15 23:20:00 +0200
committerJoel Klinghed <the_jk@yahoo.com>2017-07-22 22:08:54 +0200
commitcb17c3035bbd80bd8ea6718bae4c57cfb2555653 (patch)
treef454181a2f58071f5f2ba4408e7e179838ed3fb4 /src/gui_htmlattrtext.cc
parentface8e0a7d5f530ee3e5e63ab1e3d6ecd497326b (diff)
Initial monitor GUI
Basic monitor functionality, GTK-3.0 and QT5 backends
Diffstat (limited to 'src/gui_htmlattrtext.cc')
-rw-r--r--src/gui_htmlattrtext.cc218
1 files changed, 218 insertions, 0 deletions
diff --git a/src/gui_htmlattrtext.cc b/src/gui_htmlattrtext.cc
new file mode 100644
index 0000000..24380af
--- /dev/null
+++ b/src/gui_htmlattrtext.cc
@@ -0,0 +1,218 @@
+// -*- mode: c++; c-basic-offset: 2; -*-
+
+#include "common.hh"
+
+#include <algorithm>
+#include <deque>
+#include <string.h>
+#include <vector>
+
+#include "gui_htmlattrtext.hh"
+
+namespace {
+
+char const* ESCAPE = "&<>\"\n ";
+
+void append_escaped(std::string* out, char const* in, size_t len) {
+ size_t last = 0;
+ for (size_t i = 0; i < len; ++i) {
+ auto tmp = reinterpret_cast<char const*>(memchr(ESCAPE, in[i], 6));
+ if (!tmp) continue;
+ if (last < i) {
+ out->append(in + last, i - last);
+ }
+ last = i + 1;
+ switch (tmp - ESCAPE) {
+ case 0:
+ out->append("&amp;");
+ break;
+ case 1:
+ out->append("&lt;");
+ break;
+ case 2:
+ out->append("&gt;");
+ break;
+ case 3:
+ out->append("&quot;");
+ break;
+ case 4:
+ out->append("<br>");
+ break;
+ case 5:
+ out->append("&nbsp;");
+ break;
+ }
+ }
+ if (last < len) {
+ out->append(in + last, len - last);
+ }
+}
+
+void color(std::string* out, uint32_t color) {
+ char tmp[30];
+ if (color >> 24 == 0xff) {
+ out->append(tmp, snprintf(tmp, sizeof(tmp), "rgb(%u, %u, %u)",
+ static_cast<unsigned>((color >> 16) & 0xff),
+ static_cast<unsigned>((color >> 8) & 0xff),
+ static_cast<unsigned>(color & 0xff)));
+ } else {
+ out->append(tmp, snprintf(tmp, sizeof(tmp), "rgba(%u, %u, %u, %u)",
+ static_cast<unsigned>((color >> 16) & 0xff),
+ static_cast<unsigned>((color >> 8) & 0xff),
+ static_cast<unsigned>(color & 0xff),
+ static_cast<unsigned>(color >> 24)));
+ }
+}
+
+class HtmlAttributedTextImpl : public HtmlAttributedText {
+public:
+ void append(const char* str, size_t len, Attribute const& attr, size_t start, size_t length) override {
+ if (!str || start >= len) return;
+ length = std::min(len - start, length);
+ if (length == 0) return;
+ auto offset = text_.size();
+ text_.append(str + start, length);
+ if (attr != EMPTY) ranges_.emplace_back(attr, offset, offset + length);
+ }
+
+ void add(Attribute const& attr, size_t start, size_t length) override {
+ if (attr == EMPTY) return;
+ auto end = check_end(start, length);
+ if (end == 0) return;
+ Range r(attr, start, end);
+ auto it = std::lower_bound(ranges_.begin(), ranges_.end(), r);
+ if (it != ranges_.end() && it->start_ == r.start_ && it->end_ == r.end_) {
+ it->attr_.add(r.attr_);
+ return;
+ }
+ ranges_.insert(it, r);
+ }
+ void set(Attribute const& attr, size_t start, size_t length) override {
+ clear(start, length);
+ add(attr, start, length);
+ }
+ void clear(size_t start, size_t length) override {
+ auto end = check_end(start, length);
+ if (end == 0) return;
+ auto it = ranges_.begin();
+ while (it != ranges_.end()) {
+ if (it->start_ >= end) break;
+ if (it->end_ <= start) {
+ ++it;
+ continue;
+ }
+ if (it->start_ == start && it->end_ == end) {
+ it = ranges_.erase(it);
+ continue;
+ }
+ if (start > it->start_ && end < it->end_) {
+ auto const e = it->end_;
+ it->end_ = start;
+ it = ranges_.emplace(it + 1, it->attr_, end, e);
+ } else if (start <= it->start_) {
+ it->start_ = end;
+ } else {
+ assert(end >= it->end_);
+ it->end_ = start;
+ }
+ ++it;
+ }
+ }
+
+ std::string html() const override {
+ std::string ret;
+ std::deque<Range const*> stack;
+ size_t last = 0;
+ for (auto const& range : ranges_) {
+ while (!stack.empty() && stack.front()->end_ <= range.start_) {
+ if (stack.front()->end_ > last) {
+ append_escaped(&ret, text_.data() + last, stack.front()->end_ - last);
+ last = stack.front()->end_;
+ }
+ end_tag(&ret, stack.front()->attr_);
+ stack.pop_front();
+ }
+ if (range.start_ > last) {
+ append_escaped(&ret, text_.data() + last, range.start_ - last);
+ last = range.start_;
+ }
+ start_tag(&ret, range.attr_);
+ auto it = std::lower_bound(stack.begin(), stack.end(), &range, [](Range const* r1, Range const* r2) -> bool {
+ return r1->end_ < r2->end_;
+ });
+ stack.emplace(it, &range);
+ }
+ while (!stack.empty()) {
+ if (stack.front()->end_ > last) {
+ append_escaped(&ret, text_.data() + last, stack.front()->end_ - last);
+ last = stack.front()->end_;
+ }
+ end_tag(&ret, stack.front()->attr_);
+ stack.pop_front();
+ }
+ if (last < text_.size()) {
+ append_escaped(&ret, text_.data() + last, text_.size() - last);
+ }
+ return ret;
+ }
+
+ std::string text() const override {
+ return text_;
+ }
+
+private:
+ struct Range {
+ size_t start_;
+ size_t end_;
+ Attribute attr_;
+
+ Range(Attribute const& attr, size_t start, size_t end)
+ : start_(start), end_(end), attr_(attr) {
+ assert(start_ < end_);
+ }
+
+ bool operator<(Range const& range) const {
+ return start_ < range.start_;
+ }
+ };
+
+ size_t check_end(size_t start, size_t length) const {
+ if (start >= text_.size()) return 0;
+ length = std::min(text_.size() - start, length);
+ if (length == 0) return 0;
+ return start + length;
+ }
+
+ static void start_tag(std::string* out, Attribute const& attr) {
+ out->append("<span style=\"");
+ if (attr.bold()) out->append("font-weight: bold; ");
+ if (attr.italic()) out->append("font-style: italic; ");
+ if (attr.underline()) out->append("text-decoration: underline; ");
+ if (attr.strike()) out->append("text-decoration: line-through; ");
+ if (attr.has_foreground()) {
+ out->append("color: ");
+ color(out, attr.foreground());
+ out->append("; ");
+ }
+ if (attr.has_background()) {
+ out->append("background-color: ");
+ color(out, attr.background());
+ out->append("; ");
+ }
+ out->append("\">");
+ }
+
+ static void end_tag(std::string* out, Attribute const& UNUSED(attr)) {
+ out->append("</span>");
+ }
+
+ std::string text_;
+ std::vector<Range> ranges_;
+};
+
+} // namespace
+
+// static
+HtmlAttributedText* HtmlAttributedText::create() {
+ return new HtmlAttributedTextImpl();
+}