/*
 * nautilus-window-slot-dnd.c - Handle DnD for widgets acting as
 * NautilusWindowSlot proxies
 *
 * Copyright (C) 2000, 2001 Eazel, Inc.
 * Copyright (C) 2010, Red Hat, Inc.
 *
 * The Gnome Library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *
 * The Gnome Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with the Gnome Library; see the file COPYING.LIB.  If not,
 * see <http://www.gnu.org/licenses/>.
 *
 * Authors: Pavel Cisler <pavel@eazel.com>,
 * 	    Ettore Perazzoli <ettore@gnu.org>
 */

#include <config.h>

#include "nautilus-notebook.h"
#include "nautilus-application.h"
#include "nautilus-files-view-dnd.h"
#include "nautilus-window-slot-dnd.h"

typedef struct {
  gboolean have_data;
  gboolean have_valid_data;

  gboolean drop_occured;

  unsigned int info;
  union {
    GList *selection_list;
    GList *uri_list;
    char *netscape_url;
    GtkSelectionData *selection_data;
  } data;

  NautilusFile *target_file;
  NautilusWindowSlot *target_slot;
  GtkWidget *widget;

  gboolean is_notebook;
  guint switch_location_timer;
} NautilusDragSlotProxyInfo;

static void
switch_tab (NautilusDragSlotProxyInfo *drag_info)
{
  GtkWidget *notebook, *slot;
  gint idx, n_pages;

  if (drag_info->target_slot == NULL) {
    return;
  }

  notebook = gtk_widget_get_ancestor (GTK_WIDGET (drag_info->target_slot), NAUTILUS_TYPE_NOTEBOOK);
  n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));

  for (idx = 0; idx < n_pages; idx++)
    {
      slot = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), idx);
      if (NAUTILUS_WINDOW_SLOT (slot) == drag_info->target_slot)
        {
          gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), idx);
          break;
        }
    }
}

static void
switch_location (NautilusDragSlotProxyInfo *drag_info)
{
  GFile *location;
  GtkWidget *window;

  if (drag_info->target_file == NULL) {
    return;
  }

  window = gtk_widget_get_toplevel (drag_info->widget);
  g_assert (NAUTILUS_IS_WINDOW (window));

  location = nautilus_file_get_location (drag_info->target_file);
  nautilus_application_open_location_full (NAUTILUS_APPLICATION (g_application_get_default ()),
                                           location, NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE,
                                           NULL, NAUTILUS_WINDOW (window), NULL);
  g_object_unref (location);
}

static gboolean
slot_proxy_switch_location_timer (gpointer user_data)
{
  NautilusDragSlotProxyInfo *drag_info = user_data;

  drag_info->switch_location_timer = 0;

  if (drag_info->is_notebook)
    switch_tab (drag_info);
  else
    switch_location (drag_info);

  return FALSE;
}

static void
slot_proxy_check_switch_location_timer (NautilusDragSlotProxyInfo *drag_info,
                                        GtkWidget *widget)
{
  GtkSettings *settings;
  guint timeout;

  if (drag_info->switch_location_timer)
    return;

  settings = gtk_widget_get_settings (widget);
  g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);

  drag_info->switch_location_timer =
    gdk_threads_add_timeout (timeout,
                             slot_proxy_switch_location_timer,
                             drag_info);
}

static void
slot_proxy_remove_switch_location_timer (NautilusDragSlotProxyInfo *drag_info)
{
  if (drag_info->switch_location_timer != 0)
    {
      g_source_remove (drag_info->switch_location_timer);
      drag_info->switch_location_timer = 0;
    }
}

static gboolean
slot_proxy_drag_motion (GtkWidget          *widget,
			GdkDragContext     *context,
			int                 x,
			int                 y,
			unsigned int        time,
			gpointer            user_data)
{
  NautilusDragSlotProxyInfo *drag_info;
  NautilusWindowSlot *target_slot;
  GtkWidget *window;
  GdkAtom target;
  int action;
  char *target_uri;
  GFile *location;
  gboolean valid_text_drag;
  gboolean valid_xds_drag;

  drag_info = user_data;

  action = 0;
  valid_text_drag = FALSE;
  valid_xds_drag = FALSE;

  if (gtk_drag_get_source_widget (context) == widget) {
    goto out;
  }

  window = gtk_widget_get_toplevel (widget);
  g_assert (NAUTILUS_IS_WINDOW (window));

  if (!drag_info->have_data) {
    target = gtk_drag_dest_find_target (widget, context, NULL);

    if (target == GDK_NONE) {
      goto out;
    }

    gtk_drag_get_data (widget, context, target, time);
  }

  target_uri = NULL;
  if (drag_info->target_file != NULL) {
    target_uri = nautilus_file_get_uri (drag_info->target_file);
  } else {
    if (drag_info->target_slot != NULL) {
      target_slot = drag_info->target_slot;
    } else {
      target_slot = nautilus_window_get_active_slot (NAUTILUS_WINDOW (window));
    }

    if (target_slot != NULL) {
        location = nautilus_window_slot_get_location (target_slot);
        target_uri = g_file_get_uri (location);
    }
  }

  if (target_uri != NULL) {
    NautilusFile *file;
    gboolean can;
    file = nautilus_file_get_existing_by_uri (target_uri);
    can = nautilus_file_can_write (file);
    g_object_unref (file);
    if (!can) {
      action = 0;
      goto out;
    }
  }

  if (drag_info->have_data &&
      drag_info->have_valid_data) {
    if (drag_info->info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) {
      nautilus_drag_default_drop_action_for_icons (context, target_uri,
                                                   drag_info->data.selection_list,
                                                   &action);
    } else if (drag_info->info == NAUTILUS_ICON_DND_URI_LIST) {
      action = nautilus_drag_default_drop_action_for_uri_list (context, target_uri);
    } else if (drag_info->info == NAUTILUS_ICON_DND_NETSCAPE_URL) {
      action = nautilus_drag_default_drop_action_for_netscape_url (context);
    } else if (drag_info->info == NAUTILUS_ICON_DND_TEXT) {
      valid_text_drag = TRUE;
    } else if (drag_info->info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE ||
               drag_info->info == NAUTILUS_ICON_DND_RAW) {
      valid_xds_drag = TRUE;
    }
  }

  g_free (target_uri);

 out:
  if (action != 0 || valid_text_drag || valid_xds_drag) {
    gtk_drag_highlight (widget);
    slot_proxy_check_switch_location_timer (drag_info, widget);
  } else {
    gtk_drag_unhighlight (widget);
    slot_proxy_remove_switch_location_timer (drag_info);
  }

  gdk_drag_status (context, action, time);

  return TRUE;
}

static void
drag_info_free (gpointer user_data)
{
  NautilusDragSlotProxyInfo *drag_info = user_data;

  g_clear_object (&drag_info->target_file);
  g_clear_object (&drag_info->target_slot);

  g_slice_free (NautilusDragSlotProxyInfo, drag_info);
}

static void
drag_info_clear (NautilusDragSlotProxyInfo *drag_info)
{
  slot_proxy_remove_switch_location_timer (drag_info);

  if (!drag_info->have_data) {
    goto out;
  }

  if (drag_info->info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) {
    nautilus_drag_destroy_selection_list (drag_info->data.selection_list);
  } else if (drag_info->info == NAUTILUS_ICON_DND_URI_LIST) {
    g_list_free (drag_info->data.uri_list);
  } else if (drag_info->info == NAUTILUS_ICON_DND_NETSCAPE_URL) {
    g_free (drag_info->data.netscape_url);
  } else if (drag_info->info == NAUTILUS_ICON_DND_TEXT ||
             drag_info->info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE ||
             drag_info->info == NAUTILUS_ICON_DND_RAW) {
    if (drag_info->data.selection_data != NULL) {
      gtk_selection_data_free (drag_info->data.selection_data);
    }
  }

 out:
  drag_info->have_data = FALSE;
  drag_info->have_valid_data = FALSE;

  drag_info->drop_occured = FALSE;
}

static void
slot_proxy_drag_leave (GtkWidget          *widget,
		       GdkDragContext     *context,
		       unsigned int        time,
		       gpointer            user_data)
{
  NautilusDragSlotProxyInfo *drag_info;

  drag_info = user_data;

  gtk_drag_unhighlight (widget);
  drag_info_clear (drag_info);
}

static gboolean
slot_proxy_drag_drop (GtkWidget          *widget,
		      GdkDragContext     *context,
		      int                 x,
		      int                 y,
		      unsigned int        time,
		      gpointer            user_data)
{
  GdkAtom target;
  NautilusDragSlotProxyInfo *drag_info;

  drag_info = user_data;
  g_assert (!drag_info->have_data);

  drag_info->drop_occured = TRUE;

  target = gtk_drag_dest_find_target (widget, context, NULL);
  gtk_drag_get_data (widget, context, target, time);

  return TRUE;
}


static void
slot_proxy_handle_drop (GtkWidget                *widget,
			GdkDragContext           *context,
			unsigned int              time,
			NautilusDragSlotProxyInfo *drag_info)
{
  GtkWidget *window;
  NautilusWindowSlot *target_slot;
  NautilusFilesView *target_view;
  char *target_uri;
  GList *uri_list;
  GFile *location;

  if (!drag_info->have_data ||
      !drag_info->have_valid_data) {
    gtk_drag_finish (context, FALSE, FALSE, time);
    drag_info_clear (drag_info);
    return;
  }

  window = gtk_widget_get_toplevel (widget);
  g_assert (NAUTILUS_IS_WINDOW (window));

  if (drag_info->target_slot != NULL) {
    target_slot = drag_info->target_slot;
  } else {
    target_slot = nautilus_window_get_active_slot (NAUTILUS_WINDOW (window));
  }

  target_uri = NULL;
  if (drag_info->target_file != NULL) {
    target_uri = nautilus_file_get_uri (drag_info->target_file);
  } else if (target_slot != NULL) {
    location = nautilus_window_slot_get_location (target_slot);
    target_uri = g_file_get_uri (location);
  }

  target_view = NULL;
  if (target_slot != NULL) {
        NautilusView *view;

        view = nautilus_window_slot_get_current_view (target_slot);

        if (view && NAUTILUS_IS_FILES_VIEW (view)) {
                target_view = NAUTILUS_FILES_VIEW (view);
        }
  }

  if (target_slot != NULL && target_view != NULL) {
    if (drag_info->info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) {
      uri_list = nautilus_drag_uri_list_from_selection_list (drag_info->data.selection_list);
      g_assert (uri_list != NULL);

      nautilus_files_view_drop_proxy_received_uris (target_view,
                                              uri_list,
                                              target_uri,
                                              gdk_drag_context_get_selected_action (context));
      g_list_free_full (uri_list, g_free);
    } else if (drag_info->info == NAUTILUS_ICON_DND_URI_LIST) {
      nautilus_files_view_drop_proxy_received_uris (target_view,
                                              drag_info->data.uri_list,
                                              target_uri,
                                              gdk_drag_context_get_selected_action (context));
    } if (drag_info->info == NAUTILUS_ICON_DND_NETSCAPE_URL) {
      nautilus_files_view_handle_netscape_url_drop (target_view,
                                              drag_info->data.netscape_url,
                                              target_uri,
                                              gdk_drag_context_get_selected_action (context),
                                              0, 0);
    }


    gtk_drag_finish (context, TRUE, FALSE, time);
  } else {
    gtk_drag_finish (context, FALSE, FALSE, time);
  }

  g_free (target_uri);

  drag_info_clear (drag_info);
}

static void
slot_proxy_drag_data_received (GtkWidget          *widget,
			       GdkDragContext     *context,
			       int                 x,
			       int                 y,
			       GtkSelectionData   *data,
			       unsigned int        info,
			       unsigned int        time,
			       gpointer            user_data)
{
  NautilusDragSlotProxyInfo *drag_info;
  char **uris;

  drag_info = user_data;

  g_assert (!drag_info->have_data);

  drag_info->have_data = TRUE;
  drag_info->info = info;

  if (gtk_selection_data_get_length (data) < 0) {
    drag_info->have_valid_data = FALSE;
    return;
  }

  if (info == NAUTILUS_ICON_DND_GNOME_ICON_LIST) {
    drag_info->data.selection_list = nautilus_drag_build_selection_list (data);

    drag_info->have_valid_data = drag_info->data.selection_list != NULL;
  } else if (info == NAUTILUS_ICON_DND_URI_LIST) {
    uris = gtk_selection_data_get_uris (data);
    drag_info->data.uri_list = nautilus_drag_uri_list_from_array ((const char **) uris);
    g_strfreev (uris);

    drag_info->have_valid_data = drag_info->data.uri_list != NULL;
  } else if (info == NAUTILUS_ICON_DND_NETSCAPE_URL) {
    drag_info->data.netscape_url = g_strdup ((char *) gtk_selection_data_get_data (data));

    drag_info->have_valid_data = drag_info->data.netscape_url != NULL;
  } else if (info == NAUTILUS_ICON_DND_TEXT ||
             info == NAUTILUS_ICON_DND_XDNDDIRECTSAVE ||
             info == NAUTILUS_ICON_DND_RAW) {
    drag_info->data.selection_data = gtk_selection_data_copy (data);
    drag_info->have_valid_data = drag_info->data.selection_data != NULL;
  }

  if (drag_info->drop_occured) {
    slot_proxy_handle_drop (widget, context, time, drag_info);
  }
}

void
nautilus_drag_slot_proxy_init (GtkWidget *widget,
                               NautilusFile *target_file,
                               NautilusWindowSlot *target_slot)
{
  NautilusDragSlotProxyInfo *drag_info;

  const GtkTargetEntry targets[] = {
    { NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
    { NAUTILUS_ICON_DND_NETSCAPE_URL_TYPE, 0, NAUTILUS_ICON_DND_NETSCAPE_URL },
    { NAUTILUS_ICON_DND_XDNDDIRECTSAVE_TYPE, 0, NAUTILUS_ICON_DND_XDNDDIRECTSAVE }, /* XDS Protocol Type */
    { NAUTILUS_ICON_DND_RAW_TYPE, 0, NAUTILUS_ICON_DND_RAW }
  };
  GtkTargetList *target_list;

  g_assert (GTK_IS_WIDGET (widget));

  drag_info = g_slice_new0 (NautilusDragSlotProxyInfo);

  g_object_set_data_full (G_OBJECT (widget), "drag-slot-proxy-data", drag_info,
                          drag_info_free);

  drag_info->is_notebook = (g_object_get_data (G_OBJECT (widget), "nautilus-notebook-tab") != NULL);

  if (target_file != NULL)
    drag_info->target_file = g_object_ref (target_file);

  if (target_slot != NULL)
    drag_info->target_slot = g_object_ref (target_slot);

  drag_info->widget = widget;

  gtk_drag_dest_set (widget, 0,
                     NULL, 0,
                     GDK_ACTION_MOVE |
                     GDK_ACTION_COPY |
                     GDK_ACTION_LINK |
                     GDK_ACTION_ASK);

  target_list = gtk_target_list_new (targets, G_N_ELEMENTS (targets));
  gtk_target_list_add_uri_targets (target_list, NAUTILUS_ICON_DND_URI_LIST);
  gtk_target_list_add_text_targets (target_list, NAUTILUS_ICON_DND_TEXT);
  gtk_drag_dest_set_target_list (widget, target_list);
  gtk_target_list_unref (target_list);

  g_signal_connect (widget, "drag-motion",
                    G_CALLBACK (slot_proxy_drag_motion),
                    drag_info);
  g_signal_connect (widget, "drag-drop",
                    G_CALLBACK (slot_proxy_drag_drop),
                    drag_info);
  g_signal_connect (widget, "drag-data-received",
                    G_CALLBACK (slot_proxy_drag_data_received),
                    drag_info);
  g_signal_connect (widget, "drag-leave",
                    G_CALLBACK (slot_proxy_drag_leave),
                    drag_info);
}
