1 /* giwled.c  -  GiwLed widget's source
2   Copyright (C) 2006  Alexandre Pereira Bueno, Eduardo Parente Ribeiro
3 
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Lesser General Public
6     License as published by the Free Software Foundation; either
7     version 2.1 of the License, or (at your option) any later version.
8 
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Lesser General Public License for more details.
13 
14     You should have received a copy of the GNU Lesser General Public
15     License along with this library; if not, write to the Free Software
16     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 
18   Maintainers
19   Alexandre Pereira Bueno - alpebu@yahoo.com.br
20   James Scott Jr <skoona@users.sourceforge.net>
21 */
22 
23 // additional code G. Finch (salsaman@gmail.com) 2010 - 2013
24 
25 #include <math.h>
26 #include <stdio.h>
27 
28 #include "main.h"
29 
30 #include "giw/giwled.h"
31 
32 #define LED_DEFAULT_SIZE 14
33 
34 enum {
35   MODE_CHANGED_SIGNAL,
36   LAST_SIGNAL
37 };
38 
39 /* Forward declarations */
40 
41 static void giw_led_class_init(GiwLedClass    *klass);
42 static void giw_led_init(GiwLed         *led);
43 static void giw_led_realize(GtkWidget        *widget);
44 static void giw_led_size_allocate(GtkWidget     *widget,
45                                   GtkAllocation *allocation);
46 static gint giw_led_button_press(GtkWidget        *widget,
47                                  GdkEventButton   *event);
48 static void giw_led_size_request(GtkWidget      *widget,
49                                  GtkRequisition *requisition);
50 #if GTK_CHECK_VERSION(3, 0, 0)
51 static void giw_led_dispose(GObject        *object);
52 static void giw_led_get_preferred_width(GtkWidget *widget,
53                                         gint      *minimal_width,
54                                         gint      *natural_width);
55 static void giw_led_get_preferred_height(GtkWidget *widget,
56     gint      *minimal_height,
57     gint      *natural_height);
58 static gboolean giw_led_draw(GtkWidget *widget, cairo_t *cairo);
59 static void     giw_led_style_updated(GtkWidget      *widget);
60 static void giw_led_state_flags_changed(GtkWidget        *widget,
61                                         GtkStateFlags     previous_state);
62 #else
63 static gint giw_led_expose(GtkWidget        *widget,
64                            GdkEventExpose   *event);
65 static void giw_led_destroy(GtkObject        *object);
66 #endif
67 
68 /* Local data */
69 
70 static guint giw_led_signals[LAST_SIGNAL] = { 0 };
71 
72 
73 #if GTK_CHECK_VERSION(3, 0, 0)
G_DEFINE_TYPE(GiwLed,giw_led,GTK_TYPE_WIDGET)74 G_DEFINE_TYPE(GiwLed, giw_led, GTK_TYPE_WIDGET)
75 #define parent_class giw_led_parent_class
76 #else
77 static GtkWidgetClass *parent_class = NULL;
78 
79 
80 /*********************
81   Widget's Functions
82 *********************/
83 
84 GType giw_led_get_type() {
85   static GType led_type = 0;
86 
87   if (!led_type) {
88     static const GtkTypeInfo led_info = {
89       "GiwLed",
90       sizeof(GiwLed),
91       sizeof(GiwLedClass),
92       (GtkClassInitFunc) giw_led_class_init,
93       (GtkObjectInitFunc) giw_led_init,
94       /*(GtkArgSetFunc)*/ NULL,
95       /*(GtkArgGetFunc)*/ NULL,
96       (GtkClassInitFunc) NULL,
97     };
98 
99     led_type = gtk_type_unique(gtk_widget_get_type(), &led_info);
100   }
101 
102   return led_type;
103 }
104 
105 
106 #endif
107 
108 static void
109 giw_led_class_init(GiwLedClass *xclass) {
110 #if GTK_CHECK_VERSION(3, 0, 0)
111   GObjectClass *object_class = G_OBJECT_CLASS(xclass);
112 #else
113   GtkObjectClass *object_class = (GtkObjectClass *) xclass;
114 #endif
115   GtkWidgetClass *widget_class;
116 
117   widget_class = (GtkWidgetClass *) xclass;
118 
119 #if GTK_CHECK_VERSION(3, 0, 0)
120   object_class->dispose = giw_led_dispose;
121 #else
122   parent_class = (GtkWidgetClass *)gtk_type_class(gtk_widget_get_type());
123   object_class->destroy = giw_led_destroy;
124 #endif
125 
126   widget_class->realize = giw_led_realize;
127 #if GTK_CHECK_VERSION(3, 0, 0)
128   widget_class->get_preferred_width = giw_led_get_preferred_width;
129   widget_class->get_preferred_height = giw_led_get_preferred_height;
130   widget_class->draw = giw_led_draw;
131   widget_class->style_updated        = giw_led_style_updated;
132   widget_class->state_flags_changed = giw_led_state_flags_changed;
133 #else
134   widget_class->expose_event = giw_led_expose;
135   widget_class->size_request = giw_led_size_request;
136 #endif
137   widget_class->size_allocate = giw_led_size_allocate;
138   widget_class->button_press_event = giw_led_button_press;
139 
140   giw_led_signals[MODE_CHANGED_SIGNAL] = g_signal_new("mode_changed",
141                                          G_TYPE_FROM_CLASS(xclass),
142                                          (GSignalFlags)(G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION),
143                                          G_STRUCT_OFFSET(GiwLedClass, mode_changed),
144                                          NULL,
145                                          NULL,
146                                          g_cclosure_marshal_VOID__VOID,
147                                          G_TYPE_NONE, 0);
148 }
149 
150 static void
giw_led_init(GiwLed * led)151 giw_led_init(GiwLed *led) {
152   g_return_if_fail(led != NULL);
153   g_return_if_fail(GIW_IS_LED(led));
154 
155   led->on = 0; //Default position: off
156   led->enable_mouse = FALSE;
157 
158   // Default on color, full green
159   led->color_on.green = 65535;
160   led->color_on.red = 0;
161   led->color_on.blue = 0;
162 
163   // Default off color, white
164   led->color_off.green = 65535;
165   led->color_off.red = 65535;
166   led->color_off.blue = 65535;
167 
168 #if GTK_CHECK_VERSION(2,18,0)
169   gtk_widget_set_has_window(GTK_WIDGET(led), TRUE);
170 #endif
171 }
172 
173 GtkWidget *
giw_led_new(void)174 giw_led_new(void) {
175   GiwLed *led;
176 
177 #if GTK_CHECK_VERSION(3, 0, 0)
178   led = (GiwLed *)g_object_new(GIW_TYPE_LED, NULL);
179 #else
180   led = (GiwLed *)gtk_type_new(giw_led_get_type());
181 #endif
182 
183   return GTK_WIDGET(led);
184 }
185 
186 #if GTK_CHECK_VERSION(3, 0, 0)
giw_led_dispose(GObject * object)187 static void giw_led_dispose(GObject *object) {
188 #else
189 static void giw_led_destroy(GtkObject *object) {
190 #endif
191   g_return_if_fail(object != NULL);
192   g_return_if_fail(GIW_IS_LED(object));
193 
194 #if GTK_CHECK_VERSION(3, 0, 0)
195   G_OBJECT_CLASS(giw_led_parent_class)->dispose(object);
196 #else
197   if (LIVES_GUI_OBJECT_CLASS(parent_class)->destroy)
198     (* LIVES_GUI_OBJECT_CLASS(parent_class)->destroy)(object);
199 #endif
200 }
201 
202 
203 static void
204 giw_led_realize(GtkWidget *widget) {
205   GdkWindowAttr attributes;
206   gint attributes_mask;
207 
208 #if GTK_CHECK_VERSION(3, 0, 0)
209   GtkStyleContext *stylecon;
210 #endif
211 
212   g_return_if_fail(widget != NULL);
213   g_return_if_fail(GIW_IS_LED(widget));
214 
215 #if GTK_CHECK_VERSION(2,20,0)
216   gtk_widget_set_realized(widget, TRUE);
217 #else
218   GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED);
219 #endif
220 
221 
222   attributes.x = lives_widget_get_allocation_x(widget);
223   attributes.y = lives_widget_get_allocation_y(widget);
224   attributes.width = lives_widget_get_allocation_width(widget);
225   attributes.height = lives_widget_get_allocation_height(widget);
226   attributes.wclass = GDK_INPUT_OUTPUT;
227   attributes.window_type = GDK_WINDOW_CHILD;
228   attributes.event_mask = gtk_widget_get_events(widget) |
229                           GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
230                           GDK_BUTTON_RELEASE_MASK;
231 
232   attributes_mask = GDK_WA_X | GDK_WA_Y;
233 
234 #if !GTK_CHECK_VERSION(3, 0, 0)
235   attributes.visual = gtk_widget_get_visual(widget);
236   attributes_mask |= GDK_WA_COLORMAP | GDK_WA_VISUAL;
237   attributes.colormap = gtk_widget_get_colormap(widget);
238 #endif
239 
240 #if GTK_CHECK_VERSION(2,18,0)
241   gtk_widget_set_window(widget, gdk_window_new(lives_widget_get_xwindow(lives_widget_get_parent(widget)), &attributes,
242                         attributes_mask));
243 #else
244   widget->window = gdk_window_new(widget->parent->window, &attributes, attributes_mask);
245 #endif
246 
247 #if GTK_CHECK_VERSION(3, 0, 0)
248   stylecon = gtk_widget_get_style_context(widget);
249   if (!stylecon) {
250     stylecon = gtk_style_context_new();
251     gtk_style_context_set_path(stylecon, gtk_widget_get_path(widget));
252   }
253   gtk_style_context_add_class(stylecon, "giwled");
254   gtk_style_context_set_state(stylecon, GTK_STATE_FLAG_ACTIVE);
255   //gtk_style_context_set_background(stylecon,lives_widget_get_xwindow(widget));
256 #else
257   widget->style = gtk_style_attach(widget->style, lives_widget_get_xwindow(widget));
258   gtk_style_set_background(widget->style, lives_widget_get_xwindow(widget), GTK_STATE_ACTIVE);
259 #endif
260 
261   gdk_window_set_user_data(lives_widget_get_xwindow(widget), widget);
262 
263 }
264 
265 
266 
267 
268 static void
269 giw_led_size_request(GtkWidget      *widget,
270                      GtkRequisition *requisition) {
271   g_return_if_fail(widget != NULL);
272   g_return_if_fail(GIW_IS_LED(widget));
273   g_return_if_fail(requisition != NULL);
274 
275   requisition->width = LED_DEFAULT_SIZE;
276   requisition->height = LED_DEFAULT_SIZE;
277 }
278 
279 #if GTK_CHECK_VERSION(3, 0, 0)
280 
281 static void
282 giw_led_get_preferred_width(GtkWidget *widget,
283                             gint      *minimal_width,
284                             gint      *natural_width) {
285   GtkRequisition requisition;
286 
287   giw_led_size_request(widget, &requisition);
288 
289   *minimal_width = *natural_width = requisition.width;
290 }
291 
292 static void
293 giw_led_get_preferred_height(GtkWidget *widget,
294                              gint      *minimal_height,
295                              gint      *natural_height) {
296   GtkRequisition requisition;
297 
298   giw_led_size_request(widget, &requisition);
299 
300   *minimal_height = *natural_height = requisition.height;
301 }
302 
303 #endif
304 
305 
306 static void
307 giw_led_size_allocate(GtkWidget     *widget,
308                       GtkAllocation *allocation) {
309   GiwLed *led;
310 
311   g_return_if_fail(widget != NULL);
312   g_return_if_fail(GIW_IS_LED(widget));
313   g_return_if_fail(allocation != NULL);
314 
315 #if GTK_CHECK_VERSION(2,18,0)
316   gtk_widget_set_allocation(widget, allocation);
317 #else
318   widget->allocation = *allocation;
319 #endif
320 
321   led = GIW_LED(widget);
322 
323   if (lives_widget_is_realized(widget)) {
324     gdk_window_move_resize(lives_widget_get_xwindow(widget),
325                            allocation->x, allocation->y,
326                            allocation->width, allocation->height);
327 
328   }
329 
330   // The size of the led will be the lower dimension of the widget
331   if (lives_widget_get_allocation_width(widget) > lives_widget_get_allocation_height(widget)) {
332     led->size = lives_widget_get_allocation_height(widget);
333     led->radius = led->size - 4;
334     led->x = (lives_widget_get_allocation_width(widget) / 2) - (led->size / 2);
335     led->y = 0;
336   } else {
337     led->size = lives_widget_get_allocation_width(widget);
338     led->radius = led->size - 4;
339     led->x = 0;
340     led->y = (lives_widget_get_allocation_height(widget) / 2) - (led->size / 2);
341   }
342 }
343 
344 #if GTK_CHECK_VERSION(3, 0, 0)
345 static gboolean giw_led_draw(GtkWidget *widget, cairo_t *cairo) {
346 
347 #else
348 
349 static gint
350 giw_led_expose(GtkWidget      *widget,
351                GdkEventExpose *event) {
352 #endif
353   GiwLed *led;
354   GdkRectangle rect;
355 
356 #if !GTK_CHECK_VERSION(3, 0, 0)
357   GdkGC *gc; // To put the on and off colors
358 
359   g_return_val_if_fail(event != NULL, FALSE);
360 
361   if (event->count > 0)
362     return FALSE;
363 #endif
364 
365   g_return_val_if_fail(widget != NULL, FALSE);
366   g_return_val_if_fail(GIW_IS_LED(widget), FALSE);
367 
368   led = GIW_LED(widget);
369 
370   rect.x = 0;
371   rect.y = 0;
372   rect.width = lives_widget_get_allocation_width(widget);
373   rect.height = lives_widget_get_allocation_height(widget);
374 
375   // Drawing background
376 #if GTK_CHECK_VERSION(3, 0, 0)
377   cairo_set_line_width(cairo, 1.);
378 
379   gtk_render_background(gtk_widget_get_style_context(widget),
380                         cairo,
381                         0,
382                         0,
383                         rect.width,
384                         rect.height);
385 
386   if (led->on)
387     cairo_set_source_rgb(cairo, 1., 1., 1.);
388   else
389     cairo_set_source_rgba(cairo,
390                           led->color_off.red,
391                           led->color_off.green,
392                           led->color_off.blue,
393                           led->color_off.alpha
394                          );
395 
396   cairo_arc(cairo,
397             rect.width / 2 + 2,
398             rect.height / 2 + 2,
399             led->radius / 2,
400             0.,
401             2.*M_PI);
402 
403   if (led->on) cairo_set_source_rgb(cairo, 0., 0., 0.);
404 
405   cairo_arc(cairo,
406             rect.width / 2 + 1,
407             rect.height / 2 + 1,
408             led->radius / 2 + 1,
409             -45. / M_PI,
410             57.5 / M_PI);
411 
412   cairo_arc(cairo,
413             rect.width / 2,
414             rect.height / 2,
415             led->radius / 2 + 1.5,
416             -32 / M_PI,
417             37.5 / M_PI);
418 
419   if (led->on)
420     cairo_set_source_rgba(cairo,
421                           (double)(led->color_on.red),
422                           (double)(led->color_on.green),
423                           (double)(led->color_on.blue),
424                           (double)(led->color_on.alpha)
425                          );
426   else
427     cairo_set_source_rgba(cairo,
428                           (double)(led->color_off.red),
429                           (double)(led->color_off.green),
430                           (double)(led->color_off.blue),
431                           (double)(led->color_off.alpha)
432                          );
433 
434 
435   cairo_arc(cairo,
436             rect.width / 2 + 2,
437             rect.height / 2 + 2,
438             (led->size - 4) / 2,
439             0,
440             2.*M_PI);
441 
442   cairo_fill(cairo);
443 
444 #else
445   gtk_paint_flat_box(widget->style,
446                      widget->window,
447                      (GtkStateType)(widget->parent == NULL ? GTK_STATE_NORMAL : widget->parent->state),
448                      GTK_SHADOW_NONE,
449                      &rect,
450                      widget,
451                      NULL,
452                      0,
453                      0,
454                      -1,
455                      -1);
456 
457   gc = gdk_gc_new(widget->window); // Allocating memory
458   gdk_gc_copy(gc, widget->style->fg_gc[widget->state]);
459 
460   if (led->on)
461     gdk_gc_set_rgb_fg_color(gc, &(led->color_on));
462   else
463     gdk_gc_set_rgb_fg_color(gc, &(led->color_off));
464 
465 
466   // The border
467   gdk_draw_arc(widget->window,
468                led->on ? widget->style->white_gc : gc,
469                FALSE,
470                led->x + 2,
471                led->y + 2,
472                led->radius,
473                led->radius,
474                0,
475                64 * 360);
476 
477   gdk_draw_arc(widget->window,
478                widget->style->black_gc,
479                FALSE,
480                led->x + 1,
481                led->y + 1,
482                led->radius + 2,
483                led->radius + 2,
484                -64 * 90,
485                64 * 115);
486 
487   gdk_draw_arc(widget->window,
488                widget->style->black_gc,
489                FALSE,
490                led->x + 1,
491                led->y + 1,
492                led->radius + 3,
493                led->radius + 3,
494                -64 * 60,
495                64 * 75);
496 
497   gdk_draw_arc(widget->window,
498                gc,
499                TRUE,
500                led->x + 2,
501                led->y + 2,
502                led->size - 4,
503                led->size - 4,
504                0,
505                64 * 360);
506 
507   g_object_unref(gc);
508 #endif
509 
510   return FALSE;
511 }
512 
513 #if GTK_CHECK_VERSION(3, 0, 0)
514 static void
515 giw_led_style_updated(GtkWidget *widget) {
516   GTK_WIDGET_CLASS(giw_led_parent_class)->style_updated(widget);
517 }
518 
519 static void
520 giw_led_state_flags_changed(GtkWidget *widget, GtkStateFlags previous_state) {
521   gtk_widget_queue_draw(widget);
522 }
523 #endif
524 
525 static gint
526 giw_led_button_press(GtkWidget      *widget,
527                      GdkEventButton *event) {
528   GiwLed *led;
529   guint dx, dy, d;
530 
531   g_return_val_if_fail(widget != NULL, FALSE);
532   g_return_val_if_fail(GIW_IS_LED(widget), FALSE);
533   g_return_val_if_fail(event != NULL, FALSE);
534 
535   led = GIW_LED(widget);
536 
537   if (led->enable_mouse == 0) return FALSE;
538 
539   dx = event->x - lives_widget_get_allocation_width(widget) / 2;
540   dy = lives_widget_get_allocation_height(widget) / 2 - event->y;
541 
542   d = sqrt(dx * dx + dy * dy); // Distance between the pointer and the center
543 
544   if (d <= (led->size / 2)) { // If it's inside the led
545     if (led->on == FALSE)
546       led->on = TRUE;
547     else
548       led->on = FALSE;
549 
550     g_signal_emit(G_OBJECT(led), giw_led_signals[MODE_CHANGED_SIGNAL], 0);
551   }
552 
553   gtk_widget_queue_draw(GTK_WIDGET(led));
554 
555   return FALSE;
556 }
557 
558 /******************
559   Users Functions
560 ******************/
561 
562 void
563 giw_led_set_mode(GiwLed *led,
564                  guint8 mode) {
565   g_return_if_fail(led != NULL);
566   g_return_if_fail(GIW_IS_LED(led));
567 
568   if (led->on != mode) {
569     led->on = mode;
570     g_signal_emit(G_OBJECT(led), giw_led_signals[MODE_CHANGED_SIGNAL], 0);
571 
572     gtk_widget_queue_draw(GTK_WIDGET(led));
573   }
574 }
575 
576 guint8
577 giw_led_get_mode(GiwLed *led) {
578   g_return_val_if_fail(led != NULL, 0);
579   g_return_val_if_fail(GIW_IS_LED(led), 0);
580 
581   return (led->on);
582 }
583 
584 #if GTK_CHECK_VERSION(3, 0, 0)
585 void
586 giw_led_set_rgba(GiwLed *led,
587                  GdkRGBA on_color,
588                  GdkRGBA off_color) {
589   g_return_if_fail(led != NULL);
590   g_return_if_fail(GIW_IS_LED(led));
591 
592   led->color_on = on_color;
593   led->color_off = off_color;
594 
595   gtk_widget_queue_draw(GTK_WIDGET(led));
596 }
597 #endif
598 void
599 giw_led_set_colors(GiwLed *led,
600                    GdkColor on_color,
601                    GdkColor off_color) {
602   g_return_if_fail(led != NULL);
603   g_return_if_fail(GIW_IS_LED(led));
604 
605 #if GTK_CHECK_VERSION(3, 0, 0)
606   led->color_on.red = (gdouble)on_color.red / 65535.;
607   led->color_on.green = (gdouble)on_color.green / 65535.;
608   led->color_on.blue = (gdouble)on_color.blue / 65535.;
609   led->color_on.alpha = 1.;
610   led->color_off.red = (gdouble)off_color.red / 65535.;
611   led->color_off.green = (gdouble)off_color.green / 65535.;
612   led->color_off.blue = (gdouble)off_color.blue / 65535.;
613   led->color_off.alpha = 1.;
614 #else
615   led->color_on = on_color;
616   led->color_off = off_color;
617 #endif
618   gtk_widget_queue_draw(GTK_WIDGET(led));
619 }
620 
621 void
622 giw_led_enable_mouse(GiwLed *led,
623                      gboolean option) {
624   g_return_if_fail(led != NULL);
625   g_return_if_fail(GIW_IS_LED(led));
626 
627   led->enable_mouse = option;
628 }
629 
630 /******************
631   Local Functions
632 ******************/
633 
634 
635