summaryrefslogtreecommitdiffstats
path: root/src/object-hierarchy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/object-hierarchy.cpp')
-rw-r--r--src/object-hierarchy.cpp213
1 files changed, 213 insertions, 0 deletions
diff --git a/src/object-hierarchy.cpp b/src/object-hierarchy.cpp
new file mode 100644
index 000000000..30f13e49a
--- /dev/null
+++ b/src/object-hierarchy.cpp
@@ -0,0 +1,213 @@
+/** \file
+ * Object hierarchy implementation.
+ *
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ *
+ * Copyright (C) 2004 MenTaLguY
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "sp-object.h"
+#include "object-hierarchy.h"
+
+namespace Inkscape {
+
+/**
+ * Create new object hierarchy.
+ * \param top The first entry if non-NULL.
+ */
+ObjectHierarchy::ObjectHierarchy(SPObject *top) {
+ if (top) {
+ _addBottom(top);
+ }
+}
+
+ObjectHierarchy::~ObjectHierarchy() {
+ _clear();
+}
+
+/**
+ * Remove all entries.
+ */
+void ObjectHierarchy::clear() {
+ _clear();
+ _changed_signal.emit(NULL, NULL);
+}
+
+/**
+ * Trim or expand hierarchy on top such that object becomes top entry.
+ */
+void ObjectHierarchy::setTop(SPObject *object) {
+ g_return_if_fail(object != NULL);
+
+ if ( top() == object ) {
+ return;
+ }
+
+ if (!top()) {
+ _addTop(object);
+ } else if (object->isAncestorOf(top())) {
+ _addTop(object, top());
+ } else if ( object == bottom() || object->isAncestorOf(bottom()) ) {
+ _trimAbove(object);
+ } else {
+ _clear();
+ _addTop(object);
+ }
+
+ _changed_signal.emit(top(), bottom());
+}
+
+/**
+ * Add hierarchy from junior's parent to senior to this
+ * hierarchy's top.
+ */
+void ObjectHierarchy::_addTop(SPObject *senior, SPObject *junior) {
+ g_assert(junior != NULL);
+ g_assert(senior != NULL);
+
+ SPObject *object=SP_OBJECT_PARENT(junior);
+ do {
+ _addTop(object);
+ object = SP_OBJECT_PARENT(object);
+ } while ( object != senior );
+}
+
+/**
+ * Add object to top of hierarchy.
+ * \pre object!=NULL
+ */
+void ObjectHierarchy::_addTop(SPObject *object) {
+ g_assert(object != NULL);
+ _hierarchy.push_back(_attach(object));
+ _added_signal.emit(object);
+}
+
+/**
+ * Remove all objects above limit from hierarchy.
+ */
+void ObjectHierarchy::_trimAbove(SPObject *limit) {
+ while ( !_hierarchy.empty() && _hierarchy.back().object != limit ) {
+ SPObject *object=_hierarchy.back().object;
+
+ sp_object_ref(object, NULL);
+ _detach(_hierarchy.back());
+ _hierarchy.pop_back();
+ _removed_signal.emit(object);
+ sp_object_unref(object, NULL);
+ }
+}
+
+/**
+ * Trim or expand hierarchy at bottom such that object becomes bottom entry.
+ */
+void ObjectHierarchy::setBottom(SPObject *object) {
+ g_return_if_fail(object != NULL);
+
+ if ( bottom() == object ) {
+ return;
+ }
+
+ if (!top()) {
+ _addBottom(object);
+ } else if (bottom()->isAncestorOf(object)) {
+ _addBottom(bottom(), object);
+ } else if ( top() == object ) {
+ _trimBelow(top());
+ } else if (top()->isAncestorOf(object)) {
+ if (object->isAncestorOf(bottom())) {
+ _trimBelow(object);
+ } else { // object is a sibling or cousin of bottom()
+ SPObject *saved_top=top();
+ sp_object_ref(saved_top, NULL);
+ _clear();
+ _addBottom(saved_top);
+ _addBottom(saved_top, object);
+ sp_object_unref(saved_top, NULL);
+ }
+ } else {
+ _clear();
+ _addBottom(object);
+ }
+
+ _changed_signal.emit(top(), bottom());
+}
+
+/**
+ * Remove all objects under given object.
+ * \param limit If NULL, remove all.
+ */
+void ObjectHierarchy::_trimBelow(SPObject *limit) {
+ while ( !_hierarchy.empty() && _hierarchy.front().object != limit ) {
+ SPObject *object=_hierarchy.front().object;
+ sp_object_ref(object, NULL);
+ _detach(_hierarchy.front());
+ _hierarchy.pop_front();
+ _removed_signal.emit(object);
+ sp_object_unref(object, NULL);
+ }
+}
+
+/**
+ * Add hierarchy from senior to junior to this hierarchy's bottom.
+ */
+void ObjectHierarchy::_addBottom(SPObject *senior, SPObject *junior) {
+ g_assert(junior != NULL);
+ g_assert(senior != NULL);
+
+ if ( junior != senior ) {
+ _addBottom(senior, SP_OBJECT_PARENT(junior));
+ _addBottom(junior);
+ }
+}
+
+/**
+ * Add object at bottom of hierarchy.
+ * \pre object!=NULL
+ */
+void ObjectHierarchy::_addBottom(SPObject *object) {
+ g_assert(object != NULL);
+ _hierarchy.push_front(_attach(object));
+ _added_signal.emit(object);
+}
+
+void ObjectHierarchy::_trim_for_release(SPObject *object, ObjectHierarchy *hier)
+{
+ hier->_trimBelow(object);
+ g_assert(!hier->_hierarchy.empty());
+ g_assert(hier->_hierarchy.front().object == object);
+
+ sp_object_ref(object, NULL);
+ hier->_detach(hier->_hierarchy.front());
+ hier->_hierarchy.pop_front();
+ hier->_removed_signal.emit(object);
+ sp_object_unref(object, NULL);
+
+ hier->_changed_signal.emit(hier->top(), hier->bottom());
+}
+
+ObjectHierarchy::Record ObjectHierarchy::_attach(SPObject *object) {
+ sp_object_ref(object, NULL);
+ gulong id = g_signal_connect(G_OBJECT(object), "release", GCallback(&ObjectHierarchy::_trim_for_release), this);
+ return Record(object, id);
+}
+
+void ObjectHierarchy::_detach(ObjectHierarchy::Record const &rec) {
+ g_signal_handler_disconnect(G_OBJECT(rec.object), rec.handler_id);
+ sp_object_unref(rec.object, NULL);
+}
+
+}
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :