2010-09-08 Peter Kasting <pkasting@google.com>
[WebKit-https.git] / WebCore / rendering / RenderListBox.cpp
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 = borderAndPaddingWidth();
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 = borderAndPaddingHeight();
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()->visitedDependentColor(CSSPropertyColor) : style()->visitedDependentColor(CSSPropertyColor);
316     if (optionElement && optionElement->selected()) {
317         if (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 (frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
354             backColor = theme()->activeListBoxSelectionBackgroundColor();
355         else
356             backColor = theme()->inactiveListBoxSelectionBackgroundColor();
357     } else
358         backColor = element->renderStyle() ? element->renderStyle()->visitedDependentColor(CSSPropertyBackgroundColor) : style()->visitedDependentColor(CSSPropertyBackgroundColor);
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 = 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 = frame()->view()->windowToContents(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, Scrollbar::NotFromScrollAnimator);
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 int RenderListBox::scrollSize(ScrollbarOrientation orientation) const
527 {
528     return ((orientation == VerticalScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0;
529 }
530
531 void RenderListBox::setScrollOffsetFromAnimation(const IntPoint& offset)
532 {
533     if (m_vBar)
534         m_vBar->setValue(offset.y(), Scrollbar::FromScrollAnimator);
535 }
536
537 void RenderListBox::valueChanged(Scrollbar*)
538 {
539     int newOffset = m_vBar->value();
540     if (newOffset != m_indexOffset) {
541         m_indexOffset = newOffset;
542         repaint();
543         node()->dispatchEvent(Event::create(eventNames().scrollEvent, false, false));
544     }
545 }
546
547 int RenderListBox::itemHeight() const
548 {
549     return style()->font().height() + rowSpacing;
550 }
551
552 int RenderListBox::verticalScrollbarWidth() const
553 {
554     return m_vBar ? m_vBar->width() : 0;
555 }
556
557 // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's
558 // how the control currently paints.
559 int RenderListBox::scrollWidth() const
560 {
561     // There is no horizontal scrolling allowed.
562     return clientWidth();
563 }
564
565 int RenderListBox::scrollHeight() const
566 {
567     return max(clientHeight(), listHeight());
568 }
569
570 int RenderListBox::scrollLeft() const
571 {
572     return 0;
573 }
574
575 void RenderListBox::setScrollLeft(int)
576 {
577 }
578
579 int RenderListBox::scrollTop() const
580 {
581     return m_indexOffset * itemHeight();
582 }
583
584 void RenderListBox::setScrollTop(int newTop)
585 {
586     // Determine an index and scroll to it.    
587     int index = newTop / itemHeight();
588     if (index < 0 || index >= numItems() || index == m_indexOffset)
589         return;
590     m_indexOffset = index;
591     if (m_vBar)
592         m_vBar->setValue(index, Scrollbar::NotFromScrollAnimator);
593 }
594
595 bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
596 {
597     if (!RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction))
598         return false;
599     const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems();
600     int size = numItems();
601     tx += this->x();
602     ty += this->y();
603     for (int i = 0; i < size; ++i) {
604         if (itemBoundingBoxRect(tx, ty, i).contains(x, y)) {
605             if (Element* node = listItems[i]) {
606                 result.setInnerNode(node);
607                 if (!result.innerNonSharedNode())
608                     result.setInnerNonSharedNode(node);
609                 result.setLocalPoint(IntPoint(x - tx, y - ty));
610                 break;
611             }
612         }
613     }
614
615     return true;
616 }
617
618 IntRect RenderListBox::controlClipRect(int tx, int ty) const
619 {
620     IntRect clipRect = contentBoxRect();
621     clipRect.move(tx, ty);
622     return clipRect;
623 }
624
625 bool RenderListBox::isActive() const
626 {
627     Page* page = frame()->page();
628     return page && page->focusController()->isActive();
629 }
630
631 void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
632 {
633     IntRect scrollRect = rect;
634     scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop());
635     repaintRectangle(scrollRect);
636 }
637
638 IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
639 {
640     RenderView* view = this->view();
641     if (!view)
642         return scrollbarRect;
643
644     IntRect rect = scrollbarRect;
645
646     int scrollbarLeft = width() - borderRight() - scrollbar->width();
647     int scrollbarTop = borderTop();
648     rect.move(scrollbarLeft, scrollbarTop);
649
650     return view->frameView()->convertFromRenderer(this, rect);
651 }
652
653 IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
654 {
655     RenderView* view = this->view();
656     if (!view)
657         return parentRect;
658
659     IntRect rect = view->frameView()->convertToRenderer(this, parentRect);
660
661     int scrollbarLeft = width() - borderRight() - scrollbar->width();
662     int scrollbarTop = borderTop();
663     rect.move(-scrollbarLeft, -scrollbarTop);
664     return rect;
665 }
666
667 IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
668 {
669     RenderView* view = this->view();
670     if (!view)
671         return scrollbarPoint;
672
673     IntPoint point = scrollbarPoint;
674
675     int scrollbarLeft = width() - borderRight() - scrollbar->width();
676     int scrollbarTop = borderTop();
677     point.move(scrollbarLeft, scrollbarTop);
678
679     return view->frameView()->convertFromRenderer(this, point);
680 }
681
682 IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
683 {
684     RenderView* view = this->view();
685     if (!view)
686         return parentPoint;
687
688     IntPoint point = view->frameView()->convertToRenderer(this, parentPoint);
689
690     int scrollbarLeft = width() - borderRight() - scrollbar->width();
691     int scrollbarTop = borderTop();
692     point.move(-scrollbarLeft, -scrollbarTop);
693     return point;
694 }
695
696 PassRefPtr<Scrollbar> RenderListBox::createScrollbar()
697 {
698     RefPtr<Scrollbar> widget;
699     bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
700     if (hasCustomScrollbarStyle)
701         widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this);
702     else
703         widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart));
704     document()->view()->addChild(widget.get());        
705     return widget.release();
706 }
707
708 void RenderListBox::destroyScrollbar()
709 {
710     if (!m_vBar)
711         return;
712     
713     m_vBar->removeFromParent();
714     m_vBar->setClient(0);
715     m_vBar = 0;
716 }
717
718 void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar)
719 {
720     if (hasScrollbar == (m_vBar != 0))
721         return;
722
723     if (hasScrollbar)
724         m_vBar = createScrollbar();
725     else
726         destroyScrollbar();
727
728     if (m_vBar)
729         m_vBar->styleChanged();
730
731 #if ENABLE(DASHBOARD_SUPPORT)
732     // Force an update since we know the scrollbars have changed things.
733     if (document()->hasDashboardRegions())
734         document()->setDashboardRegionsDirty(true);
735 #endif
736 }
737
738 } // namespace WebCore