Reviewed and landed by Maciej.
[WebKit-https.git] / WebCore / platform / qt / RenderThemeQt.cpp
1 /*
2  * This file is part of the WebKit project.
3  *
4  * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
5  *               2006 Nikolas Zimmermann <zimmermann@kde.org>
6  *
7  * All rights reserved.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  *
24  */
25
26 #include "config.h"
27
28 #include <QStyle>
29 #include <QWidget>
30 #include <QPainter>
31 #include <QStyleOptionButton>
32
33 #include "Document.h"
34 #include "RenderTheme.h"
35 #include "GraphicsContext.h"
36
37 namespace WebCore {
38
39 class RenderThemeQt : public RenderTheme
40 {
41 public:
42     RenderThemeQt() : RenderTheme() { }
43
44     // A method asking if the theme's controls actually care about redrawing when hovered.
45     virtual bool supportsHover(const RenderStyle*) const { return true; }
46
47     virtual bool paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
48     {
49         return paintButton(o, i, r);
50     }
51  
52     virtual void setCheckboxSize(RenderStyle*) const;
53
54     virtual bool paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
55     {
56         return paintButton(o, i, r);
57     }
58
59     virtual void setRadioSize(RenderStyle*) const;
60
61     virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
62     virtual bool paintButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&);
63
64     virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const;
65     virtual bool paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&);
66
67     virtual bool isControlStyled(const RenderStyle*, const BorderData&,
68                                  const BackgroundLayer&, const Color&) const;
69
70     virtual bool controlSupportsTints(const RenderObject*) const;
71
72     virtual void systemFont(int propId, FontDescription&) const;
73     
74 private:
75     void addIntrinsicMargins(RenderStyle*) const;
76     void close();
77
78     bool supportsFocus(EAppearance) const;
79
80     bool getStylePainterAndWidgetFromPaintInfo(const RenderObject::PaintInfo&, QStyle*&, QPainter*&, QWidget*&) const;
81     EAppearance applyTheme(QStyleOption&, RenderObject*) const;
82 };
83
84 RenderTheme* theme()
85 {
86     static RenderThemeQt rt;
87     return &rt;
88 }
89
90 bool RenderThemeQt::isControlStyled(const RenderStyle* style, const BorderData& border,
91                                      const BackgroundLayer& background, const Color& backgroundColor) const
92 {
93     if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance)
94         return style->border() != border;
95
96     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
97 }
98
99 bool RenderThemeQt::controlSupportsTints(const RenderObject* o) const
100 {
101     if (!isEnabled(o))
102         return false;
103
104     // Checkboxes only have tint when checked.
105     if (o->style()->appearance() == CheckboxAppearance)
106         return isChecked(o);
107
108     // For now assume other controls have tint if enabled.
109     return true;
110 }
111
112 void RenderThemeQt::systemFont(int propId, FontDescription& fontDescription) const
113 {
114     // no-op
115 }
116
117 void RenderThemeQt::addIntrinsicMargins(RenderStyle* style) const
118 {
119     // Cut out the intrinsic margins completely if we end up using a small font size
120     if (style->fontSize() < 11)
121         return;
122
123     // Intrinsic margin value.
124     const int m = 2;
125
126     // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
127     if (style->width().isIntrinsicOrAuto()) {
128         if (style->marginLeft().quirk())
129             style->setMarginLeft(Length(m, Fixed));
130
131         if (style->marginRight().quirk())
132             style->setMarginRight(Length(m, Fixed));
133     }
134
135     if (style->height().isAuto()) {
136         if (style->marginTop().quirk())
137             style->setMarginTop(Length(m, Fixed));
138
139         if (style->marginBottom().quirk())
140             style->setMarginBottom(Length(m, Fixed));
141     }
142 }
143
144 bool RenderThemeQt::getStylePainterAndWidgetFromPaintInfo(const RenderObject::PaintInfo& i, QStyle*& style, QPainter*& painter, QWidget*& widget) const
145 {
146     painter = (i.context ? static_cast<QPainter*>(i.context->platformContext()) : 0);
147     widget = (painter ? static_cast<QWidget*>(painter->device()) : 0);
148     style = (widget ? widget->style() : 0);
149
150     return (painter && widget && style);
151 }
152
153 void RenderThemeQt::setCheckboxSize(RenderStyle* style) const
154 {
155     // If the width and height are both specified, then we have nothing to do.
156     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
157         return;
158
159     // FIXME:  A hard-coded size of 13 is used.  This is wrong but necessary for now.  It matches Firefox.
160     // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
161     // the higher DPI.  Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
162     // metrics.
163     if (style->width().isIntrinsicOrAuto())
164         style->setWidth(Length(13, Fixed));
165
166     if (style->height().isAuto())
167         style->setHeight(Length(13, Fixed));
168 }
169
170 void RenderThemeQt::setRadioSize(RenderStyle* style) const
171 {
172     // This is the same as checkboxes.
173     setCheckboxSize(style);
174 }
175
176 bool RenderThemeQt::supportsFocus(EAppearance appearance) const
177 {
178     switch (appearance) {
179         case PushButtonAppearance:
180         case ButtonAppearance:
181         case TextFieldAppearance:
182             return true;
183         default: // No for all others...
184             return false;
185     }
186 }
187
188 EAppearance RenderThemeQt::applyTheme(QStyleOption& option, RenderObject* o) const
189 {
190     // Default bits: no focus, no mouse over
191     option.state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
192
193     if (!isEnabled(o))
194         option.state &= ~QStyle::State_Enabled;
195
196     if (isReadOnlyControl(o))
197         // Readonly is supported on textfields.
198         option.state |= QStyle::State_ReadOnly;
199
200     if (supportsFocus(o->style()->appearance()) && isFocused(o))
201         option.state |= QStyle::State_HasFocus;
202
203     if (isHovered(o))
204         option.state |= QStyle::State_MouseOver;
205
206     if (isPressed(o))
207         option.state |= QStyle::State_Sunken;
208
209     EAppearance result = o->style()->appearance();
210     if(result == RadioAppearance || result == CheckboxAppearance)
211         option.state |= (isChecked(o) ? QStyle::State_On : QStyle::State_Off);
212
213     return result;
214 }
215
216 void RenderThemeQt::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
217 {
218     addIntrinsicMargins(style);
219 }
220
221 bool RenderThemeQt::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
222 {
223     QStyle* style = 0;
224     QPainter* painter = 0;
225     QWidget* widget = 0;
226     
227     if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget))
228         return true;
229     
230     QStyleOptionButton option;
231     option.initFrom(widget);
232     option.rect = r;
233
234     // Get the correct theme data for a button
235     EAppearance appearance = applyTheme(option, o);
236   
237     if(appearance == PushButtonAppearance || appearance == ButtonAppearance)
238         style->drawControl(QStyle::CE_PushButton, &option, painter);
239     else if(appearance == RadioAppearance)
240         style->drawControl(QStyle::CE_RadioButton, &option, painter);
241     else if(appearance == CheckboxAppearance)
242         style->drawControl(QStyle::CE_CheckBox, &option, painter);
243
244     return false;
245 }
246
247 void RenderThemeQt::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
248 {
249     addIntrinsicMargins(style);
250 }
251
252 bool RenderThemeQt::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
253 {
254     QStyle* style = 0;
255     QPainter* painter = 0;
256     QWidget* widget = 0;
257     
258     if (!getStylePainterAndWidgetFromPaintInfo(i, style, painter, widget))
259         return true;
260   
261     QStyleOption option;
262
263     // Get the correct theme data for a button
264     EAppearance appearance = applyTheme(option, o);
265     Q_ASSERT(appearance == TextFieldAppearance);
266
267     // Now paint the text field.
268     // FIXME: this is not enough for sure! (use 'option'...)
269     painter->drawRect(r);
270
271     return false;
272 }
273
274 }
275
276 // vim: ts=4 sw=4 et