Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / libgtk2ui / gtk2_ui.cc
blobb64b3deef53e636629d6e7b550efcec239540a01
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.
5 #include "chrome/browser/ui/libgtk2ui/gtk2_ui.h"
7 #include <math.h>
8 #include <set>
10 #include <pango/pango.h>
12 #include "base/command_line.h"
13 #include "base/debug/leak_annotations.h"
14 #include "base/environment.h"
15 #include "base/i18n/rtl.h"
16 #include "base/logging.h"
17 #include "base/nix/mime_util_xdg.h"
18 #include "base/nix/xdg_util.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/stringprintf.h"
22 #include "chrome/browser/themes/theme_properties.h"
23 #include "chrome/browser/ui/libgtk2ui/app_indicator_icon.h"
24 #include "chrome/browser/ui/libgtk2ui/gtk2_border.h"
25 #include "chrome/browser/ui/libgtk2ui/gtk2_event_loop.h"
26 #include "chrome/browser/ui/libgtk2ui/gtk2_key_bindings_handler.h"
27 #include "chrome/browser/ui/libgtk2ui/gtk2_signal_registrar.h"
28 #include "chrome/browser/ui/libgtk2ui/gtk2_status_icon.h"
29 #include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
30 #include "chrome/browser/ui/libgtk2ui/native_theme_gtk2.h"
31 #include "chrome/browser/ui/libgtk2ui/print_dialog_gtk2.h"
32 #include "chrome/browser/ui/libgtk2ui/printing_gtk2_util.h"
33 #include "chrome/browser/ui/libgtk2ui/select_file_dialog_impl.h"
34 #include "chrome/browser/ui/libgtk2ui/skia_utils_gtk2.h"
35 #include "chrome/browser/ui/libgtk2ui/unity_service.h"
36 #include "chrome/browser/ui/libgtk2ui/x11_input_method_context_impl_gtk2.h"
37 #include "grit/components_scaled_resources.h"
38 #include "grit/theme_resources.h"
39 #include "printing/printing_context_linux.h"
40 #include "third_party/skia/include/core/SkBitmap.h"
41 #include "third_party/skia/include/core/SkCanvas.h"
42 #include "third_party/skia/include/core/SkColor.h"
43 #include "third_party/skia/include/core/SkShader.h"
44 #include "ui/base/resource/resource_bundle.h"
45 #include "ui/gfx/canvas.h"
46 #include "ui/gfx/display.h"
47 #include "ui/gfx/geometry/rect.h"
48 #include "ui/gfx/geometry/size.h"
49 #include "ui/gfx/image/image.h"
50 #include "ui/gfx/image/image_skia_source.h"
51 #include "ui/gfx/skbitmap_operations.h"
52 #include "ui/gfx/skia_util.h"
53 #include "ui/native_theme/native_theme.h"
54 #include "ui/resources/grit/ui_resources.h"
55 #include "ui/views/controls/button/blue_button.h"
56 #include "ui/views/controls/button/label_button.h"
57 #include "ui/views/controls/button/label_button_border.h"
58 #include "ui/views/linux_ui/window_button_order_observer.h"
59 #include "ui/views/resources/grit/views_resources.h"
61 #if defined(USE_GCONF)
62 #include "chrome/browser/ui/libgtk2ui/gconf_listener.h"
63 #endif
65 // A minimized port of GtkThemeService into something that can provide colors
66 // and images for aura.
68 // TODO(erg): There's still a lot that needs ported or done for the first time:
70 // - Render and inject the omnibox background.
71 // - Make sure to test with a light on dark theme, too.
73 // Work around a header bug:
74 // linux/debian_wheezy_i386-sysroot/usr/include/linux/stddef.h redefines NULL
75 // to 0, which breaks -Wsentinel. Get back the normal definition of NULL.
76 // TODO(thakis): Remove this once we update sysroots.
77 #define __need_NULL
78 #include <stddef.h>
80 namespace libgtk2ui {
82 namespace {
84 class GtkThemeIconSource : public gfx::ImageSkiaSource {
85 public:
86 GtkThemeIconSource(int id, const char* icon, bool enabled)
87 : id_(id),
88 icon_(icon),
89 enabled_(enabled) {
92 ~GtkThemeIconSource() override {}
94 gfx::ImageSkiaRep GetImageForScale(float scale) override {
95 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
96 SkBitmap default_icon = rb.GetImageNamed(id_).AsBitmap();
98 int scalew = default_icon.width() * scale;
99 int scaleh = default_icon.height() * scale;
101 // Ask GTK to render the icon to a buffer, which we will steal from.
102 GtkIconTheme* icon_theme = gtk_icon_theme_get_default();
103 GdkPixbuf* gdk_icon = gtk_icon_theme_load_icon(
104 icon_theme,
105 icon_,
106 20 * scale,
107 (GtkIconLookupFlags)0,
108 NULL);
110 // This can theoretically happen if an icon theme doesn't provide a
111 // specific image. This should realistically never happen, but I bet there
112 // are some theme authors who don't reliably provide all icons.
113 if (!gdk_icon)
114 return gfx::ImageSkiaRep();
116 #if GTK_MAJOR_VERSION == 2
117 GtkIconSource* icon_source = gtk_icon_source_new();
118 gtk_icon_source_set_pixbuf(icon_source, gdk_icon);
120 GdkPixbuf* temp = gtk_style_render_icon(
121 gtk_rc_get_style(NativeThemeGtk2::instance()->GetButton()),
122 icon_source,
123 GTK_TEXT_DIR_NONE,
124 enabled_ ? GTK_STATE_NORMAL : GTK_STATE_INSENSITIVE,
125 (GtkIconSize)-1,
126 NativeThemeGtk2::instance()->GetButton(),
127 NULL);
129 gtk_icon_source_free(icon_source);
130 g_object_unref(gdk_icon);
132 gdk_icon = temp;
133 #endif
135 SkBitmap retval;
136 retval.allocN32Pixels(scalew, scaleh);
137 retval.eraseColor(0);
139 const SkBitmap icon = GdkPixbufToImageSkia(gdk_icon);
140 g_object_unref(gdk_icon);
142 SkCanvas canvas(retval);
143 SkPaint paint;
145 #if GTK_MAJOR_VERSION > 2
146 if (!enabled_)
147 paint.setAlpha(128);
148 #endif
150 canvas.drawBitmap(icon,
151 (scalew / 2) - (icon.width() / 2),
152 (scaleh / 2) - (icon.height() / 2),
153 &paint);
155 return gfx::ImageSkiaRep(retval, scale);
158 private:
159 int id_;
160 const char* icon_;
161 bool enabled_;
163 DISALLOW_COPY_AND_ASSIGN(GtkThemeIconSource);
167 struct GObjectDeleter {
168 void operator()(void* ptr) {
169 g_object_unref(ptr);
172 struct GtkIconInfoDeleter {
173 void operator()(GtkIconInfo* ptr) {
174 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
175 gtk_icon_info_free(ptr);
176 G_GNUC_END_IGNORE_DEPRECATIONS
179 typedef scoped_ptr<GIcon, GObjectDeleter> ScopedGIcon;
180 typedef scoped_ptr<GtkIconInfo, GtkIconInfoDeleter> ScopedGtkIconInfo;
181 typedef scoped_ptr<GdkPixbuf, GObjectDeleter> ScopedGdkPixbuf;
183 // Prefix for app indicator ids
184 const char kAppIndicatorIdPrefix[] = "chrome_app_indicator_";
186 // Number of app indicators used (used as part of app-indicator id).
187 int indicators_count;
189 // The unknown content type.
190 const char* kUnknownContentType = "application/octet-stream";
192 // The size of the rendered toolbar image.
193 const int kToolbarImageWidth = 64;
194 const int kToolbarImageHeight = 128;
196 // Values used as the new luminance and saturation values in the inactive tab
197 // text color.
198 const double kDarkInactiveLuminance = 0.85;
199 const double kLightInactiveLuminance = 0.15;
200 const double kHeavyInactiveSaturation = 0.7;
201 const double kLightInactiveSaturation = 0.3;
203 // TODO(erg): ThemeService has a whole interface just for reading default
204 // constants. Figure out what to do with that more long term; for now, just
205 // copy the constants themselves here.
207 // Default tints.
208 const color_utils::HSL kDefaultTintButtons = { -1, -1, -1 };
209 const color_utils::HSL kDefaultTintFrame = { -1, -1, -1 };
210 const color_utils::HSL kDefaultTintFrameInactive = { -1, -1, 0.75f };
211 const color_utils::HSL kDefaultTintFrameIncognito = { -1, 0.2f, 0.35f };
212 const color_utils::HSL kDefaultTintFrameIncognitoInactive = { -1, 0.3f, 0.6f };
213 const color_utils::HSL kDefaultTintBackgroundTab = { -1, 0.5, 0.75 };
217 // Get ChromeGtkFrame theme colors. No-op in GTK3.
218 void GetChromeStyleColor(const char* style_property, SkColor* ret_color) {
219 #if GTK_MAJOR_VERSION == 2
220 GdkColor* style_color = NULL;
222 gtk_widget_style_get(NativeThemeGtk2::instance()->GetWindow(),
223 style_property, &style_color,
224 NULL);
226 if (style_color) {
227 *ret_color = GdkColorToSkColor(*style_color);
228 gdk_color_free(style_color);
230 #endif
233 // Picks a button tint from a set of background colors. While
234 // |accent_color| will usually be the same color through a theme, this
235 // function will get called with the normal GtkLabel |text_color|/GtkWindow
236 // |background_color| pair and the GtkEntry |text_color|/|background_color|
237 // pair. While 3/4 of the time the resulting tint will be the same, themes that
238 // have a dark window background (with light text) and a light text entry (with
239 // dark text) will get better icons with this separated out.
240 void PickButtonTintFromColors(SkColor accent_color,
241 SkColor text_color,
242 SkColor background_color,
243 color_utils::HSL* tint) {
244 color_utils::HSL accent_tint, text_tint, background_tint;
245 color_utils::SkColorToHSL(accent_color, &accent_tint);
246 color_utils::SkColorToHSL(text_color, &text_tint);
247 color_utils::SkColorToHSL(background_color, &background_tint);
249 // If the accent color is gray, then our normal HSL tomfoolery will bring out
250 // whatever color is oddly dominant (for example, in rgb space [125, 128,
251 // 125] will tint green instead of gray). Slight differences (+/-10 (4%) to
252 // all color components) should be interpreted as this color being gray and
253 // we should switch into a special grayscale mode.
254 int rb_diff = abs(static_cast<int>(SkColorGetR(accent_color)) -
255 static_cast<int>(SkColorGetB(accent_color)));
256 int rg_diff = abs(static_cast<int>(SkColorGetR(accent_color)) -
257 static_cast<int>(SkColorGetG(accent_color)));
258 int bg_diff = abs(static_cast<int>(SkColorGetB(accent_color)) -
259 static_cast<int>(SkColorGetG(accent_color)));
260 if (rb_diff < 10 && rg_diff < 10 && bg_diff < 10) {
261 // Our accent is white/gray/black. Only the luminance of the accent color
262 // matters.
263 tint->h = -1;
265 // Use the saturation of the text.
266 tint->s = text_tint.s;
268 // Use the luminance of the accent color UNLESS there isn't enough
269 // luminance contrast between the accent color and the base color.
270 if (fabs(accent_tint.l - background_tint.l) > 0.3)
271 tint->l = accent_tint.l;
272 else
273 tint->l = text_tint.l;
274 } else {
275 // Our accent is a color.
276 tint->h = accent_tint.h;
278 // Don't modify the saturation; the amount of color doesn't matter.
279 tint->s = -1;
281 // If the text wants us to darken the icon, don't change the luminance (the
282 // icons are already dark enough). Otherwise, lighten the icon by no more
283 // than 0.9 since we don't want a pure-white icon even if the text is pure
284 // white.
285 if (text_tint.l < 0.5)
286 tint->l = -1;
287 else if (text_tint.l <= 0.9)
288 tint->l = text_tint.l;
289 else
290 tint->l = 0.9;
294 // Copied Default blah sections from ThemeService.
295 color_utils::HSL GetDefaultTint(int id) {
296 switch (id) {
297 case ThemeProperties::TINT_FRAME:
298 return kDefaultTintFrame;
299 case ThemeProperties::TINT_FRAME_INACTIVE:
300 return kDefaultTintFrameInactive;
301 case ThemeProperties::TINT_FRAME_INCOGNITO:
302 return kDefaultTintFrameIncognito;
303 case ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE:
304 return kDefaultTintFrameIncognitoInactive;
305 case ThemeProperties::TINT_BUTTONS:
306 return kDefaultTintButtons;
307 case ThemeProperties::TINT_BACKGROUND_TAB:
308 return kDefaultTintBackgroundTab;
309 default:
310 color_utils::HSL result = {-1, -1, -1};
311 return result;
315 // Returns a gfx::FontRenderParams corresponding to GTK's configuration.
316 gfx::FontRenderParams GetGtkFontRenderParams() {
317 GtkSettings* gtk_settings = gtk_settings_get_default();
318 CHECK(gtk_settings);
319 gint antialias = 0;
320 gint hinting = 0;
321 gchar* hint_style = NULL;
322 gchar* rgba = NULL;
323 g_object_get(gtk_settings,
324 "gtk-xft-antialias", &antialias,
325 "gtk-xft-hinting", &hinting,
326 "gtk-xft-hintstyle", &hint_style,
327 "gtk-xft-rgba", &rgba,
328 NULL);
330 gfx::FontRenderParams params;
331 params.antialiasing = antialias != 0;
333 if (hinting == 0 || !hint_style || strcmp(hint_style, "hintnone") == 0) {
334 params.hinting = gfx::FontRenderParams::HINTING_NONE;
335 } else if (strcmp(hint_style, "hintslight") == 0) {
336 params.hinting = gfx::FontRenderParams::HINTING_SLIGHT;
337 } else if (strcmp(hint_style, "hintmedium") == 0) {
338 params.hinting = gfx::FontRenderParams::HINTING_MEDIUM;
339 } else if (strcmp(hint_style, "hintfull") == 0) {
340 params.hinting = gfx::FontRenderParams::HINTING_FULL;
341 } else {
342 LOG(WARNING) << "Unexpected gtk-xft-hintstyle \"" << hint_style << "\"";
343 params.hinting = gfx::FontRenderParams::HINTING_NONE;
346 if (!rgba || strcmp(rgba, "none") == 0) {
347 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE;
348 } else if (strcmp(rgba, "rgb") == 0) {
349 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_RGB;
350 } else if (strcmp(rgba, "bgr") == 0) {
351 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_BGR;
352 } else if (strcmp(rgba, "vrgb") == 0) {
353 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_VRGB;
354 } else if (strcmp(rgba, "vbgr") == 0) {
355 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_VBGR;
356 } else {
357 LOG(WARNING) << "Unexpected gtk-xft-rgba \"" << rgba << "\"";
358 params.subpixel_rendering = gfx::FontRenderParams::SUBPIXEL_RENDERING_NONE;
361 g_free(hint_style);
362 g_free(rgba);
364 return params;
367 double GetDPI() {
368 GtkSettings* gtk_settings = gtk_settings_get_default();
369 CHECK(gtk_settings);
370 gint gtk_dpi = -1;
371 g_object_get(gtk_settings, "gtk-xft-dpi", &gtk_dpi, NULL);
373 // GTK multiplies the DPI by 1024 before storing it.
374 return (gtk_dpi > 0) ? gtk_dpi / 1024.0 : 96.0;
377 // Queries GTK for its font DPI setting and returns the number of pixels in a
378 // point.
379 double GetPixelsInPoint(float device_scale_factor) {
380 double dpi = GetDPI();
382 // Take device_scale_factor into account — if Chrome already scales the
383 // entire UI up by 2x, we should not also scale up.
384 dpi /= device_scale_factor;
386 // There are 72 points in an inch.
387 return dpi / 72.0;
390 views::LinuxUI::NonClientMiddleClickAction GetDefaultMiddleClickAction() {
391 scoped_ptr<base::Environment> env(base::Environment::Create());
392 switch (base::nix::GetDesktopEnvironment(env.get())) {
393 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
394 // Starting with KDE 4.4, windows' titlebars can be dragged with the
395 // middle mouse button to create tab groups. We don't support that in
396 // Chrome, but at least avoid lowering windows in response to middle
397 // clicks to avoid surprising users who expect the KDE behavior.
398 return views::LinuxUI::MIDDLE_CLICK_ACTION_NONE;
399 default:
400 return views::LinuxUI::MIDDLE_CLICK_ACTION_LOWER;
404 } // namespace
406 Gtk2UI::Gtk2UI()
407 : default_font_size_pixels_(0),
408 default_font_style_(gfx::Font::NORMAL),
409 middle_click_action_(GetDefaultMiddleClickAction()),
410 device_scale_factor_(1.0) {
411 GtkInitFromCommandLine(*base::CommandLine::ForCurrentProcess());
416 void OnThemeChanged(GObject* obj, GParamSpec* param, Gtk2UI* gtkui) {
417 gtkui->ResetStyle();
420 void Gtk2UI::Initialize() {
421 GtkSettings* settings = gtk_settings_get_default();
422 g_signal_connect_after(settings,
423 "notify::gtk-theme-name",
424 G_CALLBACK(OnThemeChanged),
425 this);
426 g_signal_connect_after(settings,
427 "notify::gtk-icon-theme-name",
428 G_CALLBACK(OnThemeChanged),
429 this);
432 LoadGtkValues();
434 printing::PrintingContextLinux::SetCreatePrintDialogFunction(
435 &PrintDialogGtk2::CreatePrintDialog);
436 printing::PrintingContextLinux::SetPdfPaperSizeFunction(
437 &GetPdfPaperSizeDeviceUnitsGtk);
439 #if defined(USE_GCONF)
440 // We must build this after GTK gets initialized.
441 gconf_listener_.reset(new GConfListener(this));
442 #endif // defined(USE_GCONF)
444 indicators_count = 0;
446 // Instantiate the singleton instance of Gtk2EventLoop.
447 Gtk2EventLoop::GetInstance();
450 Gtk2UI::~Gtk2UI() {
451 ClearAllThemeData();
454 gfx::Image Gtk2UI::GetThemeImageNamed(int id) const {
455 // Try to get our cached version:
456 ImageCache::const_iterator it = gtk_images_.find(id);
457 if (it != gtk_images_.end())
458 return it->second;
460 gfx::Image image = GenerateGtkThemeImage(id);
462 if (image.IsEmpty()) {
463 SkBitmap bitmap = GenerateGtkThemeBitmap(id);
464 if (!bitmap.empty())
465 image = gfx::Image::CreateFrom1xBitmap(bitmap);
468 gtk_images_[id] = image;
469 return image;
472 bool Gtk2UI::GetColor(int id, SkColor* color) const {
473 ColorMap::const_iterator it = colors_.find(id);
474 if (it != colors_.end()) {
475 *color = it->second;
476 return true;
479 return false;
482 bool Gtk2UI::HasCustomImage(int id) const {
483 return !GetThemeImageNamed(id).IsEmpty();
486 SkColor Gtk2UI::GetFocusRingColor() const {
487 return focus_ring_color_;
490 SkColor Gtk2UI::GetThumbActiveColor() const {
491 return thumb_active_color_;
494 SkColor Gtk2UI::GetThumbInactiveColor() const {
495 return thumb_inactive_color_;
498 SkColor Gtk2UI::GetTrackColor() const {
499 return track_color_;
502 SkColor Gtk2UI::GetActiveSelectionBgColor() const {
503 return active_selection_bg_color_;
506 SkColor Gtk2UI::GetActiveSelectionFgColor() const {
507 return active_selection_fg_color_;
510 SkColor Gtk2UI::GetInactiveSelectionBgColor() const {
511 return inactive_selection_bg_color_;
514 SkColor Gtk2UI::GetInactiveSelectionFgColor() const {
515 return inactive_selection_fg_color_;
518 double Gtk2UI::GetCursorBlinkInterval() const {
519 // From http://library.gnome.org/devel/gtk/unstable/GtkSettings.html, this is
520 // the default value for gtk-cursor-blink-time.
521 static const gint kGtkDefaultCursorBlinkTime = 1200;
523 // Dividing GTK's cursor blink cycle time (in milliseconds) by this value
524 // yields an appropriate value for
525 // content::RendererPreferences::caret_blink_interval. This matches the
526 // logic in the WebKit GTK port.
527 static const double kGtkCursorBlinkCycleFactor = 2000.0;
529 gint cursor_blink_time = kGtkDefaultCursorBlinkTime;
530 gboolean cursor_blink = TRUE;
531 g_object_get(gtk_settings_get_default(),
532 "gtk-cursor-blink-time", &cursor_blink_time,
533 "gtk-cursor-blink", &cursor_blink,
534 NULL);
535 return cursor_blink ? (cursor_blink_time / kGtkCursorBlinkCycleFactor) : 0.0;
538 ui::NativeTheme* Gtk2UI::GetNativeTheme(aura::Window* window) const {
539 ui::NativeTheme* native_theme_override = NULL;
540 if (!native_theme_overrider_.is_null())
541 native_theme_override = native_theme_overrider_.Run(window);
543 if (native_theme_override)
544 return native_theme_override;
546 return NativeThemeGtk2::instance();
549 void Gtk2UI::SetNativeThemeOverride(const NativeThemeGetter& callback) {
550 native_theme_overrider_ = callback;
553 bool Gtk2UI::GetDefaultUsesSystemTheme() const {
554 scoped_ptr<base::Environment> env(base::Environment::Create());
556 switch (base::nix::GetDesktopEnvironment(env.get())) {
557 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
558 case base::nix::DESKTOP_ENVIRONMENT_UNITY:
559 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
560 return true;
561 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
562 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
563 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
564 return false;
566 // Unless GetDesktopEnvironment() badly misbehaves, this should never happen.
567 NOTREACHED();
568 return false;
571 void Gtk2UI::SetDownloadCount(int count) const {
572 if (unity::IsRunning())
573 unity::SetDownloadCount(count);
576 void Gtk2UI::SetProgressFraction(float percentage) const {
577 if (unity::IsRunning())
578 unity::SetProgressFraction(percentage);
581 bool Gtk2UI::IsStatusIconSupported() const {
582 return true;
585 scoped_ptr<views::StatusIconLinux> Gtk2UI::CreateLinuxStatusIcon(
586 const gfx::ImageSkia& image,
587 const base::string16& tool_tip) const {
588 if (AppIndicatorIcon::CouldOpen()) {
589 ++indicators_count;
590 return scoped_ptr<views::StatusIconLinux>(new AppIndicatorIcon(
591 base::StringPrintf("%s%d", kAppIndicatorIdPrefix, indicators_count),
592 image,
593 tool_tip));
594 } else {
595 return scoped_ptr<views::StatusIconLinux>(new Gtk2StatusIcon(
596 image, tool_tip));
600 gfx::Image Gtk2UI::GetIconForContentType(
601 const std::string& content_type,
602 int size) const {
603 // This call doesn't take a reference.
604 GtkIconTheme* theme = gtk_icon_theme_get_default();
606 std::string content_types[] = {
607 content_type, kUnknownContentType
610 for (size_t i = 0; i < arraysize(content_types); ++i) {
611 ScopedGIcon icon(g_content_type_get_icon(content_types[i].c_str()));
612 ScopedGtkIconInfo icon_info(
613 gtk_icon_theme_lookup_by_gicon(
614 theme, icon.get(), size,
615 static_cast<GtkIconLookupFlags>(GTK_ICON_LOOKUP_FORCE_SIZE)));
616 if (!icon_info)
617 continue;
618 ScopedGdkPixbuf pixbuf(gtk_icon_info_load_icon(icon_info.get(), NULL));
619 if (!pixbuf)
620 continue;
622 SkBitmap bitmap = GdkPixbufToImageSkia(pixbuf.get());
623 DCHECK_EQ(size, bitmap.width());
624 DCHECK_EQ(size, bitmap.height());
625 gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
626 image_skia.MakeThreadSafe();
627 return gfx::Image(image_skia);
629 return gfx::Image();
632 scoped_ptr<views::Border> Gtk2UI::CreateNativeBorder(
633 views::LabelButton* owning_button,
634 scoped_ptr<views::LabelButtonBorder> border) {
635 if (owning_button->GetNativeTheme() != NativeThemeGtk2::instance())
636 return border.Pass();
638 return make_scoped_ptr(new Gtk2Border(this, owning_button, border.Pass()));
641 void Gtk2UI::AddWindowButtonOrderObserver(
642 views::WindowButtonOrderObserver* observer) {
643 if (!leading_buttons_.empty() || !trailing_buttons_.empty()) {
644 observer->OnWindowButtonOrderingChange(leading_buttons_,
645 trailing_buttons_);
648 observer_list_.AddObserver(observer);
651 void Gtk2UI::RemoveWindowButtonOrderObserver(
652 views::WindowButtonOrderObserver* observer) {
653 observer_list_.RemoveObserver(observer);
656 void Gtk2UI::SetWindowButtonOrdering(
657 const std::vector<views::FrameButton>& leading_buttons,
658 const std::vector<views::FrameButton>& trailing_buttons) {
659 leading_buttons_ = leading_buttons;
660 trailing_buttons_ = trailing_buttons;
662 FOR_EACH_OBSERVER(views::WindowButtonOrderObserver, observer_list_,
663 OnWindowButtonOrderingChange(leading_buttons_,
664 trailing_buttons_));
667 void Gtk2UI::SetNonClientMiddleClickAction(NonClientMiddleClickAction action) {
668 middle_click_action_ = action;
671 scoped_ptr<ui::LinuxInputMethodContext> Gtk2UI::CreateInputMethodContext(
672 ui::LinuxInputMethodContextDelegate* delegate,
673 bool is_simple) const {
674 return scoped_ptr<ui::LinuxInputMethodContext>(
675 new X11InputMethodContextImplGtk2(delegate, is_simple));
678 gfx::FontRenderParams Gtk2UI::GetDefaultFontRenderParams() const {
679 static gfx::FontRenderParams params = GetGtkFontRenderParams();
680 return params;
683 void Gtk2UI::GetDefaultFontDescription(
684 std::string* family_out,
685 int* size_pixels_out,
686 int* style_out,
687 gfx::FontRenderParams* params_out) const {
688 *family_out = default_font_family_;
689 *size_pixels_out = default_font_size_pixels_;
690 *style_out = default_font_style_;
691 *params_out = default_font_render_params_;
694 ui::SelectFileDialog* Gtk2UI::CreateSelectFileDialog(
695 ui::SelectFileDialog::Listener* listener,
696 ui::SelectFilePolicy* policy) const {
697 return SelectFileDialogImpl::Create(listener, policy);
700 bool Gtk2UI::UnityIsRunning() {
701 return unity::IsRunning();
704 views::LinuxUI::NonClientMiddleClickAction
705 Gtk2UI::GetNonClientMiddleClickAction() {
706 return middle_click_action_;
709 void Gtk2UI::NotifyWindowManagerStartupComplete() {
710 // TODO(port) Implement this using _NET_STARTUP_INFO_BEGIN/_NET_STARTUP_INFO
711 // from http://standards.freedesktop.org/startup-notification-spec/ instead.
712 gdk_notify_startup_complete();
715 bool Gtk2UI::MatchEvent(const ui::Event& event,
716 std::vector<ui::TextEditCommandAuraLinux>* commands) {
717 // Ensure that we have a keyboard handler.
718 if (!key_bindings_handler_)
719 key_bindings_handler_.reset(new Gtk2KeyBindingsHandler);
721 return key_bindings_handler_->MatchEvent(event, commands);
724 void Gtk2UI::SetScrollbarColors() {
725 thumb_active_color_ = SkColorSetRGB(244, 244, 244);
726 thumb_inactive_color_ = SkColorSetRGB(234, 234, 234);
727 track_color_ = SkColorSetRGB(211, 211, 211);
729 GetChromeStyleColor("scrollbar-slider-prelight-color", &thumb_active_color_);
730 GetChromeStyleColor("scrollbar-slider-normal-color", &thumb_inactive_color_);
731 GetChromeStyleColor("scrollbar-trough-color", &track_color_);
734 void Gtk2UI::LoadGtkValues() {
735 // TODO(erg): GtkThemeService had a comment here about having to muck with
736 // the raw Prefs object to remove prefs::kCurrentThemeImages or else we'd
737 // regress startup time. Figure out how to do that when we can't access the
738 // prefs system from here.
740 NativeThemeGtk2* theme = NativeThemeGtk2::instance();
742 SkColor toolbar_color =
743 theme->GetSystemColor(ui::NativeTheme::kColorId_LabelBackgroundColor);
744 SkColor button_color =
745 theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonHighlightColor);
746 SkColor label_color =
747 theme->GetSystemColor(ui::NativeTheme::kColorId_LabelEnabledColor);
749 colors_[ThemeProperties::COLOR_CONTROL_BACKGROUND] = toolbar_color;
750 colors_[ThemeProperties::COLOR_TOOLBAR] = toolbar_color;
752 SetThemeTint(ThemeProperties::TINT_BUTTONS, button_color);
754 colors_[ThemeProperties::COLOR_TAB_TEXT] = label_color;
755 colors_[ThemeProperties::COLOR_BOOKMARK_TEXT] = label_color;
756 colors_[ThemeProperties::COLOR_STATUS_BAR_TEXT] = label_color;
758 UpdateDefaultFont();
760 // Build the various icon tints.
761 GetNormalButtonTintHSL(&button_tint_);
762 GetNormalEntryForegroundHSL(&entry_tint_);
763 GetSelectedEntryForegroundHSL(&selected_entry_tint_);
764 SkColor frame_color = BuildFrameColors();
766 // The inactive frame color never occurs naturally in the theme, as it is a
767 // tinted version of |frame_color|. We generate another color based on the
768 // background tab color, with the lightness and saturation moved in the
769 // opposite direction. (We don't touch the hue, since there should be subtle
770 // hints of the color in the text.)
771 color_utils::HSL inactive_tab_text_hsl =
772 tints_[ThemeProperties::TINT_BACKGROUND_TAB];
773 if (inactive_tab_text_hsl.l < 0.5)
774 inactive_tab_text_hsl.l = kDarkInactiveLuminance;
775 else
776 inactive_tab_text_hsl.l = kLightInactiveLuminance;
778 if (inactive_tab_text_hsl.s < 0.5)
779 inactive_tab_text_hsl.s = kHeavyInactiveSaturation;
780 else
781 inactive_tab_text_hsl.s = kLightInactiveSaturation;
783 colors_[ThemeProperties::COLOR_BACKGROUND_TAB_TEXT] =
784 color_utils::HSLToSkColor(inactive_tab_text_hsl, 255);
786 // We pick the text and background colors for the NTP out of the colors for a
787 // GtkEntry. We do this because GtkEntries background color is never the same
788 // as |toolbar_color|, is usually a white, and when it isn't a white,
789 // provides sufficient contrast to |toolbar_color|. Try this out with
790 // Darklooks, HighContrastInverse or ThinIce.
792 SkColor ntp_background =
793 theme->GetSystemColor(
794 ui::NativeTheme::kColorId_TextfieldDefaultBackground);
795 SkColor ntp_foreground =
796 theme->GetSystemColor(
797 ui::NativeTheme::kColorId_TextfieldDefaultColor);
799 colors_[ThemeProperties::COLOR_NTP_BACKGROUND] = ntp_background;
800 colors_[ThemeProperties::COLOR_NTP_TEXT] = ntp_foreground;
802 // The NTP header is the color that surrounds the current active thumbnail on
803 // the NTP, and acts as the border of the "Recent Links" box. It would be
804 // awesome if they were separated so we could use GetBorderColor() for the
805 // border around the "Recent Links" section, but matching the frame color is
806 // more important.
808 colors_[ThemeProperties::COLOR_NTP_HEADER] = frame_color;
809 colors_[ThemeProperties::COLOR_NTP_SECTION] = toolbar_color;
810 colors_[ThemeProperties::COLOR_NTP_SECTION_TEXT] = label_color;
812 // Default link color, taken from gtklinkbutton.c.
813 SkColor link_color = SkColorSetRGB(0, 0, 0xee);
814 GetChromeStyleColor("link-color", &link_color);
816 colors_[ThemeProperties::COLOR_NTP_LINK] = link_color;
817 colors_[ThemeProperties::COLOR_NTP_LINK_UNDERLINE] = link_color;
818 colors_[ThemeProperties::COLOR_NTP_SECTION_LINK] = link_color;
819 colors_[ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE] = link_color;
821 // Generate the colors that we pass to WebKit.
822 focus_ring_color_ = frame_color;
824 SetScrollbarColors();
826 // Some GTK themes only define the text selection colors on the GtkEntry
827 // class, so we need to use that for getting selection colors.
828 active_selection_bg_color_ =
829 theme->GetSystemColor(
830 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused);
831 active_selection_fg_color_ =
832 theme->GetSystemColor(
833 ui::NativeTheme::kColorId_TextfieldSelectionColor);
834 inactive_selection_bg_color_ =
835 theme->GetSystemColor(
836 ui::NativeTheme::kColorId_TextfieldReadOnlyBackground);
837 inactive_selection_fg_color_ =
838 theme->GetSystemColor(
839 ui::NativeTheme::kColorId_TextfieldReadOnlyColor);
841 colors_[ThemeProperties::COLOR_THROBBER_SPINNING] =
842 theme->GetSystemColor(ui::NativeTheme::kColorId_ThrobberSpinningColor);
843 colors_[ThemeProperties::COLOR_THROBBER_WAITING] =
844 theme->GetSystemColor(ui::NativeTheme::kColorId_ThrobberWaitingColor);
847 SkColor Gtk2UI::BuildFrameColors() {
848 SkColor frame_color =
849 NativeThemeGtk2::instance()->GetSystemColor(
850 ui::NativeTheme::kColorId_WindowBackground);
851 SkColor temp_color;
852 SetThemeTint(ThemeProperties::TINT_BACKGROUND_TAB, frame_color);
855 #if GTK_MAJOR_VERSION == 2
856 color_utils::HSL kDefaultFrameShift = { -1, -1, 0.4 };
857 GtkStyle* style = gtk_rc_get_style(NativeThemeGtk2::instance()->GetWindow());
859 frame_color = color_utils::HSLShift(frame_color, kDefaultFrameShift);
860 GetChromeStyleColor("frame-color", &frame_color);
862 temp_color = frame_color;
863 colors_[ThemeProperties::COLOR_FRAME] = temp_color;
864 SetThemeTint(ThemeProperties::TINT_FRAME, temp_color);
866 temp_color = color_utils::HSLShift(
867 GdkColorToSkColor(style->bg[GTK_STATE_INSENSITIVE]),
868 kDefaultFrameShift);
869 GetChromeStyleColor("inactive-frame-color", &temp_color);
870 colors_[ThemeProperties::COLOR_FRAME_INACTIVE] = temp_color;
871 SetThemeTint(ThemeProperties::TINT_FRAME_INACTIVE, temp_color);
873 temp_color = color_utils::HSLShift(
874 frame_color,
875 GetDefaultTint(ThemeProperties::TINT_FRAME_INCOGNITO));
876 GetChromeStyleColor("incognito-frame-color", &temp_color);
877 colors_[ThemeProperties::COLOR_FRAME_INCOGNITO] = temp_color;
878 SetThemeTint(ThemeProperties::TINT_FRAME_INCOGNITO, temp_color);
880 temp_color = color_utils::HSLShift(
881 frame_color,
882 GetDefaultTint(ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE));
883 GetChromeStyleColor("incognito-inactive-frame-color", &temp_color);
884 colors_[ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE] = temp_color;
885 SetThemeTint(ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE, temp_color);
886 #else
887 const SkBitmap* bitmap;
889 bitmap = GetThemeImageNamed(IDR_THEME_FRAME).ToSkBitmap();
890 bitmap->lockPixels();
891 temp_color = bitmap->getColor(bitmap->width() / 2, bitmap->height() - 1);
892 bitmap->unlockPixels();
893 colors_[ThemeProperties::COLOR_FRAME] = temp_color;
895 bitmap = GetThemeImageNamed(IDR_THEME_FRAME_INACTIVE).ToSkBitmap();
896 bitmap->lockPixels();
897 temp_color = bitmap->getColor(bitmap->width() / 2, bitmap->height() - 1);
898 bitmap->unlockPixels();
899 colors_[ThemeProperties::COLOR_FRAME_INACTIVE] = temp_color;
901 bitmap = GetThemeImageNamed(IDR_THEME_FRAME_INCOGNITO).ToSkBitmap();
902 bitmap->lockPixels();
903 temp_color = bitmap->getColor(bitmap->width() / 2, bitmap->height() - 1);
904 bitmap->unlockPixels();
905 colors_[ThemeProperties::COLOR_FRAME_INCOGNITO] = temp_color;
907 bitmap = GetThemeImageNamed(IDR_THEME_FRAME_INCOGNITO_INACTIVE).ToSkBitmap();
908 bitmap->lockPixels();
909 temp_color = bitmap->getColor(bitmap->width() / 2, bitmap->height() - 1);
910 bitmap->unlockPixels();
911 colors_[ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE] = temp_color;
912 #endif
914 return frame_color;
917 void Gtk2UI::SetThemeTint(int id, SkColor color) {
918 color_utils::HSL default_tint = GetDefaultTint(id);
919 color_utils::HSL hsl;
920 color_utils::SkColorToHSL(color, &hsl);
922 if (default_tint.s != -1)
923 hsl.s = default_tint.s;
925 if (default_tint.l != -1)
926 hsl.l = default_tint.l;
928 tints_[id] = hsl;
931 gfx::Image Gtk2UI::GenerateGtkThemeImage(int id) const {
932 gfx::ImageSkiaSource* source = NULL;
934 switch (id) {
935 case IDR_BACK:
936 source = new GtkThemeIconSource(id, "go-previous", true);
937 break;
938 case IDR_BACK_D:
939 source = new GtkThemeIconSource(id, "go-previous", false);
940 break;
942 case IDR_FORWARD:
943 source = new GtkThemeIconSource(id, "go-next", true);
944 break;
945 case IDR_FORWARD_D:
946 source = new GtkThemeIconSource(id, "go-next", false);
947 break;
949 case IDR_HOME:
950 source = new GtkThemeIconSource(id, "go-home", true);
951 break;
953 case IDR_RELOAD:
954 source = new GtkThemeIconSource(id, "view-refresh", true);
955 break;
956 case IDR_RELOAD_D:
957 source = new GtkThemeIconSource(id, "view-refresh", false);
958 break;
960 case IDR_STOP:
961 source = new GtkThemeIconSource(id, "process-stop", true);
962 break;
963 case IDR_STOP_D:
964 source = new GtkThemeIconSource(id, "process-stop", false);
965 break;
968 if (source)
969 return gfx::Image(gfx::ImageSkia(source, 1));
971 return gfx::Image();
973 SkBitmap Gtk2UI::GenerateGtkThemeBitmap(int id) const {
974 switch (id) {
975 case IDR_THEME_TOOLBAR: {
976 SkBitmap bitmap;
977 bitmap.allocN32Pixels(kToolbarImageWidth, kToolbarImageHeight);
978 bitmap.eraseColor(
979 NativeThemeGtk2::instance()->GetSystemColor(
980 ui::NativeTheme::kColorId_LabelBackgroundColor));
981 return bitmap;
984 // TODO(erg): We list both the normal and *_DESKTOP versions of these
985 // images because in some contexts, we don't go through the
986 // chrome::MapThemeImage interface. That should be fixed, but tracking that
987 // down is Hard.
988 case IDR_THEME_TAB_BACKGROUND:
989 case IDR_THEME_TAB_BACKGROUND_DESKTOP:
990 return GenerateTabImage(IDR_THEME_FRAME);
991 case IDR_THEME_TAB_BACKGROUND_INCOGNITO:
992 case IDR_THEME_TAB_BACKGROUND_INCOGNITO_DESKTOP:
993 return GenerateTabImage(IDR_THEME_FRAME_INCOGNITO);
994 case IDR_FRAME:
995 case IDR_THEME_FRAME:
996 return GenerateFrameImage(ThemeProperties::COLOR_FRAME,
997 "frame-gradient-color");
998 case IDR_FRAME_INACTIVE:
999 case IDR_THEME_FRAME_INACTIVE:
1000 return GenerateFrameImage(ThemeProperties::COLOR_FRAME_INACTIVE,
1001 "inactive-frame-gradient-color");
1002 case IDR_THEME_FRAME_INCOGNITO:
1003 return GenerateFrameImage(ThemeProperties::COLOR_FRAME_INCOGNITO,
1004 "incognito-frame-gradient-color");
1005 case IDR_THEME_FRAME_INCOGNITO_INACTIVE: {
1006 return GenerateFrameImage(
1007 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE,
1008 "incognito-inactive-frame-gradient-color");
1010 // Icons that sit inside the omnibox shouldn't receive TINT_BUTTONS and
1011 // instead should tint based on the foreground text entry color in GTK+
1012 // mode because some themes that try to be dark *and* light have very
1013 // different colors between the omnibox and the normal background area.
1014 // TODO(erg): Decide what to do about other icons that appear in the
1015 // omnibox, e.g. content settings icons.
1016 case IDR_OMNIBOX_CALCULATOR:
1017 case IDR_OMNIBOX_EXTENSION_APP:
1018 case IDR_OMNIBOX_HTTP:
1019 case IDR_OMNIBOX_SEARCH:
1020 case IDR_OMNIBOX_STAR:
1021 case IDR_OMNIBOX_TTS: {
1022 return GenerateTintedIcon(id, entry_tint_);
1024 // In GTK mode, the dark versions of the omnibox icons only ever appear in
1025 // the autocomplete popup and only against the current theme's GtkEntry
1026 // base[GTK_STATE_SELECTED] color, so tint the icons so they won't collide
1027 // with the selected color.
1028 case IDR_OMNIBOX_EXTENSION_APP_DARK:
1029 case IDR_OMNIBOX_HTTP_DARK:
1030 case IDR_OMNIBOX_SEARCH_DARK:
1031 case IDR_OMNIBOX_STAR_DARK:
1032 case IDR_OMNIBOX_TTS_DARK: {
1033 return GenerateTintedIcon(id, selected_entry_tint_);
1036 case IDR_TOOLBAR_BEZEL_HOVER:
1037 return GenerateToolbarBezel(
1038 ui::NativeTheme::kHovered, IDR_TOOLBAR_BEZEL_HOVER);
1039 case IDR_TOOLBAR_BEZEL_PRESSED:
1040 return GenerateToolbarBezel(
1041 ui::NativeTheme::kPressed, IDR_TOOLBAR_BEZEL_PRESSED);
1043 // TODO(erg): The dropdown arrow should be tinted because we're injecting
1044 // various background GTK colors, but the code that accesses them needs to
1045 // be modified so that they ask their ui::ThemeProvider instead of the
1046 // ResourceBundle. (i.e. in a light on dark theme, the dropdown arrow will
1047 // be dark on dark)
1048 case IDR_MENU_DROPARROW:
1049 case IDR_BROWSER_ACTIONS_OVERFLOW:
1050 return GenerateTintedIcon(id, button_tint_);
1053 return SkBitmap();
1056 SkBitmap Gtk2UI::GenerateFrameImage(
1057 int color_id,
1058 const char* gradient_name) const {
1059 #if GTK_MAJOR_VERSION == 2
1060 ColorMap::const_iterator it = colors_.find(color_id);
1061 DCHECK(it != colors_.end());
1062 SkColor base = it->second;
1064 // We use two colors: the main color (passed in) and a lightened version of
1065 // that color (which is supposed to match the light gradient at the top of
1066 // several GTK+ themes, such as Ambiance, Clearlooks or Bluebird).
1067 const color_utils::HSL kGtkFrameShift = { -1, -1, 0.58 };
1068 gfx::Canvas canvas(gfx::Size(kToolbarImageWidth, kToolbarImageHeight),
1069 1.0f, true);
1071 SkColor gradient_top_color = color_utils::HSLShift(base, kGtkFrameShift);
1072 int gradient_size;
1074 GetChromeStyleColor(gradient_name, &gradient_top_color);
1075 gtk_widget_style_get(NativeThemeGtk2::instance()->GetWindow(),
1076 "frame-gradient-size", &gradient_size,
1077 NULL);
1079 if (gradient_size) {
1080 skia::RefPtr<SkShader> shader = gfx::CreateGradientShader(
1081 0, gradient_size, gradient_top_color, base);
1082 SkPaint paint;
1083 paint.setStyle(SkPaint::kFill_Style);
1084 paint.setAntiAlias(true);
1085 paint.setShader(shader.get());
1087 canvas.DrawRect(gfx::Rect(0, 0, kToolbarImageWidth, gradient_size), paint);
1090 canvas.FillRect(gfx::Rect(0, gradient_size, kToolbarImageWidth,
1091 kToolbarImageHeight - gradient_size), base);
1092 return canvas.ExtractImageRep().sk_bitmap();
1094 #else
1095 // Render a GtkHeaderBar as our title bar, cropping out any curved edges on
1096 // the left and right sides. Also remove the bottom border for good measure.
1097 SkBitmap bitmap;
1098 bitmap.allocN32Pixels(kToolbarImageWidth, 40);
1099 bitmap.eraseColor(0);
1102 static GtkWidget* title = NULL;
1103 if (!title) {
1104 title = gtk_header_bar_new();
1105 gtk_widget_set_size_request(title, kToolbarImageWidth * 2, 48);
1107 GtkWidget* window = gtk_offscreen_window_new();
1108 gtk_container_add(GTK_CONTAINER(window), title);
1110 gtk_widget_show_all(window);
1113 cairo_surface_t* surface = cairo_image_surface_create_for_data(
1114 static_cast<unsigned char*>(bitmap.getAddr(0, 0)),
1115 CAIRO_FORMAT_ARGB32,
1116 bitmap.width(), bitmap.height(),
1117 bitmap.width() * 4);
1118 cairo_t* cr = cairo_create(surface);
1119 cairo_translate(cr, kToolbarImageWidth / -2, 0);
1120 gtk_widget_draw(title, cr);
1121 cairo_destroy(cr);
1122 cairo_surface_destroy(surface);
1124 switch (color_id) {
1125 case ThemeProperties::COLOR_FRAME_INACTIVE:
1126 return SkBitmapOperations::CreateHSLShiftedBitmap(bitmap,
1127 kDefaultTintFrameInactive);
1128 case ThemeProperties::COLOR_FRAME_INCOGNITO:
1129 return SkBitmapOperations::CreateHSLShiftedBitmap(bitmap,
1130 kDefaultTintFrameIncognito);
1131 case ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE:
1132 return SkBitmapOperations::CreateHSLShiftedBitmap(bitmap,
1133 kDefaultTintFrameIncognitoInactive);
1136 return bitmap;
1137 #endif
1140 SkBitmap Gtk2UI::GenerateTabImage(int base_id) const {
1141 const SkBitmap* base_image = GetThemeImageNamed(base_id).ToSkBitmap();
1142 SkBitmap bg_tint = SkBitmapOperations::CreateHSLShiftedBitmap(
1143 *base_image, GetDefaultTint(ThemeProperties::TINT_BACKGROUND_TAB));
1144 return SkBitmapOperations::CreateTiledBitmap(
1145 bg_tint, 0, 0, bg_tint.width(), bg_tint.height());
1148 SkBitmap Gtk2UI::GenerateTintedIcon(
1149 int base_id,
1150 const color_utils::HSL& tint) const {
1151 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1152 return SkBitmapOperations::CreateHSLShiftedBitmap(
1153 rb.GetImageNamed(base_id).AsBitmap(), tint);
1156 SkBitmap Gtk2UI::GenerateToolbarBezel(
1157 ui::NativeTheme::State state,
1158 int sizing_idr) const {
1159 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
1160 SkBitmap default_bitmap =
1161 rb.GetImageNamed(sizing_idr).AsBitmap();
1163 return DrawGtkButtonBorder(
1164 NULL,
1165 state,
1166 default_bitmap.width(),
1167 default_bitmap.height());
1170 void Gtk2UI::GetNormalButtonTintHSL(color_utils::HSL* tint) const {
1171 NativeThemeGtk2* theme = NativeThemeGtk2::instance();
1173 SkColor accent_color =
1174 theme->GetSystemColor(
1175 ui::NativeTheme::kColorId_ButtonHighlightColor);
1176 SkColor text_color =
1177 theme->GetSystemColor(
1178 ui::NativeTheme::kColorId_LabelEnabledColor);
1179 SkColor base_color =
1180 theme->GetSystemColor(
1181 ui::NativeTheme::kColorId_LabelBackgroundColor);
1183 PickButtonTintFromColors(accent_color, text_color, base_color, tint);
1186 void Gtk2UI::GetNormalEntryForegroundHSL(color_utils::HSL* tint) const {
1187 NativeThemeGtk2* theme = NativeThemeGtk2::instance();
1189 SkColor accent_color =
1190 theme->GetSystemColor(
1191 ui::NativeTheme::kColorId_ButtonHighlightColor);
1192 SkColor text_color =
1193 theme->GetSystemColor(
1194 ui::NativeTheme::kColorId_TextfieldDefaultColor);
1195 SkColor base_color =
1196 theme->GetSystemColor(
1197 ui::NativeTheme::kColorId_TextfieldDefaultBackground);
1199 PickButtonTintFromColors(accent_color, text_color, base_color, tint);
1202 void Gtk2UI::GetSelectedEntryForegroundHSL(color_utils::HSL* tint) const {
1203 // The simplest of all the tints. We just use the selected text in the entry
1204 // since the icons tinted this way will only be displayed against
1205 // base[GTK_STATE_SELECTED].
1206 SkColor color =
1207 NativeThemeGtk2::instance()->GetSystemColor(
1208 ui::NativeTheme::kColorId_TextfieldSelectionColor);
1210 color_utils::SkColorToHSL(color, tint);
1213 SkBitmap Gtk2UI::DrawGtkButtonBorder(const char* class_name,
1214 ui::NativeTheme::State state,
1215 int width,
1216 int height) const {
1217 SkBitmap border;
1218 border.allocN32Pixels(width, height);
1219 border.eraseColor(0);
1221 // Create a temporary GTK button to snapshot
1222 GtkWidget* window = gtk_offscreen_window_new();
1223 GtkWidget* button = gtk_toggle_button_new();
1225 if (state == ui::NativeTheme::kPressed)
1226 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), true);
1227 else if (state == ui::NativeTheme::kDisabled)
1228 gtk_widget_set_sensitive(button, false);
1230 gtk_widget_set_size_request(button, width, height);
1231 gtk_container_add(GTK_CONTAINER(window), button);
1233 if (class_name == views::BlueButton::kViewClassName)
1234 TurnButtonBlue(button);
1236 gtk_widget_show_all(window);
1239 cairo_surface_t* surface = cairo_image_surface_create_for_data(
1240 static_cast<unsigned char*>(border.getAddr(0, 0)),
1241 CAIRO_FORMAT_ARGB32,
1242 width, height,
1243 width * 4);
1244 cairo_t* cr = cairo_create(surface);
1246 #if GTK_MAJOR_VERSION == 2
1247 int w, h;
1248 GdkPixmap* pixmap;
1251 // http://crbug.com/346740
1252 ANNOTATE_SCOPED_MEMORY_LEAK;
1253 pixmap = gtk_widget_get_snapshot(button, NULL);
1256 gdk_drawable_get_size(GDK_DRAWABLE(pixmap), &w, &h);
1257 GdkColormap* colormap = gdk_drawable_get_colormap(pixmap);
1258 GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable(NULL,
1259 GDK_DRAWABLE(pixmap),
1260 colormap,
1261 0, 0, 0, 0, w, h);
1263 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
1264 cairo_paint(cr);
1266 g_object_unref(pixbuf);
1267 g_object_unref(pixmap);
1268 #else
1269 gtk_widget_draw(button, cr);
1270 #endif
1272 cairo_destroy(cr);
1273 cairo_surface_destroy(surface);
1276 gtk_widget_destroy(window);
1278 return border;
1281 void Gtk2UI::ClearAllThemeData() {
1282 gtk_images_.clear();
1285 void Gtk2UI::UpdateDefaultFont() {
1286 PangoContext* pc = gtk_widget_get_pango_context(
1287 NativeThemeGtk2::instance()->GetLabel());
1288 const PangoFontDescription* desc = pango_context_get_font_description(pc);
1290 // Use gfx::FontRenderParams to select a family and determine the rendering
1291 // settings.
1292 gfx::FontRenderParamsQuery query;
1293 query.families = base::SplitString(pango_font_description_get_family(desc),
1294 ",", base::TRIM_WHITESPACE,
1295 base::SPLIT_WANT_ALL);
1297 if (pango_font_description_get_size_is_absolute(desc)) {
1298 // If the size is absolute, it's specified in Pango units. There are
1299 // PANGO_SCALE Pango units in a device unit (pixel).
1300 const int size_pixels = pango_font_description_get_size(desc) / PANGO_SCALE;
1301 default_font_size_pixels_ = size_pixels;
1302 query.pixel_size = size_pixels;
1303 } else {
1304 // Non-absolute sizes are in points (again scaled by PANGO_SIZE).
1305 // Round the value when converting to pixels to match GTK's logic.
1306 const double size_points = pango_font_description_get_size(desc) /
1307 static_cast<double>(PANGO_SCALE);
1308 default_font_size_pixels_ = static_cast<int>(
1309 GetPixelsInPoint(device_scale_factor_) * size_points + 0.5);
1310 query.point_size = static_cast<int>(size_points);
1313 query.style = gfx::Font::NORMAL;
1314 // TODO(davemoore): Support weights other than bold?
1315 if (pango_font_description_get_weight(desc) == PANGO_WEIGHT_BOLD)
1316 query.style |= gfx::Font::BOLD;
1317 // TODO(davemoore): What about PANGO_STYLE_OBLIQUE?
1318 if (pango_font_description_get_style(desc) == PANGO_STYLE_ITALIC)
1319 query.style |= gfx::Font::ITALIC;
1321 default_font_render_params_ =
1322 gfx::GetFontRenderParams(query, &default_font_family_);
1323 default_font_style_ = query.style;
1326 void Gtk2UI::ResetStyle() {
1327 ClearAllThemeData();
1328 LoadGtkValues();
1329 NativeThemeGtk2::instance()->NotifyObservers();
1332 void Gtk2UI::UpdateDeviceScaleFactor(float device_scale_factor) {
1333 device_scale_factor_ = device_scale_factor;
1334 UpdateDefaultFont();
1337 float Gtk2UI::GetDeviceScaleFactor() const {
1338 if (gfx::Display::HasForceDeviceScaleFactor())
1339 return gfx::Display::GetForcedDeviceScaleFactor();
1340 const int kCSSDefaultDPI = 96;
1341 const float scale = GetDPI() / kCSSDefaultDPI;
1342 // Round to 1 decimal, e.g. to 1.4.
1343 const float rounded = roundf(scale * 10) / 10;
1344 // See crbug.com/484400
1345 return rounded < 1.3 ? 1.0 : rounded;
1348 } // namespace libgtk2ui
1350 views::LinuxUI* BuildGtk2UI() {
1351 return new libgtk2ui::Gtk2UI;