RenderListBox.cpp revision 81bc750723a18f21cd17d1b173cd2a4dda9cea6e
1/* 2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved. 3 * 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 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. 17 * 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. 28 */ 29 30#include "config.h" 31#include "RenderListBox.h" 32 33#include "AXObjectCache.h" 34#include "CSSStyleSelector.h" 35#include "Document.h" 36#include "EventHandler.h" 37#include "EventQueue.h" 38#include "FocusController.h" 39#include "Frame.h" 40#include "FrameView.h" 41#include "GraphicsContext.h" 42#include "HTMLNames.h" 43#include "HitTestResult.h" 44#include "NodeRenderStyle.h" 45#include "OptionGroupElement.h" 46#include "OptionElement.h" 47#include "Page.h" 48#include "PaintInfo.h" 49#include "RenderLayer.h" 50#include "RenderScrollbar.h" 51#include "RenderTheme.h" 52#include "RenderView.h" 53#include "Scrollbar.h" 54#include "ScrollbarTheme.h" 55#include "SelectElement.h" 56#include "SelectionController.h" 57#include "TextRun.h" 58#include <math.h> 59 60using namespace std; 61 62namespace WebCore { 63 64using namespace HTMLNames; 65 66const int rowSpacing = 1; 67 68const int optionsSpacingHorizontal = 2; 69 70const int minSize = 4; 71const int maxDefaultSize = 10; 72 73// FIXME: This hardcoded baselineAdjustment is what we used to do for the old 74// widget, but I'm not sure this is right for the new control. 75const int baselineAdjustment = 7; 76 77RenderListBox::RenderListBox(Element* element) 78 : RenderBlock(element) 79 , m_optionsChanged(true) 80 , m_scrollToRevealSelectionAfterLayout(false) 81 , m_inAutoscroll(false) 82 , m_optionsWidth(0) 83 , m_indexOffset(0) 84{ 85} 86 87RenderListBox::~RenderListBox() 88{ 89 setHasVerticalScrollbar(false); 90} 91 92void RenderListBox::updateFromElement() 93{ 94 if (m_optionsChanged) { 95 const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems(); 96 int size = numItems(); 97 98 float width = 0; 99 for (int i = 0; i < size; ++i) { 100 Element* element = listItems[i]; 101 String text; 102 Font itemFont = style()->font(); 103 if (OptionElement* optionElement = toOptionElement(element)) 104 text = optionElement->textIndentedToRespectGroupLabel(); 105 else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) { 106 text = optionGroupElement->groupLabelText(); 107 FontDescription d = itemFont.fontDescription(); 108 d.setWeight(d.bolderWeight()); 109 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); 110 itemFont.update(document()->styleSelector()->fontSelector()); 111 } 112 113 if (!text.isEmpty()) { 114 float textWidth = itemFont.width(TextRun(text.impl(), false, 0, 0, TextRun::AllowTrailingExpansion, false, false)); 115 width = max(width, textWidth); 116 } 117 } 118 m_optionsWidth = static_cast<int>(ceilf(width)); 119 m_optionsChanged = false; 120 121 setHasVerticalScrollbar(true); 122 123 setNeedsLayoutAndPrefWidthsRecalc(); 124 } 125} 126 127void RenderListBox::selectionChanged() 128{ 129 repaint(); 130 if (!m_inAutoscroll) { 131 if (m_optionsChanged || needsLayout()) 132 m_scrollToRevealSelectionAfterLayout = true; 133 else 134 scrollToRevealSelection(); 135 } 136 137 if (AXObjectCache::accessibilityEnabled()) 138 document()->axObjectCache()->selectedChildrenChanged(this); 139} 140 141void RenderListBox::layout() 142{ 143 RenderBlock::layout(); 144 if (m_scrollToRevealSelectionAfterLayout) { 145 view()->disableLayoutState(); 146 scrollToRevealSelection(); 147 view()->enableLayoutState(); 148 } 149} 150 151void RenderListBox::scrollToRevealSelection() 152{ 153 SelectElement* select = toSelectElement(static_cast<Element*>(node())); 154 155 m_scrollToRevealSelectionAfterLayout = false; 156 157 int firstIndex = select->activeSelectionStartListIndex(); 158 if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex())) 159 scrollToRevealElementAtListIndex(firstIndex); 160} 161 162void RenderListBox::computePreferredLogicalWidths() 163{ 164 ASSERT(!m_optionsChanged); 165 166 m_minPreferredLogicalWidth = 0; 167 m_maxPreferredLogicalWidth = 0; 168 169 if (style()->width().isFixed() && style()->width().value() > 0) 170 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value()); 171 else { 172 m_maxPreferredLogicalWidth = m_optionsWidth + 2 * optionsSpacingHorizontal; 173 if (m_vBar) 174 m_maxPreferredLogicalWidth += m_vBar->width(); 175 } 176 177 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { 178 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); 179 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value())); 180 } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) 181 m_minPreferredLogicalWidth = 0; 182 else 183 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth; 184 185 if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { 186 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); 187 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value())); 188 } 189 190 int toAdd = borderAndPaddingWidth(); 191 m_minPreferredLogicalWidth += toAdd; 192 m_maxPreferredLogicalWidth += toAdd; 193 194 setPreferredLogicalWidthsDirty(false); 195} 196 197int RenderListBox::size() const 198{ 199 int specifiedSize = toSelectElement(static_cast<Element*>(node()))->size(); 200 if (specifiedSize > 1) 201 return max(minSize, specifiedSize); 202 return min(max(minSize, numItems()), maxDefaultSize); 203} 204 205int RenderListBox::numVisibleItems() const 206{ 207 // Only count fully visible rows. But don't return 0 even if only part of a row shows. 208 return max(1, (contentHeight() + rowSpacing) / itemHeight()); 209} 210 211int RenderListBox::numItems() const 212{ 213 return toSelectElement(static_cast<Element*>(node()))->listItems().size(); 214} 215 216int RenderListBox::listHeight() const 217{ 218 return itemHeight() * numItems() - rowSpacing; 219} 220 221void RenderListBox::computeLogicalHeight() 222{ 223 int toAdd = borderAndPaddingHeight(); 224 225 int itemHeight = RenderListBox::itemHeight(); 226 setHeight(itemHeight * size() - rowSpacing + toAdd); 227 228 RenderBlock::computeLogicalHeight(); 229 230 if (m_vBar) { 231 bool enabled = numVisibleItems() < numItems(); 232 m_vBar->setEnabled(enabled); 233 m_vBar->setSteps(1, min(1, numVisibleItems() - 1), itemHeight); 234 m_vBar->setProportion(numVisibleItems(), numItems()); 235 if (!enabled) 236 m_indexOffset = 0; 237 } 238} 239 240int RenderListBox::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const 241{ 242 return RenderBox::baselinePosition(baselineType, firstLine, lineDirection, linePositionMode) - baselineAdjustment; 243} 244 245IntRect RenderListBox::itemBoundingBoxRect(int tx, int ty, int index) 246{ 247 return IntRect(tx + borderLeft() + paddingLeft(), 248 ty + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset), 249 contentWidth(), itemHeight()); 250} 251 252void RenderListBox::paintObject(PaintInfo& paintInfo, int tx, int ty) 253{ 254 if (style()->visibility() != VISIBLE) 255 return; 256 257 int listItemsSize = numItems(); 258 259 if (paintInfo.phase == PaintPhaseForeground) { 260 int index = m_indexOffset; 261 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { 262 paintItemForeground(paintInfo, tx, ty, index); 263 index++; 264 } 265 } 266 267 // Paint the children. 268 RenderBlock::paintObject(paintInfo, tx, ty); 269 270 if (paintInfo.phase == PaintPhaseBlockBackground) 271 paintScrollbar(paintInfo, tx, ty); 272 else if (paintInfo.phase == PaintPhaseChildBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { 273 int index = m_indexOffset; 274 while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { 275 paintItemBackground(paintInfo, tx, ty, index); 276 index++; 277 } 278 } 279} 280 281void RenderListBox::addFocusRingRects(Vector<IntRect>& rects, int tx, int ty) 282{ 283 if (!isSpatialNavigationEnabled(frame())) 284 return RenderBlock::addFocusRingRects(rects, tx, ty); 285 286 SelectElement* select = toSelectElement(static_cast<Element*>(node())); 287 288 // Focus the last selected item. 289 int selectedItem = select->activeSelectionEndListIndex(); 290 if (selectedItem >= 0) { 291 rects.append(itemBoundingBoxRect(tx, ty, selectedItem)); 292 return; 293 } 294 295 // No selected items, find the first non-disabled item. 296 int size = numItems(); 297 const Vector<Element*>& listItems = select->listItems(); 298 for (int i = 0; i < size; ++i) { 299 OptionElement* optionElement = toOptionElement(listItems[i]); 300 if (optionElement && !optionElement->disabled()) { 301 rects.append(itemBoundingBoxRect(tx, ty, i)); 302 return; 303 } 304 } 305} 306 307void RenderListBox::paintScrollbar(PaintInfo& paintInfo, int tx, int ty) 308{ 309 if (m_vBar) { 310 IntRect scrollRect(tx + width() - borderRight() - m_vBar->width(), 311 ty + borderTop(), 312 m_vBar->width(), 313 height() - (borderTop() + borderBottom())); 314 m_vBar->setFrameRect(scrollRect); 315 m_vBar->paint(paintInfo.context, paintInfo.rect); 316 } 317} 318 319void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, int listIndex) 320{ 321 SelectElement* select = toSelectElement(static_cast<Element*>(node())); 322 const Vector<Element*>& listItems = select->listItems(); 323 Element* element = listItems[listIndex]; 324 OptionElement* optionElement = toOptionElement(element); 325 326 String itemText; 327 if (optionElement) 328 itemText = optionElement->textIndentedToRespectGroupLabel(); 329 else if (OptionGroupElement* optionGroupElement = toOptionGroupElement(element)) 330 itemText = optionGroupElement->groupLabelText(); 331 332 // Determine where the item text should be placed 333 IntRect r = itemBoundingBoxRect(tx, ty, listIndex); 334 r.move(optionsSpacingHorizontal, style()->fontMetrics().ascent()); 335 336 RenderStyle* itemStyle = element->renderStyle(); 337 if (!itemStyle) 338 itemStyle = style(); 339 340 Color textColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor); 341 if (optionElement && optionElement->selected()) { 342 if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) 343 textColor = theme()->activeListBoxSelectionForegroundColor(); 344 // Honor the foreground color for disabled items 345 else if (!element->disabled()) 346 textColor = theme()->inactiveListBoxSelectionForegroundColor(); 347 } 348 349 ColorSpace colorSpace = itemStyle->colorSpace(); 350 paintInfo.context->setFillColor(textColor, colorSpace); 351 352 Font itemFont = style()->font(); 353 if (isOptionGroupElement(element)) { 354 FontDescription d = itemFont.fontDescription(); 355 d.setWeight(d.bolderWeight()); 356 itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); 357 itemFont.update(document()->styleSelector()->fontSelector()); 358 } 359 360 unsigned length = itemText.length(); 361 const UChar* string = itemText.characters(); 362 TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, !itemStyle->isLeftToRightDirection(), itemStyle->unicodeBidi() == Override); 363 364 // Draw the item text 365 if (itemStyle->visibility() != HIDDEN) 366 paintInfo.context->drawBidiText(itemFont, textRun, r.location()); 367} 368 369void RenderListBox::paintItemBackground(PaintInfo& paintInfo, int tx, int ty, int listIndex) 370{ 371 SelectElement* select = toSelectElement(static_cast<Element*>(node())); 372 const Vector<Element*>& listItems = select->listItems(); 373 Element* element = listItems[listIndex]; 374 OptionElement* optionElement = toOptionElement(element); 375 376 Color backColor; 377 if (optionElement && optionElement->selected()) { 378 if (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) 379 backColor = theme()->activeListBoxSelectionBackgroundColor(); 380 else 381 backColor = theme()->inactiveListBoxSelectionBackgroundColor(); 382 } else 383 backColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor) : style()->visitedDependentColor(CSSPropertyBackgroundColor); 384 385 // Draw the background for this list box item 386 if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) { 387 ColorSpace colorSpace = element->renderStyle() ? element->renderStyle()->colorSpace() : style()->colorSpace(); 388 IntRect itemRect = itemBoundingBoxRect(tx, ty, listIndex); 389 itemRect.intersect(controlClipRect(tx, ty)); 390 paintInfo.context->fillRect(itemRect, backColor, colorSpace); 391 } 392} 393 394bool RenderListBox::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty) 395{ 396 if (!m_vBar) 397 return false; 398 399 IntRect vertRect(_tx + width() - borderRight() - m_vBar->width(), 400 _ty + borderTop(), 401 m_vBar->width(), 402 height() - borderTop() - borderBottom()); 403 404 if (vertRect.contains(_x, _y)) { 405 result.setScrollbar(m_vBar.get()); 406 return true; 407 } 408 return false; 409} 410 411int RenderListBox::listIndexAtOffset(int offsetX, int offsetY) 412{ 413 if (!numItems()) 414 return -1; 415 416 if (offsetY < borderTop() + paddingTop() || offsetY > height() - paddingBottom() - borderBottom()) 417 return -1; 418 419 int scrollbarWidth = m_vBar ? m_vBar->width() : 0; 420 if (offsetX < borderLeft() + paddingLeft() || offsetX > width() - borderRight() - paddingRight() - scrollbarWidth) 421 return -1; 422 423 int newOffset = (offsetY - borderTop() - paddingTop()) / itemHeight() + m_indexOffset; 424 return newOffset < numItems() ? newOffset : -1; 425} 426 427void RenderListBox::panScroll(const IntPoint& panStartMousePosition) 428{ 429 const int maxSpeed = 20; 430 const int iconRadius = 7; 431 const int speedReducer = 4; 432 433 // FIXME: This doesn't work correctly with transforms. 434 FloatPoint absOffset = localToAbsolute(); 435 436 IntPoint currentMousePosition = frame()->eventHandler()->currentMousePosition(); 437 // 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 438 static IntPoint previousMousePosition; 439 if (currentMousePosition.y() < 0) 440 currentMousePosition = previousMousePosition; 441 else 442 previousMousePosition = currentMousePosition; 443 444 int yDelta = currentMousePosition.y() - panStartMousePosition.y(); 445 446 // If the point is too far from the center we limit the speed 447 yDelta = max(min(yDelta, maxSpeed), -maxSpeed); 448 449 if (abs(yDelta) < iconRadius) // at the center we let the space for the icon 450 return; 451 452 if (yDelta > 0) 453 //offsetY = view()->viewHeight(); 454 absOffset.move(0, listHeight()); 455 else if (yDelta < 0) 456 yDelta--; 457 458 // Let's attenuate the speed 459 yDelta /= speedReducer; 460 461 IntPoint scrollPoint(0, 0); 462 scrollPoint.setY(absOffset.y() + yDelta); 463 int newOffset = scrollToward(scrollPoint); 464 if (newOffset < 0) 465 return; 466 467 m_inAutoscroll = true; 468 SelectElement* select = toSelectElement(static_cast<Element*>(node())); 469 select->updateListBoxSelection(!select->multiple()); 470 m_inAutoscroll = false; 471} 472 473int RenderListBox::scrollToward(const IntPoint& destination) 474{ 475 // FIXME: This doesn't work correctly with transforms. 476 FloatPoint absPos = localToAbsolute(); 477 int offsetX = destination.x() - absPos.x(); 478 int offsetY = destination.y() - absPos.y(); 479 480 int rows = numVisibleItems(); 481 int offset = m_indexOffset; 482 483 if (offsetY < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1)) 484 return offset - 1; 485 486 if (offsetY > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows)) 487 return offset + rows - 1; 488 489 return listIndexAtOffset(offsetX, offsetY); 490} 491 492void RenderListBox::autoscroll() 493{ 494 IntPoint pos = frame()->view()->windowToContents(frame()->eventHandler()->currentMousePosition()); 495 496 int endIndex = scrollToward(pos); 497 if (endIndex >= 0) { 498 SelectElement* select = toSelectElement(static_cast<Element*>(node())); 499 m_inAutoscroll = true; 500 501 if (!select->multiple()) 502 select->setActiveSelectionAnchorIndex(endIndex); 503 504 select->setActiveSelectionEndIndex(endIndex); 505 select->updateListBoxSelection(!select->multiple()); 506 m_inAutoscroll = false; 507 } 508} 509 510void RenderListBox::stopAutoscroll() 511{ 512 toSelectElement(static_cast<Element*>(node()))->listBoxOnChange(); 513} 514 515bool RenderListBox::scrollToRevealElementAtListIndex(int index) 516{ 517 if (index < 0 || index >= numItems() || listIndexIsVisible(index)) 518 return false; 519 520 int newOffset; 521 if (index < m_indexOffset) 522 newOffset = index; 523 else 524 newOffset = index - numVisibleItems() + 1; 525 526 ScrollableArea::scrollToYOffsetWithoutAnimation(newOffset); 527 528 return true; 529} 530 531bool RenderListBox::listIndexIsVisible(int index) 532{ 533 return index >= m_indexOffset && index < m_indexOffset + numVisibleItems(); 534} 535 536bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier, Node**) 537{ 538 return ScrollableArea::scroll(direction, granularity, multiplier); 539} 540 541bool RenderListBox::logicalScroll(ScrollLogicalDirection direction, ScrollGranularity granularity, float multiplier, Node**) 542{ 543 return ScrollableArea::scroll(logicalToPhysical(direction, style()->isHorizontalWritingMode(), style()->isFlippedBlocksWritingMode()), granularity, multiplier); 544} 545 546void RenderListBox::valueChanged(unsigned listIndex) 547{ 548 Element* element = static_cast<Element*>(node()); 549 SelectElement* select = toSelectElement(element); 550 select->setSelectedIndex(select->listToOptionIndex(listIndex)); 551 element->dispatchFormControlChangeEvent(); 552} 553 554int RenderListBox::scrollSize(ScrollbarOrientation orientation) const 555{ 556 return ((orientation == VerticalScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0; 557} 558 559int RenderListBox::scrollPosition(Scrollbar*) const 560{ 561 return m_indexOffset; 562} 563 564void RenderListBox::setScrollOffset(const IntPoint& offset) 565{ 566 scrollTo(offset.y()); 567} 568 569void RenderListBox::scrollTo(int newOffset) 570{ 571 if (newOffset == m_indexOffset) 572 return; 573 574 m_indexOffset = newOffset; 575 repaint(); 576 node()->document()->eventQueue()->enqueueScrollEvent(node(), EventQueue::ScrollEventElementTarget); 577} 578 579int RenderListBox::itemHeight() const 580{ 581 return style()->fontMetrics().height() + rowSpacing; 582} 583 584int RenderListBox::verticalScrollbarWidth() const 585{ 586 return m_vBar && !m_vBar->isOverlayScrollbar() ? m_vBar->width() : 0; 587} 588 589// FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's 590// how the control currently paints. 591int RenderListBox::scrollWidth() const 592{ 593 // There is no horizontal scrolling allowed. 594 return clientWidth(); 595} 596 597int RenderListBox::scrollHeight() const 598{ 599 return max(clientHeight(), listHeight()); 600} 601 602int RenderListBox::scrollLeft() const 603{ 604 return 0; 605} 606 607void RenderListBox::setScrollLeft(int) 608{ 609} 610 611int RenderListBox::scrollTop() const 612{ 613 return m_indexOffset * itemHeight(); 614} 615 616void RenderListBox::setScrollTop(int newTop) 617{ 618 // Determine an index and scroll to it. 619 int index = newTop / itemHeight(); 620 if (index < 0 || index >= numItems() || index == m_indexOffset) 621 return; 622 623 ScrollableArea::scrollToYOffsetWithoutAnimation(index); 624} 625 626bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) 627{ 628 if (!RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction)) 629 return false; 630 const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems(); 631 int size = numItems(); 632 tx += this->x(); 633 ty += this->y(); 634 for (int i = 0; i < size; ++i) { 635 if (itemBoundingBoxRect(tx, ty, i).contains(x, y)) { 636 if (Element* node = listItems[i]) { 637 result.setInnerNode(node); 638 if (!result.innerNonSharedNode()) 639 result.setInnerNonSharedNode(node); 640 result.setLocalPoint(IntPoint(x - tx, y - ty)); 641 break; 642 } 643 } 644 } 645 646 return true; 647} 648 649IntRect RenderListBox::controlClipRect(int tx, int ty) const 650{ 651 IntRect clipRect = contentBoxRect(); 652 clipRect.move(tx, ty); 653 return clipRect; 654} 655 656bool RenderListBox::isActive() const 657{ 658 Page* page = frame()->page(); 659 return page && page->focusController()->isActive(); 660} 661 662void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) 663{ 664 IntRect scrollRect = rect; 665 scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop()); 666 repaintRectangle(scrollRect); 667} 668 669IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const 670{ 671 RenderView* view = this->view(); 672 if (!view) 673 return scrollbarRect; 674 675 IntRect rect = scrollbarRect; 676 677 int scrollbarLeft = width() - borderRight() - scrollbar->width(); 678 int scrollbarTop = borderTop(); 679 rect.move(scrollbarLeft, scrollbarTop); 680 681 return view->frameView()->convertFromRenderer(this, rect); 682} 683 684IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const 685{ 686 RenderView* view = this->view(); 687 if (!view) 688 return parentRect; 689 690 IntRect rect = view->frameView()->convertToRenderer(this, parentRect); 691 692 int scrollbarLeft = width() - borderRight() - scrollbar->width(); 693 int scrollbarTop = borderTop(); 694 rect.move(-scrollbarLeft, -scrollbarTop); 695 return rect; 696} 697 698IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const 699{ 700 RenderView* view = this->view(); 701 if (!view) 702 return scrollbarPoint; 703 704 IntPoint point = scrollbarPoint; 705 706 int scrollbarLeft = width() - borderRight() - scrollbar->width(); 707 int scrollbarTop = borderTop(); 708 point.move(scrollbarLeft, scrollbarTop); 709 710 return view->frameView()->convertFromRenderer(this, point); 711} 712 713IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const 714{ 715 RenderView* view = this->view(); 716 if (!view) 717 return parentPoint; 718 719 IntPoint point = view->frameView()->convertToRenderer(this, parentPoint); 720 721 int scrollbarLeft = width() - borderRight() - scrollbar->width(); 722 int scrollbarTop = borderTop(); 723 point.move(-scrollbarLeft, -scrollbarTop); 724 return point; 725} 726 727IntSize RenderListBox::contentsSize() const 728{ 729 return IntSize(scrollWidth(), scrollHeight()); 730} 731 732int RenderListBox::visibleHeight() const 733{ 734 return height(); 735} 736 737int RenderListBox::visibleWidth() const 738{ 739 return width(); 740} 741 742IntPoint RenderListBox::currentMousePosition() const 743{ 744 RenderView* view = this->view(); 745 if (!view) 746 return IntPoint(); 747 return view->frameView()->currentMousePosition(); 748} 749 750PassRefPtr<Scrollbar> RenderListBox::createScrollbar() 751{ 752 RefPtr<Scrollbar> widget; 753 bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR); 754 if (hasCustomScrollbarStyle) 755 widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this); 756 else { 757 widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart)); 758 didAddVerticalScrollbar(widget.get()); 759 } 760 document()->view()->addChild(widget.get()); 761 return widget.release(); 762} 763 764void RenderListBox::destroyScrollbar() 765{ 766 if (!m_vBar) 767 return; 768 769 if (!m_vBar->isCustomScrollbar()) 770 ScrollableArea::willRemoveVerticalScrollbar(m_vBar.get()); 771 m_vBar->removeFromParent(); 772 m_vBar->disconnectFromScrollableArea(); 773 m_vBar = 0; 774} 775 776void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar) 777{ 778 if (hasScrollbar == (m_vBar != 0)) 779 return; 780 781 if (hasScrollbar) 782 m_vBar = createScrollbar(); 783 else 784 destroyScrollbar(); 785 786 if (m_vBar) 787 m_vBar->styleChanged(); 788 789#if ENABLE(DASHBOARD_SUPPORT) 790 // Force an update since we know the scrollbars have changed things. 791 if (document()->hasDashboardRegions()) 792 document()->setDashboardRegionsDirty(true); 793#endif 794} 795 796} // namespace WebCore 797