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