1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /**
3  * A CanvasItem that contains other CanvasItem's.
4  */
5 
6 /*
7  * Author:
8  *   Tavmjong Bah
9  *
10  * Copyright (C) 2020 Tavmjong Bah
11  *
12  * Rewrite of SPCanvasGroup
13  *
14  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15  */
16 
17 #include "canvas-item-group.h"
18 #include "canvas-item-ctrl.h"  // Update sizes
19 
20 namespace Inkscape {
21 
CanvasItemGroup(CanvasItemGroup * group)22 CanvasItemGroup::CanvasItemGroup(CanvasItemGroup *group)
23     : CanvasItem(group)
24 {
25     _name = "CanvasItemGroup";
26     _pickable = true; // For now all groups are pickable... look into turning this off for some groups (e.g. temp).
27 }
28 
~CanvasItemGroup()29 CanvasItemGroup::~CanvasItemGroup()
30 {
31     while (!items.empty()) {
32         CanvasItem & item = items.front();
33         remove(&item);
34     }
35 
36     if (_parent) {
37         _parent->remove(this, false); // remove() should not delete this or we'll double delete!
38     }
39 }
40 
add(CanvasItem * item)41 void CanvasItemGroup::add(CanvasItem *item)
42 {
43 #ifdef CANVAS_ITEM_DEBUG
44     std::cout << "CanvasItemGroup::add: " << item->get_name() << " to " << _name << " " << items.size() << std::endl;
45 #endif
46     items.push_back(*item);
47     // canvas request update
48 }
49 
remove(CanvasItem * item,bool Delete)50 void CanvasItemGroup::remove(CanvasItem *item, bool Delete)
51 {
52 #ifdef CANVAS_ITEM_DEBUG
53     std::cout << "CanvasItemGroup::remove: " << item->get_name() << " from " << _name << " " << items.size() << std::endl;
54 #endif
55     auto position = items.iterator_to(*item);
56     if (position != items.end()) {
57         position->set_parent(nullptr);
58         items.erase(position);
59         if (Delete) {
60             delete (&*position);  // An item directly deleted should not be deleted here.
61         }
62     }
63 }
64 
update(Geom::Affine const & affine)65 void CanvasItemGroup::update(Geom::Affine const &affine)
66 {
67     if (_affine == affine && !_need_update) {
68         // Nothing to do.
69         return;
70     }
71 
72     _affine = affine;
73     _need_update = false;
74 
75     _bounds = Geom::Rect();  // Zero
76 
77     // Update all children and calculate new bounds.
78     for (auto & item : items) {
79         item.update(_affine);
80         _bounds.unionWith(item.get_bounds());
81     }
82 }
83 
render(Inkscape::CanvasItemBuffer * buf)84 void CanvasItemGroup::render(Inkscape::CanvasItemBuffer *buf)
85 {
86     if (_visible) {
87         if (_bounds.interiorIntersects(buf->rect)) {
88             for (auto & item : items) {
89                 item.render(buf);
90             }
91          }
92     }
93 }
94 
95 // Return last visible and pickable item that contains point.
96 // SPCanvasGroup returned distance but it was not used.
pick_item(Geom::Point & p)97 CanvasItem* CanvasItemGroup::pick_item(Geom::Point& p)
98 {
99 #ifdef CANVAS_ITEM_DEBUG
100     std::cout << "CanvasItemGroup::pick_item:" << std::endl;
101     std::cout << "  PICKING: In group: " << _name << "  bounds: " << _bounds << std::endl;
102 #endif
103     for (auto item = items.rbegin(); item != items.rend(); ++item) { // C++20 will allow us to loop in reverse.
104 #ifdef CANVAS_ITEM_DEBUG
105         std::cout << "    PICKING: Checking: " << item->get_name() << "  bounds: " << item->get_bounds() << std::endl;
106 #endif
107         CanvasItem* picked_item = nullptr;
108         if (item->is_visible()   &&
109             item->is_pickable()  &&
110             item->contains(p)    ) {
111 
112             auto group = dynamic_cast<CanvasItemGroup *>(&*item);
113             if (group) {
114                 picked_item = group->pick_item(p);
115             } else {
116                 picked_item = &*item;
117             }
118         }
119 
120         if (picked_item != nullptr) {
121 #ifdef CANVAS_ITEM_DEBUG
122             std::cout << "  PICKING: pick_item: " << picked_item->get_name() << std::endl;
123 #endif
124             return picked_item;
125         }
126     }
127 
128     return nullptr;
129 }
130 
update_canvas_item_ctrl_sizes(int size_index)131 void CanvasItemGroup::update_canvas_item_ctrl_sizes(int size_index)
132 {
133     for (auto & item : items) {
134         auto ctrl = dynamic_cast<CanvasItemCtrl *>(&item);
135         if (ctrl) {
136             // We can't use set_size_default as the preference file is updated ->after<- the signal is emitted!
137             ctrl->set_size_via_index(size_index);
138         }
139         auto group = dynamic_cast<CanvasItemGroup *>(&item);
140         if (group) {
141             group->update_canvas_item_ctrl_sizes(size_index);
142         }
143     }
144 }
145 
146 } // Namespace Inkscape
147 /*
148   Local Variables:
149   mode:c++
150   c-file-style:"stroustrup"
151   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
152   indent-tabs-mode:nil
153   fill-column:99
154   End:
155 */
156 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
157