blob: 8cc43663791ab80a5820b8d98ca0d1abed5f69f3 [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/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;
}