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