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