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