]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/gpdf.cc
stylisticly nicer bonobo container 'gpdf' pinched from Nat,
[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 void
429 component_load_pf_cb (GtkWidget *button, gpointer data)
430 {
431 }
432
433 static void
434 component_load_ps_cb (GtkWidget *button, gpointer data)
435 {
436 }
437
438 static void
439 component_destroy_cb (GtkWidget *button, gpointer data)
440 {
441 }
442
443 static GnomeObjectClient *
444 container_launch_component (GnomeClientSite *client_site,
445                             GnomeContainer *container,
446                             char *component_goad_id)
447 {
448         GnomeObjectClient *object_server;
449
450         /*
451          * Launch the component.
452          */
453         object_server = gnome_object_activate_with_goad_id (
454                 NULL, component_goad_id, 0, NULL);
455
456         if (object_server == NULL)
457                 return NULL;
458
459         /*
460          * Bind it to the local ClientSite.  Every embedded component
461          * has a local GnomeClientSite object which serves as a
462          * container-side point of contact for the embeddable.  The
463          * container talks to the embeddable through its ClientSite
464          */
465         if (!gnome_client_site_bind_embeddable (client_site, object_server)) {
466                 gnome_object_unref (GNOME_OBJECT (object_server));
467                 return NULL;
468         }
469
470         /*
471          * The GnomeContainer object maintains a list of the
472          * ClientSites which it manages.  Here we add the new
473          * ClientSite to that list.
474          */
475         gnome_container_add (container, GNOME_OBJECT (client_site));
476
477         return object_server;
478 }
479
480 /*
481  * Use query_interface to see if `obj' has `interface'.
482  */
483 static gboolean
484 gnome_object_has_interface (GnomeObject *obj, char *interface)
485 {
486         CORBA_Environment ev;
487         CORBA_Object requested_interface;
488
489         CORBA_exception_init (&ev);
490
491         requested_interface = GNOME_Unknown_query_interface (
492                 gnome_object_corba_objref (obj), interface, &ev);
493
494         CORBA_exception_free (&ev);
495
496         if (!CORBA_Object_is_nil(requested_interface, &ev) &&
497             ev._major == CORBA_NO_EXCEPTION)
498         {
499                 /* Get rid of the interface we've been passed */
500                 CORBA_Object_release (requested_interface, &ev);
501                 return TRUE;
502         }
503
504         return FALSE;
505 }
506
507 static void
508 container_create_component_frame (Container *container, Component *component, char *name)
509 {
510         /*
511          * This hbox will store all the views of the component.
512          */
513         component->views_hbox = gtk_hbox_new (FALSE, 2);
514
515         gtk_box_pack_start (GTK_BOX (container->vbox), component->views_hbox,
516                             TRUE, FALSE, 5);
517         gtk_widget_show_all (component->views_hbox);
518 }
519
520 extern "C" {
521   static Component *
522   container_activate_component (Container *container, char *component_goad_id)
523   {
524     Component *component;
525     GnomeClientSite *client_site;
526     GnomeObjectClient *server;
527     
528     /*
529      * The ClientSite is the container-side point of contact for
530      * the Embeddable.  So there is a one-to-one correspondence
531      * between GnomeClientSites and GnomeEmbeddables.  */
532     client_site = gnome_client_site_new (container->container);
533     
534     /*
535      * A GnomeObjectClient is a simple wrapper for a remote
536      * GnomeObject (a server supporting GNOME::Unknown).
537      */
538     server = container_launch_component (client_site, container->container,
539                                          component_goad_id);
540     if (server == NULL) {
541       char *error_msg;
542       
543       error_msg = g_strdup_printf (_("Could not launch Embeddable %s!"),
544                                    component_goad_id);
545       gnome_warning_dialog (error_msg);
546       g_free (error_msg);
547       
548       return NULL;
549     }
550     
551     /*
552      * Create the internal data structure which we will use to
553      * keep track of this component.
554      */
555     component = g_new0 (Component, 1);
556     component->container = container;
557     component->client_site = client_site;
558     component->server = server;
559     
560     /*
561      * Now we have a GnomeEmbeddable bound to our local
562      * ClientSite.  Here we create a little on-screen box to store
563      * the embeddable in, when the user adds views for it.
564      */
565     container_create_component_frame (container, component, component_goad_id);
566     
567     component_add_view (component);
568
569     return component;
570   }
571 }
572
573 static void
574 container_create_menus (Container *container)
575 {
576         GnomeUIHandlerMenuItem *menu_list;
577
578         gnome_ui_handler_create_menubar (container->uih);
579
580         /*
581          * Create the basic menus out of UIInfo structures.
582          */
583         menu_list = gnome_ui_handler_menu_parse_uiinfo_list_with_data (container_main_menu, container);
584         gnome_ui_handler_menu_add_list (container->uih, "/", menu_list);
585         gnome_ui_handler_menu_free_list (menu_list);
586 }
587
588 static void
589 container_create (void)
590 {
591         Container *container;
592
593         container = g_new0 (Container, 1);
594
595         container->app = gnome_app_new ("sample-container",
596                                         "Sample Bonobo Container");
597
598         gtk_window_set_default_size (GTK_WINDOW (container->app), 400, 400);
599         gtk_window_set_policy (GTK_WINDOW (container->app), TRUE, TRUE, FALSE);
600
601         container->container = gnome_container_new ();
602
603         /*
604          * This is the VBox we will stuff embedded components into.
605          */
606         container->vbox = gtk_vbox_new (FALSE, 0);
607         gnome_app_set_contents (GNOME_APP (container->app), container->vbox);
608
609         /*
610          * Create the GnomeUIHandler object which will be used to
611          * create the container's menus and toolbars.  The UIHandler
612          * also creates a CORBA server which embedded components use
613          * to do menu/toolbar merging.
614          */
615         container->uih = gnome_ui_handler_new ();
616         gnome_ui_handler_set_app (container->uih, GNOME_APP (container->app));
617
618         /*
619          * Create the menus.
620          */
621         container_create_menus (container);
622         
623         gtk_widget_show_all (container->app);
624 }
625
626 int
627 main (int argc, char **argv)
628 {
629         CORBA_Environment ev;
630         CORBA_ORB orb;
631
632         CORBA_exception_init (&ev);
633
634         gnome_CORBA_init ("gnome_xpdf_viewer", "0.1", &argc, argv, 0, &ev);
635
636         CORBA_exception_free (&ev);
637
638         orb = gnome_CORBA_ORB ();
639
640         if (bonobo_init (orb, NULL, NULL) == FALSE)
641                 g_error (_("Could not initialize Bonobo!\n"));
642
643         container_create ();
644
645         gtk_main ();
646
647         return 0;
648 }