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