diff options
author | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-20 19:15:02 +0000 |
---|---|---|
committer | evan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-20 19:15:02 +0000 |
commit | e6ba540ce15f791b531cd7ac712e7a5880edf38e (patch) | |
tree | 622999398c8e4d2e46e47cf37d726f25b3382ca2 /chrome | |
parent | 7a679c414e9812d4139d3afde9d98c4efe8f5bb8 (diff) | |
download | chromium_src-e6ba540ce15f791b531cd7ac712e7a5880edf38e.zip chromium_src-e6ba540ce15f791b531cd7ac712e7a5880edf38e.tar.gz chromium_src-e6ba540ce15f791b531cd7ac712e7a5880edf38e.tar.bz2 |
Draw custom menu buttons properly.
This is an iterative process towards code to be proud of; the previous
code of mine was horrible, this is less bad, and my next attempt at it
will be pretty, I promise.
Review URL: http://codereview.chromium.org/28001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10111 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/browser/gtk/browser_toolbar_view_gtk.cc | 58 | ||||
-rw-r--r-- | chrome/browser/gtk/browser_toolbar_view_gtk.h | 10 | ||||
-rw-r--r-- | chrome/browser/gtk/custom_button.cc | 127 | ||||
-rw-r--r-- | chrome/browser/gtk/custom_button.h | 36 | ||||
-rw-r--r-- | chrome/browser/gtk/nine_box.cc | 73 | ||||
-rw-r--r-- | chrome/common/resource_bundle.h | 6 | ||||
-rw-r--r-- | chrome/common/resource_bundle_linux.cc | 26 |
7 files changed, 247 insertions, 89 deletions
diff --git a/chrome/browser/gtk/browser_toolbar_view_gtk.cc b/chrome/browser/gtk/browser_toolbar_view_gtk.cc index 611ec86..9ebccc4 100644 --- a/chrome/browser/gtk/browser_toolbar_view_gtk.cc +++ b/chrome/browser/gtk/browser_toolbar_view_gtk.cc @@ -68,7 +68,7 @@ void BrowserToolbarGtk::Init(Profile* profile) { gtk_box_pack_start(GTK_BOX(toolbar_), gtk_label_new(" "), FALSE, FALSE, 0); reload_.reset(BuildToolbarButton(IDR_RELOAD, IDR_RELOAD_P, IDR_RELOAD_H, 0, - l10n_util::GetString(IDS_TOOLTIP_RELOAD), false)); + l10n_util::GetString(IDS_TOOLTIP_RELOAD))); // TODO(port): we need to dynamically react to changes in show_home_button_ // and hide/show home appropriately. But we don't have a UI for it yet. @@ -78,7 +78,7 @@ void BrowserToolbarGtk::Init(Profile* profile) { gtk_box_pack_start(GTK_BOX(toolbar_), gtk_label_new(" "), FALSE, FALSE, 0); star_.reset(BuildToolbarButton(IDR_STAR, IDR_STAR_P, IDR_STAR_H, IDR_STAR_D, - l10n_util::GetString(IDS_TOOLTIP_STAR), false)); + l10n_util::GetString(IDS_TOOLTIP_STAR))); entry_ = gtk_entry_new(); gtk_widget_set_size_request(entry_, 0, 27); @@ -86,16 +86,15 @@ void BrowserToolbarGtk::Init(Profile* profile) { G_CALLBACK(OnEntryActivate), this); gtk_box_pack_start(GTK_BOX(toolbar_), entry_, TRUE, TRUE, 0); - go_.reset(BuildToolbarButton(IDR_GO, IDR_GO_P, IDR_GO_H, 0, L"", false)); + go_.reset(BuildToolbarButton(IDR_GO, IDR_GO_P, IDR_GO_H, 0, L"")); - // TODO(port): these buttons need even stranger drawing than the others. - page_menu_button_.reset(BuildToolbarButton(IDR_MENU_PAGE, 0, 0, 0, - l10n_util::GetString(IDS_PAGEMENU_TOOLTIP), true)); + gtk_box_pack_start(GTK_BOX(toolbar_), gtk_label_new(" "), FALSE, FALSE, 0); - // TODO(port): Need to get l10n_util::GetStringF working under linux to get - // the correct string here. - app_menu_button_.reset(BuildToolbarButton(IDR_MENU_CHROME, 0, 0, 0, - l10n_util::GetString(IDS_APPMENU_TOOLTIP), true)); + page_menu_button_.reset(BuildToolbarMenuButton(IDR_MENU_PAGE, + l10n_util::GetString(IDS_PAGEMENU_TOOLTIP))); + app_menu_button_.reset(BuildToolbarMenuButton(IDR_MENU_CHROME, + l10n_util::GetStringF(IDS_APPMENU_TOOLTIP, + l10n_util::GetString(IDS_PRODUCT_NAME)))); SetProfile(profile); } @@ -176,7 +175,7 @@ void BrowserToolbarGtk::UpdateTabContents(TabContents* contents, CustomDrawButton* BrowserToolbarGtk::BuildToolbarButton( int normal_id, int active_id, int highlight_id, int depressed_id, - const std::wstring& localized_tooltip, bool menu_button) { + const std::wstring& localized_tooltip) { CustomDrawButton* button = new CustomDrawButton(normal_id, active_id, highlight_id, depressed_id); @@ -184,15 +183,32 @@ CustomDrawButton* BrowserToolbarGtk::BuildToolbarButton( GTK_WIDGET(button->widget()), WideToUTF8(localized_tooltip).c_str(), WideToUTF8(localized_tooltip).c_str()); - if (menu_button) { - g_signal_connect(G_OBJECT(button->widget()), "button-press-event", - G_CALLBACK(OnMenuButtonPressEvent), this); - } else { - g_signal_connect(G_OBJECT(button->widget()), "clicked", - G_CALLBACK(OnButtonClick), this); - } + g_signal_connect(G_OBJECT(button->widget()), "clicked", + G_CALLBACK(OnButtonClick), this); + + gtk_box_pack_start(GTK_BOX(toolbar_), button->widget(), FALSE, FALSE, 0); + return button; +} + +CustomContainerButton* BrowserToolbarGtk::BuildToolbarMenuButton( + int icon_id, + const std::wstring& localized_tooltip) { + CustomContainerButton* button = new CustomContainerButton; + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + gtk_container_set_border_width(GTK_CONTAINER(button->widget()), 2); + gtk_container_add(GTK_CONTAINER(button->widget()), + gtk_image_new_from_pixbuf(rb.LoadPixbuf(icon_id))); + + gtk_tooltips_set_tip(GTK_TOOLTIPS(toolbar_tooltips_), + GTK_WIDGET(button->widget()), + WideToUTF8(localized_tooltip).c_str(), + WideToUTF8(localized_tooltip).c_str()); + g_signal_connect(G_OBJECT(button->widget()), "button-press-event", + G_CALLBACK(OnMenuButtonPressEvent), this); gtk_box_pack_start(GTK_BOX(toolbar_), button->widget(), FALSE, FALSE, 0); + return button; } @@ -233,6 +249,10 @@ void BrowserToolbarGtk::OnButtonClick(GtkWidget* button, gboolean BrowserToolbarGtk::OnMenuButtonPressEvent(GtkWidget* button, GdkEvent* event, BrowserToolbarGtk* toolbar) { + // TODO(port): this never puts the button into the "active" state, + // which means we never display the button-pressed-down graphics. I + // suspect a better way to do it is just to use a real GtkMenuShell + // with our custom drawing. if (event->type == GDK_BUTTON_PRESS) { GdkEventButton* event_button = reinterpret_cast<GdkEventButton*>(event); if (event_button->button == 1) { @@ -319,5 +339,5 @@ void BrowserToolbarGtk::RunAppMenu(GdkEvent* button_press_event) { CustomDrawButton* BrowserToolbarGtk::MakeHomeButton() { return BuildToolbarButton(IDR_HOME, IDR_HOME_P, IDR_HOME_H, 0, - l10n_util::GetString(IDS_TOOLTIP_HOME), false); + l10n_util::GetString(IDS_TOOLTIP_HOME)); } diff --git a/chrome/browser/gtk/browser_toolbar_view_gtk.h b/chrome/browser/gtk/browser_toolbar_view_gtk.h index 07c6904..08a797c 100644 --- a/chrome/browser/gtk/browser_toolbar_view_gtk.h +++ b/chrome/browser/gtk/browser_toolbar_view_gtk.h @@ -16,6 +16,7 @@ class BackForwardMenuModelGtk; class Browser; +class CustomContainerButton; class CustomDrawButton; class Profile; class TabContents; @@ -63,8 +64,11 @@ class BrowserToolbarGtk : public CommandUpdater::CommandObserver, int active_id, int highlight_id, int depressed_id, - const std::wstring& localized_tooltip, - bool menu_button); + const std::wstring& localized_tooltip); + + CustomContainerButton* BuildToolbarMenuButton( + int icon_id, + const std::wstring& localized_tooltip); // Gtk callback for the "activate" signal on the |entry_| widget. Responds to // enter. @@ -102,7 +106,7 @@ class BrowserToolbarGtk : public CommandUpdater::CommandObserver, scoped_ptr<CustomDrawButton> reload_; scoped_ptr<CustomDrawButton> home_; // May be NULL. scoped_ptr<CustomDrawButton> star_, go_; - scoped_ptr<CustomDrawButton> page_menu_button_, app_menu_button_; + scoped_ptr<CustomContainerButton> page_menu_button_, app_menu_button_; // The model that contains the security level, text, icon to display... ToolbarModel* model_; diff --git a/chrome/browser/gtk/custom_button.cc b/chrome/browser/gtk/custom_button.cc index 8020464..df57800 100644 --- a/chrome/browser/gtk/custom_button.cc +++ b/chrome/browser/gtk/custom_button.cc @@ -7,17 +7,23 @@ #include "base/basictypes.h" #include "base/logging.h" #include "chrome/common/resource_bundle.h" +#include "chrome/browser/gtk/nine_box.h" + +#include "grit/theme_resources.h" CustomDrawButton::CustomDrawButton(int normal_id, int active_id, int highlight_id, int depressed_id) { widget_ = gtk_button_new(); - // Load the button images from the theme resources .pak file. - pixbufs_[GTK_STATE_NORMAL] = LoadImage(normal_id); - pixbufs_[GTK_STATE_ACTIVE] = LoadImage(active_id); - pixbufs_[GTK_STATE_PRELIGHT] = LoadImage(highlight_id); + // Load the button images from the resource bundle. + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + pixbufs_[GTK_STATE_NORMAL] = normal_id ? rb.LoadPixbuf(normal_id) : NULL; + pixbufs_[GTK_STATE_ACTIVE] = active_id ? rb.LoadPixbuf(active_id) : NULL; + pixbufs_[GTK_STATE_PRELIGHT] = + highlight_id ? rb.LoadPixbuf(highlight_id) : NULL; pixbufs_[GTK_STATE_SELECTED] = NULL; - pixbufs_[GTK_STATE_INSENSITIVE] = LoadImage(depressed_id); + pixbufs_[GTK_STATE_INSENSITIVE] = + depressed_id ? rb.LoadPixbuf(depressed_id) : NULL; gtk_widget_set_size_request(widget_, gdk_pixbuf_get_width(pixbufs_[0]), @@ -37,38 +43,9 @@ CustomDrawButton::~CustomDrawButton() { } } -GdkPixbuf* CustomDrawButton::LoadImage(int resource_id) { - if (0 == resource_id) - return NULL; - - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - std::vector<unsigned char> data; - rb.LoadImageResourceBytes(resource_id, &data); - - GdkPixbufLoader* loader = gdk_pixbuf_loader_new(); - bool ok = gdk_pixbuf_loader_write(loader, static_cast<guint8*>(data.data()), - data.size(), NULL); - DCHECK(ok) << "failed to write " << resource_id; - // Calling gdk_pixbuf_loader_close forces the data to be parsed by the - // loader. We must do this before calling gdk_pixbuf_loader_get_pixbuf. - ok = gdk_pixbuf_loader_close(loader, NULL); - DCHECK(ok) << "close failed " << resource_id; - GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); - DCHECK(pixbuf) << "failed to load " << resource_id << " " << data.size(); - - // The pixbuf is owned by the loader, so add a ref so when we delete the - // loader, the pixbuf still exists. - g_object_ref(pixbuf); - g_object_unref(loader); - - return pixbuf; -} - // static -gboolean CustomDrawButton::OnExpose( - GtkWidget* widget, - GdkEventExpose* e, - CustomDrawButton* button) { +gboolean CustomDrawButton::OnExpose(GtkWidget* widget, GdkEventExpose* e, + CustomDrawButton* button) { GdkPixbuf* pixbuf = button->pixbufs_[GTK_WIDGET_STATE(widget)]; // Fall back to the default image if we don't have one for this state. @@ -84,5 +61,83 @@ gboolean CustomDrawButton::OnExpose( 0, 0, widget->allocation.x, widget->allocation.y, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); + return TRUE; } + +CustomContainerButton::CustomContainerButton() { + GdkPixbuf* images[9]; + int i = 0; + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_TOP_LEFT_H); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_TOP_H); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_TOP_RIGHT_H); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_LEFT_H); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_CENTER_H); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_RIGHT_H); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_BOTTOM_LEFT_H); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_BOTTOM_H); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_BOTTOM_RIGHT_H); + nine_box_prelight_.reset(new NineBox(images)); + + i = 0; + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_TOP_LEFT_P); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_TOP_P); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_TOP_RIGHT_P); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_LEFT_P); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_CENTER_P); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_RIGHT_P); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_BOTTOM_LEFT_P); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_BOTTOM_P); + images[i++] = rb.LoadPixbuf(IDR_TEXTBUTTON_BOTTOM_RIGHT_P); + nine_box_active_.reset(new NineBox(images)); + + widget_ = gtk_button_new(); + gtk_widget_set_app_paintable(widget_, TRUE); + g_signal_connect(G_OBJECT(widget_), "expose-event", + G_CALLBACK(OnExpose), this); +} + +CustomContainerButton::~CustomContainerButton() { +} + +// static +gboolean CustomContainerButton::OnExpose(GtkWidget* widget, GdkEventExpose* e, + CustomContainerButton* button) { + NineBox* nine_box = NULL; + if (GTK_WIDGET_STATE(widget) == GTK_STATE_PRELIGHT) + nine_box = button->nine_box_prelight_.get(); + else if (GTK_WIDGET_STATE(widget) == GTK_STATE_ACTIVE) + nine_box = button->nine_box_active_.get(); + + // Only draw theme graphics if we have some. + if (nine_box) { + GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, + true, // alpha + 8, // bits per channel + widget->allocation.width, + widget->allocation.height); + + nine_box->RenderToPixbuf(pixbuf); + + gdk_draw_pixbuf(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE(widget)], + pixbuf, + 0, 0, + widget->allocation.x, widget->allocation.y, -1, -1, + GDK_RGB_DITHER_NONE, 0, 0); + + gdk_pixbuf_unref(pixbuf); + } + + // If we return FALSE from the function, the button paints itself. + // If we return TRUE, no children are painted. + // So we return TRUE and send the expose along directly to the child. + gtk_container_propagate_expose(GTK_CONTAINER(widget), + gtk_bin_get_child(GTK_BIN(widget)), + e); + + return TRUE; // Prevent normal painting. +} + diff --git a/chrome/browser/gtk/custom_button.h b/chrome/browser/gtk/custom_button.h index d5f7721..8951aaf 100644 --- a/chrome/browser/gtk/custom_button.h +++ b/chrome/browser/gtk/custom_button.h @@ -9,9 +9,15 @@ #include <string> -// These classes implement custom-drawn buttons. They're used on the toolbar -// and the bookmarks bar. +#include "base/scoped_ptr.h" +class NineBox; + +// These classes implement two kinds of custom-drawn buttons. They're +// used on the toolbar and the bookmarks bar. + +// CustomDrawButton is a plain button where all its various states are drawn +// with static images. class CustomDrawButton { public: // The constructor takes 4 resource ids. If a resource doesn't exist for a @@ -26,9 +32,6 @@ class CustomDrawButton { GtkWidget* widget() const { return widget_; } private: - // Load an image given a resource id. - GdkPixbuf* LoadImage(int resource_id); - // Callback for expose, used to draw the custom graphics. static gboolean OnExpose(GtkWidget* widget, GdkEventExpose* e, CustomDrawButton* obj); @@ -41,4 +44,27 @@ class CustomDrawButton { GdkPixbuf* pixbufs_[GTK_STATE_INSENSITIVE + 1]; }; +// CustomContainerButton wraps another widget and uses a NineBox of +// images to draw a highlight around the edges when you mouse over it. +class CustomContainerButton { + public: + CustomContainerButton(); + ~CustomContainerButton(); + + GtkWidget* widget() const { return widget_; } + + private: + // Callback for expose, used to draw the custom graphics. + static gboolean OnExpose(GtkWidget* widget, GdkEventExpose* e, + CustomContainerButton* obj); + + // The button widget. + GtkWidget* widget_; + + // The theme graphics for when the mouse is over the button. + scoped_ptr<NineBox> nine_box_prelight_; + // The theme graphics for when the button is clicked. + scoped_ptr<NineBox> nine_box_active_; +}; + #endif // CHROME_BROWSER_GTK_CUSTOM_BUTTON_H_ diff --git a/chrome/browser/gtk/nine_box.cc b/chrome/browser/gtk/nine_box.cc index 98164d9..0e1305b 100644 --- a/chrome/browser/gtk/nine_box.cc +++ b/chrome/browser/gtk/nine_box.cc @@ -31,37 +31,55 @@ NineBox::~NineBox() { } void NineBox::RenderToPixbuf(GdkPixbuf* dst) { + // TODO(evanm): this is stupid; it should just be implemented with SkBitmaps + // and convert to a GdkPixbuf at the last second. + +#ifndef NDEBUG + // Start by filling with a bright color so we can see any pixels + // we're missing. + gdk_pixbuf_fill(dst, 0x00FFFFFF); +#endif + // This function paints one row at a time. // To make indexing sane, |images| points at the current row of images, // so images[0] always refers to the left-most image of the current row. GdkPixbuf** images = &images_[0]; - // x, y coordinate of bottom right corner drawing offset. - int x = gdk_pixbuf_get_width(dst) - gdk_pixbuf_get_width(images[2]); - int y = gdk_pixbuf_get_height(dst) - gdk_pixbuf_get_height(images[2]); + // The upper-left and lower-right corners of the center square in the + // rendering of the ninebox. + const int x1 = gdk_pixbuf_get_width(images[0]); + const int y1 = gdk_pixbuf_get_height(images[0]); + const int x2 = gdk_pixbuf_get_width(dst) - gdk_pixbuf_get_width(images[2]); + const int y2 = gdk_pixbuf_get_height(dst) - gdk_pixbuf_get_height(images[2]); DrawPixbuf(images[0], dst, 0, 0); - RenderTopCenterStrip(dst, gdk_pixbuf_get_width(images[0]), x); - DrawPixbuf(images[2], dst, x, 0); + RenderTopCenterStrip(dst, x1, x2); + DrawPixbuf(images[2], dst, x2, 0); // Center row. Needs vertical tiling. images = &images_[1 * 3]; TileImage(images[0], dst, - 0, gdk_pixbuf_get_height(images[0]), - 0, y); - // TODO(port): tile center image if it exists. - DCHECK(images[1] == NULL); + 0, y1, + 0, y2); + if (images[1]) { + const int delta_y = gdk_pixbuf_get_height(images[1]); + for (int y = y1; y < y2; y += delta_y) { + TileImage(images[1], dst, + x1, y, + x2, y); + } + } TileImage(images[2], dst, - x, gdk_pixbuf_get_height(images[0]), - x, y); + x2, y1, + x2, y2); // Bottom row. images = &images_[2 * 3]; - DrawPixbuf(images[0], dst, 0, y); + DrawPixbuf(images[0], dst, 0, y2); TileImage(images[1], dst, - gdk_pixbuf_get_width(images[0]), y, - x, y); - DrawPixbuf(images[2], dst, x, y); + x1, y2, + x2, y2); + DrawPixbuf(images[2], dst, x2, y2); } void NineBox::RenderTopCenterStrip(GdkPixbuf* dst, int x1, int x2) { @@ -72,19 +90,24 @@ void NineBox::RenderTopCenterStrip(GdkPixbuf* dst, int x1, int x2) { void NineBox::TileImage(GdkPixbuf* src, GdkPixbuf* dst, int x1, int y1, int x2, int y2) { - const int width = gdk_pixbuf_get_width(src); - const int height = gdk_pixbuf_get_height(src); + const int src_width = gdk_pixbuf_get_width(src); + const int src_height = gdk_pixbuf_get_height(src); + const int dst_width = gdk_pixbuf_get_width(dst); + const int dst_height = gdk_pixbuf_get_width(dst); - // Compute delta x or y. + // We only tile along one axis (see above TODO about nuking all this code), + // dx or dy will be nonzero along that axis. int dx = 0, dy = 0; if (x2 > x1) - dx = width; + dx = src_width; if (y2 > y1) - dy = height; - - for (int x = x1, y = y1; - x + width <= x2 || y + height <= y2; - x += dx, y += dy) { - DrawPixbuf(src, dst, x, y); + dy = src_height; + DCHECK(dx == 0 || dy == 0); + + for (int x = x1, y = y1; x < x2 || y < y2; x += dx, y += dy) { + gdk_pixbuf_copy_area(src, 0, 0, + dx ? std::min(src_width, dst_width - x) : src_width, + dy ? std::min(src_height, dst_height - y) : src_height, + dst, x, y); } } diff --git a/chrome/common/resource_bundle.h b/chrome/common/resource_bundle.h index 4b6e0fc..983f34b 100644 --- a/chrome/common/resource_bundle.h +++ b/chrome/common/resource_bundle.h @@ -23,6 +23,7 @@ namespace base { class DataPack; }; +typedef struct _GdkPixbuf GdkPixbuf; #endif class ChromeFont; class SkBitmap; @@ -108,7 +109,10 @@ class ResourceBundle { // Loads and returns a cursor from the app module. HCURSOR LoadCursor(int cursor_id); -#endif // OS_WIN +#elif defined(OS_LINUX) + // Load a theme image as a GdkPixbuf. + GdkPixbuf* LoadPixbuf(int resource_id); +#endif private: // We define a DataHandle typedef to abstract across how data is stored diff --git a/chrome/common/resource_bundle_linux.cc b/chrome/common/resource_bundle_linux.cc index 12b0858..08bbeee 100644 --- a/chrome/common/resource_bundle_linux.cc +++ b/chrome/common/resource_bundle_linux.cc @@ -4,6 +4,8 @@ #include "chrome/common/resource_bundle.h" +#include <gtk/gtk.h> + #include "base/base_paths.h" #include "base/data_pack.h" #include "base/file_path.h" @@ -114,3 +116,27 @@ std::wstring ResourceBundle::GetLocalizedString(int message_id) { data.length() / 2); return UTF16ToWide(msg); } + +GdkPixbuf* ResourceBundle::LoadPixbuf(int resource_id) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + std::vector<unsigned char> data; + rb.LoadImageResourceBytes(resource_id, &data); + + GdkPixbufLoader* loader = gdk_pixbuf_loader_new(); + bool ok = gdk_pixbuf_loader_write(loader, static_cast<guint8*>(data.data()), + data.size(), NULL); + DCHECK(ok) << "failed to write " << resource_id; + // Calling gdk_pixbuf_loader_close forces the data to be parsed by the + // loader. We must do this before calling gdk_pixbuf_loader_get_pixbuf. + ok = gdk_pixbuf_loader_close(loader, NULL); + DCHECK(ok) << "close failed " << resource_id; + GdkPixbuf* pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); + DCHECK(pixbuf) << "failed to load " << resource_id << " " << data.size(); + + // The pixbuf is owned by the loader, so add a ref so when we delete the + // loader, the pixbuf still exists. + g_object_ref(pixbuf); + g_object_unref(loader); + + return pixbuf; +} |