1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2000 Red Hat, Inc.
3  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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 /*
20  * Modified by the GTK+ Team and others 1997-2001.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24  *
25  * Updated and adapted for inclusion in Gcolor3 by Jente Hidskes 2017.
26  */
27 
28 #include "config.h"
29 
30 // TODO: get rid of all deprecated API
31 #define GDK_DISABLE_DEPRECATION_WARNINGS
32 
33 #include "gcolor3-color-selection.h"
34 #include "gcolor3-hsv.h"
35 #include "utils.h"
36 
37 #include <math.h>
38 #include <string.h>
39 #include <gdk/gdk.h>
40 #ifdef GDK_WINDOWING_WAYLAND
41 #include <gdk/gdkwayland.h>
42 #endif
43 #include <gtk/gtk.h>
44 #include <glib/gi18n.h>
45 
46 #ifdef ENABLE_NLS
47 #define P_(String) g_dgettext(GETTEXT_PACKAGE "-properties",String)
48 #else
49 #define P_(String) (String)
50 #endif
51 
52 #define I_(string) g_intern_static_string (string)
53 
54 /**
55  * SECTION:gtkcolorsel
56  * @Short_description: Widget used to select a color
57  * @Title: Gcolor3ColorSelection
58  *
59  * The #Gcolor3ColorSelection is a widget that is used to select
60  * a color.  It consists of a color wheel and number of sliders
61  * and entry boxes for color parameters such as hue, saturation,
62  * value, red, green, blue, and opacity.
63  */
64 
65 
66 /* Keep it in sync with gtksettings.c:default_color_palette */
67 #define DEFAULT_COLOR_PALETTE   "black:white:gray50:red:purple:blue:light blue:green:yellow:orange:lavender:brown:goldenrod4:dodger blue:pink:light green:gray10:gray30:gray75:gray90"
68 
69 /* Number of elements in the custom palatte */
70 #define CUSTOM_PALETTE_WIDTH 10
71 #define CUSTOM_PALETTE_HEIGHT 2
72 
73 #define CUSTOM_PALETTE_ENTRY_WIDTH   20
74 #define CUSTOM_PALETTE_ENTRY_HEIGHT  20
75 
76 /* The cursor for the dropper */
77 #define DROPPER_WIDTH 17
78 #define DROPPER_HEIGHT 17
79 #define DROPPER_STRIDE (DROPPER_WIDTH * 4)
80 #define DROPPER_X_HOT 2
81 #define DROPPER_Y_HOT 16
82 
83 #define SAMPLE_WIDTH  64
84 #define SAMPLE_HEIGHT 28
85 #define CHECK_SIZE 16
86 #define BIG_STEP 20
87 
88 /* Conversion between 0->1 double and and guint16. See
89  * scale_round() in utils.c for more general conversions
90  */
91 #define SCALE(i) (i / 65535.)
92 #define UNSCALE(d) ((guint16)(d * 65535 + 0.5))
93 #define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
94 
95 enum {
96   COLOR_CHANGED,
97   LAST_SIGNAL
98 };
99 
100 enum {
101   PROP_0,
102   PROP_HAS_PALETTE,
103   PROP_HAS_OPACITY_CONTROL,
104   PROP_CURRENT_ALPHA,
105   PROP_CURRENT_RGBA
106 };
107 
108 enum {
109   COLORSEL_RED = 0,
110   COLORSEL_GREEN = 1,
111   COLORSEL_BLUE = 2,
112   COLORSEL_OPACITY = 3,
113   COLORSEL_HUE,
114   COLORSEL_SATURATION,
115   COLORSEL_VALUE,
116   COLORSEL_NUM_CHANNELS
117 };
118 
119 
120 struct _Gcolor3ColorSelectionPrivate
121 {
122   guint has_opacity       : 1;
123   guint has_palette       : 1;
124   guint changing          : 1;
125   guint default_set       : 1;
126   guint default_alpha_set : 1;
127   guint has_grab          : 1;
128 
129   // FIXME: these can be replaced with a GdkRGBA
130   gdouble color[COLORSEL_NUM_CHANNELS];
131   gdouble old_color[COLORSEL_NUM_CHANNELS];
132 
133   GtkWidget *triangle_colorsel;
134   GtkWidget *hue_spinbutton;
135   GtkWidget *sat_spinbutton;
136   GtkWidget *val_spinbutton;
137   GtkWidget *red_spinbutton;
138   GtkWidget *green_spinbutton;
139   GtkWidget *blue_spinbutton;
140   GtkWidget *opacity_slider;
141   GtkWidget *opacity_label;
142   GtkWidget *opacity_entry;
143   GtkWidget *palette_frame;
144   GtkWidget *hex_entry;
145 
146   /* The Palette code */
147   GtkWidget *custom_palette [CUSTOM_PALETTE_WIDTH][CUSTOM_PALETTE_HEIGHT];
148 
149   /* The color_sample stuff */
150   GtkWidget *sample_area;
151   GtkWidget *old_sample;
152   GtkWidget *cur_sample;
153   GtkWidget *colorsel;
154 
155   /* Window for grabbing on */
156   GtkWidget *dropper_grab_widget;
157   guint32    grab_time;
158   GdkDevice *keyboard_device;
159   GdkDevice *pointer_device;
160 
161   /* Connection to settings */
162   gulong settings_connection;
163 };
164 
165 
166 static void gcolor3_color_selection_destroy         (GtkWidget               *widget);
167 static void gcolor3_color_selection_finalize        (GObject                 *object);
168 static void update_color                            (Gcolor3ColorSelection   *colorsel);
169 static void gcolor3_color_selection_set_property    (GObject                 *object,
170                                                      guint                    prop_id,
171                                                      const GValue            *value,
172                                                      GParamSpec              *pspec);
173 static void gcolor3_color_selection_get_property    (GObject                 *object,
174                                                      guint                    prop_id,
175                                                      GValue                  *value,
176                                                      GParamSpec              *pspec);
177 
178 static void gcolor3_color_selection_realize         (GtkWidget               *widget);
179 static void gcolor3_color_selection_unrealize       (GtkWidget               *widget);
180 static void gcolor3_color_selection_show_all        (GtkWidget               *widget);
181 static gboolean gcolor3_color_selection_grab_broken (GtkWidget               *widget,
182                                                      GdkEventGrabBroken      *event);
183 
184 static void     gcolor3_color_selection_set_palette_color   (Gcolor3ColorSelection *colorsel,
185                                                              gint                   index,
186                                                              GdkRGBA               *color);
187 static void     set_focus_line_attributes                   (GtkWidget             *drawing_area,
188                                                              cairo_t               *cr,
189                                                              gint                  *focus_width);
190 static void     default_noscreen_change_palette_func        (const GdkRGBA         *colors,
191                                                              gint                   n_colors);
192 static void     default_change_palette_func                 (GdkScreen             *screen,
193                                                              const GdkRGBA         *colors,
194                                                              gint                   n_colors);
195 static void     make_control_relations                      (AtkObject             *atk_obj,
196                                                              GtkWidget             *widget);
197 static void     make_all_relations                          (AtkObject             *atk_obj,
198                                                              Gcolor3ColorSelectionPrivate *priv);
199 
200 static void     hsv_changed                                 (GtkWidget             *hsv,
201                                                              gpointer               data);
202 static void     get_screen_color                            (GtkWidget             *button);
203 static void     adjustment_changed                          (GtkAdjustment         *adjustment,
204                                                              gpointer               data);
205 static void     opacity_entry_changed                       (GtkWidget             *opacity_entry,
206                                                              gpointer               data);
207 static void     hex_changed                                 (GtkWidget             *hex_entry,
208                                                              gpointer               data);
209 static gboolean hex_focus_out                               (GtkWidget             *hex_entry,
210                                                              GdkEventFocus         *event,
211                                                              gpointer               data);
212 static void     color_sample_new                            (Gcolor3ColorSelection *colorsel);
213 static void     make_label_spinbutton                       (Gcolor3ColorSelection *colorsel,
214                                                              GtkWidget            **spinbutton,
215                                                              gchar                 *text,
216                                                              GtkWidget             *table,
217                                                              gint                   i,
218                                                              gint                   j,
219                                                              gint                   channel_type,
220                                                              const gchar           *tooltip);
221 static void     make_palette_frame                          (Gcolor3ColorSelection *colorsel,
222                                                              GtkWidget             *table,
223                                                              gint                   i,
224                                                              gint                   j);
225 static void     set_selected_palette                        (Gcolor3ColorSelection *colorsel,
226                                                              int                    x,
227                                                              int                    y);
228 static void     set_focus_line_attributes                   (GtkWidget             *drawing_area,
229                                                              cairo_t               *cr,
230                                                              gint                  *focus_width);
231 static gboolean mouse_press                                 (GtkWidget             *invisible,
232                                                              GdkEventButton        *event,
233                                                              gpointer               data);
234 static void  palette_change_notify_instance                 (GObject    *object,
235                                                              GParamSpec *pspec,
236                                                              gpointer    data);
237 static void update_palette                                  (Gcolor3ColorSelection *colorsel);
238 static void shutdown_eyedropper                             (GtkWidget *widget);
239 
240 static guint color_selection_signals[LAST_SIGNAL] = { 0 };
241 
242 static Gcolor3ColorSelectionChangePaletteFunc noscreen_change_palette_hook = default_noscreen_change_palette_func;
243 static Gcolor3ColorSelectionChangePaletteWithScreenFunc change_palette_hook = default_change_palette_func;
244 
245 static const guchar dropper_bits[] = {
246   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
247   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377"
248   "\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
249   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\0\0\0\377"
250   "\0\0\0\377\0\0\0\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
251   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377"
252   "\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\377\377\377\377"
253   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377"
254   "\377\377\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\377\0\0"
255   "\0\377\0\0\0\377\0\0\0\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0"
256   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\0\0\0\377\0\0\0\377\0"
257   "\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\377\377\377"
258   "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
259   "\377\377\377\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0\0\0\377\0"
260   "\0\0\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
261   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\0\0\0\377\0\0"
262   "\0\377\0\0\0\377\377\377\377\377\377\377\377\377\0\0\0\0\0\0\0\0\0\0"
263   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377"
264   "\377\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\377\377\377"
265   "\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
266   "\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377"
267   "\0\0\0\377\377\377\377\377\0\0\0\377\377\377\377\377\0\0\0\0\0\0\0\0"
268   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377"
269   "\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\0\0\0\0\0\377\377"
270   "\377\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
271   "\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0"
272   "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
273   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\377"
274   "\377\377\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
275   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377"
276   "\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0"
277   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
278   "\377\377\377\377\377\377\377\377\377\377\377\377\377\0\0\0\377\0\0\0"
279   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
280   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\377\377\377\377\377\377\377\377\0\0"
281   "\0\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
282   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\0\0\0\0\377\0\0\0"
283   "\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
284   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\377\0\0\0\0\0"
285   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
286   "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"};
287 
G_DEFINE_TYPE_WITH_PRIVATE(Gcolor3ColorSelection,gcolor3_color_selection,GTK_TYPE_BOX)288 G_DEFINE_TYPE_WITH_PRIVATE (Gcolor3ColorSelection, gcolor3_color_selection, GTK_TYPE_BOX)
289 
290 static void
291 gcolor3_color_selection_class_init (Gcolor3ColorSelectionClass *klass)
292 {
293   GObjectClass *gobject_class;
294   GtkWidgetClass *widget_class;
295 
296   gobject_class = G_OBJECT_CLASS (klass);
297   gobject_class->finalize = gcolor3_color_selection_finalize;
298   gobject_class->set_property = gcolor3_color_selection_set_property;
299   gobject_class->get_property = gcolor3_color_selection_get_property;
300 
301   widget_class = GTK_WIDGET_CLASS (klass);
302   widget_class->destroy = gcolor3_color_selection_destroy;
303   widget_class->realize = gcolor3_color_selection_realize;
304   widget_class->unrealize = gcolor3_color_selection_unrealize;
305   widget_class->show_all = gcolor3_color_selection_show_all;
306   widget_class->grab_broken_event = gcolor3_color_selection_grab_broken;
307 
308   g_object_class_install_property (gobject_class,
309                                    PROP_HAS_OPACITY_CONTROL,
310                                    g_param_spec_boolean ("has-opacity-control",
311                                                          P_("Has Opacity Control"),
312                                                          P_("Whether the color selector should allow setting opacity"),
313                                                          FALSE,
314                                                          G_PARAM_READWRITE|
315                                                          G_PARAM_STATIC_NAME|
316                                                          G_PARAM_STATIC_NICK|
317                                                          G_PARAM_STATIC_BLURB));
318   g_object_class_install_property (gobject_class,
319                                    PROP_HAS_PALETTE,
320                                    g_param_spec_boolean ("has-palette",
321                                                          P_("Has palette"),
322                                                          P_("Whether a palette should be used"),
323                                                          FALSE,
324                                                          G_PARAM_READWRITE|
325                                                          G_PARAM_STATIC_NAME|
326                                                          G_PARAM_STATIC_NICK|
327                                                          G_PARAM_STATIC_BLURB));
328 
329   g_object_class_install_property (gobject_class,
330                                    PROP_CURRENT_ALPHA,
331                                    g_param_spec_uint ("current-alpha",
332                                                       P_("Current Alpha"),
333                                                       P_("The current opacity value (0 fully transparent, 65535 fully opaque)"),
334                                                       0, 65535, 65535,
335                                                       G_PARAM_READWRITE|
336                                                       G_PARAM_STATIC_NAME|
337                                                       G_PARAM_STATIC_NICK|
338                                                       G_PARAM_STATIC_BLURB));
339 
340   /**
341    * Gcolor3ColorSelection:current-rgba:
342    *
343    * The current RGBA color.
344    *
345    * Since: 3.0
346    */
347   g_object_class_install_property (gobject_class,
348                                    PROP_CURRENT_RGBA,
349                                    g_param_spec_boxed ("current-rgba",
350                                                        P_("Current RGBA"),
351                                                        P_("The current RGBA color"),
352                                                        GDK_TYPE_RGBA,
353                                                        G_PARAM_READWRITE|
354                                                        G_PARAM_STATIC_NAME|
355                                                        G_PARAM_STATIC_NICK|
356                                                        G_PARAM_STATIC_BLURB));
357 
358   /**
359    * Gcolor3ColorSelection::color-changed:
360    * @colorselection: the object which received the signal.
361    *
362    * This signal is emitted when the color changes in the #Gcolor3ColorSelection
363    * according to its update policy.
364    */
365   color_selection_signals[COLOR_CHANGED] =
366     g_signal_new (I_("color-changed"),
367                   G_OBJECT_CLASS_TYPE (gobject_class),
368                   G_SIGNAL_RUN_FIRST,
369                   G_STRUCT_OFFSET (Gcolor3ColorSelectionClass, color_changed),
370                   NULL, NULL,
371                   NULL,
372                   G_TYPE_NONE, 0);
373 }
374 
375 static void
gcolor3_color_selection_init(Gcolor3ColorSelection * colorsel)376 gcolor3_color_selection_init (Gcolor3ColorSelection *colorsel)
377 {
378   GtkWidget *top_hbox;
379   GtkWidget *top_right_vbox;
380   GtkWidget *table, *label, *hbox, *frame, *vbox, *button;
381   GtkAdjustment *adjust;
382   GtkWidget *picker_image;
383   gint i, j;
384   Gcolor3ColorSelectionPrivate *priv;
385   AtkObject *atk_obj;
386   GList *focus_chain = NULL;
387 
388   gtk_orientable_set_orientation (GTK_ORIENTABLE (colorsel),
389                                   GTK_ORIENTATION_VERTICAL);
390 
391   gtk_widget_push_composite_child ();
392 
393   priv = colorsel->private_data = gcolor3_color_selection_get_instance_private (colorsel);
394   priv->changing = FALSE;
395   priv->default_set = FALSE;
396   priv->default_alpha_set = FALSE;
397 
398   top_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
399   gtk_box_pack_start (GTK_BOX (colorsel), top_hbox, FALSE, FALSE, 0);
400 
401   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
402   priv->triangle_colorsel = gcolor3_hsv_new ();
403   g_signal_connect (priv->triangle_colorsel, "changed",
404                     G_CALLBACK (hsv_changed), colorsel);
405   gcolor3_hsv_set_metrics (GCOLOR3_HSV (priv->triangle_colorsel), 174, 15);
406   gtk_box_pack_start (GTK_BOX (top_hbox), vbox, FALSE, FALSE, 0);
407   gtk_box_pack_start (GTK_BOX (vbox), priv->triangle_colorsel, FALSE, FALSE, 0);
408   gtk_widget_set_tooltip_text (priv->triangle_colorsel,
409                         _("Select the color you want from the outer ring. "
410                           "Select the darkness or lightness of that color "
411                           "using the inner triangle."));
412 
413   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
414   gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
415 
416   frame = gtk_frame_new (NULL);
417   gtk_widget_set_size_request (frame, -1, 30);
418   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
419   color_sample_new (colorsel);
420   gtk_container_add (GTK_CONTAINER (frame), priv->sample_area);
421   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
422 
423   button = gtk_button_new ();
424 
425   gtk_widget_set_events (button, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
426   g_object_set_data (G_OBJECT (button), I_("COLORSEL"), colorsel);
427   g_signal_connect (button, "clicked",
428                     G_CALLBACK (get_screen_color), NULL);
429   picker_image = gtk_image_new_from_icon_name ("gtk-color-picker", GTK_ICON_SIZE_BUTTON);
430   gtk_container_add (GTK_CONTAINER (button), picker_image);
431   gtk_widget_show (GTK_WIDGET (picker_image));
432   gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
433 
434 #ifdef GDK_WINDOWING_WAYLAND
435   if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default()))
436     {
437       gtk_widget_set_sensitive (button, FALSE);
438       gtk_widget_set_tooltip_text (button,
439 				   _("Picking a color is currently not supported on "
440                                    "Wayland."));
441     }
442 #else
443   if (FALSE)
444     {
445     }
446 #endif
447   else
448     {
449       gtk_widget_set_tooltip_text (button,
450                                    _("Click the eyedropper, then click a color "
451                                    "anywhere on your screen to select that color."));
452     }
453 
454   top_right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
455   gtk_box_pack_start (GTK_BOX (top_hbox), top_right_vbox, FALSE, FALSE, 0);
456   table = gtk_grid_new ();
457   gtk_box_pack_start (GTK_BOX (top_right_vbox), table, FALSE, FALSE, 0);
458   gtk_grid_set_row_spacing (GTK_GRID (table), 6);
459   gtk_grid_set_column_spacing (GTK_GRID (table), 12);
460 
461   make_label_spinbutton (colorsel, &priv->hue_spinbutton, _("_Hue:"), table, 0, 0, COLORSEL_HUE,
462                          _("Position on the color wheel."));
463   gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (priv->hue_spinbutton), TRUE);
464   make_label_spinbutton (colorsel, &priv->sat_spinbutton, _("S_aturation:"), table, 0, 1, COLORSEL_SATURATION,
465                          _("Intensity of the color."));
466   make_label_spinbutton (colorsel, &priv->val_spinbutton, _("_Value:"), table, 0, 2, COLORSEL_VALUE,
467                          _("Brightness of the color."));
468   make_label_spinbutton (colorsel, &priv->red_spinbutton, _("_Red:"), table, 6, 0, COLORSEL_RED,
469                          _("Amount of red light in the color."));
470   make_label_spinbutton (colorsel, &priv->green_spinbutton, _("_Green:"), table, 6, 1, COLORSEL_GREEN,
471                          _("Amount of green light in the color."));
472   make_label_spinbutton (colorsel, &priv->blue_spinbutton, _("_Blue:"), table, 6, 2, COLORSEL_BLUE,
473                          _("Amount of blue light in the color."));
474   gtk_grid_attach (GTK_GRID (table), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL), 0, 3, 8, 1);
475 
476   priv->opacity_label = gtk_label_new_with_mnemonic (_("Op_acity:"));
477   gtk_widget_set_halign (priv->opacity_label, GTK_ALIGN_START);
478   gtk_widget_set_valign (priv->opacity_label, GTK_ALIGN_CENTER);
479   gtk_grid_attach (GTK_GRID (table), priv->opacity_label, 0, 4, 1, 1);
480   adjust = gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 1.0, 0.0);
481   g_object_set_data (G_OBJECT (adjust), I_("COLORSEL"), colorsel);
482   priv->opacity_slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, adjust);
483   gtk_widget_set_tooltip_text (priv->opacity_slider,
484                         _("Transparency of the color."));
485   gtk_label_set_mnemonic_widget (GTK_LABEL (priv->opacity_label),
486                                  priv->opacity_slider);
487   gtk_scale_set_draw_value (GTK_SCALE (priv->opacity_slider), FALSE);
488   g_signal_connect (adjust, "value-changed",
489                     G_CALLBACK (adjustment_changed),
490                     GINT_TO_POINTER (COLORSEL_OPACITY));
491   gtk_grid_attach (GTK_GRID (table), priv->opacity_slider, 1, 4, 6, 1);
492   priv->opacity_entry = gtk_entry_new ();
493   gtk_widget_set_tooltip_text (priv->opacity_entry,
494                         _("Transparency of the color."));
495   gtk_widget_set_size_request (priv->opacity_entry, 40, -1);
496 
497   g_signal_connect (priv->opacity_entry, "activate",
498                     G_CALLBACK (opacity_entry_changed), colorsel);
499   gtk_grid_attach (GTK_GRID (table), priv->opacity_entry, 7, 4, 1, 1);
500 
501   label = gtk_label_new_with_mnemonic (_("Color _name:"));
502   gtk_grid_attach (GTK_GRID (table), label, 0, 5, 1, 1);
503   gtk_widget_set_halign (label, GTK_ALIGN_START);
504   gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
505   priv->hex_entry = gtk_entry_new ();
506 
507   gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->hex_entry);
508 
509   g_signal_connect (priv->hex_entry, "activate",
510                     G_CALLBACK (hex_changed), colorsel);
511 
512   g_signal_connect (priv->hex_entry, "focus-out-event",
513                     G_CALLBACK (hex_focus_out), colorsel);
514 
515   gtk_widget_set_tooltip_text (priv->hex_entry,
516                         _("You can enter an HTML-style hexadecimal color "
517                           "value, or simply a color name such as “orange” "
518                           "in this entry."));
519 
520   gtk_entry_set_width_chars (GTK_ENTRY (priv->hex_entry), 7);
521   gtk_grid_attach (GTK_GRID (table), priv->hex_entry, 1, 5, 4, 1);
522 
523   focus_chain = g_list_append (focus_chain, priv->hue_spinbutton);
524   focus_chain = g_list_append (focus_chain, priv->sat_spinbutton);
525   focus_chain = g_list_append (focus_chain, priv->val_spinbutton);
526   focus_chain = g_list_append (focus_chain, priv->red_spinbutton);
527   focus_chain = g_list_append (focus_chain, priv->green_spinbutton);
528   focus_chain = g_list_append (focus_chain, priv->blue_spinbutton);
529   focus_chain = g_list_append (focus_chain, priv->opacity_slider);
530   focus_chain = g_list_append (focus_chain, priv->opacity_entry);
531   focus_chain = g_list_append (focus_chain, priv->hex_entry);
532   gtk_container_set_focus_chain (GTK_CONTAINER (table), focus_chain);
533   g_list_free (focus_chain);
534 
535   /* Set up the palette */
536   table = gtk_grid_new ();
537   gtk_grid_set_row_spacing (GTK_GRID (table), 1);
538   gtk_grid_set_column_spacing (GTK_GRID (table), 1);
539   for (i = 0; i < CUSTOM_PALETTE_WIDTH; i++)
540     {
541       for (j = 0; j < CUSTOM_PALETTE_HEIGHT; j++)
542         {
543           make_palette_frame (colorsel, table, i, j);
544         }
545     }
546   set_selected_palette (colorsel, 0, 0);
547   priv->palette_frame = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
548   label = gtk_label_new_with_mnemonic (_("_Palette:"));
549   gtk_widget_set_halign (label, GTK_ALIGN_START);
550   gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
551   gtk_box_pack_start (GTK_BOX (priv->palette_frame), label, FALSE, FALSE, 0);
552 
553   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
554                                  priv->custom_palette[0][0]);
555 
556   gtk_box_pack_end (GTK_BOX (top_right_vbox), priv->palette_frame, FALSE, FALSE, 0);
557   gtk_box_pack_start (GTK_BOX (priv->palette_frame), table, FALSE, FALSE, 0);
558 
559   gtk_widget_show_all (top_hbox);
560 
561   /* hide unused stuff */
562 
563   if (priv->has_opacity == FALSE)
564     {
565       gtk_widget_hide (priv->opacity_label);
566       gtk_widget_hide (priv->opacity_slider);
567       gtk_widget_hide (priv->opacity_entry);
568     }
569 
570   if (priv->has_palette == FALSE)
571     {
572       gtk_widget_hide (priv->palette_frame);
573     }
574 
575   atk_obj = gtk_widget_get_accessible (priv->triangle_colorsel);
576   if (GTK_IS_ACCESSIBLE (atk_obj))
577     {
578       atk_object_set_name (atk_obj, _("Color Wheel"));
579       atk_object_set_role (gtk_widget_get_accessible (GTK_WIDGET (colorsel)), ATK_ROLE_COLOR_CHOOSER);
580       make_all_relations (atk_obj, priv);
581     }
582 
583   gtk_widget_pop_composite_child ();
584 }
585 
586 /* GObject methods */
587 static void
gcolor3_color_selection_finalize(GObject * object)588 gcolor3_color_selection_finalize (GObject *object)
589 {
590   G_OBJECT_CLASS (gcolor3_color_selection_parent_class)->finalize (object);
591 }
592 
593 static void
gcolor3_color_selection_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)594 gcolor3_color_selection_set_property (GObject         *object,
595                                       guint            prop_id,
596                                       const GValue    *value,
597                                       GParamSpec      *pspec)
598 {
599   Gcolor3ColorSelection *colorsel = GCOLOR3_COLOR_SELECTION (object);
600 
601   switch (prop_id)
602     {
603     case PROP_HAS_OPACITY_CONTROL:
604       gcolor3_color_selection_set_has_opacity_control (colorsel,
605                                                        g_value_get_boolean (value));
606       break;
607     case PROP_HAS_PALETTE:
608       gcolor3_color_selection_set_has_palette (colorsel,
609                                                g_value_get_boolean (value));
610       break;
611     case PROP_CURRENT_ALPHA:
612       gcolor3_color_selection_set_current_alpha (colorsel, g_value_get_uint (value));
613       break;
614     case PROP_CURRENT_RGBA:
615       gcolor3_color_selection_set_current_rgba (colorsel, g_value_get_boxed (value));
616       break;
617     default:
618       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
619       break;
620     }
621 
622 }
623 
624 static void
gcolor3_color_selection_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)625 gcolor3_color_selection_get_property (GObject     *object,
626                                       guint        prop_id,
627                                       GValue      *value,
628                                       GParamSpec  *pspec)
629 {
630   Gcolor3ColorSelection *colorsel = GCOLOR3_COLOR_SELECTION (object);
631 
632   switch (prop_id)
633     {
634     case PROP_HAS_OPACITY_CONTROL:
635       g_value_set_boolean (value, gcolor3_color_selection_get_has_opacity_control (colorsel));
636       break;
637     case PROP_HAS_PALETTE:
638       g_value_set_boolean (value, gcolor3_color_selection_get_has_palette (colorsel));
639       break;
640     case PROP_CURRENT_ALPHA:
641       g_value_set_uint (value, gcolor3_color_selection_get_current_alpha (colorsel));
642       break;
643     case PROP_CURRENT_RGBA:
644       {
645         GdkRGBA rgba;
646 
647         gcolor3_color_selection_get_current_rgba (colorsel, &rgba);
648         g_value_set_boxed (value, &rgba);
649       }
650       break;
651     default:
652       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
653       break;
654     }
655 }
656 
657 /* GtkWidget methods */
658 
659 static void
gcolor3_color_selection_destroy(GtkWidget * widget)660 gcolor3_color_selection_destroy (GtkWidget *widget)
661 {
662   Gcolor3ColorSelection *cselection = GCOLOR3_COLOR_SELECTION (widget);
663   Gcolor3ColorSelectionPrivate *priv = cselection->private_data;
664 
665   if (priv->dropper_grab_widget)
666     {
667       gtk_widget_destroy (priv->dropper_grab_widget);
668       priv->dropper_grab_widget = NULL;
669     }
670 
671   GTK_WIDGET_CLASS (gcolor3_color_selection_parent_class)->destroy (widget);
672 }
673 
674 static void
gcolor3_color_selection_realize(GtkWidget * widget)675 gcolor3_color_selection_realize (GtkWidget *widget)
676 {
677   Gcolor3ColorSelection *colorsel = GCOLOR3_COLOR_SELECTION (widget);
678   Gcolor3ColorSelectionPrivate *priv = colorsel->private_data;
679   GtkSettings *settings = gtk_widget_get_settings (widget);
680 
681   priv->settings_connection =  g_signal_connect (settings,
682                                                  "notify::gtk-color-palette",
683                                                  G_CALLBACK (palette_change_notify_instance),
684                                                  widget);
685   update_palette (colorsel);
686 
687   GTK_WIDGET_CLASS (gcolor3_color_selection_parent_class)->realize (widget);
688 }
689 
690 static void
gcolor3_color_selection_unrealize(GtkWidget * widget)691 gcolor3_color_selection_unrealize (GtkWidget *widget)
692 {
693   Gcolor3ColorSelection *colorsel = GCOLOR3_COLOR_SELECTION (widget);
694   Gcolor3ColorSelectionPrivate *priv = colorsel->private_data;
695   GtkSettings *settings = gtk_widget_get_settings (widget);
696 
697   g_signal_handler_disconnect (settings, priv->settings_connection);
698 
699   GTK_WIDGET_CLASS (gcolor3_color_selection_parent_class)->unrealize (widget);
700 }
701 
702 /* We override show-all since we have internal widgets that
703  * shouldn’t be shown when you call show_all(), like the
704  * palette and opacity sliders.
705  */
706 static void
gcolor3_color_selection_show_all(GtkWidget * widget)707 gcolor3_color_selection_show_all (GtkWidget *widget)
708 {
709   gtk_widget_show (widget);
710 }
711 
712 static gboolean
gcolor3_color_selection_grab_broken(GtkWidget * widget,UNUSED GdkEventGrabBroken * event)713 gcolor3_color_selection_grab_broken (GtkWidget                 *widget,
714                                      UNUSED GdkEventGrabBroken *event)
715 {
716   shutdown_eyedropper (widget);
717 
718   return TRUE;
719 }
720 
721 /*
722  *
723  * The Sample Color
724  *
725  */
726 
727 static void color_sample_draw_sample (Gcolor3ColorSelection *colorsel,
728                                       int                    which,
729                                       cairo_t *              cr);
730 static void color_sample_update_samples (Gcolor3ColorSelection *colorsel);
731 
732 static void
set_color_internal(Gcolor3ColorSelection * colorsel,gdouble * color)733 set_color_internal (Gcolor3ColorSelection *colorsel,
734                     gdouble               *color)
735 {
736   Gcolor3ColorSelectionPrivate *priv;
737   gint i;
738 
739   priv = colorsel->private_data;
740   priv->changing = TRUE;
741   priv->color[COLORSEL_RED] = color[0];
742   priv->color[COLORSEL_GREEN] = color[1];
743   priv->color[COLORSEL_BLUE] = color[2];
744   priv->color[COLORSEL_OPACITY] = color[3];
745   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
746                   priv->color[COLORSEL_GREEN],
747                   priv->color[COLORSEL_BLUE],
748                   &priv->color[COLORSEL_HUE],
749                   &priv->color[COLORSEL_SATURATION],
750                   &priv->color[COLORSEL_VALUE]);
751   if (priv->default_set == FALSE)
752     {
753       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
754         priv->old_color[i] = priv->color[i];
755     }
756   priv->default_set = TRUE;
757   priv->default_alpha_set = TRUE;
758   update_color (colorsel);
759 }
760 
761 static void
set_color_icon(GdkDragContext * context,gdouble * colors)762 set_color_icon (GdkDragContext *context,
763                 gdouble        *colors)
764 {
765   GdkPixbuf *pixbuf;
766   guint32 pixel;
767 
768   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE,
769                            8, 48, 32);
770 
771   pixel = (((UNSCALE (colors[COLORSEL_RED])   & 0xff00) << 16) |
772            ((UNSCALE (colors[COLORSEL_GREEN]) & 0xff00) << 8) |
773            ((UNSCALE (colors[COLORSEL_BLUE])  & 0xff00)));
774 
775   gdk_pixbuf_fill (pixbuf, pixel);
776 
777   gtk_drag_set_icon_pixbuf (context, pixbuf, -2, -2);
778   g_object_unref (pixbuf);
779 }
780 
781 static void
color_sample_drag_begin(GtkWidget * widget,GdkDragContext * context,gpointer data)782 color_sample_drag_begin (GtkWidget      *widget,
783                          GdkDragContext *context,
784                          gpointer        data)
785 {
786   Gcolor3ColorSelection *colorsel = data;
787   Gcolor3ColorSelectionPrivate *priv;
788   gdouble *colsrc;
789 
790   priv = colorsel->private_data;
791 
792   if (widget == priv->old_sample)
793     colsrc = priv->old_color;
794   else
795     colsrc = priv->color;
796 
797   set_color_icon (context, colsrc);
798 }
799 
800 static void
color_sample_drag_end(GtkWidget * widget,UNUSED GdkDragContext * context,UNUSED gpointer data)801 color_sample_drag_end (GtkWidget             *widget,
802                        UNUSED GdkDragContext *context,
803                        UNUSED gpointer        data)
804 {
805   g_object_set_data (G_OBJECT (widget), I_("gtk-color-selection-drag-window"), NULL);
806 }
807 
808 static void
color_sample_drop_handle(GtkWidget * widget,UNUSED GdkDragContext * context,UNUSED gint x,UNUSED gint y,GtkSelectionData * selection_data,UNUSED guint info,UNUSED guint time,gpointer data)809 color_sample_drop_handle (GtkWidget             *widget,
810                           UNUSED GdkDragContext *context,
811                           UNUSED gint            x,
812                           UNUSED gint            y,
813                           GtkSelectionData      *selection_data,
814                           UNUSED guint           info,
815                           UNUSED guint           time,
816                           gpointer               data)
817 {
818   Gcolor3ColorSelection *colorsel = data;
819   Gcolor3ColorSelectionPrivate *priv;
820   gint length;
821   guint16 *vals;
822   gdouble color[4];
823   priv = colorsel->private_data;
824 
825   /* This is currently a guint16 array of the format:
826    * R
827    * G
828    * B
829    * opacity
830    */
831 
832   length = gtk_selection_data_get_length (selection_data);
833 
834   if (length < 0)
835     return;
836 
837   /* We accept drops with the wrong format, since the KDE color
838    * chooser incorrectly drops application/x-color with format 8.
839    */
840   if (length != 8)
841     {
842       g_warning ("Received invalid color data");
843       return;
844     }
845 
846   vals = (guint16 *) gtk_selection_data_get_data (selection_data);
847 
848   if (widget == priv->cur_sample)
849     {
850       color[0] = (gdouble)vals[0] / 0xffff;
851       color[1] = (gdouble)vals[1] / 0xffff;
852       color[2] = (gdouble)vals[2] / 0xffff;
853       color[3] = (gdouble)vals[3] / 0xffff;
854 
855       set_color_internal (colorsel, color);
856     }
857 }
858 
859 static void
color_sample_drag_handle(GtkWidget * widget,UNUSED GdkDragContext * context,GtkSelectionData * selection_data,UNUSED guint info,UNUSED guint time,gpointer data)860 color_sample_drag_handle (GtkWidget             *widget,
861                           UNUSED GdkDragContext *context,
862                           GtkSelectionData      *selection_data,
863                           UNUSED guint           info,
864                           UNUSED guint           time,
865                           gpointer               data)
866 {
867   Gcolor3ColorSelection *colorsel = data;
868   Gcolor3ColorSelectionPrivate *priv;
869   guint16 vals[4];
870   gdouble *colsrc;
871 
872   priv = colorsel->private_data;
873 
874   if (widget == priv->old_sample)
875     colsrc = priv->old_color;
876   else
877     colsrc = priv->color;
878 
879   vals[0] = colsrc[COLORSEL_RED] * 0xffff;
880   vals[1] = colsrc[COLORSEL_GREEN] * 0xffff;
881   vals[2] = colsrc[COLORSEL_BLUE] * 0xffff;
882   vals[3] = priv->has_opacity ? colsrc[COLORSEL_OPACITY] * 0xffff : 0xffff;
883 
884   gtk_selection_data_set (selection_data,
885                           gdk_atom_intern_static_string ("application/x-color"),
886                           16, (guchar *)vals, 8);
887 }
888 
889 /* which = 0 means draw old sample, which = 1 means draw new */
890 static void
color_sample_draw_sample(Gcolor3ColorSelection * colorsel,int which,cairo_t * cr)891 color_sample_draw_sample (Gcolor3ColorSelection *colorsel,
892                           int                    which,
893                           cairo_t               *cr)
894 {
895   GtkWidget *da;
896   gint x, y, goff;
897   Gcolor3ColorSelectionPrivate *priv;
898   int width, height;
899 
900   g_return_if_fail (colorsel != NULL);
901   priv = colorsel->private_data;
902 
903   g_return_if_fail (priv->sample_area != NULL);
904   if (!gtk_widget_is_drawable (priv->sample_area))
905     return;
906 
907   if (which == 0)
908     {
909       da = priv->old_sample;
910       goff = 0;
911     }
912   else
913     {
914       GtkAllocation old_sample_allocation;
915 
916       da = priv->cur_sample;
917       gtk_widget_get_allocation (priv->old_sample, &old_sample_allocation);
918       goff =  old_sample_allocation.width % 32;
919     }
920 
921   /* Below needs tweaking for non-power-of-two */
922   width = gtk_widget_get_allocated_width (da);
923   height = gtk_widget_get_allocated_height (da);
924 
925   if (priv->has_opacity)
926     {
927       /* Draw checks in background */
928 
929       cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
930       cairo_rectangle (cr, 0, 0, width, height);
931       cairo_fill (cr);
932 
933       cairo_set_source_rgb (cr, 0.75, 0.75, 0.75);
934       for (x = goff & -CHECK_SIZE; x < goff + width; x += CHECK_SIZE)
935         for (y = 0; y < height; y += CHECK_SIZE)
936           if ((x / CHECK_SIZE + y / CHECK_SIZE) % 2 == 0)
937             cairo_rectangle (cr, x - goff, y, CHECK_SIZE, CHECK_SIZE);
938       cairo_fill (cr);
939     }
940 
941   if (which == 0)
942     {
943       cairo_set_source_rgba (cr,
944                              priv->old_color[COLORSEL_RED],
945                              priv->old_color[COLORSEL_GREEN],
946                              priv->old_color[COLORSEL_BLUE],
947                              priv->has_opacity ?
948                                 priv->old_color[COLORSEL_OPACITY] : 1.0);
949     }
950   else
951     {
952       cairo_set_source_rgba (cr,
953                              priv->color[COLORSEL_RED],
954                              priv->color[COLORSEL_GREEN],
955                              priv->color[COLORSEL_BLUE],
956                              priv->has_opacity ?
957                                priv->color[COLORSEL_OPACITY] : 1.0);
958     }
959 
960   cairo_rectangle (cr, 0, 0, width, height);
961   cairo_fill (cr);
962 }
963 
964 
965 static void
color_sample_update_samples(Gcolor3ColorSelection * colorsel)966 color_sample_update_samples (Gcolor3ColorSelection *colorsel)
967 {
968   Gcolor3ColorSelectionPrivate *priv = colorsel->private_data;
969   gtk_widget_queue_draw (priv->old_sample);
970   gtk_widget_queue_draw (priv->cur_sample);
971 }
972 
973 static gboolean
color_old_sample_draw(UNUSED GtkWidget * da,cairo_t * cr,Gcolor3ColorSelection * colorsel)974 color_old_sample_draw (UNUSED GtkWidget      *da,
975                        cairo_t               *cr,
976                        Gcolor3ColorSelection *colorsel)
977 {
978   color_sample_draw_sample (colorsel, 0, cr);
979   return FALSE;
980 }
981 
982 
983 static gboolean
color_cur_sample_draw(UNUSED GtkWidget * da,cairo_t * cr,Gcolor3ColorSelection * colorsel)984 color_cur_sample_draw (UNUSED GtkWidget      *da,
985                        cairo_t               *cr,
986                        Gcolor3ColorSelection *colorsel)
987 {
988   color_sample_draw_sample (colorsel, 1, cr);
989   return FALSE;
990 }
991 
992 static void
color_sample_setup_dnd(Gcolor3ColorSelection * colorsel,GtkWidget * sample)993 color_sample_setup_dnd (Gcolor3ColorSelection *colorsel, GtkWidget *sample)
994 {
995   static const GtkTargetEntry targets[] = {
996     { "application/x-color", 0 }
997   };
998   Gcolor3ColorSelectionPrivate *priv;
999   priv = colorsel->private_data;
1000 
1001   gtk_drag_source_set (sample,
1002                        GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1003                        targets, 1,
1004                        GDK_ACTION_COPY | GDK_ACTION_MOVE);
1005 
1006   g_signal_connect (sample, "drag-begin",
1007                     G_CALLBACK (color_sample_drag_begin),
1008                     colorsel);
1009   if (sample == priv->cur_sample)
1010     {
1011 
1012       gtk_drag_dest_set (sample,
1013                          GTK_DEST_DEFAULT_HIGHLIGHT |
1014                          GTK_DEST_DEFAULT_MOTION |
1015                          GTK_DEST_DEFAULT_DROP,
1016                          targets, 1,
1017                          GDK_ACTION_COPY);
1018 
1019       g_signal_connect (sample, "drag-end",
1020                         G_CALLBACK (color_sample_drag_end),
1021                         colorsel);
1022     }
1023 
1024   g_signal_connect (sample, "drag-data-get",
1025                     G_CALLBACK (color_sample_drag_handle),
1026                     colorsel);
1027   g_signal_connect (sample, "drag-data-received",
1028                     G_CALLBACK (color_sample_drop_handle),
1029                     colorsel);
1030 
1031 }
1032 
1033 static void
update_tooltips(Gcolor3ColorSelection * colorsel)1034 update_tooltips (Gcolor3ColorSelection *colorsel)
1035 {
1036   Gcolor3ColorSelectionPrivate *priv;
1037 
1038   priv = colorsel->private_data;
1039 
1040   if (priv->has_palette == TRUE)
1041     {
1042       gtk_widget_set_tooltip_text (priv->old_sample,
1043         _("The previously-selected color, for comparison to the color "
1044           "you’re selecting now. You can drag this color to a palette "
1045           "entry, or select this color as current by dragging it to the "
1046           "other color swatch alongside."));
1047 
1048       gtk_widget_set_tooltip_text (priv->cur_sample,
1049         _("The color you’ve chosen. You can drag this color to a palette "
1050           "entry to save it for use in the future."));
1051     }
1052   else
1053     {
1054       gtk_widget_set_tooltip_text (priv->old_sample,
1055         _("The previously-selected color, for comparison to the color "
1056           "you’re selecting now."));
1057 
1058       gtk_widget_set_tooltip_text (priv->cur_sample,
1059         _("The color you’ve chosen."));
1060     }
1061 }
1062 
1063 static void
color_sample_new(Gcolor3ColorSelection * colorsel)1064 color_sample_new (Gcolor3ColorSelection *colorsel)
1065 {
1066   Gcolor3ColorSelectionPrivate *priv;
1067 
1068   priv = colorsel->private_data;
1069 
1070   priv->sample_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1071   priv->old_sample = gtk_drawing_area_new ();
1072   priv->cur_sample = gtk_drawing_area_new ();
1073 
1074   gtk_box_pack_start (GTK_BOX (priv->sample_area), priv->old_sample,
1075                       TRUE, TRUE, 0);
1076   gtk_box_pack_start (GTK_BOX (priv->sample_area), priv->cur_sample,
1077                       TRUE, TRUE, 0);
1078 
1079   g_signal_connect (priv->old_sample, "draw",
1080                     G_CALLBACK (color_old_sample_draw),
1081                     colorsel);
1082   g_signal_connect (priv->cur_sample, "draw",
1083                     G_CALLBACK (color_cur_sample_draw),
1084                     colorsel);
1085 
1086   color_sample_setup_dnd (colorsel, priv->old_sample);
1087   color_sample_setup_dnd (colorsel, priv->cur_sample);
1088 
1089   update_tooltips (colorsel);
1090 
1091   gtk_widget_show_all (priv->sample_area);
1092 }
1093 
1094 
1095 /* The palette area code */
1096 
1097 static void
palette_get_color(GtkWidget * drawing_area,gdouble * color)1098 palette_get_color (GtkWidget *drawing_area, gdouble *color)
1099 {
1100   gdouble *color_val;
1101 
1102   g_return_if_fail (color != NULL);
1103 
1104   color_val = g_object_get_data (G_OBJECT (drawing_area), "color_val");
1105   if (color_val == NULL)
1106     {
1107       /* Default to white for no good reason */
1108       color[0] = 1.0;
1109       color[1] = 1.0;
1110       color[2] = 1.0;
1111       color[3] = 1.0;
1112       return;
1113     }
1114 
1115   color[0] = color_val[0];
1116   color[1] = color_val[1];
1117   color[2] = color_val[2];
1118   color[3] = 1.0;
1119 }
1120 
1121 static gboolean
palette_draw(GtkWidget * drawing_area,cairo_t * cr,UNUSED gpointer data)1122 palette_draw (GtkWidget       *drawing_area,
1123               cairo_t         *cr,
1124               UNUSED gpointer  data)
1125 {
1126   GtkStyleContext *context;
1127   gint focus_width;
1128   GdkRGBA color;
1129 
1130   context = gtk_widget_get_style_context (drawing_area);
1131   gtk_style_context_get_background_color (context, 0, &color);
1132   gdk_cairo_set_source_rgba (cr, &color);
1133   cairo_paint (cr);
1134 
1135   if (gtk_widget_has_visible_focus (drawing_area))
1136     {
1137       set_focus_line_attributes (drawing_area, cr, &focus_width);
1138 
1139       cairo_rectangle (cr,
1140                        focus_width / 2., focus_width / 2.,
1141                        gtk_widget_get_allocated_width (drawing_area) - focus_width,
1142                        gtk_widget_get_allocated_height (drawing_area) - focus_width);
1143       cairo_stroke (cr);
1144     }
1145 
1146   return FALSE;
1147 }
1148 
1149 static void
set_focus_line_attributes(GtkWidget * drawing_area,cairo_t * cr,gint * focus_width)1150 set_focus_line_attributes (GtkWidget *drawing_area,
1151                            cairo_t   *cr,
1152                            gint      *focus_width)
1153 {
1154   gdouble color[4];
1155   gint8 *dash_list;
1156 
1157   gtk_widget_style_get (drawing_area,
1158                         "focus-line-width", focus_width,
1159                         "focus-line-pattern", (gchar *)&dash_list,
1160                         NULL);
1161 
1162   palette_get_color (drawing_area, color);
1163 
1164   if (INTENSITY (color[0], color[1], color[2]) > 0.5)
1165     cairo_set_source_rgb (cr, 0., 0., 0.);
1166   else
1167     cairo_set_source_rgb (cr, 1., 1., 1.);
1168 
1169   cairo_set_line_width (cr, *focus_width);
1170 
1171   if (dash_list[0])
1172     {
1173       gint n_dashes = strlen ((gchar *)dash_list);
1174       gdouble *dashes = g_new (gdouble, n_dashes);
1175       gdouble total_length = 0;
1176       gdouble dash_offset;
1177       gint i;
1178 
1179       for (i = 0; i < n_dashes; i++)
1180         {
1181           dashes[i] = dash_list[i];
1182           total_length += dash_list[i];
1183         }
1184 
1185       /* The dash offset here aligns the pattern to integer pixels
1186        * by starting the dash at the right side of the left border
1187        * Negative dash offsets in cairo don't work
1188        * (https://bugs.freedesktop.org/show_bug.cgi?id=2729)
1189        */
1190       dash_offset = - *focus_width / 2.;
1191       while (dash_offset < 0)
1192         dash_offset += total_length;
1193 
1194       cairo_set_dash (cr, dashes, n_dashes, dash_offset);
1195       g_free (dashes);
1196     }
1197 
1198   g_free (dash_list);
1199 }
1200 
1201 static void
palette_drag_begin(GtkWidget * widget,UNUSED GdkDragContext * context,UNUSED gpointer data)1202 palette_drag_begin (GtkWidget             *widget,
1203                     UNUSED GdkDragContext *context,
1204                     UNUSED gpointer        data)
1205 {
1206   gdouble colors[4];
1207 
1208   palette_get_color (widget, colors);
1209   set_color_icon (context, colors);
1210 }
1211 
1212 static void
palette_drag_handle(GtkWidget * widget,UNUSED GdkDragContext * context,GtkSelectionData * selection_data,UNUSED guint info,UNUSED guint time,UNUSED gpointer data)1213 palette_drag_handle (GtkWidget             *widget,
1214                      UNUSED GdkDragContext *context,
1215                      GtkSelectionData      *selection_data,
1216                      UNUSED guint           info,
1217                      UNUSED guint           time,
1218                      UNUSED gpointer        data)
1219 {
1220   guint16 vals[4];
1221   gdouble colsrc[4];
1222 
1223   palette_get_color (widget, colsrc);
1224 
1225   vals[0] = colsrc[COLORSEL_RED] * 0xffff;
1226   vals[1] = colsrc[COLORSEL_GREEN] * 0xffff;
1227   vals[2] = colsrc[COLORSEL_BLUE] * 0xffff;
1228   vals[3] = 0xffff;
1229 
1230   gtk_selection_data_set (selection_data,
1231                           gdk_atom_intern_static_string ("application/x-color"),
1232                           16, (guchar *)vals, 8);
1233 }
1234 
1235 static void
palette_drag_end(GtkWidget * widget,UNUSED GdkDragContext * context,UNUSED gpointer data)1236 palette_drag_end (GtkWidget             *widget,
1237                   UNUSED GdkDragContext *context,
1238                   UNUSED gpointer        data)
1239 {
1240   g_object_set_data (G_OBJECT (widget), I_("gtk-color-selection-drag-window"), NULL);
1241 }
1242 
1243 static GdkRGBA *
get_current_colors(Gcolor3ColorSelection * colorsel)1244 get_current_colors (Gcolor3ColorSelection *colorsel)
1245 {
1246   GtkSettings *settings;
1247   GdkRGBA *colors = NULL;
1248   gint n_colors = 0;
1249   gchar *palette;
1250 
1251   settings = gtk_widget_get_settings (GTK_WIDGET (colorsel));
1252   g_object_get (settings, "gtk-color-palette", &palette, NULL);
1253 
1254   if (!gcolor3_color_selection_palette_from_string (palette, &colors, &n_colors))
1255     {
1256       gcolor3_color_selection_palette_from_string (DEFAULT_COLOR_PALETTE,
1257                                                &colors,
1258                                                &n_colors);
1259     }
1260   else
1261     {
1262       /* If there are less colors provided than the number of slots in the
1263        * color selection, we fill in the rest from the defaults.
1264        */
1265       if (n_colors < (CUSTOM_PALETTE_WIDTH * CUSTOM_PALETTE_HEIGHT))
1266         {
1267           GdkRGBA *tmp_colors = colors;
1268           gint tmp_n_colors = n_colors;
1269 
1270           gcolor3_color_selection_palette_from_string (DEFAULT_COLOR_PALETTE,
1271                                                        &colors,
1272                                                        &n_colors);
1273           memcpy (colors, tmp_colors, sizeof (GdkRGBA) * tmp_n_colors);
1274 
1275           g_free (tmp_colors);
1276         }
1277     }
1278 
1279   /* make sure that we fill every slot */
1280   g_assert (n_colors == CUSTOM_PALETTE_WIDTH * CUSTOM_PALETTE_HEIGHT);
1281   g_free (palette);
1282 
1283   return colors;
1284 }
1285 
1286 /* Changes the model color */
1287 static void
palette_change_color(GtkWidget * drawing_area,Gcolor3ColorSelection * colorsel,gdouble * color)1288 palette_change_color (GtkWidget             *drawing_area,
1289                       Gcolor3ColorSelection *colorsel,
1290                       gdouble               *color)
1291 {
1292   gint x, y;
1293   Gcolor3ColorSelectionPrivate *priv;
1294   GdkRGBA gdk_color;
1295   GdkRGBA *current_colors;
1296   GdkScreen *screen;
1297 
1298   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
1299   g_return_if_fail (GTK_IS_DRAWING_AREA (drawing_area));
1300 
1301   priv = colorsel->private_data;
1302 
1303   gdk_color.red = color[0];
1304   gdk_color.green = color[1];
1305   gdk_color.blue = color[2];
1306 
1307   x = 0;
1308   y = 0;                        /* Quiet GCC */
1309   while (x < CUSTOM_PALETTE_WIDTH)
1310     {
1311       y = 0;
1312       while (y < CUSTOM_PALETTE_HEIGHT)
1313         {
1314           if (priv->custom_palette[x][y] == drawing_area)
1315             goto out;
1316 
1317           ++y;
1318         }
1319 
1320       ++x;
1321     }
1322 
1323  out:
1324 
1325   g_assert (x < CUSTOM_PALETTE_WIDTH || y < CUSTOM_PALETTE_HEIGHT);
1326 
1327   current_colors = get_current_colors (colorsel);
1328   current_colors[y * CUSTOM_PALETTE_WIDTH + x] = gdk_color;
1329 
1330   screen = gtk_widget_get_screen (GTK_WIDGET (colorsel));
1331   if (change_palette_hook != default_change_palette_func)
1332     (* change_palette_hook) (screen, current_colors,
1333                              CUSTOM_PALETTE_WIDTH * CUSTOM_PALETTE_HEIGHT);
1334   else if (noscreen_change_palette_hook != default_noscreen_change_palette_func)
1335     {
1336       if (screen != gdk_screen_get_default ())
1337         g_warning ("gcolor3_color_selection_set_change_palette_hook used by "
1338                    "widget is not on the default screen.");
1339       (* noscreen_change_palette_hook) (current_colors,
1340                                         CUSTOM_PALETTE_WIDTH * CUSTOM_PALETTE_HEIGHT);
1341     }
1342   else
1343     (* change_palette_hook) (screen, current_colors,
1344                              CUSTOM_PALETTE_WIDTH * CUSTOM_PALETTE_HEIGHT);
1345 
1346   g_free (current_colors);
1347 }
1348 
1349 /* Changes the view color */
1350 static void
palette_set_color(GtkWidget * drawing_area,Gcolor3ColorSelection * colorsel,gdouble * color)1351 palette_set_color (GtkWidget             *drawing_area,
1352                    Gcolor3ColorSelection *colorsel,
1353                    gdouble               *color)
1354 {
1355   gdouble *new_color = g_new (double, 4);
1356   GdkRGBA rgba;
1357 
1358   rgba.red = color[0];
1359   rgba.green = color[1];
1360   rgba.blue = color[2];
1361   rgba.alpha = 1;
1362 
1363   gtk_widget_override_background_color (drawing_area, GTK_STATE_FLAG_NORMAL, &rgba);
1364 
1365   if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drawing_area), "color_set")) == 0)
1366     {
1367       static const GtkTargetEntry targets[] = {
1368         { "application/x-color", 0 }
1369       };
1370       gtk_drag_source_set (drawing_area,
1371                            GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1372                            targets, 1,
1373                            GDK_ACTION_COPY | GDK_ACTION_MOVE);
1374 
1375       g_signal_connect (drawing_area, "drag-begin",
1376                         G_CALLBACK (palette_drag_begin),
1377                         colorsel);
1378       g_signal_connect (drawing_area, "drag-data-get",
1379                         G_CALLBACK (palette_drag_handle),
1380                         colorsel);
1381 
1382       g_object_set_data (G_OBJECT (drawing_area), I_("color_set"),
1383                          GINT_TO_POINTER (1));
1384     }
1385 
1386   new_color[0] = color[0];
1387   new_color[1] = color[1];
1388   new_color[2] = color[2];
1389   new_color[3] = 1.0;
1390 
1391   g_object_set_data_full (G_OBJECT (drawing_area),
1392                           I_("color_val"), new_color, (GDestroyNotify)g_free);
1393 }
1394 
1395 static void
save_color_selected(UNUSED GtkWidget * menuitem,gpointer data)1396 save_color_selected (UNUSED GtkWidget *menuitem,
1397                      gpointer          data)
1398 {
1399   Gcolor3ColorSelection *colorsel;
1400   GtkWidget *drawing_area;
1401   Gcolor3ColorSelectionPrivate *priv;
1402 
1403   drawing_area = GTK_WIDGET (data);
1404 
1405   colorsel = GCOLOR3_COLOR_SELECTION (g_object_get_data (G_OBJECT (drawing_area),
1406                                       "gtk-color-sel"));
1407 
1408   priv = colorsel->private_data;
1409 
1410   palette_change_color (drawing_area, colorsel, priv->color);
1411 }
1412 
1413 static void
do_popup(Gcolor3ColorSelection * colorsel,GtkWidget * drawing_area,const GdkEvent * trigger_event)1414 do_popup (Gcolor3ColorSelection *colorsel,
1415           GtkWidget             *drawing_area,
1416           const GdkEvent        *trigger_event)
1417 {
1418   GtkWidget *menu;
1419   GtkWidget *mi;
1420 
1421   g_object_set_data (G_OBJECT (drawing_area),
1422                      I_("gtk-color-sel"),
1423                      colorsel);
1424 
1425   menu = gtk_menu_new ();
1426   g_signal_connect (menu, "hide", G_CALLBACK (gtk_widget_destroy), NULL);
1427 
1428   mi = gtk_menu_item_new_with_mnemonic (_("_Save color here"));
1429 
1430   g_signal_connect (mi, "activate",
1431                     G_CALLBACK (save_color_selected),
1432                     drawing_area);
1433 
1434   gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1435 
1436   gtk_widget_show_all (mi);
1437 
1438   if (trigger_event && gdk_event_triggers_context_menu (trigger_event))
1439     gtk_menu_popup_at_pointer (GTK_MENU (menu), trigger_event);
1440   else
1441     gtk_menu_popup_at_widget (GTK_MENU (menu),
1442                               drawing_area,
1443                               GDK_GRAVITY_CENTER,
1444                               GDK_GRAVITY_NORTH_WEST,
1445                               trigger_event);
1446 }
1447 
1448 
1449 static gboolean
palette_enter(GtkWidget * drawing_area,UNUSED GdkEventCrossing * event,UNUSED gpointer data)1450 palette_enter (GtkWidget               *drawing_area,
1451                UNUSED GdkEventCrossing *event,
1452                UNUSED gpointer          data)
1453 {
1454   g_object_set_data (G_OBJECT (drawing_area),
1455                      I_("gtk-colorsel-have-pointer"),
1456                      GUINT_TO_POINTER (TRUE));
1457 
1458   return FALSE;
1459 }
1460 
1461 static gboolean
palette_leave(GtkWidget * drawing_area,UNUSED GdkEventCrossing * event,UNUSED gpointer data)1462 palette_leave (GtkWidget               *drawing_area,
1463                UNUSED GdkEventCrossing *event,
1464                UNUSED gpointer          data)
1465 {
1466   g_object_set_data (G_OBJECT (drawing_area),
1467                      I_("gtk-colorsel-have-pointer"),
1468                      NULL);
1469 
1470   return FALSE;
1471 }
1472 
1473 static gboolean
palette_press(GtkWidget * drawing_area,GdkEventButton * event,gpointer data)1474 palette_press (GtkWidget      *drawing_area,
1475                GdkEventButton *event,
1476                gpointer        data)
1477 {
1478   Gcolor3ColorSelection *colorsel = GCOLOR3_COLOR_SELECTION (data);
1479 
1480   gtk_widget_grab_focus (drawing_area);
1481 
1482   if (gdk_event_triggers_context_menu ((GdkEvent *) event))
1483     {
1484       do_popup (colorsel, drawing_area, (GdkEvent *) event);
1485       return TRUE;
1486     }
1487 
1488   return FALSE;
1489 }
1490 
1491 static gboolean
palette_release(GtkWidget * drawing_area,GdkEventButton * event,gpointer data)1492 palette_release (GtkWidget      *drawing_area,
1493                  GdkEventButton *event,
1494                  gpointer        data)
1495 {
1496   Gcolor3ColorSelection *colorsel = GCOLOR3_COLOR_SELECTION (data);
1497 
1498   gtk_widget_grab_focus (drawing_area);
1499 
1500   if (event->button == GDK_BUTTON_PRIMARY &&
1501       g_object_get_data (G_OBJECT (drawing_area),
1502                          "gtk-colorsel-have-pointer") != NULL)
1503     {
1504       if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drawing_area), "color_set")) != 0)
1505         {
1506           gdouble color[4];
1507           palette_get_color (drawing_area, color);
1508           set_color_internal (colorsel, color);
1509         }
1510     }
1511 
1512   return FALSE;
1513 }
1514 
1515 static void
palette_drop_handle(GtkWidget * widget,UNUSED GdkDragContext * context,UNUSED gint x,UNUSED gint y,GtkSelectionData * selection_data,UNUSED guint info,UNUSED guint time,gpointer data)1516 palette_drop_handle (GtkWidget             *widget,
1517                      UNUSED GdkDragContext *context,
1518                      UNUSED gint            x,
1519                      UNUSED gint            y,
1520                      GtkSelectionData      *selection_data,
1521                      UNUSED guint           info,
1522                      UNUSED guint           time,
1523                      gpointer               data)
1524 {
1525   Gcolor3ColorSelection *colorsel = GCOLOR3_COLOR_SELECTION (data);
1526   gint length;
1527   guint16 *vals;
1528   gdouble color[4];
1529 
1530   length = gtk_selection_data_get_length (selection_data);
1531 
1532   if (length < 0)
1533     return;
1534 
1535   /* We accept drops with the wrong format, since the KDE color
1536    * chooser incorrectly drops application/x-color with format 8.
1537    */
1538   if (length != 8)
1539     {
1540       g_warning ("Received invalid color data");
1541       return;
1542     }
1543 
1544   vals = (guint16 *) gtk_selection_data_get_data (selection_data);
1545 
1546   color[0] = (gdouble)vals[0] / 0xffff;
1547   color[1] = (gdouble)vals[1] / 0xffff;
1548   color[2] = (gdouble)vals[2] / 0xffff;
1549   color[3] = (gdouble)vals[3] / 0xffff;
1550   palette_change_color (widget, colorsel, color);
1551   set_color_internal (colorsel, color);
1552 }
1553 
1554 static gint
palette_activate(GtkWidget * widget,GdkEventKey * event,gpointer data)1555 palette_activate (GtkWidget   *widget,
1556                   GdkEventKey *event,
1557                   gpointer     data)
1558 {
1559   /* should have a drawing area subclass with an activate signal */
1560   if ((event->keyval == GDK_KEY_space) ||
1561       (event->keyval == GDK_KEY_Return) ||
1562       (event->keyval == GDK_KEY_ISO_Enter) ||
1563       (event->keyval == GDK_KEY_KP_Enter) ||
1564       (event->keyval == GDK_KEY_KP_Space))
1565     {
1566       if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "color_set")) != 0)
1567         {
1568           gdouble color[4];
1569           palette_get_color (widget, color);
1570           set_color_internal (GCOLOR3_COLOR_SELECTION (data), color);
1571         }
1572       return TRUE;
1573     }
1574 
1575   return FALSE;
1576 }
1577 
1578 static gboolean
palette_popup(GtkWidget * widget,gpointer data)1579 palette_popup (GtkWidget *widget,
1580                gpointer   data)
1581 {
1582   do_popup (data, widget, NULL);
1583   return TRUE;
1584 }
1585 
1586 
1587 static GtkWidget*
palette_new(Gcolor3ColorSelection * colorsel)1588 palette_new (Gcolor3ColorSelection *colorsel)
1589 {
1590   GtkWidget *retval;
1591 
1592   static const GtkTargetEntry targets[] = {
1593     { "application/x-color", 0 }
1594   };
1595 
1596   retval = gtk_drawing_area_new ();
1597 
1598   gtk_widget_set_can_focus (retval, TRUE);
1599 
1600   g_object_set_data (G_OBJECT (retval), I_("color_set"), GINT_TO_POINTER (0));
1601   gtk_widget_set_events (retval, GDK_BUTTON_PRESS_MASK
1602                          | GDK_BUTTON_RELEASE_MASK
1603                          | GDK_ENTER_NOTIFY_MASK
1604                          | GDK_LEAVE_NOTIFY_MASK);
1605 
1606   g_signal_connect (retval, "draw",
1607                     G_CALLBACK (palette_draw), colorsel);
1608   g_signal_connect (retval, "button-press-event",
1609                     G_CALLBACK (palette_press), colorsel);
1610   g_signal_connect (retval, "button-release-event",
1611                     G_CALLBACK (palette_release), colorsel);
1612   g_signal_connect (retval, "enter-notify-event",
1613                     G_CALLBACK (palette_enter), colorsel);
1614   g_signal_connect (retval, "leave-notify-event",
1615                     G_CALLBACK (palette_leave), colorsel);
1616   g_signal_connect (retval, "key-press-event",
1617                     G_CALLBACK (palette_activate), colorsel);
1618   g_signal_connect (retval, "popup-menu",
1619                     G_CALLBACK (palette_popup), colorsel);
1620 
1621   gtk_drag_dest_set (retval,
1622                      GTK_DEST_DEFAULT_HIGHLIGHT |
1623                      GTK_DEST_DEFAULT_MOTION |
1624                      GTK_DEST_DEFAULT_DROP,
1625                      targets, 1,
1626                      GDK_ACTION_COPY);
1627 
1628   g_signal_connect (retval, "drag-end",
1629                     G_CALLBACK (palette_drag_end), NULL);
1630   g_signal_connect (retval, "drag-data-received",
1631                     G_CALLBACK (palette_drop_handle), colorsel);
1632 
1633   gtk_widget_set_tooltip_text (retval,
1634     _("Click this palette entry to make it the current color. "
1635       "To change this entry, drag a color swatch here or right-click "
1636       "it and select “Save color here.”"));
1637   return retval;
1638 }
1639 
1640 
1641 /* The actual Gcolor3ColorSelection widget */
1642 
1643 static GdkCursor *
make_picker_cursor(GdkScreen * screen)1644 make_picker_cursor (GdkScreen *screen)
1645 {
1646   GdkCursor *cursor;
1647 
1648   cursor = gdk_cursor_new_from_name (gdk_screen_get_display (screen),
1649                                      "color-picker");
1650 
1651   if (!cursor)
1652     {
1653       GdkPixbuf *pixbuf;
1654 
1655       pixbuf = gdk_pixbuf_new_from_data (dropper_bits,
1656                                          GDK_COLORSPACE_RGB, TRUE, 8,
1657                                          DROPPER_WIDTH, DROPPER_HEIGHT,
1658                                          DROPPER_STRIDE,
1659                                          NULL, NULL);
1660 
1661       cursor = gdk_cursor_new_from_pixbuf (gdk_screen_get_display (screen),
1662                                            pixbuf,
1663                                            DROPPER_X_HOT, DROPPER_Y_HOT);
1664 
1665       g_object_unref (pixbuf);
1666     }
1667 
1668   return cursor;
1669 }
1670 
1671 static void
grab_color_at_pointer(GdkScreen * screen,GdkDevice * device,gint x_root,gint y_root,gpointer data)1672 grab_color_at_pointer (GdkScreen *screen,
1673                        GdkDevice *device,
1674                        gint       x_root,
1675                        gint       y_root,
1676                        gpointer   data)
1677 {
1678   GdkPixbuf *pixbuf;
1679   guchar *pixels;
1680   Gcolor3ColorSelection *colorsel = data;
1681   Gcolor3ColorSelectionPrivate *priv;
1682   GdkWindow *root_window = gdk_screen_get_root_window (screen);
1683 
1684   priv = colorsel->private_data;
1685 
1686   pixbuf = gdk_pixbuf_get_from_window (root_window,
1687                                        x_root, y_root,
1688                                        1, 1);
1689   if (!pixbuf)
1690     {
1691       gint x, y;
1692       GdkWindow *window = gdk_device_get_window_at_position (device, &x, &y);
1693       if (!window)
1694         return;
1695       pixbuf = gdk_pixbuf_get_from_window (window,
1696                                            x, y,
1697                                            1, 1);
1698       if (!pixbuf)
1699         return;
1700     }
1701   pixels = gdk_pixbuf_get_pixels (pixbuf);
1702   priv->color[COLORSEL_RED] = SCALE(pixels[0] * 0x101);
1703   priv->color[COLORSEL_GREEN] = SCALE(pixels[1] * 0x101);
1704   priv->color[COLORSEL_BLUE] = SCALE(pixels[2] * 0x101);
1705   g_object_unref (pixbuf);
1706 
1707   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1708                   priv->color[COLORSEL_GREEN],
1709                   priv->color[COLORSEL_BLUE],
1710                   &priv->color[COLORSEL_HUE],
1711                   &priv->color[COLORSEL_SATURATION],
1712                   &priv->color[COLORSEL_VALUE]);
1713 
1714   update_color (colorsel);
1715 }
1716 
1717 static void
shutdown_eyedropper(GtkWidget * widget)1718 shutdown_eyedropper (GtkWidget *widget)
1719 {
1720   Gcolor3ColorSelection *colorsel;
1721   Gcolor3ColorSelectionPrivate *priv;
1722 
1723   colorsel = GCOLOR3_COLOR_SELECTION (widget);
1724   priv = colorsel->private_data;
1725 
1726   if (priv->has_grab)
1727     {
1728       gdk_device_ungrab (priv->keyboard_device, priv->grab_time);
1729       gdk_device_ungrab (priv->pointer_device, priv->grab_time);
1730       gtk_device_grab_remove (priv->dropper_grab_widget, priv->pointer_device);
1731 
1732       priv->has_grab = FALSE;
1733       priv->keyboard_device = NULL;
1734       priv->pointer_device = NULL;
1735     }
1736 }
1737 
1738 static void
mouse_motion(UNUSED GtkWidget * invisible,GdkEventMotion * event,gpointer data)1739 mouse_motion (UNUSED GtkWidget      *invisible,
1740               GdkEventMotion        *event,
1741               gpointer               data)
1742 {
1743   grab_color_at_pointer (gdk_event_get_screen ((GdkEvent *) event),
1744                          gdk_event_get_device ((GdkEvent *) event),
1745                          event->x_root, event->y_root, data);
1746 }
1747 
1748 static gboolean
mouse_release(GtkWidget * invisible,GdkEventButton * event,gpointer data)1749 mouse_release (GtkWidget      *invisible,
1750                GdkEventButton *event,
1751                gpointer        data)
1752 {
1753   /* Gcolor3ColorSelection *colorsel = data; */
1754 
1755   if (event->button != GDK_BUTTON_PRIMARY)
1756     return FALSE;
1757 
1758   grab_color_at_pointer (gdk_event_get_screen ((GdkEvent *) event),
1759                          gdk_event_get_device ((GdkEvent *) event),
1760                          event->x_root, event->y_root, data);
1761 
1762   shutdown_eyedropper (GTK_WIDGET (data));
1763 
1764   g_signal_handlers_disconnect_by_func (invisible,
1765                                         mouse_motion,
1766                                         data);
1767   g_signal_handlers_disconnect_by_func (invisible,
1768                                         mouse_release,
1769                                         data);
1770 
1771   return TRUE;
1772 }
1773 
1774 /* Helper Functions */
1775 
1776 static gboolean
key_press(GtkWidget * invisible,GdkEventKey * event,gpointer data)1777 key_press (GtkWidget   *invisible,
1778            GdkEventKey *event,
1779            gpointer     data)
1780 {
1781   GdkScreen *screen = gdk_event_get_screen ((GdkEvent *) event);
1782   GdkDevice *device, *pointer_device;
1783   guint state = event->state & gtk_accelerator_get_default_mod_mask ();
1784   gint x, y;
1785   gint dx, dy;
1786 
1787   device = gdk_event_get_device ((GdkEvent * ) event);
1788   pointer_device = gdk_device_get_associated_device (device);
1789   gdk_device_get_position (pointer_device, NULL, &x, &y);
1790 
1791   dx = 0;
1792   dy = 0;
1793 
1794   switch (event->keyval)
1795     {
1796     case GDK_KEY_space:
1797     case GDK_KEY_Return:
1798     case GDK_KEY_ISO_Enter:
1799     case GDK_KEY_KP_Enter:
1800     case GDK_KEY_KP_Space:
1801       grab_color_at_pointer (screen, pointer_device, x, y, data);
1802       /* fall through */
1803 
1804     case GDK_KEY_Escape:
1805       shutdown_eyedropper (data);
1806 
1807       g_signal_handlers_disconnect_by_func (invisible,
1808                                             mouse_press,
1809                                             data);
1810       g_signal_handlers_disconnect_by_func (invisible,
1811                                             key_press,
1812                                             data);
1813 
1814       return TRUE;
1815 
1816     case GDK_KEY_Up:
1817     case GDK_KEY_KP_Up:
1818       dy = state == GDK_MOD1_MASK ? -BIG_STEP : -1;
1819       break;
1820 
1821     case GDK_KEY_Down:
1822     case GDK_KEY_KP_Down:
1823       dy = state == GDK_MOD1_MASK ? BIG_STEP : 1;
1824       break;
1825 
1826     case GDK_KEY_Left:
1827     case GDK_KEY_KP_Left:
1828       dx = state == GDK_MOD1_MASK ? -BIG_STEP : -1;
1829       break;
1830 
1831     case GDK_KEY_Right:
1832     case GDK_KEY_KP_Right:
1833       dx = state == GDK_MOD1_MASK ? BIG_STEP : 1;
1834       break;
1835 
1836     default:
1837       return FALSE;
1838     }
1839 
1840   gdk_device_warp (pointer_device, screen, x + dx, y + dy);
1841 
1842   return TRUE;
1843 
1844 }
1845 
1846 static gboolean
mouse_press(GtkWidget * invisible,GdkEventButton * event,gpointer data)1847 mouse_press (GtkWidget      *invisible,
1848              GdkEventButton *event,
1849              gpointer        data)
1850 {
1851   if (event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY)
1852     {
1853       g_signal_connect (invisible, "motion-notify-event",
1854                         G_CALLBACK (mouse_motion), data);
1855       g_signal_connect (invisible, "button-release-event",
1856                         G_CALLBACK (mouse_release), data);
1857       g_signal_handlers_disconnect_by_func (invisible,
1858                                             mouse_press,
1859                                             data);
1860       g_signal_handlers_disconnect_by_func (invisible,
1861                                             key_press,
1862                                             data);
1863       return TRUE;
1864     }
1865 
1866   return FALSE;
1867 }
1868 
1869 /* when the button is clicked */
1870 static void
get_screen_color(GtkWidget * button)1871 get_screen_color (GtkWidget *button)
1872 {
1873   Gcolor3ColorSelection *colorsel = g_object_get_data (G_OBJECT (button), "COLORSEL");
1874   Gcolor3ColorSelectionPrivate *priv = colorsel->private_data;
1875   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (button));
1876   GdkDevice *device, *keyb_device, *pointer_device;
1877   GdkCursor *picker_cursor;
1878   GdkGrabStatus grab_status;
1879   GdkWindow *window;
1880   GtkWidget *grab_widget, *toplevel;
1881 
1882   guint32 time = gtk_get_current_event_time ();
1883 
1884   device = gtk_get_current_event_device ();
1885 
1886   if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
1887     {
1888       keyb_device = device;
1889       pointer_device = gdk_device_get_associated_device (device);
1890     }
1891   else
1892     {
1893       pointer_device = device;
1894       keyb_device = gdk_device_get_associated_device (device);
1895     }
1896 
1897   if (priv->dropper_grab_widget == NULL)
1898     {
1899       grab_widget = gtk_window_new (GTK_WINDOW_POPUP);
1900       gtk_window_set_screen (GTK_WINDOW (grab_widget), screen);
1901       gtk_window_resize (GTK_WINDOW (grab_widget), 1, 1);
1902       gtk_window_move (GTK_WINDOW (grab_widget), -100, -100);
1903       gtk_widget_show (grab_widget);
1904 
1905       gtk_widget_add_events (grab_widget,
1906                              GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
1907 
1908       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (colorsel));
1909 
1910       if (GTK_IS_WINDOW (toplevel))
1911         {
1912           if (gtk_window_has_group (GTK_WINDOW (toplevel)))
1913             gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1914                                          GTK_WINDOW (grab_widget));
1915         }
1916 
1917       priv->dropper_grab_widget = grab_widget;
1918     }
1919 
1920   window = gtk_widget_get_window (priv->dropper_grab_widget);
1921 
1922   if (gdk_device_grab (keyb_device,
1923                        window,
1924                        GDK_OWNERSHIP_APPLICATION, FALSE,
1925                        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1926                        NULL, time) != GDK_GRAB_SUCCESS)
1927     return;
1928 
1929   picker_cursor = make_picker_cursor (screen);
1930   grab_status = gdk_device_grab (pointer_device,
1931                                  window,
1932                                  GDK_OWNERSHIP_APPLICATION,
1933                                  FALSE,
1934                                  GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
1935                                  picker_cursor,
1936                                  time);
1937   g_object_unref (picker_cursor);
1938 
1939   if (grab_status != GDK_GRAB_SUCCESS)
1940     {
1941       gdk_device_ungrab (keyb_device, time);
1942       return;
1943     }
1944 
1945   gtk_device_grab_add (priv->dropper_grab_widget,
1946                        pointer_device,
1947                        TRUE);
1948 
1949   priv->grab_time = time;
1950   priv->has_grab = TRUE;
1951   priv->keyboard_device = keyb_device;
1952   priv->pointer_device = pointer_device;
1953 
1954   g_signal_connect (priv->dropper_grab_widget, "button-press-event",
1955                     G_CALLBACK (mouse_press), colorsel);
1956   g_signal_connect (priv->dropper_grab_widget, "key-press-event",
1957                     G_CALLBACK (key_press), colorsel);
1958 }
1959 
1960 static void
hex_changed(UNUSED GtkWidget * hex_entry,gpointer data)1961 hex_changed (UNUSED GtkWidget *hex_entry,
1962              gpointer          data)
1963 {
1964   Gcolor3ColorSelection *colorsel;
1965   Gcolor3ColorSelectionPrivate *priv;
1966   GdkRGBA color;
1967   gchar *text;
1968 
1969   colorsel = GCOLOR3_COLOR_SELECTION (data);
1970   priv = colorsel->private_data;
1971 
1972   if (priv->changing)
1973     return;
1974 
1975   text = gtk_editable_get_chars (GTK_EDITABLE (priv->hex_entry), 0, -1);
1976   if (gdk_rgba_parse (&color, text))
1977     {
1978       priv->color[COLORSEL_RED]   = color.red;
1979       priv->color[COLORSEL_GREEN] = color.green;
1980       priv->color[COLORSEL_BLUE]  = color.blue;
1981       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1982                       priv->color[COLORSEL_GREEN],
1983                       priv->color[COLORSEL_BLUE],
1984                       &priv->color[COLORSEL_HUE],
1985                       &priv->color[COLORSEL_SATURATION],
1986                       &priv->color[COLORSEL_VALUE]);
1987       update_color (colorsel);
1988     }
1989   g_free (text);
1990 }
1991 
1992 static gboolean
hex_focus_out(GtkWidget * hex_entry,UNUSED GdkEventFocus * event,gpointer data)1993 hex_focus_out (GtkWidget            *hex_entry,
1994                UNUSED GdkEventFocus *event,
1995                gpointer              data)
1996 {
1997   hex_changed (hex_entry, data);
1998 
1999   return FALSE;
2000 }
2001 
2002 static void
hsv_changed(GtkWidget * hsv,gpointer data)2003 hsv_changed (GtkWidget *hsv,
2004              gpointer   data)
2005 {
2006   Gcolor3ColorSelection *colorsel;
2007   Gcolor3ColorSelectionPrivate *priv;
2008 
2009   colorsel = GCOLOR3_COLOR_SELECTION (data);
2010   priv = colorsel->private_data;
2011 
2012   if (priv->changing)
2013     return;
2014 
2015   gcolor3_hsv_get_color (GCOLOR3_HSV (hsv),
2016                          &priv->color[COLORSEL_HUE],
2017                          &priv->color[COLORSEL_SATURATION],
2018                          &priv->color[COLORSEL_VALUE]);
2019   gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
2020                   priv->color[COLORSEL_SATURATION],
2021                   priv->color[COLORSEL_VALUE],
2022                   &priv->color[COLORSEL_RED],
2023                   &priv->color[COLORSEL_GREEN],
2024                   &priv->color[COLORSEL_BLUE]);
2025   update_color (colorsel);
2026 }
2027 
2028 static void
adjustment_changed(GtkAdjustment * adjustment,gpointer data)2029 adjustment_changed (GtkAdjustment *adjustment,
2030                     gpointer       data)
2031 {
2032   Gcolor3ColorSelection *colorsel;
2033   Gcolor3ColorSelectionPrivate *priv;
2034 
2035   colorsel = GCOLOR3_COLOR_SELECTION (g_object_get_data (G_OBJECT (adjustment), "COLORSEL"));
2036   priv = colorsel->private_data;
2037 
2038   if (priv->changing)
2039     return;
2040 
2041   switch (GPOINTER_TO_INT (data))
2042     {
2043     case COLORSEL_SATURATION:
2044     case COLORSEL_VALUE:
2045       priv->color[GPOINTER_TO_INT (data)] = gtk_adjustment_get_value (adjustment) / 100;
2046       gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
2047                       priv->color[COLORSEL_SATURATION],
2048                       priv->color[COLORSEL_VALUE],
2049                       &priv->color[COLORSEL_RED],
2050                       &priv->color[COLORSEL_GREEN],
2051                       &priv->color[COLORSEL_BLUE]);
2052       break;
2053     case COLORSEL_HUE:
2054       priv->color[GPOINTER_TO_INT (data)] = gtk_adjustment_get_value (adjustment) / 360;
2055       gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
2056                       priv->color[COLORSEL_SATURATION],
2057                       priv->color[COLORSEL_VALUE],
2058                       &priv->color[COLORSEL_RED],
2059                       &priv->color[COLORSEL_GREEN],
2060                       &priv->color[COLORSEL_BLUE]);
2061       break;
2062     case COLORSEL_RED:
2063     case COLORSEL_GREEN:
2064     case COLORSEL_BLUE:
2065       priv->color[GPOINTER_TO_INT (data)] = gtk_adjustment_get_value (adjustment) / 255;
2066 
2067       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2068                       priv->color[COLORSEL_GREEN],
2069                       priv->color[COLORSEL_BLUE],
2070                       &priv->color[COLORSEL_HUE],
2071                       &priv->color[COLORSEL_SATURATION],
2072                       &priv->color[COLORSEL_VALUE]);
2073       break;
2074     default:
2075       priv->color[GPOINTER_TO_INT (data)] = gtk_adjustment_get_value (adjustment) / 255;
2076       break;
2077     }
2078   update_color (colorsel);
2079 }
2080 
2081 static void
opacity_entry_changed(UNUSED GtkWidget * opacity_entry,gpointer data)2082 opacity_entry_changed (UNUSED GtkWidget *opacity_entry,
2083                        gpointer          data)
2084 {
2085   Gcolor3ColorSelection *colorsel;
2086   Gcolor3ColorSelectionPrivate *priv;
2087   GtkAdjustment *adj;
2088   gchar *text;
2089 
2090   colorsel = GCOLOR3_COLOR_SELECTION (data);
2091   priv = colorsel->private_data;
2092 
2093   if (priv->changing)
2094     return;
2095 
2096   text = gtk_editable_get_chars (GTK_EDITABLE (priv->opacity_entry), 0, -1);
2097   adj = gtk_range_get_adjustment (GTK_RANGE (priv->opacity_slider));
2098   gtk_adjustment_set_value (adj, g_strtod (text, NULL));
2099 
2100   update_color (colorsel);
2101 
2102   g_free (text);
2103 }
2104 
2105 static void
make_label_spinbutton(Gcolor3ColorSelection * colorsel,GtkWidget ** spinbutton,gchar * text,GtkWidget * table,gint i,gint j,gint channel_type,const gchar * tooltip)2106 make_label_spinbutton (Gcolor3ColorSelection *colorsel,
2107                        GtkWidget            **spinbutton,
2108                        gchar                 *text,
2109                        GtkWidget             *table,
2110                        gint                   i,
2111                        gint                   j,
2112                        gint                   channel_type,
2113                        const gchar           *tooltip)
2114 {
2115   GtkWidget *label;
2116   GtkAdjustment *adjust;
2117 
2118   if (channel_type == COLORSEL_HUE)
2119     {
2120       adjust = gtk_adjustment_new (0.0, 0.0, 360.0, 1.0, 1.0, 0.0);
2121     }
2122   else if (channel_type == COLORSEL_SATURATION ||
2123            channel_type == COLORSEL_VALUE)
2124     {
2125       adjust = gtk_adjustment_new (0.0, 0.0, 100.0, 1.0, 1.0, 0.0);
2126     }
2127   else
2128     {
2129       adjust = gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 1.0, 0.0);
2130     }
2131   g_object_set_data (G_OBJECT (adjust), I_("COLORSEL"), colorsel);
2132   *spinbutton = gtk_spin_button_new (adjust, 10.0, 0);
2133 
2134   gtk_widget_set_tooltip_text (*spinbutton, tooltip);
2135 
2136   g_signal_connect (adjust, "value-changed",
2137                     G_CALLBACK (adjustment_changed),
2138                     GINT_TO_POINTER (channel_type));
2139   label = gtk_label_new_with_mnemonic (text);
2140   gtk_label_set_mnemonic_widget (GTK_LABEL (label), *spinbutton);
2141 
2142   gtk_widget_set_halign (label, GTK_ALIGN_START);
2143   gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
2144   gtk_grid_attach (GTK_GRID (table), label, i, j, 1, 1);
2145   gtk_grid_attach (GTK_GRID (table), *spinbutton, i+1, j, 1, 1);
2146 }
2147 
2148 static void
make_palette_frame(Gcolor3ColorSelection * colorsel,GtkWidget * table,gint i,gint j)2149 make_palette_frame (Gcolor3ColorSelection *colorsel,
2150                     GtkWidget             *table,
2151                     gint                   i,
2152                     gint                   j)
2153 {
2154   GtkWidget *frame;
2155   Gcolor3ColorSelectionPrivate *priv;
2156 
2157   priv = colorsel->private_data;
2158   frame = gtk_frame_new (NULL);
2159   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
2160   priv->custom_palette[i][j] = palette_new (colorsel);
2161   gtk_widget_set_size_request (priv->custom_palette[i][j], CUSTOM_PALETTE_ENTRY_WIDTH, CUSTOM_PALETTE_ENTRY_HEIGHT);
2162   gtk_container_add (GTK_CONTAINER (frame), priv->custom_palette[i][j]);
2163   gtk_grid_attach (GTK_GRID (table), frame, i, j, 1, 1);
2164 }
2165 
2166 /* Set the palette entry [x][y] to be the currently selected one. */
2167 static void
set_selected_palette(Gcolor3ColorSelection * colorsel,int x,int y)2168 set_selected_palette (Gcolor3ColorSelection *colorsel, int x, int y)
2169 {
2170   Gcolor3ColorSelectionPrivate *priv = colorsel->private_data;
2171 
2172   gtk_widget_grab_focus (priv->custom_palette[x][y]);
2173 }
2174 
2175 static void
update_color(Gcolor3ColorSelection * colorsel)2176 update_color (Gcolor3ColorSelection *colorsel)
2177 {
2178   Gcolor3ColorSelectionPrivate *priv = colorsel->private_data;
2179   gchar entryval[12];
2180   gchar opacity_text[32];
2181   gchar *ptr;
2182 
2183   priv->changing = TRUE;
2184   color_sample_update_samples (colorsel);
2185 
2186   gcolor3_hsv_set_color (GCOLOR3_HSV (priv->triangle_colorsel),
2187                          priv->color[COLORSEL_HUE],
2188                          priv->color[COLORSEL_SATURATION],
2189                          priv->color[COLORSEL_VALUE]);
2190   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2191                             (GTK_SPIN_BUTTON (priv->hue_spinbutton)),
2192                             scale_round (priv->color[COLORSEL_HUE], 360));
2193   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2194                             (GTK_SPIN_BUTTON (priv->sat_spinbutton)),
2195                             scale_round (priv->color[COLORSEL_SATURATION], 100));
2196   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2197                             (GTK_SPIN_BUTTON (priv->val_spinbutton)),
2198                             scale_round (priv->color[COLORSEL_VALUE], 100));
2199   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2200                             (GTK_SPIN_BUTTON (priv->red_spinbutton)),
2201                             scale_round (priv->color[COLORSEL_RED], 255));
2202   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2203                             (GTK_SPIN_BUTTON (priv->green_spinbutton)),
2204                             scale_round (priv->color[COLORSEL_GREEN], 255));
2205   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2206                             (GTK_SPIN_BUTTON (priv->blue_spinbutton)),
2207                             scale_round (priv->color[COLORSEL_BLUE], 255));
2208   gtk_adjustment_set_value (gtk_range_get_adjustment
2209                             (GTK_RANGE (priv->opacity_slider)),
2210                             scale_round (priv->color[COLORSEL_OPACITY], 255));
2211 
2212   g_snprintf (opacity_text, 32, "%.0f", scale_round (priv->color[COLORSEL_OPACITY], 255));
2213   gtk_entry_set_text (GTK_ENTRY (priv->opacity_entry), opacity_text);
2214 
2215   g_snprintf (entryval, 11, "#%2X%2X%2X",
2216               (guint) (scale_round (priv->color[COLORSEL_RED], 255)),
2217               (guint) (scale_round (priv->color[COLORSEL_GREEN], 255)),
2218               (guint) (scale_round (priv->color[COLORSEL_BLUE], 255)));
2219 
2220   for (ptr = entryval; *ptr; ptr++)
2221     if (*ptr == ' ')
2222       *ptr = '0';
2223   gtk_entry_set_text (GTK_ENTRY (priv->hex_entry), entryval);
2224   priv->changing = FALSE;
2225 
2226   g_object_ref (colorsel);
2227 
2228   g_signal_emit (colorsel, color_selection_signals[COLOR_CHANGED], 0);
2229 
2230   g_object_freeze_notify (G_OBJECT (colorsel));
2231   g_object_notify (G_OBJECT (colorsel), "current-rgba");
2232   g_object_notify (G_OBJECT (colorsel), "current-alpha");
2233   g_object_thaw_notify (G_OBJECT (colorsel));
2234 
2235   g_object_unref (colorsel);
2236 }
2237 
2238 static void
update_palette(Gcolor3ColorSelection * colorsel)2239 update_palette (Gcolor3ColorSelection *colorsel)
2240 {
2241   GdkRGBA *current_colors;
2242   gint i, j;
2243 
2244   current_colors = get_current_colors (colorsel);
2245 
2246   for (i = 0; i < CUSTOM_PALETTE_HEIGHT; i++)
2247     {
2248       for (j = 0; j < CUSTOM_PALETTE_WIDTH; j++)
2249         {
2250           gint index;
2251 
2252           index = i * CUSTOM_PALETTE_WIDTH + j;
2253 
2254           gcolor3_color_selection_set_palette_color (colorsel,
2255                                                  index,
2256                                                  &current_colors[index]);
2257         }
2258     }
2259 
2260   g_free (current_colors);
2261 }
2262 
2263 static void
palette_change_notify_instance(UNUSED GObject * object,UNUSED GParamSpec * pspec,gpointer data)2264 palette_change_notify_instance (UNUSED GObject    *object,
2265                                 UNUSED GParamSpec *pspec,
2266                                 gpointer           data)
2267 {
2268   update_palette (GCOLOR3_COLOR_SELECTION (data));
2269 }
2270 
2271 static void
default_noscreen_change_palette_func(const GdkRGBA * colors,gint n_colors)2272 default_noscreen_change_palette_func (const GdkRGBA *colors,
2273                                       gint           n_colors)
2274 {
2275   default_change_palette_func (gdk_screen_get_default (), colors, n_colors);
2276 }
2277 
2278 static void
default_change_palette_func(GdkScreen * screen,const GdkRGBA * colors,gint n_colors)2279 default_change_palette_func (GdkScreen     *screen,
2280                              const GdkRGBA *colors,
2281                              gint           n_colors)
2282 {
2283   gchar *str;
2284 
2285   str = gcolor3_color_selection_palette_to_string (colors, n_colors);
2286 
2287   gtk_settings_set_string_property (gtk_settings_get_for_screen (screen),
2288                                     "gtk-color-palette",
2289                                     str,
2290                                     "gcolor3_color_selection_palette_to_string");
2291 
2292   g_free (str);
2293 }
2294 
2295 /**
2296  * gcolor3_color_selection_new:
2297  *
2298  * Creates a new Gcolor3ColorSelection.
2299  *
2300  * Returns: a new #Gcolor3ColorSelection
2301  */
2302 GtkWidget *
gcolor3_color_selection_new(void)2303 gcolor3_color_selection_new (void)
2304 {
2305   Gcolor3ColorSelection *colorsel;
2306   Gcolor3ColorSelectionPrivate *priv;
2307   gdouble color[4];
2308   color[0] = 1.0;
2309   color[1] = 1.0;
2310   color[2] = 1.0;
2311   color[3] = 1.0;
2312 
2313   colorsel = g_object_new (GCOLOR3_TYPE_COLOR_SELECTION, NULL);
2314   priv = colorsel->private_data;
2315   set_color_internal (colorsel, color);
2316   gcolor3_color_selection_set_has_opacity_control (colorsel, TRUE);
2317 
2318   /* We want to make sure that default_set is FALSE.
2319    * This way the user can still set it.
2320    */
2321   priv->default_set = FALSE;
2322   priv->default_alpha_set = FALSE;
2323 
2324   return GTK_WIDGET (colorsel);
2325 }
2326 
2327 /**
2328  * gcolor3_color_selection_get_has_opacity_control:
2329  * @colorsel: a #Gcolor3ColorSelection
2330  *
2331  * Determines whether the colorsel has an opacity control.
2332  *
2333  * Returns: %TRUE if the @colorsel has an opacity control,
2334  *     %FALSE if it does't
2335  */
2336 gboolean
gcolor3_color_selection_get_has_opacity_control(Gcolor3ColorSelection * colorsel)2337 gcolor3_color_selection_get_has_opacity_control (Gcolor3ColorSelection *colorsel)
2338 {
2339   Gcolor3ColorSelectionPrivate *priv;
2340 
2341   g_return_val_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel), FALSE);
2342 
2343   priv = colorsel->private_data;
2344 
2345   return priv->has_opacity;
2346 }
2347 
2348 /**
2349  * gcolor3_color_selection_set_has_opacity_control:
2350  * @colorsel: a #Gcolor3ColorSelection
2351  * @has_opacity: %TRUE if @colorsel can set the opacity, %FALSE otherwise
2352  *
2353  * Sets the @colorsel to use or not use opacity.
2354  */
2355 void
gcolor3_color_selection_set_has_opacity_control(Gcolor3ColorSelection * colorsel,gboolean has_opacity)2356 gcolor3_color_selection_set_has_opacity_control (Gcolor3ColorSelection *colorsel,
2357                                                  gboolean           has_opacity)
2358 {
2359   Gcolor3ColorSelectionPrivate *priv;
2360 
2361   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2362 
2363   priv = colorsel->private_data;
2364   has_opacity = has_opacity != FALSE;
2365 
2366   if (priv->has_opacity != has_opacity)
2367     {
2368       priv->has_opacity = has_opacity;
2369       if (has_opacity)
2370         {
2371           gtk_widget_show (priv->opacity_slider);
2372           gtk_widget_show (priv->opacity_label);
2373           gtk_widget_show (priv->opacity_entry);
2374         }
2375       else
2376         {
2377           gtk_widget_hide (priv->opacity_slider);
2378           gtk_widget_hide (priv->opacity_label);
2379           gtk_widget_hide (priv->opacity_entry);
2380         }
2381       color_sample_update_samples (colorsel);
2382 
2383       g_object_notify (G_OBJECT (colorsel), "has-opacity-control");
2384     }
2385 }
2386 
2387 /**
2388  * gcolor3_color_selection_get_has_palette:
2389  * @colorsel: a #Gcolor3ColorSelection
2390  *
2391  * Determines whether the color selector has a color palette.
2392  *
2393  * Returns: %TRUE if the selector has a palette, %FALSE if it hasn't
2394  */
2395 gboolean
gcolor3_color_selection_get_has_palette(Gcolor3ColorSelection * colorsel)2396 gcolor3_color_selection_get_has_palette (Gcolor3ColorSelection *colorsel)
2397 {
2398   Gcolor3ColorSelectionPrivate *priv;
2399 
2400   g_return_val_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel), FALSE);
2401 
2402   priv = colorsel->private_data;
2403 
2404   return priv->has_palette;
2405 }
2406 
2407 /**
2408  * gcolor3_color_selection_set_has_palette:
2409  * @colorsel: a #Gcolor3ColorSelection
2410  * @has_palette: %TRUE if palette is to be visible, %FALSE otherwise
2411  *
2412  * Shows and hides the palette based upon the value of @has_palette.
2413  */
2414 void
gcolor3_color_selection_set_has_palette(Gcolor3ColorSelection * colorsel,gboolean has_palette)2415 gcolor3_color_selection_set_has_palette (Gcolor3ColorSelection *colorsel,
2416                                          gboolean               has_palette)
2417 {
2418   Gcolor3ColorSelectionPrivate *priv;
2419   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2420 
2421   priv = colorsel->private_data;
2422   has_palette = has_palette != FALSE;
2423 
2424   if (priv->has_palette != has_palette)
2425     {
2426       priv->has_palette = has_palette;
2427       if (has_palette)
2428         gtk_widget_show (priv->palette_frame);
2429       else
2430         gtk_widget_hide (priv->palette_frame);
2431 
2432       update_tooltips (colorsel);
2433 
2434       g_object_notify (G_OBJECT (colorsel), "has-palette");
2435     }
2436 }
2437 
2438 /**
2439  * gcolor3_color_selection_set_current_color:
2440  * @colorsel: a #Gcolor3ColorSelection
2441  * @color: a #GdkRGBA to set the current color with
2442  *
2443  * Sets the current color to be @color.
2444  *
2445  * The first time this is called, it will also set
2446  * the original color to be @color too.
2447  */
2448 void
gcolor3_color_selection_set_current_color(Gcolor3ColorSelection * colorsel,const GdkRGBA * color)2449 gcolor3_color_selection_set_current_color (Gcolor3ColorSelection *colorsel,
2450                                            const GdkRGBA         *color)
2451 {
2452   Gcolor3ColorSelectionPrivate *priv;
2453   gint i;
2454 
2455   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2456   g_return_if_fail (color != NULL);
2457 
2458   priv = colorsel->private_data;
2459   priv->changing = TRUE;
2460   priv->color[COLORSEL_RED] = color->red;
2461   priv->color[COLORSEL_GREEN] = color->green;
2462   priv->color[COLORSEL_BLUE] = color->blue;
2463   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2464                   priv->color[COLORSEL_GREEN],
2465                   priv->color[COLORSEL_BLUE],
2466                   &priv->color[COLORSEL_HUE],
2467                   &priv->color[COLORSEL_SATURATION],
2468                   &priv->color[COLORSEL_VALUE]);
2469   if (priv->default_set == FALSE)
2470     {
2471       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2472         priv->old_color[i] = priv->color[i];
2473     }
2474   priv->default_set = TRUE;
2475   update_color (colorsel);
2476 }
2477 
2478 /**
2479  * gcolor3_color_selection_set_current_alpha:
2480  * @colorsel: a #Gcolor3ColorSelection
2481  * @alpha: an integer between 0 and 65535
2482  *
2483  * Sets the current opacity to be @alpha.
2484  *
2485  * The first time this is called, it will also set
2486  * the original opacity to be @alpha too.
2487  */
2488 // FIXME: this can possibly be removed, together with all special alpha treatment.
2489 void
gcolor3_color_selection_set_current_alpha(Gcolor3ColorSelection * colorsel,guint16 alpha)2490 gcolor3_color_selection_set_current_alpha (Gcolor3ColorSelection *colorsel,
2491                                            guint16                alpha)
2492 {
2493   Gcolor3ColorSelectionPrivate *priv;
2494   gint i;
2495 
2496   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2497 
2498   priv = colorsel->private_data;
2499   priv->changing = TRUE;
2500   priv->color[COLORSEL_OPACITY] = SCALE (alpha);
2501   if (priv->default_alpha_set == FALSE)
2502     {
2503       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2504         priv->old_color[i] = priv->color[i];
2505     }
2506   priv->default_alpha_set = TRUE;
2507   update_color (colorsel);
2508 }
2509 
2510 /**
2511  * gcolor3_color_selection_get_current_color:
2512  * @colorsel: a #Gcolor3ColorSelection
2513  * @color: (out): a #GdkRGBA to fill in with the current color
2514  *
2515  * Sets @color to be the current color in the Gcolor3ColorSelection widget.
2516  */
2517 void
gcolor3_color_selection_get_current_color(Gcolor3ColorSelection * colorsel,GdkRGBA * color)2518 gcolor3_color_selection_get_current_color (Gcolor3ColorSelection *colorsel,
2519                                            GdkRGBA               *color)
2520 {
2521   Gcolor3ColorSelectionPrivate *priv;
2522 
2523   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2524   g_return_if_fail (color != NULL);
2525 
2526   priv = colorsel->private_data;
2527   color->red = priv->color[COLORSEL_RED];
2528   color->green = priv->color[COLORSEL_GREEN];
2529   color->blue = priv->color[COLORSEL_BLUE];
2530 }
2531 
2532 /**
2533  * gcolor3_color_selection_get_current_alpha:
2534  * @colorsel: a #Gcolor3ColorSelection
2535  *
2536  * Returns the current alpha value.
2537  *
2538  * Returns: an integer between 0 and 65535
2539  */
2540 guint16
gcolor3_color_selection_get_current_alpha(Gcolor3ColorSelection * colorsel)2541 gcolor3_color_selection_get_current_alpha (Gcolor3ColorSelection *colorsel)
2542 {
2543   Gcolor3ColorSelectionPrivate *priv;
2544 
2545   g_return_val_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel), 0);
2546 
2547   priv = colorsel->private_data;
2548   return priv->has_opacity ? UNSCALE (priv->color[COLORSEL_OPACITY]) : 65535;
2549 }
2550 
2551 /**
2552  * gcolor3_color_selection_set_previous_color:
2553  * @colorsel: a #Gcolor3ColorSelection
2554  * @color: a #GdkRGBA to set the previous color with
2555  *
2556  * Sets the “previous” color to be @color.
2557  *
2558  * This function should be called with some hesitations,
2559  * as it might seem confusing to have that color change.
2560  * Calling gcolor3_color_selection_set_current_color() will also
2561  * set this color the first time it is called.
2562  */
2563 void
gcolor3_color_selection_set_previous_color(Gcolor3ColorSelection * colorsel,const GdkRGBA * color)2564 gcolor3_color_selection_set_previous_color (Gcolor3ColorSelection *colorsel,
2565                                             const GdkRGBA         *color)
2566 {
2567   Gcolor3ColorSelectionPrivate *priv;
2568 
2569   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2570   g_return_if_fail (color != NULL);
2571 
2572   priv = colorsel->private_data;
2573   priv->changing = TRUE;
2574   priv->old_color[COLORSEL_RED] = color->red;
2575   priv->old_color[COLORSEL_GREEN] = color->green;
2576   priv->old_color[COLORSEL_BLUE] = color->blue;
2577   gtk_rgb_to_hsv (priv->old_color[COLORSEL_RED],
2578                   priv->old_color[COLORSEL_GREEN],
2579                   priv->old_color[COLORSEL_BLUE],
2580                   &priv->old_color[COLORSEL_HUE],
2581                   &priv->old_color[COLORSEL_SATURATION],
2582                   &priv->old_color[COLORSEL_VALUE]);
2583   color_sample_update_samples (colorsel);
2584   priv->default_set = TRUE;
2585   priv->changing = FALSE;
2586 }
2587 
2588 /**
2589  * gcolor3_color_selection_set_previous_alpha:
2590  * @colorsel: a #Gcolor3ColorSelection
2591  * @alpha: an integer between 0 and 65535
2592  *
2593  * Sets the “previous” alpha to be @alpha.
2594  *
2595  * This function should be called with some hesitations,
2596  * as it might seem confusing to have that alpha change.
2597  */
2598 void
gcolor3_color_selection_set_previous_alpha(Gcolor3ColorSelection * colorsel,guint16 alpha)2599 gcolor3_color_selection_set_previous_alpha (Gcolor3ColorSelection *colorsel,
2600                                             guint16                alpha)
2601 {
2602   Gcolor3ColorSelectionPrivate *priv;
2603 
2604   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2605 
2606   priv = colorsel->private_data;
2607   priv->changing = TRUE;
2608   priv->old_color[COLORSEL_OPACITY] = SCALE (alpha);
2609   color_sample_update_samples (colorsel);
2610   priv->default_alpha_set = TRUE;
2611   priv->changing = FALSE;
2612 }
2613 
2614 
2615 /**
2616  * gcolor3_color_selection_get_previous_color:
2617  * @colorsel: a #Gcolor3ColorSelection
2618  * @color: (out): a #GdkRGBA to fill in with the original color value
2619  *
2620  * Fills @color in with the original color value.
2621  */
2622 void
gcolor3_color_selection_get_previous_color(Gcolor3ColorSelection * colorsel,GdkRGBA * color)2623 gcolor3_color_selection_get_previous_color (Gcolor3ColorSelection *colorsel,
2624                                             GdkRGBA               *color)
2625 {
2626   Gcolor3ColorSelectionPrivate *priv;
2627 
2628   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2629   g_return_if_fail (color != NULL);
2630 
2631   priv = colorsel->private_data;
2632   color->red = priv->old_color[COLORSEL_RED];
2633   color->green = priv->old_color[COLORSEL_GREEN];
2634   color->blue = priv->old_color[COLORSEL_BLUE];
2635 }
2636 
2637 /**
2638  * gcolor3_color_selection_get_previous_alpha:
2639  * @colorsel: a #Gcolor3ColorSelection
2640  *
2641  * Returns the previous alpha value.
2642  *
2643  * Returns: an integer between 0 and 65535
2644  */
2645 guint16
gcolor3_color_selection_get_previous_alpha(Gcolor3ColorSelection * colorsel)2646 gcolor3_color_selection_get_previous_alpha (Gcolor3ColorSelection *colorsel)
2647 {
2648   Gcolor3ColorSelectionPrivate *priv;
2649 
2650   g_return_val_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel), 0);
2651 
2652   priv = colorsel->private_data;
2653   return priv->has_opacity ? UNSCALE (priv->old_color[COLORSEL_OPACITY]) : 65535;
2654 }
2655 
2656 /**
2657  * gcolor3_color_selection_set_current_rgba:
2658  * @colorsel: a #Gcolor3ColorSelection
2659  * @rgba: A #GdkRGBA to set the current color with
2660  *
2661  * Sets the current color to be @rgba.
2662  *
2663  * The first time this is called, it will also set
2664  * the original color to be @rgba too.
2665  *
2666  * Since: 3.0
2667  */
2668 void
gcolor3_color_selection_set_current_rgba(Gcolor3ColorSelection * colorsel,const GdkRGBA * rgba)2669 gcolor3_color_selection_set_current_rgba (Gcolor3ColorSelection *colorsel,
2670                                           const GdkRGBA         *rgba)
2671 {
2672   Gcolor3ColorSelectionPrivate *priv;
2673   gint i;
2674 
2675   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2676   g_return_if_fail (rgba != NULL);
2677 
2678   priv = colorsel->private_data;
2679   priv->changing = TRUE;
2680 
2681   priv->color[COLORSEL_RED] = CLAMP (rgba->red, 0, 1);
2682   priv->color[COLORSEL_GREEN] = CLAMP (rgba->green, 0, 1);
2683   priv->color[COLORSEL_BLUE] = CLAMP (rgba->blue, 0, 1);
2684   priv->color[COLORSEL_OPACITY] = CLAMP (rgba->alpha, 0, 1);
2685 
2686   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2687                   priv->color[COLORSEL_GREEN],
2688                   priv->color[COLORSEL_BLUE],
2689                   &priv->color[COLORSEL_HUE],
2690                   &priv->color[COLORSEL_SATURATION],
2691                   &priv->color[COLORSEL_VALUE]);
2692 
2693   if (priv->default_set == FALSE)
2694     {
2695       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2696         priv->old_color[i] = priv->color[i];
2697     }
2698 
2699   priv->default_set = TRUE;
2700   update_color (colorsel);
2701 }
2702 
2703 /**
2704  * gcolor3_color_selection_get_current_rgba:
2705  * @colorsel: a #Gcolor3ColorSelection
2706  * @rgba: (out): a #GdkRGBA to fill in with the current color
2707  *
2708  * Sets @rgba to be the current color in the Gcolor3ColorSelection widget.
2709  *
2710  * Since: 3.0
2711  */
2712 void
gcolor3_color_selection_get_current_rgba(Gcolor3ColorSelection * colorsel,GdkRGBA * rgba)2713 gcolor3_color_selection_get_current_rgba (Gcolor3ColorSelection *colorsel,
2714                                           GdkRGBA               *rgba)
2715 {
2716   Gcolor3ColorSelectionPrivate *priv;
2717 
2718   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2719   g_return_if_fail (rgba != NULL);
2720 
2721   priv = colorsel->private_data;
2722   rgba->red = priv->color[COLORSEL_RED];
2723   rgba->green = priv->color[COLORSEL_GREEN];
2724   rgba->blue = priv->color[COLORSEL_BLUE];
2725   rgba->alpha = (priv->has_opacity) ? priv->color[COLORSEL_OPACITY] : 1;
2726 }
2727 
2728 /**
2729  * gcolor3_color_selection_set_previous_rgba:
2730  * @colorsel: a #Gcolor3ColorSelection
2731  * @rgba: a #GdkRGBA to set the previous color with
2732  *
2733  * Sets the “previous” color to be @rgba.
2734  *
2735  * This function should be called with some hesitations,
2736  * as it might seem confusing to have that color change.
2737  * Calling gcolor3_color_selection_set_current_rgba() will also
2738  * set this color the first time it is called.
2739  *
2740  * Since: 3.0
2741  */
2742 void
gcolor3_color_selection_set_previous_rgba(Gcolor3ColorSelection * colorsel,const GdkRGBA * rgba)2743 gcolor3_color_selection_set_previous_rgba (Gcolor3ColorSelection *colorsel,
2744                                            const GdkRGBA         *rgba)
2745 {
2746   Gcolor3ColorSelectionPrivate *priv;
2747 
2748   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2749   g_return_if_fail (rgba != NULL);
2750 
2751   priv = colorsel->private_data;
2752   priv->changing = TRUE;
2753 
2754   priv->old_color[COLORSEL_RED] = CLAMP (rgba->red, 0, 1);
2755   priv->old_color[COLORSEL_GREEN] = CLAMP (rgba->green, 0, 1);
2756   priv->old_color[COLORSEL_BLUE] = CLAMP (rgba->blue, 0, 1);
2757   priv->old_color[COLORSEL_OPACITY] = CLAMP (rgba->alpha, 0, 1);
2758 
2759   gtk_rgb_to_hsv (priv->old_color[COLORSEL_RED],
2760                   priv->old_color[COLORSEL_GREEN],
2761                   priv->old_color[COLORSEL_BLUE],
2762                   &priv->old_color[COLORSEL_HUE],
2763                   &priv->old_color[COLORSEL_SATURATION],
2764                   &priv->old_color[COLORSEL_VALUE]);
2765 
2766   color_sample_update_samples (colorsel);
2767   priv->default_set = TRUE;
2768   priv->changing = FALSE;
2769 }
2770 
2771 /**
2772  * gcolor3_color_selection_get_previous_rgba:
2773  * @colorsel: a #Gcolor3ColorSelection
2774  * @rgba: (out): a #GdkRGBA to fill in with the original color value
2775  *
2776  * Fills @rgba in with the original color value.
2777  *
2778  * Since: 3.0
2779  */
2780 void
gcolor3_color_selection_get_previous_rgba(Gcolor3ColorSelection * colorsel,GdkRGBA * rgba)2781 gcolor3_color_selection_get_previous_rgba (Gcolor3ColorSelection *colorsel,
2782                                            GdkRGBA               *rgba)
2783 {
2784   Gcolor3ColorSelectionPrivate *priv;
2785 
2786   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2787   g_return_if_fail (rgba != NULL);
2788 
2789   priv = colorsel->private_data;
2790   rgba->red = priv->old_color[COLORSEL_RED];
2791   rgba->green = priv->old_color[COLORSEL_GREEN];
2792   rgba->blue = priv->old_color[COLORSEL_BLUE];
2793   rgba->alpha = (priv->has_opacity) ? priv->old_color[COLORSEL_OPACITY] : 1;
2794 }
2795 
2796 /**
2797  * gcolor3_color_selection_set_palette_color:
2798  * @colorsel: a #Gcolor3ColorSelection
2799  * @index: the color index of the palette
2800  * @color: A #GdkRGBA to set the palette with
2801  *
2802  * Sets the palette located at @index to have @color as its color.
2803  */
2804 static void
gcolor3_color_selection_set_palette_color(Gcolor3ColorSelection * colorsel,gint index,GdkRGBA * color)2805 gcolor3_color_selection_set_palette_color (Gcolor3ColorSelection *colorsel,
2806                                            gint                   index,
2807                                            GdkRGBA               *color)
2808 {
2809   Gcolor3ColorSelectionPrivate *priv;
2810   gint x, y;
2811   gdouble col[3];
2812 
2813   g_return_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel));
2814   g_return_if_fail (index >= 0  && index < CUSTOM_PALETTE_WIDTH*CUSTOM_PALETTE_HEIGHT);
2815 
2816   x = index % CUSTOM_PALETTE_WIDTH;
2817   y = index / CUSTOM_PALETTE_WIDTH;
2818 
2819   priv = colorsel->private_data;
2820   col[0] = color->red;
2821   col[1] = color->green;
2822   col[2] = color->blue;
2823 
2824   palette_set_color (priv->custom_palette[x][y], colorsel, col);
2825 }
2826 
2827 /**
2828  * gcolor3_color_selection_is_adjusting:
2829  * @colorsel: a #Gcolor3ColorSelection
2830  *
2831  * Gets the current state of the @colorsel.
2832  *
2833  * Returns: %TRUE if the user is currently dragging
2834  *     a color around, and %FALSE if the selection has stopped
2835  */
2836 gboolean
gcolor3_color_selection_is_adjusting(Gcolor3ColorSelection * colorsel)2837 gcolor3_color_selection_is_adjusting (Gcolor3ColorSelection *colorsel)
2838 {
2839   Gcolor3ColorSelectionPrivate *priv;
2840 
2841   g_return_val_if_fail (GCOLOR3_IS_COLOR_SELECTION (colorsel), FALSE);
2842 
2843   priv = colorsel->private_data;
2844 
2845   return (gcolor3_hsv_is_adjusting (GCOLOR3_HSV (priv->triangle_colorsel)));
2846 }
2847 
2848 
2849 /**
2850  * gcolor3_color_selection_palette_from_string:
2851  * @str: a string encoding a color palette
2852  * @colors: (out) (array length=n_colors): return location for
2853  *     allocated array of #GdkRGBA
2854  * @n_colors: return location for length of array
2855  *
2856  * Parses a color palette string; the string is a colon-separated
2857  * list of color names readable by gdk_rgba_parse().
2858  *
2859  * Returns: %TRUE if a palette was successfully parsed
2860  */
2861 gboolean
gcolor3_color_selection_palette_from_string(const gchar * str,GdkRGBA ** colors,gint * n_colors)2862 gcolor3_color_selection_palette_from_string (const gchar  *str,
2863                                              GdkRGBA    **colors,
2864                                              gint         *n_colors)
2865 {
2866   GdkRGBA *retval;
2867   gint count;
2868   gchar *p;
2869   gchar *start;
2870   gchar *copy;
2871 
2872   count = 0;
2873   retval = NULL;
2874   copy = g_strdup (str);
2875 
2876   start = copy;
2877   p = copy;
2878   while (TRUE)
2879     {
2880       if (*p == ':' || *p == '\0')
2881         {
2882           gboolean done = TRUE;
2883 
2884           if (start == p)
2885             {
2886               goto failed; /* empty entry */
2887             }
2888 
2889           if (*p)
2890             {
2891               *p = '\0';
2892               done = FALSE;
2893             }
2894 
2895           retval = g_renew (GdkRGBA, retval, count + 1);
2896           if (!gdk_rgba_parse (retval + count, start))
2897             {
2898               goto failed;
2899             }
2900 
2901           ++count;
2902 
2903           if (done)
2904             break;
2905           else
2906             start = p + 1;
2907         }
2908 
2909       ++p;
2910     }
2911 
2912   g_free (copy);
2913 
2914   if (colors)
2915     *colors = retval;
2916   else
2917     g_free (retval);
2918 
2919   if (n_colors)
2920     *n_colors = count;
2921 
2922   return TRUE;
2923 
2924  failed:
2925   g_free (copy);
2926   g_free (retval);
2927 
2928   if (colors)
2929     *colors = NULL;
2930   if (n_colors)
2931     *n_colors = 0;
2932 
2933   return FALSE;
2934 }
2935 
2936 /**
2937  * gcolor3_color_selection_palette_to_string:
2938  * @colors: (array length=n_colors): an array of colors
2939  * @n_colors: length of the array
2940  *
2941  * Encodes a palette as a string, useful for persistent storage.
2942  *
2943  * Returns: allocated string encoding the palette
2944  */
2945 gchar*
gcolor3_color_selection_palette_to_string(const GdkRGBA * colors,gint n_colors)2946 gcolor3_color_selection_palette_to_string (const GdkRGBA *colors,
2947                                            gint           n_colors)
2948 {
2949   gint i;
2950   gchar **strs = NULL;
2951   gchar *retval;
2952 
2953   if (n_colors == 0)
2954     return g_strdup ("");
2955 
2956   strs = g_new0 (gchar*, n_colors + 1);
2957 
2958   i = 0;
2959   while (i < n_colors)
2960     {
2961       gchar *ptr;
2962 
2963       strs[i] =
2964         g_strdup_printf ("#%2X%2X%2X",
2965                          (unsigned int) colors[i].red / 256,
2966                          (unsigned int) colors[i].green / 256,
2967                          (unsigned int) colors[i].blue / 256);
2968 
2969       for (ptr = strs[i]; *ptr; ptr++)
2970         if (*ptr == ' ')
2971           *ptr = '0';
2972 
2973       ++i;
2974     }
2975 
2976   retval = g_strjoinv (":", strs);
2977 
2978   g_strfreev (strs);
2979 
2980   return retval;
2981 }
2982 
2983 /**
2984  * gcolor3_color_selection_set_change_palette_with_screen_hook: (skip)
2985  * @func: a function to call when the custom palette needs saving
2986  *
2987  * Installs a global function to be called whenever the user
2988  * tries to modify the palette in a color selection.
2989  *
2990  * This function should save the new palette contents, and update
2991  * the #GtkSettings:gtk-color-palette GtkSettings property so all
2992  * Gcolor3ColorSelection widgets will be modified.
2993  *
2994  * Returns: the previous change palette hook (that was replaced)
2995  *
2996  * Since: 2.2
2997  */
2998 Gcolor3ColorSelectionChangePaletteWithScreenFunc
gcolor3_color_selection_set_change_palette_with_screen_hook(Gcolor3ColorSelectionChangePaletteWithScreenFunc func)2999 gcolor3_color_selection_set_change_palette_with_screen_hook (Gcolor3ColorSelectionChangePaletteWithScreenFunc func)
3000 {
3001   Gcolor3ColorSelectionChangePaletteWithScreenFunc old;
3002 
3003   old = change_palette_hook;
3004 
3005   change_palette_hook = func;
3006 
3007   return old;
3008 }
3009 
3010 static void
make_control_relations(AtkObject * atk_obj,GtkWidget * widget)3011 make_control_relations (AtkObject *atk_obj,
3012                         GtkWidget *widget)
3013 {
3014   AtkObject *obj;
3015 
3016   obj = gtk_widget_get_accessible (widget);
3017   atk_object_add_relationship (atk_obj, ATK_RELATION_CONTROLLED_BY, obj);
3018   atk_object_add_relationship (obj, ATK_RELATION_CONTROLLER_FOR, atk_obj);
3019 }
3020 
3021 static void
make_all_relations(AtkObject * atk_obj,Gcolor3ColorSelectionPrivate * priv)3022 make_all_relations (AtkObject                *atk_obj,
3023                     Gcolor3ColorSelectionPrivate *priv)
3024 {
3025   make_control_relations (atk_obj, priv->hue_spinbutton);
3026   make_control_relations (atk_obj, priv->sat_spinbutton);
3027   make_control_relations (atk_obj, priv->val_spinbutton);
3028   make_control_relations (atk_obj, priv->red_spinbutton);
3029   make_control_relations (atk_obj, priv->green_spinbutton);
3030   make_control_relations (atk_obj, priv->blue_spinbutton);
3031 }
3032