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