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