Changeset 17498 in webkit
- Timestamp:
- Oct 31, 2006 2:10:51 PM (17 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r17490 r17498 1 2006-10-31 Adele Peterson <adele@apple.com> 2 3 Reviewed by Adam. 4 5 Tests for http://bugs.webkit.org/show_bug.cgi?id=11127 NativeListBox: arrow and drag selection should pivot around one list item 6 and http://bugs.webkit.org/show_bug.cgi?id=11173 REGRESSION (NativeListBox): Shift-clicking items in list box doesn't expand the current selection 7 and http://bugs.webkit.org/show_bug.cgi?id=11417 REGRESSION: onchange does not fire for list-style select elements 8 9 * fast/forms/listbox-onchange-expected.txt: Added. 10 * fast/forms/listbox-onchange.html: Added. 11 * fast/forms/listbox-selection-expected.txt: Added. 12 * fast/forms/listbox-selection.html: Added. 13 1 14 2006-10-31 Justin Garcia <justin.garcia@apple.com> 2 15 -
trunk/WebCore/ChangeLog
r17496 r17498 1 2006-10-31 Adele Peterson <adele@apple.com> 2 3 Reviewed by Adam. 4 5 - Fix for http://bugs.webkit.org/show_bug.cgi?id=11127 NativeListBox: arrow and drag selection should pivot around one list item 6 and http://bugs.webkit.org/show_bug.cgi?id=11173 REGRESSION (NativeListBox): Shift-clicking items in list box doesn't expand the current selection 7 and http://bugs.webkit.org/show_bug.cgi?id=11417 REGRESSION: onchange does not fire for list-style select elements 8 9 Tests: 10 * LayoutTests/fast/forms/listbox-selection.html 11 * LayoutTests/fast/forms/listbox-onchange.html 12 13 * html/HTMLSelectElement.h: Added m_selectedListIndexBase and m_selectedListIndexExtent to track indices for the active selection in progress. 14 Added 2 vectors to cache selection state. One is kept so that the previous selection state can be restored as the active selection grows and shrinks. 15 And one for onChange, that is updated after onChange is fired. 16 Added m_activeSelectionState to keep track of whether the current drag selection is selecting or deselecting. 17 18 * html/HTMLSelectElement.cpp: 19 (WebCore::HTMLSelectElement::HTMLSelectElement): Initialized new variables. 20 (WebCore::HTMLSelectElement::setSelectedIndex): If needed, initialize m_selectedListIndexBase and m_selectedListIndexExtent. 21 (WebCore::HTMLSelectElement::dispatchBlurEvent): Only fire the onChange event here for menu lists. 22 (WebCore::HTMLSelectElement::listBoxDefaultEventHandler): Updates base and extent variables for mouse and key events. 23 (WebCore::HTMLSelectElement::setBase): Added. Also caches the selection state. 24 (WebCore::HTMLSelectElement::setExtent): Added. 25 (WebCore::HTMLSelectElement::updateListBoxSelection): Added. 26 (WebCore::HTMLSelectElement::listBoxOnChange): Added. 27 28 * rendering/RenderListBox.cpp: 29 (WebCore::RenderListBox::updateFromElement): Only scroll to reveal the first index if both the first and last indices aren't visible. 30 (WebCore::RenderListBox::listIndexAtOffset): Added. Replaces optionAtPoint, which is no longer used. 31 (WebCore::RenderListBox::autoscroll): Now sets the selection using the select's base and extent. 32 (WebCore::RenderListBox::stopAutoscroll): Added. Tells the select element to fire onChange. This is needed because the autoscroll can end from a mouseUp 33 outside of the list box, and the select element won't get a mouseUp event directly. But the frame will stop the autoscroll at that point, and now we can 34 notify the select element from here. 35 (WebCore::RenderListBox::scrollToRevealElementAtListIndex): Checks new listIndexIsVisible method. 36 (WebCore::RenderListBox::listIndexIsVisible): Added. 37 (WebCore::RenderListBox::valueChanged): Removed unnecessary printf. 38 39 * page/Frame.cpp: (WebCore::Frame::stopAutoscrollTimer): Added rendererIsBeingDestroyed argument, so when the renderer calls this during destruction, 40 we don't try to use the pointer to that renderer to call stopAutoscroll. This is done so a renderer that's still alive has a chance to do some cleanup after autoscroll. 41 * rendering/RenderListBox.h: (WebCore::RenderListBox::shouldAutoscroll): Always returns true now, since we're also updating selection from the autoscroll timer. 42 * rendering/RenderObject.h: (WebCore::RenderObject::stopAutoscroll): Added. 43 * rendering/RenderObject.cpp: (WebCore::RenderObject::destroy): Calls stopAutoscrollTimer with rendererIsBeingDestroyed argument. 44 1 45 2006-10-31 Beth Dakin <bdakin@apple.com> 2 46 -
trunk/WebCore/html/HTMLSelectElement.cpp
r17331 r17498 67 67 , m_recalcListItems(false) 68 68 , m_lastOnChangeIndex(0) 69 , m_activeSelectionAnchorIndex(-1) 70 , m_activeSelectionEndIndex(-1) 71 , m_activeSelectionState(false) 69 72 , m_repeatingChar(0) 70 73 , m_lastCharTime(0) … … 162 165 int listIndex = optionToListIndex(optionIndex); 163 166 HTMLOptionElement* element = 0; 167 164 168 if (listIndex >= 0) { 165 169 element = static_cast<HTMLOptionElement*>(items[listIndex]); 166 170 element->setSelected(true); 167 171 } 172 168 173 if (deselect) 169 174 deselectItems(element); 170 if (fireOnChange && m_lastOnChangeIndex != optionIndex) { 175 176 if (listIndex >= 0) { 177 if (m_activeSelectionAnchorIndex < 0 || deselect) 178 setActiveSelectionAnchorIndex(listIndex); 179 if (m_activeSelectionEndIndex < 0 || deselect) 180 setActiveSelectionEndIndex(listIndex); 181 } 182 183 if (usesMenuList() && fireOnChange && m_lastOnChangeIndex != optionIndex) { 171 184 m_lastOnChangeIndex = optionIndex; 172 185 onChange(); … … 523 536 { 524 537 #if !ARROW_KEYS_POP_MENU 525 if ( selectedIndex() != m_lastOnChangeIndex) {538 if (usesMenuList() && selectedIndex() != m_lastOnChangeIndex) { 526 539 m_lastOnChangeIndex = selectedIndex(); 527 540 onChange(); … … 617 630 if (evt->type() == mousedownEvent) { 618 631 MouseEvent* mEvt = static_cast<MouseEvent*>(evt); 619 i f (HTMLOptionElement* element = static_cast<RenderListBox*>(renderer())->optionAtPoint(mEvt->x(), mEvt->y())) {620 bool deselectOtherOptions = true;621 bool shouldSelect= true;632 int listIndex = static_cast<RenderListBox*>(renderer())->listIndexAtOffset(mEvt->offsetX(), mEvt->offsetY()); 633 if (listIndex >= 0) { 634 m_activeSelectionState = true; 622 635 623 636 bool multiSelectKeyPressed = false; … … 627 640 multiSelectKeyPressed = mEvt->ctrlKey(); 628 641 #endif 629 if (multiple() && multiSelectKeyPressed) 630 deselectOtherOptions = false; 631 if (element->selected() && multiSelectKeyPressed) 632 shouldSelect = false; 642 643 bool shiftSelect = multiple() && mEvt->shiftKey(); 644 bool multiSelect = multiple() && multiSelectKeyPressed && !mEvt->shiftKey(); 633 645 634 int optionIndex = element->index(); 635 if (!shouldSelect) { 636 optionIndex = -1; 637 element->m_selected = false; 646 HTMLElement* clickedElement = listItems()[listIndex]; 647 HTMLOptionElement* option = 0; 648 if (clickedElement->hasLocalName(optionTag)) { 649 option = static_cast<HTMLOptionElement*>(clickedElement); 650 651 // Keep track of whether an active selection (like during drag selection), should select or deselect 652 if (option->selected() && multiSelectKeyPressed) 653 m_activeSelectionState = false; 654 655 if (!m_activeSelectionState) 656 option->m_selected = false; 638 657 } 639 setSelectedIndex(optionIndex, deselectOtherOptions); 640 } 641 } else if (evt->type() == keypressEvent) { 658 659 // If we're not in any special multiple selection mode, then deselect all other items, excluding the clicked option. 660 // If no option was clicked, then this will deselect all items in the list. 661 if (!shiftSelect && !multiSelect) 662 deselectItems(option); 663 664 // If the anchor hasn't been set, and we're doing a single selection or a shift selection, then initialize the anchor to the first selected index. 665 if (m_activeSelectionAnchorIndex < 0 && !multiSelect) 666 setActiveSelectionAnchorIndex(selectedIndex()); 667 668 // Set the selection state of the clicked option 669 if (option && !option->disabled()) 670 option->m_selected = true; 671 672 // If there was no selectedIndex() for the previous initialization, or 673 // If we're doing a single selection, or a multiple selection (using cmd or ctrl), then initialize the anchor index to the listIndex that just got clicked. 674 if (listIndex >= 0 && (m_activeSelectionAnchorIndex < 0 || !shiftSelect)) 675 setActiveSelectionAnchorIndex(listIndex); 676 677 setActiveSelectionEndIndex(listIndex); 678 updateListBoxSelection(!multiSelect); 679 renderer()->repaint(); 680 } 681 } else if (evt->type() == mouseupEvent && document()->frame()->autoscrollRenderer() != renderer()) 682 // This makes sure we fire onChange for a single click. For drag selection, onChange will fire when the autoscroll timer stops. 683 listBoxOnChange(); 684 else if (evt->type() == keypressEvent) { 642 685 if (!evt->isKeyboardEvent()) 643 686 return; 644 687 String keyIdentifier = static_cast<KeyboardEvent*>(evt)->keyIdentifier(); 645 688 646 int index = 0;689 int endIndex = 0; 647 690 const Vector<HTMLElement*>& items = listItems(); 648 649 if (keyIdentifier == "Down") { 650 index = nextSelectableListIndex(lastSelectedListIndex()); 691 692 if (m_activeSelectionEndIndex < 0) { 693 // Initialize the end index 694 if (keyIdentifier == "Down") 695 endIndex = nextSelectableListIndex(lastSelectedListIndex()); 696 else if (keyIdentifier == "Up") 697 endIndex = previousSelectableListIndex(optionToListIndex(selectedIndex())); 698 } else { 699 // Set the end index based on the current end index 700 if (keyIdentifier == "Down") 701 endIndex = nextSelectableListIndex(m_activeSelectionEndIndex); 702 else if (keyIdentifier == "Up") 703 endIndex = previousSelectableListIndex(m_activeSelectionEndIndex); 704 } 705 706 if (keyIdentifier == "Down" || keyIdentifier == "Up") { 707 ASSERT(endIndex >= 0 && (unsigned)endIndex < items.size()); 708 setActiveSelectionEndIndex(endIndex); 651 709 652 } else if (keyIdentifier == "Up") { 653 index = previousSelectableListIndex(optionToListIndex(selectedIndex())); 654 } 655 if (keyIdentifier == "Down" || keyIdentifier == "Up") { 656 ASSERT(index >= 0 && (unsigned)index < items.size()); 657 HTMLOptionElement* element = static_cast<HTMLOptionElement*>(items[index]); 710 // If the anchor is unitialized, or if we're going to deselect all other options, then set the anchor index equal to the end index. 711 bool deselectOthers = !multiple() || !static_cast<KeyboardEvent*>(evt)->shiftKey(); 712 if (m_activeSelectionAnchorIndex < 0 || deselectOthers) { 713 m_activeSelectionState = true; 714 if (deselectOthers) 715 deselectItems(); 716 setActiveSelectionAnchorIndex(m_activeSelectionEndIndex); 717 } 718 719 static_cast<RenderListBox*>(renderer())->scrollToRevealElementAtListIndex(endIndex); 720 evt->setDefaultHandled(); 721 updateListBoxSelection(deselectOthers); 722 renderer()->repaint(); 658 723 659 setSelectedIndex(element->index(), !multiple() || !static_cast<KeyboardEvent*>(evt)->shiftKey()); 660 static_cast<RenderListBox*>(renderer())->scrollToRevealElementAtListIndex(index); 661 662 evt->setDefaultHandled(); 663 setChanged(); 664 renderer()->repaint(); 665 } 666 } 724 listBoxOnChange(); 725 } 726 } 727 } 728 729 void HTMLSelectElement::setActiveSelectionAnchorIndex(int index) 730 { 731 m_activeSelectionAnchorIndex = index; 732 733 // Cache the selection state so we can restore the old selection as the new selection pivots around this anchor index 734 const Vector<HTMLElement*>& items = listItems(); 735 m_cachedStateForActiveSelection.clear(); 736 for (unsigned i = 0; i < items.size(); i++) { 737 if (items[i]->hasLocalName(optionTag)) { 738 HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]); 739 m_cachedStateForActiveSelection.append(option->selected()); 740 } else 741 m_cachedStateForActiveSelection.append(false); 742 } 743 } 744 745 void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions) 746 { 747 unsigned start; 748 unsigned end; 749 ASSERT(m_activeSelectionAnchorIndex >= 0); 750 start = min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex); 751 end = max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex); 752 753 const Vector<HTMLElement*>& items = listItems(); 754 for (unsigned i = 0; i < items.size(); i++) { 755 if (items[i]->hasLocalName(optionTag)) { 756 HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]); 757 if (!option->disabled()) { 758 if (i >= start && i <= end) 759 option->m_selected = m_activeSelectionState; 760 else if (deselectOtherOptions) 761 option->m_selected = false; 762 else 763 option->m_selected = m_cachedStateForActiveSelection[i]; 764 } 765 } 766 } 767 if (renderer()->isListBox()) 768 static_cast<RenderListBox*>(renderer())->setSelectionChanged(true); 769 } 770 771 void HTMLSelectElement::listBoxOnChange() 772 { 773 const Vector<HTMLElement*>& items = listItems(); 774 775 // If the cached selection list is empty, or the size has changed, then rebuild the list, fire onChange, and return early. 776 if (m_lastOnChangeSelection.isEmpty() || m_lastOnChangeSelection.size() != items.size()) { 777 m_lastOnChangeSelection.clear(); 778 for (unsigned i = 0; i < items.size(); i++) { 779 if (items[i]->hasLocalName(optionTag)) { 780 HTMLOptionElement* option = static_cast<HTMLOptionElement*>(items[i]); 781 m_lastOnChangeSelection.append(option->selected()); 782 } else 783 m_lastOnChangeSelection.append(false); 784 } 785 onChange(); 786 return; 787 } 788 789 // Update m_lastOnChangeSelection and fire onChange 790 bool fireOnChange = false; 791 for (unsigned i = 0; i < items.size(); i++) { 792 bool selected = false; 793 if (items[i]->hasLocalName(optionTag)) 794 selected = static_cast<HTMLOptionElement*>(items[i])->selected(); 795 if (selected != m_lastOnChangeSelection[i]) 796 fireOnChange = true; 797 m_lastOnChangeSelection[i] = selected; 798 } 799 if (fireOnChange) 800 onChange(); 667 801 } 668 802 -
trunk/WebCore/html/HTMLSelectElement.h
r17331 r17498 62 62 void setSelectedIndex(int index, bool deselect = true, bool fireOnChange = false); 63 63 void notifyOptionSelected(HTMLOptionElement* selectedOption, bool selected); 64 64 int lastSelectedListIndex() const; 65 65 66 virtual bool isEnumeratable() const { return true; } 66 67 … … 126 127 HTMLCollection::CollectionInfo* collectionInfo() { return &m_collectionInfo; } 127 128 129 void setActiveSelectionAnchorIndex(int index); 130 void setActiveSelectionEndIndex(int index) { m_activeSelectionEndIndex = index; } 131 void updateListBoxSelection(bool deselectOtherOptions); 132 void listBoxOnChange(); 133 128 134 private: 129 135 void recalcListItems() const; 130 void deselectItems(HTMLOptionElement* excludeElement );136 void deselectItems(HTMLOptionElement* excludeElement = 0); 131 137 bool usesMenuList() const { return !m_multiple && m_size <= 1; } 132 int lastSelectedListIndex() const;133 138 int nextSelectableListIndex(int startIndex); 134 139 int previousSelectableListIndex(int startIndex); … … 138 143 139 144 mutable Vector<HTMLElement*> m_listItems; 145 Vector<bool> m_cachedStateForActiveSelection; 146 Vector<bool> m_lastOnChangeSelection; 140 147 int m_minwidth; 141 148 int m_size; … … 143 150 mutable bool m_recalcListItems; 144 151 int m_lastOnChangeIndex; 152 153 int m_activeSelectionAnchorIndex; 154 int m_activeSelectionEndIndex; 155 bool m_activeSelectionState; 145 156 146 157 // Instance variables for type-ahead find -
trunk/WebCore/page/Frame.cpp
r17494 r17498 2856 2856 } 2857 2857 2858 void Frame::stopAutoscrollTimer() 2859 { 2858 void Frame::stopAutoscrollTimer(bool rendererIsBeingDestroyed) 2859 { 2860 if (!rendererIsBeingDestroyed && autoscrollRenderer()) 2861 autoscrollRenderer()->stopAutoscroll(); 2860 2862 setAutoscrollRenderer(0); 2861 2863 d->m_autoscrollTimer.stop(); -
trunk/WebCore/page/Frame.h
r17484 r17498 727 727 void setNeedsReapplyStyles(); 728 728 729 void stopAutoscrollTimer( );729 void stopAutoscrollTimer(bool rendererIsBeingDestroyed = false); 730 730 RenderObject* autoscrollRenderer() const; 731 731 -
trunk/WebCore/rendering/RenderListBox.cpp
r17448 r17498 111 111 } 112 112 113 scrollToRevealElementAtListIndex(select->optionToListIndex(select->selectedIndex())); 113 int firstIndex = select->optionToListIndex(select->selectedIndex()); 114 int lastIndex = select->lastSelectedListIndex(); 115 if (firstIndex >= 0 && !listIndexIsVisible(firstIndex) && !listIndexIsVisible(lastIndex)) 116 scrollToRevealElementAtListIndex(firstIndex); 114 117 } 115 118 … … 344 347 } 345 348 346 HTMLOptionElement* RenderListBox::optionAtPoint(int x, int y)349 int RenderListBox::listIndexAtOffset(int offsetX, int offsetY) 347 350 { 348 351 HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); 349 352 const Vector<HTMLElement*>& listItems = select->listItems(); 350 int yOffset = y - absoluteBoundingBoxRect().y(); 351 int newOffset = max(0, yOffset/ (style()->font().height() + optionsSpacingMiddle)) + m_indexOffset;353 354 int newOffset = max(0, offsetY / (style()->font().height() + optionsSpacingMiddle)) + m_indexOffset; 352 355 newOffset = max(0, min((int)listItems.size() - 1, newOffset)); 353 356 int scrollbarWidth = m_vBar ? m_vBar->width() : 0; 354 if (x >= absoluteBoundingBoxRect().x() + borderLeft() + paddingLeft() && x < absoluteBoundingBoxRect().right() - borderRight() - paddingRight() - scrollbarWidth) 355 return static_cast<HTMLOptionElement*>(listItems[newOffset]); 356 return 0; 357 if (offsetX >= borderLeft() + paddingLeft() && offsetX < absoluteBoundingBoxRect().width() - borderRight() - paddingRight() - scrollbarWidth) 358 return newOffset; 359 360 return -1; 357 361 } 358 362 … … 360 364 { 361 365 IntPoint pos = document()->frame()->view()->windowToContents(document()->frame()->view()->currentMousePosition()); 362 IntRect bounds = absoluteBoundingBoxRect(); 363 364 HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); 365 const Vector<HTMLElement*>& items = select->listItems(); 366 HTMLOptionElement* element = 0; 366 367 int rx = 0; 368 int ry = 0; 369 absolutePosition(rx, ry); 370 int offsetX = pos.x() - rx; 371 int offsetY = pos.y() - ry; 372 373 int endIndex = -1; 367 374 int rows = size(); 368 375 int offset = m_indexOffset; 369 if ( pos.y() < bounds.y() && scrollToRevealElementAtListIndex(offset - 1) && items[offset - 1]->hasTagName(optionTag))370 e lement = static_cast<HTMLOptionElement*>(items[offset - 1]);371 else if ( pos.y() > bounds.bottom() && scrollToRevealElementAtListIndex(offset + rows) && items[offset + rows - 1]->hasTagName(optionTag))372 e lement = static_cast<HTMLOptionElement*>(items[offset + rows - 1]);376 if (offsetY < 0 && scrollToRevealElementAtListIndex(offset - 1)) 377 endIndex = offset - 1; 378 else if (offsetY > absoluteBoundingBoxRect().height() && scrollToRevealElementAtListIndex(offset + rows)) 379 endIndex = offset + rows - 1; 373 380 else 374 element = optionAtPoint(pos.x(), pos.y()); 375 376 if (element) { 377 select->setSelectedIndex(element->index(), !select->multiple()); 381 endIndex = listIndexAtOffset(offsetX, offsetY); 382 383 HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); 384 if (endIndex >= 0 && select) { 385 if (!select->multiple()) 386 select->setActiveSelectionAnchorIndex(endIndex); 387 select->setActiveSelectionEndIndex(endIndex); 388 select->updateListBoxSelection(!select->multiple()); 378 389 repaint(); 379 390 } 380 391 } 381 392 393 void RenderListBox::stopAutoscroll() 394 { 395 if ( HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node())) 396 select->listBoxOnChange(); 397 } 398 382 399 bool RenderListBox::scrollToRevealElementAtListIndex(int index) 383 400 { … … 385 402 const Vector<HTMLElement*>& listItems = select->listItems(); 386 403 387 if (index < 0 || index > (int)listItems.size() - 1 || (index >= m_indexOffset && index < m_indexOffset + size()))404 if (index < 0 || index > (int)listItems.size() - 1 || listIndexIsVisible(index)) 388 405 return false; 389 406 … … 403 420 } 404 421 422 bool RenderListBox::listIndexIsVisible(int index) 423 { 424 return index >= m_indexOffset && index < m_indexOffset + size(); 425 } 426 405 427 bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) 406 428 { … … 421 443 if (newOffset != m_indexOffset) { 422 444 m_indexOffset = newOffset; 423 // printf("value changed: new offset index: %d\n", newOffset);424 445 repaint(); 425 446 // Fire the scroll DOM event. -
trunk/WebCore/rendering/RenderListBox.h
r17448 r17498 28 28 namespace WebCore { 29 29 30 class HTMLElement; 30 31 class HTMLSelectElement; 31 32 class HTMLOptionElement; … … 61 62 virtual IntRect windowClipRect() const; 62 63 63 HTMLOptionElement* optionAtPoint(int x, int y);64 int listIndexAtOffset(int x, int y); 64 65 65 66 bool scrollToRevealElementAtListIndex(int index); 66 67 67 virtual bool shouldAutoscroll() const { return numItems() > size(); }68 virtual bool shouldAutoscroll() const { return true; } 68 69 virtual void autoscroll(); 70 virtual void stopAutoscroll(); 69 71 70 72 private: … … 75 77 void paintItemForeground(PaintInfo&, int tx, int ty, int listIndex); 76 78 void paintItemBackground(PaintInfo&, int tx, int ty, int listIndex); 79 bool listIndexIsVisible(int index); 77 80 78 81 bool m_optionsChanged; -
trunk/WebCore/rendering/RenderObject.cpp
r17494 r17498 2466 2466 // If this renderer is being autoscrolled, stop the autoscroll timer 2467 2467 if (document() && document()->frame() && document()->frame()->autoscrollRenderer() == this) 2468 document()->frame()->stopAutoscrollTimer( );2468 document()->frame()->stopAutoscrollTimer(true); 2469 2469 2470 2470 if (m_hasCounterNodeMap) { -
trunk/WebCore/rendering/RenderObject.h
r17494 r17498 609 609 virtual bool shouldAutoscroll() const; 610 610 virtual void autoscroll(); 611 virtual void stopAutoscroll() {}; 611 612 612 613 // The following seven functions are used to implement collapsing margins.
Note: See TracChangeset
for help on using the changeset viewer.