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