Compare commits

...
Sign in to create a new pull request.
Author SHA1 Message Date
mbkma
0376d06670 Improve search system
shell: Add EvFindSidebar widget to show search results
see 940ce8048e

eggfindbar: don't close the findbar when it loses the focus
see a2d781fb12

shell: Add the findbar to the main box instead of the view box

libview: Add ev_view_find_set_result
2021-06-06 10:10:41 +02:00
7 changed files with 919 additions and 113 deletions

View file

@ -7509,6 +7509,14 @@ ev_view_find_previous (EvView *view)
}
}
void
ev_view_find_set_result (EvView *view, gint page, gint result)
{
ev_document_model_set_page (view->model, page);
view->find_result = result;
jump_to_find_result (view);
}
void
ev_view_find_search_changed (EvView *view)
{

View file

@ -72,6 +72,9 @@ void ev_view_zoom (EvView *view,
/* Find */
void ev_view_find_next (EvView *view);
void ev_view_find_previous (EvView *view);
void ev_view_find_set_result (EvView *view,
gint page,
gint result);
void ev_view_find_search_changed (EvView *view);
void ev_view_find_set_highlight_search (EvView *view,
gboolean value);

View file

@ -36,6 +36,8 @@ atril_SOURCES= \
ev-bookmark-action.c \
ev-application.c \
ev-application.h \
ev-find-sidebar.h \
ev-find-sidebar.c \
ev-file-monitor.h \
ev-file-monitor.c \
ev-history.c \

View file

@ -39,7 +39,6 @@ struct _EggFindBarPrivate
GtkWidget *find_entry;
GtkWidget *status_label;
gulong set_focus_handler;
guint case_sensitive : 1;
};
@ -91,9 +90,6 @@ egg_find_bar_class_init (EggFindBarClass *klass)
object_class->finalize = egg_find_bar_finalize;
widget_class->show = egg_find_bar_show;
widget_class->hide = egg_find_bar_hide;
widget_class->grab_focus = egg_find_bar_grab_focus;
find_bar_signals[NEXT] =
@ -256,27 +252,6 @@ entry_changed_callback (GtkEntry *entry,
g_free (text);
}
static void
set_focus_cb (GtkWidget *window,
GtkWidget *widget,
EggFindBar *bar)
{
GtkWidget *wbar = GTK_WIDGET (bar);
while (widget != NULL && widget != wbar)
{
widget = gtk_widget_get_parent (widget);
}
/* if widget == bar, the new focus widget is in the bar, so we
* don't deactivate.
*/
if (widget != wbar)
{
g_signal_emit (bar, find_bar_signals[CLOSE], 0);
}
}
static void
egg_find_bar_init (EggFindBar *find_bar)
{
@ -443,46 +418,6 @@ egg_find_bar_get_property (GObject *object,
}
}
static void
egg_find_bar_show (GtkWidget *widget)
{
EggFindBar *bar = EGG_FIND_BAR (widget);
EggFindBarPrivate *priv = bar->priv;
GTK_WIDGET_CLASS (egg_find_bar_parent_class)->show (widget);
if (priv->set_focus_handler == 0)
{
GtkWidget *toplevel;
toplevel = gtk_widget_get_toplevel (widget);
priv->set_focus_handler =
g_signal_connect (toplevel, "set-focus",
G_CALLBACK (set_focus_cb), bar);
}
}
static void
egg_find_bar_hide (GtkWidget *widget)
{
EggFindBar *bar = EGG_FIND_BAR (widget);
EggFindBarPrivate *priv = bar->priv;
if (priv->set_focus_handler != 0)
{
GtkWidget *toplevel;
toplevel = gtk_widget_get_toplevel (widget);
g_signal_handlers_disconnect_by_func
(toplevel, (void (*)) G_CALLBACK (set_focus_cb), bar);
priv->set_focus_handler = 0;
}
GTK_WIDGET_CLASS (egg_find_bar_parent_class)->hide (widget);
}
void
egg_find_bar_grab_focus (GtkWidget *widget)
{

681
shell/ev-find-sidebar.c Normal file
View file

@ -0,0 +1,681 @@
/* ev-find-sidebar.c
* this file is part of evince, a gnome document viewer
*
* Copyright (C) 2013 Carlos Garcia Campos <carlosgc@gnome.org>
* Copyright (C) 2008 Sergey Pushkin <pushkinsv@gmail.com >
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ev-find-sidebar.h"
#include "ev-document-text.h"
#include <string.h>
typedef struct {
GtkWidget *tree_view;
guint selection_id;
guint process_matches_idle_id;
GtkTreePath *highlighted_result;
gint first_match_page;
EvJobFind *job;
gint job_current_page;
gint current_page;
gint insert_position;
} EvFindSidebarPrivate;
enum {
TEXT_COLUMN,
PAGE_LABEL_COLUMN,
PAGE_COLUMN,
RESULT_COLUMN,
N_COLUMNS
};
enum {
RESULT_ACTIVATED,
N_SIGNALS
};
static guint signals[N_SIGNALS];
G_DEFINE_TYPE_WITH_PRIVATE (EvFindSidebar, ev_find_sidebar, GTK_TYPE_BOX)
#define GET_PRIVATE(o) ev_find_sidebar_get_instance_private (o)
static void
ev_find_sidebar_cancel (EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
if (priv->process_matches_idle_id > 0) {
g_source_remove (priv->process_matches_idle_id);
priv->process_matches_idle_id = 0;
}
g_clear_object (&priv->job);
}
static void
ev_find_sidebar_dispose (GObject *object)
{
EvFindSidebar *sidebar = EV_FIND_SIDEBAR (object);
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
ev_find_sidebar_cancel (sidebar);
g_clear_pointer (&(priv->highlighted_result), gtk_tree_path_free);
G_OBJECT_CLASS (ev_find_sidebar_parent_class)->dispose (object);
}
static void
ev_find_sidebar_class_init (EvFindSidebarClass *find_sidebar_class)
{
GObjectClass *g_object_class = G_OBJECT_CLASS (find_sidebar_class);
g_object_class->dispose = ev_find_sidebar_dispose;
signals[RESULT_ACTIVATED] =
g_signal_new ("result-activated",
G_TYPE_FROM_CLASS (g_object_class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE, 2,
G_TYPE_INT,
G_TYPE_INT);
}
static void
ev_find_sidebar_activate_result_at_iter (EvFindSidebar *sidebar,
GtkTreeModel *model,
GtkTreeIter *iter)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
gint page;
gint result;
if (priv->highlighted_result)
gtk_tree_path_free (priv->highlighted_result);
priv->highlighted_result = gtk_tree_model_get_path (model, iter);
gtk_tree_model_get (model, iter,
PAGE_COLUMN, &page,
RESULT_COLUMN, &result,
-1);
g_signal_emit (sidebar, signals[RESULT_ACTIVATED], 0, page - 1, result);
}
static void
selection_changed_callback (GtkTreeSelection *selection,
EvFindSidebar *sidebar)
{
GtkTreeModel *model;
GtkTreeIter iter;
if (gtk_tree_selection_get_selected (selection, &model, &iter))
ev_find_sidebar_activate_result_at_iter (sidebar, model, &iter);
}
static gboolean
sidebar_tree_button_press_cb (GtkTreeView *view,
GdkEventButton *event,
EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
GtkTreeModel *model;
GtkTreePath *path;
GtkTreeIter iter;
gtk_tree_view_get_path_at_pos (view, event->x, event->y, &path,
NULL, NULL, NULL);
if (!path)
return FALSE;
if (priv->highlighted_result &&
gtk_tree_path_compare (priv->highlighted_result, path) != 0) {
gtk_tree_path_free (path);
return FALSE;
}
model = gtk_tree_view_get_model (view);
gtk_tree_model_get_iter (model, &iter, path);
gtk_tree_path_free (path);
ev_find_sidebar_activate_result_at_iter (sidebar, model, &iter);
/* Always return FALSE so the tree view gets the event and can update
* the selection etc.
*/
return FALSE;
}
static void
ev_find_sidebar_reset_model (EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
GtkListStore *model;
model = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view),
GTK_TREE_MODEL (model));
g_object_unref (model);
}
static void
ev_find_sidebar_init (EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv;
GtkWidget *swindow;
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GtkTreeSelection *selection;
priv = GET_PRIVATE (sidebar);
swindow = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
priv->tree_view = gtk_tree_view_new ();
ev_find_sidebar_reset_model (sidebar);
gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->tree_view), -1);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
gtk_widget_show (priv->tree_view);
gtk_box_pack_start (GTK_BOX (sidebar), swindow, TRUE, TRUE, 0);
gtk_widget_show (swindow);
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_expand (GTK_TREE_VIEW_COLUMN (column), TRUE);
gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), column);
renderer = (GtkCellRenderer *)g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
"ellipsize",
PANGO_ELLIPSIZE_END,
NULL);
gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column), renderer, TRUE);
gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
"markup", TEXT_COLUMN,
NULL);
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_end (GTK_TREE_VIEW_COLUMN (column), renderer, FALSE);
gtk_tree_view_column_set_attributes (GTK_TREE_VIEW_COLUMN (column), renderer,
"text", PAGE_LABEL_COLUMN,
NULL);
g_object_set (G_OBJECT (renderer), "style", PANGO_STYLE_ITALIC, NULL);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
priv->selection_id = g_signal_connect (selection, "changed",
G_CALLBACK (selection_changed_callback),
sidebar);
g_signal_connect (priv->tree_view, "button-press-event",
G_CALLBACK (sidebar_tree_button_press_cb),
sidebar);
}
GtkWidget *
ev_find_sidebar_new (void)
{
return g_object_new (EV_TYPE_FIND_SIDEBAR,
"orientation", GTK_ORIENTATION_VERTICAL,
NULL);
}
static void
ev_find_sidebar_select_highlighted_result (EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
GtkTreeSelection *selection;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
g_signal_handler_block (selection, priv->selection_id);
gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view), priv->highlighted_result, NULL, FALSE);
g_signal_handler_unblock (selection, priv->selection_id);
}
static void
ev_find_sidebar_highlight_first_match_of_page (EvFindSidebar *sidebar,
gint page)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
gint index = 0;
gint i;
if (!priv->job)
return;
for (i = 0; i < page; i++)
index += ev_job_find_get_n_results (priv->job, i);
if (priv->highlighted_result)
gtk_tree_path_free (priv->highlighted_result);
priv->highlighted_result = gtk_tree_path_new_from_indices (index, -1);
ev_find_sidebar_select_highlighted_result (sidebar);
}
static gchar *
sanitized_substring (const gchar *text,
gint start,
gint end)
{
const gchar *p;
const gchar *start_ptr;
const gchar *end_ptr;
guint len = 0;
gchar *retval;
if (end - start <= 0)
return NULL;
start_ptr = g_utf8_offset_to_pointer (text, start);
end_ptr = g_utf8_offset_to_pointer (start_ptr, end - start);
retval = g_malloc (end_ptr - start_ptr + 1);
p = start_ptr;
while (p != end_ptr) {
const gchar *next;
next = g_utf8_next_char (p);
if (next != end_ptr) {
GUnicodeBreakType break_type;
break_type = g_unichar_break_type (g_utf8_get_char (p));
if (break_type == G_UNICODE_BREAK_HYPHEN && *next == '\n') {
p = g_utf8_next_char (next);
continue;
}
}
if (*p != '\n') {
strncpy (retval + len, p, next - p);
len += next - p;
} else {
*(retval + len) = ' ';
len++;
}
p = next;
}
if (len == 0) {
g_free (retval);
return NULL;
}
retval[len] = 0;
return retval;
}
static gchar *
get_surrounding_text_markup (const gchar *text,
const gchar *find_text,
gboolean case_sensitive,
PangoLogAttr *log_attrs,
gint log_attrs_length,
gint offset)
{
gint iter;
gchar *prec = NULL;
gchar *succ = NULL;
gchar *match = NULL;
gchar *markup;
gint max_chars;
iter = MAX (0, offset - 1);
while (!log_attrs[iter].is_word_start && iter > 0)
iter--;
prec = sanitized_substring (text, iter, offset);
iter = offset;
offset += g_utf8_strlen (find_text, -1);
if (!case_sensitive)
match = g_utf8_substring (text, iter, offset);
iter = MIN (log_attrs_length, offset + 1);
max_chars = MIN (log_attrs_length - 1, iter + 100);
while (TRUE) {
gint word = iter;
while (!log_attrs[word].is_word_end && word < max_chars)
word++;
if (word > max_chars)
break;
iter = word + 1;
}
succ = sanitized_substring (text, offset, iter);
markup = g_markup_printf_escaped ("%s<span weight=\"bold\">%s</span>%s",
prec ? prec : "", match ? match : find_text, succ ? succ : "");
g_free (prec);
g_free (succ);
g_free (match);
return markup;
}
static gchar *
get_page_text (EvDocument *document,
EvPage *page,
EvRectangle **areas,
guint *n_areas)
{
gchar *text;
gboolean success;
ev_document_doc_mutex_lock ();
text = ev_document_text_get_text (EV_DOCUMENT_TEXT (document), page);
success = ev_document_text_get_text_layout (EV_DOCUMENT_TEXT (document), page, areas, n_areas);
ev_document_doc_mutex_unlock ();
if (!success) {
g_free (text);
return NULL;
}
return text;
}
static gint
get_match_offset (EvRectangle *areas,
guint n_areas,
EvRectangle *match,
gint offset)
{
gdouble x, y;
gint i;
x = match->x1;
y = (match->y1 + match->y2) / 2;
i = offset;
do {
EvRectangle *area = areas + i;
if (x >= area->x1 && x < area->x2 &&
y >= area->y1 && y <= area->y2) {
return i;
}
i = (i + 1) % n_areas;
} while (i != offset);
return -1;
}
static gboolean
process_matches_idle (EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
GtkTreeModel *model;
gint current_page;
EvDocument *document;
priv->process_matches_idle_id = 0;
if (!ev_job_find_has_results (priv->job)) {
if (ev_job_is_finished (EV_JOB (priv->job)))
g_clear_object (&priv->job);
return FALSE;
}
document = EV_JOB (priv->job)->document;
model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
do {
GList *matches, *l;
EvPage *page;
gint result;
gchar *page_label;
gchar *page_text;
EvRectangle *areas = NULL;
guint n_areas;
PangoLogAttr *text_log_attrs;
gulong text_log_attrs_length;
gint offset;
current_page = priv->current_page;
priv->current_page = (priv->current_page + 1) % priv->job->n_pages;
matches = priv->job->pages[current_page];
if (!matches)
continue;
page = ev_document_get_page (document, current_page);
page_label = ev_document_get_page_label (document, current_page);
page_text = get_page_text (document, page, &areas, &n_areas);
g_object_unref (page);
if (!page_text)
continue;
text_log_attrs_length = g_utf8_strlen (page_text, -1);
text_log_attrs = g_new0 (PangoLogAttr, text_log_attrs_length + 1);
pango_get_log_attrs (page_text, -1, -1, NULL, text_log_attrs, text_log_attrs_length + 1);
if (priv->first_match_page == -1)
priv->first_match_page = current_page;
offset = 0;
for (l = matches, result = 0; l; l = g_list_next (l), result++) {
EvRectangle *match = (EvRectangle *)l->data;
gchar *markup;
GtkTreeIter iter;
gint new_offset;
new_offset = get_match_offset (areas, n_areas, match, offset);
if (new_offset == -1) {
g_warning ("No offset found for match \"%s\" at page %d after processing %d results\n",
priv->job->text, current_page, result);
/* It may happen that a vertical text match has no corresponding text area, skip
* that but keep iterating to show any other matches in page (issue #1545) */
continue;
}
offset = new_offset;
if (current_page >= priv->job->start_page) {
gtk_list_store_append (GTK_LIST_STORE (model), &iter);
} else {
gtk_list_store_insert (GTK_LIST_STORE (model), &iter,
priv->insert_position);
priv->insert_position++;
}
markup = get_surrounding_text_markup (page_text,
priv->job->text,
priv->job->case_sensitive,
text_log_attrs,
text_log_attrs_length,
offset);
gtk_list_store_set (GTK_LIST_STORE (model), &iter,
TEXT_COLUMN, markup,
PAGE_LABEL_COLUMN, page_label,
PAGE_COLUMN, current_page + 1,
RESULT_COLUMN, result,
-1);
g_free (markup);
}
g_free (page_label);
g_free (page_text);
g_free (text_log_attrs);
g_free (areas);
} while (current_page != priv->job_current_page);
if (ev_job_is_finished (EV_JOB (priv->job)) && priv->current_page == priv->job->start_page)
ev_find_sidebar_highlight_first_match_of_page (sidebar, priv->first_match_page);
return FALSE;
}
static void
find_job_updated_cb (EvJobFind *job,
gint page,
EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
priv->job_current_page = page;
}
static void
find_job_cancelled_cb (EvJob *job,
EvFindSidebar *sidebar)
{
ev_find_sidebar_cancel (sidebar);
}
void
ev_find_sidebar_start (EvFindSidebar *sidebar,
EvJobFind *job)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
if (priv->job == job)
return;
ev_find_sidebar_clear (sidebar);
priv->job = g_object_ref (job);
g_signal_connect_object (job, "updated",
G_CALLBACK (find_job_updated_cb),
sidebar, 0);
g_signal_connect_object (job, "cancelled",
G_CALLBACK (find_job_cancelled_cb),
sidebar, 0);
priv->job_current_page = -1;
priv->first_match_page = -1;
priv->current_page = job->start_page;
priv->insert_position = 0;
}
void
ev_find_sidebar_restart (EvFindSidebar *sidebar,
gint page)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
gint first_match_page = -1;
gint i;
if (!priv->job)
return;
for (i = 0; i < priv->job->n_pages; i++) {
int index;
index = page + i;
if (index >= priv->job->n_pages)
index -= priv->job->n_pages;
if (priv->job->pages[index]) {
first_match_page = index;
break;
}
}
if (first_match_page != -1)
ev_find_sidebar_highlight_first_match_of_page (sidebar, first_match_page);
}
void
ev_find_sidebar_update (EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
if (!priv->job)
return;
if (priv->process_matches_idle_id == 0)
priv->process_matches_idle_id = g_idle_add ((GSourceFunc)process_matches_idle, sidebar);
}
void
ev_find_sidebar_clear (EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
ev_find_sidebar_cancel (sidebar);
/* It seems it's more efficient to set a new model in the tree view instead of
* clearing the model that would emit row-deleted signal for every row in the model
*/
ev_find_sidebar_reset_model (sidebar);
g_clear_pointer (&priv->highlighted_result, gtk_tree_path_free);
}
void
ev_find_sidebar_previous (EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
if (!priv->highlighted_result)
return;
if (!gtk_tree_path_prev (priv->highlighted_result)) {
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
gtk_tree_model_get_iter (model, &iter, priv->highlighted_result);
while (gtk_tree_model_iter_next (model, &iter))
gtk_tree_path_next (priv->highlighted_result);
}
ev_find_sidebar_select_highlighted_result (sidebar);
}
void
ev_find_sidebar_next (EvFindSidebar *sidebar)
{
EvFindSidebarPrivate *priv = GET_PRIVATE (sidebar);
GtkTreeModel *model;
GtkTreeIter iter;
if (!priv->highlighted_result)
return;
model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->tree_view));
gtk_tree_model_get_iter (model, &iter, priv->highlighted_result);
if (gtk_tree_model_iter_next (model, &iter)) {
gtk_tree_path_next (priv->highlighted_result);
} else {
gtk_tree_path_free (priv->highlighted_result);
priv->highlighted_result = gtk_tree_path_new_first ();
}
ev_find_sidebar_select_highlighted_result (sidebar);
}

63
shell/ev-find-sidebar.h Normal file
View file

@ -0,0 +1,63 @@
/* ev-find-sidebar.h
* this file is part of evince, a gnome document viewer
*
* Copyright (C) 2013 Carlos Garcia Campos <carlosgc@gnome.org>
* Copyright (C) 2008 Sergey Pushkin <pushkinsv@gmail.com >
*
* 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.
*/
#ifndef EV_FIND_SIDEBAR_H
#define EV_FIND_SIDEBAR_H
#include <gtk/gtk.h>
#include "ev-jobs.h"
G_BEGIN_DECLS
#define EV_TYPE_FIND_SIDEBAR (ev_find_sidebar_get_type ())
#define EV_FIND_SIDEBAR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), EV_TYPE_FIND_SIDEBAR, EvFindSidebar))
#define EV_IS_FIND_SIDEBAR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), EV_TYPE_FIND_SIDEBAR))
#define EV_FIND_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_FIND_SIDEBAR, EvFindSidebarClass))
#define EV_IS_FIND_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_FIND_SIDEBAR))
#define EV_FIND_SIDEBAR_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), EV_TYPE_FIND_SIDEBAR, EvFindSidebarClass))
typedef struct _EvFindSidebar EvFindSidebar;
typedef struct _EvFindSidebarClass EvFindSidebarClass;
struct _EvFindSidebar {
GtkBox base_instance;
};
struct _EvFindSidebarClass {
GtkBoxClass base_class;
};
GType ev_find_sidebar_get_type (void);
GtkWidget *ev_find_sidebar_new (void);
void ev_find_sidebar_start (EvFindSidebar *find_sidebar,
EvJobFind *job);
void ev_find_sidebar_restart (EvFindSidebar *find_sidebar,
gint page);
void ev_find_sidebar_update (EvFindSidebar *find_sidebar);
void ev_find_sidebar_clear (EvFindSidebar *find_sidebar);
void ev_find_sidebar_previous (EvFindSidebar *find_sidebar);
void ev_find_sidebar_next (EvFindSidebar *find_sidebar);
G_END_DECLS
#endif /* EV_FIND_SIDEBAR_H */

View file

@ -47,6 +47,7 @@
#include "egg-toolbars-model.h"
#include "eggfindbar.h"
#include "ev-find-sidebar.h"
#include "ephy-zoom-action.h"
#include "ephy-zoom.h"
@ -144,6 +145,7 @@ struct _EvWindowPrivate {
GtkWidget *password_view;
GtkWidget *sidebar_thumbs;
GtkWidget *sidebar_links;
GtkWidget *find_sidebar;
GtkWidget *sidebar_attachments;
GtkWidget *sidebar_layers;
GtkWidget *sidebar_annots;
@ -384,6 +386,9 @@ static void zoom_control_changed_cb (EphyZoomAction *action,
float zoom,
EvWindow *ev_window);
static void ev_window_show_find_bar (EvWindow *ev_window);
static void ev_window_close_find_bar (EvWindow *ev_window);
static gchar *caja_sendto = NULL;
G_DEFINE_TYPE_WITH_PRIVATE (EvWindow, ev_window, GTK_TYPE_APPLICATION_WINDOW)
@ -1733,7 +1738,7 @@ ev_window_setup_document (EvWindow *ev_window)
if (EV_IS_DOCUMENT_FIND (document)) {
if (ev_window->priv->search_string &&
!EV_WINDOW_IS_PRESENTATION (ev_window)) {
ev_window_cmd_edit_find (NULL, ev_window);
ev_window_show_find_bar (ev_window);
egg_find_bar_set_search_string (EGG_FIND_BAR (ev_window->priv->find_bar), ev_window->priv->search_string);
}
@ -2487,7 +2492,7 @@ ev_window_open_document (EvWindow *ev_window,
if (search_string && EV_IS_DOCUMENT_FIND (document) &&
mode != EV_WINDOW_MODE_PRESENTATION) {
ev_window_cmd_edit_find (NULL, ev_window);
ev_window_show_find_bar (ev_window);
egg_find_bar_set_search_string (EGG_FIND_BAR (ev_window->priv->find_bar),
search_string);
}
@ -4183,33 +4188,62 @@ ev_window_cmd_edit_select_all (GtkAction *action, EvWindow *ev_window)
#endif
}
static void
ev_window_cmd_toggle_find (GtkAction *action, EvWindow *ev_window)
{
gboolean show_find_bar;
show_find_bar = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
if (show_find_bar)
ev_window_show_find_bar (ev_window);
else
ev_window_close_find_bar (ev_window);
}
static void
ev_window_cmd_edit_find (GtkAction *action, EvWindow *ev_window)
{
if (ev_window->priv->document == NULL || !EV_IS_DOCUMENT_FIND (ev_window->priv->document)) {
g_error ("Find action should be insensitive since document doesn't support find");
return;
}
ev_window_show_find_bar (ev_window);
}
if (EV_WINDOW_IS_PRESENTATION (ev_window))
return;
static void
ev_window_find_previous (EvWindow *ev_window)
{
ev_view_find_previous (EV_VIEW (ev_window->priv->view));
ev_find_sidebar_previous (EV_FIND_SIDEBAR (ev_window->priv->find_sidebar));
}
update_chrome_flag (ev_window, EV_CHROME_FINDBAR, TRUE);
update_chrome_visibility (ev_window);
gtk_widget_grab_focus (ev_window->priv->find_bar);
static void
ev_window_find_next (EvWindow *ev_window)
{
ev_view_find_next (EV_VIEW (ev_window->priv->view));
ev_find_sidebar_next (EV_FIND_SIDEBAR (ev_window->priv->find_sidebar));
}
static gboolean
find_next_idle_cb (EvWindow *ev_window)
{
ev_window_find_next (ev_window);
return FALSE;
}
static void
ev_window_cmd_edit_find_next (GtkAction *action, EvWindow *ev_window)
{
gboolean find_bar_hidden;
if (EV_WINDOW_IS_PRESENTATION (ev_window))
return;
update_chrome_flag (ev_window, EV_CHROME_FINDBAR, TRUE);
update_chrome_visibility (ev_window);
gtk_widget_grab_focus (ev_window->priv->find_bar);
find_bar_hidden = !gtk_widget_get_visible (ev_window->priv->find_bar);
ev_window_show_find_bar (ev_window);
if (ev_window->priv->document && ev_window->priv->document->iswebdocument == FALSE) {
ev_view_find_next (EV_VIEW (ev_window->priv->view));
/* Use idle to make sure view allocation happens before find */
if (find_bar_hidden)
g_idle_add ((GSourceFunc)find_next_idle_cb, ev_window);
else
ev_window_find_next (ev_window);
}
#if ENABLE_EPUB
else {
@ -4218,17 +4252,30 @@ ev_window_cmd_edit_find_next (GtkAction *action, EvWindow *ev_window)
#endif
}
static gboolean
find_previous_idle_cb (EvWindow *ev_window)
{
ev_window_find_previous (ev_window);
return FALSE;
}
static void
ev_window_cmd_edit_find_previous (GtkAction *action, EvWindow *ev_window)
{
gboolean find_bar_hidden;
if (EV_WINDOW_IS_PRESENTATION (ev_window))
return;
update_chrome_flag (ev_window, EV_CHROME_FINDBAR, TRUE);
update_chrome_visibility (ev_window);
gtk_widget_grab_focus (ev_window->priv->find_bar);
find_bar_hidden = !gtk_widget_get_visible (ev_window->priv->find_bar);
ev_window_show_find_bar (ev_window);
if (ev_window->priv->document && ev_window->priv->document->iswebdocument == FALSE) {
ev_view_find_previous (EV_VIEW (ev_window->priv->view));
/* Use idle to make sure view allocation happens before find */
if (find_bar_hidden)
g_idle_add ((GSourceFunc)find_previous_idle_cb, ev_window);
else
ev_window_find_previous (ev_window);
}
#if ENABLE_EPUB
else {
@ -5140,14 +5187,9 @@ ev_window_cmd_escape (GtkAction *action, EvWindow *window)
widget = gtk_window_get_focus (GTK_WINDOW (window));
if (widget && gtk_widget_get_ancestor (widget, EGG_TYPE_FIND_BAR)) {
update_chrome_flag (window, EV_CHROME_FINDBAR, FALSE);
update_chrome_visibility (window);
if (window->priv->view)
gtk_widget_grab_focus (window->priv->view);
ev_window_close_find_bar (window);
#if ENABLE_EPUB
else
gtk_widget_grab_focus (window->priv->webview);
gtk_widget_grab_focus (window->priv->webview);
#endif
} else {
gboolean fullscreen;
@ -5729,6 +5771,15 @@ attachment_bar_menu_popup_cb (EvSidebarAttachments *attachbar,
return TRUE;
}
static void
find_sidebar_result_activated_cb (EvFindSidebar *find_sidebar,
gint page,
gint result,
EvWindow *window)
{
ev_view_find_set_result (EV_VIEW (window->priv->view), page, result);
}
static void
ev_window_update_find_status_message (EvWindow *ev_window)
{
@ -5794,6 +5845,7 @@ ev_window_find_job_updated_cb (EvJobFind *job,
page);
}
ev_window_update_find_status_message (ev_window);
ev_find_sidebar_update (EV_FIND_SIDEBAR (ev_window->priv->find_sidebar));
}
static void
@ -5821,10 +5873,10 @@ find_bar_previous_cb (EggFindBar *find_bar,
#if ENABLE_EPUB
if (ev_window->priv->document && ev_window->priv->document->iswebdocument == TRUE ) {
ev_web_view_find_previous(EV_WEB_VIEW(ev_window->priv->webview));
}else
} else
#endif
{
ev_view_find_previous (EV_VIEW (ev_window->priv->view));
ev_window_find_previous (ev_window);
}
}
@ -5838,7 +5890,7 @@ find_bar_next_cb (EggFindBar *find_bar,
} else
#endif
{
ev_view_find_next (EV_VIEW (ev_window->priv->view));
ev_window_find_next (ev_window);
}
}
@ -5850,14 +5902,8 @@ find_bar_close_cb (EggFindBar *find_bar,
if (ev_window->priv->document && ev_window->priv->document->iswebdocument == TRUE ) {
ev_web_view_find_cancel(EV_WEB_VIEW(ev_window->priv->webview));
}
else
#endif
{
ev_view_find_cancel (EV_VIEW (ev_window->priv->view));
}
ev_window_clear_find_job (ev_window);
update_chrome_flag (ev_window, EV_CHROME_FINDBAR, FALSE);
update_chrome_visibility (ev_window);
ev_window_close_find_bar (ev_window);
}
static void
@ -5880,6 +5926,9 @@ ev_window_search_start (EvWindow *ev_window)
search_string,
egg_find_bar_get_case_sensitive (find_bar));
ev_find_sidebar_start (EV_FIND_SIDEBAR (ev_window->priv->find_sidebar),
EV_JOB_FIND (ev_window->priv->find_job));
g_signal_connect (ev_window->priv->find_job, "finished",
G_CALLBACK (ev_window_find_job_finished_cb),
ev_window);
@ -5890,6 +5939,7 @@ ev_window_search_start (EvWindow *ev_window)
} else {
ev_window_update_actions (ev_window);
egg_find_bar_set_status_text (find_bar, NULL);
ev_find_sidebar_clear (EV_FIND_SIDEBAR (ev_window->priv->find_sidebar));
if (ev_window->priv->document->iswebdocument == FALSE) {
gtk_widget_queue_draw (GTK_WIDGET (ev_window->priv->view));
}
@ -5936,13 +5986,70 @@ find_bar_visibility_changed_cb (EggFindBar *find_bar,
#endif
ev_window_update_actions (ev_window);
if (visible)
ev_window_search_start (ev_window);
else
if (!visible)
egg_find_bar_set_status_text (EGG_FIND_BAR (ev_window->priv->find_bar), NULL);
}
}
static void
update_toggle_find_action (EvWindow *ev_window,
gboolean active)
{
GtkAction *action;
action = gtk_action_group_get_action (ev_window->priv->action_group, "EditFind");
if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) == active)
return;
g_signal_handlers_block_by_func (action, G_CALLBACK (ev_window_cmd_toggle_find), ev_window);
gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), active);
g_signal_handlers_unblock_by_func (action, G_CALLBACK (ev_window_cmd_toggle_find), ev_window);
}
static void
ev_window_show_find_bar (EvWindow *ev_window)
{
if (gtk_widget_get_visible (ev_window->priv->find_bar))
return;
if (ev_window->priv->document == NULL || !EV_IS_DOCUMENT_FIND (ev_window->priv->document)) {
g_error ("Find action should be insensitive since document doesn't support find");
return;
}
if (EV_WINDOW_IS_PRESENTATION (ev_window))
return;
g_object_ref (ev_window->priv->sidebar);
gtk_container_remove (GTK_CONTAINER (ev_window->priv->hpaned), ev_window->priv->sidebar);
gtk_paned_pack1 (GTK_PANED (ev_window->priv->hpaned),
ev_window->priv->find_sidebar, FALSE, FALSE);
gtk_widget_show (ev_window->priv->find_sidebar);
update_chrome_flag (ev_window, EV_CHROME_FINDBAR, TRUE);
update_chrome_visibility (ev_window);
gtk_widget_grab_focus (ev_window->priv->find_bar);
update_toggle_find_action (ev_window, TRUE);
}
static void
ev_window_close_find_bar (EvWindow *ev_window)
{
if (!gtk_widget_get_visible (ev_window->priv->find_bar))
return;
g_object_ref (ev_window->priv->find_sidebar);
gtk_container_remove (GTK_CONTAINER (ev_window->priv->hpaned),
ev_window->priv->find_sidebar);
gtk_paned_pack1 (GTK_PANED (ev_window->priv->hpaned),
ev_window->priv->sidebar, FALSE, FALSE);
update_chrome_flag (ev_window, EV_CHROME_FINDBAR, FALSE);
update_chrome_visibility (ev_window);
gtk_widget_grab_focus (ev_window->priv->view);
update_toggle_find_action (ev_window, FALSE);
}
static void
zoom_control_changed_cb (EphyZoomAction *action,
float zoom,
@ -6467,9 +6574,6 @@ static const GtkActionEntry entries[] = {
G_CALLBACK (ev_window_cmd_edit_copy) },
{ "EditSelectAll", "edit-select-all", N_("Select _All"), "<control>A", NULL,
G_CALLBACK (ev_window_cmd_edit_select_all) },
{ "EditFind", "edit-find", N_("_Find…"), "<control>F",
N_("Find a word or phrase in the document"),
G_CALLBACK (ev_window_cmd_edit_find) },
{ "EditFindNext", NULL, N_("Find Ne_xt"), "<control>G", NULL,
G_CALLBACK (ev_window_cmd_edit_find_next) },
{ "EditFindPrevious", NULL, N_("Find Pre_vious"), "<shift><control>G", NULL,
@ -6624,6 +6728,9 @@ static const GtkToggleActionEntry toggle_entries[] = {
N_("Activate or disable caret-navigation"),
G_CALLBACK (ev_window_cmd_view_toggle_caret_navigation) },
{ "EditFind", GTK_STOCK_FIND, N_("_Find…"), "<control>F",
N_("Find a word or phrase in the document"),
G_CALLBACK (ev_window_cmd_toggle_find) },
};
/* Popups specific items */
@ -7116,7 +7223,7 @@ do_action_named (EvWindow *window, EvLinkAction *action)
} else if (g_ascii_strcasecmp (name, "GoToPage") == 0) {
ev_window_cmd_focus_page_selector (NULL, window);
} else if (g_ascii_strcasecmp (name, "Find") == 0) {
ev_window_cmd_edit_find (NULL, window);
ev_window_show_find_bar (window);
} else if (g_ascii_strcasecmp (name, "Close") == 0) {
ev_window_cmd_file_close_window (NULL, window);
} else if (g_ascii_strcasecmp (name, "Print") == 0) {
@ -7966,6 +8073,12 @@ ev_window_init (EvWindow *ev_window)
FALSE, FALSE, 0);
gtk_widget_show (ev_window->priv->toolbar);
/* Find Bar */
ev_window->priv->find_bar = egg_find_bar_new ();
gtk_box_pack_end (GTK_BOX (ev_window->priv->main_box),
ev_window->priv->find_bar,
FALSE, TRUE, 0);
/* Add the main area */
ev_window->priv->hpaned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
g_signal_connect (ev_window->priv->hpaned,
@ -8142,11 +8255,12 @@ ev_window_init (EvWindow *ev_window)
gtk_widget_show (ev_window->priv->view);
gtk_widget_show (ev_window->priv->password_view);
/* Find Bar */
ev_window->priv->find_bar = egg_find_bar_new ();
gtk_box_pack_end (GTK_BOX (ev_window->priv->main_box),
ev_window->priv->find_bar,
FALSE, TRUE, 0);
/* Find results sidebar */
ev_window->priv->find_sidebar = ev_find_sidebar_new ();
g_signal_connect (ev_window->priv->find_sidebar,
"result-activated",
G_CALLBACK (find_sidebar_result_activated_cb),
ev_window);
/* We own a ref on these widgets, as we can swap them in and out */
g_object_ref (ev_window->priv->view);