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