From a8f1a69ef0da383f9b413c3043a74084e23b75c2 Mon Sep 17 00:00:00 2001 From: mental Date: Sun, 5 Mar 2006 21:28:27 +0000 Subject: [PATCH] Add DocumentSubset as groundwork for layers --- ChangeLog | 6 + src/Makefile_insert | 1 + src/document-subset.cpp | 362 ++++++++++++++++++++++++++++++++++++++++ src/document-subset.h | 70 ++++++++ 4 files changed, 439 insertions(+) create mode 100644 src/document-subset.cpp create mode 100644 src/document-subset.h diff --git a/ChangeLog b/ChangeLog index aef3dcd00..622e817e5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2006-03-05 MenTaLguY + + * src/Makefile_insert, src/document-subset.cpp, src/document-subset.h: + + Add DocumentSubset as groundwork for layers. + 2006-03-04 Jon A. Cruz * src/ui/widget/selected-style.h, src/ui/widget/selected-style.cpp: diff --git a/src/Makefile_insert b/src/Makefile_insert index 302bd8089..d42c2fa3b 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -53,6 +53,7 @@ libinkpre_a_SOURCES = \ desktop.cpp desktop.h \ document-undo.cpp \ document.cpp document.h document-private.h \ + document-subset.cpp document-subset.h \ doxygen-main.cpp \ draw-anchor.cpp \ draw-anchor.h \ diff --git a/src/document-subset.cpp b/src/document-subset.cpp new file mode 100644 index 000000000..08638b9f3 --- /dev/null +++ b/src/document-subset.cpp @@ -0,0 +1,362 @@ +/* + * Inkscape::DocumentSubset - view of a document including only a subset + * of nodes + * + * Copyright 2006 MenTaLguY + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "gc-finalized.h" +#include "document-subset.h" +#include "document.h" +#include "sp-object.h" + +#include + +#include + +#include "util/list.h" +#include "util/reverse-list.h" + +#include +#include +#include +#include + +namespace Inkscape { + +struct DocumentSubset::Relations : public GC::Managed, + public GC::Finalized +{ + typedef std::vector Siblings; + + struct Record { + SPObject *parent; + Siblings children; + + gulong release_connection; + + Record() : parent(NULL), release_connection(0) {} + + unsigned childIndex(SPObject *obj) { + Siblings::iterator found; + found = std::find(children.begin(), children.end(), obj); + if ( found != children.end() ) { + return found - children.begin(); + } else { + return 0; + } + } + + unsigned findInsertIndex(SPObject *obj) const { + if (children.empty()) { + return 0; + } else { + Siblings::const_iterator first=children.begin(); + Siblings::const_iterator last=children.end() - 1; + while ( first != last ) { + Siblings::const_iterator mid=first + ( last - first + 1 ) / 2; + int pos=sp_object_compare_position(*mid, obj); + if ( pos < 0 ) { + first = mid; + } else if ( pos > 0 ) { + last = mid; + } else { + g_assert_not_reached(); + } + } + return last - children.begin(); + } + } + + void addChild(SPObject *obj) { + unsigned index=findInsertIndex(obj); + children.insert(children.begin()+index, obj); + } + + template + void extractDescendants(OutputIterator descendants, + SPObject *obj) + { + Siblings new_children; + bool found_one=false; + for ( Siblings::iterator iter=children.begin() + ; iter != children.end() ; iter++ ) + { + if (obj->isAncestorOf(*iter)) { + if (!found_one) { + found_one = true; + new_children.insert(new_children.end(), + children.begin(), iter); + } + *descendants++ = *iter; + } else if (found_one) { + new_children.push_back(*iter); + } + } + if (found_one) { + children.swap(new_children); + } + } + + unsigned removeChild(SPObject *obj) { + Siblings::iterator found; + found = std::find(children.begin(), children.end(), obj); + unsigned index = found - children.begin(); + if ( found != children.end() ) { + children.erase(found); + } + return index; + } + }; + + typedef std::map Map; + Map records; + + sigc::signal changed_signal; + sigc::signal added_signal; + sigc::signal removed_signal; + + Relations() { records[NULL]; } + + ~Relations() { + for ( Map::iterator iter=records.begin() + ; iter != records.end() ; ++iter ) + { + sp_object_unref((*iter).first); + } + } + + Record *get(SPObject *obj) { + Map::iterator found=records.find(obj); + if ( found != records.end() ) { + return &(*found).second; + } else { + return NULL; + } + } + + void addOne(SPObject *obj); + void remove(SPObject *obj, bool subtree); + void reorder(SPObject *obj); + +private: + Record &_doAdd(SPObject *obj) { + sp_object_ref(obj); + Record &record=records[obj]; + record.release_connection + = g_signal_connect(obj, "release", + (GCallback)&Relations::_release_object, this); + return record; + } + + void _notifyAdded(SPObject *obj) { + added_signal.emit(obj); + } + + void _doRemove(SPObject *obj) { + Record &record=records[obj]; + if (record.release_connection) { + g_signal_handler_disconnect(obj, record.release_connection); + record.release_connection = 0; + } + records.erase(obj); + removed_signal.emit(obj); + sp_object_unref(obj); + } + + void _doRemoveSubtree(SPObject *obj) { + Record *record=get(obj); + if (record) { + Siblings &children=record->children; + for ( Siblings::iterator iter=children.begin() + ; iter != children.end() ; ++iter ) + { + _doRemoveSubtree(*iter); + } + _doRemove(obj); + } + } + + static void _release_object(SPObject *obj, void *relations_p) { + Relations &relations=*static_cast(relations_p); + if (relations.get(obj)) { + relations.remove(obj, true); + } + } +}; + +DocumentSubset::DocumentSubset(SPDocument *document) +: _document(document), _relations(new DocumentSubset::Relations()) +{ +} + +void DocumentSubset::Relations::addOne(SPObject *obj) { + g_return_if_fail( obj != NULL ); + g_return_if_fail( get(obj) != NULL ); + + Record &record=_doAdd(obj); + + /* find the nearest ancestor in the subset */ + Record *parent_record=NULL; + for ( SPObject::ParentIterator parent_iter=obj->parent + ; !parent_record && parent_iter ; ++parent_iter ) + { + parent_record = get(parent_iter); + if (parent_record) { + record.parent = parent_iter; + } + } + if (!parent_record) { + parent_record = get(NULL); + g_assert( parent_record != NULL ); + } + + Siblings &children=record.children; + + /* reparent descendants of obj to obj */ + parent_record->extractDescendants( + std::back_insert_iterator(children), + obj + ); + for ( Siblings::iterator iter=children.begin() + ; iter != children.end() ; ++iter ) + { + Record *child_record=get(*iter); + g_assert( child_record != NULL ); + child_record->parent = obj; + } + + /* add obj to the child list */ + parent_record->addChild(obj); + + _notifyAdded(obj); + changed_signal.emit(); +} + +void DocumentSubset::Relations::remove(SPObject *obj, bool subtree) { + g_return_if_fail( obj != NULL ); + + Record *record=get(obj); + g_return_if_fail( record != NULL ); + + Record *parent_record=get(record->parent); + g_assert( parent_record != NULL ); + + unsigned index=parent_record->removeChild(obj); + + if (subtree) { + _doRemoveSubtree(obj); + } else { + /* reparent obj's orphaned children to their grandparent */ + Siblings &siblings=parent_record->children; + Siblings &children=record->children; + siblings.insert(siblings.begin()+index, + children.begin(), children.end()); + + for ( Siblings::iterator iter=children.begin() + ; iter != children.end() ; iter++ ) + { + Record *child_record=get(*iter); + g_assert( child_record != NULL ); + child_record->parent = record->parent; + } + + /* remove obj's record */ + _doRemove(obj); + } + + changed_signal.emit(); +} + +void DocumentSubset::Relations::reorder(SPObject *obj) { + SPObject::ParentIterator parent=obj; + + /* find nearest ancestor in the subset */ + Record *parent_record=NULL; + while (!parent_record) { + parent_record = get(++parent); + } + + if (get(obj)) { + /* move the object if it's in the subset */ + parent_record->removeChild(obj); + parent_record->addChild(obj); + changed_signal.emit(); + } else { + /* otherwise, move any top-level descendants */ + Siblings descendants; + parent_record->extractDescendants( + std::back_insert_iterator(descendants), + obj + ); + if (!descendants.empty()) { + unsigned index=parent_record->findInsertIndex(obj); + Siblings &family=parent_record->children; + family.insert(family.begin()+index, + descendants.begin(), descendants.end()); + changed_signal.emit(); + } + } +} + +void DocumentSubset::_addOne(SPObject *obj) { + _relations->addOne(obj); +} + +void DocumentSubset::_remove(SPObject *obj, bool subtree) { + _relations->remove(obj, subtree); +} + +bool DocumentSubset::includes(SPObject *obj) const { + return _relations->get(obj); +} + +SPObject *DocumentSubset::parentOf(SPObject *obj) const { + Relations::Record *record=_relations->get(obj); + return ( record ? record->parent : NULL ); +} + +unsigned DocumentSubset::childCount(SPObject *obj) const { + Relations::Record *record=_relations->get(obj); + return ( record ? record->children.size() : 0 ); +} + +unsigned DocumentSubset::indexOf(SPObject *obj) const { + SPObject *parent=parentOf(obj); + Relations::Record *record=_relations->get(parent); + return ( record ? record->childIndex(obj) : 0 ); +} + +SPObject *DocumentSubset::nthChildOf(SPObject *obj, unsigned n) const { + Relations::Record *record=_relations->get(obj); + return ( record ? record->children[n] : NULL ); +} + +sigc::connection DocumentSubset::connectChanged(sigc::slot slot) const { + return _relations->changed_signal.connect(slot); +} + +sigc::connection +DocumentSubset::connectAdded(sigc::slot slot) const { + return _relations->added_signal.connect(slot); +} + +sigc::connection +DocumentSubset::connectRemoved(sigc::slot slot) const { + return _relations->removed_signal.connect(slot); +} + +} + +/* + 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 : diff --git a/src/document-subset.h b/src/document-subset.h new file mode 100644 index 000000000..0e40bab8d --- /dev/null +++ b/src/document-subset.h @@ -0,0 +1,70 @@ +/* + * Inkscape::DocumentSubset - view of a document including only a subset + * of nodes + * + * Copyright 2006 MenTaLguY + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_DOCUMENT_SUBSET_H +#define SEEN_INKSCAPE_DOCUMENT_SUBSET_H + +#include +#include + +#include "gc-managed.h" +#include "gc-anchored.h" + +class SPObject; +class SPDocument; + +namespace Inkscape { + +class DocumentSubset : public GC::Managed<>, + public GC::Anchored +{ +public: + bool includes(SPObject *obj) const; + + SPObject *parentOf(SPObject *obj) const; + unsigned childCount(SPObject *obj) const; + unsigned indexOf(SPObject *obj) const; + SPObject *nthChildOf(SPObject *obj, unsigned n) const; + + sigc::connection connectChanged(sigc::slot slot) const; + sigc::connection connectAdded(sigc::slot slot) const; + sigc::connection connectRemoved(sigc::slot slot) const; + +protected: + explicit DocumentSubset(SPDocument *document); + + void _addOne(SPObject *obj); + void _removeOne(SPObject *obj) { _remove(obj, false); } + void _removeSubtree(SPObject *obj) { _remove(obj, true); } + +private: + DocumentSubset(DocumentSubset const &); // no copy + void operator=(DocumentSubset const &); // no assign + + void _remove(SPObject *obj, bool subtree); + + class Relations; + + SPDocument *_document; + Relations *_relations; +}; + +} + +#endif +/* + 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 : -- 2.30.2