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