]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/gpdf.cc
more sensible menu merging.
[evince.git] / pdf / xpdf / gpdf.cc
1 /*
2  * PDF viewer Bonobo container.
3  *
4  * Author:
5  *   Michael Meeks <michael@imaginator.com>
6  *
7  */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <stddef.h>
11 #include <string.h>
12 extern "C" {
13 #define GString G_String
14 #include <gnome.h>
15 #include <libgnorba/gnorba.h>
16 #include <gdk/gdkprivate.h>
17 #include <gdk/gdkx.h>
18 #include <bonobo/gnome-bonobo.h>
19 #undef  GString 
20 }
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include "gtypes.h"
24 #include "GString.h"
25 #include "parseargs.h"
26 #include "gfile.h"
27 #include "gmem.h"
28 #include "Object.h"
29 #include "Stream.h"
30 #include "Array.h"
31 #include "Dict.h"
32 #include "XRef.h"
33 #include "Catalog.h"
34 #include "Page.h"
35 #include "Link.h"
36 #include "PDFDoc.h"
37 #include "GOutputDev.h"
38 #include "PSOutputDev.h"
39 #include "TextOutputDev.h"
40 #include "Params.h"
41 #include "Error.h"
42 #include "config.h"
43
44 poptContext ctx;
45 gint  gpdf_debug=1;
46
47 const struct poptOption gpdf_popt_options [] = {
48   { "debug", '\0', POPT_ARG_INT, &gpdf_debug, 0,
49     N_("Enables some debugging functions"), N_("LEVEL") },
50   { NULL, '\0', 0, NULL, 0 }
51 };
52
53 typedef struct {
54         GnomeContainer  *container;
55         GnomeUIHandler  *uih;
56
57         GnomeViewFrame  *active_view_frame;
58
59         GtkWidget       *app;
60         GtkWidget       *vbox;
61 } Container;
62
63 typedef struct {
64         Container         *container;
65
66         GnomeClientSite   *client_site;
67         GnomeViewFrame    *view_frame;
68         GnomeObjectClient *server;
69
70         GtkWidget         *views_hbox;
71 } Component;
72
73 GList *containers = NULL;
74 /*
75  * Static prototypes.
76  */
77 extern "C" {
78   static Container *container_new       (void);
79   static void       container_destroy   (Container *cont);
80   static void       container_open_cmd  (GtkWidget *widget, Container *container);
81   static void       container_close_cmd (GtkWidget *widget, Container *container);
82   static void       container_exit_cmd  (void);
83   static Component *container_activate_component (Container *container, char *component_goad_id);
84 }
85
86 /*
87  * The menus.
88  */
89 static GnomeUIInfo container_file_menu [] = {
90         GNOMEUIINFO_MENU_OPEN_ITEM (container_open_cmd, NULL),
91         GNOMEUIINFO_SEPARATOR,
92         GNOMEUIINFO_MENU_CLOSE_ITEM(container_close_cmd, NULL),
93         GNOMEUIINFO_SEPARATOR,
94         GNOMEUIINFO_MENU_EXIT_ITEM (container_exit_cmd, NULL),
95         GNOMEUIINFO_END
96 };
97
98 static GnomeUIInfo container_main_menu [] = {
99         GNOMEUIINFO_MENU_FILE_TREE (container_file_menu),
100         GNOMEUIINFO_END
101 };
102
103 extern "C" {
104   static void
105   open_pdf (Container *container, const char *name)
106   {
107     GnomeObjectClient *object;
108     GnomeStream *stream;
109     GNOME_PersistStream persist;
110     Component *comp;
111     CORBA_Environment ev;
112
113     comp = container_activate_component (container, "bonobo-object:image-x-pdf");
114     if (!comp || !(object = comp->server)) {
115       gnome_error_dialog (_("Could not launch bonobo object."));
116       return;
117     }
118     
119     CORBA_exception_init (&ev);
120     persist = GNOME_Unknown_query_interface (
121       gnome_object_corba_objref (GNOME_OBJECT (object)),
122       "IDL:GNOME/PersistStream:1.0", &ev);
123     
124     if (ev._major != CORBA_NO_EXCEPTION ||
125         persist == CORBA_OBJECT_NIL) {
126       gnome_error_dialog ("Panic: component is well broken.");
127       return;
128     }
129     
130     stream = gnome_stream_fs_open (name, GNOME_Storage_READ);
131     
132     if (stream == NULL) {
133       char *err = g_strconcat (_("Could not open "), name, NULL);
134       gnome_error_dialog_parented (err, GTK_WINDOW(container->app));
135       g_free (err);
136       return;
137     }
138     
139     GNOME_PersistStream_load (persist,
140                               (GNOME_Stream) gnome_object_corba_objref (GNOME_OBJECT (stream)), &ev);
141     
142     GNOME_Unknown_unref (persist, &ev);
143     CORBA_Object_release (persist, &ev);
144     CORBA_exception_free (&ev);
145 /*      app->contains_pdf = TRUE; */
146   }
147   
148   static void
149   set_ok (GtkWidget *widget, gboolean *dialog_result)
150   {
151     *dialog_result = TRUE;
152     gtk_main_quit ();
153   }
154   
155   static guint
156   file_dialog_delete_event (GtkWidget *widget, GdkEventAny *event)
157   {
158     gtk_main_quit ();
159     return TRUE;
160   }
161   
162   static void
163   container_open_cmd (GtkWidget *widget, Container *app)
164   {
165     GtkFileSelection *fsel;
166     gboolean accepted = FALSE;
167     
168     fsel = GTK_FILE_SELECTION (gtk_file_selection_new (_("Load file")));
169     gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
170     
171     gtk_window_set_transient_for (GTK_WINDOW (fsel),
172                                   GTK_WINDOW (app->app));
173     
174     /* Connect the signals for Ok and Cancel */
175     gtk_signal_connect (GTK_OBJECT (fsel->ok_button), "clicked",
176                         GTK_SIGNAL_FUNC (set_ok), &accepted);
177     gtk_signal_connect (GTK_OBJECT (fsel->cancel_button), "clicked",
178                         GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
179     gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
180     
181     /*
182      * Make sure that we quit the main loop if the window is destroyed 
183      */
184     gtk_signal_connect (GTK_OBJECT (fsel), "delete_event",
185                         GTK_SIGNAL_FUNC (file_dialog_delete_event), NULL);
186     
187     /* Run the dialog */
188     gtk_widget_show (GTK_WIDGET (fsel));
189     gtk_grab_add (GTK_WIDGET (fsel));
190     gtk_main ();
191     
192     if (accepted) {
193       char *name = gtk_file_selection_get_filename (fsel);
194       
195       if (name [strlen (name)-1] != '/') {
196 /*                      if (app->contains_pdf)
197                         app = application_new ();*/
198         char *fname = g_strdup (name);
199         open_pdf (app, fname);
200         g_free (fname);
201       } else {
202         GtkWidget *dialog;
203         dialog = gnome_message_box_new ("Can't open a directory",
204                                         GNOME_MESSAGE_BOX_ERROR,
205                                         GNOME_STOCK_BUTTON_OK, NULL);
206         gnome_dialog_set_parent (GNOME_DIALOG (dialog),
207                                  GTK_WINDOW (app->app));
208         gnome_dialog_run (GNOME_DIALOG (dialog));
209       }
210     }
211     
212     gtk_widget_destroy (GTK_WIDGET (fsel));
213   }
214
215   static void
216   container_destroy (Container *cont)
217   {
218     containers = g_list_remove (containers, cont);
219     gtk_widget_destroy (cont->app);
220     g_free (cont);
221     if (!containers)
222       gtk_main_quit ();
223   }
224   
225   static void
226   container_close_cmd (GtkWidget *widget, Container *cont)
227   {
228     container_destroy (cont);
229   }
230   
231   static void
232   container_exit_cmd (void)
233   {
234     while (containers)
235       container_destroy ((Container *)containers->data);
236   }
237
238   static void
239   component_user_activate_request_cb (GnomeViewFrame *view_frame, gpointer data)
240   {
241     Component *component = (Component *) data;
242     Container *container = component->container;
243     
244     /*
245      * If there is a
246      * If there is already an active View, deactivate it.
247      */
248     if (container->active_view_frame != NULL) {
249       /*
250        * This just sends a notice to the embedded View that
251        * it is being deactivated.  We will also forcibly
252        * cover it so that it does not receive any Gtk
253        * events.
254        */
255       gnome_view_frame_view_deactivate (container->active_view_frame);
256       
257       /*
258        * Here we manually cover it if it hasn't acquiesced.
259        * If it has consented to be deactivated, then it will
260        * already have notified us that it is inactive, and
261        * we will have covered it and set active_view_frame
262        * to NULL.  Which is why this check is here.
263        */
264       if (container->active_view_frame != NULL)
265         gnome_view_frame_set_covered (container->active_view_frame, TRUE);
266       
267       container->active_view_frame = NULL;
268     }
269     
270     /*
271      * Activate the View which the user clicked on.  This just
272      * sends a request to the embedded View to activate itself.
273      * When it agrees to be activated, it will notify its
274      * ViewFrame, and our view_activated_cb callback will be
275      * called.
276      *
277      * We do not uncover the View here, because it may not wish to
278      * be activated, and so we wait until it notifies us that it
279      * has been activated to uncover it.
280      */
281     gnome_view_frame_view_activate (view_frame);
282   }
283   
284   static void
285   component_view_activated_cb (GnomeViewFrame *view_frame, gboolean activated, gpointer data)
286   {
287     Component *component = (Component *) data;
288     Container *container = component->container;
289     
290     if (activated) {
291       /*
292        * If the View is requesting to be activated, then we
293        * check whether or not there is already an active
294        * View.
295        */
296       if (container->active_view_frame != NULL) {
297         g_warning ("View requested to be activated but there is already "
298                    "an active View!\n");
299         return;
300       }
301       
302       /*
303        * Otherwise, uncover it so that it can receive
304        * events, and set it as the active View.
305        */
306       gnome_view_frame_set_covered (view_frame, FALSE);
307       container->active_view_frame = view_frame;
308     } else {
309       /*
310        * If the View is asking to be deactivated, always
311        * oblige.  We may have already deactivated it (see
312        * user_activation_request_cb), but there's no harm in
313        * doing it again.  There is always the possibility
314        * that a View will ask to be deactivated when we have
315        * not told it to deactivate itself, and that is
316        * why we cover the view here.
317        */
318       gnome_view_frame_set_covered (view_frame, TRUE);
319       
320       if (view_frame == container->active_view_frame)
321         container->active_view_frame = NULL;
322     }                                                                       
323   }
324   
325   static void
326   component_user_context_cb (GnomeViewFrame *view_frame, gpointer data)
327   {
328     Component *component = (Component *) data;
329     char *executed_verb;
330     GList *l;
331     
332     /*
333      * See if the remote GnomeEmbeddable supports any verbs at
334      * all.
335      */
336     l = gnome_client_site_get_verbs (component->client_site);
337     if (l == NULL)
338       return;
339     gnome_client_site_free_verbs (l);
340     
341     /*
342      * Popup the verb popup and execute the chosen verb.  This
343      * function saves us the work of creating the menu, connecting
344      * the callback, and executing the verb on the remove
345      * GnomeView.  We could implement all this functionality
346      * ourselves if we wanted.
347      */
348     executed_verb = gnome_view_frame_popup_verbs (view_frame);
349     
350     g_free (executed_verb);
351   }
352 }
353
354 static void
355 component_add_view (Component *component)
356 {
357         GnomeViewFrame *view_frame;
358         GtkWidget *view_widget;
359
360         /*
361          * Create the remote view and the local ViewFrame.
362          */
363         view_frame = gnome_client_site_embeddable_new_view (component->client_site);
364         component->view_frame = view_frame;
365
366         /*
367          * Set the GnomeUIHandler for this ViewFrame.  That way, the
368          * embedded component can get access to our UIHandler server
369          * so that it can merge menu and toolbar items when it gets
370          * activated.
371          */
372         gnome_view_frame_set_ui_handler (view_frame, component->container->uih);
373
374         /*
375          * Embed the view frame into the application.
376          */
377         view_widget = gnome_view_frame_get_wrapper (view_frame);
378         gtk_box_pack_start (GTK_BOX (component->views_hbox), view_widget,
379                             FALSE, FALSE, 5);
380
381         /*
382          * The "user_activate" signal will be emitted when the user
383          * double clicks on the "cover".  The cover is a transparent
384          * window which sits on top of the component and keeps any
385          * events (mouse, keyboard) from reaching it.  When the user
386          * double clicks on the cover, the container (that's us)
387          * can choose to activate the component.
388          */
389         gtk_signal_connect (GTK_OBJECT (view_frame), "user_activate",
390                             GTK_SIGNAL_FUNC (component_user_activate_request_cb), component);
391
392         /*
393          * In-place activation of a component is a two-step process.
394          * After the user double clicks on the component, our signal
395          * callback (compoennt_user_activate_request_cb()) asks the
396          * component to activate itself (see
397          * gnome_view_frame_view_activate()).  The component can then
398          * choose to either accept or refuse activation.  When an
399          * embedded component notifies us of its decision to change
400          * its activation state, the "view_activated" signal is
401          * emitted from the view frame.  It is at that point that we
402          * actually remove the cover so that events can get through.
403          */
404         gtk_signal_connect (GTK_OBJECT (view_frame), "view_activated",
405                             GTK_SIGNAL_FUNC (component_view_activated_cb), component);
406
407         /*
408          * The "user_context" signal is emitted when the user right
409          * clicks on the wrapper.  We use it to pop up a verb menu.
410          */
411         gtk_signal_connect (GTK_OBJECT (view_frame), "user_context",
412                             GTK_SIGNAL_FUNC (component_user_context_cb), component);
413
414         /*
415          * Show the component.
416          */
417         gtk_widget_show_all (view_widget);
418 }
419
420 static void
421 component_new_view_cb (GtkWidget *button, gpointer data)
422 {
423         Component *component = (Component *) data;
424
425         component_add_view (component);
426 }
427
428 static GnomeObjectClient *
429 container_launch_component (GnomeClientSite *client_site,
430                             GnomeContainer *container,
431                             char *component_goad_id)
432 {
433         GnomeObjectClient *object_server;
434
435         /*
436          * Launch the component.
437          */
438         object_server = gnome_object_activate_with_goad_id (
439                 NULL, component_goad_id, 0, NULL);
440
441         if (object_server == NULL)
442                 return NULL;
443
444         /*
445          * Bind it to the local ClientSite.  Every embedded component
446          * has a local GnomeClientSite object which serves as a
447          * container-side point of contact for the embeddable.  The
448          * container talks to the embeddable through its ClientSite
449          */
450         if (!gnome_client_site_bind_embeddable (client_site, object_server)) {
451                 gnome_object_unref (GNOME_OBJECT (object_server));
452                 return NULL;
453         }
454
455         /*
456          * The GnomeContainer object maintains a list of the
457          * ClientSites which it manages.  Here we add the new
458          * ClientSite to that list.
459          */
460         gnome_container_add (container, GNOME_OBJECT (client_site));
461
462         return object_server;
463 }
464
465 /*
466  * Use query_interface to see if `obj' has `interface'.
467  */
468 static gboolean
469 gnome_object_has_interface (GnomeObject *obj, char *interface)
470 {
471         CORBA_Environment ev;
472         CORBA_Object requested_interface;
473
474         CORBA_exception_init (&ev);
475
476         requested_interface = GNOME_Unknown_query_interface (
477                 gnome_object_corba_objref (obj), interface, &ev);
478
479         CORBA_exception_free (&ev);
480
481         if (!CORBA_Object_is_nil(requested_interface, &ev) &&
482             ev._major == CORBA_NO_EXCEPTION)
483         {
484                 /* Get rid of the interface we've been passed */
485                 CORBA_Object_release (requested_interface, &ev);
486                 return TRUE;
487         }
488
489         return FALSE;
490 }
491
492 static void
493 container_create_component_frame (Container *container, Component *component, char *name)
494 {
495         /*
496          * This hbox will store all the views of the component.
497          */
498         component->views_hbox = gtk_hbox_new (FALSE, 2);
499
500         gtk_box_pack_start (GTK_BOX (container->vbox), component->views_hbox,
501                             TRUE, FALSE, 5);
502         gtk_widget_show_all (component->views_hbox);
503 }
504
505 extern "C" {
506   static Component *
507   container_activate_component (Container *container, char *component_goad_id)
508   {
509     Component *component;
510     GnomeClientSite *client_site;
511     GnomeObjectClient *server;
512     
513     /*
514      * The ClientSite is the container-side point of contact for
515      * the Embeddable.  So there is a one-to-one correspondence
516      * between GnomeClientSites and GnomeEmbeddables.  */
517     client_site = gnome_client_site_new (container->container);
518     
519     /*
520      * A GnomeObjectClient is a simple wrapper for a remote
521      * GnomeObject (a server supporting GNOME::Unknown).
522      */
523     server = container_launch_component (client_site, container->container,
524                                          component_goad_id);
525     if (server == NULL) {
526       char *error_msg;
527       
528       error_msg = g_strdup_printf (_("Could not launch Embeddable %s!"),
529                                    component_goad_id);
530       gnome_warning_dialog (error_msg);
531       g_free (error_msg);
532       
533       return NULL;
534     }
535     
536     /*
537      * Create the internal data structure which we will use to
538      * keep track of this component.
539      */
540     component = g_new0 (Component, 1);
541     component->container = container;
542     component->client_site = client_site;
543     component->server = server;
544     
545     /*
546      * Now we have a GnomeEmbeddable bound to our local
547      * ClientSite.  Here we create a little on-screen box to store
548      * the embeddable in, when the user adds views for it.
549      */
550     container_create_component_frame (container, component, component_goad_id);
551     
552     component_add_view (component);
553
554     return component;
555   }
556 }
557
558 static void
559 container_create_menus (Container *container)
560 {
561         GnomeUIHandlerMenuItem *menu_list;
562
563         gnome_ui_handler_create_menubar (container->uih);
564
565         /*
566          * Create the basic menus out of UIInfo structures.
567          */
568         menu_list = gnome_ui_handler_menu_parse_uiinfo_list_with_data (container_main_menu, container);
569         gnome_ui_handler_menu_add_list (container->uih, "/", menu_list);
570         gnome_ui_handler_menu_free_list (menu_list);
571 }
572
573 static void
574 container_create (void)
575 {
576         Container *container;
577
578         container = g_new0 (Container, 1);
579
580         container->app = gnome_app_new ("pdf-viewer",
581                                         "GNOME PDF viewer");
582
583         gtk_window_set_default_size (GTK_WINDOW (container->app), 400, 400);
584         gtk_window_set_policy (GTK_WINDOW (container->app), TRUE, TRUE, FALSE);
585
586         container->container = gnome_container_new ();
587
588         /*
589          * This is the VBox we will stuff embedded components into.
590          */
591         container->vbox = gtk_vbox_new (FALSE, 0);
592         gnome_app_set_contents (GNOME_APP (container->app), container->vbox);
593
594         /*
595          * Create the GnomeUIHandler object which will be used to
596          * create the container's menus and toolbars.  The UIHandler
597          * also creates a CORBA server which embedded components use
598          * to do menu/toolbar merging.
599          */
600         container->uih = gnome_ui_handler_new ();
601         gnome_ui_handler_set_app (container->uih, GNOME_APP (container->app));
602
603         /*
604          * Create the menus.
605          */
606         container_create_menus (container);
607         
608         gtk_widget_show_all (container->app);
609 }
610
611 int
612 main (int argc, char **argv)
613 {
614         CORBA_Environment ev;
615         CORBA_ORB orb;
616
617         CORBA_exception_init (&ev);
618
619         gnome_CORBA_init ("gnome_xpdf_viewer", "0.1", &argc, argv, 0, &ev);
620
621         CORBA_exception_free (&ev);
622
623         orb = gnome_CORBA_ORB ();
624
625         if (bonobo_init (orb, NULL, NULL) == FALSE)
626                 g_error (_("Could not initialize Bonobo!\n"));
627
628         container_create ();
629
630         gtk_main ();
631
632         return 0;
633 }