From 17f37a4bdb3e37586121edebd9e48cb24340b677 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Campos Date: Sun, 15 Jun 2008 14:56:06 +0000 Subject: Automaticly reload the document when the file has changed on disk. Bug 2008-06-15 Carlos Garcia Campos * shell/Makefile.am: * shell/ev-file-monitor.[ch]: * shell/ev-window.c: (ev_window_document_changed), (ev_window_clear_reload_job), (ev_window_load_job_cb), (ev_window_reload_job_cb), (ev_window_open_uri), (ev_window_reload_document), (ev_window_cmd_view_reload), (ev_window_dispose): Automaticly reload the document when the file has changed on disk. Bug #304249. svn path=/trunk/; revision=3051 --- (limited to 'shell') diff --git a/shell/Makefile.am b/shell/Makefile.am index fce876a..824cf3f 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -25,6 +25,8 @@ evince_SOURCES= \ ev-job-queue.c \ ev-jobs.h \ ev-jobs.c \ + ev-file-monitor.h \ + ev-file-monitor.c \ ev-history.c \ ev-history.h \ ev-marshal.c \ diff --git a/shell/ev-file-monitor.c b/shell/ev-file-monitor.c new file mode 100644 index 0000000..3db0e78 --- /dev/null +++ b/shell/ev-file-monitor.c @@ -0,0 +1,168 @@ +/* ev-file-monitor.c + * this file is part of evince, a gnome document viewer + * + * Copyright (C) 2008 Carlos Garcia Campos + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include +#include + +#include "ev-file-monitor.h" + +enum { + CHANGED, + N_SIGNALS +}; + +struct _EvFileMonitorPrivate { + GFileMonitor *monitor; + + guint timeout_id; +}; + +static void ev_file_monitor_timeout_start (EvFileMonitor *ev_monitor); +static void ev_file_monitor_timeout_stop (EvFileMonitor *ev_monitor); +static void ev_file_monitor_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + EvFileMonitor *ev_monitor); + +#define EV_FILE_MONITOR_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_FILE_MONITOR, EvFileMonitorPrivate)) + +G_DEFINE_TYPE (EvFileMonitor, ev_file_monitor, G_TYPE_OBJECT) + +static guint signals[N_SIGNALS]; + +static void +ev_file_monitor_init (EvFileMonitor *ev_monitor) +{ + ev_monitor->priv = EV_FILE_MONITOR_GET_PRIVATE (ev_monitor); +} + +static void +ev_file_monitor_finalize (GObject *object) +{ + EvFileMonitor *ev_monitor = EV_FILE_MONITOR (object); + + ev_file_monitor_timeout_stop (ev_monitor); + + if (ev_monitor->priv->monitor) { + g_signal_handlers_disconnect_by_func (ev_monitor->priv->monitor, + ev_file_monitor_changed_cb, + ev_monitor); + g_object_unref (ev_monitor->priv->monitor); + ev_monitor->priv->monitor = NULL; + } + + G_OBJECT_CLASS (ev_file_monitor_parent_class)->finalize (object); +} + +static void +ev_file_monitor_class_init (EvFileMonitorClass *klass) +{ + GObjectClass *g_object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (g_object_class, sizeof (EvFileMonitorPrivate)); + + g_object_class->finalize = ev_file_monitor_finalize; + + /* Signals */ + signals[CHANGED] = + g_signal_new ("changed", + EV_TYPE_FILE_MONITOR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EvFileMonitorClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static gboolean +timeout_cb (EvFileMonitor *ev_monitor) +{ + g_signal_emit (ev_monitor, signals[CHANGED], 0); + + ev_monitor->priv->timeout_id = 0; + return FALSE; +} + +static void +ev_file_monitor_timeout_start (EvFileMonitor *ev_monitor) +{ + ev_file_monitor_timeout_stop (ev_monitor); + + ev_monitor->priv->timeout_id = + g_timeout_add_seconds (5, (GSourceFunc)timeout_cb, ev_monitor); +} + +static void +ev_file_monitor_timeout_stop (EvFileMonitor *ev_monitor) +{ + if (ev_monitor->priv->timeout_id > 0) { + g_source_remove (ev_monitor->priv->timeout_id); + ev_monitor->priv->timeout_id = 0; + } +} + +static void +ev_file_monitor_changed_cb (GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + EvFileMonitor *ev_monitor) +{ + switch (event_type) { + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + ev_file_monitor_timeout_stop (ev_monitor); + g_signal_emit (ev_monitor, signals[CHANGED], 0); + + break; + case G_FILE_MONITOR_EVENT_CHANGED: + ev_file_monitor_timeout_start (ev_monitor); + break; + default: + break; + } +} + +EvFileMonitor * +ev_file_monitor_new (const gchar *uri) +{ + EvFileMonitor *ev_monitor; + GFile *file; + GError *error = NULL; + + ev_monitor = EV_FILE_MONITOR (g_object_new (EV_TYPE_FILE_MONITOR, NULL)); + + file = g_file_new_for_uri (uri); + ev_monitor->priv->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error); + if (ev_monitor->priv->monitor) { + g_signal_connect (G_OBJECT (ev_monitor->priv->monitor), "changed", + G_CALLBACK (ev_file_monitor_changed_cb), ev_monitor); + } else if (error) { + g_warning ("%s", error->message); + g_error_free (error); + } + + g_object_unref (file); + + return ev_monitor; +} diff --git a/shell/ev-file-monitor.h b/shell/ev-file-monitor.h new file mode 100644 index 0000000..bdad49b --- /dev/null +++ b/shell/ev-file-monitor.h @@ -0,0 +1,57 @@ +/* ev-file-monitor.h + * this file is part of evince, a gnome document viewer + * + * Copyright (C) 2008 Carlos Garcia Campos + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef EV_FILE_MONITOR_H +#define EV_FILE_MONITOR_H + +#include + +G_BEGIN_DECLS + +typedef struct _EvFileMonitor EvFileMonitor; +typedef struct _EvFileMonitorClass EvFileMonitorClass; +typedef struct _EvFileMonitorPrivate EvFileMonitorPrivate; + +#define EV_TYPE_FILE_MONITOR (ev_file_monitor_get_type()) +#define EV_FILE_MONITOR(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_FILE_MONITOR, EvFileMonitor)) +#define EV_FILE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_FILE_MONITOR, EvFileMonitorClass)) +#define EV_IS_FILE_MONITOR(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_FILE_MONITOR)) +#define EV_IS_FILE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_FILE_MONITOR)) +#define EV_FILE_MONITOR_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_FILE_MONITOR, EvFileMonitorClass)) + +struct _EvFileMonitor { + GObject base_instance; + + EvFileMonitorPrivate *priv; +}; + +struct _EvFileMonitorClass { + GObjectClass base_class; + + /* Signals */ + void (*changed) (EvFileMonitor *ev_monitor); +}; + +GType ev_file_monitor_get_type (void) G_GNUC_CONST; +EvFileMonitor *ev_file_monitor_new (const gchar *uri); + +G_END_DECLS + +#endif /* EV_FILE_MONITOR_H */ diff --git a/shell/ev-window.c b/shell/ev-window.c index f6a5254..dd2947f 100644 --- a/shell/ev-window.c +++ b/shell/ev-window.c @@ -67,6 +67,7 @@ #include "ev-image.h" #include "ev-message-area.h" #include "ev-mount-operation.h" +#include "ev-file-monitor.h" #include @@ -159,6 +160,7 @@ struct _EvWindowPrivate { EvLinkDest *dest; gboolean unlink_temp_file; gboolean in_reload; + EvFileMonitor *monitor; EvDocument *document; EvHistory *history; @@ -167,6 +169,7 @@ struct _EvWindowPrivate { EvWindowTitle *title; EvJob *load_job; + EvJob *reload_job; EvJob *thumbnail_job; EvJob *save_job; EvJob *print_job; @@ -217,6 +220,9 @@ static void ev_window_set_page_mode (EvWindow *windo EvWindowPageMode page_mode); static void ev_window_load_job_cb (EvJobLoad *job, gpointer data); +static void ev_window_reload_document (EvWindow *window); +static void ev_window_reload_job_cb (EvJobLoad *job, + EvWindow *window); static void ev_window_set_icon_from_thumbnail (EvJobThumbnail *job, EvWindow *ev_window); static void ev_window_print_job_cb (EvJobPrint *job, @@ -1170,6 +1176,13 @@ ev_window_set_document (EvWindow *ev_window, EvDocument *document) } static void +ev_window_document_changed (EvWindow *ev_window, + gpointer user_data) +{ + ev_window_reload_document (ev_window); +} + +static void password_dialog_response (GtkWidget *password_dialog, gint response_id, EvWindow *ev_window) @@ -1247,6 +1260,20 @@ ev_window_clear_load_job (EvWindow *ev_window) } static void +ev_window_clear_reload_job (EvWindow *ev_window) +{ + if (ev_window->priv->reload_job != NULL) { + + if (!ev_window->priv->reload_job->finished) + ev_job_queue_remove_job (ev_window->priv->reload_job); + + g_signal_handlers_disconnect_by_func (ev_window->priv->reload_job, ev_window_reload_job_cb, ev_window); + g_object_unref (ev_window->priv->reload_job); + ev_window->priv->reload_job = NULL; + } +} + +static void ev_window_clear_local_uri (EvWindow *ev_window) { if (ev_window->priv->local_uri) { @@ -1301,7 +1328,7 @@ ev_window_load_job_cb (EvJobLoad *job, EvDocument *document = EV_JOB (job)->document; g_assert (job->uri); - + ev_view_set_loading (EV_VIEW (ev_window->priv->view), FALSE); /* Success! */ @@ -1340,23 +1367,19 @@ ev_window_load_job_cb (EvJobLoad *job, break; } - /* Restart the search after reloading */ - if (ev_window->priv->in_reload) { - GtkWidget *widget; - - widget = gtk_window_get_focus (GTK_WINDOW (ev_window)); - if (widget && gtk_widget_get_ancestor (widget, EGG_TYPE_FIND_BAR)) { - find_bar_search_changed_cb (EGG_FIND_BAR (ev_window->priv->find_bar), - NULL, ev_window); - } - } else if (job->search_string && EV_IS_DOCUMENT_FIND (document)) { + if (job->search_string && EV_IS_DOCUMENT_FIND (document)) { ev_window_cmd_edit_find (NULL, ev_window); egg_find_bar_set_search_string (EGG_FIND_BAR (ev_window->priv->find_bar), job->search_string); } + /* Create a monitor for the document */ + ev_window->priv->monitor = ev_file_monitor_new (job->uri); + g_signal_connect_swapped (G_OBJECT (ev_window->priv->monitor), "changed", + G_CALLBACK (ev_window_document_changed), + ev_window); + ev_window_clear_load_job (ev_window); - ev_window->priv->in_reload = FALSE; return; } @@ -1381,10 +1404,32 @@ ev_window_load_job_cb (EvJobLoad *job, _("Unable to open document"), job->error); ev_window_clear_load_job (ev_window); - ev_window->priv->in_reload = FALSE; } +} - return; +static void +ev_window_reload_job_cb (EvJobLoad *job, + EvWindow *ev_window) +{ + GtkWidget *widget; + + if (job->error) { + ev_window_clear_reload_job (ev_window); + ev_window->priv->in_reload = FALSE; + return; + } + + ev_window_set_document (ev_window, EV_JOB (job)->document); + + /* Restart the search after reloading */ + widget = gtk_window_get_focus (GTK_WINDOW (ev_window)); + if (widget && gtk_widget_get_ancestor (widget, EGG_TYPE_FIND_BAR)) { + find_bar_search_changed_cb (EGG_FIND_BAR (ev_window->priv->find_bar), + NULL, ev_window); + } + + ev_window_clear_reload_job (ev_window); + ev_window->priv->in_reload = FALSE; } /** @@ -1533,9 +1578,17 @@ ev_window_open_uri (EvWindow *ev_window, { GFile *source_file; + ev_window->priv->in_reload = FALSE; + if (ev_window->priv->uri && g_ascii_strcasecmp (ev_window->priv->uri, uri) == 0) { - ev_window->priv->in_reload = TRUE; + ev_window_reload_document (ev_window); + return; + } + + if (ev_window->priv->monitor) { + g_object_unref (ev_window->priv->monitor); + ev_window->priv->monitor = NULL; } ev_window_close_dialogs (ev_window); @@ -1573,6 +1626,22 @@ ev_window_open_uri (EvWindow *ev_window, } static void +ev_window_reload_document (EvWindow *ev_window) +{ + const gchar *uri; + + ev_window_clear_reload_job (ev_window); + ev_window->priv->in_reload = TRUE; + + uri = ev_window->priv->local_uri ? ev_window->priv->local_uri : ev_window->priv->uri; + ev_window->priv->reload_job = ev_job_load_new (uri, NULL, 0, NULL); + g_signal_connect (ev_window->priv->reload_job, "finished", + G_CALLBACK (ev_window_reload_job_cb), + ev_window); + ev_job_queue_add_job (ev_window->priv->reload_job, EV_JOB_PRIORITY_LOW); +} + +static void file_open_dialog_response_cb (GtkWidget *chooser, gint response_id, EvWindow *ev_window) @@ -3194,11 +3263,7 @@ ev_window_cmd_go_backward (GtkAction *action, EvWindow *ev_window) static void ev_window_cmd_view_reload (GtkAction *action, EvWindow *ev_window) { - gchar *uri; - - uri = g_strdup (ev_window->priv->uri); - ev_window_open_uri (ev_window, uri, NULL, 0, NULL, FALSE, NULL); - g_free (uri); + ev_window_reload_document (ev_window); } static void @@ -4012,6 +4077,11 @@ ev_window_dispose (GObject *object) EvWindow *window = EV_WINDOW (object); EvWindowPrivate *priv = window->priv; + if (priv->monitor) { + g_object_unref (priv->monitor); + priv->monitor = NULL; + } + if (priv->title) { ev_window_title_free (priv->title); priv->title = NULL; @@ -4075,6 +4145,10 @@ ev_window_dispose (GObject *object) ev_window_clear_load_job (window); } + if (priv->reload_job) { + ev_window_clear_reload_job (window); + } + if (priv->save_job) { ev_window_clear_save_job (window); } -- cgit v0.9.1