]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorsel.c
gtk/gtkcolorsel.c: Use accessor functions to access GtkWindow
[~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   GtkWindowGroup *group;
1900   ColorSelectionPrivate *priv = colorsel->private_data;
1901   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (button));
1902   GdkDevice *device, *keyb_device, *pointer_device;
1903   GdkCursor *picker_cursor;
1904   GdkGrabStatus grab_status;
1905   GdkWindow *window;
1906   GtkWidget *grab_widget, *toplevel;
1907
1908   guint32 time = gtk_get_current_event_time ();
1909
1910   device = gtk_get_current_event_device ();
1911
1912   if (device->source == GDK_SOURCE_KEYBOARD)
1913     {
1914       keyb_device = device;
1915       pointer_device = gdk_device_get_associated_device (device);
1916     }
1917   else
1918     {
1919       pointer_device = device;
1920       keyb_device = gdk_device_get_associated_device (device);
1921     }
1922
1923   if (priv->dropper_grab_widget == NULL)
1924     {
1925       grab_widget = gtk_window_new (GTK_WINDOW_POPUP);
1926       gtk_window_set_screen (GTK_WINDOW (grab_widget), screen);
1927       gtk_window_resize (GTK_WINDOW (grab_widget), 1, 1);
1928       gtk_window_move (GTK_WINDOW (grab_widget), -100, -100);
1929       gtk_widget_show (grab_widget);
1930
1931       gtk_widget_add_events (grab_widget,
1932                              GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
1933       
1934       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (colorsel));
1935   
1936       if (GTK_IS_WINDOW (toplevel))
1937         {
1938           group = gtk_window_get_group (GTK_WINDOW (toplevel));
1939           if (group)
1940             gtk_window_group_add_window (group,
1941                                          GTK_WINDOW (grab_widget));
1942         }
1943
1944       priv->dropper_grab_widget = grab_widget;
1945     }
1946
1947   window = gtk_widget_get_window (priv->dropper_grab_widget);
1948
1949   if (gdk_device_grab (keyb_device,
1950                        window,
1951                        GDK_OWNERSHIP_APPLICATION, FALSE,
1952                        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1953                        NULL, time) != GDK_GRAB_SUCCESS)
1954     return;
1955
1956   picker_cursor = make_picker_cursor (screen);
1957   grab_status = gdk_device_grab (pointer_device,
1958                                  window,
1959                                  GDK_OWNERSHIP_APPLICATION,
1960                                  FALSE,
1961                                  GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
1962                                  picker_cursor,
1963                                  time);
1964   gdk_cursor_unref (picker_cursor);
1965
1966   if (grab_status != GDK_GRAB_SUCCESS)
1967     {
1968       gdk_device_ungrab (keyb_device, time);
1969       return;
1970     }
1971
1972   gtk_device_grab_add (priv->dropper_grab_widget,
1973                        pointer_device,
1974                        TRUE);
1975
1976   priv->grab_time = time;
1977   priv->has_grab = TRUE;
1978   priv->keyboard_device = keyb_device;
1979   priv->pointer_device = pointer_device;
1980
1981   g_signal_connect (priv->dropper_grab_widget, "button-press-event",
1982                     G_CALLBACK (mouse_press), colorsel);
1983   g_signal_connect (priv->dropper_grab_widget, "key-press-event",
1984                     G_CALLBACK (key_press), colorsel);
1985 }
1986
1987 static void
1988 hex_changed (GtkWidget *hex_entry,
1989              gpointer   data)
1990 {
1991   GtkColorSelection *colorsel;
1992   ColorSelectionPrivate *priv;
1993   GdkColor color;
1994   gchar *text;
1995   
1996   colorsel = GTK_COLOR_SELECTION (data);
1997   priv = colorsel->private_data;
1998   
1999   if (priv->changing)
2000     return;
2001   
2002   text = gtk_editable_get_chars (GTK_EDITABLE (priv->hex_entry), 0, -1);
2003   if (gdk_color_parse (text, &color))
2004     {
2005       priv->color[COLORSEL_RED] = CLAMP (color.red/65535.0, 0.0, 1.0);
2006       priv->color[COLORSEL_GREEN] = CLAMP (color.green/65535.0, 0.0, 1.0);
2007       priv->color[COLORSEL_BLUE] = CLAMP (color.blue/65535.0, 0.0, 1.0);
2008       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2009                       priv->color[COLORSEL_GREEN],
2010                       priv->color[COLORSEL_BLUE],
2011                       &priv->color[COLORSEL_HUE],
2012                       &priv->color[COLORSEL_SATURATION],
2013                       &priv->color[COLORSEL_VALUE]);
2014       update_color (colorsel);
2015     }
2016   g_free (text);
2017 }
2018
2019 static gboolean
2020 hex_focus_out (GtkWidget     *hex_entry, 
2021                GdkEventFocus *event,
2022                gpointer       data)
2023 {
2024   hex_changed (hex_entry, data);
2025   
2026   return FALSE;
2027 }
2028
2029 static void
2030 hsv_changed (GtkWidget *hsv,
2031              gpointer   data)
2032 {
2033   GtkColorSelection *colorsel;
2034   ColorSelectionPrivate *priv;
2035   
2036   colorsel = GTK_COLOR_SELECTION (data);
2037   priv = colorsel->private_data;
2038   
2039   if (priv->changing)
2040     return;
2041   
2042   gtk_hsv_get_color (GTK_HSV (hsv),
2043                      &priv->color[COLORSEL_HUE],
2044                      &priv->color[COLORSEL_SATURATION],
2045                      &priv->color[COLORSEL_VALUE]);
2046   gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
2047                   priv->color[COLORSEL_SATURATION],
2048                   priv->color[COLORSEL_VALUE],
2049                   &priv->color[COLORSEL_RED],
2050                   &priv->color[COLORSEL_GREEN],
2051                   &priv->color[COLORSEL_BLUE]);
2052   update_color (colorsel);
2053 }
2054
2055 static void
2056 adjustment_changed (GtkAdjustment *adjustment,
2057                     gpointer       data)
2058 {
2059   GtkColorSelection *colorsel;
2060   ColorSelectionPrivate *priv;
2061   
2062   colorsel = GTK_COLOR_SELECTION (g_object_get_data (G_OBJECT (adjustment), "COLORSEL"));
2063   priv = colorsel->private_data;
2064   
2065   if (priv->changing)
2066     return;
2067   
2068   switch (GPOINTER_TO_INT (data))
2069     {
2070     case COLORSEL_SATURATION:
2071     case COLORSEL_VALUE:
2072       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 100;
2073       gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
2074                       priv->color[COLORSEL_SATURATION],
2075                       priv->color[COLORSEL_VALUE],
2076                       &priv->color[COLORSEL_RED],
2077                       &priv->color[COLORSEL_GREEN],
2078                       &priv->color[COLORSEL_BLUE]);
2079       break;
2080     case COLORSEL_HUE:
2081       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 360;
2082       gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
2083                       priv->color[COLORSEL_SATURATION],
2084                       priv->color[COLORSEL_VALUE],
2085                       &priv->color[COLORSEL_RED],
2086                       &priv->color[COLORSEL_GREEN],
2087                       &priv->color[COLORSEL_BLUE]);
2088       break;
2089     case COLORSEL_RED:
2090     case COLORSEL_GREEN:
2091     case COLORSEL_BLUE:
2092       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
2093       
2094       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2095                       priv->color[COLORSEL_GREEN],
2096                       priv->color[COLORSEL_BLUE],
2097                       &priv->color[COLORSEL_HUE],
2098                       &priv->color[COLORSEL_SATURATION],
2099                       &priv->color[COLORSEL_VALUE]);
2100       break;
2101     default:
2102       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
2103       break;
2104     }
2105   update_color (colorsel);
2106 }
2107
2108 static void 
2109 opacity_entry_changed (GtkWidget *opacity_entry,
2110                        gpointer   data)
2111 {
2112   GtkColorSelection *colorsel;
2113   ColorSelectionPrivate *priv;
2114   GtkAdjustment *adj;
2115   gchar *text;
2116   
2117   colorsel = GTK_COLOR_SELECTION (data);
2118   priv = colorsel->private_data;
2119   
2120   if (priv->changing)
2121     return;
2122   
2123   text = gtk_editable_get_chars (GTK_EDITABLE (priv->opacity_entry), 0, -1);
2124   adj = gtk_range_get_adjustment (GTK_RANGE (priv->opacity_slider));
2125   gtk_adjustment_set_value (adj, g_strtod (text, NULL)); 
2126   
2127   update_color (colorsel);
2128   
2129   g_free (text);
2130 }
2131
2132 static void
2133 make_label_spinbutton (GtkColorSelection *colorsel,
2134                        GtkWidget        **spinbutton,
2135                        gchar             *text,
2136                        GtkWidget         *table,
2137                        gint               i,
2138                        gint               j,
2139                        gint               channel_type,
2140                        const gchar       *tooltip)
2141 {
2142   GtkWidget *label;
2143   GtkAdjustment *adjust;
2144
2145   if (channel_type == COLORSEL_HUE)
2146     {
2147       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 360.0, 1.0, 1.0, 0.0));
2148     }
2149   else if (channel_type == COLORSEL_SATURATION ||
2150            channel_type == COLORSEL_VALUE)
2151     {
2152       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 100.0, 1.0, 1.0, 0.0));
2153     }
2154   else
2155     {
2156       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 1.0, 0.0));
2157     }
2158   g_object_set_data (G_OBJECT (adjust), I_("COLORSEL"), colorsel);
2159   *spinbutton = gtk_spin_button_new (adjust, 10.0, 0);
2160
2161   gtk_widget_set_tooltip_text (*spinbutton, tooltip);  
2162
2163   g_signal_connect (adjust, "value-changed",
2164                     G_CALLBACK (adjustment_changed),
2165                     GINT_TO_POINTER (channel_type));
2166   label = gtk_label_new_with_mnemonic (text);
2167   gtk_label_set_mnemonic_widget (GTK_LABEL (label), *spinbutton);
2168
2169   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
2170   gtk_table_attach_defaults (GTK_TABLE (table), label, i, i+1, j, j+1);
2171   gtk_table_attach_defaults (GTK_TABLE (table), *spinbutton, i+1, i+2, j, j+1);
2172 }
2173
2174 static void
2175 make_palette_frame (GtkColorSelection *colorsel,
2176                     GtkWidget         *table,
2177                     gint               i,
2178                     gint               j)
2179 {
2180   GtkWidget *frame;
2181   ColorSelectionPrivate *priv;
2182   
2183   priv = colorsel->private_data;
2184   frame = gtk_frame_new (NULL);
2185   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
2186   priv->custom_palette[i][j] = palette_new (colorsel);
2187   gtk_widget_set_size_request (priv->custom_palette[i][j], CUSTOM_PALETTE_ENTRY_WIDTH, CUSTOM_PALETTE_ENTRY_HEIGHT);
2188   gtk_container_add (GTK_CONTAINER (frame), priv->custom_palette[i][j]);
2189   gtk_table_attach_defaults (GTK_TABLE (table), frame, i, i+1, j, j+1);
2190 }
2191
2192 /* Set the palette entry [x][y] to be the currently selected one. */
2193 static void 
2194 set_selected_palette (GtkColorSelection *colorsel, int x, int y)
2195 {
2196   ColorSelectionPrivate *priv = colorsel->private_data; 
2197
2198   gtk_widget_grab_focus (priv->custom_palette[x][y]);
2199 }
2200
2201 static double
2202 scale_round (double val, double factor)
2203 {
2204   val = floor (val * factor + 0.5);
2205   val = MAX (val, 0);
2206   val = MIN (val, factor);
2207   return val;
2208 }
2209
2210 static void
2211 update_color (GtkColorSelection *colorsel)
2212 {
2213   ColorSelectionPrivate *priv = colorsel->private_data;
2214   gchar entryval[12];
2215   gchar opacity_text[32];
2216   gchar *ptr;
2217   
2218   priv->changing = TRUE;
2219   color_sample_update_samples (colorsel);
2220   
2221   gtk_hsv_set_color (GTK_HSV (priv->triangle_colorsel),
2222                      priv->color[COLORSEL_HUE],
2223                      priv->color[COLORSEL_SATURATION],
2224                      priv->color[COLORSEL_VALUE]);
2225   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2226                             (GTK_SPIN_BUTTON (priv->hue_spinbutton)),
2227                             scale_round (priv->color[COLORSEL_HUE], 360));
2228   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2229                             (GTK_SPIN_BUTTON (priv->sat_spinbutton)),
2230                             scale_round (priv->color[COLORSEL_SATURATION], 100));
2231   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2232                             (GTK_SPIN_BUTTON (priv->val_spinbutton)),
2233                             scale_round (priv->color[COLORSEL_VALUE], 100));
2234   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2235                             (GTK_SPIN_BUTTON (priv->red_spinbutton)),
2236                             scale_round (priv->color[COLORSEL_RED], 255));
2237   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2238                             (GTK_SPIN_BUTTON (priv->green_spinbutton)),
2239                             scale_round (priv->color[COLORSEL_GREEN], 255));
2240   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
2241                             (GTK_SPIN_BUTTON (priv->blue_spinbutton)),
2242                             scale_round (priv->color[COLORSEL_BLUE], 255));
2243   gtk_adjustment_set_value (gtk_range_get_adjustment
2244                             (GTK_RANGE (priv->opacity_slider)),
2245                             scale_round (priv->color[COLORSEL_OPACITY], 255));
2246   
2247   g_snprintf (opacity_text, 32, "%.0f", scale_round (priv->color[COLORSEL_OPACITY], 255));
2248   gtk_entry_set_text (GTK_ENTRY (priv->opacity_entry), opacity_text);
2249   
2250   g_snprintf (entryval, 11, "#%2X%2X%2X",
2251               (guint) (scale_round (priv->color[COLORSEL_RED], 255)),
2252               (guint) (scale_round (priv->color[COLORSEL_GREEN], 255)),
2253               (guint) (scale_round (priv->color[COLORSEL_BLUE], 255)));
2254   
2255   for (ptr = entryval; *ptr; ptr++)
2256     if (*ptr == ' ')
2257       *ptr = '0';
2258   gtk_entry_set_text (GTK_ENTRY (priv->hex_entry), entryval);
2259   priv->changing = FALSE;
2260
2261   g_object_ref (colorsel);
2262   
2263   g_signal_emit (colorsel, color_selection_signals[COLOR_CHANGED], 0);
2264   
2265   g_object_freeze_notify (G_OBJECT (colorsel));
2266   g_object_notify (G_OBJECT (colorsel), "current-color");
2267   g_object_notify (G_OBJECT (colorsel), "current-alpha");
2268   g_object_thaw_notify (G_OBJECT (colorsel));
2269   
2270   g_object_unref (colorsel);
2271 }
2272
2273 static void
2274 update_palette (GtkColorSelection *colorsel)
2275 {
2276   GdkColor *current_colors;
2277   gint i, j;
2278
2279   current_colors = get_current_colors (colorsel);
2280   
2281   for (i = 0; i < GTK_CUSTOM_PALETTE_HEIGHT; i++)
2282     {
2283       for (j = 0; j < GTK_CUSTOM_PALETTE_WIDTH; j++)
2284         {
2285           gint index;
2286
2287           index = i * GTK_CUSTOM_PALETTE_WIDTH + j;
2288           
2289           gtk_color_selection_set_palette_color (colorsel,
2290                                                  index,
2291                                                  &current_colors[index]);
2292         }
2293     }
2294
2295   g_free (current_colors);
2296 }
2297
2298 static void
2299 palette_change_notify_instance (GObject    *object,
2300                                 GParamSpec *pspec,
2301                                 gpointer    data)
2302 {
2303   update_palette (GTK_COLOR_SELECTION (data));
2304 }
2305
2306 static void
2307 default_noscreen_change_palette_func (const GdkColor *colors,
2308                                       gint            n_colors)
2309 {
2310   default_change_palette_func (gdk_screen_get_default (), colors, n_colors);
2311 }
2312
2313 static void
2314 default_change_palette_func (GdkScreen      *screen,
2315                              const GdkColor *colors,
2316                              gint            n_colors)
2317 {
2318   gchar *str;
2319   
2320   str = gtk_color_selection_palette_to_string (colors, n_colors);
2321
2322   gtk_settings_set_string_property (gtk_settings_get_for_screen (screen),
2323                                     "gtk-color-palette",
2324                                     str,
2325                                     "gtk_color_selection_palette_to_string");
2326
2327   g_free (str);
2328 }
2329
2330 /**
2331  * gtk_color_selection_new:
2332  * 
2333  * Creates a new GtkColorSelection.
2334  * 
2335  * Return value: a new #GtkColorSelection
2336  **/
2337 GtkWidget *
2338 gtk_color_selection_new (void)
2339 {
2340   GtkColorSelection *colorsel;
2341   ColorSelectionPrivate *priv;
2342   gdouble color[4];
2343   color[0] = 1.0;
2344   color[1] = 1.0;
2345   color[2] = 1.0;
2346   color[3] = 1.0;
2347   
2348   colorsel = g_object_new (GTK_TYPE_COLOR_SELECTION, NULL);
2349   priv = colorsel->private_data;
2350   set_color_internal (colorsel, color);
2351   gtk_color_selection_set_has_opacity_control (colorsel, TRUE);
2352   
2353   /* We want to make sure that default_set is FALSE */
2354   /* This way the user can still set it */
2355   priv->default_set = FALSE;
2356   priv->default_alpha_set = FALSE;
2357   
2358   return GTK_WIDGET (colorsel);
2359 }
2360
2361 /**
2362  * gtk_color_selection_get_has_opacity_control:
2363  * @colorsel: a #GtkColorSelection.
2364  * 
2365  * Determines whether the colorsel has an opacity control.
2366  * 
2367  * Return value: %TRUE if the @colorsel has an opacity control.  %FALSE if it does't.
2368  **/
2369 gboolean
2370 gtk_color_selection_get_has_opacity_control (GtkColorSelection *colorsel)
2371 {
2372   ColorSelectionPrivate *priv;
2373   
2374   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2375   
2376   priv = colorsel->private_data;
2377   
2378   return priv->has_opacity;
2379 }
2380
2381 /**
2382  * gtk_color_selection_set_has_opacity_control:
2383  * @colorsel: a #GtkColorSelection.
2384  * @has_opacity: %TRUE if @colorsel can set the opacity, %FALSE otherwise.
2385  *
2386  * Sets the @colorsel to use or not use opacity.
2387  * 
2388  **/
2389 void
2390 gtk_color_selection_set_has_opacity_control (GtkColorSelection *colorsel,
2391                                              gboolean           has_opacity)
2392 {
2393   ColorSelectionPrivate *priv;
2394   
2395   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2396   
2397   priv = colorsel->private_data;
2398   has_opacity = has_opacity != FALSE;
2399   
2400   if (priv->has_opacity != has_opacity)
2401     {
2402       priv->has_opacity = has_opacity;
2403       if (has_opacity)
2404         {
2405           gtk_widget_show (priv->opacity_slider);
2406           gtk_widget_show (priv->opacity_label);
2407           gtk_widget_show (priv->opacity_entry);
2408         }
2409       else
2410         {
2411           gtk_widget_hide (priv->opacity_slider);
2412           gtk_widget_hide (priv->opacity_label);
2413           gtk_widget_hide (priv->opacity_entry);
2414         }
2415       color_sample_update_samples (colorsel);
2416       
2417       g_object_notify (G_OBJECT (colorsel), "has-opacity-control");
2418     }
2419 }
2420
2421 /**
2422  * gtk_color_selection_get_has_palette:
2423  * @colorsel: a #GtkColorSelection.
2424  * 
2425  * Determines whether the color selector has a color palette.
2426  * 
2427  * Return value: %TRUE if the selector has a palette.  %FALSE if it hasn't.
2428  **/
2429 gboolean
2430 gtk_color_selection_get_has_palette (GtkColorSelection *colorsel)
2431 {
2432   ColorSelectionPrivate *priv;
2433   
2434   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2435   
2436   priv = colorsel->private_data;
2437   
2438   return priv->has_palette;
2439 }
2440
2441 /**
2442  * gtk_color_selection_set_has_palette:
2443  * @colorsel: a #GtkColorSelection.
2444  * @has_palette: %TRUE if palette is to be visible, %FALSE otherwise.
2445  *
2446  * Shows and hides the palette based upon the value of @has_palette.
2447  * 
2448  **/
2449 void
2450 gtk_color_selection_set_has_palette (GtkColorSelection *colorsel,
2451                                      gboolean           has_palette)
2452 {
2453   ColorSelectionPrivate *priv;
2454   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2455   
2456   priv = colorsel->private_data;
2457   has_palette = has_palette != FALSE;
2458   
2459   if (priv->has_palette != has_palette)
2460     {
2461       priv->has_palette = has_palette;
2462       if (has_palette)
2463         gtk_widget_show (priv->palette_frame);
2464       else
2465         gtk_widget_hide (priv->palette_frame);
2466
2467       update_tooltips (colorsel);
2468
2469       g_object_notify (G_OBJECT (colorsel), "has-palette");
2470     }
2471 }
2472
2473 /**
2474  * gtk_color_selection_set_current_color:
2475  * @colorsel: a #GtkColorSelection.
2476  * @color: A #GdkColor to set the current color with.
2477  *
2478  * Sets the current color to be @color.  The first time this is called, it will
2479  * also set the original color to be @color too.
2480  **/
2481 void
2482 gtk_color_selection_set_current_color (GtkColorSelection *colorsel,
2483                                        const GdkColor    *color)
2484 {
2485   ColorSelectionPrivate *priv;
2486   gint i;
2487   
2488   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2489   g_return_if_fail (color != NULL);
2490
2491   priv = colorsel->private_data;
2492   priv->changing = TRUE;
2493   priv->color[COLORSEL_RED] = SCALE (color->red);
2494   priv->color[COLORSEL_GREEN] = SCALE (color->green);
2495   priv->color[COLORSEL_BLUE] = SCALE (color->blue);
2496   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2497                   priv->color[COLORSEL_GREEN],
2498                   priv->color[COLORSEL_BLUE],
2499                   &priv->color[COLORSEL_HUE],
2500                   &priv->color[COLORSEL_SATURATION],
2501                   &priv->color[COLORSEL_VALUE]);
2502   if (priv->default_set == FALSE)
2503     {
2504       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2505         priv->old_color[i] = priv->color[i];
2506     }
2507   priv->default_set = TRUE;
2508   update_color (colorsel);
2509 }
2510
2511 /**
2512  * gtk_color_selection_set_current_alpha:
2513  * @colorsel: a #GtkColorSelection.
2514  * @alpha: an integer between 0 and 65535.
2515  *
2516  * Sets the current opacity to be @alpha.  The first time this is called, it will
2517  * also set the original opacity to be @alpha too.
2518  **/
2519 void
2520 gtk_color_selection_set_current_alpha (GtkColorSelection *colorsel,
2521                                        guint16            alpha)
2522 {
2523   ColorSelectionPrivate *priv;
2524   gint i;
2525   
2526   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2527   
2528   priv = colorsel->private_data;
2529   priv->changing = TRUE;
2530   priv->color[COLORSEL_OPACITY] = SCALE (alpha);
2531   if (priv->default_alpha_set == FALSE)
2532     {
2533       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2534         priv->old_color[i] = priv->color[i];
2535     }
2536   priv->default_alpha_set = TRUE;
2537   update_color (colorsel);
2538 }
2539
2540 /**
2541  * gtk_color_selection_get_current_color:
2542  * @colorsel: a #GtkColorSelection.
2543  * @color: (out): a #GdkColor to fill in with the current color.
2544  *
2545  * Sets @color to be the current color in the GtkColorSelection widget.
2546  **/
2547 void
2548 gtk_color_selection_get_current_color (GtkColorSelection *colorsel,
2549                                        GdkColor          *color)
2550 {
2551   ColorSelectionPrivate *priv;
2552   
2553   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2554   g_return_if_fail (color != NULL);
2555   
2556   priv = colorsel->private_data;
2557   color->red = UNSCALE (priv->color[COLORSEL_RED]);
2558   color->green = UNSCALE (priv->color[COLORSEL_GREEN]);
2559   color->blue = UNSCALE (priv->color[COLORSEL_BLUE]);
2560 }
2561
2562 /**
2563  * gtk_color_selection_get_current_alpha:
2564  * @colorsel: a #GtkColorSelection.
2565  *
2566  * Returns the current alpha value.
2567  *
2568  * Return value: an integer between 0 and 65535.
2569  **/
2570 guint16
2571 gtk_color_selection_get_current_alpha (GtkColorSelection *colorsel)
2572 {
2573   ColorSelectionPrivate *priv;
2574   
2575   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2576   
2577   priv = colorsel->private_data;
2578   return priv->has_opacity ? UNSCALE (priv->color[COLORSEL_OPACITY]) : 65535;
2579 }
2580
2581 /**
2582  * gtk_color_selection_set_previous_color:
2583  * @colorsel: a #GtkColorSelection.
2584  * @color: a #GdkColor to set the previous color with.
2585  *
2586  * Sets the 'previous' color to be @color.  This function should be called with
2587  * some hesitations, as it might seem confusing to have that color change.
2588  * Calling gtk_color_selection_set_current_color() will also set this color the first
2589  * time it is called.
2590  **/
2591 void
2592 gtk_color_selection_set_previous_color (GtkColorSelection *colorsel,
2593                                         const GdkColor    *color)
2594 {
2595   ColorSelectionPrivate *priv;
2596   
2597   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2598   g_return_if_fail (color != NULL);
2599   
2600   priv = colorsel->private_data;
2601   priv->changing = TRUE;
2602   priv->old_color[COLORSEL_RED] = SCALE (color->red);
2603   priv->old_color[COLORSEL_GREEN] = SCALE (color->green);
2604   priv->old_color[COLORSEL_BLUE] = SCALE (color->blue);
2605   gtk_rgb_to_hsv (priv->old_color[COLORSEL_RED],
2606                   priv->old_color[COLORSEL_GREEN],
2607                   priv->old_color[COLORSEL_BLUE],
2608                   &priv->old_color[COLORSEL_HUE],
2609                   &priv->old_color[COLORSEL_SATURATION],
2610                   &priv->old_color[COLORSEL_VALUE]);
2611   color_sample_update_samples (colorsel);
2612   priv->default_set = TRUE;
2613   priv->changing = FALSE;
2614 }
2615
2616 /**
2617  * gtk_color_selection_set_previous_alpha:
2618  * @colorsel: a #GtkColorSelection.
2619  * @alpha: an integer between 0 and 65535.
2620  *
2621  * Sets the 'previous' alpha to be @alpha.  This function should be called with
2622  * some hesitations, as it might seem confusing to have that alpha change.
2623  **/
2624 void
2625 gtk_color_selection_set_previous_alpha (GtkColorSelection *colorsel,
2626                                         guint16            alpha)
2627 {
2628   ColorSelectionPrivate *priv;
2629   
2630   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2631   
2632   priv = colorsel->private_data;
2633   priv->changing = TRUE;
2634   priv->old_color[COLORSEL_OPACITY] = SCALE (alpha);
2635   color_sample_update_samples (colorsel);
2636   priv->default_alpha_set = TRUE;
2637   priv->changing = FALSE;
2638 }
2639
2640
2641 /**
2642  * gtk_color_selection_get_previous_color:
2643  * @colorsel: a #GtkColorSelection.
2644  * @color: a #GdkColor to fill in with the original color value.
2645  *
2646  * Fills @color in with the original color value.
2647  **/
2648 void
2649 gtk_color_selection_get_previous_color (GtkColorSelection *colorsel,
2650                                         GdkColor           *color)
2651 {
2652   ColorSelectionPrivate *priv;
2653   
2654   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2655   g_return_if_fail (color != NULL);
2656   
2657   priv = colorsel->private_data;
2658   color->red = UNSCALE (priv->old_color[COLORSEL_RED]);
2659   color->green = UNSCALE (priv->old_color[COLORSEL_GREEN]);
2660   color->blue = UNSCALE (priv->old_color[COLORSEL_BLUE]);
2661 }
2662
2663 /**
2664  * gtk_color_selection_get_previous_alpha:
2665  * @colorsel: a #GtkColorSelection.
2666  *
2667  * Returns the previous alpha value.
2668  *
2669  * Return value: an integer between 0 and 65535.
2670  **/
2671 guint16
2672 gtk_color_selection_get_previous_alpha (GtkColorSelection *colorsel)
2673 {
2674   ColorSelectionPrivate *priv;
2675   
2676   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2677   
2678   priv = colorsel->private_data;
2679   return priv->has_opacity ? UNSCALE (priv->old_color[COLORSEL_OPACITY]) : 65535;
2680 }
2681
2682 /**
2683  * gtk_color_selection_set_palette_color:
2684  * @colorsel: a #GtkColorSelection.
2685  * @index: the color index of the palette.
2686  * @color: A #GdkColor to set the palette with.
2687  *
2688  * Sets the palette located at @index to have @color as its color.
2689  * 
2690  **/
2691 static void
2692 gtk_color_selection_set_palette_color (GtkColorSelection   *colorsel,
2693                                        gint                 index,
2694                                        GdkColor            *color)
2695 {
2696   ColorSelectionPrivate *priv;
2697   gint x, y;
2698   gdouble col[3];
2699   
2700   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2701   g_return_if_fail (index >= 0  && index < GTK_CUSTOM_PALETTE_WIDTH*GTK_CUSTOM_PALETTE_HEIGHT);
2702
2703   x = index % GTK_CUSTOM_PALETTE_WIDTH;
2704   y = index / GTK_CUSTOM_PALETTE_WIDTH;
2705   
2706   priv = colorsel->private_data;
2707   col[0] = SCALE (color->red);
2708   col[1] = SCALE (color->green);
2709   col[2] = SCALE (color->blue);
2710   
2711   palette_set_color (priv->custom_palette[x][y], colorsel, col);
2712 }
2713
2714 /**
2715  * gtk_color_selection_is_adjusting:
2716  * @colorsel: a #GtkColorSelection.
2717  *
2718  * Gets the current state of the @colorsel.
2719  *
2720  * Return value: %TRUE if the user is currently dragging a color around, and %FALSE
2721  * if the selection has stopped.
2722  **/
2723 gboolean
2724 gtk_color_selection_is_adjusting (GtkColorSelection *colorsel)
2725 {
2726   ColorSelectionPrivate *priv;
2727   
2728   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2729   
2730   priv = colorsel->private_data;
2731   
2732   return (gtk_hsv_is_adjusting (GTK_HSV (priv->triangle_colorsel)));
2733 }
2734
2735
2736 /**
2737  * gtk_color_selection_palette_from_string:
2738  * @str: a string encoding a color palette.
2739  * @colors: return location for allocated array of #GdkColor.
2740  * @n_colors: return location for length of array.
2741  * 
2742  * Parses a color palette string; the string is a colon-separated
2743  * list of color names readable by gdk_color_parse().
2744  * 
2745  * Return value: %TRUE if a palette was successfully parsed.
2746  **/
2747 gboolean
2748 gtk_color_selection_palette_from_string (const gchar *str,
2749                                          GdkColor   **colors,
2750                                          gint        *n_colors)
2751 {
2752   GdkColor *retval;
2753   gint count;
2754   gchar *p;
2755   gchar *start;
2756   gchar *copy;
2757   
2758   count = 0;
2759   retval = NULL;
2760   copy = g_strdup (str);
2761
2762   start = copy;
2763   p = copy;
2764   while (TRUE)
2765     {
2766       if (*p == ':' || *p == '\0')
2767         {
2768           gboolean done = TRUE;
2769
2770           if (start == p)
2771             {
2772               goto failed; /* empty entry */
2773             }
2774               
2775           if (*p)
2776             {
2777               *p = '\0';
2778               done = FALSE;
2779             }
2780
2781           retval = g_renew (GdkColor, retval, count + 1);
2782           if (!gdk_color_parse (start, retval + count))
2783             {
2784               goto failed;
2785             }
2786
2787           ++count;
2788
2789           if (done)
2790             break;
2791           else
2792             start = p + 1;
2793         }
2794
2795       ++p;
2796     }
2797
2798   g_free (copy);
2799   
2800   if (colors)
2801     *colors = retval;
2802   else
2803     g_free (retval);
2804
2805   if (n_colors)
2806     *n_colors = count;
2807
2808   return TRUE;
2809   
2810  failed:
2811   g_free (copy);
2812   g_free (retval);
2813
2814   if (colors)
2815     *colors = NULL;
2816   if (n_colors)
2817     *n_colors = 0;
2818
2819   return FALSE;
2820 }
2821
2822 /**
2823  * gtk_color_selection_palette_to_string:
2824  * @colors: an array of colors.
2825  * @n_colors: length of the array.
2826  * 
2827  * Encodes a palette as a string, useful for persistent storage.
2828  * 
2829  * Return value: allocated string encoding the palette.
2830  **/
2831 gchar*
2832 gtk_color_selection_palette_to_string (const GdkColor *colors,
2833                                        gint            n_colors)
2834 {
2835   gint i;
2836   gchar **strs = NULL;
2837   gchar *retval;
2838   
2839   if (n_colors == 0)
2840     return g_strdup ("");
2841
2842   strs = g_new0 (gchar*, n_colors + 1);
2843
2844   i = 0;
2845   while (i < n_colors)
2846     {
2847       gchar *ptr;
2848       
2849       strs[i] =
2850         g_strdup_printf ("#%2X%2X%2X",
2851                          colors[i].red / 256,
2852                          colors[i].green / 256,
2853                          colors[i].blue / 256);
2854
2855       for (ptr = strs[i]; *ptr; ptr++)
2856         if (*ptr == ' ')
2857           *ptr = '0';
2858       
2859       ++i;
2860     }
2861
2862   retval = g_strjoinv (":", strs);
2863
2864   g_strfreev (strs);
2865
2866   return retval;
2867 }
2868
2869 /**
2870  * gtk_color_selection_set_change_palette_with_screen_hook:
2871  * @func: a function to call when the custom palette needs saving.
2872  * 
2873  * Installs a global function to be called whenever the user tries to
2874  * modify the palette in a color selection. This function should save
2875  * the new palette contents, and update the GtkSettings property
2876  * "gtk-color-palette" so all GtkColorSelection widgets will be modified.
2877  * 
2878  * Return value: the previous change palette hook (that was replaced).
2879  *
2880  * Since: 2.2
2881  **/
2882 GtkColorSelectionChangePaletteWithScreenFunc
2883 gtk_color_selection_set_change_palette_with_screen_hook (GtkColorSelectionChangePaletteWithScreenFunc func)
2884 {
2885   GtkColorSelectionChangePaletteWithScreenFunc old;
2886
2887   old = change_palette_hook;
2888
2889   change_palette_hook = func;
2890
2891   return old;
2892 }
2893
2894 static void
2895 make_control_relations (AtkObject *atk_obj,
2896                         GtkWidget *widget)
2897 {
2898   AtkObject *obj;
2899
2900   obj = gtk_widget_get_accessible (widget);
2901   atk_object_add_relationship (atk_obj, ATK_RELATION_CONTROLLED_BY, obj);
2902   atk_object_add_relationship (obj, ATK_RELATION_CONTROLLER_FOR, atk_obj);
2903 }
2904
2905 static void
2906 make_all_relations (AtkObject *atk_obj,
2907                     ColorSelectionPrivate *priv)
2908 {
2909   make_control_relations (atk_obj, priv->hue_spinbutton);
2910   make_control_relations (atk_obj, priv->sat_spinbutton);
2911   make_control_relations (atk_obj, priv->val_spinbutton);
2912   make_control_relations (atk_obj, priv->red_spinbutton);
2913   make_control_relations (atk_obj, priv->green_spinbutton);
2914   make_control_relations (atk_obj, priv->blue_spinbutton);
2915 }