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