diff options
Diffstat (limited to '')
-rw-r--r-- | src/xml/composite-node-observer.cpp | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/src/xml/composite-node-observer.cpp b/src/xml/composite-node-observer.cpp new file mode 100644 index 0000000..398cbc5 --- /dev/null +++ b/src/xml/composite-node-observer.cpp @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: see git history + * + * Copyright (C) 2018 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +/* + * Inkscape::XML::CompositeNodeObserver - combine multiple observers + * + * Copyright 2005 MenTaLguY <mental@rydia.net> + * + * 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 2 + * of the License, or (at your option) any later version. + * + * See the file COPYING for details. + * + */ + +#include <algorithm> +#include <cstring> +#include <glib.h> + +#include "xml/composite-node-observer.h" +#include "xml/node-event-vector.h" +#include "debug/event-tracker.h" +#include "debug/simple-event.h" + +namespace Inkscape { + +namespace XML { + +void CompositeNodeObserver::notifyChildAdded(Node &node, Node &child, Node *prev) +{ + _startIteration(); + for (auto & iter : _active) + { + if (!iter.marked) { + iter.observer.notifyChildAdded(node, child, prev); + } + } + _finishIteration(); +} + +void CompositeNodeObserver::notifyChildRemoved(Node &node, Node &child, + Node *prev) +{ + _startIteration(); + for (auto & iter : _active) + { + if (!iter.marked) { + iter.observer.notifyChildRemoved(node, child, prev); + } + } + _finishIteration(); +} + +void CompositeNodeObserver::notifyChildOrderChanged(Node &node, Node &child, + Node *old_prev, + Node *new_prev) +{ + _startIteration(); + for (auto & iter : _active) + { + if (!iter.marked) { + iter.observer.notifyChildOrderChanged(node, child, old_prev, new_prev); + } + } + _finishIteration(); +} + +void CompositeNodeObserver::notifyContentChanged( + Node &node, + Util::ptr_shared old_content, Util::ptr_shared new_content +) { + _startIteration(); + for (auto & iter : _active) + { + if (!iter.marked) { + iter.observer.notifyContentChanged(node, old_content, new_content); + } + } + _finishIteration(); +} + +void CompositeNodeObserver::notifyAttributeChanged( + Node &node, GQuark name, + Util::ptr_shared old_value, Util::ptr_shared new_value +) { + _startIteration(); + for (auto & iter : _active) + { + if (!iter.marked) { + iter.observer.notifyAttributeChanged(node, name, old_value, new_value); + } + } + _finishIteration(); +} + +void CompositeNodeObserver::notifyElementNameChanged(Node& node, GQuark old_name, GQuark new_name) +{ + _startIteration(); + for (auto& iter : _active) { + if (!iter.marked) { + iter.observer.notifyElementNameChanged(node, old_name, new_name); + } + } + _finishIteration(); +} + +void CompositeNodeObserver::add(NodeObserver &observer) { + if (_iterating) { + _pending.emplace_back(observer); + } else { + _active.emplace_back(observer); + } +} + +namespace { + +class VectorNodeObserver : public NodeObserver, public GC::Managed<> { +public: + VectorNodeObserver(NodeEventVector const &v, void *d) + : vector(v), data(d) {} + + NodeEventVector const &vector; + void * const data; + + void notifyChildAdded(Node &node, Node &child, Node *prev) override { + if (vector.child_added) { + vector.child_added(&node, &child, prev, data); + } + } + + void notifyChildRemoved(Node &node, Node &child, Node *prev) override { + if (vector.child_removed) { + vector.child_removed(&node, &child, prev, data); + } + } + + void notifyChildOrderChanged(Node &node, Node &child, Node *old_prev, Node *new_prev) override { + if (vector.order_changed) { + vector.order_changed(&node, &child, old_prev, new_prev, data); + } + } + + void notifyContentChanged(Node &node, Util::ptr_shared old_content, Util::ptr_shared new_content) override { + if (vector.content_changed) { + vector.content_changed(&node, old_content, new_content, data); + } + } + + void notifyAttributeChanged(Node &node, GQuark name, Util::ptr_shared old_value, Util::ptr_shared new_value) override { + if (vector.attr_changed) { + vector.attr_changed(&node, g_quark_to_string(name), old_value, new_value, false, data); + } + } + + void notifyElementNameChanged(Node& node, GQuark old_name, GQuark new_name) override { + if (vector.element_name_changed) { + vector.element_name_changed(&node, g_quark_to_string(old_name), g_quark_to_string(new_name), data); + } + } +}; + +} + +void CompositeNodeObserver::addListener(NodeEventVector const &vector, + void *data) +{ + Debug::EventTracker<Debug::SimpleEvent<Debug::Event::XML> > tracker("add-listener"); + add(*(new VectorNodeObserver(vector, data))); +} + +namespace { + +typedef CompositeNodeObserver::ObserverRecord ObserverRecord; +typedef CompositeNodeObserver::ObserverRecordList ObserverRecordList; + +template <typename ObserverPredicate> +struct unmarked_record_satisfying { + ObserverPredicate predicate; + unmarked_record_satisfying(ObserverPredicate p) : predicate(p) {} + bool operator()(ObserverRecord const &record) { + return !record.marked && predicate(record.observer); + } +}; + +template <typename Predicate> +bool mark_one(ObserverRecordList &observers, unsigned &marked_count, + Predicate p) +{ + ObserverRecordList::iterator found=std::find_if( + observers.begin(), observers.end(), + unmarked_record_satisfying<Predicate>(p) + ); + + if ( found != observers.end() ) { + ++marked_count; + found->marked = true; + return true; + } else { + return false; + } +} + +template <typename Predicate> +bool remove_one(ObserverRecordList &observers, unsigned &/*marked_count*/, + Predicate p) +{ + ObserverRecordList::iterator found = std::find_if( + observers.begin(), observers.end(), + unmarked_record_satisfying<Predicate>(p) + ); + + if ( found != observers.end() ) { + observers.erase(found); + return true; + } else { + return false; + } +} + +bool is_marked(ObserverRecord const &record) { return record.marked; } + +void remove_all_marked(ObserverRecordList &observers, unsigned &marked_count) +{ + if (marked_count) { + g_assert(!observers.empty()); + + observers.remove_if(is_marked); + marked_count = 0; + } +} + +} + +void CompositeNodeObserver::_finishIteration() { + if (!--_iterating) { + remove_all_marked(_active, _active_marked); + remove_all_marked(_pending, _pending_marked); + _active.splice(_active.end(), std::move(_pending)); + g_assert(_pending.empty()); + } +} + +namespace { + +struct eql_observer { + NodeObserver const &observer; + eql_observer(NodeObserver const &o) : observer(o) {} + bool operator()(NodeObserver const &other) { + return &observer == &other; + } +}; + +} + +void CompositeNodeObserver::remove(NodeObserver &observer) { + eql_observer p(observer); + if (_iterating) { + mark_one(_active, _active_marked, p) || + mark_one(_pending, _pending_marked, p); + } else { + remove_one(_active, _active_marked, p) || + remove_one(_pending, _pending_marked, p); + } +} + +namespace { + +struct vector_data_matches { + void * const data; + vector_data_matches(void *d) : data(d) {} + + bool operator()(NodeObserver const &observer) { + VectorNodeObserver const *vo=dynamic_cast<VectorNodeObserver const *>(&observer); + bool OK = false; + if (vo) { + if (vo && vo->data == data) { + OK = true; + } + } + return OK; + } +}; + +} + +void CompositeNodeObserver::removeListenerByData(void *data) { + Debug::EventTracker<Debug::SimpleEvent<Debug::Event::XML> > tracker("remove-listener-by-data"); + vector_data_matches p(data); + if (_iterating) { + mark_one(_active, _active_marked, p) || + mark_one(_pending, _pending_marked, p); + } else { + remove_one(_active, _active_marked, p) || + remove_one(_pending, _pending_marked, p); + } +} + +} + +} + +/* + 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:fileencoding=utf-8:textwidth=99 : |