summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering/RenderSlider.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/rendering/RenderSlider.cpp')
-rw-r--r--WebCore/rendering/RenderSlider.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/WebCore/rendering/RenderSlider.cpp b/WebCore/rendering/RenderSlider.cpp
new file mode 100644
index 0000000..8e60fc7
--- /dev/null
+++ b/WebCore/rendering/RenderSlider.cpp
@@ -0,0 +1,399 @@
+/**
+ *
+ * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "RenderSlider.h"
+
+#include "CSSPropertyNames.h"
+#include "Document.h"
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "HTMLInputElement.h"
+#include "HTMLDivElement.h"
+#include "HTMLNames.h"
+#include "MouseEvent.h"
+#include "RenderTheme.h"
+#include <wtf/MathExtras.h>
+
+using std::min;
+
+namespace WebCore {
+
+using namespace EventNames;
+using namespace HTMLNames;
+
+const int defaultTrackLength = 129;
+
+class HTMLSliderThumbElement : public HTMLDivElement {
+public:
+ HTMLSliderThumbElement(Document*, Node* shadowParent = 0);
+
+ virtual void defaultEventHandler(Event*);
+ virtual bool isShadowNode() const { return true; }
+ virtual Node* shadowParentNode() { return m_shadowParent; }
+
+ bool inDragMode() const { return m_inDragMode; }
+private:
+ Node* m_shadowParent;
+ IntPoint m_initialClickPoint;
+ int m_initialPosition;
+ bool m_inDragMode;
+};
+
+HTMLSliderThumbElement::HTMLSliderThumbElement(Document* doc, Node* shadowParent)
+ : HTMLDivElement(doc)
+ , m_shadowParent(shadowParent)
+ , m_initialClickPoint(IntPoint())
+ , m_initialPosition(0)
+ , m_inDragMode(false)
+{
+}
+
+void HTMLSliderThumbElement::defaultEventHandler(Event* event)
+{
+ const AtomicString& eventType = event->type();
+ if (eventType == mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
+ if (document()->frame() && renderer() && renderer()->parent()
+ && static_cast<RenderSlider*>(renderer()->parent())->mouseEventIsInThumb(mouseEvent)) {
+ // Cache the initial point where the mouse down occurred.
+ m_initialClickPoint = IntPoint(mouseEvent->pageX(), mouseEvent->pageY());
+ // Cache the initial position of the thumb.
+ m_initialPosition = static_cast<RenderSlider*>(renderer()->parent())->currentPosition();
+ m_inDragMode = true;
+
+ document()->frame()->eventHandler()->setCapturingMouseEventsNode(m_shadowParent);
+
+ event->setDefaultHandled();
+ return;
+ }
+ } else if (eventType == mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
+ if (m_inDragMode) {
+ if (Frame* frame = document()->frame())
+ frame->eventHandler()->setCapturingMouseEventsNode(0);
+ m_inDragMode = false;
+ event->setDefaultHandled();
+ return;
+ }
+ } else if (eventType == mousemoveEvent && event->isMouseEvent()) {
+ if (m_inDragMode && renderer() && renderer()->parent()) {
+ // Move the slider
+ MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
+ RenderSlider* slider = static_cast<RenderSlider*>(renderer()->parent());
+ int newPosition = slider->positionForOffset(
+ IntPoint(m_initialPosition + mouseEvent->pageX() - m_initialClickPoint.x()
+ + (renderer()->width() / 2),
+ m_initialPosition + mouseEvent->pageY() - m_initialClickPoint.y()
+ + (renderer()->height() / 2)));
+ if (slider->currentPosition() != newPosition) {
+ slider->setCurrentPosition(newPosition);
+ slider->valueChanged();
+ }
+ event->setDefaultHandled();
+ return;
+ }
+ }
+
+ HTMLDivElement::defaultEventHandler(event);
+}
+
+RenderSlider::RenderSlider(HTMLInputElement* element)
+ : RenderBlock(element)
+ , m_thumb(0)
+{
+}
+
+RenderSlider::~RenderSlider()
+{
+ if (m_thumb)
+ m_thumb->detach();
+}
+
+short RenderSlider::baselinePosition(bool b, bool isRootLineBox) const
+{
+ return height() + marginTop();
+}
+
+void RenderSlider::calcPrefWidths()
+{
+ m_minPrefWidth = 0;
+ m_maxPrefWidth = 0;
+
+ if (style()->width().isFixed() && style()->width().value() > 0)
+ m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
+ else
+ m_maxPrefWidth = defaultTrackLength;
+
+ if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
+ m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
+ m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
+ } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
+ m_minPrefWidth = 0;
+ else
+ m_minPrefWidth = m_maxPrefWidth;
+
+ if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
+ m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
+ m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
+ }
+
+ int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
+ m_minPrefWidth += toAdd;
+ m_maxPrefWidth += toAdd;
+
+ setPrefWidthsDirty(false);
+}
+
+void RenderSlider::setStyle(RenderStyle* newStyle)
+{
+ RenderBlock::setStyle(newStyle);
+
+ if (m_thumb) {
+ RenderStyle* thumbStyle = createThumbStyle(newStyle, m_thumb->renderer()->style());
+ m_thumb->renderer()->setStyle(thumbStyle);
+ }
+
+ setReplaced(isInline());
+}
+
+RenderStyle* RenderSlider::createThumbStyle(RenderStyle* parentStyle, RenderStyle* oldStyle)
+{
+ RenderStyle* style;
+
+ RenderStyle* pseudoStyle = getPseudoStyle(RenderStyle::SLIDER_THUMB);
+ if (pseudoStyle)
+ // We may be sharing style with another slider, but we must not share the thumb style.
+ style = new (renderArena()) RenderStyle(*pseudoStyle);
+ else
+ style = new (renderArena()) RenderStyle();
+
+ if (parentStyle)
+ style->inheritFrom(parentStyle);
+
+ style->setDisplay(BLOCK);
+ style->setPosition(RelativePosition);
+ if (oldStyle) {
+ style->setLeft(oldStyle->left());
+ style->setTop(oldStyle->top());
+ }
+
+ if (parentStyle->appearance() == SliderVerticalAppearance)
+ style->setAppearance(SliderThumbVerticalAppearance);
+ else if (parentStyle->appearance() == SliderHorizontalAppearance)
+ style->setAppearance(SliderThumbHorizontalAppearance);
+ else if (parentStyle->appearance() == MediaSliderAppearance)
+ style->setAppearance(MediaSliderThumbAppearance);
+
+ return style;
+}
+
+void RenderSlider::layout()
+{
+ bool relayoutChildren = false;
+
+ if (m_thumb && m_thumb->renderer()) {
+
+ int oldWidth = m_width;
+ calcWidth();
+ int oldHeight = m_height;
+ calcHeight();
+
+ if (oldWidth != m_width || oldHeight != m_height)
+ relayoutChildren = true;
+
+ // Allow the theme to set the size of the thumb
+ if (m_thumb->renderer()->style()->hasAppearance())
+ theme()->adjustSliderThumbSize(m_thumb->renderer());
+
+ if (style()->appearance() == SliderVerticalAppearance) {
+ // FIXME: Handle percentage widths correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104
+ m_thumb->renderer()->style()->setLeft(Length(contentWidth() / 2 - m_thumb->renderer()->style()->width().value() / 2, Fixed));
+ } else {
+ // FIXME: Handle percentage heights correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104
+ m_thumb->renderer()->style()->setTop(Length(contentHeight() / 2 - m_thumb->renderer()->style()->height().value() / 2, Fixed));
+ }
+
+ if (relayoutChildren)
+ setPositionFromValue(true);
+ }
+
+ RenderBlock::layoutBlock(relayoutChildren);
+}
+
+void RenderSlider::updateFromElement()
+{
+ if (!m_thumb) {
+ m_thumb = new HTMLSliderThumbElement(document(), node());
+ RenderStyle* thumbStyle = createThumbStyle(style());
+ m_thumb->setRenderer(m_thumb->createRenderer(renderArena(), thumbStyle));
+ m_thumb->renderer()->setStyle(thumbStyle);
+ m_thumb->setAttached();
+ m_thumb->setInDocument(true);
+ addChild(m_thumb->renderer());
+ }
+ setPositionFromValue();
+ setNeedsLayout(true);
+}
+
+bool RenderSlider::mouseEventIsInThumb(MouseEvent* evt)
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return false;
+
+ IntRect thumbBounds = m_thumb->renderer()->absoluteBoundingBoxRect();
+ return thumbBounds.contains(evt->pageX(), evt->pageY());
+}
+
+void RenderSlider::setValueForPosition(int position)
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return;
+
+ const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
+ const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
+ const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
+
+ double minVal = minStr.isNull() ? 0.0 : minStr.toDouble();
+ double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble();
+ minVal = min(minVal, maxVal); // Make sure the range is sane.
+
+ // Calculate the new value based on the position
+ double factor = (double)position / (double)trackSize();
+ if (style()->appearance() == SliderVerticalAppearance)
+ factor = 1.0 - factor;
+ double val = minVal + factor * (maxVal - minVal);
+
+ val = max(minVal, min(val, maxVal)); // Make sure val is within min/max.
+
+ // Force integer value if not float.
+ if (!equalIgnoringCase(precision, "float"))
+ val = lround(val);
+
+ static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val));
+
+ if (position != currentPosition())
+ setCurrentPosition(position);
+}
+
+double RenderSlider::setPositionFromValue(bool inLayout)
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return 0;
+
+ if (!inLayout)
+ document()->updateLayout();
+
+ String value = static_cast<HTMLInputElement*>(node())->value();
+ const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
+ const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
+ const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
+
+ double minVal = minStr.isNull() ? 0.0 : minStr.toDouble();
+ double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble();
+ minVal = min(minVal, maxVal); // Make sure the range is sane.
+
+ double oldVal = value.isNull() ? (maxVal + minVal)/2.0 : value.toDouble();
+ double val = max(minVal, min(oldVal, maxVal)); // Make sure val is within min/max.
+
+ // Force integer value if not float.
+ if (!equalIgnoringCase(precision, "float"))
+ val = lround(val);
+
+ // Calculate the new position based on the value
+ double factor = (val - minVal) / (maxVal - minVal);
+ if (style()->appearance() == SliderVerticalAppearance)
+ factor = 1.0 - factor;
+
+ setCurrentPosition((int)(factor * trackSize()));
+
+ if (val != oldVal)
+ static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val));
+
+ return val;
+}
+
+int RenderSlider::positionForOffset(const IntPoint& p)
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return 0;
+
+ int position;
+ if (style()->appearance() == SliderVerticalAppearance)
+ position = p.y() - m_thumb->renderer()->height() / 2;
+ else
+ position = p.x() - m_thumb->renderer()->width() / 2;
+
+ return max(0, min(position, trackSize()));
+}
+
+void RenderSlider::valueChanged()
+{
+ setValueForPosition(currentPosition());
+}
+
+int RenderSlider::currentPosition()
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return 0;
+
+ if (style()->appearance() == SliderVerticalAppearance)
+ return m_thumb->renderer()->style()->top().value();
+ return m_thumb->renderer()->style()->left().value();
+}
+
+void RenderSlider::setCurrentPosition(int pos)
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return;
+
+ if (style()->appearance() == SliderVerticalAppearance)
+ m_thumb->renderer()->style()->setTop(Length(pos, Fixed));
+ else
+ m_thumb->renderer()->style()->setLeft(Length(pos, Fixed));
+
+ m_thumb->renderer()->layer()->updateLayerPosition();
+ repaint();
+ m_thumb->renderer()->repaint();
+}
+
+int RenderSlider::trackSize()
+{
+ if (!m_thumb || !m_thumb->renderer())
+ return 0;
+
+ if (style()->appearance() == SliderVerticalAppearance)
+ return contentHeight() - m_thumb->renderer()->height();
+ return contentWidth() - m_thumb->renderer()->width();
+}
+
+void RenderSlider::forwardEvent(Event* evt)
+{
+ m_thumb->defaultEventHandler(evt);
+}
+
+bool RenderSlider::inDragMode() const
+{
+ return m_thumb->inDragMode();
+}
+
+} // namespace WebCore