Home | History | Annotate | Download | only in gtk
      1 // Copyright (c) 2011 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/environment.h"
     10 #include "base/utf_string_conversions.h"
     11 #include "chrome/browser/shell_integration.h"
     12 #include "chrome/browser/ui/gtk/gtk_util.h"
     13 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
     14 #include "chrome/browser/ui/web_applications/web_app_ui.h"
     15 #include "chrome/common/extensions/extension.h"
     16 #include "chrome/common/extensions/extension_resource.h"
     17 #include "content/browser/browser_thread.h"
     18 #include "content/browser/tab_contents/tab_contents.h"
     19 #include "content/browser/tab_contents/tab_contents_delegate.h"
     20 #include "grit/chromium_strings.h"
     21 #include "grit/generated_resources.h"
     22 #include "grit/locale_settings.h"
     23 #include "ui/base/l10n/l10n_util.h"
     24 #include "ui/gfx/gtk_util.h"
     25 
     26 namespace {
     27 
     28 // Size (in pixels) of the icon preview.
     29 const int kIconPreviewSizePixels = 32;
     30 
     31 // Height (in lines) of the shortcut description label.
     32 const int kDescriptionLabelHeightLines = 3;
     33 
     34 }  // namespace
     35 
     36 // static
     37 void CreateWebApplicationShortcutsDialogGtk::Show(
     38     GtkWindow* parent, TabContentsWrapper* tab_contents) {
     39   new CreateWebApplicationShortcutsDialogGtk(parent, tab_contents);
     40 }
     41 
     42 void CreateChromeApplicationShortcutsDialogGtk::Show(GtkWindow* parent,
     43                                                      const Extension* app) {
     44   new CreateChromeApplicationShortcutsDialogGtk(parent, app);
     45 }
     46 
     47 
     48 CreateApplicationShortcutsDialogGtk::CreateApplicationShortcutsDialogGtk(
     49     GtkWindow* parent)
     50   : parent_(parent),
     51     desktop_checkbox_(NULL),
     52     menu_checkbox_(NULL),
     53     favicon_pixbuf_(NULL),
     54     create_dialog_(NULL),
     55     error_dialog_(NULL) {
     56   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     57 
     58   // Will be balanced by Release later.
     59   AddRef();
     60 }
     61 
     62 void CreateApplicationShortcutsDialogGtk::CreateIconPixBuf(
     63     const SkBitmap& bitmap) {
     64   // Prepare the icon. Try to scale it if it's too small, otherwise it would
     65   // look weird.
     66   GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&shortcut_info_.favicon);
     67   int pixbuf_width = gdk_pixbuf_get_width(pixbuf);
     68   int pixbuf_height = gdk_pixbuf_get_height(pixbuf);
     69   if (pixbuf_width == pixbuf_height && pixbuf_width < kIconPreviewSizePixels) {
     70     // Only scale the pixbuf if it's a square (for simplicity).
     71     // Generally it should be square, if it's a favicon or app icon.
     72     // Use the highest quality interpolation. The scaling is
     73     // going to have low quality anyway, because the initial image
     74     // is likely small.
     75     favicon_pixbuf_ = gdk_pixbuf_scale_simple(pixbuf,
     76                                               kIconPreviewSizePixels,
     77                                               kIconPreviewSizePixels,
     78                                               GDK_INTERP_HYPER);
     79     g_object_unref(pixbuf);
     80   } else {
     81     favicon_pixbuf_ = pixbuf;
     82   }
     83 }
     84 
     85 void CreateApplicationShortcutsDialogGtk::CreateDialogBox(GtkWindow* parent) {
     86   // Build the dialog.
     87   create_dialog_ = gtk_dialog_new_with_buttons(
     88       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_TITLE).c_str(),
     89       parent,
     90       (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR),
     91       NULL);
     92   gtk_widget_realize(create_dialog_);
     93   gtk_window_set_resizable(GTK_WINDOW(create_dialog_), false);
     94   gtk_util::AddButtonToDialog(create_dialog_,
     95       l10n_util::GetStringUTF8(IDS_CANCEL).c_str(),
     96       GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
     97   gtk_util::AddButtonToDialog(create_dialog_,
     98       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_COMMIT).c_str(),
     99       GTK_STOCK_APPLY, GTK_RESPONSE_ACCEPT);
    100 
    101   GtkWidget* content_area = GTK_DIALOG(create_dialog_)->vbox;
    102   gtk_box_set_spacing(GTK_BOX(content_area), gtk_util::kContentAreaSpacing);
    103 
    104   GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
    105   gtk_container_add(GTK_CONTAINER(content_area), vbox);
    106 
    107   // Create a box containing basic information about the new shortcut: an image
    108   // on the left, and a description on the right.
    109   GtkWidget* hbox = gtk_hbox_new(FALSE, gtk_util::kControlSpacing);
    110   gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    111   gtk_container_set_border_width(GTK_CONTAINER(hbox),
    112                                  gtk_util::kControlSpacing);
    113 
    114   // Put the icon preview in place.
    115   GtkWidget* favicon_image = gtk_image_new_from_pixbuf(favicon_pixbuf_);
    116   gtk_box_pack_start(GTK_BOX(hbox), favicon_image, FALSE, FALSE, 0);
    117 
    118   // Create the label with application shortcut description.
    119   GtkWidget* description_label = gtk_label_new(NULL);
    120   gtk_box_pack_start(GTK_BOX(hbox), description_label, FALSE, FALSE, 0);
    121   gtk_label_set_line_wrap(GTK_LABEL(description_label), TRUE);
    122   gtk_widget_realize(description_label);
    123 
    124   // Set the size request on the label so it knows where to line wrap. The width
    125   // is the desired size of the dialog less the space reserved for padding and
    126   // the image.
    127   int label_width;
    128   gtk_util::GetWidgetSizeFromResources(
    129       description_label,
    130       IDS_CREATE_SHORTCUTS_DIALOG_WIDTH_CHARS, -1, &label_width, NULL);
    131   label_width -= gtk_util::kControlSpacing * 3 +
    132       gdk_pixbuf_get_width(favicon_pixbuf_);
    133   gtk_util::SetLabelWidth(description_label, label_width);
    134 
    135   std::string description(UTF16ToUTF8(shortcut_info_.description));
    136   std::string title(UTF16ToUTF8(shortcut_info_.title));
    137   gtk_label_set_text(GTK_LABEL(description_label),
    138                      (description.empty() ? title : description).c_str());
    139 
    140   // Label on top of the checkboxes.
    141   GtkWidget* checkboxes_label = gtk_label_new(
    142       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_LABEL).c_str());
    143   gtk_misc_set_alignment(GTK_MISC(checkboxes_label), 0, 0);
    144   gtk_box_pack_start(GTK_BOX(vbox), checkboxes_label, FALSE, FALSE, 0);
    145 
    146   // Desktop checkbox.
    147   desktop_checkbox_ = gtk_check_button_new_with_label(
    148       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_DESKTOP_CHKBOX).c_str());
    149   gtk_box_pack_start(GTK_BOX(vbox), desktop_checkbox_, FALSE, FALSE, 0);
    150   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(desktop_checkbox_), true);
    151   g_signal_connect(desktop_checkbox_, "toggled",
    152                    G_CALLBACK(OnToggleCheckboxThunk), this);
    153 
    154   // Menu checkbox.
    155   menu_checkbox_ = gtk_check_button_new_with_label(
    156       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_MENU_CHKBOX).c_str());
    157   gtk_box_pack_start(GTK_BOX(vbox), menu_checkbox_, FALSE, FALSE, 0);
    158   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(menu_checkbox_), false);
    159   g_signal_connect(menu_checkbox_, "toggled",
    160                    G_CALLBACK(OnToggleCheckboxThunk), this);
    161 
    162   g_signal_connect(create_dialog_, "response",
    163                    G_CALLBACK(OnCreateDialogResponseThunk), this);
    164   gtk_widget_show_all(create_dialog_);
    165 }
    166 
    167 CreateApplicationShortcutsDialogGtk::~CreateApplicationShortcutsDialogGtk() {
    168   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    169 
    170   gtk_widget_destroy(create_dialog_);
    171 
    172   if (error_dialog_)
    173     gtk_widget_destroy(error_dialog_);
    174 
    175   g_object_unref(favicon_pixbuf_);
    176 }
    177 
    178 void CreateApplicationShortcutsDialogGtk::OnCreateDialogResponse(
    179     GtkWidget* widget, int response) {
    180   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    181 
    182   if (response == GTK_RESPONSE_ACCEPT) {
    183     shortcut_info_.create_on_desktop =
    184         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_));
    185     shortcut_info_.create_in_applications_menu =
    186         gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_));
    187     BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
    188          NewRunnableMethod(this,
    189              &CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut,
    190              shortcut_info_));
    191 
    192     OnCreatedShortcut();
    193   } else {
    194     Release();
    195   }
    196 }
    197 
    198 void CreateApplicationShortcutsDialogGtk::OnErrorDialogResponse(
    199     GtkWidget* widget, int response) {
    200   Release();
    201 }
    202 
    203 void CreateApplicationShortcutsDialogGtk::CreateDesktopShortcut(
    204     const ShellIntegration::ShortcutInfo& shortcut_info) {
    205   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    206 
    207   scoped_ptr<base::Environment> env(base::Environment::Create());
    208 
    209   std::string shortcut_template;
    210   if (ShellIntegration::GetDesktopShortcutTemplate(env.get(),
    211                                                    &shortcut_template)) {
    212     ShellIntegration::CreateDesktopShortcut(shortcut_info,
    213                                             shortcut_template);
    214     Release();
    215   } else {
    216     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
    217         NewRunnableMethod(this,
    218             &CreateApplicationShortcutsDialogGtk::ShowErrorDialog));
    219   }
    220 }
    221 
    222 void CreateApplicationShortcutsDialogGtk::ShowErrorDialog() {
    223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    224 
    225   // Hide the create dialog so that the user can no longer interact with it.
    226   gtk_widget_hide(create_dialog_);
    227 
    228   error_dialog_ = gtk_dialog_new_with_buttons(
    229       l10n_util::GetStringUTF8(IDS_CREATE_SHORTCUTS_ERROR_TITLE).c_str(),
    230       NULL,
    231       (GtkDialogFlags) (GTK_DIALOG_NO_SEPARATOR),
    232       GTK_STOCK_OK,
    233       GTK_RESPONSE_ACCEPT,
    234       NULL);
    235   gtk_widget_realize(error_dialog_);
    236   gtk_util::SetWindowSizeFromResources(
    237       GTK_WINDOW(error_dialog_),
    238       IDS_CREATE_SHORTCUTS_ERROR_DIALOG_WIDTH_CHARS,
    239       IDS_CREATE_SHORTCUTS_ERROR_DIALOG_HEIGHT_LINES,
    240       false);  // resizable
    241   GtkWidget* content_area = GTK_DIALOG(error_dialog_)->vbox;
    242   gtk_box_set_spacing(GTK_BOX(content_area), gtk_util::kContentAreaSpacing);
    243 
    244   GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing);
    245   gtk_container_add(GTK_CONTAINER(content_area), vbox);
    246 
    247   // Label on top of the checkboxes.
    248   GtkWidget* description = gtk_label_new(
    249       l10n_util::GetStringFUTF8(
    250           IDS_CREATE_SHORTCUTS_ERROR_LABEL,
    251           l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)).c_str());
    252   gtk_label_set_line_wrap(GTK_LABEL(description), TRUE);
    253   gtk_misc_set_alignment(GTK_MISC(description), 0, 0);
    254   gtk_box_pack_start(GTK_BOX(vbox), description, FALSE, FALSE, 0);
    255 
    256   g_signal_connect(error_dialog_, "response",
    257                    G_CALLBACK(OnErrorDialogResponseThunk), this);
    258   gtk_widget_show_all(error_dialog_);
    259 }
    260 
    261 void CreateApplicationShortcutsDialogGtk::OnToggleCheckbox(GtkWidget* sender) {
    262   gboolean can_accept = FALSE;
    263 
    264   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(desktop_checkbox_)) ||
    265       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(menu_checkbox_))) {
    266     can_accept = TRUE;
    267   }
    268 
    269   gtk_dialog_set_response_sensitive(GTK_DIALOG(create_dialog_),
    270                                     GTK_RESPONSE_ACCEPT,
    271                                     can_accept);
    272 }
    273 
    274 CreateWebApplicationShortcutsDialogGtk::CreateWebApplicationShortcutsDialogGtk(
    275     GtkWindow* parent,
    276     TabContentsWrapper* tab_contents)
    277   : CreateApplicationShortcutsDialogGtk(parent),
    278     tab_contents_(tab_contents) {
    279 
    280   // Get shortcut information now, it's needed for our UI.
    281   web_app::GetShortcutInfoForTab(tab_contents_, &shortcut_info_);
    282   CreateIconPixBuf(shortcut_info_.favicon);
    283 
    284   CreateDialogBox(parent);
    285 }
    286 
    287 void CreateWebApplicationShortcutsDialogGtk::OnCreatedShortcut() {
    288   if (tab_contents_->tab_contents()->delegate())
    289     tab_contents_->tab_contents()->delegate()->ConvertContentsToApplication(
    290         tab_contents_->tab_contents());
    291 }
    292 
    293 CreateChromeApplicationShortcutsDialogGtk::
    294     CreateChromeApplicationShortcutsDialogGtk(
    295         GtkWindow* parent,
    296         const Extension* app)
    297       : CreateApplicationShortcutsDialogGtk(parent),
    298         app_(app),
    299         ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this))  {
    300 
    301   // Get shortcut information now, it's needed for our UI.
    302   shortcut_info_.extension_id = app_->id();
    303   shortcut_info_.url = GURL(app_->launch_web_url());
    304   shortcut_info_.title = UTF8ToUTF16(app_->name());
    305   shortcut_info_.description = UTF8ToUTF16(app_->description());
    306 
    307   // Get the icon.
    308   const gfx::Size max_size(kIconPreviewSizePixels, kIconPreviewSizePixels);
    309   ExtensionResource icon_resource = app_->GetIconResource(
    310       kIconPreviewSizePixels, ExtensionIconSet::MATCH_BIGGER);
    311 
    312   // If no icon exists that is the desired size or larger, get the
    313   // largest icon available:
    314   if (icon_resource.empty())
    315     icon_resource = app_->GetIconResource(
    316         kIconPreviewSizePixels, ExtensionIconSet::MATCH_SMALLER);
    317 
    318   tracker_.LoadImage(app_,
    319                      icon_resource,
    320                      max_size,
    321                      ImageLoadingTracker::DONT_CACHE);
    322 }
    323 
    324 // Called by tracker_ when the app's icon is loaded.
    325 void CreateChromeApplicationShortcutsDialogGtk::OnImageLoaded(
    326     SkBitmap* image, const ExtensionResource& resource, int index) {
    327   if (image->isNull()) {
    328     NOTREACHED() << "Corrupt image in profile?";
    329     return;
    330   }
    331   shortcut_info_.favicon = *image;
    332 
    333   CreateIconPixBuf(*image);
    334   CreateDialogBox(parent_);
    335 }
    336