1 /** \file
2 * Object hierarchy implementation.
3 *
4 * Authors:
5 * MenTaLguY <mental@rydia.net>
6 *
7 * Copyright (C) 2004 MenTaLguY
8 *
9 * Released under GNU GPL, read the file 'COPYING' for more information
10 */
12 #include "sp-object.h"
13 #include "object-hierarchy.h"
15 namespace Inkscape {
17 /**
18 * Create new object hierarchy.
19 * \param top The first entry if non-NULL.
20 */
21 ObjectHierarchy::ObjectHierarchy(SPObject *top) {
22 if (top) {
23 _addBottom(top);
24 }
25 }
27 ObjectHierarchy::~ObjectHierarchy() {
28 _clear();
29 }
31 /**
32 * Remove all entries.
33 */
34 void ObjectHierarchy::clear() {
35 _clear();
36 _changed_signal.emit(NULL, NULL);
37 }
39 /**
40 * Trim or expand hierarchy on top such that object becomes top entry.
41 */
42 void ObjectHierarchy::setTop(SPObject *object) {
43 g_return_if_fail(object != NULL);
45 if ( top() == object ) {
46 return;
47 }
49 if (!top()) {
50 _addTop(object);
51 } else if (object->isAncestorOf(top())) {
52 _addTop(object, top());
53 } else if ( object == bottom() || object->isAncestorOf(bottom()) ) {
54 _trimAbove(object);
55 } else {
56 _clear();
57 _addTop(object);
58 }
60 _changed_signal.emit(top(), bottom());
61 }
63 /**
64 * Add hierarchy from junior's parent to senior to this
65 * hierarchy's top.
66 */
67 void ObjectHierarchy::_addTop(SPObject *senior, SPObject *junior) {
68 g_assert(junior != NULL);
69 g_assert(senior != NULL);
71 SPObject *object=SP_OBJECT_PARENT(junior);
72 do {
73 _addTop(object);
74 object = SP_OBJECT_PARENT(object);
75 } while ( object != senior );
76 }
78 /**
79 * Add object to top of hierarchy.
80 * \pre object!=NULL
81 */
82 void ObjectHierarchy::_addTop(SPObject *object) {
83 g_assert(object != NULL);
84 _hierarchy.push_back(_attach(object));
85 _added_signal.emit(object);
86 }
88 /**
89 * Remove all objects above limit from hierarchy.
90 */
91 void ObjectHierarchy::_trimAbove(SPObject *limit) {
92 while ( !_hierarchy.empty() && _hierarchy.back().object != limit ) {
93 SPObject *object=_hierarchy.back().object;
95 sp_object_ref(object, NULL);
96 _detach(_hierarchy.back());
97 _hierarchy.pop_back();
98 _removed_signal.emit(object);
99 sp_object_unref(object, NULL);
100 }
101 }
103 /**
104 * Trim or expand hierarchy at bottom such that object becomes bottom entry.
105 */
106 void ObjectHierarchy::setBottom(SPObject *object) {
107 g_return_if_fail(object != NULL);
109 if ( bottom() == object ) {
110 return;
111 }
113 if (!top()) {
114 _addBottom(object);
115 } else if (bottom()->isAncestorOf(object)) {
116 _addBottom(bottom(), object);
117 } else if ( top() == object ) {
118 _trimBelow(top());
119 } else if (top()->isAncestorOf(object)) {
120 if (object->isAncestorOf(bottom())) {
121 _trimBelow(object);
122 } else { // object is a sibling or cousin of bottom()
123 SPObject *saved_top=top();
124 sp_object_ref(saved_top, NULL);
125 _clear();
126 _addBottom(saved_top);
127 _addBottom(saved_top, object);
128 sp_object_unref(saved_top, NULL);
129 }
130 } else {
131 _clear();
132 _addBottom(object);
133 }
135 _changed_signal.emit(top(), bottom());
136 }
138 /**
139 * Remove all objects under given object.
140 * \param limit If NULL, remove all.
141 */
142 void ObjectHierarchy::_trimBelow(SPObject *limit) {
143 while ( !_hierarchy.empty() && _hierarchy.front().object != limit ) {
144 SPObject *object=_hierarchy.front().object;
145 sp_object_ref(object, NULL);
146 _detach(_hierarchy.front());
147 _hierarchy.pop_front();
148 _removed_signal.emit(object);
149 sp_object_unref(object, NULL);
150 }
151 }
153 /**
154 * Add hierarchy from senior to junior to this hierarchy's bottom.
155 */
156 void ObjectHierarchy::_addBottom(SPObject *senior, SPObject *junior) {
157 g_assert(junior != NULL);
158 g_assert(senior != NULL);
160 if ( junior != senior ) {
161 _addBottom(senior, SP_OBJECT_PARENT(junior));
162 _addBottom(junior);
163 }
164 }
166 /**
167 * Add object at bottom of hierarchy.
168 * \pre object!=NULL
169 */
170 void ObjectHierarchy::_addBottom(SPObject *object) {
171 g_assert(object != NULL);
172 _hierarchy.push_front(_attach(object));
173 _added_signal.emit(object);
174 }
176 void ObjectHierarchy::_trim_for_release(SPObject *object, ObjectHierarchy *hier)
177 {
178 hier->_trimBelow(object);
179 g_assert(!hier->_hierarchy.empty());
180 g_assert(hier->_hierarchy.front().object == object);
182 sp_object_ref(object, NULL);
183 hier->_detach(hier->_hierarchy.front());
184 hier->_hierarchy.pop_front();
185 hier->_removed_signal.emit(object);
186 sp_object_unref(object, NULL);
188 hier->_changed_signal.emit(hier->top(), hier->bottom());
189 }
191 ObjectHierarchy::Record ObjectHierarchy::_attach(SPObject *object) {
192 sp_object_ref(object, NULL);
193 gulong id = g_signal_connect(G_OBJECT(object), "release", GCallback(&ObjectHierarchy::_trim_for_release), this);
194 return Record(object, id);
195 }
197 void ObjectHierarchy::_detach(ObjectHierarchy::Record const &rec) {
198 g_signal_handler_disconnect(G_OBJECT(rec.object), rec.handler_id);
199 sp_object_unref(rec.object, NULL);
200 }
202 }
204 /*
205 Local Variables:
206 mode:c++
207 c-file-style:"stroustrup"
208 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
209 indent-tabs-mode:nil
210 fill-column:99
211 End:
212 */
213 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :