Hi Dia folks.

I sent this patch for "find & replace" to Hans a few days ago.  I know he's a 
very busy guy, but I am a little worried that I may have been spam-filtered!  :)

This patch adds a find&replace option for *all* string/text properties (not 
just names) to Dia.

So, for example, you can find & replace attribute names or types inside UML 
class objects.

Details, some questions, and the patch diff are all below.  Any suggestions, 
criticisms, etc would be most welcome!

Thanks,

Johann

--- On Tue, 10/7/08, Johann Tienhaara <[EMAIL PROTECTED]> wrote:
> Hi Hans.
> 
> I downloaded Dia trunk to add "find &
> replace" to Dia, only to discover, to my delight, that
> you had already added the feature quite some time ago! 
> That'll teach me to stop using my ancient distro-bundled
> version...  I'm going to live on the bleeding edge from
> now on.  :)
> 
> I made a few changes to find-and-replace.c so that --
> optionally -- any and all text properties can be found &
> replaced (rather than just the "name" and
> "text" properties of an object).
> 
> This gives me the rest of what I was looking for -- the
> ability to replace text in UML attribute types and operation
> parameter names and what-not.
> 
> I would definitely appreciate your feedback.  Especially
> since some of the data structures had me scratching my head
> a bit (like the list-of-property-lists "records"
> inside sarray and darray properties -- why a list-of-lists
> of Properties?  Why not just a list of Properties?).  I
> suspect I've made a few hacks that should be re-done
> with cleaner/safer/better solutions.
> 
> I also am not yet sure whether there's a gauntlet of
> automated and/or manual tests that you run these sorts of
> changes through...?
> 
> I have not yet touched the i18n/l10n stuff either.  There
> is currently a rather cumbersome message for the "all
> properties" checkbox.  I'd rather have a
> terse-yet-still-meaningful message before I figure out how
> to hack the message catalogs.
> 
> 
> At the advice of the woefully out of date FAQ :)
> (http://www.gnome.org/projects/dia/faq.html#MakingPatches) I
> decided to send the patch in its current state directly to
> you, since the text is about 10K (and it's pretty hard
> to read in an email, to boot).
> 
> Let me know if you have any questions, or if you want me to
> redirect my email somewhere else, etc.  Otherwise I would
> greatly appreciate any feedback you can provide, whenever
> you have a chance.
> 
> Thanks very much Hans & all the best,
> 
> Johann Tienhaara
> Halifax, N.S., Canada


Index: app/find-and-replace.c
===================================================================
--- app/find-and-replace.c      (revision 4119)
+++ app/find-and-replace.c      (working copy)
@@ -4,6 +4,7 @@
  * find-and-replace.c - common functionality applied to diagram
  *
  * Copyright (C) 2008 Hans Breuer
+ * Copyright (C) 2008 Johann Tienhaara (patched)
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -45,7 +46,10 @@
 
 enum {
   MATCH_CASE = (1<<0),
-  MATCH_WORD = (1<<1)
+  MATCH_WORD = (1<<1),
+
+  /* Don't just match the name/text - match UML attributes etc too? */
+  MATCH_ALL_PROPERTIES = (1<<2)
 };
 
 typedef struct _SearchData {
@@ -58,42 +62,41 @@
   gboolean seen_last;
 } SearchData;
 
-/*! Match and possibly modify the given objects property */
-Property *
-_match_string_prop (DiaObject *obj, const SearchData *sd, const gchar 
*replacement)
+
+/*! Match and possibly modify the given object's given property.
+ * Returns FALSE if not matched or if the input property is NULL. */
+gboolean
+_match_text_prop (DiaObject *obj, const SearchData *sd, const gchar 
*replacement, Property *prop, gchar **value_to_match )
 {
-  Property *prop;
-  gchar   **name;
-  gboolean ret = FALSE;
+  gboolean is_match = FALSE;
   gchar    *repl = NULL;
 
-  if ((prop = object_prop_by_name(obj, "name")) != NULL)
-    name = &((StringProperty *)prop)->string_data;
-  else if ((prop = object_prop_by_name(obj, "text")) != NULL)
-    name = &((TextProperty *)prop)->text_data;
-
-  if (!prop)
-    return NULL;
+  if (!prop) {
+    return FALSE;
+  }
+  else if ( value_to_match == NULL || *value_to_match == NULL ) {
+    return FALSE;
+  }
 
   /* search part */
   if (sd->flags & MATCH_CASE) {
-    const gchar *p = strstr (*name, sd->key);
-    ret = p != NULL;
+    const gchar *p = strstr (*value_to_match, sd->key);
+    is_match = p != NULL;
     if (p && replacement) {
-      gchar *a = g_strndup (*name, p - *name);
+      gchar *a = g_strndup (*value_to_match, p - *value_to_match);
       gchar *b = g_strdup (p + strlen(sd->key));
       repl = g_strdup_printf ("%s%s%s", a, replacement, b);
       g_free (a);
       g_free (b);
     }
   } else {
-    gchar *s1 = g_utf8_casefold (*name, -1);
+    gchar *s1 = g_utf8_casefold (*value_to_match, -1);
     gchar *s2 = g_utf8_casefold (sd->key, -1);
     const gchar *p = strstr (s1, s2);
-    ret = p != NULL;
+    is_match = p != NULL;
     if (p && replacement) {
-      gchar *a = g_strndup (*name, p - s1);
-      gchar *b = g_strdup (*name + strlen(a) + strlen(sd->key));
+      gchar *a = g_strndup (*value_to_match, p - s1);
+      gchar *b = g_strdup (*value_to_match + strlen(a) + strlen(sd->key));
       repl = g_strdup_printf ("%s%s%s", a, replacement, b);
       g_free (a);
       g_free (b);
@@ -103,36 +106,242 @@
   }
 
   if (sd->flags & MATCH_WORD)
-    ret = (ret && strlen(*name) == strlen(sd->key));
+    is_match = (is_match && strlen(*value_to_match) == strlen(sd->key));
 
   /* replace part */
-  if (ret && replacement) {
-    g_free (*name);
-    *name = repl;
+  if (is_match && replacement) {
+    g_free (*value_to_match);
+    *value_to_match = repl;
   } else {
     g_free (repl);
   }
   
-  if (ret)
-    return prop;
-    
-  prop->ops->free(prop);
-  return NULL;
+  if ( is_match ) {
+    return TRUE;
+  }
+  else
+  {
+    return FALSE;
+  }
 }
 
+
+/*! Match and possibly modify the given object's name/text property */
+GPtrArray *
+_match_name_prop (DiaObject *obj, const SearchData *sd, const gchar 
*replacement)
+{
+  Property *prop;
+  gchar   **name;
+  gboolean is_match = FALSE;
+  GPtrArray *plist = NULL;
+
+  if ((prop = object_prop_by_name(obj, "name")) != NULL) {
+    name = &((StringProperty *)prop)->string_data;
+  }
+  else if ((prop = object_prop_by_name(obj, "text")) != NULL) {
+    name = &((TextProperty *)prop)->text_data;
+  }
+
+  if ( prop == NULL ) {
+    return NULL;
+  }
+
+  is_match = _match_text_prop ( obj, sd, replacement, prop, name );
+
+  if ( is_match == FALSE ) {
+    prop->ops->free ( prop );
+    return NULL;
+  }
+
+  plist = prop_list_from_single (prop);
+
+  return plist;
+}
+
+
+/*! Match and possibly modify one property in an object. */
+gboolean
+_match_prop ( DiaObject *obj, const SearchData *sd, const gchar *replacement, 
Property *prop )
+{
+  PropertyType prop_type;
+  gboolean is_match = FALSE;
+  gchar **text_data;
+
+  if ( prop == NULL ) {
+    return FALSE;
+  }
+
+  /* TODO: We could probably speed this up by using the type_quark,
+   *       but I don't know enough yet to use it safely... */
+  prop_type = prop->type;
+  if ( prop_type == NULL ) {
+    return FALSE;
+  }
+
+  /* Special case: array of sub-properties.  Do not continue with
+   * checking text for this property.  Instead, just
+   * recurse into _match_prop() for each sub-property in
+   * the array. */
+  if ( strcmp ( prop_type, PROP_TYPE_SARRAY ) == 0
+           || strcmp ( prop_type, PROP_TYPE_DARRAY ) == 0 )
+  {
+    GPtrArray *records = ((ArrayProperty *) prop)->records;
+    guint rnum;
+
+    if ( records == NULL ) {
+      return FALSE;
+    }
+
+    for ( rnum = 0; rnum < records->len; rnum ++ ) {
+      GPtrArray *sub_props = g_ptr_array_index ( records, rnum );
+      guint sub_num;
+
+      for ( sub_num = 0; sub_num < sub_props->len; sub_num ++ ) {
+       const gchar *sub_prop_name;
+       Property *sub_prop = g_ptr_array_index ( sub_props, sub_num );
+
+        is_match |= _match_prop ( obj, sd, replacement, sub_prop );
+      }
+    }
+
+    /* Done. */
+    return is_match;
+  }
+
+
+  /* Check for string / text property. */
+  if ( strcmp ( prop_type, PROP_TYPE_MULTISTRING ) == 0
+       || strcmp ( prop_type, PROP_TYPE_STRING ) == 0 )
+  {
+    text_data = &((StringProperty *) prop)->string_data;
+  }
+  else if ( strcmp ( prop_type, PROP_TYPE_TEXT ) == 0 )
+  {
+    text_data = &((TextProperty *) prop)->text_data;
+  }
+  /* TODO future:
+  else if ( strcmp ( prop_type, PROP_TYPE_STRINGLIST ) == 0 )
+  {
+  }
+  */
+  else
+  {
+    /* Not a type we're interested in (int, real, geometry, etc). */
+    text_data = NULL;
+  }
+
+
+  if ( text_data == NULL ) {
+    return FALSE;
+  }
+
+  return _match_text_prop ( obj, sd, replacement, prop, text_data );
+}
+
+
+/*! Match and possibly modify all the given object's properties. */
+GPtrArray *
+_match_all_props (DiaObject *obj, const SearchData *sd, const gchar 
*replacement)
+{
+  GPtrArray *all_plist = NULL;
+  GPtrArray *matched_plist = NULL;
+  PropDescription *prop_descriptions;
+  guint pnum;
+
+  if ( obj == NULL ) {
+    return NULL;
+  }
+
+  prop_descriptions = object_get_prop_descriptions ( obj );
+
+  if ( prop_descriptions == NULL ) {
+    return NULL;
+  }
+
+  all_plist = prop_list_from_descs ( prop_descriptions, pdtpp_true );
+  if ( all_plist == NULL ) {
+    return NULL;
+  }
+
+  /* Step though all object properties.
+   * Along the way, construct a list of matching properties (or
+   * replaced properties). */
+  for ( pnum = 0; pnum < all_plist->len; pnum ++ ) {
+    Property *prop = g_ptr_array_index ( all_plist, pnum );
+    gboolean is_match = FALSE;
+    const gchar *prop_name;
+
+    if ( prop == NULL || prop->name == NULL ) {
+      continue;
+    }
+
+    /* This extra step seems to be necessary to populate the property data. */
+    prop_name = prop->name;
+    prop->ops->free ( prop );
+    prop = object_prop_by_name ( obj, prop_name );
+
+    is_match = _match_prop ( obj, sd, replacement, prop );
+
+    if ( is_match == FALSE ) {
+      prop->ops->free ( prop );
+      continue;
+    }
+
+    /* We have a match. */
+    if ( matched_plist == NULL ) {
+      /* First time. */
+      matched_plist = prop_list_from_single (prop);
+    }
+    else {
+      /* Subsequent finds. */
+      GPtrArray *append_plist;
+      append_plist = prop_list_from_single ( prop );
+      prop_list_add_list ( matched_plist, append_plist );
+      prop_list_free ( append_plist );
+    }
+
+  } /* Continue stepping through all object properties. */
+
+  return matched_plist;
+}
+
+
+/*! Match and possibly modify one or more properties in an object.
+ *  Returns a list of modified Properties. */
+GPtrArray *
+_match_props ( DiaObject *obj, const SearchData *sd, const gchar *replacement )
+{
+  if ( obj == NULL || sd == NULL ) {
+    return FALSE;
+  }
+
+  if ( sd->flags & MATCH_ALL_PROPERTIES ) {
+    return _match_all_props ( obj, sd, replacement );
+  }
+  else {
+    return _match_name_prop ( obj, sd, replacement );
+  }
+}
+
+
+/* Only match (find), do not replace any values. */
 static gboolean
 _matches (DiaObject *obj, const SearchData *sd)
 {
-  Property *prop = NULL;
+  GPtrArray *plist = NULL;
 
-  if (!obj)
+  if (!obj) {
     return FALSE;
+  }
 
-  prop = _match_string_prop (obj, sd, NULL);
-  if (prop)
-    prop->ops->free(prop);
+  plist = _match_props (obj, sd, NULL);
+  if (plist == NULL) {
+    return FALSE;
+  }
 
-  return (prop != NULL);    
+  prop_list_free ( plist );
+
+  return TRUE;
 }
 
 static void
@@ -153,18 +362,20 @@
   }
 }
 
+/* Match and replace property values. */
 static gboolean
 _replace (DiaObject *obj, const SearchData *sd, const char *replacement)
 {
   ObjectChange *obj_change;
-  Property *prop;
-  GPtrArray *plist;
+  GPtrArray *plist = NULL;
 
-  prop = _match_string_prop (obj, sd, replacement);
-  if (!prop)
+  plist = _match_props (obj, sd, replacement );
+
+  if ( plist == NULL ) {
     return FALSE;
-    
-  plist = prop_list_from_single (prop);
+  }
+
+  /* Refresh screen and free the list of modified properties. */
   obj_change = object_apply_props (obj, plist);
   prop_list_free (plist);
 
@@ -194,6 +405,8 @@
                   g_object_get_data (G_OBJECT (widget), "match-case"))) ? 
MATCH_CASE : 0;
   sd.flags |= gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON ( 
                   g_object_get_data (G_OBJECT (widget), "match-word"))) ? 
MATCH_WORD : 0;
+  sd.flags |= gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON ( 
+                 g_object_get_data (G_OBJECT (widget), 
"match-all-properties"))) ? MATCH_ALL_PROPERTIES : 0;
   
 
   switch (response_id) {
@@ -266,6 +479,7 @@
   GtkWidget *search_entry;
   GtkWidget *match_case;
   GtkWidget *match_word;
+  GtkWidget *match_all_properties;
 
   gtk_dialog_set_default_response (GTK_DIALOG (dialog), RESPONSE_FIND);
 
@@ -311,6 +525,10 @@
   gtk_box_pack_start (GTK_BOX (vbox), match_word, FALSE, FALSE, 6);
   g_object_set_data (G_OBJECT (dialog), "match-word", match_word);
 
+  match_all_properties = gtk_check_button_new_with_mnemonic (_("Match _all 
properties (not just object name)"));
+  gtk_box_pack_start (GTK_BOX (vbox), match_all_properties, FALSE, FALSE, 6);
+  g_object_set_data (G_OBJECT (dialog), "match-all-properties", 
match_all_properties);
+
   gtk_widget_show_all (vbox);
 }



      __________________________________________________________________
Be smarter than spam. See how smart SpamGuard is at giving junk email the boot 
with the All-new Yahoo! Mail.  Click on Options in Mail and switch to New Mail 
today or register for free at http://mail.yahoo.ca
_______________________________________________
dia-list mailing list
[email protected]
http://mail.gnome.org/mailman/listinfo/dia-list
FAQ at http://live.gnome.org/Dia/Faq
Main page at http://live.gnome.org/Dia

Reply via email to