]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorchooserwidget.c
Finishing touches
[~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 "gtkcoloreditor.h"
26 #include "gtkcolorswatch.h"
27 #include "gtkbox.h"
28 #include "gtkgrid.h"
29 #include "gtkhsv.h"
30 #include "gtklabel.h"
31 #include "gtkorientable.h"
32 #include "gtkprivate.h"
33 #include "gtkintl.h"
34 #include "gtksizegroup.h"
35 #include "gtkalignment.h"
36
37 struct _GtkColorChooserWidgetPrivate
38 {
39   GtkWidget *palette;
40   GtkWidget *editor;
41
42   GtkWidget *colors;
43   GtkWidget *grays;
44   GtkWidget *custom;
45
46   GtkWidget *button;
47   GtkColorSwatch *current;
48   gboolean show_alpha;
49
50   GtkSizeGroup *size_group;
51
52   GSettings *settings;
53 };
54
55 enum
56 {
57   PROP_ZERO,
58   PROP_COLOR,
59   PROP_SHOW_ALPHA,
60   PROP_SHOW_EDITOR
61 };
62
63 static void gtk_color_chooser_widget_iface_init (GtkColorChooserInterface *iface);
64
65 G_DEFINE_TYPE_WITH_CODE (GtkColorChooserWidget, gtk_color_chooser_widget, GTK_TYPE_BOX,
66                          G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_CHOOSER,
67                                                 gtk_color_chooser_widget_iface_init))
68
69 static void
70 select_swatch (GtkColorChooserWidget *cc,
71                GtkColorSwatch        *swatch)
72 {
73   GdkRGBA color;
74
75   if (cc->priv->current == swatch)
76     return;
77   if (cc->priv->current != NULL)
78     gtk_color_swatch_set_selected (cc->priv->current, FALSE);
79   gtk_color_swatch_set_selected (swatch, TRUE);
80   cc->priv->current = swatch;
81   gtk_color_swatch_get_color (swatch, &color);
82   g_settings_set (cc->priv->settings, "selected-color", "(bdddd)",
83                   TRUE, color.red, color.green, color.blue, color.alpha);
84
85   g_object_notify (G_OBJECT (cc), "color");
86 }
87
88 static void save_custom_colors (GtkColorChooserWidget *cc);
89
90 static void
91 button_activate (GtkColorSwatch        *swatch,
92                  GtkColorChooserWidget *cc)
93 {
94   GdkRGBA color;
95
96   color.red = 0.75;
97   color.green = 0.25;
98   color.blue = 0.25;
99   color.alpha = 1.0;
100
101   gtk_color_chooser_set_color (GTK_COLOR_CHOOSER (cc->priv->editor), &color);
102
103   gtk_widget_hide (cc->priv->palette);
104   gtk_widget_show (cc->priv->editor);
105 }
106
107 static void
108 swatch_activate (GtkColorSwatch        *swatch,
109                  GtkColorChooserWidget *cc)
110 {
111   GdkRGBA color;
112
113   gtk_color_swatch_get_color (swatch, &color);
114   _gtk_color_chooser_color_activated (GTK_COLOR_CHOOSER (cc), &color);
115 }
116
117 static void
118 swatch_customize (GtkColorSwatch        *swatch,
119                   GtkColorChooserWidget *cc)
120 {
121   GdkRGBA color;
122
123   gtk_color_swatch_get_color (swatch, &color);
124   gtk_color_chooser_set_color (GTK_COLOR_CHOOSER (cc->priv->editor), &color);
125
126   gtk_widget_hide (cc->priv->palette);
127   gtk_widget_show (cc->priv->editor);
128 }
129
130 static void
131 swatch_selected (GtkColorSwatch        *swatch,
132                  GParamSpec            *pspec,
133                  GtkColorChooserWidget *cc)
134 {
135   select_swatch (cc, swatch);
136 }
137
138 static void
139 connect_swatch_signals (GtkWidget *p, gpointer data)
140 {
141   g_signal_connect (p, "activate", G_CALLBACK (swatch_activate), data);
142   g_signal_connect (p, "customize", G_CALLBACK (swatch_customize), data);
143   g_signal_connect (p, "notify::selected", G_CALLBACK (swatch_selected), data);
144 }
145
146 static void
147 connect_button_signals (GtkWidget *p, gpointer data)
148 {
149   g_signal_connect (p, "activate", G_CALLBACK (button_activate), data);
150 }
151
152 static void
153 connect_custom_signals (GtkWidget *p, gpointer data)
154 {
155   connect_swatch_signals (p, data);
156   g_signal_connect_swapped (p, "notify::color",
157                             G_CALLBACK (save_custom_colors), data);
158 }
159
160 static void
161 save_custom_colors (GtkColorChooserWidget *cc)
162 {
163   GVariantBuilder builder;
164   GVariant *variant;
165   GdkRGBA color;
166   GtkWidget *child;
167   gint i;
168
169   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(dddd)"));
170
171   i = 1;
172   while ((child = gtk_grid_get_child_at (GTK_GRID (cc->priv->custom), i, 0)) != NULL)
173     {
174       i++;
175       if (gtk_color_swatch_get_color (GTK_COLOR_SWATCH (child), &color))
176         {
177           g_variant_builder_add (&builder, "(dddd)",
178                                  color.red, color.green, color.blue, color.alpha);
179        }
180     }
181
182   variant = g_variant_builder_end (&builder);
183   g_settings_set_value (cc->priv->settings, "custom-colors", variant);
184 }
185
186 static void
187 gtk_color_chooser_widget_init (GtkColorChooserWidget *cc)
188 {
189   GtkWidget *grid;
190   GtkWidget *p;
191   GtkWidget *alignment;
192   GtkWidget *button;
193   GtkWidget *label;
194   gint i, j;
195   gint left;
196   GdkRGBA color;
197   GVariant *variant;
198   GVariantIter iter;
199   gboolean selected;
200   const gchar *default_palette[9][3] = {
201     { "#ef2929", "#cc0000", "#a40000" }, /* Scarlet Red */
202     { "#fcaf3e", "#f57900", "#ce5c00" }, /* Orange */
203     { "#fce94f", "#edd400", "#c4a000" }, /* Butter */
204     { "#8ae234", "#73d216", "#4e9a06" }, /* Chameleon */
205     { "#729fcf", "#3465a4", "#204a87" }, /* Sky Blue */
206     { "#ad7fa8", "#75507b", "#5c3566" }, /* Plum */
207     { "#e9b96e", "#c17d11", "#8f5902" }, /* Chocolate */
208     { "#888a85", "#555753", "#2e3436" }, /* Aluminum 1 */
209     { "#eeeeec", "#d3d7cf", "#babdb6" }  /* Aluminum 2 */
210   };
211   const gchar *default_grayscale[9] = {
212     "#000000",
213     "#2e3436",
214     "#555753",
215     "#888a85",
216     "#babdb6",
217     "#d3d7cf",
218     "#eeeeec",
219     "#f3f3f3",
220     "#ffffff"
221   };
222
223   cc->priv = G_TYPE_INSTANCE_GET_PRIVATE (cc, GTK_TYPE_COLOR_CHOOSER_WIDGET, GtkColorChooserWidgetPrivate);
224
225   gtk_orientable_set_orientation (GTK_ORIENTABLE (cc), GTK_ORIENTATION_VERTICAL);
226   cc->priv->palette = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
227   gtk_container_add (GTK_CONTAINER (cc), cc->priv->palette);
228
229   cc->priv->colors = grid = gtk_grid_new ();
230   gtk_widget_set_margin_bottom (grid, 12);
231   gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
232   gtk_grid_set_column_spacing (GTK_GRID (grid), 4);
233   gtk_container_add (GTK_CONTAINER (cc->priv->palette), grid);
234
235   for (i = 0; i < 9; i++)
236     {
237       for (j = 0; j < 3; j++)
238         {
239           gdk_rgba_parse (&color, default_palette[i][j]);
240
241           p = gtk_color_swatch_new ();
242           connect_swatch_signals (p, cc);
243
244           if (j == 0)
245             gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 10, 10, 1, 1);
246           else if (j == 2)
247             gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 1, 10, 10);
248           else
249             gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 1, 1, 1);
250
251           gtk_color_swatch_set_color (GTK_COLOR_SWATCH (p), &color);
252           gtk_grid_attach (GTK_GRID (grid), p, i, j, 1, 1);
253         }
254     }
255
256   cc->priv->grays = grid = gtk_grid_new ();
257   g_object_set (grid, "margin-bottom", 18, NULL);
258   gtk_grid_set_column_spacing (GTK_GRID (grid), 4);
259   gtk_container_add (GTK_CONTAINER (cc->priv->palette), grid);
260
261   left = (gtk_widget_get_direction (GTK_WIDGET (cc)) == GTK_TEXT_DIR_LTR) ? 0 : 8;
262
263   for (i = 0; i < 9; i++)
264     {
265        gdk_rgba_parse (&color, default_grayscale[i]);
266        color.alpha = 1.0;
267
268        p = gtk_color_swatch_new ();
269        connect_swatch_signals (p, cc);
270        if (i == left)
271          gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 10, 1, 1, 10);
272        else if (i == (8 - left))
273          gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 10, 10, 1);
274        else
275          gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 1, 1, 1);
276
277        gtk_color_swatch_set_color (GTK_COLOR_SWATCH (p), &color);
278        gtk_grid_attach (GTK_GRID (grid), p, i, 0, 1, 1);
279     }
280
281   label = gtk_label_new (_("Custom"));
282   gtk_widget_set_halign (label, GTK_ALIGN_START);
283   gtk_container_add (GTK_CONTAINER (cc->priv->palette), label);
284
285   cc->priv->custom = grid = gtk_grid_new ();
286   g_object_set (grid, "margin-top", 12, NULL);
287   gtk_grid_set_column_spacing (GTK_GRID (grid), 4);
288   gtk_container_add (GTK_CONTAINER (cc->priv->palette), grid);
289
290   cc->priv->button = button = gtk_color_swatch_new ();
291   gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (button), 10, 10, 10, 10);
292   connect_button_signals (button, cc);
293   gtk_color_swatch_set_icon (GTK_COLOR_SWATCH (button), "list-add-symbolic");
294   gtk_grid_attach (GTK_GRID (grid), button, 0, 0, 1, 1);
295
296   cc->priv->settings = g_settings_new_with_path ("org.gtk.Settings.ColorChooser",
297                                                  "/org/gtk/settings/color-chooser/");
298   variant = g_settings_get_value (cc->priv->settings, "custom-colors");
299   g_variant_iter_init (&iter, variant);
300   i = 0;
301   while (g_variant_iter_loop (&iter, "(dddd)", &color.red, &color.green, &color.blue, &color.alpha))
302     {
303       i++;
304       p = gtk_color_swatch_new ();
305       gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 1, 1, 1);
306       gtk_color_swatch_set_color (GTK_COLOR_SWATCH (p), &color);
307       gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (p), TRUE);
308       connect_custom_signals (p, cc);
309       gtk_grid_attach (GTK_GRID (grid), p, i, 0, 1, 1);
310
311       if (i == 8)
312         break;
313     }
314   g_variant_unref (variant);
315
316   if (i > 0)
317     {
318       if (gtk_widget_get_direction (GTK_WIDGET (cc)) == GTK_TEXT_DIR_LTR)
319         {
320           gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 10, 10, 1);
321           gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (button), 10, 1, 1, 10);
322         }
323       else
324         {
325           gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (button), 1, 10, 10, 1);
326           gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 10, 1, 1, 10);
327         }
328     }
329
330   cc->priv->editor = gtk_color_editor_new ();
331   alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
332   gtk_container_add (GTK_CONTAINER (cc), alignment);
333   gtk_container_add (GTK_CONTAINER (alignment), cc->priv->editor);
334
335   g_settings_get (cc->priv->settings, "selected-color", "(bdddd)",
336                   &selected,
337                   &color.red, &color.green, &color.blue, &color.alpha);
338   if (selected)
339     gtk_color_chooser_set_color (GTK_COLOR_CHOOSER (cc), &color);
340
341   gtk_widget_show_all (GTK_WIDGET (cc));
342   gtk_widget_hide (GTK_WIDGET (cc->priv->editor));
343   gtk_widget_hide (GTK_WIDGET (cc));
344
345   gtk_widget_set_no_show_all (cc->priv->palette, TRUE);
346   gtk_widget_set_no_show_all (cc->priv->editor, TRUE);
347
348   cc->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
349   gtk_size_group_add_widget (cc->priv->size_group, cc->priv->palette);
350   gtk_size_group_add_widget (cc->priv->size_group, alignment);
351 }
352
353 static void
354 gtk_color_chooser_widget_get_property (GObject    *object,
355                                        guint       prop_id,
356                                        GValue     *value,
357                                        GParamSpec *pspec)
358 {
359   GtkColorChooserWidget *cw = GTK_COLOR_CHOOSER_WIDGET (object);
360   GtkColorChooser *cc = GTK_COLOR_CHOOSER (object);
361
362   switch (prop_id)
363     {
364     case PROP_COLOR:
365       {
366         GdkRGBA color;
367
368         gtk_color_chooser_get_color (cc, &color);
369         g_value_set_boxed (value, &color);
370       }
371       break;
372     case PROP_SHOW_ALPHA:
373       g_value_set_boolean (value, cw->priv->show_alpha);
374       break;
375     case PROP_SHOW_EDITOR:
376       g_value_set_boolean (value, gtk_widget_get_visible (cw->priv->editor));
377       break;
378     default:
379       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
380       break;
381     }
382 }
383
384 static void
385 gtk_color_chooser_widget_set_show_alpha (GtkColorChooserWidget *cc,
386                                          gboolean               show_alpha)
387 {
388   GtkWidget *grids[3];
389   gint i;
390   GList *children, *l;
391   GtkWidget *swatch;
392
393   cc->priv->show_alpha = show_alpha;
394   gtk_color_chooser_set_show_alpha (GTK_COLOR_CHOOSER (cc->priv->editor), show_alpha);
395
396   grids[0] = cc->priv->colors;
397   grids[1] = cc->priv->grays;
398   grids[2] = cc->priv->custom;
399
400   for (i = 0; i < 3; i++)
401     {
402       children = gtk_container_get_children (GTK_CONTAINER (grids[i]));
403       for (l = children; l; l = l->next)
404         {
405           swatch = l->data;
406           gtk_color_swatch_set_show_alpha (GTK_COLOR_SWATCH (swatch), show_alpha);
407         }
408       g_list_free (children);
409     }
410
411   gtk_widget_queue_draw (GTK_WIDGET (cc));
412 }
413
414 static void
415 gtk_color_chooser_widget_set_show_editor (GtkColorChooserWidget *cc,
416                                           gboolean               show_editor)
417 {
418   gtk_widget_set_visible (cc->priv->editor, show_editor);
419   gtk_widget_set_visible (cc->priv->palette, !show_editor);
420 }
421
422 static void
423 gtk_color_chooser_widget_set_property (GObject      *object,
424                                        guint         prop_id,
425                                        const GValue *value,
426                                        GParamSpec   *pspec)
427 {
428   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object);
429
430   switch (prop_id)
431     {
432     case PROP_COLOR:
433       gtk_color_chooser_set_color (GTK_COLOR_CHOOSER (cc),
434                                    g_value_get_boxed (value));
435       break;
436     case PROP_SHOW_ALPHA:
437       gtk_color_chooser_widget_set_show_alpha (cc,
438                                                g_value_get_boolean (value));
439       break;
440     case PROP_SHOW_EDITOR:
441       gtk_color_chooser_widget_set_show_editor (cc,
442                                                 g_value_get_boolean (value));
443       break;
444     default:
445       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
446       break;
447     }
448 }
449
450 static void
451 gtk_color_chooser_widget_finalize (GObject *object)
452 {
453   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (object);
454
455   g_object_unref (cc->priv->size_group);
456   g_object_unref (cc->priv->settings);
457
458   G_OBJECT_CLASS (gtk_color_chooser_widget_parent_class)->finalize (object);
459 }
460
461 static void
462 gtk_color_chooser_widget_class_init (GtkColorChooserWidgetClass *class)
463 {
464   GObjectClass *object_class = G_OBJECT_CLASS (class);
465
466   object_class->get_property = gtk_color_chooser_widget_get_property;
467   object_class->set_property = gtk_color_chooser_widget_set_property;
468   object_class->finalize = gtk_color_chooser_widget_finalize;
469
470   g_object_class_override_property (object_class, PROP_COLOR, "color");
471   g_object_class_override_property (object_class, PROP_SHOW_ALPHA, "show-alpha");
472
473   g_object_class_install_property (object_class, PROP_SHOW_EDITOR,
474       g_param_spec_boolean ("show-editor", P_("Show editor"), P_("Show editor"),
475                             FALSE, GTK_PARAM_READWRITE));
476
477   g_type_class_add_private (object_class, sizeof (GtkColorChooserWidgetPrivate));
478 }
479
480 static void
481 gtk_color_chooser_widget_get_color (GtkColorChooser *chooser,
482                                     GdkRGBA         *color)
483 {
484   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser);
485
486   if (gtk_widget_get_visible (cc->priv->editor))
487     gtk_color_chooser_get_color (GTK_COLOR_CHOOSER (cc->priv->editor), color);
488   else if (cc->priv->current)
489     gtk_color_swatch_get_color (cc->priv->current, color);
490   else
491     {
492       color->red = 1.0;
493       color->green = 1.0;
494       color->blue = 1.0;
495       color->alpha = 1.0;
496     }
497
498   if (!cc->priv->show_alpha)
499     color->alpha = 1.0;
500 }
501
502 static void
503 add_custom_color (GtkColorChooserWidget *cc,
504                   const GdkRGBA         *color)
505 {
506   GtkWidget *last;
507   GtkWidget *p;
508
509   last = gtk_grid_get_child_at (GTK_GRID (cc->priv->custom), 8, 0);
510   if (last)
511     {
512       gtk_container_remove (GTK_CONTAINER (cc->priv->custom), last);
513       last = gtk_grid_get_child_at (GTK_GRID (cc->priv->custom), 7, 0);
514       gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (last), 1, 10, 10, 1);
515     }
516
517   gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (cc->priv->button), 10, 1, 1, 10);
518
519   p = gtk_color_swatch_new ();
520   gtk_color_swatch_set_color (GTK_COLOR_SWATCH (p), color);
521   gtk_color_swatch_set_can_drop (GTK_COLOR_SWATCH (p), TRUE);
522   connect_custom_signals (p, cc);
523
524   if (gtk_grid_get_child_at (GTK_GRID (cc->priv->custom), 1, 0) != NULL)
525     gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 1, 1, 1);
526   else
527     gtk_color_swatch_set_corner_radii (GTK_COLOR_SWATCH (p), 1, 10, 10, 1);
528
529   gtk_grid_insert_next_to (GTK_GRID (cc->priv->custom), cc->priv->button, GTK_POS_RIGHT);
530   gtk_grid_attach (GTK_GRID (cc->priv->custom), p, 1, 0, 1, 1);
531   gtk_widget_show (p);
532
533   select_swatch (cc, GTK_COLOR_SWATCH (p));
534   save_custom_colors (cc);
535 }
536
537 static void
538 gtk_color_chooser_widget_set_color (GtkColorChooser *chooser,
539                                     const GdkRGBA   *color)
540 {
541   GtkColorChooserWidget *cc = GTK_COLOR_CHOOSER_WIDGET (chooser);
542   GList *children, *l;
543   GtkColorSwatch *swatch;
544   GdkRGBA c;
545   GtkWidget *grids[3];
546   gint i;
547
548   grids[0] = cc->priv->colors;
549   grids[1] = cc->priv->grays;
550   grids[2] = cc->priv->custom;
551
552   for (i = 0; i < 3; i++)
553     {
554       children = gtk_container_get_children (GTK_CONTAINER (grids[i]));
555       for (l = children; l; l = l->next)
556         {
557           swatch = l->data;
558           gtk_color_swatch_get_color (swatch, &c);
559           if (!cc->priv->show_alpha)
560             c.alpha = color->alpha;
561           if (gdk_rgba_equal (color, &c))
562             {
563               select_swatch (cc, swatch);
564               g_list_free (children);
565               return;
566             }
567         }
568       g_list_free (children);
569     }
570
571   add_custom_color (cc, color);
572 }
573
574 static void
575 gtk_color_chooser_widget_iface_init (GtkColorChooserInterface *iface)
576 {
577   iface->get_color = gtk_color_chooser_widget_get_color;
578   iface->set_color = gtk_color_chooser_widget_set_color;
579 }
580
581 GtkWidget *
582 gtk_color_chooser_widget_new (void)
583 {
584   return g_object_new (GTK_TYPE_COLOR_CHOOSER_WIDGET, NULL);
585 }