[gnome-terminal/search] Bug 78963 - Ability to find/search text in buffer
- From: Behdad Esfahbod <behdad src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-terminal/search] Bug 78963 - Ability to find/search text in buffer
- Date: Tue, 27 Apr 2010 07:54:51 +0000 (UTC)
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]