mirror of
https://gitlab.gnome.org/GNOME/evince
synced 2024-07-05 00:59:07 +00:00
ab687f7fa4
Avoid the fact that clicking on 'previous match' and 'next match' buttons steal keyboard focus from the main view. We already do this for other toolbar buttons.
684 lines
24 KiB
C
684 lines
24 KiB
C
/* ev-search-box.c
|
|
* this file is part of evince, a gnome document viewer
|
|
*
|
|
* Copyright (C) 2015 Igalia S.L.
|
|
*
|
|
* Evince 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.
|
|
*
|
|
* Evince 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "ev-search-box.h"
|
|
|
|
#include <glib/gi18n.h>
|
|
|
|
enum {
|
|
STARTED,
|
|
UPDATED,
|
|
FINISHED,
|
|
CLEARED,
|
|
|
|
NEXT,
|
|
PREVIOUS,
|
|
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_DOCUMENT_MODEL,
|
|
PROP_OPTIONS
|
|
};
|
|
|
|
typedef struct {
|
|
EvDocumentModel *model;
|
|
EvJob *job;
|
|
EvFindOptions options;
|
|
EvFindOptions supported_options;
|
|
|
|
GtkWidget *entry;
|
|
GtkWidget *next_button;
|
|
GtkWidget *prev_button;
|
|
|
|
guint pages_searched;
|
|
} EvSearchBoxPrivate;
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (EvSearchBox, ev_search_box, GTK_TYPE_BOX)
|
|
|
|
#define GET_PRIVATE(o) ev_search_box_get_instance_private(o)
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
#define FIND_PAGE_RATE_REFRESH 100
|
|
|
|
static void
|
|
ev_search_box_update_progress (EvSearchBox *box)
|
|
{
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
gdouble fraction;
|
|
|
|
fraction = priv->job ? MIN ((gdouble)priv->pages_searched / EV_JOB_FIND (priv->job)->n_pages, 1.) : 0.;
|
|
gtk_entry_set_progress_fraction (GTK_ENTRY (priv->entry), fraction);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_clear_job (EvSearchBox *box)
|
|
{
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
if (!priv->job)
|
|
return;
|
|
|
|
if (!ev_job_is_finished (priv->job))
|
|
ev_job_cancel (priv->job);
|
|
|
|
g_signal_handlers_disconnect_matched (priv->job, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, box);
|
|
g_clear_object (&priv->job);
|
|
}
|
|
|
|
static void
|
|
find_job_finished_cb (EvJobFind *job,
|
|
EvSearchBox *box)
|
|
{
|
|
g_signal_emit (box, signals[FINISHED], 0);
|
|
ev_search_box_clear_job (box);
|
|
ev_search_box_update_progress (box);
|
|
|
|
if (!ev_job_find_has_results (job)) {
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
gtk_style_context_add_class (gtk_widget_get_style_context (priv->entry),
|
|
"error");
|
|
|
|
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->entry),
|
|
GTK_ENTRY_ICON_PRIMARY,
|
|
"face-uncertain-symbolic");
|
|
if (priv->supported_options != EV_FIND_DEFAULT) {
|
|
gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->entry),
|
|
GTK_ENTRY_ICON_PRIMARY,
|
|
_("Not found, click to change search options"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* find_bar_check_refresh_rate:
|
|
*
|
|
* Check whether the current page should trigger an status update in the
|
|
* find bar given its document size and the rate page.
|
|
*
|
|
* For documents with less pages than page_rate, it will return TRUE for
|
|
* every page. For documents with more pages, it will return TRUE every
|
|
* ((total_pages / page rate) + 1).
|
|
*
|
|
* This slow down the update rate in the GUI, making the search more
|
|
* responsive.
|
|
*/
|
|
static inline gboolean
|
|
find_check_refresh_rate (EvJobFind *job,
|
|
gint page_rate)
|
|
{
|
|
/* Always update if this is the last page of the search */
|
|
if ((job->current_page + 1) % job->n_pages == job->start_page)
|
|
return TRUE;
|
|
|
|
return ((job->current_page % (gint)((job->n_pages / page_rate) + 1)) == 0);
|
|
}
|
|
|
|
static void
|
|
find_job_updated_cb (EvJobFind *job,
|
|
gint page,
|
|
EvSearchBox *box)
|
|
{
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
priv->pages_searched++;
|
|
|
|
/* Adjust the status update when searching for a term according
|
|
* to the document size in pages. For documents smaller (or equal)
|
|
* than 100 pages, it will be updated in every page. A value of
|
|
* 100 is enough to update the find bar every 1%.
|
|
*/
|
|
if (find_check_refresh_rate (job, FIND_PAGE_RATE_REFRESH)) {
|
|
gboolean has_results = ev_job_find_has_results (job);
|
|
|
|
ev_search_box_update_progress (box);
|
|
gtk_widget_set_sensitive (priv->next_button, has_results);
|
|
gtk_widget_set_sensitive (priv->prev_button, has_results);
|
|
g_signal_emit (box, signals[UPDATED], 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
search_changed_cb (GtkSearchEntry *entry,
|
|
EvSearchBox *box)
|
|
{
|
|
const char *search_string;
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
ev_search_box_clear_job (box);
|
|
priv->pages_searched = 0;
|
|
ev_search_box_update_progress (box);
|
|
|
|
gtk_widget_set_sensitive (priv->next_button, FALSE);
|
|
gtk_widget_set_sensitive (priv->prev_button, FALSE);
|
|
|
|
gtk_style_context_remove_class (gtk_widget_get_style_context (priv->entry), GTK_STYLE_CLASS_ERROR);
|
|
gtk_entry_set_icon_from_icon_name (GTK_ENTRY (priv->entry),
|
|
GTK_ENTRY_ICON_PRIMARY,
|
|
"edit-find-symbolic");
|
|
if (priv->supported_options != EV_FIND_DEFAULT) {
|
|
gtk_entry_set_icon_tooltip_text (GTK_ENTRY (priv->entry),
|
|
GTK_ENTRY_ICON_PRIMARY,
|
|
_("Search options"));
|
|
}
|
|
|
|
search_string = gtk_entry_get_text (GTK_ENTRY (entry));
|
|
if (search_string && search_string[0]) {
|
|
EvDocument *doc = ev_document_model_get_document (priv->model);
|
|
|
|
priv->job = ev_job_find_new (doc,
|
|
ev_document_model_get_page (priv->model),
|
|
ev_document_get_n_pages (doc),
|
|
search_string,
|
|
FALSE);
|
|
ev_job_find_set_options (EV_JOB_FIND (priv->job), priv->options);
|
|
g_signal_connect (priv->job, "finished",
|
|
G_CALLBACK (find_job_finished_cb),
|
|
box);
|
|
g_signal_connect (priv->job, "updated",
|
|
G_CALLBACK (find_job_updated_cb),
|
|
box);
|
|
|
|
g_signal_emit (box, signals[STARTED], 0, priv->job);
|
|
ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
|
|
} else {
|
|
g_signal_emit (box, signals[CLEARED], 0);
|
|
}
|
|
}
|
|
|
|
static void
|
|
previous_clicked_cb (GtkButton *button,
|
|
EvSearchBox *box)
|
|
{
|
|
g_signal_emit (box, signals[PREVIOUS], 0);
|
|
}
|
|
|
|
static void
|
|
next_clicked_cb (GtkButton *button,
|
|
EvSearchBox *box)
|
|
{
|
|
g_signal_emit (box, signals[NEXT], 0);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_set_supported_options (EvSearchBox *box,
|
|
EvFindOptions options)
|
|
{
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
gboolean enable_search_options;
|
|
|
|
if (priv->supported_options == options)
|
|
return;
|
|
|
|
priv->supported_options = options;
|
|
enable_search_options = options != EV_FIND_DEFAULT;
|
|
g_object_set (priv->entry,
|
|
"primary-icon-activatable", enable_search_options,
|
|
"primary-icon-sensitive", enable_search_options,
|
|
"primary-icon-tooltip-text", enable_search_options ? _("Search options") : NULL,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_setup_document (EvSearchBox *box,
|
|
EvDocument *document)
|
|
{
|
|
if (!document || !EV_IS_DOCUMENT_FIND (document)) {
|
|
ev_search_box_set_supported_options (box, EV_FIND_DEFAULT);
|
|
gtk_widget_set_sensitive (GTK_WIDGET (box), FALSE);
|
|
return;
|
|
}
|
|
|
|
ev_search_box_set_supported_options (box, ev_document_find_get_supported_options (EV_DOCUMENT_FIND (document)));
|
|
gtk_widget_set_sensitive (GTK_WIDGET (box), ev_document_get_n_pages (document) > 0);
|
|
}
|
|
|
|
static void
|
|
document_changed_cb (EvDocumentModel *model,
|
|
GParamSpec *pspec,
|
|
EvSearchBox *box)
|
|
{
|
|
ev_search_box_setup_document (box, ev_document_model_get_document (model));
|
|
}
|
|
|
|
static void
|
|
ev_search_box_set_options (EvSearchBox *box,
|
|
EvFindOptions options)
|
|
{
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
if (priv->options == options)
|
|
return;
|
|
|
|
priv->options = options;
|
|
search_changed_cb (GTK_SEARCH_ENTRY (priv->entry), box);
|
|
}
|
|
|
|
static void
|
|
whole_words_only_toggled_cb (GtkCheckMenuItem *menu_item,
|
|
EvSearchBox *box)
|
|
{
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
EvFindOptions options = priv->options;
|
|
|
|
if (gtk_check_menu_item_get_active (menu_item))
|
|
options |= EV_FIND_WHOLE_WORDS_ONLY;
|
|
else
|
|
options &= ~EV_FIND_WHOLE_WORDS_ONLY;
|
|
ev_search_box_set_options (box, options);
|
|
}
|
|
|
|
static void
|
|
case_sensitive_toggled_cb (GtkCheckMenuItem *menu_item,
|
|
EvSearchBox *box)
|
|
{
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
EvFindOptions options = priv->options;
|
|
|
|
if (gtk_check_menu_item_get_active (menu_item))
|
|
options |= EV_FIND_CASE_SENSITIVE;
|
|
else
|
|
options &= ~EV_FIND_CASE_SENSITIVE;
|
|
ev_search_box_set_options (box, options);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_entry_populate_popup (EvSearchBox *box,
|
|
GtkWidget *menu)
|
|
{
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
if (priv->supported_options & EV_FIND_WHOLE_WORDS_ONLY) {
|
|
GtkWidget *menu_item;
|
|
|
|
menu_item = gtk_check_menu_item_new_with_mnemonic (_("_Whole Words Only"));
|
|
g_signal_connect (menu_item, "toggled",
|
|
G_CALLBACK (whole_words_only_toggled_cb),
|
|
box);
|
|
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
|
|
priv->options & EV_FIND_WHOLE_WORDS_ONLY);
|
|
gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
|
|
gtk_widget_show (menu_item);
|
|
}
|
|
|
|
if (priv->supported_options & EV_FIND_CASE_SENSITIVE) {
|
|
GtkWidget *menu_item;
|
|
|
|
menu_item = gtk_check_menu_item_new_with_mnemonic (_("C_ase Sensitive"));
|
|
g_signal_connect (menu_item, "toggled",
|
|
G_CALLBACK (case_sensitive_toggled_cb),
|
|
box);
|
|
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
|
|
priv->options & EV_FIND_CASE_SENSITIVE);
|
|
gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
|
|
gtk_widget_show (menu_item);
|
|
}
|
|
}
|
|
|
|
static void
|
|
entry_icon_release_cb (GtkEntry *entry,
|
|
GtkEntryIconPosition icon_pos,
|
|
GdkEventButton *event,
|
|
EvSearchBox *box)
|
|
{
|
|
GtkWidget *menu;
|
|
|
|
if (event->button != GDK_BUTTON_PRIMARY)
|
|
return;
|
|
|
|
if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
|
|
return;
|
|
|
|
menu = gtk_menu_new ();
|
|
ev_search_box_entry_populate_popup (box, menu);
|
|
gtk_widget_show (menu);
|
|
|
|
gtk_menu_popup_at_widget (GTK_MENU (menu), GTK_WIDGET (entry),
|
|
GDK_GRAVITY_SOUTH_WEST,
|
|
GDK_GRAVITY_NORTH_WEST,
|
|
(GdkEvent *)event);
|
|
}
|
|
|
|
static void
|
|
entry_populate_popup_cb (GtkEntry *entry,
|
|
GtkMenu *menu,
|
|
EvSearchBox *box)
|
|
{
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
GtkWidget *separator;
|
|
|
|
if (priv->supported_options == EV_FIND_DEFAULT)
|
|
return;
|
|
|
|
separator = gtk_separator_menu_item_new ();
|
|
gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), separator);
|
|
gtk_widget_show (separator);
|
|
ev_search_box_entry_populate_popup (box, GTK_WIDGET (menu));
|
|
}
|
|
|
|
static void
|
|
entry_activate_cb (GtkEntry *entry,
|
|
EvSearchBox *box)
|
|
{
|
|
g_signal_emit (box, signals[NEXT], 0);
|
|
}
|
|
|
|
static void
|
|
entry_next_match_cb (GtkSearchEntry *entry,
|
|
EvSearchBox *box)
|
|
{
|
|
g_signal_emit (box, signals[NEXT], 0);
|
|
}
|
|
|
|
static void
|
|
entry_previous_match_cb (GtkSearchEntry *entry,
|
|
EvSearchBox *box)
|
|
{
|
|
g_signal_emit (box, signals[PREVIOUS], 0);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_finalize (GObject *object)
|
|
{
|
|
EvSearchBox *box = EV_SEARCH_BOX (object);
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
if (priv->model) {
|
|
g_object_remove_weak_pointer (G_OBJECT (priv->model),
|
|
(gpointer)&priv->model);
|
|
}
|
|
|
|
G_OBJECT_CLASS (ev_search_box_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_dispose (GObject *object)
|
|
{
|
|
EvSearchBox *box = EV_SEARCH_BOX (object);
|
|
|
|
ev_search_box_clear_job (box);
|
|
|
|
G_OBJECT_CLASS (ev_search_box_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
EvSearchBox *box = EV_SEARCH_BOX (object);
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
switch (prop_id) {
|
|
case PROP_DOCUMENT_MODEL:
|
|
priv->model = g_value_get_object (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ev_search_box_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
EvSearchBox *box = EV_SEARCH_BOX (object);
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
switch (prop_id) {
|
|
case PROP_OPTIONS:
|
|
g_value_set_flags (value, priv->options);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
}
|
|
}
|
|
|
|
static void
|
|
ev_search_box_constructed (GObject *object)
|
|
{
|
|
EvSearchBox *box = EV_SEARCH_BOX (object);
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
G_OBJECT_CLASS (ev_search_box_parent_class)->constructed (object);
|
|
|
|
g_object_add_weak_pointer (G_OBJECT (priv->model),
|
|
(gpointer)&priv->model);
|
|
|
|
ev_search_box_setup_document (box, ev_document_model_get_document (priv->model));
|
|
g_signal_connect_object (priv->model, "notify::document",
|
|
G_CALLBACK (document_changed_cb),
|
|
box, 0);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_grab_focus (GtkWidget *widget)
|
|
{
|
|
EvSearchBox *box = EV_SEARCH_BOX (widget);
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
gtk_widget_grab_focus (priv->entry);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_class_init (EvSearchBoxClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
GtkBindingSet *binding_set;
|
|
|
|
object_class->finalize = ev_search_box_finalize;
|
|
object_class->dispose = ev_search_box_dispose;
|
|
object_class->constructed = ev_search_box_constructed;
|
|
object_class->set_property = ev_search_box_set_property;
|
|
object_class->get_property = ev_search_box_get_property;
|
|
|
|
widget_class->grab_focus = ev_search_box_grab_focus;
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_DOCUMENT_MODEL,
|
|
g_param_spec_object ("document-model",
|
|
"DocumentModel",
|
|
"The document model",
|
|
EV_TYPE_DOCUMENT_MODEL,
|
|
G_PARAM_WRITABLE |
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (object_class,
|
|
PROP_OPTIONS,
|
|
g_param_spec_flags ("options",
|
|
"Search options",
|
|
"The search options",
|
|
EV_TYPE_FIND_OPTIONS,
|
|
EV_FIND_DEFAULT,
|
|
G_PARAM_READABLE |
|
|
G_PARAM_STATIC_STRINGS));
|
|
|
|
signals[STARTED] =
|
|
g_signal_new ("started",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__OBJECT,
|
|
G_TYPE_NONE, 1,
|
|
EV_TYPE_JOB_FIND);
|
|
|
|
signals[UPDATED] =
|
|
g_signal_new ("updated",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__INT,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_INT);
|
|
signals[FINISHED] =
|
|
g_signal_new ("finished",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
signals[CLEARED] =
|
|
g_signal_new ("cleared",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
signals[NEXT] =
|
|
g_signal_new ("next",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
signals[PREVIOUS] =
|
|
g_signal_new ("previous",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
|
0, NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
binding_set = gtk_binding_set_by_class (klass);
|
|
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Return, GDK_SHIFT_MASK,
|
|
"previous", 0);
|
|
gtk_binding_entry_add_signal (binding_set, GDK_KEY_ISO_Enter, GDK_SHIFT_MASK,
|
|
"previous", 0);
|
|
gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Enter, GDK_SHIFT_MASK,
|
|
"previous", 0);
|
|
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK,
|
|
"previous", 0);
|
|
gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
|
|
"next", 0);
|
|
}
|
|
|
|
static void
|
|
ev_search_box_init (EvSearchBox *box)
|
|
{
|
|
GtkStyleContext *style_context;
|
|
EvSearchBoxPrivate *priv = GET_PRIVATE (box);
|
|
|
|
gtk_orientable_set_orientation (GTK_ORIENTABLE (box), GTK_ORIENTATION_HORIZONTAL);
|
|
style_context = gtk_widget_get_style_context (GTK_WIDGET (box));
|
|
gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_LINKED);
|
|
gtk_style_context_add_class (style_context, GTK_STYLE_CLASS_RAISED);
|
|
|
|
priv->entry = gtk_search_entry_new ();
|
|
|
|
gtk_box_pack_start (GTK_BOX (box), priv->entry, TRUE, TRUE, 0);
|
|
gtk_widget_show (priv->entry);
|
|
|
|
priv->prev_button = gtk_button_new_from_icon_name ("go-up-symbolic", GTK_ICON_SIZE_MENU);
|
|
gtk_widget_set_tooltip_text (priv->prev_button, _("Find previous occurrence of the search string"));
|
|
gtk_widget_set_sensitive (priv->prev_button, FALSE);
|
|
gtk_widget_set_focus_on_click (priv->prev_button, FALSE);
|
|
gtk_container_add (GTK_CONTAINER (box), priv->prev_button);
|
|
gtk_widget_show (priv->prev_button);
|
|
|
|
priv->next_button = gtk_button_new_from_icon_name ("go-down-symbolic", GTK_ICON_SIZE_MENU);
|
|
gtk_widget_set_tooltip_text (priv->next_button, _("Find next occurrence of the search string"));
|
|
gtk_widget_set_sensitive (priv->next_button, FALSE);
|
|
gtk_widget_set_focus_on_click (priv->next_button, FALSE);
|
|
gtk_container_add (GTK_CONTAINER (box), priv->next_button);
|
|
gtk_widget_show (priv->next_button);
|
|
|
|
g_signal_connect (priv->entry, "search-changed",
|
|
G_CALLBACK (search_changed_cb),
|
|
box);
|
|
g_signal_connect (priv->entry, "icon-release",
|
|
G_CALLBACK (entry_icon_release_cb),
|
|
box);
|
|
g_signal_connect (priv->entry, "populate-popup",
|
|
G_CALLBACK (entry_populate_popup_cb),
|
|
box);
|
|
g_signal_connect (priv->entry, "activate",
|
|
G_CALLBACK (entry_activate_cb),
|
|
box);
|
|
g_signal_connect (priv->entry, "next-match",
|
|
G_CALLBACK (entry_next_match_cb),
|
|
box);
|
|
g_signal_connect (priv->entry, "previous-match",
|
|
G_CALLBACK (entry_previous_match_cb),
|
|
box);
|
|
g_signal_connect (priv->prev_button, "clicked",
|
|
G_CALLBACK (previous_clicked_cb),
|
|
box);
|
|
g_signal_connect (priv->next_button, "clicked",
|
|
G_CALLBACK (next_clicked_cb),
|
|
box);
|
|
}
|
|
|
|
GtkWidget *
|
|
ev_search_box_new (EvDocumentModel *model)
|
|
{
|
|
g_return_val_if_fail (EV_IS_DOCUMENT_MODEL (model), NULL);
|
|
|
|
return GTK_WIDGET (g_object_new (EV_TYPE_SEARCH_BOX,
|
|
"document-model", model,
|
|
NULL));
|
|
}
|
|
|
|
GtkSearchEntry *
|
|
ev_search_box_get_entry (EvSearchBox *box)
|
|
{
|
|
EvSearchBoxPrivate *priv;
|
|
|
|
g_return_val_if_fail (EV_IS_SEARCH_BOX (box), NULL);
|
|
|
|
priv = GET_PRIVATE (box);
|
|
|
|
return GTK_SEARCH_ENTRY (priv->entry);
|
|
}
|
|
|
|
gboolean
|
|
ev_search_box_has_results (EvSearchBox *box)
|
|
{
|
|
EvSearchBoxPrivate *priv;
|
|
|
|
g_return_val_if_fail (EV_IS_SEARCH_BOX (box), FALSE);
|
|
|
|
priv = GET_PRIVATE (box);
|
|
|
|
return gtk_widget_get_sensitive (priv->next_button);
|
|
}
|
|
|
|
void
|
|
ev_search_box_restart (EvSearchBox *box)
|
|
{
|
|
EvSearchBoxPrivate *priv;
|
|
|
|
g_return_if_fail (EV_IS_SEARCH_BOX (box));
|
|
|
|
priv = GET_PRIVATE (box);
|
|
|
|
search_changed_cb (GTK_SEARCH_ENTRY (priv->entry), box);
|
|
}
|