[gnome-terminal/search] Bug 78963 - Ability to find/search text in buffer



commit 4164e0c42f210cd154d23d84a8d873274c361c23
Author: Behdad Esfahbod <behdad behdad org>
Date:   Mon Apr 26 12:17:25 2010 -0400

    Bug 78963 - Ability to find/search text in buffer
    
    Rewrite search.  Adapted dialog UI / code from gedit.  Calls new vte API.

 src/Makefile.am                              |    8 +-
 src/{search-find.glade => find-dialog.glade} |   84 +++-
 src/gnome-terminal.schemas.in                |   47 --
 src/terminal-search-dialog.c                 |  378 ++++++++++++++
 src/terminal-search-dialog.h                 |   49 ++
 src/terminal-search.c                        |  682 --------------------------
 src/terminal-search.h                        |   28 -
 src/terminal-window.c                        |  209 ++++++++-
 src/terminal.xml                             |   16 +-
 9 files changed, 697 insertions(+), 804 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index b2dcf01..d4b59d7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -38,8 +38,8 @@ gnome_terminal_SOURCES= \
 	terminal-screen.h \
 	terminal-screen-container.c \
 	terminal-screen-container.h \
-	terminal-search.c \
-	terminal-search.h \
+	terminal-search-dialog.c \
+	terminal-search-dialog.h \
 	terminal-tab-label.c \
 	terminal-tab-label.h \
 	terminal-tabs-menu.c \
@@ -169,16 +169,16 @@ uimanager_DATA = \
 
 builder_in_files = \
 	encodings-dialog.glade \
+	find-dialog.glade \
 	keybinding-editor.glade \
 	profile-manager.glade \
 	profile-new-dialog.glade \
 	profile-preferences.glade \
-	search-find.glade \
 	skey-challenge.glade \
 	$(NULL)
 
 builderdir = $(pkgdatadir)
-builder_DATA = $(builder_in_files:.glade=.ui)
+builder_DATA =  $(builder_in_files:.glade=.ui)
 
 CLEANFILES = \
 	stamp-terminal-type-builtins.h \
diff --git a/src/search-find.glade b/src/find-dialog.glade
similarity index 72%
rename from src/search-find.glade
rename to src/find-dialog.glade
index 91a60a4..b832e2e 100644
--- a/src/search-find.glade
+++ b/src/find-dialog.glade
@@ -4,20 +4,20 @@
 <glade-interface>
 <requires lib="gnome"/>
 
-<widget class="GtkDialog" id="dialog-find">
-  <property name="border_width">8</property>
+<widget class="GtkDialog" id="find-dialog">
+  <property name="border_width">5</property>
   <property name="visible">True</property>
   <property name="title" translatable="yes">Find</property>
   <property name="type">GTK_WINDOW_TOPLEVEL</property>
   <property name="window_position">GTK_WIN_POS_NONE</property>
   <property name="modal">False</property>
   <property name="resizable">False</property>
-  <property name="destroy_with_parent">False</property>
+  <property name="destroy_with_parent">True</property>
   <property name="icon_name">gtk-find</property>
   <property name="decorated">True</property>
   <property name="skip_taskbar_hint">False</property>
   <property name="skip_pager_hint">False</property>
-  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
   <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
   <property name="focus_on_map">True</property>
   <property name="urgency_hint">False</property>
@@ -27,7 +27,7 @@
     <widget class="GtkVBox" id="vbox1">
       <property name="visible">True</property>
       <property name="homogeneous">False</property>
-      <property name="spacing">5</property>
+      <property name="spacing">18</property>
 
       <child internal-child="action_area">
 	<widget class="GtkHButtonBox" id="hbuttonbox2">
@@ -41,7 +41,7 @@
 	      <property name="label">gtk-close</property>
 	      <property name="use_stock">True</property>
 	      <property name="relief">GTK_RELIEF_NORMAL</property>
-	      <property name="focus_on_click">True</property>
+	      <property name="focus_on_click">False</property>
 	      <property name="response_id">-7</property>
 	    </widget>
 	  </child>
@@ -55,8 +55,8 @@
 	      <property name="label">gtk-find</property>
 	      <property name="use_stock">True</property>
 	      <property name="relief">GTK_RELIEF_NORMAL</property>
-	      <property name="focus_on_click">False</property>
-	      <property name="response_id">-10</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-3</property>
 	    </widget>
 	  </child>
 	</widget>
@@ -72,10 +72,10 @@
 	<widget class="GtkHBox" id="hbox1">
 	  <property name="visible">True</property>
 	  <property name="homogeneous">False</property>
-	  <property name="spacing">0</property>
+	  <property name="spacing">12</property>
 
 	  <child>
-	    <widget class="GtkLabel" id="label1">
+	    <widget class="GtkLabel" id="search-label">
 	      <property name="visible">True</property>
 	      <property name="label" translatable="yes">_Search for:</property>
 	      <property name="use_underline">True</property>
@@ -87,7 +87,7 @@
 	      <property name="yalign">0.5</property>
 	      <property name="xpad">0</property>
 	      <property name="ypad">0</property>
-	      <property name="mnemonic_widget">entry-find</property>
+	      <property name="mnemonic_widget">search-entry</property>
 	      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
 	      <property name="width_chars">-1</property>
 	      <property name="single_line_mode">False</property>
@@ -101,17 +101,11 @@
 	  </child>
 
 	  <child>
-	    <widget class="GtkEntry" id="entry-find">
+	    <widget class="GtkComboBoxEntry" id="search-entry">
 	      <property name="visible">True</property>
-	      <property name="can_focus">True</property>
-	      <property name="editable">True</property>
-	      <property name="visibility">True</property>
-	      <property name="max_length">0</property>
-	      <property name="text" translatable="yes"></property>
+	      <property name="add_tearoffs">False</property>
 	      <property name="has_frame">True</property>
-	      <property name="invisible_char">â?¢</property>
-	      <property name="activates_default">True</property>
-	      <property name="width_chars">30</property>
+	      <property name="focus_on_click">True</property>
 	    </widget>
 	    <packing>
 	      <property name="padding">0</property>
@@ -131,13 +125,13 @@
 	<widget class="GtkVBox" id="vbox2">
 	  <property name="visible">True</property>
 	  <property name="homogeneous">False</property>
-	  <property name="spacing">0</property>
+	  <property name="spacing">12</property>
 
 	  <child>
-	    <widget class="GtkCheckButton" id="check-case">
+	    <widget class="GtkCheckButton" id="match-case-checkbutton">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
-	      <property name="label" translatable="yes">_Match Case</property>
+	      <property name="label" translatable="yes">_Match case</property>
 	      <property name="use_underline">True</property>
 	      <property name="relief">GTK_RELIEF_NORMAL</property>
 	      <property name="focus_on_click">False</property>
@@ -153,7 +147,7 @@
 	  </child>
 
 	  <child>
-	    <widget class="GtkCheckButton" id="check-whole">
+	    <widget class="GtkCheckButton" id="entire-word-checkbutton">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
 	      <property name="label" translatable="yes">Match _entire word only</property>
@@ -172,10 +166,10 @@
 	  </child>
 
 	  <child>
-	    <widget class="GtkCheckButton" id="check-regex">
+	    <widget class="GtkCheckButton" id="regex-checkbutton">
 	      <property name="visible">True</property>
 	      <property name="can_focus">True</property>
-	      <property name="label" translatable="yes">_Regular Expression</property>
+	      <property name="label" translatable="yes">Match as _regular expression</property>
 	      <property name="use_underline">True</property>
 	      <property name="relief">GTK_RELIEF_NORMAL</property>
 	      <property name="focus_on_click">False</property>
@@ -189,6 +183,44 @@
 	      <property name="fill">False</property>
 	    </packing>
 	  </child>
+
+	  <child>
+	    <widget class="GtkCheckButton" id="search-backwards-checkbutton">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label" translatable="yes">Search _backwards</property>
+	      <property name="use_underline">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">False</property>
+	      <property name="active">True</property>
+	      <property name="inconsistent">False</property>
+	      <property name="draw_indicator">True</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkCheckButton" id="wrap-around-checkbutton">
+	      <property name="visible">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label" translatable="yes">_Wrap around</property>
+	      <property name="use_underline">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">False</property>
+	      <property name="active">True</property>
+	      <property name="inconsistent">False</property>
+	      <property name="draw_indicator">True</property>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">False</property>
+	    </packing>
+	  </child>
 	</widget>
 	<packing>
 	  <property name="padding">0</property>
diff --git a/src/gnome-terminal.schemas.in b/src/gnome-terminal.schemas.in
index c2c7d33..587e8d0 100644
--- a/src/gnome-terminal.schemas.in
+++ b/src/gnome-terminal.schemas.in
@@ -1298,52 +1298,5 @@
       </locale>
     </schema>
 
-    <!-- Find settings -->
-
-    <schema>
-      <key>/schemas/apps/gnome-terminal/find/match_case</key>
-      <applyto>/apps/gnome-terminal/find/match_case</applyto>
-      <owner>gnome-terminal</owner>
-      <type>bool</type>
-      <default>false</default>
-      <locale name="C">
-         <short>Case-sensitive find</short>
-         <long>
-          When enabled, the Find Dialog will only find text that matches the find
-          string exactly.  The case of the letters is significant.
-         </long>
-      </locale>
-    </schema>
-
-    <schema>
-      <key>/schemas/apps/gnome-terminal/find/match_regex</key>
-      <applyto>/apps/gnome-terminal/find/match_regex</applyto>
-      <owner>gnome-terminal</owner>
-      <type>bool</type>
-      <default>false</default>
-      <locale name="C">
-         <short>Find using regular expressions</short>
-         <long>
-          When enabled, the Find Dialog can search for text using Perl-compatible
-          regular expressions.
-         </long>
-      </locale>
-    </schema>
-
-    <schema>
-      <key>/schemas/apps/gnome-terminal/find/match_whole</key>
-      <applyto>/apps/gnome-terminal/find/match_whole</applyto>
-      <owner>gnome-terminal</owner>
-      <type>bool</type>
-      <default>false</default>
-      <locale name="C">
-         <short>Only find whole words</short>
-         <long>
-          When enabled, the Find Dialog will only match whole words.
-          The matching text must be surrounded by spaces, tabs, punctuation, etc.
-         </long>
-      </locale>
-    </schema>
-
   </schemalist>
 </gconfschemafile>
diff --git a/src/terminal-search-dialog.c b/src/terminal-search-dialog.c
new file mode 100644
index 0000000..07510d7
--- /dev/null
+++ b/src/terminal-search-dialog.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright © 2005 Paolo Maggi
+ * Copyright © 2010 Red Hat (Red Hat author: Behdad Esfahbod)
+ *
+ * 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 <string.h>
+
+#include "terminal-search-dialog.h"
+#include "terminal-util.h"
+
+#define HISTORY_MIN_ITEM_LEN 3
+#define HISTORY_LENGTH 20
+
+static GQuark
+get_quark (void)
+{
+  static GQuark quark = 0;
+
+  if (G_UNLIKELY (!quark))
+    quark = g_quark_from_static_string ("GT:data");
+
+  return quark;
+}
+
+
+#define TERMINAL_SEARCH_DIALOG_GET_PRIVATE(object) \
+  ((TerminalSearchDialogPrivate *) g_object_get_qdata (G_OBJECT (object), get_quark ()))
+
+typedef struct _TerminalSearchDialogPrivate
+{
+  GtkWidget *search_label;
+  GtkWidget *search_entry;
+  GtkWidget *search_text_entry;
+  GtkWidget *match_case_checkbutton;
+  GtkWidget *entire_word_checkbutton;
+  GtkWidget *regex_checkbutton;
+  GtkWidget *backwards_checkbutton;
+  GtkWidget *wrap_around_checkbutton;
+
+  GtkListStore *store;
+  GtkEntryCompletion *completion;
+
+  /* Cached regex */
+  GRegex *regex;
+  GRegexCompileFlags regex_compile_flags;
+} TerminalSearchDialogPrivate;
+
+
+static void search_text_entry_changed (GtkEditable *editable,
+				       GtkWidget   *dialog);
+static void response_handler (GtkWidget *dialog,
+			      gint       response_id,
+			      gpointer   data);
+static void terminal_search_dialog_private_destroy (TerminalSearchDialogPrivate *priv);
+
+
+GtkWidget *
+terminal_search_dialog_new (GtkWindow   *parent)
+{
+  GtkWidget *dialog;
+  TerminalSearchDialogPrivate *priv;
+  GtkListStore *store;
+  GtkEntryCompletion *completion;
+
+  priv = g_new0 (TerminalSearchDialogPrivate, 1);
+
+  if (!terminal_util_load_builder_file ("find-dialog.ui",
+					"find-dialog", &dialog,
+					"search-label", &priv->search_label,
+					"search-entry", &priv->search_entry,
+					"match-case-checkbutton", &priv->match_case_checkbutton,
+					"entire-word-checkbutton", &priv->entire_word_checkbutton,
+					"regex-checkbutton", &priv->regex_checkbutton,
+					"search-backwards-checkbutton", &priv->backwards_checkbutton,
+					"wrap-around-checkbutton", &priv->wrap_around_checkbutton,
+					NULL))
+  {
+    g_free (priv);
+    return NULL;
+  }
+
+  g_object_set_qdata_full (G_OBJECT (dialog), get_quark (), priv,
+			   (GDestroyNotify) terminal_search_dialog_private_destroy);
+
+
+  priv->search_text_entry = gtk_bin_get_child (GTK_BIN (priv->search_entry));
+
+  priv->store = store = gtk_list_store_new (1, G_TYPE_STRING);
+  g_object_set (G_OBJECT (priv->search_entry),
+		"model", store,
+		"text-column", 0,
+		NULL);
+
+  priv->completion = completion = gtk_entry_completion_new ();
+  gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (store));
+  gtk_entry_completion_set_text_column (completion, 0);
+  gtk_entry_completion_set_minimum_key_length (completion, HISTORY_MIN_ITEM_LEN);
+  gtk_entry_completion_set_popup_completion (completion, FALSE);
+  gtk_entry_completion_set_inline_completion (completion, TRUE);
+  gtk_entry_set_completion (GTK_ENTRY (priv->search_text_entry), completion);
+
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
+  gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT, FALSE);
+
+  gtk_entry_set_activates_default (GTK_ENTRY (priv->search_text_entry), TRUE);
+  g_signal_connect (priv->search_text_entry, "changed", G_CALLBACK (search_text_entry_changed), dialog);
+
+  g_signal_connect (dialog, "response", G_CALLBACK (response_handler), NULL);
+
+  if (parent)
+    gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+
+  return GTK_WIDGET (dialog);
+}
+
+void
+terminal_search_dialog_present (GtkWidget *dialog)
+{
+  TerminalSearchDialogPrivate *priv;
+
+  g_return_if_fail (GTK_IS_DIALOG (dialog));
+
+  priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog);
+  g_return_if_fail (priv);
+
+  gtk_window_present (GTK_WINDOW (dialog));
+  gtk_widget_grab_focus (priv->search_text_entry);
+}
+
+static void
+terminal_search_dialog_private_destroy (TerminalSearchDialogPrivate *priv)
+{
+
+  if (priv->regex)
+    g_regex_unref (priv->regex);
+
+  g_object_unref (priv->store);
+  g_object_unref (priv->completion);
+
+  g_free (priv);
+}
+
+
+static void
+search_text_entry_changed (GtkEditable *editable,
+			   GtkWidget   *dialog)
+{
+  TerminalSearchDialogPrivate *priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog);
+  const gchar *search_string;
+
+  search_string = gtk_entry_get_text (GTK_ENTRY (editable));
+  g_return_if_fail (search_string != NULL);
+
+  gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+				     GTK_RESPONSE_ACCEPT,
+				     *search_string != '\0');
+
+  if (priv->regex) {
+    g_regex_unref (priv->regex);
+    priv->regex = NULL;
+  }
+}
+
+static gboolean
+remove_item (GtkListStore *store,
+	     const gchar  *text)
+{
+	GtkTreeIter iter;
+
+	g_return_val_if_fail (text != NULL, FALSE);
+
+	if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
+		return FALSE;
+
+	do {
+		gchar *item_text;
+
+		gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
+				    0, &item_text, -1);
+
+		if (item_text != NULL && strcmp (item_text, text) == 0)
+		{
+			gtk_list_store_remove (store, &iter);
+			g_free (item_text);
+			return TRUE;
+		}
+
+		g_free (item_text);
+
+	} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
+
+	return FALSE;
+}
+
+static void
+clamp_list_store (GtkListStore *store,
+		  guint         max)
+{
+	GtkTreePath *path;
+	GtkTreeIter iter;
+
+	/* -1 because TreePath counts from 0 */
+	path = gtk_tree_path_new_from_indices (max - 1, -1);
+
+	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path))
+		while (1)
+			if (!gtk_list_store_remove (store, &iter))
+				break;
+
+	gtk_tree_path_free (path);
+}
+
+static void
+history_entry_insert (GtkListStore *store,
+		      const gchar  *text)
+{
+	GtkTreeIter iter;
+
+	g_return_if_fail (text != NULL);
+
+	if (g_utf8_strlen (text, -1) <= HISTORY_MIN_ITEM_LEN)
+		return;
+
+	/* remove the text from the store if it was already
+	 * present. If it wasn't, clamp to max history - 1
+	 * before inserting the new row, otherwise appending
+	 * would not work */
+
+	if (!remove_item (store, text))
+		clamp_list_store (store, HISTORY_LENGTH - 1);
+
+	gtk_list_store_insert (store, &iter, 0);
+
+	gtk_list_store_set (store,
+			    &iter,
+			    0,
+			    text,
+			    -1);
+}
+
+static void
+response_handler (GtkWidget *dialog,
+		  gint       response_id,
+		  gpointer   data)
+{
+  TerminalSearchDialogPrivate *priv;
+  const gchar *str;
+
+  if (response_id != GTK_RESPONSE_ACCEPT) {
+	  gtk_widget_hide (dialog);
+	  return;
+  }
+
+  priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog);
+
+  str = gtk_entry_get_text (GTK_ENTRY (priv->search_text_entry));
+  if (*str != '\0')
+    history_entry_insert (priv->store, str);
+}
+
+
+void
+terminal_search_dialog_set_search_text (GtkWidget   *dialog,
+					const gchar *text)
+{
+  TerminalSearchDialogPrivate *priv;
+
+  g_return_if_fail (GTK_IS_DIALOG (dialog));
+  g_return_if_fail (text != NULL);
+
+  priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog);
+  g_return_if_fail (priv);
+
+  gtk_entry_set_text (GTK_ENTRY (priv->search_text_entry), text);
+
+  gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+				     GTK_RESPONSE_ACCEPT,
+				     (*text != '\0'));
+}
+
+const gchar *
+terminal_search_dialog_get_search_text (GtkWidget *dialog)
+{
+  TerminalSearchDialogPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
+
+  priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog);
+  g_return_val_if_fail (priv, NULL);
+
+  return gtk_entry_get_text (GTK_ENTRY (priv->search_text_entry));
+}
+
+#define GET_FLAG(widget) gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->widget))
+
+TerminalSearchFlags
+terminal_search_dialog_get_search_flags (GtkWidget *dialog)
+{
+  TerminalSearchDialogPrivate *priv;
+  TerminalSearchFlags flags = 0;
+
+  g_return_val_if_fail (GTK_IS_DIALOG (dialog), flags);
+
+  priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog);
+  g_return_val_if_fail (priv, flags);
+
+  if (GET_FLAG (backwards_checkbutton))
+    flags |= TERMINAL_SEARCH_FLAG_BACKWARDS;
+
+  if (GET_FLAG (wrap_around_checkbutton))
+    flags |= TERMINAL_SEARCH_FLAG_WRAP_AROUND;
+
+  return flags;
+}
+
+GRegex *
+terminal_search_dialog_get_regex (GtkWidget *dialog)
+{
+  TerminalSearchDialogPrivate *priv;
+  GRegexCompileFlags compile_flags;
+  const char *text, *pattern;
+
+  g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
+
+  priv = TERMINAL_SEARCH_DIALOG_GET_PRIVATE (dialog);
+  g_return_val_if_fail (priv, NULL);
+
+  pattern = text = terminal_search_dialog_get_search_text (dialog);
+
+  compile_flags = G_REGEX_OPTIMIZE;
+
+  if (!GET_FLAG (match_case_checkbutton))
+    compile_flags |= G_REGEX_CASELESS;
+
+  if (GET_FLAG (regex_checkbutton))
+    compile_flags |= G_REGEX_MULTILINE;
+  else
+    pattern = g_regex_escape_string (text, -1);
+
+  if (GET_FLAG (entire_word_checkbutton)) {
+    const char *old_pattern = pattern;
+    pattern = g_strdup_printf ("\\b%s\\b", pattern);
+    if (old_pattern != text)
+      g_free ((char *) old_pattern);
+  }
+
+  if (!priv->regex || priv->regex_compile_flags != compile_flags) {
+    priv->regex_compile_flags = compile_flags;
+    if (priv->regex)
+      g_regex_unref (priv->regex);
+
+    /* TODO Error handling */
+    priv->regex = g_regex_new (pattern, compile_flags, G_REGEX_MATCH_NOTEMPTY, NULL);
+  }
+
+  if (pattern != text)
+    g_free ((char *) pattern);
+
+  return priv->regex;
+}
+
diff --git a/src/terminal-search-dialog.h b/src/terminal-search-dialog.h
new file mode 100644
index 0000000..ff226b6
--- /dev/null
+++ b/src/terminal-search-dialog.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2005 Paolo Maggi
+ * Copyright © 2010 Red Hat (Red Hat author: Behdad Esfahbod)
+ *
+ * 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.
+ */
+
+#ifndef TERMINAL_SEARCH_DIALOG_H
+#define TERMINAL_SEARCH_DIALOG_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum _TerminalSearchFlags {
+  TERMINAL_SEARCH_FLAG_BACKWARDS	= 1 << 0,
+  TERMINAL_SEARCH_FLAG_WRAP_AROUND	= 1 << 1
+} TerminalSearchFlags;
+
+
+GtkWidget	*terminal_search_dialog_new		(GtkWindow   *parent);
+
+void		 terminal_search_dialog_present		(GtkWidget   *dialog);
+
+void		 terminal_search_dialog_set_search_text (GtkWidget   *dialog,
+							 const gchar *text);
+
+const gchar 	*terminal_search_dialog_get_search_text	(GtkWidget   *dialog);
+
+TerminalSearchFlags
+		 terminal_search_dialog_get_search_flags(GtkWidget   *dialog);
+GRegex		*terminal_search_dialog_get_regex	(GtkWidget   *dialog);
+
+G_END_DECLS
+
+#endif /* TERMINAL_SEARCH_DIALOG_H */
diff --git a/src/terminal-window.c b/src/terminal-window.c
index c74f89d..3727b8d 100644
--- a/src/terminal-window.c
+++ b/src/terminal-window.c
@@ -34,7 +34,7 @@
 #include "terminal-encoding.h"
 #include "terminal-intl.h"
 #include "terminal-screen-container.h"
-#include "terminal-search.h"
+#include "terminal-search-dialog.h"
 #include "terminal-tab-label.h"
 #include "terminal-tabs-menu.h"
 #include "terminal-util.h"
@@ -68,6 +68,7 @@ struct _TerminalWindowPrivate
   void *old_geometry_widget; /* only used for pointer value as it may be freed */
 
   GtkWidget *confirm_close_dialog;
+  GtkWidget *search_find_dialog;
 
   guint menubar_visible : 1;
   guint use_default_menubar_visibility : 1;
@@ -135,6 +136,8 @@ static void file_new_window_callback          (GtkAction *action,
                                                TerminalWindow *window);
 static void file_new_tab_callback             (GtkAction *action,
                                                TerminalWindow *window);
+static void file_new_profile_callback         (GtkAction *action,
+                                               TerminalWindow *window);
 static void file_close_window_callback        (GtkAction *action,
                                                TerminalWindow *window);
 #if 0
@@ -155,10 +158,6 @@ static void edit_profiles_callback            (GtkAction *action,
                                                TerminalWindow *window);
 static void edit_current_profile_callback     (GtkAction *action,
                                                TerminalWindow *window);
-static void edit_find_callback                (GtkAction *action,
-                                               TerminalWindow *window);
-static void file_new_profile_callback         (GtkAction *action,
-                                               TerminalWindow *window);
 static void view_menubar_toggled_callback     (GtkToggleAction *action,
                                                TerminalWindow *window);
 static void view_fullscreen_toggled_callback  (GtkToggleAction *action,
@@ -169,6 +168,14 @@ static void view_zoom_out_callback            (GtkAction *action,
                                                TerminalWindow *window);
 static void view_zoom_normal_callback         (GtkAction *action,
                                                TerminalWindow *window);
+static void search_find_callback              (GtkAction *action,
+                                               TerminalWindow *window);
+static void search_find_next_callback         (GtkAction *action,
+                                               TerminalWindow *window);
+static void search_find_prev_callback         (GtkAction *action,
+                                               TerminalWindow *window);
+static void search_clear_highlight_callback   (GtkAction *action,
+                                               TerminalWindow *window);
 static void terminal_set_title_callback       (GtkAction *action,
                                                TerminalWindow *window);
 static void terminal_add_encoding_callback    (GtkAction *action,
@@ -944,6 +951,27 @@ terminal_window_update_zoom_sensitivity (TerminalWindow *window)
 }
 
 static void
+terminal_window_update_search_sensitivity (TerminalScreen *screen,
+                                           TerminalWindow *window)
+{
+  TerminalWindowPrivate *priv = window->priv;
+  GtkAction *action;
+  gboolean can_search;
+
+  if (screen != priv->active_screen)
+    return;
+
+  can_search = vte_terminal_search_get_gregex (VTE_TERMINAL (screen)) != NULL;
+
+  action = gtk_action_group_get_action (priv->action_group, "SearchFindNext");
+  gtk_action_set_sensitive (action, can_search);
+  action = gtk_action_group_get_action (priv->action_group, "SearchFindPrevious");
+  gtk_action_set_sensitive (action, can_search);
+  action = gtk_action_group_get_action (priv->action_group, "SearchClearHighlight");
+  gtk_action_set_sensitive (action, can_search);
+}
+
+static void
 update_edit_menu_cb (GtkClipboard *clipboard,
                      GdkAtom *targets,
                      int n_targets,
@@ -1714,6 +1742,7 @@ terminal_window_init (TerminalWindow *window)
       { "FileNewTabProfiles", STOCK_NEW_TAB, N_("Open Ta_b") },
       { "Edit", NULL, N_("_Edit") },
       { "View", NULL, N_("_View") },
+      { "Search", NULL, N_("_Search") },
       { "Terminal", NULL, N_("_Terminal") },
       { "Tabs", NULL, N_("Ta_bs") },
       { "Help", NULL, N_("_Help") },
@@ -1764,9 +1793,6 @@ terminal_window_init (TerminalWindow *window)
       { "EditCurrentProfile", NULL, N_("Pr_ofile Preferences"), NULL,
         NULL,
         G_CALLBACK (edit_current_profile_callback) },
-      { "EditFind", NULL, N_("_Findâ?¦"), "<shift><control>F",
-        NULL,
-        G_CALLBACK (edit_find_callback) },
 
       /* View menu */
       { "ViewZoomIn", GTK_STOCK_ZOOM_IN, NULL, "<control>plus",
@@ -1779,6 +1805,28 @@ terminal_window_init (TerminalWindow *window)
         NULL,
         G_CALLBACK (view_zoom_normal_callback) },
 
+      /* Search menu */
+      { "SearchFind", GTK_STOCK_FIND, N_("_Find..."), "<shift><control>F",
+	NULL,
+	G_CALLBACK (search_find_callback) },
+      { "SearchFindNext", NULL, N_("Find Ne_xt"), "<shift><control>H",
+	NULL,
+	G_CALLBACK (search_find_next_callback) },
+      { "SearchFindPrevious", NULL, N_("Find Pre_vious"), "<shift><control>G",
+	NULL,
+	G_CALLBACK (search_find_prev_callback) },
+      { "SearchClearHighlight", NULL, N_("_Clear Highlight"), "<shift><control>J",
+	NULL,
+	G_CALLBACK (search_clear_highlight_callback) },
+#if 0
+      { "SearchGoToLine", GTK_STOCK_JUMP_TO, N_("Go to _Line..."), "<shift><control>I",
+	NULL,
+	G_CALLBACK (search_goto_line_callback) },
+      { "SearchIncrementalSearch", GTK_STOCK_FIND, N_("_Incremental Search..."), "<shift><control>K",
+	NULL,
+	G_CALLBACK (search_incremental_search_callback) },
+#endif
+
       /* Terminal menu */
       { "TerminalProfiles", NULL, N_("Change _Profile") },
       { "TerminalSetTitle", NULL, N_("_Set Titleâ?¦"), NULL,
@@ -2110,6 +2158,10 @@ terminal_window_finalize (GObject *object)
     gtk_dialog_response (GTK_DIALOG (priv->confirm_close_dialog),
                          GTK_RESPONSE_DELETE_EVENT);
 
+  if (priv->search_find_dialog)
+    gtk_dialog_response (GTK_DIALOG (priv->search_find_dialog),
+                         GTK_RESPONSE_DELETE_EVENT);
+
   G_OBJECT_CLASS (terminal_window_parent_class)->finalize (object);
 }
 
@@ -2677,6 +2729,7 @@ notebook_page_selected_callback (GtkWidget       *notebook,
   terminal_window_update_set_profile_menu_active_profile (window);
   terminal_window_update_copy_sensitivity (screen, window);
   terminal_window_update_zoom_sensitivity (window);
+  terminal_window_update_search_sensitivity (screen, window);
 }
 
 static void
@@ -3310,13 +3363,6 @@ edit_current_profile_callback (GtkAction *action,
 }
 
 static void
-edit_find_callback (GtkAction *action,
-                    TerminalWindow *window)
-{
-  terminal_find_dialog_display (GTK_WINDOW (window));
-}
-
-static void
 file_new_profile_callback (GtkAction *action,
                            TerminalWindow *window)
 {
@@ -3462,6 +3508,139 @@ view_zoom_normal_callback (GtkAction *action,
   terminal_window_update_zoom_sensitivity (window);
 }
 
+
+static void
+search_find_response_callback (GtkWidget *dialog,
+			       int        response,
+			       gpointer   user_data)
+{
+  TerminalWindow *window = TERMINAL_WINDOW (user_data);
+  TerminalWindowPrivate *priv = window->priv;
+  TerminalSearchFlags flags;
+  GRegex *regex;
+  gboolean wrap_around;
+
+  if (response != GTK_RESPONSE_ACCEPT)
+    return;
+
+  if (G_UNLIKELY (!priv->active_screen))
+    return;
+
+  regex = terminal_search_dialog_get_regex (dialog);
+  if (G_UNLIKELY (!regex))
+    return; /* TODO error handling? */
+
+  flags = terminal_search_dialog_get_search_flags (dialog);
+  wrap_around = !!(flags & TERMINAL_SEARCH_FLAG_WRAP_AROUND);
+
+  vte_terminal_search_set_gregex (VTE_TERMINAL (priv->active_screen), regex);
+  if (flags & TERMINAL_SEARCH_FLAG_BACKWARDS)
+    vte_terminal_search_find_previous (VTE_TERMINAL (priv->active_screen), wrap_around);
+  else
+    vte_terminal_search_find_next (VTE_TERMINAL (priv->active_screen), wrap_around);
+
+  terminal_window_update_search_sensitivity (priv->active_screen, window);
+}
+
+static gboolean
+search_dialog_delete_event_cb (GtkWidget   *widget,
+			       GdkEventAny *event,
+			       gpointer     user_data)
+{
+	/* prevent destruction */
+	return TRUE;
+}
+
+static void
+search_find_callback (GtkAction *action,
+		      TerminalWindow *window)
+{
+  TerminalWindowPrivate *priv = window->priv;
+
+  if (!priv->search_find_dialog) {
+    GtkWidget *dialog;
+
+    dialog = priv->search_find_dialog = terminal_search_dialog_new (GTK_WINDOW (window));
+
+    g_signal_connect (dialog, "destroy",
+		      G_CALLBACK (gtk_widget_destroyed), &priv->search_find_dialog);
+    g_signal_connect (dialog, "response",
+		      G_CALLBACK (search_find_response_callback), window);
+    g_signal_connect (dialog, "delete-event",
+		     G_CALLBACK (search_dialog_delete_event_cb), NULL);
+  }
+
+  terminal_search_dialog_present (priv->search_find_dialog);
+}
+
+static void
+search_find_next_callback (GtkAction *action,
+			   TerminalWindow *window)
+{
+  TerminalWindowPrivate *priv = window->priv;
+  GtkWidget *dialog;
+  TerminalSearchFlags flags;
+  GRegex *regex;
+  gboolean wrap_around;
+
+  if (!priv->search_find_dialog)
+    return;
+  dialog = priv->search_find_dialog;
+
+  if (G_UNLIKELY (!priv->active_screen))
+    return;
+
+  regex = terminal_search_dialog_get_regex (dialog);
+  if (G_UNLIKELY (!regex))
+    return; /* TODO error handling? */
+
+  flags = terminal_search_dialog_get_search_flags (dialog);
+  wrap_around = !!(flags & TERMINAL_SEARCH_FLAG_WRAP_AROUND);
+
+  vte_terminal_search_set_gregex (VTE_TERMINAL (priv->active_screen), regex);
+  vte_terminal_search_find_next (VTE_TERMINAL (priv->active_screen), wrap_around);
+}
+
+static void
+search_find_prev_callback (GtkAction *action,
+			   TerminalWindow *window)
+{
+  TerminalWindowPrivate *priv = window->priv;
+  GtkWidget *dialog;
+  TerminalSearchFlags flags;
+  GRegex *regex;
+  gboolean wrap_around;
+
+  if (!priv->search_find_dialog)
+    return;
+  dialog = priv->search_find_dialog;
+
+  if (G_UNLIKELY (!priv->active_screen))
+    return;
+
+  regex = terminal_search_dialog_get_regex (dialog);
+  if (G_UNLIKELY (!regex))
+    return; /* TODO error handling? */
+
+  flags = terminal_search_dialog_get_search_flags (dialog);
+  wrap_around = !!(flags & TERMINAL_SEARCH_FLAG_WRAP_AROUND);
+
+  vte_terminal_search_set_gregex (VTE_TERMINAL (priv->active_screen), regex);
+  vte_terminal_search_find_previous (VTE_TERMINAL (priv->active_screen), wrap_around);
+}
+
+static void
+search_clear_highlight_callback (GtkAction *action,
+				 TerminalWindow *window)
+{
+  TerminalWindowPrivate *priv = window->priv;
+
+  if (G_UNLIKELY (!priv->active_screen))
+    return;
+
+  vte_terminal_search_set_gregex (VTE_TERMINAL (priv->active_screen), NULL);
+}
+
 static void
 terminal_set_title_dialog_response_cb (GtkWidget *dialog,
                                        int response,
diff --git a/src/terminal.xml b/src/terminal.xml
index 4dbd7b1..cf4b01a 100644
--- a/src/terminal.xml
+++ b/src/terminal.xml
@@ -19,8 +19,6 @@
       <separator />
       <menuitem action="EditSelectAll" />
       <separator />
-      <menuitem action="EditFind" />
-      <separator />
       <menuitem action="EditProfiles" />
       <menuitem action="EditKeybindings" />
       <menuitem action="EditCurrentProfile" />
@@ -33,6 +31,20 @@
       <menuitem action="ViewZoomOut" />
       <menuitem action="ViewZoom100" />
     </menu>
+    <menu action="Search">
+      <menuitem action="SearchFind"/>
+      <menuitem action="SearchFindNext"/>
+      <menuitem action="SearchFindPrevious"/>
+      <!--
+      <menuitem action="SearchIncrementalSearch"/>
+      -->
+      <separator/>
+      <menuitem name="SearchClearHighlight" action="SearchClearHighlight"/>
+      <!--
+      <separator/>
+      <menuitem name="SearchGoToLineMenu" action="SearchGoToLine"/>
+      -->
+    </menu>
     <menu action="Terminal">
       <menu action="TerminalProfiles" />
       <menuitem action="TerminalSetTitle" />



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