[BlackBerry] Use GraphicsContext::fillPath() and strokePath instead of drawPath(...
[WebKit-https.git] / Source / WebCore / platform / blackberry / RenderThemeBlackBerry.cpp
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc.
3  * Copyright (C) 2009 Google Inc.
4  * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include "config.h"
22 #include "RenderThemeBlackBerry.h"
23
24 #include "CSSValueKeywords.h"
25 #include "Frame.h"
26 #include "HTMLMediaElement.h"
27 #include "MediaControlElements.h"
28 #include "MediaPlayerPrivateBlackBerry.h"
29 #include "PaintInfo.h"
30 #include "RenderProgress.h"
31 #include "RenderSlider.h"
32 #include "RenderView.h"
33 #include "UserAgentStyleSheets.h"
34
35 namespace WebCore {
36
37 // Sizes (unit px)
38 const unsigned smallRadius = 1;
39 const unsigned largeRadius = 3;
40 const unsigned lineWidth = 1;
41 const int marginSize = 4;
42 const int mediaSliderThumbWidth = 40;
43 const int mediaSliderThumbHeight = 13;
44 const int mediaSliderThumbRadius = 5;
45 const int sliderThumbWidth = 15;
46 const int sliderThumbHeight = 25;
47
48 // Checkbox check scalers
49 const float checkboxLeftX = 7 / 40.0;
50 const float checkboxLeftY = 1 / 2.0;
51 const float checkboxMiddleX = 19 / 50.0;
52 const float checkboxMiddleY = 7 / 25.0;
53 const float checkboxRightX = 33 / 40.0;
54 const float checkboxRightY = 1 / 5.0;
55 const float checkboxStrokeThickness = 6.5;
56
57 // Radio button scaler
58 const float radioButtonCheckStateScaler = 7 / 30.0;
59
60 // Multipliers
61 const unsigned paddingDivisor = 5;
62
63 // Colors
64 const RGBA32 caretBottom = 0xff2163bf;
65 const RGBA32 caretTop = 0xff69a5fa;
66
67 const RGBA32 regularBottom = 0xffdcdee4;
68 const RGBA32 regularTop = 0xfff7f2ee;
69 const RGBA32 hoverBottom = 0xffb5d3fc;
70 const RGBA32 hoverTop = 0xffcceaff;
71 const RGBA32 depressedBottom = 0xff3388ff;
72 const RGBA32 depressedTop = 0xff66a0f2;
73 const RGBA32 disabledBottom = 0xffe7e7e7;
74 const RGBA32 disabledTop = 0xffefefef;
75
76 const RGBA32 regularBottomOutline = 0xff6e7073;
77 const RGBA32 regularTopOutline = 0xffb9b8b8;
78 const RGBA32 hoverBottomOutline = 0xff2163bf;
79 const RGBA32 hoverTopOutline = 0xff69befa;
80 const RGBA32 depressedBottomOutline = 0xff0c3d81;
81 const RGBA32 depressedTopOutline = 0xff1d4d70;
82 const RGBA32 disabledOutline = 0xffd5d9de;
83
84 const RGBA32 progressRegularBottom = caretTop;
85 const RGBA32 progressRegularTop = caretBottom;
86
87 const RGBA32 rangeSliderRegularBottom = 0xfff6f2ee;
88 const RGBA32 rangeSliderRegularTop = 0xffdee0e5;
89 const RGBA32 rangeSliderRollBottom = 0xffc9e8fe;
90 const RGBA32 rangeSliderRollTop = 0xffb5d3fc;
91
92 const RGBA32 rangeSliderRegularBottomOutline = 0xffb9babd;
93 const RGBA32 rangeSliderRegularTopOutline = 0xffb7b7b7;
94 const RGBA32 rangeSliderRollBottomOutline = 0xff67abe0;
95 const RGBA32 rangeSliderRollTopOutline = 0xff69adf9;
96
97 const RGBA32 dragRegularLight = 0xfffdfdfd;
98 const RGBA32 dragRegularDark = 0xffbababa;
99 const RGBA32 dragRollLight = 0xfff2f2f2;
100 const RGBA32 dragRollDark = 0xff69a8ff;
101
102 const RGBA32 selection = 0xff2b8fff;
103
104 const RGBA32 blackPen = Color::black;
105 const RGBA32 focusRingPen = 0xffa3c8fe;
106
107 float RenderThemeBlackBerry::defaultFontSize = 16;
108
109 // We aim to match IE here.
110 // -IE uses a font based on the encoding as the default font for form controls.
111 // -Gecko uses MS Shell Dlg (actually calls GetStockObject(DEFAULT_GUI_FONT),
112 // which returns MS Shell Dlg)
113 // -Safari uses Lucida Grande.
114 //
115 // FIXME: The only case where we know we don't match IE is for ANSI encodings.
116 // IE uses MS Shell Dlg there, which we render incorrectly at certain pixel
117 // sizes (e.g. 15px). So we just use Arial for now.
118 const String& RenderThemeBlackBerry::defaultGUIFont()
119 {
120     DEFINE_STATIC_LOCAL(String, fontFace, ("Arial"));
121     return fontFace;
122 }
123
124 static PassRefPtr<Gradient> createLinearGradient(RGBA32 top, RGBA32 bottom, const IntPoint& a, const IntPoint& b)
125 {
126     RefPtr<Gradient> gradient = Gradient::create(a, b);
127     gradient->addColorStop(0.0, Color(top));
128     gradient->addColorStop(1.0, Color(bottom));
129     return gradient.release();
130 }
131
132 static Path roundedRectForBorder(RenderObject* object, const IntRect& rect)
133 {
134     RenderStyle* style = object->style();
135     LengthSize topLeftRadius = style->borderTopLeftRadius();
136     LengthSize topRightRadius = style->borderTopRightRadius();
137     LengthSize bottomLeftRadius = style->borderBottomLeftRadius();
138     LengthSize bottomRightRadius = style->borderBottomRightRadius();
139
140     Path roundedRect;
141     roundedRect.addRoundedRect(rect, IntSize(topLeftRadius.width().value(), topLeftRadius.height().value()),
142                                      IntSize(topRightRadius.width().value(), topRightRadius.height().value()),
143                                      IntSize(bottomLeftRadius.width().value(), bottomLeftRadius.height().value()),
144                                      IntSize(bottomRightRadius.width().value(), bottomRightRadius.height().value()));
145     return roundedRect;
146 }
147
148 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
149 {
150     static RenderTheme* theme = RenderThemeBlackBerry::create().leakRef();
151     return theme;
152 }
153
154 PassRefPtr<RenderTheme> RenderThemeBlackBerry::create()
155 {
156     return adoptRef(new RenderThemeBlackBerry());
157 }
158
159 RenderThemeBlackBerry::RenderThemeBlackBerry()
160 {
161 }
162
163 RenderThemeBlackBerry::~RenderThemeBlackBerry()
164 {
165 }
166
167 String RenderThemeBlackBerry::extraDefaultStyleSheet()
168 {
169     return String(themeBlackBerryUserAgentStyleSheet, sizeof(themeBlackBerryUserAgentStyleSheet));
170 }
171
172 #if ENABLE(VIDEO)
173 String RenderThemeBlackBerry::extraMediaControlsStyleSheet()
174 {
175     return String(mediaControlsBlackBerryUserAgentStyleSheet, sizeof(mediaControlsBlackBerryUserAgentStyleSheet));
176 }
177
178 String RenderThemeBlackBerry::formatMediaControlsRemainingTime(float, float duration) const
179 {
180     // This is a workaround to make the appearance of media time controller in
181     // in-page mode the same as in fullscreen mode.
182     return formatMediaControlsTime(duration);
183 }
184 #endif
185
186 double RenderThemeBlackBerry::caretBlinkInterval() const
187 {
188     return 0; // Turn off caret blinking.
189 }
190
191 void RenderThemeBlackBerry::systemFont(int propId, FontDescription& fontDescription) const
192 {
193     float fontSize = defaultFontSize;
194
195     if (propId == CSSValueWebkitMiniControl || propId ==  CSSValueWebkitSmallControl || propId == CSSValueWebkitControl) {
196         // Why 2 points smaller? Because that's what Gecko does. Note that we
197         // are assuming a 96dpi screen, which is the default value we use on Windows.
198         static const float pointsPerInch = 72.0f;
199         static const float pixelsPerInch = 96.0f;
200         fontSize -= (2.0f / pointsPerInch) * pixelsPerInch;
201     }
202
203     fontDescription.firstFamily().setFamily(defaultGUIFont());
204     fontDescription.setSpecifiedSize(fontSize);
205     fontDescription.setIsAbsoluteSize(true);
206     fontDescription.setGenericFamily(FontDescription::NoFamily);
207     fontDescription.setWeight(FontWeightNormal);
208     fontDescription.setItalic(false);
209 }
210
211 void RenderThemeBlackBerry::setButtonStyle(RenderStyle* style) const
212 {
213     Length vertPadding(int(style->fontSize() / paddingDivisor), Fixed);
214     style->setPaddingTop(vertPadding);
215     style->setPaddingBottom(vertPadding);
216 }
217
218 void RenderThemeBlackBerry::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
219 {
220     setButtonStyle(style);
221     style->setCursor(CURSOR_WEBKIT_GRAB);
222 }
223
224 void RenderThemeBlackBerry::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
225 {
226     setButtonStyle(style);
227 }
228
229 bool RenderThemeBlackBerry::paintTextArea(RenderObject* object, const PaintInfo& info, const IntRect& rect)
230 {
231     return paintTextFieldOrTextAreaOrSearchField(object, info, rect);
232 }
233
234 void RenderThemeBlackBerry::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
235 {
236     setButtonStyle(style);
237 }
238
239 bool RenderThemeBlackBerry::paintTextFieldOrTextAreaOrSearchField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
240 {
241     ASSERT(info.context);
242     GraphicsContext* context = info.context;
243
244     context->save();
245     context->setStrokeStyle(SolidStroke);
246     context->setStrokeThickness(lineWidth);
247     if (!isEnabled(object))
248         context->setStrokeColor(disabledOutline, ColorSpaceDeviceRGB);
249     else if (isPressed(object))
250         info.context->setStrokeGradient(createLinearGradient(depressedTopOutline, depressedBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
251     else if (isHovered(object) || isFocused(object))
252         context->setStrokeGradient(createLinearGradient(hoverTopOutline, hoverBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
253     else
254         context->setStrokeGradient(createLinearGradient(regularTopOutline, regularBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
255
256     Path textFieldRoundedRectangle = roundedRectForBorder(object, rect);
257     if (object->style()->appearance() == SearchFieldPart) {
258         // We force the fill color to White so as to match the background color of the search cancel button graphic.
259         context->setFillColor(Color::white, ColorSpaceDeviceRGB);
260         context->fillPath(textFieldRoundedRectangle);
261         context->strokePath(textFieldRoundedRectangle);
262     } else
263         context->strokePath(textFieldRoundedRectangle);
264     context->restore();
265     return false;
266 }
267
268 bool RenderThemeBlackBerry::paintTextField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
269 {
270     return paintTextFieldOrTextAreaOrSearchField(object, info, rect);
271 }
272
273 void RenderThemeBlackBerry::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
274 {
275     setButtonStyle(style);
276 }
277
278 void RenderThemeBlackBerry::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
279 {
280     static const float defaultControlFontPixelSize = 13;
281     static const float defaultCancelButtonSize = 9;
282     static const float minCancelButtonSize = 5;
283     static const float maxCancelButtonSize = 21;
284
285     // Scale the button size based on the font size
286     float fontScale = style->fontSize() / defaultControlFontPixelSize;
287     int cancelButtonSize = lroundf(std::min(std::max(minCancelButtonSize, defaultCancelButtonSize * fontScale), maxCancelButtonSize));
288     Length length(cancelButtonSize, Fixed);
289     style->setWidth(length);
290     style->setHeight(length);
291 }
292
293 bool RenderThemeBlackBerry::paintSearchField(RenderObject* object, const PaintInfo& info, const IntRect& rect)
294 {
295     return paintTextFieldOrTextAreaOrSearchField(object, info, rect);
296 }
297
298 bool RenderThemeBlackBerry::paintSearchFieldCancelButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
299 {
300     ASSERT(object->parent());
301     if (!object->parent() || !object->parent()->isBox())
302         return false;
303
304     RenderBox* parentRenderBox = toRenderBox(object->parent());
305
306     IntRect parentBox = parentRenderBox->absoluteContentBox();
307     IntRect bounds = rect;
308     // Make sure the scaled button stays square and fits in its parent's box.
309     bounds.setHeight(std::min(parentBox.width(), std::min(parentBox.height(), bounds.height())));
310     bounds.setWidth(bounds.height());
311
312     // Put the button in the middle vertically, and round up the value.
313     // So if it has to be one pixel off-center, it would be one pixel closer
314     // to the bottom of the field. This would look better with the text.
315     bounds.setY(parentBox.y() + (parentBox.height() - bounds.height() + 1) / 2);
316
317     static Image* cancelImage = Image::loadPlatformResource("searchCancel").leakRef();
318     static Image* cancelPressedImage = Image::loadPlatformResource("searchCancelPressed").leakRef();
319     paintInfo.context->drawImage(isPressed(object) ? cancelPressedImage : cancelImage, object->style()->colorSpace(), bounds);
320     return false;
321 }
322
323 void RenderThemeBlackBerry::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
324 {
325     // These seem to be reasonable padding values from observation.
326     const int paddingLeft = 8;
327     const int paddingRight = 4;
328
329     const int minHeight = style->fontSize() * 2;
330
331     style->resetPadding();
332     style->setHeight(Length(Auto));
333
334     style->setPaddingRight(Length(minHeight + paddingRight, Fixed));
335     style->setPaddingLeft(Length(paddingLeft, Fixed));
336     style->setCursor(CURSOR_WEBKIT_GRAB);
337 }
338
339 void RenderThemeBlackBerry::calculateButtonSize(RenderStyle* style) const
340 {
341     int size = style->fontSize();
342     Length length(size, Fixed);
343     if (style->appearance() == CheckboxPart || style->appearance() == RadioPart) {
344         style->setWidth(length);
345         style->setHeight(length);
346         return;
347     }
348
349     // If the width and height are both specified, then we have nothing to do.
350     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
351         return;
352
353     if (style->width().isIntrinsicOrAuto())
354         style->setWidth(length);
355
356     if (style->height().isAuto())
357         style->setHeight(length);
358 }
359
360 bool RenderThemeBlackBerry::paintCheckbox(RenderObject* object, const PaintInfo& info, const IntRect& rect)
361 {
362     return paintButton(object, info, rect);
363 }
364
365 void RenderThemeBlackBerry::setCheckboxSize(RenderStyle* style) const
366 {
367     calculateButtonSize(style);
368 }
369
370 bool RenderThemeBlackBerry::paintRadio(RenderObject* object, const PaintInfo& info, const IntRect& rect)
371 {
372     return paintButton(object, info, rect);
373 }
374
375 void RenderThemeBlackBerry::setRadioSize(RenderStyle* style) const
376 {
377     calculateButtonSize(style);
378 }
379
380 // If this function returns false, WebCore assumes the button is fully decorated
381 bool RenderThemeBlackBerry::paintButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
382 {
383     ASSERT(info.context);
384     info.context->save();
385
386     info.context->setStrokeStyle(SolidStroke);
387     info.context->setStrokeThickness(lineWidth);
388
389     Color check(blackPen);
390     if (!isEnabled(object)) {
391         info.context->setFillGradient(createLinearGradient(disabledTop, disabledBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
392         info.context->setStrokeColor(disabledOutline, ColorSpaceDeviceRGB);
393     } else if (isPressed(object)) {
394         info.context->setFillGradient(createLinearGradient(depressedTop, depressedBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
395         info.context->setStrokeGradient(createLinearGradient(depressedTopOutline, depressedBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
396     } else if (isHovered(object)) {
397         info.context->setFillGradient(createLinearGradient(hoverTop, hoverBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
398         info.context->setStrokeGradient(createLinearGradient(hoverTopOutline, hoverBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
399     } else {
400         info.context->setFillGradient(createLinearGradient(regularTop, regularBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
401         info.context->setStrokeGradient(createLinearGradient(regularTopOutline, regularBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
402     }
403
404     ControlPart part = object->style()->appearance();
405     switch (part) {
406     case CheckboxPart: {
407         FloatSize smallCorner(smallRadius, smallRadius);
408         Path path;
409         path.addRoundedRect(rect, smallCorner);
410         info.context->fillPath(path);
411         info.context->strokePath(path);
412
413         if (isChecked(object)) {
414             Path checkPath;
415             IntRect rect2 = rect;
416             rect2.inflate(-1);
417             checkPath.moveTo(FloatPoint(rect2.x() + rect2.width() * checkboxLeftX, rect2.y() + rect2.height() * checkboxLeftY));
418             checkPath.addLineTo(FloatPoint(rect2.x() + rect2.width() * checkboxMiddleX, rect2.maxY() - rect2.height() * checkboxMiddleY));
419             checkPath.addLineTo(FloatPoint(rect2.x() + rect2.width() * checkboxRightX, rect2.y() + rect2.height() * checkboxRightY));
420             info.context->setLineCap(RoundCap);
421             info.context->setStrokeColor(blackPen, ColorSpaceDeviceRGB);
422             info.context->setStrokeThickness(rect2.width() / checkboxStrokeThickness);
423             info.context->fillPath(checkPath);
424             info.context->strokePath(checkPath);
425         }
426         break;
427     }
428     case RadioPart:
429         info.context->drawEllipse(rect);
430         if (isChecked(object)) {
431             IntRect rect2 = rect;
432             rect2.inflate(-rect.width() * radioButtonCheckStateScaler);
433             info.context->setFillColor(check, ColorSpaceDeviceRGB);
434             info.context->setStrokeColor(check, ColorSpaceDeviceRGB);
435             info.context->drawEllipse(rect2);
436         }
437         break;
438     case ButtonPart:
439     case PushButtonPart: {
440         FloatSize largeCorner(largeRadius, largeRadius);
441         Path path;
442         path.addRoundedRect(rect, largeCorner);
443         info.context->fillPath(path);
444         info.context->strokePath(path);
445         break;
446     }
447     case SquareButtonPart: {
448         Path path;
449         path.addRect(rect);
450         info.context->fillPath(path);
451         info.context->strokePath(path);
452         break;
453     }
454     default:
455         info.context->restore();
456         return true;
457     }
458
459     info.context->restore();
460     return false;
461 }
462
463 void RenderThemeBlackBerry::adjustMenuListStyle(CSSStyleSelector* css, RenderStyle* style, Element* element) const
464 {
465     adjustMenuListButtonStyle(css, style, element);
466 }
467
468 void RenderThemeBlackBerry::adjustCheckboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
469 {
470     setCheckboxSize(style);
471     style->setBoxShadow(nullptr);
472     Length margin(marginSize, Fixed);
473     style->setMarginBottom(margin);
474     style->setMarginRight(margin);
475     style->setCursor(CURSOR_WEBKIT_GRAB);
476 }
477
478 void RenderThemeBlackBerry::adjustRadioStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
479 {
480     setRadioSize(style);
481     style->setBoxShadow(nullptr);
482     Length margin(marginSize, Fixed);
483     style->setMarginBottom(margin);
484     style->setMarginRight(margin);
485     style->setCursor(CURSOR_WEBKIT_GRAB);
486 }
487
488 void RenderThemeBlackBerry::paintMenuListButtonGradientAndArrow(GraphicsContext* context, RenderObject* object, IntRect buttonRect, const Path& clipPath)
489 {
490     ASSERT(context);
491     context->save();
492     if (!isEnabled(object))
493         context->setFillGradient(createLinearGradient(disabledTop, disabledBottom, buttonRect.maxXMinYCorner(), buttonRect.maxXMaxYCorner()));
494     else if (isPressed(object))
495         context->setFillGradient(createLinearGradient(depressedTop, depressedBottom, buttonRect.maxXMinYCorner(), buttonRect.maxXMaxYCorner()));
496     else if (isHovered(object))
497         context->setFillGradient(createLinearGradient(hoverTop, hoverBottom, buttonRect.maxXMinYCorner(), buttonRect.maxXMaxYCorner()));
498     else
499         context->setFillGradient(createLinearGradient(regularTop, regularBottom, buttonRect.maxXMinYCorner(), buttonRect.maxXMaxYCorner()));
500
501     // 1. Paint the background of the button.
502     context->clip(clipPath);
503     context->drawRect(buttonRect);
504     context->restore();
505
506     // 2. Paint the button arrow.
507     buttonRect.inflate(-buttonRect.width() / 3);
508     buttonRect.move(0, buttonRect.height() * 7 / 20);
509     Path path;
510     path.moveTo(FloatPoint(buttonRect.x(), buttonRect.y()));
511     path.addLineTo(FloatPoint(buttonRect.x() + buttonRect.width(), buttonRect.y()));
512     path.addLineTo(FloatPoint(buttonRect.x() + buttonRect.width() / 2.0, buttonRect.y() + buttonRect.height() / 2.0));
513     path.closeSubpath();
514
515     context->save();
516     context->setStrokeStyle(SolidStroke);
517     context->setStrokeThickness(lineWidth);
518     context->setStrokeColor(Color::black, ColorSpaceDeviceRGB);
519     context->setFillColor(Color::black, ColorSpaceDeviceRGB);
520     context->setLineJoin(BevelJoin);
521     context->fillPath(path);
522     context->restore();
523 }
524
525 static IntRect computeMenuListArrowButtonRect(const IntRect& rect)
526 {
527     // FIXME: The menu list arrow button should have a minimum and maximum width (to ensure usability) or
528     // scale with respect to the font size used in the menu list control or some combination of both.
529     return IntRect(IntPoint(rect.maxX() - rect.height(), rect.y()), IntSize(rect.height(), rect.height()));
530 }
531
532 static void paintMenuListBackground(GraphicsContext* context, const Path& menuListPath, const Color& backgroundColor)
533 {
534     ASSERT(context);
535     context->save();
536     context->setFillColor(backgroundColor, ColorSpaceDeviceRGB);
537     context->fillPath(menuListPath);
538     context->restore();
539 }
540
541 bool RenderThemeBlackBerry::paintMenuList(RenderObject* object, const PaintInfo& info, const IntRect& rect)
542 {
543     // Note, this method is not called if the menu list explicitly specifies either a border or background color.
544     // Instead, RenderThemeBlackBerry::paintMenuListButton is called. Therefore, when this method is called, we don't
545     // have to adjust rect with respect to the border dimensions.
546
547     ASSERT(info.context);
548     GraphicsContext* context = info.context;
549
550     Path menuListRoundedRectangle = roundedRectForBorder(object, rect);
551
552     // 1. Paint the background of the entire control.
553     paintMenuListBackground(context, menuListRoundedRectangle, Color::white);
554
555     // 2. Paint the background of the button and its arrow.
556     IntRect arrowButtonRectangle = computeMenuListArrowButtonRect(rect);
557     paintMenuListButtonGradientAndArrow(context, object, arrowButtonRectangle, menuListRoundedRectangle);
558
559     // 4. Stroke an outline around the entire control.
560     context->save();
561     context->setStrokeStyle(SolidStroke);
562     context->setStrokeThickness(lineWidth);
563     if (!isEnabled(object))
564         context->setStrokeColor(disabledOutline, ColorSpaceDeviceRGB);
565     else if (isPressed(object))
566         context->setStrokeGradient(createLinearGradient(depressedTopOutline, depressedBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
567     else if (isHovered(object))
568         context->setStrokeGradient(createLinearGradient(hoverTopOutline, hoverBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
569     else
570         context->setStrokeGradient(createLinearGradient(regularTopOutline, regularBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
571
572     context->strokePath(menuListRoundedRectangle);
573     context->restore();
574     return false;
575 }
576
577 bool RenderThemeBlackBerry::paintMenuListButton(RenderObject* object, const PaintInfo& info, const IntRect& rect)
578 {
579     // Note, this method is only called if the menu list explicitly specifies either a border or background color.
580     // Otherwise, RenderThemeBlackBerry::paintMenuList is called. We need to fit the arrow button with the border box
581     // of the menu-list so as to not occlude the custom border.
582
583     // We compute menuListRoundedRectangle with respect to the dimensions of the entire menu-list control (i.e. rect) and
584     // its border radius so that we clip the contour of the arrow button (when we paint it below) to match the contour of
585     // the control.
586     Path menuListRoundedRectangle = roundedRectForBorder(object, rect);
587
588     // 1. Paint the background of the entire control.
589     Color fillColor = object->style()->visitedDependentColor(CSSPropertyBackgroundColor);
590     if (!fillColor.isValid())
591         fillColor = Color::white;
592     paintMenuListBackground(info.context, menuListRoundedRectangle, fillColor);
593
594     // 2. Paint the background of the button and its arrow.
595     IntRect bounds = IntRect(rect.x() + object->style()->borderLeftWidth(),
596                          rect.y() + object->style()->borderTopWidth(),
597                          rect.width() - object->style()->borderLeftWidth() - object->style()->borderRightWidth(),
598                          rect.height() - object->style()->borderTopWidth() - object->style()->borderBottomWidth());
599
600     IntRect arrowButtonRectangle = computeMenuListArrowButtonRect(bounds); // Fit the arrow button within the border box of the menu-list.
601     paintMenuListButtonGradientAndArrow(info.context, object, arrowButtonRectangle, menuListRoundedRectangle);
602     return false;
603 }
604
605 void RenderThemeBlackBerry::adjustSliderThumbSize(RenderStyle* style) const
606 {
607     ControlPart part = style->appearance();
608     if (part == MediaVolumeSliderThumbPart || part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
609         style->setWidth(Length(part == SliderThumbVerticalPart ? sliderThumbHeight : sliderThumbWidth, Fixed));
610         style->setHeight(Length(part == SliderThumbVerticalPart ? sliderThumbWidth : sliderThumbHeight, Fixed));
611     } else if (part == MediaSliderThumbPart) {
612         style->setWidth(Length(mediaSliderThumbWidth, Fixed));
613         style->setHeight(Length(mediaSliderThumbHeight, Fixed));
614     }
615 }
616
617 bool RenderThemeBlackBerry::paintSliderTrack(RenderObject* object, const PaintInfo& info, const IntRect& rect)
618 {
619     const static int SliderTrackHeight = 5;
620     IntRect rect2;
621     if (object->style()->appearance() == SliderHorizontalPart) {
622         rect2.setHeight(SliderTrackHeight);
623         rect2.setWidth(rect.width());
624         rect2.setX(rect.x());
625         rect2.setY(rect.y() + (rect.height() - SliderTrackHeight) / 2);
626     } else {
627         rect2.setHeight(rect.height());
628         rect2.setWidth(SliderTrackHeight);
629         rect2.setX(rect.x() + (rect.width() - SliderTrackHeight) / 2);
630         rect2.setY(rect.y());
631     }
632     return paintSliderTrackRect(object, info, rect2);
633 }
634
635 bool RenderThemeBlackBerry::paintSliderTrackRect(RenderObject* object, const PaintInfo& info, const IntRect& rect)
636 {
637     return paintSliderTrackRect(object, info, rect, rangeSliderRegularTopOutline, rangeSliderRegularBottomOutline,
638                 rangeSliderRegularTop, rangeSliderRegularBottom);
639 }
640
641 bool RenderThemeBlackBerry::paintSliderTrackRect(RenderObject* object, const PaintInfo& info, const IntRect& rect,
642         RGBA32 strokeColorStart, RGBA32 strokeColorEnd, RGBA32 fillColorStart, RGBA32 fillColorEnd)
643 {
644     FloatSize smallCorner(smallRadius, smallRadius);
645
646     info.context->save();
647     info.context->setStrokeStyle(SolidStroke);
648     info.context->setStrokeThickness(lineWidth);
649
650     info.context->setStrokeGradient(createLinearGradient(strokeColorStart, strokeColorEnd, rect.maxXMinYCorner(), rect. maxXMaxYCorner()));
651     info.context->setFillGradient(createLinearGradient(fillColorStart, fillColorEnd, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
652
653     Path path;
654     path.addRoundedRect(rect, smallCorner);
655     info.context->fillPath(path);
656
657     info.context->restore();
658     return false;
659 }
660
661 bool RenderThemeBlackBerry::paintSliderThumb(RenderObject* object, const PaintInfo& info, const IntRect& rect)
662 {
663     FloatSize largeCorner(largeRadius, largeRadius);
664
665     info.context->save();
666     info.context->setStrokeStyle(SolidStroke);
667     info.context->setStrokeThickness(lineWidth);
668
669     if (isPressed(object) || isHovered(object)) {
670         info.context->setStrokeGradient(createLinearGradient(hoverTopOutline, hoverBottomOutline, rect.maxXMinYCorner(), rect. maxXMaxYCorner()));
671         info.context->setFillGradient(createLinearGradient(hoverTop, hoverBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
672     } else {
673         info.context->setStrokeGradient(createLinearGradient(regularTopOutline, regularBottomOutline, rect.maxXMinYCorner(), rect. maxXMaxYCorner()));
674         info.context->setFillGradient(createLinearGradient(regularTop, regularBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
675     }
676
677     Path path;
678     path.addRoundedRect(rect, largeCorner);
679     info.context->fillPath(path);
680
681     bool isVertical = rect.width() > rect.height();
682     IntPoint startPoint(rect.x() + (isVertical ? 5 : 2), rect.y() + (isVertical ? 2 : 5));
683     IntPoint endPoint(rect.x() + (isVertical ? 20 : 2), rect.y() + (isVertical ? 2 : 20));
684     const int lineOffset = 2;
685     const int shadowOffset = 1;
686
687     for (int i = 0; i < 3; i++) {
688         if (isVertical) {
689             startPoint.setY(startPoint.y() + lineOffset);
690             endPoint.setY(endPoint.y() + lineOffset);
691         } else {
692             startPoint.setX(startPoint.x() + lineOffset);
693             endPoint.setX(endPoint.x() + lineOffset);
694         }
695         if (isPressed(object) || isHovered(object))
696             info.context->setStrokeColor(dragRollLight, ColorSpaceDeviceRGB);
697         else
698             info.context->setStrokeColor(dragRegularLight, ColorSpaceDeviceRGB);
699         info.context->drawLine(startPoint, endPoint);
700
701         if (isVertical) {
702             startPoint.setY(startPoint.y() + shadowOffset);
703             endPoint.setY(endPoint.y() + shadowOffset);
704         } else {
705             startPoint.setX(startPoint.x() + shadowOffset);
706             endPoint.setX(endPoint.x() + shadowOffset);
707         }
708         if (isPressed(object) || isHovered(object))
709             info.context->setStrokeColor(dragRollDark, ColorSpaceDeviceRGB);
710         else
711             info.context->setStrokeColor(dragRegularDark, ColorSpaceDeviceRGB);
712         info.context->drawLine(startPoint, endPoint);
713     }
714     info.context->restore();
715     return false;
716 }
717
718 static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Image* image)
719 {
720     context->drawImage(image, ColorSpaceDeviceRGB, rect);
721     return false;
722 }
723
724 bool RenderThemeBlackBerry::paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
725 {
726 #if ENABLE(VIDEO)
727     HTMLMediaElement* mediaElement = toParentMediaElement(object);
728
729     if (!mediaElement)
730         return false;
731
732     static Image* mediaPlay = Image::loadPlatformResource("play").leakRef();
733     static Image* mediaPause = Image::loadPlatformResource("pause").leakRef();
734
735     return paintMediaButton(paintInfo.context, rect, mediaElement->canPlay() ? mediaPlay : mediaPause);
736 #else
737     UNUSED_PARAM(object);
738     UNUSED_PARAM(paintInfo);
739     UNUSED_PARAM(rect);
740     return false;
741 #endif
742 }
743
744 bool RenderThemeBlackBerry::paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
745 {
746 #if ENABLE(VIDEO)
747     HTMLMediaElement* mediaElement = toParentMediaElement(object);
748
749     if (!mediaElement)
750         return false;
751
752     static Image* mediaMute = Image::loadPlatformResource("speaker").leakRef();
753     static Image* mediaUnmute = Image::loadPlatformResource("speaker_mute").leakRef();
754
755     return paintMediaButton(paintInfo.context, rect, mediaElement->muted() || !mediaElement->volume() ? mediaUnmute : mediaMute);
756 #else
757     UNUSED_PARAM(object);
758     UNUSED_PARAM(paintInfo);
759     UNUSED_PARAM(rect);
760     return false;
761 #endif
762 }
763
764 bool RenderThemeBlackBerry::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
765 {
766 #if ENABLE(VIDEO)
767     if (!toParentMediaElement(object))
768         return false;
769
770     static Image* mediaFullscreen = Image::loadPlatformResource("fullscreen").leakRef();
771
772     return paintMediaButton(paintInfo.context, rect, mediaFullscreen);
773 #else
774     UNUSED_PARAM(object);
775     UNUSED_PARAM(paintInfo);
776     UNUSED_PARAM(rect);
777     return false;
778 #endif
779 }
780
781 bool RenderThemeBlackBerry::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
782 {
783 #if ENABLE(VIDEO)
784     IntRect rect2(rect.x() + 3, rect.y() + 14, rect.width() - 6, 2);
785
786     HTMLMediaElement* mediaElement = toParentMediaElement(object);
787
788     if (!mediaElement)
789         return false;
790
791     float loaded = 0;
792     // FIXME: replace loaded with commented out one when buffer bug is fixed (see comment in
793     // MediaPlayerPrivateMMrenderer::percentLoaded).
794     // loaded = mediaElement->percentLoaded();
795     if (mediaElement->player())
796         loaded = static_cast<MediaPlayerPrivate *>(mediaElement->player()->implementation())->percentLoaded();
797     float position = mediaElement->duration() > 0 ? (mediaElement->currentTime() / mediaElement->duration()) : 0;
798
799     int x = rect.x() + 3;
800     int y = rect.y() + 14;
801     int w = rect.width() - 6;
802     int h = 2;
803
804     int wPlayed = (w * position);
805     int wLoaded = (w - mediaSliderThumbWidth) * loaded + mediaSliderThumbWidth;
806
807     IntRect played(x, y, wPlayed, h);
808     IntRect buffered(x, y, wLoaded, h);
809
810     // This is to paint main slider bar.
811     bool result = paintSliderTrackRect(object, paintInfo, rect2);
812
813     if (loaded > 0 || position > 0) {
814         // This is to paint buffered bar.
815         paintSliderTrackRect(object, paintInfo, buffered, Color::darkGray, Color::darkGray, Color::darkGray, Color::darkGray);
816
817         // This is to paint played part of bar (left of slider thumb) using selection color.
818         paintSliderTrackRect(object, paintInfo, played, selection, selection, selection, selection);
819     }
820     return result;
821
822 #else
823     UNUSED_PARAM(object);
824     UNUSED_PARAM(paintInfo);
825     UNUSED_PARAM(rect);
826     return false;
827 #endif
828 }
829
830 bool RenderThemeBlackBerry::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
831 {
832 #if ENABLE(VIDEO)
833     if (!object->parent())
834         return false;
835
836     RenderSlider* slider = toRenderSlider(object->parent()->parent()->parent());
837     if (!slider)
838         return false;
839
840     paintInfo.context->save();
841     Path mediaThumbRoundedRectangle;
842     mediaThumbRoundedRectangle.addRoundedRect(rect, FloatSize(mediaSliderThumbRadius, mediaSliderThumbRadius));
843     paintInfo.context->setStrokeStyle(SolidStroke);
844     paintInfo.context->setStrokeThickness(0.5);
845     paintInfo.context->setStrokeColor(Color::black, ColorSpaceDeviceRGB);
846
847     if (isPressed(object) || isHovered(object) || slider->inDragMode()) {
848         paintInfo.context->setFillGradient(createLinearGradient(selection, Color(selection).dark().rgb(),
849                 rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
850     } else {
851         paintInfo.context->setFillGradient(createLinearGradient(Color::white, Color(Color::white).dark().rgb(),
852                 rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
853     }
854     paintInfo.context->fillPath(mediaThumbRoundedRectangle);
855     paintInfo.context->restore();
856
857     return true;
858 #else
859     UNUSED_PARAM(object);
860     UNUSED_PARAM(paintInfo);
861     UNUSED_PARAM(rect);
862     return false;
863 #endif
864 }
865
866 bool RenderThemeBlackBerry::paintMediaVolumeSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
867 {
868 #if ENABLE(VIDEO)
869     float pad = rect.width() * 0.45;
870     float x = rect.x() + pad;
871     float y = rect.y() + pad;
872     float width = rect.width() * 0.1;
873     float height = rect.height() - (2.0 * pad);
874
875     IntRect rect2(x, y, width, height);
876
877     return paintSliderTrackRect(object, paintInfo, rect2);
878 #else
879     UNUSED_PARAM(object);
880     UNUSED_PARAM(paintInfo);
881     UNUSED_PARAM(rect);
882     return false;
883 #endif
884 }
885
886 bool RenderThemeBlackBerry::paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
887 {
888 #if ENABLE(VIDEO)
889     static Image* mediaVolumeThumb = Image::loadPlatformResource("volume_thumb").leakRef();
890
891     return paintMediaButton(paintInfo.context, rect, mediaVolumeThumb);
892 #else
893     UNUSED_PARAM(object);
894     UNUSED_PARAM(paintInfo);
895     UNUSED_PARAM(rect);
896     return false;
897 #endif
898 }
899
900 Color RenderThemeBlackBerry::platformFocusRingColor() const
901 {
902     return focusRingPen;
903 }
904
905 #if ENABLE(TOUCH_EVENTS)
906 Color RenderThemeBlackBerry::platformTapHighlightColor() const
907 {
908     // Same color as 'focusRingPen' + 80 of alpha channel.
909     return Color(163, 200, 254, 80);
910 }
911 #endif
912
913 Color RenderThemeBlackBerry::platformActiveSelectionBackgroundColor() const
914 {
915     return Color(selection);
916 }
917
918 double RenderThemeBlackBerry::animationRepeatIntervalForProgressBar(RenderProgress* renderProgress) const
919 {
920     return renderProgress->isDeterminate() ? 0.0 : 0.1;
921 }
922
923 double RenderThemeBlackBerry::animationDurationForProgressBar(RenderProgress* renderProgress) const
924 {
925     return renderProgress->isDeterminate() ? 0.0 : 2.0;
926 }
927
928 bool RenderThemeBlackBerry::paintProgressBar(RenderObject* object, const PaintInfo& info, const IntRect& rect)
929 {
930     if (!object->isProgress())
931         return true;
932
933     RenderProgress* renderProgress = toRenderProgress(object);
934
935     FloatSize smallCorner(smallRadius, smallRadius);
936
937     info.context->save();
938     info.context->setStrokeStyle(SolidStroke);
939     info.context->setStrokeThickness(lineWidth);
940
941     info.context->setStrokeGradient(createLinearGradient(rangeSliderRegularTopOutline, rangeSliderRegularBottomOutline, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
942     info.context->setFillGradient(createLinearGradient(rangeSliderRegularTop, rangeSliderRegularBottom, rect.maxXMinYCorner(), rect.maxXMaxYCorner()));
943
944     Path path;
945     path.addRoundedRect(rect, smallCorner);
946     info.context->fillPath(path);
947
948     IntRect rect2 = rect;
949     rect2.setX(rect2.x() + 1);
950     rect2.setHeight(rect2.height() - 2);
951     rect2.setY(rect2.y() + 1);
952     info.context->setStrokeStyle(NoStroke);
953     info.context->setStrokeThickness(0);
954     if (renderProgress->isDeterminate()) {
955         rect2.setWidth(rect2.width() * renderProgress->position() - 2);
956         info.context->setFillGradient(createLinearGradient(progressRegularTop, progressRegularBottom, rect2.maxXMinYCorner(), rect2.maxXMaxYCorner()));
957     } else {
958         // Animating
959         rect2.setWidth(rect2.width() - 2);
960         RefPtr<Gradient> gradient = Gradient::create(rect2.minXMaxYCorner(), rect2.maxXMaxYCorner());
961         gradient->addColorStop(0.0, Color(progressRegularBottom));
962         gradient->addColorStop(renderProgress->animationProgress(), Color(progressRegularTop));
963         gradient->addColorStop(1.0, Color(progressRegularBottom));
964         info.context->setFillGradient(gradient);
965     }
966     Path path2;
967     path2.addRoundedRect(rect2, smallCorner);
968     info.context->fillPath(path2);
969
970     info.context->restore();
971     return false;
972 }
973
974 Color RenderThemeBlackBerry::platformActiveTextSearchHighlightColor() const
975 {
976     return Color(255, 150, 50); // Orange.
977 }
978
979 Color RenderThemeBlackBerry::platformInactiveTextSearchHighlightColor() const
980 {
981     return Color(255, 255, 0); // Yellow.
982 }
983
984 } // namespace WebCore