]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorsel.c
Add a style property ::activate_slider that allows themes to draw the
[~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   guint has_grab : 1;
112   
113   gdouble color[COLORSEL_NUM_CHANNELS];
114   gdouble old_color[COLORSEL_NUM_CHANNELS];
115   
116   GtkWidget *triangle_colorsel;
117   GtkWidget *hue_spinbutton;
118   GtkWidget *sat_spinbutton;
119   GtkWidget *val_spinbutton;
120   GtkWidget *red_spinbutton;
121   GtkWidget *green_spinbutton;
122   GtkWidget *blue_spinbutton;
123   GtkWidget *opacity_slider;
124   GtkWidget *opacity_label;
125   GtkWidget *opacity_entry;
126   GtkWidget *palette_frame;
127   GtkWidget *hex_entry;
128   
129   /* The Palette code */
130   GtkWidget *custom_palette [GTK_CUSTOM_PALETTE_WIDTH][GTK_CUSTOM_PALETTE_HEIGHT];
131   
132   /* The color_sample stuff */
133   GtkWidget *sample_area;
134   GtkWidget *old_sample;
135   GtkWidget *cur_sample;
136   GtkWidget *colorsel;
137
138   /* Tooltips group */
139   GtkTooltips *tooltips;
140
141   /* Window for grabbing on */
142   GtkWidget *dropper_grab_widget;
143   guint32    grab_time;
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), 0.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    g_type_class_add_private (gobject_class, sizeof (ColorSelectionPrivate));
1912 }
1913
1914 /* widget functions */
1915
1916 static void
1917 gtk_color_selection_init (GtkColorSelection *colorsel)
1918 {
1919   GtkWidget *top_hbox;
1920   GtkWidget *top_right_vbox;
1921   GtkWidget *table, *label, *hbox, *frame, *vbox, *button;
1922   GtkAdjustment *adjust;
1923   GtkWidget *picker_image;
1924   gint i, j;
1925   ColorSelectionPrivate *priv;
1926   AtkObject *atk_obj;
1927   GList *focus_chain = NULL;
1928   
1929   gtk_widget_push_composite_child ();
1930
1931   priv = colorsel->private_data = G_TYPE_INSTANCE_GET_PRIVATE (colorsel, GTK_TYPE_COLOR_SELECTION, ColorSelectionPrivate);
1932   priv->changing = FALSE;
1933   priv->default_set = FALSE;
1934   priv->default_alpha_set = FALSE;
1935   
1936   priv->tooltips = gtk_tooltips_new ();
1937   g_object_ref_sink (priv->tooltips);
1938   
1939   top_hbox = gtk_hbox_new (FALSE, 12);
1940   gtk_box_pack_start (GTK_BOX (colorsel), top_hbox, FALSE, FALSE, 0);
1941   
1942   vbox = gtk_vbox_new (FALSE, 6);
1943   priv->triangle_colorsel = gtk_hsv_new ();
1944   g_signal_connect (priv->triangle_colorsel, "changed",
1945                     G_CALLBACK (hsv_changed), colorsel);
1946   gtk_hsv_set_metrics (GTK_HSV (priv->triangle_colorsel), 174, 15);
1947   gtk_box_pack_start (GTK_BOX (top_hbox), vbox, FALSE, FALSE, 0);
1948   gtk_box_pack_start (GTK_BOX (vbox), priv->triangle_colorsel, FALSE, FALSE, 0);
1949   gtk_tooltips_set_tip (priv->tooltips, priv->triangle_colorsel,
1950                         _("Select the color you want from the outer ring. Select the darkness or lightness of that color using the inner triangle."), NULL);
1951   
1952   hbox = gtk_hbox_new (FALSE, 6);
1953   gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1954   
1955   frame = gtk_frame_new (NULL);
1956   gtk_widget_set_size_request (frame, -1, 30);
1957   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
1958   color_sample_new (colorsel);
1959   gtk_container_add (GTK_CONTAINER (frame), priv->sample_area);
1960   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
1961   
1962   button = gtk_button_new ();
1963
1964   gtk_widget_set_events (button, GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
1965   g_object_set_data (G_OBJECT (button), I_("COLORSEL"), colorsel); 
1966   g_signal_connect (button, "clicked",
1967                     G_CALLBACK (get_screen_color), NULL);
1968   picker_image = gtk_image_new_from_stock (GTK_STOCK_COLOR_PICKER, GTK_ICON_SIZE_BUTTON);
1969   gtk_container_add (GTK_CONTAINER (button), picker_image);
1970   gtk_widget_show (GTK_WIDGET (picker_image));
1971   gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1972
1973   gtk_tooltips_set_tip (priv->tooltips,
1974                         button,
1975                         _("Click the eyedropper, then click a color anywhere on your screen to select that color."), NULL);
1976   
1977   top_right_vbox = gtk_vbox_new (FALSE, 6);
1978   gtk_box_pack_start (GTK_BOX (top_hbox), top_right_vbox, FALSE, FALSE, 0);
1979   table = gtk_table_new (8, 6, FALSE);
1980   gtk_box_pack_start (GTK_BOX (top_right_vbox), table, FALSE, FALSE, 0);
1981   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1982   gtk_table_set_col_spacings (GTK_TABLE (table), 12);
1983   
1984   make_label_spinbutton (colorsel, &priv->hue_spinbutton, _("_Hue:"), table, 0, 0, COLORSEL_HUE,
1985                          _("Position on the color wheel."));
1986   gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (priv->hue_spinbutton), TRUE);
1987   make_label_spinbutton (colorsel, &priv->sat_spinbutton, _("_Saturation:"), table, 0, 1, COLORSEL_SATURATION,
1988                          _("\"Deepness\" of the color."));
1989   make_label_spinbutton (colorsel, &priv->val_spinbutton, _("_Value:"), table, 0, 2, COLORSEL_VALUE,
1990                          _("Brightness of the color."));
1991   make_label_spinbutton (colorsel, &priv->red_spinbutton, _("_Red:"), table, 6, 0, COLORSEL_RED,
1992                          _("Amount of red light in the color."));
1993   make_label_spinbutton (colorsel, &priv->green_spinbutton, _("_Green:"), table, 6, 1, COLORSEL_GREEN,
1994                          _("Amount of green light in the color."));
1995   make_label_spinbutton (colorsel, &priv->blue_spinbutton, _("_Blue:"), table, 6, 2, COLORSEL_BLUE,
1996                          _("Amount of blue light in the color."));
1997   gtk_table_attach_defaults (GTK_TABLE (table), gtk_hseparator_new (), 0, 8, 3, 4); 
1998
1999   priv->opacity_label = gtk_label_new_with_mnemonic (_("Op_acity:")); 
2000   gtk_misc_set_alignment (GTK_MISC (priv->opacity_label), 0.0, 0.5); 
2001   gtk_table_attach_defaults (GTK_TABLE (table), priv->opacity_label, 0, 1, 4, 5); 
2002   adjust = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 1.0, 0.0)); 
2003   g_object_set_data (G_OBJECT (adjust), I_("COLORSEL"), colorsel); 
2004   priv->opacity_slider = gtk_hscale_new (adjust);
2005   gtk_tooltips_set_tip (priv->tooltips,
2006                         priv->opacity_slider,
2007                         _("Transparency of the color."), NULL);
2008   gtk_label_set_mnemonic_widget (GTK_LABEL (priv->opacity_label),
2009                                  priv->opacity_slider);
2010   gtk_scale_set_draw_value (GTK_SCALE (priv->opacity_slider), FALSE);
2011   g_signal_connect (adjust, "value_changed",
2012                     G_CALLBACK (adjustment_changed),
2013                     GINT_TO_POINTER (COLORSEL_OPACITY));
2014   gtk_table_attach_defaults (GTK_TABLE (table), priv->opacity_slider, 1, 7, 4, 5); 
2015   priv->opacity_entry = gtk_entry_new (); 
2016   gtk_tooltips_set_tip (priv->tooltips,
2017                         priv->opacity_entry,
2018                         _("Transparency of the color."), NULL);
2019   gtk_widget_set_size_request (priv->opacity_entry, 40, -1); 
2020
2021   g_signal_connect (priv->opacity_entry, "activate",
2022                     G_CALLBACK (opacity_entry_changed), colorsel);
2023   gtk_table_attach_defaults (GTK_TABLE (table), priv->opacity_entry, 7, 8, 4, 5);
2024   
2025   label = gtk_label_new_with_mnemonic (_("Color _name:"));
2026   gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, 5, 6);
2027   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
2028   priv->hex_entry = gtk_entry_new ();
2029
2030   gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->hex_entry);
2031
2032   g_signal_connect (priv->hex_entry, "activate",
2033                     G_CALLBACK (hex_changed), colorsel);
2034
2035   g_signal_connect (priv->hex_entry, "focus_out_event",
2036                     G_CALLBACK (hex_focus_out), colorsel);
2037
2038   gtk_tooltips_set_tip (priv->tooltips,
2039                         priv->hex_entry,
2040                         _("You can enter an HTML-style hexadecimal color value, or simply a color name such as 'orange' in this entry."), NULL);
2041   
2042   gtk_entry_set_width_chars (GTK_ENTRY (priv->hex_entry), 7);
2043   gtk_table_attach_defaults (GTK_TABLE (table), priv->hex_entry, 1, 5, 5, 6);
2044
2045   focus_chain = g_list_append (focus_chain, priv->hue_spinbutton);
2046   focus_chain = g_list_append (focus_chain, priv->sat_spinbutton);
2047   focus_chain = g_list_append (focus_chain, priv->val_spinbutton);
2048   focus_chain = g_list_append (focus_chain, priv->red_spinbutton);
2049   focus_chain = g_list_append (focus_chain, priv->green_spinbutton);
2050   focus_chain = g_list_append (focus_chain, priv->blue_spinbutton);
2051   focus_chain = g_list_append (focus_chain, priv->opacity_slider);
2052   focus_chain = g_list_append (focus_chain, priv->opacity_entry);
2053   focus_chain = g_list_append (focus_chain, priv->hex_entry);
2054   gtk_container_set_focus_chain (GTK_CONTAINER (table), focus_chain);
2055   g_list_free (focus_chain);
2056
2057   /* Set up the palette */
2058   table = gtk_table_new (GTK_CUSTOM_PALETTE_HEIGHT, GTK_CUSTOM_PALETTE_WIDTH, TRUE);
2059   gtk_table_set_row_spacings (GTK_TABLE (table), 1);
2060   gtk_table_set_col_spacings (GTK_TABLE (table), 1);
2061   for (i = 0; i < GTK_CUSTOM_PALETTE_WIDTH; i++)
2062     {
2063       for (j = 0; j < GTK_CUSTOM_PALETTE_HEIGHT; j++)
2064         {
2065           make_palette_frame (colorsel, table, i, j);
2066         }
2067     }
2068   set_selected_palette (colorsel, 0, 0);
2069   priv->palette_frame = gtk_vbox_new (FALSE, 6);
2070   label = gtk_label_new_with_mnemonic (_("_Palette:"));
2071   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
2072   gtk_box_pack_start (GTK_BOX (priv->palette_frame), label, FALSE, FALSE, 0);
2073
2074   gtk_label_set_mnemonic_widget (GTK_LABEL (label),
2075                                  priv->custom_palette[0][0]);
2076   
2077   gtk_box_pack_end (GTK_BOX (top_right_vbox), priv->palette_frame, FALSE, FALSE, 0);
2078   gtk_box_pack_start (GTK_BOX (priv->palette_frame), table, FALSE, FALSE, 0);
2079   
2080   gtk_widget_show_all (top_hbox);
2081
2082   /* hide unused stuff */
2083   
2084   if (priv->has_opacity == FALSE)
2085     {
2086       gtk_widget_hide (priv->opacity_label);
2087       gtk_widget_hide (priv->opacity_slider);
2088       gtk_widget_hide (priv->opacity_entry);
2089     }
2090   
2091   if (priv->has_palette == FALSE)
2092     {
2093       gtk_widget_hide (priv->palette_frame);
2094     }
2095
2096   atk_obj = gtk_widget_get_accessible (priv->triangle_colorsel);
2097   if (GTK_IS_ACCESSIBLE (atk_obj))
2098     {
2099       atk_object_set_name (atk_obj, _("Color Wheel"));
2100       atk_object_set_role (gtk_widget_get_accessible (GTK_WIDGET (colorsel)), ATK_ROLE_COLOR_CHOOSER);
2101       make_all_relations (atk_obj, priv);
2102     } 
2103
2104   gtk_widget_pop_composite_child ();
2105 }
2106
2107 static void
2108 gtk_color_selection_destroy (GtkObject *object)
2109 {
2110   GtkColorSelection *cselection = GTK_COLOR_SELECTION (object);
2111   ColorSelectionPrivate *priv = cselection->private_data;
2112
2113   if (priv->dropper_grab_widget)
2114     {
2115       gtk_widget_destroy (priv->dropper_grab_widget);
2116       priv->dropper_grab_widget = NULL;
2117     }
2118
2119   if (priv->tooltips)
2120     {
2121       g_object_unref (priv->tooltips);
2122       priv->tooltips = NULL;
2123     }
2124   
2125   GTK_OBJECT_CLASS (parent_class)->destroy (object);
2126 }
2127
2128 static void
2129 gtk_color_selection_finalize (GObject *object)
2130 {
2131   G_OBJECT_CLASS (parent_class)->finalize (object);
2132 }
2133
2134 static void
2135 gtk_color_selection_realize (GtkWidget *widget)
2136 {
2137   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (widget);
2138   ColorSelectionPrivate *priv = colorsel->private_data;
2139   GtkSettings *settings = gtk_widget_get_settings (widget);
2140
2141   priv->settings_connection =  g_signal_connect (settings,
2142                                                  "notify::gtk-color-palette",
2143                                                  G_CALLBACK (palette_change_notify_instance),
2144                                                  widget);
2145   update_palette (colorsel);
2146
2147   GTK_WIDGET_CLASS (parent_class)->realize (widget);
2148 }
2149
2150 static void
2151 gtk_color_selection_unrealize (GtkWidget *widget)
2152 {
2153   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (widget);
2154   ColorSelectionPrivate *priv = colorsel->private_data;
2155   GtkSettings *settings = gtk_widget_get_settings (widget);
2156
2157   g_signal_handler_disconnect (settings, priv->settings_connection);
2158
2159   GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
2160 }
2161
2162 /* We override show-all since we have internal widgets that
2163  * shouldn't be shown when you call show_all(), like the
2164  * palette and opacity sliders.
2165  */
2166 static void
2167 gtk_color_selection_show_all (GtkWidget *widget)
2168 {
2169   gtk_widget_show (widget);
2170 }
2171
2172 /**
2173  * gtk_color_selection_new:
2174  * 
2175  * Creates a new GtkColorSelection.
2176  * 
2177  * Return value: a new #GtkColorSelection
2178  **/
2179 GtkWidget *
2180 gtk_color_selection_new (void)
2181 {
2182   GtkColorSelection *colorsel;
2183   ColorSelectionPrivate *priv;
2184   gdouble color[4];
2185   color[0] = 1.0;
2186   color[1] = 1.0;
2187   color[2] = 1.0;
2188   color[3] = 1.0;
2189   
2190   colorsel = g_object_new (GTK_TYPE_COLOR_SELECTION, NULL);
2191   priv = colorsel->private_data;
2192   set_color_internal (colorsel, color);
2193   gtk_color_selection_set_has_opacity_control (colorsel, TRUE);
2194   
2195   /* We want to make sure that default_set is FALSE */
2196   /* This way the user can still set it */
2197   priv->default_set = FALSE;
2198   priv->default_alpha_set = FALSE;
2199   
2200   return GTK_WIDGET (colorsel);
2201 }
2202
2203
2204 void
2205 gtk_color_selection_set_update_policy (GtkColorSelection *colorsel,
2206                                        GtkUpdateType      policy)
2207 {
2208   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2209 }
2210
2211 /**
2212  * gtk_color_selection_get_has_opacity_control:
2213  * @colorsel: a #GtkColorSelection.
2214  * 
2215  * Determines whether the colorsel has an opacity control.
2216  * 
2217  * Return value: %TRUE if the @colorsel has an opacity control.  %FALSE if it does't.
2218  **/
2219 gboolean
2220 gtk_color_selection_get_has_opacity_control (GtkColorSelection *colorsel)
2221 {
2222   ColorSelectionPrivate *priv;
2223   
2224   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2225   
2226   priv = colorsel->private_data;
2227   
2228   return priv->has_opacity;
2229 }
2230
2231 /**
2232  * gtk_color_selection_set_has_opacity_control:
2233  * @colorsel: a #GtkColorSelection.
2234  * @has_opacity: %TRUE if @colorsel can set the opacity, %FALSE otherwise.
2235  *
2236  * Sets the @colorsel to use or not use opacity.
2237  * 
2238  **/
2239 void
2240 gtk_color_selection_set_has_opacity_control (GtkColorSelection *colorsel,
2241                                              gboolean           has_opacity)
2242 {
2243   ColorSelectionPrivate *priv;
2244   
2245   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2246   
2247   priv = colorsel->private_data;
2248   has_opacity = has_opacity != FALSE;
2249   
2250   if (priv->has_opacity != has_opacity)
2251     {
2252       priv->has_opacity = has_opacity;
2253       if (has_opacity)
2254         {
2255           gtk_widget_show (priv->opacity_slider);
2256           gtk_widget_show (priv->opacity_label);
2257           gtk_widget_show (priv->opacity_entry);
2258         }
2259       else
2260         {
2261           gtk_widget_hide (priv->opacity_slider);
2262           gtk_widget_hide (priv->opacity_label);
2263           gtk_widget_hide (priv->opacity_entry);
2264         }
2265       color_sample_update_samples (colorsel);
2266       
2267       g_object_notify (G_OBJECT (colorsel), "has-opacity-control");
2268     }
2269 }
2270
2271 /**
2272  * gtk_color_selection_get_has_palette:
2273  * @colorsel: a #GtkColorSelection.
2274  * 
2275  * Determines whether the color selector has a color palette.
2276  * 
2277  * Return value: %TRUE if the selector has a palette.  %FALSE if it hasn't.
2278  **/
2279 gboolean
2280 gtk_color_selection_get_has_palette (GtkColorSelection *colorsel)
2281 {
2282   ColorSelectionPrivate *priv;
2283   
2284   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2285   
2286   priv = colorsel->private_data;
2287   
2288   return priv->has_palette;
2289 }
2290
2291 /**
2292  * gtk_color_selection_set_has_palette:
2293  * @colorsel: a #GtkColorSelection.
2294  * @has_palette: %TRUE if palette is to be visible, %FALSE otherwise.
2295  *
2296  * Shows and hides the palette based upon the value of @has_palette.
2297  * 
2298  **/
2299 void
2300 gtk_color_selection_set_has_palette (GtkColorSelection *colorsel,
2301                                      gboolean           has_palette)
2302 {
2303   ColorSelectionPrivate *priv;
2304   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2305   
2306   priv = colorsel->private_data;
2307   has_palette = has_palette != FALSE;
2308   
2309   if (priv->has_palette != has_palette)
2310     {
2311       priv->has_palette = has_palette;
2312       if (has_palette)
2313         gtk_widget_show (priv->palette_frame);
2314       else
2315         gtk_widget_hide (priv->palette_frame);
2316       
2317       g_object_notify (G_OBJECT (colorsel), "has-palette");
2318     }
2319 }
2320
2321 /**
2322  * gtk_color_selection_set_current_color:
2323  * @colorsel: a #GtkColorSelection.
2324  * @color: A #GdkColor to set the current color with.
2325  *
2326  * Sets the current color to be @color.  The first time this is called, it will
2327  * also set the original color to be @color too.
2328  **/
2329 void
2330 gtk_color_selection_set_current_color (GtkColorSelection *colorsel,
2331                                        const GdkColor    *color)
2332 {
2333   ColorSelectionPrivate *priv;
2334   gint i;
2335   
2336   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2337   g_return_if_fail (color != NULL);
2338
2339   priv = colorsel->private_data;
2340   priv->changing = TRUE;
2341   priv->color[COLORSEL_RED] = SCALE (color->red);
2342   priv->color[COLORSEL_GREEN] = SCALE (color->green);
2343   priv->color[COLORSEL_BLUE] = SCALE (color->blue);
2344   gtk_rgb_to_hsv (priv->color[COLORSEL_RED],
2345                   priv->color[COLORSEL_GREEN],
2346                   priv->color[COLORSEL_BLUE],
2347                   &priv->color[COLORSEL_HUE],
2348                   &priv->color[COLORSEL_SATURATION],
2349                   &priv->color[COLORSEL_VALUE]);
2350   if (priv->default_set == FALSE)
2351     {
2352       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2353         priv->old_color[i] = priv->color[i];
2354     }
2355   priv->default_set = TRUE;
2356   update_color (colorsel);
2357 }
2358
2359 /**
2360  * gtk_color_selection_set_current_alpha:
2361  * @colorsel: a #GtkColorSelection.
2362  * @alpha: an integer between 0 and 65535.
2363  *
2364  * Sets the current opacity to be @alpha.  The first time this is called, it will
2365  * also set the original opacity to be @alpha too.
2366  **/
2367 void
2368 gtk_color_selection_set_current_alpha (GtkColorSelection *colorsel,
2369                                        guint16            alpha)
2370 {
2371   ColorSelectionPrivate *priv;
2372   gint i;
2373   
2374   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2375   
2376   priv = colorsel->private_data;
2377   priv->changing = TRUE;
2378   priv->color[COLORSEL_OPACITY] = SCALE (alpha);
2379   if (priv->default_alpha_set == FALSE)
2380     {
2381       for (i = 0; i < COLORSEL_NUM_CHANNELS; i++)
2382         priv->old_color[i] = priv->color[i];
2383     }
2384   priv->default_alpha_set = TRUE;
2385   update_color (colorsel);
2386 }
2387
2388 /**
2389  * gtk_color_selection_set_color:
2390  * @colorsel: a #GtkColorSelection.
2391  * @color: an array of 4 doubles specifying the red, green, blue and opacity 
2392  *   to set the current color to.
2393  *
2394  * Sets the current color to be @color.  The first time this is called, it will
2395  * also set the original color to be @color too.
2396  *
2397  * Deprecated: Use gtk_color_selection_set_current_color() instead.
2398  **/
2399 void
2400 gtk_color_selection_set_color (GtkColorSelection    *colorsel,
2401                                gdouble              *color)
2402 {
2403   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2404
2405   set_color_internal (colorsel, color);
2406 }
2407
2408 /**
2409  * gtk_color_selection_get_current_color:
2410  * @colorsel: a #GtkColorSelection.
2411  * @color: a #GdkColor to fill in with the current color.
2412  *
2413  * Sets @color to be the current color in the GtkColorSelection widget.
2414  **/
2415 void
2416 gtk_color_selection_get_current_color (GtkColorSelection *colorsel,
2417                                        GdkColor          *color)
2418 {
2419   ColorSelectionPrivate *priv;
2420   
2421   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2422   g_return_if_fail (color != NULL);
2423   
2424   priv = colorsel->private_data;
2425   color->red = UNSCALE (priv->color[COLORSEL_RED]);
2426   color->green = UNSCALE (priv->color[COLORSEL_GREEN]);
2427   color->blue = UNSCALE (priv->color[COLORSEL_BLUE]);
2428 }
2429
2430 /**
2431  * gtk_color_selection_get_current_alpha:
2432  * @colorsel: a #GtkColorSelection.
2433  *
2434  * Returns the current alpha value.
2435  *
2436  * Return value: an integer between 0 and 65535.
2437  **/
2438 guint16
2439 gtk_color_selection_get_current_alpha (GtkColorSelection *colorsel)
2440 {
2441   ColorSelectionPrivate *priv;
2442   
2443   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2444   
2445   priv = colorsel->private_data;
2446   return priv->has_opacity ? UNSCALE (priv->color[COLORSEL_OPACITY]) : 65535;
2447 }
2448
2449 /**
2450  * gtk_color_selection_get_color:
2451  * @colorsel: a #GtkColorSelection.
2452  * @color: an array of 4 #gdouble to fill in with the current color.
2453  *
2454  * Sets @color to be the current color in the GtkColorSelection widget.
2455  * 
2456  * This function is deprecated, use gtk_color_selection_get_current_color() instead.
2457  **/
2458 void
2459 gtk_color_selection_get_color (GtkColorSelection *colorsel,
2460                                gdouble           *color)
2461 {
2462   ColorSelectionPrivate *priv;
2463   
2464   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2465   
2466   priv = colorsel->private_data;
2467   color[0] = priv->color[COLORSEL_RED];
2468   color[1] = priv->color[COLORSEL_GREEN];
2469   color[2] = priv->color[COLORSEL_BLUE];
2470   color[3] = priv->has_opacity ? priv->color[COLORSEL_OPACITY] : 65535;
2471 }
2472
2473 /**
2474  * gtk_color_selection_set_previous_color:
2475  * @colorsel: a #GtkColorSelection.
2476  * @color: a #GdkColor to set the previous color with.
2477  *
2478  * Sets the 'previous' color to be @color.  This function should be called with
2479  * some hesitations, as it might seem confusing to have that color change.
2480  * Calling gtk_color_selection_set_current_color() will also set this color the first
2481  * time it is called.
2482  **/
2483 void
2484 gtk_color_selection_set_previous_color (GtkColorSelection *colorsel,
2485                                         const GdkColor    *color)
2486 {
2487   ColorSelectionPrivate *priv;
2488   
2489   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2490   g_return_if_fail (color != NULL);
2491   
2492   priv = colorsel->private_data;
2493   priv->changing = TRUE;
2494   priv->old_color[COLORSEL_RED] = SCALE (color->red);
2495   priv->old_color[COLORSEL_GREEN] = SCALE (color->green);
2496   priv->old_color[COLORSEL_BLUE] = SCALE (color->blue);
2497   gtk_rgb_to_hsv (priv->old_color[COLORSEL_RED],
2498                   priv->old_color[COLORSEL_GREEN],
2499                   priv->old_color[COLORSEL_BLUE],
2500                   &priv->old_color[COLORSEL_HUE],
2501                   &priv->old_color[COLORSEL_SATURATION],
2502                   &priv->old_color[COLORSEL_VALUE]);
2503   color_sample_update_samples (colorsel);
2504   priv->default_set = TRUE;
2505   priv->changing = FALSE;
2506 }
2507
2508 /**
2509  * gtk_color_selection_set_previous_alpha:
2510  * @colorsel: a #GtkColorSelection.
2511  * @alpha: an integer between 0 and 65535.
2512  *
2513  * Sets the 'previous' alpha to be @alpha.  This function should be called with
2514  * some hesitations, as it might seem confusing to have that alpha change.
2515  **/
2516 void
2517 gtk_color_selection_set_previous_alpha (GtkColorSelection *colorsel,
2518                                         guint16            alpha)
2519 {
2520   ColorSelectionPrivate *priv;
2521   
2522   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2523   
2524   priv = colorsel->private_data;
2525   priv->changing = TRUE;
2526   priv->old_color[COLORSEL_OPACITY] = SCALE (alpha);
2527   color_sample_update_samples (colorsel);
2528   priv->default_alpha_set = TRUE;
2529   priv->changing = FALSE;
2530 }
2531
2532
2533 /**
2534  * gtk_color_selection_get_previous_color:
2535  * @colorsel: a #GtkColorSelection.
2536  * @color: a #GdkColor to fill in with the original color value.
2537  *
2538  * Fills @color in with the original color value.
2539  **/
2540 void
2541 gtk_color_selection_get_previous_color (GtkColorSelection *colorsel,
2542                                         GdkColor           *color)
2543 {
2544   ColorSelectionPrivate *priv;
2545   
2546   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2547   g_return_if_fail (color != NULL);
2548   
2549   priv = colorsel->private_data;
2550   color->red = UNSCALE (priv->old_color[COLORSEL_RED]);
2551   color->green = UNSCALE (priv->old_color[COLORSEL_GREEN]);
2552   color->blue = UNSCALE (priv->old_color[COLORSEL_BLUE]);
2553 }
2554
2555 /**
2556  * gtk_color_selection_get_previous_alpha:
2557  * @colorsel: a #GtkColorSelection.
2558  *
2559  * Returns the previous alpha value.
2560  *
2561  * Return value: an integer between 0 and 65535.
2562  **/
2563 guint16
2564 gtk_color_selection_get_previous_alpha (GtkColorSelection *colorsel)
2565 {
2566   ColorSelectionPrivate *priv;
2567   
2568   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), 0);
2569   
2570   priv = colorsel->private_data;
2571   return priv->has_opacity ? UNSCALE (priv->old_color[COLORSEL_OPACITY]) : 65535;
2572 }
2573
2574 /**
2575  * gtk_color_selection_set_palette_color:
2576  * @colorsel: a #GtkColorSelection.
2577  * @index: the color index of the palette.
2578  * @color: A #GdkColor to set the palette with.
2579  *
2580  * Sets the palette located at @index to have @color as its color.
2581  * 
2582  **/
2583 static void
2584 gtk_color_selection_set_palette_color (GtkColorSelection   *colorsel,
2585                                        gint                 index,
2586                                        GdkColor            *color)
2587 {
2588   ColorSelectionPrivate *priv;
2589   gint x, y;
2590   gdouble col[3];
2591   
2592   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
2593   g_return_if_fail (index >= 0  && index < GTK_CUSTOM_PALETTE_WIDTH*GTK_CUSTOM_PALETTE_HEIGHT);
2594
2595   x = index % GTK_CUSTOM_PALETTE_WIDTH;
2596   y = index / GTK_CUSTOM_PALETTE_WIDTH;
2597   
2598   priv = colorsel->private_data;
2599   col[0] = SCALE (color->red);
2600   col[1] = SCALE (color->green);
2601   col[2] = SCALE (color->blue);
2602   
2603   palette_set_color (priv->custom_palette[x][y], colorsel, col);
2604 }
2605
2606 /**
2607  * gtk_color_selection_is_adjusting:
2608  * @colorsel: a #GtkColorSelection.
2609  *
2610  * Gets the current state of the @colorsel.
2611  *
2612  * Return value: %TRUE if the user is currently dragging a color around, and %FALSE
2613  * if the selection has stopped.
2614  **/
2615 gboolean
2616 gtk_color_selection_is_adjusting (GtkColorSelection *colorsel)
2617 {
2618   ColorSelectionPrivate *priv;
2619   
2620   g_return_val_if_fail (GTK_IS_COLOR_SELECTION (colorsel), FALSE);
2621   
2622   priv = colorsel->private_data;
2623   
2624   return (gtk_hsv_is_adjusting (GTK_HSV (priv->triangle_colorsel)));
2625 }
2626
2627 static void
2628 gtk_color_selection_set_property (GObject         *object,
2629                                   guint            prop_id,
2630                                   const GValue    *value,
2631                                   GParamSpec      *pspec)
2632 {
2633   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (object);
2634   
2635   switch (prop_id)
2636     {
2637     case PROP_HAS_OPACITY_CONTROL:
2638       gtk_color_selection_set_has_opacity_control (colorsel, 
2639                                                    g_value_get_boolean (value));
2640       break;
2641     case PROP_HAS_PALETTE:
2642       gtk_color_selection_set_has_palette (colorsel, 
2643                                            g_value_get_boolean (value));
2644       break;
2645     case PROP_CURRENT_COLOR:
2646       gtk_color_selection_set_current_color (colorsel, g_value_get_boxed (value));
2647       break;
2648     case PROP_CURRENT_ALPHA:
2649       gtk_color_selection_set_current_alpha (colorsel, g_value_get_uint (value));
2650       break;
2651     default:
2652       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2653       break;
2654     }
2655   
2656 }
2657
2658 static void
2659 gtk_color_selection_get_property (GObject     *object,
2660                                   guint        prop_id,
2661                                   GValue      *value,
2662                                   GParamSpec  *pspec)
2663 {
2664   GtkColorSelection *colorsel = GTK_COLOR_SELECTION (object);
2665   GdkColor color;
2666   
2667   switch (prop_id)
2668     {
2669     case PROP_HAS_OPACITY_CONTROL:
2670       g_value_set_boolean (value, gtk_color_selection_get_has_opacity_control (colorsel));
2671       break;
2672     case PROP_HAS_PALETTE:
2673       g_value_set_boolean (value, gtk_color_selection_get_has_palette (colorsel));
2674       break;
2675     case PROP_CURRENT_COLOR:
2676       gtk_color_selection_get_current_color (colorsel, &color);
2677       g_value_set_boxed (value, &color);
2678       break;
2679     case PROP_CURRENT_ALPHA:
2680       g_value_set_uint (value, gtk_color_selection_get_current_alpha (colorsel));
2681       break;
2682     default:
2683       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2684       break;
2685     }
2686 }
2687
2688
2689 /**
2690  * gtk_color_selection_palette_from_string:
2691  * @str: a string encoding a color palette.
2692  * @colors: return location for allocated array of #GdkColor.
2693  * @n_colors: return location for length of array.
2694  * 
2695  * Parses a color palette string; the string is a colon-separated
2696  * list of color names readable by gdk_color_parse().
2697  * 
2698  * Return value: %TRUE if a palette was successfully parsed.
2699  **/
2700 gboolean
2701 gtk_color_selection_palette_from_string (const gchar *str,
2702                                          GdkColor   **colors,
2703                                          gint        *n_colors)
2704 {
2705   GdkColor *retval;
2706   gint count;
2707   gchar *p;
2708   gchar *start;
2709   gchar *copy;
2710   
2711   count = 0;
2712   retval = NULL;
2713   copy = g_strdup (str);
2714
2715   start = copy;
2716   p = copy;
2717   while (TRUE)
2718     {
2719       if (*p == ':' || *p == '\0')
2720         {
2721           gboolean done = TRUE;
2722
2723           if (start == p)
2724             {
2725               goto failed; /* empty entry */
2726             }
2727               
2728           if (*p)
2729             {
2730               *p = '\0';
2731               done = FALSE;
2732             }
2733
2734           retval = g_renew (GdkColor, retval, count + 1);
2735           if (!gdk_color_parse (start, retval + count))
2736             {
2737               goto failed;
2738             }
2739
2740           ++count;
2741
2742           if (done)
2743             break;
2744           else
2745             start = p + 1;
2746         }
2747
2748       ++p;
2749     }
2750
2751   g_free (copy);
2752   
2753   if (colors)
2754     *colors = retval;
2755   else
2756     g_free (retval);
2757
2758   if (n_colors)
2759     *n_colors = count;
2760
2761   return TRUE;
2762   
2763  failed:
2764   g_free (copy);
2765   g_free (retval);
2766
2767   if (colors)
2768     *colors = NULL;
2769   if (n_colors)
2770     *n_colors = 0;
2771
2772   return FALSE;
2773 }
2774
2775 /**
2776  * gtk_color_selection_palette_to_string:
2777  * @colors: an array of colors.
2778  * @n_colors: length of the array.
2779  * 
2780  * Encodes a palette as a string, useful for persistent storage.
2781  * 
2782  * Return value: allocated string encoding the palette.
2783  **/
2784 gchar*
2785 gtk_color_selection_palette_to_string (const GdkColor *colors,
2786                                        gint            n_colors)
2787 {
2788   gint i;
2789   gchar **strs = NULL;
2790   gchar *retval;
2791   
2792   if (n_colors == 0)
2793     return g_strdup ("");
2794
2795   strs = g_new0 (gchar*, n_colors + 1);
2796
2797   i = 0;
2798   while (i < n_colors)
2799     {
2800       gchar *ptr;
2801       
2802       strs[i] =
2803         g_strdup_printf ("#%2X%2X%2X",
2804                          colors[i].red / 256,
2805                          colors[i].green / 256,
2806                          colors[i].blue / 256);
2807
2808       for (ptr = strs[i]; *ptr; ptr++)
2809         if (*ptr == ' ')
2810           *ptr = '0';
2811       
2812       ++i;
2813     }
2814
2815   retval = g_strjoinv (":", strs);
2816
2817   g_strfreev (strs);
2818
2819   return retval;
2820 }
2821
2822 /**
2823  * gtk_color_selection_set_change_palette_hook:
2824  * @func: a function to call when the custom palette needs saving.
2825  * 
2826  * Installs a global function to be called whenever the user tries to
2827  * modify the palette in a color selection. This function should save
2828  * the new palette contents, and update the GtkSettings property
2829  * "gtk-color-palette" so all GtkColorSelection widgets will be modified.
2830  *
2831  * Return value: the previous change palette hook (that was replaced).
2832  *
2833  * Deprecated: This function is deprecated in favor of 
2834  * gtk_color_selection_set_change_palette_with_screen_hook(), and does
2835  * not work in multihead environments.
2836  * 
2837  **/
2838 GtkColorSelectionChangePaletteFunc
2839 gtk_color_selection_set_change_palette_hook (GtkColorSelectionChangePaletteFunc func)
2840 {
2841   GtkColorSelectionChangePaletteFunc old;
2842
2843   old = noscreen_change_palette_hook;
2844
2845   noscreen_change_palette_hook = func;
2846
2847   return old;
2848 }
2849
2850 /**
2851  * gtk_color_selection_set_change_palette_with_screen_hook:
2852  * @func: a function to call when the custom palette needs saving.
2853  * 
2854  * Installs a global function to be called whenever the user tries to
2855  * modify the palette in a color selection. This function should save
2856  * the new palette contents, and update the GtkSettings property
2857  * "gtk-color-palette" so all GtkColorSelection widgets will be modified.
2858  * 
2859  * Return value: the previous change palette hook (that was replaced).
2860  *
2861  * Since: 2.2
2862  **/
2863 GtkColorSelectionChangePaletteWithScreenFunc
2864 gtk_color_selection_set_change_palette_with_screen_hook (GtkColorSelectionChangePaletteWithScreenFunc func)
2865 {
2866   GtkColorSelectionChangePaletteWithScreenFunc old;
2867
2868   old = change_palette_hook;
2869
2870   change_palette_hook = func;
2871
2872   return old;
2873 }
2874
2875 static void
2876 make_control_relations (AtkObject *atk_obj,
2877                         GtkWidget *widget)
2878 {
2879   AtkObject *obj;
2880
2881   obj = gtk_widget_get_accessible (widget);
2882   atk_object_add_relationship (atk_obj, ATK_RELATION_CONTROLLED_BY, obj);
2883   atk_object_add_relationship (obj, ATK_RELATION_CONTROLLER_FOR, atk_obj);
2884 }
2885
2886 static void
2887 make_all_relations (AtkObject *atk_obj,
2888                     ColorSelectionPrivate *priv)
2889 {
2890   make_control_relations (atk_obj, priv->hue_spinbutton);
2891   make_control_relations (atk_obj, priv->sat_spinbutton);
2892   make_control_relations (atk_obj, priv->val_spinbutton);
2893   make_control_relations (atk_obj, priv->red_spinbutton);
2894   make_control_relations (atk_obj, priv->green_spinbutton);
2895   make_control_relations (atk_obj, priv->blue_spinbutton);
2896 }
2897
2898 #define __GTK_COLOR_SELECTION_C__
2899 #include "gtkaliasdef.c"
2900