summaryrefslogtreecommitdiffstats
path: root/chrome/browser/gtk/tab_contents_drag_source.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/gtk/tab_contents_drag_source.cc')
-rw-r--r--chrome/browser/gtk/tab_contents_drag_source.cc216
1 files changed, 216 insertions, 0 deletions
diff --git a/chrome/browser/gtk/tab_contents_drag_source.cc b/chrome/browser/gtk/tab_contents_drag_source.cc
new file mode 100644
index 0000000..639795b
--- /dev/null
+++ b/chrome/browser/gtk/tab_contents_drag_source.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/gtk/tab_contents_drag_source.h"
+
+#include "base/mime_util.h"
+#include "chrome/browser/gtk/gtk_dnd_util.h"
+#include "chrome/browser/renderer_host/render_view_host_delegate.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/browser/tab_contents/tab_contents_view.h"
+#include "chrome/common/gtk_util.h"
+#include "webkit/glue/webdropdata.h"
+
+TabContentsDragSource::TabContentsDragSource(
+ TabContentsView* tab_contents_view)
+ : tab_contents_view_(tab_contents_view),
+ drag_failed_(false),
+ drag_widget_(NULL) {
+ drag_widget_ = gtk_invisible_new();
+ g_signal_connect(drag_widget_, "drag-failed",
+ G_CALLBACK(OnDragFailedThunk), this);
+ g_signal_connect(drag_widget_, "drag-end", G_CALLBACK(OnDragEndThunk), this);
+ g_signal_connect(drag_widget_, "drag-data-get",
+ G_CALLBACK(OnDragDataGetThunk), this);
+ g_object_ref_sink(drag_widget_);
+}
+
+TabContentsDragSource::~TabContentsDragSource() {
+ g_signal_handlers_disconnect_by_func(drag_widget_,
+ reinterpret_cast<gpointer>(OnDragFailedThunk), this);
+ g_signal_handlers_disconnect_by_func(drag_widget_,
+ reinterpret_cast<gpointer>(OnDragEndThunk), this);
+ g_signal_handlers_disconnect_by_func(drag_widget_,
+ reinterpret_cast<gpointer>(OnDragDataGetThunk), this);
+
+ // Break the current drag, if any.
+ if (drop_data_.get()) {
+ gtk_grab_add(drag_widget_);
+ gtk_grab_remove(drag_widget_);
+ MessageLoopForUI::current()->RemoveObserver(this);
+ drop_data_.reset();
+ }
+
+ gtk_widget_destroy(drag_widget_);
+ g_object_unref(drag_widget_);
+ drag_widget_ = NULL;
+}
+
+TabContents* TabContentsDragSource::tab_contents() const {
+ return tab_contents_view_->tab_contents();
+}
+
+void TabContentsDragSource::StartDragging(const WebDropData& drop_data,
+ GdkEventButton* last_mouse_down) {
+ int targets_mask = 0;
+
+ if (!drop_data.plain_text.empty())
+ targets_mask |= GtkDndUtil::TEXT_PLAIN;
+ if (drop_data.url.is_valid()) {
+ targets_mask |= GtkDndUtil::TEXT_URI_LIST;
+ targets_mask |= GtkDndUtil::CHROME_NAMED_URL;
+ }
+ if (!drop_data.text_html.empty())
+ targets_mask |= GtkDndUtil::TEXT_HTML;
+ if (!drop_data.file_contents.empty())
+ targets_mask |= GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS;
+
+ if (targets_mask == 0) {
+ NOTIMPLEMENTED();
+ if (tab_contents()->render_view_host())
+ tab_contents()->render_view_host()->DragSourceSystemDragEnded();
+ }
+
+ drop_data_.reset(new WebDropData(drop_data));
+
+ GtkTargetList* list = GtkDndUtil::GetTargetListFromCodeMask(targets_mask);
+ if (targets_mask & GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS) {
+ drag_file_mime_type_ = gdk_atom_intern(
+ mime_util::GetDataMimeType(drop_data.file_contents).c_str(), FALSE);
+ gtk_target_list_add(list, drag_file_mime_type_,
+ 0, GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS);
+ }
+
+ drag_failed_ = false;
+ // If we don't pass an event, GDK won't know what event time to start grabbing
+ // mouse events. Technically it's the mouse motion event and not the mouse
+ // down event that causes the drag, but there's no reliable way to know
+ // *which* motion event initiated the drag, so this will have to do.
+ // TODO(estade): This can sometimes be very far off, e.g. if the user clicks
+ // and holds and doesn't start dragging for a long time. I doubt it matters
+ // much, but we should probably look into the possibility of getting the
+ // initiating event from webkit.
+ gtk_drag_begin(drag_widget_, list, GDK_ACTION_COPY,
+ 1, // Drags are always initiated by the left button.
+ reinterpret_cast<GdkEvent*>(last_mouse_down));
+ MessageLoopForUI::current()->AddObserver(this);
+ // The drag adds a ref; let it own the list.
+ gtk_target_list_unref(list);
+}
+
+void TabContentsDragSource::WillProcessEvent(GdkEvent* event) {
+ // No-op.
+}
+
+void TabContentsDragSource::DidProcessEvent(GdkEvent* event) {
+ if (event->type != GDK_MOTION_NOTIFY)
+ return;
+
+ GdkEventMotion* event_motion = reinterpret_cast<GdkEventMotion*>(event);
+ gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());
+
+ if (tab_contents()->render_view_host()) {
+ tab_contents()->render_view_host()->DragSourceMovedTo(
+ client.x(), client.y(), event_motion->x_root, event_motion->y_root);
+ }
+}
+
+void TabContentsDragSource::OnDragDataGet(
+ GdkDragContext* context, GtkSelectionData* selection_data,
+ guint target_type, guint time) {
+ const int bits_per_byte = 8;
+
+ switch (target_type) {
+ case GtkDndUtil::TEXT_PLAIN: {
+ std::string utf8_text = UTF16ToUTF8(drop_data_->plain_text);
+ gtk_selection_data_set_text(selection_data, utf8_text.c_str(),
+ utf8_text.length());
+ break;
+ }
+
+ case GtkDndUtil::TEXT_URI_LIST: {
+ gchar* uri_array[2];
+ uri_array[0] = strdup(drop_data_->url.spec().c_str());
+ uri_array[1] = NULL;
+ gtk_selection_data_set_uris(selection_data, uri_array);
+ free(uri_array[0]);
+ break;
+ }
+
+ case GtkDndUtil::TEXT_HTML: {
+ // TODO(estade): change relative links to be absolute using
+ // |html_base_url|.
+ std::string utf8_text = UTF16ToUTF8(drop_data_->text_html);
+ gtk_selection_data_set(selection_data,
+ GtkDndUtil::GetAtomForTarget(GtkDndUtil::TEXT_HTML),
+ bits_per_byte,
+ reinterpret_cast<const guchar*>(utf8_text.c_str()),
+ utf8_text.length());
+ break;
+ }
+
+ case GtkDndUtil::CHROME_NAMED_URL: {
+ Pickle pickle;
+ pickle.WriteString(UTF16ToUTF8(drop_data_->url_title));
+ pickle.WriteString(drop_data_->url.spec());
+ gtk_selection_data_set(
+ selection_data,
+ GtkDndUtil::GetAtomForTarget(GtkDndUtil::CHROME_NAMED_URL),
+ bits_per_byte,
+ reinterpret_cast<const guchar*>(pickle.data()),
+ pickle.size());
+ break;
+ }
+
+ case GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS: {
+ gtk_selection_data_set(
+ selection_data,
+ drag_file_mime_type_, bits_per_byte,
+ reinterpret_cast<const guchar*>(drop_data_->file_contents.data()),
+ drop_data_->file_contents.length());
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ }
+}
+
+gboolean TabContentsDragSource::OnDragFailed() {
+ drag_failed_ = true;
+
+ gfx::Point root = gtk_util::ScreenPoint(GetContentNativeView());
+ gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());
+
+ if (tab_contents()->render_view_host()) {
+ tab_contents()->render_view_host()->DragSourceCancelledAt(
+ client.x(), client.y(), root.x(), root.y());
+ }
+
+ // Let the native failure animation run.
+ return FALSE;
+}
+
+void TabContentsDragSource::OnDragEnd() {
+ MessageLoopForUI::current()->RemoveObserver(this);
+
+ if (!drag_failed_) {
+ gfx::Point root = gtk_util::ScreenPoint(GetContentNativeView());
+ gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());
+
+ if (tab_contents()->render_view_host()) {
+ tab_contents()->render_view_host()->DragSourceEndedAt(
+ client.x(), client.y(), root.x(), root.y());
+ }
+ }
+
+ if (tab_contents()->render_view_host())
+ tab_contents()->render_view_host()->DragSourceSystemDragEnded();
+
+ drop_data_.reset();
+}
+
+gfx::NativeView TabContentsDragSource::GetContentNativeView() const {
+ return tab_contents_view_->GetContentNativeView();
+}