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