| 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/extensions/extension_installed_bubble_gtk.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/i18n/rtl.h" | |
| 10 #include "base/strings/utf_string_conversions.h" | |
| 11 #include "chrome/browser/chrome_notification_types.h" | |
| 12 #include "chrome/browser/extensions/api/commands/command_service.h" | |
| 13 #include "chrome/browser/extensions/extension_action.h" | |
| 14 #include "chrome/browser/extensions/extension_action_manager.h" | |
| 15 #include "chrome/browser/ui/browser.h" | |
| 16 #include "chrome/browser/ui/browser_dialogs.h" | |
| 17 #include "chrome/browser/ui/gtk/browser_actions_toolbar_gtk.h" | |
| 18 #include "chrome/browser/ui/gtk/browser_toolbar_gtk.h" | |
| 19 #include "chrome/browser/ui/gtk/browser_window_gtk.h" | |
| 20 #include "chrome/browser/ui/gtk/gtk_theme_service.h" | |
| 21 #include "chrome/browser/ui/gtk/gtk_util.h" | |
| 22 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h" | |
| 23 #include "chrome/browser/ui/singleton_tabs.h" | |
| 24 #include "chrome/common/extensions/api/omnibox/omnibox_handler.h" | |
| 25 #include "chrome/common/url_constants.h" | |
| 26 #include "content/public/browser/notification_details.h" | |
| 27 #include "content/public/browser/notification_source.h" | |
| 28 #include "extensions/common/extension.h" | |
| 29 #include "grit/chromium_strings.h" | |
| 30 #include "grit/generated_resources.h" | |
| 31 #include "grit/theme_resources.h" | |
| 32 #include "ui/base/gtk/gtk_hig_constants.h" | |
| 33 #include "ui/base/l10n/l10n_util.h" | |
| 34 #include "ui/base/resource/resource_bundle.h" | |
| 35 #include "ui/gfx/gtk_util.h" | |
| 36 | |
| 37 using extensions::Extension; | |
| 38 using extensions::ExtensionActionManager; | |
| 39 | |
| 40 namespace { | |
| 41 | |
| 42 const int kHorizontalColumnSpacing = 10; | |
| 43 const int kIconPadding = 3; | |
| 44 const int kIconSize = 43; | |
| 45 const int kTextColumnVerticalSpacing = 7; | |
| 46 const int kTextColumnWidth = 350; | |
| 47 | |
| 48 } // namespace | |
| 49 | |
| 50 namespace chrome { | |
| 51 | |
| 52 void ShowExtensionInstalledBubble(const Extension* extension, | |
| 53 Browser* browser, | |
| 54 const SkBitmap& icon) { | |
| 55 ExtensionInstalledBubbleGtk::Show(extension, browser, icon); | |
| 56 } | |
| 57 | |
| 58 } // namespace chrome | |
| 59 | |
| 60 void ExtensionInstalledBubbleGtk::Show(const Extension* extension, | |
| 61 Browser* browser, | |
| 62 const SkBitmap& icon) { | |
| 63 new ExtensionInstalledBubbleGtk(extension, browser, icon); | |
| 64 } | |
| 65 | |
| 66 ExtensionInstalledBubbleGtk::ExtensionInstalledBubbleGtk( | |
| 67 const Extension* extension, Browser *browser, const SkBitmap& icon) | |
| 68 : bubble_(this, extension, browser, icon) { | |
| 69 } | |
| 70 | |
| 71 ExtensionInstalledBubbleGtk::~ExtensionInstalledBubbleGtk() {} | |
| 72 | |
| 73 void ExtensionInstalledBubbleGtk::OnDestroy(GtkWidget* widget) { | |
| 74 gtk_bubble_ = NULL; | |
| 75 delete this; | |
| 76 } | |
| 77 | |
| 78 bool ExtensionInstalledBubbleGtk::MaybeShowNow() { | |
| 79 BrowserWindowGtk* browser_window = | |
| 80 BrowserWindowGtk::GetBrowserWindowForNativeWindow( | |
| 81 bubble_.browser()->window()->GetNativeWindow()); | |
| 82 | |
| 83 GtkWidget* reference_widget = NULL; | |
| 84 | |
| 85 if (bubble_.type() == bubble_.BROWSER_ACTION) { | |
| 86 BrowserActionsToolbarGtk* toolbar = | |
| 87 browser_window->GetToolbar()->GetBrowserActionsToolbar(); | |
| 88 if (toolbar->animating()) | |
| 89 return false; | |
| 90 | |
| 91 reference_widget = toolbar->GetBrowserActionWidget(bubble_.extension()); | |
| 92 // glib delays recalculating layout, but we need reference_widget to know | |
| 93 // its coordinates, so we force a check_resize here. | |
| 94 gtk_container_check_resize(GTK_CONTAINER( | |
| 95 browser_window->GetToolbar()->widget())); | |
| 96 // If the widget is not visible then browser_window could be incognito | |
| 97 // with this extension disabled. Try showing it on the chevron. | |
| 98 // If that fails, fall back to default position. | |
| 99 if (reference_widget && !gtk_widget_get_visible(reference_widget)) { | |
| 100 reference_widget = gtk_widget_get_visible(toolbar->chevron()) ? | |
| 101 toolbar->chevron() : NULL; | |
| 102 } | |
| 103 } else if (bubble_.type() == bubble_.PAGE_ACTION) { | |
| 104 LocationBarViewGtk* location_bar_view = | |
| 105 browser_window->GetToolbar()->GetLocationBarView(); | |
| 106 ExtensionAction* page_action = | |
| 107 ExtensionActionManager::Get(bubble_.browser()->profile())-> | |
| 108 GetPageAction(*bubble_.extension()); | |
| 109 location_bar_view->SetPreviewEnabledPageAction(page_action, | |
| 110 true); // preview_enabled | |
| 111 reference_widget = location_bar_view->GetPageActionWidget(page_action); | |
| 112 // glib delays recalculating layout, but we need reference_widget to know | |
| 113 // its coordinates, so we force a check_resize here. | |
| 114 gtk_container_check_resize(GTK_CONTAINER( | |
| 115 browser_window->GetToolbar()->widget())); | |
| 116 DCHECK(reference_widget); | |
| 117 } else if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) { | |
| 118 LocationBarViewGtk* location_bar_view = | |
| 119 browser_window->GetToolbar()->GetLocationBarView(); | |
| 120 reference_widget = location_bar_view->location_entry_widget(); | |
| 121 DCHECK(reference_widget); | |
| 122 } | |
| 123 | |
| 124 // Default case. | |
| 125 if (reference_widget == NULL) | |
| 126 reference_widget = browser_window->GetToolbar()->GetAppMenuButton(); | |
| 127 | |
| 128 GtkThemeService* theme_provider = GtkThemeService::GetFrom( | |
| 129 bubble_.browser()->profile()); | |
| 130 | |
| 131 // Setup the BubbleGtk content. | |
| 132 GtkWidget* bubble_content = gtk_hbox_new(FALSE, kHorizontalColumnSpacing); | |
| 133 gtk_container_set_border_width(GTK_CONTAINER(bubble_content), | |
| 134 ui::kContentAreaBorder); | |
| 135 | |
| 136 if (!bubble_.icon().isNull()) { | |
| 137 // Scale icon down to 43x43, but allow smaller icons (don't scale up). | |
| 138 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(bubble_.icon()); | |
| 139 gfx::Size size(bubble_.icon().width(), bubble_.icon().height()); | |
| 140 if (size.width() > kIconSize || size.height() > kIconSize) { | |
| 141 if (size.width() > size.height()) { | |
| 142 size.set_height(size.height() * kIconSize / size.width()); | |
| 143 size.set_width(kIconSize); | |
| 144 } else { | |
| 145 size.set_width(size.width() * kIconSize / size.height()); | |
| 146 size.set_height(kIconSize); | |
| 147 } | |
| 148 | |
| 149 GdkPixbuf* old = pixbuf; | |
| 150 pixbuf = gdk_pixbuf_scale_simple(pixbuf, size.width(), size.height(), | |
| 151 GDK_INTERP_BILINEAR); | |
| 152 g_object_unref(old); | |
| 153 } | |
| 154 | |
| 155 // Put Icon in top of the left column. | |
| 156 GtkWidget* icon_column = gtk_vbox_new(FALSE, 0); | |
| 157 // Use 3 pixel padding to get visual balance with BubbleGtk border on the | |
| 158 // left. | |
| 159 gtk_box_pack_start(GTK_BOX(bubble_content), icon_column, FALSE, FALSE, | |
| 160 kIconPadding); | |
| 161 GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf); | |
| 162 g_object_unref(pixbuf); | |
| 163 gtk_box_pack_start(GTK_BOX(icon_column), image, FALSE, FALSE, 0); | |
| 164 } | |
| 165 | |
| 166 // Center text column. | |
| 167 GtkWidget* text_column = gtk_vbox_new(FALSE, kTextColumnVerticalSpacing); | |
| 168 gtk_box_pack_start(GTK_BOX(bubble_content), text_column, FALSE, FALSE, 0); | |
| 169 | |
| 170 // Heading label. | |
| 171 GtkWidget* heading_label = gtk_label_new(NULL); | |
| 172 base::string16 extension_name = | |
| 173 base::UTF8ToUTF16(bubble_.extension()->name()); | |
| 174 base::i18n::AdjustStringForLocaleDirection(&extension_name); | |
| 175 std::string heading_text = l10n_util::GetStringFUTF8( | |
| 176 IDS_EXTENSION_INSTALLED_HEADING, extension_name); | |
| 177 char* markup = g_markup_printf_escaped("<span size=\"larger\">%s</span>", | |
| 178 heading_text.c_str()); | |
| 179 gtk_label_set_markup(GTK_LABEL(heading_label), markup); | |
| 180 g_free(markup); | |
| 181 | |
| 182 gtk_util::SetLabelWidth(heading_label, kTextColumnWidth); | |
| 183 gtk_box_pack_start(GTK_BOX(text_column), heading_label, FALSE, FALSE, 0); | |
| 184 | |
| 185 bool has_keybinding = false; | |
| 186 | |
| 187 // Browser action label. | |
| 188 if (bubble_.type() == bubble_.BROWSER_ACTION) { | |
| 189 extensions::CommandService* command_service = | |
| 190 extensions::CommandService::Get(bubble_.browser()->profile()); | |
| 191 extensions::Command browser_action_command; | |
| 192 GtkWidget* info_label; | |
| 193 if (!command_service->GetBrowserActionCommand( | |
| 194 bubble_.extension()->id(), | |
| 195 extensions::CommandService::ACTIVE_ONLY, | |
| 196 &browser_action_command, | |
| 197 NULL)) { | |
| 198 info_label = gtk_label_new(l10n_util::GetStringUTF8( | |
| 199 IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO).c_str()); | |
| 200 } else { | |
| 201 info_label = gtk_label_new(l10n_util::GetStringFUTF8( | |
| 202 IDS_EXTENSION_INSTALLED_BROWSER_ACTION_INFO_WITH_SHORTCUT, | |
| 203 browser_action_command.accelerator().GetShortcutText()).c_str()); | |
| 204 has_keybinding = true; | |
| 205 } | |
| 206 gtk_util::SetLabelWidth(info_label, kTextColumnWidth); | |
| 207 gtk_box_pack_start(GTK_BOX(text_column), info_label, FALSE, FALSE, 0); | |
| 208 } | |
| 209 | |
| 210 // Page action label. | |
| 211 if (bubble_.type() == bubble_.PAGE_ACTION) { | |
| 212 extensions::CommandService* command_service = | |
| 213 extensions::CommandService::Get(bubble_.browser()->profile()); | |
| 214 extensions::Command page_action_command; | |
| 215 GtkWidget* info_label; | |
| 216 if (!command_service->GetPageActionCommand( | |
| 217 bubble_.extension()->id(), | |
| 218 extensions::CommandService::ACTIVE_ONLY, | |
| 219 &page_action_command, | |
| 220 NULL)) { | |
| 221 info_label = gtk_label_new(l10n_util::GetStringUTF8( | |
| 222 IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO).c_str()); | |
| 223 } else { | |
| 224 info_label = gtk_label_new(l10n_util::GetStringFUTF8( | |
| 225 IDS_EXTENSION_INSTALLED_PAGE_ACTION_INFO_WITH_SHORTCUT, | |
| 226 page_action_command.accelerator().GetShortcutText()).c_str()); | |
| 227 has_keybinding = true; | |
| 228 } | |
| 229 gtk_util::SetLabelWidth(info_label, kTextColumnWidth); | |
| 230 gtk_box_pack_start(GTK_BOX(text_column), info_label, FALSE, FALSE, 0); | |
| 231 } | |
| 232 | |
| 233 // Omnibox keyword label. | |
| 234 if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) { | |
| 235 GtkWidget* info_label = gtk_label_new(l10n_util::GetStringFUTF8( | |
| 236 IDS_EXTENSION_INSTALLED_OMNIBOX_KEYWORD_INFO, | |
| 237 base::UTF8ToUTF16(extensions::OmniboxInfo::GetKeyword( | |
| 238 bubble_.extension()))).c_str()); | |
| 239 gtk_util::SetLabelWidth(info_label, kTextColumnWidth); | |
| 240 gtk_box_pack_start(GTK_BOX(text_column), info_label, FALSE, FALSE, 0); | |
| 241 } | |
| 242 | |
| 243 if (has_keybinding) { | |
| 244 GtkWidget* manage_link = theme_provider->BuildChromeLinkButton( | |
| 245 l10n_util::GetStringUTF8(IDS_EXTENSION_INSTALLED_MANAGE_SHORTCUTS)); | |
| 246 GtkWidget* link_hbox = gtk_hbox_new(FALSE, 0); | |
| 247 // Stick it in an hbox so it doesn't expand to the whole width. | |
| 248 gtk_box_pack_end(GTK_BOX(link_hbox), manage_link, FALSE, FALSE, 0); | |
| 249 gtk_box_pack_start(GTK_BOX(text_column), link_hbox, FALSE, FALSE, 0); | |
| 250 g_signal_connect(manage_link, "clicked", | |
| 251 G_CALLBACK(OnLinkClickedThunk), this); | |
| 252 } else { | |
| 253 // Manage label. | |
| 254 GtkWidget* manage_label = gtk_label_new( | |
| 255 l10n_util::GetStringUTF8(IDS_EXTENSION_INSTALLED_MANAGE_INFO).c_str()); | |
| 256 gtk_util::SetLabelWidth(manage_label, kTextColumnWidth); | |
| 257 gtk_box_pack_start(GTK_BOX(text_column), manage_label, FALSE, FALSE, 0); | |
| 258 } | |
| 259 | |
| 260 // Create and pack the close button. | |
| 261 GtkWidget* close_column = gtk_vbox_new(FALSE, 0); | |
| 262 gtk_box_pack_start(GTK_BOX(bubble_content), close_column, FALSE, FALSE, 0); | |
| 263 close_button_.reset(CustomDrawButton::CloseButtonBubble(theme_provider)); | |
| 264 g_signal_connect(close_button_->widget(), "clicked", | |
| 265 G_CALLBACK(OnButtonClick), this); | |
| 266 gtk_box_pack_start(GTK_BOX(close_column), close_button_->widget(), | |
| 267 FALSE, FALSE, 0); | |
| 268 | |
| 269 BubbleGtk::FrameStyle frame_style = BubbleGtk::ANCHOR_TOP_RIGHT; | |
| 270 | |
| 271 gfx::Rect bounds = gtk_util::WidgetBounds(reference_widget); | |
| 272 if (bubble_.type() == bubble_.OMNIBOX_KEYWORD) { | |
| 273 // Reverse the arrow for omnibox keywords, since the bubble will be on the | |
| 274 // other side of the window. We also clear the width to avoid centering | |
| 275 // the popup on the URL bar. | |
| 276 frame_style = BubbleGtk::ANCHOR_TOP_LEFT; | |
| 277 if (base::i18n::IsRTL()) | |
| 278 bounds.Offset(bounds.width(), 0); | |
| 279 bounds.set_width(0); | |
| 280 } | |
| 281 | |
| 282 gtk_bubble_ = BubbleGtk::Show(reference_widget, | |
| 283 &bounds, | |
| 284 bubble_content, | |
| 285 frame_style, | |
| 286 BubbleGtk::MATCH_SYSTEM_THEME | | |
| 287 BubbleGtk::POPUP_WINDOW | | |
| 288 BubbleGtk::GRAB_INPUT, | |
| 289 theme_provider, | |
| 290 this); | |
| 291 g_signal_connect(bubble_content, "destroy", | |
| 292 G_CALLBACK(&OnDestroyThunk), this); | |
| 293 | |
| 294 // gtk_bubble_ is now the owner of |this| and deletes it when the bubble | |
| 295 // goes away. | |
| 296 bubble_.IgnoreBrowserClosing(); | |
| 297 return true; | |
| 298 } | |
| 299 | |
| 300 // static | |
| 301 void ExtensionInstalledBubbleGtk::OnButtonClick(GtkWidget* button, | |
| 302 ExtensionInstalledBubbleGtk* bubble) { | |
| 303 if (button == bubble->close_button_->widget()) { | |
| 304 bubble->gtk_bubble_->Close(); | |
| 305 } else { | |
| 306 NOTREACHED(); | |
| 307 } | |
| 308 } | |
| 309 | |
| 310 void ExtensionInstalledBubbleGtk::OnLinkClicked(GtkWidget* widget) { | |
| 311 gtk_bubble_->Close(); | |
| 312 | |
| 313 std::string configure_url = chrome::kChromeUIExtensionsURL; | |
| 314 configure_url += chrome::kExtensionConfigureCommandsSubPage; | |
| 315 chrome::NavigateParams params( | |
| 316 chrome::GetSingletonTabNavigateParams( | |
| 317 bubble_.browser(), GURL(configure_url.c_str()))); | |
| 318 chrome::Navigate(¶ms); | |
| 319 } | |
| 320 | |
| 321 void ExtensionInstalledBubbleGtk::BubbleClosing(BubbleGtk* bubble, | |
| 322 bool closed_by_escape) { | |
| 323 if (bubble_.extension() && bubble_.type() == bubble_.PAGE_ACTION) { | |
| 324 // Turn the page action preview off. | |
| 325 BrowserWindowGtk* browser_window = | |
| 326 BrowserWindowGtk::GetBrowserWindowForNativeWindow( | |
| 327 bubble_.browser()->window()->GetNativeWindow()); | |
| 328 LocationBarViewGtk* location_bar_view = | |
| 329 browser_window->GetToolbar()->GetLocationBarView(); | |
| 330 location_bar_view->SetPreviewEnabledPageAction( | |
| 331 ExtensionActionManager::Get(bubble_.browser()->profile())-> | |
| 332 GetPageAction(*bubble_.extension()), | |
| 333 false); // preview_enabled | |
| 334 } | |
| 335 } | |
| OLD | NEW |