1 /*
2 * Inkscape::XML::CompositeNodeObserver - combine multiple observers
3 *
4 * Copyright 2005 MenTaLguY <mental@rydia.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * See the file COPYING for details.
12 *
13 */
15 #include <cstring>
16 #include <glib.h>
18 #include "util/find-if-before.h"
19 #include "xml/composite-node-observer.h"
20 #include "xml/node-event-vector.h"
21 #include "debug/event-tracker.h"
22 #include "debug/simple-event.h"
24 namespace Inkscape {
26 namespace XML {
28 void CompositeNodeObserver::notifyChildAdded(Node &node, Node &child, Node *prev)
29 {
30 _startIteration();
31 for ( ObserverRecordList::iterator iter=_active.begin() ;
32 iter != _active.end() ; ++iter )
33 {
34 if (!iter->marked) {
35 iter->observer.notifyChildAdded(node, child, prev);
36 }
37 }
38 _finishIteration();
39 }
41 void CompositeNodeObserver::notifyChildRemoved(Node &node, Node &child,
42 Node *prev)
43 {
44 _startIteration();
45 for ( ObserverRecordList::iterator iter=_active.begin() ;
46 iter != _active.end() ; ++iter )
47 {
48 if (!iter->marked) {
49 iter->observer.notifyChildRemoved(node, child, prev);
50 }
51 }
52 _finishIteration();
53 }
55 void CompositeNodeObserver::notifyChildOrderChanged(Node &node, Node &child,
56 Node *old_prev,
57 Node *new_prev)
58 {
59 _startIteration();
60 for ( ObserverRecordList::iterator iter=_active.begin() ;
61 iter != _active.end() ; ++iter )
62 {
63 if (!iter->marked) {
64 iter->observer.notifyChildOrderChanged(node, child, old_prev, new_prev);
65 }
66 }
67 _finishIteration();
68 }
70 void CompositeNodeObserver::notifyContentChanged(
71 Node &node,
72 Util::ptr_shared<char> old_content, Util::ptr_shared<char> new_content
73 ) {
74 _startIteration();
75 for ( ObserverRecordList::iterator iter=_active.begin() ;
76 iter != _active.end() ; ++iter )
77 {
78 if (!iter->marked) {
79 iter->observer.notifyContentChanged(node, old_content, new_content);
80 }
81 }
82 _finishIteration();
83 }
85 void CompositeNodeObserver::notifyAttributeChanged(
86 Node &node, GQuark name,
87 Util::ptr_shared<char> old_value, Util::ptr_shared<char> new_value
88 ) {
89 _startIteration();
90 for ( ObserverRecordList::iterator iter=_active.begin() ;
91 iter != _active.end() ; ++iter )
92 {
93 if (!iter->marked) {
94 iter->observer.notifyAttributeChanged(node, name, old_value, new_value);
95 }
96 }
97 _finishIteration();
98 }
100 void CompositeNodeObserver::add(NodeObserver &observer) {
101 if (_iterating) {
102 _pending.push_back(ObserverRecord(observer));
103 } else {
104 _active.push_back(ObserverRecord(observer));
105 }
106 }
108 namespace {
110 class VectorNodeObserver : public NodeObserver, public GC::Managed<> {
111 public:
112 VectorNodeObserver(NodeEventVector const &v, void *d)
113 : vector(v), data(d) {}
115 NodeEventVector const &vector;
116 void * const data;
118 void notifyChildAdded(Node &node, Node &child, Node *prev) {
119 if (vector.child_added) {
120 vector.child_added(&node, &child, prev, data);
121 }
122 }
124 void notifyChildRemoved(Node &node, Node &child, Node *prev) {
125 if (vector.child_removed) {
126 vector.child_removed(&node, &child, prev, data);
127 }
128 }
130 void notifyChildOrderChanged(Node &node, Node &child, Node *old_prev, Node *new_prev) {
131 if (vector.order_changed) {
132 vector.order_changed(&node, &child, old_prev, new_prev, data);
133 }
134 }
136 void notifyContentChanged(Node &node, Util::ptr_shared<char> old_content, Util::ptr_shared<char> new_content) {
137 if (vector.content_changed) {
138 vector.content_changed(&node, old_content, new_content, data);
139 }
140 }
142 void notifyAttributeChanged(Node &node, GQuark name, Util::ptr_shared<char> old_value, Util::ptr_shared<char> new_value) {
143 if (vector.attr_changed) {
144 vector.attr_changed(&node, g_quark_to_string(name), old_value, new_value, false, data);
145 }
146 }
147 };
149 }
151 void CompositeNodeObserver::addListener(NodeEventVector const &vector,
152 void *data)
153 {
154 Debug::EventTracker<Debug::SimpleEvent<Debug::Event::XML> > tracker("add-listener");
155 add(*(new VectorNodeObserver(vector, data)));
156 }
158 namespace {
160 using std::find_if;
161 using Algorithms::find_if_before;
162 typedef CompositeNodeObserver::ObserverRecord ObserverRecord;
163 typedef CompositeNodeObserver::ObserverRecordList ObserverRecordList;
165 template <typename ObserverPredicate>
166 struct unmarked_record_satisfying {
167 ObserverPredicate predicate;
168 unmarked_record_satisfying(ObserverPredicate p) : predicate(p) {}
169 bool operator()(ObserverRecord const &record) {
170 return !record.marked && predicate(record.observer);
171 }
172 };
174 template <typename Predicate>
175 bool mark_one(ObserverRecordList &observers, unsigned &/*marked_count*/,
176 Predicate p)
177 {
178 ObserverRecordList::iterator found=std::find_if(
179 observers.begin(), observers.end(),
180 unmarked_record_satisfying<Predicate>(p)
181 );
183 if ( found != observers.end() ) {
184 found->marked = true;
185 return true;
186 } else {
187 return false;
188 }
189 }
191 template <typename Predicate>
192 bool remove_one(ObserverRecordList &observers, unsigned &/*marked_count*/,
193 Predicate p)
194 {
195 if (observers.empty()) {
196 return false;
197 }
199 if (unmarked_record_satisfying<Predicate>(p)(observers.front())) {
200 observers.pop_front();
201 return true;
202 }
204 ObserverRecordList::iterator found=find_if_before(
205 observers.begin(), observers.end(),
206 unmarked_record_satisfying<Predicate>(p)
207 );
209 if ( found != observers.end() ) {
210 observers.erase_after(found);
211 return true;
212 } else {
213 return false;
214 }
215 }
217 bool is_marked(ObserverRecord const &record) { return record.marked; }
219 void remove_all_marked(ObserverRecordList &observers, unsigned &marked_count)
220 {
221 ObserverRecordList::iterator iter;
223 g_assert( !observers.empty() || !marked_count );
225 while ( marked_count && observers.front().marked ) {
226 observers.pop_front();
227 --marked_count;
228 }
230 iter = observers.begin();
231 while (marked_count) {
232 iter = find_if_before(iter, observers.end(), is_marked);
233 observers.erase_after(iter);
234 --marked_count;
235 }
236 }
238 }
240 void CompositeNodeObserver::_finishIteration() {
241 if (!--_iterating) {
242 remove_all_marked(_active, _active_marked);
243 remove_all_marked(_pending, _pending_marked);
244 _active.insert(_active.end(), _pending.begin(), _pending.end());
245 _pending.clear();
246 }
247 }
249 namespace {
251 struct eql_observer {
252 NodeObserver const &observer;
253 eql_observer(NodeObserver const &o) : observer(o) {}
254 bool operator()(NodeObserver const &other) {
255 return &observer == &other;
256 }
257 };
259 }
261 void CompositeNodeObserver::remove(NodeObserver &observer) {
262 eql_observer p(observer);
263 if (_iterating) {
264 mark_one(_active, _active_marked, p) ||
265 mark_one(_pending, _pending_marked, p);
266 } else {
267 remove_one(_active, _active_marked, p) ||
268 remove_one(_pending, _pending_marked, p);
269 }
270 }
272 namespace {
274 struct vector_data_matches {
275 void * const data;
276 vector_data_matches(void *d) : data(d) {}
278 bool operator()(NodeObserver const &observer) {
279 VectorNodeObserver const *vo=dynamic_cast<VectorNodeObserver const *>(&observer);
280 return vo && vo->data == data;
281 }
282 };
284 }
286 void CompositeNodeObserver::removeListenerByData(void *data) {
287 Debug::EventTracker<Debug::SimpleEvent<Debug::Event::XML> > tracker("remove-listener-by-data");
288 vector_data_matches p(data);
289 if (_iterating) {
290 mark_one(_active, _active_marked, p) ||
291 mark_one(_pending, _pending_marked, p);
292 } else {
293 remove_one(_active, _active_marked, p) ||
294 remove_one(_pending, _pending_marked, p);
295 }
296 }
298 }
300 }
302 /*
303 Local Variables:
304 mode:c++
305 c-file-style:"stroustrup"
306 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
307 indent-tabs-mode:nil
308 fill-column:99
309 End:
310 */
311 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :