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