d49e597567498fddf291048614e222890adb4661
[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 /* Color conversion from Rapicorn */
154 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
155 void
156 Color::get_hsv (double *huep,           /* 0..360: 0=red, 120=green, 240=blue */
157                 double *saturationp,    /* 0..1 */
158                 double *valuep)         /* 0..1 */
159 {
160   double r = red(), g = green(), b = blue();
161   double value = MAX (MAX (r, g), b);
162   double delta = value - MIN (MIN (r, g), b);
163   double saturation = value == 0 ? 0 : delta / value;
164   double hue = 0;
165   if (saturation && huep)
166     {
167       if (r == value)
168         {
169           hue = 0 + 60 * (g - b) / delta;
170           if (hue <= 0)
171             hue += 360;
172         }
173       else if (g == value)
174         hue = 120 + 60 * (b - r) / delta;
175       else /* b == value */
176         hue = 240 + 60 * (r - g) / delta;
177     }
178   if (huep)
179     *huep = hue;
180   if (saturationp)
181     *saturationp = saturation;
182   if (valuep)
183     *valuep = value;
184 }
185
186 void
187 Color::set_hsv (double hue,             /* 0..360: 0=red, 120=green, 240=blue */
188                 double saturation,      /* 0..1 */
189                 double value)           /* 0..1 */
190 {
191   uint center = int (hue / 60);
192   double frac = hue / 60 - center;
193   double v1s = value * (1 - saturation);
194   double vsf = value * (1 - saturation * frac);
195   double v1f = value * (1 - saturation * (1 - frac));
196   switch (center)
197     {
198     case 6:
199     case 0: /* red */
200       set_rgb (value, v1f, v1s);
201       break;
202     case 1: /* red + green */
203       set_rgb (vsf, value, v1s);
204       break;
205     case 2: /* green */
206       set_rgb (v1s, value, v1f);
207       break;
208     case 3: /* green + blue */
209       set_rgb (v1s, vsf, value);
210       break;
211     case 4: /* blue */
212       set_rgb (v1f, v1s, value);
213       break;
214     case 5: /* blue + red */
215       set_rgb (value, v1s, vsf);
216       break;
217     }
218 }
219
220 Color
221 Color::lighter (double factor)
222 {
223   if (!m_valid)
224     return Color::null();
225
226   double h, s, v;
227   get_hsv (&h, &s, &v);
228
229   v = factor * v / 100;
230   if (v > 1)
231     {
232       // overflow: adjust saturation
233       s = max (s - (v - 1), 0.0);
234       v = 1;
235     }
236
237   Color color;
238   color.set_hsv (h, s, v);
239   return color;
240 }
241
242 Color
243 Color::darker (double factor)
244 {
245   if (!m_valid)
246     return Color::null();
247
248   double h, s, v;
249   get_hsv (&h, &s, &v);
250
251   Color color;
252   color.set_hsv (h, s, v * 100 / factor);
253   return color;
254 }