2011-01-03 Mihai Parparita <mihaip@chromium.org>
[WebKit-https.git] / WebCore / platform / chromium / ScrollbarThemeChromiumMac.mm
1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  * Copyright (C) 2009 Google Inc. All Rights Reserved.
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 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 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 #include "ScrollbarThemeChromiumMac.h"
29
30 // FIXME: Remove this (always use WebThemeEngine) once we rebaseline tests
31 #define USE_WEB_THEME_ENGINE_TO_PAINT_THUMB 1
32
33 #if USE_WEB_THEME_ENGINE_TO_PAINT_THUMB
34 #include "ChromiumBridge.h"
35 #include "FrameView.h"
36 #endif
37 #include "ImageBuffer.h"
38 #include "PlatformMouseEvent.h"
39 #include "ScrollView.h"
40 #include <Carbon/Carbon.h>
41 #include <wtf/StdLibExtras.h>
42 #include <wtf/UnusedParam.h>
43
44
45 // FIXME: There are repainting problems due to Aqua scroll bar buttons' visual overflow.
46
47 using namespace std;
48 using namespace WebCore;
49
50 // This file (and its associated .h file) is a clone of ScrollbarThemeMac.mm.
51 // Because we want to draw tickmarks in the scrollbar, we must maintain a fork.
52 // Please maintain this file by performing parallel changes to it.
53 //
54 // The only changes from ScrollbarThemeMac should be:
55 // - The classname change from ScrollbarThemeMac to ScrollbarThemeChromiumMac.
56 // - In paint() the code to paint the track, tickmarks, and thumb separately.
57 //
58 // For all other differences, if it was introduced in this file, then the
59 // maintainer forgot to include it in the list; otherwise it is an update that
60 // should have been applied to this file but was not.
61
62 static HashSet<Scrollbar*>* gScrollbars;
63
64 @interface ScrollbarPrefsObserver : NSObject
65 {
66
67 }
68
69 + (void)registerAsObserver;
70 + (void)appearancePrefsChanged:(NSNotification*)theNotification;
71 + (void)behaviorPrefsChanged:(NSNotification*)theNotification;
72
73 @end
74
75 @implementation ScrollbarPrefsObserver
76
77 + (void)appearancePrefsChanged:(NSNotification*)unusedNotification
78 {
79     UNUSED_PARAM(unusedNotification);
80
81     static_cast<ScrollbarThemeChromiumMac*>(ScrollbarTheme::nativeTheme())->preferencesChanged();
82     if (!gScrollbars)
83         return;
84     HashSet<Scrollbar*>::iterator end = gScrollbars->end();
85     for (HashSet<Scrollbar*>::iterator it = gScrollbars->begin(); it != end; ++it) {
86         (*it)->styleChanged();
87         (*it)->invalidate();
88     }
89 }
90
91 + (void)behaviorPrefsChanged:(NSNotification*)unusedNotification
92 {
93     UNUSED_PARAM(unusedNotification);
94
95     static_cast<ScrollbarThemeChromiumMac*>(ScrollbarTheme::nativeTheme())->preferencesChanged();
96 }
97
98 + (void)registerAsObserver
99 {
100     [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(appearancePrefsChanged:) name:@"AppleAquaScrollBarVariantChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
101     [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(behaviorPrefsChanged:) name:@"AppleNoRedisplayAppearancePreferenceChanged" object:nil suspensionBehavior:NSNotificationSuspensionBehaviorCoalesce];
102 }
103
104 @end
105
106 namespace WebCore {
107
108 ScrollbarTheme* ScrollbarTheme::nativeTheme()
109 {
110     DEFINE_STATIC_LOCAL(ScrollbarThemeChromiumMac, theme, ());
111     return &theme;
112 }
113
114 // FIXME: Get these numbers from CoreUI.
115 static int cScrollbarThickness[] = { 15, 11 };
116 static int cRealButtonLength[] = { 28, 21 };
117 static int cButtonInset[] = { 14, 11 };
118 static int cButtonHitInset[] = { 3, 2 };
119 // cRealButtonLength - cButtonInset
120 static int cButtonLength[] = { 14, 10 };
121 static int cThumbMinLength[] = { 26, 20 };
122
123 static int cOuterButtonLength[] = { 16, 14 }; // The outer button in a double button pair is a bit bigger.
124 static int cOuterButtonOverlap = 2;
125
126 static float gInitialButtonDelay = 0.5f;
127 static float gAutoscrollButtonDelay = 0.05f;
128 static bool gJumpOnTrackClick = false;
129 static ScrollbarButtonsPlacement gButtonPlacement = ScrollbarButtonsDoubleEnd;
130
131 static void updateArrowPlacement()
132 {
133     NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
134     if ([buttonPlacement isEqualToString:@"Single"])
135         gButtonPlacement = ScrollbarButtonsSingle;
136     else if ([buttonPlacement isEqualToString:@"DoubleMin"])
137         gButtonPlacement = ScrollbarButtonsDoubleStart;
138     else if ([buttonPlacement isEqualToString:@"DoubleBoth"])
139         gButtonPlacement = ScrollbarButtonsDoubleBoth;
140     else
141         gButtonPlacement = ScrollbarButtonsDoubleEnd; // The default is ScrollbarButtonsDoubleEnd.
142 }
143
144 void ScrollbarThemeChromiumMac::registerScrollbar(Scrollbar* scrollbar)
145 {
146     if (!gScrollbars)
147         gScrollbars = new HashSet<Scrollbar*>;
148     gScrollbars->add(scrollbar);
149 }
150
151 void ScrollbarThemeChromiumMac::unregisterScrollbar(Scrollbar* scrollbar)
152 {
153     gScrollbars->remove(scrollbar);
154     if (gScrollbars->isEmpty()) {
155         delete gScrollbars;
156         gScrollbars = 0;
157     }
158 }
159
160 ScrollbarThemeChromiumMac::ScrollbarThemeChromiumMac()
161 {
162     static bool initialized;
163     if (!initialized) {
164         initialized = true;
165         [ScrollbarPrefsObserver registerAsObserver];
166         preferencesChanged();
167     }
168 }
169
170 ScrollbarThemeChromiumMac::~ScrollbarThemeChromiumMac()
171 {
172 }
173
174 void ScrollbarThemeChromiumMac::preferencesChanged()
175 {
176     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
177     [defaults synchronize];
178     updateArrowPlacement();
179     gInitialButtonDelay = [defaults floatForKey:@"NSScrollerButtonDelay"];
180     gAutoscrollButtonDelay = [defaults floatForKey:@"NSScrollerButtonPeriod"];
181     gJumpOnTrackClick = [defaults boolForKey:@"AppleScrollerPagingBehavior"];
182 }
183
184 int ScrollbarThemeChromiumMac::scrollbarThickness(ScrollbarControlSize controlSize)
185 {
186     return cScrollbarThickness[controlSize];
187 }
188
189 double ScrollbarThemeChromiumMac::initialAutoscrollTimerDelay()
190 {
191     return gInitialButtonDelay;
192 }
193
194 double ScrollbarThemeChromiumMac::autoscrollTimerDelay()
195 {
196     return gAutoscrollButtonDelay;
197 }
198
199 ScrollbarButtonsPlacement ScrollbarThemeChromiumMac::buttonsPlacement() const
200 {
201     return gButtonPlacement;
202 }
203
204 bool ScrollbarThemeChromiumMac::hasButtons(Scrollbar* scrollbar)
205 {
206     return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
207              scrollbar->width() :
208              scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]);
209 }
210
211 bool ScrollbarThemeChromiumMac::hasThumb(Scrollbar* scrollbar)
212 {
213     return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ?
214              scrollbar->width() :
215              scrollbar->height()) >= 2 * cButtonInset[scrollbar->controlSize()] + cThumbMinLength[scrollbar->controlSize()] + 1;
216 }
217
218 static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start)
219 {
220     IntRect paintRect(buttonRect);
221     if (orientation == HorizontalScrollbar) {
222         paintRect.setWidth(cRealButtonLength[controlSize]);
223         if (!start)
224             paintRect.setX(buttonRect.x() - (cRealButtonLength[controlSize] - buttonRect.width()));
225     } else {
226         paintRect.setHeight(cRealButtonLength[controlSize]);
227         if (!start)
228             paintRect.setY(buttonRect.y() - (cRealButtonLength[controlSize] - buttonRect.height()));
229     }
230
231     return paintRect;
232 }
233
234 IntRect ScrollbarThemeChromiumMac::backButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool painting)
235 {
236     IntRect result;
237
238     if (part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd))
239         return result;
240
241     if (part == BackButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsSingle))
242         return result;
243
244     int thickness = scrollbarThickness(scrollbar->controlSize());
245     bool outerButton = part == BackButtonStartPart && (buttonsPlacement() == ScrollbarButtonsDoubleStart || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
246     if (outerButton) {
247         if (scrollbar->orientation() == HorizontalScrollbar)
248             result = IntRect(scrollbar->x(), scrollbar->y(), cOuterButtonLength[scrollbar->controlSize()] + painting ? cOuterButtonOverlap : 0, thickness);
249         else
250             result = IntRect(scrollbar->x(), scrollbar->y(), thickness, cOuterButtonLength[scrollbar->controlSize()] + painting ? cOuterButtonOverlap : 0);
251         return result;
252     }
253
254     // Our repaint rect is slightly larger, since we are a button that is adjacent to the track.
255     if (scrollbar->orientation() == HorizontalScrollbar) {
256         int start = part == BackButtonStartPart ? scrollbar->x() : scrollbar->x() + scrollbar->width() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
257         result = IntRect(start, scrollbar->y(), cButtonLength[scrollbar->controlSize()], thickness);
258     } else {
259         int start = part == BackButtonStartPart ? scrollbar->y() : scrollbar->y() + scrollbar->height() - cOuterButtonLength[scrollbar->controlSize()] - cButtonLength[scrollbar->controlSize()];
260         result = IntRect(scrollbar->x(), start, thickness, cButtonLength[scrollbar->controlSize()]);
261     }
262
263     if (painting)
264         return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == BackButtonStartPart);
265     return result;
266 }
267
268 IntRect ScrollbarThemeChromiumMac::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart part, bool painting)
269 {
270     IntRect result;
271
272     if (part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleStart))
273         return result;
274
275     if (part == ForwardButtonStartPart && (buttonsPlacement() == ScrollbarButtonsNone || buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsSingle))
276         return result;
277
278     int thickness = scrollbarThickness(scrollbar->controlSize());
279     int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
280     int buttonLength = cButtonLength[scrollbar->controlSize()];
281
282     bool outerButton = part == ForwardButtonEndPart && (buttonsPlacement() == ScrollbarButtonsDoubleEnd || buttonsPlacement() == ScrollbarButtonsDoubleBoth);
283     if (outerButton) {
284         if (scrollbar->orientation() == HorizontalScrollbar) {
285             result = IntRect(scrollbar->x() + scrollbar->width() - outerButtonLength, scrollbar->y(), outerButtonLength, thickness);
286             if (painting)
287                 result.inflateX(cOuterButtonOverlap);
288         } else {
289             result = IntRect(scrollbar->x(), scrollbar->y() + scrollbar->height() - outerButtonLength, thickness, outerButtonLength);
290             if (painting)
291                 result.inflateY(cOuterButtonOverlap);
292         }
293         return result;
294     }
295
296     if (scrollbar->orientation() == HorizontalScrollbar) {
297         int start = part == ForwardButtonEndPart ? scrollbar->x() + scrollbar->width() - buttonLength : scrollbar->x() + outerButtonLength;
298         result = IntRect(start, scrollbar->y(), buttonLength, thickness);
299     } else {
300         int start = part == ForwardButtonEndPart ? scrollbar->y() + scrollbar->height() - buttonLength : scrollbar->y() + outerButtonLength;
301         result = IntRect(scrollbar->x(), start, thickness, buttonLength);
302     }
303     if (painting)
304         return buttonRepaintRect(result, scrollbar->orientation(), scrollbar->controlSize(), part == ForwardButtonStartPart);
305     return result;
306 }
307
308 IntRect ScrollbarThemeChromiumMac::trackRect(Scrollbar* scrollbar, bool painting)
309 {
310     if (painting || !hasButtons(scrollbar))
311         return scrollbar->frameRect();
312
313     IntRect result;
314     int thickness = scrollbarThickness(scrollbar->controlSize());
315     int startWidth = 0;
316     int endWidth = 0;
317     int outerButtonLength = cOuterButtonLength[scrollbar->controlSize()];
318     int buttonLength = cButtonLength[scrollbar->controlSize()];
319     int doubleButtonLength = outerButtonLength + buttonLength;
320     switch (buttonsPlacement()) {
321         case ScrollbarButtonsSingle:
322             startWidth = buttonLength;
323             endWidth = buttonLength;
324             break;
325         case ScrollbarButtonsDoubleStart:
326             startWidth = doubleButtonLength;
327             break;
328         case ScrollbarButtonsDoubleEnd:
329             endWidth = doubleButtonLength;
330             break;
331         case ScrollbarButtonsDoubleBoth:
332             startWidth = doubleButtonLength;
333             endWidth = doubleButtonLength;
334             break;
335         default:
336             break;
337     }
338
339     int totalWidth = startWidth + endWidth;
340     if (scrollbar->orientation() == HorizontalScrollbar)
341         return IntRect(scrollbar->x() + startWidth, scrollbar->y(), scrollbar->width() - totalWidth, thickness);
342     return IntRect(scrollbar->x(), scrollbar->y() + startWidth, thickness, scrollbar->height() - totalWidth);
343 }
344
345 int ScrollbarThemeChromiumMac::minimumThumbLength(Scrollbar* scrollbar)
346 {
347     return cThumbMinLength[scrollbar->controlSize()];
348 }
349
350 bool ScrollbarThemeChromiumMac::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt)
351 {
352     if (evt.button() != LeftButton)
353         return false;
354     if (gJumpOnTrackClick)
355         return !evt.altKey();
356     return evt.altKey();
357 }
358
359 static int scrollbarPartToHIPressedState(ScrollbarPart part)
360 {
361     switch (part) {
362         case BackButtonStartPart:
363             return kThemeTopOutsideArrowPressed;
364         case BackButtonEndPart:
365             return kThemeTopOutsideArrowPressed; // This does not make much sense.  For some reason the outside constant is required.
366         case ForwardButtonStartPart:
367             return kThemeTopInsideArrowPressed;
368         case ForwardButtonEndPart:
369             return kThemeBottomOutsideArrowPressed;
370         case ThumbPart:
371             return kThemeThumbPressed;
372         default:
373             return 0;
374     }
375 }
376
377 #if USE_WEB_THEME_ENGINE_TO_PAINT_THUMB
378 static ChromiumBridge::ThemePaintState scrollbarStateToThemeState(Scrollbar* scrollbar) {
379     if (!scrollbar->enabled())
380         return ChromiumBridge::StateDisabled;
381     if (!scrollbar->client()->isActive())
382         return ChromiumBridge::StateInactive;
383     if (scrollbar->pressedPart() == ThumbPart)
384         return ChromiumBridge::StatePressed;
385
386     return ChromiumBridge::StateActive;
387 }
388 #endif // USE_WEB_THEME_ENGINE_TO_PAINT_THUMB
389
390 bool ScrollbarThemeChromiumMac::paint(Scrollbar* scrollbar, GraphicsContext* context, const IntRect& damageRect)
391 {
392     HIThemeTrackDrawInfo trackInfo;
393     trackInfo.version = 0;
394     trackInfo.kind = scrollbar->controlSize() == RegularScrollbar ? kThemeMediumScrollBar : kThemeSmallScrollBar;
395     trackInfo.bounds = scrollbar->frameRect();
396     trackInfo.min = 0;
397     trackInfo.max = scrollbar->maximum();
398     trackInfo.value = scrollbar->currentPos();
399     trackInfo.trackInfo.scrollbar.viewsize = scrollbar->visibleSize();
400     trackInfo.attributes = 0;
401     if (scrollbar->orientation() == HorizontalScrollbar)
402         trackInfo.attributes |= kThemeTrackHorizontal;
403
404     if (!scrollbar->enabled())
405         trackInfo.enableState = kThemeTrackDisabled;
406     else
407         trackInfo.enableState = scrollbar->client()->isActive() ? kThemeTrackActive : kThemeTrackInactive;
408
409     if (!hasButtons(scrollbar))
410         trackInfo.enableState = kThemeTrackNothingToScroll;
411     trackInfo.trackInfo.scrollbar.pressState = scrollbarPartToHIPressedState(scrollbar->pressedPart());
412
413     CGAffineTransform currentCTM = CGContextGetCTM(context->platformContext());
414
415     // The Aqua scrollbar is buggy when rotated and scaled.  We will just draw into a bitmap if we detect a scale or rotation.
416     bool canDrawDirectly = currentCTM.a == 1.0f && currentCTM.b == 0.0f && currentCTM.c == 0.0f && (currentCTM.d == 1.0f || currentCTM.d == -1.0f);
417     GraphicsContext* drawingContext = context;
418     OwnPtr<ImageBuffer> imageBuffer;
419     if (!canDrawDirectly) {
420         trackInfo.bounds = IntRect(IntPoint(), scrollbar->frameRect().size());
421
422         IntRect bufferRect(scrollbar->frameRect());
423         bufferRect.intersect(damageRect);
424         bufferRect.move(-scrollbar->frameRect().x(), -scrollbar->frameRect().y());
425
426         imageBuffer = ImageBuffer::create(bufferRect.size());
427         if (!imageBuffer)
428             return true;
429
430         drawingContext = imageBuffer->context();
431     }
432
433     // Draw thumbless.
434     HIThemeDrawTrack(&trackInfo, 0, drawingContext->platformContext(), kHIThemeOrientationNormal);
435
436     Vector<IntRect> tickmarks;
437     scrollbar->client()->getTickmarks(tickmarks);
438     if (scrollbar->orientation() == VerticalScrollbar && tickmarks.size()) {
439         drawingContext->save();
440         drawingContext->setShouldAntialias(false);
441         drawingContext->setStrokeColor(Color(0xCC, 0xAA, 0x00, 0xFF), ColorSpaceDeviceRGB);
442         drawingContext->setFillColor(Color(0xFF, 0xDD, 0x00, 0xFF), ColorSpaceDeviceRGB);
443
444         IntRect thumbArea = trackRect(scrollbar, false);
445         if (!canDrawDirectly) {
446             thumbArea.setX(0);
447             thumbArea.setY(0);
448         }
449         // The ends are rounded and the thumb doesn't go there.
450         thumbArea.inflateY(-thumbArea.width());
451
452         for (Vector<IntRect>::const_iterator i = tickmarks.begin(); i != tickmarks.end(); ++i) {
453             // Calculate how far down (in %) the tick-mark should appear.
454             const float percent = static_cast<float>(i->y()) / scrollbar->totalSize();
455             if (percent < 0.0 || percent > 1.0)
456               continue;
457
458             // Calculate how far down (in pixels) the tick-mark should appear.
459             const int yPos = static_cast<int>((thumbArea.topLeft().y() + (thumbArea.height() * percent))) & ~1;
460
461             // Paint.
462             const int indent = 2;
463             FloatRect tickRect(thumbArea.topLeft().x() + indent, yPos, thumbArea.width() - 2 * indent - 1, 2);
464             drawingContext->fillRect(tickRect);
465             drawingContext->strokeRect(tickRect, 1);
466         }
467
468         drawingContext->restore();
469     }
470
471     if (hasThumb(scrollbar)) {
472 #if USE_WEB_THEME_ENGINE_TO_PAINT_THUMB
473         ChromiumBridge::ThemePaintScrollbarInfo scrollbarInfo;
474         scrollbarInfo.orientation = scrollbar->orientation() == HorizontalScrollbar ? ChromiumBridge::ScrollbarOrientationHorizontal : ChromiumBridge::ScrollbarOrientationVertical;
475         scrollbarInfo.parent = scrollbar->parent()->isFrameView() && static_cast<FrameView*>(scrollbar->parent())->isScrollViewScrollbar(scrollbar) ? ChromiumBridge::ScrollbarParentScrollView : ChromiumBridge::ScrollbarParentRenderLayer;
476         scrollbarInfo.maxValue = scrollbar->maximum();
477         scrollbarInfo.currentValue = scrollbar->currentPos();
478         scrollbarInfo.visibleSize = scrollbar->visibleSize();
479         scrollbarInfo.totalSize = scrollbar->totalSize();
480
481         ChromiumBridge::paintScrollbarThumb(
482             drawingContext,
483             scrollbarStateToThemeState(scrollbar),
484             scrollbar->controlSize() == RegularScrollbar ? ChromiumBridge::SizeRegular : ChromiumBridge::SizeSmall,
485             scrollbar->frameRect(),
486             scrollbarInfo);
487 #else
488         trackInfo.attributes |= (kThemeTrackShowThumb | kThemeTrackHideTrack);
489         HIThemeDrawTrack(&trackInfo, 0, drawingContext->platformContext(), kHIThemeOrientationNormal);
490 #endif
491     }
492
493     if (!canDrawDirectly)
494         context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, scrollbar->frameRect().location());
495
496     return true;
497 }
498
499 }
500