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

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

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

Powered by Google App Engine
This is Rietveld 408576698