diff options
author | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-12-10 22:30:12 +0100 |
---|---|---|
committer | Rodrigo Arias Mallo <rodarima@gmail.com> | 2024-12-10 22:30:12 +0100 |
commit | 429d5f88b94ff28416cbfc6420b6389fa284df97 (patch) | |
tree | fb6fdaf7731de1ef396f98b748c56f3149801c84 /objects |
Import RTFL 0.1.1v0.1.1
Diffstat (limited to 'objects')
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; +} |