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