1/*
2 * Copyright (C) 2008 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ScrollbarThemeSafari.h"
28
29#if USE(SAFARI_THEME)
30
31#include "GraphicsContext.h"
32#include "IntRect.h"
33#include "Page.h"
34#include "PlatformMouseEvent.h"
35#include "ScrollableArea.h"
36#include "Scrollbar.h"
37#include "ScrollbarThemeWin.h"
38#include "Settings.h"
39#include "SoftLinking.h"
40
41#include <CoreGraphics/CoreGraphics.h>
42
43// If you have an empty placeholder SafariThemeConstants.h, then include SafariTheme.h
44// This is a workaround until a version of WebKitSupportLibrary is released with an updated SafariThemeConstants.h
45#include <SafariTheme/SafariThemeConstants.h>
46#ifndef SafariThemeConstants_h
47#include <SafariTheme/SafariTheme.h>
48#endif
49
50// FIXME: There are repainting problems due to Aqua scroll bar buttons' visual overflow.
51
52using namespace std;
53
54namespace WebCore {
55
56using namespace SafariTheme;
57
58ScrollbarTheme* ScrollbarTheme::nativeTheme()
59{
60    static ScrollbarThemeSafari safariTheme;
61    static ScrollbarThemeWin windowsTheme;
62    if (Settings::shouldPaintNativeControls())
63        return &windowsTheme;
64    return &safariTheme;
65}
66
67// FIXME: Get these numbers from CoreUI.
68static int cScrollbarThickness[] = { 15, 11 };
69static int cRealButtonLength[] = { 28, 21 };
70static int cButtonInset[] = { 14, 11 };
71static int cButtonHitInset[] = { 3, 2 };
72// cRealButtonLength - cButtonInset
73static int cButtonLength[] = { 14, 10 };
74static int cThumbMinLength[] = { 26, 20 };
75
76#ifdef DEBUG_ALL
77SOFT_LINK_DEBUG_LIBRARY(SafariTheme)
78#else
79SOFT_LINK_LIBRARY(SafariTheme)
80#endif
81
82SOFT_LINK(SafariTheme, paintThemePart, void, __stdcall,
83            (ThemePart part, CGContextRef context, const CGRect& rect, NSControlSize size, ThemeControlState state),
84            (part, context, rect, size, state))
85
86static ScrollbarControlState scrollbarControlStateFromThemeState(ThemeControlState state)
87{
88    ScrollbarControlState s = 0;
89    if (state & ActiveState)
90        s |= ActiveScrollbarState;
91    if (state & EnabledState)
92        s |= EnabledScrollbarState;
93    if (state & PressedState)
94        s |= PressedScrollbarState;
95    return s;
96}
97
98ScrollbarThemeSafari::~ScrollbarThemeSafari()
99{
100}
101
102int ScrollbarThemeSafari::scrollbarThickness(ScrollbarControlSize controlSize)
103{
104    return cScrollbarThickness[controlSize];
105}
106
107bool ScrollbarThemeSafari::hasButtons(ScrollbarThemeClient* scrollbar)
108{
109    return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
110             scrollbar->width() :
111             scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]);
112}
113
114bool ScrollbarThemeSafari::hasThumb(ScrollbarThemeClient* scrollbar)
115{
116    return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
117             scrollbar->width() :
118             scrollbar->height()) >= 2 * cButtonInset[scrollbar->controlSize()] + cThumbMinLength[scrollbar->controlSize()] + 1;
119}
120
121static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start)
122{
123    IntRect paintRect(buttonRect);
124    if (orientation == HorizontalScrollbar) {
125        paintRect.setWidth(cRealButtonLength[controlSize]);
126        if (!start)
127            paintRect.setX(buttonRect.x() - (cRealButtonLength[controlSize] - buttonRect.width()));
128    } else {
129        paintRect.setHeight(cRealButtonLength[controlSize]);
130        if (!start)
131            paintRect.setY(buttonRect.y() - (cRealButtonLength[controlSize] - buttonRect.height()));
132    }
133
134    return paintRect;
135}
136
137IntRect ScrollbarThemeSafari::backButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool painting)
138{
139    IntRect result;
140
141    // Windows just has single arrows.
142    if (part == BackButtonEndPart)
143        return result;
144
145    int thickness = scrollbarThickness(scrollbar->controlSize());
146    if (scrollbar->orientation() == HorizontalScrollbar)
147        result = IntRect(scrollbar->x(), scrollbar->y(), cButtonLength[scrollbar->controlSize()], thickness);
148    else
149        result = IntRect(scrollbar->x(), scrollbar->y(), thickness, cButtonLength[scrollbar->controlSize()]);
150    if (painting)
151        return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), true);
152    return result;
153}
154
155IntRect ScrollbarThemeSafari::forwardButtonRect(ScrollbarThemeClient* scrollbar, ScrollbarPart part, bool painting)
156{
157    IntRect result;
158
159    // Windows just has single arrows.
160    if (part == ForwardButtonStartPart)
161        return result;
162
163    int thickness = scrollbarThickness(scrollbar->controlSize());
164    if (scrollbar->orientation() == HorizontalScrollbar)
165        result = IntRect(scrollbar->x() + scrollbar->width() - cButtonLength[scrollbar->controlSize()], scrollbar->y(), cButtonLength[scrollbar->controlSize()], thickness);
166    else
167        result = IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - cButtonLength[scrollbar->controlSize()], thickness, cButtonLength[scrollbar->controlSize()]);
168    if (painting)
169        return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), false);
170    return result;
171}
172
173static IntRect trackRepaintRect(const IntRect& trackRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
174{
175    IntRect paintRect(trackRect);
176    if (orientation == HorizontalScrollbar)
177        paintRect.inflateX(cButtonLength[controlSize]);
178    else
179        paintRect.inflateY(cButtonLength[controlSize]);
180
181    return paintRect;
182}
183
184IntRect ScrollbarThemeSafari::trackRect(ScrollbarThemeClient* scrollbar, bool painting)
185{
186    if (painting || !hasButtons(scrollbar))
187        return scrollbar->frameRect();
188
189    IntRect result;
190    int thickness = scrollbarThickness(scrollbar->controlSize());
191    if (scrollbar->orientation() == HorizontalScrollbar)
192        return IntRect(scrollbar->x() + cButtonLength[scrollbar->controlSize()], scrollbar->y(), scrollbar->width() - 2 * cButtonLength[scrollbar->controlSize()], thickness);
193    return IntRect(scrollbar->x(), scrollbar->y() + cButtonLength[scrollbar->controlSize()], thickness, scrollbar->height() - 2 * cButtonLength[scrollbar->controlSize()]);
194}
195
196int ScrollbarThemeSafari::minimumThumbLength(ScrollbarThemeClient* scrollbar)
197{
198    return cThumbMinLength[scrollbar->controlSize()];
199}
200
201bool ScrollbarThemeSafari::shouldCenterOnThumb(ScrollbarThemeClient*, const PlatformMouseEvent& evt)
202{
203    return evt.shiftKey() && evt.button() == LeftButton;
204}
205
206void ScrollbarThemeSafari::paintTrackBackground(GraphicsContext* graphicsContext, ScrollbarThemeClient* scrollbar, const IntRect& trackRect)
207{
208    if (!SafariThemeLibrary())
209        return;
210    NSControlSize size = scrollbar->controlSize() == SmallScrollbar ? NSSmallControlSize : NSRegularControlSize;
211    ThemeControlState state = 0;
212    if (scrollbar->isScrollableAreaActive())
213        state |= ActiveState;
214    if (hasButtons(scrollbar))
215        state |= EnabledState;
216    paintThemePart(scrollbar->orientation() == VerticalScrollbar ? VScrollTrackPart : HScrollTrackPart, graphicsContext->platformContext(), trackRect, size, state);
217}
218
219void ScrollbarThemeSafari::paintButton(GraphicsContext* graphicsContext, ScrollbarThemeClient* scrollbar, const IntRect& buttonRect, ScrollbarPart part)
220{
221    if (!SafariThemeLibrary())
222        return;
223    NSControlSize size = scrollbar->controlSize() == SmallScrollbar ? NSSmallControlSize : NSRegularControlSize;
224    ThemeControlState state = 0;
225    if (scrollbar->isScrollableAreaActive())
226        state |= ActiveState;
227    if (hasButtons(scrollbar))
228        state |= EnabledState;
229    if (scrollbar->pressedPart() == part)
230        state |= PressedState;
231    if (part == BackButtonStartPart)
232        paintThemePart(scrollbar->orientation() == VerticalScrollbar ? ScrollUpArrowPart : ScrollLeftArrowPart, graphicsContext->platformContext(),
233                       buttonRect, size, state);
234    else if (part == ForwardButtonEndPart)
235        paintThemePart(scrollbar->orientation() == VerticalScrollbar ? ScrollDownArrowPart : ScrollRightArrowPart, graphicsContext->platformContext(),
236                       buttonRect, size, state);
237}
238
239void ScrollbarThemeSafari::paintThumb(GraphicsContext* graphicsContext, ScrollbarThemeClient* scrollbar, const IntRect& thumbRect)
240{
241    if (!SafariThemeLibrary())
242        return;
243    NSControlSize size = scrollbar->controlSize() == SmallScrollbar ? NSSmallControlSize : NSRegularControlSize;
244    ThemeControlState state = 0;
245    if (scrollbar->isScrollableAreaActive())
246        state |= ActiveState;
247    if (hasThumb(scrollbar))
248        state |= EnabledState;
249    if (scrollbar->pressedPart() == ThumbPart)
250        state |= PressedState;
251    paintThemePart(scrollbar->orientation() == VerticalScrollbar ? VScrollThumbPart : HScrollThumbPart, graphicsContext->platformContext(),
252                   thumbRect, size, state);
253}
254
255}
256
257#endif
258