summaryrefslogtreecommitdiff
path: root/objects
diff options
context:
space:
mode:
authorRodrigo Arias Mallo <rodarima@gmail.com>2024-12-10 22:30:12 +0100
committerRodrigo Arias Mallo <rodarima@gmail.com>2024-12-10 22:30:12 +0100
commit429d5f88b94ff28416cbfc6420b6389fa284df97 (patch)
treefb6fdaf7731de1ef396f98b748c56f3149801c84 /objects
Import RTFL 0.1.1v0.1.1
Diffstat (limited to 'objects')
-rw-r--r--objects/Makefile.am67
-rw-r--r--objects/objcount_controller.cc114
-rw-r--r--objects/objcount_controller.hh50
-rw-r--r--objects/objcount_window.cc445
-rw-r--r--objects/objcount_window.hh118
-rw-r--r--objects/objdelete_controller.cc204
-rw-r--r--objects/objdelete_controller.hh78
-rw-r--r--objects/objects_buffer.cc281
-rw-r--r--objects/objects_buffer.hh88
-rw-r--r--objects/objects_parser.cc401
-rw-r--r--objects/objects_parser.hh115
-rw-r--r--objects/objects_writer.cc145
-rw-r--r--objects/objects_writer.hh44
-rw-r--r--objects/objident_controller.cc339
-rw-r--r--objects/objident_controller.hh192
-rw-r--r--objects/objview_commands.cc773
-rw-r--r--objects/objview_commands.hh349
-rw-r--r--objects/objview_controller.cc171
-rw-r--r--objects/objview_controller.hh51
-rw-r--r--objects/objview_graph.cc984
-rw-r--r--objects/objview_graph.hh305
-rw-r--r--objects/objview_stacktrace.cc127
-rw-r--r--objects/objview_stacktrace.hh60
-rw-r--r--objects/objview_window.cc707
-rw-r--r--objects/objview_window.hh189
-rw-r--r--objects/rtfl_objbase.cc45
-rw-r--r--objects/rtfl_objcount.cc40
-rw-r--r--objects/rtfl_objview.cc218
28 files changed, 6700 insertions, 0 deletions
diff --git a/objects/Makefile.am b/objects/Makefile.am
new file mode 100644
index 0000000..fea2030
--- /dev/null
+++ b/objects/Makefile.am
@@ -0,0 +1,67 @@
+AM_CPPFLAGS = \
+ -I$(top_srcdir)\
+ -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/objects"'
+
+noinst_LIBRARIES = librtfl-objects.a
+
+bin_PROGRAMS = rtfl-objbase rtfl-objcount rtfl-objview
+
+rtfl_objbase_SOURCES = rtfl_objbase.cc
+
+rtfl_objbase_LDADD = \
+ librtfl-objects.a \
+ ../common/librtfl-tools.a \
+ ../lout/liblout.a
+
+librtfl_objects_a_SOURCES = \
+ objdelete_controller.hh \
+ objdelete_controller.cc \
+ objects_buffer.hh \
+ objects_buffer.cc \
+ objects_parser.hh \
+ objects_parser.cc \
+ objects_writer.hh \
+ objects_writer.cc \
+ objident_controller.hh \
+ objident_controller.cc
+
+rtfl_objcount_SOURCES = \
+ objcount_controller.hh \
+ objcount_controller.cc \
+ objcount_window.hh \
+ objcount_window.cc \
+ rtfl_objcount.cc
+
+rtfl_objcount_LDADD = \
+ librtfl-objects.a \
+ ../common/librtfl-common.a \
+ ../common/librtfl-tools.a \
+ ../lout/liblout.a \
+ @LIBFLTK_LIBS@
+
+rtfl_objview_SOURCES = \
+ objview_commands.hh \
+ objview_commands.cc \
+ objview_graph.hh \
+ objview_graph.cc \
+ objview_controller.hh \
+ objview_controller.cc \
+ objview_stacktrace.hh \
+ objview_stacktrace.cc \
+ objview_window.hh \
+ objview_window.cc \
+ rtfl_objview.cc
+
+rtfl_objview_LDADD = \
+ librtfl-objects.a \
+ ../common/librtfl-common.a \
+ ../common/librtfl-tools.a \
+ ../dwr/libDw-rtfl.a \
+ ../dw/libDw-fltk.a \
+ ../dw/libDw-core.a \
+ ../lout/liblout.a \
+ @LIBFLTK_LIBS@
+
+if USE_GRAPH2
+rtfl_objview_LDADD += @GRAPHVIZ_LIBS@
+endif
diff --git a/objects/objcount_controller.cc b/objects/objcount_controller.cc
new file mode 100644
index 0000000..b7ce4e7
--- /dev/null
+++ b/objects/objcount_controller.cc
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ *
+ * 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 "objcount_controller.hh"
+
+using namespace rtfl::tools;
+
+namespace rtfl {
+
+namespace objects {
+
+void ObjCountController::objMsg (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ table->registerObject (id);
+}
+
+void ObjCountController::objMark (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ table->registerObject (id);
+}
+
+ void ObjCountController::objMsgStart (CommonLineInfo *info, const char *id)
+{
+ table->registerObject (id);
+}
+
+void ObjCountController::objMsgEnd (CommonLineInfo *info, const char *id)
+{
+ table->registerObject (id);
+}
+
+void ObjCountController::objEnter (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *funname, const char *args)
+{
+ table->registerObject (id);
+}
+
+void ObjCountController::objLeave (CommonLineInfo *info, const char *id,
+ const char *vals)
+{
+ table->registerObject (id);
+}
+
+void ObjCountController::objCreate (CommonLineInfo *info, const char *id,
+ const char *klass)
+{
+ table->createObject (id, klass);
+}
+
+void ObjCountController::objIdent (CommonLineInfo *info, const char *id1,
+ const char *id2)
+{
+ // TODO Is this not done by ObjdentController?
+ table->addIdentity (id1, id2);
+}
+
+void ObjCountController::objNoIdent (CommonLineInfo *info)
+{
+}
+
+void ObjCountController::objAssoc (CommonLineInfo *info, const char *parent,
+ const char *child)
+{
+ table->registerObject (parent);
+ table->registerObject (child);
+}
+
+void ObjCountController::objSet (CommonLineInfo *info, const char *id,
+ const char *var, const char *val)
+{
+ table->registerObject (id);
+}
+
+void ObjCountController::objClassColor (CommonLineInfo *info, const char *klass,
+ const char *color)
+{
+ table->setClassColor (klass, color);
+}
+
+void ObjCountController::objObjectColor (CommonLineInfo *info, const char *id,
+ const char *color)
+{
+ table->registerObject (id);
+}
+
+void ObjCountController::objDelete (CommonLineInfo *info, const char *id)
+{
+ table->deleteObject (id);
+}
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objcount_controller.hh b/objects/objcount_controller.hh
new file mode 100644
index 0000000..6af5ba7
--- /dev/null
+++ b/objects/objcount_controller.hh
@@ -0,0 +1,50 @@
+#ifndef __OBJECTS_OBJCOUNT_CONTROLLER_HH__
+#define __OBJECTS_OBJCOUNT_CONTROLLER_HH__
+
+#include "objects_parser.hh"
+#include "objcount_window.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+class ObjCountController: public ObjectsControllerBase
+{
+private:
+ ObjCountTable *table;
+
+public:
+ ObjCountController (ObjCountTable *table) { this->table = table; }
+
+ void objMsg (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMark (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMsgStart (tools::CommonLineInfo *info, const char *id);
+ void objMsgEnd (tools::CommonLineInfo *info, const char *id);
+ void objEnter (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args);
+ void objLeave (tools::CommonLineInfo *info, const char *id,
+ const char *vals);
+ void objCreate (tools::CommonLineInfo *info, const char *id,
+ const char *klass);
+ void objIdent (tools::CommonLineInfo *info, const char *id1,
+ const char *id2);
+ void objNoIdent (tools::CommonLineInfo *info);
+ void objAssoc (tools::CommonLineInfo *info, const char *parent,
+ const char *child);
+ void objSet (tools::CommonLineInfo *info, const char *id, const char *var,
+ const char *val);
+ void objClassColor (tools::CommonLineInfo *info, const char *klass,
+ const char *color);
+ void objObjectColor (tools::CommonLineInfo *info, const char *id,
+ const char *color);
+ void objDelete (tools::CommonLineInfo *info, const char *id);
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJCOUNT_CONTROLLER_HH__
diff --git a/objects/objcount_window.cc b/objects/objcount_window.cc
new file mode 100644
index 0000000..d7adf06
--- /dev/null
+++ b/objects/objcount_window.cc
@@ -0,0 +1,445 @@
+/*
+ * 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.
+ *
+ * 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 "objcount_window.hh"
+#include "common/about.hh"
+
+#include <FL/Fl_Menu_Bar.H>
+#include <FL/Fl_Menu_Item.H>
+#include <FL/fl_draw.H>
+
+using namespace lout::container::typed;
+using namespace lout::object;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace objects {
+
+ObjCountTable::Class::Class (const char *name)
+{
+ this->name = strdup (name);
+ count = new lout::misc::SimpleVector<int> (1);
+ count->increase ();
+ *(count->getLastRef()) = 0;
+}
+
+
+ObjCountTable::Class::~Class ()
+{
+ free (name);
+ delete count;
+}
+
+
+int ObjCountTable::Class::compareTo(Comparable *other)
+{
+ return strcmp (name, ((Class*)other)->name);
+}
+
+
+void ObjCountTable::Class::create ()
+{
+ (*(count->getLastRef()))++;
+}
+
+
+void ObjCountTable::Class::remove ()
+{
+ (*(count->getLastRef()))--;
+}
+
+
+void ObjCountTable::Class::newSnapshot ()
+{
+ int c = *(count->getLastRef());
+ count->increase ();
+ *(count->getLastRef()) = c;
+}
+
+
+// ----------------------------------------------------------------------
+
+
+int ObjCountTable::Object::classSernoGlobal = 0;
+
+
+ObjCountTable::Object::Object (Class *klass)
+{
+ setClass (klass);
+ refCount = 0;
+}
+
+
+ObjCountTable::Object::~Object ()
+{
+}
+
+
+void ObjCountTable::Object::setClass (Class *klass)
+{
+ this->klass = klass;
+ classSerno = classSernoGlobal++;
+}
+
+
+// ----------------------------------------------------------------------
+
+
+ObjCountTable::ObjectRef::ObjectRef (rtfl::objects::ObjCountTable::Object
+ *object)
+{
+ this->object = object;
+ object->ref ();
+}
+
+
+ObjCountTable::ObjectRef::~ObjectRef ()
+{
+ object->unref ();
+}
+
+
+// ----------------------------------------------------------------------
+
+
+ObjCountTable::ObjCountTable (int x, int y, int width, int height,
+ const char *label) :
+ Fl_Table (x, y, width, height, label)
+{
+ rows (0);
+ row_header (true);
+ row_height_all (20);
+ row_resize (false);
+
+ cols (1);
+ col_header (false);
+ row_header_width (200);
+ col_width_all (80);
+ col_resize (true);
+
+ end();
+
+ objects = new HashTable<String, ObjectRef> (true, true);
+ identities = new HashTable<String, String> (true, true);
+ identitiesRev = new HashTable<String, String> (false, false);
+ classes = new HashTable<String, Class> (true, false);
+ classesList = new Vector<Class> (1, true);
+
+ ensureClass ("<unknown>");
+}
+
+
+ObjCountTable::~ObjCountTable()
+{
+ delete objects;
+ delete identitiesRev;
+ delete identities;
+ delete classes;
+ delete classesList;
+}
+
+
+void ObjCountTable::draw_cell (TableContext context, int row, int col, int x,
+ int y, int width, int height)
+{
+ switch (context) {
+ case CONTEXT_COL_HEADER:
+ break;
+
+ case CONTEXT_ROW_HEADER:
+ fl_push_clip (x, y, width, height);
+ fl_draw_box (FL_THIN_UP_BOX, x, y, width, height, row_header_color ());
+ fl_color (FL_BLACK);
+ fl_draw (classesList->get(row)->name, x, y, width, height, FL_ALIGN_LEFT);
+ fl_pop_clip ();
+ break;
+
+ case CONTEXT_CELL:
+ fl_push_clip (x, y, width, height);
+ fl_color (FL_WHITE);
+ fl_rectf (x, y, width, height);
+ fl_color (FL_BLACK);
+ char buf[6];
+ snprintf (buf, 6, "%d", classesList->get(row)->count->get (col));
+ fl_draw (buf, x, y, width, height, FL_ALIGN_RIGHT);
+ fl_pop_clip ();
+ break;
+
+ default:
+ // compiler happyness
+ break;
+ }
+}
+
+
+ObjCountTable::Class *ObjCountTable::ensureClass (const char *className)
+{
+ String key (className);
+ Class *klass = classes->get (&key);
+
+ if (klass == NULL) {
+ klass = new Class (className);
+ classes->put (new String (className), klass);
+
+ int i = classesList->bsearch (klass, false);
+ classesList->insert (klass, i);
+ for (int j = i; j < classesList->size (); j++)
+ classesList->get(j)->index = j;
+
+ rows (rows () + 1);
+ }
+
+ return klass;
+}
+
+void ObjCountTable::createObject (const char *id, const char *className)
+{
+ Class *klass = ensureClass (className);
+
+ String key (id);
+ ObjectRef *objectRef = objects->get (&key);
+
+ if (objectRef != NULL) {
+ objectRef->object->getClass()->remove ();
+ objectRef->object->setClass (klass);
+ } else {
+ rtfl::objects::ObjCountTable::Object *object;
+ String *id2 = identities->get (&key);
+ if (id2) {
+ ObjectRef *objRef2 = objects->get (id2);
+ assert (objRef2 != NULL);
+ object = objRef2->object;
+ } else
+ object = new rtfl::objects::ObjCountTable::Object (klass);
+
+ objectRef = new ObjectRef (object);
+ objects->put (new String (id), objectRef);
+ }
+
+ klass->create ();
+ damage_zone(klass->index, cols () - 1, klass->index, cols () - 1);
+}
+
+
+void ObjCountTable::deleteObject (const char *id)
+{
+ String key (id);
+ ObjectRef *objectRef = objects->get (&key);
+ if (objectRef != NULL) {
+ objectRef->object->getClass()->remove ();
+ damage_zone(objectRef->object->getClass()->index,cols () - 1,
+ objectRef->object->getClass()->index, cols () - 1);
+
+ objects->remove (&key);
+
+ String *key2 = identities->get (&key);
+ if (key2) {
+ identitiesRev->remove (key2);
+ identities->remove (&key);
+ } else {
+ key2 = identitiesRev->get (&key);
+ if (key2) {
+ identitiesRev->remove (&key);
+ identities->remove (key2);
+ }
+ }
+ }
+}
+
+
+void ObjCountTable::registerObject (const char *id)
+{
+ String key (id);
+ if (!objects->contains (&key))
+ createObject (id, "<unknown>");
+}
+
+
+void ObjCountTable::addIdentity (const char *id1, const char *id2)
+{
+ if (strcmp (id1, id2) != 0) {
+ // Note: An ObjectRef (which is != NULL) points always to an Object.
+ String key1 (id1), key2 (id2);
+ ObjectRef *objRef1 = objects->get (&key1),
+ *objRef2 = objects->get (&key2);
+
+ if (objRef1 == NULL && objRef2 == NULL) {
+ // Neither defined: create both.
+ registerObject (id1);
+ insertIdentity (id2, id1);
+ registerObject (id2);
+ } else if (objRef1 == NULL) {
+ // First not defined, but second: create from second.
+ insertIdentity (id2, id1);
+ registerObject (id2);
+ } else if (objRef2 == NULL) {
+ // Vice versa.
+ insertIdentity (id1, id2);
+ registerObject (id1);
+ } else {
+ // Both already defined ...
+ if (objRef1->object == objRef2->object)
+ // ... for same object: caller's fault.
+ fprintf (stderr, "WARNING: Identity of '%s' and '%s' added twice.",
+ id1, id2);
+ else {
+ // ... for different objects.
+ if (objRef1->object->getClassSerno () >
+ objRef2->object->getClassSerno ()) {
+ // Class definition of first object more recent, so assign it to
+ // second.
+ objRef2->object->getClass()->remove ();
+ damage_zone(objRef2->object->getClass()->index,cols () - 1,
+ objRef2->object->getClass()->index, cols () - 1);
+ objRef2->object->unref ();
+ objRef2->object = objRef1->object;
+ objRef2->object->ref ();
+ } else {
+ // Vice versa.
+ objRef1->object->getClass()->remove ();
+ damage_zone(objRef1->object->getClass()->index,cols () - 1,
+ objRef1->object->getClass()->index, cols () - 1);
+ objRef1->object->unref ();
+ objRef1->object = objRef2->object;
+ objRef1->object->ref ();
+ }
+ }
+ }
+ }
+}
+
+
+void ObjCountTable::insertIdentity (const char *id1, const char *id2)
+{
+ String *s1 = new String (id1), *s2 = new String (id2);
+ identities->put (s1, s2);
+ identitiesRev->put (s2, s1);
+}
+
+
+void ObjCountTable::setClassColor (const char *klass, const char *color)
+{
+}
+
+
+void ObjCountTable::newSnapshot ()
+{
+ for (int i = 0; i < classesList->size (); i++)
+ classesList->get(i)->newSnapshot ();
+
+ cols (cols () + 1);
+}
+
+
+void ObjCountTable::removeOldestSnapshot ()
+{
+}
+
+
+// ----------------------------------------------------------------------
+
+
+ObjCountWindow::ObjCountWindow (int width, int height, const char *title) :
+ Fl_Window (width, height, title)
+{
+ int menuHeight = 24;
+
+ callback(windowCallback, NULL);
+ box(FL_NO_BOX);
+
+ Fl_Menu_Bar *menu = new Fl_Menu_Bar(0, 0, width, menuHeight);
+
+ table = new ObjCountTable (0, menuHeight, width, height - menuHeight);
+
+ Fl_Menu_Item menuItems[] = {
+ { "&File", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 },
+ { "Quit", FL_COMMAND + 'q', quit, this, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+ { "&Snapshot", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 },
+ { "New &snapshot", FL_COMMAND + 's', newSnapshot, this, 0, 0, 0, 0, 0 },
+ { "&Delete oldest", FL_COMMAND + 'd', removeOldestSnapshot, this,
+ 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+ { "&Help", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 },
+ { "&About RTFL", 0, about, this, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+ };
+ menu->copy(menuItems);
+
+ resizable(table);
+
+ aboutWindow = NULL;
+}
+
+
+ObjCountWindow::~ObjCountWindow ()
+{
+ if (aboutWindow)
+ delete aboutWindow;
+}
+
+
+void ObjCountWindow::windowCallback (Fl_Widget *widget, void *data)
+{
+ // Ignore escape key. TODO Looks rather hackish to me.
+ if (Fl::event_key() != FL_Escape)
+ quit (widget, data);
+}
+
+
+void ObjCountWindow::quit (Fl_Widget *widget, void *data)
+{
+ exit (0);
+}
+
+
+void ObjCountWindow::newSnapshot (Fl_Widget *widget, void *data)
+{
+ ObjCountWindow *window = (ObjCountWindow*)data;
+ window->table->newSnapshot ();
+}
+
+
+void ObjCountWindow::removeOldestSnapshot (Fl_Widget *widget, void *data)
+{
+ ObjCountWindow *window = (ObjCountWindow*)data;
+ window->table->removeOldestSnapshot ();
+}
+
+
+void ObjCountWindow::about (Fl_Widget *widget, void *data)
+{
+ ObjCountWindow *window = (ObjCountWindow*)data;
+
+ if (window->aboutWindow == NULL)
+ window->aboutWindow =
+ new common::AboutWindow("rtfl-objcount", "",
+ common::AboutWindow::HEIGHT_SIMPLE);
+ window->aboutWindow->show ();
+}
+
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objcount_window.hh b/objects/objcount_window.hh
new file mode 100644
index 0000000..485be55
--- /dev/null
+++ b/objects/objcount_window.hh
@@ -0,0 +1,118 @@
+#ifndef __OBJECTS_OBJCOUNT_WINDOW_HH__
+#define __OBJECTS_OBJCOUNT_WINDOW_HH__
+
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Table.H>
+
+#include "lout/object.hh"
+#include "lout/container.hh"
+#include "lout/misc.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+class ObjCountTable : public Fl_Table
+{
+private:
+ class Class: public lout::object::Comparable
+ {
+ public:
+ char *name;
+ int index;
+ lout::misc::SimpleVector<int> *count;
+
+ Class (const char *name);
+ ~Class ();
+
+ int compareTo(Comparable *other);
+
+ void create ();
+ void remove ();
+ void newSnapshot ();
+ };
+
+ class Object: public lout::object::Object
+ {
+ private:
+ static int classSernoGlobal;
+
+ Class *klass;
+ int classSerno;
+ int refCount;
+
+ ~Object ();
+
+ public:
+ Object (Class *klass);
+
+ inline void ref () { refCount++; }
+ inline void unref () { if (--refCount == 0) delete this; }
+
+ inline Class *getClass () { return klass; }
+ void setClass (Class *klass);
+ inline int getClassSerno () { return classSerno; }
+ };
+
+ class ObjectRef: public lout::object::Object
+ {
+ public:
+ rtfl::objects::ObjCountTable::Object *object;
+
+ ObjectRef (rtfl::objects::ObjCountTable::Object *object);
+ ~ObjectRef ();
+ };
+
+ lout::container::typed::HashTable<lout::object::String, ObjectRef> *objects;
+ lout::container::typed::HashTable<lout::object::String, lout::object::String>
+ *identities, *identitiesRev;
+ lout::container::typed::HashTable<lout::object::String, Class> *classes;
+ lout::container::typed::Vector<Class> *classesList;
+
+ Class *ensureClass (const char *className);
+ void insertIdentity (const char *id1, const char *id2);
+
+public:
+ ObjCountTable (int x, int y, int width, int height,
+ const char *label = NULL);
+ ~ObjCountTable();
+
+ void draw_cell (TableContext context, int row, int col, int x, int y,
+ int width, int height);
+
+ void createObject (const char *id, const char *className);
+ void deleteObject (const char *id);
+ void registerObject (const char *id);
+ void addIdentity (const char *id1, const char *id2);
+ void setClassColor (const char *klass, const char *color);
+ void newSnapshot ();
+ void removeOldestSnapshot ();
+};
+
+
+class ObjCountWindow: public Fl_Window
+{
+private:
+ Fl_Window *aboutWindow;
+ ObjCountTable *table;
+
+ static void windowCallback (Fl_Widget *widget, void *data);
+ static void quit (Fl_Widget *widget, void *data);
+ static void newSnapshot (Fl_Widget *widget, void *data);
+ static void removeOldestSnapshot (Fl_Widget *widget, void *data);
+ static void about (Fl_Widget *widget, void *data);
+
+public:
+ ObjCountWindow (int width, int height, const char *title);
+ ~ObjCountWindow ();
+
+ inline ObjCountTable *getTable () { return table; }
+};
+
+
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJCOUNT_WINDOW_HH__
diff --git a/objects/objdelete_controller.cc b/objects/objdelete_controller.cc
new file mode 100644
index 0000000..8224029
--- /dev/null
+++ b/objects/objdelete_controller.cc
@@ -0,0 +1,204 @@
+/*
+ * RTFL
+ *
+ * Copyright 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 "objdelete_controller.hh"
+
+using namespace lout::object;
+using namespace lout::misc;
+using namespace lout::container::typed;
+using namespace rtfl::tools;
+
+namespace rtfl {
+
+namespace objects {
+
+ObjDeleteController::ObjInfo::ObjInfo (const char *id)
+{
+ numCreated = 0;
+ numDeleted = 0;
+ origId = strdup (id);
+ mappedId = strdup (id);
+}
+
+ObjDeleteController::ObjInfo::~ObjInfo ()
+{
+ free (origId);
+ free (mappedId);
+}
+
+void ObjDeleteController::ObjInfo::use ()
+{
+ numCreated = max (numCreated, 1);
+}
+
+void ObjDeleteController::ObjInfo::objCreate ()
+{
+ numCreated++;
+}
+
+void ObjDeleteController::ObjInfo::objDelete ()
+{
+ numCreated--;
+ if (numCreated <= 0) {
+ numCreated = 0;
+ numDeleted++;
+
+ free (mappedId);
+ mappedId = (char*) malloc ((strlen (origId) + 10 + 1) * sizeof (char));
+ sprintf (mappedId, "%s-%d", origId, numDeleted);
+ }
+}
+
+ObjDeleteController::ObjDeleteController (ObjectsController *successor)
+{
+ this->successor = successor;
+ successor->setObjectsSource (this);
+ setObjectsSink (successor);
+
+ objInfos = new HashTable<String, ObjInfo> (true, true);
+}
+
+ObjDeleteController::~ObjDeleteController ()
+{
+ delete objInfos;
+}
+
+void ObjDeleteController::objMsg (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ successor->objMsg (info, mapId (id), aspect, prio, message);
+}
+
+void ObjDeleteController::objMark (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ successor->objMark (info, mapId (id), aspect, prio, message);
+}
+
+void ObjDeleteController::objMsgStart (CommonLineInfo *info, const char *id)
+{
+ successor->objMsgStart (info, mapId (id));
+}
+
+void ObjDeleteController::objMsgEnd (CommonLineInfo *info, const char *id)
+{
+ successor->objMsgEnd (info, mapId (id));
+}
+
+void ObjDeleteController::objEnter (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *funname, const char *args)
+{
+ successor->objEnter (info, mapId (id), aspect, prio, funname, args);
+}
+
+void ObjDeleteController::objLeave (CommonLineInfo *info, const char *id,
+ const char *vals)
+{
+ successor->objLeave (info, mapId (id), vals);
+}
+
+void ObjDeleteController::objCreate (CommonLineInfo *info, const char *id,
+ const char *klass)
+{
+ ensureObjInfo(id)->objCreate ();
+ successor->objCreate (info, mapId (id), klass);
+}
+
+void ObjDeleteController::objIdent (CommonLineInfo *info, const char *id1,
+ const char *id2)
+{
+ successor->objIdent (info, mapId (id1), mapId (id2));
+}
+
+void ObjDeleteController::objNoIdent (CommonLineInfo *info)
+{
+ successor->objNoIdent (info);
+}
+
+void ObjDeleteController::objAssoc (CommonLineInfo *info, const char *parent,
+ const char *child)
+{
+ successor->objAssoc (info, mapId (parent), mapId (child));
+}
+
+void ObjDeleteController::objSet (CommonLineInfo *info, const char *id,
+ const char *var, const char *val)
+{
+ successor->objSet (info, mapId (id), var, val);
+}
+
+void ObjDeleteController::objClassColor (CommonLineInfo *info,
+ const char *klass, const char *color)
+{
+ successor->objClassColor (info, klass, color);
+}
+
+void ObjDeleteController::objObjectColor (CommonLineInfo *info, const char *id,
+ const char *color)
+{
+ successor->objObjectColor (info, mapId (id), color);
+}
+
+void ObjDeleteController::objDelete (CommonLineInfo *info, const char *id)
+{
+ successor->objDelete (info, mapId (id));
+ ensureObjInfo(id)->objDelete ();
+}
+
+const char *ObjDeleteController::mapId (const char *id)
+{
+ ObjInfo *objInfo = ensureObjInfo (id);
+ objInfo->use ();
+ return objInfo->getMappedId ();
+}
+
+ObjDeleteController::ObjInfo *ObjDeleteController::getObjInfo (const char *id)
+{
+ String key (id);
+ return objInfos->get (&key);
+}
+
+ObjDeleteController::ObjInfo *ObjDeleteController::ensureObjInfo (const char
+ *id)
+{
+ ObjInfo *objInfo = getObjInfo (id);
+ if (objInfo == NULL) {
+ objInfo = new ObjInfo (id);
+ objInfos->put (new String (id), objInfo);
+ }
+ return objInfo;
+}
+
+} // namespace objects
+
+} // namespace rtfl
+
diff --git a/objects/objdelete_controller.hh b/objects/objdelete_controller.hh
new file mode 100644
index 0000000..eccee06
--- /dev/null
+++ b/objects/objdelete_controller.hh
@@ -0,0 +1,78 @@
+#ifndef __OBJECTS_OBJDELETE_CONTROLLER_HH__
+#define __OBJECTS_OBJDELETE_CONTROLLER_HH__
+
+#include "objects_parser.hh"
+#include "common/tools.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+/**
+ * \brief Processes `obj-delete` specially and maps ids of deleted objects to
+ * new ones, if they are reused.
+ */
+class ObjDeleteController: public ObjectsControllerBase
+{
+private:
+ class ObjInfo: public lout::object::Object
+ {
+ private:
+ int numCreated, numDeleted;
+ char *origId, *mappedId;
+
+ public:
+ ObjInfo (const char *id);
+ ~ObjInfo ();
+
+ void use ();
+ void objCreate ();
+ void objDelete ();
+
+ inline const char *getMappedId () { return mappedId; }
+ };
+
+ ObjectsController *successor;
+ lout::container::typed::HashTable<lout::object::String, ObjInfo> *objInfos;
+
+ const char *mapId (const char *id);
+ bool objInfoCreated (const char *id);
+ ObjInfo *getObjInfo (const char *id);
+ ObjInfo *ensureObjInfo (const char *id);
+
+public:
+ ObjDeleteController (ObjectsController *successor);
+ ~ObjDeleteController ();
+
+ void objMsg (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMark (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMsgStart (tools::CommonLineInfo *info, const char *id);
+ void objMsgEnd (tools::CommonLineInfo *info, const char *id);
+ void objEnter (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args);
+ void objLeave (tools::CommonLineInfo *info, const char *id,
+ const char *vals);
+ void objCreate (tools::CommonLineInfo *info, const char *id,
+ const char *klass);
+ void objIdent (tools::CommonLineInfo *info, const char *id1,
+ const char *id2);
+ void objNoIdent (tools::CommonLineInfo *info);
+ void objAssoc (tools::CommonLineInfo *info, const char *parent,
+ const char *child);
+ void objSet (tools::CommonLineInfo *info, const char *id, const char *var,
+ const char *val);
+ void objClassColor (tools::CommonLineInfo *info, const char *klass,
+ const char *color);
+ void objObjectColor (tools::CommonLineInfo *info, const char *id,
+ const char *color);
+ void objDelete (tools::CommonLineInfo *info, const char *id);
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJDELETE_CONTROLLER_HH__
diff --git a/objects/objects_buffer.cc b/objects/objects_buffer.cc
new file mode 100644
index 0000000..e669142
--- /dev/null
+++ b/objects/objects_buffer.cc
@@ -0,0 +1,281 @@
+/*
+ * RTFL
+ *
+ * Copyright 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 "objects_buffer.hh"
+
+using namespace lout::object;
+using namespace lout::container::typed;
+using namespace rtfl::tools;
+
+namespace rtfl {
+
+namespace objects {
+
+ObjectsBuffer::ObjectCommand::ObjectCommand (CommandType type,
+ CommonLineInfo *info,
+ const char *fmt, ...)
+{
+ this->type = type;
+ this->info.fileName = strdup (info->fileName);
+ this->info.lineNo = info->lineNo;
+ this->info.processId = info->processId;
+ this->info.completeLine = strdup (info->completeLine);
+
+ numArgs = strlen (fmt);
+ args = new Arg[numArgs];
+
+ va_list vargs;
+ va_start (vargs, fmt);
+
+ char *s;
+ for (int i = 0; fmt[i]; i++) {
+ args[i].type = fmt[i];
+ switch (fmt[i]) {
+ case 'd':
+ args[i].d = va_arg(vargs, int);
+ break;
+
+ case 's':
+ s = va_arg (vargs, char*);
+ args[i].s = s ? strdup (s) : NULL;
+ break;
+ }
+ }
+}
+
+ObjectsBuffer::ObjectCommand::~ObjectCommand ()
+{
+ free (info.fileName);
+ free (info.completeLine);
+
+ for (int i = 0; i < numArgs; i++)
+ if (args[i].type == 's' && args[i].s)
+ free (args[i].s);
+
+ delete[] args;
+}
+
+// ----------------------------------------------------------------------
+
+ObjectsBuffer::ObjectsBuffer (ObjectsController *successor)
+{
+ this->successor = successor;
+ successor->setObjectsSource (this);
+
+ commandsQueue = new Vector<ObjectCommand> (1, true);
+ queued = false;
+
+ setObjectsSink (successor);
+}
+
+ObjectsBuffer::~ObjectsBuffer ()
+{
+ delete commandsQueue;
+}
+
+void ObjectsBuffer::objMsg (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message)
+{
+ process (new ObjectCommand (MSG, info, "ssds", id, aspect, prio, message));
+}
+
+void ObjectsBuffer::objMark (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message)
+{
+ process (new ObjectCommand (MARK, info, "ssds", id, aspect, prio, message));
+}
+
+void ObjectsBuffer::objMsgStart (CommonLineInfo *info, const char *id)
+{
+ process (new ObjectCommand (MSG_START, info, "s", id));
+}
+
+void ObjectsBuffer::objMsgEnd (CommonLineInfo *info, const char *id)
+{
+ process (new ObjectCommand (MSG_END, info, "s", id));
+}
+
+void ObjectsBuffer::objEnter (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args)
+{
+ process (new ObjectCommand (ENTER, info, "ssdss", id, aspect, prio, funname,
+ args));
+}
+
+void ObjectsBuffer::objLeave (CommonLineInfo *info, const char *id,
+ const char *vals)
+{
+ process (new ObjectCommand (LEAVE, info, "ss", id, vals));
+}
+
+void ObjectsBuffer::objCreate (CommonLineInfo *info, const char *id,
+ const char *klass)
+{
+ process (new ObjectCommand (CREATE, info, "ss", id, klass));
+}
+
+void ObjectsBuffer::objIdent (CommonLineInfo *info, const char *id1,
+ const char *id2)
+{
+ process (new ObjectCommand (IDENT, info, "ss", id1, id2));
+}
+
+void ObjectsBuffer::objNoIdent (CommonLineInfo *info)
+{
+ process (new ObjectCommand (NOIDENT, info, ""));
+}
+
+void ObjectsBuffer::objAssoc (CommonLineInfo *info, const char *parent,
+ const char *child)
+{
+ process (new ObjectCommand (ASSOC, info, "ss", parent, child));
+}
+
+void ObjectsBuffer::objSet (CommonLineInfo *info, const char *id,
+ const char *var, const char *val)
+{
+ process (new ObjectCommand (SET, info, "sss", id, var, val));
+}
+
+void ObjectsBuffer::objClassColor (CommonLineInfo *info, const char *klass,
+ const char *color)
+{
+ process (new ObjectCommand (CLASS_COLOR, info, "ss", klass, color));
+}
+
+void ObjectsBuffer::objObjectColor (CommonLineInfo *info, const char *id,
+ const char *color)
+{
+ process (new ObjectCommand (OBJECT_COLOR, info, "ss", id, color));
+}
+
+void ObjectsBuffer::objDelete (CommonLineInfo *info, const char *id)
+{
+ process (new ObjectCommand (DELETE, info, "s", id));
+}
+
+void ObjectsBuffer::queue ()
+{
+ queued = true;
+}
+
+void ObjectsBuffer::pass ()
+{
+ for (int i = 0; i < commandsQueue->size (); i++)
+ pass (commandsQueue->get (i));
+
+ commandsQueue->clear ();
+ queued = false;
+}
+
+void ObjectsBuffer::process (ObjectCommand *command)
+{
+ if (queued)
+ queue (command);
+ else {
+ pass (command);
+ delete command;
+ }
+}
+
+void ObjectsBuffer::queue (ObjectCommand *command)
+{
+ commandsQueue->put (command);
+}
+
+void ObjectsBuffer::pass (ObjectCommand *command)
+{
+ CommonLineInfo info = { command->info.fileName, command->info.lineNo,
+ command->info.processId,
+ command->info.completeLine };
+ ObjectCommand::Arg *a = command->args;
+
+ switch (command->type) {
+ case MSG:
+ successor->objMsg (&info, a[0].s, a[1].s, a[2].d, a[3].s);
+ break;
+
+ case MARK:
+ successor->objMark (&info, a[0].s, a[1].s, a[2].d, a[3].s);
+ break;
+
+ case MSG_START:
+ successor->objMsgStart (&info, a[0].s);
+ break;
+
+ case MSG_END:
+ successor->objMsgEnd (&info, a[0].s);
+ break;
+
+ case ENTER:
+ successor->objEnter (&info, a[0].s, a[1].s, a[2].d, a[3].s, a[4].s);
+ break;
+
+ case LEAVE:
+ successor->objLeave (&info, a[0].s, a[1].s);
+ break;
+
+ case CREATE:
+ successor->objCreate (&info, a[0].s, a[1].s);
+ break;
+
+ case IDENT:
+ successor->objIdent (&info, a[0].s, a[1].s);
+ break;
+
+ case NOIDENT:
+ successor->objNoIdent (&info);
+ break;
+
+ case ASSOC:
+ successor->objAssoc (&info, a[0].s, a[1].s);
+ break;
+
+ case SET:
+ successor->objSet (&info, a[0].s, a[1].s, a[2].s);
+ break;
+
+ case CLASS_COLOR:
+ successor->objClassColor (&info, a[0].s, a[1].s);
+ break;
+
+ case OBJECT_COLOR:
+ successor->objObjectColor (&info, a[0].s, a[1].s);
+ break;
+
+ case DELETE:
+ successor->objDelete (&info, a[0].s);
+ break;
+ }
+}
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objects_buffer.hh b/objects/objects_buffer.hh
new file mode 100644
index 0000000..f00ef89
--- /dev/null
+++ b/objects/objects_buffer.hh
@@ -0,0 +1,88 @@
+#ifndef __OBJECTS_OBJECTS_BUFFER_HH__
+#define __OBJECTS_OBJECTS_BUFFER_HH__
+
+#include "objects_parser.hh"
+#include "common/tools.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+class ObjectsBuffer: public ObjectsControllerBase
+{
+private:
+ enum CommandType {
+ MSG, MARK, MSG_START, MSG_END, ENTER, LEAVE, CREATE, IDENT, NOIDENT,
+ ASSOC, SET, CLASS_COLOR, OBJECT_COLOR, DELETE
+ };
+
+ class ObjectCommand: public lout::object::Object
+ {
+ friend class ObjectsBuffer;
+
+ private:
+ CommandType type;
+ tools::CommonLineInfo info;
+
+ int numArgs;
+ struct Arg {
+ char type;
+ union {
+ int d;
+ char *s;
+ };
+ } *args;
+
+ public:
+ ObjectCommand (CommandType type, tools::CommonLineInfo *info,
+ const char *fmt, ...);
+ ~ObjectCommand ();
+ };
+
+ ObjectsController *successor;
+ lout::container::typed::Vector<ObjectCommand> *commandsQueue;
+ bool queued;
+
+ void process (ObjectCommand *command);
+ void queue (ObjectCommand *command);
+ void pass (ObjectCommand *command);
+
+public:
+ ObjectsBuffer (ObjectsController *successor);
+ ~ObjectsBuffer ();
+
+ void objMsg (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMark (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMsgStart (tools::CommonLineInfo *info, const char *id);
+ void objMsgEnd (tools::CommonLineInfo *info, const char *id);
+ void objEnter (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args);
+ void objLeave (tools::CommonLineInfo *info, const char *id,
+ const char *vals);
+ void objCreate (tools::CommonLineInfo *info, const char *id,
+ const char *klass);
+ void objIdent (tools::CommonLineInfo *info, const char *id1,
+ const char *id2);
+ void objNoIdent (tools::CommonLineInfo *info);
+ void objAssoc (tools::CommonLineInfo *info, const char *parent,
+ const char *child);
+ void objSet (tools::CommonLineInfo *info, const char *id, const char *var,
+ const char *val);
+ void objClassColor (tools::CommonLineInfo *info, const char *klass,
+ const char *color);
+ void objObjectColor (tools::CommonLineInfo *info, const char *id,
+ const char *color);
+ void objDelete (tools::CommonLineInfo *info, const char *id);
+
+ void queue ();
+ void pass ();
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJECTS_BUFFER_HH__
diff --git a/objects/objects_parser.cc b/objects/objects_parser.cc
new file mode 100644
index 0000000..9278b56
--- /dev/null
+++ b/objects/objects_parser.cc
@@ -0,0 +1,401 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "objects_parser.hh"
+
+#if 0
+# define PRINT(fmt) printf ("---- [%p] " fmt "\n", this)
+# define PRINTF(fmt, ...) printf ("---- [%p] " fmt "\n", this, __VA_ARGS__)
+#else
+# define PRINT(fmt)
+# define PRINTF(fmt, ...)
+#endif
+
+using namespace rtfl::tools;
+
+namespace rtfl {
+
+namespace objects {
+
+namespace timeout {
+
+inline int getActualType (int type)
+{
+ return type >> 8;
+}
+
+inline int getCount (int type)
+{
+ return type & 0xff;
+}
+
+inline int makeType (int actualType, int count)
+{
+ return count | (actualType << 8);
+}
+
+inline int incType (int type)
+{
+ return makeType (getActualType (type), getCount (type) + 1);
+}
+
+inline int decType (int type)
+{
+ return makeType (getActualType (type), getCount (type) - 1);
+}
+
+} // namespace timeout
+
+ObjectsControllerBase::ObjectsControllerBase()
+{
+ predessor = NULL;
+ successor = NULL;
+}
+
+void ObjectsControllerBase::addTimeout (double secs, int type)
+{
+ PRINTF ("ObjectsControllerBase::addTimeout (%g, %d)", secs, type);
+
+ if (predessor)
+ predessor->addTimeout (secs, timeout::incType (type));
+ else
+ fprintf (stderr, "addTimeout (%g, %d): no predessor\n", secs, type);
+}
+
+void ObjectsControllerBase::removeTimeout (int type)
+{
+ if (predessor)
+ predessor->removeTimeout (timeout::incType (type));
+ else
+ fprintf (stderr, "removeTimeout (%d): no predessor\n", type);
+}
+
+void ObjectsControllerBase::setObjectsSource (ObjectsSource *source)
+{
+ predessor = source;
+}
+
+void ObjectsControllerBase::timeout (int type)
+{
+ PRINTF ("ObjectsControllerBase::timeout (%d)", type);
+
+ // We start sending count == 1 to the predessor, so we get back count == 1
+ // from it at the end.
+ if (timeout::getCount (type) == 1)
+ ownTimeout (timeout::getActualType (type));
+ else {
+ if (successor)
+ successor->timeout (timeout::decType (type));
+ else
+ fprintf (stderr, "timeout (%d): no successor\n", type);
+ }
+}
+
+void ObjectsControllerBase::finish ()
+{
+ ownFinish ();
+
+ if (successor)
+ successor->finish ();
+}
+
+void ObjectsControllerBase::setObjectsSink (ObjectsSink *sink)
+{
+ successor = sink;
+}
+
+void ObjectsControllerBase::addOwnTimeout (double secs, int type)
+{
+ PRINTF ("ObjectsControllerBase::addOwnTimeout (%g, %d)", secs, type);
+ addTimeout (secs, timeout::makeType (0, type));
+}
+
+void ObjectsControllerBase::removeOwnTimeout (int type)
+{
+ removeTimeout (timeout::makeType (0, type));
+}
+
+void ObjectsControllerBase::ownTimeout (int type)
+{
+ // No implementation.
+}
+
+void ObjectsControllerBase::ownFinish ()
+{
+ // No implementation.
+}
+
+// ----------------------------------------------------------------------
+
+ObjectsParser::ObjectsParser (ObjectsController *controller)
+{
+ this->controller = controller;
+ source = NULL;
+ controller->setObjectsSource (this);
+}
+
+ObjectsParser::~ObjectsParser ()
+{
+ controller->setObjectsSource (NULL);
+}
+
+void ObjectsParser::processCommand (CommonLineInfo *info, char *cmd, char *args)
+{
+ char **parts = NULL;
+
+ if (args == NULL)
+ // All commands need arguments here.
+ fprintf (stderr, "Missing arguments:%s\n", info->completeLine);
+ else if (strcmp (cmd, "obj-msg") == 0) {
+ parts = split (args, 4);
+ if (parts[1] && parts[2] && parts[3])
+ controller->objMsg (info, parts[0], parts[1], atoi(parts[2]),
+ parts[3]);
+ else
+ fprintf (stderr, "Incomplete line (obj-msg):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-mark") == 0) {
+ parts = split (args, 4);
+ if (parts[1] && parts[2] && parts[3])
+ controller->objMark (info, parts[0], parts[1], atoi(parts[2]),
+ parts[3]);
+ else
+ fprintf (stderr, "Incomplete line (obj-mark):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-msg-start") == 0)
+ controller->objMsgStart (info, args);
+ else if (strcmp (cmd, "obj-msg-end") == 0)
+ controller->objMsgEnd (info, args);
+ else if (strcmp (cmd, "obj-enter") == 0) {
+ parts = split (args, 5);
+ if (parts[1] && parts[2] && parts[3] && parts[4])
+ controller->objEnter (info, parts[0], parts[1], atoi(parts[2]),
+ parts[3], parts[4]);
+ else
+ fprintf (stderr, "Incomplete line (obj-enter):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-leave") == 0)
+ // Pre-version "obj-leave" does not support values.
+ controller->objLeave (info, args, NULL);
+ else if (strcmp (cmd, "obj-create") == 0) {
+ parts = split (args, 2);
+ if (parts[1])
+ controller->objCreate (info, parts[0], parts[1]);
+ else
+ fprintf (stderr, "Incomplete line (obj-create):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-ident") == 0) {
+ parts = split (args, 2);
+ if (parts[1])
+ controller->objIdent (info, parts[0], parts[1]);
+ else
+ fprintf (stderr, "Incomplete line (obj-ident):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-assoc") == 0) {
+ parts = split (args, 2);
+ if (parts[1])
+ controller->objAssoc (info, parts[0], parts[1]);
+ else
+ fprintf (stderr, "Incomplete line (obj-assoc):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-set") == 0) {
+ parts = split (args, 3);
+ if (parts[1] && parts[2])
+ controller->objSet (info, parts[0], parts[1], parts[2]);
+ else
+ fprintf (stderr, "Incomplete line (obj-set):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-color") == 0) {
+ parts = split (args, 2);
+ if (parts[1]) {
+ fprintf (stderr, "Warning: obj-color is deprecated; use "
+ "obj-class-color instead:\n%s\n", info->completeLine);
+ controller->objClassColor (info, parts[1], parts[0]);
+ } else
+ fprintf (stderr, "Incomplete line (obj-color):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-class-color") == 0) {
+ parts = split (args, 2);
+ if (parts[1])
+ controller->objClassColor (info, parts[1], parts[0]);
+ else
+ fprintf (stderr, "Incomplete line (obj-class-color):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-object-color") == 0) {
+ parts = split (args, 2);
+ if (parts[1])
+ controller->objObjectColor (info, parts[0], parts[1]);
+ else
+ fprintf (stderr, "Incomplete line (obj-object-color):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "obj-delete") == 0)
+ controller->objDelete (info, args);
+ else
+ fprintf (stderr, "Unknown command identifier '%s':\n%s\n", cmd,
+ info->completeLine);
+
+ if (parts)
+ freeSplit (parts);
+}
+
+void ObjectsParser::processVCommand (CommonLineInfo *info, const char *module,
+ int majorVersion, int minorVersion,
+ const char *cmd, char **args)
+{
+ if (strcmp (module, "obj") == 0) {
+ if (majorVersion > 1)
+ fprintf (stderr, "Last supported version is 1.0:\n%s\n",
+ info->completeLine);
+ if (args[0] == NULL) {
+ if (strcmp (cmd, "noident") == 0)
+ controller->objNoIdent (info);
+ else
+ // All other commands need arguments.
+ fprintf (stderr, "Missing arguments:%s\n", info->completeLine);
+ } else if (strcmp (cmd, "msg") == 0) {
+ if (args[1] && args[2] && args[3])
+ controller->objMsg (info, args[0], args[1], atoi(args[2]), args[3]);
+ else
+ fprintf (stderr, "Incomplete line (msg):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "mark") == 0) {
+ if (args[1] && args[2] && args[3])
+ controller->objMark (info, args[0], args[1], atoi(args[2]),
+ args[3]);
+ else
+ fprintf (stderr, "Incomplete line (mark):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "msg-start") == 0)
+ controller->objMsgStart (info, args[0]);
+ else if (strcmp (cmd, "msg-end") == 0)
+ controller->objMsgEnd (info, args[0]);
+ else if (strcmp (cmd, "enter") == 0) {
+ if (args[1] && args[2] && args[3] && args[4])
+ controller->objEnter (info, args[0], args[1], atoi(args[2]),
+ args[3], args[4]);
+ else
+ fprintf (stderr, "Incomplete line (enter):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "leave") == 0)
+ // Args[1] may be NULL.
+ controller->objLeave (info, args[0], args[1]);
+ else if (strcmp (cmd, "create") == 0) {
+ if (args[1])
+ controller->objCreate (info, args[0], args[1]);
+ else
+ fprintf (stderr, "Incomplete line (create):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "ident") == 0) {
+ if (args[1])
+ controller->objIdent (info, args[0], args[1]);
+ else
+ fprintf (stderr, "Incomplete line (ident):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "assoc") == 0) {
+ if (args[1])
+ controller->objAssoc (info, args[0], args[1]);
+ else
+ fprintf (stderr, "Incomplete line (assoc):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "set") == 0) {
+ if (args[1] && args[2])
+ controller->objSet (info, args[0], args[1], args[2]);
+ else
+ fprintf (stderr, "Incomplete line (set):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "class-color") == 0) {
+ if (args[1])
+ // Notice the changed order.
+ controller->objClassColor (info, args[0], args[1]);
+ else
+ fprintf (stderr, "Incomplete line (class-color):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "object-color") == 0) {
+ if (args[1])
+ controller->objObjectColor (info, args[0], args[1]);
+ else
+ fprintf (stderr, "Incomplete line (object-color):\n%s\n",
+ info->completeLine);
+ } else if (strcmp (cmd, "delete") == 0)
+ controller->objDelete (info, args[0]);
+ else
+ fprintf (stderr, "Unknown command identifier '%s':\n%s\n", cmd,
+ info->completeLine);
+ }
+}
+
+void ObjectsParser::setLinesSource (LinesSource *source)
+{
+ this->source = source;
+}
+
+void ObjectsParser::timeout (int type)
+{
+ PRINTF ("ObjectsParser::timeout (%d)", type);
+
+ if (timeout::getCount (type) == 0)
+ ; // ownTimeout (timeout::getActualType (type));
+ else
+ controller->timeout (timeout::decType (type));
+}
+
+void ObjectsParser::finish ()
+{
+ controller->finish ();
+}
+
+void ObjectsParser::setObjectsSink (ObjectsSink *sink)
+{
+ // This would be the controller passed in the constructor.
+}
+
+void ObjectsParser::addTimeout (double secs, int type)
+{
+ PRINTF ("ObjectsParser::addTimeout (%g, %d)", secs, type);
+
+ if (source)
+ source->addTimeout (secs, timeout::incType (type));
+ else
+ fprintf (stderr, "addTimeout (%g, %d): no source\n", secs, type);
+}
+
+void ObjectsParser::removeTimeout (int type)
+{
+ if (source)
+ source->removeTimeout (timeout::incType (type));
+ else
+ fprintf (stderr, "removeTimeout (%d): no source\n", type);
+}
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objects_parser.hh b/objects/objects_parser.hh
new file mode 100644
index 0000000..e4dd2b0
--- /dev/null
+++ b/objects/objects_parser.hh
@@ -0,0 +1,115 @@
+#ifndef __OBJECTS_OBJECTS_PARSER_HH__
+#define __OBJECTS_OBJECTS_PARSER_HH__
+
+#include "common/parser.hh"
+#include "lout/object.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+class ObjectsSink;
+
+class ObjectsSource
+{
+public:
+ virtual void setObjectsSink (ObjectsSink *sink) = 0;
+ virtual void addTimeout (double secs, int type) = 0;
+ virtual void removeTimeout (int type) = 0;
+};
+
+
+class ObjectsSink
+{
+public:
+ virtual void setObjectsSource (ObjectsSource *source) = 0;
+ virtual void timeout (int type) = 0;
+ virtual void finish () = 0;
+};
+
+
+class ObjectsController: public lout::object::Object, public ObjectsSource,
+ public ObjectsSink
+{
+public:
+ virtual void objMsg (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message) = 0;
+ virtual void objMark (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message) = 0;
+ virtual void objMsgStart (tools::CommonLineInfo *info, const char *id) = 0;
+ virtual void objMsgEnd (tools::CommonLineInfo *info, const char *id) = 0;
+ virtual void objEnter (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args) = 0;
+ virtual void objLeave (tools::CommonLineInfo *info, const char *id,
+ const char *vals) = 0;
+ virtual void objCreate (tools::CommonLineInfo *info, const char *id,
+ const char *klass) = 0;
+ virtual void objIdent (tools::CommonLineInfo *info, const char *id1,
+ const char *id2) = 0;
+ virtual void objNoIdent (tools::CommonLineInfo *info) = 0;
+ virtual void objAssoc (tools::CommonLineInfo *info, const char *parent,
+ const char *child) = 0;
+ virtual void objSet (tools::CommonLineInfo *info, const char *id,
+ const char *var, const char *val) = 0;
+ virtual void objClassColor (tools::CommonLineInfo *info, const char *klass,
+ const char *color) = 0;
+ virtual void objObjectColor (tools::CommonLineInfo *info, const char *id,
+ const char *color) = 0;
+ virtual void objDelete (tools::CommonLineInfo *info, const char *id) = 0;
+};
+
+
+class ObjectsControllerBase: public ObjectsController
+{
+ ObjectsSource *predessor;
+ ObjectsSink *successor;
+
+protected:
+ virtual void ownTimeout (int type);
+ virtual void ownFinish ();
+ void addOwnTimeout (double secs, int type);
+ void removeOwnTimeout (int type);
+
+public:
+ ObjectsControllerBase();
+
+ void setObjectsSink (ObjectsSink *sink);
+ void addTimeout (double secs, int type);
+ void removeTimeout (int type);
+ void setObjectsSource (ObjectsSource *source);
+ void timeout (int type);
+ void finish ();
+};
+
+
+class ObjectsParser: public tools::Parser, public ObjectsSource
+{
+private:
+ ObjectsController *controller;
+ tools::LinesSource *source;
+
+protected:
+ void processCommand (tools::CommonLineInfo *info, char *cmd, char *args);
+ void processVCommand (tools::CommonLineInfo *info, const char *module,
+ int majorVersion, int minorVersion, const char *cmd,
+ char **args);
+
+public:
+ ObjectsParser (ObjectsController *controller);
+ ~ObjectsParser ();
+
+ void setLinesSource (tools::LinesSource *source);
+ void timeout (int type);
+ void finish ();
+
+ void setObjectsSink (ObjectsSink *sink);
+ void addTimeout (double secs, int type);
+ void removeTimeout (int type);
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJVIEW_PARSER_HH__
diff --git a/objects/objects_writer.cc b/objects/objects_writer.cc
new file mode 100644
index 0000000..b9c2581
--- /dev/null
+++ b/objects/objects_writer.cc
@@ -0,0 +1,145 @@
+/*
+ * RTFL
+ *
+ * Copyright 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 "objects_writer.hh"
+
+#define DBG_RTFL
+
+#include "debug_rtfl.hh"
+
+using namespace rtfl::tools;
+
+// If foreign messages ("F" stands for "foreign") are passed, the original
+// filename, line number and process id must be printed.
+
+#define F_RTFL_PRINT(module, version, cmd, fmt, ...) \
+ rtfl_print (module, version, info->fileName, info->lineNo, info->processId, \
+ "s:" fmt, cmd, __VA_ARGS__)
+
+#define F_RTFL_PRINT0(module, version, cmd) \
+ rtfl_print (module, version, info->fileName, info->lineNo, info->processId, \
+ "s", cmd)
+
+#define F_RTFL_OBJ_PRINT(cmd, fmt, ...) \
+ F_RTFL_PRINT ("obj", RTFL_OBJ_VERSION, cmd, fmt, __VA_ARGS__)
+
+#define F_RTFL_OBJ_PRINT0(cmd) \
+ F_RTFL_PRINT0 ("obj", RTFL_OBJ_VERSION, cmd)
+
+namespace rtfl {
+
+namespace objects {
+
+void ObjectsWriter::objMsg (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message)
+{
+ F_RTFL_OBJ_PRINT ("msg", "s:s:d:s", id, aspect, prio, message);
+}
+
+void ObjectsWriter::objMark (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message)
+{
+ F_RTFL_OBJ_PRINT ("mark", "s:s:d:s", id, aspect, prio, message);
+}
+
+void ObjectsWriter::objMsgStart (CommonLineInfo *info, const char *id)
+{
+ F_RTFL_OBJ_PRINT ("msg-start", "s", id);
+}
+
+void ObjectsWriter::objMsgEnd (CommonLineInfo *info, const char *id)
+{
+ F_RTFL_OBJ_PRINT ("msg-end", "s", id);
+}
+
+void ObjectsWriter::objEnter (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args)
+{
+ F_RTFL_OBJ_PRINT ("enter", "s:s:d:s:s", id, aspect, prio, funname, args);
+}
+
+void ObjectsWriter::objLeave (CommonLineInfo *info, const char *id,
+ const char *vals)
+{
+ if (vals)
+ F_RTFL_OBJ_PRINT ("leave", "s:s", id, vals);
+ else
+ F_RTFL_OBJ_PRINT ("leave", "s", id);
+}
+
+void ObjectsWriter::objCreate (CommonLineInfo *info, const char *id,
+ const char *klass)
+{
+ F_RTFL_OBJ_PRINT ("create", "s:s", id, klass);
+}
+
+void ObjectsWriter::objIdent (CommonLineInfo *info, const char *id1,
+ const char *id2)
+{
+ F_RTFL_OBJ_PRINT ("create", "s:s", id1, id2);
+}
+
+void ObjectsWriter::objNoIdent (CommonLineInfo *info)
+{
+ F_RTFL_OBJ_PRINT0 ("noident");
+}
+
+void ObjectsWriter::objAssoc (CommonLineInfo *info, const char *parent,
+ const char *child)
+{
+ F_RTFL_OBJ_PRINT ("assoc", "s:s", parent, child);
+}
+
+void ObjectsWriter::objSet (CommonLineInfo *info, const char *id,
+ const char *var, const char *val)
+{
+ F_RTFL_OBJ_PRINT ("set", "s:s:s", id, var, val);
+}
+
+void ObjectsWriter::objClassColor (CommonLineInfo *info, const char *klass,
+ const char *color)
+{
+ F_RTFL_OBJ_PRINT ("class-color", "s:s", klass, color);
+}
+
+void ObjectsWriter::objObjectColor (CommonLineInfo *info, const char *id,
+ const char *color)
+{
+ F_RTFL_OBJ_PRINT ("object-color", "s:s", id, color);
+}
+
+void ObjectsWriter::objDelete (CommonLineInfo *info, const char *id)
+{
+ F_RTFL_OBJ_PRINT ("delete", "s", id);
+}
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objects_writer.hh b/objects/objects_writer.hh
new file mode 100644
index 0000000..80e629e
--- /dev/null
+++ b/objects/objects_writer.hh
@@ -0,0 +1,44 @@
+#ifndef __OBJECTS_OBJECTS_WRITER_HH__
+#define __OBJECTS_OBJECTS_WRITER_HH__
+
+#include "objects_parser.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+class ObjectsWriter: public ObjectsControllerBase
+{
+public:
+ void objMsg (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMark (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMsgStart (tools::CommonLineInfo *info, const char *id);
+ void objMsgEnd (tools::CommonLineInfo *info, const char *id);
+ void objEnter (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args);
+ void objLeave (tools::CommonLineInfo *info, const char *id,
+ const char *vals);
+ void objCreate (tools::CommonLineInfo *info, const char *id,
+ const char *klass);
+ void objIdent (tools::CommonLineInfo *info, const char *id1,
+ const char *id2);
+ void objNoIdent (tools::CommonLineInfo *info);
+ void objAssoc (tools::CommonLineInfo *info, const char *parent,
+ const char *child);
+ void objSet (tools::CommonLineInfo *info, const char *id, const char *var,
+ const char *val);
+ void objClassColor (tools::CommonLineInfo *info, const char *klass,
+ const char *color);
+ void objObjectColor (tools::CommonLineInfo *info, const char *id,
+ const char *color);
+ void objDelete (tools::CommonLineInfo *info, const char *id);
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJVIEW_WRITER_HH__
diff --git a/objects/objident_controller.cc b/objects/objident_controller.cc
new file mode 100644
index 0000000..deaf2fd
--- /dev/null
+++ b/objects/objident_controller.cc
@@ -0,0 +1,339 @@
+/*
+ * RTFL
+ *
+ * Copyright 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 <stdio.h>
+#include "objident_controller.hh"
+
+using namespace lout::object;
+using namespace lout::misc;
+using namespace lout::container::untyped;
+using namespace rtfl::tools;
+
+#define TIMEOUT_SECS 1
+
+namespace rtfl {
+
+namespace objects {
+
+ObjIdentController::PostController::PostController (ObjectsController
+ *successor)
+{
+ this->successor = successor;
+ successor->setObjectsSource (this);
+ setObjectsSink (successor);
+
+ // The "canonocal" identity is stored both as key and as value. For
+ // this reason, the second argument could be "false". Check whether
+ // this is possible (order of delete etc.).
+ identities = new EquivalenceRelation (true, true);
+}
+
+ObjIdentController::PostController::~PostController ()
+{
+ delete identities;
+}
+
+void ObjIdentController::PostController::objMsg (CommonLineInfo *info,
+ const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ successor->objMsg (info, mapId (id), aspect, prio, message);
+}
+
+void ObjIdentController::PostController::objMark (CommonLineInfo *info,
+ const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ successor->objMark (info, mapId (id), aspect, prio, message);
+}
+
+void ObjIdentController::PostController::objMsgStart (CommonLineInfo *info,
+ const char *id)
+{
+ successor->objMsgStart (info, mapId (id));
+}
+
+void ObjIdentController::PostController::objMsgEnd (CommonLineInfo *info,
+ const char *id)
+{
+ successor->objMsgEnd (info, mapId (id));
+}
+
+void ObjIdentController::PostController::objEnter (CommonLineInfo *info,
+ const char *id,
+ const char *aspect, int prio,
+ const char *funname,
+ const char *args)
+{
+ successor->objEnter (info, mapId (id), aspect, prio, funname, args);
+}
+
+void ObjIdentController::PostController::objLeave (CommonLineInfo *info,
+ const char *id,
+ const char *vals)
+{
+ successor->objLeave (info, mapId (id), vals);
+}
+
+void ObjIdentController::PostController::objCreate (CommonLineInfo *info,
+ const char *id,
+ const char *klass)
+{
+ successor->objCreate (info, mapId (id), klass);
+}
+
+void ObjIdentController::PostController::objIdent (CommonLineInfo *info,
+ const char *id1,
+ const char *id2)
+{
+ // "obj-ident" is not delegated.
+}
+
+void ObjIdentController::PostController::objNoIdent (CommonLineInfo *info)
+{
+ // "obj-noident" is not delegated.
+}
+
+void ObjIdentController::PostController::objAssoc (CommonLineInfo *info,
+ const char *parent,
+ const char *child)
+{
+ successor->objAssoc (info, mapId (parent), mapId (child));
+}
+
+void ObjIdentController::PostController::objSet (CommonLineInfo *info,
+ const char *id,
+ const char *var,
+ const char *val)
+{
+ successor->objSet (info, mapId (id), var, val);
+}
+
+void ObjIdentController::PostController::objClassColor (CommonLineInfo *info,
+ const char *klass,
+ const char *color)
+{
+ successor->objClassColor (info, klass, color);
+}
+
+void ObjIdentController::PostController::objObjectColor (CommonLineInfo *info,
+ const char *id,
+ const char *color)
+{
+ successor->objObjectColor (info, mapId (id), color);
+}
+
+void ObjIdentController::PostController::objDelete (CommonLineInfo *info,
+ const char *id)
+{
+ successor->objDelete (info, mapId (id));
+
+ // TODO Remove from identities.
+}
+
+void ObjIdentController::PostController::addIdentity (const char *id1,
+ const char *id2)
+{
+ String key1 (id1), key2 (id2);
+
+ if (!identities->contains (&key1))
+ identities->put (new String (id1), new String (id1));
+
+ if (!identities->contains (&key2))
+ identities->put (new String (id2), new String (id2));
+
+ identities->relate (&key1, &key2);
+}
+
+const char *ObjIdentController::PostController::mapId (const char *id)
+{
+ String key (id);
+ if (!identities->contains (&key))
+ identities->put (new String (id), new String (id));
+
+ return ((String*)identities->get(&key))->chars ();
+}
+
+// ----------------------------------------------------------------------
+
+ObjIdentController::ObjIdentController (ObjectsController *successor)
+{
+ noIdent = false;
+ stackDepth = 0;
+ createPending = false;
+
+ postController = new PostController (successor);
+ buffer = new ObjectsBuffer (postController);
+ buffer->setObjectsSource (this);
+ setObjectsSink (buffer);
+}
+
+ObjIdentController::~ObjIdentController ()
+{
+}
+
+void ObjIdentController::objMsg (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ buffer->objMsg (info, id, aspect, prio, message);
+}
+
+void ObjIdentController::objMark (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ buffer->objMark (info, id, aspect, prio, message);
+}
+
+void ObjIdentController::objMsgStart (CommonLineInfo *info, const char *id)
+{
+ buffer->objMsgStart (info, id);
+}
+
+void ObjIdentController::objMsgEnd (CommonLineInfo *info, const char *id)
+{
+ buffer->objMsgEnd (info, id);
+}
+
+void ObjIdentController::objEnter (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *funname, const char *args)
+{
+ stackDepth++;
+
+ buffer->objEnter (info, id, aspect, prio, funname, args);
+}
+
+void ObjIdentController::objLeave (CommonLineInfo *info, const char *id,
+ const char *vals)
+{
+ stackDepth = max (stackDepth - 1, 0);
+
+ if (createPending && stackDepth < minCreateStackDepth) {
+ pass ();
+ removeOwnTimeout (PASS);
+ }
+
+ buffer->objLeave (info, id, vals);
+}
+
+void ObjIdentController::objCreate (CommonLineInfo *info, const char *id,
+ const char *klass)
+{
+ queue ();
+ buffer->objCreate (info, id, klass);
+}
+
+void ObjIdentController::objIdent (CommonLineInfo *info, const char *id1,
+ const char *id2)
+{
+ postController->addIdentity (id1, id2);
+
+ // TODO: Possibly end queueing? Probably not.
+
+ // "obj-ident" is not delegated.
+}
+
+void ObjIdentController::objNoIdent (CommonLineInfo *info)
+{
+ noIdent = true;
+ pass ();
+
+ // "obj-noident" is not delegated.
+}
+
+void ObjIdentController::objAssoc (CommonLineInfo *info, const char *parent,
+ const char *child)
+{
+ buffer->objAssoc (info, parent, child);
+}
+
+void ObjIdentController::objSet (CommonLineInfo *info, const char *id,
+ const char *var, const char *val)
+{
+ buffer->objSet (info, id, var, val);
+}
+
+void ObjIdentController::objClassColor (CommonLineInfo *info, const char *klass,
+ const char *color)
+{
+ buffer->objClassColor (info, klass, color);
+}
+
+void ObjIdentController::objObjectColor (CommonLineInfo *info, const char *id,
+ const char *color)
+{
+ buffer->objObjectColor (info, id, color);
+}
+
+void ObjIdentController::objDelete (CommonLineInfo *info, const char *id)
+{
+ buffer->objDelete (info, id);
+}
+
+void ObjIdentController::ownTimeout (int type)
+{
+ if (type == PASS)
+ pass ();
+}
+
+void ObjIdentController::ownFinish ()
+{
+ pass ();
+}
+
+void ObjIdentController::queue ()
+{
+ if (!noIdent) {
+ if (createPending)
+ minCreateStackDepth = min (minCreateStackDepth, stackDepth);
+ else {
+ buffer->queue ();
+ createPending = true;
+ minCreateStackDepth = stackDepth;
+ }
+
+ removeOwnTimeout (PASS);
+ addOwnTimeout (TIMEOUT_SECS, PASS);
+ }
+}
+
+void ObjIdentController::pass ()
+{
+ buffer->pass ();
+ createPending = false;
+}
+
+} // namespace objects
+
+} // namespace rtfl
+
diff --git a/objects/objident_controller.hh b/objects/objident_controller.hh
new file mode 100644
index 0000000..2d83b20
--- /dev/null
+++ b/objects/objident_controller.hh
@@ -0,0 +1,192 @@
+#ifndef __OBJECTS_OBJIDENT_CONTROLLER_HH__
+#define __OBJECTS_OBJIDENT_CONTROLLER_HH__
+
+#include "objects_parser.hh"
+#include "objects_buffer.hh"
+#include "common/tools.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+/**
+ * \ident Filter Controller for handling `obj-ident`
+ *
+ * Motivation
+ * ----------
+ * When using multiple inheritance in C++, the values for `this` are not
+ * identical in all contexts. Consider a class `C`, with `A` and `B` as base
+ * classes. The following code
+ *
+ * C *c = new C();
+ * A *a = (A*)c;
+ * B *b = (B*)c;
+ * printf("a = %p\n", a);
+ * printf("b = %p\n", b);
+ * printf("c = %p\n", c);
+ *
+ * will output something like this:
+ *
+ * a = 0x1000
+ * b = 0x1010
+ * c = 0x1000
+ *
+ * (Notice the different value of `b`.) The value of `b` will differ from `a` by
+ * `sizeof(A)`.
+ *
+ * For this reason, the tested program has to declare all values as identical,
+ * in the following way:
+ *
+ * [rtfl-obj-1.0]...:create:0x1000:A
+ * [rtfl-obj-1.0]...:create:0x1010:B
+ * [rtfl-obj-1.0]...:ident:0x1000:0x1000
+ * [rtfl-obj-1.0]...:ident:0x1000:0x1010
+ * [rtfl-obj-1.0]...:create:0x1000:C
+ *
+ * The first `ident` line declares `c` (the newly created instance of `C` as
+ * identical to `a` (`(A*)c`), the second declares it identical to `b`
+ * (`(B*)c`).
+ *
+ * General Approach
+ * ----------------
+ * Handling `obj-ident` is kept out of the specific implementations of
+ * `ObjectsController` and implemented in a filter, `ObjIdentController`, which
+ * will translate all identities to actually identical identities; the output of
+ * `ObjIdentController` would, in the example above, be:
+ *
+ * [rtfl-obj-1.0]...:create:0x1000:A
+ * [rtfl-obj-1.0]...:create:0x1000:B
+ * [rtfl-obj-1.0]...:create:0x1000:C
+ *
+ * Details
+ * -------
+ * The general problem is that some messages have to be changed (especially
+ * `...:create:0x1010:B` has to be changed to `...:create:0x1000:B`) before
+ * `obj-ident` is read. Even worse, it is not certain that `obj-ident` will
+ * follow at all: the second `obj-create` may actually define a _different_
+ * object. For this reason, starting with `obj-create`, all messages are held
+ * back until something happens that makes it certain that _no_ related
+ * `obj-ident` will follow.
+ *
+ * Notice that a functioning implementation is possible even if the second
+ * condition is incompletely defined and implemented.
+ *
+ * (For more complex class hierarchies, it is not sufficient to react on
+ * `obj-ident` itself, since another `obj-ident` may follow, dealing with the
+ * same identities.)
+ *
+ * When is it certain that no related `obj-ident` will follow?
+ *
+ * 1. When a method is left (`obj-leave`), in which the objects have been
+ * created.
+ * 2. At the end of the stream.
+ * 3. (Not an exact condition, but a compromise:) After a timeout of a couple of
+ * seconds; after some time, it can be assumed that the construction of
+ * objects is over.
+ *
+ * (May be extended. See rtfl::objects::ObjIdentController::objLeave,
+ * rtfl::objects::ObjIdentController::ownTimeout, and
+ * rtfl::objects::ObjIdentController::ownFinish.
+ *
+ * Nice to have
+ * ------------
+ * - Make timeouts configurable, also whether a timeout is triggered after a
+ * certain time after "obj-create" or a certain time after no commands (latter
+ * is currently implemented).
+ */
+class ObjIdentController: public ObjectsControllerBase
+{
+private:
+ class PostController: public ObjectsControllerBase
+ {
+ tools::EquivalenceRelation *identities;
+ ObjectsController *successor;
+
+ const char *mapId (const char *id);
+
+ public:
+ PostController (ObjectsController *successor);
+ ~PostController ();
+
+ void objMsg (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMark (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMsgStart (tools::CommonLineInfo *info, const char *id);
+ void objMsgEnd (tools::CommonLineInfo *info, const char *id);
+ void objEnter (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args);
+ void objLeave (tools::CommonLineInfo *info, const char *id,
+ const char *vals);
+ void objCreate (tools::CommonLineInfo *info, const char *id,
+ const char *klass);
+ void objIdent (tools::CommonLineInfo *info, const char *id1,
+ const char *id2);
+ void objNoIdent (tools::CommonLineInfo *info);
+ void objAssoc (tools::CommonLineInfo *info, const char *parent,
+ const char *child);
+ void objSet (tools::CommonLineInfo *info, const char *id, const char *var,
+ const char *val);
+ void objClassColor (tools::CommonLineInfo *info, const char *klass,
+ const char *color);
+ void objObjectColor (tools::CommonLineInfo *info, const char *id,
+ const char *color);
+ void objDelete (tools::CommonLineInfo *info, const char *id);
+
+ void addIdentity (const char *id1, const char *id2);
+ };
+
+ enum { PASS = 0 };
+
+ ObjectsBuffer *buffer;
+ PostController *postController;
+ bool noIdent;
+
+ int stackDepth;
+ bool createPending;
+ int minCreateStackDepth;
+
+ void queue ();
+ void pass ();
+
+protected:
+ void ownTimeout (int type);
+ void ownFinish ();
+
+public:
+ ObjIdentController (ObjectsController *successor);
+ ~ObjIdentController ();
+
+ void objMsg (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMark (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMsgStart (tools::CommonLineInfo *info, const char *id);
+ void objMsgEnd (tools::CommonLineInfo *info, const char *id);
+ void objEnter (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args);
+ void objLeave (tools::CommonLineInfo *info, const char *id,
+ const char *vals);
+ void objCreate (tools::CommonLineInfo *info, const char *id,
+ const char *klass);
+ void objIdent (tools::CommonLineInfo *info, const char *id1,
+ const char *id2);
+ void objNoIdent (tools::CommonLineInfo *info);
+ void objAssoc (tools::CommonLineInfo *info, const char *parent,
+ const char *child);
+ void objSet (tools::CommonLineInfo *info, const char *id, const char *var,
+ const char *val);
+ void objClassColor (tools::CommonLineInfo *info, const char *klass,
+ const char *color);
+ void objObjectColor (tools::CommonLineInfo *info, const char *id,
+ const char *color);
+ void objDelete (tools::CommonLineInfo *info, const char *id);
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJIDENT_CONTROLLER_HH__
diff --git a/objects/objview_commands.cc b/objects/objview_commands.cc
new file mode 100644
index 0000000..81d03aa
--- /dev/null
+++ b/objects/objview_commands.cc
@@ -0,0 +1,773 @@
+/*
+ * 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 "objview_graph.hh" // Because ObjViewGraph is needed here.
+
+using namespace lout::object;
+using namespace lout::container::typed;
+using namespace lout::misc;
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace rtfl::dw;
+
+namespace rtfl {
+
+namespace objects {
+
+static void scrollToLabel (Label *label)
+{
+ Widget *parent = label->getParent (), *child = label;
+
+ while (parent != NULL && !parent->instanceOf (ObjViewGraph::CLASS_ID)) {
+ if (parent->instanceOf (Toggle::CLASS_ID)) {
+ Toggle *toggle = (Toggle*)parent;
+ if (toggle->isLargeShown () && child == toggle->getSmall ())
+ toggle->toggle (false);
+ else if (!toggle->isLargeShown () && child == toggle->getLarge ())
+ toggle->toggle (true);
+ }
+
+ child = parent;
+ parent = parent->getParent ();
+ }
+
+ label->scrollTo (HPOS_INTO_VIEW, VPOS_INTO_VIEW);
+}
+
+// ----------------------------------------------------------------------
+
+bool OVGCommonCommand::CommandLinkReceiver::click (Widget *widget, int link,
+ int img, int x, int y,
+ EventButton *event)
+{
+ // "link" is "navigableCommandsIndex".
+ graph->setCommand (link);
+ return true;
+}
+
+// ----------------------------------------------------------------------
+
+OVGCommonCommand::OVGCommonCommand (ObjViewGraph *graph, const char *id,
+ ObjViewFunction *function)
+{
+ this->id = strdup (id);
+ this->graph = graph;
+ this->function = function;
+ relatedCommand = NULL;
+ executed = false;
+ visible = true;
+}
+
+OVGCommonCommand::~OVGCommonCommand ()
+{
+ free (id);
+}
+
+const char *OVGCommonCommand::getFileName ()
+{
+ // Not needed for non-navigable command.
+ assertNotReached ();
+ return NULL;
+}
+
+int OVGCommonCommand::getLineNo ()
+{
+ // Not needed for non-navigable command.
+ assertNotReached ();
+ return 0;
+}
+
+int OVGCommonCommand::getProcessId ()
+{
+ // Not yet clear what to do with this one.
+ assertNotReached ();
+ return 0;
+}
+
+void OVGCommonCommand::exec (ObjViewGraph *graph)
+{
+ assert (!executed);
+ assert (graph == this->graph);
+ doExec ();
+ executed = true;
+}
+
+void OVGCommonCommand::show (ObjViewGraph *graph)
+{
+ assert (executed);
+ assert (graph == this->graph);
+ visible = true;
+ doShow ();
+}
+
+void OVGCommonCommand::hide (ObjViewGraph *graph)
+{
+ assert (executed);
+ assert (graph == this->graph);
+ visible = false;
+ doHide ();
+}
+
+void OVGCommonCommand::scrollTo (ObjViewGraph *graph)
+{
+ assert (executed);
+ assert (graph == this->graph);
+ doScrollTo ();
+}
+
+void OVGCommonCommand::select (ObjViewGraph *graph)
+{
+ assert (executed);
+ assert (graph == this->graph);
+ doSelect ();
+}
+
+void OVGCommonCommand::unselect (ObjViewGraph *graph)
+{
+ assert (executed);
+ assert (graph == this->graph);
+ doUnselect ();
+}
+
+// Most commands do not have to impelemt these:
+
+void OVGCommonCommand::doShow ()
+{
+ assertNotReached ();
+}
+
+void OVGCommonCommand::doHide ()
+{
+ assertNotReached ();
+}
+
+void OVGCommonCommand::doScrollTo ()
+{
+ assertNotReached ();
+}
+
+void OVGCommonCommand::doSelect ()
+{
+ assertNotReached ();
+}
+
+void OVGCommonCommand::doUnselect ()
+{
+ assertNotReached ();
+}
+
+bool OVGCommonCommand::isVisible (ObjViewGraph *graph)
+{
+ return visible;
+}
+
+bool OVGCommonCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return true;
+}
+
+int OVGCommonCommand::getNavigableCommandsIndex (ObjViewGraph *graph)
+{
+ assertNotReached ();
+ return -1;
+}
+
+void OVGCommonCommand::setNavigableCommandsIndex (ObjViewGraph *graph,
+ int navigableCommandsIndex)
+{
+ assertNotReached ();
+}
+
+void OVGCommonCommand::setVisibleIndex (ObjViewGraph *graph,
+ int visibleIndex)
+{
+ assertNotReached ();
+}
+
+// ----------------------------------------------------------------------
+
+OVGNavigableCommand::OVGNavigableCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph, const char *id,
+ ObjViewFunction *function) :
+ OVGCommonCommand (graph, id, function)
+{
+ this->fileName = strdup (fileName);
+ this->lineNo = lineNo;
+
+ navigableCommandsIndex = -1;
+ label1 = label2 = NULL;
+ hbox = NULL;
+}
+
+OVGNavigableCommand::~OVGNavigableCommand ()
+{
+ free (fileName);
+}
+
+const char *OVGNavigableCommand::getFileName ()
+{
+ return fileName;
+}
+
+int OVGNavigableCommand::getLineNo ()
+{
+ return lineNo;
+}
+
+void OVGNavigableCommand::initWidgets ()
+{
+ label1->setLink (navigableCommandsIndex);
+ label1->connectLink (&linkReceiver);
+ label2->setLink (navigableCommandsIndex);
+ label2->connectLink (&linkReceiver);
+}
+
+void OVGNavigableCommand::removeWidgets ()
+{
+ delete hbox;
+
+ label1 = label2 = NULL;
+ hbox = NULL;
+}
+
+int OVGNavigableCommand::getNavigableCommandsIndex (ObjViewGraph *graph)
+{
+ return navigableCommandsIndex;
+}
+
+void OVGNavigableCommand::setNavigableCommandsIndex (ObjViewGraph *graph,
+ int navigableCommandsIndex)
+{
+ this->navigableCommandsIndex = navigableCommandsIndex;
+ if (label1)
+ label1->setLink (navigableCommandsIndex);
+ if (label2)
+ label2->setLink (navigableCommandsIndex);
+}
+
+void OVGNavigableCommand::setVisibleIndex (ObjViewGraph *graph,
+ int visibleIndex)
+{
+ assert (label1 != NULL);
+
+ // Assume 5 digits (99999) at max.
+ char buf[15];
+ snprintf (buf, 15, "<i>%d:</i> ", visibleIndex);
+ label1->setText (buf);
+}
+
+void OVGNavigableCommand::doShow ()
+{
+ label1->show ();
+ label2->show ();
+}
+
+void OVGNavigableCommand::doHide ()
+{
+ label1->hide ();
+ label2->hide ();
+}
+
+void OVGNavigableCommand::doScrollTo ()
+{
+ scrollToLabel (label2);
+}
+
+void OVGNavigableCommand::doSelect ()
+{
+ label1->select ();
+ label2->select ();
+}
+
+void OVGNavigableCommand::doUnselect ()
+{
+ label1->unselect ();
+ label2->unselect ();
+}
+
+// ----------------------------------------------------------------------
+
+OVGIncIndentCommand::OVGIncIndentCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph, const char *id,
+ ObjViewFunction *function) :
+ OVGNavigableCommand (fileName, lineNo, graph, id, function)
+{
+ linkReceiver.setData (graph, this);
+}
+
+OVGIncIndentCommand::~OVGIncIndentCommand ()
+{
+}
+
+bool OVGIncIndentCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return graph->filterTool->isTypeSelected (OVG_COMMAND_INDENT);
+}
+
+void OVGIncIndentCommand::doExec ()
+{
+ graph->addMessage (id, "<i>start</i>", &hbox, &label1, &label2);
+ initWidgets ();
+ success = graph->incIndent (id);
+}
+
+// ----------------------------------------------------------------------
+
+OVGDecIndentCommand::OVGDecIndentCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph, const char *id,
+ OVGIncIndentCommand *startCommand,
+ ObjViewFunction *function) :
+ OVGNavigableCommand (fileName, lineNo, graph, id, function)
+{
+ linkReceiver.setData (graph, this);
+
+ setRelatedCommand (startCommand);
+ startCommand->setRelatedCommand (this);
+}
+
+OVGDecIndentCommand::~OVGDecIndentCommand ()
+{
+}
+
+bool OVGDecIndentCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return graph->filterTool->isTypeSelected (OVG_COMMAND_INDENT);
+}
+
+void OVGDecIndentCommand::doExec ()
+{
+ success = graph->decIndent (id);
+ graph->addMessage (id, "<i>end</i>", &hbox, &label1, &label2);
+ initWidgets ();
+}
+
+// ----------------------------------------------------------------------
+
+OVGEnterCommand::OVGEnterCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph, const char *id,
+ const char *aspect, int prio,
+ const char *funname, const char *args,
+ ObjViewFunction *function) :
+ OVGNavigableCommand (fileName, lineNo, graph, id, function)
+{
+ linkReceiver.setData (graph, this);
+
+ graph->filterTool->addAspect (aspect);
+ graph->filterTool->addPriority (prio);
+
+ this->aspect = strdup (aspect);
+ this->prio = prio;
+ this->funname = strdup (funname);
+ this->args = strdup (args);
+}
+
+OVGEnterCommand::~OVGEnterCommand ()
+{
+ free (aspect);
+ free (funname);
+ free (args);
+}
+
+bool OVGEnterCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return graph->filterTool->isTypeSelected (OVG_COMMAND_FUNCTION) &&
+ graph->filterTool->isAspectSelected (aspect) &&
+ graph->filterTool->isPrioritySelected (prio);
+}
+
+void OVGEnterCommand::doExec ()
+{
+ char *message = createMessage ("<i>enter</i>: <b>", "</b> (", ")", "");
+ graph->addMessage (id, message, &hbox, &label1, &label2);
+ freeMessage (message);
+ initWidgets ();
+
+ success = graph->incIndent (id);
+}
+
+char *OVGEnterCommand::createMessage (const char *c1, const char *c2,
+ const char *c3a, const char *c3b)
+{
+ size_t l = strlen (c1) + strlen (funname) + strlen (c2) + strlen (args) +
+ strlen (c3a) + strlen (c3b) + 1;
+ char *message = new char[l];
+ snprintf (message, l, "%s%s%s%s%s%s", c1, funname, c2, args, c3a, c3b);
+ return message;
+}
+
+void OVGEnterCommand::freeMessage (char *message)
+{
+ delete[] message;
+}
+
+char *OVGEnterCommand::createName ()
+{
+ size_t l =
+ 1 + strlen (id) + 2 + strlen (funname) + 2 + strlen (args) + 1 + 1;
+ char *message = new char[l];
+ snprintf (message, l, "[%s] %s (%s)", id, funname, args);
+ return message;
+}
+
+void OVGEnterCommand::freeName (char *name)
+{
+ delete[] name;
+}
+
+int OVGEnterCommand::getColor ()
+{
+ return graph->getObjectColor (id);
+}
+
+ObjViewFunction *OVGEnterCommand::getParent ()
+{
+ return getFunction ();
+}
+
+bool OVGEnterCommand::isSelectable ()
+{
+ return isVisible (graph);
+}
+
+void OVGEnterCommand::select ()
+{
+ graph->clearSelection ();
+ graph->navigableCommandsPos = navigableCommandsIndex;
+ graph->unclearSelection ();
+}
+
+// ----------------------------------------------------------------------
+
+OVGLeaveCommand::OVGLeaveCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph, const char *id,
+ const char *vals,
+ OVGEnterCommand *enterCommand) :
+ OVGNavigableCommand (fileName, lineNo, graph, id, enterCommand)
+{
+ linkReceiver.setData (graph, this);
+
+ this->enterCommand = enterCommand;
+ this->vals = vals ? strdup (vals) : NULL;
+
+ setRelatedCommand (enterCommand);
+ enterCommand->setRelatedCommand (this);
+}
+
+OVGLeaveCommand::~OVGLeaveCommand ()
+{
+ if (vals)
+ free (vals);
+}
+
+bool OVGLeaveCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return enterCommand->calcVisibility (graph);
+}
+
+void OVGLeaveCommand::doExec ()
+{
+ success = graph->decIndent (id);
+
+ char *message = vals ?
+ enterCommand->createMessage ("<i>leave: ", "</i> (", ") ⇒ ", vals) :
+ enterCommand->createMessage ("<i>leave: ", "</i> (", ")", "");
+
+ graph->addMessage (id, message, &hbox, &label1, &label2);
+ enterCommand->freeMessage (message);
+ initWidgets ();
+}
+
+// ----------------------------------------------------------------------
+
+OVGAddMessageCommand::OVGAddMessageCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph,
+ const char *id, const char *aspect,
+ int prio, const char *message,
+ ObjViewFunction *function) :
+ OVGNavigableCommand (fileName, lineNo, graph, id, function)
+{
+ linkReceiver.setData (graph, this);
+
+ graph->filterTool->addAspect (aspect);
+ graph->filterTool->addPriority (prio);
+
+ this->aspect = strdup (aspect);
+ this->prio = prio;
+ this->message = strdup (message);
+}
+
+OVGAddMessageCommand::~OVGAddMessageCommand ()
+{
+ free (aspect);
+ free (message);
+}
+
+bool OVGAddMessageCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return graph->filterTool->isTypeSelected (OVG_COMMAND_MESSAGE) &&
+ graph->filterTool->isAspectSelected (aspect) &&
+ graph->filterTool->isPrioritySelected (prio);
+}
+
+void OVGAddMessageCommand::doExec ()
+{
+ graph->addMessage (id, message, &hbox, &label1, &label2);
+ initWidgets ();
+}
+
+// ----------------------------------------------------------------------
+
+OVGAddMarkCommand::OVGAddMarkCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph,
+ const char *id, const char *aspect,
+ int prio, const char *mark,
+ ObjViewFunction *function) :
+ OVGNavigableCommand (fileName, lineNo, graph, id, function)
+{
+ linkReceiver.setData (graph, this);
+
+ graph->filterTool->addAspect (aspect);
+ graph->filterTool->addPriority (prio);
+
+ this->aspect = strdup (aspect);
+ this->prio = prio;
+ this->mark = strdup (mark);
+}
+
+OVGAddMarkCommand::~OVGAddMarkCommand ()
+{
+ free (aspect);
+ free (mark);
+}
+
+bool OVGAddMarkCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return graph->filterTool->isTypeSelected (OVG_COMMAND_MARK) &&
+ graph->filterTool->isAspectSelected (aspect) &&
+ graph->filterTool->isPrioritySelected (prio);
+}
+
+void OVGAddMarkCommand::doExec ()
+{
+ size_t l = 13 + strlen (mark) + 1;
+ char *message = new char[l];
+ snprintf (message, l, "<i>mark:</i> %s", mark);
+
+ graph->addMessage (id, message, &hbox, &label1, &label2);
+ initWidgets ();
+
+ delete[] message;
+}
+
+bool OVGAddMarkCommand::isSelectable ()
+{
+ return isVisible (graph);
+}
+
+void OVGAddMarkCommand::select ()
+{
+ graph->clearSelection ();
+ graph->navigableCommandsPos = navigableCommandsIndex;
+ graph->unclearSelection ();
+}
+
+// ----------------------------------------------------------------------
+
+OVGCreateCommand::OVGCreateCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph, const char *id,
+ const char *className,
+ ObjViewFunction *function):
+ OVGNavigableCommand (fileName, lineNo, graph, id, function)
+{
+ this->className = strdup (className);
+ linkReceiver.setData (graph, this);
+}
+
+OVGCreateCommand::~OVGCreateCommand ()
+{
+ free (className);
+}
+
+bool OVGCreateCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return graph->filterTool->isTypeSelected (OVG_COMMAND_CREATE);
+}
+
+void OVGCreateCommand::doExec ()
+{
+ size_t l = 15 + strlen (className) + 1;
+ char *message = new char[l];
+ snprintf (message, l, "<i>create:</i> %s", className);
+
+ graph->addMessage (id, message, &hbox, &label1, &label2);
+ initWidgets ();
+
+ delete[] message;
+
+ graph->setClassName (id, className);
+}
+
+// ----------------------------------------------------------------------
+
+OVGAddAssocCommand::OVGAddAssocCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph,
+ const char *id1, const char *id2,
+ ObjViewFunction *function) :
+ OVGNavigableCommand (fileName, lineNo, graph, id1, function)
+{
+ linkReceiver.setData (graph, this);
+ this->id2 = strdup (id2);
+}
+
+OVGAddAssocCommand::~OVGAddAssocCommand ()
+{
+ free (id2);
+}
+
+bool OVGAddAssocCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return graph->filterTool->isTypeSelected (OVG_COMMAND_ASSOC);
+}
+
+void OVGAddAssocCommand::doExec ()
+{
+ size_t l = 14 + strlen (id2) + 5 + 1;
+ char *message = new char[l];
+ snprintf (message, l, "<i>assoc → %s</i>", id2);
+
+ graph->addMessage (id, message, &hbox, &label1, &label2);
+ initWidgets ();
+
+ delete[] message;
+
+ graph->addAssoc (id, id2);
+}
+
+// ----------------------------------------------------------------------
+
+OVGAddAttrCommand::OVGAddAttrCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph, const char *id,
+ const char *name,const char *value,
+ ObjViewFunction *function) :
+ OVGNavigableCommand (fileName, lineNo, graph, id, function)
+{
+ linkReceiver.setData (graph, this);
+
+ this->name = strdup (name);
+ this->value = strdup (value);
+
+ smallLabel = NULL;
+ attribute = NULL;
+ childNo = -1;
+}
+
+OVGAddAttrCommand::~OVGAddAttrCommand ()
+{
+ free (name);
+ free (value);
+}
+
+bool OVGAddAttrCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return graph->filterTool->isTypeSelected (OVG_COMMAND_ADD_ATTR);
+}
+
+void OVGAddAttrCommand::doExec ()
+{
+ attribute = graph->addAttribute (id, name, value, &smallLabel, NULL, &label1,
+ &label2);
+ childNo = attribute->registerChild ();
+ initWidgets ();
+}
+
+void OVGAddAttrCommand::doShow ()
+{
+ OVGNavigableCommand::doShow ();
+
+ // Set the appropriate current value (seen when the history is
+ // hidden). It can be assumed that doShow() is called in the order
+ // of the creation, so the last visible command will stay; this
+ // does indeed represent the current value.
+ assert (smallLabel != NULL);
+ smallLabel->setText (value);
+
+ // ...
+ assert (attribute != NULL);
+ assert (childNo != -1);
+ attribute->childShown (childNo);
+}
+
+void OVGAddAttrCommand::doHide ()
+{
+ OVGNavigableCommand::doHide ();
+
+ // ...
+ assert (attribute != NULL);
+ assert (childNo != -1);
+ attribute->childHidden (childNo);
+}
+
+// ----------------------------------------------------------------------
+
+OVGDeleteCommand::OVGDeleteCommand (const char *fileName, int lineNo,
+ ObjViewGraph *graph, const char *id,
+ ObjViewFunction *function) :
+ OVGNavigableCommand (fileName, lineNo, graph, id, function)
+{
+ linkReceiver.setData (graph, this);
+}
+
+OVGDeleteCommand::~OVGDeleteCommand ()
+{
+}
+
+bool OVGDeleteCommand::calcVisibility (ObjViewGraph *graph)
+{
+ return graph->filterTool->isTypeSelected (OVG_COMMAND_DELETE);
+}
+
+void OVGDeleteCommand::doExec ()
+{
+ graph->addMessage (id, "<i>delete</i>", &hbox, &label1, &label2);
+ initWidgets ();
+
+ // Change appearance of window (simple experiment).
+ //Widget *node = graph->ensureObject(id)->node;
+ //StyleAttrs attrs = *(node->getStyle());
+ //attrs.setBorderStyle (BORDER_DOTTED);
+ //node->setStyle (Style::create (&attrs));
+
+ // Furthermore, this message is added often several times, since this command
+ // is printed in all destructors in the class hierarchie. Should be limited
+ // to once.
+}
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objview_commands.hh b/objects/objview_commands.hh
new file mode 100644
index 0000000..8e81e99
--- /dev/null
+++ b/objects/objview_commands.hh
@@ -0,0 +1,349 @@
+#ifndef __OBJECTS_OBJVIEW_COMMANDS_HH__
+#define __OBJECTS_OBJVIEW_COMMANDS_HH__
+
+#include "objview_stacktrace.hh"
+#include "dwr/toggle.hh"
+#include "dwr/vbox.hh"
+#include "dwr/hbox.hh"
+#include "dwr/label.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+class ObjViewGraph;
+class OVGAttribute;
+
+/*
+ * TODO: OVGCommand was originally introduced to provide a undo/redo mechanism
+ * needed for handling "obj-ident". Now, after "obj-ident" is handled by
+ * ObjIdentController, OVGCommand can be simplified and reduced to navigable
+ * commands.
+ */
+
+class OVGCommand: public lout::object::Object
+{
+public:
+ virtual const char *getFileName () = 0;
+ virtual int getLineNo () = 0;
+ virtual int getProcessId () = 0;
+
+ virtual void exec (ObjViewGraph *graph) = 0;
+ virtual void show (ObjViewGraph *graph) = 0;
+ virtual void hide (ObjViewGraph *graph) = 0;
+ virtual void scrollTo (ObjViewGraph *graph) = 0;
+ virtual void select (ObjViewGraph *graph) = 0;
+ virtual void unselect (ObjViewGraph *graph) = 0;
+ virtual bool isVisible (ObjViewGraph *graph) = 0;
+ virtual bool calcVisibility (ObjViewGraph *graph) = 0;
+ virtual int getNavigableCommandsIndex (ObjViewGraph *graph) = 0;
+ virtual void setNavigableCommandsIndex (ObjViewGraph *graph,
+ int navigableCommandsIndex) = 0;
+ virtual void setVisibleIndex (ObjViewGraph *graph,
+ int visibleIndex) = 0;
+ virtual ObjViewFunction *getFunction () = 0;
+ virtual void setRelatedCommand (OVGCommand *relatedCommand) = 0;
+ virtual OVGCommand *getRelatedCommand () = 0;
+};
+
+
+class OVGCommonCommand: public OVGCommand
+{
+private:
+ bool executed, visible;
+ ObjViewFunction *function;
+ OVGCommand *relatedCommand;
+
+protected:
+ class CommandLinkReceiver: public ::dw::core::Layout::LinkReceiver
+ {
+ private:
+ ObjViewGraph *graph;
+ OVGCommand *command;
+
+ public:
+ inline CommandLinkReceiver () { graph = NULL; }
+ inline void setData (ObjViewGraph *graph,
+ OVGCommand *command)
+ { this->graph = graph; this->command = command; }
+
+ bool click (::dw::core::Widget *widget, int link, int img, int x, int y,
+ ::dw::core::EventButton *event);
+ };
+
+ char *id;
+ ObjViewGraph *graph;
+
+ virtual void doExec () = 0;
+ virtual void doShow ();
+ virtual void doHide ();
+ virtual void doScrollTo ();
+ virtual void doSelect ();
+ virtual void doUnselect ();
+
+public:
+ OVGCommonCommand (ObjViewGraph *graph, const char *id,
+ ObjViewFunction *function);
+ ~OVGCommonCommand ();
+
+ const char *getFileName ();
+ int getLineNo ();
+ int getProcessId ();
+
+ void exec (ObjViewGraph *graph);
+ void show (ObjViewGraph *graph);
+ void hide (ObjViewGraph *graph);
+ void scrollTo (ObjViewGraph *graph);
+ void select (ObjViewGraph *graph);
+ void unselect (ObjViewGraph *graph);
+ bool isVisible (ObjViewGraph *graph);
+ bool calcVisibility (ObjViewGraph *graph);
+ int getNavigableCommandsIndex (ObjViewGraph *graph);
+ void setNavigableCommandsIndex (ObjViewGraph *graph,
+ int navigableCommandsIndex);
+ void setVisibleIndex (ObjViewGraph *graph, int visibleIndex);
+ ObjViewFunction *getFunction () { return function; }
+ void setRelatedCommand (OVGCommand *relatedCommand)
+ { this->relatedCommand = relatedCommand; }
+ OVGCommand *getRelatedCommand () { return relatedCommand; }
+};
+
+
+class OVGNavigableCommand: public OVGCommonCommand
+{
+private:
+ char *fileName;
+ int lineNo;
+
+protected:
+ int navigableCommandsIndex;
+ dw::HBox *hbox;
+ dw::Label *label1, *label2;
+ CommandLinkReceiver linkReceiver;
+
+ void doShow ();
+ void doHide ();
+ void doScrollTo ();
+ void doSelect ();
+ void doUnselect ();
+
+ void initWidgets ();
+ void removeWidgets ();
+
+public:
+ OVGNavigableCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, ObjViewFunction *function);
+ ~OVGNavigableCommand ();
+
+ const char *getFileName ();
+ int getLineNo ();
+
+ int getNavigableCommandsIndex (ObjViewGraph *graph);
+ void setNavigableCommandsIndex (ObjViewGraph *graph,
+ int navigableCommandsIndex);
+ void setVisibleIndex (ObjViewGraph *graph, int visibleIndex);
+};
+
+class OVGIncIndentCommand: public OVGNavigableCommand
+{
+private:
+ bool success;
+
+protected:
+ void doExec ();
+
+public:
+ OVGIncIndentCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, ObjViewFunction *function);
+ ~OVGIncIndentCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+};
+
+
+class OVGDecIndentCommand: public OVGNavigableCommand
+{
+private:
+ bool success;
+
+protected:
+ void doExec ();
+
+public:
+ OVGDecIndentCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, OVGIncIndentCommand *startCommand,
+ ObjViewFunction *function);
+ ~OVGDecIndentCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+};
+
+class OVGEnterCommand: public OVGNavigableCommand, public ObjViewFunction
+{
+private:
+ char *aspect, *funname, *args;
+ int prio;
+ bool success;
+
+protected:
+ void doExec ();
+
+public:
+ OVGEnterCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, const char *aspect, int prio,
+ const char *funname, const char *args,
+ ObjViewFunction *function);
+ ~OVGEnterCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+
+ char *createMessage (const char *c1, const char *c2, const char *c3a,
+ const char *c3b);
+ void freeMessage (char *message);
+
+ char *createName ();
+ void freeName (char *name);
+ int getColor ();
+ ObjViewFunction *getParent ();
+ bool isSelectable ();
+ void select ();
+};
+
+class OVGLeaveCommand: public OVGNavigableCommand
+{
+private:
+ OVGEnterCommand *enterCommand;
+ char *vals;
+ bool success;
+
+protected:
+ void doExec ();
+
+public:
+ OVGLeaveCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, const char *vals,
+ OVGEnterCommand *enterCommand);
+ ~OVGLeaveCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+};
+
+class OVGAddMessageCommand: public OVGNavigableCommand
+{
+private:
+ char *aspect, *message;
+ int prio;
+
+protected:
+ void doExec ();
+
+public:
+ OVGAddMessageCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, const char *aspect, int prio,
+ const char *message, ObjViewFunction *function);
+ ~OVGAddMessageCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+};
+
+
+class OVGAddMarkCommand: public OVGNavigableCommand
+{
+private:
+ char *aspect, *mark;
+ int prio;
+
+protected:
+ void doExec ();
+
+public:
+ OVGAddMarkCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, const char *aspect, int prio,
+ const char *mark, ObjViewFunction *function);
+ ~OVGAddMarkCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+
+ const char *getMark () { return mark; }
+ bool isSelectable ();
+ void select ();
+};
+
+
+class OVGCreateCommand: public OVGNavigableCommand
+{
+private:
+ char *className;
+
+protected:
+ void doExec ();
+
+public:
+ OVGCreateCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, const char *className,
+ ObjViewFunction *function);
+ ~OVGCreateCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+};
+
+
+class OVGAddAssocCommand: public OVGNavigableCommand
+{
+private:
+ char *id2;
+
+protected:
+ void doExec ();
+
+public:
+ OVGAddAssocCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id1, const char *id2,
+ ObjViewFunction *function);
+ ~OVGAddAssocCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+};
+
+
+class OVGAddAttrCommand: public OVGNavigableCommand
+{
+private:
+ char *name, *value;
+ dw::Label *smallLabel;
+ OVGAttribute *attribute;
+ int childNo;
+
+protected:
+ void doExec ();
+ void doShow ();
+ void doHide ();
+
+public:
+ OVGAddAttrCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, const char *name, const char *value,
+ ObjViewFunction *function);
+ ~OVGAddAttrCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+};
+
+
+class OVGDeleteCommand: public OVGNavigableCommand
+{
+protected:
+ void doExec ();
+
+public:
+ OVGDeleteCommand (const char *fileName, int lineNo, ObjViewGraph *graph,
+ const char *id, ObjViewFunction *function);
+ ~OVGDeleteCommand ();
+
+ bool calcVisibility (ObjViewGraph *graph);
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJVIEW_COMMANDS_HH__
diff --git a/objects/objview_controller.cc b/objects/objview_controller.cc
new file mode 100644
index 0000000..8d40dbc
--- /dev/null
+++ b/objects/objview_controller.cc
@@ -0,0 +1,171 @@
+/*
+ * 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 <stdio.h>
+#include "objview_controller.hh"
+
+using namespace rtfl::tools;
+
+namespace rtfl {
+
+namespace objects {
+
+void ObjViewController::objMsg (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ graph->addCommand (new OVGAddMessageCommand (info->fileName, info->lineNo,
+ graph, id, aspect, prio,
+ message,
+ graph->getLastEnterCommand ()),
+ true);
+}
+
+void ObjViewController::objMark (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *message)
+{
+ OVGAddMarkCommand *markCommand =
+ new OVGAddMarkCommand (info->fileName, info->lineNo, graph, id, aspect,
+ prio, message, graph->getLastEnterCommand ());
+ graph->addCommand (markCommand, true);
+ graph->addCommandMark (markCommand);
+}
+
+void ObjViewController::objMsgStart (CommonLineInfo *info, const char *id)
+{
+ OVGIncIndentCommand *startCommand =
+ new OVGIncIndentCommand (info->fileName, info->lineNo, graph, id,
+ graph->getLastEnterCommand ());
+ graph->addCommand (startCommand, true);
+ graph->pushStartCommand (startCommand);
+}
+
+void ObjViewController::objMsgEnd (CommonLineInfo *info, const char *id)
+{
+ OVGIncIndentCommand *lastStartCommand = graph->getLastStartCommand ();
+ if (lastStartCommand == NULL)
+ fprintf (stderr, "'obj-msg-end' without 'obj-msg-start:\n%s\n",
+ info->completeLine);
+ else {
+ graph->addCommand (new OVGDecIndentCommand (info->fileName, info->lineNo,
+ graph, id, lastStartCommand,
+ graph->getLastEnterCommand()),
+ true);
+ graph->popStartCommand ();
+ }
+}
+
+void ObjViewController::objEnter (CommonLineInfo *info, const char *id,
+ const char *aspect, int prio,
+ const char *funname, const char *args)
+{
+ OVGEnterCommand *enterCommand =
+ new OVGEnterCommand (info->fileName, info->lineNo, graph, id, aspect,
+ prio, funname, args, graph->getLastEnterCommand ());
+ graph->addCommand (enterCommand, true);
+ graph->pushEnterCommand (enterCommand);
+}
+
+void ObjViewController::objLeave (CommonLineInfo *info, const char *id,
+ const char *vals)
+{
+ OVGEnterCommand *lastEnterCommand = graph->getLastEnterCommand ();
+ if (lastEnterCommand == NULL)
+ fprintf (stderr, "'obj-leave' without 'obj-enter:\n%s\n",
+ info->completeLine);
+ else {
+ graph->addCommand (new OVGLeaveCommand (info->fileName, info->lineNo,
+ graph, id, vals,
+ lastEnterCommand),
+ true);
+ graph->popEnterCommand ();
+ }
+}
+
+void ObjViewController::objCreate (CommonLineInfo *info, const char *id,
+ const char *klass)
+{
+ graph->addCommand (new OVGCreateCommand (info->fileName, info->lineNo, graph,
+ id, klass,
+ graph->getLastEnterCommand ()),
+ true);
+}
+
+void ObjViewController::objIdent (CommonLineInfo *info, const char *id1,
+ const char *id2)
+{
+ // TODO Is this not done by ObjdentController?
+ graph->addIdentity (id1, id2);
+}
+
+void ObjViewController::objNoIdent (CommonLineInfo *info)
+{
+}
+
+void ObjViewController::objAssoc (CommonLineInfo *info, const char *parent,
+ const char *child)
+{
+ graph->addCommand (new OVGAddAssocCommand (info->fileName, info->lineNo,
+ graph, parent, child,
+ graph->getLastEnterCommand ()),
+ true);
+}
+
+void ObjViewController::objSet (CommonLineInfo *info, const char *id,
+ const char *var, const char *val)
+{
+ graph->addCommand (new OVGAddAttrCommand (info->fileName, info->lineNo,
+ graph, id, var, val,
+ graph->getLastEnterCommand ()),
+ true);
+}
+
+void ObjViewController::objClassColor (CommonLineInfo *info, const char *klass,
+ const char *color)
+{
+ graph->setClassColor (klass, color);
+}
+
+void ObjViewController::objObjectColor (CommonLineInfo *info, const char *id,
+ const char *color)
+{
+ graph->setObjectColor (id, color);
+}
+
+void ObjViewController::objDelete (CommonLineInfo *info, const char *id)
+{
+ graph->addCommand (new OVGDeleteCommand (info->fileName, info->lineNo, graph,
+ id, graph->getLastEnterCommand ()),
+ true);
+}
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objview_controller.hh b/objects/objview_controller.hh
new file mode 100644
index 0000000..cc14a7f
--- /dev/null
+++ b/objects/objview_controller.hh
@@ -0,0 +1,51 @@
+#ifndef __OBJECTS_OBJVIEW_CONTROLLER_HH__
+#define __OBJECTS_OBJVIEW_CONTROLLER_HH__
+
+#include "objects_parser.hh"
+#include "objview_graph.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+class ObjViewController: public ObjectsControllerBase
+{
+private:
+ ObjViewGraph *graph;
+
+public:
+ ObjViewController (ObjViewGraph *graph) { this->graph = graph; }
+
+ void objMsg (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMark (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *message);
+ void objMsgStart (tools::CommonLineInfo *info, const char *id);
+ void objMsgEnd (tools::CommonLineInfo *info, const char *id);
+ void objEnter (tools::CommonLineInfo *info, const char *id,
+ const char *aspect, int prio, const char *funname,
+ const char *args);
+ void objLeave (tools::CommonLineInfo *info, const char *id,
+ const char *vals);
+ void objCreate (tools::CommonLineInfo *info, const char *id,
+ const char *klass);
+ void objIdent (tools::CommonLineInfo *info, const char *id1,
+ const char *id2);
+ void objNoIdent (tools::CommonLineInfo *info);
+ void objAssoc (tools::CommonLineInfo *info, const char *parent,
+ const char *child);
+ void objSet (tools::CommonLineInfo *info, const char *id, const char *var,
+ const char *val);
+ void objClassColor (tools::CommonLineInfo *info, const char *klass,
+ const char *color);
+ void objObjectColor (tools::CommonLineInfo *info, const char *id,
+ const char *color);
+ void objDelete (tools::CommonLineInfo *info, const char *id);
+
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJVIEW_CONTROLLER_HH__
diff --git a/objects/objview_graph.cc b/objects/objview_graph.cc
new file mode 100644
index 0000000..6528f8a
--- /dev/null
+++ b/objects/objview_graph.cc
@@ -0,0 +1,984 @@
+/*
+ * 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 "objview_graph.hh"
+
+#include <fnmatch.h>
+#include <unistd.h>
+
+using namespace lout::object;
+using namespace lout::container;
+using namespace lout::container::typed;
+using namespace lout::misc;
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace rtfl::dw;
+
+namespace rtfl {
+
+namespace objects {
+
+void ObjViewGraphListener::close (ObjViewStacktraceWindow *window)
+{
+ graph->stacktraceWindows->removeRef (window);
+}
+
+// ----------------------------------------------------------------------
+
+OVGAttributesList::OVGAttributesList (OVGAttributesList *parent)
+{
+ this->parent = parent;
+
+ childNoCount = numChildrenShown = 0;
+ childrenShown = new BitSet (8);
+ shown = true;
+
+ if (parent)
+ childNo = parent->registerChild ();
+ else
+ childNo = -1;
+
+ attributes = new HashTable<String, OVGAttribute> (true, true);
+}
+
+OVGAttributesList::~OVGAttributesList ()
+{
+ if (parent)
+ parent->unregisterChild (childNo);
+
+ delete childrenShown;
+
+ delete attributes;
+}
+
+void OVGAttributesList::initWidgets (Style *widgetStyle, dw::Box *parent,
+ bool showLarge)
+{
+ toggle = new Toggle (showLarge);
+ toggle->setStyle (widgetStyle);
+ parent->addChild (toggle);
+
+ vbox = new VBox (false);
+ vbox->setStyle (widgetStyle);
+ toggle->setLarge (vbox);
+}
+
+int OVGAttributesList::registerChild ()
+{
+ int childNo = childNoCount++;
+
+ //printf (" %p (%p) / %d -- registerChild => %d\n",
+ // this, parent, this->childNo, childNo);
+
+ childShown (childNo);
+ return childNo;
+}
+
+void OVGAttributesList::unregisterChild (int childNo)
+{
+ // Should (could?) actually destroy this attribute (if this is an
+ // attribute) when no registered children are left.
+ childHidden (childNo);
+}
+
+void OVGAttributesList::childShown (int childNo)
+{
+ if (!childrenShown->get (childNo)) {
+ childrenShown->set (childNo, true);
+ numChildrenShown++;
+ checkVisibility ();
+ }
+}
+
+void OVGAttributesList::childHidden (int childNo)
+{
+ if (childrenShown->get (childNo)) {
+ childrenShown->set (childNo, false);
+ numChildrenShown--;
+ checkVisibility ();
+ }
+}
+
+void OVGAttributesList::checkVisibility ()
+{
+ //printf (" %p (%p) / %d -- %d children shown\n",
+ // this, parent, childNo, numChildrenShown);
+
+ if (shown) {
+ if (numChildrenShown <= 0) {
+ shown = false;
+ hide ();
+ if (parent)
+ parent->childHidden (childNo);
+ }
+ } else {
+ if (numChildrenShown > 0) {
+ shown = true;
+ show ();
+ if (parent)
+ parent->childShown (childNo);
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+
+OVGTopAttributes::OVGTopAttributes () : OVGAttributesList (NULL)
+{
+ sortedList = new Vector<String> (4, false);
+}
+
+OVGTopAttributes::~OVGTopAttributes ()
+{
+ delete sortedList;
+}
+
+int OVGTopAttributes::add (String *key, OVGAttribute *attribute)
+{
+ int newPos = sortedList->insertSorted (key);
+ attributes->put (key, attribute);
+ return newPos;
+}
+
+void OVGTopAttributes::show ()
+{
+ // Keep the top list visible.
+}
+
+void OVGTopAttributes::hide ()
+{
+ // Keep the top list visible.
+}
+
+// ----------------------------------------------------------------------
+
+OVGAttribute::OVGAttribute (const char *name, OVGAttributesList *parent) :
+ OVGAttributesList (parent)
+{
+ this->name = strdup (name);
+}
+
+OVGAttribute::~OVGAttribute ()
+{
+ free (name);
+}
+
+void OVGAttribute::initWidgets (Style *widgetStyle, dw::Box *parent,
+ bool showLarge, int newPos)
+{
+ hbox = new HBox (false);
+ hbox->setStyle (widgetStyle);
+ parent->addChild (hbox, newPos);
+
+ label = new Label (name);
+ label->setStyle (widgetStyle);
+ hbox->addChild (label);
+
+ OVGAttributesList::initWidgets (widgetStyle, hbox, showLarge);
+}
+
+int OVGAttribute::add (String *key, OVGAttribute *attribute)
+{
+ attributes->put (key, attribute);
+ return -1;
+}
+
+void OVGAttribute::show ()
+{
+ hbox->show ();
+}
+
+void OVGAttribute::hide ()
+{
+ hbox->hide ();
+}
+
+// ----------------------------------------------------------------------
+
+ObjViewGraph::GraphObject::GraphObject (ObjViewGraph *graph, const char *id)
+{
+ this->graph = graph;
+ this->id = strdup (id);
+
+ className = NULL;
+ node = NULL;
+ attributes = NULL;
+ messageStyle = graph->noBorderStyle;
+ messageStyle->ref ();
+}
+
+ObjViewGraph::GraphObject::~GraphObject ()
+{
+ // Simplified, when the whole graph is deleted.
+ if (node && !graph->inDestructor)
+ delete node;
+
+ free (id);
+ if (className)
+ free (className);
+ if (attributes)
+ delete attributes;
+ if (messageStyle)
+ messageStyle->unref ();
+}
+
+// ----------------------------------------------------------------------
+
+int ObjViewGraph::ColorComparator::specifity (const char *pattern)
+{
+ int s = 0;
+ for (const char *p = pattern; *p; p++) {
+ if (*p == '*')
+ s -= 100;
+ else if (*p == '?')
+ s += 1;
+ else
+ s += 100;
+ }
+ return s;
+}
+
+int ObjViewGraph::ColorComparator::compare (Object *o1, Object *o2)
+{
+ return specifity (((Color*)o2)->identifier)
+ - specifity (((Color*)o1)->identifier);
+}
+
+// ----------------------------------------------------------------------
+
+ObjViewGraph::Color::Color (const char *identifier, const char *color,
+ Layout *layout)
+{
+ this->identifier = strdup (identifier);
+
+ this->color = NULL;
+ if (*color == '#') {
+ char *end;
+ int c = (int)strtol (color + 1, &end, 16);
+ if (*end == 0) {
+ if (end - color == 7)
+ this->color = style::Color::create (layout, c);
+ else if (end - color == 4)
+ this->color =
+ style::Color::create (layout,
+ (c & 0xf00) >> 8 | (c && 0xf0) >> 4
+ | (c & 0xf));
+ this->color->ref ();
+ }
+ }
+
+ if (this->color == NULL)
+ fprintf (stderr, "WARNING: invalid color '%s'.\n", color);
+}
+
+ObjViewGraph::Color::~Color ()
+{
+ free (identifier);
+
+ if (this->color)
+ this->color->unref ();
+}
+
+// ----------------------------------------------------------------------
+
+ObjViewGraph::ObjViewGraph (ObjViewFilterTool *filterTool)
+{
+ inDestructor = false;
+
+ this->filterTool = filterTool;
+
+ commands = new Vector<OVGCommand> (4, true);
+ navigableCommands = new Vector<OVGCommand> (4, false);
+
+ objectsById = new HashTable<String, GraphObject> (true, false);
+ allObjects = new Vector<GraphObject> (1, true);
+ classColors = new Vector<Color> (1, true);
+ objectColors = new Vector<Color> (1, true);
+ enterCommands = new Stack<OVGEnterCommand> (false);
+ startCommands = new Stack<OVGIncIndentCommand> (false);
+
+ navigableCommandsPos = hiddenBefore = hiddenAfter = -1;
+ numVisibleCommands = 0;
+ objectContents = objectMessages = true;
+ codeViewer = strdup ("xterm -e 'vi +%n %p' &");
+
+ stacktraceWindows = new List <ObjViewStacktraceWindow> (true);
+ stacktraceListener = new ObjViewGraphListener (this);
+
+ nodeStyle = noBorderStyle = topBorderStyle = bottomBorderStyle =
+ leftBorderStyle = NULL;
+}
+
+ObjViewGraph::~ObjViewGraph ()
+{
+ inDestructor = true;
+
+ free (codeViewer);
+ delete commands;
+ delete navigableCommands;
+
+ delete objectsById;
+ delete allObjects;
+ delete classColors;
+ delete objectColors;
+ delete enterCommands;
+ delete startCommands;
+
+ delete stacktraceWindows;
+ delete stacktraceListener;
+
+ if (nodeStyle)
+ nodeStyle->unref ();
+ if (noBorderStyle)
+ noBorderStyle->unref ();
+ if (topBorderStyle)
+ topBorderStyle->unref ();
+ if (bottomBorderStyle)
+ bottomBorderStyle->unref ();
+ if (leftBorderStyle)
+ leftBorderStyle->unref ();
+}
+
+void ObjViewGraph::initStyles (const char *fontName, int fontSize, int fgColor,
+ int graphBgColor, int objBgColor,
+ int borderThickness, int graphMargin)
+{
+ StyleAttrs styleAttrs;
+ styleAttrs.initValues ();
+
+ FontAttrs fontAttrs;
+ fontAttrs.name = fontName;
+ fontAttrs.size = fontSize;
+ fontAttrs.weight = 400;
+ fontAttrs.style = FONT_STYLE_NORMAL;
+ fontAttrs.letterSpacing = 0;
+ fontAttrs.fontVariant = FONT_VARIANT_NORMAL;
+ styleAttrs.font = style::Font::create (layout, &fontAttrs);
+
+ styleAttrs.padding.setVal (graphMargin);
+ styleAttrs.color = style::Color::create (layout, fgColor);
+ styleAttrs.setBorderColor (styleAttrs.color);
+ styleAttrs.setBorderStyle (BORDER_SOLID);
+ styleAttrs.backgroundColor = style::Color::create (layout, graphBgColor);
+ Style *graphStyle = Style::create (&styleAttrs);
+ setStyle (graphStyle);
+
+ styleAttrs.padding.setVal (0);
+ styleAttrs.backgroundColor = style::Color::create (layout, objBgColor);
+ styleAttrs.borderWidth.setVal (1);
+ nodeStyle = Style::create (&styleAttrs);
+ setRefStyle (nodeStyle);
+
+ styleAttrs.backgroundColor = NULL;
+ styleAttrs.borderWidth.setVal (0);
+ noBorderStyle = Style::create (&styleAttrs);
+
+ styleAttrs.borderWidth.setVal (0);
+ styleAttrs.borderWidth.top = 1;
+ topBorderStyle = Style::create (&styleAttrs);
+
+ styleAttrs.borderWidth.setVal (0);
+ styleAttrs.borderWidth.bottom = 1;
+ bottomBorderStyle = Style::create (&styleAttrs);
+
+ styleAttrs.borderWidth.setVal (0);
+ styleAttrs.borderWidth.left = 1;
+ leftBorderStyle = Style::create (&styleAttrs);
+}
+
+ObjViewGraph::GraphObject *ObjViewGraph::ensureObject (const char *id)
+{
+ String key (id);
+ if (!objectsById->contains (&key)) {
+ GraphObject *obj = new GraphObject (this, id);
+
+ obj->node = new Toggle (objectContents);
+ applyClassOrObjectStyle (obj);
+ addNode (obj->node);
+
+ obj->id1 = new Label (id);
+ obj->id1->setStyle (noBorderStyle);
+ obj->node->setSmall (obj->id1);
+
+ VBox *large = new VBox (false);
+ large->setStyle (leftBorderStyle);
+ obj->node->setLarge (large);
+
+ obj->id2 = new Label (id);
+ obj->id2->setStyle (bottomBorderStyle);
+ large->addChild (obj->id2);
+
+ obj->attributes = new OVGTopAttributes ();
+ obj->attributes->initWidgets (noBorderStyle, large, true);
+
+ Toggle *mToggle = new Toggle (objectMessages);
+ mToggle->setStyle (topBorderStyle);
+ large->addChild (mToggle);
+
+ obj->messages = new VBox (false);
+ obj->messages->setStyle (noBorderStyle);
+ mToggle->setLarge (obj->messages);
+
+ objectsById->put (new String (id), obj);
+ allObjects->put (obj);
+ }
+
+ return objectsById->get (&key);
+}
+
+void ObjViewGraph::addMessage (const char *id, const char *message,
+ HBox **mainBox, Label **indexLabel,
+ Label **mainLabel)
+{
+ GraphObject *obj = ensureObject (id);
+
+ HBox *hbox = new HBox (false);
+ hbox->setStyle (noBorderStyle);
+ obj->messages->addChild (hbox);
+
+ Label *label1 = new Label ("???: ");
+ label1->setStyle (obj->messageStyle);
+ hbox->addChild (label1);
+
+ Label *label2 = new Label (message);
+ label2->setStyle (noBorderStyle);
+ hbox->addChild (label2);
+
+ if (mainBox)
+ *mainBox = hbox;
+ if (indexLabel)
+ *indexLabel = label1;
+ if (mainLabel)
+ *mainLabel = label2;
+}
+
+bool ObjViewGraph::incIndent (const char *id)
+{
+ GraphObject *obj = ensureObject (id);
+
+ Style *oldMessageStyle = obj->messageStyle;
+ StyleAttrs styleAttrs = *(obj->messageStyle);
+ styleAttrs.padding.left += 3 * obj->messageStyle->font->spaceWidth;
+ obj->messageStyle = Style::create (&styleAttrs);
+ oldMessageStyle->unref ();
+
+ return true; // just for symmetry with decIndent
+}
+
+bool ObjViewGraph::decIndent (const char *id)
+{
+ GraphObject *obj = ensureObject (id);
+
+ Style *oldMessageStyle = obj->messageStyle;
+ int indentWidth = 3 * obj->messageStyle->font->spaceWidth;
+ if (oldMessageStyle->padding.left >= indentWidth) {
+ StyleAttrs styleAttrs = *(obj->messageStyle);
+ styleAttrs.padding.left -= indentWidth;
+ obj->messageStyle = Style::create (&styleAttrs);
+ oldMessageStyle->unref ();
+ return true;
+ } else
+ return false;
+}
+
+OVGAttribute *ObjViewGraph::addAttribute (const char *id, const char *name,
+ const char *value, Label **smallLabel,
+ HBox **histBox, Label **indexLabel,
+ Label **histLabel)
+{
+ GraphObject *obj = ensureObject (id);
+
+ int numDots = 0;
+ for (const char *s = name; *s; s++)
+ if (*s == '.')
+ numDots++;
+
+ //printf ("'%s' contains %d dots\n", name, numDots);
+ char **parts = new char*[numDots + 2];
+
+ int i = 0;
+ const char *s = name;
+ for (const char *e = s; i < numDots + 1; e++)
+ if (*e == '.') {
+ parts[i] = strndup (s, e - s);
+ //printf ("%d of %d: '%s'\n", i, numDots + 1, parts[i]);
+ s = e + 1;
+ i++;
+ } else if (*e == 0) {
+ parts[i] = strdup (s);
+ //printf ("%d of %d (last): '%s'\n", i, numDots + 1, parts[i]);
+ s = e;
+ i++;
+ }
+
+ //printf ("finished\n");
+
+ parts[numDots + 1] = NULL;
+
+ OVGAttribute *attribute = addAttribute (obj->attributes, parts, value,
+ smallLabel, histBox, indexLabel,
+ histLabel);
+
+ for (char **p = parts; *p; p++)
+ free (*p);
+
+ delete[] parts;
+
+ return attribute;
+}
+
+// Returns "large" label.
+OVGAttribute *ObjViewGraph::addAttribute (OVGAttributesList *attributesList,
+ char **parts, const char *value,
+ Label **smallLabel, HBox **histBox,
+ Label **indexLabel, Label **histLabel)
+{
+ if (*parts) {
+ String key (*parts);
+ OVGAttribute *attribute = attributesList->get (&key);
+ if (attribute == NULL) {
+ String *str = new String (*parts);
+ attribute = new OVGAttribute (*parts, attributesList);
+ int newPos = attributesList->add (str, attribute);
+ attribute->initWidgets (noBorderStyle, attributesList->vbox, false,
+ newPos);
+ }
+
+ OVGAttribute *subAttribute = addAttribute (attribute, parts + 1, value,
+ smallLabel, histBox,
+ indexLabel, histLabel);
+ return subAttribute ? subAttribute : attribute;
+ } else {
+ HBox *hbox = new HBox (false);
+ hbox->setStyle (noBorderStyle);
+ attributesList->vbox->addChild (hbox);
+
+ Label *label1 = new Label ("???: ");
+ label1->setStyle (noBorderStyle);
+ hbox->addChild (label1);
+
+ Label *label2 = new Label (value);
+ label2->setStyle (noBorderStyle);
+ hbox->addChild (label2);
+
+ if (attributesList->toggle->getSmall () != NULL) {
+ assert (attributesList->toggle->getSmall()
+ ->instanceOf (Label::CLASS_ID));
+ ((Label*)attributesList->toggle->getSmall())->setText (value);
+ } else {
+ Label *cur = new Label (value);
+ cur->setStyle (noBorderStyle);
+ attributesList->toggle->setSmall (cur);
+ }
+
+ if (smallLabel)
+ *smallLabel = (Label*)attributesList ->toggle->getSmall ();
+ if (histBox)
+ *histBox = hbox;
+ if (indexLabel)
+ *indexLabel = label1;
+ if (histLabel)
+ *histLabel = label2;
+
+ // Here, no attribute is created; the caller must discard this
+ // return value.
+ return NULL;
+ }
+}
+
+void ObjViewGraph::setClassName (const char *id, const char *className)
+{
+ GraphObject *obj = ensureObject (id);
+
+ if (obj->className)
+ free (obj->className);
+ obj->className = strdup (className);
+
+ int bufLen = strlen (id) + 2 + strlen (className) + 1;
+ char *buf = new char[bufLen];
+ snprintf (buf, bufLen, "%s: %s", id, className);
+
+ obj->id1->setText (buf);
+ obj->id2->setText (buf);
+
+ delete[] buf;
+
+ applyClassOrObjectStyle (obj);
+}
+
+style::Color *ObjViewGraph::getObjectColor (GraphObject *obj)
+{
+ style::Color *objectColor = NULL;
+
+ // TODO Identities are currently not considered
+ for (int i = 0; objectColor == NULL && i < objectColors->size (); i++) {
+ Color *color = objectColors->get (i);
+ if (color->color && strcmp (color->identifier, obj->id) == 0)
+ objectColor = color->color;
+ }
+
+ if (obj->className) {
+ for (int i = 0; objectColor == NULL && i < classColors->size (); i++) {
+ Color *color = classColors->get (i);
+ if (color->color &&
+ fnmatch (color->identifier, obj->className, 0) == 0)
+ objectColor = color->color;
+ }
+ }
+
+ return objectColor;
+}
+
+void ObjViewGraph::applyClassOrObjectStyle (GraphObject *obj)
+{
+ style::Color *usedColor = getObjectColor (obj);
+
+ if (usedColor) {
+ // TODO Perhaps it is better to create styles initially (and
+ // change them when setStyle is called?). Should especially
+ // reduce memory usage.
+ StyleAttrs attrs = *nodeStyle;
+ attrs.backgroundColor = usedColor;
+ Style *newStyle = Style::create (&attrs);
+ obj->node->setStyle (newStyle);
+ newStyle->unref ();
+ } else
+ obj->node->setStyle (nodeStyle);
+}
+
+
+void ObjViewGraph::applyClassOrObjectStyles ()
+{
+ for (typed::Iterator<String> it = objectsById->iterator ();
+ it.hasNext (); ) {
+ String *key = it.getNext ();
+ GraphObject *obj = (GraphObject*) objectsById->get(key);
+ applyClassOrObjectStyle (obj);
+ }
+}
+
+int ObjViewGraph::getObjectColor (const char *id)
+{
+ String key (id);
+ GraphObject *obj = (GraphObject*) objectsById->get (&key);
+ if (obj) {
+ style::Color *objectColor = getObjectColor (obj);
+ return objectColor ?
+ objectColor->getColor () : nodeStyle->backgroundColor->getColor ();
+ } else
+ return nodeStyle->backgroundColor->getColor ();
+}
+
+void ObjViewGraph::addIdentity (const char *id1, const char *id2)
+{
+ // Done in ObjIdentController.
+}
+
+void ObjViewGraph::addAssoc (const char *id1, const char *id2)
+{
+ GraphObject *obj1 = ensureObject (id1);
+ GraphObject *obj2 = ensureObject (id2);
+ addEdge (obj1->node, obj2->node);
+}
+
+void ObjViewGraph::setClassColor (const char *klass, const char *color)
+{
+ ColorComparator cmp;
+ classColors->insertSorted (new Color (klass, color, layout), &cmp);
+ applyClassOrObjectStyles ();
+}
+
+void ObjViewGraph::setObjectColor (const char *id, const char *color)
+{
+ objectColors->put (new Color (id, color, layout));
+ applyClassOrObjectStyles ();
+}
+
+void ObjViewGraph::addCommand (OVGCommand *command, bool navigable)
+{
+ commands->put (command);
+ command->exec (this);
+
+ if (navigable) {
+ navigableCommands->put (command);
+ command->setNavigableCommandsIndex (this, navigableCommands->size () -1);
+
+ if (command->calcVisibility (this)) {
+ command->show (this);
+ command->setVisibleIndex (this, numVisibleCommands);
+ numVisibleCommands++;
+ } else
+ command->hide (this);
+ }
+}
+
+void ObjViewGraph::clearSelection ()
+{
+ if (navigableCommandsPos != -1)
+ navigableCommands->get(navigableCommandsPos)->unselect (this);
+}
+
+void ObjViewGraph::unclearSelection ()
+{
+ if (navigableCommandsPos != - 1) {
+ navigableCommands->get(navigableCommandsPos)->select (this);
+ navigableCommands->get(navigableCommandsPos)->scrollTo (this);
+ }
+}
+
+int ObjViewGraph::firstCommand ()
+{
+ if (hiddenBefore != -1)
+ return hiddenBefore;
+ else
+ return navigableCommands->size () > 0 ? 0 : -1;
+}
+
+int ObjViewGraph::lastCommand ()
+{
+ if (hiddenAfter != -1)
+ return hiddenAfter;
+ else
+ return navigableCommands->size () - 1; // may be -1
+}
+
+void ObjViewGraph::previousCommand ()
+{
+ clearSelection ();
+
+ if (numVisibleCommands == 0)
+ // No visible command;
+ navigableCommandsPos = -1;
+ else
+ do {
+ if (navigableCommandsPos == -1)
+ navigableCommandsPos = lastCommand ();
+ else {
+ navigableCommandsPos--;
+ if (navigableCommandsPos < firstCommand ())
+ navigableCommandsPos = lastCommand ();
+ }
+ } while (!navigableCommands->get(navigableCommandsPos)->isVisible (this));
+
+ unclearSelection ();
+}
+
+void ObjViewGraph::nextCommand ()
+{
+ clearSelection ();
+
+ if (numVisibleCommands == 0)
+ // No visible command;
+ navigableCommandsPos = -1;
+ else {
+ do {
+ if (navigableCommandsPos == -1)
+ navigableCommandsPos = firstCommand ();
+ else {
+ navigableCommandsPos++;
+ if (navigableCommandsPos > lastCommand ())
+ navigableCommandsPos = firstCommand ();
+ }
+ } while (!navigableCommands->get(navigableCommandsPos)->isVisible (this));
+ }
+
+ unclearSelection ();
+}
+
+void ObjViewGraph::viewCodeOfCommand ()
+{
+ if (navigableCommandsPos != -1) {
+ OVGCommand *command = navigableCommands->get (navigableCommandsPos);
+
+ char lineNoBuf[32];
+ char partBuf[32] = { 0, 0 };
+ StringBuffer sb;
+
+ snprintf (lineNoBuf, 32, "+%d", command->getLineNo ());
+
+ for (char *s = codeViewer; *s; s++) {
+ if (*s == '%' && (s[1] == 'p' || s[1] == 'n')) {
+ s++;
+ switch (*s) {
+ case 'p':
+ sb.append (command->getFileName ());
+ break;
+
+ case 'n':
+ sb.append (lineNoBuf);
+ break;
+ }
+ } else {
+ *partBuf = *s;
+ sb.append (partBuf);
+ }
+ }
+
+ system (sb.getChars());
+ }
+}
+
+void ObjViewGraph::showStackTraceOfCommand ()
+{
+ if (navigableCommandsPos != -1) {
+ OVGCommand *command = navigableCommands->get (navigableCommandsPos);
+ ObjViewStacktraceWindow *stWin =
+ new ObjViewStacktraceWindow (command->getFunction (),
+ stacktraceListener);
+ stacktraceWindows->append (stWin);
+ stWin->show ();
+ stWin->update ();
+ }
+}
+
+void ObjViewGraph::switchBetweenRelatedCommands ()
+{
+ if (navigableCommandsPos != -1) {
+ OVGCommand *command = navigableCommands->get (navigableCommandsPos);
+ OVGCommand *relatedCommand = command->getRelatedCommand ();
+ if (relatedCommand && relatedCommand->isVisible (this)) {
+ clearSelection ();
+ navigableCommandsPos =
+ relatedCommand->getNavigableCommandsIndex (this);
+ unclearSelection ();
+ }
+ }
+}
+
+void ObjViewGraph::setCommand (int index)
+{
+ clearSelection ();
+ navigableCommandsPos = index;
+ unclearSelection ();
+}
+
+void ObjViewGraph::hideBeforeCommand ()
+{
+ // Note: The selected command cannot be hidden, so there are never
+ // elements to show again.
+
+ if (navigableCommandsPos != -1) {
+ hiddenBefore = navigableCommandsPos;
+ for (int i = 0; i < hiddenBefore; i++)
+ navigableCommands->get(i)->hide (this);
+ }
+
+ recalculateCommandsVisibility (); // Could be faster
+}
+
+void ObjViewGraph::hideAfterCommand ()
+{
+ // See also note in hideBeforeCommand().
+
+ if (navigableCommandsPos != -1) {
+ hiddenAfter = navigableCommandsPos;
+ for (int i = hiddenAfter + 1; i < navigableCommands->size (); i++)
+ navigableCommands->get(i)->hide (this);
+ }
+
+ recalculateCommandsVisibility (); // Could be faster
+}
+
+void ObjViewGraph::hideAllCommands ()
+{
+ navigableCommandsPos = -1;
+ hiddenBefore = navigableCommands->size ();
+ for (int i = 0; i < navigableCommands->size (); i++)
+ navigableCommands->get(i)->hide (this);
+
+ recalculateCommandsVisibility (); // Necessary?
+}
+
+void ObjViewGraph::showBeforeCommand ()
+{
+ if (hiddenBefore != -1) {
+ hiddenBefore = -1;
+ recalculateCommandsVisibility (); // Could be faster
+ }
+}
+
+void ObjViewGraph::showAfterCommand ()
+{
+ if (hiddenAfter != -1) {
+ hiddenAfter = -1;
+ recalculateCommandsVisibility (); // Could be faster
+ }
+}
+
+void ObjViewGraph::showAllCommands ()
+{
+ if (hiddenBefore != -1 || hiddenAfter != -1) {
+ hiddenBefore = hiddenAfter = -1;
+ recalculateCommandsVisibility (); // Could be faster
+ }
+}
+
+void ObjViewGraph::setCodeViewer (const char *codeViewer)
+{
+ free (this->codeViewer);
+ this->codeViewer = strdup (codeViewer);
+}
+
+void ObjViewGraph::recalculateCommandsVisibility ()
+{
+ numVisibleCommands = 0;
+ for (int i = max (firstCommand (), 0); i <= lastCommand (); i++) {
+ OVGCommand *command = navigableCommands->get (i);
+
+ if (command->calcVisibility (this)) {
+ command->show (this);
+ command->setVisibleIndex (this, numVisibleCommands);
+ numVisibleCommands++;
+ } else
+ command->hide (this);
+ }
+
+ if (navigableCommandsPos != -1) {
+ if (!navigableCommands->get(navigableCommandsPos)->isVisible (this))
+ // Selected command has become invisible. Select next one. (May
+ // also be the first one, if, e. g., the selected one was the last
+ // one. Could test this and instead select the last visible
+ // *before*.)
+ nextCommand ();
+ else
+ // May have become out of view.
+ navigableCommands->get(navigableCommandsPos)->scrollTo (this);
+ }
+
+ // Update stacktrace windows (hidden commands should not be selectable).
+ for (lout::container::typed::Iterator <ObjViewStacktraceWindow> it =
+ stacktraceWindows->iterator (); it.hasNext (); ) {
+ ObjViewStacktraceWindow *stWin = it.getNext ();
+ stWin->update ();
+ }
+}
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objview_graph.hh b/objects/objview_graph.hh
new file mode 100644
index 0000000..9619f7e
--- /dev/null
+++ b/objects/objview_graph.hh
@@ -0,0 +1,305 @@
+#ifndef __OBJECTS_OBJVIEW_GRAPH_HH__
+#define __OBJECTS_OBJVIEW_GRAPH_HH__
+
+#include "objview_stacktrace.hh"
+#include "objview_commands.hh"
+#include "dwr/graph.hh"
+#include "dwr/graph2.hh"
+#include "dwr/toggle.hh"
+#include "dwr/vbox.hh"
+#include "dwr/hbox.hh"
+#include "dwr/label.hh"
+#include "common/tools.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+enum OVGCommandType {
+ OVG_COMMAND_CREATE,
+ OVG_COMMAND_INDENT,
+ OVG_COMMAND_MESSAGE,
+ OVG_COMMAND_MARK,
+ OVG_COMMAND_FUNCTION,
+ OVG_COMMAND_ASSOC,
+ OVG_COMMAND_ADD_ATTR,
+ OVG_COMMAND_DELETE
+};
+
+class ObjViewGraphListener: public ObjViewListener
+{
+private:
+ ObjViewGraph *graph;
+
+public:
+ ObjViewGraphListener (ObjViewGraph *graph) { this->graph = graph; }
+
+ void close (ObjViewStacktraceWindow *window);
+};
+
+
+class ObjViewFilterTool
+{
+public:
+ virtual void addAspect (const char *aspect) = 0;
+ virtual void addPriority (int priority) = 0;
+ virtual bool isAspectSelected (const char *aspect) = 0;
+ virtual bool isPrioritySelected (int priority) = 0;
+ virtual bool isTypeSelected (OVGCommandType type) = 0;
+ virtual void addMark (OVGAddMarkCommand *markCommand) = 0;
+};
+
+class OVGAttribute;
+
+class OVGAttributesList: public lout::object::Object
+{
+private:
+ OVGAttributesList *parent;
+ int childNoCount, numChildrenShown, childNo;
+ lout::misc::BitSet *childrenShown;
+ bool shown;
+
+ void checkVisibility ();
+
+protected:
+ lout::container::typed::HashTable<lout::object::String,
+ OVGAttribute> *attributes;
+
+ virtual void show () = 0;
+ virtual void hide () = 0;
+
+public:
+ dw::VBox *vbox;
+ dw::Toggle *toggle;
+
+ OVGAttributesList (OVGAttributesList *parent);
+ ~OVGAttributesList ();
+
+ inline OVGAttribute *get (lout::object::String *key)
+ { return attributes->get (key); }
+ virtual int add (lout::object::String *key, OVGAttribute *attribute) = 0;
+
+ void initWidgets (::dw::core::style::Style *widgetStyle, dw::Box *parent,
+ bool showLarge);
+
+ int registerChild ();
+ void unregisterChild (int childNo);
+ void childShown (int childNo);
+ void childHidden (int childNo);
+};
+
+class OVGTopAttributes: public OVGAttributesList
+{
+protected:
+ lout::container::typed::Vector<lout::object::String> *sortedList;
+
+ void show ();
+ void hide ();
+
+public:
+ OVGTopAttributes ();
+ ~OVGTopAttributes ();
+
+ int add (lout::object::String *key, OVGAttribute *attribute);
+};
+
+// Hint: the OVGAttributesList'ness refers to the children.
+class OVGAttribute: public OVGAttributesList
+{
+protected:
+ void show ();
+ void hide ();
+
+public:
+ dw::HBox *hbox;
+ dw::Label *label;
+ char *name;
+
+ OVGAttribute (const char *name, OVGAttributesList *parent);
+ ~OVGAttribute ();
+
+ void initWidgets (::dw::core::style::Style *widgetStyle,
+ dw::Box *parent, bool showLarge, int newPos);
+
+ int add (lout::object::String *key, OVGAttribute *attribute);
+};
+
+class ObjViewGraph: public
+#if USE_GRAPH2
+ dw::Graph2
+#else
+ dw::Graph
+#endif
+{
+ friend class OVGCommonCommand;
+ friend class OVGIncIndentCommand;
+ friend class OVGDecIndentCommand;
+ friend class OVGEnterCommand;
+ friend class OVGLeaveCommand;
+ friend class OVGAddMessageCommand;
+ friend class OVGAddMarkCommand;
+ friend class OVGCreateCommand;
+ friend class OVGAddAssocCommand;
+ friend class OVGAddAttrCommand;
+ friend class OVGDeleteCommand;
+ friend class ObjViewGraphListener;
+
+private:
+ class CreateRefCommand;
+
+ class GraphObject: public lout::object::Object
+ {
+ friend class CreateRefCommand;
+ friend class ObjViewGraph;
+
+ private:
+ ObjViewGraph *graph;
+
+ public:
+ char *id, *className;
+ ::dw::core::style::Style *messageStyle;
+ dw::Toggle *node;
+ dw::Label *id1, *id2;
+ OVGTopAttributes *attributes;
+ dw::VBox *messages;
+
+ GraphObject (ObjViewGraph *graph, const char *id);
+ ~GraphObject ();
+
+ };
+
+ // Inernally added for the first occurence of an identity.
+ class CreateRefCommand: public OVGCommonCommand
+ {
+ protected:
+ void doExec ();
+ void doUndo ();
+
+ public:
+ CreateRefCommand (ObjViewGraph *graph, const char *id,
+ GraphObject *graphObject);
+ };
+
+ class Color: public lout::object::Object
+ {
+ public:
+ char *identifier;
+ ::dw::core::style::Color *color;
+
+ Color (const char *classPattern, const char *color,
+ ::dw::core::Layout *layout);
+ ~Color ();
+ };
+
+ class ColorComparator: public lout::object::Comparator
+ {
+ private:
+ int specifity (const char *pattern);
+
+ public:
+ int compare(Object *o1, Object *o2);
+ };
+
+ ObjViewFilterTool *filterTool;
+
+ lout::container::typed::Vector<OVGCommand> *commands;
+ lout::container::typed::Vector<OVGCommand> *navigableCommands;
+ lout::container::typed::HashTable<lout::object::String,
+ GraphObject> *objectsById;
+ lout::container::typed::Vector<GraphObject> *allObjects;
+ lout::container::typed::Vector<Color> *classColors;
+ lout::container::typed::Vector<Color> *objectColors;
+
+ lout::container::typed::Stack<OVGEnterCommand> *enterCommands;
+ lout::container::typed::Stack<OVGIncIndentCommand> *startCommands;
+
+ bool objectContents, objectMessages;
+ char *codeViewer;
+ int navigableCommandsPos, hiddenBefore, hiddenAfter, numVisibleCommands;
+
+ ::dw::core::style::Style *nodeStyle, *noBorderStyle, *topBorderStyle,
+ *bottomBorderStyle, *leftBorderStyle;
+ bool inDestructor;
+
+ lout::container::typed::List <ObjViewStacktraceWindow> *stacktraceWindows;
+ ObjViewGraphListener *stacktraceListener;
+
+ GraphObject *ensureObject (const char *id);
+ OVGAttribute *addAttribute (OVGAttributesList *attributesList, char **parts,
+ const char *value, dw::Label **smallLabel,
+ dw::HBox **histBox, dw::Label **indexLabel,
+ dw::Label **histLabel);
+
+ ::dw::core::style::Color *getObjectColor (GraphObject *obj);
+ void applyClassOrObjectStyle (GraphObject *obj);
+ void applyClassOrObjectStyles ();
+
+ void addMessage (const char *id, const char *message, dw::HBox **mainBox,
+ dw::Label **indexLabel, dw::Label **mainLabel);
+ bool incIndent (const char *id);
+ bool decIndent (const char *id);
+ OVGAttribute *addAttribute (const char *id, const char *name,
+ const char *value, dw::Label **smallLabel,
+ dw::HBox **histBox, dw::Label **indexLabel,
+ dw::Label **histLabel);
+ void setClassName (const char *id, const char *className);
+ void addAssoc (const char *id1, const char *id2);
+
+ void clearSelection ();
+ void unclearSelection ();
+ int firstCommand ();
+ int lastCommand ();
+
+public:
+ ObjViewGraph (ObjViewFilterTool *objectFilterTool);
+ ~ObjViewGraph ();
+ void initStyles (const char *fontName, int fontSize, int fgColor,
+ int graphBgColor, int objBgColor, int borderThickness,
+ int grraphMargin);
+
+ void addCommand (OVGCommand *command, bool navigable);
+ void addIdentity (const char *id1, const char *id2);
+ void setClassColor (const char *klass, const char *color);
+ void setObjectColor (const char *id, const char *color);
+
+ int getObjectColor (const char *id);
+
+ inline void pushEnterCommand (OVGEnterCommand *enterCommand)
+ { enterCommands->push (enterCommand); }
+ void popEnterCommand () { enterCommands->pop (); }
+ OVGEnterCommand *getLastEnterCommand () { return enterCommands->getTop (); }
+
+ inline void pushStartCommand (OVGIncIndentCommand *startCommand)
+ { startCommands->push (startCommand); }
+ void popStartCommand () { startCommands->pop (); }
+ OVGIncIndentCommand *getLastStartCommand ()
+ { return startCommands->getTop (); }
+
+ inline void addCommandMark (OVGAddMarkCommand *markCommand)
+ { filterTool->addMark (markCommand); }
+
+ void previousCommand ();
+ void nextCommand ();
+ void viewCodeOfCommand ();
+ void showStackTraceOfCommand ();
+ void switchBetweenRelatedCommands ();
+ void setCommand (int index);
+ void hideBeforeCommand ();
+ void hideAfterCommand ();
+ void hideAllCommands ();
+ void showBeforeCommand ();
+ void showAfterCommand ();
+ void showAllCommands ();
+
+ inline void showObjectContents (bool val) { objectContents = val; }
+ inline void showObjectMessages (bool val) { objectMessages = val; }
+ void setCodeViewer (const char *codeViewer);
+
+ void recalculateCommandsVisibility ();
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJVIEW_GRAPH_HH__
diff --git a/objects/objview_stacktrace.cc b/objects/objview_stacktrace.cc
new file mode 100644
index 0000000..e5c8dc8
--- /dev/null
+++ b/objects/objview_stacktrace.cc
@@ -0,0 +1,127 @@
+/*
+ * 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 "objview_stacktrace.hh"
+
+#include "config.h"
+
+#include <FL/Fl_Button.H>
+#include <FL/Fl_Return_Button.H>
+
+namespace rtfl {
+
+namespace objects {
+
+ObjViewStacktraceWindow::ObjViewStacktraceWindow (ObjViewFunction *topFunction,
+ ObjViewListener *listener) :
+ Fl_Window (WIDTH, HEIGHT, "RTFL: Stack trace")
+{
+ callback(windowCallback, this);
+
+ browser =
+ new Fl_Hold_Browser (SPACE, SPACE, WIDTH - 2 * SPACE,
+ HEIGHT - 3 * SPACE - BUTTON_HEIGHT, NULL);
+ Fl_Button *jumpTo =
+ new Fl_Button(WIDTH - 2 * BUTTON_WIDTH - 2 * SPACE,
+ HEIGHT - BUTTON_HEIGHT - SPACE, BUTTON_WIDTH,
+ BUTTON_HEIGHT, "Jump to");
+ jumpTo->callback (ObjViewStacktraceWindow::jumpTo, this);
+ Fl_Return_Button *close =
+ new Fl_Return_Button(WIDTH - BUTTON_WIDTH - SPACE,
+ HEIGHT - BUTTON_HEIGHT - SPACE, BUTTON_WIDTH,
+ BUTTON_HEIGHT, "Close");
+ close->callback (ObjViewStacktraceWindow::close, this);
+
+ resizable (browser);
+
+ this->topFunction = topFunction;
+ this->listener = listener;
+}
+
+ObjViewStacktraceWindow::~ObjViewStacktraceWindow ()
+{
+}
+
+void ObjViewStacktraceWindow::update ()
+{
+ browser->clear ();
+
+ for (ObjViewFunction *fun = topFunction; fun;
+ fun = fun->getParent ()) {
+ char *name = fun->createName ();
+
+ char name2[1024];
+ name2[0] = 0;
+
+ if (!fun->isSelectable ()) {
+ strcpy (name2, "@i");
+ }
+
+ sprintf (name2 + strlen (name2), "@B%ld@.",
+ ((long)fun->getColor ()) << 8);
+
+ char *s = name2 + strlen (name2);
+ for (int i = 0; name[i]; i++) {
+ if (name[i] == '@')
+ *(s++) = '@';
+ *(s++) = name[i];
+ }
+ *s = 0;
+
+ browser->add (name2, NULL);
+
+ fun->freeName (name);
+ }
+}
+
+void ObjViewStacktraceWindow::windowCallback (Fl_Widget *widget, void *data)
+{
+ close (widget, data);
+}
+
+void ObjViewStacktraceWindow::jumpTo (Fl_Widget *widget, void *data)
+{
+ ObjViewStacktraceWindow *win = (ObjViewStacktraceWindow*)data;
+
+ ObjViewFunction *fun = win->topFunction;
+ for (int i = 1; fun; fun = fun->getParent (), i++) {
+ if (win->browser->selected (i) && fun->isSelectable ())
+ fun->select ();
+ }
+}
+
+void ObjViewStacktraceWindow::close (Fl_Widget *widget, void *data)
+{
+ ObjViewStacktraceWindow *win = (ObjViewStacktraceWindow*)data;
+ win->listener->close (win);
+}
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objview_stacktrace.hh b/objects/objview_stacktrace.hh
new file mode 100644
index 0000000..5ea8d7e
--- /dev/null
+++ b/objects/objview_stacktrace.hh
@@ -0,0 +1,60 @@
+#ifndef __OBJECTS_OBJVIEW_STRACKTRACE_HH__
+#define __OBJECTS_OBJVIEW_STRACKTRACE_HH__
+
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Hold_Browser.H>
+
+#include "lout/object.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+class ObjViewFunction
+{
+public:
+ virtual char *createName () = 0;
+ virtual void freeName (char *name) = 0;
+ virtual int getColor () = 0; // as 0xRRGGBB
+ virtual ObjViewFunction *getParent () = 0;
+ virtual bool isSelectable () = 0;
+ virtual void select () = 0;
+};
+
+class ObjViewStacktraceWindow;
+
+// Somewhat simpler than using signals
+class ObjViewListener
+{
+public:
+ virtual ~ObjViewListener () { }
+ virtual void close (ObjViewStacktraceWindow *window) = 0;
+};
+
+class ObjViewStacktraceWindow: public Fl_Window, public lout::object::Object
+{
+private:
+ enum { WIDTH = 450, HEIGHT = 300, BUTTON_WIDTH = 80, BUTTON_HEIGHT = 25,
+ SPACE = 10 };
+
+ Fl_Hold_Browser *browser;
+ ObjViewFunction *topFunction;
+ ObjViewListener *listener;
+
+ static void windowCallback (Fl_Widget *widget, void *data);
+ static void jumpTo (Fl_Widget *widget, void *data);
+ static void close (Fl_Widget *widget, void *data);
+
+public:
+ ObjViewStacktraceWindow (ObjViewFunction *topFunction,
+ ObjViewListener *listener);
+ ~ObjViewStacktraceWindow ();
+
+ void update ();
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJVIEW_STRACKTRACE_HH__
diff --git a/objects/objview_window.cc b/objects/objview_window.cc
new file mode 100644
index 0000000..211076d
--- /dev/null
+++ b/objects/objview_window.cc
@@ -0,0 +1,707 @@
+/*
+ * 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 <FL/Fl.H>
+#include <limits.h>
+
+#include "objview_window.hh"
+
+#include "dw/core.hh"
+#include "dw/fltkcore.hh"
+#include "dw/fltkviewport.hh"
+
+#include "dwr/graph.hh"
+
+using namespace dw;
+using namespace dw::core;
+using namespace dw::core::style;
+using namespace dw::fltk;
+using namespace lout::object;
+using namespace lout::container::typed;
+using namespace lout::misc;
+
+namespace rtfl {
+
+namespace objects {
+
+ObjViewWindow::Aspect::Aspect (ObjViewWindow *window, const char *name,
+ bool set)
+{
+ this->window = window;
+ this->name = strdup (name);
+ this->set = set;
+}
+
+
+ObjViewWindow::Aspect::~Aspect ()
+{
+ free (name);
+}
+
+
+bool ObjViewWindow::Aspect::equals(Object *other)
+{
+ Aspect *otherAspect = (Aspect*)other;
+ return strcmp (name, otherAspect->name) == 0 &&
+ ((set && otherAspect->set) || (!set && !otherAspect->set));
+}
+
+
+int ObjViewWindow::Aspect::compareTo(Comparable *other)
+{
+ Aspect *otherAspect = (Aspect*)other;
+ return strcmp (name, otherAspect->name);
+}
+
+
+// ----------------------------------------------------------------------
+
+
+ObjViewWindow::Priority::Priority (ObjViewWindow *window, int value)
+{
+ this->window = window;
+ this->value = value;
+}
+
+
+bool ObjViewWindow::Priority::equals(Object *other)
+{
+ Priority *otherPriority = (Priority*)other;
+ return value == otherPriority->value;
+}
+
+
+int ObjViewWindow::Priority::compareTo(Comparable *other)
+{
+ Priority *otherPriority = (Priority*)other;
+ return value - otherPriority->value;
+}
+
+
+// ----------------------------------------------------------------------
+
+
+ObjViewWindow::ObjViewWindow (int width, int height, const char *title):
+ Fl_Window (width, height, title)
+{
+ shown = false;
+
+ aspects = new HashTable<String, Aspect> (true, true);
+ aspectsMenuPositions = new Vector<Integer> (1, true);;
+ aspectsInitiallySet = true;
+
+ priorities = new HashTable<Integer, Priority> (true, true);
+ prioritiesMenuPositions = new Vector<Integer> (1, true);;
+ selectedPriority = INT_MAX;
+
+ // Special priority "no priority" (INT_MAX):
+ Priority *noLimitsPriority = new Priority (this, INT_MAX);
+ priorities->put (new Integer (INT_MAX), noLimitsPriority);
+
+ aboutWindow = NULL;
+
+ int menuHeight = 24;
+
+ FltkPlatform *platform = new FltkPlatform ();
+ layout = new Layout (platform);
+
+ callback(windowCallback, NULL);
+ box(FL_NO_BOX);
+
+ menu = new Fl_Menu_Bar(0, 0, width, menuHeight);
+
+ FltkViewport *viewport =
+ new FltkViewport (0, menuHeight, width, height - menuHeight);
+ layout->attachView (viewport);
+
+ graph = new ObjViewGraph (this);
+ layout->setWidget (graph);
+ graph->initStyles ("DejaVu Sans", 12, 0x000000, 0xffffff, 0xffffd0, 1, 10);
+
+ Fl_Menu_Item menuItems[] = {
+ { "&File", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 },
+ { "Quit", FL_COMMAND + 'q', quit, this, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+ { "&Command", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 },
+ { "&Previous", FL_COMMAND + 'p', previous, this, 0, 0, 0, 0, 0 },
+ { "&Next", FL_COMMAND + 'n', next, this, 0, 0, 0, 0, 0 },
+ { "View &Code", FL_COMMAND + 'c', viewCode, this, FL_MENU_DIVIDER,
+ 0, 0, 0, 0 },
+ { "Hide &before", FL_COMMAND + 'b', hideBefore, this, 0, 0, 0, 0, 0 },
+ { "Hide &after", FL_COMMAND + 'a', hideAfter, this, 0, 0, 0, 0, 0 },
+ { "&Hide all", FL_COMMAND + 'h', hideAll, this, FL_MENU_DIVIDER,
+ 0, 0, 0, 0 },
+ { "Show b&efore", FL_COMMAND + 'e', showBefore, this, 0, 0, 0, 0, 0 },
+ { "Show a&fter", FL_COMMAND + 'f', showAfter, this, 0, 0, 0, 0, 0 },
+ { "&Show all", FL_COMMAND + 's', showAll, this, FL_MENU_DIVIDER,
+ 0, 0, 0, 0 },
+ { "Show stack trace", FL_COMMAND + 't', showStackTrace, this, 0, 0, 0, 0,
+ 0 },
+ { "Switch between related", FL_COMMAND + 'r', switchBetweenRelated, this,
+ FL_MENU_DIVIDER, 0, 0, 0, 0 },
+ { "&Creations", 0, toggleCommandTypeVisibility, this, FL_MENU_TOGGLE,
+ 0, 0, 0, 0 },
+ { "&Indentations", 0, toggleCommandTypeVisibility, this, FL_MENU_TOGGLE,
+ 0, 0, 0, 0 },
+ { "&Messages", 0, toggleCommandTypeVisibility, this,
+ FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 },
+ { "M&arks", 0, toggleCommandTypeVisibility, this,
+ FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 },
+ { "&Functions", 0, toggleCommandTypeVisibility, this,
+ FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 },
+ { "A&ssociations", 0, toggleCommandTypeVisibility, this,
+ FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 },
+ { "A&ttributes", 0, toggleCommandTypeVisibility, this,
+ FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 },
+ { "&Deletions", 0, toggleCommandTypeVisibility, this,
+ FL_MENU_TOGGLE | FL_MENU_VALUE, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+ { "&Aspects", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 },
+ { "&Show all", 0, showAllAspects, this, FL_MENU_TOGGLE | FL_MENU_VALUE,
+ 0, 0, 0, 0 },
+ { "&Hide all", 0, hideAllAspects, this, FL_MENU_TOGGLE | FL_MENU_DIVIDER,
+ 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+ { "&Priorities", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 },
+ { "&No limit", 0, setPriority, noLimitsPriority,
+ FL_MENU_RADIO | FL_MENU_VALUE /*| FL_MENU_DIVIDER*/, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+ { "&Marks", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+ { "&Help", 0, 0, 0, FL_SUBMENU, 0, 0, 0, 0 },
+ { "&About RTFL", 0, about, this, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+ };
+ menu->copy(menuItems);
+
+ noLimitsPriority->menuItem =
+ (Fl_Menu_Item*)(menu->find_item ("&Priorities/&No limit"));
+
+ resizable(viewport);
+}
+
+ObjViewWindow::~ObjViewWindow ()
+{
+ delete aspects;
+ delete aspectsMenuPositions;
+ delete priorities;
+ delete prioritiesMenuPositions;
+ if (aboutWindow)
+ delete aboutWindow;
+ delete layout;
+}
+
+
+void ObjViewWindow::show ()
+{
+ shown = true;
+
+ Integer key (selectedPriority);
+ Priority *priorityEntry = priorities->get (&key);
+ priorityEntry->menuItem->activate ();
+
+ Fl_Window::show ();
+}
+
+void ObjViewWindow::addAspect (const char *aspect)
+{
+ addAspect (aspect, aspectsInitiallySet);
+}
+
+void ObjViewWindow::setAspectsInitiallySet (bool val)
+{
+ aspectsInitiallySet = val;
+ showOrHideAllAspects (aspectsInitiallySet);
+
+ if (aspectsInitiallySet) {
+ getShowAllAspectsMenuItem()->set ();
+ getHideAllAspectsMenuItem()->clear ();
+ } else {
+ getShowAllAspectsMenuItem()->clear ();
+ getHideAllAspectsMenuItem()->set ();
+ }
+}
+
+void ObjViewWindow::addPriority (int priority)
+{
+ addPriority (priority, false);
+}
+
+
+bool ObjViewWindow::isAspectSelected (const char *aspect)
+{
+ String key (aspect);
+ Aspect *aspectEntry = aspects->get (&key);
+ assert (aspectEntry != NULL); // Should have been added before.
+ return aspectEntry->set;
+}
+
+
+bool ObjViewWindow::isPrioritySelected (int priority)
+{
+ return priority <= selectedPriority;
+}
+
+
+bool ObjViewWindow::isTypeSelected (OVGCommandType type)
+{
+ switch (type) {
+ case OVG_COMMAND_CREATE:
+ return getCreateMenuItem()->value ();
+
+ case OVG_COMMAND_INDENT:
+ return getIndentMenuItem()->value ();
+
+ case OVG_COMMAND_MESSAGE:
+ return getMessageMenuItem()->value ();
+
+ case OVG_COMMAND_MARK:
+ return getMarkMenuItem()->value ();
+
+ case OVG_COMMAND_FUNCTION:
+ return getFunctionMenuItem()->value ();
+
+ case OVG_COMMAND_ASSOC:
+ return getAssocMenuItem()->value ();
+
+ case OVG_COMMAND_ADD_ATTR:
+ return getAddAttrMenuItem()->value ();
+
+ case OVG_COMMAND_DELETE:
+ return getDeleteMenuItem()->value ();
+
+ }
+
+ assertNotReached ();
+ return false;
+}
+
+void ObjViewWindow::addMark (OVGAddMarkCommand *markCommand)
+{
+ int pathLen = 6 + strlen (markCommand->getMark ()) + 1;
+ char *path = new char[pathLen];
+ snprintf (path, pathLen, "Marks/%s", markCommand->getMark ());
+ int menuPos = menu->add (path, 0, NULL, NULL, 0);
+ delete[] path;
+ Fl_Menu_Item *menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos);
+ menuItem->callback (jumpToMark, markCommand);
+}
+
+
+void ObjViewWindow::addAspect (const char *aspect, bool val)
+{
+ String key (aspect);
+ Aspect *aspectEntry = aspects->get (&key);
+ if (aspectEntry == NULL) {
+ aspectEntry = new Aspect (this, aspect, val);
+ aspects->put (new String (aspect), aspectEntry);
+
+#if 1
+ int pathLen = 8 + strlen (aspect) + 1;
+ char *path = new char[pathLen];
+ snprintf (path, pathLen, "Aspects/%s", aspect);
+ int menuPos =
+ menu->add (path, 0, NULL, NULL,
+ FL_MENU_TOGGLE | (aspectEntry->set ? FL_MENU_VALUE : 0));
+ delete[] path;
+ aspectEntry->menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos);
+ aspectEntry->menuItem->callback (toggleAspect, aspectEntry);
+
+ // TODO aspectsMenuPositions is only used in the deactivated
+ // code.
+#else
+ // Aspects are sorted alphabetically, so we choose the following
+ // approach:
+ //
+ // 1. Add a new entry, which is not used for the new aspect, but
+ // for the last aspect after sorting. For this reason, a
+ // random name is choosen.
+ //
+ // 2. Sort all aspects.
+ //
+ // 3. Re-assign all aspects to the menu entries.
+
+ // 1. Add new entry.
+ //
+ // It seams that a menu entry must have a unique name, so it is
+ // important to choose a temporary one which does not collide
+ // with an aspect. This one is the result of:
+ //
+ // $ head -c 32 < /dev/random | base64 | sed 's/\///g'
+ //
+ // (Further checks are not done, hoping that noone chooses this
+ // as an aspect.)
+
+ int menuPos =
+ menu->add ("Aspects/xgIUCOm3HTvK5P33Dsk3lPSL1qgXg4Iye3APe0ckMo0=",
+ 0, NULL, NULL, FL_MENU_TOGGLE);
+ aspectsMenuPositions->put (new Integer (menuPos));
+
+ // 2. Sort all aspects.
+
+ Vector<Aspect> sortedAspects (1, false);
+ for (lout::container::typed::Iterator<String> it = aspects->iterator ();
+ it.hasNext (); ) {
+ String *key = it.getNext ();
+ Aspect *value = aspects->get (key);
+ sortedAspects.insertSorted (value);
+ }
+
+ // 3. Re-assign all aspects to the menu entries.
+
+ assert (sortedAspects.size () == aspectsMenuPositions->size ());
+
+ for (int i = 0; i < sortedAspects.size (); i++) {
+ Aspect *aspect = sortedAspects.get (i);
+ int menuPos = aspectsMenuPositions->get(i)->getValue ();
+
+ // This is somewhat ugly (ignoring "const"):
+ aspect->menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos);
+ //aspect->menuItem->label (aspect->name);
+ menu->replace (menuPos, aspect->name);
+ if (aspect->set)
+ aspect->menuItem->set ();
+ else
+ aspect->menuItem->clear ();
+ aspect->menuItem->callback (toggleAspect, aspect);
+ }
+#endif
+ }
+}
+
+
+ObjViewWindow::Priority *ObjViewWindow::addPriority (int priority, bool val)
+{
+ // Similar to addAspect().
+
+ Integer key (priority);
+ Priority *priorityEntry = priorities->get (&key);
+ if (priorityEntry == NULL) {
+ priorityEntry = new Priority (this, priority);
+ priorities->put (new Integer (priority), priorityEntry);
+
+#if 1
+ int pathLen = 11 + 5 + 1;
+ char path[pathLen];
+ snprintf (path, pathLen, "Priorities/%d", priority);
+ int menuPos = menu->add (path, 0, NULL, NULL,
+ FL_MENU_RADIO | (val ? FL_MENU_VALUE : 0));
+ priorityEntry->menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos);
+ priorityEntry->menuItem->callback (setPriority, priorityEntry);
+
+ // TODO prioritiesMenuPositions is only used in the deactivated
+ // code.
+#else
+ // 1. Add new entry.
+
+ int menuPos =
+ menu->add ("Priorities/xgIUCOm3HTvK5P33Dsk3lPSL1qgXg4Iye3APe0ckMo0=",
+ 0, NULL, NULL, FL_MENU_RADIO);
+ prioritiesMenuPositions->put (new Integer (menuPos));
+
+ // 2. Sort all priorities.
+
+ Vector<Priority> sortedPriorities (1, false);
+ for (lout::container::typed::Iterator<Integer> it =
+ priorities->iterator (); it.hasNext (); ) {
+ Integer *key = it.getNext ();
+ Priority *value = priorities->get (key);
+ // "No limits" entry is not sorted.
+ if (value->value != INT_MAX)
+ sortedPriorities.insertSorted (value);
+ }
+
+ // 3. Re-assign all priorities to the menu entries (except "No limits").
+
+ assert (sortedPriorities.size () == prioritiesMenuPositions->size ());
+
+ for (int i = 0; i < sortedPriorities.size (); i++) {
+ Priority *priority = sortedPriorities.get (i);
+ int menuPos = prioritiesMenuPositions->get(i)->getValue ();
+
+ // Assuming maximal priorities of 99999 (snprintf will at
+ // least prevent buffer overflows).
+ snprintf (priority->valueBuf, 6, "%d", priority->value);
+
+ // This is somewhat ugly (ignoring "const"):
+ priority->menuItem = (Fl_Menu_Item*)(menu->menu() + menuPos);
+ //priority->menuItem->label (priority->valueBuf);
+ menu->replace (menuPos, priority->valueBuf);
+ if (priority->value == selectedPriority)
+ priority->menuItem->set ();
+ else
+ priority->menuItem->clear ();
+ priority->menuItem->callback (setPriority, priority);
+ }
+#endif
+ }
+
+ return priorityEntry;
+}
+
+void ObjViewWindow::setPriority (int priority)
+{
+ assert (!shown);
+
+ selectedPriority = priority;
+ addPriority (priority, true);
+ graph->recalculateCommandsVisibility ();
+}
+
+
+void ObjViewWindow::setAnyPriority ()
+{
+ assert (!shown);
+
+ selectedPriority = INT_MAX;
+ graph->recalculateCommandsVisibility ();
+}
+
+
+
+void ObjViewWindow::quit (Fl_Widget *widget, void *data)
+{
+ exit (0);
+}
+
+
+void ObjViewWindow::previous (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->previousCommand ();
+}
+
+
+void ObjViewWindow::next (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->nextCommand ();
+}
+
+
+void ObjViewWindow::viewCode (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->viewCodeOfCommand ();
+}
+
+
+void ObjViewWindow::hideBefore (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->hideBeforeCommand ();
+}
+
+
+void ObjViewWindow::hideAfter (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->hideAfterCommand ();
+}
+
+
+void ObjViewWindow::hideAll (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->hideAllCommands ();
+}
+
+
+void ObjViewWindow::showBefore (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->showBeforeCommand ();
+}
+
+
+void ObjViewWindow::showAfter (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->showAfterCommand ();
+}
+
+
+void ObjViewWindow::showAll (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->showAllCommands ();
+}
+
+void ObjViewWindow::showStackTrace (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->showStackTraceOfCommand ();
+}
+
+void ObjViewWindow::switchBetweenRelated (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->switchBetweenRelatedCommands ();
+}
+
+void ObjViewWindow::toggleCommandTypeVisibility (Fl_Widget *widget, void *data)
+{
+ ((ObjViewWindow*)data)->graph->recalculateCommandsVisibility ();
+}
+
+
+void ObjViewWindow::showAllAspects (Fl_Widget *widget, void *data)
+{
+ ObjViewWindow *objectsWindow = (ObjViewWindow*)data;
+ bool showSet = objectsWindow->getShowAllAspectsMenuItem()->value ();
+ objectsWindow->showOrHideAllAspects (showSet);
+
+ if (showSet)
+ objectsWindow->getHideAllAspectsMenuItem()->clear ();
+ else
+ objectsWindow->getHideAllAspectsMenuItem()->set ();
+
+ objectsWindow->aspectsInitiallySet = true;
+}
+
+
+void ObjViewWindow::hideAllAspects (Fl_Widget *widget, void *data)
+{
+ ObjViewWindow *objectsWindow = (ObjViewWindow*)data;
+ bool hideSet = objectsWindow->getHideAllAspectsMenuItem()->value ();
+ objectsWindow->showOrHideAllAspects (!hideSet);
+
+ if (hideSet)
+ objectsWindow->getShowAllAspectsMenuItem()->clear ();
+ else
+ objectsWindow->getShowAllAspectsMenuItem()->set ();
+
+ objectsWindow->aspectsInitiallySet = false;
+}
+
+
+void ObjViewWindow::showOrHideAllAspects (bool value)
+{
+ for (lout::container::typed::Iterator<String> it = aspects->iterator ();
+ it.hasNext (); ) {
+ String *key = it.getNext ();
+ Aspect *aspect = aspects->get (key);
+ aspect->set = value;
+ if (value)
+ aspect->menuItem->set ();
+ else
+ aspect->menuItem->clear ();
+ }
+
+ graph->recalculateCommandsVisibility ();
+}
+
+
+void ObjViewWindow::toggleAspect (Fl_Widget *widget, void *data)
+{
+ Aspect *aspect = (Aspect*)data;
+ aspect->set = aspect->menuItem->value ();
+
+ bool allShown = true, allHidden = true;
+ for (lout::container::typed::Iterator<String> it =
+ aspect->window->aspects->iterator (); it.hasNext (); ) {
+ String *key = it.getNext ();
+ Aspect *value = aspect->window->aspects->get (key);
+ if (value->set)
+ allHidden = false;
+ else
+ allShown = false;
+ }
+
+ // There is at least one entry; otherwise, this method would not be
+ // called. (With no elements, allShown && allHidden would always be
+ // true.)
+ assert (!(allShown && allHidden));
+
+ // If neither all shown, nor all hidden, aspectsInitiallySet is not
+ // changed.
+
+ if (allShown) {
+ aspect->window->getShowAllAspectsMenuItem()->set ();
+ aspect->window->aspectsInitiallySet = true;
+ } else
+ aspect->window->getShowAllAspectsMenuItem()->clear ();
+
+ if (allHidden) {
+ aspect->window->getHideAllAspectsMenuItem()->set ();
+ aspect->window->aspectsInitiallySet = false;
+ } else
+ aspect->window->getHideAllAspectsMenuItem()->clear ();
+
+ aspect->window->graph->recalculateCommandsVisibility ();
+}
+
+
+void ObjViewWindow::setPriority (Fl_Widget *widget, void *data)
+{
+ Priority *priority = (Priority*)data;
+ priority->window->selectedPriority = priority->value;
+ priority->window->graph->recalculateCommandsVisibility ();
+}
+
+void ObjViewWindow::jumpToMark (Fl_Widget *widget, void *data)
+{
+ OVGAddMarkCommand *markCommand = (OVGAddMarkCommand*)data;
+ if (markCommand->isSelectable ())
+ markCommand->select ();
+}
+
+void ObjViewWindow::about (Fl_Widget *widget, void *data)
+{
+ ObjViewWindow *window = (ObjViewWindow*)data;
+
+ if (window->aboutWindow == NULL)
+ window->aboutWindow =
+ new common::AboutWindow("rtfl-objview",
+ "; with the following exception:\n"
+ "\n"
+ "The copyright holders of RTFL give you "
+ "permission to link rtfl-objview "
+ "statically or dynamically against all "
+ "versions of the graphviz library, which are "
+ "published by AT&T Corp. under one of the "
+ "following licenses:\n"
+ "\n"
+ "- Common Public License version 1.0 as "
+ "published by International Business Machines "
+ " Corporation (IBM), or\n"
+ "- Eclipse Public License version 1.0 as "
+ "published by the Eclipse Foundation",
+ common::AboutWindow::HEIGHT_EXCEPTION);
+ window->aboutWindow->show ();
+}
+
+
+void ObjViewWindow::windowCallback (Fl_Widget *widget, void *data)
+{
+ // Ignore escape key. TODO Looks rather hackish to me.
+ if (Fl::event_key() != FL_Escape)
+ quit (widget, data);
+}
+
+
+} // namespace objects
+
+} // namespace rtfl
diff --git a/objects/objview_window.hh b/objects/objview_window.hh
new file mode 100644
index 0000000..053b845
--- /dev/null
+++ b/objects/objview_window.hh
@@ -0,0 +1,189 @@
+#ifndef __OBJECTS_OBJVIEW_WINDOW_HH__
+#define __OBJECTS_OBJVIEW_WINDOW_HH__
+
+#include <FL/Fl_Window.H>
+#include <FL/Fl_Menu_Bar.H>
+#include <FL/Fl_Menu_Item.H>
+#include "objview_graph.hh"
+#include "lout/container.hh"
+#include "common/about.hh"
+
+namespace rtfl {
+
+namespace objects {
+
+class ObjViewWindow: public Fl_Window, public ObjViewFilterTool
+{
+private:
+ class Aspect: public lout::object::Comparable
+ {
+ public:
+ ObjViewWindow *window;
+ char *name;
+ bool set;
+ Fl_Menu_Item *menuItem;
+
+ Aspect (ObjViewWindow *window, const char *name, bool set);
+ ~Aspect ();
+
+ bool equals(Object *other);
+ int compareTo(Comparable *other);
+ };
+
+ class Priority: public lout::object::Comparable
+ {
+ public:
+ ObjViewWindow *window;
+ int value;
+ char valueBuf[6];
+ Fl_Menu_Item *menuItem;
+
+ Priority (ObjViewWindow *window, int value);
+
+ bool equals(Object *other);
+ int compareTo(Comparable *other);
+ };
+
+ bool shown;
+
+ ::dw::core::Layout *layout;
+ ObjViewGraph *graph;
+ Fl_Window *aboutWindow;
+ Fl_Menu_Bar *menu;
+
+ lout::container::typed::HashTable<lout::object::String, Aspect> *aspects;
+ lout::container::typed::Vector<lout::object::Integer> *aspectsMenuPositions;
+ bool aspectsInitiallySet;
+
+ lout::container::typed::HashTable<lout::object::Integer, Priority>
+ *priorities;
+ lout::container::typed::Vector<lout::object::Integer>
+ *prioritiesMenuPositions;
+ int selectedPriority;
+
+ inline Fl_Menu_Item *getCreateMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Command/&Creations"); }
+
+ inline Fl_Menu_Item *getIndentMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Command/&Indentations"); }
+
+ inline Fl_Menu_Item *getMessageMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Command/&Messages"); }
+
+ inline Fl_Menu_Item *getMarkMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Command/M&arks"); }
+
+ inline Fl_Menu_Item *getFunctionMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Command/&Functions"); }
+
+ inline Fl_Menu_Item *getAssocMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Command/A&ssociations"); }
+
+ inline Fl_Menu_Item *getAddAttrMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Command/A&ttributes"); }
+
+ inline Fl_Menu_Item *getDeleteMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Command/&Deletions"); }
+
+ inline Fl_Menu_Item *getShowAllAspectsMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Aspects/&Show all"); }
+
+ inline Fl_Menu_Item *getHideAllAspectsMenuItem ()
+ { return (Fl_Menu_Item*)menu->find_item("&Aspects/&Hide all"); }
+
+ static void quit (Fl_Widget *widget, void *data);
+ static void previous (Fl_Widget *widget, void *data);
+ static void next (Fl_Widget *widget, void *data);
+ static void viewCode (Fl_Widget *widget, void *data);
+ static void hideBefore (Fl_Widget *widget, void *data);
+ static void hideAfter (Fl_Widget *widget, void *data);
+ static void hideAll (Fl_Widget *widget, void *data);
+ static void showBefore (Fl_Widget *widget, void *data);
+ static void showAfter (Fl_Widget *widget, void *data);
+ static void showAll (Fl_Widget *widget, void *data);
+ static void showStackTrace (Fl_Widget *widget, void *data);
+ static void switchBetweenRelated (Fl_Widget *widget, void *data);
+ static void toggleCommandTypeVisibility (Fl_Widget *widget, void *data);
+ static void showAllAspects (Fl_Widget *widget, void *data);
+ static void hideAllAspects (Fl_Widget *widget, void *data);
+ static void toggleAspect (Fl_Widget *widget, void *data);
+ static void setPriority (Fl_Widget *widget, void *data);
+ static void jumpToMark (Fl_Widget *widget, void *data);
+ static void about (Fl_Widget *widget, void *data);
+ static void windowCallback (Fl_Widget *widget, void *data);
+
+ Priority *addPriority (int priority, bool val);
+
+ void showOrHideAllAspects (bool value);
+
+public:
+ ObjViewWindow (int width, int height, const char *title);
+ ~ObjViewWindow ();
+
+ void show();
+
+ void addAspect (const char *aspect);
+ void addPriority (int priority);
+ bool isAspectSelected (const char *aspect);
+ bool isPrioritySelected (int priority);
+ bool isTypeSelected (OVGCommandType type);
+ void addMark (OVGAddMarkCommand *markCommand);
+
+ void addAspect (const char *aspect, bool val);
+ void setAspectsInitiallySet (bool val);
+ void setPriority (int priority);
+ void setAnyPriority ();
+
+ inline ObjViewGraph *getObjViewGraph () { return graph; }
+
+ inline void showCreateCommands (bool val) {
+ if (val) getCreateMenuItem()->set ();
+ else getCreateMenuItem()->clear ();
+ }
+
+ inline void showIndentCommands (bool val) {
+ if (val) getIndentMenuItem()->set ();
+ else getIndentMenuItem()->clear ();
+ }
+
+ inline void showMessageCommands (bool val) {
+ if (val) getMessageMenuItem()->set ();
+ else getMessageMenuItem()->clear ();
+ }
+
+ inline void showMarkCommands (bool val) {
+ if (val) getMarkMenuItem()->set ();
+ else getMarkMenuItem()->clear ();
+ }
+
+ inline void showFunctionCommands (bool val) {
+ if (val) getFunctionMenuItem()->set ();
+ else getFunctionMenuItem()->clear ();
+ }
+
+ inline void showAssocCommands (bool val) {
+ if (val) getAssocMenuItem()->set ();
+ else getAssocMenuItem()->clear ();
+ }
+
+ inline void showAddAttrCommands (bool val) {
+ if (val) getAddAttrMenuItem()->set ();
+ else getAddAttrMenuItem()->clear ();
+ }
+
+ inline void showDeleteCommands (bool val) {
+ if (val) getDeleteMenuItem()->set ();
+ else getDeleteMenuItem()->clear ();
+ }
+
+ inline void showObjectMessages (bool val) { graph->showObjectMessages(val); }
+ inline void showObjectContents (bool val) { graph->showObjectContents(val); }
+ inline void setCodeViewer (const char *codeViewer) {
+ graph->setCodeViewer (codeViewer); }
+};
+
+} // namespace objects
+
+} // namespace rtfl
+
+#endif // __OBJECTS_OBJVIEW_WINDOW_HH__
diff --git a/objects/rtfl_objbase.cc b/objects/rtfl_objbase.cc
new file mode 100644
index 0000000..1da24bb
--- /dev/null
+++ b/objects/rtfl_objbase.cc
@@ -0,0 +1,45 @@
+/*
+ * RTFL
+ *
+ * Copyright 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.
+ *
+ * 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 "objects_parser.hh"
+#include "objects_writer.hh"
+#include "objdelete_controller.hh"
+#include "objident_controller.hh"
+
+#include <fcntl.h>
+
+using namespace rtfl::tools;
+using namespace rtfl::objects;
+
+int main(int argc, char **argv)
+{
+ LinesSourceSequence source (true);
+ int fd = open (".rtfl", O_RDONLY);
+ if (fd != -1)
+ source.add (new BlockingLinesSource (fd));
+ source.add (new BlockingLinesSource (0));
+
+ ObjectsWriter writer;
+ ObjIdentController identController (&writer);
+ ObjDeleteController deleteController (&identController);
+ ObjectsParser parser (&deleteController);
+ source.setup (&parser);
+
+ return 0;
+}
diff --git a/objects/rtfl_objcount.cc b/objects/rtfl_objcount.cc
new file mode 100644
index 0000000..fb71fbb
--- /dev/null
+++ b/objects/rtfl_objcount.cc
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *
+ * 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 "common/fltk_lines.hh"
+#include "objcount_window.hh"
+#include "objcount_controller.hh"
+
+using namespace rtfl::objects;
+using namespace rtfl::common;
+
+int main(int argc, char **argv)
+{
+ ObjCountWindow *window = new ObjCountWindow(800, 600, "RTFL: Objects count");
+ window->show();
+
+ FltkDefaultSource source;
+ ObjCountController controller (window->getTable ());
+ ObjectsParser parser (&controller);
+ source.setup (&parser);
+
+ int errorCode = Fl::run();
+ delete window;
+ return errorCode;
+}
diff --git a/objects/rtfl_objview.cc b/objects/rtfl_objview.cc
new file mode 100644
index 0000000..bed41bd
--- /dev/null
+++ b/objects/rtfl_objview.cc
@@ -0,0 +1,218 @@
+/*
+ * 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 <unistd.h>
+#include <FL/Fl.H>
+#include "common/fltk_lines.hh"
+#include "objview_window.hh"
+#include "objview_controller.hh"
+#include "objdelete_controller.hh"
+#include "objident_controller.hh"
+
+using namespace rtfl::objects;
+using namespace rtfl::common;
+
+static void printHelp (const char *argv0)
+{
+ fprintf
+ (stderr, "Usage: %s <options>\n"
+ "\n"
+ "Options:\n"
+ " -a <aspect> Show,\n"
+ " -A <aspect> hide aspects. <aspect> may be '*'.\n"
+ " -b Do,\n"
+ " -B do not apply \".rtfl\" and filtering identities "
+ "(what \n"
+ " \"rtfl-objbase\" does).\n"
+ " -m Show,\n"
+ " -M hide the messages of all object boxes.\n"
+ " -o Show,\n"
+ " -O hide the contents of all object boxes.\n"
+ " -p <prio> Set priority. <prio> is a number or '*'.\n"
+ " -t <types> Show,\n"
+ " -T <types> hide command types. <types> is a sequence of any "
+ "of the\n"
+ " characters 'c', 'i', 'm', 'a', 'f', 's', 't', "
+ "'d'.\n"
+ " -v <viewer> Use <viewer> to view code. Contains '%%p' as "
+ "variable for\n"
+ " the path, and '%%n' for the line number.\n"
+ "\n"
+ "See RTFL documentation for more details.\n",
+ argv0);
+}
+
+static bool toggleCommandTypes (ObjViewWindow *window, const char *arg,
+ bool val)
+{
+ for (const char *s = arg; *s; s++) {
+ switch (*s) {
+ case 'c':
+ window->showCreateCommands (val);
+ break;
+
+ case 'i':
+ window->showIndentCommands (val);
+ break;
+
+ case 'm':
+ window->showMessageCommands (val);
+ break;
+
+ case 'a':
+ window->showMarkCommands (val);
+ break;
+
+ case 'f':
+ window->showFunctionCommands (val);
+ break;
+
+ case 's':
+ window->showAssocCommands (val);
+ break;
+
+ case 't':
+ window->showAddAttrCommands (val);
+ break;
+
+ case 'd':
+ window->showDeleteCommands (val);
+ break;
+
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ ObjViewWindow *window = new ObjViewWindow(800, 600, "RTFL: Objects view");
+
+ int opt;
+ bool baseFiltering = true;
+
+ while ((opt = getopt(argc, argv, "a:A:bBMmOop:t:T:v:")) != -1) {
+ switch (opt) {
+ case 'a':
+ if (strcmp (optarg, "*") == 0)
+ window->setAspectsInitiallySet (true);
+ else
+ window->addAspect (optarg, true);
+ break;
+
+ case 'A':
+ if (strcmp (optarg, "*") == 0)
+ window->setAspectsInitiallySet (false);
+ else
+ window->addAspect (optarg, false);
+ break;
+
+ case 'b':
+ baseFiltering = true;
+ break;
+
+ case 'B':
+ baseFiltering = false;
+ break;
+
+ case 'm':
+ window->showObjectMessages (true);
+ break;
+
+ case 'M':
+ window->showObjectMessages (false);
+ break;
+
+ case 'o':
+ window->showObjectContents (true);
+ break;
+
+ case 'O':
+ window->showObjectContents (false);
+ break;
+
+ case 'p':
+ if (strcmp (optarg, "*") == 0)
+ window->setAnyPriority ();
+ else
+ window->setPriority (atoi (optarg));
+ break;
+
+ case 't':
+ if (!toggleCommandTypes (window, optarg, true)) {
+ printHelp (argv[0]);
+ delete window;
+ return 1;
+ }
+ break;
+
+ case 'T':
+ if (!toggleCommandTypes (window, optarg, false)) {
+ printHelp (argv[0]);
+ delete window;
+ return 1;
+ }
+ break;
+
+ case 'v':
+ window->setCodeViewer (optarg);
+ break;
+
+ default:
+ printHelp (argv[0]);
+ delete window;
+ return 1;
+ }
+ }
+
+ int errorCode;
+ ObjViewController viewController (window->getObjViewGraph ());
+
+ if (baseFiltering) {
+ FltkDefaultSource source;
+ ObjIdentController identController (&viewController);
+ ObjDeleteController deleteController (&identController);
+ ObjectsParser parser (&deleteController);
+ source.setup (&parser);
+ window->show();
+ errorCode = Fl::run();
+ } else {
+ FltkLinesSource source;
+ ObjectsParser parser (&viewController);
+ source.setup (&parser);
+ window->show();
+ errorCode = Fl::run();
+ }
+
+ delete window;
+ return errorCode;
+}