Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(393)

Side by Side Diff: chrome/browser/tab_contents/tab_contents_view_gtk.cc

Issue 150198: GTK: first cut at web contents as drag destination.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/tab_contents/tab_contents_view_gtk.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/tab_contents/tab_contents_view_gtk.h" 5 #include "chrome/browser/tab_contents/tab_contents_view_gtk.h"
6 6
7 #include <gdk/gdk.h> 7 #include <gdk/gdk.h>
8 #include <gtk/gtk.h> 8 #include <gtk/gtk.h>
9 9
10 #include "base/mime_util.h" 10 #include "base/mime_util.h"
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
95 void SetSizeRequest(GtkWidget* widget, gpointer userdata) { 95 void SetSizeRequest(GtkWidget* widget, gpointer userdata) {
96 gfx::Size* size = static_cast<gfx::Size*>(userdata); 96 gfx::Size* size = static_cast<gfx::Size*>(userdata);
97 if (widget->allocation.width != size->width() || 97 if (widget->allocation.width != size->width() ||
98 widget->allocation.height != size->height()) { 98 widget->allocation.height != size->height()) {
99 gtk_widget_set_size_request(widget, size->width(), size->height()); 99 gtk_widget_set_size_request(widget, size->width(), size->height());
100 } 100 }
101 } 101 }
102 102
103 } // namespace 103 } // namespace
104 104
105 // A helper class that handles DnD for drops in the renderer. In GTK parlance,
106 // this handles destination-side DnD, but not source-side DnD.
107 class WebDragDest {
108 public:
109 explicit WebDragDest(TabContents* tab_contents, GtkWidget* widget)
110 : tab_contents_(tab_contents),
111 widget_(widget),
112 context_(NULL),
113 method_factory_(this) {
114 gtk_drag_dest_set(widget, static_cast<GtkDestDefaults>(0),
115 NULL, 0, GDK_ACTION_COPY);
116 g_signal_connect(widget, "drag-motion",
117 G_CALLBACK(OnDragMotionThunk), this);
118 g_signal_connect(widget, "drag-leave",
119 G_CALLBACK(OnDragLeaveThunk), this);
120 g_signal_connect(widget, "drag-drop",
121 G_CALLBACK(OnDragDropThunk), this);
122 g_signal_connect(widget, "drag-data-received",
123 G_CALLBACK(OnDragDataReceivedThunk),this);
124 }
125
126 ~WebDragDest() {
127 gtk_drag_dest_unset(widget_);
128 }
129
130 // This is called when the renderer responds to a drag motion event. We must
131 // update the system drag cursor.
132 void UpdateDragStatus(bool is_drop_target) {
133 if (context_) {
134 // TODO(estade): we might want to support other actions besides copy,
135 // but that would increase the cost of getting our drag success guess
136 // wrong.
137 gdk_drag_status(context_, GDK_ACTION_COPY, drag_over_time_);
138 is_drop_target_ = false;
139 }
140 }
141
142 // Informs the renderer when a system drag has left the render view.
143 // See OnDragLeave().
144 void DragLeave() {
145 tab_contents_->render_view_host()->DragTargetDragLeave();
146 }
147
148 private:
149 static gboolean OnDragMotionThunk(GtkWidget* widget,
150 GdkDragContext* drag_context, gint x, gint y, guint time,
151 WebDragDest* dest) {
152 return dest->OnDragMotion(drag_context, x, y, time);
153 }
154 static void OnDragLeaveThunk(GtkWidget* widget,
155 GdkDragContext* drag_context, guint time, WebDragDest* dest) {
156 dest->OnDragLeave(drag_context, time);
157 }
158 static gboolean OnDragDropThunk(GtkWidget* widget,
159 GdkDragContext* drag_context, gint x, gint y, guint time,
160 WebDragDest* dest) {
161 return dest->OnDragDrop(drag_context, x, y, time);
162 }
163 static void OnDragDataReceivedThunk(GtkWidget* widget,
164 GdkDragContext* drag_context, gint x, gint y,
165 GtkSelectionData* data, guint info, guint time, WebDragDest* dest) {
166 dest->OnDragDataReceived(drag_context, x, y, data, info, time);
167 }
168
169 // Called when a system drag crosses over the render view. As there is no drag
170 // enter event, we treat it as an enter event (and not a regular motion event)
171 // when |context_| is NULL.
172 gboolean OnDragMotion(GdkDragContext* context, gint x, gint y, guint time) {
173 if (context_ != context) {
174 context_ = context;
175 drop_data_.reset(new WebDropData);
176 data_requests_ = 0;
177 is_drop_target_ = false;
178
179 // TODO(estade): support other targets. When we start support URL drags,
180 // we'll have to worry about interstitial pages (see web_drop_target.cc).
181 data_requests_++;
182 gtk_drag_get_data(widget_, context,
183 gdk_atom_intern("text/plain", FALSE), time);
184 } else if (data_requests_ == 0) {
185 tab_contents_->render_view_host()->
186 DragTargetDragOver(ClientPoint(), ScreenPoint());
187 drag_over_time_ = time;
188 }
189
190 // Pretend we are a drag destination because we don't want to wait for
191 // the renderer to tell us if we really are or not.
192 return TRUE;
193 }
194
195 // We make a series of requests for the drag data when the drag first enters
196 // the render view. This is the callback that is used to give us the data
197 // for each individual target. When |data_requests_| reaches 0, we know we
198 // have attained all the data, and we can finally tell the renderer about the
199 // drag.
200 void OnDragDataReceived(GdkDragContext* context, gint x, gint y,
201 GtkSelectionData* data, guint info, guint time) {
202 // We might get the data from an old get_data() request that we no longer
203 // care about.
204 if (context != context_)
205 return;
206
207 data_requests_--;
208
209 // If the source can't provide us with valid data for a requested target,
210 // data->data will be NULL.
211 if (data->data) {
212 drop_data_->plain_text = UTF8ToUTF16(std::string(
213 reinterpret_cast<char*>(data->data), data->length));
214 }
215
216 if (data_requests_ == 0) {
217 // |x| and |y| are seemingly arbitrary at this point.
218 tab_contents_->render_view_host()->
219 DragTargetDragEnter(*drop_data_.get(), ClientPoint(), ScreenPoint());
220 drag_over_time_ = time;
221 }
222 }
223
224 // The drag has left our widget; forward this information to the renderer.
225 void OnDragLeave(GdkDragContext* context, guint time) {
226 // Set |context_| to NULL to make sure we will recognize the next DragMotion
227 // as an enter.
228 context_ = NULL;
229 drop_data_.reset();
230 // When GTK sends us a drag-drop signal, it is shortly (and synchronously)
231 // preceded by a drag-leave. The renderer doesn't like getting the signals
232 // in this order so delay telling it about the drag-leave till we are sure
233 // we are not getting a drop as well.
234 MessageLoop::current()->PostTask(FROM_HERE,
235 method_factory_.NewRunnableMethod(&WebDragDest::DragLeave));
236 }
237
238 // Called by GTK when the user releases the mouse, executing a drop.
239 gboolean OnDragDrop(GdkDragContext* context, gint x, gint y, guint time) {
240 // Cancel that drag leave!
241 method_factory_.RevokeAll();
242
243 tab_contents_->render_view_host()->
244 DragTargetDrop(ClientPoint(), ScreenPoint());
245
246 // The second parameter is just an educated guess, but at least we will
247 // get the drag-end animation right sometimes.
248 gtk_drag_finish(context, is_drop_target_, FALSE, time);
249 return TRUE;
250 }
251
252 // Get the current location of the mouse cursor, relative to the screen.
253 gfx::Point ScreenPoint() {
254 int x, y;
255 gdk_display_get_pointer(gtk_widget_get_display(widget_), NULL, &x, &y,
256 NULL);
257 return gfx::Point(x, y);
258 }
259
260 // Get the current location of the mouse cursor, relative to the render view.
261 gfx::Point ClientPoint() {
262 int x, y;
263 gtk_widget_get_pointer(widget_, &x, &y);
264 return gfx::Point(x, y);
265 }
266
267 TabContents* tab_contents_;
268 // The render view.
269 GtkWidget* widget_;
270 // The current drag context for system drags over our render view, or NULL if
271 // there is no system drag or the system drag is not over our render view.
272 GdkDragContext* context_;
273 // The data for the current drag, or NULL if |context_| is NULL.
274 scoped_ptr<WebDropData> drop_data_;
275
276 // The number of outstanding drag data requests we have sent to the drag
277 // source.
278 int data_requests_;
279
280 // The last time we sent a message to the renderer related to a drag motion.
281 gint drag_over_time_;
282
283 // Whether the cursor is over a drop target, according to the last message we
284 // got from the renderer.
285 bool is_drop_target_;
286
287 ScopedRunnableMethodFactory<WebDragDest> method_factory_;
288 DISALLOW_COPY_AND_ASSIGN(WebDragDest);
289 };
290
105 // static 291 // static
106 TabContentsView* TabContentsView::Create(TabContents* tab_contents) { 292 TabContentsView* TabContentsView::Create(TabContents* tab_contents) {
107 return new TabContentsViewGtk(tab_contents); 293 return new TabContentsViewGtk(tab_contents);
108 } 294 }
109 295
110 TabContentsViewGtk::TabContentsViewGtk(TabContents* tab_contents) 296 TabContentsViewGtk::TabContentsViewGtk(TabContents* tab_contents)
111 : TabContentsView(tab_contents), 297 : TabContentsView(tab_contents),
112 floating_(gtk_floating_container_new()), 298 floating_(gtk_floating_container_new()),
113 fixed_(gtk_fixed_new()), 299 fixed_(gtk_fixed_new()),
114 popup_view_(NULL) { 300 popup_view_(NULL) {
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
192 G_CALLBACK(OnLeaveNotify), tab_contents()); 378 G_CALLBACK(OnLeaveNotify), tab_contents());
193 g_signal_connect(content_view, "motion-notify-event", 379 g_signal_connect(content_view, "motion-notify-event",
194 G_CALLBACK(OnMouseMove), tab_contents()); 380 G_CALLBACK(OnMouseMove), tab_contents());
195 g_signal_connect(content_view, "scroll-event", 381 g_signal_connect(content_view, "scroll-event",
196 G_CALLBACK(OnMouseScroll), tab_contents()); 382 G_CALLBACK(OnMouseScroll), tab_contents());
197 gtk_widget_add_events(content_view, GDK_LEAVE_NOTIFY_MASK | 383 gtk_widget_add_events(content_view, GDK_LEAVE_NOTIFY_MASK |
198 GDK_POINTER_MOTION_MASK); 384 GDK_POINTER_MOTION_MASK);
199 g_signal_connect(content_view, "button-press-event", 385 g_signal_connect(content_view, "button-press-event",
200 G_CALLBACK(OnMouseDown), this); 386 G_CALLBACK(OnMouseDown), this);
201 387
202 // DnD signals. 388 // Renderer DnD.
203 g_signal_connect(content_view, "drag-end", G_CALLBACK(OnDragEnd), this); 389 g_signal_connect(content_view, "drag-end", G_CALLBACK(OnDragEnd), this);
204 g_signal_connect(content_view, "drag-data-get", G_CALLBACK(OnDragDataGet), 390 g_signal_connect(content_view, "drag-data-get", G_CALLBACK(OnDragDataGet),
205 this); 391 this);
392 drag_dest_.reset(new WebDragDest(tab_contents(), content_view));
206 393
207 InsertIntoContentArea(content_view); 394 InsertIntoContentArea(content_view);
208 return view; 395 return view;
209 } 396 }
210 397
211 gfx::NativeView TabContentsViewGtk::GetNativeView() const { 398 gfx::NativeView TabContentsViewGtk::GetNativeView() const {
212 return floating_.get(); 399 return floating_.get();
213 } 400 }
214 401
215 gfx::NativeView TabContentsViewGtk::GetContentNativeView() const { 402 gfx::NativeView TabContentsViewGtk::GetContentNativeView() const {
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
285 } 472 }
286 473
287 void TabContentsViewGtk::RestoreFocus() { 474 void TabContentsViewGtk::RestoreFocus() {
288 if (focus_store_.widget()) 475 if (focus_store_.widget())
289 gtk_widget_grab_focus(focus_store_.widget()); 476 gtk_widget_grab_focus(focus_store_.widget());
290 else 477 else
291 SetInitialFocus(); 478 SetInitialFocus();
292 } 479 }
293 480
294 void TabContentsViewGtk::UpdateDragCursor(bool is_drop_target) { 481 void TabContentsViewGtk::UpdateDragCursor(bool is_drop_target) {
295 NOTIMPLEMENTED(); 482 drag_dest_->UpdateDragStatus(is_drop_target);
296 } 483 }
297 484
298 void TabContentsViewGtk::GotFocus() { 485 void TabContentsViewGtk::GotFocus() {
299 NOTIMPLEMENTED(); 486 NOTIMPLEMENTED();
300 } 487 }
301 488
302 // This is called when we the renderer asks us to take focus back (i.e., it has 489 // This is called when we the renderer asks us to take focus back (i.e., it has
303 // iterated past the last focusable element on the page). 490 // iterated past the last focusable element on the page).
304 void TabContentsViewGtk::TakeFocus(bool reverse) { 491 void TabContentsViewGtk::TakeFocus(bool reverse) {
305 gtk_widget_child_focus(GTK_WIDGET(GetTopLevelNativeWindow()), 492 gtk_widget_child_focus(GTK_WIDGET(GetTopLevelNativeWindow()),
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
535 gtk_container_child_set_property(GTK_CONTAINER(floating_container), 722 gtk_container_child_set_property(GTK_CONTAINER(floating_container),
536 widget, "x", &value); 723 widget, "x", &value);
537 724
538 int child_y = std::max(half_view_height - (requisition.height / 2), 0); 725 int child_y = std::max(half_view_height - (requisition.height / 2), 0);
539 g_value_set_int(&value, child_y); 726 g_value_set_int(&value, child_y);
540 gtk_container_child_set_property(GTK_CONTAINER(floating_container), 727 gtk_container_child_set_property(GTK_CONTAINER(floating_container),
541 widget, "y", &value); 728 widget, "y", &value);
542 g_value_unset(&value); 729 g_value_unset(&value);
543 } 730 }
544 } 731 }
OLDNEW
« no previous file with comments | « chrome/browser/tab_contents/tab_contents_view_gtk.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698