blob: 46605c213a902fa43faf5d3242f01d8b89444d1e [file] [log] [blame]
[email protected]534b1632012-08-14 18:54:481// 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 "chrome/browser/ui/gtk/gtk_window_util.h"
6
7#include <dlfcn.h>
8#include "content/public/browser/render_view_host.h"
9#include "content/public/browser/web_contents.h"
10
11using content::RenderWidgetHost;
12using content::WebContents;
13
14namespace gtk_window_util {
15
[email protected]e3fcf7e2012-08-21 07:04:4416// Keep track of the last click time and the last click position so we can
17// filter out extra GDK_BUTTON_PRESS events when a double click happens.
18static guint32 last_click_time;
19static int last_click_x;
20static int last_click_y;
21
[email protected]534b1632012-08-14 18:54:4822// Performs Cut/Copy/Paste operation on the |window|.
23// If the current render view is focused, then just call the specified |method|
24// against the current render view host, otherwise emit the specified |signal|
25// against the focused widget.
26// TODO(suzhe): This approach does not work for plugins.
27void DoCutCopyPaste(GtkWindow* window,
28 WebContents* web_contents,
29 void (RenderWidgetHost::*method)(),
30 const char* signal) {
31 GtkWidget* widget = gtk_window_get_focus(window);
32 if (widget == NULL)
33 return; // Do nothing if no focused widget.
34
35 if (web_contents && widget == web_contents->GetContentNativeView()) {
36 (web_contents->GetRenderViewHost()->*method)();
37 } else {
38 guint id;
39 if ((id = g_signal_lookup(signal, G_OBJECT_TYPE(widget))) != 0)
40 g_signal_emit(widget, id, 0);
41 }
42}
43
44void DoCut(GtkWindow* window, WebContents* web_contents) {
45 DoCutCopyPaste(window, web_contents,
46 &RenderWidgetHost::Cut, "cut-clipboard");
47}
48
49void DoCopy(GtkWindow* window, WebContents* web_contents) {
50 DoCutCopyPaste(window, web_contents,
51 &RenderWidgetHost::Copy, "copy-clipboard");
52}
53
54void DoPaste(GtkWindow* window, WebContents* web_contents) {
55 DoCutCopyPaste(window, web_contents,
56 &RenderWidgetHost::Paste, "paste-clipboard");
57}
58
59// Ubuntu patches their version of GTK+ so that there is always a
60// gripper in the bottom right corner of the window. We dynamically
61// look up this symbol because it's a non-standard Ubuntu extension to
62// GTK+. We always need to disable this feature since we can't
63// communicate this to WebKit easily.
64typedef void (*gtk_window_set_has_resize_grip_func)(GtkWindow*, gboolean);
65gtk_window_set_has_resize_grip_func gtk_window_set_has_resize_grip_sym;
66
67void DisableResizeGrip(GtkWindow* window) {
68 static bool resize_grip_looked_up = false;
69 if (!resize_grip_looked_up) {
70 resize_grip_looked_up = true;
71 gtk_window_set_has_resize_grip_sym =
72 reinterpret_cast<gtk_window_set_has_resize_grip_func>(
73 dlsym(NULL, "gtk_window_set_has_resize_grip"));
74 }
75 if (gtk_window_set_has_resize_grip_sym)
76 gtk_window_set_has_resize_grip_sym(window, FALSE);
77}
78
79GdkCursorType GdkWindowEdgeToGdkCursorType(GdkWindowEdge edge) {
80 switch (edge) {
81 case GDK_WINDOW_EDGE_NORTH_WEST:
82 return GDK_TOP_LEFT_CORNER;
83 case GDK_WINDOW_EDGE_NORTH:
84 return GDK_TOP_SIDE;
85 case GDK_WINDOW_EDGE_NORTH_EAST:
86 return GDK_TOP_RIGHT_CORNER;
87 case GDK_WINDOW_EDGE_WEST:
88 return GDK_LEFT_SIDE;
89 case GDK_WINDOW_EDGE_EAST:
90 return GDK_RIGHT_SIDE;
91 case GDK_WINDOW_EDGE_SOUTH_WEST:
92 return GDK_BOTTOM_LEFT_CORNER;
93 case GDK_WINDOW_EDGE_SOUTH:
94 return GDK_BOTTOM_SIDE;
95 case GDK_WINDOW_EDGE_SOUTH_EAST:
96 return GDK_BOTTOM_RIGHT_CORNER;
97 default:
98 NOTREACHED();
99 }
100 return GDK_LAST_CURSOR;
101}
102
[email protected]e3fcf7e2012-08-21 07:04:44103bool BoundsMatchMonitorSize(GtkWindow* window, gfx::Rect bounds) {
104 // A screen can be composed of multiple monitors.
105 GdkScreen* screen = gtk_window_get_screen(window);
106 gint monitor_num = gdk_screen_get_monitor_at_window(screen,
107 gtk_widget_get_window(GTK_WIDGET(window)));
108
109 GdkRectangle monitor_size;
110 gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor_size);
111 return bounds.size() == gfx::Size(monitor_size.width, monitor_size.height);
112}
113
114bool HandleTitleBarLeftMousePress(
115 GtkWindow* window,
116 const gfx::Rect& bounds,
117 GdkEventButton* event) {
118 // We want to start a move when the user single clicks, but not start a
119 // move when the user double clicks. However, a double click sends the
120 // following GDK events: GDK_BUTTON_PRESS, GDK_BUTTON_RELEASE,
121 // GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_BUTTON_RELEASE. If we
122 // start a gtk_window_begin_move_drag on the second GDK_BUTTON_PRESS,
123 // the call to gtk_window_maximize fails. To work around this, we
124 // keep track of the last click and if it's going to be a double click,
125 // we don't call gtk_window_begin_move_drag.
126 DCHECK(event->type == GDK_BUTTON_PRESS);
127 DCHECK(event->button == 1);
128
129 static GtkSettings* settings = gtk_settings_get_default();
130 gint double_click_time = 250;
131 gint double_click_distance = 5;
132 g_object_get(G_OBJECT(settings),
133 "gtk-double-click-time", &double_click_time,
134 "gtk-double-click-distance", &double_click_distance,
135 NULL);
136
137 guint32 click_time = event->time - last_click_time;
138 int click_move_x = abs(event->x - last_click_x);
139 int click_move_y = abs(event->y - last_click_y);
140
141 last_click_time = event->time;
142 last_click_x = static_cast<int>(event->x);
143 last_click_y = static_cast<int>(event->y);
144
145 if (click_time > static_cast<guint32>(double_click_time) ||
146 click_move_x > double_click_distance ||
147 click_move_y > double_click_distance) {
148 // Ignore drag requests if the window is the size of the screen.
149 // We do this to avoid triggering fullscreen mode in metacity
150 // (without the --no-force-fullscreen flag) and in compiz (with
151 // Legacy Fullscreen Mode enabled).
152 if (!BoundsMatchMonitorSize(window, bounds)) {
153 gtk_window_begin_move_drag(window, event->button,
154 static_cast<gint>(event->x_root),
155 static_cast<gint>(event->y_root),
156 event->time);
157 }
158 return TRUE;
159 }
160 return FALSE;
161}
162
163void UnMaximize(GtkWindow* window,
164 const gfx::Rect& bounds,
165 const gfx::Rect& restored_bounds) {
166 gtk_window_unmaximize(window);
167
168 // It can happen that you end up with a window whose restore size is the same
169 // as the size of the screen, so unmaximizing it merely remaximizes it due to
170 // the same WM feature that SetWindowSize() works around. We try to detect
171 // this and resize the window to work around the issue.
172 if (bounds.size() == restored_bounds.size())
173 gtk_window_resize(window, bounds.width(), bounds.height() - 1);
174}
175
[email protected]534b1632012-08-14 18:54:48176} // namespace gtk_window_util