e23f8d1002771dc085fedd25d4c80afbd021bc88
[WebKit-https.git] / WebCore / platform / qt / RenderThemeQt.cpp
1 /*
2  * This file is part of the WebKit project.
3  *
4  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
5  *               2006 Dirk Mueller <mueller@kde.org>
6  *               2006 Nikolas Zimmermann <zimmermann@kde.org>
7  *
8  * All rights reserved.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  *
25  */
26
27 #include "config.h"
28
29 #include "qwebpage.h"
30 #include "RenderThemeQt.h"
31 #include "ChromeClientQt.h"
32 #include "NotImplemented.h"
33
34 #include <QApplication>
35 #include <QColor>
36 #include <QDebug>
37 #include <QStyle>
38 #include <QWidget>
39 #include <QPainter>
40 #include <QStyleOptionButton>
41 #include <QStyleOptionFrameV2>
42
43 #include "Color.h"
44 #include "Document.h"
45 #include "Page.h"
46 #include "Font.h"
47 #include "RenderTheme.h"
48 #include "GraphicsContext.h"
49
50 namespace WebCore {
51
52 RenderTheme* theme()
53 {
54     static RenderThemeQt rt;
55     return &rt;
56 }
57
58 RenderThemeQt::RenderThemeQt()
59     : RenderTheme()
60 {
61 }
62
63 bool RenderThemeQt::supportsHover(const RenderStyle*) const
64 {
65     return true;
66 }
67
68 bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const
69 {
70     return supportsFocus(style->appearance());
71 }
72
73 short RenderThemeQt::baselinePosition(const RenderObject* o) const
74 {
75     if (o->style()->appearance() == CheckboxAppearance ||
76         o->style()->appearance() == RadioAppearance)
77         return o->marginTop() + o->height() - 2; // Same as in old khtml
78     return RenderTheme::baselinePosition(o);
79 }
80
81 bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const
82 {
83     if (!isEnabled(o))
84         return false;
85
86     // Checkboxes only have tint when checked.
87     if (o->style()->appearance() == CheckboxAppearance)
88         return isChecked(o);
89
90     // For now assume other controls have tint if enabled.
91     return true;
92 }
93
94 bool RenderThemeQt::supportsControlTints() const
95 {
96     return true;
97 }
98
99 void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& r)
100 {
101     switch (o->style()->appearance()) {
102     case CheckboxAppearance: {
103         break;
104     }
105     case RadioAppearance: {
106         break;
107     }
108     case PushButtonAppearance:
109     case ButtonAppearance: {
110         break;
111     }
112     case MenulistAppearance: {
113         break;
114     }
115     default:
116         break;
117     }
118 }
119
120 bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border,
121                                      const BackgroundLayer& background, const Color& backgroundColor) const
122 {
123     if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance || style->appearance() == ListboxAppearance)
124         return style->border() != border;
125
126     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
127 }
128
129 Color RenderThemeQt::platformActiveSelectionBackgroundColor() const
130 {
131     QPalette pal = QApplication::palette();
132     return pal.brush(QPalette::Active, QPalette::Highlight).color();
133 }
134
135 Color RenderThemeQt::platformInactiveSelectionBackgroundColor() const
136 {
137     QPalette pal = QApplication::palette();
138     return pal.brush(QPalette::Inactive, QPalette::Highlight).color();
139 }
140
141 Color RenderThemeQt::platformActiveSelectionForegroundColor() const
142 {
143     QPalette pal = QApplication::palette();
144     return pal.brush(QPalette::Active, QPalette::HighlightedText).color();
145 }
146
147 Color RenderThemeQt::platformInactiveSelectionForegroundColor() const
148 {
149     QPalette pal = QApplication::palette();
150     return pal.brush(QPalette::Inactive, QPalette::HighlightedText).color();
151 }
152
153 void RenderThemeQt::systemFont(int propId, FontDescription& fontDescription) const
154 {
155     // no-op
156 }
157
158 int RenderThemeQt::minimumMenuListSize(RenderStyle*) const
159 {
160     const QFontMetrics &fm = QApplication::fontMetrics();
161     return 7 * fm.width(QLatin1Char('x'));
162 }
163
164 void RenderThemeQt::adjustSliderThumbSize(RenderObject* o) const
165 {
166     RenderTheme::adjustSliderThumbSize(o);
167 }
168
169 bool RenderThemeQt::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
170 {
171     return paintButton(o, i, r);
172 }
173
174 void RenderThemeQt::setCheckboxSize(RenderStyle* style) const
175 {
176     // If the width and height are both specified, then we have nothing to do.
177     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
178         return;
179
180     // FIXME:  A hard-coded size of 13 is used.  This is wrong but necessary for now.  It matches Firefox.
181     // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
182     // the higher DPI.  Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
183     // metrics.
184     const int ff = 13;
185     if (style->width().isIntrinsicOrAuto())
186         style->setWidth(Length(ff, Fixed));
187
188     if (style->height().isAuto())
189         style->setHeight(Length(ff, Fixed));
190 }
191
192 bool RenderThemeQt::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
193 {
194     return paintButton(o, i, r);
195 }
196
197 void RenderThemeQt::setRadioSize(RenderStyle* style) const
198 {
199     // This is the same as checkboxes.
200     setCheckboxSize(style);
201 }
202
203 void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
204 {
205     // Ditch the border.
206     style->resetBorder();
207
208     // Height is locked to auto.
209     style->setHeight(Length(Auto));
210
211     // White-space is locked to pre
212     style->setWhiteSpace(PRE);
213
214     setButtonSize(style);
215
216     setButtonPadding(style);
217 }
218
219 bool RenderThemeQt::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
220 {
221     QStyle* style = 0;
222     QPainter* painter = 0;
223     QWidget* widget = 0;
224
225     if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget))
226         return true;
227
228     QStyleOptionButton option;
229     if (widget)
230         option.initFrom(widget);
231     option.rect = r;
232
233     // Get the correct theme data for a button
234     EAppearance appearance = applyTheme(option, o);
235
236     if(appearance == PushButtonAppearance || appearance == ButtonAppearance)
237         style->drawControl(QStyle::CE_PushButton, &option, painter);
238     else if(appearance == RadioAppearance)
239         style->drawPrimitive(QStyle::PE_IndicatorRadioButton, &option, painter, widget);
240     else if(appearance == CheckboxAppearance)
241         style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter, widget);
242
243     return false;
244 }
245
246 void RenderThemeQt::setButtonSize(RenderStyle* style) const
247 {
248     setPrimitiveSize(style);
249 }
250
251 bool RenderThemeQt::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
252 {
253     QStyle* style = 0;
254     QPainter* painter = 0;
255     QWidget* widget = 0;
256
257     if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget))
258         return true;
259
260     QStyleOptionFrameV2 panel;
261     if (widget)
262         panel.initFrom(widget);
263     panel.rect = r;
264     panel.state |= QStyle::State_Sunken;
265     panel.features = QStyleOptionFrameV2::None;
266
267     // Get the correct theme data for a button
268     EAppearance appearance = applyTheme(panel, o);
269     Q_ASSERT(appearance == TextFieldAppearance || appearance == SearchFieldAppearance);
270
271     // Now paint the text field.
272     style->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, painter, widget);
273     style->drawPrimitive(QStyle::PE_FrameLineEdit, &panel, painter, widget);
274       
275     return false;
276 }
277
278 void RenderThemeQt::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
279 {
280 }
281
282 void RenderThemeQt::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
283 {
284     style->resetBorder();
285
286     // Height is locked to auto.
287     style->setHeight(Length(Auto));
288
289     // White-space is locked to pre
290     style->setWhiteSpace(PRE);
291
292     setPrimitiveSize(style);
293
294     // Add in the padding that we'd like to use.
295     setPopupPadding(style);
296
297     // Our font is locked to the appropriate system font size for the control.  To clarify, we first use the CSS-specified font to figure out
298     // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
299     // system font for the control size instead.
300     //setFontFromControlSize(selector, style);
301 }
302
303 bool RenderThemeQt::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
304 {
305     QStyle* style = 0;
306     QPainter* painter = 0;
307     QWidget* widget = 0;
308
309     if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget))
310         return true;
311
312     QStyleOptionComboBox opt;
313     if (widget)
314         opt.initFrom(widget);
315     EAppearance appearance = applyTheme(opt, o);
316     const QPoint topLeft = r.topLeft();
317     painter->translate(topLeft);
318     opt.rect.moveTo(QPoint(0,0));
319     opt.rect.setSize(r.size());
320
321     opt.frame = false;
322
323     style->drawComplexControl(QStyle::CC_ComboBox, &opt, painter, widget);
324     painter->translate(-topLeft);
325     return false;
326 }
327
328
329 bool RenderThemeQt::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& pi,
330                                         const IntRect& r)
331 {
332     notImplemented();
333     return RenderTheme::paintMenuListButton(o, pi, r);
334 }
335
336 void RenderThemeQt::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style,
337                                               Element* e) const
338 {
339     notImplemented();
340     RenderTheme::adjustMenuListButtonStyle(selector, style, e);
341 }
342
343 bool RenderThemeQt::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& pi,
344                                      const IntRect& r)
345 {
346     notImplemented();
347     return RenderTheme::paintSliderTrack(o, pi, r);
348 }
349
350 bool RenderThemeQt::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& pi,
351                                      const IntRect& r)
352 {
353     notImplemented();
354     return RenderTheme::paintSliderThumb(o, pi, r);
355 }
356
357 bool RenderThemeQt::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& pi,
358                                      const IntRect& r)
359 {
360     paintTextField(o, pi, r);
361     return false;
362 }
363
364 void RenderThemeQt::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style,
365                                            Element* e) const
366 {
367     notImplemented();
368     RenderTheme::adjustSearchFieldStyle(selector, style, e);
369 }
370
371 void RenderThemeQt::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style,
372                                                        Element* e) const
373 {
374     notImplemented();
375     RenderTheme::adjustSearchFieldCancelButtonStyle(selector, style, e);
376 }
377
378 bool RenderThemeQt::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& pi,
379                                                  const IntRect& r)
380 {
381     notImplemented();
382     return RenderTheme::paintSearchFieldCancelButton(o, pi, r);
383 }
384
385 void RenderThemeQt::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
386                                                      Element* e) const
387 {
388     notImplemented();
389     RenderTheme::adjustSearchFieldDecorationStyle(selector, style, e);
390 }
391
392 bool RenderThemeQt::paintSearchFieldDecoration(RenderObject* o, const RenderObject::PaintInfo& pi,
393                                                const IntRect& r)
394 {
395     notImplemented();
396     return RenderTheme::paintSearchFieldDecoration(o, pi, r);
397 }
398
399 void RenderThemeQt::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
400                                                             Element* e) const
401 {
402     notImplemented();
403     RenderTheme::adjustSearchFieldResultsDecorationStyle(selector, style, e);
404 }
405
406 bool RenderThemeQt::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& pi,
407                                                       const IntRect& r)
408 {
409     notImplemented();
410     return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r);
411 }
412
413 bool RenderThemeQt::supportsFocus(EAppearance appearance) const
414 {
415     switch (appearance) {
416         case PushButtonAppearance:
417         case ButtonAppearance:
418         case TextFieldAppearance:
419         case MenulistAppearance:
420         case RadioAppearance:
421         case CheckboxAppearance:
422             return true;
423         default: // No for all others...
424             return false;
425     }
426 }
427
428 bool RenderThemeQt::getStylePainterAndWidgetFromPaintInfo(const RenderObject::PaintInfo& i, QStyle*& style,
429                                                           QPainter*& painter, QWidget*& widget) const
430 {
431     painter = (i.context ? static_cast<QPainter*>(i.context->platformContext()) : 0);
432     widget = 0;
433     QPaintDevice* dev = 0;
434     if (painter)
435         dev = painter->device();
436     if (dev && dev->devType() == QInternal::Widget)
437         widget = static_cast<QWidget*>(dev);
438     style = (widget ? widget->style() : QApplication::style());
439
440     return (painter && style);
441 }
442
443 EAppearance RenderThemeQt::applyTheme(QStyleOption& option, RenderObject* o) const
444 {
445     // Default bits: no focus, no mouse over
446     option.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
447
448     if (!isEnabled(o))
449         option.state &= ~QStyle::State_Enabled;
450
451     if (isReadOnlyControl(o))
452         // Readonly is supported on textfields.
453         option.state |= QStyle::State_ReadOnly;
454
455     if (supportsFocus(o->style()->appearance()) && isFocused(o))
456         option.state |= QStyle::State_HasFocus;
457
458     if (isHovered(o))
459         option.state |= QStyle::State_MouseOver;
460
461     EAppearance result = o->style()->appearance();
462
463     switch (result) {
464         case PushButtonAppearance:
465         case SquareButtonAppearance:
466         case ButtonAppearance:
467         case ButtonBevelAppearance:
468         case ListItemAppearance:
469         case MenulistButtonAppearance:
470         case ScrollbarButtonLeftAppearance:
471         case ScrollbarButtonRightAppearance:
472         case ScrollbarTrackHorizontalAppearance:
473         case ScrollbarTrackVerticalAppearance:
474         case ScrollbarThumbHorizontalAppearance:
475         case ScrollbarThumbVerticalAppearance:
476         case SearchFieldResultsButtonAppearance:
477         case SearchFieldCancelButtonAppearance: {
478             if (isPressed(o))
479                 option.state |= QStyle::State_Sunken;
480             else if (result == PushButtonAppearance)
481                 option.state |= QStyle::State_Raised;
482             break;
483         }
484     }
485
486     if(result == RadioAppearance || result == CheckboxAppearance)
487         option.state |= (isChecked(o) ? QStyle::State_On : QStyle::State_Off);
488
489     // If the webview has a custom palette, use it
490     Page* page = o->document()->page();
491     if (page) {
492         QWidget* view = static_cast<ChromeClientQt*>(page->chrome()->client())->m_webPage->view();
493         if (view)
494             option.palette = view->palette();
495     }
496
497     return result;
498 }
499
500 void RenderThemeQt::setSizeFromFont(RenderStyle* style) const
501 {
502     // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
503      IntSize size = sizeForFont(style);
504     if (style->width().isIntrinsicOrAuto() && size.width() > 0)
505         style->setWidth(Length(size.width(), Fixed));
506     if (style->height().isAuto() && size.height() > 0)
507         style->setHeight(Length(size.height(), Fixed));
508 }
509
510 IntSize RenderThemeQt::sizeForFont(RenderStyle* style) const
511 {
512     const QFontMetrics fm(style->font().font());
513     QSize size(0, 0);
514     switch (style->appearance()) {
515     case CheckboxAppearance: {
516         break;
517     }
518     case RadioAppearance: {
519         break;
520     }
521     case PushButtonAppearance:
522     case ButtonAppearance: {
523         QSize sz = fm.size(Qt::TextShowMnemonic, QString::fromLatin1("X"));
524         QStyleOptionButton opt;
525         sz = QApplication::style()->sizeFromContents(QStyle::CT_PushButton,
526                                                      &opt, sz, 0);
527         size.setHeight(sz.height());
528         break;
529     }
530     case MenulistAppearance: {
531         QSize sz;
532         sz.setHeight(qMax(fm.lineSpacing(), 14) + 2);
533         QStyleOptionComboBox opt;
534         sz = QApplication::style()->sizeFromContents(QStyle::CT_ComboBox,
535                                                      &opt, sz, 0);
536         size.setHeight(sz.height());
537         break;
538     }
539     case TextFieldAppearance: {
540         const int verticalMargin   = 1;
541         const int horizontalMargin = 2;
542         int h = qMax(fm.lineSpacing(), 14) + 2*verticalMargin;
543         int w = fm.width(QLatin1Char('x')) * 17 + 2*horizontalMargin;
544         QStyleOptionFrameV2 opt;
545         opt.lineWidth = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth,
546                                                            &opt, 0);
547         QSize sz = QApplication::style()->sizeFromContents(QStyle::CT_LineEdit,
548                                                            &opt,
549                                                            QSize(w, h).expandedTo(QApplication::globalStrut()),
550                                                            0);
551         size.setHeight(sz.height());
552         break;
553     }
554     default:
555         break;
556     }
557     return size;
558 }
559
560 void RenderThemeQt::setButtonPadding(RenderStyle* style) const
561 {
562     const int padding = 8;
563     style->setPaddingLeft(Length(padding, Fixed));
564     style->setPaddingRight(Length(padding, Fixed));
565     style->setPaddingTop(Length(0, Fixed));
566     style->setPaddingBottom(Length(0, Fixed));
567 }
568
569 void RenderThemeQt::setPopupPadding(RenderStyle* style) const
570 {
571     const int padding = 8;
572     style->setPaddingLeft(Length(padding, Fixed));
573     QStyleOptionComboBox opt;
574     int w = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize, &opt, 0);
575     style->setPaddingRight(Length(padding + w, Fixed));
576
577     style->setPaddingTop(Length(1, Fixed));
578     style->setPaddingBottom(Length(0, Fixed));
579 }
580
581 void RenderThemeQt::setPrimitiveSize(RenderStyle* style) const
582 {
583     // If the width and height are both specified, then we have nothing to do.
584     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
585         return;
586
587     // Use the font size to determine the intrinsic width of the control.
588     setSizeFromFont(style);
589 }
590
591 }
592
593 // vim: ts=4 sw=4 et