| Index: views/widget/drop_target_gtk.cc
|
| diff --git a/views/widget/drop_target_gtk.cc b/views/widget/drop_target_gtk.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8d8e76012ecae4760c5f69dc0db9580fa3dc204d
|
| --- /dev/null
|
| +++ b/views/widget/drop_target_gtk.cc
|
| @@ -0,0 +1,329 @@
|
| +// Copyright (c) 2011 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 <string>
|
| +#include <vector>
|
| +
|
| +#include "base/file_path.h"
|
| +#include "base/utf_string_conversions.h"
|
| +#include "net/base/net_util.h"
|
| +#include "ui/base/dragdrop/drag_drop_types.h"
|
| +#include "ui/base/dragdrop/gtk_dnd_util.h"
|
| +#include "ui/base/dragdrop/os_exchange_data_provider_gtk.h"
|
| +#include "ui/gfx/point.h"
|
| +#include "views/widget/root_view.h"
|
| +#include "views/widget/native_widget_gtk.h"
|
| +
|
| +using ui::OSExchangeData;
|
| +
|
| +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" ||
|
| + name == "text/plain;charset=utf-8";
|
| +}
|
| +
|
| +// 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;
|
| + 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 == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) {
|
| + types |= OSExchangeData::URL;
|
| + } else if (atom == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) {
|
| + // TEXT_URI_LIST is used for files as well as urls.
|
| + types |= OSExchangeData::URL | OSExchangeData::FILE_NAME;
|
| + } else {
|
| + std::string target_name = GdkAtomToString(atom);
|
| + 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(internal::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(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 == ui::GetAtomForTarget(ui::CHROME_NAMED_URL)) {
|
| + GURL url;
|
| + string16 title;
|
| + ui::ExtractNamedURL(data, &url, &title);
|
| + data_provider().SetURL(url, title);
|
| + } else if (data->type == ui::GetAtomForTarget(ui::TEXT_URI_LIST)) {
|
| + std::vector<GURL> urls;
|
| + ui::ExtractURIList(data, &urls);
|
| + if (urls.size() == 1 && urls[0].is_valid()) {
|
| + data_provider().SetURL(urls[0], string16());
|
| +
|
| + // TEXT_URI_LIST is used for files as well as urls.
|
| + if (urls[0].SchemeIsFile()) {
|
| + FilePath file_path;
|
| + if (net::FileURLToFilePath(urls[0], &file_path))
|
| + data_provider().SetFilename(file_path);
|
| + }
|
| + } 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(), string16());
|
| + }
|
| + }
|
| +
|
| + if (!data_->HasAllFormats(requested_formats_, requested_custom_formats_))
|
| + return; // Waiting on more data.
|
| +
|
| + int drag_operation = ui::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>(
|
| + ui::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<NativeWidgetGtk*>(
|
| + helper_.root_view()->GetWidget()->native_widget())->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 = ui::DragDropTypes::GdkDragActionToDragOperation(
|
| + context->actions);
|
| + drag_operation = helper_.OnDragOver(*data_, root_view_location,
|
| + drag_operation);
|
| + if (!received_drop_) {
|
| + GdkDragAction gdk_action =
|
| + static_cast<GdkDragAction>(
|
| + ui::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 = ui::DragDropTypes::GdkDragActionToDragOperation(
|
| + context->actions);
|
| + drag_operation = helper_.OnDrop(*data_, root_view_location,
|
| + drag_operation);
|
| + GdkDragAction gdk_action =
|
| + static_cast<GdkDragAction>(
|
| + ui::DragDropTypes::DragOperationToGdkDragAction(drag_operation));
|
| + gtk_drag_finish(context, gdk_action != 0, (gdk_action & GDK_ACTION_MOVE),
|
| + time);
|
| +
|
| + static_cast<NativeWidgetGtk*>(helper_.root_view()->GetWidget()->
|
| + native_widget())->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<NativeWidgetGtk*>(helper_.root_view()->
|
| + GetWidget()->native_widget())->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_atom_intern("UTF8_STRING", false))) {
|
| + gtk_drag_get_data(widget, context,
|
| + gdk_atom_intern("UTF8_STRING", false), time);
|
| + } else if (known_formats.count(gdk_atom_intern("text/plain;charset=utf-8",
|
| + false))) {
|
| + gtk_drag_get_data(widget, context,
|
| + gdk_atom_intern("text/plain;charset=utf-8", false),
|
| + time);
|
| + } else 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);
|
| + }
|
| + }
|
| + if ((formats & OSExchangeData::URL) != 0 &&
|
| + (requested_formats_ & OSExchangeData::URL) == 0) {
|
| + requested_formats_ |= OSExchangeData::URL;
|
| + if (known_formats.count(ui::GetAtomForTarget(ui::CHROME_NAMED_URL))) {
|
| + gtk_drag_get_data(widget, context,
|
| + ui::GetAtomForTarget(ui::CHROME_NAMED_URL), time);
|
| + } else if (known_formats.count(
|
| + ui::GetAtomForTarget(ui::TEXT_URI_LIST))) {
|
| + gtk_drag_get_data(widget, context,
|
| + ui::GetAtomForTarget(ui::TEXT_URI_LIST), time);
|
| + }
|
| + }
|
| + if (((formats & OSExchangeData::FILE_NAME) != 0) &&
|
| + (requested_formats_ & OSExchangeData::FILE_NAME) == 0) {
|
| + requested_formats_ |= OSExchangeData::FILE_NAME;
|
| + gtk_drag_get_data(widget, context,
|
| + ui::GetAtomForTarget(ui::TEXT_URI_LIST), time);
|
| + }
|
| + 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
|
|
|