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