summaryrefslogtreecommitdiffstats
path: root/src/xml/composite-node-observer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/xml/composite-node-observer.cpp')
-rw-r--r--src/xml/composite-node-observer.cpp336
1 files changed, 336 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..77a1bd2
--- /dev/null
+++ b/src/xml/composite-node-observer.cpp
@@ -0,0 +1,336 @@
+// 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 <cstring>
+#include <glib.h>
+
+#include "util/find-if-before.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.push_back(ObserverRecord(observer));
+ } else {
+ _active.push_back(ObserverRecord(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() ) {
+ found->marked = true;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+template <typename Predicate>
+bool remove_one(ObserverRecordList &observers, unsigned &/*marked_count*/,
+ Predicate p)
+{
+ if (observers.empty()) {
+ return false;
+ }
+
+ if (unmarked_record_satisfying<Predicate>(p)(observers.front())) {
+ observers.pop_front();
+ return true;
+ }
+
+ ObserverRecordList::iterator found=Algorithms::find_if_before(
+ observers.begin(), observers.end(),
+ unmarked_record_satisfying<Predicate>(p)
+ );
+
+ if ( found != observers.end() ) {
+ observers.erase_after(found);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool is_marked(ObserverRecord const &record) { return record.marked; }
+
+void remove_all_marked(ObserverRecordList &observers, unsigned &marked_count)
+{
+ ObserverRecordList::iterator iter;
+
+ g_assert( !observers.empty() || !marked_count );
+
+ while ( marked_count && observers.front().marked ) {
+ observers.pop_front();
+ --marked_count;
+ }
+
+ iter = observers.begin();
+ while (marked_count) {
+ iter = Algorithms::find_if_before(iter, observers.end(), is_marked);
+ observers.erase_after(iter);
+ --marked_count;
+ }
+}
+
+}
+
+void CompositeNodeObserver::_finishIteration() {
+ if (!--_iterating) {
+ remove_all_marked(_active, _active_marked);
+ remove_all_marked(_pending, _pending_marked);
+ _active.insert(_active.end(), _pending.begin(), _pending.end());
+ _pending.clear();
+ }
+}
+
+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 :