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