2009-01-12 Alberto Garcia <agarcia@igalia.com>
[hildon] / src / hildon-check-button.c
index fa1c3d7..b647c1c 100644 (file)
  * SECTION:hildon-check-button
  * @short_description: Button with a check box inside
  *
- * This is a standard GtkButton which contains a check box and a
- * label. Functions are provided to get and set the values of both the
- * check box and the label. Standard GtkButton methods must not be
- * used for that.
+ * #HildonCheckButton is a button containing a label and a check box
+ * which will remain 'pressed-in' when clicked. Clicking again will
+ * make the check box toggle its state.
+ *
+ * The state of a #HildonCheckButton can be set using
+ * hildon_check_button_set_active(), and retrieved using
+ * hildon_check_button_get_active(). The label can be set using
+ * gtk_button_set_label() and retrieved using gtk_button_get_label().
+ *
+ * <note>
+ *   <para>
+ * #HildonCheckButton does NOT support an image, so don't use
+ * gtk_button_set_image().
+ *   </para>
+ * </note>
  *
  * <example>
+ * <title>Using a Hildon check button</title>
  * <programlisting>
  * void
- * button_clicked (GtkButton *button, gpointer user_data)
+ * button_toggled (HildonCheckButton *button, gpointer user_data)
  * {
  *     gboolean active;
  * <!-- -->
  *     GtkWidget *button;
  * <!-- -->
  *     button = hildon_check_button_new (HILDON_SIZE_AUTO);
- *     hildon_check_button_set_label (GTK_BUTTON (button), "Click me");
+ *     gtk_button_set_label (GTK_BUTTON (button), "Click me");
  * <!-- -->
- *     g_signal_connect (button, "clicked", G_CALLBACK (button_clicked), NULL);
+ *     g_signal_connect (button, "toggled", G_CALLBACK (button_toggled), NULL);
  * <!-- -->
  *     return button;
  * }
  * </programlisting>
  * </example>
-
  */
 
 #include                                        "hildon-check-button.h"
 
-static void
-check_button_clicked                            (GtkButton             *button,
-                                                 GtkCellRendererToggle *renderer)
-{
-    gboolean current = gtk_cell_renderer_toggle_get_active (renderer);
-    gtk_cell_renderer_toggle_set_active (renderer, !current);
-}
+enum {
+  TOGGLED,
+  LAST_SIGNAL
+};
 
-/**
- * hildon_check_button_set_label:
- * @button: A #GtkButton created with hildon_check_button_new()
- * @label: New text for the label.
- *
- * Sets the text of the button label to @label.
- **/
-void
-hildon_check_button_set_label                   (GtkButton   *button,
-                                                 const gchar *label)
-{
-    GtkCellRendererText *text_renderer;
+static guint                                    signals[LAST_SIGNAL] = { 0 };
 
-    g_return_if_fail (GTK_IS_BUTTON (button));
-    text_renderer = GTK_CELL_RENDERER_TEXT (g_object_get_data (G_OBJECT (button), "text-renderer"));
-    g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (text_renderer));
+G_DEFINE_TYPE                                   (HildonCheckButton, hildon_check_button, GTK_TYPE_BUTTON);
 
-    g_object_set (text_renderer, "text", label, NULL);
-}
+#define                                         HILDON_CHECK_BUTTON_GET_PRIVATE(obj) \
+                                                (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
+                                                HILDON_TYPE_CHECK_BUTTON, HildonCheckButtonPrivate));
+
+struct                                          _HildonCheckButtonPrivate
+{
+    GtkCellRendererToggle *toggle_renderer;
+};
 
 /**
- * hildon_check_button_get_label:
- * @button: A #GtkButton created with hildon_check_button_new()
+ * hildon_check_button_toggled:
+ * @button: A #HildonCheckButton
  *
- * Gets the text of the label inside the button.
+ * Emits the #HildonCheckButton::toggled signal on the #HildonCheckButton.
+ * There is no good reason for an application ever to call this function.
  *
- * Return value: the text of the label. This string is owned by the
- * button and must not be modified or freed.
- **/
-const gchar *
-hildon_check_button_get_label                   (GtkButton   *button)
+ * Since: 2.2
+ */
+void
+hildon_check_button_toggled                     (HildonCheckButton *button)
 {
-    GtkCellRendererText *text_renderer;
-    const gchar *text;
-
-    g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
-    text_renderer = GTK_CELL_RENDERER_TEXT (g_object_get_data (G_OBJECT (button), "text-renderer"));
-    g_return_val_if_fail (GTK_IS_CELL_RENDERER_TEXT (text_renderer), NULL);
-
-    g_object_get (text_renderer, "text", &text, NULL);
+    g_return_if_fail (HILDON_IS_CHECK_BUTTON (button));
 
-    return text;
+    g_signal_emit (button, signals[TOGGLED], 0);
 }
 
 /**
  * hildon_check_button_set_active:
- * @button: A #GtkButton created with hildon_check_button_new()
- * @is_active: new state for the check box
+ * @button: A #HildonCheckButton
+ * @is_active: new state for the button
  *
- * Sets the state of the check box.
+ * Sets the status of a #HildonCheckButton. Set to %TRUE if you want
+ * @button to be 'pressed-in', and %FALSE to raise it. This action
+ * causes the #HildonCheckButton::toggled signal to be emitted.
+ *
+ * Since: 2.2
  **/
 void
-hildon_check_button_set_active                  (GtkButton *button,
-                                                 gboolean   is_active)
+hildon_check_button_set_active                  (HildonCheckButton *button,
+                                                 gboolean           is_active)
 {
-    GtkCellRendererToggle *toggle_renderer;
+    gboolean prev_is_active;
 
-    g_return_if_fail (GTK_IS_BUTTON (button));
-    toggle_renderer = GTK_CELL_RENDERER_TOGGLE (g_object_get_data (G_OBJECT (button), "toggle-renderer"));
-    g_return_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle_renderer));
+    g_return_if_fail (HILDON_IS_CHECK_BUTTON (button));
 
-    gtk_cell_renderer_toggle_set_active (toggle_renderer, is_active);
+    prev_is_active = hildon_check_button_get_active (button);
+
+    if (prev_is_active != is_active) {
+        gtk_button_clicked (GTK_BUTTON (button));
+        gtk_widget_queue_draw (GTK_WIDGET (button));
+    }
 }
 
 /**
  * hildon_check_button_get_active:
- * @button: A #GtkButton created with hildon_check_button_new()
+ * @button: A #HildonCheckButton
+ *
+ * Gets the current state of @button.
  *
- * Gets the state of the check box.
+ * Return value: %TRUE if @button is active, %FALSE otherwise.
  *
- * Return value: %TRUE if the check box is active, %FALSE otherwise.
+ * Since: 2.2
  **/
 gboolean
-hildon_check_button_get_active                  (GtkButton *button)
+hildon_check_button_get_active                  (HildonCheckButton *button)
 {
-    GtkCellRendererToggle *toggle_renderer;
-
-    g_return_val_if_fail (GTK_IS_BUTTON (button), FALSE);
-    toggle_renderer = GTK_CELL_RENDERER_TOGGLE (g_object_get_data (G_OBJECT (button), "toggle-renderer"));
-    g_return_val_if_fail (GTK_IS_CELL_RENDERER_TOGGLE (toggle_renderer), FALSE);
+    g_return_val_if_fail (HILDON_IS_CHECK_BUTTON (button), FALSE);
 
-    return gtk_cell_renderer_toggle_get_active (toggle_renderer);
+    return gtk_cell_renderer_toggle_get_active (button->priv->toggle_renderer);
 }
 
 /**
  * hildon_check_button_new:
  * @size: Flags indicating the size of the new button
  *
- * This function creates a #GtkButton containing a label and a check
- * box.
+ * Creates a new #HildonCheckButton.
  *
- * This button has specific functions to get and set the values of the
- * label and the check box. You must not use the standard #GtkButton
- * methods for that.
+ * Return value: A newly created #HildonCheckButton
  *
- * To set the alignment you can use gtk_button_set_alignment()
- *
- * Return value: A newly created #GtkButton widget.
+ * Since: 2.2
  **/
 GtkWidget *
 hildon_check_button_new                         (HildonSizeType size)
 {
-    GtkWidget *button = gtk_button_new ();
+    GtkWidget *button = g_object_new (HILDON_TYPE_CHECK_BUTTON, "xalign", 0.0, NULL);
+    hildon_gtk_widget_set_theme_size (button, size);
+    return button;
+}
+
+static void
+hildon_check_button_clicked                     (GtkButton *button)
+{
+    HildonCheckButton *checkbutton = HILDON_CHECK_BUTTON (button);
+    gboolean current = hildon_check_button_get_active (checkbutton);
+
+    gtk_cell_renderer_toggle_set_active (checkbutton->priv->toggle_renderer, !current);
+
+    hildon_check_button_toggled (checkbutton);
+}
+
+static void
+hildon_check_button_apply_style                 (GtkWidget *widget)
+{
+    guint checkbox_size;
+    HildonCheckButtonPrivate *priv = HILDON_CHECK_BUTTON (widget)->priv;
+
+    gtk_widget_style_get (widget, "checkbox-size", &checkbox_size, NULL);
+
+    g_object_set (priv->toggle_renderer, "indicator-size", checkbox_size, NULL);
+}
+
+static void
+hildon_check_button_style_set                   (GtkWidget *widget,
+                                                 GtkStyle  *previous_style)
+{
+    if (GTK_WIDGET_CLASS (hildon_check_button_parent_class)->style_set)
+        GTK_WIDGET_CLASS (hildon_check_button_parent_class)->style_set (widget, previous_style);
+
+    hildon_check_button_apply_style (widget);
+}
+
+static void
+hildon_check_button_class_init                  (HildonCheckButtonClass *klass)
+{
+    GObjectClass *gobject_class = (GObjectClass*) klass;
+    GtkWidgetClass *widget_class = (GtkWidgetClass*) klass;
+    GtkButtonClass *button_class = (GtkButtonClass*) klass;
+
+    widget_class->style_set = hildon_check_button_style_set;
+    button_class->clicked = hildon_check_button_clicked;
+
+    klass->toggled = NULL;
+
+    /**
+     * HildonCheckButton::toggled
+     *
+     * Emitted when the #HildonCheckButton's state is changed.
+     *
+     * Since: 2.2
+     */
+    signals[TOGGLED] =
+        g_signal_new ("toggled",
+                      G_OBJECT_CLASS_TYPE (gobject_class),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (HildonCheckButtonClass, toggled),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+
+    gtk_widget_class_install_style_property (
+        widget_class,
+        g_param_spec_uint (
+            "checkbox-size",
+            "Size of the check box",
+            "Size of the check box",
+            0, G_MAXUINT, 26,
+            G_PARAM_READABLE));
+
+    g_type_class_add_private (klass, sizeof (HildonCheckButtonPrivate));
+}
+
+static void
+hildon_check_button_init                        (HildonCheckButton *button)
+{
+    HildonCheckButtonPrivate *priv = HILDON_CHECK_BUTTON_GET_PRIVATE (button);
     GtkWidget *cell_view = gtk_cell_view_new ();
-    GtkCellRenderer *toggle_renderer = gtk_cell_renderer_toggle_new ();
-    GtkCellRenderer *text_renderer = gtk_cell_renderer_text_new ();
-    GtkWidget *alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
 
-    /* Set the size of the button */
-    hildon_gtk_widget_set_theme_size (button, size);
+    /* Store private part */
+    button->priv = priv;
 
-    /* Pack everything */
-    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view), toggle_renderer, FALSE);
-    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view), text_renderer, FALSE);
-    gtk_container_add (GTK_CONTAINER (alignment), cell_view);
-    gtk_container_add (GTK_CONTAINER (button), alignment);
-    gtk_widget_show_all (GTK_WIDGET (alignment));
+    /* Make sure that the check box is always shown, no matter the value of gtk-button-images */
+    g_signal_connect (cell_view, "notify::visible", G_CALLBACK (gtk_widget_show), NULL);
 
-    /* Toggle the check box when the button is clicked */
-    g_signal_connect (button, "clicked", G_CALLBACK (check_button_clicked), toggle_renderer);
+    /* Create toggle renderer and pack it into the cell view */
+    priv->toggle_renderer = GTK_CELL_RENDERER_TOGGLE (gtk_cell_renderer_toggle_new ());
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view),
+                                GTK_CELL_RENDERER (priv->toggle_renderer), FALSE);
 
-    /* Set the data */
-    g_object_set_data (G_OBJECT (button), "toggle-renderer", toggle_renderer);
-    g_object_set_data (G_OBJECT (button), "text-renderer", text_renderer);
+    /* Add cell view to the image */
+    gtk_button_set_image (GTK_BUTTON (button), cell_view);
 
-    return button;
+    hildon_check_button_apply_style (GTK_WIDGET (button));
 }