[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]
Dia find & replace?
- From: Johann Tienhaara <jtienhaara yahoo com>
- To: dia-list gnome org
- Subject: Dia find & replace?
- Date: Fri, 10 Oct 2008 19:12:19 -0700 (PDT)
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 <jtienhaara yahoo com> 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
[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]