gEDA-dev: [PATCH 2/2] Make the multi-attribute editor non-modal

Peter Clifton pcjc2 at cam.ac.uk
Tue Jul 17 09:11:01 EDT 2007


---
 gschem/include/prototype.h     |    2 +-
 gschem/include/x_multiattrib.h |    6 +-
 gschem/src/o_misc.c            |    2 +-
 gschem/src/x_multiattrib.c     |  229 +++++++++++++++++++++++++++++++--------
 gschem/src/x_pagesel.c         |    8 ++-
 libgeda/include/struct.h       |    2 +
 libgeda/src/s_toplevel.c       |    2 +
 7 files changed, 200 insertions(+), 51 deletions(-)

diff --git a/gschem/include/prototype.h b/gschem/include/prototype.h
index e2eb4b9..f537d7b 100644
--- a/gschem/include/prototype.h
+++ b/gschem/include/prototype.h
@@ -842,7 +842,7 @@ gboolean g_file_set_contents(const gchar *filename, const gchar *contents,
       gssize length, GError **error);
 #endif
 /* x_multiattrib.c */
-void x_multiattrib_open (TOPLEVEL *toplevel, OBJECT *object);
+void x_multiattrib_open (TOPLEVEL *toplevel);
 /* x_multimulti.c */
 /* x_pagesel.c */
 void x_pagesel_open (TOPLEVEL *toplevel);
diff --git a/gschem/include/x_multiattrib.h b/gschem/include/x_multiattrib.h
index e53f06e..59a21b8 100644
--- a/gschem/include/x_multiattrib.h
+++ b/gschem/include/x_multiattrib.h
@@ -44,6 +44,7 @@ struct _MultiattribClass {
 struct _Multiattrib {
   GschemDialog parent_instance;
 
+  SELECTION *selection;
   OBJECT *object;
 
   GtkTreeView    *treeview;
@@ -52,7 +53,10 @@ struct _Multiattrib {
   GtkTextView    *textview_value;
   GtkCheckButton *button_visible;
   GtkOptionMenu  *optionmenu_shownv;
-  
+  GtkWidget      *frame_attributes;
+  GtkWidget      *frame_add;
+
+  gulong selection_changed_id;
 };
 
 
diff --git a/gschem/src/o_misc.c b/gschem/src/o_misc.c
index 246e65b..012cf4f 100644
--- a/gschem/src/o_misc.c
+++ b/gschem/src/o_misc.c
@@ -75,7 +75,7 @@ void o_edit(TOPLEVEL *w_current, GList *list)
     case(OBJ_NET):
     case(OBJ_PIN):
     case(OBJ_BUS):
-    x_multiattrib_open (w_current, o_current);
+    x_multiattrib_open (w_current);
     break;
 
     case(OBJ_PICTURE):
diff --git a/gschem/src/x_multiattrib.c b/gschem/src/x_multiattrib.c
index f0fe51f..970ce42 100644
--- a/gschem/src/x_multiattrib.c
+++ b/gschem/src/x_multiattrib.c
@@ -41,45 +41,67 @@
 #include "../include/gschem_dialog.h"
 #include "../include/x_multiattrib.h"
 
+
+
+/*! \brief Process the response returned by the multi-attribte dialog.
+ *  \par Function Description
+ *  This function handles the response <B>arg1</B> of the multi-attribute
+ *  editor dialog <B>dialog</B>.
+ *
+ *  Parameter <B>user_data</B> is a pointer on the relevant toplevel
+ *  structure.
+ *
+ *  \param [in] dialog    The multi-attribute editor dialog.
+ *  \param [in] arg1      The response ID.
+ *  \param [in] user_data A pointer on the toplevel environment.
+ */
+static void
+x_multiattrib_callback_response (GtkDialog *dialog,
+                                 gint arg1,
+                                 gpointer user_data)
+{
+  TOPLEVEL *toplevel = (TOPLEVEL*)user_data;
+
+  switch (arg1) {
+      case GTK_RESPONSE_CLOSE:
+      case GTK_RESPONSE_DELETE_EVENT:
+        g_assert (GTK_WIDGET (dialog) == toplevel->multiattribwindow);
+        gtk_widget_destroy (GTK_WIDGET (dialog));
+        toplevel->multiattribwindow = NULL;
+        break;
+  }
+}
+
 /*! \brief Open multiple attribute editor dialog.
  *  \par Function Description
  *  Opens the multiple attribute editor dialog for <B>object</B> in the
  *  context of <B>toplevel</B>.
  *
- *  The dialog is modal and this function does not return until the user
- *  closes the dialog.
- *
  *  \param [in] toplevel  The TOPLEVEL object.
- *  \param [in] object    OBJECT to edit attributes on.
  */
-void x_multiattrib_open (TOPLEVEL *toplevel, OBJECT *object)
+void x_multiattrib_open (TOPLEVEL *toplevel)
 {
-  GtkWidget *dialog;
+  if ( toplevel->multiattribwindow == NULL ) {
+    toplevel->multiattribwindow = GTK_WIDGET (g_object_new (TYPE_MULTIATTRIB,
+                                              "selection", toplevel->page_current->selection_list,
+                                              /* GschemDialog */
+                                              "settings-name", "multiattrib",
+                                              "toplevel", toplevel,
+                                              NULL));
 
-  dialog = GTK_WIDGET (g_object_new (TYPE_MULTIATTRIB,
-                                     "object", object,
-                                     /* GschemDialog */
-                                     "settings-name", "multiattrib",
-                                     "toplevel", toplevel,
-                                     NULL));
+      g_signal_connect (toplevel->multiattribwindow,
+                        "response",
+                        G_CALLBACK (x_multiattrib_callback_response),
+                        toplevel);
 
-  gtk_window_set_transient_for(GTK_WINDOW(dialog),
-			       GTK_WINDOW(toplevel->main_window));
+    gtk_window_set_transient_for (GTK_WINDOW(toplevel->multiattribwindow),
+                                  GTK_WINDOW(toplevel->main_window));
 
-  multiattrib_update (MULTIATTRIB(dialog));
-  gtk_widget_show (dialog);
-  switch (gtk_dialog_run ((GtkDialog*)dialog)) {
-      case GTK_RESPONSE_CLOSE:
-      case GTK_RESPONSE_DELETE_EVENT:
-        /* reset state and update message in toolbar */
-        i_set_state (toplevel, SELECT);
-        i_update_toolbar (toplevel);
-        break;
-      default:
-        g_assert_not_reached ();
+    multiattrib_update (MULTIATTRIB(toplevel->multiattribwindow));
+    gtk_widget_show (toplevel->multiattribwindow);
+  } else {
+    gtk_window_present (GTK_WINDOW(toplevel->multiattribwindow));
   }
-  gtk_widget_destroy (dialog);
-  
 }
 
 /*! \section celltextview-widget Cell TextView Widget Code.
@@ -380,7 +402,7 @@ static void cellrenderermultilinetext_init(CellRendererMultiLineText *self)
 
 
 enum {
-  PROP_OBJECT = 1
+  PROP_SELECTION = 1
 };
 
 enum {
@@ -388,6 +410,7 @@ enum {
   NUM_COLUMNS
 };
 
+static GObjectClass *multiattrib_parent_class = NULL;
 
 static void multiattrib_class_init (MultiattribClass *class);
 static void multiattrib_init       (Multiattrib *multiattrib);
@@ -1237,6 +1260,42 @@ GType multiattrib_get_type()
   return multiattrib_type;
 }
 
+
+/*! \todo Finish function documentation
+ *  \brief
+ *  \par Function Description
+ *
+ */
+static void selection_died_cb( gpointer data, GObject *where_the_object_was )
+{
+  Multiattrib *multiattrib = (Multiattrib *)data;
+
+  multiattrib->selection = NULL;
+  multiattrib_update( multiattrib );
+}
+
+
+/*! \todo Finish function documentation
+ *  \brief
+ *  \par Function Description
+ *
+ */
+static void multiattrib_finalize( GObject *object )
+{
+  Multiattrib *multiattrib = MULTIATTRIB(object);
+
+  if ( multiattrib->selection ) {
+    g_signal_handler_disconnect( multiattrib->selection,
+                                 multiattrib->selection_changed_id );
+    g_object_weak_unref( G_OBJECT( multiattrib->selection ),
+                         selection_died_cb,
+                         multiattrib );
+  }
+
+  G_OBJECT_CLASS(multiattrib_parent_class)->finalize( object );
+}
+
+
 /*! \todo Finish function documentation
  *  \brief
  *  \par Function Description
@@ -1246,16 +1305,18 @@ static void multiattrib_class_init(MultiattribClass *klass)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
+  multiattrib_parent_class = g_type_class_peek_parent (klass);
+
   gobject_class->set_property = multiattrib_set_property;
   gobject_class->get_property = multiattrib_get_property;
+  gobject_class->finalize     = multiattrib_finalize;
 
   g_object_class_install_property (
-    gobject_class, PROP_OBJECT,
-    g_param_spec_pointer ("object",
+    gobject_class, PROP_SELECTION,
+    g_param_spec_pointer ("selection",
                           "",
                           "",
-                          G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
-	
+                          G_PARAM_READWRITE));
 }
 
 /*! \todo Finish function documentation
@@ -1281,7 +1342,6 @@ static void multiattrib_init(Multiattrib *multiattrib)
                 "title",           _("Edit Attributes"),
                 "default-width",   320,
                 "default-height",  350,
-                "modal",           TRUE,
                 "window-position", GTK_WIN_POS_MOUSE,
                 "allow-grow",      TRUE,
                 "allow-shrink",    FALSE,
@@ -1296,6 +1356,7 @@ static void multiattrib_init(Multiattrib *multiattrib)
 				    /* GtkFrame */
 				    "label", _("Attributes"),
 				    NULL));
+  multiattrib->frame_add = frame;
   /*   - create the model for the treeview */
   store = (GtkTreeModel*)gtk_list_store_new (NUM_COLUMNS,
 					     G_TYPE_POINTER); /* attribute */
@@ -1461,6 +1522,7 @@ static void multiattrib_init(Multiattrib *multiattrib)
   frame = GTK_WIDGET (g_object_new (GTK_TYPE_FRAME,
 				    "label", _("Add Attribute"),
 				    NULL));
+  multiattrib->frame_attributes = frame;
   table = GTK_WIDGET (g_object_new (GTK_TYPE_TABLE,
 				    /* GtkTable */
 				    "n-rows",      4,
@@ -1560,6 +1622,53 @@ static void multiattrib_init(Multiattrib *multiattrib)
   
 }
 
+
+/*! \todo Finish function documentation
+ *  \brief
+ *  \par Function Description
+ *
+ */
+static void selection_changed_cb( SELECTION *selection, Multiattrib *multiattrib )
+{
+  int object_count = 0;
+  GList *selection_glist;
+  GList *iter;
+  OBJECT *object;
+
+  selection_glist = geda_list_get_glist( selection );
+
+  for ( iter = selection_glist;
+        iter != NULL;
+        iter = g_list_next( iter ) ) {
+    object = (OBJECT *)iter->data;
+    g_assert( object != NULL );
+
+    if (object->type == OBJ_COMPLEX ||
+        object->type == OBJ_PLACEHOLDER ||
+        object->type == OBJ_NET ||
+        object->type == OBJ_BUS) {
+      object_count++;
+    }
+  }
+
+  if ( object_count == 0 ) {
+    /* TODO: If the user selects a single attribute which is
+     *       not floating, should we find its parent object and
+     *       display the multi-attribute editor for that?
+     *       Bonus marks for making it jump to the correct attrib.
+     */
+    multiattrib->object = NULL;
+  } else if ( object_count == 1 ) {
+    multiattrib->object = (OBJECT *)selection_glist->data;
+  } else {
+    /* TODO: Something clever with multiple objects selected */
+    multiattrib->object = NULL;
+  }
+
+  multiattrib_update( multiattrib );
+}
+
+
 /*! \todo Finish function documentation
  *  \brief
  *  \par Function Description
@@ -1573,9 +1682,30 @@ static void multiattrib_set_property (GObject *object,
   Multiattrib *multiattrib = MULTIATTRIB (object);
 
   switch(property_id) {
-      case PROP_OBJECT:
-        multiattrib->object = (OBJECT*)g_value_get_pointer (value);
-        multiattrib_update (multiattrib);
+      case PROP_SELECTION:
+        if ( multiattrib->selection ) {
+          g_signal_handler_disconnect( multiattrib->selection,
+                                       multiattrib->selection_changed_id );
+          g_object_weak_unref( G_OBJECT( multiattrib->selection ),
+                               selection_died_cb,
+                               multiattrib );
+        }
+        multiattrib->selection = (SELECTION*)g_value_get_pointer (value);
+        if ( multiattrib->selection ) {
+          g_object_weak_ref( G_OBJECT( multiattrib->selection ),
+                             selection_died_cb,
+                             multiattrib );
+          multiattrib->selection_changed_id =
+            g_signal_connect( G_OBJECT( multiattrib->selection ),
+                              "changed",
+                              G_CALLBACK( selection_changed_cb ),
+                              multiattrib );
+          /* TODO: We might want to add a weak reference so we can ensure we are
+           *       safe when a page is closed and its selection object goes away.
+           */
+        }
+        /* Synthesise a selection changed update to refresh the view */
+        selection_changed_cb( multiattrib->selection, multiattrib );
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -1596,8 +1726,8 @@ static void multiattrib_get_property (GObject *object,
   Multiattrib *multiattrib = MULTIATTRIB (object);
 
   switch(property_id) {
-      case PROP_OBJECT:
-        g_value_set_pointer (value, (gpointer)multiattrib->object);
+      case PROP_SELECTION:
+        g_value_set_pointer (value, (gpointer)multiattrib->selection);
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -1616,18 +1746,23 @@ void multiattrib_update (Multiattrib *multiattrib)
   GtkTreeIter iter;
   OBJECT **object_attribs, *o_current;
   gint i;
-  
-  if (GSCHEM_DIALOG (multiattrib)->toplevel == NULL ||
-      multiattrib->object   == NULL) {
-    /* we can not do anything until both toplevel and object are set */
-    return;
-  }
+  gboolean sensitive;
 
-  liststore = (GtkListStore*)gtk_tree_view_get_model (multiattrib->treeview);
+  g_assert( GSCHEM_DIALOG (multiattrib)->toplevel != NULL );
+
+  /* Update sensitivities */
+  sensitive = (multiattrib->object != NULL);
+  gtk_widget_set_sensitive( GTK_WIDGET( multiattrib->frame_attributes ), sensitive );
+  gtk_widget_set_sensitive( GTK_WIDGET( multiattrib->frame_add ), sensitive );
 
   /* clear the list of attributes */
+  liststore = (GtkListStore*)gtk_tree_view_get_model (multiattrib->treeview);
   gtk_list_store_clear (liststore);
- 
+
+  /* If we aren't sensitive, there is nothing more to do */
+  if ( multiattrib->object == NULL )
+    return;
+
   /* get list of attributes */
   object_attribs = o_attrib_return_attribs (
     GSCHEM_DIALOG (multiattrib)->toplevel->page_current->object_head,
@@ -1645,5 +1780,5 @@ void multiattrib_update (Multiattrib *multiattrib)
   }
   /* delete the list of attribute objects */
   o_attrib_free_returned (object_attribs);
-  
 }
+
diff --git a/gschem/src/x_pagesel.c b/gschem/src/x_pagesel.c
index f13515b..5ccfc33 100644
--- a/gschem/src/x_pagesel.c
+++ b/gschem/src/x_pagesel.c
@@ -103,7 +103,13 @@ void x_pagesel_update (TOPLEVEL *toplevel)
     g_assert (IS_PAGESEL (toplevel->pswindow));
     pagesel_update (PAGESEL (toplevel->pswindow));
   }
-  
+#warning THIS BREAKES ABSTRACTION, BUT IS A USEFUL HACK FOR NOW
+  if (toplevel->multiattribwindow) {
+    g_object_set (G_OBJECT (toplevel->multiattribwindow),
+                  "selection",
+                  toplevel->page_current->selection_list,
+                  NULL);
+  }
 }
 
 /*! \brief Callback for page manager response.
diff --git a/libgeda/include/struct.h b/libgeda/include/struct.h
index 6f0befa..f9b52ec 100644
--- a/libgeda/include/struct.h
+++ b/libgeda/include/struct.h
@@ -563,6 +563,8 @@ struct st_toplevel {
   GtkWidget *clib_list;
   GtkWidget *basename_list;
 
+  GtkWidget *multiattribwindow;        /* multi-attribute editor */
+
   GtkWidget *iwindow;			/* image write dialog box */
   GtkWidget *ifilename_entry; 
 
diff --git a/libgeda/src/s_toplevel.c b/libgeda/src/s_toplevel.c
index 8acadd7..c94dc57 100644
--- a/libgeda/src/s_toplevel.c
+++ b/libgeda/src/s_toplevel.c
@@ -212,6 +212,8 @@ TOPLEVEL *s_toplevel_new (void)
   toplevel->clib_list     = NULL;
   toplevel->basename_list = NULL;
 
+  toplevel->multiattribwindow = NULL;
+
 /*   toplevel->fileselect */
 
   toplevel->iwindow = NULL;
-- 
1.5.2.3




More information about the geda-dev mailing list