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