Home | History | Annotate | Download | only in themes
      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 
     15 NSString* const kBrowserThemeDidChangeNotification =
     16     @"BrowserThemeDidChangeNotification";
     17 
     18 namespace {
     19 
     20 void 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 
     32 NSImage* 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 
     75 NSColor* 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 
    100 NSColor* 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 
    136 NSColor* 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 
    179 NSGradient* 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.
    284 void ThemeService::NotifyPlatformThemeChanged() {
    285   NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
    286   [defaultCenter postNotificationName:kBrowserThemeDidChangeNotification
    287                                object:[NSValue valueWithPointer:this]];
    288 }
    289 
    290 void 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