979462cef0b81c06a88e43b91d9aafc430a8f78e
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>
17 #include "algorithms/find-if-before.h"
18 #include "xml/composite-node-observer.h"
19 #include "xml/node-event-vector.h"
20 #include "debug/event-tracker.h"
21 #include "debug/simple-event.h"
23 namespace Inkscape {
25 namespace XML {
27 void CompositeNodeObserver::notifyChildAdded(Node &node, Node &child, Node *prev)
28 {
29 _startIteration();
30 for ( ObserverRecordList::iterator iter=_active.begin() ;
31 iter != _active.end() ; ++iter )
32 {
33 if (!iter->marked) {
34 iter->observer.notifyChildAdded(node, child, prev);
35 }
36 }
37 _finishIteration();
38 }
40 void CompositeNodeObserver::notifyChildRemoved(Node &node, Node &child,
41 Node *prev)
42 {
43 _startIteration();
44 for ( ObserverRecordList::iterator iter=_active.begin() ;
45 iter != _active.end() ; ++iter )
46 {
47 if (!iter->marked) {
48 iter->observer.notifyChildRemoved(node, child, prev);
49 }
50 }
51 _finishIteration();
52 }
54 void CompositeNodeObserver::notifyChildOrderChanged(Node &node, Node &child,
55 Node *old_prev,
56 Node *new_prev)
57 {
58 _startIteration();
59 for ( ObserverRecordList::iterator iter=_active.begin() ;
60 iter != _active.end() ; ++iter )
61 {
62 if (!iter->marked) {
63 iter->observer.notifyChildOrderChanged(node, child, old_prev, new_prev);
64 }
65 }
66 _finishIteration();
67 }
69 void CompositeNodeObserver::notifyContentChanged(
70 Node &node,
71 Util::ptr_shared<char> old_content, Util::ptr_shared<char> new_content
72 ) {
73 _startIteration();
74 for ( ObserverRecordList::iterator iter=_active.begin() ;
75 iter != _active.end() ; ++iter )
76 {
77 if (!iter->marked) {
78 iter->observer.notifyContentChanged(node, old_content, new_content);
79 }
80 }
81 _finishIteration();
82 }
84 void CompositeNodeObserver::notifyAttributeChanged(
85 Node &node, GQuark name,
86 Util::ptr_shared<char> old_value, Util::ptr_shared<char> new_value
87 ) {
88 _startIteration();
89 for ( ObserverRecordList::iterator iter=_active.begin() ;
90 iter != _active.end() ; ++iter )
91 {
92 if (!iter->marked) {
93 iter->observer.notifyAttributeChanged(node, name, old_value, new_value);
94 }
95 }
96 _finishIteration();
97 }
99 void CompositeNodeObserver::add(NodeObserver &observer) {
100 if (_iterating) {
101 _pending.push_back(ObserverRecord(observer));
102 } else {
103 _active.push_back(ObserverRecord(observer));
104 }
105 }
107 namespace {
109 class VectorNodeObserver : public NodeObserver, public GC::Managed<> {
110 public:
111 VectorNodeObserver(NodeEventVector const &v, void *d)
112 : vector(v), data(d) {}
114 NodeEventVector const &vector;
115 void * const data;
117 void notifyChildAdded(Node &node, Node &child, Node *prev) {
118 if (vector.child_added) {
119 vector.child_added(&node, &child, prev, data);
120 }
121 }
123 void notifyChildRemoved(Node &node, Node &child, Node *prev) {
124 if (vector.child_removed) {
125 vector.child_removed(&node, &child, prev, data);
126 }
127 }
129 void notifyChildOrderChanged(Node &node, Node &child, Node *old_prev, Node *new_prev) {
130 if (vector.order_changed) {
131 vector.order_changed(&node, &child, old_prev, new_prev, data);
132 }
133 }
135 void notifyContentChanged(Node &node, Util::ptr_shared<char> old_content, Util::ptr_shared<char> new_content) {
136 if (vector.content_changed) {
137 vector.content_changed(&node, old_content, new_content, data);
138 }
139 }
141 void notifyAttributeChanged(Node &node, GQuark name, Util::ptr_shared<char> old_value, Util::ptr_shared<char> new_value) {
142 if (vector.attr_changed) {
143 vector.attr_changed(&node, g_quark_to_string(name), old_value, new_value, false, data);
144 }
145 }
146 };
148 }
150 void CompositeNodeObserver::addListener(NodeEventVector const &vector,
151 void *data)
152 {
153 Debug::EventTracker<Debug::SimpleEvent<Debug::Event::XML> > tracker("add-listener");
154 add(*(new VectorNodeObserver(vector, data)));
155 }
157 namespace {
159 using std::find_if;
160 using Algorithms::find_if_before;
161 typedef CompositeNodeObserver::ObserverRecord ObserverRecord;
162 typedef CompositeNodeObserver::ObserverRecordList ObserverRecordList;
164 template <typename ObserverPredicate>
165 struct unmarked_record_satisfying {
166 ObserverPredicate predicate;
167 unmarked_record_satisfying(ObserverPredicate p) : predicate(p) {}
168 bool operator()(ObserverRecord const &record) {
169 return !record.marked && predicate(record.observer);
170 }
171 };
173 template <typename Predicate>
174 bool mark_one(ObserverRecordList &observers, unsigned &marked_count,
175 Predicate p)
176 {
177 ObserverRecordList::iterator found=std::find_if(
178 observers.begin(), observers.end(),
179 unmarked_record_satisfying<Predicate>(p)
180 );
182 if ( found != observers.end() ) {
183 found->marked = true;
184 return true;
185 } else {
186 return false;
187 }
188 }
190 template <typename Predicate>
191 bool remove_one(ObserverRecordList &observers, unsigned &marked_count,
192 Predicate p)
193 {
194 if (observers.empty()) {
195 return false;
196 }
198 if (unmarked_record_satisfying<Predicate>(p)(observers.front())) {
199 observers.pop_front();
200 return true;
201 }
203 ObserverRecordList::iterator found=find_if_before(
204 observers.begin(), observers.end(),
205 unmarked_record_satisfying<Predicate>(p)
206 );
208 if ( found != observers.end() ) {
209 observers.erase_after(found);
210 return true;
211 } else {
212 return false;
213 }
214 }
216 bool is_marked(ObserverRecord const &record) { return record.marked; }
218 void remove_all_marked(ObserverRecordList &observers, unsigned &marked_count)
219 {
220 ObserverRecordList::iterator iter;
222 g_assert( !observers.empty() || !marked_count );
224 while ( marked_count && observers.front().marked ) {
225 observers.pop_front();
226 --marked_count;
227 }
229 iter = observers.begin();
230 while (marked_count) {
231 iter = find_if_before(iter, observers.end(), is_marked);
232 observers.erase_after(iter);
233 --marked_count;
234 }
235 }
237 }
239 void CompositeNodeObserver::_finishIteration() {
240 if (!--_iterating) {
241 remove_all_marked(_active, _active_marked);
242 remove_all_marked(_pending, _pending_marked);
243 _active.insert(_active.end(), _pending.begin(), _pending.end());
244 _pending.clear();
245 }
246 }
248 namespace {
250 struct eql_observer {
251 NodeObserver const &observer;
252 eql_observer(NodeObserver const &o) : observer(o) {}
253 bool operator()(NodeObserver const &other) {
254 return &observer == &other;
255 }
256 };
258 }
260 void CompositeNodeObserver::remove(NodeObserver &observer) {
261 eql_observer p(observer);
262 if (_iterating) {
263 mark_one(_active, _active_marked, p) ||
264 mark_one(_pending, _pending_marked, p);
265 } else {
266 remove_one(_active, _active_marked, p) ||
267 remove_one(_pending, _pending_marked, p);
268 }
269 }
271 namespace {
273 struct vector_data_matches {
274 void * const data;
275 vector_data_matches(void *d) : data(d) {}
277 bool operator()(NodeObserver const &observer) {
278 VectorNodeObserver const *vo=dynamic_cast<VectorNodeObserver const *>(&observer);
279 return vo && vo->data == data;
280 }
281 };
283 }
285 void CompositeNodeObserver::removeListenerByData(void *data) {
286 Debug::EventTracker<Debug::SimpleEvent<Debug::Event::XML> > tracker("remove-listener-by-data");
287 vector_data_matches p(data);
288 if (_iterating) {
289 mark_one(_active, _active_marked, p) ||
290 mark_one(_pending, _pending_marked, p);
291 } else {
292 remove_one(_active, _active_marked, p) ||
293 remove_one(_pending, _pending_marked, p);
294 }
295 }
297 }
299 }
301 /*
302 Local Variables:
303 mode:c++
304 c-file-style:"stroustrup"
305 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
306 indent-tabs-mode:nil
307 fill-column:99
308 End:
309 */
310 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :