summaryrefslogtreecommitdiff
path: root/dwr
diff options
context:
space:
mode:
Diffstat (limited to 'dwr')
-rw-r--r--dwr/Makefile.am31
-rw-r--r--dwr/box.cc258
-rw-r--r--dwr/box.hh110
-rw-r--r--dwr/graph.cc642
-rw-r--r--dwr/graph.hh126
-rw-r--r--dwr/graph2.cc505
-rw-r--r--dwr/graph2.hh122
-rw-r--r--dwr/graph2_iterator.cc139
-rw-r--r--dwr/hbox.cc123
-rw-r--r--dwr/hbox.hh31
-rw-r--r--dwr/hideable.cc104
-rw-r--r--dwr/hideable.hh39
-rw-r--r--dwr/label.cc378
-rw-r--r--dwr/label.hh87
-rw-r--r--dwr/toggle.cc402
-rw-r--r--dwr/toggle.hh77
-rw-r--r--dwr/tools.cc243
-rw-r--r--dwr/tools.hh24
-rw-r--r--dwr/vbox.cc164
-rw-r--r--dwr/vbox.hh35
20 files changed, 3640 insertions, 0 deletions
diff --git a/dwr/Makefile.am b/dwr/Makefile.am
new file mode 100644
index 0000000..afd9925
--- /dev/null
+++ b/dwr/Makefile.am
@@ -0,0 +1,31 @@
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/dwr"'
+
+noinst_LIBRARIES = \
+ libDw-rtfl.a
+
+libDw_rtfl_a_SOURCES = \
+ box.hh \
+ box.cc \
+ graph.hh \
+ graph.cc \
+ hbox.hh \
+ hbox.cc \
+ hideable.hh \
+ hideable.cc \
+ label.hh \
+ label.cc \
+ toggle.hh \
+ toggle.cc \
+ tools.hh \
+ tools.cc \
+ vbox.hh \
+ vbox.cc
+
+if USE_GRAPH2
+libDw_rtfl_a_SOURCES += \
+ graph2.hh \
+ graph2.cc \
+ graph2_iterator.cc
+endif
diff --git a/dwr/box.cc b/dwr/box.cc
new file mode 100644
index 0000000..2e72866
--- /dev/null
+++ b/dwr/box.cc
@@ -0,0 +1,258 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <math.h>
+
+#include "box.hh"
+
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace lout::container::typed;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace dw {
+
+int Box::CLASS_ID = -1;
+
+#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
+
+const char Box::countZeroTable[256] = {
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ LT(3), LT(2), LT(2), LT(1), LT(1), LT(1), LT(1),
+ LT(0), LT(0), LT(0), LT(0), LT(0), LT(0), LT(0), LT(0)
+};
+
+// ----------------------------------------------------------------------
+
+Box::BoxIterator::BoxIterator (Box *box, Content::Type mask,
+ bool atEnd) : Iterator (box, mask, atEnd)
+{
+ index = atEnd ? box->children->size() : -1;
+ content.type = atEnd ? Content::END : Content::START;
+}
+
+Box::BoxIterator::BoxIterator (Box *box, Content::Type mask,
+ int index) : Iterator (box, mask, false)
+{
+ this->index = index;
+
+ if (index < 0)
+ content.type = Content::START;
+ else if (index >= box->children->size ())
+ content.type = Content::END;
+ else {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = box->children->get (index);
+ }
+}
+
+lout::object::Object *Box::BoxIterator::clone ()
+{
+ return new BoxIterator ((Box*)getWidget(), getMask(), index);
+}
+
+int Box::BoxIterator::compareTo (lout::object::Comparable *other)
+{
+ return index - ((BoxIterator*)other)->index;
+}
+
+bool Box::BoxIterator::next ()
+{
+ Box *box = (Box*)getWidget();
+
+ if (content.type == Content::END)
+ return false;
+
+ // boxs only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::END;
+ return false;
+ }
+
+ index++;
+ if (index >= box->children->size ()) {
+ content.type = Content::END;
+ return false;
+ } else {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = box->children->get (index);
+ return true;
+ }
+}
+
+bool Box::BoxIterator::prev ()
+{
+ Box *box = (Box*)getWidget();
+
+ if (content.type == Content::START)
+ return false;
+
+ // boxs only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::START;
+ return false;
+ }
+
+ index--;
+ if (index < 0) {
+ content.type = Content::START;
+ return false;
+ } else {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = box->children->get (index);
+ return true;
+ }
+}
+
+void Box::BoxIterator::highlight (int start, int end, HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void Box::BoxIterator::unhighlight (int direction, HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void Box::BoxIterator::getAllocation (int start, int end,
+ Allocation *allocation)
+{
+ /** \bug Not implemented. */
+}
+
+// ----------------------------------------------------------------------
+
+Box::Box (bool stretchChildren)
+{
+ DBG_OBJ_CREATE ("rtfl::dw::Box");
+ registerName ("rtfl::dw::Box", &CLASS_ID);
+
+ this->stretchChildren = stretchChildren;
+ children = new Vector<Widget> (4, true);
+ inDestructor = false;
+}
+
+Box::~Box ()
+{
+ inDestructor = true;
+ delete children;
+
+ DBG_OBJ_DELETE ();
+}
+
+void Box::sizeRequestImplImpl (Requisition *requisition)
+{
+ actualSizeRequestImplImpl (requisition, 0);
+}
+
+void Box::actualSizeRequestImplImpl (::dw::core::Requisition *requisition,
+ int data1)
+{
+ // "data1" is a value passed from a special implementation of
+ // sizeRequestImplImpl to accumulateSize. See VBox for an example
+ // on usage. Extend by "data2" ... when needed.
+
+ requisition->width = requisition->ascent = requisition->descent = 0;
+
+ for (int i = 0; i < children->size (); i++) {
+ Requisition childReq;
+ children->get(i)->sizeRequest (&childReq);
+ accumulateSize (i, children->size (), requisition, &childReq, data1);
+ }
+
+ requisition->width += getStyle()->boxDiffWidth ();
+ requisition->ascent += getStyle()->boxOffsetY ();
+ requisition->descent += getStyle()->boxRestHeight ();
+}
+
+void Box::getExtremesImplImpl (Extremes *extremes)
+{
+ extremes->minWidth = extremes->maxWidth = 0;
+
+ for (int i = 0; i < children->size (); i++) {
+ Extremes childExtr;
+ children->get(i)->getExtremes (&childExtr);
+ accumulateExtremes (i, children->size (), extremes, &childExtr);
+ }
+
+ extremes->minWidth += getStyle()->boxDiffWidth ();
+ extremes->maxWidth += getStyle()->boxDiffWidth ();
+}
+
+void Box::drawImpl (View *view, Rectangle *area)
+{
+ DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
+ area->x, area->y, area->width, area->height);
+
+ drawWidgetBox (view, area, false);
+ for (int i = 0; i < children->size (); i++) {
+ Widget *child = children->get (i);
+ Rectangle childArea;
+ if (child->intersects (area, &childArea))
+ child->draw (view, &childArea);
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+::dw::core::Iterator *Box::iterator (Content::Type mask, bool atEnd)
+{
+ return new BoxIterator (this, mask, atEnd);
+}
+
+void Box::removeChild (Widget *child)
+{
+ if (!inDestructor) {
+ for (int i = 0; i < children->size (); i++) {
+ if (children->get (i) == child) {
+ children->remove (i);
+ return;
+ }
+ }
+
+ assertNotReached ();
+ }
+}
+
+void Box::addChild (Widget *widget, int newPos)
+{
+ if (newPos == -1)
+ children->put (widget);
+ else
+ children->insert (widget, newPos);
+
+ widget->setParent (this);
+ queueResize (0, true);
+}
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/box.hh b/dwr/box.hh
new file mode 100644
index 0000000..2fe5b2b
--- /dev/null
+++ b/dwr/box.hh
@@ -0,0 +1,110 @@
+#ifndef __DWR_BOX_HH__
+#define __DWR_BOX_HH__
+
+#include "hideable.hh"
+
+namespace rtfl {
+
+namespace dw {
+
+class Box: public Hideable
+{
+private:
+ class BoxIterator: public ::dw::core::Iterator
+ {
+ private:
+ int index;
+
+ BoxIterator (Box *box, ::dw::core::Content::Type mask, int index);
+
+ public:
+ BoxIterator (Box *box, ::dw::core::Content::Type mask, bool atEnd);
+
+ lout::object::Object *clone ();
+ int compareTo (lout::object::Comparable *other);
+
+ bool next ();
+ bool prev ();
+ void highlight (int start, int end, ::dw::core::HighlightLayer layer);
+ void unhighlight (int direction, ::dw::core::HighlightLayer layer);
+ void getAllocation (int start, int end,
+ ::dw::core::Allocation *allocation);
+ };
+
+ bool inDestructor;
+ static const char countZeroTable[256];
+
+ /**
+ * \brief Calculate the number of bits 0 on the left.
+ *
+ * This is similar to calculating the logarithm base 2 (which finds
+ * the right-most bit 1), and a lookup as described at
+ * <http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup>
+ * is used.
+ */
+ static inline int getZeroBits (int i)
+ {
+ // TODO This should not work for 64 bit integers.
+ unsigned int ih, ilh, ihh;
+ if ((ih = i >> 16))
+ return ((ihh = ih >> 8)) ?
+ countZeroTable[ihh] : 8 + countZeroTable[ih];
+ else
+ return ((ilh = i >> 8)) ?
+ 16 + countZeroTable[ilh] : 24 + countZeroTable[i];
+ }
+
+protected:
+ lout::container::typed::Vector<Widget> *children;
+ bool stretchChildren;
+
+ void sizeRequestImplImpl (::dw::core::Requisition *requisition);
+ void actualSizeRequestImplImpl (::dw::core::Requisition *requisition,
+ int data1);
+ void getExtremesImplImpl (::dw::core::Extremes *extremes);
+
+ void drawImpl (::dw::core::View *view, ::dw::core::Rectangle *area);
+
+ /**
+ * \brief Return a * b / c, but avoid overflow for large a and b.
+ */
+ static inline unsigned int safeATimesBDividedByC (unsigned int a,
+ unsigned int b,
+ unsigned int c)
+ {
+ // TODO This should not work for 64 bit integers.
+ int z = getZeroBits (a) + getZeroBits (b);
+ if (z >= 32)
+ return a * b / c;
+ else {
+ //int n = 32 - z, n1 = n / 2, n2 = (n + 1) / 2;
+ int n = 32 - z, n1 = 0, n2 = n;
+ return (a >> n1) * (b >> n2) / (c >> n);
+ }
+ }
+
+ virtual void accumulateSize (int index, int size,
+ ::dw::core::Requisition *totalReq,
+ ::dw::core::Requisition *childReq,
+ int data1) = 0;
+ virtual void accumulateExtremes (int index, int size,
+ ::dw::core::Extremes *totalExtr,
+ ::dw::core::Extremes *childExtr) = 0;
+
+public:
+ static int CLASS_ID;
+
+ Box (bool stretchChildren);
+ ~Box ();
+
+ ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd);
+ void removeChild (Widget *child);
+
+ void addChild (::dw::core::Widget *widget, int newPos = -1);
+};
+
+} // namespace rtfl
+
+} // namespace dw
+
+#endif // __DWR_BOX_HH__
diff --git a/dwr/graph.cc b/dwr/graph.cc
new file mode 100644
index 0000000..d8a1b35
--- /dev/null
+++ b/dwr/graph.cc
@@ -0,0 +1,642 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "graph.hh"
+#include "tools.hh"
+
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace lout::container::typed;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace dw {
+
+int Graph::CLASS_ID = -1;
+
+// ----------------------------------------------------------------------
+
+// TODO: GraphIterator ignores the text nodes (for references). Are
+// they needed, anyway?
+Graph::GraphIterator::GraphIterator (Graph *graph, Content::Type mask,
+ bool atEnd) : Iterator (graph, mask, atEnd)
+{
+ index = atEnd ? graph->allWidgets->size() : -1;
+ content.type = atEnd ? Content::END : Content::START;
+}
+
+Graph::GraphIterator::GraphIterator (Graph *graph, Content::Type mask,
+ int index) : Iterator (graph, mask, false)
+{
+ this->index = index;
+
+ if (index < 0)
+ content.type = Content::START;
+ else if (index >= graph->allWidgets->size ())
+ content.type = Content::END;
+ else {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = graph->allWidgets->get (index);
+ }
+}
+
+lout::object::Object *Graph::GraphIterator::clone ()
+{
+ return new GraphIterator ((Graph*)getWidget(), getMask(), index);
+}
+
+int Graph::GraphIterator::compareTo (lout::object::Comparable *other)
+{
+ return index - ((GraphIterator*)other)->index;
+}
+
+bool Graph::GraphIterator::next ()
+{
+ Graph *graph = (Graph*)getWidget();
+
+ if (content.type == Content::END)
+ return false;
+
+ // graphs only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::END;
+ return false;
+ }
+
+ index++;
+ if (index >= graph->allWidgets->size ()) {
+ content.type = Content::END;
+ return false;
+ } else {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = graph->allWidgets->get (index);
+ return true;
+ }
+}
+
+bool Graph::GraphIterator::prev ()
+{
+ Graph *graph = (Graph*)getWidget();
+
+ if (content.type == Content::START)
+ return false;
+
+ // graphs only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::START;
+ return false;
+ }
+
+ index--;
+ if (index < 0) {
+ content.type = Content::START;
+ return false;
+ } else {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = graph->allWidgets->get (index);
+ return true;
+ }
+}
+
+void Graph::GraphIterator::highlight (int start, int end, HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void Graph::GraphIterator::unhighlight (int direction, HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void Graph::GraphIterator::getAllocation (int start, int end,
+ Allocation *allocation)
+{
+ /** \bug Not implemented. */
+}
+
+// ----------------------------------------------------------------------
+
+Graph::Node::Node (Widget *widget, Node *parent)
+{
+ type = WIDGET;
+ this->widget = widget;
+ this->parent = parent;
+ children = new List<Node> (true);
+}
+
+Graph::Node::Node (const char *text, Node *parent)
+{
+ type = REF;
+ this->text = strdup (text);
+ this->parent = parent;
+ children = new List<Node> (true);
+}
+
+Graph::Node::~Node ()
+{
+ if (type == WIDGET) {
+ if (widget) // see Graph::removeChild
+ delete widget;
+ } else
+ free (text);
+ delete children;
+}
+
+// ----------------------------------------------------------------------
+
+Graph::Graph ()
+{
+ DBG_OBJ_CREATE ("rtfl::dw::Graph");
+ registerName ("rtfl::dw::Graph", &CLASS_ID);
+
+ allWidgets = new Vector<Widget> (4, false);
+ topLevelNodes = new List<Node> (true);
+ refStyle = NULL;
+ pressedRefNode = NULL;
+ inDestructor = false;
+}
+
+Graph::~Graph ()
+{
+ inDestructor = true;
+ delete allWidgets;
+ delete topLevelNodes;
+ if (refStyle)
+ refStyle->unref ();
+
+ DBG_OBJ_DELETE ();
+}
+
+void Graph::sizeRequestImpl (Requisition *requisition)
+{
+ sizeRequest (requisition, topLevelNodes);
+
+ requisition->width += getStyle()->boxDiffWidth ();
+ requisition->ascent += getStyle()->boxOffsetY ();
+ requisition->descent += getStyle()->boxRestHeight ();
+}
+
+void Graph::sizeRequest (Requisition *requisition, List<Node> *list)
+{
+ // For simlicity, descent is set to 0. (Anyway never needed within
+ // RTFL.)
+ requisition->width = requisition->descent = 0;
+ requisition->ascent = max (VDIFF * (list->size () - 1), 0);
+
+ for (lout::container::typed::Iterator<Node> it = list->iterator ();
+ it.hasNext (); ) {
+ Node *node = it.getNext ();
+ Requisition nodeReq;
+ sizeRequest (&nodeReq, node);
+
+ requisition->width = max (requisition->width, nodeReq.width);
+ requisition->ascent += (nodeReq.ascent + nodeReq.descent);
+ }
+}
+
+void Graph::sizeRequest (Requisition *requisition, Node *node)
+{
+ if (node->type == Node::WIDGET)
+ node->widget->sizeRequest (&node->rootReq);
+ else {
+ node->rootReq.width =
+ layout->textWidth (refStyle->font, node->text, strlen (node->text)) +
+ refStyle->boxDiffWidth ();
+ node->rootReq.ascent = refStyle->font->ascent + refStyle->boxOffsetY ();
+ node->rootReq.descent =
+ refStyle->font->descent + refStyle->boxRestHeight ();
+ }
+
+ if (node->children->isEmpty ())
+ *requisition = node->rootReq;
+ else {
+ sizeRequest (&node->childrenReq, node->children);
+
+ requisition->ascent =
+ max (node->rootReq.ascent + node->rootReq.descent,
+ node->childrenReq.ascent + node->childrenReq.descent);
+ requisition->descent = 0;
+ requisition->width =
+ node->rootReq.width + HDIFF + node->childrenReq.width;
+ }
+}
+
+void Graph::getExtremesImpl (Extremes *extremes)
+{
+ getExtremes (extremes, topLevelNodes);
+
+ extremes->minWidth += getStyle()->boxDiffWidth ();
+ extremes->maxWidth += getStyle()->boxDiffWidth ();
+}
+
+void Graph::getExtremes (Extremes *extremes, List<Node> *list)
+{
+ extremes->minWidth = extremes->maxWidth = 0;
+
+ for (lout::container::typed::Iterator<Node> it = list->iterator ();
+ it.hasNext (); ) {
+ Node *node = it.getNext ();
+ Extremes nodeExtr;
+ getExtremes (&nodeExtr, node);
+
+ extremes->minWidth = max (extremes->minWidth, nodeExtr.minWidth);
+ extremes->maxWidth = max (extremes->maxWidth, nodeExtr.maxWidth);
+ }
+}
+
+void Graph::getExtremes (Extremes *extremes, Node *node)
+{
+ Extremes rootExtr;
+ if (node->type == Node::WIDGET)
+ node->widget->getExtremes (&rootExtr);
+ else
+ rootExtr.minWidth = rootExtr.maxWidth =
+ layout->textWidth (refStyle->font, node->text, strlen (node->text)) +
+ refStyle->boxDiffWidth ();
+
+ if (node->children->isEmpty ())
+ *extremes = rootExtr;
+ else {
+ Extremes childrenExtr;
+ getExtremes (&childrenExtr, node->children);
+
+ extremes->minWidth = rootExtr.minWidth + HDIFF + childrenExtr.minWidth;
+ extremes->maxWidth = rootExtr.maxWidth + HDIFF + childrenExtr.maxWidth;
+ }
+}
+
+void Graph::sizeAllocateImpl (Allocation *allocation)
+{
+ // Assumes that sizeRequest has been called before. Only position,
+ // not allocation size, is considered.
+ //printf ("sizeAllocate (%d, %d, ...)\n", allocation->x, allocation->y);
+ sizeAllocate (allocation->x + getStyle()->boxOffsetX (),
+ allocation->y + getStyle()->boxOffsetY (), topLevelNodes);
+}
+
+void Graph::sizeAllocate (int x, int y, List<Node> *list)
+{
+ //printf (" List: sizeAllocate (%d, %d, ...)\n", x, y);
+
+ for (lout::container::typed::Iterator<Node> it = list->iterator ();
+ it.hasNext (); ) {
+ Node *node = it.getNext ();
+ sizeAllocate (x, y, node);
+ int h = node->children->isEmpty () ?
+ node->rootReq.ascent + node->rootReq.descent :
+ max (node->rootReq.ascent + node->rootReq.descent,
+ node->childrenReq.ascent + node->childrenReq.descent);
+ y += (VDIFF + h);
+ }
+}
+
+void Graph::sizeAllocate (int x, int y, Node *node)
+{
+ // Notice that node->childrenReq is undefined when there are no
+ // children.
+
+ node->rootX = x;
+ node->rootY = node->children->isEmpty () ? y :
+ y + max ((node->childrenReq.ascent + node->childrenReq.descent
+ - (node->rootReq.ascent + node->rootReq.descent)) / 2, 0);
+
+
+ if (node->type == Node::WIDGET) {
+ Allocation rootAlloc;
+ rootAlloc.x = node->rootX;
+ rootAlloc.y = node->rootY;
+ rootAlloc.width = node->rootReq.width;
+ rootAlloc.ascent = node->rootReq.ascent;
+ rootAlloc.descent = node->rootReq.descent;
+ node->widget->sizeAllocate (&rootAlloc);
+ }
+
+ if (!node->children->isEmpty ())
+ sizeAllocate (x + HDIFF + node->rootReq.width,
+ y + max ((node->rootReq.ascent + node->rootReq.descent
+ - (node->childrenReq.ascent
+ + node->childrenReq.descent)) / 2, 0),
+ node->children);
+}
+
+void Graph::draw (View *view, Rectangle *area)
+{
+ DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
+ area->x, area->y, area->width, area->height);
+
+ drawWidgetBox (view, area, false);
+ draw (view, area, topLevelNodes);
+
+ DBG_OBJ_LEAVE ();
+}
+
+void Graph::draw (View *view, Rectangle *area, List<Node> *list)
+{
+ for (lout::container::typed::Iterator<Node> it = list->iterator ();
+ it.hasNext (); ) {
+ Node *node = it.getNext ();
+
+ if (node->type == Node::WIDGET) {
+ Rectangle childArea;
+ if (node->widget->intersects (area, &childArea))
+ node->widget->draw (view, &childArea);
+ } else {
+ drawBox (view, refStyle, area, node->rootX, node->rootY,
+ node->rootReq.width,
+ node->rootReq.ascent + node->rootReq.descent, false);
+ view-> drawText (refStyle->font, refStyle->color,
+ Color::SHADING_NORMAL,
+ node->rootX + refStyle->boxOffsetX (),
+ node->rootY + node->rootReq.ascent,
+ node->text, strlen (node->text));
+ }
+
+ drawArrows (view, area, node);
+
+ draw (view, area, node->children);
+ }
+}
+
+void Graph::drawArrows (View *view, Rectangle *area, Node *node)
+{
+ // TODO Regarding "area" could make this faster, especially for
+ // large graphs.
+
+ int x1 = node->rootX + node->rootReq.width;
+ int y1 = node->rootY + (node->rootReq.ascent + node->rootReq.descent) / 2;
+
+ for (lout::container::typed::Iterator<Node> it = node->children->iterator ();
+ it.hasNext (); ) {
+ Node *child = it.getNext ();
+
+ int x2 = child->rootX;
+ int y2 =
+ child->rootY + (child->rootReq.ascent + child->rootReq.descent) / 2;
+
+ view->drawLine (getStyle()->color, Color::SHADING_NORMAL, x1, y1, x2, y2);
+ tools::drawArrowHead (view, getStyle(), x1, y1, x2, y2, AHEADLEN);
+ }
+}
+
+::dw::core::Iterator *Graph::iterator (Content::Type mask, bool atEnd)
+{
+ return new GraphIterator (this, mask, atEnd);
+}
+
+void Graph::removeChild (Widget *child)
+{
+ if (!inDestructor) {
+ Node *node = searchWidget (child);
+ assert (node != NULL);
+ assert (node->type == Node::WIDGET);
+
+ // Otherwise, Node::~Node would delete the widget again:
+ Widget *widget = node->widget;
+ node->widget = NULL;
+
+ // TODO No full implementation, only when all edges have been removed.
+ assert (node->parent == NULL);
+ assert (node->children->isEmpty ());
+
+ bool removed = topLevelNodes->removeRef (node);
+ assert (removed);
+
+ int pos = -1;
+ for (int i = 0; pos == -1 && i < allWidgets->size (); i++) {
+ if (allWidgets->get (i) == widget)
+ pos = i;
+ }
+
+ assert (pos != -1);
+ allWidgets->remove (pos);
+ }
+}
+
+void Graph::setStyle (Style *style)
+{
+ Widget::setStyle (style);
+ if (refStyle == NULL && style != NULL) {
+ refStyle = style;
+ refStyle->ref ();
+ }
+}
+
+Graph::Node *Graph::searchRefNode (MousePositionEvent *event, List<Node> *list)
+{
+ for (lout::container::typed::Iterator<Node> it = list->iterator ();
+ it.hasNext (); ) {
+ Node *node = it.getNext ();
+ if (event->xCanvas >= node->rootX &&
+ event->xCanvas < node->rootX + node->rootReq.width &&
+ event->yCanvas >= node->rootY &&
+ event->yCanvas < node->rootY + node->rootReq.ascent
+ + node->rootReq.descent)
+ return node;
+
+ Node *node2 = searchRefNode (event, node->children);
+ if (node2)
+ return node2;
+ }
+
+ return NULL;
+}
+
+bool Graph::buttonPressImpl (EventButton *event)
+{
+ if (event->button == 1) {
+ pressedRefNode = searchRefNode (event);
+ return pressedRefNode != NULL;
+ } else
+ return false;
+}
+
+bool Graph::buttonReleaseImpl (EventButton *event)
+{
+ if (event->button == 1 && pressedRefNode != NULL) {
+ Node *newRefNode = searchRefNode (event);
+ if (newRefNode == pressedRefNode) {
+ Node *ref = pressedRefNode->reference;
+ scrollTo (HPOS_CENTER, VPOS_CENTER, ref->rootX + getAllocation()->x,
+ ref->rootY + getAllocation()->y, ref->rootReq.width,
+ ref->rootReq.ascent + ref->rootReq.descent);
+ pressedRefNode = NULL;
+ }
+ return true;
+ } else
+ return false;
+}
+
+bool Graph::motionNotifyImpl (::dw::core::EventMotion *event)
+{
+ if ((event->state & BUTTON1_MASK) && pressedRefNode) {
+ Node *newRefNode = searchRefNode (event);
+ if (newRefNode != pressedRefNode)
+ pressedRefNode = NULL;
+ }
+ return false;
+}
+
+void Graph::leaveNotifyImpl (EventCrossing *event)
+{
+ pressedRefNode = NULL;
+}
+
+bool Graph::isAncestorOf (Node *n1, Node *n2)
+{
+ for (Node *node = n2; node; node = node->parent)
+ if (node == n1)
+ return true;
+
+ return false;
+}
+
+void Graph::addReference (Node *from, Node *to)
+{
+ assert (from->type == Node::WIDGET);
+ assert (to->type == Node::WIDGET);
+ //printf ("edge from %s %p to %s %p implemented as reference.\n",
+ // from->widget->getClassName (), from->widget,
+ // to->widget->getClassName (), to->widget);
+
+ Node *ref1 = new Node ("…", from);
+ from->children->append (ref1);
+
+ Node *ref2 = new Node ("…", to);
+ List<Node> *list = to->parent ? to->parent->children : topLevelNodes;
+ list->insertBefore (to, ref2);
+ list->detachRef (to);
+ ref2->children->append (to);
+
+ ref1->reference = ref2;
+ ref2->reference = ref1;
+}
+
+void Graph::addNode (Widget *widget)
+{
+ if (searchWidget (widget))
+ fprintf (stderr, "WARNING: widget %p added twice.\n", widget);
+ else {
+ allWidgets->put (widget);
+ topLevelNodes->append (new Node (widget, NULL));
+ widget->setParent (this);
+ queueResize (0, true);
+ }
+}
+
+void Graph::addEdge (Widget *from, Widget *to)
+{
+ Node *nodeFrom, *nodeTo;
+
+ if ((nodeFrom = searchWidget (from)) == NULL) {
+ fprintf (stderr, "WARNING: adding edge starting from unknown widget %p\n",
+ from);
+ return;
+ }
+
+ if ((nodeTo = searchWidget (to)) == NULL) {
+ fprintf (stderr, "WARNING: adding edge ending in unknown widget %p\n",
+ to);
+ return;
+ }
+
+ if (nodeTo->parent) {
+ if (nodeTo->parent == nodeFrom)
+ fprintf (stderr,
+ "WARNING: edge from widget %p to widget %p added twice.\n",
+ from, to);
+ else {
+ // Second node is already the end of an edge, so no new is
+ // added, but a reference.
+ addReference (nodeFrom, nodeTo);
+ }
+ } else {
+ // Here, nodeTo->parent is NULL.
+ if (isAncestorOf (nodeTo, nodeFrom))
+ // Would cause a cycle otherwise.
+ addReference (nodeFrom, nodeTo);
+ else {
+ topLevelNodes->detachRef (nodeTo);
+ nodeTo->parent = nodeFrom;
+ nodeFrom->children->append (nodeTo);
+ }
+ }
+
+ queueResize (0, true);
+}
+
+void Graph::removeEdge (::dw::core::Widget *from, ::dw::core::Widget *to)
+{
+ Node *nodeFrom, *nodeTo;
+
+ if ((nodeFrom = searchWidget (from)) == NULL) {
+ fprintf (stderr, "WARNING: adding edge starting from unknown widget %p\n",
+ from);
+ return;
+ }
+
+ if ((nodeTo = searchWidget (to)) == NULL) {
+ fprintf (stderr, "WARNING: adding edge ending in unknown widget %p\n",
+ to);
+ return;
+ }
+
+ assertNotReached (); // TODO
+
+ queueResize (0, true);
+}
+
+Graph::Node *Graph::searchWidget (Widget *widget, List<Node> *list)
+{
+ for (lout::container::typed::Iterator<Node> it = list->iterator ();
+ it.hasNext (); ) {
+ Node *node = it.getNext ();
+ if (node->type == Node::WIDGET && node->widget == widget)
+ return node;
+
+ Node *node2 = searchWidget (widget, node->children);
+ if (node2)
+ return node2;
+ }
+
+ return NULL;
+}
+
+void Graph::setRefStyle (::dw::core::style::Style *style)
+{
+ if (refStyle)
+ refStyle->unref ();
+
+ refStyle = style;
+ refStyle->ref ();
+}
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/graph.hh b/dwr/graph.hh
new file mode 100644
index 0000000..7e0897e
--- /dev/null
+++ b/dwr/graph.hh
@@ -0,0 +1,126 @@
+#ifndef __DWR_GRAPH_HH__
+#define __DWR_GRAPH_HH__
+
+#include "dw/core.hh"
+#include "lout/misc.hh"
+
+namespace rtfl {
+
+namespace dw {
+
+class Graph: public ::dw::core::Widget
+{
+private:
+ class GraphIterator: public ::dw::core::Iterator
+ {
+ private:
+ int index;
+
+ GraphIterator (Graph *graph, ::dw::core::Content::Type mask, int index);
+
+ public:
+ GraphIterator (Graph *graph, ::dw::core::Content::Type mask, bool atEnd);
+
+ lout::object::Object *clone ();
+ int compareTo (lout::object::Comparable *other);
+
+ bool next ();
+ bool prev ();
+ void highlight (int start, int end, ::dw::core::HighlightLayer layer);
+ void unhighlight (int direction, ::dw::core::HighlightLayer layer);
+ void getAllocation (int start, int end,
+ ::dw::core::Allocation *allocation);
+ };
+
+ class Node: public lout::object::Object
+ {
+ public:
+ enum { WIDGET, REF } type;
+ union {
+ ::dw::core::Widget *widget;
+ struct {
+ char *text;
+ Node *reference;
+ };
+ };
+ Node *parent;
+ lout::container::typed::List<Node> *children;
+
+ int rootX, rootY;
+ ::dw::core::Requisition rootReq, childrenReq;
+
+ Node (::dw::core::Widget *widget, Node *parent);
+ Node (const char *text, Node *parent);
+ ~Node ();
+ };
+
+ enum { HDIFF = 50, VDIFF = 20, AHEADLEN = 10 };
+
+ lout::container::typed::Vector<Widget> *allWidgets;
+ lout::container::typed::List<Node> *topLevelNodes;
+ ::dw::core::style::Style *refStyle;
+ Node *pressedRefNode;
+ bool inDestructor;
+
+ Node *searchWidget (::dw::core::Widget *widget,
+ lout::container::typed::List<Node> *list);
+ inline Node *searchWidget (::dw::core::Widget *widget)
+ { return searchWidget (widget, topLevelNodes); }
+
+ void sizeRequest (::dw::core::Requisition *requisition,
+ lout::container::typed::List<Node> *list);
+ void sizeRequest (::dw::core::Requisition *requisition, Node *node);
+ void getExtremes (::dw::core::Extremes *extremes,
+ lout::container::typed::List<Node> *list);
+ void getExtremes (::dw::core::Extremes *extremes, Node *node);
+ void sizeAllocate (int x, int y, lout::container::typed::List<Node> *list);
+ void sizeAllocate (int x, int y, Node *node);
+
+ void draw (::dw::core::View *view, ::dw::core::Rectangle *area,
+ lout::container::typed::List<Node> *list);
+ void drawArrows (::dw::core::View *view, ::dw::core::Rectangle *area,
+ Node *node);
+
+ bool isAncestorOf (Node *n1, Node *n2);
+ void addReference (Node *from, Node *to);
+
+ // TODO Not very efficient. Since there are not many ref nodes,
+ // they could be kept in one list.
+ Node *searchRefNode (::dw::core::MousePositionEvent *event)
+ { return searchRefNode (event, topLevelNodes); }
+ Node *searchRefNode (::dw::core::MousePositionEvent *event,
+ lout::container::typed::List<Node> *list);
+
+protected:
+ void sizeRequestImpl (::dw::core::Requisition *requisition);
+ void getExtremesImpl (::dw::core::Extremes *extremes);
+ void sizeAllocateImpl (::dw::core::Allocation *allocation);
+
+ bool buttonPressImpl (::dw::core::EventButton *event);
+ bool buttonReleaseImpl (::dw::core::EventButton *event);
+ bool motionNotifyImpl (::dw::core::EventMotion *event);
+ void leaveNotifyImpl (::dw::core::EventCrossing *event);
+
+public:
+ static int CLASS_ID;
+
+ Graph ();
+ ~Graph ();
+
+ void draw (::dw::core::View *view, ::dw::core::Rectangle *area);
+ ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd);
+ void removeChild (Widget *child);
+ void setStyle (::dw::core::style::Style *style);
+
+ void addNode (::dw::core::Widget *widget);
+ void addEdge (::dw::core::Widget *from, ::dw::core::Widget *to);
+ void removeEdge (::dw::core::Widget *from, ::dw::core::Widget *to);
+
+ void setRefStyle (::dw::core::style::Style *style);
+};
+
+} // namespace rtfl
+
+} // namespace dw
+
+#endif // __DWR_GRAPH_HH__
diff --git a/dwr/graph2.cc b/dwr/graph2.cc
new file mode 100644
index 0000000..28f03f9
--- /dev/null
+++ b/dwr/graph2.cc
@@ -0,0 +1,505 @@
+/*
+ * RTFL
+ *
+ * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* ----------------------------------------------------------------------
+
+ This is an experimenal graph widget which replaces the widget Graph
+ currently used by rtfl-objview. It uses Graphviz for layouting; in
+ detail, pixels and points (1 point = 1/72 of an inch) are treated
+ as identical, for attributes "bb", "pos" etc. ("width" and "height"
+ are given in inch, so these values are calculated by division by
+ 72).
+
+ See <http://www.graphviz.org/doc/info/attrs.html> for informations
+ on Graphviz attributes.
+
+ Open issues:
+
+ - The order of the children should be preserved. When B1, B2 and B3
+ are associated to A, in this order, this order should be visible,
+ even at the expense of other factors.
+
+ (My original idea was to implement this widget from scratch,
+ using a force directed approach with
+
+ (i) invisible edges between the "children" (in the example
+ above: B1 -> B2 and B2 -> B3), which cause attraction of
+ the respective nodes in a similar way the visible nodes do
+ ("springs");
+
+ (ii) a homogeneous force field directed to the right and acting
+ on the heads of the *visible* edges, so giving the graph
+ layout an orientation from left to right;
+
+ (iii) a second homogeneous force field directed to the bottom,
+ which acts only on the heads of the *invisible* edges.
+
+ (i) and (iii) would lead to the desired result.)
+
+ Update: setting "ordering" to "out" is a step into this
+ direction. (This seems to enforce the order of the edges, not the
+ nodes.)
+
+ - Configurability: the algorithm (currently "dot") and some more
+ parameters (like "rankdir=LR") could be made configurable.
+
+ ---------------------------------------------------------------------- */
+
+#include "graph2.hh"
+#include "tools.hh"
+#include "../common/tools.hh"
+#include "../lout/misc.hh"
+
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace lout::container::typed;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace dw {
+
+int Graph2::CLASS_ID = -1;
+
+Graph2::Node::Node (Graph2 *graph, Widget *widget, int index)
+{
+ this->graph = graph;
+ this->widget = widget;
+ this->index = index;
+
+ initAg ();
+}
+
+Graph2::Node::~Node ()
+{
+ if (widget)
+ delete widget;
+
+ cleanupAg ();
+}
+
+void Graph2::Node::initAg ()
+{
+ assert (graph->graph);
+
+ char buf[64];
+ snprintf (buf, 64, "n%d", index);
+ node = agnode(graph->graph, buf, TRUE);
+ agsafeset (node, (char*)"shape", (char*)"rect", (char*)"");
+}
+
+void Graph2::Node::cleanupAg ()
+{
+ if (node) {
+ assert (graph->graph);
+ agdelete (graph->graph, node);
+ node = NULL;
+ }
+}
+
+Graph2::Edge::Edge (Graph2 *graph, Node *from, Node *to, int index)
+{
+ this->graph = graph;
+ this->from = from;
+ this->to = to;
+ this->index = index;
+
+ initAg ();
+
+ numPoints = numPointsAlloc = 0;
+ pointX = NULL;
+ pointY = NULL;
+ pointType = NULL;
+
+ count = 1;
+}
+
+Graph2::Edge::~Edge ()
+{
+ cleanupAg ();
+ cleanupPoints ();
+}
+
+void Graph2::Edge::initAg ()
+{
+ assert (graph->graph);
+
+ char buf[64];
+ snprintf (buf, 64, "e%d", index);
+ edge = agedge (graph->graph, from->node, to->node, (char*)buf, TRUE);
+}
+
+void Graph2::Edge::cleanupAg ()
+{
+ if (edge) {
+ assert (graph->graph);
+ agdelete (graph->graph, edge);
+ edge = NULL;
+ }
+}
+
+void Graph2::Edge::cleanupPoints ()
+{
+ if (numPointsAlloc > 0) {
+ delete[] pointX;
+ delete[] pointY;
+ delete[] pointType;
+
+ pointX = NULL;
+ pointY = NULL;
+ pointType = NULL;
+
+ numPointsAlloc = 0;
+ }
+}
+
+void Graph2::Edge::setNumPoints (int numPoints)
+{
+ if (numPoints > numPointsAlloc) {
+ cleanupPoints ();
+
+ numPointsAlloc = numPoints;
+
+ pointX = new int[numPointsAlloc];
+ pointY = new int[numPointsAlloc];
+ pointType = new char[numPointsAlloc];
+ }
+
+ this->numPoints = numPoints;
+}
+
+void Graph2::Edge::sortPoints ()
+{
+ // If a start point exists, it is at the beginning. If an end point
+ // exists, it is also at the beginning or follows the start point.
+ // Here, it is moved at the end of the list.
+
+ if ((numPoints >= 1 && pointType[0] == 'e') ||
+ (numPoints >= 2 && pointType[1] == 'e')) {
+ int epos = pointType[0] == 'e' ? 0 : 1;
+ int ex = pointX[epos], ey = pointY[epos];
+
+ for (int i = epos; i < numPoints - 1; i++) {
+ pointX[i] = pointX[i + 1];
+ pointY[i] = pointY[i + 1];
+ pointType[i] = pointType[i + 1];
+ }
+
+ pointX[numPoints - 1] = ex;
+ pointY[numPoints - 1] = ey;
+ pointType[numPoints - 1] = 'e';
+ }
+}
+
+Graph2::Graph2 ()
+{
+ DBG_OBJ_CREATE ("rtfl::dw:"###);
+ registerName ("rtfl::dw::Graph2", &CLASS_ID);
+
+ nodes = new Vector<Node> (4, true);
+ edges = new Vector<Edge> (4, true);
+
+ initAg ();
+}
+
+Graph2::~Graph2 ()
+{
+ inDestructor = true;
+
+ cleanupAg ();
+
+ delete nodes;
+ delete edges;
+
+ DBG_OBJ_DELETE ();
+}
+
+void Graph2::initAg ()
+{
+ gvc = gvContext ();
+ graph = agopen ((char*)"", Agdirected, NULL);
+ agsafeset (graph, (char*)"rankdir", (char*)"LR", (char*)"");
+ agsafeset (graph, (char*)"ordering", (char*)"out", (char*)"");
+
+ for (int i = 0; i < nodes->size (); i++)
+ nodes->get(i)->initAg ();
+
+ for (int i = 0; i < edges->size (); i++)
+ edges->get(i)->initAg ();
+}
+
+void Graph2::cleanupAg ()
+{
+ for (int i = 0; i < nodes->size (); i++)
+ nodes->get(i)->cleanupAg ();
+
+ for (int i = 0; i < edges->size (); i++)
+ edges->get(i)->cleanupAg ();
+
+ if (graph) {
+ agclose (graph);
+ graph = NULL;
+ }
+
+ if (gvc) {
+ gvFreeContext(gvc);
+ gvc = NULL;
+ }
+}
+
+void Graph2::sizeRequestImpl (Requisition *requisition)
+{
+ for (int i = 0; i < nodes->size (); i++) {
+ Node *node = nodes->get (i);
+
+ Requisition childReq;
+ node->widget->sizeRequest (&childReq);
+
+ char buf[64];
+ snprintf (buf, 64, "%f", (float)childReq.width / 72);
+ agsafeset (node->node, (char*)"width", buf, (char*)"");
+ snprintf (buf, 64, "%f",
+ (float)(childReq.ascent + childReq.descent) / 72);
+ agsafeset (node->node, (char*)"height", buf, (char*)"");
+ }
+
+ //puts ("---------- before layouting ----------");
+ //agwrite (graph, stdout);
+
+ gvLayout (gvc, graph, "dot");
+ gvRender(gvc, graph, "dot", NULL);
+ gvFreeLayout(gvc, graph);
+
+ //puts ("---------- after layouting ----------");
+ //agwrite (graph, stdout);
+
+ float x, y, width, height;
+ sscanf (agget (graph, (char*)"bb"), "%f,%f,%f,%f", &x, &y, &width, &height);
+
+ requisition->width = width + getStyle()->boxDiffWidth ();
+ requisition->ascent = height + getStyle()->boxOffsetY ();
+ requisition->descent = getStyle()->boxRestHeight ();
+}
+
+void Graph2::getExtremesImpl (Extremes *extremes)
+{
+ assertNotReached ();
+}
+
+void Graph2::sizeAllocateImpl (Allocation *allocation)
+{
+ //agwrite (graph, stdout);
+
+ Requisition req;
+ sizeRequest (&req);
+
+ for (int i = 0; i < nodes->size (); i++) {
+ Node *node = nodes->get (i);
+
+ Requisition childReq;
+ node->widget->sizeRequest (&childReq);
+
+ float x, y;
+ sscanf (agget (node->node, (char*)"pos"), "%f,%f", &x, &y);
+
+ Allocation childAlloc;
+ childAlloc.x = getStyle()->boxOffsetX () + x - childReq.width / 2;
+ childAlloc.y = getStyle()->boxOffsetY ()
+ + (req.ascent + req.descent - getStyle()->boxDiffHeight ())
+ - y - (childReq.ascent + childReq.descent) / 2;
+ childAlloc.width = childReq.width;
+ childAlloc.ascent = childReq.ascent;
+ childAlloc.descent = childReq.descent;
+ node->widget->sizeAllocate (&childAlloc);
+ }
+
+ for (int i = 0; i < edges->size (); i++) {
+ Edge *edge = edges->get (i);
+
+ char *pos = agget (edge->edge, (char*)"pos");
+ int lenPos = strlen (pos);
+ char *posBuf = new char[lenPos + 1];
+ strncpy (posBuf, pos, lenPos + 1);
+
+ int numSpaces = 0;
+ for (char *s = posBuf; *s; s++)
+ if (*s == ' ')
+ numSpaces++;
+
+ edge->setNumPoints (numSpaces + 1);
+
+ int noPoint = 0;
+ bool atEnd = false;
+ for (char *start = posBuf; !atEnd; ) {
+ char *end = start;
+ while (*end != ' ' && *end != 0)
+ end++;
+ atEnd = (*end == 0);
+
+ *end = 0;
+
+ char pointType, *posStart;
+ if ((start[0] == 's' || start[0] == 'e') && start[1] == ',') {
+ pointType = start[0];
+ posStart = start + 2;
+ } else {
+ pointType = 0;
+ posStart = start;
+ }
+
+ float x, y;
+ sscanf (posStart, "%f,%f", &x, &y);
+
+ edge->pointX[noPoint] = getStyle()->boxOffsetX () + x;
+ edge->pointY[noPoint] = getStyle()->boxOffsetY ()
+ + (req.ascent + req.descent - getStyle()->boxDiffHeight ()) - y;
+ edge->pointType[noPoint] = pointType;
+
+ noPoint++;
+ start = end + 1;
+ }
+
+ edge->sortPoints ();
+
+ delete[] posBuf;
+ }
+}
+
+void Graph2::draw (View *view, Rectangle *area)
+{
+ DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
+ area->x, area->y, area->width, area->height);
+
+ drawWidgetBox (view, area, false);
+
+ for (int i = 0; i < nodes->size (); i++) {
+ Widget *widget = nodes->get(i)->widget;
+ Rectangle childArea;
+ if (widget->intersects (area, &childArea))
+ widget->draw (view, &childArea);
+ }
+
+ for (int i = 0; i < edges->size (); i++) {
+ Edge *edge = edges->get (i);
+
+ tools::drawBSpline (view, getStyle(), 4, edge->numPoints, edge->pointX,
+ edge->pointY);
+
+ for (int j = 0; j < edge->numPoints - 1; j++) {
+ // TODO: arrow heads should only be drawn at the first and the last
+ // point. In this case, the direction is correct; in other cases, the
+ // the points are not even part of the curve.
+
+ if (edge->pointType[j] == 'e')
+ tools::drawArrowHead (view, getStyle (),
+ edge->pointX[j + 1], edge->pointY[j + 1],
+ edge->pointX[j], edge->pointY[j],
+ AHEADLEN);
+
+ if (edge->pointType[j + 1] == 'e')
+ tools::drawArrowHead (view, getStyle (),
+ edge->pointX[j], edge->pointY[j],
+ edge->pointX[j + 1], edge->pointY[j + 1],
+ AHEADLEN);
+ }
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+::dw::core::Iterator *Graph2::iterator (Content::Type mask, bool atEnd)
+{
+ return new Graph2Iterator (this, mask, atEnd);
+}
+
+void Graph2::removeChild (Widget *child)
+{
+ if (!inDestructor) {
+ int nodeIndex = searchNodeIndex (child);
+ Node *node = nodes->get (nodeIndex);
+
+ // Otherwise, Node::~Node would delete the widget again:
+ node->widget = NULL;
+
+ for (int i = 0; i < edges->size (); i++) {
+ Edge *edge = edges->get (i);
+ if (edge->from == node || edge->to == node)
+ edges->remove (i);
+ }
+
+ nodes->remove (nodeIndex);
+
+ queueResize (0, true);
+ }
+}
+
+void Graph2::addNode (Widget *widget)
+{
+ Node *node = new Node (this, widget, nodes->size ());
+ nodes->put (node);
+
+ widget->setParent (this);
+
+ queueResize (0, true);
+}
+
+void Graph2::addEdge (Widget *from, Widget *to)
+{
+ for (int i = 0; i < edges->size (); i++) {
+ Edge *edge = edges->get (i);
+ if (edge->from->widget == from && edge->to->widget == to) {
+ edge->count++;
+ printf ("WARNING: Edge already added the %d%s time.\n", edge->count,
+ rtfl::tools::numSuffix (edge->count));
+ return;
+ }
+ }
+
+ Edge *edge =
+ new Edge (this, searchNode (from), searchNode (to), edges->size ());
+ edges->put (edge);
+
+ queueResize (0, true);
+}
+
+int Graph2::searchNodeIndex (Widget *widget)
+{
+ for (int i = 0; i < nodes->size (); i++) {
+ Node *node = nodes->get (i);
+ if (node->widget == widget)
+ return i;
+ }
+
+ assertNotReached ();
+ return 0;
+}
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/graph2.hh b/dwr/graph2.hh
new file mode 100644
index 0000000..128ba32
--- /dev/null
+++ b/dwr/graph2.hh
@@ -0,0 +1,122 @@
+#ifndef __DWR_GRAPH2_HH__
+#define __DWR_GRAPH2_HH__
+
+#include <graphviz/gvc.h>
+
+#include "dw/core.hh"
+
+namespace rtfl {
+
+namespace dw {
+
+class Graph2: public ::dw::core::Widget
+{
+private:
+ class Graph2Iterator: public ::dw::core::Iterator
+ {
+ private:
+ int index;
+
+ Graph2Iterator (Graph2 *graph, ::dw::core::Content::Type mask, int index);
+
+ public:
+ Graph2Iterator (Graph2 *graph, ::dw::core::Content::Type mask,
+ bool atEnd);
+
+ lout::object::Object *clone ();
+ int compareTo (lout::object::Comparable *other);
+
+ bool next ();
+ bool prev ();
+ void highlight (int start, int end, ::dw::core::HighlightLayer layer);
+ void unhighlight (int direction, ::dw::core::HighlightLayer layer);
+ void getAllocation (int start, int end,
+ ::dw::core::Allocation *allocation);
+ };
+
+ class Node: public lout::object::Object
+ {
+ private:
+ Graph2 *graph;
+ int index;
+
+ public:
+ Node (Graph2 *graph, Widget *widget, int index);
+ ~Node ();
+
+ void initAg ();
+ void cleanupAg ();
+
+ Widget *widget;
+ Agnode_t *node;
+ };
+
+ class Edge: public lout::object::Object
+ {
+ private:
+ Graph2 *graph;
+ int numPointsAlloc, index;
+
+ void cleanupPoints ();
+
+ public:
+ Edge (Graph2 *graph, Node *from, Node *to, int index);
+ ~Edge ();
+
+ void initAg ();
+ void cleanupAg ();
+
+ void setNumPoints (int numPoints);
+ void sortPoints ();
+
+ Node *from, *to;
+ Agedge_t *edge;
+ int numPoints;
+ int *pointX, *pointY;
+ char *pointType;
+
+ int count;
+ };
+
+ enum { AHEADLEN = 10 };
+
+ Agraph_t *graph;
+ GVC_t *gvc;
+ lout::container::typed::Vector<Node> *nodes;
+ lout::container::typed::Vector<Edge> *edges;
+ bool inDestructor;
+
+ void initAg ();
+ void cleanupAg ();
+
+ int searchNodeIndex (Widget *widget);
+ inline Node *searchNode (Widget *widget)
+ { return nodes->get (searchNodeIndex (widget)); }
+
+protected:
+ void sizeRequestImpl (::dw::core::Requisition *requisition);
+ void getExtremesImpl (::dw::core::Extremes *extremes);
+ void sizeAllocateImpl (::dw::core::Allocation *allocation);
+
+public:
+ static int CLASS_ID;
+
+ Graph2 ();
+ ~Graph2 ();
+
+ void draw (::dw::core::View *view, ::dw::core::Rectangle *area);
+ ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd);
+ void removeChild (Widget *child);
+
+ inline void setRefStyle (::dw::core::style::Style *style)
+ { /* No need anymore. */ }
+
+ void addNode (Widget *widget);
+ void addEdge (::dw::core::Widget *from, ::dw::core::Widget *to);
+};
+
+} // namespace rtfl
+
+} // namespace dw
+
+#endif // __DWR_GRAPH2_HH__
diff --git a/dwr/graph2_iterator.cc b/dwr/graph2_iterator.cc
new file mode 100644
index 0000000..d546d39
--- /dev/null
+++ b/dwr/graph2_iterator.cc
@@ -0,0 +1,139 @@
+/*
+ * RTFL
+ *
+ * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "graph2.hh"
+
+using namespace dw::core;
+
+namespace rtfl {
+
+namespace dw {
+
+Graph2::Graph2Iterator::Graph2Iterator (Graph2 *graph, Content::Type mask,
+ bool atEnd) :
+ Iterator (graph, mask, atEnd)
+{
+ index = atEnd ? graph->nodes->size() : -1;
+ content.type = atEnd ? Content::END : Content::START;
+}
+
+Graph2::Graph2Iterator::Graph2Iterator (Graph2 *graph, Content::Type mask,
+ int index) :
+ Iterator (graph, mask, false)
+{
+ this->index = index;
+
+ if (index < 0)
+ content.type = Content::START;
+ else if (index >= graph->nodes->size ())
+ content.type = Content::END;
+ else {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = graph->nodes->get(index)->widget;
+ }
+}
+
+lout::object::Object *Graph2::Graph2Iterator::clone ()
+{
+ return new Graph2Iterator ((Graph2*)getWidget(), getMask(), index);
+}
+
+int Graph2::Graph2Iterator::compareTo (lout::object::Comparable *other)
+{
+ return index - ((Graph2Iterator*)other)->index;
+}
+
+bool Graph2::Graph2Iterator::next ()
+{
+ Graph2 *graph = (Graph2*)getWidget();
+
+ if (content.type == Content::END)
+ return false;
+
+ // graphs only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::END;
+ return false;
+ }
+
+ index++;
+ if (index >= graph->nodes->size ()) {
+ content.type = Content::END;
+ return false;
+ } else {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = graph->nodes->get(index)->widget;
+ return true;
+ }
+}
+
+bool Graph2::Graph2Iterator::prev ()
+{
+ Graph2 *graph = (Graph2*)getWidget();
+
+ if (content.type == Content::START)
+ return false;
+
+ // graphs only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::START;
+ return false;
+ }
+
+ index--;
+ if (index < 0) {
+ content.type = Content::START;
+ return false;
+ } else {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = graph->nodes->get(index)->widget;
+ return true;
+ }
+}
+
+void Graph2::Graph2Iterator::highlight (int start, int end,
+ HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void Graph2::Graph2Iterator::unhighlight (int direction, HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void Graph2::Graph2Iterator::getAllocation (int start, int end,
+ Allocation *allocation)
+{
+ /** \bug Not implemented. */
+}
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/hbox.cc b/dwr/hbox.cc
new file mode 100644
index 0000000..6184078
--- /dev/null
+++ b/dwr/hbox.cc
@@ -0,0 +1,123 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hbox.hh"
+
+using namespace dw::core;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace dw {
+
+int HBox::CLASS_ID = -1;
+
+HBox::HBox (bool stretchChildren): Box (stretchChildren)
+{
+ DBG_OBJ_CREATE ("rtfl::dw::HBox");
+ registerName ("rtfl::dw::HBox", &CLASS_ID);
+}
+
+HBox::~HBox ()
+{
+ DBG_OBJ_DELETE ();
+}
+
+void HBox::sizeAllocateImpl (Allocation *allocation)
+{
+ Allocation allocWOBorders;
+ allocWOBorders.x = allocation->x + getStyle()->boxOffsetX ();
+ allocWOBorders.y = allocation->y + getStyle()->boxOffsetY ();
+ allocWOBorders.width = allocation->width - getStyle()->boxDiffWidth ();
+ allocWOBorders.ascent = allocation->ascent - getStyle()->boxOffsetY ();
+ allocWOBorders.descent = allocation->descent - getStyle()->boxRestHeight ();
+
+ int sumReqWidth = 0;
+ for (int i = 0; i < children->size (); i++) {
+ Requisition childReq;
+ children->get(i)->sizeRequest (&childReq);
+ sumReqWidth += childReq.width;
+ }
+
+ bool stretch = stretchChildren && sumReqWidth < allocWOBorders.width;
+
+ // Cf. doc/rounding-errors.doc, with: x[i] = child requisition,
+ // y[i] = child allocation, a = total allocation, b = sumReqWidth.
+ int cumChildReqWidth = 0, cumChildAllocWidth = 0;
+ for (int i = 0; i < children->size (); i++) {
+ Widget *child = children->get (i);
+
+ Requisition childReq;
+ child->sizeRequest (&childReq);
+
+ Allocation childAlloc;
+ childAlloc.x = allocWOBorders.x + cumChildAllocWidth;
+ childAlloc.y = allocWOBorders.y;
+
+ if (stretch) {
+ cumChildReqWidth += childReq.width;
+ childAlloc.width = sumReqWidth > 0 ?
+ safeATimesBDividedByC (cumChildReqWidth, allocWOBorders.width,
+ sumReqWidth) - cumChildAllocWidth :
+ 0;
+ childAlloc.ascent = allocWOBorders.ascent;
+ childAlloc.descent = allocWOBorders.descent;
+ } else
+ childAlloc.width = childReq.width;
+
+ childAlloc.ascent = allocWOBorders.ascent;
+ childAlloc.descent = allocWOBorders.descent;
+ cumChildAllocWidth += childAlloc.width;
+
+ child->sizeAllocate (&childAlloc);
+
+ //printf ("hbox: %dth child at (%d, %d), %d, (%d x %d)\n",
+ // i, childAlloc.x, childAlloc.y, childAlloc.width,
+ // childAlloc.ascent, childAlloc.descent);
+ }
+}
+
+void HBox::accumulateSize (int index, int size, Requisition *totalReq,
+ Requisition *childReq, int data1)
+{
+ totalReq->width += childReq->width;
+ totalReq->ascent = max (totalReq->ascent, childReq->ascent);
+ totalReq->descent = max (totalReq->descent, childReq->descent);
+}
+
+void HBox::accumulateExtremes (int index, int size, Extremes *totalExtr,
+ Extremes *childExtr)
+{
+ totalExtr->minWidth += childExtr->minWidth;
+ totalExtr->maxWidth += childExtr->maxWidth;
+}
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/hbox.hh b/dwr/hbox.hh
new file mode 100644
index 0000000..79b71ff
--- /dev/null
+++ b/dwr/hbox.hh
@@ -0,0 +1,31 @@
+#ifndef __DWR_HBOX_HH__
+#define __DWR_HBOX_HH__
+
+#include "box.hh"
+
+namespace rtfl {
+
+namespace dw {
+
+class HBox: public Box
+{
+protected:
+ void sizeAllocateImpl (::dw::core::Allocation *allocation);
+ void accumulateSize (int index, int size, ::dw::core::Requisition *totalReq,
+ ::dw::core::Requisition *childReq, int data1);
+ void accumulateExtremes (int index, int size,
+ ::dw::core::Extremes *totalExtr,
+ ::dw::core::Extremes *childExtr);
+
+public:
+ static int CLASS_ID;
+
+ HBox (bool stretchChildren);
+ ~HBox ();
+};
+
+} // namespace rtfl
+
+} // namespace dw
+
+#endif // __DWR_HBOX_HH__
diff --git a/dwr/hideable.cc b/dwr/hideable.cc
new file mode 100644
index 0000000..def90a5
--- /dev/null
+++ b/dwr/hideable.cc
@@ -0,0 +1,104 @@
+/*
+ * RTFL
+ *
+ * Copyright 2014, 2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hideable.hh"
+
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace dw {
+
+int Hideable::CLASS_ID = -1;
+
+Hideable::Hideable ()
+{
+ DBG_OBJ_CREATE ("rtfl::dw::Hideable");
+ registerName ("rtfl::dw::Hideable", &CLASS_ID);
+
+ hidden = drawable = false;
+}
+
+Hideable::~Hideable ()
+{
+ DBG_OBJ_DELETE ();
+}
+
+void Hideable::sizeRequestImpl (Requisition *requisition)
+{
+ if (hidden) {
+ requisition->width = requisition->ascent = requisition->descent = 0;
+ drawable = false;
+ } else {
+ sizeRequestImplImpl (requisition);
+ drawable = true;
+ }
+}
+
+void Hideable::getExtremesImpl (Extremes *extremes)
+{
+ if (hidden)
+ extremes->minWidth = extremes->minWidthIntrinsic = extremes->maxWidth =
+ extremes->maxWidthIntrinsic = 0;
+ else
+ getExtremesImplImpl (extremes);
+}
+
+void Hideable::draw (View *view, Rectangle *area)
+{
+ DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
+ area->x, area->y, area->width, area->height);
+
+ if (drawable)
+ drawImpl (view, area);
+
+ DBG_OBJ_LEAVE ();
+}
+
+void Hideable::show ()
+{
+ if (hidden) {
+ hidden = false;
+ queueResize (0, true);
+ }
+}
+
+void Hideable::hide ()
+{
+ if (!hidden) {
+ hidden = true;
+ queueResize (0, true);
+ }
+}
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/hideable.hh b/dwr/hideable.hh
new file mode 100644
index 0000000..aabf807
--- /dev/null
+++ b/dwr/hideable.hh
@@ -0,0 +1,39 @@
+#ifndef __DWR_HIDEABLE_HH__
+#define __DWR_HIDEABLE_HH__
+
+#include "dw/core.hh"
+
+namespace rtfl {
+
+namespace dw {
+
+class Hideable: public ::dw::core::Widget
+{
+ bool hidden, drawable;
+
+protected:
+ void sizeRequestImpl (::dw::core::Requisition *requisition);
+ void getExtremesImpl (::dw::core::Extremes *extremes);
+
+ virtual void sizeRequestImplImpl (::dw::core::Requisition *requisition) = 0;
+ virtual void getExtremesImplImpl (::dw::core::Extremes *extremes) = 0;
+ virtual void drawImpl (::dw::core::View *view, ::dw::core::Rectangle *area)
+ = 0;
+
+public:
+ static int CLASS_ID;
+
+ Hideable ();
+ ~Hideable ();
+
+ void draw (::dw::core::View *view, ::dw::core::Rectangle *area);
+
+ void show ();
+ void hide ();
+};
+
+} // namespace rtfl
+
+} // namespace dw
+
+#endif // __DWR_HIDEABLE_HH__
diff --git a/dwr/label.cc b/dwr/label.cc
new file mode 100644
index 0000000..dfef145
--- /dev/null
+++ b/dwr/label.cc
@@ -0,0 +1,378 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "label.hh"
+
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace dw {
+
+int Label::CLASS_ID = -1;
+
+// ----------------------------------------------------------------------
+
+Label::LabelIterator::LabelIterator (Label *label, Content::Type mask,
+ bool atEnd) : Iterator (label, mask, atEnd)
+{
+ index = atEnd ? label->words->size() : -1;
+ content.type = atEnd ? Content::END : Content::START;
+}
+
+Label::LabelIterator::LabelIterator (Label *label, Content::Type mask,
+ int index) : Iterator (label, mask, false)
+{
+ this->index = index;
+
+ if (index < 0)
+ content.type = Content::START;
+ else if (index >= label->words->size ())
+ content.type = Content::END;
+ else {
+ content.type = Content::TEXT;
+ content.text = label->words->getRef(index)->text;
+ }
+}
+
+lout::object::Object *Label::LabelIterator::clone()
+{
+ return new LabelIterator ((Label*)getWidget(), getMask(), index);
+}
+
+int Label::LabelIterator::compareTo(lout::object::Comparable *other)
+{
+ return index - ((LabelIterator*)other)->index;
+}
+
+bool Label::LabelIterator::next ()
+{
+ Label *label = (Label*)getWidget();
+
+ if (content.type == Content::END)
+ return false;
+
+ // labels only contain widgets:
+ if ((getMask() & Content::TEXT) == 0) {
+ content.type = Content::END;
+ return false;
+ }
+
+ index++;
+ if (index >= label->words->size ()) {
+ content.type = Content::END;
+ return false;
+ } else {
+ content.type = Content::TEXT;
+ content.text = label->words->getRef(index)->text;
+ return true;
+ }
+}
+
+bool Label::LabelIterator::prev ()
+{
+ Label *label = (Label*)getWidget();
+
+ if (content.type == Content::START)
+ return false;
+
+ // labels only contain widgets:
+ if ((getMask() & Content::TEXT) == 0) {
+ content.type = Content::START;
+ return false;
+ }
+
+ index--;
+ if (index < 0) {
+ content.type = Content::START;
+ return false;
+ } else {
+ content.type = Content::TEXT;
+ content.text = label->words->getRef(index)->text;
+ return true;
+ }
+}
+
+void Label::LabelIterator::highlight (int start, int end, HighlightLayer layer)
+{
+ /** \bug Not implemented. */
+}
+
+void Label::LabelIterator::unhighlight (int direction, HighlightLayer layer)
+{
+ /** \bug Not implemented. */
+}
+
+void Label::LabelIterator::getAllocation (int start, int end,
+ Allocation *allocation)
+{
+ /** \bug Not implemented. */
+}
+
+// ----------------------------------------------------------------------
+
+Label::Label (const char *text, int link)
+{
+ DBG_OBJ_CREATE ("rtfl::dw::Label");
+ registerName ("rtfl::dw::Label", &CLASS_ID);
+
+ words = new SimpleVector<Word> (1);
+
+ for (int i = 0; i < 4; i++)
+ styles[i] = NULL;
+
+ selected = buttonDown = false;
+ this->link = link;
+
+ setText (text);
+}
+
+Label::~Label ()
+{
+ clearWords ();
+ delete words;
+
+ clearStyles ();
+
+ DBG_OBJ_DELETE ();
+}
+
+void Label::setText (const char *text)
+{
+ clearWords ();
+
+ DBG_OBJ_SET_STR ("text", text);
+
+ // Parse text for tags <i>, </i>, <b>, </b>. Very simple, no stack.
+ const char *start = text;
+ int styleIndex = 0;
+ while (*start) {
+ const char *end = start;
+
+ while (!(*end == 0 ||
+ strncmp (end, "<i>", 3) == 0 || strncmp (end, "<b>", 3) == 0 ||
+ strncmp (end, "</i>", 4) == 0 || strncmp (end, "</b>", 4) == 0))
+ end++;
+
+ //printf ("start: '%s', end: '%s'\n", start, end);
+
+ if (end > start) {
+ words->increase ();
+ Word *word = words->getLastRef ();
+ word->text = strndup (start, end - start);
+ word->styleIndex = styleIndex;
+
+ //printf (" new word '%s' with attributes %c%c\n", word->text,
+ // (word->styleIndex & ITALIC) ? 'i' : '-',
+ // (word->styleIndex & BOLD) ? 'b' : '-');
+ }
+
+ if (*end == 0)
+ start = end;
+ else if (strncmp (end, "<i>", 3) == 0) {
+ start = end + 3;
+ styleIndex |= ITALIC;
+ } else if (strncmp (end, "<b>", 3) == 0) {
+ start = end + 3;
+ styleIndex |= BOLD;
+ } else if (strncmp (end, "</i>", 4) == 0) {
+ start = end + 4;
+ styleIndex &= ~ITALIC;
+ } else if (strncmp (end, "</b>", 4) == 0) {
+ start = end + 4;
+ styleIndex &= ~BOLD;
+ } else
+ assertNotReached ();
+ }
+
+ queueResize (0, true);
+}
+
+void Label::clearWords ()
+{
+ for (int i = 0; i < words->size (); i++)
+ free (words->getRef(i)->text);
+ words->setSize (0);
+}
+
+void Label::clearStyles ()
+{
+ for (int i = 0; i < 4; i++)
+ if (styles[i]) {
+ styles[i]->unref ();
+ styles[i] = NULL;
+ }
+}
+
+void Label::ensureStyles ()
+{
+ if (getStyle () && styles[0] == NULL) {
+ styles[0] = getStyle ();
+ styles[0]->ref ();
+
+ for (int i = 1; i < 4; i++) {
+ StyleAttrs styleAttrs = *getStyle ();
+ FontAttrs fontAttrs = *(styleAttrs.font);
+ fontAttrs.weight = (i & BOLD) ? 700 : 400;
+ fontAttrs.style = (i & ITALIC) ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL;
+ styleAttrs.font = Font::create (layout, &fontAttrs);
+ styles[i] = Style::create (&styleAttrs);
+ }
+ }
+}
+
+void Label::sizeRequestImplImpl (Requisition *requisition)
+{
+ totalSize.width = totalSize.ascent = totalSize.descent = 0;
+
+ if (getStyle ()) {
+ ensureStyles ();
+
+ for (int i = 0; i < words->size (); i++) {
+ Word *word = words->getRef(i);
+ Font *font = styles[(int)word->styleIndex]->font;
+
+ word->size.width =
+ layout->textWidth (font, word->text, strlen (word->text));
+ word->size.ascent = font->ascent;
+ word->size.descent = font->descent;
+
+ totalSize.width += word->size.width;
+ totalSize.ascent = max (totalSize.ascent, word->size.ascent);
+ totalSize.descent = max (totalSize.descent, word->size.descent);
+ }
+ } else
+ totalSize.width = totalSize.ascent = totalSize.descent = 0;
+
+ requisition->width = totalSize.width + getStyle()->boxDiffWidth ();
+ requisition->ascent = totalSize.ascent + getStyle()->boxOffsetY ();
+ requisition->descent = totalSize.descent + getStyle()->boxRestHeight ();
+}
+
+void Label::getExtremesImplImpl (Extremes *extremes)
+{
+ // Not used within RTFL.
+ assertNotReached ();
+}
+
+void Label::drawImpl (View *view, Rectangle *area)
+{
+ DBG_OBJ_ENTER ("drawImpl", 0, "draw", "%d, %d, %d * %d",
+ area->x, area->y, area->width, area->height);
+
+ drawWidgetBox (view, area, selected);
+
+ if (getStyle ()) {
+ ensureStyles ();
+
+ // Could adhere to style::textAlign. This can be used for
+ // centered text:
+ //
+ // int x = getAllocation()->x
+ // + (getAllocation()->width - totalSize.width) / 2;
+ //
+ // Now, only left-aligned text is supported.
+
+ int x = getAllocation()->x + getStyle()->boxOffsetX ();
+
+ // Same for style::valign:
+ //
+ // int baseY = getAllocation()->y
+ // + (getHeight () - (totalSize.ascent + totalSize.descent)) / 2
+ // + totalSize.ascent;
+
+ int baseY =
+ getAllocation()->y + totalSize.ascent + getStyle()->boxOffsetY ();
+
+ for (int i = 0; i < words->size (); i++) {
+ Word *word = words->getRef(i);
+ view-> drawText (styles[(int)word->styleIndex]->font,
+ styles[(int)word->styleIndex]->color,
+ selected ? Color::SHADING_INVERSE
+ : Color::SHADING_NORMAL,
+ x, baseY, word->text, strlen (word->text));
+ x += word->size.width;
+ }
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+void Label::leaveNotifyImpl (EventCrossing *event)
+{
+ buttonDown = false;
+}
+
+bool Label::buttonPressImpl (EventButton *event)
+{
+ if (link != -1 && event->button == 1) {
+ buttonDown = true;
+ return true;
+ } else
+ return false;
+}
+
+bool Label::buttonReleaseImpl (EventButton *event)
+{
+ if (link != -1 && event->button == 1) {
+ if (buttonDown)
+ linkEmitter.emitClick (this, link, -1, -1, -1, event);
+ return true;
+ } else
+ return false;
+}
+
+Iterator *Label::iterator (Content::Type mask, bool atEnd)
+{
+ return new LabelIterator (this, mask, atEnd);
+}
+
+void Label::setStyle (Style *style)
+{
+ Widget::setStyle (style);
+ clearStyles ();
+}
+
+void Label::select ()
+{
+ selected = true;
+ queueDraw ();
+}
+
+void Label::unselect ()
+{
+ selected = false;
+ queueDraw ();
+}
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/label.hh b/dwr/label.hh
new file mode 100644
index 0000000..8a069b0
--- /dev/null
+++ b/dwr/label.hh
@@ -0,0 +1,87 @@
+#ifndef __DWR_LABEL_HH__
+#define __DWR_LABEL_HH__
+
+#include "dwr/hideable.hh"
+#include "lout/misc.hh"
+
+namespace rtfl {
+
+namespace dw {
+
+class Label: public Hideable
+{
+private:
+ class LabelIterator: public ::dw::core::Iterator
+ {
+ private:
+ int index;
+
+ LabelIterator (Label *label, ::dw::core::Content::Type mask, int index);
+
+ public:
+ LabelIterator (Label *label, ::dw::core::Content::Type mask, bool atEnd);
+
+ lout::object::Object *clone ();
+ int compareTo (lout::object::Comparable *other);
+
+ bool next ();
+ bool prev ();
+ void highlight (int start, int end, ::dw::core::HighlightLayer layer);
+ void unhighlight (int direction, ::dw::core::HighlightLayer layer);
+ void getAllocation (int start, int end,
+ ::dw::core::Allocation *allocation);
+ };
+
+ enum { ITALIC = 2, BOLD = 1 };
+
+ struct Word
+ {
+ char *text;
+ char styleIndex;
+ ::dw::core::Requisition size;
+ };
+
+ ::dw::core::style::Style *styles[4];
+ lout::misc::SimpleVector<Word> *words;
+ ::dw::core::Requisition totalSize;
+ bool selected, buttonDown;
+ int link;
+ ::dw::core::Layout::LinkEmitter linkEmitter;
+
+ void clearWords ();
+ void clearStyles ();
+ void ensureStyles ();
+
+protected:
+ void sizeRequestImplImpl (::dw::core::Requisition *requisition);
+ void getExtremesImplImpl (::dw::core::Extremes *extremes);
+ void drawImpl (::dw::core::View *view, ::dw::core::Rectangle *area);
+
+ bool buttonPressImpl (::dw::core::EventButton *event);
+ bool buttonReleaseImpl (::dw::core::EventButton *event);
+ void leaveNotifyImpl (::dw::core::EventCrossing *event);
+
+public:
+ static int CLASS_ID;
+
+ Label (const char *text, int link = -1);
+ ~Label ();
+
+ void setText (const char *text);
+ inline void setLink (int link) { this->link = link; }
+
+ ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd);
+ void setStyle (::dw::core::style::Style *style);
+
+ void select ();
+ void unselect ();
+
+ inline void connectLink (::dw::core::Layout::LinkReceiver *receiver)
+ { linkEmitter.connectLink (receiver); }
+};
+
+} // namespace rtfl
+
+} // namespace dw
+
+#endif // __DWR_LABEL_HH__
diff --git a/dwr/toggle.cc b/dwr/toggle.cc
new file mode 100644
index 0000000..a400237
--- /dev/null
+++ b/dwr/toggle.cc
@@ -0,0 +1,402 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <math.h>
+
+#include "toggle.hh"
+
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace dw {
+
+int Toggle::CLASS_ID = -1;
+
+Toggle::ButtonStyle Toggle::buttonStyle = PLUS_MINUS;
+
+// ----------------------------------------------------------------------
+
+Toggle::ToggleIterator::ToggleIterator (Toggle *toggle, Content::Type mask,
+ bool atEnd) :
+ Iterator (toggle, mask, atEnd)
+{
+ content.type = atEnd ? Content::END : Content::START;
+}
+
+lout::object::Object *Toggle::ToggleIterator::clone ()
+{
+ ToggleIterator *ti =
+ new ToggleIterator ((Toggle*)getWidget(), getMask(), false);
+ ti->content = content;
+ return ti;
+}
+
+int Toggle::ToggleIterator::index ()
+{
+ switch (content.type) {
+ case Content::START:
+ return 0;
+ case Content::WIDGET_IN_FLOW:
+ return content.widget == ((Toggle*)getWidget())->small ? 1 : 2;
+ case Content::END:
+ return 3;
+ default:
+ assertNotReached ();
+ return 0;
+ }
+}
+
+int Toggle::ToggleIterator::compareTo (lout::object::Comparable *other)
+{
+ return index () - ((ToggleIterator*)other)->index ();
+}
+
+bool Toggle::ToggleIterator::next ()
+{
+ Toggle *toggle = (Toggle*)getWidget();
+
+ if (content.type == Content::END)
+ return false;
+
+ // toggles only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::END;
+ return false;
+ }
+
+ if (content.type == Content::START) {
+ if (toggle->small != NULL) {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = toggle->small;
+ return true;
+ } else if (toggle->large != NULL) {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = toggle->large;
+ return true;
+ } else {
+ content.type = Content::END;
+ return false;
+ }
+ } else /* if (content.type == Content::WIDGET) */ {
+ if (content.widget == toggle->small && toggle->large != NULL) {
+ content.widget = toggle->large;
+ return true;
+ } else {
+ content.type = Content::END;
+ return false;
+ }
+ }
+}
+
+bool Toggle::ToggleIterator::prev ()
+{
+ Toggle *toggle = (Toggle*)getWidget();
+
+ if (content.type == Content::START)
+ return false;
+
+ // toggles only contain widgets:
+ if ((getMask() & Content::WIDGET_IN_FLOW) == 0) {
+ content.type = Content::START;
+ return false;
+ }
+
+ if (content.type == Content::END) {
+ if (toggle->large != NULL) {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = toggle->large;
+ return true;
+ } else if (toggle->small != NULL) {
+ content.type = Content::WIDGET_IN_FLOW;
+ content.widget = toggle->small;
+ return true;
+ } else {
+ content.type = Content::START;
+ return false;
+ }
+ } else /* if (content.type == Content::WIDGET) */ {
+ if (content.widget == toggle->large && toggle->small != NULL) {
+ content.widget = toggle->small;
+ return true;
+ } else {
+ content.type = Content::START;
+ return false;
+ }
+ }
+}
+
+void Toggle::ToggleIterator::highlight (int start, int end, HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void Toggle::ToggleIterator::unhighlight (int direction, HighlightLayer layer)
+{
+ /** todo Needs this an implementation? */
+}
+
+void Toggle::ToggleIterator::getAllocation (int start, int end,
+ Allocation *allocation)
+{
+ /** \bug Not implemented. */
+}
+
+// ----------------------------------------------------------------------
+
+Toggle::Toggle (bool showLarge)
+{
+ DBG_OBJ_CREATE ("rtfl::dw::Toggle");
+ registerName ("rtfl::dw::Toggle", &CLASS_ID);
+
+ this->showLarge = showLarge;
+ small = large = NULL;
+ buttonDown = false;
+}
+
+Toggle::~Toggle ()
+{
+ if (small)
+ delete small;
+ if (large)
+ delete large;
+
+ DBG_OBJ_DELETE ();
+}
+
+void Toggle::sizeRequestImplImpl (Requisition *requisition)
+{
+ Widget *child = showLarge ? large : small;
+ Requisition childReq;
+ if (child)
+ child->sizeRequest (&childReq);
+ else
+ childReq.width = childReq.ascent = childReq.descent = 0;
+
+ int buttonSize = calcButtonSize ();
+ requisition->width =
+ buttonSize + childReq.width + getStyle()->boxDiffWidth ();
+ requisition->ascent =
+ max (buttonSize, childReq.ascent) + getStyle()->boxOffsetY ();
+ requisition->descent = childReq.descent + getStyle()->boxRestHeight ();
+}
+
+
+void Toggle::getExtremesImplImpl (Extremes *extremes)
+{
+ Widget *child = showLarge ? large : small;
+ Extremes childExtr;
+ if (child)
+ child->getExtremes (&childExtr);
+ else
+ childExtr.minWidth = childExtr.maxWidth = 0;
+
+ int buttonSize = calcButtonSize ();
+ extremes->minWidth =
+ buttonSize + childExtr.minWidth + getStyle()->boxDiffWidth ();
+ extremes->maxWidth =
+ buttonSize + childExtr.maxWidth + getStyle()->boxDiffWidth ();
+}
+
+
+void Toggle::sizeAllocateImpl (Allocation *allocation)
+{
+ Widget *visChild = showLarge ? large : small;
+ Widget *invisChild = showLarge ? small : large;
+ Allocation childAlloc;
+ int buttonSize = calcButtonSize ();
+
+ if (visChild) {
+ childAlloc.x = allocation->x + buttonSize + getStyle()->boxOffsetX ();
+ childAlloc.y = allocation->y + getStyle()->boxOffsetY ();
+ childAlloc.width =
+ allocation->width - buttonSize - getStyle()->boxDiffWidth ();
+ childAlloc.ascent = allocation->ascent - getStyle()->boxOffsetY ();
+ childAlloc.descent = allocation->descent - getStyle()->boxRestHeight ();
+ visChild->sizeAllocate (&childAlloc);
+ }
+
+ //printf ("toggle: visible child at (%d, %d), %d, (%d x %d)\n",
+ // childAlloc.x, childAlloc.y, childAlloc.width,
+ // childAlloc.ascent, childAlloc.descent);
+
+ if (invisChild) {
+ // Zero size is used to hide a widget.
+ childAlloc.width = childAlloc.ascent = childAlloc.descent = 0;
+ invisChild->sizeAllocate (&childAlloc);
+ }
+}
+
+void Toggle::drawImpl (View *view, Rectangle *area)
+{
+ DBG_OBJ_ENTER ("draw", 0, "draw", "%d, %d, %d * %d",
+ area->x, area->y, area->width, area->height);
+
+ drawWidgetBox (view, area, false);
+ Widget *child = showLarge ? large : small;
+ Rectangle childArea;
+ if (child && child->intersects (area, &childArea))
+ child->draw (view, &childArea);
+
+ int buttonSize = calcButtonSize ();
+ int x0 = getAllocation()->x + getStyle()->boxOffsetX ();
+ int y0 = getAllocation()->y + getAllocation()->ascent - buttonSize;
+
+ switch (buttonStyle) {
+ case PLUS_MINUS:
+ view->drawLine (getStyle()->color, Color::SHADING_NORMAL,
+ x0 + 1, y0 + buttonSize / 2,
+ x0 + buttonSize - 2, y0 + buttonSize / 2);
+ if (!showLarge)
+ view->drawLine (getStyle()->color, Color::SHADING_NORMAL,
+ x0 + buttonSize / 2, y0 + 1,
+ x0 + buttonSize / 2, y0 + buttonSize - 2);
+ break;
+
+ case TRIANGLE:
+ if (showLarge) {
+ view->drawLine (getStyle()->color, Color::SHADING_NORMAL,
+ x0 + 1, y0 + 1, x0 + buttonSize - 2, y0 + 1);
+ view->drawLine (getStyle()->color, Color::SHADING_NORMAL,
+ x0 + 1, y0 + 1,
+ x0 + buttonSize / 2, y0 + buttonSize - 2);
+ view->drawLine (getStyle()->color, Color::SHADING_NORMAL,
+ x0 + buttonSize - 2, y0 + 1,
+ x0 + buttonSize / 2, y0 + buttonSize - 2);
+ } else {
+ view->drawLine (getStyle()->color, Color::SHADING_NORMAL,
+ x0 + 1, y0 + 1, x0 + 1, y0 + buttonSize - 2);
+ view->drawLine (getStyle()->color, Color::SHADING_NORMAL,
+ x0 + 1, y0 + 1,
+ x0 + buttonSize - 2, y0 + buttonSize / 2);
+ view->drawLine (getStyle()->color, Color::SHADING_NORMAL,
+ x0 + 1, y0 + buttonSize - 2,
+ x0 + buttonSize - 2, y0 + buttonSize / 2);
+ }
+ break;
+ }
+
+ DBG_OBJ_LEAVE ();
+}
+
+::dw::core::Iterator *Toggle::iterator (Content::Type mask, bool atEnd)
+{
+ return new ToggleIterator (this, mask, atEnd);
+}
+
+bool Toggle::insideButton (MousePositionEvent *event)
+{
+ int buttonSize = calcButtonSize ();
+ return
+ event->xWidget >= getStyle()->boxOffsetX () &&
+ event->xWidget < getStyle()->boxOffsetX () + buttonSize &&
+ event->yWidget >= getAllocation()->ascent - buttonSize &&
+ event->yWidget < getAllocation()->ascent;
+}
+
+bool Toggle::buttonPressImpl (EventButton *event)
+{
+ if (event->button == 1 && insideButton (event)) {
+ buttonDown = true;
+ return true;
+ } else
+ return false;
+}
+
+bool Toggle::buttonReleaseImpl (EventButton *event)
+{
+ if (event->button == 1 && insideButton (event)) {
+ if (buttonDown) {
+ showLarge = !showLarge;
+ queueResize (0, true);
+ }
+ return true;
+ } else
+ return false;
+}
+
+bool Toggle::motionNotifyImpl (::dw::core::EventMotion *event)
+{
+ if ((event->state & BUTTON1_MASK) && !insideButton (event))
+ buttonDown = false;
+ return false;
+}
+
+void Toggle::leaveNotifyImpl (EventCrossing *event)
+{
+ buttonDown = false;
+}
+
+void Toggle::removeChild (Widget *child)
+{
+ if (child == small)
+ small = NULL;
+ else if (child == large)
+ large = NULL;
+ else
+ assertNotReached ();
+}
+
+void Toggle::setSmall (Widget *widget)
+{
+ if (small)
+ delete small;
+
+ small = widget;
+ if (small)
+ small->setParent (this);
+
+ if (!showLarge)
+ queueResize (0, true);
+}
+
+void Toggle::setLarge (Widget *widget)
+{
+ if (large)
+ delete large;
+
+ large = widget;
+ if (large)
+ large->setParent (this);
+
+ if (showLarge)
+ queueResize (0, true);
+}
+
+void Toggle::toggle (bool showLarge)
+{
+ this->showLarge = showLarge;
+ queueResize (0, true);
+}
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/toggle.hh b/dwr/toggle.hh
new file mode 100644
index 0000000..8962ade
--- /dev/null
+++ b/dwr/toggle.hh
@@ -0,0 +1,77 @@
+#ifndef __DWR_TOGGLE_HH__
+#define __DWR_TOGGLE_HH__
+
+#include "hideable.hh"
+
+namespace rtfl {
+
+namespace dw {
+
+class Toggle: public Hideable
+{
+private:
+ class ToggleIterator: public ::dw::core::Iterator
+ {
+ private:
+ int index ();
+
+ public:
+ ToggleIterator (Toggle *toggle, ::dw::core::Content::Type mask,
+ bool atEnd);
+
+ lout::object::Object *clone ();
+ int compareTo (lout::object::Comparable *other);
+
+ bool next ();
+ bool prev ();
+ void highlight (int start, int end, ::dw::core::HighlightLayer layer);
+ void unhighlight (int direction, ::dw::core::HighlightLayer layer);
+ void getAllocation (int start, int end,
+ ::dw::core::Allocation *allocation);
+ };
+
+ static enum ButtonStyle { PLUS_MINUS, TRIANGLE } buttonStyle;
+
+ bool showLarge, buttonDown;
+ Widget *small, *large;
+
+ bool insideButton (::dw::core::MousePositionEvent *event);
+ inline int calcButtonSize ()
+ { // Always return an odd number.
+ int s = getStyle()->font->ascent; return (s % 2) ? s : s - 1; }
+
+protected:
+ void sizeRequestImplImpl (::dw::core::Requisition *requisition);
+ void getExtremesImplImpl (::dw::core::Extremes *extremes);
+ void sizeAllocateImpl (::dw::core::Allocation *allocation);
+
+ void drawImpl (::dw::core::View *view, ::dw::core::Rectangle *area);
+
+ bool buttonPressImpl (::dw::core::EventButton *event);
+ bool buttonReleaseImpl (::dw::core::EventButton *event);
+ bool motionNotifyImpl (::dw::core::EventMotion *event);
+ void leaveNotifyImpl (::dw::core::EventCrossing *event);
+
+public:
+ static int CLASS_ID;
+
+ Toggle (bool showLarge);
+ ~Toggle ();
+
+ ::dw::core::Iterator *iterator (::dw::core::Content::Type mask, bool atEnd);
+ void removeChild (Widget *child);
+
+ inline ::dw::core::Widget *getSmall () { return small; }
+ inline ::dw::core::Widget *getLarge () { return large; }
+ void setSmall (::dw::core::Widget *widget);
+ void setLarge (::dw::core::Widget *widget);
+
+ inline bool isLargeShown () { return showLarge; }
+ void toggle (bool showLarge);
+};
+
+} // namespace rtfl
+
+} // namespace dw
+
+#endif // __DWR_TOGGLE_HH__
diff --git a/dwr/tools.cc b/dwr/tools.cc
new file mode 100644
index 0000000..d45ebd3
--- /dev/null
+++ b/dwr/tools.cc
@@ -0,0 +1,243 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ----------------------------------------------------------------------
+ *
+ * A part of the code was written by Keith Vertanen and has been
+ * released to the public domain; see below.
+ */
+
+#include "tools.hh"
+
+#include <math.h>
+
+using namespace dw::core;
+using namespace dw::core::style;
+
+namespace rtfl {
+
+namespace dw {
+
+namespace tools {
+
+// Used for b-splines, see below.
+struct point {
+ double x;
+ double y;
+ double z;
+};
+
+static void bspline (int n, int t, point *control, point *output,
+ int num_output);
+
+void drawArrowHead (View *view, Style *style,
+ int x1, int y1, int x2, int y2, int aheadlen)
+{
+ if (x1 != x2 || y1 != y2) {
+ // TODO: Use faster algorithm avoding floating point numbers. Also,
+ // regard that using integers could cause overflow errors.
+
+ int l = sqrt ((double)(x2 - x1) * (double)(x2 - x1) +
+ (double)(y2 - y1) * (double)(y2 - y1));
+ int x3 = (aheadlen * x1 + (l - aheadlen) * x2) / l;
+ int y3 = (aheadlen * y1 + (l - aheadlen) * y2) / l;
+
+ int x4 = x3 - (y2 - y3) / 2;
+ int y4 = y3 + (x2 - x3) / 2;
+ view->drawLine (style->color, Color::SHADING_NORMAL, x2, y2, x4, y4);
+
+ int x5 = x3 + (y2 - y3) / 2;
+ int y5 = y3 - (x2 - x3) / 2;
+ view->drawLine (style->color, Color::SHADING_NORMAL, x2, y2, x5, y5);
+ }
+}
+
+void drawBSpline (::dw::core::View *view, ::dw::core::style::Style *style,
+ int degree, int numPoints, int *x, int *y)
+{
+ point *in = new point[numPoints];
+ for (int i = 0; i < numPoints; i++) {
+ in[i].x = x[i];
+ in[i].y = y[i];
+ in[i].z = 0;
+ }
+
+ int numOut = 5 * numPoints;
+ point *out = new point[numOut];
+
+ bspline(numPoints - 1, degree, in, out, numOut);
+
+ for (int i = 0; i < numOut - 1; i++)
+ view->drawLine (style->color, ::dw::core::style::Color::SHADING_NORMAL,
+ out[i].x, out[i].y,
+ out[i + 1].x, out[i + 1].y);
+
+ delete[] in;
+ delete[] out;
+}
+
+/* ----------------------------------------------------------------------
+ The following code was copied from
+ <ftp://ftp.grnet.gr/pub/lang/algorithms/c++/bspline.cpp>.
+
+ It should be modified so that it is better adapted to our needs
+ (no z coordinate, no unnecessary conversion between int and
+ double, etc.)
+ ---------------------------------------------------------------------- */
+
+/*********************************************************************
+
+ Simple b-spline curve algorithm
+
+ Copyright 1994 by Keith Vertanen (vertankd@cda.mrs.umn.edu)
+
+ Released to the public domain (your mileage may vary)
+
+**********************************************************************/
+
+static void compute_intervals(int *u, int n, int t);
+static double blend(int k, int t, int *u, double v);
+static void compute_point(int *u, int n, int t, double v, point *control,
+ point *output);
+
+void bspline(int n, int t, point *control, point *output, int num_output)
+
+/*********************************************************************
+
+Parameters:
+ n - the number of control points minus 1
+ t - the degree of the polynomial plus 1
+ control - control point array made up of point stucture
+ output - array in which the calculate spline points are to be put
+ num_output - how many points on the spline are to be calculated
+
+Pre-conditions:
+ n+2>t (no curve results if n+2<=t)
+ control array contains the number of points specified by n
+ output array is the proper size to hold num_output point structures
+
+
+**********************************************************************/
+
+{
+ int *u;
+ double increment,interval;
+ point calcxyz;
+ int output_index;
+
+ u=new int[n+t+1];
+ compute_intervals(u, n, t);
+
+ increment=(double) (n-t+2)/(num_output-1); // how much parameter goes up each time
+ interval=0;
+
+ for (output_index=0; output_index<num_output-1; output_index++)
+ {
+ compute_point(u, n, t, interval, control, &calcxyz);
+ output[output_index].x = calcxyz.x;
+ output[output_index].y = calcxyz.y;
+ output[output_index].z = calcxyz.z;
+ interval=interval+increment; // increment our parameter
+ }
+ output[num_output-1].x=control[n].x; // put in the last point
+ output[num_output-1].y=control[n].y;
+ output[num_output-1].z=control[n].z;
+
+ delete u;
+}
+
+double blend(int k, int t, int *u, double v) // calculate the blending value
+{
+ double value;
+
+ if (t==1) // base case for the recursion
+ {
+ if ((u[k]<=v) && (v<u[k+1]))
+ value=1;
+ else
+ value=0;
+ }
+ else
+ {
+ if ((u[k+t-1]==u[k]) && (u[k+t]==u[k+1])) // check for divide by zero
+ value = 0;
+ else
+ if (u[k+t-1]==u[k]) // if a term's denominator is zero,use just the other
+ value = (u[k+t] - v) / (u[k+t] - u[k+1]) * blend(k+1, t-1, u, v);
+ else
+ if (u[k+t]==u[k+1])
+ value = (v - u[k]) / (u[k+t-1] - u[k]) * blend(k, t-1, u, v);
+ else
+ value = (v - u[k]) / (u[k+t-1] - u[k]) * blend(k, t-1, u, v) +
+ (u[k+t] - v) / (u[k+t] - u[k+1]) * blend(k+1, t-1, u, v);
+ }
+ return value;
+}
+
+void compute_intervals(int *u, int n, int t) // figure out the knots
+{
+ int j;
+
+ for (j=0; j<=n+t; j++)
+ {
+ if (j<t)
+ u[j]=0;
+ else
+ if ((t<=j) && (j<=n))
+ u[j]=j-t+1;
+ else
+ if (j>n)
+ u[j]=n-t+2; // if n-t=-2 then we're screwed, everything goes to 0
+ }
+}
+
+void compute_point(int *u, int n, int t, double v, point *control,
+ point *output)
+{
+ int k;
+ double temp;
+
+ // initialize the variables that will hold our outputted point
+ output->x=0;
+ output->y=0;
+ output->z=0;
+
+ for (k=0; k<=n; k++)
+ {
+ temp = blend(k,t,u,v); // same blend is used for each dimension coordinate
+ output->x = output->x + (control[k]).x * temp;
+ output->y = output->y + (control[k]).y * temp;
+ output->z = output->z + (control[k]).z * temp;
+ }
+}
+
+} // namespace tools
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/tools.hh b/dwr/tools.hh
new file mode 100644
index 0000000..8e7bb2b
--- /dev/null
+++ b/dwr/tools.hh
@@ -0,0 +1,24 @@
+#ifndef __DWR_TOOLS_HH__
+#define __DWR_TOOLS_HH__
+
+#include "dw/core.hh"
+
+namespace rtfl {
+
+namespace dw {
+
+namespace tools {
+
+void drawArrowHead (::dw::core::View *view, ::dw::core::style::Style *style,
+ int x1, int y1, int x2, int y2, int aheadlen);
+
+void drawBSpline (::dw::core::View *view, ::dw::core::style::Style *style,
+ int degree, int numPoints, int *x, int *y);
+
+} // namespace tools
+
+} // namespace rtfl
+
+} // namespace dw
+
+#endif // __DWR_TOOLS_HH__
diff --git a/dwr/vbox.cc b/dwr/vbox.cc
new file mode 100644
index 0000000..6649488
--- /dev/null
+++ b/dwr/vbox.cc
@@ -0,0 +1,164 @@
+/*
+ * RTFL
+ *
+ * Copyright 2013-2015 Sebastian Geerken <sgeerken@dillo.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version; with the following exception:
+ *
+ * The copyright holders of RTFL give you permission to link this file
+ * statically or dynamically against all versions of the graphviz
+ * library, which are published by AT&T Corp. under one of the following
+ * licenses:
+ *
+ * - Common Public License version 1.0 as published by International
+ * Business Machines Corporation (IBM), or
+ * - Eclipse Public License version 1.0 as published by the Eclipse
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "vbox.hh"
+#include "lout/misc.hh"
+
+using namespace dw::core;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace dw {
+
+int VBox::CLASS_ID = -1;
+
+VBox::VBox (bool stretchChildren): Box (stretchChildren)
+{
+ DBG_OBJ_CREATE ("rtfl::dw::VBox");
+ registerName ("rtfl::dw::VBox", &CLASS_ID);
+}
+
+VBox::~VBox ()
+{
+ DBG_OBJ_DELETE ();
+}
+
+int VBox::findFirstVisibleChild ()
+{
+ for (int i = 0; i < children->size (); i++) {
+ Requisition childReq;
+ children->get(i)->sizeRequest (&childReq);
+ if (childReq.ascent > 0 || childReq.descent > 0)
+ return i;
+ }
+
+ return 0;
+}
+
+void VBox::sizeRequestImplImpl (Requisition *requisition)
+{
+ actualSizeRequestImplImpl (requisition, findFirstVisibleChild ());
+}
+
+void VBox::sizeAllocateImpl (Allocation *allocation)
+{
+ // TODO "stretchChildren" is not regarded.
+
+ Allocation allocWOBorders;
+ allocWOBorders.x = allocation->x + getStyle()->boxOffsetX ();
+ allocWOBorders.y = allocation->y + getStyle()->boxOffsetY ();
+ allocWOBorders.width = allocation->width - getStyle()->boxDiffWidth ();
+ allocWOBorders.ascent = allocation->ascent - getStyle()->boxOffsetY ();
+ allocWOBorders.descent = allocation->descent - getStyle()->boxRestHeight ();
+
+ // Unlike HBox, the ascent of the first visible widget (set as
+ // allocation ascent) is treated differently; the distribution is
+ // only done for the rest. Also, the difference is distributed to
+ // ascent (except first widget) and descent independently.
+
+ int sumReqHeight = 0, firstVisibleChild = findFirstVisibleChild ();
+ for (int i = firstVisibleChild; i < children->size (); i++) {
+ Requisition childReq;
+ children->get(i)->sizeRequest (&childReq);
+ if (i > firstVisibleChild)
+ sumReqHeight += childReq.ascent;
+ sumReqHeight += childReq.descent;
+ }
+
+ //bool stretch = stretchChildren && sumReqHeight < allocWOBorders.descent;
+
+ // Cf. doc/rounding-errors.doc, with: x[i] = child requisition,
+ // y[i] = child allocation, a = total allocation, b = sumReqHeight.
+ int cumChildReqHeight = 0, cumChildAllocHeight = 0;
+ for (int i = 0; i < children->size (); i++) {
+ Widget *child = children->get (i);
+
+ Requisition childReq;
+ child->sizeRequest (&childReq);
+
+ Allocation childAlloc;
+ childAlloc.x = allocWOBorders.x;
+ childAlloc.width = allocWOBorders.width;
+
+ if (i == firstVisibleChild) {
+ childAlloc.y = allocWOBorders.y;
+ childAlloc.ascent = allocWOBorders.ascent;
+ } else {
+ childAlloc.y =
+ allocWOBorders.y + allocWOBorders.ascent + cumChildAllocHeight;
+ cumChildReqHeight += childReq.ascent;
+ childAlloc.ascent = sumReqHeight > 0 ?
+ safeATimesBDividedByC (cumChildReqHeight, allocWOBorders.descent,
+ sumReqHeight) - cumChildAllocHeight :
+ 0;
+ cumChildAllocHeight += childAlloc.ascent;
+ }
+
+ cumChildReqHeight += childReq.descent;
+ childAlloc.descent = sumReqHeight > 0 ?
+ safeATimesBDividedByC (cumChildReqHeight, allocWOBorders.descent,
+ sumReqHeight) - cumChildAllocHeight :
+ 0;
+ cumChildAllocHeight += childAlloc.descent;
+
+ child->sizeAllocate (&childAlloc);
+
+ //printf ("vbox: %dth child at (%d, %d), %d, (%d x %d)\n",
+ // i, childAlloc.x, childAlloc.y, childAlloc.width,
+ // childAlloc.ascent, childAlloc.descent);
+
+ }
+}
+
+void VBox::accumulateSize (int index, int size, Requisition *totalReq,
+ Requisition *childReq, int data1)
+{
+ int firstVisibleChild = data1;
+
+ totalReq->width = max (totalReq->width, childReq->width);
+ if (index == firstVisibleChild) {
+ // Resulting baseline is the baseline of the first child.
+ totalReq->ascent = childReq->ascent;
+ totalReq->descent = childReq->descent;
+ } else
+ totalReq->descent += (childReq->ascent + childReq->descent);
+}
+
+
+void VBox::accumulateExtremes (int index, int size, Extremes *totalExtr,
+ Extremes *childExtr)
+{
+ totalExtr->minWidth = max (totalExtr->minWidth, childExtr->minWidth);
+ totalExtr->maxWidth = max (totalExtr->maxWidth, childExtr->maxWidth);
+}
+
+} // namespace rtfl
+
+} // namespace dw
diff --git a/dwr/vbox.hh b/dwr/vbox.hh
new file mode 100644
index 0000000..b586217
--- /dev/null
+++ b/dwr/vbox.hh
@@ -0,0 +1,35 @@
+#ifndef __DWR_VBOX_HH__
+#define __DWR_VBOX_HH__
+
+#include "box.hh"
+
+namespace rtfl {
+
+namespace dw {
+
+class VBox: public Box
+{
+private:
+ int findFirstVisibleChild ();
+
+protected:
+ void sizeRequestImplImpl (::dw::core::Requisition *requisition);
+ void sizeAllocateImpl (::dw::core::Allocation *allocation);
+ void accumulateSize (int index, int size, ::dw::core::Requisition *totalReq,
+ ::dw::core::Requisition *childReq, int data1);
+ void accumulateExtremes (int index, int size,
+ ::dw::core::Extremes *totalExtr,
+ ::dw::core::Extremes *childExtr);
+
+public:
+ static int CLASS_ID;
+
+ VBox (bool stretchChildren);
+ ~VBox ();
+};
+
+} // namespace rtfl
+
+} // namespace dw
+
+#endif // __DWR_VBOX_HH__