diff options
Diffstat (limited to 'views/widget/drop_target_gtk.cc')
-rw-r--r-- | views/widget/drop_target_gtk.cc | 329 |
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 |