OLD | NEW |
| (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 "chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/environment.h" | |
11 #include "base/strings/utf_string_conversions.h" | |
12 #include "chrome/browser/profiles/profile.h" | |
13 #include "chrome/browser/shell_integration.h" | |
14 #include "chrome/browser/shell_integration_linux.h" | |
15 #include "chrome/browser/ui/browser.h" | |
16 #include "chrome/browser/ui/browser_commands.h" | |
17 #include "chrome/browser/ui/browser_dialogs.h" | |
18 #include "chrome/browser/ui/browser_finder.h" | |
19 #include "chrome/browser/ui/gtk/gtk_util.h" | |
20 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" | |
21 #include "chrome/browser/web_applications/web_app.h" | |
22 #include "content/public/browser/browser_thread.h" | |
23 #include "content/public/browser/web_contents.h" | |
24 #include "content/public/browser/web_contents_delegate.h" | |
25 #include "extensions/common/extension.h" | |
26 #include "extensions/common/manifest_handlers/icons_handler.h" | |
27 #include "grit/chromium_strings.h" | |
28 #include "grit/generated_resources.h" | |
29 #include "grit/locale_settings.h" | |
30 #include "grit/theme_resources.h" | |
31 #include "ui/base/gtk/gtk_hig_constants.h" | |
32 #include "ui/base/l10n/l10n_util.h" | |
33 #include "ui/gfx/gtk_util.h" | |
34 #include "ui/gfx/image/image.h" | |
35 #include "ui/gfx/image/image_family.h" | |
36 #include "ui/gfx/image/image_skia.h" | |
37 | |
38 using content::BrowserThread; | |
39 using extensions::Extension; | |
40 | |
41 namespace { | |
42 | |
43 // Size (in pixels) of the icon preview. | |
44 const int kIconPreviewSizePixels = 32; | |
45 | |
46 // Minimum width (in pixels) of the shortcut description label. | |
47 const int kDescriptionLabelMinimumWidthPixels = 200; | |
48 | |
49 } // namespace | |
50 | |
51 namespace chrome { | |
52 | |
53 void ShowCreateWebAppShortcutsDialog(gfx::NativeWindow parent_window, | |
54 content::WebContents* web_contents) { | |
55 new CreateWebApplicationShortcutsDialogGtk(parent_window, web_contents); | |
56 } | |
57 | |
58 void ShowCreateChromeAppShortcutsDialog( | |
59 gfx::NativeWindow parent_window, | |
60 Profile* profile, | |
61 const extensions::Extension* app, | |
62 const base::Closure& close_callback) { | |
63 new CreateChromeApplicationShortcutsDialogGtk(parent_window, profile, app, | |
64 close_callback); | |
65 } | |
66 | |
67 } // namespace chrome | |
68 | |
69 CreateApplicationShortcutsDialogGtk::CreateApplicationShortcutsDialogGtk( | |
70 GtkWindow* parent) | |
71 : parent_(parent), | |
72 desktop_checkbox_(NULL), | |
73 menu_checkbox_(NULL), | |
74 favicon_pixbuf_(NULL), | |
75 create_dialog_(NULL), | |
76 error_dialog_(NULL) { | |
77 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
78 | |
79 // Will be balanced by Release later. | |
80 AddRef(); | |
81 } | |
82 | |
83 void CreateApplicationShortcutsDialogGtk::CreateIconPixBuf( | |
84 const gfx::ImageFamily& image) { | |
85 // Get the icon closest to the desired preview size. | |
86 const gfx::Image* icon = image.GetBest(kIconPreviewSizePixels, | |
87 kIconPreviewSizePixels); | |
88 // There must be at least one icon in the image family. | |
89 CHECK(icon); | |
90 GdkPixbuf* pixbuf = icon->CopyGdkPixbuf(); | |
91 // Prepare the icon. Scale it to the correct size to display in the dialog. | |
92 int pixbuf_width = gdk_pixbuf_get_width(pixbuf); | |
93 int pixbuf_height = gdk_pixbuf_get_height(pixbuf); | |
94 if (pixbuf_width == pixbuf_height) { | |
95 // Only scale the pixbuf if it's a square (for simplicity). | |
96 // Generally it should be square, if it's a favicon or app icon. | |
97 // Use the highest quality interpolation. | |
98 favicon_pixbuf_ = gdk_pixbuf_scale_simple(pixbuf, | |
99 kIconPreviewSizePixels, | |
100 kIconPreviewSizePixels, | |
101 GDK_INTERP_HYPER); | |
102 g_object_unref(pixbuf); | |
103 } else { | |
104 favicon_pixbuf_ = pixbuf; | |
105 } | |
106 } | |
107 | |
108 void CreateApplicationShortcutsDialogGtk::CreateDialogBox(GtkWindow* parent) { | |
109 // Build the dialog. | |
110 create_dialog_ = gtk_dialog_new_with_buttons( | |
111 l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_TITLE).c_str(), | |
112 parent, | |
113 (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR), | |
114 NULL); | |
115 gtk_widget_realize(create_dialog_); | |
116 gtk_window_set_resizable(GTK_WINDOW(create_dialog_), false); | |
117 gtk_util::AddButtonToDialog(create_dialog_, | |
118 l10n_util::GetStringUTF8(IDS_CANCEL).c_str(), | |
119 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT); | |
120 gtk_util::AddButtonToDialog(create_dialog_, | |
121 l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_COMMIT).c_str(), | |
122 GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT); | |
123 | |
124 GtkWidget* content_area = | |
125 gtk_dialog_get_content_area(GTK_DIALOG(create_dialog_)); | |
126 gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing); | |
127 | |
128 GtkWidget* vbox = gtk_vbox_new(FALSE, ui::kControlSpacing); | |
129 gtk_container_add(GTK_CONTAINER(content_area), vbox); | |
130 | |
131 // Create a box containing basic information about the new shortcut: an image | |
132 // on the left, and a description on the right. | |
133 GtkWidget* hbox = gtk_hbox_new(FALSE, ui::kControlSpacing); | |
134 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); | |
135 gtk_container_set_border_width(GTK_CONTAINER(hbox), | |
136 ui::kControlSpacing); | |
137 | |
138 // Put the icon preview in place. | |
139 GtkWidget* favicon_image = gtk_image_new_from_pixbuf(favicon_pixbuf_); | |
140 gtk_box_pack_start(GTK_BOX(hbox), favicon_image, FALSE, FALSE, 0); | |
141 | |
142 // Create the label with application shortcut description. | |
143 GtkWidget* description_label = gtk_label_new(NULL); | |
144 gtk_box_pack_start(GTK_BOX(hbox), description_label, FALSE, FALSE, 0); | |
145 gtk_label_set_line_wrap(GTK_LABEL(description_label), TRUE); | |
146 gtk_widget_realize(description_label); | |
147 | |
148 // Set the size request on the label so it knows where to line wrap. The width | |
149 // is the desired size of the dialog less the space reserved for padding and | |
150 // the image. | |
151 int label_width; | |
152 gtk_util::GetWidgetSizeFromResources( | |
153 description_label, | |
154 IDS_CREATE_SHORTCUTS_DIALOG_WIDTH_CHARS, -1, &label_width, NULL); | |
155 label_width -= ui::kControlSpacing * 3 + | |
156 gdk_pixbuf_get_width(favicon_pixbuf_); | |
157 // Enforce a minimum width, so that very large icons do not cause the label | |
158 // width to shrink to unreadable size, or become negative (which would crash). | |
159 if (label_width < kDescriptionLabelMinimumWidthPixels) | |
160 label_width = kDescriptionLabelMinimumWidthPixels; | |
161 gtk_util::SetLabelWidth(description_label, label_width); | |
162 | |
163 std::string description(base::UTF16ToUTF8(shortcut_info_.description)); | |
164 std::string title(base::UTF16ToUTF8(shortcut_info_.title)); | |
165 gtk_label_set_text(GTK_LABEL(description_label), | |
166 (description.empty() ? title : description).c_str()); | |
167 | |
168 // Label on top of the checkboxes. | |
169 GtkWidget* checkboxes_label = gtk_label_new( | |
170 l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_LABEL).c_str()); | |
171 gtk_misc_set_alignment(GTK_MISC(checkboxes_label), 0, 0); | |
172 gtk_box_pack_start(GTK_BOX(vbox), checkboxes_label, FALSE, FALSE, 0); | |
173 | |
174 // Desktop checkbox. | |
175 desktop_checkbox_ = gtk_check_button_new_with_label( | |
176 l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_DESKTOP_CHKBOX).c_str()); | |
177 gtk_box_pack_start(GTK_BOX(vbox), desktop_checkbox_, FALSE, FALSE, 0); | |
178 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(desktop_checkbox_), true); | |
179 g_signal_connect(desktop_checkbox_, "toggled", | |
180 G_CALLBACK(OnToggleCheckboxThunk), this); | |
181 | |
182 // Menu checkbox. | |
183 menu_checkbox_ = gtk_check_button_new_with_label( | |
184 l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_MENU_CHKBOX).c_str()); | |
185 gtk_box_pack_start(GTK_BOX(vbox), menu_checkbox_, FALSE, FALSE, 0); | |
186 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(menu_checkbox_), false); | |
187 g_signal_connect(menu_checkbox_, "toggled", | |
188 G_CALLBACK(OnToggleCheckboxThunk), this); | |
189 | |
190 g_signal_connect(create_dialog_, "response", | |
191 G_CALLBACK(OnCreateDialogResponseThunk), this); | |
192 gtk_widget_show_all(create_dialog_); | |
193 } | |
194 | |
195 CreateApplicationShortcutsDialogGtk::~CreateApplicationShortcutsDialogGtk() { | |
196 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
197 | |
198 gtk_widget_destroy(create_dialog_); | |
199 | |
200 if (error_dialog_) | |
201 gtk_widget_destroy(error_dialog_); | |
202 | |
203 g_object_unref(favicon_pixbuf_); | |
204 } | |
205 | |
206 void CreateApplicationShortcutsDialogGtk::OnCreateDialogResponse( | |
207 GtkWidget* widget, int response) { | |
208 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
209 | |
210 if (response == GTK_RESPONSE_ACCEPT) { | |
211 ShellIntegration::ShortcutLocations creation_locations; | |
212 creation_locations.on_desktop = | |
213 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_)); | |
214 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_))) { | |
215 creation_locations.applications_menu_location = | |
216 create_in_chrome_apps_subdir_ ? | |
217 ShellIntegration::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS : | |
218 ShellIntegration::APP_MENU_LOCATION_ROOT; | |
219 } | |
220 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | |
221 base::Bind(&CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut, | |
222 this, shortcut_info_, creation_locations)); | |
223 | |
224 OnCreatedShortcut(); | |
225 } else { | |
226 Release(); | |
227 } | |
228 } | |
229 | |
230 void CreateApplicationShortcutsDialogGtk::OnErrorDialogResponse( | |
231 GtkWidget* widget, int response) { | |
232 Release(); | |
233 } | |
234 | |
235 void CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut( | |
236 const ShellIntegration::ShortcutInfo& shortcut_info, | |
237 const ShellIntegration::ShortcutLocations& creation_locations) { | |
238 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
239 ShellIntegrationLinux::CreateDesktopShortcut(shortcut_info, | |
240 creation_locations); | |
241 Release(); | |
242 } | |
243 | |
244 void CreateApplicationShortcutsDialogGtk::ShowErrorDialog() { | |
245 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
246 | |
247 // Hide the create dialog so that the user can no longer interact with it. | |
248 gtk_widget_hide(create_dialog_); | |
249 | |
250 error_dialog_ = gtk_dialog_new_with_buttons( | |
251 l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_TITLE).c_str(), | |
252 NULL, | |
253 (GtkDialogFlags) (GTK_DIALOG_NO_SEPARATOR), | |
254 GTK_STOCK_OK, | |
255 GTK_RESPONSE_ACCEPT, | |
256 NULL); | |
257 gtk_widget_realize(error_dialog_); | |
258 gtk_util::SetWindowSizeFromResources( | |
259 GTK_WINDOW(error_dialog_), | |
260 IDS_CREATE_SHORTCUTS_ERROR_DIALOG_WIDTH_CHARS, | |
261 IDS_CREATE_SHORTCUTS_ERROR_DIALOG_HEIGHT_LINES, | |
262 false); // resizable | |
263 GtkWidget* content_area = | |
264 gtk_dialog_get_content_area(GTK_DIALOG(error_dialog_)); | |
265 gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing); | |
266 | |
267 GtkWidget* vbox = gtk_vbox_new(FALSE, ui::kControlSpacing); | |
268 gtk_container_add(GTK_CONTAINER(content_area), vbox); | |
269 | |
270 // Label on top of the checkboxes. | |
271 GtkWidget* description = gtk_label_new( | |
272 l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_LABEL).c_str()); | |
273 gtk_label_set_line_wrap(GTK_LABEL(description), TRUE); | |
274 gtk_misc_set_alignment(GTK_MISC(description), 0, 0); | |
275 gtk_box_pack_start(GTK_BOX(vbox), description, FALSE, FALSE, 0); | |
276 | |
277 g_signal_connect(error_dialog_, "response", | |
278 G_CALLBACK(OnErrorDialogResponseThunk), this); | |
279 gtk_widget_show_all(error_dialog_); | |
280 } | |
281 | |
282 void CreateApplicationShortcutsDialogGtk::OnToggleCheckbox(GtkWidget* sender) { | |
283 gboolean can_accept = FALSE; | |
284 | |
285 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_)) || | |
286 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_))) { | |
287 can_accept = TRUE; | |
288 } | |
289 | |
290 gtk_dialog_set_response_sensitive(GTK_DIALOG(create_dialog_), | |
291 GTK_RESPONSE_ACCEPT, | |
292 can_accept); | |
293 } | |
294 | |
295 CreateWebApplicationShortcutsDialogGtk::CreateWebApplicationShortcutsDialogGtk( | |
296 GtkWindow* parent, | |
297 content::WebContents* web_contents) | |
298 : CreateApplicationShortcutsDialogGtk(parent), | |
299 web_contents_(web_contents) { | |
300 | |
301 // Get shortcut information now, it's needed for our UI. | |
302 web_app::GetShortcutInfoForTab(web_contents, &shortcut_info_); | |
303 CreateIconPixBuf(shortcut_info_.favicon); | |
304 | |
305 // Create URL app shortcuts in the top-level menu. | |
306 create_in_chrome_apps_subdir_ = false; | |
307 | |
308 CreateDialogBox(parent); | |
309 } | |
310 | |
311 void CreateWebApplicationShortcutsDialogGtk::OnCreatedShortcut() { | |
312 Browser* browser = chrome::FindBrowserWithWebContents(web_contents_); | |
313 if (browser) | |
314 chrome::ConvertTabToAppWindow(browser, web_contents_); | |
315 } | |
316 | |
317 CreateChromeApplicationShortcutsDialogGtk:: | |
318 CreateChromeApplicationShortcutsDialogGtk( | |
319 GtkWindow* parent, | |
320 Profile* profile, | |
321 const Extension* app, | |
322 const base::Closure& close_callback) | |
323 : CreateApplicationShortcutsDialogGtk(parent), | |
324 app_(app), | |
325 profile_path_(profile->GetPath()), | |
326 close_callback_(close_callback) { | |
327 | |
328 // Place Chrome app shortcuts in the "Chrome Apps" submenu. | |
329 create_in_chrome_apps_subdir_ = true; | |
330 | |
331 // Get shortcut information and icon now; they are needed for our UI. | |
332 web_app::UpdateShortcutInfoAndIconForApp( | |
333 app, profile, | |
334 base::Bind( | |
335 &CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded, | |
336 this)); | |
337 } | |
338 | |
339 CreateChromeApplicationShortcutsDialogGtk:: | |
340 ~CreateChromeApplicationShortcutsDialogGtk() { | |
341 if (!close_callback_.is_null()) | |
342 close_callback_.Run(); | |
343 } | |
344 | |
345 // Called when the app's ShortcutInfo (with icon) is loaded. | |
346 void CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded( | |
347 const ShellIntegration::ShortcutInfo& shortcut_info) { | |
348 shortcut_info_ = shortcut_info; | |
349 | |
350 CreateIconPixBuf(shortcut_info_.favicon); | |
351 CreateDialogBox(parent_); | |
352 } | |
353 | |
354 void CreateChromeApplicationShortcutsDialogGtk::CreateDesktopShortcut( | |
355 const ShellIntegration::ShortcutInfo& shortcut_info, | |
356 const ShellIntegration::ShortcutLocations& creation_locations) { | |
357 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | |
358 | |
359 if (web_app::internals::CreateShortcutsOnFileThread( | |
360 web_app::SHORTCUT_CREATION_BY_USER, | |
361 creation_locations, | |
362 shortcut_info)) { | |
363 Release(); | |
364 } else { | |
365 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
366 base::Bind(&CreateChromeApplicationShortcutsDialogGtk::ShowErrorDialog, | |
367 this)); | |
368 } | |
369 } | |
OLD | NEW |