1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "WebThemeEngineDRTMac.h"
32
33#include "third_party/WebKit/Source/WebKit/chromium/public/WebCanvas.h"
34#include "third_party/WebKit/Source/WebKit/chromium/public/WebRect.h"
35#import <AppKit/NSAffineTransform.h>
36#import <AppKit/NSGraphicsContext.h>
37#import <AppKit/NSScroller.h>
38#import <AppKit/NSWindow.h>
39#include <Carbon/Carbon.h>
40
41using WebKit::WebCanvas;
42using WebKit::WebRect;
43using WebKit::WebThemeEngine;
44
45// We can't directly tell the NSScroller to draw itself as active or inactive,
46// instead we have to make it a child of an (in)active window. This class lets
47// us fake that parent window.
48@interface FakeActiveWindow : NSWindow {
49@private
50    BOOL hasActiveControls;
51}
52+ (NSWindow*)alwaysActiveWindow;
53+ (NSWindow*)alwaysInactiveWindow;
54- (id)initWithActiveControls:(BOOL)_hasActiveControls;
55- (BOOL)_hasActiveControls;
56@end
57
58@implementation FakeActiveWindow
59
60static NSWindow* alwaysActiveWindow = nil;
61static NSWindow* alwaysInactiveWindow = nil;
62
63+ (NSWindow*)alwaysActiveWindow
64{
65    if (alwaysActiveWindow == nil)
66        alwaysActiveWindow = [[self alloc] initWithActiveControls:YES];
67    return alwaysActiveWindow;
68}
69
70+ (NSWindow*)alwaysInactiveWindow
71{
72    if (alwaysInactiveWindow == nil)
73        alwaysInactiveWindow = [[self alloc] initWithActiveControls:NO];
74    return alwaysInactiveWindow;
75}
76
77- (id)initWithActiveControls:(BOOL)_hasActiveControls
78{
79    self = [super init];
80    hasActiveControls = _hasActiveControls;
81    return self;
82}
83
84- (BOOL)_hasActiveControls
85{
86    return hasActiveControls;
87}
88
89@end
90
91void WebThemeEngineDRTMac::paintScrollbarThumb(
92    WebCanvas* canvas,
93    WebThemeEngine::State state,
94    WebThemeEngine::Size size,
95    const WebRect& rect,
96    const WebThemeEngine::ScrollbarInfo& scrollbarInfo)
97{
98    // To match the Mac port, we still use HITheme for inner scrollbars.
99    if (scrollbarInfo.parent == WebThemeEngine::ScrollbarParentRenderLayer)
100        paintHIThemeScrollbarThumb(canvas, state, size, rect, scrollbarInfo);
101    else
102        paintNSScrollerScrollbarThumb(canvas, state, size, rect, scrollbarInfo);
103}
104
105static ThemeTrackEnableState stateToHIEnableState(WebThemeEngine::State state)
106{
107    switch (state) {
108    case WebThemeEngine::StateDisabled:
109        return kThemeTrackDisabled;
110    case WebThemeEngine::StateInactive:
111        return kThemeTrackInactive;
112    default:
113        return kThemeTrackActive;
114    }
115}
116
117// Duplicated from webkit/glue/webthemeengine_impl_mac.cc in the downstream
118// Chromium WebThemeEngine implementation.
119void WebThemeEngineDRTMac::paintHIThemeScrollbarThumb(
120    WebCanvas* canvas,
121    WebThemeEngine::State state,
122    WebThemeEngine::Size size,
123    const WebRect& rect,
124    const WebThemeEngine::ScrollbarInfo& scrollbarInfo)
125{
126    HIThemeTrackDrawInfo trackInfo;
127    trackInfo.version = 0;
128    trackInfo.kind = size == WebThemeEngine::SizeRegular ? kThemeMediumScrollBar : kThemeSmallScrollBar;
129    trackInfo.bounds = CGRectMake(rect.x, rect.y, rect.width, rect.height);
130    trackInfo.min = 0;
131    trackInfo.max = scrollbarInfo.maxValue;
132    trackInfo.value = scrollbarInfo.currentValue;
133    trackInfo.trackInfo.scrollbar.viewsize = scrollbarInfo.visibleSize;
134    trackInfo.attributes = 0;
135    if (scrollbarInfo.orientation == WebThemeEngine::ScrollbarOrientationHorizontal)
136        trackInfo.attributes |= kThemeTrackHorizontal;
137
138    trackInfo.enableState = stateToHIEnableState(state);
139
140    trackInfo.trackInfo.scrollbar.pressState =
141        state == WebThemeEngine::StatePressed ? kThemeThumbPressed : 0;
142    trackInfo.attributes |= (kThemeTrackShowThumb | kThemeTrackHideTrack);
143    HIThemeDrawTrack(&trackInfo, 0, canvas, kHIThemeOrientationNormal);
144}
145
146void WebThemeEngineDRTMac::paintNSScrollerScrollbarThumb(
147    WebCanvas* canvas,
148    WebThemeEngine::State state,
149    WebThemeEngine::Size size,
150    const WebRect& rect,
151    const WebThemeEngine::ScrollbarInfo& scrollbarInfo)
152{
153    NSScroller* scroller = [[NSScroller alloc] initWithFrame:NSMakeRect(rect.x, rect.y, rect.width, rect.height)];
154    [scroller setEnabled:state != WebThemeEngine::StateDisabled];
155    if (state == WebThemeEngine::StateInactive)
156        [[[FakeActiveWindow alwaysInactiveWindow] contentView] addSubview:scroller];
157    else
158        [[[FakeActiveWindow alwaysActiveWindow] contentView] addSubview:scroller];
159
160    [scroller setControlSize:size == WebThemeEngine::SizeRegular ? NSRegularControlSize : NSSmallControlSize];
161
162    double value = double(scrollbarInfo.currentValue) / double(scrollbarInfo.maxValue);
163    [scroller setDoubleValue: value];
164
165    float knobProportion = float(scrollbarInfo.visibleSize) / float(scrollbarInfo.totalSize);
166    [scroller setKnobProportion: knobProportion];
167
168    NSGraphicsContext* previousGraphicsContext = [NSGraphicsContext currentContext];
169    NSGraphicsContext* nsGraphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:canvas flipped:YES];
170    [NSGraphicsContext setCurrentContext:nsGraphicsContext];
171
172    // Despite passing in frameRect() to the scroller, it always draws at (0, 0).
173    // Force it to draw in the right location by translating the whole graphics
174    // context.
175    [nsGraphicsContext saveGraphicsState];
176    NSAffineTransform *transform = [NSAffineTransform transform];
177    [transform translateXBy:rect.x yBy:rect.y];
178    [transform concat];
179
180    [scroller drawKnob];
181
182    [scroller release];
183
184    [nsGraphicsContext restoreGraphicsState];
185    [NSGraphicsContext setCurrentContext:previousGraphicsContext];
186}
187