| Index: chrome/browser/tab_contents/tab_contents_view_gtk.cc
|
| ===================================================================
|
| --- chrome/browser/tab_contents/tab_contents_view_gtk.cc (revision 19956)
|
| +++ chrome/browser/tab_contents/tab_contents_view_gtk.cc (working copy)
|
| @@ -102,6 +102,192 @@
|
|
|
| } // namespace
|
|
|
| +// A helper class that handles DnD for drops in the renderer. In GTK parlance,
|
| +// this handles destination-side DnD, but not source-side DnD.
|
| +class WebDragDest {
|
| + public:
|
| + explicit WebDragDest(TabContents* tab_contents, GtkWidget* widget)
|
| + : tab_contents_(tab_contents),
|
| + widget_(widget),
|
| + context_(NULL),
|
| + method_factory_(this) {
|
| + gtk_drag_dest_set(widget, static_cast<GtkDestDefaults>(0),
|
| + NULL, 0, GDK_ACTION_COPY);
|
| + g_signal_connect(widget, "drag-motion",
|
| + G_CALLBACK(OnDragMotionThunk), this);
|
| + g_signal_connect(widget, "drag-leave",
|
| + G_CALLBACK(OnDragLeaveThunk), this);
|
| + g_signal_connect(widget, "drag-drop",
|
| + G_CALLBACK(OnDragDropThunk), this);
|
| + g_signal_connect(widget, "drag-data-received",
|
| + G_CALLBACK(OnDragDataReceivedThunk),this);
|
| + }
|
| +
|
| + ~WebDragDest() {
|
| + gtk_drag_dest_unset(widget_);
|
| + }
|
| +
|
| + // This is called when the renderer responds to a drag motion event. We must
|
| + // update the system drag cursor.
|
| + void UpdateDragStatus(bool is_drop_target) {
|
| + if (context_) {
|
| + // TODO(estade): we might want to support other actions besides copy,
|
| + // but that would increase the cost of getting our drag success guess
|
| + // wrong.
|
| + gdk_drag_status(context_, GDK_ACTION_COPY, drag_over_time_);
|
| + is_drop_target_ = false;
|
| + }
|
| + }
|
| +
|
| + // Informs the renderer when a system drag has left the render view.
|
| + // See OnDragLeave().
|
| + void DragLeave() {
|
| + tab_contents_->render_view_host()->DragTargetDragLeave();
|
| + }
|
| +
|
| + private:
|
| + static gboolean OnDragMotionThunk(GtkWidget* widget,
|
| + GdkDragContext* drag_context, gint x, gint y, guint time,
|
| + WebDragDest* dest) {
|
| + return dest->OnDragMotion(drag_context, x, y, time);
|
| + }
|
| + static void OnDragLeaveThunk(GtkWidget* widget,
|
| + GdkDragContext* drag_context, guint time, WebDragDest* dest) {
|
| + dest->OnDragLeave(drag_context, time);
|
| + }
|
| + static gboolean OnDragDropThunk(GtkWidget* widget,
|
| + GdkDragContext* drag_context, gint x, gint y, guint time,
|
| + WebDragDest* dest) {
|
| + return dest->OnDragDrop(drag_context, x, y, time);
|
| + }
|
| + static void OnDragDataReceivedThunk(GtkWidget* widget,
|
| + GdkDragContext* drag_context, gint x, gint y,
|
| + GtkSelectionData* data, guint info, guint time, WebDragDest* dest) {
|
| + dest->OnDragDataReceived(drag_context, x, y, data, info, time);
|
| + }
|
| +
|
| + // Called when a system drag crosses over the render view. As there is no drag
|
| + // enter event, we treat it as an enter event (and not a regular motion event)
|
| + // when |context_| is NULL.
|
| + gboolean OnDragMotion(GdkDragContext* context, gint x, gint y, guint time) {
|
| + if (context_ != context) {
|
| + context_ = context;
|
| + drop_data_.reset(new WebDropData);
|
| + data_requests_ = 0;
|
| + is_drop_target_ = false;
|
| +
|
| + // TODO(estade): support other targets. When we start support URL drags,
|
| + // we'll have to worry about interstitial pages (see web_drop_target.cc).
|
| + data_requests_++;
|
| + gtk_drag_get_data(widget_, context,
|
| + gdk_atom_intern("text/plain", FALSE), time);
|
| + } else if (data_requests_ == 0) {
|
| + tab_contents_->render_view_host()->
|
| + DragTargetDragOver(ClientPoint(), ScreenPoint());
|
| + drag_over_time_ = time;
|
| + }
|
| +
|
| + // Pretend we are a drag destination because we don't want to wait for
|
| + // the renderer to tell us if we really are or not.
|
| + return TRUE;
|
| + }
|
| +
|
| + // We make a series of requests for the drag data when the drag first enters
|
| + // the render view. This is the callback that is used to give us the data
|
| + // for each individual target. When |data_requests_| reaches 0, we know we
|
| + // have attained all the data, and we can finally tell the renderer about the
|
| + // drag.
|
| + void OnDragDataReceived(GdkDragContext* context, gint x, gint y,
|
| + GtkSelectionData* data, guint info, guint time) {
|
| + // We might get the data from an old get_data() request that we no longer
|
| + // care about.
|
| + if (context != context_)
|
| + return;
|
| +
|
| + data_requests_--;
|
| +
|
| + // If the source can't provide us with valid data for a requested target,
|
| + // data->data will be NULL.
|
| + if (data->data) {
|
| + drop_data_->plain_text = UTF8ToUTF16(std::string(
|
| + reinterpret_cast<char*>(data->data), data->length));
|
| + }
|
| +
|
| + if (data_requests_ == 0) {
|
| + // |x| and |y| are seemingly arbitrary at this point.
|
| + tab_contents_->render_view_host()->
|
| + DragTargetDragEnter(*drop_data_.get(), ClientPoint(), ScreenPoint());
|
| + drag_over_time_ = time;
|
| + }
|
| + }
|
| +
|
| + // The drag has left our widget; forward this information to the renderer.
|
| + void OnDragLeave(GdkDragContext* context, guint time) {
|
| + // Set |context_| to NULL to make sure we will recognize the next DragMotion
|
| + // as an enter.
|
| + context_ = NULL;
|
| + drop_data_.reset();
|
| + // When GTK sends us a drag-drop signal, it is shortly (and synchronously)
|
| + // preceded by a drag-leave. The renderer doesn't like getting the signals
|
| + // in this order so delay telling it about the drag-leave till we are sure
|
| + // we are not getting a drop as well.
|
| + MessageLoop::current()->PostTask(FROM_HERE,
|
| + method_factory_.NewRunnableMethod(&WebDragDest::DragLeave));
|
| + }
|
| +
|
| + // Called by GTK when the user releases the mouse, executing a drop.
|
| + gboolean OnDragDrop(GdkDragContext* context, gint x, gint y, guint time) {
|
| + // Cancel that drag leave!
|
| + method_factory_.RevokeAll();
|
| +
|
| + tab_contents_->render_view_host()->
|
| + DragTargetDrop(ClientPoint(), ScreenPoint());
|
| +
|
| + // The second parameter is just an educated guess, but at least we will
|
| + // get the drag-end animation right sometimes.
|
| + gtk_drag_finish(context, is_drop_target_, FALSE, time);
|
| + return TRUE;
|
| + }
|
| +
|
| + // Get the current location of the mouse cursor, relative to the screen.
|
| + gfx::Point ScreenPoint() {
|
| + int x, y;
|
| + gdk_display_get_pointer(gtk_widget_get_display(widget_), NULL, &x, &y,
|
| + NULL);
|
| + return gfx::Point(x, y);
|
| + }
|
| +
|
| + // Get the current location of the mouse cursor, relative to the render view.
|
| + gfx::Point ClientPoint() {
|
| + int x, y;
|
| + gtk_widget_get_pointer(widget_, &x, &y);
|
| + return gfx::Point(x, y);
|
| + }
|
| +
|
| + TabContents* tab_contents_;
|
| + // The render view.
|
| + GtkWidget* widget_;
|
| + // The current drag context for system drags over our render view, or NULL if
|
| + // there is no system drag or the system drag is not over our render view.
|
| + GdkDragContext* context_;
|
| + // The data for the current drag, or NULL if |context_| is NULL.
|
| + scoped_ptr<WebDropData> drop_data_;
|
| +
|
| + // The number of outstanding drag data requests we have sent to the drag
|
| + // source.
|
| + int data_requests_;
|
| +
|
| + // The last time we sent a message to the renderer related to a drag motion.
|
| + gint drag_over_time_;
|
| +
|
| + // Whether the cursor is over a drop target, according to the last message we
|
| + // got from the renderer.
|
| + bool is_drop_target_;
|
| +
|
| + ScopedRunnableMethodFactory<WebDragDest> method_factory_;
|
| + DISALLOW_COPY_AND_ASSIGN(WebDragDest);
|
| +};
|
| +
|
| // static
|
| TabContentsView* TabContentsView::Create(TabContents* tab_contents) {
|
| return new TabContentsViewGtk(tab_contents);
|
| @@ -199,10 +385,11 @@
|
| g_signal_connect(content_view, "button-press-event",
|
| G_CALLBACK(OnMouseDown), this);
|
|
|
| - // DnD signals.
|
| + // Renderer DnD.
|
| g_signal_connect(content_view, "drag-end", G_CALLBACK(OnDragEnd), this);
|
| g_signal_connect(content_view, "drag-data-get", G_CALLBACK(OnDragDataGet),
|
| this);
|
| + drag_dest_.reset(new WebDragDest(tab_contents(), content_view));
|
|
|
| InsertIntoContentArea(content_view);
|
| return view;
|
| @@ -292,7 +479,7 @@
|
| }
|
|
|
| void TabContentsViewGtk::UpdateDragCursor(bool is_drop_target) {
|
| - NOTIMPLEMENTED();
|
| + drag_dest_->UpdateDragStatus(is_drop_target);
|
| }
|
|
|
| void TabContentsViewGtk::GotFocus() {
|
|
|