• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RenderListBox(Element * element)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 
~RenderListBox()85 RenderListBox::~RenderListBox()
86 {
87     setHasVerticalScrollbar(false);
88 }
89 
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)90 void RenderListBox::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
91 {
92     RenderBlock::styleDidChange(diff, oldStyle);
93     setReplaced(isInline());
94 }
95 
updateFromElement()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 
selectionChanged()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 
layout()145 void RenderListBox::layout()
146 {
147     RenderBlock::layout();
148     if (m_scrollToRevealSelectionAfterLayout)
149         scrollToRevealSelection();
150 }
151 
scrollToRevealSelection()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 
calcPrefWidths()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 
size() const198 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 
numVisibleItems() const206 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 
numItems() const212 int RenderListBox::numItems() const
213 {
214     return toSelectElement(static_cast<Element*>(node()))->listItems().size();
215 }
216 
listHeight() const217 int RenderListBox::listHeight() const
218 {
219     return itemHeight() * numItems() - rowSpacing;
220 }
221 
calcHeight()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 
baselinePosition(bool,bool) const241 int RenderListBox::baselinePosition(bool, bool) const
242 {
243     return height() + marginTop() + marginBottom() - baselineAdjustment;
244 }
245 
itemBoundingBoxRect(int tx,int ty,int index)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 
paintObject(PaintInfo & paintInfo,int tx,int ty)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 
paintScrollbar(PaintInfo & paintInfo,int tx,int ty)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 
paintItemForeground(PaintInfo & paintInfo,int tx,int ty,int listIndex)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     paintInfo.context->setFillColor(textColor);
325 
326     Font itemFont = style()->font();
327     if (isOptionGroupElement(element)) {
328         FontDescription d = itemFont.fontDescription();
329         d.setWeight(d.bolderWeight());
330         itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing());
331         itemFont.update(document()->styleSelector()->fontSelector());
332     }
333 
334     unsigned length = itemText.length();
335     const UChar* string = itemText.characters();
336     TextRun textRun(string, length, 0, 0, 0, itemStyle->direction() == RTL, itemStyle->unicodeBidi() == Override, false, false);
337 
338     // Draw the item text
339     if (itemStyle->visibility() != HIDDEN)
340         paintInfo.context->drawBidiText(itemFont, textRun, r.location());
341 }
342 
paintItemBackground(PaintInfo & paintInfo,int tx,int ty,int listIndex)343 void RenderListBox::paintItemBackground(PaintInfo& paintInfo, int tx, int ty, int listIndex)
344 {
345     SelectElement* select = toSelectElement(static_cast<Element*>(node()));
346     const Vector<Element*>& listItems = select->listItems();
347     Element* element = listItems[listIndex];
348     OptionElement* optionElement = toOptionElement(element);
349 
350     Color backColor;
351     if (optionElement && optionElement->selected()) {
352         if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node())
353             backColor = theme()->activeListBoxSelectionBackgroundColor();
354         else
355             backColor = theme()->inactiveListBoxSelectionBackgroundColor();
356     } else
357         backColor = element->renderStyle() ? element->renderStyle()->backgroundColor() : style()->backgroundColor();
358 
359     // Draw the background for this list box item
360     if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) {
361         IntRect itemRect = itemBoundingBoxRect(tx, ty, listIndex);
362         itemRect.intersect(controlClipRect(tx, ty));
363         paintInfo.context->fillRect(itemRect, backColor);
364     }
365 }
366 
isPointInOverflowControl(HitTestResult & result,int _x,int _y,int _tx,int _ty)367 bool RenderListBox::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty)
368 {
369     if (!m_vBar)
370         return false;
371 
372     IntRect vertRect(_tx + width() - borderRight() - m_vBar->width(),
373                      _ty + borderTop(),
374                      m_vBar->width(),
375                      height() - borderTop() - borderBottom());
376 
377     if (vertRect.contains(_x, _y)) {
378         result.setScrollbar(m_vBar.get());
379         return true;
380     }
381     return false;
382 }
383 
listIndexAtOffset(int offsetX,int offsetY)384 int RenderListBox::listIndexAtOffset(int offsetX, int offsetY)
385 {
386     if (!numItems())
387         return -1;
388 
389     if (offsetY < borderTop() + paddingTop() || offsetY > height() - paddingBottom() - borderBottom())
390         return -1;
391 
392     int scrollbarWidth = m_vBar ? m_vBar->width() : 0;
393     if (offsetX < borderLeft() + paddingLeft() || offsetX > width() - borderRight() - paddingRight() - scrollbarWidth)
394         return -1;
395 
396     int newOffset = (offsetY - borderTop() - paddingTop()) / itemHeight() + m_indexOffset;
397     return newOffset < numItems() ? newOffset : -1;
398 }
399 
panScroll(const IntPoint & panStartMousePosition)400 void RenderListBox::panScroll(const IntPoint& panStartMousePosition)
401 {
402     const int maxSpeed = 20;
403     const int iconRadius = 7;
404     const int speedReducer = 4;
405 
406     // FIXME: This doesn't work correctly with transforms.
407     FloatPoint absOffset = localToAbsolute();
408 
409     IntPoint currentMousePosition = document()->frame()->eventHandler()->currentMousePosition();
410     // 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
411     static IntPoint previousMousePosition;
412     if (currentMousePosition.y() < 0)
413         currentMousePosition = previousMousePosition;
414     else
415         previousMousePosition = currentMousePosition;
416 
417     int yDelta = currentMousePosition.y() - panStartMousePosition.y();
418 
419     // If the point is too far from the center we limit the speed
420     yDelta = max(min(yDelta, maxSpeed), -maxSpeed);
421 
422     if (abs(yDelta) < iconRadius) // at the center we let the space for the icon
423         return;
424 
425     if (yDelta > 0)
426         //offsetY = view()->viewHeight();
427         absOffset.move(0, listHeight());
428     else if (yDelta < 0)
429         yDelta--;
430 
431     // Let's attenuate the speed
432     yDelta /= speedReducer;
433 
434     IntPoint scrollPoint(0, 0);
435     scrollPoint.setY(absOffset.y() + yDelta);
436     int newOffset = scrollToward(scrollPoint);
437     if (newOffset < 0)
438         return;
439 
440     m_inAutoscroll = true;
441     SelectElement* select = toSelectElement(static_cast<Element*>(node()));
442     select->updateListBoxSelection(!select->multiple());
443     m_inAutoscroll = false;
444 }
445 
scrollToward(const IntPoint & destination)446 int RenderListBox::scrollToward(const IntPoint& destination)
447 {
448     // FIXME: This doesn't work correctly with transforms.
449     FloatPoint absPos = localToAbsolute();
450     int offsetX = destination.x() - absPos.x();
451     int offsetY = destination.y() - absPos.y();
452 
453     int rows = numVisibleItems();
454     int offset = m_indexOffset;
455 
456     if (offsetY < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1))
457         return offset - 1;
458 
459     if (offsetY > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows))
460         return offset + rows - 1;
461 
462     return listIndexAtOffset(offsetX, offsetY);
463 }
464 
autoscroll()465 void RenderListBox::autoscroll()
466 {
467     IntPoint pos = document()->frame()->view()->windowToContents(document()->frame()->eventHandler()->currentMousePosition());
468 
469     int endIndex = scrollToward(pos);
470     if (endIndex >= 0) {
471         SelectElement* select = toSelectElement(static_cast<Element*>(node()));
472         m_inAutoscroll = true;
473 
474         if (!select->multiple())
475             select->setActiveSelectionAnchorIndex(endIndex);
476 
477         select->setActiveSelectionEndIndex(endIndex);
478         select->updateListBoxSelection(!select->multiple());
479         m_inAutoscroll = false;
480     }
481 }
482 
stopAutoscroll()483 void RenderListBox::stopAutoscroll()
484 {
485     toSelectElement(static_cast<Element*>(node()))->listBoxOnChange();
486 }
487 
scrollToRevealElementAtListIndex(int index)488 bool RenderListBox::scrollToRevealElementAtListIndex(int index)
489 {
490     if (index < 0 || index >= numItems() || listIndexIsVisible(index))
491         return false;
492 
493     int newOffset;
494     if (index < m_indexOffset)
495         newOffset = index;
496     else
497         newOffset = index - numVisibleItems() + 1;
498 
499     m_indexOffset = newOffset;
500     if (m_vBar)
501         m_vBar->setValue(m_indexOffset);
502 
503     return true;
504 }
505 
listIndexIsVisible(int index)506 bool RenderListBox::listIndexIsVisible(int index)
507 {
508     return index >= m_indexOffset && index < m_indexOffset + numVisibleItems();
509 }
510 
scroll(ScrollDirection direction,ScrollGranularity granularity,float multiplier)511 bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
512 {
513     return m_vBar && m_vBar->scroll(direction, granularity, multiplier);
514 }
515 
valueChanged(unsigned listIndex)516 void RenderListBox::valueChanged(unsigned listIndex)
517 {
518     Element* element = static_cast<Element*>(node());
519     SelectElement* select = toSelectElement(element);
520     select->setSelectedIndex(select->listToOptionIndex(listIndex));
521     element->dispatchFormControlChangeEvent();
522 }
523 
valueChanged(Scrollbar *)524 void RenderListBox::valueChanged(Scrollbar*)
525 {
526     int newOffset = m_vBar->value();
527     if (newOffset != m_indexOffset) {
528         m_indexOffset = newOffset;
529         repaint();
530         // Fire the scroll DOM event.
531         node()->dispatchEvent(eventNames().scrollEvent, false, false);
532     }
533 }
534 
itemHeight() const535 int RenderListBox::itemHeight() const
536 {
537     return style()->font().height() + rowSpacing;
538 }
539 
verticalScrollbarWidth() const540 int RenderListBox::verticalScrollbarWidth() const
541 {
542     return m_vBar ? m_vBar->width() : 0;
543 }
544 
545 // FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's
546 // how the control currently paints.
scrollWidth() const547 int RenderListBox::scrollWidth() const
548 {
549     // There is no horizontal scrolling allowed.
550     return clientWidth();
551 }
552 
scrollHeight() const553 int RenderListBox::scrollHeight() const
554 {
555     return max(clientHeight(), listHeight());
556 }
557 
scrollLeft() const558 int RenderListBox::scrollLeft() const
559 {
560     return 0;
561 }
562 
setScrollLeft(int)563 void RenderListBox::setScrollLeft(int)
564 {
565 }
566 
scrollTop() const567 int RenderListBox::scrollTop() const
568 {
569     return m_indexOffset * itemHeight();
570 }
571 
setScrollTop(int newTop)572 void RenderListBox::setScrollTop(int newTop)
573 {
574     // Determine an index and scroll to it.
575     int index = newTop / itemHeight();
576     if (index < 0 || index >= numItems() || index == m_indexOffset)
577         return;
578     m_indexOffset = index;
579     if (m_vBar)
580         m_vBar->setValue(index);
581 }
582 
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,HitTestAction hitTestAction)583 bool RenderListBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction)
584 {
585     if (!RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction))
586         return false;
587     const Vector<Element*>& listItems = toSelectElement(static_cast<Element*>(node()))->listItems();
588     int size = numItems();
589     tx += this->x();
590     ty += this->y();
591     for (int i = 0; i < size; ++i) {
592         if (itemBoundingBoxRect(tx, ty, i).contains(x, y)) {
593             if (Element* node = listItems[i]) {
594                 result.setInnerNode(node);
595                 if (!result.innerNonSharedNode())
596                     result.setInnerNonSharedNode(node);
597                 result.setLocalPoint(IntPoint(x - tx, y - ty));
598                 break;
599             }
600         }
601     }
602 
603     return true;
604 }
605 
controlClipRect(int tx,int ty) const606 IntRect RenderListBox::controlClipRect(int tx, int ty) const
607 {
608     IntRect clipRect = contentBoxRect();
609     clipRect.move(tx, ty);
610     return clipRect;
611 }
612 
isActive() const613 bool RenderListBox::isActive() const
614 {
615     Page* page = document()->frame()->page();
616     return page && page->focusController()->isActive();
617 }
618 
invalidateScrollbarRect(Scrollbar * scrollbar,const IntRect & rect)619 void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect)
620 {
621     IntRect scrollRect = rect;
622     scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop());
623     repaintRectangle(scrollRect);
624 }
625 
convertFromScrollbarToContainingView(const Scrollbar * scrollbar,const IntRect & scrollbarRect) const626 IntRect RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
627 {
628     RenderView* view = this->view();
629     if (!view)
630         return scrollbarRect;
631 
632     IntRect rect = scrollbarRect;
633 
634     int scrollbarLeft = width() - borderRight() - scrollbar->width();
635     int scrollbarTop = borderTop();
636     rect.move(scrollbarLeft, scrollbarTop);
637 
638     return view->frameView()->convertFromRenderer(this, rect);
639 }
640 
convertFromContainingViewToScrollbar(const Scrollbar * scrollbar,const IntRect & parentRect) const641 IntRect RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
642 {
643     RenderView* view = this->view();
644     if (!view)
645         return parentRect;
646 
647     IntRect rect = view->frameView()->convertToRenderer(this, parentRect);
648 
649     int scrollbarLeft = width() - borderRight() - scrollbar->width();
650     int scrollbarTop = borderTop();
651     rect.move(-scrollbarLeft, -scrollbarTop);
652     return rect;
653 }
654 
convertFromScrollbarToContainingView(const Scrollbar * scrollbar,const IntPoint & scrollbarPoint) const655 IntPoint RenderListBox::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
656 {
657     RenderView* view = this->view();
658     if (!view)
659         return scrollbarPoint;
660 
661     IntPoint point = scrollbarPoint;
662 
663     int scrollbarLeft = width() - borderRight() - scrollbar->width();
664     int scrollbarTop = borderTop();
665     point.move(scrollbarLeft, scrollbarTop);
666 
667     return view->frameView()->convertFromRenderer(this, point);
668 }
669 
convertFromContainingViewToScrollbar(const Scrollbar * scrollbar,const IntPoint & parentPoint) const670 IntPoint RenderListBox::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
671 {
672     RenderView* view = this->view();
673     if (!view)
674         return parentPoint;
675 
676     IntPoint point = view->frameView()->convertToRenderer(this, parentPoint);
677 
678     int scrollbarLeft = width() - borderRight() - scrollbar->width();
679     int scrollbarTop = borderTop();
680     point.move(-scrollbarLeft, -scrollbarTop);
681     return point;
682 }
683 
createScrollbar()684 PassRefPtr<Scrollbar> RenderListBox::createScrollbar()
685 {
686     RefPtr<Scrollbar> widget;
687     bool hasCustomScrollbarStyle = style()->hasPseudoStyle(SCROLLBAR);
688     if (hasCustomScrollbarStyle)
689         widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this);
690     else
691         widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, theme()->scrollbarControlSizeForPart(ListboxPart));
692     document()->view()->addChild(widget.get());
693     return widget.release();
694 }
695 
destroyScrollbar()696 void RenderListBox::destroyScrollbar()
697 {
698     if (!m_vBar)
699         return;
700 
701     m_vBar->removeFromParent();
702     m_vBar->setClient(0);
703     m_vBar = 0;
704 }
705 
setHasVerticalScrollbar(bool hasScrollbar)706 void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar)
707 {
708     if (hasScrollbar == (m_vBar != 0))
709         return;
710 
711     if (hasScrollbar)
712         m_vBar = createScrollbar();
713     else
714         destroyScrollbar();
715 
716     if (m_vBar)
717         m_vBar->styleChanged();
718 
719 #if ENABLE(DASHBOARD_SUPPORT)
720     // Force an update since we know the scrollbars have changed things.
721     if (document()->hasDashboardRegions())
722         document()->setDashboardRegionsDirty(true);
723 #endif
724 }
725 
726 } // namespace WebCore
727