1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2
3 #include "smwidget.hh"
4 #include "smleakdebugger.hh"
5 #include "smwindow.hh"
6 #include "smscrollview.hh"
7 #include "smeventloop.hh"
8 #include "smtimer.hh"
9 #include <glib.h>
10
11 using namespace SpectMorph;
12
13 using std::string;
14 using std::max;
15 using std::min;
16
17 static LeakDebugger leak_debugger ("SpectMorph::Widget");
18
Widget(Widget * parent,double x,double y,double width,double height)19 Widget::Widget (Widget *parent, double x, double y, double width, double height) :
20 parent (parent), m_x (x), m_y (y), m_width (width), m_height (height)
21 {
22 leak_debugger.add (this);
23
24 if (parent)
25 parent->children.push_back (this);
26
27 // ensure that newly created widget is drawn, too
28 // (we don't want that in every constructor)
29 update_full();
30 }
31
~Widget()32 Widget::~Widget()
33 {
34 // ensure that deleted widget is no longer visible, too
35 // (we don't want that in every destructor)
36 update();
37
38 while (!children.empty())
39 {
40 delete children.front();
41 }
42
43 while (!timers.empty())
44 delete timers.front();
45
46 Window *win = window();
47 if (win)
48 win->on_widget_deleted (this);
49
50 if (parent)
51 parent->remove_child (this);
52 leak_debugger.del (this);
53 }
54
55 void
remove_child(Widget * child)56 Widget::remove_child (Widget *child)
57 {
58 for (std::vector<Widget *>::iterator ci = children.begin(); ci != children.end(); ci++)
59 if (*ci == child)
60 {
61 children.erase (ci);
62 return;
63 }
64 g_assert_not_reached();
65 }
66
67 void
set_x(double x)68 Widget::set_x (double x)
69 {
70 if (x != m_x)
71 {
72 m_x = x;
73 signal_x_changed();
74 }
75 }
76
77 void
set_y(double y)78 Widget::set_y (double y)
79 {
80 if (y != m_y)
81 {
82 m_y = y;
83 signal_y_changed();
84 }
85 }
86
87 void
set_width(double width)88 Widget::set_width (double width)
89 {
90 if (width != m_width)
91 {
92 m_width = width;
93 signal_width_changed();
94 }
95 }
96
97 void
set_height(double height)98 Widget::set_height (double height)
99 {
100 if (height != m_height)
101 {
102 m_height = height;
103 signal_height_changed();
104 }
105 }
106
107
108 /* map relative to absolute coordinates */
109 double
abs_x() const110 Widget::abs_x() const
111 {
112 if (!parent)
113 return x();
114 else
115 return parent->abs_x() + x();
116 }
117
118 double
abs_y() const119 Widget::abs_y() const
120 {
121 if (!parent)
122 return y();
123 else
124 return parent->abs_y() + y();
125 }
126
127 Rect
abs_visible_rect()128 Widget::abs_visible_rect()
129 {
130 ScrollView *sview = scroll_view();
131
132 Rect visible_rect (abs_x(), abs_y(), width(), height());
133
134 if (sview && sview->is_scroll_child (this))
135 {
136 return visible_rect.intersection (sview->child_rect());
137 }
138 else
139 {
140 return visible_rect;
141 }
142 }
143
144 void
draw(const DrawEvent & devent)145 Widget::draw (const DrawEvent& devent)
146 {
147 cairo_t *cr = devent.cr;
148
149 if (m_background_color)
150 {
151 DrawUtils du (cr);
152 du.set_color (m_background_color);
153 cairo_rectangle (cr, 0, 0, width(), height());
154 cairo_fill (cr);
155 }
156 }
157
158 void
update()159 Widget::update()
160 {
161 Window *win = window();
162
163 if (win)
164 win->need_update (this);
165 }
166
167 void
update_with_children()168 Widget::update_with_children()
169 {
170 // recursively update this widget and all children
171 update();
172
173 for (auto c : children)
174 c->update_with_children();
175 }
176
177 void
update_full()178 Widget::update_full()
179 {
180 Window *win = window();
181
182 if (win)
183 win->need_update (nullptr);
184 }
185
186 void
update(double x,double y,double width,double height)187 Widget::update (double x, double y, double width, double height)
188 {
189 Window *win = window();
190
191 if (win)
192 {
193 const Rect r (x, y, width, height);
194
195 win->need_update (this, &r);
196 }
197 }
198
199 void
delete_later()200 Widget::delete_later()
201 {
202 Window *win = window();
203 if (win)
204 win->event_loop()->add_delete_later (this);
205 }
206
207 void
add_timer(Timer * timer)208 Widget::add_timer (Timer *timer)
209 {
210 timers.push_back (timer);
211 }
212
213 void
remove_timer(Timer * timer)214 Widget::remove_timer (Timer *timer)
215 {
216 timers.erase (std::remove (timers.begin(), timers.end(), timer), timers.end());
217 }
218
219 /* Color conversion from Rapicorn */
220 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
221 void
get_hsv(double * huep,double * saturationp,double * valuep)222 Color::get_hsv (double *huep, /* 0..360: 0=red, 120=green, 240=blue */
223 double *saturationp, /* 0..1 */
224 double *valuep) /* 0..1 */
225 {
226 double r = red(), g = green(), b = blue();
227 double value = MAX (MAX (r, g), b);
228 double delta = value - MIN (MIN (r, g), b);
229 double saturation = value == 0 ? 0 : delta / value;
230 double hue = 0;
231 if (saturation && huep)
232 {
233 if (r == value)
234 {
235 hue = 0 + 60 * (g - b) / delta;
236 if (hue <= 0)
237 hue += 360;
238 }
239 else if (g == value)
240 hue = 120 + 60 * (b - r) / delta;
241 else /* b == value */
242 hue = 240 + 60 * (r - g) / delta;
243 }
244 if (huep)
245 *huep = hue;
246 if (saturationp)
247 *saturationp = saturation;
248 if (valuep)
249 *valuep = value;
250 }
251
252 void
set_hsv(double hue,double saturation,double value)253 Color::set_hsv (double hue, /* 0..360: 0=red, 120=green, 240=blue */
254 double saturation, /* 0..1 */
255 double value) /* 0..1 */
256 {
257 uint center = int (hue / 60);
258 double frac = hue / 60 - center;
259 double v1s = value * (1 - saturation);
260 double vsf = value * (1 - saturation * frac);
261 double v1f = value * (1 - saturation * (1 - frac));
262 switch (center)
263 {
264 case 6:
265 case 0: /* red */
266 set_rgb (value, v1f, v1s);
267 break;
268 case 1: /* red + green */
269 set_rgb (vsf, value, v1s);
270 break;
271 case 2: /* green */
272 set_rgb (v1s, value, v1f);
273 break;
274 case 3: /* green + blue */
275 set_rgb (v1s, vsf, value);
276 break;
277 case 4: /* blue */
278 set_rgb (v1f, v1s, value);
279 break;
280 case 5: /* blue + red */
281 set_rgb (value, v1s, vsf);
282 break;
283 }
284 }
285
286 Color
lighter(double factor)287 Color::lighter (double factor)
288 {
289 if (!m_valid)
290 return Color::null();
291
292 double h, s, v;
293 get_hsv (&h, &s, &v);
294
295 v = factor * v / 100;
296 if (v > 1)
297 {
298 // overflow: adjust saturation
299 s = max (s - (v - 1), 0.0);
300 v = 1;
301 }
302
303 Color color;
304 color.set_hsv (h, s, v);
305 return color;
306 }
307
308 Color
darker(double factor)309 Color::darker (double factor)
310 {
311 if (!m_valid)
312 return Color::null();
313
314 double h, s, v;
315 get_hsv (&h, &s, &v);
316
317 Color color;
318 color.set_hsv (h, s, v * 100 / factor);
319 return color;
320 }
321