prefer bool operator
[ardour.git] / libs / canvas / box.cc
1 /*
2     Copyright (C) 2011-2014 Paul Davis
3     Author: Carl Hetherington <cth@carlh.net>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <algorithm>
21
22 #include "canvas/box.h"
23 #include "canvas/rectangle.h"
24
25 using namespace ArdourCanvas;
26
27 Box::Box (Canvas* canvas, Orientation o)
28         : Item (canvas)
29         , orientation (o)
30         , spacing (0)
31         , top_padding (0), right_padding (0), bottom_padding (0), left_padding (0)
32         , top_margin (0), right_margin (0), bottom_margin (0), left_margin (0)
33         , homogenous (false)
34 {
35         self = new Rectangle (this);
36         self->set_outline (false);
37         self->set_fill (false);
38 }
39
40 Box::Box (Item* parent, Orientation o)
41         : Item (parent)
42         , orientation (o)
43         , spacing (0)
44         , top_padding (0), right_padding (0), bottom_padding (0), left_padding (0)
45         , top_margin (0), right_margin (0), bottom_margin (0), left_margin (0)
46         , homogenous (false)
47 {
48         self = new Rectangle (this);
49         self->set_outline (false);
50         self->set_fill (false);
51 }
52
53
54 Box::Box (Item* parent, Duple const & p, Orientation o)
55         : Item (parent, p)
56         , orientation (o)
57         , spacing (0)
58         , top_padding (0), right_padding (0), bottom_padding (0), left_padding (0)
59         , top_margin (0), right_margin (0), bottom_margin (0), left_margin (0)
60         , homogenous (false)
61 {
62         self = new Rectangle (this);
63         self->set_outline (false);
64         self->set_fill (false);
65 }
66
67 void
68 Box::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
69 {
70         Item::render_children (area, context);
71 }
72
73 void
74 Box::compute_bounding_box () const
75 {
76         _bounding_box = boost::none;
77
78         if (_items.empty()) {
79                 _bounding_box_dirty = false;
80                 return;
81         }
82
83         add_child_bounding_boxes (!collapse_on_hide);
84
85         if (_bounding_box) {
86                 Rect r = _bounding_box.get();
87
88                 _bounding_box = r.expand (top_padding + outline_width() + top_margin,
89                                           right_padding + outline_width() + right_margin,
90                                           bottom_padding + outline_width() + bottom_margin,
91                                           left_padding + outline_width() + left_margin);
92         }
93
94         _bounding_box_dirty = false;
95 }
96
97 void
98 Box::set_spacing (double s)
99 {
100         spacing = s;
101 }
102
103 void
104 Box::set_padding (double t, double r, double b, double l)
105 {
106         double last = t;
107
108         top_padding = t;
109
110         if (r >= 0) {
111                 last = r;
112         }
113         right_padding = last;
114         if (b >= 0) {
115                 last = b;
116         }
117         bottom_padding = last;
118         if (l >= 0) {
119                 last = l;
120         }
121         left_padding = last;
122 }
123
124 void
125 Box::set_margin (double t, double r, double b, double l)
126 {
127         double last = t;
128         top_margin = t;
129         if (r >= 0) {
130                 last = r;
131         }
132         right_margin = last;
133         if (b >= 0) {
134                 last = b;
135         }
136         bottom_margin = last;
137         if (l >= 0) {
138                 last = l;
139         }
140         left_margin = last;
141 }
142
143 void
144 Box::reset_self ()
145 {
146         if (_bounding_box_dirty) {
147                 compute_bounding_box ();
148         }
149
150         if (!_bounding_box) {
151                 self->hide ();
152                 return;
153         }
154
155         Rect r (_bounding_box.get());
156
157         /* XXX need to shrink by margin */
158
159         self->set (r);
160 }
161
162 void
163 Box::reposition_children ()
164 {
165         Duple previous_edge (0, 0);
166         Distance largest_width = 0;
167         Distance largest_height = 0;
168
169         if (homogenous) {
170
171                 for (std::list<Item*>::iterator i = _items.begin(); ++i != _items.end(); ++i) {
172                         boost::optional<Rect> bb = (*i)->bounding_box();
173                         if (bb) {
174                                 largest_height = std::max (largest_height, bb.get().height());
175                                 largest_width = std::max (largest_width, bb.get().width());
176                         }
177                 }
178         }
179
180         for (std::list<Item*>::iterator i = _items.begin(); ++i != _items.end(); ++i) {
181
182                 (*i)->set_position (previous_edge);
183
184                 if (orientation == Vertical) {
185
186                         Distance shift = 0;
187
188                         if (homogenous) {
189                                 shift = largest_height;
190                         } else {
191                                 boost::optional<Rect> bb = (*i)->bounding_box();
192
193                                 if (!(*i)->visible()) {
194                                         /* invisible child */
195                                         if (!collapse_on_hide) {
196                                                 /* still add in its size */
197                                                 if (bb) {
198                                                         shift += bb.get().height();
199                                                 }
200                                         }
201                                 } else {
202                                         if (bb) {
203                                                 shift += bb.get().height();
204                                         }
205                                 }
206                         }
207
208                         previous_edge = previous_edge.translate (Duple (0, spacing + shift));
209
210                 } else {
211
212                         Distance shift = 0;
213
214                         if (homogenous) {
215                                 shift = largest_width;
216                         } else {
217                                 boost::optional<Rect> bb = (*i)->bounding_box();
218
219                                 if (!(*i)->visible()) {
220                                         if (!collapse_on_hide) {
221                                                 if (bb) {
222                                                         shift += bb.get().width();
223                                                 }
224                                         }
225                                 } else {
226                                         if (bb) {
227                                                 shift += bb.get().width();
228                                         }
229                                 }
230                         }
231
232                         previous_edge = previous_edge.translate (Duple (spacing + shift, 0));
233                 }
234         }
235
236         _bounding_box_dirty = true;
237         reset_self ();
238 }
239
240 void
241 Box::pack_end (Item* i, double extra_padding)
242 {
243         if (!i) {
244                 return;
245         }
246
247         /* prepend new child */
248         Item::add_front (i);
249         reposition_children ();
250 }
251 void
252 Box::pack_start (Item* i, double extra_padding)
253 {
254         if (!i) {
255                 return;
256         }
257
258         /* append new child */
259         Item::add (i);
260         reposition_children ();
261 }
262
263 void
264 Box::add (Item* i)
265 {
266         pack_start (i);
267 }
268
269 void
270 Box::child_changed ()
271 {
272         /* catch visibility and size changes */
273
274         Item::child_changed ();
275         reposition_children ();
276 }
277
278 void
279 Box::set_collapse_on_hide (bool yn)
280 {
281         if (collapse_on_hide != yn) {
282                 collapse_on_hide = yn;
283                 reposition_children ();
284         }
285 }
286
287 /*----*/
288
289 VBox::VBox (Canvas* c)
290         : Box (c, Vertical)
291 {
292 }
293 VBox::VBox (Item* i)
294         : Box (i, Vertical)
295 {
296 }
297 VBox::VBox (Item* i, Duple const & position)
298         : Box (i, position, Vertical)
299 {
300 }
301
302 HBox::HBox (Canvas* c)
303         : Box (c, Horizontal)
304 {
305 }
306 HBox::HBox (Item* i)
307         : Box (i, Horizontal)
308 {
309 }
310 HBox::HBox (Item* i, Duple const & position)
311         : Box (i, position, Horizontal)
312 {
313 }