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, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "config.h"
20 
21 #include "gtkcolorchooserprivate.h"
22 #include "gtkcolorchooserwidget.h"
23 #include "gtkcoloreditorprivate.h"
24 #include "gtkcolorswatchprivate.h"
25 #include "gtkgrid.h"
26 #include "gtklabel.h"
27 #include "gtkorientable.h"
28 #include "gtkprivate.h"
29 #include "gtkintl.h"
30 #include "gtksizegroup.h"
31 #include "gtkboxlayout.h"
32 #include "gtkwidgetprivate.h"
33 #include "gdkrgbaprivate.h"
34 
35 #include <math.h>
36 
37 /**
38  * GtkColorChooserWidget:
39  *
40  * The `GtkColorChooserWidget` widget lets the user select a color.
41  *
42  * By default, the chooser presents a predefined palette of colors,
43  * plus a small number of settable custom colors. It is also possible
44  * to select a different color with the single-color editor.
45  *
46  * To enter the single-color editing mode, use the context menu of any
47  * color of the palette, or use the '+' button to add a new custom color.
48  *
49  * The chooser automatically remembers the last selection, as well
50  * as custom colors.
51  *
52  * To create a `GtkColorChooserWidget`, use [ctor@Gtk.ColorChooserWidget.new].
53  *
54  * To change the initially selected color, use
55  * [method@Gtk.ColorChooser.set_rgba]. To get the selected color use
56  * [method@Gtk.ColorChooser.get_rgba].
57  *
58  * The `GtkColorChooserWidget` is used in the [class@Gtk.ColorChooserDialog]
59  * to provide a dialog for selecting colors.
60  *
61  * # CSS names
62  *
63  * `GtkColorChooserWidget` has a single CSS node with name colorchooser.
64  */
65 
66 typedef struct _GtkColorChooserWidgetClass   GtkColorChooserWidgetClass;
67 
68 struct _GtkColorChooserWidget
69 {
70   GtkWidget parent_instance;
71 
72   GtkWidget *palette;
73   GtkWidget *editor;
74   GtkSizeGroup *size_group;
75 
76   GtkWidget *custom_label;
77   GtkWidget *custom;
78 
79   GtkWidget *button;
80   GtkColorSwatch *current;
81 
82   gboolean use_alpha;
83   gboolean has_default_palette;
84 
85   GSettings *settings;
86 
87   int max_custom;
88 };
89 
90 struct _GtkColorChooserWidgetClass
91 {
92   GtkWidgetClass parent_class;
93 };
94 
95 enum
96 {
97   PROP_ZERO,
98   PROP_RGBA,
99   PROP_USE_ALPHA,
100   PROP_SHOW_EDITOR
101 };
102 
103 static void gtk_color_chooser_widget_iface_init (GtkColorChooserInterface *iface);
104 
G_DEFINE_TYPE_WITH_CODE(GtkColorChooserWidget,gtk_color_chooser_widget,GTK_TYPE_WIDGET,G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER,gtk_color_chooser_widget_iface_init))105 G_DEFINE_TYPE_WITH_CODE (GtkColorChooserWidget, gtk_color_chooser_widget, GTK_TYPE_WIDGET,
106                          G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER,
107                                                 gtk_color_chooser_widget_iface_init))
108 
109 static void
110 select_swatch (GtkColorChooserWidget *cc,
111                GtkColorSwatch        *swatch)
112 {
113   GdkRGBA color;
114   double red, green, blue, alpha;
115 
116   if (cc->current == swatch)
117     return;
118 
119   if (cc->current != NULL)
120     gtk_widget_unset_state_flags (GTK_WIDGET (cc->current), GTK_STATE_FLAG_SELECTED);
121 
122   gtk_widget_set_state_flags (GTK_WIDGET (swatch), GTK_STATE_FLAG_SELECTED, FALSE);
123   cc->current = swatch;
124 
125   gtk_color_swatch_get_rgba (swatch, &color);
126 
127   red = color.red;
128   green = color.green;
129   blue = color.blue;
130   alpha = color.alpha;
131   g_settings_set (cc->settings, "selected-color", "(bdddd)",
132                   TRUE, red, green, blue, alpha);
133 
134   if (gtk_widget_get_visible (GTK_WIDGET (cc->editor)))
135     gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->editor), &color);
136   else
137     g_object_notify (G_OBJECT (cc), "rgba");
138 }
139 
140 static void
swatch_selected(GtkColorSwatch * swatch,GtkStateFlags previous,GtkColorChooserWidget * cc)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
connect_swatch_signals(GtkWidget * p,gpointer data)154 connect_swatch_signals (GtkWidget *p,
155                         gpointer   data)
156 {
157   g_signal_connect (p, "state-flags-changed", G_CALLBACK (swatch_selected), data);
158 }
159 
160 static void
connect_button_signals(GtkWidget * p,gpointer data)161 connect_button_signals (GtkWidget *p,
162                         gpointer   data)
163 {
164 //  g_signal_connect (p, "activate", G_CALLBACK (button_activate), data);
165 }
166 
167 static void
save_custom_colors(GtkColorChooserWidget * cc)168 save_custom_colors (GtkColorChooserWidget *cc)
169 {
170   GVariantBuilder builder;
171   GVariant *variant;
172   GdkRGBA color;
173   GtkWidget *child;
174   gboolean first;
175 
176   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(dddd)"));
177 
178   for (child = gtk_widget_get_first_child (cc->custom), first = TRUE;
179        child != NULL;
180        child = gtk_widget_get_next_sibling (child), first = FALSE)
181     {
182       if (first)
183         continue;
184 
185       if (gtk_color_swatch_get_rgba (GTK_COLOR_SWATCH (child), &color))
186         {
187           double red, green, blue, alpha;
188 
189           red = color.red;
190           green = color.green;
191           blue = color.blue;
192           alpha = color.alpha;
193           g_variant_builder_add (&builder, "(dddd)", red, green, blue, alpha);
194         }
195     }
196 
197   variant = g_variant_builder_end (&builder);
198   g_settings_set_value (cc->settings, "custom-colors", variant);
199 }
200 
201 static void
connect_custom_signals(GtkWidget * p,gpointer data)202 connect_custom_signals (GtkWidget *p,
203                         gpointer   data)
204 {
205   connect_swatch_signals (p, data);
206   g_signal_connect_swapped (p, "notify::rgba",
207                             G_CALLBACK (save_custom_colors), data);
208 }
209 
210 static void
gtk_color_chooser_widget_set_use_alpha(GtkColorChooserWidget * cc,gboolean use_alpha)211 gtk_color_chooser_widget_set_use_alpha (GtkColorChooserWidget *cc,
212                                         gboolean               use_alpha)
213 {
214   GtkWidget *child;
215   GtkWidget *grid;
216 
217   if (cc->use_alpha == use_alpha)
218     return;
219 
220   cc->use_alpha = use_alpha;
221   gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (cc->editor), use_alpha);
222 
223   for (grid = gtk_widget_get_first_child (cc->palette);
224        grid != NULL;
225        grid = gtk_widget_get_next_sibling (grid))
226     {
227       for (child = gtk_widget_get_first_child (grid);
228            child != NULL;
229            child = gtk_widget_get_next_sibling (child))
230         {
231           if (GTK_IS_COLOR_SWATCH (child))
232             gtk_color_swatch_set_use_alpha (GTK_COLOR_SWATCH (child), use_alpha);
233         }
234     }
235 
236   gtk_widget_queue_draw (GTK_WIDGET (cc));
237   g_object_notify (G_OBJECT (cc), "use-alpha");
238 }
239 
240 static void
gtk_color_chooser_widget_set_show_editor(GtkColorChooserWidget * cc,gboolean show_editor)241 gtk_color_chooser_widget_set_show_editor (GtkColorChooserWidget *cc,
242                                           gboolean               show_editor)
243 {
244   if (show_editor)
245     {
246       GdkRGBA color = { 0.75, 0.25, 0.25, 1.0 };
247 
248       if (cc->current)
249         gtk_color_swatch_get_rgba (cc->current, &color);
250       gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->editor), &color);
251     }
252 
253   gtk_widget_set_visible (cc->editor, show_editor);
254   gtk_widget_set_visible (cc->palette, !show_editor);
255 }
256 
257 static void
update_from_editor(GtkColorEditor * editor,GParamSpec * pspec,GtkColorChooserWidget * widget)258 update_from_editor (GtkColorEditor        *editor,
259                     GParamSpec            *pspec,
260                     GtkColorChooserWidget *widget)
261 {
262   if (gtk_widget_get_visible (GTK_WIDGET (editor)))
263     g_object_notify (G_OBJECT (widget), "rgba");
264 }
265 
266 /* UI construction {{{1 */
267 
268 static void
remove_palette(GtkColorChooserWidget * cc)269 remove_palette (GtkColorChooserWidget *cc)
270 {
271   GList *children, *l;
272   GtkWidget *widget;
273 
274   if (cc->current != NULL &&
275       gtk_widget_get_parent (GTK_WIDGET (cc->current)) != cc->custom)
276     cc->current = NULL;
277 
278   children = NULL;
279   for (widget = gtk_widget_get_first_child (cc->palette);
280        widget != NULL;
281        widget = gtk_widget_get_next_sibling (widget))
282     children = g_list_prepend (children, widget);
283 
284   for (l = children; l; l = l->next)
285     {
286       widget = l->data;
287       if (widget == cc->custom_label || widget == cc->custom)
288         continue;
289       gtk_box_remove (GTK_BOX (cc->palette), widget);
290     }
291   g_list_free (children);
292 }
293 
294 static guint
scale_round(double value,double scale)295 scale_round (double value,
296              double scale)
297 {
298   value = floor (value * scale + 0.5);
299   value = MAX (value, 0);
300   value = MIN (value, scale);
301   return (guint)value;
302 }
303 
304 static char *
accessible_color_name(GdkRGBA * color)305 accessible_color_name (GdkRGBA *color)
306 {
307   if (color->alpha < 1.0)
308     return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%, Alpha %d%%"),
309                             scale_round (color->red, 100),
310                             scale_round (color->green, 100),
311                             scale_round (color->blue, 100),
312                             scale_round (color->alpha, 100));
313   else
314     return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%"),
315                             scale_round (color->red, 100),
316                             scale_round (color->green, 100),
317                             scale_round (color->blue, 100));
318 }
319 
320 static void
add_palette(GtkColorChooserWidget * cc,GtkOrientation orientation,int colors_per_line,int n_colors,GdkRGBA * colors,const char ** names)321 add_palette (GtkColorChooserWidget  *cc,
322              GtkOrientation          orientation,
323              int                     colors_per_line,
324              int                     n_colors,
325              GdkRGBA                *colors,
326              const char            **names)
327 {
328   GtkWidget *grid;
329   GtkWidget *p;
330   int line, pos;
331   int i;
332   int left, right;
333 
334   if (colors == NULL)
335     {
336       remove_palette (cc);
337       return;
338     }
339 
340   grid = gtk_grid_new ();
341   gtk_widget_set_margin_bottom (grid, 12);
342   gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
343   gtk_grid_set_column_spacing (GTK_GRID (grid), 4);
344   gtk_box_append (GTK_BOX (cc->palette), grid);
345 
346   left = 0;
347   right = colors_per_line - 1;
348   if (gtk_widget_get_direction (GTK_WIDGET (cc)) == GTK_TEXT_DIR_RTL)
349     {
350       i = left;
351       left = right;
352       right = i;
353     }
354 
355   for (i = 0; i < n_colors; i++)
356     {
357       p = gtk_color_swatch_new ();
358       if (names)
359         {
360           gtk_accessible_update_property (GTK_ACCESSIBLE (p),
361                                           GTK_ACCESSIBLE_PROPERTY_LABEL,
362                                           g_dpgettext2 (GETTEXT_PACKAGE, "Color name", names[i]),
363                                           -1);
364         }
365       else
366         {
367           char *name;
368           char *text;
369 
370           name = accessible_color_name (&colors[i]);
371           text = g_strdup_printf (_("Color: %s"), name);
372           gtk_accessible_update_property (GTK_ACCESSIBLE (p),
373                                           GTK_ACCESSIBLE_PROPERTY_LABEL, text,
374                                           -1);
375           g_free (name);
376           g_free (text);
377         }
378       gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), &colors[i]);
379       connect_swatch_signals (p, cc);
380 
381       line = i / colors_per_line;
382       pos = i % colors_per_line;
383 
384       if (orientation == GTK_ORIENTATION_HORIZONTAL)
385         {
386           if (pos == left)
387             gtk_widget_add_css_class (p, "left");
388           else if (pos == right)
389             gtk_widget_add_css_class (p, "right");
390 
391           gtk_grid_attach (GTK_GRID (grid), p, pos, line, 1, 1);
392         }
393       else
394         {
395           if (pos == 0)
396             gtk_widget_add_css_class (p, "top");
397           else if (pos == colors_per_line - 1)
398             gtk_widget_add_css_class (p, "bottom");
399 
400           gtk_grid_attach (GTK_GRID (grid), p, line, pos, 1, 1);
401        }
402     }
403 
404   if (orientation == GTK_ORIENTATION_HORIZONTAL)
405     cc->max_custom = MAX (cc->max_custom, colors_per_line);
406   else
407     cc->max_custom = MAX (cc->max_custom, n_colors / colors_per_line);
408 }
409 
410 static void
remove_default_palette(GtkColorChooserWidget * cc)411 remove_default_palette (GtkColorChooserWidget *cc)
412 {
413   if (!cc->has_default_palette)
414     return;
415 
416   remove_palette (cc);
417   cc->has_default_palette = FALSE;
418   cc->max_custom = 0;
419 }
420 
421 static void
add_default_palette(GtkColorChooserWidget * cc)422 add_default_palette (GtkColorChooserWidget *cc)
423 {
424   GdkRGBA colors[9*5] = {
425     GDK_RGBA("99c1f1"), GDK_RGBA("62a0ea"), GDK_RGBA("3584e4"), GDK_RGBA("1c71d8"), GDK_RGBA("1a5fb4"), /* Blue */
426     GDK_RGBA("8ff0a4"), GDK_RGBA("57e389"), GDK_RGBA("33d17a"), GDK_RGBA("2ec27e"), GDK_RGBA("26a269"), /* Green */
427     GDK_RGBA("f9f06b"), GDK_RGBA("f8e45c"), GDK_RGBA("f6d32d"), GDK_RGBA("f5c211"), GDK_RGBA("e5a50a"), /* Yellow */
428     GDK_RGBA("ffbe6f"), GDK_RGBA("ffa348"), GDK_RGBA("ff7800"), GDK_RGBA("e66100"), GDK_RGBA("c64600"), /* Orange */
429     GDK_RGBA("f66151"), GDK_RGBA("ed333b"), GDK_RGBA("e01b24"), GDK_RGBA("c01c28"), GDK_RGBA("a51d2d"), /* Red */
430     GDK_RGBA("dc8add"), GDK_RGBA("c061cb"), GDK_RGBA("9141ac"), GDK_RGBA("813d9c"), GDK_RGBA("613583"), /* Purple */
431     GDK_RGBA("cdab8f"), GDK_RGBA("b5835a"), GDK_RGBA("986a44"), GDK_RGBA("865e3c"), GDK_RGBA("63452c"), /* Brown */
432     GDK_RGBA("ffffff"), GDK_RGBA("f6f5f4"), GDK_RGBA("deddda"), GDK_RGBA("c0bfbc"), GDK_RGBA("9a9996"), /* Light */
433     GDK_RGBA("77767b"), GDK_RGBA("5e5c64"), GDK_RGBA("3d3846"), GDK_RGBA("241f31"), GDK_RGBA("000000")  /* Dark */
434   };
435   const char *color_names[] = {
436     NC_("Color name", "Very Light Blue"),
437     NC_("Color name", "Light Blue"),
438     NC_("Color name", "Blue"),
439     NC_("Color name", "Dark Blue"),
440     NC_("Color name", "Very Dark Blue"),
441     NC_("Color name", "Very Light Green"),
442     NC_("Color name", "Light Green"),
443     NC_("Color name", "Green"),
444     NC_("Color name", "Dark Green"),
445     NC_("Color name", "Very Dark Green"),
446     NC_("Color name", "Very Light Yellow"),
447     NC_("Color name", "Light Yellow"),
448     NC_("Color name", "Yellow"),
449     NC_("Color name", "Dark Yellow"),
450     NC_("Color name", "Very Dark Yellow"),
451     NC_("Color name", "Very Light Orange"),
452     NC_("Color name", "Light Orange"),
453     NC_("Color name", "Orange"),
454     NC_("Color name", "Dark Orange"),
455     NC_("Color name", "Very Dark Orange"),
456     NC_("Color name", "Very Light Red"),
457     NC_("Color name", "Light Red"),
458     NC_("Color name", "Red"),
459     NC_("Color name", "Dark Red"),
460     NC_("Color name", "Very Dark Red"),
461     NC_("Color name", "Very Light Purple"),
462     NC_("Color name", "Light Purple"),
463     NC_("Color name", "Purple"),
464     NC_("Color name", "Dark Purple"),
465     NC_("Color name", "Very Dark Purple"),
466     NC_("Color name", "Very Light Brown"),
467     NC_("Color name", "Light Brown"),
468     NC_("Color name", "Brown"),
469     NC_("Color name", "Dark Brown"),
470     NC_("Color name", "Very Dark Brown"),
471     NC_("Color name", "White"),
472     NC_("Color name", "Light Gray 1"),
473     NC_("Color name", "Light Gray 2"),
474     NC_("Color name", "Light Gray 3"),
475     NC_("Color name", "Light Gray 4"),
476     NC_("Color name", "Dark Gray 1"),
477     NC_("Color name", "Dark Gray 2"),
478     NC_("Color name", "Dark Gray 3"),
479     NC_("Color name", "Dark Gray 4"),
480     NC_("Color name", "Black"),
481   };
482 
483   add_palette (cc, GTK_ORIENTATION_VERTICAL, 5, 9*5, colors, color_names);
484 
485   cc->has_default_palette = TRUE;
486 }
487 
488 static void
gtk_color_chooser_widget_activate_color_customize(GtkWidget * widget,const char * name,GVariant * parameter)489 gtk_color_chooser_widget_activate_color_customize (GtkWidget  *widget,
490                                                    const char *name,
491                                                    GVariant   *parameter)
492 {
493   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (widget);
494   double red, green, blue, alpha;
495   GdkRGBA color;
496 
497   g_variant_get (parameter, "(dddd)", &red, &green, &blue, &alpha);
498   color.red = red;
499   color.green = green;
500   color.blue = blue;
501   color.alpha = alpha;
502 
503   gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->editor), &color);
504 
505   gtk_widget_hide (cc->palette);
506   gtk_widget_show (cc->editor);
507   g_object_notify (G_OBJECT (cc), "show-editor");
508 }
509 
510 static void
gtk_color_chooser_widget_activate_color_select(GtkWidget * widget,const char * name,GVariant * parameter)511 gtk_color_chooser_widget_activate_color_select (GtkWidget  *widget,
512                                                 const char *name,
513                                                 GVariant   *parameter)
514 {
515   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (widget);
516   GdkRGBA color;
517   double red, green, blue, alpha;
518 
519   g_variant_get (parameter, "(dddd)", &red, &green, &blue, &alpha);
520   color.red = red;
521   color.green = green;
522   color.blue = blue;
523   color.alpha = alpha;
524 
525   _gtk_color_chooser_color_activated (GTK_COLOR_CHOOSER (cc), &color);
526 }
527 
528 static void
gtk_color_chooser_widget_init(GtkColorChooserWidget * cc)529 gtk_color_chooser_widget_init (GtkColorChooserWidget *cc)
530 {
531   GtkWidget *box;
532   GtkWidget *p;
533   GtkWidget *button;
534   GtkWidget *label;
535   int i;
536   double color[4];
537   GdkRGBA rgba;
538   GVariant *variant;
539   GVariantIter iter;
540   gboolean selected;
541   char *name;
542   char *text;
543 
544   cc->use_alpha = TRUE;
545 
546   cc->palette = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
547   gtk_widget_set_parent (cc->palette, GTK_WIDGET (cc));
548 
549   add_default_palette (cc);
550 
551   /* translators: label for the custom section in the color chooser */
552   cc->custom_label = label = gtk_label_new (_("Custom"));
553   gtk_widget_set_halign (label, GTK_ALIGN_START);
554   gtk_box_append (GTK_BOX (cc->palette), label);
555 
556   cc->custom = box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
557   g_object_set (box, "margin-top", 12, NULL);
558   gtk_box_append (GTK_BOX (cc->palette), box);
559 
560   cc->button = button = gtk_color_swatch_new ();
561   gtk_widget_set_name (button, "add-color-button");
562   connect_button_signals (button, cc);
563   gtk_color_swatch_set_icon (GTK_COLOR_SWATCH (button), "list-add-symbolic");
564   gtk_color_swatch_set_selectable (GTK_COLOR_SWATCH (button), FALSE);
565   gtk_box_append (GTK_BOX (box), button);
566 
567   cc->settings = g_settings_new ("org.gtk.gtk4.Settings.ColorChooser");
568   variant = g_settings_get_value (cc->settings, I_("custom-colors"));
569   g_variant_iter_init (&iter, variant);
570   i = 0;
571   p = NULL;
572   while (g_variant_iter_loop (&iter, "(dddd)", &color[0], &color[1], &color[2], &color[3]))
573     {
574       i++;
575       p = gtk_color_swatch_new ();
576 
577       rgba.red = color[0];
578       rgba.green = color[1];
579       rgba.blue = color[2];
580       rgba.alpha = color[3];
581 
582       gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), &rgba);
583 
584       name = accessible_color_name (&rgba);
585       text = g_strdup_printf (_("Custom color %d: %s"), i, name);
586       gtk_accessible_update_property (GTK_ACCESSIBLE (p),
587                                       GTK_ACCESSIBLE_PROPERTY_LABEL, text,
588                                       -1);
589       g_free (name);
590       g_free (text);
591 
592       gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (p), TRUE);
593       connect_custom_signals (p, cc);
594       gtk_box_append (GTK_BOX (box), p);
595 
596       if (i == 8)
597         break;
598     }
599   g_variant_unref (variant);
600 
601   cc->editor = gtk_color_editor_new ();
602   gtk_widget_set_halign (cc->editor, GTK_ALIGN_CENTER);
603   gtk_widget_set_hexpand (cc->editor, TRUE);
604   g_signal_connect (cc->editor, "notify::rgba",
605                     G_CALLBACK (update_from_editor), cc);
606 
607   gtk_widget_set_parent (cc->editor, GTK_WIDGET (cc));
608 
609   g_settings_get (cc->settings, I_("selected-color"), "(bdddd)",
610                   &selected,
611                   &color[0], &color[1], &color[2], &color[3]);
612   if (selected)
613     {
614       rgba.red = color[0];
615       rgba.green = color[1];
616       rgba.blue = color[2];
617       rgba.alpha = color[3];
618       gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc), &rgba);
619     }
620 
621   gtk_widget_hide (GTK_WIDGET (cc->editor));
622 }
623 
624 /* GObject implementation {{{1 */
625 
626 static void
gtk_color_chooser_widget_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)627 gtk_color_chooser_widget_get_property (GObject    *object,
628                                        guint       prop_id,
629                                        GValue     *value,
630                                        GParamSpec *pspec)
631 {
632   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object);
633 
634   switch (prop_id)
635     {
636     case PROP_RGBA:
637       {
638         GdkRGBA color;
639 
640         gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cc), &color);
641         g_value_set_boxed (value, &color);
642       }
643       break;
644     case PROP_USE_ALPHA:
645       g_value_set_boolean (value, cc->use_alpha);
646       break;
647     case PROP_SHOW_EDITOR:
648       g_value_set_boolean (value, gtk_widget_get_visible (cc->editor));
649       break;
650     default:
651       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
652       break;
653     }
654 }
655 
656 static void
gtk_color_chooser_widget_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)657 gtk_color_chooser_widget_set_property (GObject      *object,
658                                        guint         prop_id,
659                                        const GValue *value,
660                                        GParamSpec   *pspec)
661 {
662   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object);
663 
664   switch (prop_id)
665     {
666     case PROP_RGBA:
667       gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc),
668                                   g_value_get_boxed (value));
669       break;
670     case PROP_USE_ALPHA:
671       gtk_color_chooser_widget_set_use_alpha (cc,
672                                               g_value_get_boolean (value));
673       break;
674     case PROP_SHOW_EDITOR:
675       gtk_color_chooser_widget_set_show_editor (cc,
676                                                 g_value_get_boolean (value));
677       break;
678     default:
679       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
680       break;
681     }
682 }
683 
684 static void
gtk_color_chooser_widget_finalize(GObject * object)685 gtk_color_chooser_widget_finalize (GObject *object)
686 {
687   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object);
688 
689   g_object_unref (cc->settings);
690 
691   gtk_widget_unparent (cc->editor);
692   gtk_widget_unparent (cc->palette);
693 
694   G_OBJECT_CLASS (gtk_color_chooser_widget_parent_class)->finalize (object);
695 }
696 
697 static void
gtk_color_chooser_widget_class_init(GtkColorChooserWidgetClass * class)698 gtk_color_chooser_widget_class_init (GtkColorChooserWidgetClass *class)
699 {
700   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
701   GObjectClass *object_class = G_OBJECT_CLASS (class);
702 
703   object_class->get_property = gtk_color_chooser_widget_get_property;
704   object_class->set_property = gtk_color_chooser_widget_set_property;
705   object_class->finalize = gtk_color_chooser_widget_finalize;
706 
707   widget_class->grab_focus = gtk_widget_grab_focus_child;
708   widget_class->focus = gtk_widget_focus_child;
709 
710   g_object_class_override_property (object_class, PROP_RGBA, "rgba");
711   g_object_class_override_property (object_class, PROP_USE_ALPHA, "use-alpha");
712 
713   /**
714    * GtkColorChooserWidget:show-editor:
715    *
716    * %TRUE when the color chooser is showing the single-color editor.
717    *
718    * It can be set to switch the color chooser into single-color editing mode.
719    */
720   g_object_class_install_property (object_class, PROP_SHOW_EDITOR,
721       g_param_spec_boolean ("show-editor", P_("Show editor"), P_("Show editor"),
722                             FALSE, GTK_PARAM_READWRITE));
723 
724   gtk_widget_class_set_css_name (widget_class, I_("colorchooser"));
725   gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BOX_LAYOUT);
726 
727   /**
728    * GtkColorChooserWidget|color.select:
729    * @red: the red value, between 0 and 1
730    * @green: the green value, between 0 and 1
731    * @blue: the blue value, between 0 and 1
732    * @alpha: the alpha value, between 0 and 1
733    *
734    * Emits the [signal@Gtk.ColorChooser::color-activated] signal for
735    * the given color.
736    */
737   gtk_widget_class_install_action (widget_class, "color.select", "(dddd)",
738                                    gtk_color_chooser_widget_activate_color_select);
739 
740   /**
741    * GtkColorChooserWidget|color.customize:
742    * @red: the red value, between 0 and 1
743    * @green: the green value, between 0 and 1
744    * @blue: the blue value, between 0 and 1
745    * @alpha: the alpha value, between 0 and 1
746    *
747    * Activates the color editor for the given color.
748    */
749   gtk_widget_class_install_action (widget_class, "color.customize", "(dddd)",
750                                    gtk_color_chooser_widget_activate_color_customize);
751 }
752 
753 /* GtkColorChooser implementation {{{1 */
754 
755 static void
gtk_color_chooser_widget_get_rgba(GtkColorChooser * chooser,GdkRGBA * color)756 gtk_color_chooser_widget_get_rgba (GtkColorChooser *chooser,
757                                    GdkRGBA         *color)
758 {
759   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser);
760 
761   if (gtk_widget_get_visible (cc->editor))
762     gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cc->editor), color);
763   else if (cc->current)
764     gtk_color_swatch_get_rgba (cc->current, color);
765   else
766     {
767       color->red = 1.0;
768       color->green = 1.0;
769       color->blue = 1.0;
770       color->alpha = 1.0;
771     }
772 
773   if (!cc->use_alpha)
774     color->alpha = 1.0;
775 }
776 
777 static void
add_custom_color(GtkColorChooserWidget * cc,const GdkRGBA * color)778 add_custom_color (GtkColorChooserWidget *cc,
779                   const GdkRGBA         *color)
780 {
781   GtkWidget *widget;
782   GtkWidget *p;
783   int n;
784 
785   n = 0;
786   for (widget = gtk_widget_get_first_child (cc->custom);
787        widget != NULL;
788        widget = gtk_widget_get_next_sibling (widget))
789     n++;
790 
791   while (n >= cc->max_custom)
792     {
793       GtkWidget *last = gtk_widget_get_last_child (cc->custom);
794 
795       if (last == (GtkWidget *)cc->current)
796         cc->current = NULL;
797 
798       gtk_box_remove (GTK_BOX (cc->custom), last);
799       n--;
800     }
801 
802   p = gtk_color_swatch_new ();
803   gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), color);
804   gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (p), TRUE);
805   connect_custom_signals (p, cc);
806 
807   gtk_box_insert_child_after (GTK_BOX (cc->custom), p, gtk_widget_get_first_child (cc->custom));
808   gtk_widget_show (p);
809 
810   select_swatch (cc, GTK_COLOR_SWATCH (p));
811   save_custom_colors (cc);
812 }
813 
814 static void
gtk_color_chooser_widget_set_rgba(GtkColorChooser * chooser,const GdkRGBA * color)815 gtk_color_chooser_widget_set_rgba (GtkColorChooser *chooser,
816                                    const GdkRGBA   *color)
817 {
818   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser);
819   GtkWidget *swatch;
820   GtkWidget *w;
821 
822   GdkRGBA c;
823 
824   for (w = gtk_widget_get_first_child (cc->palette);
825        w != NULL;
826        w = gtk_widget_get_next_sibling (w))
827     {
828       if (!GTK_IS_GRID (w) && !GTK_IS_BOX (w))
829         continue;
830 
831       for (swatch = gtk_widget_get_first_child (w);
832            swatch != NULL;
833            swatch = gtk_widget_get_next_sibling (swatch))
834         {
835           gtk_color_swatch_get_rgba (GTK_COLOR_SWATCH (swatch), &c);
836           if (!cc->use_alpha)
837             c.alpha = color->alpha;
838           if (gdk_rgba_equal (color, &c))
839             {
840               select_swatch (cc, GTK_COLOR_SWATCH (swatch));
841               return;
842             }
843         }
844     }
845 
846   add_custom_color (cc, color);
847 }
848 
849 static void
gtk_color_chooser_widget_add_palette(GtkColorChooser * chooser,GtkOrientation orientation,int colors_per_line,int n_colors,GdkRGBA * colors)850 gtk_color_chooser_widget_add_palette (GtkColorChooser *chooser,
851                                       GtkOrientation   orientation,
852                                       int              colors_per_line,
853                                       int              n_colors,
854                                       GdkRGBA         *colors)
855 {
856   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser);
857 
858   remove_default_palette (cc);
859   add_palette (cc, orientation, colors_per_line, n_colors, colors, NULL);
860 
861   gtk_box_reorder_child_after (GTK_BOX (cc->palette), cc->custom_label, gtk_widget_get_last_child (cc->palette));
862   gtk_box_reorder_child_after (GTK_BOX (cc->palette), cc->custom, cc->custom_label);
863 }
864 
865 static void
gtk_color_chooser_widget_iface_init(GtkColorChooserInterface * iface)866 gtk_color_chooser_widget_iface_init (GtkColorChooserInterface *iface)
867 {
868   iface->get_rgba = gtk_color_chooser_widget_get_rgba;
869   iface->set_rgba = gtk_color_chooser_widget_set_rgba;
870   iface->add_palette = gtk_color_chooser_widget_add_palette;
871 }
872 
873  /* Public API {{{1 */
874 
875 /**
876  * gtk_color_chooser_widget_new:
877  *
878  * Creates a new `GtkColorChooserWidget`.
879  *
880  * Returns: a new `GtkColorChooserWidget`
881  */
882 GtkWidget *
gtk_color_chooser_widget_new(void)883 gtk_color_chooser_widget_new (void)
884 {
885   return g_object_new (GTK_TYPE_COLOR_CHOOSER_WIDGET, NULL);
886 }
887 
888 /* vim:set foldmethod=marker: */
889