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 #include <sigc++/functors/mem_fun.h>
17 namespace Inkscape {
19 /**
20 * Create new object hierarchy.
21 * \param top The first entry if non-NULL.
22 */
23 ObjectHierarchy::ObjectHierarchy(SPObject *top) {
24 if (top) {
25 _addBottom(top);
26 }
27 }
29 ObjectHierarchy::~ObjectHierarchy() {
30 _clear();
31 }
33 /**
34 * Remove all entries.
35 */
36 void ObjectHierarchy::clear() {
37 _clear();
38 _changed_signal.emit(NULL, NULL);
39 }
41 /**
42 * Trim or expand hierarchy on top such that object becomes top entry.
43 */
44 void ObjectHierarchy::setTop(SPObject *object) {
45 g_return_if_fail(object != NULL);
47 if ( top() == object ) {
48 return;
49 }
51 if (!top()) {
52 _addTop(object);
53 } else if (object->isAncestorOf(top())) {
54 _addTop(object, top());
55 } else if ( object == bottom() || object->isAncestorOf(bottom()) ) {
56 _trimAbove(object);
57 } else {
58 _clear();
59 _addTop(object);
60 }
62 _changed_signal.emit(top(), bottom());
63 }
65 /**
66 * Add hierarchy from junior's parent to senior to this
67 * hierarchy's top.
68 */
69 void ObjectHierarchy::_addTop(SPObject *senior, SPObject *junior) {
70 g_assert(junior != NULL);
71 g_assert(senior != NULL);
73 SPObject *object=SP_OBJECT_PARENT(junior);
74 do {
75 _addTop(object);
76 object = SP_OBJECT_PARENT(object);
77 } while ( object != senior );
78 }
80 /**
81 * Add object to top of hierarchy.
82 * \pre object!=NULL
83 */
84 void ObjectHierarchy::_addTop(SPObject *object) {
85 g_assert(object != NULL);
86 _hierarchy.push_back(_attach(object));
87 _added_signal.emit(object);
88 }
90 /**
91 * Remove all objects above limit from hierarchy.
92 */
93 void ObjectHierarchy::_trimAbove(SPObject *limit) {
94 while ( !_hierarchy.empty() && _hierarchy.back().object != limit ) {
95 SPObject *object=_hierarchy.back().object;
97 sp_object_ref(object, NULL);
98 _detach(_hierarchy.back());
99 _hierarchy.pop_back();
100 _removed_signal.emit(object);
101 sp_object_unref(object, NULL);
102 }
103 }
105 /**
106 * Trim or expand hierarchy at bottom such that object becomes bottom entry.
107 */
108 void ObjectHierarchy::setBottom(SPObject *object) {
109 g_return_if_fail(object != NULL);
111 if ( bottom() == object ) {
112 return;
113 }
115 if (!top()) {
116 _addBottom(object);
117 } else if (bottom()->isAncestorOf(object)) {
118 _addBottom(bottom(), object);
119 } else if ( top() == object ) {
120 _trimBelow(top());
121 } else if (top()->isAncestorOf(object)) {
122 if (object->isAncestorOf(bottom())) {
123 _trimBelow(object);
124 } else { // object is a sibling or cousin of bottom()
125 SPObject *saved_top=top();
126 sp_object_ref(saved_top, NULL);
127 _clear();
128 _addBottom(saved_top);
129 _addBottom(saved_top, object);
130 sp_object_unref(saved_top, NULL);
131 }
132 } else {
133 _clear();
134 _addBottom(object);
135 }
137 _changed_signal.emit(top(), bottom());
138 }
140 /**
141 * Remove all objects under given object.
142 * \param limit If NULL, remove all.
143 */
144 void ObjectHierarchy::_trimBelow(SPObject *limit) {
145 while ( !_hierarchy.empty() && _hierarchy.front().object != limit ) {
146 SPObject *object=_hierarchy.front().object;
147 sp_object_ref(object, NULL);
148 _detach(_hierarchy.front());
149 _hierarchy.pop_front();
150 _removed_signal.emit(object);
151 sp_object_unref(object, NULL);
152 }
153 }
155 /**
156 * Add hierarchy from senior to junior to this hierarchy's bottom.
157 */
158 void ObjectHierarchy::_addBottom(SPObject *senior, SPObject *junior) {
159 g_assert(junior != NULL);
160 g_assert(senior != NULL);
162 if ( junior != senior ) {
163 _addBottom(senior, SP_OBJECT_PARENT(junior));
164 _addBottom(junior);
165 }
166 }
168 /**
169 * Add object at bottom of hierarchy.
170 * \pre object!=NULL
171 */
172 void ObjectHierarchy::_addBottom(SPObject *object) {
173 g_assert(object != NULL);
174 _hierarchy.push_front(_attach(object));
175 _added_signal.emit(object);
176 }
178 void ObjectHierarchy::_trim_for_release(SPObject *object) {
179 this->_trimBelow(object);
180 g_assert(!this->_hierarchy.empty());
181 g_assert(this->_hierarchy.front().object == object);
183 sp_object_ref(object, NULL);
184 this->_detach(this->_hierarchy.front());
185 this->_hierarchy.pop_front();
186 this->_removed_signal.emit(object);
187 sp_object_unref(object, NULL);
189 this->_changed_signal.emit(this->top(), this->bottom());
190 }
192 ObjectHierarchy::Record ObjectHierarchy::_attach(SPObject *object) {
193 sp_object_ref(object, NULL);
194 sigc::connection connection
195 = object->connectRelease(
196 sigc::mem_fun(*this, &ObjectHierarchy::_trim_for_release)
197 );
198 return Record(object, connection);
199 }
201 void ObjectHierarchy::_detach(ObjectHierarchy::Record &rec) {
202 rec.connection.disconnect();
203 sp_object_unref(rec.object, NULL);
204 }
206 }
208 /*
209 Local Variables:
210 mode:c++
211 c-file-style:"stroustrup"
212 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
213 indent-tabs-mode:nil
214 fill-column:99
215 End:
216 */
217 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :