root/chrome/browser/ui/gtk/create_application_shortcuts_dialog_gtk.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ShowCreateWebAppShortcutsDialog
  2. ShowCreateChromeAppShortcutsDialog
  3. error_dialog_
  4. CreateIconPixBuf
  5. CreateDialogBox
  6. OnCreateDialogResponse
  7. OnErrorDialogResponse
  8. CreateDesktopShortcut
  9. ShowErrorDialog
  10. OnToggleCheckbox
  11. web_contents_
  12. OnCreatedShortcut
  13. close_callback_
  14. CreateChromeApplicationShortcutsDialogGtk
  15. OnShortcutInfoLoaded
  16. CreateDesktopShortcut

// 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/webui/extensions/extension_icon_source.h"
#include "chrome/browser/web_applications/web_app.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 "extensions/common/extension.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);
}

void ShowCreateChromeAppShortcutsDialog(
    gfx::NativeWindow parent_window,
    Profile* profile,
    const extensions::Extension* app,
    const base::Closure& close_callback) {
  new CreateChromeApplicationShortcutsDialogGtk(parent_window, profile, app,
                                                close_callback);
}

}  // namespace chrome

CreateApplicationShortcutsDialogGtk::CreateApplicationShortcutsDialogGtk(
    GtkWindow* parent)
  : parent_(parent),
    desktop_checkbox_(NULL),
    menu_checkbox_(NULL),
    favicon_pixbuf_(NULL),
    create_dialog_(NULL),
    error_dialog_(NULL) {
  DCHECK_CURRENTLY_ON(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(base::UTF16ToUTF8(shortcut_info_.description));
  std::string title(base::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_CURRENTLY_ON(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_CURRENTLY_ON(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_));
    if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_))) {
      creation_locations.applications_menu_location =
          create_in_chrome_apps_subdir_ ?
              ShellIntegration::APP_MENU_LOCATION_SUBDIR_CHROMEAPPS :
              ShellIntegration::APP_MENU_LOCATION_ROOT;
    }
    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_CURRENTLY_ON(BrowserThread::FILE);
  ShellIntegrationLinux::CreateDesktopShortcut(shortcut_info,
                                               creation_locations);
  Release();
}

void CreateApplicationShortcutsDialogGtk::ShowErrorDialog() {
  DCHECK_CURRENTLY_ON(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);

  // Create URL app shortcuts in the top-level menu.
  create_in_chrome_apps_subdir_ = false;

  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,
        const base::Closure& close_callback)
    : CreateApplicationShortcutsDialogGtk(parent),
      app_(app),
      profile_path_(profile->GetPath()),
      close_callback_(close_callback) {

  // Place Chrome app shortcuts in the "Chrome Apps" submenu.
  create_in_chrome_apps_subdir_ = true;

  // Get shortcut information and icon now; they are needed for our UI.
  web_app::UpdateShortcutInfoAndIconForApp(
      app, profile,
      base::Bind(
          &CreateChromeApplicationShortcutsDialogGtk::OnShortcutInfoLoaded,
          this));
}

CreateChromeApplicationShortcutsDialogGtk::
    ~CreateChromeApplicationShortcutsDialogGtk() {
  if (!close_callback_.is_null())
    close_callback_.Run();
}

// 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_CURRENTLY_ON(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));
  }
}

/* [<][>][^][v][top][bottom][index][help] */