summaryrefslogtreecommitdiffstats
path: root/chrome/browser/tab_contents
diff options
context:
space:
mode:
authorestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-07 03:10:34 +0000
committerestade@chromium.org <estade@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-07 03:10:34 +0000
commit02d6be5f104727eb713729925a48f884ecaf4188 (patch)
tree69c39b84ba6b63b976c74a70c94e5d4dbf0f8009 /chrome/browser/tab_contents
parente41552495803d482f3168923b743668c0b75b610 (diff)
downloadchromium_src-02d6be5f104727eb713729925a48f884ecaf4188.zip
chromium_src-02d6be5f104727eb713729925a48f884ecaf4188.tar.gz
chromium_src-02d6be5f104727eb713729925a48f884ecaf4188.tar.bz2
GTK: first cut at web contents as drag destination.
TEST=drag plain text into the web page in some place that will accept it, e.g. a text entry box. Also, no crashes when dragging from various places (tab strip, bookmark bar, desktop icons, omnibox, etc.) BUG=http://crbug.com/15429 Review URL: http://codereview.chromium.org/150198 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20010 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/tab_contents')
-rw-r--r--chrome/browser/tab_contents/tab_contents_view_gtk.cc203
-rw-r--r--chrome/browser/tab_contents/tab_contents_view_gtk.h4
2 files changed, 205 insertions, 2 deletions
diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.cc b/chrome/browser/tab_contents/tab_contents_view_gtk.cc
index 2792639..4a64bab 100644
--- a/chrome/browser/tab_contents/tab_contents_view_gtk.cc
+++ b/chrome/browser/tab_contents/tab_contents_view_gtk.cc
@@ -102,6 +102,204 @@ void SetSizeRequest(GtkWidget* widget, gpointer userdata) {
} // 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);
+
+ destroy_handler_ = g_signal_connect(widget, "destroy",
+ G_CALLBACK(gtk_widget_destroyed), &widget_);
+ }
+
+ ~WebDragDest() {
+ if (widget_) {
+ gtk_drag_dest_unset(widget_);
+ g_signal_handler_disconnect(widget_, destroy_handler_);
+ }
+ }
+
+ // 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_;
+
+ // Handler ID for the destroy signal handler. We connect to the destroy
+ // signal handler so that we won't call dest_unset on it after it is
+ // destroyed, but we have to cancel the handler if we are destroyed before
+ // |widget_| is.
+ int destroy_handler_;
+
+ ScopedRunnableMethodFactory<WebDragDest> method_factory_;
+ DISALLOW_COPY_AND_ASSIGN(WebDragDest);
+};
+
// static
TabContentsView* TabContentsView::Create(TabContents* tab_contents) {
return new TabContentsViewGtk(tab_contents);
@@ -199,10 +397,11 @@ RenderWidgetHostView* TabContentsViewGtk::CreateViewForWidget(
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 +491,7 @@ void TabContentsViewGtk::RestoreFocus() {
}
void TabContentsViewGtk::UpdateDragCursor(bool is_drop_target) {
- NOTIMPLEMENTED();
+ drag_dest_->UpdateDragStatus(is_drop_target);
}
void TabContentsViewGtk::GotFocus() {
diff --git a/chrome/browser/tab_contents/tab_contents_view_gtk.h b/chrome/browser/tab_contents/tab_contents_view_gtk.h
index b1d56d0..897d2b7 100644
--- a/chrome/browser/tab_contents/tab_contents_view_gtk.h
+++ b/chrome/browser/tab_contents/tab_contents_view_gtk.h
@@ -20,6 +20,7 @@ class BlockedPopupContainerViewGtk;
class ConstrainedWindowGtk;
class RenderViewContextMenuGtk;
class SadTabGtk;
+class WebDragDest;
typedef struct _GtkFloatingContainer GtkFloatingContainer;
class TabContentsViewGtk : public TabContentsView,
@@ -140,6 +141,9 @@ class TabContentsViewGtk : public TabContentsView,
scoped_ptr<WebDropData> drop_data_;
// The mime type for the file contents of the current drag (if any).
GdkAtom drag_file_mime_type_;
+ // The helper object that handles drag destination related interactions with
+ // GTK.
+ scoped_ptr<WebDragDest> drag_dest_;
DISALLOW_COPY_AND_ASSIGN(TabContentsViewGtk);
};