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