/* | |
* This file is part of the select element renderer in WebCore. | |
* | |
* Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. | |
* 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
* its contributors may be used to endorse or promote products derived | |
* from this software without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#include "config.h" | |
#include "RenderListBox.h" | |
#include "AXObjectCache.h" | |
#include "CSSStyleSelector.h" | |
#include "Document.h" | |
#include "EventHandler.h" | |
#include "EventNames.h" | |
#include "FocusController.h" | |
#include "Frame.h" | |
#include "FrameView.h" | |
#include "GraphicsContext.h" | |
#include "HTMLNames.h" | |
#include "HitTestResult.h" | |
#include "OptionGroupElement.h" | |
#include "OptionElement.h" | |
#include "Page.h" | |
#include "RenderScrollbar.h" | |
#include "RenderTheme.h" | |
#include "RenderView.h" | |
#include "Scrollbar.h" | |
#include "SelectElement.h" | |
#include "SelectionController.h" | |
#include "NodeRenderStyle.h" | |
#include <math.h> | |
using namespace std; | |
namespace WebCore { | |
using namespace HTMLNames; | |
const int rowSpacing = 1; | |
const int optionsSpacingHorizontal = 2; | |
const int minSize = 4; | |
const int maxDefaultSize = 10; | |
// FIXME: This hardcoded baselineAdjustment is what we used to do for the old | |
// widget, but I'm not sure this is right for the new control. | |
const int baselineAdjustment = 7; | |
RenderListBox::RenderListBox(Element* element) | |
: RenderBlock(element) | |
, m_optionsChanged(true) | |
, m_scrollToRevealSelectionAfterLayout(false) | |
, m_inAutoscroll(false) | |
, m_optionsWidth(0) | |
, m_indexOffset(0) | |
{ | |
} | |
RenderListBox::~RenderListBox() | |
{ | |
setHasVerticalScrollbar(false); | |
} | |
void RenderListBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) | |
{ | |
RenderBlock::styleDidChange(diff, oldStyle); | |
setReplaced(isInline()); | |
} | |
void RenderListBox::updateFromElement() | |
{ | |
if (m_optionsChanged) { | |
const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems(); | |
int size = numItems(); | |
float width = 0; | |
for (int i = 0; i < size; ++i) { | |
Element* element = listItems[i]; | |
String text; | |
Font itemFont = style()->font(); | |
if (OptionElement* optionElement = toOptionElement(element)) | |
text = optionElement->textIndentedToRespectGroupLabel(); | |
else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) { | |
text = optionGroupElement->groupLabelText(); | |
FontDescription d = itemFont.fontDescription(); | |
d.setWeight(d.bolderWeight()); | |
itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); | |
itemFont.update(document()->styleSelector()->fontSelector()); | |
} | |
if (!text.isEmpty()) { | |
float textWidth = itemFont.floatWidth(TextRun(text.impl(), 0, 0, 0, false, false, false, false)); | |
width = max(width, textWidth); | |
} | |
} | |
m_optionsWidth = static_cast<int>(ceilf(width)); | |
m_optionsChanged = false; | |
setHasVerticalScrollbar(true); | |
setNeedsLayoutAndPrefWidthsRecalc(); | |
} | |
} | |
void RenderListBox::selectionChanged() | |
{ | |
repaint(); | |
if (!m_inAutoscroll) { | |
if (m_optionsChanged || needsLayout()) | |
m_scrollToRevealSelectionAfterLayout = true; | |
else | |
scrollToRevealSelection(); | |
} | |
if (AXObjectCache::accessibilityEnabled()) | |
document()->axObjectCache()->selectedChildrenChanged(this); | |
} | |
void RenderListBox::layout() | |
{ | |
RenderBlock::layout(); | |
if (m_scrollToRevealSelectionAfterLayout) | |
scrollToRevealSelection(); | |
} | |
void RenderListBox::scrollToRevealSelection() | |
{ | |
SelectElement* select = toSelectElement(static_cast<Element*>(node())); | |
m_scrollToRevealSelectionAfterLayout = false; | |
int firstIndex = select->activeSelectionStartListIndex(); | |
if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex())) | |
scrollToRevealElementAtListIndex(firstIndex); | |
} | |
void RenderListBox::calcPrefWidths() | |
{ | |
ASSERT(!m_optionsChanged); | |
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 = m_optionsWidth + 2 * optionsSpacingHorizontal; | |
if (m_vBar) | |
m_maxPrefWidth += m_vBar->width(); | |
} | |
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); | |
} | |
int RenderListBox::size() const | |
{ | |
int specifiedSize = toSelectElement(static_cast<Element*>(node()))->size(); | |
if (specifiedSize > 1) | |
return max(minSize, specifiedSize); | |
return min(max(minSize, numItems()), maxDefaultSize); | |
} | |
int RenderListBox::numVisibleItems() const | |
{ | |
// Only count fully visible rows. But don't return 0 even if only part of a row shows. | |
return max(1, (contentHeight() + rowSpacing) / itemHeight()); | |
} | |
int RenderListBox::numItems() const | |
{ | |
return toSelectElement(static_cast<Element*>(node()))->listItems().size(); | |
} | |
int RenderListBox::listHeight() const | |
{ | |
return itemHeight() * numItems() - rowSpacing; | |
} | |
void RenderListBox::calcHeight() | |
{ | |
int toAdd = paddingTop() + paddingBottom() + borderTop() + borderBottom(); | |
int itemHeight = RenderListBox::itemHeight(); | |
setHeight(itemHeight * size() - rowSpacing + toAdd); | |
RenderBlock::calcHeight(); | |
if (m_vBar) { | |
bool enabled = numVisibleItems() < numItems(); | |
m_vBar->setEnabled(enabled); | |
m_vBar->setSteps(1, min(1, numVisibleItems() - 1), itemHeight); | |
m_vBar->setProportion(numVisibleItems(), numItems()); | |
if (!enabled) | |
m_indexOffset = 0; | |
} | |
} | |
int RenderListBox::baselinePosition(bool, bool) const | |
{ | |
return height() + marginTop() + marginBottom() - baselineAdjustment; | |
} | |
IntRect RenderListBox::itemBoundingBoxRect(int tx, int ty, int index) | |
{ | |
return IntRect(tx + borderLeft() + paddingLeft(), | |
ty + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset), | |
contentWidth(), itemHeight()); | |
} | |
void RenderListBox::paintObject(PaintInfo& paintInfo, int tx, int ty) | |
{ | |
if (style()->visibility() != VISIBLE) | |
return; | |
int listItemsSize = numItems(); | |
if (paintInfo.phase == PaintPhaseForeground) { | |
int index = m_indexOffset; | |
while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { | |
paintItemForeground(paintInfo, tx, ty, index); | |
index++; | |
} | |
} | |
// Paint the children. | |
RenderBlock::paintObject(paintInfo, tx, ty); | |
if (paintInfo.phase == PaintPhaseBlockBackground) | |
paintScrollbar(paintInfo, tx, ty); | |
else if (paintInfo.phase == PaintPhaseChildBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { | |
int index = m_indexOffset; | |
while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { | |
paintItemBackground(paintInfo, tx, ty, index); | |
index++; | |
} | |
} | |
} | |
void RenderListBox::paintScrollbar(PaintInfo& paintInfo, int tx, int ty) | |
{ | |
if (m_vBar) { | |
IntRect scrollRect(tx + width() - borderRight() - m_vBar->width(), | |
ty + borderTop(), | |
m_vBar->width(), | |
height() - (borderTop() + borderBottom())); | |
m_vBar->setFrameRect(scrollRect); | |
m_vBar->paint(paintInfo.context, paintInfo.rect); | |
} | |
} | |
void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, int listIndex) | |
{ | |
SelectElement* select = toSelectElement(static_cast<Element*>(node())); | |
const Vector<Element*>& listItems = select->listItems(); | |
Element* element = listItems[listIndex]; | |
OptionElement* optionElement = toOptionElement(element); | |
String itemText; | |
if (optionElement) | |
itemText = optionElement->textIndentedToRespectGroupLabel(); | |
else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) | |
itemText = optionGroupElement->groupLabelText(); | |
// Determine where the item text should be placed | |
IntRect r = itemBoundingBoxRect(tx, ty, listIndex); | |
r.move(optionsSpacingHorizontal, style()->font().ascent()); | |
RenderStyle* itemStyle = element->renderStyle(); | |
if (!itemStyle) | |
itemStyle = style(); | |
Color textColor = element->renderStyle() ? element->renderStyle()->color() : style()->color(); | |
if (optionElement && optionElement->selected()) { | |
if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) | |
textColor = theme()->activeListBoxSelectionForegroundColor(); | |
// Honor the foreground color for disabled items | |
else if (!element->disabled()) | |
textColor = theme()->inactiveListBoxSelectionForegroundColor(); | |
} | |
ColorSpace colorSpace = itemStyle->colorSpace(); | |
paintInfo.context->setFillColor(textColor, colorSpace); | |
Font itemFont = style()->font(); | |
if (isOptionGroupElement(element)) { | |
FontDescription d = itemFont.fontDescription(); | |
d.setWeight(d.bolderWeight()); | |
itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); | |
itemFont.update(document()->styleSelector()->fontSelector()); | |
} | |
unsigned length = itemText.length(); | |
const UChar* string = itemText.characters(); | |
TextRun textRun(string, length, 0, 0, 0, itemStyle->direction() == RTL, itemStyle->unicodeBidi() == Override, false, false); | |
// Draw the item text | |
if (itemStyle->visibility() != HIDDEN) | |
paintInfo.context->drawBidiText(itemFont, textRun, r.location()); | |
} | |
void RenderListBox::paintItemBackground(PaintInfo& paintInfo, int tx, int ty, int listIndex) | |
{ | |
SelectElement* select = toSelectElement(static_cast<Element*>(node())); | |
const Vector<Element*>& listItems = select->listItems(); | |
Element* element = listItems[listIndex]; | |
OptionElement* optionElement = toOptionElement(element); | |
Color backColor; | |
if (optionElement && optionElement->selected()) { | |
if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) | |
backColor = theme()->activeListBoxSelectionBackgroundColor(); | |
else | |
backColor = theme()->inactiveListBoxSelectionBackgroundColor(); | |
} else | |
backColor = element->renderStyle() ? element->renderStyle()->backgroundColor() : style()->backgroundColor(); | |
// Draw the background for this list box item | |
if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) { | |
ColorSpace colorSpace = element->renderStyle() ? element->renderStyle()->colorSpace() : style()->colorSpace(); | |
IntRect itemRect = itemBoundingBoxRect(tx, ty, listIndex); | |
itemRect.intersect(controlClipRect(tx, ty)); | |
paintInfo.context->fillRect(itemRect, backColor, colorSpace); | |
} | |
} | |
bool RenderListBox::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty) | |
{ | |
if (!m_vBar) | |
return false; | |
IntRect vertRect(_tx + width() - borderRight() - m_vBar->width(), | |
_ty + borderTop(), | |
m_vBar->width(), | |
height() - borderTop() - borderBottom()); | |
if (vertRect.contains(_x, _y)) { | |
result.setScrollbar(m_vBar.get()); | |
return true; | |
} | |
return false; | |
} | |
int RenderListBox::listIndexAtOffset(int offsetX, int offsetY) | |
{ | |
if (!numItems()) | |
return -1; | |
if (offsetY < borderTop() + paddingTop() || offsetY > height() - paddingBottom() - borderBottom()) | |
return -1; | |
int scrollbarWidth = m_vBar ? m_vBar->width() : 0; | |
if (offsetX < borderLeft() + paddingLeft() || offsetX > width() - borderRight() - paddingRight() - scrollbarWidth) | |
return -1; | |
int newOffset = (offsetY - borderTop() - paddingTop()) / itemHeight() + m_indexOffset; | |
return newOffset < numItems() ? newOffset : -1; | |
} | |
void RenderListBox::panScroll(const IntPoint& panStartMousePosition) | |
{ | |
const int maxSpeed = 20; | |
const int iconRadius = 7; | |
const int speedReducer = 4; | |
// FIXME: This doesn't work correctly with transforms. | |
FloatPoint absOffset = localToAbsolute(); | |
IntPoint currentMousePosition = document()->frame()->eventHandler()->currentMousePosition(); | |
// We need to check if the current mouse position is out of the window. When the mouse is out of the window, the position is incoherent | |
static IntPoint previousMousePosition; | |
if (currentMousePosition.y() < 0) | |
currentMousePosition = previousMousePosition; | |
else | |
previousMousePosition = currentMousePosition; | |
int yDelta = currentMousePosition.y() - panStartMousePosition.y(); | |
// If the point is too far from the center we limit the speed | |
yDelta = max(min(yDelta, maxSpeed), -maxSpeed); | |
if (abs(yDelta) < iconRadius) // at the center we let the space for the icon | |
return; | |
if (yDelta > 0) | |
//offsetY = view()->viewHeight(); | |
absOffset.move(0, listHeight()); | |
else if (yDelta < 0) | |
yDelta--; | |
// Let's attenuate the speed | |
yDelta /= speedReducer; | |
IntPoint scrollPoint(0, 0); | |
scrollPoint.setY(absOffset.y() + yDelta); | |
int newOffset = scrollToward(scrollPoint); | |
if (newOffset < 0) | |
return; | |
m_inAutoscroll = true; | |
SelectElement* select = toSelectElement(static_cast<Element*>(node())); | |
select->updateListBoxSelection(!select->multiple()); | |
m_inAutoscroll = false; | |
} | |
int RenderListBox::scrollToward(const IntPoint& destination) | |
{ | |
// FIXME: This doesn't work correctly with transforms. | |
FloatPoint absPos = localToAbsolute(); | |
int offsetX = destination.x() - absPos.x(); | |
int offsetY = destination.y() - absPos.y(); | |
int rows = numVisibleItems(); | |
int offset = m_indexOffset; | |
if (offsetY < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1)) | |
return offset - 1; | |
if (offsetY > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows)) | |
return offset + rows - 1; | |
return listIndexAtOffset(offsetX, offsetY); | |
} | |
void RenderListBox::autoscroll() | |
{ | |
IntPoint pos = document()->frame()->view()->windowToContents(document()->frame()->eventHandler()->currentMousePosition()); | |
int endIndex = scrollToward(pos); | |
if (endIndex >= 0) { | |
SelectElement* select = toSelectElement(static_cast<Element*>(node())); | |
m_inAutoscroll = true; | |
if (!select->multiple()) | |
select->setActiveSelectionAnchorIndex(endIndex); | |
select->setActiveSelectionEndIndex(endIndex); | |
select->updateListBoxSelection(!select->multiple()); | |
m_inAutoscroll = false; | |
} | |
} | |
void RenderListBox::stopAutoscroll() | |
{ | |
toSelectElement(static_cast<Element*>(node()))->listBoxOnChange(); | |
} | |
bool RenderListBox::scrollToRevealElementAtListIndex(int index) | |
{ | |
if (index < 0 || index >= numItems() || listIndexIsVisible(index)) | |
return false; | |
int newOffset; | |
if (index < m_indexOffset) | |
newOffset = index; | |
else | |
newOffset = index - numVisibleItems() + 1; | |
m_indexOffset = newOffset; | |
if (m_vBar) | |
m_vBar->setValue(m_indexOffset); | |
return true; | |
} | |
bool RenderListBox::listIndexIsVisible(int index) | |
{ | |
return index >= m_indexOffset && index < m_indexOffset + numVisibleItems(); | |
} | |
bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node**) | |
{ | |
return m_vBar && m_vBar->scroll(direction, granularity, multiplier); | |
} | |
void RenderListBox::valueChanged(unsigned listIndex) | |
{ | |
Element* element = static_cast<Element*>(node()); | |
SelectElement* select = toSelectElement(element); | |
select->setSelectedIndex(select->listToOptionIndex(listIndex)); | |
element->dispatchFormControlChangeEvent(); | |
} | |
void RenderListBox::valueChanged(Scrollbar*) | |
{ | |
int newOffset = m_vBar->value(); | |
if (newOffset != m_indexOffset) { | |
m_indexOffset = newOffset; | |
repaint(); | |
node()->dispatchEvent(Event::create(eventNames().scrollEvent, false, false)); | |
} | |
} | |
int RenderListBox::itemHeight() const | |
{ | |
return style()->font().height() + rowSpacing; | |
} | |
int RenderListBox::verticalScrollbarWidth() const | |
{ | |
return m_vBar ? m_vBar->width() : 0; | |
} | |
// FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's | |
// how the control currently paints. | |
int RenderListBox::scrollWidth() const | |
{ | |
// There is no horizontal scrolling allowed. | |
return clientWidth(); | |
} | |
int RenderListBox::scrollHeight() const | |
{ | |
return max(clientHeight(), listHeight()); | |
} | |
int RenderListBox::scrollLeft() const | |
{ | |
return 0; | |
} | |
void RenderListBox::setScrollLeft(int) | |
{ | |
} | |
int RenderListBox::scrollTop() const | |
{ | |
return m_indexOffset * itemHeight(); | |
} | |
void RenderListBox::setScrollTop(int newTop) | |
{ | |
// Determine an index and scroll to it. | |
int index = newTop / itemHeight(); | |
if (index < 0 || index >= numItems() || index == m_indexOffset) | |
return; | |
m_indexOffset = index; | |
if (m_vBar) | |
m_vBar->setValue(index); | |
} | |
bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) | |
{ | |
if (!RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction)) | |
return false; | |
const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems(); | |
int size = numItems(); | |
tx += this->x(); | |
ty += this->y(); | |
for (int i = 0; i < size; ++i) { | |
if (itemBoundingBoxRect(tx, ty, i).contains(x, y)) { | |
if (Element* node = listItems[i]) { | |
result.setInnerNode(node); | |
if (!result.innerNonSharedNode()) | |
result.setInnerNonSharedNode(node); | |
result.setLocalPoint(IntPoint(x - tx, y - ty)); | |
break; | |
} | |
} | |
} | |
return true; | |
} | |
IntRect RenderListBox::controlClipRect(int tx, int ty) const | |
{ | |
IntRect clipRect = contentBoxRect(); | |
clipRect.move(tx, ty); | |
return clipRect; | |
} | |
bool RenderListBox::isActive() const | |
{ | |
Page* page = document()->frame()->page(); | |
return page && page->focusController()->isActive(); | |
} | |
void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) | |
{ | |
IntRect scrollRect = rect; | |
scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop()); | |
repaintRectangle(scrollRect); | |
} | |
IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const | |
{ | |
RenderView* view = this->view(); | |
if (!view) | |
return scrollbarRect; | |
IntRect rect = scrollbarRect; | |
int scrollbarLeft = width() - borderRight() - scrollbar->width(); | |
int scrollbarTop = borderTop(); | |
rect.move(scrollbarLeft, scrollbarTop); | |
return view->frameView()->convertFromRenderer(this, rect); | |
} | |
IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const | |
{ | |
RenderView* view = this->view(); | |
if (!view) | |
return parentRect; | |
IntRect rect = view->frameView()->convertToRenderer(this, parentRect); | |
int scrollbarLeft = width() - borderRight() - scrollbar->width(); | |
int scrollbarTop = borderTop(); | |
rect.move(-scrollbarLeft, -scrollbarTop); | |
return rect; | |
} | |
IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const | |
{ | |
RenderView* view = this->view(); | |
if (!view) | |
return scrollbarPoint; | |
IntPoint point = scrollbarPoint; | |
int scrollbarLeft = width() - borderRight() - scrollbar->width(); | |
int scrollbarTop = borderTop(); | |
point.move(scrollbarLeft, scrollbarTop); | |
return view->frameView()->convertFromRenderer(this, point); | |
} | |
IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const | |
{ | |
RenderView* view = this->view(); | |
if (!view) | |
return parentPoint; | |
IntPoint point = view->frameView()->convertToRenderer(this, parentPoint); | |
int scrollbarLeft = width() - borderRight() - scrollbar->width(); | |
int scrollbarTop = borderTop(); | |
point.move(-scrollbarLeft, -scrollbarTop); | |
return point; | |
} | |
PassRefPtr<Scrollbar> RenderListBox::createScrollbar() | |
{ | |
RefPtr<Scrollbar> widget; | |
bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR); | |
if (hasCustomScrollbarStyle) | |
widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this); | |
else | |
widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart)); | |
document()->view()->addChild(widget.get()); | |
return widget.release(); | |
} | |
void RenderListBox::destroyScrollbar() | |
{ | |
if (!m_vBar) | |
return; | |
m_vBar->removeFromParent(); | |
m_vBar->setClient(0); | |
m_vBar = 0; | |
} | |
void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar) | |
{ | |
if (hasScrollbar == (m_vBar != 0)) | |
return; | |
if (hasScrollbar) | |
m_vBar = createScrollbar(); | |
else | |
destroyScrollbar(); | |
if (m_vBar) | |
m_vBar->styleChanged(); | |
#if ENABLE(DASHBOARD_SUPPORT) | |
// Force an update since we know the scrollbars have changed things. | |
if (document()->hasDashboardRegions()) | |
document()->setDashboardRegionsDirty(true); | |
#endif | |
} | |
} // namespace WebCore |