Home | History | Annotate | Download | only in rendering
      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