[glade/gbinding] Add support for setting transformation functions from "Bind to source..."



commit 03aa243b282b94c6eb62ed080ede14ba197ac360
Author: Denis Washington <denisw src gnome org>
Date:   Mon Aug 1 13:00:48 2011 +0200

    Add support for setting transformation functions from "Bind to source..."
    
    There is now a new checkbutton in the "Bind to source..." dialog to set
    a transformation for a binding. Because we don't have the transformation
    function and the source and target properties might have different types,
    there is no in-workspace preview of the binding; instead, a property with
    a transformed binding is shown as having its default value.

 gladeui/glade-command.c         |   16 ++++-
 gladeui/glade-command.h         |    3 +-
 gladeui/glade-editor-property.c |  174 ++++++++++++++++++++++++++++++++++-----
 gladeui/glade-editor-property.h |    3 +-
 gladeui/glade-popup.c           |   11 ++-
 gladeui/glade-property.c        |   47 ++++++++---
 6 files changed, 217 insertions(+), 37 deletions(-)
---
diff --git a/gladeui/glade-command.c b/gladeui/glade-command.c
index 9bb4685..b1e4c3c 100644
--- a/gladeui/glade-command.c
+++ b/gladeui/glade-command.c
@@ -799,6 +799,8 @@ typedef struct
   GladeProperty *target;
   GladeProperty *new_source;
   GladeProperty *old_source;
+  gchar *old_transform;
+  gchar *new_transform;
   GValue old_value;
   gboolean undo;
 } GladeCommandBindProperty;
@@ -827,14 +829,17 @@ glade_command_bind_property_execute (GladeCommand * cmd)
 {
   GladeCommandBindProperty *bcmd; 
   GladeProperty *target, *source;
+  gchar *transform_func;
 
   g_return_val_if_fail (GLADE_IS_COMMAND_BIND_PROPERTY (cmd), TRUE);
   
   bcmd = GLADE_COMMAND_BIND_PROPERTY (cmd);
   target = bcmd->target;
   source = bcmd->undo ? bcmd->old_source : bcmd->new_source;
+  transform_func = bcmd->undo ? bcmd->old_transform : bcmd->new_transform;
 
   glade_property_set_binding_source (target, source);
+  glade_property_set_binding_transform_func (target, transform_func);
 
   if (!source && G_IS_VALUE (&bcmd->old_value))
       glade_property_set_value (target, &bcmd->old_value);
@@ -846,6 +851,11 @@ glade_command_bind_property_execute (GladeCommand * cmd)
 static void
 glade_command_bind_property_finalize (GObject * obj)
 {
+  GladeCommandBindProperty *cmd = GLADE_COMMAND_BIND_PROPERTY (obj);
+
+  g_free (cmd->old_transform);
+  g_free (cmd->new_transform);
+
   glade_command_finalize (obj);
 }
 
@@ -879,7 +889,9 @@ glade_command_bind_property_collapse (GladeCommand * this_cmd,
 }
 
 void
-glade_command_bind_property (GladeProperty * target, GladeProperty * source)
+glade_command_bind_property (GladeProperty * target,
+                             GladeProperty * source,
+                             const gchar   * transform_func)
 {
   GladeCommandBindProperty *me;
   GladeCommand *cmd;
@@ -892,6 +904,8 @@ glade_command_bind_property (GladeProperty * target, GladeProperty * source)
   me->target = target;
   me->old_source = glade_property_get_binding_source (target);
   me->new_source = source;
+  me->old_transform = g_strdup (glade_property_get_binding_transform_func (target));
+  me->new_transform = g_strdup (transform_func);
 
   if (!me->old_source)
     glade_property_get_value (target, &me->old_value);
diff --git a/gladeui/glade-command.h b/gladeui/glade-command.h
index 4e1c26d..897f8b8 100644
--- a/gladeui/glade-command.h
+++ b/gladeui/glade-command.h
@@ -96,7 +96,8 @@ void           glade_command_set_properties_list (GladeProject  *project,
 						  GList         *props); /* list of GCSetPropData */
 
 void           glade_command_bind_property        (GladeProperty *target,     
-					           GladeProperty *source);
+					           GladeProperty *source,
+					           const gchar   *transform_func);
 
 /************************** name ******************************/
 
diff --git a/gladeui/glade-editor-property.c b/gladeui/glade-editor-property.c
index 6fc340e..7e92fe5 100644
--- a/gladeui/glade-editor-property.c
+++ b/gladeui/glade-editor-property.c
@@ -175,8 +175,7 @@ GladePropertyClass *
 glade_editor_property_get_pclass (GladeEditorProperty *eprop)
 {
   g_return_val_if_fail (GLADE_IS_EDITOR_PROPERTY (eprop), NULL);
-
-  return eprop->priv->klass;
+  return eprop->priv->klass;  
 }
 
 GladeProperty *
@@ -201,6 +200,7 @@ typedef struct {
   GtkWidget *object_view;
   GtkWidget *property_view;
   gboolean transform_func_enabled;
+  gboolean transform_func_valid;
 } GladeBindDialog;
 
 enum {
@@ -269,11 +269,37 @@ glade_bind_dialog_update_property_view (GladeBindDialog  *dialog,
                           PROPERTY_COLUMN_PROP_NAME,
                           glade_property_class_get_name (pclass),
                           PROPERTY_COLUMN_PROP_SELECTABLE,
+                          dialog->transform_func_enabled ||
                           g_type_is_a (type, target_type),
                           -1);      
     }
 }
 
+static void
+glade_bind_dialog_update_buttons (GladeBindDialog  *dialog,
+                                  GtkTreeSelection *prop_selection)
+{  
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  if ((!dialog->transform_func_enabled || dialog->transform_func_valid) &&
+      gtk_tree_selection_get_selected (prop_selection, &model, &iter))
+    {
+      gboolean selectable;
+
+      gtk_tree_model_get (model, &iter,
+                          PROPERTY_COLUMN_PROP_SELECTABLE, &selectable,
+                          -1);
+      
+      gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog->widget),
+                                         GTK_RESPONSE_OK, selectable);
+    }
+  else
+    gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog->widget),
+                                       GTK_RESPONSE_OK, FALSE);
+  
+}
+
 static int
 property_sort_func (GtkTreeModel *model,
                     GtkTreeIter  *a,
@@ -375,30 +401,76 @@ glade_bind_dialog_setup_property_view (GladeBindDialog *dialog)
       while (gtk_tree_model_iter_next (model, &iter));
     }
 
+  glade_bind_dialog_update_buttons (dialog, selection);
   return view_widget;
 }
 
 static void
-glade_bind_dialog_update_buttons (GladeBindDialog  *dialog,
-                                  GtkTreeSelection *prop_selection)
+glade_bind_dialog_update_property_selectability (GladeBindDialog *dialog,
+                                                 GtkWidget       *checkbutton)
 {
-  GtkTreeModel *model;
+  gboolean active;
+  GtkTreeView *prop_view;
+  GtkTreeModel *prop_model;
+  GtkTreeSelection *prop_selection;
   GtkTreeIter iter;
-  
-  if (gtk_tree_selection_get_selected (prop_selection, &model, &iter))
+  GladePropertyClass *target_pclass ;
+  GType target_type;
+
+  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (checkbutton));
+  dialog->transform_func_enabled = active;
+
+  prop_view = GTK_TREE_VIEW (dialog->property_view);
+  prop_model = gtk_tree_view_get_model (prop_view);
+
+  target_pclass = glade_property_get_class (dialog->target);
+  target_type = G_PARAM_SPEC_TYPE (glade_property_class_get_pspec (target_pclass));
+
+  /* Temporarily disable sorting, otherwise the tree model iteration
+   * code below might get confused */
+  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (prop_model),
+                                        GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
+                                        GTK_SORT_ASCENDING);
+
+  gtk_tree_model_get_iter_first (prop_model, &iter);
+  do
     {
-      gboolean selectable;
+      GladeProperty *prop;
+      GladePropertyClass *pclass;
+      GType type;
 
-      gtk_tree_model_get (model, &iter,
-                          PROPERTY_COLUMN_PROP_SELECTABLE, &selectable,
-                          -1);
-      
-      gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog->widget),
-                                         GTK_RESPONSE_OK, selectable);
+      gtk_tree_model_get (prop_model, &iter,
+                          PROPERTY_COLUMN_PROP, &prop, -1);
+
+      pclass = glade_property_get_class (prop);
+      type = G_PARAM_SPEC_TYPE (glade_property_class_get_pspec (pclass));
+
+      gtk_list_store_set (GTK_LIST_STORE (prop_model), &iter,
+                          PROPERTY_COLUMN_PROP_SELECTABLE,
+                          dialog->transform_func_enabled ||
+                          g_type_is_a (type, target_type), -1);
     }
-  else
-    gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog->widget),
-                                       GTK_RESPONSE_OK, FALSE);
+  while (gtk_tree_model_iter_next (prop_model, &iter));      
+
+  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (prop_model),
+                                        GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
+                                        GTK_SORT_ASCENDING);
+
+  prop_selection = gtk_tree_view_get_selection (prop_view);
+  glade_bind_dialog_update_buttons (dialog, prop_selection);
+}
+
+static void
+glade_bind_dialog_transform_func_changed (GladeBindDialog *dialog,
+                                          GParamSpec      *pspec,
+                                          GtkEntry        *entry)
+{
+  GtkTreeSelection *selection;
+
+  dialog->transform_func_valid = (strlen (gtk_entry_get_text (entry)) > 0);
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->property_view));
+  glade_bind_dialog_update_buttons (dialog, selection);
 }
 
 /**
@@ -413,7 +485,8 @@ gboolean
 glade_editor_property_show_bind_dialog (GladeProject * project,
                                         GtkWidget * parent,
                                         GladeProperty *target,
-                                        GladeProperty ** source)
+                                        GladeProperty **source,
+                                        gchar **transform_func)
 {
   GladeBindDialog *dialog;
   GtkWidget *content_area, *action_area;
@@ -423,6 +496,10 @@ glade_editor_property_show_bind_dialog (GladeProject * project,
   GtkWidget *obj_view, *prop_view;
   GladeProperty *current_source;
   GList *selected = NULL;
+  GtkWidget *frame, *frame_label;
+  GtkWidget *trans_vbox, *trans_hbox;
+  GtkWidget *trans_check, *trans_label, *trans_entry;
+  const gchar *current_transform;
   gint res;
 
   g_return_val_if_fail (GLADE_IS_PROJECT (project), FALSE);
@@ -503,6 +580,32 @@ glade_editor_property_show_bind_dialog (GladeProject * project,
   prop_view = dialog->property_view;
   gtk_container_add (GTK_CONTAINER (prop_sw), prop_view);
 
+  frame = gtk_frame_new (_("<b>Transformation Function</b>"));
+  frame_label = GTK_WIDGET (gtk_frame_get_label_widget (GTK_FRAME (frame)));
+  gtk_label_set_use_markup (GTK_LABEL (frame_label), TRUE);
+  gtk_box_pack_start (GTK_BOX (content_area), frame, FALSE, FALSE, 0);
+
+  trans_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+  gtk_widget_set_margin_top (trans_vbox, 6);
+  gtk_widget_set_margin_left (trans_vbox, 12);
+  gtk_container_add (GTK_CONTAINER (frame), trans_vbox);
+
+  trans_check = gtk_check_button_new_with_mnemonic (_("_Connect a function for "
+                                                      "source-to-target value "
+                                                      "transformation"));
+  gtk_box_pack_start (GTK_BOX (trans_vbox), trans_check, FALSE, FALSE, 0);
+
+  trans_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+  gtk_widget_set_margin_left (trans_hbox, 12);
+  gtk_box_pack_start (GTK_BOX (trans_vbox), trans_hbox, FALSE, FALSE, 0);
+
+  trans_label = gtk_label_new_with_mnemonic (_("_Transformation function:"));
+  gtk_box_pack_start (GTK_BOX (trans_hbox), trans_label, FALSE, FALSE, 0);
+
+  trans_entry = gtk_entry_new ();
+  gtk_label_set_mnemonic_widget (GTK_LABEL (trans_label), trans_entry);
+  gtk_box_pack_start (GTK_BOX (trans_hbox), trans_entry, TRUE, TRUE, 0);
+
   g_signal_connect_swapped (gtk_tree_view_get_selection (GTK_TREE_VIEW (obj_view)),
                             "changed",
                             G_CALLBACK (glade_bind_dialog_update_property_view),
@@ -511,6 +614,23 @@ glade_editor_property_show_bind_dialog (GladeProject * project,
                             "changed",
                             G_CALLBACK (glade_bind_dialog_update_buttons),
                             dialog);
+  
+  g_object_bind_property (trans_check, "active",
+                          trans_hbox, "sensitive",
+                          G_BINDING_SYNC_CREATE);
+  g_signal_connect_swapped (trans_check, "toggled",
+                            G_CALLBACK (glade_bind_dialog_update_property_selectability),
+                            dialog);
+  g_signal_connect_swapped (trans_entry, "notify::text",
+                            G_CALLBACK (glade_bind_dialog_transform_func_changed),
+                            dialog);
+
+  current_transform = glade_property_get_binding_transform_func (target);
+  if (current_transform)
+    {
+      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (trans_check), TRUE);
+      gtk_entry_set_text (GTK_ENTRY (trans_entry), current_transform);
+    }
 
   gtk_widget_show_all (content_area);
   res = gtk_dialog_run (GTK_DIALOG (dialog->widget));
@@ -519,11 +639,25 @@ glade_editor_property_show_bind_dialog (GladeProject * project,
       GtkTreeSelection *selection;
       GtkTreeModel *model;
       GtkTreeIter iter;
-      
+      gchar *trans_text;
+
       selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (prop_view));
       gtk_tree_selection_get_selected (selection, &model, &iter);
-
       gtk_tree_model_get (model, &iter, PROPERTY_COLUMN_PROP, source, -1);
+
+      if (dialog->transform_func_enabled)
+        {
+          trans_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (trans_entry)));
+          if (strlen (g_strstrip (trans_text)) == 0)
+            {
+              g_free (trans_text);
+              *transform_func = NULL;
+            }
+          else
+            *transform_func = g_strdup (trans_text);
+        }
+      else
+        *transform_func = NULL;
     }
 
   gtk_widget_destroy (dialog->widget);
diff --git a/gladeui/glade-editor-property.h b/gladeui/glade-editor-property.h
index b63e840..af052ee 100644
--- a/gladeui/glade-editor-property.h
+++ b/gladeui/glade-editor-property.h
@@ -135,7 +135,8 @@ gboolean             glade_editor_property_show_object_dialog (GladeProject
 gboolean             glade_editor_property_show_bind_dialog (GladeProject         *project,
                                                              GtkWidget            *parent,
                                                              GladeProperty        *target,
-                                                             GladeProperty       **source);
+                                                             GladeProperty       **source,
+                                                             gchar               **transform_func);
 
 /* Generic eprops */
 #define GLADE_TYPE_EPROP_NUMERIC         (glade_eprop_numeric_get_type())
diff --git a/gladeui/glade-popup.c b/gladeui/glade-popup.c
index cddcdcb..82b3564 100644
--- a/gladeui/glade-popup.c
+++ b/gladeui/glade-popup.c
@@ -623,17 +623,22 @@ glade_popup_bind_property_cb (GtkMenuItem * item, GladeProperty * property)
   GladeWidget *widget = glade_property_get_widget (property);
   GladeProject *project = glade_widget_get_project (widget);
   GladeProperty *source;
+  gchar *transform_func;
   GtkWidget *parent =
     gtk_widget_get_toplevel (GTK_WIDGET (glade_widget_get_object (widget)));
   
-  if (glade_editor_property_show_bind_dialog (project, parent, property, &source))
-    glade_command_bind_property (property, source);
+  if (glade_editor_property_show_bind_dialog (project, parent, property,
+                                              &source, &transform_func))
+    {
+      glade_command_bind_property (property, source, transform_func);
+      g_free (transform_func);
+    }
 }
 
 static void
 glade_popup_unbind_property_cb (GtkMenuItem * item, GladeProperty * property)
 {
-  glade_command_bind_property (property, NULL);
+  glade_command_bind_property (property, NULL, NULL);
 }
 
 static void
diff --git a/gladeui/glade-property.c b/gladeui/glade-property.c
index 84e713f..860f62f 100644
--- a/gladeui/glade-property.c
+++ b/gladeui/glade-property.c
@@ -1842,11 +1842,13 @@ void
 glade_property_set_binding_source (GladeProperty *property,
                                    GladeProperty *binding_source)
 {
+  GladeProperty *old_source;
   GValue source_val = {0};
 
   g_return_if_fail (GLADE_IS_PROPERTY (property));
   g_return_if_fail (!binding_source || GLADE_IS_PROPERTY (binding_source));
 
+  old_source = glade_property_get_binding_source (property);
   glade_property_remove_binding_source (property);
   property->priv->binding_source = binding_source;
 
@@ -1860,11 +1862,7 @@ glade_property_set_binding_source (GladeProperty *property,
                          (GWeakNotify) glade_property_binding_source_weak_notify_cb,
                          property);
 
-      property->priv->binding_handler =
-        g_signal_connect (binding_source, "value-changed",
-                          G_CALLBACK (glade_property_binding_source_value_changed_cb),
-                          property);
-
+      /* To be called when the binding source widget is deleted */
       closure =
         g_cclosure_new (G_CALLBACK (glade_property_binding_source_widget_cb),
                         G_OBJECT (property), NULL);
@@ -1883,9 +1881,23 @@ glade_property_set_binding_source (GladeProperty *property,
 
       property->priv->binding_source_valid = TRUE;
 
-      /* Synchronize the source and target property values once */
-      glade_property_get_value (binding_source, &source_val);
-      glade_property_set_value (property, &source_val);
+      /* Synchronize the source and target property values if there
+       * is no transformation function; if there is, the best thing we
+       * can do is to reset the target property to the default value
+       * (the source and target property types might not be compatible)
+       */
+      if (glade_property_get_binding_transform_func (property))
+        glade_property_reset (property);
+      else
+        {
+          property->priv->binding_handler =
+            g_signal_connect (binding_source, "value-changed",
+                              G_CALLBACK (glade_property_binding_source_value_changed_cb),
+                              property);
+
+          glade_property_get_value (binding_source, &source_val);
+          glade_property_set_value (property, &source_val);
+        }
     }
   else
     {
@@ -1894,7 +1906,9 @@ glade_property_set_binding_source (GladeProperty *property,
       property->priv->binding_widget_add_handler = 0;
     }
 
-  g_object_notify_by_pspec (G_OBJECT (property), properties[PROP_BINDING_SOURCE]);
+  if (binding_source != old_source)
+    g_object_notify_by_pspec (G_OBJECT (property),
+                              properties[PROP_BINDING_SOURCE]);
 }
 
 const gchar *
@@ -1902,7 +1916,10 @@ glade_property_get_binding_transform_func (GladeProperty *property)
 {
   g_return_val_if_fail (GLADE_IS_PROPERTY (property), NULL);
 
-  return property->priv->binding_transform_func;
+  if (property->priv->binding_source_valid)
+    return property->priv->binding_transform_func;
+  else
+    return NULL;
 }
 
 void
@@ -1912,8 +1929,16 @@ glade_property_set_binding_transform_func (GladeProperty *property,
   g_return_if_fail (GLADE_IS_PROPERTY (property));
 
   g_free (property->priv->binding_transform_func);
-  property->priv->binding_transform_func = g_strdup (transform_func);
+  if (transform_func)
+    property->priv->binding_transform_func = g_strdup (transform_func);
+  else
+    property->priv->binding_transform_func = NULL;
 
+  /* Call glade_property_set_binding_source() to adjust to the new
+   * transformation function setting */
+  glade_property_set_binding_source (property,
+                                     glade_property_get_binding_source (property));
+  
   g_object_notify_by_pspec (G_OBJECT (property),
                             properties[PROP_BINDING_TRANSFORM_FUNC]);  
 }



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