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