• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/themes/theme_service.h"
6
7#import <Cocoa/Cocoa.h>
8
9#include "base/logging.h"
10#include "chrome/browser/themes/browser_theme_pack.h"
11#include "chrome/browser/themes/theme_properties.h"
12#include "skia/ext/skia_utils_mac.h"
13#import "third_party/google_toolbox_for_mac/src/AppKit/GTMNSColor+Luminance.h"
14#include "ui/base/resource/resource_bundle.h"
15#include "ui/gfx/color_utils.h"
16#include "ui/gfx/image/image.h"
17#include "skia/ext/skia_utils_mac.h"
18
19NSString* const kBrowserThemeDidChangeNotification =
20    @"BrowserThemeDidChangeNotification";
21
22typedef ThemeProperties Properties;
23
24namespace {
25
26void HSLToHSB(const color_utils::HSL& hsl, CGFloat* h, CGFloat* s, CGFloat* b) {
27  SkColor color = color_utils::HSLToSkColor(hsl, 255);  // alpha doesn't matter
28  SkScalar hsv[3];
29  SkColorToHSV(color, hsv);
30
31  *h = SkScalarToDouble(hsv[0]) / 360.0;
32  *s = SkScalarToDouble(hsv[1]);
33  *b = SkScalarToDouble(hsv[2]);
34}
35
36}  // namespace
37
38NSImage* ThemeService::GetNSImageNamed(int id) const {
39  DCHECK(CalledOnValidThread());
40
41  // Check to see if we already have the image in the cache.
42  NSImageMap::const_iterator nsimage_iter = nsimage_cache_.find(id);
43  if (nsimage_iter != nsimage_cache_.end())
44    return nsimage_iter->second;
45
46  // Why don't we load the file directly into the image instead of the whole
47  // gfx::Image > native conversion?
48  // - For consistency with other platforms.
49  // - To get the generated tinted images.
50  NSImage* nsimage = nil;
51  if (theme_supplier_.get()) {
52    gfx::Image image = theme_supplier_->GetImageNamed(id);
53    if (!image.IsEmpty())
54      nsimage = image.ToNSImage();
55  }
56
57  // If the theme didn't override this image then load it from the resource
58  // bundle.
59  if (!nsimage) {
60    nsimage = rb_.GetNativeImageNamed(id).ToNSImage();
61  }
62
63  // We loaded successfully.  Cache the image.
64  if (nsimage) {
65    nsimage_cache_[id] = [nsimage retain];
66    return nsimage;
67  }
68
69  // We failed to retrieve the bitmap, show a debugging red square.
70  LOG(WARNING) << "Unable to load NSImage with id " << id;
71  NOTREACHED();  // Want to assert in debug mode.
72
73  static NSImage* empty_image = NULL;
74  if (!empty_image) {
75    // The placeholder image is bright red so people notice the problem.  This
76    // image will be leaked, but this code should never be hit.
77    NSRect image_rect = NSMakeRect(0, 0, 32, 32);
78    empty_image = [[NSImage alloc] initWithSize:image_rect.size];
79    [empty_image lockFocus];
80    [[NSColor redColor] set];
81    NSRectFill(image_rect);
82    [empty_image unlockFocus];
83  }
84
85  return empty_image;
86}
87
88NSColor* ThemeService::GetNSImageColorNamed(int id) const {
89  DCHECK(CalledOnValidThread());
90
91  // Check to see if we already have the color in the cache.
92  NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
93  if (nscolor_iter != nscolor_cache_.end())
94    return nscolor_iter->second;
95
96  NSImage* image = GetNSImageNamed(id);
97  if (!image)
98    return nil;
99  NSColor* image_color = [NSColor colorWithPatternImage:image];
100
101  // We loaded successfully.  Cache the color.
102  if (image_color)
103    nscolor_cache_[id] = [image_color retain];
104
105  return image_color;
106}
107
108NSColor* ThemeService::GetNSColor(int id) const {
109  DCHECK(CalledOnValidThread());
110
111  // Check to see if we already have the color in the cache.
112  NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
113  if (nscolor_iter != nscolor_cache_.end())
114    return nscolor_iter->second;
115
116  SkColor sk_color = GetColor(id);
117  NSColor* color = gfx::SkColorToCalibratedNSColor(sk_color);
118
119  // We loaded successfully.  Cache the color.
120  if (color)
121    nscolor_cache_[id] = [color retain];
122
123  return color;
124}
125
126NSColor* ThemeService::GetNSColorTint(int id) const {
127  DCHECK(CalledOnValidThread());
128
129  // Check to see if we already have the color in the cache.
130  NSColorMap::const_iterator nscolor_iter = nscolor_cache_.find(id);
131  if (nscolor_iter != nscolor_cache_.end())
132    return nscolor_iter->second;
133
134  color_utils::HSL tint = GetTint(id);
135  NSColor* tint_color = nil;
136  if (tint.h == -1 && tint.s == -1 && tint.l == -1) {
137    tint_color = [NSColor blackColor];
138  } else {
139    CGFloat hue, saturation, brightness;
140    HSLToHSB(tint, &hue, &saturation, &brightness);
141
142    tint_color = [NSColor colorWithCalibratedHue:hue
143                                      saturation:saturation
144                                      brightness:brightness
145                                           alpha:1.0];
146  }
147
148  // We loaded successfully.  Cache the color.
149  if (tint_color)
150    nscolor_cache_[id] = [tint_color retain];
151
152  return tint_color;
153}
154
155NSGradient* ThemeService::GetNSGradient(int id) const {
156  DCHECK(CalledOnValidThread());
157
158  // Check to see if we already have the gradient in the cache.
159  NSGradientMap::const_iterator nsgradient_iter = nsgradient_cache_.find(id);
160  if (nsgradient_iter != nsgradient_cache_.end())
161    return nsgradient_iter->second;
162
163  NSGradient* gradient = nil;
164
165  // Note that we are not leaking when we assign a retained object to
166  // |gradient|; in all cases we cache it before we return.
167  switch (id) {
168    case Properties::GRADIENT_FRAME_INCOGNITO:
169    case Properties::GRADIENT_FRAME_INCOGNITO_INACTIVE: {
170      // TODO(avi): can we simplify this?
171      BOOL active = id == Properties::GRADIENT_FRAME_INCOGNITO;
172      NSColor* base_color = [NSColor colorWithCalibratedRed:83/255.0
173                                                      green:108.0/255.0
174                                                       blue:140/255.0
175                                                      alpha:1.0];
176
177      NSColor *start_color =
178          [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
179                                     faded:!active];
180      NSColor *end_color =
181          [base_color gtm_colorAdjustedFor:GTMColorationBaseShadow
182                                     faded:!active];
183
184      if (!active) {
185        start_color = [start_color gtm_colorByAdjustingLuminance:0.1
186                                                      saturation:0.5];
187        end_color = [end_color gtm_colorByAdjustingLuminance:0.1
188                                                  saturation:0.5];
189      }
190
191      gradient = [[NSGradient alloc] initWithStartingColor:start_color
192                                               endingColor:end_color];
193      break;
194    }
195
196    case Properties::GRADIENT_TOOLBAR:
197    case Properties::GRADIENT_TOOLBAR_INACTIVE: {
198      NSColor* base_color = [NSColor colorWithCalibratedWhite:0.2 alpha:1.0];
199      BOOL faded = (id == Properties::GRADIENT_TOOLBAR_INACTIVE ) ||
200                   (id == Properties::GRADIENT_TOOLBAR_BUTTON_INACTIVE);
201      NSColor* start_color =
202          [base_color gtm_colorAdjustedFor:GTMColorationLightHighlight
203                                     faded:faded];
204      NSColor* mid_color =
205          [base_color gtm_colorAdjustedFor:GTMColorationLightMidtone
206                                     faded:faded];
207      NSColor* end_color =
208          [base_color gtm_colorAdjustedFor:GTMColorationLightShadow
209                                     faded:faded];
210      NSColor* glow_color =
211          [base_color gtm_colorAdjustedFor:GTMColorationLightPenumbra
212                                     faded:faded];
213
214      gradient =
215          [[NSGradient alloc] initWithColorsAndLocations:start_color, 0.0,
216                                                         mid_color, 0.25,
217                                                         end_color, 0.5,
218                                                         glow_color, 0.75,
219                                                         nil];
220      break;
221    }
222
223    case Properties::GRADIENT_TOOLBAR_BUTTON:
224    case Properties::GRADIENT_TOOLBAR_BUTTON_INACTIVE: {
225      NSColor* start_color = [NSColor colorWithCalibratedWhite:1.0 alpha:0.0];
226      NSColor* end_color = [NSColor colorWithCalibratedWhite:1.0 alpha:0.3];
227      gradient = [[NSGradient alloc] initWithStartingColor:start_color
228                                               endingColor:end_color];
229      break;
230    }
231    case Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED:
232    case Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED_INACTIVE: {
233      NSColor* base_color = [NSColor colorWithCalibratedWhite:0.5 alpha:1.0];
234      BOOL faded = id == Properties::GRADIENT_TOOLBAR_BUTTON_PRESSED_INACTIVE;
235      NSColor* start_color =
236          [base_color gtm_colorAdjustedFor:GTMColorationBaseShadow
237                                     faded:faded];
238      NSColor* end_color =
239          [base_color gtm_colorAdjustedFor:GTMColorationBaseMidtone
240                                     faded:faded];
241
242      gradient = [[NSGradient alloc] initWithStartingColor:start_color
243                                               endingColor:end_color];
244      break;
245    }
246    default:
247      LOG(WARNING) << "Gradient request with unknown id " << id;
248      NOTREACHED();  // Want to assert in debug mode.
249      break;
250  }
251
252  // We loaded successfully.  Cache the gradient.
253  if (gradient)
254    nsgradient_cache_[id] = gradient;  // created retained
255
256  return gradient;
257}
258
259// Let all the browser views know that themes have changed in a platform way.
260void ThemeService::NotifyPlatformThemeChanged() {
261  NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
262  [defaultCenter postNotificationName:kBrowserThemeDidChangeNotification
263                               object:[NSValue valueWithPointer:this]];
264}
265
266void ThemeService::FreePlatformCaches() {
267  DCHECK(CalledOnValidThread());
268
269  // Free images.
270  for (NSImageMap::iterator i = nsimage_cache_.begin();
271       i != nsimage_cache_.end(); i++) {
272    [i->second release];
273  }
274  nsimage_cache_.clear();
275
276  // Free colors.
277  for (NSColorMap::iterator i = nscolor_cache_.begin();
278       i != nscolor_cache_.end(); i++) {
279    [i->second release];
280  }
281  nscolor_cache_.clear();
282
283  // Free gradients.
284  for (NSGradientMap::iterator i = nsgradient_cache_.begin();
285       i != nsgradient_cache_.end(); i++) {
286    [i->second release];
287  }
288  nsgradient_cache_.clear();
289}
290