Refactor management of overview window copy lifetime into a separate class.
[chromium-blink-merge.git] / content / shell / browser / shell_gtk.cc
blobf6e9059d4b888fc1e9f20032a4d09950eaf4190c
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 "content/shell/browser/shell.h"
7 #include <gdk/gdkkeysyms.h>
8 #include <gtk/gtk.h>
10 #include "base/logging.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/native_web_keyboard_event.h"
15 #include "content/public/browser/render_widget_host_view.h"
16 #include "content/public/browser/web_contents.h"
17 #include "content/public/browser/web_contents_view.h"
18 #include "content/public/common/renderer_preferences.h"
19 #include "content/shell/browser/shell_browser_context.h"
20 #include "content/shell/browser/shell_content_browser_client.h"
22 namespace content {
24 namespace {
26 // Callback for Debug > Show web inspector... menu item.
27 gboolean ShowWebInspectorActivated(GtkWidget* widget, Shell* shell) {
28 shell->ShowDevTools();
29 return FALSE; // Don't stop this message.
32 GtkWidget* AddMenuEntry(GtkWidget* menu_widget, const char* text,
33 GCallback callback, Shell* shell) {
34 GtkWidget* entry = gtk_menu_item_new_with_label(text);
35 g_signal_connect(entry, "activate", callback, shell);
36 gtk_menu_shell_append(GTK_MENU_SHELL(menu_widget), entry);
37 return entry;
40 GtkWidget* CreateMenu(GtkWidget* menu_bar, const char* text) {
41 GtkWidget* menu_widget = gtk_menu_new();
42 GtkWidget* menu_header = gtk_menu_item_new_with_label(text);
43 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_header), menu_widget);
44 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_header);
45 return menu_widget;
48 GtkWidget* CreateMenuBar(Shell* shell) {
49 GtkWidget* menu_bar = gtk_menu_bar_new();
50 GtkWidget* debug_menu = CreateMenu(menu_bar, "Debug");
51 AddMenuEntry(debug_menu, "Show web inspector...",
52 G_CALLBACK(ShowWebInspectorActivated), shell);
53 return menu_bar;
56 } // namespace
58 void Shell::PlatformInitialize(const gfx::Size& default_window_size) {
61 void Shell::PlatformCleanUp() {
62 // Nothing to clean up; GTK will clean up the widgets shortly after.
65 void Shell::PlatformEnableUIControl(UIControl control, bool is_enabled) {
66 if (headless_)
67 return;
69 GtkToolItem* item = NULL;
70 switch (control) {
71 case BACK_BUTTON:
72 item = back_button_;
73 break;
74 case FORWARD_BUTTON:
75 item = forward_button_;
76 break;
77 case STOP_BUTTON:
78 item = stop_button_;
79 break;
80 default:
81 NOTREACHED() << "Unknown UI control";
82 return;
84 gtk_widget_set_sensitive(GTK_WIDGET(item), is_enabled);
87 void Shell::PlatformSetAddressBarURL(const GURL& url) {
88 if (headless_)
89 return;
91 gtk_entry_set_text(GTK_ENTRY(url_edit_view_), url.spec().c_str());
94 void Shell::PlatformSetIsLoading(bool loading) {
95 if (headless_)
96 return;
98 if (loading)
99 gtk_spinner_start(GTK_SPINNER(spinner_));
100 else
101 gtk_spinner_stop(GTK_SPINNER(spinner_));
104 void Shell::PlatformCreateWindow(int width, int height) {
105 ui_elements_height_ = 0;
106 if (headless_) {
107 content_width_ = width;
108 content_height_ = height;
109 return;
112 window_ = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
113 gtk_window_set_title(window_, "Content Shell");
114 g_signal_connect(G_OBJECT(window_), "destroy",
115 G_CALLBACK(OnWindowDestroyedThunk), this);
117 vbox_ = gtk_vbox_new(FALSE, 0);
119 // Create the menu bar.
120 GtkWidget* menu_bar = CreateMenuBar(this);
121 gtk_box_pack_start(GTK_BOX(vbox_), menu_bar, FALSE, FALSE, 0);
123 // Create the object that mediates accelerators.
124 GtkAccelGroup* accel_group = gtk_accel_group_new();
125 gtk_window_add_accel_group(GTK_WINDOW(window_), accel_group);
127 // Set global window handling accelerators:
128 gtk_accel_group_connect(
129 accel_group, GDK_w, GDK_CONTROL_MASK,
130 GTK_ACCEL_VISIBLE,
131 g_cclosure_new(G_CALLBACK(OnCloseWindowKeyPressedThunk),
132 this, NULL));
134 gtk_accel_group_connect(
135 accel_group, GDK_n, GDK_CONTROL_MASK,
136 GTK_ACCEL_VISIBLE,
137 g_cclosure_new(G_CALLBACK(OnNewWindowKeyPressedThunk),
138 this, NULL));
140 gtk_accel_group_connect(
141 accel_group, GDK_F5, (GdkModifierType)0,
142 GTK_ACCEL_VISIBLE,
143 g_cclosure_new(G_CALLBACK(OnReloadKeyPressedThunk),
144 this, NULL));
146 GtkWidget* toolbar = gtk_toolbar_new();
147 // Turn off the labels on the toolbar buttons.
148 gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
150 back_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK);
151 g_signal_connect(back_button_, "clicked",
152 G_CALLBACK(&OnBackButtonClickedThunk), this);
153 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), back_button_, -1 /* append */);
154 gtk_widget_add_accelerator(GTK_WIDGET(back_button_), "clicked", accel_group,
155 GDK_Left, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
157 forward_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD);
158 g_signal_connect(forward_button_, "clicked",
159 G_CALLBACK(&OnForwardButtonClickedThunk), this);
160 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), forward_button_, -1 /* append */);
161 gtk_widget_add_accelerator(GTK_WIDGET(forward_button_), "clicked",
162 accel_group,
163 GDK_Right, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
165 reload_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_REFRESH);
166 g_signal_connect(reload_button_, "clicked",
167 G_CALLBACK(&OnReloadButtonClickedThunk), this);
168 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), reload_button_, -1 /* append */);
169 gtk_widget_add_accelerator(GTK_WIDGET(reload_button_), "clicked",
170 accel_group,
171 GDK_r, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
173 stop_button_ = gtk_tool_button_new_from_stock(GTK_STOCK_STOP);
174 g_signal_connect(stop_button_, "clicked",
175 G_CALLBACK(&OnStopButtonClickedThunk), this);
176 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), stop_button_, -1 /* append */);
178 url_edit_view_ = gtk_entry_new();
179 g_signal_connect(G_OBJECT(url_edit_view_), "activate",
180 G_CALLBACK(&OnURLEntryActivateThunk), this);
182 gtk_accel_group_connect(
183 accel_group, GDK_l, GDK_CONTROL_MASK,
184 GTK_ACCEL_VISIBLE,
185 g_cclosure_new(G_CALLBACK(OnHighlightURLViewThunk),
186 this, NULL));
188 GtkToolItem* tool_item = gtk_tool_item_new();
189 gtk_container_add(GTK_CONTAINER(tool_item), url_edit_view_);
190 gtk_tool_item_set_expand(tool_item, TRUE);
191 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1 /* append */);
193 // Center a 20x20 spinner in a 26x24 area.
194 GtkWidget* spinner_alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
195 gtk_alignment_set_padding(GTK_ALIGNMENT(spinner_alignment), 2, 2, 4, 4);
196 spinner_ = gtk_spinner_new();
197 gtk_widget_set_size_request(spinner_, 20, 20);
198 gtk_container_add(GTK_CONTAINER(spinner_alignment), spinner_);
200 spinner_item_ = gtk_tool_item_new();
201 gtk_container_add(GTK_CONTAINER(spinner_item_), spinner_alignment);
202 gtk_toolbar_insert(GTK_TOOLBAR(toolbar), spinner_item_, -1 /* append */);
204 gtk_box_pack_start(GTK_BOX(vbox_), toolbar, FALSE, FALSE, 0);
206 gtk_container_add(GTK_CONTAINER(window_), vbox_);
208 // Trigger layout of the UI elements, so that we can measure their
209 // heights. The width and height passed to this method are meant for the web
210 // contents view, not the top-level window. Since Gtk only seems to provide a
211 // suitable resizing function for top-level windows, we need to know how to
212 // convert from web contents view size to top-level window size.
213 gtk_widget_show_all(GTK_WIDGET(vbox_));
215 // Measure the heights of the UI elements, now that they have been laid out.
216 GtkRequisition elm_size;
217 gtk_widget_size_request(menu_bar, &elm_size);
218 ui_elements_height_ += elm_size.height;
219 gtk_widget_size_request(toolbar, &elm_size);
220 ui_elements_height_ += elm_size.height;
222 // We're ready to set an initial window size.
223 SizeTo(width, height);
225 // Finally, show the window.
226 gtk_widget_show_all(GTK_WIDGET(window_));
229 void Shell::PlatformSetContents() {
230 if (headless_) {
231 SizeTo(content_width_, content_height_);
232 return;
235 WebContentsView* content_view = web_contents_->GetView();
236 gtk_container_add(GTK_CONTAINER(vbox_), content_view->GetNativeView());
239 void Shell::SizeTo(int width, int height) {
240 content_width_ = width;
241 content_height_ = height;
243 if (window_) {
244 gtk_window_resize(window_, width, height + ui_elements_height_);
245 } else if (web_contents_) {
246 RenderWidgetHostView* render_widget_host_view =
247 web_contents_->GetRenderWidgetHostView();
248 if (render_widget_host_view)
249 render_widget_host_view->SetSize(gfx::Size(width, height));
253 void Shell::PlatformResizeSubViews() {
254 // Not needed; the subviews are bound.
257 void Shell::Close() {
258 if (headless_) {
259 delete this;
260 return;
263 gtk_widget_destroy(GTK_WIDGET(window_));
266 void Shell::OnBackButtonClicked(GtkWidget* widget) {
267 GoBackOrForward(-1);
270 void Shell::OnForwardButtonClicked(GtkWidget* widget) {
271 GoBackOrForward(1);
274 void Shell::OnReloadButtonClicked(GtkWidget* widget) {
275 Reload();
278 void Shell::OnStopButtonClicked(GtkWidget* widget) {
279 Stop();
282 void Shell::OnURLEntryActivate(GtkWidget* entry) {
283 const gchar* str = gtk_entry_get_text(GTK_ENTRY(entry));
284 GURL url(str);
285 if (!url.has_scheme())
286 url = GURL(std::string("http://") + std::string(str));
287 if (url.is_valid())
288 LoadURL(url);
291 // Callback for when the main window is destroyed.
292 gboolean Shell::OnWindowDestroyed(GtkWidget* window) {
293 delete this;
294 return FALSE; // Don't stop this message.
297 gboolean Shell::OnCloseWindowKeyPressed(GtkAccelGroup* accel_group,
298 GObject* acceleratable,
299 guint keyval,
300 GdkModifierType modifier) {
301 gtk_widget_destroy(GTK_WIDGET(window_));
302 return TRUE;
305 gboolean Shell::OnNewWindowKeyPressed(GtkAccelGroup* accel_group,
306 GObject* acceleratable,
307 guint keyval,
308 GdkModifierType modifier) {
309 ShellBrowserContext* browser_context =
310 ShellContentBrowserClient::Get()->browser_context();
311 Shell::CreateNewWindow(browser_context,
312 GURL(),
313 NULL,
314 MSG_ROUTING_NONE,
315 gfx::Size());
316 return TRUE;
319 gboolean Shell::OnHighlightURLView(GtkAccelGroup* accel_group,
320 GObject* acceleratable,
321 guint keyval,
322 GdkModifierType modifier) {
323 gtk_widget_grab_focus(GTK_WIDGET(url_edit_view_));
324 return TRUE;
327 gboolean Shell::OnReloadKeyPressed(GtkAccelGroup* accel_group,
328 GObject* acceleratable,
329 guint keyval,
330 GdkModifierType modifier) {
331 Reload();
332 return TRUE;
335 void Shell::PlatformSetTitle(const string16& title) {
336 if (headless_)
337 return;
339 std::string title_utf8 = UTF16ToUTF8(title);
340 gtk_window_set_title(GTK_WINDOW(window_), title_utf8.c_str());
343 } // namespace content