• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  *
3  * Copyright (C) 2006, 2007, 2008 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 "MediaControlElements.h"
35 #include "MouseEvent.h"
36 #include "RenderTheme.h"
37 #include <wtf/MathExtras.h>
38 
39 #ifdef ANDROID_LAYOUT
40 #include "Settings.h"
41 #endif
42 
43 using std::min;
44 
45 namespace WebCore {
46 
47 using namespace HTMLNames;
48 
49 const int defaultTrackLength = 129;
50 
51 class HTMLSliderThumbElement : public HTMLDivElement {
52 public:
53     HTMLSliderThumbElement(Document*, Node* shadowParent = 0);
54 
55     virtual void defaultEventHandler(Event*);
isShadowNode() const56     virtual bool isShadowNode() const { return true; }
shadowParentNode()57     virtual Node* shadowParentNode() { return m_shadowParent; }
58 
inDragMode() const59     bool inDragMode() const { return m_inDragMode; }
60 private:
61     Node* m_shadowParent;
62     FloatPoint m_initialClickPoint;       // initial click point in RenderSlider-local coordinates
63     int m_initialPosition;
64     bool m_inDragMode;
65 };
66 
HTMLSliderThumbElement(Document * doc,Node * shadowParent)67 HTMLSliderThumbElement::HTMLSliderThumbElement(Document* doc, Node* shadowParent)
68     : HTMLDivElement(divTag, doc)
69     , m_shadowParent(shadowParent)
70     , m_initialClickPoint(IntPoint())
71     , m_initialPosition(0)
72     , m_inDragMode(false)
73 {
74 }
75 
defaultEventHandler(Event * event)76 void HTMLSliderThumbElement::defaultEventHandler(Event* event)
77 {
78     const AtomicString& eventType = event->type();
79     if (eventType == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
80         MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
81         RenderSlider* slider;
82         if (document()->frame() && renderer() && renderer()->parent() &&
83                 (slider = static_cast<RenderSlider*>(renderer()->parent())) &&
84                 slider->mouseEventIsInThumb(mouseEvent)) {
85             // Cache the initial point where the mouse down occurred, in slider coordinates
86             m_initialClickPoint = slider->absoluteToLocal(FloatPoint(mouseEvent->pageX(), mouseEvent->pageY()), false, true);
87             // Cache the initial position of the thumb.
88             m_initialPosition = slider->currentPosition();
89             m_inDragMode = true;
90 
91             document()->frame()->eventHandler()->setCapturingMouseEventsNode(m_shadowParent);
92 
93             event->setDefaultHandled();
94             return;
95         }
96     } else if (eventType == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) {
97         if (m_inDragMode) {
98             if (Frame* frame = document()->frame())
99                 frame->eventHandler()->setCapturingMouseEventsNode(0);
100             m_inDragMode = false;
101             event->setDefaultHandled();
102             return;
103         }
104     } else if (eventType == eventNames().mousemoveEvent && event->isMouseEvent()) {
105         if (m_inDragMode && renderer() && renderer()->parent()) {
106             // Move the slider
107             MouseEvent* mouseEvent = static_cast<MouseEvent*>(event);
108             RenderSlider* slider = static_cast<RenderSlider*>(renderer()->parent());
109             FloatPoint curPoint = slider->absoluteToLocal(FloatPoint(mouseEvent->pageX(), mouseEvent->pageY()), false, true);
110             int newPosition = slider->positionForOffset(
111                 IntPoint(m_initialPosition + curPoint.x() - m_initialClickPoint.x()
112                         + (renderBox()->width() / 2),
113                     m_initialPosition + curPoint.y() - m_initialClickPoint.y()
114                         + (renderBox()->height() / 2)));
115             if (slider->currentPosition() != newPosition) {
116                 slider->setCurrentPosition(newPosition);
117                 slider->valueChanged();
118             }
119             event->setDefaultHandled();
120             return;
121         }
122     }
123 
124     HTMLDivElement::defaultEventHandler(event);
125 }
126 
RenderSlider(HTMLInputElement * element)127 RenderSlider::RenderSlider(HTMLInputElement* element)
128     : RenderBlock(element)
129     , m_thumb(0)
130 {
131 }
132 
~RenderSlider()133 RenderSlider::~RenderSlider()
134 {
135     if (m_thumb)
136         m_thumb->detach();
137 }
138 
baselinePosition(bool,bool) const139 int RenderSlider::baselinePosition(bool, bool) const
140 {
141     return height() + marginTop();
142 }
143 
calcPrefWidths()144 void RenderSlider::calcPrefWidths()
145 {
146     m_minPrefWidth = 0;
147     m_maxPrefWidth = 0;
148 
149     if (style()->width().isFixed() && style()->width().value() > 0)
150         m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
151     else
152         m_maxPrefWidth = defaultTrackLength * style()->effectiveZoom();
153 
154     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
155         m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
156         m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
157     } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
158         m_minPrefWidth = 0;
159     else
160         m_minPrefWidth = m_maxPrefWidth;
161 
162     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
163         m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
164         m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
165     }
166 
167     int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
168     m_minPrefWidth += toAdd;
169     m_maxPrefWidth += toAdd;
170 
171     setPrefWidthsDirty(false);
172 }
173 
styleDidChange(RenderStyle::Diff diff,const RenderStyle * oldStyle)174 void RenderSlider::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle)
175 {
176     RenderBlock::styleDidChange(diff, oldStyle);
177 
178     if (m_thumb)
179         m_thumb->renderer()->setStyle(createThumbStyle(style(), m_thumb->renderer()->style()));
180 
181     setReplaced(isInline());
182 }
183 
createThumbStyle(const RenderStyle * parentStyle,const RenderStyle * oldStyle)184 PassRefPtr<RenderStyle> RenderSlider::createThumbStyle(const RenderStyle* parentStyle, const RenderStyle* oldStyle)
185 {
186     RefPtr<RenderStyle> style;
187     RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::SLIDER_THUMB);
188     if (pseudoStyle)
189         // We may be sharing style with another slider, but we must not share the thumb style.
190         style = RenderStyle::clone(pseudoStyle);
191     else
192         style = RenderStyle::create();
193 
194     if (parentStyle)
195         style->inheritFrom(parentStyle);
196 
197     style->setDisplay(BLOCK);
198     style->setPosition(RelativePosition);
199     if (oldStyle) {
200         style->setLeft(oldStyle->left());
201         style->setTop(oldStyle->top());
202     }
203 
204     if (parentStyle->appearance() == SliderVerticalPart)
205        style->setAppearance(SliderThumbVerticalPart);
206     else if (parentStyle->appearance() == SliderHorizontalPart)
207        style->setAppearance(SliderThumbHorizontalPart);
208     else if (parentStyle->appearance() == MediaSliderPart)
209         style->setAppearance(MediaSliderThumbPart);
210 
211     return style.release();
212 }
213 
layout()214 void RenderSlider::layout()
215 {
216     bool relayoutChildren = false;
217 
218     if (m_thumb && m_thumb->renderer()) {
219 
220 #ifdef ANDROID_LAYOUT
221         int oldVisibleWidth = m_visibleWidth;
222 #endif
223 
224         int oldWidth = width();
225         calcWidth();
226         int oldHeight = height();
227         calcHeight();
228 
229         if (oldWidth != width() || oldHeight != height())
230             relayoutChildren = true;
231 
232 #ifdef ANDROID_LAYOUT
233         const Settings* settings = document()->settings();
234         ASSERT(settings);
235         if (oldVisibleWidth != m_visibleWidth
236                 && settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen)
237             relayoutChildren = true;
238 #endif
239 
240         // Allow the theme to set the size of the thumb
241         if (m_thumb->renderer()->style()->hasAppearance())
242             theme()->adjustSliderThumbSize(m_thumb->renderer());
243 
244         if (style()->appearance() == SliderVerticalPart) {
245             // FIXME: Handle percentage widths correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104
246             m_thumb->renderer()->style()->setLeft(Length(contentWidth() / 2 - m_thumb->renderer()->style()->width().value() / 2, Fixed));
247         } else {
248             // FIXME: Handle percentage heights correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104
249             m_thumb->renderer()->style()->setTop(Length(contentHeight() / 2 - m_thumb->renderer()->style()->height().value() / 2, Fixed));
250         }
251 
252         if (relayoutChildren)
253             setPositionFromValue(true);
254     }
255 
256     RenderBlock::layoutBlock(relayoutChildren);
257 }
258 
updateFromElement()259 void RenderSlider::updateFromElement()
260 {
261     if (!m_thumb) {
262         m_thumb = new HTMLSliderThumbElement(document(), node());
263         RefPtr<RenderStyle> thumbStyle = createThumbStyle(style());
264         m_thumb->setRenderer(m_thumb->createRenderer(renderArena(), thumbStyle.get()));
265         m_thumb->renderer()->setStyle(thumbStyle.release());
266         m_thumb->setAttached();
267         m_thumb->setInDocument(true);
268         addChild(m_thumb->renderer());
269     }
270     setPositionFromValue();
271     setNeedsLayout(true, false);
272 }
273 
mouseEventIsInThumb(MouseEvent * evt)274 bool RenderSlider::mouseEventIsInThumb(MouseEvent* evt)
275 {
276     if (!m_thumb || !m_thumb->renderer())
277         return false;
278 
279 #if ENABLE(VIDEO)
280     if (style()->appearance() == MediaSliderPart) {
281         MediaControlInputElement *sliderThumb = static_cast<MediaControlInputElement*>(m_thumb->renderer()->node());
282         IntPoint absPoint(evt->pageX(), evt->pageY());
283         return sliderThumb->hitTest(absPoint);
284     } else
285 #endif
286     {
287         FloatPoint localPoint = m_thumb->renderBox()->absoluteToLocal(FloatPoint(evt->pageX(), evt->pageY()), false, true);
288         IntRect thumbBounds = m_thumb->renderBox()->borderBoxRect();
289         return thumbBounds.contains(roundedIntPoint(localPoint));
290     }
291 }
292 
setValueForPosition(int position)293 void RenderSlider::setValueForPosition(int position)
294 {
295     if (!m_thumb || !m_thumb->renderer())
296         return;
297 
298     const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
299     const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
300     const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
301 
302     double minVal = minStr.isNull() ? 0.0 : minStr.toDouble();
303     double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble();
304     minVal = min(minVal, maxVal); // Make sure the range is sane.
305 
306     // Calculate the new value based on the position
307     double factor = (double)position / (double)trackSize();
308     if (style()->appearance() == SliderVerticalPart)
309         factor = 1.0 - factor;
310     double val = minVal + factor * (maxVal - minVal);
311 
312     val = max(minVal, min(val, maxVal)); // Make sure val is within min/max.
313 
314     // Force integer value if not float.
315     if (!equalIgnoringCase(precision, "float"))
316         val = lround(val);
317 
318     static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val));
319 
320     if (position != currentPosition()) {
321         setCurrentPosition(position);
322         static_cast<HTMLInputElement*>(node())->onChange();
323     }
324 }
325 
setPositionFromValue(bool inLayout)326 double RenderSlider::setPositionFromValue(bool inLayout)
327 {
328     if (!m_thumb || !m_thumb->renderer())
329         return 0;
330 
331     if (!inLayout)
332         document()->updateLayout();
333 
334     String value = static_cast<HTMLInputElement*>(node())->value();
335     const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr);
336     const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr);
337     const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr);
338 
339     double minVal = minStr.isNull() ? 0.0 : minStr.toDouble();
340     double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble();
341     minVal = min(minVal, maxVal); // Make sure the range is sane.
342 
343     double oldVal = value.isNull() ? (maxVal + minVal)/2.0 : value.toDouble();
344     double val = max(minVal, min(oldVal, maxVal)); // Make sure val is within min/max.
345 
346     // Force integer value if not float.
347     if (!equalIgnoringCase(precision, "float"))
348         val = lround(val);
349 
350     // Calculate the new position based on the value
351     double factor = (val - minVal) / (maxVal - minVal);
352     if (style()->appearance() == SliderVerticalPart)
353         factor = 1.0 - factor;
354 
355     setCurrentPosition((int)(factor * trackSize()));
356 
357     if (value.isNull() || val != oldVal)
358         static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val));
359 
360     return val;
361 }
362 
positionForOffset(const IntPoint & p)363 int RenderSlider::positionForOffset(const IntPoint& p)
364 {
365     if (!m_thumb || !m_thumb->renderer())
366         return 0;
367 
368     int position;
369     if (style()->appearance() == SliderVerticalPart)
370         position = p.y() - m_thumb->renderBox()->height() / 2;
371     else
372         position = p.x() - m_thumb->renderBox()->width() / 2;
373 
374     return max(0, min(position, trackSize()));
375 }
376 
valueChanged()377 void RenderSlider::valueChanged()
378 {
379     setValueForPosition(currentPosition());
380     static_cast<HTMLInputElement*>(node())->onChange();
381 }
382 
currentPosition()383 int RenderSlider::currentPosition()
384 {
385     if (!m_thumb || !m_thumb->renderer())
386         return 0;
387 
388     if (style()->appearance() == SliderVerticalPart)
389         return m_thumb->renderer()->style()->top().value();
390     return m_thumb->renderer()->style()->left().value();
391 }
392 
setCurrentPosition(int pos)393 void RenderSlider::setCurrentPosition(int pos)
394 {
395     if (!m_thumb || !m_thumb->renderer())
396         return;
397 
398     if (style()->appearance() == SliderVerticalPart)
399         m_thumb->renderer()->style()->setTop(Length(pos, Fixed));
400     else
401         m_thumb->renderer()->style()->setLeft(Length(pos, Fixed));
402 
403     m_thumb->renderBox()->layer()->updateLayerPosition();
404     repaint();
405     m_thumb->renderer()->repaint();
406 }
407 
trackSize()408 int RenderSlider::trackSize()
409 {
410     if (!m_thumb || !m_thumb->renderer())
411         return 0;
412 
413     if (style()->appearance() == SliderVerticalPart)
414         return contentHeight() - m_thumb->renderBox()->height();
415     return contentWidth() - m_thumb->renderBox()->width();
416 }
417 
forwardEvent(Event * evt)418 void RenderSlider::forwardEvent(Event* evt)
419 {
420     m_thumb->defaultEventHandler(evt);
421 }
422 
inDragMode() const423 bool RenderSlider::inDragMode() const
424 {
425     return m_thumb->inDragMode();
426 }
427 
428 } // namespace WebCore
429