2 * PDF viewer Bonobo container.
5 * Michael Meeks <michael@imaginator.com>
13 #define GString G_String
15 #include <libgnorba/gnorba.h>
16 #include <gdk/gdkprivate.h>
18 #include <bonobo/gnome-bonobo.h>
25 #include "parseargs.h"
37 #include "GOutputDev.h"
38 #include "PSOutputDev.h"
39 #include "TextOutputDev.h"
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 }
53 typedef struct _Component Component;
54 typedef struct _Container Container;
55 /* NB. there is a 1 to 1 Container -> Component mapping, this
56 is due to how much MDI sucks; unutterably */
58 GnomeContainer *container;
61 GnomeViewFrame *active_view_frame;
64 GtkWidget *view_widget;
72 GnomeClientSite *client_site;
73 GnomeViewFrame *view_frame;
74 GnomeObjectClient *server;
77 GList *containers = NULL;
82 static Container *container_new (const char *fname);
83 static void container_destroy (Container *cont);
84 static void container_open_cmd (GtkWidget *widget, Container *container);
85 static void container_close_cmd (GtkWidget *widget, Container *container);
86 static void container_exit_cmd (void);
87 static Component *container_activate_component (Container *container, char *component_goad_id);
88 static void zoom_in_cmd (GtkWidget *widget, Container *container);
89 static void zoom_out_cmd (GtkWidget *widget, Container *container);
90 static void zoom_set (Container *container);
96 static GnomeUIInfo container_file_menu [] = {
97 GNOMEUIINFO_MENU_OPEN_ITEM (container_open_cmd, NULL),
98 GNOMEUIINFO_SEPARATOR,
99 GNOMEUIINFO_MENU_CLOSE_ITEM(container_close_cmd, NULL),
100 GNOMEUIINFO_SEPARATOR,
101 GNOMEUIINFO_MENU_EXIT_ITEM (container_exit_cmd, NULL),
105 static GnomeUIInfo container_menu_zoom [] = {
106 { GNOME_APP_UI_ITEM, N_("_Zoom in"),
107 N_("Increase the size of objects in the PDF"),
109 { GNOME_APP_UI_ITEM, N_("_Zoom out"),
110 N_("Decrease the size of objects in the PDF"),
115 static GnomeUIInfo container_main_menu [] = {
116 GNOMEUIINFO_MENU_FILE_TREE (container_file_menu),
117 { GNOME_APP_UI_SUBTREE, N_("_Zoom"), NULL, container_menu_zoom },
121 static GnomeUIInfo container_toolbar [] = {
122 GNOMEUIINFO_ITEM_STOCK (
123 N_("Open"), N_("Opens an existing workbook"),
124 container_open_cmd, GNOME_STOCK_PIXMAP_OPEN),
126 GNOMEUIINFO_SEPARATOR,
132 open_pdf (Container *container, const char *name)
134 GnomeObjectClient *object;
136 GNOME_PersistStream persist;
138 CORBA_Environment ev;
140 g_return_val_if_fail (container != NULL, FALSE);
141 g_return_val_if_fail (container->view_widget == NULL, FALSE);
143 comp = container_activate_component (container, "bonobo-object:image-x-pdf");
144 if (!comp || !(object = comp->server)) {
145 gnome_error_dialog (_("Could not launch bonobo object."));
149 CORBA_exception_init (&ev);
150 persist = GNOME_Unknown_query_interface (
151 gnome_object_corba_objref (GNOME_OBJECT (object)),
152 "IDL:GNOME/PersistStream:1.0", &ev);
154 if (ev._major != CORBA_NO_EXCEPTION ||
155 persist == CORBA_OBJECT_NIL) {
156 gnome_error_dialog ("Panic: component is well broken.");
160 stream = gnome_stream_fs_open (name, GNOME_Storage_READ);
162 if (stream == NULL) {
163 char *err = g_strconcat (_("Could not open "), name, NULL);
164 gnome_error_dialog_parented (err, GTK_WINDOW(container->app));
169 GNOME_PersistStream_load (persist,
170 (GNOME_Stream) gnome_object_corba_objref (GNOME_OBJECT (stream)), &ev);
174 GNOME_Unknown_unref (persist, &ev);
175 CORBA_Object_release (persist, &ev);
176 CORBA_exception_free (&ev);
181 set_ok (GtkWidget *widget, gboolean *dialog_result)
183 *dialog_result = TRUE;
188 file_dialog_delete_event (GtkWidget *widget, GdkEventAny *event)
195 container_open_cmd (GtkWidget *widget, Container *container)
197 GtkFileSelection *fsel;
198 gboolean accepted = FALSE;
200 fsel = GTK_FILE_SELECTION (gtk_file_selection_new (_("Load file")));
201 gtk_window_set_modal (GTK_WINDOW (fsel), TRUE);
203 gtk_window_set_transient_for (GTK_WINDOW (fsel),
204 GTK_WINDOW (container->app));
206 /* Connect the signals for Ok and Cancel */
207 gtk_signal_connect (GTK_OBJECT (fsel->ok_button), "clicked",
208 GTK_SIGNAL_FUNC (set_ok), &accepted);
209 gtk_signal_connect (GTK_OBJECT (fsel->cancel_button), "clicked",
210 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
211 gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);
214 * Make sure that we quit the main loop if the window is destroyed
216 gtk_signal_connect (GTK_OBJECT (fsel), "delete_event",
217 GTK_SIGNAL_FUNC (file_dialog_delete_event), NULL);
220 gtk_widget_show (GTK_WIDGET (fsel));
221 gtk_grab_add (GTK_WIDGET (fsel));
225 char *name = gtk_file_selection_get_filename (fsel);
227 if (name [strlen (name)-1] != '/') {
228 char *fname = g_strdup (name);
229 if (container->view_widget) /* any sort of MDI sucks :-] */
230 container = container_new (fname);
232 open_pdf (container, fname);
236 dialog = gnome_message_box_new ("Can't open a directory",
237 GNOME_MESSAGE_BOX_ERROR,
238 GNOME_STOCK_BUTTON_OK, NULL);
239 gnome_dialog_set_parent (GNOME_DIALOG (dialog),
240 GTK_WINDOW (container->app));
241 gnome_dialog_run (GNOME_DIALOG (dialog));
245 gtk_widget_destroy (GTK_WIDGET (fsel));
249 container_destroy (Container *cont)
251 containers = g_list_remove (containers, cont);
252 gtk_widget_destroy (cont->app);
259 container_close_cmd (GtkWidget *widget, Container *cont)
261 container_destroy (cont);
265 container_exit_cmd (void)
268 container_destroy ((Container *)containers->data);
272 * Enforces the containers zoom factor.
275 zoom_set (Container *container)
277 g_return_if_fail (container != NULL);
278 g_return_if_fail (container->component != NULL);
280 gnome_view_frame_set_zoom_factor (container->component->view_frame,
285 zoom_in_cmd (GtkWidget *widget, Container *container)
287 g_return_if_fail (container != NULL);
288 container->zoom *= 1.4;
289 zoom_set (container);
293 zoom_out_cmd (GtkWidget *widget, Container *container)
295 g_return_if_fail (container != NULL);
296 container->zoom /= 1.4;
297 zoom_set (container);
301 component_user_activate_request_cb (GnomeViewFrame *view_frame, gpointer data)
303 Component *component = (Component *) data;
304 Container *container = component->container;
308 * If there is already an active View, deactivate it.
310 if (container->active_view_frame != NULL) {
312 * This just sends a notice to the embedded View that
313 * it is being deactivated. We will also forcibly
314 * cover it so that it does not receive any Gtk
317 gnome_view_frame_view_deactivate (container->active_view_frame);
320 * Here we manually cover it if it hasn't acquiesced.
321 * If it has consented to be deactivated, then it will
322 * already have notified us that it is inactive, and
323 * we will have covered it and set active_view_frame
324 * to NULL. Which is why this check is here.
326 if (container->active_view_frame != NULL)
327 gnome_view_frame_set_covered (container->active_view_frame, TRUE);
329 container->active_view_frame = NULL;
333 * Activate the View which the user clicked on. This just
334 * sends a request to the embedded View to activate itself.
335 * When it agrees to be activated, it will notify its
336 * ViewFrame, and our view_activated_cb callback will be
339 * We do not uncover the View here, because it may not wish to
340 * be activated, and so we wait until it notifies us that it
341 * has been activated to uncover it.
343 gnome_view_frame_view_activate (view_frame);
347 component_view_activated_cb (GnomeViewFrame *view_frame, gboolean activated, gpointer data)
349 Component *component = (Component *) data;
350 Container *container = component->container;
354 * If the View is requesting to be activated, then we
355 * check whether or not there is already an active
358 if (container->active_view_frame != NULL) {
359 g_warning ("View requested to be activated but there is already "
360 "an active View!\n");
365 * Otherwise, uncover it so that it can receive
366 * events, and set it as the active View.
368 gnome_view_frame_set_covered (view_frame, FALSE);
369 container->active_view_frame = view_frame;
372 * If the View is asking to be deactivated, always
373 * oblige. We may have already deactivated it (see
374 * user_activation_request_cb), but there's no harm in
375 * doing it again. There is always the possibility
376 * that a View will ask to be deactivated when we have
377 * not told it to deactivate itself, and that is
378 * why we cover the view here.
380 gnome_view_frame_set_covered (view_frame, TRUE);
382 if (view_frame == container->active_view_frame)
383 container->active_view_frame = NULL;
388 component_user_context_cb (GnomeViewFrame *view_frame, gpointer data)
390 Component *component = (Component *) data;
395 * See if the remote GnomeEmbeddable supports any verbs at
398 l = gnome_client_site_get_verbs (component->client_site);
401 gnome_client_site_free_verbs (l);
404 * Popup the verb popup and execute the chosen verb. This
405 * function saves us the work of creating the menu, connecting
406 * the callback, and executing the verb on the remove
407 * GnomeView. We could implement all this functionality
408 * ourselves if we wanted.
410 executed_verb = gnome_view_frame_popup_verbs (view_frame);
412 g_free (executed_verb);
417 container_set_view (Container *container, Component *component)
419 GnomeViewFrame *view_frame;
420 GtkWidget *view_widget;
423 * Create the remote view and the local ViewFrame.
425 view_frame = gnome_client_site_new_view (component->client_site);
426 component->view_frame = view_frame;
429 * Set the GnomeUIHandler for this ViewFrame. That way, the
430 * embedded component can get access to our UIHandler server
431 * so that it can merge menu and toolbar items when it gets
434 gnome_view_frame_set_ui_handler (view_frame, container->uih);
437 * Embed the view frame into the application.
439 view_widget = gnome_view_frame_get_wrapper (view_frame);
440 container->view_widget = view_widget;
441 container->component = component;
442 /* gtk_box_pack_start (GTK_BOX (container->app), view_widget,
444 gnome_app_set_contents (GNOME_APP (container->app), view_widget);
446 * The "user_activate" signal will be emitted when the user
447 * double clicks on the "cover". The cover is a transparent
448 * window which sits on top of the component and keeps any
449 * events (mouse, keyboard) from reaching it. When the user
450 * double clicks on the cover, the container (that's us)
451 * can choose to activate the component.
453 gtk_signal_connect (GTK_OBJECT (view_frame), "user_activate",
454 GTK_SIGNAL_FUNC (component_user_activate_request_cb), component);
457 * In-place activation of a component is a two-step process.
458 * After the user double clicks on the component, our signal
459 * callback (compoennt_user_activate_request_cb()) asks the
460 * component to activate itself (see
461 * gnome_view_frame_view_activate()). The component can then
462 * choose to either accept or refuse activation. When an
463 * embedded component notifies us of its decision to change
464 * its activation state, the "view_activated" signal is
465 * emitted from the view frame. It is at that point that we
466 * actually remove the cover so that events can get through.
468 gtk_signal_connect (GTK_OBJECT (view_frame), "view_activated",
469 GTK_SIGNAL_FUNC (component_view_activated_cb), component);
472 * The "user_context" signal is emitted when the user right
473 * clicks on the wrapper. We use it to pop up a verb menu.
475 gtk_signal_connect (GTK_OBJECT (view_frame), "user_context",
476 GTK_SIGNAL_FUNC (component_user_context_cb), component);
479 * Show the component.
481 gtk_widget_show_all (view_widget);
484 static GnomeObjectClient *
485 container_launch_component (GnomeClientSite *client_site,
486 GnomeContainer *container,
487 char *component_goad_id)
489 GnomeObjectClient *object_server;
492 * Launch the component.
494 object_server = gnome_object_activate_with_goad_id (
495 NULL, component_goad_id, 0, NULL);
497 if (object_server == NULL)
501 * Bind it to the local ClientSite. Every embedded component
502 * has a local GnomeClientSite object which serves as a
503 * container-side point of contact for the embeddable. The
504 * container talks to the embeddable through its ClientSite
506 if (!gnome_client_site_bind_embeddable (client_site, object_server)) {
507 gnome_object_unref (GNOME_OBJECT (object_server));
512 * The GnomeContainer object maintains a list of the
513 * ClientSites which it manages. Here we add the new
514 * ClientSite to that list.
516 gnome_container_add (container, GNOME_OBJECT (client_site));
518 return object_server;
522 * Use query_interface to see if `obj' has `interface'.
525 gnome_object_has_interface (GnomeObject *obj, char *interface)
527 CORBA_Environment ev;
528 CORBA_Object requested_interface;
530 CORBA_exception_init (&ev);
532 requested_interface = GNOME_Unknown_query_interface (
533 gnome_object_corba_objref (obj), interface, &ev);
535 CORBA_exception_free (&ev);
537 if (!CORBA_Object_is_nil(requested_interface, &ev) &&
538 ev._major == CORBA_NO_EXCEPTION)
540 /* Get rid of the interface we've been passed */
541 CORBA_Object_release (requested_interface, &ev);
550 container_activate_component (Container *container, char *component_goad_id)
552 Component *component;
553 GnomeClientSite *client_site;
554 GnomeObjectClient *server;
557 * The ClientSite is the container-side point of contact for
558 * the Embeddable. So there is a one-to-one correspondence
559 * between GnomeClientSites and GnomeEmbeddables. */
560 client_site = gnome_client_site_new (container->container);
563 * A GnomeObjectClient is a simple wrapper for a remote
564 * GnomeObject (a server supporting GNOME::Unknown).
566 server = container_launch_component (client_site, container->container,
568 if (server == NULL) {
571 error_msg = g_strdup_printf (_("Could not launch Embeddable %s!"),
573 gnome_warning_dialog (error_msg);
580 * Create the internal data structure which we will use to
581 * keep track of this component.
583 component = g_new0 (Component, 1);
584 component->container = container;
585 component->client_site = client_site;
586 component->server = server;
588 container_set_view (container, component);
595 container_create_menus (Container *container)
597 GnomeUIHandlerMenuItem *menu_list;
599 gnome_ui_handler_create_menubar (container->uih);
602 * Create the basic menus out of UIInfo structures.
604 menu_list = gnome_ui_handler_menu_parse_uiinfo_list_with_data (container_main_menu, container);
605 gnome_ui_handler_menu_add_list (container->uih, "/", menu_list);
606 gnome_ui_handler_menu_free_list (menu_list);
610 container_create_toolbar (Container *container)
612 GnomeUIHandlerToolbarItem *toolbar;
614 gnome_ui_handler_create_toolbar (container->uih, "pdf");
615 toolbar = gnome_ui_handler_toolbar_parse_uiinfo_list (container->uih, container_toolbar);
616 gnome_ui_handler_toolbar_add_list (container->uih, "/", toolbar);
617 gnome_ui_handler_toolbar_free_list (toolbar);
621 container_new (const char *fname)
623 Container *container;
625 container = g_new0 (Container, 1);
627 container->app = gnome_app_new ("pdf-viewer",
629 container->zoom = 43.0;
631 gtk_window_set_default_size (GTK_WINDOW (container->app), 400, 400);
632 gtk_window_set_policy (GTK_WINDOW (container->app), TRUE, TRUE, FALSE);
634 container->container = gnome_container_new ();
635 container->view_widget = NULL;
638 * Create the GnomeUIHandler object which will be used to
639 * create the container's menus and toolbars. The UIHandler
640 * also creates a CORBA server which embedded components use
641 * to do menu/toolbar merging.
643 container->uih = gnome_ui_handler_new ();
644 gnome_ui_handler_set_app (container->uih, GNOME_APP (container->app));
646 container_create_menus (container);
647 container_create_toolbar (container);
649 gtk_widget_show_all (container->app);
652 if (!open_pdf (container, fname)) {
653 container_destroy (container);
657 containers = g_list_append (containers, container);
659 gtk_widget_show_all (container->app);
665 main (int argc, char **argv)
667 CORBA_Environment ev;
669 char **view_files = NULL;
672 CORBA_exception_init (&ev);
674 gnome_CORBA_init_with_popt_table ("PDFViewer", "0.0.1",
676 gpdf_popt_options, 0, &ctx,
677 GNORBA_INIT_SERVER_FUNC, &ev);
679 CORBA_exception_free (&ev);
681 orb = gnome_CORBA_ORB ();
683 if (bonobo_init (orb, NULL, NULL) == FALSE)
684 g_error (_("Could not initialize Bonobo!\n"));
687 view_files = poptGetArgs (ctx);
692 for (i = 0; view_files[i]; i++)
693 container_new (view_files[i]);
696 container_new (NULL);
698 poptFreeContext (ctx);