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