1 // Copyright (c) 2011 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.
4
5 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
6
7 #include <gtk/gtk.h>
8
9 #include <set>
10 #include <string>
11
12 #include "base/environment.h"
13 #include "base/nix/xdg_util.h"
14 #include "base/stl_util-inl.h"
15 #include "chrome/browser/metrics/user_metrics.h"
16 #include "chrome/browser/prefs/pref_service.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/themes/theme_service_factory.h"
19 #include "chrome/browser/ui/browser_list.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/gtk/cairo_cached_surface.h"
22 #include "chrome/browser/ui/gtk/chrome_gtk_frame.h"
23 #include "chrome/browser/ui/gtk/gtk_chrome_button.h"
24 #include "chrome/browser/ui/gtk/gtk_util.h"
25 #include "chrome/browser/ui/gtk/hover_controller_gtk.h"
26 #include "chrome/common/pref_names.h"
27 #include "content/common/notification_details.h"
28 #include "content/common/notification_service.h"
29 #include "content/common/notification_source.h"
30 #include "content/common/notification_type.h"
31 #include "grit/app_resources.h"
32 #include "grit/theme_resources.h"
33 #include "third_party/skia/include/core/SkBitmap.h"
34 #include "third_party/skia/include/core/SkCanvas.h"
35 #include "third_party/skia/include/core/SkColor.h"
36 #include "third_party/skia/include/core/SkShader.h"
37 #include "ui/base/gtk/gtk_signal_registrar.h"
38 #include "ui/base/resource/resource_bundle.h"
39 #include "ui/gfx/canvas_skia.h"
40 #include "ui/gfx/color_utils.h"
41 #include "ui/gfx/gtk_util.h"
42 #include "ui/gfx/skbitmap_operations.h"
43 #include "ui/gfx/skia_util.h"
44 #include "ui/gfx/skia_utils_gtk.h"
45
46 namespace {
47
48 // The size of the rendered toolbar image.
49 const int kToolbarImageWidth = 64;
50 const int kToolbarImageHeight = 128;
51
52 // How much to tint the GTK+ color lighter at the top of the window.
53 const color_utils::HSL kGtkFrameShift = { -1, -1, 0.58 };
54
55 // How much to tint the GTK+ color when an explicit frame color hasn't been
56 // specified.
57 const color_utils::HSL kDefaultFrameShift = { -1, -1, 0.4 };
58
59 // Values used as the new luminance and saturation values in the inactive tab
60 // text color.
61 const double kDarkInactiveLuminance = 0.85;
62 const double kLightInactiveLuminance = 0.15;
63 const double kHeavyInactiveSaturation = 0.7;
64 const double kLightInactiveSaturation = 0.3;
65
66 // Number of times that the background color should be counted when trying to
67 // calculate the border color in GTK theme mode.
68 const int kBgWeight = 3;
69
70 // Padding to left, top and bottom of vertical separators.
71 const int kSeparatorPadding = 2;
72
73 // Default color for links on the NTP when the GTK+ theme doesn't define a
74 // link color. Constant taken from gtklinkbutton.c.
75 const GdkColor kDefaultLinkColor = { 0, 0, 0, 0xeeee };
76
77 // Middle color of the separator gradient.
78 const double kMidSeparatorColor[] =
79 { 194.0 / 255.0, 205.0 / 255.0, 212.0 / 212.0 };
80 // Top color of the separator gradient.
81 const double kTopSeparatorColor[] =
82 { 222.0 / 255.0, 234.0 / 255.0, 248.0 / 255.0 };
83
84 // Converts a GdkColor to a SkColor.
GdkToSkColor(const GdkColor * color)85 SkColor GdkToSkColor(const GdkColor* color) {
86 return SkColorSetRGB(color->red >> 8,
87 color->green >> 8,
88 color->blue >> 8);
89 }
90
91 // A list of images that we provide while in gtk mode.
92 const int kThemeImages[] = {
93 IDR_THEME_TOOLBAR,
94 IDR_THEME_TAB_BACKGROUND,
95 IDR_THEME_TAB_BACKGROUND_INCOGNITO,
96 IDR_THEME_FRAME,
97 IDR_THEME_FRAME_INACTIVE,
98 IDR_THEME_FRAME_INCOGNITO,
99 IDR_THEME_FRAME_INCOGNITO_INACTIVE,
100 };
101
102 // A list of icons used in the autocomplete view that should be tinted to the
103 // current gtk theme selection color so they stand out against the GtkEntry's
104 // base color.
105 const int kAutocompleteImages[] = {
106 IDR_OMNIBOX_EXTENSION_APP,
107 IDR_OMNIBOX_HTTP,
108 IDR_OMNIBOX_HTTP_DARK,
109 IDR_OMNIBOX_HISTORY,
110 IDR_OMNIBOX_HISTORY_DARK,
111 IDR_OMNIBOX_SEARCH,
112 IDR_OMNIBOX_SEARCH_DARK,
113 IDR_OMNIBOX_STAR,
114 IDR_OMNIBOX_STAR_DARK,
115 IDR_GEOLOCATION_ALLOWED_LOCATIONBAR_ICON,
116 IDR_GEOLOCATION_DENIED_LOCATIONBAR_ICON,
117 };
118
IsOverridableImage(int id)119 bool IsOverridableImage(int id) {
120 static std::set<int> images;
121 if (images.empty()) {
122 images.insert(kThemeImages, kThemeImages + arraysize(kThemeImages));
123 images.insert(kAutocompleteImages,
124 kAutocompleteImages + arraysize(kAutocompleteImages));
125
126 const std::set<int>& buttons = ThemeService::GetTintableToolbarButtons();
127 images.insert(buttons.begin(), buttons.end());
128 }
129
130 return images.count(id) > 0;
131 }
132
133 // Picks a button tint from a set of background colors. While
134 // |accent_gdk_color| will usually be the same color through a theme, this
135 // function will get called with the normal GtkLabel |text_color|/GtkWindow
136 // |background_color| pair and the GtkEntry |text_color|/|background_color|
137 // pair. While 3/4 of the time the resulting tint will be the same, themes that
138 // have a dark window background (with light text) and a light text entry (with
139 // dark text) will get better icons with this separated out.
PickButtonTintFromColors(const GdkColor & accent_gdk_color,const GdkColor & text_color,const GdkColor & background_color,color_utils::HSL * tint)140 void PickButtonTintFromColors(const GdkColor& accent_gdk_color,
141 const GdkColor& text_color,
142 const GdkColor& background_color,
143 color_utils::HSL* tint) {
144 SkColor accent_color = GdkToSkColor(&accent_gdk_color);
145 color_utils::HSL accent_tint;
146 color_utils::SkColorToHSL(accent_color, &accent_tint);
147
148 color_utils::HSL text_tint;
149 color_utils::SkColorToHSL(GdkToSkColor(&text_color), &text_tint);
150
151 color_utils::HSL background_tint;
152 color_utils::SkColorToHSL(GdkToSkColor(&background_color), &background_tint);
153
154 // If the accent color is gray, then our normal HSL tomfoolery will bring out
155 // whatever color is oddly dominant (for example, in rgb space [125, 128,
156 // 125] will tint green instead of gray). Slight differences (+/-10 (4%) to
157 // all color components) should be interpreted as this color being gray and
158 // we should switch into a special grayscale mode.
159 int rb_diff = abs(SkColorGetR(accent_color) - SkColorGetB(accent_color));
160 int rg_diff = abs(SkColorGetR(accent_color) - SkColorGetG(accent_color));
161 int bg_diff = abs(SkColorGetB(accent_color) - SkColorGetG(accent_color));
162 if (rb_diff < 10 && rg_diff < 10 && bg_diff < 10) {
163 // Our accent is white/gray/black. Only the luminance of the accent color
164 // matters.
165 tint->h = -1;
166
167 // Use the saturation of the text.
168 tint->s = text_tint.s;
169
170 // Use the luminance of the accent color UNLESS there isn't enough
171 // luminance contrast between the accent color and the base color.
172 if (fabs(accent_tint.l - background_tint.l) > 0.3)
173 tint->l = accent_tint.l;
174 else
175 tint->l = text_tint.l;
176 } else {
177 // Our accent is a color.
178 tint->h = accent_tint.h;
179
180 // Don't modify the saturation; the amount of color doesn't matter.
181 tint->s = -1;
182
183 // If the text wants us to darken the icon, don't change the luminance (the
184 // icons are already dark enough). Otherwise, lighten the icon by no more
185 // than 0.9 since we don't want a pure-white icon even if the text is pure
186 // white.
187 if (text_tint.l < 0.5)
188 tint->l = -1;
189 else if (text_tint.l <= 0.9)
190 tint->l = text_tint.l;
191 else
192 tint->l = 0.9;
193 }
194 }
195
196
197 // Builds and tints the image with |id| to the GtkStateType |state| and
198 // places the result in |icon_set|.
BuildIconFromIDRWithColor(int id,GtkStyle * style,GtkStateType state,GtkIconSet * icon_set)199 void BuildIconFromIDRWithColor(int id,
200 GtkStyle* style,
201 GtkStateType state,
202 GtkIconSet* icon_set) {
203 SkColor color = GdkToSkColor(&style->fg[state]);
204 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
205 SkBitmap original = *rb.GetBitmapNamed(id);
206
207 SkBitmap fill_color;
208 fill_color.setConfig(SkBitmap::kARGB_8888_Config,
209 original.width(), original.height(), 0);
210 fill_color.allocPixels();
211 fill_color.eraseColor(color);
212 SkBitmap masked = SkBitmapOperations::CreateMaskedBitmap(
213 fill_color, original);
214
215 GtkIconSource* icon = gtk_icon_source_new();
216 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&masked);
217 gtk_icon_source_set_pixbuf(icon, pixbuf);
218 g_object_unref(pixbuf);
219
220 gtk_icon_source_set_direction_wildcarded(icon, TRUE);
221 gtk_icon_source_set_size_wildcarded(icon, TRUE);
222
223 gtk_icon_source_set_state(icon, state);
224 // All fields default to wildcarding being on and setting a property doesn't
225 // turn off wildcarding. You need to do this yourself. This is stated once in
226 // the documentation in the gtk_icon_source_new() function, and no where else.
227 gtk_icon_source_set_state_wildcarded(
228 icon, state == GTK_STATE_NORMAL);
229
230 gtk_icon_set_add_source(icon_set, icon);
231 gtk_icon_source_free(icon);
232 }
233
234 // Applies an HSL shift to a GdkColor (instead of an SkColor)
GdkColorHSLShift(const color_utils::HSL & shift,GdkColor * frame_color)235 void GdkColorHSLShift(const color_utils::HSL& shift, GdkColor* frame_color) {
236 SkColor shifted = color_utils::HSLShift(GdkToSkColor(frame_color), shift);
237 frame_color->pixel = 0;
238 frame_color->red = SkColorGetR(shifted) * kSkiaToGDKMultiplier;
239 frame_color->green = SkColorGetG(shifted) * kSkiaToGDKMultiplier;
240 frame_color->blue = SkColorGetB(shifted) * kSkiaToGDKMultiplier;
241 }
242
243 } // namespace
244
245 GtkWidget* GtkThemeService::icon_widget_ = NULL;
246 GdkPixbuf* GtkThemeService::default_folder_icon_ = NULL;
247 GdkPixbuf* GtkThemeService::default_bookmark_icon_ = NULL;
248
249 // static
GetFrom(Profile * profile)250 GtkThemeService* GtkThemeService::GetFrom(Profile* profile) {
251 return static_cast<GtkThemeService*>(
252 ThemeServiceFactory::GetForProfile(profile));
253 }
254
GtkThemeService()255 GtkThemeService::GtkThemeService()
256 : ThemeService(),
257 fake_window_(gtk_window_new(GTK_WINDOW_TOPLEVEL)),
258 fake_frame_(chrome_gtk_frame_new()),
259 signals_(new ui::GtkSignalRegistrar),
260 fullscreen_icon_set_(NULL) {
261 fake_label_.Own(gtk_label_new(""));
262 fake_entry_.Own(gtk_entry_new());
263 fake_menu_item_.Own(gtk_menu_item_new());
264
265 // Only realized widgets receive style-set notifications, which we need to
266 // broadcast new theme images and colors. Only realized widgets have style
267 // properties, too, which we query for some colors.
268 gtk_widget_realize(fake_frame_);
269 gtk_widget_realize(fake_window_);
270 signals_->Connect(fake_frame_, "style-set",
271 G_CALLBACK(&OnStyleSetThunk), this);
272 }
273
~GtkThemeService()274 GtkThemeService::~GtkThemeService() {
275 gtk_widget_destroy(fake_window_);
276 gtk_widget_destroy(fake_frame_);
277 fake_label_.Destroy();
278 fake_entry_.Destroy();
279 fake_menu_item_.Destroy();
280
281 FreeIconSets();
282
283 // We have to call this because FreePlatformCached() in ~ThemeService
284 // doesn't call the right virutal FreePlatformCaches.
285 FreePlatformCaches();
286 }
287
Init(Profile * profile)288 void GtkThemeService::Init(Profile* profile) {
289 registrar_.Init(profile->GetPrefs());
290 registrar_.Add(prefs::kUsesSystemTheme, this);
291 use_gtk_ = profile->GetPrefs()->GetBoolean(prefs::kUsesSystemTheme);
292
293 ThemeService::Init(profile);
294 }
295
GetBitmapNamed(int id) const296 SkBitmap* GtkThemeService::GetBitmapNamed(int id) const {
297 // Try to get our cached version:
298 ImageCache::const_iterator it = gtk_images_.find(id);
299 if (it != gtk_images_.end())
300 return it->second;
301
302 if (use_gtk_ && IsOverridableImage(id)) {
303 // We haven't built this image yet:
304 SkBitmap* bitmap = GenerateGtkThemeBitmap(id);
305 gtk_images_[id] = bitmap;
306 return bitmap;
307 }
308
309 return ThemeService::GetBitmapNamed(id);
310 }
311
GetColor(int id) const312 SkColor GtkThemeService::GetColor(int id) const {
313 if (use_gtk_) {
314 ColorMap::const_iterator it = colors_.find(id);
315 if (it != colors_.end())
316 return it->second;
317 }
318
319 return ThemeService::GetColor(id);
320 }
321
HasCustomImage(int id) const322 bool GtkThemeService::HasCustomImage(int id) const {
323 if (use_gtk_)
324 return IsOverridableImage(id);
325
326 return ThemeService::HasCustomImage(id);
327 }
328
InitThemesFor(NotificationObserver * observer)329 void GtkThemeService::InitThemesFor(NotificationObserver* observer) {
330 observer->Observe(NotificationType::BROWSER_THEME_CHANGED,
331 Source<ThemeService>(this),
332 NotificationService::NoDetails());
333 }
334
SetTheme(const Extension * extension)335 void GtkThemeService::SetTheme(const Extension* extension) {
336 profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, false);
337 LoadDefaultValues();
338 ThemeService::SetTheme(extension);
339 }
340
UseDefaultTheme()341 void GtkThemeService::UseDefaultTheme() {
342 profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, false);
343 LoadDefaultValues();
344 ThemeService::UseDefaultTheme();
345 }
346
SetNativeTheme()347 void GtkThemeService::SetNativeTheme() {
348 profile()->GetPrefs()->SetBoolean(prefs::kUsesSystemTheme, true);
349 ClearAllThemeData();
350 LoadGtkValues();
351 NotifyThemeChanged();
352 }
353
UsingDefaultTheme()354 bool GtkThemeService::UsingDefaultTheme() {
355 return !use_gtk_ && ThemeService::UsingDefaultTheme();
356 }
357
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)358 void GtkThemeService::Observe(NotificationType type,
359 const NotificationSource& source,
360 const NotificationDetails& details) {
361 if ((type == NotificationType::PREF_CHANGED) &&
362 (*Details<std::string>(details).ptr() == prefs::kUsesSystemTheme)) {
363 use_gtk_ = profile()->GetPrefs()->GetBoolean(prefs::kUsesSystemTheme);
364 } else {
365 ThemeService::Observe(type, source, details);
366 }
367 }
368
BuildChromeButton()369 GtkWidget* GtkThemeService::BuildChromeButton() {
370 GtkWidget* button = HoverControllerGtk::CreateChromeButton();
371 gtk_chrome_button_set_use_gtk_rendering(GTK_CHROME_BUTTON(button), use_gtk_);
372 chrome_buttons_.push_back(button);
373
374 signals_->Connect(button, "destroy", G_CALLBACK(OnDestroyChromeButtonThunk),
375 this);
376 return button;
377 }
378
CreateToolbarSeparator()379 GtkWidget* GtkThemeService::CreateToolbarSeparator() {
380 GtkWidget* separator = gtk_vseparator_new();
381 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
382 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
383 kSeparatorPadding, kSeparatorPadding, kSeparatorPadding, 0);
384 gtk_container_add(GTK_CONTAINER(alignment), separator);
385
386 signals_->Connect(separator, "expose-event",
387 G_CALLBACK(OnSeparatorExposeThunk), this);
388 return alignment;
389 }
390
UseGtkTheme() const391 bool GtkThemeService::UseGtkTheme() const {
392 return use_gtk_;
393 }
394
GetGdkColor(int id) const395 GdkColor GtkThemeService::GetGdkColor(int id) const {
396 return gfx::SkColorToGdkColor(GetColor(id));
397 }
398
GetBorderColor() const399 GdkColor GtkThemeService::GetBorderColor() const {
400 GtkStyle* style = gtk_rc_get_style(fake_window_);
401
402 GdkColor text;
403 GdkColor bg;
404 if (use_gtk_) {
405 text = style->text[GTK_STATE_NORMAL];
406 bg = style->bg[GTK_STATE_NORMAL];
407 } else {
408 text = GetGdkColor(COLOR_BOOKMARK_TEXT);
409 bg = GetGdkColor(COLOR_TOOLBAR);
410 }
411
412 // Creates a weighted average between the text and base color where
413 // the base color counts more than once.
414 GdkColor color;
415 color.pixel = 0;
416 color.red = (text.red + (bg.red * kBgWeight)) / (1 + kBgWeight);
417 color.green = (text.green + (bg.green * kBgWeight)) / (1 + kBgWeight);
418 color.blue = (text.blue + (bg.blue * kBgWeight)) / (1 + kBgWeight);
419
420 return color;
421 }
422
GetIconSetForId(int id) const423 GtkIconSet* GtkThemeService::GetIconSetForId(int id) const {
424 if (id == IDR_FULLSCREEN_MENU_BUTTON)
425 return fullscreen_icon_set_;
426
427 return NULL;
428 }
429
GetScrollbarColors(GdkColor * thumb_active_color,GdkColor * thumb_inactive_color,GdkColor * track_color)430 void GtkThemeService::GetScrollbarColors(GdkColor* thumb_active_color,
431 GdkColor* thumb_inactive_color,
432 GdkColor* track_color) {
433 const GdkColor* theme_thumb_active = NULL;
434 const GdkColor* theme_thumb_inactive = NULL;
435 const GdkColor* theme_trough_color = NULL;
436 gtk_widget_style_get(GTK_WIDGET(fake_frame_),
437 "scrollbar-slider-prelight-color", &theme_thumb_active,
438 "scrollbar-slider-normal-color", &theme_thumb_inactive,
439 "scrollbar-trough-color", &theme_trough_color,
440 NULL);
441
442 // Ask the theme if the theme specifies all the scrollbar colors and short
443 // circuit the expensive painting/compositing if we have all of them.
444 if (theme_thumb_active && theme_thumb_inactive && theme_trough_color) {
445 *thumb_active_color = *theme_thumb_active;
446 *thumb_inactive_color = *theme_thumb_inactive;
447 *track_color = *theme_trough_color;
448 return;
449 }
450
451 // Create window containing scrollbar elements
452 GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP);
453 GtkWidget* fixed = gtk_fixed_new();
454 GtkWidget* scrollbar = gtk_hscrollbar_new(NULL);
455 gtk_container_add(GTK_CONTAINER(window), fixed);
456 gtk_container_add(GTK_CONTAINER(fixed), scrollbar);
457 gtk_widget_realize(window);
458 gtk_widget_realize(scrollbar);
459
460 // Draw scrollbar thumb part and track into offscreen image
461 const int kWidth = 100;
462 const int kHeight = 20;
463 GtkStyle* style = gtk_rc_get_style(scrollbar);
464 GdkPixmap* pm = gdk_pixmap_new(window->window, kWidth, kHeight, -1);
465 GdkRectangle rect = { 0, 0, kWidth, kHeight };
466 unsigned char data[3 * kWidth * kHeight];
467 for (int i = 0; i < 3; ++i) {
468 if (i < 2) {
469 // Thumb part
470 gtk_paint_slider(style, pm,
471 i == 0 ? GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
472 GTK_SHADOW_OUT, &rect, scrollbar, "slider", 0, 0,
473 kWidth, kHeight, GTK_ORIENTATION_HORIZONTAL);
474 } else {
475 // Track
476 gtk_paint_box(style, pm, GTK_STATE_ACTIVE, GTK_SHADOW_IN, &rect,
477 scrollbar, "trough-upper", 0, 0, kWidth, kHeight);
478 }
479 GdkPixbuf* pb = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB,
480 FALSE, 8, kWidth, kHeight,
481 3 * kWidth, 0, 0);
482 gdk_pixbuf_get_from_drawable(pb, pm, NULL, 0, 0, 0, 0, kWidth, kHeight);
483
484 // Sample pixels
485 int components[3] = { 0 };
486 for (int y = 2; y < kHeight - 2; ++y) {
487 for (int c = 0; c < 3; ++c) {
488 // Sample a vertical slice of pixels at about one-thirds from the
489 // left edge. This allows us to avoid any fixed graphics that might be
490 // located at the edges or in the center of the scrollbar.
491 // Each pixel is made up of a red, green, and blue component; taking up
492 // a total of three bytes.
493 components[c] += data[3 * (kWidth / 3 + y * kWidth) + c];
494 }
495 }
496 GdkColor* color = i == 0 ? thumb_active_color :
497 i == 1 ? thumb_inactive_color :
498 track_color;
499 color->pixel = 0;
500 // We sampled pixels across the full height of the image, ignoring a two
501 // pixel border. In some themes, the border has a completely different
502 // color which we do not want to factor into our average color computation.
503 //
504 // We now need to scale the colors from the 0..255 range, to the wider
505 // 0..65535 range, and we need to actually compute the average color; so,
506 // we divide by the total number of pixels in the sample.
507 color->red = components[0] * 65535 / (255 * (kHeight - 4));
508 color->green = components[1] * 65535 / (255 * (kHeight - 4));
509 color->blue = components[2] * 65535 / (255 * (kHeight - 4));
510
511 g_object_unref(pb);
512 }
513 g_object_unref(pm);
514
515 gtk_widget_destroy(window);
516
517 // Override any of the default colors with ones that were specified by the
518 // theme.
519 if (theme_thumb_active)
520 *thumb_active_color = *theme_thumb_active;
521
522 if (theme_thumb_inactive)
523 *thumb_inactive_color = *theme_thumb_inactive;
524
525 if (theme_trough_color)
526 *track_color = *theme_trough_color;
527 }
528
GetSurfaceNamed(int id,GtkWidget * widget_on_display)529 CairoCachedSurface* GtkThemeService::GetSurfaceNamed(
530 int id,
531 GtkWidget* widget_on_display) {
532 return GetSurfaceNamedImpl(id,
533 &per_display_surfaces_,
534 GetPixbufNamed(id),
535 widget_on_display);
536 }
537
GetRTLEnabledSurfaceNamed(int id,GtkWidget * widget_on_display)538 CairoCachedSurface* GtkThemeService::GetRTLEnabledSurfaceNamed(
539 int id,
540 GtkWidget* widget_on_display) {
541 // We flip the sign of |id| when passing it to GetSurfaceNamedImpl() for the
542 // same reason that ThemeService::GetPixbufImpl() does: so that if one
543 // location calls this function with a resource ID, and another place calls
544 // GetSurfaceNamed() with the same ID, they'll correctly get different
545 // surfaces in RTL mode.
546 return GetSurfaceNamedImpl(-id,
547 &per_display_surfaces_,
548 GetRTLEnabledPixbufNamed(id),
549 widget_on_display);
550 }
551
GetUnthemedSurfaceNamed(int id,GtkWidget * widget_on_display)552 CairoCachedSurface* GtkThemeService::GetUnthemedSurfaceNamed(
553 int id,
554 GtkWidget* widget_on_display) {
555 return GetSurfaceNamedImpl(id,
556 &per_display_unthemed_surfaces_,
557 ResourceBundle::GetSharedInstance().GetPixbufNamed(id),
558 widget_on_display);
559 }
560
561 // static
GetFolderIcon(bool native)562 GdkPixbuf* GtkThemeService::GetFolderIcon(bool native) {
563 if (native) {
564 if (!icon_widget_)
565 icon_widget_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
566 // We never release our ref, so we will leak this on program shutdown.
567 if (!default_folder_icon_) {
568 default_folder_icon_ =
569 gtk_widget_render_icon(icon_widget_, GTK_STOCK_DIRECTORY,
570 GTK_ICON_SIZE_MENU, NULL);
571 }
572 if (default_folder_icon_)
573 return default_folder_icon_;
574 }
575
576 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
577 static GdkPixbuf* default_folder_icon_ = rb.GetPixbufNamed(
578 IDR_BOOKMARK_BAR_FOLDER);
579 return default_folder_icon_;
580 }
581
582 // static
GetDefaultFavicon(bool native)583 GdkPixbuf* GtkThemeService::GetDefaultFavicon(bool native) {
584 if (native) {
585 if (!icon_widget_)
586 icon_widget_ = gtk_window_new(GTK_WINDOW_TOPLEVEL);
587 // We never release our ref, so we will leak this on program shutdown.
588 if (!default_bookmark_icon_) {
589 default_bookmark_icon_ =
590 gtk_widget_render_icon(icon_widget_, GTK_STOCK_FILE,
591 GTK_ICON_SIZE_MENU, NULL);
592 }
593 if (default_bookmark_icon_)
594 return default_bookmark_icon_;
595 }
596
597 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
598 static GdkPixbuf* default_bookmark_icon_ = rb.GetPixbufNamed(
599 IDR_DEFAULT_FAVICON);
600 return default_bookmark_icon_;
601 }
602
603 // static
DefaultUsesSystemTheme()604 bool GtkThemeService::DefaultUsesSystemTheme() {
605 #if defined(OS_CHROMEOS)
606 return false;
607 #else
608 scoped_ptr<base::Environment> env(base::Environment::Create());
609
610 switch (base::nix::GetDesktopEnvironment(env.get())) {
611 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
612 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
613 return true;
614 default:
615 return false;
616 }
617 #endif
618 }
619
ClearAllThemeData()620 void GtkThemeService::ClearAllThemeData() {
621 colors_.clear();
622 tints_.clear();
623
624 ThemeService::ClearAllThemeData();
625 }
626
LoadThemePrefs()627 void GtkThemeService::LoadThemePrefs() {
628 if (use_gtk_) {
629 LoadGtkValues();
630 } else {
631 LoadDefaultValues();
632 ThemeService::LoadThemePrefs();
633 }
634
635 RebuildMenuIconSets();
636 }
637
NotifyThemeChanged()638 void GtkThemeService::NotifyThemeChanged() {
639 ThemeService::NotifyThemeChanged();
640
641 // Notify all GtkChromeButtons of their new rendering mode:
642 for (std::vector<GtkWidget*>::iterator it = chrome_buttons_.begin();
643 it != chrome_buttons_.end(); ++it) {
644 gtk_chrome_button_set_use_gtk_rendering(
645 GTK_CHROME_BUTTON(*it), use_gtk_);
646 }
647
648 Browser* browser = BrowserList::GetLastActive();
649 if (browser && browser->window()) {
650 gtk_util::SetDefaultWindowIcon(browser->window()->GetNativeHandle());
651 }
652 }
653
FreePlatformCaches()654 void GtkThemeService::FreePlatformCaches() {
655 ThemeService::FreePlatformCaches();
656 FreePerDisplaySurfaces(&per_display_surfaces_);
657 FreePerDisplaySurfaces(&per_display_unthemed_surfaces_);
658 STLDeleteValues(>k_images_);
659 }
660
OnStyleSet(GtkWidget * widget,GtkStyle * previous_style)661 void GtkThemeService::OnStyleSet(GtkWidget* widget,
662 GtkStyle* previous_style) {
663 GdkPixbuf* default_folder_icon = default_folder_icon_;
664 GdkPixbuf* default_bookmark_icon = default_bookmark_icon_;
665 default_folder_icon_ = NULL;
666 default_bookmark_icon_ = NULL;
667
668 if (profile()->GetPrefs()->GetBoolean(prefs::kUsesSystemTheme)) {
669 ClearAllThemeData();
670 LoadGtkValues();
671 NotifyThemeChanged();
672 }
673
674 RebuildMenuIconSets();
675
676 // Free the old icons only after the theme change notification has gone
677 // through.
678 if (default_folder_icon)
679 g_object_unref(default_folder_icon);
680 if (default_bookmark_icon)
681 g_object_unref(default_bookmark_icon);
682 }
683
LoadGtkValues()684 void GtkThemeService::LoadGtkValues() {
685 // Before we start setting images and values, we have to clear out old, stale
686 // values. (If we don't do this, we'll regress startup time in the case where
687 // someone installs a heavyweight theme, then goes back to GTK.)
688 profile()->GetPrefs()->ClearPref(prefs::kCurrentThemeImages);
689
690 GtkStyle* frame_style = gtk_rc_get_style(fake_frame_);
691
692 GtkStyle* window_style = gtk_rc_get_style(fake_window_);
693 SetThemeColorFromGtk(ThemeService::COLOR_CONTROL_BACKGROUND,
694 &window_style->bg[GTK_STATE_NORMAL]);
695 SetThemeColorFromGtk(ThemeService::COLOR_BUTTON_BACKGROUND,
696 &window_style->bg[GTK_STATE_NORMAL]);
697
698 GdkColor toolbar_color = window_style->bg[GTK_STATE_NORMAL];
699 SetThemeColorFromGtk(ThemeService::COLOR_TOOLBAR, &toolbar_color);
700
701 GdkColor button_color = window_style->bg[GTK_STATE_SELECTED];
702 SetThemeTintFromGtk(ThemeService::TINT_BUTTONS, &button_color);
703
704 GtkStyle* label_style = gtk_rc_get_style(fake_label_.get());
705 GdkColor label_color = label_style->fg[GTK_STATE_NORMAL];
706 SetThemeColorFromGtk(ThemeService::COLOR_TAB_TEXT, &label_color);
707 SetThemeColorFromGtk(ThemeService::COLOR_BOOKMARK_TEXT, &label_color);
708
709 // Build the various icon tints.
710 GetNormalButtonTintHSL(&button_tint_);
711 GetNormalEntryForegroundHSL(&entry_tint_);
712 GetSelectedEntryForegroundHSL(&selected_entry_tint_);
713 GdkColor frame_color = BuildFrameColors(frame_style);
714
715 // The inactive frame color never occurs naturally in the theme, as it is a
716 // tinted version of |frame_color|. We generate another color based on the
717 // background tab color, with the lightness and saturation moved in the
718 // opposite direction. (We don't touch the hue, since there should be subtle
719 // hints of the color in the text.)
720 color_utils::HSL inactive_tab_text_hsl = tints_[TINT_BACKGROUND_TAB];
721 if (inactive_tab_text_hsl.l < 0.5)
722 inactive_tab_text_hsl.l = kDarkInactiveLuminance;
723 else
724 inactive_tab_text_hsl.l = kLightInactiveLuminance;
725
726 if (inactive_tab_text_hsl.s < 0.5)
727 inactive_tab_text_hsl.s = kHeavyInactiveSaturation;
728 else
729 inactive_tab_text_hsl.s = kLightInactiveSaturation;
730
731 colors_[ThemeService::COLOR_BACKGROUND_TAB_TEXT] =
732 color_utils::HSLToSkColor(inactive_tab_text_hsl, 255);
733
734 // We pick the text and background colors for the NTP out of the colors for a
735 // GtkEntry. We do this because GtkEntries background color is never the same
736 // as |toolbar_color|, is usually a white, and when it isn't a white,
737 // provides sufficient contrast to |toolbar_color|. Try this out with
738 // Darklooks, HighContrastInverse or ThinIce.
739 GtkStyle* entry_style = gtk_rc_get_style(fake_entry_.get());
740 GdkColor ntp_background = entry_style->base[GTK_STATE_NORMAL];
741 GdkColor ntp_foreground = entry_style->text[GTK_STATE_NORMAL];
742 SetThemeColorFromGtk(ThemeService::COLOR_NTP_BACKGROUND,
743 &ntp_background);
744 SetThemeColorFromGtk(ThemeService::COLOR_NTP_TEXT,
745 &ntp_foreground);
746
747 // The NTP header is the color that surrounds the current active thumbnail on
748 // the NTP, and acts as the border of the "Recent Links" box. It would be
749 // awesome if they were separated so we could use GetBorderColor() for the
750 // border around the "Recent Links" section, but matching the frame color is
751 // more important.
752 SetThemeColorFromGtk(ThemeService::COLOR_NTP_HEADER,
753 &frame_color);
754 SetThemeColorFromGtk(ThemeService::COLOR_NTP_SECTION,
755 &toolbar_color);
756 SetThemeColorFromGtk(ThemeService::COLOR_NTP_SECTION_TEXT,
757 &label_color);
758
759 // Override the link color if the theme provides it.
760 const GdkColor* link_color = NULL;
761 gtk_widget_style_get(GTK_WIDGET(fake_window_),
762 "link-color", &link_color, NULL);
763 if (!link_color)
764 link_color = &kDefaultLinkColor;
765
766 SetThemeColorFromGtk(ThemeService::COLOR_NTP_LINK,
767 link_color);
768 SetThemeColorFromGtk(ThemeService::COLOR_NTP_LINK_UNDERLINE,
769 link_color);
770 SetThemeColorFromGtk(ThemeService::COLOR_NTP_SECTION_LINK,
771 link_color);
772 SetThemeColorFromGtk(ThemeService::COLOR_NTP_SECTION_LINK_UNDERLINE,
773 link_color);
774
775 // Generate the colors that we pass to WebKit.
776 focus_ring_color_ = GdkToSkColor(&frame_color);
777 GdkColor thumb_active_color, thumb_inactive_color, track_color;
778 GtkThemeService::GetScrollbarColors(&thumb_active_color,
779 &thumb_inactive_color,
780 &track_color);
781 thumb_active_color_ = GdkToSkColor(&thumb_active_color);
782 thumb_inactive_color_ = GdkToSkColor(&thumb_inactive_color);
783 track_color_ = GdkToSkColor(&track_color);
784
785 // Some GTK themes only define the text selection colors on the GtkEntry
786 // class, so we need to use that for getting selection colors.
787 active_selection_bg_color_ =
788 GdkToSkColor(&entry_style->base[GTK_STATE_SELECTED]);
789 active_selection_fg_color_ =
790 GdkToSkColor(&entry_style->text[GTK_STATE_SELECTED]);
791 inactive_selection_bg_color_ =
792 GdkToSkColor(&entry_style->base[GTK_STATE_ACTIVE]);
793 inactive_selection_fg_color_ =
794 GdkToSkColor(&entry_style->text[GTK_STATE_ACTIVE]);
795 }
796
BuildFrameColors(GtkStyle * frame_style)797 GdkColor GtkThemeService::BuildFrameColors(GtkStyle* frame_style) {
798 const GdkColor* theme_frame = NULL;
799 const GdkColor* theme_inactive_frame = NULL;
800 const GdkColor* theme_incognito_frame = NULL;
801 const GdkColor* theme_incognito_inactive_frame = NULL;
802 gtk_widget_style_get(GTK_WIDGET(fake_frame_),
803 "frame-color", &theme_frame,
804 "inactive-frame-color", &theme_inactive_frame,
805 "incognito-frame-color", &theme_incognito_frame,
806 "incognito-inactive-frame-color",
807 &theme_incognito_inactive_frame,
808 NULL);
809
810 GdkColor frame_color = BuildAndSetFrameColor(
811 &frame_style->bg[GTK_STATE_SELECTED],
812 theme_frame,
813 kDefaultFrameShift,
814 ThemeService::COLOR_FRAME,
815 ThemeService::TINT_FRAME);
816 SetThemeTintFromGtk(ThemeService::TINT_BACKGROUND_TAB, &frame_color);
817
818 BuildAndSetFrameColor(
819 &frame_style->bg[GTK_STATE_INSENSITIVE],
820 theme_inactive_frame,
821 kDefaultFrameShift,
822 ThemeService::COLOR_FRAME_INACTIVE,
823 ThemeService::TINT_FRAME_INACTIVE);
824
825 BuildAndSetFrameColor(
826 &frame_color,
827 theme_incognito_frame,
828 GetDefaultTint(ThemeService::TINT_FRAME_INCOGNITO),
829 ThemeService::COLOR_FRAME_INCOGNITO,
830 ThemeService::TINT_FRAME_INCOGNITO);
831
832 BuildAndSetFrameColor(
833 &frame_color,
834 theme_incognito_inactive_frame,
835 GetDefaultTint(ThemeService::TINT_FRAME_INCOGNITO_INACTIVE),
836 ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE,
837 ThemeService::TINT_FRAME_INCOGNITO_INACTIVE);
838
839 return frame_color;
840 }
841
LoadDefaultValues()842 void GtkThemeService::LoadDefaultValues() {
843 focus_ring_color_ = SkColorSetARGB(255, 229, 151, 0);
844 thumb_active_color_ = SkColorSetRGB(244, 244, 244);
845 thumb_inactive_color_ = SkColorSetRGB(234, 234, 234);
846 track_color_ = SkColorSetRGB(211, 211, 211);
847
848 active_selection_bg_color_ = SkColorSetRGB(30, 144, 255);
849 active_selection_fg_color_ = SK_ColorWHITE;
850 inactive_selection_bg_color_ = SkColorSetRGB(200, 200, 200);
851 inactive_selection_fg_color_ = SkColorSetRGB(50, 50, 50);
852 }
853
RebuildMenuIconSets()854 void GtkThemeService::RebuildMenuIconSets() {
855 FreeIconSets();
856
857 GtkStyle* style = gtk_rc_get_style(fake_menu_item_.get());
858
859 fullscreen_icon_set_ = gtk_icon_set_new();
860 BuildIconFromIDRWithColor(IDR_FULLSCREEN_MENU_BUTTON,
861 style,
862 GTK_STATE_PRELIGHT,
863 fullscreen_icon_set_);
864 BuildIconFromIDRWithColor(IDR_FULLSCREEN_MENU_BUTTON,
865 style,
866 GTK_STATE_NORMAL,
867 fullscreen_icon_set_);
868 }
869
SetThemeColorFromGtk(int id,const GdkColor * color)870 void GtkThemeService::SetThemeColorFromGtk(int id, const GdkColor* color) {
871 colors_[id] = GdkToSkColor(color);
872 }
873
SetThemeTintFromGtk(int id,const GdkColor * color)874 void GtkThemeService::SetThemeTintFromGtk(int id, const GdkColor* color) {
875 color_utils::HSL default_tint = GetDefaultTint(id);
876 color_utils::HSL hsl;
877 color_utils::SkColorToHSL(GdkToSkColor(color), &hsl);
878
879 if (default_tint.s != -1)
880 hsl.s = default_tint.s;
881
882 if (default_tint.l != -1)
883 hsl.l = default_tint.l;
884
885 tints_[id] = hsl;
886 }
887
BuildAndSetFrameColor(const GdkColor * base,const GdkColor * gtk_base,const color_utils::HSL & tint,int color_id,int tint_id)888 GdkColor GtkThemeService::BuildAndSetFrameColor(const GdkColor* base,
889 const GdkColor* gtk_base,
890 const color_utils::HSL& tint,
891 int color_id,
892 int tint_id) {
893 GdkColor out_color = *base;
894 if (gtk_base) {
895 // The theme author specified a color to use, use it without modification.
896 out_color = *gtk_base;
897 } else {
898 // Tint the basic color since this is a heuristic color instead of one
899 // specified by the theme author.
900 GdkColorHSLShift(tint, &out_color);
901 }
902 SetThemeColorFromGtk(color_id, &out_color);
903 SetThemeTintFromGtk(tint_id, &out_color);
904
905 return out_color;
906 }
907
FreePerDisplaySurfaces(PerDisplaySurfaceMap * per_display_map)908 void GtkThemeService::FreePerDisplaySurfaces(
909 PerDisplaySurfaceMap* per_display_map) {
910 for (PerDisplaySurfaceMap::iterator it = per_display_map->begin();
911 it != per_display_map->end(); ++it) {
912 for (CairoCachedSurfaceMap::iterator jt = it->second.begin();
913 jt != it->second.end(); ++jt) {
914 delete jt->second;
915 }
916 }
917 per_display_map->clear();
918 }
919
FreeIconSets()920 void GtkThemeService::FreeIconSets() {
921 if (fullscreen_icon_set_) {
922 gtk_icon_set_unref(fullscreen_icon_set_);
923 fullscreen_icon_set_ = NULL;
924 }
925 }
926
GenerateGtkThemeBitmap(int id) const927 SkBitmap* GtkThemeService::GenerateGtkThemeBitmap(int id) const {
928 switch (id) {
929 case IDR_THEME_TOOLBAR: {
930 GtkStyle* style = gtk_rc_get_style(fake_window_);
931 GdkColor* color = &style->bg[GTK_STATE_NORMAL];
932 SkBitmap* bitmap = new SkBitmap;
933 bitmap->setConfig(SkBitmap::kARGB_8888_Config,
934 kToolbarImageWidth, kToolbarImageHeight);
935 bitmap->allocPixels();
936 bitmap->eraseRGB(color->red >> 8, color->green >> 8, color->blue >> 8);
937 return bitmap;
938 }
939 case IDR_THEME_TAB_BACKGROUND:
940 return GenerateTabImage(IDR_THEME_FRAME);
941 case IDR_THEME_TAB_BACKGROUND_INCOGNITO:
942 return GenerateTabImage(IDR_THEME_FRAME_INCOGNITO);
943 case IDR_THEME_FRAME:
944 return GenerateFrameImage(ThemeService::COLOR_FRAME,
945 "frame-gradient-color");
946 case IDR_THEME_FRAME_INACTIVE:
947 return GenerateFrameImage(ThemeService::COLOR_FRAME_INACTIVE,
948 "inactive-frame-gradient-color");
949 case IDR_THEME_FRAME_INCOGNITO:
950 return GenerateFrameImage(ThemeService::COLOR_FRAME_INCOGNITO,
951 "incognito-frame-gradient-color");
952 case IDR_THEME_FRAME_INCOGNITO_INACTIVE: {
953 return GenerateFrameImage(
954 ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE,
955 "incognito-inactive-frame-gradient-color");
956 }
957 // Icons that sit inside the omnibox shouldn't receive TINT_BUTTONS and
958 // instead should tint based on the foreground text entry color in GTK+
959 // mode because some themes that try to be dark *and* light have very
960 // different colors between the omnibox and the normal background area.
961 case IDR_OMNIBOX_EXTENSION_APP:
962 case IDR_OMNIBOX_HISTORY:
963 case IDR_OMNIBOX_HTTP:
964 case IDR_OMNIBOX_SEARCH:
965 case IDR_OMNIBOX_STAR:
966 case IDR_GEOLOCATION_ALLOWED_LOCATIONBAR_ICON:
967 case IDR_GEOLOCATION_DENIED_LOCATIONBAR_ICON: {
968 return GenerateTintedIcon(id, entry_tint_);
969 }
970 // In GTK mode, the dark versions of the omnibox icons only ever appear in
971 // the autocomplete popup and only against the current theme's GtkEntry
972 // base[GTK_STATE_SELECTED] color, so tint the icons so they won't collide
973 // with the selected color.
974 case IDR_OMNIBOX_EXTENSION_APP_DARK:
975 case IDR_OMNIBOX_HISTORY_DARK:
976 case IDR_OMNIBOX_HTTP_DARK:
977 case IDR_OMNIBOX_SEARCH_DARK:
978 case IDR_OMNIBOX_STAR_DARK: {
979 return GenerateTintedIcon(id, selected_entry_tint_);
980 }
981 default: {
982 return GenerateTintedIcon(id, button_tint_);
983 }
984 }
985 }
986
GenerateFrameImage(int color_id,const char * gradient_name) const987 SkBitmap* GtkThemeService::GenerateFrameImage(
988 int color_id,
989 const char* gradient_name) const {
990 // We use two colors: the main color (passed in) and a lightened version of
991 // that color (which is supposed to match the light gradient at the top of
992 // several GTK+ themes, such as Ambiance, Clearlooks or Bluebird).
993 ColorMap::const_iterator it = colors_.find(color_id);
994 DCHECK(it != colors_.end());
995 SkColor base = it->second;
996
997 gfx::CanvasSkia canvas(kToolbarImageWidth, kToolbarImageHeight, true);
998
999 int gradient_size;
1000 const GdkColor* gradient_top_color = NULL;
1001 gtk_widget_style_get(GTK_WIDGET(fake_frame_),
1002 "frame-gradient-size", &gradient_size,
1003 gradient_name, &gradient_top_color,
1004 NULL);
1005 if (gradient_size) {
1006 SkColor lighter = gradient_top_color ? GdkToSkColor(gradient_top_color)
1007 : color_utils::HSLShift(base, kGtkFrameShift);
1008 SkShader* shader = gfx::CreateGradientShader(
1009 0, gradient_size, lighter, base);
1010 SkPaint paint;
1011 paint.setStyle(SkPaint::kFill_Style);
1012 paint.setAntiAlias(true);
1013 paint.setShader(shader);
1014 shader->unref();
1015
1016 canvas.DrawRectInt(0, 0, kToolbarImageWidth, gradient_size, paint);
1017 }
1018
1019 canvas.FillRectInt(base, 0, gradient_size,
1020 kToolbarImageWidth,
1021 kToolbarImageHeight - gradient_size);
1022 return new SkBitmap(canvas.ExtractBitmap());
1023 }
1024
GenerateTabImage(int base_id) const1025 SkBitmap* GtkThemeService::GenerateTabImage(int base_id) const {
1026 SkBitmap* base_image = GetBitmapNamed(base_id);
1027 SkBitmap bg_tint = SkBitmapOperations::CreateHSLShiftedBitmap(
1028 *base_image, GetTint(ThemeService::TINT_BACKGROUND_TAB));
1029 return new SkBitmap(SkBitmapOperations::CreateTiledBitmap(
1030 bg_tint, 0, 0, bg_tint.width(), bg_tint.height()));
1031 }
1032
GenerateTintedIcon(int base_id,const color_utils::HSL & tint) const1033 SkBitmap* GtkThemeService::GenerateTintedIcon(
1034 int base_id,
1035 const color_utils::HSL& tint) const {
1036 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1037 scoped_ptr<SkBitmap> button(new SkBitmap(*rb.GetBitmapNamed(base_id)));
1038 return new SkBitmap(SkBitmapOperations::CreateHSLShiftedBitmap(
1039 *button, tint));
1040 }
1041
GetNormalButtonTintHSL(color_utils::HSL * tint) const1042 void GtkThemeService::GetNormalButtonTintHSL(
1043 color_utils::HSL* tint) const {
1044 GtkStyle* window_style = gtk_rc_get_style(fake_window_);
1045 const GdkColor accent_gdk_color = window_style->bg[GTK_STATE_SELECTED];
1046 const GdkColor base_color = window_style->base[GTK_STATE_NORMAL];
1047
1048 GtkStyle* label_style = gtk_rc_get_style(fake_label_.get());
1049 const GdkColor text_color = label_style->fg[GTK_STATE_NORMAL];
1050
1051 PickButtonTintFromColors(accent_gdk_color, text_color, base_color, tint);
1052 }
1053
GetNormalEntryForegroundHSL(color_utils::HSL * tint) const1054 void GtkThemeService::GetNormalEntryForegroundHSL(
1055 color_utils::HSL* tint) const {
1056 GtkStyle* window_style = gtk_rc_get_style(fake_window_);
1057 const GdkColor accent_gdk_color = window_style->bg[GTK_STATE_SELECTED];
1058
1059 GtkStyle* style = gtk_rc_get_style(fake_entry_.get());
1060 const GdkColor text_color = style->text[GTK_STATE_NORMAL];
1061 const GdkColor base_color = style->base[GTK_STATE_NORMAL];
1062
1063 PickButtonTintFromColors(accent_gdk_color, text_color, base_color, tint);
1064 }
1065
GetSelectedEntryForegroundHSL(color_utils::HSL * tint) const1066 void GtkThemeService::GetSelectedEntryForegroundHSL(
1067 color_utils::HSL* tint) const {
1068 // The simplest of all the tints. We just use the selected text in the entry
1069 // since the icons tinted this way will only be displayed against
1070 // base[GTK_STATE_SELECTED].
1071 GtkStyle* style = gtk_rc_get_style(fake_entry_.get());
1072 const GdkColor color = style->text[GTK_STATE_SELECTED];
1073 color_utils::SkColorToHSL(GdkToSkColor(&color), tint);
1074 }
1075
GetSurfaceNamedImpl(int id,PerDisplaySurfaceMap * display_surface_map,GdkPixbuf * pixbuf,GtkWidget * widget_on_display)1076 CairoCachedSurface* GtkThemeService::GetSurfaceNamedImpl(
1077 int id,
1078 PerDisplaySurfaceMap* display_surface_map,
1079 GdkPixbuf* pixbuf,
1080 GtkWidget* widget_on_display) {
1081 GdkDisplay* display = gtk_widget_get_display(widget_on_display);
1082 CairoCachedSurfaceMap& surface_map = (*display_surface_map)[display];
1083
1084 // Check to see if we already have the pixbuf in the cache.
1085 CairoCachedSurfaceMap::const_iterator found = surface_map.find(id);
1086 if (found != surface_map.end())
1087 return found->second;
1088
1089 CairoCachedSurface* surface = new CairoCachedSurface;
1090 surface->UsePixbuf(pixbuf);
1091
1092 surface_map[id] = surface;
1093
1094 return surface;
1095 }
1096
OnDestroyChromeButton(GtkWidget * button)1097 void GtkThemeService::OnDestroyChromeButton(GtkWidget* button) {
1098 std::vector<GtkWidget*>::iterator it =
1099 find(chrome_buttons_.begin(), chrome_buttons_.end(), button);
1100 if (it != chrome_buttons_.end())
1101 chrome_buttons_.erase(it);
1102 }
1103
OnSeparatorExpose(GtkWidget * widget,GdkEventExpose * event)1104 gboolean GtkThemeService::OnSeparatorExpose(GtkWidget* widget,
1105 GdkEventExpose* event) {
1106 if (UseGtkTheme())
1107 return FALSE;
1108
1109 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
1110 gdk_cairo_rectangle(cr, &event->area);
1111 cairo_clip(cr);
1112
1113 GdkColor bottom_color = GetGdkColor(ThemeService::COLOR_TOOLBAR);
1114 double bottom_color_rgb[] = {
1115 static_cast<double>(bottom_color.red / 257) / 255.0,
1116 static_cast<double>(bottom_color.green / 257) / 255.0,
1117 static_cast<double>(bottom_color.blue / 257) / 255.0, };
1118
1119 cairo_pattern_t* pattern =
1120 cairo_pattern_create_linear(widget->allocation.x, widget->allocation.y,
1121 widget->allocation.x,
1122 widget->allocation.y +
1123 widget->allocation.height);
1124 cairo_pattern_add_color_stop_rgb(
1125 pattern, 0.0,
1126 kTopSeparatorColor[0], kTopSeparatorColor[1], kTopSeparatorColor[2]);
1127 cairo_pattern_add_color_stop_rgb(
1128 pattern, 0.5,
1129 kMidSeparatorColor[0], kMidSeparatorColor[1], kMidSeparatorColor[2]);
1130 cairo_pattern_add_color_stop_rgb(
1131 pattern, 1.0,
1132 bottom_color_rgb[0], bottom_color_rgb[1], bottom_color_rgb[2]);
1133 cairo_set_source(cr, pattern);
1134
1135 double start_x = 0.5 + widget->allocation.x;
1136 cairo_new_path(cr);
1137 cairo_set_line_width(cr, 1.0);
1138 cairo_move_to(cr, start_x, widget->allocation.y);
1139 cairo_line_to(cr, start_x,
1140 widget->allocation.y + widget->allocation.height);
1141 cairo_stroke(cr);
1142 cairo_destroy(cr);
1143 cairo_pattern_destroy(pattern);
1144
1145 return TRUE;
1146 }
1147