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

Side by Side Diff: chrome/browser/ui/gtk/apps/native_app_window_gtk.cc

Issue 231733005: Delete the GTK+ port of Chrome. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remerge to ToT Created 6 years, 8 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
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/ui/gtk/apps/native_app_window_gtk.h"
6
7 #include <gdk/gdkx.h>
8 #include <vector>
9
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"
26
27 using apps::AppWindow;
28
29 namespace {
30
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;
34
35 const char* kAtomsToCache[] = {
36 "_NET_WM_STATE",
37 "_NET_WM_STATE_HIDDEN",
38 NULL
39 };
40
41 } // namespace
42
43 NativeAppWindowGtk::NativeAppWindowGtk(AppWindow* app_window,
44 const AppWindow::CreateParams& params)
45 : app_window_(app_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 == AppWindow::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());
57
58 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
59
60 gfx::NativeView native_view =
61 web_contents()->GetView()->GetNativeView();
62 gtk_container_add(GTK_CONTAINER(window_), native_view);
63
64 gfx::Insets frame_insets = GetFrameInsets();
65 gfx::Rect initial_bounds = params.GetInitialWindowBounds(frame_insets);
66
67 typedef apps::AppWindow::BoundsSpecification BoundsSpecification;
68 if (initial_bounds.x() != BoundsSpecification::kUnspecifiedPosition &&
69 initial_bounds.y() != BoundsSpecification::kUnspecifiedPosition) {
70 gtk_window_move(window_, initial_bounds.x(), initial_bounds.y());
71 }
72
73 // This is done to avoid a WM "feature" where setting the window size to
74 // the monitor size causes the WM to set the EWMH for full screen mode.
75 int win_height = initial_bounds.height();
76 if (frameless_ &&
77 gtk_window_util::BoundsMatchMonitorSize(window_, initial_bounds)) {
78 win_height -= 1;
79 }
80 gtk_window_set_default_size(window_, initial_bounds.width(), win_height);
81
82 resizable_ = params.resizable;
83 if (!resizable_) {
84 // If the window doesn't have a size request when we set resizable to
85 // false, GTK will shrink the window to 1x1px.
86 gtk_widget_set_size_request(GTK_WIDGET(window_),
87 initial_bounds.width(), win_height);
88 gtk_window_set_resizable(window_, FALSE);
89 }
90
91 // make sure bounds_ and restored_bounds_ have correct values until we
92 // get our first configure-event
93 bounds_ = restored_bounds_ = initial_bounds;
94 gint x, y;
95 gtk_window_get_position(window_, &x, &y);
96 bounds_.set_origin(gfx::Point(x, y));
97
98 // Hide titlebar when {frame: 'none'} specified on AppWindow..
99 if (frameless_)
100 gtk_window_set_decorated(window_, false);
101
102 if (always_on_top_)
103 gtk_window_set_keep_above(window_, TRUE);
104
105 SetContentSizeConstraints(params.GetContentMinimumSize(frame_insets),
106 params.GetContentMaximumSize(frame_insets));
107
108 // In some (older) versions of compiz, raising top-level windows when they
109 // are partially off-screen causes them to get snapped back on screen, not
110 // always even on the current virtual desktop. If we are running under
111 // compiz, suppress such raises, as they are not necessary in compiz anyway.
112 if (ui::GuessWindowManager() == ui::WM_COMPIZ)
113 suppress_window_raise_ = true;
114
115 gtk_window_set_title(window_, extension()->name().c_str());
116
117 std::string app_name = web_app::GenerateApplicationNameFromExtensionId(
118 extension()->id());
119 gtk_window_util::SetWindowCustomClass(window_,
120 web_app::GetWMClassFromAppName(app_name));
121
122 g_signal_connect(window_, "delete-event",
123 G_CALLBACK(OnMainWindowDeleteEventThunk), this);
124 g_signal_connect(window_, "configure-event",
125 G_CALLBACK(OnConfigureThunk), this);
126 g_signal_connect(window_, "window-state-event",
127 G_CALLBACK(OnWindowStateThunk), this);
128 if (frameless_) {
129 g_signal_connect(window_, "button-press-event",
130 G_CALLBACK(OnButtonPressThunk), this);
131 g_signal_connect(window_, "motion-notify-event",
132 G_CALLBACK(OnMouseMoveEventThunk), this);
133 }
134
135 // If _NET_WM_STATE_HIDDEN is in _NET_SUPPORTED, listen for XEvent to work
136 // around GTK+ not reporting minimization state changes. See comment in the
137 // |OnXEvent|.
138 std::vector< ::Atom> supported_atoms;
139 if (ui::GetAtomArrayProperty(ui::GetX11RootWindow(),
140 "_NET_SUPPORTED",
141 &supported_atoms)) {
142 if (std::find(supported_atoms.begin(),
143 supported_atoms.end(),
144 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN")) !=
145 supported_atoms.end()) {
146 GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(window_));
147 gdk_window_add_filter(window,
148 &NativeAppWindowGtk::OnXEventThunk,
149 this);
150 is_x_event_listened_ = true;
151 }
152 }
153
154 // Add the keybinding registry.
155 extension_keybinding_registry_.reset(new ExtensionKeybindingRegistryGtk(
156 Profile::FromBrowserContext(app_window_->browser_context()),
157 window_,
158 extensions::ExtensionKeybindingRegistry::PLATFORM_APPS_ONLY,
159 NULL));
160
161 ui::ActiveWindowWatcherX::AddObserver(this);
162 }
163
164 NativeAppWindowGtk::~NativeAppWindowGtk() {
165 ui::ActiveWindowWatcherX::RemoveObserver(this);
166 if (is_x_event_listened_) {
167 gdk_window_remove_filter(NULL,
168 &NativeAppWindowGtk::OnXEventThunk,
169 this);
170 }
171 }
172
173 bool NativeAppWindowGtk::IsActive() const {
174 if (ui::ActiveWindowWatcherX::WMSupportsActivation())
175 return is_active_;
176
177 // This still works even though we don't get the activation notification.
178 return gtk_window_is_active(window_);
179 }
180
181 bool NativeAppWindowGtk::IsMaximized() const {
182 return (state_ & GDK_WINDOW_STATE_MAXIMIZED);
183 }
184
185 bool NativeAppWindowGtk::IsMinimized() const {
186 return (state_ & GDK_WINDOW_STATE_ICONIFIED);
187 }
188
189 bool NativeAppWindowGtk::IsFullscreen() const {
190 return (state_ & GDK_WINDOW_STATE_FULLSCREEN);
191 }
192
193 gfx::NativeWindow NativeAppWindowGtk::GetNativeWindow() {
194 return window_;
195 }
196
197 gfx::Rect NativeAppWindowGtk::GetRestoredBounds() const {
198 gfx::Rect window_bounds = restored_bounds_;
199 window_bounds.Inset(-GetFrameInsets());
200 return window_bounds;
201 }
202
203 ui::WindowShowState NativeAppWindowGtk::GetRestoredState() const {
204 if (IsMaximized())
205 return ui::SHOW_STATE_MAXIMIZED;
206 if (IsFullscreen())
207 return ui::SHOW_STATE_FULLSCREEN;
208 return ui::SHOW_STATE_NORMAL;
209 }
210
211 gfx::Rect NativeAppWindowGtk::GetBounds() const {
212 // :GetBounds() is expecting the outer window bounds to be returned (ie.
213 // including window decorations). The internal |bounds_| is not including them
214 // and trying to add the decoration to |bounds_| would require calling
215 // gdk_window_get_frame_extents. The best thing to do is to directly get the
216 // frame bounds and only use the internal value if we can't.
217 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
218 if (!gdk_window)
219 return bounds_;
220
221 GdkRectangle window_bounds = {0};
222 gdk_window_get_frame_extents(gdk_window, &window_bounds);
223 return gfx::Rect(window_bounds.x, window_bounds.y,
224 window_bounds.width, window_bounds.height);
225 }
226
227 void NativeAppWindowGtk::Show() {
228 gtk_window_present(window_);
229 }
230
231 void NativeAppWindowGtk::ShowInactive() {
232 gtk_window_set_focus_on_map(window_, false);
233 gtk_widget_show(GTK_WIDGET(window_));
234 }
235
236 void NativeAppWindowGtk::Hide() {
237 gtk_widget_hide(GTK_WIDGET(window_));
238 }
239
240 void NativeAppWindowGtk::Close() {
241 app_window_->OnNativeWindowChanged();
242
243 // Cancel any pending callback from the window configure debounce timer.
244 window_configure_debounce_timer_.Stop();
245
246 GtkWidget* window = GTK_WIDGET(window_);
247 // To help catch bugs in any event handlers that might get fired during the
248 // destruction, set window_ to NULL before any handlers will run.
249 window_ = NULL;
250
251 // OnNativeClose does a delete this so no other members should
252 // be accessed after. gtk_widget_destroy is safe (and must
253 // be last).
254 app_window_->OnNativeClose();
255 gtk_widget_destroy(window);
256 }
257
258 void NativeAppWindowGtk::Activate() {
259 gtk_window_present(window_);
260 }
261
262 void NativeAppWindowGtk::Deactivate() {
263 gdk_window_lower(gtk_widget_get_window(GTK_WIDGET(window_)));
264 }
265
266 void NativeAppWindowGtk::Maximize() {
267 // Represent the window first in order to keep the maximization behavior
268 // consistency with Windows platform. Otherwise the window will be hidden if
269 // it has been minimized.
270 gtk_window_present(window_);
271
272 if (!resizable_) {
273 // When the window is not resizable, we still want to make this call succeed
274 // but gtk will not allow it if the window is not resizable. The actual
275 // maximization will happen with the subsequent OnConfigureDebounced call,
276 // that will be triggered when the window manager's resizable property
277 // changes.
278 maximize_pending_ = true;
279 gtk_window_set_resizable(window_, TRUE);
280 } else {
281 gtk_window_maximize(window_);
282 }
283 }
284
285 void NativeAppWindowGtk::Minimize() {
286 gtk_window_iconify(window_);
287 }
288
289 void NativeAppWindowGtk::Restore() {
290 if (IsMaximized())
291 gtk_window_unmaximize(window_);
292 else if (IsMinimized())
293 gtk_window_deiconify(window_);
294
295 // Represent the window to keep restoration behavior consistency with Windows
296 // platform.
297 // TODO(zhchbin): verify whether we need this until http://crbug.com/261013 is
298 // fixed.
299 gtk_window_present(window_);
300 }
301
302 void NativeAppWindowGtk::SetBounds(const gfx::Rect& bounds) {
303 gfx::Rect content_bounds = bounds;
304 gtk_window_move(window_, content_bounds.x(), content_bounds.y());
305 if (!resizable_) {
306 if (frameless_ &&
307 gtk_window_util::BoundsMatchMonitorSize(window_, content_bounds)) {
308 content_bounds.set_height(content_bounds.height() - 1);
309 }
310 // TODO(jeremya): set_size_request doesn't honor min/max size, so the
311 // bounds should be constrained manually.
312 gtk_widget_set_size_request(GTK_WIDGET(window_),
313 content_bounds.width(), content_bounds.height());
314 } else {
315 gtk_window_util::SetWindowSize(window_,
316 gfx::Size(bounds.width(), bounds.height()));
317 }
318 }
319
320 GdkFilterReturn NativeAppWindowGtk::OnXEvent(GdkXEvent* gdk_x_event,
321 GdkEvent* gdk_event) {
322 // Work around GTK+ not reporting minimization state changes. Listen
323 // for _NET_WM_STATE property changes and use _NET_WM_STATE_HIDDEN's
324 // presence to set or clear the iconified bit if _NET_WM_STATE_HIDDEN
325 // is supported. http://crbug.com/162794.
326 XEvent* x_event = static_cast<XEvent*>(gdk_x_event);
327 std::vector< ::Atom> atom_list;
328
329 if (x_event->type == PropertyNotify &&
330 x_event->xproperty.atom == atom_cache_.GetAtom("_NET_WM_STATE") &&
331 GTK_WIDGET(window_)->window &&
332 ui::GetAtomArrayProperty(GDK_WINDOW_XWINDOW(GTK_WIDGET(window_)->window),
333 "_NET_WM_STATE",
334 &atom_list)) {
335 std::vector< ::Atom>::iterator it =
336 std::find(atom_list.begin(),
337 atom_list.end(),
338 atom_cache_.GetAtom("_NET_WM_STATE_HIDDEN"));
339
340 GdkWindowState previous_state = state_;
341 state_ = (it != atom_list.end()) ? GDK_WINDOW_STATE_ICONIFIED :
342 static_cast<GdkWindowState>(state_ & ~GDK_WINDOW_STATE_ICONIFIED);
343
344 if (previous_state != state_) {
345 app_window_->OnNativeWindowChanged();
346 }
347 }
348
349 return GDK_FILTER_CONTINUE;
350 }
351
352 void NativeAppWindowGtk::FlashFrame(bool flash) {
353 gtk_window_set_urgency_hint(window_, flash);
354 }
355
356 bool NativeAppWindowGtk::IsAlwaysOnTop() const {
357 return always_on_top_;
358 }
359
360 void NativeAppWindowGtk::RenderViewHostChanged(
361 content::RenderViewHost* old_host,
362 content::RenderViewHost* new_host) {
363 web_contents()->GetView()->Focus();
364 }
365
366 void NativeAppWindowGtk::SetAlwaysOnTop(bool always_on_top) {
367 if (always_on_top_ != always_on_top) {
368 // gdk_window_get_state() does not give us the correct value for the
369 // GDK_WINDOW_STATE_ABOVE bit. Cache the current state.
370 always_on_top_ = always_on_top;
371 gtk_window_set_keep_above(window_, always_on_top_ ? TRUE : FALSE);
372 }
373 }
374
375 gfx::NativeView NativeAppWindowGtk::GetHostView() const {
376 NOTIMPLEMENTED();
377 return NULL;
378 }
379
380 gfx::Point NativeAppWindowGtk::GetDialogPosition(const gfx::Size& size) {
381 gint current_width = 0;
382 gint current_height = 0;
383 gtk_window_get_size(window_, &current_width, &current_height);
384 return gfx::Point(current_width / 2 - size.width() / 2,
385 current_height / 2 - size.height() / 2);
386 }
387
388 gfx::Size NativeAppWindowGtk::GetMaximumDialogSize() {
389 gint current_width = 0;
390 gint current_height = 0;
391 gtk_window_get_size(window_, &current_width, &current_height);
392 return gfx::Size(current_width, current_height);
393 }
394
395 void NativeAppWindowGtk::AddObserver(
396 web_modal::ModalDialogHostObserver* observer) {
397 observer_list_.AddObserver(observer);
398 }
399
400 void NativeAppWindowGtk::RemoveObserver(
401 web_modal::ModalDialogHostObserver* observer) {
402 observer_list_.RemoveObserver(observer);
403 }
404
405 void NativeAppWindowGtk::ActiveWindowChanged(GdkWindow* active_window) {
406 // Do nothing if we're in the process of closing the browser window.
407 if (!window_)
408 return;
409
410 is_active_ = gtk_widget_get_window(GTK_WIDGET(window_)) == active_window;
411 if (is_active_)
412 app_window_->OnNativeWindowActivated();
413 }
414
415 // Callback for the delete event. This event is fired when the user tries to
416 // close the window (e.g., clicking on the X in the window manager title bar).
417 gboolean NativeAppWindowGtk::OnMainWindowDeleteEvent(GtkWidget* widget,
418 GdkEvent* event) {
419 Close();
420
421 // Return true to prevent the GTK window from being destroyed. Close will
422 // destroy it for us.
423 return TRUE;
424 }
425
426 gboolean NativeAppWindowGtk::OnConfigure(GtkWidget* widget,
427 GdkEventConfigure* event) {
428 // We update |bounds_| but not |restored_bounds_| here. The latter needs
429 // to be updated conditionally when the window is non-maximized and non-
430 // fullscreen, but whether those state updates have been processed yet is
431 // window-manager specific. We update |restored_bounds_| in the debounced
432 // handler below, after the window state has been updated.
433 bounds_.SetRect(event->x, event->y, event->width, event->height);
434
435 // The GdkEventConfigure* we get here doesn't have quite the right
436 // coordinates though (they're relative to the drawable window area, rather
437 // than any window manager decorations, if enabled), so we need to call
438 // gtk_window_get_position() to get the right values. (Otherwise session
439 // restore, if enabled, will restore windows to incorrect positions.) That's
440 // a round trip to the X server though, so we set a debounce timer and only
441 // call it (in OnConfigureDebounced() below) after we haven't seen a
442 // reconfigure event in a short while.
443 // We don't use Reset() because the timer may not yet be running.
444 // (In that case Stop() is a no-op.)
445 window_configure_debounce_timer_.Stop();
446 window_configure_debounce_timer_.Start(FROM_HERE,
447 base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds), this,
448 &NativeAppWindowGtk::OnConfigureDebounced);
449
450 return FALSE;
451 }
452
453 void NativeAppWindowGtk::OnConfigureDebounced() {
454 gtk_window_util::UpdateWindowPosition(this, &bounds_, &restored_bounds_);
455 app_window_->OnNativeWindowChanged();
456
457 FOR_EACH_OBSERVER(web_modal::ModalDialogHostObserver,
458 observer_list_,
459 OnPositionRequiresUpdate());
460
461 // Fullscreen of non-resizable windows requires them to be made resizable
462 // first. After that takes effect and OnConfigure is called we transition
463 // to fullscreen.
464 if (!IsFullscreen() && IsFullscreenOrPending()) {
465 gtk_window_fullscreen(window_);
466 }
467
468 // maximize_pending_ is the boolean that lets us know that the window is in
469 // the process of being maximized but was set as not resizable.
470 // This function will be called twice during the maximization process:
471 // 1. gtk_window_maximize() is called to maximize the window;
472 // 2. gtk_set_resizable(, FALSE) is called to make the window no longer
473 // resizable.
474 // gtk_window_maximize() will cause ::OnConfigureDebounced to be called
475 // again, at which time we will run into the second step.
476 if (maximize_pending_) {
477 if (!(state_ & GDK_WINDOW_STATE_MAXIMIZED)) {
478 gtk_window_maximize(window_);
479 } else {
480 maximize_pending_ = false;
481 if (!resizable_)
482 gtk_window_set_resizable(window_, FALSE);
483 }
484 }
485 }
486
487 void NativeAppWindowGtk::UpdateContentMinMaxSize() {
488 GdkGeometry hints;
489 int hints_mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE;
490
491 gfx::Size min_size = size_constraints_.GetMinimumSize();
492 hints.min_height = min_size.height();
493 hints.min_width = min_size.width();
494
495 gfx::Size max_size = size_constraints_.GetMaximumSize();
496 const int kUnboundedSize = apps::SizeConstraints::kUnboundedSize;
497 hints.max_height = max_size.height() == kUnboundedSize ?
498 G_MAXINT : max_size.height();
499 hints.max_width = max_size.width() == kUnboundedSize ?
500 G_MAXINT : max_size.width();
501
502 gtk_window_set_geometry_hints(
503 window_,
504 GTK_WIDGET(window_),
505 &hints,
506 static_cast<GdkWindowHints>(hints_mask));
507 }
508
509 gboolean NativeAppWindowGtk::OnWindowState(GtkWidget* sender,
510 GdkEventWindowState* event) {
511 state_ = event->new_window_state;
512
513 if (content_thinks_its_fullscreen_ &&
514 !(state_ & GDK_WINDOW_STATE_FULLSCREEN)) {
515 content_thinks_its_fullscreen_ = false;
516 content::RenderViewHost* rvh = web_contents()->GetRenderViewHost();
517 if (rvh)
518 rvh->ExitFullscreen();
519 }
520
521 return FALSE;
522 }
523
524 bool NativeAppWindowGtk::GetWindowEdge(int x, int y, GdkWindowEdge* edge) {
525 if (!frameless_)
526 return false;
527
528 if (IsMaximized() || IsFullscreen())
529 return false;
530
531 return gtk_window_util::GetWindowEdge(bounds_.size(), 0, x, y, edge);
532 }
533
534 gboolean NativeAppWindowGtk::OnMouseMoveEvent(GtkWidget* widget,
535 GdkEventMotion* event) {
536 if (!frameless_) {
537 // Reset the cursor.
538 if (frame_cursor_) {
539 frame_cursor_ = NULL;
540 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)), NULL);
541 }
542 return FALSE;
543 }
544
545 if (!resizable_)
546 return FALSE;
547
548 // Update the cursor if we're on the custom frame border.
549 GdkWindowEdge edge;
550 bool has_hit_edge = GetWindowEdge(static_cast<int>(event->x),
551 static_cast<int>(event->y), &edge);
552 GdkCursorType new_cursor = GDK_LAST_CURSOR;
553 if (has_hit_edge)
554 new_cursor = gtk_window_util::GdkWindowEdgeToGdkCursorType(edge);
555
556 GdkCursorType last_cursor = GDK_LAST_CURSOR;
557 if (frame_cursor_)
558 last_cursor = frame_cursor_->type;
559
560 if (last_cursor != new_cursor) {
561 frame_cursor_ = has_hit_edge ? gfx::GetCursor(new_cursor) : NULL;
562 gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(window_)),
563 frame_cursor_);
564 }
565 return FALSE;
566 }
567
568 gboolean NativeAppWindowGtk::OnButtonPress(GtkWidget* widget,
569 GdkEventButton* event) {
570 DCHECK(frameless_);
571 // Make the button press coordinate relative to the browser window.
572 int win_x, win_y;
573 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
574 gdk_window_get_origin(gdk_window, &win_x, &win_y);
575
576 GdkWindowEdge edge;
577 gfx::Point point(static_cast<int>(event->x_root - win_x),
578 static_cast<int>(event->y_root - win_y));
579 bool has_hit_edge = resizable_ && GetWindowEdge(point.x(), point.y(), &edge);
580 bool has_hit_titlebar =
581 draggable_region_ && draggable_region_->contains(event->x, event->y);
582
583 if (event->button == 1) {
584 if (GDK_BUTTON_PRESS == event->type) {
585 // Raise the window after a click on either the titlebar or the border to
586 // match the behavior of most window managers, unless that behavior has
587 // been suppressed.
588 if ((has_hit_titlebar || has_hit_edge) && !suppress_window_raise_)
589 gdk_window_raise(GTK_WIDGET(widget)->window);
590
591 if (has_hit_edge) {
592 gtk_window_begin_resize_drag(window_, edge, event->button,
593 static_cast<gint>(event->x_root),
594 static_cast<gint>(event->y_root),
595 event->time);
596 return TRUE;
597 } else if (has_hit_titlebar) {
598 return gtk_window_util::HandleTitleBarLeftMousePress(
599 window_, bounds_, event);
600 }
601 } else if (GDK_2BUTTON_PRESS == event->type) {
602 if (has_hit_titlebar && resizable_) {
603 // Maximize/restore on double click.
604 if (IsMaximized()) {
605 gtk_window_util::UnMaximize(GTK_WINDOW(widget),
606 bounds_, restored_bounds_);
607 } else {
608 gtk_window_maximize(window_);
609 }
610 return TRUE;
611 }
612 }
613 } else if (event->button == 2) {
614 if (has_hit_titlebar || has_hit_edge)
615 gdk_window_lower(gdk_window);
616 return TRUE;
617 }
618
619 return FALSE;
620 }
621
622 // NativeAppWindow implementation:
623
624 void NativeAppWindowGtk::SetFullscreen(int fullscreen_types) {
625 bool fullscreen = (fullscreen_types != AppWindow::FULLSCREEN_TYPE_NONE);
626 content_thinks_its_fullscreen_ = fullscreen;
627 if (fullscreen) {
628 if (resizable_) {
629 gtk_window_fullscreen(window_);
630 } else {
631 // We must first make the window resizable. That won't take effect
632 // immediately, so OnConfigureDebounced completes the fullscreen call.
633 gtk_window_set_resizable(window_, TRUE);
634 }
635 } else {
636 gtk_window_unfullscreen(window_);
637 if (!resizable_)
638 gtk_window_set_resizable(window_, FALSE);
639 }
640 }
641
642 bool NativeAppWindowGtk::IsFullscreenOrPending() const {
643 // |content_thinks_its_fullscreen_| is used when transitioning, and when
644 // the state change will not be made for some time. However, it is possible
645 // for a state update to be made before the final fullscreen state comes.
646 // In that case, |content_thinks_its_fullscreen_| will be cleared, but we
647 // will fall back to |IsFullscreen| which will soon have the correct state.
648 return content_thinks_its_fullscreen_ || IsFullscreen();
649 }
650
651 bool NativeAppWindowGtk::IsDetached() const {
652 return false;
653 }
654
655 void NativeAppWindowGtk::UpdateWindowIcon() {
656 Profile* profile =
657 Profile::FromBrowserContext(app_window_->browser_context());
658 gfx::Image app_icon = app_window_->app_icon();
659 if (!app_icon.IsEmpty())
660 gtk_util::SetWindowIcon(window_, profile, app_icon.ToGdkPixbuf());
661 else
662 gtk_util::SetWindowIcon(window_, profile);
663 }
664
665 void NativeAppWindowGtk::UpdateWindowTitle() {
666 base::string16 title = app_window_->GetTitle();
667 gtk_window_set_title(window_, base::UTF16ToUTF8(title).c_str());
668 }
669
670 void NativeAppWindowGtk::UpdateBadgeIcon() {
671 NOTIMPLEMENTED();
672 }
673
674 void NativeAppWindowGtk::UpdateDraggableRegions(
675 const std::vector<extensions::DraggableRegion>& regions) {
676 // Draggable region is not supported for non-frameless window.
677 if (!frameless_)
678 return;
679
680 draggable_region_.reset(AppWindow::RawDraggableRegionsToSkRegion(regions));
681 }
682
683 SkRegion* NativeAppWindowGtk::GetDraggableRegion() {
684 return draggable_region_.get();
685 }
686
687 void NativeAppWindowGtk::UpdateShape(scoped_ptr<SkRegion> region) {
688 NOTIMPLEMENTED();
689 }
690
691 void NativeAppWindowGtk::HandleKeyboardEvent(
692 const content::NativeWebKeyboardEvent& event) {
693 // No-op.
694 }
695
696 bool NativeAppWindowGtk::IsFrameless() const {
697 return frameless_;
698 }
699
700 bool NativeAppWindowGtk::HasFrameColor() const {
701 return false;
702 }
703
704 SkColor NativeAppWindowGtk::FrameColor() const {
705 return SkColor();
706 }
707
708 gfx::Insets NativeAppWindowGtk::GetFrameInsets() const {
709 if (frameless_)
710 return gfx::Insets();
711 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_));
712 if (!gdk_window)
713 return gfx::Insets();
714
715 gint current_width = 0;
716 gint current_height = 0;
717 gtk_window_get_size(window_, &current_width, &current_height);
718 gint current_x = 0;
719 gint current_y = 0;
720 gdk_window_get_position(gdk_window, &current_x, &current_y);
721 GdkRectangle rect_with_decorations = {0};
722 gdk_window_get_frame_extents(gdk_window,
723 &rect_with_decorations);
724
725 int left_inset = current_x - rect_with_decorations.x;
726 int top_inset = current_y - rect_with_decorations.y;
727 return gfx::Insets(
728 top_inset,
729 left_inset,
730 rect_with_decorations.height - current_height - top_inset,
731 rect_with_decorations.width - current_width - left_inset);
732 }
733
734 void NativeAppWindowGtk::HideWithApp() {}
735 void NativeAppWindowGtk::ShowWithApp() {}
736
737 void NativeAppWindowGtk::UpdateShelfMenu() {
738 // TODO(tmdiep): To be implemented for GTK.
739 NOTIMPLEMENTED();
740 }
741
742 gfx::Size NativeAppWindowGtk::GetContentMinimumSize() const {
743 return size_constraints_.GetMinimumSize();
744 }
745
746 gfx::Size NativeAppWindowGtk::GetContentMaximumSize() const {
747 return size_constraints_.GetMaximumSize();
748 }
749
750 void NativeAppWindowGtk::SetContentSizeConstraints(
751 const gfx::Size& min_size, const gfx::Size& max_size) {
752 bool changed = size_constraints_.GetMinimumSize() != min_size ||
753 size_constraints_.GetMaximumSize() != max_size;
754 if (!changed)
755 return;
756
757 size_constraints_.set_minimum_size(min_size);
758 size_constraints_.set_maximum_size(max_size);
759 UpdateContentMinMaxSize();
760 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698