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