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