]> www.fi.muni.cz Git - evince.git/blobdiff - shell/ev-window.c
Fix double free that cause crash.
[evince.git] / shell / ev-window.c
index ad2767962becb24f1c5ac95bc65f3a45b9f4efcd..692014444ecac920696c0fa1981d24ab1029cc80 100644 (file)
@@ -29,6 +29,7 @@
 #endif
 
 #include "ev-window.h"
+#include "ev-window-title.h"
 #include "ev-page-action.h"
 #include "ev-sidebar.h"
 #include "ev-sidebar-links.h"
@@ -62,6 +63,7 @@
 #include "ev-metadata-manager.h"
 #include "ev-file-helpers.h"
 #include "ev-utils.h"
+#include "ev-debug.h"
 
 #include <poppler.h>
 
@@ -105,6 +107,8 @@ struct _EvWindowPrivate {
        GtkWidget *sidebar_thumbs;
        GtkWidget *sidebar_links;
 
+       EvWindowTitle *title;
+
        /* Dialogs */
        GtkWidget *properties;
 
@@ -148,9 +152,12 @@ static const GtkTargetEntry ev_drop_types[] = {
 #define PAGE_SELECTOR_ACTION   "PageSelector"
 #define ZOOM_CONTROL_ACTION    "ViewZoom"
 
-#define GCONF_CHROME_TOOLBAR   "/apps/evince/show_toolbar"
-#define GCONF_LOCKDOWN_SAVE     "/desktop/gnome/lockdown/disable_save_to_disk"
-#define GCONF_LOCKDOWN_PRINT    "/desktop/gnome/lockdown/disable_printing"
+#define GCONF_CHROME_TOOLBAR        "/apps/evince/show_toolbar"
+#define GCONF_OVERRIDE_RESTRICTIONS "/apps/evince/override_restrictions"
+#define GCONF_LOCKDOWN_SAVE         "/desktop/gnome/lockdown/disable_save_to_disk"
+#define GCONF_LOCKDOWN_PRINT        "/desktop/gnome/lockdown/disable_printing"
+
+#define FULLSCREEN_TIMEOUT 5 * 1000
 
 #define SIDEBAR_DEFAULT_SIZE    132
 #define LINKS_SIDEBAR_ID "links"
@@ -211,6 +218,9 @@ update_action_sensitivity (EvWindow *ev_window)
        gboolean ok_to_print = TRUE;
        gboolean ok_to_copy = TRUE;
        gboolean has_properties = TRUE;
+       gboolean override_restrictions = FALSE;
+       gboolean can_get_text = FALSE;
+       gboolean ok_to_copy_text = FALSE;
        GConfClient *client;
 
        view = EV_VIEW (ev_window->priv->view);
@@ -229,7 +239,11 @@ update_action_sensitivity (EvWindow *ev_window)
                has_pages = has_document && n_pages > 0;
        }
 
-       if (info && info->fields_mask & EV_DOCUMENT_INFO_PERMISSIONS) {
+       client = gconf_client_get_default ();
+       override_restrictions = gconf_client_get_bool (client, 
+                                                      GCONF_OVERRIDE_RESTRICTIONS, 
+                                                      NULL);
+       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);
        }
@@ -241,8 +255,6 @@ update_action_sensitivity (EvWindow *ev_window)
                has_properties = FALSE;
        }
        
-       client = gconf_client_get_default ();
-
        if (gconf_client_get_bool (client, GCONF_LOCKDOWN_SAVE, NULL)) {
                ok_to_copy = FALSE;
        }
@@ -250,7 +262,14 @@ update_action_sensitivity (EvWindow *ev_window)
        if (gconf_client_get_bool (client, GCONF_LOCKDOWN_PRINT, NULL)) {
                ok_to_print = FALSE;
        }
+       
+       g_object_unref (client);
 
+       if (has_document && ev_document_can_get_text (document)) {
+               can_get_text = TRUE;
+               ok_to_copy_text = ev_view_get_has_selection (view);
+       }
+       
        /* File menu */
        /* "FileOpen": always sensitive */
        set_action_sensitive (ev_window, "FileSaveAs", has_document && ok_to_copy);
@@ -260,8 +279,8 @@ update_action_sensitivity (EvWindow *ev_window)
 
         /* Edit menu */
        sensitive = has_pages && ev_document_can_get_text (document);
-       set_action_sensitive (ev_window, "EditCopy", sensitive && ok_to_copy);
-       set_action_sensitive (ev_window, "EditSelectAll", sensitive && ok_to_copy);
+       set_action_sensitive (ev_window, "EditCopy", sensitive && ok_to_copy_text);
+       set_action_sensitive (ev_window, "EditSelectAll", sensitive && can_get_text);
        set_action_sensitive (ev_window, "EditFind",
                              has_pages && EV_IS_DOCUMENT_FIND (document));
        set_action_sensitive (ev_window, "Slash",
@@ -299,7 +318,9 @@ update_action_sensitivity (EvWindow *ev_window)
        set_action_sensitive (ev_window, PAGE_SELECTOR_ACTION, has_pages);
        set_action_sensitive (ev_window, ZOOM_CONTROL_ACTION,  has_pages);
 
-       if (has_pages && ev_view_get_sizing_mode (view) == EV_SIZING_FREE) {
+       if (has_pages &&
+           ev_view_get_sizing_mode (view) != EV_SIZING_FIT_WIDTH &&
+           ev_view_get_sizing_mode (view) != EV_SIZING_BEST_FIT) {
                GtkAction *action;
                float      zoom;
                float      real_zoom;
@@ -534,6 +555,14 @@ ev_window_is_empty (const EvWindow *ev_window)
                (ev_window->priv->xfer_job == NULL);
 }
 
+static void
+unable_to_load_dialog_response_cb (GtkWidget *dialog,
+                                  gint       response_id,
+                                  EvWindow  *ev_window)
+{
+       gtk_widget_destroy (dialog);
+}
+
 static void
 unable_to_load (EvWindow   *ev_window,
                const char *error_message)
@@ -547,66 +576,10 @@ unable_to_load (EvWindow   *ev_window,
                                         _("Unable to open document"));
        gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
                                                  "%s", error_message);
-       gtk_dialog_run (GTK_DIALOG (dialog));
-       gtk_widget_destroy (dialog);
-}
-
-static void
-update_window_title (EvDocument *document, GParamSpec *pspec, EvWindow *ev_window)
-{
-       char *title = NULL;
-       char *doc_title = NULL;
-       gboolean password_needed;
-
-       password_needed = (ev_window->priv->password_document != NULL);
-       if (document && ev_window->priv->page_cache) {
-               doc_title = g_strdup (ev_page_cache_get_title (ev_window->priv->page_cache));
-
-               /* Make sure we get a valid title back */
-               if (doc_title) {
-                       if (doc_title[0] == '\000' ||
-                           !g_utf8_validate (doc_title, -1, NULL)) {
-                               doc_title = NULL;
-                       }
-               }
-       }
-
-       if (doc_title) {
-               char *p;
-
-               for (p = doc_title; *p; ++p) {
-                       /* an '\n' byte is always ASCII, no need for UTF-8 special casing */
-                       if (*p == '\n')
-                               *p = ' ';
-               }
-       }
-
-       if (doc_title == NULL && ev_window->priv->uri) {
-               char *display_name;
-
-               display_name = gnome_vfs_format_uri_for_display (ev_window->priv->uri);
-               doc_title = g_path_get_basename (display_name);
-               g_free (display_name);
-       }
-
-       if (password_needed) {
-               if (doc_title == NULL) {
-                       title = g_strdup (_("Document Viewer - Password Required"));
-               } else {
-                       title = g_strdup_printf (_("%s - Password Required"), doc_title);
-               }
-       } else {
-               if (doc_title == NULL) {
-                       title = g_strdup (_("Document Viewer"));
-               } else {
-                       title = g_strdup (doc_title);
-               }
-       }
-
-       gtk_window_set_title (GTK_WINDOW (ev_window), title);
-
-       g_free (doc_title);
-       g_free (title);
+       g_signal_connect (dialog, "response",
+                         G_CALLBACK (unable_to_load_dialog_response_cb),
+                         ev_window);
+       gtk_widget_show (dialog);
 }
 
 static void
@@ -638,6 +611,18 @@ update_document_mode (EvWindow *window, EvDocumentMode mode)
        }
 }
 
+static void
+update_sidebar_visibility (EvWindow *window)
+{
+       char *uri = window->priv->uri;
+       GValue sidebar_visibility = { 0, };
+
+       if (uri && ev_metadata_manager_get (uri, "sidebar_visibility", &sidebar_visibility)) {
+               set_widget_visibility (window->priv->sidebar,
+                                      g_value_get_boolean (&sidebar_visibility));
+       }
+}
+
 static void
 setup_document_from_metadata (EvWindow *window)
 {
@@ -651,6 +636,40 @@ setup_document_from_metadata (EvWindow *window)
        }
 }
 
+static void
+setup_sidebar_from_metadata (EvWindow *window, EvDocument *document)
+{
+       char *uri = window->priv->uri;
+       GtkWidget *sidebar = window->priv->sidebar;
+       GtkWidget *links = window->priv->sidebar_links;
+       GtkWidget *thumbs = window->priv->sidebar_thumbs;
+       GValue sidebar_size = { 0, };
+       GValue sidebar_page = { 0, };
+
+       if (ev_metadata_manager_get (uri, "sidebar_size", &sidebar_size)) {
+               gtk_paned_set_position (GTK_PANED (window->priv->hpaned),
+                                       g_value_get_int (&sidebar_size));
+       }
+
+       if (ev_metadata_manager_get (uri, "sidebar_page", &sidebar_page)) {
+               const char *page_id = g_value_get_string (&sidebar_page);
+
+               if (strcmp (page_id, "links") == 0) {
+                       ev_sidebar_set_page (EV_SIDEBAR (sidebar), links);
+               } else if (strcmp (page_id, "thumbnails")) {
+                       ev_sidebar_set_page (EV_SIDEBAR (sidebar), thumbs);
+               }
+       } else {
+               if (ev_sidebar_page_support_document (EV_SIDEBAR_PAGE (links), document)) {
+                       ev_sidebar_set_page (EV_SIDEBAR (sidebar), links);
+               } else if (ev_sidebar_page_support_document (EV_SIDEBAR_PAGE (thumbs), document)) {
+                       ev_sidebar_set_page (EV_SIDEBAR (sidebar), thumbs);
+               }
+       }
+
+       update_sidebar_visibility (window);
+}
+
 static void
 ev_window_setup_document (EvWindow *ev_window)
 {
@@ -664,10 +683,6 @@ ev_window_setup_document (EvWindow *ev_window)
        ev_window->priv->page_cache = ev_page_cache_get (ev_window->priv->document);
        g_signal_connect (ev_window->priv->page_cache, "page-changed", G_CALLBACK (page_changed_cb), ev_window);
 
-       g_signal_connect_object (G_OBJECT (document),
-                                "notify::title",
-                                G_CALLBACK (update_window_title),
-                                ev_window, 0);
        if (EV_IS_DOCUMENT_FIND (document)) {
                g_signal_connect_object (G_OBJECT (document),
                                         "find_changed",
@@ -683,7 +698,8 @@ ev_window_setup_document (EvWindow *ev_window)
                ev_view_set_document (view, document);
        }
 
-       update_window_title (document, NULL, ev_window);
+       ev_window_title_set_document (ev_window->priv->title, document);
+       ev_window_title_set_uri (ev_window->priv->title, ev_window->priv->uri);
        action = gtk_action_group_get_action (ev_window->priv->action_group, PAGE_SELECTOR_ACTION);
        ev_page_action_set_document (EV_PAGE_ACTION (action), document);
        update_action_sensitivity (ev_window);
@@ -697,6 +713,7 @@ ev_window_setup_document (EvWindow *ev_window)
        }
 
        setup_document_from_metadata (ev_window);
+       setup_sidebar_from_metadata (ev_window, document);
 }
 
 static void
@@ -726,7 +743,9 @@ password_dialog_response (GtkWidget *password_dialog,
 
                ev_window->priv->password_document = NULL;
                ev_window->priv->password_uri = NULL;
-               
+
+               ev_window_title_set_type (ev_window->priv->title, EV_WINDOW_TITLE_DOCUMENT);
+
                ev_job_queue_add_job (ev_window->priv->load_job, EV_JOB_PRIORITY_HIGH);
                
                gtk_widget_destroy (password_dialog);
@@ -753,7 +772,9 @@ ev_window_popup_password_dialog (EvWindow *ev_window)
 
        gtk_widget_set_sensitive (ev_window->priv->password_view, FALSE);
 
-       update_window_title (ev_window->priv->password_document, NULL, ev_window);
+       ev_window_title_set_uri (ev_window->priv->title, ev_window->priv->password_uri);
+       ev_window_title_set_type (ev_window->priv->title, EV_WINDOW_TITLE_PASSWORD);
+
        if (ev_window->priv->password_dialog == NULL) {
                ev_window->priv->password_dialog =
                        g_object_new (EV_TYPE_PASSWORD_DIALOG, "uri", ev_window->priv->password_uri, NULL);
@@ -912,18 +933,6 @@ ev_window_xfer_job_cb  (EvJobXfer *job,
        }               
 }
 
-static void
-update_sidebar_visibility (EvWindow *window)
-{
-       char *uri = window->priv->uri;
-       GValue sidebar_visibility = { 0, };
-
-       if (uri && ev_metadata_manager_get (uri, "sidebar_visibility", &sidebar_visibility)) {
-               set_widget_visibility (window->priv->sidebar,
-                                      g_value_get_boolean (&sidebar_visibility));
-       }
-}
-
 static void
 setup_view_from_metadata (EvWindow *window)
 {
@@ -942,8 +951,6 @@ setup_view_from_metadata (EvWindow *window)
        GValue presentation = { 0, };
        GValue fullscreen = { 0, };
        GValue rotation = { 0, };
-       GValue sidebar_size = { 0, };
-       GValue sidebar_page = { 0, };
 
        if (window->priv->uri == NULL) {
                return;
@@ -1029,26 +1036,6 @@ setup_view_from_metadata (EvWindow *window)
                        }
                }
        }
-
-       /* Sidebar */
-       if (ev_metadata_manager_get (uri, "sidebar_size", &sidebar_size)) {
-               gtk_paned_set_position (GTK_PANED (window->priv->hpaned),
-                                       g_value_get_int (&sidebar_size));
-       }
-
-       if (ev_metadata_manager_get (uri, "sidebar_page", &sidebar_page)) {
-               const char *page_id = g_value_get_string (&sidebar_page);
-
-               if (strcmp (page_id, "links") == 0) {
-                       ev_sidebar_set_page (EV_SIDEBAR (window->priv->sidebar),
-                                            window->priv->sidebar_links);
-               } else if (strcmp (page_id, "thumbnails")) {
-                       ev_sidebar_set_page (EV_SIDEBAR (window->priv->sidebar),
-                                            window->priv->sidebar_thumbs);
-               }
-       }
-
-       update_sidebar_visibility (window);
 }
 
 void
@@ -1586,6 +1573,7 @@ ev_window_update_fullscreen_popup (EvWindow *window)
 {
        GtkWidget *popup = window->priv->fullscreen_popup;
        int popup_width, popup_height;
+       GdkScreen *screen;
        GdkRectangle screen_rect;
        gboolean toolbar;
 
@@ -1599,10 +1587,10 @@ ev_window_update_fullscreen_popup (EvWindow *window)
        popup_width = popup->requisition.width;
        popup_height = popup->requisition.height;
 
-       /* FIXME multihead */
-       gdk_screen_get_monitor_geometry (gdk_screen_get_default (),
+       screen = gtk_widget_get_screen (GTK_WIDGET (window));
+       gdk_screen_get_monitor_geometry (screen,
                        gdk_screen_get_monitor_at_window
-                        (gdk_screen_get_default (),
+                        (screen,
                          GTK_WIDGET (window)->window),
                          &screen_rect);
        if (toolbar) {
@@ -1694,7 +1682,7 @@ fullscreen_set_timeout (EvWindow *window)
                g_source_destroy (window->priv->fullscreen_timeout_source);
        }
 
-       source = g_timeout_source_new (1000);
+       source = g_timeout_source_new (FULLSCREEN_TIMEOUT);
        g_source_set_callback (source, fullscreen_timeout_cb, window, NULL);
        g_source_attach (source, NULL);
        window->priv->fullscreen_timeout_source = source;
@@ -1780,6 +1768,7 @@ ev_window_create_fullscreen_popup (EvWindow *window)
        GtkWidget *popup;
        GtkWidget *hbox;
        GtkWidget *button;
+       GdkScreen *screen;
 
        window->priv->fullscreen_toolbar = egg_editable_toolbar_new_with_model
                        (window->priv->ui_manager, ev_application_get_toolbars_model (EV_APP));
@@ -1798,14 +1787,17 @@ ev_window_create_fullscreen_popup (EvWindow *window)
 
        gtk_window_set_resizable (GTK_WINDOW (popup), FALSE);
 
-       /* FIXME multihead */
-       g_signal_connect_object (gdk_screen_get_default (), "size-changed",
+       screen = gtk_widget_get_screen (GTK_WIDGET (window));
+       g_signal_connect_object (screen, "size-changed",
                                 G_CALLBACK (screen_size_changed_cb),
                                 window, 0);
        g_signal_connect_object (popup, "size_request",
                                 G_CALLBACK (fullscreen_popup_size_request_cb),
                                 window, 0);
 
+       gtk_window_set_screen (GTK_WINDOW (popup),
+                              gtk_widget_get_screen (GTK_WIDGET (window)));
+
        return popup;
 }
 
@@ -2009,6 +2001,32 @@ ev_window_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
        return GTK_WIDGET_CLASS (ev_window_parent_class)->focus_out_event (widget, event);
 }
 
+static void
+ev_window_screen_changed (GtkWidget *widget,
+                         GdkScreen *old_screen)
+{
+       EvWindow *window = EV_WINDOW (widget);
+       EvWindowPrivate *priv = window->priv;
+       GdkScreen *screen;
+
+       if (GTK_WIDGET_CLASS (ev_window_parent_class)->screen_changed) {
+               GTK_WIDGET_CLASS (ev_window_parent_class)->screen_changed (widget, old_screen);
+       }
+
+       if (priv->fullscreen_popup != NULL) {
+               g_signal_handlers_disconnect_by_func
+                       (old_screen, G_CALLBACK (screen_size_changed_cb), window);
+
+               screen = gtk_widget_get_screen (widget);
+               g_signal_connect_object (screen, "size-changed",
+                                        G_CALLBACK (screen_size_changed_cb),
+                                        window, 0);
+               gtk_window_set_screen (GTK_WINDOW (priv->fullscreen_popup), screen);
+
+               ev_window_update_fullscreen_popup (window);
+       }
+}
+
 static void
 ev_window_set_page_mode (EvWindow         *window,
                         EvWindowPageMode  page_mode)
@@ -2076,6 +2094,7 @@ ev_window_cmd_edit_toolbar (GtkAction *action, EvWindow *ev_window)
                                              GTK_STOCK_CLOSE,
                                              GTK_RESPONSE_CLOSE, 
                                              NULL);
+       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 (GTK_DIALOG (dialog)->vbox), 2);
        gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
@@ -2122,7 +2141,7 @@ ev_window_cmd_go_previous_page (GtkAction *action, EvWindow *ev_window)
 {
         g_return_if_fail (EV_IS_WINDOW (ev_window));
 
-       ev_page_cache_prev_page (ev_window->priv->page_cache);
+       ev_view_previous_page (EV_VIEW (ev_window->priv->view));
 }
 
 static void
@@ -2130,7 +2149,7 @@ ev_window_cmd_go_next_page (GtkAction *action, EvWindow *ev_window)
 {
         g_return_if_fail (EV_IS_WINDOW (ev_window));
 
-       ev_page_cache_next_page (ev_window->priv->page_cache);
+       ev_view_next_page (EV_VIEW (ev_window->priv->view));
 }
 
 static void
@@ -2411,6 +2430,12 @@ ev_window_rotation_changed_cb (EvView *view, GParamSpec *pspec, EvWindow *window
                                       rotation);
 }
 
+static void
+ev_window_has_selection_changed_cb (EvView *view, GParamSpec *pspec, EvWindow *window)
+{
+       update_action_sensitivity (window);
+}
+
 static void     
 ev_window_dual_mode_changed_cb (EvView *view, GParamSpec *pspec, EvWindow *ev_window)
 {
@@ -2648,6 +2673,7 @@ find_bar_search_changed_cb (EggFindBar *find_bar,
                        ev_document_find_cancel (EV_DOCUMENT_FIND (ev_window->priv->document));
                        ev_document_doc_mutex_unlock ();
 
+                       update_action_sensitivity (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));
@@ -2682,23 +2708,14 @@ zoom_control_changed_cb (EphyZoomAction *action,
 static void
 ev_window_finalize (GObject *object)
 {
-       gboolean empty = TRUE;
-       GList *list, *windows;
-
+       GList *windows = ev_application_get_windows (EV_APP);
 
-       windows = gtk_window_list_toplevels ();
-
-       for (list = windows; list; list = list->next) {
-               if (EV_IS_WINDOW (list->data)) {
-                       empty = FALSE;
-                       break;
-               }
-       }
-       
-       if (empty)
+       if (windows == NULL) {
                ev_application_shutdown (EV_APP);
+       } else {
+               g_list_free (windows);
+       }
        
-       g_list_free (windows);
        G_OBJECT_CLASS (ev_window_parent_class)->finalize (object);
 }
 
@@ -2708,6 +2725,11 @@ ev_window_dispose (GObject *object)
        EvWindow *window = EV_WINDOW (object);
        EvWindowPrivate *priv = window->priv;
 
+       if (priv->title) {
+               ev_window_title_free (priv->title);
+               priv->title = NULL;
+       }
+
        if (priv->recent_view) {
                g_object_unref (priv->recent_view);
                priv->recent_view = NULL;
@@ -2799,6 +2821,7 @@ ev_window_class_init (EvWindowClass *ev_window_class)
 
        widget_class->focus_in_event = ev_window_focus_in_event;
        widget_class->focus_out_event = ev_window_focus_out_event;
+       widget_class->screen_changed = ev_window_screen_changed;
 
        g_type_class_add_private (g_object_class, sizeof (EvWindowPrivate));
 }
@@ -2815,40 +2838,31 @@ static const GtkActionEntry entries[] = {
        { "FileOpen", GTK_STOCK_OPEN, N_("_Open..."), "<control>O",
          N_("Open an existing document"),
          G_CALLBACK (ev_window_cmd_file_open) },
-               { "FileSaveAs", GTK_STOCK_SAVE_AS, N_("_Save a Copy..."), NULL,
-         N_("Save the current document with a new filename"),
+               { "FileSaveAs", GTK_STOCK_SAVE_AS, N_("_Save a Copy..."), NULL, NULL,
          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",
-         N_("View the properties of this document"),
+       { "FileProperties", GTK_STOCK_PROPERTIES, N_("P_roperties"), "<alt>Return", NULL,
          G_CALLBACK (ev_window_cmd_file_properties) },                       
-       { "FileCloseWindow", GTK_STOCK_CLOSE, NULL, "<control>W",
-         N_("Close this window"),
+       { "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",
-          N_("Copy text from the document"),
+        { "EditCopy", GTK_STOCK_COPY, NULL, "<control>C", NULL,
           G_CALLBACK (ev_window_cmd_edit_copy) },
-       { "EditSelectAll", NULL, N_("Select _All"), "<control>A",
-         N_("Select the entire page"),
+       { "EditSelectAll", NULL, N_("Select _All"), "<control>A", NULL,
          G_CALLBACK (ev_window_cmd_edit_select_all) },
         { "EditFind", GTK_STOCK_FIND, NULL, "<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",
-         N_("Find next occurrence of the word or phrase"),
+       { "EditFindNext", NULL, N_("Find Ne_xt"), "<control>G", NULL,
          G_CALLBACK (ev_window_cmd_edit_find_next) },
-        { "EditToolbar", NULL, N_("T_oolbar"), NULL,
-          N_("Customize the toolbar"),
+        { "EditToolbar", NULL, N_("T_oolbar"), NULL, NULL,
           G_CALLBACK (ev_window_cmd_edit_toolbar) },
-       { "EditRotateLeft", NULL, N_("Rotate _Left"), NULL,
-         N_("Rotate the document to the left"),
+       { "EditRotateLeft", NULL, N_("Rotate _Left"), NULL, NULL,
          G_CALLBACK (ev_window_cmd_edit_rotate_left) },
-       { "EditRotateRight", NULL, N_("Rotate _Right"), NULL,
-         N_("Rotate the document to the right"),
+       { "EditRotateRight", NULL, N_("Rotate _Right"), NULL, NULL,
          G_CALLBACK (ev_window_cmd_edit_rotate_right) },
 
         /* View menu */
@@ -2877,12 +2891,10 @@ static const GtkActionEntry entries[] = {
           G_CALLBACK (ev_window_cmd_go_last_page) },
 
        /* Help menu */
-       { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1",
-         N_("Display help for the viewer application"),
+       { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), "F1", NULL,
          G_CALLBACK (ev_window_cmd_help_contents) },
 
-       { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL,
-         N_("Display credits for the document viewer creators"),
+       { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL,
          G_CALLBACK (ev_window_cmd_help_about) },
 
        /* Toolbar-only */
@@ -2893,50 +2905,41 @@ static const GtkActionEntry entries[] = {
        /* Accellerators */
        { "Escape", NULL, "", "Escape", "",
          G_CALLBACK (ev_window_cmd_escape) },
-        { "Slash", GTK_STOCK_FIND, NULL, "slash",
-          N_("Find a word or phrase in the document"),
+        { "Slash", GTK_STOCK_FIND, NULL, "slash", NULL,
           G_CALLBACK (ev_window_cmd_edit_find) },
-        { "PageDown", NULL, "", "Page_Down",
-          N_("Scroll one page forward"),
+        { "PageDown", NULL, "", "Page_Down", NULL,
           G_CALLBACK (ev_window_cmd_scroll_forward) },
-        { "PageUp", NULL, "", "Page_Up",
-          N_("Scroll one page backward"),
+        { "PageUp", NULL, "", "Page_Up", NULL,
           G_CALLBACK (ev_window_cmd_scroll_backward) },
-        { "Space", NULL, "", "space",
-          N_("Scroll one page forward"),
+        { "Space", NULL, "", "space", NULL,
           G_CALLBACK (ev_window_cmd_scroll_forward) },
-        { "ShiftSpace", NULL, "", "<shift>space",
-          N_("Scroll one page backward"),
+        { "ShiftSpace", NULL, "", "<shift>space", NULL,
           G_CALLBACK (ev_window_cmd_scroll_backward) },
-        { "BackSpace", NULL, "", "BackSpace",
-          N_("Scroll one page backward"),
+        { "BackSpace", NULL, "", "BackSpace", NULL,
           G_CALLBACK (ev_window_cmd_scroll_backward) },
-        { "ShiftBackSpace", NULL, "", "<shift>BackSpace",
-          N_("Scroll one page forward"),
+        { "ShiftBackSpace", NULL, "", "<shift>BackSpace", NULL,
           G_CALLBACK (ev_window_cmd_scroll_forward) },
-        { "Plus", GTK_STOCK_ZOOM_IN, NULL, "plus",
-          N_("Enlarge the document"),
+        { "Plus", GTK_STOCK_ZOOM_IN, NULL, "plus", NULL,
           G_CALLBACK (ev_window_cmd_view_zoom_in) },
-        { "CtrlEqual", GTK_STOCK_ZOOM_IN, NULL, "<control>equal",
-          N_("Enlarge the document"),
+        { "CtrlEqual", GTK_STOCK_ZOOM_IN, NULL, "<control>equal", NULL,
           G_CALLBACK (ev_window_cmd_view_zoom_in) },
-        { "Minus", GTK_STOCK_ZOOM_OUT, NULL, "minus",
-          N_("Shrink the document"),
+        { "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",
-          N_("Focus the page selector"),
+        { "FocusPageSelector", NULL, "", "<control>l", NULL,
           G_CALLBACK (ev_window_cmd_focus_page_selector) },
-        { "GoBackwardFast", NULL, "", "<shift>Page_Up",
-          N_("Go ten pages backward"),
+        { "GoBackwardFast", NULL, "", "<shift>Page_Up", NULL,
           G_CALLBACK (ev_window_cmd_go_backward) },
-        { "GoForwardFast", NULL, "", "<shift>Page_Down",
-          N_("Go ten pages forward"),
+        { "GoForwardFast", NULL, "", "<shift>Page_Down", NULL,
           G_CALLBACK (ev_window_cmd_go_forward) },
-        { "KpPlus", GTK_STOCK_ZOOM_IN, NULL, "KP_Add",
-          N_("Enlarge the document"),
+        { "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",
-          N_("Shrink the document"),
+        { "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) },
 };
 
@@ -3158,6 +3161,7 @@ ev_window_set_view_accels_sensitivity (EvWindow *window, gboolean sensitive)
                set_action_sensitive (window, "Minus", sensitive);
                set_action_sensitive (window, "KpPlus", sensitive);
                set_action_sensitive (window, "KpMinus", sensitive);
+               set_action_sensitive (window, "Equal", sensitive);
        }
 }
 
@@ -3243,6 +3247,68 @@ sidebar_links_link_activated_cb (EvSidebarLinks *sidebar_links, EvLink *link, Ev
        ev_view_goto_link (EV_VIEW (window->priv->view), link);
 }
 
+static void
+launch_link (EvWindow *window, EvLink *link)
+{
+       const char *filename = ev_link_get_filename (link);
+       char *uri = NULL;
+
+       if (filename  && g_path_is_absolute (filename)) {
+               uri = gnome_vfs_get_uri_from_local_path (filename);
+       } else {
+               GnomeVFSURI *base_uri, *resolved_uri;
+
+               base_uri = gnome_vfs_uri_new (window->priv->uri);
+               if (base_uri && filename) {
+                       resolved_uri = gnome_vfs_uri_resolve_relative (base_uri, filename);     
+                       if (resolved_uri) {
+                               uri = gnome_vfs_uri_to_string (resolved_uri, GNOME_VFS_URI_HIDE_NONE);
+                               gnome_vfs_uri_unref (resolved_uri);
+                       }
+                       gnome_vfs_uri_unref (base_uri);
+               }
+       }
+
+       if (uri) {
+               gnome_vfs_url_show (uri);
+       } else {
+               gnome_vfs_url_show (filename);
+       }
+
+       g_free (uri);
+
+       /* 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, EvLink *link)
+{
+       const char *uri;
+       char *escaped;
+
+       uri = ev_link_get_uri (link);
+       escaped = gnome_vfs_escape_host_and_path_string (uri);
+
+       gnome_vfs_url_show (escaped);
+       g_free (escaped);
+}
+
+static void
+view_external_link_cb (EvView *view, EvLink *link, EvWindow *window)
+{
+       switch (ev_link_get_link_type (link)) {
+       case EV_LINK_TYPE_EXTERNAL_URI:
+               launch_external_uri (window, link);
+               break;
+       case EV_LINK_TYPE_LAUNCH:
+               launch_link (window, link);
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
 static void
 ev_window_init (EvWindow *ev_window)
 {
@@ -3259,7 +3325,7 @@ ev_window_init (EvWindow *ev_window)
        ev_window->priv = EV_WINDOW_GET_PRIVATE (ev_window);
 
        ev_window->priv->page_mode = PAGE_MODE_DOCUMENT;
-       update_window_title (NULL, NULL, ev_window);
+       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);
@@ -3383,6 +3449,9 @@ ev_window_init (EvWindow *ev_window)
        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);
        gtk_widget_show (ev_window->priv->view);
        gtk_widget_show (ev_window->priv->password_view);
 
@@ -3419,6 +3488,10 @@ ev_window_init (EvWindow *ev_window)
                          "notify::rotation",
                          G_CALLBACK (ev_window_rotation_changed_cb),
                          ev_window);
+       g_signal_connect (ev_window->priv->view,
+                         "notify::has-selection",
+                         G_CALLBACK (ev_window_has_selection_changed_cb),
+                         ev_window);
 
        ev_window->priv->find_bar = egg_find_bar_new ();
        gtk_box_pack_end (GTK_BOX (ev_window->priv->main_box),