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