1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Inkscape::DocumentSubset - view of a document including only a subset
6 * Copyright 2006 MenTaLguY <mental@rydia.net>
9 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 #include "document-subset.h"
16 #include "object/sp-object.h"
21 struct DocumentSubset::Relations
23 typedef std::vector
<SPObject
*> Siblings
;
29 sigc::connection release_connection
;
30 sigc::connection position_changed_connection
;
32 Record() : parent(nullptr) {}
34 unsigned childIndex(SPObject
*obj
) {
35 Siblings::iterator found
;
36 found
= std::find(children
.begin(), children
.end(), obj
);
37 if ( found
!= children
.end() ) {
38 return found
- children
.begin();
44 unsigned findInsertIndex(SPObject
*obj
) const {
45 if (children
.empty()) {
48 Siblings::const_iterator first
=children
.begin();
49 Siblings::const_iterator last
=children
.end() - 1;
51 while ( first
!= last
) {
52 Siblings::const_iterator mid
= first
+ ( last
- first
+ 1 ) / 2;
53 int pos
= sp_object_compare_position(*mid
, obj
);
56 } else if ( pos
> 0 ) {
58 last
= mid
- 1; // already at the top limit
63 g_assert_not_reached();
67 if ( first
== last
) {
68 // compare to the single possibility left
69 int pos
= sp_object_compare_position(*last
, obj
);
75 return last
- children
.begin();
79 void addChild(SPObject
*obj
) {
80 unsigned index
=findInsertIndex(obj
);
81 children
.insert(children
.begin()+index
, obj
);
84 template <typename OutputIterator
>
85 void extractDescendants(OutputIterator descendants
,
88 Siblings new_children
;
90 for ( Siblings::iterator iter
=children
.begin()
91 ; iter
!= children
.end() ; ++iter
)
93 if (obj
->isAncestorOf(*iter
)) {
96 new_children
.insert(new_children
.end(),
97 children
.begin(), iter
);
99 *descendants
++ = *iter
;
100 } else if (found_one
) {
101 new_children
.push_back(*iter
);
105 children
.swap(new_children
);
109 unsigned removeChild(SPObject
*obj
) {
110 Siblings::iterator found
;
111 found
= std::find(children
.begin(), children
.end(), obj
);
112 unsigned index
= found
- children
.begin();
113 if ( found
!= children
.end() ) {
114 children
.erase(found
);
120 typedef std::map
<SPObject
*, Record
> Map
;
123 sigc::signal
<void ()> changed_signal
;
124 sigc::signal
<void (SPObject
*)> added_signal
;
125 sigc::signal
<void (SPObject
*)> removed_signal
;
127 Relations() { records
[nullptr]; }
130 for (auto & iter
: records
)
133 sp_object_unref(iter
.first
);
134 Record
&record
=iter
.second
;
135 record
.release_connection
.disconnect();
136 record
.position_changed_connection
.disconnect();
141 Record
*get(SPObject
*obj
) {
142 auto found
=records
.find(obj
);
143 if ( found
!= records
.end() ) {
144 return &found
->second
;
150 void addOne(SPObject
*obj
);
151 void remove(SPObject
*obj
, bool subtree
);
152 void reorder(SPObject
*obj
);
156 Record
&_doAdd(SPObject
*obj
) {
158 Record
&record
=records
[obj
];
159 record
.release_connection
160 = obj
->connectRelease(
161 sigc::mem_fun(*this, &Relations::_release_object
)
163 record
.position_changed_connection
164 = obj
->connectPositionChanged(
165 sigc::mem_fun(*this, &Relations::reorder
)
170 void _notifyAdded(SPObject
*obj
) {
171 added_signal
.emit(obj
);
174 void _doRemove(SPObject
*obj
) {
175 Record
&record
=records
[obj
];
177 if ( record
.parent
== nullptr ) {
178 Record
&root
= records
[nullptr];
179 for ( Siblings::iterator it
= root
.children
.begin(); it
!= root
.children
.end(); ++it
) {
181 root
.children
.erase( it
);
187 record
.release_connection
.disconnect();
188 record
.position_changed_connection
.disconnect();
190 removed_signal
.emit(obj
);
191 sp_object_unref(obj
);
194 void _doRemoveSubtree(SPObject
*obj
) {
195 Record
*record
=get(obj
);
197 Siblings
&children
=record
->children
;
198 for (auto & iter
: children
)
200 _doRemoveSubtree(iter
);
206 void _release_object(SPObject
*obj
) {
213 DocumentSubset::DocumentSubset()
214 : _relations(std::make_unique
<DocumentSubset::Relations
>())
218 DocumentSubset::~DocumentSubset() = default;
221 void DocumentSubset::Relations::addOne(SPObject
*obj
) {
222 g_return_if_fail( obj
!= nullptr );
223 g_return_if_fail( get(obj
) == nullptr );
225 Record
&record
=_doAdd(obj
);
227 /* find the nearest ancestor in the subset */
228 Record
*parent_record
=nullptr;
229 for ( SPObject::ParentIterator parent_iter
=obj
->parent
230 ; !parent_record
&& parent_iter
; ++parent_iter
)
232 parent_record
= get(parent_iter
);
234 record
.parent
= parent_iter
;
237 if (!parent_record
) {
238 parent_record
= get(nullptr);
239 g_assert( parent_record
!= nullptr );
242 Siblings
&children
=record
.children
;
244 /* reparent descendants of obj to obj */
245 parent_record
->extractDescendants(
246 std::back_insert_iterator
<Siblings
>(children
),
249 for (auto & iter
: children
)
251 Record
*child_record
=get(iter
);
252 g_assert( child_record
!= nullptr );
253 child_record
->parent
= obj
;
256 /* add obj to the child list */
257 parent_record
->addChild(obj
);
260 changed_signal
.emit();
263 void DocumentSubset::Relations::remove(SPObject
*obj
, bool subtree
) {
264 g_return_if_fail( obj
!= nullptr );
266 Record
*record
=get(obj
);
267 g_return_if_fail( record
!= nullptr );
269 Record
*parent_record
=get(record
->parent
);
270 g_assert( parent_record
!= nullptr );
272 unsigned index
=parent_record
->removeChild(obj
);
275 _doRemoveSubtree(obj
);
277 /* reparent obj's orphaned children to their grandparent */
278 Siblings
&siblings
=parent_record
->children
;
279 Siblings
&children
=record
->children
;
280 siblings
.insert(siblings
.begin()+index
,
281 children
.begin(), children
.end());
283 for (auto & iter
: children
)
285 Record
*child_record
=get(iter
);
286 g_assert( child_record
!= nullptr );
287 child_record
->parent
= record
->parent
;
290 /* remove obj's record */
294 changed_signal
.emit();
297 void DocumentSubset::Relations::clear() {
298 Record
&root
=records
[nullptr];
300 while (!root
.children
.empty()) {
301 _doRemoveSubtree(root
.children
.front());
304 changed_signal
.emit();
307 void DocumentSubset::Relations::reorder(SPObject
*obj
) {
308 SPObject::ParentIterator parent
=obj
;
310 /* find nearest ancestor in the subset */
311 Record
*parent_record
=nullptr;
312 while (!parent_record
) {
313 parent_record
= get(++parent
);
317 /* move the object if it's in the subset */
318 parent_record
->removeChild(obj
);
319 parent_record
->addChild(obj
);
320 changed_signal
.emit();
322 /* otherwise, move any top-level descendants */
323 Siblings descendants
;
324 parent_record
->extractDescendants(
325 std::back_insert_iterator
<Siblings
>(descendants
),
328 if (!descendants
.empty()) {
329 unsigned index
=parent_record
->findInsertIndex(obj
);
330 Siblings
&family
=parent_record
->children
;
331 family
.insert(family
.begin()+index
,
332 descendants
.begin(), descendants
.end());
333 changed_signal
.emit();
338 void DocumentSubset::_addOne(SPObject
*obj
) {
339 _relations
->addOne(obj
);
342 void DocumentSubset::_remove(SPObject
*obj
, bool subtree
) {
343 _relations
->remove(obj
, subtree
);
346 void DocumentSubset::_clear() {
350 bool DocumentSubset::includes(SPObject
*obj
) const {
351 return _relations
->get(obj
);
354 SPObject
*DocumentSubset::parentOf(SPObject
*obj
) const {
355 auto const record
= _relations
->get(obj
);
356 return ( record
? record
->parent
: nullptr );
359 unsigned DocumentSubset::childCount(SPObject
*obj
) const {
360 auto const record
= _relations
->get(obj
);
361 return ( record
? record
->children
.size() : 0 );
364 unsigned DocumentSubset::indexOf(SPObject
*obj
) const {
365 SPObject
*parent
=parentOf(obj
);
366 auto const record
= _relations
->get(parent
);
367 return ( record
? record
->childIndex(obj
) : 0 );
370 SPObject
*DocumentSubset::nthChildOf(SPObject
*obj
, unsigned n
) const {
371 auto const record
= _relations
->get(obj
);
372 return ( record
? record
->children
[n
] : nullptr );
375 sigc::connection
DocumentSubset::connectChanged(sigc::slot
<void ()> slot
) const {
376 return _relations
->changed_signal
.connect(slot
);
380 DocumentSubset::connectAdded(sigc::slot
<void (SPObject
*)> slot
) const {
381 return _relations
->added_signal
.connect(slot
);
385 DocumentSubset::connectRemoved(sigc::slot
<void (SPObject
*)> slot
) const {
386 return _relations
->removed_signal
.connect(slot
);
394 c-file-style:"stroustrup"
395 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
400 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :