]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorchooserwidget.c
Improve a11y names for colors
[~andy/gtk] / gtk / gtkcolorchooserwidget.c
1 /* GTK - The GIMP Toolkit
2  *
3  * Copyright (C) 2012 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22
23 #include "gtkcolorchooserprivate.h"
24 #include "gtkcolorchooserwidget.h"
25 #include "gtkcoloreditorprivate.h"
26 #include "gtkcolorswatchprivate.h"
27 #include "gtkbox.h"
28 #include "gtkgrid.h"
29 #include "gtklabel.h"
30 #include "gtkorientable.h"
31 #include "gtkprivate.h"
32 #include "gtkintl.h"
33 #include "gtksizegroup.h"
34
35 #include <math.h>
36
37 /**
38  * SECTION:gtkcolorchooserwidget
39  * @Short_description: A widget for choosing colors
40  * @Title: GtkColorChooserWidget
41  * @See_also: #GtkColorChooserDialog
42  *
43  * The #GtkColorChooserWidget widget lets the user select a
44  * color. By default, the chooser presents a prefined palette
45  * of colors, plus a small number of settable custom colors.
46  * It is also possible to select a different color with the
47  * single-color editor. To enter the single-color editing mode,
48  * use the context menu of any color of the palette, or use the
49  * '+' button to add a new custom color.
50  *
51  * The chooser automatically remembers the last selection, as well
52  * as custom colors.
53  *
54  * To change the initially selected color, use gtk_color_chooser_set_rgba().
55  * To get the selected font use gtk_color_chooser_get_rgba().
56  *
57  * The #GtkColorChooserWidget is used in the #GtkColorChooserDialog
58  * to provide a dialog for selecting colors.
59  *
60  * Since: 3.4
61  */
62
63 struct _GtkColorChooserWidgetPrivate
64 {
65   GtkWidget *palette;
66   GtkWidget *editor;
67   GtkSizeGroup *size_group;
68
69   GtkWidget *custom_label;
70   GtkWidget *custom;
71
72   GtkWidget *button;
73   GtkColorSwatch *current;
74
75   gboolean use_alpha;
76   gboolean has_default_palette;
77
78   GSettings *settings;
79 };
80
81 enum
82 {
83   PROP_ZERO,
84   PROP_RGBA,
85   PROP_USE_ALPHA,
86   PROP_SHOW_EDITOR
87 };
88
89 static void gtk_color_chooser_widget_iface_init (GtkColorChooserInterface *iface);
90
91 G_DEFINE_TYPE_WITH_CODE (GtkColorChooserWidget, gtk_color_chooser_widget, GTK_TYPE_BOX,
92                          G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER,
93                                                 gtk_color_chooser_widget_iface_init))
94
95 static void
96 select_swatch (GtkColorChooserWidget *cc,
97                GtkColorSwatch        *swatch)
98 {
99   GdkRGBA color;
100
101   if (cc->priv->current == swatch)
102     return;
103
104   if (cc->priv->current != NULL)
105     gtk_widget_unset_state_flags (GTK_WIDGET (cc->priv->current), GTK_STATE_FLAG_SELECTED);
106   gtk_widget_set_state_flags (GTK_WIDGET (swatch), GTK_STATE_FLAG_SELECTED, FALSE);
107   cc->priv->current = swatch;
108
109   gtk_color_swatch_get_rgba (swatch, &color);
110   g_settings_set (cc->priv->settings, "selected-color", "(bdddd)",
111                   TRUE, color.red, color.green, color.blue, color.alpha);
112
113   g_object_notify (G_OBJECT (cc), "rgba");
114 }
115
116 static void
117 swatch_activate (GtkColorSwatch        *swatch,
118                  GtkColorChooserWidget *cc)
119 {
120   GdkRGBA color;
121
122   gtk_color_swatch_get_rgba (swatch, &color);
123   _gtk_color_chooser_color_activated (GTK_COLOR_CHOOSER (cc), &color);
124 }
125
126 static void
127 swatch_customize (GtkColorSwatch        *swatch,
128                   GtkColorChooserWidget *cc)
129 {
130   GdkRGBA color;
131
132   gtk_color_swatch_get_rgba (swatch, &color);
133   gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), &color);
134
135   gtk_widget_hide (cc->priv->palette);
136   gtk_widget_show (cc->priv->editor);
137   g_object_notify (G_OBJECT (cc), "show-editor");
138 }
139
140 static void
141 swatch_selected (GtkColorSwatch        *swatch,
142                  GtkStateFlags          previous,
143                  GtkColorChooserWidget *cc)
144 {
145   GtkStateFlags flags;
146
147   flags = gtk_widget_get_state_flags (GTK_WIDGET (swatch));
148   if ((flags & GTK_STATE_FLAG_SELECTED) != (previous & GTK_STATE_FLAG_SELECTED) &&
149       (flags & GTK_STATE_FLAG_SELECTED) != 0)
150     select_swatch (cc, swatch);
151 }
152
153 static void
154 connect_swatch_signals (GtkWidget *p,
155                         gpointer   data)
156 {
157   g_signal_connect (p, "activate", G_CALLBACK (swatch_activate), data);
158   g_signal_connect (p, "customize", G_CALLBACK (swatch_customize), data);
159   g_signal_connect (p, "state-flags-changed", G_CALLBACK (swatch_selected), data);
160 }
161
162 static void
163 button_activate (GtkColorSwatch        *swatch,
164                  GtkColorChooserWidget *cc)
165 {
166   /* somewhat random, makes the hairline nicely visible */
167   GdkRGBA color = { 0.75, 0.25, 0.25, 1.0 };
168
169   gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), &color);
170
171   gtk_widget_hide (cc->priv->palette);
172   gtk_widget_show (cc->priv->editor);
173   g_object_notify (G_OBJECT (cc), "show-editor");
174 }
175
176 static void
177 connect_button_signals (GtkWidget *p,
178                         gpointer   data)
179 {
180   g_signal_connect (p, "activate", G_CALLBACK (button_activate), data);
181 }
182
183 static void
184 save_custom_colors (GtkColorChooserWidget *cc)
185 {
186   GVariantBuilder builder;
187   GVariant *variant;
188   GdkRGBA color;
189   GList *children, *l;
190   GtkWidget *child;
191
192   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(dddd)"));
193
194   children = gtk_container_get_children (GTK_CONTAINER (cc->priv->custom));
195   for (l = g_list_nth (children, 1); l != NULL; l = l->next)
196     {
197       child = l->data;
198       if (gtk_color_swatch_get_rgba (GTK_COLOR_SWATCH (child), &color))
199         g_variant_builder_add (&builder, "(dddd)",
200                                color.red, color.green, color.blue, color.alpha);
201     }
202
203   variant = g_variant_builder_end (&builder);
204   g_settings_set_value (cc->priv->settings, "custom-colors", variant);
205
206   g_list_free (children);
207 }
208
209 static void
210 connect_custom_signals (GtkWidget *p,
211                         gpointer   data)
212 {
213   connect_swatch_signals (p, data);
214   g_signal_connect_swapped (p, "notify::rgba",
215                             G_CALLBACK (save_custom_colors), data);
216 }
217
218 static void
219 gtk_color_chooser_widget_set_use_alpha (GtkColorChooserWidget *cc,
220                                         gboolean               use_alpha)
221 {
222   GList *children, *l;
223   GList *palettes, *p;
224   GtkWidget *swatch;
225   GtkWidget *grid;
226
227   cc->priv->use_alpha = use_alpha;
228   gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (cc->priv->editor), use_alpha);
229
230   palettes = gtk_container_get_children (GTK_CONTAINER (cc->priv->palette));
231   for (p = palettes; p; p = p->next)
232     {
233       grid = p->data;
234
235       if (!GTK_IS_CONTAINER (grid))
236         continue;
237
238       children = gtk_container_get_children (GTK_CONTAINER (grid));
239       for (l = children; l; l = l->next)
240         {
241           swatch = l->data;
242           gtk_color_swatch_set_use_alpha (GTK_COLOR_SWATCH (swatch), use_alpha);
243         }
244       g_list_free (children);
245     }
246   g_list_free (palettes);
247
248   gtk_widget_queue_draw (GTK_WIDGET (cc));
249 }
250
251 static void
252 gtk_color_chooser_widget_set_show_editor (GtkColorChooserWidget *cc,
253                                           gboolean               show_editor)
254 {
255   if (show_editor)
256     {
257       GdkRGBA color = { 0.75, 0.25, 0.25, 1.0 };
258
259       if (cc->priv->current)
260         gtk_color_swatch_get_rgba (cc->priv->current, &color);
261       gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), &color);
262     }
263
264   gtk_widget_set_visible (cc->priv->editor, show_editor);
265   gtk_widget_set_visible (cc->priv->palette, !show_editor);
266 }
267
268 /* UI construction {{{1 */
269
270 static guint
271 scale_round (gdouble value, gdouble scale)
272 {
273   value = floor (value * scale + 0.5);
274   value = MAX (value, 0);
275   value = MIN (value, scale);
276   return (guint)value;
277 }
278
279 gchar *
280 accessible_color_name (GdkRGBA *color)
281 {
282   if (color->alpha < 1.0)
283     return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%, Alpha %d%%"),
284                             scale_round (color->red, 100),
285                             scale_round (color->green, 100),
286                             scale_round (color->blue, 100),
287                             scale_round (color->alpha, 100));
288   else
289     return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%"),
290                             scale_round (color->red, 100),
291                             scale_round (color->green, 100),
292                             scale_round (color->blue, 100));
293 }
294
295
296 static void
297 add_palette (GtkColorChooserWidget  *cc,
298              gboolean                horizontal,
299              gint                    colors_per_line,
300              gint                    n_colors,
301              GdkRGBA                *colors,
302              const gchar           **names)
303 {
304   GtkWidget *grid;
305   GtkWidget *p;
306   AtkObject *atk_obj;
307   gint line, pos;
308   gint i;
309   gint left, right;
310
311   grid = gtk_grid_new ();
312   gtk_widget_set_margin_bottom (grid, 12);
313   gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
314   gtk_grid_set_column_spacing (GTK_GRID (grid), 4);
315   gtk_container_add (GTK_CONTAINER (cc->priv->palette), grid);
316
317   left = 0;
318   right = colors_per_line - 1;
319   if (gtk_widget_get_direction (GTK_WIDGET (cc)) == GTK_TEXT_DIR_RTL)
320     {
321       i = left;
322       left = right;
323       right = i;
324     }
325
326   for (i = 0; i < n_colors; i++)
327     {
328       p = gtk_color_swatch_new ();
329       atk_obj = gtk_widget_get_accessible (p);
330       if (names)
331         {
332           atk_object_set_description (atk_obj,
333                                       g_dpgettext2 (GETTEXT_PACKAGE, "Color name", names[i]));
334         }
335       else
336         {
337           gchar *text, *name;
338
339           name = accessible_color_name (&colors[i]);
340           text = g_strdup_printf (_("Color: %s"), name);
341           atk_object_set_description (atk_obj, text);
342           g_free (text);
343           g_free (name);
344         }
345       gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), &colors[i]);
346       connect_swatch_signals (p, cc);
347
348       line = i / colors_per_line;
349       pos = i % colors_per_line;
350
351       if (horizontal)
352         {
353             if (pos == left)
354               gtk_style_context_add_class (gtk_widget_get_style_context (p), GTK_STYLE_CLASS_LEFT);
355             else if (pos == right)
356               gtk_style_context_add_class (gtk_widget_get_style_context (p), GTK_STYLE_CLASS_RIGHT);
357
358             gtk_grid_attach (GTK_GRID (grid), p, pos, line, 1, 1);
359         }
360       else
361         {
362           if (pos == 0)
363             gtk_style_context_add_class (gtk_widget_get_style_context (p), GTK_STYLE_CLASS_TOP);
364           else if (pos == colors_per_line - 1)
365             gtk_style_context_add_class (gtk_widget_get_style_context (p), GTK_STYLE_CLASS_BOTTOM);
366
367           gtk_grid_attach (GTK_GRID (grid), p, line, pos, 1, 1);
368        }
369     }
370
371   gtk_widget_show_all (grid);
372 }
373
374 static void
375 remove_default_palette (GtkColorChooserWidget *cc)
376 {
377   GList *children, *l;
378   GtkWidget *widget;
379
380   if (!cc->priv->has_default_palette)
381     return;
382
383   children = gtk_container_get_children (GTK_CONTAINER (cc->priv->palette));
384   for (l = children; l; l = l->next)
385     {
386       widget = l->data;
387       if (widget == cc->priv->custom_label || widget == cc->priv->custom)
388         continue;
389       gtk_container_remove (GTK_CONTAINER (cc->priv->palette), widget);
390     }
391   g_list_free (children);
392
393   cc->priv->has_default_palette = FALSE;
394 }
395
396 static void
397 add_default_palette (GtkColorChooserWidget *cc)
398 {
399   const gchar *default_colors[9][3] = {
400     { "#ef2929", "#cc0000", "#a40000" }, /* Scarlet Red */
401     { "#fcaf3e", "#f57900", "#ce5c00" }, /* Orange */
402     { "#fce94f", "#edd400", "#c4a000" }, /* Butter */
403     { "#8ae234", "#73d216", "#4e9a06" }, /* Chameleon */
404     { "#729fcf", "#3465a4", "#204a87" }, /* Sky Blue */
405     { "#ad7fa8", "#75507b", "#5c3566" }, /* Plum */
406     { "#e9b96e", "#c17d11", "#8f5902" }, /* Chocolate */
407     { "#888a85", "#555753", "#2e3436" }, /* Aluminum 1 */
408     { "#eeeeec", "#d3d7cf", "#babdb6" }  /* Aluminum 2 */
409   };
410   const gchar *color_names[] = {
411     NC_("Color name", "Light Scarlet Red"),
412     NC_("Color name", "Scarlet Red"),
413     NC_("Color name", "Dark Scarlet Red"),
414     NC_("Color name", "Light Orange"),
415     NC_("Color name", "Orange"),
416     NC_("Color name", "Dark Orange"),
417     NC_("Color name", "Light Butter"),
418     NC_("Color name", "Butter"),
419     NC_("Color name", "Dark Butter"),
420     NC_("Color name", "Light Chameleon"),
421     NC_("Color name", "Chameleon"),
422     NC_("Color name", "Dark Chameleon"),
423     NC_("Color name", "Light Sky Blue"),
424     NC_("Color name", "Sky Blue"),
425     NC_("Color name", "Dark Sky Blue"),
426     NC_("Color name", "Light Plum"),
427     NC_("Color name", "Plum"),
428     NC_("Color name", "Dark Plum"),
429     NC_("Color name", "Light Chocolate"),
430     NC_("Color name", "Chocolate"),
431     NC_("Color name", "Dark Chocolate"),
432     NC_("Color name", "Light Aluminum 1"),
433     NC_("Color name", "Aluminum 1"),
434     NC_("Color name", "Dark Aluminum 1"),
435     NC_("Color name", "Light Aluminum 2"),
436     NC_("Color name", "Aluminum 2"),
437     NC_("Color name", "Dark Aluminum 2")
438   };
439   const gchar *default_grays[9] = {
440     "#000000", /* black */
441     "#2e3436", /* very dark gray */
442     "#555753", /* darker gray */
443     "#888a85", /* dark gray */
444     "#babdb6", /* medium gray */
445     "#d3d7cf", /* light gray */
446     "#eeeeec", /* lighter gray */
447     "#f3f3f3", /* very light gray */
448     "#ffffff"  /* white */
449   };
450   const gchar *gray_names[] = {
451     NC_("Color name", "Black"),
452     NC_("Color name", "Very Dark Gray"),
453     NC_("Color name", "Darker Gray"),
454     NC_("Color name", "Dark Gray"),
455     NC_("Color name", "Medium Gray"),
456     NC_("Color name", "Light Gray"),
457     NC_("Color name", "Lighter Gray"),
458     NC_("Color name", "Very Light Gray"),
459     NC_("Color name", "White")
460   };
461   GdkRGBA colors[9*3];
462   gint i, j;
463
464   for (i = 0; i < 9; i++)
465     for (j = 0; j < 3; j++)
466       gdk_rgba_parse (&colors[i*3 + j], default_colors[i][j]);
467
468   add_palette (cc, FALSE, 3, 9*3, colors, color_names);
469
470   for (i = 0; i < 9; i++)
471     gdk_rgba_parse (&colors[i], default_grays[i]);
472
473   add_palette (cc, TRUE, 9, 9, colors, gray_names);
474
475   cc->priv->has_default_palette = TRUE;
476 }
477
478 static void
479 gtk_color_chooser_widget_init (GtkColorChooserWidget *cc)
480 {
481   GtkWidget *box;
482   GtkWidget *p;
483   GtkWidget *button;
484   GtkWidget *label;
485   gint i;
486   GdkRGBA color;
487   GVariant *variant;
488   GVariantIter iter;
489   gboolean selected;
490   AtkObject *atk_obj;
491   gchar *text, *name;
492
493   cc->priv = G_TYPE_INSTANCE_GET_PRIVATE (cc, GTK_TYPE_COLOR_CHOOSER_WIDGET, GtkColorChooserWidgetPrivate);
494
495   cc->priv->use_alpha = TRUE;
496
497   gtk_orientable_set_orientation (GTK_ORIENTABLE (cc), GTK_ORIENTATION_VERTICAL);
498   cc->priv->palette = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
499   gtk_container_add (GTK_CONTAINER (cc), cc->priv->palette);
500
501   add_default_palette (cc);
502
503   cc->priv->custom = box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
504   g_object_set (box, "margin-top", 12, NULL);
505   gtk_box_pack_end (GTK_BOX (cc->priv->palette), box, FALSE, TRUE, 0);
506
507   /* translators: label for the custom section in the color chooser */
508   cc->priv->custom_label = label = gtk_label_new (_("Custom"));
509   gtk_widget_set_halign (label, GTK_ALIGN_START);
510   gtk_box_pack_end (GTK_BOX (cc->priv->palette), label, FALSE, TRUE, 0);
511
512   cc->priv->button = button = gtk_color_swatch_new ();
513   atk_obj = gtk_widget_get_accessible (button);
514   atk_object_set_role (atk_obj, ATK_ROLE_PUSH_BUTTON);
515   atk_object_set_description (atk_obj, _("Create custom color"));
516   connect_button_signals (button, cc);
517   gtk_color_swatch_set_icon (GTK_COLOR_SWATCH (button), "list-add-symbolic");
518   gtk_container_add (GTK_CONTAINER (box), button);
519
520   cc->priv->settings = g_settings_new_with_path ("org.gtk.Settings.ColorChooser",
521                                                  "/org/gtk/settings/color-chooser/");
522   variant = g_settings_get_value (cc->priv->settings, "custom-colors");
523   g_variant_iter_init (&iter, variant);
524   i = 0;
525   p = NULL;
526   while (g_variant_iter_loop (&iter, "(dddd)", &color.red, &color.green, &color.blue, &color.alpha))
527     {
528       i++;
529       p = gtk_color_swatch_new ();
530       gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), &color);
531       gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (p), TRUE);
532       atk_obj = gtk_widget_get_accessible (p);
533       name = accessible_color_name (&color);
534       text = g_strdup_printf (_("Custom color %d: %s"), i, name);
535       atk_object_set_description (atk_obj, text);
536       g_free (text);
537       g_free (name);
538       connect_custom_signals (p, cc);
539       gtk_container_add (GTK_CONTAINER (box), p);
540
541       if (i == 8)
542         break;
543     }
544   g_variant_unref (variant);
545
546   cc->priv->editor = gtk_color_editor_new ();
547   gtk_widget_set_halign (cc->priv->editor, GTK_ALIGN_CENTER);
548   gtk_widget_set_hexpand (cc->priv->editor, TRUE);
549
550   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
551   gtk_container_add (GTK_CONTAINER (cc), box);
552   gtk_container_add (GTK_CONTAINER (box), cc->priv->editor);
553
554   g_settings_get (cc->priv->settings, "selected-color", "(bdddd)",
555                   &selected,
556                   &color.red, &color.green, &color.blue, &color.alpha);
557   if (selected)
558     gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc), &color);
559
560   gtk_widget_show_all (GTK_WIDGET (cc));
561   gtk_widget_hide (GTK_WIDGET (cc->priv->editor));
562   gtk_widget_hide (GTK_WIDGET (cc));
563
564   gtk_widget_set_no_show_all (cc->priv->palette, TRUE);
565   gtk_widget_set_no_show_all (cc->priv->editor, TRUE);
566
567   cc->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
568   gtk_size_group_add_widget (cc->priv->size_group, cc->priv->palette);
569   gtk_size_group_add_widget (cc->priv->size_group, box);
570 }
571
572 /* GObject implementation {{{1 */
573
574 static void
575 gtk_color_chooser_widget_get_property (GObject    *object,
576                                        guint       prop_id,
577                                        GValue     *value,
578                                        GParamSpec *pspec)
579 {
580   GtkColorChooserWidget *cw = GTK_COLOR_CHOOSER_WIDGET (object);
581   GtkColorChooser *cc = GTK_COLOR_CHOOSER (object);
582
583   switch (prop_id)
584     {
585     case PROP_RGBA:
586       {
587         GdkRGBA color;
588
589         gtk_color_chooser_get_rgba (cc, &color);
590         g_value_set_boxed (value, &color);
591       }
592       break;
593     case PROP_USE_ALPHA:
594       g_value_set_boolean (value, cw->priv->use_alpha);
595       break;
596     case PROP_SHOW_EDITOR:
597       g_value_set_boolean (value, gtk_widget_get_visible (cw->priv->editor));
598       break;
599     default:
600       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
601       break;
602     }
603 }
604
605 static void
606 gtk_color_chooser_widget_set_property (GObject      *object,
607                                        guint         prop_id,
608                                        const GValue *value,
609                                        GParamSpec   *pspec)
610 {
611   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object);
612
613   switch (prop_id)
614     {
615     case PROP_RGBA:
616       gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc),
617                                   g_value_get_boxed (value));
618       break;
619     case PROP_USE_ALPHA:
620       gtk_color_chooser_widget_set_use_alpha (cc,
621                                               g_value_get_boolean (value));
622       break;
623     case PROP_SHOW_EDITOR:
624       gtk_color_chooser_widget_set_show_editor (cc,
625                                                 g_value_get_boolean (value));
626       break;
627     default:
628       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
629       break;
630     }
631 }
632
633 static void
634 gtk_color_chooser_widget_finalize (GObject *object)
635 {
636   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object);
637
638   g_object_unref (cc->priv->size_group);
639   g_object_unref (cc->priv->settings);
640
641   G_OBJECT_CLASS (gtk_color_chooser_widget_parent_class)->finalize (object);
642 }
643
644 static void
645 gtk_color_chooser_widget_class_init (GtkColorChooserWidgetClass *class)
646 {
647   GObjectClass *object_class = G_OBJECT_CLASS (class);
648
649   object_class->get_property = gtk_color_chooser_widget_get_property;
650   object_class->set_property = gtk_color_chooser_widget_set_property;
651   object_class->finalize = gtk_color_chooser_widget_finalize;
652
653   g_object_class_override_property (object_class, PROP_RGBA, "rgba");
654   g_object_class_override_property (object_class, PROP_USE_ALPHA, "use-alpha");
655
656   /**
657    * GtkColorChooserWidget:show-editor:
658    *
659    * The ::show-editor property is %TRUE when the color chooser
660    * is showing the single-color editor. It can be set to switch
661    * the color chooser into single-color editing mode.
662    *
663    * Since: 3.4
664    */
665   g_object_class_install_property (object_class, PROP_SHOW_EDITOR,
666       g_param_spec_boolean ("show-editor", P_("Show editor"), P_("Show editor"),
667                             FALSE, GTK_PARAM_READWRITE));
668
669   g_type_class_add_private (object_class, sizeof (GtkColorChooserWidgetPrivate));
670 }
671
672 /* GtkColorChooser implementation {{{1 */
673
674 static void
675 gtk_color_chooser_widget_get_rgba (GtkColorChooser *chooser,
676                                    GdkRGBA         *color)
677 {
678   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser);
679
680   if (gtk_widget_get_visible (cc->priv->editor))
681     gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cc->priv->editor), color);
682   else if (cc->priv->current)
683     gtk_color_swatch_get_rgba (cc->priv->current, color);
684   else
685     {
686       color->red = 1.0;
687       color->green = 1.0;
688       color->blue = 1.0;
689       color->alpha = 1.0;
690     }
691
692   if (!cc->priv->use_alpha)
693     color->alpha = 1.0;
694 }
695
696 static void
697 add_custom_color (GtkColorChooserWidget *cc,
698                   const GdkRGBA         *color)
699 {
700   GtkWidget *last;
701   GtkWidget *p;
702   GList *children;
703
704   children = gtk_container_get_children (GTK_CONTAINER (cc->priv->custom));
705   if (g_list_length (children) >= 8)
706     {
707       last = g_list_last (children)->data;
708       gtk_widget_destroy (last);
709     }
710
711   g_list_free (children);
712
713   p = gtk_color_swatch_new ();
714   gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), color);
715   gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (p), TRUE);
716   connect_custom_signals (p, cc);
717
718   gtk_container_add (GTK_CONTAINER (cc->priv->custom), p);
719   gtk_box_reorder_child (GTK_BOX (cc->priv->custom), p, 1);
720   gtk_widget_show (p);
721
722   select_swatch (cc, GTK_COLOR_SWATCH (p));
723   save_custom_colors (cc);
724 }
725
726 static void
727 gtk_color_chooser_widget_set_rgba (GtkColorChooser *chooser,
728                                    const GdkRGBA   *color)
729 {
730   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser);
731   GList *children, *l;
732   GList *palettes, *p;
733   GtkColorSwatch *swatch;
734   GtkWidget *w;
735   GdkRGBA c;
736
737   palettes = gtk_container_get_children (GTK_CONTAINER (cc->priv->palette));
738   for (p = palettes; p; p = p->next)
739     {
740       w = p->data;
741       if (!GTK_IS_GRID (w) && !GTK_IS_BOX (w))
742         continue;
743
744       children = gtk_container_get_children (GTK_CONTAINER (w));
745       for (l = children; l; l = l->next)
746         {
747           swatch = l->data;
748           gtk_color_swatch_get_rgba (swatch, &c);
749           if (!cc->priv->use_alpha)
750             c.alpha = color->alpha;
751           if (gdk_rgba_equal (color, &c))
752             {
753               select_swatch (cc, swatch);
754               g_list_free (children);
755               return;
756             }
757         }
758       g_list_free (children);
759     }
760   g_list_free (palettes);
761
762   add_custom_color (cc, color);
763 }
764
765 static void
766 gtk_color_chooser_widget_add_palette (GtkColorChooser *chooser,
767                                       gboolean         horizontal,
768                                       gint             colors_per_line,
769                                       gint             n_colors,
770                                       GdkRGBA         *colors)
771 {
772   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser);
773
774   remove_default_palette (cc);
775   add_palette (cc, horizontal, colors_per_line, n_colors, colors, NULL);
776 }
777
778 static void
779 gtk_color_chooser_widget_iface_init (GtkColorChooserInterface *iface)
780 {
781   iface->get_rgba = gtk_color_chooser_widget_get_rgba;
782   iface->set_rgba = gtk_color_chooser_widget_set_rgba;
783   iface->add_palette = gtk_color_chooser_widget_add_palette;
784 }
785
786 /* Public API {{{1 */
787
788 /**
789  * gtk_color_chooser_widget_new:
790  *
791  * Creates a new #GtkColorChooserWidget.
792  *
793  * Returns: a new #GtkColorChooserWidget
794  *
795  * Since: 3.4
796  */
797 GtkWidget *
798 gtk_color_chooser_widget_new (void)
799 {
800   return g_object_new (GTK_TYPE_COLOR_CHOOSER_WIDGET, NULL);
801 }
802
803 /* vim:set foldmethod=marker: */