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 36 namespace WebCore { 37 38 using namespace HTMLNames; 39 40 RenderButton::RenderButton(Node* node) 41 : RenderFlexibleBox(node) 42 , m_buttonText(0) 43 , m_inner(0) 44 , m_default(false) 45 { 46 } 47 48 RenderButton::~RenderButton() 49 { 50 } 51 52 void 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 66 void 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 75 void 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 87 void 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 107 void 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 116 void 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 139 bool 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 147 void 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 165 String RenderButton::text() const 166 { 167 return m_buttonText ? m_buttonText->text() : 0; 168 } 169 170 void 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 178 IntRect 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 184 void 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