From 85c366bda9f25b1249ba8333604eb3e757bc6edf Mon Sep 17 00:00:00 2001
From: Marco Pesenti Gritti <mpg@redhat.com>
Date: Mon, 26 Sep 2005 10:28:48 +0000
Subject: [PATCH] Implement launch links. Delegate external link opening to the
 window.

2005-09-26  Marco Pesenti Gritti  <mpg@redhat.com>

        * backend/ev-link.c: (ev_link_type_get_type),
        (ev_link_get_filename), (ev_link_get_params),
        (ev_link_get_property), (ev_link_set_property),
        (ev_window_dispose), (ev_link_class_init), (ev_link_new_launch):
        * backend/ev-link.h:
        * pdf/ev-poppler.cc:
        * shell/ev-view.c: (ev_view_goto_link), (ev_view_class_init):
        * shell/ev-window.c: (launch_link), (view_external_link_cb),
        (ev_window_init):

        Implement launch links. Delegate external link opening to
        the window.
---
 ChangeLog         | 15 ++++++++++
 backend/ev-link.c | 74 +++++++++++++++++++++++++++++++++++++++++++++--
 backend/ev-link.h |  6 ++++
 pdf/ev-poppler.cc |  3 +-
 shell/ev-view.c   | 16 ++++++++--
 shell/ev-window.c | 55 +++++++++++++++++++++++++++++++++++
 6 files changed, 163 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 71e8678c..c5faf622 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,18 @@
+2005-09-26  Marco Pesenti Gritti  <mpg@redhat.com>
+
+	* backend/ev-link.c: (ev_link_type_get_type),
+	(ev_link_get_filename), (ev_link_get_params),
+	(ev_link_get_property), (ev_link_set_property),
+	(ev_window_dispose), (ev_link_class_init), (ev_link_new_launch):
+	* backend/ev-link.h:
+	* pdf/ev-poppler.cc:
+	* shell/ev-view.c: (ev_view_goto_link), (ev_view_class_init):
+	* shell/ev-window.c: (launch_link), (view_external_link_cb),
+	(ev_window_init):
+
+	Implement launch links. Delegate external link opening to
+	the window.
+
 2005-09-24  Marco Pesenti Gritti  <mpg@redhat.com>
 
 	* backend/ev-link.c: (ev_link_type_get_type), (ev_link_get_bottom),
diff --git a/backend/ev-link.c b/backend/ev-link.c
index 57af5fa2..b8721d9b 100644
--- a/backend/ev-link.c
+++ b/backend/ev-link.c
@@ -34,7 +34,9 @@ enum {
 	PROP_TOP,
 	PROP_BOTTOM,
 	PROP_RIGHT,
-	PROP_ZOOM
+	PROP_ZOOM,
+	PROP_FILENAME,
+	PROP_PARAMS
 };
 
 
@@ -50,6 +52,8 @@ struct _EvLinkClass {
 struct _EvLinkPrivate {
 	char *title;
 	char *uri;
+	char *filename;
+	char *params;
 	EvLinkType type;
 	int page;
 	double top;
@@ -79,6 +83,7 @@ ev_link_type_get_type (void)
 			{ EV_LINK_TYPE_PAGE_FITV, "EV_LINK_TYPE_PAGE_FITV", "page-fitv" },
 			{ EV_LINK_TYPE_PAGE_FITR, "EV_LINK_TYPE_PAGE_FITR", "page-fitr" },
 			{ EV_LINK_TYPE_EXTERNAL_URI, "EV_LINK_TYPE_EXTERNAL_URI", "external" },
+			{ EV_LINK_TYPE_LAUNCH, "EV_LINK_TYPE_LAUNCH", "launch" },
 			{ 0, NULL, NULL }
                 };
 
@@ -152,6 +157,22 @@ ev_link_get_right (EvLink *self)
 	return self->priv->right;
 }
 
+const char *
+ev_link_get_filename (EvLink *link)
+{
+	g_return_val_if_fail (EV_IS_LINK (link), NULL);
+
+	return link->priv->filename;
+}
+
+const char *
+ev_link_get_params (EvLink *link)
+{
+	g_return_val_if_fail (EV_IS_LINK (link), NULL);
+
+	return link->priv->params;
+}
+
 double
 ev_link_get_zoom (EvLink *self)
 {
@@ -196,6 +217,10 @@ ev_link_get_property (GObject *object, guint prop_id, GValue *value,
 	case PROP_ZOOM:
 		g_value_set_double (value, self->priv->zoom);
 		break;
+	case PROP_FILENAME:
+		g_value_set_string (value, self->priv->filename);
+	case PROP_PARAMS:
+		g_value_set_string (value, self->priv->params);
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
 						   prop_id,
@@ -238,6 +263,14 @@ ev_link_set_property (GObject *object, guint prop_id, const GValue *value,
 	case PROP_ZOOM:
 		link->priv->zoom = g_value_get_double (value);
 		break;
+	case PROP_FILENAME:
+		g_free (link->priv->filename);
+		link->priv->filename = g_strdup (g_value_get_string (value));
+		break;
+	case PROP_PARAMS:
+		g_free (link->priv->params);
+		link->priv->params = g_strdup (g_value_get_string (value));
+		break;
 
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object,
@@ -266,6 +299,16 @@ ev_window_dispose (GObject *object)
 		priv->uri = NULL;
 	}
 
+	if (priv->filename) {
+		g_free (priv->filename);
+		priv->filename = NULL;
+	}
+
+	if (priv->params) {
+		g_free (priv->params);
+		priv->params = NULL;
+	}
+
 	G_OBJECT_CLASS (ev_link_parent_class)->dispose (object);
 }
 
@@ -305,6 +348,22 @@ ev_link_class_init (EvLinkClass *ev_window_class)
 							      NULL,
 							      G_PARAM_READWRITE |
 				     			      G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (g_object_class,
+					 PROP_FILENAME,
+					 g_param_spec_string ("filename",
+				     	 		      "Filename",
+				     			      "The link filename",
+							      NULL,
+							      G_PARAM_READWRITE |
+				     			      G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (g_object_class,
+					 PROP_PARAMS,
+					 g_param_spec_string ("params",
+				     	 		      "Params",
+				     			      "The link params",
+							      NULL,
+							      G_PARAM_READWRITE |
+				     			      G_PARAM_CONSTRUCT_ONLY));
 	g_object_class_install_property (g_object_class,
 					 PROP_TYPE,
 			 		 g_param_spec_enum  ("type",
@@ -479,7 +538,18 @@ ev_link_new_external (const char *title, const char *uri)
 				      NULL));
 }
 
-
+EvLink *
+ev_link_new_launch (const char *title,
+		    const char *filename,
+		    const char *params)
+{
+	return EV_LINK (g_object_new (EV_TYPE_LINK,
+				      "title", title,
+				      "filename", filename,
+				      "params", params,
+				      "type", EV_LINK_TYPE_LAUNCH,
+				      NULL));
+}
 
 static void
 ev_link_mapping_free_foreach (EvLinkMapping *mapping)
diff --git a/backend/ev-link.h b/backend/ev-link.h
index c014c010..6ab2811b 100644
--- a/backend/ev-link.h
+++ b/backend/ev-link.h
@@ -49,6 +49,7 @@ typedef enum
 	EV_LINK_TYPE_PAGE_FITV,
 	EV_LINK_TYPE_PAGE_FITR,
 	EV_LINK_TYPE_EXTERNAL_URI,
+	EV_LINK_TYPE_LAUNCH
 	/* We'll probably fill this in more as we support the other types of
 	 * links */
 } EvLinkType;
@@ -80,6 +81,9 @@ EvLink	       *ev_link_new_page_fit	(const char     *title,
 					 int             page);
 EvLink	       *ev_link_new_external	(const char     *title,
 					 const char     *uri);
+EvLink	       *ev_link_new_launch	(const char     *title,
+					 const char     *filename,
+					 const char     *params);
 
 const char     *ev_link_get_title	(EvLink     *link);
 const char     *ev_link_get_uri		(EvLink     *link);
@@ -90,6 +94,8 @@ double		ev_link_get_left	(EvLink     *link);
 double		ev_link_get_bottom	(EvLink     *link);
 double		ev_link_get_right	(EvLink     *link);
 double		ev_link_get_zoom	(EvLink     *link);
+const char     *ev_link_get_filename    (EvLink     *link);
+const char     *ev_link_get_params      (EvLink     *link);
 
 /* Link Mapping stuff */
 
diff --git a/pdf/ev-poppler.cc b/pdf/ev-poppler.cc
index 30b5a72e..c8405d33 100644
--- a/pdf/ev-poppler.cc
+++ b/pdf/ev-poppler.cc
@@ -784,7 +784,8 @@ ev_link_from_action (PopplerAction *action)
 		unimplemented_action = "POPPLER_ACTION_GOTO_REMOTE";
 		break;
 	case POPPLER_ACTION_LAUNCH:
-		unimplemented_action = "POPPLER_ACTION_LAUNCH";
+		link = ev_link_new_launch (title, action->launch.file_name,
+					   action->launch.params);
 		break;
 	case POPPLER_ACTION_URI:
 		link = ev_link_new_external (title, action->uri.uri);
diff --git a/shell/ev-view.c b/shell/ev-view.c
index c5cc5038..15bd4124 100644
--- a/shell/ev-view.c
+++ b/shell/ev-view.c
@@ -60,6 +60,7 @@ enum {
 enum {
 	SIGNAL_BINDING_ACTIVATED,
 	SIGNAL_ZOOM_INVALID,
+	SIGNAL_EXTERNAL_LINK,
 	N_SIGNALS,
 };
 
@@ -188,6 +189,8 @@ struct _EvViewClass {
 					   GtkScrollType   scroll,
 					   gboolean        horizontal);
 	void    (*zoom_invalid)		  (EvView         *view);
+	void    (*external_link)	  (EvView         *view,
+					   EvLink         *link);
 };
 
 /*** Scrolling ***/
@@ -1234,7 +1237,6 @@ void
 ev_view_goto_link (EvView *view, EvLink *link)
 {
 	EvLinkType type;
-	const char *uri;
 	int page;
 
 	type = ev_link_get_link_type (link);
@@ -1262,8 +1264,8 @@ ev_view_goto_link (EvView *view, EvLink *link)
 			goto_xyz_link (view, link);
 			break;
 		case EV_LINK_TYPE_EXTERNAL_URI:
-			uri = ev_link_get_uri (link);
-			gnome_vfs_url_show (uri);
+		case EV_LINK_TYPE_LAUNCH:
+			g_signal_emit (view, signals[SIGNAL_EXTERNAL_LINK], 0, link);
 			break;
 	}
 }
@@ -2288,6 +2290,14 @@ ev_view_class_init (EvViewClass *class)
 		         NULL, NULL,
 		         ev_marshal_VOID__VOID,
 		         G_TYPE_NONE, 0, G_TYPE_NONE);
+	signals[SIGNAL_EXTERNAL_LINK] = g_signal_new ("external-link",
+	  	         G_TYPE_FROM_CLASS (object_class),
+		         G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+		         G_STRUCT_OFFSET (EvViewClass, external_link),
+		         NULL, NULL,
+		         g_cclosure_marshal_VOID__OBJECT,
+		         G_TYPE_NONE, 1,
+			 G_TYPE_OBJECT);
 
 	g_object_class_install_property (object_class,
 					 PROP_STATUS,
diff --git a/shell/ev-window.c b/shell/ev-window.c
index 5abda4d7..3ca912ea 100644
--- a/shell/ev-window.c
+++ b/shell/ev-window.c
@@ -3228,6 +3228,58 @@ sidebar_links_link_activated_cb (EvSidebarLinks *sidebar_links, EvLink *link, Ev
 	ev_view_goto_link (EV_VIEW (window->priv->view), link);
 }
 
+static void
+launch_link (EvWindow *window, EvLink *link)
+{
+	const char *filename = ev_link_get_filename (link);
+	char *uri = NULL;
+
+	if (g_path_is_absolute (filename)) {
+		uri = g_strconcat ("file://", filename, NULL);
+	} else {
+		GnomeVFSURI *base_uri, *resolved_uri;
+
+		base_uri = gnome_vfs_uri_new (window->priv->uri);
+		if (base_uri) {
+			resolved_uri = gnome_vfs_uri_resolve_relative (base_uri, filename);	
+			if (resolved_uri) {
+				uri = gnome_vfs_uri_to_string (resolved_uri, GNOME_VFS_URI_HIDE_NONE);
+			}
+			gnome_vfs_uri_unref (resolved_uri);
+		}
+		gnome_vfs_uri_unref (base_uri);
+	}
+
+	if (uri) {
+		gnome_vfs_url_show (uri);
+	} else {
+		gnome_vfs_url_show (filename);
+	}
+
+	g_free (uri);
+
+	/* According to the PDF spec filename can be an executable. I'm not sure
+	   allowing to launch executables is a good idea though. -- marco */
+}
+
+static void
+view_external_link_cb (EvView *view, EvLink *link, EvWindow *window)
+{
+	const char *uri;
+
+	switch (ev_link_get_link_type (link)) {
+	case EV_LINK_TYPE_EXTERNAL_URI:
+		uri = ev_link_get_uri (link);
+		gnome_vfs_url_show (uri);
+		break;
+	case EV_LINK_TYPE_LAUNCH:
+		launch_link (window, link);
+		break;
+	default:
+		g_assert_not_reached ();
+	}
+}
+
 static void
 ev_window_init (EvWindow *ev_window)
 {
@@ -3368,6 +3420,9 @@ ev_window_init (EvWindow *ev_window)
 	g_signal_connect_object (ev_window->priv->view, "focus_out_event",
 			         G_CALLBACK (view_actions_focus_out_cb),
 			         ev_window, 0);
+	g_signal_connect_object (ev_window->priv->view, "external-link",
+			         G_CALLBACK (view_external_link_cb),
+			         ev_window, 0);
 	gtk_widget_show (ev_window->priv->view);
 	gtk_widget_show (ev_window->priv->password_view);
 
-- 
2.43.5