// 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 * * 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 #include #include #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 > tracker("add-listener"); add(*(new VectorNodeObserver(vector, data))); } namespace { typedef CompositeNodeObserver::ObserverRecord ObserverRecord; typedef CompositeNodeObserver::ObserverRecordList ObserverRecordList; template 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 bool mark_one(ObserverRecordList &observers, unsigned &marked_count, Predicate p) { ObserverRecordList::iterator found=std::find_if( observers.begin(), observers.end(), unmarked_record_satisfying(p) ); if ( found != observers.end() ) { ++marked_count; found->marked = true; return true; } else { return false; } } template bool remove_one(ObserverRecordList &observers, unsigned &/*marked_count*/, Predicate p) { ObserverRecordList::iterator found = std::find_if( observers.begin(), observers.end(), unmarked_record_satisfying(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(&observer); bool OK = false; if (vo) { if (vo && vo->data == data) { OK = true; } } return OK; } }; } void CompositeNodeObserver::removeListenerByData(void *data) { Debug::EventTracker > 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 :