]> www.fi.muni.cz Git - evince.git/blob - shell/ev-window.c
368919ad9ccc84a7b8c824aabbf157f24641af82
[evince.git] / shell / ev-window.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; c-indent-level: 8 -*- */
2 /* this file is part of evince, a gnome document viewer
3  *
4  *  Copyright (C) 2004 Martin Kretzschmar
5  *  Copyright (C) 2004 Red Hat, Inc.
6  *  Copyright (C) 2000, 2001, 2002, 2003, 2004 Marco Pesenti Gritti
7  *  Copyright (C) 2003, 2004 Christian Persch
8  *
9  *  Author:
10  *    Martin Kretzschmar <martink@gnome.org>
11  *
12  * Evince is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * Evince is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "ev-window.h"
32 #include "ev-page-action.h"
33 #include "ev-sidebar.h"
34 #include "ev-sidebar-links.h"
35 #include "ev-sidebar-thumbnails.h"
36 #include "ev-view.h"
37 #include "ev-page-view.h"
38 #include "ev-password.h"
39 #include "ev-password-view.h"
40 #include "ev-print-job.h"
41 #include "ev-document-find.h"
42 #include "ev-document-security.h"
43 #include "eggfindbar.h"
44
45 #include "pdf-document.h"
46 #include "pixbuf-document.h"
47 #include "ps-document.h"
48
49 #include <glib/gi18n.h>
50 #include <gtk/gtk.h>
51
52 #include <libgnomevfs/gnome-vfs-mime-utils.h>
53 #include <libgnomeprintui/gnome-print-dialog.h>
54
55 #include <string.h>
56
57 #include "ev-application.h"
58 #include "ev-stock-icons.h"
59
60 typedef enum {
61         PAGE_MODE_SINGLE_PAGE,
62         PAGE_MODE_CONTINUOUS_PAGE,
63         PAGE_MODE_PASSWORD,
64 } EvWindowPageMode;
65
66 struct _EvWindowPrivate {
67         GtkWidget *main_box;
68         GtkWidget *hpaned;
69         GtkWidget *sidebar;
70         GtkWidget *thumbs_sidebar;
71         GtkWidget *find_bar;
72         GtkWidget *scrolled_window;
73         GtkWidget *view;
74         GtkWidget *page_view;
75         GtkWidget *password_view;
76         GtkActionGroup *action_group;
77         GtkUIManager *ui_manager;
78         GtkWidget *statusbar;
79         guint help_message_cid;
80         guint view_message_cid;
81         GtkWidget *exit_fullscreen_popup;
82         char *uri;
83
84         EvDocument *document;
85
86         EvWindowPageMode page_mode;
87         /* These members are used temporarily when in PAGE_MODE_PASSWORD */
88         EvDocument *password_document;
89         GtkWidget *password_dialog;
90         char *password_uri;
91
92         gboolean fullscreen_mode;
93 };
94 #define EV_WINDOW_GET_PRIVATE(object) \
95         (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_WINDOW, EvWindowPrivate))
96
97
98 #define PAGE_SELECTOR_ACTION "PageSelector"
99
100
101 static void     ev_window_update_fullscreen_popup (EvWindow         *window);
102 static void     ev_window_set_page_mode           (EvWindow         *window,
103                                                    EvWindowPageMode  page_mode);
104 static gboolean start_loading_document            (EvWindow         *ev_window,
105                                                    EvDocument       *document,
106                                                    const char       *uri);
107
108
109 G_DEFINE_TYPE (EvWindow, ev_window, GTK_TYPE_WINDOW)
110
111 static void
112 set_action_sensitive (EvWindow   *ev_window,
113                       const char *name,
114                       gboolean    sensitive)
115 {
116         GtkAction *action = gtk_action_group_get_action (ev_window->priv->action_group,
117                                                          name);
118         gtk_action_set_sensitive (action, sensitive);
119 }
120
121 static void
122 update_action_sensitivity (EvWindow *ev_window)
123 {
124         EvDocument *document;
125         EvWindowPageMode page_mode;
126         EvView *view;
127
128         document = ev_window->priv->document;
129         page_mode = ev_window->priv->page_mode;
130
131         view = EV_VIEW (ev_window->priv->view);
132
133         /* File menu */
134         /* "FileOpen": always sensitive */
135         set_action_sensitive (ev_window, "FileSaveAs", document!=NULL);
136         set_action_sensitive (ev_window, "FilePrint", document!=NULL);
137         /* "FileCloseWindow": always sensitive */
138
139         /* Edit menu */
140         set_action_sensitive (ev_window, "EditCopy", document!=NULL);
141         set_action_sensitive (ev_window, "EditSelectAll", document!=NULL);
142
143         if (document)
144                 set_action_sensitive (ev_window, "EditFind", EV_IS_DOCUMENT_FIND (document));
145         else
146                 set_action_sensitive (ev_window, "EditFind", FALSE);
147
148         /* View menu */
149         set_action_sensitive (ev_window, "ViewZoomIn", document!=NULL);
150         set_action_sensitive (ev_window, "ViewZoomOut", document!=NULL);
151         set_action_sensitive (ev_window, "ViewNormalSize", document!=NULL);
152         set_action_sensitive (ev_window, "ViewBestFit", document!=NULL);
153         set_action_sensitive (ev_window, "ViewPageWidth", document!=NULL);
154
155         /* Go menu */
156         if (document) {
157                 int n_pages;
158                 int page;
159
160                 page = ev_view_get_page (EV_VIEW (ev_window->priv->view));
161                 n_pages = ev_document_get_n_pages (document);
162
163                 set_action_sensitive (ev_window, "GoPageUp", page > 1);
164                 set_action_sensitive (ev_window, "GoPageDown", page < n_pages);
165                 set_action_sensitive (ev_window, "GoFirstPage", page > 1);
166                 set_action_sensitive (ev_window, "GoLastPage", page < n_pages);
167         } else {
168                 set_action_sensitive (ev_window, "GoFirstPage", FALSE);
169                 set_action_sensitive (ev_window, "GoPageUp", FALSE);
170                 set_action_sensitive (ev_window, "GoPageDown", FALSE);
171                 set_action_sensitive (ev_window, "GoLastPage", FALSE);
172         }
173
174         /* Page View radio group */
175         if (document) {
176                 set_action_sensitive (ev_window, "SinglePage", page_mode != PAGE_MODE_PASSWORD);
177                 set_action_sensitive (ev_window, "ContinuousPage", page_mode != PAGE_MODE_PASSWORD);
178         } else {
179                 set_action_sensitive (ev_window, "SinglePage", FALSE);
180                 set_action_sensitive (ev_window, "ContinuousPage", FALSE);
181         }
182         /* Help menu */
183         /* "HelpContents": always sensitive */
184         /* "HelpAbout": always sensitive */
185
186         /* Toolbar-specific actions: */
187         set_action_sensitive (ev_window, PAGE_SELECTOR_ACTION, document!=NULL);
188 }
189
190 void
191 ev_window_open_page (EvWindow *ev_window, int page)
192 {
193         ev_view_set_page (EV_VIEW (ev_window->priv->view), page);
194 }
195
196 void
197 ev_window_open_link (EvWindow *ev_window, EvLink *link)
198 {
199         ev_view_go_to_link (EV_VIEW (ev_window->priv->view), link);
200 }
201
202 gboolean
203 ev_window_is_empty (const EvWindow *ev_window)
204 {
205         g_return_val_if_fail (EV_IS_WINDOW (ev_window), FALSE);
206
207         return ev_window->priv->document == NULL;
208 }
209
210 static void
211 unable_to_load (EvWindow   *ev_window,
212                 const char *error_message)
213 {
214         GtkWidget *dialog;
215
216         dialog = gtk_message_dialog_new (GTK_WINDOW (ev_window),
217                                          GTK_DIALOG_DESTROY_WITH_PARENT,
218                                          GTK_MESSAGE_ERROR,
219                                          GTK_BUTTONS_CLOSE,
220                                          _("Unable to open document"));
221         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
222                                                   "%s", error_message);
223         gtk_dialog_run (GTK_DIALOG (dialog));
224         gtk_widget_destroy (dialog);
225 }
226
227 /* Would be nice to have this in gdk-pixbuf */
228 static gboolean
229 mime_type_supported_by_gdk_pixbuf (const gchar *mime_type)
230 {
231         GSList *formats, *list;
232         gboolean retval = FALSE;
233
234         formats = gdk_pixbuf_get_formats ();
235
236         list = formats;
237         while (list) {
238                 GdkPixbufFormat *format = list->data;
239                 int i;
240                 gchar **mime_types;
241
242                 if (gdk_pixbuf_format_is_disabled (format))
243                         continue;
244
245                 mime_types = gdk_pixbuf_format_get_mime_types (format);
246
247                 for (i = 0; mime_types[i] != NULL; i++) {
248                         if (strcmp (mime_types[i], mime_type) == 0) {
249                                 retval = TRUE;
250                                 break;
251                         }
252                 }
253
254                 if (retval)
255                         break;
256
257                 list = list->next;
258         }
259
260         g_slist_free (formats);
261
262         return retval;
263 }
264
265 static void
266 update_window_title (EvDocument *document, GParamSpec *pspec, EvWindow *ev_window)
267 {
268         char *title = NULL;
269         char *doc_title = NULL;
270         gboolean password_needed;
271
272         password_needed = (ev_window->priv->password_document != NULL);
273
274         if (document) {
275                 doc_title = ev_document_get_title (document);
276
277                 /* Make sure we get a valid title back */
278                 if (doc_title) {
279                         if (doc_title[0] == '\000' ||
280                             !g_utf8_validate (doc_title, -1, NULL)) {
281                                 g_free (doc_title);
282                                 doc_title = NULL;
283                         }
284                 }
285         }
286         if (doc_title == NULL && ev_window->priv->uri) {
287                 doc_title = g_path_get_basename (ev_window->priv->uri);
288         }
289
290         if (password_needed) {
291                 if (doc_title == NULL) {
292                         title = g_strdup (_("Document Viewer - Password Required"));
293                 } else {
294                         title = g_strdup_printf (_("%s - Password Required"), doc_title);
295                 }
296         } else {
297                 if (doc_title == NULL) {
298                         title = g_strdup (_("Document Viewer"));
299                 } else {
300                         title = g_strdup (doc_title);
301                 }
302         }
303
304         gtk_window_set_title (GTK_WINDOW (ev_window), title);
305
306         g_free (doc_title);
307         g_free (title);
308 }
309
310 static void
311 update_total_pages (EvWindow *ev_window)
312 {
313         GtkAction *action;
314         int pages;
315
316         pages = ev_document_get_n_pages (ev_window->priv->document);
317         action = gtk_action_group_get_action
318                 (ev_window->priv->action_group, PAGE_SELECTOR_ACTION);
319         ev_page_action_set_total_pages (EV_PAGE_ACTION (action), pages);
320 }
321
322 /* This function assumes that ev_window just had ev_window->document set.
323  */
324 static void
325 ev_window_setup_document (EvWindow *ev_window)
326 {
327         EvDocument *document;
328         EvView *view = EV_VIEW (ev_window->priv->view);
329         EvPageView *page_view = EV_PAGE_VIEW (ev_window->priv->page_view);
330         EvSidebar *sidebar = EV_SIDEBAR (ev_window->priv->sidebar);
331
332         document = ev_window->priv->document;
333
334         g_signal_connect_object (G_OBJECT (document),
335                                  "notify::title",
336                                  G_CALLBACK (update_window_title),
337                                  ev_window, 0);
338
339         ev_window_set_page_mode (ev_window, PAGE_MODE_SINGLE_PAGE);
340
341         ev_sidebar_set_document (sidebar, document);
342         ev_view_set_document (view, document);
343         ev_page_view_set_document (page_view, document);
344
345         update_window_title (document, NULL, ev_window);
346         update_total_pages (ev_window);
347         update_action_sensitivity (ev_window);
348 }
349
350 static void
351 password_dialog_response (GtkWidget *password_dialog,
352                           gint       response_id,
353                           EvWindow  *ev_window)
354 {
355         char *password;
356         
357         if (response_id == GTK_RESPONSE_OK) {
358                 EvDocument *document;
359                 gchar *uri;
360
361                 password = ev_password_dialog_get_password (password_dialog);
362                 if (password)
363                         ev_document_security_set_password (EV_DOCUMENT_SECURITY (ev_window->priv->password_document),
364                                                            password);
365                 g_free (password);
366
367                 document = ev_window->priv->password_document;
368                 uri = ev_window->priv->password_uri;
369
370                 ev_window->priv->password_document = NULL;
371                 ev_window->priv->password_uri = NULL;
372
373                 if (start_loading_document (ev_window, document, uri)) {
374                         gtk_widget_destroy (password_dialog);
375                 }
376
377                 g_object_unref (document);
378                 g_free (uri);
379
380                 return;
381         }
382
383         gtk_widget_set_sensitive (ev_window->priv->password_view, TRUE);
384         gtk_widget_destroy (password_dialog);
385 }
386
387 /* Called either by start_loading_document or by the "unlock" callback on the
388  * password_view page.  It assumes that ev_window->priv->password_* has been set
389  * correctly.  These are cleared by password_dialog_response() */
390
391 static void
392 ev_window_popup_password_dialog (EvWindow *ev_window)
393 {
394         g_assert (ev_window->priv->password_document);
395         g_assert (ev_window->priv->password_uri);
396
397         gtk_widget_set_sensitive (ev_window->priv->password_view, FALSE);
398
399         update_window_title (ev_window->priv->password_document, NULL, ev_window);
400         if (ev_window->priv->password_dialog == NULL) {
401                 gchar *file_name;
402
403                 file_name = g_path_get_basename (ev_window->priv->password_uri);
404                 ev_window->priv->password_dialog =
405                         ev_password_dialog_new (GTK_WIDGET (ev_window), file_name);
406                 g_object_add_weak_pointer (G_OBJECT (ev_window->priv->password_dialog),
407                                            (gpointer *) &(ev_window->priv->password_dialog));
408                 g_signal_connect (ev_window->priv->password_dialog,
409                                   "response",
410                                   G_CALLBACK (password_dialog_response),
411                                   ev_window);
412                 g_free (file_name);
413                 gtk_widget_show (ev_window->priv->password_dialog);
414         } else {
415                 ev_password_dialog_set_bad_pass (ev_window->priv->password_dialog);
416         }
417 }
418
419 /* This wil try to load the document.  It might be called multiple times on the
420  * same document by the password dialog.
421  *
422  * Since the flow of the error dialog is very confusing, we assume that both
423  * document and uri will go away after this function is called, and thus we need
424  * to ref/dup them.  Additionally, it needs to clear
425  * ev_window->priv->password_{uri,document}, and thus people who call this
426  * function should _not_ necessarily expect those to exist after being
427  * called. */
428 static gboolean
429 start_loading_document (EvWindow   *ev_window,
430                         EvDocument *document,
431                         const char *uri)
432 {
433         gboolean result;
434         GError *error = NULL;
435
436         g_assert (document);
437         g_assert (document != ev_window->priv->document);
438         g_assert (uri);
439         if (ev_window->priv->password_document) {
440                 g_object_unref (ev_window->priv->password_document);
441                 ev_window->priv->password_document = NULL;
442         }
443         if (ev_window->priv->password_uri) {
444                 g_free (ev_window->priv->password_uri);
445                 ev_window->priv->password_uri = NULL;
446         }
447
448         result = ev_document_load (document, uri, &error);
449
450         /* Success! */
451         if (result) {
452                 if (ev_window->priv->document)
453                         g_object_unref (ev_window->priv->document);
454                 ev_window->priv->document = g_object_ref (document);
455                 ev_window_setup_document (ev_window);
456
457                 return TRUE;
458         }
459
460         /* unable to load the document */
461         g_assert (error != NULL);
462
463         if (error->domain == EV_DOCUMENT_ERROR &&
464             error->code == EV_DOCUMENT_ERROR_ENCRYPTED) {
465                 char *file_name;
466
467                 ev_window->priv->password_document = g_object_ref (document);
468                 ev_window->priv->password_uri = g_strdup (uri);
469
470                 file_name = g_path_get_basename (uri);
471                 ev_password_view_set_file_name (EV_PASSWORD_VIEW (ev_window->priv->password_view),
472                                                 file_name);
473                 g_free (file_name);
474                 ev_window_set_page_mode (ev_window, PAGE_MODE_PASSWORD);
475
476                 ev_window_popup_password_dialog (ev_window);
477         } else {
478                 unable_to_load (ev_window, error->message);
479         }
480         g_error_free (error);
481
482         return FALSE;
483 }
484
485 void
486 ev_window_open (EvWindow *ev_window, const char *uri)
487 {
488         EvDocument *document = NULL;
489         char *mime_type;
490
491         g_free (ev_window->priv->uri);
492         ev_window->priv->uri = g_strdup (uri);
493
494         mime_type = gnome_vfs_get_mime_type (uri);
495
496         if (mime_type == NULL)
497                 document = NULL;
498         else if (!strcmp (mime_type, "application/pdf"))
499                 document = g_object_new (PDF_TYPE_DOCUMENT, NULL);
500         else if (!strcmp (mime_type, "application/postscript") ||
501                  !strcmp (mime_type, "image/x-eps"))
502                 document = g_object_new (PS_TYPE_DOCUMENT, NULL);
503         else if (mime_type_supported_by_gdk_pixbuf (mime_type))
504                 document = g_object_new (PIXBUF_TYPE_DOCUMENT, NULL);
505
506         if (document) {
507                 start_loading_document (ev_window, document, uri);
508                 /* See the comment on start_loading_document on ref counting.
509                  * As the password dialog flow is confusing, we're very explicit
510                  * on ref counting. */
511                 g_object_unref (document);
512         } else {
513                 char *error_message;
514
515                 error_message = g_strdup_printf (_("Unhandled MIME type: '%s'"),
516                                                  mime_type?mime_type:"<Unknown MIME Type>");
517                 unable_to_load (ev_window, error_message);
518                 g_free (error_message);
519         }
520
521         g_free (mime_type);
522 }
523
524 static void
525 ev_window_cmd_file_open (GtkAction *action, EvWindow *ev_window)
526 {
527         ev_application_open (EV_APP, NULL);
528 }
529
530 /* FIXME
531 static gboolean
532 overwrite_existing_file (GtkWindow *window, const gchar *file_name)
533 {
534         GtkWidget *msgbox;
535         gchar *utf8_file_name;
536         AtkObject *obj;
537         gint ret;
538
539         utf8_file_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
540         msgbox = gtk_message_dialog_new (
541                 window,
542                 (GtkDialogFlags)GTK_DIALOG_DESTROY_WITH_PARENT,
543                 GTK_MESSAGE_QUESTION,
544                 GTK_BUTTONS_NONE,
545                 _("A file named \"%s\" already exists."),
546                 utf8_file_name);
547         g_free (utf8_file_name);
548
549         gtk_message_dialog_format_secondary_text (
550                 GTK_MESSAGE_DIALOG (msgbox),
551                 _("Do you want to replace it with the one you are saving?"));
552
553         gtk_dialog_add_button (GTK_DIALOG (msgbox),
554                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
555
556         gtk_dialog_add_button (GTK_DIALOG (msgbox),
557                                _("_Replace"), GTK_RESPONSE_YES);
558
559         gtk_dialog_set_default_response (GTK_DIALOG (msgbox),
560                                          GTK_RESPONSE_CANCEL);
561
562         obj = gtk_widget_get_accessible (msgbox);
563
564         if (GTK_IS_ACCESSIBLE (obj))
565                 atk_object_set_name (obj, _("Question"));
566
567         ret = gtk_dialog_run (GTK_DIALOG (msgbox));
568         gtk_widget_destroy (msgbox);
569
570         return (ret == GTK_RESPONSE_YES);
571 }
572 */
573
574 static void
575 save_error_dialog (GtkWindow *window, const gchar *file_name)
576 {
577         GtkWidget *error_dialog;
578
579         error_dialog = gtk_message_dialog_new (
580                 window,
581                 (GtkDialogFlags)GTK_DIALOG_DESTROY_WITH_PARENT,
582                 GTK_MESSAGE_ERROR,
583                 GTK_BUTTONS_CLOSE,
584                 _("The file could not be saved as \"%s\"."),
585                 file_name);
586
587         /* Easy way to make the text bold while keeping the string
588          * above free from pango markup (FIXME ?) */
589         gtk_message_dialog_format_secondary_text (
590                 GTK_MESSAGE_DIALOG (error_dialog), " ");
591
592         gtk_dialog_run (GTK_DIALOG (error_dialog));
593         gtk_widget_destroy (error_dialog);
594 }
595
596 static void
597 ev_window_cmd_save_as (GtkAction *action, EvWindow *ev_window)
598 {
599         GtkWidget *fc;
600         GtkFileFilter *pdf_filter, *all_filter;
601         gchar *uri = NULL;
602
603         fc = gtk_file_chooser_dialog_new (
604                 _("Save a Copy"),
605                 NULL, GTK_FILE_CHOOSER_ACTION_SAVE,
606                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
607                 GTK_STOCK_SAVE, GTK_RESPONSE_OK,
608                 NULL);
609         gtk_window_set_modal (GTK_WINDOW (fc), TRUE);
610
611         pdf_filter = gtk_file_filter_new ();
612         gtk_file_filter_set_name (pdf_filter, _("PDF Documents"));
613         gtk_file_filter_add_mime_type (pdf_filter, "application/pdf");
614         gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fc), pdf_filter);
615
616         all_filter = gtk_file_filter_new ();
617         gtk_file_filter_set_name (all_filter, _("All Files"));
618         gtk_file_filter_add_pattern (all_filter, "*");
619         gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fc), all_filter);
620         gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (fc), pdf_filter);
621
622         gtk_dialog_set_default_response (GTK_DIALOG (fc), GTK_RESPONSE_OK);
623
624         gtk_widget_show (fc);
625
626         while (gtk_dialog_run (GTK_DIALOG (fc)) == GTK_RESPONSE_OK) {
627                 uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (fc));
628
629 /* FIXME
630                 if (g_file_test (uri, G_FILE_TEST_EXISTS) &&
631                     !overwrite_existing_file (GTK_WINDOW (fc), uri))
632                                 continue;
633 */
634
635                 if (ev_document_save (ev_window->priv->document, uri, NULL))
636                         break;
637                 else
638                         save_error_dialog (GTK_WINDOW (fc), uri);
639         }
640         gtk_widget_destroy (fc);
641 }
642
643 static gboolean
644 using_postscript_printer (GnomePrintConfig *config)
645 {
646         const guchar *driver;
647         const guchar *transport;
648
649         driver = gnome_print_config_get (
650                 config, (const guchar *)"Settings.Engine.Backend.Driver");
651
652         transport = gnome_print_config_get (
653                 config, (const guchar *)"Settings.Transport.Backend");
654
655         if (driver) {
656                 if (!strcmp ((const gchar *)driver, "gnome-print-ps"))
657                         return TRUE;
658                 else
659                         return FALSE;
660         } else  if (transport) {
661                 if (!strcmp ((const gchar *)transport, "CUPS"))
662                         return TRUE;
663         }
664
665         return FALSE;
666 }
667
668 static void
669 ev_window_print (EvWindow *ev_window)
670 {
671         GnomePrintConfig *config;
672         GnomePrintJob *job;
673         GtkWidget *print_dialog;
674         EvPrintJob *print_job = NULL;
675
676         g_return_if_fail (EV_IS_WINDOW (ev_window));
677         g_return_if_fail (ev_window->priv->document != NULL);
678
679         config = gnome_print_config_default ();
680         job = gnome_print_job_new (config);
681
682         print_dialog = gnome_print_dialog_new (job, _("Print"),
683                                                (GNOME_PRINT_DIALOG_RANGE |
684                                                 GNOME_PRINT_DIALOG_COPIES));
685         gtk_dialog_set_response_sensitive (GTK_DIALOG (print_dialog),
686                                            GNOME_PRINT_DIALOG_RESPONSE_PREVIEW,
687                                            FALSE);
688
689         while (TRUE) {
690                 int response;
691                 response = gtk_dialog_run (GTK_DIALOG (print_dialog));
692
693                 if (response != GNOME_PRINT_DIALOG_RESPONSE_PRINT)
694                         break;
695
696                 /* FIXME: Change this when we have the first backend
697                  * that can print more than postscript
698                  */
699                 if (!using_postscript_printer (config)) {
700                         GtkWidget *dialog;
701
702                         dialog = gtk_message_dialog_new (
703                                 GTK_WINDOW (print_dialog), GTK_DIALOG_MODAL,
704                                 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
705                                 _("Printing is not supported on this printer."));
706                         gtk_message_dialog_format_secondary_text (
707                                 GTK_MESSAGE_DIALOG (dialog),
708                                 _("You were trying to print to a printer using the \"%s\" driver. This program requires a PostScript printer driver."),
709                                 gnome_print_config_get (
710                                         config, "Settings.Engine.Backend.Driver"));
711                         gtk_dialog_run (GTK_DIALOG (dialog));
712                         gtk_widget_destroy (dialog);
713
714                         continue;
715                 }
716
717                 print_job = g_object_new (EV_TYPE_PRINT_JOB,
718                                           "gnome_print_job", job,
719                                           "document", ev_window->priv->document,
720                                           "print_dialog", print_dialog,
721                                           NULL);
722                 break;
723         }
724
725         gtk_widget_destroy (print_dialog);
726
727         if (print_job != NULL) {
728                 ev_print_job_print (print_job, GTK_WINDOW (ev_window));
729                 g_object_unref (print_job);
730         }
731 }
732
733 static void
734 ev_window_cmd_file_print (GtkAction *action, EvWindow *ev_window)
735 {
736         ev_window_print (ev_window);
737 }
738
739 static void
740 ev_window_cmd_file_close_window (GtkAction *action, EvWindow *ev_window)
741 {
742         g_return_if_fail (EV_IS_WINDOW (ev_window));
743
744         gtk_widget_destroy (GTK_WIDGET (ev_window));
745 }
746
747 static void
748 find_not_supported_dialog (EvWindow   *ev_window)
749 {
750         GtkWidget *dialog;
751
752         /* If you change this so it isn't modal, be sure you don't
753          * allow multiple copies of the dialog...
754          */
755
756         dialog = gtk_message_dialog_new (GTK_WINDOW (ev_window),
757                                          GTK_DIALOG_DESTROY_WITH_PARENT,
758                                          GTK_MESSAGE_ERROR,
759                                          GTK_BUTTONS_CLOSE,
760                                          _("The \"Find\" feature will not work with this document"));
761         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
762                                                   _("Searching for text is only supported for PDF documents."));
763         gtk_dialog_run (GTK_DIALOG (dialog));
764         gtk_widget_destroy (dialog);
765 }
766
767 static void
768 ev_window_cmd_edit_select_all (GtkAction *action, EvWindow *ev_window)
769 {
770         g_return_if_fail (EV_IS_WINDOW (ev_window));
771
772         ev_view_select_all (EV_VIEW (ev_window->priv->view));
773 }
774
775 static void
776 ev_window_cmd_edit_find (GtkAction *action, EvWindow *ev_window)
777 {
778         g_return_if_fail (EV_IS_WINDOW (ev_window));
779
780         if (ev_window->priv->document == NULL) {
781                 g_printerr ("We should have set the Find menu item insensitive since there's no document\n");
782         } else if (!EV_IS_DOCUMENT_FIND (ev_window->priv->document)) {
783                 find_not_supported_dialog (ev_window);
784         } else {
785                 gtk_widget_show (ev_window->priv->find_bar);
786
787                 if (ev_window->priv->exit_fullscreen_popup)
788                         ev_window_update_fullscreen_popup (ev_window);
789
790                 egg_find_bar_grab_focus (EGG_FIND_BAR (ev_window->priv->find_bar));
791         }
792 }
793
794 static void
795 ev_window_cmd_edit_copy (GtkAction *action, EvWindow *ev_window)
796 {
797         g_return_if_fail (EV_IS_WINDOW (ev_window));
798
799         ev_view_copy (EV_VIEW (ev_window->priv->view));
800 }
801
802 static void
803 ev_window_update_fullscreen_popup (EvWindow *window)
804 {
805         GtkWidget *popup = window->priv->exit_fullscreen_popup;
806         int popup_width, popup_height;
807         GdkRectangle screen_rect;
808
809         g_return_if_fail (popup != NULL);
810
811         if (!popup)
812                 return;
813
814         popup_width = popup->requisition.width;
815         popup_height = popup->requisition.height;
816
817         /* FIXME multihead */
818         gdk_screen_get_monitor_geometry (gdk_screen_get_default (),
819                         gdk_screen_get_monitor_at_window
820                         (gdk_screen_get_default (),
821                          GTK_WIDGET (window)->window),
822                          &screen_rect);
823
824         if (GTK_WIDGET_VISIBLE (window->priv->find_bar)) {
825                 GtkRequisition req;
826
827                 gtk_widget_size_request (window->priv->find_bar, &req);
828
829                 screen_rect.height -= req.height;
830         }
831
832         if (gtk_widget_get_direction (popup) == GTK_TEXT_DIR_RTL)
833         {
834                 gtk_window_move (GTK_WINDOW (popup),
835                                  screen_rect.x + screen_rect.width - popup_width,
836                                  screen_rect.height - popup_height);
837         }
838         else
839         {
840                 gtk_window_move (GTK_WINDOW (popup),
841                                 screen_rect.x, screen_rect.height - popup_height);
842         }
843 }
844
845 static void
846 screen_size_changed_cb (GdkScreen *screen,
847                         EvWindow *window)
848 {
849         ev_window_update_fullscreen_popup (window);
850 }
851
852 static void
853 destroy_exit_fullscreen_popup (EvWindow *window)
854 {
855         if (window->priv->exit_fullscreen_popup != NULL)
856         {
857                 /* FIXME multihead */
858                 g_signal_handlers_disconnect_by_func
859                         (gdk_screen_get_default (),
860                          G_CALLBACK (screen_size_changed_cb), window);
861
862                 gtk_widget_destroy (window->priv->exit_fullscreen_popup);
863                 window->priv->exit_fullscreen_popup = NULL;
864         }
865 }
866
867 static void
868 exit_fullscreen_button_clicked_cb (GtkWidget *button, EvWindow *window)
869 {
870         GtkAction *action;
871
872         action = gtk_action_group_get_action (window->priv->action_group, "ViewFullscreen");
873         g_return_if_fail (action != NULL);
874
875         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
876 }
877
878 static void
879 fullscreen_popup_size_request_cb (GtkWidget *popup, GtkRequisition *req, EvWindow *window)
880 {
881         ev_window_update_fullscreen_popup (window);
882 }
883
884 static void
885 ev_window_fullscreen (EvWindow *window)
886 {
887         GtkWidget *popup, *button, *icon, *label, *hbox, *main_menu;
888
889         window->priv->fullscreen_mode = TRUE;
890
891         popup = gtk_window_new (GTK_WINDOW_POPUP);
892         window->priv->exit_fullscreen_popup = popup;
893
894         button = gtk_button_new ();
895         g_signal_connect (button, "clicked",
896                           G_CALLBACK (exit_fullscreen_button_clicked_cb),
897                           window);
898         gtk_widget_show (button);
899         gtk_container_add (GTK_CONTAINER (popup), button);
900
901         hbox = gtk_hbox_new (FALSE, 2);
902         gtk_widget_show (hbox);
903         gtk_container_add (GTK_CONTAINER (button), hbox);
904
905         icon = gtk_image_new_from_stock (GTK_STOCK_QUIT, GTK_ICON_SIZE_BUTTON);
906         gtk_widget_show (icon);
907         gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
908
909         label = gtk_label_new (_("Exit Fullscreen"));
910         gtk_widget_show (label);
911         gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
912
913         gtk_window_set_resizable (GTK_WINDOW (popup), FALSE);
914
915         /* FIXME multihead */
916         g_signal_connect (gdk_screen_get_default (), "size-changed",
917                           G_CALLBACK (screen_size_changed_cb), window);
918         g_signal_connect (popup, "size_request",
919                           G_CALLBACK (fullscreen_popup_size_request_cb), window);
920
921         main_menu = gtk_ui_manager_get_widget (window->priv->ui_manager, "/MainMenu");
922         gtk_widget_hide (main_menu);
923         gtk_widget_hide (window->priv->statusbar);
924
925         ev_window_update_fullscreen_popup (window);
926
927         gtk_widget_show (popup);
928 }
929
930 static void
931 ev_window_unfullscreen (EvWindow *window)
932 {
933         GtkWidget *main_menu;
934
935         window->priv->fullscreen_mode = FALSE;
936
937         main_menu = gtk_ui_manager_get_widget (window->priv->ui_manager, "/MainMenu");
938         gtk_widget_show (main_menu);
939         gtk_widget_show (window->priv->statusbar);
940
941         destroy_exit_fullscreen_popup (window);
942 }
943
944 static void
945 ev_window_cmd_view_fullscreen (GtkAction *action, EvWindow *ev_window)
946 {
947         gboolean fullscreen;
948
949         g_return_if_fail (EV_IS_WINDOW (ev_window));
950
951         fullscreen = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
952
953         if (fullscreen) {
954                 gtk_window_fullscreen (GTK_WINDOW (ev_window));
955         } else {
956                 gtk_window_unfullscreen (GTK_WINDOW (ev_window));
957         }
958 }
959
960 static gboolean
961 ev_window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event, EvWindow *window)
962 {
963         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
964         {
965                 GtkActionGroup *action_group;
966                 GtkAction *action;
967                 gboolean fullscreen;
968
969                 fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
970
971                 if (fullscreen)
972                 {
973                         ev_window_fullscreen (window);
974                 }
975                 else
976                 {
977                         ev_window_unfullscreen (window);
978                 }
979
980                 action_group = window->priv->action_group;
981
982                 action = gtk_action_group_get_action (action_group, "ViewFullscreen");
983                 g_signal_handlers_block_by_func
984                         (action, G_CALLBACK (ev_window_cmd_view_fullscreen), window);
985                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), fullscreen);
986                 g_signal_handlers_unblock_by_func
987                         (action, G_CALLBACK (ev_window_cmd_view_fullscreen), window);
988
989         }
990
991         return FALSE;
992 }
993
994 static gboolean
995 ev_window_focus_out_cb (GtkWidget *widget, GdkEventFocus *event, EvWindow *ev_window)
996 {
997         gtk_window_unfullscreen (GTK_WINDOW (ev_window));
998
999         return FALSE;
1000 }
1001
1002 static void
1003 ev_window_set_page_mode (EvWindow         *window,
1004                          EvWindowPageMode  page_mode)
1005 {
1006         GtkWidget *child = NULL;
1007         GtkWidget *real_child;
1008
1009         if (window->priv->page_mode == page_mode)
1010                 return;
1011
1012         window->priv->page_mode = page_mode;
1013
1014         switch (page_mode) {
1015         case PAGE_MODE_SINGLE_PAGE:
1016                 child = window->priv->view;
1017                 break;
1018         case PAGE_MODE_PASSWORD:
1019                 child = window->priv->password_view;
1020                 break;
1021         case PAGE_MODE_CONTINUOUS_PAGE:
1022                 child = window->priv->page_view;
1023                 break;
1024         default:
1025                 g_assert_not_reached ();
1026         }
1027
1028         real_child = gtk_bin_get_child (GTK_BIN (window->priv->scrolled_window));
1029         if (child != real_child) {
1030                 gtk_container_remove (GTK_CONTAINER (window->priv->scrolled_window),
1031                                       real_child);
1032                 gtk_container_add (GTK_CONTAINER (window->priv->scrolled_window),
1033                                    child);
1034         }
1035         update_action_sensitivity (window);
1036 }
1037
1038 static void
1039 ev_window_cmd_view_zoom_in (GtkAction *action, EvWindow *ev_window)
1040 {
1041         g_return_if_fail (EV_IS_WINDOW (ev_window));
1042
1043         ev_view_zoom_in (EV_VIEW (ev_window->priv->view));
1044 }
1045
1046 static void
1047 ev_window_cmd_view_zoom_out (GtkAction *action, EvWindow *ev_window)
1048 {
1049         g_return_if_fail (EV_IS_WINDOW (ev_window));
1050
1051         ev_view_zoom_out (EV_VIEW (ev_window->priv->view));
1052 }
1053
1054 static void
1055 ev_window_cmd_view_normal_size (GtkAction *action, EvWindow *ev_window)
1056 {
1057         g_return_if_fail (EV_IS_WINDOW (ev_window));
1058
1059         ev_view_normal_size (EV_VIEW (ev_window->priv->view));
1060 }
1061
1062 static void
1063 ev_window_cmd_view_best_fit (GtkAction *action, EvWindow *ev_window)
1064 {
1065         g_return_if_fail (EV_IS_WINDOW (ev_window));
1066
1067         ev_view_best_fit (EV_VIEW (ev_window->priv->view));
1068 }
1069
1070 static void
1071 ev_window_cmd_view_page_width (GtkAction *action, EvWindow *ev_window)
1072 {
1073         g_return_if_fail (EV_IS_WINDOW (ev_window));
1074
1075         ev_view_fit_width (EV_VIEW (ev_window->priv->view));
1076 }
1077
1078 static void
1079 ev_window_cmd_go_page_up (GtkAction *action, EvWindow *ev_window)
1080 {
1081         g_return_if_fail (EV_IS_WINDOW (ev_window));
1082
1083         ev_view_set_page (EV_VIEW (ev_window->priv->view),
1084                           ev_view_get_page (EV_VIEW (ev_window->priv->view)) - 1);
1085 }
1086
1087 static void
1088 ev_window_cmd_go_page_down (GtkAction *action, EvWindow *ev_window)
1089 {
1090         g_return_if_fail (EV_IS_WINDOW (ev_window));
1091
1092         ev_view_set_page (EV_VIEW (ev_window->priv->view),
1093                           ev_view_get_page (EV_VIEW (ev_window->priv->view)) + 1);
1094 }
1095
1096 static void
1097 ev_window_cmd_go_first_page (GtkAction *action, EvWindow *ev_window)
1098 {
1099         g_return_if_fail (EV_IS_WINDOW (ev_window));
1100
1101         ev_view_set_page (EV_VIEW (ev_window->priv->view), 1);
1102 }
1103
1104 static void
1105 ev_window_cmd_go_last_page (GtkAction *action, EvWindow *ev_window)
1106 {
1107         g_return_if_fail (EV_IS_WINDOW (ev_window));
1108
1109         ev_view_set_page (EV_VIEW (ev_window->priv->view), G_MAXINT);
1110 }
1111
1112 static void
1113 ev_window_cmd_help_contents (GtkAction *action, EvWindow *ev_window)
1114 {
1115         g_return_if_fail (EV_IS_WINDOW (ev_window));
1116
1117         /* FIXME */
1118 }
1119
1120 static void
1121 ev_window_cmd_help_about (GtkAction *action, EvWindow *ev_window)
1122 {
1123         const char *authors[] = {
1124                 N_("Many..."),
1125                 NULL
1126         };
1127
1128         const char *documenters[] = {
1129                 N_("Not so many..."),
1130                 NULL
1131         };
1132
1133         const char *license[] = {
1134                 N_("Evince is free software; you can redistribute it and/or modify\n"
1135                    "it under the terms of the GNU General Public License as published by\n"
1136                    "the Free Software Foundation; either version 2 of the License, or\n"
1137                    "(at your option) any later version.\n"),
1138                 N_("Evince is distributed in the hope that it will be useful,\n"
1139                    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1140                    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1141                    "GNU General Public License for more details.\n"),
1142                 N_("You should have received a copy of the GNU General Public License\n"
1143                    "along with Evince; if not, write to the Free Software Foundation, Inc.,\n"
1144                    "59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n")
1145         };
1146
1147         char *license_trans;
1148
1149 #ifdef ENABLE_NLS
1150         const char **p;
1151
1152         for (p = authors; *p; ++p)
1153                 *p = _(*p);
1154
1155         for (p = documenters; *p; ++p)
1156                 *p = _(*p);
1157 #endif
1158
1159         license_trans = g_strconcat (_(license[0]), "\n", _(license[1]), "\n",
1160                                      _(license[2]), "\n", NULL);
1161
1162         gtk_show_about_dialog (
1163                 GTK_WINDOW (ev_window),
1164                 "name", _("Evince"),
1165                 "version", VERSION,
1166                 "copyright",
1167                 _("\xc2\xa9 1996-2004 The Evince authors"),
1168                 "license", license_trans,
1169                 "website", "http://www.gnome.org/projects/evince",
1170                 "comments", _("PostScript and PDF File Viewer."),
1171                 "authors", authors,
1172                 "documenters", documenters,
1173                 "translator-credits", _("translator-credits"),
1174                 NULL);
1175
1176         g_free (license_trans);
1177 }
1178
1179 static void
1180 ev_window_view_toolbar_cb (GtkAction *action, EvWindow *ev_window)
1181 {
1182         g_object_set (
1183                 G_OBJECT (gtk_ui_manager_get_widget (
1184                                   ev_window->priv->ui_manager,
1185                                   "/ToolBar")),
1186                 "visible",
1187                 gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)),
1188                 NULL);
1189 }
1190
1191 static void
1192 ev_window_view_statusbar_cb (GtkAction *action, EvWindow *ev_window)
1193 {
1194         g_object_set (
1195                 ev_window->priv->statusbar,
1196                 "visible",
1197                 gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)),
1198                 NULL);
1199 }
1200
1201 static void
1202 ev_window_view_sidebar_cb (GtkAction *action, EvWindow *ev_window)
1203 {
1204         g_object_set (
1205                 ev_window->priv->sidebar,
1206                 "visible",
1207                 gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)),
1208                 NULL);
1209 }
1210
1211 static void
1212 menu_item_select_cb (GtkMenuItem *proxy, EvWindow *ev_window)
1213 {
1214         GtkAction *action;
1215         char *message;
1216
1217         action = g_object_get_data (G_OBJECT (proxy), "gtk-action");
1218         g_return_if_fail (action != NULL);
1219
1220         g_object_get (G_OBJECT (action), "tooltip", &message, NULL);
1221         if (message) {
1222                 gtk_statusbar_push (GTK_STATUSBAR (ev_window->priv->statusbar),
1223                                     ev_window->priv->help_message_cid, message);
1224                 g_free (message);
1225         }
1226 }
1227
1228 static void
1229 menu_item_deselect_cb (GtkMenuItem *proxy, EvWindow *ev_window)
1230 {
1231         gtk_statusbar_pop (GTK_STATUSBAR (ev_window->priv->statusbar),
1232                            ev_window->priv->help_message_cid);
1233 }
1234
1235 static void
1236 connect_proxy_cb (GtkUIManager *ui_manager, GtkAction *action,
1237                   GtkWidget *proxy, EvWindow *ev_window)
1238 {
1239         if (GTK_IS_MENU_ITEM (proxy)) {
1240                 g_signal_connect (proxy, "select",
1241                                   G_CALLBACK (menu_item_select_cb), ev_window);
1242                 g_signal_connect (proxy, "deselect",
1243                                   G_CALLBACK (menu_item_deselect_cb),
1244                                   ev_window);
1245         }
1246 }
1247
1248 static void
1249 disconnect_proxy_cb (GtkUIManager *ui_manager, GtkAction *action,
1250                      GtkWidget *proxy, EvWindow *ev_window)
1251 {
1252         if (GTK_IS_MENU_ITEM (proxy)) {
1253                 g_signal_handlers_disconnect_by_func
1254                         (proxy, G_CALLBACK (menu_item_select_cb), ev_window);
1255                 g_signal_handlers_disconnect_by_func
1256                         (proxy, G_CALLBACK (menu_item_deselect_cb), ev_window);
1257         }
1258 }
1259
1260 static void
1261 update_current_page (EvWindow *ev_window,
1262                      EvView   *view)
1263 {
1264         int page;
1265         GtkAction *action;
1266         EvSidebarThumbnails *thumbs;
1267
1268         thumbs = EV_SIDEBAR_THUMBNAILS (ev_window->priv->thumbs_sidebar);
1269         ev_sidebar_thumbnails_select_page (thumbs, ev_view_get_page (view));
1270
1271         action = gtk_action_group_get_action
1272                 (ev_window->priv->action_group, PAGE_SELECTOR_ACTION);
1273
1274         page = ev_view_get_page (EV_VIEW (ev_window->priv->view));
1275         ev_page_action_set_current_page (EV_PAGE_ACTION (action), page);
1276 }
1277
1278 static void
1279 view_page_changed_cb (EvView   *view,
1280                       EvWindow *ev_window)
1281 {
1282         update_current_page (ev_window, view);
1283         update_action_sensitivity (ev_window);
1284 }
1285
1286 static void
1287 view_status_changed_cb (EvView     *view,
1288                         GParamSpec *pspec,
1289                         EvWindow   *ev_window)
1290 {
1291         const char *message;
1292
1293         gtk_statusbar_pop (GTK_STATUSBAR (ev_window->priv->statusbar),
1294                            ev_window->priv->view_message_cid);
1295
1296         message = ev_view_get_status (view);
1297         if (message) {
1298                 gtk_statusbar_push (GTK_STATUSBAR (ev_window->priv->statusbar),
1299                                     ev_window->priv->view_message_cid, message);
1300         }
1301 }
1302
1303 static void
1304 view_find_status_changed_cb (EvView     *view,
1305                              GParamSpec *pspec,
1306                              EvWindow   *ev_window)
1307 {
1308         const char *text;
1309
1310         text = ev_view_get_find_status (view);
1311         egg_find_bar_set_status_text (EGG_FIND_BAR (ev_window->priv->find_bar),
1312                                       text);
1313 }
1314
1315 static void
1316 find_bar_previous_cb (EggFindBar *find_bar,
1317                       EvWindow   *ev_window)
1318 {
1319         ev_view_find_previous (EV_VIEW (ev_window->priv->view));
1320 }
1321
1322 static void
1323 find_bar_next_cb (EggFindBar *find_bar,
1324                   EvWindow   *ev_window)
1325 {
1326         ev_view_find_next (EV_VIEW (ev_window->priv->view));
1327 }
1328
1329 static void
1330 find_bar_close_cb (EggFindBar *find_bar,
1331                    EvWindow   *ev_window)
1332 {
1333         gtk_widget_hide (ev_window->priv->find_bar);
1334
1335         if (ev_window->priv->exit_fullscreen_popup)
1336                 ev_window_update_fullscreen_popup (ev_window);
1337 }
1338
1339 static void
1340 ev_window_page_mode_cb (GtkRadioAction *action,
1341                         GtkRadioAction *activated_action,
1342                         EvWindow       *window)
1343 {
1344         int mode;
1345
1346         mode = gtk_radio_action_get_current_value (action);
1347
1348         g_assert (mode == PAGE_MODE_CONTINUOUS_PAGE ||
1349                   mode == PAGE_MODE_SINGLE_PAGE);
1350
1351         ev_window_set_page_mode (window, (EvWindowPageMode) mode);
1352 }
1353
1354 static void
1355 find_bar_search_changed_cb (EggFindBar *find_bar,
1356                             GParamSpec *param,
1357                             EvWindow   *ev_window)
1358 {
1359         gboolean case_sensitive;
1360         gboolean visible;
1361         const char *search_string;
1362
1363         g_return_if_fail (EV_IS_WINDOW (ev_window));
1364
1365         /* Either the string or case sensitivity could have changed,
1366          * we connect this callback to both. We also connect it
1367          * to ::visible so when the find bar is hidden, we should
1368          * pretend the search string is NULL/""
1369          */
1370
1371         case_sensitive = egg_find_bar_get_case_sensitive (find_bar);
1372         visible = GTK_WIDGET_VISIBLE (find_bar);
1373         search_string = egg_find_bar_get_search_string (find_bar);
1374
1375 #if 0
1376         g_printerr ("search for '%s'\n", search_string ? search_string : "(nil)");
1377 #endif
1378
1379         if (ev_window->priv->document &&
1380             EV_IS_DOCUMENT_FIND (ev_window->priv->document)) {
1381                 if (visible && search_string) {
1382                         ev_document_find_begin (EV_DOCUMENT_FIND (ev_window->priv->document), search_string, case_sensitive);
1383                 } else {
1384                         ev_document_find_cancel (EV_DOCUMENT_FIND (ev_window->priv->document));
1385                         egg_find_bar_set_status_text (EGG_FIND_BAR (ev_window->priv->find_bar),
1386                                                       NULL);
1387                         gtk_widget_queue_draw (GTK_WIDGET (ev_window->priv->view));
1388                 }
1389         }
1390 }
1391
1392 static void
1393 ev_window_dispose (GObject *object)
1394 {
1395         EvWindowPrivate *priv;
1396
1397         g_return_if_fail (object != NULL && EV_IS_WINDOW (object));
1398
1399         priv = EV_WINDOW (object)->priv;
1400
1401         if (priv->ui_manager) {
1402                 g_object_unref (priv->ui_manager);
1403                 priv->ui_manager = NULL;
1404         }
1405
1406         if (priv->action_group) {
1407                 g_object_unref (priv->action_group);
1408                 priv->action_group = NULL;
1409         }
1410
1411         if (priv->document) {
1412                 g_object_unref (priv->document);
1413                 priv->document = NULL;
1414         }
1415         
1416         if (priv->password_document) {
1417                 g_object_unref (priv->password_document);
1418                 priv->password_document = NULL;
1419         }
1420         
1421         if (priv->password_uri) {
1422                 g_free (priv->password_uri);
1423                 priv->password_uri = NULL;
1424         }
1425         
1426         G_OBJECT_CLASS (ev_window_parent_class)->dispose (object);
1427 }
1428
1429 static void
1430 ev_window_class_init (EvWindowClass *ev_window_class)
1431 {
1432         GObjectClass *g_object_class;
1433
1434         g_object_class = G_OBJECT_CLASS (ev_window_class);
1435         g_object_class->dispose = ev_window_dispose;
1436
1437         g_type_class_add_private (g_object_class, sizeof (EvWindowPrivate));
1438
1439 }
1440
1441 /* Normal items */
1442 static GtkActionEntry entries[] = {
1443         { "File", NULL, N_("_File") },
1444         { "Edit", NULL, N_("_Edit") },
1445         { "View", NULL, N_("_View") },
1446         { "Go", NULL, N_("_Go") },
1447         { "Help", NULL, N_("_Help") },
1448
1449         /* File menu */
1450         { "FileOpen", GTK_STOCK_OPEN, N_("_Open"), "<control>O",
1451           N_("Open a file"),
1452           G_CALLBACK (ev_window_cmd_file_open) },
1453         { "FileSaveAs", GTK_STOCK_SAVE_AS, N_("_Save a Copy..."), NULL,
1454           N_("Save the current document with a new filename"),
1455           G_CALLBACK (ev_window_cmd_save_as) },
1456         { "FilePrint", GTK_STOCK_PRINT, N_("_Print"), "<control>P",
1457           N_("Print this document"),
1458           G_CALLBACK (ev_window_cmd_file_print) },
1459         { "FileCloseWindow", GTK_STOCK_CLOSE, N_("_Close"), "<control>W",
1460           N_("Close this window"),
1461           G_CALLBACK (ev_window_cmd_file_close_window) },
1462
1463         /* Edit menu */
1464         { "EditCopy", GTK_STOCK_COPY, N_("_Copy"), "<control>C",
1465           N_("Copy text from the document"),
1466           G_CALLBACK (ev_window_cmd_edit_copy) },
1467         { "EditSelectAll", NULL, N_("Select _All"), "<control>A",
1468           N_("Select the entire page"),
1469           G_CALLBACK (ev_window_cmd_edit_select_all) },
1470         { "EditFind", GTK_STOCK_FIND, N_("_Find"), "<control>F",
1471           N_("Find a word or phrase in the document"),
1472           G_CALLBACK (ev_window_cmd_edit_find) },
1473
1474         /* View menu */
1475         { "ViewZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus",
1476           N_("Enlarge the document"),
1477           G_CALLBACK (ev_window_cmd_view_zoom_in) },
1478         { "ViewZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus",
1479           N_("Shrink the document"),
1480           G_CALLBACK (ev_window_cmd_view_zoom_out) },
1481         { "ViewNormalSize", GTK_STOCK_ZOOM_100, N_("_Normal Size"), "<control>0",
1482           N_("Zoom to the normal size"),
1483           G_CALLBACK (ev_window_cmd_view_normal_size) },
1484         { "ViewBestFit", GTK_STOCK_ZOOM_FIT, N_("_Best Fit"), NULL,
1485           N_("Zoom to fit the document to the current window"),
1486           G_CALLBACK (ev_window_cmd_view_best_fit) },
1487         { "ViewPageWidth", EV_STOCK_ZOOM_FIT_WIDTH, N_("Fit Page _Width"), NULL,
1488           N_("Zoom to fit the width of the current window "),
1489           G_CALLBACK (ev_window_cmd_view_page_width) },
1490
1491         /* Go menu */
1492         { "GoPageUp", GTK_STOCK_GO_UP, N_("_Page Up"), "<control>Page_Up",
1493           N_("Go to the previous page"),
1494           G_CALLBACK (ev_window_cmd_go_page_up) },
1495         { "GoPageDown", GTK_STOCK_GO_DOWN, N_("_Page Down"), "<control>Page_Down",
1496           N_("Go to the next page"),
1497           G_CALLBACK (ev_window_cmd_go_page_down) },
1498         { "GoFirstPage", GTK_STOCK_GOTO_FIRST, N_("_First Page"), "<control>Home",
1499           N_("Go to the first page"),
1500           G_CALLBACK (ev_window_cmd_go_first_page) },
1501         { "GoLastPage", GTK_STOCK_GOTO_LAST, N_("_Last Page"), "<control>End",
1502           N_("Go to the last page"),
1503           G_CALLBACK (ev_window_cmd_go_last_page) },
1504
1505         /* Help menu */
1506         { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), NULL,
1507           N_("Display help for the viewer application"),
1508           G_CALLBACK (ev_window_cmd_help_contents) },
1509
1510         { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL,
1511           N_("Display credits for the document viewer creators"),
1512           G_CALLBACK (ev_window_cmd_help_about) },
1513 };
1514
1515 /* Toggle items */
1516 static GtkToggleActionEntry toggle_entries[] = {
1517         /* View Menu */
1518         { "ViewToolbar", NULL, N_("_Toolbar"), "<shift><control>T",
1519           N_("Show or hide toolbar"),
1520           G_CALLBACK (ev_window_view_toolbar_cb), TRUE },
1521         { "ViewStatusbar", NULL, N_("_Statusbar"), NULL,
1522           N_("Show or hide statusbar"),
1523           G_CALLBACK (ev_window_view_statusbar_cb), TRUE },
1524         { "ViewSidebar", NULL, N_("Side_bar"), "F9",
1525           N_("Show or hide sidebar"),
1526           G_CALLBACK (ev_window_view_sidebar_cb), TRUE },
1527         { "ViewFullscreen", NULL, N_("_Fullscreen"), "F11",
1528           N_("Expand the window to fill the screen"),
1529           G_CALLBACK (ev_window_cmd_view_fullscreen) },
1530 };
1531
1532 static GtkRadioActionEntry page_view_entries[] = {
1533         { "SinglePage", GTK_STOCK_DND, N_("Single"), NULL,
1534           N_("Show the document one page at a time"),
1535           PAGE_MODE_SINGLE_PAGE },
1536         { "ContinuousPage", GTK_STOCK_DND_MULTIPLE, N_("Multi"), NULL,
1537           N_("Show the full document at once"),
1538           PAGE_MODE_CONTINUOUS_PAGE }
1539 };
1540
1541 static void
1542 goto_page_cb (GtkAction *action, int page_number, EvWindow *ev_window)
1543 {
1544         EvView *view = EV_VIEW (ev_window->priv->view);
1545
1546         if (ev_view_get_page (view) != page_number) {
1547                 ev_view_set_page (view, page_number);
1548         }
1549 }
1550
1551 static void
1552 register_custom_actions (EvWindow *window, GtkActionGroup *group)
1553 {
1554         GtkAction *action;
1555
1556         action = g_object_new (EV_TYPE_PAGE_ACTION,
1557                                "name", PAGE_SELECTOR_ACTION,
1558                                "label", _("Page"),
1559                                "tooltip", _("Select Page"),
1560                                NULL);
1561         g_signal_connect (action, "goto_page",
1562                           G_CALLBACK (goto_page_cb), window);
1563         gtk_action_group_add_action (group, action);
1564         g_object_unref (action);
1565 }
1566
1567 static void
1568 set_short_labels (GtkActionGroup *action_group)
1569 {
1570         GtkAction *action;
1571
1572         action = gtk_action_group_get_action (action_group, "GoPageUp");
1573         g_object_set (action, "short_label", _("Up"), NULL);
1574         action = gtk_action_group_get_action (action_group, "GoPageDown");
1575         g_object_set (action, "short_label", _("Down"), NULL);
1576         action = gtk_action_group_get_action (action_group, "ViewPageWidth");
1577         g_object_set (action, "short_label", _("Fit Width"), NULL);
1578 }
1579
1580 static void
1581 ev_window_init (EvWindow *ev_window)
1582 {
1583         GtkActionGroup *action_group;
1584         GtkAccelGroup *accel_group;
1585         GError *error = NULL;
1586         GtkWidget *menubar;
1587         GtkWidget *toolbar;
1588         GtkWidget *sidebar_widget;
1589
1590         ev_window->priv = EV_WINDOW_GET_PRIVATE (ev_window);
1591
1592         ev_window->priv->page_mode = PAGE_MODE_SINGLE_PAGE;
1593         update_window_title (NULL, NULL, ev_window);
1594
1595         ev_window->priv->main_box = gtk_vbox_new (FALSE, 0);
1596         gtk_container_add (GTK_CONTAINER (ev_window), ev_window->priv->main_box);
1597         gtk_widget_show (ev_window->priv->main_box);
1598
1599         action_group = gtk_action_group_new ("MenuActions");
1600         ev_window->priv->action_group = action_group;
1601         gtk_action_group_set_translation_domain (action_group, NULL);
1602         gtk_action_group_add_actions (action_group, entries,
1603                                       G_N_ELEMENTS (entries), ev_window);
1604         gtk_action_group_add_toggle_actions (action_group, toggle_entries,
1605                                              G_N_ELEMENTS (toggle_entries),
1606                                              ev_window);
1607         gtk_action_group_add_radio_actions (action_group, page_view_entries,
1608                                             G_N_ELEMENTS (page_view_entries),
1609                                             ev_window->priv->page_mode,
1610                                             G_CALLBACK (ev_window_page_mode_cb),
1611                                             ev_window);
1612         set_short_labels (action_group);
1613         register_custom_actions (ev_window, action_group);
1614
1615         ev_window->priv->ui_manager = gtk_ui_manager_new ();
1616         gtk_ui_manager_insert_action_group (ev_window->priv->ui_manager,
1617                                             action_group, 0);
1618
1619         accel_group =
1620                 gtk_ui_manager_get_accel_group (ev_window->priv->ui_manager);
1621         gtk_window_add_accel_group (GTK_WINDOW (ev_window), accel_group);
1622
1623         g_signal_connect (ev_window->priv->ui_manager, "connect_proxy",
1624                           G_CALLBACK (connect_proxy_cb), ev_window);
1625         g_signal_connect (ev_window->priv->ui_manager, "disconnect_proxy",
1626                           G_CALLBACK (disconnect_proxy_cb), ev_window);
1627
1628         if (!gtk_ui_manager_add_ui_from_file (ev_window->priv->ui_manager,
1629                                               DATADIR"/evince-ui.xml",
1630                                               &error)) {
1631                 g_message ("building menus failed: %s", error->message);
1632                 g_error_free (error);
1633         }
1634
1635         menubar = gtk_ui_manager_get_widget (ev_window->priv->ui_manager,
1636                                              "/MainMenu");
1637         gtk_box_pack_start (GTK_BOX (ev_window->priv->main_box), menubar,
1638                             FALSE, FALSE, 0);
1639
1640         toolbar = gtk_ui_manager_get_widget (ev_window->priv->ui_manager,
1641                                              "/ToolBar");
1642         gtk_box_pack_start (GTK_BOX (ev_window->priv->main_box), toolbar,
1643                             FALSE, FALSE, 0);
1644
1645         /* Add the main area */
1646         ev_window->priv->hpaned = gtk_hpaned_new ();
1647         gtk_widget_show (ev_window->priv->hpaned);
1648         gtk_box_pack_start (GTK_BOX (ev_window->priv->main_box), ev_window->priv->hpaned,
1649                             TRUE, TRUE, 0);
1650
1651         ev_window->priv->sidebar = ev_sidebar_new ();
1652         gtk_widget_show (ev_window->priv->sidebar);
1653         gtk_paned_add1 (GTK_PANED (ev_window->priv->hpaned),
1654                         ev_window->priv->sidebar);
1655
1656         /* Stub sidebar, for now */
1657         sidebar_widget = ev_sidebar_links_new ();
1658         gtk_widget_show (sidebar_widget);
1659         ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
1660                              "index",
1661                              _("Index"),
1662                              sidebar_widget);
1663
1664         ev_window->priv->thumbs_sidebar = ev_sidebar_thumbnails_new ();
1665         gtk_widget_show (ev_window->priv->thumbs_sidebar);
1666         ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
1667                              "thumbnails",
1668                              _("Thumbnails"),
1669                              ev_window->priv->thumbs_sidebar);
1670
1671         ev_window->priv->scrolled_window =
1672                 GTK_WIDGET (g_object_new (GTK_TYPE_SCROLLED_WINDOW,
1673                                           "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
1674                                           "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
1675                                           "shadow-type", GTK_SHADOW_IN,
1676                                           NULL));
1677         gtk_widget_show (ev_window->priv->scrolled_window);
1678
1679         gtk_paned_add2 (GTK_PANED (ev_window->priv->hpaned),
1680                         ev_window->priv->scrolled_window);
1681
1682         ev_window->priv->view = ev_view_new ();
1683         ev_window->priv->page_view = ev_page_view_new ();
1684         ev_window->priv->password_view = ev_password_view_new ();
1685         g_signal_connect_swapped (ev_window->priv->password_view,
1686                                   "unlock",
1687                                   G_CALLBACK (ev_window_popup_password_dialog),
1688                                   ev_window);
1689         gtk_widget_show (ev_window->priv->view);
1690         gtk_widget_show (ev_window->priv->page_view);
1691         gtk_widget_show (ev_window->priv->password_view);
1692
1693         /* We own a ref on these widgets, as we can swap them in and out */
1694         g_object_ref (ev_window->priv->view);
1695         g_object_ref (ev_window->priv->page_view);
1696         g_object_ref (ev_window->priv->password_view);
1697
1698         gtk_container_add (GTK_CONTAINER (ev_window->priv->scrolled_window),
1699                            ev_window->priv->view);
1700         g_signal_connect (ev_window->priv->view,
1701                           "page-changed",
1702                           G_CALLBACK (view_page_changed_cb),
1703                           ev_window);
1704         g_signal_connect (ev_window->priv->view,
1705                           "notify::find-status",
1706                           G_CALLBACK (view_find_status_changed_cb),
1707                           ev_window);
1708         g_signal_connect (ev_window->priv->view,
1709                           "notify::status",
1710                           G_CALLBACK (view_status_changed_cb),
1711                           ev_window);
1712
1713         ev_window->priv->statusbar = gtk_statusbar_new ();
1714         gtk_widget_show (ev_window->priv->statusbar);
1715         gtk_box_pack_end (GTK_BOX (ev_window->priv->main_box),
1716                           ev_window->priv->statusbar,
1717                           FALSE, TRUE, 0);
1718         ev_window->priv->help_message_cid = gtk_statusbar_get_context_id
1719                 (GTK_STATUSBAR (ev_window->priv->statusbar), "help_message");
1720         ev_window->priv->view_message_cid = gtk_statusbar_get_context_id
1721                 (GTK_STATUSBAR (ev_window->priv->statusbar), "view_message");
1722
1723         ev_window->priv->find_bar = egg_find_bar_new ();
1724         gtk_box_pack_end (GTK_BOX (ev_window->priv->main_box),
1725                           ev_window->priv->find_bar,
1726                           FALSE, TRUE, 0);
1727
1728         /* Connect to find bar signals */
1729         g_signal_connect (ev_window->priv->find_bar,
1730                           "previous",
1731                           G_CALLBACK (find_bar_previous_cb),
1732                           ev_window);
1733         g_signal_connect (ev_window->priv->find_bar,
1734                           "next",
1735                           G_CALLBACK (find_bar_next_cb),
1736                           ev_window);
1737         g_signal_connect (ev_window->priv->find_bar,
1738                           "close",
1739                           G_CALLBACK (find_bar_close_cb),
1740                           ev_window);
1741         g_signal_connect (ev_window->priv->find_bar,
1742                           "notify::search-string",
1743                           G_CALLBACK (find_bar_search_changed_cb),
1744                           ev_window);
1745         g_signal_connect (ev_window->priv->find_bar,
1746                           "notify::case-sensitive",
1747                           G_CALLBACK (find_bar_search_changed_cb),
1748                           ev_window);
1749         g_signal_connect (ev_window->priv->find_bar,
1750                           "notify::visible",
1751                           G_CALLBACK (find_bar_search_changed_cb),
1752                           ev_window);
1753
1754         g_signal_connect (ev_window, "window-state-event",
1755                           G_CALLBACK (ev_window_state_event_cb),
1756                           ev_window);
1757         g_signal_connect (ev_window, "focus_out_event",
1758                           G_CALLBACK (ev_window_focus_out_cb),
1759                           ev_window);
1760
1761         /* Give focus to the scrolled window */
1762         gtk_widget_grab_focus (ev_window->priv->scrolled_window);
1763
1764         update_action_sensitivity (ev_window);
1765 }