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