webengine/osswebengine/WebCore/rendering/RenderSlider.cpp
changeset 0 dd21522fd290
child 47 e1bea15f9a39
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /**
       
     2  *
       
     3  * Copyright (C) 2006 Apple Computer, Inc.
       
     4  *
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Library General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Library General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU Library General Public License
       
    16  * along with this library; see the file COPYING.LIB.  If not, write to
       
    17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    18  * Boston, MA 02110-1301, USA.
       
    19  *
       
    20  */
       
    21 
       
    22 #include "config.h"
       
    23 #include "RenderSlider.h"
       
    24 
       
    25 #include "CSSPropertyNames.h"
       
    26 #include "Document.h"
       
    27 #include "Event.h"
       
    28 #include "EventHandler.h"
       
    29 #include "EventNames.h"
       
    30 #include "Frame.h"
       
    31 #include "HTMLInputElement.h"
       
    32 #include "HTMLDivElement.h"
       
    33 #include "HTMLNames.h"
       
    34 #include "MouseEvent.h"
       
    35 #include "RenderTheme.h"
       
    36 #include <wtf/MathExtras.h>
       
    37 
       
    38 using std::min;
       
    39 
       
    40 namespace WebCore {
       
    41 
       
    42 using namespace EventNames;
       
    43 using namespace HTMLNames;
       
    44 
       
    45 const int defaultTrackLength = 129;
       
    46 
       
    47 class HTMLSliderThumbElement : public HTMLDivElement {
       
    48 public:
       
    49     HTMLSliderThumbElement(Document*, Node* shadowParent = 0);
       
    50         
       
    51     virtual void defaultEventHandler(Event*);
       
    52     virtual bool isShadowNode() const { return true; }
       
    53     virtual Node* shadowParentNode() { return m_shadowParent; }
       
    54     
       
    55     bool inDragMode() const { return m_inDragMode; }
       
    56 private:
       
    57     Node* m_shadowParent;
       
    58     IntPoint m_initialClickPoint;
       
    59     int m_initialPosition;
       
    60     bool m_inDragMode;
       
    61 };
       
    62 
       
    63 HTMLSliderThumbElement::HTMLSliderThumbElement(Document* doc, Node* shadowParent)
       
    64     : HTMLDivElement(doc)
       
    65     , m_shadowParent(shadowParent)
       
    66     , m_initialClickPoint(IntPoint())
       
    67     , m_initialPosition(0)
       
    68     , m_inDragMode(false)
       
    69 {
       
    70 }
       
    71 
       
    72 void HTMLSliderThumbElement::defaultEventHandler(Event* event)
       
    73 {
       
    74     const AtomicString& eventType = event->type();
       
    75     if (eventType == mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
       
    76         MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
       
    77         if (document()->frame() && renderer() && renderer()->parent()
       
    78                 && static_cast<RenderSlider*>(renderer()->parent())->mouseEventIsInThumb(mouseEvent)) {
       
    79             // Cache the initial point where the mouse down occurred.
       
    80             m_initialClickPoint = IntPoint(mouseEvent->x(), mouseEvent->y());
       
    81             // Cache the initial position of the thumb.
       
    82             m_initialPosition = static_cast<RenderSlider*>(renderer()->parent())->currentPosition();
       
    83             m_inDragMode = true;
       
    84             
       
    85             document()->frame()->eventHandler()->setCapturingMouseEventsNode(this);
       
    86             
       
    87             event->setDefaultHandled();
       
    88             return;
       
    89         }
       
    90     } else if (eventType == mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
       
    91         if (m_inDragMode) {
       
    92             if (Frame* frame = document()->frame())
       
    93                 frame->eventHandler()->setCapturingMouseEventsNode(0);      
       
    94             m_inDragMode = false;
       
    95             event->setDefaultHandled();
       
    96             return;
       
    97         }
       
    98     } else if (eventType == mousemoveEvent && event->isMouseEvent()) {
       
    99         if (m_inDragMode && renderer() && renderer()->parent()) {
       
   100             // Move the slider
       
   101             MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
       
   102             RenderSlider* slider = static_cast<RenderSlider*>(renderer()->parent());
       
   103             int newPosition = slider->positionForOffset(
       
   104                 IntPoint(m_initialPosition + mouseEvent->x() - m_initialClickPoint.x()
       
   105                         + (renderer()->absoluteBoundingBoxRect().width() / 2), 
       
   106                     m_initialPosition + mouseEvent->y() - m_initialClickPoint.y()
       
   107                         + (renderer()->absoluteBoundingBoxRect().height() / 2)));
       
   108             if (slider->currentPosition() != newPosition) {
       
   109                 slider->setCurrentPosition(newPosition);
       
   110                 slider->valueChanged();
       
   111             }
       
   112         }
       
   113     }
       
   114 
       
   115     HTMLDivElement::defaultEventHandler(event);
       
   116 }
       
   117 
       
   118 RenderSlider::RenderSlider(HTMLInputElement* element)
       
   119     : RenderBlock(element)
       
   120     , m_thumb(0)
       
   121 {
       
   122 }
       
   123 
       
   124 RenderSlider::~RenderSlider()
       
   125 {
       
   126     if (m_thumb)
       
   127         m_thumb->detach();
       
   128 }
       
   129 
       
   130 short RenderSlider::baselinePosition(bool b, bool isRootLineBox) const
       
   131 {
       
   132     return height() + marginTop();
       
   133 }
       
   134 
       
   135 void RenderSlider::calcPrefWidths()
       
   136 {
       
   137     m_minPrefWidth = 0;
       
   138     m_maxPrefWidth = 0;
       
   139 
       
   140     if (style()->width().isFixed() && style()->width().value() > 0)
       
   141         m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
       
   142     else
       
   143         m_maxPrefWidth = defaultTrackLength;
       
   144 
       
   145     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
       
   146         m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
       
   147         m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
       
   148     } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
       
   149         m_minPrefWidth = 0;
       
   150     else
       
   151         m_minPrefWidth = m_maxPrefWidth;
       
   152     
       
   153     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
       
   154         m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
       
   155         m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
       
   156     }
       
   157 
       
   158     int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
       
   159     m_minPrefWidth += toAdd;
       
   160     m_maxPrefWidth += toAdd;
       
   161 
       
   162     setPrefWidthsDirty(false); 
       
   163 }
       
   164 
       
   165 void RenderSlider::setStyle(RenderStyle* newStyle)
       
   166 {
       
   167     RenderBlock::setStyle(newStyle);
       
   168     
       
   169     if (m_thumb) {
       
   170         RenderStyle* thumbStyle = createThumbStyle(newStyle);
       
   171         m_thumb->renderer()->setStyle(thumbStyle);
       
   172     }
       
   173         
       
   174     setReplaced(isInline());
       
   175 }
       
   176 
       
   177 RenderStyle* RenderSlider::createThumbStyle(RenderStyle* parentStyle)
       
   178 {
       
   179     RenderStyle* style;
       
   180 
       
   181     RenderStyle* pseudoStyle = getPseudoStyle(RenderStyle::SLIDER_THUMB);
       
   182     if (pseudoStyle)
       
   183         // We may be sharing style with another slider, but we must not share the thumb style.
       
   184         style = new (renderArena()) RenderStyle(*pseudoStyle);
       
   185     else
       
   186         style = new (renderArena()) RenderStyle();
       
   187 
       
   188     if (parentStyle)
       
   189         style->inheritFrom(parentStyle);
       
   190 
       
   191     style->setDisplay(BLOCK);
       
   192     style->setPosition(RelativePosition);
       
   193 
       
   194     if (parentStyle->appearance() == SliderVerticalAppearance)
       
   195        style->setAppearance(SliderThumbVerticalAppearance);
       
   196     else if (parentStyle->appearance() == SliderHorizontalAppearance)
       
   197        style->setAppearance(SliderThumbHorizontalAppearance);
       
   198 
       
   199     return style;
       
   200 }
       
   201 
       
   202 void RenderSlider::layout()
       
   203 {    
       
   204     bool relayoutChildren = false;
       
   205     
       
   206     if (m_thumb && m_thumb->renderer()) {
       
   207             
       
   208         int oldWidth = m_width;
       
   209         calcWidth();
       
   210         int oldHeight = m_height;
       
   211         calcHeight();
       
   212         
       
   213         if (oldWidth != m_width || oldHeight != m_height)
       
   214             relayoutChildren = true;  
       
   215 
       
   216         // Allow the theme to set the size of the thumb
       
   217         if (m_thumb->renderer()->style()->hasAppearance())
       
   218             theme()->adjustSliderThumbSize(m_thumb->renderer());
       
   219 
       
   220         if (style()->appearance() == SliderVerticalAppearance) {
       
   221             // FIXME: Handle percentage widths correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104
       
   222             m_thumb->renderer()->style()->setLeft(Length(m_width / 2 - m_thumb->renderer()->style()->width().value() / 2, Fixed));
       
   223         } else {
       
   224             // FIXME: Handle percentage heights correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104
       
   225             m_thumb->renderer()->style()->setTop(Length(m_height / 2 - m_thumb->renderer()->style()->height().value() / 2, Fixed));
       
   226         }
       
   227 
       
   228         if (relayoutChildren)
       
   229             setPositionFromValue(true);
       
   230     }
       
   231 
       
   232     RenderBlock::layoutBlock(relayoutChildren);
       
   233 }
       
   234 
       
   235 void RenderSlider::updateFromElement()
       
   236 {
       
   237     if (!m_thumb) {
       
   238         m_thumb = new HTMLSliderThumbElement(document(), node());
       
   239         RenderStyle* thumbStyle = createThumbStyle(style());
       
   240         m_thumb->setRenderer(m_thumb->createRenderer(renderArena(), thumbStyle));
       
   241         m_thumb->renderer()->setStyle(thumbStyle);
       
   242         m_thumb->setAttached();
       
   243         m_thumb->setInDocument(true);
       
   244         addChild(m_thumb->renderer());
       
   245     }
       
   246     setPositionFromValue();
       
   247     setNeedsLayout(true);
       
   248 }
       
   249 
       
   250 bool RenderSlider::mouseEventIsInThumb(MouseEvent* evt)
       
   251 {
       
   252     if (!m_thumb || !m_thumb->renderer())
       
   253         return false;
       
   254  
       
   255     ASSERT(evt->target()->toNode() == node());
       
   256     
       
   257     IntRect thumbBounds = m_thumb->renderer()->absoluteBoundingBoxRect();
       
   258     thumbBounds.setX(m_thumb->renderer()->style()->left().value());
       
   259     thumbBounds.setY(m_thumb->renderer()->style()->top().value());
       
   260     
       
   261     return thumbBounds.contains(evt->offsetX(), evt->offsetY());
       
   262 }
       
   263 
       
   264 void RenderSlider::setValueForPosition(int position)
       
   265 {
       
   266     if (!m_thumb || !m_thumb->renderer())
       
   267         return;
       
   268     
       
   269     const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
       
   270     const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
       
   271     const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
       
   272     
       
   273     double minVal = minStr.isNull() ? 0.0 : minStr.toDouble();
       
   274     double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble();
       
   275     minVal = min(minVal, maxVal); // Make sure the range is sane.
       
   276     
       
   277     // Calculate the new value based on the position
       
   278     double factor = (double)position / (double)trackSize();
       
   279     if (style()->appearance() == SliderVerticalAppearance)
       
   280         factor = 1.0 - factor;
       
   281     double val = minVal + factor * (maxVal - minVal);
       
   282             
       
   283     val = max(minVal, min(val, maxVal)); // Make sure val is within min/max.
       
   284 
       
   285     // Force integer value if not float.
       
   286     if (!equalIgnoringCase(precision, "float"))
       
   287         val = lround(val);
       
   288 
       
   289     static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val));
       
   290     
       
   291     if (position != currentPosition())
       
   292         setCurrentPosition(position);
       
   293 }
       
   294 
       
   295 double RenderSlider::setPositionFromValue(bool inLayout)
       
   296 {
       
   297     if (!m_thumb || !m_thumb->renderer())
       
   298         return 0;
       
   299     
       
   300     if (!inLayout)
       
   301         document()->updateLayout();
       
   302         
       
   303     String value = static_cast<HTMLInputElement*>(node())->value();
       
   304     const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
       
   305     const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
       
   306     const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
       
   307     
       
   308     double minVal = minStr.isNull() ? 0.0 : minStr.toDouble();
       
   309     double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble();
       
   310     minVal = min(minVal, maxVal); // Make sure the range is sane.
       
   311     
       
   312     double oldVal = value.isNull() ? (maxVal + minVal)/2.0 : value.toDouble();
       
   313     double val = max(minVal, min(oldVal, maxVal)); // Make sure val is within min/max.
       
   314         
       
   315     // Force integer value if not float.
       
   316     if (!equalIgnoringCase(precision, "float"))
       
   317         val = lround(val);
       
   318 
       
   319     // Calculate the new position based on the value
       
   320     double factor = (val - minVal) / (maxVal - minVal);
       
   321     if (style()->appearance() == SliderVerticalAppearance)
       
   322         factor = 1.0 - factor;
       
   323 
       
   324     setCurrentPosition((int)(factor * trackSize()));
       
   325     
       
   326     if (val != oldVal)
       
   327         static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val));
       
   328     
       
   329     return val;
       
   330 }
       
   331 
       
   332 int RenderSlider::positionForOffset(const IntPoint& p)
       
   333 {
       
   334     if (!m_thumb || !m_thumb->renderer())
       
   335         return 0;
       
   336    
       
   337     int position;
       
   338     if (style()->appearance() == SliderVerticalAppearance) {
       
   339         position = max(0, min(p.y() - (m_thumb->renderer()->absoluteBoundingBoxRect().height() / 2), 
       
   340                               absoluteBoundingBoxRect().height() - m_thumb->renderer()->absoluteBoundingBoxRect().height()));
       
   341     } else {
       
   342         position = max(0, min(p.x() - (m_thumb->renderer()->absoluteBoundingBoxRect().width() / 2), 
       
   343                               absoluteBoundingBoxRect().width() - m_thumb->renderer()->absoluteBoundingBoxRect().width()));
       
   344     }
       
   345     return position;
       
   346 }
       
   347 
       
   348 void RenderSlider::valueChanged()
       
   349 {
       
   350     setValueForPosition(currentPosition());
       
   351 }
       
   352 
       
   353 int RenderSlider::currentPosition()
       
   354 {
       
   355     if (!m_thumb || !m_thumb->renderer())
       
   356         return 0;
       
   357 
       
   358     if (style()->appearance() == SliderVerticalAppearance)
       
   359         return m_thumb->renderer()->style()->top().value();
       
   360     return m_thumb->renderer()->style()->left().value();
       
   361 }
       
   362 
       
   363 void RenderSlider::setCurrentPosition(int pos)
       
   364 {
       
   365     if (!m_thumb || !m_thumb->renderer())
       
   366         return;
       
   367 
       
   368     if (style()->appearance() == SliderVerticalAppearance)
       
   369         m_thumb->renderer()->style()->setTop(Length(pos, Fixed));
       
   370     else
       
   371         m_thumb->renderer()->style()->setLeft(Length(pos, Fixed));
       
   372 
       
   373     m_thumb->renderer()->layer()->updateLayerPosition();
       
   374     repaint();
       
   375     m_thumb->renderer()->repaint();
       
   376 }
       
   377 
       
   378 int RenderSlider::trackSize()
       
   379 {
       
   380     if (!m_thumb || !m_thumb->renderer())
       
   381         return 0;
       
   382 
       
   383     if (style()->appearance() == SliderVerticalAppearance)
       
   384         return absoluteBoundingBoxRect().height() - m_thumb->renderer()->absoluteBoundingBoxRect().height();
       
   385     return absoluteBoundingBoxRect().width() - m_thumb->renderer()->absoluteBoundingBoxRect().width();
       
   386 }
       
   387 
       
   388 void RenderSlider::forwardEvent(Event* evt)
       
   389 {
       
   390     m_thumb->defaultEventHandler(evt);
       
   391 }
       
   392 
       
   393 bool RenderSlider::inDragMode() const
       
   394 {
       
   395     return m_thumb->inDragMode();
       
   396 }
       
   397 
       
   398 } // namespace WebCore