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