tizen beta release
[framework/web/webkit-efl.git] / Source / WebCore / platform / ScrollbarThemeComposite.cpp
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 "ScrollbarThemeComposite.h"
28
29 #include "Chrome.h"
30 #include "ChromeClient.h"
31 #include "Frame.h"
32 #include "FrameView.h"
33 #include "GraphicsContext.h"
34 #include "Page.h"
35 #include "PlatformMouseEvent.h"
36 #include "Scrollbar.h"
37 #include "ScrollableArea.h"
38 #include "Settings.h"
39
40 using namespace std;
41
42 namespace WebCore {
43
44 bool ScrollbarThemeComposite::paint(Scrollbar* scrollbar, GraphicsContext* graphicsContext, const IntRect& damageRect)
45 {
46     // Create the ScrollbarControlPartMask based on the damageRect
47     ScrollbarControlPartMask scrollMask = NoPart;
48
49     IntRect backButtonStartPaintRect;
50     IntRect backButtonEndPaintRect;
51     IntRect forwardButtonStartPaintRect;
52     IntRect forwardButtonEndPaintRect;
53     if (hasButtons(scrollbar)) {
54         backButtonStartPaintRect = backButtonRect(scrollbar, BackButtonStartPart, true);
55         if (damageRect.intersects(backButtonStartPaintRect))
56             scrollMask |= BackButtonStartPart;
57         backButtonEndPaintRect = backButtonRect(scrollbar, BackButtonEndPart, true);
58         if (damageRect.intersects(backButtonEndPaintRect))
59             scrollMask |= BackButtonEndPart;
60         forwardButtonStartPaintRect = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
61         if (damageRect.intersects(forwardButtonStartPaintRect))
62             scrollMask |= ForwardButtonStartPart;
63         forwardButtonEndPaintRect = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
64         if (damageRect.intersects(forwardButtonEndPaintRect))
65             scrollMask |= ForwardButtonEndPart;
66     }
67
68     IntRect startTrackRect;
69     IntRect thumbRect;
70     IntRect endTrackRect;
71     IntRect trackPaintRect = trackRect(scrollbar, true);
72     if (damageRect.intersects(trackPaintRect))
73         scrollMask |= TrackBGPart;
74     bool thumbPresent = hasThumb(scrollbar);
75     if (thumbPresent) {
76         IntRect track = trackRect(scrollbar);
77         splitTrack(scrollbar, track, startTrackRect, thumbRect, endTrackRect);
78         if (damageRect.intersects(thumbRect))
79             scrollMask |= ThumbPart;
80         if (damageRect.intersects(startTrackRect))
81             scrollMask |= BackTrackPart;
82         if (damageRect.intersects(endTrackRect))
83             scrollMask |= ForwardTrackPart;
84     }
85
86     // Paint the scrollbar background (only used by custom CSS scrollbars).
87     paintScrollbarBackground(graphicsContext, scrollbar);
88
89     // Paint the back and forward buttons.
90     if (scrollMask & BackButtonStartPart)
91         paintButton(graphicsContext, scrollbar, backButtonStartPaintRect, BackButtonStartPart);
92     if (scrollMask & BackButtonEndPart)
93         paintButton(graphicsContext, scrollbar, backButtonEndPaintRect, BackButtonEndPart);
94     if (scrollMask & ForwardButtonStartPart)
95         paintButton(graphicsContext, scrollbar, forwardButtonStartPaintRect, ForwardButtonStartPart);
96     if (scrollMask & ForwardButtonEndPart)
97         paintButton(graphicsContext, scrollbar, forwardButtonEndPaintRect, ForwardButtonEndPart);
98     
99     if (scrollMask & TrackBGPart)
100         paintTrackBackground(graphicsContext, scrollbar, trackPaintRect);
101     
102     if ((scrollMask & ForwardTrackPart) || (scrollMask & BackTrackPart)) {
103         // Paint the track pieces above and below the thumb.
104         if (scrollMask & BackTrackPart)
105             paintTrackPiece(graphicsContext, scrollbar, startTrackRect, BackTrackPart);
106         if (scrollMask & ForwardTrackPart)
107             paintTrackPiece(graphicsContext, scrollbar, endTrackRect, ForwardTrackPart);
108
109         paintTickmarks(graphicsContext, scrollbar, trackPaintRect);
110     }
111
112     // Paint the thumb.
113     if (scrollMask & ThumbPart)
114         paintThumb(graphicsContext, scrollbar, thumbRect);
115
116     return true;
117 }
118
119 ScrollbarPart ScrollbarThemeComposite::hitTest(Scrollbar* scrollbar, const PlatformMouseEvent& evt)
120 {
121     ScrollbarPart result = NoPart;
122     if (!scrollbar->enabled())
123         return result;
124
125     IntPoint mousePosition = scrollbar->convertFromContainingWindow(evt.pos());
126     mousePosition.move(scrollbar->x(), scrollbar->y());
127     
128     if (!scrollbar->frameRect().contains(mousePosition))
129         return NoPart;
130
131     result = ScrollbarBGPart;
132
133     IntRect track = trackRect(scrollbar);
134     if (track.contains(mousePosition)) {
135         IntRect beforeThumbRect;
136         IntRect thumbRect;
137         IntRect afterThumbRect;
138         splitTrack(scrollbar, track, beforeThumbRect, thumbRect, afterThumbRect);
139         if (thumbRect.contains(mousePosition))
140             result = ThumbPart;
141         else if (beforeThumbRect.contains(mousePosition))
142             result = BackTrackPart;
143         else if (afterThumbRect.contains(mousePosition))
144             result = ForwardTrackPart;
145         else
146             result = TrackBGPart;
147     } else if (backButtonRect(scrollbar, BackButtonStartPart).contains(mousePosition))
148         result = BackButtonStartPart;
149     else if (backButtonRect(scrollbar, BackButtonEndPart).contains(mousePosition))
150         result = BackButtonEndPart;
151     else if (forwardButtonRect(scrollbar, ForwardButtonStartPart).contains(mousePosition))
152         result = ForwardButtonStartPart;
153     else if (forwardButtonRect(scrollbar, ForwardButtonEndPart).contains(mousePosition))
154         result = ForwardButtonEndPart;
155     return result;
156 }
157
158 void ScrollbarThemeComposite::invalidatePart(Scrollbar* scrollbar, ScrollbarPart part)
159 {
160     if (part == NoPart)
161         return;
162
163     IntRect result;    
164     switch (part) {
165         case BackButtonStartPart:
166             result = backButtonRect(scrollbar, BackButtonStartPart, true);
167             break;
168         case BackButtonEndPart:
169             result = backButtonRect(scrollbar, BackButtonEndPart, true);
170             break;
171         case ForwardButtonStartPart:
172             result = forwardButtonRect(scrollbar, ForwardButtonStartPart, true);
173             break;
174         case ForwardButtonEndPart:
175             result = forwardButtonRect(scrollbar, ForwardButtonEndPart, true);
176             break;
177         case TrackBGPart:
178             result = trackRect(scrollbar, true);
179             break;
180         case ScrollbarBGPart:
181             result = scrollbar->frameRect();
182             break;
183         default: {
184             IntRect beforeThumbRect, thumbRect, afterThumbRect;
185             splitTrack(scrollbar, trackRect(scrollbar), beforeThumbRect, thumbRect, afterThumbRect);
186             if (part == BackTrackPart)
187                 result = beforeThumbRect;
188             else if (part == ForwardTrackPart)
189                 result = afterThumbRect;
190             else
191                 result = thumbRect;
192         }
193     }
194     result.moveBy(-scrollbar->location());
195     scrollbar->invalidateRect(result);
196 }
197
198 void ScrollbarThemeComposite::splitTrack(Scrollbar* scrollbar, const IntRect& unconstrainedTrackRect, IntRect& beforeThumbRect, IntRect& thumbRect, IntRect& afterThumbRect)
199 {
200     // This function won't even get called unless we're big enough to have some combination of these three rects where at least
201     // one of them is non-empty.
202     IntRect trackRect = constrainTrackRectToTrackPieces(scrollbar, unconstrainedTrackRect);
203     int thickness = scrollbar->orientation() == HorizontalScrollbar ? scrollbar->height() : scrollbar->width();
204     int thumbPos = thumbPosition(scrollbar);
205     if (scrollbar->orientation() == HorizontalScrollbar) {
206         thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - thickness) / 2, thumbLength(scrollbar), thickness); 
207         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos + thumbRect.width() / 2, trackRect.height()); 
208         afterThumbRect = IntRect(trackRect.x() + beforeThumbRect.width(), trackRect.y(), trackRect.maxX() - beforeThumbRect.maxX(), trackRect.height());
209     } else {
210         thumbRect = IntRect(trackRect.x() + (trackRect.width() - thickness) / 2, trackRect.y() + thumbPos, thickness, thumbLength(scrollbar));
211         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(), thumbPos + thumbRect.height() / 2); 
212         afterThumbRect = IntRect(trackRect.x(), trackRect.y() + beforeThumbRect.height(), trackRect.width(), trackRect.maxY() - beforeThumbRect.maxY());
213     }
214 }
215
216 // Returns the size represented by track taking into account scrolling past
217 // the end of the document.
218 static float usedTotalSize(Scrollbar* scrollbar)
219 {
220     float overhangAtStart = -scrollbar->currentPos();
221     float overhangAtEnd = scrollbar->currentPos() + scrollbar->visibleSize() - scrollbar->totalSize();
222     float overhang = max(0.0f, max(overhangAtStart, overhangAtEnd));
223     return scrollbar->totalSize() + overhang;
224 }
225
226 int ScrollbarThemeComposite::thumbPosition(Scrollbar* scrollbar)
227 {
228     if (scrollbar->enabled()) {
229         float pos = max(0.0f, scrollbar->currentPos()) * (trackLength(scrollbar) - thumbLength(scrollbar)) / (usedTotalSize(scrollbar) - scrollbar->visibleSize());
230         return (pos < 1 && pos > 0) ? 1 : pos;
231     }
232     return 0;
233 }
234
235 int ScrollbarThemeComposite::thumbLength(Scrollbar* scrollbar)
236 {
237     if (!scrollbar->enabled())
238         return 0;
239
240     float proportion = scrollbar->visibleSize() / usedTotalSize(scrollbar);
241     int trackLen = trackLength(scrollbar);
242     int length = proportion * trackLen;
243     length = max(length, minimumThumbLength(scrollbar));
244     if (length > trackLen)
245         length = 0; // Once the thumb is below the track length, it just goes away (to make more room for the track).
246     return length;
247 }
248
249 int ScrollbarThemeComposite::minimumThumbLength(Scrollbar* scrollbar)
250 {
251     return scrollbarThickness(scrollbar->controlSize());
252 }
253
254 int ScrollbarThemeComposite::trackPosition(Scrollbar* scrollbar)
255 {
256     IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
257     return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.x() - scrollbar->x() : constrainedTrackRect.y() - scrollbar->y();
258 }
259
260 int ScrollbarThemeComposite::trackLength(Scrollbar* scrollbar)
261 {
262     IntRect constrainedTrackRect = constrainTrackRectToTrackPieces(scrollbar, trackRect(scrollbar));
263     return (scrollbar->orientation() == HorizontalScrollbar) ? constrainedTrackRect.width() : constrainedTrackRect.height();
264 }
265
266 void ScrollbarThemeComposite::paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect)
267 {
268     context->fillRect(cornerRect, Color::white, ColorSpaceDeviceRGB);
269 }
270
271 void ScrollbarThemeComposite::paintOverhangAreas(ScrollView*, GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect& dirtyRect)
272 {    
273     context->setFillColor(Color::white, ColorSpaceDeviceRGB);
274     if (!horizontalOverhangRect.isEmpty())
275         context->fillRect(intersection(horizontalOverhangRect, dirtyRect));
276
277     context->setFillColor(Color::white, ColorSpaceDeviceRGB);
278     if (!verticalOverhangRect.isEmpty())
279         context->fillRect(intersection(verticalOverhangRect, dirtyRect));
280 }
281
282 }