]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorswatch.c
Change FSF Address
[~andy/gtk] / gtk / gtkcolorswatch.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2012 Red Hat, Inc.
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 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, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include "gtkcolorswatchprivate.h"
21
22 #include "gtkcolorchooserprivate.h"
23 #include "gtkroundedboxprivate.h"
24 #include "gtkthemingbackgroundprivate.h"
25 #include "gtkdnd.h"
26 #include "gtkicontheme.h"
27 #include "gtkmain.h"
28 #include "gtkmenu.h"
29 #include "gtkmenuitem.h"
30 #include "gtkmenushell.h"
31 #include "gtkprivate.h"
32 #include "gtkintl.h"
33 #include "a11y/gtkcolorswatchaccessible.h"
34
35
36 struct _GtkColorSwatchPrivate
37 {
38   GdkRGBA color;
39   gdouble radius[4];
40   gchar *icon;
41   guint    has_color        : 1;
42   guint    contains_pointer : 1;
43   guint    use_alpha        : 1;
44   guint    selectable       : 1;
45
46   GdkWindow *event_window;
47 };
48
49 enum
50 {
51   PROP_ZERO,
52   PROP_RGBA,
53   PROP_SELECTABLE
54 };
55
56 enum
57 {
58   ACTIVATE,
59   CUSTOMIZE,
60   LAST_SIGNAL
61 };
62
63 static guint signals[LAST_SIGNAL];
64
65 G_DEFINE_TYPE (GtkColorSwatch, gtk_color_swatch, GTK_TYPE_WIDGET)
66
67 static void
68 gtk_color_swatch_init (GtkColorSwatch *swatch)
69 {
70   swatch->priv = G_TYPE_INSTANCE_GET_PRIVATE (swatch,
71                                               GTK_TYPE_COLOR_SWATCH,
72                                               GtkColorSwatchPrivate);
73
74   gtk_widget_set_can_focus (GTK_WIDGET (swatch), TRUE);
75   gtk_widget_set_has_window (GTK_WIDGET (swatch), FALSE);
76
77   swatch->priv->use_alpha = TRUE;
78   swatch->priv->selectable = TRUE;
79 }
80
81 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
82 #define ACTIVE_BADGE_RADIUS 10
83
84 static gboolean
85 swatch_draw (GtkWidget *widget,
86              cairo_t   *cr)
87 {
88   GtkColorSwatch *swatch = (GtkColorSwatch*)widget;
89   GtkThemingBackground background;
90   gdouble width, height;
91   GtkStyleContext *context;
92   GtkStateFlags state;
93   GtkIconTheme *theme;
94   GtkIconInfo *icon_info = NULL;
95
96   theme = gtk_icon_theme_get_default ();
97   context = gtk_widget_get_style_context (widget);
98   state = gtk_widget_get_state_flags (widget);
99   width = gtk_widget_get_allocated_width (widget);
100   height = gtk_widget_get_allocated_height (widget);
101
102   cairo_save (cr);
103
104   gtk_style_context_save (context);
105   gtk_style_context_set_state (context, state);
106
107   _gtk_theming_background_init_from_context (&background, context,
108                                              0, 0, width, height,
109                                              GTK_JUNCTION_NONE);
110
111   if (swatch->priv->has_color)
112     {
113       cairo_pattern_t *pattern;
114       cairo_matrix_t matrix;
115
116       if (swatch->priv->use_alpha)
117         {
118           cairo_save (cr);
119
120           _gtk_rounded_box_path (&background.clip_box, cr);
121           cairo_clip_preserve (cr);
122
123           cairo_set_source_rgb (cr, 0.33, 0.33, 0.33);
124           cairo_fill_preserve (cr);
125
126           pattern = _gtk_color_chooser_get_checkered_pattern ();
127           cairo_matrix_init_scale (&matrix, 0.125, 0.125);
128           cairo_pattern_set_matrix (pattern, &matrix);
129
130           cairo_set_source_rgb (cr, 0.66, 0.66, 0.66);
131           cairo_mask (cr, pattern);
132           cairo_pattern_destroy (pattern);
133
134           cairo_restore (cr);
135
136           background.bg_color = swatch->priv->color;
137         }
138       else
139         {
140           background.bg_color = swatch->priv->color;
141           background.bg_color.alpha = 1.0;
142         }
143
144       _gtk_theming_background_render (&background, cr);
145     }
146   else
147     _gtk_theming_background_render (&background, cr);
148
149   gtk_render_frame (context, cr,
150                     0, 0, width, height);
151
152   if (gtk_widget_has_visible_focus (widget))
153     {
154       cairo_set_line_width (cr, 2);
155       if (swatch->priv->has_color && INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) < 0.5)
156         cairo_set_source_rgba (cr, 1., 1., 1., 0.4);
157       else
158         cairo_set_source_rgba (cr, 0., 0., 0., 0.4);
159       _gtk_rounded_box_shrink (&background.clip_box, 3, 3, 3, 3);
160       _gtk_rounded_box_path (&background.clip_box, cr);
161       cairo_stroke (cr);
162     }
163
164   if (swatch->priv->icon)
165     {
166       icon_info = gtk_icon_theme_lookup_icon (theme, swatch->priv->icon, 16,
167                                               GTK_ICON_LOOKUP_GENERIC_FALLBACK
168                                               | GTK_ICON_LOOKUP_USE_BUILTIN);
169     }
170   else if ((state & GTK_STATE_FLAG_SELECTED) != 0)
171     {
172       GdkRGBA bg, border;
173       GtkBorder border_width;
174       GIcon *gicon;
175
176       gtk_style_context_add_class (context, "color-active-badge");
177       _gtk_theming_background_init_from_context (&background, context,
178                                                  (width - 2 * ACTIVE_BADGE_RADIUS) / 2, (height - 2 * ACTIVE_BADGE_RADIUS) / 2,
179                                                  2 * ACTIVE_BADGE_RADIUS, 2* ACTIVE_BADGE_RADIUS,
180                                                  GTK_JUNCTION_NONE);
181
182       if (_gtk_theming_background_has_background_image (&background))
183         {
184           _gtk_theming_background_render (&background, cr);
185         }
186       else
187         {
188           gtk_style_context_get_background_color (context, state, &bg);
189           gtk_style_context_get_border_color (context, state, &border);
190           gtk_style_context_get_border (context, state, &border_width);
191
192           cairo_new_sub_path (cr);
193           cairo_arc (cr, width / 2, height / 2, ACTIVE_BADGE_RADIUS, 0, 2 * G_PI);
194           cairo_close_path (cr);
195           gdk_cairo_set_source_rgba (cr, &bg);
196           cairo_fill_preserve (cr);
197
198           gdk_cairo_set_source_rgba (cr, &border);
199           cairo_set_line_width (cr, border_width.left);
200           cairo_stroke (cr);
201
202           gicon = g_themed_icon_new ("object-select-symbolic");
203           /* fallback for themes that don't have object-select-symbolic */
204           g_themed_icon_append_name (G_THEMED_ICON (gicon), "gtk-apply");
205
206           icon_info = gtk_icon_theme_lookup_by_gicon (theme, gicon, 16,
207                                                       GTK_ICON_LOOKUP_GENERIC_FALLBACK
208                                                       | GTK_ICON_LOOKUP_USE_BUILTIN);
209           g_object_unref (gicon);
210         }
211     }
212
213   if (icon_info != NULL)
214     {
215       GdkPixbuf *pixbuf;
216
217       pixbuf = gtk_icon_info_load_symbolic_for_context (icon_info, context,
218                                                         NULL, NULL);
219
220       if (pixbuf != NULL)
221         {
222           gtk_render_icon (context, cr, pixbuf,
223                            (width - gdk_pixbuf_get_width (pixbuf)) / 2,
224                            (height - gdk_pixbuf_get_height (pixbuf)) / 2);
225           g_object_unref (pixbuf);
226         }
227
228       gtk_icon_info_free (icon_info);
229     }
230
231   cairo_restore (cr);
232   gtk_style_context_restore (context);
233
234   return FALSE;
235 }
236
237 static void
238 drag_set_color_icon (GdkDragContext *context,
239                      const GdkRGBA  *color)
240 {
241   cairo_surface_t *surface;
242   cairo_t *cr;
243
244   surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 48, 32);
245   cr = cairo_create (surface);
246   gdk_cairo_set_source_rgba (cr, color);
247   cairo_paint (cr);
248
249   cairo_surface_set_device_offset (surface, -4, -4);
250   gtk_drag_set_icon_surface (context, surface);
251
252   cairo_destroy (cr);
253   cairo_surface_destroy (surface);
254 }
255
256 static void
257 swatch_drag_begin (GtkWidget      *widget,
258                    GdkDragContext *context)
259 {
260   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
261   GdkRGBA color;
262
263   gtk_color_swatch_get_rgba (swatch, &color);
264   drag_set_color_icon (context, &color);
265 }
266
267 static void
268 swatch_drag_data_get (GtkWidget        *widget,
269                       GdkDragContext   *context,
270                       GtkSelectionData *selection_data,
271                       guint             info,
272                       guint             time)
273 {
274   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
275   guint16 vals[4];
276   GdkRGBA color;
277
278   gtk_color_swatch_get_rgba (swatch, &color);
279
280   vals[0] = color.red * 0xffff;
281   vals[1] = color.green * 0xffff;
282   vals[2] = color.blue * 0xffff;
283   vals[3] = color.alpha * 0xffff;
284
285   gtk_selection_data_set (selection_data,
286                           gdk_atom_intern_static_string ("application/x-color"),
287                           16, (guchar *)vals, 8);
288 }
289
290 static void
291 swatch_drag_data_received (GtkWidget        *widget,
292                            GdkDragContext   *context,
293                            gint              x,
294                            gint              y,
295                            GtkSelectionData *selection_data,
296                            guint             info,
297                            guint             time)
298 {
299   gint length;
300   guint16 *vals;
301   GdkRGBA color;
302
303   length = gtk_selection_data_get_length (selection_data);
304
305   if (length < 0)
306     return;
307
308   /* We accept drops with the wrong format, since the KDE color
309    * chooser incorrectly drops application/x-color with format 8.
310    */
311   if (length != 8)
312     {
313       g_warning ("Received invalid color data\n");
314       return;
315     }
316
317   vals = (guint16 *) gtk_selection_data_get_data (selection_data);
318
319   color.red   = (gdouble)vals[0] / 0xffff;
320   color.green = (gdouble)vals[1] / 0xffff;
321   color.blue  = (gdouble)vals[2] / 0xffff;
322   color.alpha = (gdouble)vals[3] / 0xffff;
323
324   gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (widget), &color);
325 }
326
327 static void
328 swatch_get_preferred_width (GtkWidget *widget,
329                             gint      *min,
330                             gint      *nat)
331 {
332   *min = *nat = 48;
333 }
334
335 static void
336 swatch_get_preferred_height (GtkWidget *widget,
337                              gint      *min,
338                              gint      *nat)
339 {
340   *min = *nat = 32;
341 }
342
343 static gboolean
344 swatch_key_press (GtkWidget   *widget,
345                   GdkEventKey *event)
346 {
347   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
348
349   if (event->keyval == GDK_KEY_space ||
350       event->keyval == GDK_KEY_Return ||
351       event->keyval == GDK_KEY_ISO_Enter||
352       event->keyval == GDK_KEY_KP_Enter ||
353       event->keyval == GDK_KEY_KP_Space)
354     {
355       if (swatch->priv->has_color && 
356           swatch->priv->selectable &&
357           (gtk_widget_get_state_flags (widget) & GTK_STATE_FLAG_SELECTED) == 0)
358         gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
359       else
360         g_signal_emit (swatch, signals[ACTIVATE], 0);
361       return TRUE;
362     }
363
364   if (GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->key_press_event (widget, event))
365     return TRUE;
366
367   return FALSE;
368 }
369
370 static gboolean
371 swatch_enter_notify (GtkWidget        *widget,
372                      GdkEventCrossing *event)
373 {
374   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
375   swatch->priv->contains_pointer = TRUE;
376   gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_PRELIGHT, FALSE);
377
378   return FALSE;
379 }
380
381 static gboolean
382 swatch_leave_notify (GtkWidget        *widget,
383                      GdkEventCrossing *event)
384 {
385   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
386   swatch->priv->contains_pointer = FALSE;
387   gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_PRELIGHT);
388
389   return FALSE;
390 }
391
392 static void
393 emit_customize (GtkColorSwatch *swatch)
394 {
395   g_signal_emit (swatch, signals[CUSTOMIZE], 0);
396 }
397
398 static void
399 popup_position_func (GtkMenu   *menu,
400                      gint      *x,
401                      gint      *y,
402                      gboolean  *push_in,
403                      gpointer   user_data)
404 {
405   GtkWidget *widget;
406   GtkRequisition req;
407   gint root_x, root_y;
408   GdkScreen *screen;
409   GdkWindow *window;
410   GdkRectangle monitor;
411   gint monitor_num;
412
413   widget = GTK_WIDGET (user_data);
414   g_return_if_fail (gtk_widget_get_realized (widget));
415   window = gtk_widget_get_window (widget);
416
417   screen = gtk_widget_get_screen (widget);
418   monitor_num = gdk_screen_get_monitor_at_window (screen, window);
419   if (monitor_num < 0)
420     monitor_num = 0;
421   gtk_menu_set_monitor (menu, monitor_num);
422
423   gdk_window_get_origin (window, &root_x, &root_y);
424   gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
425
426   /* Put corner of menu centered on swatch */
427   *x = root_x + gtk_widget_get_allocated_width (widget) / 2;
428   *y = root_y + gtk_widget_get_allocated_height (widget) / 2;
429
430   /* Ensure sanity */
431   gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
432   *x = CLAMP (*x, monitor.x, MAX (monitor.x, monitor.width - req.width));
433   *y = CLAMP (*y, monitor.y, MAX (monitor.y, monitor.height - req.height));
434 }
435
436 static void
437 do_popup (GtkWidget      *swatch,
438           GdkEventButton *event)
439 {
440   GtkWidget *menu;
441   GtkWidget *item;
442
443   menu = gtk_menu_new ();
444   item = gtk_menu_item_new_with_mnemonic (_("_Customize"));
445   gtk_menu_attach_to_widget (GTK_MENU (menu), swatch, NULL);
446
447   g_signal_connect_swapped (item, "activate",
448                             G_CALLBACK (emit_customize), swatch);
449
450   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
451
452   gtk_widget_show_all (item);
453
454   if (event)
455     gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
456                     NULL, NULL, event->button, event->time);
457   else
458     gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
459                     popup_position_func, swatch,
460                     0, gtk_get_current_event_time ());
461 }
462
463 static gboolean
464 swatch_button_press (GtkWidget      *widget,
465                      GdkEventButton *event)
466 {
467   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
468
469   gtk_widget_grab_focus (widget);
470
471   if (gdk_event_triggers_context_menu ((GdkEvent *) event) &&
472       swatch->priv->has_color)
473     {
474       do_popup (widget, event);
475       return TRUE;
476     }
477   else if (event->type == GDK_2BUTTON_PRESS &&
478            event->button == GDK_BUTTON_PRIMARY)
479     {
480       g_signal_emit (swatch, signals[ACTIVATE], 0);
481       return TRUE;
482     }
483
484   return FALSE;
485 }
486
487 static gboolean
488 swatch_button_release (GtkWidget      *widget,
489                        GdkEventButton *event)
490 {
491   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
492   GtkStateFlags flags;
493
494   if (event->button == GDK_BUTTON_PRIMARY &&
495       swatch->priv->contains_pointer)
496     {
497       flags = gtk_widget_get_state_flags (widget);
498       if (!swatch->priv->has_color)
499         {
500           g_signal_emit (swatch, signals[ACTIVATE], 0);
501           return TRUE;
502         }
503       else if (swatch->priv->selectable &&
504                (flags & GTK_STATE_FLAG_SELECTED) == 0)
505         {
506           gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_SELECTED, FALSE);
507           return TRUE;
508         }
509     }
510
511   return FALSE;
512 }
513
514 static void
515 swatch_map (GtkWidget *widget)
516 {
517   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
518
519   GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->map (widget);
520
521   if (swatch->priv->event_window)
522     gdk_window_show (swatch->priv->event_window);
523 }
524
525 static void
526 swatch_unmap (GtkWidget *widget)
527 {
528   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
529
530   if (swatch->priv->event_window)
531     gdk_window_hide (swatch->priv->event_window);
532
533   GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->unmap (widget);
534 }
535
536 static void
537 swatch_realize (GtkWidget *widget)
538 {
539   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
540   GtkAllocation allocation;
541   GdkWindow *window;
542   GdkWindowAttr attributes;
543   gint attributes_mask;
544
545   gtk_widget_get_allocation (widget, &allocation);
546   gtk_widget_set_realized (widget, TRUE);
547
548   attributes.window_type = GDK_WINDOW_CHILD;
549   attributes.x = allocation.x;
550   attributes.y = allocation.y;
551   attributes.width = allocation.width;
552   attributes.height = allocation.height;
553   attributes.wclass = GDK_INPUT_ONLY;
554   attributes.event_mask = gtk_widget_get_events (widget);
555   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
556                             GDK_BUTTON_RELEASE_MASK |
557                             GDK_ENTER_NOTIFY_MASK |
558                             GDK_LEAVE_NOTIFY_MASK);
559
560   attributes_mask = GDK_WA_X | GDK_WA_Y;
561
562   window = gtk_widget_get_parent_window (widget);
563   gtk_widget_set_window (widget, window);
564   g_object_ref (window);
565
566   swatch->priv->event_window = 
567     gdk_window_new (window,
568                     &attributes, attributes_mask);
569   gdk_window_set_user_data (swatch->priv->event_window, widget);
570 }
571
572 static void
573 swatch_unrealize (GtkWidget *widget)
574 {
575   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
576
577   if (swatch->priv->event_window)
578     {
579       gdk_window_set_user_data (swatch->priv->event_window, NULL);
580       gdk_window_destroy (swatch->priv->event_window);
581       swatch->priv->event_window = NULL;
582     }
583
584   GTK_WIDGET_CLASS (gtk_color_swatch_parent_class)->unrealize (widget);
585 }
586
587 static void
588 swatch_size_allocate (GtkWidget *widget,
589                       GtkAllocation *allocation)
590 {
591   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (widget);
592
593   gtk_widget_set_allocation (widget, allocation);
594
595   if (gtk_widget_get_realized (widget))
596     gdk_window_move_resize (swatch->priv->event_window,
597                             allocation->x,
598                             allocation->y,
599                             allocation->width,
600                             allocation->height);
601 }
602
603 static gboolean
604 swatch_popup_menu (GtkWidget *swatch)
605 {
606   do_popup (swatch, NULL);
607   return TRUE;
608 }
609
610 /* GObject implementation {{{1 */
611
612 static void
613 swatch_get_property (GObject    *object,
614                      guint       prop_id,
615                      GValue     *value,
616                      GParamSpec *pspec)
617 {
618   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
619   GdkRGBA color;
620
621   switch (prop_id)
622     {
623     case PROP_RGBA:
624       gtk_color_swatch_get_rgba (swatch, &color);
625       g_value_set_boxed (value, &color);
626       break;
627     case PROP_SELECTABLE:
628       g_value_set_boolean (value, gtk_color_swatch_get_selectable (swatch));
629       break;
630     default:
631       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
632       break;
633     }
634 }
635
636 static void
637 swatch_set_property (GObject      *object,
638                      guint         prop_id,
639                      const GValue *value,
640                      GParamSpec   *pspec)
641 {
642   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
643
644   switch (prop_id)
645     {
646     case PROP_RGBA:
647       gtk_color_swatch_set_rgba (swatch, g_value_get_boxed (value));
648       break;
649     case PROP_SELECTABLE:
650       gtk_color_swatch_set_selectable (swatch, g_value_get_boolean (value));
651       break;
652     default:
653       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
654       break;
655     }
656 }
657
658 static void
659 swatch_finalize (GObject *object)
660 {
661   GtkColorSwatch *swatch = GTK_COLOR_SWATCH (object);
662
663   g_free (swatch->priv->icon);
664
665   G_OBJECT_CLASS (gtk_color_swatch_parent_class)->finalize (object);
666 }
667
668 static void
669 gtk_color_swatch_class_init (GtkColorSwatchClass *class)
670 {
671   GtkWidgetClass *widget_class = (GtkWidgetClass *)class;
672   GObjectClass *object_class = (GObjectClass *)class;
673
674   object_class->get_property = swatch_get_property;
675   object_class->set_property = swatch_set_property;
676   object_class->finalize = swatch_finalize;
677
678   widget_class->get_preferred_width = swatch_get_preferred_width;
679   widget_class->get_preferred_height = swatch_get_preferred_height;
680   widget_class->draw = swatch_draw;
681   widget_class->drag_begin = swatch_drag_begin;
682   widget_class->drag_data_get = swatch_drag_data_get;
683   widget_class->drag_data_received = swatch_drag_data_received;
684   widget_class->key_press_event = swatch_key_press;
685   widget_class->popup_menu = swatch_popup_menu;
686   widget_class->button_press_event = swatch_button_press;
687   widget_class->button_release_event = swatch_button_release;
688   widget_class->enter_notify_event = swatch_enter_notify;
689   widget_class->leave_notify_event = swatch_leave_notify;
690   widget_class->realize = swatch_realize;
691   widget_class->unrealize = swatch_unrealize;
692   widget_class->map = swatch_map;
693   widget_class->unmap = swatch_unmap;
694   widget_class->size_allocate = swatch_size_allocate;
695
696   signals[ACTIVATE] =
697     g_signal_new ("activate",
698                   GTK_TYPE_COLOR_SWATCH,
699                   G_SIGNAL_RUN_FIRST,
700                   G_STRUCT_OFFSET (GtkColorSwatchClass, activate),
701                   NULL, NULL, NULL, G_TYPE_NONE, 0);
702
703   signals[CUSTOMIZE] =
704     g_signal_new ("customize",
705                   GTK_TYPE_COLOR_SWATCH,
706                   G_SIGNAL_RUN_FIRST,
707                   G_STRUCT_OFFSET (GtkColorSwatchClass, customize),
708                   NULL, NULL, NULL, G_TYPE_NONE, 0);
709
710   g_object_class_install_property (object_class, PROP_RGBA,
711       g_param_spec_boxed ("rgba", P_("RGBA Color"), P_("Color as RGBA"),
712                           GDK_TYPE_RGBA, GTK_PARAM_READWRITE));
713   g_object_class_install_property (object_class, PROP_SELECTABLE,
714       g_param_spec_boolean ("selectable", P_("Selectable"), P_("Whether the swatch is selectable"),
715                             TRUE, GTK_PARAM_READWRITE));
716
717   g_type_class_add_private (object_class, sizeof (GtkColorSwatchPrivate));
718
719   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_COLOR_SWATCH_ACCESSIBLE);
720 }
721
722 /* Public API {{{1 */
723
724 GtkWidget *
725 gtk_color_swatch_new (void)
726 {
727   return (GtkWidget *) g_object_new (GTK_TYPE_COLOR_SWATCH, NULL);
728 }
729
730 static const GtkTargetEntry dnd_targets[] = {
731   { "application/x-color", 0 }
732 };
733
734 void
735 gtk_color_swatch_set_rgba (GtkColorSwatch *swatch,
736                            const GdkRGBA  *color)
737 {
738   GtkStyleContext *context;
739
740   context = gtk_widget_get_style_context (GTK_WIDGET (swatch));
741
742   if (!swatch->priv->has_color)
743     {
744       gtk_drag_source_set (GTK_WIDGET (swatch),
745                            GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
746                            dnd_targets, G_N_ELEMENTS (dnd_targets),
747                            GDK_ACTION_COPY | GDK_ACTION_MOVE);
748     }
749   else
750     {
751       gtk_style_context_remove_class (context, "color-light");
752       gtk_style_context_remove_class (context, "color-dark");
753     }
754
755   swatch->priv->has_color = TRUE;
756   swatch->priv->color = *color;
757
758   if (INTENSITY (swatch->priv->color.red, swatch->priv->color.green, swatch->priv->color.blue) > 0.5)
759     gtk_style_context_add_class (context, "color-light");
760   else
761     gtk_style_context_add_class (context, "color-dark");
762
763   gtk_widget_queue_draw (GTK_WIDGET (swatch));
764   g_object_notify (G_OBJECT (swatch), "rgba");
765 }
766
767 gboolean
768 gtk_color_swatch_get_rgba (GtkColorSwatch *swatch,
769                            GdkRGBA        *color)
770 {
771   if (swatch->priv->has_color)
772     {
773       color->red = swatch->priv->color.red;
774       color->green = swatch->priv->color.green;
775       color->blue = swatch->priv->color.blue;
776       color->alpha = swatch->priv->color.alpha;
777       return TRUE;
778     }
779   else
780     {
781       color->red = 1.0;
782       color->green = 1.0;
783       color->blue = 1.0;
784       color->alpha = 1.0;
785       return FALSE;
786     }
787 }
788
789 void
790 gtk_color_swatch_set_icon (GtkColorSwatch *swatch,
791                            const gchar    *icon)
792 {
793   swatch->priv->icon = g_strdup (icon);
794   gtk_widget_queue_draw (GTK_WIDGET (swatch));
795 }
796
797 void
798 gtk_color_swatch_set_can_drop (GtkColorSwatch *swatch,
799                                gboolean        can_drop)
800 {
801   if (can_drop)
802     {
803       gtk_drag_dest_set (GTK_WIDGET (swatch),
804                          GTK_DEST_DEFAULT_HIGHLIGHT |
805                          GTK_DEST_DEFAULT_MOTION |
806                          GTK_DEST_DEFAULT_DROP,
807                          dnd_targets, G_N_ELEMENTS (dnd_targets),
808                          GDK_ACTION_COPY);
809     }
810   else
811     {
812       gtk_drag_dest_unset (GTK_WIDGET (swatch));
813     }
814 }
815
816 void
817 gtk_color_swatch_set_use_alpha (GtkColorSwatch *swatch,
818                                 gboolean        use_alpha)
819 {
820   swatch->priv->use_alpha = use_alpha;
821   gtk_widget_queue_draw (GTK_WIDGET (swatch));
822 }
823
824 void
825 gtk_color_swatch_set_selectable (GtkColorSwatch *swatch,
826                                  gboolean selectable)
827 {
828   if (selectable == swatch->priv->selectable)
829     return;
830
831   swatch->priv->selectable = selectable;
832   g_object_notify (G_OBJECT (swatch), "selectable");
833 }
834
835 gboolean
836 gtk_color_swatch_get_selectable (GtkColorSwatch *swatch)
837 {
838   return swatch->priv->selectable;
839 }
840
841 /* vim:set foldmethod=marker: */