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