]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorsel.c
7298d3f9142c022c0bba8da55fcbce64c29a4f48
[~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,
922                     "drag_begin",
923                     G_CALLBACK (color_sample_drag_begin),
924                     colorsel);
925   if (sample == priv->cur_sample)
926     {
927       
928       gtk_drag_dest_set (sample,
929                          GTK_DEST_DEFAULT_HIGHLIGHT |
930                          GTK_DEST_DEFAULT_MOTION |
931                          GTK_DEST_DEFAULT_DROP,
932                          targets, 1,
933                          GDK_ACTION_COPY);
934       
935       g_signal_connect (sample,
936                         "drag_end",
937                         G_CALLBACK (color_sample_drag_end),
938                         colorsel);
939     }
940   
941   g_signal_connect (sample,
942                     "drag_data_get",
943                     G_CALLBACK (color_sample_drag_handle),
944                     colorsel);
945   g_signal_connect (sample,
946                     "drag_data_received",
947                     G_CALLBACK (color_sample_drop_handle),
948                     colorsel);
949   
950 }
951
952
953 static void
954 color_sample_new (GtkColorSelection *colorsel)
955 {
956   ColorSelectionPrivate *priv;
957   
958   priv = colorsel->private_data;
959   
960   priv->sample_area = gtk_hbox_new (FALSE, 0);
961   priv->old_sample = gtk_drawing_area_new ();
962   priv->cur_sample = gtk_drawing_area_new ();
963
964   gtk_box_pack_start (GTK_BOX (priv->sample_area), priv->old_sample,
965                       TRUE, TRUE, 0);
966   gtk_box_pack_start (GTK_BOX (priv->sample_area), priv->cur_sample,
967                       TRUE, TRUE, 0);
968   
969   g_signal_connect (priv->old_sample, "expose_event",
970                     G_CALLBACK (color_old_sample_expose),
971                     colorsel);
972   g_signal_connect (priv->cur_sample, "expose_event",
973                     G_CALLBACK (color_cur_sample_expose),
974                     colorsel);
975   
976   color_sample_setup_dnd (colorsel, priv->old_sample);
977   color_sample_setup_dnd (colorsel, priv->cur_sample);
978
979   gtk_widget_set_tooltip_text (priv->old_sample,
980                         _("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."));
981
982
983   gtk_widget_set_tooltip_text (priv->cur_sample,
984                         _("The color you've chosen. You can drag this color to a palette entry to save it for use in the future."));
985   
986   gtk_widget_show_all (priv->sample_area);
987 }
988
989
990 /*
991  *
992  * The palette area code
993  *
994  */
995
996 static void
997 palette_get_color (GtkWidget *drawing_area, gdouble *color)
998 {
999   gdouble *color_val;
1000   
1001   g_return_if_fail (color != NULL);
1002   
1003   color_val = g_object_get_data (G_OBJECT (drawing_area), "color_val");
1004   if (color_val == NULL)
1005     {
1006       /* Default to white for no good reason */
1007       color[0] = 1.0;
1008       color[1] = 1.0;
1009       color[2] = 1.0;
1010       color[3] = 1.0;
1011       return;
1012     }
1013   
1014   color[0] = color_val[0];
1015   color[1] = color_val[1];
1016   color[2] = color_val[2];
1017   color[3] = 1.0;
1018 }
1019
1020 static void
1021 palette_paint (GtkWidget    *drawing_area,
1022                GdkRectangle *area,
1023                gpointer      data)
1024 {
1025   cairo_t *cr;
1026   gint focus_width;
1027     
1028   if (drawing_area->window == NULL)
1029     return;
1030
1031   cr = gdk_cairo_create (drawing_area->window);
1032
1033   gdk_cairo_set_source_color (cr, &drawing_area->style->bg[GTK_STATE_NORMAL]);
1034   gdk_cairo_rectangle (cr, area);
1035   cairo_fill (cr);
1036   
1037   if (GTK_WIDGET_HAS_FOCUS (drawing_area))
1038     {
1039       set_focus_line_attributes (drawing_area, cr, &focus_width);
1040
1041       cairo_rectangle (cr,
1042                        focus_width / 2., focus_width / 2.,
1043                        drawing_area->allocation.width - focus_width,
1044                        drawing_area->allocation.height - focus_width);
1045       cairo_stroke (cr);
1046     }
1047
1048   cairo_destroy (cr);
1049 }
1050
1051 static void
1052 set_focus_line_attributes (GtkWidget *drawing_area,
1053                            cairo_t   *cr,
1054                            gint      *focus_width)
1055 {
1056   gdouble color[4];
1057   gint8 *dash_list;
1058   
1059   gtk_widget_style_get (drawing_area,
1060                         "focus-line-width", focus_width,
1061                         "focus-line-pattern", (gchar *)&dash_list,
1062                         NULL);
1063       
1064   palette_get_color (drawing_area, color);
1065
1066   if (INTENSITY (color[0], color[1], color[2]) > 0.5)
1067     cairo_set_source_rgb (cr, 0., 0., 0.);
1068   else
1069     cairo_set_source_rgb (cr, 1., 1., 1.);
1070
1071   cairo_set_line_width (cr, *focus_width);
1072
1073   if (dash_list[0])
1074     {
1075       gint n_dashes = strlen (dash_list);
1076       gdouble *dashes = g_new (gdouble, n_dashes);
1077       gdouble total_length = 0;
1078       gdouble dash_offset;
1079       gint i;
1080
1081       for (i = 0; i < n_dashes; i++)
1082         {
1083           dashes[i] = dash_list[i];
1084           total_length += dash_list[i];
1085         }
1086
1087       /* The dash offset here aligns the pattern to integer pixels
1088        * by starting the dash at the right side of the left border
1089        * Negative dash offsets in cairo don't work
1090        * (https://bugs.freedesktop.org/show_bug.cgi?id=2729)
1091        */
1092       dash_offset = - *focus_width / 2.;
1093       while (dash_offset < 0)
1094         dash_offset += total_length;
1095       
1096       cairo_set_dash (cr, dashes, n_dashes, dash_offset);
1097       g_free (dashes);
1098     }
1099
1100   g_free (dash_list);
1101 }
1102
1103 static void
1104 palette_drag_begin (GtkWidget      *widget,
1105                     GdkDragContext *context,
1106                     gpointer        data)
1107 {
1108   gdouble colors[4];
1109   
1110   palette_get_color (widget, colors);
1111   set_color_icon (context, colors);
1112 }
1113
1114 static void
1115 palette_drag_handle (GtkWidget        *widget,
1116                      GdkDragContext   *context,
1117                      GtkSelectionData *selection_data,
1118                      guint             info,
1119                      guint             time,
1120                      gpointer          data)
1121 {
1122   guint16 vals[4];
1123   gdouble colsrc[4];
1124   
1125   palette_get_color (widget, colsrc);
1126   
1127   vals[0] = colsrc[COLORSEL_RED] * 0xffff;
1128   vals[1] = colsrc[COLORSEL_GREEN] * 0xffff;
1129   vals[2] = colsrc[COLORSEL_BLUE] * 0xffff;
1130   vals[3] = 0xffff;
1131   
1132   gtk_selection_data_set (selection_data,
1133                           gdk_atom_intern_static_string ("application/x-color"),
1134                           16, (guchar *)vals, 8);
1135 }
1136
1137 static void
1138 palette_drag_end (GtkWidget      *widget,
1139                   GdkDragContext *context,
1140                   gpointer        data)
1141 {
1142   g_object_set_data (G_OBJECT (widget), I_("gtk-color-selection-drag-window"), NULL);
1143 }
1144
1145 static GdkColor *
1146 get_current_colors (GtkColorSelection *colorsel)
1147 {
1148   GtkSettings *settings;
1149   GdkColor *colors = NULL;
1150   gint n_colors = 0;
1151   gchar *palette;
1152
1153   settings = gtk_widget_get_settings (GTK_WIDGET (colorsel));
1154   g_object_get (settings,
1155                 "gtk-color-palette", &palette,
1156                 NULL);
1157   
1158   if (!gtk_color_selection_palette_from_string (palette, &colors, &n_colors))
1159     {
1160       gtk_color_selection_palette_from_string (default_colors, &colors, &n_colors);
1161     }
1162   else
1163     {
1164       /* If there are less colors provided than the number of slots in the
1165        * color selection, we fill in the rest from the defaults.
1166        */
1167       if (n_colors < (GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT))
1168         {
1169           GdkColor *tmp_colors = colors;
1170           gint tmp_n_colors = n_colors;
1171           
1172           gtk_color_selection_palette_from_string (default_colors, &colors, &n_colors);
1173           memcpy (colors, tmp_colors, sizeof (GdkColor) * tmp_n_colors);
1174
1175           g_free (tmp_colors);
1176         }
1177     }
1178
1179   g_assert (n_colors >= GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT);
1180   g_free (palette);
1181   
1182   return colors;
1183 }
1184
1185 /* Changes the model color */
1186 static void
1187 palette_change_color (GtkWidget         *drawing_area,
1188                       GtkColorSelection *colorsel,
1189                       gdouble           *color)
1190 {
1191   gint x, y;
1192   ColorSelectionPrivate *priv;
1193   GdkColor gdk_color;
1194   GdkColor *current_colors;
1195   GdkScreen *screen;
1196
1197   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
1198   g_return_if_fail (GTK_IS_DRAWING_AREA (drawing_area));
1199   
1200   priv = colorsel->private_data;
1201   
1202   gdk_color.red = UNSCALE (color[0]);
1203   gdk_color.green = UNSCALE (color[1]);
1204   gdk_color.blue = UNSCALE (color[2]);
1205   gdk_color.pixel = 0;
1206
1207   x = 0;
1208   y = 0;                        /* Quiet GCC */
1209   while (x < GTK_CUSTOM_PALETTE_WIDTH)
1210     {
1211       y = 0;
1212       while (y < GTK_CUSTOM_PALETTE_HEIGHT)
1213         {
1214           if (priv->custom_palette[x][y] == drawing_area)
1215             goto out;
1216           
1217           ++y;
1218         }
1219
1220       ++x;
1221     }
1222
1223  out:
1224   
1225   g_assert (x < GTK_CUSTOM_PALETTE_WIDTH || y < GTK_CUSTOM_PALETTE_HEIGHT);
1226
1227   current_colors = get_current_colors (colorsel);
1228   current_colors[y * GTK_CUSTOM_PALETTE_WIDTH + x] = gdk_color;
1229
1230   screen = gtk_widget_get_screen (GTK_WIDGET (colorsel));
1231   if (change_palette_hook != default_change_palette_func)
1232     (* change_palette_hook) (screen, current_colors, 
1233                              GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT);
1234   else if (noscreen_change_palette_hook != default_noscreen_change_palette_func)
1235     {
1236       if (screen != gdk_screen_get_default ())
1237         g_warning ("gtk_color_selection_set_change_palette_hook used by widget is not on the default screen.");
1238       (* noscreen_change_palette_hook) (current_colors, 
1239                                         GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT);
1240     }
1241   else
1242     (* change_palette_hook) (screen, current_colors, 
1243                              GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT);
1244
1245   g_free (current_colors);
1246 }
1247
1248 /* Changes the view color */
1249 static void
1250 palette_set_color (GtkWidget         *drawing_area,
1251                    GtkColorSelection *colorsel,
1252                    gdouble           *color)
1253 {
1254   gdouble *new_color = g_new (double, 4);
1255   GdkColor gdk_color;
1256   
1257   gdk_color.red = UNSCALE (color[0]);
1258   gdk_color.green = UNSCALE (color[1]);
1259   gdk_color.blue = UNSCALE (color[2]);
1260
1261   gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &gdk_color);
1262   
1263   if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drawing_area), "color_set")) == 0)
1264     {
1265       static const GtkTargetEntry targets[] = {
1266         { "application/x-color", 0 }
1267       };
1268       gtk_drag_source_set (drawing_area,
1269                            GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
1270                            targets, 1,
1271                            GDK_ACTION_COPY | GDK_ACTION_MOVE);
1272       
1273       g_signal_connect (drawing_area,
1274                         "drag_begin",
1275                         G_CALLBACK (palette_drag_begin),
1276                         colorsel);
1277       g_signal_connect (drawing_area,
1278                         "drag_data_get",
1279                         G_CALLBACK (palette_drag_handle),
1280                         colorsel);
1281       
1282       g_object_set_data (G_OBJECT (drawing_area), I_("color_set"),
1283                          GINT_TO_POINTER (1));
1284     }
1285
1286   new_color[0] = color[0];
1287   new_color[1] = color[1];
1288   new_color[2] = color[2];
1289   new_color[3] = 1.0;
1290   
1291   g_object_set_data_full (G_OBJECT (drawing_area), I_("color_val"), new_color, (GDestroyNotify)g_free);
1292 }
1293
1294 static gboolean
1295 palette_expose (GtkWidget      *drawing_area,
1296                 GdkEventExpose *event,
1297                 gpointer        data)
1298 {
1299   if (drawing_area->window == NULL)
1300     return FALSE;
1301   
1302   palette_paint (drawing_area, &(event->area), data);
1303   
1304   return FALSE;
1305 }
1306
1307 static void
1308 popup_position_func (GtkMenu   *menu,
1309                      gint      *x,
1310                      gint      *y,
1311                      gboolean  *push_in,
1312                      gpointer   user_data)
1313 {
1314   GtkWidget *widget;
1315   GtkRequisition req;      
1316   gint root_x, root_y;
1317   GdkScreen *screen;
1318   
1319   widget = GTK_WIDGET (user_data);
1320   
1321   g_return_if_fail (GTK_WIDGET_REALIZED (widget));
1322
1323   gdk_window_get_origin (widget->window, &root_x, &root_y);
1324   
1325   gtk_widget_size_request (GTK_WIDGET (menu), &req);
1326
1327   /* Put corner of menu centered on color cell */
1328   *x = root_x + widget->allocation.width / 2;
1329   *y = root_y + widget->allocation.height / 2;
1330
1331   /* Ensure sanity */
1332   screen = gtk_widget_get_screen (widget);
1333   *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
1334   *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
1335 }
1336
1337 static void
1338 save_color_selected (GtkWidget *menuitem,
1339                      gpointer   data)
1340 {
1341   GtkColorSelection *colorsel;
1342   GtkWidget *drawing_area;
1343   ColorSelectionPrivate *priv;
1344
1345   drawing_area = GTK_WIDGET (data);
1346   
1347   colorsel = GTK_COLOR_SELECTION (g_object_get_data (G_OBJECT (drawing_area),
1348                                                      "gtk-color-sel"));
1349
1350   priv = colorsel->private_data;
1351   
1352   palette_change_color (drawing_area, colorsel, priv->color);  
1353 }
1354
1355 static void
1356 do_popup (GtkColorSelection *colorsel,
1357           GtkWidget         *drawing_area,
1358           guint32            timestamp)
1359 {
1360   GtkWidget *menu;
1361   GtkWidget *mi;
1362   
1363   g_object_set_data (G_OBJECT (drawing_area),
1364                      I_("gtk-color-sel"),
1365                      colorsel);
1366   
1367   menu = gtk_menu_new ();
1368
1369   mi = gtk_menu_item_new_with_mnemonic (_("_Save color here"));
1370
1371   g_signal_connect (mi, "activate",
1372                     G_CALLBACK (save_color_selected),
1373                     drawing_area);
1374   
1375   gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
1376
1377   gtk_widget_show_all (mi);
1378
1379   gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
1380                   popup_position_func, drawing_area,
1381                   3, timestamp);
1382 }
1383
1384
1385 static gboolean
1386 palette_enter (GtkWidget        *drawing_area,
1387                GdkEventCrossing *event,
1388                gpointer        data)
1389 {
1390   g_object_set_data (G_OBJECT (drawing_area),
1391                      I_("gtk-colorsel-have-pointer"),
1392                      GUINT_TO_POINTER (TRUE));
1393
1394   return FALSE;
1395 }
1396
1397 static gboolean
1398 palette_leave (GtkWidget        *drawing_area,
1399                GdkEventCrossing *event,
1400                gpointer        data)
1401 {
1402   g_object_set_data (G_OBJECT (drawing_area),
1403                      I_("gtk-colorsel-have-pointer"),
1404                      NULL);
1405
1406   return FALSE;
1407 }
1408
1409 static gboolean
1410 palette_press (GtkWidget      *drawing_area,
1411                GdkEventButton *event,
1412                gpointer        data)
1413 {
1414   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
1415
1416   gtk_widget_grab_focus (drawing_area);
1417   
1418   if (event->button == 3 &&
1419       event->type == GDK_BUTTON_PRESS)
1420     {
1421       do_popup (colorsel, drawing_area, event->time);
1422       return TRUE;
1423     }
1424
1425   return FALSE;
1426 }
1427
1428 static gboolean
1429 palette_release (GtkWidget      *drawing_area,
1430                  GdkEventButton *event,
1431                  gpointer        data)
1432 {
1433   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
1434
1435   gtk_widget_grab_focus (drawing_area);
1436
1437   if (event->button == 1 &&
1438       g_object_get_data (G_OBJECT (drawing_area),
1439                          "gtk-colorsel-have-pointer") != NULL)
1440     {
1441       if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drawing_area), "color_set")) != 0)
1442         {
1443           gdouble color[4];
1444           palette_get_color (drawing_area, color);
1445           set_color_internal (colorsel, color);
1446         }
1447     }
1448
1449   return FALSE;
1450 }
1451
1452 static void
1453 palette_drop_handle (GtkWidget        *widget,
1454                      GdkDragContext   *context,
1455                      gint              x,
1456                      gint              y,
1457                      GtkSelectionData *selection_data,
1458                      guint             info,
1459                      guint             time,
1460                      gpointer          data)
1461 {
1462   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
1463   guint16 *vals;
1464   gdouble color[4];
1465   
1466   if (selection_data->length < 0)
1467     return;
1468   
1469   /* We accept drops with the wrong format, since the KDE color
1470    * chooser incorrectly drops application/x-color with format 8.
1471    */
1472   if (selection_data->length != 8)
1473     {
1474       g_warning ("Received invalid color data\n");
1475       return;
1476     }
1477   
1478   vals = (guint16 *)selection_data->data;
1479   
1480   color[0] = (gdouble)vals[0] / 0xffff;
1481   color[1] = (gdouble)vals[1] / 0xffff;
1482   color[2] = (gdouble)vals[2] / 0xffff;
1483   color[3] = (gdouble)vals[3] / 0xffff;
1484   palette_change_color (widget, colorsel, color);
1485   set_color_internal (colorsel, color);
1486 }
1487
1488 static gint
1489 palette_activate (GtkWidget   *widget,
1490                   GdkEventKey *event,
1491                   gpointer     data)
1492 {
1493   /* should have a drawing area subclass with an activate signal */
1494   if ((event->keyval == GDK_space) ||
1495       (event->keyval == GDK_Return) ||
1496       (event->keyval == GDK_ISO_Enter) ||
1497       (event->keyval == GDK_KP_Enter) ||
1498       (event->keyval == GDK_KP_Space))
1499     {
1500       if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "color_set")) != 0)
1501         {
1502           gdouble color[4];
1503           palette_get_color (widget, color);
1504           set_color_internal (GTK_COLOR_SELECTION (data), color);
1505         }
1506       return TRUE;
1507     }
1508   
1509   return FALSE;
1510 }
1511
1512 static gboolean
1513 palette_popup (GtkWidget *widget,
1514                gpointer   data)
1515 {
1516   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
1517
1518   do_popup (colorsel, widget, GDK_CURRENT_TIME);
1519   return TRUE;
1520 }
1521                
1522
1523 static GtkWidget*
1524 palette_new (GtkColorSelection *colorsel)
1525 {
1526   GtkWidget *retval;
1527   ColorSelectionPrivate *priv;
1528   
1529   static const GtkTargetEntry targets[] = {
1530     { "application/x-color", 0 }
1531   };
1532
1533   priv = colorsel->private_data;
1534   
1535   retval = gtk_drawing_area_new ();
1536
1537   GTK_WIDGET_SET_FLAGS (retval, GTK_CAN_FOCUS);
1538   
1539   g_object_set_data (G_OBJECT (retval), I_("color_set"), GINT_TO_POINTER (0)); 
1540   gtk_widget_set_events (retval, GDK_BUTTON_PRESS_MASK
1541                          | GDK_BUTTON_RELEASE_MASK
1542                          | GDK_EXPOSURE_MASK
1543                          | GDK_ENTER_NOTIFY_MASK
1544                          | GDK_LEAVE_NOTIFY_MASK);
1545   
1546   g_signal_connect (retval, "expose_event",
1547                     G_CALLBACK (palette_expose), colorsel);
1548   g_signal_connect (retval, "button_press_event",
1549                     G_CALLBACK (palette_press), colorsel);
1550   g_signal_connect (retval, "button_release_event",
1551                     G_CALLBACK (palette_release), colorsel);
1552   g_signal_connect (retval, "enter_notify_event",
1553                     G_CALLBACK (palette_enter), colorsel);
1554   g_signal_connect (retval, "leave_notify_event",
1555                     G_CALLBACK (palette_leave), colorsel);
1556   g_signal_connect (retval, "key_press_event",
1557                     G_CALLBACK (palette_activate), colorsel);
1558   g_signal_connect (retval, "popup_menu",
1559                     G_CALLBACK (palette_popup), colorsel);
1560   
1561   gtk_drag_dest_set (retval,
1562                      GTK_DEST_DEFAULT_HIGHLIGHT |
1563                      GTK_DEST_DEFAULT_MOTION |
1564                      GTK_DEST_DEFAULT_DROP,
1565                      targets, 1,
1566                      GDK_ACTION_COPY);
1567   
1568   g_signal_connect (retval, "drag_end",
1569                     G_CALLBACK (palette_drag_end), NULL);
1570   g_signal_connect (retval, "drag_data_received",
1571                     G_CALLBACK (palette_drop_handle), colorsel);
1572
1573   gtk_widget_set_tooltip_text (retval,
1574                         _("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.\""));
1575   return retval;
1576 }
1577
1578
1579 /*
1580  *
1581  * The actual GtkColorSelection widget
1582  *
1583  */
1584
1585 static GdkCursor *
1586 make_picker_cursor (GdkScreen *screen)
1587 {
1588   GdkCursor *cursor;
1589
1590   cursor = gdk_cursor_new_from_name (gdk_screen_get_display (screen),
1591                                      "color-picker");
1592
1593   if (!cursor)
1594     {
1595       GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
1596       GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
1597       GdkWindow *window;
1598       GdkPixmap *pixmap, *mask;
1599
1600       window = gdk_screen_get_root_window (screen);
1601       
1602       pixmap =
1603         gdk_bitmap_create_from_data (window, (gchar *) dropper_bits,
1604                                      DROPPER_WIDTH, DROPPER_HEIGHT);
1605       
1606       mask =
1607         gdk_bitmap_create_from_data (window, (gchar *) dropper_mask,
1608                                      DROPPER_WIDTH, DROPPER_HEIGHT);
1609       
1610       cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg,
1611                                            DROPPER_X_HOT, DROPPER_Y_HOT);
1612       
1613       g_object_unref (pixmap);
1614       g_object_unref (mask);
1615     }
1616       
1617   return cursor;
1618 }
1619
1620 static void
1621 grab_color_at_mouse (GdkScreen *screen,
1622                      gint       x_root,
1623                      gint       y_root,
1624                      gpointer   data)
1625 {
1626   GdkImage *image;
1627   guint32 pixel;
1628   GtkColorSelection *colorsel = data;
1629   ColorSelectionPrivate *priv;
1630   GdkColor color;
1631   GdkColormap *colormap = gdk_screen_get_system_colormap (screen);
1632   GdkWindow *root_window = gdk_screen_get_root_window (screen);
1633   
1634   priv = colorsel->private_data;
1635   
1636   image = gdk_drawable_get_image (root_window, x_root, y_root, 1, 1);
1637   if (!image)
1638     {
1639       gint x, y;
1640       GdkDisplay *display = gdk_screen_get_display (screen);
1641       GdkWindow *window = gdk_display_get_window_at_pointer (display, &x, &y);
1642       if (!window)
1643         return;
1644       image = gdk_drawable_get_image (window, x, y, 1, 1);
1645       if (!image)
1646         return;
1647     }
1648   pixel = gdk_image_get_pixel (image, 0, 0);
1649   g_object_unref (image);
1650
1651   gdk_colormap_query_color (colormap, pixel, &color);
1652   
1653   priv->color[COLORSEL_RED] = SCALE (color.red);
1654   priv->color[COLORSEL_GREEN] = SCALE (color.green);
1655   priv->color[COLORSEL_BLUE] = SCALE (color.blue);
1656   
1657   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1658                   priv->color[COLORSEL_GREEN],
1659                   priv->color[COLORSEL_BLUE],
1660                   &priv->color[COLORSEL_HUE],
1661                   &priv->color[COLORSEL_SATURATION],
1662                   &priv->color[COLORSEL_VALUE]);
1663
1664   update_color (colorsel);
1665 }
1666
1667 static void
1668 shutdown_eyedropper (GtkWidget *widget)
1669 {
1670   GtkColorSelection *colorsel;
1671   ColorSelectionPrivate *priv;
1672   GdkDisplay *display = gtk_widget_get_display (widget);
1673
1674   colorsel = GTK_COLOR_SELECTION (widget);
1675   priv = colorsel->private_data;    
1676
1677   if (priv->has_grab)
1678     {
1679       gdk_display_keyboard_ungrab (display, priv->grab_time);
1680       gdk_display_pointer_ungrab (display, priv->grab_time);
1681       gtk_grab_remove (priv->dropper_grab_widget);
1682
1683       priv->has_grab = FALSE;
1684     }
1685 }
1686
1687 static void
1688 mouse_motion (GtkWidget      *invisible,
1689               GdkEventMotion *event,
1690               gpointer        data)
1691 {
1692   grab_color_at_mouse (gdk_event_get_screen ((GdkEvent *)event),
1693                        event->x_root, event->y_root, data); 
1694 }
1695
1696 static gboolean
1697 mouse_release (GtkWidget      *invisible,
1698                GdkEventButton *event,
1699                gpointer        data)
1700 {
1701   /* GtkColorSelection *colorsel = data; */
1702
1703   if (event->button != 1)
1704     return FALSE;
1705
1706   grab_color_at_mouse (gdk_event_get_screen ((GdkEvent *)event),
1707                        event->x_root, event->y_root, data);
1708
1709   shutdown_eyedropper (GTK_WIDGET (data));
1710   
1711   g_signal_handlers_disconnect_by_func (invisible,
1712                                         mouse_motion,
1713                                         data);
1714   g_signal_handlers_disconnect_by_func (invisible,
1715                                         mouse_release,
1716                                         data);
1717
1718   return TRUE;
1719 }
1720
1721 /* Helper Functions */
1722
1723 static gboolean
1724 key_press (GtkWidget   *invisible,
1725            GdkEventKey *event,
1726            gpointer     data)
1727 {  
1728   GdkDisplay *display = gtk_widget_get_display (invisible);
1729   GdkScreen *screen = gdk_event_get_screen ((GdkEvent *)event);
1730   guint state = event->state & gtk_accelerator_get_default_mod_mask ();
1731   gint x, y;
1732   gint dx, dy;
1733
1734   gdk_display_get_pointer (display, NULL, &x, &y, NULL);
1735
1736   dx = 0;
1737   dy = 0;
1738
1739   switch (event->keyval) 
1740     {
1741     case GDK_space:
1742     case GDK_Return:
1743     case GDK_ISO_Enter:
1744     case GDK_KP_Enter:
1745     case GDK_KP_Space:
1746       grab_color_at_mouse (screen, x, y, data);
1747       /* fall through */
1748
1749     case GDK_Escape:
1750       shutdown_eyedropper (data);
1751       
1752       g_signal_handlers_disconnect_by_func (invisible,
1753                                             mouse_press,
1754                                             data);
1755       g_signal_handlers_disconnect_by_func (invisible,
1756                                             key_press,
1757                                             data);
1758       
1759       return TRUE;
1760
1761 #if defined GDK_WINDOWING_X11 || defined GDK_WINDOWING_WIN32
1762     case GDK_Up:
1763     case GDK_KP_Up:
1764       dy = state == GDK_MOD1_MASK ? -BIG_STEP : -1;
1765       break;
1766
1767     case GDK_Down:
1768     case GDK_KP_Down:
1769       dy = state == GDK_MOD1_MASK ? BIG_STEP : 1;
1770       break;
1771
1772     case GDK_Left:
1773     case GDK_KP_Left:
1774       dx = state == GDK_MOD1_MASK ? -BIG_STEP : -1;
1775       break;
1776
1777     case GDK_Right:
1778     case GDK_KP_Right:
1779       dx = state == GDK_MOD1_MASK ? BIG_STEP : 1;
1780       break;
1781 #endif
1782
1783     default:
1784       return FALSE;
1785     }
1786
1787   gdk_display_warp_pointer (display, screen, x + dx, y + dy);
1788   
1789   return TRUE;
1790
1791 }
1792
1793 static gboolean
1794 mouse_press (GtkWidget      *invisible,
1795              GdkEventButton *event,
1796              gpointer        data)
1797 {
1798   /* GtkColorSelection *colorsel = data; */
1799   
1800   if (event->type == GDK_BUTTON_PRESS &&
1801       event->button == 1)
1802     {
1803       g_signal_connect (invisible, "motion_notify_event",
1804                         G_CALLBACK (mouse_motion),
1805                         data);
1806       g_signal_connect (invisible, "button_release_event",
1807                         G_CALLBACK (mouse_release),
1808                         data);
1809       g_signal_handlers_disconnect_by_func (invisible,
1810                                             mouse_press,
1811                                             data);
1812       g_signal_handlers_disconnect_by_func (invisible,
1813                                             key_press,
1814                                             data);
1815       return TRUE;
1816     }
1817
1818   return FALSE;
1819 }
1820
1821 /* when the button is clicked */
1822 static void
1823 get_screen_color (GtkWidget *button)
1824 {
1825   GtkColorSelection *colorsel = g_object_get_data (G_OBJECT (button), "COLORSEL");
1826   ColorSelectionPrivate *priv = colorsel->private_data;
1827   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (button));
1828   GdkCursor *picker_cursor;
1829   GdkGrabStatus grab_status;
1830   GtkWidget *grab_widget, *toplevel;
1831
1832   guint32 time = gtk_get_current_event_time ();
1833   
1834   if (priv->dropper_grab_widget == NULL)
1835     {
1836       grab_widget = gtk_window_new (GTK_WINDOW_POPUP);
1837       gtk_window_set_screen (GTK_WINDOW (grab_widget), screen);
1838       gtk_window_resize (GTK_WINDOW (grab_widget), 1, 1);
1839       gtk_window_move (GTK_WINDOW (grab_widget), -100, -100);
1840       gtk_widget_show (grab_widget);
1841
1842       gtk_widget_add_events (grab_widget,
1843                              GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
1844       
1845       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (colorsel));
1846   
1847       if (GTK_IS_WINDOW (toplevel))
1848         {
1849           if (GTK_WINDOW (toplevel)->group)
1850             gtk_window_group_add_window (GTK_WINDOW (toplevel)->group, 
1851                                          GTK_WINDOW (grab_widget));
1852         }
1853
1854       priv->dropper_grab_widget = grab_widget;
1855     }
1856
1857   if (gdk_keyboard_grab (priv->dropper_grab_widget->window,
1858                          FALSE, time) != GDK_GRAB_SUCCESS)
1859     return;
1860   
1861   picker_cursor = make_picker_cursor (screen);
1862   grab_status = gdk_pointer_grab (priv->dropper_grab_widget->window,
1863                                   FALSE,
1864                                   GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
1865                                   NULL,
1866                                   picker_cursor,
1867                                   time);
1868   gdk_cursor_unref (picker_cursor);
1869   
1870   if (grab_status != GDK_GRAB_SUCCESS)
1871     {
1872       gdk_display_keyboard_ungrab (gtk_widget_get_display (button), time);
1873       return;
1874     }
1875
1876   gtk_grab_add (priv->dropper_grab_widget);
1877   priv->grab_time = time;
1878   priv->has_grab = TRUE;
1879   
1880   g_signal_connect (priv->dropper_grab_widget, "button_press_event",
1881                     G_CALLBACK (mouse_press), colorsel);
1882   g_signal_connect (priv->dropper_grab_widget, "key_press_event",
1883                     G_CALLBACK (key_press), colorsel);
1884 }
1885
1886 static void
1887 hex_changed (GtkWidget *hex_entry,
1888              gpointer   data)
1889 {
1890   GtkColorSelection *colorsel;
1891   ColorSelectionPrivate *priv;
1892   GdkColor color;
1893   gchar *text;
1894   
1895   colorsel = GTK_COLOR_SELECTION (data);
1896   priv = colorsel->private_data;
1897   
1898   if (priv->changing)
1899     return;
1900   
1901   text = gtk_editable_get_chars (GTK_EDITABLE (priv->hex_entry), 0, -1);
1902   if (gdk_color_parse (text, &color))
1903     {
1904       priv->color[COLORSEL_RED] = CLAMP (color.red/65535.0, 0.0, 1.0);
1905       priv->color[COLORSEL_GREEN] = CLAMP (color.green/65535.0, 0.0, 1.0);
1906       priv->color[COLORSEL_BLUE] = CLAMP (color.blue/65535.0, 0.0, 1.0);
1907       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1908                       priv->color[COLORSEL_GREEN],
1909                       priv->color[COLORSEL_BLUE],
1910                       &priv->color[COLORSEL_HUE],
1911                       &priv->color[COLORSEL_SATURATION],
1912                       &priv->color[COLORSEL_VALUE]);
1913       update_color (colorsel);
1914     }
1915   g_free (text);
1916 }
1917
1918 static gboolean
1919 hex_focus_out (GtkWidget     *hex_entry, 
1920                GdkEventFocus *event,
1921                gpointer       data)
1922 {
1923   hex_changed (hex_entry, data);
1924   
1925   return FALSE;
1926 }
1927
1928 static void
1929 hsv_changed (GtkWidget *hsv,
1930              gpointer   data)
1931 {
1932   GtkColorSelection *colorsel;
1933   ColorSelectionPrivate *priv;
1934   
1935   colorsel = GTK_COLOR_SELECTION (data);
1936   priv = colorsel->private_data;
1937   
1938   if (priv->changing)
1939     return;
1940   
1941   gtk_hsv_get_color (GTK_HSV (hsv),
1942                      &priv->color[COLORSEL_HUE],
1943                      &priv->color[COLORSEL_SATURATION],
1944                      &priv->color[COLORSEL_VALUE]);
1945   gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
1946                   priv->color[COLORSEL_SATURATION],
1947                   priv->color[COLORSEL_VALUE],
1948                   &priv->color[COLORSEL_RED],
1949                   &priv->color[COLORSEL_GREEN],
1950                   &priv->color[COLORSEL_BLUE]);
1951   update_color (colorsel);
1952 }
1953
1954 static void
1955 adjustment_changed (GtkAdjustment *adjustment,
1956                     gpointer       data)
1957 {
1958   GtkColorSelection *colorsel;
1959   ColorSelectionPrivate *priv;
1960   
1961   colorsel = GTK_COLOR_SELECTION (g_object_get_data (G_OBJECT (adjustment), "COLORSEL"));
1962   priv = colorsel->private_data;
1963   
1964   if (priv->changing)
1965     return;
1966   
1967   switch (GPOINTER_TO_INT (data))
1968     {
1969     case COLORSEL_SATURATION:
1970     case COLORSEL_VALUE:
1971       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 100;
1972       gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
1973                       priv->color[COLORSEL_SATURATION],
1974                       priv->color[COLORSEL_VALUE],
1975                       &priv->color[COLORSEL_RED],
1976                       &priv->color[COLORSEL_GREEN],
1977                       &priv->color[COLORSEL_BLUE]);
1978       break;
1979     case COLORSEL_HUE:
1980       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 360;
1981       gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
1982                       priv->color[COLORSEL_SATURATION],
1983                       priv->color[COLORSEL_VALUE],
1984                       &priv->color[COLORSEL_RED],
1985                       &priv->color[COLORSEL_GREEN],
1986                       &priv->color[COLORSEL_BLUE]);
1987       break;
1988     case COLORSEL_RED:
1989     case COLORSEL_GREEN:
1990     case COLORSEL_BLUE:
1991       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
1992       
1993       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1994                       priv->color[COLORSEL_GREEN],
1995                       priv->color[COLORSEL_BLUE],
1996                       &priv->color[COLORSEL_HUE],
1997                       &priv->color[COLORSEL_SATURATION],
1998                       &priv->color[COLORSEL_VALUE]);
1999       break;
2000     default:
2001       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
2002       break;
2003     }
2004   update_color (colorsel);
2005 }
2006
2007 static void 
2008 opacity_entry_changed (GtkWidget *opacity_entry,
2009                        gpointer   data)
2010 {
2011   GtkColorSelection *colorsel;
2012   ColorSelectionPrivate *priv;
2013   GtkAdjustment *adj;
2014   gchar *text;
2015   
2016   colorsel = GTK_COLOR_SELECTION (data);
2017   priv = colorsel->private_data;
2018   
2019   if (priv->changing)
2020     return;
2021   
2022   text = gtk_editable_get_chars (GTK_EDITABLE (priv->opacity_entry), 0, -1);
2023   adj = gtk_range_get_adjustment (GTK_RANGE (priv->opacity_slider));
2024   gtk_adjustment_set_value (adj, g_strtod (text, NULL)); 
2025   
2026   update_color (colorsel);
2027   
2028   g_free (text);
2029 }
2030
2031 static void
2032 make_label_spinbutton (GtkColorSelection *colorsel,
2033                        GtkWidget        **spinbutton,
2034                        gchar             *text,
2035                        GtkWidget         *table,
2036                        gint               i,
2037                        gint               j,
2038                        gint               channel_type,
2039                        const gchar       *tooltip)
2040 {
2041   GtkWidget *label;
2042   GtkAdjustment *adjust;
2043
2044   if (channel_type == COLORSEL_HUE)
2045     {
2046       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 360.0, 1.0, 1.0, 1.0));
2047     }
2048   else if (channel_type == COLORSEL_SATURATION ||
2049            channel_type == COLORSEL_VALUE)
2050     {
2051       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 100.0, 1.0, 1.0, 1.0));
2052     }
2053   else
2054     {
2055       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 1.0, 1.0));
2056     }
2057   g_object_set_data (G_OBJECT (adjust), I_("COLORSEL"), colorsel);
2058   *spinbutton = gtk_spin_button_new (adjust, 10.0, 0);
2059
2060   gtk_widget_set_tooltip_text (*spinbutton, tooltip);  
2061
2062   g_signal_connect (adjust, "value_changed",
2063                     G_CALLBACK (adjustment_changed),
2064                     GINT_TO_POINTER (channel_type));
2065   label = gtk_label_new_with_mnemonic (text);
2066   gtk_label_set_mnemonic_widget (GTK_LABEL (label), *spinbutton);
2067
2068   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
2069   gtk_table_attach_defaults (GTK_TABLE (table), label, i, i+1, j, j+1);
2070   gtk_table_attach_defaults (GTK_TABLE (table), *spinbutton, i+1, i+2, j, j+1);
2071 }
2072
2073 static void
2074 make_palette_frame (GtkColorSelection *colorsel,
2075                     GtkWidget         *table,
2076                     gint               i,
2077                     gint               j)
2078 {
2079   GtkWidget *frame;
2080   ColorSelectionPrivate *priv;
2081   
2082   priv = colorsel->private_data;
2083   frame = gtk_frame_new (NULL);
2084   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
2085   priv->custom_palette[i][j] = palette_new (colorsel);
2086   gtk_widget_set_size_request (priv->custom_palette[i][j], CUSTOM_PALETTE_ENTRY_WIDTH, CUSTOM_PALETTE_ENTRY_HEIGHT);
2087   gtk_container_add (GTK_CONTAINER (frame), priv->custom_palette[i][j]);
2088   gtk_table_attach_defaults (GTK_TABLE (table), frame, i, i+1, j, j+1);
2089 }
2090
2091 /* Set the palette entry [x][y] to be the currently selected one. */
2092 static void 
2093 set_selected_palette (GtkColorSelection *colorsel, int x, int y)
2094 {
2095   ColorSelectionPrivate *priv = colorsel->private_data; 
2096
2097   gtk_widget_grab_focus (priv->custom_palette[x][y]);
2098 }
2099
2100 static double
2101 scale_round (double val, double factor)
2102 {
2103   val = floor (val * factor + 0.5);
2104   val = MAX (val, 0);
2105   val = MIN (val, factor);
2106   return val;
2107 }
2108
2109 static void
2110 update_color (GtkColorSelection *colorsel)
2111 {
2112   ColorSelectionPrivate *priv = colorsel->private_data;
2113   gchar entryval[12];
2114   gchar opacity_text[32];
2115   gchar *ptr;
2116   
2117   priv->changing = TRUE;
2118   color_sample_update_samples (colorsel);
2119   
2120   gtk_hsv_set_color (GTK_HSV (priv->triangle_colorsel),
2121                      priv->color[COLORSEL_HUE],
2122                      priv->color[COLORSEL_SATURATION],
2123                      priv->color[COLORSEL_VALUE]);
2124   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2125                             (GTK_SPIN_BUTTON (priv->hue_spinbutton)),
2126                             scale_round (priv->color[COLORSEL_HUE], 360));
2127   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2128                             (GTK_SPIN_BUTTON (priv->sat_spinbutton)),
2129                             scale_round (priv->color[COLORSEL_SATURATION], 100));
2130   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2131                             (GTK_SPIN_BUTTON (priv->val_spinbutton)),
2132                             scale_round (priv->color[COLORSEL_VALUE], 100));
2133   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2134                             (GTK_SPIN_BUTTON (priv->red_spinbutton)),
2135                             scale_round (priv->color[COLORSEL_RED], 255));
2136   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2137                             (GTK_SPIN_BUTTON (priv->green_spinbutton)),
2138                             scale_round (priv->color[COLORSEL_GREEN], 255));
2139   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2140                             (GTK_SPIN_BUTTON (priv->blue_spinbutton)),
2141                             scale_round (priv->color[COLORSEL_BLUE], 255));
2142   gtk_adjustment_set_value (gtk_range_get_adjustment
2143                             (GTK_RANGE (priv->opacity_slider)),
2144                             scale_round (priv->color[COLORSEL_OPACITY], 255));
2145   
2146   g_snprintf (opacity_text, 32, "%.0f", scale_round (priv->color[COLORSEL_OPACITY], 255));
2147   gtk_entry_set_text (GTK_ENTRY (priv->opacity_entry), opacity_text);
2148   
2149   g_snprintf (entryval, 11, "#%2X%2X%2X",
2150               (guint) (scale_round (priv->color[COLORSEL_RED], 255)),
2151               (guint) (scale_round (priv->color[COLORSEL_GREEN], 255)),
2152               (guint) (scale_round (priv->color[COLORSEL_BLUE], 255)));
2153   
2154   for (ptr = entryval; *ptr; ptr++)
2155     if (*ptr == ' ')
2156       *ptr = '0';
2157   gtk_entry_set_text (GTK_ENTRY (priv->hex_entry), entryval);
2158   priv->changing = FALSE;
2159
2160   g_object_ref (colorsel);
2161   
2162   g_signal_emit (colorsel, color_selection_signals[COLOR_CHANGED], 0);
2163   
2164   g_object_freeze_notify (G_OBJECT (colorsel));
2165   g_object_notify (G_OBJECT (colorsel), "current-color");
2166   g_object_notify (G_OBJECT (colorsel), "current-alpha");
2167   g_object_thaw_notify (G_OBJECT (colorsel));
2168   
2169   g_object_unref (colorsel);
2170 }
2171
2172 static void
2173 update_palette (GtkColorSelection *colorsel)
2174 {
2175   GdkColor *current_colors;
2176   gint i, j;
2177
2178   current_colors = get_current_colors (colorsel);
2179   
2180   for (i = 0; i < GTK_CUSTOM_PALETTE_HEIGHT; i++)
2181     {
2182       for (j = 0; j < GTK_CUSTOM_PALETTE_WIDTH; j++)
2183         {
2184           gint index;
2185
2186           index = i * GTK_CUSTOM_PALETTE_WIDTH + j;
2187           
2188           gtk_color_selection_set_palette_color (colorsel,
2189                                                  index,
2190                                                  &current_colors[index]);
2191         }
2192     }
2193
2194   g_free (current_colors);
2195 }
2196
2197 static void
2198 palette_change_notify_instance (GObject    *object,
2199                                 GParamSpec *pspec,
2200                                 gpointer    data)
2201 {
2202   update_palette (GTK_COLOR_SELECTION (data));
2203 }
2204
2205 static void
2206 default_noscreen_change_palette_func (const GdkColor *colors,
2207                                       gint            n_colors)
2208 {
2209   default_change_palette_func (gdk_screen_get_default (), colors, n_colors);
2210 }
2211
2212 static void
2213 default_change_palette_func (GdkScreen      *screen,
2214                              const GdkColor *colors,
2215                              gint            n_colors)
2216 {
2217   gchar *str;
2218   
2219   str = gtk_color_selection_palette_to_string (colors, n_colors);
2220
2221   gtk_settings_set_string_property (gtk_settings_get_for_screen (screen),
2222                                     "gtk-color-palette",
2223                                     str,
2224                                     "gtk_color_selection_palette_to_string");
2225
2226   g_free (str);
2227 }
2228
2229 /**
2230  * gtk_color_selection_new:
2231  * 
2232  * Creates a new GtkColorSelection.
2233  * 
2234  * Return value: a new #GtkColorSelection
2235  **/
2236 GtkWidget *
2237 gtk_color_selection_new (void)
2238 {
2239   GtkColorSelection *colorsel;
2240   ColorSelectionPrivate *priv;
2241   gdouble color[4];
2242   color[0] = 1.0;
2243   color[1] = 1.0;
2244   color[2] = 1.0;
2245   color[3] = 1.0;
2246   
2247   colorsel = g_object_new (GTK_TYPE_COLOR_SELECTION, NULL);
2248   priv = colorsel->private_data;
2249   set_color_internal (colorsel, color);
2250   gtk_color_selection_set_has_opacity_control (colorsel, TRUE);
2251   
2252   /* We want to make sure that default_set is FALSE */
2253   /* This way the user can still set it */
2254   priv->default_set = FALSE;
2255   priv->default_alpha_set = FALSE;
2256   
2257   return GTK_WIDGET (colorsel);
2258 }
2259
2260
2261 void
2262 gtk_color_selection_set_update_policy (GtkColorSelection *colorsel,
2263                                        GtkUpdateType      policy)
2264 {
2265   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2266 }
2267
2268 /**
2269  * gtk_color_selection_get_has_opacity_control:
2270  * @colorsel: a #GtkColorSelection.
2271  * 
2272  * Determines whether the colorsel has an opacity control.
2273  * 
2274  * Return value: %TRUE if the @colorsel has an opacity control.  %FALSE if it does't.
2275  **/
2276 gboolean
2277 gtk_color_selection_get_has_opacity_control (GtkColorSelection *colorsel)
2278 {
2279   ColorSelectionPrivate *priv;
2280   
2281   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2282   
2283   priv = colorsel->private_data;
2284   
2285   return priv->has_opacity;
2286 }
2287
2288 /**
2289  * gtk_color_selection_set_has_opacity_control:
2290  * @colorsel: a #GtkColorSelection.
2291  * @has_opacity: %TRUE if @colorsel can set the opacity, %FALSE otherwise.
2292  *
2293  * Sets the @colorsel to use or not use opacity.
2294  * 
2295  **/
2296 void
2297 gtk_color_selection_set_has_opacity_control (GtkColorSelection *colorsel,
2298                                              gboolean           has_opacity)
2299 {
2300   ColorSelectionPrivate *priv;
2301   
2302   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2303   
2304   priv = colorsel->private_data;
2305   has_opacity = has_opacity != FALSE;
2306   
2307   if (priv->has_opacity != has_opacity)
2308     {
2309       priv->has_opacity = has_opacity;
2310       if (has_opacity)
2311         {
2312           gtk_widget_show (priv->opacity_slider);
2313           gtk_widget_show (priv->opacity_label);
2314           gtk_widget_show (priv->opacity_entry);
2315         }
2316       else
2317         {
2318           gtk_widget_hide (priv->opacity_slider);
2319           gtk_widget_hide (priv->opacity_label);
2320           gtk_widget_hide (priv->opacity_entry);
2321         }
2322       color_sample_update_samples (colorsel);
2323       
2324       g_object_notify (G_OBJECT (colorsel), "has-opacity-control");
2325     }
2326 }
2327
2328 /**
2329  * gtk_color_selection_get_has_palette:
2330  * @colorsel: a #GtkColorSelection.
2331  * 
2332  * Determines whether the color selector has a color palette.
2333  * 
2334  * Return value: %TRUE if the selector has a palette.  %FALSE if it hasn't.
2335  **/
2336 gboolean
2337 gtk_color_selection_get_has_palette (GtkColorSelection *colorsel)
2338 {
2339   ColorSelectionPrivate *priv;
2340   
2341   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2342   
2343   priv = colorsel->private_data;
2344   
2345   return priv->has_palette;
2346 }
2347
2348 /**
2349  * gtk_color_selection_set_has_palette:
2350  * @colorsel: a #GtkColorSelection.
2351  * @has_palette: %TRUE if palette is to be visible, %FALSE otherwise.
2352  *
2353  * Shows and hides the palette based upon the value of @has_palette.
2354  * 
2355  **/
2356 void
2357 gtk_color_selection_set_has_palette (GtkColorSelection *colorsel,
2358                                      gboolean           has_palette)
2359 {
2360   ColorSelectionPrivate *priv;
2361   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2362   
2363   priv = colorsel->private_data;
2364   has_palette = has_palette != FALSE;
2365   
2366   if (priv->has_palette != has_palette)
2367     {
2368       priv->has_palette = has_palette;
2369       if (has_palette)
2370         gtk_widget_show (priv->palette_frame);
2371       else
2372         gtk_widget_hide (priv->palette_frame);
2373       
2374       g_object_notify (G_OBJECT (colorsel), "has-palette");
2375     }
2376 }
2377
2378 /**
2379  * gtk_color_selection_set_current_color:
2380  * @colorsel: a #GtkColorSelection.
2381  * @color: A #GdkColor to set the current color with.
2382  *
2383  * Sets the current color to be @color.  The first time this is called, it will
2384  * also set the original color to be @color too.
2385  **/
2386 void
2387 gtk_color_selection_set_current_color (GtkColorSelection *colorsel,
2388                                        const GdkColor    *color)
2389 {
2390   ColorSelectionPrivate *priv;
2391   gint i;
2392   
2393   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2394   g_return_if_fail (color != NULL);
2395
2396   priv = colorsel->private_data;
2397   priv->changing = TRUE;
2398   priv->color[COLORSEL_RED] = SCALE (color->red);
2399   priv->color[COLORSEL_GREEN] = SCALE (color->green);
2400   priv->color[COLORSEL_BLUE] = SCALE (color->blue);
2401   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2402                   priv->color[COLORSEL_GREEN],
2403                   priv->color[COLORSEL_BLUE],
2404                   &priv->color[COLORSEL_HUE],
2405                   &priv->color[COLORSEL_SATURATION],
2406                   &priv->color[COLORSEL_VALUE]);
2407   if (priv->default_set == FALSE)
2408     {
2409       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2410         priv->old_color[i] = priv->color[i];
2411     }
2412   priv->default_set = TRUE;
2413   update_color (colorsel);
2414 }
2415
2416 /**
2417  * gtk_color_selection_set_current_alpha:
2418  * @colorsel: a #GtkColorSelection.
2419  * @alpha: an integer between 0 and 65535.
2420  *
2421  * Sets the current opacity to be @alpha.  The first time this is called, it will
2422  * also set the original opacity to be @alpha too.
2423  **/
2424 void
2425 gtk_color_selection_set_current_alpha (GtkColorSelection *colorsel,
2426                                        guint16            alpha)
2427 {
2428   ColorSelectionPrivate *priv;
2429   gint i;
2430   
2431   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2432   
2433   priv = colorsel->private_data;
2434   priv->changing = TRUE;
2435   priv->color[COLORSEL_OPACITY] = SCALE (alpha);
2436   if (priv->default_alpha_set == FALSE)
2437     {
2438       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2439         priv->old_color[i] = priv->color[i];
2440     }
2441   priv->default_alpha_set = TRUE;
2442   update_color (colorsel);
2443 }
2444
2445 /**
2446  * gtk_color_selection_set_color:
2447  * @colorsel: a #GtkColorSelection.
2448  * @color: an array of 4 doubles specifying the red, green, blue and opacity 
2449  *   to set the current color to.
2450  *
2451  * Sets the current color to be @color.  The first time this is called, it will
2452  * also set the original color to be @color too.
2453  *
2454  * Deprecated: 2.0: Use gtk_color_selection_set_current_color() instead.
2455  **/
2456 void
2457 gtk_color_selection_set_color (GtkColorSelection    *colorsel,
2458                                gdouble              *color)
2459 {
2460   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2461
2462   set_color_internal (colorsel, color);
2463 }
2464
2465 /**
2466  * gtk_color_selection_get_current_color:
2467  * @colorsel: a #GtkColorSelection.
2468  * @color: a #GdkColor to fill in with the current color.
2469  *
2470  * Sets @color to be the current color in the GtkColorSelection widget.
2471  **/
2472 void
2473 gtk_color_selection_get_current_color (GtkColorSelection *colorsel,
2474                                        GdkColor          *color)
2475 {
2476   ColorSelectionPrivate *priv;
2477   
2478   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2479   g_return_if_fail (color != NULL);
2480   
2481   priv = colorsel->private_data;
2482   color->red = UNSCALE (priv->color[COLORSEL_RED]);
2483   color->green = UNSCALE (priv->color[COLORSEL_GREEN]);
2484   color->blue = UNSCALE (priv->color[COLORSEL_BLUE]);
2485 }
2486
2487 /**
2488  * gtk_color_selection_get_current_alpha:
2489  * @colorsel: a #GtkColorSelection.
2490  *
2491  * Returns the current alpha value.
2492  *
2493  * Return value: an integer between 0 and 65535.
2494  **/
2495 guint16
2496 gtk_color_selection_get_current_alpha (GtkColorSelection *colorsel)
2497 {
2498   ColorSelectionPrivate *priv;
2499   
2500   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2501   
2502   priv = colorsel->private_data;
2503   return priv->has_opacity ? UNSCALE (priv->color[COLORSEL_OPACITY]) : 65535;
2504 }
2505
2506 /**
2507  * gtk_color_selection_get_color:
2508  * @colorsel: a #GtkColorSelection.
2509  * @color: an array of 4 #gdouble to fill in with the current color.
2510  *
2511  * Sets @color to be the current color in the GtkColorSelection widget.
2512  * 
2513  * This function is deprecated, use gtk_color_selection_get_current_color() instead.
2514  **/
2515 void
2516 gtk_color_selection_get_color (GtkColorSelection *colorsel,
2517                                gdouble           *color)
2518 {
2519   ColorSelectionPrivate *priv;
2520   
2521   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2522   
2523   priv = colorsel->private_data;
2524   color[0] = priv->color[COLORSEL_RED];
2525   color[1] = priv->color[COLORSEL_GREEN];
2526   color[2] = priv->color[COLORSEL_BLUE];
2527   color[3] = priv->has_opacity ? priv->color[COLORSEL_OPACITY] : 65535;
2528 }
2529
2530 /**
2531  * gtk_color_selection_set_previous_color:
2532  * @colorsel: a #GtkColorSelection.
2533  * @color: a #GdkColor to set the previous color with.
2534  *
2535  * Sets the 'previous' color to be @color.  This function should be called with
2536  * some hesitations, as it might seem confusing to have that color change.
2537  * Calling gtk_color_selection_set_current_color() will also set this color the first
2538  * time it is called.
2539  **/
2540 void
2541 gtk_color_selection_set_previous_color (GtkColorSelection *colorsel,
2542                                         const GdkColor    *color)
2543 {
2544   ColorSelectionPrivate *priv;
2545   
2546   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2547   g_return_if_fail (color != NULL);
2548   
2549   priv = colorsel->private_data;
2550   priv->changing = TRUE;
2551   priv->old_color[COLORSEL_RED] = SCALE (color->red);
2552   priv->old_color[COLORSEL_GREEN] = SCALE (color->green);
2553   priv->old_color[COLORSEL_BLUE] = SCALE (color->blue);
2554   gtk_rgb_to_hsv (priv->old_color[COLORSEL_RED],
2555                   priv->old_color[COLORSEL_GREEN],
2556                   priv->old_color[COLORSEL_BLUE],
2557                   &priv->old_color[COLORSEL_HUE],
2558                   &priv->old_color[COLORSEL_SATURATION],
2559                   &priv->old_color[COLORSEL_VALUE]);
2560   color_sample_update_samples (colorsel);
2561   priv->default_set = TRUE;
2562   priv->changing = FALSE;
2563 }
2564
2565 /**
2566  * gtk_color_selection_set_previous_alpha:
2567  * @colorsel: a #GtkColorSelection.
2568  * @alpha: an integer between 0 and 65535.
2569  *
2570  * Sets the 'previous' alpha to be @alpha.  This function should be called with
2571  * some hesitations, as it might seem confusing to have that alpha change.
2572  **/
2573 void
2574 gtk_color_selection_set_previous_alpha (GtkColorSelection *colorsel,
2575                                         guint16            alpha)
2576 {
2577   ColorSelectionPrivate *priv;
2578   
2579   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2580   
2581   priv = colorsel->private_data;
2582   priv->changing = TRUE;
2583   priv->old_color[COLORSEL_OPACITY] = SCALE (alpha);
2584   color_sample_update_samples (colorsel);
2585   priv->default_alpha_set = TRUE;
2586   priv->changing = FALSE;
2587 }
2588
2589
2590 /**
2591  * gtk_color_selection_get_previous_color:
2592  * @colorsel: a #GtkColorSelection.
2593  * @color: a #GdkColor to fill in with the original color value.
2594  *
2595  * Fills @color in with the original color value.
2596  **/
2597 void
2598 gtk_color_selection_get_previous_color (GtkColorSelection *colorsel,
2599                                         GdkColor           *color)
2600 {
2601   ColorSelectionPrivate *priv;
2602   
2603   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2604   g_return_if_fail (color != NULL);
2605   
2606   priv = colorsel->private_data;
2607   color->red = UNSCALE (priv->old_color[COLORSEL_RED]);
2608   color->green = UNSCALE (priv->old_color[COLORSEL_GREEN]);
2609   color->blue = UNSCALE (priv->old_color[COLORSEL_BLUE]);
2610 }
2611
2612 /**
2613  * gtk_color_selection_get_previous_alpha:
2614  * @colorsel: a #GtkColorSelection.
2615  *
2616  * Returns the previous alpha value.
2617  *
2618  * Return value: an integer between 0 and 65535.
2619  **/
2620 guint16
2621 gtk_color_selection_get_previous_alpha (GtkColorSelection *colorsel)
2622 {
2623   ColorSelectionPrivate *priv;
2624   
2625   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2626   
2627   priv = colorsel->private_data;
2628   return priv->has_opacity ? UNSCALE (priv->old_color[COLORSEL_OPACITY]) : 65535;
2629 }
2630
2631 /**
2632  * gtk_color_selection_set_palette_color:
2633  * @colorsel: a #GtkColorSelection.
2634  * @index: the color index of the palette.
2635  * @color: A #GdkColor to set the palette with.
2636  *
2637  * Sets the palette located at @index to have @color as its color.
2638  * 
2639  **/
2640 static void
2641 gtk_color_selection_set_palette_color (GtkColorSelection   *colorsel,
2642                                        gint                 index,
2643                                        GdkColor            *color)
2644 {
2645   ColorSelectionPrivate *priv;
2646   gint x, y;
2647   gdouble col[3];
2648   
2649   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2650   g_return_if_fail (index >= 0  && index < GTK_CUSTOM_PALETTE_WIDTH*GTK_CUSTOM_PALETTE_HEIGHT);
2651
2652   x = index % GTK_CUSTOM_PALETTE_WIDTH;
2653   y = index / GTK_CUSTOM_PALETTE_WIDTH;
2654   
2655   priv = colorsel->private_data;
2656   col[0] = SCALE (color->red);
2657   col[1] = SCALE (color->green);
2658   col[2] = SCALE (color->blue);
2659   
2660   palette_set_color (priv->custom_palette[x][y], colorsel, col);
2661 }
2662
2663 /**
2664  * gtk_color_selection_is_adjusting:
2665  * @colorsel: a #GtkColorSelection.
2666  *
2667  * Gets the current state of the @colorsel.
2668  *
2669  * Return value: %TRUE if the user is currently dragging a color around, and %FALSE
2670  * if the selection has stopped.
2671  **/
2672 gboolean
2673 gtk_color_selection_is_adjusting (GtkColorSelection *colorsel)
2674 {
2675   ColorSelectionPrivate *priv;
2676   
2677   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2678   
2679   priv = colorsel->private_data;
2680   
2681   return (gtk_hsv_is_adjusting (GTK_HSV (priv->triangle_colorsel)));
2682 }
2683
2684
2685 /**
2686  * gtk_color_selection_palette_from_string:
2687  * @str: a string encoding a color palette.
2688  * @colors: return location for allocated array of #GdkColor.
2689  * @n_colors: return location for length of array.
2690  * 
2691  * Parses a color palette string; the string is a colon-separated
2692  * list of color names readable by gdk_color_parse().
2693  * 
2694  * Return value: %TRUE if a palette was successfully parsed.
2695  **/
2696 gboolean
2697 gtk_color_selection_palette_from_string (const gchar *str,
2698                                          GdkColor   **colors,
2699                                          gint        *n_colors)
2700 {
2701   GdkColor *retval;
2702   gint count;
2703   gchar *p;
2704   gchar *start;
2705   gchar *copy;
2706   
2707   count = 0;
2708   retval = NULL;
2709   copy = g_strdup (str);
2710
2711   start = copy;
2712   p = copy;
2713   while (TRUE)
2714     {
2715       if (*p == ':' || *p == '\0')
2716         {
2717           gboolean done = TRUE;
2718
2719           if (start == p)
2720             {
2721               goto failed; /* empty entry */
2722             }
2723               
2724           if (*p)
2725             {
2726               *p = '\0';
2727               done = FALSE;
2728             }
2729
2730           retval = g_renew (GdkColor, retval, count + 1);
2731           if (!gdk_color_parse (start, retval + count))
2732             {
2733               goto failed;
2734             }
2735
2736           ++count;
2737
2738           if (done)
2739             break;
2740           else
2741             start = p + 1;
2742         }
2743
2744       ++p;
2745     }
2746
2747   g_free (copy);
2748   
2749   if (colors)
2750     *colors = retval;
2751   else
2752     g_free (retval);
2753
2754   if (n_colors)
2755     *n_colors = count;
2756
2757   return TRUE;
2758   
2759  failed:
2760   g_free (copy);
2761   g_free (retval);
2762
2763   if (colors)
2764     *colors = NULL;
2765   if (n_colors)
2766     *n_colors = 0;
2767
2768   return FALSE;
2769 }
2770
2771 /**
2772  * gtk_color_selection_palette_to_string:
2773  * @colors: an array of colors.
2774  * @n_colors: length of the array.
2775  * 
2776  * Encodes a palette as a string, useful for persistent storage.
2777  * 
2778  * Return value: allocated string encoding the palette.
2779  **/
2780 gchar*
2781 gtk_color_selection_palette_to_string (const GdkColor *colors,
2782                                        gint            n_colors)
2783 {
2784   gint i;
2785   gchar **strs = NULL;
2786   gchar *retval;
2787   
2788   if (n_colors == 0)
2789     return g_strdup ("");
2790
2791   strs = g_new0 (gchar*, n_colors + 1);
2792
2793   i = 0;
2794   while (i < n_colors)
2795     {
2796       gchar *ptr;
2797       
2798       strs[i] =
2799         g_strdup_printf ("#%2X%2X%2X",
2800                          colors[i].red / 256,
2801                          colors[i].green / 256,
2802                          colors[i].blue / 256);
2803
2804       for (ptr = strs[i]; *ptr; ptr++)
2805         if (*ptr == ' ')
2806           *ptr = '0';
2807       
2808       ++i;
2809     }
2810
2811   retval = g_strjoinv (":", strs);
2812
2813   g_strfreev (strs);
2814
2815   return retval;
2816 }
2817
2818 /**
2819  * gtk_color_selection_set_change_palette_hook:
2820  * @func: a function to call when the custom palette needs saving.
2821  * 
2822  * Installs a global function to be called whenever the user tries to
2823  * modify the palette in a color selection. This function should save
2824  * the new palette contents, and update the GtkSettings property
2825  * "gtk-color-palette" so all GtkColorSelection widgets will be modified.
2826  *
2827  * Return value: the previous change palette hook (that was replaced).
2828  *
2829  * Deprecated: 2.4: This function is deprecated in favor of 
2830  * gtk_color_selection_set_change_palette_with_screen_hook(), and does
2831  * not work in multihead environments.
2832  * 
2833  **/
2834 GtkColorSelectionChangePaletteFunc
2835 gtk_color_selection_set_change_palette_hook (GtkColorSelectionChangePaletteFunc func)
2836 {
2837   GtkColorSelectionChangePaletteFunc old;
2838
2839   old = noscreen_change_palette_hook;
2840
2841   noscreen_change_palette_hook = func;
2842
2843   return old;
2844 }
2845
2846 /**
2847  * gtk_color_selection_set_change_palette_with_screen_hook:
2848  * @func: a function to call when the custom palette needs saving.
2849  * 
2850  * Installs a global function to be called whenever the user tries to
2851  * modify the palette in a color selection. This function should save
2852  * the new palette contents, and update the GtkSettings property
2853  * "gtk-color-palette" so all GtkColorSelection widgets will be modified.
2854  * 
2855  * Return value: the previous change palette hook (that was replaced).
2856  *
2857  * Since: 2.2
2858  **/
2859 GtkColorSelectionChangePaletteWithScreenFunc
2860 gtk_color_selection_set_change_palette_with_screen_hook (GtkColorSelectionChangePaletteWithScreenFunc func)
2861 {
2862   GtkColorSelectionChangePaletteWithScreenFunc old;
2863
2864   old = change_palette_hook;
2865
2866   change_palette_hook = func;
2867
2868   return old;
2869 }
2870
2871 static void
2872 make_control_relations (AtkObject *atk_obj,
2873                         GtkWidget *widget)
2874 {
2875   AtkObject *obj;
2876
2877   obj = gtk_widget_get_accessible (widget);
2878   atk_object_add_relationship (atk_obj, ATK_RELATION_CONTROLLED_BY, obj);
2879   atk_object_add_relationship (obj, ATK_RELATION_CONTROLLER_FOR, atk_obj);
2880 }
2881
2882 static void
2883 make_all_relations (AtkObject *atk_obj,
2884                     ColorSelectionPrivate *priv)
2885 {
2886   make_control_relations (atk_obj, priv->hue_spinbutton);
2887   make_control_relations (atk_obj, priv->sat_spinbutton);
2888   make_control_relations (atk_obj, priv->val_spinbutton);
2889   make_control_relations (atk_obj, priv->red_spinbutton);
2890   make_control_relations (atk_obj, priv->green_spinbutton);
2891   make_control_relations (atk_obj, priv->blue_spinbutton);
2892 }
2893
2894 #define __GTK_COLOR_SELECTION_C__
2895 #include "gtkaliasdef.c"
2896