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
48RenderButton::~RenderButton()
49{
50}
51
52void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild)
53{
54    if (!m_inner) {
55        // Create an anonymous block.
56        ASSERT(!firstChild());
57        bool isFlexibleBox = style()->display() == BOX || style()->display() == INLINE_BOX;
58        m_inner = createAnonymousBlock(isFlexibleBox);
59        setupInnerStyle(m_inner->style());
60        RenderFlexibleBox::addChild(m_inner);
61    }
62
63    m_inner->addChild(newChild, beforeChild);
64}
65
66void RenderButton::removeChild(RenderObject* oldChild)
67{
68    if (oldChild == m_inner || !m_inner) {
69        RenderFlexibleBox::removeChild(oldChild);
70        m_inner = 0;
71    } else
72        m_inner->removeChild(oldChild);
73}
74
75void RenderButton::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
76{
77    if (m_inner) {
78        // RenderBlock::setStyle is going to apply a new style to the inner block, which
79        // will have the initial box flex value, 0. The current value is 1, because we set
80        // it right below. Here we change it back to 0 to avoid getting a spurious layout hint
81        // because of the difference.
82        m_inner->style()->setBoxFlex(0);
83    }
84    RenderBlock::styleWillChange(diff, newStyle);
85}
86
87void RenderButton::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
88{
89    RenderBlock::styleDidChange(diff, oldStyle);
90
91    if (m_buttonText)
92        m_buttonText->setStyle(style());
93    if (m_inner) // RenderBlock handled updating the anonymous block's style.
94        setupInnerStyle(m_inner->style());
95
96    if (!m_default && theme()->isDefault(this)) {
97        if (!m_timer)
98            m_timer.set(new Timer<RenderButton>(this, &RenderButton::timerFired));
99        m_timer->startRepeating(0.03);
100        m_default = true;
101    } else if (m_default && !theme()->isDefault(this)) {
102        m_default = false;
103        m_timer.clear();
104    }
105}
106
107void RenderButton::setupInnerStyle(RenderStyle* innerStyle)
108{
109    ASSERT(innerStyle->refCount() == 1);
110    // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is
111    // safe to modify.
112    innerStyle->setBoxFlex(1.0f);
113    innerStyle->setBoxOrient(style()->boxOrient());
114}
115
116void RenderButton::updateFromElement()
117{
118    // If we're an input element, we may need to change our button text.
119    if (node()->hasTagName(inputTag)) {
120        HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
121        String value = input->valueWithDefault();
122        setText(value);
123    }
124
125
126#if ENABLE(WML)
127    else if (node()->hasTagName(WMLNames::doTag)) {
128        WMLDoElement* doElement = static_cast<WMLDoElement*>(node());
129
130        String value = doElement->label();
131        if (value.isEmpty())
132            value = doElement->name();
133
134        setText(value);
135    }
136#endif
137}
138
139bool RenderButton::canHaveChildren() const
140{
141    // Input elements can't have children, but button elements can.  We'll
142    // write the code assuming any other button types that might emerge in the future
143    // can also have children.
144    return !node()->hasTagName(inputTag);
145}
146
147void RenderButton::setText(const String& str)
148{
149    if (str.isEmpty()) {
150        if (m_buttonText) {
151            m_buttonText->destroy();
152            m_buttonText = 0;
153        }
154    } else {
155        if (m_buttonText)
156            m_buttonText->setText(str.impl());
157        else {
158            m_buttonText = new (renderArena()) RenderTextFragment(document(), str.impl());
159            m_buttonText->setStyle(style());
160            addChild(m_buttonText);
161        }
162    }
163}
164
165String RenderButton::text() const
166{
167    return m_buttonText ? m_buttonText->text() : 0;
168}
169
170void RenderButton::updateBeforeAfterContent(PseudoId type)
171{
172    if (m_inner)
173        m_inner->children()->updateBeforeAfterContent(m_inner, type, this);
174    else
175        children()->updateBeforeAfterContent(this, type);
176}
177
178IntRect RenderButton::controlClipRect(int tx, int ty) const
179{
180    // Clip to the padding box to at least give content the extra padding space.
181    return IntRect(tx + borderLeft(), ty + borderTop(), width() - borderLeft() - borderRight(), height() - borderTop() - borderBottom());
182}
183
184void RenderButton::timerFired(Timer<RenderButton>*)
185{
186    // FIXME Bug 25110: Ideally we would stop our timer when our Document
187    // enters the page cache. But we currently have no way of being notified
188    // when that happens, so we'll just ignore the timer firing as long as
189    // we're in the cache.
190    if (document()->inPageCache())
191        return;
192
193    repaint();
194}
195
196} // namespace WebCore
197