OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "views/widget/native_widget_gtk.h" |
| 6 |
| 7 #include <X11/Xatom.h> |
| 8 #include <X11/Xlib.h> |
| 9 #include <X11/extensions/shape.h> |
| 10 #include <gdk/gdk.h> |
| 11 #include <gdk/gdkx.h> |
| 12 |
| 13 #include <set> |
| 14 #include <vector> |
| 15 |
| 16 #include "base/auto_reset.h" |
| 17 #include "base/bind.h" |
| 18 #include "base/compiler_specific.h" |
| 19 #include "base/message_loop.h" |
| 20 #include "base/utf_string_conversions.h" |
| 21 #include "ui/base/dragdrop/drag_drop_types.h" |
| 22 #include "ui/base/dragdrop/os_exchange_data.h" |
| 23 #include "ui/base/dragdrop/os_exchange_data_provider_gtk.h" |
| 24 #include "ui/base/gtk/g_object_destructor_filo.h" |
| 25 #include "ui/base/gtk/gtk_signal_registrar.h" |
| 26 #include "ui/base/gtk/gtk_windowing.h" |
| 27 #include "ui/base/gtk/scoped_handle_gtk.h" |
| 28 #include "ui/base/hit_test.h" |
| 29 #include "ui/base/x/x11_util.h" |
| 30 #include "ui/gfx/canvas_skia_paint.h" |
| 31 #include "ui/gfx/compositor/compositor.h" |
| 32 #include "ui/gfx/gtk_util.h" |
| 33 #include "ui/gfx/path.h" |
| 34 #include "ui/gfx/screen.h" |
| 35 #include "ui/views/bubble/bubble_delegate.h" |
| 36 #include "ui/views/focus/view_storage.h" |
| 37 #include "ui/views/ime/input_method_gtk.h" |
| 38 #include "views/controls/textfield/native_textfield_views.h" |
| 39 #include "views/views_delegate.h" |
| 40 #include "views/widget/drop_target_gtk.h" |
| 41 #include "views/widget/gtk_views_fixed.h" |
| 42 #include "views/widget/gtk_views_window.h" |
| 43 #include "views/widget/root_view.h" |
| 44 #include "views/widget/widget_delegate.h" |
| 45 |
| 46 #if defined(TOUCH_UI) |
| 47 #include "ui/base/touch/touch_factory.h" |
| 48 #include "views/widget/tooltip_manager_views.h" |
| 49 #else |
| 50 #include "views/widget/tooltip_manager_gtk.h" |
| 51 #endif |
| 52 |
| 53 #if defined(HAVE_IBUS) |
| 54 #include "ui/views/ime/input_method_ibus.h" |
| 55 #endif |
| 56 |
| 57 using ui::OSExchangeData; |
| 58 using ui::OSExchangeDataProviderGtk; |
| 59 using ui::ActiveWindowWatcherX; |
| 60 |
| 61 namespace views { |
| 62 |
| 63 namespace { |
| 64 |
| 65 // Links the GtkWidget to its NativeWidget. |
| 66 const char* const kNativeWidgetKey = "__VIEWS_NATIVE_WIDGET__"; |
| 67 |
| 68 // A g_object data key to associate a CompositePainter object to a GtkWidget. |
| 69 const char* kCompositePainterKey = "__VIEWS_COMPOSITE_PAINTER__"; |
| 70 |
| 71 // A g_object data key to associate the flag whether or not the widget |
| 72 // is composited to a GtkWidget. gtk_widget_is_composited simply tells |
| 73 // if x11 supports composition and cannot be used to tell if given widget |
| 74 // is composited. |
| 75 const char* kCompositeEnabledKey = "__VIEWS_COMPOSITE_ENABLED__"; |
| 76 |
| 77 // A g_object data key to associate the expose handler id that is |
| 78 // used to remove FREEZE_UPDATE property on the window. |
| 79 const char* kExposeHandlerIdKey = "__VIEWS_EXPOSE_HANDLER_ID__"; |
| 80 |
| 81 // CompositePainter draws a composited child widgets image into its |
| 82 // drawing area. This object is created at most once for a widget and kept |
| 83 // until the widget is destroyed. |
| 84 class CompositePainter { |
| 85 public: |
| 86 explicit CompositePainter(GtkWidget* parent) |
| 87 : parent_object_(G_OBJECT(parent)) { |
| 88 handler_id_ = g_signal_connect_after( |
| 89 parent_object_, "expose_event", G_CALLBACK(OnCompositePaint), NULL); |
| 90 } |
| 91 |
| 92 static void AddCompositePainter(GtkWidget* widget) { |
| 93 CompositePainter* painter = static_cast<CompositePainter*>( |
| 94 g_object_get_data(G_OBJECT(widget), kCompositePainterKey)); |
| 95 if (!painter) { |
| 96 g_object_set_data(G_OBJECT(widget), kCompositePainterKey, |
| 97 new CompositePainter(widget)); |
| 98 g_signal_connect(widget, "destroy", |
| 99 G_CALLBACK(&DestroyPainter), NULL); |
| 100 } |
| 101 } |
| 102 |
| 103 // Set the composition flag. |
| 104 static void SetComposited(GtkWidget* widget) { |
| 105 g_object_set_data(G_OBJECT(widget), kCompositeEnabledKey, |
| 106 const_cast<char*>("")); |
| 107 } |
| 108 |
| 109 // Returns true if the |widget| is composited and ready to be drawn. |
| 110 static bool IsComposited(GtkWidget* widget) { |
| 111 return g_object_get_data(G_OBJECT(widget), kCompositeEnabledKey) != NULL; |
| 112 } |
| 113 |
| 114 private: |
| 115 virtual ~CompositePainter() {} |
| 116 |
| 117 // Composes a image from one child. |
| 118 static void CompositeChildWidget(GtkWidget* child, gpointer data) { |
| 119 GdkEventExpose* event = static_cast<GdkEventExpose*>(data); |
| 120 GtkWidget* parent = gtk_widget_get_parent(child); |
| 121 DCHECK(parent); |
| 122 if (IsComposited(child)) { |
| 123 cairo_t* cr = gdk_cairo_create(parent->window); |
| 124 gdk_cairo_set_source_pixmap(cr, child->window, |
| 125 child->allocation.x, |
| 126 child->allocation.y); |
| 127 GdkRegion* region = gdk_region_rectangle(&child->allocation); |
| 128 gdk_region_intersect(region, event->region); |
| 129 gdk_cairo_region(cr, region); |
| 130 cairo_clip(cr); |
| 131 cairo_set_operator(cr, CAIRO_OPERATOR_OVER); |
| 132 cairo_paint(cr); |
| 133 cairo_destroy(cr); |
| 134 } |
| 135 } |
| 136 |
| 137 // Expose-event handler that compose & draws children's image into |
| 138 // the |parent|'s drawing area. |
| 139 static gboolean OnCompositePaint(GtkWidget* parent, GdkEventExpose* event) { |
| 140 gtk_container_foreach(GTK_CONTAINER(parent), |
| 141 CompositeChildWidget, |
| 142 event); |
| 143 return false; |
| 144 } |
| 145 |
| 146 static void DestroyPainter(GtkWidget* object) { |
| 147 CompositePainter* painter = reinterpret_cast<CompositePainter*>( |
| 148 g_object_get_data(G_OBJECT(object), kCompositePainterKey)); |
| 149 DCHECK(painter); |
| 150 delete painter; |
| 151 } |
| 152 |
| 153 GObject* parent_object_; |
| 154 gulong handler_id_; |
| 155 |
| 156 DISALLOW_COPY_AND_ASSIGN(CompositePainter); |
| 157 }; |
| 158 |
| 159 void EnumerateChildWidgetsForNativeWidgets(GtkWidget* child_widget, |
| 160 gpointer param) { |
| 161 // Walk child widgets, if necessary. |
| 162 if (GTK_IS_CONTAINER(child_widget)) { |
| 163 gtk_container_foreach(GTK_CONTAINER(child_widget), |
| 164 EnumerateChildWidgetsForNativeWidgets, |
| 165 param); |
| 166 } |
| 167 |
| 168 Widget* widget = Widget::GetWidgetForNativeView(child_widget); |
| 169 if (widget) { |
| 170 Widget::Widgets* widgets = reinterpret_cast<Widget::Widgets*>(param); |
| 171 widgets->insert(widget); |
| 172 } |
| 173 } |
| 174 |
| 175 void RemoveExposeHandlerIfExists(GtkWidget* widget) { |
| 176 gulong id = reinterpret_cast<gulong>(g_object_get_data(G_OBJECT(widget), |
| 177 kExposeHandlerIdKey)); |
| 178 if (id) { |
| 179 g_signal_handler_disconnect(G_OBJECT(widget), id); |
| 180 g_object_set_data(G_OBJECT(widget), kExposeHandlerIdKey, 0); |
| 181 } |
| 182 } |
| 183 |
| 184 GtkWindowType WindowTypeToGtkWindowType(Widget::InitParams::Type type) { |
| 185 switch (type) { |
| 186 case Widget::InitParams::TYPE_BUBBLE: |
| 187 case Widget::InitParams::TYPE_WINDOW: |
| 188 case Widget::InitParams::TYPE_WINDOW_FRAMELESS: |
| 189 return GTK_WINDOW_TOPLEVEL; |
| 190 default: |
| 191 return GTK_WINDOW_POPUP; |
| 192 } |
| 193 NOTREACHED(); |
| 194 return GTK_WINDOW_TOPLEVEL; |
| 195 } |
| 196 |
| 197 // Converts a Windows-style hit test result code into a GDK window edge. |
| 198 GdkWindowEdge HitTestCodeToGDKWindowEdge(int hittest_code) { |
| 199 switch (hittest_code) { |
| 200 case HTBOTTOM: |
| 201 return GDK_WINDOW_EDGE_SOUTH; |
| 202 case HTBOTTOMLEFT: |
| 203 return GDK_WINDOW_EDGE_SOUTH_WEST; |
| 204 case HTBOTTOMRIGHT: |
| 205 case HTGROWBOX: |
| 206 return GDK_WINDOW_EDGE_SOUTH_EAST; |
| 207 case HTLEFT: |
| 208 return GDK_WINDOW_EDGE_WEST; |
| 209 case HTRIGHT: |
| 210 return GDK_WINDOW_EDGE_EAST; |
| 211 case HTTOP: |
| 212 return GDK_WINDOW_EDGE_NORTH; |
| 213 case HTTOPLEFT: |
| 214 return GDK_WINDOW_EDGE_NORTH_WEST; |
| 215 case HTTOPRIGHT: |
| 216 return GDK_WINDOW_EDGE_NORTH_EAST; |
| 217 default: |
| 218 NOTREACHED(); |
| 219 break; |
| 220 } |
| 221 // Default to something defaultish. |
| 222 return HitTestCodeToGDKWindowEdge(HTGROWBOX); |
| 223 } |
| 224 |
| 225 // Converts a Windows-style hit test result code into a GDK cursor type. |
| 226 GdkCursorType HitTestCodeToGdkCursorType(int hittest_code) { |
| 227 switch (hittest_code) { |
| 228 case HTBOTTOM: |
| 229 return GDK_BOTTOM_SIDE; |
| 230 case HTBOTTOMLEFT: |
| 231 return GDK_BOTTOM_LEFT_CORNER; |
| 232 case HTBOTTOMRIGHT: |
| 233 case HTGROWBOX: |
| 234 return GDK_BOTTOM_RIGHT_CORNER; |
| 235 case HTLEFT: |
| 236 return GDK_LEFT_SIDE; |
| 237 case HTRIGHT: |
| 238 return GDK_RIGHT_SIDE; |
| 239 case HTTOP: |
| 240 return GDK_TOP_SIDE; |
| 241 case HTTOPLEFT: |
| 242 return GDK_TOP_LEFT_CORNER; |
| 243 case HTTOPRIGHT: |
| 244 return GDK_TOP_RIGHT_CORNER; |
| 245 default: |
| 246 break; |
| 247 } |
| 248 // Default to something defaultish. |
| 249 return GDK_LEFT_PTR; |
| 250 } |
| 251 |
| 252 } // namespace |
| 253 |
| 254 // During drag and drop GTK sends a drag-leave during a drop. This means we |
| 255 // have no way to tell the difference between a normal drag leave and a drop. |
| 256 // To work around that we listen for DROP_START, then ignore the subsequent |
| 257 // drag-leave that GTK generates. |
| 258 class NativeWidgetGtk::DropObserver : public MessageLoopForUI::Observer { |
| 259 public: |
| 260 DropObserver() {} |
| 261 |
| 262 static DropObserver* GetInstance() { |
| 263 return Singleton<DropObserver>::get(); |
| 264 } |
| 265 #if defined(TOUCH_UI) |
| 266 virtual base::EventStatus WillProcessEvent( |
| 267 const base::NativeEvent& event) OVERRIDE { |
| 268 return base::EVENT_CONTINUE; |
| 269 } |
| 270 |
| 271 virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE { |
| 272 } |
| 273 #else |
| 274 virtual void WillProcessEvent(GdkEvent* event) { |
| 275 if (event->type == GDK_DROP_START) { |
| 276 NativeWidgetGtk* widget = GetNativeWidgetGtkForEvent(event); |
| 277 if (widget) |
| 278 widget->ignore_drag_leave_ = true; |
| 279 } |
| 280 } |
| 281 |
| 282 virtual void DidProcessEvent(GdkEvent* event) { |
| 283 } |
| 284 #endif |
| 285 |
| 286 private: |
| 287 NativeWidgetGtk* GetNativeWidgetGtkForEvent(GdkEvent* event) { |
| 288 GtkWidget* gtk_widget = gtk_get_event_widget(event); |
| 289 if (!gtk_widget) |
| 290 return NULL; |
| 291 |
| 292 return static_cast<NativeWidgetGtk*>( |
| 293 internal::NativeWidgetPrivate::GetNativeWidgetForNativeView( |
| 294 gtk_widget)); |
| 295 } |
| 296 |
| 297 DISALLOW_COPY_AND_ASSIGN(DropObserver); |
| 298 }; |
| 299 |
| 300 // Returns the position of a widget on screen. |
| 301 static void GetWidgetPositionOnScreen(GtkWidget* widget, int* x, int *y) { |
| 302 // First get the root window. |
| 303 GtkWidget* root = widget; |
| 304 while (root && !GTK_IS_WINDOW(root)) { |
| 305 root = gtk_widget_get_parent(root); |
| 306 } |
| 307 if (!root) { |
| 308 // If root is null we're not parented. Return 0x0 and assume the caller will |
| 309 // query again when we're parented. |
| 310 *x = *y = 0; |
| 311 return; |
| 312 } |
| 313 // Translate the coordinate from widget to root window. |
| 314 gtk_widget_translate_coordinates(widget, root, 0, 0, x, y); |
| 315 // Then adjust the position with the position of the root window. |
| 316 int window_x, window_y; |
| 317 gtk_window_get_position(GTK_WINDOW(root), &window_x, &window_y); |
| 318 *x += window_x; |
| 319 *y += window_y; |
| 320 } |
| 321 |
| 322 // "expose-event" handler of drag icon widget that renders drag image pixbuf. |
| 323 static gboolean DragIconWidgetPaint(GtkWidget* widget, |
| 324 GdkEventExpose* event, |
| 325 gpointer data) { |
| 326 GdkPixbuf* pixbuf = reinterpret_cast<GdkPixbuf*>(data); |
| 327 |
| 328 cairo_t* cr = gdk_cairo_create(widget->window); |
| 329 |
| 330 gdk_cairo_region(cr, event->region); |
| 331 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); |
| 332 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0.0, 0.0); |
| 333 cairo_paint(cr); |
| 334 |
| 335 cairo_destroy(cr); |
| 336 return true; |
| 337 } |
| 338 |
| 339 // Creates a drag icon widget that draws drag_image. |
| 340 static GtkWidget* CreateDragIconWidget(GdkPixbuf* drag_image) { |
| 341 GdkColormap* rgba_colormap = |
| 342 gdk_screen_get_rgba_colormap(gdk_screen_get_default()); |
| 343 if (!rgba_colormap) |
| 344 return NULL; |
| 345 |
| 346 GtkWidget* drag_widget = gtk_window_new(GTK_WINDOW_POPUP); |
| 347 |
| 348 gtk_widget_set_colormap(drag_widget, rgba_colormap); |
| 349 gtk_widget_set_app_paintable(drag_widget, true); |
| 350 gtk_widget_set_size_request(drag_widget, |
| 351 gdk_pixbuf_get_width(drag_image), |
| 352 gdk_pixbuf_get_height(drag_image)); |
| 353 |
| 354 g_signal_connect(G_OBJECT(drag_widget), "expose-event", |
| 355 G_CALLBACK(&DragIconWidgetPaint), drag_image); |
| 356 return drag_widget; |
| 357 } |
| 358 |
| 359 // static |
| 360 GtkWidget* NativeWidgetGtk::null_parent_ = NULL; |
| 361 |
| 362 //////////////////////////////////////////////////////////////////////////////// |
| 363 // NativeWidgetGtk, public: |
| 364 |
| 365 NativeWidgetGtk::NativeWidgetGtk(internal::NativeWidgetDelegate* delegate) |
| 366 : delegate_(delegate), |
| 367 widget_(NULL), |
| 368 window_contents_(NULL), |
| 369 child_(false), |
| 370 ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)), |
| 371 ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET), |
| 372 transparent_(false), |
| 373 ignore_events_(false), |
| 374 ignore_drag_leave_(false), |
| 375 opacity_(255), |
| 376 drag_data_(NULL), |
| 377 window_state_(GDK_WINDOW_STATE_WITHDRAWN), |
| 378 is_active_(false), |
| 379 transient_to_parent_(false), |
| 380 got_initial_focus_in_(false), |
| 381 has_focus_(false), |
| 382 always_on_top_(false), |
| 383 is_double_buffered_(false), |
| 384 should_handle_menu_key_release_(false), |
| 385 dragged_view_(NULL), |
| 386 painted_(false), |
| 387 has_pointer_grab_(false), |
| 388 has_keyboard_grab_(false), |
| 389 grab_notify_signal_id_(0), |
| 390 is_menu_(false), |
| 391 signal_registrar_(new ui::GtkSignalRegistrar) { |
| 392 #if defined(TOUCH_UI) |
| 393 // Make sure the touch factory is initialized so that it can setup XInput2 for |
| 394 // the widget. |
| 395 ui::TouchFactory::GetInstance(); |
| 396 #endif |
| 397 static bool installed_message_loop_observer = false; |
| 398 if (!installed_message_loop_observer) { |
| 399 installed_message_loop_observer = true; |
| 400 MessageLoopForUI* loop = MessageLoopForUI::current(); |
| 401 if (loop) |
| 402 loop->AddObserver(DropObserver::GetInstance()); |
| 403 } |
| 404 } |
| 405 |
| 406 NativeWidgetGtk::~NativeWidgetGtk() { |
| 407 // We need to delete the input method before calling DestroyRootView(), |
| 408 // because it'll set focus_manager_ to NULL. |
| 409 if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) { |
| 410 DCHECK(widget_ == NULL); |
| 411 delete delegate_; |
| 412 } else { |
| 413 // Disconnect from GObjectDestructorFILO because we're |
| 414 // deleting the NativeWidgetGtk. |
| 415 bool has_widget = !!widget_; |
| 416 if (has_widget) |
| 417 ui::GObjectDestructorFILO::GetInstance()->Disconnect( |
| 418 G_OBJECT(widget_), &OnDestroyedThunk, this); |
| 419 CloseNow(); |
| 420 // Call OnNativeWidgetDestroyed because we're not calling |
| 421 // OnDestroyedThunk |
| 422 if (has_widget) |
| 423 delegate_->OnNativeWidgetDestroyed(); |
| 424 } |
| 425 } |
| 426 |
| 427 GtkWindow* NativeWidgetGtk::GetTransientParent() const { |
| 428 return (!child_ && widget_) ? |
| 429 gtk_window_get_transient_for(GTK_WINDOW(widget_)) : NULL; |
| 430 } |
| 431 |
| 432 bool NativeWidgetGtk::MakeTransparent() { |
| 433 // Transparency can only be enabled only if we haven't realized the widget. |
| 434 DCHECK(!widget_); |
| 435 |
| 436 if (!gdk_screen_is_composited(gdk_screen_get_default())) { |
| 437 // Transparency is only supported for compositing window managers. |
| 438 // NOTE: there's a race during ChromeOS startup such that X might think |
| 439 // compositing isn't supported. We ignore it if the wm says compositing |
| 440 // isn't supported. |
| 441 DLOG(WARNING) << "compositing not supported; allowing anyway"; |
| 442 } |
| 443 |
| 444 if (!gdk_screen_get_rgba_colormap(gdk_screen_get_default())) { |
| 445 // We need rgba to make the window transparent. |
| 446 return false; |
| 447 } |
| 448 |
| 449 transparent_ = true; |
| 450 return true; |
| 451 } |
| 452 |
| 453 void NativeWidgetGtk::EnableDoubleBuffer(bool enabled) { |
| 454 is_double_buffered_ = enabled; |
| 455 if (window_contents_) { |
| 456 if (is_double_buffered_) |
| 457 GTK_WIDGET_SET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED); |
| 458 else |
| 459 GTK_WIDGET_UNSET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED); |
| 460 } |
| 461 } |
| 462 |
| 463 void NativeWidgetGtk::AddChild(GtkWidget* child) { |
| 464 gtk_container_add(GTK_CONTAINER(window_contents_), child); |
| 465 } |
| 466 |
| 467 void NativeWidgetGtk::RemoveChild(GtkWidget* child) { |
| 468 // We can be called after the contents widget has been destroyed, e.g. any |
| 469 // NativeViewHost not removed from the view hierarchy before the window is |
| 470 // closed. |
| 471 if (GTK_IS_CONTAINER(window_contents_)) { |
| 472 gtk_container_remove(GTK_CONTAINER(window_contents_), child); |
| 473 gtk_views_fixed_set_widget_size(child, 0, 0); |
| 474 } |
| 475 } |
| 476 |
| 477 void NativeWidgetGtk::ReparentChild(GtkWidget* child) { |
| 478 gtk_widget_reparent(child, window_contents_); |
| 479 } |
| 480 |
| 481 void NativeWidgetGtk::PositionChild(GtkWidget* child, int x, int y, int w, |
| 482 int h) { |
| 483 gtk_views_fixed_set_widget_size(child, w, h); |
| 484 gtk_fixed_move(GTK_FIXED(window_contents_), child, x, y); |
| 485 } |
| 486 |
| 487 void NativeWidgetGtk::DoDrag(const OSExchangeData& data, int operation) { |
| 488 const OSExchangeDataProviderGtk& data_provider = |
| 489 static_cast<const OSExchangeDataProviderGtk&>(data.provider()); |
| 490 GtkTargetList* targets = data_provider.GetTargetList(); |
| 491 GdkEvent* current_event = gtk_get_current_event(); |
| 492 const OSExchangeDataProviderGtk& provider( |
| 493 static_cast<const OSExchangeDataProviderGtk&>(data.provider())); |
| 494 |
| 495 GdkDragContext* context = gtk_drag_begin( |
| 496 window_contents_, |
| 497 targets, |
| 498 static_cast<GdkDragAction>( |
| 499 ui::DragDropTypes::DragOperationToGdkDragAction(operation)), |
| 500 1, |
| 501 current_event); |
| 502 |
| 503 GtkWidget* drag_icon_widget = NULL; |
| 504 |
| 505 // Set the drag image if one was supplied. |
| 506 if (provider.drag_image()) { |
| 507 drag_icon_widget = CreateDragIconWidget(provider.drag_image()); |
| 508 if (drag_icon_widget) { |
| 509 // Use a widget as the drag icon when compositing is enabled for proper |
| 510 // transparency handling. |
| 511 g_object_ref(provider.drag_image()); |
| 512 gtk_drag_set_icon_widget(context, |
| 513 drag_icon_widget, |
| 514 provider.cursor_offset().x(), |
| 515 provider.cursor_offset().y()); |
| 516 } else { |
| 517 gtk_drag_set_icon_pixbuf(context, |
| 518 provider.drag_image(), |
| 519 provider.cursor_offset().x(), |
| 520 provider.cursor_offset().y()); |
| 521 } |
| 522 } |
| 523 |
| 524 if (current_event) |
| 525 gdk_event_free(current_event); |
| 526 gtk_target_list_unref(targets); |
| 527 |
| 528 drag_data_ = &data_provider; |
| 529 |
| 530 // Block the caller until drag is done by running a nested message loop. |
| 531 MessageLoopForUI::current()->RunWithDispatcher(NULL); |
| 532 |
| 533 drag_data_ = NULL; |
| 534 |
| 535 if (drag_icon_widget) { |
| 536 gtk_widget_destroy(drag_icon_widget); |
| 537 g_object_unref(provider.drag_image()); |
| 538 } |
| 539 } |
| 540 |
| 541 void NativeWidgetGtk::OnActiveChanged() { |
| 542 delegate_->OnNativeWidgetActivationChanged(IsActive()); |
| 543 } |
| 544 |
| 545 void NativeWidgetGtk::ResetDropTarget() { |
| 546 ignore_drag_leave_ = false; |
| 547 drop_target_.reset(NULL); |
| 548 } |
| 549 |
| 550 void NativeWidgetGtk::GetRequestedSize(gfx::Size* out) const { |
| 551 int width, height; |
| 552 if (GTK_IS_VIEWS_FIXED(widget_) && |
| 553 gtk_views_fixed_get_widget_size(GetNativeView(), &width, &height)) { |
| 554 out->SetSize(width, height); |
| 555 } else { |
| 556 GtkRequisition requisition; |
| 557 gtk_widget_get_child_requisition(GetNativeView(), &requisition); |
| 558 out->SetSize(requisition.width, requisition.height); |
| 559 } |
| 560 } |
| 561 |
| 562 //////////////////////////////////////////////////////////////////////////////// |
| 563 // NativeWidgetGtk, ActiveWindowWatcherX::Observer implementation: |
| 564 |
| 565 void NativeWidgetGtk::ActiveWindowChanged(GdkWindow* active_window) { |
| 566 if (!GetNativeView()) |
| 567 return; |
| 568 |
| 569 bool was_active = IsActive(); |
| 570 is_active_ = (active_window == GTK_WIDGET(GetNativeView())->window); |
| 571 if (!is_active_ && active_window && !child_) { |
| 572 // We're not active, but the force the window to be rendered as active if |
| 573 // a child window is transient to us. |
| 574 gpointer data = NULL; |
| 575 gdk_window_get_user_data(active_window, &data); |
| 576 GtkWidget* widget = reinterpret_cast<GtkWidget*>(data); |
| 577 is_active_ = |
| 578 (widget && GTK_IS_WINDOW(widget) && |
| 579 gtk_window_get_transient_for(GTK_WINDOW(widget)) == GTK_WINDOW( |
| 580 widget_)); |
| 581 } |
| 582 if (was_active != IsActive()) { |
| 583 OnActiveChanged(); |
| 584 GetWidget()->GetRootView()->SchedulePaint(); |
| 585 } |
| 586 } |
| 587 |
| 588 //////////////////////////////////////////////////////////////////////////////// |
| 589 // NativeWidgetGtk implementation: |
| 590 |
| 591 bool NativeWidgetGtk::HandleKeyboardEvent(const KeyEvent& key) { |
| 592 if (!GetWidget()->GetFocusManager()) |
| 593 return false; |
| 594 |
| 595 const int key_code = key.key_code(); |
| 596 bool handled = false; |
| 597 |
| 598 // Always reset |should_handle_menu_key_release_| unless we are handling a |
| 599 // VKEY_MENU key release event. It ensures that VKEY_MENU accelerator can only |
| 600 // be activated when handling a VKEY_MENU key release event which is preceded |
| 601 // by an un-handled VKEY_MENU key press event. |
| 602 if (key_code != ui::VKEY_MENU || key.type() != ui::ET_KEY_RELEASED) |
| 603 should_handle_menu_key_release_ = false; |
| 604 |
| 605 if (key.type() == ui::ET_KEY_PRESSED) { |
| 606 // VKEY_MENU is triggered by key release event. |
| 607 // FocusManager::OnKeyEvent() returns false when the key has been consumed. |
| 608 if (key_code != ui::VKEY_MENU) |
| 609 handled = !GetWidget()->GetFocusManager()->OnKeyEvent(key); |
| 610 else |
| 611 should_handle_menu_key_release_ = true; |
| 612 } else if (key_code == ui::VKEY_MENU && should_handle_menu_key_release_ && |
| 613 (key.flags() & ~ui::EF_ALT_DOWN) == 0) { |
| 614 // Trigger VKEY_MENU when only this key is pressed and released, and both |
| 615 // press and release events are not handled by others. |
| 616 ui::Accelerator accelerator(ui::VKEY_MENU, false, false, false); |
| 617 handled = GetWidget()->GetFocusManager()->ProcessAccelerator(accelerator); |
| 618 } |
| 619 |
| 620 return handled; |
| 621 } |
| 622 |
| 623 bool NativeWidgetGtk::SuppressFreezeUpdates() { |
| 624 if (!painted_) { |
| 625 painted_ = true; |
| 626 return true; |
| 627 } |
| 628 return false; |
| 629 } |
| 630 |
| 631 // static |
| 632 void NativeWidgetGtk::UpdateFreezeUpdatesProperty(GtkWindow* window, |
| 633 bool enable) { |
| 634 if (!GTK_WIDGET_REALIZED(GTK_WIDGET(window))) |
| 635 gtk_widget_realize(GTK_WIDGET(window)); |
| 636 GdkWindow* gdk_window = GTK_WIDGET(window)->window; |
| 637 |
| 638 static GdkAtom freeze_atom_ = |
| 639 gdk_atom_intern("_CHROME_FREEZE_UPDATES", FALSE); |
| 640 if (enable) { |
| 641 VLOG(1) << "setting FREEZE UPDATES property. xid=" << |
| 642 GDK_WINDOW_XID(gdk_window); |
| 643 int32 val = 1; |
| 644 gdk_property_change(gdk_window, |
| 645 freeze_atom_, |
| 646 freeze_atom_, |
| 647 32, |
| 648 GDK_PROP_MODE_REPLACE, |
| 649 reinterpret_cast<const guchar*>(&val), |
| 650 1); |
| 651 } else { |
| 652 VLOG(1) << "deleting FREEZE UPDATES property. xid=" << |
| 653 GDK_WINDOW_XID(gdk_window); |
| 654 gdk_property_delete(gdk_window, freeze_atom_); |
| 655 } |
| 656 } |
| 657 |
| 658 // static |
| 659 void NativeWidgetGtk::RegisterChildExposeHandler(GtkWidget* child) { |
| 660 RemoveExposeHandlerIfExists(child); |
| 661 gulong id = g_signal_connect_after(child, "expose-event", |
| 662 G_CALLBACK(&ChildExposeHandler), NULL); |
| 663 g_object_set_data(G_OBJECT(child), kExposeHandlerIdKey, |
| 664 reinterpret_cast<void*>(id)); |
| 665 } |
| 666 |
| 667 //////////////////////////////////////////////////////////////////////////////// |
| 668 // NativeWidgetGtk, NativeWidget implementation: |
| 669 |
| 670 void NativeWidgetGtk::InitNativeWidget(const Widget::InitParams& params) { |
| 671 SetInitParams(params); |
| 672 |
| 673 Widget::InitParams modified_params = params; |
| 674 if (params.parent_widget) { |
| 675 NativeWidgetGtk* parent_gtk = |
| 676 static_cast<NativeWidgetGtk*>(params.parent_widget->native_widget()); |
| 677 modified_params.parent = child_ ? parent_gtk->window_contents() |
| 678 : params.parent_widget->GetNativeView(); |
| 679 } |
| 680 |
| 681 if (!child_) |
| 682 ActiveWindowWatcherX::AddObserver(this); |
| 683 |
| 684 // Make container here. |
| 685 CreateGtkWidget(modified_params); |
| 686 |
| 687 if (params.type == Widget::InitParams::TYPE_MENU) { |
| 688 gtk_window_set_destroy_with_parent(GTK_WINDOW(GetNativeView()), TRUE); |
| 689 gtk_window_set_type_hint(GTK_WINDOW(GetNativeView()), |
| 690 GDK_WINDOW_TYPE_HINT_MENU); |
| 691 } |
| 692 |
| 693 if (View::get_use_acceleration_when_possible()) { |
| 694 if (ui::Compositor::compositor_factory()) { |
| 695 compositor_ = (*ui::Compositor::compositor_factory())(this); |
| 696 } else { |
| 697 gint width, height; |
| 698 gdk_drawable_get_size(window_contents_->window, &width, &height); |
| 699 compositor_ = ui::Compositor::Create(this, |
| 700 GDK_WINDOW_XID(window_contents_->window), |
| 701 gfx::Size(width, height)); |
| 702 } |
| 703 if (compositor_.get()) { |
| 704 View* root_view = delegate_->AsWidget()->GetRootView(); |
| 705 root_view->SetPaintToLayer(true); |
| 706 compositor_->SetRootLayer(root_view->layer()); |
| 707 root_view->SetFillsBoundsOpaquely(!transparent_); |
| 708 } |
| 709 } |
| 710 |
| 711 delegate_->OnNativeWidgetCreated(); |
| 712 |
| 713 if (opacity_ != 255) |
| 714 SetOpacity(opacity_); |
| 715 |
| 716 // Make sure we receive our motion events. |
| 717 |
| 718 // In general we register most events on the parent of all widgets. At a |
| 719 // minimum we need painting to happen on the parent (otherwise painting |
| 720 // doesn't work at all), and similarly we need mouse release events on the |
| 721 // parent as windows don't get mouse releases. |
| 722 gtk_widget_add_events(window_contents_, |
| 723 GDK_ENTER_NOTIFY_MASK | |
| 724 GDK_LEAVE_NOTIFY_MASK | |
| 725 GDK_BUTTON_PRESS_MASK | |
| 726 GDK_BUTTON_RELEASE_MASK | |
| 727 GDK_POINTER_MOTION_MASK | |
| 728 GDK_KEY_PRESS_MASK | |
| 729 GDK_KEY_RELEASE_MASK); |
| 730 |
| 731 signal_registrar_->ConnectAfter(G_OBJECT(window_contents_), "size_request", |
| 732 G_CALLBACK(&OnSizeRequestThunk), this); |
| 733 signal_registrar_->ConnectAfter(G_OBJECT(window_contents_), "size_allocate", |
| 734 G_CALLBACK(&OnSizeAllocateThunk), this); |
| 735 gtk_widget_set_app_paintable(window_contents_, true); |
| 736 signal_registrar_->Connect(window_contents_, "expose_event", |
| 737 G_CALLBACK(&OnPaintThunk), this); |
| 738 signal_registrar_->Connect(window_contents_, "enter_notify_event", |
| 739 G_CALLBACK(&OnEnterNotifyThunk), this); |
| 740 signal_registrar_->Connect(window_contents_, "leave_notify_event", |
| 741 G_CALLBACK(&OnLeaveNotifyThunk), this); |
| 742 signal_registrar_->Connect(window_contents_, "motion_notify_event", |
| 743 G_CALLBACK(&OnMotionNotifyThunk), this); |
| 744 signal_registrar_->Connect(window_contents_, "button_press_event", |
| 745 G_CALLBACK(&OnButtonPressThunk), this); |
| 746 signal_registrar_->Connect(window_contents_, "button_release_event", |
| 747 G_CALLBACK(&OnButtonReleaseThunk), this); |
| 748 signal_registrar_->Connect(window_contents_, "grab_broken_event", |
| 749 G_CALLBACK(&OnGrabBrokeEventThunk), this); |
| 750 signal_registrar_->Connect(window_contents_, "scroll_event", |
| 751 G_CALLBACK(&OnScrollThunk), this); |
| 752 signal_registrar_->Connect(window_contents_, "visibility_notify_event", |
| 753 G_CALLBACK(&OnVisibilityNotifyThunk), this); |
| 754 |
| 755 // In order to receive notification when the window is no longer the front |
| 756 // window, we need to install these on the widget. |
| 757 // NOTE: this doesn't work with focus follows mouse. |
| 758 signal_registrar_->Connect(widget_, "focus_in_event", |
| 759 G_CALLBACK(&OnFocusInThunk), this); |
| 760 signal_registrar_->Connect(widget_, "focus_out_event", |
| 761 G_CALLBACK(&OnFocusOutThunk), this); |
| 762 signal_registrar_->Connect(widget_, "destroy", |
| 763 G_CALLBACK(&OnDestroyThunk), this); |
| 764 signal_registrar_->Connect(widget_, "show", |
| 765 G_CALLBACK(&OnShowThunk), this); |
| 766 signal_registrar_->Connect(widget_, "map", |
| 767 G_CALLBACK(&OnMapThunk), this); |
| 768 signal_registrar_->Connect(widget_, "hide", |
| 769 G_CALLBACK(&OnHideThunk), this); |
| 770 signal_registrar_->Connect(widget_, "configure-event", |
| 771 G_CALLBACK(&OnConfigureEventThunk), this); |
| 772 |
| 773 // Views/FocusManager (re)sets the focus to the root window, |
| 774 // so we need to connect signal handlers to the gtk window. |
| 775 // See views::Views::Focus and views::FocusManager::ClearNativeFocus |
| 776 // for more details. |
| 777 signal_registrar_->Connect(widget_, "key_press_event", |
| 778 G_CALLBACK(&OnEventKeyThunk), this); |
| 779 signal_registrar_->Connect(widget_, "key_release_event", |
| 780 G_CALLBACK(&OnEventKeyThunk), this); |
| 781 |
| 782 // Drag and drop. |
| 783 gtk_drag_dest_set(window_contents_, static_cast<GtkDestDefaults>(0), |
| 784 NULL, 0, GDK_ACTION_COPY); |
| 785 signal_registrar_->Connect(window_contents_, "drag_motion", |
| 786 G_CALLBACK(&OnDragMotionThunk), this); |
| 787 signal_registrar_->Connect(window_contents_, "drag_data_received", |
| 788 G_CALLBACK(&OnDragDataReceivedThunk), this); |
| 789 signal_registrar_->Connect(window_contents_, "drag_drop", |
| 790 G_CALLBACK(&OnDragDropThunk), this); |
| 791 signal_registrar_->Connect(window_contents_, "drag_leave", |
| 792 G_CALLBACK(&OnDragLeaveThunk), this); |
| 793 signal_registrar_->Connect(window_contents_, "drag_data_get", |
| 794 G_CALLBACK(&OnDragDataGetThunk), this); |
| 795 signal_registrar_->Connect(window_contents_, "drag_end", |
| 796 G_CALLBACK(&OnDragEndThunk), this); |
| 797 signal_registrar_->Connect(window_contents_, "drag_failed", |
| 798 G_CALLBACK(&OnDragFailedThunk), this); |
| 799 signal_registrar_->Connect(G_OBJECT(widget_), "window-state-event", |
| 800 G_CALLBACK(&OnWindowStateEventThunk), this); |
| 801 |
| 802 ui::GObjectDestructorFILO::GetInstance()->Connect( |
| 803 G_OBJECT(widget_), &OnDestroyedThunk, this); |
| 804 |
| 805 #if defined(TOUCH_UI) |
| 806 if (params.type != Widget::InitParams::TYPE_TOOLTIP) { |
| 807 views::TooltipManagerViews* manager = new views::TooltipManagerViews( |
| 808 static_cast<internal::RootView*>(GetWidget()->GetRootView())); |
| 809 tooltip_manager_.reset(manager); |
| 810 } |
| 811 #else |
| 812 tooltip_manager_.reset(new TooltipManagerGtk(this)); |
| 813 #endif |
| 814 |
| 815 // Register for tooltips. |
| 816 g_object_set(G_OBJECT(window_contents_), "has-tooltip", TRUE, NULL); |
| 817 signal_registrar_->Connect(window_contents_, "query_tooltip", |
| 818 G_CALLBACK(&OnQueryTooltipThunk), this); |
| 819 |
| 820 if (child_) { |
| 821 if (modified_params.parent) |
| 822 SetBounds(params.bounds); |
| 823 } else { |
| 824 gtk_widget_add_events(widget_, |
| 825 GDK_STRUCTURE_MASK); |
| 826 if (params.bounds.width() > 0 && params.bounds.height() > 0) |
| 827 gtk_window_resize(GTK_WINDOW(widget_), params.bounds.width(), |
| 828 params.bounds.height()); |
| 829 gtk_window_move(GTK_WINDOW(widget_), params.bounds.x(), params.bounds.y()); |
| 830 } |
| 831 } |
| 832 |
| 833 NonClientFrameView* NativeWidgetGtk::CreateNonClientFrameView() { |
| 834 return NULL; |
| 835 } |
| 836 |
| 837 void NativeWidgetGtk::UpdateFrameAfterFrameChange() { |
| 838 // We currently don't support different frame types on Gtk, so we don't |
| 839 // need to implement this. |
| 840 NOTIMPLEMENTED(); |
| 841 } |
| 842 |
| 843 bool NativeWidgetGtk::ShouldUseNativeFrame() const { |
| 844 return false; |
| 845 } |
| 846 |
| 847 void NativeWidgetGtk::FrameTypeChanged() { |
| 848 // This is called when the Theme has changed, so forward the event to the root |
| 849 // widget. |
| 850 GetWidget()->ThemeChanged(); |
| 851 GetWidget()->GetRootView()->SchedulePaint(); |
| 852 } |
| 853 |
| 854 Widget* NativeWidgetGtk::GetWidget() { |
| 855 return delegate_->AsWidget(); |
| 856 } |
| 857 |
| 858 const Widget* NativeWidgetGtk::GetWidget() const { |
| 859 return delegate_->AsWidget(); |
| 860 } |
| 861 |
| 862 gfx::NativeView NativeWidgetGtk::GetNativeView() const { |
| 863 return widget_; |
| 864 } |
| 865 |
| 866 gfx::NativeWindow NativeWidgetGtk::GetNativeWindow() const { |
| 867 return child_ ? NULL : GTK_WINDOW(widget_); |
| 868 } |
| 869 |
| 870 Widget* NativeWidgetGtk::GetTopLevelWidget() { |
| 871 NativeWidgetPrivate* native_widget = GetTopLevelNativeWidget(GetNativeView()); |
| 872 return native_widget ? native_widget->GetWidget() : NULL; |
| 873 } |
| 874 |
| 875 const ui::Compositor* NativeWidgetGtk::GetCompositor() const { |
| 876 return compositor_.get(); |
| 877 } |
| 878 |
| 879 ui::Compositor* NativeWidgetGtk::GetCompositor() { |
| 880 return compositor_.get(); |
| 881 } |
| 882 |
| 883 void NativeWidgetGtk::CalculateOffsetToAncestorWithLayer( |
| 884 gfx::Point* offset, |
| 885 ui::Layer** layer_parent) { |
| 886 } |
| 887 |
| 888 void NativeWidgetGtk::ReorderLayers() { |
| 889 } |
| 890 |
| 891 void NativeWidgetGtk::ViewRemoved(View* view) { |
| 892 if (drop_target_.get()) |
| 893 drop_target_->ResetTargetViewIfEquals(view); |
| 894 } |
| 895 |
| 896 void NativeWidgetGtk::SetNativeWindowProperty(const char* name, void* value) { |
| 897 g_object_set_data(G_OBJECT(widget_), name, value); |
| 898 } |
| 899 |
| 900 void* NativeWidgetGtk::GetNativeWindowProperty(const char* name) const { |
| 901 return g_object_get_data(G_OBJECT(widget_), name); |
| 902 } |
| 903 |
| 904 TooltipManager* NativeWidgetGtk::GetTooltipManager() const { |
| 905 return tooltip_manager_.get(); |
| 906 } |
| 907 |
| 908 bool NativeWidgetGtk::IsScreenReaderActive() const { |
| 909 return false; |
| 910 } |
| 911 |
| 912 void NativeWidgetGtk::SendNativeAccessibilityEvent( |
| 913 View* view, |
| 914 ui::AccessibilityTypes::Event event_type) { |
| 915 // In the future if we add native GTK accessibility support, the |
| 916 // notification should be sent here. |
| 917 } |
| 918 |
| 919 void NativeWidgetGtk::SetMouseCapture() { |
| 920 DCHECK(!HasMouseCapture()); |
| 921 |
| 922 // Release the current grab. |
| 923 GtkWidget* current_grab_window = gtk_grab_get_current(); |
| 924 if (current_grab_window) |
| 925 gtk_grab_remove(current_grab_window); |
| 926 |
| 927 if (is_menu_ && gdk_pointer_is_grabbed()) |
| 928 gdk_pointer_ungrab(GDK_CURRENT_TIME); |
| 929 |
| 930 // Make sure all app mouse/keyboard events are targeted at us only. |
| 931 gtk_grab_add(window_contents_); |
| 932 if (gtk_grab_get_current() == window_contents_ && !grab_notify_signal_id_) { |
| 933 // "grab_notify" is sent any time the grab changes. We only care about grab |
| 934 // changes when we have done a grab. |
| 935 grab_notify_signal_id_ = g_signal_connect( |
| 936 window_contents_, "grab_notify", G_CALLBACK(&OnGrabNotifyThunk), this); |
| 937 } |
| 938 |
| 939 if (is_menu_) { |
| 940 // For menus we do a pointer grab too. This ensures we get mouse events from |
| 941 // other apps. In theory we should do this for all widget types, but doing |
| 942 // so leads to gdk_pointer_grab randomly returning GDK_GRAB_ALREADY_GRABBED. |
| 943 GdkGrabStatus pointer_grab_status = |
| 944 gdk_pointer_grab(window_contents()->window, FALSE, |
| 945 static_cast<GdkEventMask>( |
| 946 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | |
| 947 GDK_POINTER_MOTION_MASK), |
| 948 NULL, NULL, GDK_CURRENT_TIME); |
| 949 // NOTE: technically grab may fail. We may want to try and continue on in |
| 950 // that case. |
| 951 DCHECK_EQ(GDK_GRAB_SUCCESS, pointer_grab_status); |
| 952 has_pointer_grab_ = pointer_grab_status == GDK_GRAB_SUCCESS; |
| 953 |
| 954 #if defined(TOUCH_UI) |
| 955 ::Window window = GDK_WINDOW_XID(window_contents()->window); |
| 956 Display* display = GDK_WINDOW_XDISPLAY(window_contents()->window); |
| 957 bool xi2grab = |
| 958 ui::TouchFactory::GetInstance()->GrabTouchDevices(display, window); |
| 959 // xi2grab should always succeed if has_pointer_grab_ succeeded. |
| 960 DCHECK(xi2grab); |
| 961 has_pointer_grab_ = has_pointer_grab_ && xi2grab; |
| 962 #endif |
| 963 } |
| 964 } |
| 965 |
| 966 void NativeWidgetGtk::ReleaseMouseCapture() { |
| 967 bool delegate_lost_capture = HasMouseCapture(); |
| 968 if (GTK_WIDGET_HAS_GRAB(window_contents_)) |
| 969 gtk_grab_remove(window_contents_); |
| 970 if (grab_notify_signal_id_) { |
| 971 g_signal_handler_disconnect(window_contents_, grab_notify_signal_id_); |
| 972 grab_notify_signal_id_ = 0; |
| 973 } |
| 974 if (has_pointer_grab_) { |
| 975 has_pointer_grab_ = false; |
| 976 gdk_pointer_ungrab(GDK_CURRENT_TIME); |
| 977 #if defined(TOUCH_UI) |
| 978 ui::TouchFactory::GetInstance()->UngrabTouchDevices( |
| 979 GDK_WINDOW_XDISPLAY(window_contents()->window)); |
| 980 #endif |
| 981 } |
| 982 if (delegate_lost_capture) |
| 983 delegate_->OnMouseCaptureLost(); |
| 984 } |
| 985 |
| 986 bool NativeWidgetGtk::HasMouseCapture() const { |
| 987 return GTK_WIDGET_HAS_GRAB(window_contents_) || has_pointer_grab_; |
| 988 } |
| 989 |
| 990 InputMethod* NativeWidgetGtk::CreateInputMethod() { |
| 991 // Create input method when pure views is enabled but not on views desktop. |
| 992 // TODO(suzhe): Always enable input method when we start to use |
| 993 // RenderWidgetHostViewViews in normal ChromeOS. |
| 994 if (views::Widget::IsPureViews()) { |
| 995 #if defined(HAVE_IBUS) |
| 996 InputMethod* input_method = |
| 997 InputMethodIBus::IsInputMethodIBusEnabled() ? |
| 998 static_cast<InputMethod*>(new InputMethodIBus(this)) : |
| 999 static_cast<InputMethod*>(new InputMethodGtk(this)); |
| 1000 #else |
| 1001 InputMethod* input_method = new InputMethodGtk(this); |
| 1002 #endif |
| 1003 input_method->Init(GetWidget()); |
| 1004 return input_method; |
| 1005 } |
| 1006 // GTK's textfield will handle IME. |
| 1007 return NULL; |
| 1008 } |
| 1009 |
| 1010 void NativeWidgetGtk::CenterWindow(const gfx::Size& size) { |
| 1011 gfx::Rect center_rect; |
| 1012 |
| 1013 GtkWindow* parent = gtk_window_get_transient_for(GetNativeWindow()); |
| 1014 if (parent) { |
| 1015 // We have a parent window, center over it. |
| 1016 gint parent_x = 0; |
| 1017 gint parent_y = 0; |
| 1018 gtk_window_get_position(parent, &parent_x, &parent_y); |
| 1019 gint parent_w = 0; |
| 1020 gint parent_h = 0; |
| 1021 gtk_window_get_size(parent, &parent_w, &parent_h); |
| 1022 center_rect = gfx::Rect(parent_x, parent_y, parent_w, parent_h); |
| 1023 } else { |
| 1024 // We have no parent window, center over the screen. |
| 1025 center_rect = gfx::Screen::GetMonitorWorkAreaNearestWindow(GetNativeView()); |
| 1026 } |
| 1027 gfx::Rect bounds(center_rect.x() + (center_rect.width() - size.width()) / 2, |
| 1028 center_rect.y() + (center_rect.height() - size.height()) / 2, |
| 1029 size.width(), size.height()); |
| 1030 GetWidget()->SetBoundsConstrained(bounds); |
| 1031 } |
| 1032 |
| 1033 void NativeWidgetGtk::GetWindowPlacement( |
| 1034 gfx::Rect* bounds, |
| 1035 ui::WindowShowState* show_state) const { |
| 1036 // Do nothing for now. ChromeOS isn't yet saving window placement. |
| 1037 } |
| 1038 |
| 1039 void NativeWidgetGtk::SetWindowTitle(const string16& title) { |
| 1040 // We don't have a window title on ChromeOS (right now). |
| 1041 } |
| 1042 |
| 1043 void NativeWidgetGtk::SetWindowIcons(const SkBitmap& window_icon, |
| 1044 const SkBitmap& app_icon) { |
| 1045 // We don't have window icons on ChromeOS. |
| 1046 } |
| 1047 |
| 1048 void NativeWidgetGtk::SetAccessibleName(const string16& name) { |
| 1049 } |
| 1050 |
| 1051 void NativeWidgetGtk::SetAccessibleRole(ui::AccessibilityTypes::Role role) { |
| 1052 } |
| 1053 |
| 1054 void NativeWidgetGtk::SetAccessibleState(ui::AccessibilityTypes::State state) { |
| 1055 } |
| 1056 |
| 1057 void NativeWidgetGtk::BecomeModal() { |
| 1058 gtk_window_set_modal(GetNativeWindow(), true); |
| 1059 } |
| 1060 |
| 1061 gfx::Rect NativeWidgetGtk::GetWindowScreenBounds() const { |
| 1062 // Client == Window bounds on Gtk. |
| 1063 return GetClientAreaScreenBounds(); |
| 1064 } |
| 1065 |
| 1066 gfx::Rect NativeWidgetGtk::GetClientAreaScreenBounds() const { |
| 1067 // Due to timing we can get a request for bounds after Close(). |
| 1068 // TODO(beng): Figure out if this is bogus. |
| 1069 if (!widget_) |
| 1070 return gfx::Rect(size_); |
| 1071 |
| 1072 int x = 0, y = 0, w = 0, h = 0; |
| 1073 if (GTK_IS_WINDOW(widget_)) { |
| 1074 gtk_window_get_position(GTK_WINDOW(widget_), &x, &y); |
| 1075 // NOTE: this doesn't include frame decorations, but it should be good |
| 1076 // enough for our uses. |
| 1077 gtk_window_get_size(GTK_WINDOW(widget_), &w, &h); |
| 1078 } else { |
| 1079 GetWidgetPositionOnScreen(widget_, &x, &y); |
| 1080 w = widget_->allocation.width; |
| 1081 h = widget_->allocation.height; |
| 1082 } |
| 1083 return gfx::Rect(x, y, w, h); |
| 1084 } |
| 1085 |
| 1086 gfx::Rect NativeWidgetGtk::GetRestoredBounds() const { |
| 1087 // We currently don't support tiling, so this doesn't matter. |
| 1088 return GetWindowScreenBounds(); |
| 1089 } |
| 1090 |
| 1091 void NativeWidgetGtk::SetBounds(const gfx::Rect& bounds) { |
| 1092 if (child_) { |
| 1093 GtkWidget* parent = gtk_widget_get_parent(widget_); |
| 1094 if (GTK_IS_VIEWS_FIXED(parent)) { |
| 1095 NativeWidgetGtk* parent_widget = static_cast<NativeWidgetGtk*>( |
| 1096 internal::NativeWidgetPrivate::GetNativeWidgetForNativeView(parent)); |
| 1097 parent_widget->PositionChild(widget_, bounds.x(), bounds.y(), |
| 1098 bounds.width(), bounds.height()); |
| 1099 } else { |
| 1100 DCHECK(GTK_IS_FIXED(parent)) |
| 1101 << "Parent of NativeWidgetGtk has to be Fixed or ViewsFixed"; |
| 1102 // Just request the size if the parent is not NativeWidgetGtk but plain |
| 1103 // GtkFixed. NativeWidgetGtk does not know the minimum size so we assume |
| 1104 // the caller of the SetBounds knows exactly how big it wants to be. |
| 1105 gtk_widget_set_size_request(widget_, bounds.width(), bounds.height()); |
| 1106 if (parent != null_parent_) |
| 1107 gtk_fixed_move(GTK_FIXED(parent), widget_, bounds.x(), bounds.y()); |
| 1108 } |
| 1109 } else { |
| 1110 if (GTK_WIDGET_MAPPED(widget_)) { |
| 1111 // If the widget is mapped (on screen), we can move and resize with one |
| 1112 // call, which avoids two separate window manager steps. |
| 1113 gdk_window_move_resize(widget_->window, bounds.x(), bounds.y(), |
| 1114 bounds.width(), bounds.height()); |
| 1115 } |
| 1116 |
| 1117 // Always call gtk_window_move and gtk_window_resize so that GtkWindow's |
| 1118 // geometry info is up-to-date. |
| 1119 GtkWindow* gtk_window = GTK_WINDOW(widget_); |
| 1120 // TODO: this may need to set an initial size if not showing. |
| 1121 // TODO: need to constrain based on screen size. |
| 1122 if (!bounds.IsEmpty()) { |
| 1123 gtk_window_resize(gtk_window, bounds.width(), bounds.height()); |
| 1124 } |
| 1125 gtk_window_move(gtk_window, bounds.x(), bounds.y()); |
| 1126 } |
| 1127 } |
| 1128 |
| 1129 void NativeWidgetGtk::SetSize(const gfx::Size& size) { |
| 1130 if (child_) { |
| 1131 GtkWidget* parent = gtk_widget_get_parent(widget_); |
| 1132 if (GTK_IS_VIEWS_FIXED(parent)) { |
| 1133 gtk_views_fixed_set_widget_size(widget_, size.width(), size.height()); |
| 1134 } else { |
| 1135 gtk_widget_set_size_request(widget_, size.width(), size.height()); |
| 1136 } |
| 1137 } else { |
| 1138 if (GTK_WIDGET_MAPPED(widget_)) |
| 1139 gdk_window_resize(widget_->window, size.width(), size.height()); |
| 1140 GtkWindow* gtk_window = GTK_WINDOW(widget_); |
| 1141 if (!size.IsEmpty()) |
| 1142 gtk_window_resize(gtk_window, size.width(), size.height()); |
| 1143 } |
| 1144 } |
| 1145 |
| 1146 void NativeWidgetGtk::MoveAbove(gfx::NativeView native_view) { |
| 1147 ui::StackPopupWindow(GetNativeView(), native_view); |
| 1148 } |
| 1149 |
| 1150 void NativeWidgetGtk::MoveToTop() { |
| 1151 DCHECK(GTK_IS_WINDOW(GetNativeView())); |
| 1152 gtk_window_present(GTK_WINDOW(GetNativeView())); |
| 1153 } |
| 1154 |
| 1155 void NativeWidgetGtk::SetShape(gfx::NativeRegion region) { |
| 1156 if (widget_ && widget_->window) { |
| 1157 gdk_window_shape_combine_region(widget_->window, region, 0, 0); |
| 1158 gdk_region_destroy(region); |
| 1159 } |
| 1160 } |
| 1161 |
| 1162 void NativeWidgetGtk::Close() { |
| 1163 if (!widget_) |
| 1164 return; // No need to do anything. |
| 1165 |
| 1166 // Hide first. |
| 1167 Hide(); |
| 1168 if (!close_widget_factory_.HasWeakPtrs()) { |
| 1169 // And we delay the close just in case we're on the stack. |
| 1170 MessageLoop::current()->PostTask( |
| 1171 FROM_HERE, |
| 1172 base::Bind(&NativeWidgetGtk::CloseNow, |
| 1173 close_widget_factory_.GetWeakPtr())); |
| 1174 } |
| 1175 } |
| 1176 |
| 1177 void NativeWidgetGtk::CloseNow() { |
| 1178 if (widget_) { |
| 1179 gtk_widget_destroy(widget_); // Triggers OnDestroy(). |
| 1180 } |
| 1181 } |
| 1182 |
| 1183 void NativeWidgetGtk::EnableClose(bool enable) { |
| 1184 gtk_window_set_deletable(GetNativeWindow(), enable); |
| 1185 } |
| 1186 |
| 1187 void NativeWidgetGtk::Show() { |
| 1188 if (widget_) { |
| 1189 gtk_widget_show(widget_); |
| 1190 if (widget_->window) |
| 1191 gdk_window_raise(widget_->window); |
| 1192 } |
| 1193 } |
| 1194 |
| 1195 void NativeWidgetGtk::Hide() { |
| 1196 if (widget_) { |
| 1197 gtk_widget_hide(widget_); |
| 1198 if (widget_->window) |
| 1199 gdk_window_lower(widget_->window); |
| 1200 } |
| 1201 } |
| 1202 |
| 1203 void NativeWidgetGtk::ShowMaximizedWithBounds( |
| 1204 const gfx::Rect& restored_bounds) { |
| 1205 // TODO: when we add maximization support update this. |
| 1206 Show(); |
| 1207 } |
| 1208 |
| 1209 void NativeWidgetGtk::ShowWithWindowState(ui::WindowShowState show_state) { |
| 1210 // No concept of maximization (yet) on ChromeOS. |
| 1211 if (show_state == ui::SHOW_STATE_INACTIVE) |
| 1212 gtk_window_set_focus_on_map(GetNativeWindow(), false); |
| 1213 gtk_widget_show(GetNativeView()); |
| 1214 } |
| 1215 |
| 1216 bool NativeWidgetGtk::IsVisible() const { |
| 1217 return GTK_WIDGET_VISIBLE(GetNativeView()) && (GetWidget()->is_top_level() || |
| 1218 GetWidget()->GetTopLevelWidget()->IsVisible()); |
| 1219 } |
| 1220 |
| 1221 void NativeWidgetGtk::Activate() { |
| 1222 gtk_window_present(GetNativeWindow()); |
| 1223 } |
| 1224 |
| 1225 void NativeWidgetGtk::Deactivate() { |
| 1226 gdk_window_lower(GTK_WIDGET(GetNativeView())->window); |
| 1227 } |
| 1228 |
| 1229 bool NativeWidgetGtk::IsActive() const { |
| 1230 DCHECK(!child_); |
| 1231 return is_active_; |
| 1232 } |
| 1233 |
| 1234 void NativeWidgetGtk::SetAlwaysOnTop(bool on_top) { |
| 1235 DCHECK(!child_); |
| 1236 always_on_top_ = on_top; |
| 1237 if (widget_) |
| 1238 gtk_window_set_keep_above(GTK_WINDOW(widget_), on_top); |
| 1239 } |
| 1240 |
| 1241 void NativeWidgetGtk::Maximize() { |
| 1242 #if defined(TOUCH_UI) |
| 1243 // There may not be a window manager. So maximize ourselves: move to the |
| 1244 // top-left corner and resize to the entire bounds of the screen. |
| 1245 gfx::Rect screen = gfx::Screen::GetMonitorAreaNearestWindow(GetNativeView()); |
| 1246 gtk_window_move(GTK_WINDOW(GetNativeWindow()), screen.x(), screen.y()); |
| 1247 // TODO(backer): Remove this driver bug workaround once it is fixed. |
| 1248 gtk_window_resize(GTK_WINDOW(GetNativeWindow()), |
| 1249 screen.width() - 1, screen.height()); |
| 1250 #else |
| 1251 gtk_window_maximize(GetNativeWindow()); |
| 1252 #endif |
| 1253 } |
| 1254 |
| 1255 void NativeWidgetGtk::Minimize() { |
| 1256 gtk_window_iconify(GetNativeWindow()); |
| 1257 } |
| 1258 |
| 1259 bool NativeWidgetGtk::IsMaximized() const { |
| 1260 return window_state_ & GDK_WINDOW_STATE_MAXIMIZED; |
| 1261 } |
| 1262 |
| 1263 bool NativeWidgetGtk::IsMinimized() const { |
| 1264 return window_state_ & GDK_WINDOW_STATE_ICONIFIED; |
| 1265 } |
| 1266 |
| 1267 void NativeWidgetGtk::Restore() { |
| 1268 if (IsFullscreen()) { |
| 1269 SetFullscreen(false); |
| 1270 } else { |
| 1271 if (IsMaximized()) |
| 1272 gtk_window_unmaximize(GetNativeWindow()); |
| 1273 else if (IsMinimized()) |
| 1274 gtk_window_deiconify(GetNativeWindow()); |
| 1275 } |
| 1276 } |
| 1277 |
| 1278 void NativeWidgetGtk::SetFullscreen(bool fullscreen) { |
| 1279 if (fullscreen) |
| 1280 gtk_window_fullscreen(GetNativeWindow()); |
| 1281 else |
| 1282 gtk_window_unfullscreen(GetNativeWindow()); |
| 1283 } |
| 1284 |
| 1285 bool NativeWidgetGtk::IsFullscreen() const { |
| 1286 return window_state_ & GDK_WINDOW_STATE_FULLSCREEN; |
| 1287 } |
| 1288 |
| 1289 void NativeWidgetGtk::SetOpacity(unsigned char opacity) { |
| 1290 opacity_ = opacity; |
| 1291 if (widget_) { |
| 1292 // We can only set the opacity when the widget has been realized. |
| 1293 gdk_window_set_opacity(widget_->window, static_cast<gdouble>(opacity) / |
| 1294 static_cast<gdouble>(255)); |
| 1295 } |
| 1296 } |
| 1297 |
| 1298 void NativeWidgetGtk::SetUseDragFrame(bool use_drag_frame) { |
| 1299 NOTIMPLEMENTED(); |
| 1300 } |
| 1301 |
| 1302 bool NativeWidgetGtk::IsAccessibleWidget() const { |
| 1303 return false; |
| 1304 } |
| 1305 |
| 1306 void NativeWidgetGtk::RunShellDrag(View* view, |
| 1307 const ui::OSExchangeData& data, |
| 1308 int operation) { |
| 1309 DoDrag(data, operation); |
| 1310 } |
| 1311 |
| 1312 void NativeWidgetGtk::SchedulePaintInRect(const gfx::Rect& rect) { |
| 1313 // No need to schedule paint if |
| 1314 // 1) widget_ is NULL. This may happen because this instance may |
| 1315 // be deleted after the gtk widget has been destroyed (See OnDestroy()). |
| 1316 // 2) widget_ is not drawable (mapped and visible) |
| 1317 // 3) If it's never painted before. The first expose event will |
| 1318 // paint the area that has to be painted. |
| 1319 if (widget_ && GTK_WIDGET_DRAWABLE(widget_) && painted_) { |
| 1320 gtk_widget_queue_draw_area(widget_, rect.x(), rect.y(), rect.width(), |
| 1321 rect.height()); |
| 1322 } |
| 1323 } |
| 1324 |
| 1325 void NativeWidgetGtk::SetCursor(gfx::NativeCursor cursor) { |
| 1326 #if defined(TOUCH_UI) |
| 1327 if (!ui::TouchFactory::GetInstance()->is_cursor_visible()) |
| 1328 cursor = gfx::GetCursor(GDK_BLANK_CURSOR); |
| 1329 #endif |
| 1330 // |window_contents_| is placed on top of |widget_|. So the cursor needs to be |
| 1331 // set on |window_contents_| instead of |widget_|. |
| 1332 if (window_contents_) |
| 1333 gdk_window_set_cursor(window_contents_->window, cursor); |
| 1334 } |
| 1335 |
| 1336 void NativeWidgetGtk::ClearNativeFocus() { |
| 1337 DCHECK(!child_); |
| 1338 if (!GetNativeView()) { |
| 1339 NOTREACHED(); |
| 1340 return; |
| 1341 } |
| 1342 gtk_window_set_focus(GTK_WINDOW(GetNativeView()), NULL); |
| 1343 } |
| 1344 |
| 1345 void NativeWidgetGtk::FocusNativeView(gfx::NativeView native_view) { |
| 1346 if (native_view && !gtk_widget_is_focus(native_view)) |
| 1347 gtk_widget_grab_focus(native_view); |
| 1348 } |
| 1349 |
| 1350 bool NativeWidgetGtk::ConvertPointFromAncestor( |
| 1351 const Widget* ancestor, gfx::Point* point) const { |
| 1352 NOTREACHED(); |
| 1353 return false; |
| 1354 } |
| 1355 |
| 1356 gfx::Rect NativeWidgetGtk::GetWorkAreaBoundsInScreen() const { |
| 1357 return gfx::Screen::GetMonitorWorkAreaNearestWindow(GetNativeView()); |
| 1358 } |
| 1359 |
| 1360 void NativeWidgetGtk::SetInactiveRenderingDisabled(bool value) { |
| 1361 } |
| 1362 |
| 1363 //////////////////////////////////////////////////////////////////////////////// |
| 1364 // NativeWidgetGtk, protected: |
| 1365 |
| 1366 void NativeWidgetGtk::OnSizeRequest(GtkWidget* widget, |
| 1367 GtkRequisition* requisition) { |
| 1368 // Do only return the preferred size for child windows. GtkWindow interprets |
| 1369 // the requisition as a minimum size for top level windows, returning a |
| 1370 // preferred size for these would prevents us from setting smaller window |
| 1371 // sizes. |
| 1372 if (child_) { |
| 1373 gfx::Size size(GetWidget()->GetRootView()->GetPreferredSize()); |
| 1374 requisition->width = size.width(); |
| 1375 requisition->height = size.height(); |
| 1376 } |
| 1377 } |
| 1378 |
| 1379 void NativeWidgetGtk::OnSizeAllocate(GtkWidget* widget, |
| 1380 GtkAllocation* allocation) { |
| 1381 // See comment next to size_ as to why we do this. Also note, it's tempting |
| 1382 // to put this in the static method so subclasses don't need to worry about |
| 1383 // it, but if a subclasses needs to set a shape then they need to always |
| 1384 // reset the shape in this method regardless of whether the size changed. |
| 1385 gfx::Size new_size(allocation->width, allocation->height); |
| 1386 if (new_size == size_) |
| 1387 return; |
| 1388 size_ = new_size; |
| 1389 if (compositor_.get()) |
| 1390 compositor_->WidgetSizeChanged(size_); |
| 1391 delegate_->OnNativeWidgetSizeChanged(size_); |
| 1392 |
| 1393 if (GetWidget()->non_client_view()) { |
| 1394 // The Window's NonClientView may provide a custom shape for the Window. |
| 1395 gfx::Path window_mask; |
| 1396 GetWidget()->non_client_view()->GetWindowMask(gfx::Size(allocation->width, |
| 1397 allocation->height), |
| 1398 &window_mask); |
| 1399 GdkRegion* mask_region = window_mask.CreateNativeRegion(); |
| 1400 gdk_window_shape_combine_region(GetNativeView()->window, mask_region, 0, 0); |
| 1401 if (mask_region) |
| 1402 gdk_region_destroy(mask_region); |
| 1403 |
| 1404 SaveWindowPosition(); |
| 1405 } |
| 1406 } |
| 1407 |
| 1408 gboolean NativeWidgetGtk::OnPaint(GtkWidget* widget, GdkEventExpose* event) { |
| 1409 gdk_window_set_debug_updates(Widget::IsDebugPaintEnabled()); |
| 1410 |
| 1411 if (transparent_ && child_) { |
| 1412 // Clear the background before drawing any view and native components. |
| 1413 DrawTransparentBackground(widget, event); |
| 1414 if (!CompositePainter::IsComposited(widget_) && |
| 1415 gdk_screen_is_composited(gdk_screen_get_default())) { |
| 1416 // Let the parent draw the content only after something is drawn on |
| 1417 // the widget. |
| 1418 CompositePainter::SetComposited(widget_); |
| 1419 } |
| 1420 } |
| 1421 |
| 1422 ui::ScopedRegion region(gdk_region_copy(event->region)); |
| 1423 if (!gdk_region_empty(region.Get())) { |
| 1424 GdkRectangle clip_bounds; |
| 1425 gdk_region_get_clipbox(region.Get(), &clip_bounds); |
| 1426 if (!delegate_->OnNativeWidgetPaintAccelerated(gfx::Rect(clip_bounds))) { |
| 1427 gfx::CanvasSkiaPaint canvas(event); |
| 1428 if (!canvas.is_empty()) { |
| 1429 canvas.set_composite_alpha(is_transparent()); |
| 1430 delegate_->OnNativeWidgetPaint(&canvas); |
| 1431 } |
| 1432 } |
| 1433 } |
| 1434 |
| 1435 if (!painted_) { |
| 1436 painted_ = true; |
| 1437 if (!child_) |
| 1438 UpdateFreezeUpdatesProperty(GTK_WINDOW(widget_), false /* remove */); |
| 1439 } |
| 1440 return false; // False indicates other widgets should get the event as well. |
| 1441 } |
| 1442 |
| 1443 void NativeWidgetGtk::OnDragDataGet(GtkWidget* widget, |
| 1444 GdkDragContext* context, |
| 1445 GtkSelectionData* data, |
| 1446 guint info, |
| 1447 guint time) { |
| 1448 if (!drag_data_) { |
| 1449 NOTREACHED(); |
| 1450 return; |
| 1451 } |
| 1452 drag_data_->WriteFormatToSelection(info, data); |
| 1453 } |
| 1454 |
| 1455 void NativeWidgetGtk::OnDragDataReceived(GtkWidget* widget, |
| 1456 GdkDragContext* context, |
| 1457 gint x, |
| 1458 gint y, |
| 1459 GtkSelectionData* data, |
| 1460 guint info, |
| 1461 guint time) { |
| 1462 if (drop_target_.get()) |
| 1463 drop_target_->OnDragDataReceived(context, x, y, data, info, time); |
| 1464 } |
| 1465 |
| 1466 gboolean NativeWidgetGtk::OnDragDrop(GtkWidget* widget, |
| 1467 GdkDragContext* context, |
| 1468 gint x, |
| 1469 gint y, |
| 1470 guint time) { |
| 1471 if (drop_target_.get()) { |
| 1472 return drop_target_->OnDragDrop(context, x, y, time); |
| 1473 } |
| 1474 return FALSE; |
| 1475 } |
| 1476 |
| 1477 void NativeWidgetGtk::OnDragEnd(GtkWidget* widget, GdkDragContext* context) { |
| 1478 if (!drag_data_) { |
| 1479 // This indicates we didn't start a drag operation, and should never |
| 1480 // happen. |
| 1481 NOTREACHED(); |
| 1482 return; |
| 1483 } |
| 1484 // Quit the nested message loop we spawned in DoDrag. |
| 1485 MessageLoop::current()->Quit(); |
| 1486 } |
| 1487 |
| 1488 gboolean NativeWidgetGtk::OnDragFailed(GtkWidget* widget, |
| 1489 GdkDragContext* context, |
| 1490 GtkDragResult result) { |
| 1491 return FALSE; |
| 1492 } |
| 1493 |
| 1494 void NativeWidgetGtk::OnDragLeave(GtkWidget* widget, |
| 1495 GdkDragContext* context, |
| 1496 guint time) { |
| 1497 if (ignore_drag_leave_) { |
| 1498 ignore_drag_leave_ = false; |
| 1499 return; |
| 1500 } |
| 1501 if (drop_target_.get()) { |
| 1502 drop_target_->OnDragLeave(context, time); |
| 1503 drop_target_.reset(NULL); |
| 1504 } |
| 1505 } |
| 1506 |
| 1507 gboolean NativeWidgetGtk::OnDragMotion(GtkWidget* widget, |
| 1508 GdkDragContext* context, |
| 1509 gint x, |
| 1510 gint y, |
| 1511 guint time) { |
| 1512 if (!drop_target_.get()) { |
| 1513 drop_target_.reset(new DropTargetGtk( |
| 1514 reinterpret_cast<internal::RootView*>(GetWidget()->GetRootView()), |
| 1515 context)); |
| 1516 } |
| 1517 return drop_target_->OnDragMotion(context, x, y, time); |
| 1518 } |
| 1519 |
| 1520 gboolean NativeWidgetGtk::OnEnterNotify(GtkWidget* widget, |
| 1521 GdkEventCrossing* event) { |
| 1522 if (HasMouseCapture() && event->mode == GDK_CROSSING_GRAB) { |
| 1523 // Doing a grab results an async enter event, regardless of where the mouse |
| 1524 // is. We don't want to generate a mouse move in this case. |
| 1525 return false; |
| 1526 } |
| 1527 |
| 1528 if (!GetWidget()->last_mouse_event_was_move_ && |
| 1529 !GetWidget()->is_mouse_button_pressed_) { |
| 1530 // When a mouse button is pressed gtk generates a leave, enter, press. |
| 1531 // RootView expects to get a mouse move before a press, otherwise enter is |
| 1532 // not set. So we generate a move here. |
| 1533 GdkEventMotion motion = { GDK_MOTION_NOTIFY, event->window, |
| 1534 event->send_event, event->time, event->x, event->y, NULL, event->state, |
| 1535 0, NULL, event->x_root, event->y_root }; |
| 1536 |
| 1537 // If this event is the result of pressing a button then one of the button |
| 1538 // modifiers is set. Unset it as we're compensating for the leave generated |
| 1539 // when you press a button. |
| 1540 motion.state &= ~(GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK); |
| 1541 |
| 1542 MouseEvent mouse_event(TransformEvent(&motion)); |
| 1543 delegate_->OnMouseEvent(mouse_event); |
| 1544 } |
| 1545 return false; |
| 1546 } |
| 1547 |
| 1548 gboolean NativeWidgetGtk::OnLeaveNotify(GtkWidget* widget, |
| 1549 GdkEventCrossing* event) { |
| 1550 gdk_window_set_cursor(widget->window, gfx::GetCursor(GDK_LEFT_PTR)); |
| 1551 |
| 1552 GetWidget()->ResetLastMouseMoveFlag(); |
| 1553 |
| 1554 if (!HasMouseCapture() && !GetWidget()->is_mouse_button_pressed_) { |
| 1555 // Don't convert if the event is synthetic and has 0x0 coordinates. |
| 1556 if (event->x_root || event->y_root || event->x || event->y || |
| 1557 !event->send_event) { |
| 1558 TransformEvent(event); |
| 1559 } |
| 1560 MouseEvent mouse_event(reinterpret_cast<GdkEvent*>(event)); |
| 1561 delegate_->OnMouseEvent(mouse_event); |
| 1562 } |
| 1563 return false; |
| 1564 } |
| 1565 |
| 1566 gboolean NativeWidgetGtk::OnMotionNotify(GtkWidget* widget, |
| 1567 GdkEventMotion* event) { |
| 1568 if (GetWidget()->non_client_view()) { |
| 1569 GdkEventMotion transformed_event = *event; |
| 1570 TransformEvent(&transformed_event); |
| 1571 gfx::Point translated_location(transformed_event.x, transformed_event.y); |
| 1572 |
| 1573 // Update the cursor for the screen edge. |
| 1574 int hittest_code = |
| 1575 GetWidget()->non_client_view()->NonClientHitTest(translated_location); |
| 1576 if (hittest_code != HTCLIENT) { |
| 1577 GdkCursorType cursor_type = HitTestCodeToGdkCursorType(hittest_code); |
| 1578 gdk_window_set_cursor(widget->window, gfx::GetCursor(cursor_type)); |
| 1579 } |
| 1580 } |
| 1581 |
| 1582 MouseEvent mouse_event(TransformEvent(event)); |
| 1583 delegate_->OnMouseEvent(mouse_event); |
| 1584 return true; |
| 1585 } |
| 1586 |
| 1587 gboolean NativeWidgetGtk::OnButtonPress(GtkWidget* widget, |
| 1588 GdkEventButton* event) { |
| 1589 if (GetWidget()->non_client_view()) { |
| 1590 GdkEventButton transformed_event = *event; |
| 1591 MouseEvent mouse_event(TransformEvent(&transformed_event)); |
| 1592 |
| 1593 int hittest_code = GetWidget()->non_client_view()->NonClientHitTest( |
| 1594 mouse_event.location()); |
| 1595 switch (hittest_code) { |
| 1596 case HTCAPTION: { |
| 1597 // Start dragging if the mouse event is a single click and *not* a right |
| 1598 // click. If it is a right click, then pass it through to |
| 1599 // NativeWidgetGtk::OnButtonPress so that View class can show |
| 1600 // ContextMenu upon a mouse release event. We only start drag on single |
| 1601 // clicks as we get a crash in Gtk on double/triple clicks. |
| 1602 if (event->type == GDK_BUTTON_PRESS && |
| 1603 !mouse_event.IsOnlyRightMouseButton()) { |
| 1604 gfx::Point screen_point(event->x, event->y); |
| 1605 View::ConvertPointToScreen(GetWidget()->GetRootView(), &screen_point); |
| 1606 gtk_window_begin_move_drag(GetNativeWindow(), event->button, |
| 1607 screen_point.x(), screen_point.y(), |
| 1608 event->time); |
| 1609 return TRUE; |
| 1610 } |
| 1611 break; |
| 1612 } |
| 1613 case HTBOTTOM: |
| 1614 case HTBOTTOMLEFT: |
| 1615 case HTBOTTOMRIGHT: |
| 1616 case HTGROWBOX: |
| 1617 case HTLEFT: |
| 1618 case HTRIGHT: |
| 1619 case HTTOP: |
| 1620 case HTTOPLEFT: |
| 1621 case HTTOPRIGHT: { |
| 1622 gfx::Point screen_point(event->x, event->y); |
| 1623 View::ConvertPointToScreen(GetWidget()->GetRootView(), &screen_point); |
| 1624 // TODO(beng): figure out how to get a good minimum size. |
| 1625 gtk_widget_set_size_request(GetNativeView(), 100, 100); |
| 1626 gtk_window_begin_resize_drag(GetNativeWindow(), |
| 1627 HitTestCodeToGDKWindowEdge(hittest_code), |
| 1628 event->button, screen_point.x(), |
| 1629 screen_point.y(), event->time); |
| 1630 return TRUE; |
| 1631 } |
| 1632 default: |
| 1633 // Everything else falls into standard client event handling... |
| 1634 break; |
| 1635 } |
| 1636 } |
| 1637 |
| 1638 if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) { |
| 1639 // The sequence for double clicks is press, release, press, 2press, release. |
| 1640 // This means that at the time we get the second 'press' we don't know |
| 1641 // whether it corresponds to a double click or not. For now we're completely |
| 1642 // ignoring the 2press/3press events as they are duplicate. To make this |
| 1643 // work right we need to write our own code that detects if the press is a |
| 1644 // double/triple. For now we're completely punting, which means we always |
| 1645 // get single clicks. |
| 1646 // TODO: fix this. |
| 1647 return true; |
| 1648 } |
| 1649 |
| 1650 MouseEvent mouse_event(TransformEvent(event)); |
| 1651 // Returns true to consume the event when widget is not transparent. |
| 1652 return delegate_->OnMouseEvent(mouse_event) || !transparent_; |
| 1653 } |
| 1654 |
| 1655 gboolean NativeWidgetGtk::OnButtonRelease(GtkWidget* widget, |
| 1656 GdkEventButton* event) { |
| 1657 // GTK generates a mouse release at the end of dnd. We need to ignore it. |
| 1658 if (!drag_data_) { |
| 1659 MouseEvent mouse_event(TransformEvent(event)); |
| 1660 delegate_->OnMouseEvent(mouse_event); |
| 1661 } |
| 1662 return true; |
| 1663 } |
| 1664 |
| 1665 gboolean NativeWidgetGtk::OnScroll(GtkWidget* widget, GdkEventScroll* event) { |
| 1666 MouseWheelEvent mouse_event(TransformEvent(event)); |
| 1667 return delegate_->OnMouseEvent(mouse_event); |
| 1668 } |
| 1669 |
| 1670 gboolean NativeWidgetGtk::OnFocusIn(GtkWidget* widget, GdkEventFocus* event) { |
| 1671 if (has_focus_) |
| 1672 return false; // This is the second focus-in event in a row, ignore it. |
| 1673 has_focus_ = true; |
| 1674 |
| 1675 should_handle_menu_key_release_ = false; |
| 1676 |
| 1677 if (!GetWidget()->is_top_level()) |
| 1678 return false; |
| 1679 |
| 1680 // Only top-level Widget should have an InputMethod instance. |
| 1681 InputMethod* input_method = GetWidget()->GetInputMethod(); |
| 1682 if (input_method) |
| 1683 input_method->OnFocus(); |
| 1684 |
| 1685 // See description of got_initial_focus_in_ for details on this. |
| 1686 if (!got_initial_focus_in_) { |
| 1687 got_initial_focus_in_ = true; |
| 1688 // Sets initial focus here. On X11/Gtk, window creation |
| 1689 // is asynchronous and a focus request has to be made after a window |
| 1690 // gets created. |
| 1691 GetWidget()->SetInitialFocus(); |
| 1692 } |
| 1693 return false; |
| 1694 } |
| 1695 |
| 1696 gboolean NativeWidgetGtk::OnFocusOut(GtkWidget* widget, GdkEventFocus* event) { |
| 1697 if (!has_focus_) |
| 1698 return false; // This is the second focus-out event in a row, ignore it. |
| 1699 has_focus_ = false; |
| 1700 |
| 1701 if (!GetWidget()->is_top_level()) |
| 1702 return false; |
| 1703 |
| 1704 // Only top-level Widget should have an InputMethod instance. |
| 1705 InputMethod* input_method = GetWidget()->GetInputMethod(); |
| 1706 if (input_method) |
| 1707 input_method->OnBlur(); |
| 1708 return false; |
| 1709 } |
| 1710 |
| 1711 gboolean NativeWidgetGtk::OnEventKey(GtkWidget* widget, GdkEventKey* event) { |
| 1712 KeyEvent key(reinterpret_cast<GdkEvent*>(event)); |
| 1713 InputMethod* input_method = GetWidget()->GetInputMethod(); |
| 1714 if (input_method) |
| 1715 input_method->DispatchKeyEvent(key); |
| 1716 else |
| 1717 DispatchKeyEventPostIME(key); |
| 1718 |
| 1719 // Returns true to prevent GtkWindow's default key event handler. |
| 1720 return true; |
| 1721 } |
| 1722 |
| 1723 gboolean NativeWidgetGtk::OnQueryTooltip(GtkWidget* widget, |
| 1724 gint x, |
| 1725 gint y, |
| 1726 gboolean keyboard_mode, |
| 1727 GtkTooltip* tooltip) { |
| 1728 #if defined(TOUCH_UI) |
| 1729 return false; // Tell GTK not to draw tooltips as we draw tooltips in views |
| 1730 #else |
| 1731 return static_cast<TooltipManagerGtk*>(tooltip_manager_.get())-> |
| 1732 ShowTooltip(x, y, keyboard_mode, tooltip); |
| 1733 #endif |
| 1734 } |
| 1735 |
| 1736 gboolean NativeWidgetGtk::OnVisibilityNotify(GtkWidget* widget, |
| 1737 GdkEventVisibility* event) { |
| 1738 return false; |
| 1739 } |
| 1740 |
| 1741 gboolean NativeWidgetGtk::OnGrabBrokeEvent(GtkWidget* widget, GdkEvent* event) { |
| 1742 if (!has_pointer_grab_ && !has_keyboard_grab_) { |
| 1743 // We don't have any grabs; don't attempt to do anything. |
| 1744 return false; |
| 1745 } |
| 1746 |
| 1747 // Sent when either the keyboard or pointer grab is broke. We drop both grabs |
| 1748 // in this case. |
| 1749 if (event->grab_broken.keyboard) { |
| 1750 // Keyboard grab was broke. |
| 1751 has_keyboard_grab_ = false; |
| 1752 if (has_pointer_grab_) { |
| 1753 has_pointer_grab_ = false; |
| 1754 gdk_pointer_ungrab(GDK_CURRENT_TIME); |
| 1755 delegate_->OnMouseCaptureLost(); |
| 1756 } |
| 1757 } else { |
| 1758 // Mouse grab was broke. |
| 1759 has_pointer_grab_ = false; |
| 1760 if (has_keyboard_grab_) { |
| 1761 has_keyboard_grab_ = false; |
| 1762 gdk_keyboard_ungrab(GDK_CURRENT_TIME); |
| 1763 } |
| 1764 delegate_->OnMouseCaptureLost(); |
| 1765 } |
| 1766 ReleaseMouseCapture(); |
| 1767 |
| 1768 #if defined(TOUCH_UI) |
| 1769 ui::TouchFactory::GetInstance()->UngrabTouchDevices( |
| 1770 GDK_WINDOW_XDISPLAY(window_contents()->window)); |
| 1771 #endif |
| 1772 return false; // To let other widgets get the event. |
| 1773 } |
| 1774 |
| 1775 void NativeWidgetGtk::OnGrabNotify(GtkWidget* widget, gboolean was_grabbed) { |
| 1776 // Sent when gtk_grab_add changes. |
| 1777 if (!window_contents_) |
| 1778 return; // Grab broke after window destroyed, don't try processing it. |
| 1779 if (!was_grabbed) // Indicates we've been shadowed (lost grab). |
| 1780 HandleGtkGrabBroke(); |
| 1781 } |
| 1782 |
| 1783 void NativeWidgetGtk::OnDestroy(GtkWidget* object) { |
| 1784 signal_registrar_.reset(); |
| 1785 if (grab_notify_signal_id_) { |
| 1786 g_signal_handler_disconnect(window_contents_, grab_notify_signal_id_); |
| 1787 grab_notify_signal_id_ = 0; |
| 1788 } |
| 1789 delegate_->OnNativeWidgetDestroying(); |
| 1790 if (!child_) |
| 1791 ActiveWindowWatcherX::RemoveObserver(this); |
| 1792 // Note that this handler is hooked to GtkObject::destroy. |
| 1793 // NULL out pointers here since we might still be in an observer list |
| 1794 // until deletion happens. |
| 1795 widget_ = window_contents_ = NULL; |
| 1796 } |
| 1797 |
| 1798 void NativeWidgetGtk::OnDestroyed(GObject *where_the_object_was) { |
| 1799 delegate_->OnNativeWidgetDestroyed(); |
| 1800 if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET) |
| 1801 delete this; |
| 1802 } |
| 1803 |
| 1804 void NativeWidgetGtk::OnShow(GtkWidget* widget) { |
| 1805 delegate_->OnNativeWidgetVisibilityChanged(true); |
| 1806 } |
| 1807 |
| 1808 void NativeWidgetGtk::OnMap(GtkWidget* widget) { |
| 1809 #if defined(TOUCH_UI) |
| 1810 // Force an expose event to trigger OnPaint for touch. This is |
| 1811 // a workaround for a bug that X Expose event does not trigger |
| 1812 // Gdk's expose signal. This happens when you try to open views menu |
| 1813 // while a virtual keyboard gets kicked in or out. This seems to be |
| 1814 // a bug in message_pump_x.cc as we do get X Expose event but |
| 1815 // it doesn't trigger gtk's expose signal. We're not going to fix this |
| 1816 // as we're removing gtk and migrating to new compositor. |
| 1817 gdk_window_process_all_updates(); |
| 1818 #endif |
| 1819 } |
| 1820 |
| 1821 void NativeWidgetGtk::OnHide(GtkWidget* widget) { |
| 1822 delegate_->OnNativeWidgetVisibilityChanged(false); |
| 1823 } |
| 1824 |
| 1825 gboolean NativeWidgetGtk::OnWindowStateEvent(GtkWidget* widget, |
| 1826 GdkEventWindowState* event) { |
| 1827 if (GetWidget()->non_client_view() && |
| 1828 !(event->new_window_state & GDK_WINDOW_STATE_WITHDRAWN)) { |
| 1829 SaveWindowPosition(); |
| 1830 } |
| 1831 window_state_ = event->new_window_state; |
| 1832 return FALSE; |
| 1833 } |
| 1834 |
| 1835 gboolean NativeWidgetGtk::OnConfigureEvent(GtkWidget* widget, |
| 1836 GdkEventConfigure* event) { |
| 1837 SaveWindowPosition(); |
| 1838 return FALSE; |
| 1839 } |
| 1840 |
| 1841 void NativeWidgetGtk::HandleGtkGrabBroke() { |
| 1842 ReleaseMouseCapture(); |
| 1843 delegate_->OnMouseCaptureLost(); |
| 1844 } |
| 1845 |
| 1846 //////////////////////////////////////////////////////////////////////////////// |
| 1847 // NativeWidgetGtk, private: |
| 1848 |
| 1849 void NativeWidgetGtk::ScheduleDraw() { |
| 1850 SchedulePaintInRect(gfx::Rect(gfx::Point(), size_)); |
| 1851 } |
| 1852 |
| 1853 void NativeWidgetGtk::DispatchKeyEventPostIME(const KeyEvent& key) { |
| 1854 // Always reset |should_handle_menu_key_release_| unless we are handling a |
| 1855 // VKEY_MENU key release event. It ensures that VKEY_MENU accelerator can only |
| 1856 // be activated when handling a VKEY_MENU key release event which is preceded |
| 1857 // by an unhandled VKEY_MENU key press event. See also HandleKeyboardEvent(). |
| 1858 if (key.key_code() != ui::VKEY_MENU || key.type() != ui::ET_KEY_RELEASED) |
| 1859 should_handle_menu_key_release_ = false; |
| 1860 |
| 1861 // Send the key event to View hierarchy first. |
| 1862 bool handled = delegate_->OnKeyEvent(key); |
| 1863 |
| 1864 if (key.key_code() == ui::VKEY_PROCESSKEY || handled) |
| 1865 return; |
| 1866 |
| 1867 // Dispatch the key event to native GtkWidget hierarchy. |
| 1868 // To prevent GtkWindow from handling the key event as a keybinding, we need |
| 1869 // to bypass GtkWindow's default key event handler and dispatch the event |
| 1870 // here. |
| 1871 GdkEventKey* event = reinterpret_cast<GdkEventKey*>(key.gdk_event()); |
| 1872 if (!handled && event && GTK_IS_WINDOW(widget_)) |
| 1873 handled = gtk_window_propagate_key_event(GTK_WINDOW(widget_), event); |
| 1874 |
| 1875 // On Linux, in order to handle VKEY_MENU (Alt) accelerator key correctly and |
| 1876 // avoid issues like: http://crbug.com/40966 and http://crbug.com/49701, we |
| 1877 // should only send the key event to the focus manager if it's not handled by |
| 1878 // any View or native GtkWidget. |
| 1879 // The flow is different when the focus is in a RenderWidgetHostViewGtk, which |
| 1880 // always consumes the key event and send it back to us later by calling |
| 1881 // HandleKeyboardEvent() directly, if it's not handled by webkit. |
| 1882 if (!handled) |
| 1883 handled = HandleKeyboardEvent(key); |
| 1884 |
| 1885 // Dispatch the key event for bindings processing. |
| 1886 if (!handled && event && GTK_IS_WINDOW(widget_)) |
| 1887 gtk_bindings_activate_event(GTK_OBJECT(widget_), event); |
| 1888 } |
| 1889 |
| 1890 void NativeWidgetGtk::SetInitParams(const Widget::InitParams& params) { |
| 1891 DCHECK(!GetNativeView()); |
| 1892 |
| 1893 ownership_ = params.ownership; |
| 1894 child_ = params.child; |
| 1895 is_menu_ = params.type == Widget::InitParams::TYPE_MENU; |
| 1896 |
| 1897 // TODO(beng): The secondary checks here actually obviate the need for |
| 1898 // params.transient but that's only because NativeWidgetGtk |
| 1899 // considers any top-level widget to be a transient widget. We |
| 1900 // will probably want to ammend this assumption at some point. |
| 1901 if (params.transient || params.parent || params.parent_widget) |
| 1902 transient_to_parent_ = true; |
| 1903 if (params.transparent) |
| 1904 MakeTransparent(); |
| 1905 if (!params.accept_events && !child_) |
| 1906 ignore_events_ = true; |
| 1907 if (params.double_buffer) |
| 1908 EnableDoubleBuffer(true); |
| 1909 } |
| 1910 |
| 1911 gboolean NativeWidgetGtk::OnWindowPaint(GtkWidget* widget, |
| 1912 GdkEventExpose* event) { |
| 1913 // Clear the background to be totally transparent. We don't need to |
| 1914 // paint the root view here as that is done by OnPaint. |
| 1915 DCHECK(transparent_); |
| 1916 DrawTransparentBackground(widget, event); |
| 1917 // The Keyboard layout view has a renderer that covers the entire |
| 1918 // window, which prevents OnPaint from being called on window_contents_, |
| 1919 // so we need to remove the FREEZE_UPDATES property here. |
| 1920 if (!painted_) { |
| 1921 painted_ = true; |
| 1922 UpdateFreezeUpdatesProperty(GTK_WINDOW(widget_), false /* remove */); |
| 1923 } |
| 1924 return false; |
| 1925 } |
| 1926 |
| 1927 void NativeWidgetGtk::OnChildExpose(GtkWidget* child) { |
| 1928 DCHECK(!child_); |
| 1929 if (!painted_) { |
| 1930 painted_ = true; |
| 1931 UpdateFreezeUpdatesProperty(GTK_WINDOW(widget_), false /* remove */); |
| 1932 } |
| 1933 RemoveExposeHandlerIfExists(child); |
| 1934 } |
| 1935 |
| 1936 // static |
| 1937 gboolean NativeWidgetGtk::ChildExposeHandler(GtkWidget* widget, |
| 1938 GdkEventExpose* event) { |
| 1939 GtkWidget* toplevel = gtk_widget_get_ancestor(widget, GTK_TYPE_WINDOW); |
| 1940 CHECK(toplevel); |
| 1941 Widget* views_widget = Widget::GetWidgetForNativeView(toplevel); |
| 1942 CHECK(views_widget); |
| 1943 NativeWidgetGtk* widget_gtk = |
| 1944 static_cast<NativeWidgetGtk*>(views_widget->native_widget()); |
| 1945 widget_gtk->OnChildExpose(widget); |
| 1946 return false; |
| 1947 } |
| 1948 |
| 1949 void NativeWidgetGtk::CreateGtkWidget(const Widget::InitParams& params) { |
| 1950 // We turn off double buffering for two reasons: |
| 1951 // 1. We draw to a canvas then composite to the screen, which means we're |
| 1952 // doing our own double buffering already. |
| 1953 // 2. GTKs double buffering clips to the dirty region. RootView occasionally |
| 1954 // needs to expand the paint region (see RootView::OnPaint). This means |
| 1955 // that if we use GTK's double buffering and we tried to expand the dirty |
| 1956 // region, it wouldn't get painted. |
| 1957 if (child_) { |
| 1958 window_contents_ = widget_ = gtk_views_fixed_new(); |
| 1959 gtk_widget_set_name(widget_, "views-gtkwidget-child-fixed"); |
| 1960 if (!is_double_buffered_) |
| 1961 GTK_WIDGET_UNSET_FLAGS(widget_, GTK_DOUBLE_BUFFERED); |
| 1962 gtk_fixed_set_has_window(GTK_FIXED(widget_), true); |
| 1963 if (!params.parent && !null_parent_) { |
| 1964 GtkWidget* popup = gtk_window_new(GTK_WINDOW_POPUP); |
| 1965 null_parent_ = gtk_fixed_new(); |
| 1966 gtk_widget_set_name(null_parent_, "views-gtkwidget-null-parent"); |
| 1967 gtk_container_add(GTK_CONTAINER(popup), null_parent_); |
| 1968 gtk_widget_realize(null_parent_); |
| 1969 } |
| 1970 if (transparent_) { |
| 1971 // transparency has to be configured before widget is realized. |
| 1972 DCHECK(params.parent) << |
| 1973 "Transparent widget must have parent when initialized"; |
| 1974 ConfigureWidgetForTransparentBackground(params.parent); |
| 1975 } |
| 1976 gtk_container_add( |
| 1977 GTK_CONTAINER(params.parent ? params.parent : null_parent_), widget_); |
| 1978 gtk_widget_realize(widget_); |
| 1979 if (transparent_) { |
| 1980 // The widget has to be realized to set composited flag. |
| 1981 // I tried "realize" signal to set this flag, but it did not work |
| 1982 // when the top level is popup. |
| 1983 DCHECK(GTK_WIDGET_REALIZED(widget_)); |
| 1984 gdk_window_set_composited(widget_->window, true); |
| 1985 } |
| 1986 if (params.parent && !params.bounds.size().IsEmpty()) { |
| 1987 // Make sure that an widget is given it's initial size before |
| 1988 // we're done initializing, to take care of some potential |
| 1989 // corner cases when programmatically arranging hierarchies as |
| 1990 // seen in |
| 1991 // http://code.google.com/p/chromium-os/issues/detail?id=5987 |
| 1992 |
| 1993 // This can't be done without a parent present, or stale data |
| 1994 // might show up on the screen as seen in |
| 1995 // http://code.google.com/p/chromium/issues/detail?id=53870 |
| 1996 GtkAllocation alloc = |
| 1997 { 0, 0, params.bounds.width(), params.bounds.height() }; |
| 1998 gtk_widget_size_allocate(widget_, &alloc); |
| 1999 } |
| 2000 if (params.type == Widget::InitParams::TYPE_CONTROL) { |
| 2001 // Controls are initially visible. |
| 2002 gtk_widget_show(widget_); |
| 2003 } |
| 2004 } else { |
| 2005 Widget::InitParams::Type type = params.type; |
| 2006 if (type == Widget::InitParams::TYPE_BUBBLE && |
| 2007 params.delegate->AsBubbleDelegate() && |
| 2008 params.delegate->AsBubbleDelegate()->use_focusless()) { |
| 2009 // Handles focusless bubble type, which are bubbles that should |
| 2010 // act like popups rather than gtk windows. They do not get focus |
| 2011 // and are not controlled by window manager placement. |
| 2012 type = Widget::InitParams::TYPE_POPUP; |
| 2013 } |
| 2014 |
| 2015 // Use our own window class to override GtkWindow's move_focus method. |
| 2016 widget_ = gtk_views_window_new(WindowTypeToGtkWindowType(type)); |
| 2017 gtk_widget_set_name(widget_, "views-gtkwidget-window"); |
| 2018 if (transient_to_parent_) { |
| 2019 gtk_window_set_transient_for(GTK_WINDOW(widget_), |
| 2020 GTK_WINDOW(params.parent)); |
| 2021 } |
| 2022 GTK_WIDGET_UNSET_FLAGS(widget_, GTK_DOUBLE_BUFFERED); |
| 2023 |
| 2024 // Gtk determines the size for windows based on the requested size of the |
| 2025 // child. For NativeWidgetGtk the child is a fixed. If the fixed ends up |
| 2026 // with a child widget it's possible the child widget will drive the |
| 2027 // requested size of the widget, which we don't want. We explicitly set a |
| 2028 // value of 1x1 here so that gtk doesn't attempt to resize the window if we |
| 2029 // end up with a situation where the requested size of a child of the fixed |
| 2030 // is greater than the size of the window. By setting the size in this |
| 2031 // manner we're also allowing users of WidgetGtk to change the requested |
| 2032 // size at any time. |
| 2033 gtk_widget_set_size_request(widget_, 1, 1); |
| 2034 |
| 2035 if (!params.bounds.size().IsEmpty()) { |
| 2036 // When we realize the window, the window manager is given a size. If we |
| 2037 // don't specify a size before then GTK defaults to 200x200. Specify |
| 2038 // a size now so that the window manager sees the requested size. |
| 2039 GtkAllocation alloc = |
| 2040 { 0, 0, params.bounds.width(), params.bounds.height() }; |
| 2041 gtk_widget_size_allocate(widget_, &alloc); |
| 2042 } |
| 2043 gtk_window_set_decorated(GTK_WINDOW(widget_), false); |
| 2044 // We'll take care of positioning our window. |
| 2045 gtk_window_set_position(GTK_WINDOW(widget_), GTK_WIN_POS_NONE); |
| 2046 |
| 2047 window_contents_ = gtk_views_fixed_new(); |
| 2048 gtk_widget_set_name(window_contents_, "views-gtkwidget-window-fixed"); |
| 2049 if (!is_double_buffered_) |
| 2050 GTK_WIDGET_UNSET_FLAGS(window_contents_, GTK_DOUBLE_BUFFERED); |
| 2051 gtk_fixed_set_has_window(GTK_FIXED(window_contents_), true); |
| 2052 gtk_container_add(GTK_CONTAINER(widget_), window_contents_); |
| 2053 gtk_widget_show(window_contents_); |
| 2054 g_object_set_data(G_OBJECT(window_contents_), kNativeWidgetKey, |
| 2055 static_cast<NativeWidgetGtk*>(this)); |
| 2056 if (transparent_) |
| 2057 ConfigureWidgetForTransparentBackground(NULL); |
| 2058 |
| 2059 if (ignore_events_) |
| 2060 ConfigureWidgetForIgnoreEvents(); |
| 2061 |
| 2062 // Realize the window_contents_ so that we can always get a handle for |
| 2063 // acceleration. Without this we need to check every time paint is |
| 2064 // invoked. |
| 2065 gtk_widget_realize(window_contents_); |
| 2066 |
| 2067 SetAlwaysOnTop(always_on_top_); |
| 2068 // UpdateFreezeUpdatesProperty will realize the widget and handlers like |
| 2069 // size-allocate will function properly. |
| 2070 UpdateFreezeUpdatesProperty(GTK_WINDOW(widget_), true /* add */); |
| 2071 } |
| 2072 SetNativeWindowProperty(kNativeWidgetKey, this); |
| 2073 } |
| 2074 |
| 2075 void NativeWidgetGtk::ConfigureWidgetForTransparentBackground( |
| 2076 GtkWidget* parent) { |
| 2077 DCHECK(widget_ && window_contents_); |
| 2078 |
| 2079 GdkColormap* rgba_colormap = |
| 2080 gdk_screen_get_rgba_colormap(gtk_widget_get_screen(widget_)); |
| 2081 if (!rgba_colormap) { |
| 2082 transparent_ = false; |
| 2083 return; |
| 2084 } |
| 2085 // To make the background transparent we need to install the RGBA colormap |
| 2086 // on both the window and fixed. In addition we need to make sure no |
| 2087 // decorations are drawn. The last bit is to make sure the widget doesn't |
| 2088 // attempt to draw a pixmap in it's background. |
| 2089 if (!child_) { |
| 2090 DCHECK(parent == NULL); |
| 2091 gtk_widget_set_colormap(widget_, rgba_colormap); |
| 2092 gtk_widget_set_app_paintable(widget_, true); |
| 2093 signal_registrar_->Connect(widget_, "expose_event", |
| 2094 G_CALLBACK(&OnWindowPaintThunk), this); |
| 2095 gtk_widget_realize(widget_); |
| 2096 gdk_window_set_decorations(widget_->window, |
| 2097 static_cast<GdkWMDecoration>(0)); |
| 2098 } else { |
| 2099 DCHECK(parent); |
| 2100 CompositePainter::AddCompositePainter(parent); |
| 2101 } |
| 2102 DCHECK(!GTK_WIDGET_REALIZED(window_contents_)); |
| 2103 gtk_widget_set_colormap(window_contents_, rgba_colormap); |
| 2104 } |
| 2105 |
| 2106 void NativeWidgetGtk::ConfigureWidgetForIgnoreEvents() { |
| 2107 gtk_widget_realize(widget_); |
| 2108 GdkWindow* gdk_window = widget_->window; |
| 2109 Display* display = GDK_WINDOW_XDISPLAY(gdk_window); |
| 2110 XID win = GDK_WINDOW_XID(gdk_window); |
| 2111 |
| 2112 // This sets the clickable area to be empty, allowing all events to be |
| 2113 // passed to any windows behind this one. |
| 2114 XShapeCombineRectangles( |
| 2115 display, |
| 2116 win, |
| 2117 ShapeInput, |
| 2118 0, // x offset |
| 2119 0, // y offset |
| 2120 NULL, // rectangles |
| 2121 0, // num rectangles |
| 2122 ShapeSet, |
| 2123 0); |
| 2124 } |
| 2125 |
| 2126 void NativeWidgetGtk::DrawTransparentBackground(GtkWidget* widget, |
| 2127 GdkEventExpose* event) { |
| 2128 cairo_t* cr = gdk_cairo_create(widget->window); |
| 2129 cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); |
| 2130 gdk_cairo_region(cr, event->region); |
| 2131 cairo_fill(cr); |
| 2132 cairo_destroy(cr); |
| 2133 } |
| 2134 |
| 2135 void NativeWidgetGtk::SaveWindowPosition() { |
| 2136 // The delegate may have gone away on us. |
| 2137 if (!GetWidget()->widget_delegate()) |
| 2138 return; |
| 2139 |
| 2140 ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL; |
| 2141 if (IsMaximized()) |
| 2142 show_state = ui::SHOW_STATE_MAXIMIZED; |
| 2143 else if (IsMinimized()) |
| 2144 show_state = ui::SHOW_STATE_MINIMIZED; |
| 2145 GetWidget()->widget_delegate()->SaveWindowPlacement( |
| 2146 GetWidget()->GetWindowScreenBounds(), |
| 2147 show_state); |
| 2148 } |
| 2149 |
| 2150 //////////////////////////////////////////////////////////////////////////////// |
| 2151 // Widget, public: |
| 2152 |
| 2153 // static |
| 2154 void Widget::NotifyLocaleChanged() { |
| 2155 GList *window_list = gtk_window_list_toplevels(); |
| 2156 for (GList* element = window_list; element; element = g_list_next(element)) { |
| 2157 Widget* widget = |
| 2158 Widget::GetWidgetForNativeWindow(GTK_WINDOW(element->data)); |
| 2159 if (widget) |
| 2160 widget->LocaleChanged(); |
| 2161 } |
| 2162 g_list_free(window_list); |
| 2163 } |
| 2164 |
| 2165 // static |
| 2166 void Widget::CloseAllSecondaryWidgets() { |
| 2167 GList* windows = gtk_window_list_toplevels(); |
| 2168 for (GList* window = windows; window; |
| 2169 window = g_list_next(window)) { |
| 2170 Widget* widget = Widget::GetWidgetForNativeView(GTK_WIDGET(window->data)); |
| 2171 if (widget && widget->is_secondary_widget()) |
| 2172 widget->Close(); |
| 2173 } |
| 2174 g_list_free(windows); |
| 2175 } |
| 2176 |
| 2177 // static |
| 2178 bool Widget::ConvertRect(const Widget* source, |
| 2179 const Widget* target, |
| 2180 gfx::Rect* rect) { |
| 2181 DCHECK(source); |
| 2182 DCHECK(target); |
| 2183 DCHECK(rect); |
| 2184 |
| 2185 // TODO(oshima): Add check if source and target belongs to the same |
| 2186 // screen. |
| 2187 |
| 2188 if (source == target) |
| 2189 return true; |
| 2190 if (!source || !target) |
| 2191 return false; |
| 2192 |
| 2193 gfx::Point source_point = source->GetWindowScreenBounds().origin(); |
| 2194 gfx::Point target_point = target->GetWindowScreenBounds().origin(); |
| 2195 |
| 2196 rect->set_origin( |
| 2197 source_point.Subtract(target_point).Add(rect->origin())); |
| 2198 return true; |
| 2199 } |
| 2200 |
| 2201 namespace internal { |
| 2202 |
| 2203 //////////////////////////////////////////////////////////////////////////////// |
| 2204 // NativeWidgetPrivate, public: |
| 2205 |
| 2206 // static |
| 2207 NativeWidgetPrivate* NativeWidgetPrivate::CreateNativeWidget( |
| 2208 NativeWidgetDelegate* delegate) { |
| 2209 return new NativeWidgetGtk(delegate); |
| 2210 } |
| 2211 |
| 2212 // static |
| 2213 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView( |
| 2214 gfx::NativeView native_view) { |
| 2215 if (!native_view) |
| 2216 return NULL; |
| 2217 return reinterpret_cast<NativeWidgetGtk*>( |
| 2218 g_object_get_data(G_OBJECT(native_view), kNativeWidgetKey)); |
| 2219 } |
| 2220 |
| 2221 // static |
| 2222 NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( |
| 2223 gfx::NativeWindow native_window) { |
| 2224 if (!native_window) |
| 2225 return NULL; |
| 2226 return reinterpret_cast<NativeWidgetGtk*>( |
| 2227 g_object_get_data(G_OBJECT(native_window), kNativeWidgetKey)); |
| 2228 } |
| 2229 |
| 2230 // static |
| 2231 NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget( |
| 2232 gfx::NativeView native_view) { |
| 2233 if (!native_view) |
| 2234 return NULL; |
| 2235 |
| 2236 NativeWidgetPrivate* widget = NULL; |
| 2237 |
| 2238 GtkWidget* parent_gtkwidget = native_view; |
| 2239 NativeWidgetPrivate* parent_widget; |
| 2240 do { |
| 2241 parent_widget = GetNativeWidgetForNativeView(parent_gtkwidget); |
| 2242 if (parent_widget) |
| 2243 widget = parent_widget; |
| 2244 parent_gtkwidget = gtk_widget_get_parent(parent_gtkwidget); |
| 2245 } while (parent_gtkwidget); |
| 2246 |
| 2247 return widget && widget->GetWidget()->is_top_level() ? widget : NULL; |
| 2248 } |
| 2249 |
| 2250 // static |
| 2251 void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, |
| 2252 Widget::Widgets* children) { |
| 2253 if (!native_view) |
| 2254 return; |
| 2255 |
| 2256 Widget* widget = Widget::GetWidgetForNativeView(native_view); |
| 2257 if (widget) |
| 2258 children->insert(widget); |
| 2259 gtk_container_foreach(GTK_CONTAINER(native_view), |
| 2260 EnumerateChildWidgetsForNativeWidgets, |
| 2261 reinterpret_cast<gpointer>(children)); |
| 2262 } |
| 2263 |
| 2264 // static |
| 2265 void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, |
| 2266 gfx::NativeView new_parent) { |
| 2267 if (!native_view) |
| 2268 return; |
| 2269 |
| 2270 gfx::NativeView previous_parent = gtk_widget_get_parent(native_view); |
| 2271 if (previous_parent == new_parent) |
| 2272 return; |
| 2273 |
| 2274 Widget::Widgets widgets; |
| 2275 GetAllChildWidgets(native_view, &widgets); |
| 2276 |
| 2277 // First notify all the widgets that they are being disassociated |
| 2278 // from their previous parent. |
| 2279 for (Widget::Widgets::iterator it = widgets.begin(); |
| 2280 it != widgets.end(); ++it) { |
| 2281 // TODO(beng): Rename this notification to NotifyNativeViewChanging() |
| 2282 // and eliminate the bool parameter. |
| 2283 (*it)->NotifyNativeViewHierarchyChanged(false, previous_parent); |
| 2284 } |
| 2285 |
| 2286 if (gtk_widget_get_parent(native_view)) |
| 2287 gtk_widget_reparent(native_view, new_parent); |
| 2288 else |
| 2289 gtk_container_add(GTK_CONTAINER(new_parent), native_view); |
| 2290 |
| 2291 // And now, notify them that they have a brand new parent. |
| 2292 for (Widget::Widgets::iterator it = widgets.begin(); |
| 2293 it != widgets.end(); ++it) { |
| 2294 (*it)->NotifyNativeViewHierarchyChanged(true, new_parent); |
| 2295 } |
| 2296 } |
| 2297 |
| 2298 // static |
| 2299 bool NativeWidgetPrivate::IsMouseButtonDown() { |
| 2300 bool button_pressed = false; |
| 2301 GdkEvent* event = gtk_get_current_event(); |
| 2302 if (event) { |
| 2303 button_pressed = event->type == GDK_BUTTON_PRESS || |
| 2304 event->type == GDK_2BUTTON_PRESS || |
| 2305 event->type == GDK_3BUTTON_PRESS; |
| 2306 gdk_event_free(event); |
| 2307 } |
| 2308 return button_pressed; |
| 2309 } |
| 2310 |
| 2311 } // namespace internal |
| 2312 } // namespace views |
OLD | NEW |