2008-02-29 Brent Fulgham <bfulgham@gmail.com>
[WebKit-https.git] / WebCore / platform / win / PlatformScrollBarWin.cpp
1 /*
2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
3  * Copyright (C) 2008 Brent Fulgham
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28
29 #include "PlatformScrollBar.h"
30
31 #include "EventHandler.h"
32 #include "FrameView.h"
33 #include "Frame.h"
34 #include "GraphicsContext.h"
35 #include "IntRect.h"
36 #include "PlatformMouseEvent.h"
37 #include "SoftLinking.h"
38
39 #include <windows.h>
40 #include "RenderThemeWin.h"
41
42 // Generic state constants
43 #define TS_NORMAL    1
44 #define TS_HOVER     2
45 #define TS_ACTIVE    3
46 #define TS_DISABLED  4
47 #define TS_FOCUSED   5
48
49 #define ABS_UPNORMAL 1
50 #define ABS_DOWNNORMAL 5
51 #define ABS_LEFTNORMAL 9
52 #define ABS_RIGHTNORMAL 13
53
54 static const unsigned SP_ABS_HOT_MODIFIER = 1;
55 static const unsigned SP_ABS_PRESSED_MODIFIER = 2;
56 static const unsigned SP_ABS_DISABLE_MODIFIER = 3;
57
58 // Scrollbar constants
59 #define SP_BUTTON 1
60 #define SP_THUMBHOR 2
61 #define SP_THUMBVERT 3
62 #define SP_TRACKSTARTHOR 4
63 #define SP_TRACKENDHOR 5
64 #define SP_TRACKSTARTVERT 6
65 #define SP_TRACKENDVERT 7
66 #define SP_GRIPPERHOR 8
67 #define SP_GRIPPERVERT 9
68
69 using namespace std;
70
71 namespace WebCore {
72
73 // FIXME: We should get these numbers from SafariTheme
74 static int cHorizontalWidth;
75 static int cHorizontalHeight;
76 static int cVerticalWidth;
77 static int cVerticalHeight;
78 static int cHorizontalButtonWidth;
79 static int cVerticalButtonHeight;
80 static int cRealButtonLength = 28;
81 static int cButtonInset = 14;
82 static int cButtonHitInset = 3;
83 // cRealButtonLength - cButtonInset
84 static int cThumbWidth;
85 static int cThumbHeight;
86 static int cThumbMinLength = 26;
87
88 static HANDLE cScrollBarTheme = 0;
89
90 // FIXME:  Refactor the soft-linking code so that it can be shared with RenderThemeWin
91 SOFT_LINK_LIBRARY(uxtheme)
92 SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList))
93 SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme))
94 SOFT_LINK(uxtheme, DrawThemeBackground, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, pClipRect))
95 SOFT_LINK(uxtheme, DrawThemeEdge, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, unsigned uEdge, unsigned uFlags, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, uEdge, uFlags, pClipRect))
96 SOFT_LINK(uxtheme, GetThemeContentRect, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pContentRect), (hTheme, hdc, iPartId, iStateId, pRect, pContentRect))
97 SOFT_LINK(uxtheme, GetThemePartSize, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, RECT* pRect, int ts, SIZE* psz), (hTheme, hdc, iPartId, iStateId, pRect, ts, psz))
98 SOFT_LINK(uxtheme, GetThemeSysFont, HRESULT, WINAPI, (HANDLE hTheme, int iFontId, OUT LOGFONT* pFont), (hTheme, iFontId, pFont))
99 SOFT_LINK(uxtheme, GetThemeColor, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, int iPropId, OUT COLORREF* pColor), (hTheme, hdc, iPartId, iStateId, iPropId, pColor))
100
101 static void checkAndInitScrollbarTheme()
102 {
103     if (uxthemeLibrary() && !cScrollBarTheme)
104         cScrollBarTheme = OpenThemeData(0, L"Scrollbar");
105 }
106
107 // May need to add stuff to these later, so keep the graphics context retrieval/release in some helpers.
108 static HDC prepareForDrawing(GraphicsContext* g, const IntRect& r)
109 {
110     return g->getWindowsContext(r);
111 }
112  
113 static void doneDrawing(GraphicsContext* g, HDC hdc, const IntRect& r)
114 {
115     g->releaseWindowsContext(hdc, r);
116 }
117 // End Copied from RenderThemeWin
118
119 const double cInitialTimerDelay = 0.25;
120 const double cNormalTimerDelay = 0.05;
121
122 PlatformScrollbar::PlatformScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize size)
123     : Scrollbar(client, orientation, size)
124     , m_hoveredPart(NoPart)
125     , m_pressedPart(NoPart)
126     , m_pressedPos(0)
127     , m_scrollTimer(this, &PlatformScrollbar::autoscrollTimerFired)
128     , m_overlapsResizer(false)
129 {
130     // Obtain the correct scrollbar sizes from the system.
131     // FIXME:  We should update these on a WM_SETTINGSCHANGE, too.
132     if (!cHorizontalHeight) {
133        cHorizontalHeight = ::GetSystemMetrics(SM_CYHSCROLL);
134        cHorizontalWidth = ::GetSystemMetrics(SM_CXHSCROLL);
135        cVerticalHeight = ::GetSystemMetrics(SM_CYVSCROLL);
136        cVerticalWidth = ::GetSystemMetrics(SM_CXVSCROLL);
137        cThumbWidth = ::GetSystemMetrics(SM_CXHTHUMB);
138        cThumbHeight = ::GetSystemMetrics(SM_CYVTHUMB);
139        cHorizontalButtonWidth = ::GetSystemMetrics(SM_CYVSCROLL);
140        cVerticalButtonHeight = ::GetSystemMetrics(SM_CXHSCROLL);
141     }
142
143     if (orientation == VerticalScrollbar)
144         setFrameGeometry(IntRect(0, 0, cVerticalWidth, cVerticalHeight));
145     else
146         setFrameGeometry(IntRect(0, 0, cHorizontalWidth, cHorizontalHeight));
147 }
148
149 PlatformScrollbar::~PlatformScrollbar()
150 {
151     stopTimerIfNeeded();
152 }
153
154 void PlatformScrollbar::updateThumbPosition()
155 {
156     invalidateTrack();
157 }
158
159 void PlatformScrollbar::updateThumbProportion()
160 {
161     invalidateTrack();
162 }
163
164 static IntRect trackRepaintRect(const IntRect& trackRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize)
165 {
166     const int cButtonLength = (orientation == VerticalScrollbar) ? cVerticalButtonHeight : cHorizontalButtonWidth;
167
168     IntRect paintRect(trackRect);
169     if (orientation == HorizontalScrollbar)
170         paintRect.inflateX(cButtonLength);
171     else
172         paintRect.inflateY(cButtonLength);
173
174     return paintRect;
175 }
176
177 static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start)
178 {
179     IntRect paintRect(buttonRect);
180     if (orientation == HorizontalScrollbar) {
181         paintRect.setWidth(cRealButtonLength);
182         if (!start)
183             paintRect.setX(buttonRect.x() - (cRealButtonLength - buttonRect.width()));
184     } else {
185         paintRect.setHeight(cRealButtonLength);
186         if (!start)
187             paintRect.setY(buttonRect.y() - (cRealButtonLength - buttonRect.height()));
188     }
189
190     return paintRect;
191 }
192
193 void PlatformScrollbar::invalidateTrack()
194 {
195     IntRect rect = trackRepaintRect(trackRect(), m_orientation, controlSize());
196     rect.move(-x(), -y());
197     invalidateRect(rect);
198 }
199
200 void PlatformScrollbar::invalidatePart(ScrollbarPart part)
201 {
202     if (part == NoPart)
203         return;
204
205     IntRect result;    
206     switch (part) {
207         case BackButtonPart:
208             result = buttonRepaintRect(backButtonRect(), m_orientation, controlSize(), true);
209             break;
210         case ForwardButtonPart:
211             result = buttonRepaintRect(forwardButtonRect(), m_orientation, controlSize(), false);
212             break;
213         default: {
214             IntRect beforeThumbRect, thumbRect, afterThumbRect;
215             splitTrack(trackRect(), beforeThumbRect, thumbRect, afterThumbRect);
216             if (part == BackTrackPart)
217                 result = beforeThumbRect;
218             else if (part == ForwardTrackPart)
219                 result = afterThumbRect;
220             else
221                 result = thumbRect;
222         }
223     }
224     result.move(-x(), -y());
225     invalidateRect(result);
226 }
227
228 int PlatformScrollbar::width() const
229 {
230     return Widget::width();
231 }
232
233 int PlatformScrollbar::height() const
234 {
235     return Widget::height();
236 }
237
238 void PlatformScrollbar::setRect(const IntRect& rect)
239 {
240     // Get our window resizer rect and see if we overlap.  Adjust to avoid the overlap
241     // if necessary.
242     IntRect adjustedRect(rect);
243     if (parent() && parent()->isFrameView()) {
244         bool overlapsResizer = false;
245         FrameView* view = static_cast<FrameView*>(parent());
246         IntRect resizerRect = view->windowResizerRect();
247         resizerRect.setLocation(view->convertFromContainingWindow(resizerRect.location()));
248         if (rect.intersects(resizerRect)) {
249             if (orientation() == HorizontalScrollbar) {
250                 int overlap = rect.right() - resizerRect.x();
251                 if (overlap > 0 && resizerRect.right() >= rect.right()) {
252                     adjustedRect.setWidth(rect.width() - overlap);
253                     overlapsResizer = true;
254                 }
255             } else {
256                 int overlap = rect.bottom() - resizerRect.y();
257                 if (overlap > 0 && resizerRect.bottom() >= rect.bottom()) {
258                     adjustedRect.setHeight(rect.height() - overlap);
259                     overlapsResizer = true;
260                 }
261             }
262         }
263
264         if (overlapsResizer != m_overlapsResizer) {
265             m_overlapsResizer = overlapsResizer;
266             view->adjustOverlappingScrollbarCount(m_overlapsResizer ? 1 : -1);
267         }
268     }
269
270     setFrameGeometry(adjustedRect);
271 }
272
273 void PlatformScrollbar::setParent(ScrollView* parentView)
274 {
275     if (!parentView && m_overlapsResizer && parent() && parent()->isFrameView())
276         static_cast<FrameView*>(parent())->adjustOverlappingScrollbarCount(-1);
277     Widget::setParent(parentView);
278 }
279
280 void PlatformScrollbar::setEnabled(bool enabled)
281 {
282     if (enabled != isEnabled()) {
283         Widget::setEnabled(enabled);
284         invalidate();
285     }
286 }
287
288 void PlatformScrollbar::paint(GraphicsContext* graphicsContext, const IntRect& damageRect)
289 {
290     if (graphicsContext->updatingControlTints()) {
291         invalidate();
292         return;
293     }
294
295     if (graphicsContext->paintingDisabled())
296         return;
297
298     // Don't paint anything if the scrollbar doesn't intersect the damage rect.
299     if (!frameGeometry().intersects(damageRect))
300         return;
301
302     IntRect track = trackRect();
303     paintTrack(graphicsContext, track, true, damageRect);
304
305     if (hasButtons()) {
306         paintButton(graphicsContext, backButtonRect(), true, damageRect);
307         paintButton(graphicsContext, forwardButtonRect(), false, damageRect);
308     }
309
310     if (hasThumb() && damageRect.intersects(track)) {
311         IntRect startTrackRect, thumbRect, endTrackRect;
312         splitTrack(track, startTrackRect, thumbRect, endTrackRect);
313         paintThumb(graphicsContext, thumbRect, damageRect);
314     }
315 }
316
317 bool PlatformScrollbar::hasButtons() const
318 {
319     return isEnabled() && (m_orientation == HorizontalScrollbar ? width() : height()) >= 2 * (cRealButtonLength - cButtonHitInset);
320 }
321
322 bool PlatformScrollbar::hasThumb() const
323 {
324     return isEnabled() && (m_orientation == HorizontalScrollbar ? width() : height()) >= 2 * cButtonInset + cThumbMinLength + 1;
325 }
326
327 IntRect PlatformScrollbar::backButtonRect() const
328 {
329     // Our actual rect will shrink to half the available space when
330     // we have < 34 pixels left.  This allows the scrollbar
331     // to scale down and function even at tiny sizes.
332     if (m_orientation == HorizontalScrollbar)
333         return IntRect(x(), y(), cHorizontalButtonWidth, cHorizontalHeight);
334     return IntRect(x(), y(), cVerticalWidth, cVerticalButtonHeight);
335 }
336
337 IntRect PlatformScrollbar::forwardButtonRect() const
338 {
339     // Our desired rect is essentially 17x17.
340     
341     // Our actual rect will shrink to half the available space when
342     // we have < 34 pixels left.  This allows the scrollbar
343     // to scale down and function even at tiny sizes.
344     if (m_orientation == HorizontalScrollbar)
345         return IntRect(x() + width() - cHorizontalButtonWidth, y(), cHorizontalButtonWidth, cHorizontalHeight);
346     return IntRect(x(), y() + height() - cVerticalButtonHeight, cVerticalWidth, cVerticalButtonHeight);
347 }
348
349 IntRect PlatformScrollbar::trackRect() const
350 {
351     if (m_orientation == HorizontalScrollbar) {
352         if (!hasButtons())
353             return IntRect(x(), y(), width(), cHorizontalHeight);
354         return IntRect(x() + cHorizontalButtonWidth, y(), width() - 2 * cHorizontalButtonWidth, cHorizontalHeight);
355     }
356
357     if (!hasButtons())
358         return IntRect(x(), y(), cVerticalWidth, height());
359     return IntRect(x(), y() + cVerticalButtonHeight, cVerticalWidth, height() - 2 * cVerticalButtonHeight);
360 }
361
362 IntRect PlatformScrollbar::thumbRect() const
363 {
364     IntRect beforeThumbRect, thumbRect, afterThumbRect;
365     splitTrack(trackRect(), beforeThumbRect, thumbRect, afterThumbRect);
366     return thumbRect;
367 }
368
369 IntRect PlatformScrollbar::gripperRect(const IntRect& thumbRect) const
370 {
371     return IntRect();
372 }
373
374 void PlatformScrollbar::splitTrack(const IntRect& trackRect, IntRect& beforeThumbRect, IntRect& thumbRect, IntRect& afterThumbRect) const
375 {
376     // This function won't even get called unless we're big enough to have some combination of these three rects where at least
377     // one of them is non-empty.
378     int thumbPos = thumbPosition();
379     if (m_orientation == HorizontalScrollbar) {
380         thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - cThumbHeight) / 2, thumbLength(), cThumbHeight);
381         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos, trackRect.height());
382         afterThumbRect = IntRect(thumbRect.x() + thumbRect.width(), trackRect.y(), trackRect.right() - thumbRect.right(), trackRect.height());
383     } else {
384         thumbRect = IntRect(trackRect.x() + (trackRect.width() - cThumbWidth) / 2, trackRect.y() + thumbPos, cThumbWidth, thumbLength());
385         beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(), thumbPos);
386         afterThumbRect = IntRect(trackRect.x(), thumbRect.y() + thumbRect.height(), trackRect.width(), trackRect.bottom() - thumbRect.bottom());
387     }
388 }
389
390 int PlatformScrollbar::thumbPosition() const
391 {
392     if (isEnabled())
393         return (float)m_currentPos * (trackLength() - thumbLength()) / (m_totalSize - m_visibleSize);
394     return 0;
395 }
396
397 int PlatformScrollbar::thumbLength() const
398 {
399     if (!isEnabled())
400         return 0;
401
402     float proportion = (float)(m_visibleSize) / m_totalSize;
403     int trackLen = trackLength();
404     int length = proportion * trackLen;
405     int minLength = cThumbMinLength;
406     length = max(length, minLength);
407     if (length > trackLen)
408         length = 0; // Once the thumb is below the track length, it just goes away (to make more room for the track).
409     return length;
410 }
411
412 int PlatformScrollbar::trackLength() const
413 {
414     return (m_orientation == HorizontalScrollbar) ? trackRect().width() : trackRect().height();
415 }
416
417 void PlatformScrollbar::paintButton(GraphicsContext* context, const IntRect& rect, bool start, const IntRect& damageRect) const
418 {
419     IntRect paintRect = buttonRepaintRect(rect, m_orientation, controlSize(), start);
420     
421     if (!damageRect.intersects(paintRect))
422         return;
423
424     unsigned part = 0;
425     unsigned state = 0;
426     unsigned classicPart = 0;
427     unsigned classicState = 0;
428
429     if (m_orientation == HorizontalScrollbar) {
430        state = start ? ABS_LEFTNORMAL : ABS_RIGHTNORMAL;
431        classicPart = start ? DFCS_SCROLLLEFT : DFCS_SCROLLRIGHT;
432     } else {
433        state = start ? ABS_UPNORMAL : ABS_DOWNNORMAL;
434        classicPart = start ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
435     }
436
437     if (!isEnabled()) {
438         state += SP_ABS_DISABLE_MODIFIER;
439         classicState |= DFCS_INACTIVE;
440     } else if ((m_pressedPart == BackButtonPart && start)
441             || (m_pressedPart == ForwardButtonPart && !start)) {
442         state += SP_ABS_PRESSED_MODIFIER;
443         classicState |= DFCS_PUSHED | DFCS_FLAT;
444     } else if (m_client->isActive()) {
445        state += SP_ABS_HOT_MODIFIER;
446        classicState |= DFCS_HOT;
447     }
448
449     HDC hdc = prepareForDrawing(context, rect);
450     RECT widgetRect = rect;
451     checkAndInitScrollbarTheme();
452
453     if (cScrollBarTheme)
454         DrawThemeBackground(cScrollBarTheme, hdc, SP_BUTTON, state, &widgetRect, NULL);
455     else
456         DrawFrameControl(hdc, &widgetRect, classicPart, classicState);
457
458     doneDrawing(context, hdc, rect);
459 }
460
461 void PlatformScrollbar::paintTrack(GraphicsContext* context, const IntRect& rect, bool start, const IntRect& damageRect) const
462 {
463     IntRect paintRect = hasButtons() ? trackRepaintRect(rect, m_orientation, controlSize()) : rect;
464     
465     if (!damageRect.intersects(paintRect))
466         return;
467
468     unsigned part = 0;
469     unsigned classicPart = DFC_SCROLL;
470     if (m_orientation == HorizontalScrollbar)
471        part = start ? SP_TRACKSTARTHOR : SP_TRACKENDHOR;
472     else
473        part = start ? SP_TRACKSTARTVERT : SP_TRACKENDVERT;
474
475     unsigned state = TS_DISABLED;
476     unsigned classicState = DFCS_MONO;
477     if (m_client->isActive())
478         state |= TS_ACTIVE;
479     else
480        classicState |= DFCS_INACTIVE;
481
482     if (hasButtons())
483         state |= TS_NORMAL;
484
485     HDC hdc = prepareForDrawing(context, rect);
486     RECT widgetRect = rect;
487     checkAndInitScrollbarTheme();
488
489     if (cScrollBarTheme)
490         DrawThemeBackground(cScrollBarTheme, hdc, part, state, &widgetRect, NULL);
491     else
492         DrawFrameControl(hdc, &widgetRect, DFC_SCROLL, classicState);
493
494     doneDrawing(context, hdc, rect);
495 }
496
497 void PlatformScrollbar::paintThumb(GraphicsContext* context, const IntRect& rect, const IntRect& damageRect) const
498 {
499     if (!damageRect.intersects(rect))
500         return;
501
502     unsigned part = (m_orientation == HorizontalScrollbar) ? SP_THUMBHOR : SP_THUMBVERT;
503     unsigned state = 0;
504
505     if (!isEnabled())
506         state += SP_ABS_DISABLE_MODIFIER;
507     else if (m_client->isActive())
508        state += SP_ABS_HOT_MODIFIER;
509
510     HDC hdc = prepareForDrawing(context, rect);
511     RECT widgetRect = rect;
512     checkAndInitScrollbarTheme();
513
514     if (cScrollBarTheme) {
515         DrawThemeBackground(cScrollBarTheme, hdc, part, state, &widgetRect, NULL);
516         paintGripper(hdc, widgetRect);
517     } else {
518         HGDIOBJ hSaveBrush = SelectObject(hdc, GetSysColorBrush(COLOR_BTNFACE));
519         DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT);
520         SelectObject(hdc,hSaveBrush);
521     }
522
523     doneDrawing(context, hdc, rect);
524 }
525
526 void PlatformScrollbar::paintGripper(HDC hdc, const IntRect& rect) const
527 {
528     unsigned part = (m_orientation == HorizontalScrollbar) ? SP_GRIPPERHOR : SP_GRIPPERVERT;
529     unsigned state = 0;
530
531     if (m_client->isActive())
532         state |= TS_ACTIVE;
533
534     RECT widgetRect = rect;
535     checkAndInitScrollbarTheme();
536
537     if (cScrollBarTheme)
538         DrawThemeBackground(cScrollBarTheme, hdc, part, state, &widgetRect, NULL);
539 }
540
541 ScrollbarPart PlatformScrollbar::hitTest(const PlatformMouseEvent& evt)
542 {
543     if (!isEnabled())
544         return NoPart;
545
546     IntPoint mousePosition = convertFromContainingWindow(evt.pos());
547     mousePosition.move(x(), y());
548
549     if (hasButtons()) {
550         if (backButtonRect().contains(mousePosition))
551             return BackButtonPart;
552
553         if (forwardButtonRect().contains(mousePosition))
554             return ForwardButtonPart;
555     }
556
557     if (!hasThumb())
558         return NoPart;
559
560     IntRect track = trackRect();
561     if (track.contains(mousePosition)) {
562         IntRect beforeThumbRect, thumbRect, afterThumbRect;
563         splitTrack(track, beforeThumbRect, thumbRect, afterThumbRect);
564         if (beforeThumbRect.contains(mousePosition))
565             return BackTrackPart;
566         if (thumbRect.contains(mousePosition))
567             return ThumbPart;
568         return ForwardTrackPart;
569     }
570
571     return NoPart;
572 }
573
574 bool PlatformScrollbar::handleMouseMoveEvent(const PlatformMouseEvent& evt)
575 {
576     if (m_pressedPart == ThumbPart) {
577         // Drag the thumb.
578         int thumbPos = thumbPosition();
579         int thumbLen = thumbLength();
580         int trackLen = trackLength();
581         int maxPos = trackLen - thumbLen;
582         int delta = 0;
583         if (m_orientation == HorizontalScrollbar)
584             delta = convertFromContainingWindow(evt.pos()).x() - m_pressedPos;
585         else
586             delta = convertFromContainingWindow(evt.pos()).y() - m_pressedPos;
587
588         if (delta > 0)
589             // The mouse moved down/right.
590             delta = min(maxPos - thumbPos, delta);
591         else if (delta < 0)
592             // The mouse moved up/left.
593             delta = max(-thumbPos, delta);
594
595         if (delta != 0) {
596             setValue((float)(thumbPos + delta) * (m_totalSize - m_visibleSize) / (trackLen - thumbLen));
597             m_pressedPos += thumbPosition() - thumbPos;
598         }
599
600         return true;
601     }
602
603     if (m_pressedPart != NoPart)
604         m_pressedPos = (m_orientation == HorizontalScrollbar ? convertFromContainingWindow(evt.pos()).x() : convertFromContainingWindow(evt.pos()).y());
605
606     ScrollbarPart part = hitTest(evt);    
607     if (part != m_hoveredPart) {
608         if (m_pressedPart != NoPart) {
609             if (part == m_pressedPart) {
610                 // The mouse is moving back over the pressed part.  We
611                 // need to start up the timer action again.
612                 startTimerIfNeeded(cNormalTimerDelay);
613                 invalidatePart(m_pressedPart);
614             } else if (m_hoveredPart == m_pressedPart) {
615                 // The mouse is leaving the pressed part.  Kill our timer
616                 // if needed.
617                 stopTimerIfNeeded();
618                 invalidatePart(m_pressedPart);
619             }
620         } else {
621             invalidatePart(part);
622             invalidatePart(m_hoveredPart);
623         }
624         m_hoveredPart = part;
625     } 
626
627     return true;
628 }
629
630 bool PlatformScrollbar::handleMouseOutEvent(const PlatformMouseEvent& evt)
631 {
632     invalidatePart(m_hoveredPart);
633     m_hoveredPart = NoPart;
634
635     return true;
636 }
637
638 bool PlatformScrollbar::handleMousePressEvent(const PlatformMouseEvent& evt)
639 {
640     m_pressedPart = hitTest(evt);
641     m_pressedPos = (m_orientation == HorizontalScrollbar ? convertFromContainingWindow(evt.pos()).x() : convertFromContainingWindow(evt.pos()).y());
642     invalidatePart(m_pressedPart);
643     autoscrollPressedPart(cInitialTimerDelay);
644     return true;
645 }
646
647 bool PlatformScrollbar::handleMouseReleaseEvent(const PlatformMouseEvent& evt)
648 {
649     invalidatePart(m_pressedPart);
650     m_pressedPart = NoPart;
651     m_pressedPos = 0;
652     stopTimerIfNeeded();
653
654     if (parent() && parent()->isFrameView())
655         static_cast<FrameView*>(parent())->frame()->eventHandler()->setMousePressed(false);
656
657     return true;
658 }
659
660 void PlatformScrollbar::startTimerIfNeeded(double delay)
661 {
662     // Don't do anything for the thumb.
663     if (m_pressedPart == ThumbPart)
664         return;
665
666     // Handle the track.  We halt track scrolling once the thumb is level
667     // with us.
668     if ((m_pressedPart == BackTrackPart || m_pressedPart == ForwardTrackPart) && thumbUnderMouse()) {
669         invalidatePart(m_pressedPart);
670         m_hoveredPart = ThumbPart;
671         return;
672     }
673
674     // We can't scroll if we've hit the beginning or end.
675     ScrollDirection dir = pressedPartScrollDirection();
676     if (dir == ScrollUp || dir == ScrollLeft) {
677         if (m_currentPos == 0)
678             return;
679     } else {
680         if (m_currentPos == m_totalSize - m_visibleSize)
681             return;
682     }
683
684     m_scrollTimer.startOneShot(delay);
685 }
686
687 void PlatformScrollbar::stopTimerIfNeeded()
688 {
689     if (m_scrollTimer.isActive())
690         m_scrollTimer.stop();
691 }
692
693 void PlatformScrollbar::autoscrollPressedPart(double delay)
694 {
695     // Don't do anything for the thumb or if nothing was pressed.
696     if (m_pressedPart == ThumbPart || m_pressedPart == NoPart)
697         return;
698
699     // Handle the track.
700     if ((m_pressedPart == BackTrackPart || m_pressedPart == ForwardTrackPart) && thumbUnderMouse()) {
701         invalidatePart(m_pressedPart);
702         m_hoveredPart = ThumbPart;
703         return;
704     }
705
706     // Handle the arrows and track.
707     if (scroll(pressedPartScrollDirection(), pressedPartScrollGranularity()))
708         startTimerIfNeeded(delay);
709 }
710
711 void PlatformScrollbar::autoscrollTimerFired(Timer<PlatformScrollbar>*)
712 {
713     autoscrollPressedPart(cNormalTimerDelay);
714 }
715
716 ScrollDirection PlatformScrollbar::pressedPartScrollDirection()
717 {
718     if (m_orientation == HorizontalScrollbar) {
719         if (m_pressedPart == BackButtonPart || m_pressedPart == BackTrackPart)
720             return ScrollLeft;
721         return ScrollRight;
722     } else {
723         if (m_pressedPart == BackButtonPart || m_pressedPart == BackTrackPart)
724             return ScrollUp;
725         return ScrollDown;
726     }
727 }
728
729 ScrollGranularity PlatformScrollbar::pressedPartScrollGranularity()
730 {
731     if (m_pressedPart == BackButtonPart || m_pressedPart == ForwardButtonPart)
732         return ScrollByLine;
733     return ScrollByPage;
734 }
735
736 bool PlatformScrollbar::thumbUnderMouse()
737 {
738     // Construct a rect.
739     IntRect thumb = thumbRect();
740     thumb.move(-x(), -y());
741     int begin = (m_orientation == HorizontalScrollbar) ? thumb.x() : thumb.y();
742     int end = (m_orientation == HorizontalScrollbar) ? thumb.right() : thumb.bottom();
743     return (begin <= m_pressedPos && m_pressedPos < end);
744 }
745
746 int PlatformScrollbar::horizontalScrollbarHeight(ScrollbarControlSize controlSize)
747 {
748     return cHorizontalWidth;
749 }
750
751 int PlatformScrollbar::verticalScrollbarWidth(ScrollbarControlSize controlSize)
752 {
753     return cVerticalHeight;
754 }
755
756 IntRect PlatformScrollbar::windowClipRect() const
757 {
758     IntRect clipRect(0, 0, width(), height());
759
760     clipRect = convertToContainingWindow(clipRect);
761     if (m_client)
762         clipRect.intersect(m_client->windowClipRect());
763
764     return clipRect;
765 }
766
767 void PlatformScrollbar::themeChanged()
768 {
769 }
770
771 }
772