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