]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorsel.c
72660d3d5e7a2ba5cb2ecf2dd7a299ac80f5a18a
[~andy/gtk] / gtk / gtkcolorsel.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <math.h>
30 #include <gdk/gdk.h>
31 #include "gtkcolorsel.h"
32 #include "gtkwindow.h"
33 #include "gtkhbbox.h"
34 #include "gtkintl.h"
35 #include "gtkdnd.h"
36 #include "gtkselection.h"
37
38 /*
39  * If you change the way the color values are stored,
40  * please make sure to update the drag & drop support so it sends
41  * across all the color info (currently RGBA). - Elliot
42  */
43
44 #ifndef M_PI
45 #define M_PI    3.14159265358979323846
46 #endif /* M_PI */
47
48 #define DEGTORAD(a) (2.0*M_PI*a/360.0)
49 #define SQR(a) (a*a)
50
51 #define TIMER_DELAY 300
52
53 #define CIRCLE_RADIUS 65
54
55 #define WHEEL_WIDTH   2*CIRCLE_RADIUS+2
56 #define WHEEL_HEIGHT  2*CIRCLE_RADIUS+2
57
58 #define VALUE_WIDTH   32
59 #define VALUE_HEIGHT  WHEEL_HEIGHT
60
61 #define SAMPLE_WIDTH  WHEEL_WIDTH+VALUE_WIDTH+5
62 #define SAMPLE_HEIGHT 28
63
64 static void gtk_color_selection_class_init (GtkColorSelectionClass *klass);
65 static void gtk_color_selection_init (GtkColorSelection *colorsel);
66 static void gtk_color_selection_dialog_class_init(GtkColorSelectionDialogClass *klass);
67 static void gtk_color_selection_dialog_init(GtkColorSelectionDialog *colorseldiag);
68
69 enum
70 {
71   COLOR_CHANGED,
72   LAST_SIGNAL
73 };
74
75 enum
76 {
77   RGB_INPUTS     = 1 << 0,
78   HSV_INPUTS     = 1 << 1,
79   OPACITY_INPUTS = 1 << 2
80 };
81
82 enum
83 {
84   SCALE,
85   ENTRY,
86   BOTH
87 };
88
89 enum
90 {
91   HUE,
92   SATURATION,
93   VALUE,
94   RED,
95   GREEN,
96   BLUE,
97   OPACITY,
98   NUM_CHANNELS
99 };
100
101 typedef struct
102 {
103   gchar *label;
104   gfloat lower, upper, step_inc, page_inc;
105   GtkSignalFunc updater;
106 } scale_val_type;
107
108
109 #define HSV_TO_RGB()  gtk_color_selection_hsv_to_rgb( \
110                         colorsel->values[HUE], \
111                         colorsel->values[SATURATION], \
112                         colorsel->values[VALUE], \
113                         &colorsel->values[RED], \
114                         &colorsel->values[GREEN], \
115                         &colorsel->values[BLUE])
116
117 #define RGB_TO_HSV()  gtk_color_selection_rgb_to_hsv( \
118                         colorsel->values[RED], \
119                         colorsel->values[GREEN], \
120                         colorsel->values[BLUE], \
121                         &colorsel->values[HUE], \
122                         &colorsel->values[SATURATION], \
123                         &colorsel->values[VALUE])
124
125
126 static void gtk_color_selection_hsv_updater       (GtkWidget         *widget,
127                                                    gpointer           data);
128 static void gtk_color_selection_rgb_updater       (GtkWidget         *widget,
129                                                    gpointer           data);
130 static void gtk_color_selection_opacity_updater   (GtkWidget         *widget,
131                                                    gpointer           data);
132 static void gtk_color_selection_realize           (GtkWidget         *widget);
133 static void gtk_color_selection_unrealize         (GtkWidget         *widget);
134 static void gtk_color_selection_finalize          (GtkObject         *object);
135 static void gtk_color_selection_color_changed     (GtkColorSelection *colorsel);
136 static void gtk_color_selection_update_input      (GtkWidget         *scale,
137                                                    GtkWidget         *entry,
138                                                    gdouble            value);
139 static void gtk_color_selection_update_inputs     (GtkColorSelection *colorsel,
140                                                    gint               inputs,
141                                                    gint               which);
142 static void gtk_color_selection_update_value      (GtkColorSelection *colorsel,
143                                                    gint               y);
144 static void gtk_color_selection_update_wheel      (GtkColorSelection *colorsel,
145                                                    gint               x,
146                                                    gint               y);
147 static void gtk_color_selection_value_resize      (GtkWidget          *widget,
148                                                    gpointer            data);
149 static gint gtk_color_selection_value_events      (GtkWidget          *area,
150                                                    GdkEvent           *event);
151 static gint gtk_color_selection_value_timeout     (GtkColorSelection  *colorsel);
152 static void gtk_color_selection_wheel_resize      (GtkWidget          *widget,
153                                                    gpointer            data);
154 static gint gtk_color_selection_wheel_events      (GtkWidget          *area,
155                                                    GdkEvent           *event);
156 static gint gtk_color_selection_wheel_timeout     (GtkColorSelection  *colorsel);
157 static void gtk_color_selection_sample_resize     (GtkWidget          *widget,
158                                                    gpointer            data);
159 static void gtk_color_selection_drag_begin        (GtkWidget          *widget,
160                                                    GdkDragContext     *context,
161                                                    gpointer            data);
162 static void gtk_color_selection_drag_end          (GtkWidget          *widget,
163                                                    GdkDragContext     *context,
164                                                    gpointer            data);
165 static void gtk_color_selection_drop_handle       (GtkWidget          *widget, 
166                                                    GdkDragContext     *context,
167                                                    gint                x,
168                                                    gint                y,
169                                                    GtkSelectionData   *selection_data,
170                                                    guint               info,
171                                                    guint               time,
172                                                    gpointer            data);
173 static void gtk_color_selection_drag_handle       (GtkWidget        *widget, 
174                                                    GdkDragContext   *context,
175                                                    GtkSelectionData *selection_data,
176                                                    guint             info,
177                                                    guint             time,
178                                                    gpointer          data);
179 static void gtk_color_selection_draw_wheel_marker (GtkColorSelection  *colorsel);
180 static void gtk_color_selection_draw_wheel_frame  (GtkColorSelection  *colorsel);
181 static void gtk_color_selection_draw_value_marker (GtkColorSelection  *colorsel);
182 static void gtk_color_selection_draw_value_bar    (GtkColorSelection  *colorsel,
183                                                    gint                resize);
184 static void gtk_color_selection_draw_wheel        (GtkColorSelection  *colorsel,
185                                                    gint                resize);
186 static void gtk_color_selection_draw_sample       (GtkColorSelection  *colorsel,
187                                                    gint                resize);
188
189 static gint gtk_color_selection_eval_wheel        (gint     x, gint     y,
190                                                    gdouble cx, gdouble cy,
191                                                    gdouble *h, gdouble *s);
192
193 static void gtk_color_selection_hsv_to_rgb        (gdouble  h, gdouble  s, gdouble  v,
194                                                    gdouble *r, gdouble *g, gdouble *b);
195 static void gtk_color_selection_rgb_to_hsv        (gdouble  r, gdouble  g, gdouble  b,
196                                                    gdouble *h, gdouble *s, gdouble *v);
197
198
199 static GtkVBoxClass *color_selection_parent_class = NULL;
200 static GtkWindowClass *color_selection_dialog_parent_class = NULL;
201
202
203 static guint color_selection_signals[LAST_SIGNAL] = {0};
204
205 static const gchar      *value_index_key = "gtk-value-index";
206
207
208 #define SF GtkSignalFunc
209
210
211 static const scale_val_type scale_vals[NUM_CHANNELS] =
212 {
213   {N_("Hue:"),        0.0, 360.0, 1.00, 10.00, (SF) gtk_color_selection_hsv_updater},
214   {N_("Saturation:"), 0.0,   1.0, 0.01,  0.01, (SF) gtk_color_selection_hsv_updater},
215   {N_("Value:"),      0.0,   1.0, 0.01,  0.01, (SF) gtk_color_selection_hsv_updater},
216   {N_("Red:"),        0.0,   1.0, 0.01,  0.01, (SF) gtk_color_selection_rgb_updater},
217   {N_("Green:"),      0.0,   1.0, 0.01,  0.01, (SF) gtk_color_selection_rgb_updater},
218   {N_("Blue:"),       0.0,   1.0, 0.01,  0.01, (SF) gtk_color_selection_rgb_updater},
219   {N_("Opacity:"),    0.0,   1.0, 0.01,  0.01, (SF) gtk_color_selection_opacity_updater}
220 };
221
222 GtkType
223 gtk_color_selection_get_type (void)
224 {
225   static GtkType color_selection_type = 0;
226
227   if (!color_selection_type)
228     {
229       static const GtkTypeInfo colorsel_info =
230       {
231         "GtkColorSelection",
232         sizeof (GtkColorSelection),
233         sizeof (GtkColorSelectionClass),
234         (GtkClassInitFunc) gtk_color_selection_class_init,
235         (GtkObjectInitFunc) gtk_color_selection_init,
236         /* reserved_1 */ NULL,
237         /* reserved_2 */ NULL,
238         (GtkClassInitFunc) NULL,
239       };
240
241       color_selection_type = gtk_type_unique (gtk_vbox_get_type (), &colorsel_info);
242     }
243
244   return color_selection_type;
245 }
246
247 static void
248 gtk_color_selection_class_init (GtkColorSelectionClass *klass)
249 {
250   GtkObjectClass *object_class;
251   GtkWidgetClass *widget_class;
252   GtkContainerClass *container_class;
253
254   object_class = (GtkObjectClass*) klass;
255   widget_class = (GtkWidgetClass*) klass;
256   container_class = (GtkContainerClass*) klass;
257
258   color_selection_parent_class = gtk_type_class (gtk_vbox_get_type ());
259
260   color_selection_signals[COLOR_CHANGED] =
261      gtk_signal_new ("color_changed",
262                      GTK_RUN_FIRST,
263                      object_class->type,
264                      GTK_SIGNAL_OFFSET (GtkColorSelectionClass, color_changed),
265                      gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
266
267   gtk_object_class_add_signals (object_class, color_selection_signals, LAST_SIGNAL);
268
269   object_class->finalize = gtk_color_selection_finalize;
270
271   widget_class->realize = gtk_color_selection_realize;
272   widget_class->unrealize = gtk_color_selection_unrealize;
273 }
274
275 static void
276 gtk_color_selection_init (GtkColorSelection *colorsel)
277 {
278   GtkWidget *frame, *hbox, *vbox, *hbox2, *label, *table;
279   GtkObject *adj;
280   gint old_mask, n;
281   gchar txt[32];
282
283   for (n = RED; n <= OPACITY; n++)
284     colorsel->values[n] = 1.0;
285
286   RGB_TO_HSV ();
287
288   for (n = HUE; n <= OPACITY; n++)
289     colorsel->old_values[n] = colorsel->values[n];
290
291   colorsel->wheel_gc = NULL;
292   colorsel->value_gc = NULL;
293   colorsel->sample_gc = NULL;
294   colorsel->wheel_buf = NULL;
295   colorsel->value_buf = NULL;
296   colorsel->sample_buf = NULL;
297
298   colorsel->use_opacity = FALSE;
299   colorsel->timer_active = FALSE;
300   colorsel->policy = GTK_UPDATE_CONTINUOUS;
301
302   hbox = gtk_hbox_new (FALSE, 5);
303   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
304   gtk_container_add (GTK_CONTAINER (colorsel), hbox);
305
306   vbox = gtk_vbox_new (FALSE, 5);
307   gtk_container_add (GTK_CONTAINER (hbox), vbox);
308   gtk_widget_show (vbox);
309
310   hbox2 = gtk_hbox_new (FALSE, 5);
311   gtk_container_add (GTK_CONTAINER (vbox), hbox2);
312   gtk_widget_show (hbox2);
313
314   colorsel->wheel_area = gtk_preview_new (GTK_PREVIEW_COLOR);
315   old_mask = gtk_widget_get_events(colorsel->wheel_area);
316   gtk_widget_set_events (colorsel->wheel_area,
317                          old_mask |
318                          GDK_BUTTON_PRESS_MASK |
319                          GDK_BUTTON_RELEASE_MASK |
320                          GDK_BUTTON_MOTION_MASK |
321                          GDK_POINTER_MOTION_HINT_MASK);
322   gtk_preview_size (GTK_PREVIEW (colorsel->wheel_area), WHEEL_WIDTH, WHEEL_HEIGHT);
323   gtk_preview_set_expand (GTK_PREVIEW (colorsel->wheel_area), TRUE);
324   gtk_container_add (GTK_CONTAINER (hbox2), colorsel->wheel_area);
325   gtk_widget_show (colorsel->wheel_area);
326
327   old_mask = gtk_widget_get_events (colorsel->wheel_area);
328
329   gtk_signal_connect (GTK_OBJECT (colorsel->wheel_area), "event",
330     (SF) gtk_color_selection_wheel_events, (gpointer) colorsel->wheel_area);
331   gtk_signal_connect_after (GTK_OBJECT (colorsel->wheel_area), "expose_event",
332     (SF) gtk_color_selection_wheel_events, (gpointer) colorsel->wheel_area);
333   gtk_signal_connect_after (GTK_OBJECT (colorsel->wheel_area), "size_allocate",
334     (SF) gtk_color_selection_wheel_resize, (gpointer) colorsel->wheel_area);
335   gtk_object_set_data (GTK_OBJECT (colorsel->wheel_area), "_GtkColorSelection", (gpointer) colorsel);
336
337   frame = gtk_frame_new (NULL);
338   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
339   gtk_container_set_border_width (GTK_CONTAINER (frame), 0);
340   gtk_box_pack_start (GTK_BOX (hbox2), frame, FALSE, TRUE, 0);
341   gtk_widget_show (frame);
342
343   colorsel->value_area = gtk_preview_new (GTK_PREVIEW_COLOR);
344   gtk_preview_size (GTK_PREVIEW (colorsel->value_area), VALUE_WIDTH, VALUE_HEIGHT);
345   gtk_preview_set_expand (GTK_PREVIEW (colorsel->value_area), TRUE);
346   gtk_container_add (GTK_CONTAINER (frame), colorsel->value_area);
347   gtk_widget_show (colorsel->value_area);
348
349   old_mask = gtk_widget_get_events (colorsel->value_area);
350   gtk_widget_set_events (colorsel->value_area,
351                          old_mask |
352                          GDK_BUTTON_PRESS_MASK |
353                          GDK_BUTTON_RELEASE_MASK |
354                          GDK_BUTTON_MOTION_MASK |
355                          GDK_POINTER_MOTION_HINT_MASK);
356
357   gtk_signal_connect_after (GTK_OBJECT (colorsel->value_area), "expose_event",
358     (SF) gtk_color_selection_value_events, (gpointer) colorsel->value_area);
359   gtk_signal_connect_after (GTK_OBJECT (colorsel->value_area), "size_allocate",
360     (SF) gtk_color_selection_value_resize, (gpointer) colorsel->value_area);
361   gtk_signal_connect (GTK_OBJECT (colorsel->value_area), "event",
362     (SF) gtk_color_selection_value_events, (gpointer) colorsel->value_area);
363   gtk_object_set_data (GTK_OBJECT (colorsel->value_area), "_GtkColorSelection", (gpointer) colorsel);
364
365   /* New/old color samples */
366   /* ===================== */
367
368   frame = gtk_frame_new (NULL);
369   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
370   gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, TRUE, 0);
371   gtk_widget_show (frame);
372
373 /*  colorsel->sample_area_eb = gtk_button_new ();
374   gtk_container_add (GTK_CONTAINER (frame), colorsel->sample_area_eb);
375   gtk_widget_show (colorsel->sample_area_eb); */
376
377   colorsel->sample_area = gtk_preview_new (GTK_PREVIEW_COLOR);
378   gtk_preview_size (GTK_PREVIEW (colorsel->sample_area), SAMPLE_WIDTH, SAMPLE_HEIGHT);
379   gtk_preview_set_expand (GTK_PREVIEW (colorsel->sample_area), TRUE);
380   gtk_container_add (GTK_CONTAINER (frame),
381                      colorsel->sample_area);
382   gtk_widget_set_events(colorsel->sample_area,
383                 gtk_widget_get_events(colorsel->sample_area)
384                 | GDK_BUTTON_MOTION_MASK
385                 | GDK_BUTTON_PRESS_MASK
386                 | GDK_BUTTON_RELEASE_MASK
387                 | GDK_ENTER_NOTIFY_MASK
388                 | GDK_LEAVE_NOTIFY_MASK);
389   gtk_widget_show (colorsel->sample_area);
390
391   gtk_signal_connect_after (GTK_OBJECT (colorsel->sample_area),
392                             "size_allocate",
393                             GTK_SIGNAL_FUNC (gtk_color_selection_sample_resize),
394                             colorsel->sample_area);
395   gtk_object_set_data (GTK_OBJECT (colorsel->sample_area), "_GtkColorSelection", (gpointer) colorsel);
396
397   table = gtk_table_new (NUM_CHANNELS, 3, FALSE);
398   gtk_table_set_col_spacings (GTK_TABLE (table), 3);
399   gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, TRUE, 0);
400
401   for (n = HUE; n <= OPACITY; n++)
402     {
403       label = gtk_label_new (_(scale_vals[n].label));
404       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
405       gtk_table_attach_defaults (GTK_TABLE (table), label, 0, 1, n, n + 1);
406
407       adj = gtk_adjustment_new (colorsel->values[n], scale_vals[n].lower,
408                                 scale_vals[n].upper, scale_vals[n].step_inc,
409                                 scale_vals[n].page_inc, 0.0);
410       colorsel->scales[n] = gtk_hscale_new (GTK_ADJUSTMENT (adj));
411       gtk_widget_set_usize (colorsel->scales[n], 128, 0);
412       gtk_scale_set_value_pos (GTK_SCALE (colorsel->scales[n]), GTK_POS_TOP);
413
414       gtk_range_set_update_policy (GTK_RANGE (colorsel->scales[n]), colorsel->policy);
415       gtk_scale_set_draw_value (GTK_SCALE (colorsel->scales[n]), FALSE);
416       gtk_scale_set_digits (GTK_SCALE (colorsel->scales[n]), 2);
417       gtk_table_attach_defaults (GTK_TABLE (table), colorsel->scales[n], 1, 2, n, n + 1);
418
419       colorsel->entries[n] = gtk_entry_new ();
420       gtk_widget_set_usize (colorsel->entries[n], 40, 0);
421       sprintf (txt, "%.2f", colorsel->values[n]);
422       gtk_entry_set_text (GTK_ENTRY (colorsel->entries[n]), txt);
423       gtk_table_attach_defaults (GTK_TABLE (table), colorsel->entries[n], 2, 3, n, n + 1);
424
425       if (n != OPACITY)
426         {
427           gtk_widget_show (label);
428           gtk_widget_show (colorsel->scales[n]);
429           gtk_widget_show (colorsel->entries[n]);
430         }
431
432       gtk_signal_connect_object (GTK_OBJECT (adj), "value_changed",
433                                  scale_vals[n].updater, (gpointer) colorsel->scales[n]);
434       gtk_object_set_data (GTK_OBJECT (colorsel->scales[n]), "_GtkColorSelection", (gpointer) colorsel);
435       gtk_object_set_data (GTK_OBJECT (colorsel->scales[n]), value_index_key, GINT_TO_POINTER (n));
436       gtk_signal_connect_object (GTK_OBJECT (colorsel->entries[n]), "changed",
437                                  scale_vals[n].updater, (gpointer) colorsel->entries[n]);
438       gtk_object_set_data (GTK_OBJECT (colorsel->entries[n]), "_GtkColorSelection", (gpointer) colorsel);
439       gtk_object_set_data (GTK_OBJECT (colorsel->entries[n]), value_index_key, GINT_TO_POINTER (n));
440     }
441
442   colorsel->opacity_label = label;
443
444   gtk_widget_show (table);
445   gtk_widget_show (hbox);
446 }
447
448 GtkWidget *
449 gtk_color_selection_new (void)
450 {
451   GtkColorSelection *colorsel;
452
453   colorsel = gtk_type_new (gtk_color_selection_get_type ());
454
455   return GTK_WIDGET (colorsel);
456 }
457
458 void
459 gtk_color_selection_set_update_policy (GtkColorSelection *colorsel,
460                                        GtkUpdateType      policy)
461 {
462   gint n;
463
464   g_return_if_fail (colorsel != NULL);
465
466   if (policy != colorsel->policy)
467     {
468       colorsel->policy = policy;
469
470       for (n = 0; n < NUM_CHANNELS; n++)
471         gtk_range_set_update_policy (GTK_RANGE (colorsel->scales[n]), policy);
472     }
473 }
474
475
476 void
477 gtk_color_selection_set_color (GtkColorSelection *colorsel,
478                                gdouble           *color)
479 {
480   gint n, i = 0;
481
482   g_return_if_fail (colorsel != NULL);
483   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
484
485   if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (colorsel)))
486     gtk_color_selection_draw_wheel_marker (colorsel);
487
488   for (n = RED; n <= BLUE; n++)
489     {
490       colorsel->old_values[n] = colorsel->values[n];
491       colorsel->values[n] = color[i++];
492     }
493
494   if (colorsel->use_opacity)
495     {
496       colorsel->old_values[OPACITY] = colorsel->values[OPACITY];
497       colorsel->values[OPACITY] = color[i];
498     }
499
500   RGB_TO_HSV ();
501
502   gtk_color_selection_update_inputs (colorsel, RGB_INPUTS | HSV_INPUTS | OPACITY_INPUTS, BOTH);
503
504   if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (colorsel)))
505     {
506       gtk_color_selection_draw_value_bar (colorsel, FALSE);
507       gtk_color_selection_draw_sample (colorsel, FALSE);
508       gtk_color_selection_draw_wheel_marker (colorsel);
509     }
510 }
511
512 void
513 gtk_color_selection_get_color (GtkColorSelection *colorsel,
514                                gdouble           *color)
515 {
516   gint n, i = 0;
517
518   g_return_if_fail (colorsel != NULL);
519   g_return_if_fail (GTK_IS_COLOR_SELECTION (colorsel));
520
521   for (n = RED; n <= BLUE; n++)
522     color[i++] = colorsel->values[n];
523   if (colorsel->use_opacity)
524     color[i] = colorsel->values[OPACITY];
525 }
526
527 static void
528 gtk_color_selection_realize (GtkWidget         *widget)
529 {
530   GtkColorSelection *colorsel;
531
532   static const GtkTargetEntry targets[] = {
533     { "application/x-color", 0 }
534   };
535
536   g_return_if_fail (widget != NULL);
537   g_return_if_fail (GTK_IS_COLOR_SELECTION (widget));
538
539   colorsel = GTK_COLOR_SELECTION (widget);
540
541   if (GTK_WIDGET_CLASS (color_selection_parent_class)->realize)
542     (*GTK_WIDGET_CLASS (color_selection_parent_class)->realize) (widget);
543
544   gtk_drag_dest_set (colorsel->sample_area,
545                        GTK_DEST_DEFAULT_HIGHLIGHT |
546                        GTK_DEST_DEFAULT_MOTION |
547                        GTK_DEST_DEFAULT_DROP,
548                        targets, 1,
549                        GDK_ACTION_COPY);
550
551   gtk_drag_source_set (colorsel->sample_area, 
552                        GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
553                        targets, 1,
554                        GDK_ACTION_COPY | GDK_ACTION_MOVE);
555
556   gtk_signal_connect (GTK_OBJECT (colorsel->sample_area),
557                       "drag_begin",
558                       GTK_SIGNAL_FUNC (gtk_color_selection_drag_begin),
559                       colorsel);
560   gtk_signal_connect (GTK_OBJECT (colorsel->sample_area),
561                       "drag_end",
562                       GTK_SIGNAL_FUNC (gtk_color_selection_drag_end),
563                       colorsel);
564   gtk_signal_connect (GTK_OBJECT (colorsel->sample_area),
565                       "drag_data_get",
566                       GTK_SIGNAL_FUNC (gtk_color_selection_drag_handle),
567                       colorsel);
568   gtk_signal_connect (GTK_OBJECT (colorsel->sample_area),
569                       "drag_data_received",
570                       GTK_SIGNAL_FUNC (gtk_color_selection_drop_handle),
571                       colorsel);
572 }
573
574 static void
575 gtk_color_selection_unrealize (GtkWidget      *widget)
576 {
577   GtkColorSelection *colorsel;
578
579   g_return_if_fail (widget != NULL);
580   g_return_if_fail (GTK_IS_COLOR_SELECTION (widget));
581
582   colorsel = GTK_COLOR_SELECTION (widget);
583
584   if (colorsel->value_gc != NULL)
585     {
586       gdk_gc_unref (colorsel->value_gc);
587       colorsel->value_gc = NULL;
588     }
589   if (colorsel->wheel_gc != NULL)
590     {
591       gdk_gc_unref (colorsel->wheel_gc);
592       colorsel->wheel_gc = NULL;
593     }
594   if (colorsel->sample_gc != NULL)
595     {
596       gdk_gc_unref (colorsel->sample_gc);
597       colorsel->sample_gc = NULL;
598     }
599
600   (* GTK_WIDGET_CLASS (color_selection_parent_class)->unrealize) (widget);
601 }
602
603 static void
604 gtk_color_selection_finalize (GtkObject *object)
605 {
606   GtkColorSelection *colorsel;
607
608   g_return_if_fail (object != NULL);
609   g_return_if_fail (GTK_IS_COLOR_SELECTION (object));
610
611   colorsel = GTK_COLOR_SELECTION (object);
612
613   if (colorsel->wheel_buf != NULL)
614     g_free (colorsel->wheel_buf);
615   if (colorsel->value_buf != NULL)
616     g_free (colorsel->value_buf);
617   if (colorsel->sample_buf != NULL)
618     g_free (colorsel->sample_buf);
619
620   (*GTK_OBJECT_CLASS (color_selection_parent_class)->finalize) (object);
621 }
622
623 static void
624 gtk_color_selection_color_changed (GtkColorSelection *colorsel)
625 {
626   gtk_signal_emit (GTK_OBJECT (colorsel), color_selection_signals[COLOR_CHANGED]);
627 }
628
629 static void
630 gtk_color_selection_update_input (GtkWidget *scale,
631                                   GtkWidget *entry,
632                                   gdouble    value)
633 {
634   GtkAdjustment *adj;
635   gchar txt[32];
636
637   if (scale != NULL)
638     {
639       adj = gtk_range_get_adjustment (GTK_RANGE (scale));
640       adj->value = (gfloat) value;
641       gtk_signal_handler_block_by_data (GTK_OBJECT (adj), (gpointer) scale);
642       gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
643       gtk_range_slider_update (GTK_RANGE (scale));
644       gtk_signal_handler_unblock_by_data (GTK_OBJECT (adj), (gpointer) scale);
645     }
646
647   if (entry != NULL)
648     {
649       gtk_signal_handler_block_by_data (GTK_OBJECT (entry), (gpointer) entry);
650       sprintf (txt, "%.2f", value);
651       gtk_entry_set_text (GTK_ENTRY (entry), txt);
652       gtk_signal_handler_unblock_by_data (GTK_OBJECT (entry), (gpointer) entry);
653     }
654 }
655
656 static void
657 gtk_color_selection_update_inputs (GtkColorSelection *colorsel,
658                                    gint               inputs,
659                                    gint               which)
660 {
661   gint n;
662
663   switch (which)
664     {
665     case SCALE:
666       if ((inputs & RGB_INPUTS) != 0)
667         for (n = RED; n <= BLUE; n++)
668           gtk_color_selection_update_input (colorsel->scales[n], NULL,
669                                             colorsel->values[n]);
670       if ((inputs & HSV_INPUTS) != 0)
671         for (n = HUE; n <= VALUE; n++)
672           gtk_color_selection_update_input (colorsel->scales[n], NULL,
673                                             colorsel->values[n]);
674       if ((inputs & OPACITY_INPUTS) != 0)
675         gtk_color_selection_update_input(colorsel->scales[OPACITY], NULL,
676                                          colorsel->values[OPACITY]);
677       break;
678     case ENTRY:
679       if ((inputs & RGB_INPUTS) != 0)
680         for (n = RED; n <= BLUE; n++)
681           gtk_color_selection_update_input (NULL, colorsel->entries[n], colorsel->values[n]);
682       if ((inputs & HSV_INPUTS) != 0)
683         for (n = HUE; n <= VALUE; n++)
684           gtk_color_selection_update_input (NULL, colorsel->entries[n], colorsel->values[n]);
685       if ((inputs & OPACITY_INPUTS) != 0)
686         gtk_color_selection_update_input(NULL, colorsel->entries[OPACITY], colorsel->values[OPACITY]);
687       break;
688     default:
689       if ((inputs & RGB_INPUTS) != 0)
690         for (n = RED; n <= BLUE; n++)
691           gtk_color_selection_update_input (colorsel->scales[n], colorsel->entries[n],
692                                             colorsel->values[n]);
693       if ((inputs & HSV_INPUTS) != 0)
694         for (n = HUE; n <= VALUE; n++)
695           gtk_color_selection_update_input (colorsel->scales[n], colorsel->entries[n],
696                                             colorsel->values[n]);
697       if ((inputs & OPACITY_INPUTS) != 0)
698         gtk_color_selection_update_input(colorsel->scales[OPACITY], colorsel->entries[OPACITY],
699                                          colorsel->values[OPACITY]);
700       break;
701     }
702 }
703
704 static void
705 gtk_color_selection_hsv_updater (GtkWidget *widget,
706                                  gpointer   data)
707 {
708   GtkColorSelection *colorsel;
709   GtkAdjustment *adj;
710   gdouble newvalue;
711   gint i, which = SCALE;
712
713   if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (widget)))
714     {
715       colorsel = (GtkColorSelection*) gtk_object_get_data (GTK_OBJECT (widget), "_GtkColorSelection");
716       i = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (widget), value_index_key));
717
718       if (GTK_IS_SCALE (widget))
719         {
720           adj = gtk_range_get_adjustment (GTK_RANGE (GTK_SCALE (widget)));
721           newvalue = (gdouble) adj->value;
722           which = ENTRY;
723         }
724       else
725         newvalue = (gdouble) atof (gtk_entry_get_text (GTK_ENTRY (widget)));
726
727       if (i == VALUE)
728         {
729           gtk_color_selection_draw_value_marker (colorsel);
730           colorsel->values[i] = newvalue;
731
732           HSV_TO_RGB ();
733
734           gtk_color_selection_draw_value_marker (colorsel);
735         }
736       else
737         {
738           gtk_color_selection_draw_wheel_marker (colorsel);
739           colorsel->values[i] = newvalue;
740
741           HSV_TO_RGB ();
742
743           gtk_color_selection_draw_wheel_marker (colorsel);
744           gtk_color_selection_draw_value_bar (colorsel, FALSE);
745         }
746
747       gtk_color_selection_draw_sample (colorsel, FALSE);
748       gtk_color_selection_color_changed (colorsel);
749       gtk_color_selection_update_inputs (colorsel, HSV_INPUTS, which);
750       gtk_color_selection_update_inputs (colorsel, RGB_INPUTS, BOTH);
751     }
752 }
753
754 static void
755 gtk_color_selection_rgb_updater (GtkWidget *widget,
756                                  gpointer   data)
757 {
758   GtkColorSelection *colorsel;
759   GtkAdjustment *adj;
760   gdouble newvalue;
761   gint i, which = SCALE;
762
763   if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (widget)))
764     {
765       colorsel = (GtkColorSelection*) gtk_object_get_data (GTK_OBJECT (widget), "_GtkColorSelection");
766       i = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (widget), value_index_key));
767
768       if (GTK_IS_SCALE (widget))
769         {
770           adj = gtk_range_get_adjustment (GTK_RANGE (GTK_SCALE (widget)));
771           newvalue = (gdouble) adj->value;
772           which = ENTRY;
773         }
774       else
775         newvalue = (gdouble) atof (gtk_entry_get_text (GTK_ENTRY (widget)));
776
777       gtk_color_selection_draw_wheel_marker (colorsel);
778
779       colorsel->values[i] = newvalue;
780       RGB_TO_HSV ();
781
782       gtk_color_selection_draw_wheel_marker (colorsel);
783       gtk_color_selection_draw_value_bar (colorsel, FALSE);
784       gtk_color_selection_draw_sample (colorsel, FALSE);
785       gtk_color_selection_color_changed (colorsel);
786       gtk_color_selection_update_inputs (colorsel, RGB_INPUTS, which);
787       gtk_color_selection_update_inputs (colorsel, HSV_INPUTS, BOTH);
788     }
789 }
790
791 static void
792 gtk_color_selection_opacity_updater (GtkWidget *widget,
793                                      gpointer   data)
794 {
795   GtkColorSelection *colorsel;
796   GtkAdjustment *adj;
797
798   colorsel = (GtkColorSelection*) gtk_object_get_data (GTK_OBJECT (widget), "_GtkColorSelection");
799
800   if (GTK_IS_SCALE (widget))
801     {
802       adj = gtk_range_get_adjustment (GTK_RANGE (widget));
803       colorsel->values[OPACITY] = (gdouble) adj->value;
804       gtk_color_selection_update_input (NULL, colorsel->entries[OPACITY], colorsel->values[OPACITY]);
805     }
806   else
807     {
808       colorsel->values[OPACITY] = (gdouble) atof (gtk_entry_get_text (GTK_ENTRY (widget)));
809       gtk_color_selection_update_input (colorsel->scales[OPACITY], NULL, colorsel->values[OPACITY]);
810     }
811
812   gtk_color_selection_draw_sample (colorsel, FALSE);
813   gtk_color_selection_color_changed (colorsel);
814 }
815
816 void
817 gtk_color_selection_set_opacity (GtkColorSelection *colorsel,
818                                  gint               use_opacity)
819 {
820   g_return_if_fail (colorsel != NULL);
821
822   colorsel->use_opacity = use_opacity;
823
824   if (!use_opacity && GTK_WIDGET_VISIBLE (colorsel->scales[OPACITY]))
825     {
826       gtk_widget_hide (colorsel->opacity_label);
827       gtk_widget_hide (colorsel->scales[OPACITY]);
828       gtk_widget_hide (colorsel->entries[OPACITY]);
829     }
830   else if (use_opacity && !GTK_WIDGET_VISIBLE (colorsel->scales[OPACITY]))
831     {
832       gtk_widget_show (colorsel->opacity_label);
833       gtk_widget_show (colorsel->scales[OPACITY]);
834       gtk_widget_show (colorsel->entries[OPACITY]);
835     }
836
837   if (GTK_WIDGET_DRAWABLE (colorsel->sample_area))
838     gtk_color_selection_draw_sample (colorsel, FALSE);
839 }
840
841 static void
842 gtk_color_selection_value_resize (GtkWidget *widget,
843                                   gpointer   data)
844 {
845   GtkColorSelection *colorsel;
846
847   colorsel = (GtkColorSelection*) gtk_object_get_data (GTK_OBJECT (widget), "_GtkColorSelection");
848   gtk_color_selection_draw_value_bar (colorsel, TRUE);
849 }
850
851 static void
852 gtk_color_selection_wheel_resize (GtkWidget *widget,
853                                   gpointer   data)
854 {
855   GtkColorSelection *colorsel;
856
857   colorsel = (GtkColorSelection*) gtk_object_get_data (GTK_OBJECT (widget), "_GtkColorSelection");
858   gtk_color_selection_draw_wheel (colorsel, TRUE);
859 }
860
861 static void
862 gtk_color_selection_sample_resize (GtkWidget *widget,
863                                    gpointer   data)
864 {
865   GtkColorSelection *colorsel;
866
867   colorsel = (GtkColorSelection*) gtk_object_get_data (GTK_OBJECT (widget), "_GtkColorSelection");
868   gtk_color_selection_draw_sample (colorsel, TRUE);
869 }
870
871 static void
872 gtk_color_selection_drag_begin (GtkWidget      *widget,
873                                 GdkDragContext *context,
874                                 gpointer        data)
875 {
876   GtkColorSelection *colorsel = data;
877   GtkWidget *window;
878   gdouble colors[4];
879   GdkColor bg;
880
881   window = gtk_window_new (GTK_WINDOW_POPUP);
882   gtk_widget_set_app_paintable (GTK_WIDGET (window), TRUE);
883   gtk_widget_set_usize (window, 48, 32);
884   gtk_widget_realize (window);
885   gtk_object_set_data_full (GTK_OBJECT (widget),
886                             "gtk-color-selection-drag-window",
887                             window,
888                             (GtkDestroyNotify) gtk_widget_destroy);
889
890   gtk_color_selection_get_color (colorsel, colors);
891   bg.red = 0xffff * colors[0];
892   bg.green = 0xffff * colors[1];
893   bg.blue = 0xffff * colors[2];
894
895   gdk_color_alloc (gtk_widget_get_colormap (window), &bg);
896   gdk_window_set_background (window->window, &bg);
897
898   gtk_drag_set_icon_widget (context, window, -2, -2);
899 }
900
901 static void
902 gtk_color_selection_drag_end (GtkWidget      *widget,
903                               GdkDragContext *context,
904                               gpointer        data)
905 {
906   gtk_object_set_data (GTK_OBJECT (widget), "gtk-color-selection-drag-window", NULL);
907 }
908
909 static void
910 gtk_color_selection_drop_handle (GtkWidget        *widget, 
911                                  GdkDragContext   *context,
912                                  gint              x,
913                                  gint              y,
914                                  GtkSelectionData *selection_data,
915                                  guint             info,
916                                  guint             time,
917                                  gpointer          data)
918 {
919   GtkColorSelection *colorsel = data;
920   guint16 *vals;
921   gdouble colors[4];
922
923   /* This is currently a guint16 array of the format:
924      R
925      G
926      B
927      opacity
928   */
929
930   if (selection_data->length < 0)
931     return;
932
933   if ((selection_data->format != 16) || 
934       (selection_data->length != 8))
935     {
936       g_warning ("Received invalid color data\n");
937       return;
938     }
939   
940   vals = (guint16 *)selection_data->data;
941
942   colors[0] = (gdouble)vals[0] / 0xffff;
943   colors[1] = (gdouble)vals[1] / 0xffff;
944   colors[2] = (gdouble)vals[2] / 0xffff;
945   colors[3] = (gdouble)vals[3] / 0xffff;
946   
947   gtk_color_selection_set_color(colorsel, colors);
948 }
949
950 static void
951 gtk_color_selection_drag_handle (GtkWidget        *widget, 
952                                  GdkDragContext   *context,
953                                  GtkSelectionData *selection_data,
954                                  guint             info,
955                                  guint             time,
956                                  gpointer          data)
957 {
958   GtkColorSelection *colorsel = data;
959   guint16 vals[4];
960   gdouble colors[4];
961
962   gtk_color_selection_get_color(colorsel, colors);
963
964   vals[0] = colors[0] * 0xffff;
965   vals[1] = colors[1] * 0xffff;
966   vals[2] = colors[2] * 0xffff;
967   vals[3] = colorsel->use_opacity ? colors[3] * 0xffff : 0xffff;
968
969   gtk_selection_data_set (selection_data,
970                           gdk_atom_intern ("application/x-color", FALSE),
971                           16, (guchar *)vals, 8);
972 }
973
974 static void
975 gtk_color_selection_draw_wheel_marker (GtkColorSelection *colorsel)
976 {
977   gint xpos, ypos;
978
979   gdk_gc_set_function (colorsel->wheel_gc, GDK_INVERT);
980
981   xpos = (gint) ((-(gdouble) (colorsel->wheel_area->allocation.width) / 2.0) *
982                  colorsel->values[SATURATION] * cos (DEGTORAD ((colorsel->values[HUE] - 90)))) +
983                  (colorsel->wheel_area->allocation.width >> 1) - 4;
984   ypos = (gint) (((gdouble) (colorsel->wheel_area->allocation.height) / 2.0) *
985                  colorsel->values[SATURATION] * sin (DEGTORAD ((colorsel->values[HUE] - 90)))) +
986                  (colorsel->wheel_area->allocation.height >> 1) - 4;
987
988   gdk_draw_arc (colorsel->wheel_area->window, colorsel->wheel_gc, FALSE, xpos, ypos, 8, 8, 0, 360 * 64);
989 }
990
991 static void
992 gtk_color_selection_draw_value_marker (GtkColorSelection *colorsel)
993 {
994   gint y;
995
996   gdk_gc_set_function (colorsel->value_gc, GDK_INVERT);
997
998   y = (gint) ((gdouble) (colorsel->value_area->allocation.height) * (1.0 - colorsel->values[VALUE]));
999   gdk_draw_line (colorsel->value_area->window, colorsel->value_gc,
1000                  0, y, colorsel->value_area->allocation.width, y);
1001 }
1002
1003 static void
1004 gtk_color_selection_update_value (GtkColorSelection *colorsel,
1005                                   gint               y)
1006 {
1007   gtk_color_selection_draw_value_marker (colorsel);
1008
1009   if (y < 0)
1010     y = 0;
1011   else if (y > colorsel->value_area->allocation.height - 1)
1012     y = colorsel->value_area->allocation.height - 1;
1013
1014   colorsel->values[VALUE] = 1.0 - (gdouble) y / (gdouble) (colorsel->value_area->allocation.height);
1015
1016   HSV_TO_RGB ();
1017
1018   gtk_color_selection_draw_value_marker (colorsel);
1019   gtk_color_selection_draw_sample (colorsel, FALSE);
1020   gtk_color_selection_update_input (colorsel->scales[VALUE], colorsel->entries[VALUE],
1021                                     colorsel->values[VALUE]);
1022   gtk_color_selection_update_inputs (colorsel, RGB_INPUTS, BOTH);
1023 }
1024
1025 static void
1026 gtk_color_selection_update_wheel (GtkColorSelection *colorsel,
1027                                   gint               x,
1028                                   gint               y)
1029 {
1030   gdouble wid, heig;
1031   gint res;
1032
1033   gtk_color_selection_draw_wheel_marker (colorsel);
1034
1035   wid = (gdouble) (colorsel->wheel_area->allocation.width) / 2.0;
1036   heig = (gdouble) (colorsel->wheel_area->allocation.height) / 2.0;
1037
1038   res = gtk_color_selection_eval_wheel (x, y, wid, heig, &colorsel->values[HUE],
1039                                         &colorsel->values[SATURATION]);
1040
1041   HSV_TO_RGB ();
1042
1043   gtk_color_selection_draw_wheel_marker (colorsel);
1044   gtk_color_selection_draw_value_bar (colorsel, FALSE);
1045   gtk_color_selection_draw_sample (colorsel, FALSE);
1046   gtk_color_selection_update_inputs (colorsel, RGB_INPUTS | HSV_INPUTS, BOTH);
1047 }
1048
1049 static gint
1050 gtk_color_selection_value_timeout (GtkColorSelection *colorsel)
1051 {
1052   gint x, y;
1053   
1054   GDK_THREADS_ENTER ();
1055   
1056   gdk_window_get_pointer (colorsel->value_area->window, &x, &y, NULL);
1057   gtk_color_selection_update_value (colorsel, y);
1058   gtk_color_selection_color_changed (colorsel);
1059
1060   GDK_THREADS_LEAVE ();
1061
1062   return (TRUE);
1063 }
1064
1065 static gint
1066 gtk_color_selection_value_events (GtkWidget *area,
1067                                   GdkEvent  *event)
1068 {
1069   GtkColorSelection *colorsel;
1070   gint y;
1071
1072   colorsel = (GtkColorSelection*) gtk_object_get_data (GTK_OBJECT (area), "_GtkColorSelection");
1073
1074   if (colorsel->value_gc == NULL)
1075     colorsel->value_gc = gdk_gc_new (colorsel->value_area->window);
1076
1077   switch (event->type)
1078     {
1079     case GDK_MAP:
1080       gtk_color_selection_draw_value_bar (colorsel, FALSE);
1081       gtk_color_selection_draw_value_marker (colorsel);
1082       break;
1083     case GDK_EXPOSE:
1084       gtk_color_selection_draw_value_marker (colorsel);
1085       break;
1086     case GDK_BUTTON_PRESS:
1087       gtk_grab_add (area);
1088       gtk_color_selection_update_value (colorsel, event->button.y);
1089       gtk_color_selection_color_changed (colorsel);
1090       break;
1091     case GDK_BUTTON_RELEASE:
1092       gtk_grab_remove (area);
1093       if (colorsel->timer_active)
1094         gtk_timeout_remove (colorsel->timer_tag);
1095       colorsel->timer_active = FALSE;
1096
1097       y = event->button.y;
1098       if (event->button.window != area->window)
1099         gdk_window_get_pointer (area->window, NULL, &y, NULL);
1100
1101       gtk_color_selection_update_value (colorsel, y);
1102       gtk_color_selection_color_changed (colorsel);
1103       break;
1104     case GDK_MOTION_NOTIFY:
1105       /* Even though we select BUTTON_MOTION_MASK, we seem to need
1106        * to occasionally get events without buttons pressed.
1107        */
1108       if (!(event->motion.state &
1109             (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)))
1110         return FALSE;
1111       
1112       y = event->motion.y;
1113
1114       if (event->motion.is_hint || (event->motion.window != area->window))
1115         gdk_window_get_pointer (area->window, NULL, &y, NULL);
1116
1117       switch (colorsel->policy)
1118         {
1119         case GTK_UPDATE_CONTINUOUS:
1120           gtk_color_selection_update_value (colorsel, y);
1121           gtk_color_selection_color_changed (colorsel);
1122           break;
1123         case GTK_UPDATE_DELAYED:
1124           if (colorsel->timer_active)
1125             gtk_timeout_remove (colorsel->timer_tag);
1126
1127           colorsel->timer_tag = gtk_timeout_add (TIMER_DELAY,
1128                                                  (GtkFunction) gtk_color_selection_value_timeout,
1129                                                  (gpointer) colorsel);
1130           colorsel->timer_active = TRUE;
1131           break;
1132         default:
1133           break;
1134         }
1135       break;
1136     default:
1137       break;
1138     }
1139
1140   return (FALSE);
1141 }
1142
1143 static gint
1144 gtk_color_selection_wheel_timeout (GtkColorSelection *colorsel)
1145 {
1146   gint x, y;
1147
1148   gdk_window_get_pointer (colorsel->wheel_area->window, &x, &y, NULL);
1149   gtk_color_selection_update_wheel (colorsel, x, y);
1150   gtk_color_selection_color_changed (colorsel);
1151
1152   return (TRUE);
1153 }
1154
1155 static gint
1156 gtk_color_selection_wheel_events (GtkWidget *area,
1157                                   GdkEvent  *event)
1158 {
1159   GtkColorSelection *colorsel;
1160   gint x, y;
1161
1162   colorsel = (GtkColorSelection*) gtk_object_get_data (GTK_OBJECT (area), "_GtkColorSelection");
1163   
1164   if (colorsel->wheel_gc == NULL)
1165     colorsel->wheel_gc = gdk_gc_new (colorsel->wheel_area->window);
1166   if (colorsel->sample_gc == NULL)
1167     colorsel->sample_gc = gdk_gc_new (colorsel->sample_area->window);
1168   if (colorsel->value_gc == NULL)
1169     colorsel->value_gc = gdk_gc_new (colorsel->value_area->window);
1170   
1171   switch (event->type)
1172     {
1173     case GDK_MAP:
1174       gtk_color_selection_draw_wheel (colorsel, TRUE);
1175       gtk_color_selection_draw_wheel_marker (colorsel);
1176       gtk_color_selection_draw_sample (colorsel, TRUE);
1177       break;
1178     case GDK_EXPOSE:
1179       gtk_color_selection_draw_wheel_marker (colorsel);
1180       gtk_color_selection_draw_wheel_frame (colorsel);
1181       break;
1182     case GDK_BUTTON_PRESS:
1183       gtk_grab_add (area);
1184       gtk_color_selection_update_wheel (colorsel, event->button.x, event->button.y);
1185       gtk_color_selection_color_changed (colorsel);
1186       break;
1187     case GDK_BUTTON_RELEASE:
1188       gtk_grab_remove (area);
1189       if (colorsel->timer_active)
1190         gtk_timeout_remove (colorsel->timer_tag);
1191       colorsel->timer_active = FALSE;
1192
1193       x = event->button.x;
1194       y = event->button.y;
1195
1196       if (event->button.window != area->window)
1197         gdk_window_get_pointer (area->window, &x, &y, NULL);
1198
1199       gtk_color_selection_update_wheel (colorsel, x, y);
1200       gtk_color_selection_color_changed (colorsel);
1201       break;
1202     case GDK_MOTION_NOTIFY:
1203       /* Even though we select BUTTON_MOTION_MASK, we seem to need
1204        * to occasionally get events without buttons pressed.
1205        */
1206       if (!(event->motion.state &
1207             (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)))
1208         return FALSE;
1209       
1210       x = event->motion.x;
1211       y = event->motion.y;
1212
1213       if (event->motion.is_hint || (event->motion.window != area->window))
1214         gdk_window_get_pointer (area->window, &x, &y, NULL);
1215
1216       switch (colorsel->policy)
1217         {
1218         case GTK_UPDATE_CONTINUOUS:
1219           gtk_color_selection_update_wheel (colorsel, x, y);
1220           gtk_color_selection_color_changed (colorsel);
1221           break;
1222         case GTK_UPDATE_DELAYED:
1223           if (colorsel->timer_active)
1224             gtk_timeout_remove (colorsel->timer_tag);
1225           colorsel->timer_tag = gtk_timeout_add (TIMER_DELAY,
1226                                                  (GtkFunction) gtk_color_selection_wheel_timeout,
1227                                                  (gpointer) colorsel);
1228           colorsel->timer_active = TRUE;
1229           break;
1230         default:
1231           break;
1232         }
1233       break;
1234     default:
1235       break;
1236     }
1237
1238   return (FALSE);
1239 }
1240
1241 static void
1242 gtk_color_selection_draw_value_bar (GtkColorSelection *colorsel,
1243                                     gint               resize)
1244 {
1245   gint x, y, wid, heig, n;
1246   gdouble sv, v;
1247
1248   wid = colorsel->value_area->allocation.width;
1249   heig = colorsel->value_area->allocation.height;
1250
1251   if (resize || !colorsel->value_buf)
1252     {
1253       g_free (colorsel->value_buf);
1254       colorsel->value_buf = g_new0 (guchar, 3 * wid);
1255     }
1256
1257   v = 1.0;
1258   sv = 1.0 / (gdouble) (heig - 1);
1259
1260   for (y = 0; y < heig; y++)
1261     {
1262       gdouble c[3];
1263       guchar rc[3];
1264       gint i = 0;
1265
1266       gtk_color_selection_hsv_to_rgb (colorsel->values[HUE],
1267                                       colorsel->values[SATURATION],
1268                                       v,
1269                                       &c[0], &c[1], &c[2]);
1270
1271       for (n = 0; n < 3; n++)
1272         rc[n] = (guchar) (255.0 * c[n]);
1273
1274       for (x = 0; x < wid; x++)
1275         {
1276           for (n = 0; n < 3; n++)
1277             colorsel->value_buf[i++] = rc[n];
1278         }
1279
1280       gtk_preview_draw_row (GTK_PREVIEW (colorsel->value_area), colorsel->value_buf, 0, y, wid);
1281       v -= sv;
1282     }
1283
1284   gtk_widget_queue_draw (colorsel->value_area);
1285 }
1286
1287 static void
1288 gtk_color_selection_draw_wheel_frame (GtkColorSelection *colorsel)
1289 {
1290   GtkStyle *style;
1291   gint w, h;
1292
1293   style = gtk_widget_get_style (colorsel->wheel_area);
1294
1295   w = colorsel->wheel_area->allocation.width;
1296   h = colorsel->wheel_area->allocation.height;
1297
1298   gdk_draw_arc (colorsel->wheel_area->window, style->black_gc,
1299                 FALSE, 1, 1, w - 1, h - 1, 30 * 64, 180 * 64);
1300   gdk_draw_arc (colorsel->wheel_area->window, style->mid_gc[GTK_STATE_NORMAL],
1301                 FALSE, 0, 0, w, h, 30 * 64, 180 * 64);
1302
1303   gdk_draw_arc (colorsel->wheel_area->window, style->bg_gc[GTK_STATE_NORMAL],
1304                 FALSE, 1, 1, w - 1, h - 1, 210 * 64, 180 * 64);
1305   gdk_draw_arc (colorsel->wheel_area->window, style->light_gc[GTK_STATE_NORMAL],
1306                 FALSE, 0, 0, w, h, 210 * 64, 180 * 64);
1307 }
1308
1309 static void
1310 gtk_color_selection_draw_wheel (GtkColorSelection *colorsel,
1311                                 gint               resize)
1312 {
1313   gint x, y, i, wid, heig, n;
1314   gdouble cx, cy, h, s, c[3];
1315   guchar bg[3];
1316   GtkStyle *style = gtk_widget_get_style (colorsel->wheel_area);
1317
1318   wid = colorsel->wheel_area->allocation.width;
1319   heig = colorsel->wheel_area->allocation.height;
1320
1321   if (resize)
1322     {
1323       if (colorsel->wheel_buf != NULL)
1324         g_free (colorsel->wheel_buf);
1325
1326       colorsel->wheel_buf = g_new(guchar, 3 * wid);
1327     }
1328
1329   cx = (gdouble) (wid) / 2.0;
1330   cy = (gdouble) (heig) / 2.0;
1331
1332   bg[0] = style->bg[GTK_STATE_NORMAL].red >> 8;
1333   bg[1] = style->bg[GTK_STATE_NORMAL].green >> 8;
1334   bg[2] = style->bg[GTK_STATE_NORMAL].blue >> 8;
1335
1336   for (y = 0; y < heig; y++)
1337     {
1338       i = 0;
1339       for (x = 0; x < wid; x++)
1340         {
1341           if (gtk_color_selection_eval_wheel (x, y, cx, cy, &h, &s))
1342             {
1343               for (n = 0; n < 3; n++)
1344                 colorsel->wheel_buf[i++] = bg[n];
1345             }
1346           else
1347             {
1348               gtk_color_selection_hsv_to_rgb (h, s, 1.0, &c[0], &c[1], &c[2]);
1349               for (n = 0; n < 3; n++)
1350                 colorsel->wheel_buf[i++] = (guchar) (255.0 * c[n]);
1351             }
1352         }
1353
1354       gtk_preview_draw_row (GTK_PREVIEW (colorsel->wheel_area), colorsel->wheel_buf, 0, y, wid);
1355     }
1356
1357   if (colorsel->wheel_area->window)
1358      {
1359         GdkPixmap *pm = NULL;
1360         GdkGC     *pmgc = NULL;
1361         GdkColor   col;
1362         gint w, h;
1363         
1364         pm = gdk_pixmap_new (colorsel->wheel_area->window, wid, heig, 1);
1365         pmgc = gdk_gc_new (pm);
1366         
1367         col.pixel = 0;
1368         gdk_gc_set_foreground(pmgc, &col);
1369         gdk_draw_rectangle(pm, pmgc, TRUE, 0, 0, wid, heig);
1370         col.pixel = 1;
1371         
1372         gdk_gc_set_foreground(pmgc, &col);
1373         gdk_draw_arc (pm, pmgc, TRUE, 0, 0, wid, heig, 0, 360*64);
1374
1375         w = colorsel->wheel_area->allocation.width;
1376         h = colorsel->wheel_area->allocation.height;
1377         
1378         gdk_draw_arc (pm, pmgc,
1379                       FALSE, 1, 1, w - 1, h - 1, 30 * 64, 180 * 64);
1380         gdk_draw_arc (pm, pmgc,
1381                       FALSE, 0, 0, w, h, 30 * 64, 180 * 64);
1382         gdk_draw_arc (pm, pmgc,
1383                       FALSE, 1, 1, w - 1, h - 1, 210 * 64, 180 * 64);
1384         gdk_draw_arc (pm, pmgc,
1385                       FALSE, 0, 0, w, h, 210 * 64, 180 * 64);
1386         gdk_window_shape_combine_mask (colorsel->wheel_area->window, pm, 0, 0);
1387         gdk_pixmap_unref (pm);
1388         gdk_gc_destroy (pmgc);
1389      }
1390 }
1391
1392 static void
1393 gtk_color_selection_draw_sample (GtkColorSelection *colorsel,
1394                                  gint               resize)
1395 {
1396   gint x, y, i, wid, heig, f, half, n;
1397   guchar c[3 * 2], cc[3 * 4], *cp = c;
1398   gdouble o, oldo;
1399
1400   wid = colorsel->sample_area->allocation.width;
1401   heig = colorsel->sample_area->allocation.height;
1402   half = wid >> 1;
1403
1404   if (resize)
1405     {
1406       if (colorsel->sample_buf != NULL)
1407         g_free (colorsel->sample_buf);
1408
1409       colorsel->sample_buf = g_new(guchar, 3 * wid);
1410     }
1411
1412   i = RED;
1413   for (n = 0; n < 3; n++)
1414     {
1415       c[n] = (guchar) (255.0 * colorsel->old_values[i]);
1416       c[n + 3] = (guchar) (255.0 * colorsel->values[i++]);
1417     }
1418
1419   if (colorsel->use_opacity)
1420     {
1421       o = colorsel->values[OPACITY];
1422       oldo = colorsel->old_values[OPACITY];
1423
1424       for (n = 0; n < 3; n++)
1425         {
1426           cc[n] = (guchar) ((1.0 - oldo) * 192 + (oldo * (gdouble) c[n]));
1427           cc[n + 3] = (guchar) ((1.0 - oldo) * 128 + (oldo * (gdouble) c[n]));
1428           cc[n + 6] = (guchar) ((1.0 - o) * 192 + (o * (gdouble) c[n + 3]));
1429           cc[n + 9] = (guchar) ((1.0 - o) * 128 + (o * (gdouble) c[n + 3]));
1430         }
1431       cp = cc;
1432     }
1433
1434   for (y = 0; y < heig; y++)
1435     {
1436       i = 0;
1437       for (x = 0; x < wid; x++)
1438         {
1439           if (colorsel->use_opacity)
1440             {
1441               f = 3 * (((x % 32) < 16) ^ ((y % 32) < 16));
1442               f += (x > half) * 6;
1443             }
1444           else
1445             f = (x > half) * 3;
1446
1447           for (n = 0; n < 3; n++)
1448             colorsel->sample_buf[i++] = cp[n + f];
1449         }
1450
1451       gtk_preview_draw_row (GTK_PREVIEW (colorsel->sample_area), colorsel->sample_buf, 0, y, wid);
1452     }
1453
1454   gtk_widget_queue_draw (colorsel->sample_area);
1455 }
1456
1457 static gint
1458 gtk_color_selection_eval_wheel (gint     x,  gint     y,
1459                                 gdouble  cx, gdouble  cy,
1460                                 gdouble *h,  gdouble *s)
1461 {
1462   gdouble r, rx, ry;
1463
1464   rx = ((gdouble) x - cx);
1465   ry = ((gdouble) y - cy);
1466
1467   rx = rx/cx;
1468   ry = ry/cy;
1469
1470   r = sqrt (SQR (rx) + SQR (ry));
1471
1472   if (r != 0.0)
1473     *h = atan2 (rx / r, ry / r);
1474   else
1475     *h = 0.0;
1476
1477   *s = r;
1478   *h = 360.0 * (*h) / (2.0 * M_PI) + 180;
1479
1480   if (*s == 0.0)
1481     *s = 0.00001;
1482   else if (*s > 1.0)
1483     {
1484       *s = 1.0;
1485       return TRUE;
1486     }
1487   return FALSE;
1488 }
1489
1490 static void
1491 gtk_color_selection_hsv_to_rgb (gdouble  h, gdouble  s, gdouble  v,
1492                                 gdouble *r, gdouble *g, gdouble *b)
1493 {
1494   gint i;
1495   gdouble f, w, q, t;
1496
1497   if (s == 0.0)
1498     s = 0.000001;
1499
1500   if (h == -1.0)
1501     {
1502       *r = v;
1503       *g = v;
1504       *b = v;
1505     }
1506   else
1507     {
1508       if (h == 360.0)
1509         h = 0.0;
1510       h = h / 60.0;
1511       i = (gint) h;
1512       f = h - i;
1513       w = v * (1.0 - s);
1514       q = v * (1.0 - (s * f));
1515       t = v * (1.0 - (s * (1.0 - f)));
1516
1517       switch (i)
1518         {
1519         case 0:
1520           *r = v;
1521           *g = t;
1522           *b = w;
1523           break;
1524         case 1:
1525           *r = q;
1526           *g = v;
1527           *b = w;
1528           break;
1529         case 2:
1530           *r = w;
1531           *g = v;
1532           *b = t;
1533           break;
1534         case 3:
1535           *r = w;
1536           *g = q;
1537           *b = v;
1538           break;
1539         case 4:
1540           *r = t;
1541           *g = w;
1542           *b = v;
1543           break;
1544         case 5:
1545           *r = v;
1546           *g = w;
1547           *b = q;
1548           break;
1549         }
1550     }
1551 }
1552
1553 static void
1554 gtk_color_selection_rgb_to_hsv (gdouble  r, gdouble  g, gdouble  b,
1555                                 gdouble *h, gdouble *s, gdouble *v)
1556 {
1557   double max, min, delta;
1558
1559   max = r;
1560   if (g > max)
1561     max = g;
1562   if (b > max)
1563     max = b;
1564
1565   min = r;
1566   if (g < min)
1567     min = g;
1568   if (b < min)
1569     min = b;
1570
1571   *v = max;
1572
1573   if (max != 0.0)
1574     *s = (max - min) / max;
1575   else
1576     *s = 0.0;
1577
1578   if (*s == 0.0)
1579     *h = -1.0;
1580   else
1581     {
1582       delta = max - min;
1583
1584       if (r == max)
1585         *h = (g - b) / delta;
1586       else if (g == max)
1587         *h = 2.0 + (b - r) / delta;
1588       else if (b == max)
1589         *h = 4.0 + (r - g) / delta;
1590
1591       *h = *h * 60.0;
1592
1593       if (*h < 0.0)
1594         *h = *h + 360;
1595     }
1596 }
1597
1598 /***************************/
1599 /* GtkColorSelectionDialog */
1600 /***************************/
1601
1602 guint
1603 gtk_color_selection_dialog_get_type (void)
1604 {
1605   static guint color_selection_dialog_type = 0;
1606
1607   if (!color_selection_dialog_type)
1608     {
1609       GtkTypeInfo colorsel_diag_info =
1610       {
1611         "GtkColorSelectionDialog",
1612         sizeof (GtkColorSelectionDialog),
1613         sizeof (GtkColorSelectionDialogClass),
1614         (GtkClassInitFunc) gtk_color_selection_dialog_class_init,
1615         (GtkObjectInitFunc) gtk_color_selection_dialog_init,
1616         /* reserved_1 */ NULL,
1617         /* reserved_2 */ NULL,
1618         (GtkClassInitFunc) NULL,
1619       };
1620
1621       color_selection_dialog_type = gtk_type_unique (gtk_window_get_type (), &colorsel_diag_info);
1622     }
1623
1624   return color_selection_dialog_type;
1625 }
1626
1627 static void
1628 gtk_color_selection_dialog_class_init (GtkColorSelectionDialogClass *klass)
1629 {
1630   GtkObjectClass *object_class;
1631
1632   object_class = (GtkObjectClass*) klass;
1633
1634   color_selection_dialog_parent_class = gtk_type_class (gtk_window_get_type ());
1635 }
1636
1637 static void
1638 gtk_color_selection_dialog_init (GtkColorSelectionDialog *colorseldiag)
1639 {
1640   GtkWidget *action_area, *frame;
1641
1642   gtk_widget_set_visual (GTK_WIDGET (colorseldiag), gdk_rgb_get_visual ());
1643   gtk_widget_set_colormap (GTK_WIDGET (colorseldiag), gdk_rgb_get_cmap ());
1644
1645   gtk_widget_push_visual (gdk_rgb_get_visual ());
1646   gtk_widget_push_colormap (gdk_rgb_get_cmap ());
1647
1648   colorseldiag->main_vbox = gtk_vbox_new (FALSE, 10);
1649   gtk_container_set_border_width (GTK_CONTAINER (colorseldiag), 10);
1650   gtk_container_add (GTK_CONTAINER (colorseldiag), colorseldiag->main_vbox);
1651   gtk_widget_show (colorseldiag->main_vbox);
1652
1653   frame = gtk_frame_new (NULL);
1654   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
1655   gtk_container_add (GTK_CONTAINER (colorseldiag->main_vbox), frame);
1656   gtk_widget_show (frame);
1657
1658   colorseldiag->colorsel = gtk_color_selection_new ();
1659   gtk_container_add (GTK_CONTAINER (frame), colorseldiag->colorsel);
1660   gtk_widget_show (colorseldiag->colorsel);
1661
1662   action_area = gtk_hbutton_box_new ();
1663   gtk_button_box_set_layout(GTK_BUTTON_BOX(action_area), GTK_BUTTONBOX_END);
1664   gtk_button_box_set_spacing(GTK_BUTTON_BOX(action_area), 5);
1665   gtk_box_pack_end (GTK_BOX (colorseldiag->main_vbox), action_area, FALSE, FALSE, 0);
1666   gtk_widget_show (action_area);
1667
1668   colorseldiag->ok_button = gtk_button_new_with_label (_("OK"));
1669   GTK_WIDGET_SET_FLAGS (colorseldiag->ok_button, GTK_CAN_DEFAULT);
1670   gtk_box_pack_start (GTK_BOX (action_area), colorseldiag->ok_button, TRUE, TRUE, 0);
1671   gtk_widget_grab_default (colorseldiag->ok_button);
1672   gtk_widget_show (colorseldiag->ok_button);
1673
1674   colorseldiag->cancel_button = gtk_button_new_with_label (_("Cancel"));
1675   GTK_WIDGET_SET_FLAGS (colorseldiag->cancel_button, GTK_CAN_DEFAULT);
1676   gtk_box_pack_start (GTK_BOX (action_area), colorseldiag->cancel_button, TRUE, TRUE, 0);
1677   gtk_widget_show (colorseldiag->cancel_button);
1678
1679   colorseldiag->help_button = gtk_button_new_with_label (_("Help"));
1680   GTK_WIDGET_SET_FLAGS (colorseldiag->help_button, GTK_CAN_DEFAULT);
1681   gtk_box_pack_start (GTK_BOX (action_area), colorseldiag->help_button, TRUE, TRUE, 0);
1682   gtk_widget_show (colorseldiag->help_button);
1683
1684   gtk_widget_pop_colormap ();
1685   gtk_widget_pop_visual ();
1686 }
1687
1688 GtkWidget *
1689 gtk_color_selection_dialog_new (const gchar *title)
1690 {
1691   GtkColorSelectionDialog *colorseldiag;
1692
1693   colorseldiag = gtk_type_new (gtk_color_selection_dialog_get_type ());
1694   gtk_window_set_title (GTK_WINDOW (colorseldiag), title);
1695
1696   return GTK_WIDGET (colorseldiag);
1697 }