Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / gtk / apps / native_app_window_gtk.cc
blobed2cae6a4a9a467c50bb2b2358650c862fc105c3
1 // Copyright 2013 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.
5 #include "chrome/browser/ui/gtk/apps/native_app_window_gtk.h"
7 #include <gdk/gdkx.h>
8 #include <vector>
10 #include "base/message_loop/message_pump_gtk.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/gtk/extensions/extension_keybinding_registry_gtk.h"
14 #include "chrome/browser/ui/gtk/gtk_util.h"
15 #include "chrome/browser/ui/gtk/gtk_window_util.h"
16 #include "chrome/browser/web_applications/web_app.h"
17 #include "content/public/browser/render_view_host.h"
18 #include "content/public/browser/render_widget_host_view.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/browser/web_contents_view.h"
21 #include "extensions/common/extension.h"
22 #include "ui/base/x/active_window_watcher_x.h"
23 #include "ui/gfx/gtk_util.h"
24 #include "ui/gfx/image/image.h"
25 #include "ui/gfx/rect.h"
27 using apps::ShellWindow;
29 namespace {
31 // The timeout in milliseconds before we'll get the true window position with
32 // gtk_window_get_position() after the last GTK configure-event signal.
33 const int kDebounceTimeoutMilliseconds = 100;
35 const char* kAtomsToCache[] = {
36 "_NET_WM_STATE",
37 "_NET_WM_STATE_HIDDEN",
38 NULL
41 } // namespace
43 NativeAppWindowGtk::NativeAppWindowGtk(ShellWindow* shell_window,
44 const ShellWindow::CreateParams& params)
45 : shell_window_(shell_window),
46 window_(NULL),
47 state_(GDK_WINDOW_STATE_WITHDRAWN),
48 is_active_(false),
49 content_thinks_its_fullscreen_(false),
50 maximize_pending_(false),
51 frameless_(params.frame == ShellWindow::FRAME_NONE),
52 always_on_top_(params.always_on_top),
53 frame_cursor_(NULL),
54 atom_cache_(base::MessagePumpGtk::GetDefaultXDisplay(), kAtomsToCache),
55 is_x_event_listened_(false) {
56 Observe(web_contents());
58 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
60 gfx::NativeView native_view =
61 web_contents()->GetView()->GetNativeView();
62 gtk_container_add(GTK_CONTAINER(window_), native_view);
64 if (params.bounds.x() != INT_MIN && params.bounds.y() != INT_MIN)
65 gtk_window_move(window_, params.bounds.x(), params.bounds.y());
67 // This is done to avoid a WM "feature" where setting the window size to
68 // the monitor size causes the WM to set the EWMH for full screen mode.
69 int win_height = params.bounds.height();
70 if (frameless_ &&
71 gtk_window_util::BoundsMatchMonitorSize(window_, params.bounds)) {
72 win_height -= 1;
74 gtk_window_set_default_size(window_, params.bounds.width(), win_height);
76 resizable_ = params.resizable;
77 if (!resizable_) {
78 // If the window doesn't have a size request when we set resizable to
79 // false, GTK will shrink the window to 1x1px.
80 gtk_widget_set_size_request(GTK_WIDGET(window_),
81 params.bounds.width(), win_height);
82 gtk_window_set_resizable(window_, FALSE);
85 // make sure bounds_ and restored_bounds_ have correct values until we
86 // get our first configure-event
87 bounds_ = restored_bounds_ = params.bounds;
88 gint x, y;
89 gtk_window_get_position(window_, &x, &y);
90 bounds_.set_origin(gfx::Point(x, y));
92 // Hide titlebar when {frame: 'none'} specified on ShellWindow.
93 if (frameless_)
94 gtk_window_set_decorated(window_, false);
96 if (always_on_top_)
97 gtk_window_set_keep_above(window_, TRUE);
99 UpdateWindowMinMaxSize();
101 // In some (older) versions of compiz, raising top-level windows when they
102 // are partially off-screen causes them to get snapped back on screen, not
103 // always even on the current virtual desktop. If we are running under
104 // compiz, suppress such raises, as they are not necessary in compiz anyway.
105 if (ui::GuessWindowManager() == ui::WM_COMPIZ)
106 suppress_window_raise_ = true;
108 gtk_window_set_title(window_, extension()->name().c_str());
110 std::string app_name = web_app::GenerateApplicationNameFromExtensionId(
111 extension()->id());
112 gtk_window_util::SetWindowCustomClass(window_,
113 web_app::GetWMClassFromAppName(app_name));
115 g_signal_connect(window_, "delete-event",
116 G_CALLBACK(OnMainWindowDeleteEventThunk), this);
117 g_signal_connect(window_, "configure-event",
118 G_CALLBACK(OnConfigureThunk), this);
119 g_signal_connect(window_, "window-state-event",
120 G_CALLBACK(OnWindowStateThunk), this);
121 if (frameless_) {
122 g_signal_connect(window_, "button-press-event",
123 G_CALLBACK(OnButtonPressThunk), this);
124 g_signal_connect(window_, "motion-notify-event",
125 G_CALLBACK(OnMouseMoveEventThunk), this);
128 // If _NET_WM_STATE_HIDDEN is in _NET_SUPPORTED, listen for XEvent to work
129 // around GTK+ not reporting minimization state changes. See comment in the
130 // |OnXEvent|.
131 std::vector< ::Atom> supported_atoms;
132 if (ui::GetAtomArrayProperty(ui::GetX11RootWindow(),
133 "_NET_SUPPORTED",
134 &supported_atoms)) {
135 if (std::find(supported_atoms.begin(),
136 supported_atoms.end(),
137 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN")) !=
138 supported_atoms.end()) {
139 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(window_));
140 gdk_window_add_filter(window,
141 &NativeAppWindowGtk::OnXEventThunk,
142 this);
143 is_x_event_listened_ = true;
147 // Add the keybinding registry.
148 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk(
149 shell_window_->profile(),
150 window_,
151 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
152 shell_window_));
154 ui::ActiveWindowWatcherX::AddObserver(this);
157 NativeAppWindowGtk::~NativeAppWindowGtk() {
158 ui::ActiveWindowWatcherX::RemoveObserver(this);
159 if (is_x_event_listened_) {
160 gdk_window_remove_filter(NULL,
161 &NativeAppWindowGtk::OnXEventThunk,
162 this);
166 bool NativeAppWindowGtk::IsActive() const {
167 if (ui::ActiveWindowWatcherX::WMSupportsActivation())
168 return is_active_;
170 // This still works even though we don't get the activation notification.
171 return gtk_window_is_active(window_);
174 bool NativeAppWindowGtk::IsMaximized() const {
175 return (state_ & GDK_WINDOW_STATE_MAXIMIZED);
178 bool NativeAppWindowGtk::IsMinimized() const {
179 return (state_ & GDK_WINDOW_STATE_ICONIFIED);
182 bool NativeAppWindowGtk::IsFullscreen() const {
183 return (state_ & GDK_WINDOW_STATE_FULLSCREEN);
186 gfx::NativeWindow NativeAppWindowGtk::GetNativeWindow() {
187 return window_;
190 gfx::Rect NativeAppWindowGtk::GetRestoredBounds() const {
191 gfx::Rect window_bounds = restored_bounds_;
192 window_bounds.Inset(-GetFrameInsets());
193 return window_bounds;
196 ui::WindowShowState NativeAppWindowGtk::GetRestoredState() const {
197 if (IsMaximized())
198 return ui::SHOW_STATE_MAXIMIZED;
199 if (IsFullscreen())
200 return ui::SHOW_STATE_FULLSCREEN;
201 return ui::SHOW_STATE_NORMAL;
204 gfx::Rect NativeAppWindowGtk::GetBounds() const {
205 // :GetBounds() is expecting the outer window bounds to be returned (ie.
206 // including window decorations). The internal |bounds_| is not including them
207 // and trying to add the decoration to |bounds_| would require calling
208 // gdk_window_get_frame_extents. The best thing to do is to directly get the
209 // frame bounds and only use the internal value if we can't.
210 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
211 if (!gdk_window)
212 return bounds_;
214 GdkRectangle window_bounds = {0};
215 gdk_window_get_frame_extents(gdk_window, &window_bounds);
216 return gfx::Rect(window_bounds.x, window_bounds.y,
217 window_bounds.width, window_bounds.height);
220 void NativeAppWindowGtk::Show() {
221 gtk_window_present(window_);
224 void NativeAppWindowGtk::ShowInactive() {
225 gtk_window_set_focus_on_map(window_, false);
226 gtk_widget_show(GTK_WIDGET(window_));
229 void NativeAppWindowGtk::Hide() {
230 gtk_widget_hide(GTK_WIDGET(window_));
233 void NativeAppWindowGtk::Close() {
234 shell_window_->OnNativeWindowChanged();
236 // Cancel any pending callback from the window configure debounce timer.
237 window_configure_debounce_timer_.Stop();
239 GtkWidget* window = GTK_WIDGET(window_);
240 // To help catch bugs in any event handlers that might get fired during the
241 // destruction, set window_ to NULL before any handlers will run.
242 window_ = NULL;
244 // OnNativeClose does a delete this so no other members should
245 // be accessed after. gtk_widget_destroy is safe (and must
246 // be last).
247 shell_window_->OnNativeClose();
248 gtk_widget_destroy(window);
251 void NativeAppWindowGtk::Activate() {
252 gtk_window_present(window_);
255 void NativeAppWindowGtk::Deactivate() {
256 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_)));
259 void NativeAppWindowGtk::Maximize() {
260 // Represent the window first in order to keep the maximization behavior
261 // consistency with Windows platform. Otherwise the window will be hidden if
262 // it has been minimized.
263 gtk_window_present(window_);
265 if (!resizable_) {
266 // When the window is not resizable, we still want to make this call succeed
267 // but gtk will not allow it if the window is not resizable. The actual
268 // maximization will happen with the subsequent OnConfigureDebounced call,
269 // that will be triggered when the window manager's resizable property
270 // changes.
271 maximize_pending_ = true;
272 gtk_window_set_resizable(window_, TRUE);
273 } else {
274 gtk_window_maximize(window_);
278 void NativeAppWindowGtk::Minimize() {
279 gtk_window_iconify(window_);
282 void NativeAppWindowGtk::Restore() {
283 if (IsMaximized())
284 gtk_window_unmaximize(window_);
285 else if (IsMinimized())
286 gtk_window_deiconify(window_);
288 // Represent the window to keep restoration behavior consistency with Windows
289 // platform.
290 // TODO(zhchbin): verify whether we need this until http://crbug.com/261013 is
291 // fixed.
292 gtk_window_present(window_);
295 void NativeAppWindowGtk::SetBounds(const gfx::Rect& bounds) {
296 gfx::Rect content_bounds = bounds;
297 gtk_window_move(window_, content_bounds.x(), content_bounds.y());
298 if (!resizable_) {
299 if (frameless_ &&
300 gtk_window_util::BoundsMatchMonitorSize(window_, content_bounds)) {
301 content_bounds.set_height(content_bounds.height() - 1);
303 // TODO(jeremya): set_size_request doesn't honor min/max size, so the
304 // bounds should be constrained manually.
305 gtk_widget_set_size_request(GTK_WIDGET(window_),
306 content_bounds.width(), content_bounds.height());
307 } else {
308 gtk_window_util::SetWindowSize(window_,
309 gfx::Size(bounds.width(), bounds.height()));
313 GdkFilterReturn NativeAppWindowGtk::OnXEvent(GdkXEvent* gdk_x_event,
314 GdkEvent* gdk_event) {
315 // Work around GTK+ not reporting minimization state changes. Listen
316 // for _NET_WM_STATE property changes and use _NET_WM_STATE_HIDDEN's
317 // presence to set or clear the iconified bit if _NET_WM_STATE_HIDDEN
318 // is supported. http://crbug.com/162794.
319 XEvent* x_event = static_cast<XEvent*>(gdk_x_event);
320 std::vector< ::Atom> atom_list;
322 if (x_event->type == PropertyNotify &&
323 x_event->xproperty.atom == atom_cache_.GetAtom("_NET_WM_STATE") &&
324 GTK_WIDGET(window_)->window &&
325 ui::GetAtomArrayProperty(GDK_WINDOW_XWINDOW(GTK_WIDGET(window_)->window),
326 "_NET_WM_STATE",
327 &atom_list)) {
328 std::vector< ::Atom>::iterator it =
329 std::find(atom_list.begin(),
330 atom_list.end(),
331 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN"));
333 GdkWindowState previous_state = state_;
334 state_ = (it != atom_list.end()) ? GDK_WINDOW_STATE_ICONIFIED :
335 static_cast<GdkWindowState>(state_ & ~GDK_WINDOW_STATE_ICONIFIED);
337 if (previous_state != state_) {
338 shell_window_->OnNativeWindowChanged();
342 return GDK_FILTER_CONTINUE;
345 void NativeAppWindowGtk::FlashFrame(bool flash) {
346 gtk_window_set_urgency_hint(window_, flash);
349 bool NativeAppWindowGtk::IsAlwaysOnTop() const {
350 return always_on_top_;
353 void NativeAppWindowGtk::RenderViewHostChanged(
354 content::RenderViewHost* old_host,
355 content::RenderViewHost* new_host) {
356 web_contents()->GetView()->Focus();
359 void NativeAppWindowGtk::SetAlwaysOnTop(bool always_on_top) {
360 if (always_on_top_ != always_on_top) {
361 // gdk_window_get_state() does not give us the correct value for the
362 // GDK_WINDOW_STATE_ABOVE bit. Cache the current state.
363 always_on_top_ = always_on_top;
364 gtk_window_set_keep_above(window_, always_on_top_ ? TRUE : FALSE);
368 gfx::NativeView NativeAppWindowGtk::GetHostView() const {
369 NOTIMPLEMENTED();
370 return NULL;
373 gfx::Point NativeAppWindowGtk::GetDialogPosition(const gfx::Size& size) {
374 gint current_width = 0;
375 gint current_height = 0;
376 gtk_window_get_size(window_, &current_width, &current_height);
377 return gfx::Point(current_width / 2 - size.width() / 2,
378 current_height / 2 - size.height() / 2);
381 gfx::Size NativeAppWindowGtk::GetMaximumDialogSize() {
382 gint current_width = 0;
383 gint current_height = 0;
384 gtk_window_get_size(window_, &current_width, &current_height);
385 return gfx::Size(current_width, current_height);
388 void NativeAppWindowGtk::AddObserver(
389 web_modal::ModalDialogHostObserver* observer) {
390 observer_list_.AddObserver(observer);
393 void NativeAppWindowGtk::RemoveObserver(
394 web_modal::ModalDialogHostObserver* observer) {
395 observer_list_.RemoveObserver(observer);
398 void NativeAppWindowGtk::ActiveWindowChanged(GdkWindow* active_window) {
399 // Do nothing if we're in the process of closing the browser window.
400 if (!window_)
401 return;
403 is_active_ = gtk_widget_get_window(GTK_WIDGET(window_)) == active_window;
404 if (is_active_)
405 shell_window_->OnNativeWindowActivated();
408 // Callback for the delete event. This event is fired when the user tries to
409 // close the window (e.g., clicking on the X in the window manager title bar).
410 gboolean NativeAppWindowGtk::OnMainWindowDeleteEvent(GtkWidget* widget,
411 GdkEvent* event) {
412 Close();
414 // Return true to prevent the GTK window from being destroyed. Close will
415 // destroy it for us.
416 return TRUE;
419 gboolean NativeAppWindowGtk::OnConfigure(GtkWidget* widget,
420 GdkEventConfigure* event) {
421 // We update |bounds_| but not |restored_bounds_| here. The latter needs
422 // to be updated conditionally when the window is non-maximized and non-
423 // fullscreen, but whether those state updates have been processed yet is
424 // window-manager specific. We update |restored_bounds_| in the debounced
425 // handler below, after the window state has been updated.
426 bounds_.SetRect(event->x, event->y, event->width, event->height);
428 // The GdkEventConfigure* we get here doesn't have quite the right
429 // coordinates though (they're relative to the drawable window area, rather
430 // than any window manager decorations, if enabled), so we need to call
431 // gtk_window_get_position() to get the right values. (Otherwise session
432 // restore, if enabled, will restore windows to incorrect positions.) That's
433 // a round trip to the X server though, so we set a debounce timer and only
434 // call it (in OnConfigureDebounced() below) after we haven't seen a
435 // reconfigure event in a short while.
436 // We don't use Reset() because the timer may not yet be running.
437 // (In that case Stop() is a no-op.)
438 window_configure_debounce_timer_.Stop();
439 window_configure_debounce_timer_.Start(FROM_HERE,
440 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds), this,
441 &NativeAppWindowGtk::OnConfigureDebounced);
443 return FALSE;
446 void NativeAppWindowGtk::OnConfigureDebounced() {
447 gtk_window_util::UpdateWindowPosition(this, &bounds_, &restored_bounds_);
448 shell_window_->OnNativeWindowChanged();
450 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
451 observer_list_,
452 OnPositionRequiresUpdate());
454 // Fullscreen of non-resizable windows requires them to be made resizable
455 // first. After that takes effect and OnConfigure is called we transition
456 // to fullscreen.
457 if (!IsFullscreen() && IsFullscreenOrPending()) {
458 gtk_window_fullscreen(window_);
461 // maximize_pending_ is the boolean that lets us know that the window is in
462 // the process of being maximized but was set as not resizable.
463 // This function will be called twice during the maximization process:
464 // 1. gtk_window_maximize() is called to maximize the window;
465 // 2. gtk_set_resizable(, FALSE) is called to make the window no longer
466 // resizable.
467 // gtk_window_maximize() will cause ::OnConfigureDebounced to be called
468 // again, at which time we will run into the second step.
469 if (maximize_pending_) {
470 if (!(state_ & GDK_WINDOW_STATE_MAXIMIZED)) {
471 gtk_window_maximize(window_);
472 } else {
473 maximize_pending_ = false;
474 if (!resizable_)
475 gtk_window_set_resizable(window_, FALSE);
480 gboolean NativeAppWindowGtk::OnWindowState(GtkWidget* sender,
481 GdkEventWindowState* event) {
482 state_ = event->new_window_state;
484 if (content_thinks_its_fullscreen_ &&
485 !(state_ & GDK_WINDOW_STATE_FULLSCREEN)) {
486 content_thinks_its_fullscreen_ = false;
487 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
488 if (rvh)
489 rvh->ExitFullscreen();
492 return FALSE;
495 bool NativeAppWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) {
496 if (!frameless_)
497 return false;
499 if (IsMaximized() || IsFullscreen())
500 return false;
502 return gtk_window_util::GetWindowEdge(bounds_.size(), 0, x, y, edge);
505 gboolean NativeAppWindowGtk::OnMouseMoveEvent(GtkWidget* widget,
506 GdkEventMotion* event) {
507 if (!frameless_) {
508 // Reset the cursor.
509 if (frame_cursor_) {
510 frame_cursor_ = NULL;
511 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL);
513 return FALSE;
516 if (!resizable_)
517 return FALSE;
519 // Update the cursor if we're on the custom frame border.
520 GdkWindowEdge edge;
521 bool has_hit_edge = GetWindowEdge(static_cast<int>(event->x),
522 static_cast<int>(event->y), &edge);
523 GdkCursorType new_cursor = GDK_LAST_CURSOR;
524 if (has_hit_edge)
525 new_cursor = gtk_window_util::GdkWindowEdgeToGdkCursorType(edge);
527 GdkCursorType last_cursor = GDK_LAST_CURSOR;
528 if (frame_cursor_)
529 last_cursor = frame_cursor_->type;
531 if (last_cursor != new_cursor) {
532 frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL;
533 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)),
534 frame_cursor_);
536 return FALSE;
539 gboolean NativeAppWindowGtk::OnButtonPress(GtkWidget* widget,
540 GdkEventButton* event) {
541 DCHECK(frameless_);
542 // Make the button press coordinate relative to the browser window.
543 int win_x, win_y;
544 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
545 gdk_window_get_origin(gdk_window, &win_x, &win_y);
547 GdkWindowEdge edge;
548 gfx::Point point(static_cast<int>(event->x_root - win_x),
549 static_cast<int>(event->y_root - win_y));
550 bool has_hit_edge = resizable_ && GetWindowEdge(point.x(), point.y(), &edge);
551 bool has_hit_titlebar =
552 draggable_region_ && draggable_region_->contains(event->x, event->y);
554 if (event->button == 1) {
555 if (GDK_BUTTON_PRESS == event->type) {
556 // Raise the window after a click on either the titlebar or the border to
557 // match the behavior of most window managers, unless that behavior has
558 // been suppressed.
559 if ((has_hit_titlebar || has_hit_edge) && !suppress_window_raise_)
560 gdk_window_raise(GTK_WIDGET(widget)->window);
562 if (has_hit_edge) {
563 gtk_window_begin_resize_drag(window_, edge, event->button,
564 static_cast<gint>(event->x_root),
565 static_cast<gint>(event->y_root),
566 event->time);
567 return TRUE;
568 } else if (has_hit_titlebar) {
569 return gtk_window_util::HandleTitleBarLeftMousePress(
570 window_, bounds_, event);
572 } else if (GDK_2BUTTON_PRESS == event->type) {
573 if (has_hit_titlebar && resizable_) {
574 // Maximize/restore on double click.
575 if (IsMaximized()) {
576 gtk_window_util::UnMaximize(GTK_WINDOW(widget),
577 bounds_, restored_bounds_);
578 } else {
579 gtk_window_maximize(window_);
581 return TRUE;
584 } else if (event->button == 2) {
585 if (has_hit_titlebar || has_hit_edge)
586 gdk_window_lower(gdk_window);
587 return TRUE;
590 return FALSE;
593 // NativeAppWindow implementation:
595 void NativeAppWindowGtk::SetFullscreen(int fullscreen_types) {
596 bool fullscreen = (fullscreen_types != ShellWindow::FULLSCREEN_TYPE_NONE);
597 content_thinks_its_fullscreen_ = fullscreen;
598 if (fullscreen) {
599 if (resizable_) {
600 gtk_window_fullscreen(window_);
601 } else {
602 // We must first make the window resizable. That won't take effect
603 // immediately, so OnConfigureDebounced completes the fullscreen call.
604 gtk_window_set_resizable(window_, TRUE);
606 } else {
607 gtk_window_unfullscreen(window_);
608 if (!resizable_)
609 gtk_window_set_resizable(window_, FALSE);
613 bool NativeAppWindowGtk::IsFullscreenOrPending() const {
614 // |content_thinks_its_fullscreen_| is used when transitioning, and when
615 // the state change will not be made for some time. However, it is possible
616 // for a state update to be made before the final fullscreen state comes.
617 // In that case, |content_thinks_its_fullscreen_| will be cleared, but we
618 // will fall back to |IsFullscreen| which will soon have the correct state.
619 return content_thinks_its_fullscreen_ || IsFullscreen();
622 bool NativeAppWindowGtk::IsDetached() const {
623 return false;
626 void NativeAppWindowGtk::UpdateWindowIcon() {
627 Profile* profile = shell_window_->profile();
628 gfx::Image app_icon = shell_window_->app_icon();
629 if (!app_icon.IsEmpty())
630 gtk_util::SetWindowIcon(window_, profile, app_icon.ToGdkPixbuf());
631 else
632 gtk_util::SetWindowIcon(window_, profile);
635 void NativeAppWindowGtk::UpdateWindowTitle() {
636 base::string16 title = shell_window_->GetTitle();
637 gtk_window_set_title(window_, base::UTF16ToUTF8(title).c_str());
640 void NativeAppWindowGtk::UpdateDraggableRegions(
641 const std::vector<extensions::DraggableRegion>& regions) {
642 // Draggable region is not supported for non-frameless window.
643 if (!frameless_)
644 return;
646 draggable_region_.reset(ShellWindow::RawDraggableRegionsToSkRegion(regions));
649 SkRegion* NativeAppWindowGtk::GetDraggableRegion() {
650 return draggable_region_.get();
653 void NativeAppWindowGtk::UpdateShape(scoped_ptr<SkRegion> region) {
654 NOTIMPLEMENTED();
657 void NativeAppWindowGtk::HandleKeyboardEvent(
658 const content::NativeWebKeyboardEvent& event) {
659 // No-op.
662 bool NativeAppWindowGtk::IsFrameless() const {
663 return frameless_;
666 gfx::Insets NativeAppWindowGtk::GetFrameInsets() const {
667 if (frameless_)
668 return gfx::Insets();
669 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
670 if (!gdk_window)
671 return gfx::Insets();
673 gint current_width = 0;
674 gint current_height = 0;
675 gtk_window_get_size(window_, &current_width, &current_height);
676 gint current_x = 0;
677 gint current_y = 0;
678 gdk_window_get_position(gdk_window, &current_x, &current_y);
679 GdkRectangle rect_with_decorations = {0};
680 gdk_window_get_frame_extents(gdk_window,
681 &rect_with_decorations);
683 int left_inset = current_x - rect_with_decorations.x;
684 int top_inset = current_y - rect_with_decorations.y;
685 return gfx::Insets(
686 top_inset,
687 left_inset,
688 rect_with_decorations.height - current_height - top_inset,
689 rect_with_decorations.width - current_width - left_inset);
692 void NativeAppWindowGtk::HideWithApp() {}
693 void NativeAppWindowGtk::ShowWithApp() {}
695 void NativeAppWindowGtk::UpdateWindowMinMaxSize() {
696 GdkGeometry hints;
697 int hints_mask = 0;
698 if (shell_window_->size_constraints().HasMinimumSize()) {
699 gfx::Size min_size = shell_window_->size_constraints().GetMinimumSize();
700 hints.min_height = min_size.height();
701 hints.min_width = min_size.width();
702 hints_mask |= GDK_HINT_MIN_SIZE;
704 if (shell_window_->size_constraints().HasMaximumSize()) {
705 gfx::Size max_size = shell_window_->size_constraints().GetMaximumSize();
706 const int kUnboundedSize = ShellWindow::SizeConstraints::kUnboundedSize;
707 hints.max_height = max_size.height() == kUnboundedSize ?
708 G_MAXINT : max_size.height();
709 hints.max_width = max_size.width() == kUnboundedSize ?
710 G_MAXINT : max_size.width();
711 hints_mask |= GDK_HINT_MAX_SIZE;
713 if (hints_mask) {
714 gtk_window_set_geometry_hints(
715 window_,
716 GTK_WIDGET(window_),
717 &hints,
718 static_cast<GdkWindowHints>(hints_mask));