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