| // 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/infobars/extension_infobar_gtk.h" |
| |
| #include "base/debug/trace_event.h" |
| #include "chrome/browser/extensions/extension_context_menu_model.h" |
| #include "chrome/browser/extensions/extension_view_host.h" |
| #include "chrome/browser/extensions/image_loader.h" |
| #include "chrome/browser/platform_util.h" |
| #include "chrome/browser/ui/gtk/browser_window_gtk.h" |
| #include "chrome/browser/ui/gtk/custom_button.h" |
| #include "chrome/browser/ui/gtk/gtk_chrome_button.h" |
| #include "chrome/browser/ui/gtk/gtk_util.h" |
| #include "chrome/browser/ui/gtk/infobars/infobar_container_gtk.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "chrome/common/extensions/extension_icon_set.h" |
| #include "chrome/common/extensions/manifest_handlers/icons_handler.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/browser/render_widget_host_view.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/common/extension_resource.h" |
| #include "grit/theme_resources.h" |
| #include "ui/base/gtk/gtk_signal_registrar.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/gtk_util.h" |
| #include "ui/gfx/image/image.h" |
| |
| |
| // ExtensionInfoBarDelegate --------------------------------------------------- |
| |
| // static |
| scoped_ptr<InfoBar> ExtensionInfoBarDelegate::CreateInfoBar( |
| scoped_ptr<ExtensionInfoBarDelegate> delegate) { |
| return scoped_ptr<InfoBar>(new ExtensionInfoBarGtk(delegate.Pass())); |
| } |
| |
| |
| // ExtensionInfoBarGtk -------------------------------------------------------- |
| |
| ExtensionInfoBarGtk::ExtensionInfoBarGtk( |
| scoped_ptr<ExtensionInfoBarDelegate> delegate) |
| : InfoBarGtk(delegate.PassAs<InfoBarDelegate>()), |
| view_(NULL), |
| button_(NULL), |
| icon_(NULL), |
| alignment_(NULL), |
| weak_ptr_factory_(this) { |
| int height = GetDelegate()->height(); |
| SetBarTargetHeight((height > 0) ? (height + kSeparatorLineHeight) : 0); |
| } |
| |
| ExtensionInfoBarGtk::~ExtensionInfoBarGtk() { |
| } |
| |
| void ExtensionInfoBarGtk::PlatformSpecificSetOwner() { |
| InfoBarGtk::PlatformSpecificSetOwner(); |
| |
| // Always render the close button as if we were doing chrome style widget |
| // rendering. For extension infobars, we force chrome style rendering because |
| // extension authors are going to expect to match the declared gradient in |
| // extensions_infobar.css, and the close button provided by some GTK+ themes |
| // won't look good on this background. |
| ForceCloseButtonToUseChromeTheme(); |
| |
| icon_ = gtk_image_new(); |
| gtk_misc_set_alignment(GTK_MISC(icon_), 0.5, 0.5); |
| |
| extensions::ExtensionViewHost* extension_view_host = |
| GetDelegate()->extension_view_host(); |
| const extensions::Extension* extension = extension_view_host->extension(); |
| |
| if (extension->ShowConfigureContextMenus()) { |
| button_ = gtk_chrome_button_new(); |
| gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(button_), FALSE); |
| g_object_set_data(G_OBJECT(button_), "left-align-popup", |
| reinterpret_cast<void*>(true)); |
| |
| gtk_button_set_image(GTK_BUTTON(button_), icon_); |
| gtk_util::CenterWidgetInHBox(hbox(), button_, false, 0); |
| } else { |
| gtk_util::CenterWidgetInHBox(hbox(), icon_, false, 0); |
| } |
| |
| // Start loading the image for the menu button. |
| extensions::ExtensionResource icon_resource = |
| extensions::IconsInfo::GetIconResource( |
| extension, |
| extension_misc::EXTENSION_ICON_BITTY, |
| ExtensionIconSet::MATCH_EXACTLY); |
| // Load image asynchronously, calling back OnImageLoaded. |
| extensions::ImageLoader* loader = |
| extensions::ImageLoader::Get(extension_view_host->browser_context()); |
| loader->LoadImageAsync(extension, icon_resource, |
| gfx::Size(extension_misc::EXTENSION_ICON_BITTY, |
| extension_misc::EXTENSION_ICON_BITTY), |
| base::Bind(&ExtensionInfoBarGtk::OnImageLoaded, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| // Pad the bottom of the infobar by one pixel for the border. |
| alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); |
| gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_), 0, 1, 0, 0); |
| gtk_box_pack_start(GTK_BOX(hbox()), alignment_, TRUE, TRUE, 0); |
| |
| view_ = extension_view_host->view(); |
| |
| if (gtk_widget_get_parent(view_->native_view())) { |
| gtk_widget_reparent(view_->native_view(), alignment_); |
| } else { |
| gtk_container_add(GTK_CONTAINER(alignment_), view_->native_view()); |
| } |
| |
| if (button_) { |
| signals()->Connect(button_, "button-press-event", |
| G_CALLBACK(&OnButtonPressThunk), this); |
| } |
| signals()->Connect(view_->native_view(), "expose-event", |
| G_CALLBACK(&OnExposeThunk), this); |
| signals()->Connect(view_->native_view(), "size_allocate", |
| G_CALLBACK(&OnSizeAllocateThunk), this); |
| } |
| |
| void ExtensionInfoBarGtk::PlatformSpecificHide(bool animate) { |
| DCHECK(view_); |
| DCHECK(alignment_); |
| gtk_util::RemoveAllChildren(alignment_); |
| } |
| |
| void ExtensionInfoBarGtk::GetTopColor(InfoBarDelegate::Type type, |
| double* r, double* g, double* b) { |
| // Extension infobars are always drawn with chrome-theme colors. |
| *r = *g = *b = 233.0 / 255.0; |
| } |
| |
| void ExtensionInfoBarGtk::GetBottomColor(InfoBarDelegate::Type type, |
| double* r, double* g, double* b) { |
| *r = *g = *b = 218.0 / 255.0; |
| } |
| |
| void ExtensionInfoBarGtk::StoppedShowing() { |
| if (button_) |
| gtk_chrome_button_unset_paint_state(GTK_CHROME_BUTTON(button_)); |
| } |
| |
| void ExtensionInfoBarGtk::OnImageLoaded(const gfx::Image& image) { |
| |
| DCHECK(icon_); |
| // TODO(erg): IDR_EXTENSIONS_SECTION should have an IDR_INFOBAR_EXTENSIONS |
| // icon of the correct size with real subpixel shading and such. |
| const gfx::ImageSkia* icon = NULL; |
| ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| if (image.IsEmpty()) |
| icon = rb.GetImageSkiaNamed(IDR_EXTENSIONS_SECTION); |
| else |
| icon = image.ToImageSkia(); |
| |
| SkBitmap bitmap; |
| if (button_) { |
| gfx::ImageSkia* drop_image = rb.GetImageSkiaNamed(IDR_APP_DROPARROW); |
| |
| int image_size = extension_misc::EXTENSION_ICON_BITTY; |
| // The margin between the extension icon and the drop-down arrow bitmap. |
| static const int kDropArrowLeftMargin = 3; |
| scoped_ptr<gfx::Canvas> canvas(new gfx::Canvas( |
| gfx::Size(image_size + kDropArrowLeftMargin + drop_image->width(), |
| image_size), 1.0f, false)); |
| canvas->DrawImageInt(*icon, 0, 0, icon->width(), icon->height(), 0, 0, |
| image_size, image_size, false); |
| canvas->DrawImageInt(*drop_image, image_size + kDropArrowLeftMargin, |
| image_size / 2); |
| bitmap = canvas->ExtractImageRep().sk_bitmap(); |
| } else { |
| bitmap = *icon->bitmap(); |
| } |
| |
| GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(bitmap); |
| gtk_image_set_from_pixbuf(GTK_IMAGE(icon_), pixbuf); |
| g_object_unref(pixbuf); |
| } |
| |
| ExtensionInfoBarDelegate* ExtensionInfoBarGtk::GetDelegate() { |
| return delegate()->AsExtensionInfoBarDelegate(); |
| } |
| |
| Browser* ExtensionInfoBarGtk::GetBrowser() { |
| DCHECK(icon_); |
| // Get the Browser object this infobar is attached to. |
| GtkWindow* parent = platform_util::GetTopLevel(icon_); |
| return parent ? |
| BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent)->browser() : |
| NULL; |
| } |
| |
| ExtensionContextMenuModel* ExtensionInfoBarGtk::BuildMenuModel() { |
| const extensions::Extension* extension = GetDelegate()->extension(); |
| if (!extension->ShowConfigureContextMenus()) |
| return NULL; |
| |
| Browser* browser = GetBrowser(); |
| if (!browser) |
| return NULL; |
| |
| return new ExtensionContextMenuModel(extension, browser); |
| } |
| |
| void ExtensionInfoBarGtk::OnSizeAllocate(GtkWidget* widget, |
| GtkAllocation* allocation) { |
| gfx::Size new_size(allocation->width, allocation->height); |
| |
| GetDelegate()->extension_view_host()->view()->render_view_host()->GetView()-> |
| SetSize(new_size); |
| } |
| |
| gboolean ExtensionInfoBarGtk::OnButtonPress(GtkWidget* widget, |
| GdkEventButton* event) { |
| if (event->button != 1) |
| return FALSE; |
| |
| DCHECK(button_); |
| |
| context_menu_model_ = BuildMenuModel(); |
| if (!context_menu_model_.get()) |
| return FALSE; |
| |
| gtk_chrome_button_set_paint_state(GTK_CHROME_BUTTON(widget), |
| GTK_STATE_ACTIVE); |
| ShowMenuWithModel(widget, this, context_menu_model_.get()); |
| |
| return TRUE; |
| } |
| |
| gboolean ExtensionInfoBarGtk::OnExpose(GtkWidget* sender, |
| GdkEventExpose* event) { |
| TRACE_EVENT0("ui::gtk", "ExtensionInfoBarGtk::OnExpose"); |
| |
| // We also need to draw our infobar arrows over the renderer. |
| static_cast<InfoBarContainerGtk*>(container())-> |
| PaintInfobarBitsOn(sender, event, this); |
| |
| return FALSE; |
| } |