OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |