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.
4
5#include "chrome/browser/ui/cocoa/background_gradient_view.h"
6
7#import "chrome/browser/themes/theme_properties.h"
8#import "chrome/browser/themes/theme_service.h"
9#import "chrome/browser/ui/cocoa/themed_window.h"
10#include "grit/theme_resources.h"
11#import "ui/base/cocoa/nsview_additions.h"
12
13@interface BackgroundGradientView (Private)
14- (void)commonInit;
15- (NSColor*)backgroundImageColor;
16@end
17
18@implementation BackgroundGradientView
19
20@synthesize showsDivider = showsDivider_;
21
22- (id)initWithFrame:(NSRect)frameRect {
23  if ((self = [super initWithFrame:frameRect])) {
24    [self commonInit];
25  }
26  return self;
27}
28
29- (id)initWithCoder:(NSCoder*)decoder {
30  if ((self = [super initWithCoder:decoder])) {
31    [self commonInit];
32  }
33  return self;
34}
35
36- (void)dealloc {
37  [[NSNotificationCenter defaultCenter] removeObserver:self];
38  [super dealloc];
39}
40
41- (void)commonInit {
42  showsDivider_ = YES;
43}
44
45- (void)setShowsDivider:(BOOL)show {
46  if (showsDivider_ == show)
47    return;
48  showsDivider_ = show;
49  [self setNeedsDisplay:YES];
50}
51
52- (void)drawBackgroundWithOpaque:(BOOL)opaque {
53  const NSRect bounds = [self bounds];
54
55  if (opaque) {
56    // If the background image is semi transparent then we need something
57    // to blend against. Using 20% black gives us a color similar to Windows.
58    [[NSColor colorWithCalibratedWhite:0.2 alpha:1.0] set];
59    NSRectFill(bounds);
60  }
61
62  [[self backgroundImageColor] set];
63  NSRectFillUsingOperation(bounds, NSCompositeSourceOver);
64
65  if (showsDivider_) {
66    // Draw bottom stroke
67    [[self strokeColor] set];
68    NSRect borderRect, contentRect;
69    NSDivideRect(bounds, &borderRect, &contentRect, [self cr_lineWidth],
70                 NSMinYEdge);
71    NSRectFillUsingOperation(borderRect, NSCompositeSourceOver);
72  }
73}
74
75- (NSColor*)strokeColor {
76  NSWindow* window = [self window];
77
78  // Some views have a child NSWindow between them and the window that is
79  // active (e.g, OmniboxPopupTopSeparatorView). For these, check the status
80  // of parentWindow instead. Note that this is not tracked correctly (but
81  // the views that do this appear to be removed when the window loses focus
82  // anyway).
83  if ([window parentWindow])
84    window = [window parentWindow];
85  BOOL isActive = [window isMainWindow];
86
87  ui::ThemeProvider* themeProvider = [window themeProvider];
88  if (!themeProvider)
89    return [NSColor blackColor];
90  return themeProvider->GetNSColor(
91      isActive ? ThemeProperties::COLOR_TOOLBAR_STROKE :
92                 ThemeProperties::COLOR_TOOLBAR_STROKE_INACTIVE);
93}
94
95- (NSColor*)backgroundImageColor {
96  ThemeService* themeProvider =
97      static_cast<ThemeService*>([[self window] themeProvider]);
98  if (!themeProvider)
99    return [[self window] backgroundColor];
100
101  // Themes don't have an inactive image so only look for one if there's no
102  // theme.
103  BOOL isActive = [[self window] isMainWindow];
104  if (!isActive && themeProvider->UsingDefaultTheme()) {
105    NSColor* color = themeProvider->GetNSImageColorNamed(
106        IDR_THEME_TOOLBAR_INACTIVE);
107    if (color)
108      return color;
109  }
110
111  return themeProvider->GetNSImageColorNamed(IDR_THEME_TOOLBAR);
112}
113
114- (void)windowFocusDidChange:(NSNotification*)notification {
115  // Some child views will indirectly use BackgroundGradientView by calling an
116  // ancestor's draw function (e.g, BookmarkButtonView). Call setNeedsDisplay
117  // on all descendants to ensure that these views re-draw.
118  // TODO(ccameron): Enable these views to listen for focus notifications
119  // directly.
120  [self cr_recursivelySetNeedsDisplay:YES];
121}
122
123- (void)viewWillMoveToWindow:(NSWindow*)window {
124  if ([self window]) {
125    [[NSNotificationCenter defaultCenter]
126        removeObserver:self
127                  name:NSWindowDidBecomeKeyNotification
128                object:[self window]];
129    [[NSNotificationCenter defaultCenter]
130        removeObserver:self
131                  name:NSWindowDidBecomeMainNotification
132                object:[self window]];
133  }
134  if (window) {
135    [[NSNotificationCenter defaultCenter]
136        addObserver:self
137           selector:@selector(windowFocusDidChange:)
138               name:NSWindowDidBecomeMainNotification
139             object:window];
140    [[NSNotificationCenter defaultCenter]
141        addObserver:self
142           selector:@selector(windowFocusDidChange:)
143               name:NSWindowDidResignMainNotification
144             object:window];
145    // The new window for the view may have a different focus state than the
146    // last window this view was part of. Force a re-draw to ensure that the
147    // view draws the right state.
148    [self windowFocusDidChange:nil];
149  }
150  [super viewWillMoveToWindow:window];
151}
152
153- (void)setFrameOrigin:(NSPoint)origin {
154  // The background color depends on the view's vertical position. This impacts
155  // any child views that draw using this view's functions.
156  if (NSMinY([self frame]) != origin.y)
157    [self cr_recursivelySetNeedsDisplay:YES];
158
159  [super setFrameOrigin:origin];
160}
161
162@end
163