summaryrefslogtreecommitdiffstats
path: root/views/widget/drop_target_gtk.cc
diff options
context:
space:
mode:
Diffstat (limited to 'views/widget/drop_target_gtk.cc')
-rw-r--r--views/widget/drop_target_gtk.cc329
1 files changed, 329 insertions, 0 deletions
diff --git a/views/widget/drop_target_gtk.cc b/views/widget/drop_target_gtk.cc
new file mode 100644
index 0000000..d262e3e
--- /dev/null
+++ b/views/widget/drop_target_gtk.cc
@@ -0,0 +1,329 @@
+// 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 "views/widget/drop_target_gtk.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "app/drag_drop_types.h"
+#include "app/gtk_dnd_util.h"
+#include "app/os_exchange_data_provider_gtk.h"
+#include "base/gfx/point.h"
+#include "base/string_util.h"
+#include "views/widget/root_view.h"
+#include "views/widget/widget_gtk.h"
+
+namespace {
+
+std::string GdkAtomToString(GdkAtom atom) {
+ gchar* c_name = gdk_atom_name(atom);
+ std::string name(c_name);
+ g_free(c_name);
+ return name;
+}
+
+// Returns true if |name| is a known name of plain text.
+bool IsTextType(const std::string& name) {
+ return name == "text/plain" || name == "TEXT" ||
+ name == "STRING" || name == "UTF8_STRING";
+}
+
+// Returns the OSExchangeData::Formats in |targets| and all the
+// OSExchangeData::CustomFormats in |type_set|.
+int CalculateTypes(GList* targets, std::set<GdkAtom>* type_set) {
+ int types = 0;
+ NOTIMPLEMENTED(); // Need to support FILE_NAME, FILE_CONTENTS
+ for (GList* element = targets; element;
+ element = g_list_next(element)) {
+ GdkAtom atom = static_cast<GdkAtom>(element->data);
+ type_set->insert(atom);
+ if (atom == GDK_TARGET_STRING) {
+ types |= OSExchangeData::STRING;
+ } else if (atom == GtkDndUtil::GetAtomForTarget(
+ GtkDndUtil::CHROME_NAMED_URL) ||
+ atom == GtkDndUtil::GetAtomForTarget(
+ GtkDndUtil::TEXT_URI_LIST)) {
+ types |= OSExchangeData::URL;
+ } else {
+ std::string target_name = GdkAtomToString(atom);
+ if (target_name == "text/html") {
+ types |= OSExchangeData::HTML;
+ } else if (IsTextType(target_name)) {
+ types |= OSExchangeData::STRING;
+ } else {
+ // Assume any unknown data is pickled.
+ types |= OSExchangeData::PICKLED_DATA;
+ }
+ }
+ }
+ return types;
+}
+
+} // namespace
+
+namespace views {
+
+DropTargetGtk::DropTargetGtk(RootView* root_view,
+ GdkDragContext* context)
+ : helper_(root_view),
+ requested_formats_(0),
+ waiting_for_data_(false),
+ received_drop_(false),
+ pending_view_(NULL) {
+ std::set<GdkAtom> all_formats;
+ int source_formats = CalculateTypes(context->targets, &all_formats);
+ data_.reset(new OSExchangeData(new OSExchangeDataProviderGtk(
+ source_formats, all_formats)));
+}
+
+DropTargetGtk::~DropTargetGtk() {
+}
+
+void DropTargetGtk::ResetTargetViewIfEquals(View* view) {
+ helper_.ResetTargetViewIfEquals(view);
+}
+
+void DropTargetGtk::OnDragDataReceived(GdkDragContext* context,
+ gint x,
+ gint y,
+ GtkSelectionData* data,
+ guint info,
+ guint time) {
+ std::string target_name = GdkAtomToString(data->type);
+ if (data->type == GDK_TARGET_STRING || IsTextType(target_name)) {
+ guchar* text_data = gtk_selection_data_get_text(data);
+ string16 result;
+ if (text_data) {
+ char* as_char = reinterpret_cast<char*>(text_data);
+ UTF8ToUTF16(as_char, strlen(as_char), &result);
+ g_free(text_data);
+ }
+ data_provider().SetString(UTF16ToWideHack(result));
+ } else if (requested_custom_formats_.find(data->type) !=
+ requested_custom_formats_.end()) {
+ Pickle result;
+ if (data->length > 0)
+ result = Pickle(reinterpret_cast<char*>(data->data), data->length);
+ data_provider().SetPickledData(data->type, result);
+ } else if (data->type == GtkDndUtil::GetAtomForTarget(
+ GtkDndUtil::CHROME_NAMED_URL)) {
+ GURL url;
+ string16 title;
+ GtkDndUtil::ExtractNamedURL(data, &url, &title);
+ data_provider().SetURL(url, UTF16ToWideHack(title));
+ } else if (data->type == GtkDndUtil::GetAtomForTarget(
+ GtkDndUtil::TEXT_URI_LIST)) {
+ std::vector<GURL> urls;
+ GtkDndUtil::ExtractURIList(data, &urls);
+ if (urls.size() == 1) {
+ data_provider().SetURL(urls[0], std::wstring());
+ } else {
+ // Consumers of OSExchangeData will see this as an invalid URL. That is,
+ // when GetURL is invoked on the OSExchangeData this triggers false to
+ // be returned.
+ data_provider().SetURL(GURL(), std::wstring());
+ }
+ } else {
+ NOTIMPLEMENTED(); // Need to support FILE_NAME, FILE_CONTENTS, HTML.
+ }
+
+ if (!data_->HasAllFormats(requested_formats_, requested_custom_formats_))
+ return; // Waiting on more data.
+
+ int drag_operation = DragDropTypes::GdkDragActionToDragOperation(
+ context->actions);
+ gfx::Point root_view_location(x, y);
+ drag_operation = helper_.OnDragOver(*data_, root_view_location,
+ drag_operation);
+ GdkDragAction gdk_action = static_cast<GdkDragAction>(
+ DragDropTypes::DragOperationToGdkDragAction(drag_operation));
+ if (!received_drop_)
+ gdk_drag_status(context, gdk_action, time);
+
+ waiting_for_data_ = false;
+
+ if (pending_view_ && received_drop_) {
+ FinishDrop(context, x, y, time);
+ // WARNING: we've been deleted.
+ return;
+ }
+}
+
+gboolean DropTargetGtk::OnDragDrop(GdkDragContext* context,
+ gint x,
+ gint y,
+ guint time) {
+ received_drop_ = true;
+ OnDragMotion(context, x, y, time);
+ if (!pending_view_) {
+ // User isn't over a view, no drop can occur.
+ static_cast<WidgetGtk*>(
+ helper_.root_view()->GetWidget())->ResetDropTarget();
+ // WARNING: we've been deleted.
+ return FALSE;
+ }
+
+ if (!waiting_for_data_) {
+ // We've got all the data now.
+ FinishDrop(context, x, y, time);
+ // WARNING: we've been deleted.
+ return TRUE;
+ }
+ // We're waiting on data.
+ return TRUE;
+}
+
+void DropTargetGtk::OnDragLeave(GdkDragContext* context, guint time) {
+ helper_.OnDragExit();
+}
+
+gboolean DropTargetGtk::OnDragMotion(GdkDragContext* context,
+ gint x,
+ gint y,
+ guint time) {
+ waiting_for_data_ = false;
+ gfx::Point root_view_location(x, y);
+ pending_view_ =
+ helper_.CalculateTargetView(root_view_location, *data_, false);
+ if (pending_view_ &&
+ (received_drop_ || (pending_view_ != helper_.target_view() &&
+ pending_view_->AreDropTypesRequired()))) {
+ // The target requires drop types before it can answer CanDrop,
+ // ask for the data now.
+ int formats = 0;
+ std::set<GdkAtom> custom_formats;
+ pending_view_->GetDropFormats(&formats, &custom_formats);
+ IntersectFormats(data_provider().known_formats(),
+ data_provider().known_custom_formats(),
+ &formats, &custom_formats);
+ if (!data_provider().HasDataForAllFormats(formats, custom_formats)) {
+ if (!received_drop_)
+ helper_.OnDragExit();
+
+ // The target needs data for all the types before it can test if the
+ // drop is valid, but we don't have all the data. Request the data
+ // now. When we get back the data we'll update the target.
+ RequestFormats(context, formats, custom_formats, time);
+
+ waiting_for_data_ = true;
+
+ return TRUE;
+ }
+ }
+
+ int drag_operation = DragDropTypes::GdkDragActionToDragOperation(
+ context->actions);
+ drag_operation = helper_.OnDragOver(*data_, root_view_location,
+ drag_operation);
+ if (!received_drop_) {
+ GdkDragAction gdk_action =
+ static_cast<GdkDragAction>(
+ DragDropTypes::DragOperationToGdkDragAction(drag_operation));
+ gdk_drag_status(context, gdk_action, time);
+ }
+ return TRUE;
+}
+
+void DropTargetGtk::FinishDrop(GdkDragContext* context,
+ gint x, gint y, guint time) {
+ gfx::Point root_view_location(x, y);
+ int drag_operation = DragDropTypes::GdkDragActionToDragOperation(
+ context->actions);
+ drag_operation = helper_.OnDrop(*data_, root_view_location,
+ drag_operation);
+ GdkDragAction gdk_action =
+ static_cast<GdkDragAction>(
+ DragDropTypes::DragOperationToGdkDragAction(drag_operation));
+ gtk_drag_finish(context, gdk_action != 0, (gdk_action & GDK_ACTION_MOVE),
+ time);
+
+ static_cast<WidgetGtk*>(helper_.root_view()->GetWidget())->ResetDropTarget();
+ // WARNING: we've been deleted.
+}
+
+void DropTargetGtk::IntersectFormats(int f1, const std::set<GdkAtom>& cf1,
+ int* f2, std::set<GdkAtom>* cf2) {
+ *f2 = (*f2 & f1);
+ std::set<GdkAtom> cf;
+ std::set_intersection(
+ cf1.begin(), cf1.end(), cf2->begin(), cf2->end(),
+ std::insert_iterator<std::set<GdkAtom> >(cf, cf.begin()));
+ cf.swap(*cf2);
+}
+
+void DropTargetGtk::RequestFormats(GdkDragContext* context,
+ int formats,
+ const std::set<GdkAtom>& custom_formats,
+ guint time) {
+ GtkWidget* widget =
+ static_cast<WidgetGtk*>(helper_.root_view()->GetWidget())->
+ window_contents();
+
+ const std::set<GdkAtom>& known_formats =
+ data_provider().known_custom_formats();
+ if ((formats & OSExchangeData::STRING) != 0 &&
+ (requested_formats_ & OSExchangeData::STRING) == 0) {
+ requested_formats_ |= OSExchangeData::STRING;
+ if (known_formats.count(GDK_TARGET_STRING)) {
+ gtk_drag_get_data(widget, context, GDK_TARGET_STRING, time);
+ } else if (known_formats.count(gdk_atom_intern("text/plain", false))) {
+ gtk_drag_get_data(widget, context, gdk_atom_intern("text/plain", false),
+ time);
+ } else if (known_formats.count(gdk_atom_intern("TEXT", false))) {
+ gtk_drag_get_data(widget, context, gdk_atom_intern("TEXT", false),
+ time);
+ } else if (known_formats.count(gdk_atom_intern("STRING", false))) {
+ gtk_drag_get_data(widget, context, gdk_atom_intern("STRING", false),
+ time);
+ } else if (known_formats.count(gdk_atom_intern("UTF8_STRING", false))) {
+ gtk_drag_get_data(widget, context,
+ gdk_atom_intern("UTF8_STRING", false), time);
+ }
+ }
+ if ((formats & OSExchangeData::URL) != 0 &&
+ (requested_formats_ & OSExchangeData::URL) == 0) {
+ requested_formats_ |= OSExchangeData::URL;
+ if (known_formats.count(
+ GtkDndUtil::GetAtomForTarget(GtkDndUtil::CHROME_NAMED_URL))) {
+ gtk_drag_get_data(widget, context,
+ GtkDndUtil::GetAtomForTarget(
+ GtkDndUtil::CHROME_NAMED_URL), time);
+ } else if (known_formats.count(
+ GtkDndUtil::GetAtomForTarget(GtkDndUtil::TEXT_URI_LIST))) {
+ gtk_drag_get_data(widget, context,
+ GtkDndUtil::GetAtomForTarget(
+ GtkDndUtil::TEXT_URI_LIST), time);
+ }
+ }
+ if ((formats & OSExchangeData::FILE_CONTENTS) != 0 &&
+ (requested_formats_ & OSExchangeData::FILE_CONTENTS) == 0) {
+ requested_formats_ |= OSExchangeData::FILE_CONTENTS;
+ NOTIMPLEMENTED();
+ }
+ if ((formats & OSExchangeData::FILE_NAME != 0) &&
+ (requested_formats_ & OSExchangeData::FILE_NAME) == 0) {
+ requested_formats_ |= OSExchangeData::FILE_NAME;
+ NOTIMPLEMENTED();
+ }
+ if ((formats & OSExchangeData::HTML) != 0 &&
+ (requested_formats_ & OSExchangeData::HTML) == 0) {
+ requested_formats_ |= OSExchangeData::HTML;
+ NOTIMPLEMENTED();
+ }
+ for (std::set<GdkAtom>::const_iterator i = custom_formats.begin();
+ i != custom_formats.end(); ++i) {
+ if (requested_custom_formats_.find(*i) ==
+ requested_custom_formats_.end()) {
+ requested_custom_formats_.insert(*i);
+ gtk_drag_get_data(widget, context, *i, time);
+ }
+ }
+}
+
+OSExchangeDataProviderGtk& DropTargetGtk::data_provider() const {
+ return static_cast<OSExchangeDataProviderGtk&>(data_->provider());
+}
+
+} // namespace views