aboutsummaryrefslogtreecommitdiff
path: root/lout
diff options
context:
space:
mode:
Diffstat (limited to 'lout')
-rw-r--r--lout/Makefile.am14
-rw-r--r--lout/container.cc558
-rw-r--r--lout/container.hh441
-rw-r--r--lout/debug.hh152
-rw-r--r--lout/identity.cc114
-rw-r--r--lout/identity.hh148
-rw-r--r--lout/misc.cc237
-rw-r--r--lout/misc.hh320
-rw-r--r--lout/object.cc311
-rw-r--r--lout/object.hh161
-rw-r--r--lout/signal.cc171
-rw-r--r--lout/signal.hh310
12 files changed, 2937 insertions, 0 deletions
diff --git a/lout/Makefile.am b/lout/Makefile.am
new file mode 100644
index 00000000..18e00cf2
--- /dev/null
+++ b/lout/Makefile.am
@@ -0,0 +1,14 @@
+noinst_LIBRARIES = liblout.a
+
+liblout_a_SOURCES = \
+ container.cc \
+ container.hh \
+ debug.hh \
+ identity.cc \
+ identity.hh \
+ misc.cc \
+ misc.hh \
+ object.cc \
+ object.hh \
+ signal.cc \
+ signal.hh
diff --git a/lout/container.cc b/lout/container.cc
new file mode 100644
index 00000000..0b00c195
--- /dev/null
+++ b/lout/container.cc
@@ -0,0 +1,558 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2005-2007 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+
+#include "container.hh"
+#include "misc.hh"
+
+namespace lout {
+
+using namespace object;
+
+namespace container {
+
+namespace untyped {
+
+// -------------
+// Iterator
+// -------------
+
+Iterator::Iterator()
+{
+ impl = NULL;
+}
+
+Iterator::Iterator(const Iterator &it2)
+{
+ impl = it2.impl;
+ if(impl)
+ impl->ref();
+}
+
+Iterator::Iterator(Iterator &it2)
+{
+ impl = it2.impl;
+ if(impl)
+ impl->ref();
+}
+
+Iterator &Iterator::operator=(const Iterator &it2)
+{
+ if(impl)
+ impl->unref();
+ impl = it2.impl;
+ if(impl)
+ impl->ref();
+ return *this;
+}
+
+Iterator &Iterator::operator=(Iterator &it2)
+{
+ if(impl)
+ impl->unref();
+ impl = it2.impl;
+ if(impl)
+ impl->ref();
+ return *this;
+}
+
+Iterator::~Iterator()
+{
+ if(impl)
+ impl->unref();
+}
+
+// ----------------
+// Collection
+// ----------------
+
+void Collection::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append("{ ");
+ bool first = true;
+ for(Iterator it = iterator(); it.hasNext(); ) {
+ if(!first)
+ sb->append(", ");
+ it.getNext()->intoStringBuffer(sb);
+ first = false;
+ }
+ sb->append(" }");
+}
+
+// ------------
+// Vector
+// ------------
+
+Vector::Vector(int initSize, bool ownerOfObjects)
+{
+ numAlloc = initSize == 0 ? 1 : initSize;
+ this->ownerOfObjects = ownerOfObjects;
+ numElements = 0;
+ array = (Object**)malloc(numAlloc * sizeof(Object*));
+}
+
+Vector::~Vector()
+{
+ clear();
+ free(array);
+}
+
+void Vector::put(Object *newElement, int newPos)
+{
+ if(newPos == -1)
+ newPos = numElements;
+
+ // Old entry is overwritten.
+ if(newPos < numElements) {
+ if(ownerOfObjects && array[newPos])
+ delete array[newPos];
+ }
+
+ // Allocated memory has to be increased.
+ if(newPos >= numAlloc) {
+ while (newPos >= numAlloc)
+ numAlloc *= 2;
+ array = (Object**)realloc(array, numAlloc * sizeof(Object*));
+ }
+
+ // Insert NULL's into possible gap before new position.
+ for(int i = numElements; i < newPos; i++)
+ array[i] = NULL;
+
+ if(newPos >= numElements)
+ numElements = newPos + 1;
+
+ array[newPos] = newElement;
+}
+
+void Vector::clear()
+{
+ if (ownerOfObjects) {
+ for(int i = 0; i < numElements; i++)
+ if(array[i])
+ delete array[i];
+ }
+
+ numElements = 0;
+}
+
+void Vector::insert(Object *newElement, int pos)
+{
+ if(pos >= numElements)
+ put(newElement, pos);
+ else {
+ numElements++;
+
+ // Allocated memory has to be increased.
+ if(numElements >= numAlloc) {
+ numAlloc *= 2;
+ array = (Object**)realloc(array, numAlloc * sizeof(Object*));
+ }
+
+ for(int i = numElements - 1; i > pos; i--)
+ array[i] = array[i - 1];
+
+ array[pos] = newElement;
+ }
+}
+
+void Vector::remove(int pos)
+{
+ if(ownerOfObjects && array[pos])
+ delete array[pos];
+
+ for(int i = pos + 1; i < numElements; i++)
+ array[i - 1] = array[i];
+
+ numElements--;
+}
+
+/**
+ * Sort the elements in the vector. Assumes that all elements are Comparable's.
+ */
+void Vector::sort()
+{
+ qsort(array, numElements, sizeof(Object*), misc::Comparable::compareFun);
+}
+
+
+/**
+ * \bug Not implemented.
+ */
+Collection0::AbstractIterator* Vector::createIterator()
+{
+ return NULL;
+}
+
+// ------------
+// List
+// ------------
+
+List::List(bool ownerOfObjects)
+{
+ this->ownerOfObjects = ownerOfObjects;
+ first = last = NULL;
+ numElements = 0;
+}
+
+List::~List()
+{
+ clear();
+}
+
+void List::clear()
+{
+ while(first) {
+ if(ownerOfObjects && first->object)
+ delete first->object;
+ Node *next = first->next;
+ delete first;
+ first = next;
+ }
+
+ last = NULL;
+ numElements = 0;
+}
+
+void List::append(Object *element)
+{
+ Node *newLast = new Node;
+ newLast->next = NULL;
+ newLast->object = element;
+
+ if(last) {
+ last->next = newLast;
+ last = newLast;
+ } else
+ first = last = newLast;
+
+ numElements++;
+}
+
+
+bool List::remove0(Object *element, bool compare, bool doNotDeleteAtAll)
+{
+ Node *beforeCur, *cur;
+
+ for(beforeCur = NULL, cur = first; cur; beforeCur = cur, cur = cur->next) {
+ if (compare ?
+ (cur->object && element->equals(cur->object)) :
+ element == cur->object) {
+ if(beforeCur) {
+ beforeCur->next = cur->next;
+ if(cur->next == NULL)
+ last = beforeCur;
+ } else {
+ first = cur->next;
+ if(first == NULL)
+ last = NULL;
+ }
+
+ if(ownerOfObjects && cur->object && !doNotDeleteAtAll)
+ delete cur->object;
+ delete cur;
+
+ numElements--;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Object *List::ListIterator::getNext()
+{
+ Object *object;
+
+ if(current) {
+ object = current->object;
+ current = current->next;
+ } else
+ object = NULL;
+
+ return object;
+}
+
+bool List::ListIterator::hasNext()
+{
+ return current != NULL;
+}
+
+Collection0::AbstractIterator* List::createIterator()
+{
+ return new ListIterator(first);
+}
+
+
+// ---------------
+// HashTable
+// ---------------
+
+HashTable::HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize)
+{
+ this->ownerOfKeys = ownerOfKeys;
+ this->ownerOfValues = ownerOfValues;
+ this->tableSize = tableSize;
+
+ table = new Node*[tableSize];
+ for(int i = 0; i < tableSize; i++)
+ table[i] = NULL;
+}
+
+HashTable::~HashTable()
+{
+ for(int i = 0; i < tableSize; i++) {
+ Node *n1 = table[i];
+ while(n1) {
+ Node *n2 = n1->next;
+
+ if(ownerOfValues && n1->value)
+ delete n1->value;
+ if(ownerOfKeys)
+ delete n1->key;
+ delete n1;
+
+ n1 = n2;
+ }
+ }
+
+ delete[] table;
+}
+
+void HashTable::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append("{ ");
+
+ bool first = true;
+ for(int i = 0; i < tableSize; i++) {
+ for(Node *node = table[i]; node; node = node->next) {
+ if(!first)
+ sb->append(", ");
+ node->key->intoStringBuffer(sb);
+ sb->append(" => ");
+ node->value->intoStringBuffer(sb);
+ first = false;
+ }
+ }
+
+ sb->append(" }");
+}
+
+void HashTable::put(Object *key, Object *value)
+{
+ int h = calcHashValue(key);
+ Node *n = new Node;
+ n->key = key;
+ n->value = value;
+ n->next = table[h];
+ table[h] = n;
+}
+
+bool HashTable::contains(Object *key)
+{
+ int h = calcHashValue(key);
+ for(Node *n = table[h]; n; n = n->next) {
+ if (key->equals(n->key))
+ return true;
+ }
+
+ return false;
+}
+
+Object *HashTable::get(Object *key)
+{
+ int h = calcHashValue(key);
+ for(Node *n = table[h]; n; n = n->next) {
+ if (key->equals(n->key))
+ return n->value;
+ }
+
+ return NULL;
+}
+
+bool HashTable::remove(Object *key)
+{
+ int h = calcHashValue(key);
+ Node *last, *cur;
+
+ for(last = NULL, cur = table[h]; cur; last = cur, cur = cur->next) {
+ if (key->equals(cur->key)) {
+ if(last)
+ last->next = cur->next;
+ else
+ table[h] = cur->next;
+
+ if(ownerOfValues && cur->value)
+ delete cur->value;
+ if(ownerOfKeys)
+ delete cur->key;
+ delete cur;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Object *HashTable::getKey (Object *key)
+{
+ int h = calcHashValue(key);
+ for(Node *n = table[h]; n; n = n->next) {
+ if (key->equals(n->key))
+ return n->key;
+ }
+
+ return NULL;
+}
+
+HashTable::HashTableIterator::HashTableIterator(HashTable *table)
+{
+ this->table = table;
+ node = NULL;
+ pos = -1;
+ gotoNext();
+}
+
+void HashTable::HashTableIterator::gotoNext()
+{
+ if(node)
+ node = node->next;
+
+ while(node == NULL) {
+ if(pos >= table->tableSize - 1)
+ return;
+ pos++;
+ node = table->table[pos];
+ }
+}
+
+
+Object *HashTable::HashTableIterator::getNext()
+{
+ Object *result;
+ if(node)
+ result = node->key;
+ else
+ result = NULL;
+
+ gotoNext();
+ return result;
+}
+
+bool HashTable::HashTableIterator::hasNext()
+{
+ return node != NULL;
+}
+
+Collection0::AbstractIterator* HashTable::createIterator()
+{
+ return new HashTableIterator(this);
+}
+
+// -----------
+// Stack
+// -----------
+
+Stack::Stack (bool ownerOfObjects)
+{
+ this->ownerOfObjects = ownerOfObjects;
+ bottom = top = NULL;
+ numElements = 0;
+}
+
+Stack::~Stack()
+{
+ while (top)
+ pop ();
+}
+
+void Stack::push (object::Object *object)
+{
+ Node *newTop = new Node ();
+ newTop->object = object;
+ newTop->prev = top;
+
+ top = newTop;
+ if(bottom == NULL)
+ bottom = top;
+
+ numElements++;
+}
+
+void Stack::pushUnder (object::Object *object)
+{
+ Node *newBottom = new Node ();
+ newBottom->object = object;
+ newBottom->prev = NULL;
+ if(bottom != NULL)
+ bottom->prev = newBottom;
+
+ bottom = newBottom;
+ if(top == NULL)
+ top = bottom;
+
+ numElements++;
+}
+
+void Stack::pop ()
+{
+ Node *newTop = top->prev;
+
+ if (ownerOfObjects)
+ delete top->object;
+ delete top;
+
+ top = newTop;
+ if(top == NULL)
+ bottom = NULL;
+
+ numElements--;
+}
+
+Object *Stack::StackIterator::getNext()
+{
+ Object *object;
+
+ if(current) {
+ object = current->object;
+ current = current->prev;
+ } else
+ object = NULL;
+
+ return object;
+}
+
+bool Stack::StackIterator::hasNext()
+{
+ return current != NULL;
+}
+
+Collection0::AbstractIterator* Stack::createIterator()
+{
+ return new StackIterator(top);
+}
+
+} // namespace untyped
+
+} // namespace container
+
+} // namespace lout
diff --git a/lout/container.hh b/lout/container.hh
new file mode 100644
index 00000000..26803e23
--- /dev/null
+++ b/lout/container.hh
@@ -0,0 +1,441 @@
+#ifndef __LOUT_CONTAINER_HH_
+#define __LOUT_CONTAINER_HH_
+
+#include "object.hh"
+
+/**
+ * \brief This namespace contains a framework for container classes, which
+ * members are instances of object::Object.
+ *
+ * A common problem in languanges without garbage collection is, where the
+ * children belong to, and so, who is responsible to delete them (instanciation
+ * is always done by the caller). This information is here told to the
+ * collections, each container has a constructor with the parameter
+ * "ownerOfObjects" (HashTable has two such parameters, for keys and values).
+ *
+ * \sa container::untyped, container::typed
+ */
+namespace lout {
+
+namespace container {
+
+/**
+ * \brief The container classes defined here contain instances of
+ * object::Object.
+ *
+ * Different sub-classes may be mixed, and you have to care about casting,
+ * there is no type-safety.
+ */
+namespace untyped {
+
+/**
+ * \brief ...
+ */
+class Collection0: public object::Object
+{
+ friend class Iterator;
+
+protected:
+ /**
+ * \brief The base class for all iterators, as created by
+ * container::untyped::Collection::createIterator.
+ */
+ class AbstractIterator: public object::Object
+ {
+ private:
+ int refcount;
+
+ public:
+ AbstractIterator() { refcount = 1; }
+
+ void ref () { refcount++; }
+ void unref () { refcount--; if (refcount == 0) delete this; }
+
+ virtual bool hasNext () = 0;
+ virtual Object *getNext () = 0;
+ };
+
+protected:
+ virtual AbstractIterator* createIterator() = 0;
+};
+
+/**
+ * \brief This is a small wrapper for AbstractIterator, which may be used
+ * directly, not as a pointer, to makes memory management simpler.
+ */
+class Iterator
+{
+ friend class Collection;
+
+private:
+ Collection0::AbstractIterator *impl;
+
+ // Should not instanciated directly.
+ inline Iterator(Collection0::AbstractIterator *impl) { this->impl = impl; }
+
+public:
+ Iterator();
+ Iterator(const Iterator &it2);
+ Iterator(Iterator &it2);
+ ~Iterator();
+ Iterator &operator=(const Iterator &it2);
+ Iterator &operator=(Iterator &it2);
+
+ inline bool hasNext() { return impl->hasNext(); }
+ inline object::Object *getNext() { return impl->getNext(); }
+};
+
+/**
+ * \brief Abstract base class for all container objects in container::untyped.
+ */
+class Collection: public Collection0
+{
+public:
+ void intoStringBuffer(misc::StringBuffer *sb);
+ inline Iterator iterator() { Iterator it(createIterator()); return it; }
+};
+
+
+/**
+ * \brief Container, which is implemented by an array, which is
+ * dynamically resized.
+ */
+class Vector: public Collection
+{
+private:
+ object::Object **array;
+ int numAlloc, numElements;
+ bool ownerOfObjects;
+
+protected:
+ AbstractIterator* createIterator();
+
+public:
+ Vector(int initSize, bool ownerOfObjects);
+ ~Vector();
+
+ void put(object::Object *newElement, int newPos = -1);
+ void insert(object::Object *newElement, int pos);
+ void remove(int pos);
+ inline object::Object *get(int pos)
+ { return (pos >= 0 && pos < numElements) ? array[pos] : NULL; }
+ inline int size() { return numElements; }
+ void clear();
+ void sort();
+};
+
+
+/**
+ * \brief A single-linked list.
+ */
+class List: public Collection
+{
+ friend class ListIterator;
+
+private:
+ struct Node
+ {
+ public:
+ object::Object *object;
+ Node *next;
+ };
+
+ class ListIterator: public AbstractIterator
+ {
+ private:
+ List::Node *current;
+ public:
+ ListIterator(List::Node *node) { current = node; }
+ bool hasNext();
+ Object *getNext();
+ };
+
+ Node *first, *last;
+ bool ownerOfObjects;
+ int numElements;
+
+ bool remove0(object::Object *element, bool compare, bool doNotDeleteAtAll);
+
+protected:
+ AbstractIterator* createIterator();
+
+public:
+ List(bool ownerOfObjects);
+ ~List();
+
+ void clear();
+ void append(object::Object *element);
+ inline bool removeRef(object::Object *element)
+ { return remove0(element, false, false); }
+ inline bool remove(object::Object *element)
+ { return remove0(element, true, false); }
+ inline bool detachRef(object::Object *element)
+ { return remove0(element, false, true); }
+ inline int size() { return numElements; }
+ inline bool isEmpty() { return numElements == 0; }
+ inline object::Object *getFirst() { return first->object; }
+ inline object::Object *getLast() { return last->object; }
+};
+
+
+/**
+ * \brief A hash table.
+ */
+class HashTable: public Collection
+{
+ friend class HashTableIterator;
+
+private:
+ struct Node
+ {
+ object::Object *key, *value;
+ Node *next;
+ };
+
+ class HashTableIterator: public Collection0::AbstractIterator
+ {
+ private:
+ HashTable *table;
+ HashTable::Node *node;
+ int pos;
+
+ void gotoNext();
+
+ public:
+ HashTableIterator(HashTable *table);
+ bool hasNext();
+ Object *getNext();
+ };
+
+ Node **table;
+ int tableSize;
+ bool ownerOfKeys, ownerOfValues;
+
+private:
+ inline int calcHashValue(object::Object *key)
+ {
+ return abs(key->hashValue()) % tableSize;
+ }
+
+protected:
+ AbstractIterator* createIterator();
+
+public:
+ HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251);
+ ~HashTable();
+
+ void intoStringBuffer(misc::StringBuffer *sb);
+
+ void put (object::Object *key, object::Object *value);
+ bool contains (object::Object *key);
+ Object *get (object::Object *key);
+ bool remove (object::Object *key);
+ Object *getKey (Object *key);
+};
+
+/**
+ * \brief A stack (LIFO).
+ *
+ * Note that the iterator returns all elements in the reversed order they have
+ * been put on the stack.
+ */
+class Stack: public Collection
+{
+ friend class StackIterator;
+
+private:
+ class Node
+ {
+ public:
+ object::Object *object;
+ Node *prev;
+ };
+
+ class StackIterator: public AbstractIterator
+ {
+ private:
+ Stack::Node *current;
+ public:
+ StackIterator(Stack::Node *node) { current = node; }
+ bool hasNext();
+ Object *getNext();
+ };
+
+ Node *bottom, *top;
+ bool ownerOfObjects;
+ int numElements;
+
+protected:
+ AbstractIterator* createIterator();
+
+public:
+ Stack (bool ownerOfObjects);
+ ~Stack();
+
+ void push (object::Object *object);
+ void pushUnder (object::Object *object);
+ inline object::Object *getTop () { return top ? top->object : NULL; }
+ void pop ();
+ inline int size() { return numElements; }
+};
+
+} // namespace untyped
+
+/**
+ * \brief This namespace provides thin wrappers, implemented as C++ templates,
+ * to gain type-safety.
+ *
+ * Notice, that nevertheless, all objects still have to be instances of
+ * object::Object.
+ */
+namespace typed {
+
+template <class T> class Collection;
+
+/**
+ * \brief Typed version of container::untyped::Iterator.
+ */
+template <class T> class Iterator
+{
+ friend class Collection<T>;
+
+private:
+ untyped::Iterator base;
+
+public:
+ inline Iterator() { }
+ inline Iterator(const Iterator<T> &it2) { this->base = it2.base; }
+ inline Iterator(Iterator<T> &it2) { this->base = it2.base; }
+ ~Iterator() { }
+ inline Iterator &operator=(const Iterator<T> &it2)
+ { this->base = it2.base; return *this; }
+ inline Iterator &operator=(Iterator<T> &it2)
+ { this->base = it2.base; return *this; }
+
+ inline bool hasNext() { return this->base.hasNext(); }
+ inline T *getNext() { return (T*)this->base.getNext(); }
+};
+
+/**
+ * \brief Abstract base class template for all container objects in
+ * container::typed.
+ *
+ * Actually, a wrapper for container::untyped::Collection.
+ */
+template <class T> class Collection: public object::Object
+{
+protected:
+ untyped::Collection *base;
+
+public:
+ void intoStringBuffer(misc::StringBuffer *sb)
+ { this->base->intoStringBuffer(sb); }
+
+ inline Iterator<T> iterator() {
+ Iterator<T> it; it.base = this->base->iterator(); return it; }
+};
+
+
+/**
+ * \brief Typed version of container::untyped::Vector.
+ */
+template <class T> class Vector: public Collection <T>
+{
+public:
+ inline Vector(int initSize, bool ownerOfObjects) {
+ this->base = new untyped::Vector(initSize, ownerOfObjects); }
+ ~Vector() { delete this->base; }
+
+ inline void put(T *newElement, int newPos = -1)
+ { ((untyped::Vector*)this->base)->put(newElement, newPos); }
+ inline void insert(T *newElement, int pos)
+ { ((untyped::Vector*)this->base)->insert(newElement, pos); }
+ inline void remove(int pos) { ((untyped::Vector*)this->base)->remove(pos); }
+ inline T *get(int pos)
+ { return (T*)((untyped::Vector*)this->base)->get(pos); }
+ inline int size() { return ((untyped::Vector*)this->base)->size(); }
+ inline void clear() { ((untyped::Vector*)this->base)->clear(); }
+ inline void sort() { ((untyped::Vector*)this->base)->sort(); }
+};
+
+
+/**
+ * \brief Typed version of container::untyped::List.
+ */
+template <class T> class List: public Collection <T>
+{
+public:
+ inline List(bool ownerOfObjects)
+ { this->base = new untyped::List(ownerOfObjects); }
+ ~List() { delete this->base; }
+
+ inline void clear() { ((untyped::List*)this->base)->clear(); }
+ inline void append(T *element)
+ { ((untyped::List*)this->base)->append(element); }
+ inline bool removeRef(T *element) {
+ return ((untyped::List*)this->base)->removeRef(element); }
+ inline bool remove(T *element) {
+ return ((untyped::List*)this->base)->remove(element); }
+ inline bool detachRef(T *element) {
+ return ((untyped::List*)this->base)->detachRef(element); }
+
+ inline int size() { return ((untyped::List*)this->base)->size(); }
+ inline bool isEmpty()
+ { return ((untyped::List*)this->base)->isEmpty(); }
+ inline T *getFirst()
+ { return (T*)((untyped::List*)this->base)->getFirst(); }
+ inline T *getLast()
+ { return (T*)((untyped::List*)this->base)->getLast(); }
+};
+
+
+/**
+ * \brief Typed version of container::untyped::HashTable.
+ */
+template <class K, class V> class HashTable: public Collection <K>
+{
+public:
+ inline HashTable(bool ownerOfKeys, bool ownerOfValues, int tableSize = 251)
+ { this->base = new untyped::HashTable(ownerOfKeys, ownerOfValues,
+ tableSize); }
+ ~HashTable() { delete this->base; }
+
+ inline void put(K *key, V *value)
+ { return ((untyped::HashTable*)this->base)->put(key, value); }
+ inline bool contains(K *key)
+ { return ((untyped::HashTable*)this->base)->contains(key); }
+ inline V *get(K *key)
+ { return (V*)((untyped::HashTable*)this->base)->get(key); }
+ inline bool remove(K *key)
+ { return ((untyped::HashTable*)this->base)->remove(key); }
+ inline K *getKey(K *key)
+ { return (K*)((untyped::HashTable*)this->base)->getKey(key); }
+};
+
+/**
+ * \brief Typed version of container::untyped::Stack.
+ */
+template <class T> class Stack: public Collection <T>
+{
+public:
+ inline Stack (bool ownerOfObjects)
+ { this->base = new untyped::Stack (ownerOfObjects); }
+ ~Stack() { delete this->base; }
+
+ inline void push (T *object) {
+ ((untyped::Stack*)this->base)->push (object); }
+ inline void pushUnder (T *object)
+ { ((untyped::Stack*)this->base)->pushUnder (object); }
+ inline T *getTop ()
+ { return (T*)((untyped::Stack*)this->base)->getTop (); }
+ inline void pop () { ((untyped::Stack*)this->base)->pop (); }
+ inline int size() { return ((untyped::Stack*)this->base)->size(); }
+};
+
+} // namespace untyped
+
+} // namespace container
+
+} // namespace lout
+
+#endif // __LOUT_CONTAINER_HH_
diff --git a/lout/debug.hh b/lout/debug.hh
new file mode 100644
index 00000000..a2c08393
--- /dev/null
+++ b/lout/debug.hh
@@ -0,0 +1,152 @@
+#ifndef __LOUT_DEBUG_H__
+#define __LOUT_DEBUG_H__
+
+/*
+ * Simple debug messages. Add:
+ *
+ * #define DEBUG_LEVEL <n>
+ * #include "debug.h"
+ *
+ * to the file you are working on, or let DEBUG_LEVEL undefined to
+ * disable all messages. A higher level denotes a greater importance
+ * of the message.
+ */
+
+#include <stdio.h>
+
+#define D_STMT_START do
+#define D_STMT_END while (0)
+
+# ifdef DEBUG_LEVEL
+# define DEBUG_MSG(level, ...) \
+ D_STMT_START { \
+ if (DEBUG_LEVEL && (level) >= DEBUG_LEVEL) \
+ printf(__VA_ARGS__); \
+ } D_STMT_END
+# else
+# define DEBUG_MSG(level, ...)
+# endif /* DEBUG_LEVEL */
+
+
+
+/*
+ * Following is experimental, and will be explained soon.
+ */
+
+#ifdef DBG_RTFL
+
+#include <unistd.h>
+#include <stdio.h>
+
+#define DBG_MSG(obj, aspect, prio, msg) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:%s\n", \
+ __FILE__, __LINE__, getpid(), obj, aspect, prio, msg); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_MSGF(obj, aspect, prio, fmt, ...) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:msg:%p:%s:%d:" fmt "\n", \
+ __FILE__, __LINE__, getpid(), obj, aspect, prio, __VA_ARGS__); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_MSG_START(obj) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:msg-start:%p\n", \
+ __FILE__, __LINE__, getpid(), obj); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_MSG_END(obj) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:msg-end:%p\n", \
+ __FILE__, __LINE__, getpid(), obj); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_CREATE(obj, klass) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:obj-create:%p:%s\n", \
+ __FILE__, __LINE__, getpid(), obj, klass); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ASSOC(child, parent) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:obj-assoc:%p:%p\n", \
+ __FILE__, __LINE__, getpid(), child, parent); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_SET_NUM(obj, var, val) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%d\n", \
+ __FILE__, __LINE__, getpid(), obj, var, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_SET_STR(obj, var, val) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%s\n", \
+ __FILE__, __LINE__, getpid(), obj, var, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_SET_PTR(obj, var, val) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:obj-set:%p:%s:%p\n", \
+ __FILE__, __LINE__, getpid(), obj, var, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ARRSET_NUM(obj, var, ind, val) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%d\n", \
+ __FILE__, __LINE__, getpid(), obj, ind, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ARRSET_STR(obj, var, ind, val) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%s\n", \
+ __FILE__, __LINE__, getpid(), obj, ind, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_ARRSET_PTR(obj, var, ind, val) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:obj-set:%p:" var ":%p\n", \
+ __FILE__, __LINE__, getpid(), obj, ind, val); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#define DBG_OBJ_COLOR(klass, color) \
+ D_STMT_START { \
+ printf ("[rtfl]%s:%d:%d:obj-color:%s:%s\n", \
+ __FILE__, __LINE__, getpid(), klass, color); \
+ fflush (stdout); \
+ } D_STMT_END
+
+#else /* DBG_RTFL */
+
+#define DBG_MSG(obj, aspect, prio, msg)
+#define DBG_MSGF(obj, aspect, prio, fmt, ...)
+#define DBG_MSG_START(obj)
+#define DBG_MSG_END(obj)
+#define DBG_OBJ_CREATE(obj, klass)
+#define DBG_OBJ_ASSOC(child, parent)
+#define DBG_OBJ_SET_NUM(obj, var, val)
+#define DBG_OBJ_SET_STR(obj, var, val)
+#define DBG_OBJ_SET_PTR(obj, var, val)
+#define DBG_OBJ_ARRSET_NUM(obj, var, ind, val)
+#define DBG_OBJ_ARRSET_STR(obj, var, ind, val)
+#define DBG_OBJ_ARRSET_PTR(obj, var, ind, val)
+#define DBG_OBJ_COLOR(klass, color)
+
+#endif /* DBG_RTFL */
+
+#endif /* __LOUT_DEBUG_H__ */
+
+
diff --git a/lout/identity.cc b/lout/identity.cc
new file mode 100644
index 00000000..5e4965f1
--- /dev/null
+++ b/lout/identity.cc
@@ -0,0 +1,114 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2005-2007 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+
+#include "identity.hh"
+
+#include <stdio.h>
+
+namespace lout {
+namespace identity {
+
+// ------------------------
+// IdentifiableObject
+// ------------------------
+
+using namespace object;
+using namespace container::typed;
+
+IdentifiableObject::Class::Class (IdentifiableObject::Class *parent, int id,
+ const char *className)
+{
+ this->parent = parent;
+ this->id = id;
+ this->className = className;
+}
+
+HashTable <ConstString, IdentifiableObject::Class>
+ *IdentifiableObject::classesByName =
+ new HashTable<ConstString, IdentifiableObject::Class> (true, true);
+Vector <IdentifiableObject::Class> *IdentifiableObject::classesById =
+ new Vector <IdentifiableObject::Class> (16, false);
+IdentifiableObject::Class *IdentifiableObject::currentlyConstructedClass;
+
+IdentifiableObject::IdentifiableObject ()
+{
+ currentlyConstructedClass = NULL;
+}
+
+void IdentifiableObject::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append("<instance of ");
+ sb->append(getClassName());
+ sb->append(">");
+}
+
+/**
+ * \brief This method must be called in the constructor for the sub class.
+ * See class comment for details.
+ */
+void IdentifiableObject::registerName (const char *className, int *classId)
+{
+ ConstString str (className);
+ Class *klass = classesByName->get (&str);
+ if (klass == NULL) {
+ klass = new Class (currentlyConstructedClass, classesById->size (),
+ className);
+ ConstString *key = new ConstString (className);
+ classesByName->put (key, klass);
+ classesById->put (klass);
+ }
+
+ this->classId = klass->id;
+ currentlyConstructedClass = klass;
+}
+
+/**
+ * \brief Returns, whether this class is an instance of the class, given by
+ * \em otherClassId, or of a sub class of this class.
+ */
+bool IdentifiableObject::instanceOf (int otherClassId)
+{
+ if (otherClassId == -1)
+ // Other class has not been registered yet, while it should have been,
+ // if this class is an instance of it or of a sub-class.
+ return false;
+
+ Class *otherClass = classesById->get (otherClassId);
+
+ if (otherClass == NULL) {
+ fprintf (stderr,
+ "WARNING: Something got wrong here, it seems that a "
+ "CLASS_ID was not initialized properly.\n");
+ return false;
+ }
+
+ for (Class *klass = classesById->get (classId); klass != NULL;
+ klass = klass->parent) {
+ if (klass == otherClass)
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace identity
+} // namespace lout
diff --git a/lout/identity.hh b/lout/identity.hh
new file mode 100644
index 00000000..7dcdbac4
--- /dev/null
+++ b/lout/identity.hh
@@ -0,0 +1,148 @@
+#ifndef __LOUT_OBJECTX_HH__
+#define __LOUT_OBJECTX_HH__
+
+#include "object.hh"
+#include "container.hh"
+#include "signal.hh"
+
+/**
+ * \brief Some stuff to identify classes of objects at run-time.
+ */
+
+namespace lout {
+
+namespace identity {
+
+/**
+ * \brief Instances of classes, which are sub classes of this class, may
+ * be identified at run-time.
+ *
+ * <h3>Testing the class</h3>
+ *
+ * Since e.g. dw::Textblock is a sub class of IdentifiableObject, and
+ * implemented in the correct way (as described below), for any given
+ * IdentifiableObject the following test can be done:
+ *
+ * \code
+ * identity::IdentifiableObject *o;
+ * // ...
+ * bool isATextblock = o->instanceOf(dw::Textblock::CLASS_ID);
+ * \endcode
+ *
+ * \em isATextblock is true, when \em o is an instance of dw::Textblock,
+ * or of a sub class of dw::Textblock. Otherwise, \em isATextblock is false.
+ *
+ * It is also possible to get the class identifier of an
+ * identity::IdentifiableObject, e.g.
+ *
+ * \code
+ * bool isOnlyATextblock = o->getClassId() == dw::Textblock::CLASS_ID;
+ * \endcode
+ *
+ * would result in true, if o is an instance of dw::Textblock, but not an
+ * instance of a sub class of dw::Textblock.
+ *
+ * <h3>Defining Sub Classes</h3>
+ *
+ * Each direct or indirect sub class of IdentifiableObject must
+ *
+ * <ul>
+ * <li> add a static int CLASS_ID with -1 as initial value, and
+ * <li> call registerName (\em name, &CLASS_ID) in the constructor, where
+ * \em name should be unique, e.g. the fully qualified class name.
+ * </ul>
+ *
+ * After this, <i>class</i>::CLASS_ID refers to a number, which denotes the
+ * class. (If this is still -1, since the class has not yet been instanciated,
+ * any test will fail, which is correct.)
+ *
+ * <h3>Notes on implementation</h3>
+ *
+ * If there are some classes like this:
+ *
+ * \dot
+ * digraph G {
+ * node [shape=record, fontname=Helvetica, fontsize=10];
+ * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
+ * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
+ * fontname=Helvetica; fontsize=10;
+ * IdentifiableObject [color="#a0a0a0"];
+ * A;
+ * B [color="#a0a0a0"];
+ * C;
+ * IdentifiableObject -> A;
+ * IdentifiableObject -> B;
+ * B -> C;
+ * }
+ * \enddot
+ *
+ * <center>[\ref uml-legend "legend"]</center>
+ *
+ * and first, an instance of A, and then an instance of C is created, there
+ * will be the following calls of functions and constructors:
+ *
+ * <ol>
+ * <li> %IdentifiableObject ();
+ * <li> %registerName ("A", &A::CLASS_ID);
+ * <li> %IdentifiableObject ();
+ * <li> %registerName ("B", &B::CLASS_ID);
+ * <li> %registerName ("C", &C::CLASS_ID);
+ * </ol>
+ *
+ * From this, the class hierarchy above can easily constructed, and stored
+ * in identity::IdentifiableObject::classesByName and
+ * in identity::IdentifiableObject::classesById. See the code for details.
+ *
+ * N.b. Multiple inheritance is not supported, the construction of the
+ * tree would become confused.
+ */
+class IdentifiableObject: public object::Object
+{
+private:
+ class Class: public object::Object
+ {
+ public:
+ Class *parent;
+ int id;
+ const char *className;
+
+ Class (Class *parent, int id, const char *className);
+ };
+
+ static container::typed::HashTable <object::ConstString,
+ Class> *classesByName;
+ static container::typed::Vector <Class> *classesById;
+ static Class *currentlyConstructedClass;
+
+ int classId;
+
+protected:
+ void registerName (const char *className, int *classId);
+
+public:
+ IdentifiableObject ();
+
+ virtual void intoStringBuffer(misc::StringBuffer *sb);
+
+ /**
+ * \brief Returns the class identifier.
+ *
+ * This is rarely used, rather, tests with
+ * identity::IdentifiableObject::instanceOf are done.
+ */
+ int getClassId () { return classId; }
+
+ /**
+ * \brief Return the name, under which the class of this object was
+ * registered.
+ */
+ const char *getClassName() { return classesById->get(classId)->className; }
+
+ bool instanceOf (int otherClassId);
+};
+
+} // namespace identity
+
+} // namespace lout
+
+#endif // __LOUT_OBJECTX_HH__
diff --git a/lout/misc.cc b/lout/misc.cc
new file mode 100644
index 00000000..2008737b
--- /dev/null
+++ b/lout/misc.cc
@@ -0,0 +1,237 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2005-2007 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+
+#include "misc.hh"
+
+#include <ctype.h>
+#include <config.h>
+
+#define PRGNAME PACKAGE "/" VERSION
+
+namespace lout {
+
+namespace misc {
+
+const char *prgName = PRGNAME;
+
+void init (int argc, char *argv[])
+{
+ prgName = strdup (argv[0]);
+}
+
+void chop (char *s)
+{
+ char *p = s + strlen (s) - 1;
+ while (*p == '\n') {
+ *p = 0;
+ p--;
+ }
+}
+
+char *strip (char *s)
+{
+ while (isspace (*s))
+ s++;
+
+ char *p = s + strlen (s) - 1;
+ while (isspace (*p)) {
+ *p = 0;
+ p--;
+ }
+
+ return s;
+}
+
+// ----------------
+// Comparable
+// ----------------
+
+Comparable::~Comparable()
+{
+}
+
+/**
+ * \brief This static method may be used as compare function for qsort(3), for
+ * an array of Object* (Object*[] or Object**).
+ */
+int Comparable::compareFun(const void *p1, const void *p2)
+{
+ Comparable **c1 = (Comparable**)p1;
+ Comparable **c2 = (Comparable**)p2;
+ if(c1 && c2)
+ return ((*c1)->compareTo(*c2));
+ else if(c1)
+ return 1;
+ else if(c2)
+ return -1;
+ else
+ return 0;
+}
+
+
+// ------------------
+// StringBuffer
+// ------------------
+
+
+StringBuffer::StringBuffer()
+{
+ firstNode = lastNode = NULL;
+ numChars = 0;
+ str = NULL;
+ strValid = false;
+}
+
+StringBuffer::~StringBuffer()
+{
+ clear ();
+ if(str)
+ delete[] str;
+}
+
+/**
+ * \brief Append a NUL-terminated string to the buffer, without copying.
+ *
+ * No copy is made, so this method should only be used in cases, where
+ * the string would otherwise be freed again. (This method may then
+ * save some CPU cycles.)
+ */
+void StringBuffer::appendNoCopy(char *str)
+{
+ Node *node = new Node();
+ node->data = str;
+ node->next = NULL;
+
+ if(firstNode == NULL) {
+ firstNode = node;
+ lastNode = node;
+ } else {
+ lastNode->next = node;
+ lastNode = node;
+ }
+
+ numChars += strlen(str);
+ strValid = false;
+}
+
+/**
+ * \brief Return a NUL-terminated strings containing all appended strings.
+ *
+ * The caller does not have to free the string, this is done in
+ * misc::StringBuffer::~StringBuffer.
+ */
+const char *StringBuffer::getChars()
+{
+ if(strValid)
+ return str;
+
+ if(str)
+ delete[] str;
+ str = new char[numChars + 1];
+ char *p = str;
+
+ for(Node *node = firstNode; node; node = node->next) {
+ int l = strlen(node->data);
+ memcpy(p, node->data, l * sizeof(char));
+ p += l;
+ }
+
+ *p = 0;
+ strValid = true;
+ return str;
+}
+
+/**
+ * \brief Remove all strings appended to the string buffer.
+ */
+void StringBuffer::clear ()
+{
+ Node *node, *nextNode;
+ for(node = firstNode; node; node = nextNode) {
+ nextNode = node->next;
+ delete node->data;
+ delete node;
+ }
+ firstNode = lastNode = NULL;
+ numChars = 0;
+ strValid = false;
+}
+
+
+// ------------
+// BitSet
+// ------------
+
+BitSet::BitSet(int initBits)
+{
+ numBytes = bytesForBits(initBits);
+ bits = (unsigned char*)malloc(numBytes * sizeof(unsigned char));
+ clear();
+}
+
+BitSet::~BitSet()
+{
+ free(bits);
+}
+
+void BitSet::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append("[");
+ for(int i = 0; i < numBytes; i++)
+ sb->append(get(i) ? "1" : "0");
+ sb->append("]");
+}
+
+bool BitSet::get(int i)
+{
+ if(8 * i >= numBytes)
+ return false;
+ else
+ return bits[i / 8] & (1 << (i % 8));
+}
+
+void BitSet::set(int i, bool val)
+{
+ if(8 * i >= numBytes) {
+ int newNumBytes = numBytes;
+ while(8 * i >= newNumBytes)
+ newNumBytes *= 2;
+ bits =
+ (unsigned char*)realloc(bits, newNumBytes * sizeof(unsigned char));
+ memset(bits + numBytes, 0, newNumBytes - numBytes);
+ numBytes = newNumBytes;
+ }
+
+ if(val)
+ bits[i / 8] |= (1 << (i % 8));
+ else
+ bits[i / 8] &= ~(1 << (i % 8));
+}
+
+void BitSet::clear()
+{
+ memset(bits, 0, numBytes);
+}
+
+} // namespace misc
+
+} // namespace lout
diff --git a/lout/misc.hh b/lout/misc.hh
new file mode 100644
index 00000000..f9184bf3
--- /dev/null
+++ b/lout/misc.hh
@@ -0,0 +1,320 @@
+#ifndef __LOUT_MISC_HH__
+#define __LOUT_MISC_HH__
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/**
+ * \brief Miscellaneous stuff, which does not fit anywhere else.
+ *
+ * Actually, the other parts, beginning with ::object, depend on this.
+ */
+namespace lout {
+
+namespace misc {
+
+template <class T> inline T min (T a, T b) { return a < b ? a : b; }
+template <class T> inline T max (T a, T b) { return a > b ? a : b; }
+
+template <class T> inline T min (T a, T b, T c)
+{
+ return (min (a, min (b, c)));
+}
+template <class T> inline T max (T a, T b, T c)
+{
+ return (max (a, max (b, c)));
+}
+
+extern const char *prgName;
+
+void init (int argc, char *argv[]);
+void chop (char *s);
+char *strip (char *s);
+
+
+inline void assertNotReached ()
+{
+ fprintf (stderr, "*** [%s] This should not happen! ***\n", prgName);
+ abort ();
+}
+
+/**
+ * \brief Instances of a sub class of this interface may be compared (less,
+ * greater).
+ *
+ * Used for sorting etc.
+ */
+class Comparable
+{
+public:
+ virtual ~Comparable();
+
+ /**
+ * \brief Compare two objects c1 and c2.
+ *
+ * return a value < 0, when c1 is less than c2, a value > 0, when c1
+ * is greater than c2, or 0, when c1 and c2 are equal.
+ *
+ * If also object::Object is implemented, and if c1.equals(c2),
+ * c1.compareTo(c2) must be 0, but, unlike you may expect,
+ * the reversed is not necessarily true. This method returns 0, if,
+ * according to the rules for sorting, there is no difference, but there
+ * may still be differences (not relevant for sorting), which "equals" will
+ * care about.
+ */
+ virtual int compareTo(Comparable *other) = 0;
+
+ static int compareFun(const void *p1, const void *p2);
+};
+
+/**
+ * \brief Simple (simpler than container::untyped::Vector and
+ * container::typed::Vector) template based vector.
+ */
+template <class T> class SimpleVector
+{
+private:
+ enum {
+ /**
+ * \brief Edit this for debugging. Should be optimized by the compiler.
+ */
+ BOUND_CHECKING = 1
+ };
+
+ T *array;
+ int num, numAlloc;
+
+ inline void resize ()
+ {
+ /* This algorithm was tunned for memory&speed with this huge page:
+ * http://downloads.mysql.com/docs/refman-6.0-en.html.tar.gz
+ */
+ if (array == NULL) {
+ this->numAlloc = 1;
+ this->array = (T*) malloc (sizeof (T));
+ }
+ if (this->numAlloc < this->num) {
+ this->numAlloc = (this->num < 100) ?
+ this->num : this->num + this->num/10;
+ this->array =
+ (T*) realloc(this->array, (this->numAlloc * sizeof (T)));
+ }
+ }
+
+public:
+ inline SimpleVector (int initAlloc)
+ {
+ this->num = 0;
+ this->numAlloc = initAlloc;
+ this->array = NULL;
+ }
+
+ inline ~SimpleVector ()
+ {
+ if (this->array)
+ free (this->array);
+ }
+
+ /**
+ * \brief Return the number of elements put into this vector.
+ */
+ inline int size() { return this->num; }
+
+ inline T* getArray() { return array; }
+
+ /**
+ * \brief Increase the vector size by one.
+ *
+ * May be necessary before calling misc::SimpleVector::set.
+ */
+ inline void increase() { this->num++; this->resize (); }
+
+ /**
+ * \brief Set the size explicitely.
+ *
+ * May be necessary before called before misc::SimpleVector::set.
+ */
+ inline void setSize(int newSize) { this->num = newSize; this->resize (); }
+
+ /**
+ * \brief Set the size explicitely and initialize new values.
+ *
+ * May be necessary before called before misc::SimpleVector::set.
+ */
+ inline void setSize (int newSize, T t) {
+ int oldSize = this->num;
+ setSize (newSize);
+ for (int i = oldSize; i < newSize; i++)
+ set (i, t);
+ }
+
+ /**
+ * \brief Return the reference of one element.
+ *
+ * \sa misc::SimpleVector::get
+ */
+ inline T* getRef (int i) {
+ if (BOUND_CHECKING)
+ assert (i >= 0 && i < this->num);
+ return array + i;
+ }
+
+ /**
+ * \brief Return the one element, explicitety.
+ *
+ * The element is copied, so for complex elements, you should rather used
+ * misc::SimpleVector::getRef.
+ */
+ inline T get (int i) {
+ if (BOUND_CHECKING)
+ assert (i >= 0 && i < this->num);
+ return this->array[i];
+ }
+
+ /**
+ * \brief Store an object in the vector.
+ *
+ * Unlike in container::untyped::Vector and container::typed::Vector,
+ * you have to care about the size, so a call to
+ * misc::SimpleVector::increase or misc::SimpleVector::setSize may
+ * be necessary before.
+ */
+ inline void set (int i, T t) {
+ if (BOUND_CHECKING)
+ assert (i >= 0 && i < this->num);
+ this->array[i] = t;
+ }
+};
+
+
+/**
+ * \brief A class for fast concatenation of a large number of strings.
+ */
+class StringBuffer
+{
+private:
+ struct Node
+ {
+ char *data;
+ Node *next;
+ };
+
+ Node *firstNode, *lastNode;
+ int numChars;
+ char *str;
+ bool strValid;
+
+public:
+ StringBuffer();
+ ~StringBuffer();
+
+ /**
+ * \brief Append a NUL-terminated string to the buffer, with copying.
+ *
+ * A copy is kept in the buffer, so the caller does not have to care
+ * about memory management.
+ */
+ inline void append(const char *str) { appendNoCopy(strdup(str)); }
+ void appendNoCopy(char *str);
+ const char *getChars();
+ void clear ();
+};
+
+
+/**
+ * \brief A bit set, which automatically reallocates when needed.
+ */
+class BitSet
+{
+private:
+ unsigned char *bits;
+ int numBytes;
+
+ inline int bytesForBits(int bits) { return bits == 0 ? 1 : (bits + 7) / 8; }
+
+public:
+ BitSet(int initBits);
+ ~BitSet();
+ void intoStringBuffer(misc::StringBuffer *sb);
+ bool get(int i);
+ void set(int i, bool val);
+ void clear();
+};
+
+/**
+ * \brief A simple allocator optimized to handle many small chunks of memory.
+ * The chunks can not be free'd individually. Instead the whole zone must be
+ * free'd with zoneFree().
+ */
+class ZoneAllocator
+{
+private:
+ size_t poolSize, poolLimit, freeIdx;
+ SimpleVector <char*> *pools;
+ SimpleVector <char*> *bulk;
+
+public:
+ ZoneAllocator (size_t poolSize) {
+ this->poolSize = poolSize;
+ this->poolLimit = poolSize / 4;
+ this->freeIdx = poolSize;
+ this->pools = new SimpleVector <char*> (1);
+ this->bulk = new SimpleVector <char*> (1);
+ };
+
+ ~ZoneAllocator () {
+ zoneFree ();
+ delete pools;
+ delete bulk;
+ }
+
+ inline void * zoneAlloc (size_t t) {
+ void *ret;
+
+ if (t > poolLimit) {
+ bulk->increase ();
+ bulk->set (bulk->size () - 1, (char*) malloc (t));
+ return bulk->get (bulk->size () - 1);
+ }
+
+ if (t > poolSize - freeIdx) {
+ pools->increase ();
+ pools->set (pools->size () - 1, (char*) malloc (poolSize));
+ freeIdx = 0;
+ }
+
+ ret = pools->get (pools->size () - 1) + freeIdx;
+ freeIdx += t;
+ return ret;
+ }
+
+ inline void zoneFree () {
+ for (int i = 0; i < pools->size (); i++)
+ free (pools->get (i));
+ pools->setSize (0);
+ for (int i = 0; i < bulk->size (); i++)
+ free (bulk->get (i));
+ bulk->setSize (0);
+ freeIdx = poolSize;
+ }
+
+ inline const char *strndup (const char *str, size_t t) {
+ char *new_str = (char *) zoneAlloc (t + 1);
+ memcpy (new_str, str, t);
+ new_str[t] = '\0';
+ return new_str;
+ }
+
+ inline const char *strdup (const char *str) {
+ return strndup (str, strlen (str));
+ }
+};
+
+} // namespace misc
+
+} // namespace lout
+
+#endif // __LOUT_MISC_HH__
diff --git a/lout/object.cc b/lout/object.cc
new file mode 100644
index 00000000..7da124fa
--- /dev/null
+++ b/lout/object.cc
@@ -0,0 +1,311 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2005-2007 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+
+#include "object.hh"
+#include <stdio.h>
+
+namespace lout {
+
+namespace object {
+
+// ------------
+// Object
+// ------------
+
+/**
+ * \brief The destructor is defined as virtual (but not abstract), so that
+ * destruction of Object's works properly.
+ */
+Object::~Object()
+{
+}
+
+/**
+ * \brief Returns, whether two objects are equal.
+ *
+ * The caller should ensure, that this and the object have the same class;
+ * this makes casting of "other" safe. Typically, an implementation should
+ * check this == other first, the caller can assume a fast implementation.
+ */
+bool Object::equals(Object *other)
+{
+ misc::assertNotReached ();
+ return false;
+}
+
+/**
+ * \brief Return a hash value for the object.
+ */
+int Object::hashValue()
+{
+ fprintf (stderr, "Object::hashValue() should be implemented.\n");
+ return 0;
+}
+
+/**
+ * \brief Return an exact copy of the object.
+ */
+Object *Object::clone()
+{
+ misc::assertNotReached ();
+ return NULL;
+}
+
+/**
+ * \brief Use object::Object::intoStringBuffer to return a textual
+ * representation of the object.
+ *
+ * The caller does not have to free the memory, object::Object is responsible
+ * for this.
+ */
+const char *Object::toString()
+{
+ /** \todo garbage! */
+ misc::StringBuffer sb;
+ intoStringBuffer(&sb);
+ char *s = strdup(sb.getChars());
+ return s;
+}
+
+/**
+ * \brief Store a textual representation of the object in a misc::StringBuffer.
+ *
+ * This is used by object::Object::toString.
+ */
+void Object::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append("<not further specified object>");
+}
+
+/**
+ * \brief Return the number of bytes, this object totally uses.
+ */
+size_t Object::sizeOf()
+{
+ fprintf (stderr, "Object::sizeOf() should be implemented.\n");
+ return sizeof(Object*);
+}
+
+// -------------
+// Pointer
+// -------------
+
+bool Pointer::equals(Object *other)
+{
+ return value == ((Pointer*)other)->value;
+}
+
+int Pointer::hashValue()
+{
+/* For some unknown reason, this doesn't compile on some 64bit platforms:
+ *
+ * if (sizeof (int) == sizeof (void*))
+ * return (int)value;
+ * else
+ * return ((int*)value)[0] ^ ((int*)value)[1];
+ */
+#if SIZEOF_VOID_P == 4
+ return (int)value;
+#else
+ return ((int*)value)[0] ^ ((int*)value)[1];
+#endif
+}
+
+void Pointer::intoStringBuffer(misc::StringBuffer *sb)
+{
+ char buf[64];
+ snprintf(buf, sizeof(buf), "0x%p", value);
+ sb->append(buf);
+}
+
+// -------------
+// Integer
+// -------------
+
+bool Integer::equals(Object *other)
+{
+ return value == ((Integer*)other)->value;
+}
+
+int Integer::hashValue()
+{
+ return (int)value;
+}
+
+void Integer::intoStringBuffer(misc::StringBuffer *sb)
+{
+ char buf[64];
+ sprintf(buf, "%d", value);
+ sb->append(buf);
+}
+
+int Integer::compareTo(Comparable *other)
+{
+ return value - ((Integer*)other)->value;
+}
+
+// -----------------
+// ConstString
+// -----------------
+
+bool ConstString::equals(Object *other)
+{
+ ConstString *otherString = (ConstString*)other;
+ return
+ this == other ||
+ (str == NULL && otherString->str == NULL) ||
+ (str != NULL && otherString->str != NULL &&
+ strcmp(str, otherString->str) == 0);
+}
+
+int ConstString::hashValue()
+{
+ return hashValue(str);
+}
+
+
+int ConstString::compareTo(Comparable *other)
+{
+ String *otherString = (String*)other;
+ if(str && otherString->str)
+ return strcmp(str, otherString->str);
+ else if(str)
+ return 1;
+ else if(otherString->str)
+ return -1;
+ else
+ return 0;
+}
+
+
+int ConstString::hashValue(const char *str)
+{
+ if(str) {
+ int h = 0;
+ for (int i = 0; str[i]; i++)
+ h = (h * 256 + str[i]);
+ return h;
+ } else
+ return 0;
+}
+
+void ConstString::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append(str);
+}
+
+// ------------
+// String
+// ------------
+
+String::String (const char *str): ConstString (str ? strdup(str) : NULL)
+{
+}
+
+String::~String ()
+{
+ if(str)
+ delete str;
+}
+
+// ------------
+// Pair
+// ------------
+
+PairBase::PairBase(Object *first, Object *second)
+{
+ this->first = first;
+ this->second = second;
+}
+
+PairBase::~PairBase()
+{
+ if(first)
+ delete first;
+ if(second)
+ delete second;
+}
+
+bool PairBase::equals(Object *other)
+{
+ PairBase *otherPair = (PairBase*)other;
+
+ return
+ // Identical?
+ this == other ||
+ (// Both first parts are NULL, ...
+ (first == NULL && otherPair->first == NULL) ||
+ // ... or both first parts are not NULL and equal
+ (first != NULL && otherPair->first != NULL
+ && first->equals (otherPair->first))) &&
+ // Same with second part.
+ ((second == NULL && otherPair->second == NULL) ||
+ (second != NULL && otherPair->second != NULL
+ && second->equals (otherPair->second)));
+}
+
+int PairBase::hashValue()
+{
+ int value = 0;
+
+ if(first)
+ value ^= first->hashValue();
+ if(second)
+ value ^= second->hashValue();
+
+ return value;
+}
+
+void PairBase::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append("<pair: ");
+
+ if(first)
+ first->intoStringBuffer(sb);
+ else
+ sb->append("(nil)");
+
+ sb->append(",");
+
+ if(second)
+ second->intoStringBuffer(sb);
+ else
+ sb->append("(nil)");
+
+ sb->append(">");
+}
+
+size_t PairBase::sizeOf()
+{
+ size_t size = 0;
+
+ if(first)
+ size += first->sizeOf();
+ if(second)
+ size += second->sizeOf();
+
+ return size;
+}
+
+} // namespace object
+
+} // namespace lout
diff --git a/lout/object.hh b/lout/object.hh
new file mode 100644
index 00000000..7d505c99
--- /dev/null
+++ b/lout/object.hh
@@ -0,0 +1,161 @@
+#ifndef __LOUT_OBJECT_HH__
+#define __LOUT_OBJECT_HH__
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "misc.hh"
+
+/**
+ * \brief Here, some common classes (or interfaces) are defined, to standardize
+ * the access to other classes.
+ */
+namespace lout {
+
+namespace object {
+
+/**
+ * \brief This is the base class for many other classes, which defines very
+ * common virtual methods.
+ *
+ * For convenience, none of them are abstract, but they
+ * must be defined, when they are needed, especially for containers.
+ */
+class Object
+{
+public:
+ virtual ~Object();
+ virtual bool equals(Object *other);
+ virtual int hashValue();
+ virtual Object *clone();
+ virtual void intoStringBuffer(misc::StringBuffer *sb);
+ const char *toString();
+ virtual size_t sizeOf();
+};
+
+/**
+ * \brief An object::Object wrapper for void pointers.
+ */
+class Pointer: public Object
+{
+private:
+ void *value;
+
+public:
+ Pointer(void *value) { this->value = value; }
+ bool equals(Object *other);
+ int hashValue();
+ void intoStringBuffer(misc::StringBuffer *sb);
+ inline void *getValue() { return value; }
+};
+
+/**
+ * \brief A typed version of object::Pointer.
+ */
+template <class T> class TypedPointer: public Pointer
+{
+public:
+ inline TypedPointer(T *value) : Pointer ((void*)value) { }
+ inline T *getTypedValue() { return (T*)getValue(); }
+};
+
+
+/**
+ * \brief An object::Object wrapper for int's.
+ */
+class Integer: public Object, misc::Comparable
+{
+ int value;
+
+public:
+ Integer(int value) { this->value = value; }
+ bool equals(Object *other);
+ int hashValue();
+ void intoStringBuffer(misc::StringBuffer *sb);
+ int compareTo(Comparable *other);
+ inline int getValue() { return value; }
+};
+
+
+/**
+ * \brief An object::Object wrapper for constant strings (char*).
+ *
+ * As opposed to object::String, the char array is not copied.
+ */
+class ConstString: public Object, misc::Comparable
+{
+protected:
+ const char *str;
+
+public:
+ ConstString(const char *str) { this->str = str; }
+ bool equals(Object *other);
+ int hashValue();
+ int compareTo(Comparable *other);
+ void intoStringBuffer(misc::StringBuffer *sb);
+
+ inline const char *chars() { return str; }
+
+ static int hashValue(const char *str);
+};
+
+
+/**
+ * \brief An object::Object wrapper for strings (char*).
+ *
+ * As opposed to object::ConstantString, the char array is copied.
+ */
+class String: public ConstString
+{
+public:
+ String(const char *str);
+ ~String();
+};
+
+/**
+ * \todo Comment
+ */
+class PairBase: public Object
+{
+protected:
+ Object *first, *second;
+
+public:
+ PairBase(Object *first, Object *second);
+ ~PairBase();
+
+ bool equals(Object *other);
+ int hashValue();
+ void intoStringBuffer(misc::StringBuffer *sb);
+ size_t sizeOf();
+};
+
+/**
+ * \todo Comment
+ */
+class Pair: public PairBase
+{
+public:
+ Pair(Object *first, Object *second): PairBase (first, second) { }
+
+ inline Object *getFirst () { return first; }
+ inline Object *getSecond () { return second; }
+};
+
+/**
+ * \todo Comment
+ */
+template <class F, class S> class TypedPair: public PairBase
+{
+public:
+ TypedPair(F *first, S *second): PairBase (first, second) { }
+
+ inline F *getFirst () { return first; }
+ inline S *getSecond () { return second; }
+};
+
+} // namespace object
+
+} // namespace lout
+
+#endif // __LOUT_OBJECT_HH__
diff --git a/lout/signal.cc b/lout/signal.cc
new file mode 100644
index 00000000..46aae626
--- /dev/null
+++ b/lout/signal.cc
@@ -0,0 +1,171 @@
+/*
+ * Dillo Widget
+ *
+ * Copyright 2005-2007 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+
+#include "signal.hh"
+
+namespace lout {
+namespace signal {
+
+using namespace container::typed;
+
+// ------------
+// Emitter
+// ------------
+
+Emitter::Emitter ()
+{
+ receivers = new List <Receiver> (false);
+}
+
+Emitter::~Emitter ()
+{
+ for(Iterator<Receiver> it = receivers->iterator (); it.hasNext (); ) {
+ Receiver *receiver = it.getNext ();
+ receiver->unconnectFrom (this);
+ }
+ delete receivers;
+}
+
+void Emitter::intoStringBuffer(misc::StringBuffer *sb)
+{
+ sb->append ("<emitter: ");
+ receivers->intoStringBuffer (sb);
+ sb->append (">");
+}
+
+void Emitter::unconnect (Receiver *receiver)
+{
+ receivers->removeRef (receiver);
+}
+
+/**
+ * \brief Connect a receiver to the emitter.
+ *
+ * This is protected, a sub class should define a wrapper, with the respective
+ * receiver as an argument, to gain type safety.
+ */
+void Emitter::connect (Receiver *receiver)
+{
+ receivers->append (receiver);
+ receiver->connectTo (this);
+}
+
+/**
+ * \brief Emit a void signal.
+ *
+ * This method should be called by a wrapper (return value void), which
+ * \em folds the signal, and delegates the emission to here.
+ */
+void Emitter::emitVoid (int signalNo, int argc, Object **argv)
+{
+ for(Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) {
+ Receiver *receiver = it.getNext();
+ emitToReceiver (receiver, signalNo, argc, argv);
+ }
+}
+
+/**
+ * \brief Emit a boolean signal.
+ *
+ * This method should be called by a wrapper, which \em folds the signal,
+ * delegates the emission to here, and returns the same boolean value.
+ */
+bool Emitter::emitBool (int signalNo, int argc, Object **argv)
+{
+ bool b = false, bt;
+
+ for(Iterator <Receiver> it = receivers->iterator (); it.hasNext (); ) {
+ Receiver *receiver = it.getNext();
+ // Note: All receivers are called, even if one returns true.
+ // Therefore, something like
+ // b = b || emitToReceiver (receiver, signalNo, argc, argv);
+ // does not work.
+ bt = emitToReceiver (receiver, signalNo, argc, argv);
+ b = b || bt;
+ }
+
+ return b;
+}
+
+
+// --------------
+// Receiver
+// --------------
+
+Receiver::Receiver()
+{
+ emitters = new List <Emitter> (false);
+}
+
+Receiver::~Receiver()
+{
+ for(Iterator<Emitter> it = emitters->iterator(); it.hasNext(); ) {
+ Emitter *emitter = it.getNext();
+ emitter->unconnect (this);
+ }
+ delete emitters;
+}
+
+void Receiver::intoStringBuffer(misc::StringBuffer *sb)
+{
+ // emitters are not listed, to prevent recursion
+ sb->append ("<receiver>");
+}
+
+void Receiver::connectTo(Emitter *emitter)
+{
+ emitters->append (emitter);
+}
+
+void Receiver::unconnectFrom(Emitter *emitter)
+{
+ emitters->removeRef (emitter);
+}
+
+// ------------------------
+// ObservedObject
+// ------------------------
+
+bool ObservedObject::DeletionEmitter::emitToReceiver (Receiver *receiver,
+ int signalNo,
+ int argc, Object **argv)
+{
+ object::TypedPointer <ObservedObject> *p =
+ (object::TypedPointer<ObservedObject>*)argv[0];
+ ((DeletionReceiver*)receiver)->deleted (p->getTypedValue ());
+ return false;
+}
+
+void ObservedObject::DeletionEmitter::emitDeletion (ObservedObject *obj)
+{
+ object::TypedPointer <ObservedObject> p(obj);
+ object::Object *argv[1] = { &p };
+ emitVoid (0, 1, argv);
+}
+
+ObservedObject::~ObservedObject()
+{
+ deletionEmitter.emitDeletion (this);
+}
+
+} // namespace signal
+} // namespace lout
diff --git a/lout/signal.hh b/lout/signal.hh
new file mode 100644
index 00000000..c96247be
--- /dev/null
+++ b/lout/signal.hh
@@ -0,0 +1,310 @@
+#ifndef __LOUT_SIGNALS_HH__
+#define __LOUT_SIGNALS_HH__
+
+#include "object.hh"
+#include "container.hh"
+
+/**
+ * \brief This namespace provides base classes to define signals.
+ *
+ * By using signals, objects may be connected at run-time, e.g. a general
+ * button widget may be connected to another application-specific object,
+ * which reacts on the operations on the button by the user. In this case,
+ * the button e.g. defines a signal "clicked", which is "emitted" each
+ * time the user clicks on the button. After the application-specific
+ * object has been connected to this signal, a specific method of it will
+ * be called each time, this button emits the "clicked" signal.
+ *
+ * Below, we will call the level, on which signals are defined, the
+ * "general level", and the level, on which the signals are connected,
+ * the "caller level".
+ *
+ * <h3>Defining Signals</h3>
+ *
+ * Typically, signals are grouped. To define a signal group \em bar for your
+ * class \em Foo, you have to define two classes, the emitter and the
+ * receiver (BarEmitter and BarReceiver), and instanciate the emitter:
+ *
+ * \dot
+ * digraph G {
+ * node [shape=record, fontname=Helvetica, fontsize=10];
+ * edge [arrowhead="none", arrowtail="empty", labelfontname=Helvetica,
+ * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
+ * fontname=Helvetica; fontsize=10;
+ *
+ * subgraph cluster_signal {
+ * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
+ * label="signal";
+ *
+ * Emitter [color="#a0a0a0", URL="\ref signal::Emitter"];
+ * Receiver [color="#a0a0a0", URL="\ref signal::Receiver"];
+ * }
+ *
+ * subgraph cluster_foo {
+ * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
+ * label="General (foo)";
+ *
+ * Foo;
+ * BarEmitter;
+ * BarReceiver [color="#a0a0a0"];
+ * }
+ *
+ * Emitter -> BarEmitter;
+ * Receiver -> BarReceiver;
+ * Foo -> BarEmitter [arrowhead="open", arrowtail="none",
+ * headlabel="1", taillabel="1"];
+ * }
+ * \enddot
+ *
+ * <center>[\ref uml-legend "legend"]</center>
+ *
+ * BarEmitter (class and instance) may be kept private, but BarReceiver must
+ * be public, since the caller of Foo must create a sub class of it. For
+ * BarEmitter, several methods must be implemented, see signal::Emitter for
+ * details. In BarReceiver, only some virtual abstract methods are defined,
+ * which the caller must implement. In this case, it is recommended to define
+ * a connectBar(BarReceiver*) method in Foo, which is delegated to the
+ * BarEmitter.
+ *
+ * <h3>Connecting to Signals</h3>
+ *
+ * A caller, which wants to connect to a signal, must define a sub class of
+ * the receiver, and implement the virtual methods. A typical design looks
+ * like this:
+ *
+ * \dot
+ * digraph G {
+ * node [shape=record, fontname=Helvetica, fontsize=10];
+ * edge [arrowhead="open", arrowtail="none", labelfontname=Helvetica,
+ * labelfontsize=10, color="#404040", labelfontcolor="#000080"];
+ * fontname=Helvetica; fontsize=10;
+ *
+ * subgraph cluster_foo {
+ * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
+ * label="Generall (foo)";
+ *
+ * BarReceiver [color="#a0a0a0"];
+ * }
+ *
+ * subgraph cluster_qix {
+ * style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
+ * label="Caller (qix)";
+ *
+ * Qix;
+ * QixBarReceiver;
+ * }
+ *
+ * BarReceiver -> QixBarReceiver [arrowhead="none", arrowtail="empty"];
+ * QixBarReceiver -> Qix [headlabel="1", taillabel="*"];
+ * }
+ * \enddot
+ *
+ * <center>[\ref uml-legend "legend"]</center>
+ *
+ * (We skip "baz" in the canon, for better readability.)
+ *
+ * Here, the QixBarReceiver is connected to the Qix, so that the signals can
+ * be delegated to the Qix. Notice that the receiver gets automatically
+ * disconnected, when deleted (see signal::Receiver::~Receiver).
+ *
+ * <h3>Void and Boolean Signals</h3>
+ *
+ * In the simplest case, signal emitting involves calling a list of
+ * signal receivers (void signals). For boolean signals, the receivers return
+ * a boolean value, and the result of the signal emission (the return value of
+ * signal::Emitter::emitBool) returns the disjunction of the values returned
+ * by the receivers. Typically, a receiver states with its return value,
+ * whether the signal was used in any way, the resulting return value so
+ * indicates, whether at least one receiver has used the signal.
+ *
+ * In Dw, events are processed this way. In the simplest case, they are
+ * delegated to the parent widget, if the widget does not process them (by
+ * returning false). As an addition, signals are emitted, and if a receiver
+ * processes the event, this is handled the same way, as if the widget itself
+ * would have processed it.
+ *
+ * Notice, that also for boolean signals, all receivers are called, even
+ * after one receiver has already returned true.
+ *
+ * <h3>Memory Management</h3>
+ *
+ * <h4>Emitters</h4>
+ *
+ * Emitters are typically instanciated one, for one object emitting the
+ * signals. In the example above, the class Foo will contain a field
+ * "BarEmitter barEmitter" (not as a pointer, "BarEmitter *barEmitter").
+ *
+ * <h4>Receivers</h4>
+ *
+ * It is important, that a emitter never deletes a receiver, it just removes
+ * them from the receivers list. Likewise, when a receiver is deleted, it
+ * unconnects itself from all emitters. (The same receiver instance can indeed
+ * be connected to multiple emitters.) So, the caller has to care about
+ * deleting receivers.
+ *
+ * In the example above, something like that will work:
+ *
+ * \code
+ * class Qix
+ * {
+ * private:
+ * class QixBarReceiver
+ * {
+ * public:
+ * Qix *qix;
+ * // ...
+ * };
+ *
+ * QixBarReceiver barReceiver;
+ *
+ * // ...
+ * };
+ * \endcode
+ *
+ * The constructor of Qix should then set \em qix:
+ *
+ * \code
+ * Qix::Qix ()
+ * {
+ * barReceiver.qix = this.
+ * // ...
+ * }
+ * \endcode
+ *
+ * After this, &\em barReceiver can be connected to all instances of
+ * BarEmitter, also multiple times.
+ */
+namespace lout {
+
+namespace signal {
+
+class Receiver;
+
+/**
+ * \brief The base class for signal emitters.
+ *
+ * If defining a signal group, a sub class of this class must be defined,
+ * with
+ *
+ * <ul>
+ * <li> a definition of the different signals (as enumeration),
+ * <li> an implementation of signal::Emitter::emitToReceiver,
+ * <li> wrappers for signal::Emitter::emitVoid and signal::Emitter::emitBool,
+ * respectively (one for each signal), and
+ * <li> a wrapper for signal::Emitter::connect.
+ * </ul>
+ *
+ * There are two representations of signals:
+ *
+ * <ul>
+ * <li> In the \em unfolded representation, the signal itself is represented
+ * by the method itself (in the emitter or the receiver), and the
+ * arguments are represented as normal C++ types.
+ *
+ * <li> \em Folding signals means to represent the signal itself by an integer
+ * number (enumeration), and translate the arguments in an object::Object*
+ * array. (If a given argument is not an instance of object::Object*,
+ * the wrappers in ::object can be used.)
+ * </ul>
+ *
+ * \sa ::signal
+ */
+class Emitter: public object::Object
+{
+ friend class Receiver;
+
+private:
+ container::typed::List <Receiver> *receivers;
+
+ void unconnect (Receiver *receiver);
+
+protected:
+ void emitVoid (int signalNo, int argc, Object **argv);
+ bool emitBool (int signalNo, int argc, Object **argv);
+ void connect(Receiver *receiver);
+
+ /**
+ * \brief A sub class must implement this for a call to a single
+ * receiver.
+ *
+ * This methods gets the signal in a \em folded representation, it has
+ * to unfold it, and pass it to a single receiver. For boolean signals,
+ * the return value of the receiver must be returned, for void signals,
+ * the return value is discarded.
+ */
+ virtual bool emitToReceiver (Receiver *receiver, int signalNo,
+ int argc, Object **argv) = 0;
+
+public:
+ Emitter();
+ ~Emitter();
+
+ void intoStringBuffer(misc::StringBuffer *sb);
+};
+
+/**
+ * \brief The base class for signal receiver base classes.
+ *
+ * If defining a signal group, a sub class of this class must be defined,
+ * in which only the abstract signal methods must be defined.
+ *
+ * \sa ::signal
+ */
+class Receiver: public object::Object
+{
+ friend class Emitter;
+
+private:
+ container::typed::List<Emitter> *emitters;
+
+ void connectTo(Emitter *emitter);
+ void unconnectFrom(Emitter *emitter);
+
+public:
+ Receiver();
+ ~Receiver();
+
+ void intoStringBuffer(misc::StringBuffer *sb);
+};
+
+/**
+ * \brief An observed object has a signal emitter, which tells the
+ * receivers, when the object is deleted.
+ */
+class ObservedObject
+{
+public:
+ class DeletionReceiver: public signal::Receiver
+ {
+ public:
+ virtual void deleted (ObservedObject *object) = 0;
+ };
+
+private:
+ class DeletionEmitter: public signal::Emitter
+ {
+ protected:
+ bool emitToReceiver (signal::Receiver *receiver, int signalNo,
+ int argc, Object **argv);
+
+ public:
+ inline void connectDeletion (DeletionReceiver *receiver)
+ { connect (receiver); }
+
+ void emitDeletion (ObservedObject *obj);
+ };
+
+ DeletionEmitter deletionEmitter;
+
+public:
+ virtual ~ObservedObject();
+
+ inline void connectDeletion (DeletionReceiver *receiver)
+ { deletionEmitter.connectDeletion (receiver); }
+};
+
+} // namespace signal
+
+} // namespace lout
+
+#endif // __LOUT_SIGNALS_HH__