]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorsel.c
Update color on focus out of hex_entry. (#112665) (hex_focus_out): New
[~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 const gchar default_colors[] = "black:white:gray50:red:purple:blue:light blue:green:yellow:orange:lavender:brown:goldenrod4:dodger blue:pink:light green:gray10:gray30:gray75:gray90";
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_draw_rectangle (drawing_area->window,
612                       drawing_area->style->bg_gc[GTK_STATE_NORMAL],
613                       TRUE,
614                       area->x, area->y, area->width, area->height);
615   
616   if (GTK_WIDGET_HAS_FOCUS (drawing_area))
617     {
618       gint focus_width;
619       GdkGC *gc = get_focus_gc (drawing_area, &focus_width);
620       gdk_draw_rectangle (drawing_area->window,
621                           gc, FALSE, focus_width / 2, focus_width / 2,
622                           drawing_area->allocation.width - focus_width,
623                           drawing_area->allocation.height - focus_width);
624       g_object_unref (gc);
625     }
626 }
627
628 static GdkGC *
629 get_focus_gc (GtkWidget *drawing_area,
630               gint      *focus_width)
631 {
632   GdkGC *gc = gdk_gc_new (drawing_area->window);
633   gdouble color[4];
634   gint8 *dash_list;
635   
636   gtk_widget_style_get (drawing_area,
637                         "focus-line-width", focus_width,
638                         "focus-line-pattern", (gchar *)&dash_list,
639                         NULL);
640       
641   palette_get_color (drawing_area, color);
642       
643   if (INTENSITY (color[0], color[1], color[2]) > 0.5)
644     gdk_gc_copy (gc, drawing_area->style->black_gc);
645   else
646     gdk_gc_copy (gc, drawing_area->style->white_gc);
647
648   gdk_gc_set_line_attributes (gc, *focus_width,
649                               dash_list[0] ? GDK_LINE_ON_OFF_DASH : GDK_LINE_SOLID,
650                               GDK_CAP_BUTT, GDK_JOIN_MITER);
651
652   if (dash_list[0])
653     gdk_gc_set_dashes (gc, 0, dash_list, strlen ((char *)dash_list));
654
655   g_free (dash_list);
656   
657   return gc;
658 }
659
660 static void
661 palette_drag_begin (GtkWidget      *widget,
662                     GdkDragContext *context,
663                     gpointer        data)
664 {
665   gdouble colors[4];
666   
667   palette_get_color (widget, colors);
668   set_color_icon (context, colors);
669 }
670
671 static void
672 palette_drag_handle (GtkWidget        *widget,
673                      GdkDragContext   *context,
674                      GtkSelectionData *selection_data,
675                      guint             info,
676                      guint             time,
677                      gpointer          data)
678 {
679   guint16 vals[4];
680   gdouble colsrc[4];
681   
682   palette_get_color (widget, colsrc);
683   
684   vals[0] = colsrc[COLORSEL_RED] * 0xffff;
685   vals[1] = colsrc[COLORSEL_GREEN] * 0xffff;
686   vals[2] = colsrc[COLORSEL_BLUE] * 0xffff;
687   vals[3] = 0xffff;
688   
689   gtk_selection_data_set (selection_data,
690                           gdk_atom_intern ("application/x-color", FALSE),
691                           16, (guchar *)vals, 8);
692 }
693
694 static void
695 palette_drag_end (GtkWidget      *widget,
696                   GdkDragContext *context,
697                   gpointer        data)
698 {
699   g_object_set_data (G_OBJECT (widget), "gtk-color-selection-drag-window", NULL);
700 }
701
702 static GdkColor *
703 get_current_colors (GtkColorSelection *colorsel)
704 {
705   GtkSettings *settings;
706   GdkColor *colors = NULL;
707   gint n_colors = 0;
708   gchar *palette;
709
710   settings = gtk_widget_get_settings (GTK_WIDGET (colorsel));
711   g_object_get (settings,
712                 "gtk-color-palette", &palette,
713                 NULL);
714   
715   if (!gtk_color_selection_palette_from_string (palette, &colors, &n_colors))
716     {
717       gtk_color_selection_palette_from_string (default_colors, &colors, &n_colors);
718     }
719   else
720     {
721       /* If there are less colors provided than the number of slots in the
722        * color selection, we fill in the rest from the defaults.
723        */
724       if (n_colors < (GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT))
725         {
726           GdkColor *tmp_colors = colors;
727           gint tmp_n_colors = n_colors;
728           
729           gtk_color_selection_palette_from_string (default_colors, &colors, &n_colors);
730           memcpy (colors, tmp_colors, sizeof (GdkColor) * tmp_n_colors);
731
732           g_free (tmp_colors);
733         }
734     }
735
736   g_assert (n_colors >= GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT);
737   g_free (palette);
738   
739   return colors;
740 }
741
742 /* Changes the model color */
743 static void
744 palette_change_color (GtkWidget         *drawing_area,
745                       GtkColorSelection *colorsel,
746                       gdouble           *color)
747 {
748   gint x, y;
749   ColorSelectionPrivate *priv;
750   GdkColor gdk_color;
751   GdkColor *current_colors;
752   GdkScreen *screen;
753
754   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
755   g_return_if_fail (GTK_IS_DRAWING_AREA (drawing_area));
756   
757   priv = colorsel->private_data;
758   
759   gdk_color.red = UNSCALE (color[0]);
760   gdk_color.green = UNSCALE (color[1]);
761   gdk_color.blue = UNSCALE (color[2]);
762
763   x = 0;
764   y = 0;                        /* Quiet GCC */
765   while (x < GTK_CUSTOM_PALETTE_WIDTH)
766     {
767       y = 0;
768       while (y < GTK_CUSTOM_PALETTE_HEIGHT)
769         {
770           if (priv->custom_palette[x][y] == drawing_area)
771             goto out;
772           
773           ++y;
774         }
775
776       ++x;
777     }
778
779  out:
780   
781   g_assert (x < GTK_CUSTOM_PALETTE_WIDTH || y < GTK_CUSTOM_PALETTE_HEIGHT);
782
783   current_colors = get_current_colors (colorsel);
784   current_colors[y * GTK_CUSTOM_PALETTE_WIDTH + x] = gdk_color;
785
786   screen = gtk_widget_get_screen (GTK_WIDGET (colorsel));
787   if (change_palette_hook != default_change_palette_func)
788     (* change_palette_hook) (screen, current_colors, 
789                              GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT);
790   else if (noscreen_change_palette_hook != default_noscreen_change_palette_func)
791     {
792       if (screen != gdk_screen_get_default ())
793         g_warning ("gtk_color_selection_set_change_palette_hook used by widget is not on the default screen.");
794       (* noscreen_change_palette_hook) (current_colors, 
795                                         GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT);
796     }
797   else
798     (* change_palette_hook) (screen, current_colors, 
799                              GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT);
800
801   g_free (current_colors);
802 }
803
804 /* Changes the view color */
805 static void
806 palette_set_color (GtkWidget         *drawing_area,
807                    GtkColorSelection *colorsel,
808                    gdouble           *color)
809 {
810   gdouble *new_color = g_new (double, 4);
811   GdkColor gdk_color;
812   
813   gdk_color.red = UNSCALE (color[0]);
814   gdk_color.green = UNSCALE (color[1]);
815   gdk_color.blue = UNSCALE (color[2]);
816
817   gtk_widget_modify_bg (drawing_area, GTK_STATE_NORMAL, &gdk_color);
818   
819   if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drawing_area), "color_set")) == 0)
820     {
821       static const GtkTargetEntry targets[] = {
822         { "application/x-color", 0 }
823       };
824       gtk_drag_source_set (drawing_area,
825                            GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
826                            targets, 1,
827                            GDK_ACTION_COPY | GDK_ACTION_MOVE);
828       
829       g_signal_connect (drawing_area,
830                         "drag_begin",
831                         G_CALLBACK (palette_drag_begin),
832                         colorsel);
833       g_signal_connect (drawing_area,
834                         "drag_data_get",
835                         G_CALLBACK (palette_drag_handle),
836                         colorsel);
837       
838       g_object_set_data (G_OBJECT (drawing_area), "color_set",
839                          GINT_TO_POINTER (1));
840     }
841
842   new_color[0] = color[0];
843   new_color[1] = color[1];
844   new_color[2] = color[2];
845   new_color[3] = 1.0;
846   
847   g_object_set_data_full (G_OBJECT (drawing_area), "color_val", new_color, (GDestroyNotify)g_free);
848 }
849
850 static gboolean
851 palette_expose (GtkWidget      *drawing_area,
852                 GdkEventExpose *event,
853                 gpointer        data)
854 {
855   if (drawing_area->window == NULL)
856     return FALSE;
857   
858   palette_paint (drawing_area, &(event->area), data);
859   
860   return FALSE;
861 }
862
863 static void
864 popup_position_func (GtkMenu   *menu,
865                      gint      *x,
866                      gint      *y,
867                      gboolean  *push_in,
868                      gpointer   user_data)
869 {
870   GtkWidget *widget;
871   GtkRequisition req;      
872   gint root_x, root_y;
873   GdkScreen *screen;
874   
875   widget = GTK_WIDGET (user_data);
876   
877   g_return_if_fail (GTK_WIDGET_REALIZED (widget));
878
879   gdk_window_get_origin (widget->window, &root_x, &root_y);
880   
881   gtk_widget_size_request (GTK_WIDGET (menu), &req);
882
883   /* Put corner of menu centered on color cell */
884   *x = root_x + widget->allocation.width / 2;
885   *y = root_y + widget->allocation.height / 2;
886
887   /* Ensure sanity */
888   screen = gtk_widget_get_screen (widget);
889   *x = CLAMP (*x, 0, MAX (0, gdk_screen_get_width (screen) - req.width));
890   *y = CLAMP (*y, 0, MAX (0, gdk_screen_get_height (screen) - req.height));
891 }
892
893 static void
894 save_color_selected (GtkWidget *menuitem,
895                      gpointer   data)
896 {
897   GtkColorSelection *colorsel;
898   GtkWidget *drawing_area;
899   ColorSelectionPrivate *priv;
900
901   drawing_area = GTK_WIDGET (data);
902   
903   colorsel = GTK_COLOR_SELECTION (g_object_get_data (G_OBJECT (drawing_area),
904                                                      "gtk-color-sel"));
905
906   priv = colorsel->private_data;
907   
908   palette_change_color (drawing_area, colorsel, priv->color);  
909 }
910
911 static void
912 do_popup (GtkColorSelection *colorsel,
913           GtkWidget         *drawing_area,
914           guint32            timestamp)
915 {
916   GtkWidget *menu;
917   GtkWidget *mi;
918   
919   g_object_set_data (G_OBJECT (drawing_area),
920                      "gtk-color-sel",
921                      colorsel);
922   
923   menu = gtk_menu_new ();
924
925   mi = gtk_menu_item_new_with_mnemonic (_("_Save color here"));
926
927   g_signal_connect (mi, "activate",
928                     G_CALLBACK (save_color_selected),
929                     drawing_area);
930   
931   gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
932
933   gtk_widget_show_all (mi);
934
935   gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
936                   popup_position_func, drawing_area,
937                   3, timestamp);
938 }
939
940
941 static gboolean
942 palette_enter (GtkWidget        *drawing_area,
943                GdkEventCrossing *event,
944                gpointer        data)
945 {
946   g_object_set_data (G_OBJECT (drawing_area),
947                      "gtk-colorsel-have-pointer",
948                      GUINT_TO_POINTER (TRUE));
949
950   return FALSE;
951 }
952
953 static gboolean
954 palette_leave (GtkWidget        *drawing_area,
955                GdkEventCrossing *event,
956                gpointer        data)
957 {
958   g_object_set_data (G_OBJECT (drawing_area),
959                      "gtk-colorsel-have-pointer",
960                      NULL);
961
962   return FALSE;
963 }
964
965 static gboolean
966 palette_press (GtkWidget      *drawing_area,
967                GdkEventButton *event,
968                gpointer        data)
969 {
970   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
971
972   gtk_widget_grab_focus (drawing_area);
973   
974   if (event->button == 3 &&
975       event->type == GDK_BUTTON_PRESS)
976     {
977       do_popup (colorsel, drawing_area, event->time);
978       return TRUE;
979     }
980
981   return FALSE;
982 }
983
984 static gboolean
985 palette_release (GtkWidget      *drawing_area,
986                  GdkEventButton *event,
987                  gpointer        data)
988 {
989   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
990
991   gtk_widget_grab_focus (drawing_area);
992
993   if (event->button == 1 &&
994       g_object_get_data (G_OBJECT (drawing_area),
995                          "gtk-colorsel-have-pointer") != NULL)
996     {
997       if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (drawing_area), "color_set")) != 0)
998         {
999           gdouble color[4];
1000           palette_get_color (drawing_area, color);
1001           set_color_internal (colorsel, color);
1002         }
1003     }
1004
1005   return FALSE;
1006 }
1007
1008 static void
1009 palette_drop_handle (GtkWidget        *widget,
1010                      GdkDragContext   *context,
1011                      gint              x,
1012                      gint              y,
1013                      GtkSelectionData *selection_data,
1014                      guint             info,
1015                      guint             time,
1016                      gpointer          data)
1017 {
1018   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
1019   guint16 *vals;
1020   gdouble color[4];
1021   
1022   if (selection_data->length < 0)
1023     return;
1024   
1025   if ((selection_data->format != 16) ||
1026       (selection_data->length != 8))
1027     {
1028       g_warning ("Received invalid color data\n");
1029       return;
1030     }
1031   
1032   vals = (guint16 *)selection_data->data;
1033   
1034   color[0] = (gdouble)vals[0] / 0xffff;
1035   color[1] = (gdouble)vals[1] / 0xffff;
1036   color[2] = (gdouble)vals[2] / 0xffff;
1037   color[3] = (gdouble)vals[3] / 0xffff;
1038   palette_change_color (widget, colorsel, color);
1039   set_color_internal (colorsel, color);
1040 }
1041
1042 static gint
1043 palette_activate (GtkWidget   *widget,
1044                   GdkEventKey *event,
1045                   gpointer     data)
1046 {
1047   /* should have a drawing area subclass with an activate signal */
1048   if ((event->keyval == GDK_space) ||
1049       (event->keyval == GDK_Return) ||
1050       (event->keyval == GDK_KP_Enter) ||
1051       (event->keyval == GDK_KP_Space))
1052     {
1053       if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "color_set")) != 0)
1054         {
1055           gdouble color[4];
1056           palette_get_color (widget, color);
1057           set_color_internal (GTK_COLOR_SELECTION (data), color);
1058         }
1059       return TRUE;
1060     }
1061   
1062   return FALSE;
1063 }
1064
1065 static gboolean
1066 palette_popup (GtkWidget *widget,
1067                gpointer   data)
1068 {
1069   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (data);
1070
1071   do_popup (colorsel, widget, GDK_CURRENT_TIME);
1072   return TRUE;
1073 }
1074                
1075
1076 static GtkWidget*
1077 palette_new (GtkColorSelection *colorsel)
1078 {
1079   GtkWidget *retval;
1080   ColorSelectionPrivate *priv;
1081   
1082   static const GtkTargetEntry targets[] = {
1083     { "application/x-color", 0 }
1084   };
1085
1086   priv = colorsel->private_data;
1087   
1088   retval = gtk_drawing_area_new ();
1089
1090   GTK_WIDGET_SET_FLAGS (retval, GTK_CAN_FOCUS);
1091   
1092   g_object_set_data (G_OBJECT (retval), "color_set", GINT_TO_POINTER (0)); 
1093   gtk_widget_set_events (retval, GDK_BUTTON_PRESS_MASK
1094                          | GDK_BUTTON_RELEASE_MASK
1095                          | GDK_EXPOSURE_MASK
1096                          | GDK_ENTER_NOTIFY_MASK
1097                          | GDK_LEAVE_NOTIFY_MASK);
1098   
1099   g_signal_connect (retval, "expose_event",
1100                     G_CALLBACK (palette_expose), colorsel);
1101   g_signal_connect (retval, "button_press_event",
1102                     G_CALLBACK (palette_press), colorsel);
1103   g_signal_connect (retval, "button_release_event",
1104                     G_CALLBACK (palette_release), colorsel);
1105   g_signal_connect (retval, "enter_notify_event",
1106                     G_CALLBACK (palette_enter), colorsel);
1107   g_signal_connect (retval, "leave_notify_event",
1108                     G_CALLBACK (palette_leave), colorsel);
1109   g_signal_connect (retval, "key_press_event",
1110                     G_CALLBACK (palette_activate), colorsel);
1111   g_signal_connect (retval, "popup_menu",
1112                     G_CALLBACK (palette_popup), colorsel);
1113   
1114   gtk_drag_dest_set (retval,
1115                      GTK_DEST_DEFAULT_HIGHLIGHT |
1116                      GTK_DEST_DEFAULT_MOTION |
1117                      GTK_DEST_DEFAULT_DROP,
1118                      targets, 1,
1119                      GDK_ACTION_COPY);
1120   
1121   g_signal_connect (retval, "drag_end",
1122                     G_CALLBACK (palette_drag_end), NULL);
1123   g_signal_connect (retval, "drag_data_received",
1124                     G_CALLBACK (palette_drop_handle), colorsel);
1125
1126   gtk_tooltips_set_tip (priv->tooltips,
1127                         retval,
1128                         _("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.\""),
1129                         NULL);
1130   return retval;
1131 }
1132
1133
1134 /*
1135  *
1136  * The actual GtkColorSelection widget
1137  *
1138  */
1139
1140 static GdkCursor *
1141 make_picker_cursor (GdkScreen *screen)
1142 {
1143   GdkCursor *cursor;
1144   GdkColor bg = { 0, 0xffff, 0xffff, 0xffff };
1145   GdkColor fg = { 0, 0x0000, 0x0000, 0x0000 };
1146   GdkWindow *window = gdk_screen_get_root_window (screen);
1147   
1148   GdkPixmap *pixmap =
1149     gdk_bitmap_create_from_data (window, (gchar *) dropper_bits,
1150                                  DROPPER_WIDTH, DROPPER_HEIGHT);
1151
1152   GdkPixmap *mask =
1153     gdk_bitmap_create_from_data (window, (gchar *) dropper_mask,
1154                                  DROPPER_WIDTH, DROPPER_HEIGHT);
1155
1156   cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg,
1157                                        DROPPER_X_HOT, DROPPER_Y_HOT);
1158   
1159   g_object_unref (pixmap);
1160   g_object_unref (mask);
1161
1162   return cursor;
1163 }
1164
1165 static void
1166 grab_color_at_mouse (GdkScreen *screen,
1167                      gint       x_root,
1168                      gint       y_root,
1169                      gpointer   data)
1170 {
1171   GdkImage *image;
1172   guint32 pixel;
1173   GtkColorSelection *colorsel = data;
1174   ColorSelectionPrivate *priv;
1175   GdkColor color;
1176   GdkColormap *colormap = gdk_screen_get_system_colormap (screen);
1177   GdkWindow *root_window = gdk_screen_get_root_window (screen);
1178   
1179   priv = colorsel->private_data;
1180   
1181   image = gdk_drawable_get_image (root_window, x_root, y_root, 1, 1);
1182   pixel = gdk_image_get_pixel (image, 0, 0);
1183   g_object_unref (image);
1184
1185   gdk_colormap_query_color (colormap, pixel, &color);
1186   
1187   priv->color[COLORSEL_RED] = SCALE (color.red);
1188   priv->color[COLORSEL_GREEN] = SCALE (color.green);
1189   priv->color[COLORSEL_BLUE] = SCALE (color.blue);
1190   
1191   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1192                   priv->color[COLORSEL_GREEN],
1193                   priv->color[COLORSEL_BLUE],
1194                   &priv->color[COLORSEL_HUE],
1195                   &priv->color[COLORSEL_SATURATION],
1196                   &priv->color[COLORSEL_VALUE]);
1197
1198   update_color (colorsel);
1199 }
1200
1201 static void
1202 shutdown_eyedropper (GtkWidget *widget)
1203 {
1204   GtkColorSelection *colorsel;
1205   ColorSelectionPrivate *priv;
1206   GdkDisplay *display = gtk_widget_get_display (widget);
1207   guint32 time = gtk_get_current_event_time ();
1208
1209   colorsel = GTK_COLOR_SELECTION (widget);
1210   priv = colorsel->private_data;    
1211
1212   gdk_display_keyboard_ungrab (display, time);
1213   gdk_display_pointer_ungrab (display, time);
1214   gtk_grab_remove (priv->dropper_grab_widget);
1215 }
1216
1217 static void
1218 mouse_motion (GtkWidget      *invisible,
1219               GdkEventMotion *event,
1220               gpointer        data)
1221 {
1222   grab_color_at_mouse (gdk_event_get_screen ((GdkEvent *)event),
1223                        event->x_root, event->y_root, data); 
1224 }
1225
1226 static gboolean
1227 mouse_release (GtkWidget      *invisible,
1228                GdkEventButton *event,
1229                gpointer        data)
1230 {
1231   GtkColorSelection *colorsel = data;
1232   ColorSelectionPrivate *priv;
1233   priv = colorsel->private_data;  
1234
1235   if (event->button != 1)
1236     return FALSE;
1237
1238   grab_color_at_mouse (gdk_event_get_screen ((GdkEvent *)event),
1239                        event->x_root, event->y_root, data);
1240
1241   shutdown_eyedropper (GTK_WIDGET (data));
1242   
1243   g_signal_handlers_disconnect_by_func (invisible,
1244                                         mouse_motion,
1245                                         data);
1246   g_signal_handlers_disconnect_by_func (invisible,
1247                                         mouse_release,
1248                                         data);
1249
1250   return TRUE;
1251 }
1252
1253 /* Helper Functions */
1254
1255 static gboolean mouse_press (GtkWidget      *invisible,
1256                              GdkEventButton *event,
1257                              gpointer        data);
1258
1259 static gboolean
1260 key_press (GtkWidget   *invisible,
1261            GdkEventKey *event,
1262            gpointer     data)
1263 {  
1264   if (event->keyval == GDK_Escape)
1265     {
1266       shutdown_eyedropper (data);
1267
1268       g_signal_handlers_disconnect_by_func (invisible,
1269                                             mouse_press,
1270                                             data);
1271       g_signal_handlers_disconnect_by_func (invisible,
1272                                             key_press,
1273                                             data);
1274       
1275       return TRUE;
1276     }
1277
1278   return FALSE;
1279 }
1280
1281 static gboolean
1282 mouse_press (GtkWidget      *invisible,
1283              GdkEventButton *event,
1284              gpointer        data)
1285 {
1286   GtkColorSelection *colorsel = data;
1287   ColorSelectionPrivate *priv;
1288   priv = colorsel->private_data;
1289   
1290   if (event->type == GDK_BUTTON_PRESS &&
1291       event->button == 1)
1292     {
1293       g_signal_connect (invisible, "motion_notify_event",
1294                         G_CALLBACK (mouse_motion),
1295                         data);
1296       g_signal_connect (invisible, "button_release_event",
1297                         G_CALLBACK (mouse_release),
1298                         data);
1299       g_signal_handlers_disconnect_by_func (invisible,
1300                                             mouse_press,
1301                                             data);
1302       g_signal_handlers_disconnect_by_func (invisible,
1303                                             key_press,
1304                                             data);
1305       return TRUE;
1306     }
1307
1308   return FALSE;
1309 }
1310
1311 /* when the button is clicked */
1312 static void
1313 get_screen_color (GtkWidget *button)
1314 {
1315   GtkColorSelection *colorsel = g_object_get_data (G_OBJECT (button), "COLORSEL");
1316   ColorSelectionPrivate *priv = colorsel->private_data;
1317   GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (button));
1318   GdkCursor *picker_cursor;
1319   GdkGrabStatus grab_status;
1320   
1321   if (priv->dropper_grab_widget == NULL)
1322     {
1323       priv->dropper_grab_widget = gtk_invisible_new_for_screen (screen);
1324
1325       gtk_widget_add_events (priv->dropper_grab_widget,
1326                              GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
1327       
1328       gtk_widget_show (priv->dropper_grab_widget);
1329     }
1330
1331   if (gdk_keyboard_grab (priv->dropper_grab_widget->window,
1332                          FALSE,
1333                          gtk_get_current_event_time ()) != GDK_GRAB_SUCCESS)
1334     return;
1335   
1336   picker_cursor = make_picker_cursor (screen);
1337   grab_status = gdk_pointer_grab (priv->dropper_grab_widget->window,
1338                                   FALSE,
1339                                   GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
1340                                   NULL,
1341                                   picker_cursor,
1342                                   gtk_get_current_event_time ());
1343   gdk_cursor_unref (picker_cursor);
1344   
1345   if (grab_status != GDK_GRAB_SUCCESS)
1346     {
1347       gdk_display_keyboard_ungrab (gtk_widget_get_display (button), GDK_CURRENT_TIME);
1348       return;
1349     }
1350
1351   gtk_grab_add (priv->dropper_grab_widget);
1352   
1353   g_signal_connect (priv->dropper_grab_widget, "button_press_event",
1354                     G_CALLBACK (mouse_press), colorsel);
1355   g_signal_connect (priv->dropper_grab_widget, "key_press_event",
1356                     G_CALLBACK (key_press), colorsel);
1357 }
1358
1359 static void
1360 hex_changed (GtkWidget *hex_entry,
1361              gpointer   data)
1362 {
1363   GtkColorSelection *colorsel;
1364   ColorSelectionPrivate *priv;
1365   GdkColor color;
1366   gchar *text;
1367   
1368   colorsel = GTK_COLOR_SELECTION (data);
1369   priv = colorsel->private_data;
1370   
1371   if (priv->changing)
1372     return;
1373   
1374   text = gtk_editable_get_chars (GTK_EDITABLE (priv->hex_entry), 0, -1);
1375   if (gdk_color_parse (text, &color))
1376     {
1377       priv->color[COLORSEL_RED] = CLAMP (color.red/65535.0, 0.0, 1.0);
1378       priv->color[COLORSEL_GREEN] = CLAMP (color.green/65535.0, 0.0, 1.0);
1379       priv->color[COLORSEL_BLUE] = CLAMP (color.blue/65535.0, 0.0, 1.0);
1380       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1381                       priv->color[COLORSEL_GREEN],
1382                       priv->color[COLORSEL_BLUE],
1383                       &priv->color[COLORSEL_HUE],
1384                       &priv->color[COLORSEL_SATURATION],
1385                       &priv->color[COLORSEL_VALUE]);
1386       update_color (colorsel);
1387     }
1388   g_free (text);
1389 }
1390
1391 static gboolean
1392 hex_focus_out (GtkWidget     *hex_entry, 
1393                GdkEventFocus *event,
1394                gpointer       data)
1395 {
1396   hex_changed (hex_entry, data);
1397   
1398   return FALSE;
1399 }
1400
1401 static void
1402 hsv_changed (GtkWidget *hsv,
1403              gpointer   data)
1404 {
1405   GtkColorSelection *colorsel;
1406   ColorSelectionPrivate *priv;
1407   
1408   colorsel = GTK_COLOR_SELECTION (data);
1409   priv = colorsel->private_data;
1410   
1411   if (priv->changing)
1412     return;
1413   
1414   gtk_hsv_get_color (GTK_HSV (hsv),
1415                      &priv->color[COLORSEL_HUE],
1416                      &priv->color[COLORSEL_SATURATION],
1417                      &priv->color[COLORSEL_VALUE]);
1418   gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
1419                   priv->color[COLORSEL_SATURATION],
1420                   priv->color[COLORSEL_VALUE],
1421                   &priv->color[COLORSEL_RED],
1422                   &priv->color[COLORSEL_GREEN],
1423                   &priv->color[COLORSEL_BLUE]);
1424   update_color (colorsel);
1425 }
1426
1427 static void
1428 adjustment_changed (GtkAdjustment *adjustment,
1429                     gpointer       data)
1430 {
1431   GtkColorSelection *colorsel;
1432   ColorSelectionPrivate *priv;
1433   
1434   colorsel = GTK_COLOR_SELECTION (g_object_get_data (G_OBJECT (adjustment), "COLORSEL"));
1435   priv = colorsel->private_data;
1436   
1437   if (priv->changing)
1438     return;
1439   
1440   switch (GPOINTER_TO_INT (data))
1441     {
1442     case COLORSEL_SATURATION:
1443     case COLORSEL_VALUE:
1444       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
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_HUE:
1453       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 360;
1454       gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
1455                       priv->color[COLORSEL_SATURATION],
1456                       priv->color[COLORSEL_VALUE],
1457                       &priv->color[COLORSEL_RED],
1458                       &priv->color[COLORSEL_GREEN],
1459                       &priv->color[COLORSEL_BLUE]);
1460       break;
1461     case COLORSEL_RED:
1462     case COLORSEL_GREEN:
1463     case COLORSEL_BLUE:
1464       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
1465       
1466       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1467                       priv->color[COLORSEL_GREEN],
1468                       priv->color[COLORSEL_BLUE],
1469                       &priv->color[COLORSEL_HUE],
1470                       &priv->color[COLORSEL_SATURATION],
1471                       &priv->color[COLORSEL_VALUE]);
1472       break;
1473     default:
1474       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
1475       break;
1476     }
1477   update_color (colorsel);
1478 }
1479
1480 static void 
1481 opacity_entry_changed (GtkWidget *opacity_entry,
1482                        gpointer   data)
1483 {
1484   GtkColorSelection *colorsel;
1485   ColorSelectionPrivate *priv;
1486   GtkAdjustment *adj;
1487   gchar *text;
1488   
1489   colorsel = GTK_COLOR_SELECTION (data);
1490   priv = colorsel->private_data;
1491   
1492   if (priv->changing)
1493     return;
1494   
1495   text = gtk_editable_get_chars (GTK_EDITABLE (priv->opacity_entry), 0, -1);
1496   adj = gtk_range_get_adjustment (GTK_RANGE (priv->opacity_slider));
1497   gtk_adjustment_set_value (adj, g_strtod (text, NULL)); 
1498   
1499   update_color (colorsel);
1500   
1501   g_free (text);
1502 }
1503
1504 static void
1505 make_label_spinbutton (GtkColorSelection *colorsel,
1506                        GtkWidget        **spinbutton,
1507                        gchar             *text,
1508                        GtkWidget         *table,
1509                        gint               i,
1510                        gint               j,
1511                        gint               channel_type,
1512                        const gchar       *tooltip)
1513 {
1514   GtkWidget *label;
1515   GtkAdjustment *adjust;
1516   ColorSelectionPrivate *priv = colorsel->private_data;
1517   
1518   if (channel_type == COLORSEL_HUE)
1519     {
1520       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 360.0, 1.0, 1.0, 1.0));
1521     }
1522   else
1523     {
1524       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 1.0, 1.0));
1525     }
1526   g_object_set_data (G_OBJECT (adjust), "COLORSEL", colorsel);
1527   *spinbutton = gtk_spin_button_new (adjust, 10.0, 0);
1528
1529   gtk_tooltips_set_tip (priv->tooltips, *spinbutton, tooltip, NULL);  
1530
1531   g_signal_connect (adjust, "value_changed",
1532                     G_CALLBACK (adjustment_changed),
1533                     GINT_TO_POINTER (channel_type));
1534   label = gtk_label_new_with_mnemonic (text);
1535   gtk_label_set_mnemonic_widget (GTK_LABEL (label), *spinbutton);
1536
1537   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
1538   gtk_table_attach_defaults (GTK_TABLE (table), label, i, i+1, j, j+1);
1539   gtk_table_attach_defaults (GTK_TABLE (table), *spinbutton, i+1, i+2, j, j+1);
1540 }
1541
1542 static void
1543 make_palette_frame (GtkColorSelection *colorsel,
1544                     GtkWidget         *table,
1545                     gint               i,
1546                     gint               j)
1547 {
1548   GtkWidget *frame;
1549   ColorSelectionPrivate *priv;
1550   
1551   priv = colorsel->private_data;
1552   frame = gtk_frame_new (NULL);
1553   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
1554   priv->custom_palette[i][j] = palette_new (colorsel);
1555   gtk_widget_set_size_request (priv->custom_palette[i][j], CUSTOM_PALETTE_ENTRY_WIDTH, CUSTOM_PALETTE_ENTRY_HEIGHT);
1556   gtk_container_add (GTK_CONTAINER (frame), priv->custom_palette[i][j]);
1557   gtk_table_attach_defaults (GTK_TABLE (table), frame, i, i+1, j, j+1);
1558 }
1559
1560 /* Set the palette entry [x][y] to be the currently selected one. */
1561 static void 
1562 set_selected_palette (GtkColorSelection *colorsel, int x, int y)
1563 {
1564   ColorSelectionPrivate *priv = colorsel->private_data; 
1565
1566   gtk_widget_grab_focus (priv->custom_palette[x][y]);
1567 }
1568
1569 static double
1570 scale_round (double val, double factor)
1571 {
1572   val = floor (val * factor + 0.5);
1573   val = MAX (val, 0);
1574   val = MIN (val, factor);
1575   return val;
1576 }
1577
1578 static void
1579 update_color (GtkColorSelection *colorsel)
1580 {
1581   ColorSelectionPrivate *priv = colorsel->private_data;
1582   gchar entryval[12];
1583   gchar opacity_text[32];
1584   gchar *ptr;
1585   
1586   priv->changing = TRUE;
1587   color_sample_draw_samples (colorsel);
1588   
1589   gtk_hsv_set_color (GTK_HSV (priv->triangle_colorsel),
1590                      priv->color[COLORSEL_HUE],
1591                      priv->color[COLORSEL_SATURATION],
1592                      priv->color[COLORSEL_VALUE]);
1593   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1594                             (GTK_SPIN_BUTTON (priv->hue_spinbutton)),
1595                             scale_round (priv->color[COLORSEL_HUE], 360));
1596   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1597                             (GTK_SPIN_BUTTON (priv->sat_spinbutton)),
1598                             scale_round (priv->color[COLORSEL_SATURATION], 255));
1599   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1600                             (GTK_SPIN_BUTTON (priv->val_spinbutton)),
1601                             scale_round (priv->color[COLORSEL_VALUE], 255));
1602   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1603                             (GTK_SPIN_BUTTON (priv->red_spinbutton)),
1604                             scale_round (priv->color[COLORSEL_RED], 255));
1605   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1606                             (GTK_SPIN_BUTTON (priv->green_spinbutton)),
1607                             scale_round (priv->color[COLORSEL_GREEN], 255));
1608   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1609                             (GTK_SPIN_BUTTON (priv->blue_spinbutton)),
1610                             scale_round (priv->color[COLORSEL_BLUE], 255));
1611   gtk_adjustment_set_value (gtk_range_get_adjustment
1612                             (GTK_RANGE (priv->opacity_slider)),
1613                             scale_round (priv->color[COLORSEL_OPACITY], 255));
1614   
1615   g_snprintf (opacity_text, 32, "%.0f", scale_round (priv->color[COLORSEL_OPACITY], 255));
1616   gtk_entry_set_text (GTK_ENTRY (priv->opacity_entry), opacity_text);
1617   
1618   g_snprintf (entryval, 11, "#%2X%2X%2X",
1619               (guint) (scale_round (priv->color[COLORSEL_RED], 255)),
1620               (guint) (scale_round (priv->color[COLORSEL_GREEN], 255)),
1621               (guint) (scale_round (priv->color[COLORSEL_BLUE], 255)));
1622   
1623   for (ptr = entryval; *ptr; ptr++)
1624     if (*ptr == ' ')
1625       *ptr = '0';
1626   gtk_entry_set_text (GTK_ENTRY (priv->hex_entry), entryval);
1627   priv->changing = FALSE;
1628
1629   g_object_ref (colorsel);
1630   
1631   g_signal_emit (colorsel, color_selection_signals[COLOR_CHANGED], 0);
1632   
1633   g_object_freeze_notify (G_OBJECT (colorsel));
1634   g_object_notify (G_OBJECT (colorsel), "current_color");
1635   g_object_notify (G_OBJECT (colorsel), "current_alpha");
1636   g_object_thaw_notify (G_OBJECT (colorsel));
1637   
1638   g_object_unref (colorsel);
1639 }
1640
1641 static void
1642 update_palette (GtkColorSelection *colorsel)
1643 {
1644   GdkColor *current_colors;
1645   gint i, j;
1646
1647   current_colors = get_current_colors (colorsel);
1648   
1649   for (i = 0; i < GTK_CUSTOM_PALETTE_HEIGHT; i++)
1650     {
1651       for (j = 0; j < GTK_CUSTOM_PALETTE_WIDTH; j++)
1652         {
1653           gint index;
1654
1655           index = i * GTK_CUSTOM_PALETTE_WIDTH + j;
1656           
1657           gtk_color_selection_set_palette_color (colorsel,
1658                                                  index,
1659                                                  &current_colors[index]);
1660         }
1661     }
1662
1663   g_free (current_colors);
1664 }
1665
1666 static void
1667 palette_change_notify_instance (GObject    *object,
1668                                 GParamSpec *pspec,
1669                                 gpointer    data)
1670 {
1671   update_palette (GTK_COLOR_SELECTION (data));
1672 }
1673
1674 static void
1675 default_noscreen_change_palette_func (const GdkColor *colors,
1676                                       gint            n_colors)
1677 {
1678   default_change_palette_func (gdk_screen_get_default (), colors, n_colors);
1679 }
1680
1681 static void
1682 default_change_palette_func (GdkScreen      *screen,
1683                              const GdkColor *colors,
1684                              gint            n_colors)
1685 {
1686   gchar *str;
1687   
1688   str = gtk_color_selection_palette_to_string (colors, n_colors);
1689
1690   gtk_settings_set_string_property (gtk_settings_get_for_screen (screen),
1691                                     "gtk-color-palette",
1692                                     str,
1693                                     "gtk_color_selection_palette_to_string");
1694
1695   g_free (str);
1696 }
1697
1698 GType
1699 gtk_color_selection_get_type (void)
1700 {
1701   static GType color_selection_type = 0;
1702   
1703   if (!color_selection_type)
1704     {
1705       static const GTypeInfo color_selection_info =
1706       {
1707         sizeof (GtkColorSelectionClass),
1708         NULL,           /* base_init */
1709         NULL,           /* base_finalize */
1710         (GClassInitFunc) gtk_color_selection_class_init,
1711         NULL,           /* class_finalize */
1712         NULL,           /* class_data */
1713         sizeof (GtkColorSelection),
1714         0,              /* n_preallocs */
1715         (GInstanceInitFunc) gtk_color_selection_init,
1716       };
1717       
1718       color_selection_type =
1719         g_type_register_static (GTK_TYPE_VBOX, "GtkColorSelection",
1720                                 &color_selection_info, 0);
1721     }
1722   
1723   return color_selection_type;
1724 }
1725
1726 static void
1727 gtk_color_selection_class_init (GtkColorSelectionClass *klass)
1728 {
1729   GObjectClass *gobject_class;
1730   GtkObjectClass *object_class;
1731   GtkWidgetClass *widget_class;
1732   
1733   gobject_class = G_OBJECT_CLASS (klass);
1734   object_class = GTK_OBJECT_CLASS (klass);
1735   widget_class = GTK_WIDGET_CLASS (klass);
1736   
1737   parent_class = g_type_class_peek_parent (klass);
1738   
1739   object_class->destroy = gtk_color_selection_destroy;
1740   gobject_class->finalize = gtk_color_selection_finalize;
1741   
1742   gobject_class->set_property = gtk_color_selection_set_property;
1743   gobject_class->get_property = gtk_color_selection_get_property;
1744
1745   widget_class->realize = gtk_color_selection_realize;
1746   widget_class->unrealize = gtk_color_selection_unrealize;
1747   widget_class->show_all = gtk_color_selection_show_all;
1748   
1749   g_object_class_install_property (gobject_class,
1750                                    PROP_HAS_OPACITY_CONTROL,
1751                                    g_param_spec_boolean ("has_opacity_control",
1752                                                          _("Has Opacity Control"),
1753                                                          _("Whether the color selector should allow setting opacity"),
1754                                                          FALSE,
1755                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
1756   g_object_class_install_property (gobject_class,
1757                                    PROP_HAS_PALETTE,
1758                                    g_param_spec_boolean ("has_palette",
1759                                                          _("Has palette"),
1760                                                          _("Whether a palette should be used"),
1761                                                          FALSE,
1762                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
1763   g_object_class_install_property (gobject_class,
1764                                    PROP_CURRENT_COLOR,
1765                                    g_param_spec_boxed ("current_color",
1766                                                        _("Current Color"),
1767                                                        _("The current color"),
1768                                                        GDK_TYPE_COLOR,
1769                                                        G_PARAM_READABLE | G_PARAM_WRITABLE));
1770   g_object_class_install_property (gobject_class,
1771                                    PROP_CURRENT_ALPHA,
1772                                    g_param_spec_uint ("current_alpha",
1773                                                       _("Current Alpha"),
1774                                                       _("The current opacity value (0 fully transparent, 65535 fully opaque)"),
1775                                                       0, 65535, 65535,
1776                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
1777   
1778   color_selection_signals[COLOR_CHANGED] =
1779     g_signal_new ("color_changed",
1780                   G_OBJECT_CLASS_TYPE (object_class),
1781                   G_SIGNAL_RUN_FIRST,
1782                   G_STRUCT_OFFSET (GtkColorSelectionClass, color_changed),
1783                   NULL, NULL,
1784                   _gtk_marshal_VOID__VOID,
1785                   G_TYPE_NONE, 0);
1786
1787   gtk_settings_install_property (g_param_spec_string ("gtk-color-palette",
1788                                                       _("Custom palette"),
1789                                                       _("Palette to use in the color selector"),
1790                                                       default_colors,
1791                                                       G_PARAM_READWRITE));
1792 }
1793
1794 /* widget functions */
1795
1796 static void
1797 gtk_color_selection_init (GtkColorSelection *colorsel)
1798 {
1799   GtkWidget *top_hbox;
1800   GtkWidget *top_right_vbox;
1801   GtkWidget *table, *label, *hbox, *frame, *vbox, *button;
1802   GtkAdjustment *adjust;
1803   GtkWidget *picker_image;
1804   gint i, j;
1805   ColorSelectionPrivate *priv;
1806   
1807   gtk_widget_push_composite_child ();
1808
1809   priv = colorsel->private_data = g_new0 (ColorSelectionPrivate, 1);
1810   priv->changing = FALSE;
1811   priv->default_set = FALSE;
1812   priv->default_alpha_set = FALSE;
1813   
1814   priv->tooltips = gtk_tooltips_new ();
1815   g_object_ref (priv->tooltips);
1816   gtk_object_sink (GTK_OBJECT (priv->tooltips));
1817   
1818   gtk_box_set_spacing (GTK_BOX (colorsel), 4);
1819   top_hbox = gtk_hbox_new (FALSE, 8);
1820   gtk_box_pack_start (GTK_BOX (colorsel), top_hbox, FALSE, FALSE, 0);
1821   
1822   vbox = gtk_vbox_new (FALSE, 4);
1823   priv->triangle_colorsel = gtk_hsv_new ();
1824   g_signal_connect (priv->triangle_colorsel, "changed",
1825                     G_CALLBACK (hsv_changed), colorsel);
1826   gtk_hsv_set_metrics (GTK_HSV (priv->triangle_colorsel), 174, 15);
1827   gtk_box_pack_start (GTK_BOX (top_hbox), vbox, FALSE, FALSE, 0);
1828   gtk_box_pack_start (GTK_BOX (vbox), priv->triangle_colorsel, FALSE, FALSE, 0);
1829   gtk_tooltips_set_tip (priv->tooltips, priv->triangle_colorsel,
1830                         _("Select the color you want from the outer ring. Select the darkness or lightness of that color using the inner triangle."), NULL);
1831   
1832   hbox = gtk_hbox_new (FALSE, 4);
1833   gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1834   
1835   frame = gtk_frame_new (NULL);
1836   gtk_widget_set_size_request (frame, -1, 30);
1837   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
1838   color_sample_new (colorsel);
1839   gtk_container_add (GTK_CONTAINER (frame), priv->sample_area);
1840   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
1841   
1842   button = gtk_button_new ();
1843
1844   gtk_widget_set_events (button, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
1845   g_object_set_data (G_OBJECT (button), "COLORSEL", colorsel); 
1846   g_signal_connect (button, "clicked",
1847                     G_CALLBACK (get_screen_color), NULL);
1848   picker_image = gtk_image_new_from_stock (GTK_STOCK_COLOR_PICKER, GTK_ICON_SIZE_BUTTON);
1849   gtk_container_add (GTK_CONTAINER (button), picker_image);
1850   gtk_widget_show (GTK_WIDGET (picker_image));
1851   gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1852
1853   gtk_tooltips_set_tip (priv->tooltips,
1854                         button,
1855                         _("Click the eyedropper, then click a color anywhere on your screen to select that color."), NULL);
1856   
1857   top_right_vbox = gtk_vbox_new (FALSE, 4);
1858   gtk_box_pack_start (GTK_BOX (top_hbox), top_right_vbox, FALSE, FALSE, 0);
1859   table = gtk_table_new (8, 6, FALSE);
1860   gtk_box_pack_start (GTK_BOX (top_right_vbox), table, FALSE, FALSE, 0);
1861   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
1862   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
1863   
1864   make_label_spinbutton (colorsel, &priv->hue_spinbutton, _("_Hue:"), table, 0, 0, COLORSEL_HUE,
1865                          _("Position on the color wheel."));
1866   make_label_spinbutton (colorsel, &priv->sat_spinbutton, _("_Saturation:"), table, 0, 1, COLORSEL_SATURATION,
1867                          _("\"Deepness\" of the color."));
1868   make_label_spinbutton (colorsel, &priv->val_spinbutton, _("_Value:"), table, 0, 2, COLORSEL_VALUE,
1869                          _("Brightness of the color."));
1870   make_label_spinbutton (colorsel, &priv->red_spinbutton, _("_Red:"), table, 6, 0, COLORSEL_RED,
1871                          _("Amount of red light in the color."));
1872   make_label_spinbutton (colorsel, &priv->green_spinbutton, _("_Green:"), table, 6, 1, COLORSEL_GREEN,
1873                          _("Amount of green light in the color."));
1874   make_label_spinbutton (colorsel, &priv->blue_spinbutton, _("_Blue:"), table, 6, 2, COLORSEL_BLUE,
1875                          _("Amount of blue light in the color."));
1876   gtk_table_attach_defaults (GTK_TABLE (table), gtk_hseparator_new (), 0, 8, 3, 4); 
1877   
1878   priv->opacity_label = gtk_label_new_with_mnemonic (_("_Opacity:")); 
1879   gtk_misc_set_alignment (GTK_MISC (priv->opacity_label), 1.0, 0.5); 
1880   gtk_table_attach_defaults (GTK_TABLE (table), priv->opacity_label, 0, 1, 4, 5); 
1881   adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 1.0, 0.0)); 
1882   g_object_set_data (G_OBJECT (adjust), "COLORSEL", colorsel); 
1883   priv->opacity_slider = gtk_hscale_new (adjust);
1884   gtk_tooltips_set_tip (priv->tooltips,
1885                         priv->opacity_slider,
1886                         _("Transparency of the currently-selected color."), NULL);
1887   gtk_label_set_mnemonic_widget (GTK_LABEL (priv->opacity_label),
1888                                  priv->opacity_slider);
1889   gtk_scale_set_draw_value (GTK_SCALE (priv->opacity_slider), FALSE);
1890   g_signal_connect (adjust, "value_changed",
1891                     G_CALLBACK (adjustment_changed),
1892                     GINT_TO_POINTER (COLORSEL_OPACITY));
1893   gtk_table_attach_defaults (GTK_TABLE (table), priv->opacity_slider, 1, 7, 4, 5); 
1894   priv->opacity_entry = gtk_entry_new (); 
1895   gtk_widget_set_size_request (priv->opacity_entry, 40, -1); 
1896
1897   g_signal_connect (priv->opacity_entry, "activate",
1898                     G_CALLBACK (opacity_entry_changed), colorsel);
1899   gtk_table_attach_defaults (GTK_TABLE (table), priv->opacity_entry, 7, 8, 4, 5);
1900   
1901   label = gtk_label_new_with_mnemonic (_("Color _Name:"));
1902   gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 5, 6);
1903   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
1904   priv->hex_entry = gtk_entry_new ();
1905
1906   gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->hex_entry);
1907
1908   g_signal_connect (priv->hex_entry, "activate",
1909                     G_CALLBACK (hex_changed), colorsel);
1910
1911   g_signal_connect (priv->hex_entry, "focus_out_event",
1912                     G_CALLBACK (hex_focus_out), colorsel);
1913
1914   gtk_tooltips_set_tip (priv->tooltips,
1915                         priv->hex_entry,
1916                         _("You can enter an HTML-style hexadecimal color value, or simply a color name such as 'orange' in this entry."), NULL);
1917   
1918   gtk_widget_set_size_request (priv->hex_entry, 75, -1);  
1919   gtk_table_set_col_spacing (GTK_TABLE (table), 3, 15);
1920   gtk_table_attach_defaults (GTK_TABLE (table), priv->hex_entry, 1, 5, 5, 6);
1921   
1922   /* Set up the palette */
1923   table = gtk_table_new (GTK_CUSTOM_PALETTE_HEIGHT, GTK_CUSTOM_PALETTE_WIDTH, TRUE);
1924   gtk_table_set_row_spacings (GTK_TABLE (table), 1);
1925   gtk_table_set_col_spacings (GTK_TABLE (table), 1);
1926   for (i = 0; i < GTK_CUSTOM_PALETTE_WIDTH; i++)
1927     {
1928       for (j = 0; j < GTK_CUSTOM_PALETTE_HEIGHT; j++)
1929         {
1930           make_palette_frame (colorsel, table, i, j);
1931         }
1932     }
1933   set_selected_palette (colorsel, 0, 0);
1934   priv->palette_frame = gtk_frame_new (NULL);
1935   label = gtk_label_new_with_mnemonic (_("_Palette"));
1936   gtk_frame_set_label_widget (GTK_FRAME (priv->palette_frame), label);
1937
1938   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
1939                                  priv->custom_palette[0][0]);
1940   
1941   gtk_box_pack_end (GTK_BOX (top_right_vbox), priv->palette_frame, FALSE, FALSE, 0);
1942   vbox = gtk_vbox_new (FALSE, 4);
1943   gtk_container_add (GTK_CONTAINER (priv->palette_frame), vbox);
1944   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
1945
1946   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
1947   
1948   gtk_widget_show_all (top_hbox);
1949
1950   /* hide unused stuff */
1951   
1952   if (priv->has_opacity == FALSE)
1953     {
1954       gtk_widget_hide (priv->opacity_label);
1955       gtk_widget_hide (priv->opacity_slider);
1956       gtk_widget_hide (priv->opacity_entry);
1957     }
1958   
1959   if (priv->has_palette == FALSE)
1960     {
1961       gtk_widget_hide (priv->palette_frame);
1962     }
1963
1964   gtk_widget_pop_composite_child ();
1965 }
1966
1967 static void
1968 gtk_color_selection_destroy (GtkObject *object)
1969 {
1970   GtkColorSelection *cselection = GTK_COLOR_SELECTION (object);
1971   ColorSelectionPrivate *priv = cselection->private_data;
1972
1973   if (priv->dropper_grab_widget)
1974     {
1975       gtk_widget_destroy (priv->dropper_grab_widget);
1976       priv->dropper_grab_widget = NULL;
1977     }
1978
1979   if (priv->tooltips)
1980     {
1981       g_object_unref (priv->tooltips);
1982       priv->tooltips = NULL;
1983     }
1984   
1985   GTK_OBJECT_CLASS (parent_class)->destroy (object);
1986 }
1987
1988 static void
1989 gtk_color_selection_finalize (GObject *object)
1990 {
1991   GtkColorSelection *cselection = GTK_COLOR_SELECTION (object);
1992   
1993   if (cselection->private_data)
1994     {
1995       g_free (cselection->private_data);
1996       cselection->private_data = NULL;
1997     }
1998   
1999   G_OBJECT_CLASS (parent_class)->finalize (object);
2000 }
2001
2002 static void
2003 gtk_color_selection_realize (GtkWidget *widget)
2004 {
2005   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (widget);
2006   ColorSelectionPrivate *priv = colorsel->private_data;
2007   GtkSettings *settings = gtk_widget_get_settings (widget);
2008
2009   priv->settings_connection =  g_signal_connect (settings,
2010                                                  "notify::gtk-color-palette",
2011                                                  G_CALLBACK (palette_change_notify_instance),
2012                                                  widget);
2013   update_palette (colorsel);
2014
2015   GTK_WIDGET_CLASS (parent_class)->realize (widget);
2016 }
2017
2018 static void
2019 gtk_color_selection_unrealize (GtkWidget *widget)
2020 {
2021   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (widget);
2022   ColorSelectionPrivate *priv = colorsel->private_data;
2023   GtkSettings *settings = gtk_widget_get_settings (widget);
2024
2025   g_signal_handler_disconnect (settings, priv->settings_connection);
2026
2027   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
2028 }
2029
2030 /* We override show-all since we have internal widgets that
2031  * shouldn't be shown when you call show_all(), like the
2032  * palette and opacity sliders.
2033  */
2034 static void
2035 gtk_color_selection_show_all (GtkWidget *widget)
2036 {
2037   gtk_widget_show (widget);
2038 }
2039
2040 /**
2041  * gtk_color_selection_new:
2042  * 
2043  * Creates a new GtkColorSelection.
2044  * 
2045  * Return value: a new #GtkColorSelection
2046  **/
2047 GtkWidget *
2048 gtk_color_selection_new (void)
2049 {
2050   GtkColorSelection *colorsel;
2051   ColorSelectionPrivate *priv;
2052   gdouble color[4];
2053   color[0] = 1.0;
2054   color[1] = 1.0;
2055   color[2] = 1.0;
2056   color[3] = 1.0;
2057   
2058   colorsel = g_object_new (GTK_TYPE_COLOR_SELECTION, NULL);
2059   priv = colorsel->private_data;
2060   set_color_internal (colorsel, color);
2061   gtk_color_selection_set_has_opacity_control (colorsel, TRUE);
2062   
2063   /* We want to make sure that default_set is FALSE */
2064   /* This way the user can still set it */
2065   priv->default_set = FALSE;
2066   priv->default_alpha_set = FALSE;
2067   
2068   return GTK_WIDGET (colorsel);
2069 }
2070
2071
2072 void
2073 gtk_color_selection_set_update_policy (GtkColorSelection *colorsel,
2074                                        GtkUpdateType      policy)
2075 {
2076   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2077 }
2078
2079 /**
2080  * gtk_color_selection_get_has_opacity_control:
2081  * @colorsel: a #GtkColorSelection.
2082  * 
2083  * Determines whether the colorsel has an opacity control.
2084  * 
2085  * Return value: %TRUE if the @colorsel has an opacity control.  %FALSE if it does't.
2086  **/
2087 gboolean
2088 gtk_color_selection_get_has_opacity_control (GtkColorSelection *colorsel)
2089 {
2090   ColorSelectionPrivate *priv;
2091   
2092   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2093   
2094   priv = colorsel->private_data;
2095   
2096   return priv->has_opacity;
2097 }
2098
2099 /**
2100  * gtk_color_selection_set_has_opacity_control:
2101  * @colorsel: a #GtkColorSelection.
2102  * @has_opacity: %TRUE if @colorsel can set the opacity, %FALSE otherwise.
2103  *
2104  * Sets the @colorsel to use or not use opacity.
2105  * 
2106  **/
2107 void
2108 gtk_color_selection_set_has_opacity_control (GtkColorSelection *colorsel,
2109                                              gboolean           has_opacity)
2110 {
2111   ColorSelectionPrivate *priv;
2112   
2113   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2114   
2115   priv = colorsel->private_data;
2116   has_opacity = has_opacity != FALSE;
2117   
2118   if (priv->has_opacity != has_opacity)
2119     {
2120       priv->has_opacity = has_opacity;
2121       if (has_opacity)
2122         {
2123           gtk_widget_show (priv->opacity_slider);
2124           gtk_widget_show (priv->opacity_label);
2125           gtk_widget_show (priv->opacity_entry);
2126         }
2127       else
2128         {
2129           gtk_widget_hide (priv->opacity_slider);
2130           gtk_widget_hide (priv->opacity_label);
2131           gtk_widget_hide (priv->opacity_entry);
2132         }
2133       color_sample_draw_samples (colorsel);
2134       
2135       g_object_notify (G_OBJECT (colorsel), "has_opacity_control");
2136     }
2137 }
2138
2139 /**
2140  * gtk_color_selection_get_has_palette:
2141  * @colorsel: a #GtkColorSelection.
2142  * 
2143  * Determines whether the color selector has a color palette.
2144  * 
2145  * Return value: %TRUE if the selector has a palette.  %FALSE if it hasn't.
2146  **/
2147 gboolean
2148 gtk_color_selection_get_has_palette (GtkColorSelection *colorsel)
2149 {
2150   ColorSelectionPrivate *priv;
2151   
2152   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2153   
2154   priv = colorsel->private_data;
2155   
2156   return priv->has_palette;
2157 }
2158
2159 /**
2160  * gtk_color_selection_set_has_palette:
2161  * @colorsel: a #GtkColorSelection.
2162  * @has_palette: %TRUE if palette is to be visible, %FALSE otherwise.
2163  *
2164  * Shows and hides the palette based upon the value of @has_palette.
2165  * 
2166  **/
2167 void
2168 gtk_color_selection_set_has_palette (GtkColorSelection *colorsel,
2169                                      gboolean           has_palette)
2170 {
2171   ColorSelectionPrivate *priv;
2172   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2173   
2174   priv = colorsel->private_data;
2175   has_palette = has_palette != FALSE;
2176   
2177   if (priv->has_palette != has_palette)
2178     {
2179       priv->has_palette = has_palette;
2180       if (has_palette)
2181         gtk_widget_show (priv->palette_frame);
2182       else
2183         gtk_widget_hide (priv->palette_frame);
2184       
2185       g_object_notify (G_OBJECT (colorsel), "has_palette");
2186     }
2187 }
2188
2189 /**
2190  * gtk_color_selection_set_current_color:
2191  * @colorsel: a #GtkColorSelection.
2192  * @color: A #GdkColor to set the current color with.
2193  *
2194  * Sets the current color to be @color.  The first time this is called, it will
2195  * also set the original color to be @color too.
2196  **/
2197 void
2198 gtk_color_selection_set_current_color (GtkColorSelection *colorsel,
2199                                        GdkColor          *color)
2200 {
2201   ColorSelectionPrivate *priv;
2202   gint i;
2203   gboolean previous_changed;
2204   
2205   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2206
2207   priv = colorsel->private_data;
2208   priv->changing = TRUE;
2209   priv->color[COLORSEL_RED] = SCALE (color->red);
2210   priv->color[COLORSEL_GREEN] = SCALE (color->green);
2211   priv->color[COLORSEL_BLUE] = SCALE (color->blue);
2212   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2213                   priv->color[COLORSEL_GREEN],
2214                   priv->color[COLORSEL_BLUE],
2215                   &priv->color[COLORSEL_HUE],
2216                   &priv->color[COLORSEL_SATURATION],
2217                   &priv->color[COLORSEL_VALUE]);
2218   if (priv->default_set == FALSE)
2219     {
2220       previous_changed = TRUE;
2221       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2222         priv->old_color[i] = priv->color[i];
2223     }
2224   priv->default_set = TRUE;
2225   update_color (colorsel);
2226 }
2227
2228 /**
2229  * gtk_color_selection_set_current_alpha:
2230  * @colorsel: a #GtkColorSelection.
2231  * @alpha: an integer between 0 and 65535.
2232  *
2233  * Sets the current opacity to be @alpha.  The first time this is called, it will
2234  * also set the original opacity to be @alpha too.
2235  **/
2236 void
2237 gtk_color_selection_set_current_alpha (GtkColorSelection *colorsel,
2238                                        guint16            alpha)
2239 {
2240   ColorSelectionPrivate *priv;
2241   gint i;
2242   
2243   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2244   
2245   priv = colorsel->private_data;
2246   priv->changing = TRUE;
2247   priv->color[COLORSEL_OPACITY] = SCALE (alpha);
2248   if (priv->default_alpha_set == FALSE)
2249     {
2250       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2251         priv->old_color[i] = priv->color[i];
2252     }
2253   priv->default_alpha_set = TRUE;
2254   update_color (colorsel);
2255 }
2256
2257 /**
2258  * gtk_color_selection_set_color:
2259  * @colorsel: a #GtkColorSelection.
2260  * @color: an array of 4 doubles specifying the red, green, blue and opacity 
2261  *   to set the current color to.
2262  *
2263  * Sets the current color to be @color.  The first time this is called, it will
2264  * also set the original color to be @color too.
2265  *
2266  * Deprecated: Use gtk_color_selection_set_current_color() instead.
2267  **/
2268 void
2269 gtk_color_selection_set_color (GtkColorSelection    *colorsel,
2270                                gdouble              *color)
2271 {
2272   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2273
2274   set_color_internal (colorsel, color);
2275 }
2276
2277 /**
2278  * gtk_color_selection_get_current_color:
2279  * @colorsel: a #GtkColorSelection.
2280  * @color: a #GdkColor to fill in with the current color.
2281  *
2282  * Sets @color to be the current color in the GtkColorSelection widget.
2283  **/
2284 void
2285 gtk_color_selection_get_current_color (GtkColorSelection *colorsel,
2286                                        GdkColor          *color)
2287 {
2288   ColorSelectionPrivate *priv;
2289   
2290   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2291   
2292   priv = colorsel->private_data;
2293   color->red = UNSCALE (priv->color[COLORSEL_RED]);
2294   color->green = UNSCALE (priv->color[COLORSEL_GREEN]);
2295   color->blue = UNSCALE (priv->color[COLORSEL_BLUE]);
2296 }
2297
2298 /**
2299  * gtk_color_selection_get_current_alpha:
2300  * @colorsel: a #GtkColorSelection.
2301  *
2302  * Returns the current alpha value.
2303  *
2304  * Return value: an integer between 0 and 65535.
2305  **/
2306 guint16
2307 gtk_color_selection_get_current_alpha (GtkColorSelection *colorsel)
2308 {
2309   ColorSelectionPrivate *priv;
2310   
2311   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2312   
2313   priv = colorsel->private_data;
2314   return priv->has_opacity ? UNSCALE (priv->color[COLORSEL_OPACITY]) : 65535;
2315 }
2316
2317 /**
2318  * gtk_color_selection_get_color:
2319  * @colorsel: a #GtkColorSelection.
2320  * @color: an array of 4 #gdouble to fill in with the current color.
2321  *
2322  * Sets @color to be the current color in the GtkColorSelection widget.
2323  * 
2324  * This function is deprecated, use gtk_color_selection_get_current_color() instead.
2325  **/
2326 void
2327 gtk_color_selection_get_color (GtkColorSelection *colorsel,
2328                                gdouble           *color)
2329 {
2330   ColorSelectionPrivate *priv;
2331   
2332   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2333   
2334   priv = colorsel->private_data;
2335   color[0] = priv->color[COLORSEL_RED];
2336   color[1] = priv->color[COLORSEL_GREEN];
2337   color[2] = priv->color[COLORSEL_BLUE];
2338   color[3] = priv->has_opacity ? priv->color[COLORSEL_OPACITY] : 65535;
2339 }
2340
2341 /**
2342  * gtk_color_selection_set_previous_color:
2343  * @colorsel: a #GtkColorSelection.
2344  * @color: a #GdkColor to set the previous color with.
2345  *
2346  * Sets the 'previous' color to be @color.  This function should be called with
2347  * some hesitations, as it might seem confusing to have that color change.
2348  * Calling gtk_color_selection_set_current_color() will also set this color the first
2349  * time it is called.
2350  **/
2351 void
2352 gtk_color_selection_set_previous_color (GtkColorSelection *colorsel,
2353                                         GdkColor          *color)
2354 {
2355   ColorSelectionPrivate *priv;
2356   
2357   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2358   
2359   priv = colorsel->private_data;
2360   priv->changing = TRUE;
2361   priv->old_color[COLORSEL_RED] = SCALE (color->red);
2362   priv->old_color[COLORSEL_GREEN] = SCALE (color->green);
2363   priv->old_color[COLORSEL_BLUE] = SCALE (color->blue);
2364   gtk_rgb_to_hsv (priv->old_color[COLORSEL_RED],
2365                   priv->old_color[COLORSEL_GREEN],
2366                   priv->old_color[COLORSEL_BLUE],
2367                   &priv->old_color[COLORSEL_HUE],
2368                   &priv->old_color[COLORSEL_SATURATION],
2369                   &priv->old_color[COLORSEL_VALUE]);
2370   color_sample_draw_samples (colorsel);
2371   priv->default_set = TRUE;
2372   priv->changing = FALSE;
2373 }
2374
2375 /**
2376  * gtk_color_selection_set_previous_alpha:
2377  * @colorsel: a #GtkColorSelection.
2378  * @alpha: an integer between 0 and 65535.
2379  *
2380  * Sets the 'previous' alpha to be @alpha.  This function should be called with
2381  * some hesitations, as it might seem confusing to have that alpha change.
2382  **/
2383 void
2384 gtk_color_selection_set_previous_alpha (GtkColorSelection *colorsel,
2385                                         guint16            alpha)
2386 {
2387   ColorSelectionPrivate *priv;
2388   
2389   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2390   
2391   priv = colorsel->private_data;
2392   priv->changing = TRUE;
2393   priv->old_color[COLORSEL_OPACITY] = SCALE (alpha);
2394   color_sample_draw_samples (colorsel);
2395   priv->default_alpha_set = TRUE;
2396   priv->changing = FALSE;
2397 }
2398
2399
2400 /**
2401  * gtk_color_selection_get_previous_color:
2402  * @colorsel: a #GtkColorSelection.
2403  * @color: a #GdkColor to fill in with the original color value.
2404  *
2405  * Fills @color in with the original color value.
2406  **/
2407 void
2408 gtk_color_selection_get_previous_color (GtkColorSelection *colorsel,
2409                                         GdkColor           *color)
2410 {
2411   ColorSelectionPrivate *priv;
2412   
2413   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2414   
2415   priv = colorsel->private_data;
2416   color->red = UNSCALE (priv->old_color[COLORSEL_RED]);
2417   color->green = UNSCALE (priv->old_color[COLORSEL_GREEN]);
2418   color->blue = UNSCALE (priv->old_color[COLORSEL_BLUE]);
2419 }
2420
2421 /**
2422  * gtk_color_selection_get_previous_alpha:
2423  * @colorsel: a #GtkColorSelection.
2424  *
2425  * Returns the previous alpha value.
2426  *
2427  * Return value: an integer between 0 and 65535.
2428  **/
2429 guint16
2430 gtk_color_selection_get_previous_alpha (GtkColorSelection *colorsel)
2431 {
2432   ColorSelectionPrivate *priv;
2433   
2434   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2435   
2436   priv = colorsel->private_data;
2437   return priv->has_opacity ? UNSCALE (priv->old_color[COLORSEL_OPACITY]) : 65535;
2438 }
2439
2440 /**
2441  * gtk_color_selection_set_palette_color:
2442  * @colorsel: a #GtkColorSelection.
2443  * @index: the color index of the palette.
2444  * @color: A #GdkColor to set the palette with.
2445  *
2446  * Sets the palette located at @index to have @color as its color.
2447  * 
2448  **/
2449 static void
2450 gtk_color_selection_set_palette_color (GtkColorSelection   *colorsel,
2451                                        gint                 index,
2452                                        GdkColor            *color)
2453 {
2454   ColorSelectionPrivate *priv;
2455   gint x, y;
2456   gdouble col[3];
2457   
2458   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2459   g_return_if_fail (index >= 0  && index < GTK_CUSTOM_PALETTE_WIDTH*GTK_CUSTOM_PALETTE_HEIGHT);
2460
2461   x = index % GTK_CUSTOM_PALETTE_WIDTH;
2462   y = index / GTK_CUSTOM_PALETTE_WIDTH;
2463   
2464   priv = colorsel->private_data;
2465   col[0] = SCALE (color->red);
2466   col[1] = SCALE (color->green);
2467   col[2] = SCALE (color->blue);
2468   
2469   palette_set_color (priv->custom_palette[x][y], colorsel, col);
2470 }
2471
2472 /**
2473  * gtk_color_selection_is_adjusting:
2474  * @colorsel: a #GtkColorSelection.
2475  *
2476  * Gets the current state of the @colorsel.
2477  *
2478  * Return value: %TRUE if the user is currently dragging a color around, and %FALSE
2479  * if the selection has stopped.
2480  **/
2481 gboolean
2482 gtk_color_selection_is_adjusting (GtkColorSelection *colorsel)
2483 {
2484   ColorSelectionPrivate *priv;
2485   
2486   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2487   
2488   priv = colorsel->private_data;
2489   
2490   return (gtk_hsv_is_adjusting (GTK_HSV (priv->triangle_colorsel)));
2491 }
2492
2493 static void
2494 gtk_color_selection_set_property (GObject         *object,
2495                                   guint            prop_id,
2496                                   const GValue    *value,
2497                                   GParamSpec      *pspec)
2498 {
2499   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (object);
2500   
2501   switch (prop_id)
2502     {
2503     case PROP_HAS_OPACITY_CONTROL:
2504       gtk_color_selection_set_has_opacity_control (colorsel, 
2505                                                    g_value_get_boolean (value));
2506       break;
2507     case PROP_HAS_PALETTE:
2508       gtk_color_selection_set_has_palette (colorsel, 
2509                                            g_value_get_boolean (value));
2510       break;
2511     case PROP_CURRENT_COLOR:
2512       gtk_color_selection_set_current_color (colorsel, g_value_get_boxed (value));
2513       break;
2514     case PROP_CURRENT_ALPHA:
2515       gtk_color_selection_set_current_alpha (colorsel, g_value_get_uint (value));
2516       break;
2517     default:
2518       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2519       break;
2520     }
2521   
2522 }
2523
2524 static void
2525 gtk_color_selection_get_property (GObject     *object,
2526                                   guint        prop_id,
2527                                   GValue      *value,
2528                                   GParamSpec  *pspec)
2529 {
2530   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (object);
2531   GdkColor color;
2532   
2533   switch (prop_id)
2534     {
2535     case PROP_HAS_OPACITY_CONTROL:
2536       g_value_set_boolean (value, gtk_color_selection_get_has_opacity_control (colorsel));
2537       break;
2538     case PROP_HAS_PALETTE:
2539       g_value_set_boolean (value, gtk_color_selection_get_has_palette (colorsel));
2540       break;
2541     case PROP_CURRENT_COLOR:
2542       gtk_color_selection_get_current_color (colorsel, &color);
2543       g_value_set_boxed (value, &color);
2544       break;
2545     case PROP_CURRENT_ALPHA:
2546       g_value_set_uint (value, gtk_color_selection_get_current_alpha (colorsel));
2547       break;
2548     default:
2549       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2550       break;
2551     }
2552 }
2553
2554
2555 /**
2556  * gtk_color_selection_palette_from_string:
2557  * @str: a string encoding a color palette.
2558  * @colors: return location for allocated array of #GdkColor.
2559  * @n_colors: return location for length of array.
2560  * 
2561  * Parses a color palette string; the string is a colon-separated
2562  * list of color names readable by gdk_color_parse().
2563  * 
2564  * Return value: %TRUE if a palette was successfully parsed.
2565  **/
2566 gboolean
2567 gtk_color_selection_palette_from_string (const gchar *str,
2568                                          GdkColor   **colors,
2569                                          gint        *n_colors)
2570 {
2571   GdkColor *retval;
2572   gint count;
2573   gchar *p;
2574   gchar *start;
2575   gchar *copy;
2576   
2577   count = 0;
2578   retval = NULL;
2579   copy = g_strdup (str);
2580
2581   start = copy;
2582   p = copy;
2583   while (TRUE)
2584     {
2585       if (*p == ':' || *p == '\0')
2586         {
2587           gboolean done = TRUE;
2588
2589           if (start == p)
2590             {
2591               goto failed; /* empty entry */
2592             }
2593               
2594           if (*p)
2595             {
2596               *p = '\0';
2597               done = FALSE;
2598             }
2599
2600           retval = g_renew (GdkColor, retval, count + 1);
2601           if (!gdk_color_parse (start, retval + count))
2602             {
2603               goto failed;
2604             }
2605
2606           ++count;
2607
2608           if (done)
2609             break;
2610           else
2611             start = p + 1;
2612         }
2613
2614       ++p;
2615     }
2616
2617   g_free (copy);
2618   
2619   if (colors)
2620     *colors = retval;
2621   else
2622     g_free (retval);
2623
2624   if (n_colors)
2625     *n_colors = count;
2626
2627   return TRUE;
2628   
2629  failed:
2630   g_free (copy);
2631   g_free (retval);
2632
2633   if (colors)
2634     *colors = NULL;
2635   if (n_colors)
2636     *n_colors = 0;
2637
2638   return FALSE;
2639 }
2640
2641 /**
2642  * gtk_color_selection_palette_to_string:
2643  * @colors: an array of colors.
2644  * @n_colors: length of the array.
2645  * 
2646  * Encodes a palette as a string, useful for persistent storage.
2647  * 
2648  * Return value: allocated string encoding the palette.
2649  **/
2650 gchar*
2651 gtk_color_selection_palette_to_string (const GdkColor *colors,
2652                                        gint            n_colors)
2653 {
2654   gint i;
2655   gchar **strs = NULL;
2656   gchar *retval;
2657   
2658   if (n_colors == 0)
2659     return g_strdup ("");
2660
2661   strs = g_new0 (gchar*, n_colors + 1);
2662
2663   i = 0;
2664   while (i < n_colors)
2665     {
2666       gchar *ptr;
2667       
2668       strs[i] =
2669         g_strdup_printf ("#%2X%2X%2X",
2670                          colors[i].red / 256,
2671                          colors[i].green / 256,
2672                          colors[i].blue / 256);
2673
2674       for (ptr = strs[i]; *ptr; ptr++)
2675         if (*ptr == ' ')
2676           *ptr = '0';
2677       
2678       ++i;
2679     }
2680
2681   retval = g_strjoinv (":", strs);
2682
2683   g_strfreev (strs);
2684
2685   return retval;
2686 }
2687
2688 /**
2689  * gtk_color_selection_set_change_palette_hook:
2690  * @func: a function to call when the custom palette needs saving.
2691  * 
2692  * Installs a global function to be called whenever the user tries to
2693  * modify the palette in a color selection. This function should save
2694  * the new palette contents, and update the GtkSettings property
2695  * "gtk-color-palette" so all GtkColorSelection widgets will be modified.
2696  *
2697  * Return value: the previous change palette hook (that was replaced).
2698  *
2699  * Deprecated: This function is deprecated in favor of 
2700  * gtk_color_selection_set_change_palette_with_screen_hook(), and does
2701  * not work in multihead environments.
2702  * 
2703  **/
2704 GtkColorSelectionChangePaletteFunc
2705 gtk_color_selection_set_change_palette_hook (GtkColorSelectionChangePaletteFunc func)
2706 {
2707   GtkColorSelectionChangePaletteFunc old;
2708
2709   old = noscreen_change_palette_hook;
2710
2711   noscreen_change_palette_hook = func;
2712
2713   return old;
2714 }
2715
2716 /**
2717  * gtk_color_selection_set_change_palette_with_screen_hook:
2718  * @func: a function to call when the custom palette needs saving.
2719  * 
2720  * Installs a global function to be called whenever the user tries to
2721  * modify the palette in a color selection. This function should save
2722  * the new palette contents, and update the GtkSettings property
2723  * "gtk-color-palette" so all GtkColorSelection widgets will be modified.
2724  * 
2725  * Return value: the previous change palette hook (that was replaced).
2726  *
2727  * Since: 2.2
2728  **/
2729 GtkColorSelectionChangePaletteWithScreenFunc
2730 gtk_color_selection_set_change_palette_with_screen_hook (GtkColorSelectionChangePaletteWithScreenFunc func)
2731 {
2732   GtkColorSelectionChangePaletteWithScreenFunc old;
2733
2734   old = change_palette_hook;
2735
2736   change_palette_hook = func;
2737
2738   return old;
2739 }