[gtk+/buttons: 1/5] GtkButton: link to a GAction



commit da39db7b2fc7501b465c37b69c64a8876f151350
Author: Ryan Lortie <desrt desrt ca>
Date:   Wed Dec 1 00:55:27 2010 -0500

    GtkButton: link to a GAction
    
    Link GtkButton to GAction and track the state of the action.
    
    Make the 'active' state of GtkToggleButton decided from the state of the
    GAction.  Do no internal tracking of this state.
    
    Bug #636108

 gtk/gtkbutton.c        |  193 +++++++++++++++++++++++++++++++++++++++++++++---
 gtk/gtkbutton.h        |    7 ++-
 gtk/gtkbuttonprivate.h |    5 +
 gtk/gtkcheckbutton.c   |    8 ++-
 gtk/gtkradiobutton.c   |    8 ++-
 gtk/gtktogglebutton.c  |   26 ++++---
 6 files changed, 222 insertions(+), 25 deletions(-)
---
diff --git a/gtk/gtkbutton.c b/gtk/gtkbutton.c
index 71fb865..5337887 100644
--- a/gtk/gtkbutton.c
+++ b/gtk/gtkbutton.c
@@ -90,6 +90,8 @@ enum {
   PROP_XALIGN,
   PROP_YALIGN,
   PROP_IMAGE_POSITION,
+  PROP_IS_TOGGLE,
+  PROP_ACTION,
 
   /* activatable properties */
   PROP_ACTIVATABLE_RELATED_ACTION,
@@ -99,6 +101,7 @@ enum {
 
 static void gtk_button_destroy        (GtkWidget          *widget);
 static void gtk_button_dispose        (GObject            *object);
+static void gtk_button_finalize       (GObject            *object);
 static void gtk_button_set_property   (GObject            *object,
                                        guint               prop_id,
                                        const GValue       *value,
@@ -188,6 +191,7 @@ gtk_button_class_init (GtkButtonClass *klass)
   gobject_class->dispose      = gtk_button_dispose;
   gobject_class->set_property = gtk_button_set_property;
   gobject_class->get_property = gtk_button_get_property;
+  gobject_class->finalize     = gtk_button_finalize;
 
   widget_class->get_preferred_width  = gtk_button_get_preferred_width;
   widget_class->get_preferred_height = gtk_button_get_preferred_height;
@@ -330,6 +334,36 @@ gtk_button_class_init (GtkButtonClass *klass)
                                                       GTK_POS_LEFT,
                                                       GTK_PARAM_READWRITE));
 
+  /**
+   * GtkButton:is-toggle:
+   *
+   * Determines if the default action added to the button has a boolean
+   * state or not.  This property is only used at construct time.
+   * 
+   * Since: 3.0
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_IS_TOGGLE,
+                                   g_param_spec_boolean ("is-toggle",
+                                                         P_("Is toggle"),
+                                                         P_("If the default action should be stateful"),
+                                                         FALSE, GTK_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+  /**
+   * GtkButton:action:
+   *
+   * The #GAction that the button activates.
+   *
+   * Since: 3.0
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_ACTION,
+                                   g_param_spec_object ("action",
+                                                        P_("Action"),
+                                                        P_("The GAction that the button activates."),
+                                                        G_TYPE_ACTION,
+                                                        GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_RELATED_ACTION, "related-action");
   g_object_class_override_property (gobject_class, PROP_ACTIVATABLE_USE_ACTION_APPEARANCE, "use-action-appearance");
 
@@ -578,6 +612,81 @@ gtk_button_destroy (GtkWidget *widget)
   GTK_WIDGET_CLASS (gtk_button_parent_class)->destroy (widget);
 }
 
+static void
+gtk_button_action_state_changed (GAction    *action,
+                                 GParamSpec *pspec,
+                                 gpointer    user_data)
+{
+  GtkButton *button = GTK_BUTTON (user_data);
+  GVariant *state;
+
+  state = g_action_get_state (action);
+
+  if (!g_variant_equal (state, button->priv->state))
+    {
+      g_variant_unref (button->priv->state);
+      button->priv->state = g_variant_ref (state);
+
+      GTK_BUTTON_GET_CLASS (button)
+        ->action_state_changed (button, state);
+    }
+
+  g_variant_unref (state);
+}
+
+static void
+gtk_button_release_action (GtkButton *button)
+{
+  if (button->priv->state_id)
+    g_signal_handler_disconnect (button->priv->g_action,
+                                 button->priv->state_id);
+
+  g_object_unref (button->priv->g_action);
+  button->priv->state_id = 0;
+}
+
+static void
+gtk_button_setup_action (GtkButton *button)
+{
+  GVariant *state;
+
+  state = g_action_get_state (button->priv->g_action);
+
+  if (state != NULL)
+    button->priv->state_id =
+      g_signal_connect (button->priv->g_action, "notify::state",
+                        G_CALLBACK (gtk_button_action_state_changed), button);
+
+  if (state != button->priv->state)
+    {
+      if (state == NULL ||
+          button->priv->state == NULL ||
+          !g_variant_equal (state, button->priv->state))
+        {
+          if (button->priv->state != NULL)
+            g_variant_unref (button->priv->state);
+
+          button->priv->state = NULL;
+
+          if (state != NULL)
+            button->priv->state = g_variant_ref (state);
+
+          GTK_BUTTON_GET_CLASS (button)
+            ->action_state_changed (button, state);
+        }
+    }
+
+  if (state)
+    g_variant_unref (state);
+}
+
+static void
+action_activated (GAction  *action,
+                  gpointer  user_data)
+{
+  g_action_set_state (action, g_variant_new_boolean (!g_variant_get_boolean (g_action_get_state (action))));
+}
+
 static GObject*
 gtk_button_constructor (GType                  type,
 			guint                  n_construct_properties,
@@ -596,9 +705,31 @@ gtk_button_constructor (GType                  type,
 
   priv->constructed = TRUE;
 
+  if (priv->g_action == NULL)
+    {
+      if (priv->is_toggle)
+        {
+          priv->g_action = G_ACTION (g_simple_action_new_stateful ("anonymous", NULL,
+                                                                   g_variant_new_boolean (FALSE)));
+          g_signal_connect (priv->g_action, "activate",
+                            G_CALLBACK (action_activated), NULL);
+        }
+      else
+        priv->g_action = G_ACTION (g_simple_action_new ("anonymous", NULL));
+
+      gtk_button_setup_action (button);
+    }
+
   if (priv->label_text != NULL)
     gtk_button_construct_child (button);
-  
+
+   /* This should be a default handler, but for compatibility reasons
+   * we need to support derived classes that don't chain up their
+   * clicked handler.
+   */
+  g_signal_connect_after (button, "clicked",
+                          G_CALLBACK (gtk_real_button_clicked), NULL);
+ 
   return object;
 }
 
@@ -662,10 +793,22 @@ gtk_button_dispose (GObject *object)
       gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), NULL);
       priv->action = NULL;
     }
+
   G_OBJECT_CLASS (gtk_button_parent_class)->dispose (object);
 }
 
 static void
+gtk_button_finalize (GObject *object)
+{
+  GtkButton *button = GTK_BUTTON (object);
+
+  gtk_button_release_action (button);
+
+  G_OBJECT_CLASS (gtk_button_parent_class)
+    ->finalize (object);
+}
+
+static void
 gtk_button_set_property (GObject         *object,
                          guint            prop_id,
                          const GValue    *value,
@@ -703,6 +846,13 @@ gtk_button_set_property (GObject         *object,
     case PROP_IMAGE_POSITION:
       gtk_button_set_image_position (button, g_value_get_enum (value));
       break;
+    case PROP_IS_TOGGLE:
+      priv->is_toggle = g_value_get_boolean (value);
+      break;
+    case PROP_ACTION:
+      if (g_value_get_object (value) != NULL)
+        gtk_button_set_action (button, g_value_get_object (value));
+      break;
     case PROP_ACTIVATABLE_RELATED_ACTION:
       gtk_button_set_related_action (button, g_value_get_object (value));
       break;
@@ -753,6 +903,9 @@ gtk_button_get_property (GObject         *object,
     case PROP_IMAGE_POSITION:
       g_value_set_enum (value, priv->image_position);
       break;
+    case PROP_ACTION:
+      g_value_set_object (value, priv->g_action);
+      break;
     case PROP_ACTIVATABLE_RELATED_ACTION:
       g_value_set_object (value, priv->action);
       break;
@@ -893,6 +1046,32 @@ gtk_button_sync_action_properties (GtkActivatable *activatable,
     }
 }
 
+GAction *
+gtk_button_get_action (GtkButton *button)
+{
+  g_return_val_if_fail (GTK_IS_BUTTON (button), NULL);
+
+  return button->priv->g_action;
+}
+
+void
+gtk_button_set_action (GtkButton *button,
+                       GAction   *action)
+{
+  g_return_if_fail (GTK_IS_BUTTON (button));
+  g_return_if_fail (G_IS_ACTION (action));
+
+  if (button->priv->g_action != action)
+    {
+      g_object_freeze_notify (G_OBJECT (button));
+      gtk_button_release_action (button);
+      button->priv->g_action = g_object_ref (action);
+      gtk_button_setup_action (button);
+      g_object_notify (G_OBJECT (button), "action");
+      g_object_thaw_notify (G_OBJECT (button));
+    }
+}
+
 static void
 gtk_button_set_related_action (GtkButton *button,
 			       GtkAction *action)
@@ -902,15 +1081,6 @@ gtk_button_set_related_action (GtkButton *button,
   if (priv->action == action)
     return;
 
-  /* This should be a default handler, but for compatibility reasons
-   * we need to support derived classes that don't chain up their
-   * clicked handler.
-   */
-  g_signal_handlers_disconnect_by_func (button, gtk_real_button_clicked, NULL);
-  if (action)
-    g_signal_connect_after (button, "clicked",
-                            G_CALLBACK (gtk_real_button_clicked), NULL);
-
   gtk_activatable_do_set_related_action (GTK_ACTIVATABLE (button), action);
 
   priv->action = action;
@@ -1807,6 +1977,9 @@ gtk_real_button_clicked (GtkButton *button)
 
   if (priv->action)
     gtk_action_activate (priv->action);
+
+  if (priv->g_action)
+    g_action_activate (priv->g_action, NULL);
 }
 
 static gboolean
diff --git a/gtk/gtkbutton.h b/gtk/gtkbutton.h
index 0bab7e6..3e2fee8 100644
--- a/gtk/gtkbutton.h
+++ b/gtk/gtkbutton.h
@@ -69,10 +69,11 @@ struct _GtkButtonClass
   void (* activate) (GtkButton *button);
   
   /* Padding for future expansion */
+  void (* action_state_changed) (GtkButton *button,
+                                 GVariant  *state);
   void (*_gtk_reserved1) (void);
   void (*_gtk_reserved2) (void);
   void (*_gtk_reserved3) (void);
-  void (*_gtk_reserved4) (void);
 };
 
 
@@ -120,6 +121,9 @@ void                  gtk_button_set_image_position (GtkButton      *button,
 GtkPositionType       gtk_button_get_image_position (GtkButton      *button);
 
 GdkWindow*            gtk_button_get_event_window   (GtkButton      *button);
+void                  gtk_button_set_action         (GtkButton      *button,
+                                                     GAction        *action);
+GAction *             gtk_button_get_action         (GtkButton      *button);
 
 void _gtk_button_set_depressed             (GtkButton          *button,
 					    gboolean            depressed);
@@ -132,6 +136,7 @@ void _gtk_button_paint                     (GtkButton          *button,
 					    const gchar        *main_detail,
 					    const gchar        *default_detail);
 
+
 G_END_DECLS
 
 #endif /* __GTK_BUTTON_H__ */
diff --git a/gtk/gtkbuttonprivate.h b/gtk/gtkbuttonprivate.h
index b6007cd..da5f56b 100644
--- a/gtk/gtkbuttonprivate.h
+++ b/gtk/gtkbuttonprivate.h
@@ -24,6 +24,10 @@
 struct _GtkButtonPrivate
 {
   GtkAction             *action;
+  GAction               *g_action;
+  guint                  state_id;
+  GVariant              *state;
+
   GtkPositionType        image_position;
   GtkWidget             *image;
 
@@ -50,6 +54,7 @@ struct _GtkButtonPrivate
   guint          use_action_appearance : 1;
   guint          use_stock             : 1;
   guint          use_underline         : 1;
+  guint          is_toggle             : 1;
 };
 
 #endif /* __GTK_BUTTON_PRIVATE_H__ */
diff --git a/gtk/gtkcheckbutton.c b/gtk/gtkcheckbutton.c
index f07ecf1..f9087b5 100644
--- a/gtk/gtkcheckbutton.c
+++ b/gtk/gtkcheckbutton.c
@@ -101,14 +101,17 @@ gtk_check_button_init (GtkCheckButton *check_button)
 GtkWidget*
 gtk_check_button_new (void)
 {
-  return g_object_new (GTK_TYPE_CHECK_BUTTON, NULL);
+  return g_object_new (GTK_TYPE_CHECK_BUTTON, "is-toggle", TRUE, NULL);
 }
 
 
 GtkWidget*
 gtk_check_button_new_with_label (const gchar *label)
 {
-  return g_object_new (GTK_TYPE_CHECK_BUTTON, "label", label, NULL);
+  return g_object_new (GTK_TYPE_CHECK_BUTTON,
+                       "is-toggle", TRUE,
+                       "label", label,
+                       NULL);
 }
 
 /**
@@ -125,6 +128,7 @@ GtkWidget*
 gtk_check_button_new_with_mnemonic (const gchar *label)
 {
   return g_object_new (GTK_TYPE_CHECK_BUTTON, 
+                       "is-toggle", TRUE,
                        "label", label, 
                        "use-underline", TRUE, 
                        NULL);
diff --git a/gtk/gtkradiobutton.c b/gtk/gtkradiobutton.c
index ff607ce..085f9ee 100644
--- a/gtk/gtkradiobutton.c
+++ b/gtk/gtkradiobutton.c
@@ -416,7 +416,7 @@ gtk_radio_button_new (GSList *group)
 {
   GtkRadioButton *radio_button;
 
-  radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, NULL);
+  radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, "is-toggle", TRUE, NULL);
 
   if (group)
     gtk_radio_button_set_group (radio_button, group);
@@ -440,7 +440,10 @@ gtk_radio_button_new_with_label (GSList      *group,
 {
   GtkWidget *radio_button;
 
-  radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, "label", label, NULL) ;
+  radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON,
+                               "is-toggle", TRUE,
+                               "label", label,
+                               NULL);
 
   if (group)
     gtk_radio_button_set_group (GTK_RADIO_BUTTON (radio_button), group);
@@ -469,6 +472,7 @@ gtk_radio_button_new_with_mnemonic (GSList      *group,
   GtkWidget *radio_button;
 
   radio_button = g_object_new (GTK_TYPE_RADIO_BUTTON, 
+                               "is-toggle", TRUE,
 			       "label", label, 
 			       "use-underline", TRUE, 
 			       NULL);
diff --git a/gtk/gtktogglebutton.c b/gtk/gtktogglebutton.c
index a4979b3..eda7ab3 100644
--- a/gtk/gtktogglebutton.c
+++ b/gtk/gtktogglebutton.c
@@ -68,7 +68,6 @@ static gboolean gtk_toggle_button_mnemonic_activate  (GtkWidget            *widg
                                                       gboolean              group_cycling);
 static void gtk_toggle_button_pressed       (GtkButton            *button);
 static void gtk_toggle_button_released      (GtkButton            *button);
-static void gtk_toggle_button_clicked       (GtkButton            *button);
 static void gtk_toggle_button_set_property  (GObject              *object,
 					     guint                 prop_id,
 					     const GValue         *value,
@@ -79,6 +78,9 @@ static void gtk_toggle_button_get_property  (GObject              *object,
 					     GParamSpec           *pspec);
 static void gtk_toggle_button_update_state  (GtkButton            *button);
 
+static void gtk_toggle_button_action_state_changed (GtkButton     *button,
+                                                    GVariant      *state);
+
 
 static void gtk_toggle_button_activatable_interface_init (GtkActivatableIface  *iface);
 static void gtk_toggle_button_update         	     (GtkActivatable       *activatable,
@@ -113,7 +115,7 @@ gtk_toggle_button_class_init (GtkToggleButtonClass *class)
 
   button_class->pressed = gtk_toggle_button_pressed;
   button_class->released = gtk_toggle_button_released;
-  button_class->clicked = gtk_toggle_button_clicked;
+  button_class->action_state_changed = gtk_toggle_button_action_state_changed;
   button_class->enter = gtk_toggle_button_update_state;
   button_class->leave = gtk_toggle_button_update_state;
 
@@ -165,7 +167,6 @@ gtk_toggle_button_init (GtkToggleButton *toggle_button)
                                                      GtkToggleButtonPrivate);
   priv = toggle_button->priv;
 
-  priv->active = FALSE;
   priv->draw_indicator = FALSE;
   GTK_BUTTON (toggle_button)->priv->depress_on_activate = TRUE;
 }
@@ -220,13 +221,16 @@ gtk_toggle_button_sync_action_properties (GtkActivatable *activatable,
 GtkWidget*
 gtk_toggle_button_new (void)
 {
-  return g_object_new (GTK_TYPE_TOGGLE_BUTTON, NULL);
+  return g_object_new (GTK_TYPE_TOGGLE_BUTTON, "is-toggle", TRUE, NULL);
 }
 
 GtkWidget*
 gtk_toggle_button_new_with_label (const gchar *label)
 {
-  return g_object_new (GTK_TYPE_TOGGLE_BUTTON, "label", label, NULL);
+  return g_object_new (GTK_TYPE_TOGGLE_BUTTON,
+                       "is-toggle", TRUE,
+                       "label", label,
+                       NULL);
 }
 
 /**
@@ -243,6 +247,7 @@ GtkWidget*
 gtk_toggle_button_new_with_mnemonic (const gchar *label)
 {
   return g_object_new (GTK_TYPE_TOGGLE_BUTTON, 
+                       "is-toggle", TRUE,
 		       "label", label, 
 		       "use-underline", TRUE, 
 		       NULL);
@@ -377,6 +382,7 @@ void
 _gtk_toggle_button_set_active (GtkToggleButton *toggle_button,
                                gboolean         is_active)
 {
+  g_warning ("this is broken...");
   toggle_button->priv->active = is_active;
 }
 
@@ -528,21 +534,21 @@ gtk_toggle_button_released (GtkButton *button)
 }
 
 static void
-gtk_toggle_button_clicked (GtkButton *button)
+gtk_toggle_button_action_state_changed (GtkButton *button,
+                                        GVariant  *state)
 {
   GtkToggleButton *toggle_button = GTK_TOGGLE_BUTTON (button);
   GtkToggleButtonPrivate *priv = toggle_button->priv;
 
-  priv->active = !priv->active;
+  priv->active = state &&
+                 g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN) &&
+                 g_variant_get_boolean (state);
 
   gtk_toggle_button_toggled (toggle_button);
 
   gtk_toggle_button_update_state (button);
 
   g_object_notify (G_OBJECT (toggle_button), "active");
-
-  if (GTK_BUTTON_CLASS (gtk_toggle_button_parent_class)->clicked)
-    GTK_BUTTON_CLASS (gtk_toggle_button_parent_class)->clicked (button);
 }
 
 static void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]