]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorsel.c
98a0546c55697f5ed8face83e0d0577ebf267d0f
[~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 gboolean
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   return TRUE;
1028 }
1029                
1030
1031 static GtkWidget*
1032 palette_new (GtkColorSelection *colorsel)
1033 {
1034   GtkWidget *retval;
1035   ColorSelectionPrivate *priv;
1036   
1037   static const GtkTargetEntry targets[] = {
1038     { "application/x-color", 0 }
1039   };
1040
1041   priv = colorsel->private_data;
1042   
1043   retval = gtk_drawing_area_new ();
1044
1045   GTK_WIDGET_SET_FLAGS (retval, GTK_CAN_FOCUS);
1046   
1047   gtk_object_set_data (GTK_OBJECT (retval), "color_set", GINT_TO_POINTER (0)); 
1048   gtk_widget_set_events (retval, GDK_BUTTON_PRESS_MASK
1049                          | GDK_BUTTON_RELEASE_MASK
1050                          | GDK_EXPOSURE_MASK
1051                          | GDK_ENTER_NOTIFY_MASK
1052                          | GDK_LEAVE_NOTIFY_MASK);
1053   
1054
1055   gtk_signal_connect (GTK_OBJECT (retval), "expose_event",
1056                       GTK_SIGNAL_FUNC (palette_expose), colorsel);
1057   gtk_signal_connect (GTK_OBJECT (retval), "button_press_event",
1058                       GTK_SIGNAL_FUNC (palette_press), colorsel);
1059   gtk_signal_connect (GTK_OBJECT (retval), "key_press_event",
1060                       GTK_SIGNAL_FUNC (palette_activate), colorsel);
1061   gtk_signal_connect (GTK_OBJECT (retval), "popup_menu",
1062                       GTK_SIGNAL_FUNC (palette_popup), colorsel);
1063   
1064   gtk_drag_dest_set (retval,
1065                      GTK_DEST_DEFAULT_HIGHLIGHT |
1066                      GTK_DEST_DEFAULT_MOTION |
1067                      GTK_DEST_DEFAULT_DROP,
1068                      targets, 1,
1069                      GDK_ACTION_COPY);
1070   
1071   gtk_signal_connect (GTK_OBJECT (retval), "drag_end",
1072                       GTK_SIGNAL_FUNC (palette_drag_end), NULL);
1073   gtk_signal_connect (GTK_OBJECT (retval), "drag_data_received",
1074                       GTK_SIGNAL_FUNC (palette_drop_handle), colorsel);
1075
1076   gtk_tooltips_set_tip (priv->tooltips,
1077                         retval,
1078                         _("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.\""),
1079                         NULL);
1080   return retval;
1081 }
1082
1083
1084 /*
1085  *
1086  * The actual GtkColorSelection widget
1087  *
1088  */
1089
1090 static void
1091 initialize_cursor (void)
1092 {
1093   GdkColor fg, bg;
1094   
1095   GdkPixmap *pixmap =
1096     gdk_bitmap_create_from_data (NULL,
1097                                  dropper_bits,
1098                                  DROPPER_WIDTH, DROPPER_HEIGHT);
1099   GdkPixmap *mask =
1100     gdk_bitmap_create_from_data (NULL,
1101                                  dropper_mask,
1102                                  DROPPER_WIDTH, DROPPER_HEIGHT);
1103   
1104   gdk_color_white (gdk_colormap_get_system (), &bg);
1105   gdk_color_black (gdk_colormap_get_system (), &fg);
1106   
1107   picker_cursor = gdk_cursor_new_from_pixmap (pixmap, mask, &fg, &bg, DROPPER_X_HOT ,DROPPER_Y_HOT);
1108   
1109   gdk_pixmap_unref (pixmap);
1110   gdk_pixmap_unref (mask);
1111   
1112 }
1113
1114 static void
1115 grab_color_at_mouse (GtkWidget *invisible,
1116                      gint       x_root,
1117                      gint       y_root,
1118                      gpointer   data)
1119 {
1120   GdkImage *image;
1121   guint32 pixel;
1122   GtkColorSelection *colorsel = data;
1123   ColorSelectionPrivate *priv;
1124   GdkColormap *colormap = gdk_colormap_get_system ();
1125   GdkColor color;
1126   
1127   priv = colorsel->private_data;
1128   
1129   image = gdk_image_get (GDK_ROOT_PARENT (), x_root, y_root, 1, 1);
1130   pixel = gdk_image_get_pixel (image, 0, 0);
1131   gdk_image_unref (image);
1132
1133   gdk_colormap_query_color (colormap, pixel, &color);
1134   
1135   priv->color[COLORSEL_RED] = SCALE (color.red);
1136   priv->color[COLORSEL_GREEN] = SCALE (color.green);
1137   priv->color[COLORSEL_BLUE] = SCALE (color.blue);
1138   
1139   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1140                   priv->color[COLORSEL_GREEN],
1141                   priv->color[COLORSEL_BLUE],
1142                   &priv->color[COLORSEL_HUE],
1143                   &priv->color[COLORSEL_SATURATION],
1144                   &priv->color[COLORSEL_VALUE]);
1145
1146   update_color (colorsel);
1147 }
1148
1149 static void
1150 shutdown_eyedropper (GtkWidget *widget)
1151 {
1152   GtkColorSelection *colorsel;
1153   ColorSelectionPrivate *priv;
1154
1155   colorsel = GTK_COLOR_SELECTION (widget);
1156   priv = colorsel->private_data;    
1157
1158   gdk_keyboard_ungrab (gtk_get_current_event_time ());
1159   gdk_pointer_ungrab (gtk_get_current_event_time ());
1160   gtk_grab_remove (priv->dropper_grab_widget);
1161 }
1162
1163 static void
1164 mouse_motion (GtkWidget      *invisible,
1165               GdkEventMotion *event,
1166               gpointer        data)
1167 {
1168   grab_color_at_mouse (invisible, event->x_root, event->y_root, data); 
1169 }
1170
1171 static gboolean
1172 mouse_release (GtkWidget      *invisible,
1173                GdkEventButton *event,
1174                gpointer        data)
1175 {
1176   GtkColorSelection *colorsel = data;
1177   ColorSelectionPrivate *priv;
1178   priv = colorsel->private_data;  
1179
1180   if (event->button != 1)
1181     return FALSE;
1182
1183   grab_color_at_mouse (invisible, event->x_root, event->y_root, data);
1184
1185   shutdown_eyedropper (GTK_WIDGET (data));
1186   
1187   gtk_signal_disconnect_by_func (GTK_OBJECT (invisible),
1188                                  GTK_SIGNAL_FUNC (mouse_motion), data);
1189   gtk_signal_disconnect_by_func (GTK_OBJECT (invisible),
1190                                  GTK_SIGNAL_FUNC (mouse_release), data);
1191
1192   return TRUE;
1193 }
1194
1195 /* Helper Functions */
1196
1197 static gboolean mouse_press (GtkWidget      *invisible,
1198                              GdkEventButton *event,
1199                              gpointer        data);
1200
1201 static gboolean
1202 key_press (GtkWidget   *invisible,
1203            GdkEventKey *event,
1204            gpointer     data)
1205 {  
1206   if (event->keyval == GDK_Escape)
1207     {
1208       shutdown_eyedropper (data);
1209
1210       gtk_signal_disconnect_by_func (GTK_OBJECT (invisible),
1211                                      GTK_SIGNAL_FUNC (mouse_press),
1212                                      data);
1213       gtk_signal_disconnect_by_func (GTK_OBJECT (invisible),
1214                                      GTK_SIGNAL_FUNC (key_press),
1215                                      data);
1216       
1217       return TRUE;
1218     }
1219
1220   return FALSE;
1221 }
1222
1223 static gboolean
1224 mouse_press (GtkWidget      *invisible,
1225              GdkEventButton *event,
1226              gpointer        data)
1227 {
1228   GtkColorSelection *colorsel = data;
1229   ColorSelectionPrivate *priv;
1230   priv = colorsel->private_data;
1231   
1232   if (event->type == GDK_BUTTON_PRESS &&
1233       event->button == 1)
1234     {
1235       gtk_signal_connect (GTK_OBJECT (invisible), "motion_notify_event",
1236                           GTK_SIGNAL_FUNC (mouse_motion),
1237                           data);
1238       gtk_signal_connect (GTK_OBJECT (invisible), "button_release_event",
1239                           GTK_SIGNAL_FUNC (mouse_release),
1240                           data);
1241       gtk_signal_disconnect_by_func (GTK_OBJECT (invisible),
1242                                      GTK_SIGNAL_FUNC (mouse_press),
1243                                      data);
1244       gtk_signal_disconnect_by_func (GTK_OBJECT (invisible),
1245                                      GTK_SIGNAL_FUNC (key_press),
1246                                      data);
1247       return TRUE;
1248     }
1249
1250   return FALSE;
1251 }
1252
1253 /* when the button is clicked */
1254 static void
1255 get_screen_color (GtkWidget *button)
1256 {
1257   GtkColorSelection *colorsel = gtk_object_get_data (GTK_OBJECT (button), "COLORSEL");
1258   ColorSelectionPrivate *priv = colorsel->private_data; 
1259   
1260   if (picker_cursor == NULL)
1261     {
1262       initialize_cursor ();
1263     }
1264
1265   if (priv->dropper_grab_widget == NULL)
1266     {
1267       priv->dropper_grab_widget = gtk_invisible_new ();
1268
1269       gtk_widget_add_events (priv->dropper_grab_widget,
1270                              GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
1271       
1272       gtk_widget_show (priv->dropper_grab_widget);
1273     }
1274
1275   if (gdk_keyboard_grab (priv->dropper_grab_widget->window,
1276                          FALSE,
1277                          gtk_get_current_event_time ()) != GDK_GRAB_SUCCESS)
1278     {
1279       g_warning ("Failed to grab keyboard to do eyedropper");
1280       return;
1281     }
1282   
1283   if (gdk_pointer_grab (priv->dropper_grab_widget->window,
1284                         FALSE,
1285                         GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
1286                         NULL,
1287                         picker_cursor,
1288                         gtk_get_current_event_time ()) != GDK_GRAB_SUCCESS)
1289     {
1290       gdk_keyboard_ungrab (GDK_CURRENT_TIME);
1291       g_warning ("Failed to grab pointer to do eyedropper");
1292       return;
1293     }
1294
1295   gtk_grab_add (priv->dropper_grab_widget);
1296   
1297   gtk_signal_connect (GTK_OBJECT (priv->dropper_grab_widget), "button_press_event",
1298                       GTK_SIGNAL_FUNC (mouse_press), colorsel);
1299   gtk_signal_connect (GTK_OBJECT (priv->dropper_grab_widget), "key_press_event",
1300                       GTK_SIGNAL_FUNC (key_press), colorsel);
1301 }
1302
1303 static void
1304 hex_changed (GtkWidget *hex_entry,
1305              gpointer   data)
1306 {
1307   GtkColorSelection *colorsel;
1308   ColorSelectionPrivate *priv;
1309   GdkColor color;
1310   gchar *text;
1311   
1312   colorsel = GTK_COLOR_SELECTION (data);
1313   priv = colorsel->private_data;
1314   
1315   if (priv->changing)
1316     return;
1317   
1318   text = gtk_editable_get_chars (GTK_EDITABLE (priv->hex_entry), 0, -1);
1319   if (gdk_color_parse (text, &color))
1320     {
1321       priv->color[COLORSEL_RED] = CLAMP (color.red/65280.0, 0.0, 1.0);
1322       priv->color[COLORSEL_GREEN] = CLAMP (color.green/65280.0, 0.0, 1.0);
1323       priv->color[COLORSEL_BLUE] = CLAMP (color.blue/65280.0, 0.0, 1.0);
1324       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1325                       priv->color[COLORSEL_GREEN],
1326                       priv->color[COLORSEL_BLUE],
1327                       &priv->color[COLORSEL_HUE],
1328                       &priv->color[COLORSEL_SATURATION],
1329                       &priv->color[COLORSEL_VALUE]);
1330       update_color (colorsel);
1331     }
1332   g_free (text);
1333 }
1334
1335 static void
1336 hsv_changed (GtkWidget *hsv,
1337              gpointer   data)
1338 {
1339   GtkColorSelection *colorsel;
1340   ColorSelectionPrivate *priv;
1341   
1342   colorsel = GTK_COLOR_SELECTION (data);
1343   priv = colorsel->private_data;
1344   
1345   if (priv->changing)
1346     return;
1347   
1348   gtk_hsv_get_color (GTK_HSV (hsv),
1349                      &priv->color[COLORSEL_HUE],
1350                      &priv->color[COLORSEL_SATURATION],
1351                      &priv->color[COLORSEL_VALUE]);
1352   gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
1353                   priv->color[COLORSEL_SATURATION],
1354                   priv->color[COLORSEL_VALUE],
1355                   &priv->color[COLORSEL_RED],
1356                   &priv->color[COLORSEL_GREEN],
1357                   &priv->color[COLORSEL_BLUE]);
1358   update_color (colorsel);
1359 }
1360
1361 static void
1362 adjustment_changed (GtkAdjustment *adjustment,
1363                     gpointer       data)
1364 {
1365   GtkColorSelection *colorsel;
1366   ColorSelectionPrivate *priv;
1367   
1368   colorsel = GTK_COLOR_SELECTION (gtk_object_get_data (GTK_OBJECT (adjustment), "COLORSEL"));
1369   priv = colorsel->private_data;
1370   
1371   if (priv->changing)
1372     return;
1373   
1374   switch (GPOINTER_TO_INT (data))
1375     {
1376     case COLORSEL_SATURATION:
1377     case COLORSEL_VALUE:
1378       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
1379       gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
1380                       priv->color[COLORSEL_SATURATION],
1381                       priv->color[COLORSEL_VALUE],
1382                       &priv->color[COLORSEL_RED],
1383                       &priv->color[COLORSEL_GREEN],
1384                       &priv->color[COLORSEL_BLUE]);
1385       break;
1386     case COLORSEL_HUE:
1387       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 360;
1388       gtk_hsv_to_rgb (priv->color[COLORSEL_HUE],
1389                       priv->color[COLORSEL_SATURATION],
1390                       priv->color[COLORSEL_VALUE],
1391                       &priv->color[COLORSEL_RED],
1392                       &priv->color[COLORSEL_GREEN],
1393                       &priv->color[COLORSEL_BLUE]);
1394       break;
1395     case COLORSEL_RED:
1396     case COLORSEL_GREEN:
1397     case COLORSEL_BLUE:
1398       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
1399       
1400       gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
1401                       priv->color[COLORSEL_GREEN],
1402                       priv->color[COLORSEL_BLUE],
1403                       &priv->color[COLORSEL_HUE],
1404                       &priv->color[COLORSEL_SATURATION],
1405                       &priv->color[COLORSEL_VALUE]);
1406       break;
1407     default:
1408       priv->color[GPOINTER_TO_INT (data)] = adjustment->value / 255;
1409       break;
1410     }
1411   update_color (colorsel);
1412 }
1413
1414 static void 
1415 opacity_entry_changed (GtkWidget *opacity_entry,
1416                        gpointer   data)
1417 {
1418   GtkColorSelection *colorsel;
1419   ColorSelectionPrivate *priv;
1420   GtkAdjustment *adj;
1421   gchar *text;
1422   
1423   colorsel = GTK_COLOR_SELECTION (data);
1424   priv = colorsel->private_data;
1425   
1426   if (priv->changing)
1427     return;
1428   
1429   text = gtk_editable_get_chars (GTK_EDITABLE (priv->opacity_entry), 0, -1);
1430   adj = gtk_range_get_adjustment (GTK_RANGE (priv->opacity_slider));
1431   gtk_adjustment_set_value (adj, g_strtod (text, NULL)); 
1432   
1433   update_color (colorsel);
1434   
1435   g_free (text);
1436 }
1437
1438 static void
1439 make_label_spinbutton (GtkColorSelection *colorsel,
1440                        GtkWidget        **spinbutton,
1441                        gchar             *text,
1442                        GtkWidget         *table,
1443                        gint               i,
1444                        gint               j,
1445                        gint               channel_type,
1446                        const gchar       *tooltip)
1447 {
1448   GtkWidget *label;
1449   GtkAdjustment *adjust;
1450   ColorSelectionPrivate *priv = colorsel->private_data;
1451   
1452   if (channel_type == COLORSEL_HUE)
1453     {
1454       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 360.0, 1.0, 1.0, 1.0));
1455     }
1456   else
1457     {
1458       adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 1.0, 1.0));
1459     }
1460   gtk_object_set_data (GTK_OBJECT (adjust), "COLORSEL", colorsel);
1461   *spinbutton = gtk_spin_button_new (adjust, 10.0, 0);
1462
1463   gtk_tooltips_set_tip (priv->tooltips, *spinbutton, tooltip, NULL);  
1464
1465   gtk_signal_connect (GTK_OBJECT (adjust), "value_changed",
1466                       GTK_SIGNAL_FUNC (adjustment_changed),
1467                       GINT_TO_POINTER (channel_type));
1468   label = gtk_label_new_with_mnemonic (text);
1469   gtk_label_set_mnemonic_widget (GTK_LABEL (label), *spinbutton);
1470
1471   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
1472   gtk_table_attach_defaults (GTK_TABLE (table), label, i, i+1, j, j+1);
1473   gtk_table_attach_defaults (GTK_TABLE (table), *spinbutton, i+1, i+2, j, j+1);
1474   
1475 }
1476
1477 static void
1478 make_palette_frame (GtkColorSelection *colorsel,
1479                     GtkWidget         *table,
1480                     gint               i,
1481                     gint               j)
1482 {
1483   GtkWidget *frame;
1484   ColorSelectionPrivate *priv;
1485   
1486   priv = colorsel->private_data;
1487   frame = gtk_frame_new (NULL);
1488   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
1489   priv->custom_palette[i][j] = palette_new (colorsel);
1490   gtk_widget_set_usize (priv->custom_palette[i][j], CUSTOM_PALETTE_ENTRY_WIDTH, CUSTOM_PALETTE_ENTRY_HEIGHT);
1491   gtk_container_add (GTK_CONTAINER (frame), priv->custom_palette[i][j]);
1492   gtk_table_attach_defaults (GTK_TABLE (table), frame, i, i+1, j, j+1);
1493 }
1494
1495 /* Set the palette entry [x][y] to be the currently selected one. */
1496 static void 
1497 set_selected_palette (GtkColorSelection *colorsel, int x, int y)
1498 {
1499   ColorSelectionPrivate *priv = colorsel->private_data; 
1500
1501   gtk_widget_grab_focus (priv->custom_palette[x][y]);
1502 }
1503
1504 static double
1505 scale_round (double val, double factor)
1506 {
1507   val = floor (val * factor + 0.5);
1508   val = MAX (val, 0);
1509   val = MIN (val, factor);
1510   return val;
1511 }
1512
1513 static void
1514 update_color (GtkColorSelection *colorsel)
1515 {
1516   ColorSelectionPrivate *priv = colorsel->private_data;
1517   gchar entryval[12];
1518   gchar opacity_text[32];
1519   gchar *ptr;
1520   
1521   priv->changing = TRUE;
1522   color_sample_draw_samples (colorsel);
1523   
1524   gtk_hsv_set_color (GTK_HSV (priv->triangle_colorsel),
1525                      priv->color[COLORSEL_HUE],
1526                      priv->color[COLORSEL_SATURATION],
1527                      priv->color[COLORSEL_VALUE]);
1528   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1529                             (GTK_SPIN_BUTTON (priv->hue_spinbutton)),
1530                             scale_round (priv->color[COLORSEL_HUE], 360));
1531   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1532                             (GTK_SPIN_BUTTON (priv->sat_spinbutton)),
1533                             scale_round (priv->color[COLORSEL_SATURATION], 255));
1534   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1535                             (GTK_SPIN_BUTTON (priv->val_spinbutton)),
1536                             scale_round (priv->color[COLORSEL_VALUE], 255));
1537   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1538                             (GTK_SPIN_BUTTON (priv->red_spinbutton)),
1539                             scale_round (priv->color[COLORSEL_RED], 255));
1540   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1541                             (GTK_SPIN_BUTTON (priv->green_spinbutton)),
1542                             scale_round (priv->color[COLORSEL_GREEN], 255));
1543   gtk_adjustment_set_value (gtk_spin_button_get_adjustment
1544                             (GTK_SPIN_BUTTON (priv->blue_spinbutton)),
1545                             scale_round (priv->color[COLORSEL_BLUE], 255));
1546   gtk_adjustment_set_value (gtk_range_get_adjustment
1547                             (GTK_RANGE (priv->opacity_slider)),
1548                             scale_round (priv->color[COLORSEL_OPACITY], 255));
1549   
1550   g_snprintf (opacity_text, 32, "%.0f", scale_round (priv->color[COLORSEL_OPACITY], 255));
1551   gtk_entry_set_text (GTK_ENTRY (priv->opacity_entry), opacity_text);
1552   
1553   g_snprintf (entryval, 11, "#%2X%2X%2X",
1554               (guint) (scale_round (priv->color[COLORSEL_RED], 255)),
1555               (guint) (scale_round (priv->color[COLORSEL_GREEN], 255)),
1556               (guint) (scale_round (priv->color[COLORSEL_BLUE], 255)));
1557   
1558   for (ptr = entryval; *ptr; ptr++)
1559     if (*ptr == ' ')
1560       *ptr = '0';
1561   gtk_entry_set_text (GTK_ENTRY (priv->hex_entry), entryval);
1562   priv->changing = FALSE;
1563
1564   g_object_freeze_notify (G_OBJECT (colorsel));
1565   g_object_notify (G_OBJECT (colorsel), "current_color");
1566   g_object_notify (G_OBJECT (colorsel), "current_alpha");
1567   g_object_thaw_notify (G_OBJECT (colorsel));
1568 }
1569
1570
1571 static void
1572 fill_palette_from_string (const gchar *str)
1573 {
1574   GdkColor *colors = NULL;
1575   gint n_colors = 0;
1576
1577   if (str == NULL)
1578     return;
1579   
1580   if (!gtk_color_selection_palette_from_string (str, &colors, &n_colors))
1581     return;
1582
1583   if (n_colors > (GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT))    
1584     n_colors = GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT;
1585
1586   memcpy (current_colors, colors, sizeof (GdkColor) * n_colors);
1587
1588   g_free (colors);
1589 }
1590
1591 static void
1592 palette_change_notify_class (GObject    *object,
1593                              GParamSpec *pspec)
1594 {
1595   gchar *str = NULL;
1596   
1597   g_object_get (object, pspec->name, &str, NULL);
1598
1599   fill_palette_from_string (str);
1600
1601   g_free (str);
1602 }
1603
1604 static void
1605 update_palette (GtkColorSelection *colorsel)
1606 {
1607   gint i, j;
1608   
1609   for (i = 0; i < GTK_CUSTOM_PALETTE_HEIGHT; i++)
1610     {
1611       for (j = 0; j < GTK_CUSTOM_PALETTE_WIDTH; j++)
1612         {
1613           gint index;
1614
1615           index = i * GTK_CUSTOM_PALETTE_WIDTH + j;
1616           
1617           gtk_color_selection_set_palette_color (colorsel,
1618                                                  index,
1619                                                  &current_colors[index]);
1620         }
1621     }
1622 }
1623
1624 static void
1625 palette_change_notify_instance (GObject    *object,
1626                                 GParamSpec *pspec,
1627                                 gpointer    data)
1628 {
1629   update_palette (GTK_COLOR_SELECTION (data));
1630 }
1631
1632 static void
1633 default_change_palette_func (const GdkColor *colors,
1634                              gint            n_colors)
1635 {
1636   gchar *str;
1637   
1638   str = gtk_color_selection_palette_to_string (colors, n_colors);
1639
1640   gtk_settings_set_string_property (gtk_settings_get_default (),
1641                                     "gtk-color-palette",
1642                                     str,
1643                                     "gtk_color_selection_palette_to_string");
1644
1645   g_free (str);
1646 }
1647
1648 GtkType
1649 gtk_color_selection_get_type (void)
1650 {
1651   static GtkType color_selection_type = 0;
1652   
1653   if (!color_selection_type)
1654     {
1655       static const GtkTypeInfo color_selection_info =
1656       {
1657         "GtkColorSelection",
1658         sizeof (GtkColorSelection),
1659         sizeof (GtkColorSelectionClass),
1660         (GtkClassInitFunc) gtk_color_selection_class_init,
1661         (GtkObjectInitFunc) gtk_color_selection_init,
1662         /* reserved_1 */ NULL,
1663         /* reserved_2 */ NULL,
1664         (GtkClassInitFunc) NULL,
1665       };
1666       
1667       color_selection_type = gtk_type_unique (GTK_TYPE_VBOX, &color_selection_info);
1668     }
1669   
1670   return color_selection_type;
1671 }
1672
1673 static void
1674 gtk_color_selection_class_init (GtkColorSelectionClass *klass)
1675 {
1676   GtkObjectClass *object_class;
1677   GObjectClass *gobject_class;
1678   GtkWidgetClass *widget_class;
1679   
1680   object_class = GTK_OBJECT_CLASS (klass);
1681   gobject_class = G_OBJECT_CLASS (klass);
1682   widget_class = GTK_WIDGET_CLASS (klass);
1683   
1684   parent_class = gtk_type_class (GTK_TYPE_VBOX);
1685   
1686   object_class->destroy = gtk_color_selection_destroy;
1687   gobject_class->finalize = gtk_color_selection_finalize;
1688
1689   gobject_class->set_property = gtk_color_selection_set_property;
1690   gobject_class->get_property = gtk_color_selection_get_property;
1691   
1692   widget_class->realize = gtk_color_selection_realize;
1693
1694   g_object_class_install_property (gobject_class,
1695                                    PROP_HAS_OPACITY_CONTROL,
1696                                    g_param_spec_boolean ("has_opacity_control",
1697                                                          _("Has Opacity Control"),
1698                                                          _("Whether the color selector should allow setting opacity"),
1699                                                          FALSE,
1700                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
1701   g_object_class_install_property (gobject_class,
1702                                    PROP_HAS_PALETTE,
1703                                    g_param_spec_boolean ("has_palette",
1704                                                          _("Has palette"),
1705                                                          _("Whether a palette should be used"),
1706                                                          FALSE,
1707                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
1708   g_object_class_install_property (gobject_class,
1709                                    PROP_CURRENT_COLOR,
1710                                    g_param_spec_boxed ("current_color",
1711                                                        _("Current Color"),
1712                                                        _("The current color"),
1713                                                        GDK_TYPE_COLOR,
1714                                                        G_PARAM_READABLE | G_PARAM_WRITABLE));
1715   g_object_class_install_property (gobject_class,
1716                                    PROP_CURRENT_ALPHA,
1717                                    g_param_spec_uint ("current_alpha",
1718                                                       _("Current Alpha"),
1719                                                       _("The current opacity value (0 fully transparent, 65535 fully opaque)"),
1720                                                       0, 65535, 65535,
1721                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
1722   
1723   color_selection_signals[COLOR_CHANGED] =
1724     gtk_signal_new ("color_changed",
1725                     GTK_RUN_FIRST,
1726                     GTK_CLASS_TYPE (object_class),
1727                     GTK_SIGNAL_OFFSET (GtkColorSelectionClass, color_changed),
1728                     _gtk_marshal_VOID__VOID,
1729                     GTK_TYPE_NONE, 0);
1730
1731   gtk_settings_install_property (g_param_spec_string ("gtk-color-palette",
1732                                                       _("Custom palette"),
1733                                                       _("Palette to use in the color selector"),
1734                                                       default_colors,
1735                                                       G_PARAM_READWRITE));
1736 }
1737
1738 /* widget functions */
1739
1740 static void
1741 gtk_color_selection_init (GtkColorSelection *colorsel)
1742 {
1743   GtkWidget *top_hbox;
1744   GtkWidget *top_right_vbox;
1745   GtkWidget *table, *label, *hbox, *frame, *vbox;
1746   GtkAdjustment *adjust;
1747   GdkPixmap *dropper_pixmap;
1748   GtkWidget *dropper_image;
1749   GtkWidget *button;
1750   GdkBitmap *mask = NULL;
1751   gint i, j;
1752   ColorSelectionPrivate *priv;
1753   
1754   gtk_widget_push_composite_child ();
1755
1756   priv = colorsel->private_data = g_new0 (ColorSelectionPrivate, 1);
1757   priv->changing = FALSE;
1758   priv->default_set = FALSE;
1759   priv->default_alpha_set = FALSE;
1760   
1761   priv->tooltips = gtk_tooltips_new ();
1762   g_object_ref (priv->tooltips);
1763   gtk_object_sink (GTK_OBJECT (priv->tooltips));
1764   
1765   gtk_box_set_spacing (GTK_BOX (colorsel), 4);
1766   top_hbox = gtk_hbox_new (FALSE, 8);
1767   gtk_box_pack_start (GTK_BOX (colorsel), top_hbox, FALSE, FALSE, 0);
1768   
1769   vbox = gtk_vbox_new (FALSE, 4);
1770   priv->triangle_colorsel = gtk_hsv_new ();
1771   gtk_signal_connect (GTK_OBJECT (priv->triangle_colorsel), "changed",
1772                       GTK_SIGNAL_FUNC (hsv_changed), colorsel);
1773   gtk_hsv_set_metrics (GTK_HSV (priv->triangle_colorsel), 174, 15);
1774   gtk_box_pack_start (GTK_BOX (top_hbox), vbox, FALSE, FALSE, 0);
1775   gtk_box_pack_start (GTK_BOX (vbox), priv->triangle_colorsel, FALSE, FALSE, 0);
1776   gtk_tooltips_set_tip (priv->tooltips, priv->triangle_colorsel,
1777                         _("Select the color you want from the outer ring. Select the darkness or lightness of that color using the inner triangle."), NULL);
1778   
1779   hbox = gtk_hbox_new (FALSE, 4);
1780   gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1781   
1782   frame = gtk_frame_new (NULL);
1783   gtk_widget_set_usize (frame, -1, 30);
1784   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
1785   color_sample_new (colorsel);
1786   gtk_container_add (GTK_CONTAINER (frame), priv->sample_area);
1787   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
1788   
1789   button = gtk_button_new ();
1790
1791   gtk_widget_set_events (button, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
1792   gtk_object_set_data (GTK_OBJECT (button), "COLORSEL", colorsel); 
1793   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1794                       GTK_SIGNAL_FUNC (get_screen_color), NULL);
1795   dropper_pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, gtk_widget_get_colormap (button), &mask, NULL, picker);
1796   dropper_image = gtk_pixmap_new (dropper_pixmap, mask);
1797   gdk_pixmap_unref (dropper_pixmap);
1798   if (mask)
1799     gdk_pixmap_unref (mask);
1800   gtk_container_add (GTK_CONTAINER (button), dropper_image);
1801   gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1802
1803   gtk_tooltips_set_tip (priv->tooltips,
1804                         button,
1805                         _("Click the eyedropper, then click a color anywhere on your screen to select that color."), NULL);
1806   
1807   top_right_vbox = gtk_vbox_new (FALSE, 4);
1808   gtk_box_pack_start (GTK_BOX (top_hbox), top_right_vbox, FALSE, FALSE, 0);
1809   table = gtk_table_new (8, 6, FALSE);
1810   gtk_box_pack_start (GTK_BOX (top_right_vbox), table, FALSE, FALSE, 0);
1811   gtk_table_set_row_spacings (GTK_TABLE (table), 4);
1812   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
1813   
1814   make_label_spinbutton (colorsel, &priv->hue_spinbutton, _("_Hue:"), table, 0, 0, COLORSEL_HUE,
1815                          _("Position on the color wheel."));
1816   make_label_spinbutton (colorsel, &priv->sat_spinbutton, _("_Saturation:"), table, 0, 1, COLORSEL_SATURATION,
1817                          _("\"Deepness\" of the color."));
1818   make_label_spinbutton (colorsel, &priv->val_spinbutton, _("_Value:"), table, 0, 2, COLORSEL_VALUE,
1819                          _("Brightness of the color."));
1820   make_label_spinbutton (colorsel, &priv->red_spinbutton, _("_Red:"), table, 6, 0, COLORSEL_RED,
1821                          _("Amount of red light in the color."));
1822   make_label_spinbutton (colorsel, &priv->green_spinbutton, _("_Green:"), table, 6, 1, COLORSEL_GREEN,
1823                          _("Amount of green light in the color."));
1824   make_label_spinbutton (colorsel, &priv->blue_spinbutton, _("_Blue:"), table, 6, 2, COLORSEL_BLUE,
1825                          _("Amount of blue light in the color."));
1826   gtk_table_attach_defaults (GTK_TABLE (table), gtk_hseparator_new (), 0, 8, 3, 4); 
1827   
1828   priv->opacity_label = gtk_label_new_with_mnemonic (_("_Opacity:")); 
1829   gtk_misc_set_alignment (GTK_MISC (priv->opacity_label), 1.0, 0.5); 
1830   gtk_table_attach_defaults (GTK_TABLE (table), priv->opacity_label, 0, 1, 4, 5); 
1831   adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 1.0, 0.0)); 
1832   gtk_object_set_data (GTK_OBJECT (adjust), "COLORSEL", colorsel); 
1833   priv->opacity_slider = gtk_hscale_new (adjust);
1834   gtk_tooltips_set_tip (priv->tooltips,
1835                         priv->opacity_slider,
1836                         _("Transparency of the currently-selected color."), NULL);
1837   gtk_label_set_mnemonic_widget (GTK_LABEL (priv->opacity_label),
1838                                  priv->opacity_slider);
1839   gtk_scale_set_draw_value (GTK_SCALE (priv->opacity_slider), FALSE);
1840   gtk_signal_connect (GTK_OBJECT (adjust), "value_changed",
1841                       GTK_SIGNAL_FUNC (adjustment_changed),
1842                       GINT_TO_POINTER (COLORSEL_OPACITY));
1843   gtk_table_attach_defaults (GTK_TABLE (table), priv->opacity_slider, 1, 7, 4, 5); 
1844   priv->opacity_entry = gtk_entry_new (); 
1845   gtk_widget_set_usize (priv->opacity_entry, 40, 0); 
1846
1847   gtk_signal_connect (GTK_OBJECT (priv->opacity_entry), "activate",
1848                       GTK_SIGNAL_FUNC (opacity_entry_changed), colorsel);
1849   gtk_table_attach_defaults (GTK_TABLE (table), priv->opacity_entry, 7, 8, 4, 5);
1850   
1851   label = gtk_label_new_with_mnemonic (_("Color _Name:"));
1852   gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 5, 6);
1853   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
1854   priv->hex_entry = gtk_entry_new ();
1855
1856   gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->hex_entry);
1857
1858   gtk_signal_connect (GTK_OBJECT (priv->hex_entry), "activate",
1859                       GTK_SIGNAL_FUNC (hex_changed), colorsel);
1860
1861   gtk_tooltips_set_tip (priv->tooltips,
1862                         priv->hex_entry,
1863                         _("You can enter an HTML-style hexadecimal color value, or simply a color name such as 'orange' in this entry."), NULL);
1864   
1865   gtk_widget_set_usize (priv->hex_entry, 75, -1);  
1866   gtk_table_set_col_spacing (GTK_TABLE (table), 3, 15);
1867   gtk_table_attach_defaults (GTK_TABLE (table), priv->hex_entry, 1, 5, 5, 6);
1868   
1869   /* Set up the palette */
1870   table = gtk_table_new (GTK_CUSTOM_PALETTE_HEIGHT, GTK_CUSTOM_PALETTE_WIDTH, TRUE);
1871   gtk_table_set_row_spacings (GTK_TABLE (table), 1);
1872   gtk_table_set_col_spacings (GTK_TABLE (table), 1);
1873   for (i = 0; i < GTK_CUSTOM_PALETTE_WIDTH; i++)
1874     {
1875       for (j = 0; j < GTK_CUSTOM_PALETTE_HEIGHT; j++)
1876         {
1877           make_palette_frame (colorsel, table, i, j);
1878         }
1879     }
1880   set_selected_palette (colorsel, 0, 0);
1881   priv->palette_frame = gtk_frame_new (NULL);
1882   label = gtk_label_new_with_mnemonic (_("_Palette"));
1883   gtk_frame_set_label_widget (GTK_FRAME (priv->palette_frame), label);
1884
1885   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
1886                                  priv->custom_palette[0][0]);
1887   
1888   gtk_box_pack_end (GTK_BOX (top_right_vbox), priv->palette_frame, FALSE, FALSE, 0);
1889   vbox = gtk_vbox_new (FALSE, 4);
1890   gtk_container_add (GTK_CONTAINER (priv->palette_frame), vbox);
1891   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
1892
1893   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
1894   
1895   gtk_widget_show_all (top_hbox);
1896
1897   /* hide unused stuff */
1898   
1899   if (priv->has_opacity == FALSE)
1900     {
1901       gtk_widget_hide (priv->opacity_label);
1902       gtk_widget_hide (priv->opacity_slider);
1903       gtk_widget_hide (priv->opacity_entry);
1904     }
1905   
1906   if (priv->has_palette == FALSE)
1907     {
1908       gtk_widget_hide (priv->palette_frame);
1909     }
1910
1911   gtk_widget_pop_composite_child ();
1912 }
1913
1914 static void
1915 gtk_color_selection_destroy (GtkObject *object)
1916 {
1917   GtkColorSelection *cselection = GTK_COLOR_SELECTION (object);
1918   ColorSelectionPrivate *priv = cselection->private_data;
1919
1920   if (priv->dropper_grab_widget)
1921     {
1922       gtk_widget_destroy (priv->dropper_grab_widget);
1923       priv->dropper_grab_widget = NULL;
1924     }
1925
1926   if (priv->tooltips)
1927     {
1928       g_object_unref (priv->tooltips);
1929       priv->tooltips = NULL;
1930     }
1931   
1932   GTK_OBJECT_CLASS (parent_class)->destroy (object);
1933 }
1934
1935 static void
1936 gtk_color_selection_finalize (GObject *object)
1937 {
1938   GtkColorSelection *cselection = GTK_COLOR_SELECTION (object);
1939   
1940   if (cselection->private_data)
1941     {
1942       ColorSelectionPrivate *priv;
1943
1944       priv = cselection->private_data;
1945
1946       g_signal_handler_disconnect (gtk_settings_get_default (),
1947                                    priv->settings_connection);
1948       
1949       g_free (cselection->private_data);
1950       cselection->private_data = NULL;
1951     }
1952   
1953   G_OBJECT_CLASS (parent_class)->finalize (object);
1954 }
1955
1956 static void
1957 gtk_color_selection_realize (GtkWidget *widget)
1958 {
1959   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (widget);
1960   ColorSelectionPrivate *priv = colorsel->private_data;
1961   gchar *palette;
1962   static gboolean initialized = FALSE;
1963
1964   if (!initialized)
1965     {
1966       g_object_get (gtk_settings_get_default (),
1967                     "gtk-color-palette", &palette,
1968                     NULL);
1969       
1970       fill_palette_from_string (palette);
1971       g_free (palette);
1972
1973       g_signal_connect (gtk_settings_get_default (),
1974                         "notify::gtk-color-palette",
1975                         G_CALLBACK (palette_change_notify_class),
1976                         NULL);
1977
1978       initialized = TRUE;
1979     }
1980   
1981   /* Set default colors */
1982
1983   update_palette (colorsel);
1984   priv->settings_connection =
1985     g_signal_connect (gtk_settings_get_default (),
1986                       "notify::gtk-color-palette",
1987                       G_CALLBACK (palette_change_notify_instance),
1988                       colorsel);
1989
1990   if (GTK_WIDGET_CLASS (parent_class)->realize)
1991     GTK_WIDGET_CLASS (parent_class)->realize (widget);
1992 }
1993
1994 /**
1995  * gtk_color_selection_new:
1996  * 
1997  * Creates a new GtkColorSelection.
1998  * 
1999  * Return value: a new #GtkColorSelection
2000  **/
2001 GtkWidget *
2002 gtk_color_selection_new (void)
2003 {
2004   GtkColorSelection *colorsel;
2005   ColorSelectionPrivate *priv;
2006   gdouble color[4];
2007   color[0] = 1.0;
2008   color[1] = 1.0;
2009   color[2] = 1.0;
2010   color[3] = 1.0;
2011   
2012   colorsel = gtk_type_new (GTK_TYPE_COLOR_SELECTION);
2013   priv = colorsel->private_data;
2014   gtk_color_selection_set_color (colorsel, color);
2015   gtk_color_selection_set_has_opacity_control (colorsel, TRUE);
2016   
2017   /* We want to make sure that default_set is FALSE */
2018   /* This way the user can still set it */
2019   priv->default_set = FALSE;
2020   priv->default_alpha_set = FALSE;
2021   
2022   return GTK_WIDGET (colorsel);
2023 }
2024
2025
2026 void
2027 gtk_color_selection_set_update_policy (GtkColorSelection *colorsel,
2028                                        GtkUpdateType      policy)
2029 {
2030   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2031 }
2032
2033 /**
2034  * gtk_color_selection_get_has_opacity_control:
2035  * @colorsel: a #GtkColorSelection.
2036  * 
2037  * Determines whether the colorsel has an opacity control.
2038  * 
2039  * Return value: %TRUE if the @colorsel has an opacity control.  %FALSE if it does't.
2040  **/
2041 gboolean
2042 gtk_color_selection_get_has_opacity_control (GtkColorSelection *colorsel)
2043 {
2044   ColorSelectionPrivate *priv;
2045   
2046   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2047   
2048   priv = colorsel->private_data;
2049   
2050   return priv->has_opacity;
2051 }
2052
2053 /**
2054  * gtk_color_selection_set_has_opacity_control:
2055  * @colorsel: a #GtkColorSelection.
2056  * @has_opacity: %TRUE if @colorsel can set the opacity, %FALSE otherwise.
2057  *
2058  * Sets the @colorsel to use or not use opacity.
2059  * 
2060  **/
2061 void
2062 gtk_color_selection_set_has_opacity_control (GtkColorSelection *colorsel,
2063                                              gboolean           has_opacity)
2064 {
2065   ColorSelectionPrivate *priv;
2066   
2067   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2068   
2069   priv = colorsel->private_data;
2070   has_opacity = has_opacity != FALSE;
2071   
2072   if (priv->has_opacity != has_opacity)
2073     {
2074       priv->has_opacity = has_opacity;
2075       if (has_opacity)
2076         {
2077           gtk_widget_show (priv->opacity_slider);
2078           gtk_widget_show (priv->opacity_label);
2079           gtk_widget_show (priv->opacity_entry);
2080         }
2081       else
2082         {
2083           gtk_widget_hide (priv->opacity_slider);
2084           gtk_widget_hide (priv->opacity_label);
2085           gtk_widget_hide (priv->opacity_entry);
2086         }
2087       color_sample_draw_samples (colorsel);
2088       
2089       g_object_notify (G_OBJECT (colorsel), "has_opacity_control");
2090     }
2091 }
2092
2093 /**
2094  * gtk_color_selection_get_has_palette:
2095  * @colorsel: a #GtkColorSelection.
2096  * 
2097  * Determines whether the color selector has a color palette.
2098  * 
2099  * Return value: %TRUE if the selector has a palette.  %FALSE if it hasn't.
2100  **/
2101 gboolean
2102 gtk_color_selection_get_has_palette (GtkColorSelection *colorsel)
2103 {
2104   ColorSelectionPrivate *priv;
2105   
2106   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2107   
2108   priv = colorsel->private_data;
2109   
2110   return priv->has_palette;
2111 }
2112
2113 /**
2114  * gtk_color_selection_set_has_palette:
2115  * @colorsel: a #GtkColorSelection.
2116  * @has_palette: %TRUE if palette is to be visible, %FALSE otherwise.
2117  *
2118  * Shows and hides the palette based upon the value of @has_palette.
2119  * 
2120  **/
2121 void
2122 gtk_color_selection_set_has_palette (GtkColorSelection *colorsel,
2123                                      gboolean           has_palette)
2124 {
2125   ColorSelectionPrivate *priv;
2126   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2127   
2128   priv = colorsel->private_data;
2129   has_palette = has_palette != FALSE;
2130   
2131   if (priv->has_palette != has_palette)
2132     {
2133       priv->has_palette = has_palette;
2134       if (has_palette)
2135         gtk_widget_show (priv->palette_frame);
2136       else
2137         gtk_widget_hide (priv->palette_frame);
2138       
2139       g_object_notify (G_OBJECT (colorsel), "has_palette");
2140     }
2141 }
2142
2143 /**
2144  * gtk_color_selection_set_current_color:
2145  * @colorsel: a #GtkColorSelection.
2146  * @color: A #GdkColor to set the current color with.
2147  *
2148  * Sets the current color to be @color.  The first time this is called, it will
2149  * also set the original color to be @color too.
2150  **/
2151 void
2152 gtk_color_selection_set_current_color (GtkColorSelection *colorsel,
2153                                        GdkColor          *color)
2154 {
2155   ColorSelectionPrivate *priv;
2156   gint i;
2157   gboolean previous_changed;
2158   
2159   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2160
2161   priv = colorsel->private_data;
2162   priv->changing = TRUE;
2163   priv->color[COLORSEL_RED] = SCALE (color->red);
2164   priv->color[COLORSEL_GREEN] = SCALE (color->green);
2165   priv->color[COLORSEL_BLUE] = SCALE (color->blue);
2166   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2167                   priv->color[COLORSEL_GREEN],
2168                   priv->color[COLORSEL_BLUE],
2169                   &priv->color[COLORSEL_HUE],
2170                   &priv->color[COLORSEL_SATURATION],
2171                   &priv->color[COLORSEL_VALUE]);
2172   if (priv->default_set == FALSE)
2173     {
2174       previous_changed = TRUE;
2175       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2176         priv->old_color[i] = priv->color[i];
2177     }
2178   update_color (colorsel);
2179   priv->default_set = TRUE;
2180 }
2181
2182 /**
2183  * gtk_color_selection_set_current_alpha:
2184  * @colorsel: a #GtkColorSelection.
2185  * @alpha: an integer between 0 and 65535.
2186  *
2187  * Sets the current opacity to be @alpha.  The first time this is called, it will
2188  * also set the original opacity to be @alpha too.
2189  **/
2190 void
2191 gtk_color_selection_set_current_alpha (GtkColorSelection *colorsel,
2192                                        guint16            alpha)
2193 {
2194   ColorSelectionPrivate *priv;
2195   gint i;
2196   
2197   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2198   
2199   priv = colorsel->private_data;
2200   priv->changing = TRUE;
2201   priv->color[COLORSEL_OPACITY] = SCALE (alpha);
2202   if (priv->default_alpha_set == FALSE)
2203     {
2204       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2205         priv->old_color[i] = priv->color[i];
2206     }
2207   update_color (colorsel);
2208   priv->default_alpha_set = TRUE;
2209 }
2210
2211 /**
2212  * gtk_color_selection_set_color:
2213  * @colorsel: a #GtkColorSelection.
2214  * @color: an array of 4 doubles specifying the red, green, blue and opacity 
2215  *   to set the current color to.
2216  *
2217  * Sets the current color to be @color.  The first time this is called, it will
2218  * also set the original color to be @color too.
2219  *
2220  * This function is deprecated, use gtk_color_selection_set_current_color() instead.
2221  **/
2222 void
2223 gtk_color_selection_set_color (GtkColorSelection    *colorsel,
2224                                gdouble              *color)
2225 {
2226   ColorSelectionPrivate *priv;
2227   gint i;
2228   
2229   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2230   
2231   priv = colorsel->private_data;
2232   priv->changing = TRUE;
2233   priv->color[COLORSEL_RED] = color[0];
2234   priv->color[COLORSEL_GREEN] = color[1];
2235   priv->color[COLORSEL_BLUE] = color[2];
2236   priv->color[COLORSEL_OPACITY] = color[3];
2237   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2238                   priv->color[COLORSEL_GREEN],
2239                   priv->color[COLORSEL_BLUE],
2240                   &priv->color[COLORSEL_HUE],
2241                   &priv->color[COLORSEL_SATURATION],
2242                   &priv->color[COLORSEL_VALUE]);
2243   if (priv->default_set == FALSE)
2244     {
2245       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2246         priv->old_color[i] = priv->color[i];
2247     }
2248   update_color (colorsel);
2249   priv->default_set = TRUE;
2250   priv->default_alpha_set = TRUE;
2251 }
2252
2253 /**
2254  * gtk_color_selection_get_current_color:
2255  * @colorsel: a #GtkColorSelection.
2256  * @color: a #GdkColor to fill in with the current color.
2257  *
2258  * Sets @color to be the current color in the GtkColorSelection widget.
2259  **/
2260 void
2261 gtk_color_selection_get_current_color (GtkColorSelection *colorsel,
2262                                        GdkColor          *color)
2263 {
2264   ColorSelectionPrivate *priv;
2265   
2266   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2267   
2268   priv = colorsel->private_data;
2269   color->red = UNSCALE (priv->color[COLORSEL_RED]);
2270   color->green = UNSCALE (priv->color[COLORSEL_GREEN]);
2271   color->blue = UNSCALE (priv->color[COLORSEL_BLUE]);
2272 }
2273
2274 /**
2275  * gtk_color_selection_get_current_alpha:
2276  * @colorsel: a #GtkColorSelection.
2277  *
2278  * Returns the current alpha value.
2279  *
2280  * Return value: an integer between 0 and 65535.
2281  **/
2282 guint16
2283 gtk_color_selection_get_current_alpha (GtkColorSelection *colorsel)
2284 {
2285   ColorSelectionPrivate *priv;
2286   
2287   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2288   
2289   priv = colorsel->private_data;
2290   return priv->has_opacity ? UNSCALE (priv->color[COLORSEL_OPACITY]) : 65535;
2291 }
2292
2293 /**
2294  * gtk_color_selection_get_color:
2295  * @colorsel: a #GtkColorSelection.
2296  * @color: an array of 4 #gdouble to fill in with the current color.
2297  *
2298  * Sets @color to be the current color in the GtkColorSelection widget.
2299  * 
2300  * This function is deprecated, use gtk_color_selection_get_current_color() instead.
2301  **/
2302 void
2303 gtk_color_selection_get_color (GtkColorSelection *colorsel,
2304                                gdouble           *color)
2305 {
2306   ColorSelectionPrivate *priv;
2307   
2308   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2309   
2310   priv = colorsel->private_data;
2311   color[0] = priv->color[COLORSEL_RED];
2312   color[1] = priv->color[COLORSEL_GREEN];
2313   color[2] = priv->color[COLORSEL_BLUE];
2314   color[3] = priv->has_opacity ? priv->color[COLORSEL_OPACITY] : 65535;
2315 }
2316
2317 /**
2318  * gtk_color_selection_set_previous_color:
2319  * @colorsel: a #GtkColorSelection.
2320  * @color: a #GdkColor to set the previous color with.
2321  *
2322  * Sets the 'previous' color to be @color.  This function should be called with
2323  * some hesitations, as it might seem confusing to have that color change.
2324  * Calling gtk_color_selection_set_current_color() will also set this color the first
2325  * time it is called.
2326  **/
2327 void
2328 gtk_color_selection_set_previous_color (GtkColorSelection *colorsel,
2329                                         GdkColor          *color)
2330 {
2331   ColorSelectionPrivate *priv;
2332   
2333   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2334   
2335   priv = colorsel->private_data;
2336   priv->changing = TRUE;
2337   priv->old_color[COLORSEL_RED] = SCALE (color->red);
2338   priv->old_color[COLORSEL_GREEN] = SCALE (color->green);
2339   priv->old_color[COLORSEL_BLUE] = SCALE (color->blue);
2340   gtk_rgb_to_hsv (priv->old_color[COLORSEL_RED],
2341                   priv->old_color[COLORSEL_GREEN],
2342                   priv->old_color[COLORSEL_BLUE],
2343                   &priv->old_color[COLORSEL_HUE],
2344                   &priv->old_color[COLORSEL_SATURATION],
2345                   &priv->old_color[COLORSEL_VALUE]);
2346   color_sample_draw_samples (colorsel);
2347   priv->default_set = TRUE;
2348 }
2349
2350 /**
2351  * gtk_color_selection_set_previous_alpha:
2352  * @colorsel: a #GtkColorSelection.
2353  * @alpha: an integer between 0 and 65535.
2354  *
2355  * Sets the 'previous' alpha to be @alpha.  This function should be called with
2356  * some hesitations, as it might seem confusing to have that alpha change.
2357  **/
2358 void
2359 gtk_color_selection_set_previous_alpha (GtkColorSelection *colorsel,
2360                                         guint16            alpha)
2361 {
2362   ColorSelectionPrivate *priv;
2363   
2364   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2365   
2366   priv = colorsel->private_data;
2367   priv->changing = TRUE;
2368   priv->old_color[COLORSEL_OPACITY] = SCALE (alpha);
2369   color_sample_draw_samples (colorsel);
2370   priv->default_alpha_set = TRUE;
2371 }
2372
2373
2374 /**
2375  * gtk_color_selection_get_previous_color:
2376  * @colorsel: a #GtkColorSelection.
2377  * @color: a #GdkColor to fill in with the original color value.
2378  *
2379  * Fills @color in with the original color value.
2380  **/
2381 void
2382 gtk_color_selection_get_previous_color (GtkColorSelection *colorsel,
2383                                         GdkColor           *color)
2384 {
2385   ColorSelectionPrivate *priv;
2386   
2387   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2388   
2389   priv = colorsel->private_data;
2390   color->red = UNSCALE (priv->old_color[COLORSEL_RED]);
2391   color->green = UNSCALE (priv->old_color[COLORSEL_GREEN]);
2392   color->blue = UNSCALE (priv->old_color[COLORSEL_BLUE]);
2393 }
2394
2395 /**
2396  * gtk_color_selection_get_previous_alpha:
2397  * @colorsel: a #GtkColorSelection.
2398  *
2399  * Returns the previous alpha value.
2400  *
2401  * Return value: an integer between 0 and 65535.
2402  **/
2403 guint16
2404 gtk_color_selection_get_previous_alpha (GtkColorSelection *colorsel)
2405 {
2406   ColorSelectionPrivate *priv;
2407   
2408   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2409   
2410   priv = colorsel->private_data;
2411   return priv->has_opacity ? UNSCALE (priv->old_color[COLORSEL_OPACITY]) : 65535;
2412 }
2413
2414 /**
2415  * gtk_color_selection_set_palette_color:
2416  * @colorsel: a #GtkColorSelection.
2417  * @index: the color index of the palette.
2418  * @color: A #GdkColor to set the palette with.
2419  *
2420  * Sets the palette located at @index to have @color as its color.
2421  * 
2422  **/
2423 static void
2424 gtk_color_selection_set_palette_color (GtkColorSelection   *colorsel,
2425                                        gint                 index,
2426                                        GdkColor            *color)
2427 {
2428   ColorSelectionPrivate *priv;
2429   gint x, y;
2430   gdouble col[3];
2431   
2432   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2433   g_return_if_fail (index >= 0  && index < GTK_CUSTOM_PALETTE_WIDTH*GTK_CUSTOM_PALETTE_HEIGHT);
2434
2435   x = index % GTK_CUSTOM_PALETTE_WIDTH;
2436   y = index / GTK_CUSTOM_PALETTE_WIDTH;
2437   
2438   priv = colorsel->private_data;
2439   col[0] = SCALE (color->red);
2440   col[1] = SCALE (color->green);
2441   col[2] = SCALE (color->blue);
2442   
2443   palette_set_color (priv->custom_palette[x][y], colorsel, col);
2444 }
2445
2446 /**
2447  * gtk_color_selection_get_palette_color:
2448  * @colorsel: a #GtkColorSelection.
2449  * @index: the color index of the palette.
2450  * @color: a #GdkColor to fill in with the color value.
2451  * 
2452  * Set @color to have the color found in the palette at @index.  If
2453  * the palette is unset, it will leave the color unset.
2454  * 
2455  * Return value: %TRUE if the palette located at @index has a color set.  %FALSE
2456  * if it doesn't.
2457  **/
2458 static gboolean
2459 gtk_color_selection_get_palette_color (GtkColorSelection   *colorsel,
2460                                        gint                 index,
2461                                        GdkColor            *color)
2462 {
2463   ColorSelectionPrivate *priv;
2464   gint x, y;
2465   gdouble col[4];
2466   
2467   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2468   g_return_val_if_fail (index >= 0  && index < GTK_CUSTOM_PALETTE_WIDTH*GTK_CUSTOM_PALETTE_HEIGHT, FALSE);
2469   
2470   priv = colorsel->private_data;
2471
2472   x = index % GTK_CUSTOM_PALETTE_WIDTH;
2473   y = index / GTK_CUSTOM_PALETTE_WIDTH;
2474
2475   if (GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (priv->custom_palette[x][y]), "color_set")) == 0)
2476     return FALSE;
2477   
2478   palette_get_color (priv->custom_palette[x][y], col);
2479
2480   color->red = UNSCALE (col[0]);
2481   color->green = UNSCALE (col[1]);
2482   color->blue = UNSCALE (col[2]);
2483   
2484   return TRUE;
2485 }
2486
2487 /**
2488  * gtk_color_selection_unset_palette_color:
2489  * @colorsel: a #GtkColorSelection.
2490  * @index: the color index in the palette.
2491  *
2492  * Changes the palette located @index to have no color set.
2493  **/
2494 static void
2495 gtk_color_selection_unset_palette_color (GtkColorSelection   *colorsel,
2496                                          gint                 index)
2497 {
2498   ColorSelectionPrivate *priv;
2499   gint x, y;
2500   
2501   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2502   g_return_if_fail (index >= 0  && index < GTK_CUSTOM_PALETTE_WIDTH*GTK_CUSTOM_PALETTE_HEIGHT);
2503   
2504   x = index % GTK_CUSTOM_PALETTE_WIDTH;
2505   y = index / GTK_CUSTOM_PALETTE_WIDTH;
2506   
2507   priv = colorsel->private_data;
2508   palette_unset_color (priv->custom_palette[x][y]);
2509 }
2510
2511 /**
2512  * gtk_color_selection_get_current_alpha:
2513  * @colorsel: a #GtkColorSelection.
2514  *
2515  * Returns the maximum number of palette colors.
2516  *
2517  * Return value: the maximum number of palette indexes.
2518  **/
2519 static gint
2520 gtk_color_selection_get_palette_size (GtkColorSelection *colorsel)
2521 {
2522   return GTK_CUSTOM_PALETTE_WIDTH * GTK_CUSTOM_PALETTE_HEIGHT;
2523 }
2524
2525
2526 /**
2527  * gtk_color_selection_is_adjusting:
2528  * @colorsel: a #GtkColorSelection.
2529  *
2530  * Gets the current state of the @colorsel.
2531  *
2532  * Return value: %TRUE if the user is currently dragging a color around, and %FALSE
2533  * if the selection has stopped.
2534  **/
2535 gboolean
2536 gtk_color_selection_is_adjusting (GtkColorSelection *colorsel)
2537 {
2538   ColorSelectionPrivate *priv;
2539   
2540   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2541   
2542   priv = colorsel->private_data;
2543   
2544   return (gtk_hsv_is_adjusting (GTK_HSV (priv->triangle_colorsel)));
2545 }
2546
2547 static void
2548 gtk_color_selection_set_property (GObject         *object,
2549                                   guint            prop_id,
2550                                   const GValue    *value,
2551                                   GParamSpec      *pspec)
2552 {
2553   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (object);
2554   
2555   switch (prop_id)
2556     {
2557     case PROP_HAS_OPACITY_CONTROL:
2558       gtk_color_selection_set_has_opacity_control (colorsel, 
2559                                                    g_value_get_boolean (value));
2560       break;
2561     case PROP_HAS_PALETTE:
2562       gtk_color_selection_set_has_palette (colorsel, 
2563                                            g_value_get_boolean (value));
2564       break;
2565     case PROP_CURRENT_COLOR:
2566       gtk_color_selection_set_current_color (colorsel, g_value_get_boxed (value));
2567       break;
2568     case PROP_CURRENT_ALPHA:
2569       gtk_color_selection_set_current_alpha (colorsel, g_value_get_uint (value));
2570       break;
2571     default:
2572       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2573       break;
2574     }
2575   
2576 }
2577
2578 static void
2579 gtk_color_selection_get_property (GObject     *object,
2580                                   guint        prop_id,
2581                                   GValue      *value,
2582                                   GParamSpec  *pspec)
2583 {
2584   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (object);
2585   GdkColor color;
2586   
2587   switch (prop_id)
2588     {
2589     case PROP_HAS_OPACITY_CONTROL:
2590       g_value_set_boolean (value, gtk_color_selection_get_has_opacity_control (colorsel));
2591       break;
2592     case PROP_HAS_PALETTE:
2593       g_value_set_boolean (value, gtk_color_selection_get_has_palette (colorsel));
2594       break;
2595     case PROP_CURRENT_COLOR:
2596       gtk_color_selection_get_current_color (colorsel, &color);
2597       g_value_set_boxed (value, &color);
2598       break;
2599     case PROP_CURRENT_ALPHA:
2600       g_value_set_uint (value, gtk_color_selection_get_current_alpha (colorsel));
2601       break;
2602     default:
2603       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2604       break;
2605     }
2606 }
2607
2608
2609 /**
2610  * gtk_color_selection_palette_from_string:
2611  * @str: a string encoding a color palette.
2612  * @colors: return location for allocated array of #GdkColor.
2613  * @n_colors: return location for length of array.
2614  * 
2615  * Parses a color palette string; the string is a colon-separated
2616  * list of color names readable by gdk_color_parse().
2617  * 
2618  * Return value: %TRUE if a palette was successfully parsed.
2619  **/
2620 gboolean
2621 gtk_color_selection_palette_from_string (const gchar *str,
2622                                          GdkColor   **colors,
2623                                          gint        *n_colors)
2624 {
2625   GdkColor *retval;
2626   gint count;
2627   gchar *p;
2628   gchar *start;
2629   gchar *copy;
2630   
2631   count = 0;
2632   retval = NULL;
2633   copy = g_strdup (str);
2634
2635   start = copy;
2636   p = copy;
2637   while (TRUE)
2638     {
2639       if (*p == ':' || *p == '\0')
2640         {
2641           gboolean done = TRUE;
2642
2643           if (start == p)
2644             {
2645               goto failed; /* empty entry */
2646             }
2647               
2648           if (*p)
2649             {
2650               *p = '\0';
2651               done = FALSE;
2652             }
2653
2654           retval = g_renew (GdkColor, retval, count + 1);
2655           if (!gdk_color_parse (start, retval + count))
2656             {
2657               goto failed;
2658             }
2659
2660           ++count;
2661
2662           if (done)
2663             break;
2664           else
2665             start = p + 1;
2666         }
2667
2668       ++p;
2669     }
2670
2671   g_free (copy);
2672   
2673   if (colors)
2674     *colors = retval;
2675   else
2676     g_free (retval);
2677
2678   if (n_colors)
2679     *n_colors = count;
2680
2681   return TRUE;
2682   
2683  failed:
2684   g_free (copy);
2685   g_free (retval);
2686
2687   if (colors)
2688     *colors = NULL;
2689   if (n_colors)
2690     *n_colors = 0;
2691
2692   return FALSE;
2693 }
2694
2695 /**
2696  * gtk_color_selection_palette_to_string:
2697  * @colors: an array of colors.
2698  * @n_colors: length of the array.
2699  * 
2700  * Encodes a palette as a string, useful for persistent storage.
2701  * 
2702  * Return value: allocated string encoding the palette.
2703  **/
2704 gchar*
2705 gtk_color_selection_palette_to_string (const GdkColor *colors,
2706                                        gint            n_colors)
2707 {
2708   gint i;
2709   gchar **strs = NULL;
2710   gchar *retval;
2711   
2712   if (n_colors == 0)
2713     return g_strdup ("");
2714
2715   strs = g_new0 (gchar*, n_colors + 1);
2716
2717   i = 0;
2718   while (i < n_colors)
2719     {
2720       gchar *ptr;
2721       
2722       strs[i] =
2723         g_strdup_printf ("#%2X%2X%2X",
2724                          colors[i].red / 256,
2725                          colors[i].green / 256,
2726                          colors[i].blue / 256);
2727
2728       for (ptr = strs[i]; *ptr; ptr++)
2729         if (*ptr == ' ')
2730           *ptr = '0';
2731       
2732       ++i;
2733     }
2734
2735   retval = g_strjoinv (":", strs);
2736
2737   g_strfreev (strs);
2738
2739   return retval;
2740 }
2741
2742 /**
2743  * gtk_color_selection_set_change_palette_hook:
2744  * @func: a function to call when the custom palette needs saving.
2745  * 
2746  * Installs a global function to be called whenever the user tries to
2747  * modify the palette in a color selection. This function should save
2748  * the new palette contents, and update the GtkSettings property
2749  * "gtk-color-palette" so all GtkColorSelection widgets will be modified.
2750  * 
2751  * Return value: the previous change palette hook (that was replaced).
2752  **/
2753 GtkColorSelectionChangePaletteFunc
2754 gtk_color_selection_set_change_palette_hook (GtkColorSelectionChangePaletteFunc func)
2755 {
2756   GtkColorSelectionChangePaletteFunc old;
2757
2758   old = change_palette_hook;
2759
2760   change_palette_hook = func;
2761
2762   return old;
2763 }