/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

/*
 * nautilus-view-dnd.c: DnD helpers for NautilusView
 *
 * Copyright (C) 1999, 2000  Free Software Foundaton
 * Copyright (C) 2000, 2001  Eazel, Inc.
 *
 * 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 of the
 * License, 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.
 *
 * Authors: Ettore Perazzoli
 * 	    Darin Adler <darin@bentspoon.com>
 * 	    John Sullivan <sullivan@eazel.com>
 *          Pavel Cisler <pavel@eazel.com>
 */

#include <config.h>

#include "nautilus-view-dnd.h"

#include "nautilus-desktop-icon-view.h"
#include "nautilus-view.h"

#include <eel/eel-stock-dialogs.h>
#include <eel/eel-string.h>

#include <libnautilus-private/nautilus-clipboard.h>
#include <libnautilus-private/nautilus-dnd.h>

#define GET_ANCESTOR(obj) \
	GTK_WINDOW (gtk_widget_get_ancestor (GTK_WIDGET (obj), GTK_TYPE_WINDOW))

static inline void
view_widget_to_file_operation_position (NautilusView *view,
                                        GdkPoint *position)
{
	NautilusViewClass *class = NAUTILUS_VIEW_GET_CLASS (view);

	if (class->widget_to_file_operation_position != NULL) {
		class->widget_to_file_operation_position (view, position);
	}
}

static void
view_widget_to_file_operation_position_xy (NautilusView *view,
                                           int *x, int *y)
{
	GdkPoint position;

	position.x = *x;
	position.y = *y;
	view_widget_to_file_operation_position (view, &position);
	*x = position.x;
	*y = position.y;
}


typedef struct {
	NautilusView  *view;
	GCancellable *cancellable;
	char *encoded_url;
	char *target_uri;
	int x;
	int y;
	guint timeout;
} NetscapeUrlDropAsk;

static GdkDragAction
ask_link_action (NautilusView *view)
{
	int button_pressed;
	GdkDragAction result;
	GtkWindow *parent_window;
	GtkWidget *dialog;

	parent_window = NULL;

	/* Don't use desktop window as parent, since that means
	   we show up an all desktops etc */
	if (! NAUTILUS_IS_DESKTOP_ICON_VIEW (view)) {
		parent_window = GTK_WINDOW (GET_ANCESTOR (view));
	}

	dialog = gtk_message_dialog_new (parent_window,
					 GTK_DIALOG_DESTROY_WITH_PARENT,
					 GTK_MESSAGE_QUESTION,
					 GTK_BUTTONS_NONE,
					 _("Download location?"));

	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
						  _("You can download it or make a link to it."));

	gtk_dialog_add_button (GTK_DIALOG (dialog),
			       _("Make a _Link"), 0);
	gtk_dialog_add_button (GTK_DIALOG (dialog),
			       GTK_STOCK_CANCEL, 1);
	gtk_dialog_add_button (GTK_DIALOG (dialog),
			       _("_Download"), 2);

	gtk_window_set_title (GTK_WINDOW (dialog), ""); /* as per HIG */
	gtk_window_set_focus_on_map (GTK_WINDOW (dialog), TRUE);
	gtk_dialog_set_default_response (GTK_DIALOG (dialog), 2);

	gtk_window_present (GTK_WINDOW (dialog));

	button_pressed = gtk_dialog_run (GTK_DIALOG (dialog));

	gtk_widget_destroy (dialog);

	switch (button_pressed) {
	case 0:
		result = GDK_ACTION_LINK;
		break;
	case 1:
	case GTK_RESPONSE_DELETE_EVENT:
		result = 0;
		break;
	case 2:
		result = GDK_ACTION_COPY;
		break;
	default:
		g_assert_not_reached ();
		result = 0;
	}

	return result;
}

static void
revert_slashes (char *string)
{
	while (*string != 0) {
		if (*string == '/') {
			*string = '\\';
		}
		string++;
	}
}

static void
handle_netscape_url_drop_ask_cb (GObject *source_object,
				 GAsyncResult *res,
				 gpointer user_data)
{
	NetscapeUrlDropAsk *data;
	GdkDragAction action;
	GFileInfo *info;
	GFile *f;
	const char *mime_type;

	data = user_data;
	f = G_FILE (source_object);

	info = g_file_query_info_finish (f, res, NULL);
	mime_type = NULL;

	if (info) {
		mime_type = g_file_info_get_content_type (info);
	}

	if (mime_type != NULL &&
	    (g_content_type_equals (mime_type, "text/html") ||
	     g_content_type_equals (mime_type, "text/xml")  ||
	     g_content_type_equals (mime_type, "application/xhtml+xml"))) {
		action = GDK_ACTION_LINK;
	} else if (mime_type != NULL &&
		   g_content_type_equals (mime_type, "text/plain")) {
		action = ask_link_action (data->view);
	} else {
		action = GDK_ACTION_COPY;
	}
	if (info) {
		g_object_unref (info);
	}
	
	if (action != 0) {
		nautilus_view_handle_netscape_url_drop (data->view,
                                                        data->encoded_url,
                                                        data->target_uri,
                                                        action,
                                                        data->x, data->y);
	}
	
	g_object_unref (data->view);
	g_object_unref (data->cancellable);
	if (data->timeout != 0) {
		g_source_remove (data->timeout);
	}
	g_free (data->encoded_url);
	g_free (data->target_uri);
	g_free (data);
}

static gboolean
handle_netscape_url_drop_timeout (gpointer user_data)
{
	NetscapeUrlDropAsk *data;

	data = user_data;

	g_cancellable_cancel (data->cancellable);
	data->timeout = 0;
	
	return FALSE;
}

void
nautilus_view_handle_netscape_url_drop (NautilusView  *view,
                                        const char    *encoded_url,
                                        const char    *target_uri,
                                        GdkDragAction  action,
                                        int            x,
                                        int            y)
{
	GdkPoint point;
	GdkScreen *screen;
	int screen_num;
	char *url, *title;
	char *link_name, *link_display_name;
	char *container_uri;
	GArray *points;
	char **bits;
	GList *uri_list = NULL;
	GFile *f;

	if (encoded_url == NULL) {
		return;
	}

	container_uri = NULL;
	if (target_uri == NULL) {
		container_uri = nautilus_view_get_backing_uri (view);
		g_assert (container_uri != NULL);
	}

	f = g_file_new_for_uri (target_uri != NULL ? target_uri : container_uri);
	if (!g_file_is_native (f)) {
		eel_show_warning_dialog (_("Drag and drop is not supported."),
					 _("Drag and drop is only supported on local file systems."),
                                         GET_ANCESTOR (view));
		g_object_unref (f);
		g_free (container_uri);
		return;
	}
	g_object_unref (f);

	/* _NETSCAPE_URL_ works like this: $URL\n$TITLE */
	bits = g_strsplit (encoded_url, "\n", 0);
	switch (g_strv_length (bits)) {
	case 0:
		g_strfreev (bits);
		g_free (container_uri);
		return;
	case 1:
		url = bits[0];
		title = NULL;
		break;
	default:
		url = bits[0];
		title = bits[1];
	}

	if (action == GDK_ACTION_ASK) {
		NetscapeUrlDropAsk *data;

		f = g_file_new_for_uri (url);
		data = g_new0 (NetscapeUrlDropAsk, 1);
		data->view = g_object_ref (view);
		data->cancellable = g_cancellable_new ();
		data->encoded_url = g_strdup (encoded_url);
		data->target_uri = g_strdup (target_uri);
		data->x = x;
		data->y = y;
		/* Ensure we wait at most 1 second for mimetype */
		data->timeout = g_timeout_add (1000,
					       handle_netscape_url_drop_timeout,
					       data);
		g_file_query_info_async (f,
					 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0,
					 0, data->cancellable,
					 handle_netscape_url_drop_ask_cb,
					 data);
		
		g_free (container_uri);
		return;
	}

	view_widget_to_file_operation_position_xy (view, &x, &y);

	/* We don't support GDK_ACTION_ASK or GDK_ACTION_PRIVATE
	 * and we don't support combinations either. */
	if ((action != GDK_ACTION_DEFAULT) &&
	    (action != GDK_ACTION_COPY) &&
	    (action != GDK_ACTION_MOVE) &&
	    (action != GDK_ACTION_LINK)) {
		eel_show_warning_dialog (_("Drag and drop is not supported."),
					 _("An invalid drag type was used."),
                                         GET_ANCESTOR (view));
		g_free (container_uri);
		return;
	}

	if (action == GDK_ACTION_LINK) {
		if (eel_str_is_empty (title)) {
			GFile *f;

			f = g_file_new_for_uri (url);
			link_name = g_file_get_basename (f);
			g_object_unref (f);
		} else {
			link_name = g_strdup (title);
		}
		
		if (!eel_str_is_empty (link_name)) {
			link_display_name = g_strdup_printf (_("Link to %s"), link_name);

			/* The filename can't contain slashes, strip em.
			   (the basename of http://foo/ is http://foo/) */
			revert_slashes (link_name);

			point.x = x;
			point.y = y;

			screen = gtk_widget_get_screen (GTK_WIDGET (view));
			screen_num = gdk_screen_get_number (screen);

			nautilus_link_local_create (target_uri != NULL ? target_uri : container_uri,
						    link_name,
						    link_display_name,
						    "gnome-fs-bookmark",
						    url,
						    &point,
						    screen_num,
						    TRUE);

			g_free (link_display_name);
		}
		g_free (link_name);
	} else {
		GdkPoint tmp_point = { 0, 0 };

		/* pass in a 1-item array of icon positions, relative to x, y */
		points = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
		g_array_append_val (points, tmp_point);

		uri_list = g_list_append (uri_list, url);

		nautilus_view_move_copy_items (view, uri_list, points,
                                               target_uri != NULL ? target_uri : container_uri,
                                               action, x, y);

		g_list_free (uri_list);
		g_array_free (points, TRUE);
	}

	g_strfreev (bits);
	g_free (container_uri);
}

void
nautilus_view_handle_uri_list_drop (NautilusView  *view,
                                    const char    *item_uris,
                                    const char    *target_uri,
                                    GdkDragAction  action,
                                    int            x,
                                    int            y)
{
	gchar **uri_list;
	GList *real_uri_list = NULL;
	char *container_uri;
	int n_uris, i;
	GArray *points;

	if (item_uris == NULL) {
		return;
	}

	container_uri = NULL;
	if (target_uri == NULL) {
		container_uri = nautilus_view_get_backing_uri (view);
		g_assert (container_uri != NULL);
	}

	if (action == GDK_ACTION_ASK) {
		action = nautilus_drag_drop_action_ask
			(GTK_WIDGET (view),
			 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
		if (action == 0) {
			g_free (container_uri);
			return;
		}
	}

	/* We don't support GDK_ACTION_ASK or GDK_ACTION_PRIVATE
	 * and we don't support combinations either. */
	if ((action != GDK_ACTION_DEFAULT) &&
	    (action != GDK_ACTION_COPY) &&
	    (action != GDK_ACTION_MOVE) &&
	    (action != GDK_ACTION_LINK)) {
		eel_show_warning_dialog (_("Drag and drop is not supported."),
					 _("An invalid drag type was used."),
                                         GET_ANCESTOR (view));
		g_free (container_uri);
		return;
	}

	n_uris = 0;
	uri_list = g_uri_list_extract_uris (item_uris);
	for (i = 0; uri_list[i] != NULL; i++) {
		real_uri_list = g_list_append (real_uri_list, uri_list[i]);
		n_uris++;
	}
	g_free (uri_list);

	/* do nothing if no real uris are left */
	if (n_uris == 0) {
		g_free (container_uri);
		return;
	}

	if (n_uris == 1) {
		GdkPoint tmp_point = { 0, 0 };

		/* pass in a 1-item array of icon positions, relative to x, y */
		points = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
		g_array_append_val (points, tmp_point);
	} else {
		points = NULL;
	}

	view_widget_to_file_operation_position_xy (view, &x, &y);

	nautilus_view_move_copy_items (view, real_uri_list, points,
				       target_uri != NULL ? target_uri : container_uri,
				       action, x, y);

	g_list_free_full (real_uri_list, g_free);

	if (points != NULL)
		g_array_free (points, TRUE);

	g_free (container_uri);
}

void
nautilus_view_handle_text_drop (NautilusView  *view,
                                const char    *text,
                                const char    *target_uri,
                                GdkDragAction  action,
                                int            x,
                                int            y)
{
	int length;
	char *container_uri;
	GdkPoint pos;

	if (text == NULL) {
		return;
	}

	g_return_if_fail (action == GDK_ACTION_COPY);

	container_uri = NULL;
	if (target_uri == NULL) {
		container_uri = nautilus_view_get_backing_uri (view);
		g_assert (container_uri != NULL);
	}

	length = strlen (text);

	pos.x = x;
	pos.y = y;
	view_widget_to_file_operation_position (view, &pos);

	nautilus_view_new_file_with_initial_contents (
		view, target_uri != NULL ? target_uri : container_uri,
		/* Translator: This is the filename used for when you dnd text to a directory */
		_("dropped text.txt"),
		text, length, &pos);

	g_free (container_uri);
}

void
nautilus_view_handle_raw_drop (NautilusView *view,
                               const char   *raw_data,
                               int           length,
                               const char   *target_uri,
                               const char   *direct_save_uri,
                               GdkDragAction action,
                               int           x,
                               int           y)
{
	char *container_uri, *filename;
	GFile *direct_save_full;
	GdkPoint pos;

	if (raw_data == NULL) {
		return;
	}

	g_return_if_fail (action == GDK_ACTION_COPY);

	container_uri = NULL;
	if (target_uri == NULL) {
		container_uri = nautilus_view_get_backing_uri (view);
		g_assert (container_uri != NULL);
	}

	pos.x = x;
	pos.y = y;
	view_widget_to_file_operation_position (view, &pos);

	filename = NULL;
	if (direct_save_uri != NULL) {
		direct_save_full = g_file_new_for_uri (direct_save_uri);
		filename = g_file_get_basename (direct_save_full);
	}
	if (filename == NULL) {
		/* Translator: This is the filename used for when you dnd raw
		 * data to a directory, if the source didn't supply a name.
		 */
		filename = g_strdup (_("dropped data"));
	}

	nautilus_view_new_file_with_initial_contents (
		view, target_uri != NULL ? target_uri : container_uri,
		filename, raw_data, length, &pos);

	g_free (container_uri);
	g_free (filename);
}

void 
nautilus_view_drop_proxy_received_uris (NautilusView *view,
					const GList *source_uri_list,
					const char *target_uri,
					GdkDragAction action)
{
	char *container_uri;

	container_uri = NULL;
	if (target_uri == NULL) {
		container_uri = nautilus_view_get_backing_uri (view);
		g_assert (container_uri != NULL);
	}

	if (action == GDK_ACTION_ASK) {
		action = nautilus_drag_drop_action_ask
			(GTK_WIDGET (view),
			 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
		if (action == 0) {
			return;
		}
	}

	nautilus_clipboard_clear_if_colliding_uris (GTK_WIDGET (view),
						    source_uri_list,
						    nautilus_view_get_copied_files_atom (view));

	nautilus_view_move_copy_items (view, source_uri_list, NULL,
				       target_uri != NULL ? target_uri : container_uri,
				       action, 0, 0);

	g_free (container_uri);
}
