summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-20 19:15:02 +0000
committerevan@chromium.org <evan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-20 19:15:02 +0000
commite6ba540ce15f791b531cd7ac712e7a5880edf38e (patch)
tree622999398c8e4d2e46e47cf37d726f25b3382ca2 /chrome
parent7a679c414e9812d4139d3afde9d98c4efe8f5bb8 (diff)
downloadchromium_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.cc58
-rw-r--r--chrome/browser/gtk/browser_toolbar_view_gtk.h10
-rw-r--r--chrome/browser/gtk/custom_button.cc127
-rw-r--r--chrome/browser/gtk/custom_button.h36
-rw-r--r--chrome/browser/gtk/nine_box.cc73
-rw-r--r--chrome/common/resource_bundle.h6
-rw-r--r--chrome/common/resource_bundle_linux.cc26
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;
+}