summaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
Diffstat (limited to 'shell')
-rw-r--r--shell/Makefile.am160
-rw-r--r--shell/eggfindbar.c740
-rw-r--r--shell/eggfindbar.h82
-rw-r--r--shell/ev-annotation-properties-dialog.c327
-rw-r--r--shell/ev-annotation-properties-dialog.h54
-rw-r--r--shell/ev-application.c1151
-rw-r--r--shell/ev-application.h88
-rw-r--r--shell/ev-convert-metadata.c302
-rw-r--r--shell/ev-daemon.c553
-rw-r--r--shell/ev-file-monitor.c168
-rw-r--r--shell/ev-file-monitor.h57
-rw-r--r--shell/ev-history.c148
-rw-r--r--shell/ev-history.h65
-rw-r--r--shell/ev-keyring.c116
-rw-r--r--shell/ev-keyring.h37
-rw-r--r--shell/ev-marshal.list1
-rw-r--r--shell/ev-media-player-keys.c233
-rw-r--r--shell/ev-media-player-keys.h49
-rw-r--r--shell/ev-message-area.c354
-rw-r--r--shell/ev-message-area.h74
-rw-r--r--shell/ev-metadata.c313
-rw-r--r--shell/ev-metadata.h69
-rw-r--r--shell/ev-navigation-action-widget.c214
-rw-r--r--shell/ev-navigation-action-widget.h46
-rw-r--r--shell/ev-navigation-action.c247
-rw-r--r--shell/ev-navigation-action.h64
-rw-r--r--shell/ev-open-recent-action.c101
-rw-r--r--shell/ev-open-recent-action.h52
-rw-r--r--shell/ev-password-view.c434
-rw-r--r--shell/ev-password-view.h59
-rw-r--r--shell/ev-progress-message-area.c198
-rw-r--r--shell/ev-progress-message-area.h64
-rw-r--r--shell/ev-properties-dialog.c138
-rw-r--r--shell/ev-properties-dialog.h47
-rw-r--r--shell/ev-properties-fonts.c224
-rw-r--r--shell/ev-properties-fonts.h48
-rw-r--r--shell/ev-properties-license.c169
-rw-r--r--shell/ev-properties-license.h50
-rw-r--r--shell/ev-sidebar-annotations.c542
-rw-r--r--shell/ev-sidebar-annotations.h62
-rw-r--r--shell/ev-sidebar-attachments.c707
-rw-r--r--shell/ev-sidebar-attachments.h62
-rw-r--r--shell/ev-sidebar-layers.c411
-rw-r--r--shell/ev-sidebar-layers.h58
-rw-r--r--shell/ev-sidebar-links.c760
-rw-r--r--shell/ev-sidebar-links.h66
-rw-r--r--shell/ev-sidebar-page.c92
-rw-r--r--shell/ev-sidebar-page.h64
-rw-r--r--shell/ev-sidebar-thumbnails.c974
-rw-r--r--shell/ev-sidebar-thumbnails.h59
-rw-r--r--shell/ev-sidebar.c543
-rw-r--r--shell/ev-sidebar.h67
-rw-r--r--shell/ev-utils.c389
-rw-r--r--shell/ev-utils.h47
-rw-r--r--shell/ev-window-title.c203
-rw-r--r--shell/ev-window-title.h47
-rw-r--r--shell/ev-window.c7149
-rw-r--r--shell/ev-window.h93
-rw-r--r--shell/evince-icon.rc1
-rw-r--r--shell/main.c328
60 files changed, 20020 insertions, 0 deletions
diff --git a/shell/Makefile.am b/shell/Makefile.am
new file mode 100644
index 00000000..e80c9986
--- /dev/null
+++ b/shell/Makefile.am
@@ -0,0 +1,160 @@
+INCLUDES= \
+ -DEVINCEDATADIR=\"$(pkgdatadir)\" \
+ -DMATEDATADIR=\"$(datadir)\" \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -I$(top_srcdir)/cut-n-paste/zoom-control/ \
+ -I$(top_srcdir)/cut-n-paste/toolbar-editor/ \
+ -I$(top_srcdir)/cut-n-paste/totem-screensaver/ \
+ -I$(top_srcdir)/cut-n-paste/gimpcellrenderertoggle/ \
+ -I$(top_srcdir)/cut-n-paste/smclient/ \
+ -I$(top_srcdir)/libdocument \
+ -I$(top_builddir)/libdocument \
+ -I$(top_srcdir)/libview \
+ -I$(top_builddir)/libview \
+ -I$(top_srcdir)/libmisc \
+ -I$(top_srcdir)/properties \
+ -DMATEICONDIR=\""$(datadir)/pixmaps"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DEVINCE_COMPILATION \
+ $(SHELL_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED)
+
+bin_PROGRAMS=evince
+
+libexec_PROGRAMS=evince-convert-metadata
+if ENABLE_DBUS
+libexec_PROGRAMS += evinced
+endif
+
+evince_SOURCES= \
+ eggfindbar.c \
+ eggfindbar.h \
+ ev-annotation-properties-dialog.h \
+ ev-annotation-properties-dialog.c \
+ ev-application.c \
+ ev-application.h \
+ ev-file-monitor.h \
+ ev-file-monitor.c \
+ ev-history.c \
+ ev-history.h \
+ ev-keyring.h \
+ ev-keyring.c \
+ ev-marshal.c \
+ ev-marshal.h \
+ ev-message-area.c \
+ ev-message-area.h \
+ ev-metadata.c \
+ ev-metadata.h \
+ ev-navigation-action.c \
+ ev-navigation-action.h \
+ ev-navigation-action-widget.c \
+ ev-navigation-action-widget.h \
+ ev-password-view.h \
+ ev-password-view.c \
+ ev-progress-message-area.h \
+ ev-progress-message-area.c \
+ ev-properties-dialog.c \
+ ev-properties-dialog.h \
+ ev-properties-fonts.c \
+ ev-properties-fonts.h \
+ ev-properties-license.c \
+ ev-properties-license.h \
+ ev-open-recent-action.c \
+ ev-open-recent-action.h \
+ ev-utils.c \
+ ev-utils.h \
+ ev-window.c \
+ ev-window.h \
+ ev-window-title.c \
+ ev-window-title.h \
+ ev-sidebar.c \
+ ev-sidebar.h \
+ ev-sidebar-annotations.c \
+ ev-sidebar-annotations.h \
+ ev-sidebar-attachments.c \
+ ev-sidebar-attachments.h \
+ ev-sidebar-layers.c \
+ ev-sidebar-layers.h \
+ ev-sidebar-links.c \
+ ev-sidebar-links.h \
+ ev-sidebar-page.c \
+ ev-sidebar-page.h \
+ ev-sidebar-thumbnails.c \
+ ev-sidebar-thumbnails.h \
+ main.c
+
+if ENABLE_DBUS
+evince_SOURCES += \
+ ev-media-player-keys.c \
+ ev-media-player-keys.h
+endif
+
+
+evince_LDFLAGS = $(AM_LDFLAGS)
+
+if PLATFORM_WIN32
+evince_LDFLAGS += -mwindows
+endif
+
+evince_LDADD= \
+ $(top_builddir)/cut-n-paste/zoom-control/libephyzoom.la \
+ $(top_builddir)/cut-n-paste/toolbar-editor/libtoolbareditor.la \
+ $(top_builddir)/cut-n-paste/totem-screensaver/libtotemscrsaver.la \
+ $(top_builddir)/cut-n-paste/gimpcellrenderertoggle/libgimpcellrenderertoggle.la \
+ $(top_builddir)/cut-n-paste/smclient/libsmclient.la \
+ $(top_builddir)/properties/libevproperties.la \
+ $(top_builddir)/libdocument/libevdocument.la \
+ $(top_builddir)/libview/libevview.la \
+ $(top_builddir)/libmisc/libevmisc.la \
+ $(SHELL_LIBS)
+
+if PLATFORM_WIN32
+evince_LDADD += evince-icon.o
+
+evince-icon.o: $(srcdir)/evince-icon.rc $(top_srcdir)/data/evince.ico
+ $(WINDRES) $(srcdir)/evince-icon.rc -I $(top_srcdir)/data evince-icon.o
+endif
+
+BUILT_SOURCES = ev-marshal.h ev-marshal.c
+
+evince_convert_metadata_SOURCES= \
+ ev-convert-metadata.c
+
+evince_convert_metadata_LDADD= \
+ $(SHELL_LIBS)
+
+if ENABLE_DBUS
+evinced_SOURCES= \
+ ev-daemon.c
+
+evinced_CFLAGS= \
+ -DEVINCEDATADIR=\"$(pkgdatadir)\" \
+ -DMATEDATADIR=\"$(datadir)\" \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DEVINCE_COMPILATION \
+ $(EV_DAEMON_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(DISABLE_DEPRECATED)
+
+evinced_LDADD= \
+ $(EV_DAEMON_LIBS)
+endif
+
+EXTRA_DIST = ev-marshal.list \
+ evince-icon.rc
+
+ev-marshal.h: $(srcdir)/ev-marshal.list
+ $(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=ev_marshal $(srcdir)/ev-marshal.list --header > ev-marshal.h
+
+ev-marshal.c: $(srcdir)/ev-marshal.list
+ echo '#include "ev-marshal.h"' > ev-marshal.c
+ $(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=ev_marshal $(srcdir)/ev-marshal.list --body >> ev-marshal.c
+
+DISTCLEANFILES =
+
+-include $(top_srcdir)/git.mk
diff --git a/shell/eggfindbar.c b/shell/eggfindbar.c
new file mode 100644
index 00000000..a00e74ed
--- /dev/null
+++ b/shell/eggfindbar.c
@@ -0,0 +1,740 @@
+/* Copyright (C) 2004 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Mate Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "eggfindbar.h"
+
+struct _EggFindBarPrivate
+{
+ gchar *search_string;
+
+ GtkToolItem *next_button;
+ GtkToolItem *previous_button;
+ GtkToolItem *status_separator;
+ GtkToolItem *status_item;
+ GtkToolItem *case_button;
+
+ GtkWidget *find_entry;
+ GtkWidget *status_label;
+
+ gulong set_focus_handler;
+ guint case_sensitive : 1;
+};
+
+#define EGG_FIND_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EGG_TYPE_FIND_BAR, EggFindBarPrivate))
+
+enum {
+ PROP_0,
+ PROP_SEARCH_STRING,
+ PROP_CASE_SENSITIVE
+};
+
+static void egg_find_bar_finalize (GObject *object);
+static void egg_find_bar_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void egg_find_bar_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void egg_find_bar_show (GtkWidget *widget);
+static void egg_find_bar_hide (GtkWidget *widget);
+static void egg_find_bar_grab_focus (GtkWidget *widget);
+
+G_DEFINE_TYPE (EggFindBar, egg_find_bar, GTK_TYPE_TOOLBAR);
+
+enum
+ {
+ NEXT,
+ PREVIOUS,
+ CLOSE,
+ SCROLL,
+ LAST_SIGNAL
+ };
+
+static guint find_bar_signals[LAST_SIGNAL] = { 0 };
+
+static void
+egg_find_bar_class_init (EggFindBarClass *klass)
+{
+ GObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkBindingSet *binding_set;
+
+ egg_find_bar_parent_class = g_type_class_peek_parent (klass);
+
+ object_class = (GObjectClass *)klass;
+ widget_class = (GtkWidgetClass *)klass;
+
+ object_class->set_property = egg_find_bar_set_property;
+ object_class->get_property = egg_find_bar_get_property;
+
+ 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] =
+ g_signal_new ("next",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EggFindBarClass, next),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ find_bar_signals[PREVIOUS] =
+ g_signal_new ("previous",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EggFindBarClass, previous),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ find_bar_signals[CLOSE] =
+ g_signal_new ("close",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EggFindBarClass, close),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ find_bar_signals[SCROLL] =
+ g_signal_new ("scroll",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EggFindBarClass, scroll),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__ENUM,
+ G_TYPE_NONE, 1,
+ GTK_TYPE_SCROLL_TYPE);
+
+ /**
+ * EggFindBar:search_string:
+ *
+ * The current string to search for. NULL or empty string
+ * both mean no current string.
+ *
+ */
+ g_object_class_install_property (object_class,
+ PROP_SEARCH_STRING,
+ g_param_spec_string ("search_string",
+ "Search string",
+ "The name of the string to be found",
+ NULL,
+ G_PARAM_READWRITE));
+
+ /**
+ * EggFindBar:case_sensitive:
+ *
+ * TRUE for a case sensitive search.
+ *
+ */
+ g_object_class_install_property (object_class,
+ PROP_CASE_SENSITIVE,
+ g_param_spec_boolean ("case_sensitive",
+ "Case sensitive",
+ "TRUE for a case sensitive search",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ /* Style properties */
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_boxed ("all_matches_color",
+ "Highlight color",
+ "Color of highlight for all matches",
+ GDK_TYPE_COLOR,
+ G_PARAM_READABLE));
+
+ gtk_widget_class_install_style_property (widget_class,
+ g_param_spec_boxed ("current_match_color",
+ "Current color",
+ "Color of highlight for the current match",
+ GDK_TYPE_COLOR,
+ G_PARAM_READABLE));
+
+ g_type_class_add_private (object_class, sizeof (EggFindBarPrivate));
+
+ binding_set = gtk_binding_set_by_class (klass);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
+ "close", 0);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
+ "scroll", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
+ "scroll", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
+ "scroll", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, 0,
+ "scroll", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_BACKWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
+ "scroll", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
+ gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, 0,
+ "scroll", 1,
+ GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_FORWARD);
+}
+
+static void
+egg_find_bar_emit_next (EggFindBar *find_bar)
+{
+ g_signal_emit (find_bar, find_bar_signals[NEXT], 0);
+}
+
+static void
+egg_find_bar_emit_previous (EggFindBar *find_bar)
+{
+ g_signal_emit (find_bar, find_bar_signals[PREVIOUS], 0);
+}
+
+static void
+next_clicked_callback (GtkButton *button,
+ void *data)
+{
+ EggFindBar *find_bar = EGG_FIND_BAR (data);
+
+ egg_find_bar_emit_next (find_bar);
+}
+
+static void
+previous_clicked_callback (GtkButton *button,
+ void *data)
+{
+ EggFindBar *find_bar = EGG_FIND_BAR (data);
+
+ egg_find_bar_emit_previous (find_bar);
+}
+
+static void
+case_sensitive_toggled_callback (GtkCheckButton *button,
+ void *data)
+{
+ EggFindBar *find_bar = EGG_FIND_BAR (data);
+
+ egg_find_bar_set_case_sensitive (find_bar,
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)));
+}
+
+static void
+entry_activate_callback (GtkEntry *entry,
+ void *data)
+{
+ EggFindBar *find_bar = EGG_FIND_BAR (data);
+
+ if (find_bar->priv->search_string != NULL)
+ egg_find_bar_emit_next (find_bar);
+}
+
+static void
+entry_changed_callback (GtkEntry *entry,
+ void *data)
+{
+ EggFindBar *find_bar = EGG_FIND_BAR (data);
+ char *text;
+
+ /* paranoid strdup because set_search_string() sets
+ * the entry text
+ */
+ text = g_strdup (gtk_entry_get_text (entry));
+
+ egg_find_bar_set_search_string (find_bar, text);
+
+ 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)
+{
+ EggFindBarPrivate *priv;
+ GtkWidget *label;
+ GtkWidget *alignment;
+ GtkWidget *box;
+ GtkToolItem *item;
+ GtkWidget *arrow;
+
+ /* Data */
+ priv = EGG_FIND_BAR_GET_PRIVATE (find_bar);
+
+ find_bar->priv = priv;
+ priv->search_string = NULL;
+
+ gtk_toolbar_set_style (GTK_TOOLBAR (find_bar), GTK_TOOLBAR_BOTH_HORIZ);
+
+ /* Find: |_____| */
+ item = gtk_tool_item_new ();
+ box = gtk_hbox_new (FALSE, 12);
+
+ alignment = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 2, 2);
+
+ label = gtk_label_new_with_mnemonic (_("Find:"));
+
+ priv->find_entry = gtk_entry_new ();
+ gtk_entry_set_width_chars (GTK_ENTRY (priv->find_entry), 32);
+ gtk_entry_set_max_length (GTK_ENTRY (priv->find_entry), 512);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->find_entry);
+
+ /* Prev */
+ arrow = gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE);
+ priv->previous_button = gtk_tool_button_new (arrow, Q_("Find Pre_vious"));
+ gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (priv->previous_button), TRUE);
+ gtk_tool_item_set_is_important (priv->previous_button, TRUE);
+ gtk_widget_set_tooltip_text (GTK_WIDGET (priv->previous_button),
+ _("Find previous occurrence of the search string"));
+
+ /* Next */
+ arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
+ priv->next_button = gtk_tool_button_new (arrow, Q_("Find Ne_xt"));
+ gtk_tool_button_set_use_underline (GTK_TOOL_BUTTON (priv->next_button), TRUE);
+ gtk_tool_item_set_is_important (priv->next_button, TRUE);
+ gtk_widget_set_tooltip_text (GTK_WIDGET (priv->next_button),
+ _("Find next occurrence of the search string"));
+
+ /* Separator*/
+ priv->status_separator = gtk_separator_tool_item_new();
+
+ /* Case button */
+ priv->case_button = gtk_toggle_tool_button_new ();
+ g_object_set (G_OBJECT (priv->case_button), "label", _("C_ase Sensitive"), NULL);
+ gtk_tool_item_set_is_important (priv->case_button, TRUE);
+ gtk_widget_set_tooltip_text (GTK_WIDGET (priv->case_button),
+ _("Toggle case sensitive search"));
+
+ /* Status */
+ priv->status_item = gtk_tool_item_new();
+ gtk_tool_item_set_expand (priv->status_item, TRUE);
+ priv->status_label = gtk_label_new (NULL);
+ gtk_label_set_ellipsize (GTK_LABEL (priv->status_label),
+ PANGO_ELLIPSIZE_END);
+ gtk_misc_set_alignment (GTK_MISC (priv->status_label), 0.0, 0.5);
+
+
+ g_signal_connect (priv->find_entry, "changed",
+ G_CALLBACK (entry_changed_callback),
+ find_bar);
+ g_signal_connect (priv->find_entry, "activate",
+ G_CALLBACK (entry_activate_callback),
+ find_bar);
+ g_signal_connect (priv->next_button, "clicked",
+ G_CALLBACK (next_clicked_callback),
+ find_bar);
+ g_signal_connect (priv->previous_button, "clicked",
+ G_CALLBACK (previous_clicked_callback),
+ find_bar);
+ g_signal_connect (priv->case_button, "toggled",
+ G_CALLBACK (case_sensitive_toggled_callback),
+ find_bar);
+
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box), priv->find_entry, TRUE, TRUE, 0);
+ gtk_container_add (GTK_CONTAINER (alignment), box);
+ gtk_container_add (GTK_CONTAINER (item), alignment);
+ gtk_toolbar_insert (GTK_TOOLBAR (find_bar), item, -1);
+ gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->previous_button, -1);
+ gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->next_button, -1);
+ gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->case_button, -1);
+ gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->status_separator, -1);
+ gtk_container_add (GTK_CONTAINER (priv->status_item), priv->status_label);
+ gtk_toolbar_insert (GTK_TOOLBAR (find_bar), priv->status_item, -1);
+
+ /* don't show status separator/label until they are set */
+
+ gtk_widget_show_all (GTK_WIDGET (item));
+ gtk_widget_show_all (GTK_WIDGET (priv->next_button));
+ gtk_widget_show_all (GTK_WIDGET (priv->previous_button));
+ gtk_widget_show (priv->status_label);
+}
+
+static void
+egg_find_bar_finalize (GObject *object)
+{
+ EggFindBar *find_bar = EGG_FIND_BAR (object);
+ EggFindBarPrivate *priv = (EggFindBarPrivate *)find_bar->priv;
+
+ g_free (priv->search_string);
+
+ G_OBJECT_CLASS (egg_find_bar_parent_class)->finalize (object);
+}
+
+static void
+egg_find_bar_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EggFindBar *find_bar = EGG_FIND_BAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_SEARCH_STRING:
+ egg_find_bar_set_search_string (find_bar, g_value_get_string (value));
+ break;
+ case PROP_CASE_SENSITIVE:
+ egg_find_bar_set_case_sensitive (find_bar, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+egg_find_bar_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EggFindBar *find_bar = EGG_FIND_BAR (object);
+ EggFindBarPrivate *priv = (EggFindBarPrivate *)find_bar->priv;
+
+ switch (prop_id)
+ {
+ case PROP_SEARCH_STRING:
+ g_value_set_string (value, priv->search_string);
+ break;
+ case PROP_CASE_SENSITIVE:
+ g_value_set_boolean (value, priv->case_sensitive);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+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);
+}
+
+static void
+egg_find_bar_grab_focus (GtkWidget *widget)
+{
+ EggFindBar *find_bar = EGG_FIND_BAR (widget);
+ EggFindBarPrivate *priv = find_bar->priv;
+
+ gtk_widget_grab_focus (priv->find_entry);
+}
+
+/**
+ * egg_find_bar_new:
+ *
+ * Creates a new #EggFindBar.
+ *
+ * Returns: a newly created #EggFindBar
+ *
+ * Since: 2.6
+ */
+GtkWidget *
+egg_find_bar_new (void)
+{
+ EggFindBar *find_bar;
+
+ find_bar = g_object_new (EGG_TYPE_FIND_BAR, NULL);
+
+ return GTK_WIDGET (find_bar);
+}
+
+/**
+ * egg_find_bar_set_search_string:
+ *
+ * Sets the string that should be found/highlighted in the document.
+ * Empty string is converted to NULL.
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_set_search_string (EggFindBar *find_bar,
+ const char *search_string)
+{
+ EggFindBarPrivate *priv;
+
+ g_return_if_fail (EGG_IS_FIND_BAR (find_bar));
+
+ priv = (EggFindBarPrivate *)find_bar->priv;
+
+ g_object_freeze_notify (G_OBJECT (find_bar));
+
+ if (priv->search_string != search_string)
+ {
+ char *old;
+
+ old = priv->search_string;
+
+ if (search_string && *search_string == '\0')
+ search_string = NULL;
+
+ /* Only update if the string has changed; setting the entry
+ * will emit changed on the entry which will re-enter
+ * this function, but we'll handle that fine with this
+ * short-circuit.
+ */
+ if ((old && search_string == NULL) ||
+ (old == NULL && search_string) ||
+ (old && search_string &&
+ strcmp (old, search_string) != 0))
+ {
+ gboolean not_empty;
+
+ priv->search_string = g_strdup (search_string);
+ g_free (old);
+
+ gtk_entry_set_text (GTK_ENTRY (priv->find_entry),
+ priv->search_string ?
+ priv->search_string :
+ "");
+
+ not_empty = (search_string == NULL) ? FALSE : TRUE;
+
+ gtk_widget_set_sensitive (GTK_WIDGET (find_bar->priv->next_button), not_empty);
+ gtk_widget_set_sensitive (GTK_WIDGET (find_bar->priv->previous_button), not_empty);
+
+ g_object_notify (G_OBJECT (find_bar),
+ "search_string");
+ }
+ }
+
+ g_object_thaw_notify (G_OBJECT (find_bar));
+}
+
+
+/**
+ * egg_find_bar_get_search_string:
+ *
+ * Gets the string that should be found/highlighted in the document.
+ *
+ * Returns: the string
+ *
+ * Since: 2.6
+ */
+const char*
+egg_find_bar_get_search_string (EggFindBar *find_bar)
+{
+ EggFindBarPrivate *priv;
+
+ g_return_val_if_fail (EGG_IS_FIND_BAR (find_bar), NULL);
+
+ priv = find_bar->priv;
+
+ return priv->search_string ? priv->search_string : "";
+}
+
+/**
+ * egg_find_bar_set_case_sensitive:
+ *
+ * Sets whether the search is case sensitive
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_set_case_sensitive (EggFindBar *find_bar,
+ gboolean case_sensitive)
+{
+ EggFindBarPrivate *priv;
+
+ g_return_if_fail (EGG_IS_FIND_BAR (find_bar));
+
+ priv = (EggFindBarPrivate *)find_bar->priv;
+
+ g_object_freeze_notify (G_OBJECT (find_bar));
+
+ case_sensitive = case_sensitive != FALSE;
+
+ if (priv->case_sensitive != case_sensitive)
+ {
+ priv->case_sensitive = case_sensitive;
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->case_button),
+ priv->case_sensitive);
+
+ g_object_notify (G_OBJECT (find_bar),
+ "case_sensitive");
+ }
+
+ g_object_thaw_notify (G_OBJECT (find_bar));
+}
+
+/**
+ * egg_find_bar_get_case_sensitive:
+ *
+ * Gets whether the search is case sensitive
+ *
+ * Returns: TRUE if it's case sensitive
+ *
+ * Since: 2.6
+ */
+gboolean
+egg_find_bar_get_case_sensitive (EggFindBar *find_bar)
+{
+ EggFindBarPrivate *priv;
+
+ g_return_val_if_fail (EGG_IS_FIND_BAR (find_bar), FALSE);
+
+ priv = (EggFindBarPrivate *)find_bar->priv;
+
+ return priv->case_sensitive;
+}
+
+static void
+get_style_color (EggFindBar *find_bar,
+ const char *style_prop_name,
+ GdkColor *color)
+{
+ GdkColor *style_color;
+
+ gtk_widget_ensure_style (GTK_WIDGET (find_bar));
+ gtk_widget_style_get (GTK_WIDGET (find_bar),
+ "color", &style_color, NULL);
+ if (style_color)
+ {
+ *color = *style_color;
+ gdk_color_free (style_color);
+ }
+}
+
+/**
+ * egg_find_bar_get_all_matches_color:
+ *
+ * Gets the color to use to highlight all the
+ * known matches.
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_get_all_matches_color (EggFindBar *find_bar,
+ GdkColor *color)
+{
+ GdkColor found_color = { 0, 0, 0, 0x0f0f };
+
+ get_style_color (find_bar, "all_matches_color", &found_color);
+
+ *color = found_color;
+}
+
+/**
+ * egg_find_bar_get_current_match_color:
+ *
+ * Gets the color to use to highlight the match
+ * we're currently on.
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_get_current_match_color (EggFindBar *find_bar,
+ GdkColor *color)
+{
+ GdkColor found_color = { 0, 0, 0, 0xffff };
+
+ get_style_color (find_bar, "current_match_color", &found_color);
+
+ *color = found_color;
+}
+
+/**
+ * egg_find_bar_set_status_text:
+ *
+ * Sets some text to display if there's space; typical text would
+ * be something like "5 results on this page" or "No results"
+ *
+ * @text: the text to display
+ *
+ * Since: 2.6
+ */
+void
+egg_find_bar_set_status_text (EggFindBar *find_bar,
+ const char *text)
+{
+ EggFindBarPrivate *priv;
+
+ g_return_if_fail (EGG_IS_FIND_BAR (find_bar));
+
+ priv = (EggFindBarPrivate *)find_bar->priv;
+
+ gtk_label_set_text (GTK_LABEL (priv->status_label), text);
+ g_object_set (priv->status_separator, "visible", text != NULL && *text != '\0', NULL);
+ g_object_set (priv->status_item, "visible", text != NULL && *text !='\0', NULL);
+}
diff --git a/shell/eggfindbar.h b/shell/eggfindbar.h
new file mode 100644
index 00000000..7a7487e3
--- /dev/null
+++ b/shell/eggfindbar.h
@@ -0,0 +1,82 @@
+/* Copyright (C) 2004 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the Mate Library; see the file COPYING.LIB. If not,
+ * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __EGG_FIND_BAR_H__
+#define __EGG_FIND_BAR_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_FIND_BAR (egg_find_bar_get_type ())
+#define EGG_FIND_BAR(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), EGG_TYPE_FIND_BAR, EggFindBar))
+#define EGG_FIND_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_FIND_BAR, EggFindBarClass))
+#define EGG_IS_FIND_BAR(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), EGG_TYPE_FIND_BAR))
+#define EGG_IS_FIND_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_FIND_BAR))
+#define EGG_FIND_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_FIND_BAR, EggFindBarClass))
+
+typedef struct _EggFindBar EggFindBar;
+typedef struct _EggFindBarClass EggFindBarClass;
+typedef struct _EggFindBarPrivate EggFindBarPrivate;
+
+struct _EggFindBar
+{
+ GtkToolbar parent;
+
+ /*< private >*/
+ EggFindBarPrivate *priv;
+};
+
+struct _EggFindBarClass
+{
+ GtkToolbarClass parent_class;
+
+ void (* next) (EggFindBar *find_bar);
+ void (* previous) (EggFindBar *find_bar);
+ void (* close) (EggFindBar *find_bar);
+ void (* scroll) (EggFindBar *find_bar, GtkScrollType* scroll);
+
+ /* Padding for future expansion */
+ void (*_gtk_reserved1) (void);
+ void (*_gtk_reserved2) (void);
+ void (*_gtk_reserved3) (void);
+ void (*_gtk_reserved4) (void);
+};
+
+GType egg_find_bar_get_type (void) G_GNUC_CONST;
+GtkWidget *egg_find_bar_new (void);
+
+void egg_find_bar_set_search_string (EggFindBar *find_bar,
+ const char *search_string);
+const char* egg_find_bar_get_search_string (EggFindBar *find_bar);
+void egg_find_bar_set_case_sensitive (EggFindBar *find_bar,
+ gboolean case_sensitive);
+gboolean egg_find_bar_get_case_sensitive (EggFindBar *find_bar);
+void egg_find_bar_get_all_matches_color (EggFindBar *find_bar,
+ GdkColor *color);
+void egg_find_bar_get_current_match_color (EggFindBar *find_bar,
+ GdkColor *color);
+void egg_find_bar_set_status_text (EggFindBar *find_bar,
+ const char *text);
+
+G_END_DECLS
+
+#endif /* __EGG_FIND_BAR_H__ */
+
+
diff --git a/shell/ev-annotation-properties-dialog.c b/shell/ev-annotation-properties-dialog.c
new file mode 100644
index 00000000..8828eba4
--- /dev/null
+++ b/shell/ev-annotation-properties-dialog.c
@@ -0,0 +1,327 @@
+/* ev-annotation-properties-dialog.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <[email protected]>
+ *
+ * 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 <glib/gi18n.h>
+
+#include "ev-annotation-properties-dialog.h"
+
+enum {
+ PROP_0,
+ PROP_ANNOT_TYPE
+};
+
+struct _EvAnnotationPropertiesDialog {
+ GtkDialog base_instance;
+
+ EvAnnotationType annot_type;
+ EvAnnotation *annot;
+
+ GtkWidget *table;
+
+ GtkWidget *author;
+ GtkWidget *color;
+ GtkWidget *opacity;
+ GtkWidget *popup_state;
+
+ /* Text Annotations */
+ GtkWidget *icon;
+};
+
+struct _EvAnnotationPropertiesDialogClass {
+ GtkDialogClass base_class;
+};
+
+G_DEFINE_TYPE (EvAnnotationPropertiesDialog, ev_annotation_properties_dialog, GTK_TYPE_DIALOG)
+
+static void
+ev_annotation_properties_dialog_dispose (GObject *object)
+{
+ EvAnnotationPropertiesDialog *dialog = EV_ANNOTATION_PROPERTIES_DIALOG (object);
+
+ if (dialog->annot) {
+ g_object_unref (dialog->annot);
+ dialog->annot = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_annotation_properties_dialog_parent_class)->dispose (object);
+}
+
+static void
+ev_annotation_properties_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EvAnnotationPropertiesDialog *dialog = EV_ANNOTATION_PROPERTIES_DIALOG (object);
+
+ switch (prop_id) {
+ case PROP_ANNOT_TYPE:
+ dialog->annot_type = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ev_annotation_properties_dialog_constructed (GObject *object)
+{
+ EvAnnotationPropertiesDialog *dialog = EV_ANNOTATION_PROPERTIES_DIALOG (object);
+ GtkWidget *contant_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ GtkWidget *table = dialog->table;
+ GtkWidget *label;
+
+ contant_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ switch (dialog->annot_type) {
+ case EV_ANNOTATION_TYPE_TEXT:
+ label = gtk_label_new (_("Icon:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 5, 6,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ dialog->icon = gtk_combo_box_new_text ();
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Note"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Comment"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Key"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Help"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("New Paragraph"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Paragraph"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Insert"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Cross"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Circle"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (dialog->icon), _("Unknown"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->icon), 0);
+ gtk_table_attach (GTK_TABLE (table), dialog->icon,
+ 1, 2, 5, 6,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (dialog->icon);
+
+ break;
+ case EV_ANNOTATION_TYPE_ATTACHMENT:
+ /* TODO */
+ default:
+ break;
+ }
+}
+
+static void
+ev_annotation_properties_dialog_init (EvAnnotationPropertiesDialog *annot_dialog)
+{
+ GtkDialog *dialog = GTK_DIALOG (annot_dialog);
+ GtkWidget *content_area;
+ GtkWidget *label;
+ GtkWidget *table;
+ GtkWidget *hbox;
+ gchar *markup;
+ GdkColor color = { 0, 65535, 65535, 0 };
+
+ gtk_window_set_title (GTK_WINDOW (annot_dialog), _("Annotation Properties"));
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (annot_dialog), TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (annot_dialog), 5);
+ gtk_dialog_set_has_separator (dialog, FALSE);
+ gtk_dialog_add_buttons (dialog,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+ GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
+ NULL);
+ gtk_dialog_set_default_response (dialog, GTK_RESPONSE_APPLY);
+
+ content_area = gtk_dialog_get_content_area (dialog);
+ gtk_box_set_spacing (GTK_BOX (content_area), 2);
+
+ table = gtk_table_new (5, 2, FALSE);
+ annot_dialog->table = table;
+ gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (table), 12);
+ gtk_box_pack_start (GTK_BOX (content_area), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ label = gtk_label_new (_("Author:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ annot_dialog->author = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (annot_dialog->author), g_get_real_name ());
+ gtk_table_attach (GTK_TABLE (table), annot_dialog->author,
+ 1, 2, 0, 1,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (annot_dialog->author);
+
+ label = gtk_label_new (_("Color:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ annot_dialog->color = gtk_color_button_new_with_color (&color);
+ gtk_table_attach (GTK_TABLE (table), annot_dialog->color,
+ 1, 2, 1, 2,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (annot_dialog->color);
+
+ label = gtk_label_new (_("Style:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ annot_dialog->opacity = gtk_hscale_new_with_range (0, 100, 5);
+ gtk_range_set_value (GTK_RANGE (annot_dialog->opacity), 100);
+ gtk_table_attach (GTK_TABLE (table), annot_dialog->opacity,
+ 1, 2, 2, 3,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (annot_dialog->opacity);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+
+ label = gtk_label_new (NULL);
+ markup = g_strdup_printf ("<small>%s</small>", _("Transparent"));
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (NULL);
+ markup = g_strdup_printf ("<small>%s</small>", _("Opaque"));
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+ gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_table_attach (GTK_TABLE (table), hbox,
+ 1, 2, 3, 4,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (hbox);
+
+ label = gtk_label_new (_("Initial window state:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0., 0.5);
+ gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ annot_dialog->popup_state = gtk_combo_box_new_text ();
+ gtk_combo_box_append_text (GTK_COMBO_BOX (annot_dialog->popup_state), _("Open"));
+ gtk_combo_box_append_text (GTK_COMBO_BOX (annot_dialog->popup_state), _("Close"));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (annot_dialog->popup_state), 1);
+ gtk_table_attach (GTK_TABLE (table), annot_dialog->popup_state,
+ 1, 2, 4, 5,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (annot_dialog->popup_state);
+}
+
+static void
+ev_annotation_properties_dialog_class_init (EvAnnotationPropertiesDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ev_annotation_properties_dialog_dispose;
+ object_class->constructed = ev_annotation_properties_dialog_constructed;
+ object_class->set_property = ev_annotation_properties_dialog_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_ANNOT_TYPE,
+ g_param_spec_enum ("annot-type",
+ "AnnotType",
+ "The type of annotation",
+ EV_TYPE_ANNOTATION_TYPE,
+ EV_ANNOTATION_TYPE_TEXT,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+GtkWidget *
+ev_annotation_properties_dialog_new (EvAnnotationType annot_type)
+{
+ return GTK_WIDGET (g_object_new (EV_TYPE_ANNOTATION_PROPERTIES_DIALOG,
+ "annot-type", annot_type,
+ NULL));
+}
+
+GtkWidget *
+ev_annotation_properties_dialog_new_with_annotation (EvAnnotation *annot)
+{
+ EvAnnotationPropertiesDialog *dialog;
+ const gchar *label;
+ gdouble opacity;
+ gboolean is_open;
+ GdkColor color;
+
+ dialog = (EvAnnotationPropertiesDialog *)ev_annotation_properties_dialog_new (ev_annotation_get_annotation_type (annot));
+ dialog->annot = g_object_ref (annot);
+
+ label = ev_annotation_markup_get_label (EV_ANNOTATION_MARKUP (annot));
+ if (label)
+ gtk_entry_set_text (GTK_ENTRY (dialog->author), label);
+
+ ev_annotation_get_color (annot, &color);
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (dialog->color), &color);
+
+ opacity = ev_annotation_markup_get_opacity (EV_ANNOTATION_MARKUP (annot));
+ gtk_range_set_value (GTK_RANGE (dialog->opacity), opacity * 100);
+
+ is_open = ev_annotation_markup_get_popup_is_open (EV_ANNOTATION_MARKUP (annot));
+ gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->popup_state),
+ is_open ? 0 : 1);
+
+ if (EV_IS_ANNOTATION_TEXT (annot)) {
+ EvAnnotationText *annot_text = EV_ANNOTATION_TEXT (annot);
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->icon),
+ ev_annotation_text_get_icon (annot_text));
+ }
+
+ return GTK_WIDGET (dialog);
+}
+
+const gchar *
+ev_annotation_properties_dialog_get_author (EvAnnotationPropertiesDialog *dialog)
+{
+ return gtk_entry_get_text (GTK_ENTRY (dialog->author));
+}
+
+void
+ev_annotation_properties_dialog_get_color (EvAnnotationPropertiesDialog *dialog,
+ GdkColor *color)
+{
+ gtk_color_button_get_color (GTK_COLOR_BUTTON (dialog->color), color);
+}
+
+gdouble
+ev_annotation_properties_dialog_get_opacity (EvAnnotationPropertiesDialog *dialog)
+{
+ return gtk_range_get_value (GTK_RANGE (dialog->opacity)) / 100;
+}
+
+gboolean
+ev_annotation_properties_dialog_get_popup_is_open (EvAnnotationPropertiesDialog *dialog)
+{
+ return gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->popup_state)) == 0;
+}
+
+EvAnnotationTextIcon
+ev_annotation_properties_dialog_get_text_icon (EvAnnotationPropertiesDialog *dialog)
+{
+ return gtk_combo_box_get_active (GTK_COMBO_BOX (dialog->icon));
+}
diff --git a/shell/ev-annotation-properties-dialog.h b/shell/ev-annotation-properties-dialog.h
new file mode 100644
index 00000000..eee533e1
--- /dev/null
+++ b/shell/ev-annotation-properties-dialog.h
@@ -0,0 +1,54 @@
+/* ev-annotation-properties-dialog.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <[email protected]>
+ *
+ * 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_ANNOTATION_PROPERTIES_DIALOG_H__
+#define __EV_ANNOTATION_PROPERTIES_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+#include <evince-document.h>
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_ANNOTATION_PROPERTIES_DIALOG (ev_annotation_properties_dialog_get_type())
+#define EV_ANNOTATION_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST((o), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG, EvAnnotationPropertiesDialog))
+#define EV_ANNOTATION_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG, EvAnnotationPropertiesDialogClass))
+#define EV_IS_ANNOTATION_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG))
+#define EV_IS_ANNOTATION_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG))
+#define EV_ANNOTATION_PROPERTIES_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), EV_TYPE_ANNOTATION_PROPERTIES_DIALOG, EvAnnotationPropertiesDialogClass))
+
+typedef struct _EvAnnotationPropertiesDialog EvAnnotationPropertiesDialog;
+typedef struct _EvAnnotationPropertiesDialogClass EvAnnotationPropertiesDialogClass;
+
+GType ev_annotation_properties_dialog_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_annotation_properties_dialog_new (EvAnnotationType annot_type);
+GtkWidget *ev_annotation_properties_dialog_new_with_annotation (EvAnnotation *annot);
+
+const gchar *ev_annotation_properties_dialog_get_author (EvAnnotationPropertiesDialog *dialog);
+void ev_annotation_properties_dialog_get_color (EvAnnotationPropertiesDialog *dialog,
+ GdkColor *color);
+gdouble ev_annotation_properties_dialog_get_opacity (EvAnnotationPropertiesDialog *dialog);
+gboolean ev_annotation_properties_dialog_get_popup_is_open (EvAnnotationPropertiesDialog *dialog);
+EvAnnotationTextIcon ev_annotation_properties_dialog_get_text_icon (EvAnnotationPropertiesDialog *dialog);
+
+G_END_DECLS
+
+#endif /* __EV_ANNOTATION_PROPERTIES_DIALOG_H__ */
diff --git a/shell/ev-application.c b/shell/ev-application.c
new file mode 100644
index 00000000..af70617a
--- /dev/null
+++ b/shell/ev-application.c
@@ -0,0 +1,1151 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2004 Martin Kretzschmar
+ * Copyright Ā© 2010 Christian Persch
+ *
+ * Author:
+ * Martin Kretzschmar <[email protected]>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+#include <unistd.h>
+
+#include "totem-scrsaver.h"
+
+#ifdef WITH_SMCLIENT
+#include "eggsmclient.h"
+#endif
+
+#include "ev-application.h"
+#include "ev-file-helpers.h"
+#include "ev-stock-icons.h"
+
+#ifdef ENABLE_DBUS
+#include "ev-media-player-keys.h"
+#endif /* ENABLE_DBUS */
+
+struct _EvApplication {
+ GObject base_instance;
+
+ gchar *uri;
+
+ gchar *dot_dir;
+ gchar *data_dir;
+
+#ifdef ENABLE_DBUS
+ GDBusConnection *connection;
+ guint registration_id;
+ EvMediaPlayerKeys *keys;
+ gboolean doc_registered;
+#endif
+
+ TotemScrsaver *scr_saver;
+
+#ifdef WITH_SMCLIENT
+ EggSMClient *smclient;
+#endif
+
+ gchar *filechooser_open_uri;
+ gchar *filechooser_save_uri;
+};
+
+struct _EvApplicationClass {
+ GObjectClass base_class;
+};
+
+static EvApplication *instance;
+
+G_DEFINE_TYPE (EvApplication, ev_application, G_TYPE_OBJECT);
+
+#ifdef ENABLE_DBUS
+#define APPLICATION_DBUS_OBJECT_PATH "/org/mate/evince/Evince"
+#define APPLICATION_DBUS_INTERFACE "org.mate.evince.Application"
+
+#define EVINCE_DAEMON_SERVICE "org.mate.evince.Daemon"
+#define EVINCE_DAEMON_OBJECT_PATH "/org/mate/evince/Daemon"
+#define EVINCE_DAEMON_INTERFACE "org.mate.evince.Daemon"
+#endif
+
+static const gchar *userdir = NULL;
+
+static void _ev_application_open_uri_at_dest (EvApplication *application,
+ const gchar *uri,
+ GdkScreen *screen,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string,
+ guint timestamp);
+static void ev_application_open_uri_in_window (EvApplication *application,
+ const char *uri,
+ EvWindow *ev_window,
+ GdkScreen *screen,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string,
+ guint timestamp);
+
+/**
+ * ev_application_get_instance:
+ *
+ * Checks for #EvApplication instance, if it doesn't exist it does create it.
+ *
+ * Returns: an instance of the #EvApplication data.
+ */
+EvApplication *
+ev_application_get_instance (void)
+{
+ if (!instance) {
+ instance = EV_APPLICATION (g_object_new (EV_TYPE_APPLICATION, NULL));
+ }
+
+ return instance;
+}
+
+/* Session */
+gboolean
+ev_application_load_session (EvApplication *application)
+{
+ GKeyFile *state_file;
+ gchar *uri;
+
+#ifdef WITH_SMCLIENT
+ if (egg_sm_client_is_resumed (application->smclient)) {
+ state_file = egg_sm_client_get_state_file (application->smclient);
+ if (!state_file)
+ return FALSE;
+ } else
+#endif /* WITH_SMCLIENT */
+ return FALSE;
+
+ uri = g_key_file_get_string (state_file, "Evince", "uri", NULL);
+ if (!uri)
+ return FALSE;
+
+ ev_application_open_uri_at_dest (application, uri,
+ gdk_screen_get_default (),
+ NULL, 0, NULL,
+ GDK_CURRENT_TIME);
+ g_free (uri);
+ g_key_file_free (state_file);
+
+ return TRUE;
+}
+
+#ifdef WITH_SMCLIENT
+
+static void
+smclient_save_state_cb (EggSMClient *client,
+ GKeyFile *state_file,
+ EvApplication *application)
+{
+ if (!application->uri)
+ return;
+
+ g_key_file_set_string (state_file, "Evince", "uri", application->uri);
+}
+
+static void
+smclient_quit_cb (EggSMClient *client,
+ EvApplication *application)
+{
+ ev_application_shutdown (application);
+}
+#endif /* WITH_SMCLIENT */
+
+static void
+ev_application_init_session (EvApplication *application)
+{
+#ifdef WITH_SMCLIENT
+ application->smclient = egg_sm_client_get ();
+ g_signal_connect (application->smclient, "save_state",
+ G_CALLBACK (smclient_save_state_cb),
+ application);
+ g_signal_connect (application->smclient, "quit",
+ G_CALLBACK (smclient_quit_cb),
+ application);
+#endif
+}
+
+/**
+ * ev_display_open_if_needed:
+ * @name: the name of the display to be open if it's needed.
+ *
+ * Search among all the open displays if any of them have the same name as the
+ * passed name. If the display isn't found it tries the open it.
+ *
+ * Returns: a #GdkDisplay of the display with the passed name.
+ */
+static GdkDisplay *
+ev_display_open_if_needed (const gchar *name)
+{
+ GSList *displays;
+ GSList *l;
+ GdkDisplay *display = NULL;
+
+ displays = gdk_display_manager_list_displays (gdk_display_manager_get ());
+
+ for (l = displays; l != NULL; l = l->next) {
+ const gchar *display_name = gdk_display_get_name ((GdkDisplay *) l->data);
+
+ if (g_ascii_strcasecmp (display_name, name) == 0) {
+ display = l->data;
+ break;
+ }
+ }
+
+ g_slist_free (displays);
+
+ return display != NULL ? display : gdk_display_open (name);
+}
+
+static void
+child_setup (gpointer user_data)
+{
+ gchar *startup_id;
+
+ startup_id = g_strdup_printf ("_TIME%lu",
+ (unsigned long)GPOINTER_TO_INT (user_data));
+ g_setenv ("DESKTOP_STARTUP_ID", startup_id, TRUE);
+ g_free (startup_id);
+}
+
+static void
+ev_spawn (const char *uri,
+ GdkScreen *screen,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string,
+ guint timestamp)
+{
+ gchar *argv[6];
+ guint arg = 0;
+ gint i;
+ gboolean res;
+ GError *error = NULL;
+
+#ifdef G_OS_WIN32
+{
+ gchar *dir;
+
+ dir = g_win32_get_package_installation_directory_of_module (NULL);
+ argv[arg++] = g_build_filename (dir, "bin", "evince", NULL);
+ g_free (dir);
+}
+#else
+ argv[arg++] = g_build_filename (BINDIR, "evince", NULL);
+#endif
+
+ /* Page label */
+ if (dest) {
+ const gchar *page_label;
+
+ page_label = ev_link_dest_get_page_label (dest);
+ if (page_label)
+ argv[arg++] = g_strdup_printf ("--page-label=%s", page_label);
+ else
+ argv[arg++] = g_strdup_printf ("--page-label=%d",
+ ev_link_dest_get_page (dest));
+ }
+
+ /* Find string */
+ if (search_string) {
+ argv[arg++] = g_strdup_printf ("--find=%s", search_string);
+ }
+
+ /* Mode */
+ switch (mode) {
+ case EV_WINDOW_MODE_FULLSCREEN:
+ argv[arg++] = g_strdup ("-f");
+ break;
+ case EV_WINDOW_MODE_PRESENTATION:
+ argv[arg++] = g_strdup ("-s");
+ break;
+ default:
+ break;
+ }
+
+ argv[arg++] = (gchar *)uri;
+ argv[arg] = NULL;
+
+ res = gdk_spawn_on_screen (screen, NULL /* wd */, argv, NULL /* env */,
+ 0,
+ child_setup,
+ GINT_TO_POINTER(timestamp),
+ NULL, &error);
+ if (!res) {
+ g_warning ("Error launching evince %s: %s\n", uri, error->message);
+ g_error_free (error);
+ }
+
+ for (i = 0; i < arg - 1; i++) {
+ g_free (argv[i]);
+ }
+}
+
+static GList *
+ev_application_get_windows (EvApplication *application)
+{
+ GList *l, *toplevels;
+ GList *windows = NULL;
+
+ toplevels = gtk_window_list_toplevels ();
+
+ for (l = toplevels; l != NULL; l = l->next) {
+ if (EV_IS_WINDOW (l->data)) {
+ windows = g_list_append (windows, l->data);
+ }
+ }
+
+ g_list_free (toplevels);
+
+ return windows;
+}
+
+static EvWindow *
+ev_application_get_empty_window (EvApplication *application,
+ GdkScreen *screen)
+{
+ EvWindow *empty_window = NULL;
+ GList *windows = ev_application_get_windows (application);
+ GList *l;
+
+ for (l = windows; l != NULL; l = l->next) {
+ EvWindow *window = EV_WINDOW (l->data);
+
+ if (ev_window_is_empty (window) &&
+ gtk_window_get_screen (GTK_WINDOW (window)) == screen) {
+ empty_window = window;
+ break;
+ }
+ }
+
+ g_list_free (windows);
+
+ return empty_window;
+}
+
+
+#ifdef ENABLE_DBUS
+typedef struct {
+ gchar *uri;
+ GdkScreen *screen;
+ EvLinkDest *dest;
+ EvWindowRunMode mode;
+ gchar *search_string;
+ guint timestamp;
+} EvRegisterDocData;
+
+static void
+ev_register_doc_data_free (EvRegisterDocData *data)
+{
+ if (!data)
+ return;
+
+ g_free (data->uri);
+ if (data->search_string)
+ g_free (data->search_string);
+ if (data->dest)
+ g_object_unref (data->dest);
+
+ g_free (data);
+}
+
+static void
+on_reload_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
+ GVariant *value;
+ GError *error = NULL;
+
+ value = g_dbus_connection_call_finish (connection, res, &error);
+ if (!value) {
+ g_warning ("Failed to Reload: %s", error->message);
+ g_error_free (error);
+ }
+ g_variant_unref (value);
+
+ ev_application_shutdown (EV_APP);
+}
+
+static void
+on_register_uri_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GDBusConnection *connection = G_DBUS_CONNECTION (source_object);
+ EvRegisterDocData *data = (EvRegisterDocData *)user_data;
+ EvApplication *application = EV_APP;
+ GVariant *value;
+ const gchar *owner;
+ GVariantBuilder builder;
+ GError *error = NULL;
+
+ value = g_dbus_connection_call_finish (connection, res, &error);
+ if (!value) {
+ g_warning ("Error registering document: %s\n", error->message);
+ g_error_free (error);
+
+ _ev_application_open_uri_at_dest (application,
+ data->uri,
+ data->screen,
+ data->dest,
+ data->mode,
+ data->search_string,
+ data->timestamp);
+ ev_register_doc_data_free (data);
+
+ return;
+ }
+
+ g_variant_get (value, "(&s)", &owner);
+
+ /* This means that the document wasn't already registered; go
+ * ahead with opening it.
+ */
+ if (owner[0] == '\0') {
+ g_variant_unref (value);
+
+ application->doc_registered = TRUE;
+
+ _ev_application_open_uri_at_dest (application,
+ data->uri,
+ data->screen,
+ data->dest,
+ data->mode,
+ data->search_string,
+ data->timestamp);
+ ev_register_doc_data_free (data);
+
+ return;
+ }
+
+ /* Already registered */
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a{sv}u)"));
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder, "{sv}",
+ "display",
+ g_variant_new_string (gdk_display_get_name (gdk_screen_get_display (data->screen))));
+ g_variant_builder_add (&builder, "{sv}",
+ "screen",
+ g_variant_new_int32 (gdk_screen_get_number (data->screen)));
+ if (data->dest) {
+ g_variant_builder_add (&builder, "{sv}",
+ "page-label",
+ g_variant_new_string (ev_link_dest_get_page_label (data->dest)));
+ }
+ if (data->search_string) {
+ g_variant_builder_add (&builder, "{sv}",
+ "find-string",
+ g_variant_new_string (data->search_string));
+ }
+ if (data->mode != EV_WINDOW_MODE_NORMAL) {
+ g_variant_builder_add (&builder, "{sv}",
+ "mode",
+ g_variant_new_uint32 (data->mode));
+ }
+ g_variant_builder_close (&builder);
+
+ g_variant_builder_add (&builder, "u", data->timestamp);
+
+ g_dbus_connection_call (connection,
+ owner,
+ APPLICATION_DBUS_OBJECT_PATH,
+ APPLICATION_DBUS_INTERFACE,
+ "Reload",
+ g_variant_builder_end (&builder),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ on_reload_cb,
+ NULL);
+ g_variant_unref (value);
+ ev_register_doc_data_free (data);
+}
+
+/*
+ * ev_application_register_uri:
+ * @application:
+ * @uri:
+ * @screen:
+ * @dest:
+ * @mode:
+ * @search_string:
+ * @timestamp:
+ *
+ * Registers @uri with evince-daemon.
+ *
+ */
+static void
+ev_application_register_uri (EvApplication *application,
+ const gchar *uri,
+ GdkScreen *screen,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string,
+ guint timestamp)
+{
+ EvRegisterDocData *data;
+
+ if (!application->connection)
+ return;
+
+ if (application->doc_registered) {
+ /* Already registered, reload */
+ GList *windows, *l;
+
+ windows = ev_application_get_windows (application);
+ for (l = windows; l != NULL; l = g_list_next (l)) {
+ EvWindow *ev_window = EV_WINDOW (l->data);
+
+ ev_application_open_uri_in_window (application, uri, ev_window,
+ screen, dest, mode,
+ search_string,
+ timestamp);
+ }
+ g_list_free (windows);
+
+ return;
+ }
+
+ data = g_new (EvRegisterDocData, 1);
+ data->uri = g_strdup (uri);
+ data->screen = screen;
+ data->dest = dest ? g_object_ref (dest) : NULL;
+ data->mode = mode;
+ data->search_string = search_string ? g_strdup (search_string) : NULL;
+ data->timestamp = timestamp;
+
+ g_dbus_connection_call (application->connection,
+ EVINCE_DAEMON_SERVICE,
+ EVINCE_DAEMON_OBJECT_PATH,
+ EVINCE_DAEMON_INTERFACE,
+ "RegisterDocument",
+ g_variant_new ("(s)", uri),
+ G_VARIANT_TYPE ("(s)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ on_register_uri_cb,
+ data);
+}
+
+static void
+ev_application_unregister_uri (EvApplication *application,
+ const gchar *uri)
+{
+ GVariant *value;
+ GError *error = NULL;
+
+ if (!application->doc_registered)
+ return;
+
+ /* This is called from ev_application_shutdown(),
+ * so it's safe to use the sync api
+ */
+ value = g_dbus_connection_call_sync (
+ application->connection,
+ EVINCE_DAEMON_SERVICE,
+ EVINCE_DAEMON_OBJECT_PATH,
+ EVINCE_DAEMON_INTERFACE,
+ "UnregisterDocument",
+ g_variant_new ("(s)", uri),
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+ if (value == NULL) {
+ g_warning ("Error unregistering document: %s\n", error->message);
+ g_error_free (error);
+ } else {
+ g_variant_unref (value);
+ }
+}
+#endif /* ENABLE_DBUS */
+
+static void
+ev_application_open_uri_in_window (EvApplication *application,
+ const char *uri,
+ EvWindow *ev_window,
+ GdkScreen *screen,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string,
+ guint timestamp)
+{
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *gdk_window;
+#endif
+
+ if (screen) {
+ ev_stock_icons_set_screen (screen);
+ gtk_window_set_screen (GTK_WINDOW (ev_window), screen);
+ }
+
+ /* We need to load uri before showing the window, so
+ we can restore window size without flickering */
+ ev_window_open_uri (ev_window, uri, dest, mode, search_string);
+
+ if (!gtk_widget_get_realized (GTK_WIDGET (ev_window)))
+ gtk_widget_realize (GTK_WIDGET (ev_window));
+
+#ifdef GDK_WINDOWING_X11
+ gdk_window = gtk_widget_get_window (GTK_WIDGET (ev_window));
+
+ if (timestamp <= 0)
+ timestamp = gdk_x11_get_server_time (gdk_window);
+ gdk_x11_window_set_user_time (gdk_window, timestamp);
+
+ gtk_window_present (GTK_WINDOW (ev_window));
+#else
+ gtk_window_present_with_time (GTK_WINDOW (ev_window), timestamp);
+#endif /* GDK_WINDOWING_X11 */
+}
+
+static void
+_ev_application_open_uri_at_dest (EvApplication *application,
+ const gchar *uri,
+ GdkScreen *screen,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string,
+ guint timestamp)
+{
+ EvWindow *ev_window;
+
+ ev_window = ev_application_get_empty_window (application, screen);
+ if (!ev_window)
+ ev_window = EV_WINDOW (ev_window_new ());
+
+ ev_application_open_uri_in_window (application, uri, ev_window,
+ screen, dest, mode,
+ search_string,
+ timestamp);
+}
+
+/**
+ * ev_application_open_uri_at_dest:
+ * @application: The instance of the application.
+ * @uri: The uri to be opened.
+ * @screen: Thee screen where the link will be shown.
+ * @dest: The #EvLinkDest of the document.
+ * @mode: The run mode of the window.
+ * @timestamp: Current time value.
+ */
+void
+ev_application_open_uri_at_dest (EvApplication *application,
+ const char *uri,
+ GdkScreen *screen,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string,
+ guint timestamp)
+{
+ g_return_if_fail (uri != NULL);
+
+ if (application->uri && strcmp (application->uri, uri) != 0) {
+ /* spawn a new evince process */
+ ev_spawn (uri, screen, dest, mode, search_string, timestamp);
+ return;
+ } else if (!application->uri) {
+ application->uri = g_strdup (uri);
+ }
+
+#ifdef ENABLE_DBUS
+ /* Register the uri or send Reload to
+ * remote instance if already registered
+ */
+ ev_application_register_uri (application, uri, screen, dest, mode, search_string, timestamp);
+#else
+ _ev_application_open_uri_at_dest (application, uri, screen, dest, mode, search_string, timestamp);
+#endif /* ENABLE_DBUS */
+}
+
+/**
+ * ev_application_open_window:
+ * @application: The instance of the application.
+ * @timestamp: Current time value.
+ *
+ * Creates a new window
+ */
+void
+ev_application_open_window (EvApplication *application,
+ GdkScreen *screen,
+ guint32 timestamp)
+{
+ GtkWidget *new_window = ev_window_new ();
+#ifdef GDK_WINDOWING_X11
+ GdkWindow *gdk_window;
+#endif
+
+ if (screen) {
+ ev_stock_icons_set_screen (screen);
+ gtk_window_set_screen (GTK_WINDOW (new_window), screen);
+ }
+
+ if (!gtk_widget_get_realized (new_window))
+ gtk_widget_realize (new_window);
+
+#ifdef GDK_WINDOWING_X11
+ gdk_window = gtk_widget_get_window (GTK_WIDGET (new_window));
+
+ if (timestamp <= 0)
+ timestamp = gdk_x11_get_server_time (gdk_window);
+ gdk_x11_window_set_user_time (gdk_window, timestamp);
+
+ gtk_window_present (GTK_WINDOW (new_window));
+#else
+ gtk_window_present_with_time (GTK_WINDOW (new_window), timestamp);
+#endif /* GDK_WINDOWING_X11 */
+}
+
+#ifdef ENABLE_DBUS
+static void
+method_call_cb (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ EvApplication *application = EV_APPLICATION (user_data);
+ GList *windows, *l;
+ guint timestamp;
+ GVariantIter *iter;
+ const gchar *key;
+ GVariant *value;
+ GdkDisplay *display = NULL;
+ int screen_number = 0;
+ EvLinkDest *dest = NULL;
+ EvWindowRunMode mode = EV_WINDOW_MODE_NORMAL;
+ const gchar *search_string = NULL;
+ GdkScreen *screen = NULL;
+
+ if (g_strcmp0 (method_name, "Reload") == 0) {
+ g_variant_get (parameters, "(a{sv}u)", &iter, &timestamp);
+
+ while (g_variant_iter_loop (iter, "{&sv}", &key, &value)) {
+ if (strcmp (key, "display") == 0 && g_variant_classify (value) == G_VARIANT_CLASS_STRING) {
+ display = ev_display_open_if_needed (g_variant_get_string (value, NULL));
+ } else if (strcmp (key, "screen") == 0 && g_variant_classify (value) == G_VARIANT_CLASS_STRING) {
+ screen_number = g_variant_get_int32 (value);
+ } else if (strcmp (key, "mode") == 0 && g_variant_classify (value) == G_VARIANT_CLASS_UINT32) {
+ mode = g_variant_get_uint32 (value);
+ } else if (strcmp (key, "page-label") == 0 && g_variant_classify (value) == G_VARIANT_CLASS_STRING) {
+ dest = ev_link_dest_new_page_label (g_variant_get_string (value, NULL));
+ } else if (strcmp (key, "find-string") == 0 && g_variant_classify (value) == G_VARIANT_CLASS_STRING) {
+ search_string = g_variant_get_string (value, NULL);
+ }
+ }
+ g_variant_iter_free (iter);
+
+ if (display != NULL &&
+ screen_number >= 0 &&
+ screen_number < gdk_display_get_n_screens (display))
+ screen = gdk_display_get_screen (display, screen_number);
+ else
+ screen = gdk_screen_get_default ();
+
+ windows = ev_application_get_windows (application);
+ for (l = windows; l != NULL; l = g_list_next (l)) {
+ EvWindow *ev_window = EV_WINDOW (l->data);
+
+ ev_application_open_uri_in_window (application, application->uri,
+ ev_window,
+ screen, dest, mode,
+ search_string,
+ timestamp);
+ }
+ g_list_free (windows);
+
+ if (dest)
+ g_object_unref (dest);
+
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
+ } else if (g_strcmp0 (method_name, "GetWindowList") == 0) {
+ GList *windows = ev_application_get_windows (application);
+ GVariantBuilder builder;
+ GList *l;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ao)"));
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("ao"));
+
+ for (l = windows; l; l = g_list_next (l)) {
+ EvWindow *window = (EvWindow *)l->data;
+
+ g_variant_builder_add (&builder, "o", ev_window_get_dbus_object_path (window));
+ }
+
+ g_variant_builder_close (&builder);
+ g_list_free (windows);
+
+ g_dbus_method_invocation_return_value (invocation, g_variant_builder_end (&builder));
+ }
+}
+
+static const char introspection_xml[] =
+ "<node>"
+ "<interface name='org.mate.evince.Application'>"
+ "<method name='Reload'>"
+ "<arg type='a{sv}' name='args' direction='in'/>"
+ "<arg type='u' name='timestamp' direction='in'/>"
+ "</method>"
+ "<method name='GetWindowList'>"
+ "<arg type='ao' name='window_list' direction='out'/>"
+ "</method>"
+ "</interface>"
+ "</node>";
+
+static const GDBusInterfaceVTable interface_vtable = {
+ method_call_cb,
+ NULL,
+ NULL
+};
+
+static GDBusNodeInfo *introspection_data;
+#endif /* ENABLE_DBUS */
+
+void
+ev_application_open_uri_list (EvApplication *application,
+ GSList *uri_list,
+ GdkScreen *screen,
+ guint timestamp)
+{
+ GSList *l;
+
+ for (l = uri_list; l != NULL; l = l->next) {
+ ev_application_open_uri_at_dest (application, (char *)l->data,
+ screen, NULL, 0, NULL,
+ timestamp);
+ }
+}
+
+static void
+ev_application_accel_map_save (EvApplication *application)
+{
+ gchar *accel_map_file;
+ gchar *tmp_filename;
+ gint fd;
+
+ if (userdir) {
+ accel_map_file = g_build_filename (userdir, "accels",
+ "evince", NULL);
+ } else {
+ accel_map_file = g_build_filename (g_get_home_dir (),
+ ".mate2", "accels",
+ "evince", NULL);
+ }
+
+ tmp_filename = g_strdup_printf ("%s.XXXXXX", accel_map_file);
+
+ fd = g_mkstemp (tmp_filename);
+ if (fd == -1) {
+ g_free (accel_map_file);
+ g_free (tmp_filename);
+
+ return;
+ }
+ gtk_accel_map_save_fd (fd);
+ close (fd);
+
+ if (g_rename (tmp_filename, accel_map_file) == -1) {
+ /* FIXME: win32? */
+ g_unlink (tmp_filename);
+ }
+
+ g_free (accel_map_file);
+ g_free (tmp_filename);
+}
+
+static void
+ev_application_accel_map_load (EvApplication *application)
+{
+ gchar *accel_map_file;
+
+ if (userdir) {
+ accel_map_file = g_build_filename (userdir, "accels",
+ "evince", NULL);
+ } else {
+ accel_map_file = g_build_filename (g_get_home_dir (),
+ ".mate2", "accels",
+ "evince", NULL);
+ }
+
+ gtk_accel_map_load (accel_map_file);
+ g_free (accel_map_file);
+}
+
+void
+ev_application_shutdown (EvApplication *application)
+{
+ if (application->uri) {
+#ifdef ENABLE_DBUS
+ ev_application_unregister_uri (application,
+ application->uri);
+#endif
+ g_free (application->uri);
+ application->uri = NULL;
+ }
+
+ ev_application_accel_map_save (application);
+
+ g_object_unref (application->scr_saver);
+ application->scr_saver = NULL;
+
+#ifdef ENABLE_DBUS
+ if (application->keys) {
+ g_object_unref (application->keys);
+ application->keys = NULL;
+ }
+ if (application->registration_id != 0) {
+ g_dbus_connection_unregister_object (application->connection,
+ application->registration_id);
+ application->registration_id = 0;
+ }
+ if (application->connection != NULL) {
+ g_object_unref (application->connection);
+ application->connection = NULL;
+ }
+ if (introspection_data) {
+ g_dbus_node_info_ref (introspection_data);
+ introspection_data = NULL;
+ }
+#endif /* ENABLE_DBUS */
+
+ g_free (application->dot_dir);
+ application->dot_dir = NULL;
+ g_free (application->data_dir);
+ application->data_dir = NULL;
+ g_free (application->filechooser_open_uri);
+ application->filechooser_open_uri = NULL;
+ g_free (application->filechooser_save_uri);
+ application->filechooser_save_uri = NULL;
+
+ g_object_unref (application);
+ instance = NULL;
+
+ gtk_main_quit ();
+}
+
+static void
+ev_application_class_init (EvApplicationClass *ev_application_class)
+{
+}
+
+static void
+ev_application_init (EvApplication *ev_application)
+{
+ GError *error = NULL;
+
+ userdir = g_getenv ("MATE22_USER_DIR");
+ if (userdir)
+ ev_application->dot_dir = g_build_filename (userdir, "evince", NULL);
+ else
+ ev_application->dot_dir = g_build_filename (g_get_home_dir (),
+ ".mate2",
+ "evince",
+ NULL);
+
+#ifdef G_OS_WIN32
+{
+ gchar *dir;
+
+ dir = g_win32_get_package_installation_directory_of_module (NULL);
+ ev_application->data_dir = g_build_filename (dir, "share", "evince", NULL);
+ g_free (dir);
+}
+#else
+ ev_application->data_dir = g_strdup (EVINCEDATADIR);
+#endif
+
+ ev_application_init_session (ev_application);
+
+ ev_application_accel_map_load (ev_application);
+
+#ifdef ENABLE_DBUS
+ ev_application->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ if (ev_application->connection != NULL) {
+ introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ g_assert (introspection_data != NULL);
+
+ ev_application->registration_id =
+ g_dbus_connection_register_object (ev_application->connection,
+ APPLICATION_DBUS_OBJECT_PATH,
+ introspection_data->interfaces[0],
+ &interface_vtable,
+ ev_application, NULL,
+ &error);
+ if (ev_application->registration_id == 0) {
+ g_printerr ("Failed to register bus object: %s\n", error->message);
+ g_error_free (error);
+ }
+ } else {
+ g_printerr ("Failed to get bus connection: %s\n", error->message);
+ g_error_free (error);
+ }
+
+ ev_application->keys = ev_media_player_keys_new ();
+#endif /* ENABLE_DBUS */
+
+ ev_application->scr_saver = totem_scrsaver_new ();
+ g_object_set (ev_application->scr_saver,
+ "reason", _("Running in presentation mode"),
+ NULL);
+}
+
+GDBusConnection *
+ev_application_get_dbus_connection (EvApplication *application)
+{
+#ifdef ENABLE_DBUS
+ return application->connection;
+#else
+ return NULL;
+#endif
+}
+
+gboolean
+ev_application_has_window (EvApplication *application)
+{
+ GList *l, *toplevels;
+ gboolean retval = FALSE;
+
+ toplevels = gtk_window_list_toplevels ();
+
+ for (l = toplevels; l != NULL && !retval; l = l->next) {
+ if (EV_IS_WINDOW (l->data))
+ retval = TRUE;
+ }
+
+ g_list_free (toplevels);
+
+ return retval;
+}
+
+guint
+ev_application_get_n_windows (EvApplication *application)
+{
+ GList *l, *toplevels;
+ guint retval = 0;
+
+ toplevels = gtk_window_list_toplevels ();
+
+ for (l = toplevels; l != NULL; l = l->next) {
+ if (EV_IS_WINDOW (l->data))
+ retval++;
+ }
+
+ g_list_free (toplevels);
+
+ return retval;
+}
+
+const gchar *
+ev_application_get_uri (EvApplication *application)
+{
+ return application->uri;
+}
+
+/**
+ * ev_application_get_media_keys:
+ * @application: The instance of the application.
+ *
+ * It gives you access to the media player keys handler object.
+ *
+ * Returns: A #EvMediaPlayerKeys.
+ */
+GObject *
+ev_application_get_media_keys (EvApplication *application)
+{
+#ifdef ENABLE_DBUS
+ return G_OBJECT (application->keys);
+#else
+ return NULL;
+#endif /* ENABLE_DBUS */
+}
+
+void
+ev_application_set_filechooser_uri (EvApplication *application,
+ GtkFileChooserAction action,
+ const gchar *uri)
+{
+ if (action == GTK_FILE_CHOOSER_ACTION_OPEN) {
+ g_free (application->filechooser_open_uri);
+ application->filechooser_open_uri = g_strdup (uri);
+ } else if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
+ g_free (application->filechooser_save_uri);
+ application->filechooser_save_uri = g_strdup (uri);
+ }
+}
+
+const gchar *
+ev_application_get_filechooser_uri (EvApplication *application,
+ GtkFileChooserAction action)
+{
+ if (action == GTK_FILE_CHOOSER_ACTION_OPEN) {
+ if (application->filechooser_open_uri)
+ return application->filechooser_open_uri;
+ } else if (action == GTK_FILE_CHOOSER_ACTION_SAVE) {
+ if (application->filechooser_save_uri)
+ return application->filechooser_save_uri;
+ }
+
+ return NULL;
+}
+
+void
+ev_application_screensaver_enable (EvApplication *application)
+{
+ totem_scrsaver_enable (application->scr_saver);
+}
+
+void
+ev_application_screensaver_disable (EvApplication *application)
+{
+ totem_scrsaver_disable (application->scr_saver);
+}
+
+const gchar *
+ev_application_get_dot_dir (EvApplication *application,
+ gboolean create)
+{
+ if (create)
+ g_mkdir_with_parents (application->dot_dir, 0700);
+
+ return application->dot_dir;
+}
+
+const gchar *
+ev_application_get_data_dir (EvApplication *application)
+{
+ return application->data_dir;
+}
diff --git a/shell/ev-application.h b/shell/ev-application.h
new file mode 100644
index 00000000..1e21ebde
--- /dev/null
+++ b/shell/ev-application.h
@@ -0,0 +1,88 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2004 Martin Kretzschmar
+ *
+ * Author:
+ * Martin Kretzschmar <[email protected]>
+ *
+ * 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_APPLICATION_H
+#define EV_APPLICATION_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "ev-window.h"
+
+G_BEGIN_DECLS
+
+typedef struct _EvApplication EvApplication;
+typedef struct _EvApplicationClass EvApplicationClass;
+
+#define EV_TYPE_APPLICATION (ev_application_get_type ())
+#define EV_APPLICATION(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_APPLICATION, EvApplication))
+#define EV_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_APPLICATION, EvApplicationClass))
+#define EV_IS_APPLICATION(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_APPLICATION))
+#define EV_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_APPLICATION))
+#define EV_APPLICATION_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_APPLICATION, EvApplicationClass))
+
+#define EV_APP (ev_application_get_instance ())
+
+GType ev_application_get_type (void) G_GNUC_CONST;
+EvApplication *ev_application_get_instance (void);
+
+void ev_application_shutdown (EvApplication *application);
+gboolean ev_application_load_session (EvApplication *application);
+void ev_application_open_window (EvApplication *application,
+ GdkScreen *screen,
+ guint32 timestamp);
+void ev_application_open_uri_at_dest (EvApplication *application,
+ const char *uri,
+ GdkScreen *screen,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string,
+ guint32 timestamp);
+void ev_application_open_uri_list (EvApplication *application,
+ GSList *uri_list,
+ GdkScreen *screen,
+ guint32 timestamp);
+GDBusConnection *ev_application_get_dbus_connection (EvApplication *application);
+gboolean ev_application_has_window (EvApplication *application);
+guint ev_application_get_n_windows (EvApplication *application);
+const gchar * ev_application_get_uri (EvApplication *application);
+GObject *ev_application_get_media_keys (EvApplication *application);
+
+void ev_application_set_filechooser_uri (EvApplication *application,
+ GtkFileChooserAction action,
+ const gchar *uri);
+const gchar *ev_application_get_filechooser_uri (EvApplication *application,
+ GtkFileChooserAction action);
+void ev_application_screensaver_enable (EvApplication *application);
+void ev_application_screensaver_disable (EvApplication *application);
+const gchar *ev_application_get_dot_dir (EvApplication *application,
+ gboolean create);
+const gchar *ev_application_get_data_dir (EvApplication *application);
+
+G_END_DECLS
+
+#endif /* !EV_APPLICATION_H */
+
diff --git a/shell/ev-convert-metadata.c b/shell/ev-convert-metadata.c
new file mode 100644
index 00000000..485ce133
--- /dev/null
+++ b/shell/ev-convert-metadata.c
@@ -0,0 +1,302 @@
+/* ev-convert-metadata.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2009 Carlos Garcia Campos <[email protected]>
+ *
+ * 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 <gtk/gtk.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <libxml/tree.h>
+
+#define EV_METADATA_NAMESPACE "metadata::evince"
+
+typedef struct {
+ xmlNodePtr cur;
+ xmlChar *uri;
+} DocItem;
+
+typedef struct {
+ GtkWidget *progress;
+ GtkWidget *label;
+
+ xmlDocPtr doc;
+ GList *items;
+ GList *current;
+ guint n_item;
+} ConvertData;
+
+static void
+free_doc_item (DocItem *item)
+{
+ xmlFree (item->uri);
+ g_free (item);
+}
+
+static void
+convert_finish (ConvertData *data)
+{
+ g_list_foreach (data->items, (GFunc)free_doc_item, NULL);
+ g_list_free (data->items);
+ xmlFreeDoc (data->doc);
+ g_free (data);
+
+ gtk_main_quit ();
+}
+
+static gboolean
+convert_file (ConvertData *data)
+{
+ GFile *file;
+ DocItem *item;
+ const gchar *uri;
+ xmlNodePtr node;
+ xmlNodePtr cur;
+ gint total, current;
+ gchar *text;
+
+ if (!data->current)
+ return FALSE;
+
+ item = (DocItem *) data->current->data;
+ uri = (const gchar *)item->uri;
+ node = item->cur;
+ data->current = g_list_next (data->current);
+
+ /* Update progress information */
+ total = g_list_length (data->items);
+ current = ++(data->n_item);
+
+ text = g_strdup_printf (_("Converting %s"), uri);
+ gtk_label_set_text (GTK_LABEL (data->label), text);
+ g_free (text);
+
+ text = g_strdup_printf (_("%d of %d documents converted"), current, total);
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (data->progress), text);
+ g_free (text);
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (data->progress),
+ (gdouble)(current - 1) / total);
+
+ file = g_file_new_for_uri (uri);
+ if (!g_file_query_exists (file, NULL)) {
+ g_printerr ("Uri %s does not exist\n", uri);
+ g_object_unref (file);
+
+ return data->current != NULL;
+ }
+
+ for (cur = node->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ xmlChar *key;
+ xmlChar *value;
+
+ if (xmlStrcmp (cur->name, (const xmlChar *)"entry") != 0)
+ continue;
+
+ key = xmlGetProp (cur, (const xmlChar *)"key");
+ value = xmlGetProp (cur, (const xmlChar *)"value");
+ if (key && value) {
+ GFileInfo *info;
+ gchar *gio_key;
+ GError *error = NULL;
+
+ info = g_file_info_new ();
+
+ gio_key = g_strconcat (EV_METADATA_NAMESPACE"::", key, NULL);
+ g_file_info_set_attribute_string (info, gio_key, (const gchar *)value);
+ g_free (gio_key);
+
+ if (!g_file_set_attributes_from_info (file, info, 0, NULL, &error)) {
+ g_printerr ("Error setting metadata for %s: %s\n",
+ uri, error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (info);
+ }
+
+ if (key)
+ xmlFree (key);
+ if (value)
+ xmlFree (value);
+ }
+
+ g_object_unref (file);
+
+ return data->current != NULL;
+}
+
+static void
+convert_metadata_cancel (GtkDialog *dialog,
+ gint response_id,
+ ConvertData *data)
+{
+ convert_finish (data);
+ exit (1);
+}
+
+static void
+show_progress_dialog (ConvertData *data)
+{
+ GtkWidget *dialog;
+ GtkWidget *action_area;
+ GtkWidget *vbox, *pbox;
+ GtkWidget *label;
+ GtkWidget *progress;
+ gchar *text;
+
+ dialog = gtk_dialog_new_with_buttons (_("Converting metadata"),
+ NULL,
+ GTK_DIALOG_NO_SEPARATOR,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+ action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
+ gtk_box_set_spacing (GTK_BOX (action_area), 6);
+
+ vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_box_set_spacing (GTK_BOX (vbox), 12);
+
+ label = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ text = g_strdup_printf ("<b>%s</b>", _("Converting metadata"));
+ gtk_label_set_markup (GTK_LABEL (label), text);
+ g_free (text);
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ label = gtk_label_new (_("The metadata format used by Evince "
+ "has changed, and hence it needs to be migrated. "
+ "If the migration is cancelled the metadata "
+ "storage will not work."));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+ gtk_widget_show (label);
+
+ pbox = gtk_vbox_new (FALSE, 6);
+ progress = gtk_progress_bar_new ();
+ data->progress = progress;
+ gtk_box_pack_start (GTK_BOX (pbox), progress, TRUE, TRUE, 0);
+ gtk_widget_show (progress);
+
+ label = gtk_label_new (NULL);
+ data->label = label;
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+ gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
+ gtk_box_pack_start (GTK_BOX (pbox), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ gtk_box_pack_start (GTK_BOX (vbox), pbox, TRUE, TRUE, 0);
+ gtk_widget_show (pbox);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (convert_metadata_cancel),
+ data);
+
+ gtk_widget_show (dialog);
+}
+
+static gboolean
+convert_metadata_file (const gchar *filename)
+{
+ ConvertData *data;
+ xmlDocPtr doc;
+ xmlNodePtr cur;
+
+ if (!g_file_test (filename, G_FILE_TEST_EXISTS))
+ return FALSE;
+
+ doc = xmlParseFile (filename);
+ if (!doc) {
+ g_printerr ("Error loading metadata file %s\n", filename);
+ return FALSE;
+ }
+
+ cur = xmlDocGetRootElement (doc);
+ if (!cur) {
+ g_printerr ("Metadata file %s is empty\n", filename);
+ xmlFreeDoc (doc);
+ return TRUE;
+ }
+
+ if (xmlStrcmp (cur->name, (const xmlChar *) "metadata")) {
+ g_printerr ("File %s is not a valid evince metadata file\n", filename);
+ xmlFreeDoc (doc);
+ return FALSE;
+ }
+
+ data = g_new0 (ConvertData, 1);
+ data->doc = doc;
+
+ for (cur = cur->xmlChildrenNode; cur != NULL; cur = cur->next) {
+ xmlChar *uri;
+ DocItem *item;
+
+ if (xmlStrcmp (cur->name, (const xmlChar *)"document") != 0)
+ continue;
+
+ uri = xmlGetProp (cur, (const xmlChar *)"uri");
+ if (!uri)
+ continue;
+
+ item = g_new (DocItem, 1);
+ item->uri = uri;
+ item->cur = cur;
+ data->items = g_list_prepend (data->items, item);
+ }
+
+ if (!data->items) {
+ xmlFreeDoc (data->doc);
+ g_free (data);
+
+ return TRUE;
+ }
+
+ show_progress_dialog (data);
+
+ data->current = data->items;
+ g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+ (GSourceFunc)convert_file,
+ data,
+ (GDestroyNotify)convert_finish);
+
+ return TRUE;
+}
+
+gint
+main (gint argc, gchar **argv)
+{
+ if (argc != 2) {
+ g_printerr ("%s\n", "Usage: evince-convert-metadata FILE");
+ return 1;
+ }
+
+ gtk_init (&argc, &argv);
+
+ if (!convert_metadata_file (argv[1]))
+ return 1;
+
+ gtk_main ();
+
+ return 0;
+}
diff --git a/shell/ev-daemon.c b/shell/ev-daemon.c
new file mode 100644
index 00000000..53bd30ea
--- /dev/null
+++ b/shell/ev-daemon.c
@@ -0,0 +1,553 @@
+/* ev-metadata.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2009 Carlos Garcia Campos <[email protected]>
+ * Copyright Ā© 2010 Christian Persch
+ *
+ * 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 <glib.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define EV_DBUS_DAEMON_NAME "org.mate.evince.Daemon"
+#define EV_DBUS_DAEMON_INTERFACE_NAME "org.mate.evince.Daemon"
+#define EV_DBUS_DAEMON_OBJECT_PATH "/org/mate/evince/Daemon"
+
+#define EV_DBUS_WINDOW_INTERFACE_NAME "org.mate.evince.Window"
+
+#define DAEMON_TIMEOUT (30) /* seconds */
+
+#define LOG g_printerr
+
+static GList *ev_daemon_docs = NULL;
+static guint kill_timer_id;
+static GHashTable *pending_invocations = NULL;
+
+typedef struct {
+ gchar *dbus_name;
+ gchar *uri;
+ guint watch_id;
+ guint loaded_id;
+} EvDoc;
+
+static void
+ev_doc_free (EvDoc *doc)
+{
+ if (!doc)
+ return;
+
+ g_free (doc->dbus_name);
+ g_free (doc->uri);
+
+ g_bus_unwatch_name (doc->watch_id);
+
+ g_free (doc);
+}
+
+static EvDoc *
+ev_daemon_find_doc (const gchar *uri)
+{
+ GList *l;
+
+ for (l = ev_daemon_docs; l != NULL; l = l->next) {
+ EvDoc *doc = (EvDoc *)l->data;
+
+ if (strcmp (doc->uri, uri) == 0)
+ return doc;
+ }
+
+ return NULL;
+}
+
+static void
+ev_daemon_stop_killtimer (void)
+{
+ if (kill_timer_id != 0)
+ g_source_remove (kill_timer_id);
+ kill_timer_id = 0;
+}
+
+static gboolean
+ev_daemon_shutdown (gpointer user_data)
+{
+ GMainLoop *loop = (GMainLoop *) user_data;
+
+ LOG ("Timeout; exiting daemon.\n");
+
+ if (g_main_loop_is_running (loop))
+ g_main_loop_quit (loop);
+
+ return FALSE;
+}
+
+static void
+ev_daemon_maybe_start_killtimer (gpointer data)
+{
+ ev_daemon_stop_killtimer ();
+ if (ev_daemon_docs != NULL)
+ return;
+
+ kill_timer_id = g_timeout_add_seconds (DAEMON_TIMEOUT,
+ (GSourceFunc) ev_daemon_shutdown,
+ data);
+}
+
+static gboolean
+convert_metadata (const gchar *metadata)
+{
+ GFile *file;
+ char *argv[3];
+ gint exit_status;
+ GFileAttributeInfoList *namespaces;
+ gboolean supported = FALSE;
+ GError *error = NULL;
+ gboolean retval;
+
+ /* If metadata is not supported for a local file
+ * is likely because and old gvfs version is running.
+ */
+ file = g_file_new_for_path (metadata);
+ namespaces = g_file_query_writable_namespaces (file, NULL, NULL);
+ if (namespaces) {
+ gint i;
+
+ for (i = 0; i < namespaces->n_infos; i++) {
+ if (strcmp (namespaces->infos[i].name, "metadata") == 0) {
+ supported = TRUE;
+ break;
+ }
+ }
+ g_file_attribute_info_list_unref (namespaces);
+ }
+ if (!supported) {
+ g_warning ("GVFS metadata not supported. "
+ "Evince will run without metadata support.\n");
+ g_object_unref (file);
+ return FALSE;
+ }
+ g_object_unref (file);
+
+ argv[0] = g_build_filename (LIBEXECDIR, "evince-convert-metadata", NULL);
+ argv[1] = (char *) metadata;
+ argv[2] = NULL;
+
+ retval = g_spawn_sync (NULL /* wd */, argv, NULL /* env */,
+ 0, NULL, NULL, NULL, NULL,
+ &exit_status, &error);
+ g_free (argv[0]);
+
+ if (!retval) {
+ g_printerr ("Error migrating metadata: %s\n", error->message);
+ g_error_free (error);
+ }
+
+ return retval && WIFEXITED (exit_status) && WEXITSTATUS (exit_status) == 0;
+}
+
+static void
+ev_migrate_metadata (void)
+{
+ gchar *updated;
+ gchar *metadata;
+ gchar *dot_dir;
+ const gchar *userdir;
+
+ userdir = g_getenv ("MATE22_USER_DIR");
+ if (userdir) {
+ dot_dir = g_build_filename (userdir, "evince", NULL);
+ } else {
+ dot_dir = g_build_filename (g_get_home_dir (),
+ ".mate2",
+ "evince",
+ NULL);
+ }
+
+ updated = g_build_filename (dot_dir, "migrated-to-gvfs", NULL);
+ if (g_file_test (updated, G_FILE_TEST_EXISTS)) {
+ /* Already migrated */
+ g_free (updated);
+ g_free (dot_dir);
+ return;
+ }
+
+ metadata = g_build_filename (dot_dir, "ev-metadata.xml", NULL);
+ if (g_file_test (metadata, G_FILE_TEST_EXISTS)) {
+ if (convert_metadata (metadata)) {
+ gint fd;
+
+ fd = g_creat (updated, 0600);
+ if (fd != -1) {
+ close (fd);
+ }
+ }
+ }
+
+ g_free (dot_dir);
+ g_free (updated);
+ g_free (metadata);
+}
+
+static gboolean
+spawn_evince (const gchar *uri)
+{
+ gchar *argv[3];
+ gboolean retval;
+ GError *error = NULL;
+
+ /* TODO Check that the uri exists */
+ argv[0] = g_build_filename (BINDIR, "evince", NULL);
+ argv[1] = (gchar *) uri;
+ argv[2] = NULL;
+
+ retval = g_spawn_async (NULL /* wd */, argv, NULL /* env */,
+ 0, NULL, NULL, NULL, &error);
+ if (!retval) {
+ g_printerr ("Error spawning evince for uri %s: %s\n", uri, error->message);
+ g_error_free (error);
+ }
+ g_free (argv[0]);
+
+ return retval;
+}
+
+static void
+name_appeared_cb (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ LOG ("Watch name'%s' appeared with owner '%s'\n", name, name_owner);
+}
+
+static void
+name_vanished_cb (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GList *l;
+
+ LOG ("Watch name'%s' disappeared\n", name);
+
+ for (l = ev_daemon_docs; l != NULL; l = l->next) {
+ EvDoc *doc = (EvDoc *) l->data;
+
+ if (strcmp (doc->dbus_name, name) != 0)
+ continue;
+
+ LOG ("Watch found URI '%s' for name; removing\n", doc->uri);
+
+ ev_daemon_docs = g_list_delete_link (ev_daemon_docs, l);
+ ev_doc_free (doc);
+
+ ev_daemon_maybe_start_killtimer (user_data);
+ return;
+ }
+}
+
+static void
+process_pending_invocations (const gchar *uri,
+ const gchar *dbus_name)
+{
+ GList *l;
+ GList *uri_invocations;
+
+ LOG ("RegisterDocument process pending invocations for URI %s\n", uri);
+ uri_invocations = g_hash_table_lookup (pending_invocations, uri);
+
+ for (l = uri_invocations; l != NULL; l = l->next) {
+ GDBusMethodInvocation *invocation;
+
+ invocation = (GDBusMethodInvocation *)l->data;
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(s)", dbus_name));
+ }
+
+ g_list_free (uri_invocations);
+ g_hash_table_remove (pending_invocations, uri);
+}
+
+static void
+document_loaded_cb (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ EvDoc *doc)
+{
+ const gchar *uri;
+
+ g_variant_get (parameters, "(&s)", &uri);
+ if (strcmp (uri, doc->uri) == 0)
+ process_pending_invocations (uri, sender_name);
+ g_dbus_connection_signal_unsubscribe (connection, doc->loaded_id);
+}
+
+static void
+method_call_cb (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ if (g_strcmp0 (interface_name, EV_DBUS_DAEMON_INTERFACE_NAME) != 0)
+ return;
+
+ if (g_strcmp0 (method_name, "RegisterDocument") == 0) {
+ EvDoc *doc;
+ const gchar *uri;
+
+ g_variant_get (parameters, "(&s)", &uri);
+
+ doc = ev_daemon_find_doc (uri);
+ if (doc != NULL) {
+ LOG ("RegisterDocument found owner '%s' for URI '%s'\n", doc->dbus_name, uri);
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(s)", doc->dbus_name));
+ return;
+ }
+
+ ev_daemon_stop_killtimer ();
+
+ doc = g_new (EvDoc, 1);
+ doc->dbus_name = g_strdup (sender);
+ doc->uri = g_strdup (uri);
+
+ doc->loaded_id = g_dbus_connection_signal_subscribe (connection,
+ doc->dbus_name,
+ EV_DBUS_WINDOW_INTERFACE_NAME,
+ "DocumentLoaded",
+ NULL,
+ NULL,
+ 0,
+ (GDBusSignalCallback) document_loaded_cb,
+ doc,
+ NULL);
+ doc->watch_id = g_bus_watch_name_on_connection (connection,
+ sender,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ name_appeared_cb,
+ name_vanished_cb,
+ user_data, NULL);
+
+ LOG ("RegisterDocument registered owner '%s' for URI '%s'\n", doc->dbus_name, uri);
+ ev_daemon_docs = g_list_prepend (ev_daemon_docs, doc);
+
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ""));
+ } else if (g_strcmp0 (method_name, "UnregisterDocument") == 0) {
+ EvDoc *doc;
+ const gchar *uri;
+
+ g_variant_get (parameters, "(&s)", &uri);
+
+ LOG ("UnregisterDocument URI '%s'\n", uri);
+
+ doc = ev_daemon_find_doc (uri);
+ if (doc == NULL) {
+ LOG ("UnregisterDocument URI was not registered!\n");
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_INVALID_ARGS,
+ "URI not registered");
+ return;
+ }
+
+ if (strcmp (doc->dbus_name, sender) != 0) {
+ LOG ("UnregisterDocument called by non-owner (owner '%s' sender '%s')\n",
+ doc->dbus_name, sender);
+
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_BAD_ADDRESS,
+ "Only owner can call this method");
+ return;
+ }
+
+ ev_daemon_docs = g_list_remove (ev_daemon_docs, doc);
+ ev_doc_free (doc);
+ ev_daemon_maybe_start_killtimer (user_data);
+
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
+ } else if (g_strcmp0 (method_name, "FindDocument") == 0) {
+ EvDoc *doc;
+ const gchar *uri;
+ gboolean spawn;
+
+ g_variant_get (parameters, "(&sb)", &uri, &spawn);
+
+ LOG ("FindDocument URI '%s' \n", uri);
+
+ doc = ev_daemon_find_doc (uri);
+ if (doc != NULL) {
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(s)", doc->dbus_name));
+ return;
+ }
+
+ if (spawn) {
+ GList *uri_invocations;
+ gboolean ret_val = TRUE;
+
+ uri_invocations = g_hash_table_lookup (pending_invocations, uri);
+
+ if (uri_invocations == NULL) {
+ /* Only spawn once. */
+ ret_val = spawn_evince (uri);
+ }
+
+ if (ret_val) {
+ /* Only defer DBUS answer if evince was succesfully spawned */
+ uri_invocations = g_list_prepend (uri_invocations, invocation);
+ g_hash_table_insert (pending_invocations,
+ g_strdup (uri),
+ uri_invocations);
+ return;
+ }
+ }
+
+ LOG ("FindDocument URI '%s' was not registered!\n", uri);
+ g_dbus_method_invocation_return_value (invocation,
+ g_variant_new ("(s)",""));
+ }
+}
+
+static const char introspection_xml[] =
+ "<node>"
+ "<interface name='org.mate.evince.Daemon'>"
+ "<method name='RegisterDocument'>"
+ "<arg type='s' name='uri' direction='in'/>"
+ "<arg type='s' name='owner' direction='out'/>"
+ "</method>"
+ "<method name='UnregisterDocument'>"
+ "<arg type='s' name='uri' direction='in'/>"
+ "</method>"
+ "<method name='FindDocument'>"
+ "<arg type='s' name='uri' direction='in'/>"
+ "<arg type='b' name='spawn' direction='in'/>"
+ "<arg type='s' name='owner' direction='out'/>"
+ "</method>"
+ "</interface>"
+ "</node>";
+
+static const GDBusInterfaceVTable interface_vtable = {
+ method_call_cb,
+ NULL,
+ NULL
+};
+
+static GDBusNodeInfo *introspection_data;
+
+static void
+bus_acquired_cb (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GMainLoop *loop = (GMainLoop *) user_data;
+ guint registration_id;
+ GError *error = NULL;
+
+ if (!introspection_data)
+ introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+
+ registration_id = g_dbus_connection_register_object (connection,
+ EV_DBUS_DAEMON_OBJECT_PATH,
+ introspection_data->interfaces[0],
+ &interface_vtable,
+ g_main_loop_ref (loop),
+ (GDestroyNotify) g_main_loop_unref,
+ &error);
+ if (registration_id == 0) {
+ g_printerr ("Failed to register object: %s\n", error->message);
+ g_error_free (error);
+
+ if (g_main_loop_is_running (loop))
+ g_main_loop_quit (loop);
+ }
+}
+
+static void
+name_acquired_cb (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ ev_migrate_metadata ();
+
+ ev_daemon_maybe_start_killtimer (user_data);
+}
+
+static void
+name_lost_cb (GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ GMainLoop *loop = (GMainLoop *) user_data;
+
+ /* Failed to acquire the name; exit daemon */
+ if (g_main_loop_is_running (loop))
+ g_main_loop_quit (loop);
+}
+
+gint
+main (gint argc, gchar **argv)
+{
+ GMainLoop *loop;
+ guint owner_id;
+
+ g_set_prgname ("evince-daemon");
+
+ g_type_init ();
+
+ loop = g_main_loop_new (NULL, FALSE);
+
+ pending_invocations = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify)g_free,
+ NULL);
+
+ owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ EV_DBUS_DAEMON_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ bus_acquired_cb,
+ name_acquired_cb,
+ name_lost_cb,
+ g_main_loop_ref (loop),
+ (GDestroyNotify) g_main_loop_unref);
+
+ g_main_loop_run (loop);
+
+ g_bus_unown_name (owner_id);
+
+ g_main_loop_unref (loop);
+ if (introspection_data)
+ g_dbus_node_info_unref (introspection_data);
+ g_list_foreach (ev_daemon_docs, (GFunc)ev_doc_free, NULL);
+ g_list_free (ev_daemon_docs);
+ g_hash_table_destroy (pending_invocations);
+
+ return 0;
+}
diff --git a/shell/ev-file-monitor.c b/shell/ev-file-monitor.c
new file mode 100644
index 00000000..3de0ce01
--- /dev/null
+++ b/shell/ev-file-monitor.c
@@ -0,0 +1,168 @@
+/* ev-file-monitor.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ *
+ * 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 <glib.h>
+#include <gio/gio.h>
+
+#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 (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 00000000..6656671f
--- /dev/null
+++ b/shell/ev-file-monitor.h
@@ -0,0 +1,57 @@
+/* ev-file-monitor.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ *
+ * 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_FILE_MONITOR_H
+#define EV_FILE_MONITOR_H
+
+#include <glib-object.h>
+
+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-history.c b/shell/ev-history.c
new file mode 100644
index 00000000..9b59014c
--- /dev/null
+++ b/shell/ev-history.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2005 Marco Pesenti Gritti
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include "ev-history.h"
+
+
+enum
+{
+ HISTORY_CHANGED,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = {0, };
+
+struct _EvHistoryPrivate
+{
+ GList *links;
+};
+
+static void ev_history_init (EvHistory *history);
+static void ev_history_class_init (EvHistoryClass *class);
+
+G_DEFINE_TYPE (EvHistory, ev_history, G_TYPE_OBJECT)
+
+#define EV_HISTORY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_HISTORY, EvHistoryPrivate))
+
+static void
+ev_history_init (EvHistory *history)
+{
+ history->priv = EV_HISTORY_GET_PRIVATE (history);
+
+ history->priv->links = NULL;
+}
+
+static void
+free_links_list (GList *l)
+{
+ g_list_foreach (l, (GFunc)g_object_unref, NULL);
+ g_list_free (l);
+}
+
+static void
+ev_history_finalize (GObject *object)
+{
+ EvHistory *history = EV_HISTORY (object);
+
+ free_links_list (history->priv->links);
+
+ G_OBJECT_CLASS (ev_history_parent_class)->finalize (object);
+}
+
+static void
+ev_history_class_init (EvHistoryClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = ev_history_finalize;
+
+ signals[HISTORY_CHANGED] =
+ g_signal_new ("changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvHistoryClass, changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (object_class, sizeof (EvHistoryPrivate));
+}
+
+#define HISTORY_LENGTH 7
+
+void
+ev_history_add_link (EvHistory *history, EvLink *link)
+{
+ GList *l;
+
+ g_return_if_fail (EV_IS_HISTORY (history));
+ g_return_if_fail (EV_IS_LINK (link));
+
+ for (l = history->priv->links; l; l = l->next) {
+ if (!strcmp (ev_link_get_title (EV_LINK (l->data)), ev_link_get_title (link))) {
+ g_object_unref (G_OBJECT (l->data));
+ history->priv->links = g_list_delete_link (history->priv->links, l);
+ break;
+ }
+ }
+
+ g_object_ref (link);
+ history->priv->links = g_list_append (history->priv->links,
+ link);
+
+ if (g_list_length (history->priv->links) > HISTORY_LENGTH) {
+ g_object_unref (G_OBJECT (history->priv->links->data));
+ history->priv->links = g_list_delete_link (history->priv->links,
+ history->priv->links);
+ }
+
+ g_signal_emit (history, signals[HISTORY_CHANGED], 0);
+}
+
+EvLink *
+ev_history_get_link_nth (EvHistory *history, int index)
+{
+ GList *l;
+
+ g_return_val_if_fail (EV_IS_HISTORY (history), NULL);
+
+ l = g_list_nth (history->priv->links, index);
+
+ return EV_LINK (l->data);
+}
+
+int
+ev_history_get_n_links (EvHistory *history)
+{
+ g_return_val_if_fail (EV_IS_HISTORY (history), -1);
+
+ return g_list_length (history->priv->links);
+}
+
+EvHistory *
+ev_history_new (void)
+{
+ return EV_HISTORY (g_object_new (EV_TYPE_HISTORY, NULL));
+}
+
diff --git a/shell/ev-history.h b/shell/ev-history.h
new file mode 100644
index 00000000..d8bb53f5
--- /dev/null
+++ b/shell/ev-history.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2005 Marco Pesenti Gritti
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA..
+ *
+ */
+
+#ifndef EV_HISTORY_H
+#define EV_HISTORY_H
+
+#include <glib-object.h>
+
+#include "ev-link.h"
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_HISTORY (ev_history_get_type ())
+#define EV_HISTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_HISTORY, EvHistory))
+#define EV_HISTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_HISTORY, EvHistoryClass))
+#define EV_IS_HISTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_HISTORY))
+#define EV_IS_HISTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EV_TYPE_HISTORY))
+#define EV_HISTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EV_TYPE_HISTORY, EvHistoryClass))
+
+typedef struct _EvHistory EvHistory;
+typedef struct _EvHistoryPrivate EvHistoryPrivate;
+typedef struct _EvHistoryClass EvHistoryClass;
+
+struct _EvHistory
+{
+ GObject parent;
+
+ /*< private >*/
+ EvHistoryPrivate *priv;
+};
+
+struct _EvHistoryClass
+{
+ GObjectClass parent_class;
+
+ void (*changed) (EvHistory *history);
+};
+
+GType ev_history_get_type (void);
+EvHistory *ev_history_new (void);
+void ev_history_add_link (EvHistory *history,
+ EvLink *linkk);
+EvLink *ev_history_get_link_nth (EvHistory *history,
+ int index);
+int ev_history_get_n_links (EvHistory *history);
+
+G_END_DECLS
+
+#endif
diff --git a/shell/ev-keyring.c b/shell/ev-keyring.c
new file mode 100644
index 00000000..f7d935f0
--- /dev/null
+++ b/shell/ev-keyring.c
@@ -0,0 +1,116 @@
+/* ev-keyring.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ *
+ * 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 <glib/gi18n.h>
+
+#include "ev-keyring.h"
+
+#ifdef WITH_KEYRING
+#include <mate-keyring.h>
+
+static const MateKeyringPasswordSchema doc_password_schema = {
+ MATE_KEYRING_ITEM_GENERIC_SECRET,
+ {
+ { "type", MATE_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { "uri", MATE_KEYRING_ATTRIBUTE_TYPE_STRING },
+ { NULL, 0 }
+ }
+};
+const MateKeyringPasswordSchema *EV_DOCUMENT_PASSWORD_SCHEMA = &doc_password_schema;
+#endif /* WITH_KEYRING */
+
+gboolean
+ev_keyring_is_available (void)
+{
+#ifdef WITH_KEYRING
+ return mate_keyring_is_available ();
+#else
+ return FALSE;
+#endif
+}
+
+gchar *
+ev_keyring_lookup_password (const gchar *uri)
+{
+ gchar *retval = NULL;
+#ifdef WITH_KEYRING
+ MateKeyringResult result;
+ gchar *password = NULL;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ if (!mate_keyring_is_available ())
+ return NULL;
+
+ result = mate_keyring_find_password_sync (EV_DOCUMENT_PASSWORD_SCHEMA,
+ &password,
+ "type", "document_password",
+ "uri", uri,
+ NULL);
+ if (result != MATE_KEYRING_RESULT_OK || !password) {
+ if (password)
+ mate_keyring_free_password (password);
+ return NULL;
+ }
+
+ retval = g_strdup (password);
+ mate_keyring_free_password (password);
+#endif /* WITH_KEYRING */
+ return retval;
+}
+
+gboolean
+ev_keyring_save_password (const gchar *uri,
+ const gchar *password,
+ GPasswordSave flags)
+{
+#ifdef WITH_KEYRING
+ MateKeyringResult result;
+ const gchar *keyring;
+ gchar *name;
+ gchar *unescaped_uri;
+
+ g_return_val_if_fail (uri != NULL, FALSE);
+
+ if (!mate_keyring_is_available ())
+ return FALSE;
+
+ if (flags == G_PASSWORD_SAVE_NEVER)
+ return FALSE;
+
+ keyring = (flags == G_PASSWORD_SAVE_FOR_SESSION) ? "session" : NULL;
+ unescaped_uri = g_uri_unescape_string (uri, NULL);
+ name = g_strdup_printf (_("Password for document %s"), unescaped_uri);
+ g_free (unescaped_uri);
+
+ result = mate_keyring_store_password_sync (EV_DOCUMENT_PASSWORD_SCHEMA,
+ keyring, name, password,
+ "type", "document_password",
+ "uri", uri,
+ NULL);
+ g_free (name);
+
+ return (result == MATE_KEYRING_RESULT_OK);
+#else
+ return FALSE;
+#endif /* WITH_KEYRING */
+}
diff --git a/shell/ev-keyring.h b/shell/ev-keyring.h
new file mode 100644
index 00000000..d1cc5226
--- /dev/null
+++ b/shell/ev-keyring.h
@@ -0,0 +1,37 @@
+/* ev-keyring.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ *
+ * 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_KEYRING_H__
+#define __EV_KEYRING_H__
+
+#include <glib.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+gboolean ev_keyring_is_available (void);
+gchar *ev_keyring_lookup_password (const gchar *uri);
+gboolean ev_keyring_save_password (const gchar *uri,
+ const gchar *password,
+ GPasswordSave flags);
+
+G_END_DECLS
+
+#endif /* __EV_KEYRING_H__ */
diff --git a/shell/ev-marshal.list b/shell/ev-marshal.list
new file mode 100644
index 00000000..72f99379
--- /dev/null
+++ b/shell/ev-marshal.list
@@ -0,0 +1 @@
+VOID:STRING,STRING
diff --git a/shell/ev-media-player-keys.c b/shell/ev-media-player-keys.c
new file mode 100644
index 00000000..f3237873
--- /dev/null
+++ b/shell/ev-media-player-keys.c
@@ -0,0 +1,233 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Jan Arne Petersen <[email protected]>
+ * Copyright (C) 2008 Bastien Nocera <[email protected]>
+ * Copyright Ā© 2010 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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "config.h"
+
+#include "ev-media-player-keys.h"
+
+#include <string.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#define SD_NAME "org.mate.SettingsDaemon"
+#define SD_OBJECT_PATH "/org/mate/SettingsDaemon/MediaKeys"
+#define SD_INTERFACE "org.mate.SettingsDaemon.MediaKeys"
+
+enum {
+ KEY_PRESSED,
+ LAST_SIGNAL
+};
+
+struct _EvMediaPlayerKeys
+{
+ GObject parent;
+
+ GDBusProxy *proxy;
+ gboolean has_name_owner;
+};
+
+struct _EvMediaPlayerKeysClass
+{
+ GObjectClass parent_class;
+
+ /* Signals */
+ void (* key_pressed) (EvMediaPlayerKeys *keys,
+ const gchar *key);
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (EvMediaPlayerKeys, ev_media_player_keys, G_TYPE_OBJECT)
+
+static void ev_media_player_keys_finalize (GObject *object);
+
+static void
+ev_media_player_keys_class_init (EvMediaPlayerKeysClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = ev_media_player_keys_finalize;
+
+ signals[KEY_PRESSED] =
+ g_signal_new ("key_pressed",
+ EV_TYPE_MEDIA_PLAYER_KEYS,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EvMediaPlayerKeysClass, key_pressed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE,
+ 1, G_TYPE_STRING);
+}
+
+static void
+ev_media_player_keys_update_has_name_owner (EvMediaPlayerKeys *keys)
+{
+ gchar *name_owner;
+
+ if (!keys->proxy) {
+ keys->has_name_owner = FALSE;
+ return;
+ }
+
+ name_owner = g_dbus_proxy_get_name_owner (keys->proxy);
+ keys->has_name_owner = (name_owner != NULL);
+ g_free (name_owner);
+}
+
+static void
+ev_media_player_keys_grab_keys (EvMediaPlayerKeys *keys)
+{
+ if (!keys->has_name_owner)
+ return;
+
+ /*
+ * The uint as second argument is time. We give a very low value so that
+ * if a media player is there it gets higher priority on the keys (0 is
+ * a special value having maximum priority).
+ */
+ g_dbus_proxy_call (keys->proxy,
+ "GrabMediaPlayerKeys",
+ g_variant_new ("(su)", "Evince", 1),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL, NULL, NULL);
+}
+
+static void
+ev_media_player_keys_release_keys (EvMediaPlayerKeys *keys)
+{
+ if (!keys->has_name_owner)
+ return;
+
+ g_dbus_proxy_call (keys->proxy,
+ "ReleaseMediaPlayerKeys",
+ g_variant_new ("(s)", "Evince"),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL, NULL, NULL);
+}
+
+static void
+media_player_key_pressed_cb (GDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ const char *application, *key;
+
+ if (g_strcmp0 (sender_name, SD_NAME) != 0)
+ return;
+
+ if (g_strcmp0 (signal_name, "MediaPlayerKeyPressed") != 0)
+ return;
+
+ if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(ss)")))
+ return;
+
+ g_variant_get (parameters, "(&s&s)", &application, &key);
+
+ if (strcmp ("Evince", application) == 0) {
+ g_signal_emit (user_data, signals[KEY_PRESSED], 0, key);
+ }
+}
+
+static void
+mediakeys_name_owner_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ EvMediaPlayerKeys *keys = EV_MEDIA_PLAYER_KEYS (user_data);
+
+ ev_media_player_keys_update_has_name_owner (keys);
+}
+
+static void
+mediakeys_service_appeared_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ EvMediaPlayerKeys *keys = EV_MEDIA_PLAYER_KEYS (user_data);
+ GDBusProxy *proxy;
+
+ proxy = g_dbus_proxy_new_for_bus_finish (res, NULL);
+
+ if (proxy == NULL) {
+ return;
+ }
+
+ g_signal_connect (proxy, "g-signal",
+ G_CALLBACK (media_player_key_pressed_cb),
+ keys);
+ g_signal_connect (proxy, "notify::g-name-owner",
+ G_CALLBACK (mediakeys_name_owner_changed),
+ keys);
+
+ keys->proxy = proxy;
+ ev_media_player_keys_update_has_name_owner (keys);
+ ev_media_player_keys_grab_keys (keys);
+}
+
+static void
+ev_media_player_keys_init (EvMediaPlayerKeys *keys)
+{
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ SD_NAME,
+ SD_OBJECT_PATH,
+ SD_INTERFACE,
+ NULL,
+ mediakeys_service_appeared_cb,
+ keys);
+}
+
+void
+ev_media_player_keys_focused (EvMediaPlayerKeys *keys)
+{
+ if (keys->proxy == NULL)
+ return;
+
+ ev_media_player_keys_grab_keys (keys);
+}
+
+static void
+ev_media_player_keys_finalize (GObject *object)
+{
+ EvMediaPlayerKeys *keys = EV_MEDIA_PLAYER_KEYS (object);
+
+ if (keys->proxy != NULL) {
+ ev_media_player_keys_release_keys (keys);
+ g_object_unref (keys->proxy);
+ keys->proxy = NULL;
+ keys->has_name_owner = FALSE;
+ }
+
+ G_OBJECT_CLASS (ev_media_player_keys_parent_class)->finalize (object);
+}
+
+EvMediaPlayerKeys *
+ev_media_player_keys_new (void)
+{
+ return g_object_new (EV_TYPE_MEDIA_PLAYER_KEYS, NULL);
+}
+
diff --git a/shell/ev-media-player-keys.h b/shell/ev-media-player-keys.h
new file mode 100644
index 00000000..8e2d62be
--- /dev/null
+++ b/shell/ev-media-player-keys.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2007 Jan Arne Petersen <[email protected]>
+ * Copyright (C) 2008 Bastien Nocera <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef EV_MEDIA_PLAYER_KEYS_H
+#define EV_MEDIA_PLAYER_KEYS_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_MEDIA_PLAYER_KEYS (ev_media_player_keys_get_type ())
+#define EV_MEDIA_PLAYER_KEYS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_MEDIA_PLAYER_KEYS, EvMediaPlayerKeys))
+#define EV_MEDIA_PLAYER_KEYS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_MEDIA_PLAYER_KEYS, EvMediaPlayerKeysClass))
+#define EV_IS_MEDIA_PLAYER_KEYS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_MEDIA_PLAYER_KEYS))
+#define EV_IS_MEDIA_PLAYER_KEYS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_MEDIA_PLAYER_KEYS))
+#define EV_MEDIA_PLAYER_KEYS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EV_TYPE_MEDIA_PLAYER_KEYS, EvMediaPlayerKeysClass))
+
+typedef struct _EvMediaPlayerKeys EvMediaPlayerKeys;
+typedef struct _EvMediaPlayerKeysClass EvMediaPlayerKeysClass;
+
+
+GType ev_media_player_keys_get_type (void) G_GNUC_CONST;
+
+EvMediaPlayerKeys *ev_media_player_keys_new (void);
+
+void ev_media_player_keys_focused (EvMediaPlayerKeys *keys);
+
+G_END_DECLS
+
+#endif /* !EV_MEDIA_PLAYER_KEYS_H */
diff --git a/shell/ev-message-area.c b/shell/ev-message-area.c
new file mode 100644
index 00000000..3530f4a4
--- /dev/null
+++ b/shell/ev-message-area.c
@@ -0,0 +1,354 @@
+/* ev-message-area.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2007 Carlos Garcia Campos
+ *
+ * Author:
+ * Carlos Garcia Campos <[email protected]>
+ *
+ * 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-message-area.h"
+
+#define EV_MESSAGE_AREA_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EV_TYPE_MESSAGE_AREA, EvMessageAreaPrivate))
+
+struct _EvMessageAreaPrivate {
+ GtkWidget *main_box;
+ GtkWidget *image;
+ GtkWidget *label;
+ GtkWidget *secondary_label;
+
+ guint message_type : 3;
+};
+
+enum {
+ PROP_0,
+ PROP_TEXT,
+ PROP_SECONDARY_TEXT,
+ PROP_IMAGE
+};
+
+static void ev_message_area_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void ev_message_area_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+G_DEFINE_TYPE (EvMessageArea, ev_message_area, GTK_TYPE_INFO_BAR)
+
+static void
+ev_message_area_class_init (EvMessageAreaClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = ev_message_area_set_property;
+ gobject_class->get_property = ev_message_area_get_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_TEXT,
+ g_param_spec_string ("text",
+ "Text",
+ "The primary text of the message dialog",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_SECONDARY_TEXT,
+ g_param_spec_string ("secondary-text",
+ "Secondary Text",
+ "The secondary text of the message dialog",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_IMAGE,
+ g_param_spec_object ("image",
+ "Image",
+ "The image",
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (gobject_class, sizeof (EvMessageAreaPrivate));
+}
+
+static void
+ev_message_area_init (EvMessageArea *area)
+{
+ GtkWidget *hbox, *vbox;
+ GtkWidget *content_area;
+
+ area->priv = EV_MESSAGE_AREA_GET_PRIVATE (area);
+
+ area->priv->main_box = gtk_vbox_new (FALSE, 12);
+
+ hbox = gtk_hbox_new (FALSE, 12);
+ vbox = gtk_vbox_new (FALSE, 12);
+
+ area->priv->label = gtk_label_new (NULL);
+ gtk_label_set_use_markup (GTK_LABEL (area->priv->label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (area->priv->label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (area->priv->label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (area->priv->label), 0.0, 0.5);
+ gtk_widget_set_can_focus (area->priv->label, TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), area->priv->label, TRUE, TRUE, 0);
+ gtk_widget_show (area->priv->label);
+
+ area->priv->secondary_label = gtk_label_new (NULL);
+ gtk_label_set_use_markup (GTK_LABEL (area->priv->secondary_label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (area->priv->secondary_label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (area->priv->secondary_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (area->priv->secondary_label), 0.0, 0.5);
+ gtk_widget_set_can_focus (area->priv->secondary_label, TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), area->priv->secondary_label, TRUE, TRUE, 0);
+
+ area->priv->image = gtk_image_new_from_stock (NULL, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (area->priv->image), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), area->priv->image, FALSE, FALSE, 0);
+ gtk_widget_show (area->priv->image);
+
+ gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ gtk_box_pack_start (GTK_BOX (area->priv->main_box), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (area));
+ gtk_container_add (GTK_CONTAINER (content_area), area->priv->main_box);
+ gtk_widget_show (area->priv->main_box);
+}
+
+static void
+ev_message_area_set_image_for_type (EvMessageArea *area,
+ GtkMessageType type)
+{
+ const gchar *stock_id = NULL;
+ AtkObject *atk_obj;
+
+ switch (type) {
+ case GTK_MESSAGE_INFO:
+ stock_id = GTK_STOCK_DIALOG_INFO;
+ break;
+ case GTK_MESSAGE_QUESTION:
+ stock_id = GTK_STOCK_DIALOG_QUESTION;
+ break;
+ case GTK_MESSAGE_WARNING:
+ stock_id = GTK_STOCK_DIALOG_WARNING;
+ break;
+ case GTK_MESSAGE_ERROR:
+ stock_id = GTK_STOCK_DIALOG_ERROR;
+ break;
+ case GTK_MESSAGE_OTHER:
+ break;
+ default:
+ g_warning ("Unknown GtkMessageType %u", type);
+ break;
+ }
+
+ if (stock_id)
+ gtk_image_set_from_stock (GTK_IMAGE (area->priv->image), stock_id,
+ GTK_ICON_SIZE_DIALOG);
+
+ atk_obj = gtk_widget_get_accessible (GTK_WIDGET (area));
+ if (GTK_IS_ACCESSIBLE (atk_obj)) {
+ atk_object_set_role (atk_obj, ATK_ROLE_ALERT);
+ if (stock_id) {
+ GtkStockItem item;
+
+ gtk_stock_lookup (stock_id, &item);
+ atk_object_set_name (atk_obj, item.label);
+ }
+ }
+}
+
+static void
+ev_message_area_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EvMessageArea *area = EV_MESSAGE_AREA (object);
+
+ switch (prop_id) {
+ case PROP_TEXT:
+ ev_message_area_set_text (area, g_value_get_string (value));
+ break;
+ case PROP_SECONDARY_TEXT:
+ ev_message_area_set_secondary_text (area, g_value_get_string (value));
+ break;
+ case PROP_IMAGE:
+ ev_message_area_set_image (area, (GtkWidget *)g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ev_message_area_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EvMessageArea *area = EV_MESSAGE_AREA (object);
+
+ switch (prop_id) {
+ case PROP_TEXT:
+ g_value_set_string (value, gtk_label_get_label (GTK_LABEL (area->priv->label)));
+ break;
+ case PROP_SECONDARY_TEXT:
+ g_value_set_string (value, gtk_label_get_label (GTK_LABEL (area->priv->secondary_label)));
+ break;
+ case PROP_IMAGE:
+ g_value_set_object (value, area->priv->image);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+void
+_ev_message_area_add_buttons_valist (EvMessageArea *area,
+ const gchar *first_button_text,
+ va_list args)
+{
+ const gchar* text;
+ gint response_id;
+
+ if (first_button_text == NULL)
+ return;
+
+ text = first_button_text;
+ response_id = va_arg (args, gint);
+
+ while (text != NULL) {
+ gtk_info_bar_add_button (GTK_INFO_BAR (area), text, response_id);
+
+ text = va_arg (args, gchar*);
+ if (text == NULL)
+ break;
+
+ response_id = va_arg (args, int);
+ }
+}
+
+GtkWidget *
+_ev_message_area_get_main_box (EvMessageArea *area)
+{
+ return area->priv->main_box;
+}
+
+GtkWidget *
+ev_message_area_new (GtkMessageType type,
+ const gchar *text,
+ const gchar *first_button_text,
+ ...)
+{
+ GtkWidget *widget;
+
+ widget = g_object_new (EV_TYPE_MESSAGE_AREA,
+ "message-type", type,
+ "text", text,
+ NULL);
+ ev_message_area_set_image_for_type (EV_MESSAGE_AREA (widget), type);
+ if (first_button_text) {
+ va_list args;
+
+ va_start (args, first_button_text);
+ _ev_message_area_add_buttons_valist (EV_MESSAGE_AREA (widget),
+ first_button_text, args);
+ va_end (args);
+ }
+
+ return widget;
+}
+
+void
+ev_message_area_set_image (EvMessageArea *area,
+ GtkWidget *image)
+{
+ GtkWidget *parent;
+
+ g_return_if_fail (EV_IS_MESSAGE_AREA (area));
+
+ area->priv->message_type = GTK_MESSAGE_OTHER;
+
+ parent = gtk_widget_get_parent (area->priv->image);
+ gtk_container_add (GTK_CONTAINER (parent), image);
+ gtk_container_remove (GTK_CONTAINER (parent), area->priv->image);
+ gtk_box_reorder_child (GTK_BOX (parent), image, 0);
+
+ area->priv->image = image;
+
+ g_object_notify (G_OBJECT (area), "image");
+}
+
+void
+ev_message_area_set_image_from_stock (EvMessageArea *area,
+ const gchar *stock_id)
+{
+ g_return_if_fail (EV_IS_MESSAGE_AREA (area));
+ g_return_if_fail (stock_id != NULL);
+
+ gtk_image_set_from_stock (GTK_IMAGE (area->priv->image),
+ stock_id,
+ GTK_ICON_SIZE_DIALOG);
+}
+
+void
+ev_message_area_set_text (EvMessageArea *area,
+ const gchar *str)
+{
+ g_return_if_fail (EV_IS_MESSAGE_AREA (area));
+
+ if (str) {
+ gchar *msg;
+
+ msg = g_strdup_printf ("<b>%s</b>", str);
+ gtk_label_set_markup (GTK_LABEL (area->priv->label), msg);
+ g_free (msg);
+ } else {
+ gtk_label_set_markup (GTK_LABEL (area->priv->label), NULL);
+ }
+
+ g_object_notify (G_OBJECT (area), "text");
+}
+
+void
+ev_message_area_set_secondary_text (EvMessageArea *area,
+ const gchar *str)
+{
+ g_return_if_fail (EV_IS_MESSAGE_AREA (area));
+
+ if (str) {
+ gchar *msg;
+
+ msg = g_strdup_printf ("<small>%s</small>", str);
+ gtk_label_set_markup (GTK_LABEL (area->priv->secondary_label), msg);
+ g_free (msg);
+ gtk_widget_show (area->priv->secondary_label);
+ } else {
+ gtk_label_set_markup (GTK_LABEL (area->priv->secondary_label), NULL);
+ gtk_widget_hide (area->priv->secondary_label);
+ }
+
+ g_object_notify (G_OBJECT (area), "secondary-text");
+}
diff --git a/shell/ev-message-area.h b/shell/ev-message-area.h
new file mode 100644
index 00000000..0fc7dd8c
--- /dev/null
+++ b/shell/ev-message-area.h
@@ -0,0 +1,74 @@
+/* ev-message-area.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2007 Carlos Garcia Campos
+ *
+ * Author:
+ * Carlos Garcia Campos <[email protected]>
+ *
+ * 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_MESSAGE_AREA_H
+#define EV_MESSAGE_AREA_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_MESSAGE_AREA (ev_message_area_get_type ())
+#define EV_MESSAGE_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_MESSAGE_AREA, EvMessageArea))
+#define EV_MESSAGE_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_MESSAGE_AREA, EvMessageAreaClass))
+#define EV_IS_MESSAGE_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_MESSAGE_AREA))
+#define EV_IS_MESSAGE_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_MESSAGE_AREA))
+#define EV_MESSAGE_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_MESSAGE_AREA, EvMessageAreaClass))
+
+typedef struct _EvMessageArea EvMessageArea;
+typedef struct _EvMessageAreaClass EvMessageAreaClass;
+typedef struct _EvMessageAreaPrivate EvMessageAreaPrivate;
+
+struct _EvMessageArea {
+ GtkInfoBar parent_instance;
+
+ /*< private >*/
+ EvMessageAreaPrivate *priv;
+};
+
+struct _EvMessageAreaClass {
+ GtkInfoBarClass parent_class;
+};
+
+GType ev_message_area_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_message_area_new (GtkMessageType type,
+ const gchar *text,
+ const gchar *first_button_text,
+ ...);
+void ev_message_area_set_image (EvMessageArea *area,
+ GtkWidget *image);
+void ev_message_area_set_image_from_stock (EvMessageArea *area,
+ const gchar *stock_id);
+void ev_message_area_set_text (EvMessageArea *area,
+ const gchar *str);
+void ev_message_area_set_secondary_text (EvMessageArea *area,
+ const gchar *str);
+
+void _ev_message_area_add_buttons_valist (EvMessageArea *area,
+ const gchar *first_button_text,
+ va_list args);
+GtkWidget *_ev_message_area_get_main_box (EvMessageArea *area);
+
+G_END_DECLS
+
+#endif /* EV_MESSAGE_AREA_H */
diff --git a/shell/ev-metadata.c b/shell/ev-metadata.c
new file mode 100644
index 00000000..9056b2fd
--- /dev/null
+++ b/shell/ev-metadata.c
@@ -0,0 +1,313 @@
+/* ev-metadata.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2009 Carlos Garcia Campos <[email protected]>
+ *
+ * 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 <gio/gio.h>
+#include <string.h>
+
+#include "ev-metadata.h"
+
+struct _EvMetadata {
+ GObject base;
+
+ GFile *file;
+ GHashTable *items;
+};
+
+struct _EvMetadataClass {
+ GObjectClass base_class;
+};
+
+G_DEFINE_TYPE (EvMetadata, ev_metadata, G_TYPE_OBJECT)
+
+#define EV_METADATA_NAMESPACE "metadata::evince"
+
+static void
+ev_metadata_finalize (GObject *object)
+{
+ EvMetadata *metadata = EV_METADATA (object);
+
+ if (metadata->items) {
+ g_hash_table_destroy (metadata->items);
+ metadata->items = NULL;
+ }
+
+ if (metadata->file) {
+ g_object_unref (metadata->file);
+ metadata->file = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_metadata_parent_class)->finalize (object);
+}
+
+static void
+ev_metadata_init (EvMetadata *metadata)
+{
+ metadata->items = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_free);
+}
+
+static void
+ev_metadata_class_init (EvMetadataClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = ev_metadata_finalize;
+}
+
+static void
+ev_metadata_load (EvMetadata *metadata)
+{
+ GFileInfo *info;
+ gchar **attrs;
+ gint i;
+ GError *error = NULL;
+
+ info = g_file_query_info (metadata->file, "metadata::*", 0, NULL, &error);
+ if (!info) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+
+ return;
+ }
+
+ if (!g_file_info_has_namespace (info, "metadata")) {
+ g_object_unref (info);
+
+ return;
+ }
+
+ attrs = g_file_info_list_attributes (info, "metadata");
+ for (i = 0; attrs[i]; i++) {
+ GFileAttributeType type;
+ gpointer value;
+ const gchar *key;
+
+ if (!g_str_has_prefix (attrs[i], EV_METADATA_NAMESPACE))
+ continue;
+
+ if (!g_file_info_get_attribute_data (info, attrs[i],
+ &type, &value, NULL)) {
+ continue;
+ }
+
+ key = attrs[i] + strlen (EV_METADATA_NAMESPACE"::");
+
+ if (type == G_FILE_ATTRIBUTE_TYPE_STRING) {
+ g_hash_table_insert (metadata->items,
+ g_strdup (key),
+ g_strdup (value));
+ }
+ }
+ g_strfreev (attrs);
+ g_object_unref (info);
+}
+
+EvMetadata *
+ev_metadata_new (GFile *file)
+{
+ EvMetadata *metadata;
+
+ g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+ metadata = EV_METADATA (g_object_new (EV_TYPE_METADATA, NULL));
+ metadata->file = g_object_ref (file);
+
+ ev_metadata_load (metadata);
+
+ return metadata;
+}
+
+gboolean
+ev_metadata_is_empty (EvMetadata *metadata)
+{
+ return g_hash_table_size (metadata->items) == 0;
+}
+
+gboolean
+ev_metadata_get_string (EvMetadata *metadata,
+ const gchar *key,
+ gchar **value)
+{
+ gchar *v;
+
+ v = g_hash_table_lookup (metadata->items, key);
+ if (!v)
+ return FALSE;
+
+ *value = v;
+ return TRUE;
+}
+
+static void
+metadata_set_callback (GObject *file,
+ GAsyncResult *result,
+ EvMetadata *metadata)
+{
+ GError *error = NULL;
+
+ if (!g_file_set_attributes_finish (G_FILE (file), result, NULL, &error)) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+gboolean
+ev_metadata_set_string (EvMetadata *metadata,
+ const gchar *key,
+ const gchar *value)
+{
+ GFileInfo *info;
+ gchar *gio_key;
+
+ info = g_file_info_new ();
+
+ gio_key = g_strconcat (EV_METADATA_NAMESPACE"::", key, NULL);
+ if (value) {
+ g_file_info_set_attribute_string (info, gio_key, value);
+ } else {
+ g_file_info_set_attribute (info, gio_key,
+ G_FILE_ATTRIBUTE_TYPE_INVALID,
+ NULL);
+ }
+ g_free (gio_key);
+
+ g_hash_table_insert (metadata->items, g_strdup (key), g_strdup (value));
+ g_file_set_attributes_async (metadata->file,
+ info,
+ 0,
+ G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback)metadata_set_callback,
+ metadata);
+ g_object_unref (info);
+
+ return TRUE;
+}
+
+gboolean
+ev_metadata_get_int (EvMetadata *metadata,
+ const gchar *key,
+ gint *value)
+{
+ gchar *string_value;
+ gchar *endptr;
+ gint int_value;
+
+ if (!ev_metadata_get_string (metadata, key, &string_value))
+ return FALSE;
+
+ int_value = g_ascii_strtoull (string_value, &endptr, 0);
+ if (int_value == 0 && string_value == endptr)
+ return FALSE;
+
+ *value = int_value;
+ return TRUE;
+}
+
+gboolean
+ev_metadata_set_int (EvMetadata *metadata,
+ const gchar *key,
+ gint value)
+{
+ gchar string_value[32];
+
+ g_snprintf (string_value, sizeof (string_value), "%d", value);
+
+ return ev_metadata_set_string (metadata, key, string_value);
+}
+
+gboolean
+ev_metadata_get_double (EvMetadata *metadata,
+ const gchar *key,
+ gdouble *value)
+{
+ gchar *string_value;
+ gchar *endptr;
+ gdouble double_value;
+
+ if (!ev_metadata_get_string (metadata, key, &string_value))
+ return FALSE;
+
+ double_value = g_ascii_strtod (string_value, &endptr);
+ if (double_value == 0. && string_value == endptr)
+ return FALSE;
+
+ *value = double_value;
+ return TRUE;
+}
+
+gboolean
+ev_metadata_set_double (EvMetadata *metadata,
+ const gchar *key,
+ gdouble value)
+{
+ gchar string_value[G_ASCII_DTOSTR_BUF_SIZE];
+
+ g_ascii_dtostr (string_value, G_ASCII_DTOSTR_BUF_SIZE, value);
+
+ return ev_metadata_set_string (metadata, key, string_value);
+}
+
+gboolean
+ev_metadata_get_boolean (EvMetadata *metadata,
+ const gchar *key,
+ gboolean *value)
+{
+ gint int_value;
+
+ if (!ev_metadata_get_int (metadata, key, &int_value))
+ return FALSE;
+
+ *value = int_value;
+ return TRUE;
+}
+
+gboolean
+ev_metadata_set_boolean (EvMetadata *metadata,
+ const gchar *key,
+ gboolean value)
+{
+ return ev_metadata_set_string (metadata, key, value ? "1" : "0");
+}
+
+gboolean
+ev_is_metadata_supported_for_file (GFile *file)
+{
+ GFileAttributeInfoList *namespaces;
+ gint i;
+ gboolean retval = FALSE;
+
+ namespaces = g_file_query_writable_namespaces (file, NULL, NULL);
+ if (!namespaces)
+ return retval;
+
+ for (i = 0; i < namespaces->n_infos; i++) {
+ if (strcmp (namespaces->infos[i].name, "metadata") == 0) {
+ retval = TRUE;
+ break;
+ }
+ }
+
+ g_file_attribute_info_list_unref (namespaces);
+
+ return retval;
+}
diff --git a/shell/ev-metadata.h b/shell/ev-metadata.h
new file mode 100644
index 00000000..69468c80
--- /dev/null
+++ b/shell/ev-metadata.h
@@ -0,0 +1,69 @@
+/* ev-metadata.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2009 Carlos Garcia Campos <[email protected]>
+ *
+ * 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_METADATA_H
+#define EV_METADATA_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_METADATA (ev_metadata_get_type())
+#define EV_METADATA(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_METADATA, EvMetadata))
+#define EV_METADATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_METADATA, EvMetadataClass))
+#define EV_IS_METADATA(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_METADATA))
+
+typedef struct _EvMetadata EvMetadata;
+typedef struct _EvMetadataClass EvMetadataClass;
+
+GType ev_metadata_get_type (void) G_GNUC_CONST;
+EvMetadata *ev_metadata_new (GFile *file);
+gboolean ev_metadata_is_empty (EvMetadata *metadata);
+
+gboolean ev_metadata_get_string (EvMetadata *metadata,
+ const gchar *key,
+ gchar **value);
+gboolean ev_metadata_set_string (EvMetadata *metadata,
+ const gchar *key,
+ const gchar *value);
+gboolean ev_metadata_get_int (EvMetadata *metadata,
+ const gchar *key,
+ gint *value);
+gboolean ev_metadata_set_int (EvMetadata *metadata,
+ const gchar *key,
+ gint value);
+gboolean ev_metadata_get_double (EvMetadata *metadata,
+ const gchar *key,
+ gdouble *value);
+gboolean ev_metadata_set_double (EvMetadata *metadata,
+ const gchar *key,
+ gdouble value);
+gboolean ev_metadata_get_boolean (EvMetadata *metadata,
+ const gchar *key,
+ gboolean *value);
+gboolean ev_metadata_set_boolean (EvMetadata *metadata,
+ const gchar *key,
+ gboolean value);
+
+gboolean ev_is_metadata_supported_for_file (GFile *file);
+
+G_END_DECLS
+
+#endif /* EV_METADATA_H */
diff --git a/shell/ev-navigation-action-widget.c b/shell/ev-navigation-action-widget.c
new file mode 100644
index 00000000..95c7fdff
--- /dev/null
+++ b/shell/ev-navigation-action-widget.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2003, 2004 Marco Pesenti Gritti
+ * Copyright (C) 2003, 2004 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 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "ev-navigation-action-widget.h"
+
+#include <glib/gi18n.h>
+#include <string.h>
+
+static void ev_navigation_action_widget_init (EvNavigationActionWidget *action_widget);
+static void ev_navigation_action_widget_class_init (EvNavigationActionWidgetClass *action_widget);
+static void ev_navigation_action_widget_toggled (GtkToggleToolButton *toggle);
+static gboolean ev_navigation_action_widget_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data);
+
+G_DEFINE_TYPE (EvNavigationActionWidget, ev_navigation_action_widget, GTK_TYPE_TOGGLE_TOOL_BUTTON)
+
+enum
+{
+ SHOW_MENU,
+ LAST_SIGNAL
+};
+
+static gint signals[LAST_SIGNAL];
+
+static void
+ev_navigation_action_widget_init (EvNavigationActionWidget *action_widget)
+{
+ GtkWidget *toggle_button;
+
+ /* It's rather dirty hack but we need a child to connect to
+ * button press event
+ */
+
+ toggle_button = gtk_bin_get_child (GTK_BIN (action_widget));
+
+ g_signal_connect (toggle_button, "button-press-event",
+ G_CALLBACK (ev_navigation_action_widget_button_press_event),
+ action_widget);
+ return;
+}
+
+static void
+ev_navigation_action_widget_class_init (EvNavigationActionWidgetClass *klass)
+{
+ GtkToggleToolButtonClass *toggle_tool_button_class = GTK_TOGGLE_TOOL_BUTTON_CLASS (klass);
+
+ toggle_tool_button_class->toggled = ev_navigation_action_widget_toggled;
+
+ signals[SHOW_MENU] =
+ g_signal_new ("show-menu",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EvNavigationActionWidgetClass, show_menu),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static int
+menu_deactivate_cb (GtkMenuShell *menu_shell,
+ EvNavigationActionWidget *widget)
+{
+ gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (widget), FALSE);
+ return TRUE;
+}
+
+static void
+menu_detacher (GtkWidget *widget,
+ GtkMenu *menu)
+{
+ EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (widget);
+ g_return_if_fail (button->menu == menu);
+ button->menu = NULL;
+}
+
+void
+ev_navigation_action_widget_set_menu(EvNavigationActionWidget *button, GtkWidget *menu)
+{
+
+ if (button->menu == GTK_MENU (menu))
+ return;
+
+ if (button->menu && gtk_widget_get_visible (GTK_WIDGET (button->menu)))
+ gtk_menu_shell_deactivate (GTK_MENU_SHELL (button->menu));
+
+ if (button->menu) {
+ g_signal_handlers_disconnect_by_func (button->menu,
+ menu_deactivate_cb,
+ button);
+ gtk_menu_detach (button->menu);
+ }
+
+ button->menu = GTK_MENU (menu);
+
+ if (button->menu) {
+ gtk_menu_attach_to_widget (button->menu, GTK_WIDGET (button),
+ menu_detacher);
+ g_signal_connect (button->menu, "deactivate",
+ G_CALLBACK (menu_deactivate_cb), button);
+ }
+}
+
+static void
+menu_position_func (GtkMenu *menu,
+ int *x,
+ int *y,
+ gboolean *push_in,
+ EvNavigationActionWidget *button)
+{
+ GtkWidget *widget = GTK_WIDGET (button);
+ GtkRequisition menu_req;
+ GtkAllocation allocation;
+ GtkTextDirection direction;
+ GdkWindow *gdk_window;
+ GdkRectangle monitor;
+ gint monitor_num;
+ GdkScreen *screen;
+
+ gtk_widget_size_request (GTK_WIDGET (button->menu), &menu_req);
+ direction = gtk_widget_get_direction (widget);
+ screen = gtk_widget_get_screen (GTK_WIDGET (menu));
+
+ gdk_window = gtk_widget_get_window (widget);
+ monitor_num = gdk_screen_get_monitor_at_window (screen, gdk_window);
+ if (monitor_num < 0)
+ monitor_num = 0;
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ gdk_window_get_origin (gdk_window, x, y);
+ gtk_widget_get_allocation (widget, &allocation);
+ *x += allocation.x;
+ *y += allocation.y;
+
+ if (direction == GTK_TEXT_DIR_LTR)
+ *x += MAX (allocation.width - menu_req.width, 0);
+ else if (menu_req.width > allocation.width)
+ *x -= menu_req.width - allocation.width;
+
+ if ((*y + allocation.height + menu_req.height) <= monitor.y + monitor.height)
+ *y += allocation.height;
+ else if ((*y - menu_req.height) >= monitor.y)
+ *y -= menu_req.height;
+ else if (monitor.y + monitor.height - (*y + allocation.height) > *y)
+ *y += allocation.height;
+ else
+ *y -= menu_req.height;
+
+ *push_in = FALSE;
+}
+
+static void
+popup_menu_under_arrow (EvNavigationActionWidget *button,
+ GdkEventButton *event)
+{
+ g_signal_emit (button, signals[SHOW_MENU], 0);
+
+ if (!button->menu)
+ return;
+
+ gtk_menu_popup (button->menu, NULL, NULL,
+ (GtkMenuPositionFunc) menu_position_func,
+ button,
+ event ? event->button : 0,
+ event ? event->time : gtk_get_current_event_time ());
+}
+
+static void
+ev_navigation_action_widget_toggled (GtkToggleToolButton *toggle)
+{
+ EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (toggle);
+ if (!button->menu)
+ return;
+
+ if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button)) &&
+ !gtk_widget_get_visible (GTK_WIDGET (button->menu))) {
+ /* we get here only when the menu is activated by a key
+ * press, so that we can select the first menu item */
+ popup_menu_under_arrow (button, NULL);
+ gtk_menu_shell_select_first (GTK_MENU_SHELL (button->menu), FALSE);
+ }
+}
+
+static gboolean
+ev_navigation_action_widget_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (data);
+
+ if (event->button == 1) {
+ popup_menu_under_arrow (button, event);
+ gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (button), TRUE);
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/shell/ev-navigation-action-widget.h b/shell/ev-navigation-action-widget.h
new file mode 100644
index 00000000..4241cd31
--- /dev/null
+++ b/shell/ev-navigation-action-widget.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2003, 2004 Marco Pesenti Gritti
+ * Copyright (C) 2003, 2004 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 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <gtk/gtk.h>
+
+#define EV_TYPE_NAVIGATION_ACTION_WIDGET (ev_navigation_action_widget_get_type ())
+#define EV_NAVIGATION_ACTION_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_NAVIGATION_ACTION_WIDGET, EvNavigationActionWidget))
+
+typedef struct _EvNavigationActionWidget EvNavigationActionWidget;
+typedef struct _EvNavigationActionWidgetClass EvNavigationActionWidgetClass;
+
+struct _EvNavigationActionWidget
+{
+ GtkToggleToolButton parent;
+
+ GtkMenu *menu;
+};
+
+struct _EvNavigationActionWidgetClass
+{
+ GtkToggleToolButtonClass parent_class;
+
+ void (*show_menu) (EvNavigationActionWidget *widget);
+};
+
+GType ev_navigation_action_widget_get_type (void);
+
+void
+ev_navigation_action_widget_set_menu(EvNavigationActionWidget *widget, GtkWidget *menu);
diff --git a/shell/ev-navigation-action.c b/shell/ev-navigation-action.c
new file mode 100644
index 00000000..b34c3789
--- /dev/null
+++ b/shell/ev-navigation-action.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2003, 2004 Marco Pesenti Gritti
+ * Copyright (C) 2003, 2004 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 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ev-navigation-action.h"
+#include "ev-navigation-action-widget.h"
+
+
+enum
+{
+ WIDGET_ACTIVATE_LINK,
+ WIDGET_N_SIGNALS
+};
+
+static guint widget_signals[WIDGET_N_SIGNALS] = {0, };
+
+struct _EvNavigationActionPrivate
+{
+ EvHistory *history;
+};
+
+static void ev_navigation_action_init (EvNavigationAction *action);
+static void ev_navigation_action_class_init (EvNavigationActionClass *class);
+
+G_DEFINE_TYPE (EvNavigationAction, ev_navigation_action, GTK_TYPE_ACTION)
+
+#define MAX_LABEL_LENGTH 48
+
+#define EV_NAVIGATION_ACTION_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_NAVIGATION_ACTION, EvNavigationActionPrivate))
+
+static void
+ev_navigation_action_history_changed (EvHistory *history,
+ gpointer data)
+{
+ EvNavigationAction *action = EV_NAVIGATION_ACTION (data);
+
+ gtk_action_set_sensitive (GTK_ACTION (action),
+ ev_history_get_n_links (history) > 0);
+}
+
+void
+ev_navigation_action_set_history (EvNavigationAction *action,
+ EvHistory *history)
+{
+ action->priv->history = history;
+
+ g_object_add_weak_pointer (G_OBJECT (action->priv->history),
+ (gpointer) &action->priv->history);
+
+ g_signal_connect_object (history, "changed",
+ G_CALLBACK (ev_navigation_action_history_changed),
+ action, 0);
+}
+
+static void
+activate_menu_item_cb (GtkWidget *widget, EvNavigationAction *action)
+{
+ int index;
+
+ g_return_if_fail (EV_IS_HISTORY (action->priv->history));
+
+ index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "index"));
+
+ if (action->priv->history) {
+ EvLink *link;
+
+ link = ev_history_get_link_nth (action->priv->history, index);
+
+ g_signal_emit (action, widget_signals[WIDGET_ACTIVATE_LINK], 0, link);
+ }
+}
+
+static GtkWidget *
+new_history_menu_item (EvNavigationAction *action,
+ EvLink *link,
+ int index)
+{
+ GtkLabel *label;
+ GtkWidget *item;
+ const char *title;
+
+ title = ev_link_get_title (link);
+ item = gtk_image_menu_item_new_with_label (title);
+ label = GTK_LABEL (gtk_bin_get_child (GTK_BIN (item)));
+ gtk_label_set_use_markup (label, TRUE);
+ g_object_set_data (G_OBJECT (item), "index",
+ GINT_TO_POINTER (index));
+
+ gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_END);
+ gtk_label_set_max_width_chars (label, MAX_LABEL_LENGTH);
+
+ g_signal_connect (item, "activate",
+ G_CALLBACK (activate_menu_item_cb),
+ action);
+
+ gtk_widget_show (item);
+
+ return item;
+}
+
+static GtkWidget *
+build_menu (EvNavigationAction *action)
+{
+ GtkMenuShell *menu;
+ GtkWidget *item;
+ EvLink *link;
+ EvHistory *history = action->priv->history;
+ int start, end, i;
+
+ if (history == NULL || ev_history_get_n_links (history) <= 0) {
+ return NULL;
+ }
+
+ menu = GTK_MENU_SHELL (gtk_menu_new ());
+
+ start = 0;
+ end = ev_history_get_n_links (history);
+
+ for (i = start; i < end; i++) {
+ link = ev_history_get_link_nth (history, i);
+ item = new_history_menu_item (action, link, i);
+ gtk_menu_shell_prepend (menu, item);
+ }
+
+ return GTK_WIDGET (menu);
+}
+
+static void
+menu_activated_cb (EvNavigationActionWidget *button,
+ EvNavigationAction *action)
+{
+ GtkWidget *menu;
+
+ menu = build_menu (action);
+ ev_navigation_action_widget_set_menu (button, menu);
+}
+
+static void
+connect_proxy (GtkAction *action, GtkWidget *proxy)
+{
+ GtkWidget *menu;
+
+ if (GTK_IS_TOOL_ITEM (proxy)) {
+ /* set dummy menu so the arrow gets sensitive */
+ menu = gtk_menu_new ();
+ ev_navigation_action_widget_set_menu (EV_NAVIGATION_ACTION_WIDGET (proxy), menu);
+
+ g_signal_connect (proxy, "show-menu",
+ G_CALLBACK (menu_activated_cb), action);
+ }
+
+ GTK_ACTION_CLASS (ev_navigation_action_parent_class)->connect_proxy (action, proxy);
+}
+
+static GtkWidget *
+create_tool_item (GtkAction *action)
+{
+ EvNavigationActionWidget *proxy;
+
+ proxy = g_object_new (EV_TYPE_NAVIGATION_ACTION_WIDGET, NULL);
+ gtk_widget_show (GTK_WIDGET (proxy));
+
+ return GTK_WIDGET (proxy);
+}
+
+static GtkWidget *
+create_menu_item (GtkAction *action)
+{
+ GtkWidget *menu;
+ GtkWidget *menu_item;
+
+ menu = build_menu (EV_NAVIGATION_ACTION (action));
+
+ menu_item = GTK_ACTION_CLASS (ev_navigation_action_parent_class)->create_menu_item (action);
+
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
+
+ gtk_widget_show (menu_item);
+
+ return menu_item;
+}
+
+static void
+ev_navigation_action_init (EvNavigationAction *action)
+{
+ action->priv = EV_NAVIGATION_ACTION_GET_PRIVATE (action);
+}
+
+static void
+ev_navigation_action_finalize (GObject *object)
+{
+ EvNavigationAction *action = EV_NAVIGATION_ACTION (object);
+
+ if (action->priv->history) {
+ g_object_remove_weak_pointer (G_OBJECT (action->priv->history),
+ (gpointer) &action->priv->history);
+ action->priv->history = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_navigation_action_parent_class)->finalize (object);
+}
+
+static void
+ev_navigation_action_class_init (EvNavigationActionClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkActionClass *action_class = GTK_ACTION_CLASS (class);
+
+ object_class->finalize = ev_navigation_action_finalize;
+
+ action_class->toolbar_item_type = GTK_TYPE_TOOL_ITEM;
+ action_class->create_tool_item = create_tool_item;
+ action_class->connect_proxy = connect_proxy;
+ action_class->create_menu_item = create_menu_item;
+
+ widget_signals[WIDGET_ACTIVATE_LINK] = g_signal_new ("activate_link",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvNavigationActionClass, activate_link),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1,
+ G_TYPE_OBJECT);
+
+ g_type_class_add_private (object_class, sizeof (EvNavigationActionPrivate));
+}
diff --git a/shell/ev-navigation-action.h b/shell/ev-navigation-action.h
new file mode 100644
index 00000000..8089e4f4
--- /dev/null
+++ b/shell/ev-navigation-action.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2003, 2004 Marco Pesenti Gritti
+ * Copyright (C) 2003, 2004 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 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef EV_NAVIGATION_ACTION_H
+#define EV_NAVIGATION_ACTION_H
+
+#include <gtk/gtk.h>
+
+#include "ev-history.h"
+#include "ev-link.h"
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_NAVIGATION_ACTION (ev_navigation_action_get_type ())
+#define EV_NAVIGATION_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_NAVIGATION_ACTION, EvNavigationAction))
+#define EV_NAVIGATION_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_NAVIGATION_ACTION, EvNavigationActionClass))
+#define EV_IS_NAVIGATION_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_NAVIGATION_ACTION))
+#define EV_IS_NAVIGATION_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EV_TYPE_NAVIGATION_ACTION))
+#define EV_NAVIGATION_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EV_TYPE_NAVIGATION_ACTION, EvNavigationActionClass))
+
+typedef struct _EvNavigationAction EvNavigationAction;
+typedef struct _EvNavigationActionPrivate EvNavigationActionPrivate;
+typedef struct _EvNavigationActionClass EvNavigationActionClass;
+
+struct _EvNavigationAction
+{
+ GtkAction parent;
+
+ /*< private >*/
+ EvNavigationActionPrivate *priv;
+};
+
+struct _EvNavigationActionClass
+{
+ GtkActionClass parent_class;
+
+ void (* activate_link) (EvNavigationAction *action,
+ EvLink *link);
+};
+
+GType ev_navigation_action_get_type (void);
+void ev_navigation_action_set_history (EvNavigationAction *action,
+ EvHistory *history);
+
+G_END_DECLS
+
+#endif
diff --git a/shell/ev-open-recent-action.c b/shell/ev-open-recent-action.c
new file mode 100644
index 00000000..557e87a6
--- /dev/null
+++ b/shell/ev-open-recent-action.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2007, Carlos Garcia Campos <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ev-open-recent-action.h"
+
+
+enum {
+ ITEM_ACTIVATED,
+ N_SIGNALS
+};
+
+static void ev_open_recent_action_init (EvOpenRecentAction *action);
+static void ev_open_recent_action_class_init (EvOpenRecentActionClass *class);
+
+static guint action_signals[N_SIGNALS];
+
+G_DEFINE_TYPE (EvOpenRecentAction, ev_open_recent_action, GTK_TYPE_ACTION)
+
+static void
+recent_chooser_item_activated (GtkRecentChooser *chooser,
+ GtkAction *action)
+{
+ gchar *uri;
+
+ uri = gtk_recent_chooser_get_current_uri (chooser);
+ g_signal_emit (action, action_signals[ITEM_ACTIVATED], 0, uri);
+ g_free (uri);
+}
+
+static GtkWidget *
+ev_open_recent_action_create_tool_item (GtkAction *action)
+{
+ GtkWidget *tool_item;
+ GtkWidget *toolbar_recent_menu;
+ GtkRecentFilter *filter;
+
+ toolbar_recent_menu = gtk_recent_chooser_menu_new_for_manager (gtk_recent_manager_get_default ());
+ gtk_recent_chooser_set_local_only (GTK_RECENT_CHOOSER (toolbar_recent_menu), FALSE);
+ gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (toolbar_recent_menu), GTK_RECENT_SORT_MRU);
+ gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (toolbar_recent_menu), 5);
+ g_signal_connect (toolbar_recent_menu, "item_activated",
+ G_CALLBACK (recent_chooser_item_activated),
+ action);
+
+ filter = gtk_recent_filter_new ();
+ gtk_recent_filter_add_application (filter, g_get_application_name ());
+ gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (toolbar_recent_menu), filter);
+
+ tool_item = GTK_WIDGET (gtk_menu_tool_button_new_from_stock (GTK_STOCK_OPEN));
+ gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (tool_item),
+ _("Open a recently used document"));
+ gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (tool_item),
+ GTK_WIDGET (toolbar_recent_menu));
+ return tool_item;
+}
+
+static void
+ev_open_recent_action_init (EvOpenRecentAction *action)
+{
+}
+
+static void
+ev_open_recent_action_class_init (EvOpenRecentActionClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkActionClass *action_class = GTK_ACTION_CLASS (class);
+
+ action_class->toolbar_item_type = GTK_TYPE_MENU_TOOL_BUTTON;
+ action_class->create_tool_item = ev_open_recent_action_create_tool_item;
+
+ action_signals[ITEM_ACTIVATED] =
+ g_signal_new ("item_activated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvOpenRecentActionClass, item_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+}
diff --git a/shell/ev-open-recent-action.h b/shell/ev-open-recent-action.h
new file mode 100644
index 00000000..87a8fed7
--- /dev/null
+++ b/shell/ev-open-recent-action.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2007, Carlos Garcia Campos <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA..
+ *
+ */
+
+#ifndef EV_OPEN_RECENT_ACTION_H
+#define EV_OPEN_RECENT_ACTION_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_OPEN_RECENT_ACTION (ev_open_recent_action_get_type ())
+#define EV_OPEN_RECENT_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_OPEN_RECENT_ACTION, EvOpenRecentAction))
+#define EV_OPEN_RECENT_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_OPEN_RECENT_ACTION, EvOpenRecentActionClass))
+#define EV_IS_OPEN_RECENT_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_OPEN_RECENT_ACTION))
+#define EV_IS_OPEN_RECENT_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), EV_TYPE_OPEN_RECENT_ACTION))
+#define EV_OPEN_RECENT_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EV_TYPE_OPEN_RECENT_ACTION, EvOpenRecentActionClass))
+
+typedef struct _EvOpenRecentAction EvOpenRecentAction;
+typedef struct _EvOpenRecentActionClass EvOpenRecentActionClass;
+
+struct _EvOpenRecentAction {
+ GtkAction parent;
+};
+
+struct _EvOpenRecentActionClass {
+ GtkActionClass parent_class;
+
+ void (* item_activated) (EvOpenRecentAction *action,
+ const gchar *uri);
+};
+
+GType ev_open_recent_action_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* EV_OPEN_RECENT_ACTION_H */
diff --git a/shell/ev-password-view.c b/shell/ev-password-view.c
new file mode 100644
index 00000000..8a1162ac
--- /dev/null
+++ b/shell/ev-password-view.c
@@ -0,0 +1,434 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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 <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+#include "ev-keyring.h"
+#include "ev-password-view.h"
+
+enum {
+ UNLOCK,
+ LAST_SIGNAL
+};
+struct _EvPasswordViewPrivate {
+ GtkWindow *parent_window;
+ GtkWidget *label;
+ GtkWidget *password_entry;
+
+ gchar *password;
+ GPasswordSave password_save;
+
+ GFile *uri_file;
+};
+
+#define EV_PASSWORD_VIEW_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_PASSWORD_VIEW, EvPasswordViewPrivate));
+
+static guint password_view_signals [LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (EvPasswordView, ev_password_view, GTK_TYPE_VIEWPORT)
+
+static void
+ev_password_view_finalize (GObject *object)
+{
+ EvPasswordView *password_view = EV_PASSWORD_VIEW (object);
+
+ if (password_view->priv->password) {
+ g_free (password_view->priv->password);
+ password_view->priv->password = NULL;
+ }
+
+ password_view->priv->parent_window = NULL;
+
+ if (password_view->priv->uri_file) {
+ g_object_unref (password_view->priv->uri_file);
+ password_view->priv->uri_file = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_password_view_parent_class)->finalize (object);
+}
+
+static void
+ev_password_view_class_init (EvPasswordViewClass *class)
+{
+ GObjectClass *g_object_class;
+ GtkWidgetClass *widget_class;
+
+ g_object_class = G_OBJECT_CLASS (class);
+ widget_class = GTK_WIDGET_CLASS (class);
+
+ password_view_signals[UNLOCK] =
+ g_signal_new ("unlock",
+ G_TYPE_FROM_CLASS (g_object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvPasswordViewClass, unlock),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (g_object_class, sizeof (EvPasswordViewPrivate));
+
+ g_object_class->finalize = ev_password_view_finalize;
+}
+
+static void
+ev_password_view_clicked_cb (GtkWidget *button,
+ EvPasswordView *password_view)
+{
+ ev_password_view_ask_password (password_view);
+}
+
+static void
+ev_password_view_init (EvPasswordView *password_view)
+{
+ GtkWidget *align;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GtkWidget *button;
+ GtkWidget *label;
+ gchar *markup;
+
+ password_view->priv = EV_PASSWORD_VIEW_GET_PRIVATE (password_view);
+
+ password_view->priv->password_save = G_PASSWORD_SAVE_NEVER;
+
+ gtk_widget_push_composite_child ();
+
+ /* set ourselves up */
+ align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
+ vbox = gtk_vbox_new (FALSE, 24);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 24);
+ gtk_container_add (GTK_CONTAINER (password_view), align);
+ gtk_container_add (GTK_CONTAINER (align), vbox);
+
+ password_view->priv->label =
+ (GtkWidget *) g_object_new (GTK_TYPE_LABEL,
+ "wrap", TRUE,
+ "selectable", TRUE,
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), password_view->priv->label, FALSE, FALSE, 0);
+
+ image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
+ GTK_ICON_SIZE_DIALOG);
+ gtk_box_pack_start (GTK_BOX (vbox), image, FALSE, FALSE, 0);
+
+ label = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ markup = g_strdup_printf ("<span size=\"x-large\">%s</span>",
+ _("This document is locked and can only be read by entering the correct password."));
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+
+ gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+ button = gtk_button_new_with_mnemonic (_("_Unlock Document"));
+ g_signal_connect (button, "clicked", G_CALLBACK (ev_password_view_clicked_cb), password_view);
+ gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (align);
+ gtk_widget_pop_composite_child ();
+}
+
+/* Public functions */
+void
+ev_password_view_set_uri (EvPasswordView *password_view,
+ const char *uri)
+{
+ gchar *markup, *file_name;
+ GFile *file;
+
+ g_return_if_fail (EV_IS_PASSWORD_VIEW (password_view));
+ g_return_if_fail (uri != NULL);
+
+ file = g_file_new_for_uri (uri);
+ if (password_view->priv->uri_file &&
+ g_file_equal (file, password_view->priv->uri_file)) {
+ g_object_unref (file);
+ return;
+ }
+ if (password_view->priv->uri_file)
+ g_object_unref (password_view->priv->uri_file);
+ password_view->priv->uri_file = file;
+
+ file_name = g_file_get_basename (password_view->priv->uri_file);
+ markup = g_markup_printf_escaped ("<span size=\"x-large\" weight=\"bold\">%s</span>",
+ file_name);
+ g_free (file_name);
+
+ gtk_label_set_markup (GTK_LABEL (password_view->priv->label), markup);
+ g_free (markup);
+}
+
+static void
+ev_password_dialog_got_response (GtkDialog *dialog,
+ gint response_id,
+ EvPasswordView *password_view)
+{
+ gtk_widget_set_sensitive (GTK_WIDGET (password_view), TRUE);
+
+ if (response_id == GTK_RESPONSE_OK) {
+ g_free (password_view->priv->password);
+ password_view->priv->password =
+ g_strdup (gtk_entry_get_text (GTK_ENTRY (password_view->priv->password_entry)));
+
+ g_signal_emit (password_view, password_view_signals[UNLOCK], 0);
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+ev_password_dialog_remember_button_toggled (GtkToggleButton *button,
+ EvPasswordView *password_view)
+{
+ if (gtk_toggle_button_get_active (button)) {
+ gpointer data;
+
+ data = g_object_get_data (G_OBJECT (button), "password-save");
+ password_view->priv->password_save = GPOINTER_TO_INT (data);
+ }
+}
+
+static void
+ev_password_dialog_entry_changed_cb (GtkEditable *editable,
+ GtkDialog *dialog)
+{
+ const char *text;
+
+ text = gtk_entry_get_text (GTK_ENTRY (editable));
+
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK,
+ (text != NULL && *text != '\0'));
+}
+
+static void
+ev_password_dialog_entry_activated_cb (GtkEntry *entry,
+ GtkDialog *dialog)
+{
+ gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+}
+
+void
+ev_password_view_ask_password (EvPasswordView *password_view)
+{
+ GtkDialog *dialog;
+ GtkWidget *content_area, *action_area;
+ GtkWidget *entry_container;
+ GtkWidget *hbox, *main_vbox, *vbox, *icon;
+ GtkWidget *table;
+ GtkWidget *label;
+ gchar *format, *markup, *file_name;
+
+ gtk_widget_set_sensitive (GTK_WIDGET (password_view), FALSE);
+
+ dialog = GTK_DIALOG (gtk_dialog_new ());
+ content_area = gtk_dialog_get_content_area (dialog);
+ action_area = gtk_dialog_get_action_area (dialog);
+
+ /* Set the dialog up with HIG properties */
+ gtk_dialog_set_has_separator (dialog, FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
+ gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
+ gtk_box_set_spacing (GTK_BOX (action_area), 6);
+
+ gtk_window_set_title (GTK_WINDOW (dialog), _("Enter password"));
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_DIALOG_AUTHENTICATION);
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), password_view->priv->parent_window);
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+
+ gtk_dialog_add_buttons (dialog,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ _("_Unlock Document"), GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (dialog, GTK_RESPONSE_OK);
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK, FALSE);
+ gtk_dialog_set_alternative_button_order (dialog,
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ /* Build contents */
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+ gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
+ gtk_widget_show (hbox);
+
+ icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
+ GTK_ICON_SIZE_DIALOG);
+
+ gtk_misc_set_alignment (GTK_MISC (icon), 0.5, 0.0);
+ gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
+ gtk_widget_show (icon);
+
+ main_vbox = gtk_vbox_new (FALSE, 18);
+ gtk_box_pack_start (GTK_BOX (hbox), main_vbox, TRUE, TRUE, 0);
+ gtk_widget_show (main_vbox);
+
+ label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ file_name = g_file_get_basename (password_view->priv->uri_file);
+ format = g_strdup_printf ("<span size=\"larger\" weight=\"bold\">%s</span>\n\n%s",
+ _("Password required"),
+ _("The document ā€œ%sā€ is locked and requires a password before it can be opened."));
+ markup = g_markup_printf_escaped (format, file_name);
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (format);
+ g_free (markup);
+ g_free (file_name);
+ gtk_box_pack_start (GTK_BOX (main_vbox), label,
+ FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ /* The table that holds the entries */
+ entry_container = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
+
+ gtk_alignment_set_padding (GTK_ALIGNMENT (entry_container),
+ 0, 0, 0, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), entry_container,
+ FALSE, FALSE, 0);
+ gtk_widget_show (entry_container);
+
+ table = gtk_table_new (1, 2, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_container_add (GTK_CONTAINER (entry_container), table);
+ gtk_widget_show (table);
+
+ label = gtk_label_new_with_mnemonic (_("_Password:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+ password_view->priv->password_entry = gtk_entry_new ();
+ gtk_entry_set_visibility (GTK_ENTRY (password_view->priv->password_entry), FALSE);
+ g_signal_connect (password_view->priv->password_entry, "changed",
+ G_CALLBACK (ev_password_dialog_entry_changed_cb),
+ dialog);
+ g_signal_connect (password_view->priv->password_entry, "activate",
+ G_CALLBACK (ev_password_dialog_entry_activated_cb),
+ dialog);
+ gtk_table_attach (GTK_TABLE (table), label,
+ 0, 1, 0, 1,
+ GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ gtk_table_attach_defaults (GTK_TABLE (table), password_view->priv->password_entry,
+ 1, 2, 0, 1);
+ gtk_widget_show (password_view->priv->password_entry);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label),
+ password_view->priv->password_entry);
+
+ if (ev_keyring_is_available ()) {
+ GtkWidget *choice;
+ GtkWidget *remember_box;
+ GSList *group;
+
+ remember_box = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), remember_box,
+ FALSE, FALSE, 0);
+ gtk_widget_show (remember_box);
+
+ choice = gtk_radio_button_new_with_mnemonic (NULL, _("Forget password _immediately"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (choice),
+ password_view->priv->password_save == G_PASSWORD_SAVE_NEVER);
+ g_object_set_data (G_OBJECT (choice), "password-save",
+ GINT_TO_POINTER (G_PASSWORD_SAVE_NEVER));
+ g_signal_connect (choice, "toggled",
+ G_CALLBACK (ev_password_dialog_remember_button_toggled),
+ password_view);
+ gtk_box_pack_start (GTK_BOX (remember_box), choice, FALSE, FALSE, 0);
+ gtk_widget_show (choice);
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (choice));
+ choice = gtk_radio_button_new_with_mnemonic (group, _("Remember password until you _log out"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (choice),
+ password_view->priv->password_save == G_PASSWORD_SAVE_FOR_SESSION);
+ g_object_set_data (G_OBJECT (choice), "password-save",
+ GINT_TO_POINTER (G_PASSWORD_SAVE_FOR_SESSION));
+ g_signal_connect (choice, "toggled",
+ G_CALLBACK (ev_password_dialog_remember_button_toggled),
+ password_view);
+ gtk_box_pack_start (GTK_BOX (remember_box), choice, FALSE, FALSE, 0);
+ gtk_widget_show (choice);
+
+ group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (choice));
+ choice = gtk_radio_button_new_with_mnemonic (group, _("Remember _forever"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (choice),
+ password_view->priv->password_save == G_PASSWORD_SAVE_PERMANENTLY);
+ g_object_set_data (G_OBJECT (choice), "password-save",
+ GINT_TO_POINTER (G_PASSWORD_SAVE_PERMANENTLY));
+ g_signal_connect (choice, "toggled",
+ G_CALLBACK (ev_password_dialog_remember_button_toggled),
+ password_view);
+ gtk_box_pack_start (GTK_BOX (remember_box), choice, FALSE, FALSE, 0);
+ gtk_widget_show (choice);
+ }
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (ev_password_dialog_got_response),
+ password_view);
+
+ gtk_widget_show (GTK_WIDGET (dialog));
+}
+
+const gchar *
+ev_password_view_get_password (EvPasswordView *password_view)
+{
+ return password_view->priv->password;
+}
+
+GPasswordSave
+ev_password_view_get_password_save_flags (EvPasswordView *password_view)
+{
+ return password_view->priv->password_save;
+}
+
+GtkWidget *
+ev_password_view_new (GtkWindow *parent)
+{
+ EvPasswordView *retval;
+
+ retval = EV_PASSWORD_VIEW (g_object_new (EV_TYPE_PASSWORD_VIEW, NULL));
+
+ retval->priv->parent_window = parent;
+
+ return GTK_WIDGET (retval);
+}
+
diff --git a/shell/ev-password-view.h b/shell/ev-password-view.h
new file mode 100644
index 00000000..a98e40c2
--- /dev/null
+++ b/shell/ev-password-view.h
@@ -0,0 +1,59 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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_PASSWORD_VIEW_H__
+#define __EV_PASSWORD_VIEW_H__
+
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_PASSWORD_VIEW (ev_password_view_get_type ())
+#define EV_PASSWORD_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_PASSWORD_VIEW, EvPasswordView))
+#define EV_IS_PASSWORD_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_PASSWORD_VIEW))
+
+typedef struct _EvPasswordView EvPasswordView;
+typedef struct _EvPasswordViewClass EvPasswordViewClass;
+typedef struct _EvPasswordViewPrivate EvPasswordViewPrivate;
+
+struct _EvPasswordView {
+ GtkViewport parent_instance;
+
+ EvPasswordViewPrivate *priv;
+};
+
+struct _EvPasswordViewClass {
+ GtkViewportClass parent_class;
+
+ /* signals */
+ void (*unlock) (EvPasswordView *self);
+};
+
+GType ev_password_view_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_password_view_new (GtkWindow *parent);
+void ev_password_view_set_uri (EvPasswordView *password_view,
+ const char *uri);
+void ev_password_view_ask_password (EvPasswordView *password_view);
+const gchar *ev_password_view_get_password (EvPasswordView *password_view);
+GPasswordSave ev_password_view_get_password_save_flags (EvPasswordView *password_view);
+
+G_END_DECLS
+
+#endif /* __EV_PASSWORD_VIEW_H__ */
diff --git a/shell/ev-progress-message-area.c b/shell/ev-progress-message-area.c
new file mode 100644
index 00000000..a0b74b0a
--- /dev/null
+++ b/shell/ev-progress-message-area.c
@@ -0,0 +1,198 @@
+/* ev-progress-message-area.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ *
+ * 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 <gtk/gtk.h>
+
+#include "ev-progress-message-area.h"
+
+#define EV_PROGRESS_MESSAGE_AREA_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EV_TYPE_PROGRESS_MESSAGE_AREA, EvProgressMessageAreaPrivate))
+
+struct _EvProgressMessageAreaPrivate {
+ GtkWidget *label;
+ GtkWidget *progress_bar;
+};
+
+enum {
+ PROP_0,
+ PROP_STATUS,
+ PROP_FRACTION
+};
+
+static void ev_progress_message_area_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void ev_progress_message_area_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+G_DEFINE_TYPE (EvProgressMessageArea, ev_progress_message_area, EV_TYPE_MESSAGE_AREA)
+
+static void
+ev_progress_message_area_class_init (EvProgressMessageAreaClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = ev_progress_message_area_set_property;
+ gobject_class->get_property = ev_progress_message_area_get_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_STATUS,
+ g_param_spec_string ("status",
+ "Status",
+ "The status text of the progress area",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_FRACTION,
+ g_param_spec_double ("fraction",
+ "Fraction",
+ "The fraction of total work that has been completed",
+ 0.0, 1.0, 0.0,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (gobject_class, sizeof (EvProgressMessageAreaPrivate));
+}
+
+static void
+ev_progress_message_area_init (EvProgressMessageArea *area)
+{
+ GtkWidget *contents;
+ GtkWidget *vbox;
+
+ area->priv = EV_PROGRESS_MESSAGE_AREA_GET_PRIVATE (area);
+
+ contents = _ev_message_area_get_main_box (EV_MESSAGE_AREA (area));
+
+ vbox = gtk_vbox_new (FALSE, 6);
+
+ area->priv->label = gtk_label_new (NULL);
+ gtk_label_set_use_markup (GTK_LABEL (area->priv->label), TRUE);
+ gtk_label_set_ellipsize (GTK_LABEL (area->priv->label),
+ PANGO_ELLIPSIZE_END);
+ gtk_misc_set_alignment (GTK_MISC (area->priv->label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox), area->priv->label, TRUE, TRUE, 0);
+ gtk_widget_show (area->priv->label);
+
+ area->priv->progress_bar = gtk_progress_bar_new ();
+ gtk_widget_set_size_request (area->priv->progress_bar, -1, 15);
+ gtk_box_pack_start (GTK_BOX (vbox), area->priv->progress_bar, TRUE, FALSE, 0);
+ gtk_widget_show (area->priv->progress_bar);
+
+ gtk_box_pack_start (GTK_BOX (contents), vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+}
+
+static void
+ev_progress_message_area_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EvProgressMessageArea *area = EV_PROGRESS_MESSAGE_AREA (object);
+
+ switch (prop_id) {
+ case PROP_STATUS:
+ ev_progress_message_area_set_status (area, g_value_get_string (value));
+ break;
+ case PROP_FRACTION:
+ ev_progress_message_area_set_fraction (area, g_value_get_double (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ev_progress_message_area_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EvProgressMessageArea *area = EV_PROGRESS_MESSAGE_AREA (object);
+
+ switch (prop_id) {
+ case PROP_STATUS:
+ g_value_set_string (value, gtk_label_get_label (GTK_LABEL (area->priv->label)));
+ break;
+ case PROP_FRACTION: {
+ gdouble fraction;
+
+ fraction = gtk_progress_bar_get_fraction (GTK_PROGRESS_BAR (area->priv->progress_bar));
+ g_value_set_double (value, fraction);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+GtkWidget *
+ev_progress_message_area_new (const gchar *stock_id,
+ const gchar *text,
+ const gchar *first_button_text,
+ ...)
+{
+ GtkWidget *widget;
+
+ widget = g_object_new (EV_TYPE_PROGRESS_MESSAGE_AREA,
+ "message-type", GTK_MESSAGE_OTHER,
+ "text", text,
+ NULL);
+ if (first_button_text) {
+ va_list args;
+
+ va_start (args, first_button_text);
+ _ev_message_area_add_buttons_valist (EV_MESSAGE_AREA (widget),
+ first_button_text,
+ args);
+ va_end (args);
+ }
+
+ ev_message_area_set_image_from_stock (EV_MESSAGE_AREA (widget), stock_id);
+
+ return widget;
+}
+
+void
+ev_progress_message_area_set_status (EvProgressMessageArea *area,
+ const gchar *str)
+{
+ g_return_if_fail (EV_IS_PROGRESS_MESSAGE_AREA (area));
+
+ gtk_label_set_text (GTK_LABEL (area->priv->label), str);
+
+ g_object_notify (G_OBJECT (area), "status");
+}
+
+void
+ev_progress_message_area_set_fraction (EvProgressMessageArea *area,
+ gdouble fraction)
+{
+ g_return_if_fail (EV_IS_PROGRESS_MESSAGE_AREA (area));
+
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (area->priv->progress_bar),
+ fraction);
+ g_object_notify (G_OBJECT (area), "fraction");
+}
diff --git a/shell/ev-progress-message-area.h b/shell/ev-progress-message-area.h
new file mode 100644
index 00000000..ff96d0f7
--- /dev/null
+++ b/shell/ev-progress-message-area.h
@@ -0,0 +1,64 @@
+/* ev-progress-message-area.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ *
+ * 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_PROGRESS_MESSAGE_AREA_H
+#define EV_PROGRESS_MESSAGE_AREA_H
+
+#include <gtk/gtk.h>
+
+#include "ev-message-area.h"
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_PROGRESS_MESSAGE_AREA (ev_progress_message_area_get_type ())
+#define EV_PROGRESS_MESSAGE_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EV_TYPE_PROGRESS_MESSAGE_AREA, EvProgressMessageArea))
+#define EV_PROGRESS_MESSAGE_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EV_TYPE_PROGRESS_MESSAGE_AREA, EvProgressMessageAreaClass))
+#define EV_IS_PROGRESS_MESSAGE_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EV_TYPE_PROGRESS_MESSAGE_AREA))
+#define EV_IS_PROGRESS_MESSAGE_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EV_TYPE_PROGRESS_MESSAGE_AREA))
+#define EV_PROGRESS_MESSAGE_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EV_TYPE_PROGRESS_MESSAGE_AREA, EvProgressMessageAreaClass))
+
+typedef struct _EvProgressMessageArea EvProgressMessageArea;
+typedef struct _EvProgressMessageAreaClass EvProgressMessageAreaClass;
+typedef struct _EvProgressMessageAreaPrivate EvProgressMessageAreaPrivate;
+
+struct _EvProgressMessageArea {
+ EvMessageArea parent_instance;
+
+ /*< private >*/
+ EvProgressMessageAreaPrivate *priv;
+};
+
+struct _EvProgressMessageAreaClass {
+ EvMessageAreaClass parent_class;
+};
+
+GType ev_progress_message_area_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_progress_message_area_new (const gchar *stock_id,
+ const gchar *text,
+ const gchar *first_button_text,
+ ...);
+void ev_progress_message_area_set_status (EvProgressMessageArea *area,
+ const gchar *str);
+void ev_progress_message_area_set_fraction (EvProgressMessageArea *area,
+ gdouble fraction);
+
+G_END_DECLS
+
+#endif /* EV_PROGRESS_MESSAGE_AREA_H */
diff --git a/shell/ev-properties-dialog.c b/shell/ev-properties-dialog.c
new file mode 100644
index 00000000..722c298a
--- /dev/null
+++ b/shell/ev-properties-dialog.c
@@ -0,0 +1,138 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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 <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ev-document-fonts.h"
+#include "ev-properties-dialog.h"
+#include "ev-properties-fonts.h"
+#include "ev-properties-view.h"
+#include "ev-properties-license.h"
+
+struct _EvPropertiesDialog {
+ GtkDialog base_instance;
+
+ EvDocument *document;
+ GtkWidget *notebook;
+ GtkWidget *general_page;
+ GtkWidget *fonts_page;
+ GtkWidget *license_page;
+};
+
+struct _EvPropertiesDialogClass {
+ GtkDialogClass base_class;
+};
+
+G_DEFINE_TYPE (EvPropertiesDialog, ev_properties_dialog, GTK_TYPE_DIALOG)
+
+static void
+ev_properties_dialog_class_init (EvPropertiesDialogClass *properties_class)
+{
+}
+
+static void
+ev_properties_dialog_init (EvPropertiesDialog *properties)
+{
+ GtkBox *content_area;
+
+ content_area = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (properties)));
+
+ gtk_window_set_title (GTK_WINDOW (properties), _("Properties"));
+ gtk_window_set_destroy_with_parent (GTK_WINDOW (properties), TRUE);
+ gtk_dialog_set_has_separator (GTK_DIALOG (properties), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (properties), 5);
+ gtk_box_set_spacing (content_area, 2);
+
+ gtk_dialog_add_button (GTK_DIALOG (properties), GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CANCEL);
+ gtk_dialog_set_default_response (GTK_DIALOG (properties),
+ GTK_RESPONSE_CANCEL);
+
+ properties->notebook = gtk_notebook_new ();
+ gtk_container_set_border_width (GTK_CONTAINER (properties->notebook), 5);
+ gtk_box_pack_start (content_area, properties->notebook, TRUE, TRUE, 0);
+ gtk_widget_show (properties->notebook);
+
+ g_signal_connect (properties, "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+}
+
+void
+ev_properties_dialog_set_document (EvPropertiesDialog *properties,
+ const gchar *uri,
+ EvDocument *document)
+{
+ GtkWidget *label;
+ const EvDocumentInfo *info;
+
+ properties->document = document;
+
+ info = ev_document_get_info (document);
+
+ if (properties->general_page == NULL) {
+ label = gtk_label_new (_("General"));
+ properties->general_page = ev_properties_view_new (uri);
+ gtk_notebook_append_page (GTK_NOTEBOOK (properties->notebook),
+ properties->general_page, label);
+ gtk_widget_show (properties->general_page);
+ }
+ ev_properties_view_set_info (EV_PROPERTIES_VIEW (properties->general_page), info);
+
+ if (EV_IS_DOCUMENT_FONTS (document)) {
+ if (properties->fonts_page == NULL) {
+ label = gtk_label_new (_("Fonts"));
+ properties->fonts_page = ev_properties_fonts_new ();
+ gtk_notebook_append_page (GTK_NOTEBOOK (properties->notebook),
+ properties->fonts_page, label);
+ gtk_widget_show (properties->fonts_page);
+ }
+
+ ev_properties_fonts_set_document
+ (EV_PROPERTIES_FONTS (properties->fonts_page), document);
+ }
+
+ if (info->fields_mask & EV_DOCUMENT_INFO_LICENSE && info->license) {
+ if (properties->license_page == NULL) {
+ label = gtk_label_new (_("Document License"));
+ properties->license_page = ev_properties_license_new ();
+ gtk_notebook_append_page (GTK_NOTEBOOK (properties->notebook),
+ properties->license_page, label);
+ gtk_widget_show (properties->license_page);
+ }
+
+ ev_properties_license_set_license
+ (EV_PROPERTIES_LICENSE (properties->license_page), info->license);
+ }
+}
+
+GtkWidget *
+ev_properties_dialog_new ()
+{
+ EvPropertiesDialog *properties;
+
+ properties = g_object_new (EV_TYPE_PROPERTIES_DIALOG, NULL);
+
+ return GTK_WIDGET (properties);
+}
diff --git a/shell/ev-properties-dialog.h b/shell/ev-properties-dialog.h
new file mode 100644
index 00000000..9484627c
--- /dev/null
+++ b/shell/ev-properties-dialog.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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_PROPERTIES_DIALOG_H__
+#define __EV_PROPERTIES_DIALOG_H__
+
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+typedef struct _EvPropertiesDialog EvPropertiesDialog;
+typedef struct _EvPropertiesDialogClass EvPropertiesDialogClass;
+typedef struct _EvPropertiesDialogPrivate EvPropertiesDialogPrivate;
+
+#define EV_TYPE_PROPERTIES_DIALOG (ev_properties_dialog_get_type())
+#define EV_PROPERTIES_DIALOG(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_PROPERTIES_DIALOG, EvPropertiesDialog))
+#define EV_PROPERTIES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_PROPERTIES_DIALOG, EvPropertiesDialogClass))
+#define EV_IS_PROPERTIES_DIALOG(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_PROPERTIES_DIALOG))
+#define EV_IS_PROPERTIES_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_PROPERTIES_DIALOG))
+#define EV_PROPERTIES_DIALOG_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_PROPERTIES_DIALOG, EvPropertiesDialogClass))
+
+GType ev_properties_dialog_get_type (void);
+GtkWidget *ev_properties_dialog_new (void);
+void ev_properties_dialog_set_document (EvPropertiesDialog *properties,
+ const gchar *uri,
+ EvDocument *document);
+
+G_END_DECLS
+
+#endif /* __EV_PROPERTIES_DIALOG_H__ */
diff --git a/shell/ev-properties-fonts.c b/shell/ev-properties-fonts.c
new file mode 100644
index 00000000..8480ebd2
--- /dev/null
+++ b/shell/ev-properties-fonts.c
@@ -0,0 +1,224 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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 <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ev-document-fonts.h"
+#include "ev-job-scheduler.h"
+#include "ev-jobs.h"
+#include "ev-properties-fonts.h"
+
+struct _EvPropertiesFonts {
+ GtkVBox base_instance;
+
+ GtkWidget *fonts_treeview;
+ GtkWidget *fonts_progress_label;
+ EvJob *fonts_job;
+
+ EvDocument *document;
+};
+
+struct _EvPropertiesFontsClass {
+ GtkVBoxClass base_class;
+};
+
+static void
+job_fonts_finished_cb (EvJob *job, EvPropertiesFonts *properties);
+
+G_DEFINE_TYPE (EvPropertiesFonts, ev_properties_fonts, GTK_TYPE_VBOX)
+
+static void
+ev_properties_fonts_dispose (GObject *object)
+{
+ EvPropertiesFonts *properties = EV_PROPERTIES_FONTS (object);
+
+ if (properties->fonts_job) {
+ g_signal_handlers_disconnect_by_func (properties->fonts_job,
+ job_fonts_finished_cb,
+ properties);
+ ev_job_cancel (properties->fonts_job);
+
+ g_object_unref (properties->fonts_job);
+ properties->fonts_job = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_properties_fonts_parent_class)->dispose (object);
+}
+
+static void
+ev_properties_fonts_class_init (EvPropertiesFontsClass *properties_class)
+{
+ GObjectClass *g_object_class = G_OBJECT_CLASS (properties_class);
+
+ g_object_class->dispose = ev_properties_fonts_dispose;
+}
+
+static void
+font_cell_data_func (GtkTreeViewColumn *col, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
+{
+ char *name;
+ char *details;
+ char *markup;
+
+ gtk_tree_model_get (model, iter,
+ EV_DOCUMENT_FONTS_COLUMN_NAME, &name,
+ EV_DOCUMENT_FONTS_COLUMN_DETAILS, &details,
+ -1);
+
+ if (details) {
+ markup = g_strdup_printf ("<b><big>%s</big></b>\n<small>%s</small>",
+ name, details);
+ } else {
+ markup = g_strdup_printf ("<b><big>%s</big></b>", name);
+ }
+
+ g_object_set (renderer, "markup", markup, NULL);
+
+ g_free (markup);
+ g_free (details);
+ g_free (name);
+}
+
+static void
+ev_properties_fonts_init (EvPropertiesFonts *properties)
+{
+ GtkWidget *swindow;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ gtk_container_set_border_width (GTK_CONTAINER (properties), 12);
+ gtk_box_set_spacing (GTK_BOX (properties), 6);
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
+ GTK_SHADOW_IN);
+
+ properties->fonts_treeview = gtk_tree_view_new ();
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (properties->fonts_treeview),
+ FALSE);
+ 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 (properties->fonts_treeview),
+ column);
+
+ renderer = GTK_CELL_RENDERER (g_object_new (GTK_TYPE_CELL_RENDERER_TEXT,
+ "ypad", 6, NULL));
+ gtk_tree_view_column_pack_start (GTK_TREE_VIEW_COLUMN (column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_title (GTK_TREE_VIEW_COLUMN (column),
+ _("Font"));
+ gtk_tree_view_column_set_cell_data_func (column, renderer,
+ font_cell_data_func,
+ NULL, NULL);
+
+ gtk_container_add (GTK_CONTAINER (swindow), properties->fonts_treeview);
+ gtk_widget_show (properties->fonts_treeview);
+
+ gtk_box_pack_start (GTK_BOX (properties), swindow,
+ TRUE, TRUE, 0);
+ gtk_widget_show (swindow);
+
+ properties->fonts_progress_label = gtk_label_new (NULL);
+ g_object_set (G_OBJECT (properties->fonts_progress_label),
+ "xalign", 0.0,
+ NULL);
+ gtk_box_pack_start (GTK_BOX (properties),
+ properties->fonts_progress_label,
+ FALSE, FALSE, 0);
+ gtk_widget_show (properties->fonts_progress_label);
+}
+
+static void
+update_progress_label (GtkWidget *label, double progress)
+{
+ if (progress > 0) {
+ char *progress_text;
+ progress_text = g_strdup_printf (_("Gathering font informationā€¦ %3d%%"),
+ (int) (progress * 100));
+ gtk_label_set_text (GTK_LABEL (label), progress_text);
+ g_free (progress_text);
+ gtk_widget_show (label);
+ } else {
+ gtk_widget_hide (label);
+ }
+}
+
+static void
+job_fonts_finished_cb (EvJob *job, EvPropertiesFonts *properties)
+{
+ g_signal_handlers_disconnect_by_func (job, job_fonts_finished_cb, properties);
+ g_object_unref (properties->fonts_job);
+ properties->fonts_job = NULL;
+}
+
+static void
+job_fonts_updated_cb (EvJobFonts *job, gdouble progress, EvPropertiesFonts *properties)
+{
+ GtkTreeModel *model;
+ EvDocumentFonts *document_fonts = EV_DOCUMENT_FONTS (properties->document);
+
+ update_progress_label (properties->fonts_progress_label, progress);
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (properties->fonts_treeview));
+ /* Documen lock is already held by the jop */
+ ev_document_fonts_fill_model (document_fonts, model);
+}
+
+void
+ev_properties_fonts_set_document (EvPropertiesFonts *properties,
+ EvDocument *document)
+{
+ GtkTreeView *tree_view = GTK_TREE_VIEW (properties->fonts_treeview);
+ GtkListStore *list_store;
+
+ properties->document = document;
+
+ list_store = gtk_list_store_new (EV_DOCUMENT_FONTS_COLUMN_NUM_COLUMNS,
+ G_TYPE_STRING, G_TYPE_STRING);
+ gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (list_store));
+
+ properties->fonts_job = ev_job_fonts_new (properties->document);
+ g_signal_connect (properties->fonts_job, "updated",
+ G_CALLBACK (job_fonts_updated_cb),
+ properties);
+ g_signal_connect (properties->fonts_job, "finished",
+ G_CALLBACK (job_fonts_finished_cb),
+ properties);
+ ev_job_scheduler_push_job (properties->fonts_job, EV_JOB_PRIORITY_NONE);
+}
+
+GtkWidget *
+ev_properties_fonts_new ()
+{
+ EvPropertiesFonts *properties;
+
+ properties = g_object_new (EV_TYPE_PROPERTIES_FONTS, NULL);
+
+ return GTK_WIDGET (properties);
+}
diff --git a/shell/ev-properties-fonts.h b/shell/ev-properties-fonts.h
new file mode 100644
index 00000000..9f6ec0cd
--- /dev/null
+++ b/shell/ev-properties-fonts.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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_PROPERTIES_FONTS_H__
+#define __EV_PROPERTIES_FONTS_H__
+
+#include "ev-document.h"
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EvPropertiesFonts EvPropertiesFonts;
+typedef struct _EvPropertiesFontsClass EvPropertiesFontsClass;
+typedef struct _EvPropertiesFontsPrivate EvPropertiesFontsPrivate;
+
+#define EV_TYPE_PROPERTIES_FONTS (ev_properties_fonts_get_type())
+#define EV_PROPERTIES_FONTS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_PROPERTIES_FONTS, EvPropertiesFonts))
+#define EV_PROPERTIES_FONTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_PROPERTIES_FONTS, EvPropertiesFontsClass))
+#define EV_IS_PROPERTIES(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_PROPERTIES_FONTS))
+#define EV_IS_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_PROPERTIES_FONTS))
+#define EV_PROPERTIES_FONTS_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_PROPERTIES_FONTS, EvPropertiesFontsClass))
+
+GType ev_properties_fonts_get_type (void);
+GtkWidget *ev_properties_fonts_new (void);
+void ev_properties_fonts_set_document (EvPropertiesFonts *properties,
+ EvDocument *document);
+
+G_END_DECLS
+
+#endif /* __EV_PROPERTIES_FONTS_H__ */
diff --git a/shell/ev-properties-license.c b/shell/ev-properties-license.c
new file mode 100644
index 00000000..1216df45
--- /dev/null
+++ b/shell/ev-properties-license.c
@@ -0,0 +1,169 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2009 Juanjo MarĆ­n <[email protected]>
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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 <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ev-properties-license.h"
+
+struct _EvPropertiesLicense {
+ GtkVBox base_instance;
+};
+
+struct _EvPropertiesLicenseClass {
+ GtkVBoxClass base_class;
+};
+
+G_DEFINE_TYPE (EvPropertiesLicense, ev_properties_license, GTK_TYPE_VBOX)
+
+static void
+ev_properties_license_class_init (EvPropertiesLicenseClass *properties_license_class)
+{
+}
+
+static GtkWidget *
+get_license_text_widget (EvDocumentLicense *license)
+{
+ GtkWidget *swindow, *textview;
+ GtkTextBuffer *buffer;
+
+ textview = gtk_text_view_new ();
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (textview), GTK_WRAP_WORD);
+ gtk_text_view_set_left_margin (GTK_TEXT_VIEW (textview), 8);
+ gtk_text_view_set_right_margin (GTK_TEXT_VIEW (textview), 8);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview));
+ gtk_text_buffer_set_text (buffer, ev_document_license_get_text (license), -1);
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
+ GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (swindow), textview);
+ gtk_widget_show (textview);
+
+ return swindow;
+}
+
+static GtkWidget *
+get_license_uri_widget (const gchar *uri)
+{
+ GtkWidget *label;
+ gchar *checked_uri;
+ gchar *markup;
+
+ label = gtk_label_new (NULL);
+ g_object_set (G_OBJECT (label),
+ "xalign", 0.0,
+ "width_chars", 25,
+ "selectable", TRUE,
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
+
+ checked_uri = g_uri_parse_scheme (uri);
+ if (checked_uri) {
+ markup = g_markup_printf_escaped ("<a href=\"%s\">%s</a>", uri, uri);
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+ g_free (checked_uri);
+ } else {
+ gtk_label_set_text (GTK_LABEL (label), uri);
+ }
+
+ return label;
+}
+
+static void
+ev_properties_license_add_section (EvPropertiesLicense *properties,
+ const gchar *title_text,
+ GtkWidget *contents)
+{
+ GtkWidget *title;
+ GtkWidget *alignment;
+ gchar *markup;
+
+ title = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (title), 0.0, 0.5);
+ gtk_label_set_use_markup (GTK_LABEL (title), TRUE);
+ markup = g_strdup_printf ("<b>%s</b>", title_text);
+ gtk_label_set_markup (GTK_LABEL (title), markup);
+ g_free (markup);
+ gtk_box_pack_start (GTK_BOX (properties), title, FALSE, FALSE, 0);
+ gtk_widget_show (title);
+
+ alignment = gtk_alignment_new (0.5, 0.5, 1., 1.);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 12, 0);
+ gtk_container_add (GTK_CONTAINER (alignment), contents);
+ gtk_widget_show (contents);
+
+ gtk_box_pack_start (GTK_BOX (properties), alignment, FALSE, TRUE, 0);
+ gtk_widget_show (alignment);
+}
+
+void
+ev_properties_license_set_license (EvPropertiesLicense *properties,
+ EvDocumentLicense *license)
+{
+ const gchar *text = ev_document_license_get_text (license);
+ const gchar *uri = ev_document_license_get_uri (license);
+ const gchar *web_statement = ev_document_license_get_web_statement (license);
+
+ if (text) {
+ ev_properties_license_add_section (properties,
+ _("Usage terms"),
+ get_license_text_widget (license));
+ }
+
+ if (uri) {
+ ev_properties_license_add_section (properties,
+ _("Text License"),
+ get_license_uri_widget (uri));
+ }
+
+ if (web_statement) {
+ ev_properties_license_add_section (properties,
+ _("Further Information"),
+ get_license_uri_widget (web_statement));
+ }
+}
+
+static void
+ev_properties_license_init (EvPropertiesLicense *properties)
+{
+ gtk_box_set_spacing (GTK_BOX (properties), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (properties), 12);
+}
+
+GtkWidget *
+ev_properties_license_new (void)
+{
+ EvPropertiesLicense *properties_license;
+
+ properties_license = g_object_new (EV_TYPE_PROPERTIES_LICENSE, NULL);
+
+ return GTK_WIDGET (properties_license);
+}
diff --git a/shell/ev-properties-license.h b/shell/ev-properties-license.h
new file mode 100644
index 00000000..7b38fa55
--- /dev/null
+++ b/shell/ev-properties-license.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2009 Juanjo MarĆ­n <[email protected]>
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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_PROPERTIES_LICENSE_VIEW_H__
+#define __EV_PROPERTIES_LICENSE_VIEW_H__
+
+#include <gtk/gtk.h>
+
+#include <evince-document.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EvPropertiesLicense EvPropertiesLicense;
+typedef struct _EvPropertiesLicenseClass EvPropertiesLicenseClass;
+typedef struct _EvPropertiesLicensePrivate EvPropertiesLicensePrivate;
+
+#define EV_TYPE_PROPERTIES_LICENSE (ev_properties_license_get_type())
+#define EV_PROPERTIES_LICENSE(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_PROPERTIES_LICENSE, EvPropertiesLicense))
+#define EV_PROPERTIES_LICENSE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_PROPERTIES_LICENSE, EvPropertiesLicenseClass))
+#define EV_IS_PROPERTIES_LICENSE(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_PROPERTIES_LICENSE))
+#define EV_IS_PROPERTIES_LICENSE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_PROPERTIES_LICENSE))
+#define EV_PROPERTIES_LICENSE_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_PROPERTIES_LICENSE, EvPropertiesLicenseClass))
+
+GType ev_properties_license_get_type (void) G_GNUC_CONST;
+
+GtkWidget *ev_properties_license_new (void);
+void ev_properties_license_set_license (EvPropertiesLicense *properties_license,
+ EvDocumentLicense *license);
+
+G_END_DECLS
+
+#endif /* __EV_PROPERTIES_LICENSE_VIEW_H__ */
diff --git a/shell/ev-sidebar-annotations.c b/shell/ev-sidebar-annotations.c
new file mode 100644
index 00000000..5b8b8309
--- /dev/null
+++ b/shell/ev-sidebar-annotations.c
@@ -0,0 +1,542 @@
+/* ev-sidebar-annotations.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <[email protected]>
+ *
+ * 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 <glib/gi18n.h>
+
+#include "ev-document-annotations.h"
+#include "ev-sidebar-page.h"
+#include "ev-sidebar-annotations.h"
+#include "ev-jobs.h"
+#include "ev-job-scheduler.h"
+#include "ev-stock-icons.h"
+
+enum {
+ PROP_0,
+ PROP_WIDGET
+};
+
+enum {
+ COLUMN_MARKUP,
+ COLUMN_ICON,
+ COLUMN_ANNOT_MAPPING,
+ N_COLUMNS
+};
+
+enum {
+ ANNOT_ACTIVATED,
+ BEGIN_ANNOT_ADD,
+ ANNOT_ADD_CANCELLED,
+ N_SIGNALS
+};
+
+struct _EvSidebarAnnotationsPrivate {
+ EvDocument *document;
+
+ GtkWidget *notebook;
+ GtkWidget *tree_view;
+ GtkWidget *palette;
+ GtkToolItem *annot_text_item;
+
+ EvJob *job;
+ guint selection_changed_id;
+};
+
+static void ev_sidebar_annotations_page_iface_init (EvSidebarPageInterface *iface);
+static void ev_sidebar_annotations_load (EvSidebarAnnotations *sidebar_annots);
+
+static guint signals[N_SIGNALS];
+
+G_DEFINE_TYPE_EXTENDED (EvSidebarAnnotations,
+ ev_sidebar_annotations,
+ GTK_TYPE_VBOX,
+ 0,
+ G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
+ ev_sidebar_annotations_page_iface_init))
+
+#define EV_SIDEBAR_ANNOTATIONS_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_ANNOTATIONS, EvSidebarAnnotationsPrivate))
+
+static void
+ev_sidebar_annotations_dispose (GObject *object)
+{
+ EvSidebarAnnotations *sidebar_annots = EV_SIDEBAR_ANNOTATIONS (object);
+ EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv;
+
+ if (priv->document) {
+ g_object_unref (priv->document);
+ priv->document = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_sidebar_annotations_parent_class)->dispose (object);
+}
+
+static GtkTreeModel *
+ev_sidebar_annotations_create_simple_model (const gchar *message)
+{
+ GtkTreeModel *retval;
+ GtkTreeIter iter;
+ gchar *markup;
+
+ /* Creates a fake model to indicate that we're loading */
+ retval = (GtkTreeModel *)gtk_list_store_new (N_COLUMNS,
+ G_TYPE_STRING,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_POINTER);
+
+ gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
+ markup = g_strdup_printf ("<span size=\"larger\" style=\"italic\">%s</span>",
+ message);
+ gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
+ COLUMN_MARKUP, markup,
+ -1);
+ g_free (markup);
+
+ return retval;
+}
+
+static void
+ev_sidebar_annotations_add_annots_list (EvSidebarAnnotations *ev_annots)
+{
+ GtkWidget *swindow;
+ GtkTreeModel *loading_model;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+ GtkWidget *label;
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
+ GTK_SHADOW_IN);
+
+ /* Create tree view */
+ loading_model = ev_sidebar_annotations_create_simple_model (_("Loadingā€¦"));
+ ev_annots->priv->tree_view = gtk_tree_view_new_with_model (loading_model);
+ g_object_unref (loading_model);
+
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (ev_annots->priv->tree_view),
+ FALSE);
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (ev_annots->priv->tree_view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
+
+ column = gtk_tree_view_column_new ();
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "pixbuf", COLUMN_ICON,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "markup", COLUMN_MARKUP,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (ev_annots->priv->tree_view),
+ column);
+
+ gtk_container_add (GTK_CONTAINER (swindow), ev_annots->priv->tree_view);
+ gtk_widget_show (ev_annots->priv->tree_view);
+
+ label = gtk_label_new (_("List"));
+ gtk_notebook_append_page (GTK_NOTEBOOK (ev_annots->priv->notebook),
+ swindow, label);
+ gtk_widget_show (label);
+
+ gtk_widget_show (swindow);
+}
+
+static void
+ev_sidebar_annotations_text_annot_button_toggled (GtkToggleToolButton *toolbutton,
+ EvSidebarAnnotations *sidebar_annots)
+{
+ EvAnnotationType annot_type;
+
+ if (!gtk_toggle_tool_button_get_active (toolbutton)) {
+ g_signal_emit (sidebar_annots, signals[ANNOT_ADD_CANCELLED], 0, NULL);
+ return;
+ }
+
+ if (GTK_TOOL_ITEM (toolbutton) == sidebar_annots->priv->annot_text_item)
+ annot_type = EV_ANNOTATION_TYPE_TEXT;
+ else
+ annot_type = EV_ANNOTATION_TYPE_UNKNOWN;
+
+ g_signal_emit (sidebar_annots, signals[BEGIN_ANNOT_ADD], 0, annot_type);
+}
+
+static void
+ev_sidebar_annotations_add_annots_palette (EvSidebarAnnotations *ev_annots)
+{
+ GtkWidget *swindow;
+ GtkWidget *group;
+ GtkToolItem *item;
+ GtkWidget *label;
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
+ GTK_SHADOW_IN);
+
+ ev_annots->priv->palette = gtk_tool_palette_new ();
+ group = gtk_tool_item_group_new (_("Annotations"));
+ gtk_container_add (GTK_CONTAINER (ev_annots->priv->palette), group);
+
+ /* FIXME: use a better icon than EDIT */
+ item = gtk_toggle_tool_button_new ();
+ gtk_tool_button_set_icon_name (GTK_TOOL_BUTTON (item), GTK_STOCK_EDIT);
+ gtk_tool_button_set_label (GTK_TOOL_BUTTON (item), _("Text"));
+ gtk_widget_set_tooltip_text (GTK_WIDGET (item), _("Add text annotation"));
+ ev_annots->priv->annot_text_item = item;
+ g_signal_connect (item, "toggled",
+ G_CALLBACK (ev_sidebar_annotations_text_annot_button_toggled),
+ ev_annots);
+ gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ gtk_container_add (GTK_CONTAINER (swindow), ev_annots->priv->palette);
+ gtk_widget_show (ev_annots->priv->palette);
+
+ label = gtk_label_new (_("Add"));
+ gtk_notebook_append_page (GTK_NOTEBOOK (ev_annots->priv->notebook),
+ swindow, label);
+ gtk_widget_show (label);
+
+ gtk_widget_show (swindow);
+}
+
+static void
+ev_sidebar_annotations_init (EvSidebarAnnotations *ev_annots)
+{
+ ev_annots->priv = EV_SIDEBAR_ANNOTATIONS_GET_PRIVATE (ev_annots);
+
+ ev_annots->priv->notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (ev_annots->priv->notebook), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (ev_annots->priv->notebook), FALSE);
+ ev_sidebar_annotations_add_annots_list (ev_annots);
+ ev_sidebar_annotations_add_annots_palette (ev_annots);
+ gtk_container_add (GTK_CONTAINER (ev_annots), ev_annots->priv->notebook);
+ gtk_widget_show (ev_annots->priv->notebook);
+}
+
+static void
+ev_sidebar_annotations_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EvSidebarAnnotations *ev_sidebar_annots;
+
+ ev_sidebar_annots = EV_SIDEBAR_ANNOTATIONS (object);
+
+ switch (prop_id) {
+ case PROP_WIDGET:
+ g_value_set_object (value, ev_sidebar_annots->priv->notebook);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ev_sidebar_annotations_class_init (EvSidebarAnnotationsClass *klass)
+{
+ GObjectClass *g_object_class = G_OBJECT_CLASS (klass);
+
+ g_object_class->get_property = ev_sidebar_annotations_get_property;
+ g_object_class->dispose = ev_sidebar_annotations_dispose;
+
+ g_type_class_add_private (g_object_class, sizeof (EvSidebarAnnotationsPrivate));
+
+ g_object_class_override_property (g_object_class, PROP_WIDGET, "main-widget");
+
+ signals[ANNOT_ACTIVATED] =
+ g_signal_new ("annot-activated",
+ G_TYPE_FROM_CLASS (g_object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvSidebarAnnotationsClass, annot_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+ signals[BEGIN_ANNOT_ADD] =
+ g_signal_new ("begin-annot-add",
+ G_TYPE_FROM_CLASS (g_object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvSidebarAnnotationsClass, begin_annot_add),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__ENUM,
+ G_TYPE_NONE, 1,
+ EV_TYPE_ANNOTATION_TYPE);
+ signals[ANNOT_ADD_CANCELLED] =
+ g_signal_new ("annot-add-cancelled",
+ G_TYPE_FROM_CLASS (g_object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvSidebarAnnotationsClass, annot_add_cancelled),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0,
+ G_TYPE_NONE);
+}
+
+GtkWidget *
+ev_sidebar_annotations_new (void)
+{
+ return GTK_WIDGET (g_object_new (EV_TYPE_SIDEBAR_ANNOTATIONS, NULL));
+}
+
+void
+ev_sidebar_annotations_annot_added (EvSidebarAnnotations *sidebar_annots,
+ EvAnnotation *annot)
+{
+ GtkToggleToolButton *toolbutton;
+
+ if (EV_IS_ANNOTATION_TEXT (annot)) {
+ toolbutton = GTK_TOGGLE_TOOL_BUTTON (sidebar_annots->priv->annot_text_item);
+ g_signal_handlers_block_by_func (toolbutton,
+ ev_sidebar_annotations_text_annot_button_toggled,
+ sidebar_annots);
+ gtk_toggle_tool_button_set_active (toolbutton, FALSE);
+ g_signal_handlers_unblock_by_func (toolbutton,
+ ev_sidebar_annotations_text_annot_button_toggled,
+ sidebar_annots);
+ }
+
+ ev_sidebar_annotations_load (sidebar_annots);
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *selection,
+ EvSidebarAnnotations *sidebar_annots)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ EvMapping *mapping = NULL;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_ANNOT_MAPPING, &mapping,
+ -1);
+ if (mapping)
+ g_signal_emit (sidebar_annots, signals[ANNOT_ACTIVATED], 0, mapping);
+ }
+}
+
+static void
+job_finished_callback (EvJobAnnots *job,
+ EvSidebarAnnotations *sidebar_annots)
+{
+ EvSidebarAnnotationsPrivate *priv;
+ GtkTreeStore *model;
+ GtkTreeSelection *selection;
+ GList *l;
+ GdkPixbuf *text_icon = NULL;
+ GdkPixbuf *attachment_icon = NULL;
+
+ priv = sidebar_annots->priv;
+
+ if (!job->annots) {
+ GtkTreeModel *list;
+
+ list = ev_sidebar_annotations_create_simple_model (_("Document contains no annotations"));
+ gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), list);
+ g_object_unref (list);
+
+ g_object_unref (job);
+ priv->job = NULL;
+
+ return;
+ }
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+ if (priv->selection_changed_id == 0) {
+ priv->selection_changed_id =
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (selection_changed_cb),
+ sidebar_annots);
+ }
+
+ model = gtk_tree_store_new (N_COLUMNS,
+ G_TYPE_STRING,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_POINTER);
+
+ for (l = job->annots; l; l = g_list_next (l)) {
+ EvMappingList *mapping_list;
+ GList *ll;
+ gchar *page_label;
+ GtkTreeIter iter;
+ gboolean found = FALSE;
+
+ mapping_list = (EvMappingList *)l->data;
+ page_label = g_strdup_printf (_("Page %d"),
+ ev_mapping_list_get_page (mapping_list) + 1);
+ gtk_tree_store_append (model, &iter, NULL);
+ gtk_tree_store_set (model, &iter,
+ COLUMN_MARKUP, page_label,
+ -1);
+ g_free (page_label);
+
+ for (ll = ev_mapping_list_get_list (mapping_list); ll; ll = g_list_next (ll)) {
+ EvAnnotation *annot;
+ const gchar *label;
+ const gchar *modified;
+ gchar *markup;
+ GtkTreeIter child_iter;
+ GdkPixbuf *pixbuf = NULL;
+
+ annot = ((EvMapping *)(ll->data))->data;
+ if (!EV_IS_ANNOTATION_MARKUP (annot))
+ continue;
+
+ label = ev_annotation_markup_get_label (EV_ANNOTATION_MARKUP (annot));
+ modified = ev_annotation_get_modified (annot);
+ if (modified) {
+ markup = g_strdup_printf ("<span weight=\"bold\">%s</span>\n%s",
+ label, modified);
+ } else {
+ markup = g_strdup_printf ("<span weight=\"bold\">%s</span>", label);
+ }
+
+ if (EV_IS_ANNOTATION_TEXT (annot)) {
+ if (!text_icon) {
+ /* FIXME: use a better icon than EDIT */
+ text_icon = gtk_widget_render_icon (priv->tree_view,
+ GTK_STOCK_EDIT,
+ GTK_ICON_SIZE_BUTTON,
+ NULL);
+ }
+ pixbuf = text_icon;
+ } else if (EV_IS_ANNOTATION_ATTACHMENT (annot)) {
+ if (!attachment_icon) {
+ attachment_icon = gtk_widget_render_icon (priv->tree_view,
+ EV_STOCK_ATTACHMENT,
+ GTK_ICON_SIZE_BUTTON,
+ NULL);
+ }
+ pixbuf = attachment_icon;
+ }
+
+ gtk_tree_store_append (model, &child_iter, &iter);
+ gtk_tree_store_set (model, &child_iter,
+ COLUMN_MARKUP, markup,
+ COLUMN_ICON, pixbuf,
+ COLUMN_ANNOT_MAPPING, ll->data,
+ -1);
+ g_free (markup);
+ found = TRUE;
+ }
+
+ if (!found)
+ gtk_tree_store_remove (model, &iter);
+ }
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view),
+ GTK_TREE_MODEL (model));
+ g_object_unref (model);
+
+ if (text_icon)
+ g_object_unref (text_icon);
+ if (attachment_icon)
+ g_object_unref (attachment_icon);
+
+ g_object_unref (job);
+ priv->job = NULL;
+}
+
+static void
+ev_sidebar_annotations_load (EvSidebarAnnotations *sidebar_annots)
+{
+ EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv;
+
+ if (priv->job) {
+ g_signal_handlers_disconnect_by_func (priv->job,
+ job_finished_callback,
+ sidebar_annots);
+ g_object_unref (priv->job);
+ }
+
+ priv->job = ev_job_annots_new (priv->document);
+ g_signal_connect (priv->job, "finished",
+ G_CALLBACK (job_finished_callback),
+ sidebar_annots);
+ /* The priority doesn't matter for this job */
+ ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
+}
+
+static void
+ev_sidebar_annotations_document_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvSidebarAnnotations *sidebar_annots)
+{
+ EvDocument *document = ev_document_model_get_document (model);
+ EvSidebarAnnotationsPrivate *priv = sidebar_annots->priv;
+ gboolean show_tabs;
+
+ if (!EV_IS_DOCUMENT_ANNOTATIONS (document))
+ return;
+
+ if (priv->document)
+ g_object_unref (priv->document);
+ priv->document = g_object_ref (document);
+
+ show_tabs = ev_document_annotations_can_add_annotation (EV_DOCUMENT_ANNOTATIONS (document));
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), show_tabs);
+
+ ev_sidebar_annotations_load (sidebar_annots);
+}
+
+/* EvSidebarPageIface */
+static void
+ev_sidebar_annotations_set_model (EvSidebarPage *sidebar_page,
+ EvDocumentModel *model)
+{
+ g_signal_connect (model, "notify::document",
+ G_CALLBACK (ev_sidebar_annotations_document_changed_cb),
+ sidebar_page);
+}
+
+static gboolean
+ev_sidebar_annotations_support_document (EvSidebarPage *sidebar_page,
+ EvDocument *document)
+{
+ return (EV_IS_DOCUMENT_ANNOTATIONS (document));
+}
+
+static const gchar *
+ev_sidebar_annotations_get_label (EvSidebarPage *sidebar_page)
+{
+ return _("Annotations");
+}
+
+static void
+ev_sidebar_annotations_page_iface_init (EvSidebarPageInterface *iface)
+{
+ iface->support_document = ev_sidebar_annotations_support_document;
+ iface->set_model = ev_sidebar_annotations_set_model;
+ iface->get_label = ev_sidebar_annotations_get_label;
+}
diff --git a/shell/ev-sidebar-annotations.h b/shell/ev-sidebar-annotations.h
new file mode 100644
index 00000000..6160868d
--- /dev/null
+++ b/shell/ev-sidebar-annotations.h
@@ -0,0 +1,62 @@
+/* ev-sidebar-annotations.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2010 Carlos Garcia Campos <[email protected]>
+ *
+ * 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_SIDEBAR_ANNOTATIONS_H__
+#define __EV_SIDEBAR_ANNOTATIONS_H__
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EvSidebarAnnotations EvSidebarAnnotations;
+typedef struct _EvSidebarAnnotationsClass EvSidebarAnnotationsClass;
+typedef struct _EvSidebarAnnotationsPrivate EvSidebarAnnotationsPrivate;
+
+#define EV_TYPE_SIDEBAR_ANNOTATIONS (ev_sidebar_annotations_get_type())
+#define EV_SIDEBAR_ANNOTATIONS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_SIDEBAR_ANNOTATIONS, EvSidebarAnnotations))
+#define EV_SIDEBAR_ANNOTATIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_SIDEBAR_ANNOTATIONS, EvSidebarAnnotationsClass))
+#define EV_IS_SIDEBAR_ANNOTATIONS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_SIDEBAR_ANNOTATIONS))
+#define EV_IS_SIDEBAR_ANNOTATIONS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_SIDEBAR_ANNOTATIONS))
+#define EV_SIDEBAR_ANNOTATIONS_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_SIDEBAR_ANNOTATIONS, EvSidebarAnnotationsClass))
+
+struct _EvSidebarAnnotations {
+ GtkVBox base_instance;
+
+ EvSidebarAnnotationsPrivate *priv;
+};
+
+struct _EvSidebarAnnotationsClass {
+ GtkVBoxClass base_class;
+
+ void (* annot_activated) (EvSidebarAnnotations *sidebar_annots,
+ EvMapping *mapping);
+ void (* begin_annot_add) (EvSidebarAnnotations *sidebar_annots,
+ EvAnnotationType annot_type);
+ void (* annot_add_cancelled) (EvSidebarAnnotations *sidebar_annots);
+};
+
+GType ev_sidebar_annotations_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_sidebar_annotations_new (void);
+void ev_sidebar_annotations_annot_added (EvSidebarAnnotations *sidebar_annots,
+ EvAnnotation *annot);
+G_END_DECLS
+
+#endif /* __EV_SIDEBAR_ANNOTATIONS_H__ */
diff --git a/shell/ev-sidebar-attachments.c b/shell/ev-sidebar-attachments.c
new file mode 100644
index 00000000..273b784f
--- /dev/null
+++ b/shell/ev-sidebar-attachments.c
@@ -0,0 +1,707 @@
+/* ev-sidebar-attachments.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2006 Carlos Garcia Campos
+ *
+ * Author:
+ * Carlos Garcia Campos <[email protected]>
+ *
+ * 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 <string.h>
+
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include "ev-document-attachments.h"
+#include "ev-jobs.h"
+#include "ev-job-scheduler.h"
+#include "ev-file-helpers.h"
+#include "ev-sidebar-attachments.h"
+#include "ev-sidebar-page.h"
+
+enum {
+ COLUMN_ICON,
+ COLUMN_NAME,
+ COLUMN_DESCRIPTION,
+ COLUMN_ATTACHMENT,
+ N_COLS
+};
+
+enum {
+ PROP_0,
+ PROP_WIDGET,
+};
+
+enum {
+ SIGNAL_POPUP_MENU,
+ N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+
+struct _EvSidebarAttachmentsPrivate {
+ GtkWidget *icon_view;
+ GtkListStore *model;
+
+ /* Icons */
+ GtkIconTheme *icon_theme;
+ GHashTable *icon_cache;
+};
+
+static void ev_sidebar_attachments_page_iface_init (EvSidebarPageInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (EvSidebarAttachments,
+ ev_sidebar_attachments,
+ GTK_TYPE_VBOX,
+ 0,
+ G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
+ ev_sidebar_attachments_page_iface_init))
+
+#define EV_SIDEBAR_ATTACHMENTS_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_ATTACHMENTS, EvSidebarAttachmentsPrivate))
+
+/* Icon cache */
+static void
+ev_sidebar_attachments_icon_cache_add (EvSidebarAttachments *ev_attachbar,
+ const gchar *mime_type,
+ const GdkPixbuf *pixbuf)
+{
+ g_assert (mime_type != NULL);
+ g_assert (GDK_IS_PIXBUF (pixbuf));
+
+ g_hash_table_insert (ev_attachbar->priv->icon_cache,
+ (gpointer)g_strdup (mime_type),
+ (gpointer)pixbuf);
+
+}
+
+static GdkPixbuf *
+icon_theme_get_pixbuf_from_mime_type (GtkIconTheme *icon_theme,
+ const gchar *mime_type)
+{
+ const char *separator;
+ GString *icon_name;
+ GdkPixbuf *pixbuf;
+
+ separator = strchr (mime_type, '/');
+ if (!separator)
+ return NULL; /* maybe we should return a GError with "invalid MIME-type" */
+
+ icon_name = g_string_new ("mate-mime-");
+ g_string_append_len (icon_name, mime_type, separator - mime_type);
+ g_string_append_c (icon_name, '-');
+ g_string_append (icon_name, separator + 1);
+ pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str, 48, 0, NULL);
+ g_string_free (icon_name, TRUE);
+ if (pixbuf)
+ return pixbuf;
+
+ icon_name = g_string_new ("mate-mime-");
+ g_string_append_len (icon_name, mime_type, separator - mime_type);
+ pixbuf = gtk_icon_theme_load_icon (icon_theme, icon_name->str, 48, 0, NULL);
+ g_string_free (icon_name, TRUE);
+
+ return pixbuf;
+}
+
+static GdkPixbuf *
+ev_sidebar_attachments_icon_cache_get (EvSidebarAttachments *ev_attachbar,
+ const gchar *mime_type)
+{
+ GdkPixbuf *pixbuf = NULL;
+
+ g_assert (mime_type != NULL);
+
+ pixbuf = g_hash_table_lookup (ev_attachbar->priv->icon_cache,
+ mime_type);
+
+ if (GDK_IS_PIXBUF (pixbuf))
+ return pixbuf;
+
+ pixbuf = icon_theme_get_pixbuf_from_mime_type (ev_attachbar->priv->icon_theme,
+ mime_type);
+
+ if (GDK_IS_PIXBUF (pixbuf))
+ ev_sidebar_attachments_icon_cache_add (ev_attachbar,
+ mime_type,
+ pixbuf);
+
+ return pixbuf;
+}
+
+static gboolean
+icon_cache_update_icon (gchar *key,
+ GdkPixbuf *value,
+ EvSidebarAttachments *ev_attachbar)
+{
+ GdkPixbuf *pixbuf = NULL;
+
+ pixbuf = icon_theme_get_pixbuf_from_mime_type (ev_attachbar->priv->icon_theme,
+ key);
+
+ ev_sidebar_attachments_icon_cache_add (ev_attachbar,
+ key,
+ pixbuf);
+
+ return FALSE;
+}
+
+static void
+ev_sidebar_attachments_icon_cache_refresh (EvSidebarAttachments *ev_attachbar)
+{
+ g_hash_table_foreach_remove (ev_attachbar->priv->icon_cache,
+ (GHRFunc) icon_cache_update_icon,
+ ev_attachbar);
+}
+
+static EvAttachment *
+ev_sidebar_attachments_get_attachment_at_pos (EvSidebarAttachments *ev_attachbar,
+ gint x,
+ gint y)
+{
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ EvAttachment *attachment = NULL;
+
+ path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
+ x, y);
+ if (!path) {
+ return NULL;
+ }
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
+ &iter, path);
+ gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
+ COLUMN_ATTACHMENT, &attachment,
+ -1);
+
+ gtk_icon_view_select_path (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
+ path);
+
+ gtk_tree_path_free (path);
+
+ return attachment;
+}
+
+static gboolean
+ev_sidebar_attachments_popup_menu_show (EvSidebarAttachments *ev_attachbar,
+ gint x,
+ gint y)
+{
+ GtkIconView *icon_view;
+ GtkTreePath *path;
+ GList *selected = NULL, *l;
+ GList *attach_list = NULL;
+
+ icon_view = GTK_ICON_VIEW (ev_attachbar->priv->icon_view);
+
+ path = gtk_icon_view_get_path_at_pos (icon_view, x, y);
+ if (!path)
+ return FALSE;
+
+ if (!gtk_icon_view_path_is_selected (icon_view, path)) {
+ gtk_icon_view_unselect_all (icon_view);
+ gtk_icon_view_select_path (icon_view, path);
+ }
+
+ gtk_tree_path_free (path);
+
+ selected = gtk_icon_view_get_selected_items (icon_view);
+ if (!selected)
+ return FALSE;
+
+ for (l = selected; l && l->data; l = g_list_next (l)) {
+ GtkTreeIter iter;
+ EvAttachment *attachment = NULL;
+
+ path = (GtkTreePath *) l->data;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
+ &iter, path);
+ gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
+ COLUMN_ATTACHMENT, &attachment,
+ -1);
+
+ if (attachment)
+ attach_list = g_list_prepend (attach_list, attachment);
+
+ gtk_tree_path_free (path);
+ }
+
+ g_list_free (selected);
+
+ if (!attach_list)
+ return FALSE;
+
+ g_signal_emit (ev_attachbar, signals[SIGNAL_POPUP_MENU], 0, attach_list);
+
+ return TRUE;
+}
+
+static gboolean
+ev_sidebar_attachments_popup_menu (GtkWidget *widget)
+{
+ EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (widget);
+ gint x, y;
+
+ gtk_widget_get_pointer (widget, &x, &y);
+
+ return ev_sidebar_attachments_popup_menu_show (ev_attachbar, x, y);
+}
+
+static gboolean
+ev_sidebar_attachments_button_press (EvSidebarAttachments *ev_attachbar,
+ GdkEventButton *event,
+ GtkWidget *icon_view)
+{
+ if (!gtk_widget_has_focus (icon_view)) {
+ gtk_widget_grab_focus (icon_view);
+ }
+
+ if (event->button == 2)
+ return FALSE;
+
+ switch (event->button) {
+ case 1:
+ if (event->type == GDK_2BUTTON_PRESS) {
+ GError *error = NULL;
+ EvAttachment *attachment;
+
+ attachment = ev_sidebar_attachments_get_attachment_at_pos (ev_attachbar,
+ event->x,
+ event->y);
+ if (!attachment)
+ return FALSE;
+
+ ev_attachment_open (attachment,
+ gtk_widget_get_screen (GTK_WIDGET (ev_attachbar)),
+ event->time,
+ &error);
+
+ if (error) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (attachment);
+
+ return TRUE;
+ }
+ break;
+ case 3:
+ return ev_sidebar_attachments_popup_menu_show (ev_attachbar, event->x, event->y);
+ }
+
+ return FALSE;
+}
+
+static void
+ev_sidebar_attachments_update_icons (EvSidebarAttachments *ev_attachbar,
+ gpointer user_data)
+{
+ GtkTreeIter iter;
+ gboolean valid;
+
+ ev_sidebar_attachments_icon_cache_refresh (ev_attachbar);
+
+ valid = gtk_tree_model_get_iter_first (
+ GTK_TREE_MODEL (ev_attachbar->priv->model),
+ &iter);
+
+ while (valid) {
+ EvAttachment *attachment = NULL;
+ GdkPixbuf *pixbuf = NULL;
+ const gchar *mime_type;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
+ COLUMN_ATTACHMENT, &attachment,
+ -1);
+
+ mime_type = ev_attachment_get_mime_type (attachment);
+
+ if (attachment)
+ g_object_unref (attachment);
+
+ pixbuf = ev_sidebar_attachments_icon_cache_get (ev_attachbar,
+ mime_type);
+
+ gtk_list_store_set (ev_attachbar->priv->model, &iter,
+ COLUMN_ICON, pixbuf,
+ -1);
+
+ valid = gtk_tree_model_iter_next (
+ GTK_TREE_MODEL (ev_attachbar->priv->model),
+ &iter);
+ }
+}
+
+static void
+ev_sidebar_attachments_screen_changed (GtkWidget *widget,
+ GdkScreen *old_screen)
+{
+ EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (widget);
+ GdkScreen *screen;
+
+ if (!ev_attachbar->priv->icon_theme)
+ return;
+
+ screen = gtk_widget_get_screen (widget);
+ if (screen == old_screen)
+ return;
+
+ if (old_screen) {
+ g_signal_handlers_disconnect_by_func (
+ gtk_icon_theme_get_for_screen (old_screen),
+ G_CALLBACK (ev_sidebar_attachments_update_icons),
+ ev_attachbar);
+ }
+
+ ev_attachbar->priv->icon_theme = gtk_icon_theme_get_for_screen (screen);
+ g_signal_connect_swapped (ev_attachbar->priv->icon_theme,
+ "changed",
+ G_CALLBACK (ev_sidebar_attachments_update_icons),
+ (gpointer) ev_attachbar);
+
+ if (GTK_WIDGET_CLASS (ev_sidebar_attachments_parent_class)->screen_changed) {
+ GTK_WIDGET_CLASS (ev_sidebar_attachments_parent_class)->screen_changed (widget, old_screen);
+ }
+}
+
+static void
+ev_sidebar_attachments_drag_data_get (GtkWidget *widget,
+ GdkDragContext *drag_context,
+ GtkSelectionData *data,
+ guint info,
+ guint time,
+ gpointer user_data)
+{
+ EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (user_data);
+ GList *selected = NULL, *l;
+ GPtrArray *uris;
+ char **uri_list;
+
+ selected = gtk_icon_view_get_selected_items (GTK_ICON_VIEW (ev_attachbar->priv->icon_view));
+ if (!selected)
+ return;
+
+ uris = g_ptr_array_new ();
+
+ for (l = selected; l && l->data; l = g_list_next (l)) {
+ EvAttachment *attachment;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GFile *file;
+ gchar *template;
+ GError *error = NULL;
+
+ path = (GtkTreePath *) l->data;
+
+ gtk_tree_model_get_iter (GTK_TREE_MODEL (ev_attachbar->priv->model),
+ &iter, path);
+ gtk_tree_model_get (GTK_TREE_MODEL (ev_attachbar->priv->model), &iter,
+ COLUMN_ATTACHMENT, &attachment,
+ -1);
+
+ /* FIXMEchpe: convert to filename encoding first! */
+ template = g_strdup_printf ("%s.XXXXXX", ev_attachment_get_name (attachment));
+ file = ev_mkstemp_file (template, &error);
+ g_free (template);
+
+ if (file != NULL && ev_attachment_save (attachment, file, &error)) {
+ gchar *uri;
+
+ uri = g_file_get_uri (file);
+ g_ptr_array_add (uris, uri);
+ }
+
+ if (error) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ gtk_tree_path_free (path);
+ g_object_unref (file);
+ g_object_unref (attachment);
+ }
+
+ g_ptr_array_add (uris, NULL); /* NULL-terminate */
+ uri_list = (char **) g_ptr_array_free (uris, FALSE);
+ gtk_selection_data_set_uris (data, uri_list);
+ g_strfreev (uri_list);
+
+ g_list_free (selected);
+}
+
+static void
+ev_sidebar_attachments_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EvSidebarAttachments *ev_sidebar_attachments;
+
+ ev_sidebar_attachments = EV_SIDEBAR_ATTACHMENTS (object);
+
+ switch (prop_id) {
+ case PROP_WIDGET:
+ g_value_set_object (value, ev_sidebar_attachments->priv->icon_view);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ev_sidebar_attachments_destroy (GtkObject *object)
+{
+ EvSidebarAttachments *ev_attachbar = EV_SIDEBAR_ATTACHMENTS (object);
+
+ if (ev_attachbar->priv->icon_theme) {
+ g_signal_handlers_disconnect_by_func (
+ ev_attachbar->priv->icon_theme,
+ G_CALLBACK (ev_sidebar_attachments_update_icons),
+ ev_attachbar);
+ ev_attachbar->priv->icon_theme = NULL;
+ }
+
+ if (ev_attachbar->priv->model) {
+ g_object_unref (ev_attachbar->priv->model);
+ ev_attachbar->priv->model = NULL;
+ }
+
+ if (ev_attachbar->priv->icon_cache) {
+ g_hash_table_destroy (ev_attachbar->priv->icon_cache);
+ ev_attachbar->priv->icon_cache = NULL;
+ }
+
+ (* GTK_OBJECT_CLASS (ev_sidebar_attachments_parent_class)->destroy) (object);
+}
+
+static void
+ev_sidebar_attachments_class_init (EvSidebarAttachmentsClass *ev_attachbar_class)
+{
+ GObjectClass *g_object_class;
+ GtkObjectClass *gtk_object_class;
+ GtkWidgetClass *gtk_widget_class;
+
+ g_object_class = G_OBJECT_CLASS (ev_attachbar_class);
+ gtk_object_class = GTK_OBJECT_CLASS (ev_attachbar_class);
+ gtk_widget_class = GTK_WIDGET_CLASS (ev_attachbar_class);
+
+ g_object_class->get_property = ev_sidebar_attachments_get_property;
+ gtk_object_class->destroy = ev_sidebar_attachments_destroy;
+ gtk_widget_class->popup_menu = ev_sidebar_attachments_popup_menu;
+ gtk_widget_class->screen_changed = ev_sidebar_attachments_screen_changed;
+
+ g_type_class_add_private (g_object_class, sizeof (EvSidebarAttachmentsPrivate));
+
+ /* Signals */
+ signals[SIGNAL_POPUP_MENU] =
+ g_signal_new ("popup",
+ G_TYPE_FROM_CLASS (g_object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvSidebarAttachmentsClass, popup_menu),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ G_TYPE_POINTER);
+
+ g_object_class_override_property (g_object_class,
+ PROP_WIDGET,
+ "main-widget");
+}
+
+static void
+ev_sidebar_attachments_init (EvSidebarAttachments *ev_attachbar)
+{
+ GtkWidget *swindow;
+
+ ev_attachbar->priv = EV_SIDEBAR_ATTACHMENTS_GET_PRIVATE (ev_attachbar);
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
+ GTK_SHADOW_IN);
+ /* Data Model */
+ ev_attachbar->priv->model = gtk_list_store_new (N_COLS,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ EV_TYPE_ATTACHMENT);
+
+ /* Icon View */
+ ev_attachbar->priv->icon_view =
+ gtk_icon_view_new_with_model (GTK_TREE_MODEL (ev_attachbar->priv->model));
+ gtk_icon_view_set_selection_mode (GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
+ GTK_SELECTION_MULTIPLE);
+ gtk_icon_view_set_columns (GTK_ICON_VIEW (ev_attachbar->priv->icon_view), -1);
+ g_object_set (G_OBJECT (ev_attachbar->priv->icon_view),
+ "text-column", COLUMN_NAME,
+ "pixbuf-column", COLUMN_ICON,
+ NULL);
+ g_signal_connect_swapped (ev_attachbar->priv->icon_view,
+ "button-press-event",
+ G_CALLBACK (ev_sidebar_attachments_button_press),
+ (gpointer) ev_attachbar);
+
+ gtk_container_add (GTK_CONTAINER (swindow),
+ ev_attachbar->priv->icon_view);
+
+ gtk_container_add (GTK_CONTAINER (ev_attachbar),
+ swindow);
+ gtk_widget_show_all (GTK_WIDGET (ev_attachbar));
+
+ /* Icon Theme */
+ ev_attachbar->priv->icon_theme = NULL;
+
+ /* Icon Cache */
+ ev_attachbar->priv->icon_cache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
+
+ /* Drag and Drop */
+ gtk_icon_view_enable_model_drag_source (
+ GTK_ICON_VIEW (ev_attachbar->priv->icon_view),
+ GDK_BUTTON1_MASK,
+ NULL, 0,
+ GDK_ACTION_COPY);
+ gtk_drag_source_add_uri_targets (ev_attachbar->priv->icon_view);
+
+ g_signal_connect (ev_attachbar->priv->icon_view,
+ "drag-data-get",
+ G_CALLBACK (ev_sidebar_attachments_drag_data_get),
+ (gpointer) ev_attachbar);
+}
+
+GtkWidget *
+ev_sidebar_attachments_new (void)
+{
+ GtkWidget *ev_attachbar;
+
+ ev_attachbar = g_object_new (EV_TYPE_SIDEBAR_ATTACHMENTS, NULL);
+
+ return ev_attachbar;
+}
+
+static void
+job_finished_callback (EvJobAttachments *job,
+ EvSidebarAttachments *ev_attachbar)
+{
+ GList *l;
+
+ for (l = job->attachments; l && l->data; l = g_list_next (l)) {
+ EvAttachment *attachment;
+ GtkTreeIter iter;
+ GdkPixbuf *pixbuf = NULL;
+ const gchar *mime_type;
+
+ attachment = EV_ATTACHMENT (l->data);
+
+ mime_type = ev_attachment_get_mime_type (attachment);
+ pixbuf = ev_sidebar_attachments_icon_cache_get (ev_attachbar,
+ mime_type);
+
+ gtk_list_store_append (ev_attachbar->priv->model, &iter);
+ gtk_list_store_set (ev_attachbar->priv->model, &iter,
+ COLUMN_NAME, ev_attachment_get_name (attachment),
+ COLUMN_ICON, pixbuf,
+ COLUMN_ATTACHMENT, attachment,
+ -1);
+ }
+
+ g_object_unref (job);
+}
+
+
+static void
+ev_sidebar_attachments_document_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvSidebarAttachments *ev_attachbar)
+{
+ EvDocument *document = ev_document_model_get_document (model);
+ EvJob *job;
+
+ if (!EV_IS_DOCUMENT_ATTACHMENTS (document))
+ return;
+
+ if (!ev_document_attachments_has_attachments (EV_DOCUMENT_ATTACHMENTS (document)))
+ return;
+
+ if (!ev_attachbar->priv->icon_theme) {
+ GdkScreen *screen;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (ev_attachbar));
+ ev_attachbar->priv->icon_theme = gtk_icon_theme_get_for_screen (screen);
+ g_signal_connect_swapped (ev_attachbar->priv->icon_theme,
+ "changed",
+ G_CALLBACK (ev_sidebar_attachments_update_icons),
+ (gpointer) ev_attachbar);
+ }
+
+ gtk_list_store_clear (ev_attachbar->priv->model);
+
+ job = ev_job_attachments_new (document);
+ g_signal_connect (job, "finished",
+ G_CALLBACK (job_finished_callback),
+ ev_attachbar);
+ g_signal_connect (job, "cancelled",
+ G_CALLBACK (g_object_unref),
+ NULL);
+ /* The priority doesn't matter for this job */
+ ev_job_scheduler_push_job (job, EV_JOB_PRIORITY_NONE);
+}
+
+static void
+ev_sidebar_attachments_set_model (EvSidebarPage *page,
+ EvDocumentModel *model)
+{
+ g_signal_connect (model, "notify::document",
+ G_CALLBACK (ev_sidebar_attachments_document_changed_cb),
+ page);
+}
+
+static gboolean
+ev_sidebar_attachments_support_document (EvSidebarPage *sidebar_page,
+ EvDocument *document)
+{
+ return (EV_IS_DOCUMENT_ATTACHMENTS (document) &&
+ ev_document_attachments_has_attachments (EV_DOCUMENT_ATTACHMENTS (document)));
+}
+
+static const gchar*
+ev_sidebar_attachments_get_label (EvSidebarPage *sidebar_page)
+{
+ return _("Attachments");
+}
+
+static void
+ev_sidebar_attachments_page_iface_init (EvSidebarPageInterface *iface)
+{
+ iface->support_document = ev_sidebar_attachments_support_document;
+ iface->set_model = ev_sidebar_attachments_set_model;
+ iface->get_label = ev_sidebar_attachments_get_label;
+}
+
diff --git a/shell/ev-sidebar-attachments.h b/shell/ev-sidebar-attachments.h
new file mode 100644
index 00000000..35e2a324
--- /dev/null
+++ b/shell/ev-sidebar-attachments.h
@@ -0,0 +1,62 @@
+/* ev-sidebar-attachments.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2006 Carlos Garcia Campos
+ *
+ * Author:
+ * Carlos Garcia Campos <[email protected]>
+ *
+ * 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_SIDEBAR_ATTACHMENTS_H__
+#define __EV_SIDEBAR_ATTACHMENTS_H__
+
+#include "ev-attachment.h"
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+typedef struct _EvSidebarAttachments EvSidebarAttachments;
+typedef struct _EvSidebarAttachmentsClass EvSidebarAttachmentsClass;
+typedef struct _EvSidebarAttachmentsPrivate EvSidebarAttachmentsPrivate;
+
+#define EV_TYPE_SIDEBAR_ATTACHMENTS (ev_sidebar_attachments_get_type())
+#define EV_SIDEBAR_ATTACHMENTS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_SIDEBAR_ATTACHMENTS, EvSidebarAttachments))
+#define EV_SIDEBAR_ATTACHMENTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_SIDEBAR_ATTACHMENTS, EvSidebarAttachmentsClass))
+#define EV_IS_SIDEBAR_ATTACHMENTS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_SIDEBAR_ATTACHMENTS))
+#define EV_IS_SIDEBAR_ATTACHMENTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_SIDEBAR_ATTACHMENTS))
+#define EV_SIDEBAR_ATTACHMENTS_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_SIDEBAR_ATTACHMENTS, EvSidebarAttachmentsClass))
+
+struct _EvSidebarAttachments {
+ GtkVBox base_instance;
+
+ EvSidebarAttachmentsPrivate *priv;
+};
+
+struct _EvSidebarAttachmentsClass {
+ GtkVBoxClass base_class;
+
+ /* Signals */
+ void (*popup_menu) (EvSidebarAttachments *ev_attachbar,
+ EvAttachment *attachment);
+};
+
+GType ev_sidebar_attachments_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_sidebar_attachments_new (void);
+
+G_END_DECLS
+
+#endif /* __EV_SIDEBAR_ATTACHMENTS_H__ */
diff --git a/shell/ev-sidebar-layers.c b/shell/ev-sidebar-layers.c
new file mode 100644
index 00000000..686cacaa
--- /dev/null
+++ b/shell/ev-sidebar-layers.c
@@ -0,0 +1,411 @@
+/* ev-sidebar-layers.c
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ *
+ * 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 <glib/gi18n.h>
+#include "gimpcellrenderertoggle.h"
+
+#include "ev-document-layers.h"
+#include "ev-sidebar-page.h"
+#include "ev-jobs.h"
+#include "ev-job-scheduler.h"
+#include "ev-stock-icons.h"
+#include "ev-sidebar-layers.h"
+
+struct _EvSidebarLayersPrivate {
+ GtkTreeView *tree_view;
+
+ EvDocument *document;
+ EvJob *job;
+};
+
+enum {
+ PROP_0,
+ PROP_WIDGET
+};
+
+enum {
+ LAYERS_VISIBILITY_CHANGED,
+ N_SIGNALS
+};
+
+static void ev_sidebar_layers_page_iface_init (EvSidebarPageInterface *iface);
+static void job_finished_callback (EvJobLayers *job,
+ EvSidebarLayers *sidebar_layers);
+
+static guint signals[N_SIGNALS];
+
+G_DEFINE_TYPE_EXTENDED (EvSidebarLayers,
+ ev_sidebar_layers,
+ GTK_TYPE_VBOX,
+ 0,
+ G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
+ ev_sidebar_layers_page_iface_init))
+
+#define EV_SIDEBAR_LAYERS_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_LAYERS, EvSidebarLayersPrivate))
+
+static void
+ev_sidebar_layers_dispose (GObject *object)
+{
+ EvSidebarLayers *sidebar = EV_SIDEBAR_LAYERS (object);
+
+ if (sidebar->priv->job) {
+ g_signal_handlers_disconnect_by_func (sidebar->priv->job,
+ job_finished_callback,
+ sidebar);
+ ev_job_cancel (sidebar->priv->job);
+ g_object_unref (sidebar->priv->job);
+ sidebar->priv->job = NULL;
+ }
+
+ if (sidebar->priv->document) {
+ g_object_unref (sidebar->priv->document);
+ sidebar->priv->document = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_sidebar_layers_parent_class)->dispose (object);
+}
+
+static void
+ev_sidebar_layers_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EvSidebarLayers *ev_sidebar_layers;
+
+ ev_sidebar_layers = EV_SIDEBAR_LAYERS (object);
+
+ switch (prop_id) {
+ case PROP_WIDGET:
+ g_value_set_object (value, ev_sidebar_layers->priv->tree_view);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static GtkTreeModel *
+ev_sidebar_layers_create_loading_model (void)
+{
+ GtkTreeModel *retval;
+ GtkTreeIter iter;
+ gchar *markup;
+
+ /* Creates a fake model to indicate that we're loading */
+ retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LAYERS_N_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_INT);
+
+ gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
+ markup = g_strdup_printf ("<span size=\"larger\" style=\"italic\">%s</span>", _("Loadingā€¦"));
+ gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
+ EV_DOCUMENT_LAYERS_COLUMN_TITLE, markup,
+ EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, FALSE,
+ EV_DOCUMENT_LAYERS_COLUMN_ENABLED, TRUE,
+ EV_DOCUMENT_LAYERS_COLUMN_SHOWTOGGLE, FALSE,
+ EV_DOCUMENT_LAYERS_COLUMN_RBGROUP, -1,
+ EV_DOCUMENT_LAYERS_COLUMN_LAYER, NULL,
+ -1);
+ g_free (markup);
+
+ return retval;
+}
+
+static gboolean
+update_kids (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ if (gtk_tree_store_is_ancestor (GTK_TREE_STORE (model), parent, iter)) {
+ gboolean visible;
+
+ gtk_tree_model_get (model, parent,
+ EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, &visible,
+ -1);
+ gtk_tree_store_set (GTK_TREE_STORE (model), iter,
+ EV_DOCUMENT_LAYERS_COLUMN_ENABLED, visible,
+ -1);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+clear_rb_group (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gint *rb_group)
+{
+ gint group;
+
+ gtk_tree_model_get (model, iter,
+ EV_DOCUMENT_LAYERS_COLUMN_RBGROUP, &group,
+ -1);
+
+ if (group == *rb_group) {
+ gtk_tree_store_set (GTK_TREE_STORE (model), iter,
+ EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, FALSE,
+ -1);
+ }
+
+ return FALSE;
+}
+
+static void
+ev_sidebar_layers_visibility_changed (GtkCellRendererToggle *cell,
+ gchar *path_str,
+ EvSidebarLayers *ev_layers)
+{
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean visible;
+ EvLayer *layer;
+
+ model = gtk_tree_view_get_model (ev_layers->priv->tree_view);
+
+ path = gtk_tree_path_new_from_string (path_str);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter,
+ EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, &visible,
+ EV_DOCUMENT_LAYERS_COLUMN_LAYER, &layer,
+ -1);
+
+ visible = !visible;
+ if (visible) {
+ gint rb_group;
+
+ ev_document_layers_show_layer (EV_DOCUMENT_LAYERS (ev_layers->priv->document),
+ layer);
+
+ rb_group = ev_layer_get_rb_group (layer);
+ if (rb_group) {
+ gtk_tree_model_foreach (model,
+ (GtkTreeModelForeachFunc)clear_rb_group,
+ &rb_group);
+ }
+ } else {
+ ev_document_layers_hide_layer (EV_DOCUMENT_LAYERS (ev_layers->priv->document),
+ layer);
+ }
+
+ gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
+ EV_DOCUMENT_LAYERS_COLUMN_VISIBLE, visible,
+ -1);
+
+ if (ev_layer_is_parent (layer)) {
+ gtk_tree_model_foreach (model,
+ (GtkTreeModelForeachFunc)update_kids,
+ &iter);
+ }
+
+ gtk_tree_path_free (path);
+
+ g_signal_emit (ev_layers, signals[LAYERS_VISIBILITY_CHANGED], 0);
+}
+
+static GtkTreeView *
+ev_sidebar_layers_create_tree_view (EvSidebarLayers *ev_layers)
+{
+ GtkTreeView *tree_view;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+
+ tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
+ gtk_tree_view_set_headers_visible (tree_view, FALSE);
+ gtk_tree_selection_set_mode (gtk_tree_view_get_selection (tree_view),
+ GTK_SELECTION_NONE);
+
+
+ column = gtk_tree_view_column_new ();
+
+ renderer = gimp_cell_renderer_toggle_new (EV_STOCK_VISIBLE);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "active", EV_DOCUMENT_LAYERS_COLUMN_VISIBLE,
+ "activatable", EV_DOCUMENT_LAYERS_COLUMN_ENABLED,
+ "visible", EV_DOCUMENT_LAYERS_COLUMN_SHOWTOGGLE,
+ "sensitive", EV_DOCUMENT_LAYERS_COLUMN_ENABLED,
+ NULL);
+ g_object_set (G_OBJECT (renderer),
+ "xpad", 0,
+ "ypad", 0,
+ NULL);
+ g_signal_connect (renderer, "toggled",
+ G_CALLBACK (ev_sidebar_layers_visibility_changed),
+ (gpointer)ev_layers);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "markup", EV_DOCUMENT_LAYERS_COLUMN_TITLE,
+ "sensitive", EV_DOCUMENT_LAYERS_COLUMN_ENABLED,
+ NULL);
+ g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+
+ gtk_tree_view_append_column (tree_view, column);
+
+ return tree_view;
+}
+
+static void
+ev_sidebar_layers_init (EvSidebarLayers *ev_layers)
+{
+ GtkWidget *swindow;
+ GtkTreeModel *model;
+
+ ev_layers->priv = EV_SIDEBAR_LAYERS_GET_PRIVATE (ev_layers);
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
+ GTK_SHADOW_IN);
+ /* Data Model */
+ model = ev_sidebar_layers_create_loading_model ();
+
+ /* Layers list */
+ ev_layers->priv->tree_view = ev_sidebar_layers_create_tree_view (ev_layers);
+ gtk_tree_view_set_model (ev_layers->priv->tree_view, model);
+ g_object_unref (model);
+
+ gtk_container_add (GTK_CONTAINER (swindow),
+ GTK_WIDGET (ev_layers->priv->tree_view));
+
+ gtk_container_add (GTK_CONTAINER (ev_layers), swindow);
+ gtk_widget_show_all (GTK_WIDGET (ev_layers));
+}
+
+static void
+ev_sidebar_layers_class_init (EvSidebarLayersClass *ev_layers_class)
+{
+ GObjectClass *g_object_class = G_OBJECT_CLASS (ev_layers_class);
+
+ g_object_class->get_property = ev_sidebar_layers_get_property;
+ g_object_class->dispose = ev_sidebar_layers_dispose;
+
+ g_type_class_add_private (g_object_class, sizeof (EvSidebarLayersPrivate));
+
+ g_object_class_override_property (g_object_class, PROP_WIDGET, "main-widget");
+
+ signals[LAYERS_VISIBILITY_CHANGED] =
+ g_signal_new ("layers_visibility_changed",
+ G_TYPE_FROM_CLASS (g_object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvSidebarLayersClass, layers_visibility_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0, G_TYPE_NONE);
+}
+
+GtkWidget *
+ev_sidebar_layers_new (void)
+{
+ return GTK_WIDGET (g_object_new (EV_TYPE_SIDEBAR_LAYERS, NULL));
+}
+
+static void
+job_finished_callback (EvJobLayers *job,
+ EvSidebarLayers *sidebar_layers)
+{
+ EvSidebarLayersPrivate *priv;
+
+ priv = sidebar_layers->priv;
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), job->model);
+
+ g_object_unref (job);
+ priv->job = NULL;
+}
+
+static void
+ev_sidebar_layers_document_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvSidebarLayers *sidebar_layers)
+{
+ EvDocument *document = ev_document_model_get_document (model);
+ EvSidebarLayersPrivate *priv = sidebar_layers->priv;
+
+ if (!EV_IS_DOCUMENT_LAYERS (document))
+ return;
+
+ if (priv->document) {
+ gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), NULL);
+ g_object_unref (priv->document);
+ }
+
+ priv->document = g_object_ref (document);
+
+ if (priv->job) {
+ g_signal_handlers_disconnect_by_func (priv->job,
+ job_finished_callback,
+ sidebar_layers);
+ g_object_unref (priv->job);
+ }
+
+ priv->job = ev_job_layers_new (document);
+ g_signal_connect (priv->job, "finished",
+ G_CALLBACK (job_finished_callback),
+ sidebar_layers);
+ /* The priority doesn't matter for this job */
+ ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
+}
+
+static void
+ev_sidebar_layers_set_model (EvSidebarPage *sidebar_page,
+ EvDocumentModel *model)
+{
+ g_signal_connect (model, "notify::document",
+ G_CALLBACK (ev_sidebar_layers_document_changed_cb),
+ sidebar_page);
+}
+
+static gboolean
+ev_sidebar_layers_support_document (EvSidebarPage *sidebar_page,
+ EvDocument *document)
+{
+ return (EV_IS_DOCUMENT_LAYERS (document) &&
+ ev_document_layers_has_layers (EV_DOCUMENT_LAYERS (document)));
+}
+
+static const gchar*
+ev_sidebar_layers_get_label (EvSidebarPage *sidebar_page)
+{
+ return _("Layers");
+}
+
+static void
+ev_sidebar_layers_page_iface_init (EvSidebarPageInterface *iface)
+{
+ iface->support_document = ev_sidebar_layers_support_document;
+ iface->set_model = ev_sidebar_layers_set_model;
+ iface->get_label = ev_sidebar_layers_get_label;
+}
+
diff --git a/shell/ev-sidebar-layers.h b/shell/ev-sidebar-layers.h
new file mode 100644
index 00000000..573dc277
--- /dev/null
+++ b/shell/ev-sidebar-layers.h
@@ -0,0 +1,58 @@
+/* ev-sidebar-layers.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2008 Carlos Garcia Campos <[email protected]>
+ *
+ * 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_SIDEBAR_LAYERS_H__
+#define __EV_SIDEBAR_LAYERS_H__
+
+#include <gtk/gtk.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EvSidebarLayers EvSidebarLayers;
+typedef struct _EvSidebarLayersClass EvSidebarLayersClass;
+typedef struct _EvSidebarLayersPrivate EvSidebarLayersPrivate;
+
+#define EV_TYPE_SIDEBAR_LAYERS (ev_sidebar_layers_get_type())
+#define EV_SIDEBAR_LAYERS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_SIDEBAR_LAYERS, EvSidebarLayers))
+#define EV_SIDEBAR_LAYERS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_SIDEBAR_LAYERS, EvSidebarLayersClass))
+#define EV_IS_SIDEBAR_LAYERS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_SIDEBAR_LAYERS))
+#define EV_IS_SIDEBAR_LAYERS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_SIDEBAR_LAYERS))
+#define EV_SIDEBAR_LAYERS_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_SIDEBAR_LAYERS, EvSidebarLayersClass))
+
+struct _EvSidebarLayers {
+ GtkVBox base_instance;
+
+ EvSidebarLayersPrivate *priv;
+};
+
+struct _EvSidebarLayersClass {
+ GtkVBoxClass base_class;
+
+ /* Signals */
+ void (* layers_visibility_changed) (EvSidebarLayers *ev_layers);
+};
+
+GType ev_sidebar_layers_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_sidebar_layers_new (void);
+
+G_END_DECLS
+
+#endif /* __EV_SIDEBAR_LAYERS_H__ */
diff --git a/shell/ev-sidebar-links.c b/shell/ev-sidebar-links.c
new file mode 100644
index 00000000..402f3be7
--- /dev/null
+++ b/shell/ev-sidebar-links.c
@@ -0,0 +1,760 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Author:
+ * Jonathan Blandford <[email protected]>
+ *
+ * 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 <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ev-document-links.h"
+#include "ev-job-scheduler.h"
+#include "ev-sidebar-links.h"
+#include "ev-sidebar-page.h"
+#include "ev-window.h"
+
+struct _EvSidebarLinksPrivate {
+ GtkWidget *tree_view;
+
+ /* Keep these ids around for blocking */
+ guint selection_id;
+ guint page_changed_id;
+ guint row_activated_id;
+
+ EvJob *job;
+ GtkTreeModel *model;
+ EvDocument *document;
+ EvDocumentModel *doc_model;
+};
+
+enum {
+ PROP_0,
+ PROP_MODEL,
+ PROP_WIDGET,
+};
+
+enum {
+ LINK_ACTIVATED,
+ N_SIGNALS
+};
+
+static void update_page_callback (EvSidebarLinks *sidebar_links,
+ gint old_page,
+ gint current_page);
+static void row_activated_callback (GtkTreeView *treeview,
+ GtkTreePath *arg1,
+ GtkTreeViewColumn *arg2,
+ gpointer user_data);
+static void ev_sidebar_links_set_links_model (EvSidebarLinks *links,
+ GtkTreeModel *model);
+static void job_finished_callback (EvJobLinks *job,
+ EvSidebarLinks *sidebar_links);
+static void ev_sidebar_links_set_current_page (EvSidebarLinks *sidebar_links,
+ gint current_page);
+static void ev_sidebar_links_page_iface_init (EvSidebarPageInterface *iface);
+static gboolean ev_sidebar_links_support_document (EvSidebarPage *sidebar_page,
+ EvDocument *document);
+static const gchar* ev_sidebar_links_get_label (EvSidebarPage *sidebar_page);
+
+static guint signals[N_SIGNALS];
+
+G_DEFINE_TYPE_EXTENDED (EvSidebarLinks,
+ ev_sidebar_links,
+ GTK_TYPE_VBOX,
+ 0,
+ G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
+ ev_sidebar_links_page_iface_init))
+
+
+#define EV_SIDEBAR_LINKS_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_LINKS, EvSidebarLinksPrivate))
+
+static void
+ev_sidebar_links_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EvSidebarLinks *ev_sidebar_links = EV_SIDEBAR_LINKS (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ ev_sidebar_links_set_links_model (ev_sidebar_links, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ev_sidebar_links_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EvSidebarLinks *ev_sidebar_links;
+
+ ev_sidebar_links = EV_SIDEBAR_LINKS (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODEL:
+ g_value_set_object (value, ev_sidebar_links->priv->model);
+ break;
+ case PROP_WIDGET:
+ g_value_set_object (value, ev_sidebar_links->priv->tree_view);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ev_sidebar_links_dispose (GObject *object)
+{
+ EvSidebarLinks *sidebar = EV_SIDEBAR_LINKS (object);
+
+ if (sidebar->priv->job) {
+ g_signal_handlers_disconnect_by_func (sidebar->priv->job,
+ job_finished_callback, sidebar);
+ ev_job_cancel (sidebar->priv->job);
+ g_object_unref (sidebar->priv->job);
+ sidebar->priv->job = NULL;
+ }
+
+ if (sidebar->priv->model) {
+ g_object_unref (sidebar->priv->model);
+ sidebar->priv->model = NULL;
+ }
+
+ if (sidebar->priv->document) {
+ g_object_unref (sidebar->priv->document);
+ sidebar->priv->document = NULL;
+ sidebar->priv->doc_model = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_sidebar_links_parent_class)->dispose (object);
+}
+
+static void
+ev_sidebar_links_map (GtkWidget *widget)
+{
+ EvSidebarLinks *links;
+
+ links = EV_SIDEBAR_LINKS (widget);
+
+ GTK_WIDGET_CLASS (ev_sidebar_links_parent_class)->map (widget);
+
+ if (links->priv->model) {
+ ev_sidebar_links_set_current_page (links,
+ ev_document_model_get_page (links->priv->doc_model));
+ }
+}
+
+static void
+ev_sidebar_links_class_init (EvSidebarLinksClass *ev_sidebar_links_class)
+{
+ GObjectClass *g_object_class;
+ GtkWidgetClass *widget_class;
+
+ g_object_class = G_OBJECT_CLASS (ev_sidebar_links_class);
+ widget_class = GTK_WIDGET_CLASS (ev_sidebar_links_class);
+
+ g_object_class->set_property = ev_sidebar_links_set_property;
+ g_object_class->get_property = ev_sidebar_links_get_property;
+ g_object_class->dispose = ev_sidebar_links_dispose;
+
+ widget_class->map = ev_sidebar_links_map;
+
+ signals[LINK_ACTIVATED] = g_signal_new ("link-activated",
+ G_TYPE_FROM_CLASS (g_object_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (EvSidebarLinksClass, link_activated),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+ g_object_class_install_property (g_object_class,
+ PROP_MODEL,
+ g_param_spec_object ("model",
+ "Model",
+ "Current Model",
+ GTK_TYPE_TREE_MODEL,
+ G_PARAM_READWRITE));
+ g_object_class_override_property (g_object_class,
+ PROP_WIDGET,
+ "main-widget");
+
+ g_type_class_add_private (g_object_class, sizeof (EvSidebarLinksPrivate));
+}
+
+static void
+selection_changed_callback (GtkTreeSelection *selection,
+ EvSidebarLinks *ev_sidebar_links)
+{
+ EvDocument *document;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ document = EV_DOCUMENT (ev_sidebar_links->priv->document);
+ g_return_if_fail (ev_sidebar_links->priv->document != NULL);
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ EvLink *link;
+
+ gtk_tree_model_get (model, &iter,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+ -1);
+
+ if (link == NULL)
+ return;
+
+ g_signal_handler_block (ev_sidebar_links->priv->doc_model,
+ ev_sidebar_links->priv->page_changed_id);
+ g_signal_emit (ev_sidebar_links, signals[LINK_ACTIVATED], 0, link);
+ g_signal_handler_unblock (ev_sidebar_links->priv->doc_model,
+ ev_sidebar_links->priv->page_changed_id);
+
+ g_object_unref (link);
+ }
+}
+
+static GtkTreeModel *
+create_loading_model (void)
+{
+ GtkTreeModel *retval;
+ GtkTreeIter iter;
+ gchar *markup;
+
+ /* Creates a fake model to indicate that we're loading */
+ retval = (GtkTreeModel *)gtk_list_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_OBJECT,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING);
+
+ gtk_list_store_append (GTK_LIST_STORE (retval), &iter);
+ markup = g_strdup_printf ("<span size=\"larger\" style=\"italic\">%s</span>", _("Loadingā€¦"));
+ gtk_list_store_set (GTK_LIST_STORE (retval), &iter,
+ EV_DOCUMENT_LINKS_COLUMN_MARKUP, markup,
+ EV_DOCUMENT_LINKS_COLUMN_EXPAND, FALSE,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, NULL,
+ -1);
+ g_free (markup);
+
+ return retval;
+}
+
+static void
+print_section_cb (GtkWidget *menuitem, EvSidebarLinks *sidebar)
+{
+ GtkWidget *window;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ selection = gtk_tree_view_get_selection
+ (GTK_TREE_VIEW (sidebar->priv->tree_view));
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ EvLink *link;
+ int first_page, last_page = -1;
+
+ gtk_tree_model_get (model, &iter,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+ -1);
+
+ if (!link)
+ return;
+
+ first_page = ev_link_get_page (link);
+ if (first_page == -1) {
+ g_object_unref (link);
+ return;
+ }
+
+ first_page++;
+ g_object_unref (link);
+
+ if (gtk_tree_model_iter_next (model, &iter)) {
+ gtk_tree_model_get (model, &iter,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+ -1);
+
+ if (link) {
+ last_page = ev_link_get_page (link);
+ g_object_unref (link);
+ }
+ } else {
+ last_page = ev_document_get_n_pages (sidebar->priv->document);
+ }
+
+ if (last_page == -1)
+ last_page = ev_document_get_n_pages (sidebar->priv->document);
+
+ window = gtk_widget_get_toplevel (GTK_WIDGET (sidebar));
+ if (EV_IS_WINDOW (window)) {
+ ev_window_print_range (EV_WINDOW (window), first_page, last_page);
+ }
+ }
+}
+
+static GtkMenu *
+build_popup_menu (EvSidebarLinks *sidebar)
+{
+ GtkWidget *menu;
+ GtkWidget *item;
+
+ menu = gtk_menu_new ();
+ item = gtk_image_menu_item_new_from_stock (GTK_STOCK_PRINT, NULL);
+ gtk_label_set_label (GTK_LABEL (gtk_bin_get_child (GTK_BIN (item))), _("Printā€¦"));
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
+ g_signal_connect (item, "activate",
+ G_CALLBACK (print_section_cb), sidebar);
+
+ return GTK_MENU (menu);
+}
+
+static void
+popup_menu_cb (GtkWidget *treeview, EvSidebarLinks *sidebar)
+{
+ GtkMenu *menu = build_popup_menu (sidebar);
+
+ gtk_menu_popup (menu, NULL, NULL,
+ ev_gui_menu_position_tree_selection,
+ sidebar->priv->tree_view, 0,
+ gtk_get_current_event_time ());
+ gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
+}
+
+static gboolean
+button_press_cb (GtkWidget *treeview,
+ GdkEventButton *event,
+ EvSidebarLinks *sidebar)
+{
+ GtkTreePath *path = NULL;
+
+ if (event->button == 3) {
+ if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (treeview),
+ event->x,
+ event->y,
+ &path,
+ NULL, NULL, NULL)) {
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (treeview),
+ path, NULL, FALSE);
+ gtk_menu_popup (build_popup_menu (sidebar), NULL,
+ NULL, NULL, NULL, event->button,
+ gtk_get_current_event_time ());
+ gtk_tree_path_free (path);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void
+ev_sidebar_links_construct (EvSidebarLinks *ev_sidebar_links)
+{
+ EvSidebarLinksPrivate *priv;
+ GtkWidget *swindow;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *selection;
+ GtkTreeModel *loading_model;
+
+ priv = ev_sidebar_links->priv;
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swindow),
+ GTK_SHADOW_IN);
+
+ /* Create tree view */
+ loading_model = create_loading_model ();
+ priv->tree_view = gtk_tree_view_new_with_model (loading_model);
+ g_object_unref (loading_model);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_NONE);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
+ gtk_container_add (GTK_CONTAINER (swindow), priv->tree_view);
+
+ gtk_box_pack_start (GTK_BOX (ev_sidebar_links), swindow, TRUE, TRUE, 0);
+ gtk_widget_show_all (GTK_WIDGET (ev_sidebar_links));
+
+ 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", EV_DOCUMENT_LINKS_COLUMN_MARKUP,
+ 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", EV_DOCUMENT_LINKS_COLUMN_PAGE_LABEL,
+ NULL);
+ g_object_set (G_OBJECT (renderer), "style", PANGO_STYLE_ITALIC, NULL);
+
+ g_signal_connect (priv->tree_view,
+ "button_press_event",
+ G_CALLBACK (button_press_cb),
+ ev_sidebar_links);
+ g_signal_connect (priv->tree_view,
+ "popup_menu",
+ G_CALLBACK (popup_menu_cb),
+ ev_sidebar_links);
+}
+
+static void
+ev_sidebar_links_init (EvSidebarLinks *ev_sidebar_links)
+{
+ ev_sidebar_links->priv = EV_SIDEBAR_LINKS_GET_PRIVATE (ev_sidebar_links);
+
+ ev_sidebar_links_construct (ev_sidebar_links);
+}
+
+static gboolean
+fill_page_labels (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ EvSidebarLinks *sidebar_links)
+{
+ EvLink *link;
+ gint page;
+ gchar *page_label;
+
+ gtk_tree_model_get (tree_model, iter,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+ -1);
+
+ if (!link)
+ return FALSE;
+
+ page = ev_link_get_page (link);
+
+ if (page < 0)
+ return FALSE;
+
+ page_label = ev_document_get_page_label (sidebar_links->priv->document,
+ page);
+ gtk_tree_store_set (GTK_TREE_STORE (tree_model), iter,
+ EV_DOCUMENT_LINKS_COLUMN_PAGE_LABEL, page_label,
+ -1);
+
+ g_free (page_label);
+
+ g_object_unref (link);
+ return FALSE;
+}
+
+/* Public Functions */
+
+GtkWidget *
+ev_sidebar_links_new (void)
+{
+ GtkWidget *ev_sidebar_links;
+
+ ev_sidebar_links = g_object_new (EV_TYPE_SIDEBAR_LINKS, NULL);
+
+ return ev_sidebar_links;
+}
+
+static gboolean
+update_page_callback_foreach (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ EvSidebarLinks *sidebar_links = (data);
+ EvLink *link;
+
+ gtk_tree_model_get (model, iter,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+ -1);
+
+ if (link) {
+ int current_page;
+ int dest_page;
+
+ dest_page = ev_link_get_page (link);
+ g_object_unref (link);
+
+ current_page = ev_document_model_get_page (sidebar_links->priv->doc_model);
+
+ if (dest_page == current_page) {
+ gtk_tree_view_expand_to_path (GTK_TREE_VIEW (sidebar_links->priv->tree_view),
+ path);
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (sidebar_links->priv->tree_view),
+ path, NULL, FALSE);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+ev_sidebar_links_set_current_page (EvSidebarLinks *sidebar_links,
+ gint current_page)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* Widget is not currently visible */
+ if (!gtk_widget_get_mapped (GTK_WIDGET (sidebar_links)))
+ return;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (sidebar_links->priv->tree_view));
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ EvLink *link;
+
+ gtk_tree_model_get (model, &iter,
+ EV_DOCUMENT_LINKS_COLUMN_LINK, &link,
+ -1);
+ if (link) {
+ gint dest_page;
+
+ dest_page = ev_link_get_page (link);
+ g_object_unref (link);
+
+ if (dest_page == current_page)
+ return;
+ }
+ }
+
+ /* We go through the tree linearly looking for the first page that
+ * matches. This is pretty inefficient. We can do something neat with
+ * a GtkTreeModelSort here to make it faster, if it turns out to be
+ * slow.
+ */
+ g_signal_handler_block (selection, sidebar_links->priv->selection_id);
+ g_signal_handler_block (sidebar_links->priv->tree_view, sidebar_links->priv->row_activated_id);
+
+ gtk_tree_model_foreach (model,
+ update_page_callback_foreach,
+ sidebar_links);
+
+ g_signal_handler_unblock (selection, sidebar_links->priv->selection_id);
+ g_signal_handler_unblock (sidebar_links->priv->tree_view, sidebar_links->priv->row_activated_id);
+}
+
+static void
+update_page_callback (EvSidebarLinks *sidebar_links,
+ gint old_page,
+ gint new_page)
+{
+ ev_sidebar_links_set_current_page (sidebar_links, new_page);
+}
+
+static void
+row_activated_callback (GtkTreeView *treeview,
+ GtkTreePath *arg1,
+ GtkTreeViewColumn *arg2,
+ gpointer user_data)
+{
+ if (gtk_tree_view_row_expanded (GTK_TREE_VIEW (treeview), arg1)) {
+ gtk_tree_view_collapse_row (GTK_TREE_VIEW (treeview), arg1);
+ } else {
+ gtk_tree_view_expand_row (GTK_TREE_VIEW (treeview), arg1, FALSE);
+ }
+}
+
+static void
+expand_open_links (GtkTreeView *tree_view, GtkTreeModel *model, GtkTreeIter *parent)
+{
+ GtkTreeIter iter;
+ gboolean expand;
+
+ if (gtk_tree_model_iter_children (model, &iter, parent)) {
+ do {
+ gtk_tree_model_get (model, &iter,
+ EV_DOCUMENT_LINKS_COLUMN_EXPAND, &expand,
+ -1);
+ if (expand) {
+ GtkTreePath *path;
+
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_view_expand_row (tree_view, path, FALSE);
+ gtk_tree_path_free (path);
+ }
+
+ expand_open_links (tree_view, model, &iter);
+ } while (gtk_tree_model_iter_next (model, &iter));
+ }
+}
+
+static void
+ev_sidebar_links_set_links_model (EvSidebarLinks *sidebar_links,
+ GtkTreeModel *model)
+{
+ EvSidebarLinksPrivate *priv = sidebar_links->priv;
+
+ if (priv->model == model)
+ return;
+
+ if (priv->model)
+ g_object_unref (priv->model);
+ priv->model = g_object_ref (model);
+
+ g_object_notify (G_OBJECT (sidebar_links), "model");
+}
+
+static void
+job_finished_callback (EvJobLinks *job,
+ EvSidebarLinks *sidebar_links)
+{
+ EvSidebarLinksPrivate *priv = sidebar_links->priv;
+ GtkTreeSelection *selection;
+
+ ev_sidebar_links_set_links_model (sidebar_links, job->model);
+
+ gtk_tree_model_foreach (priv->model, (GtkTreeModelForeachFunc)fill_page_labels, sidebar_links);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), job->model);
+
+ g_object_unref (job);
+ priv->job = NULL;
+
+ expand_open_links (GTK_TREE_VIEW (priv->tree_view), priv->model, NULL);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
+ gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
+
+ if (priv->selection_id <= 0) {
+ priv->selection_id =
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (selection_changed_callback),
+ sidebar_links);
+ }
+ priv->page_changed_id =
+ g_signal_connect_swapped (priv->doc_model, "page-changed",
+ G_CALLBACK (update_page_callback),
+ sidebar_links);
+ if (priv->row_activated_id <= 0) {
+ priv->row_activated_id =
+ g_signal_connect (priv->tree_view, "row-activated",
+ G_CALLBACK (row_activated_callback),
+ sidebar_links);
+ }
+
+ ev_sidebar_links_set_current_page (sidebar_links,
+ ev_document_model_get_page (priv->doc_model));
+}
+
+static void
+ev_sidebar_links_document_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvSidebarLinks *sidebar_links)
+{
+ EvDocument *document = ev_document_model_get_document (model);
+ EvSidebarLinksPrivate *priv = sidebar_links->priv;
+
+ if (!EV_IS_DOCUMENT_LINKS (document))
+ return;
+
+ if (!ev_document_links_has_document_links (EV_DOCUMENT_LINKS (document)))
+ return;
+
+ if (priv->document) {
+ gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), NULL);
+ g_object_unref (priv->document);
+ }
+
+ priv->document = g_object_ref (document);
+
+ if (priv->job) {
+ g_signal_handlers_disconnect_by_func (priv->job,
+ job_finished_callback,
+ sidebar_links);
+ g_object_unref (priv->job);
+ }
+
+ priv->job = ev_job_links_new (document);
+ g_signal_connect (priv->job,
+ "finished",
+ G_CALLBACK (job_finished_callback),
+ sidebar_links);
+ /* The priority doesn't matter for this job */
+ ev_job_scheduler_push_job (priv->job, EV_JOB_PRIORITY_NONE);
+}
+
+static void
+ev_sidebar_links_set_model (EvSidebarPage *sidebar_page,
+ EvDocumentModel *model)
+{
+ EvSidebarLinks *sidebar_links = EV_SIDEBAR_LINKS (sidebar_page);
+ EvSidebarLinksPrivate *priv = sidebar_links->priv;
+
+ if (priv->doc_model == model)
+ return;
+
+ priv->doc_model = model;
+ g_signal_connect (model, "notify::document",
+ G_CALLBACK (ev_sidebar_links_document_changed_cb),
+ sidebar_page);
+}
+
+static gboolean
+ev_sidebar_links_support_document (EvSidebarPage *sidebar_page,
+ EvDocument *document)
+{
+ return (EV_IS_DOCUMENT_LINKS (document) &&
+ ev_document_links_has_document_links (EV_DOCUMENT_LINKS (document)));
+}
+
+static const gchar*
+ev_sidebar_links_get_label (EvSidebarPage *sidebar_page)
+{
+ return _("Index");
+}
+
+static void
+ev_sidebar_links_page_iface_init (EvSidebarPageInterface *iface)
+{
+ iface->support_document = ev_sidebar_links_support_document;
+ iface->set_model = ev_sidebar_links_set_model;
+ iface->get_label = ev_sidebar_links_get_label;
+}
+
diff --git a/shell/ev-sidebar-links.h b/shell/ev-sidebar-links.h
new file mode 100644
index 00000000..dd4acce6
--- /dev/null
+++ b/shell/ev-sidebar-links.h
@@ -0,0 +1,66 @@
+/* ev-sidebar-links.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Author:
+ * Jonathan Blandford <[email protected]>
+ *
+ * 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_SIDEBAR_LINKS_H__
+#define __EV_SIDEBAR_LINKS_H__
+
+#include <gtk/gtk.h>
+
+#include "ev-document.h"
+#include "ev-link.h"
+#include "ev-utils.h"
+
+G_BEGIN_DECLS
+
+typedef struct _EvSidebarLinks EvSidebarLinks;
+typedef struct _EvSidebarLinksClass EvSidebarLinksClass;
+typedef struct _EvSidebarLinksPrivate EvSidebarLinksPrivate;
+
+#define EV_TYPE_SIDEBAR_LINKS (ev_sidebar_links_get_type())
+#define EV_SIDEBAR_LINKS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_SIDEBAR_LINKS, EvSidebarLinks))
+#define EV_SIDEBAR_LINKS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_SIDEBAR_LINKS, EvSidebarLinksClass))
+#define EV_IS_SIDEBAR_LINKS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_SIDEBAR_LINKS))
+#define EV_IS_SIDEBAR_LINKS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_SIDEBAR_LINKS))
+#define EV_SIDEBAR_LINKS_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_SIDEBAR_LINKS, EvSidebarLinksClass))
+
+struct _EvSidebarLinks {
+ GtkVBox base_instance;
+
+ EvSidebarLinksPrivate *priv;
+};
+
+struct _EvSidebarLinksClass {
+ GtkVBoxClass base_class;
+
+ void (* link_activated) (EvSidebarLinks *sidebar_links,
+ EvLink *link);
+};
+
+GType ev_sidebar_links_get_type (void);
+GtkWidget *ev_sidebar_links_new (void);
+
+G_END_DECLS
+
+#endif /* __EV_SIDEBAR_LINKS_H__ */
+
+
diff --git a/shell/ev-sidebar-page.c b/shell/ev-sidebar-page.c
new file mode 100644
index 00000000..fdb8d4af
--- /dev/null
+++ b/shell/ev-sidebar-page.c
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2005 Marco Pesenti Gritti
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "ev-sidebar-page.h"
+
+G_DEFINE_INTERFACE (EvSidebarPage, ev_sidebar_page, 0)
+
+gboolean
+ev_sidebar_page_support_document (EvSidebarPage *sidebar_page,
+ EvDocument *document)
+{
+ EvSidebarPageInterface *iface;
+
+ g_return_val_if_fail (EV_IS_SIDEBAR_PAGE (sidebar_page), FALSE);
+ g_return_val_if_fail (EV_IS_DOCUMENT (document), FALSE);
+
+ iface = EV_SIDEBAR_PAGE_GET_IFACE (sidebar_page);
+
+ g_return_val_if_fail (iface->support_document, FALSE);
+
+ return iface->support_document (sidebar_page, document);
+}
+
+void
+ev_sidebar_page_set_model (EvSidebarPage *sidebar_page,
+ EvDocumentModel *model)
+{
+ EvSidebarPageInterface *iface;
+
+ g_return_if_fail (EV_IS_SIDEBAR_PAGE (sidebar_page));
+ g_return_if_fail (EV_IS_DOCUMENT_MODEL (model));
+
+ iface = EV_SIDEBAR_PAGE_GET_IFACE (sidebar_page);
+
+ g_assert (iface->set_model);
+
+ iface->set_model (sidebar_page, model);
+}
+
+const gchar *
+ev_sidebar_page_get_label (EvSidebarPage *sidebar_page)
+{
+ EvSidebarPageInterface *iface;
+
+ g_return_val_if_fail (EV_IS_SIDEBAR_PAGE (sidebar_page), NULL);
+
+ iface = EV_SIDEBAR_PAGE_GET_IFACE (sidebar_page);
+
+ g_assert (iface->get_label);
+
+ return iface->get_label (sidebar_page);
+}
+
+
+static void
+ev_sidebar_page_default_init (EvSidebarPageInterface *iface)
+{
+ static gboolean initialized = FALSE;
+
+ if (!initialized) {
+ g_object_interface_install_property (iface,
+ g_param_spec_object ("main-widget",
+ "Main Widget",
+ "Main page widget, used to handle focus",
+ GTK_TYPE_WIDGET,
+ G_PARAM_READABLE));
+ initialized = TRUE;
+ }
+}
diff --git a/shell/ev-sidebar-page.h b/shell/ev-sidebar-page.h
new file mode 100644
index 00000000..21ef92cf
--- /dev/null
+++ b/shell/ev-sidebar-page.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2005 Marco Pesenti Gritti
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef EV_SIDEBAR_PAGE_H
+#define EV_SIDEBAR_PAGE_H
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include "ev-document.h"
+#include "ev-document-model.h"
+
+G_BEGIN_DECLS
+
+#define EV_TYPE_SIDEBAR_PAGE (ev_sidebar_page_get_type ())
+#define EV_SIDEBAR_PAGE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EV_TYPE_SIDEBAR_PAGE, EvSidebarPage))
+#define EV_SIDEBAR_PAGE_IFACE(k) (G_TYPE_CHECK_CLASS_CAST((k), EV_TYPE_SIDEBAR_PAGE, EvSidebarPageInterface))
+#define EV_IS_SIDEBAR_PAGE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EV_TYPE_SIDEBAR_PAGE))
+#define EV_IS_SIDEBAR_PAGE_IFACE(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EV_TYPE_SIDEBAR_PAGE))
+#define EV_SIDEBAR_PAGE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), EV_TYPE_SIDEBAR_PAGE, EvSidebarPageInterface))
+
+typedef struct _EvSidebarPage EvSidebarPage;
+typedef struct _EvSidebarPageInterface EvSidebarPageInterface;
+
+struct _EvSidebarPageInterface
+{
+ GTypeInterface base_iface;
+
+ /* Methods */
+ gboolean (* support_document) (EvSidebarPage *sidebar_page,
+ EvDocument *document);
+ void (* set_model) (EvSidebarPage *sidebar_page,
+ EvDocumentModel *model);
+ const gchar*(* get_label) (EvSidebarPage *sidebar_page);
+};
+
+GType ev_sidebar_page_get_type (void) G_GNUC_CONST;
+gboolean ev_sidebar_page_support_document (EvSidebarPage *sidebar_page,
+ EvDocument *document);
+void ev_sidebar_page_set_model (EvSidebarPage *sidebar_page,
+ EvDocumentModel *model);
+const gchar* ev_sidebar_page_get_label (EvSidebarPage *page);
+
+
+G_END_DECLS
+
+#endif /* EV_SIDEBAR_PAGE */
diff --git a/shell/ev-sidebar-thumbnails.c b/shell/ev-sidebar-thumbnails.c
new file mode 100644
index 00000000..65fb3141
--- /dev/null
+++ b/shell/ev-sidebar-thumbnails.c
@@ -0,0 +1,974 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2004, 2005 Anders Carlsson <[email protected]>
+ *
+ * Authors:
+ * Jonathan Blandford <[email protected]>
+ * Anders Carlsson <[email protected]>
+ *
+ * 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 <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ev-document-misc.h"
+#include "ev-document-thumbnails.h"
+#include "ev-job-scheduler.h"
+#include "ev-sidebar-page.h"
+#include "ev-sidebar-thumbnails.h"
+#include "ev-utils.h"
+#include "ev-window.h"
+
+#define THUMBNAIL_WIDTH 100
+
+/* The IconView doesn't scale nearly as well as the TreeView, so we arbitrarily
+ * limit its use */
+#define MAX_ICON_VIEW_PAGE_COUNT 1500
+
+typedef struct _EvThumbsSize
+{
+ gint width;
+ gint height;
+} EvThumbsSize;
+
+typedef struct _EvThumbsSizeCache {
+ gboolean uniform;
+ gint uniform_width;
+ gint uniform_height;
+ EvThumbsSize *sizes;
+} EvThumbsSizeCache;
+
+struct _EvSidebarThumbnailsPrivate {
+ GtkWidget *swindow;
+ GtkWidget *icon_view;
+ GtkWidget *tree_view;
+ GtkAdjustment *vadjustment;
+ GtkListStore *list_store;
+ GHashTable *loading_icons;
+ EvDocument *document;
+ EvDocumentModel *model;
+ EvThumbsSizeCache *size_cache;
+
+ gint n_pages, pages_done;
+
+ int rotation;
+ gboolean inverted_colors;
+
+ /* Visible pages */
+ gint start_page, end_page;
+};
+
+enum {
+ COLUMN_PAGE_STRING,
+ COLUMN_PIXBUF,
+ COLUMN_THUMBNAIL_SET,
+ COLUMN_JOB,
+ NUM_COLUMNS
+};
+
+enum {
+ PROP_0,
+ PROP_WIDGET,
+};
+
+static void ev_sidebar_thumbnails_clear_model (EvSidebarThumbnails *sidebar);
+static gboolean ev_sidebar_thumbnails_support_document (EvSidebarPage *sidebar_page,
+ EvDocument *document);
+static void ev_sidebar_thumbnails_page_iface_init (EvSidebarPageInterface *iface);
+static const gchar* ev_sidebar_thumbnails_get_label (EvSidebarPage *sidebar_page);
+static void thumbnail_job_completed_callback (EvJobThumbnail *job,
+ EvSidebarThumbnails *sidebar_thumbnails);
+static void adjustment_changed_cb (EvSidebarThumbnails *sidebar_thumbnails);
+
+G_DEFINE_TYPE_EXTENDED (EvSidebarThumbnails,
+ ev_sidebar_thumbnails,
+ GTK_TYPE_VBOX,
+ 0,
+ G_IMPLEMENT_INTERFACE (EV_TYPE_SIDEBAR_PAGE,
+ ev_sidebar_thumbnails_page_iface_init))
+
+#define EV_SIDEBAR_THUMBNAILS_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR_THUMBNAILS, EvSidebarThumbnailsPrivate));
+
+/* Thumbnails dimensions cache */
+#define EV_THUMBNAILS_SIZE_CACHE_KEY "ev-thumbnails-size-cache"
+
+static EvThumbsSizeCache *
+ev_thumbnails_size_cache_new (EvDocument *document)
+{
+ EvThumbsSizeCache *cache;
+ EvRenderContext *rc = NULL;
+ gint i, n_pages;
+ EvThumbsSize *thumb_size;
+
+ cache = g_new0 (EvThumbsSizeCache, 1);
+
+ n_pages = ev_document_get_n_pages (document);
+
+ /* Assume all pages are the same size until proven otherwise */
+ cache->uniform = TRUE;
+
+ for (i = 0; i < n_pages; i++) {
+ EvPage *page;
+ gdouble page_width, page_height;
+ gint thumb_width = 0;
+ gint thumb_height = 0;
+
+ page = ev_document_get_page (document, i);
+
+ ev_document_get_page_size (document, i, &page_width, &page_height);
+
+ if (!rc) {
+ rc = ev_render_context_new (page, 0, (gdouble)THUMBNAIL_WIDTH / page_width);
+ } else {
+ ev_render_context_set_page (rc, page);
+ ev_render_context_set_scale (rc, (gdouble)THUMBNAIL_WIDTH / page_width);
+ }
+
+ ev_document_thumbnails_get_dimensions (EV_DOCUMENT_THUMBNAILS (document),
+ rc, &thumb_width, &thumb_height);
+
+ if (i == 0) {
+ cache->uniform_width = thumb_width;
+ cache->uniform_height = thumb_height;
+ } else if (cache->uniform &&
+ (cache->uniform_width != thumb_width ||
+ cache->uniform_height != thumb_height)) {
+ /* It's a different thumbnail size. Backfill the array. */
+ int j;
+
+ cache->sizes = g_new0 (EvThumbsSize, n_pages);
+
+ for (j = 0; j < i; j++) {
+ thumb_size = &(cache->sizes[j]);
+ thumb_size->width = cache->uniform_width;
+ thumb_size->height = cache->uniform_height;
+ }
+ cache->uniform = FALSE;
+ }
+
+ if (! cache->uniform) {
+ thumb_size = &(cache->sizes[i]);
+
+ thumb_size->width = thumb_width;
+ thumb_size->height = thumb_height;
+ }
+
+ g_object_unref (page);
+ }
+
+ if (rc) {
+ g_object_unref (rc);
+ }
+
+ return cache;
+}
+
+static void
+ev_thumbnails_size_cache_get_size (EvThumbsSizeCache *cache,
+ gint page,
+ gint rotation,
+ gint *width,
+ gint *height)
+{
+ gint w, h;
+
+ if (cache->uniform) {
+ w = cache->uniform_width;
+ h = cache->uniform_height;
+ } else {
+ EvThumbsSize *thumb_size;
+
+ thumb_size = &(cache->sizes[page]);
+
+ w = thumb_size->width;
+ h = thumb_size->height;
+ }
+
+ if (rotation == 0 || rotation == 180) {
+ if (width) *width = w;
+ if (height) *height = h;
+ } else {
+ if (width) *width = h;
+ if (height) *height = w;
+ }
+}
+
+static void
+ev_thumbnails_size_cache_free (EvThumbsSizeCache *cache)
+{
+ if (cache->sizes) {
+ g_free (cache->sizes);
+ cache->sizes = NULL;
+ }
+
+ g_free (cache);
+}
+
+static EvThumbsSizeCache *
+ev_thumbnails_size_cache_get (EvDocument *document)
+{
+ EvThumbsSizeCache *cache;
+
+ cache = g_object_get_data (G_OBJECT (document), EV_THUMBNAILS_SIZE_CACHE_KEY);
+ if (!cache) {
+ cache = ev_thumbnails_size_cache_new (document);
+ g_object_set_data_full (G_OBJECT (document),
+ EV_THUMBNAILS_SIZE_CACHE_KEY,
+ cache,
+ (GDestroyNotify)ev_thumbnails_size_cache_free);
+ }
+
+ return cache;
+}
+
+
+static void
+ev_sidebar_thumbnails_dispose (GObject *object)
+{
+ EvSidebarThumbnails *sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (object);
+
+ if (sidebar_thumbnails->priv->loading_icons) {
+ g_hash_table_destroy (sidebar_thumbnails->priv->loading_icons);
+ sidebar_thumbnails->priv->loading_icons = NULL;
+ }
+
+ if (sidebar_thumbnails->priv->list_store) {
+ ev_sidebar_thumbnails_clear_model (sidebar_thumbnails);
+ g_object_unref (sidebar_thumbnails->priv->list_store);
+ sidebar_thumbnails->priv->list_store = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_sidebar_thumbnails_parent_class)->dispose (object);
+}
+
+static void
+ev_sidebar_thumbnails_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EvSidebarThumbnails *sidebar = EV_SIDEBAR_THUMBNAILS (object);
+
+ switch (prop_id) {
+ case PROP_WIDGET:
+ if (sidebar->priv->tree_view)
+ g_value_set_object (value, sidebar->priv->tree_view);
+ else
+ g_value_set_object (value, sidebar->priv->icon_view);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+ev_sidebar_thumbnails_map (GtkWidget *widget)
+{
+ EvSidebarThumbnails *sidebar;
+
+ sidebar = EV_SIDEBAR_THUMBNAILS (widget);
+
+ GTK_WIDGET_CLASS (ev_sidebar_thumbnails_parent_class)->map (widget);
+
+ adjustment_changed_cb (sidebar);
+}
+
+static void
+ev_sidebar_thumbnails_class_init (EvSidebarThumbnailsClass *ev_sidebar_thumbnails_class)
+{
+ GObjectClass *g_object_class;
+ GtkObjectClass *gtk_object_class;
+ GtkWidgetClass *widget_class;
+
+ g_object_class = G_OBJECT_CLASS (ev_sidebar_thumbnails_class);
+ gtk_object_class = GTK_OBJECT_CLASS (ev_sidebar_thumbnails_class);
+ widget_class = GTK_WIDGET_CLASS (ev_sidebar_thumbnails_class);
+
+ g_object_class->dispose = ev_sidebar_thumbnails_dispose;
+ g_object_class->get_property = ev_sidebar_thumbnails_get_property;
+ widget_class->map = ev_sidebar_thumbnails_map;
+
+ g_object_class_override_property (g_object_class,
+ PROP_WIDGET,
+ "main-widget");
+
+ g_type_class_add_private (g_object_class, sizeof (EvSidebarThumbnailsPrivate));
+}
+
+GtkWidget *
+ev_sidebar_thumbnails_new (void)
+{
+ GtkWidget *ev_sidebar_thumbnails;
+
+ ev_sidebar_thumbnails = g_object_new (EV_TYPE_SIDEBAR_THUMBNAILS, NULL);
+
+ return ev_sidebar_thumbnails;
+}
+
+static GdkPixbuf *
+ev_sidebar_thumbnails_get_loading_icon (EvSidebarThumbnails *sidebar_thumbnails,
+ gint width,
+ gint height)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+ GdkPixbuf *icon;
+ gchar *key;
+
+ key = g_strdup_printf ("%dx%d", width, height);
+ icon = g_hash_table_lookup (priv->loading_icons, key);
+ if (!icon) {
+ gboolean inverted_colors;
+
+ inverted_colors = ev_document_model_get_inverted_colors (priv->model);
+ icon = ev_document_misc_get_loading_thumbnail (width, height, inverted_colors);
+ g_hash_table_insert (priv->loading_icons, key, icon);
+ } else {
+ g_free (key);
+ }
+
+ return icon;
+}
+
+static void
+clear_range (EvSidebarThumbnails *sidebar_thumbnails,
+ gint start_page,
+ gint end_page)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean result;
+ gint prev_width = -1;
+ gint prev_height = -1;
+
+ g_assert (start_page <= end_page);
+
+ path = gtk_tree_path_new_from_indices (start_page, -1);
+ for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->list_store), &iter, path);
+ result && start_page <= end_page;
+ result = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store), &iter), start_page ++) {
+ EvJobThumbnail *job;
+ GdkPixbuf *loading_icon = NULL;
+ gint width, height;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store),
+ &iter,
+ COLUMN_JOB, &job,
+ -1);
+
+ if (job) {
+ g_signal_handlers_disconnect_by_func (job, thumbnail_job_completed_callback, sidebar_thumbnails);
+ ev_job_cancel (EV_JOB (job));
+ g_object_unref (job);
+ }
+
+ ev_thumbnails_size_cache_get_size (priv->size_cache, start_page,
+ priv->rotation,
+ &width, &height);
+ if (!loading_icon || (width != prev_width && height != prev_height)) {
+ loading_icon =
+ ev_sidebar_thumbnails_get_loading_icon (sidebar_thumbnails,
+ width, height);
+ }
+
+ prev_width = width;
+ prev_height = height;
+
+ gtk_list_store_set (priv->list_store, &iter,
+ COLUMN_JOB, NULL,
+ COLUMN_THUMBNAIL_SET, FALSE,
+ COLUMN_PIXBUF, loading_icon,
+ -1);
+ }
+ gtk_tree_path_free (path);
+}
+
+static gdouble
+get_scale_for_page (EvSidebarThumbnails *sidebar_thumbnails,
+ gint page)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+ gdouble width;
+
+ ev_document_get_page_size (priv->document, page, &width, NULL);
+
+ return (gdouble)THUMBNAIL_WIDTH / width;
+}
+
+static void
+add_range (EvSidebarThumbnails *sidebar_thumbnails,
+ gint start_page,
+ gint end_page)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ gboolean result;
+ gint page = start_page;
+
+ g_assert (start_page <= end_page);
+
+ path = gtk_tree_path_new_from_indices (start_page, -1);
+ for (result = gtk_tree_model_get_iter (GTK_TREE_MODEL (priv->list_store), &iter, path);
+ result && page <= end_page;
+ result = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store), &iter), page ++) {
+ EvJob *job;
+ gboolean thumbnail_set;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store), &iter,
+ COLUMN_JOB, &job,
+ COLUMN_THUMBNAIL_SET, &thumbnail_set,
+ -1);
+
+ if (job == NULL && !thumbnail_set) {
+ job = ev_job_thumbnail_new (priv->document,
+ page, priv->rotation,
+ get_scale_for_page (sidebar_thumbnails, page));
+ ev_job_scheduler_push_job (EV_JOB (job), EV_JOB_PRIORITY_HIGH);
+
+ g_object_set_data_full (G_OBJECT (job), "tree_iter",
+ gtk_tree_iter_copy (&iter),
+ (GDestroyNotify) gtk_tree_iter_free);
+ g_signal_connect (job, "finished",
+ G_CALLBACK (thumbnail_job_completed_callback),
+ sidebar_thumbnails);
+ gtk_list_store_set (priv->list_store, &iter,
+ COLUMN_JOB, job,
+ -1);
+
+ /* The queue and the list own a ref to the job now */
+ g_object_unref (job);
+ } else if (job) {
+ g_object_unref (job);
+ }
+ }
+ gtk_tree_path_free (path);
+}
+
+/* This modifies start */
+static void
+update_visible_range (EvSidebarThumbnails *sidebar_thumbnails,
+ gint start_page,
+ gint end_page)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+ int old_start_page, old_end_page;
+
+ old_start_page = priv->start_page;
+ old_end_page = priv->end_page;
+
+ if (start_page == old_start_page &&
+ end_page == old_end_page)
+ return;
+
+ /* Clear the areas we no longer display */
+ if (old_start_page >= 0 && old_start_page < start_page)
+ clear_range (sidebar_thumbnails, old_start_page, MIN (start_page - 1, old_end_page));
+
+ if (old_end_page > 0 && old_end_page > end_page)
+ clear_range (sidebar_thumbnails, MAX (end_page + 1, old_start_page), old_end_page);
+
+ add_range (sidebar_thumbnails, start_page, end_page);
+
+ priv->start_page = start_page;
+ priv->end_page = end_page;
+}
+
+static void
+adjustment_changed_cb (EvSidebarThumbnails *sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+ GtkTreePath *path = NULL;
+ GtkTreePath *path2 = NULL;
+ gdouble page_size;
+ gdouble value;
+ gint wy1;
+ gint wy2;
+
+ /* Widget is not currently visible */
+ if (!gtk_widget_get_mapped (GTK_WIDGET (sidebar_thumbnails)))
+ return;
+
+ page_size = gtk_adjustment_get_page_size (priv->vadjustment);
+
+ if (page_size == 0)
+ return;
+
+ value = gtk_adjustment_get_value (priv->vadjustment);
+
+ if (priv->tree_view) {
+ if (! gtk_widget_get_realized (priv->tree_view))
+ return;
+
+ gtk_tree_view_convert_tree_to_bin_window_coords (GTK_TREE_VIEW (priv->tree_view),
+ 0, (int) value,
+ NULL, &wy1);
+ gtk_tree_view_convert_tree_to_bin_window_coords (GTK_TREE_VIEW (priv->tree_view),
+ 0, (int) (value + page_size),
+ NULL, &wy2);
+ gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
+ 1, wy1 + 1, &path,
+ NULL, NULL, NULL);
+ gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
+ 1, wy2 -1, &path2,
+ NULL, NULL, NULL);
+ } else if (priv->icon_view) {
+ if (! gtk_widget_get_realized (priv->icon_view))
+ return;
+ if (! gtk_icon_view_get_visible_range (GTK_ICON_VIEW (priv->icon_view), &path, &path2))
+ return;
+ } else {
+ return;
+ }
+
+ if (path && path2) {
+ update_visible_range (sidebar_thumbnails,
+ gtk_tree_path_get_indices (path)[0],
+ gtk_tree_path_get_indices (path2)[0]);
+ }
+
+ gtk_tree_path_free (path);
+ gtk_tree_path_free (path2);
+}
+
+static void
+ev_sidebar_thumbnails_fill_model (EvSidebarThumbnails *sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+ GtkTreeIter iter;
+ int i;
+ gint prev_width = -1;
+ gint prev_height = -1;
+
+ for (i = 0; i < sidebar_thumbnails->priv->n_pages; i++) {
+ gchar *page_label;
+ gchar *page_string;
+ GdkPixbuf *loading_icon = NULL;
+ gint width, height;
+
+ page_label = ev_document_get_page_label (priv->document, i);
+ page_string = g_markup_printf_escaped ("<i>%s</i>", page_label);
+ ev_thumbnails_size_cache_get_size (sidebar_thumbnails->priv->size_cache, i,
+ sidebar_thumbnails->priv->rotation,
+ &width, &height);
+ if (!loading_icon || (width != prev_width && height != prev_height)) {
+ loading_icon =
+ ev_sidebar_thumbnails_get_loading_icon (sidebar_thumbnails,
+ width, height);
+ }
+
+ prev_width = width;
+ prev_height = height;
+
+ gtk_list_store_append (priv->list_store, &iter);
+ gtk_list_store_set (priv->list_store, &iter,
+ COLUMN_PAGE_STRING, page_string,
+ COLUMN_PIXBUF, loading_icon,
+ COLUMN_THUMBNAIL_SET, FALSE,
+ -1);
+ g_free (page_label);
+ g_free (page_string);
+ }
+}
+
+static void
+ev_sidebar_tree_selection_changed (GtkTreeSelection *selection,
+ EvSidebarThumbnails *ev_sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ int page;
+
+ if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->list_store),
+ &iter);
+ page = gtk_tree_path_get_indices (path)[0];
+ gtk_tree_path_free (path);
+
+ ev_document_model_set_page (priv->model, page);
+}
+
+static void
+ev_sidebar_icon_selection_changed (GtkIconView *icon_view,
+ EvSidebarThumbnails *ev_sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv = ev_sidebar_thumbnails->priv;
+ GtkTreePath *path;
+ GList *selected;
+ int page;
+
+ selected = gtk_icon_view_get_selected_items (icon_view);
+ if (selected == NULL)
+ return;
+
+ /* We don't handle or expect multiple selection. */
+ g_assert (selected->next == NULL);
+
+ path = selected->data;
+ page = gtk_tree_path_get_indices (path)[0];
+
+ gtk_tree_path_free (path);
+ g_list_free (selected);
+
+ ev_document_model_set_page (priv->model, page);
+}
+
+static void
+ev_sidebar_init_tree_view (EvSidebarThumbnails *ev_sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+
+ priv = ev_sidebar_thumbnails->priv;
+ priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->list_store));
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
+ g_signal_connect (selection, "changed",
+ G_CALLBACK (ev_sidebar_tree_selection_changed), ev_sidebar_thumbnails);
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
+ renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF,
+ "xpad", 2,
+ "ypad", 2,
+ NULL);
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view), -1,
+ NULL, renderer,
+ "pixbuf", 1,
+ NULL);
+ gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (priv->tree_view), -1,
+ NULL, gtk_cell_renderer_text_new (),
+ "markup", 0, NULL);
+ gtk_container_add (GTK_CONTAINER (priv->swindow), priv->tree_view);
+ gtk_widget_show (priv->tree_view);
+}
+
+static void
+ev_sidebar_init_icon_view (EvSidebarThumbnails *ev_sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv;
+
+ priv = ev_sidebar_thumbnails->priv;
+
+ priv->icon_view = gtk_icon_view_new_with_model (GTK_TREE_MODEL (priv->list_store));
+ gtk_icon_view_set_markup_column (GTK_ICON_VIEW (priv->icon_view), 0);
+ gtk_icon_view_set_pixbuf_column (GTK_ICON_VIEW (priv->icon_view), 1);
+ g_signal_connect (priv->icon_view, "selection-changed",
+ G_CALLBACK (ev_sidebar_icon_selection_changed), ev_sidebar_thumbnails);
+
+ gtk_container_add (GTK_CONTAINER (priv->swindow), priv->icon_view);
+ gtk_widget_show (priv->icon_view);
+}
+
+static gboolean
+ev_sidebar_thumbnails_use_icon_view (EvSidebarThumbnails *sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+
+ return (ev_document_get_n_pages (priv->document) <= MAX_ICON_VIEW_PAGE_COUNT);
+}
+
+static void
+ev_sidebar_thumbnails_init (EvSidebarThumbnails *ev_sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv;
+
+ priv = ev_sidebar_thumbnails->priv = EV_SIDEBAR_THUMBNAILS_GET_PRIVATE (ev_sidebar_thumbnails);
+
+ priv->list_store = gtk_list_store_new (NUM_COLUMNS,
+ G_TYPE_STRING,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_BOOLEAN,
+ EV_TYPE_JOB_THUMBNAIL);
+
+ priv->swindow = gtk_scrolled_window_new (NULL, NULL);
+
+ /* We actually don't want GTK_POLICY_AUTOMATIC for horizontal scrollbar here
+ * it's just a workaround for bug #449462
+ */
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->swindow),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->swindow),
+ GTK_SHADOW_IN);
+ priv->vadjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (priv->swindow));
+ g_signal_connect_data (priv->vadjustment, "value-changed",
+ G_CALLBACK (adjustment_changed_cb),
+ ev_sidebar_thumbnails, NULL,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+ g_signal_connect_swapped (priv->swindow, "size-allocate",
+ G_CALLBACK (adjustment_changed_cb),
+ ev_sidebar_thumbnails);
+ gtk_box_pack_start (GTK_BOX (ev_sidebar_thumbnails), priv->swindow, TRUE, TRUE, 0);
+
+ /* Put it all together */
+ gtk_widget_show_all (priv->swindow);
+}
+
+static void
+ev_sidebar_thumbnails_set_current_page (EvSidebarThumbnails *sidebar,
+ gint page)
+{
+ GtkTreeView *tree_view;
+ GtkTreePath *path;
+
+ path = gtk_tree_path_new_from_indices (page, -1);
+
+ if (sidebar->priv->tree_view) {
+ tree_view = GTK_TREE_VIEW (sidebar->priv->tree_view);
+ gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
+ gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
+ } else if (sidebar->priv->icon_view) {
+
+ g_signal_handlers_block_by_func
+ (sidebar->priv->icon_view,
+ G_CALLBACK (ev_sidebar_icon_selection_changed), sidebar);
+
+ gtk_icon_view_select_path (GTK_ICON_VIEW (sidebar->priv->icon_view), path);
+
+ g_signal_handlers_unblock_by_func
+ (sidebar->priv->icon_view,
+ G_CALLBACK (ev_sidebar_icon_selection_changed), sidebar);
+
+ gtk_icon_view_set_cursor (GTK_ICON_VIEW (sidebar->priv->icon_view), path, NULL, FALSE);
+ }
+
+ gtk_tree_path_free (path);
+}
+
+static void
+page_changed_cb (EvSidebarThumbnails *sidebar,
+ gint old_page,
+ gint new_page)
+{
+ ev_sidebar_thumbnails_set_current_page (sidebar, new_page);
+}
+
+static gboolean
+refresh (EvSidebarThumbnails *sidebar_thumbnails)
+{
+ adjustment_changed_cb (sidebar_thumbnails);
+ return FALSE;
+}
+
+static void
+ev_sidebar_thumbnails_reload (EvSidebarThumbnails *sidebar_thumbnails)
+{
+ EvDocumentModel *model;
+
+ if (sidebar_thumbnails->priv->loading_icons)
+ g_hash_table_remove_all (sidebar_thumbnails->priv->loading_icons);
+
+ if (sidebar_thumbnails->priv->document == NULL ||
+ sidebar_thumbnails->priv->n_pages <= 0)
+ return;
+
+ model = sidebar_thumbnails->priv->model;
+
+ ev_sidebar_thumbnails_clear_model (sidebar_thumbnails);
+ ev_sidebar_thumbnails_fill_model (sidebar_thumbnails);
+
+ /* Trigger a redraw */
+ sidebar_thumbnails->priv->start_page = -1;
+ sidebar_thumbnails->priv->end_page = -1;
+ ev_sidebar_thumbnails_set_current_page (sidebar_thumbnails,
+ ev_document_model_get_page (model));
+ g_idle_add ((GSourceFunc)refresh, sidebar_thumbnails);
+}
+
+static void
+ev_sidebar_thumbnails_rotation_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvSidebarThumbnails *sidebar_thumbnails)
+{
+ gint rotation = ev_document_model_get_rotation (model);
+
+ sidebar_thumbnails->priv->rotation = rotation;
+ ev_sidebar_thumbnails_reload (sidebar_thumbnails);
+}
+
+static void
+ev_sidebar_thumbnails_inverted_colors_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvSidebarThumbnails *sidebar_thumbnails)
+{
+ gboolean inverted_colors = ev_document_model_get_inverted_colors (model);
+
+ sidebar_thumbnails->priv->inverted_colors = inverted_colors;
+ ev_sidebar_thumbnails_reload (sidebar_thumbnails);
+}
+
+static void
+thumbnail_job_completed_callback (EvJobThumbnail *job,
+ EvSidebarThumbnails *sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+ GtkTreeIter *iter;
+
+ iter = (GtkTreeIter *) g_object_get_data (G_OBJECT (job), "tree_iter");
+ if (priv->inverted_colors)
+ ev_document_misc_invert_pixbuf (job->thumbnail);
+ gtk_list_store_set (priv->list_store,
+ iter,
+ COLUMN_PIXBUF, job->thumbnail,
+ COLUMN_THUMBNAIL_SET, TRUE,
+ COLUMN_JOB, NULL,
+ -1);
+}
+
+static void
+ev_sidebar_thumbnails_document_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvSidebarThumbnails *sidebar_thumbnails)
+{
+ EvDocument *document = ev_document_model_get_document (model);
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+
+ if (!EV_IS_DOCUMENT_THUMBNAILS (document) ||
+ ev_document_get_n_pages (document) <= 0 ||
+ !ev_document_check_dimensions (document)) {
+ return;
+ }
+
+ priv->size_cache = ev_thumbnails_size_cache_get (document);
+ priv->document = document;
+ priv->n_pages = ev_document_get_n_pages (document);
+ priv->rotation = ev_document_model_get_rotation (model);
+ priv->inverted_colors = ev_document_model_get_inverted_colors (model);
+ priv->loading_icons = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_object_unref);
+
+ ev_sidebar_thumbnails_clear_model (sidebar_thumbnails);
+ ev_sidebar_thumbnails_fill_model (sidebar_thumbnails);
+
+ /* Create the view widget, and remove the old one, if needed */
+ if (ev_sidebar_thumbnails_use_icon_view (sidebar_thumbnails)) {
+ if (priv->tree_view) {
+ gtk_container_remove (GTK_CONTAINER (priv->swindow), priv->tree_view);
+ priv->tree_view = NULL;
+ }
+
+ if (! priv->icon_view) {
+ ev_sidebar_init_icon_view (sidebar_thumbnails);
+ g_object_notify (G_OBJECT (sidebar_thumbnails), "main_widget");
+ } else {
+ gtk_widget_queue_resize (priv->icon_view);
+ }
+ } else {
+ if (priv->icon_view) {
+ gtk_container_remove (GTK_CONTAINER (priv->swindow), priv->icon_view);
+ priv->icon_view = NULL;
+ }
+
+ if (! priv->tree_view) {
+ ev_sidebar_init_tree_view (sidebar_thumbnails);
+ g_object_notify (G_OBJECT (sidebar_thumbnails), "main_widget");
+ }
+ }
+
+ /* Connect to the signal and trigger a fake callback */
+ g_signal_connect_swapped (priv->model, "page-changed",
+ G_CALLBACK (page_changed_cb),
+ sidebar_thumbnails);
+ g_signal_connect (priv->model, "notify::rotation",
+ G_CALLBACK (ev_sidebar_thumbnails_rotation_changed_cb),
+ sidebar_thumbnails);
+ g_signal_connect (priv->model, "notify::inverted-colors",
+ G_CALLBACK (ev_sidebar_thumbnails_inverted_colors_changed_cb),
+ sidebar_thumbnails);
+ sidebar_thumbnails->priv->start_page = -1;
+ sidebar_thumbnails->priv->end_page = -1;
+ ev_sidebar_thumbnails_set_current_page (sidebar_thumbnails,
+ ev_document_model_get_page (model));
+ adjustment_changed_cb (sidebar_thumbnails);
+}
+
+static void
+ev_sidebar_thumbnails_set_model (EvSidebarPage *sidebar_page,
+ EvDocumentModel *model)
+{
+ EvSidebarThumbnails *sidebar_thumbnails = EV_SIDEBAR_THUMBNAILS (sidebar_page);
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+
+ if (priv->model == model)
+ return;
+
+ priv->model = model;
+ g_signal_connect (model, "notify::document",
+ G_CALLBACK (ev_sidebar_thumbnails_document_changed_cb),
+ sidebar_page);
+}
+
+static gboolean
+ev_sidebar_thumbnails_clear_job (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ EvJob *job;
+
+ gtk_tree_model_get (model, iter, COLUMN_JOB, &job, -1);
+
+ if (job != NULL) {
+ ev_job_cancel (job);
+ g_signal_handlers_disconnect_by_func (job, thumbnail_job_completed_callback, data);
+ g_object_unref (job);
+ }
+
+ return FALSE;
+}
+
+static void
+ev_sidebar_thumbnails_clear_model (EvSidebarThumbnails *sidebar_thumbnails)
+{
+ EvSidebarThumbnailsPrivate *priv = sidebar_thumbnails->priv;
+
+ gtk_tree_model_foreach (GTK_TREE_MODEL (priv->list_store), ev_sidebar_thumbnails_clear_job, sidebar_thumbnails);
+ gtk_list_store_clear (priv->list_store);
+}
+
+static gboolean
+ev_sidebar_thumbnails_support_document (EvSidebarPage *sidebar_page,
+ EvDocument *document)
+{
+ return (EV_IS_DOCUMENT_THUMBNAILS (document));
+}
+
+static const gchar*
+ev_sidebar_thumbnails_get_label (EvSidebarPage *sidebar_page)
+{
+ return _("Thumbnails");
+}
+
+static void
+ev_sidebar_thumbnails_page_iface_init (EvSidebarPageInterface *iface)
+{
+ iface->support_document = ev_sidebar_thumbnails_support_document;
+ iface->set_model = ev_sidebar_thumbnails_set_model;
+ iface->get_label = ev_sidebar_thumbnails_get_label;
+}
diff --git a/shell/ev-sidebar-thumbnails.h b/shell/ev-sidebar-thumbnails.h
new file mode 100644
index 00000000..0e312469
--- /dev/null
+++ b/shell/ev-sidebar-thumbnails.h
@@ -0,0 +1,59 @@
+/* ev-sidebar-thumbnails.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Author:
+ * Jonathan Blandford <[email protected]>
+ *
+ * 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_SIDEBAR_THUMBNAILS_H__
+#define __EV_SIDEBAR_THUMBNAILS_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _EvSidebarThumbnails EvSidebarThumbnails;
+typedef struct _EvSidebarThumbnailsClass EvSidebarThumbnailsClass;
+typedef struct _EvSidebarThumbnailsPrivate EvSidebarThumbnailsPrivate;
+
+#define EV_TYPE_SIDEBAR_THUMBNAILS (ev_sidebar_thumbnails_get_type())
+#define EV_SIDEBAR_THUMBNAILS(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_SIDEBAR_THUMBNAILS, EvSidebarThumbnails))
+#define EV_SIDEBAR_THUMBNAILS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_SIDEBAR_THUMBNAILS, EvSidebarThumbnailsClass))
+#define EV_IS_SIDEBAR_THUMBNAILS(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_SIDEBAR_THUMBNAILS))
+#define EV_IS_SIDEBAR_THUMBNAILS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_SIDEBAR_THUMBNAILS))
+#define EV_SIDEBAR_THUMBNAILS_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_SIDEBAR_THUMBNAILS, EvSidebarThumbnailsClass))
+
+struct _EvSidebarThumbnails {
+ GtkVBox base_instance;
+
+ EvSidebarThumbnailsPrivate *priv;
+};
+
+struct _EvSidebarThumbnailsClass {
+ GtkVBoxClass base_class;
+};
+
+GType ev_sidebar_thumbnails_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_sidebar_thumbnails_new (void);
+
+G_END_DECLS
+
+#endif /* __EV_SIDEBAR_THUMBNAILS_H__ */
+
+
diff --git a/shell/ev-sidebar.c b/shell/ev-sidebar.c
new file mode 100644
index 00000000..cf7b5f84
--- /dev/null
+++ b/shell/ev-sidebar.c
@@ -0,0 +1,543 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Author:
+ * Jonathan Blandford <[email protected]>
+ *
+ * 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 <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include "ev-sidebar.h"
+#include "ev-sidebar-page.h"
+
+enum
+{
+ PROP_0,
+ PROP_CURRENT_PAGE
+};
+
+enum
+{
+ PAGE_COLUMN_TITLE,
+ PAGE_COLUMN_MENU_ITEM,
+ PAGE_COLUMN_MAIN_WIDGET,
+ PAGE_COLUMN_NOTEBOOK_INDEX,
+ PAGE_COLUMN_NUM_COLS
+};
+
+struct _EvSidebarPrivate {
+ GtkWidget *notebook;
+ GtkWidget *select_button;
+ GtkWidget *menu;
+ GtkWidget *hbox;
+ GtkWidget *label;
+
+ EvDocumentModel *model;
+ GtkTreeModel *page_model;
+};
+
+G_DEFINE_TYPE (EvSidebar, ev_sidebar, GTK_TYPE_VBOX)
+
+#define EV_SIDEBAR_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR, EvSidebarPrivate))
+
+static void
+ev_sidebar_destroy (GtkObject *object)
+{
+ EvSidebar *ev_sidebar = EV_SIDEBAR (object);
+
+ if (ev_sidebar->priv->menu) {
+ gtk_menu_detach (GTK_MENU (ev_sidebar->priv->menu));
+ ev_sidebar->priv->menu = NULL;
+ }
+
+ if (ev_sidebar->priv->page_model) {
+ g_object_unref (ev_sidebar->priv->page_model);
+ ev_sidebar->priv->page_model = NULL;
+ }
+
+
+ (* GTK_OBJECT_CLASS (ev_sidebar_parent_class)->destroy) (object);
+}
+
+static void
+ev_sidebar_select_page (EvSidebar *ev_sidebar, GtkTreeIter *iter)
+{
+ char *title;
+ int index;
+
+ gtk_tree_model_get (ev_sidebar->priv->page_model, iter,
+ PAGE_COLUMN_TITLE, &title,
+ PAGE_COLUMN_NOTEBOOK_INDEX, &index,
+ -1);
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (ev_sidebar->priv->notebook), index);
+ gtk_label_set_text (GTK_LABEL (ev_sidebar->priv->label), title);
+
+ g_free (title);
+}
+
+void
+ev_sidebar_set_page (EvSidebar *ev_sidebar,
+ GtkWidget *main_widget)
+{
+ GtkTreeIter iter;
+ gboolean valid;
+
+ valid = gtk_tree_model_get_iter_first (ev_sidebar->priv->page_model, &iter);
+
+ while (valid) {
+ GtkWidget *widget;
+
+ gtk_tree_model_get (ev_sidebar->priv->page_model, &iter,
+ PAGE_COLUMN_MAIN_WIDGET, &widget,
+ -1);
+
+ if (widget == main_widget) {
+ ev_sidebar_select_page (ev_sidebar, &iter);
+ valid = FALSE;
+ } else {
+ valid = gtk_tree_model_iter_next (ev_sidebar->priv->page_model, &iter);
+ }
+ g_object_unref (widget);
+ }
+
+ g_object_notify (G_OBJECT (ev_sidebar), "current-page");
+}
+
+static void
+ev_sidebar_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ EvSidebar *sidebar = EV_SIDEBAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_CURRENT_PAGE:
+ ev_sidebar_set_page (sidebar, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static GtkWidget *
+ev_sidebar_get_current_page (EvSidebar *sidebar)
+{
+ GtkNotebook *notebook = GTK_NOTEBOOK (sidebar->priv->notebook);
+
+ return gtk_notebook_get_nth_page
+ (notebook, gtk_notebook_get_current_page (notebook));
+}
+
+static void
+ev_sidebar_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ EvSidebar *sidebar = EV_SIDEBAR (object);
+
+ switch (prop_id)
+ {
+ case PROP_CURRENT_PAGE:
+ g_value_set_object (value, ev_sidebar_get_current_page (sidebar));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ev_sidebar_class_init (EvSidebarClass *ev_sidebar_class)
+{
+ GObjectClass *g_object_class;
+ GtkWidgetClass *widget_class;
+ GtkObjectClass *gtk_object_klass;
+
+ g_object_class = G_OBJECT_CLASS (ev_sidebar_class);
+ widget_class = GTK_WIDGET_CLASS (ev_sidebar_class);
+ gtk_object_klass = GTK_OBJECT_CLASS (ev_sidebar_class);
+
+ g_type_class_add_private (g_object_class, sizeof (EvSidebarPrivate));
+
+ gtk_object_klass->destroy = ev_sidebar_destroy;
+ g_object_class->get_property = ev_sidebar_get_property;
+ g_object_class->set_property = ev_sidebar_set_property;
+
+ g_object_class_install_property (g_object_class,
+ PROP_CURRENT_PAGE,
+ g_param_spec_object ("current-page",
+ "Current page",
+ "The currently visible page",
+ GTK_TYPE_WIDGET,
+ G_PARAM_READWRITE));
+}
+
+static void
+ev_sidebar_menu_position_under (GtkMenu *menu,
+ int *x,
+ int *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ GtkWidget *widget;
+ GtkAllocation allocation;
+
+ g_return_if_fail (GTK_IS_BUTTON (user_data));
+ g_return_if_fail (!gtk_widget_get_has_window (GTK_WIDGET (user_data)));
+
+ widget = GTK_WIDGET (user_data);
+
+ gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ *x += allocation.x;
+ *y += allocation.y + allocation.height;
+
+ *push_in = FALSE;
+}
+
+static gboolean
+ev_sidebar_select_button_press_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
+
+ if (event->button == 1) {
+ GtkRequisition requisition;
+ GtkAllocation allocation;
+ gint width;
+
+ gtk_widget_get_allocation (widget, &allocation);
+ width = allocation.width;
+ gtk_widget_set_size_request (ev_sidebar->priv->menu, -1, -1);
+ gtk_widget_size_request (ev_sidebar->priv->menu, &requisition);
+ gtk_widget_set_size_request (ev_sidebar->priv->menu,
+ MAX (width, requisition.width), -1);
+
+ gtk_widget_grab_focus (widget);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+ gtk_menu_popup (GTK_MENU (ev_sidebar->priv->menu),
+ NULL, NULL, ev_sidebar_menu_position_under, widget,
+ event->button, event->time);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+ev_sidebar_select_button_key_press_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
+
+ if (event->keyval == GDK_space ||
+ event->keyval == GDK_KP_Space ||
+ event->keyval == GDK_Return ||
+ event->keyval == GDK_KP_Enter) {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+ gtk_menu_popup (GTK_MENU (ev_sidebar->priv->menu),
+ NULL, NULL, ev_sidebar_menu_position_under, widget,
+ 1, event->time);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+ev_sidebar_close_clicked_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
+
+ gtk_widget_hide (GTK_WIDGET (ev_sidebar));
+}
+
+static void
+ev_sidebar_menu_deactivate_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkWidget *menu_button;
+
+ menu_button = GTK_WIDGET (user_data);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menu_button), FALSE);
+}
+
+static void
+ev_sidebar_menu_detach_cb (GtkWidget *widget,
+ GtkMenu *menu)
+{
+ EvSidebar *ev_sidebar = EV_SIDEBAR (widget);
+
+ ev_sidebar->priv->menu = NULL;
+}
+
+static void
+ev_sidebar_menu_item_activate_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
+ GtkTreeIter iter;
+ GtkWidget *menu_item, *item;
+ gboolean valid;
+
+ menu_item = gtk_menu_get_active (GTK_MENU (ev_sidebar->priv->menu));
+ valid = gtk_tree_model_get_iter_first (ev_sidebar->priv->page_model, &iter);
+
+ while (valid) {
+ gtk_tree_model_get (ev_sidebar->priv->page_model, &iter,
+ PAGE_COLUMN_MENU_ITEM, &item,
+ -1);
+
+ if (item == menu_item) {
+ ev_sidebar_select_page (ev_sidebar, &iter);
+ valid = FALSE;
+ } else {
+ valid = gtk_tree_model_iter_next (ev_sidebar->priv->page_model, &iter);
+ }
+ g_object_unref (item);
+ }
+
+ g_object_notify (G_OBJECT (ev_sidebar), "current-page");
+}
+
+static void
+ev_sidebar_init (EvSidebar *ev_sidebar)
+{
+ GtkWidget *hbox;
+ GtkWidget *close_button;
+ GtkWidget *select_hbox;
+ GtkWidget *arrow;
+ GtkWidget *image;
+
+ ev_sidebar->priv = EV_SIDEBAR_GET_PRIVATE (ev_sidebar);
+
+ /* data model */
+ ev_sidebar->priv->page_model = (GtkTreeModel *)
+ gtk_list_store_new (PAGE_COLUMN_NUM_COLS,
+ G_TYPE_STRING,
+ GTK_TYPE_WIDGET,
+ GTK_TYPE_WIDGET,
+ G_TYPE_INT);
+
+ /* top option menu */
+ hbox = gtk_hbox_new (FALSE, 0);
+ ev_sidebar->priv->hbox = hbox;
+ gtk_box_pack_start (GTK_BOX (ev_sidebar), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ ev_sidebar->priv->select_button = gtk_toggle_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (ev_sidebar->priv->select_button), GTK_RELIEF_NONE);
+ g_signal_connect (ev_sidebar->priv->select_button, "button_press_event",
+ G_CALLBACK (ev_sidebar_select_button_press_cb),
+ ev_sidebar);
+ g_signal_connect (ev_sidebar->priv->select_button, "key_press_event",
+ G_CALLBACK (ev_sidebar_select_button_key_press_cb),
+ ev_sidebar);
+
+ select_hbox = gtk_hbox_new (FALSE, 0);
+
+ ev_sidebar->priv->label = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (select_hbox),
+ ev_sidebar->priv->label,
+ FALSE, FALSE, 0);
+ gtk_widget_show (ev_sidebar->priv->label);
+
+ arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+ gtk_box_pack_end (GTK_BOX (select_hbox), arrow, FALSE, FALSE, 0);
+ gtk_widget_show (arrow);
+
+ gtk_container_add (GTK_CONTAINER (ev_sidebar->priv->select_button), select_hbox);
+ gtk_widget_show (select_hbox);
+
+ gtk_box_pack_start (GTK_BOX (hbox), ev_sidebar->priv->select_button, TRUE, TRUE, 0);
+ gtk_widget_show (ev_sidebar->priv->select_button);
+
+ close_button = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
+ g_signal_connect (close_button, "clicked",
+ G_CALLBACK (ev_sidebar_close_clicked_cb),
+ ev_sidebar);
+
+ image = gtk_image_new_from_stock (GTK_STOCK_CLOSE,
+ GTK_ICON_SIZE_MENU);
+ gtk_container_add (GTK_CONTAINER (close_button), image);
+ gtk_widget_show (image);
+
+ gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
+ gtk_widget_show (close_button);
+
+ ev_sidebar->priv->menu = gtk_menu_new ();
+ g_signal_connect (ev_sidebar->priv->menu, "deactivate",
+ G_CALLBACK (ev_sidebar_menu_deactivate_cb),
+ ev_sidebar->priv->select_button);
+ gtk_menu_attach_to_widget (GTK_MENU (ev_sidebar->priv->menu),
+ GTK_WIDGET (ev_sidebar),
+ ev_sidebar_menu_detach_cb);
+ gtk_widget_show (ev_sidebar->priv->menu);
+
+ ev_sidebar->priv->notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (ev_sidebar->priv->notebook), FALSE);
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (ev_sidebar->priv->notebook), FALSE);
+ gtk_box_pack_start (GTK_BOX (ev_sidebar), ev_sidebar->priv->notebook,
+ TRUE, TRUE, 0);
+ gtk_widget_show (ev_sidebar->priv->notebook);
+
+ gtk_widget_set_sensitive (GTK_WIDGET (ev_sidebar->priv->notebook), FALSE);
+ gtk_widget_set_sensitive (GTK_WIDGET (ev_sidebar->priv->select_button), FALSE);
+}
+
+/* Public functions */
+
+GtkWidget *
+ev_sidebar_new (void)
+{
+ GtkWidget *ev_sidebar;
+
+ ev_sidebar = g_object_new (EV_TYPE_SIDEBAR, NULL);
+
+ return ev_sidebar;
+}
+
+void
+ev_sidebar_add_page (EvSidebar *ev_sidebar,
+ GtkWidget *main_widget)
+{
+ GtkTreeIter iter;
+ GtkWidget *menu_item;
+ gchar *label_title;
+ const gchar *title;
+ int index;
+
+ g_return_if_fail (EV_IS_SIDEBAR (ev_sidebar));
+ g_return_if_fail (EV_IS_SIDEBAR_PAGE (main_widget));
+ g_return_if_fail (GTK_IS_WIDGET (main_widget));
+
+ ev_sidebar_page_set_model (EV_SIDEBAR_PAGE (main_widget),
+ ev_sidebar->priv->model);
+ title = ev_sidebar_page_get_label (EV_SIDEBAR_PAGE (main_widget));
+
+ index = gtk_notebook_append_page (GTK_NOTEBOOK (ev_sidebar->priv->notebook),
+ main_widget, NULL);
+
+ menu_item = gtk_image_menu_item_new_with_label (title);
+ g_signal_connect (menu_item, "activate",
+ G_CALLBACK (ev_sidebar_menu_item_activate_cb),
+ ev_sidebar);
+ gtk_widget_show (menu_item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (ev_sidebar->priv->menu),
+ menu_item);
+
+ /* Insert and move to end */
+ gtk_list_store_insert_with_values (GTK_LIST_STORE (ev_sidebar->priv->page_model),
+ &iter, 0,
+ PAGE_COLUMN_TITLE, title,
+ PAGE_COLUMN_MENU_ITEM, menu_item,
+ PAGE_COLUMN_MAIN_WIDGET, main_widget,
+ PAGE_COLUMN_NOTEBOOK_INDEX, index,
+ -1);
+ gtk_list_store_move_before(GTK_LIST_STORE(ev_sidebar->priv->page_model),
+ &iter, NULL);
+
+
+ /* Set the first item added as active */
+ gtk_tree_model_get_iter_first (ev_sidebar->priv->page_model, &iter);
+ gtk_tree_model_get (ev_sidebar->priv->page_model,
+ &iter,
+ PAGE_COLUMN_TITLE, &label_title,
+ PAGE_COLUMN_NOTEBOOK_INDEX, &index,
+ -1);
+
+ gtk_menu_set_active (GTK_MENU (ev_sidebar->priv->menu), index);
+ gtk_label_set_text (GTK_LABEL (ev_sidebar->priv->label), label_title);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (ev_sidebar->priv->notebook),
+ index);
+ g_free (label_title);
+}
+
+static void
+ev_sidebar_document_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvSidebar *sidebar)
+{
+ EvSidebarPrivate *priv = sidebar->priv;
+ EvDocument *document = ev_document_model_get_document (model);
+ GtkTreeIter iter;
+ gboolean valid;
+ gboolean has_pages = FALSE;
+
+ for (valid = gtk_tree_model_get_iter_first (priv->page_model, &iter);
+ valid;
+ valid = gtk_tree_model_iter_next (priv->page_model, &iter)) {
+ GtkWidget *widget;
+ GtkWidget *menu_widget;
+
+ gtk_tree_model_get (priv->page_model, &iter,
+ PAGE_COLUMN_MAIN_WIDGET, &widget,
+ PAGE_COLUMN_MENU_ITEM, &menu_widget,
+ -1);
+
+ if (ev_sidebar_page_support_document (EV_SIDEBAR_PAGE (widget), document)) {
+ has_pages = TRUE;
+ } else {
+ gtk_widget_set_sensitive (menu_widget, FALSE);
+ }
+ g_object_unref (widget);
+ g_object_unref (menu_widget);
+ }
+
+ if (!has_pages) {
+ gtk_widget_hide (GTK_WIDGET (sidebar));
+ } else {
+ gtk_widget_set_sensitive (GTK_WIDGET (sidebar->priv->notebook), TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (sidebar->priv->select_button), TRUE);
+ }
+}
+
+void
+ev_sidebar_set_model (EvSidebar *sidebar,
+ EvDocumentModel *model)
+{
+ g_return_if_fail (EV_IS_SIDEBAR (sidebar));
+ g_return_if_fail (EV_IS_DOCUMENT_MODEL (model));
+
+ if (model == sidebar->priv->model)
+ return;
+
+ sidebar->priv->model = model;
+ g_signal_connect (model, "notify::document",
+ G_CALLBACK (ev_sidebar_document_changed_cb),
+ sidebar);
+}
diff --git a/shell/ev-sidebar.h b/shell/ev-sidebar.h
new file mode 100644
index 00000000..68440221
--- /dev/null
+++ b/shell/ev-sidebar.h
@@ -0,0 +1,67 @@
+/* ev-sidebar.h
+ * this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Author:
+ * Jonathan Blandford <[email protected]>
+ *
+ * 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_SIDEBAR_H__
+#define __EV_SIDEBAR_H__
+
+#include <gtk/gtk.h>
+
+#include "ev-document-model.h"
+
+G_BEGIN_DECLS
+
+typedef struct _EvSidebar EvSidebar;
+typedef struct _EvSidebarClass EvSidebarClass;
+typedef struct _EvSidebarPrivate EvSidebarPrivate;
+
+#define EV_TYPE_SIDEBAR (ev_sidebar_get_type())
+#define EV_SIDEBAR(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_SIDEBAR, EvSidebar))
+#define EV_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_SIDEBAR, EvSidebarClass))
+#define EV_IS_SIDEBAR(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_SIDEBAR))
+#define EV_IS_SIDEBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_SIDEBAR))
+#define EV_SIDEBAR_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_SIDEBAR, EvSidebarClass))
+
+struct _EvSidebar {
+ GtkVBox base_instance;
+
+ EvSidebarPrivate *priv;
+};
+
+struct _EvSidebarClass {
+ GtkVBoxClass base_class;
+};
+
+GType ev_sidebar_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_sidebar_new (void);
+void ev_sidebar_add_page (EvSidebar *ev_sidebar,
+ GtkWidget *main_widget);
+void ev_sidebar_set_page (EvSidebar *ev_sidebar,
+ GtkWidget *main_widget);
+void ev_sidebar_set_model (EvSidebar *sidebar,
+ EvDocumentModel *model);
+
+G_END_DECLS
+
+#endif /* __EV_SIDEBAR_H__ */
+
+
diff --git a/shell/ev-utils.c b/shell/ev-utils.c
new file mode 100644
index 00000000..83cfcdbc
--- /dev/null
+++ b/shell/ev-utils.c
@@ -0,0 +1,389 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2004 Anders Carlsson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include <config.h>
+
+#include "ev-utils.h"
+#include "ev-file-helpers.h"
+
+#include <string.h>
+#include <math.h>
+#include <glib/gi18n.h>
+
+typedef struct
+{
+ int size;
+ double *data;
+} ConvFilter;
+
+static double
+gaussian (double x, double y, double r)
+{
+ return ((1 / (2 * M_PI * r)) *
+ exp ((- (x * x + y * y)) / (2 * r * r)));
+}
+
+static ConvFilter *
+create_blur_filter (int radius)
+{
+ ConvFilter *filter;
+ int x, y;
+ double sum;
+
+ filter = g_new0 (ConvFilter, 1);
+ filter->size = radius * 2 + 1;
+ filter->data = g_new (double, filter->size * filter->size);
+
+ sum = 0.0;
+
+ for (y = 0 ; y < filter->size; y++)
+ {
+ for (x = 0 ; x < filter->size; x++)
+ {
+ sum += filter->data[y * filter->size + x] = gaussian (x - (filter->size >> 1),
+ y - (filter->size >> 1),
+ radius);
+ }
+ }
+
+ for (y = 0; y < filter->size; y++)
+ {
+ for (x = 0; x < filter->size; x++)
+ {
+ filter->data[y * filter->size + x] /= sum;
+ }
+ }
+
+ return filter;
+
+}
+
+static GdkPixbuf *
+create_shadow (GdkPixbuf *src, int blur_radius,
+ int x_offset, int y_offset, double opacity)
+{
+ int x, y, i, j;
+ int width, height;
+ GdkPixbuf *dest;
+ static ConvFilter *filter = NULL;
+ int src_rowstride, dest_rowstride;
+ int src_bpp, dest_bpp;
+
+ guchar *src_pixels, *dest_pixels;
+
+ if (!filter)
+ filter = create_blur_filter (blur_radius);
+
+ if (x_offset < 0)
+ x_offset = (blur_radius * 4) / 5;
+
+ if (y_offset < 0)
+ y_offset = (blur_radius * 4) / 5;
+
+
+ width = gdk_pixbuf_get_width (src) + blur_radius * 2 + x_offset;
+ height = gdk_pixbuf_get_height (src) + blur_radius * 2 + y_offset;
+
+ dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src), TRUE,
+ gdk_pixbuf_get_bits_per_sample (src),
+ width, height);
+ gdk_pixbuf_fill (dest, 0);
+ src_pixels = gdk_pixbuf_get_pixels (src);
+ src_rowstride = gdk_pixbuf_get_rowstride (src);
+ src_bpp = gdk_pixbuf_get_has_alpha (src) ? 4 : 3;
+
+ dest_pixels = gdk_pixbuf_get_pixels (dest);
+ dest_rowstride = gdk_pixbuf_get_rowstride (dest);
+ dest_bpp = gdk_pixbuf_get_has_alpha (dest) ? 4 : 3;
+
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ {
+ int sumr = 0, sumg = 0, sumb = 0, suma = 0;
+
+ for (i = 0; i < filter->size; i++)
+ {
+ for (j = 0; j < filter->size; j++)
+ {
+ int src_x, src_y;
+
+ src_y = -(blur_radius + x_offset) + y - (filter->size >> 1) + i;
+ src_x = -(blur_radius + y_offset) + x - (filter->size >> 1) + j;
+
+ if (src_y < 0 || src_y > gdk_pixbuf_get_height (src) ||
+ src_x < 0 || src_x > gdk_pixbuf_get_width (src))
+ continue;
+
+ sumr += src_pixels [src_y * src_rowstride +
+ src_x * src_bpp + 0] *
+ filter->data [i * filter->size + j];
+ sumg += src_pixels [src_y * src_rowstride +
+ src_x * src_bpp + 1] *
+ filter->data [i * filter->size + j];
+
+ sumb += src_pixels [src_y * src_rowstride +
+ src_x * src_bpp + 2] *
+ filter->data [i * filter->size + j];
+
+ if (src_bpp == 4)
+ suma += src_pixels [src_y * src_rowstride +
+ src_x * src_bpp + 3] *
+ filter->data [i * filter->size + j];
+ else
+ suma += 0xff;
+
+ }
+ }
+
+ if (dest_bpp == 4)
+ dest_pixels [y * dest_rowstride +
+ x * dest_bpp + 3] = (suma * opacity) / (filter->size * filter->size);
+
+ }
+ }
+
+ return dest;
+}
+
+GdkPixbuf *
+ev_pixbuf_add_shadow (GdkPixbuf *src, int size,
+ int x_offset, int y_offset, double opacity)
+{
+ GdkPixbuf *dest;
+
+ dest = create_shadow (src, size, x_offset, y_offset, opacity);
+
+ gdk_pixbuf_composite (src, dest,
+ size, size,
+ gdk_pixbuf_get_width (src),
+ gdk_pixbuf_get_height (src),
+ size, size,
+ 1.0, 1.0,
+ GDK_INTERP_NEAREST, 255);
+
+ return dest;
+}
+
+
+/* Simple function to output the contents of a region. Used solely for testing
+ * the region code.
+ */
+void
+ev_print_region_contents (cairo_region_t *region)
+{
+ gint n_rectangles, i;
+
+ if (region == NULL) {
+ g_print ("<empty region>\n");
+ return;
+ }
+
+ g_print ("<region %p>\n", region);
+ n_rectangles = cairo_region_num_rectangles (region);
+ for (i = 0; i < n_rectangles; i++) {
+ GdkRectangle rect;
+
+ cairo_region_get_rectangle (region, i, &rect);
+ g_print ("\t(%d %d, %d %d) [%dx%d]\n",
+ rect.x,
+ rect.y,
+ rect.x + rect.width,
+ rect.y + rect.height,
+ rect.width,
+ rect.height);
+ }
+}
+
+static void
+ev_gui_sanitise_popup_position (GtkMenu *menu,
+ GtkWidget *widget,
+ gint *x,
+ gint *y)
+{
+ GdkScreen *screen = gtk_widget_get_screen (widget);
+ gint monitor_num;
+ GdkRectangle monitor;
+ GtkRequisition req;
+
+ g_return_if_fail (widget != NULL);
+
+ gtk_widget_size_request (GTK_WIDGET (menu), &req);
+
+ monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
+ gtk_menu_set_monitor (menu, monitor_num);
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
+ *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
+}
+
+void
+ev_gui_menu_position_tree_selection (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ GtkTreeSelection *selection;
+ GList *selected_rows;
+ GtkTreeModel *model;
+ GtkTreeView *tree_view = GTK_TREE_VIEW (user_data);
+ GtkWidget *widget = GTK_WIDGET (user_data);
+ GtkRequisition req;
+ GtkAllocation allocation;
+ GdkRectangle visible;
+
+ gtk_widget_size_request (GTK_WIDGET (menu), &req);
+ gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ *x += (allocation.width - req.width) / 2;
+
+ /* Add on height for the treeview title */
+ gtk_tree_view_get_visible_rect (tree_view, &visible);
+ *y += allocation.height - visible.height;
+
+ selection = gtk_tree_view_get_selection (tree_view);
+ selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+ if (selected_rows)
+ {
+ GdkRectangle cell_rect;
+
+ gtk_tree_view_get_cell_area (tree_view, selected_rows->data,
+ NULL, &cell_rect);
+
+ *y += CLAMP (cell_rect.y + cell_rect.height, 0, visible.height);
+
+ g_list_foreach (selected_rows, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free (selected_rows);
+ }
+
+ ev_gui_sanitise_popup_position (menu, widget, x, y);
+}
+
+/**
+ * get_num_monitors: Get the number of user monitors.
+ * @window: optional GtkWindow to look at.
+ *
+ * Returns: Number of monitors, -1 if uncertain situation (like multiple screens)
+ */
+gint
+get_num_monitors (GtkWindow *window)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ gint num_screen;
+
+ display = gdk_display_get_default ();
+ num_screen = gdk_display_get_n_screens (display);
+
+ if (num_screen != 1)
+ return -1;
+
+ if (window)
+ screen = gtk_window_get_screen (window);
+ else
+ screen = gdk_display_get_screen (display, 0);
+
+ return gdk_screen_get_n_monitors (screen);
+}
+
+void
+file_chooser_dialog_add_writable_pixbuf_formats (GtkFileChooser *chooser)
+{
+ GSList *pixbuf_formats = NULL;
+ GSList *iter;
+ GtkFileFilter *filter;
+ int i;
+
+ filter = gtk_file_filter_new();
+ gtk_file_filter_set_name (filter, _("By extension"));
+ g_object_set_data (G_OBJECT(filter), "pixbuf-format", NULL);
+ gtk_file_chooser_add_filter (chooser, filter);
+
+ pixbuf_formats = gdk_pixbuf_get_formats ();
+
+ for (iter = pixbuf_formats; iter; iter = iter->next) {
+ GdkPixbufFormat *format = iter->data;
+
+ gchar *description, *name, *extensions;
+ gchar **extension_list, **mime_types;
+
+ if (gdk_pixbuf_format_is_disabled (format) ||
+ !gdk_pixbuf_format_is_writable (format))
+ continue;
+
+ name = gdk_pixbuf_format_get_description (format);
+ extension_list = gdk_pixbuf_format_get_extensions (format);
+ extensions = g_strjoinv (", ", extension_list);
+ g_strfreev (extension_list);
+ description = g_strdup_printf ("%s (%s)", name, extensions);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, description);
+ g_object_set_data (G_OBJECT (filter), "pixbuf-format", format);
+ gtk_file_chooser_add_filter (chooser, filter);
+
+ g_free (description);
+ g_free (extensions);
+ g_free (name);
+
+ mime_types = gdk_pixbuf_format_get_mime_types (format);
+ for (i = 0; mime_types[i] != 0; i++)
+ gtk_file_filter_add_mime_type (filter, mime_types[i]);
+ g_strfreev (mime_types);
+ }
+
+ g_slist_free (pixbuf_formats);
+}
+
+GdkPixbufFormat*
+get_gdk_pixbuf_format_by_extension (gchar *uri)
+{
+ GSList *pixbuf_formats = NULL;
+ GSList *iter;
+ int i;
+
+ pixbuf_formats = gdk_pixbuf_get_formats ();
+
+ for (iter = pixbuf_formats; iter; iter = iter->next) {
+ gchar **extension_list;
+ GdkPixbufFormat *format = iter->data;
+
+ if (gdk_pixbuf_format_is_disabled (format) ||
+ !gdk_pixbuf_format_is_writable (format))
+ continue;
+
+ extension_list = gdk_pixbuf_format_get_extensions (format);
+
+ for (i = 0; extension_list[i] != 0; i++) {
+ if (g_str_has_suffix (uri, extension_list[i])) {
+ g_slist_free (pixbuf_formats);
+ g_strfreev (extension_list);
+ return format;
+ }
+ }
+ g_strfreev (extension_list);
+ }
+
+ g_slist_free (pixbuf_formats);
+ return NULL;
+}
diff --git a/shell/ev-utils.h b/shell/ev-utils.h
new file mode 100644
index 00000000..81acc463
--- /dev/null
+++ b/shell/ev-utils.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/*
+ * Copyright (C) 2004 Anders Carlsson <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __EV_UTILS_H__
+#define __EV_UTILS_H__
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+GdkPixbuf* ev_pixbuf_add_shadow (GdkPixbuf *src, int size,
+ int x_offset, int y_offset, double opacity);
+
+void ev_print_region_contents (cairo_region_t *region);
+
+void ev_gui_menu_position_tree_selection (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data);
+
+gint get_num_monitors (GtkWindow * window);
+
+void file_chooser_dialog_add_writable_pixbuf_formats (GtkFileChooser *chooser);
+GdkPixbufFormat* get_gdk_pixbuf_format_by_extension (gchar *uri);
+
+G_END_DECLS
+
+#endif /* __EV_VIEW_H__ */
diff --git a/shell/ev-window-title.c b/shell/ev-window-title.c
new file mode 100644
index 00000000..ad2837a4
--- /dev/null
+++ b/shell/ev-window-title.c
@@ -0,0 +1,203 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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-window-title.h"
+#include "ev-backends-manager.h"
+#include "ev-utils.h"
+
+#include <string.h>
+#include <gio/gio.h>
+#include <glib/gi18n.h>
+
+/* Known backends (for bad extensions fix) */
+#define EV_BACKEND_PS "psdocument"
+#define EV_BACKEND_PDF "pdfdocument"
+
+typedef struct
+{
+ const gchar *backend;
+ const gchar *text;
+} BadTitleEntry;
+
+struct _EvWindowTitle
+{
+ EvWindow *window;
+ EvWindowTitleType type;
+ EvDocument *document;
+ char *uri;
+};
+
+static const BadTitleEntry bad_extensions[] = {
+ { EV_BACKEND_PS, ".dvi" },
+ { EV_BACKEND_PDF, ".doc" },
+ { EV_BACKEND_PDF, ".dvi" },
+ { EV_BACKEND_PDF, ".indd" },
+ { EV_BACKEND_PDF, ".rtf" }
+};
+
+static const BadTitleEntry bad_prefixes[] = {
+ { EV_BACKEND_PDF, "Microsoft Word - " },
+ { EV_BACKEND_PDF, "Microsoft PowerPoint - " }
+};
+
+EvWindowTitle *
+ev_window_title_new (EvWindow *window)
+{
+ EvWindowTitle *window_title;
+
+ window_title = g_new0 (EvWindowTitle, 1);
+ window_title->window = window;
+ window_title->type = EV_WINDOW_TITLE_DOCUMENT;
+
+ return window_title;
+}
+
+static char *
+get_filename_from_uri (const char *uri)
+{
+ char *filename;
+ char *basename;
+
+ filename = g_uri_unescape_string (uri, NULL);
+ basename = g_path_get_basename (filename);
+ g_free(filename);
+
+ return basename;
+}
+
+/* Some docs report titles with confusing extensions (ex. .doc for pdf).
+ Let's show the filename in this case */
+static void
+ev_window_title_sanitize_title (EvWindowTitle *window_title, char **title) {
+ const gchar *backend;
+ int i;
+
+ backend = ev_backends_manager_get_document_module_name (window_title->document);
+
+ for (i = 0; i < G_N_ELEMENTS (bad_extensions); i++) {
+ if (g_ascii_strcasecmp (bad_extensions[i].backend, backend) == 0 &&
+ g_str_has_suffix (*title, bad_extensions[i].text)) {
+ char *new_title;
+ char *filename = get_filename_from_uri (window_title->uri);
+
+ new_title = g_strdup_printf ("%s (%s)", *title, filename);
+ g_free (*title);
+ *title = new_title;
+
+ g_free (filename);
+ }
+ }
+ for (i = 0; i < G_N_ELEMENTS (bad_prefixes); i++) {
+ if (g_ascii_strcasecmp (bad_prefixes[i].backend, backend) == 0 &&
+ g_str_has_prefix (*title, bad_prefixes[i].text)) {
+ char *new_title;
+ int len = strlen(bad_prefixes[i].text);
+
+ new_title = g_strdup_printf ("%s", (*title) + len);
+ g_free (*title);
+ *title = new_title;
+ }
+ }
+}
+
+static void
+ev_window_title_update (EvWindowTitle *window_title)
+{
+ GtkWindow *window = GTK_WINDOW (window_title->window);
+ char *title = NULL, *password_title, *p;
+
+ if (window_title->document != NULL) {
+ gchar *doc_title;
+
+ doc_title = g_strdup (ev_document_get_title (window_title->document));
+
+ /* Make sure we get a valid title back */
+ if (doc_title != NULL) {
+ doc_title = g_strstrip (doc_title);
+
+ if (doc_title[0] != '\0' &&
+ g_utf8_validate (doc_title, -1, NULL)) {
+ title = g_strdup (doc_title);
+ }
+
+ g_free (doc_title);
+ }
+ }
+
+ if (title && window_title->uri) {
+ ev_window_title_sanitize_title (window_title, &title);
+ } else if (window_title->uri) {
+ title = get_filename_from_uri (window_title->uri);
+ } else if (!title) {
+ title = g_strdup (_("Document Viewer"));
+ }
+
+ for (p = title; *p; ++p) {
+ /* an '\n' byte is always ASCII, no need for UTF-8 special casing */
+ if (*p == '\n') *p = ' ';
+ }
+
+ switch (window_title->type) {
+ case EV_WINDOW_TITLE_DOCUMENT:
+ gtk_window_set_title (window, title);
+ break;
+ case EV_WINDOW_TITLE_PASSWORD:
+ password_title = g_strdup_printf (_("%s ā€” Password Required"), title);
+ gtk_window_set_title (window, password_title);
+ g_free (password_title);
+ break;
+ }
+
+ g_free (title);
+}
+
+void
+ev_window_title_set_type (EvWindowTitle *window_title, EvWindowTitleType type)
+{
+ window_title->type = type;
+
+ ev_window_title_update (window_title);
+}
+
+void
+ev_window_title_set_document (EvWindowTitle *window_title,
+ EvDocument *document)
+{
+ window_title->document = document;
+
+ ev_window_title_update (window_title);
+}
+
+void
+ev_window_title_set_uri (EvWindowTitle *window_title,
+ const char *uri)
+{
+ g_free (window_title->uri);
+ window_title->uri = g_strdup (uri);
+
+ ev_window_title_update (window_title);
+}
+
+void
+ev_window_title_free (EvWindowTitle *window_title)
+{
+ g_free (window_title->uri);
+ g_free (window_title);
+}
diff --git a/shell/ev-window-title.h b/shell/ev-window-title.h
new file mode 100644
index 00000000..8002ee35
--- /dev/null
+++ b/shell/ev-window-title.h
@@ -0,0 +1,47 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2005 Red Hat, Inc
+ *
+ * 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_WINDOW_TITLE_H__
+#define __EV_WINDOW_TITLE_H__
+
+#include "ev-window.h"
+#include "ev-document.h"
+
+G_BEGIN_DECLS
+
+typedef struct _EvWindowTitle EvWindowTitle;
+
+typedef enum
+{
+ EV_WINDOW_TITLE_DOCUMENT,
+ EV_WINDOW_TITLE_PASSWORD
+} EvWindowTitleType;
+
+EvWindowTitle *ev_window_title_new (EvWindow *window);
+void ev_window_title_set_type (EvWindowTitle *window_title,
+ EvWindowTitleType type);
+void ev_window_title_set_document (EvWindowTitle *window_title,
+ EvDocument *document);
+void ev_window_title_set_uri (EvWindowTitle *window_title,
+ const char *uri);
+void ev_window_title_free (EvWindowTitle *window_title);
+
+G_END_DECLS
+
+#endif /* __EV_WINDOW_TITLE_H__ */
diff --git a/shell/ev-window.c b/shell/ev-window.c
new file mode 100644
index 00000000..088ac95b
--- /dev/null
+++ b/shell/ev-window.c
@@ -0,0 +1,7149 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2009 Juanjo MarĆ­n <[email protected]>
+ * Copyright (C) 2008 Carlos Garcia Campos
+ * Copyright (C) 2004 Martin Kretzschmar
+ * Copyright (C) 2004 Red Hat, Inc.
+ * Copyright (C) 2000, 2001, 2002, 2003, 2004 Marco Pesenti Gritti
+ * Copyright Ā© 2003, 2004, 2005, 2009 Christian Persch
+ *
+ * Author:
+ * Martin Kretzschmar <[email protected]>
+ *
+ * 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 <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#ifdef WITH_MATECONF
+#include <mateconf/mateconf-client.h>
+#endif
+
+#include "egg-editable-toolbar.h"
+#include "egg-toolbar-editor.h"
+#include "egg-toolbars-model.h"
+
+#include "eggfindbar.h"
+
+#include "ephy-zoom-action.h"
+#include "ephy-zoom.h"
+
+#include "ev-application.h"
+#include "ev-document-factory.h"
+#include "ev-document-find.h"
+#include "ev-document-fonts.h"
+#include "ev-document-images.h"
+#include "ev-document-links.h"
+#include "ev-document-thumbnails.h"
+#include "ev-document-annotations.h"
+#include "ev-document-type-builtins.h"
+#include "ev-document-misc.h"
+#include "ev-file-exporter.h"
+#include "ev-file-helpers.h"
+#include "ev-file-monitor.h"
+#include "ev-history.h"
+#include "ev-image.h"
+#include "ev-job-scheduler.h"
+#include "ev-jobs.h"
+#include "ev-message-area.h"
+#include "ev-metadata.h"
+#include "ev-navigation-action.h"
+#include "ev-open-recent-action.h"
+#include "ev-page-action.h"
+#include "ev-password-view.h"
+#include "ev-properties-dialog.h"
+#include "ev-sidebar-annotations.h"
+#include "ev-sidebar-attachments.h"
+#include "ev-sidebar.h"
+#include "ev-sidebar-links.h"
+#include "ev-sidebar-page.h"
+#include "ev-sidebar-thumbnails.h"
+#include "ev-sidebar-layers.h"
+#include "ev-stock-icons.h"
+#include "ev-utils.h"
+#include "ev-keyring.h"
+#include "ev-view.h"
+#include "ev-view-presentation.h"
+#include "ev-view-type-builtins.h"
+#include "ev-window.h"
+#include "ev-window-title.h"
+#include "ev-print-operation.h"
+#include "ev-progress-message-area.h"
+#include "ev-annotation-properties-dialog.h"
+
+#ifdef ENABLE_DBUS
+#include "ev-media-player-keys.h"
+#endif /* ENABLE_DBUS */
+
+typedef enum {
+ PAGE_MODE_DOCUMENT,
+ PAGE_MODE_PASSWORD
+} EvWindowPageMode;
+
+typedef enum {
+ EV_CHROME_MENUBAR = 1 << 0,
+ EV_CHROME_TOOLBAR = 1 << 1,
+ EV_CHROME_FINDBAR = 1 << 2,
+ EV_CHROME_RAISE_TOOLBAR = 1 << 3,
+ EV_CHROME_FULLSCREEN_TOOLBAR = 1 << 4,
+ EV_CHROME_SIDEBAR = 1 << 5,
+ EV_CHROME_NORMAL = EV_CHROME_MENUBAR | EV_CHROME_TOOLBAR | EV_CHROME_SIDEBAR
+} EvChrome;
+
+typedef enum {
+ EV_SAVE_DOCUMENT,
+ EV_SAVE_ATTACHMENT,
+ EV_SAVE_IMAGE
+} EvSaveType;
+
+struct _EvWindowPrivate {
+ /* UI */
+ EvChrome chrome;
+
+ GtkWidget *main_box;
+ GtkWidget *menubar;
+ GtkWidget *toolbar;
+ GtkWidget *hpaned;
+ GtkWidget *view_box;
+ GtkWidget *sidebar;
+ GtkWidget *find_bar;
+ GtkWidget *scrolled_window;
+ GtkWidget *view;
+ GtkWidget *presentation_view;
+ GtkWidget *message_area;
+ GtkWidget *password_view;
+ GtkWidget *sidebar_thumbs;
+ GtkWidget *sidebar_links;
+ GtkWidget *sidebar_attachments;
+ GtkWidget *sidebar_layers;
+ GtkWidget *sidebar_annots;
+
+ /* Settings */
+ GSettings *settings;
+ GSettings *default_settings;
+
+ /* Menubar accels */
+ guint menubar_accel_keyval;
+ GdkModifierType menubar_accel_modifier;
+
+ /* Progress Messages */
+ guint progress_idle;
+ GCancellable *progress_cancellable;
+
+ /* Dialogs */
+ GtkWidget *properties;
+ GtkWidget *print_dialog;
+
+ /* UI Builders */
+ GtkActionGroup *action_group;
+ GtkActionGroup *view_popup_action_group;
+ GtkActionGroup *attachment_popup_action_group;
+ GtkRecentManager *recent_manager;
+ GtkActionGroup *recent_action_group;
+ guint recent_ui_id;
+ GtkUIManager *ui_manager;
+
+ /* Fullscreen mode */
+ GtkWidget *fullscreen_toolbar;
+
+ /* Popup view */
+ GtkWidget *view_popup;
+ EvLink *link;
+ EvImage *image;
+ EvAnnotation *annot;
+
+ /* Popup attachment */
+ GtkWidget *attachment_popup;
+ GList *attach_list;
+
+ /* Document */
+ EvDocumentModel *model;
+ char *uri;
+ glong uri_mtime;
+ char *local_uri;
+ gboolean in_reload;
+ EvFileMonitor *monitor;
+ guint setup_document_idle;
+
+ EvDocument *document;
+ EvHistory *history;
+ EvWindowPageMode page_mode;
+ EvWindowTitle *title;
+ EvMetadata *metadata;
+ gboolean is_new_doc;
+
+ /* Load params */
+ EvLinkDest *dest;
+ gchar *search_string;
+ EvWindowRunMode window_mode;
+
+ EvJob *load_job;
+ EvJob *reload_job;
+ EvJob *thumbnail_job;
+ EvJob *save_job;
+ EvJob *find_job;
+
+ /* Printing */
+ GQueue *print_queue;
+ GtkPrintSettings *print_settings;
+ GtkPageSetup *print_page_setup;
+ gboolean close_after_print;
+#ifdef WITH_MATECONF
+ MateConfClient *mateconf_client;
+#endif
+#ifdef ENABLE_DBUS
+ /* DBus */
+ guint dbus_object_id;
+ gchar *dbus_object_path;
+#endif
+};
+
+#define EV_WINDOW_GET_PRIVATE(object) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_WINDOW, EvWindowPrivate))
+
+#define EV_WINDOW_IS_PRESENTATION(w) (w->priv->presentation_view != NULL)
+
+#define PAGE_SELECTOR_ACTION "PageSelector"
+#define ZOOM_CONTROL_ACTION "ViewZoom"
+#define NAVIGATION_ACTION "Navigation"
+
+#define MATECONF_LOCKDOWN_DIR "/desktop/mate/lockdown"
+#define MATECONF_LOCKDOWN_SAVE "/desktop/mate/lockdown/disable_save_to_disk"
+#define MATECONF_LOCKDOWN_PRINT "/desktop/mate/lockdown/disable_printing"
+#define MATECONF_LOCKDOWN_PRINT_SETUP "/desktop/mate/lockdown/disable_print_setup"
+
+#ifdef ENABLE_DBUS
+#define EV_WINDOW_DBUS_OBJECT_PATH "/org/mate/evince/Window/%d"
+#define EV_WINDOW_DBUS_INTERFACE "org.mate.evince.Window"
+#endif
+
+#define GS_SCHEMA_NAME "org.mate.Evince"
+#define GS_OVERRIDE_RESTRICTIONS "override-restrictions"
+
+#define SIDEBAR_DEFAULT_SIZE 132
+#define LINKS_SIDEBAR_ID "links"
+#define THUMBNAILS_SIDEBAR_ID "thumbnails"
+#define ATTACHMENTS_SIDEBAR_ID "attachments"
+#define LAYERS_SIDEBAR_ID "layers"
+#define ANNOTS_SIDEBAR_ID "annotations"
+
+#define EV_PRINT_SETTINGS_FILE "print-settings"
+#define EV_PRINT_SETTINGS_GROUP "Print Settings"
+#define EV_PAGE_SETUP_GROUP "Page Setup"
+
+#define EV_TOOLBARS_FILENAME "evince-toolbar.xml"
+
+#define MIN_SCALE 0.05409
+#define PAGE_CACHE_SIZE 52428800 /* 50MB */
+
+#define MAX_RECENT_ITEM_LEN (40)
+
+static const gchar *document_print_settings[] = {
+ GTK_PRINT_SETTINGS_N_COPIES,
+ GTK_PRINT_SETTINGS_COLLATE,
+ GTK_PRINT_SETTINGS_REVERSE,
+ GTK_PRINT_SETTINGS_NUMBER_UP,
+ GTK_PRINT_SETTINGS_SCALE,
+ GTK_PRINT_SETTINGS_PRINT_PAGES,
+ GTK_PRINT_SETTINGS_PAGE_RANGES,
+ GTK_PRINT_SETTINGS_PAGE_SET,
+ GTK_PRINT_SETTINGS_OUTPUT_URI
+};
+
+static void ev_window_update_actions (EvWindow *ev_window);
+static void ev_window_sidebar_visibility_changed_cb (EvSidebar *ev_sidebar,
+ GParamSpec *pspec,
+ EvWindow *ev_window);
+static void ev_window_view_toolbar_cb (GtkAction *action,
+ EvWindow *ev_window);
+static void ev_window_set_page_mode (EvWindow *window,
+ EvWindowPageMode page_mode);
+static void ev_window_load_job_cb (EvJob *job,
+ gpointer data);
+static void ev_window_reload_document (EvWindow *window,
+ EvLinkDest *dest);
+static void ev_window_reload_job_cb (EvJob *job,
+ EvWindow *window);
+static void ev_window_set_icon_from_thumbnail (EvJobThumbnail *job,
+ EvWindow *ev_window);
+static void ev_window_save_job_cb (EvJob *save,
+ EvWindow *window);
+static void ev_window_sizing_mode_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvWindow *ev_window);
+static void ev_window_zoom_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvWindow *ev_window);
+static void ev_window_add_recent (EvWindow *window,
+ const char *filename);
+static void ev_window_run_fullscreen (EvWindow *window);
+static void ev_window_stop_fullscreen (EvWindow *window,
+ gboolean unfullscreen_window);
+static void ev_window_cmd_view_fullscreen (GtkAction *action,
+ EvWindow *window);
+static void ev_window_run_presentation (EvWindow *window);
+static void ev_window_stop_presentation (EvWindow *window,
+ gboolean unfullscreen_window);
+static void ev_window_cmd_view_presentation (GtkAction *action,
+ EvWindow *window);
+static void ev_view_popup_cmd_open_link (GtkAction *action,
+ EvWindow *window);
+static void ev_view_popup_cmd_open_link_new_window (GtkAction *action,
+ EvWindow *window);
+static void ev_view_popup_cmd_copy_link_address (GtkAction *action,
+ EvWindow *window);
+static void ev_view_popup_cmd_save_image_as (GtkAction *action,
+ EvWindow *window);
+static void ev_view_popup_cmd_copy_image (GtkAction *action,
+ EvWindow *window);
+static void ev_view_popup_cmd_annot_properties (GtkAction *action,
+ EvWindow *window);
+static void ev_attachment_popup_cmd_open_attachment (GtkAction *action,
+ EvWindow *window);
+static void ev_attachment_popup_cmd_save_attachment_as (GtkAction *action,
+ EvWindow *window);
+static void ev_window_cmd_view_best_fit (GtkAction *action,
+ EvWindow *ev_window);
+static void ev_window_cmd_view_page_width (GtkAction *action,
+ EvWindow *ev_window);
+static void view_handle_link_cb (EvView *view,
+ EvLink *link,
+ EvWindow *window);
+static void ev_window_update_find_status_message (EvWindow *ev_window);
+static void ev_window_cmd_edit_find (GtkAction *action,
+ EvWindow *ev_window);
+static void find_bar_search_changed_cb (EggFindBar *find_bar,
+ GParamSpec *param,
+ EvWindow *ev_window);
+static void ev_window_load_file_remote (EvWindow *ev_window,
+ GFile *source_file);
+static void ev_window_media_player_key_pressed (EvWindow *window,
+ const gchar *key,
+ gpointer user_data);
+static void ev_window_update_max_min_scale (EvWindow *window);
+#ifdef ENABLE_DBUS
+static void ev_window_emit_closed (EvWindow *window);
+static void ev_window_emit_doc_loaded (EvWindow *window);
+#endif
+
+static guint ev_window_n_copies = 0;
+
+G_DEFINE_TYPE (EvWindow, ev_window, GTK_TYPE_WINDOW)
+
+static gdouble
+get_screen_dpi (EvWindow *window)
+{
+ GdkScreen *screen;
+
+ screen = gtk_window_get_screen (GTK_WINDOW (window));
+ return ev_document_misc_get_screen_dpi (screen);
+}
+
+static void
+ev_window_set_action_sensitive (EvWindow *ev_window,
+ const char *name,
+ gboolean sensitive)
+{
+ GtkAction *action = gtk_action_group_get_action (ev_window->priv->action_group,
+ name);
+ gtk_action_set_sensitive (action, sensitive);
+}
+
+
+static void
+ev_window_setup_action_sensitivity (EvWindow *ev_window)
+{
+ EvDocument *document = ev_window->priv->document;
+ const EvDocumentInfo *info = NULL;
+ gboolean has_document = FALSE;
+ gboolean ok_to_print = TRUE;
+ gboolean ok_to_copy = TRUE;
+ gboolean has_properties = TRUE;
+ gboolean override_restrictions = TRUE;
+ gboolean can_get_text = FALSE;
+ gboolean has_pages = FALSE;
+ gboolean can_find = FALSE;
+
+ if (document) {
+ has_document = TRUE;
+ has_pages = ev_document_get_n_pages (document) > 0;
+ info = ev_document_get_info (document);
+ }
+
+ if (!info || info->fields_mask == 0) {
+ has_properties = FALSE;
+ }
+
+ if (has_document && EV_IS_SELECTION (document)) {
+ can_get_text = TRUE;
+ }
+
+ if (has_pages && EV_IS_DOCUMENT_FIND (document)) {
+ can_find = TRUE;
+ }
+
+ if (has_document && ev_window->priv->settings) {
+ override_restrictions =
+ g_settings_get_boolean (ev_window->priv->settings,
+ GS_OVERRIDE_RESTRICTIONS);
+ }
+
+ if (!override_restrictions && info && info->fields_mask & EV_DOCUMENT_INFO_PERMISSIONS) {
+ ok_to_print = (info->permissions & EV_DOCUMENT_PERMISSIONS_OK_TO_PRINT);
+ ok_to_copy = (info->permissions & EV_DOCUMENT_PERMISSIONS_OK_TO_COPY);
+ }
+
+ if (has_document && !ev_print_operation_exists_for_document(document))
+ ok_to_print = FALSE;
+
+#ifdef WITH_MATECONF
+ if (has_document &&
+ mateconf_client_get_bool (ev_window->priv->mateconf_client, MATECONF_LOCKDOWN_SAVE, NULL)) {
+ ok_to_copy = FALSE;
+ }
+
+ if (has_document &&
+ mateconf_client_get_bool (ev_window->priv->mateconf_client, MATECONF_LOCKDOWN_PRINT, NULL)) {
+ ok_to_print = FALSE;
+ }
+#endif
+
+ /* File menu */
+ ev_window_set_action_sensitive (ev_window, "FileOpenCopy", has_document);
+ ev_window_set_action_sensitive (ev_window, "FileSaveAs", has_document && ok_to_copy);
+ ev_window_set_action_sensitive (ev_window, "FilePrint", has_pages && ok_to_print);
+ ev_window_set_action_sensitive (ev_window, "FileProperties", has_document && has_properties);
+
+ /* Edit menu */
+ ev_window_set_action_sensitive (ev_window, "EditSelectAll", has_pages && can_get_text);
+ ev_window_set_action_sensitive (ev_window, "EditFind", can_find);
+ ev_window_set_action_sensitive (ev_window, "Slash", can_find);
+ ev_window_set_action_sensitive (ev_window, "EditRotateLeft", has_pages);
+ ev_window_set_action_sensitive (ev_window, "EditRotateRight", has_pages);
+
+ /* View menu */
+ ev_window_set_action_sensitive (ev_window, "ViewContinuous", has_pages);
+ ev_window_set_action_sensitive (ev_window, "ViewDual", has_pages);
+ ev_window_set_action_sensitive (ev_window, "ViewBestFit", has_pages);
+ ev_window_set_action_sensitive (ev_window, "ViewPageWidth", has_pages);
+ ev_window_set_action_sensitive (ev_window, "ViewReload", has_pages);
+ ev_window_set_action_sensitive (ev_window, "ViewAutoscroll", has_pages);
+ ev_window_set_action_sensitive (ev_window, "ViewInvertedColors", has_pages);
+
+ /* Toolbar-specific actions: */
+ ev_window_set_action_sensitive (ev_window, PAGE_SELECTOR_ACTION, has_pages);
+ ev_window_set_action_sensitive (ev_window, ZOOM_CONTROL_ACTION, has_pages);
+ ev_window_set_action_sensitive (ev_window, NAVIGATION_ACTION, FALSE);
+
+ ev_window_update_actions (ev_window);
+}
+
+static void
+ev_window_update_actions (EvWindow *ev_window)
+{
+ EvView *view = EV_VIEW (ev_window->priv->view);
+ int n_pages = 0, page = -1;
+ gboolean has_pages = FALSE;
+ gboolean presentation_mode;
+ gboolean can_find_in_page = FALSE;
+ EvSizingMode sizing_mode;
+
+ if (ev_window->priv->document) {
+ page = ev_document_model_get_page (ev_window->priv->model);
+ n_pages = ev_document_get_n_pages (ev_window->priv->document);
+ has_pages = n_pages > 0;
+ }
+
+ can_find_in_page = (ev_window->priv->find_job &&
+ ev_job_find_has_results (EV_JOB_FIND (ev_window->priv->find_job)));
+
+ ev_window_set_action_sensitive (ev_window, "EditCopy",
+ has_pages &&
+ ev_view_get_has_selection (view));
+ ev_window_set_action_sensitive (ev_window, "EditFindNext",
+ has_pages && can_find_in_page);
+ ev_window_set_action_sensitive (ev_window, "EditFindPrevious",
+ has_pages && can_find_in_page);
+ ev_window_set_action_sensitive (ev_window, "F3",
+ has_pages && can_find_in_page);
+
+ presentation_mode = EV_WINDOW_IS_PRESENTATION (ev_window);
+
+ ev_window_set_action_sensitive (ev_window, "ViewZoomIn",
+ has_pages &&
+ ev_view_can_zoom_in (view) &&
+ !presentation_mode);
+ ev_window_set_action_sensitive (ev_window, "ViewZoomOut",
+ has_pages &&
+ ev_view_can_zoom_out (view) &&
+ !presentation_mode);
+
+ /* Go menu */
+ if (has_pages) {
+ ev_window_set_action_sensitive (ev_window, "GoPreviousPage", page > 0);
+ ev_window_set_action_sensitive (ev_window, "GoNextPage", page < n_pages - 1);
+ ev_window_set_action_sensitive (ev_window, "GoFirstPage", page > 0);
+ ev_window_set_action_sensitive (ev_window, "GoLastPage", page < n_pages - 1);
+ } else {
+ ev_window_set_action_sensitive (ev_window, "GoFirstPage", FALSE);
+ ev_window_set_action_sensitive (ev_window, "GoPreviousPage", FALSE);
+ ev_window_set_action_sensitive (ev_window, "GoNextPage", FALSE);
+ ev_window_set_action_sensitive (ev_window, "GoLastPage", FALSE);
+ }
+
+ sizing_mode = ev_document_model_get_sizing_mode (ev_window->priv->model);
+ if (has_pages && sizing_mode != EV_SIZING_FIT_WIDTH && sizing_mode != EV_SIZING_BEST_FIT) {
+ GtkAction *action;
+ float zoom;
+ float real_zoom;
+
+ action = gtk_action_group_get_action (ev_window->priv->action_group,
+ ZOOM_CONTROL_ACTION);
+
+ real_zoom = ev_document_model_get_scale (ev_window->priv->model);
+ real_zoom *= 72.0 / get_screen_dpi (ev_window);
+ zoom = ephy_zoom_get_nearest_zoom_level (real_zoom);
+
+ ephy_zoom_action_set_zoom_level (EPHY_ZOOM_ACTION (action), zoom);
+ }
+}
+
+static void
+ev_window_set_view_accels_sensitivity (EvWindow *window, gboolean sensitive)
+{
+ gboolean can_find;
+
+ can_find = window->priv->document &&
+ EV_IS_DOCUMENT_FIND (window->priv->document);
+
+ if (window->priv->action_group) {
+ ev_window_set_action_sensitive (window, "PageDown", sensitive);
+ ev_window_set_action_sensitive (window, "PageUp", sensitive);
+ ev_window_set_action_sensitive (window, "Space", sensitive);
+ ev_window_set_action_sensitive (window, "ShiftSpace", sensitive);
+ ev_window_set_action_sensitive (window, "BackSpace", sensitive);
+ ev_window_set_action_sensitive (window, "ShiftBackSpace", sensitive);
+ ev_window_set_action_sensitive (window, "Return", sensitive);
+ ev_window_set_action_sensitive (window, "ShiftReturn", sensitive);
+ ev_window_set_action_sensitive (window, "Plus", sensitive);
+ ev_window_set_action_sensitive (window, "Minus", sensitive);
+ ev_window_set_action_sensitive (window, "KpPlus", sensitive);
+ ev_window_set_action_sensitive (window, "KpMinus", sensitive);
+ ev_window_set_action_sensitive (window, "Equal", sensitive);
+ ev_window_set_action_sensitive (window, "p", sensitive);
+ ev_window_set_action_sensitive (window, "n", sensitive);
+
+ ev_window_set_action_sensitive (window, "Slash", sensitive && can_find);
+ }
+}
+
+static void
+set_widget_visibility (GtkWidget *widget, gboolean visible)
+{
+ g_assert (GTK_IS_WIDGET (widget));
+
+ if (visible)
+ gtk_widget_show (widget);
+ else
+ gtk_widget_hide (widget);
+}
+
+static void
+update_chrome_visibility (EvWindow *window)
+{
+ EvWindowPrivate *priv = window->priv;
+ gboolean menubar, toolbar, findbar, fullscreen_toolbar, sidebar;
+ gboolean fullscreen_mode, presentation, fullscreen;
+
+ presentation = EV_WINDOW_IS_PRESENTATION (window);
+ fullscreen = ev_document_model_get_fullscreen (priv->model);
+ fullscreen_mode = fullscreen || presentation;
+
+ menubar = (priv->chrome & EV_CHROME_MENUBAR) != 0 && !fullscreen_mode;
+ toolbar = ((priv->chrome & EV_CHROME_TOOLBAR) != 0 ||
+ (priv->chrome & EV_CHROME_RAISE_TOOLBAR) != 0) && !fullscreen_mode;
+ fullscreen_toolbar = ((priv->chrome & EV_CHROME_FULLSCREEN_TOOLBAR) != 0 ||
+ (priv->chrome & EV_CHROME_RAISE_TOOLBAR) != 0) && fullscreen;
+ findbar = (priv->chrome & EV_CHROME_FINDBAR) != 0;
+ sidebar = (priv->chrome & EV_CHROME_SIDEBAR) != 0 && priv->document && !presentation;
+
+ set_widget_visibility (priv->menubar, menubar);
+ set_widget_visibility (priv->toolbar, toolbar);
+ set_widget_visibility (priv->find_bar, findbar);
+ set_widget_visibility (priv->sidebar, sidebar);
+
+ ev_window_set_action_sensitive (window, "EditToolbar", toolbar);
+
+ if (priv->fullscreen_toolbar != NULL) {
+ set_widget_visibility (priv->fullscreen_toolbar, fullscreen_toolbar);
+ }
+}
+
+static void
+update_chrome_flag (EvWindow *window, EvChrome flag, gboolean active)
+{
+ EvWindowPrivate *priv = window->priv;
+
+ if (active) {
+ priv->chrome |= flag;
+ } else {
+ priv->chrome &= ~flag;
+ }
+}
+
+static void
+update_sizing_buttons (EvWindow *window)
+{
+ GtkActionGroup *action_group = window->priv->action_group;
+ GtkAction *action;
+ gboolean best_fit, page_width;
+
+ switch (ev_document_model_get_sizing_mode (window->priv->model)) {
+ case EV_SIZING_BEST_FIT:
+ best_fit = TRUE;
+ page_width = FALSE;
+ break;
+ case EV_SIZING_FIT_WIDTH:
+ best_fit = FALSE;
+ page_width = TRUE;
+ break;
+ default:
+ best_fit = page_width = FALSE;
+ break;
+ }
+
+ action = gtk_action_group_get_action (action_group, "ViewBestFit");
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_best_fit), window);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), best_fit);
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_best_fit), window);
+
+ action = gtk_action_group_get_action (action_group, "ViewPageWidth");
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_page_width), window);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), page_width);
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_page_width), window);
+
+ action = gtk_action_group_get_action (window->priv->action_group,
+ ZOOM_CONTROL_ACTION);
+ if (best_fit) {
+ ephy_zoom_action_set_zoom_level (EPHY_ZOOM_ACTION (action),
+ EPHY_ZOOM_BEST_FIT);
+ } else if (page_width) {
+ ephy_zoom_action_set_zoom_level (EPHY_ZOOM_ACTION (action),
+ EPHY_ZOOM_FIT_WIDTH);
+ }
+}
+
+static void
+update_chrome_actions (EvWindow *window)
+{
+ EvWindowPrivate *priv = window->priv;
+ GtkActionGroup *action_group = priv->action_group;
+ GtkAction *action;
+
+ action= gtk_action_group_get_action (action_group, "ViewToolbar");
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (ev_window_view_toolbar_cb), window);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ (priv->chrome & EV_CHROME_TOOLBAR) != 0);
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (ev_window_view_toolbar_cb), window);
+}
+
+/**
+ * ev_window_is_empty:
+ * @ev_window: The instance of the #EvWindow.
+ *
+ * It does look if there is any document loaded or if there is any job to load
+ * a document.
+ *
+ * Returns: %TRUE if there isn't any document loaded or any any documente to be
+ * loaded, %FALSE in other case.
+ */
+gboolean
+ev_window_is_empty (const EvWindow *ev_window)
+{
+ g_return_val_if_fail (EV_IS_WINDOW (ev_window), FALSE);
+
+ return (ev_window->priv->document == NULL) &&
+ (ev_window->priv->load_job == NULL);
+}
+
+static void
+ev_window_set_message_area (EvWindow *window,
+ GtkWidget *area)
+{
+ if (window->priv->message_area == area)
+ return;
+
+ if (window->priv->message_area)
+ gtk_widget_destroy (window->priv->message_area);
+ window->priv->message_area = area;
+
+ if (!area)
+ return;
+
+ gtk_box_pack_start (GTK_BOX (window->priv->view_box),
+ window->priv->message_area,
+ FALSE, FALSE, 0);
+ gtk_box_reorder_child (GTK_BOX (window->priv->view_box),
+ window->priv->message_area, 0);
+ g_object_add_weak_pointer (G_OBJECT (window->priv->message_area),
+ (gpointer) &(window->priv->message_area));
+}
+
+static void
+ev_window_message_area_response_cb (EvMessageArea *area,
+ gint response_id,
+ EvWindow *window)
+{
+ ev_window_set_message_area (window, NULL);
+}
+
+static void
+ev_window_error_message (EvWindow *window,
+ GError *error,
+ const gchar *format,
+ ...)
+{
+ GtkWidget *area;
+ va_list args;
+ gchar *msg = NULL;
+
+ if (window->priv->message_area)
+ return;
+
+ va_start (args, format);
+ msg = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ area = ev_message_area_new (GTK_MESSAGE_ERROR,
+ msg,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ NULL);
+ g_free (msg);
+
+ if (error)
+ ev_message_area_set_secondary_text (EV_MESSAGE_AREA (area), error->message);
+ g_signal_connect (area, "response",
+ G_CALLBACK (ev_window_message_area_response_cb),
+ window);
+ gtk_widget_show (area);
+ ev_window_set_message_area (window, area);
+}
+
+static void
+ev_window_warning_message (EvWindow *window,
+ const gchar *format,
+ ...)
+{
+ GtkWidget *area;
+ va_list args;
+ gchar *msg = NULL;
+
+ if (window->priv->message_area)
+ return;
+
+ va_start (args, format);
+ msg = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ area = ev_message_area_new (GTK_MESSAGE_WARNING,
+ msg,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ NULL);
+ g_free (msg);
+
+ g_signal_connect (area, "response",
+ G_CALLBACK (ev_window_message_area_response_cb),
+ window);
+ gtk_widget_show (area);
+ ev_window_set_message_area (window, area);
+}
+
+typedef struct _FindTask {
+ const gchar *page_label;
+ gchar *chapter;
+} FindTask;
+
+static gboolean
+ev_window_find_chapter (GtkTreeModel *tree_model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ FindTask *task = (FindTask *)data;
+ gchar *page_string;
+
+ gtk_tree_model_get (tree_model, iter,
+ EV_DOCUMENT_LINKS_COLUMN_PAGE_LABEL, &page_string,
+ -1);
+
+ if (!page_string)
+ return FALSE;
+
+ if (!strcmp (page_string, task->page_label)) {
+ gtk_tree_model_get (tree_model, iter,
+ EV_DOCUMENT_LINKS_COLUMN_MARKUP, &task->chapter,
+ -1);
+ g_free (page_string);
+ return TRUE;
+ }
+
+ g_free (page_string);
+ return FALSE;
+}
+
+static void
+ev_window_add_history (EvWindow *window, gint page, EvLink *link)
+{
+ gchar *page_label = NULL;
+ gchar *link_title;
+ FindTask find_task;
+ EvLink *real_link;
+ EvLinkAction *action;
+ EvLinkDest *dest;
+
+ if (window->priv->history == NULL)
+ return;
+
+ if (!EV_IS_DOCUMENT_LINKS (window->priv->document))
+ return;
+
+ if (link) {
+ action = g_object_ref (ev_link_get_action (link));
+ dest = ev_link_action_get_dest (action);
+ page_label = ev_document_links_get_dest_page_label (EV_DOCUMENT_LINKS (window->priv->document), dest);
+ } else {
+ dest = ev_link_dest_new_page (page);
+ action = ev_link_action_new_dest (dest);
+ page_label = ev_document_get_page_label (window->priv->document, page);
+ }
+
+ if (!page_label)
+ return;
+
+ find_task.page_label = page_label;
+ find_task.chapter = NULL;
+
+ if (ev_document_links_has_document_links (EV_DOCUMENT_LINKS (window->priv->document))) {
+ GtkTreeModel *model;
+
+ g_object_get (G_OBJECT (window->priv->sidebar_links), "model", &model, NULL);
+
+ if (model) {
+ gtk_tree_model_foreach (model,
+ ev_window_find_chapter,
+ &find_task);
+
+ g_object_unref (model);
+ }
+ }
+
+ if (find_task.chapter)
+ link_title = g_strdup_printf (_("Page %s ā€” %s"), page_label, find_task.chapter);
+ else
+ link_title = g_strdup_printf (_("Page %s"), page_label);
+
+ real_link = ev_link_new (link_title, action);
+
+ ev_history_add_link (window->priv->history, real_link);
+
+ g_free (find_task.chapter);
+ g_free (link_title);
+ g_free (page_label);
+ g_object_unref (real_link);
+}
+
+static void
+view_handle_link_cb (EvView *view, EvLink *link, EvWindow *window)
+{
+ int current_page = ev_document_model_get_page (window->priv->model);
+
+ ev_window_add_history (window, 0, link);
+ ev_window_add_history (window, current_page, NULL);
+}
+
+static void
+view_selection_changed_cb (EvView *view,
+ EvWindow *window)
+{
+ ev_window_set_action_sensitive (window, "EditCopy",
+ ev_view_get_has_selection (view));
+}
+
+static void
+ev_window_page_changed_cb (EvWindow *ev_window,
+ gint old_page,
+ gint new_page,
+ EvDocumentModel *model)
+{
+ ev_window_update_actions (ev_window);
+
+ ev_window_update_find_status_message (ev_window);
+
+ if (abs (new_page - old_page) > 1) {
+ ev_window_add_history (ev_window, new_page, NULL);
+ ev_window_add_history (ev_window, old_page, NULL);
+ }
+
+ if (ev_window->priv->metadata && !ev_window_is_empty (ev_window))
+ ev_metadata_set_int (ev_window->priv->metadata, "page", new_page);
+}
+
+static const gchar *
+ev_window_sidebar_get_current_page_id (EvWindow *ev_window)
+{
+ GtkWidget *current_page;
+ const gchar *id;
+
+ g_object_get (ev_window->priv->sidebar,
+ "current_page", &current_page,
+ NULL);
+
+ if (current_page == ev_window->priv->sidebar_links) {
+ id = LINKS_SIDEBAR_ID;
+ } else if (current_page == ev_window->priv->sidebar_thumbs) {
+ id = THUMBNAILS_SIDEBAR_ID;
+ } else if (current_page == ev_window->priv->sidebar_attachments) {
+ id = ATTACHMENTS_SIDEBAR_ID;
+ } else if (current_page == ev_window->priv->sidebar_layers) {
+ id = LAYERS_SIDEBAR_ID;
+ } else if (current_page == ev_window->priv->sidebar_annots) {
+ id = ANNOTS_SIDEBAR_ID;
+ } else {
+ g_assert_not_reached();
+ }
+
+ g_object_unref (current_page);
+
+ return id;
+}
+
+static void
+ev_window_sidebar_set_current_page (EvWindow *window,
+ const gchar *page_id)
+{
+ EvDocument *document = window->priv->document;
+ EvSidebar *sidebar = EV_SIDEBAR (window->priv->sidebar);
+ GtkWidget *links = window->priv->sidebar_links;
+ GtkWidget *thumbs = window->priv->sidebar_thumbs;
+ GtkWidget *attachments = window->priv->sidebar_attachments;
+ GtkWidget *annots = window->priv->sidebar_annots;
+ GtkWidget *layers = window->priv->sidebar_layers;
+
+ if (strcmp (page_id, LINKS_SIDEBAR_ID) == 0 &&
+ ev_sidebar_page_support_document (EV_SIDEBAR_PAGE (links), document)) {
+ ev_sidebar_set_page (sidebar, links);
+ } else if (strcmp (page_id, THUMBNAILS_SIDEBAR_ID) == 0 &&
+ ev_sidebar_page_support_document (EV_SIDEBAR_PAGE (thumbs), document)) {
+ ev_sidebar_set_page (sidebar, thumbs);
+ } else if (strcmp (page_id, ATTACHMENTS_SIDEBAR_ID) == 0 &&
+ ev_sidebar_page_support_document (EV_SIDEBAR_PAGE (attachments), document)) {
+ ev_sidebar_set_page (sidebar, attachments);
+ } else if (strcmp (page_id, LAYERS_SIDEBAR_ID) == 0 &&
+ ev_sidebar_page_support_document (EV_SIDEBAR_PAGE (layers), document)) {
+ ev_sidebar_set_page (sidebar, layers);
+ } else if (strcmp (page_id, ANNOTS_SIDEBAR_ID) == 0 &&
+ ev_sidebar_page_support_document (EV_SIDEBAR_PAGE (annots), document)) {
+ ev_sidebar_set_page (sidebar, annots);
+ }
+}
+
+static void
+update_document_mode (EvWindow *window, EvDocumentMode mode)
+{
+ if (mode == EV_DOCUMENT_MODE_PRESENTATION) {
+ ev_window_run_presentation (window);
+ }
+ else if (mode == EV_DOCUMENT_MODE_FULL_SCREEN) {
+ ev_window_run_fullscreen (window);
+ }
+}
+
+static void
+setup_chrome_from_metadata (EvWindow *window)
+{
+ EvChrome chrome = EV_CHROME_NORMAL;
+ gboolean show_toolbar = TRUE;
+
+ if (ev_window_is_empty (window)) {
+ window->priv->chrome = chrome;
+
+ return;
+ }
+
+ if (!window->priv->metadata) {
+ show_toolbar = g_settings_get_boolean (window->priv->default_settings, "show-toolbar");
+ } else if (!ev_metadata_get_boolean (window->priv->metadata, "show_toolbar", &show_toolbar)) {
+ if (window->priv->is_new_doc)
+ show_toolbar = g_settings_get_boolean (window->priv->default_settings, "show-toolbar");
+ }
+
+ if (!show_toolbar)
+ chrome &= ~EV_CHROME_TOOLBAR;
+
+ window->priv->chrome = chrome;
+}
+
+static void
+setup_sidebar_from_metadata (EvWindow *window)
+{
+ EvDocument *document = window->priv->document;
+ GSettings *settings = window->priv->default_settings;
+ gchar *page_id;
+ gint sidebar_size;
+ gboolean sidebar_visibility = TRUE;
+
+ if (ev_window_is_empty (window))
+ return;
+
+ if (!window->priv->metadata) {
+ sidebar_visibility = g_settings_get_boolean (settings, "show-sidebar");
+ } else if (!ev_metadata_get_boolean (window->priv->metadata, "sidebar_visibility", &sidebar_visibility)) {
+ if (window->priv->is_new_doc)
+ sidebar_visibility = g_settings_get_boolean (settings, "show-sidebar");
+ }
+
+ update_chrome_flag (window, EV_CHROME_SIDEBAR, sidebar_visibility);
+ update_chrome_visibility (window);
+
+ if (!window->priv->metadata) {
+ /* Set default values */
+ gtk_paned_set_position (GTK_PANED (window->priv->hpaned),
+ g_settings_get_int (settings, "sidebar-size"));
+ if (document) {
+ page_id = g_settings_get_string (settings, "sidebar-page");
+ ev_window_sidebar_set_current_page (window, page_id);
+ g_free (page_id);
+ }
+
+ return;
+ }
+
+ if (ev_metadata_get_int (window->priv->metadata, "sidebar_size", &sidebar_size)) {
+ gtk_paned_set_position (GTK_PANED (window->priv->hpaned), sidebar_size);
+ } else if (window->priv->is_new_doc) {
+ gtk_paned_set_position (GTK_PANED (window->priv->hpaned),
+ g_settings_get_int (settings, "sidebar-size"));
+ }
+
+ if (!document)
+ return;
+
+ if (ev_metadata_get_string (window->priv->metadata, "sidebar_page", &page_id)) {
+ ev_window_sidebar_set_current_page (window, page_id);
+ } else if (window->priv->is_new_doc) {
+ page_id = g_settings_get_string (settings, "sidebar-page");
+ ev_window_sidebar_set_current_page (window, page_id);
+ g_free (page_id);
+ }
+}
+
+static void
+setup_model_from_metadata (EvWindow *window)
+{
+ GSettings *settings = window->priv->default_settings;
+ gint page;
+ gchar *sizing_mode;
+ gdouble zoom;
+ gint rotation;
+ gboolean inverted_colors = FALSE;
+ gboolean continuous = FALSE;
+ gboolean dual_page = FALSE;
+ gboolean fullscreen = FALSE;
+
+ if (!window->priv->metadata) {
+ /* Set default values */
+ ev_document_model_set_sizing_mode (window->priv->model,
+ g_settings_get_enum (settings, "sizing-mode"));
+ ev_document_model_set_inverted_colors (window->priv->model,
+ g_settings_get_boolean (settings, "inverted-colors"));
+ ev_document_model_set_continuous (window->priv->model,
+ g_settings_get_boolean (settings, "continuous"));
+ ev_document_model_set_dual_page (window->priv->model,
+ g_settings_get_boolean (settings, "dual-page"));
+ fullscreen = g_settings_get_boolean (settings, "fullscreen");
+ if (fullscreen)
+ ev_window_run_fullscreen (window);
+
+ return;
+ }
+
+ /* Current page */
+ if (!window->priv->dest &&
+ ev_metadata_get_int (window->priv->metadata, "page", &page)) {
+ ev_document_model_set_page (window->priv->model, page);
+ }
+
+ /* Sizing mode */
+ if (ev_metadata_get_string (window->priv->metadata, "sizing_mode", &sizing_mode)) {
+ GEnumValue *enum_value;
+
+ enum_value = g_enum_get_value_by_nick
+ (g_type_class_peek (EV_TYPE_SIZING_MODE), sizing_mode);
+ ev_document_model_set_sizing_mode (window->priv->model, enum_value->value);
+ } else if (window->priv->is_new_doc) {
+ ev_document_model_set_sizing_mode (window->priv->model,
+ g_settings_get_enum (settings, "sizing-mode"));
+ }
+
+ /* Zoom */
+ if (ev_document_model_get_sizing_mode (window->priv->model) == EV_SIZING_FREE &&
+ ev_metadata_get_double (window->priv->metadata, "zoom", &zoom)) {
+ zoom *= get_screen_dpi (window) / 72.0;
+ ev_document_model_set_scale (window->priv->model, zoom);
+ }
+
+ /* Rotation */
+ if (ev_metadata_get_int (window->priv->metadata, "rotation", &rotation)) {
+ switch (rotation) {
+ case 90:
+ rotation = 90;
+ break;
+ case 180:
+ rotation = 180;
+ break;
+ case 270:
+ rotation = 270;
+ break;
+ default:
+ rotation = 0;
+ break;
+ }
+ ev_document_model_set_rotation (window->priv->model, rotation);
+ }
+
+ /* Inverted Colors */
+ if (ev_metadata_get_boolean (window->priv->metadata, "inverted-colors", &inverted_colors)) {
+ ev_document_model_set_inverted_colors (window->priv->model, inverted_colors);
+ } else if (window->priv->is_new_doc) {
+ ev_document_model_set_inverted_colors (window->priv->model,
+ g_settings_get_boolean (settings, "inverted-colors"));
+ }
+
+ /* Continuous */
+ if (ev_metadata_get_boolean (window->priv->metadata, "continuous", &continuous)) {
+ ev_document_model_set_continuous (window->priv->model, continuous);
+ } else if (window->priv->is_new_doc) {
+ ev_document_model_set_continuous (window->priv->model,
+ g_settings_get_boolean (settings, "continuous"));
+ }
+
+ /* Dual page */
+ if (ev_metadata_get_boolean (window->priv->metadata, "dual-page", &dual_page)) {
+ ev_document_model_set_dual_page (window->priv->model, dual_page);
+ } else if (window->priv->is_new_doc) {
+ ev_document_model_set_dual_page (window->priv->model,
+ g_settings_get_boolean (settings, "dual-page"));
+ }
+
+ /* Fullscreen */
+ if (!ev_metadata_get_boolean (window->priv->metadata, "fullscreen", &fullscreen)) {
+ if (window->priv->is_new_doc)
+ fullscreen = g_settings_get_boolean (settings, "fullscreen");
+ }
+
+ if (fullscreen)
+ ev_window_run_fullscreen (window);
+}
+
+static void
+setup_document_from_metadata (EvWindow *window)
+{
+ gint page, n_pages;
+ gint width;
+ gint height;
+ gdouble width_ratio;
+ gdouble height_ratio;
+
+ setup_sidebar_from_metadata (window);
+
+ if (window->priv->metadata) {
+ /* Make sure to not open a document on the last page,
+ * since closing it on the last page most likely means the
+ * user was finished reading the document. In that case, reopening should
+ * show the first page. */
+ page = ev_document_model_get_page (window->priv->model);
+ n_pages = ev_document_get_n_pages (window->priv->document);
+ if (page == n_pages - 1)
+ ev_document_model_set_page (window->priv->model, 0);
+
+ if (ev_metadata_get_int (window->priv->metadata, "window_width", &width) &&
+ ev_metadata_get_int (window->priv->metadata, "window_height", &height))
+ return; /* size was already set in setup_size_from_metadata */
+ }
+
+ g_settings_get (window->priv->default_settings, "window-ratio", "(dd)", &width_ratio, &height_ratio);
+ if (width_ratio > 0. && height_ratio > 0.) {
+ gdouble document_width;
+ gdouble document_height;
+ GdkScreen *screen;
+ gint request_width;
+ gint request_height;
+
+ ev_document_get_max_page_size (window->priv->document,
+ &document_width, &document_height);
+
+ request_width = (gint)(width_ratio * document_width + 0.5);
+ request_height = (gint)(height_ratio * document_height + 0.5);
+
+ screen = gtk_window_get_screen (GTK_WINDOW (window));
+ if (screen) {
+ request_width = MIN (request_width, gdk_screen_get_width (screen));
+ request_height = MIN (request_height, gdk_screen_get_height (screen));
+ }
+
+ if (request_width > 0 && request_height > 0) {
+ gtk_window_resize (GTK_WINDOW (window),
+ request_width,
+ request_height);
+ }
+ }
+}
+
+static void
+setup_size_from_metadata (EvWindow *window)
+{
+ gint width;
+ gint height;
+ gboolean maximized;
+ gint x;
+ gint y;
+
+ if (!window->priv->metadata)
+ return;
+
+ if (ev_metadata_get_boolean (window->priv->metadata, "window_maximized", &maximized)) {
+ if (maximized) {
+ gtk_window_maximize (GTK_WINDOW (window));
+ return;
+ } else {
+ gtk_window_unmaximize (GTK_WINDOW (window));
+ }
+ }
+
+ if (ev_metadata_get_int (window->priv->metadata, "window_x", &x) &&
+ ev_metadata_get_int (window->priv->metadata, "window_y", &y)) {
+ gtk_window_move (GTK_WINDOW (window), x, y);
+ }
+
+ if (ev_metadata_get_int (window->priv->metadata, "window_width", &width) &&
+ ev_metadata_get_int (window->priv->metadata, "window_height", &height)) {
+ gtk_window_resize (GTK_WINDOW (window), width, height);
+ }
+}
+
+static void
+setup_view_from_metadata (EvWindow *window)
+{
+ gboolean presentation;
+
+ if (!window->priv->metadata)
+ return;
+
+ /* Presentation */
+ if (ev_metadata_get_boolean (window->priv->metadata, "presentation", &presentation)) {
+ if (presentation) {
+ ev_window_run_presentation (window);
+ }
+ }
+}
+
+static void
+ev_window_clear_thumbnail_job (EvWindow *ev_window)
+{
+ if (ev_window->priv->thumbnail_job != NULL) {
+ if (!ev_job_is_finished (ev_window->priv->thumbnail_job))
+ ev_job_cancel (ev_window->priv->thumbnail_job);
+
+ g_signal_handlers_disconnect_by_func (ev_window->priv->thumbnail_job,
+ ev_window_set_icon_from_thumbnail,
+ ev_window);
+ g_object_unref (ev_window->priv->thumbnail_job);
+ ev_window->priv->thumbnail_job = NULL;
+ }
+}
+
+static void
+ev_window_set_icon_from_thumbnail (EvJobThumbnail *job,
+ EvWindow *ev_window)
+{
+ if (job->thumbnail) {
+ if (ev_document_model_get_inverted_colors (ev_window->priv->model))
+ ev_document_misc_invert_pixbuf (job->thumbnail);
+ gtk_window_set_icon (GTK_WINDOW (ev_window),
+ job->thumbnail);
+ }
+
+ ev_window_clear_thumbnail_job (ev_window);
+}
+
+static void
+ev_window_refresh_window_thumbnail (EvWindow *ev_window)
+{
+ gdouble page_width;
+ gdouble scale;
+ gint rotation;
+ EvDocument *document = ev_window->priv->document;
+
+ if (!EV_IS_DOCUMENT_THUMBNAILS (document) ||
+ ev_document_get_n_pages (document) <= 0 ||
+ !ev_document_check_dimensions (document)) {
+ return;
+ }
+
+ ev_window_clear_thumbnail_job (ev_window);
+
+ ev_document_get_page_size (document, 0, &page_width, NULL);
+ scale = 128. / page_width;
+ rotation = ev_document_model_get_rotation (ev_window->priv->model);
+
+ ev_window->priv->thumbnail_job = ev_job_thumbnail_new (document, 0, rotation, scale);
+ g_signal_connect (ev_window->priv->thumbnail_job, "finished",
+ G_CALLBACK (ev_window_set_icon_from_thumbnail),
+ ev_window);
+ ev_job_scheduler_push_job (ev_window->priv->thumbnail_job, EV_JOB_PRIORITY_NONE);
+}
+
+static void
+override_restrictions_changed (GSettings *settings,
+ gchar *key,
+ EvWindow *ev_window)
+{
+ ev_window_setup_action_sensitivity (ev_window);
+}
+
+#ifdef WITH_MATECONF
+static void
+lockdown_changed (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ EvWindow *ev_window)
+{
+ ev_window_setup_action_sensitivity (ev_window);
+}
+#endif /* WITH_MATECONF */
+
+static gboolean
+ev_window_setup_document (EvWindow *ev_window)
+{
+ const EvDocumentInfo *info;
+ EvDocument *document = ev_window->priv->document;
+ GtkAction *action;
+
+ ev_window->priv->setup_document_idle = 0;
+
+ ev_window_refresh_window_thumbnail (ev_window);
+
+ ev_window_set_page_mode (ev_window, PAGE_MODE_DOCUMENT);
+ ev_window_title_set_document (ev_window->priv->title, document);
+ ev_window_title_set_uri (ev_window->priv->title, ev_window->priv->uri);
+
+ if (!ev_window->priv->settings) {
+ ev_window->priv->settings = g_settings_new (GS_SCHEMA_NAME);
+ g_signal_connect (ev_window->priv->settings,
+ "changed::"GS_OVERRIDE_RESTRICTIONS,
+ G_CALLBACK (override_restrictions_changed),
+ ev_window);
+ }
+
+#ifdef WITH_MATECONF
+ if (!ev_window->priv->mateconf_client)
+ ev_window->priv->mateconf_client = mateconf_client_get_default ();
+ mateconf_client_add_dir (ev_window->priv->mateconf_client,
+ MATECONF_LOCKDOWN_DIR,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+ mateconf_client_notify_add (ev_window->priv->mateconf_client,
+ MATECONF_LOCKDOWN_DIR,
+ (MateConfClientNotifyFunc)lockdown_changed,
+ ev_window, NULL, NULL);
+#endif /* WITH_MATECONF */
+
+ ev_window_setup_action_sensitivity (ev_window);
+
+ if (ev_window->priv->history)
+ g_object_unref (ev_window->priv->history);
+ ev_window->priv->history = ev_history_new ();
+ action = gtk_action_group_get_action (ev_window->priv->action_group, NAVIGATION_ACTION);
+ ev_navigation_action_set_history (EV_NAVIGATION_ACTION (action), ev_window->priv->history);
+
+ if (ev_window->priv->properties) {
+ ev_properties_dialog_set_document (EV_PROPERTIES_DIALOG (ev_window->priv->properties),
+ ev_window->priv->uri,
+ ev_window->priv->document);
+ }
+
+ info = ev_document_get_info (document);
+ update_document_mode (ev_window, info->mode);
+
+ if (EV_WINDOW_IS_PRESENTATION (ev_window))
+ gtk_widget_grab_focus (ev_window->priv->presentation_view);
+ else
+ gtk_widget_grab_focus (ev_window->priv->view);
+
+ return FALSE;
+}
+
+static void
+ev_window_set_document (EvWindow *ev_window, EvDocument *document)
+{
+ if (ev_window->priv->document == document)
+ return;
+
+ if (ev_window->priv->document)
+ g_object_unref (ev_window->priv->document);
+ ev_window->priv->document = g_object_ref (document);
+
+ ev_window_update_max_min_scale (ev_window);
+
+ ev_window_set_message_area (ev_window, NULL);
+
+ if (ev_document_get_n_pages (document) <= 0) {
+ ev_window_warning_message (ev_window, "%s",
+ _("The document contains no pages"));
+ } else if (!ev_document_check_dimensions (document)) {
+ ev_window_warning_message (ev_window, "%s",
+ _("The document contains only empty pages"));
+ }
+
+ if (EV_WINDOW_IS_PRESENTATION (ev_window)) {
+ gtk_widget_destroy (ev_window->priv->presentation_view);
+ ev_window->priv->presentation_view = NULL;
+ ev_window_run_presentation (ev_window);
+ }
+
+ if (ev_window->priv->setup_document_idle > 0)
+ g_source_remove (ev_window->priv->setup_document_idle);
+
+ ev_window->priv->setup_document_idle = g_idle_add ((GSourceFunc)ev_window_setup_document, ev_window);
+}
+
+static void
+ev_window_document_changed (EvWindow *ev_window,
+ gpointer user_data)
+{
+ ev_window_reload_document (ev_window, NULL);
+}
+
+static void
+ev_window_password_view_unlock (EvWindow *ev_window)
+{
+ const gchar *password;
+
+ g_assert (ev_window->priv->load_job);
+
+ password = ev_password_view_get_password (EV_PASSWORD_VIEW (ev_window->priv->password_view));
+ ev_job_load_set_password (EV_JOB_LOAD (ev_window->priv->load_job), password);
+ ev_job_scheduler_push_job (ev_window->priv->load_job, EV_JOB_PRIORITY_NONE);
+}
+
+static void
+ev_window_clear_load_job (EvWindow *ev_window)
+{
+ if (ev_window->priv->load_job != NULL) {
+ if (!ev_job_is_finished (ev_window->priv->load_job))
+ ev_job_cancel (ev_window->priv->load_job);
+
+ g_signal_handlers_disconnect_by_func (ev_window->priv->load_job, ev_window_load_job_cb, ev_window);
+ g_object_unref (ev_window->priv->load_job);
+ ev_window->priv->load_job = NULL;
+ }
+}
+
+static void
+ev_window_clear_reload_job (EvWindow *ev_window)
+{
+ if (ev_window->priv->reload_job != NULL) {
+ if (!ev_job_is_finished (ev_window->priv->reload_job))
+ ev_job_cancel (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) {
+ ev_tmp_uri_unlink (ev_window->priv->local_uri);
+ g_free (ev_window->priv->local_uri);
+ ev_window->priv->local_uri = NULL;
+ }
+}
+
+static void
+ev_window_handle_link (EvWindow *ev_window,
+ EvLinkDest *dest)
+{
+ if (dest) {
+ EvLink *link;
+ EvLinkAction *link_action;
+
+ link_action = ev_link_action_new_dest (dest);
+ link = ev_link_new (NULL, link_action);
+ ev_view_handle_link (EV_VIEW (ev_window->priv->view), link);
+ g_object_unref (link);
+ }
+}
+
+/* This callback will executed when load job will be finished.
+ *
+ * Since the flow of the error dialog is very confusing, we assume that both
+ * document and uri will go away after this function is called, and thus we need
+ * to ref/dup them. Additionally, it needs to clear
+ * ev_window->priv->password_{uri,document}, and thus people who call this
+ * function should _not_ necessarily expect those to exist after being
+ * called. */
+static void
+ev_window_load_job_cb (EvJob *job,
+ gpointer data)
+{
+ EvWindow *ev_window = EV_WINDOW (data);
+ EvDocument *document = EV_JOB (job)->document;
+ EvJobLoad *job_load = EV_JOB_LOAD (job);
+
+ g_assert (job_load->uri);
+
+ ev_view_set_loading (EV_VIEW (ev_window->priv->view), FALSE);
+
+ /* Success! */
+ if (!ev_job_is_failed (job)) {
+ ev_document_model_set_document (ev_window->priv->model, document);
+
+#ifdef ENABLE_DBUS
+ ev_window_emit_doc_loaded (ev_window);
+#endif
+ setup_chrome_from_metadata (ev_window);
+ update_chrome_actions (ev_window);
+ setup_document_from_metadata (ev_window);
+ setup_view_from_metadata (ev_window);
+
+ ev_window_add_recent (ev_window, ev_window->priv->uri);
+
+ ev_window_title_set_type (ev_window->priv->title,
+ EV_WINDOW_TITLE_DOCUMENT);
+ if (job_load->password) {
+ GPasswordSave flags;
+
+ flags = ev_password_view_get_password_save_flags (
+ EV_PASSWORD_VIEW (ev_window->priv->password_view));
+ ev_keyring_save_password (ev_window->priv->uri,
+ job_load->password,
+ flags);
+ }
+
+ ev_window_handle_link (ev_window, ev_window->priv->dest);
+ /* Already unrefed by ev_link_action
+ * FIXME: link action should inc dest ref counting
+ * or not unref it at all
+ */
+ ev_window->priv->dest = NULL;
+
+ switch (ev_window->priv->window_mode) {
+ case EV_WINDOW_MODE_FULLSCREEN:
+ ev_window_run_fullscreen (ev_window);
+ break;
+ case EV_WINDOW_MODE_PRESENTATION:
+ ev_window_run_presentation (ev_window);
+ break;
+ default:
+ break;
+ }
+
+ if (ev_window->priv->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),
+ ev_window->priv->search_string);
+ }
+
+ g_free (ev_window->priv->search_string);
+ ev_window->priv->search_string = NULL;
+
+ /* Create a monitor for the document */
+ ev_window->priv->monitor = ev_file_monitor_new (ev_window->priv->uri);
+ g_signal_connect_swapped (ev_window->priv->monitor, "changed",
+ G_CALLBACK (ev_window_document_changed),
+ ev_window);
+
+ ev_window_clear_load_job (ev_window);
+ return;
+ }
+
+ if (g_error_matches (job->error, EV_DOCUMENT_ERROR, EV_DOCUMENT_ERROR_ENCRYPTED)) {
+ gchar *password;
+
+ setup_view_from_metadata (ev_window);
+
+ /* First look whether password is in keyring */
+ password = ev_keyring_lookup_password (ev_window->priv->uri);
+ if (password) {
+ if (job_load->password && strcmp (password, job_load->password) == 0) {
+ /* Password in kering is wrong */
+ ev_job_load_set_password (job_load, NULL);
+ /* FIXME: delete password from keyring? */
+ } else {
+ ev_job_load_set_password (job_load, password);
+ ev_job_scheduler_push_job (job, EV_JOB_PRIORITY_NONE);
+ g_free (password);
+ return;
+ }
+
+ g_free (password);
+ }
+
+ /* We need to ask the user for a password */
+ ev_window_title_set_uri (ev_window->priv->title,
+ ev_window->priv->uri);
+ ev_window_title_set_type (ev_window->priv->title,
+ EV_WINDOW_TITLE_PASSWORD);
+
+ ev_password_view_set_uri (EV_PASSWORD_VIEW (ev_window->priv->password_view),
+ job_load->uri);
+
+ ev_window_set_page_mode (ev_window, PAGE_MODE_PASSWORD);
+
+ ev_job_load_set_password (job_load, NULL);
+ ev_password_view_ask_password (EV_PASSWORD_VIEW (ev_window->priv->password_view));
+ } else {
+ ev_window_error_message (ev_window, job->error,
+ "%s", _("Unable to open document"));
+ ev_window_clear_load_job (ev_window);
+ }
+}
+
+static void
+ev_window_reload_job_cb (EvJob *job,
+ EvWindow *ev_window)
+{
+ GtkWidget *widget;
+
+ if (ev_job_is_failed (job)) {
+ ev_window_clear_reload_job (ev_window);
+ ev_window->priv->in_reload = FALSE;
+ if (ev_window->priv->dest) {
+ g_object_unref (ev_window->priv->dest);
+ ev_window->priv->dest = NULL;
+ }
+
+ return;
+ }
+
+ ev_document_model_set_document (ev_window->priv->model,
+ job->document);
+ if (ev_window->priv->dest) {
+ ev_window_handle_link (ev_window, ev_window->priv->dest);
+ /* Already unrefed by ev_link_action
+ * FIXME: link action should inc dest ref counting
+ * or not unref it at all
+ */
+ ev_window->priv->dest = NULL;
+ }
+
+ /* 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;
+}
+
+/**
+ * ev_window_get_uri:
+ * @ev_window: The instance of the #EvWindow.
+ *
+ * It returns the uri of the document showed in the #EvWindow.
+ *
+ * Returns: the uri of the document showed in the #EvWindow.
+ */
+const char *
+ev_window_get_uri (EvWindow *ev_window)
+{
+ return ev_window->priv->uri;
+}
+
+/**
+ * ev_window_close_dialogs:
+ * @ev_window: The window where dialogs will be closed.
+ *
+ * It looks for password, print and properties dialogs and closes them and
+ * frees them from memory. If there is any print job it does free it too.
+ */
+static void
+ev_window_close_dialogs (EvWindow *ev_window)
+{
+ if (ev_window->priv->print_dialog)
+ gtk_widget_destroy (ev_window->priv->print_dialog);
+ ev_window->priv->print_dialog = NULL;
+
+ if (ev_window->priv->properties)
+ gtk_widget_destroy (ev_window->priv->properties);
+ ev_window->priv->properties = NULL;
+}
+
+static void
+ev_window_clear_progress_idle (EvWindow *ev_window)
+{
+ if (ev_window->priv->progress_idle > 0)
+ g_source_remove (ev_window->priv->progress_idle);
+ ev_window->priv->progress_idle = 0;
+}
+
+static void
+reset_progress_idle (EvWindow *ev_window)
+{
+ ev_window->priv->progress_idle = 0;
+}
+
+static void
+ev_window_show_progress_message (EvWindow *ev_window,
+ guint interval,
+ GSourceFunc function)
+{
+ if (ev_window->priv->progress_idle > 0)
+ g_source_remove (ev_window->priv->progress_idle);
+ ev_window->priv->progress_idle =
+ g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+ interval, function,
+ ev_window,
+ (GDestroyNotify)reset_progress_idle);
+}
+
+static void
+ev_window_reset_progress_cancellable (EvWindow *ev_window)
+{
+ if (ev_window->priv->progress_cancellable)
+ g_cancellable_reset (ev_window->priv->progress_cancellable);
+ else
+ ev_window->priv->progress_cancellable = g_cancellable_new ();
+}
+
+static void
+ev_window_progress_response_cb (EvProgressMessageArea *area,
+ gint response,
+ EvWindow *ev_window)
+{
+ if (response == GTK_RESPONSE_CANCEL)
+ g_cancellable_cancel (ev_window->priv->progress_cancellable);
+ ev_window_set_message_area (ev_window, NULL);
+}
+
+static gboolean
+show_loading_progress (EvWindow *ev_window)
+{
+ GtkWidget *area;
+ gchar *text;
+ gchar *display_name;
+
+ if (ev_window->priv->message_area)
+ return FALSE;
+
+ text = g_uri_unescape_string (ev_window->priv->uri, NULL);
+ display_name = g_markup_escape_text (text, -1);
+ g_free (text);
+ text = g_strdup_printf (_("Loading document from ā€œ%sā€"),
+ display_name);
+
+ area = ev_progress_message_area_new (GTK_STOCK_OPEN,
+ text,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+ g_signal_connect (area, "response",
+ G_CALLBACK (ev_window_progress_response_cb),
+ ev_window);
+ gtk_widget_show (area);
+ ev_window_set_message_area (ev_window, area);
+
+ g_free (text);
+ g_free (display_name);
+
+ return FALSE;
+}
+
+static void
+ev_window_load_remote_failed (EvWindow *ev_window,
+ GError *error)
+{
+ ev_view_set_loading (EV_VIEW (ev_window->priv->view), FALSE);
+ ev_window->priv->in_reload = FALSE;
+ ev_window_error_message (ev_window, error,
+ "%s", _("Unable to open document"));
+ g_free (ev_window->priv->local_uri);
+ ev_window->priv->local_uri = NULL;
+ ev_window->priv->uri_mtime = 0;
+}
+
+static void
+set_uri_mtime (GFile *source,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GFileInfo *info;
+ GError *error = NULL;
+
+ info = g_file_query_info_finish (source, async_result, &error);
+
+ if (error) {
+ ev_window->priv->uri_mtime = 0;
+ g_error_free (error);
+ } else {
+ GTimeVal mtime;
+
+ g_file_info_get_modification_time (info, &mtime);
+ ev_window->priv->uri_mtime = mtime.tv_sec;
+ g_object_unref (info);
+ }
+
+ g_object_unref (source);
+}
+
+static void
+mount_volume_ready_cb (GFile *source,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GError *error = NULL;
+
+ g_file_mount_enclosing_volume_finish (source, async_result, &error);
+
+ if (error) {
+ ev_window_load_remote_failed (ev_window, error);
+ g_object_unref (source);
+ g_error_free (error);
+ } else {
+ /* Volume successfully mounted,
+ try opening the file again */
+ ev_window_load_file_remote (ev_window, source);
+ }
+}
+
+static void
+window_open_file_copy_ready_cb (GFile *source,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GError *error = NULL;
+
+ ev_window_clear_progress_idle (ev_window);
+ ev_window_set_message_area (ev_window, NULL);
+
+ g_file_copy_finish (source, async_result, &error);
+ if (!error) {
+ ev_job_scheduler_push_job (ev_window->priv->load_job, EV_JOB_PRIORITY_NONE);
+ g_file_query_info_async (source,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ 0, G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback)set_uri_mtime,
+ ev_window);
+ return;
+ }
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED)) {
+ GMountOperation *operation;
+
+ operation = gtk_mount_operation_new (GTK_WINDOW (ev_window));
+ g_file_mount_enclosing_volume (source,
+ G_MOUNT_MOUNT_NONE,
+ operation, NULL,
+ (GAsyncReadyCallback)mount_volume_ready_cb,
+ ev_window);
+ g_object_unref (operation);
+ } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ ev_window_clear_load_job (ev_window);
+ ev_window_clear_local_uri (ev_window);
+ g_free (ev_window->priv->uri);
+ ev_window->priv->uri = NULL;
+ g_object_unref (source);
+
+ ev_view_set_loading (EV_VIEW (ev_window->priv->view), FALSE);
+ } else {
+ ev_window_load_remote_failed (ev_window, error);
+ g_object_unref (source);
+ }
+
+ g_error_free (error);
+}
+
+static void
+window_open_file_copy_progress_cb (goffset n_bytes,
+ goffset total_bytes,
+ EvWindow *ev_window)
+{
+ gchar *status;
+ gdouble fraction;
+
+ if (!ev_window->priv->message_area)
+ return;
+
+ if (total_bytes <= 0)
+ return;
+
+ fraction = n_bytes / (gdouble)total_bytes;
+ status = g_strdup_printf (_("Downloading document (%d%%)"),
+ (gint)(fraction * 100));
+
+ ev_progress_message_area_set_status (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ status);
+ ev_progress_message_area_set_fraction (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ fraction);
+
+ g_free (status);
+}
+
+static void
+ev_window_load_file_remote (EvWindow *ev_window,
+ GFile *source_file)
+{
+ GFile *target_file;
+
+ if (!ev_window->priv->local_uri) {
+ char *base_name, *template;
+ GFile *tmp_file;
+ GError *err = NULL;
+
+ /* We'd like to keep extension of source uri since
+ * it helps to resolve some mime types, say cbz.
+ */
+ base_name = g_file_get_basename (source_file);
+ template = g_strdup_printf ("document.XXXXXX-%s", base_name);
+ g_free (base_name);
+
+ tmp_file = ev_mkstemp_file (template, &err);
+ g_free (template);
+ if (tmp_file == NULL) {
+ ev_window_error_message (ev_window, err,
+ "%s", _("Failed to load remote file."));
+ g_error_free (err);
+ return;
+ }
+
+ ev_window->priv->local_uri = g_file_get_uri (tmp_file);
+ g_object_unref (tmp_file);
+
+ ev_job_load_set_uri (EV_JOB_LOAD (ev_window->priv->load_job),
+ ev_window->priv->local_uri);
+ }
+
+ ev_window_reset_progress_cancellable (ev_window);
+
+ target_file = g_file_new_for_uri (ev_window->priv->local_uri);
+ g_file_copy_async (source_file, target_file,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ ev_window->priv->progress_cancellable,
+ (GFileProgressCallback)window_open_file_copy_progress_cb,
+ ev_window,
+ (GAsyncReadyCallback)window_open_file_copy_ready_cb,
+ ev_window);
+ g_object_unref (target_file);
+
+ ev_window_show_progress_message (ev_window, 1,
+ (GSourceFunc)show_loading_progress);
+}
+
+void
+ev_window_open_uri (EvWindow *ev_window,
+ const char *uri,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string)
+{
+ 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_reload_document (ev_window, dest);
+ return;
+ }
+
+ if (ev_window->priv->monitor) {
+ g_object_unref (ev_window->priv->monitor);
+ ev_window->priv->monitor = NULL;
+ }
+
+ ev_window_close_dialogs (ev_window);
+ ev_window_clear_load_job (ev_window);
+ ev_window_clear_local_uri (ev_window);
+
+ ev_window->priv->window_mode = mode;
+
+ if (ev_window->priv->uri)
+ g_free (ev_window->priv->uri);
+ ev_window->priv->uri = g_strdup (uri);
+
+ if (ev_window->priv->metadata)
+ g_object_unref (ev_window->priv->metadata);
+
+ source_file = g_file_new_for_uri (uri);
+ if (!ev_file_is_temp (source_file) && ev_is_metadata_supported_for_file (source_file)) {
+ ev_window->priv->metadata = ev_metadata_new (source_file);
+ ev_window->priv->is_new_doc = ev_metadata_is_empty (ev_window->priv->metadata);
+ } else {
+ ev_window->priv->metadata = NULL;
+ }
+
+ if (ev_window->priv->search_string)
+ g_free (ev_window->priv->search_string);
+ ev_window->priv->search_string = search_string ?
+ g_strdup (search_string) : NULL;
+
+ if (ev_window->priv->dest)
+ g_object_unref (ev_window->priv->dest);
+ ev_window->priv->dest = dest ? g_object_ref (dest) : NULL;
+
+ setup_size_from_metadata (ev_window);
+ setup_model_from_metadata (ev_window);
+
+ ev_window->priv->load_job = ev_job_load_new (uri);
+ g_signal_connect (ev_window->priv->load_job,
+ "finished",
+ G_CALLBACK (ev_window_load_job_cb),
+ ev_window);
+
+ if (!g_file_is_native (source_file) && !ev_window->priv->local_uri) {
+ ev_window_load_file_remote (ev_window, source_file);
+ } else {
+ ev_view_set_loading (EV_VIEW (ev_window->priv->view), TRUE);
+ g_object_unref (source_file);
+ ev_job_scheduler_push_job (ev_window->priv->load_job, EV_JOB_PRIORITY_NONE);
+ }
+}
+
+void
+ev_window_open_document (EvWindow *ev_window,
+ EvDocument *document,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string)
+{
+ if (document == ev_window->priv->document)
+ return;
+
+ ev_window_close_dialogs (ev_window);
+ ev_window_clear_load_job (ev_window);
+ ev_window_clear_local_uri (ev_window);
+
+ if (ev_window->priv->monitor) {
+ g_object_unref (ev_window->priv->monitor);
+ ev_window->priv->monitor = NULL;
+ }
+
+ if (ev_window->priv->uri)
+ g_free (ev_window->priv->uri);
+ ev_window->priv->uri = g_strdup (ev_document_get_uri (document));
+
+ setup_size_from_metadata (ev_window);
+ setup_model_from_metadata (ev_window);
+
+ ev_document_model_set_document (ev_window->priv->model, document);
+
+ setup_document_from_metadata (ev_window);
+ setup_view_from_metadata (ev_window);
+
+ if (dest) {
+ EvLink *link;
+ EvLinkAction *link_action;
+
+ link_action = ev_link_action_new_dest (dest);
+ link = ev_link_new (NULL, link_action);
+ ev_view_handle_link (EV_VIEW (ev_window->priv->view), link);
+ /* FIXME: link action should inc dest ref counting
+ * or not unref it at all
+ */
+ g_object_ref (dest);
+ g_object_unref (link);
+ }
+
+ switch (mode) {
+ case EV_WINDOW_MODE_FULLSCREEN:
+ ev_window_run_fullscreen (ev_window);
+ break;
+ case EV_WINDOW_MODE_PRESENTATION:
+ ev_window_run_presentation (ev_window);
+ break;
+ default:
+ break;
+ }
+
+ if (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),
+ search_string);
+ }
+
+ /* Create a monitor for the document */
+ ev_window->priv->monitor = ev_file_monitor_new (ev_window->priv->uri);
+ g_signal_connect_swapped (ev_window->priv->monitor, "changed",
+ G_CALLBACK (ev_window_document_changed),
+ ev_window);
+}
+
+static void
+ev_window_reload_local (EvWindow *ev_window)
+{
+ const gchar *uri;
+
+ 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);
+ g_signal_connect (ev_window->priv->reload_job, "finished",
+ G_CALLBACK (ev_window_reload_job_cb),
+ ev_window);
+ ev_job_scheduler_push_job (ev_window->priv->reload_job, EV_JOB_PRIORITY_NONE);
+}
+
+static gboolean
+show_reloading_progress (EvWindow *ev_window)
+{
+ GtkWidget *area;
+ gchar *text;
+
+ if (ev_window->priv->message_area)
+ return FALSE;
+
+ text = g_strdup_printf (_("Reloading document from %s"),
+ ev_window->priv->uri);
+ area = ev_progress_message_area_new (GTK_STOCK_REFRESH,
+ text,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+ g_signal_connect (area, "response",
+ G_CALLBACK (ev_window_progress_response_cb),
+ ev_window);
+ gtk_widget_show (area);
+ ev_window_set_message_area (ev_window, area);
+ g_free (text);
+
+ return FALSE;
+}
+
+static void
+reload_remote_copy_ready_cb (GFile *remote,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GError *error = NULL;
+
+ ev_window_clear_progress_idle (ev_window);
+
+ g_file_copy_finish (remote, async_result, &error);
+ if (error) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ ev_window_error_message (ev_window, error,
+ "%s", _("Failed to reload document."));
+ g_error_free (error);
+ } else {
+ ev_window_reload_local (ev_window);
+ }
+
+ g_object_unref (remote);
+}
+
+static void
+reload_remote_copy_progress_cb (goffset n_bytes,
+ goffset total_bytes,
+ EvWindow *ev_window)
+{
+ gchar *status;
+ gdouble fraction;
+
+ if (!ev_window->priv->message_area)
+ return;
+
+ if (total_bytes <= 0)
+ return;
+
+ fraction = n_bytes / (gdouble)total_bytes;
+ status = g_strdup_printf (_("Downloading document (%d%%)"),
+ (gint)(fraction * 100));
+
+ ev_progress_message_area_set_status (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ status);
+ ev_progress_message_area_set_fraction (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ fraction);
+
+ g_free (status);
+}
+
+static void
+query_remote_uri_mtime_cb (GFile *remote,
+ GAsyncResult *async_result,
+ EvWindow *ev_window)
+{
+ GFileInfo *info;
+ GTimeVal mtime;
+ GError *error = NULL;
+
+ info = g_file_query_info_finish (remote, async_result, &error);
+ if (error) {
+ g_error_free (error);
+ g_object_unref (remote);
+ ev_window_reload_local (ev_window);
+
+ return;
+ }
+
+ g_file_info_get_modification_time (info, &mtime);
+ if (ev_window->priv->uri_mtime != mtime.tv_sec) {
+ GFile *target_file;
+
+ /* Remote file has changed */
+ ev_window->priv->uri_mtime = mtime.tv_sec;
+
+ ev_window_reset_progress_cancellable (ev_window);
+
+ target_file = g_file_new_for_uri (ev_window->priv->local_uri);
+ g_file_copy_async (remote, target_file,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ ev_window->priv->progress_cancellable,
+ (GFileProgressCallback)reload_remote_copy_progress_cb,
+ ev_window,
+ (GAsyncReadyCallback)reload_remote_copy_ready_cb,
+ ev_window);
+ g_object_unref (target_file);
+ ev_window_show_progress_message (ev_window, 1,
+ (GSourceFunc)show_reloading_progress);
+ } else {
+ g_object_unref (remote);
+ ev_window_reload_local (ev_window);
+ }
+
+ g_object_unref (info);
+}
+
+static void
+ev_window_reload_remote (EvWindow *ev_window)
+{
+ GFile *remote;
+
+ remote = g_file_new_for_uri (ev_window->priv->uri);
+ /* Reload the remote uri only if it has changed */
+ g_file_query_info_async (remote,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ 0, G_PRIORITY_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback)query_remote_uri_mtime_cb,
+ ev_window);
+}
+
+static void
+ev_window_reload_document (EvWindow *ev_window,
+ EvLinkDest *dest)
+{
+ gint page;
+
+
+ ev_window_clear_reload_job (ev_window);
+ ev_window->priv->in_reload = TRUE;
+
+ page = ev_document_model_get_page (ev_window->priv->model);
+
+ if (ev_window->priv->dest)
+ g_object_unref (ev_window->priv->dest);
+ ev_window->priv->dest = dest ? g_object_ref (dest) : NULL;
+
+ if (ev_window->priv->local_uri) {
+ ev_window_reload_remote (ev_window);
+ } else {
+ ev_window_reload_local (ev_window);
+ }
+}
+
+static void
+file_open_dialog_response_cb (GtkWidget *chooser,
+ gint response_id,
+ EvWindow *ev_window)
+{
+ if (response_id == GTK_RESPONSE_OK) {
+ GSList *uris;
+ gchar *uri;
+
+ uris = gtk_file_chooser_get_uris (GTK_FILE_CHOOSER (chooser));
+
+ ev_application_open_uri_list (EV_APP, uris,
+ gtk_window_get_screen (GTK_WINDOW (ev_window)),
+ gtk_get_current_event_time ());
+
+ g_slist_foreach (uris, (GFunc)g_free, NULL);
+ g_slist_free (uris);
+
+ uri = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (chooser));
+ ev_application_set_filechooser_uri (EV_APP,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ uri);
+ g_free (uri);
+ }
+
+ gtk_widget_destroy (chooser);
+}
+
+static void
+ev_window_cmd_file_open (GtkAction *action, EvWindow *window)
+{
+ GtkWidget *chooser;
+ const gchar *default_uri;
+ gchar *parent_uri = NULL;
+
+ chooser = gtk_file_chooser_dialog_new (_("Open Document"),
+ GTK_WINDOW (window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_OK,
+ NULL);
+
+ ev_document_factory_add_filters (chooser, NULL);
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), FALSE);
+
+ default_uri = ev_application_get_filechooser_uri (EV_APP, GTK_FILE_CHOOSER_ACTION_OPEN);
+ if (!default_uri && window->priv->uri) {
+ GFile *file, *parent;
+
+ file = g_file_new_for_uri (window->priv->uri);
+ parent = g_file_get_parent (file);
+ if (parent) {
+ parent_uri = g_file_get_uri (parent);
+ default_uri = parent_uri;
+ g_object_unref (parent);
+ }
+ g_object_unref (file);
+ }
+
+ if (default_uri) {
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (chooser), default_uri);
+ } else {
+ const gchar *folder;
+
+ folder = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
+ folder ? folder : g_get_home_dir ());
+ }
+ g_free (parent_uri);
+
+ g_signal_connect (chooser, "response",
+ G_CALLBACK (file_open_dialog_response_cb),
+ window);
+
+ gtk_widget_show (chooser);
+}
+
+static void
+ev_window_open_copy_at_dest (EvWindow *window,
+ EvLinkDest *dest)
+{
+ EvWindow *new_window = EV_WINDOW (ev_window_new ());
+
+ ev_window_n_copies++;
+
+ if (window->priv->metadata)
+ new_window->priv->metadata = g_object_ref (window->priv->metadata);
+ ev_window_open_document (new_window,
+ window->priv->document,
+ dest, 0, NULL);
+ gtk_window_present (GTK_WINDOW (new_window));
+}
+
+static void
+ev_window_cmd_file_open_copy (GtkAction *action, EvWindow *window)
+{
+ ev_window_open_copy_at_dest (window, NULL);
+}
+
+static void
+ev_window_cmd_recent_file_activate (GtkAction *action,
+ EvWindow *window)
+{
+ GtkRecentInfo *info;
+ const gchar *uri;
+
+ info = g_object_get_data (G_OBJECT (action), "gtk-recent-info");
+ g_assert (info != NULL);
+
+ uri = gtk_recent_info_get_uri (info);
+
+ ev_application_open_uri_at_dest (EV_APP, uri,
+ gtk_window_get_screen (GTK_WINDOW (window)),
+ NULL, 0, NULL, gtk_get_current_event_time ());
+}
+
+static void
+ev_window_open_recent_action_item_activated (EvOpenRecentAction *action,
+ const gchar *uri,
+ EvWindow *window)
+{
+ ev_application_open_uri_at_dest (EV_APP, uri,
+ gtk_window_get_screen (GTK_WINDOW (window)),
+ NULL, 0, NULL, gtk_get_current_event_time ());
+}
+
+static void
+ev_window_add_recent (EvWindow *window, const char *filename)
+{
+ gtk_recent_manager_add_item (window->priv->recent_manager, filename);
+}
+
+static gint
+compare_recent_items (GtkRecentInfo *a, GtkRecentInfo *b)
+{
+ gboolean has_ev_a, has_ev_b;
+ const gchar *evince = g_get_application_name ();
+
+ has_ev_a = gtk_recent_info_has_application (a, evince);
+ has_ev_b = gtk_recent_info_has_application (b, evince);
+
+ if (has_ev_a && has_ev_b) {
+ time_t time_a, time_b;
+
+ time_a = gtk_recent_info_get_modified (a);
+ time_b = gtk_recent_info_get_modified (b);
+
+ return (time_b - time_a);
+ } else if (has_ev_a) {
+ return -1;
+ } else if (has_ev_b) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Doubles underscore to avoid spurious menu accels.
+ */
+static gchar *
+ev_window_get_recent_file_label (gint index, const gchar *filename)
+{
+ GString *str;
+ gint length;
+ const gchar *p;
+ const gchar *end;
+ gboolean is_rtl;
+
+ is_rtl = (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL);
+
+ g_return_val_if_fail (filename != NULL, NULL);
+
+ length = strlen (filename);
+ str = g_string_sized_new (length + 10);
+ g_string_printf (str, "%s_%d. ", is_rtl ? "\xE2\x80\x8F" : "", index);
+
+ p = filename;
+ end = filename + length;
+
+ while (p != end) {
+ const gchar *next;
+ next = g_utf8_next_char (p);
+
+ switch (*p) {
+ case '_':
+ g_string_append (str, "__");
+ break;
+ default:
+ g_string_append_len (str, p, next - p);
+ break;
+ }
+
+ p = next;
+ }
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+ev_window_recent_action_connect_proxy_cb (GtkActionGroup *action_group,
+ GtkAction *action,
+ GtkWidget *proxy,
+ gpointer data)
+{
+ GtkLabel *label;
+
+ if (!GTK_IS_MENU_ITEM (proxy))
+ return;
+
+ label = GTK_LABEL (gtk_bin_get_child (GTK_BIN (proxy)));
+
+ gtk_label_set_ellipsize (label, PANGO_ELLIPSIZE_MIDDLE);
+ gtk_label_set_max_width_chars (label, MAX_RECENT_ITEM_LEN);
+}
+
+static void
+ev_window_setup_recent (EvWindow *ev_window)
+{
+ GList *items, *l;
+ guint n_items = 0;
+ const gchar *evince = g_get_application_name ();
+ static guint i = 0;
+
+ if (ev_window->priv->recent_ui_id > 0) {
+ gtk_ui_manager_remove_ui (ev_window->priv->ui_manager,
+ ev_window->priv->recent_ui_id);
+ gtk_ui_manager_ensure_update (ev_window->priv->ui_manager);
+ }
+ ev_window->priv->recent_ui_id = gtk_ui_manager_new_merge_id (ev_window->priv->ui_manager);
+
+ if (ev_window->priv->recent_action_group) {
+ gtk_ui_manager_remove_action_group (ev_window->priv->ui_manager,
+ ev_window->priv->recent_action_group);
+ g_object_unref (ev_window->priv->recent_action_group);
+ }
+ ev_window->priv->recent_action_group = gtk_action_group_new ("RecentFilesActions");
+ g_signal_connect (ev_window->priv->recent_action_group, "connect-proxy",
+ G_CALLBACK (ev_window_recent_action_connect_proxy_cb), NULL);
+
+ gtk_ui_manager_insert_action_group (ev_window->priv->ui_manager,
+ ev_window->priv->recent_action_group, -1);
+
+ items = gtk_recent_manager_get_items (ev_window->priv->recent_manager);
+ items = g_list_sort (items, (GCompareFunc) compare_recent_items);
+
+ for (l = items; l && l->data; l = g_list_next (l)) {
+ GtkRecentInfo *info;
+ GtkAction *action;
+ gchar *action_name;
+ gchar *label;
+ const gchar *mime_type;
+ gchar *content_type;
+ GIcon *icon = NULL;
+
+ info = (GtkRecentInfo *) l->data;
+
+ if (!gtk_recent_info_has_application (info, evince) ||
+ (gtk_recent_info_is_local (info) && !gtk_recent_info_exists (info)))
+ continue;
+
+ action_name = g_strdup_printf ("RecentFile%u", i++);
+ label = ev_window_get_recent_file_label (
+ n_items + 1, gtk_recent_info_get_display_name (info));
+
+ mime_type = gtk_recent_info_get_mime_type (info);
+ content_type = g_content_type_from_mime_type (mime_type);
+ if (content_type != NULL) {
+ icon = g_content_type_get_icon (content_type);
+ g_free (content_type);
+ }
+
+ action = g_object_new (GTK_TYPE_ACTION,
+ "name", action_name,
+ "label", label,
+ "gicon", icon,
+ "always-show-image", TRUE,
+ NULL);
+
+ g_object_set_data_full (G_OBJECT (action),
+ "gtk-recent-info",
+ gtk_recent_info_ref (info),
+ (GDestroyNotify) gtk_recent_info_unref);
+
+ g_signal_connect (action, "activate",
+ G_CALLBACK (ev_window_cmd_recent_file_activate),
+ (gpointer) ev_window);
+
+ gtk_action_group_add_action (ev_window->priv->recent_action_group,
+ action);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (ev_window->priv->ui_manager,
+ ev_window->priv->recent_ui_id,
+ "/MainMenu/FileMenu/RecentFilesMenu",
+ label,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+ g_free (action_name);
+ g_free (label);
+ if (icon != NULL)
+ g_object_unref (icon);
+
+ if (++n_items == 5)
+ break;
+ }
+
+ g_list_foreach (items, (GFunc) gtk_recent_info_unref, NULL);
+ g_list_free (items);
+}
+
+static gboolean
+show_saving_progress (GFile *dst)
+{
+ EvWindow *ev_window;
+ GtkWidget *area;
+ gchar *text;
+ gchar *uri;
+ EvSaveType save_type;
+
+ ev_window = EV_WINDOW (g_object_get_data (G_OBJECT (dst), "ev-window"));
+ ev_window->priv->progress_idle = 0;
+
+ if (ev_window->priv->message_area)
+ return FALSE;
+
+ save_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dst), "save-type"));
+ uri = g_file_get_uri (dst);
+ switch (save_type) {
+ case EV_SAVE_DOCUMENT:
+ text = g_strdup_printf (_("Saving document to %s"), uri);
+ break;
+ case EV_SAVE_ATTACHMENT:
+ text = g_strdup_printf (_("Saving attachment to %s"), uri);
+ break;
+ case EV_SAVE_IMAGE:
+ text = g_strdup_printf (_("Saving image to %s"), uri);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ g_free (uri);
+ area = ev_progress_message_area_new (GTK_STOCK_SAVE,
+ text,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+ g_signal_connect (area, "response",
+ G_CALLBACK (ev_window_progress_response_cb),
+ ev_window);
+ gtk_widget_show (area);
+ ev_window_set_message_area (ev_window, area);
+ g_free (text);
+
+ return FALSE;
+}
+
+static void
+window_save_file_copy_ready_cb (GFile *src,
+ GAsyncResult *async_result,
+ GFile *dst)
+{
+ EvWindow *ev_window;
+ GError *error = NULL;
+
+ ev_window = EV_WINDOW (g_object_get_data (G_OBJECT (dst), "ev-window"));
+ ev_window_clear_progress_idle (ev_window);
+
+ if (g_file_copy_finish (src, async_result, &error)) {
+ ev_tmp_file_unlink (src);
+ return;
+ }
+
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ gchar *name;
+
+ name = g_file_get_basename (dst);
+ ev_window_error_message (ev_window, error,
+ _("The file could not be saved as ā€œ%sā€."),
+ name);
+ g_free (name);
+ }
+ ev_tmp_file_unlink (src);
+ g_error_free (error);
+}
+
+static void
+window_save_file_copy_progress_cb (goffset n_bytes,
+ goffset total_bytes,
+ GFile *dst)
+{
+ EvWindow *ev_window;
+ EvSaveType save_type;
+ gchar *status;
+ gdouble fraction;
+
+ ev_window = EV_WINDOW (g_object_get_data (G_OBJECT (dst), "ev-window"));
+
+ if (!ev_window->priv->message_area)
+ return;
+
+ if (total_bytes <= 0)
+ return;
+
+ fraction = n_bytes / (gdouble)total_bytes;
+ save_type = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dst), "save-type"));
+
+ switch (save_type) {
+ case EV_SAVE_DOCUMENT:
+ status = g_strdup_printf (_("Uploading document (%d%%)"),
+ (gint)(fraction * 100));
+ break;
+ case EV_SAVE_ATTACHMENT:
+ status = g_strdup_printf (_("Uploading attachment (%d%%)"),
+ (gint)(fraction * 100));
+ break;
+ case EV_SAVE_IMAGE:
+ status = g_strdup_printf (_("Uploading image (%d%%)"),
+ (gint)(fraction * 100));
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ ev_progress_message_area_set_status (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ status);
+ ev_progress_message_area_set_fraction (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ fraction);
+
+ g_free (status);
+}
+
+static void
+ev_window_save_remote (EvWindow *ev_window,
+ EvSaveType save_type,
+ GFile *src,
+ GFile *dst)
+{
+ ev_window_reset_progress_cancellable (ev_window);
+ g_object_set_data (G_OBJECT (dst), "ev-window", ev_window);
+ g_object_set_data (G_OBJECT (dst), "save-type", GINT_TO_POINTER (save_type));
+ g_file_copy_async (src, dst,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ ev_window->priv->progress_cancellable,
+ (GFileProgressCallback)window_save_file_copy_progress_cb,
+ dst,
+ (GAsyncReadyCallback)window_save_file_copy_ready_cb,
+ dst);
+ ev_window->priv->progress_idle =
+ g_timeout_add_seconds_full (G_PRIORITY_DEFAULT,
+ 1,
+ (GSourceFunc)show_saving_progress,
+ dst,
+ NULL);
+}
+
+static void
+ev_window_clear_save_job (EvWindow *ev_window)
+{
+ if (ev_window->priv->save_job != NULL) {
+ if (!ev_job_is_finished (ev_window->priv->save_job))
+ ev_job_cancel (ev_window->priv->save_job);
+
+ g_signal_handlers_disconnect_by_func (ev_window->priv->save_job,
+ ev_window_save_job_cb,
+ ev_window);
+ g_object_unref (ev_window->priv->save_job);
+ ev_window->priv->save_job = NULL;
+ }
+}
+
+static void
+ev_window_save_job_cb (EvJob *job,
+ EvWindow *window)
+{
+ if (ev_job_is_failed (job)) {
+ ev_window_error_message (window, job->error,
+ _("The file could not be saved as ā€œ%sā€."),
+ EV_JOB_SAVE (job)->uri);
+ } else {
+ ev_window_add_recent (window, EV_JOB_SAVE (job)->uri);
+ }
+
+ ev_window_clear_save_job (window);
+}
+
+static void
+file_save_dialog_response_cb (GtkWidget *fc,
+ gint response_id,
+ EvWindow *ev_window)
+{
+ gchar *uri;
+ GFile *file, *parent;
+
+ if (response_id != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (fc);
+ return;
+ }
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fc));
+ file = g_file_new_for_uri (uri);
+ parent = g_file_get_parent (file);
+ g_object_unref (file);
+ if (parent) {
+ gchar *folder_uri;
+
+ folder_uri = g_file_get_uri (parent);
+ ev_application_set_filechooser_uri (EV_APP,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ folder_uri);
+ g_free (folder_uri);
+ g_object_unref (parent);
+ }
+
+ /* FIXME: remote copy should be done here rather than in the save job,
+ * so that we can track progress and cancel the operation
+ */
+
+ ev_window_clear_save_job (ev_window);
+ ev_window->priv->save_job = ev_job_save_new (ev_window->priv->document,
+ uri, ev_window->priv->uri);
+ g_signal_connect (ev_window->priv->save_job, "finished",
+ G_CALLBACK (ev_window_save_job_cb),
+ ev_window);
+ /* The priority doesn't matter for this job */
+ ev_job_scheduler_push_job (ev_window->priv->save_job, EV_JOB_PRIORITY_NONE);
+
+ g_free (uri);
+ gtk_widget_destroy (fc);
+}
+
+static void
+ev_window_cmd_save_as (GtkAction *action, EvWindow *ev_window)
+{
+ GtkWidget *fc;
+ gchar *base_name;
+ GFile *file;
+ const gchar *default_uri;
+
+ fc = gtk_file_chooser_dialog_new (
+ _("Save a Copy"),
+ GTK_WINDOW (ev_window), GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+ NULL);
+
+ ev_document_factory_add_filters (fc, ev_window->priv->document);
+ gtk_dialog_set_default_response (GTK_DIALOG (fc), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (fc),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fc), FALSE);
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fc), TRUE);
+ file = g_file_new_for_uri (ev_window->priv->uri);
+ base_name = g_file_get_basename (file);
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (fc), base_name);
+
+ default_uri = ev_application_get_filechooser_uri (EV_APP, GTK_FILE_CHOOSER_ACTION_SAVE);
+ if (default_uri) {
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (fc), default_uri);
+ } else {
+ const gchar *folder;
+
+ folder = g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (fc),
+ folder ? folder : g_get_home_dir ());
+ }
+
+ g_object_unref (file);
+ g_free (base_name);
+
+ g_signal_connect (fc, "response",
+ G_CALLBACK (file_save_dialog_response_cb),
+ ev_window);
+
+ gtk_widget_show (fc);
+}
+
+static GKeyFile *
+get_print_settings_file (void)
+{
+ GKeyFile *print_settings_file;
+ gchar *filename;
+ GError *error = NULL;
+
+ print_settings_file = g_key_file_new ();
+
+ filename = g_build_filename (ev_application_get_dot_dir (EV_APP, FALSE),
+ EV_PRINT_SETTINGS_FILE, NULL);
+ if (!g_key_file_load_from_file (print_settings_file,
+ filename,
+ G_KEY_FILE_KEEP_COMMENTS |
+ G_KEY_FILE_KEEP_TRANSLATIONS,
+ &error)) {
+
+ /* Don't warn if the file simply doesn't exist */
+ if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+ g_warning ("%s", error->message);
+
+ g_error_free (error);
+ }
+
+ g_free (filename);
+
+ return print_settings_file;
+}
+
+static void
+save_print_setting_file (GKeyFile *key_file)
+{
+ gchar *filename;
+ gchar *data;
+ gsize data_length;
+ GError *error = NULL;
+
+ filename = g_build_filename (ev_application_get_dot_dir (EV_APP, TRUE),
+ EV_PRINT_SETTINGS_FILE, NULL);
+ data = g_key_file_to_data (key_file, &data_length, NULL);
+ g_file_set_contents (filename, data, data_length, &error);
+ if (error) {
+ g_warning ("Failed to save print settings: %s", error->message);
+ g_error_free (error);
+ }
+ g_free (data);
+ g_free (filename);
+}
+
+static void
+ev_window_save_print_settings (EvWindow *window,
+ GtkPrintSettings *print_settings)
+{
+ GKeyFile *key_file;
+ gint i;
+
+ key_file = get_print_settings_file ();
+ gtk_print_settings_to_key_file (print_settings, key_file, EV_PRINT_SETTINGS_GROUP);
+
+ /* Save print settings that are specific to the document */
+ for (i = 0; i < G_N_ELEMENTS (document_print_settings); i++) {
+ /* Remove it from global settings */
+ g_key_file_remove_key (key_file, EV_PRINT_SETTINGS_GROUP,
+ document_print_settings[i], NULL);
+
+ if (window->priv->metadata) {
+ const gchar *value;
+
+ value = gtk_print_settings_get (print_settings,
+ document_print_settings[i]);
+ ev_metadata_set_string (window->priv->metadata,
+ document_print_settings[i], value);
+ }
+ }
+
+ save_print_setting_file (key_file);
+ g_key_file_free (key_file);
+}
+
+static void
+ev_window_save_print_page_setup (EvWindow *window,
+ GtkPageSetup *page_setup)
+{
+ GKeyFile *key_file;
+
+ key_file = get_print_settings_file ();
+ gtk_page_setup_to_key_file (page_setup, key_file, EV_PAGE_SETUP_GROUP);
+
+ /* Do not save document settings in global file */
+ g_key_file_remove_key (key_file, EV_PAGE_SETUP_GROUP,
+ "page-setup-orientation", NULL);
+ g_key_file_remove_key (key_file, EV_PAGE_SETUP_GROUP,
+ "page-setup-margin-top", NULL);
+ g_key_file_remove_key (key_file, EV_PAGE_SETUP_GROUP,
+ "page-setup-margin-bottom", NULL);
+ g_key_file_remove_key (key_file, EV_PAGE_SETUP_GROUP,
+ "page-setup-margin-left", NULL);
+ g_key_file_remove_key (key_file, EV_PAGE_SETUP_GROUP,
+ "page-setup-margin-right", NULL);
+
+ save_print_setting_file (key_file);
+ g_key_file_free (key_file);
+
+ if (!window->priv->metadata)
+ return;
+
+ /* Save page setup options that are specific to the document */
+ ev_metadata_set_int (window->priv->metadata, "page-setup-orientation",
+ gtk_page_setup_get_orientation (page_setup));
+ ev_metadata_set_double (window->priv->metadata, "page-setup-margin-top",
+ gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_MM));
+ ev_metadata_set_double (window->priv->metadata, "page-setup-margin-bottom",
+ gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_MM));
+ ev_metadata_set_double (window->priv->metadata, "page-setup-margin-left",
+ gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_MM));
+ ev_metadata_set_double (window->priv->metadata, "page-setup-margin-right",
+ gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_MM));
+}
+
+static void
+ev_window_load_print_settings_from_metadata (EvWindow *window,
+ GtkPrintSettings *print_settings)
+{
+ gint i;
+
+ if (!window->priv->metadata)
+ return;
+
+ /* Load print setting that are specific to the document */
+ for (i = 0; i < G_N_ELEMENTS (document_print_settings); i++) {
+ gchar *value = NULL;
+
+ ev_metadata_get_string (window->priv->metadata,
+ document_print_settings[i], &value);
+ gtk_print_settings_set (print_settings,
+ document_print_settings[i], value);
+ }
+}
+
+static void
+ev_window_load_print_page_setup_from_metadata (EvWindow *window,
+ GtkPageSetup *page_setup)
+{
+ gint int_value;
+ gdouble double_value;
+ GtkPaperSize *paper_size = gtk_page_setup_get_paper_size (page_setup);
+
+ /* Load page setup options that are specific to the document */
+ if (window->priv->metadata &&
+ ev_metadata_get_int (window->priv->metadata, "page-setup-orientation", &int_value)) {
+ gtk_page_setup_set_orientation (page_setup, int_value);
+ } else {
+ gtk_page_setup_set_orientation (page_setup, GTK_PAGE_ORIENTATION_PORTRAIT);
+ }
+
+ if (window->priv->metadata &&
+ ev_metadata_get_double (window->priv->metadata, "page-setup-margin-top", &double_value)) {
+ gtk_page_setup_set_top_margin (page_setup, double_value, GTK_UNIT_MM);
+ } else {
+ gtk_page_setup_set_top_margin (page_setup,
+ gtk_paper_size_get_default_top_margin (paper_size, GTK_UNIT_MM),
+ GTK_UNIT_MM);
+ }
+
+ if (window->priv->metadata &&
+ ev_metadata_get_double (window->priv->metadata, "page-setup-margin-bottom", &double_value)) {
+ gtk_page_setup_set_bottom_margin (page_setup, double_value, GTK_UNIT_MM);
+ } else {
+ gtk_page_setup_set_bottom_margin (page_setup,
+ gtk_paper_size_get_default_bottom_margin (paper_size, GTK_UNIT_MM),
+ GTK_UNIT_MM);
+ }
+
+ if (window->priv->metadata &&
+ ev_metadata_get_double (window->priv->metadata, "page-setup-margin-left", &double_value)) {
+ gtk_page_setup_set_left_margin (page_setup, double_value, GTK_UNIT_MM);
+ } else {
+ gtk_page_setup_set_left_margin (page_setup,
+ gtk_paper_size_get_default_left_margin (paper_size, GTK_UNIT_MM),
+ GTK_UNIT_MM);
+ }
+
+ if (window->priv->metadata &&
+ ev_metadata_get_double (window->priv->metadata, "page-setup-margin-right", &double_value)) {
+ gtk_page_setup_set_right_margin (page_setup, double_value, GTK_UNIT_MM);
+ } else {
+ gtk_page_setup_set_right_margin (page_setup,
+ gtk_paper_size_get_default_right_margin (paper_size, GTK_UNIT_MM),
+ GTK_UNIT_MM);
+ }
+}
+
+static GtkPrintSettings *
+get_print_settings (GKeyFile *key_file)
+{
+ GtkPrintSettings *print_settings;
+
+ print_settings = g_key_file_has_group (key_file, EV_PRINT_SETTINGS_GROUP) ?
+ gtk_print_settings_new_from_key_file (key_file, EV_PRINT_SETTINGS_GROUP, NULL) :
+ gtk_print_settings_new ();
+
+ return print_settings ? print_settings : gtk_print_settings_new ();
+}
+
+static GtkPageSetup *
+get_print_page_setup (GKeyFile *key_file)
+{
+ GtkPageSetup *page_setup;
+
+ page_setup = g_key_file_has_group (key_file, EV_PAGE_SETUP_GROUP) ?
+ gtk_page_setup_new_from_key_file (key_file, EV_PAGE_SETUP_GROUP, NULL) :
+ gtk_page_setup_new ();
+
+ return page_setup ? page_setup : gtk_page_setup_new ();
+}
+
+static void
+ev_window_print_cancel (EvWindow *ev_window)
+{
+ EvPrintOperation *op;
+
+ if (!ev_window->priv->print_queue)
+ return;
+
+ while ((op = g_queue_peek_tail (ev_window->priv->print_queue))) {
+ ev_print_operation_cancel (op);
+ }
+}
+
+static void
+ev_window_print_update_pending_jobs_message (EvWindow *ev_window,
+ gint n_jobs)
+{
+ gchar *text = NULL;
+
+ if (!EV_IS_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area) ||
+ !ev_window->priv->print_queue)
+ return;
+
+ if (n_jobs == 0) {
+ ev_window_set_message_area (ev_window, NULL);
+ return;
+ }
+
+ if (n_jobs > 1) {
+ text = g_strdup_printf (ngettext ("%d pending job in queue",
+ "%d pending jobs in queue",
+ n_jobs - 1), n_jobs - 1);
+ }
+
+ ev_message_area_set_secondary_text (EV_MESSAGE_AREA (ev_window->priv->message_area),
+ text);
+ g_free (text);
+}
+
+static gboolean
+destroy_window (GtkWidget *window)
+{
+ gtk_widget_destroy (window);
+
+ return FALSE;
+}
+
+static void
+ev_window_print_operation_done (EvPrintOperation *op,
+ GtkPrintOperationResult result,
+ EvWindow *ev_window)
+{
+ gint n_jobs;
+
+ switch (result) {
+ case GTK_PRINT_OPERATION_RESULT_APPLY: {
+ GtkPrintSettings *print_settings;
+
+ print_settings = ev_print_operation_get_print_settings (op);
+ ev_window_save_print_settings (ev_window, print_settings);
+
+ if (ev_print_operation_get_embed_page_setup (op)) {
+ GtkPageSetup *page_setup;
+
+ page_setup = ev_print_operation_get_default_page_setup (op);
+ ev_window_save_print_page_setup (ev_window, page_setup);
+ }
+ }
+
+ break;
+ case GTK_PRINT_OPERATION_RESULT_ERROR: {
+ GtkWidget *dialog;
+ GError *error = NULL;
+
+
+ ev_print_operation_get_error (op, &error);
+
+ /* The message area is already used by
+ * the printing progress, so it's better to
+ * use a popup dialog in this case
+ */
+ dialog = gtk_message_dialog_new (GTK_WINDOW (ev_window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "%s", _("Failed to print document"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", error->message);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+ gtk_widget_show (dialog);
+
+ g_error_free (error);
+ }
+ break;
+ case GTK_PRINT_OPERATION_RESULT_CANCEL:
+ default:
+ break;
+ }
+
+ g_queue_remove (ev_window->priv->print_queue, op);
+ g_object_unref (op);
+ n_jobs = g_queue_get_length (ev_window->priv->print_queue);
+ ev_window_print_update_pending_jobs_message (ev_window, n_jobs);
+
+ if (n_jobs == 0 && ev_window->priv->close_after_print)
+ g_idle_add ((GSourceFunc)destroy_window,
+ ev_window);
+}
+
+static void
+ev_window_print_progress_response_cb (EvProgressMessageArea *area,
+ gint response,
+ EvWindow *ev_window)
+{
+ if (response == GTK_RESPONSE_CANCEL) {
+ EvPrintOperation *op;
+
+ op = g_queue_peek_tail (ev_window->priv->print_queue);
+ ev_print_operation_cancel (op);
+ } else {
+ gtk_widget_hide (GTK_WIDGET (area));
+ }
+}
+
+static void
+ev_window_print_operation_status_changed (EvPrintOperation *op,
+ EvWindow *ev_window)
+{
+ const gchar *status;
+ gdouble fraction;
+
+ status = ev_print_operation_get_status (op);
+ fraction = ev_print_operation_get_progress (op);
+
+ if (!ev_window->priv->message_area) {
+ GtkWidget *area;
+ const gchar *job_name;
+ gchar *text;
+
+ job_name = ev_print_operation_get_job_name (op);
+ text = g_strdup_printf (_("Printing job ā€œ%sā€"), job_name);
+
+ area = ev_progress_message_area_new (GTK_STOCK_PRINT,
+ text,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ NULL);
+ ev_window_print_update_pending_jobs_message (ev_window, 1);
+ g_signal_connect (area, "response",
+ G_CALLBACK (ev_window_print_progress_response_cb),
+ ev_window);
+ gtk_widget_show (area);
+ ev_window_set_message_area (ev_window, area);
+ g_free (text);
+ }
+
+ ev_progress_message_area_set_status (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ status);
+ ev_progress_message_area_set_fraction (EV_PROGRESS_MESSAGE_AREA (ev_window->priv->message_area),
+ fraction);
+}
+
+static void
+ev_window_print_operation_begin_print (EvPrintOperation *op,
+ EvWindow *ev_window)
+{
+ if (!ev_window->priv->print_queue)
+ ev_window->priv->print_queue = g_queue_new ();
+
+ g_queue_push_head (ev_window->priv->print_queue, op);
+ ev_window_print_update_pending_jobs_message (ev_window,
+ g_queue_get_length (ev_window->priv->print_queue));
+}
+
+void
+ev_window_print_range (EvWindow *ev_window,
+ gint first_page,
+ gint last_page)
+{
+ EvPrintOperation *op;
+ GKeyFile *print_settings_file;
+ GtkPrintSettings *print_settings;
+ GtkPageSetup *print_page_setup;
+ gint current_page;
+ gint document_last_page;
+
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+ g_return_if_fail (ev_window->priv->document != NULL);
+
+ if (!ev_window->priv->print_queue)
+ ev_window->priv->print_queue = g_queue_new ();
+
+ op = ev_print_operation_new (ev_window->priv->document);
+ if (!op) {
+ g_warning ("%s", "Printing is not supported for document\n");
+ return;
+ }
+
+ g_signal_connect (op, "begin_print",
+ G_CALLBACK (ev_window_print_operation_begin_print),
+ (gpointer)ev_window);
+ g_signal_connect (op, "status_changed",
+ G_CALLBACK (ev_window_print_operation_status_changed),
+ (gpointer)ev_window);
+ g_signal_connect (op, "done",
+ G_CALLBACK (ev_window_print_operation_done),
+ (gpointer)ev_window);
+
+ current_page = ev_document_model_get_page (ev_window->priv->model);
+ document_last_page = ev_document_get_n_pages (ev_window->priv->document);
+
+ print_settings_file = get_print_settings_file ();
+
+ print_settings = get_print_settings (print_settings_file);
+ ev_window_load_print_settings_from_metadata (ev_window, print_settings);
+
+ print_page_setup = get_print_page_setup (print_settings_file);
+ ev_window_load_print_page_setup_from_metadata (ev_window, print_page_setup);
+
+ if (first_page != 1 || last_page != document_last_page) {
+ GtkPageRange range;
+
+ /* Ranges in GtkPrint are 0 - N */
+ range.start = first_page - 1;
+ range.end = last_page - 1;
+
+ gtk_print_settings_set_print_pages (print_settings,
+ GTK_PRINT_PAGES_RANGES);
+ gtk_print_settings_set_page_ranges (print_settings,
+ &range, 1);
+ }
+
+ ev_print_operation_set_job_name (op, gtk_window_get_title (GTK_WINDOW (ev_window)));
+ ev_print_operation_set_current_page (op, current_page);
+ ev_print_operation_set_print_settings (op, print_settings);
+ ev_print_operation_set_default_page_setup (op, print_page_setup);
+#ifdef WITH_MATECONF
+ ev_print_operation_set_embed_page_setup (op, !mateconf_client_get_bool (ev_window->priv->mateconf_client,
+ MATECONF_LOCKDOWN_PRINT_SETUP,
+ NULL));
+#else
+ ev_print_operation_set_embed_page_setup (op, TRUE);
+#endif
+
+ g_object_unref (print_settings);
+ g_object_unref (print_page_setup);
+ g_key_file_free (print_settings_file);
+
+ ev_print_operation_run (op, GTK_WINDOW (ev_window));
+}
+
+static void
+ev_window_print (EvWindow *window)
+{
+ ev_window_print_range (window, 1,
+ ev_document_get_n_pages (window->priv->document));
+}
+
+static void
+ev_window_cmd_file_print (GtkAction *action, EvWindow *ev_window)
+{
+ ev_window_print (ev_window);
+}
+
+static void
+ev_window_cmd_file_properties (GtkAction *action, EvWindow *ev_window)
+{
+ if (ev_window->priv->properties == NULL) {
+ ev_window->priv->properties = ev_properties_dialog_new ();
+ ev_properties_dialog_set_document (EV_PROPERTIES_DIALOG (ev_window->priv->properties),
+ ev_window->priv->uri,
+ ev_window->priv->document);
+ g_object_add_weak_pointer (G_OBJECT (ev_window->priv->properties),
+ (gpointer) &(ev_window->priv->properties));
+ gtk_window_set_transient_for (GTK_WINDOW (ev_window->priv->properties),
+ GTK_WINDOW (ev_window));
+ }
+
+ ev_document_fc_mutex_lock ();
+ gtk_widget_show (ev_window->priv->properties);
+ ev_document_fc_mutex_unlock ();
+}
+
+static void
+document_modified_confirmation_dialog_response (GtkDialog *dialog,
+ gint response,
+ EvWindow *ev_window)
+{
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ switch (response) {
+ case GTK_RESPONSE_YES:
+ ev_window_cmd_save_as (NULL, ev_window);
+ break;
+ case GTK_RESPONSE_NO:
+ gtk_widget_destroy (GTK_WIDGET (ev_window));
+ break;
+ case GTK_RESPONSE_CANCEL:
+ default:
+ break;
+ }
+}
+
+static gboolean
+ev_window_check_document_modified (EvWindow *ev_window)
+{
+ EvDocument *document = ev_window->priv->document;
+ GtkWidget *dialog;
+ gchar *text, *markup;
+ const gchar *secondary_text;
+
+ if (!document)
+ return FALSE;
+
+ if (EV_IS_DOCUMENT_FORMS (document) &&
+ ev_document_forms_document_is_modified (EV_DOCUMENT_FORMS (document))) {
+ secondary_text = _("Document contains form fields that have been filled out. "
+ "If you don't save a copy, changes will be permanently lost.");
+ } else if (EV_IS_DOCUMENT_ANNOTATIONS (document) &&
+ ev_document_annotations_document_is_modified (EV_DOCUMENT_ANNOTATIONS (document))) {
+ secondary_text = _("Document contains new or modified annotations. "
+ "If you don't save a copy, changes will be permanently lost.");
+ } else {
+ return FALSE;
+ }
+
+
+ text = g_markup_printf_escaped (_("Save a copy of document ā€œ%sā€ before closing?"),
+ gtk_window_get_title (GTK_WINDOW (ev_window)));
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (ev_window),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ NULL);
+
+ markup = g_strdup_printf ("<b>%s</b>", text);
+ g_free (text);
+
+ gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), markup);
+ g_free (markup);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", secondary_text);
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ _("Close _without Saving"),
+ GTK_RESPONSE_NO,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ _("Save a _Copy"),
+ GTK_RESPONSE_YES,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_YES,
+ GTK_RESPONSE_NO,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (document_modified_confirmation_dialog_response),
+ ev_window);
+ gtk_widget_show (dialog);
+
+ return TRUE;
+}
+
+static void
+print_jobs_confirmation_dialog_response (GtkDialog *dialog,
+ gint response,
+ EvWindow *ev_window)
+{
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ switch (response) {
+ case GTK_RESPONSE_YES:
+ if (!ev_window->priv->print_queue ||
+ g_queue_is_empty (ev_window->priv->print_queue))
+ gtk_widget_destroy (GTK_WIDGET (ev_window));
+ else
+ ev_window->priv->close_after_print = TRUE;
+ break;
+ case GTK_RESPONSE_NO:
+ ev_window->priv->close_after_print = TRUE;
+ if (ev_window->priv->print_queue &&
+ !g_queue_is_empty (ev_window->priv->print_queue)) {
+ gtk_widget_set_sensitive (GTK_WIDGET (ev_window), FALSE);
+ ev_window_print_cancel (ev_window);
+ } else {
+ gtk_widget_destroy (GTK_WIDGET (ev_window));
+ }
+ break;
+ case GTK_RESPONSE_CANCEL:
+ default:
+ ev_window->priv->close_after_print = FALSE;
+ }
+}
+
+static gboolean
+ev_window_check_print_queue (EvWindow *ev_window)
+{
+ GtkWidget *dialog;
+ gchar *text, *markup;
+ gint n_print_jobs;
+
+ n_print_jobs = ev_window->priv->print_queue ?
+ g_queue_get_length (ev_window->priv->print_queue) : 0;
+
+ if (n_print_jobs == 0)
+ return FALSE;
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (ev_window),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_NONE,
+ NULL);
+ if (n_print_jobs == 1) {
+ EvPrintOperation *op;
+ const gchar *job_name;
+
+ op = g_queue_peek_tail (ev_window->priv->print_queue);
+ job_name = ev_print_operation_get_job_name (op);
+
+ text = g_strdup_printf (_("Wait until print job ā€œ%sā€ finishes before closing?"),
+ job_name);
+ } else {
+ text = g_strdup_printf (_("There are %d print jobs active. "
+ "Wait until print finishes before closing?"),
+ n_print_jobs);
+ }
+
+ markup = g_strdup_printf ("<b>%s</b>", text);
+ g_free (text);
+
+ gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), markup);
+ g_free (markup);
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s",
+ _("If you close the window, pending print "
+ "jobs will not be printed."));
+
+ gtk_dialog_add_buttons (GTK_DIALOG (dialog),
+ _("Cancel _print and Close"),
+ GTK_RESPONSE_NO,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ _("Close _after Printing"),
+ GTK_RESPONSE_YES,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_YES,
+ GTK_RESPONSE_NO,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (print_jobs_confirmation_dialog_response),
+ ev_window);
+ gtk_widget_show (dialog);
+
+ return TRUE;
+}
+
+static gboolean
+ev_window_close (EvWindow *ev_window)
+{
+ if (EV_WINDOW_IS_PRESENTATION (ev_window)) {
+ gint current_page;
+
+ /* Save current page */
+ current_page = ev_view_presentation_get_current_page (
+ EV_VIEW_PRESENTATION (ev_window->priv->presentation_view));
+ ev_document_model_set_page (ev_window->priv->model, current_page);
+ }
+
+ if (ev_window_check_document_modified (ev_window))
+ return FALSE;
+
+ if (ev_window_check_print_queue (ev_window))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+ev_window_cmd_file_close_window (GtkAction *action, EvWindow *ev_window)
+{
+ if (ev_window_close (ev_window))
+ gtk_widget_destroy (GTK_WIDGET (ev_window));
+}
+
+static void
+ev_window_cmd_focus_page_selector (GtkAction *act, EvWindow *window)
+{
+ GtkAction *action;
+
+ update_chrome_flag (window, EV_CHROME_RAISE_TOOLBAR, TRUE);
+ ev_window_set_action_sensitive (window, "ViewToolbar", FALSE);
+ update_chrome_visibility (window);
+
+ action = gtk_action_group_get_action (window->priv->action_group,
+ PAGE_SELECTOR_ACTION);
+ ev_page_action_grab_focus (EV_PAGE_ACTION (action));
+}
+
+static void
+ev_window_cmd_scroll_forward (GtkAction *action, EvWindow *window)
+{
+ ev_view_scroll (EV_VIEW (window->priv->view), GTK_SCROLL_PAGE_FORWARD, FALSE);
+}
+
+static void
+ev_window_cmd_scroll_backward (GtkAction *action, EvWindow *window)
+{
+ ev_view_scroll (EV_VIEW (window->priv->view), GTK_SCROLL_PAGE_BACKWARD, FALSE);
+}
+
+static void
+ev_window_cmd_continuous (GtkAction *action, EvWindow *ev_window)
+{
+ gboolean continuous;
+
+ ev_window_stop_presentation (ev_window, TRUE);
+ continuous = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+ ev_document_model_set_continuous (ev_window->priv->model, continuous);
+}
+
+static void
+ev_window_cmd_dual (GtkAction *action, EvWindow *ev_window)
+{
+ gboolean dual_page;
+
+ ev_window_stop_presentation (ev_window, TRUE);
+ dual_page = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+ ev_document_model_set_dual_page (ev_window->priv->model, dual_page);
+}
+
+static void
+ev_window_cmd_view_best_fit (GtkAction *action, EvWindow *ev_window)
+{
+ ev_window_stop_presentation (ev_window, TRUE);
+
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
+ ev_document_model_set_sizing_mode (ev_window->priv->model, EV_SIZING_BEST_FIT);
+ } else {
+ ev_document_model_set_sizing_mode (ev_window->priv->model, EV_SIZING_FREE);
+ }
+ ev_window_update_actions (ev_window);
+}
+
+static void
+ev_window_cmd_view_page_width (GtkAction *action, EvWindow *ev_window)
+{
+ ev_window_stop_presentation (ev_window, TRUE);
+
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) {
+ ev_document_model_set_sizing_mode (ev_window->priv->model, EV_SIZING_FIT_WIDTH);
+ } else {
+ ev_document_model_set_sizing_mode (ev_window->priv->model, EV_SIZING_FREE);
+ }
+ ev_window_update_actions (ev_window);
+}
+
+
+static void
+ev_window_cmd_edit_select_all (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ ev_view_select_all (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+ev_window_cmd_edit_find (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (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;
+ }
+
+ 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_cmd_edit_find_next (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ update_chrome_flag (ev_window, EV_CHROME_FINDBAR, TRUE);
+ update_chrome_visibility (ev_window);
+ gtk_widget_grab_focus (ev_window->priv->find_bar);
+ ev_view_find_next (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+ev_window_cmd_edit_find_previous (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ update_chrome_flag (ev_window, EV_CHROME_FINDBAR, TRUE);
+ update_chrome_visibility (ev_window);
+ gtk_widget_grab_focus (ev_window->priv->find_bar);
+ ev_view_find_previous (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+ev_window_cmd_edit_copy (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ ev_view_copy (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+ev_window_sidebar_position_change_cb (GObject *object,
+ GParamSpec *pspec,
+ EvWindow *ev_window)
+{
+ if (ev_window->priv->metadata && !ev_window_is_empty (ev_window))
+ ev_metadata_set_int (ev_window->priv->metadata, "sidebar_size",
+ gtk_paned_get_position (GTK_PANED (object)));
+}
+
+static void
+ev_window_update_fullscreen_action (EvWindow *window)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (window->priv->action_group, "ViewFullscreen");
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_fullscreen), window);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ ev_document_model_get_fullscreen (window->priv->model));
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_fullscreen), window);
+}
+
+static void
+fullscreen_toolbar_setup_item_properties (GtkUIManager *ui_manager)
+{
+ GtkWidget *item;
+
+ item = gtk_ui_manager_get_widget (ui_manager, "/FullscreenToolbar/GoPreviousPage");
+ g_object_set (item, "is-important", FALSE, NULL);
+
+ item = gtk_ui_manager_get_widget (ui_manager, "/FullscreenToolbar/GoNextPage");
+ g_object_set (item, "is-important", FALSE, NULL);
+
+ item = gtk_ui_manager_get_widget (ui_manager, "/FullscreenToolbar/StartPresentation");
+ g_object_set (item, "is-important", TRUE, NULL);
+
+ item = gtk_ui_manager_get_widget (ui_manager, "/FullscreenToolbar/LeaveFullscreen");
+ g_object_set (item, "is-important", TRUE, NULL);
+}
+
+static void
+fullscreen_toolbar_remove_shadow (GtkWidget *toolbar)
+{
+ static gboolean done = FALSE;
+
+ if (!done) {
+ gtk_rc_parse_string (
+ "\n"
+ " style \"fullscreen-toolbar-style\"\n"
+ " {\n"
+ " GtkToolbar::shadow-type=GTK_SHADOW_NONE\n"
+ " }\n"
+ "\n"
+ " widget \"*.fullscreen-toolbar\" style \"fullscreen-toolbar-style\"\n"
+ "\n");
+ done = TRUE;
+ }
+
+ gtk_widget_set_name (toolbar, "fullscreen-toolbar");
+}
+
+static void
+ev_window_run_fullscreen (EvWindow *window)
+{
+ gboolean fullscreen_window = TRUE;
+
+ if (ev_document_model_get_fullscreen (window->priv->model))
+ return;
+
+ if (!window->priv->fullscreen_toolbar) {
+ window->priv->fullscreen_toolbar =
+ gtk_ui_manager_get_widget (window->priv->ui_manager,
+ "/FullscreenToolbar");
+
+ gtk_toolbar_set_style (GTK_TOOLBAR (window->priv->fullscreen_toolbar),
+ GTK_TOOLBAR_BOTH_HORIZ);
+ fullscreen_toolbar_remove_shadow (window->priv->fullscreen_toolbar);
+ fullscreen_toolbar_setup_item_properties (window->priv->ui_manager);
+
+ gtk_box_pack_start (GTK_BOX (window->priv->main_box),
+ window->priv->fullscreen_toolbar,
+ FALSE, FALSE, 0);
+ gtk_box_reorder_child (GTK_BOX (window->priv->main_box),
+ window->priv->fullscreen_toolbar, 1);
+ }
+
+ if (EV_WINDOW_IS_PRESENTATION (window)) {
+ ev_window_stop_presentation (window, FALSE);
+ fullscreen_window = FALSE;
+ }
+
+ g_object_set (G_OBJECT (window->priv->scrolled_window),
+ "shadow-type", GTK_SHADOW_NONE,
+ NULL);
+
+ ev_document_model_set_fullscreen (window->priv->model, TRUE);
+ ev_window_update_fullscreen_action (window);
+
+ /* If the user doesn't have the main toolbar he/she won't probably want
+ * the toolbar in fullscreen mode. See bug #483048
+ */
+ update_chrome_flag (window, EV_CHROME_FULLSCREEN_TOOLBAR,
+ (window->priv->chrome & EV_CHROME_TOOLBAR) != 0);
+ update_chrome_visibility (window);
+
+ if (fullscreen_window)
+ gtk_window_fullscreen (GTK_WINDOW (window));
+ gtk_widget_grab_focus (window->priv->view);
+
+ if (window->priv->metadata && !ev_window_is_empty (window))
+ ev_metadata_set_boolean (window->priv->metadata, "fullscreen", TRUE);
+}
+
+static void
+ev_window_stop_fullscreen (EvWindow *window,
+ gboolean unfullscreen_window)
+{
+ if (!ev_document_model_get_fullscreen (window->priv->model))
+ return;
+
+ g_object_set (G_OBJECT (window->priv->scrolled_window),
+ "shadow-type", GTK_SHADOW_IN,
+ NULL);
+
+ ev_document_model_set_fullscreen (window->priv->model, FALSE);
+ ev_window_update_fullscreen_action (window);
+ update_chrome_flag (window, EV_CHROME_FULLSCREEN_TOOLBAR, FALSE);
+ update_chrome_visibility (window);
+ if (unfullscreen_window)
+ gtk_window_unfullscreen (GTK_WINDOW (window));
+
+ if (window->priv->metadata && !ev_window_is_empty (window))
+ ev_metadata_set_boolean (window->priv->metadata, "fullscreen", FALSE);
+}
+
+static void
+ev_window_cmd_view_fullscreen (GtkAction *action, EvWindow *window)
+{
+ gboolean fullscreen;
+
+ fullscreen = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+ if (fullscreen) {
+ ev_window_run_fullscreen (window);
+ } else {
+ ev_window_stop_fullscreen (window, TRUE);
+ }
+}
+
+static void
+ev_window_update_presentation_action (EvWindow *window)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (window->priv->action_group, "ViewPresentation");
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_presentation), window);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ EV_WINDOW_IS_PRESENTATION (window));
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_presentation), window);
+}
+
+static void
+ev_window_view_presentation_finished (EvWindow *window)
+{
+ ev_window_stop_presentation (window, TRUE);
+}
+
+static void
+ev_window_run_presentation (EvWindow *window)
+{
+ gboolean fullscreen_window = TRUE;
+ guint current_page;
+ guint rotation;
+ gboolean inverted_colors;
+
+ if (EV_WINDOW_IS_PRESENTATION (window))
+ return;
+
+ if (ev_document_model_get_fullscreen (window->priv->model)) {
+ ev_window_stop_fullscreen (window, FALSE);
+ fullscreen_window = FALSE;
+ }
+
+ current_page = ev_document_model_get_page (window->priv->model);
+ rotation = ev_document_model_get_rotation (window->priv->model);
+ inverted_colors = ev_document_model_get_inverted_colors (window->priv->model);
+ window->priv->presentation_view = ev_view_presentation_new (window->priv->document,
+ current_page,
+ rotation,
+ inverted_colors);
+ g_signal_connect_swapped (window->priv->presentation_view, "finished",
+ G_CALLBACK (ev_window_view_presentation_finished),
+ window);
+
+ gtk_box_pack_start (GTK_BOX (window->priv->main_box),
+ window->priv->presentation_view,
+ TRUE, TRUE, 0);
+
+ gtk_widget_hide (window->priv->hpaned);
+ ev_window_update_presentation_action (window);
+ update_chrome_visibility (window);
+
+ gtk_widget_grab_focus (window->priv->presentation_view);
+ if (fullscreen_window)
+ gtk_window_fullscreen (GTK_WINDOW (window));
+
+ gtk_widget_show (window->priv->presentation_view);
+
+ ev_application_screensaver_disable (EV_APP);
+
+ if (window->priv->metadata && !ev_window_is_empty (window))
+ ev_metadata_set_boolean (window->priv->metadata, "presentation", TRUE);
+}
+
+static void
+ev_window_stop_presentation (EvWindow *window,
+ gboolean unfullscreen_window)
+{
+ guint current_page;
+
+ if (!EV_WINDOW_IS_PRESENTATION (window))
+ return;
+
+ current_page = ev_view_presentation_get_current_page (EV_VIEW_PRESENTATION (window->priv->presentation_view));
+ ev_document_model_set_page (window->priv->model, current_page);
+
+ gtk_container_remove (GTK_CONTAINER (window->priv->main_box),
+ window->priv->presentation_view);
+ window->priv->presentation_view = NULL;
+
+ gtk_widget_show (window->priv->hpaned);
+ ev_window_update_presentation_action (window);
+ update_chrome_visibility (window);
+ if (unfullscreen_window)
+ gtk_window_unfullscreen (GTK_WINDOW (window));
+
+ gtk_widget_grab_focus (window->priv->view);
+
+ ev_application_screensaver_enable (EV_APP);
+
+ if (window->priv->metadata && !ev_window_is_empty (window))
+ ev_metadata_set_boolean (window->priv->metadata, "presentation", FALSE);
+}
+
+static void
+ev_window_cmd_view_presentation (GtkAction *action, EvWindow *window)
+{
+ gboolean presentation;
+
+ presentation = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+ if (presentation) {
+ ev_window_run_presentation (window);
+ }
+}
+
+static void
+ev_window_setup_gtk_settings (EvWindow *window)
+{
+ GtkSettings *settings;
+ GdkScreen *screen;
+ gchar *menubar_accel_accel;
+
+ screen = gtk_window_get_screen (GTK_WINDOW (window));
+ settings = gtk_settings_get_for_screen (screen);
+
+ g_object_get (settings,
+ "gtk-menu-bar-accel", &menubar_accel_accel,
+ NULL);
+ if (menubar_accel_accel != NULL && menubar_accel_accel[0] != '\0') {
+ gtk_accelerator_parse (menubar_accel_accel,
+ &window->priv->menubar_accel_keyval,
+ &window->priv->menubar_accel_modifier);
+ if (window->priv->menubar_accel_keyval == 0) {
+ g_warning ("Failed to parse menu bar accelerator '%s'\n",
+ menubar_accel_accel);
+ }
+ } else {
+ window->priv->menubar_accel_keyval = 0;
+ window->priv->menubar_accel_modifier = 0;
+ }
+
+ g_free (menubar_accel_accel);
+}
+
+static void
+ev_window_update_max_min_scale (EvWindow *window)
+{
+ gdouble dpi;
+ GtkAction *action;
+ gdouble min_width, min_height;
+ gdouble width, height;
+ gdouble max_scale;
+ gint rotation = ev_document_model_get_rotation (window->priv->model);
+
+ if (!window->priv->document)
+ return;
+
+ dpi = get_screen_dpi (window) / 72.0;
+
+ ev_document_get_min_page_size (window->priv->document, &min_width, &min_height);
+ width = (rotation == 0 || rotation == 180) ? min_width : min_height;
+ height = (rotation == 0 || rotation == 180) ? min_height : min_width;
+ max_scale = sqrt (PAGE_CACHE_SIZE / (width * dpi * 4 * height * dpi));
+
+ action = gtk_action_group_get_action (window->priv->action_group,
+ ZOOM_CONTROL_ACTION);
+ ephy_zoom_action_set_max_zoom_level (EPHY_ZOOM_ACTION (action), max_scale * dpi);
+
+ ev_document_model_set_min_scale (window->priv->model, MIN_SCALE * dpi);
+ ev_document_model_set_max_scale (window->priv->model, max_scale * dpi);
+}
+
+static void
+ev_window_screen_changed (GtkWidget *widget,
+ GdkScreen *old_screen)
+{
+ EvWindow *window = EV_WINDOW (widget);
+ GdkScreen *screen;
+
+ screen = gtk_widget_get_screen (widget);
+ if (screen == old_screen)
+ return;
+
+ ev_window_setup_gtk_settings (window);
+ ev_window_update_max_min_scale (window);
+
+ if (GTK_WIDGET_CLASS (ev_window_parent_class)->screen_changed) {
+ GTK_WIDGET_CLASS (ev_window_parent_class)->screen_changed (widget, old_screen);
+ }
+}
+
+static gboolean
+ev_window_state_event (GtkWidget *widget,
+ GdkEventWindowState *event)
+{
+ EvWindow *window = EV_WINDOW (widget);
+
+ if (GTK_WIDGET_CLASS (ev_window_parent_class)->window_state_event) {
+ GTK_WIDGET_CLASS (ev_window_parent_class)->window_state_event (widget, event);
+ }
+
+ if ((event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) == 0)
+ return FALSE;
+
+ if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) {
+ if (ev_document_model_get_fullscreen (window->priv->model) || EV_WINDOW_IS_PRESENTATION (window))
+ return FALSE;
+
+ ev_window_run_fullscreen (window);
+ } else {
+ if (ev_document_model_get_fullscreen (window->priv->model))
+ ev_window_stop_fullscreen (window, FALSE);
+ else if (EV_WINDOW_IS_PRESENTATION (window))
+ ev_window_stop_presentation (window, FALSE);
+ }
+
+ return FALSE;
+}
+
+static void
+ev_window_set_page_mode (EvWindow *window,
+ EvWindowPageMode page_mode)
+{
+ GtkWidget *child = NULL;
+ GtkWidget *real_child;
+
+ if (window->priv->page_mode == page_mode)
+ return;
+
+ window->priv->page_mode = page_mode;
+
+ switch (page_mode) {
+ case PAGE_MODE_DOCUMENT:
+ child = window->priv->view;
+ break;
+ case PAGE_MODE_PASSWORD:
+ child = window->priv->password_view;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ real_child = gtk_bin_get_child (GTK_BIN (window->priv->scrolled_window));
+ if (child != real_child) {
+ gtk_container_remove (GTK_CONTAINER (window->priv->scrolled_window),
+ real_child);
+ gtk_container_add (GTK_CONTAINER (window->priv->scrolled_window),
+ child);
+ }
+ ev_window_update_actions (window);
+}
+
+
+static void
+ev_window_cmd_edit_rotate_left (GtkAction *action, EvWindow *ev_window)
+{
+ gint rotation = ev_document_model_get_rotation (ev_window->priv->model);
+
+ ev_document_model_set_rotation (ev_window->priv->model, rotation - 90);
+}
+
+static void
+ev_window_cmd_edit_rotate_right (GtkAction *action, EvWindow *ev_window)
+{
+ gint rotation = ev_document_model_get_rotation (ev_window->priv->model);
+
+ ev_document_model_set_rotation (ev_window->priv->model, rotation + 90);
+}
+
+static void
+ev_window_cmd_view_inverted_colors (GtkAction *action, EvWindow *ev_window)
+{
+ gboolean inverted_colors = ev_document_model_get_inverted_colors (ev_window->priv->model);
+
+ ev_document_model_set_inverted_colors (ev_window->priv->model, !inverted_colors);
+}
+
+static void
+ev_window_cmd_edit_toolbar_cb (GtkDialog *dialog,
+ gint response,
+ EvWindow *ev_window)
+{
+ EggEditableToolbar *toolbar;
+ gchar *toolbars_file;
+
+ toolbar = EGG_EDITABLE_TOOLBAR (ev_window->priv->toolbar);
+ egg_editable_toolbar_set_edit_mode (toolbar, FALSE);
+
+ toolbars_file = g_build_filename (ev_application_get_dot_dir (EV_APP, TRUE),
+ "evince_toolbar.xml", NULL);
+ egg_toolbars_model_save_toolbars (egg_editable_toolbar_get_model (toolbar),
+ toolbars_file, "1.0");
+ g_free (toolbars_file);
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+ev_window_cmd_edit_toolbar (GtkAction *action, EvWindow *ev_window)
+{
+ GtkWidget *dialog;
+ GtkWidget *editor;
+ GtkWidget *content_area;
+ EggEditableToolbar *toolbar;
+
+ dialog = gtk_dialog_new_with_buttons (_("Toolbar Editor"),
+ GTK_WINDOW (ev_window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CLOSE,
+ GTK_RESPONSE_CLOSE,
+ NULL);
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE);
+ gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dialog)), 5);
+ gtk_box_set_spacing (GTK_BOX (content_area), 2);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_window_set_default_size (GTK_WINDOW (dialog), 500, 400);
+
+ toolbar = EGG_EDITABLE_TOOLBAR (ev_window->priv->toolbar);
+ editor = egg_toolbar_editor_new (ev_window->priv->ui_manager,
+ egg_editable_toolbar_get_model (toolbar));
+
+ gtk_container_set_border_width (GTK_CONTAINER (editor), 5);
+ gtk_box_set_spacing (GTK_BOX (EGG_TOOLBAR_EDITOR (editor)), 5);
+
+ gtk_container_add (GTK_CONTAINER (content_area), editor);
+
+ egg_editable_toolbar_set_edit_mode (toolbar, TRUE);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (ev_window_cmd_edit_toolbar_cb),
+ ev_window);
+ gtk_widget_show_all (dialog);
+}
+
+static void
+ev_window_cmd_edit_save_settings (GtkAction *action, EvWindow *ev_window)
+{
+ EvWindowPrivate *priv = ev_window->priv;
+ EvDocumentModel *model = priv->model;
+ GSettings *settings = priv->default_settings;
+
+ g_settings_set_boolean (settings, "continuous",
+ ev_document_model_get_continuous (model));
+ g_settings_set_boolean (settings, "dual-page",
+ ev_document_model_get_dual_page (model));
+ g_settings_set_boolean (settings, "fullscreen",
+ ev_document_model_get_fullscreen (model));
+ g_settings_set_boolean (settings, "inverted-colors",
+ ev_document_model_get_inverted_colors (model));
+ g_settings_set_enum (settings, "sizing-mode",
+ ev_document_model_get_sizing_mode (model));
+ g_settings_set_boolean (settings, "show-toolbar",
+ gtk_widget_get_visible (priv->toolbar));
+ g_settings_set_boolean (settings, "show-sidebar",
+ gtk_widget_get_visible (priv->sidebar));
+ g_settings_set_int (settings, "sidebar-size",
+ gtk_paned_get_position (GTK_PANED (priv->hpaned)));
+ g_settings_set_string (settings, "sidebar-page",
+ ev_window_sidebar_get_current_page_id (ev_window));
+ g_settings_apply (settings);
+}
+
+static void
+ev_window_cmd_view_zoom_in (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ ev_document_model_set_sizing_mode (ev_window->priv->model, EV_SIZING_FREE);
+ ev_view_zoom_in (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+ev_window_cmd_view_zoom_out (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ ev_document_model_set_sizing_mode (ev_window->priv->model, EV_SIZING_FREE);
+ ev_view_zoom_out (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+ev_window_cmd_go_previous_page (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ ev_view_previous_page (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+ev_window_cmd_go_next_page (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ ev_view_next_page (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+ev_window_cmd_go_first_page (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ ev_document_model_set_page (ev_window->priv->model, 0);
+}
+
+static void
+ev_window_cmd_go_last_page (GtkAction *action, EvWindow *ev_window)
+{
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ ev_document_model_set_page (ev_window->priv->model,
+ ev_document_get_n_pages (ev_window->priv->document) - 1);
+}
+
+static void
+ev_window_cmd_go_forward (GtkAction *action, EvWindow *ev_window)
+{
+ int n_pages, current_page;
+
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ n_pages = ev_document_get_n_pages (ev_window->priv->document);
+ current_page = ev_document_model_get_page (ev_window->priv->model);
+
+ if (current_page + 10 < n_pages) {
+ ev_document_model_set_page (ev_window->priv->model, current_page + 10);
+ }
+}
+
+static void
+ev_window_cmd_go_backward (GtkAction *action, EvWindow *ev_window)
+{
+ int current_page;
+
+ g_return_if_fail (EV_IS_WINDOW (ev_window));
+
+ current_page = ev_document_model_get_page (ev_window->priv->model);
+
+ if (current_page - 10 >= 0) {
+ ev_document_model_set_page (ev_window->priv->model, current_page - 10);
+ }
+}
+
+static void
+ev_window_cmd_view_reload (GtkAction *action, EvWindow *ev_window)
+{
+ ev_window_reload_document (ev_window, NULL);
+}
+
+static void
+ev_window_cmd_view_autoscroll (GtkAction *action, EvWindow *ev_window)
+{
+ ev_view_autoscroll_start (EV_VIEW (ev_window->priv->view));
+}
+
+#if OFFLINE_HELP_ENABLED
+#define EV_HELP "ghelp:evince"
+#else
+#define EV_HELP "http://library.mate.org/users/evince/stable/"
+#endif
+
+static void
+ev_window_cmd_help_contents (GtkAction *action, EvWindow *ev_window)
+{
+ GError *error = NULL;
+
+ gtk_show_uri (gtk_window_get_screen (GTK_WINDOW (ev_window)),
+ EV_HELP,
+ gtk_get_current_event_time (),
+ &error);
+ if (error) {
+ ev_window_error_message (ev_window, error,
+ "%s", _("There was an error displaying help"));
+ g_error_free (error);
+ }
+}
+
+static void
+ev_window_cmd_leave_fullscreen (GtkAction *action, EvWindow *window)
+{
+ ev_window_stop_fullscreen (window, TRUE);
+}
+
+static void
+ev_window_cmd_start_presentation (GtkAction *action, EvWindow *window)
+{
+ ev_window_run_presentation (window);
+}
+
+static void
+ev_window_cmd_escape (GtkAction *action, EvWindow *window)
+{
+ GtkWidget *widget;
+
+ ev_view_autoscroll_stop (EV_VIEW (window->priv->view));
+
+ 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);
+ gtk_widget_grab_focus (window->priv->view);
+ } else {
+ gboolean fullscreen;
+
+ fullscreen = ev_document_model_get_fullscreen (window->priv->model);
+
+ if (fullscreen) {
+ ev_window_stop_fullscreen (window, TRUE);
+ } else if (EV_WINDOW_IS_PRESENTATION (window)) {
+ ev_window_stop_presentation (window, TRUE);
+ gtk_widget_grab_focus (window->priv->view);
+ } else {
+ gtk_widget_grab_focus (window->priv->view);
+ }
+
+ if (fullscreen && EV_WINDOW_IS_PRESENTATION (window))
+ g_warning ("Both fullscreen and presentation set somehow");
+ }
+}
+
+static void
+save_sizing_mode (EvWindow *window)
+{
+ EvSizingMode mode;
+ GEnumValue *enum_value;
+
+ if (!window->priv->metadata || ev_window_is_empty (window))
+ return;
+
+ mode = ev_document_model_get_sizing_mode (window->priv->model);
+ enum_value = g_enum_get_value (g_type_class_peek (EV_TYPE_SIZING_MODE), mode);
+ ev_metadata_set_string (window->priv->metadata, "sizing_mode",
+ enum_value->value_nick);
+}
+
+static void
+ev_window_document_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvWindow *ev_window)
+{
+ ev_window_set_document (ev_window,
+ ev_document_model_get_document (model));
+}
+
+static void
+ev_window_sizing_mode_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvWindow *ev_window)
+{
+ EvSizingMode sizing_mode = ev_document_model_get_sizing_mode (model);
+
+ g_object_set (ev_window->priv->scrolled_window,
+ "hscrollbar-policy",
+ sizing_mode == EV_SIZING_FREE ?
+ GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER,
+ "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
+ NULL);
+
+ update_sizing_buttons (ev_window);
+ save_sizing_mode (ev_window);
+}
+
+static void
+ev_window_zoom_changed_cb (EvDocumentModel *model, GParamSpec *pspec, EvWindow *ev_window)
+{
+ ev_window_update_actions (ev_window);
+
+ if (!ev_window->priv->metadata)
+ return;
+
+ if (ev_document_model_get_sizing_mode (model) == EV_SIZING_FREE && !ev_window_is_empty (ev_window)) {
+ gdouble zoom;
+
+ zoom = ev_document_model_get_scale (model);
+ zoom *= 72.0 / get_screen_dpi (ev_window);
+ ev_metadata_set_double (ev_window->priv->metadata, "zoom", zoom);
+ }
+}
+
+static void
+ev_window_update_continuous_action (EvWindow *window)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (window->priv->action_group, "ViewContinuous");
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (ev_window_cmd_continuous), window);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ ev_document_model_get_continuous (window->priv->model));
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (ev_window_cmd_continuous), window);
+}
+
+static void
+ev_window_update_dual_page_action (EvWindow *window)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (window->priv->action_group, "ViewDual");
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (ev_window_cmd_dual), window);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ ev_document_model_get_dual_page (window->priv->model));
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (ev_window_cmd_dual), window);
+}
+
+static void
+ev_window_continuous_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvWindow *ev_window)
+{
+ ev_window_update_continuous_action (ev_window);
+
+ if (ev_window->priv->metadata && !ev_window_is_empty (ev_window))
+ ev_metadata_set_boolean (ev_window->priv->metadata, "continuous",
+ ev_document_model_get_continuous (model));
+}
+
+static void
+ev_window_rotation_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvWindow *window)
+{
+ gint rotation = ev_document_model_get_rotation (model);
+
+ if (window->priv->metadata && !ev_window_is_empty (window))
+ ev_metadata_set_int (window->priv->metadata, "rotation",
+ rotation);
+
+ ev_window_update_max_min_scale (window);
+ ev_window_refresh_window_thumbnail (window);
+}
+
+static void
+ev_window_update_inverted_colors_action (EvWindow *window)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (window->priv->action_group, "ViewInvertedColors");
+ g_signal_handlers_block_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_inverted_colors), window);
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+ ev_document_model_get_inverted_colors (window->priv->model));
+ g_signal_handlers_unblock_by_func
+ (action, G_CALLBACK (ev_window_cmd_view_inverted_colors), window);
+}
+
+static void
+ev_window_inverted_colors_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvWindow *window)
+{
+ gboolean inverted_colors = ev_document_model_get_inverted_colors (model);
+
+ ev_window_update_inverted_colors_action (window);
+
+ if (window->priv->metadata && !ev_window_is_empty (window))
+ ev_metadata_set_boolean (window->priv->metadata, "inverted-colors",
+ inverted_colors);
+
+ ev_window_refresh_window_thumbnail (window);
+}
+
+static void
+ev_window_dual_mode_changed_cb (EvDocumentModel *model,
+ GParamSpec *pspec,
+ EvWindow *ev_window)
+{
+ ev_window_update_dual_page_action (ev_window);
+
+ if (ev_window->priv->metadata && !ev_window_is_empty (ev_window))
+ ev_metadata_set_boolean (ev_window->priv->metadata, "dual-page",
+ ev_document_model_get_dual_page (model));
+}
+
+static char *
+build_comments_string (EvDocument *document)
+{
+ gchar *comments = NULL;
+ EvDocumentBackendInfo info;
+
+ if (document && ev_document_get_backend_info (document, &info)) {
+ comments = g_strdup_printf (
+ _("Document Viewer\nUsing %s (%s)"),
+ info.name, info.version);
+ } else {
+ comments = g_strdup_printf (
+ _("Document Viewer"));
+ }
+
+ return comments;
+}
+
+static void
+ev_window_cmd_help_about (GtkAction *action, EvWindow *ev_window)
+{
+ const char *authors[] = {
+ "Martin Kretzschmar <[email protected]>",
+ "Jonathan Blandford <[email protected]>",
+ "Marco Pesenti Gritti <[email protected]>",
+ "Nickolay V. Shmyrev <[email protected]>",
+ "Bryan Clark <[email protected]>",
+ "Carlos Garcia Campos <[email protected]>",
+ "Wouter Bolsterlee <[email protected]>",
+ "Christian Persch <chpe" "\100" "mate.org>",
+ NULL
+ };
+
+ const char *documenters[] = {
+ "Nickolay V. Shmyrev <[email protected]>",
+ NULL
+ };
+
+ const char *license[] = {
+ N_("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.\n"),
+ N_("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.\n"),
+ N_("You should have received a copy of the GNU General Public License "
+ "along with Evince; if not, write to the Free Software Foundation, Inc., "
+ "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n")
+ };
+
+ char *license_trans;
+ char *comments;
+
+#ifdef ENABLE_NLS
+ const char **p;
+
+ for (p = authors; *p; ++p)
+ *p = _(*p);
+
+ for (p = documenters; *p; ++p)
+ *p = _(*p);
+#endif
+
+ license_trans = g_strconcat (_(license[0]), "\n", _(license[1]), "\n",
+ _(license[2]), "\n", NULL);
+
+ comments = build_comments_string (ev_window->priv->document);
+
+ gtk_show_about_dialog (
+ GTK_WINDOW (ev_window),
+ "name", _("Evince"),
+ "version", VERSION,
+ "copyright",
+ _("Ā© 1996ā€“2009 The Evince authors"),
+ "license", license_trans,
+ "website", "http://www.mate.org/projects/evince",
+ "comments", comments,
+ "authors", authors,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "logo-icon-name", "evince",
+ "wrap-license", TRUE,
+ NULL);
+
+ g_free (comments);
+ g_free (license_trans);
+}
+
+static void
+ev_window_view_toolbar_cb (GtkAction *action, EvWindow *ev_window)
+{
+ gboolean active;
+
+ active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
+ update_chrome_flag (ev_window, EV_CHROME_TOOLBAR, active);
+ update_chrome_visibility (ev_window);
+ if (ev_window->priv->metadata)
+ ev_metadata_set_boolean (ev_window->priv->metadata, "show_toolbar", active);
+}
+
+static void
+ev_window_view_sidebar_cb (GtkAction *action, EvWindow *ev_window)
+{
+ if (EV_WINDOW_IS_PRESENTATION (ev_window))
+ return;
+
+ update_chrome_flag (ev_window, EV_CHROME_SIDEBAR,
+ gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+ update_chrome_visibility (ev_window);
+}
+
+static void
+ev_window_sidebar_current_page_changed_cb (EvSidebar *ev_sidebar,
+ GParamSpec *pspec,
+ EvWindow *ev_window)
+{
+ if (ev_window->priv->metadata && !ev_window_is_empty (ev_window)) {
+ ev_metadata_set_string (ev_window->priv->metadata,
+ "sidebar_page",
+ ev_window_sidebar_get_current_page_id (ev_window));
+ }
+}
+
+static void
+ev_window_sidebar_visibility_changed_cb (EvSidebar *ev_sidebar,
+ GParamSpec *pspec,
+ EvWindow *ev_window)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (ev_window->priv->action_group, "ViewSidebar");
+
+ if (!EV_WINDOW_IS_PRESENTATION (ev_window)) {
+ gboolean visible = gtk_widget_get_visible (GTK_WIDGET (ev_sidebar));
+
+ gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), visible);
+
+ if (ev_window->priv->metadata)
+ ev_metadata_set_boolean (ev_window->priv->metadata, "sidebar_visibility",
+ visible);
+ }
+}
+
+static void
+view_menu_link_popup (EvWindow *ev_window,
+ EvLink *link)
+{
+ gboolean show_external = FALSE;
+ gboolean show_internal = FALSE;
+ GtkAction *action;
+
+ if (ev_window->priv->link)
+ g_object_unref (ev_window->priv->link);
+
+ if (link)
+ ev_window->priv->link = g_object_ref (link);
+ else
+ ev_window->priv->link = NULL;
+
+ if (ev_window->priv->link) {
+ EvLinkAction *ev_action;
+
+ ev_action = ev_link_get_action (link);
+ if (ev_action) {
+ switch (ev_link_action_get_action_type (ev_action)) {
+ case EV_LINK_ACTION_TYPE_GOTO_DEST:
+ case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
+ show_internal = TRUE;
+ break;
+ case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
+ case EV_LINK_ACTION_TYPE_LAUNCH:
+ show_external = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
+ "OpenLink");
+ gtk_action_set_visible (action, show_external);
+
+ action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
+ "CopyLinkAddress");
+ gtk_action_set_visible (action, show_external);
+
+ action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
+ "GoLink");
+ gtk_action_set_visible (action, show_internal);
+
+ action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
+ "OpenLinkNewWindow");
+ gtk_action_set_visible (action, show_internal);
+}
+
+static void
+view_menu_image_popup (EvWindow *ev_window,
+ EvImage *image)
+{
+ GtkAction *action;
+ gboolean show_image = FALSE;
+
+ if (ev_window->priv->image)
+ g_object_unref (ev_window->priv->image);
+
+ if (image)
+ ev_window->priv->image = g_object_ref (image);
+ else
+ ev_window->priv->image = NULL;
+
+ show_image = (ev_window->priv->image != NULL);
+
+ action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
+ "SaveImageAs");
+ gtk_action_set_visible (action, show_image);
+
+ action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
+ "CopyImage");
+ gtk_action_set_visible (action, show_image);
+}
+
+static void
+view_menu_annot_popup (EvWindow *ev_window,
+ EvAnnotation *annot)
+{
+ GtkAction *action;
+ gboolean show_annot = FALSE;
+
+ if (ev_window->priv->annot)
+ g_object_unref (ev_window->priv->annot);
+ ev_window->priv->annot = (annot) ? g_object_ref (annot) : NULL;
+
+ action = gtk_action_group_get_action (ev_window->priv->view_popup_action_group,
+ "AnnotProperties");
+ gtk_action_set_visible (action, (annot != NULL && EV_IS_ANNOTATION_MARKUP (annot)));
+
+ if (annot && EV_IS_ANNOTATION_ATTACHMENT (annot)) {
+ EvAttachment *attachment;
+
+ attachment = ev_annotation_attachment_get_attachment (EV_ANNOTATION_ATTACHMENT (annot));
+ if (attachment) {
+ show_annot = TRUE;
+ if (ev_window->priv->attach_list) {
+ g_list_foreach (ev_window->priv->attach_list,
+ (GFunc) g_object_unref, NULL);
+ g_list_free (ev_window->priv->attach_list);
+ ev_window->priv->attach_list = NULL;
+ }
+ ev_window->priv->attach_list =
+ g_list_prepend (ev_window->priv->attach_list,
+ g_object_ref (attachment));
+ }
+ }
+
+ action = gtk_action_group_get_action (ev_window->priv->attachment_popup_action_group,
+ "OpenAttachment");
+ gtk_action_set_visible (action, show_annot);
+
+ action = gtk_action_group_get_action (ev_window->priv->attachment_popup_action_group,
+ "SaveAttachmentAs");
+ gtk_action_set_visible (action, show_annot);
+}
+
+static gboolean
+view_menu_popup_cb (EvView *view,
+ GList *items,
+ EvWindow *ev_window)
+{
+ GList *l;
+ gboolean has_link = FALSE;
+ gboolean has_image = FALSE;
+ gboolean has_annot = FALSE;
+
+ for (l = items; l; l = g_list_next (l)) {
+ if (EV_IS_LINK (l->data)) {
+ view_menu_link_popup (ev_window, EV_LINK (l->data));
+ has_link = TRUE;
+ } else if (EV_IS_IMAGE (l->data)) {
+ view_menu_image_popup (ev_window, EV_IMAGE (l->data));
+ has_image = TRUE;
+ } else if (EV_IS_ANNOTATION (l->data)) {
+ view_menu_annot_popup (ev_window, EV_ANNOTATION (l->data));
+ has_annot = TRUE;
+ }
+ }
+
+ if (!has_link)
+ view_menu_link_popup (ev_window, NULL);
+ if (!has_image)
+ view_menu_image_popup (ev_window, NULL);
+ if (!has_annot)
+ view_menu_annot_popup (ev_window, NULL);
+
+ gtk_menu_popup (GTK_MENU (ev_window->priv->view_popup),
+ NULL, NULL, NULL, NULL,
+ 3, gtk_get_current_event_time ());
+ return TRUE;
+}
+
+static gboolean
+attachment_bar_menu_popup_cb (EvSidebarAttachments *attachbar,
+ GList *attach_list,
+ EvWindow *ev_window)
+{
+ GtkWidget *popup;
+
+ g_assert (attach_list != NULL);
+
+ if (ev_window->priv->attach_list) {
+ g_list_foreach (ev_window->priv->attach_list,
+ (GFunc) g_object_unref, NULL);
+ g_list_free (ev_window->priv->attach_list);
+ }
+
+ ev_window->priv->attach_list = attach_list;
+
+ popup = ev_window->priv->attachment_popup;
+
+ gtk_menu_popup (GTK_MENU (popup), NULL, NULL,
+ NULL, NULL,
+ 3, gtk_get_current_event_time ());
+
+ return TRUE;
+}
+
+static void
+ev_window_update_find_status_message (EvWindow *ev_window)
+{
+ gchar *message;
+
+ if (!ev_window->priv->find_job)
+ return;
+
+ if (ev_job_is_finished (ev_window->priv->find_job)) {
+ EvJobFind *job_find = EV_JOB_FIND (ev_window->priv->find_job);
+
+ if (ev_job_find_has_results (job_find)) {
+ gint n_results;
+
+ n_results = ev_job_find_get_n_results (job_find,
+ ev_document_model_get_page (ev_window->priv->model));
+ /* TRANS: Sometimes this could be better translated as
+ "%d hit(s) on this page". Therefore this string
+ contains plural cases. */
+ message = g_strdup_printf (ngettext ("%d found on this page",
+ "%d found on this page",
+ n_results),
+ n_results);
+ } else {
+ message = g_strdup (_("Not found"));
+ }
+ } else {
+ gdouble percent;
+
+ percent = ev_job_find_get_progress (EV_JOB_FIND (ev_window->priv->find_job));
+ message = g_strdup_printf (_("%3d%% remaining to search"),
+ (gint) ((1.0 - percent) * 100));
+ }
+
+ egg_find_bar_set_status_text (EGG_FIND_BAR (ev_window->priv->find_bar), message);
+ g_free (message);
+}
+
+static void
+ev_window_find_job_finished_cb (EvJobFind *job,
+ EvWindow *ev_window)
+{
+ ev_window_update_find_status_message (ev_window);
+}
+
+static void
+ev_window_find_job_updated_cb (EvJobFind *job,
+ gint page,
+ EvWindow *ev_window)
+{
+ ev_window_update_actions (ev_window);
+
+ ev_view_find_changed (EV_VIEW (ev_window->priv->view),
+ ev_job_find_get_results (job),
+ page);
+ ev_window_update_find_status_message (ev_window);
+}
+
+static void
+ev_window_clear_find_job (EvWindow *ev_window)
+{
+ if (ev_window->priv->find_job != NULL) {
+ if (!ev_job_is_finished (ev_window->priv->find_job))
+ ev_job_cancel (ev_window->priv->find_job);
+
+ g_signal_handlers_disconnect_by_func (ev_window->priv->find_job,
+ ev_window_find_job_finished_cb,
+ ev_window);
+ g_signal_handlers_disconnect_by_func (ev_window->priv->find_job,
+ ev_window_find_job_updated_cb,
+ ev_window);
+ g_object_unref (ev_window->priv->find_job);
+ ev_window->priv->find_job = NULL;
+ }
+}
+
+static void
+find_bar_previous_cb (EggFindBar *find_bar,
+ EvWindow *ev_window)
+{
+ ev_view_find_previous (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+find_bar_next_cb (EggFindBar *find_bar,
+ EvWindow *ev_window)
+{
+ ev_view_find_next (EV_VIEW (ev_window->priv->view));
+}
+
+static void
+find_bar_close_cb (EggFindBar *find_bar,
+ EvWindow *ev_window)
+{
+ 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);
+}
+
+static void
+find_bar_search_changed_cb (EggFindBar *find_bar,
+ GParamSpec *param,
+ EvWindow *ev_window)
+{
+ gboolean case_sensitive;
+ const char *search_string;
+
+ if (!ev_window->priv->document || !EV_IS_DOCUMENT_FIND (ev_window->priv->document))
+ return;
+
+ /* Either the string or case sensitivity could have changed. */
+ case_sensitive = egg_find_bar_get_case_sensitive (find_bar);
+ search_string = egg_find_bar_get_search_string (find_bar);
+
+ ev_view_find_search_changed (EV_VIEW (ev_window->priv->view));
+
+ ev_window_clear_find_job (ev_window);
+
+ if (search_string && search_string[0]) {
+ ev_window->priv->find_job = ev_job_find_new (ev_window->priv->document,
+ ev_document_model_get_page (ev_window->priv->model),
+ ev_document_get_n_pages (ev_window->priv->document),
+ search_string,
+ case_sensitive);
+ g_signal_connect (ev_window->priv->find_job, "finished",
+ G_CALLBACK (ev_window_find_job_finished_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->find_job, "updated",
+ G_CALLBACK (ev_window_find_job_updated_cb),
+ ev_window);
+ ev_job_scheduler_push_job (ev_window->priv->find_job, EV_JOB_PRIORITY_NONE);
+ } else {
+ ev_window_update_actions (ev_window);
+ egg_find_bar_set_status_text (EGG_FIND_BAR (ev_window->priv->find_bar),
+ NULL);
+ gtk_widget_queue_draw (GTK_WIDGET (ev_window->priv->view));
+ }
+}
+
+static void
+find_bar_visibility_changed_cb (EggFindBar *find_bar,
+ GParamSpec *param,
+ EvWindow *ev_window)
+{
+ gboolean visible;
+
+ visible = gtk_widget_get_visible (GTK_WIDGET (find_bar));
+
+ if (ev_window->priv->document &&
+ EV_IS_DOCUMENT_FIND (ev_window->priv->document)) {
+ ev_view_find_set_highlight_search (EV_VIEW (ev_window->priv->view), visible);
+ ev_view_find_search_changed (EV_VIEW (ev_window->priv->view));
+ ev_window_update_actions (ev_window);
+
+ if (visible)
+ find_bar_search_changed_cb (find_bar, NULL, ev_window);
+ else
+ egg_find_bar_set_status_text (EGG_FIND_BAR (ev_window->priv->find_bar), NULL);
+ }
+}
+
+static void
+find_bar_scroll (EggFindBar *find_bar,
+ GtkScrollType scroll,
+ EvWindow *ev_window)
+{
+ ev_view_scroll (EV_VIEW (ev_window->priv->view), scroll, FALSE);
+}
+
+static void
+zoom_control_changed_cb (EphyZoomAction *action,
+ float zoom,
+ EvWindow *ev_window)
+{
+ EvSizingMode mode;
+
+ if (zoom == EPHY_ZOOM_BEST_FIT) {
+ mode = EV_SIZING_BEST_FIT;
+ } else if (zoom == EPHY_ZOOM_FIT_WIDTH) {
+ mode = EV_SIZING_FIT_WIDTH;
+ } else {
+ mode = EV_SIZING_FREE;
+ }
+
+ ev_document_model_set_sizing_mode (ev_window->priv->model, mode);
+
+ if (mode == EV_SIZING_FREE) {
+ ev_document_model_set_scale (ev_window->priv->model,
+ zoom * get_screen_dpi (ev_window) / 72.0);
+ }
+}
+
+static void
+ev_window_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+
+{
+ EvWindow *window = EV_WINDOW (widget);
+ gchar **uris;
+ gint i = 0;
+ GSList *uri_list = NULL;
+ GtkWidget *source;
+
+ source = gtk_drag_get_source_widget (context);
+ if (source && widget == gtk_widget_get_toplevel (source)) {
+ gtk_drag_finish (context, FALSE, FALSE, time);
+ return;
+ }
+
+ uris = gtk_selection_data_get_uris (selection_data);
+ if (!uris) {
+ gtk_drag_finish (context, FALSE, FALSE, time);
+ return;
+ }
+
+ for (i = 0; uris[i]; i++) {
+ uri_list = g_slist_prepend (uri_list, (gpointer) uris[i]);
+ }
+
+ ev_application_open_uri_list (EV_APP, uri_list,
+ gtk_window_get_screen (GTK_WINDOW (window)),
+ 0);
+ gtk_drag_finish (context, TRUE, FALSE, time);
+
+ g_strfreev (uris);
+ g_slist_free (uri_list);
+}
+
+static void
+ev_window_finalize (GObject *object)
+{
+ G_OBJECT_CLASS (ev_window_parent_class)->finalize (object);
+
+ if (ev_window_n_copies == 0) {
+ ev_application_shutdown (EV_APP);
+ } else {
+ ev_window_n_copies--;
+ }
+}
+
+static void
+ev_window_dispose (GObject *object)
+{
+ EvWindow *window = EV_WINDOW (object);
+ EvWindowPrivate *priv = window->priv;
+ GObject *mpkeys = ev_application_get_media_keys (EV_APP);
+
+ if (mpkeys) {
+ g_signal_handlers_disconnect_by_func (mpkeys,
+ ev_window_media_player_key_pressed,
+ window);
+ }
+
+#ifdef ENABLE_DBUS
+ if (priv->dbus_object_id > 0) {
+ ev_window_emit_closed (window);
+ g_dbus_connection_unregister_object (ev_application_get_dbus_connection (EV_APP),
+ priv->dbus_object_id);
+ priv->dbus_object_id = 0;
+ }
+
+ if (priv->dbus_object_path) {
+ g_free (priv->dbus_object_path);
+ priv->dbus_object_path = NULL;
+ }
+#endif /* ENABLE_DBUS */
+
+ if (priv->metadata) {
+ g_object_unref (priv->metadata);
+ priv->metadata = NULL;
+ }
+
+ if (priv->setup_document_idle > 0) {
+ g_source_remove (priv->setup_document_idle);
+ priv->setup_document_idle = 0;
+ }
+
+#ifdef WITH_MATECONF
+ if (priv->mateconf_client) {
+ g_object_unref (priv->mateconf_client);
+ priv->mateconf_client = NULL;
+ }
+#endif
+
+ if (priv->monitor) {
+ g_object_unref (priv->monitor);
+ priv->monitor = NULL;
+ }
+
+ if (priv->title) {
+ ev_window_title_free (priv->title);
+ priv->title = NULL;
+ }
+
+ if (priv->ui_manager) {
+ g_object_unref (priv->ui_manager);
+ priv->ui_manager = NULL;
+ }
+
+ if (priv->action_group) {
+ g_object_unref (priv->action_group);
+ priv->action_group = NULL;
+ }
+
+ if (priv->view_popup_action_group) {
+ g_object_unref (priv->view_popup_action_group);
+ priv->view_popup_action_group = NULL;
+ }
+
+ if (priv->attachment_popup_action_group) {
+ g_object_unref (priv->attachment_popup_action_group);
+ priv->attachment_popup_action_group = NULL;
+ }
+
+ if (priv->recent_action_group) {
+ g_object_unref (priv->recent_action_group);
+ priv->recent_action_group = NULL;
+ }
+
+ if (priv->recent_manager) {
+ g_signal_handlers_disconnect_by_func (priv->recent_manager,
+ ev_window_setup_recent,
+ window);
+ priv->recent_manager = NULL;
+ }
+
+ if (priv->settings) {
+ g_object_unref (priv->settings);
+ priv->settings = NULL;
+ }
+
+ if (priv->default_settings) {
+ g_settings_apply (priv->default_settings);
+ g_object_unref (priv->default_settings);
+ priv->default_settings = NULL;
+ }
+
+ priv->recent_ui_id = 0;
+
+ if (priv->model) {
+ g_signal_handlers_disconnect_by_func (priv->model,
+ ev_window_page_changed_cb,
+ window);
+ g_object_unref (priv->model);
+ priv->model = NULL;
+ }
+
+ if (priv->document) {
+ g_object_unref (priv->document);
+ priv->document = NULL;
+ }
+
+ if (priv->view) {
+ g_object_unref (priv->view);
+ priv->view = NULL;
+ }
+
+ if (priv->password_view) {
+ g_object_unref (priv->password_view);
+ priv->password_view = NULL;
+ }
+
+ if (priv->load_job) {
+ 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);
+ }
+
+ if (priv->thumbnail_job) {
+ ev_window_clear_thumbnail_job (window);
+ }
+
+ if (priv->find_job) {
+ ev_window_clear_find_job (window);
+ }
+
+ if (priv->local_uri) {
+ ev_window_clear_local_uri (window);
+ priv->local_uri = NULL;
+ }
+
+ ev_window_clear_progress_idle (window);
+ if (priv->progress_cancellable) {
+ g_object_unref (priv->progress_cancellable);
+ priv->progress_cancellable = NULL;
+ }
+
+ ev_window_close_dialogs (window);
+
+ if (priv->link) {
+ g_object_unref (priv->link);
+ priv->link = NULL;
+ }
+
+ if (priv->image) {
+ g_object_unref (priv->image);
+ priv->image = NULL;
+ }
+
+ if (priv->annot) {
+ g_object_unref (priv->annot);
+ priv->annot = NULL;
+ }
+
+ if (priv->attach_list) {
+ g_list_foreach (priv->attach_list,
+ (GFunc) g_object_unref,
+ NULL);
+ g_list_free (priv->attach_list);
+ priv->attach_list = NULL;
+ }
+
+ if (priv->find_bar) {
+ g_signal_handlers_disconnect_by_func
+ (window->priv->find_bar,
+ G_CALLBACK (find_bar_close_cb),
+ window);
+ priv->find_bar = NULL;
+ }
+
+ if (priv->uri) {
+ g_free (priv->uri);
+ priv->uri = NULL;
+ }
+
+ if (priv->search_string) {
+ g_free (priv->search_string);
+ priv->search_string = NULL;
+ }
+
+ if (priv->dest) {
+ g_object_unref (priv->dest);
+ priv->dest = NULL;
+ }
+
+ if (priv->history) {
+ g_object_unref (priv->history);
+ priv->history = NULL;
+ }
+
+ if (priv->print_queue) {
+ g_queue_free (priv->print_queue);
+ priv->print_queue = NULL;
+ }
+
+ G_OBJECT_CLASS (ev_window_parent_class)->dispose (object);
+}
+
+static void
+menubar_deactivate_cb (GtkWidget *menubar,
+ EvWindow *window)
+{
+ g_signal_handlers_disconnect_by_func (menubar,
+ G_CALLBACK (menubar_deactivate_cb),
+ window);
+
+ gtk_menu_shell_deselect (GTK_MENU_SHELL (menubar));
+
+ update_chrome_visibility (window);
+}
+
+static gboolean
+ev_window_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ EvWindow *ev_window = EV_WINDOW (widget);
+ EvWindowPrivate *priv = ev_window->priv;
+ gboolean handled = FALSE;
+
+ /* Propagate the event to the view first
+ * It's needed to be able to type in
+ * annot popups windows
+ */
+ if (priv->view) {
+ g_object_ref (priv->view);
+ if (gtk_widget_is_sensitive (priv->view))
+ handled = gtk_widget_event (priv->view, (GdkEvent*) event);
+ g_object_unref (priv->view);
+ }
+
+ if (!handled && !EV_WINDOW_IS_PRESENTATION (ev_window)) {
+ guint modifier = event->state & gtk_accelerator_get_default_mod_mask ();
+
+ if (priv->menubar_accel_keyval != 0 &&
+ event->keyval == priv->menubar_accel_keyval &&
+ modifier == priv->menubar_accel_modifier) {
+ if (!gtk_widget_get_visible (priv->menubar)) {
+ g_signal_connect (priv->menubar, "deactivate",
+ G_CALLBACK (menubar_deactivate_cb),
+ ev_window);
+
+ gtk_widget_show (priv->menubar);
+ gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menubar),
+ FALSE);
+
+ handled = TRUE;
+ }
+ }
+ }
+
+ if (!handled)
+ handled = GTK_WIDGET_CLASS (ev_window_parent_class)->key_press_event (widget, event);
+
+ return handled;
+}
+
+static gboolean
+ev_window_delete_event (GtkWidget *widget,
+ GdkEventAny *event)
+{
+ return !ev_window_close (EV_WINDOW (widget));
+}
+
+static void
+ev_window_class_init (EvWindowClass *ev_window_class)
+{
+ GObjectClass *g_object_class = G_OBJECT_CLASS (ev_window_class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (ev_window_class);
+
+ g_object_class->dispose = ev_window_dispose;
+ g_object_class->finalize = ev_window_finalize;
+
+ widget_class->delete_event = ev_window_delete_event;
+ widget_class->key_press_event = ev_window_key_press_event;
+ widget_class->screen_changed = ev_window_screen_changed;
+ widget_class->window_state_event = ev_window_state_event;
+ widget_class->drag_data_received = ev_window_drag_data_received;
+
+ g_type_class_add_private (g_object_class, sizeof (EvWindowPrivate));
+}
+
+/* Normal items */
+static const GtkActionEntry entries[] = {
+ { "File", NULL, N_("_File") },
+ { "Edit", NULL, N_("_Edit") },
+ { "View", NULL, N_("_View") },
+ { "Go", NULL, N_("_Go") },
+ { "Help", NULL, N_("_Help") },
+
+ /* File menu */
+ { "FileOpen", GTK_STOCK_OPEN, N_("_Openā€¦"), "<control>O",
+ N_("Open an existing document"),
+ G_CALLBACK (ev_window_cmd_file_open) },
+ { "FileOpenCopy", NULL, N_("Op_en a Copy"), "<control>N",
+ N_("Open a copy of the current document in a new window"),
+ G_CALLBACK (ev_window_cmd_file_open_copy) },
+ { "FileSaveAs", GTK_STOCK_SAVE_AS, N_("_Save a Copyā€¦"), "<control>S",
+ N_("Save a copy of the current document"),
+ G_CALLBACK (ev_window_cmd_save_as) },
+ { "FilePrint", GTK_STOCK_PRINT, N_("_Printā€¦"), "<control>P",
+ N_("Print this document"),
+ G_CALLBACK (ev_window_cmd_file_print) },
+ { "FileProperties", GTK_STOCK_PROPERTIES, N_("P_roperties"), "<alt>Return", NULL,
+ G_CALLBACK (ev_window_cmd_file_properties) },
+ { "FileCloseWindow", GTK_STOCK_CLOSE, NULL, "<control>W", NULL,
+ G_CALLBACK (ev_window_cmd_file_close_window) },
+
+ /* Edit menu */
+ { "EditCopy", GTK_STOCK_COPY, NULL, "<control>C", NULL,
+ G_CALLBACK (ev_window_cmd_edit_copy) },
+ { "EditSelectAll", GTK_STOCK_SELECT_ALL, N_("Select _All"), "<control>A", NULL,
+ G_CALLBACK (ev_window_cmd_edit_select_all) },
+ { "EditFind", GTK_STOCK_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,
+ G_CALLBACK (ev_window_cmd_edit_find_previous) },
+ { "EditToolbar", NULL, N_("T_oolbar"), NULL, NULL,
+ G_CALLBACK (ev_window_cmd_edit_toolbar) },
+ { "EditRotateLeft", EV_STOCK_ROTATE_LEFT, N_("Rotate _Left"), "<control>Left", NULL,
+ G_CALLBACK (ev_window_cmd_edit_rotate_left) },
+ { "EditRotateRight", EV_STOCK_ROTATE_RIGHT, N_("Rotate _Right"), "<control>Right", NULL,
+ G_CALLBACK (ev_window_cmd_edit_rotate_right) },
+ { "EditSaveSettings", NULL, N_("Save Current Settings as _Default"), "<control>T", NULL,
+ G_CALLBACK (ev_window_cmd_edit_save_settings) },
+
+
+ /* View menu */
+ { "ViewZoomIn", GTK_STOCK_ZOOM_IN, NULL, "<control>plus",
+ N_("Enlarge the document"),
+ G_CALLBACK (ev_window_cmd_view_zoom_in) },
+ { "ViewZoomOut", GTK_STOCK_ZOOM_OUT, NULL, "<control>minus",
+ N_("Shrink the document"),
+ G_CALLBACK (ev_window_cmd_view_zoom_out) },
+ { "ViewReload", GTK_STOCK_REFRESH, N_("_Reload"), "<control>R",
+ N_("Reload the document"),
+ G_CALLBACK (ev_window_cmd_view_reload) },
+
+ { "ViewAutoscroll", GTK_STOCK_MEDIA_PLAY, N_("Auto_scroll"), NULL, NULL,
+ G_CALLBACK (ev_window_cmd_view_autoscroll) },
+
+ /* Go menu */
+ { "GoPreviousPage", GTK_STOCK_GO_UP, N_("_Previous Page"), "<control>Page_Up",
+ N_("Go to the previous page"),
+ G_CALLBACK (ev_window_cmd_go_previous_page) },
+ { "GoNextPage", GTK_STOCK_GO_DOWN, N_("_Next Page"), "<control>Page_Down",
+ N_("Go to the next page"),
+ G_CALLBACK (ev_window_cmd_go_next_page) },
+ { "GoFirstPage", GTK_STOCK_GOTO_TOP, N_("_First Page"), "<control>Home",
+ N_("Go to the first page"),
+ G_CALLBACK (ev_window_cmd_go_first_page) },
+ { "GoLastPage", GTK_STOCK_GOTO_BOTTOM, N_("_Last Page"), "<control>End",
+ N_("Go to the last page"),
+ G_CALLBACK (ev_window_cmd_go_last_page) },
+
+ /* Help menu */
+ { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1", NULL,
+ G_CALLBACK (ev_window_cmd_help_contents) },
+
+ { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL,
+ G_CALLBACK (ev_window_cmd_help_about) },
+
+ /* Toolbar-only */
+ { "LeaveFullscreen", GTK_STOCK_LEAVE_FULLSCREEN, N_("Leave Fullscreen"), NULL,
+ N_("Leave fullscreen mode"),
+ G_CALLBACK (ev_window_cmd_leave_fullscreen) },
+ { "StartPresentation", EV_STOCK_RUN_PRESENTATION, N_("Start Presentation"), NULL,
+ N_("Start a presentation"),
+ G_CALLBACK (ev_window_cmd_start_presentation) },
+
+ /* Accellerators */
+ { "Escape", NULL, "", "Escape", "",
+ G_CALLBACK (ev_window_cmd_escape) },
+ { "Slash", GTK_STOCK_FIND, NULL, "slash", NULL,
+ G_CALLBACK (ev_window_cmd_edit_find) },
+ { "F3", NULL, "", "F3", NULL,
+ G_CALLBACK (ev_window_cmd_edit_find_next) },
+ { "PageDown", NULL, "", "Page_Down", NULL,
+ G_CALLBACK (ev_window_cmd_scroll_forward) },
+ { "PageUp", NULL, "", "Page_Up", NULL,
+ G_CALLBACK (ev_window_cmd_scroll_backward) },
+ { "Space", NULL, "", "space", NULL,
+ G_CALLBACK (ev_window_cmd_scroll_forward) },
+ { "ShiftSpace", NULL, "", "<shift>space", NULL,
+ G_CALLBACK (ev_window_cmd_scroll_backward) },
+ { "BackSpace", NULL, "", "BackSpace", NULL,
+ G_CALLBACK (ev_window_cmd_scroll_backward) },
+ { "ShiftBackSpace", NULL, "", "<shift>BackSpace", NULL,
+ G_CALLBACK (ev_window_cmd_scroll_forward) },
+ { "Return", NULL, "", "Return", NULL,
+ G_CALLBACK (ev_window_cmd_scroll_forward) },
+ { "ShiftReturn", NULL, "", "<shift>Return", NULL,
+ G_CALLBACK (ev_window_cmd_scroll_backward) },
+ { "p", GTK_STOCK_GO_UP, "", "p", NULL,
+ G_CALLBACK (ev_window_cmd_go_previous_page) },
+ { "n", GTK_STOCK_GO_DOWN, "", "n", NULL,
+ G_CALLBACK (ev_window_cmd_go_next_page) },
+ { "Plus", GTK_STOCK_ZOOM_IN, NULL, "plus", NULL,
+ G_CALLBACK (ev_window_cmd_view_zoom_in) },
+ { "CtrlEqual", GTK_STOCK_ZOOM_IN, NULL, "<control>equal", NULL,
+ G_CALLBACK (ev_window_cmd_view_zoom_in) },
+ { "Equal", GTK_STOCK_ZOOM_IN, NULL, "equal", NULL,
+ G_CALLBACK (ev_window_cmd_view_zoom_in) },
+ { "Minus", GTK_STOCK_ZOOM_OUT, NULL, "minus", NULL,
+ G_CALLBACK (ev_window_cmd_view_zoom_out) },
+ { "FocusPageSelector", NULL, "", "<control>l", NULL,
+ G_CALLBACK (ev_window_cmd_focus_page_selector) },
+ { "GoBackwardFast", NULL, "", "<shift>Page_Up", NULL,
+ G_CALLBACK (ev_window_cmd_go_backward) },
+ { "GoForwardFast", NULL, "", "<shift>Page_Down", NULL,
+ G_CALLBACK (ev_window_cmd_go_forward) },
+ { "KpPlus", GTK_STOCK_ZOOM_IN, NULL, "KP_Add", NULL,
+ G_CALLBACK (ev_window_cmd_view_zoom_in) },
+ { "KpMinus", GTK_STOCK_ZOOM_OUT, NULL, "KP_Subtract", NULL,
+ G_CALLBACK (ev_window_cmd_view_zoom_out) },
+ { "CtrlKpPlus", GTK_STOCK_ZOOM_IN, NULL, "<control>KP_Add", NULL,
+ G_CALLBACK (ev_window_cmd_view_zoom_in) },
+ { "CtrlKpMinus", GTK_STOCK_ZOOM_OUT, NULL, "<control>KP_Subtract", NULL,
+ G_CALLBACK (ev_window_cmd_view_zoom_out) },
+ { "CtrlInsert", GTK_STOCK_COPY, NULL, "<control>Insert", NULL,
+ G_CALLBACK (ev_window_cmd_edit_copy) },
+};
+
+/* Toggle items */
+static const GtkToggleActionEntry toggle_entries[] = {
+ /* View Menu */
+ { "ViewToolbar", NULL, N_("_Toolbar"), NULL,
+ N_("Show or hide the toolbar"),
+ G_CALLBACK (ev_window_view_toolbar_cb), TRUE },
+ { "ViewSidebar", GTK_STOCK_INDEX, N_("Side _Pane"), "F9",
+ N_("Show or hide the side pane"),
+ G_CALLBACK (ev_window_view_sidebar_cb), TRUE },
+ { "ViewContinuous", EV_STOCK_VIEW_CONTINUOUS, N_("_Continuous"), NULL,
+ N_("Show the entire document"),
+ G_CALLBACK (ev_window_cmd_continuous), TRUE },
+ { "ViewDual", EV_STOCK_VIEW_DUAL, N_("_Dual"), NULL,
+ N_("Show two pages at once"),
+ G_CALLBACK (ev_window_cmd_dual), FALSE },
+ { "ViewFullscreen", GTK_STOCK_FULLSCREEN, N_("_Fullscreen"), "F11",
+ N_("Expand the window to fill the screen"),
+ G_CALLBACK (ev_window_cmd_view_fullscreen) },
+ { "ViewPresentation", EV_STOCK_RUN_PRESENTATION, N_("Pre_sentation"), "F5",
+ N_("Run document as a presentation"),
+ G_CALLBACK (ev_window_cmd_view_presentation) },
+ { "ViewBestFit", EV_STOCK_ZOOM_PAGE, N_("_Best Fit"), NULL,
+ N_("Make the current document fill the window"),
+ G_CALLBACK (ev_window_cmd_view_best_fit) },
+ { "ViewPageWidth", EV_STOCK_ZOOM_WIDTH, N_("Fit Page _Width"), NULL,
+ N_("Make the current document fill the window width"),
+ G_CALLBACK (ev_window_cmd_view_page_width) },
+ { "ViewInvertedColors", EV_STOCK_INVERTED_COLORS, N_("_Inverted Colors"), "<control>I",
+ N_("Show page contents with the colors inverted"),
+ G_CALLBACK (ev_window_cmd_view_inverted_colors) },
+
+};
+
+/* Popups specific items */
+static const GtkActionEntry view_popup_entries [] = {
+ /* Links */
+ { "OpenLink", GTK_STOCK_OPEN, N_("_Open Link"), NULL,
+ NULL, G_CALLBACK (ev_view_popup_cmd_open_link) },
+ { "GoLink", GTK_STOCK_GO_FORWARD, N_("_Go To"), NULL,
+ NULL, G_CALLBACK (ev_view_popup_cmd_open_link) },
+ { "OpenLinkNewWindow", NULL, N_("Open in New _Window"), NULL,
+ NULL, G_CALLBACK (ev_view_popup_cmd_open_link_new_window) },
+ { "CopyLinkAddress", NULL, N_("_Copy Link Address"), NULL,
+ NULL, G_CALLBACK (ev_view_popup_cmd_copy_link_address) },
+ { "SaveImageAs", NULL, N_("_Save Image Asā€¦"), NULL,
+ NULL, G_CALLBACK (ev_view_popup_cmd_save_image_as) },
+ { "CopyImage", NULL, N_("Copy _Image"), NULL,
+ NULL, G_CALLBACK (ev_view_popup_cmd_copy_image) },
+ { "AnnotProperties", NULL, N_("Annotation Propertiesā€¦"), NULL,
+ NULL, G_CALLBACK (ev_view_popup_cmd_annot_properties) }
+};
+
+static const GtkActionEntry attachment_popup_entries [] = {
+ { "OpenAttachment", GTK_STOCK_OPEN, N_("_Open Attachment"), NULL,
+ NULL, G_CALLBACK (ev_attachment_popup_cmd_open_attachment) },
+ { "SaveAttachmentAs", GTK_STOCK_SAVE_AS, N_("_Save Attachment Asā€¦"), NULL,
+ NULL, G_CALLBACK (ev_attachment_popup_cmd_save_attachment_as) },
+};
+
+static void
+sidebar_links_link_activated_cb (EvSidebarLinks *sidebar_links, EvLink *link, EvWindow *window)
+{
+ ev_view_handle_link (EV_VIEW (window->priv->view), link);
+}
+
+static void
+activate_link_cb (EvPageAction *page_action, EvLink *link, EvWindow *window)
+{
+ ev_view_handle_link (EV_VIEW (window->priv->view), link);
+ gtk_widget_grab_focus (window->priv->view);
+}
+
+static void
+navigation_action_activate_link_cb (EvNavigationAction *action, EvLink *link, EvWindow *window)
+{
+
+ ev_view_handle_link (EV_VIEW (window->priv->view), link);
+ gtk_widget_grab_focus (window->priv->view);
+}
+
+static void
+sidebar_layers_visibility_changed (EvSidebarLayers *layers,
+ EvWindow *window)
+{
+ ev_view_reload (EV_VIEW (window->priv->view));
+}
+
+static void
+sidebar_annots_annot_activated_cb (EvSidebarAnnotations *sidebar_annots,
+ EvMapping *annot_mapping,
+ EvWindow *window)
+{
+ ev_view_focus_annotation (EV_VIEW (window->priv->view), annot_mapping);
+}
+
+static void
+sidebar_annots_begin_annot_add (EvSidebarAnnotations *sidebar_annots,
+ EvAnnotationType annot_type,
+ EvWindow *window)
+{
+ ev_view_begin_add_annotation (EV_VIEW (window->priv->view), annot_type);
+}
+
+static void
+view_annot_added (EvView *view,
+ EvAnnotation *annot,
+ EvWindow *window)
+{
+ ev_sidebar_annotations_annot_added (EV_SIDEBAR_ANNOTATIONS (window->priv->sidebar_annots),
+ annot);
+}
+
+static void
+sidebar_annots_annot_add_cancelled (EvSidebarAnnotations *sidebar_annots,
+ EvWindow *window)
+{
+ ev_view_cancel_add_annotation (EV_VIEW (window->priv->view));
+}
+
+static void
+register_custom_actions (EvWindow *window, GtkActionGroup *group)
+{
+ GtkAction *action;
+
+ action = g_object_new (EV_TYPE_PAGE_ACTION,
+ "name", PAGE_SELECTOR_ACTION,
+ "label", _("Page"),
+ "tooltip", _("Select Page"),
+ "icon_name", "text-x-generic",
+ "visible_overflown", FALSE,
+ NULL);
+ ev_page_action_set_model (EV_PAGE_ACTION (action),
+ window->priv->model);
+ g_signal_connect (action, "activate_link",
+ G_CALLBACK (activate_link_cb), window);
+ gtk_action_group_add_action (group, action);
+ g_object_unref (action);
+
+ action = g_object_new (EPHY_TYPE_ZOOM_ACTION,
+ "name", ZOOM_CONTROL_ACTION,
+ "label", _("Zoom"),
+ "stock_id", EV_STOCK_ZOOM,
+ "tooltip", _("Adjust the zoom level"),
+ "zoom", 1.0,
+ NULL);
+ g_signal_connect (action, "zoom_to_level",
+ G_CALLBACK (zoom_control_changed_cb), window);
+ gtk_action_group_add_action (group, action);
+ g_object_unref (action);
+
+ action = g_object_new (EV_TYPE_NAVIGATION_ACTION,
+ "name", NAVIGATION_ACTION,
+ "label", _("Navigation"),
+ "is_important", TRUE,
+ "short_label", _("Back"),
+ "stock_id", GTK_STOCK_GO_DOWN,
+ /*translators: this is the history action*/
+ "tooltip", _("Move across visited pages"),
+ NULL);
+ g_signal_connect (action, "activate_link",
+ G_CALLBACK (navigation_action_activate_link_cb), window);
+ gtk_action_group_add_action (group, action);
+ g_object_unref (action);
+
+ action = g_object_new (EV_TYPE_OPEN_RECENT_ACTION,
+ "name", "FileOpenRecent",
+ "label", _("_Openā€¦"),
+ "tooltip", _("Open an existing document"),
+ "stock_id", GTK_STOCK_OPEN,
+ NULL);
+ g_signal_connect (action, "activate",
+ G_CALLBACK (ev_window_cmd_file_open), window);
+ g_signal_connect (action, "item_activated",
+ G_CALLBACK (ev_window_open_recent_action_item_activated),
+ window);
+ gtk_action_group_add_action (group, action);
+ g_object_unref (action);
+}
+
+static void
+set_action_properties (GtkActionGroup *action_group)
+{
+ GtkAction *action;
+
+ action = gtk_action_group_get_action (action_group, "GoPreviousPage");
+ g_object_set (action, "is-important", TRUE, NULL);
+ /*translators: this is the label for toolbar button*/
+ g_object_set (action, "short_label", _("Previous"), NULL);
+
+ action = gtk_action_group_get_action (action_group, "GoNextPage");
+ g_object_set (action, "is-important", TRUE, NULL);
+ /*translators: this is the label for toolbar button*/
+ g_object_set (action, "short_label", _("Next"), NULL);
+
+ action = gtk_action_group_get_action (action_group, "ViewZoomIn");
+ /*translators: this is the label for toolbar button*/
+ g_object_set (action, "short_label", _("Zoom In"), NULL);
+
+ action = gtk_action_group_get_action (action_group, "ViewZoomOut");
+ /*translators: this is the label for toolbar button*/
+ g_object_set (action, "short_label", _("Zoom Out"), NULL);
+
+ action = gtk_action_group_get_action (action_group, "ViewBestFit");
+ /*translators: this is the label for toolbar button*/
+ g_object_set (action, "short_label", _("Best Fit"), NULL);
+
+ action = gtk_action_group_get_action (action_group, "ViewPageWidth");
+ /*translators: this is the label for toolbar button*/
+ g_object_set (action, "short_label", _("Fit Width"), NULL);
+
+ action = gtk_action_group_get_action (action_group, "LeaveFullscreen");
+ g_object_set (action, "is-important", TRUE, NULL);
+}
+
+static void
+sidebar_widget_model_set (EvSidebarLinks *ev_sidebar_links,
+ GParamSpec *pspec,
+ EvWindow *ev_window)
+{
+ GtkTreeModel *model;
+ GtkAction *action;
+
+ g_object_get (G_OBJECT (ev_sidebar_links),
+ "model", &model,
+ NULL);
+
+ action = gtk_action_group_get_action (ev_window->priv->action_group, PAGE_SELECTOR_ACTION);
+ ev_page_action_set_links_model (EV_PAGE_ACTION (action), model);
+ g_object_unref (model);
+}
+
+static gboolean
+view_actions_focus_in_cb (GtkWidget *widget, GdkEventFocus *event, EvWindow *window)
+{
+#ifdef ENABLE_DBUS
+ GObject *keys;
+
+ keys = ev_application_get_media_keys (EV_APP);
+ ev_media_player_keys_focused (EV_MEDIA_PLAYER_KEYS (keys));
+#endif /* ENABLE_DBUS */
+
+ update_chrome_flag (window, EV_CHROME_RAISE_TOOLBAR, FALSE);
+ ev_window_set_action_sensitive (window, "ViewToolbar", TRUE);
+
+ ev_window_set_view_accels_sensitivity (window, TRUE);
+
+ update_chrome_visibility (window);
+
+ return FALSE;
+}
+
+static gboolean
+view_actions_focus_out_cb (GtkWidget *widget, GdkEventFocus *event, EvWindow *window)
+{
+ ev_window_set_view_accels_sensitivity (window, FALSE);
+
+ return FALSE;
+}
+
+static void
+sidebar_page_main_widget_update_cb (GObject *ev_sidebar_page,
+ GParamSpec *pspec,
+ EvWindow *ev_window)
+{
+ GtkWidget *widget;
+
+ g_object_get (ev_sidebar_page, "main_widget", &widget, NULL);
+
+ if (widget != NULL) {
+ g_signal_connect_object (widget, "focus_in_event",
+ G_CALLBACK (view_actions_focus_in_cb),
+ ev_window, 0);
+ g_signal_connect_object (widget, "focus_out_event",
+ G_CALLBACK (view_actions_focus_out_cb),
+ ev_window, 0);
+ g_object_unref (widget);
+ }
+}
+
+static gboolean
+window_state_event_cb (EvWindow *window, GdkEventWindowState *event, gpointer dummy)
+{
+ if (!(event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)) {
+ gboolean maximized;
+
+ maximized = event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED;
+ if (window->priv->metadata && !ev_window_is_empty (window))
+ ev_metadata_set_boolean (window->priv->metadata, "window_maximized", maximized);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+window_configure_event_cb (EvWindow *window, GdkEventConfigure *event, gpointer dummy)
+{
+ GdkWindowState state;
+ gdouble document_width, document_height;
+
+ if (!window->priv->metadata)
+ return FALSE;
+
+ state = gdk_window_get_state (gtk_widget_get_window (GTK_WIDGET (window)));
+
+ if (!(state & GDK_WINDOW_STATE_FULLSCREEN)) {
+ if (window->priv->document) {
+ ev_document_get_max_page_size (window->priv->document,
+ &document_width, &document_height);
+ g_settings_set (window->priv->default_settings, "window-ratio", "(dd)",
+ (double)event->width / document_width,
+ (double)event->height / document_height);
+
+ ev_metadata_set_int (window->priv->metadata, "window_x", event->x);
+ ev_metadata_set_int (window->priv->metadata, "window_y", event->y);
+ ev_metadata_set_int (window->priv->metadata, "window_width", event->width);
+ ev_metadata_set_int (window->priv->metadata, "window_height", event->height);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+launch_action (EvWindow *window, EvLinkAction *action)
+{
+ const char *filename = ev_link_action_get_filename (action);
+ GAppInfo *app_info;
+ GFile *file;
+ GList file_list = {NULL};
+ GAppLaunchContext *context;
+ GError *error = NULL;
+
+ if (filename == NULL)
+ return;
+
+ if (g_path_is_absolute (filename)) {
+ file = g_file_new_for_path (filename);
+ } else {
+ GFile *base_file;
+ gchar *dir;
+
+ dir = g_path_get_dirname (window->priv->uri);
+ base_file = g_file_new_for_uri (dir);
+ g_free (dir);
+
+ file = g_file_resolve_relative_path (base_file, filename);
+ g_object_unref (base_file);
+ }
+
+ app_info = g_file_query_default_handler (file, NULL, &error);
+ if (!app_info) {
+ ev_window_error_message (window, error,
+ "%s",
+ _("Unable to launch external application."));
+ g_object_unref (file);
+ g_error_free (error);
+
+ return;
+ }
+
+ context = G_APP_LAUNCH_CONTEXT (gdk_app_launch_context_new ());
+ gdk_app_launch_context_set_screen (GDK_APP_LAUNCH_CONTEXT (context),
+ gtk_window_get_screen (GTK_WINDOW (window)));
+ gdk_app_launch_context_set_timestamp (GDK_APP_LAUNCH_CONTEXT (context),
+ gtk_get_current_event_time ());
+
+ file_list.data = file;
+ if (!g_app_info_launch (app_info, &file_list, context, &error)) {
+ ev_window_error_message (window, error,
+ "%s",
+ _("Unable to launch external application."));
+ g_error_free (error);
+ }
+
+ g_object_unref (app_info);
+ g_object_unref (file);
+ /* FIXMEchpe: unref launch context? */
+
+ /* According to the PDF spec filename can be an executable. I'm not sure
+ allowing to launch executables is a good idea though. -- marco */
+}
+
+static void
+launch_external_uri (EvWindow *window, EvLinkAction *action)
+{
+ const gchar *uri = ev_link_action_get_uri (action);
+ GError *error = NULL;
+ gboolean ret;
+ GAppLaunchContext *context;
+
+ context = G_APP_LAUNCH_CONTEXT (gdk_app_launch_context_new ());
+ gdk_app_launch_context_set_screen (GDK_APP_LAUNCH_CONTEXT (context),
+ gtk_window_get_screen (GTK_WINDOW (window)));
+ gdk_app_launch_context_set_timestamp (GDK_APP_LAUNCH_CONTEXT (context),
+ gtk_get_current_event_time ());
+
+ if (!g_strstr_len (uri, strlen (uri), "://") &&
+ !g_str_has_prefix (uri, "mailto:")) {
+ gchar *new_uri;
+
+ /* Not a valid uri, assume http if it starts with www */
+ if (g_str_has_prefix (uri, "www.")) {
+ new_uri = g_strdup_printf ("http://%s", uri);
+ } else {
+ GFile *file, *parent;
+
+ file = g_file_new_for_uri (window->priv->uri);
+ parent = g_file_get_parent (file);
+ g_object_unref (file);
+ if (parent) {
+ gchar *parent_uri = g_file_get_uri (parent);
+
+ new_uri = g_build_filename (parent_uri, uri, NULL);
+ g_free (parent_uri);
+ g_object_unref (parent);
+ } else {
+ new_uri = g_strdup_printf ("file:///%s", uri);
+ }
+ }
+ ret = g_app_info_launch_default_for_uri (new_uri, context, &error);
+ g_free (new_uri);
+ } else {
+ ret = g_app_info_launch_default_for_uri (uri, context, &error);
+ }
+
+ if (ret == FALSE) {
+ ev_window_error_message (window, error,
+ "%s", _("Unable to open external link"));
+ g_error_free (error);
+ }
+
+ /* FIXMEchpe: unref launch context? */
+}
+
+static void
+open_remote_link (EvWindow *window, EvLinkAction *action)
+{
+ gchar *uri;
+ gchar *dir;
+
+ dir = g_path_get_dirname (window->priv->uri);
+
+ uri = g_build_filename (dir, ev_link_action_get_filename (action),
+ NULL);
+ g_free (dir);
+
+ ev_application_open_uri_at_dest (EV_APP, uri,
+ gtk_window_get_screen (GTK_WINDOW (window)),
+ ev_link_action_get_dest (action),
+ 0,
+ NULL,
+ gtk_get_current_event_time ());
+
+ g_free (uri);
+}
+
+static void
+do_action_named (EvWindow *window, EvLinkAction *action)
+{
+ const gchar *name = ev_link_action_get_name (action);
+
+ if (g_ascii_strcasecmp (name, "FirstPage") == 0) {
+ ev_window_cmd_go_first_page (NULL, window);
+ } else if (g_ascii_strcasecmp (name, "PrevPage") == 0) {
+ ev_window_cmd_go_previous_page (NULL, window);
+ } else if (g_ascii_strcasecmp (name, "NextPage") == 0) {
+ ev_window_cmd_go_next_page (NULL, window);
+ } else if (g_ascii_strcasecmp (name, "LastPage") == 0) {
+ ev_window_cmd_go_last_page (NULL, window);
+ } 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);
+ } else if (g_ascii_strcasecmp (name, "Close") == 0) {
+ ev_window_cmd_file_close_window (NULL, window);
+ } else if (g_ascii_strcasecmp (name, "Print") == 0) {
+ ev_window_cmd_file_print (NULL, window);
+ } else {
+ g_warning ("Unimplemented named action: %s, please post a "
+ "bug report in Evince bugzilla "
+ "(http://bugzilla.mate.org) with a testcase.",
+ name);
+ }
+}
+
+static void
+view_external_link_cb (EvView *view, EvLinkAction *action, EvWindow *window)
+{
+ switch (ev_link_action_get_action_type (action)) {
+ case EV_LINK_ACTION_TYPE_GOTO_DEST: {
+ EvLinkDest *dest;
+
+ dest = ev_link_action_get_dest (action);
+ if (!dest)
+ return;
+
+ ev_window_open_copy_at_dest (window, dest);
+ }
+ break;
+ case EV_LINK_ACTION_TYPE_EXTERNAL_URI:
+ launch_external_uri (window, action);
+ break;
+ case EV_LINK_ACTION_TYPE_LAUNCH:
+ launch_action (window, action);
+ break;
+ case EV_LINK_ACTION_TYPE_GOTO_REMOTE:
+ open_remote_link (window, action);
+ break;
+ case EV_LINK_ACTION_TYPE_NAMED:
+ do_action_named (window, action);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+ev_view_popup_cmd_open_link (GtkAction *action, EvWindow *window)
+{
+ ev_view_handle_link (EV_VIEW (window->priv->view), window->priv->link);
+}
+
+static void
+ev_view_popup_cmd_open_link_new_window (GtkAction *action, EvWindow *window)
+{
+ EvLinkAction *ev_action = NULL;
+ EvLinkDest *dest;
+
+ ev_action = ev_link_get_action (window->priv->link);
+ if (!ev_action)
+ return;
+
+ dest = ev_link_action_get_dest (ev_action);
+ if (!dest)
+ return;
+
+ ev_window_open_copy_at_dest (window, dest);
+}
+
+static void
+ev_view_popup_cmd_copy_link_address (GtkAction *action, EvWindow *window)
+{
+ EvLinkAction *ev_action;
+
+ ev_action = ev_link_get_action (window->priv->link);
+ if (!ev_action)
+ return;
+
+ ev_view_copy_link_address (EV_VIEW (window->priv->view),
+ ev_action);
+}
+
+
+static void
+image_save_dialog_response_cb (GtkWidget *fc,
+ gint response_id,
+ EvWindow *ev_window)
+{
+ GFile *target_file;
+ gboolean is_native;
+ GError *error = NULL;
+ GdkPixbuf *pixbuf;
+ gchar *uri;
+ gchar **extensions;
+ gchar *filename;
+ gchar *file_format;
+ GdkPixbufFormat *format;
+ GtkFileFilter *filter;
+
+ if (response_id != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (fc);
+ return;
+ }
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fc));
+ filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (fc));
+ format = g_object_get_data (G_OBJECT (filter), "pixbuf-format");
+
+ if (format == NULL) {
+ format = get_gdk_pixbuf_format_by_extension (uri);
+ }
+
+ if (format == NULL && g_strrstr (uri, ".") == NULL) {
+ /* no extension found and no extension provided within uri */
+ format = get_gdk_pixbuf_format_by_extension (".png");
+ if (format == NULL) {
+ /* no .png support, try .jpeg */
+ format = get_gdk_pixbuf_format_by_extension (".jpeg");
+ }
+ }
+
+ if (format == NULL) {
+ ev_window_error_message (ev_window, NULL,
+ "%s",
+ _("Couldn't find appropriate format to save image"));
+ g_free (uri);
+ gtk_widget_destroy (fc);
+
+ return;
+ }
+
+ extensions = gdk_pixbuf_format_get_extensions (format);
+ if (!g_str_has_suffix (uri, extensions[0])) {
+ gchar *uri_extension;
+
+ uri_extension = g_strconcat (uri, ".", extensions[0], NULL);
+ target_file = g_file_new_for_uri (uri_extension);
+ g_free (uri_extension);
+ } else {
+ target_file = g_file_new_for_uri (uri);
+ }
+ g_strfreev (extensions);
+ g_free (uri);
+
+ is_native = g_file_is_native (target_file);
+ if (is_native) {
+ filename = g_file_get_path (target_file);
+ } else {
+ /* Create a temporary local file to save to */
+ if (ev_mkstemp ("saveimage.XXXXXX", &filename, &error) == -1)
+ goto has_error;
+ }
+
+ ev_document_doc_mutex_lock ();
+ pixbuf = ev_document_images_get_image (EV_DOCUMENT_IMAGES (ev_window->priv->document),
+ ev_window->priv->image);
+ ev_document_doc_mutex_unlock ();
+
+ file_format = gdk_pixbuf_format_get_name (format);
+ gdk_pixbuf_save (pixbuf, filename, file_format, &error, NULL);
+ g_free (file_format);
+ g_object_unref (pixbuf);
+
+ has_error:
+ if (error) {
+ ev_window_error_message (ev_window, error,
+ "%s", _("The image could not be saved."));
+ g_error_free (error);
+ g_free (filename);
+ g_object_unref (target_file);
+ gtk_widget_destroy (fc);
+
+ return;
+ }
+
+ if (!is_native) {
+ GFile *source_file;
+
+ source_file = g_file_new_for_path (filename);
+
+ ev_window_save_remote (ev_window, EV_SAVE_IMAGE,
+ source_file, target_file);
+ g_object_unref (source_file);
+ }
+
+ g_free (filename);
+ g_object_unref (target_file);
+ gtk_widget_destroy (fc);
+}
+
+static void
+ev_view_popup_cmd_save_image_as (GtkAction *action, EvWindow *window)
+{
+ GtkWidget *fc;
+
+ if (!window->priv->image)
+ return;
+
+ fc = gtk_file_chooser_dialog_new (_("Save Image"),
+ GTK_WINDOW (window),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (fc), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (fc),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fc), FALSE);
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fc), TRUE);
+
+ file_chooser_dialog_add_writable_pixbuf_formats (GTK_FILE_CHOOSER (fc));
+
+ g_signal_connect (fc, "response",
+ G_CALLBACK (image_save_dialog_response_cb),
+ window);
+
+ gtk_widget_show (fc);
+}
+
+static void
+ev_view_popup_cmd_copy_image (GtkAction *action, EvWindow *window)
+{
+ GtkClipboard *clipboard;
+ GdkPixbuf *pixbuf;
+
+ if (!window->priv->image)
+ return;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window),
+ GDK_SELECTION_CLIPBOARD);
+ ev_document_doc_mutex_lock ();
+ pixbuf = ev_document_images_get_image (EV_DOCUMENT_IMAGES (window->priv->document),
+ window->priv->image);
+ ev_document_doc_mutex_unlock ();
+
+ gtk_clipboard_set_image (clipboard, pixbuf);
+ g_object_unref (pixbuf);
+}
+
+static void
+ev_view_popup_cmd_annot_properties (GtkAction *action,
+ EvWindow *window)
+{
+ const gchar *author;
+ GdkColor color;
+ gdouble opacity;
+ gboolean popup_is_open;
+ EvAnnotationPropertiesDialog *dialog;
+ EvAnnotation *annot = window->priv->annot;
+ EvAnnotationsSaveMask mask = EV_ANNOTATIONS_SAVE_NONE;
+
+ if (!annot)
+ return;
+
+ dialog = EV_ANNOTATION_PROPERTIES_DIALOG (ev_annotation_properties_dialog_new_with_annotation (window->priv->annot));
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_APPLY) {
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ return;
+ }
+
+ /* Set annotations changes */
+ author = ev_annotation_properties_dialog_get_author (dialog);
+ if (ev_annotation_markup_set_label (EV_ANNOTATION_MARKUP (annot), author))
+ mask |= EV_ANNOTATIONS_SAVE_LABEL;
+
+ ev_annotation_properties_dialog_get_color (dialog, &color);
+ if (ev_annotation_set_color (annot, &color))
+ mask |= EV_ANNOTATIONS_SAVE_COLOR;
+
+ opacity = ev_annotation_properties_dialog_get_opacity (dialog);
+ if (ev_annotation_markup_set_opacity (EV_ANNOTATION_MARKUP (annot), opacity))
+ mask |= EV_ANNOTATIONS_SAVE_OPACITY;
+
+ popup_is_open = ev_annotation_properties_dialog_get_popup_is_open (dialog);
+ if (ev_annotation_markup_set_popup_is_open (EV_ANNOTATION_MARKUP (annot), popup_is_open))
+ mask |= EV_ANNOTATIONS_SAVE_POPUP_IS_OPEN;
+
+ if (EV_IS_ANNOTATION_TEXT (annot)) {
+ EvAnnotationTextIcon icon;
+
+ icon = ev_annotation_properties_dialog_get_text_icon (dialog);
+ if (ev_annotation_text_set_icon (EV_ANNOTATION_TEXT (annot), icon))
+ mask |= EV_ANNOTATIONS_SAVE_TEXT_ICON;
+ }
+
+ if (mask != EV_ANNOTATIONS_SAVE_NONE) {
+ ev_document_doc_mutex_lock ();
+ ev_document_annotations_save_annotation (EV_DOCUMENT_ANNOTATIONS (window->priv->document),
+ window->priv->annot,
+ mask);
+ ev_document_doc_mutex_unlock ();
+
+ /* FIXME: update annot region only */
+ ev_view_reload (EV_VIEW (window->priv->view));
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+}
+
+static void
+ev_attachment_popup_cmd_open_attachment (GtkAction *action, EvWindow *window)
+{
+ GList *l;
+ GdkScreen *screen;
+
+ if (!window->priv->attach_list)
+ return;
+
+ screen = gtk_window_get_screen (GTK_WINDOW (window));
+
+ for (l = window->priv->attach_list; l && l->data; l = g_list_next (l)) {
+ EvAttachment *attachment;
+ GError *error = NULL;
+
+ attachment = (EvAttachment *) l->data;
+
+ ev_attachment_open (attachment, screen, gtk_get_current_event_time (), &error);
+
+ if (error) {
+ ev_window_error_message (window, error,
+ "%s", _("Unable to open attachment"));
+ g_error_free (error);
+ }
+ }
+}
+
+static void
+attachment_save_dialog_response_cb (GtkWidget *fc,
+ gint response_id,
+ EvWindow *ev_window)
+{
+ GFile *target_file;
+ gchar *uri;
+ GList *l;
+ GtkFileChooserAction fc_action;
+ gboolean is_dir;
+ gboolean is_native;
+
+ if (response_id != GTK_RESPONSE_OK) {
+ gtk_widget_destroy (fc);
+ return;
+ }
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fc));
+ target_file = g_file_new_for_uri (uri);
+ g_object_get (G_OBJECT (fc), "action", &fc_action, NULL);
+ is_dir = (fc_action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+ is_native = g_file_is_native (target_file);
+
+ for (l = ev_window->priv->attach_list; l && l->data; l = g_list_next (l)) {
+ EvAttachment *attachment;
+ GFile *save_to = NULL;
+ GError *error = NULL;
+
+ attachment = (EvAttachment *) l->data;
+
+ if (is_native) {
+ if (is_dir) {
+ save_to = g_file_get_child (target_file,
+ /* FIXMEchpe: file name encoding! */
+ ev_attachment_get_name (attachment));
+ } else {
+ save_to = g_object_ref (target_file);
+ }
+ } else {
+ save_to = ev_mkstemp_file ("saveattachment.XXXXXX", &error);
+ }
+
+ if (save_to)
+ ev_attachment_save (attachment, save_to, &error);
+
+ if (error) {
+ ev_window_error_message (ev_window, error,
+ "%s", _("The attachment could not be saved."));
+ g_error_free (error);
+ g_object_unref (save_to);
+
+ continue;
+ }
+
+ if (!is_native) {
+ GFile *dest_file;
+
+ if (is_dir) {
+ dest_file = g_file_get_child (target_file,
+ ev_attachment_get_name (attachment));
+ } else {
+ dest_file = g_object_ref (target_file);
+ }
+
+ ev_window_save_remote (ev_window, EV_SAVE_ATTACHMENT,
+ save_to, dest_file);
+
+ g_object_unref (dest_file);
+ }
+
+ g_object_unref (save_to);
+ }
+
+ g_free (uri);
+ g_object_unref (target_file);
+
+ gtk_widget_destroy (fc);
+}
+
+static void
+ev_attachment_popup_cmd_save_attachment_as (GtkAction *action, EvWindow *window)
+{
+ GtkWidget *fc;
+ EvAttachment *attachment = NULL;
+
+ if (!window->priv->attach_list)
+ return;
+
+ if (g_list_length (window->priv->attach_list) == 1)
+ attachment = (EvAttachment *) window->priv->attach_list->data;
+
+ fc = gtk_file_chooser_dialog_new (
+ _("Save Attachment"),
+ GTK_WINDOW (window),
+ attachment ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_OK,
+ NULL);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (fc), GTK_RESPONSE_OK);
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (fc),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (fc), TRUE);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fc), FALSE);
+
+ if (attachment)
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (fc),
+ ev_attachment_get_name (attachment));
+
+ g_signal_connect (fc, "response",
+ G_CALLBACK (attachment_save_dialog_response_cb),
+ window);
+
+ gtk_widget_show (fc);
+}
+
+static void
+ev_window_media_player_key_pressed (EvWindow *window,
+ const gchar *key,
+ gpointer user_data)
+{
+ if (!gtk_window_is_active (GTK_WINDOW (window)))
+ return;
+
+ /* Note how Previous/Next only go to the
+ * next/previous page despite their icon telling you
+ * they should go to the beginning/end.
+ *
+ * There's very few keyboards with FFW/RWD though,
+ * so we stick the most useful keybinding on the most
+ * often seen keys
+ */
+ if (strcmp (key, "Play") == 0) {
+ ev_window_run_presentation (window);
+ } else if (strcmp (key, "Previous") == 0) {
+ ev_window_cmd_go_previous_page (NULL, window);
+ } else if (strcmp (key, "Next") == 0) {
+ ev_window_cmd_go_next_page (NULL, window);
+ } else if (strcmp (key, "FastForward") == 0) {
+ ev_window_cmd_go_last_page (NULL, window);
+ } else if (strcmp (key, "Rewind") == 0) {
+ ev_window_cmd_go_first_page (NULL, window);
+ }
+}
+
+static EggToolbarsModel *
+get_toolbars_model (void)
+{
+ EggToolbarsModel *toolbars_model;
+ gchar *toolbars_file;
+ gchar *toolbars_path;
+ gint i;
+
+ toolbars_model = egg_toolbars_model_new ();
+
+ toolbars_file = g_build_filename (ev_application_get_dot_dir (EV_APP, FALSE),
+ "evince_toolbar.xml", NULL);
+ toolbars_path = g_build_filename (ev_application_get_data_dir (EV_APP),
+ "evince-toolbar.xml", NULL);
+ egg_toolbars_model_load_names (toolbars_model, toolbars_path);
+
+ if (!egg_toolbars_model_load_toolbars (toolbars_model, toolbars_file)) {
+ egg_toolbars_model_load_toolbars (toolbars_model, toolbars_path);
+ goto skip_conversion;
+ }
+
+ /* Open item doesn't exist anymore,
+ * convert it to OpenRecent for compatibility
+ */
+ for (i = 0; i < egg_toolbars_model_n_items (toolbars_model, 0); i++) {
+ const gchar *item;
+
+ item = egg_toolbars_model_item_nth (toolbars_model, 0, i);
+ if (g_ascii_strcasecmp (item, "FileOpen") == 0) {
+ egg_toolbars_model_remove_item (toolbars_model, 0, i);
+ egg_toolbars_model_add_item (toolbars_model, 0, i,
+ "FileOpenRecent");
+ egg_toolbars_model_save_toolbars (toolbars_model, toolbars_file, "1.0");
+ break;
+ }
+ }
+
+ skip_conversion:
+ g_free (toolbars_file);
+ g_free (toolbars_path);
+
+ egg_toolbars_model_set_flags (toolbars_model, 0, EGG_TB_MODEL_NOT_REMOVABLE);
+
+ return toolbars_model;
+}
+
+#ifdef ENABLE_DBUS
+static void
+ev_window_sync_source (EvWindow *window,
+ EvSourceLink *link)
+{
+ GDBusConnection *connection;
+ GError *error = NULL;
+
+ if (window->priv->dbus_object_id <= 0)
+ return;
+
+ connection = ev_application_get_dbus_connection (EV_APP);
+ if (!connection)
+ return;
+
+ g_dbus_connection_emit_signal (connection,
+ NULL,
+ window->priv->dbus_object_path,
+ EV_WINDOW_DBUS_INTERFACE,
+ "SyncSource",
+ g_variant_new ("(s(ii))",
+ link->filename,
+ link->line,
+ link->col),
+ &error);
+ if (error) {
+ g_printerr ("Failed to emit DBus signal SyncSource: %s\n",
+ error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+ev_window_emit_closed (EvWindow *window)
+{
+ GDBusConnection *connection;
+ GError *error = NULL;
+
+ if (window->priv->dbus_object_id <= 0)
+ return;
+
+ connection = ev_application_get_dbus_connection (EV_APP);
+ if (!connection)
+ return;
+
+ g_dbus_connection_emit_signal (connection,
+ NULL,
+ window->priv->dbus_object_path,
+ EV_WINDOW_DBUS_INTERFACE,
+ "Closed",
+ NULL,
+ &error);
+ if (error) {
+ g_printerr ("Failed to emit DBus signal Closed: %s\n",
+ error->message);
+ g_error_free (error);
+
+ return;
+ }
+
+ /* If this is the last window call g_dbus_connection_flush_sync()
+ * to make sure the signal is emitted.
+ */
+ if (ev_application_get_n_windows (EV_APP) == 1)
+ g_dbus_connection_flush_sync (connection, NULL, NULL);
+}
+
+static void
+ev_window_emit_doc_loaded (EvWindow *window)
+{
+ GDBusConnection *connection;
+ GError *error = NULL;
+
+ if (window->priv->dbus_object_id <= 0)
+ return;
+
+ connection = ev_application_get_dbus_connection (EV_APP);
+ if (!connection)
+ return;
+
+ g_dbus_connection_emit_signal (connection,
+ NULL,
+ window->priv->dbus_object_path,
+ EV_WINDOW_DBUS_INTERFACE,
+ "DocumentLoaded",
+ g_variant_new("(s)", window->priv->uri),
+ &error);
+ if (error) {
+ g_printerr ("Failed to emit DBus signal DocumentLoaded: %s\n",
+ error->message);
+ g_error_free (error);
+
+ return;
+ }
+}
+
+static void
+method_call_cb (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ EvWindow *window = EV_WINDOW (user_data);
+
+ if (g_strcmp0 (method_name, "SyncView") != 0)
+ return;
+
+ if (window->priv->document && ev_document_has_synctex (window->priv->document)) {
+ EvSourceLink link;
+
+ g_variant_get (parameters, "(&s(ii))", &link.filename, &link.line, &link.col);
+ ev_view_highlight_forward_search (EV_VIEW (window->priv->view), &link);
+ gtk_window_present (GTK_WINDOW (window));
+ }
+
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
+}
+
+static const char introspection_xml[] =
+ "<node>"
+ "<interface name='org.mate.evince.Window'>"
+ "<method name='SyncView'>"
+ "<arg type='s' name='source_file' direction='in'/>"
+ "<arg type='(ii)' name='source_point' direction='in'/>"
+ "</method>"
+ "<signal name='SyncSource'>"
+ "<arg type='s' name='source_file' direction='out'/>"
+ "<arg type='(ii)' name='source_point' direction='out'/>"
+ "</signal>"
+ "<signal name='Closed'/>"
+ "<signal name='DocumentLoaded'>"
+ "<arg type='s' name='uri' direction='out'/>"
+ "</signal>"
+ "</interface>"
+ "</node>";
+
+static const GDBusInterfaceVTable interface_vtable = {
+ method_call_cb,
+ NULL,
+ NULL
+};
+
+static GDBusNodeInfo *introspection_data;
+#endif /* ENABLE_DBUS */
+
+static void
+ev_window_init (EvWindow *ev_window)
+{
+ GtkActionGroup *action_group;
+ GtkAccelGroup *accel_group;
+ GError *error = NULL;
+ GtkWidget *sidebar_widget;
+ GtkWidget *menuitem;
+ EggToolbarsModel *toolbars_model;
+ GObject *mpkeys;
+ gchar *ui_path;
+#ifdef ENABLE_DBUS
+ GDBusConnection *connection;
+ static gint window_id = 0;
+#endif
+
+ g_signal_connect (ev_window, "configure_event",
+ G_CALLBACK (window_configure_event_cb), NULL);
+ g_signal_connect (ev_window, "window_state_event",
+ G_CALLBACK (window_state_event_cb), NULL);
+
+ ev_window->priv = EV_WINDOW_GET_PRIVATE (ev_window);
+
+#ifdef ENABLE_DBUS
+ connection = ev_application_get_dbus_connection (EV_APP);
+ if (connection) {
+ if (!introspection_data) {
+ introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, &error);
+ if (error) g_warning ("%s\n", error->message);
+ }
+ g_assert (introspection_data != NULL);
+
+ ev_window->priv->dbus_object_path = g_strdup_printf (EV_WINDOW_DBUS_OBJECT_PATH, window_id++);
+ ev_window->priv->dbus_object_id =
+ g_dbus_connection_register_object (connection,
+ ev_window->priv->dbus_object_path,
+ introspection_data->interfaces[0],
+ &interface_vtable,
+ ev_window, NULL,
+ &error);
+ if (ev_window->priv->dbus_object_id == 0) {
+ g_printerr ("Failed to register bus object %s: %s\n",
+ ev_window->priv->dbus_object_path, error->message);
+ g_error_free (error);
+ g_free (ev_window->priv->dbus_object_path);
+ ev_window->priv->dbus_object_path = NULL;
+ error = NULL;
+ }
+ }
+
+#endif /* ENABLE_DBUS */
+
+ ev_window->priv->model = ev_document_model_new ();
+
+ ev_window->priv->page_mode = PAGE_MODE_DOCUMENT;
+ ev_window->priv->title = ev_window_title_new (ev_window);
+
+ ev_window->priv->main_box = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (ev_window), ev_window->priv->main_box);
+ gtk_widget_show (ev_window->priv->main_box);
+
+ action_group = gtk_action_group_new ("MenuActions");
+ ev_window->priv->action_group = action_group;
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group, entries,
+ G_N_ELEMENTS (entries), ev_window);
+ gtk_action_group_add_toggle_actions (action_group, toggle_entries,
+ G_N_ELEMENTS (toggle_entries),
+ ev_window);
+ set_action_properties (action_group);
+ register_custom_actions (ev_window, action_group);
+
+ ev_window->priv->ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ev_window->priv->ui_manager,
+ action_group, 0);
+
+ accel_group =
+ gtk_ui_manager_get_accel_group (ev_window->priv->ui_manager);
+ gtk_window_add_accel_group (GTK_WINDOW (ev_window), accel_group);
+
+ ev_window_set_view_accels_sensitivity (ev_window, FALSE);
+
+ action_group = gtk_action_group_new ("ViewPopupActions");
+ ev_window->priv->view_popup_action_group = action_group;
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group, view_popup_entries,
+ G_N_ELEMENTS (view_popup_entries),
+ ev_window);
+ gtk_ui_manager_insert_action_group (ev_window->priv->ui_manager,
+ action_group, 0);
+
+ action_group = gtk_action_group_new ("AttachmentPopupActions");
+ ev_window->priv->attachment_popup_action_group = action_group;
+ gtk_action_group_set_translation_domain (action_group, NULL);
+ gtk_action_group_add_actions (action_group, attachment_popup_entries,
+ G_N_ELEMENTS (attachment_popup_entries),
+ ev_window);
+ gtk_ui_manager_insert_action_group (ev_window->priv->ui_manager,
+ action_group, 0);
+
+ ui_path = g_build_filename (ev_application_get_data_dir (EV_APP),
+ "evince-ui.xml", NULL);
+ if (!gtk_ui_manager_add_ui_from_file (
+ ev_window->priv->ui_manager, ui_path, &error))
+ {
+ g_warning ("building menus failed: %s", error->message);
+ g_error_free (error);
+ }
+ g_free (ui_path);
+
+ ev_window->priv->recent_manager = gtk_recent_manager_get_default ();
+ ev_window->priv->recent_action_group = NULL;
+ ev_window->priv->recent_ui_id = 0;
+ g_signal_connect_swapped (ev_window->priv->recent_manager,
+ "changed",
+ G_CALLBACK (ev_window_setup_recent),
+ ev_window);
+
+ ev_window->priv->menubar =
+ gtk_ui_manager_get_widget (ev_window->priv->ui_manager,
+ "/MainMenu");
+ gtk_box_pack_start (GTK_BOX (ev_window->priv->main_box),
+ ev_window->priv->menubar,
+ FALSE, FALSE, 0);
+ menuitem = gtk_ui_manager_get_widget (ev_window->priv->ui_manager,
+ "/MainMenu/EditMenu/EditRotateLeftMenu");
+ gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
+ menuitem = gtk_ui_manager_get_widget (ev_window->priv->ui_manager,
+ "/MainMenu/EditMenu/EditRotateRightMenu");
+ gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
+
+ toolbars_model = get_toolbars_model ();
+ ev_window->priv->toolbar = GTK_WIDGET
+ (g_object_new (EGG_TYPE_EDITABLE_TOOLBAR,
+ "ui-manager", ev_window->priv->ui_manager,
+ "popup-path", "/ToolbarPopup",
+ "model", toolbars_model,
+ NULL));
+ g_object_unref (toolbars_model);
+
+ egg_editable_toolbar_show (EGG_EDITABLE_TOOLBAR (ev_window->priv->toolbar),
+ "DefaultToolBar");
+ gtk_box_pack_start (GTK_BOX (ev_window->priv->main_box),
+ ev_window->priv->toolbar,
+ FALSE, FALSE, 0);
+ gtk_widget_show (ev_window->priv->toolbar);
+
+ /* Add the main area */
+ ev_window->priv->hpaned = gtk_hpaned_new ();
+ g_signal_connect (ev_window->priv->hpaned,
+ "notify::position",
+ G_CALLBACK (ev_window_sidebar_position_change_cb),
+ ev_window);
+
+ gtk_paned_set_position (GTK_PANED (ev_window->priv->hpaned), SIDEBAR_DEFAULT_SIZE);
+ gtk_box_pack_start (GTK_BOX (ev_window->priv->main_box), ev_window->priv->hpaned,
+ TRUE, TRUE, 0);
+ gtk_widget_show (ev_window->priv->hpaned);
+
+ ev_window->priv->sidebar = ev_sidebar_new ();
+ ev_sidebar_set_model (EV_SIDEBAR (ev_window->priv->sidebar),
+ ev_window->priv->model);
+ gtk_paned_pack1 (GTK_PANED (ev_window->priv->hpaned),
+ ev_window->priv->sidebar, FALSE, FALSE);
+ gtk_widget_show (ev_window->priv->sidebar);
+
+ /* Stub sidebar, for now */
+
+ sidebar_widget = ev_sidebar_thumbnails_new ();
+ ev_window->priv->sidebar_thumbs = sidebar_widget;
+ g_signal_connect (sidebar_widget,
+ "notify::main-widget",
+ G_CALLBACK (sidebar_page_main_widget_update_cb),
+ ev_window);
+ sidebar_page_main_widget_update_cb (G_OBJECT (sidebar_widget), NULL, ev_window);
+ gtk_widget_show (sidebar_widget);
+ ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
+ sidebar_widget);
+
+ sidebar_widget = ev_sidebar_links_new ();
+ ev_window->priv->sidebar_links = sidebar_widget;
+ g_signal_connect (sidebar_widget,
+ "notify::model",
+ G_CALLBACK (sidebar_widget_model_set),
+ ev_window);
+ g_signal_connect (sidebar_widget,
+ "link_activated",
+ G_CALLBACK (sidebar_links_link_activated_cb),
+ ev_window);
+ sidebar_page_main_widget_update_cb (G_OBJECT (sidebar_widget), NULL, ev_window);
+ gtk_widget_show (sidebar_widget);
+ ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
+ sidebar_widget);
+
+ sidebar_widget = ev_sidebar_attachments_new ();
+ ev_window->priv->sidebar_attachments = sidebar_widget;
+ g_signal_connect_object (sidebar_widget,
+ "popup",
+ G_CALLBACK (attachment_bar_menu_popup_cb),
+ ev_window, 0);
+ gtk_widget_show (sidebar_widget);
+ ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
+ sidebar_widget);
+
+ sidebar_widget = ev_sidebar_layers_new ();
+ ev_window->priv->sidebar_layers = sidebar_widget;
+ g_signal_connect (sidebar_widget,
+ "layers_visibility_changed",
+ G_CALLBACK (sidebar_layers_visibility_changed),
+ ev_window);
+ gtk_widget_show (sidebar_widget);
+ ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
+ sidebar_widget);
+
+ sidebar_widget = ev_sidebar_annotations_new ();
+ ev_window->priv->sidebar_annots = sidebar_widget;
+ g_signal_connect (sidebar_widget,
+ "annot_activated",
+ G_CALLBACK (sidebar_annots_annot_activated_cb),
+ ev_window);
+ g_signal_connect (sidebar_widget,
+ "begin_annot_add",
+ G_CALLBACK (sidebar_annots_begin_annot_add),
+ ev_window);
+ g_signal_connect (sidebar_widget,
+ "annot_add_cancelled",
+ G_CALLBACK (sidebar_annots_annot_add_cancelled),
+ ev_window);
+ gtk_widget_show (sidebar_widget);
+ ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
+ sidebar_widget);
+
+ ev_window->priv->view_box = gtk_vbox_new (FALSE, 0);
+ ev_window->priv->scrolled_window =
+ GTK_WIDGET (g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+ "shadow-type", GTK_SHADOW_IN,
+ NULL));
+ gtk_box_pack_start (GTK_BOX (ev_window->priv->view_box),
+ ev_window->priv->scrolled_window,
+ TRUE, TRUE, 0);
+ gtk_widget_show (ev_window->priv->scrolled_window);
+
+ gtk_paned_add2 (GTK_PANED (ev_window->priv->hpaned),
+ ev_window->priv->view_box);
+ gtk_widget_show (ev_window->priv->view_box);
+
+ ev_window->priv->view = ev_view_new ();
+ ev_view_set_page_cache_size (EV_VIEW (ev_window->priv->view), PAGE_CACHE_SIZE);
+ ev_view_set_model (EV_VIEW (ev_window->priv->view), ev_window->priv->model);
+
+ ev_window->priv->password_view = ev_password_view_new (GTK_WINDOW (ev_window));
+ g_signal_connect_swapped (ev_window->priv->password_view,
+ "unlock",
+ G_CALLBACK (ev_window_password_view_unlock),
+ ev_window);
+ g_signal_connect_object (ev_window->priv->view, "focus_in_event",
+ G_CALLBACK (view_actions_focus_in_cb),
+ ev_window, 0);
+ g_signal_connect_object (ev_window->priv->view, "focus_out_event",
+ G_CALLBACK (view_actions_focus_out_cb),
+ ev_window, 0);
+ g_signal_connect_object (ev_window->priv->view, "external-link",
+ G_CALLBACK (view_external_link_cb),
+ ev_window, 0);
+ g_signal_connect_object (ev_window->priv->view, "handle-link",
+ G_CALLBACK (view_handle_link_cb),
+ ev_window, 0);
+ g_signal_connect_object (ev_window->priv->view, "popup",
+ G_CALLBACK (view_menu_popup_cb),
+ ev_window, 0);
+ g_signal_connect_object (ev_window->priv->view, "selection-changed",
+ G_CALLBACK (view_selection_changed_cb),
+ ev_window, 0);
+ g_signal_connect_object (ev_window->priv->view, "annot-added",
+ G_CALLBACK (view_annot_added),
+ ev_window, 0);
+#ifdef ENABLE_DBUS
+ g_signal_connect_swapped (ev_window->priv->view, "sync-source",
+ G_CALLBACK (ev_window_sync_source),
+ ev_window);
+#endif
+ 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);
+
+ /* We own a ref on these widgets, as we can swap them in and out */
+ g_object_ref (ev_window->priv->view);
+ g_object_ref (ev_window->priv->password_view);
+
+ gtk_container_add (GTK_CONTAINER (ev_window->priv->scrolled_window),
+ ev_window->priv->view);
+
+ /* Connect to model signals */
+ g_signal_connect_swapped (ev_window->priv->model,
+ "page-changed",
+ G_CALLBACK (ev_window_page_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->model,
+ "notify::document",
+ G_CALLBACK (ev_window_document_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->model,
+ "notify::scale",
+ G_CALLBACK (ev_window_zoom_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->model,
+ "notify::sizing-mode",
+ G_CALLBACK (ev_window_sizing_mode_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->model,
+ "notify::rotation",
+ G_CALLBACK (ev_window_rotation_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->model,
+ "notify::continuous",
+ G_CALLBACK (ev_window_continuous_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->model,
+ "notify::dual-page",
+ G_CALLBACK (ev_window_dual_mode_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->model,
+ "notify::inverted-colors",
+ G_CALLBACK (ev_window_inverted_colors_changed_cb),
+ ev_window);
+
+ /* Connect sidebar signals */
+ g_signal_connect (ev_window->priv->sidebar,
+ "notify::visible",
+ G_CALLBACK (ev_window_sidebar_visibility_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->sidebar,
+ "notify::current-page",
+ G_CALLBACK (ev_window_sidebar_current_page_changed_cb),
+ ev_window);
+
+ /* Connect to find bar signals */
+ g_signal_connect (ev_window->priv->find_bar,
+ "previous",
+ G_CALLBACK (find_bar_previous_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->find_bar,
+ "next",
+ G_CALLBACK (find_bar_next_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->find_bar,
+ "close",
+ G_CALLBACK (find_bar_close_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->find_bar,
+ "notify::search-string",
+ G_CALLBACK (find_bar_search_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->find_bar,
+ "notify::case-sensitive",
+ G_CALLBACK (find_bar_search_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->find_bar,
+ "notify::visible",
+ G_CALLBACK (find_bar_visibility_changed_cb),
+ ev_window);
+ g_signal_connect (ev_window->priv->find_bar,
+ "scroll",
+ G_CALLBACK (find_bar_scroll),
+ ev_window);
+
+ /* Popups */
+ ev_window->priv->view_popup = gtk_ui_manager_get_widget (ev_window->priv->ui_manager,
+ "/DocumentPopup");
+ ev_window->priv->link = NULL;
+
+ ev_window->priv->attachment_popup = gtk_ui_manager_get_widget (ev_window->priv->ui_manager,
+ "/AttachmentPopup");
+ ev_window->priv->attach_list = NULL;
+
+ /* Media player keys */
+ mpkeys = ev_application_get_media_keys (EV_APP);
+ if (mpkeys) {
+ g_signal_connect_swapped (mpkeys, "key_pressed",
+ G_CALLBACK (ev_window_media_player_key_pressed),
+ ev_window);
+ }
+
+ /* Give focus to the document view */
+ gtk_widget_grab_focus (ev_window->priv->view);
+
+ ev_window->priv->default_settings = g_settings_new (GS_SCHEMA_NAME".Default");
+ g_settings_delay (ev_window->priv->default_settings);
+
+ /* Set it user interface params */
+ ev_window_setup_recent (ev_window);
+
+ ev_window_setup_gtk_settings (ev_window);
+
+ setup_chrome_from_metadata (ev_window);
+ update_chrome_actions (ev_window);
+ update_chrome_visibility (ev_window);
+
+ gtk_window_set_default_size (GTK_WINDOW (ev_window), 600, 600);
+
+ setup_view_from_metadata (ev_window);
+ setup_sidebar_from_metadata (ev_window);
+
+ ev_window_sizing_mode_changed_cb (ev_window->priv->model, NULL, ev_window);
+ ev_window_setup_action_sensitivity (ev_window);
+
+ /* Drag and Drop */
+ gtk_drag_dest_set (GTK_WIDGET (ev_window),
+ GTK_DEST_DEFAULT_ALL,
+ NULL, 0,
+ GDK_ACTION_COPY);
+ gtk_drag_dest_add_uri_targets (GTK_WIDGET (ev_window));
+}
+
+/**
+ * ev_window_new:
+ *
+ * Creates a #GtkWidget that represents the window.
+ *
+ * Returns: the #GtkWidget that represents the window.
+ */
+GtkWidget *
+ev_window_new (void)
+{
+ GtkWidget *ev_window;
+
+ ev_window = GTK_WIDGET (g_object_new (EV_TYPE_WINDOW,
+ "type", GTK_WINDOW_TOPLEVEL,
+ NULL));
+
+ return ev_window;
+}
+
+const gchar *
+ev_window_get_dbus_object_path (EvWindow *ev_window)
+{
+#ifdef ENABLE_DBUS
+ return ev_window->priv->dbus_object_path;
+#else
+ return NULL;
+#endif
+}
diff --git a/shell/ev-window.h b/shell/ev-window.h
new file mode 100644
index 00000000..c6e4bd1b
--- /dev/null
+++ b/shell/ev-window.h
@@ -0,0 +1,93 @@
+/* this file is part of evince, a mate document viewer
+ *
+ * Copyright (C) 2004 Martin Kretzschmar
+ *
+ * Author:
+ * Martin Kretzschmar <[email protected]>
+ *
+ * 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_WINDOW_H
+#define EV_WINDOW_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "ev-link.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ EV_WINDOW_MODE_NORMAL,
+ EV_WINDOW_MODE_FULLSCREEN,
+ EV_WINDOW_MODE_PRESENTATION
+} EvWindowRunMode;
+
+typedef struct {
+ gint start;
+ gint end;
+} EvPrintRange;
+
+typedef enum {
+ EV_PRINT_PAGE_SET_ALL,
+ EV_PRINT_PAGE_SET_EVEN,
+ EV_PRINT_PAGE_SET_ODD
+} EvPrintPageSet;
+
+typedef struct _EvWindow EvWindow;
+typedef struct _EvWindowClass EvWindowClass;
+typedef struct _EvWindowPrivate EvWindowPrivate;
+
+#define EV_TYPE_WINDOW (ev_window_get_type())
+#define EV_WINDOW(object) (G_TYPE_CHECK_INSTANCE_CAST((object), EV_TYPE_WINDOW, EvWindow))
+#define EV_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), EV_TYPE_WINDOW, EvWindowClass))
+#define EV_IS_WINDOW(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), EV_TYPE_WINDOW))
+#define EV_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), EV_TYPE_WINDOW))
+#define EV_WINDOW_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), EV_TYPE_WINDOW, EvWindowClass))
+
+
+struct _EvWindow {
+ GtkWindow base_instance;
+ EvWindowPrivate *priv;
+};
+
+struct _EvWindowClass {
+ GtkWindowClass base_class;
+};
+
+GType ev_window_get_type (void) G_GNUC_CONST;
+GtkWidget *ev_window_new (void);
+const char *ev_window_get_uri (EvWindow *ev_window);
+void ev_window_open_uri (EvWindow *ev_window,
+ const char *uri,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string);
+void ev_window_open_document (EvWindow *ev_window,
+ EvDocument *document,
+ EvLinkDest *dest,
+ EvWindowRunMode mode,
+ const gchar *search_string);
+gboolean ev_window_is_empty (const EvWindow *ev_window);
+void ev_window_print_range (EvWindow *ev_window,
+ int first_page,
+ int last_page);
+const gchar * ev_window_get_dbus_object_path (EvWindow *ev_window);
+
+
+G_END_DECLS
+
+#endif /* !EV_WINDOW_H */
diff --git a/shell/evince-icon.rc b/shell/evince-icon.rc
new file mode 100644
index 00000000..7a37b524
--- /dev/null
+++ b/shell/evince-icon.rc
@@ -0,0 +1 @@
+1 ICON "evince.ico"
diff --git a/shell/main.c b/shell/main.c
new file mode 100644
index 00000000..5e3b6774
--- /dev/null
+++ b/shell/main.c
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2004 Marco Pesenti Gritti
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "ev-application.h"
+#include "ev-debug.h"
+#include "ev-init.h"
+#include "ev-file-helpers.h"
+#include "ev-stock-icons.h"
+#include "ev-metadata.h"
+
+#ifdef WITH_SMCLIENT
+#include "eggsmclient.h"
+#ifdef GDK_WINDOWING_X11
+#include "eggdesktopfile.h"
+#endif
+#endif /* WITH_SMCLIENT */
+
+#ifdef G_OS_WIN32
+#include <io.h>
+#include <conio.h>
+#if !(_WIN32_WINNT >= 0x0500)
+#error "_WIN32_WINNT must be defined >= 0x0500"
+#endif
+#include <windows.h>
+#endif
+
+static gchar *ev_page_label;
+static gchar *ev_find_string;
+static gint ev_page_index = 0;
+static gboolean preview_mode = FALSE;
+static gboolean fullscreen_mode = FALSE;
+static gboolean presentation_mode = FALSE;
+static gboolean unlink_temp_file = FALSE;
+static gchar *print_settings;
+static const char **file_arguments = NULL;
+
+
+static gboolean
+option_version_cb (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ g_print ("%s %s\n", _("MATE Document Viewer"), VERSION);
+
+ exit (0);
+ return FALSE;
+}
+
+static const GOptionEntry goption_options[] =
+{
+ { "page-label", 'p', 0, G_OPTION_ARG_STRING, &ev_page_label, N_("The page label of the document to display."), N_("PAGE")},
+ { "page-index", 'i', 0, G_OPTION_ARG_INT, &ev_page_index, N_("The page number of the document to display."), N_("NUMBER")},
+ { "fullscreen", 'f', 0, G_OPTION_ARG_NONE, &fullscreen_mode, N_("Run evince in fullscreen mode"), NULL },
+ { "presentation", 's', 0, G_OPTION_ARG_NONE, &presentation_mode, N_("Run evince in presentation mode"), NULL },
+ { "preview", 'w', 0, G_OPTION_ARG_NONE, &preview_mode, N_("Run evince as a previewer"), NULL },
+ { "find", 'l', 0, G_OPTION_ARG_STRING, &ev_find_string, N_("The word or phrase to find in the document"), N_("STRING")},
+ { "unlink-tempfile", 'u', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &unlink_temp_file, NULL, NULL },
+ { "print-settings", 't', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &print_settings, NULL, NULL },
+ { "version", 0, G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, option_version_cb, NULL, NULL },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &file_arguments, NULL, N_("[FILEā€¦]") },
+ { NULL }
+};
+
+static gboolean
+launch_previewer (void)
+{
+ GString *cmd_str;
+ gchar *cmd;
+ gint argc;
+ gchar **argv;
+ gboolean retval = FALSE;
+ GError *error = NULL;
+
+ /* Rebuild the command line, ignoring options
+ * not supported by the previewer and taking only
+ * the first path given
+ */
+ cmd_str = g_string_new ("evince-previewer");
+
+ if (print_settings) {
+ gchar *quoted;
+
+ quoted = g_shell_quote (print_settings);
+ g_string_append_printf (cmd_str, " --print-settings %s", quoted);
+ g_free (quoted);
+ }
+
+ if (unlink_temp_file)
+ g_string_append (cmd_str, " --unlink-tempfile");
+
+ if (file_arguments) {
+ gchar *quoted;
+
+ quoted = g_shell_quote (file_arguments[0]);
+ g_string_append_printf (cmd_str, " %s", quoted);
+ g_free (quoted);
+ }
+
+ cmd = g_string_free (cmd_str, FALSE);
+ g_shell_parse_argv (cmd, &argc, &argv, &error);
+ g_free (cmd);
+
+ if (!error) {
+ retval = gdk_spawn_on_screen (gdk_screen_get_default (),
+ NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL,
+ &error);
+ g_strfreev (argv);
+ }
+
+ if (error) {
+ g_warning ("Error launching previewer: %s\n", error->message);
+ g_error_free (error);
+ }
+
+ return retval;
+}
+
+static gchar *
+get_label_from_filename (const gchar *filename)
+{
+ GFile *file;
+ gchar *label;
+ gboolean exists;
+
+ label = g_strrstr (filename, "#");
+ if (!label)
+ return NULL;
+
+ /* Filename contains a #, check
+ * whether it's part of the path
+ * or a label
+ */
+ file = g_file_new_for_commandline_arg (filename);
+ exists = g_file_query_exists (file, NULL);
+ g_object_unref (file);
+
+ return exists ? NULL : label;
+}
+
+static void
+load_files (const char **files)
+{
+ GdkScreen *screen = gdk_screen_get_default ();
+ EvWindowRunMode mode = EV_WINDOW_MODE_NORMAL;
+ gint i;
+ EvLinkDest *global_dest = NULL;
+
+ if (!files) {
+ if (!ev_application_has_window (EV_APP))
+ ev_application_open_window (EV_APP, screen, GDK_CURRENT_TIME);
+ return;
+ }
+
+ if (ev_page_label)
+ global_dest = ev_link_dest_new_page_label (ev_page_label);
+ else if (ev_page_index)
+ global_dest = ev_link_dest_new_page (MAX (0, ev_page_index - 1));
+
+ if (fullscreen_mode)
+ mode = EV_WINDOW_MODE_FULLSCREEN;
+ else if (presentation_mode)
+ mode = EV_WINDOW_MODE_PRESENTATION;
+
+ for (i = 0; files[i]; i++) {
+ const gchar *filename;
+ gchar *uri;
+ gchar *label;
+ GFile *file;
+ EvLinkDest *dest = NULL;
+ const gchar *app_uri;
+
+ filename = files[i];
+ label = get_label_from_filename (filename);
+ if (label) {
+ *label = 0;
+ label++;
+ dest = ev_link_dest_new_page_label (label);
+ } else if (global_dest) {
+ dest = g_object_ref (global_dest);
+ }
+
+ file = g_file_new_for_commandline_arg (filename);
+ uri = g_file_get_uri (file);
+ g_object_unref (file);
+
+ app_uri = ev_application_get_uri (EV_APP);
+ if (app_uri && strcmp (app_uri, uri) == 0) {
+ g_free (uri);
+ continue;
+ }
+
+
+
+ ev_application_open_uri_at_dest (EV_APP, uri, screen, dest,
+ mode, ev_find_string,
+ GDK_CURRENT_TIME);
+
+ if (dest)
+ g_object_unref (dest);
+ g_free (uri);
+ }
+}
+
+int
+main (int argc, char *argv[])
+{
+ GOptionContext *context;
+ GError *error = NULL;
+
+#ifdef G_OS_WIN32
+
+ if (fileno (stdout) != -1 &&
+ _get_osfhandle (fileno (stdout)) != -1)
+ {
+ /* stdout is fine, presumably redirected to a file or pipe */
+ }
+ else
+ {
+ typedef BOOL (* WINAPI AttachConsole_t) (DWORD);
+
+ AttachConsole_t p_AttachConsole =
+ (AttachConsole_t) GetProcAddress (GetModuleHandle ("kernel32.dll"), "AttachConsole");
+
+ if (p_AttachConsole != NULL && p_AttachConsole (ATTACH_PARENT_PROCESS))
+ {
+ freopen ("CONOUT$", "w", stdout);
+ dup2 (fileno (stdout), 1);
+ freopen ("CONOUT$", "w", stderr);
+ dup2 (fileno (stderr), 2);
+
+ }
+ }
+#endif
+
+ /* Init glib threads asap */
+ if (!g_thread_supported ())
+ g_thread_init (NULL);
+
+#ifdef ENABLE_NLS
+ /* Initialize the i18n stuff */
+ bindtextdomain (GETTEXT_PACKAGE, ev_get_locale_dir());
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ context = g_option_context_new (N_("MATE Document Viewer"));
+ g_option_context_set_translation_domain(context, GETTEXT_PACKAGE);
+ g_option_context_add_main_entries (context, goption_options, GETTEXT_PACKAGE);
+
+#ifdef WITH_SMCLIENT
+ g_option_context_add_group (context, egg_sm_client_get_option_group ());
+#endif
+
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+
+ if (!g_option_context_parse (context, &argc, &argv, &error)) {
+ g_printerr ("Cannot parse arguments: %s\n", error->message);
+ g_error_free (error);
+ g_option_context_free (context);
+
+ return 1;
+ }
+ g_option_context_free (context);
+
+ if (preview_mode) {
+ gboolean retval;
+
+ retval = launch_previewer ();
+
+ return retval ? 0 : 1;
+ }
+
+ if (!ev_init ())
+ return 1;
+
+ ev_stock_icons_init ();
+
+#if defined(WITH_SMCLIENT) && defined(GDK_WINDOWING_X11)
+ egg_set_desktop_file (MATEDATADIR "/applications/evince.desktop");
+#else
+ /* Manually set name and icon */
+ g_set_application_name (_("Document Viewer"));
+ gtk_window_set_default_icon_name ("evince");
+#endif /* WITH_SMCLIENT && GDK_WINDOWING_X11 */
+
+ ev_application_load_session (EV_APP);
+ load_files (file_arguments);
+
+ /* Change directory so we don't prevent unmounting in case the initial cwd
+ * is on an external device (see bug #575436)
+ */
+ g_chdir (g_get_home_dir ());
+
+ gtk_main ();
+
+ ev_shutdown ();
+ ev_stock_icons_shutdown ();
+
+ return 0;
+}