diff -urN epiphany-extensions/configure.ac epiphany-extensions.new/configure.ac
--- epiphany-extensions/configure.ac	2004-09-28 13:37:12 -0500
+++ epiphany-extensions.new/configure.ac	2004-10-11 13:06:08 -0500
@@ -14,7 +14,7 @@
 dnl with this program; if not, write to the Free Software Foundation, Inc.,
 dnl 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 dnl
-dnl $Id: configure.ac,v 1.75.2.9 2004/09/28 18:37:12 chpe Exp $
+dnl $Id: configure.ac,v 1.75.2.7 2004/09/28 18:08:45 chpe Exp $
 
 AC_INIT([Epiphany Extensions], [1.4.1.90], [http://bugzilla.gnome.org/enter_bug.cgi?product=epiphany-extensions], [epiphany-extensions])
 GNOME_COMMON_INIT
@@ -24,7 +24,7 @@
 
 AC_PREREQ(2.57)
 
-AC_REVISION($Revision: 1.75.2.9 $)
+AC_REVISION($Revision: 1.75.2.7 $)
 
 AC_CONFIG_SRCDIR(extensions)
 AM_CONFIG_HEADER(config.h)
@@ -136,9 +136,9 @@
 
 AC_MSG_CHECKING([extensions to build])
 
-all_extensions="certificates,dashboard,error-viewer,gestures,page-info,sample,sample-mozilla,select-stylesheet,sidebar,smart-bookmarks,tabsmenu,tab-groups"
+all_extensions="certificates,dashboard,error-viewer,gestures,page-info,sample,sample-mozilla,select-stylesheet,sidebar,smart-bookmarks,tabsmenu,tab-groups,openwith"
 all_useful_extensions="certificates,dashboard,error-viewer,gestures,page-info,select-stylesheet,sidebar,smart-bookmarks,tabsmenu,tab-groups"
-default_extensions="certificates,error-viewer,gestures,page-info,select-stylesheet,smart-bookmarks,tabsmenu,tab-groups"
+default_extensions="certificates,error-viewer,gestures,page-info,select-stylesheet,smart-bookmarks,tabsmenu,tab-groups,openwith"
 mozilla_dependent_extensions="certificates,error-viewer,page-info,sample-mozilla,select-stylesheet,sidebar,smart-bookmarks"
 
 dnl due to an autoconf bug, commas in the first arg to
@@ -195,6 +195,7 @@
 AM_CONDITIONAL(ENABLE_SMART_BOOKMARKS_EXTENSION, echo "$extensions" | egrep '(^|,)smart-bookmarks($|,)' > /dev/null)
 AM_CONDITIONAL(ENABLE_TABSMENU_EXTENSION, echo "$extensions" | egrep '(^|,)tabsmenu($|,)' > /dev/null)
 AM_CONDITIONAL(ENABLE_TAB_GROUPS_EXTENSION, echo "$extensions" | egrep '(^|,)tab-groups($|,)' > /dev/null)
+AM_CONDITIONAL(ENABLE_OPENWITH_EXTENSION, echo "$extensions" | egrep '(^|,)openwith($|,)' > /dev/null)
 dnl AM_CONDITIONAL(ENABLE_TOOLBAR_EXTRAS_EXTENSION, echo "$extensions" | egrep '(^|,)toolbar-extras($|,)' > /dev/null)
 
 dnl ******************
@@ -351,6 +352,7 @@
 extensions/smart-bookmarks/mozilla/Makefile
 extensions/tabsmenu/Makefile
 extensions/tab-groups/Makefile
+extensions/openwith/Makefile
 po/Makefile.in
 tools/Makefile
 ])
diff -urN epiphany-extensions/CVS/Entries epiphany-extensions.new/CVS/Entries
--- epiphany-extensions/CVS/Entries	2004-10-11 13:07:18 -0500
+++ epiphany-extensions.new/CVS/Entries	2004-10-11 13:06:08 -0500
@@ -5,7 +5,6 @@
 /ChangeLog/1.239.2.27/Fri Oct  8 16:58:32 2004//Tgnome-2-8
 /HACKING/1.4/Thu Mar 11 14:25:28 2004//Tgnome-2-8
 /MAINTAINERS/1.8/Thu Mar 11 14:25:28 2004//Tgnome-2-8
-/Makefile.am/1.9.4.2/Tue Sep 28 18:36:18 2004//Tgnome-2-8
 /NEWS/1.17.2.1/Tue Sep 28 18:08:45 2004//Tgnome-2-8
 /README/1.12/Mon Sep 13 04:18:12 2004//Tgnome-2-8
 /TODO/1.3/Wed Nov 12 20:17:30 2003//Tgnome-2-8
@@ -16,3 +15,4 @@
 D/include////
 D/po////
 D/tools////
+/Makefile.am/1.9.4.2/Mon Oct 11 18:03:26 2004//Tgnome-2-8
diff -urN epiphany-extensions/extensions/CVS/Entries epiphany-extensions.new/extensions/CVS/Entries
--- epiphany-extensions/extensions/CVS/Entries	2004-10-11 13:07:18 -0500
+++ epiphany-extensions.new/extensions/CVS/Entries	2004-10-11 13:06:09 -0500
@@ -13,3 +13,4 @@
 D/tab-groups////
 D/tabsmenu////
 D/toolbar-extras////
+D/openwith////
diff -urN epiphany-extensions/extensions/Makefile.am epiphany-extensions.new/extensions/Makefile.am
--- epiphany-extensions/extensions/Makefile.am	2004-09-28 13:36:18 -0500
+++ epiphany-extensions.new/extensions/Makefile.am	2004-10-11 13:06:09 -0500
@@ -48,10 +48,14 @@
 SUBDIRS += tab-groups
 endif
 
+if ENABLE_OPENWITH_EXTENSION
+SUBDIRS += openwith
+endif
+
 #if ENABLE_TOOLBAR_EXTRAS_EXTENSION
 #SUBDIRS += toolbar-extras
 #endif
 
-DIST_SUBDIRS = certificates dashboard error-viewer gestures page-info sample sample-mozilla select-stylesheet sidebar smart-bookmarks tabsmenu tab-groups
+DIST_SUBDIRS = certificates dashboard error-viewer gestures page-info sample sample-mozilla select-stylesheet sidebar smart-bookmarks tabsmenu tab-groups openwith
 
 EXTRA_DIST =
diff -urN epiphany-extensions/extensions/openwith/CVS/Entries epiphany-extensions.new/extensions/openwith/CVS/Entries
--- epiphany-extensions/extensions/openwith/CVS/Entries	1969-12-31 18:00:00 -0600
+++ epiphany-extensions.new/extensions/openwith/CVS/Entries	2004-10-11 13:06:09 -0500
@@ -0,0 +1 @@
+D
diff -urN epiphany-extensions/extensions/openwith/CVS/Repository epiphany-extensions.new/extensions/openwith/CVS/Repository
--- epiphany-extensions/extensions/openwith/CVS/Repository	1969-12-31 18:00:00 -0600
+++ epiphany-extensions.new/extensions/openwith/CVS/Repository	2004-10-11 13:06:09 -0500
@@ -0,0 +1 @@
+epiphany-extensions/extensions/openwith
diff -urN epiphany-extensions/extensions/openwith/CVS/Root epiphany-extensions.new/extensions/openwith/CVS/Root
--- epiphany-extensions/extensions/openwith/CVS/Root	1969-12-31 18:00:00 -0600
+++ epiphany-extensions.new/extensions/openwith/CVS/Root	2004-10-11 13:06:09 -0500
@@ -0,0 +1 @@
+:pserver:anonymous@anoncvs.gnome.org:/cvs/gnome
diff -urN epiphany-extensions/extensions/openwith/CVS/Tag epiphany-extensions.new/extensions/openwith/CVS/Tag
--- epiphany-extensions/extensions/openwith/CVS/Tag	1969-12-31 18:00:00 -0600
+++ epiphany-extensions.new/extensions/openwith/CVS/Tag	2004-10-11 13:06:09 -0500
@@ -0,0 +1 @@
+Tgnome-2-8
diff -urN epiphany-extensions/extensions/openwith/ephy-openwith-extension.c epiphany-extensions.new/extensions/openwith/ephy-openwith-extension.c
--- epiphany-extensions/extensions/openwith/ephy-openwith-extension.c	1969-12-31 18:00:00 -0600
+++ epiphany-extensions.new/extensions/openwith/ephy-openwith-extension.c	2004-10-11 13:06:09 -0500
@@ -0,0 +1,850 @@
+/*
+ *  Copyright (C) 2004 Jerry Haltom
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  $Id$
+ */
+ 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include "ephy-openwith-extension.h"
+#include "ephy-debug.h"
+
+#include <epiphany/ephy-extension.h>
+#include <epiphany/ephy-embed.h>
+#include <epiphany/ephy-embed-factory.h>
+#include <epiphany/ephy-embed-persist.h>
+#include <epiphany/ephy-command-manager.h>
+#include <epiphany/ephy-shell.h>
+#include <epiphany/ephy-session.h>
+#include <epiphany/ephy-node.h>
+
+#include <gmodule.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkaction.h>
+#include <gtk/gtkactiongroup.h>
+#include <gtk/gtkuimanager.h>
+
+#include <glib/gi18n-lib.h>
+#include <glib/gprintf.h>
+
+#include <libgnomevfs/gnome-vfs.h>
+#include <libgnomevfs/gnome-vfs-mime.h>
+#include <libgnomevfs/gnome-vfs-mime-handlers.h>
+#include <libgnomevfs/gnome-vfs-mime-monitor.h>
+#include <libgnomevfs/gnome-vfs-mime-info.h>
+
+
+#define EPHY_OPENWITH_EXTENSION_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_OPENWITH_EXTENSION, EphyOpenWithExtensionPrivate))
+
+
+#define WINDOW_DATA_KEY		"OpenWithExtWindowData"
+
+struct EphyOpenWithExtensionPrivate
+{
+
+};
+
+/* Special data attached to each window. */
+typedef struct
+{
+	EphyWindow *window;
+	GtkUIManager *manager;
+	GtkActionGroup *action_group;
+
+	guint merge_id;
+
+	GnomeVFSURI *uri;
+
+	const char *short_mime_type;
+	const char *right_mime_type;
+
+	GList *short_apps; /* derived from path extension */
+	GList *right_apps; /* derived from HTTP HEAD */
+	
+	GnomeVFSAsyncHandle *async_op_handle;
+} WindowData;
+
+/* Passed along with the open with action. */
+typedef struct
+{
+	WindowData *state;
+	GnomeVFSMimeApplication *app;
+	GnomeVFSURI *uri;
+} ActionData;
+
+static void ephy_openwith_extension_class_init	(EphyOpenWithExtensionClass *klass);
+static void ephy_openwith_extension_iface_init	(EphyExtensionIface *iface);
+static void ephy_openwith_extension_init	(EphyOpenWithExtension *extension);
+
+enum
+{
+	PROP_0
+};
+
+static GObjectClass *openwith_extension_parent_class = NULL;
+
+static GType openwith_extension_type = 0;
+
+GType
+ephy_openwith_extension_get_type (void)
+{
+	return openwith_extension_type;
+};
+
+GType
+ephy_openwith_extension_register_type (GTypeModule *module)
+{
+	static const GTypeInfo our_info = {
+		sizeof (EphyOpenWithExtensionClass),
+		NULL, /* base_init */
+		NULL, /* base_finalize */
+		(GClassInitFunc) ephy_openwith_extension_class_init,
+		NULL,
+		NULL, /* class_data */
+		sizeof (EphyOpenWithExtension),
+		0, /* n_preallocs */
+		(GInstanceInitFunc) ephy_openwith_extension_init
+	};
+
+	static const GInterfaceInfo extension_info = {
+		(GInterfaceInitFunc) ephy_openwith_extension_iface_init,
+		NULL,
+		NULL
+	};
+
+	openwith_extension_type = g_type_module_register_type (
+		module,
+		G_TYPE_OBJECT,
+		"EphyOpenWithExtension",
+		&our_info,
+		0
+	);
+
+	g_type_module_add_interface (
+		module,
+		openwith_extension_type,
+		EPHY_TYPE_EXTENSION,
+		&extension_info
+	);
+
+	return openwith_extension_type;
+}
+
+static void
+ephy_openwith_extension_init (EphyOpenWithExtension *extension)
+{
+	LOG ("EphyOpenWithExtension initializing");
+	extension->priv = EPHY_OPENWITH_EXTENSION_GET_PRIVATE (extension);
+}
+
+static void
+ephy_openwith_extension_finalize (GObject *object)
+{
+	LOG ("EphyOpenWithExtension finalizing");
+	G_OBJECT_CLASS (openwith_extension_parent_class)->finalize (object);
+}
+
+static void
+ephy_openwith_extension_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	
+}
+
+static void
+ephy_openwith_extension_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	
+}
+
+static void
+ephy_openwith_extension_class_init (EphyOpenWithExtensionClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+	openwith_extension_parent_class = g_type_class_peek_parent (klass);
+    
+	object_class->finalize = ephy_openwith_extension_finalize;
+	object_class->get_property = ephy_openwith_extension_get_property;
+	object_class->set_property = ephy_openwith_extension_set_property;
+
+	g_type_class_add_private (object_class, sizeof (EphyOpenWithExtensionPrivate));
+}
+
+/**
+ * Stolen from Epiphany lib/ephy-file-helpers.c.
+ */
+extern char *
+ephy_file_tmp_filename (const char *base,
+			const char *extension);
+
+/**
+ * Does the actual application launch.
+ *
+ * @param app the application to launch with
+ * @param path either a uri or local file path to be passed to the application
+ */
+static void
+openwith_launch (GnomeVFSMimeApplication *app, const char *path)
+{
+	LOG ("openwith_launch");
+
+	GtkWidget *dialog;
+	GError *error = NULL;
+	char *argv[3];
+
+
+	argv[0] = g_strchomp (app->command);
+	argv[1] = (char *) path;
+	argv[2] = NULL;
+
+	g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
+
+	if (error != NULL) {
+		dialog = gtk_message_dialog_new (NULL,
+						 GTK_DIALOG_DESTROY_WITH_PARENT,
+						 GTK_MESSAGE_ERROR,
+						 GTK_BUTTONS_CLOSE,
+						 "Error opening: '%s' with '%s': %s",
+						 argv[1], app->name, error->message);
+ 		gtk_dialog_run (GTK_DIALOG (dialog));
+		gtk_widget_destroy (dialog);
+
+		g_error_free (error);
+	}
+
+	g_free (argv[0]);
+}
+
+/**
+ * Called when the download is complete. Hands the commands off to be laucnhed.
+ *
+ * @param persist reference to the download context
+ * @param user_data reference to the action data
+ */
+static void
+openwith_download_completed (EphyEmbedPersist *persist, gpointer user_data)
+{
+	LOG ("openwith_download_completed");
+
+	ActionData *data;
+	const char *dest;
+
+
+	/* get our important state objects */
+	data = (ActionData *) user_data;
+
+	/* our file */
+	dest = ephy_embed_persist_get_dest (persist);
+	openwith_launch (data->app, dest);
+
+	g_free (data);
+}
+
+/**
+ * Called if the download is cancelled by the user. Cleans up the ActionData
+ * which shouldn't be left lying around.
+ *
+ * @param persist reference to the download context
+ * @param user_data reference to the action data
+ */
+static void
+openwith_download_cancelled (EphyEmbedPersist *persist, gpointer user_data)
+{
+	LOG ("openwith_download_canceled");
+
+	ActionData *data;
+
+
+	/* get our important state objects */
+	data = (ActionData *) user_data;
+
+	g_free (data);
+}
+
+/**
+ * Called in response to a selection of one of the items in the Open With menu.
+ * Launches the application.
+ */
+static void
+ephy_openwith_action (GtkAction *action, gpointer user_data)
+{
+	LOG ("ephy_openwith_action");
+
+	ActionData *data;
+	EphyEmbed *embed;
+	EphyEmbedPersist *persist;
+
+	char *uri;
+	char *base, *base_converted, *dest;
+
+
+	/* get our important state objects */
+	data = (ActionData *) user_data;
+
+	/* plain text url string */
+	uri = gnome_vfs_uri_to_string (data->uri, GNOME_VFS_URI_HIDE_PASSWORD);
+
+	/* check if the application supports uris, if not, download the file */
+	if (!(data->app->expects_uris & GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS)) {
+		LOG ("application does not support uris: downloading");
+
+		embed = ephy_window_get_active_embed (data->state->window);
+		persist = EPHY_EMBED_PERSIST (ephy_embed_factory_new_object ("EphyEmbedPersist"));
+
+		base = g_path_get_basename (uri);
+		base_converted = g_filename_from_utf8 (base, -1, NULL, NULL, NULL);
+		base_converted = g_strdup_printf ("%s.XXXXXX", base_converted);
+		dest = ephy_file_tmp_filename (base_converted, NULL);
+		dest = g_build_path ("/", g_get_tmp_dir (), dest);
+		g_print ("%s\n", dest);
+
+		ephy_embed_persist_set_embed (persist, embed);
+		ephy_embed_persist_set_dest (persist, dest);
+		ephy_embed_persist_set_flags (persist, EMBED_PERSIST_ASK_DESTINATION | EMBED_PERSIST_DO_CONVERSION);
+		ephy_embed_persist_set_source (persist, uri);
+
+		g_signal_connect (persist, "completed",
+				  G_CALLBACK (openwith_download_completed),
+				  data);
+
+		g_signal_connect (persist, "cancelled",
+				  G_CALLBACK (openwith_download_cancelled),
+				  data);
+
+		ephy_embed_persist_save (persist);  
+	} else {
+		LOG ("application supports uris: launching");
+		openwith_launch (data->app, uri);
+	}
+}
+
+/**
+ * Returns a GList<GnomeVFSURI> from a event.
+ *
+ * @param event a reference to the event
+ */
+static GList *
+openwith_get_uri_list_from_event (EphyEmbedEvent *event)
+{
+	LOG ("openwith_get_uri_list_from_event");
+
+	const GValue *value;
+	const char *link;
+	GnomeVFSURI *uri;
+	GList *uris = NULL;
+
+	
+	/* extract the link out of the event object */
+	ephy_embed_event_get_property (event, "link", &value);
+	g_return_val_if_fail (value != NULL, NULL);
+	link = g_value_get_string (value);
+
+	/* bundle the plain text link into a GList<GnomeVFSURI> as required by
+           GnomeVFS */
+	uri = gnome_vfs_uri_new (link);
+	uris = g_list_append (uris, (gpointer) uri);
+
+	return uris;
+}
+
+/**
+ * Clears the previous set of changes from the UI. Also removes associated
+ * GtkActions.
+ *
+ * @param data reference to the data structure of the window
+ */
+static void
+openwith_unmerge_ui (WindowData *data)
+{
+	LOG ("openwith_unmerge_ui");
+
+	/* if there is a previous outstanding gnomevfs request, cancel it */
+	if (data->async_op_handle != NULL) {
+		gnome_vfs_async_cancel (data->async_op_handle);
+		data->async_op_handle = NULL;
+	}
+
+	/* if there is a currently merge UI, remove it */
+	if (data->merge_id != 0) {
+		gtk_ui_manager_remove_ui (data->manager, data->merge_id);
+		data->merge_id = 0;
+	}
+
+	/* unref the action group */
+	if (data->action_group != NULL) {
+		gtk_ui_manager_remove_action_group (data->manager, data->action_group);
+		g_object_unref (data->action_group);
+		data->action_group = NULL;
+	}
+
+	gtk_ui_manager_ensure_update (data->manager);
+}
+
+/**
+ * Adds a single list of apps to the UI. Loops over the apps, backwards, adding
+ * each UI element to the top of the menu. This allows it to be run twice, once
+ * for the apps derived from the extension and again for the real apps.
+ *
+ * @param data a reference to the window state information
+ * @param uri uri to be opened
+ * @param apps list of applications to add
+ */
+static void
+openwith_merge_ui_add_apps (WindowData *data, GnomeVFSURI *uri, GList *apps)
+{
+	LOG ("openwith_merge_ui_add_apps");
+
+	/* enumeration */
+	GList *l;
+	GnomeVFSMimeApplication *app;
+
+	/* variables to build a new action */
+	guint32 action_number;
+	char *action_name;
+	char *action_path;
+	GtkAction *action;
+	ActionData *action_data;
+
+
+	/* enumerate over the apps, backwards, adding to the top */
+	for (l = g_list_last (apps); l; l = l->prev) {
+		app = (GnomeVFSMimeApplication *) l->data;
+
+		/* generate a custom name for this action */
+		/* hack to make sure the action names are unique */
+		action_number = g_random_int_range (0, 65535);
+		action_name = g_strdup_printf ("%s_%d", app->id, action_number);
+		action_path = g_strdup_printf ("OpenWithExtAction_%s_%d", app->id, action_number);
+
+		/* create the actions associated to the ui */
+		action = gtk_action_new (action_name, app->name, NULL, GTK_STOCK_EXECUTE);
+		gtk_action_group_add_action (data->action_group, action);
+
+		/* some additional information needed by the action when it is invoked */
+		action_data = g_new (ActionData, 1);
+		action_data->state = data;
+		action_data->app = app;
+		action_data->uri = uri;
+		g_object_set_data_full (G_OBJECT (action), "action_data", action_data, (GDestroyNotify) g_free);
+		g_signal_connect (G_OBJECT (action), "activate", G_CALLBACK (ephy_openwith_action), action_data);
+
+		/* add the new action's UI to the manager */
+		gtk_ui_manager_add_ui (data->manager, data->merge_id,
+				       "/EphyLinkPopup/OpenWithExtMenu",
+				       action_path, action_name,
+				       GTK_UI_MANAGER_MENUITEM, TRUE);
+
+		gtk_ui_manager_add_ui (data->manager, data->merge_id,
+				       "/EphyImageLinkPopup/OpenWithExtMenu",
+				       action_path, action_name,
+				       GTK_UI_MANAGER_MENUITEM, TRUE);
+
+		g_object_unref (action);
+		g_free (action_name);
+		g_free (action_path);
+	}
+
+	gtk_ui_manager_ensure_update (data->manager);
+}
+
+/**
+ * Adds a insensitive menu item to the menu. The text is the full description
+ * of the mime type.
+ *
+ * @param data reference to the window state data
+ * @param mime_type short mime type
+ */
+static void
+openwith_merge_ui_add_type (WindowData *data, const char *mime_type)
+{
+	LOG ("openwith_merge_ui_add_type");
+
+	const char *mime_desc;
+	char *action_name;
+	char *action_path;
+	GtkAction *action;
+
+
+	/* get the full text description of the type */
+	mime_desc = gnome_vfs_mime_get_description (mime_type);
+
+	/* make up some fake action names */
+	action_name = g_strdup_printf ("%s", mime_type);
+	action_path = g_strdup_printf ("OpenWithExtAction_%s", mime_type);
+
+	/* create an action for the type header */
+	action = gtk_action_new (mime_type, mime_desc, NULL, NULL);
+	gtk_action_group_add_action (data->action_group, action);
+	g_object_set (G_OBJECT (action), "sensitive", FALSE);
+
+	/* add the new action's UI to the manager */
+	gtk_ui_manager_add_ui (data->manager, data->merge_id,
+			       "/EphyLinkPopup/OpenWithExtMenu",
+			       action_path, action_name,
+			       GTK_UI_MANAGER_MENUITEM, TRUE);
+
+	gtk_ui_manager_add_ui (data->manager, data->merge_id,
+			       "/EphyImageLinkPopup/OpenWithExtMenu",
+			       action_path, action_name,
+			       GTK_UI_MANAGER_MENUITEM, TRUE);
+
+	g_object_unref (action);
+	g_free (action_name);
+	g_free (action_path);
+}
+
+/**
+ * Removes all of the entries from the short_apps that appear in right_apps.
+ * This has the end effect of moving the "certified" applications to the top of
+ * the list and the extension based ones to the bottom, without duplicates.
+ *
+ * This is a C newbie's lack of experience incarnate. I have no idea how to do
+ * this properly, so, here it is.
+ *
+ * @param data reference to our window state
+ */
+static void
+openwith_remove_short_apps (WindowData *data)
+{
+	LOG ("openwith_remove_short_apps");
+
+	GList *l, *m;
+	GList *dead = NULL;
+	GnomeVFSMimeApplication *lapp, *mapp;
+
+	for (l = data->right_apps; l; l = l->next) {
+		lapp = (GnomeVFSMimeApplication *) l->data;
+
+		for (m = data->short_apps; m; m = m->next) {
+			mapp = (GnomeVFSMimeApplication *) m->data;
+
+			if (g_str_equal (lapp->name, mapp->name)) {
+				dead = g_list_append (dead, mapp);
+			}
+		}
+	}
+
+	for (l = dead; l; l= l->next) {
+		data->short_apps = g_list_remove_all (data->short_apps, l->data);
+	}
+
+	g_list_free (dead);
+}
+
+/**
+ * Builds the new UI based on the two passed lists of applications. Each of the
+ * menu items opens the passed uri in the associated application. A seperator
+ * is rendered in between if neccassary.
+ *
+ * @param data reference to the data structure of the window
+ * @param uri reference to the uri to be opened
+ * @param short_apps applications based on file extension
+ * @param right_apps applications based on real data
+ */
+static void
+openwith_merge_ui (WindowData *data)
+{
+	LOG ("openwith_merge_ui");
+
+	/* revert the UI */
+	openwith_unmerge_ui (data);
+
+	openwith_remove_short_apps (data);
+
+	/* get a new merge id for a new set of ui changes */
+	data->merge_id = gtk_ui_manager_new_merge_id (data->manager);
+
+	/* our group of dynamic actions to be appended to the UI */
+	data->action_group = gtk_action_group_new ("EphyOpenWithExtDynaActions");
+	gtk_action_group_set_translation_domain (data->action_group, GETTEXT_PACKAGE);
+
+	gtk_ui_manager_insert_action_group (data->manager, data->action_group, 0);
+
+	/* if there are short apps, add them */
+	if (data->short_apps != NULL) {
+		openwith_merge_ui_add_apps (data, data->uri, data->short_apps);
+		openwith_merge_ui_add_type (data, data->short_mime_type);
+	}
+
+	/* if there are both short and right apps, add a seperator */
+	if (data->short_apps != NULL && data->right_apps != NULL) {
+		gtk_ui_manager_add_ui (data->manager, data->merge_id,
+				       "/EphyLinkPopup/OpenWithExtMenu",
+				       "OpenWithExtSep0", NULL,
+				       GTK_UI_MANAGER_SEPARATOR, TRUE);
+		gtk_ui_manager_add_ui (data->manager, data->merge_id,
+				       "/EphyImageLinkPopup/OpenWithExtMenu",
+				       "OpenWithExtSep0", NULL,
+				       GTK_UI_MANAGER_SEPARATOR, TRUE);
+	}
+
+	/* if there are right_apps, add them */
+	if (data->right_apps != NULL) {
+		openwith_merge_ui_add_apps (data, data->uri, data->right_apps);
+		openwith_merge_ui_add_type (data, data->right_mime_type);
+	}
+
+	gtk_ui_manager_ensure_update (data->manager);
+}
+
+/**
+ * Callback for the asynchronous GnomeVFS method to determine the real type of
+ * the link. Compiles an additional list of applications and remerges the UI.
+ *
+ * @param handle handle to the GnomeVFS API
+ * @param results GList<GnomeVFSGetFileInfoResult>
+ * @param data pointer to our window state data
+ */
+static void
+openwith_async_cb (GnomeVFSAsyncHandle *handle, GList *results, WindowData *data)
+{
+	LOG ("openwith_async_cb");
+
+	GnomeVFSGetFileInfoResult *result;
+
+	/* derived type information */
+	const char *right_mime_type;
+	GList *right_apps = NULL;
+
+	/* exit this routine if there were no results */
+	if (results == NULL) return;
+	if (data->async_op_handle == NULL) return;
+
+	/* get the results */
+	result = results->data;
+
+	/* extract the type */
+	right_mime_type = result->file_info->mime_type;
+
+	/* gather applications which support this mime type */
+	right_apps = gnome_vfs_mime_get_all_applications (result->file_info->mime_type);
+	if (right_apps == NULL) return;
+
+	/* add the new info to the state data and rebuild the UI */
+	data->right_mime_type = right_mime_type;
+	data->right_apps = right_apps;
+
+	openwith_merge_ui (data);
+}
+
+/**
+ * Called when the user exposes the context menu.
+ *
+ * @param embed the component the menu was shown on
+ * @param event specific information related to the event
+ */
+static void
+context_menu_cb (EphyEmbed *embed, EphyEmbedEvent *event)
+{
+	LOG ("context_menu_cb");
+
+	/* variables to important related objects */
+	EphyWindow *window;
+	WindowData *data;
+
+	/* derived from extension*/
+	const char *short_mime_type;
+	GList *short_apps = NULL;
+
+	/* information relating to the event */
+	EmbedEventContext event_context;
+
+	/* uri stuff */
+	const char *path;
+	GList *uris = NULL;
+
+	/* grab our important related objects */
+	window = EPHY_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (embed)));
+	data = g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY);
+
+	/* clean up the window state */
+	data->uri = NULL;
+	data->short_mime_type = NULL;
+	data->right_mime_type = NULL;
+	g_list_free (data->short_apps);
+	g_list_free (data->right_apps);
+	data->short_apps = NULL;
+	data->right_apps = NULL;
+
+	/* if the user has not clicked on a relavent link */
+	event_context = ephy_embed_event_get_context (event);
+	if (!(event_context & EMBED_CONTEXT_LINK))
+		return;
+
+	/* extract the link information */
+	uris = openwith_get_uri_list_from_event (event);
+	path = gnome_vfs_uri_get_path (uris->data);
+	data->uri = uris->data;
+
+	/* find the preliminary type based on the name */
+	short_mime_type = gnome_vfs_mime_type_from_name (path);
+	data->short_mime_type = short_mime_type;
+	if (short_mime_type != NULL) {
+		/* return applications which support this mime type */
+		short_apps = gnome_vfs_mime_get_all_applications (short_mime_type);
+		data->short_apps = short_apps;
+		openwith_merge_ui (data);
+	}
+
+	/* begin an async check on the mime type of the link */
+	gnome_vfs_async_get_file_info (
+		&data->async_op_handle,
+		uris,
+		GNOME_VFS_FILE_INFO_GET_MIME_TYPE,
+		GNOME_VFS_PRIORITY_DEFAULT,
+		(GnomeVFSAsyncGetFileInfoCallback)openwith_async_cb,
+		(gpointer) data
+	);
+}
+
+/**
+ * Called when Epiphany creates a new tab. We simply want to hook up to the
+ * context_menu signal.
+ *
+ * @param notebook a reference to the notebook containing the tab
+ * @param window a reference to the tab object
+ * @param extension 
+ */
+static void
+tab_added_cb (GtkWidget *notebook, EphyTab *tab, gpointer *user_data)
+{
+	LOG ("tab_added_cb");
+
+	EphyEmbed *embed;
+
+	embed = ephy_tab_get_embed (tab);
+	g_signal_connect (embed, "ge_context_menu", G_CALLBACK (context_menu_cb), NULL);
+}
+
+/**
+ * Called when Epiphany deletes a tab. We disconnect our signals.
+ *
+ * @param notebook a reference to the notebook containing the tab
+ * @param window a reference to the tab object
+ */
+static void
+tab_removed_cb (GtkWidget *notebook, EphyTab *tab, gpointer *user_data)
+{
+	LOG ("tab_removed_cb");
+
+	EphyEmbed *embed;
+
+	embed = ephy_tab_get_embed (tab);
+	g_signal_handlers_disconnect_by_func (embed, G_CALLBACK (context_menu_cb), NULL);
+}
+
+/**
+ * Called when Epiphany creates a new window. Creates our WindowData struct and
+ * initializes it's values. Merges the main portions of our UI into the
+ * UIManager.
+ *
+ * @param extension a reference to the extension object
+ * @param window a reference to the window to be attached to
+ */
+static void
+impl_attach_window (EphyExtension *extension, EphyWindow *window)
+{
+	LOG ("impl_attach_window");
+
+	WindowData *data;
+	GtkUIManager *manager;
+	GtkAction *action;
+	GtkActionGroup *action_group;
+	guint merge_id;
+
+	GtkWidget *notebook;
+
+
+	/* reference to the window UI manager */
+	manager = GTK_UI_MANAGER (window->ui_merge);
+
+	/* create a new merge_id for the main UI portions */
+	merge_id = gtk_ui_manager_new_merge_id (manager);
+	
+	/* our group of actions to be appended to the UI */
+	action_group = gtk_action_group_new ("EphyOpenWithExtActions");
+	gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
+
+	/* action associated with the Open With sub menu */
+	action = gtk_action_new ("OpenWithExtAction", N_("Open Link with"), NULL, NULL);
+	gtk_action_group_add_action (action_group, action);
+	g_object_unref (action);
+
+	/* EphyLinkPopup UI */
+	gtk_ui_manager_add_ui (manager, merge_id,
+			       "/EphyLinkPopup",
+			       "OpenWithExtMenu", "OpenWithExtAction",
+			       GTK_UI_MANAGER_MENU, TRUE);
+
+	/* EphyImageLinkPopup UI */
+	gtk_ui_manager_add_ui (manager, merge_id,
+			       "/EphyImageLinkPopup",
+			       "OpenWithExtMenu", "OpenWithExtAction",
+			       GTK_UI_MANAGER_MENU, TRUE);
+	
+	gtk_ui_manager_insert_action_group (manager, action_group, 0);
+	g_object_unref (action_group);
+
+	gtk_ui_manager_ensure_update (manager);
+
+	/* attach our special data struct to the window
+           we will use this to store extension information related to this
+           window */
+	data = g_new0 (WindowData, 1);
+	data->window = window;
+	data->manager = manager;
+	data->action_group = NULL;
+	data->merge_id = 0;
+	g_object_set_data_full (G_OBJECT (window), WINDOW_DATA_KEY, data, (GDestroyNotify) g_free);
+
+	/* attach events to the creation of a tab */
+	notebook = ephy_window_get_notebook (window);
+	g_signal_connect_after (notebook, "tab_added", G_CALLBACK (tab_added_cb), NULL);
+	g_signal_connect_after (notebook, "tab_removed", G_CALLBACK (tab_removed_cb), NULL);
+}
+
+/**
+ * Called when Epiphany destroys a window. Removes the signals we have
+ * registered with the window.
+ *
+ * @param extension a reference to the extension object
+ * @param window a reference to the window to be detached from
+ */
+static void
+impl_detach_window (EphyExtension *extension, EphyWindow *window)
+{
+	LOG ("impl_detach_window");
+
+	GtkWidget *notebook;
+
+	notebook = ephy_window_get_notebook (window);
+	g_signal_handlers_disconnect_by_func (notebook, G_CALLBACK (tab_added_cb), extension);
+	g_signal_handlers_disconnect_by_func (notebook, G_CALLBACK (tab_removed_cb), extension);
+}
+
+static void
+ephy_openwith_extension_iface_init (EphyExtensionIface *iface)
+{
+	LOG ("ephy_openwith_extension_iface_init");
+
+	iface->attach_window = impl_attach_window;
+	iface->detach_window = impl_detach_window;
+}
diff -urN epiphany-extensions/extensions/openwith/ephy-openwith-extension.h epiphany-extensions.new/extensions/openwith/ephy-openwith-extension.h
--- epiphany-extensions/extensions/openwith/ephy-openwith-extension.h	1969-12-31 18:00:00 -0600
+++ epiphany-extensions.new/extensions/openwith/ephy-openwith-extension.h	2004-10-11 13:06:09 -0500
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (C) 2004 Jerry Haltom
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  $Id$
+ */
+ 
+#ifndef EPHY_OPENWITH_EXTENSION_H
+#define EPHY_OPENWITH_EXTENSION_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_OPENWITH_EXTENSION		(ephy_openwith_extension_get_type ())
+#define EPHY_OPENWITH_EXTENSION(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_OPENWITH_EXTENSION, EphyOpenWithExtension))
+#define EPHY_OPENWITH_EXTENSION_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_OPENWITH_EXTENSION, EphyOpenWithExtensionClass))
+#define EPHY_IS_SAMPLE_EXTENSION(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_OPENWITH_EXTENSION))
+#define EPHY_IS_SAMPLE_EXTENSION_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_OPENWITH_EXTENSION))
+#define EPHY_OPENWITH_EXTENSION_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_OPENWITH_EXTENSION, EphyOpenWithExtensionClass))
+
+typedef struct EphyOpenWithExtension EphyOpenWithExtension;
+typedef struct EphyOpenWithExtensionClass EphyOpenWithExtensionClass;
+typedef struct EphyOpenWithExtensionPrivate EphyOpenWithExtensionPrivate;
+
+struct EphyOpenWithExtensionClass
+{
+	GObjectClass parent_class;
+};
+
+struct EphyOpenWithExtension
+{
+	GObject parent_instance;
+
+	EphyOpenWithExtensionPrivate *priv;
+};
+
+GType ephy_openwith_extension_get_type (void);
+GType ephy_openwith_extension_register_type (GTypeModule *module);
+
+G_END_DECLS
+
+#endif
diff -urN epiphany-extensions/extensions/openwith/Makefile.am epiphany-extensions.new/extensions/openwith/Makefile.am
--- epiphany-extensions/extensions/openwith/Makefile.am	1969-12-31 18:00:00 -0600
+++ epiphany-extensions.new/extensions/openwith/Makefile.am	2004-10-11 13:09:59 -0500
@@ -0,0 +1,19 @@
+extensiondir = $(libdir)/epiphany-1.4/extensions
+
+INCLUDES = \
+  -I$(top_srcdir)/include \
+  $(EPIPHANY_DEPENDENCY_CFLAGS) \
+  -DSHARE_DIR=\"pkgdatadir\" \
+  -DEPHY_EXTENSIONS_LOCALEDIR=\"$(datadir)/locale\" \
+  -DG_DISABLE_DEPRECATED \
+  -DGDK_DISABLE_DEPRECATED \
+  -DGTK_DISABLE_DEPRECATED \
+  -DGDK_PIXBUF_DISABLE_DEPRECATED \
+  -DGNOME_DISABLE_DEPRECATED
+
+extension_LTLIBRARIES = libopenwithextension.la
+
+libopenwithextension_la_SOURCES = \
+  ephy-openwith-extension.c \
+  ephy-openwith-extension.h \
+  openwith.c
diff -urN epiphany-extensions/extensions/openwith/openwith.c epiphany-extensions.new/extensions/openwith/openwith.c
--- epiphany-extensions/extensions/openwith/openwith.c	1969-12-31 18:00:00 -0600
+++ epiphany-extensions.new/extensions/openwith/openwith.c	2004-10-11 13:06:09 -0500
@@ -0,0 +1,33 @@
+/*
+ *  Copyright (C) 2004 Jerry Haltom
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  $Id$
+ */
+ 
+#include "ephy-openwith-extension.h"
+#include "ephy-debug.h"
+
+#include <gmodule.h>
+
+G_MODULE_EXPORT GType register_module (GTypeModule *module);
+
+G_MODULE_EXPORT GType
+register_module (GTypeModule *module)
+{
+	LOG ("Registering EphyOpenWithExtension")
+	return ephy_openwith_extension_register_type (module);
+}
