[gnome-terminal] window: Improve search
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-terminal] window: Improve search
- Date: Tue, 22 Sep 2015 18:11:18 +0000 (UTC)
commit 6ff2df44defa586ce8110b3d5d730d1e49c13b42
Author: Christian Persch <chpe gnome org>
Date: Tue Sep 22 14:30:54 2015 +0200
window: Improve search
Use a popover instead of a dialogue, and use a GtkSearchEntry.
Note: You will need to use the 'searchbar' branch of glade to edit the .ui file
until the branch is merged in glade.
configure.ac | 2 +-
src/Makefile.am | 6 +-
src/find-dialog.ui | 222 ------------------
src/search-popover.ui | 249 ++++++++++++++++++++
src/terminal-libgsystem.h | 10 +
src/terminal-search-dialog.c | 372 -----------------------------
src/terminal-search-dialog.h | 54 -----
src/terminal-search-popover.c | 515 +++++++++++++++++++++++++++++++++++++++++
src/terminal-search-popover.h | 48 ++++
src/terminal-window.c | 119 +++++++---
src/terminal.gresource.xml | 2 +-
11 files changed, 908 insertions(+), 691 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 5e45759..2da8c5d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -54,7 +54,7 @@ AC_MSG_RESULT([$with_gtk])
case "$with_gtk" in
3.0) GTK_API_VERSION=3.0
- GTK_REQUIRED=3.10.0
+ GTK_REQUIRED=3.12.0
GTK_MIN_REQUIRED=3.8
GTK_MAX_ALLOWED=3.14
VTE_API_VERSION=2.91
diff --git a/src/Makefile.am b/src/Makefile.am
index 0af81d7..16766d3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -67,8 +67,8 @@ gnome_terminal_server_SOURCES = \
terminal-screen.h \
terminal-screen-container.c \
terminal-screen-container.h \
- terminal-search-dialog.c \
- terminal-search-dialog.h \
+ terminal-search-popover.c \
+ terminal-search-popover.h \
terminal-tab-label.c \
terminal-tab-label.h \
terminal-tabs-menu.c \
@@ -381,7 +381,7 @@ EXTRA_DIST = \
terminal-type-builtins.h.template \
org.gnome.Terminal.xml \
nautilus.symbols \
- find-dialog.ui \
+ search-popover.ui \
preferences.ui \
profile-preferences.ui \
terminal.common.css \
diff --git a/src/search-popover.ui b/src/search-popover.ui
new file mode 100644
index 0000000..68908c7
--- /dev/null
+++ b/src/search-popover.ui
@@ -0,0 +1,249 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.19.0 -->
+<interface>
+ <requires lib="gtk+" version="3.16"/>
+ <template class="TerminalSearchPopover" parent="GtkPopover">
+ <property name="can_focus">False</property>
+ <property name="transitions_enabled">False</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkBox" id="box4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkSearchEntry" id="search_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ <property name="width_chars">30</property>
+ <property name="primary_icon_name">edit-find-symbolic</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">False</property>
+ <property name="placeholder_text" translatable="yes">Search</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="search_prev_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Search for previous
occurrence</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-up-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="search_next_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Search for next occurrence</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">go-down-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <style>
+ <class name="linked"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="reveal_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Toggle search options</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">view-context-menu-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ <accessibility>
+ <relation type="controller-for" target="revealer"/>
+ </accessibility>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="close_button">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="focus_on_click">False</property>
+ <child>
+ <object class="GtkImage" id="image4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">window-close-symbolic</property>
+ <property name="use_fallback">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkRevealer" id="revealer">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="transition_type">none</property>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">18</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="match_case_checkbutton">
+ <property name="label" translatable="yes">_Match case</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0.49000000953674316</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="entire_word_checkbutton">
+ <property name="label" translatable="yes">Match _entire word only</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="regex_checkbutton">
+ <property name="label" translatable="yes">Match as _regular expression</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="wrap_around_checkbutton">
+ <property name="label" translatable="yes">_Wrap around</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="focus_on_click">False</property>
+ <property name="xalign">0</property>
+ <property name="active">True</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/src/terminal-libgsystem.h b/src/terminal-libgsystem.h
index 3a91c7f..2d23c86 100644
--- a/src/terminal-libgsystem.h
+++ b/src/terminal-libgsystem.h
@@ -56,6 +56,7 @@ GS_DEFINE_CLEANUP_FUNCTION0(GHashTable*, gs_local_hashtable_unref, g_hash_table_
GS_DEFINE_CLEANUP_FUNCTION0(GList*, gs_local_list_free, g_list_free)
GS_DEFINE_CLEANUP_FUNCTION0(GObject*, gs_local_obj_unref, g_object_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GPtrArray*, gs_local_ptrarray_unref, g_ptr_array_unref)
+GS_DEFINE_CLEANUP_FUNCTION0(GRegex*, gs_local_regex_unref, g_regex_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GVariant*, gs_local_variant_unref, g_variant_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GVariantBuilder*, gs_local_variant_builder_unref, g_variant_builder_unref)
GS_DEFINE_CLEANUP_FUNCTION0(GVariantIter*, gs_local_variant_iter_free, g_variant_iter_free)
@@ -80,6 +81,15 @@ GS_DEFINE_CLEANUP_FUNCTION(void*, gs_local_free, g_free)
#define gs_unref_object __attribute__ ((cleanup(gs_local_obj_unref)))
/**
+ * gs_unref_regex:
+ *
+ * Call g_regex_unref() on a variable location when it goes out of
+ * scope. Note that unlike g_regex_unref(), the variable may be
+ * %NULL.
+ */
+#define gs_unref_regex __attribute__ ((cleanup(gs_local_regex_unref)))
+
+/**
* gs_unref_variant:
*
* Call g_variant_unref() on a variable location when it goes out of
diff --git a/src/terminal-search-popover.c b/src/terminal-search-popover.c
new file mode 100644
index 0000000..4f286ad
--- /dev/null
+++ b/src/terminal-search-popover.c
@@ -0,0 +1,515 @@
+/*
+ * Copyright © 2015 Christian Persch
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "terminal-search-popover.h"
+#include "terminal-intl.h"
+#include "terminal-window.h"
+#include "terminal-app.h"
+#include "terminal-libgsystem.h"
+
+typedef struct _TerminalSearchPopoverPrivate TerminalSearchPopoverPrivate;
+
+struct _TerminalSearchPopover
+{
+ GtkPopover parent_instance;
+};
+
+struct _TerminalSearchPopoverClass
+{
+ GtkPopoverClass parent_class;
+
+ /* Signals */
+ void (* search) (TerminalSearchPopover *popover,
+ gboolean backward);
+};
+
+struct _TerminalSearchPopoverPrivate
+{
+ GtkWidget *search_entry;
+ GtkWidget *search_prev_button;
+ GtkWidget *search_next_button;
+ GtkWidget *reveal_button;
+ GtkWidget *close_button;
+ GtkWidget *revealer;
+ GtkWidget *match_case_checkbutton;
+ GtkWidget *entire_word_checkbutton;
+ GtkWidget *regex_checkbutton;
+ GtkWidget *wrap_around_checkbutton;
+
+ gboolean search_text_changed;
+
+ /* Cached regex */
+ GRegex *regex;
+ GRegexCompileFlags regex_compile_flags;
+};
+
+enum {
+ PROP_0,
+ PROP_REGEX,
+ PROP_WRAP_AROUND,
+ LAST_PROP
+};
+
+enum {
+ SEARCH,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+static GParamSpec *pspecs[LAST_PROP];
+static GtkListStore *history_store;
+
+G_DEFINE_TYPE_WITH_PRIVATE (TerminalSearchPopover, terminal_search_popover, GTK_TYPE_POPOVER)
+
+#define PRIV(obj) ((TerminalSearchPopoverPrivate *) terminal_search_popover_get_instance_private
((TerminalSearchPopover *)(obj)))
+
+/* history */
+
+#define HISTORY_MIN_ITEM_LEN (3)
+#define HISTORY_LENGTH (10)
+
+static gboolean
+history_enabled (void)
+{
+ gboolean enabled;
+
+ /* not quite an exact setting for this, but close enough… */
+ g_object_get (gtk_settings_get_default (), "gtk-recent-files-enabled", &enabled, NULL);
+ if (!enabled)
+ return FALSE;
+
+ if (history_store == NULL) {
+ history_store = gtk_list_store_new (1, G_TYPE_STRING);
+ g_object_set_data_full (G_OBJECT (terminal_app_get ()), "search-history-store",
+ history_store, (GDestroyNotify) g_object_unref);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+history_remove_item (const char *text)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (history_store);
+ GtkTreeIter iter;
+
+ if (!gtk_tree_model_get_iter_first (model, &iter))
+ return FALSE;
+
+ do {
+ gs_free gchar *item_text;
+
+ gtk_tree_model_get (model, &iter, 0, &item_text, -1);
+
+ if (item_text != NULL && strcmp (item_text, text) == 0) {
+ gtk_list_store_remove (history_store, &iter);
+ return TRUE;
+ }
+ } while (gtk_tree_model_iter_next (model, &iter));
+
+ return FALSE;
+}
+
+static void
+history_clamp (int 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 (history_store), &iter, path))
+ while (1)
+ if (!gtk_list_store_remove (history_store, &iter))
+ break;
+
+ gtk_tree_path_free (path);
+}
+
+static void
+history_insert_item (const char *text)
+{
+ GtkTreeIter iter;
+
+ if (!history_enabled () || text == NULL)
+ return;
+
+ 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 (!history_remove_item (text))
+ history_clamp (HISTORY_LENGTH - 1);
+
+ gtk_list_store_insert_with_values (history_store, &iter, 0,
+ 0, text,
+ -1);
+}
+
+/* helper functions */
+
+static void
+update_sensitivity (TerminalSearchPopover *popover)
+{
+ TerminalSearchPopoverPrivate *priv = PRIV (popover);
+ gboolean can_search;
+
+ can_search = priv->regex != NULL;
+
+ gtk_widget_set_sensitive (priv->search_prev_button, can_search);
+ gtk_widget_set_sensitive (priv->search_next_button, can_search);
+}
+
+static void
+perform_search (TerminalSearchPopover *popover,
+ gboolean backward)
+{
+ TerminalSearchPopoverPrivate *priv = PRIV (popover);
+
+ if (priv->regex == NULL)
+ return;
+
+ /* Add to search history */
+ if (priv->search_text_changed) {
+ const char *search_text;
+
+ search_text = gtk_entry_get_text (GTK_ENTRY (priv->search_entry));
+ history_insert_item (search_text);
+
+ priv->search_text_changed = FALSE;
+ }
+
+ g_signal_emit (popover, signals[SEARCH], 0, backward);
+}
+
+static void
+previous_match_cb (GtkWidget *widget,
+ TerminalSearchPopover *popover)
+{
+ perform_search (popover, TRUE);
+}
+
+static void
+next_match_cb (GtkWidget *widget,
+ TerminalSearchPopover *popover)
+{
+ perform_search (popover, FALSE);
+}
+
+static void
+close_clicked_cb (GtkWidget *widget,
+ GtkWidget *popover)
+{
+ gtk_widget_hide (popover);
+}
+
+static void
+search_button_clicked_cb (GtkWidget *button,
+ TerminalSearchPopover *popover)
+{
+ TerminalSearchPopoverPrivate *priv = PRIV (popover);
+
+ perform_search (popover, button == priv->search_prev_button);
+}
+
+static void
+update_regex (TerminalSearchPopover *popover)
+{
+ TerminalSearchPopoverPrivate *priv = PRIV (popover);
+ GRegexCompileFlags compile_flags;
+ const char *search_text;
+ gs_free char *pattern;
+
+ search_text = gtk_entry_get_text (GTK_ENTRY (priv->search_entry));
+
+ compile_flags = G_REGEX_OPTIMIZE;
+
+ if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->match_case_checkbutton)))
+ compile_flags |= G_REGEX_CASELESS;
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->regex_checkbutton))) {
+ pattern = g_strdup (search_text);
+ compile_flags |= G_REGEX_MULTILINE;
+ } else {
+ pattern = g_regex_escape_string (search_text, -1);
+ }
+
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->entire_word_checkbutton))) {
+ char *new_pattern;
+ new_pattern = g_strdup_printf ("\\b%s\\b", pattern);
+ g_free (pattern);
+ pattern = new_pattern;
+ }
+
+ if (priv->regex != NULL &&
+ priv->regex_compile_flags == compile_flags &&
+ g_strcmp0 (pattern, g_regex_get_pattern (priv->regex)) == 0)
+ return;
+
+ priv->regex_compile_flags = compile_flags;
+ if (priv->regex)
+ g_regex_unref (priv->regex);
+
+ /* FIXME: if comping the regex fails, show the error message somewhere */
+ if (search_text[0] != '\0')
+ priv->regex = g_regex_new (pattern, compile_flags, 0, NULL);
+ else
+ priv->regex = NULL;
+
+ update_sensitivity (popover);
+
+ g_object_notify_by_pspec (G_OBJECT (popover), pspecs[PROP_REGEX]);
+}
+
+static void
+search_text_changed_cb (GtkToggleButton *button,
+ TerminalSearchPopover *popover)
+{
+ TerminalSearchPopoverPrivate *priv = PRIV (popover);
+
+ update_regex (popover);
+ priv->search_text_changed = TRUE;
+}
+
+static void
+search_parameters_changed_cb (GtkToggleButton *button,
+ TerminalSearchPopover *popover)
+{
+ update_regex (popover);
+}
+
+static void
+wrap_around_toggled_cb (GtkToggleButton *button,
+ TerminalSearchPopover *popover)
+{
+ g_object_notify_by_pspec (G_OBJECT (popover), pspecs[PROP_WRAP_AROUND]);
+}
+
+/* public functions */
+
+/* Class implementation */
+
+static void
+terminal_search_popover_init (TerminalSearchPopover *popover)
+{
+ TerminalSearchPopoverPrivate *priv = PRIV (popover);
+ GtkWidget *widget = GTK_WIDGET (popover);
+
+ gtk_widget_init_template (widget);
+
+ /* Make the search entry reasonably wide */
+ gtk_widget_set_size_request (priv->search_entry, 300, -1);
+
+ /* Add entry completion with history */
+#if 0
+ g_object_set (G_OBJECT (priv->search_entry),
+ "model", history_store,
+ "entry-text-column", 0,
+ NULL);
+#endif
+
+ if (history_enabled ()) {
+ gs_unref_object GtkEntryCompletion *completion;
+
+ completion = gtk_entry_completion_new ();
+ gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (history_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_entry), completion);
+ }
+
+#if GTK_CHECK_VERSION (3, 17, 2)
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gtk_popover_set_default_widget (GTK_POPOVER (popover), priv->search_next_button);
+ G_GNUC_END_IGNORE_DEPRECATIONS
+#endif
+
+ g_signal_connect (priv->search_entry, "previous-match", G_CALLBACK (previous_match_cb), popover);
+ g_signal_connect (priv->search_entry, "next-match", G_CALLBACK (next_match_cb), popover);
+
+ g_signal_connect (priv->search_prev_button, "clicked", G_CALLBACK (search_button_clicked_cb), popover);
+ g_signal_connect (priv->search_next_button, "clicked", G_CALLBACK (search_button_clicked_cb), popover);
+
+ g_signal_connect (priv->close_button, "clicked", G_CALLBACK (close_clicked_cb), popover);
+
+ g_object_bind_property (priv->reveal_button, "active",
+ priv->revealer, "reveal-child",
+ G_BINDING_DEFAULT);
+
+ priv->regex = NULL;
+ update_sensitivity (popover);
+
+ g_signal_connect (priv->search_entry, "search-changed", G_CALLBACK (search_text_changed_cb), popover);
+ g_signal_connect (priv->match_case_checkbutton, "toggled", G_CALLBACK (search_parameters_changed_cb),
popover);
+ g_signal_connect (priv->entire_word_checkbutton, "toggled", G_CALLBACK (search_parameters_changed_cb),
popover);
+ g_signal_connect (priv->regex_checkbutton, "toggled", G_CALLBACK (search_parameters_changed_cb), popover);
+
+ g_signal_connect (priv->wrap_around_checkbutton, "toggled", G_CALLBACK (wrap_around_toggled_cb), popover);
+}
+
+static void
+terminal_search_popover_finalize (GObject *object)
+{
+ TerminalSearchPopover *popover = TERMINAL_SEARCH_POPOVER (object);
+ TerminalSearchPopoverPrivate *priv = PRIV (popover);
+
+ if (priv->regex)
+ g_regex_unref(priv->regex);
+
+ G_OBJECT_CLASS (terminal_search_popover_parent_class)->finalize (object);
+}
+
+static void
+terminal_search_popover_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TerminalSearchPopover *popover = TERMINAL_SEARCH_POPOVER (object);
+
+ switch (prop_id) {
+ case PROP_REGEX:
+ g_value_set_boxed (value, terminal_search_popover_get_regex (popover));
+ break;
+ case PROP_WRAP_AROUND:
+ g_value_set_boolean (value, terminal_search_popover_get_wrap_around (popover));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+terminal_search_popover_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_REGEX:
+ case PROP_WRAP_AROUND:
+ /* not writable */
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+terminal_search_popover_class_init (TerminalSearchPopoverClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ gobject_class->finalize = terminal_search_popover_finalize;
+ gobject_class->get_property = terminal_search_popover_get_property;
+ gobject_class->set_property = terminal_search_popover_set_property;
+
+ signals[SEARCH] =
+ g_signal_new (I_("search"),
+ G_OBJECT_CLASS_TYPE (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (TerminalSearchPopoverClass, search),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+
+ pspecs[PROP_REGEX] =
+ g_param_spec_boxed ("regex", NULL, NULL,
+ G_TYPE_REGEX,
+ G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB);
+
+ pspecs[PROP_WRAP_AROUND] =
+ g_param_spec_boolean ("wrap-around", NULL, NULL,
+ FALSE,
+ G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK |
G_PARAM_STATIC_BLURB);
+
+ g_object_class_install_properties (gobject_class, G_N_ELEMENTS (pspecs), pspecs);
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/terminal/ui/search-popover.ui");
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover, search_entry);
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover, search_prev_button);
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover, search_next_button);
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover, reveal_button);
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover, close_button);
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover, revealer);
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover, match_case_checkbutton);
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover,
entire_word_checkbutton);
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover, regex_checkbutton);
+ gtk_widget_class_bind_template_child_private (widget_class, TerminalSearchPopover,
wrap_around_checkbutton);
+}
+
+/* public API */
+
+/**
+ * terminal_search_popover_new:
+ *
+ * Returns: a new #TerminalSearchPopover
+ */
+TerminalSearchPopover *
+terminal_search_popover_new (GtkWidget *relative_to_widget)
+{
+ return g_object_new (TERMINAL_TYPE_SEARCH_POPOVER,
+ "relative-to", relative_to_widget,
+ NULL);
+}
+
+/**
+ * terminal_search_popover_get_regex:
+ * @popover: a #TerminalSearchPopover
+ *
+ * Returns: (transfer none): the search regex, or %NULL
+ */
+GRegex *
+terminal_search_popover_get_regex (TerminalSearchPopover *popover)
+{
+ g_return_val_if_fail (TERMINAL_IS_SEARCH_POPOVER (popover), NULL);
+
+ return PRIV (popover)->regex;
+}
+
+/**
+ * terminal_search_popover_get_wrap_around:
+ * @popover: a #TerminalSearchPopover
+ *
+ * Returns: (transfer none): whether search should wrap around
+ */
+gboolean
+terminal_search_popover_get_wrap_around (TerminalSearchPopover *popover)
+{
+ g_return_val_if_fail (TERMINAL_IS_SEARCH_POPOVER (popover), FALSE);
+
+ return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (PRIV (popover)->wrap_around_checkbutton));
+}
diff --git a/src/terminal-search-popover.h b/src/terminal-search-popover.h
new file mode 100644
index 0000000..8998144
--- /dev/null
+++ b/src/terminal-search-popover.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2008 Christian Persch
+ *
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TERMINAL_SEARCH_POPOVER_H
+#define TERMINAL_SEARCH_POPOVER_H
+
+#include <gtk/gtk.h>
+
+#include "terminal-screen.h"
+
+G_BEGIN_DECLS
+
+#define TERMINAL_TYPE_SEARCH_POPOVER (terminal_search_popover_get_type ())
+#define TERMINAL_SEARCH_POPOVER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TERMINAL_TYPE_SEARCH_POPOVER,
TerminalSearchPopover))
+#define TERMINAL_SEARCH_POPOVER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TERMINAL_TYPE_SEARCH_POPOVER,
TerminalSearchPopoverClass))
+#define TERMINAL_IS_SEARCH_POPOVER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TERMINAL_TYPE_SEARCH_POPOVER))
+#define TERMINAL_IS_SEARCH_POPOVER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TERMINAL_TYPE_SEARCH_POPOVER))
+#define TERMINAL_SEARCH_POPOVER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TERMINAL_TYPE_SEARCH_POPOVER,
TerminalSearchPopoverClass))
+
+typedef struct _TerminalSearchPopover TerminalSearchPopover;
+typedef struct _TerminalSearchPopoverClass TerminalSearchPopoverClass;
+
+GType terminal_search_popover_get_type (void);
+
+TerminalSearchPopover *terminal_search_popover_new (GtkWidget *relative_to_widget);
+
+GRegex *terminal_search_popover_get_regex (TerminalSearchPopover *popover);
+
+gboolean terminal_search_popover_get_wrap_around (TerminalSearchPopover *popover);
+
+G_END_DECLS
+
+#endif /* !TERMINAL_SEARCH_POPOVER_H */
diff --git a/src/terminal-window.c b/src/terminal-window.c
index 9127966..f673e99 100644
--- a/src/terminal-window.c
+++ b/src/terminal-window.c
@@ -41,7 +41,7 @@
#include "terminal-notebook.h"
#include "terminal-schemas.h"
#include "terminal-screen-container.h"
-#include "terminal-search-dialog.h"
+#include "terminal-search-popover.h"
#include "terminal-tab-label.h"
#include "terminal-tabs-menu.h"
#include "terminal-util.h"
@@ -80,7 +80,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;
+ TerminalSearchPopover *search_popover;
guint menubar_visible : 1;
guint use_default_menubar_visibility : 1;
@@ -867,34 +867,83 @@ action_edit_profile_cb (GSimpleAction *action,
}
static void
-find_response_cb (GtkWidget *dialog,
- int response,
- gpointer user_data)
+search_popover_search_cb (TerminalSearchPopover *popover,
+ gboolean backward,
+ TerminalWindow *window)
+{
+ TerminalWindowPrivate *priv = window->priv;
+
+ if (G_UNLIKELY (priv->active_screen == NULL))
+ return;
+
+ if (backward)
+ vte_terminal_search_find_previous (VTE_TERMINAL (priv->active_screen));
+ else
+ vte_terminal_search_find_next (VTE_TERMINAL (priv->active_screen));
+}
+
+static void
+search_popover_notify_regex_cb (TerminalSearchPopover *popover,
+ GParamSpec *pspec G_GNUC_UNUSED,
+ TerminalWindow *window)
{
- TerminalWindow *window = TERMINAL_WINDOW (user_data);
TerminalWindowPrivate *priv = window->priv;
- TerminalSearchFlags flags;
GRegex *regex;
- if (response != GTK_RESPONSE_ACCEPT)
+ if (G_UNLIKELY (priv->active_screen == NULL))
return;
- if (G_UNLIKELY (!priv->active_screen))
+ regex = terminal_search_popover_get_regex (popover);
+ vte_terminal_search_set_gregex (VTE_TERMINAL (priv->active_screen), regex, 0);
+
+ terminal_window_update_search_sensitivity (priv->active_screen, window);
+}
+
+static void
+search_popover_notify_wrap_around_cb (TerminalSearchPopover *popover,
+ GParamSpec *pspec G_GNUC_UNUSED,
+ TerminalWindow *window)
+{
+ TerminalWindowPrivate *priv = window->priv;
+ gboolean wrap;
+
+ if (G_UNLIKELY (priv->active_screen == NULL))
return;
- regex = terminal_search_dialog_get_regex (dialog);
- flags = terminal_search_dialog_get_search_flags (dialog);
+ wrap = terminal_search_popover_get_wrap_around (popover);
+ vte_terminal_search_set_wrap_around (VTE_TERMINAL (priv->active_screen), wrap);
+}
- vte_terminal_search_set_gregex (VTE_TERMINAL (priv->active_screen), regex, 0);
- vte_terminal_search_set_wrap_around (VTE_TERMINAL (priv->active_screen),
- (flags & TERMINAL_SEARCH_FLAG_WRAP_AROUND));
+static void
+terminal_window_ensure_search_popover (TerminalWindow *window)
+{
+ TerminalWindowPrivate *priv = window->priv;
- if (flags & TERMINAL_SEARCH_FLAG_BACKWARDS)
- vte_terminal_search_find_previous (VTE_TERMINAL (priv->active_screen));
- else
- vte_terminal_search_find_next (VTE_TERMINAL (priv->active_screen));
+ if (G_UNLIKELY(priv->active_screen == NULL))
+ return;
- terminal_window_update_search_sensitivity (priv->active_screen, window);
+ if (priv->search_popover != NULL) {
+ search_popover_notify_regex_cb (priv->search_popover, NULL, window);
+ search_popover_notify_wrap_around_cb (priv->search_popover, NULL, window);
+
+ gtk_widget_show (GTK_WIDGET (priv->search_popover));
+ return;
+ }
+
+ if (priv->active_screen == NULL)
+ return;
+
+ priv->search_popover = terminal_search_popover_new (GTK_WIDGET (priv->menubar));
+
+ g_signal_connect (priv->search_popover, "search", G_CALLBACK (search_popover_search_cb), window);
+
+ search_popover_notify_regex_cb (priv->search_popover, NULL, window);
+ g_signal_connect (priv->search_popover, "notify::regex", G_CALLBACK (search_popover_notify_regex_cb),
window);
+
+ search_popover_notify_wrap_around_cb (priv->search_popover, NULL, window);
+ g_signal_connect (priv->search_popover, "notify::wrap-around", G_CALLBACK
(search_popover_notify_wrap_around_cb), window);
+
+ gtk_widget_show (GTK_WIDGET (priv->search_popover));
}
static void
@@ -912,20 +961,7 @@ action_find_cb (GSimpleAction *action,
g_variant_get (parameter, "&s", &mode);
if (g_str_equal (mode, "find")) {
- 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 (find_response_cb), window);
- /* prevent destruction */
- g_signal_connect (dialog, "delete-event", G_CALLBACK (gtk_true), NULL);
- }
-
- terminal_search_dialog_present (priv->search_find_dialog);
+ terminal_window_ensure_search_popover (window);
} else if (g_str_equal (mode, "next")) {
vte_terminal_search_find_next (VTE_TERMINAL (priv->active_screen));
} else if (g_str_equal (mode, "previous")) {
@@ -2813,6 +2849,8 @@ terminal_window_dispose (GObject *object)
GtkClipboard *clipboard;
GSList *list, *l;
+ priv->disposed = TRUE;
+
/* Deactivate open popup menus. This fixes a crash if the window is closed
* while the context menu is open.
*/
@@ -2824,7 +2862,13 @@ terminal_window_dispose (GObject *object)
remove_popup_info (window);
- priv->disposed = TRUE;
+ if (priv->search_popover != NULL)
+ {
+ g_signal_handlers_disconnect_matched (priv->search_popover, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, window);
+ gtk_widget_destroy (GTK_WIDGET (priv->search_popover));
+ priv->search_popover = NULL;
+ }
if (priv->tabs_menu)
{
@@ -2876,10 +2920,6 @@ 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_free (priv->uuid);
G_OBJECT_CLASS (terminal_window_parent_class)->finalize (object);
@@ -3298,6 +3338,9 @@ mdi_screen_switched_cb (TerminalMdiContainer *container,
if (screen == NULL || old_active_screen == screen)
return;
+ if (priv->search_popover != NULL)
+ gtk_widget_hide (GTK_WIDGET (priv->search_popover));
+
_terminal_debug_print (TERMINAL_DEBUG_MDI,
"[window %p] MDI: setting active tab to screen %p (old active screen %p)\n",
window, screen, priv->active_screen);
diff --git a/src/terminal.gresource.xml b/src/terminal.gresource.xml
index c54f9dd..6be1a1c 100644
--- a/src/terminal.gresource.xml
+++ b/src/terminal.gresource.xml
@@ -19,7 +19,7 @@
<gresource prefix="/org/gnome/terminal">
<file alias="ui/terminal.xml" compressed="true" preprocess="xml-stripblanks">terminal.xml</file>
<file alias="ui/terminal.about" compressed="true">terminal.about</file>
- <file alias="ui/find-dialog.ui" compressed="true" preprocess="xml-stripblanks">find-dialog.ui</file>
+ <file alias="ui/search-popover.ui" compressed="true"
preprocess="xml-stripblanks">search-popover.ui</file>
<file alias="ui/preferences.ui" compressed="true" preprocess="xml-stripblanks">preferences.ui</file>
<file alias="ui/profile-preferences.ui" compressed="true"
preprocess="xml-stripblanks">profile-preferences.ui</file>
<file alias="ui/window.ui" compressed="true" preprocess="xml-stripblanks">terminal-window.ui</file>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]