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