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
66typedef struct _GtkColorChooserWidgetClass GtkColorChooserWidgetClass;
67
68struct _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
90struct _GtkColorChooserWidgetClass
91{
92 GtkWidgetClass parent_class;
93};
94
95enum
96{
97 PROP_ZERO,
98 PROP_RGBA,
99 PROP_USE_ALPHA,
100 PROP_SHOW_EDITOR
101};
102
103static void gtk_color_chooser_widget_iface_init (GtkColorChooserInterface *iface);
104
105G_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
109static void
110select_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), flags: GTK_STATE_FLAG_SELECTED);
121
122 gtk_widget_set_state_flags (GTK_WIDGET (swatch), flags: GTK_STATE_FLAG_SELECTED, FALSE);
123 cc->current = swatch;
124
125 gtk_color_swatch_get_rgba (swatch, color: &color);
126
127 red = color.red;
128 green = color.green;
129 blue = color.blue;
130 alpha = color.alpha;
131 g_settings_set (settings: cc->settings, key: "selected-color", format: "(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: &color);
136 else
137 g_object_notify (G_OBJECT (cc), property_name: "rgba");
138}
139
140static void
141swatch_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
153static void
154connect_swatch_signals (GtkWidget *p,
155 gpointer data)
156{
157 g_signal_connect (p, "state-flags-changed", G_CALLBACK (swatch_selected), data);
158}
159
160static void
161connect_button_signals (GtkWidget *p,
162 gpointer data)
163{
164// g_signal_connect (p, "activate", G_CALLBACK (button_activate), data);
165}
166
167static void
168save_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: &builder, G_VARIANT_TYPE ("a(dddd)"));
177
178 for (child = gtk_widget_get_first_child (widget: cc->custom), first = TRUE;
179 child != NULL;
180 child = gtk_widget_get_next_sibling (widget: child), first = FALSE)
181 {
182 if (first)
183 continue;
184
185 if (gtk_color_swatch_get_rgba (GTK_COLOR_SWATCH (child), color: &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: &builder, format_string: "(dddd)", red, green, blue, alpha);
194 }
195 }
196
197 variant = g_variant_builder_end (builder: &builder);
198 g_settings_set_value (settings: cc->settings, key: "custom-colors", value: variant);
199}
200
201static void
202connect_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
210static void
211gtk_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 (widget: cc->palette);
224 grid != NULL;
225 grid = gtk_widget_get_next_sibling (widget: grid))
226 {
227 for (child = gtk_widget_get_first_child (widget: grid);
228 child != NULL;
229 child = gtk_widget_get_next_sibling (widget: 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), property_name: "use-alpha");
238}
239
240static void
241gtk_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 (swatch: cc->current, color: &color);
250 gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cc->editor), color: &color);
251 }
252
253 gtk_widget_set_visible (widget: cc->editor, visible: show_editor);
254 gtk_widget_set_visible (widget: cc->palette, visible: !show_editor);
255}
256
257static void
258update_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), property_name: "rgba");
264}
265
266/* UI construction {{{1 */
267
268static void
269remove_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 (widget: cc->palette);
280 widget != NULL;
281 widget = gtk_widget_get_next_sibling (widget))
282 children = g_list_prepend (list: children, data: 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), child: widget);
290 }
291 g_list_free (list: children);
292}
293
294static guint
295scale_round (double value,
296 double scale)
297{
298 value = floor (x: value * scale + 0.5);
299 value = MAX (value, 0);
300 value = MIN (value, scale);
301 return (guint)value;
302}
303
304static char *
305accessible_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 (value: color->red, scale: 100),
310 scale_round (value: color->green, scale: 100),
311 scale_round (value: color->blue, scale: 100),
312 scale_round (value: color->alpha, scale: 100));
313 else
314 return g_strdup_printf (_("Red %d%%, Green %d%%, Blue %d%%"),
315 scale_round (value: color->red, scale: 100),
316 scale_round (value: color->green, scale: 100),
317 scale_round (value: color->blue, scale: 100));
318}
319
320static void
321add_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 (widget: grid, margin: 12);
342 gtk_grid_set_row_spacing (GTK_GRID (grid), spacing: 2);
343 gtk_grid_set_column_spacing (GTK_GRID (grid), spacing: 4);
344 gtk_box_append (GTK_BOX (cc->palette), child: 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 (self: GTK_ACCESSIBLE (ptr: p),
361 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL,
362 g_dpgettext2 (GETTEXT_PACKAGE, context: "Color name", msgid: names[i]),
363 -1);
364 }
365 else
366 {
367 char *name;
368 char *text;
369
370 name = accessible_color_name (color: &colors[i]);
371 text = g_strdup_printf (_("Color: %s"), name);
372 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: p),
373 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, text,
374 -1);
375 g_free (mem: name);
376 g_free (mem: text);
377 }
378 gtk_color_swatch_set_rgba (GTK_COLOR_SWATCH (p), color: &colors[i]);
379 connect_swatch_signals (p, data: 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 (widget: p, css_class: "left");
388 else if (pos == right)
389 gtk_widget_add_css_class (widget: p, css_class: "right");
390
391 gtk_grid_attach (GTK_GRID (grid), child: p, column: pos, row: line, width: 1, height: 1);
392 }
393 else
394 {
395 if (pos == 0)
396 gtk_widget_add_css_class (widget: p, css_class: "top");
397 else if (pos == colors_per_line - 1)
398 gtk_widget_add_css_class (widget: p, css_class: "bottom");
399
400 gtk_grid_attach (GTK_GRID (grid), child: p, column: line, row: pos, width: 1, height: 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
410static void
411remove_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
421static void
422add_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, orientation: GTK_ORIENTATION_VERTICAL, colors_per_line: 5, n_colors: 9*5, colors, names: color_names);
484
485 cc->has_default_palette = TRUE;
486}
487
488static void
489gtk_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 (value: parameter, format_string: "(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: &color);
504
505 gtk_widget_hide (widget: cc->palette);
506 gtk_widget_show (widget: cc->editor);
507 g_object_notify (G_OBJECT (cc), property_name: "show-editor");
508}
509
510static void
511gtk_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 (value: parameter, format_string: "(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: &color);
526}
527
528static void
529gtk_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 (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0);
547 gtk_widget_set_parent (widget: 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 (widget: label, align: GTK_ALIGN_START);
554 gtk_box_append (GTK_BOX (cc->palette), child: label);
555
556 cc->custom = box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 4);
557 g_object_set (object: box, first_property_name: "margin-top", 12, NULL);
558 gtk_box_append (GTK_BOX (cc->palette), child: box);
559
560 cc->button = button = gtk_color_swatch_new ();
561 gtk_widget_set_name (widget: button, name: "add-color-button");
562 connect_button_signals (p: button, data: cc);
563 gtk_color_swatch_set_icon (GTK_COLOR_SWATCH (button), icon: "list-add-symbolic");
564 gtk_color_swatch_set_selectable (GTK_COLOR_SWATCH (button), FALSE);
565 gtk_box_append (GTK_BOX (box), child: button);
566
567 cc->settings = g_settings_new (schema_id: "org.gtk.gtk4.Settings.ColorChooser");
568 variant = g_settings_get_value (settings: cc->settings, I_("custom-colors"));
569 g_variant_iter_init (iter: &iter, value: variant);
570 i = 0;
571 p = NULL;
572 while (g_variant_iter_loop (iter: &iter, format_string: "(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), color: &rgba);
583
584 name = accessible_color_name (color: &rgba);
585 text = g_strdup_printf (_("Custom color %d: %s"), i, name);
586 gtk_accessible_update_property (self: GTK_ACCESSIBLE (ptr: p),
587 first_property: GTK_ACCESSIBLE_PROPERTY_LABEL, text,
588 -1);
589 g_free (mem: name);
590 g_free (mem: text);
591
592 gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (p), TRUE);
593 connect_custom_signals (p, data: cc);
594 gtk_box_append (GTK_BOX (box), child: p);
595
596 if (i == 8)
597 break;
598 }
599 g_variant_unref (value: variant);
600
601 cc->editor = gtk_color_editor_new ();
602 gtk_widget_set_halign (widget: cc->editor, align: GTK_ALIGN_CENTER);
603 gtk_widget_set_hexpand (widget: cc->editor, TRUE);
604 g_signal_connect (cc->editor, "notify::rgba",
605 G_CALLBACK (update_from_editor), cc);
606
607 gtk_widget_set_parent (widget: cc->editor, GTK_WIDGET (cc));
608
609 g_settings_get (settings: cc->settings, I_("selected-color"), format: "(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), color: &rgba);
619 }
620
621 gtk_widget_hide (GTK_WIDGET (cc->editor));
622}
623
624/* GObject implementation {{{1 */
625
626static void
627gtk_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: &color);
641 g_value_set_boxed (value, v_boxed: &color);
642 }
643 break;
644 case PROP_USE_ALPHA:
645 g_value_set_boolean (value, v_boolean: cc->use_alpha);
646 break;
647 case PROP_SHOW_EDITOR:
648 g_value_set_boolean (value, v_boolean: gtk_widget_get_visible (widget: cc->editor));
649 break;
650 default:
651 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
652 break;
653 }
654}
655
656static void
657gtk_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 color: g_value_get_boxed (value));
669 break;
670 case PROP_USE_ALPHA:
671 gtk_color_chooser_widget_set_use_alpha (cc,
672 use_alpha: g_value_get_boolean (value));
673 break;
674 case PROP_SHOW_EDITOR:
675 gtk_color_chooser_widget_set_show_editor (cc,
676 show_editor: 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
684static void
685gtk_color_chooser_widget_finalize (GObject *object)
686{
687 GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object);
688
689 g_object_unref (object: cc->settings);
690
691 gtk_widget_unparent (widget: cc->editor);
692 gtk_widget_unparent (widget: cc->palette);
693
694 G_OBJECT_CLASS (gtk_color_chooser_widget_parent_class)->finalize (object);
695}
696
697static void
698gtk_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 (oclass: object_class, property_id: PROP_RGBA, name: "rgba");
711 g_object_class_override_property (oclass: object_class, property_id: PROP_USE_ALPHA, name: "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 (oclass: object_class, property_id: PROP_SHOW_EDITOR,
721 pspec: g_param_spec_boolean (name: "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, action_name: "color.select", parameter_type: "(dddd)",
738 activate: 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, action_name: "color.customize", parameter_type: "(dddd)",
750 activate: gtk_color_chooser_widget_activate_color_customize);
751}
752
753/* GtkColorChooser implementation {{{1 */
754
755static void
756gtk_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 (widget: 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 (swatch: 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
777static void
778add_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 (widget: 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 (widget: cc->custom);
794
795 if (last == (GtkWidget *)cc->current)
796 cc->current = NULL;
797
798 gtk_box_remove (GTK_BOX (cc->custom), child: 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, data: cc);
806
807 gtk_box_insert_child_after (GTK_BOX (cc->custom), child: p, sibling: gtk_widget_get_first_child (widget: cc->custom));
808 gtk_widget_show (widget: p);
809
810 select_swatch (cc, GTK_COLOR_SWATCH (p));
811 save_custom_colors (cc);
812}
813
814static void
815gtk_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 (widget: cc->palette);
825 w != NULL;
826 w = gtk_widget_get_next_sibling (widget: w))
827 {
828 if (!GTK_IS_GRID (w) && !GTK_IS_BOX (w))
829 continue;
830
831 for (swatch = gtk_widget_get_first_child (widget: w);
832 swatch != NULL;
833 swatch = gtk_widget_get_next_sibling (widget: swatch))
834 {
835 gtk_color_swatch_get_rgba (GTK_COLOR_SWATCH (swatch), color: &c);
836 if (!cc->use_alpha)
837 c.alpha = color->alpha;
838 if (gdk_rgba_equal (p1: color, p2: &c))
839 {
840 select_swatch (cc, GTK_COLOR_SWATCH (swatch));
841 return;
842 }
843 }
844 }
845
846 add_custom_color (cc, color);
847}
848
849static void
850gtk_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), child: cc->custom_label, sibling: gtk_widget_get_last_child (widget: cc->palette));
862 gtk_box_reorder_child_after (GTK_BOX (cc->palette), child: cc->custom, sibling: cc->custom_label);
863}
864
865static void
866gtk_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 */
882GtkWidget *
883gtk_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

source code of gtk/gtk/gtkcolorchooserwidget.c