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