]> Pileus Git - ~andy/gtk/blob - gtk/gtkcolorbutton.c
06cea22a16f873f89e2b99769dd51c2253515dd6
[~andy/gtk] / gtk / gtkcolorbutton.c
1 /*
2  * GTK - The GIMP Toolkit
3  * Copyright (C) 1998, 1999 Red Hat, Inc.
4  * All rights reserved.
5  *
6  * This Library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This Library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with the Gnome Library; see the file COPYING.LIB.  If not,
18  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21 /* Color picker button for GNOME
22  *
23  * Author: Federico Mena <federico@nuclecu.unam.mx>
24  *
25  * Modified by the GTK+ Team and others 2003.  See the AUTHORS
26  * file for a list of people on the GTK+ Team.  See the ChangeLog
27  * files for a list of changes.  These files are distributed with
28  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
29  */
30
31 #include <config.h>
32
33 #include "gtkcolorbutton.h"
34 #include "gdk/gdkkeysyms.h"
35 #include "gdk-pixbuf/gdk-pixbuf.h"
36 #include "gtkbutton.h"
37 #include "gtkmain.h"
38 #include "gtkalignment.h"
39 #include "gtkcolorsel.h"
40 #include "gtkcolorseldialog.h"
41 #include "gtkdnd.h"
42 #include "gtkdrawingarea.h"
43 #include "gtkframe.h"
44 #include "gtksignal.h"
45 #include "gtkmarshalers.h"
46 #include "gtkintl.h"
47
48 /* Size of checks and gray levels for alpha compositing checkerboard */
49 #define CHECK_SIZE  4
50 #define CHECK_DARK  21845  /* 65535 / 3     */
51 #define CHECK_LIGHT 43690 
52
53 #define GTK_COLOR_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COLOR_BUTTON, GtkColorButtonPrivate))
54
55 struct _GtkColorButtonPrivate 
56 {
57   GdkPixbuf *pixbuf;    /* Pixbuf for rendering sample */
58   GdkGC *gc;            /* GC for drawing */
59   
60   GtkWidget *drawing_area;/* Drawing area for color sample */
61   GtkWidget *cs_dialog; /* Color selection dialog */
62   
63   gchar *title;         /* Title for the color selection window */
64   
65   GdkColor color;
66   guint16 alpha;
67   
68   guint use_alpha : 1;  /* Use alpha or not */
69 };
70
71 /* Properties */
72 enum 
73 {
74   PROP_0,
75   PROP_USE_ALPHA,
76   PROP_TITLE,
77   PROP_COLOR,
78   PROP_ALPHA
79 };
80
81 /* Signals */
82 enum 
83 {
84   COLOR_SET,
85   LAST_SIGNAL
86 };
87
88 static void gtk_color_button_class_init    (GtkColorButtonClass *klass);
89 static void gtk_color_button_init          (GtkColorButton      *color_button);
90
91 /* gobject signals */
92 static void gtk_color_button_finalize      (GObject             *object);
93 static void gtk_color_button_set_property  (GObject        *object,
94                                             guint           param_id,
95                                             const GValue   *value,
96                                             GParamSpec     *pspec);
97 static void gtk_color_button_get_property  (GObject        *object,
98                                             guint           param_id,
99                                             GValue         *value,
100                                             GParamSpec     *pspec);
101
102 /* gtkwidget signals */
103 static void gtk_color_button_realize       (GtkWidget *widget);
104 static void gtk_color_button_state_changed (GtkWidget           *widget, 
105                                             GtkStateType         previous_state);
106 static void gtk_color_button_style_set     (GtkWidget *widget, 
107                                             GtkStyle  *previous_style);
108
109 /* gtkbutton signals */
110 static void gtk_color_button_clicked       (GtkButton           *button);
111
112 /* source side drag signals */
113 static void gtk_color_button_drag_begin (GtkWidget        *widget,
114                                          GdkDragContext   *context,
115                                          gpointer          data);
116 static void gtk_color_button_drag_data_get (GtkWidget        *widget,
117                                             GdkDragContext   *context,
118                                             GtkSelectionData *selection_data,
119                                             guint             info,
120                                             guint             time,
121                                             GtkColorButton   *color_button);
122
123 /* target side drag signals */
124 static void gtk_color_button_drag_data_received (GtkWidget        *widget,
125                                                  GdkDragContext   *context,
126                                                  gint              x,
127                                                  gint              y,
128                                                  GtkSelectionData *selection_data,
129                                                  guint             info,
130                                                  guint32           time,
131                                                  GtkColorButton   *color_button);
132
133
134 static gpointer parent_class = NULL;
135 static guint color_button_signals[LAST_SIGNAL] = { 0 };
136
137 static GtkTargetEntry drop_types[] = { { "application/x-color", 0, 0 } };
138
139 GType
140 gtk_color_button_get_type (void)
141 {
142   static GType color_button_type = 0;
143   
144   if (!color_button_type)
145     {
146       static const GTypeInfo color_button_info =
147       {
148         sizeof (GtkColorButtonClass),
149         NULL,           /* base_init */
150         NULL,           /* base_finalize */
151         (GClassInitFunc) gtk_color_button_class_init,
152         NULL,           /* class_finalize */
153         NULL,           /* class_data */
154         sizeof (GtkColorButton),
155         0,              /* n_preallocs */
156         (GInstanceInitFunc) gtk_color_button_init,
157       };
158       
159       color_button_type =
160         g_type_register_static (GTK_TYPE_BUTTON, "GtkColorButton",
161                                 &color_button_info, 0);
162     }
163   
164   return color_button_type;
165 }
166
167 static void
168 gtk_color_button_class_init (GtkColorButtonClass *klass)
169 {
170   GObjectClass *gobject_class;
171   GtkObjectClass *object_class;
172   GtkWidgetClass *widget_class;
173   GtkButtonClass *button_class;
174
175   gobject_class = G_OBJECT_CLASS (klass);
176   object_class = GTK_OBJECT_CLASS (klass);
177   widget_class = GTK_WIDGET_CLASS (klass);
178   button_class = GTK_BUTTON_CLASS (klass);
179
180   parent_class = g_type_class_peek_parent (klass);
181
182   gobject_class->get_property = gtk_color_button_get_property;
183   gobject_class->set_property = gtk_color_button_set_property;
184   gobject_class->finalize = gtk_color_button_finalize;
185   widget_class->state_changed = gtk_color_button_state_changed;
186   widget_class->realize = gtk_color_button_realize;
187   widget_class->style_set = gtk_color_button_style_set;
188   button_class->clicked = gtk_color_button_clicked;
189   klass->color_set = NULL;
190
191   /**
192    * GtkColorButton:use-alpha:
193    *
194    * If this property is set to %TRUE, the color swatch on the button is rendered against a 
195    * checkerboard background to show its opacity and the opacity slider is displayed in the 
196    * color selection dialog. 
197    *
198    * Since: 2.4
199    */
200   g_object_class_install_property (gobject_class,
201                                    PROP_USE_ALPHA,
202                                    g_param_spec_boolean ("use_alpha", _("Use alpha"), 
203                                                          _("Whether or not to give the color an alpha value"),
204                                                          FALSE,
205                                                          (G_PARAM_READABLE | G_PARAM_WRITABLE)));
206
207   /**
208    * GtkColorButton:title:
209    *
210    * The title of the color selection dialog
211    *
212    * Since: 2.4
213    */
214   g_object_class_install_property (gobject_class,
215                                    PROP_TITLE,
216                                    g_param_spec_string ("title", 
217                                                         _("Title"), 
218                                                         _("The title of the color selection dialog"),
219                                                         _("Pick a Color"),
220                                                         (G_PARAM_READABLE | G_PARAM_WRITABLE)));
221
222   /**
223    * GtkColorButton:color:
224    *
225    * The selected color.
226    *
227    * Since: 2.4
228    */
229   g_object_class_install_property (gobject_class,
230                                    PROP_COLOR,
231                                    g_param_spec_boxed ("color",
232                                                        _("Current Color"),
233                                                        _("The selected color"),
234                                                        GDK_TYPE_COLOR,
235                                                        G_PARAM_READABLE | G_PARAM_WRITABLE));
236
237   /**
238    * GtkColorButton:alpha:
239    *
240    * The selected opacity value (0 fully transparent, 65535 fully opaque). 
241    *
242    * Since: 2.4
243    */
244   g_object_class_install_property (gobject_class,
245                                    PROP_ALPHA,
246                                    g_param_spec_uint ("alpha",
247                                                       _("Current Alpha"),
248                                                       _("The selected opacity value (0 fully transparent, 65535 fully opaque)"),
249                                                       0, 65535, 65535,
250                                                       G_PARAM_READABLE | G_PARAM_WRITABLE));
251         
252   /**
253    * GtkColorButton::color-set:
254    * @widget: the object which received the signal.
255    * 
256    * The ::color-set signal is emitted when the user selects a color. When handling this signal,
257    * use gtk_color_button_get_color() and gtk_color_button_get_alpha() to find out which color 
258    * was just selected.
259    *
260    * Since: 2.4
261    */
262   color_button_signals[COLOR_SET] = g_signal_new ("color_set",
263                                                   G_TYPE_FROM_CLASS (gobject_class),
264                                                   G_SIGNAL_RUN_FIRST,
265                                                   G_STRUCT_OFFSET (GtkColorButtonClass, color_set),
266                                                   NULL, NULL,
267                                                   _gtk_marshal_VOID__VOID,
268                                                   G_TYPE_NONE, 0);
269
270   g_type_class_add_private (gobject_class, sizeof (GtkColorButtonPrivate));
271 }
272
273 static void
274 render (GtkColorButton *color_button)
275 {
276   gint dark_r, dark_g, dark_b;
277   gint light_r, light_g, light_b;
278   gint i, j, rowstride;
279   gint width, height;
280   gint c1[3], c2[3];
281   guchar *pixels;
282   guint8 insensitive_r = 0;
283   guint8 insensitive_g = 0;
284   guint8 insensitive_b = 0;
285
286   width = color_button->priv->drawing_area->allocation.width;
287   height = color_button->priv->drawing_area->allocation.height;
288   if (color_button->priv->pixbuf == NULL || 
289       gdk_pixbuf_get_width (color_button->priv->pixbuf) != width ||
290       gdk_pixbuf_get_height (color_button->priv->pixbuf) != height) 
291     {
292       if (color_button->priv->pixbuf != NULL)
293         g_object_unref (color_button->priv->pixbuf);
294       color_button->priv->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height);
295     }
296   
297
298   /* Compute dark and light check colors */
299
300   insensitive_r = GTK_WIDGET(color_button)->style->bg[GTK_STATE_INSENSITIVE].red >> 8;
301   insensitive_g = GTK_WIDGET(color_button)->style->bg[GTK_STATE_INSENSITIVE].green >> 8;
302   insensitive_b = GTK_WIDGET(color_button)->style->bg[GTK_STATE_INSENSITIVE].blue >> 8;
303
304   if (color_button->priv->use_alpha) 
305     {
306       dark_r = ((CHECK_DARK << 16) + (color_button->priv->color.red - CHECK_DARK) * color_button->priv->alpha) >> 24;
307       dark_g = ((CHECK_DARK << 16) + (color_button->priv->color.green - CHECK_DARK) * color_button->priv->alpha) >> 24;
308       dark_b = ((CHECK_DARK << 16) + (color_button->priv->color.blue - CHECK_DARK) * color_button->priv->alpha) >> 24;
309       
310       light_r = ((CHECK_LIGHT << 16) + (color_button->priv->color.red - CHECK_LIGHT) * color_button->priv->alpha) >> 24;
311       light_g = ((CHECK_LIGHT << 16) + (color_button->priv->color.green - CHECK_LIGHT) * color_button->priv->alpha) >> 24;
312       light_b = ((CHECK_LIGHT << 16) + (color_button->priv->color.blue - CHECK_LIGHT) * color_button->priv->alpha) >> 24;
313     } 
314   else 
315     {
316       dark_r = light_r = color_button->priv->color.red >> 8;
317       dark_g = light_g = color_button->priv->color.green >> 8;
318       dark_b = light_b = color_button->priv->color.blue >> 8;
319     }
320
321   /* Fill image buffer */
322   pixels = gdk_pixbuf_get_pixels (color_button->priv->pixbuf);
323   rowstride = gdk_pixbuf_get_rowstride (color_button->priv->pixbuf);
324   for (j = 0; j < height; j++) 
325     {
326       if ((j / CHECK_SIZE) & 1) 
327         {
328           c1[0] = dark_r;
329           c1[1] = dark_g;
330           c1[2] = dark_b;
331           
332           c2[0] = light_r;
333           c2[1] = light_g;
334           c2[2] = light_b;
335         } 
336       else 
337         {
338           c1[0] = light_r;
339           c1[1] = light_g;
340           c1[2] = light_b;
341           
342           c2[0] = dark_r;
343           c2[1] = dark_g;
344           c2[2] = dark_b;
345         }
346       
347     for (i = 0; i < width; i++) 
348       {
349         if (!GTK_WIDGET_SENSITIVE (GTK_WIDGET (color_button)) && (i+j)%2) 
350           {
351             *(pixels + j * rowstride + i * 3) = insensitive_r;
352             *(pixels + j * rowstride + i * 3 + 1) = insensitive_g;
353             *(pixels + j * rowstride + i * 3 + 2) = insensitive_b;
354           } 
355         else if ((i / CHECK_SIZE) & 1) 
356           {
357             *(pixels + j * rowstride + i * 3)     = c1[0];
358             *(pixels + j * rowstride + i * 3 + 1) = c1[1];
359             *(pixels + j * rowstride + i * 3 + 2) = c1[2];
360           }
361         else
362           {
363             *(pixels + j * rowstride + i * 3)     = c2[0];
364             *(pixels + j * rowstride + i * 3 + 1) = c2[1];
365             *(pixels + j * rowstride + i * 3 + 2) = c2[2];
366           }
367       }
368     }
369 }
370
371 /* Handle exposure events for the color picker's drawing area */
372 static gint
373 expose_event (GtkWidget      *widget, 
374               GdkEventExpose *event, 
375               gpointer        data)
376 {
377   GtkColorButton *color_button = GTK_COLOR_BUTTON (data);
378
379   gint width = color_button->priv->drawing_area->allocation.width;
380   gint height = color_button->priv->drawing_area->allocation.height;
381
382   if (color_button->priv->pixbuf == NULL ||
383       width != gdk_pixbuf_get_width (color_button->priv->pixbuf) ||
384       height != gdk_pixbuf_get_height (color_button->priv->pixbuf)) 
385     render (color_button);
386
387   gdk_draw_pixbuf (widget->window,
388                    color_button->priv->gc,
389                    color_button->priv->pixbuf,
390                    event->area.x,
391                    event->area.y,
392                    event->area.x,
393                    event->area.y,
394                    event->area.width,
395                    event->area.height,
396                    GDK_RGB_DITHER_MAX,
397                    event->area.x,
398                    event->area.y);
399   return FALSE;
400 }
401
402 static void
403 gtk_color_button_realize (GtkWidget *widget)
404 {
405   GtkColorButton *color_button = GTK_COLOR_BUTTON (widget);
406
407   GTK_WIDGET_CLASS (parent_class)->realize (widget);
408
409   if (color_button->priv->gc == NULL)
410     color_button->priv->gc = gdk_gc_new (widget->window);
411
412   render (color_button);
413 }
414
415 static void
416 gtk_color_button_style_set (GtkWidget *widget, 
417                             GtkStyle  *previous_style)
418 {
419   GtkColorButton *color_button = GTK_COLOR_BUTTON (widget);
420
421   GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
422
423   if (GTK_WIDGET_REALIZED (widget)) 
424     {
425       if (color_button->priv->pixbuf != NULL)
426         g_object_unref (color_button->priv->pixbuf);
427       color_button->priv->pixbuf = NULL;
428     }
429 }
430
431 static void
432 gtk_color_button_state_changed (GtkWidget   *widget, 
433                                           GtkStateType previous_state)
434 {
435   GtkColorButton *color_button = GTK_COLOR_BUTTON (widget);
436
437   if (widget->state == GTK_STATE_INSENSITIVE || previous_state == GTK_STATE_INSENSITIVE)
438     {
439       if (color_button->priv->pixbuf != NULL)
440         g_object_unref (color_button->priv->pixbuf);
441       color_button->priv->pixbuf = NULL;
442     }
443 }
444
445 static void
446 gtk_color_button_drag_data_received (GtkWidget        *widget,
447                                      GdkDragContext   *context,
448                                      gint              x,
449                                      gint              y,
450                                      GtkSelectionData *selection_data,
451                                      guint             info,
452                                      guint32           time,
453                                      GtkColorButton   *color_button)
454 {
455   guint16 *dropped;
456
457   if (selection_data->length < 0)
458     return;
459
460   /* We accept drops with the wrong format, since the KDE color
461    * chooser incorrectly drops application/x-color with format 8.
462    */
463   if (selection_data->length != 8) 
464     {
465       g_warning (_("Received invalid color data\n"));
466       return;
467     }
468
469
470   dropped = (guint16 *)selection_data->data;
471
472   color_button->priv->color.red = dropped[0];
473   color_button->priv->color.green = dropped[1];
474   color_button->priv->color.blue = dropped[2];
475   color_button->priv->alpha = dropped[3];
476
477   if (color_button->priv->pixbuf != NULL)
478     g_object_unref (color_button->priv->pixbuf);
479   color_button->priv->pixbuf = NULL;
480
481   gtk_widget_queue_draw (color_button->priv->drawing_area);
482
483   g_signal_emit (color_button, color_button_signals[COLOR_SET], 0);
484
485   g_object_freeze_notify (G_OBJECT (color_button));
486   g_object_notify (G_OBJECT (color_button), "color");
487   g_object_notify (G_OBJECT (color_button), "alpha");
488   g_object_thaw_notify (G_OBJECT (color_button));
489 }
490
491 static void
492 set_color_icon (GdkDragContext *context,
493                 GdkColor       *color)
494 {
495   GdkPixbuf *pixbuf;
496   guint32 pixel;
497
498   pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE,
499                            8, 48, 32);
500
501   pixel = ((color->red & 0xff00) << 16) | 
502           ((color->green & 0xff00) << 8) | 
503            (color->blue & 0xff00);
504
505   gdk_pixbuf_fill (pixbuf, pixel);
506   
507   gtk_drag_set_icon_pixbuf (context, pixbuf, -2, -2);
508   g_object_unref (pixbuf);
509 }
510
511 static void
512 gtk_color_button_drag_begin (GtkWidget      *widget,
513                              GdkDragContext *context,
514                              gpointer        data)
515 {
516   GtkColorButton *color_button = data;
517   
518   set_color_icon (context, &color_button->priv->color);
519 }
520
521 static void
522 gtk_color_button_drag_data_get (GtkWidget        *widget,
523                                 GdkDragContext   *context,
524                                 GtkSelectionData *selection_data,
525                                 guint             info,
526                                 guint             time,
527                                 GtkColorButton   *color_button)
528 {
529   guint16 dropped[4];
530
531   dropped[0] = color_button->priv->color.red;
532   dropped[1] = color_button->priv->color.green;
533   dropped[2] = color_button->priv->color.blue;
534   dropped[3] = color_button->priv->alpha;
535
536   gtk_selection_data_set (selection_data, selection_data->target,
537                           16, (guchar *)dropped, 8);
538 }
539
540 static void
541 gtk_color_button_init (GtkColorButton *color_button)
542 {
543   GtkWidget *alignment;
544   GtkWidget *frame;
545   PangoLayout *layout;
546   PangoRectangle rect;
547
548   /* Create the widgets */
549   color_button->priv = GTK_COLOR_BUTTON_GET_PRIVATE (color_button);
550
551   gtk_widget_push_composite_child ();
552
553   alignment = gtk_alignment_new (0.5, 0.5, 0.5, 1.0);
554   gtk_container_set_border_width (GTK_CONTAINER (alignment), 1);
555   gtk_container_add (GTK_CONTAINER (color_button), alignment);
556   gtk_widget_show (alignment);
557
558   frame = gtk_frame_new (NULL);
559   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_OUT);
560   gtk_container_add (GTK_CONTAINER (alignment), frame);
561   gtk_widget_show (frame);
562
563   color_button->priv->drawing_area = gtk_drawing_area_new ();
564
565   layout = gtk_widget_create_pango_layout (GTK_WIDGET (color_button), "Black");
566   pango_layout_get_pixel_extents (layout, NULL, &rect);
567   gtk_widget_set_size_request (color_button->priv->drawing_area, rect.width - 2, rect.height - 2);
568   g_signal_connect (color_button->priv->drawing_area, "expose_event",
569                     G_CALLBACK (expose_event), color_button);
570   gtk_container_add (GTK_CONTAINER (frame), color_button->priv->drawing_area);
571   gtk_widget_show (color_button->priv->drawing_area);
572
573   color_button->priv->title = g_strdup (_("Pick a Color")); /* default title */
574
575   /* Create the buffer for the image so that we can create an image.  
576    * Also create the picker's pixmap.
577    */
578   color_button->priv->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, rect.width, rect.height);
579
580   color_button->priv->gc = NULL;
581
582   /* Start with opaque black, alpha disabled */
583
584   color_button->priv->color.red = 0;
585   color_button->priv->color.green = 0;
586   color_button->priv->color.blue = 0;
587   color_button->priv->alpha = 65535;
588   color_button->priv->use_alpha = FALSE;
589
590   gtk_drag_dest_set (GTK_WIDGET (color_button),
591                      GTK_DEST_DEFAULT_MOTION |
592                      GTK_DEST_DEFAULT_HIGHLIGHT |
593                      GTK_DEST_DEFAULT_DROP,
594                      drop_types, 1, GDK_ACTION_COPY);
595   gtk_drag_source_set (GTK_WIDGET(color_button),
596                        GDK_BUTTON1_MASK|GDK_BUTTON3_MASK,
597                        drop_types, 1,
598                        GDK_ACTION_COPY);
599   g_signal_connect (color_button, "drag_begin",
600                     G_CALLBACK (gtk_color_button_drag_begin), color_button);
601   g_signal_connect (color_button, "drag_data_received",
602                     G_CALLBACK (gtk_color_button_drag_data_received), color_button);
603   g_signal_connect (color_button, "drag_data_get",
604                     G_CALLBACK (gtk_color_button_drag_data_get), color_button);
605
606   gtk_widget_pop_composite_child ();
607 }
608
609 static void
610 gtk_color_button_finalize (GObject *object)
611 {
612   GtkColorButton *color_button = GTK_COLOR_BUTTON (object);
613
614   if (color_button->priv->gc != NULL)
615     g_object_unref (G_OBJECT (color_button->priv->gc));
616   color_button->priv->gc = NULL;
617
618   if (color_button->priv->cs_dialog != NULL)
619     gtk_widget_destroy (color_button->priv->cs_dialog);
620   color_button->priv->cs_dialog = NULL;
621
622   if (color_button->priv->pixbuf != NULL)
623     g_object_unref (color_button->priv->pixbuf);
624   color_button->priv->pixbuf = NULL;
625
626   g_free (color_button->priv->title);
627   color_button->priv->title = NULL;
628
629   G_OBJECT_CLASS (parent_class)->finalize (object);
630 }
631
632
633 /**
634  * gtk_color_button_new:
635  *
636  * Creates a new color button. This returns a widget in the form of
637  * a small button containing a swatch representing the current selected 
638  * color. When the button is clicked, a color-selection dialog will open, 
639  * allowing the user to select a color. The swatch will be updated to reflect 
640  * the new color when the user finishes.
641  *
642  * Returns: a new color button.
643  *
644  * Since: 2.4
645  */
646 GtkWidget *
647 gtk_color_button_new (void)
648 {
649   return g_object_new (GTK_TYPE_COLOR_BUTTON, NULL);
650 }
651
652 /**
653  * gtk_color_button_new_with_color:
654  * @color: A #GdkColor to set the current color with.
655  *
656  * Creates a new color button. 
657  *
658  * Returns: a new color button.
659  *
660  * Since: 2.4
661  */
662 GtkWidget *
663 gtk_color_button_new_with_color (GdkColor *color)
664 {
665   return g_object_new (GTK_TYPE_COLOR_BUTTON, "color", color, NULL);
666 }
667
668 static void
669 dialog_ok_clicked (GtkWidget *widget, 
670                    gpointer   data)
671 {
672   GtkColorButton *color_button = GTK_COLOR_BUTTON (data);
673   GtkColorSelection *color_selection;
674
675   color_selection = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (color_button->priv->cs_dialog)->colorsel);
676
677   gtk_color_selection_get_current_color (color_selection, &color_button->priv->color);
678   color_button->priv->alpha = gtk_color_selection_get_current_alpha (color_selection);
679
680   if (color_button->priv->pixbuf != NULL)
681     g_object_unref (color_button->priv->pixbuf);
682   color_button->priv->pixbuf = NULL;
683
684   gtk_widget_hide (color_button->priv->cs_dialog);
685
686   gtk_widget_queue_draw (color_button->priv->drawing_area);
687
688   g_signal_emit (color_button, color_button_signals[COLOR_SET], 0);
689
690   g_object_freeze_notify (G_OBJECT (color_button));
691   g_object_notify (G_OBJECT (color_button), "color");
692   g_object_notify (G_OBJECT (color_button), "alpha");
693   g_object_thaw_notify (G_OBJECT (color_button));
694 }
695
696 static gboolean
697 dialog_destroy (GtkWidget *widget, 
698                 gpointer   data)
699 {
700   GtkColorButton *color_button = GTK_COLOR_BUTTON (data);
701   
702   color_button->priv->cs_dialog = NULL;
703
704   return FALSE;
705 }
706
707 static void
708 dialog_cancel_clicked (GtkWidget *widget,
709                        gpointer   data)
710 {
711   GtkColorButton *color_button = GTK_COLOR_BUTTON (data);
712   
713   gtk_widget_hide (color_button->priv->cs_dialog);  
714 }
715
716 static void
717 gtk_color_button_clicked (GtkButton *button)
718 {
719   GtkColorButton *color_button = GTK_COLOR_BUTTON (button);
720   GtkColorSelectionDialog *color_dialog;
721
722   /* if dialog already exists, make sure it's shown and raised */
723   if (!color_button->priv->cs_dialog) 
724     {
725       /* Create the dialog and connects its buttons */
726       GtkWidget *parent;
727       
728       parent = gtk_widget_get_toplevel (GTK_WIDGET (color_button));
729       
730       color_button->priv->cs_dialog = gtk_color_selection_dialog_new (color_button->priv->title);
731       
732       color_dialog = GTK_COLOR_SELECTION_DIALOG (color_button->priv->cs_dialog);
733
734       if (parent)
735         gtk_window_set_transient_for (GTK_WINDOW (color_dialog),
736                                       GTK_WINDOW (parent));
737       
738       g_signal_connect (color_dialog->ok_button, "clicked",
739                         G_CALLBACK (dialog_ok_clicked), color_button);
740       g_signal_connect (color_dialog->cancel_button, "clicked",
741                         G_CALLBACK (dialog_cancel_clicked), color_button);
742       g_signal_connect (color_dialog, "destroy",
743                         G_CALLBACK (dialog_destroy), color_button);
744       
745       /* If there is a grabbed window, set new dialog as modal */
746       if (gtk_grab_get_current ())
747         gtk_window_set_modal (GTK_WINDOW (color_button->priv->cs_dialog),TRUE);
748     }
749
750   color_dialog = GTK_COLOR_SELECTION_DIALOG (color_button->priv->cs_dialog);
751
752   gtk_color_selection_set_has_opacity_control (GTK_COLOR_SELECTION (color_dialog->colorsel),
753                                                color_button->priv->use_alpha);
754   
755   gtk_color_selection_set_previous_color (GTK_COLOR_SELECTION (color_dialog->colorsel), 
756                                           &color_button->priv->color);
757   gtk_color_selection_set_previous_alpha (GTK_COLOR_SELECTION (color_dialog->colorsel), 
758                                           color_button->priv->alpha);
759
760   gtk_color_selection_set_current_color (GTK_COLOR_SELECTION (color_dialog->colorsel), 
761                                          &color_button->priv->color);
762   gtk_color_selection_set_current_alpha (GTK_COLOR_SELECTION (color_dialog->colorsel), 
763                                          color_button->priv->alpha);
764
765   gtk_window_present (GTK_WINDOW (color_button->priv->cs_dialog));
766 }
767
768 /**
769  * gtk_color_button_set_color:
770  * @color_button: a #GtkColorButton.
771  * @color: A #GdkColor to set the current color with.
772  *
773  * Sets the current color to be @color.
774  *
775  * Since: 2.4
776  **/
777 void
778 gtk_color_button_set_color (GtkColorButton *color_button,
779                             GdkColor       *color)
780 {
781   g_return_if_fail (GTK_IS_COLOR_BUTTON (color_button));
782
783   color_button->priv->color.red = color->red;
784   color_button->priv->color.green = color->green;
785   color_button->priv->color.blue = color->blue;
786
787   g_object_notify (G_OBJECT (color_button), "color");
788 }
789
790
791 /**
792  * gtk_color_button_set_alpha:
793  * @color_button: a #GtkColorButton.
794  * @alpha: an integer between 0 and 65535.
795  *
796  * Sets the current opacity to be @alpha. 
797  *
798  * Since: 2.4
799  **/
800 void
801 gtk_color_button_set_alpha (GtkColorButton *color_button,
802                             guint16         alpha)
803 {
804   g_return_if_fail (GTK_IS_COLOR_BUTTON (color_button));
805
806   color_button->priv->alpha = alpha;
807   
808   g_object_notify (G_OBJECT (color_button), "alpha");
809 }
810
811 /**
812  * gtk_color_button_get_color:
813  * @color_button: a #GtkColorButton.
814  * @color: a #GdkColor to fill in with the current color.
815  *
816  * Sets @color to be the current color in the #GtkColorButton widget.
817  *
818  * Since: 2.4
819  **/
820 void
821 gtk_color_button_get_color (GtkColorButton *color_button,
822                             GdkColor       *color)
823 {
824   g_return_if_fail (GTK_IS_COLOR_BUTTON (color_button));
825   
826   color->red = color_button->priv->color.red;
827   color->green = color_button->priv->color.green;
828   color->blue = color_button->priv->color.blue;
829 }
830
831 /**
832  * gtk_color_button_get_alpha:
833  * @color_button: a #GtkColorButton.
834  *
835  * Returns the current alpha value. 
836  *
837  * Return value: an integer between 0 and 65535.
838  *
839  * Since: 2.4
840  **/
841 guint16
842 gtk_color_button_get_alpha (GtkColorButton *color_button)
843 {
844   g_return_val_if_fail (GTK_IS_COLOR_BUTTON (color_button), 0);
845   
846   return color_button->priv->alpha;
847 }
848
849 /**
850  * gtk_color_button_set_use_alpha:
851  * @color_button: a #GtkColorButton.
852  * @use_alpha: %TRUE if color button should use alpha channel, %FALSE if not.
853  *
854  * Sets whether or not the color button should use the alpha channel.
855  *
856  * Since: 2.4
857  */
858 void
859 gtk_color_button_set_use_alpha (GtkColorButton *color_button, 
860                                 gboolean        use_alpha)
861 {
862   g_return_if_fail (GTK_IS_COLOR_BUTTON (color_button));
863
864   use_alpha = (use_alpha != FALSE);
865
866   if (color_button->priv->use_alpha != use_alpha) 
867     {
868       color_button->priv->use_alpha = use_alpha;
869
870       render (color_button);
871       gtk_widget_queue_draw (color_button->priv->drawing_area);
872
873       g_object_notify (G_OBJECT (color_button), "use_alpha");
874     }
875 }
876
877 /**
878  * gtk_color_button_get_use_alpha:
879  * @color_button: a #GtkColorButton.
880  *
881  * Does the color selection dialog use the alpha channel?
882  *
883  * Returns: %TRUE if the color sample uses alpha channel, %FALSE if not.
884  *
885  * Since: 2.4
886  */
887 gboolean
888 gtk_color_button_get_use_alpha (GtkColorButton *color_button)
889 {
890   g_return_val_if_fail (GTK_IS_COLOR_BUTTON (color_button), FALSE);
891
892   return color_button->priv->use_alpha;
893 }
894
895
896 /**
897  * gtk_color_button_set_title:
898  * @color_button: a #GtkColorButton
899  * @title: String containing new window title.
900  *
901  * Sets the title for the color selection dialog.
902  *
903  * Since: 2.4
904  */
905 void
906 gtk_color_button_set_title (GtkColorButton *color_button, 
907                             const gchar    *title)
908 {
909   gchar *old_title;
910
911   g_return_if_fail (GTK_IS_COLOR_BUTTON (color_button));
912
913   old_title = color_button->priv->title;
914   color_button->priv->title = g_strdup (title);
915   g_free (old_title);
916
917   if (color_button->priv->cs_dialog)
918     gtk_window_set_title (GTK_WINDOW (color_button->priv->cs_dialog), 
919                           color_button->priv->title);
920   
921   g_object_notify (G_OBJECT (color_button), "title");
922 }
923
924 /**
925  * gtk_color_button_get_title:
926  * @color_button: a #GtkColorButton
927  *
928  * Gets the title of the color selection dialog.
929  *
930  * Returns: An internal string, do not free the return value
931  *
932  * Since: 2.4
933  */
934 G_CONST_RETURN gchar *
935 gtk_color_button_get_title (GtkColorButton *color_button)
936 {
937   g_return_val_if_fail (GTK_IS_COLOR_BUTTON (color_button), NULL);
938
939   return color_button->priv->title;
940 }
941
942 static void
943 gtk_color_button_set_property (GObject      *object,
944                                guint         param_id,
945                                const GValue *value,
946                                GParamSpec   *pspec)
947 {
948   GtkColorButton *color_button = GTK_COLOR_BUTTON (object);
949
950   switch (param_id) 
951     {
952     case PROP_USE_ALPHA:
953       gtk_color_button_set_use_alpha (color_button, g_value_get_boolean (value));
954       break;
955     case PROP_TITLE:
956       gtk_color_button_set_title (color_button, g_value_get_string (value));
957       break;
958     case PROP_COLOR:
959       gtk_color_button_set_color (color_button, g_value_get_boxed (value));
960       break;
961     case PROP_ALPHA:
962       gtk_color_button_set_alpha (color_button, g_value_get_uint (value));
963       break;
964     default:
965       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
966       break;
967     }
968 }
969
970 static void
971 gtk_color_button_get_property (GObject    *object,
972                                guint       param_id,
973                                GValue     *value,
974                                GParamSpec *pspec)
975 {
976   GtkColorButton *color_button = GTK_COLOR_BUTTON (object);
977   GdkColor color;
978
979   switch (param_id) 
980     {
981     case PROP_USE_ALPHA:
982       g_value_set_boolean (value, gtk_color_button_get_use_alpha (color_button));
983       break;
984     case PROP_TITLE:
985       g_value_set_string (value, gtk_color_button_get_title (color_button));
986       break;
987     case PROP_COLOR:
988       gtk_color_button_get_color (color_button, &color);
989       g_value_set_boxed (value, &color);
990       break;
991     case PROP_ALPHA:
992       g_value_set_uint (value, gtk_color_button_get_alpha (color_button));
993       break;
994     default:
995       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
996       break;
997     }
998 }