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