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

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

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

Powered by Google App Engine
This is Rietveld 408576698