2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
3 * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include "RenderListBox.h"
33 #include "AXObjectCache.h"
34 #include "CSSFontSelector.h"
36 #include "DocumentEventQueue.h"
37 #include "EventHandler.h"
38 #include "FocusController.h"
39 #include "FontCache.h"
41 #include "FrameSelection.h"
42 #include "FrameView.h"
43 #include "GraphicsContext.h"
44 #include "HTMLNames.h"
45 #include "HTMLOptionElement.h"
46 #include "HTMLOptGroupElement.h"
47 #include "HTMLSelectElement.h"
48 #include "HitTestResult.h"
49 #include "NodeRenderStyle.h"
51 #include "PaintInfo.h"
52 #include "RenderLayer.h"
53 #include "RenderScrollbar.h"
54 #include "RenderText.h"
55 #include "RenderTheme.h"
56 #include "RenderView.h"
57 #include "Scrollbar.h"
58 #include "ScrollbarTheme.h"
59 #include "SpatialNavigation.h"
60 #include "StyleResolver.h"
62 #include <wtf/StackStats.h>
68 using namespace HTMLNames;
70 const int rowSpacing = 1;
72 const int optionsSpacingHorizontal = 2;
74 // The minSize constant was originally defined to render scrollbars correctly.
75 // This might vary for different platforms.
76 const int minSize = 4;
78 // Default size when the multiple attribute is present but size attribute is absent.
79 const int defaultSize = 4;
81 // FIXME: This hardcoded baselineAdjustment is what we used to do for the old
82 // widget, but I'm not sure this is right for the new control.
83 const int baselineAdjustment = 7;
85 RenderListBox::RenderListBox(Element* element)
86 : RenderBlock(element)
87 , m_optionsChanged(true)
88 , m_scrollToRevealSelectionAfterLayout(false)
89 , m_inAutoscroll(false)
94 ASSERT(element->isHTMLElement());
95 ASSERT(element->hasTagName(HTMLNames::selectTag));
97 if (FrameView* frameView = frame()->view())
98 frameView->addScrollableArea(this);
101 RenderListBox::~RenderListBox()
103 setHasVerticalScrollbar(false);
105 if (FrameView* frameView = frame()->view())
106 frameView->removeScrollableArea(this);
109 inline HTMLSelectElement* RenderListBox::selectElement() const
111 return toHTMLSelectElement(node());
114 void RenderListBox::updateFromElement()
116 FontCachePurgePreventer fontCachePurgePreventer;
118 if (m_optionsChanged) {
119 const Vector<HTMLElement*>& listItems = selectElement()->listItems();
120 int size = numItems();
123 for (int i = 0; i < size; ++i) {
124 HTMLElement* element = listItems[i];
126 Font itemFont = style()->font();
127 if (isHTMLOptionElement(element))
128 text = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
129 else if (isHTMLOptGroupElement(element)) {
130 text = toHTMLOptGroupElement(element)->groupLabelText();
131 FontDescription d = itemFont.fontDescription();
132 d.setWeight(d.bolderWeight());
133 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
134 itemFont.update(document()->ensureStyleResolver().fontSelector());
137 if (!text.isEmpty()) {
138 applyTextTransform(style(), text, ' ');
139 // FIXME: Why is this always LTR? Can't text direction affect the width?
140 TextRun textRun = constructTextRun(this, itemFont, text, style(), TextRun::AllowTrailingExpansion);
141 textRun.disableRoundingHacks();
142 float textWidth = itemFont.width(textRun);
143 width = max(width, textWidth);
146 m_optionsWidth = static_cast<int>(ceilf(width));
147 m_optionsChanged = false;
149 setHasVerticalScrollbar(true);
151 setNeedsLayoutAndPrefWidthsRecalc();
155 bool RenderListBox::canBeReplacedWithInlineRunIn() const
160 void RenderListBox::selectionChanged()
163 if (!m_inAutoscroll) {
164 if (m_optionsChanged || needsLayout())
165 m_scrollToRevealSelectionAfterLayout = true;
167 scrollToRevealSelection();
170 if (AXObjectCache* cache = document()->existingAXObjectCache())
171 cache->selectedChildrenChanged(this);
174 void RenderListBox::layout()
176 StackStats::LayoutCheckPoint layoutCheckPoint;
177 RenderBlock::layout();
180 bool enabled = numVisibleItems() < numItems();
181 m_vBar->setEnabled(enabled);
182 m_vBar->setSteps(1, max(1, numVisibleItems() - 1), itemHeight());
183 m_vBar->setProportion(numVisibleItems(), numItems());
185 scrollToOffsetWithoutAnimation(VerticalScrollbar, 0);
190 if (m_scrollToRevealSelectionAfterLayout) {
191 LayoutStateDisabler layoutStateDisabler(view());
192 scrollToRevealSelection();
196 void RenderListBox::scrollToRevealSelection()
198 HTMLSelectElement* select = selectElement();
200 m_scrollToRevealSelectionAfterLayout = false;
202 int firstIndex = select->activeSelectionStartListIndex();
203 if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex()))
204 scrollToRevealElementAtListIndex(firstIndex);
207 void RenderListBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
209 maxLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal;
211 maxLogicalWidth += m_vBar->width();
212 if (!style()->width().isPercent())
213 minLogicalWidth = maxLogicalWidth;
216 void RenderListBox::computePreferredLogicalWidths()
218 ASSERT(!m_optionsChanged);
220 m_minPreferredLogicalWidth = 0;
221 m_maxPreferredLogicalWidth = 0;
223 if (style()->width().isFixed() && style()->width().value() > 0)
224 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value());
226 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
228 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
229 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value()));
230 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value()));
233 if (style()->maxWidth().isFixed()) {
234 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value()));
235 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value()));
238 LayoutUnit toAdd = borderAndPaddingWidth();
239 m_minPreferredLogicalWidth += toAdd;
240 m_maxPreferredLogicalWidth += toAdd;
242 setPreferredLogicalWidthsDirty(false);
245 int RenderListBox::size() const
247 int specifiedSize = selectElement()->size();
248 if (specifiedSize > 1)
249 return max(minSize, specifiedSize);
254 int RenderListBox::numVisibleItems() const
256 // Only count fully visible rows. But don't return 0 even if only part of a row shows.
257 return max<int>(1, (contentHeight() + rowSpacing) / itemHeight());
260 int RenderListBox::numItems() const
262 return selectElement()->listItems().size();
265 LayoutUnit RenderListBox::listHeight() const
267 return itemHeight() * numItems() - rowSpacing;
270 void RenderListBox::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
272 LayoutUnit height = itemHeight() * size() - rowSpacing + borderAndPaddingHeight();
273 RenderBox::computeLogicalHeight(height, logicalTop, computedValues);
276 int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const
278 return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, linePositionMode) - baselineAdjustment;
281 LayoutRect RenderListBox::itemBoundingBoxRect(const LayoutPoint& additionalOffset, int index)
283 return LayoutRect(additionalOffset.x() + borderLeft() + paddingLeft(),
284 additionalOffset.y() + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset),
285 contentWidth(), itemHeight());
288 void RenderListBox::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
290 if (style()->visibility() != VISIBLE)
293 int listItemsSize = numItems();
295 if (paintInfo.phase == PaintPhaseForeground) {
296 int index = m_indexOffset;
297 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
298 paintItemForeground(paintInfo, paintOffset, index);
303 // Paint the children.
304 RenderBlock::paintObject(paintInfo, paintOffset);
306 switch (paintInfo.phase) {
307 // Depending on whether we have overlay scrollbars they
308 // get rendered in the foreground or background phases
309 case PaintPhaseForeground:
310 if (m_vBar->isOverlayScrollbar())
311 paintScrollbar(paintInfo, paintOffset);
313 case PaintPhaseBlockBackground:
314 if (!m_vBar->isOverlayScrollbar())
315 paintScrollbar(paintInfo, paintOffset);
317 case PaintPhaseChildBlockBackground:
318 case PaintPhaseChildBlockBackgrounds: {
319 int index = m_indexOffset;
320 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) {
321 paintItemBackground(paintInfo, paintOffset, index);
331 void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
333 if (!isSpatialNavigationEnabled(frame()))
334 return RenderBlock::addFocusRingRects(rects, additionalOffset, paintContainer);
336 HTMLSelectElement* select = selectElement();
338 // Focus the last selected item.
339 int selectedItem = select->activeSelectionEndListIndex();
340 if (selectedItem >= 0) {
341 rects.append(pixelSnappedIntRect(itemBoundingBoxRect(additionalOffset, selectedItem)));
345 // No selected items, find the first non-disabled item.
346 int size = numItems();
347 const Vector<HTMLElement*>& listItems = select->listItems();
348 for (int i = 0; i < size; ++i) {
349 HTMLElement* element = listItems[i];
350 if (isHTMLOptionElement(element) && !element->isDisabledFormControl()) {
351 select->setActiveSelectionEndIndex(i);
352 rects.append(pixelSnappedIntRect(itemBoundingBoxRect(additionalOffset, i)));
358 void RenderListBox::paintScrollbar(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
361 IntRect scrollRect = pixelSnappedIntRect(paintOffset.x() + width() - borderRight() - m_vBar->width(),
362 paintOffset.y() + borderTop(),
364 height() - (borderTop() + borderBottom()));
365 m_vBar->setFrameRect(scrollRect);
366 m_vBar->paint(paintInfo.context, paintInfo.rect);
370 static LayoutSize itemOffsetForAlignment(TextRun textRun, RenderStyle* itemStyle, Font itemFont, LayoutRect itemBoudingBox)
372 ETextAlign actualAlignment = itemStyle->textAlign();
373 // FIXME: Firefox doesn't respect JUSTIFY. Should we?
374 // FIXME: Handle TAEND here
375 if (actualAlignment == TASTART || actualAlignment == JUSTIFY)
376 actualAlignment = itemStyle->isLeftToRightDirection() ? LEFT : RIGHT;
378 LayoutSize offset = LayoutSize(0, itemFont.fontMetrics().ascent());
379 if (actualAlignment == RIGHT || actualAlignment == WEBKIT_RIGHT) {
380 float textWidth = itemFont.width(textRun);
381 offset.setWidth(itemBoudingBox.width() - textWidth - optionsSpacingHorizontal);
382 } else if (actualAlignment == CENTER || actualAlignment == WEBKIT_CENTER) {
383 float textWidth = itemFont.width(textRun);
384 offset.setWidth((itemBoudingBox.width() - textWidth) / 2);
386 offset.setWidth(optionsSpacingHorizontal);
390 void RenderListBox::paintItemForeground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
392 FontCachePurgePreventer fontCachePurgePreventer;
394 HTMLSelectElement* select = selectElement();
396 const Vector<HTMLElement*>& listItems = select->listItems();
397 HTMLElement* element = listItems[listIndex];
399 RenderStyle* itemStyle = element->renderStyle();
403 if (itemStyle->visibility() == HIDDEN)
407 bool isOptionElement = isHTMLOptionElement(element);
409 itemText = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel();
410 else if (isHTMLOptGroupElement(element))
411 itemText = toHTMLOptGroupElement(element)->groupLabelText();
412 applyTextTransform(style(), itemText, ' ');
414 Color textColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor);
415 if (isOptionElement && toHTMLOptionElement(element)->selected()) {
416 if (frame()->selection().isFocusedAndActive() && document()->focusedElement() == node())
417 textColor = theme()->activeListBoxSelectionForegroundColor();
418 // Honor the foreground color for disabled items
419 else if (!element->isDisabledFormControl() && !select->isDisabledFormControl())
420 textColor = theme()->inactiveListBoxSelectionForegroundColor();
423 ColorSpace colorSpace = itemStyle->colorSpace();
424 paintInfo.context->setFillColor(textColor, colorSpace);
426 TextRun textRun(itemText, 0, 0, TextRun::AllowTrailingExpansion, itemStyle->direction(), isOverride(itemStyle->unicodeBidi()), true, TextRun::NoRounding);
427 Font itemFont = style()->font();
428 LayoutRect r = itemBoundingBoxRect(paintOffset, listIndex);
429 r.move(itemOffsetForAlignment(textRun, itemStyle, itemFont, r));
431 if (isHTMLOptGroupElement(element)) {
432 FontDescription d = itemFont.fontDescription();
433 d.setWeight(d.bolderWeight());
434 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
435 itemFont.update(document()->ensureStyleResolver().fontSelector());
438 // Draw the item text
439 paintInfo.context->drawBidiText(itemFont, textRun, roundedIntPoint(r.location()));
442 void RenderListBox::paintItemBackground(PaintInfo& paintInfo, const LayoutPoint& paintOffset, int listIndex)
444 const Vector<HTMLElement*>& listItems = selectElement()->listItems();
445 HTMLElement* element = listItems[listIndex];
448 if (isHTMLOptionElement(element) && toHTMLOptionElement(element)->selected()) {
449 if (frame()->selection().isFocusedAndActive() && document()->focusedElement() == node())
450 backColor = theme()->activeListBoxSelectionBackgroundColor();
452 backColor = theme()->inactiveListBoxSelectionBackgroundColor();
454 backColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor) : style()->visitedDependentColor(CSSPropertyBackgroundColor);
456 // Draw the background for this list box item
457 if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) {
458 ColorSpace colorSpace = element->renderStyle() ? element->renderStyle()->colorSpace() : style()->colorSpace();
459 LayoutRect itemRect = itemBoundingBoxRect(paintOffset, listIndex);
460 itemRect.intersect(controlClipRect(paintOffset));
461 paintInfo.context->fillRect(pixelSnappedIntRect(itemRect), backColor, colorSpace);
465 bool RenderListBox::isPointInOverflowControl(HitTestResult& result, const LayoutPoint& locationInContainer, const LayoutPoint& accumulatedOffset)
467 if (!m_vBar || !m_vBar->shouldParticipateInHitTesting())
470 LayoutRect vertRect(accumulatedOffset.x() + width() - borderRight() - m_vBar->width(),
471 accumulatedOffset.y() + borderTop(),
473 height() - borderTop() - borderBottom());
475 if (vertRect.contains(locationInContainer)) {
476 result.setScrollbar(m_vBar.get());
482 int RenderListBox::listIndexAtOffset(const LayoutSize& offset)
487 if (offset.height() < borderTop() + paddingTop() || offset.height() > height() - paddingBottom() - borderBottom())
490 int scrollbarWidth = m_vBar ? m_vBar->width() : 0;
491 if (offset.width() < borderLeft() + paddingLeft() || offset.width() > width() - borderRight() - paddingRight() - scrollbarWidth)
494 int newOffset = (offset.height() - borderTop() - paddingTop()) / itemHeight() + m_indexOffset;
495 return newOffset < numItems() ? newOffset : -1;
498 void RenderListBox::panScroll(const IntPoint& panStartMousePosition)
500 const int maxSpeed = 20;
501 const int iconRadius = 7;
502 const int speedReducer = 4;
504 // FIXME: This doesn't work correctly with transforms.
505 FloatPoint absOffset = localToAbsolute();
507 IntPoint lastKnownMousePosition = frame()->eventHandler().lastKnownMousePosition();
508 // We need to check if the last known mouse position is out of the window. When the mouse is out of the window, the position is incoherent
509 static IntPoint previousMousePosition;
510 if (lastKnownMousePosition.y() < 0)
511 lastKnownMousePosition = previousMousePosition;
513 previousMousePosition = lastKnownMousePosition;
515 int yDelta = lastKnownMousePosition.y() - panStartMousePosition.y();
517 // If the point is too far from the center we limit the speed
518 yDelta = max<int>(min<int>(yDelta, maxSpeed), -maxSpeed);
520 if (abs(yDelta) < iconRadius) // at the center we let the space for the icon
524 //offsetY = view()->viewHeight();
525 absOffset.move(0, listHeight());
529 // Let's attenuate the speed
530 yDelta /= speedReducer;
532 IntPoint scrollPoint(0, 0);
533 scrollPoint.setY(absOffset.y() + yDelta);
534 int newOffset = scrollToward(scrollPoint);
538 m_inAutoscroll = true;
539 HTMLSelectElement* select = selectElement();
540 select->updateListBoxSelection(!select->multiple());
541 m_inAutoscroll = false;
544 int RenderListBox::scrollToward(const IntPoint& destination)
546 // FIXME: This doesn't work correctly with transforms.
547 FloatPoint absPos = localToAbsolute();
548 IntSize positionOffset = roundedIntSize(destination - absPos);
550 int rows = numVisibleItems();
551 int offset = m_indexOffset;
553 if (positionOffset.height() < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1))
556 if (positionOffset.height() > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows))
557 return offset + rows - 1;
559 return listIndexAtOffset(positionOffset);
562 void RenderListBox::autoscroll(const IntPoint&)
564 IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler().lastKnownMousePosition());
566 int endIndex = scrollToward(pos);
567 if (selectElement()->isDisabledFormControl())
571 HTMLSelectElement* select = selectElement();
572 m_inAutoscroll = true;
574 if (!select->multiple())
575 select->setActiveSelectionAnchorIndex(endIndex);
577 select->setActiveSelectionEndIndex(endIndex);
578 select->updateListBoxSelection(!select->multiple());
579 m_inAutoscroll = false;
583 void RenderListBox::stopAutoscroll()
585 if (selectElement()->isDisabledFormControl())
588 selectElement()->listBoxOnChange();
591 bool RenderListBox::scrollToRevealElementAtListIndex(int index)
593 if (index < 0 || index >= numItems() || listIndexIsVisible(index))
597 if (index < m_indexOffset)
600 newOffset = index - numVisibleItems() + 1;
602 scrollToOffsetWithoutAnimation(VerticalScrollbar, newOffset);
607 bool RenderListBox::listIndexIsVisible(int index)
609 return index >= m_indexOffset && index < m_indexOffset + numVisibleItems();
612 bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node**)
614 return ScrollableArea::scroll(direction, granularity, multiplier);
617 bool RenderListBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node**)
619 return ScrollableArea::scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier);
622 void RenderListBox::valueChanged(unsigned listIndex)
624 HTMLSelectElement* element = selectElement();
625 element->setSelectedIndex(element->listToOptionIndex(listIndex));
626 element->dispatchFormControlChangeEvent();
629 int RenderListBox::scrollSize(ScrollbarOrientation orientation) const
631 return ((orientation == VerticalScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0;
634 int RenderListBox::scrollPosition(Scrollbar*) const
636 return m_indexOffset;
639 void RenderListBox::setScrollOffset(const IntPoint& offset)
641 scrollTo(offset.y());
644 void RenderListBox::scrollTo(int newOffset)
646 if (newOffset == m_indexOffset)
649 m_indexOffset = newOffset;
651 node()->document()->eventQueue()->enqueueOrDispatchScrollEvent(node(), DocumentEventQueue::ScrollEventElementTarget);
654 LayoutUnit RenderListBox::itemHeight() const
656 return style()->fontMetrics().height() + rowSpacing;
659 int RenderListBox::verticalScrollbarWidth() const
661 return m_vBar && !m_vBar->isOverlayScrollbar() ? m_vBar->width() : 0;
664 // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's
665 // how the control currently paints.
666 int RenderListBox::scrollWidth() const
668 // There is no horizontal scrolling allowed.
669 return pixelSnappedClientWidth();
672 int RenderListBox::scrollHeight() const
674 return max(pixelSnappedClientHeight(), roundToInt(listHeight()));
677 int RenderListBox::scrollLeft() const
682 void RenderListBox::setScrollLeft(int)
686 int RenderListBox::scrollTop() const
688 return m_indexOffset * itemHeight();
691 void RenderListBox::setScrollTop(int newTop)
693 // Determine an index and scroll to it.
694 int index = newTop / itemHeight();
695 if (index < 0 || index >= numItems() || index == m_indexOffset)
698 scrollToOffsetWithoutAnimation(VerticalScrollbar, index);
701 bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
703 if (!RenderBlock::nodeAtPoint(request, result, locationInContainer, accumulatedOffset, hitTestAction))
705 const Vector<HTMLElement*>& listItems = selectElement()->listItems();
706 int size = numItems();
707 LayoutPoint adjustedLocation = accumulatedOffset + location();
709 for (int i = 0; i < size; ++i) {
710 if (itemBoundingBoxRect(adjustedLocation, i).contains(locationInContainer.point())) {
711 if (Element* node = listItems[i]) {
712 result.setInnerNode(node);
713 if (!result.innerNonSharedNode())
714 result.setInnerNonSharedNode(node);
715 result.setLocalPoint(locationInContainer.point() - toLayoutSize(adjustedLocation));
724 LayoutRect RenderListBox::controlClipRect(const LayoutPoint& additionalOffset) const
726 LayoutRect clipRect = contentBoxRect();
727 clipRect.moveBy(additionalOffset);
731 bool RenderListBox::isActive() const
733 Page* page = frame()->page();
734 return page && page->focusController().isActive();
737 void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
739 IntRect scrollRect = rect;
740 scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop());
741 repaintRectangle(scrollRect);
744 IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
746 RenderView* view = this->view();
748 return scrollbarRect;
750 IntRect rect = scrollbarRect;
752 int scrollbarLeft = width() - borderRight() - scrollbar->width();
753 int scrollbarTop = borderTop();
754 rect.move(scrollbarLeft, scrollbarTop);
756 return view->frameView().convertFromRenderer(this, rect);
759 IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
761 RenderView* view = this->view();
765 IntRect rect = view->frameView().convertToRenderer(this, parentRect);
767 int scrollbarLeft = width() - borderRight() - scrollbar->width();
768 int scrollbarTop = borderTop();
769 rect.move(-scrollbarLeft, -scrollbarTop);
773 IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
775 RenderView* view = this->view();
777 return scrollbarPoint;
779 IntPoint point = scrollbarPoint;
781 int scrollbarLeft = width() - borderRight() - scrollbar->width();
782 int scrollbarTop = borderTop();
783 point.move(scrollbarLeft, scrollbarTop);
785 return view->frameView().convertFromRenderer(this, point);
788 IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
790 RenderView* view = this->view();
794 IntPoint point = view->frameView().convertToRenderer(this, parentPoint);
796 int scrollbarLeft = width() - borderRight() - scrollbar->width();
797 int scrollbarTop = borderTop();
798 point.move(-scrollbarLeft, -scrollbarTop);
802 IntSize RenderListBox::contentsSize() const
804 return IntSize(scrollWidth(), scrollHeight());
807 int RenderListBox::visibleHeight() const
812 int RenderListBox::visibleWidth() const
817 IntPoint RenderListBox::lastKnownMousePosition() const
819 RenderView* view = this->view();
822 return view->frameView().lastKnownMousePosition();
825 bool RenderListBox::isHandlingWheelEvent() const
827 RenderView* view = this->view();
830 return view->frameView().isHandlingWheelEvent();
833 bool RenderListBox::shouldSuspendScrollAnimations() const
835 RenderView* view = this->view();
838 return view->frameView().shouldSuspendScrollAnimations();
841 bool RenderListBox::scrollbarsCanBeActive() const
843 RenderView* view = this->view();
846 return view->frameView().scrollbarsCanBeActive();
849 bool RenderListBox::scrollbarAnimationsAreSuppressed() const
851 RenderView* view = this->view();
854 return view->frameView().scrollbarAnimationsAreSuppressed();
857 ScrollableArea* RenderListBox::enclosingScrollableArea() const
859 // FIXME: Return a RenderLayer that's scrollable.
863 IntRect RenderListBox::scrollableAreaBoundingBox() const
865 return absoluteBoundingBoxRect();
868 PassRefPtr<Scrollbar> RenderListBox::createScrollbar()
870 RefPtr<Scrollbar> widget;
871 bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
872 if (hasCustomScrollbarStyle)
873 widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this->node());
875 widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart));
876 didAddScrollbar(widget.get(), VerticalScrollbar);
878 document()->view()->addChild(widget.get());
879 return widget.release();
882 void RenderListBox::destroyScrollbar()
887 if (!m_vBar->isCustomScrollbar())
888 ScrollableArea::willRemoveScrollbar(m_vBar.get(), VerticalScrollbar);
889 m_vBar->removeFromParent();
890 m_vBar->disconnectFromScrollableArea();
894 void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar)
896 if (hasScrollbar == (m_vBar != 0))
900 m_vBar = createScrollbar();
905 m_vBar->styleChanged();
907 // Force an update since we know the scrollbars have changed things.
908 #if ENABLE(DASHBOARD_SUPPORT) || ENABLE(DRAGGABLE_REGION)
909 if (document()->hasAnnotatedRegions())
910 document()->setAnnotatedRegionsDirty(true);
914 } // namespace WebCore