1/**
2 * Copyright (C) 2005 Apple Computer, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "config.h"
22#include "RenderButton.h"
23
24#include "Document.h"
25#include "GraphicsContext.h"
26#include "HTMLInputElement.h"
27#include "HTMLNames.h"
28#include "RenderTextFragment.h"
29#include "RenderTheme.h"
30
31#if ENABLE(WML)
32#include "WMLDoElement.h"
33#include "WMLNames.h"
34#endif
35
36namespace WebCore {
37
38using namespace HTMLNames;
39
40RenderButton::RenderButton(Node* node)
41    : RenderFlexibleBox(node)
42    , m_buttonText(0)
43    , m_inner(0)
44    , m_default(false)
45{
46}
47
48void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild)
49{
50    if (!m_inner) {
51        // Create an anonymous block.
52        ASSERT(!firstChild());
53        bool isFlexibleBox = style()->display() == BOX || style()->display() == INLINE_BOX;
54        m_inner = createAnonymousBlock(isFlexibleBox);
55        setupInnerStyle(m_inner->style());
56        RenderFlexibleBox::addChild(m_inner);
57    }
58
59    m_inner->addChild(newChild, beforeChild);
60}
61
62void RenderButton::removeChild(RenderObject* oldChild)
63{
64    if (oldChild == m_inner || !m_inner) {
65        RenderFlexibleBox::removeChild(oldChild);
66        m_inner = 0;
67    } else
68        m_inner->removeChild(oldChild);
69}
70
71void RenderButton::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
72{
73    if (m_inner) {
74        // RenderBlock::setStyle is going to apply a new style to the inner block, which
75        // will have the initial box flex value, 0. The current value is 1, because we set
76        // it right below. Here we change it back to 0 to avoid getting a spurious layout hint
77        // because of the difference.
78        m_inner->style()->setBoxFlex(0);
79    }
80    RenderBlock::styleWillChange(diff, newStyle);
81}
82
83void RenderButton::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
84{
85    RenderBlock::styleDidChange(diff, oldStyle);
86
87    if (m_buttonText)
88        m_buttonText->setStyle(style());
89    if (m_inner) // RenderBlock handled updating the anonymous block's style.
90        setupInnerStyle(m_inner->style());
91    setReplaced(isInline());
92
93    if (!m_default && theme()->isDefault(this)) {
94        if (!m_timer)
95            m_timer.set(new Timer<RenderButton>(this, &RenderButton::timerFired));
96        m_timer->startRepeating(0.03);
97        m_default = true;
98    } else if (m_default && !theme()->isDefault(this)) {
99        m_default = false;
100        m_timer.clear();
101    }
102}
103
104void RenderButton::setupInnerStyle(RenderStyle* innerStyle)
105{
106    ASSERT(innerStyle->refCount() == 1);
107    // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is
108    // safe to modify.
109    innerStyle->setBoxFlex(1.0f);
110    innerStyle->setBoxOrient(style()->boxOrient());
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
118void 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
141bool 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
149void 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
167String RenderButton::text() const
168{
169    return m_buttonText ? m_buttonText->text() : 0;
170}
171
172void 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
180IntRect 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
186void 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