GLUI: add Widget::delete_later(), fixes ParamLabel related crash
[spectmorph.git] / glui / smwidget.cc
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 <glib.h>
8
9 using namespace SpectMorph;
10
11 using std::string;
12 using std::max;
13 using std::min;
14
15 static LeakDebugger leak_debugger ("SpectMorph::Widget");
16
17 Widget::Widget (Widget *parent, double x, double y, double width, double height) :
18   parent (parent), x (x), y (y), width (width), height (height)
19 {
20   leak_debugger.add (this);
21
22   if (parent)
23     parent->children.push_back (this);
24
25   // ensure that newly created widget is drawn, too
26   // (we don't want that in every constructor)
27   update_full();
28 }
29
30 Widget::~Widget()
31 {
32   // ensure that deleted widget is no longer visible, too
33   // (we don't want that in every destructor)
34   update();
35
36   while (!children.empty())
37     {
38       delete children.front();
39     }
40
41   Window *win = window();
42   if (win)
43     win->on_widget_deleted (this);
44
45   if (parent)
46     parent->remove_child (this);
47   leak_debugger.del (this);
48 }
49
50 void
51 Widget::remove_child (Widget *child)
52 {
53   for (std::vector<Widget *>::iterator ci = children.begin(); ci != children.end(); ci++)
54     if (*ci == child)
55       {
56         children.erase (ci);
57         return;
58       }
59   g_assert_not_reached();
60 }
61
62 /* map relative to absolute coordinates */
63 double
64 Widget::abs_x() const
65 {
66   if (!parent)
67     return x;
68   else
69     return parent->abs_x() + x;
70 }
71
72 double
73 Widget::abs_y() const
74 {
75   if (!parent)
76     return y;
77   else
78     return parent->abs_y() + y;
79 }
80
81 Rect
82 Widget::abs_visible_rect()
83 {
84   ScrollView *sview = scroll_view();
85
86   Rect visible_rect (abs_x(), abs_y(), width, height);
87
88   if (sview && sview->is_scroll_child (this))
89     {
90       return visible_rect.intersection (sview->child_rect());
91     }
92   else
93     {
94       return visible_rect;
95     }
96 }
97
98 void
99 Widget::draw (const DrawEvent& devent)
100 {
101   cairo_t *cr = devent.cr;
102
103   if (m_background_color)
104     {
105       DrawUtils du (cr);
106       du.set_color (m_background_color);
107       cairo_rectangle (cr, 0, 0, width, height);
108       cairo_fill (cr);
109     }
110 }
111
112 void
113 Widget::update()
114 {
115   Window *win = window();
116
117   if (win)
118     win->need_update (this);
119 }
120
121 void
122 Widget::update_with_children()
123 {
124   // recursively update this widget and all children
125   update();
126
127   for (auto c : children)
128     c->update_with_children();
129 }
130
131 void
132 Widget::update_full()
133 {
134   Window *win = window();
135
136   if (win)
137     win->need_update (nullptr);
138 }
139
140 void
141 Widget::update (double x, double y, double width, double height)
142 {
143   Window *win = window();
144
145   if (win)
146     {
147       const Rect r (x, y, width, height);
148
149       win->need_update (this, &r);
150     }
151 }
152
153 void
154 Widget::delete_later()
155 {
156   Window *win = window();
157   if (win)
158     win->add_delete_later (this);
159 }
160
161 /* Color conversion from Rapicorn */
162 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
163 void
164 Color::get_hsv (double *huep,           /* 0..360: 0=red, 120=green, 240=blue */
165                 double *saturationp,    /* 0..1 */
166                 double *valuep)         /* 0..1 */
167 {
168   double r = red(), g = green(), b = blue();
169   double value = MAX (MAX (r, g), b);
170   double delta = value - MIN (MIN (r, g), b);
171   double saturation = value == 0 ? 0 : delta / value;
172   double hue = 0;
173   if (saturation && huep)
174     {
175       if (r == value)
176         {
177           hue = 0 + 60 * (g - b) / delta;
178           if (hue <= 0)
179             hue += 360;
180         }
181       else if (g == value)
182         hue = 120 + 60 * (b - r) / delta;
183       else /* b == value */
184         hue = 240 + 60 * (r - g) / delta;
185     }
186   if (huep)
187     *huep = hue;
188   if (saturationp)
189     *saturationp = saturation;
190   if (valuep)
191     *valuep = value;
192 }
193
194 void
195 Color::set_hsv (double hue,             /* 0..360: 0=red, 120=green, 240=blue */
196                 double saturation,      /* 0..1 */
197                 double value)           /* 0..1 */
198 {
199   uint center = int (hue / 60);
200   double frac = hue / 60 - center;
201   double v1s = value * (1 - saturation);
202   double vsf = value * (1 - saturation * frac);
203   double v1f = value * (1 - saturation * (1 - frac));
204   switch (center)
205     {
206     case 6:
207     case 0: /* red */
208       set_rgb (value, v1f, v1s);
209       break;
210     case 1: /* red + green */
211       set_rgb (vsf, value, v1s);
212       break;
213     case 2: /* green */
214       set_rgb (v1s, value, v1f);
215       break;
216     case 3: /* green + blue */
217       set_rgb (v1s, vsf, value);
218       break;
219     case 4: /* blue */
220       set_rgb (v1f, v1s, value);
221       break;
222     case 5: /* blue + red */
223       set_rgb (value, v1s, vsf);
224       break;
225     }
226 }
227
228 Color
229 Color::lighter (double factor)
230 {
231   if (!m_valid)
232     return Color::null();
233
234   double h, s, v;
235   get_hsv (&h, &s, &v);
236
237   v = factor * v / 100;
238   if (v > 1)
239     {
240       // overflow: adjust saturation
241       s = max (s - (v - 1), 0.0);
242       v = 1;
243     }
244
245   Color color;
246   color.set_hsv (h, s, v);
247   return color;
248 }
249
250 Color
251 Color::darker (double factor)
252 {
253   if (!m_valid)
254     return Color::null();
255
256   double h, s, v;
257   get_hsv (&h, &s, &v);
258
259   Color color;
260   color.set_hsv (h, s, v * 100 / factor);
261   return color;
262 }