028ffba6094fc8ece827dd9e084958515839597c
[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.lineWidth = style->pixelMetric(QStyle::PM_DefaultFrameWidth, &panel, widget);
265     panel.state |= QStyle::State_Sunken;
266     panel.features = QStyleOptionFrameV2::None;
267
268     // Get the correct theme data for a button
269     EAppearance appearance = applyTheme(panel, o);
270     Q_ASSERT(appearance == TextFieldAppearance || appearance == SearchFieldAppearance);
271
272     // Now paint the text field.
273     style->drawPrimitive(QStyle::PE_PanelLineEdit, &panel, painter, widget);
274     style->drawPrimitive(QStyle::PE_FrameLineEdit, &panel, painter, widget);
275       
276     return false;
277 }
278
279 void RenderThemeQt::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
280 {
281 }
282
283 void RenderThemeQt::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
284 {
285     style->resetBorder();
286
287     // Height is locked to auto.
288     style->setHeight(Length(Auto));
289
290     // White-space is locked to pre
291     style->setWhiteSpace(PRE);
292
293     setPrimitiveSize(style);
294
295     // Add in the padding that we'd like to use.
296     setPopupPadding(style);
297
298     // 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
299     // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
300     // system font for the control size instead.
301     //setFontFromControlSize(selector, style);
302 }
303
304 bool RenderThemeQt::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
305 {
306     QStyle* style = 0;
307     QPainter* painter = 0;
308     QWidget* widget = 0;
309
310     if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget))
311         return true;
312
313     QStyleOptionComboBox opt;
314     if (widget)
315         opt.initFrom(widget);
316     EAppearance appearance = applyTheme(opt, o);
317     const QPoint topLeft = r.topLeft();
318     painter->translate(topLeft);
319     opt.rect.moveTo(QPoint(0,0));
320     opt.rect.setSize(r.size());
321
322     opt.frame = false;
323
324     style->drawComplexControl(QStyle::CC_ComboBox, &opt, painter, widget);
325     painter->translate(-topLeft);
326     return false;
327 }
328
329
330 bool RenderThemeQt::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& pi,
331                                         const IntRect& r)
332 {
333     notImplemented();
334     return RenderTheme::paintMenuListButton(o, pi, r);
335 }
336
337 void RenderThemeQt::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style,
338                                               Element* e) const
339 {
340     notImplemented();
341     RenderTheme::adjustMenuListButtonStyle(selector, style, e);
342 }
343
344 bool RenderThemeQt::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& pi,
345                                      const IntRect& r)
346 {
347     notImplemented();
348     return RenderTheme::paintSliderTrack(o, pi, r);
349 }
350
351 bool RenderThemeQt::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& pi,
352                                      const IntRect& r)
353 {
354     notImplemented();
355     return RenderTheme::paintSliderThumb(o, pi, r);
356 }
357
358 bool RenderThemeQt::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& pi,
359                                      const IntRect& r)
360 {
361     paintTextField(o, pi, r);
362     return false;
363 }
364
365 void RenderThemeQt::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style,
366                                            Element* e) const
367 {
368     notImplemented();
369     RenderTheme::adjustSearchFieldStyle(selector, style, e);
370 }
371
372 void RenderThemeQt::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style,
373                                                        Element* e) const
374 {
375     notImplemented();
376     RenderTheme::adjustSearchFieldCancelButtonStyle(selector, style, e);
377 }
378
379 bool RenderThemeQt::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& pi,
380                                                  const IntRect& r)
381 {
382     notImplemented();
383     return RenderTheme::paintSearchFieldCancelButton(o, pi, r);
384 }
385
386 void RenderThemeQt::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
387                                                      Element* e) const
388 {
389     notImplemented();
390     RenderTheme::adjustSearchFieldDecorationStyle(selector, style, e);
391 }
392
393 bool RenderThemeQt::paintSearchFieldDecoration(RenderObject* o, const RenderObject::PaintInfo& pi,
394                                                const IntRect& r)
395 {
396     notImplemented();
397     return RenderTheme::paintSearchFieldDecoration(o, pi, r);
398 }
399
400 void RenderThemeQt::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
401                                                             Element* e) const
402 {
403     notImplemented();
404     RenderTheme::adjustSearchFieldResultsDecorationStyle(selector, style, e);
405 }
406
407 bool RenderThemeQt::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& pi,
408                                                       const IntRect& r)
409 {
410     notImplemented();
411     return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r);
412 }
413
414 bool RenderThemeQt::supportsFocus(EAppearance appearance) const
415 {
416     switch (appearance) {
417         case PushButtonAppearance:
418         case ButtonAppearance:
419         case TextFieldAppearance:
420         case MenulistAppearance:
421         case RadioAppearance:
422         case CheckboxAppearance:
423             return true;
424         default: // No for all others...
425             return false;
426     }
427 }
428
429 bool RenderThemeQt::getStylePainterAndWidgetFromPaintInfo(const RenderObject::PaintInfo& i, QStyle*& style,
430                                                           QPainter*& painter, QWidget*& widget) const
431 {
432     painter = (i.context ? static_cast<QPainter*>(i.context->platformContext()) : 0);
433     widget = 0;
434     QPaintDevice* dev = 0;
435     if (painter)
436         dev = painter->device();
437     if (dev && dev->devType() == QInternal::Widget)
438         widget = static_cast<QWidget*>(dev);
439     style = (widget ? widget->style() : QApplication::style());
440
441     return (painter && style);
442 }
443
444 EAppearance RenderThemeQt::applyTheme(QStyleOption& option, RenderObject* o) const
445 {
446     // Default bits: no focus, no mouse over
447     option.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
448
449     if (!isEnabled(o))
450         option.state &= ~QStyle::State_Enabled;
451
452     if (isReadOnlyControl(o))
453         // Readonly is supported on textfields.
454         option.state |= QStyle::State_ReadOnly;
455
456     if (supportsFocus(o->style()->appearance()) && isFocused(o))
457         option.state |= QStyle::State_HasFocus;
458
459     if (isHovered(o))
460         option.state |= QStyle::State_MouseOver;
461
462     EAppearance result = o->style()->appearance();
463
464     switch (result) {
465         case PushButtonAppearance:
466         case SquareButtonAppearance:
467         case ButtonAppearance:
468         case ButtonBevelAppearance:
469         case ListItemAppearance:
470         case MenulistButtonAppearance:
471         case ScrollbarButtonLeftAppearance:
472         case ScrollbarButtonRightAppearance:
473         case ScrollbarTrackHorizontalAppearance:
474         case ScrollbarTrackVerticalAppearance:
475         case ScrollbarThumbHorizontalAppearance:
476         case ScrollbarThumbVerticalAppearance:
477         case SearchFieldResultsButtonAppearance:
478         case SearchFieldCancelButtonAppearance: {
479             if (isPressed(o))
480                 option.state |= QStyle::State_Sunken;
481             else if (result == PushButtonAppearance)
482                 option.state |= QStyle::State_Raised;
483             break;
484         }
485     }
486
487     if(result == RadioAppearance || result == CheckboxAppearance)
488         option.state |= (isChecked(o) ? QStyle::State_On : QStyle::State_Off);
489
490     // If the webview has a custom palette, use it
491     Page* page = o->document()->page();
492     if (page) {
493         QWidget* view = static_cast<ChromeClientQt*>(page->chrome()->client())->m_webPage->view();
494         if (view)
495             option.palette = view->palette();
496     }
497
498     return result;
499 }
500
501 void RenderThemeQt::setSizeFromFont(RenderStyle* style) const
502 {
503     // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
504      IntSize size = sizeForFont(style);
505     if (style->width().isIntrinsicOrAuto() && size.width() > 0)
506         style->setWidth(Length(size.width(), Fixed));
507     if (style->height().isAuto() && size.height() > 0)
508         style->setHeight(Length(size.height(), Fixed));
509 }
510
511 IntSize RenderThemeQt::sizeForFont(RenderStyle* style) const
512 {
513     const QFontMetrics fm(style->font().font());
514     QSize size(0, 0);
515     switch (style->appearance()) {
516     case CheckboxAppearance: {
517         break;
518     }
519     case RadioAppearance: {
520         break;
521     }
522     case PushButtonAppearance:
523     case ButtonAppearance: {
524         QSize sz = fm.size(Qt::TextShowMnemonic, QString::fromLatin1("X"));
525         QStyleOptionButton opt;
526         sz = QApplication::style()->sizeFromContents(QStyle::CT_PushButton,
527                                                      &opt, sz, 0);
528         size.setHeight(sz.height());
529         break;
530     }
531     case MenulistAppearance: {
532         QSize sz;
533         sz.setHeight(qMax(fm.lineSpacing(), 14) + 2);
534         QStyleOptionComboBox opt;
535         sz = QApplication::style()->sizeFromContents(QStyle::CT_ComboBox,
536                                                      &opt, sz, 0);
537         size.setHeight(sz.height());
538         break;
539     }
540     case TextFieldAppearance: {
541         const int verticalMargin   = 1;
542         const int horizontalMargin = 2;
543         int h = qMax(fm.lineSpacing(), 14) + 2*verticalMargin;
544         int w = fm.width(QLatin1Char('x')) * 17 + 2*horizontalMargin;
545         QStyleOptionFrameV2 opt;
546         opt.lineWidth = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth,
547                                                            &opt, 0);
548         QSize sz = QApplication::style()->sizeFromContents(QStyle::CT_LineEdit,
549                                                            &opt,
550                                                            QSize(w, h).expandedTo(QApplication::globalStrut()),
551                                                            0);
552         size.setHeight(sz.height());
553         break;
554     }
555     default:
556         break;
557     }
558     return size;
559 }
560
561 void RenderThemeQt::setButtonPadding(RenderStyle* style) const
562 {
563     const int padding = 8;
564     style->setPaddingLeft(Length(padding, Fixed));
565     style->setPaddingRight(Length(padding, Fixed));
566     style->setPaddingTop(Length(0, Fixed));
567     style->setPaddingBottom(Length(0, Fixed));
568 }
569
570 void RenderThemeQt::setPopupPadding(RenderStyle* style) const
571 {
572     const int padding = 8;
573     style->setPaddingLeft(Length(padding, Fixed));
574     QStyleOptionComboBox opt;
575     int w = QApplication::style()->pixelMetric(QStyle::PM_ButtonIconSize, &opt, 0);
576     style->setPaddingRight(Length(padding + w, Fixed));
577
578     style->setPaddingTop(Length(1, Fixed));
579     style->setPaddingBottom(Length(0, Fixed));
580 }
581
582 void RenderThemeQt::setPrimitiveSize(RenderStyle* style) const
583 {
584     // If the width and height are both specified, then we have nothing to do.
585     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
586         return;
587
588     // Use the font size to determine the intrinsic width of the control.
589     setSizeFromFont(style);
590 }
591
592 }
593
594 // vim: ts=4 sw=4 et