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