]> www.fi.muni.cz Git - evince.git/blob - shell/ev-window.c
e0445c67fe1ed0a06736de06d1597a7a2d8ebd26
[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-sidebar.h"
33 #include "ev-sidebar-bookmarks.h"
34 #include "ev-sidebar-thumbnails.h"
35 #include "ev-view.h"
36 #include "ev-print-job.h"
37 #include "eggfindbar.h"
38
39 #include "pdf-document.h"
40 #include "pixbuf-document.h"
41 #include "gtkgs.h"
42
43 #include <glib/gi18n.h>
44 #include <gtk/gtk.h>
45 #include <libgnomevfs/gnome-vfs-mime-utils.h>
46 #include <libgnomeprintui/gnome-print-dialog.h>
47
48 #include <string.h>
49
50 #include "ev-application.h"
51 #include "ev-stock-icons.h"
52
53 enum {
54         PROP_0,
55         PROP_ATTRIBUTE
56 };
57
58 enum {
59         SIGNAL,
60         N_SIGNALS
61 };
62
63 struct _EvWindowPrivate {
64         GtkWidget *main_box;
65         GtkWidget *hpaned;
66         GtkWidget *sidebar;
67         GtkWidget *find_bar;
68         GtkWidget *view;
69         GtkActionGroup *action_group;
70         GtkUIManager *ui_manager;
71         GtkWidget *statusbar;
72         guint help_message_cid;
73         GtkWidget *exit_fullscreen_popup;
74
75         EvDocument *document;
76
77         gboolean fullscreen_mode;
78 };
79
80 #if 0
81 /* enable these to add support for signals */
82 static guint ev_window_signals [N_SIGNALS] = { 0 };
83 #endif
84
85 static void update_fullscreen_popup (EvWindow *window);
86
87 static GObjectClass *parent_class = NULL;
88
89 G_DEFINE_TYPE (EvWindow, ev_window, GTK_TYPE_WINDOW)
90
91 #define EV_WINDOW_GET_PRIVATE(object) \
92         (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_WINDOW, EvWindowPrivate))
93
94 #if 0
95 const char *
96 ev_window_get_attribute (EvWindow *self)
97 {
98         g_return_val_if_fail (self != NULL && EV_IS_WINDOW (self), NULL);
99         
100         return self->priv->attribute;
101 }
102
103 void
104 ev_window_set_attribute (EvWindow* self, const char *attribute)
105 {
106         g_assert (self != NULL && EV_IS_WINDOW (self));
107         g_assert (attribute != NULL);
108
109         if (self->priv->attribute != NULL) {
110                 g_free (self->priv->attribute);
111         }
112
113         self->priv->attribute = g_strdup (attribute);
114
115         g_object_notify (G_OBJECT (self), "attribute");
116 }
117
118 static void
119 ev_window_get_property (GObject *object, guint prop_id, GValue *value,
120                         GParamSpec *param_spec)
121 {
122         EvWindow *self;
123
124         self = EV_WINDOW (object);
125
126         switch (prop_id) {
127         case PROP_ATTRIBUTE:
128                 g_value_set_string (value, self->priv->attribute);
129                 break;
130         default:
131                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
132                                                    prop_id,
133                                                    param_spec);
134                 break;
135         }
136 }
137
138 static void
139 ev_window_set_property (GObject *object, guint prop_id, const GValue *value,
140                         GParamSpec *param_spec)
141 {
142         EvWindow *self;
143         
144         self = EV_WINDOW (object);
145         
146         switch (prop_id) {
147         case PROP_ATTRIBUTE:
148                 ev_window_set_attribute (self, g_value_get_string (value));
149                 break;
150         default:
151                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
152                                                    prop_id,
153                                                    param_spec);
154                 break;
155         }
156 }
157 #endif
158
159 static void
160 set_action_sensitive (EvWindow   *ev_window,
161                       const char *name,
162                       gboolean    sensitive)
163 {
164         GtkAction *action = gtk_action_group_get_action (ev_window->priv->action_group,
165                                                          name);
166         gtk_action_set_sensitive (action, sensitive);
167 }
168
169 static void
170 update_action_sensitivity (EvWindow *ev_window)
171 {
172         int n_pages;
173         int page;
174
175         if (ev_window->priv->document)
176                 n_pages = ev_document_get_n_pages (ev_window->priv->document);
177         else
178                 n_pages = 1;
179
180         page = ev_view_get_page (EV_VIEW (ev_window->priv->view));
181
182         set_action_sensitive (ev_window, "GoFirstPage", page > 1);
183         set_action_sensitive (ev_window, "GoPreviousPage", page > 1);
184         set_action_sensitive (ev_window, "GoNextPage", page < n_pages);
185         set_action_sensitive (ev_window, "GoLastPage", page < n_pages);
186 }
187
188 gboolean
189 ev_window_is_empty (const EvWindow *ev_window)
190 {
191         g_return_val_if_fail (EV_IS_WINDOW (ev_window), FALSE);
192         
193         return ev_window->priv->document == NULL;
194 }
195
196 static void
197 unable_to_load (EvWindow   *ev_window,
198                 const char *error_message)
199 {
200         GtkWidget *dialog;
201
202         dialog = gtk_message_dialog_new (GTK_WINDOW (ev_window),
203                                          GTK_DIALOG_DESTROY_WITH_PARENT,
204                                          GTK_MESSAGE_ERROR,
205                                          GTK_BUTTONS_CLOSE,
206                                          _("Unable to open document"));
207         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
208                                                   "%s", error_message);
209         gtk_dialog_run (GTK_DIALOG (dialog));
210         gtk_widget_destroy (dialog);
211 }
212
213 /* Would be nice to have this in gdk-pixbuf */
214 static gboolean
215 mime_type_supported_by_gdk_pixbuf (const gchar *mime_type)
216 {
217         GSList *formats, *list;
218         gboolean retval = FALSE;
219         
220         formats = gdk_pixbuf_get_formats ();
221         
222         list = formats;
223         while (list) {
224                 GdkPixbufFormat *format = list->data;
225                 int i;
226                 gchar **mime_types;
227                 
228                 if (gdk_pixbuf_format_is_disabled (format))
229                         continue;
230
231                 mime_types = gdk_pixbuf_format_get_mime_types (format);
232                 
233                 for (i = 0; mime_types[i] != NULL; i++) {
234                         if (strcmp (mime_types[i], mime_type) == 0) {
235                                 retval = TRUE;
236                                 break;
237                         }
238                 }
239                 
240                 if (retval)
241                         break;
242                 
243                 list = list->next;
244         }
245         
246         g_slist_free (formats);
247
248         return retval;
249 }
250
251 void
252 ev_window_open (EvWindow *ev_window, const char *uri)
253 {
254         EvDocument *document = NULL;
255         char *mime_type;
256
257         mime_type = gnome_vfs_get_mime_type (uri);
258
259         if (!strcmp (mime_type, "application/pdf"))
260                 document = g_object_new (PDF_TYPE_DOCUMENT, NULL);
261         else if (!strcmp (mime_type, "application/postscript"))
262                 document = g_object_new (GTK_GS_TYPE, NULL);
263         else if (mime_type_supported_by_gdk_pixbuf (mime_type))
264                 document = g_object_new (PIXBUF_TYPE_DOCUMENT, NULL);
265         
266         if (document) {
267                 GError *error = NULL;
268
269                 if (ev_document_load (document, uri, &error)) {
270                         if (ev_window->priv->document)
271                                 g_object_unref (ev_window->priv->document);
272                         ev_window->priv->document = document;
273
274                         ev_view_set_document (EV_VIEW (ev_window->priv->view),
275                                               document);
276                         ev_sidebar_set_document (EV_SIDEBAR (ev_window->priv->sidebar),
277                                                  document);
278
279                         update_action_sensitivity (ev_window);
280                 
281                 } else {
282                         g_assert (error != NULL);
283                         g_object_unref (document);
284                         unable_to_load (ev_window, error->message);
285                         g_error_free (error);
286                 }
287         } else {
288                 char *error_message;
289
290                 error_message = g_strdup_printf (_("Unhandled MIME type: '%s'"),
291                                                  mime_type);
292                 unable_to_load (ev_window, error_message);
293                 g_free (error_message);
294         }
295
296         g_free (mime_type);
297 }
298
299 static void
300 ev_window_cmd_file_open (GtkAction *action, EvWindow *ev_window)
301 {
302         ev_application_open (EV_APP, NULL);
303 }
304
305 static gboolean
306 using_postscript_printer (GnomePrintConfig *config)
307 {
308         const guchar *driver;
309         const guchar *transport;
310
311         driver = gnome_print_config_get (
312                 config, (const guchar *)"Settings.Engine.Backend.Driver");
313         
314         transport = gnome_print_config_get (
315                 config, (const guchar *)"Settings.Transport.Backend");
316         
317         if (driver) {
318                 if (!strcmp ((const gchar *)driver, "gnome-print-ps"))
319                         return TRUE;
320                 else 
321                         return FALSE;
322         } else  if (transport) {
323                 if (!strcmp ((const gchar *)transport, "CUPS"))
324                         return TRUE;
325         }
326         
327         return FALSE;
328 }
329
330 static void
331 ev_window_print (EvWindow *ev_window)
332 {
333         GnomePrintConfig *config;
334         GnomePrintJob *job;
335         GtkWidget *print_dialog;
336         EvPrintJob *print_job = NULL;
337
338         g_return_if_fail (EV_IS_WINDOW (ev_window));
339         g_return_if_fail (ev_window->priv->document != NULL);
340
341         config = gnome_print_config_default ();
342         job = gnome_print_job_new (config);
343
344         print_dialog = gnome_print_dialog_new (job, _("Print"),
345                                                (GNOME_PRINT_DIALOG_RANGE |
346                                                 GNOME_PRINT_DIALOG_COPIES));
347         gtk_dialog_set_response_sensitive (GTK_DIALOG (print_dialog),
348                                            GNOME_PRINT_DIALOG_RESPONSE_PREVIEW,
349                                            FALSE);
350         
351         while (TRUE) {
352                 int response;
353                 response = gtk_dialog_run (GTK_DIALOG (print_dialog));
354                 
355                 if (response != GNOME_PRINT_DIALOG_RESPONSE_PRINT)
356                         break;
357
358                 /* FIXME: Change this when we have the first backend
359                  * that can print more than postscript
360                  */
361                 if (!using_postscript_printer (config)) {
362                         GtkWidget *dialog;
363                         
364                         dialog = gtk_message_dialog_new (
365                                 GTK_WINDOW (print_dialog), GTK_DIALOG_MODAL,
366                                 GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
367                                 _("Printing is not supported on this printer."));
368                         gtk_message_dialog_format_secondary_text (
369                                 GTK_MESSAGE_DIALOG (dialog),
370                                 _("You were trying to print to a printer using the \"%s\" driver. This program requires a PostScript printer driver."),
371                                 gnome_print_config_get (
372                                         config, "Settings.Engine.Backend.Driver"));
373                         gtk_dialog_run (GTK_DIALOG (dialog));
374                         gtk_widget_destroy (dialog);
375
376                         continue;
377                 }
378
379                 print_job = g_object_new (EV_TYPE_PRINT_JOB,
380                                           "gnome_print_job", job,
381                                           "document", ev_window->priv->document,
382                                           "print_dialog", print_dialog,
383                                           NULL);
384                 break;
385         }
386                                 
387         gtk_widget_destroy (print_dialog);
388
389         if (print_job != NULL)
390                 ev_print_job_print (print_job, GTK_WINDOW (ev_window));
391 }
392
393 static void
394 ev_window_cmd_file_print (GtkAction *action, EvWindow *ev_window)
395 {
396         ev_window_print (ev_window);
397 }
398
399 static void
400 ev_window_cmd_file_close_window (GtkAction *action, EvWindow *ev_window)
401 {
402         g_return_if_fail (EV_IS_WINDOW (ev_window));
403
404         gtk_widget_destroy (GTK_WIDGET (ev_window));
405 }
406
407 static void
408 ev_window_cmd_edit_find (GtkAction *action, EvWindow *ev_window)
409 {
410         g_return_if_fail (EV_IS_WINDOW (ev_window));
411
412         gtk_widget_show (ev_window->priv->find_bar);
413
414         if (ev_window->priv->exit_fullscreen_popup) 
415                 update_fullscreen_popup (ev_window);
416         
417         egg_find_bar_grab_focus (EGG_FIND_BAR (ev_window->priv->find_bar));
418 }
419
420 static void
421 ev_window_cmd_edit_copy (GtkAction *action, EvWindow *ev_window)
422 {
423         g_return_if_fail (EV_IS_WINDOW (ev_window));
424
425         /* FIXME */
426 }
427
428 static void
429 update_fullscreen_popup (EvWindow *window)
430 {
431         GtkWidget *popup = window->priv->exit_fullscreen_popup;
432         int popup_width, popup_height;
433         GdkRectangle screen_rect;
434
435         g_return_if_fail (popup != NULL);
436
437         if (!popup)
438                 return;
439         
440         popup_width = popup->requisition.width;
441         popup_height = popup->requisition.height;
442
443         /* FIXME multihead */
444         gdk_screen_get_monitor_geometry (gdk_screen_get_default (),
445                         gdk_screen_get_monitor_at_window
446                         (gdk_screen_get_default (),
447                          GTK_WIDGET (window)->window),
448                          &screen_rect);
449
450         if (GTK_WIDGET_VISIBLE (window->priv->find_bar)) {
451                 GtkRequisition req;
452
453                 gtk_widget_size_request (window->priv->find_bar, &req);
454                 
455                 screen_rect.height -= req.height;
456         }
457         
458         if (gtk_widget_get_direction (popup) == GTK_TEXT_DIR_RTL)
459         {
460                 gtk_window_move (GTK_WINDOW (popup),
461                                  screen_rect.x + screen_rect.width - popup_width,
462                                  screen_rect.height - popup_height);
463         }
464         else
465         {
466                 gtk_window_move (GTK_WINDOW (popup),
467                                 screen_rect.x, screen_rect.height - popup_height);
468         }
469 }
470
471 static void
472 screen_size_changed_cb (GdkScreen *screen,
473                         EvWindow *window)
474 {
475         update_fullscreen_popup (window);
476 }
477
478 static void
479 destroy_exit_fullscreen_popup (EvWindow *window)
480 {
481         if (window->priv->exit_fullscreen_popup != NULL)
482         {
483                 /* FIXME multihead */
484                 g_signal_handlers_disconnect_by_func
485                         (gdk_screen_get_default (),
486                          G_CALLBACK (screen_size_changed_cb), window);
487
488                 gtk_widget_destroy (window->priv->exit_fullscreen_popup);
489                 window->priv->exit_fullscreen_popup = NULL;
490         }
491 }
492
493 static void
494 exit_fullscreen_button_clicked_cb (GtkWidget *button, EvWindow *window)
495 {
496         GtkAction *action;
497
498         action = gtk_action_group_get_action (window->priv->action_group, "ViewFullscreen");
499         g_return_if_fail (action != NULL);
500
501         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
502 }
503
504 static void
505 fullscreen_popup_size_request_cb (GtkWidget *popup, GtkRequisition *req, EvWindow *window)
506 {
507         update_fullscreen_popup (window);
508 }
509
510 static void
511 ev_window_fullscreen (EvWindow *window)
512 {
513         GtkWidget *popup, *button, *icon, *label, *hbox, *main_menu;
514
515         window->priv->fullscreen_mode = TRUE;
516
517         popup = gtk_window_new (GTK_WINDOW_POPUP);
518         window->priv->exit_fullscreen_popup = popup;
519
520         button = gtk_button_new ();
521         g_signal_connect (button, "clicked",
522                           G_CALLBACK (exit_fullscreen_button_clicked_cb),
523                           window);
524         gtk_widget_show (button);
525         gtk_container_add (GTK_CONTAINER (popup), button);
526
527         hbox = gtk_hbox_new (FALSE, 2);
528         gtk_widget_show (hbox);
529         gtk_container_add (GTK_CONTAINER (button), hbox);
530
531         icon = gtk_image_new_from_stock (GTK_STOCK_QUIT, GTK_ICON_SIZE_BUTTON);
532         gtk_widget_show (icon);
533         gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
534
535         label = gtk_label_new (_("Exit Fullscreen"));
536         gtk_widget_show (label);
537         gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
538
539         gtk_window_set_resizable (GTK_WINDOW (popup), FALSE);
540
541         /* FIXME multihead */
542         g_signal_connect (gdk_screen_get_default (), "size-changed",
543                           G_CALLBACK (screen_size_changed_cb), window);
544         g_signal_connect (popup, "size_request",
545                           G_CALLBACK (fullscreen_popup_size_request_cb), window);
546
547         main_menu = gtk_ui_manager_get_widget (window->priv->ui_manager, "/MainMenu");
548         gtk_widget_hide (main_menu);
549         gtk_widget_hide (window->priv->statusbar);
550         
551         update_fullscreen_popup (window);
552
553         gtk_widget_show (popup);
554 }
555
556 static void
557 ev_window_unfullscreen (EvWindow *window)
558 {
559         GtkWidget *main_menu;
560         
561         window->priv->fullscreen_mode = FALSE;
562
563         main_menu = gtk_ui_manager_get_widget (window->priv->ui_manager, "/MainMenu");
564         gtk_widget_show (main_menu);
565         gtk_widget_show (window->priv->statusbar);
566         
567         destroy_exit_fullscreen_popup (window);
568 }
569  
570 static void
571 ev_window_cmd_view_fullscreen (GtkAction *action, EvWindow *ev_window)
572 {
573         gboolean fullscreen;
574         
575         g_return_if_fail (EV_IS_WINDOW (ev_window));
576
577         fullscreen = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
578
579         if (fullscreen) {
580                 gtk_window_fullscreen (GTK_WINDOW (ev_window));
581         } else {
582                 gtk_window_unfullscreen (GTK_WINDOW (ev_window));
583         }
584 }
585
586 static gboolean
587 ev_window_state_event_cb (GtkWidget *widget, GdkEventWindowState *event, EvWindow *window)
588 {
589         if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
590         {
591                 GtkActionGroup *action_group;
592                 GtkAction *action;
593                 gboolean fullscreen;
594
595                 fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN;
596
597                 if (fullscreen)
598                 {
599                         ev_window_fullscreen (window);
600                 }
601                 else
602                 {
603                         ev_window_unfullscreen (window);
604                 }
605
606                 action_group = window->priv->action_group;
607
608                 action = gtk_action_group_get_action (action_group, "ViewFullscreen");
609                 g_signal_handlers_block_by_func
610                         (action, G_CALLBACK (ev_window_cmd_view_fullscreen), window);
611                 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), fullscreen);
612                 g_signal_handlers_unblock_by_func
613                         (action, G_CALLBACK (ev_window_cmd_view_fullscreen), window);
614
615         }
616
617         return FALSE;
618 }
619
620 static gboolean
621 ev_window_focus_out_cb (GtkWidget *widget, GdkEventFocus *event, EvWindow *ev_window)
622 {
623         gtk_window_unfullscreen (GTK_WINDOW (ev_window));
624         
625         return FALSE;
626 }
627
628
629 static void
630 ev_window_cmd_view_zoom_in (GtkAction *action, EvWindow *ev_window)
631 {
632         g_return_if_fail (EV_IS_WINDOW (ev_window));
633
634         ev_view_zoom_in (EV_VIEW (ev_window->priv->view));
635 }
636
637 static void
638 ev_window_cmd_view_zoom_out (GtkAction *action, EvWindow *ev_window)
639 {
640         g_return_if_fail (EV_IS_WINDOW (ev_window));
641
642         ev_view_zoom_out (EV_VIEW (ev_window->priv->view));
643 }
644
645 static void
646 ev_window_cmd_view_normal_size (GtkAction *action, EvWindow *ev_window)
647 {
648         g_return_if_fail (EV_IS_WINDOW (ev_window));
649
650         ev_view_normal_size (EV_VIEW (ev_window->priv->view));
651 }
652
653 static void
654 ev_window_cmd_view_best_fit (GtkAction *action, EvWindow *ev_window)
655 {
656         g_return_if_fail (EV_IS_WINDOW (ev_window));
657
658         ev_view_best_fit (EV_VIEW (ev_window->priv->view));
659 }
660
661 static void
662 ev_window_cmd_view_page_width (GtkAction *action, EvWindow *ev_window)
663 {
664         g_return_if_fail (EV_IS_WINDOW (ev_window));
665
666         ev_view_fit_width (EV_VIEW (ev_window->priv->view));
667 }
668
669 static void
670 ev_window_cmd_go_back (GtkAction *action, EvWindow *ev_window)
671 {
672         g_return_if_fail (EV_IS_WINDOW (ev_window));
673
674         /* FIXME */
675 }
676
677 static void
678 ev_window_cmd_go_forward (GtkAction *action, EvWindow *ev_window)
679 {
680         g_return_if_fail (EV_IS_WINDOW (ev_window));
681
682         /* FIXME */
683 }
684
685 static void
686 ev_window_cmd_go_previous_page (GtkAction *action, EvWindow *ev_window)
687 {
688         g_return_if_fail (EV_IS_WINDOW (ev_window));
689
690         ev_view_set_page (EV_VIEW (ev_window->priv->view),
691                           ev_view_get_page (EV_VIEW (ev_window->priv->view)) - 1);
692 }
693
694 static void
695 ev_window_cmd_go_next_page (GtkAction *action, EvWindow *ev_window)
696 {
697         g_return_if_fail (EV_IS_WINDOW (ev_window));
698
699         ev_view_set_page (EV_VIEW (ev_window->priv->view),
700                           ev_view_get_page (EV_VIEW (ev_window->priv->view)) + 1);
701 }
702
703 static void
704 ev_window_cmd_go_first_page (GtkAction *action, EvWindow *ev_window)
705 {
706         g_return_if_fail (EV_IS_WINDOW (ev_window));
707
708         ev_view_set_page (EV_VIEW (ev_window->priv->view), 1);
709 }
710
711 static void
712 ev_window_cmd_go_last_page (GtkAction *action, EvWindow *ev_window)
713 {
714         g_return_if_fail (EV_IS_WINDOW (ev_window));
715
716         ev_view_set_page (EV_VIEW (ev_window->priv->view), G_MAXINT);
717 }
718
719 static void
720 ev_window_cmd_help_contents (GtkAction *action, EvWindow *ev_window)
721 {
722         g_return_if_fail (EV_IS_WINDOW (ev_window));
723
724         /* FIXME */
725 }
726
727 static void
728 ev_window_cmd_help_about (GtkAction *action, EvWindow *ev_window)
729 {
730         const char *authors[] = {
731                 N_("Many..."),
732                 NULL
733         };
734
735         const char *documenters[] = {
736                 N_("Not so many..."),
737                 NULL
738         };
739
740         const char *license[] = {
741                 N_("Evince is free software; you can redistribute it and/or modify\n"
742                    "it under the terms of the GNU General Public License as published by\n"
743                    "the Free Software Foundation; either version 2 of the License, or\n"
744                    "(at your option) any later version.\n"),            
745                 N_("Evince is distributed in the hope that it will be useful,\n"
746                    "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
747                    "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
748                    "GNU General Public License for more details.\n"),
749                 N_("You should have received a copy of the GNU General Public License\n"
750                    "along with Evince; if not, write to the Free Software Foundation, Inc.,\n"
751                    "59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n")
752         };
753
754         char *license_trans;
755
756 #ifdef ENABLE_NLS
757         const char **p;
758
759         for (p = authors; *p; ++p)
760                 *p = _(*p);
761
762         for (p = documenters; *p; ++p)
763                 *p = _(*p);
764 #endif
765
766         license_trans = g_strconcat (_(license[0]), "\n", _(license[1]), "\n",
767                                      _(license[2]), "\n", NULL);
768
769         gtk_show_about_dialog (
770                 GTK_WINDOW (ev_window),
771                 "name", _("Evince"),
772                 "version", VERSION,
773                 "copyright",
774                 _("\xc2\xa9 1996-2004 The Evince authors"),
775                 "license", license_trans,
776                 "website", "http://www.gnome.org/projects/evince",
777                 "comments", _("PostScript and PDF File Viewer."),
778                 "authors", authors,
779                 "documenters", documenters,
780                 "translator-credits", _("translator-credits"),
781                 NULL);
782
783         g_free (license_trans);
784 }
785
786 static void
787 ev_window_view_toolbar_cb (GtkAction *action, EvWindow *ev_window)
788 {
789         g_object_set (
790                 G_OBJECT (gtk_ui_manager_get_widget (
791                                   ev_window->priv->ui_manager,
792                                   "/ToolBar")),
793                 "visible",
794                 gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)),
795                 NULL);
796 }
797
798 static void
799 ev_window_view_statusbar_cb (GtkAction *action, EvWindow *ev_window)
800 {
801         g_object_set (
802                 ev_window->priv->statusbar,
803                 "visible",
804                 gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)),
805                 NULL);
806 }
807
808 static void
809 ev_window_view_sidebar_cb (GtkAction *action, EvWindow *ev_window)
810 {
811         /* FIXME */
812 }
813
814 static void
815 menu_item_select_cb (GtkMenuItem *proxy, EvWindow *ev_window)
816 {
817         GtkAction *action;
818         char *message;
819
820         action = g_object_get_data (G_OBJECT (proxy), "gtk-action");
821         g_return_if_fail (action != NULL);
822         
823         g_object_get (G_OBJECT (action), "tooltip", &message, NULL);
824         if (message) {
825                 gtk_statusbar_push (GTK_STATUSBAR (ev_window->priv->statusbar),
826                                     ev_window->priv->help_message_cid, message);
827                 g_free (message);
828         }
829 }
830
831 static void
832 menu_item_deselect_cb (GtkMenuItem *proxy, EvWindow *ev_window)
833 {
834         gtk_statusbar_pop (GTK_STATUSBAR (ev_window->priv->statusbar),
835                            ev_window->priv->help_message_cid);
836 }
837
838 static void
839 connect_proxy_cb (GtkUIManager *ui_manager, GtkAction *action,
840                   GtkWidget *proxy, EvWindow *ev_window)
841 {
842         if (GTK_IS_MENU_ITEM (proxy)) {
843                 g_signal_connect (proxy, "select",
844                                   G_CALLBACK (menu_item_select_cb), ev_window);
845                 g_signal_connect (proxy, "deselect",
846                                   G_CALLBACK (menu_item_deselect_cb),
847                                   ev_window);
848         }
849 }
850
851 static void
852 disconnect_proxy_cb (GtkUIManager *ui_manager, GtkAction *action,
853                      GtkWidget *proxy, EvWindow *ev_window)
854 {
855         if (GTK_IS_MENU_ITEM (proxy)) {
856                 g_signal_handlers_disconnect_by_func
857                         (proxy, G_CALLBACK (menu_item_select_cb), ev_window);
858                 g_signal_handlers_disconnect_by_func
859                         (proxy, G_CALLBACK (menu_item_deselect_cb), ev_window);
860         }
861 }
862
863 static void
864 view_page_changed_cb (EvView   *view,
865                       EvWindow *ev_window)
866 {
867         update_action_sensitivity (ev_window);
868 }
869
870 static void
871 find_bar_previous_cb (EggFindBar *find_bar,
872                       EvWindow   *ev_window)
873 {
874         /* FIXME - highlight previous result */
875         g_printerr ("Find Previous\n");
876
877 }
878
879 static void
880 find_bar_next_cb (EggFindBar *find_bar,
881                   EvWindow   *ev_window)
882 {
883         /* FIXME - highlight next result */
884         g_printerr ("Find Next\n");
885 }
886
887 static void
888 find_bar_close_cb (EggFindBar *find_bar,
889                    EvWindow   *ev_window)
890 {
891         gtk_widget_hide (ev_window->priv->find_bar);
892
893         if (ev_window->priv->exit_fullscreen_popup)
894                 update_fullscreen_popup (ev_window);
895 }
896
897 static void
898 find_bar_search_changed_cb (EggFindBar *find_bar,
899                             GParamSpec *param,
900                             EvWindow   *ev_window)
901 {
902         gboolean case_sensitive;
903         gboolean visible;
904         const char *search_string;
905
906         g_return_if_fail (EV_IS_WINDOW (ev_window));
907         
908         /* Either the string or case sensitivity could have changed,
909          * we connect this callback to both. We also connect it
910          * to ::visible so when the find bar is hidden, we should
911          * pretend the search string is NULL/""
912          */
913
914         case_sensitive = egg_find_bar_get_case_sensitive (find_bar);
915         visible = GTK_WIDGET_VISIBLE (find_bar);
916         search_string = egg_find_bar_get_search_string (find_bar);
917         
918 #if 0
919         g_printerr ("search for '%s'\n", search_string ? search_string : "(nil)");
920 #endif
921
922         /* We don't require begin/end find calls to be matched up, it's really
923          * start_find and cancel_any_find_that_may_not_be_finished
924          */
925         if (visible && search_string) {
926                 ev_document_begin_find (ev_window->priv->document, search_string, case_sensitive);
927         } else {
928                 ev_document_end_find (ev_window->priv->document);
929         }
930 }
931
932 static void
933 ev_window_dispose (GObject *object)
934 {
935         EvWindowPrivate *priv;
936
937         g_return_if_fail (object != NULL && EV_IS_WINDOW (object));
938
939         priv = EV_WINDOW (object)->priv;
940
941         if (priv->ui_manager) {
942                 g_object_unref (priv->ui_manager);
943                 priv->ui_manager = NULL;
944         }
945
946         if (priv->action_group) {
947                 g_object_unref (priv->action_group);
948                 priv->action_group = NULL;
949         }
950         
951         G_OBJECT_CLASS (parent_class)->dispose (object);
952 }
953
954 static void
955 ev_window_class_init (EvWindowClass *ev_window_class)
956 {
957         GObjectClass *g_object_class;
958
959         parent_class = g_type_class_peek_parent (ev_window_class);
960
961         g_object_class = G_OBJECT_CLASS (ev_window_class);
962         g_object_class->dispose = ev_window_dispose;
963
964         g_type_class_add_private (g_object_class, sizeof (EvWindowPrivate));
965
966 #if 0
967         /* setting up signal system */
968         ev_window_class->signal = ev_window_signal;
969
970         ev_window_signals [SIGNAL] = g_signal_new (
971                 "signal",
972                 EV_TYPE_WINDOW,
973                 G_SIGNAL_RUN_LAST,
974                 G_STRUCT_OFFSET (EvWindowClass,
975                                  signal),
976                 NULL,
977                 NULL,
978                 g_cclosure_marshal_VOID__STRING,
979                 G_TYPE_NONE,
980                 0);
981         /* setting up property system */
982         g_object_class->set_property = ev_window_set_property;
983         g_object_class->get_property = ev_window_get_property;
984
985         g_object_class_install_property (
986                 g_object_class,
987                 PROP_ATTRIBUTE,
988                 g_param_spec_string ("attribute",
989                                      "Attribute",
990                                      "A simple unneccessary attribute that "
991                                      "does nothing special except being a "
992                                      "demonstration for the correct implem"
993                                      "entation of a GObject property",
994                                      "default_value",
995                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
996 #endif
997 }
998
999 /* Normal items */
1000 static GtkActionEntry entries[] = {
1001         { "File", NULL, N_("_File") },
1002         { "Edit", NULL, N_("_Edit") },
1003         { "View", NULL, N_("_View") },
1004         { "Go", NULL, N_("_Go") },
1005         { "Help", NULL, N_("_Help") },
1006
1007         /* File menu */
1008         { "FileOpen", GTK_STOCK_OPEN, N_("_Open"), "<control>O",
1009           N_("Open a file"),
1010           G_CALLBACK (ev_window_cmd_file_open) },
1011         { "FilePrint", GTK_STOCK_PRINT, N_("_Print"), "<control>P",
1012           N_("Print this document"),
1013           G_CALLBACK (ev_window_cmd_file_print) },
1014         { "FileCloseWindow", GTK_STOCK_CLOSE, N_("_Close"), "<control>W",
1015           N_("Close this window"),
1016           G_CALLBACK (ev_window_cmd_file_close_window) },
1017
1018         /* Edit menu */
1019         { "EditCopy", GTK_STOCK_COPY, N_("_Copy"), "<control>C",
1020           N_("Copy text from the document"),
1021           G_CALLBACK (ev_window_cmd_edit_copy) },
1022         
1023         { "EditFind", GTK_STOCK_FIND, N_("_Find"), "<control>F",
1024           N_("Find a word or phrase in the document"),
1025           G_CALLBACK (ev_window_cmd_edit_find) },
1026
1027         /* View menu */
1028         { "ViewZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus",
1029           N_("Enlarge the document"),
1030           G_CALLBACK (ev_window_cmd_view_zoom_in) },
1031         { "ViewZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus",
1032           N_("Shrink the document"),
1033           G_CALLBACK (ev_window_cmd_view_zoom_out) },
1034         { "ViewNormalSize", GTK_STOCK_ZOOM_100, N_("_Normal Size"), "<control>0",
1035           N_("Zoom to the normal size"),
1036           G_CALLBACK (ev_window_cmd_view_normal_size) },
1037         { "ViewBestFit", GTK_STOCK_ZOOM_FIT, N_("_Best Fit"), NULL,
1038           N_("Zoom to fit the document to the current window"),
1039           G_CALLBACK (ev_window_cmd_view_best_fit) },
1040         { "ViewPageWidth", EV_STOCK_ZOOM_FIT_WIDTH, N_("Fit Page _Width"), NULL,
1041           N_("Zoom to fit the width of the current window "),
1042           G_CALLBACK (ev_window_cmd_view_page_width) },
1043
1044         /* Go menu */
1045         { "GoBack", GTK_STOCK_GO_BACK, N_("_Back"), "<mod1>Left",
1046           N_("Go to the page viewed before this one"),
1047           G_CALLBACK (ev_window_cmd_go_back) },
1048         { "GoForward", GTK_STOCK_GO_FORWARD, N_("Fo_rward"), "<mod1>Right",
1049           N_("Go to the page viewed before this one"),
1050           G_CALLBACK (ev_window_cmd_go_forward) },
1051         { "GoPreviousPage", GTK_STOCK_GO_BACK, N_("_Previous Page"), "<control>Page_Up",
1052           N_("Go to the previous page"),
1053           G_CALLBACK (ev_window_cmd_go_previous_page) },
1054         { "GoNextPage", GTK_STOCK_GO_FORWARD, N_("_Next Page"), "<control>Page_Down",
1055           N_("Go to the next page"),
1056           G_CALLBACK (ev_window_cmd_go_next_page) },
1057         { "GoFirstPage", GTK_STOCK_GOTO_FIRST, N_("_First Page"), "<control>Home",
1058           N_("Go to the first page"),
1059           G_CALLBACK (ev_window_cmd_go_first_page) },        
1060         { "GoLastPage", GTK_STOCK_GOTO_LAST, N_("_Last Page"), "<control>End",
1061           N_("Go to the last page"),
1062           G_CALLBACK (ev_window_cmd_go_last_page) },
1063         
1064         /* Help menu */
1065         { "HelpContents", GTK_STOCK_HELP, N_("_Contents"), NULL,
1066           N_("Display help for the viewer application"),
1067           G_CALLBACK (ev_window_cmd_help_contents) },
1068         
1069         { "HelpAbout", GTK_STOCK_ABOUT, N_("_About"), NULL,
1070           N_("Display credits for the document viewer creators"),
1071           G_CALLBACK (ev_window_cmd_help_about) },
1072 };
1073
1074 /* Toggle items */
1075 static GtkToggleActionEntry toggle_entries[] = {
1076         /* View Menu */
1077         { "ViewToolbar", NULL, N_("_Toolbar"), "<shift><control>T",
1078           N_("Show or hide toolbar"),
1079           G_CALLBACK (ev_window_view_toolbar_cb), TRUE },
1080         { "ViewStatusbar", NULL, N_("_Statusbar"), NULL,
1081           N_("Show or hide statusbar"),
1082           G_CALLBACK (ev_window_view_statusbar_cb), TRUE },
1083         { "ViewSidebar", NULL, N_("Side_bar"), "F9",
1084           N_("Show or hide sidebar"),
1085           G_CALLBACK (ev_window_view_sidebar_cb), FALSE },
1086         { "ViewFullscreen", NULL, N_("_Fullscreen"), "F11",
1087           N_("Expand the window to fill the screen"),
1088           G_CALLBACK (ev_window_cmd_view_fullscreen) },
1089 };
1090
1091 static void
1092 ev_window_init (EvWindow *ev_window)
1093 {
1094         GtkActionGroup *action_group;
1095         GtkAccelGroup *accel_group;
1096         GError *error = NULL;
1097         GtkWidget *scrolled_window;
1098         GtkWidget *menubar;
1099         GtkWidget *toolbar;
1100         GtkWidget *sidebar_widget;
1101
1102         ev_window->priv = EV_WINDOW_GET_PRIVATE (ev_window);
1103
1104         gtk_window_set_title (GTK_WINDOW (ev_window), _("Document Viewer"));
1105
1106         ev_window->priv->main_box = gtk_vbox_new (FALSE, 0);
1107         gtk_container_add (GTK_CONTAINER (ev_window), ev_window->priv->main_box);
1108         gtk_widget_show (ev_window->priv->main_box);
1109         
1110         action_group = gtk_action_group_new ("MenuActions");
1111         ev_window->priv->action_group = action_group;
1112         gtk_action_group_set_translation_domain (action_group, NULL);
1113         gtk_action_group_add_actions (action_group, entries,
1114                                       G_N_ELEMENTS (entries), ev_window);
1115         gtk_action_group_add_toggle_actions (action_group, toggle_entries,
1116                                              G_N_ELEMENTS (toggle_entries),
1117                                              ev_window);
1118
1119         ev_window->priv->ui_manager = gtk_ui_manager_new ();
1120         gtk_ui_manager_insert_action_group (ev_window->priv->ui_manager,
1121                                             action_group, 0);
1122
1123         accel_group =
1124                 gtk_ui_manager_get_accel_group (ev_window->priv->ui_manager);
1125         gtk_window_add_accel_group (GTK_WINDOW (ev_window), accel_group);
1126
1127         g_signal_connect (ev_window->priv->ui_manager, "connect_proxy",
1128                           G_CALLBACK (connect_proxy_cb), ev_window);
1129         g_signal_connect (ev_window->priv->ui_manager, "disconnect_proxy",
1130                           G_CALLBACK (disconnect_proxy_cb), ev_window);
1131
1132         if (!gtk_ui_manager_add_ui_from_file (ev_window->priv->ui_manager,
1133                                               DATADIR"/evince-ui.xml",
1134                                               &error)) {
1135                 g_message ("building menus failed: %s", error->message);
1136                 g_error_free (error);
1137         }
1138
1139         menubar = gtk_ui_manager_get_widget (ev_window->priv->ui_manager,
1140                                              "/MainMenu");
1141         gtk_box_pack_start (GTK_BOX (ev_window->priv->main_box), menubar,
1142                             FALSE, FALSE, 0);
1143
1144         toolbar = gtk_ui_manager_get_widget (ev_window->priv->ui_manager,
1145                                              "/ToolBar");
1146         gtk_box_pack_start (GTK_BOX (ev_window->priv->main_box), toolbar,
1147                             FALSE, FALSE, 0);
1148
1149         /* Add the main area */
1150         ev_window->priv->hpaned = gtk_hpaned_new ();
1151         gtk_widget_show (ev_window->priv->hpaned);
1152         gtk_box_pack_start (GTK_BOX (ev_window->priv->main_box), ev_window->priv->hpaned,
1153                             TRUE, TRUE, 0);
1154
1155         ev_window->priv->sidebar = ev_sidebar_new ();
1156         gtk_widget_show (ev_window->priv->sidebar);
1157         gtk_paned_add1 (GTK_PANED (ev_window->priv->hpaned),
1158                         ev_window->priv->sidebar);
1159
1160         /* Stub sidebar, for now */
1161         sidebar_widget = ev_sidebar_bookmarks_new ();
1162         gtk_widget_show (sidebar_widget);
1163         ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
1164                              "bookmarks",
1165                              _("Bookmarks"),
1166                              sidebar_widget);
1167
1168         sidebar_widget = ev_sidebar_thumbnails_new ();
1169         gtk_widget_show (sidebar_widget);
1170         ev_sidebar_add_page (EV_SIDEBAR (ev_window->priv->sidebar),
1171                              "thumbnails",
1172                              _("Thumbnails"),
1173                              sidebar_widget);
1174         
1175         scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1176         gtk_widget_show (scrolled_window);
1177         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
1178                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1179
1180         gtk_paned_add2 (GTK_PANED (ev_window->priv->hpaned),
1181                         scrolled_window);
1182
1183         ev_window->priv->view = ev_view_new ();
1184         gtk_widget_show (ev_window->priv->view);
1185         gtk_container_add (GTK_CONTAINER (scrolled_window),
1186                            ev_window->priv->view);
1187         g_signal_connect (ev_window->priv->view,
1188                           "page-changed",
1189                           G_CALLBACK (view_page_changed_cb),
1190                           ev_window);
1191
1192         ev_window->priv->statusbar = gtk_statusbar_new ();
1193         gtk_widget_show (ev_window->priv->statusbar);
1194         gtk_box_pack_end (GTK_BOX (ev_window->priv->main_box),
1195                           ev_window->priv->statusbar,
1196                           FALSE, TRUE, 0);
1197         ev_window->priv->help_message_cid = gtk_statusbar_get_context_id
1198                 (GTK_STATUSBAR (ev_window->priv->statusbar), "help_message");
1199
1200         ev_window->priv->find_bar = egg_find_bar_new ();
1201         gtk_box_pack_end (GTK_BOX (ev_window->priv->main_box),
1202                           ev_window->priv->find_bar,
1203                           FALSE, TRUE, 0);
1204         
1205         /* Connect to find bar signals */
1206         g_signal_connect (ev_window->priv->find_bar,
1207                           "previous",
1208                           G_CALLBACK (find_bar_previous_cb),
1209                           ev_window);
1210         g_signal_connect (ev_window->priv->find_bar,
1211                           "next",
1212                           G_CALLBACK (find_bar_next_cb),
1213                           ev_window);
1214         g_signal_connect (ev_window->priv->find_bar,
1215                           "close",
1216                           G_CALLBACK (find_bar_close_cb),
1217                           ev_window);
1218         g_signal_connect (ev_window->priv->find_bar,
1219                           "notify::search-string",
1220                           G_CALLBACK (find_bar_search_changed_cb),
1221                           ev_window);
1222         g_signal_connect (ev_window->priv->find_bar,
1223                           "notify::case-sensitive",
1224                           G_CALLBACK (find_bar_search_changed_cb),
1225                           ev_window);
1226         g_signal_connect (ev_window->priv->find_bar,
1227                           "notify::visible",
1228                           G_CALLBACK (find_bar_search_changed_cb),
1229                           ev_window);
1230
1231         g_signal_connect (ev_window, "window-state-event",
1232                           G_CALLBACK (ev_window_state_event_cb),
1233                           ev_window);
1234         g_signal_connect (ev_window, "focus_out_event",
1235                           G_CALLBACK (ev_window_focus_out_cb),
1236                           ev_window);
1237         
1238         /* Give focus to the scrolled window */
1239         gtk_widget_grab_focus (scrolled_window);
1240         
1241         update_action_sensitivity (ev_window);
1242