1 /**
2 * This file is part of the html renderer for KDE.
3 *
4 * Copyright (C) 2005 Apple Computer, Inc.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "config.h"
24 #include "RenderButton.h"
25
26 #include "Document.h"
27 #include "GraphicsContext.h"
28 #include "HTMLInputElement.h"
29 #include "HTMLNames.h"
30 #include "RenderTextFragment.h"
31 #include "RenderTheme.h"
32
33 #if ENABLE(WML)
34 #include "WMLDoElement.h"
35 #include "WMLNames.h"
36 #endif
37
38 namespace WebCore {
39
40 using namespace HTMLNames;
41
RenderButton(Node * node)42 RenderButton::RenderButton(Node* node)
43 : RenderFlexibleBox(node)
44 , m_buttonText(0)
45 , m_inner(0)
46 , m_default(false)
47 {
48 }
49
addChild(RenderObject * newChild,RenderObject * beforeChild)50 void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild)
51 {
52 if (!m_inner) {
53 // Create an anonymous block.
54 ASSERT(!firstChild());
55 m_inner = createAnonymousBlock();
56 setupInnerStyle(m_inner->style());
57 RenderFlexibleBox::addChild(m_inner);
58 }
59
60 m_inner->addChild(newChild, beforeChild);
61 }
62
removeChild(RenderObject * oldChild)63 void RenderButton::removeChild(RenderObject* oldChild)
64 {
65 if (oldChild == m_inner || !m_inner) {
66 RenderFlexibleBox::removeChild(oldChild);
67 m_inner = 0;
68 } else
69 m_inner->removeChild(oldChild);
70 }
71
styleWillChange(StyleDifference diff,const RenderStyle * newStyle)72 void RenderButton::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
73 {
74 if (m_inner) {
75 // RenderBlock::setStyle is going to apply a new style to the inner block, which
76 // will have the initial box flex value, 0. The current value is 1, because we set
77 // it right below. Here we change it back to 0 to avoid getting a spurious layout hint
78 // because of the difference.
79 m_inner->style()->setBoxFlex(0);
80 }
81 RenderBlock::styleWillChange(diff, newStyle);
82 }
83
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)84 void RenderButton::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
85 {
86 RenderBlock::styleDidChange(diff, oldStyle);
87
88 if (m_buttonText)
89 m_buttonText->setStyle(style());
90 if (m_inner) // RenderBlock handled updating the anonymous block's style.
91 setupInnerStyle(m_inner->style());
92 setReplaced(isInline());
93
94 if (!m_default && theme()->isDefault(this)) {
95 if (!m_timer)
96 m_timer.set(new Timer<RenderButton>(this, &RenderButton::timerFired));
97 m_timer->startRepeating(0.03);
98 m_default = true;
99 } else if (m_default && !theme()->isDefault(this)) {
100 m_default = false;
101 m_timer.clear();
102 }
103 }
104
setupInnerStyle(RenderStyle * innerStyle)105 void RenderButton::setupInnerStyle(RenderStyle* innerStyle)
106 {
107 ASSERT(innerStyle->refCount() == 1);
108 // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is
109 // safe to modify.
110 innerStyle->setBoxFlex(1.0f);
111
112 innerStyle->setPaddingTop(Length(theme()->buttonInternalPaddingTop(), Fixed));
113 innerStyle->setPaddingRight(Length(theme()->buttonInternalPaddingRight(), Fixed));
114 innerStyle->setPaddingBottom(Length(theme()->buttonInternalPaddingBottom(), Fixed));
115 innerStyle->setPaddingLeft(Length(theme()->buttonInternalPaddingLeft(), Fixed));
116 }
117
updateFromElement()118 void RenderButton::updateFromElement()
119 {
120 // If we're an input element, we may need to change our button text.
121 if (node()->hasTagName(inputTag)) {
122 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
123 String value = input->valueWithDefault();
124 setText(value);
125 }
126
127
128 #if ENABLE(WML)
129 else if (node()->hasTagName(WMLNames::doTag)) {
130 WMLDoElement* doElement = static_cast<WMLDoElement*>(node());
131
132 String value = doElement->label();
133 if (value.isEmpty())
134 value = doElement->name();
135
136 setText(value);
137 }
138 #endif
139 }
140
canHaveChildren() const141 bool RenderButton::canHaveChildren() const
142 {
143 // Input elements can't have children, but button elements can. We'll
144 // write the code assuming any other button types that might emerge in the future
145 // can also have children.
146 return !node()->hasTagName(inputTag);
147 }
148
setText(const String & str)149 void RenderButton::setText(const String& str)
150 {
151 if (str.isEmpty()) {
152 if (m_buttonText) {
153 m_buttonText->destroy();
154 m_buttonText = 0;
155 }
156 } else {
157 if (m_buttonText)
158 m_buttonText->setText(str.impl());
159 else {
160 m_buttonText = new (renderArena()) RenderTextFragment(document(), str.impl());
161 m_buttonText->setStyle(style());
162 addChild(m_buttonText);
163 }
164 }
165 }
166
text() const167 String RenderButton::text() const
168 {
169 return m_buttonText ? m_buttonText->text() : 0;
170 }
171
updateBeforeAfterContent(PseudoId type)172 void RenderButton::updateBeforeAfterContent(PseudoId type)
173 {
174 if (m_inner)
175 m_inner->children()->updateBeforeAfterContent(m_inner, type, this);
176 else
177 children()->updateBeforeAfterContent(this, type);
178 }
179
controlClipRect(int tx,int ty) const180 IntRect RenderButton::controlClipRect(int tx, int ty) const
181 {
182 // Clip to the padding box to at least give content the extra padding space.
183 return IntRect(tx + borderLeft(), ty + borderTop(), width() - borderLeft() - borderRight(), height() - borderTop() - borderBottom());
184 }
185
timerFired(Timer<RenderButton> *)186 void RenderButton::timerFired(Timer<RenderButton>*)
187 {
188 // FIXME Bug 25110: Ideally we would stop our timer when our Document
189 // enters the page cache. But we currently have no way of being notified
190 // when that happens, so we'll just ignore the timer firing as long as
191 // we're in the cache.
192 if (document()->inPageCache())
193 return;
194
195 repaint();
196 }
197
198 } // namespace WebCore
199