/*
 *  Copyright (C) 2001 Philip Langdale
 *
 *  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.
 */

/*
 * The functioning of the download architecture, as described by Philip
 * on 28 May 2001 and updated on 28 June 2001:
 * 
 * When mozilla runs into a file it cannot render internally or that it
 * does not have a plugin for, it calls the
 * nsIExternalHelperAppService. This service will then either attempt to
 * save the file or run it with a helper app depending on what the
 * mozilla mime database returns.
 * 
 * nsIExternalHelperAppService then calls out to the nsIHelperAppDialog
 * interface which handles the UI for the service. This is the interface
 * which we have reimplemented. Therefore, with a major caveat, we have
 * put a GNOME/GTK frontend on an unmodified mozilla backend.
 * 
 * Now for the caveat. With respect to saving files to disk, the mozilla
 * backend works exactly the same as it does in
 * mozilla-the-browser. However, for dealing with helper apps, we do not
 * use the mozilla backend at all. This is because we want to use the
 * gnome-vfs database to retrieve helper-app info, rather than the
 * mozilla helper app database.
 * 
 * How it works:
 * 
 * a) The user clicks on a link or follows a redirect to a file of a type
 * that mozilla cannot handle. Mozilla passes the link to the
 * ExternalHelperAppService which in turn calls the Show() method of
 * nsIHelperAppDialog.
 * 
 * b) In our implementation of Show() we first compare the passed mime
 * type to galeon's mime list. If the mime type is in the list, we then
 * lookup the Action associated with the mime type. Currently, the
 * possible mime-actions are:
 * 
 * Save to Disk
 * Run with Helper App
 * Ask User
 * 
 * The default action is Ask User, and if the mime-type is not in our
 * list, this is what will be assumed.
 * 
 * c) If Ask User is the chosen action, a dialog will be shown to the
 * user allowing the user to choose from the other two possible actions
 * as well as a checkbox to let the user set the default action to the
 * chosen action for the future.
 * 
 * d-1) The "Save to Disk" action. We first check galeon preferences to
 * see if the user wants to use the built-in mozilla downloader, gtm or
 * a command-line executed downloader.
 *
 * d-2a) The built-in downloader.  This action is handled by the mozilla
 * backend. Our nsIHelperAppDialog does the same thing that the
 * mozilla-the-browser one does, which is to call the SaveToDisk() method
 * of nsIExternalHelperAppService. This in turn calls the
 * PromptForSaveToFile() method of nsIHelperAppDialog putting the ball
 * back in our court.
 * 
 * d-2b) Now, if galeon is configured to always ask for a download
 * directory, it will pop up a file selector so that the user can select
 * the directory and filename to save the file to.  Otherwise, it will
 * use galeon's default download directory and proceed without
 * interaction.
 * 
 * d-2c) When PromptForSaveToFile() returns, nsIExternalHelperAppService
 * will then call the ShowProgressDialog() method of
 * nsIHelperAppDialog. This progress dialog, obviously, tracks the
 * progress of the download. It is worth noting that mozilla starts the
 * actual download as soon as the user clicks on the link or follows the
 * redirect. While the user is deciding what action to take, the file is
 * downloading. Often, for small files, the file is already downloaded
 * when the user decides what directory to put it in. The progress dialog
 * does not appear in these cases. Also, we currently have a small
 * problem where our progress dialog times the download from the point
 * the dialog appears, not from the time the download starts. This is due
 * to the timestamp that is passed to us is just plain weird, and I
 * haven't worked out how to turn it into a useable time. The fact that
 * the download starts early means that the file is actually downloaded
 * to a temp file and only at the end is it moved to it's final location.
 * 
 * d-3a) The two external downloader options.  These options are
 * handled completely by galeon. The first thing that we do is call the
 * Cancel() method of nsIExternalHelperAppService to cancel the mozilla
 * download. We then pass the url to our own LaunchExternalDownloader()
 * method. This method will ask for a download directory as appropriate
 * as with the "Save to disk" action.
 * 
 * d-3b) Finally, depending on whether GTM or a command line handler was
 * selected in prefs, the external handler will be called with the url
 * passed and the directory selected.
 * 
 * e-1) The "Run with Helper App" action.  This action is currently only
 * working with a minimal implementation.  First, we explicitly call
 * ShowProgressDialog() so the user knows that the file is being
 * downloaded. We also need this so that we only run the helper after the
 * file is completely downloaded. The file will download to temp location
 * that it would be moved from if the action was "Save to Disk".  We have
 * to call ShowProgressDialog() ourselves because we are not using
 * mozilla's helper mechanism which would usually make the call for us.
 * 
 * e-2) If there is a default helper app in our mime database and alwaysAsk
 * is false, galeon will run the default helper automatically. Otherwise it
 * will pop up a helper chooser dialog which lists the helpers that gnome-vfs
 * knows about as well as providing a GnomeFileEntry to allow the user to
 * select and arbitrary application. The default value of the GnomeFileEntry
 * is the helper stored in our database if one exits.
 * 
 * f) General notes.  We cannot use this infrastructure to override
 * native mozilla types. mozilla will attempt to render these types and
 * never call out to us. We are at the end of the chain as the handler of
 * last resort, so native and plugin types will never reach us. This also
 * means that a file with an incorrect mime-type ( eg: .tar.bz2 marked as
 * text/plain ) will be incorrectly rendered by mozilla. We cannot help
 * this.
 * 
 * Despite the apparent user-side similarity with explicit downloads by
 * a shift-click or context-menu item, there is actually none at all.
 * Explicit downloads are handled by the nsIStreamTransfer manager which
 * we use as is. Currently the progress dialog for the stream transfer
 * manager is un-overridable, so it appears in XUL. This will change in
 * due course.
 * 
 * Matt would like the modifiy the progress dialog so each file currently
 * being downloaded becomes a clist entry in a master dialog rather than
 * causing a separate progress dialog. a lot of progress dialogs gets
 * really messy.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

extern "C" {
#include "libgnomevfs/gnome-vfs-mime-handlers.h"
}

#include "galeon-embed-shell.h"
#include "prefs-strings.h"
#include "eel-gconf-extensions.h"
#include "gul-download.h"
#include "gul-glade.h"
#include "gul-string.h"
#include "gul-general.h"
#include "gul-gui.h"
#include "galeon-embed-utils.h"
#include "ContentHandler.h"
#include "gul-file-chooser.h"

#include <gtk/gtkentry.h>
#include <gtk/gtkprogress.h>
#include <gtk/gtkliststore.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkimage.h>
#include <gtk/gtklabel.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtkexpander.h>
#include <libgnome/gnome-exec.h>
#include <glib/gi18n.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-util.h>
#include <libgnomevfs/gnome-vfs-mime.h>

#include "MozillaPrivate.h"
#include "ProgressListener.h"

#include "nsCRT.h"
#include "nsCOMPtr.h"
#include "nsISupportsArray.h"
#include "nsIServiceManager.h"
#include "nsWeakReference.h"
#include "nsIInterfaceRequestorUtils.h"

#include "nsString.h"
#include "nsIURI.h"
#include "nsIURL.h"
#include "nsIMIMEInfo.h"
#include "nsIChannel.h"
#include "nsIFTPChannel.h"
#include "nsILocalFile.h"
#include "nsIPrefService.h"
#include "nsIDOMWindow.h"
#include "nsIDOMWindowInternal.h"

class GContentHandler;
struct MimeAskActionDialog;
struct HelperAppChooserDialog;

extern "C"
void mime_ask_dialog_save_clicked_cb (GtkButton *button,
				      MimeAskActionDialog *dialog);
extern "C"
void mime_ask_dialog_open_clicked_cb (GtkButton *button,
				      MimeAskActionDialog *dialog);
extern "C"
gint mime_ask_dialog_cancel_clicked_cb (GtkButton *button,
					MimeAskActionDialog *dialog);

/*
 * MimeAskActionDialog: the representation of dialogs used to ask
 * about actions on MIME types
 */
struct MimeAskActionDialog
{
	MimeAskActionDialog(GContentHandler *aContentHandler,
			    GtkWidget *aParentWidget,
			    const char *aMimeType);
	~MimeAskActionDialog();

	GContentHandler *mContentHandler;
	GladeXML *mGXml;
	GtkWidget *mDialogWidget;
	GtkWidget *mParent;
	GtkWidget *mListView;

	GList *mApps;
	GtkListStore *mListStore;

	enum
	{
		APP_COLUMN,
		N_COLUMNS
	};

	// How many rows to show in the applications list
	static const int NUM_APPLICATIONS = 4; 
};

/* Implementation file */
NS_IMPL_ISUPPORTS1(GContentHandler, nsIHelperAppLauncherDialog)

GContentHandler::GContentHandler() : mUri(nsnull)
{
	/* member initializers and constructor code */
}

GContentHandler::~GContentHandler()
{
	/* destructor code */
}

////////////////////////////////////////////////////////////////////////////////
// begin nsIHelperAppLauncher impl
////////////////////////////////////////////////////////////////////////////////

/* void show (in nsIHelperAppLauncher aLauncher, in nsISupports aContext); */
NS_IMETHODIMP GContentHandler::Show(nsIHelperAppLauncher *aLauncher,
				    nsISupports *aContext
#if MOZILLA_SNAPSHOT > 9
, PRBool aForced)
#else
)
#endif
{
	/* aForced reflects if the content being sent is normally viewable
	 * in mozilla or not. That fact doesn't affect us, so ignore it
	 */
	nsresult rv;

	mLauncher = aLauncher;
	mContext = aContext;
	rv = Init ();
	
	MIMEAskAction ();

	return NS_OK;
}

/* nsILocalFile promptForSaveToFile (in nsISupports aWindowContext, in wstring aDefaultFile, in wstring aSuggestedFileExtension); */
NS_IMETHODIMP GContentHandler::PromptForSaveToFile(
#if MOZILLA_SNAPSHOT > 10
						   nsIHelperAppLauncher *aLauncher,
#endif
						   nsISupports *aWindowContext,
						   const PRUnichar *aDefaultFile,
						   const PRUnichar *aSuggestedFileExtension,
						   nsILocalFile **_retval)
{
	mContext = aWindowContext;

	nsCOMPtr<nsIDOMWindow> parentDOMWindow = do_GetInterface (aWindowContext);
	GtkWidget *parentWindow = GTK_WIDGET (MozillaFindGtkParent (parentDOMWindow));

	/* Get the directory to use for downloading into */
	if (!eel_gconf_get_boolean (CONF_DOWNLOADING_ASK_DIR))
	{
		nsresult rv =  BuildDownloadPath (NS_ConvertUCS2toUTF8 (aDefaultFile).get(),
						  parentWindow, _retval);
		if (NS_SUCCEEDED (rv)) return rv;
	}

	GulFileChooser *dialog;
	dialog = gul_file_chooser_new (_("Save"), parentWindow,
					GTK_FILE_CHOOSER_ACTION_SAVE,
					CONF_STATE_LAST_DOWNLOAD_DIR);
	gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog),
					   NS_ConvertUCS2toUTF8 (aDefaultFile).get());

	while (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
	{
		gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
		if (!filename)
		{
			continue;
		}

		if (gul_gui_confirm_overwrite_file (GTK_WIDGET (dialog),
						    filename) == FALSE)
		{
			g_free (filename);
			continue;
		}

		nsCOMPtr <nsILocalFile> destFile;

		NS_NewNativeLocalFile (nsDependentCString (filename),
				       PR_TRUE,
				       getter_AddRefs(destFile));
		g_free (filename);

		NS_ADDREF (*_retval = destFile);

		gtk_widget_destroy (GTK_WIDGET (dialog));

		return NS_OK;
	}
	gtk_widget_destroy (GTK_WIDGET (dialog));

	return NS_ERROR_FAILURE;
}

#if MOZILLA_SNAPSHOT <= 9
/* void showProgressDialog (in nsIHelperAppLauncher aLauncher, in nsISupports aContext); */
NS_METHOD GContentHandler::ShowProgressDialog(nsIHelperAppLauncher *aLauncher,
					      nsISupports *aContext)
{
	g_print ("GContentHandler::ShowProgressDialog is depreciated!\n");
	return NS_ERROR_NOT_IMPLEMENTED;
}
#endif

////////////////////////////////////////////////////////////////////////////////
// begin local public methods impl
////////////////////////////////////////////////////////////////////////////////

NS_METHOD GContentHandler::LaunchExternalDownloader (void)
{
	gul_download_external_save_url (mUrl.get());
	return NS_OK;
}


NS_METHOD GContentHandler::FindHelperApp (void)
{
	if (mUrlHelper)
	{
		return LaunchHelperApp ();
	}
	else
	{
		if (NS_SUCCEEDED(SynchroniseMIMEInfo()))
		{
			return mLauncher->LaunchWithApplication(nsnull, PR_FALSE);
		}
		else
		{
			return NS_ERROR_FAILURE;
		}
	}
}

NS_METHOD GContentHandler::LaunchHelperApp (void)
{
	if (mMimeType.Length())
	{
		nsresult rv;
		nsCOMPtr<nsIExternalHelperAppService> helperService =
			do_GetService (NS_EXTERNALHELPERAPPSERVICE_CONTRACTID);

		nsCOMPtr<nsPIExternalAppLauncher> appLauncher =
			do_QueryInterface (helperService, &rv);
		if (NS_SUCCEEDED(rv))
		{
			appLauncher->DeleteTemporaryFileOnExit(mTempFile);
		}

		nsString uFileName;
		mTempFile->GetPath(uFileName);
		const nsCString &aFileName = NS_ConvertUCS2toUTF8(uFileName);

		const nsCString &document = (mUrlHelper) ? mUrl : aFileName;

		char *param = g_strdup (document.get());
		gul_general_launch_application (mHelperApp->command,
						param,
						mHelperApp->requires_terminal);

		if(mUrlHelper) mLauncher->Cancel();

		g_free (param);
	}
	else
	{
		mLauncher->Cancel ();
	}

	return NS_OK;
}

NS_METHOD GContentHandler::GetLauncher (nsIHelperAppLauncher * *_retval)
{
	NS_IF_ADDREF (*_retval = mLauncher);
	return NS_OK;
}

NS_METHOD GContentHandler::GetContext (nsISupports * *_retval)
{
	NS_IF_ADDREF (*_retval = mContext);
	return NS_OK;
}

static gboolean 
application_support_scheme (GnomeVFSMimeApplication *app, const nsCString &aScheme)
{
	GList *l;

	g_return_val_if_fail (app != NULL, FALSE);
	g_return_val_if_fail (!aScheme.IsEmpty(), FALSE);
	
	if (app->expects_uris != GNOME_VFS_MIME_APPLICATION_ARGUMENT_TYPE_URIS)
		return FALSE;
	
	for (l = app->supported_uri_schemes; l != NULL; l = l->next)
	{
		char *uri_scheme = (char *)l->data;
		g_return_val_if_fail (uri_scheme != NULL, FALSE);
		if (aScheme.Equals(uri_scheme)) return TRUE;
	}

	return FALSE;
}

NS_METHOD GContentHandler::SetHelperApp(GnomeVFSMimeApplication *aHelperApp,
					PRBool alwaysUse)
{
	mHelperApp = aHelperApp;
	mUrlHelper = application_support_scheme (aHelperApp, mScheme);

	return NS_OK;
}

NS_METHOD GContentHandler::SynchroniseMIMEInfo (void)
{
	nsresult rv;
	nsCOMPtr<nsIMIMEInfo> mimeInfo;
	rv = mLauncher->GetMIMEInfo(getter_AddRefs(mimeInfo));
	if(NS_FAILED(rv)) return NS_ERROR_FAILURE;

	gchar *command = g_find_program_in_path (mHelperApp->command);
	if (command == NULL) return NS_ERROR_FAILURE;

	nsCOMPtr<nsILocalFile> helperFile;
	rv = NS_NewNativeLocalFile(nsDependentCString(command),
				   PR_TRUE,
				   getter_AddRefs(helperFile));
	g_free (command);
	if(NS_FAILED(rv)) return NS_ERROR_FAILURE;

	rv = mimeInfo->SetPreferredApplicationHandler(helperFile);
	if(NS_FAILED(rv)) return NS_ERROR_FAILURE;	

	nsMIMEInfoHandleAction mimeInfoAction;
	mimeInfoAction = nsIMIMEInfo::useHelperApp;

	if(mHelperApp->requires_terminal) //Information passing kludge!
	{
		rv = mimeInfo->SetApplicationDescription
#if MOZILLA_SNAPSHOT > 16
				(NS_LITERAL_STRING("runInTerminal"));
#else
				(NS_LITERAL_STRING("runInTerminal").get());
#endif
		if(NS_FAILED(rv)) return NS_ERROR_FAILURE;
	}

	rv = mimeInfo->SetPreferredAction(mimeInfoAction);
	if(NS_FAILED(rv)) return NS_ERROR_FAILURE;

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// begin local private methods impl
////////////////////////////////////////////////////////////////////////////////
NS_METHOD GContentHandler::Init (void)
{
	nsresult rv;

	nsCOMPtr<nsIMIMEInfo> MIMEInfo;
	rv = mLauncher->GetMIMEInfo (getter_AddRefs(MIMEInfo));
#if MOZILLA_SNAPSHOT > 16
	rv = MIMEInfo->GetMIMEType (mMimeType);
#else
	char *type = nsnull;
	rv = MIMEInfo->GetMIMEType (&type);
	mMimeType.Adopt (type);
#endif
	
#if MOZILLA_SNAPSHOT > 11
	rv = mLauncher->GetSource(getter_AddRefs(mUri));
	rv = mLauncher->GetTargetFile(getter_AddRefs(mTempFile));
#else
	rv = mLauncher->GetDownloadInfo(getter_AddRefs(mUri),
					&mTimeDownloadStarted,
					getter_AddRefs(mTempFile));
#endif

	rv = mUri->GetSpec (mUrl);
	rv = mUri->GetScheme (mScheme);

	ProcessMimeInfo ();

	return NS_OK;
}

NS_METHOD GContentHandler::ProcessMimeInfo (void)
{
	if (mMimeType.IsEmpty() ||
	    mMimeType.Equals("application/octet-stream"))
	{
		nsresult rv;
		nsCOMPtr<nsIURL> url = do_QueryInterface(mUri, &rv);
		if (NS_SUCCEEDED(rv) && url)
		{
			nsCAutoString uriFileName;
			url->GetFileName(uriFileName);
			mMimeType.Assign(gnome_vfs_mime_type_from_name
						(uriFileName.get()));
		}
		else
			mMimeType.Assign("application/octet-stream");
	}

	return NS_OK;
}

NS_METHOD GContentHandler::MIMEAskAction (void)
{
	nsCOMPtr<nsIDOMWindow> parent = do_GetInterface(mContext);
	GtkWidget *parentWidget = MozillaFindGtkParent(parent);

	new MimeAskActionDialog(this, parentWidget, mMimeType.get());

	return NS_OK;
}

////////////////////////////////////////////////////////////////////////////////
// begin MIMEAskActionDialog methods.
////////////////////////////////////////////////////////////////////////////////

MimeAskActionDialog::MimeAskActionDialog(GContentHandler *aContentHandler,
					 GtkWidget *aParentWidget,
					 const char *aMimeType) :
					 mContentHandler(aContentHandler),
					 mParent(aParentWidget)
{
	GtkWidget *label;
	char *markup;

	mApps = gnome_vfs_mime_get_all_applications(aMimeType);
	if (mApps)
	{
		mGXml = gul_glade_widget_new("galeon.glade",
					     "mime_ask_open_dialog", 
					     &mDialogWidget, this);

		mListView = glade_xml_get_widget(mGXml, "mime_ask_dialog_helper_list_view");

		GnomeVFSMimeApplication *defaultApp =
			gnome_vfs_mime_get_default_application(aMimeType);

		char *title;
		if (defaultApp)
		{
			title = g_strdup_printf (_("Open this file with \"%s\"?"), defaultApp->name);
		}
		else
		{
			// can this actually happen?
			title = g_strdup (_("Open this file with another application?"));
		}
		markup = g_strdup_printf ("<span size=\"larger\" weight=\"bold\">%s</span>", title);
		g_free (title);

		label = glade_xml_get_widget(mGXml, "mime_ask_action_title");
		gtk_label_set_markup (GTK_LABEL(label), markup);
		g_free (markup);


		mListStore = gtk_list_store_new(N_COLUMNS,
						G_TYPE_STRING);
		gtk_tree_view_set_model(GTK_TREE_VIEW(mListView),
					GTK_TREE_MODEL(mListStore));

		GtkCellRenderer *renderer;
		renderer = gtk_cell_renderer_text_new();

		GtkTreeViewColumn *column;
		column = gtk_tree_view_column_new_with_attributes("Application",
						renderer,
						"text", APP_COLUMN,
						NULL);
		gtk_tree_view_append_column(GTK_TREE_VIEW(mListView), column);

		GtkTreeSelection *selection =
				gtk_tree_view_get_selection(GTK_TREE_VIEW(mListView));
		gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);

		guint index;
		for (GList *i = mApps ; i ; i = i->next, index++)
		{
			GnomeVFSMimeApplication *app =
				static_cast<GnomeVFSMimeApplication *>(i->data);

			GtkTreeIter iter;
			gtk_list_store_append (mListStore, &iter);
			gtk_list_store_set (mListStore, &iter,
					    APP_COLUMN, app->name,
					    -1);

			if (defaultApp &&
			    !g_ascii_strcasecmp(app->name, defaultApp->name))
			{
				gtk_tree_selection_select_iter(selection, &iter);
			}
		}

		// Calculate the correct width for the list view for showing
		// NUM_APPLICATION rows.
		gint width, height, ypad;
		gtk_cell_renderer_get_size (renderer, GTK_WIDGET (mListView),
				            NULL, NULL, NULL, &width, &height);
		g_object_get (renderer, "ypad", &ypad, NULL);
		height = NUM_APPLICATIONS*(height + ypad) + 2*ypad;
		gtk_widget_set_size_request (GTK_WIDGET (mListView), 0, height);
	}
	else
	{
		mGXml = gul_glade_widget_new("galeon.glade",
					     "mime_ask_save_dialog", 
					     &mDialogWidget, this);
		GtkWidget *w;
		w = glade_xml_get_widget(mGXml, "mime_ask_dialog_save");
		gtk_widget_grab_default (w);
	}

	GtkWidget *mimeIcon = glade_xml_get_widget(mGXml,
						   "mime_ask_action_icon");
	gul_gui_image_set_from_mime_type (mimeIcon, 
			                  aMimeType, GTK_ICON_SIZE_DIALOG);


	nsCOMPtr<nsIHelperAppLauncher> launcher;
	(void)mContentHandler->GetLauncher(getter_AddRefs(launcher));

	// suggested filename
#if MOZILLA_SNAPSHOT <= 16
	PRUnichar *filename = nsnull;
	launcher->GetSuggestedFileName(&filename);
	markup = g_strdup_printf ("<b>%s</b>", NS_ConvertUCS2toUTF8(filename).get());
	nsMemory::Free(filename);
#else
        nsAutoString filename;
        launcher->GetSuggestedFileName (filename);
	markup = g_strdup_printf ("<b>%s</b>", NS_ConvertUCS2toUTF8(filename).get());
#endif

	label = glade_xml_get_widget(mGXml, "mime_ask_action_name");
	gtk_label_set_markup(GTK_LABEL(label), markup);
	g_free (markup);

	// MIME type description
	const char *description = gnome_vfs_mime_get_description(aMimeType);
	if (!description) description = aMimeType;
	
	label = glade_xml_get_widget(mGXml, "mime_ask_action_description");
	gtk_label_set_text(GTK_LABEL(label), description);

	// source
	nsCOMPtr<nsIURI> source;
	launcher->GetSource(getter_AddRefs(source));

	nsCAutoString spec;
	source->Resolve(NS_LITERAL_CSTRING("."), spec);

	markup = g_strdup_printf ("<i>%s</i>", PromiseFlatCString(spec).get());
	label = glade_xml_get_widget(mGXml, "mime_ask_action_source");
	gtk_label_set_markup(GTK_LABEL(label), markup);
	g_free (markup);

	gtk_window_set_transient_for(GTK_WINDOW(mDialogWidget), 
				     GTK_WINDOW(aParentWidget));

	gtk_widget_show(mDialogWidget);
}

MimeAskActionDialog::~MimeAskActionDialog()
{
	if(mApps)
		gnome_vfs_mime_application_list_free(mApps);

	gtk_widget_destroy(mDialogWidget);
	g_object_unref(G_OBJECT(mGXml));
}

////////////////////////////////////////////////////////////////////////////////
// begin MIMEAskActionDialog callbacks.
////////////////////////////////////////////////////////////////////////////////

extern "C" void
mime_ask_dialog_save_clicked_cb(GtkButton *button, MimeAskActionDialog *dialog)
{
	gtk_widget_hide(dialog->mDialogWidget);

	nsresult rv;
	nsCOMPtr<nsIHelperAppLauncher> launcher;
	rv = dialog->mContentHandler->GetLauncher(getter_AddRefs(launcher));
	
 	if (eel_gconf_get_boolean(CONF_DOWNLOADING_EXTERNAL_DOWNLOADER)
 	    == DOWNLOADER_BUILTIN)
 	{
		launcher->SaveToDisk(nsnull,PR_FALSE);
	}
	else
	{
		launcher->Cancel();
		dialog->mContentHandler->LaunchExternalDownloader();
	}
	
	delete dialog;
}

static void
mime_ask_dialog_download_cancel (MimeAskActionDialog *dialog)
{
	nsresult rv;
	nsCOMPtr<nsIHelperAppLauncher> launcher;
	rv = dialog->mContentHandler->GetLauncher(getter_AddRefs(launcher));

	launcher->Cancel();

	delete dialog;
}

extern "C" void
mime_ask_dialog_open_clicked_cb (GtkButton *button, MimeAskActionDialog *dialog)
{
	nsresult rv;
	nsCOMPtr<nsIHelperAppLauncher> launcher;
	rv = dialog->mContentHandler->GetLauncher (getter_AddRefs(launcher));

	GnomeVFSMimeApplication *app(NULL);
	if (dialog->mApps)
	{
		GtkTreeSelection *selection =
			gtk_tree_view_get_selection(GTK_TREE_VIEW(dialog->mListView));

		GtkTreeModel *model;
		GtkTreeIter iter;
		if (gtk_tree_selection_get_selected (selection, &model, &iter))
		{
			GtkTreePath *path = gtk_tree_model_get_path (model, &iter);
			gint selected = atoi (gtk_tree_path_to_string (path));
			gtk_tree_path_free (path);

			app = static_cast<GnomeVFSMimeApplication *>
				(g_list_nth_data (dialog->mApps, selected));
		}
	}

	if (app)
	{
		dialog->mContentHandler->SetHelperApp(app, FALSE);
		dialog->mContentHandler->FindHelperApp();
		delete dialog;
	}
	else
	{	
		mime_ask_dialog_download_cancel(dialog);
		galeon_embed_utils_nohandler_dialog_run(dialog->mParent);
	}
}

extern "C" gint
mime_ask_dialog_cancel_clicked_cb(GtkButton *button,
				   MimeAskActionDialog *dialog)
{
	mime_ask_dialog_download_cancel(dialog);
	return 0; /* FIXME: philipl, is this the right thing to return? */
}

extern "C" void
mime_ask_dialog_toggle_helpers_toggled_cb(GtkExpander *expander,
					  GParamSpec *pspec,
					  MimeAskActionDialog *dialog)
{
	gboolean val = gtk_expander_get_expanded (expander);
	gtk_window_set_resizable (GTK_WINDOW(dialog->mDialogWidget), val);
}

extern "C" void
mime_ask_dialog_close_cb(GtkWidget *dialogWidget, MimeAskActionDialog *dialog)
{
	nsresult rv;
	nsCOMPtr<nsIHelperAppLauncher> launcher;
	rv = dialog->mContentHandler->GetLauncher(getter_AddRefs(launcher));

	launcher->Cancel();
}

