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 |