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