3 * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc.
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.
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.
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.
23 #include "RenderSlider.h"
25 #include "CSSPropertyNames.h"
28 #include "EventHandler.h"
29 #include "EventNames.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>
42 using namespace EventNames;
43 using namespace HTMLNames;
45 const int defaultTrackLength = 129;
47 class HTMLSliderThumbElement : public HTMLDivElement {
49 HTMLSliderThumbElement(Document*, Node* shadowParent = 0);
51 virtual void defaultEventHandler(Event*);
52 virtual bool isShadowNode() const { return true; }
53 virtual Node* shadowParentNode() { return m_shadowParent; }
55 bool inDragMode() const { return m_inDragMode; }
58 IntPoint m_initialClickPoint;
59 int m_initialPosition;
63 HTMLSliderThumbElement::HTMLSliderThumbElement(Document* doc, Node* shadowParent)
65 , m_shadowParent(shadowParent)
66 , m_initialClickPoint(IntPoint())
67 , m_initialPosition(0)
72 void HTMLSliderThumbElement::defaultEventHandler(Event* event)
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->pageX(), mouseEvent->pageY());
81 // Cache the initial position of the thumb.
82 m_initialPosition = static_cast<RenderSlider*>(renderer()->parent())->currentPosition();
85 document()->frame()->eventHandler()->setCapturingMouseEventsNode(this);
87 event->setDefaultHandled();
90 } else if (eventType == mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
92 if (Frame* frame = document()->frame())
93 frame->eventHandler()->setCapturingMouseEventsNode(0);
95 event->setDefaultHandled();
98 } else if (eventType == mousemoveEvent && event->isMouseEvent()) {
99 if (m_inDragMode && renderer() && renderer()->parent()) {
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->pageX() - m_initialClickPoint.x()
105 + (renderer()->absoluteBoundingBoxRect().width() / 2),
106 m_initialPosition + mouseEvent->pageY() - m_initialClickPoint.y()
107 + (renderer()->absoluteBoundingBoxRect().height() / 2)));
108 if (slider->currentPosition() != newPosition) {
109 slider->setCurrentPosition(newPosition);
110 slider->valueChanged();
115 HTMLDivElement::defaultEventHandler(event);
118 RenderSlider::RenderSlider(HTMLInputElement* element)
119 : RenderBlock(element)
124 RenderSlider::~RenderSlider()
130 short RenderSlider::baselinePosition(bool b, bool isRootLineBox) const
132 return height() + marginTop();
135 void RenderSlider::calcPrefWidths()
140 if (style()->width().isFixed() && style()->width().value() > 0)
141 m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
143 m_maxPrefWidth = defaultTrackLength;
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()))
151 m_minPrefWidth = m_maxPrefWidth;
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()));
158 int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
159 m_minPrefWidth += toAdd;
160 m_maxPrefWidth += toAdd;
162 setPrefWidthsDirty(false);
165 void RenderSlider::setStyle(RenderStyle* newStyle)
167 RenderBlock::setStyle(newStyle);
170 RenderStyle* thumbStyle = createThumbStyle(newStyle, m_thumb->renderer()->style());
171 m_thumb->renderer()->setStyle(thumbStyle);
174 setReplaced(isInline());
177 RenderStyle* RenderSlider::createThumbStyle(RenderStyle* parentStyle, RenderStyle* oldStyle)
181 RenderStyle* pseudoStyle = getPseudoStyle(RenderStyle::SLIDER_THUMB);
183 // We may be sharing style with another slider, but we must not share the thumb style.
184 style = new (renderArena()) RenderStyle(*pseudoStyle);
186 style = new (renderArena()) RenderStyle();
189 style->inheritFrom(parentStyle);
191 style->setDisplay(BLOCK);
192 style->setPosition(RelativePosition);
194 style->setLeft(oldStyle->left());
195 style->setTop(oldStyle->top());
198 if (parentStyle->appearance() == SliderVerticalAppearance)
199 style->setAppearance(SliderThumbVerticalAppearance);
200 else if (parentStyle->appearance() == SliderHorizontalAppearance)
201 style->setAppearance(SliderThumbHorizontalAppearance);
202 else if (parentStyle->appearance() == MediaSliderAppearance)
203 style->setAppearance(MediaSliderThumbAppearance);
208 void RenderSlider::layout()
210 bool relayoutChildren = false;
212 if (m_thumb && m_thumb->renderer()) {
214 int oldWidth = m_width;
216 int oldHeight = m_height;
219 if (oldWidth != m_width || oldHeight != m_height)
220 relayoutChildren = true;
222 // Allow the theme to set the size of the thumb
223 if (m_thumb->renderer()->style()->hasAppearance())
224 theme()->adjustSliderThumbSize(m_thumb->renderer());
226 if (style()->appearance() == SliderVerticalAppearance) {
227 // FIXME: Handle percentage widths correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104
228 m_thumb->renderer()->style()->setLeft(Length(m_width / 2 - m_thumb->renderer()->style()->width().value() / 2, Fixed));
230 // FIXME: Handle percentage heights correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104
231 m_thumb->renderer()->style()->setTop(Length(m_height / 2 - m_thumb->renderer()->style()->height().value() / 2, Fixed));
234 if (relayoutChildren)
235 setPositionFromValue(true);
238 RenderBlock::layoutBlock(relayoutChildren);
241 void RenderSlider::updateFromElement()
244 m_thumb = new HTMLSliderThumbElement(document(), node());
245 RenderStyle* thumbStyle = createThumbStyle(style());
246 m_thumb->setRenderer(m_thumb->createRenderer(renderArena(), thumbStyle));
247 m_thumb->renderer()->setStyle(thumbStyle);
248 m_thumb->setAttached();
249 m_thumb->setInDocument(true);
250 addChild(m_thumb->renderer());
252 setPositionFromValue();
253 setNeedsLayout(true);
256 bool RenderSlider::mouseEventIsInThumb(MouseEvent* evt)
258 if (!m_thumb || !m_thumb->renderer())
261 IntRect thumbBounds = m_thumb->renderer()->absoluteBoundingBoxRect();
262 return thumbBounds.contains(evt->pageX(), evt->pageY());
265 void RenderSlider::setValueForPosition(int position)
267 if (!m_thumb || !m_thumb->renderer())
270 const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
271 const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
272 const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
274 double minVal = minStr.isNull() ? 0.0 : minStr.toDouble();
275 double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble();
276 minVal = min(minVal, maxVal); // Make sure the range is sane.
278 // Calculate the new value based on the position
279 double factor = (double)position / (double)trackSize();
280 if (style()->appearance() == SliderVerticalAppearance)
281 factor = 1.0 - factor;
282 double val = minVal + factor * (maxVal - minVal);
284 val = max(minVal, min(val, maxVal)); // Make sure val is within min/max.
286 // Force integer value if not float.
287 if (!equalIgnoringCase(precision, "float"))
290 static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val));
292 if (position != currentPosition())
293 setCurrentPosition(position);
296 double RenderSlider::setPositionFromValue(bool inLayout)
298 if (!m_thumb || !m_thumb->renderer())
302 document()->updateLayout();
304 String value = static_cast<HTMLInputElement*>(node())->value();
305 const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
306 const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
307 const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
309 double minVal = minStr.isNull() ? 0.0 : minStr.toDouble();
310 double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble();
311 minVal = min(minVal, maxVal); // Make sure the range is sane.
313 double oldVal = value.isNull() ? (maxVal + minVal)/2.0 : value.toDouble();
314 double val = max(minVal, min(oldVal, maxVal)); // Make sure val is within min/max.
316 // Force integer value if not float.
317 if (!equalIgnoringCase(precision, "float"))
320 // Calculate the new position based on the value
321 double factor = (val - minVal) / (maxVal - minVal);
322 if (style()->appearance() == SliderVerticalAppearance)
323 factor = 1.0 - factor;
325 setCurrentPosition((int)(factor * trackSize()));
328 static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val));
333 int RenderSlider::positionForOffset(const IntPoint& p)
335 if (!m_thumb || !m_thumb->renderer())
339 if (style()->appearance() == SliderVerticalAppearance) {
340 position = max(0, min(p.y() - (m_thumb->renderer()->absoluteBoundingBoxRect().height() / 2),
341 absoluteBoundingBoxRect().height() - m_thumb->renderer()->absoluteBoundingBoxRect().height()));
343 position = max(0, min(p.x() - (m_thumb->renderer()->absoluteBoundingBoxRect().width() / 2),
344 absoluteBoundingBoxRect().width() - m_thumb->renderer()->absoluteBoundingBoxRect().width()));
349 void RenderSlider::valueChanged()
351 setValueForPosition(currentPosition());
354 int RenderSlider::currentPosition()
356 if (!m_thumb || !m_thumb->renderer())
359 if (style()->appearance() == SliderVerticalAppearance)
360 return m_thumb->renderer()->style()->top().value();
361 return m_thumb->renderer()->style()->left().value();
364 void RenderSlider::setCurrentPosition(int pos)
366 if (!m_thumb || !m_thumb->renderer())
369 if (style()->appearance() == SliderVerticalAppearance)
370 m_thumb->renderer()->style()->setTop(Length(pos, Fixed));
372 m_thumb->renderer()->style()->setLeft(Length(pos, Fixed));
374 m_thumb->renderer()->layer()->updateLayerPosition();
376 m_thumb->renderer()->repaint();
379 int RenderSlider::trackSize()
381 if (!m_thumb || !m_thumb->renderer())
384 if (style()->appearance() == SliderVerticalAppearance)
385 return absoluteBoundingBoxRect().height() - m_thumb->renderer()->absoluteBoundingBoxRect().height();
386 return absoluteBoundingBoxRect().width() - m_thumb->renderer()->absoluteBoundingBoxRect().width();
389 void RenderSlider::forwardEvent(Event* evt)
391 m_thumb->defaultEventHandler(evt);
394 bool RenderSlider::inDragMode() const
396 return m_thumb->inDragMode();
399 } // namespace WebCore