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