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