blob: c3035cfdbcf903755c0fe1545b8f5f2496a7c7e2 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.h"
#include <string>
#include "base/bind.h"
#include "base/environment.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/shell_integration_linux.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/gtk/gtk_util.h"
#include "chrome/browser/ui/web_applications/web_app_ui.h"
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
#include "chrome/browser/web_applications/web_app.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#include "ui/base/gtk/gtk_hig_constants.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/gtk_util.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_family.h"
#include "ui/gfx/image/image_skia.h"
using content::BrowserThread;
using extensions::Extension;
namespace {
// Size (in pixels) of the icon preview.
const int kIconPreviewSizePixels = 32;
// Minimum width (in pixels) of the shortcut description label.
const int kDescriptionLabelMinimumWidthPixels = 200;
} // namespace
namespace chrome {
void ShowCreateWebAppShortcutsDialog(gfx::NativeWindow parent_window,
content::WebContents* web_contents) {
new CreateWebApplicationShortcutsDialogGtk(parent_window, web_contents);
}
} // namespace chrome
void CreateChromeApplicationShortcutsDialogGtk::Show(GtkWindow* parent,
Profile* profile,
const Extension* app) {
new CreateChromeApplicationShortcutsDialogGtk(parent, profile, app);
}
CreateApplicationShortcutsDialogGtk::CreateApplicationShortcutsDialogGtk(
GtkWindow* parent)
: parent_(parent),
desktop_checkbox_(NULL),
menu_checkbox_(NULL),
favicon_pixbuf_(NULL),
create_dialog_(NULL),
error_dialog_(NULL) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Will be balanced by Release later.
AddRef();
}
void CreateApplicationShortcutsDialogGtk::CreateIconPixBuf(
const gfx::ImageFamily& image) {
// Get the icon closest to the desired preview size.
const gfx::Image* icon = image.GetBest(kIconPreviewSizePixels,
kIconPreviewSizePixels);
// There must be at least one icon in the image family.
CHECK(icon);
GdkPixbuf* pixbuf = icon->CopyGdkPixbuf();
// Prepare the icon. Scale it to the correct size to display in the dialog.
int pixbuf_width = gdk_pixbuf_get_width(pixbuf);
int pixbuf_height = gdk_pixbuf_get_height(pixbuf);
if (pixbuf_width == pixbuf_height) {
// Only scale the pixbuf if it's a square (for simplicity).
// Generally it should be square, if it's a favicon or app icon.
// Use the highest quality interpolation.
favicon_pixbuf_ = gdk_pixbuf_scale_simple(pixbuf,
kIconPreviewSizePixels,
kIconPreviewSizePixels,
GDK_INTERP_HYPER);
g_object_unref(pixbuf);
} else {
favicon_pixbuf_ = pixbuf;
}
}
void CreateApplicationShortcutsDialogGtk::CreateDialogBox(GtkWindow* parent) {
// Build the dialog.
create_dialog_ = gtk_dialog_new_with_buttons(
l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_TITLE).c_str(),
parent,
(GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
NULL);
gtk_widget_realize(create_dialog_);
gtk_window_set_resizable(GTK_WINDOW(create_dialog_), false);
gtk_util::AddButtonToDialog(create_dialog_,
l10n_util::GetStringUTF8(IDS_CANCEL).c_str(),
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
gtk_util::AddButtonToDialog(create_dialog_,
l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_COMMIT).c_str(),
GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT);
GtkWidget* content_area =
gtk_dialog_get_content_area(GTK_DIALOG(create_dialog_));
gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing);
GtkWidget* vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
gtk_container_add(GTK_CONTAINER(content_area), vbox);
// Create a box containing basic information about the new shortcut: an image
// on the left, and a description on the right.
GtkWidget* hbox = gtk_hbox_new(FALSE, ui::kControlSpacing);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(hbox),
ui::kControlSpacing);
// Put the icon preview in place.
GtkWidget* favicon_image = gtk_image_new_from_pixbuf(favicon_pixbuf_);
gtk_box_pack_start(GTK_BOX(hbox), favicon_image, FALSE, FALSE, 0);
// Create the label with application shortcut description.
GtkWidget* description_label = gtk_label_new(NULL);
gtk_box_pack_start(GTK_BOX(hbox), description_label, FALSE, FALSE, 0);
gtk_label_set_line_wrap(GTK_LABEL(description_label), TRUE);
gtk_widget_realize(description_label);
// Set the size request on the label so it knows where to line wrap. The width
// is the desired size of the dialog less the space reserved for padding and
// the image.
int label_width;
gtk_util::GetWidgetSizeFromResources(
description_label,
IDS_CREATE_SHORTCUTS_DIALOG_WIDTH_CHARS, -1, &label_width, NULL);
label_width -= ui::kControlSpacing * 3 +
gdk_pixbuf_get_width(favicon_pixbuf_);
// Enforce a minimum width, so that very large icons do not cause the label
// width to shrink to unreadable size, or become negative (which would crash).
if (label_width < kDescriptionLabelMinimumWidthPixels)
label_width = kDescriptionLabelMinimumWidthPixels;
gtk_util::SetLabelWidth(description_label, label_width);
std::string description(UTF16ToUTF8(shortcut_info_.description));
std::string title(UTF16ToUTF8(shortcut_info_.title));
gtk_label_set_text(GTK_LABEL(description_label),
(description.empty() ? title : description).c_str());
// Label on top of the checkboxes.
GtkWidget* checkboxes_label = gtk_label_new(
l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_LABEL).c_str());
gtk_misc_set_alignment(GTK_MISC(checkboxes_label), 0, 0);
gtk_box_pack_start(GTK_BOX(vbox), checkboxes_label, FALSE, FALSE, 0);
// Desktop checkbox.
desktop_checkbox_ = gtk_check_button_new_with_label(
l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_DESKTOP_CHKBOX).c_str());
gtk_box_pack_start(GTK_BOX(vbox), desktop_checkbox_, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(desktop_checkbox_), true);
g_signal_connect(desktop_checkbox_, "toggled",
G_CALLBACK(OnToggleCheckboxThunk), this);
// Menu checkbox.
menu_checkbox_ = gtk_check_button_new_with_label(
l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_MENU_CHKBOX).c_str());
gtk_box_pack_start(GTK_BOX(vbox), menu_checkbox_, FALSE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(menu_checkbox_), false);
g_signal_connect(menu_checkbox_, "toggled",
G_CALLBACK(OnToggleCheckboxThunk), this);
g_signal_connect(create_dialog_, "response",
G_CALLBACK(OnCreateDialogResponseThunk), this);
gtk_widget_show_all(create_dialog_);
}
CreateApplicationShortcutsDialogGtk::~CreateApplicationShortcutsDialogGtk() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
gtk_widget_destroy(create_dialog_);
if (error_dialog_)
gtk_widget_destroy(error_dialog_);
g_object_unref(favicon_pixbuf_);
}
void CreateApplicationShortcutsDialogGtk::OnCreateDialogResponse(
GtkWidget* widget, int response) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (response == GTK_RESPONSE_ACCEPT) {
ShellIntegration::ShortcutLocations creation_locations;
creation_locations.on_desktop =
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_));
creation_locations.in_applications_menu =
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_));
creation_locations.applications_menu_subdir = shortcut_menu_subdir_;
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut,
this, shortcut_info_, creation_locations));
OnCreatedShortcut();
} else {
Release();
}
}
void CreateApplicationShortcutsDialogGtk::OnErrorDialogResponse(
GtkWidget* widget, int response) {
Release();
}
void CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut(
const ShellIntegration::ShortcutInfo& shortcut_info,
const ShellIntegration::ShortcutLocations& creation_locations) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
ShellIntegrationLinux::CreateDesktopShortcut(shortcut_info,
creation_locations);
Release();
}
void CreateApplicationShortcutsDialogGtk::ShowErrorDialog() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Hide the create dialog so that the user can no longer interact with it.
gtk_widget_hide(create_dialog_);
error_dialog_ = gtk_dialog_new_with_buttons(
l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_TITLE).c_str(),
NULL,
(GtkDialogFlags) (GTK_DIALOG_NO_SEPARATOR),
GTK_STOCK_OK,
GTK_RESPONSE_ACCEPT,
NULL);
gtk_widget_realize(error_dialog_);
gtk_util::SetWindowSizeFromResources(
GTK_WINDOW(error_dialog_),
IDS_CREATE_SHORTCUTS_ERROR_DIALOG_WIDTH_CHARS,
IDS_CREATE_SHORTCUTS_ERROR_DIALOG_HEIGHT_LINES,
false); // resizable
GtkWidget* content_area =
gtk_dialog_get_content_area(GTK_DIALOG(error_dialog_));
gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing);
GtkWidget* vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
gtk_container_add(GTK_CONTAINER(content_area), vbox);
// Label on top of the checkboxes.
GtkWidget* description = gtk_label_new(
l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_LABEL).c_str());
gtk_label_set_line_wrap(GTK_LABEL(description), TRUE);
gtk_misc_set_alignment(GTK_MISC(description), 0, 0);
gtk_box_pack_start(GTK_BOX(vbox), description, FALSE, FALSE, 0);
g_signal_connect(error_dialog_, "response",
G_CALLBACK(OnErrorDialogResponseThunk), this);
gtk_widget_show_all(error_dialog_);
}
void CreateApplicationShortcutsDialogGtk::OnToggleCheckbox(GtkWidget* sender) {
gboolean can_accept = FALSE;
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_)) ||
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_))) {
can_accept = TRUE;
}
gtk_dialog_set_response_sensitive(GTK_DIALOG(create_dialog_),
GTK_RESPONSE_ACCEPT,
can_accept);
}
CreateWebApplicationShortcutsDialogGtk::CreateWebApplicationShortcutsDialogGtk(
GtkWindow* parent,
content::WebContents* web_contents)
: CreateApplicationShortcutsDialogGtk(parent),
web_contents_(web_contents) {
// Get shortcut information now, it's needed for our UI.
web_app::GetShortcutInfoForTab(web_contents, &shortcut_info_);
CreateIconPixBuf(shortcut_info_.favicon);
// NOTE: Leave shortcut_menu_subdir_ blank to create URL app shortcuts in the
// top-level menu.
CreateDialogBox(parent);
}
void CreateWebApplicationShortcutsDialogGtk::OnCreatedShortcut() {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents_);
if (browser)
chrome::ConvertTabToAppWindow(browser, web_contents_);
}
CreateChromeApplicationShortcutsDialogGtk::
CreateChromeApplicationShortcutsDialogGtk(
GtkWindow* parent,
Profile* profile,
const Extension* app)
: CreateApplicationShortcutsDialogGtk(parent),
app_(app),
profile_path_(profile->GetPath()) {
// Place Chrome app shortcuts in the "Chrome Apps" submenu.
shortcut_menu_subdir_ = web_app::GetAppShortcutsSubdirName();
// Get shortcut information and icon now; they are needed for our UI.
web_app::UpdateShortcutInfoAndIconForApp(
*app, profile,
base::Bind(
&CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded,
this));
}
// Called when the app's ShortcutInfo (with icon) is loaded.
void CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded(
const ShellIntegration::ShortcutInfo& shortcut_info) {
shortcut_info_ = shortcut_info;
CreateIconPixBuf(shortcut_info_.favicon);
CreateDialogBox(parent_);
}
void CreateChromeApplicationShortcutsDialogGtk::CreateDesktopShortcut(
const ShellIntegration::ShortcutInfo& shortcut_info,
const ShellIntegration::ShortcutLocations& creation_locations) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
if (web_app::CreateShortcutsOnFileThread(
shortcut_info, creation_locations,
web_app::SHORTCUT_CREATION_BY_USER)) {
Release();
} else {
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
base::Bind(&CreateChromeApplicationShortcutsDialogGtk::ShowErrorDialog,
this));
}
}