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

Side by Side Diff: views/widget/native_widget_gtk.cc

Issue 8598031: views: Move widget/ directory to ui/views. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: reland for real Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « views/widget/native_widget_gtk.h ('k') | views/widget/native_widget_private.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « views/widget/native_widget_gtk.h ('k') | views/widget/native_widget_private.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698