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