dia r4130 - in trunk: . app



Author: hans
Date: Fri Oct 17 18:58:53 2008
New Revision: 4130
URL: http://svn.gnome.org/viewvc/dia?rev=4130&view=rev

Log:
2008-10-17  Hans Breuer  <hans breuer org>

	* app/find-and-replace.c : patch from Johann Tienhaara adding the
	ability to find and/or replace (almost) any text content of the objects


Modified:
   trunk/ChangeLog
   trunk/app/find-and-replace.c

Modified: trunk/app/find-and-replace.c
==============================================================================
--- trunk/app/find-and-replace.c	(original)
+++ trunk/app/find-and-replace.c	Fri Oct 17 18:58:53 2008
@@ -1,387 +1,561 @@
-/* Dia -- an diagram creation/manipulation program
- * Copyright (C) 1998 Alexander Larsson
- *
- * find-and-replace.c - common functionality applied to diagram
- *
- * Copyright (C) 2008 Hans Breuer
- *
- * 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
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <config.h>
-
-#include <gtk/gtk.h>
-
-#include "intl.h"
-
-#include "diagram.h"
-#include "display.h"
-#include "object.h"
-#include "object_ops.h"
-#include "connectionpoint_ops.h"
-#include "undo.h"
-
-#include "find-and-replace.h"
-/* messing with property internals */
-#include "propinternals.h"
-
-enum {
-  RESPONSE_FIND = -20,
-  RESPONSE_REPLACE = -21,
-  RESPONSE_REPLACE_ALL = -23
-};
-
-enum {
-  MATCH_CASE = (1<<0),
-  MATCH_WORD = (1<<1)
-};
-
-typedef struct _SearchData {
-  const gchar *key;
-  guint      flags;
-  Diagram *diagram;
-  DiaObject *first; /* the first one found */
-  DiaObject *found; /* the one we were looking for */
-  DiaObject  *last; /* previously found */
-  gboolean seen_last;
-} SearchData;
-
-/*! Match and possibly modify the given objects property */
-Property *
-_match_string_prop (DiaObject *obj, const SearchData *sd, const gchar *replacement)
-{
-  Property *prop;
-  gchar   **name;
-  gboolean ret = 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;
-
-  /* search part */
-  if (sd->flags & MATCH_CASE) {
-    const gchar *p = strstr (*name, sd->key);
-    ret = p != NULL;
-    if (p && replacement) {
-      gchar *a = g_strndup (*name, p - *name);
-      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 *s2 = g_utf8_casefold (sd->key, -1);
-    const gchar *p = strstr (s1, s2);
-    ret = p != NULL;
-    if (p && replacement) {
-      gchar *a = g_strndup (*name, p - s1);
-      gchar *b = g_strdup (*name + strlen(a) + strlen(sd->key));
-      repl = g_strdup_printf ("%s%s%s", a, replacement, b);
-      g_free (a);
-      g_free (b);
-    }
-    g_free (s1);
-    g_free (s2);
-  }
-
-  if (sd->flags & MATCH_WORD)
-    ret = (ret && strlen(*name) == strlen(sd->key));
-
-  /* replace part */
-  if (ret && replacement) {
-    g_free (*name);
-    *name = repl;
-  } else {
-    g_free (repl);
-  }
-  
-  if (ret)
-    return prop;
-    
-  prop->ops->free(prop);
-  return NULL;
-}
-
-static gboolean
-_matches (DiaObject *obj, const SearchData *sd)
-{
-  Property *prop = NULL;
-
-  if (!obj)
-    return FALSE;
-
-  prop = _match_string_prop (obj, sd, NULL);
-  if (prop)
-    prop->ops->free(prop);
-
-  return (prop != NULL);    
-}
-
-static void
-find_func (DiaObject *obj, gpointer user_data)
-{
-  SearchData *sd = (SearchData *)user_data;
-  
-  if (!sd->found) {
-    if (_matches (obj, sd)) {
-      if (!sd->first)
-        sd->first = obj;
-      if (obj == sd->last)
-        sd->seen_last = TRUE;
-      else if (sd->seen_last) {
-        sd->found = obj;
-      }
-    }
-  }
-}
-
-static gboolean
-_replace (DiaObject *obj, const SearchData *sd, const char *replacement)
-{
-  ObjectChange *obj_change;
-  Property *prop;
-  GPtrArray *plist;
-
-  prop = _match_string_prop (obj, sd, replacement);
-  if (!prop)
-    return FALSE;
-    
-  plist = prop_list_from_single (prop);
-  obj_change = object_apply_props (obj, plist);
-  prop_list_free (plist);
-
-  if (obj_change)
-    undo_object_change(sd->diagram, obj, obj_change);
-    
-  object_add_updates(obj, sd->diagram);
-  diagram_update_connections_object(sd->diagram, obj, TRUE);
-  diagram_modified(sd->diagram);
-  diagram_object_modified(sd->diagram, obj);
-  diagram_update_extents(sd->diagram);
-  diagram_flush(sd->diagram);
-  
-  return TRUE;
-}
-
-static gint
-fnr_respond (GtkWidget *widget, gint response_id, gpointer data)
-{
-  const gchar *search = gtk_entry_get_text (g_object_get_data (G_OBJECT (widget), "search-entry")); 
-  const gchar *replace;
-  DDisplay *ddisp = (DDisplay*)data;
-  GList *list;
-  SearchData sd = { 0, };
-  sd.diagram = ddisp->diagram;
-  sd.flags =  gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON ( 
-                  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;
-  
-
-  switch (response_id) {
-  case RESPONSE_FIND :
-    sd.key = search;
-    sd.last = g_object_get_data (G_OBJECT (widget), "last-found");
-    if (!_matches (sd.last, &sd))
-      sd.last = NULL; /* reset if we start a new search */
-    diagram_remove_all_selected (ddisp->diagram, TRUE);
-    data_foreach_object (ddisp->diagram->data, find_func, &sd);
-    /* remember it */
-    sd.last = sd.found ? sd.found : sd.first;
-    g_object_set_data (G_OBJECT (widget), "last-found", sd.last);
-    if (sd.last) {
-      if (dia_object_get_parent_layer(sd.last) != ddisp->diagram->data->active_layer) {
-        /* can only select objects in the active layer */
-        data_set_active_layer(ddisp->diagram->data, dia_object_get_parent_layer(sd.last));
-        diagram_add_update_all(ddisp->diagram);
-        diagram_flush(ddisp->diagram);
-      }
-      diagram_select (ddisp->diagram, sd.last);
-    }
-    break;
-  case RESPONSE_REPLACE :
-    replace = gtk_entry_get_text (g_object_get_data (G_OBJECT (widget), "replace-entry"));
-    sd.key = search;
-    sd.last = g_object_get_data (G_OBJECT (widget), "last-found");
-    if (!_matches (sd.last, &sd)) {
-      sd.last = NULL; /* reset if we start a new search */
-      data_foreach_object (ddisp->diagram->data, find_func, &sd);
-    }
-    sd.last = sd.found ? sd.found : sd.first;
-    g_object_set_data (G_OBJECT (widget), "last-found", sd.last);
-    if (sd.last) {
-      _replace (sd.last, &sd, replace);
-      undo_set_transactionpoint(ddisp->diagram->undo);
-    }
-    g_object_set_data (G_OBJECT (widget), "last-found", sd.last);
-    break;
-  case RESPONSE_REPLACE_ALL :
-    replace = gtk_entry_get_text (g_object_get_data (G_OBJECT (widget), "replace-entry"));
-    sd.key = search;
-    sd.last = g_object_get_data (G_OBJECT (widget), "last-found");
-    do {
-      if (!_matches (sd.last, &sd)) {
-        sd.last = NULL; /* reset if we start a new search */
-	sd.first = NULL;
-        data_foreach_object (ddisp->diagram->data, find_func, &sd);
-      }
-      sd.last = sd.found ? sd.found : sd.first;
-      if (sd.last)
-        if (!_replace (sd.last, &sd, replace))
-	  sd.last = NULL;
-    } while (sd.last);
-    g_object_set_data (G_OBJECT (widget), "last-found", sd.last);
-    undo_set_transactionpoint(ddisp->diagram->undo);
-    break;
-  default:
-    gtk_widget_hide (widget);
-  }
-  return 0;
-}
-
-static void
-fnr_dialog_setup_common (GtkWidget *dialog, gboolean is_replace, DDisplay *ddisp)
-{
-  GtkWidget *vbox;
-  GtkWidget *hbox;
-  GtkWidget *label;
-  GtkWidget *search_entry;
-  GtkWidget *match_case;
-  GtkWidget *match_word;
-
-  gtk_dialog_set_default_response (GTK_DIALOG (dialog), RESPONSE_FIND);
-
-  /* don't destroy dialog when window manager close button pressed */
-  g_signal_connect(G_OBJECT (dialog), "response",
-		   G_CALLBACK(fnr_respond), ddisp);
-  g_signal_connect(G_OBJECT(dialog), "delete_event",
-		   G_CALLBACK(gtk_widget_hide), NULL);
-  g_signal_connect(GTK_OBJECT(dialog), "delete_event",
-		   G_CALLBACK(gtk_true), NULL);
-
-  vbox = GTK_DIALOG(dialog)->vbox;
-
-  hbox = gtk_hbox_new (FALSE, 12);
-  label = gtk_label_new_with_mnemonic (_("_Search for:"));
-  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-  search_entry = gtk_entry_new ();
-  g_object_set_data (G_OBJECT (dialog), "search-entry", search_entry);
-  gtk_label_set_mnemonic_widget (GTK_LABEL (label), search_entry);
-  gtk_entry_set_width_chars (GTK_ENTRY (search_entry), 30);
-  gtk_box_pack_start (GTK_BOX (hbox), search_entry, TRUE, TRUE, 0);
-  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6);
-  
-  if (is_replace) {
-    GtkWidget *replace_entry;
-
-    hbox = gtk_hbox_new (FALSE, 12);
-    label = gtk_label_new_with_mnemonic (_("Replace _with:"));
-    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
-    replace_entry = gtk_entry_new ();
-    g_object_set_data (G_OBJECT (dialog), "replace-entry", replace_entry);
-    gtk_label_set_mnemonic_widget (GTK_LABEL (label), replace_entry);
-    gtk_entry_set_width_chars (GTK_ENTRY (replace_entry), 30);
-    gtk_box_pack_start (GTK_BOX (hbox), replace_entry, TRUE, TRUE, 0);
-    gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6);
-  }
-
-  match_case = gtk_check_button_new_with_mnemonic (_("_Match case"));
-  gtk_box_pack_start (GTK_BOX (vbox), match_case, FALSE, FALSE, 6);
-  g_object_set_data (G_OBJECT (dialog), "match-case", match_case);
-
-  match_word = gtk_check_button_new_with_mnemonic (_("Match _entire word only"));
-  gtk_box_pack_start (GTK_BOX (vbox), match_word, FALSE, FALSE, 6);
-  g_object_set_data (G_OBJECT (dialog), "match-word", match_word);
-
-  gtk_widget_show_all (vbox);
-}
-
-/**
- * React to <Display>/Edit/Find
- */
-void
-edit_find_callback(gpointer data, guint action, GtkWidget *widget)
-{
-  DDisplay *ddisp;
-  Diagram *dia;
-  GtkWidget *dialog;
-
-  ddisp = ddisplay_active();
-  if (!ddisp) return;
-  dia = ddisp->diagram;
-
-  /* no static var, instead we are attaching the dialog to the diplay shell */
-  dialog = g_object_get_data (G_OBJECT (ddisp->shell), "edit-find-dialog");
-  if (!dialog) {
-    dialog = gtk_dialog_new_with_buttons (
-		_("Find"), 
-		GTK_WINDOW (ddisp->shell), GTK_DIALOG_DESTROY_WITH_PARENT,
-		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
-		GTK_STOCK_FIND, RESPONSE_FIND,
-		NULL);
-
-    fnr_dialog_setup_common (dialog, FALSE, ddisp);
-  }
-  g_object_set_data (G_OBJECT (ddisp->shell), "edit-find-dialog", dialog);
-
-  gtk_dialog_run (GTK_DIALOG (dialog));  
-}
-
-/**
- * React to <Display>/Edit/Replace
- */
-void
-edit_replace_callback(gpointer data, guint action, GtkWidget *widget)
-{
-  DDisplay *ddisp;
-  Diagram *dia;
-  GtkWidget *dialog;
-
-  ddisp = ddisplay_active();
-  if (!ddisp) return;
-  dia = ddisp->diagram;
-
-  /* no static var, instead we are attaching the dialog to the diplay shell */
-  dialog = g_object_get_data (G_OBJECT (ddisp->shell), "edit-replace-dialog");
-  if (!dialog) {
-    GtkWidget *button;
-    dialog = gtk_dialog_new_with_buttons (
-		_("Replace"),
-		GTK_WINDOW (ddisp->shell), GTK_DIALOG_DESTROY_WITH_PARENT,
-		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
-		_("Replace _All"), RESPONSE_REPLACE_ALL,
-		NULL);
-    /* not adding the button in the list above to modify it's text; 
-     * the default "Find and Replace" is just too long for my taste ;) 
-     */
-    button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Replace"), RESPONSE_REPLACE);
-    gtk_button_set_image (GTK_BUTTON (button), 
-                          gtk_image_new_from_stock (GTK_STOCK_FIND_AND_REPLACE, GTK_ICON_SIZE_BUTTON));
-
-    gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_FIND, RESPONSE_FIND);
-
-    fnr_dialog_setup_common (dialog, TRUE, ddisp);
-  }
-  g_object_set_data (G_OBJECT (ddisp->shell), "edit-replace-dialog", dialog);
-
-  gtk_dialog_run (GTK_DIALOG (dialog));  
-}
-
+/* Dia -- an diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <gtk/gtk.h>
+
+#include "intl.h"
+
+#include "diagram.h"
+#include "display.h"
+#include "object.h"
+#include "object_ops.h"
+#include "connectionpoint_ops.h"
+#include "undo.h"
+
+#include "find-and-replace.h"
+/* messing with property internals */
+#include "propinternals.h"
+
+enum {
+  RESPONSE_FIND = -20,
+  RESPONSE_REPLACE = -21,
+  RESPONSE_REPLACE_ALL = -23
+};
+
+enum {
+  MATCH_CASE = (1<<0),
+  MATCH_WORD = (1<<1),
+  /* Don't just match the name/text - match UML attributes etc too? */
+  MATCH_ALL_PROPERTIES = (1<<2)
+};
+
+typedef struct _SearchData {
+  const gchar *key;
+  guint      flags;
+  Diagram *diagram;
+  DiaObject *first; /* the first one found */
+  DiaObject *found; /* the one we were looking for */
+  DiaObject  *last; /* previously found */
+  gboolean seen_last;
+} SearchData;
+
+
+/*! Match and possibly modify the given object's given property.
+ * Returns FALSE if not matched or if the input property is NULL. */
+static gboolean
+_match_text_prop (DiaObject *obj, const SearchData *sd, const gchar *replacement, gchar **value_to_match)
+{
+  gboolean is_match = FALSE;
+  gchar    *repl = NULL;
+
+  if (!value_to_match || *value_to_match == NULL)
+    return FALSE;
+
+  /* search part */
+  if (sd->flags & MATCH_CASE) {
+    const gchar *p = strstr (*value_to_match, sd->key);
+    is_match = p != NULL;
+    if (p && replacement) {
+      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 (*value_to_match, -1);
+    gchar *s2 = g_utf8_casefold (sd->key, -1);
+    const gchar *p = strstr (s1, s2);
+    is_match = p != NULL;
+    if (p && replacement) {
+      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);
+    }
+    g_free (s1);
+    g_free (s2);
+  }
+
+  if (sd->flags & MATCH_WORD)
+    is_match = (is_match && strlen(*value_to_match) == strlen(sd->key));
+
+  /* replace part */
+  if (is_match && replacement) {
+    g_free (*value_to_match);
+    *value_to_match = repl;
+  } else {
+    g_free (repl);
+  }
+  
+  return is_match;
+}
+
+
+/*! Match and possibly modify the given object's name/text property */
+static 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;
+  else
+    return NULL;
+
+  is_match = _match_text_prop (obj, sd, replacement, name);
+
+  if (!is_match) {
+    prop->ops->free (prop);
+    return NULL;
+  }
+
+  plist = prop_list_from_single (prop);
+
+  return plist;
+}
+
+/*! Match and possibly modify one property in an object. */
+static 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)
+    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)
+    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)
+      return FALSE;
+
+    for (rnum = 0; rnum < records->len && !is_match; ++rnum) {
+      GPtrArray *sub_props = g_ptr_array_index (records, rnum);
+      guint sub_num;
+
+      for (sub_num = 0; sub_num < sub_props->len && !is_match; ++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). */
+    return FALSE;
+  }
+
+  return _match_text_prop (obj, sd, replacement, text_data);
+}
+
+/*! Match and possibly modify all the given object's properties. */
+static GPtrArray *
+_match_all_props (DiaObject *obj, const SearchData *sd, const gchar *replacement)
+{
+  GPtrArray *all_plist = NULL;
+  GPtrArray *matched_plist = NULL;
+  const PropDescription *desc;
+  guint pnum;
+
+  if (!obj)
+    return NULL;
+
+  desc = object_get_prop_descriptions (obj);
+  if (!desc)
+    return NULL;
+
+  all_plist = prop_list_from_descs (desc, pdtpp_true);
+  if (!all_plist)
+    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 || !prop->name)
+      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) {
+      prop->ops->free (prop);
+      continue;
+    }
+
+    /* We have a match. */
+    if (!matched_plist) {
+      /* First time. */
+      matched_plist = prop_list_from_single (prop);
+    } else {
+      //FIXME: do we realy want a replace all here?
+      /* 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. */
+static GPtrArray *
+_match_props (DiaObject *obj, const SearchData *sd, const gchar *replacement)
+{
+  g_return_val_if_fail (obj && sd, NULL);
+
+  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)
+{
+  GPtrArray *plist = NULL;
+
+  if (!obj)
+    return FALSE;
+
+  plist = _match_props (obj, sd, NULL);
+  if (!plist)
+    return FALSE;
+
+  prop_list_free (plist);
+
+  return TRUE;
+}
+
+static void
+find_func (DiaObject *obj, gpointer user_data)
+{
+  SearchData *sd = (SearchData *)user_data;
+  
+  if (!sd->found) {
+    if (_matches (obj, sd)) {
+      if (!sd->first)
+        sd->first = obj;
+      if (obj == sd->last)
+        sd->seen_last = TRUE;
+      else if (sd->seen_last) {
+        sd->found = obj;
+      }
+    }
+  }
+}
+
+/* Match and replace property values. */
+static gboolean
+_replace (DiaObject *obj, const SearchData *sd, const char *replacement)
+{
+  ObjectChange *obj_change;
+  GPtrArray *plist = NULL;
+
+  plist = _match_props (obj, sd, replacement);
+  if (!plist)
+    return FALSE;
+
+  /* Refresh screen and free the list of modified properties. */
+  obj_change = object_apply_props (obj, plist);
+  prop_list_free (plist);
+
+  if (obj_change)
+    undo_object_change(sd->diagram, obj, obj_change);
+    
+  object_add_updates(obj, sd->diagram);
+  diagram_update_connections_object(sd->diagram, obj, TRUE);
+  diagram_modified(sd->diagram);
+  diagram_object_modified(sd->diagram, obj);
+  diagram_update_extents(sd->diagram);
+  diagram_flush(sd->diagram);
+  
+  return TRUE;
+}
+
+static gint
+fnr_respond (GtkWidget *widget, gint response_id, gpointer data)
+{
+  const gchar *search = gtk_entry_get_text (g_object_get_data (G_OBJECT (widget), "search-entry")); 
+  const gchar *replace;
+  DDisplay *ddisp = (DDisplay*)data;
+  GList *list;
+  SearchData sd = { 0, };
+  sd.diagram = ddisp->diagram;
+  sd.flags =  gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON ( 
+                  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) {
+  case RESPONSE_FIND :
+    sd.key = search;
+    sd.last = g_object_get_data (G_OBJECT (widget), "last-found");
+    if (!_matches (sd.last, &sd))
+      sd.last = NULL; /* reset if we start a new search */
+    diagram_remove_all_selected (ddisp->diagram, TRUE);
+    data_foreach_object (ddisp->diagram->data, find_func, &sd);
+    /* remember it */
+    sd.last = sd.found ? sd.found : sd.first;
+    g_object_set_data (G_OBJECT (widget), "last-found", sd.last);
+    if (sd.last) {
+      if (dia_object_get_parent_layer(sd.last) != ddisp->diagram->data->active_layer) {
+        /* can only select objects in the active layer */
+        data_set_active_layer(ddisp->diagram->data, dia_object_get_parent_layer(sd.last));
+        diagram_add_update_all(ddisp->diagram);
+        diagram_flush(ddisp->diagram);
+      }
+      diagram_select (ddisp->diagram, sd.last);
+    }
+    break;
+  case RESPONSE_REPLACE :
+    replace = gtk_entry_get_text (g_object_get_data (G_OBJECT (widget), "replace-entry"));
+    sd.key = search;
+    sd.last = g_object_get_data (G_OBJECT (widget), "last-found");
+    if (!_matches (sd.last, &sd)) {
+      sd.last = NULL; /* reset if we start a new search */
+      data_foreach_object (ddisp->diagram->data, find_func, &sd);
+    }
+    sd.last = sd.found ? sd.found : sd.first;
+    g_object_set_data (G_OBJECT (widget), "last-found", sd.last);
+    if (sd.last) {
+      _replace (sd.last, &sd, replace);
+      undo_set_transactionpoint(ddisp->diagram->undo);
+    }
+    g_object_set_data (G_OBJECT (widget), "last-found", sd.last);
+    break;
+  case RESPONSE_REPLACE_ALL :
+    replace = gtk_entry_get_text (g_object_get_data (G_OBJECT (widget), "replace-entry"));
+    sd.key = search;
+    sd.last = g_object_get_data (G_OBJECT (widget), "last-found");
+    do {
+      if (!_matches (sd.last, &sd)) {
+        sd.last = NULL; /* reset if we start a new search */
+	sd.first = NULL;
+        data_foreach_object (ddisp->diagram->data, find_func, &sd);
+      }
+      sd.last = sd.found ? sd.found : sd.first;
+      if (sd.last)
+        if (!_replace (sd.last, &sd, replace))
+	  sd.last = NULL;
+    } while (sd.last);
+    g_object_set_data (G_OBJECT (widget), "last-found", sd.last);
+    undo_set_transactionpoint(ddisp->diagram->undo);
+    break;
+  default:
+    gtk_widget_hide (widget);
+  }
+  return 0;
+}
+
+static void
+fnr_dialog_setup_common (GtkWidget *dialog, gboolean is_replace, DDisplay *ddisp)
+{
+  GtkWidget *vbox;
+  GtkWidget *hbox;
+  GtkWidget *label;
+  GtkWidget *search_entry;
+  GtkWidget *match_case;
+  GtkWidget *match_word;
+  GtkWidget *match_all_properties;
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), RESPONSE_FIND);
+
+  /* don't destroy dialog when window manager close button pressed */
+  g_signal_connect(G_OBJECT (dialog), "response",
+		   G_CALLBACK(fnr_respond), ddisp);
+  g_signal_connect(G_OBJECT(dialog), "delete_event",
+		   G_CALLBACK(gtk_widget_hide), NULL);
+  g_signal_connect(GTK_OBJECT(dialog), "delete_event",
+		   G_CALLBACK(gtk_true), NULL);
+
+  vbox = GTK_DIALOG(dialog)->vbox;
+
+  hbox = gtk_hbox_new (FALSE, 12);
+  label = gtk_label_new_with_mnemonic (_("_Search for:"));
+  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+  search_entry = gtk_entry_new ();
+  g_object_set_data (G_OBJECT (dialog), "search-entry", search_entry);
+  gtk_label_set_mnemonic_widget (GTK_LABEL (label), search_entry);
+  gtk_entry_set_width_chars (GTK_ENTRY (search_entry), 30);
+  gtk_box_pack_start (GTK_BOX (hbox), search_entry, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6);
+  
+  if (is_replace) {
+    GtkWidget *replace_entry;
+
+    hbox = gtk_hbox_new (FALSE, 12);
+    label = gtk_label_new_with_mnemonic (_("Replace _with:"));
+    gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+    replace_entry = gtk_entry_new ();
+    g_object_set_data (G_OBJECT (dialog), "replace-entry", replace_entry);
+    gtk_label_set_mnemonic_widget (GTK_LABEL (label), replace_entry);
+    gtk_entry_set_width_chars (GTK_ENTRY (replace_entry), 30);
+    gtk_box_pack_start (GTK_BOX (hbox), replace_entry, TRUE, TRUE, 0);
+    gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 6);
+  }
+
+  match_case = gtk_check_button_new_with_mnemonic (_("_Match case"));
+  gtk_box_pack_start (GTK_BOX (vbox), match_case, FALSE, FALSE, 6);
+  g_object_set_data (G_OBJECT (dialog), "match-case", match_case);
+
+  match_word = gtk_check_button_new_with_mnemonic (_("Match _entire word only"));
+  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);
+}
+
+/**
+ * React to <Display>/Edit/Find
+ */
+void
+edit_find_callback(gpointer data, guint action, GtkWidget *widget)
+{
+  DDisplay *ddisp;
+  Diagram *dia;
+  GtkWidget *dialog;
+
+  ddisp = ddisplay_active();
+  if (!ddisp) return;
+  dia = ddisp->diagram;
+
+  /* no static var, instead we are attaching the dialog to the diplay shell */
+  dialog = g_object_get_data (G_OBJECT (ddisp->shell), "edit-find-dialog");
+  if (!dialog) {
+    dialog = gtk_dialog_new_with_buttons (
+		_("Find"), 
+		GTK_WINDOW (ddisp->shell), GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+		GTK_STOCK_FIND, RESPONSE_FIND,
+		NULL);
+
+    fnr_dialog_setup_common (dialog, FALSE, ddisp);
+  }
+  g_object_set_data (G_OBJECT (ddisp->shell), "edit-find-dialog", dialog);
+
+  gtk_dialog_run (GTK_DIALOG (dialog));  
+}
+
+/**
+ * React to <Display>/Edit/Replace
+ */
+void
+edit_replace_callback(gpointer data, guint action, GtkWidget *widget)
+{
+  DDisplay *ddisp;
+  Diagram *dia;
+  GtkWidget *dialog;
+
+  ddisp = ddisplay_active();
+  if (!ddisp) return;
+  dia = ddisp->diagram;
+
+  /* no static var, instead we are attaching the dialog to the diplay shell */
+  dialog = g_object_get_data (G_OBJECT (ddisp->shell), "edit-replace-dialog");
+  if (!dialog) {
+    GtkWidget *button;
+    dialog = gtk_dialog_new_with_buttons (
+		_("Replace"),
+		GTK_WINDOW (ddisp->shell), GTK_DIALOG_DESTROY_WITH_PARENT,
+		GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+		_("Replace _All"), RESPONSE_REPLACE_ALL,
+		NULL);
+    /* not adding the button in the list above to modify it's text; 
+     * the default "Find and Replace" is just too long for my taste ;) 
+     */
+    button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Replace"), RESPONSE_REPLACE);
+    gtk_button_set_image (GTK_BUTTON (button), 
+                          gtk_image_new_from_stock (GTK_STOCK_FIND_AND_REPLACE, GTK_ICON_SIZE_BUTTON));
+
+    gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_FIND, RESPONSE_FIND);
+
+    fnr_dialog_setup_common (dialog, TRUE, ddisp);
+  }
+  g_object_set_data (G_OBJECT (ddisp->shell), "edit-replace-dialog", dialog);
+
+  gtk_dialog_run (GTK_DIALOG (dialog));  
+}



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