/:
[WebKit-https.git] / WebCore / platform / gtk / RenderThemeGtk.cpp
1 /*
2  * This file is part of the WebKit project.
3  *
4  * Copyright (C) 2006 Apple Computer, Inc.
5  * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com 
6  * Copyright (C) 2007 Holger Hans Peter Freyther
7  * Copyright (C) 2007 Alp Toker <alp.toker@collabora.co.uk>
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 #include "RenderThemeGtk.h"
29
30 #include "NotImplemented.h"
31 #include "RenderObject.h"
32
33 #include <gdk/gdk.h>
34
35 #define THEME_COLOR 204
36 #define THEME_FONT  210
37
38 // Button constants
39 #define BP_BUTTON    1
40 #define BP_RADIO     2
41 #define BP_CHECKBOX  3
42
43 // Textfield constants
44 #define TFP_TEXTFIELD 1
45 #define TFS_READONLY  6
46
47 /*
48  * Approach to theming:
49  *  a) keep one copy of each to be drawn widget, GtkEntry, GtkButton, Gtk...
50  *     + the button will look like the native control
51  *     + we don't need to worry about style updates and loading the right GtkStyle
52  *     - resources are wasted. The native windows will not be used, we might have issues
53  *       with
54  *
55  *  b) Use GtkStyle directly and copy and paste Gtk+ code
56  *
57  *
58  * We will mix a and b
59  *
60  * - Create GtkWidgets to hold the state (disabled/enabled), selected, not selected.
61  * - Use a GdkPixmap to make the GtkStyle draw to and then try to convert set it the
62  *   source of the current operation.
63  *
64  */
65
66 namespace WebCore {
67
68 RenderTheme* theme()
69 {
70     static RenderThemeGtk gdkTheme;
71     return &gdkTheme;
72 }
73
74 RenderThemeGtk::RenderThemeGtk()
75     : m_gtkButton(0)
76     , m_gtkCheckbox(0)
77     , m_gtkRadioButton(0)
78     , m_gtkEntry(0)
79     , m_gtkEditable(0)
80     , m_unmappedWindow(0)
81     , m_container(0)
82 {
83 }
84
85 void RenderThemeGtk::close()
86 {
87 }
88
89 void RenderThemeGtk::addIntrinsicMargins(RenderStyle* style) const
90 {
91     // Cut out the intrinsic margins completely if we end up using a small font size
92     if (style->fontSize() < 11)
93         return;
94
95     // Intrinsic margin value.
96     const int m = 2;
97
98     // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
99     if (style->width().isIntrinsicOrAuto()) {
100         if (style->marginLeft().quirk())
101             style->setMarginLeft(Length(m, Fixed));
102         if (style->marginRight().quirk())
103             style->setMarginRight(Length(m, Fixed));
104     }
105
106     if (style->height().isAuto()) {
107         if (style->marginTop().quirk())
108             style->setMarginTop(Length(m, Fixed));
109         if (style->marginBottom().quirk())
110             style->setMarginBottom(Length(m, Fixed));
111     }
112 }
113
114 bool RenderThemeGtk::supportsFocus(EAppearance appearance)
115 {
116     switch (appearance) {
117         case PushButtonAppearance:
118         case ButtonAppearance:
119         case TextFieldAppearance:
120             return true;
121         default:
122             return false;
123     }
124
125     return false;
126 }
127
128 GtkStateType RenderThemeGtk::determineState(RenderObject* o)
129 {
130     GtkStateType result = GTK_STATE_NORMAL;
131     if (!isEnabled(o))
132         result = GTK_STATE_INSENSITIVE;
133     else if (isPressed(o))
134         result = GTK_STATE_ACTIVE;
135     else if (isHovered(o))
136         result = GTK_STATE_PRELIGHT;
137     return result;
138 }
139
140 GtkShadowType RenderThemeGtk::determineShadow(RenderObject* o)
141 {
142     return isChecked(o) ? GTK_SHADOW_IN : GTK_SHADOW_OUT;
143 }
144
145 ThemeData RenderThemeGtk::getThemeData(RenderObject* o)
146 {
147     ThemeData result;
148     switch (o->style()->appearance()) {
149         case PushButtonAppearance:
150         case ButtonAppearance:
151             result.m_part = BP_BUTTON;
152             result.m_state = determineState(o);
153             break;
154         case CheckboxAppearance:
155             result.m_part = BP_CHECKBOX;
156             result.m_state = determineState(o);
157             break;
158         case RadioAppearance:
159             result.m_part = BP_RADIO;
160             result.m_state = determineState(o);
161             break;
162         case TextFieldAppearance:
163             result.m_part = TFP_TEXTFIELD;
164             result.m_state = determineState(o);
165             break;
166         default:
167             // FIXME: much more?
168             break;
169     }
170
171     return result;
172 }
173
174 void RenderThemeGtk::setCheckboxSize(RenderStyle* style) const 
175
176     setRadioSize(style);
177 }
178
179 bool RenderThemeGtk::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
180 {
181     // FIXME: is it the right thing to do?
182     GtkWidget* checkbox = gtkCheckbox();
183     IntPoint pos = i.context->translatePoint(rect.location());
184     gtk_paint_check(checkbox->style, i.context->gdkDrawable(),
185                     determineState(o), determineShadow(o),
186                     NULL, checkbox, "checkbutton",
187                     pos.x(), pos.y(), rect.width(), rect.height());
188
189     return false;
190 }
191
192 void RenderThemeGtk::setRadioSize(RenderStyle* style) const 
193
194     notImplemented(); 
195     // If the width and height are both specified, then we have nothing to do.
196     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
197         return;
198
199
200     // FIXME:  A hard-coded size of 13 is used.  This is wrong but necessary for now.  It matches Firefox.
201     // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for
202     // the higher DPI.  Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's
203     // metrics.
204     const int ff = 13;
205     if (style->width().isIntrinsicOrAuto())
206         style->setWidth(Length(ff, Fixed));
207
208     if (style->height().isAuto())
209         style->setHeight(Length(ff, Fixed));
210 }
211
212 bool RenderThemeGtk::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect)
213
214     // FIXME: is it the right thing to do?
215     GtkWidget* radio = gtkRadioButton();
216     IntPoint pos = i.context->translatePoint(rect.location());
217     gtk_paint_option(radio->style, i.context->gdkDrawable(),
218                      determineState(o), determineShadow(o),
219                      NULL, radio, "radiobutton",
220                      pos.x(), pos.y(), rect.width(), rect.height());
221
222     return false;
223 }
224
225 bool RenderThemeGtk::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& rect) 
226
227     // FIXME: should use theme-aware drawing. This should honor the state as well
228     GtkWidget* button = gtkButton();
229     IntPoint pos = i.context->translatePoint(rect.location());
230     gtk_paint_box(button->style, i.context->gdkDrawable(),
231                   determineState(o), determineShadow(o),
232                   NULL, button, "button",
233                   pos.x(), pos.y(), rect.width(), rect.height());
234     return false;
235 }
236
237 void RenderThemeGtk::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element* e) const 
238
239     notImplemented(); 
240 }
241
242 bool RenderThemeGtk::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
243 {
244     // FIXME: should use theme-aware drawing
245     return true;
246 }
247
248 bool RenderThemeGtk::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r)
249 {
250     return paintTextField(o, i, r);
251 }
252
253 void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const
254 {
255     addIntrinsicMargins(style);
256 }
257
258 void RenderThemeGtk::systemFont(int propId, FontDescription&) const
259 {
260 }
261
262 GtkWidget* RenderThemeGtk::gtkButton() const
263 {
264     if (!m_gtkButton) {
265         m_gtkButton = gtk_button_new();
266         gtk_container_add(GTK_CONTAINER(gtkWindowContainer()), m_gtkButton);
267         gtk_widget_realize(m_gtkButton);
268     }
269
270     return m_gtkButton;
271 }
272
273 GtkWidget* RenderThemeGtk::gtkCheckbox() const
274 {
275     if (!m_gtkCheckbox) {
276         m_gtkCheckbox = gtk_check_button_new();
277         gtk_container_add(GTK_CONTAINER(gtkWindowContainer()), m_gtkCheckbox);
278         gtk_widget_realize(m_gtkCheckbox);
279     }
280
281     return m_gtkCheckbox;
282 }
283
284 GtkWidget* RenderThemeGtk::gtkRadioButton() const
285 {
286     if (!m_gtkRadioButton) {
287         m_gtkRadioButton = gtk_radio_button_new(NULL);
288         gtk_container_add(GTK_CONTAINER(gtkWindowContainer()), m_gtkRadioButton);
289         gtk_widget_realize(m_gtkRadioButton);
290     }
291
292     return m_gtkRadioButton;
293 }
294
295 GtkWidget* RenderThemeGtk::gtkWindowContainer() const
296 {
297     if (!m_container) {
298         m_unmappedWindow = gtk_window_new(GTK_WINDOW_POPUP);
299         // Some GTK themes (i.e. Clearlooks) draw the buttons differently
300         // (in particular, call gtk_style_apply_default_background) if they
301         // are unallocated and are children of a GtkFixed widget, which is
302         // apparently what some "make Firefox buttons look like GTK" code
303         // does.  To avoid this ugly look, we use a GtkHBox as a parent,
304         // rather than a GtkFixed.
305         m_container = gtk_hbox_new(false, 0);
306         gtk_container_add(GTK_CONTAINER(m_unmappedWindow), m_container);
307         gtk_widget_realize(m_unmappedWindow);
308     }
309
310     return m_container;
311 }
312 }