Fix rendering of lineedits and remove the spurious
[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., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  *
25  */
26
27 #include "config.h"
28
29 #include "RenderThemeQt.h"
30
31 #include <QApplication>
32 #include <QColor>
33 #include <QDebug>
34 #include <QStyle>
35 #include <QWidget>
36 #include <QPainter>
37 #include <QStyleOptionButton>
38 #include <QStyleOptionFrameV2>
39
40 #include "Color.h"
41 #include "Document.h"
42 #include "Font.h"
43 #include "RenderTheme.h"
44 #include "GraphicsContext.h"
45
46 namespace WebCore {
47
48 RenderTheme* theme()
49 {
50     static RenderThemeQt rt;
51     return &rt;
52 }
53
54 RenderThemeQt::RenderThemeQt()
55     : RenderTheme()
56 {
57 }
58
59 bool RenderThemeQt::supportsHover(const RenderStyle*) const
60 {
61     return true;
62 }
63
64 bool RenderThemeQt::supportsFocusRing(const RenderStyle* style) const
65 {
66     return true;
67 }
68
69 short RenderThemeQt::baselinePosition(const RenderObject* o) const
70 {
71     if (o->style()->appearance() == CheckboxAppearance ||
72         o->style()->appearance() == RadioAppearance)
73         return o->marginTop() + o->height() - 2; // Same as in old khtml
74     return RenderTheme::baselinePosition(o);
75 }
76
77 bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const
78 {
79     if (!isEnabled(o))
80         return false;
81
82     // Checkboxes only have tint when checked.
83     if (o->style()->appearance() == CheckboxAppearance)
84         return isChecked(o);
85
86     // For now assume other controls have tint if enabled.
87     return true;
88 }
89
90 bool RenderThemeQt::supportsControlTints() const
91 {
92     return true;
93 }
94
95 void RenderThemeQt::adjustRepaintRect(const RenderObject* o, IntRect& r)
96 {
97     switch (o->style()->appearance()) {
98     case CheckboxAppearance: {
99         break;
100     }
101     case RadioAppearance: {
102         break;
103     }
104     case PushButtonAppearance:
105     case ButtonAppearance: {
106         break;
107     }
108     case MenulistAppearance: {
109         break;
110     }
111     default:
112         break;
113     }
114 }
115
116 bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border,
117                                      const BackgroundLayer& background, const Color& backgroundColor) const
118 {
119     if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance)
120         return style->border() != border;
121
122     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
123 }
124
125 void RenderThemeQt::paintResizeControl(GraphicsContext*, const IntRect&)
126 {
127 }
128
129
130 Color RenderThemeQt::platformActiveSelectionBackgroundColor() const
131 {
132     QPalette pal = QApplication::palette();
133     return pal.brush(QPalette::Active, QPalette::Highlight).color();
134 }
135
136 Color RenderThemeQt::platformInactiveSelectionBackgroundColor() const
137 {
138     QPalette pal = QApplication::palette();
139     return pal.brush(QPalette::Inactive, QPalette::Highlight).color();
140 }
141
142 Color RenderThemeQt::platformActiveSelectionForegroundColor() const
143 {
144     QPalette pal = QApplication::palette();
145     return pal.brush(QPalette::Active, QPalette::HighlightedText).color();
146 }
147
148 Color RenderThemeQt::platformInactiveSelectionForegroundColor() const
149 {
150     QPalette pal = QApplication::palette();
151     return pal.brush(QPalette::Inactive, QPalette::HighlightedText).color();
152 }
153
154 void RenderThemeQt::systemFont(int propId, FontDescription& fontDescription) const
155 {
156     // no-op
157 }
158
159 int RenderThemeQt::minimumMenuListSize(RenderStyle*) const
160 {
161     const QFontMetrics &fm = QApplication::fontMetrics();
162     return 7 * fm.width(QLatin1Char('x'));
163 }
164
165 void RenderThemeQt::adjustSliderThumbSize(RenderObject* o) const
166 {
167     RenderTheme::adjustSliderThumbSize(o);
168 }
169
170 bool RenderThemeQt::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
171 {
172     return paintButton(o, i, r);
173 }
174
175 void RenderThemeQt::setCheckboxSize(RenderStyle* style) const
176 {
177     // If the width and height are both specified, then we have nothing to do.
178     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
179         return;
180
181     // FIXME:  A hard-coded size of 13 is used.  This is wrong but necessary for now.  It matches Firefox.
182     // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
183     // the higher DPI.  Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
184     // metrics.
185 #ifdef Q_WS_MAC
186     const int ff = 16;
187 #else
188     const int ff = 13;
189 #endif
190     if (style->width().isIntrinsicOrAuto())
191         style->setWidth(Length(ff, Fixed));
192
193     if (style->height().isAuto())
194         style->setHeight(Length(ff, Fixed));
195 }
196
197 bool RenderThemeQt::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
198 {
199     return paintButton(o, i, r);
200 }
201
202 void RenderThemeQt::setRadioSize(RenderStyle* style) const
203 {
204     // This is the same as checkboxes.
205     setCheckboxSize(style);
206 }
207
208 void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
209 {
210     // Ditch the border.
211     style->resetBorder();
212
213     // Height is locked to auto.
214     style->setHeight(Length(Auto));
215
216     // White-space is locked to pre
217     style->setWhiteSpace(PRE);
218
219     setButtonSize(style);
220
221     setButtonPadding(style);
222 }
223
224 bool RenderThemeQt::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
225 {
226     QStyle* style = 0;
227     QPainter* painter = 0;
228     QWidget* widget = 0;
229
230     if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget))
231         return true;
232
233     QStyleOptionButton option;
234     option.initFrom(widget);
235     option.rect = r;
236
237     // Get the correct theme data for a button
238     EAppearance appearance = applyTheme(option, o);
239
240     if(appearance == PushButtonAppearance || appearance == ButtonAppearance)
241         style->drawControl(QStyle::CE_PushButton, &option, painter);
242     else if(appearance == RadioAppearance)
243         style->drawControl(QStyle::CE_RadioButton, &option, painter);
244     else if(appearance == CheckboxAppearance)
245         style->drawControl(QStyle::CE_CheckBox, &option, painter);
246
247     return false;
248 }
249
250 void RenderThemeQt::setButtonSize(RenderStyle* style) const
251 {
252     setPrimitiveSize(style);
253 }
254
255 bool RenderThemeQt::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
256 {
257     QStyle* style = 0;
258     QPainter* painter = 0;
259     QWidget* widget = 0;
260
261     if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget))
262         return true;
263
264     QStyleOptionFrameV2 panel;
265     panel.initFrom(widget);
266     panel.rect = r;
267     // Get the correct theme data for a button
268     EAppearance appearance = applyTheme(panel, o);
269     Q_ASSERT(appearance == TextFieldAppearance);
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     opt.initFrom(widget);
314     opt.rect = r;
315     opt.frame = false;
316
317     style->drawComplexControl(QStyle::CC_ComboBox, &opt, painter, widget);
318     return false;
319 }
320
321
322 bool RenderThemeQt::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& pi,
323                                         const IntRect& r)
324 {
325     return RenderTheme::paintMenuListButton(o, pi, r);
326 }
327
328 void RenderThemeQt::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style,
329                                               Element* e) const
330 {
331     RenderTheme::adjustMenuListButtonStyle(selector, style, e);
332 }
333
334 bool RenderThemeQt::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& pi,
335                                      const IntRect& r)
336 {
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     return RenderTheme::paintSliderThumb(o, pi, r);
344 }
345
346 bool RenderThemeQt::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& pi,
347                                      const IntRect& r)
348 {
349     return RenderTheme::paintSearchField(o, pi, r);
350 }
351
352 void RenderThemeQt::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style,
353                                            Element* e) const
354 {
355     RenderTheme::adjustSearchFieldStyle(selector, style, e);
356 }
357
358 void RenderThemeQt::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style,
359                                                        Element* e) const
360 {
361     RenderTheme::adjustSearchFieldCancelButtonStyle(selector, style, e);
362 }
363
364 bool RenderThemeQt::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& pi,
365                                                  const IntRect& r)
366 {
367     return RenderTheme::paintSearchFieldCancelButton(o, pi, r);
368 }
369
370 void RenderThemeQt::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
371                                                      Element* e) const
372 {
373     RenderTheme::adjustSearchFieldDecorationStyle(selector, style, e);
374 }
375
376 bool RenderThemeQt::paintSearchFieldDecoration(RenderObject* o, const RenderObject::PaintInfo& pi,
377                                                const IntRect& r)
378 {
379     return RenderTheme::paintSearchFieldDecoration(o, pi, r);
380 }
381
382 void RenderThemeQt::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style,
383                                                             Element* e) const
384 {
385     RenderTheme::adjustSearchFieldResultsDecorationStyle(selector, style, e);
386 }
387
388 bool RenderThemeQt::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& pi,
389                                                       const IntRect& r)
390 {
391     return RenderTheme::paintSearchFieldResultsDecoration(o, pi, r);
392 }
393
394 bool RenderThemeQt::supportsFocus(EAppearance appearance) const
395 {
396     switch (appearance) {
397         case PushButtonAppearance:
398         case ButtonAppearance:
399         case TextFieldAppearance:
400         case MenulistAppearance:
401             return true;
402         default: // No for all others...
403             return false;
404     }
405 }
406
407 bool RenderThemeQt::getStylePainterAndWidgetFromPaintInfo(const RenderObject::PaintInfo& i, QStyle*& style,
408                                                           QPainter*& painter, QWidget*& widget) const
409 {
410     painter = (i.context ? static_cast<QPainter*>(i.context->platformContext()) : 0);
411     widget = (painter ? static_cast<QWidget*>(painter->device()) : 0);
412     style = (widget ? widget->style() : 0);
413
414     return (painter && widget && style);
415 }
416
417 EAppearance RenderThemeQt::applyTheme(QStyleOption& option, RenderObject* o) const
418 {
419     // Default bits: no focus, no mouse over
420     option.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
421
422     if (!isEnabled(o))
423         option.state &= ~QStyle::State_Enabled;
424
425     if (isReadOnlyControl(o))
426         // Readonly is supported on textfields.
427         option.state |= QStyle::State_ReadOnly;
428
429     if (supportsFocus(o->style()->appearance()) && isFocused(o))
430         option.state |= QStyle::State_HasFocus;
431
432     if (isHovered(o))
433         option.state |= QStyle::State_MouseOver;
434
435     EAppearance result = o->style()->appearance();
436
437     if (isPressed(o))
438         option.state |= QStyle::State_Sunken;
439     else if (result == PushButtonAppearance)
440         option.state |= QStyle::State_Raised;
441
442     if(result == RadioAppearance || result == CheckboxAppearance)
443         option.state |= (isChecked(o) ? QStyle::State_On : QStyle::State_Off);
444
445     return result;
446 }
447
448 void RenderThemeQt::setSizeFromFont(RenderStyle* style) const
449 {
450     // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
451      IntSize size = sizeForFont(style);
452     if (style->width().isIntrinsicOrAuto() && size.width() > 0)
453         style->setWidth(Length(size.width(), Fixed));
454     if (style->height().isAuto() && size.height() > 0)
455         style->setHeight(Length(size.height(), Fixed));
456 }
457
458 IntSize RenderThemeQt::sizeForFont(RenderStyle* style) const
459 {
460     const QFontMetrics fm(style->font());
461     QSize size(0, 0);
462     switch (style->appearance()) {
463     case CheckboxAppearance: {
464         break;
465     }
466     case RadioAppearance: {
467         break;
468     }
469     case PushButtonAppearance:
470     case ButtonAppearance: {
471         QSize sz = fm.size(Qt::TextShowMnemonic, QString::fromLatin1("X"));
472         QStyleOptionButton opt;
473         sz = QApplication::style()->sizeFromContents(QStyle::CT_PushButton,
474                                                      &opt, sz, 0);
475         size.setHeight(sz.height());
476         break;
477     }
478     case MenulistAppearance: {
479         QSize sz;
480         sz.setHeight(qMax(fm.lineSpacing(), 14) + 2);
481         QStyleOptionComboBox opt;
482         sz = QApplication::style()->sizeFromContents(QStyle::CT_ComboBox,
483                                                      &opt, sz, 0);
484         size.setHeight(sz.height());
485         break;
486     }
487     case TextFieldAppearance: {
488         const int verticalMargin   = 1;
489         const int horizontalMargin = 2;
490         int h = qMax(fm.lineSpacing(), 14) + 2*verticalMargin;
491         int w = fm.width(QLatin1Char('x')) * 17 + 2*horizontalMargin;
492         QStyleOptionFrameV2 opt;
493         opt.lineWidth = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth,
494                                                            &opt, 0);
495         QSize sz = QApplication::style()->sizeFromContents(QStyle::CT_LineEdit,
496                                                            &opt,
497                                                            QSize(w, h).expandedTo(QApplication::globalStrut()),
498                                                            0);
499         size.setHeight(sz.height());
500         break;
501     }
502     default:
503         break;
504     }
505     return size;
506 }
507
508 void RenderThemeQt::setButtonPadding(RenderStyle* style) const
509 {
510     const int padding = 8;
511     style->setPaddingLeft(Length(padding, Fixed));
512     style->setPaddingRight(Length(padding, Fixed));
513     style->setPaddingTop(Length(0, Fixed));
514     style->setPaddingBottom(Length(0, Fixed));
515 }
516
517 void RenderThemeQt::setPopupPadding(RenderStyle* style) const
518 {
519     const int padding = 8;
520     style->setPaddingLeft(Length(padding, Fixed));
521     style->setPaddingRight(Length(padding, Fixed));
522     style->setPaddingTop(Length(1, Fixed));
523     style->setPaddingBottom(Length(0, Fixed));
524 }
525
526 void RenderThemeQt::setPrimitiveSize(RenderStyle* style) const
527 {
528     // If the width and height are both specified, then we have nothing to do.
529     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
530         return;
531
532     // Use the font size to determine the intrinsic width of the control.
533     setSizeFromFont(style);
534 }
535
536 }
537
538 // vim: ts=4 sw=4 et