be908242ef9caeefc20ecaac37c9a7d9bd9993ca
[hildon] / src / hildon-color-button.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-color-button
27  * @short_description: A widget to open HildonColorChooserDialog
28  * @see_also: #HildonColorChooserDialog, #HildonColorPopup
29  *
30  * HildonColorButton is a widget to open a HildonColorChooserDialog.
31  * The selected color is shown in the button.
32  * The selected color is a property of the button.
33  * The property name is "color" and its type is GtkColor.
34  * 
35  * <example>
36  * <title>HildonColorButton example</title>
37  * <programlisting>
38  * HildonColorButton *cbutton;
39  * GtkColor *color;
40  * <!-- -->
41  * cbutton = hildon_color_button_new();
42  * gtk_object_get( GTK_OBJECT(cbutton), "color", color );
43  * </programlisting>
44  * </example>
45  * 
46  */
47
48 #ifdef                                          HAVE_CONFIG_H
49 #include                                        <config.h>
50 #endif
51
52 #include                                        "hildon-color-button.h"
53 #include                                        <gtk/gtkbutton.h>
54 #include                                        <gtk/gtkalignment.h>
55 #include                                        <gtk/gtkdrawingarea.h>
56 #include                                        <gtk/gtksignal.h>
57 #include                                        <gdk/gdkkeysyms.h>
58 #include                                        "hildon-defines.h"
59 #include                                        "hildon-color-chooser-dialog.h"
60 #include                                        "hildon-color-button-private.h"
61
62 #define                                         COLOR_FILLED_HEIGHT 22
63
64 #define                                         COLOR_FILLED_WIDTH 22
65
66 #define                                         COLOR_BUTTON_WIDTH 52
67
68 #define                                         COLOR_BUTTON_HEIGHT 48
69
70 #define                                         OUTER_BORDER_RED 0
71
72 #define                                         OUTER_BORDER_BLUE 0
73
74 #define                                         OUTER_BORDER_GREEN 0
75
76 #define                                         OUTER_BORDER_THICKNESS 1
77
78 #define                                         INNER_BORDER_RED 65535
79
80 #define                                         INNER_BORDER_BLUE 65535
81
82 #define                                         INNER_BORDER_GREEN 65535
83
84 #define                                         INNER_BORDER_THICKNESS 2
85
86 enum 
87 {
88     PROP_0,
89     PROP_COLOR
90 };
91
92 static void
93 hildon_color_button_class_init                  (HildonColorButtonClass *klass);
94
95 static void
96 hildon_color_button_init                        (HildonColorButton *color_button);
97
98 static void
99 hildon_color_button_finalize                    (GObject *object);
100
101 static void
102 hildon_color_button_set_property                (GObject *object, 
103                                                  guint param_id,
104                                                  const GValue *value, 
105                                                  GParamSpec *pspec);
106
107 static void
108 hildon_color_button_get_property                (GObject *object, 
109                                                  guint param_id,
110                                                  GValue *value, 
111                                                  GParamSpec *pspec);
112
113 static void
114 hildon_color_button_realize                     (GtkWidget *widget);
115
116 static void
117 hildon_color_button_unrealize                   (GtkWidget *widget);
118
119 static void
120 hildon_color_button_clicked                     (GtkButton *button);
121
122 static gboolean
123 hildon_color_button_key_pressed                 (GtkWidget *button, 
124                                                  GdkEventKey *event,
125                                                  gpointer data);
126
127 static gint
128 hildon_color_field_expose_event                 (GtkWidget *widget, 
129                                                  GdkEventExpose *event,
130                                                  HildonColorButton *cb);
131
132 static gboolean
133 hildon_color_button_mnemonic_activate           (GtkWidget *widget,
134                                                  gboolean group_cycling);
135
136 static void
137 draw_grid                                       (GdkDrawable *drawable, 
138                                                  GdkGC *gc, 
139                                                  int x, 
140                                                  int y, 
141                                                  gint w, 
142                                                  gint h);
143
144 static gpointer                                 parent_class = NULL;
145
146 GType G_GNUC_CONST
147 hildon_color_button_get_type                    (void)
148 {
149     static GType color_button_type = 0;
150
151     if (! color_button_type)
152     {
153         static const GTypeInfo color_button_info =
154         {
155             sizeof (HildonColorButtonClass),
156             NULL,           /* base_init */
157             NULL,           /* base_finalize */
158             (GClassInitFunc) hildon_color_button_class_init,
159             NULL,           /* class_finalize */
160             NULL,           /* class_data */
161             sizeof (HildonColorButton),
162             0,              /* n_preallocs */
163             (GInstanceInitFunc) hildon_color_button_init,
164         };
165
166         color_button_type = g_type_register_static (GTK_TYPE_BUTTON, "HildonColorButton",
167                 &color_button_info, 0);
168     }
169
170     return color_button_type;
171 }
172
173 static void
174 hildon_color_button_class_init                  (HildonColorButtonClass *klass)
175 {
176     GObjectClass *gobject_class;
177     GtkButtonClass *button_class;
178     GtkWidgetClass *widget_class;
179
180     gobject_class = G_OBJECT_CLASS (klass);
181     button_class = GTK_BUTTON_CLASS (klass);
182     widget_class = GTK_WIDGET_CLASS (klass);
183     
184     parent_class = g_type_class_peek_parent (klass);
185
186     gobject_class->get_property     = hildon_color_button_get_property;
187     gobject_class->set_property     = hildon_color_button_set_property;
188     gobject_class->finalize         = hildon_color_button_finalize;
189     widget_class->realize           = hildon_color_button_realize;
190     widget_class->unrealize         = hildon_color_button_unrealize;
191     button_class->clicked           = hildon_color_button_clicked;
192     widget_class->mnemonic_activate = hildon_color_button_mnemonic_activate;
193
194     /**
195      * HildonColorButton:color:
196      *
197      * The currently selected color.
198      */
199     g_object_class_install_property (gobject_class, PROP_COLOR,
200             g_param_spec_boxed ("color",
201                 "Current Color",
202                 "The selected color",
203                 GDK_TYPE_COLOR,
204                 G_PARAM_READWRITE));
205
206     g_type_class_add_private (gobject_class, sizeof (HildonColorButtonPrivate));
207 }
208
209 /* FIXME Draw a dotted grid over the specified area to make it look 
210  * insensitive. Actually, we should generate that pixbuf once and 
211  * just render it over later... */
212 static void
213 draw_grid                                       (GdkDrawable *drawable, 
214                                                  GdkGC *gc, 
215                                                  int x, 
216                                                  int y,
217                                                  gint w, 
218                                                  gint h)
219 {
220     int currentx;
221     int currenty;
222     for (currenty = y; currenty <= h; currenty++)
223         for (currentx = ((currenty % 2 == 0) ? x : x + 1); currentx <= w; currentx += 2)
224             gdk_draw_point (drawable, gc, currentx, currenty);
225 }
226
227 /* Handle exposure events for the color picker's drawing area */
228 static gint
229 hildon_color_field_expose_event                 (GtkWidget *widget, 
230                                                  GdkEventExpose *event,
231                                                  HildonColorButton *cb)
232 {
233     HildonColorButtonPrivate *priv = HILDON_COLOR_BUTTON_GET_PRIVATE (cb);
234     GdkColor outer_border, inner_border;
235
236     g_assert (priv);
237
238     /* Create the outer border color */
239     outer_border.pixel = 0;
240     outer_border.red   = OUTER_BORDER_RED;
241     outer_border.blue  = OUTER_BORDER_BLUE;
242     outer_border.green = OUTER_BORDER_GREEN;
243
244     /* Create the inner border color */
245     inner_border.pixel = 0;
246     inner_border.red   = INNER_BORDER_RED;
247     inner_border.blue  = INNER_BORDER_BLUE;
248     inner_border.green = INNER_BORDER_GREEN;
249
250     /* serve the outer border color to the Graphic Context */
251     gdk_gc_set_rgb_fg_color (priv->gc, &outer_border);
252     /* draw the outer border as a filled rectangle */
253     gdk_draw_rectangle (widget->window,
254             (GTK_WIDGET_IS_SENSITIVE (widget)) ?  priv->gc : widget->style->bg_gc [GTK_STATE_INSENSITIVE],
255             TRUE,
256             0, 
257             0,
258             widget->allocation.width,
259             widget->allocation.height);
260
261     /* serve the inner border color to the Graphic Context */
262     gdk_gc_set_rgb_fg_color (priv->gc, &inner_border);
263
264     /* draw the inner border as a filled rectangle */
265     gdk_draw_rectangle (widget->window,
266             priv->gc,
267             TRUE,
268             OUTER_BORDER_THICKNESS, 
269             OUTER_BORDER_THICKNESS,
270             widget->allocation.width - (OUTER_BORDER_THICKNESS * 2),
271             widget->allocation.height - (OUTER_BORDER_THICKNESS * 2));
272
273     /* serve the actual color to the Graphic Context */
274     gdk_gc_set_rgb_fg_color(priv->gc, &priv->color);
275
276     /* draw the actual rectangle */
277     gdk_draw_rectangle(widget->window,
278             priv->gc,
279             TRUE,
280             INNER_BORDER_THICKNESS + OUTER_BORDER_THICKNESS,
281             INNER_BORDER_THICKNESS + OUTER_BORDER_THICKNESS,
282             widget->allocation.width - ((INNER_BORDER_THICKNESS + OUTER_BORDER_THICKNESS)*2),
283             widget->allocation.height - ((INNER_BORDER_THICKNESS + OUTER_BORDER_THICKNESS)*2));
284
285     if (! GTK_WIDGET_IS_SENSITIVE (widget)) {
286         draw_grid (GDK_DRAWABLE (widget->window), widget->style->bg_gc [GTK_STATE_INSENSITIVE], 
287                 INNER_BORDER_THICKNESS + OUTER_BORDER_THICKNESS,
288                 INNER_BORDER_THICKNESS + OUTER_BORDER_THICKNESS,
289                 widget->allocation.width  - ((INNER_BORDER_THICKNESS + OUTER_BORDER_THICKNESS)*2) + 2,
290                 widget->allocation.height - ((INNER_BORDER_THICKNESS + OUTER_BORDER_THICKNESS)*2) + 2);
291     }
292
293     return FALSE;
294 }
295
296 static void
297 hildon_color_button_init                        (HildonColorButton *cb)
298 {
299     GtkWidget *align;
300     GtkWidget *drawing_area;
301     HildonColorButtonPrivate *priv = HILDON_COLOR_BUTTON_GET_PRIVATE (cb);
302
303     priv->dialog = NULL;
304     priv->gc = NULL;
305
306     gtk_widget_push_composite_child ();
307
308     /* create widgets and pixbuf */
309     align = gtk_alignment_new (0.5, 0.5, 0, 0); /* composite widget */
310
311     drawing_area = gtk_drawing_area_new (); /* composite widget */
312
313     /* setting minimum sizes */
314     gtk_widget_set_size_request (GTK_WIDGET (cb), COLOR_BUTTON_WIDTH,
315             COLOR_BUTTON_HEIGHT);
316
317     gtk_widget_set_size_request (GTK_WIDGET(drawing_area),
318             COLOR_FILLED_WIDTH, COLOR_FILLED_HEIGHT);
319
320     /* Connect the callback function for exposure event */
321     g_signal_connect (drawing_area, "expose-event",
322             G_CALLBACK (hildon_color_field_expose_event), cb);
323
324     /* Connect to callback function for key press event */
325     g_signal_connect (G_OBJECT(cb), "key-press-event",
326             G_CALLBACK(hildon_color_button_key_pressed), cb);
327
328     /* packing */
329     gtk_container_add (GTK_CONTAINER (align), drawing_area);
330     gtk_container_add (GTK_CONTAINER (cb), align);
331
332     gtk_widget_show_all (align);
333
334     gtk_widget_pop_composite_child ();
335 }
336
337 /* Free memory used by HildonColorButton */
338 static void
339 hildon_color_button_finalize                    (GObject *object)
340 {
341     HildonColorButtonPrivate *priv = HILDON_COLOR_BUTTON_GET_PRIVATE (object);
342     g_assert (priv);
343
344     if (priv->dialog)
345     {
346         gtk_widget_destroy(priv->dialog);
347         priv->dialog = NULL;
348     }
349
350     if (G_OBJECT_CLASS (parent_class)->finalize)
351         G_OBJECT_CLASS (parent_class)->finalize (object);
352 }
353
354 static void
355 hildon_color_button_realize                     (GtkWidget *widget)
356 {
357     HildonColorButtonPrivate *priv = HILDON_COLOR_BUTTON_GET_PRIVATE (widget);
358     g_assert (priv);
359
360     GTK_WIDGET_CLASS (parent_class)->realize (widget);
361
362     priv->gc = gdk_gc_new (widget->window);
363 }
364
365 static void
366 hildon_color_button_unrealize                   (GtkWidget *widget)
367 {
368     HildonColorButtonPrivate *priv = HILDON_COLOR_BUTTON_GET_PRIVATE (widget);
369     g_assert (priv);
370
371     if (priv->gc != NULL) { 
372         g_object_unref (priv->gc);
373         priv->gc = NULL;
374     }
375
376     GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
377 }
378
379 /* Make the widget sensitive with the keyboard event */
380 static gboolean
381 hildon_color_button_mnemonic_activate           (GtkWidget *widget,
382                                                  gboolean group_cycling)
383 {
384     gtk_widget_grab_focus (widget);
385     return TRUE;
386 }
387
388 /* Popup a color selector dialog on button click */
389 static void
390 hildon_color_button_clicked                     (GtkButton *button)
391 {
392     HildonColorButton *cb = HILDON_COLOR_BUTTON (button);
393     HildonColorButtonPrivate *priv = HILDON_COLOR_BUTTON_GET_PRIVATE (button);
394     HildonColorChooserDialog *cs_dialog;
395     
396     g_assert (priv);
397     
398     cs_dialog = (HildonColorChooserDialog *) priv->dialog;
399
400     /* Popup the color selector dialog */
401     if (! cs_dialog)
402     {
403         /* The dialog hasn't been created yet, do it */
404         GtkWidget *parent = gtk_widget_get_toplevel (GTK_WIDGET(cb));
405         priv->dialog = hildon_color_chooser_dialog_new ();
406         cs_dialog = HILDON_COLOR_CHOOSER_DIALOG (priv->dialog);
407         if (parent)
408             gtk_window_set_transient_for (GTK_WINDOW (cs_dialog), GTK_WINDOW (parent));
409     }
410
411     /* Set the initial color for the color selector dialog */
412     hildon_color_chooser_dialog_set_color (cs_dialog, &priv->color);
413
414     /* Update the color for color button if selection was made */
415     if (gtk_dialog_run (GTK_DIALOG (cs_dialog)) == GTK_RESPONSE_OK)
416     {
417         hildon_color_chooser_dialog_get_color (cs_dialog, &priv->color);
418         hildon_color_button_set_color (HILDON_COLOR_BUTTON (button), &priv->color);
419         // FIXME A queue-draw should be enough here (not set needed)
420     } 
421
422     gtk_widget_hide (GTK_WIDGET(cs_dialog));
423 }
424
425 /* Popup a color selector dialog on hardkey Select press.
426  * FIXME This is a bit hacky, should work without thi. Check. */
427 static gboolean
428 hildon_color_button_key_pressed                 (GtkWidget *button, 
429                                                  GdkEventKey *event,
430                                                  gpointer data)
431 {
432     g_return_val_if_fail (HILDON_IS_COLOR_BUTTON (button), FALSE);
433
434     if (event->keyval == HILDON_HARDKEY_SELECT)
435     {
436         hildon_color_button_clicked (GTK_BUTTON (button));
437         return TRUE;
438     }
439
440     return FALSE;
441 }
442
443 static void
444 hildon_color_button_set_property                (GObject *object, 
445                                                  guint param_id,
446                                                  const GValue *value, 
447                                                  GParamSpec *pspec)
448 {
449     HildonColorButton *cb = HILDON_COLOR_BUTTON (object);
450     HildonColorButtonPrivate *priv = HILDON_COLOR_BUTTON_GET_PRIVATE (cb);
451     g_assert (priv);
452
453     switch (param_id) 
454     {
455
456         case PROP_COLOR:
457             priv->color = *(GdkColor *) g_value_get_boxed (value); 
458             gtk_widget_queue_draw (GTK_WIDGET (cb));
459             break;
460
461         default:
462             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
463             break;
464     }
465 }
466
467 static void
468 hildon_color_button_get_property                (GObject *object, 
469                                                  guint param_id,
470                                                  GValue *value, 
471                                                  GParamSpec *pspec)
472 {
473     HildonColorButton *cb = HILDON_COLOR_BUTTON (object);
474     HildonColorButtonPrivate *priv = HILDON_COLOR_BUTTON_GET_PRIVATE (cb);
475     g_assert (priv);
476
477     switch (param_id) 
478     {
479
480         case PROP_COLOR:
481             g_value_set_boxed (value, &priv->color);
482             break;
483
484         default:
485             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
486             break;
487     }
488 }
489
490 /**
491  * hildon_color_button_new:
492  *
493  * Creates a new color button. This returns a widget in the form of a
494  * small button containing a swatch representing the selected color.
495  * When the button is clicked, a color-selection dialog will open,
496  * allowing the user to select a color. The swatch will be updated to
497  * reflect the new color when the user finishes.
498  *
499  * Returns: a new color button
500  */
501 GtkWidget*
502 hildon_color_button_new                         (void)
503 {
504     return g_object_new (HILDON_TYPE_COLOR_BUTTON, NULL);
505 }
506
507 /**
508  * hildon_color_button_new_with_color:
509  * @color: a #GdkColor for the initial color
510  *
511  * Creates a new color button with @color as the initial color. 
512  *
513  * Returns: a new color button
514  */
515 GtkWidget*
516 hildon_color_button_new_with_color              (const GdkColor *color)
517 {
518     return g_object_new (HILDON_TYPE_COLOR_BUTTON, "color", color, NULL);
519 }
520
521 /**
522  * hildon_color_button_set_color:
523  * @button: a #HildonColorButton
524  * @color: a color to be set
525  *
526  * Sets the color selected by the button.
527  */
528 void
529 hildon_color_button_set_color                   (HildonColorButton *button, 
530                                                  GdkColor *color)
531 {
532     g_return_if_fail (HILDON_IS_COLOR_BUTTON (button));
533
534     g_object_set (G_OBJECT (button), "color", color, NULL);
535 }
536
537 /**
538  * hildon_color_button_get_color:
539  * @button: a #HildonColorButton
540  * @color: a color #GdkColor to be fillled with the current color
541  *
542  */
543 void
544 hildon_color_button_get_color                   (HildonColorButton *button, 
545                                                  GdkColor *color)
546 {
547     HildonColorButtonPrivate *priv = NULL; 
548     g_return_if_fail (HILDON_IS_COLOR_BUTTON (button));
549     g_return_if_fail (color != NULL);
550    
551     priv = HILDON_COLOR_BUTTON_GET_PRIVATE (button);
552     g_assert (priv);
553
554     color->red = priv->color.red;
555     color->green = priv->color.green;
556     color->blue = priv->color.blue;
557     color->pixel = priv->color.pixel;
558 }
559