Make onChange, reflow system in comboboxes/lists better (bug 112241). r=rods, sr=kin

This commit is contained in:
jkeiser%netscape.com 2002-01-25 19:08:03 +00:00
parent a7723c632d
commit a555e7ec64
13 changed files with 447 additions and 421 deletions

View File

@ -57,7 +57,7 @@
#include "nsIDOMHTMLCollection.h"
#include "nsIJSNativeInitializer.h"
#include "nsISelectElement.h"
#include "nsIComboboxControlFrame.h"
#include "nsISelectControlFrame.h"
// Notify/query select frame for selected state
#include "nsIFormControlFrame.h"
@ -386,12 +386,12 @@ nsHTMLOptionElement::SetLabel(const nsAReadableString& aValue)
nsIFormControlFrame* fcFrame = GetSelectFrame();
if (fcFrame) {
nsIComboboxControlFrame* selectFrame = nsnull;
nsISelectControlFrame* selectFrame = nsnull;
CallQueryInterface(fcFrame, &selectFrame);
if (selectFrame) {
selectFrame->UpdateSelection(PR_FALSE, PR_TRUE, 0);
selectFrame->OnOptionTextChanged(this);
}
}
}
@ -610,12 +610,12 @@ nsHTMLOptionElement::SetText(const nsAReadableString& aText)
nsIFormControlFrame* fcFrame = GetSelectFrame();
if (fcFrame) {
nsIComboboxControlFrame* selectFrame = nsnull;
nsISelectControlFrame* selectFrame = nsnull;
CallQueryInterface(fcFrame, &selectFrame);
if (selectFrame) {
selectFrame->UpdateSelection(PR_FALSE, PR_TRUE, 0);
selectFrame->OnOptionTextChanged(this);
}
}
}

View File

@ -291,7 +291,6 @@ nsComboboxControlFrame::nsComboboxControlFrame()
mDisplayFrame = nsnull;
mButtonFrame = nsnull;
mDropdownFrame = nsnull;
mSelectedIndex = -1;
mCacheSize.width = kSizeNotSet;
mCacheSize.height = kSizeNotSet;
@ -412,17 +411,15 @@ nsComboboxControlFrame::Init(nsIPresContext* aPresContext,
void
nsComboboxControlFrame::InitTextStr()
{
nsAutoString textToDisplay;
PRInt32 selectedIndex;
mListControlFrame->GetSelectedIndex(&selectedIndex);
// Update the selected text string
if (selectedIndex == -1) {
mListControlFrame->GetOptionText(0, mTextStr);
} else {
mListControlFrame->GetOptionText(selectedIndex, mTextStr);
if (selectedIndex != -1) {
mListControlFrame->GetOptionText(selectedIndex, textToDisplay);
}
// Update the display by setting the value attribute
mDisplayContent->SetText(mTextStr.get(), mTextStr.Length(), PR_FALSE);
mDisplayedIndex = selectedIndex;
ActuallyDisplayText(textToDisplay, PR_FALSE);
}
@ -1848,22 +1845,6 @@ nsComboboxControlFrame::GetDropDown(nsIFrame** aDropDownFrame)
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::ListWasSelected(nsIPresContext* aPresContext, PRBool aForceUpdate, PRBool aSendEvent) // Added "aForceUpdate" for Bug 42661
{
if (aPresContext == nsnull) {
aPresContext = mPresContext;
}
ShowList(aPresContext, PR_FALSE);
mListControlFrame->CaptureMouseEvents(aPresContext, PR_FALSE);
PRInt32 indx;
mListControlFrame->GetSelectedIndex(&indx);
UpdateSelection(aSendEvent, aForceUpdate, indx); // Added "aForceUpdate" for Bug 42661
return NS_OK;
}
// Toggle dropdown list.
NS_IMETHODIMP
@ -1875,29 +1856,6 @@ nsComboboxControlFrame::ToggleList(nsIPresContext* aPresContext)
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate, PRInt32 aNewIndex)
{
if (mListControlFrame) {
// Check to see if the selection changed
if (mSelectedIndex != aNewIndex || aForceUpdate) {
mListControlFrame->GetOptionText(aNewIndex, mTextStr);
SelectionChanged();
// Fix for Bug 42661 (remove comment later)
#ifdef DO_REFLOW_DEBUG
char * str = ToNewCString(mTextStr);
REFLOW_DEBUG_MSG2("UpdateSelection %s\n", str);
delete [] str;
#endif
mSelectedIndex = aNewIndex;
mListControlFrame->UpdateSelection();
}
}
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::AbsolutelyPositionDropDown()
{
@ -1926,8 +1884,30 @@ nsComboboxControlFrame::GetAbsoluteRect(nsRect* aRect)
///////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsComboboxControlFrame::SelectionChanged()
nsComboboxControlFrame::RedisplaySelectedText()
{
PRInt32 selectedIndex;
mListControlFrame->GetSelectedIndex(&selectedIndex);
return RedisplayText(selectedIndex);
}
nsresult
nsComboboxControlFrame::RedisplayText(PRInt32 aIndex)
{
// Get the text to display
nsAutoString textToDisplay;
if (aIndex != -1) {
mListControlFrame->GetOptionText(aIndex, textToDisplay);
}
mDisplayedIndex = aIndex;
#ifdef DO_REFLOW_DEBUG
char * str = ToNewCString(textToDisplay);
REFLOW_DEBUG_MSG2("RedisplayText %s\n", str);
delete [] str;
#endif
// Send reflow command because the new text maybe larger
nsresult rv = NS_OK;
if (mDisplayContent) {
@ -1941,16 +1921,13 @@ nsComboboxControlFrame::SelectionChanged()
if (NS_FAILED(result) || value.Length() == 0) {
shouldSetValue = PR_TRUE;
} else {
shouldSetValue = value != mTextStr;
REFLOW_DEBUG_MSG3("**** CBX::SelectionChanged Old[%s] New[%s]\n", NS_LossyConvertUCS2toASCII(value).get(), NS_LossyConvertUCS2toASCII(mTextStr).get());
shouldSetValue = value != textToDisplay;
REFLOW_DEBUG_MSG3("**** CBX::RedisplayText Old[%s] New[%s]\n",
NS_LossyConvertUCS2toASCII(value).get(),
NS_LossyConvertUCS2toASCII(textToDisplay).get());
}
if (shouldSetValue) {
if (mTextStr.Length() == 0) {
nsAutoString space(NS_LITERAL_STRING(" "));
rv = mDisplayContent->SetText(space.get(), space.Length(), PR_TRUE);
} else {
rv = mDisplayContent->SetText(mTextStr.get(), mTextStr.Length(), PR_TRUE);
}
rv = ActuallyDisplayText(textToDisplay, PR_TRUE);
nsFrameState state;
//mTextFrame->GetFrameState(&state);
//state |= NS_FRAME_IS_DIRTY;
@ -1970,11 +1947,26 @@ nsComboboxControlFrame::SelectionChanged()
return rv;
}
nsresult
nsComboboxControlFrame::ActuallyDisplayText(nsAString& aText, PRBool aNotify)
{
nsresult rv = NS_OK;
if (aText.IsEmpty()) {
nsAutoString space(PRUnichar(' '));
rv = mDisplayContent->SetText(space.get(), space.Length(), aNotify);
} else {
const nsAFlatString& flat = PromiseFlatString(aText);
rv = mDisplayContent->SetText(flat.get(), flat.Length(), aNotify);
}
return rv;
}
NS_IMETHODIMP
nsComboboxControlFrame::GetIndexOfDisplayArea(PRInt32* aSelectedIndex)
{
NS_ENSURE_ARG_POINTER(aSelectedIndex);
*aSelectedIndex = mSelectedIndex;
*aSelectedIndex = mDisplayedIndex;
return NS_OK;
}
@ -2473,6 +2465,15 @@ nsComboboxControlFrame::Rollup()
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::RollupFromList(nsIPresContext* aPresContext)
{
ShowList(aPresContext, PR_FALSE);
mListControlFrame->CaptureMouseEvents(aPresContext, PR_FALSE);
return NS_OK;
}
NS_METHOD
nsComboboxControlFrame::Paint(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@ -2579,13 +2580,36 @@ nsComboboxControlFrame::OnOptionSelected(nsIPresContext* aPresContext,
PRInt32 aIndex,
PRBool aSelected)
{
if (aSelected && !mDroppedDown) {
mListControlFrame->GetOptionText(aIndex, mTextStr);
SelectionChanged();
if (aSelected) {
if (!mDroppedDown) {
RedisplayText(aIndex);
} else {
nsCOMPtr<nsISelectControlFrame> selectFrame
= do_QueryInterface(mListControlFrame);
if (selectFrame) {
selectFrame->OnOptionSelected(aPresContext, aIndex, aSelected);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::OnOptionTextChanged(nsIDOMHTMLOptionElement* option)
{
RedisplaySelectedText();
if (mDroppedDown) {
nsCOMPtr<nsISelectControlFrame> selectFrame
= do_QueryInterface(mListControlFrame);
if (selectFrame) {
selectFrame->OnOptionTextChanged(option);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::OnContentReset()
{

View File

@ -169,10 +169,6 @@ public:
nscoord aCharWidth) const;
virtual nsresult RequiresWidget(PRBool &aRequiresWidget);
NS_IMETHOD SelectionChanged();// Called when the selection has changed.
// If the the same item in the list is selected
// it is NOT called.
// nsIFormMouseListener
virtual void MouseClicked(nsIPresContext* aPresContext);
@ -181,11 +177,11 @@ public:
NS_IMETHOD ShowDropDown(PRBool aDoDropDown);
NS_IMETHOD GetDropDown(nsIFrame** aDropDownFrame);
NS_IMETHOD SetDropDown(nsIFrame* aDropDownFrame);
NS_IMETHOD ListWasSelected(nsIPresContext* aPresContext, PRBool aForceUpdate, PRBool aSendEvent);
NS_IMETHOD UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate, PRInt32 aNewIndex);
NS_IMETHOD RollupFromList(nsIPresContext* aPresContext);
NS_IMETHOD AbsolutelyPositionDropDown();
NS_IMETHOD GetAbsoluteRect(nsRect* aRect);
NS_IMETHOD GetIndexOfDisplayArea(PRInt32* aSelectedIndex);
NS_IMETHOD RedisplaySelectedText();
// nsISelectControlFrame
NS_IMETHOD AddOption(nsIPresContext* aPresContext, PRInt32 index);
@ -195,6 +191,7 @@ public:
NS_IMETHOD OnOptionSelected(nsIPresContext* aPresContext,
PRInt32 aIndex,
PRBool aSelected);
NS_IMETHOD OnOptionTextChanged(nsIDOMHTMLOptionElement* option);
NS_IMETHOD GetDummyFrame(nsIFrame** aFrame);
NS_IMETHOD SetDummyFrame(nsIFrame* aFrame);
@ -223,6 +220,7 @@ public:
NS_IMETHOD RestoreState(nsIPresContext* aPresContext, nsIPresState* aState);
protected:
NS_IMETHOD CreateDisplayFrame(nsIPresContext* aPresContext);
#ifdef DO_NEW_REFLOW
@ -253,6 +251,8 @@ protected:
void ShowList(nsIPresContext* aPresContext, PRBool aShowList);
void SetChildFrameSize(nsIFrame* aFrame, nscoord aWidth, nscoord aHeight);
void InitTextStr();
nsresult RedisplayText(PRInt32 aIndex);
nsresult ActuallyDisplayText(nsAString& aText, PRBool aNotify);
nsresult GetPrimaryComboFrame(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame** aFrame);
NS_IMETHOD ToggleList(nsIPresContext* aPresContext);
@ -271,8 +271,6 @@ protected:
nsFrameList mPopupFrames; // additional named child list
nsIPresContext* mPresContext; // XXX: Remove the need to cache the pres context.
nsFormFrame* mFormFrame; // Parent Form Frame
nsString mTextStr; // Current Combo box selection
PRInt32 mSelectedIndex; // current selected index
nsCOMPtr<nsITextContent> mDisplayContent; // Anonymous content used to display the current selection
PRPackedBool mDroppedDown; // Current state of the dropdown list, PR_TRUE is dropped down
nsIFrame* mDisplayFrame; // frame to display selection
@ -295,6 +293,8 @@ protected:
PRPackedBool mGoodToGo;
PRInt32 mDisplayedIndex;
// make someone to listen to the button. If its programmatically pressed by someone like Accessibility
// then open or close the combo box.
nsCOMPtr<nsIDOMMouseListener> mButtonListener;

View File

@ -89,26 +89,15 @@ public:
NS_IMETHOD SetDropDown(nsIFrame* aDropDownFrame) = 0;
/**
* Notifies the Combobox the List was selected
* Tells the combobox to roll up
*
*/
NS_IMETHOD ListWasSelected(nsIPresContext* aPresContext, PRBool aForceUpdate, PRBool aSendEvent) = 0;
NS_IMETHOD RollupFromList(nsIPresContext* aPresContext) = 0;
/**
* Asks the Combobox to update the display frame
* aDoDispatchEvent - indicates whether an event should be dispatched to the DOM
* aForceUpdate - indicates whether the indexx and the text value should both be
* whether the index has changed or not.
*
* Redisplay the selected text (will do nothing if text has not changed)
*/
NS_IMETHOD UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate, PRInt32 aNewIndex) = 0;
/**
* Asks the Combobox to update the display frame when the selection has
* changed
*
*/
NS_IMETHOD SelectionChanged() = 0;
NS_IMETHOD RedisplaySelectedText() = 0;
/**
*

View File

@ -46,6 +46,8 @@
{ 0x96, 0xea, 0x0, 0x60, 0xb0, 0xfb, 0x99, 0x56 } }
// {162A2AE3-5A79-11d3-96EA-0060B0FB9956}
class nsIDOMHTMLOptionElement;
/**
* nsISelectControlFrame is the interface for combo boxes and listboxes
*/
@ -83,6 +85,13 @@ public:
PRInt32 aIndex,
PRBool aSelected) = 0;
/**
* Notify the frame when an option's text changes
* (We don't pass in the index because it would be expensive for
* the option to figure that out)
*/
NS_IMETHOD OnOptionTextChanged(nsIDOMHTMLOptionElement* option) = 0;
/**
* For the content model to tell if there's a dummy frame or not
*/

View File

@ -1234,37 +1234,41 @@ nsListControlFrame::GetIndexFromContent(nsIContent *aContent)
}
//---------------------------------------------------------
void
PRBool
nsListControlFrame::ExtendedSelection(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aClearAll)
{
SetOptionsSelectedFromFrame(aStartIndex, aEndIndex, PR_TRUE, aClearAll);
return SetOptionsSelectedFromFrame(aStartIndex, aEndIndex,
PR_TRUE, aClearAll);
}
//---------------------------------------------------------
void
PRBool
nsListControlFrame::SingleSelection(PRInt32 aSelectedIndex, PRBool aDoToggle)
{
PRBool wasChanged = PR_FALSE;
// Get Current selection
if (aDoToggle) {
ToggleOptionSelectedFromFrame(aSelectedIndex);
wasChanged = ToggleOptionSelectedFromFrame(aSelectedIndex);
} else {
SetOptionsSelectedFromFrame(aSelectedIndex, aSelectedIndex,
wasChanged = SetOptionsSelectedFromFrame(aSelectedIndex, aSelectedIndex,
PR_TRUE, PR_TRUE);
}
ScrollToIndex(aSelectedIndex);
mStartSelectionIndex = aSelectedIndex;
mEndSelectionIndex = aSelectedIndex;
mChangesNotNotified = PR_TRUE;
return wasChanged;
}
//---------------------------------------------------------
void
PRBool
nsListControlFrame::PerformSelection(PRInt32 aSelectedIndex,
PRBool aIsShift,
PRBool aIsControl)
{
PRBool wasChanged = PR_FALSE;
PRBool isMultiple;
GetMultiple(&isMultiple);
@ -1286,7 +1290,7 @@ nsListControlFrame::PerformSelection(PRInt32 aSelectedIndex,
endIndex = mStartSelectionIndex;
}
ExtendedSelection(startIndex, endIndex, PR_TRUE);
wasChanged = ExtendedSelection(startIndex, endIndex, PR_TRUE);
ScrollToIndex(aSelectedIndex);
if (mStartSelectionIndex == kNothingSelected) {
@ -1295,14 +1299,13 @@ nsListControlFrame::PerformSelection(PRInt32 aSelectedIndex,
} else {
mEndSelectionIndex = aSelectedIndex;
}
mChangesNotNotified = PR_TRUE;
} else if (aIsControl) {
SingleSelection(aSelectedIndex, PR_TRUE);
wasChanged = SingleSelection(aSelectedIndex, PR_TRUE);
} else {
SingleSelection(aSelectedIndex, PR_FALSE);
wasChanged = SingleSelection(aSelectedIndex, PR_FALSE);
}
} else {
SingleSelection(aSelectedIndex, PR_FALSE);
wasChanged = SingleSelection(aSelectedIndex, PR_FALSE);
}
#ifdef DEBUG_rodsX
@ -1311,10 +1314,12 @@ nsListControlFrame::PerformSelection(PRInt32 aSelectedIndex,
printf("mLastStartIndex: %d\n", mLastStartIndex);
printf("mLastEndIndex: %d\n", mLastEndIndex);
#endif
return wasChanged;
}
//---------------------------------------------------------
void
PRBool
nsListControlFrame::HandleListSelection(nsIDOMEvent* aEvent,
PRInt32 aSelectedIndex)
{
@ -1327,7 +1332,7 @@ nsListControlFrame::HandleListSelection(nsIDOMEvent* aEvent,
mouseEvent->GetCtrlKey(&isControl);
#endif
mouseEvent->GetShiftKey(&isShift);
PerformSelection(aSelectedIndex, isShift, isControl);
return PerformSelection(aSelectedIndex, isShift, isControl);
}
//---------------------------------------------------------
@ -1518,7 +1523,6 @@ nsListControlFrame::Init(nsIPresContext* aPresContext,
mStartSelectionIndex = kNothingSelected;
mEndSelectionIndex = kNothingSelected;
mChangesNotNotified = PR_FALSE;
return result;
}
@ -1711,6 +1715,12 @@ nsListControlFrame::OnOptionSelected(nsIPresContext* aPresContext,
return NS_OK;
}
NS_IMETHODIMP
nsListControlFrame::OnOptionTextChanged(nsIDOMHTMLOptionElement* option)
{
return NS_OK;
}
//---------------------------------------------------------
PRIntn
@ -1777,13 +1787,8 @@ nsListControlFrame::ResetList(nsIPresContext* aPresContext, nsVoidArray * aInxLi
mStartSelectionIndex = kNothingSelected;
mEndSelectionIndex = kNothingSelected;
mChangesNotNotified = PR_FALSE;
if (mComboboxFrame != nsnull) {
// don't dispatch event
mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, indexToSelect);
}
// Combobox will redisplay itself with the OnOptionSelected event
}
//---------------------------------------------------------
@ -2030,7 +2035,7 @@ nsListControlFrame::RemoveOption(nsIPresContext* aPresContext, PRInt32 aIndex)
// Set the option selected in the DOM. This method is named
// as it is because it indicates that the frame is the source
// of this event rather than the receiver.
nsresult
PRBool
nsListControlFrame::SetOptionsSelectedFromFrame(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aValue,
@ -2046,25 +2051,22 @@ nsListControlFrame::SetOptionsSelectedFromFrame(PRInt32 aStartIndex,
PR_TRUE,
&wasChanged);
NS_ASSERTION(NS_SUCCEEDED(rv), "SetSelected failed");
if (NS_SUCCEEDED(rv) && !mChangesNotNotified && wasChanged) {
mChangesNotNotified = PR_TRUE;
}
return rv;
return wasChanged;
}
nsresult
PRBool
nsListControlFrame::ToggleOptionSelectedFromFrame(PRInt32 aIndex)
{
nsCOMPtr<nsIDOMHTMLCollection> options(getter_AddRefs(GetOptions(mContent)));
NS_ASSERTION(options, "No options");
if (!options) {
return NS_ERROR_FAILURE;
return PR_FALSE;
}
nsCOMPtr<nsIDOMHTMLOptionElement> option(
getter_AddRefs(GetOption(*options, aIndex)));
NS_ASSERTION(option, "No option");
if (!option) {
return NS_ERROR_FAILURE;
return PR_FALSE;
}
PRBool value = PR_FALSE;
@ -2082,11 +2084,8 @@ nsListControlFrame::ToggleOptionSelectedFromFrame(PRInt32 aIndex)
&wasChanged);
NS_ASSERTION(NS_SUCCEEDED(rv), "SetSelected failed");
if (wasChanged) {
mChangesNotNotified = PR_TRUE;
}
return rv;
return wasChanged;
}
@ -2094,7 +2093,7 @@ nsListControlFrame::ToggleOptionSelectedFromFrame(PRInt32 aIndex)
NS_IMETHODIMP
nsListControlFrame::UpdateSelection()
{
if (!mIsAllFramesHere || !mIsAllContentHere || !mChangesNotNotified) {
if (!mIsAllFramesHere || !mIsAllContentHere) {
return NS_OK;
}
nsresult rv = NS_OK;
@ -2104,23 +2103,30 @@ nsListControlFrame::UpdateSelection()
mComboboxFrame->IsDroppedDown(&isDroppedDown);
}
if (!isDroppedDown) {
rv = SelectionChanged(); // Dispatch event
mChangesNotNotified = PR_FALSE;
rv = FireOnChange(); // Dispatch event
}
return rv;
}
NS_IMETHODIMP
nsListControlFrame::ComboboxUpdateSelection(PRBool aForceUpdate,
PRBool aSendEvent)
void
nsListControlFrame::ComboboxFinish(PRInt32 aIndex)
{
if (mComboboxFrame) {
mComboboxFrame->ListWasSelected(mPresContext, aForceUpdate, aSendEvent);
}
mChangesNotNotified = PR_FALSE;
PerformSelection(aIndex, PR_FALSE, PR_FALSE);
return NS_OK;
PRInt32 displayIndex;
mComboboxFrame->GetIndexOfDisplayArea(&displayIndex);
if (displayIndex != aIndex) {
mComboboxFrame->RedisplaySelectedText();
}
mComboboxFrame->RollupFromList(mPresContext);
if (aIndex != mSelectedIndexWhenPoppedDown) {
FireOnChange();
}
}
}
NS_IMETHODIMP
@ -2132,7 +2138,7 @@ nsListControlFrame::GetOptionsContainer(nsIPresContext* aPresContext,
// Send out an onchange notification.
nsresult
nsListControlFrame::SelectionChanged()
nsListControlFrame::FireOnChange()
{
nsresult ret = NS_ERROR_FAILURE;
@ -2353,31 +2359,21 @@ nsListControlFrame::AboutToDropDown()
NS_IMETHODIMP
nsListControlFrame::AboutToRollup()
{
// XXX To have clicking outside the combobox ALWAYS reset the contents to the
// state before it was dropped, remove the all the code in the "if" below and replace it
// with just the call to ResetSelectedItem()
// We've been updating the combobox with the keyboard up until now, but not
// with the mouse. The problem is, even with mouse selection, we are
// updating the <select>. So if the mouse goes over an option just before
// he leaves the box and clicks, that's what the <select> will show.
//
//
// When the dropdown is dropped down via a mouse click and the user moves the mouse
// up and down without clicking, the currently selected item is being tracking inside
// the dropdown, but the combobox is not being updated. When the user selects items
// with the arrow keys, the combobox is being updated. So when the user clicks outside
// the dropdown and it needs to roll up it has to decide whether to keep the current
// selection or not. The GetIndexOfDisplayArea method is used to get the current index
// in the combobox to compare it to the current index in the dropdown to see if the combox
// has been updated and that way it knows whether to "cancel" the the current selection
// residing in the dropdown. Or whether to leave the selection alone.
// To deal with this we say "whatever is in the combobox is canonical."
// - IF the combobox is different from the current selected index, we
// reset the index.
// - IF the combobox is different from the index when it was popped down,
// we fire onChange() since it has changed.
if (IsInDropDownMode() == PR_TRUE) {
PRInt32 index;
mComboboxFrame->GetIndexOfDisplayArea(&index);
// if the indexes do NOT match then the selection in the combobox
// was never updated, and therefore we should reset the the selection back to
// whatever it was before it was dropped down.
if (index != mStartSelectionIndex) {
ResetSelectedItem();
} else {
ComboboxUpdateSelection(PR_FALSE, PR_TRUE);
}
ComboboxFinish(index);
}
return NS_OK;
}
@ -2500,21 +2496,6 @@ nsListControlFrame::IsOptionDisabled(PRInt32 anIndex, PRBool &aIsDisabled)
return NS_ERROR_FAILURE;
}
//----------------------------------------------------------------------
// This is used to reset the the list and it's selection because the
// selection was cancelled and the list rolled up.
void nsListControlFrame::ResetSelectedItem()
{
if (mIsAllFramesHere) {
SingleSelection(mSelectedIndexWhenPoppedDown, PR_FALSE);
// We're resetting ... don't send OnChange
mChangesNotNotified = PR_FALSE;
if (IsInDropDownMode() == PR_TRUE) {
ComboboxUpdateSelection(PR_TRUE, PR_FALSE);
}
}
}
//----------------------------------------------------------------------
// helper
//----------------------------------------------------------------------
@ -2619,11 +2600,9 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent)
}
if (kNothingSelected != selectedIndex) {
if (!mChangesNotNotified) {
ResetSelectedItem();
}
ComboboxFinish(selectedIndex);
}
ComboboxUpdateSelection(PR_TRUE, PR_TRUE);
mouseEvent->clickCount = 1;
} else {
// the click was out side of the select or its dropdown
@ -2633,7 +2612,9 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent)
REFLOW_DEBUG_MSG(">>>>>> Didn't find");
CaptureMouseEvents(mPresContext, PR_FALSE);
// Notify
UpdateSelection();
if (mChangesSinceDragStart) {
UpdateSelection();
}
#if 0 // XXX - this is a partial fix for Bug 29990
if (mSelectedIndex != mStartExtendedIndex) {
mEndExtendedIndex = mSelectedIndex;
@ -2761,7 +2742,7 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent)
if (!IsInDropDownMode()) {
// Handle Like List
CaptureMouseEvents(mPresContext, PR_TRUE);
HandleListSelection(aMouseEvent, selectedIndex);
mChangesSinceDragStart = HandleListSelection(aMouseEvent, selectedIndex);
}
} else {
// NOTE: the combo box is responsible for dropping it down
@ -2792,7 +2773,6 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent)
if (isDroppedDown) {
CaptureMouseEvents(mPresContext, PR_FALSE);
}
return NS_OK;
}
}
}
@ -2848,7 +2828,9 @@ nsListControlFrame::DragMove(nsIDOMEvent* aMouseEvent)
mouseEvent->GetCtrlKey(&isControl);
#endif
// Turn SHIFT on when you are dragging, unless control is on.
PerformSelection(selectedIndex, !isControl, isControl);
PRBool wasChanged = PerformSelection(selectedIndex,
!isControl, isControl);
mChangesSinceDragStart = mChangesSinceDragStart || wasChanged;
}
}
return NS_OK;
@ -3172,20 +3154,21 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
case nsIDOMKeyEvent::DOM_VK_RETURN: {
if (mComboboxFrame != nsnull) {
if (IsInDropDownMode() == PR_TRUE) {
PRBool isDroppedDown;
mComboboxFrame->IsDroppedDown(&isDroppedDown);
ComboboxUpdateSelection(isDroppedDown, PR_TRUE);
} else {
UpdateSelection();
PRBool droppedDown = PR_FALSE;
mComboboxFrame->IsDroppedDown(&droppedDown);
if (droppedDown) {
ComboboxFinish(mEndSelectionIndex);
}
return NS_OK;
} else {
newIndex = mEndSelectionIndex;
}
} break;
case nsIDOMKeyEvent::DOM_VK_ESCAPE: {
if (IsInDropDownMode() == PR_TRUE) {
ResetSelectedItem();
}
ComboboxFinish(mSelectedIndexWhenPoppedDown);
}
} break;
case nsIDOMKeyEvent::DOM_VK_PAGE_UP: {
@ -3242,13 +3225,14 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
ToLowerCase(text);
PRUnichar firstChar = text.CharAt(0);
if (firstChar == (PRUnichar)code) {
PerformSelection(selectedIndex, isShift, isControl);
PRBool wasChanged = PerformSelection(selectedIndex,
isShift, isControl);
// If it's a combobox, redisplay the text
if (mComboboxFrame && mIsAllFramesHere) {
// dispatch event
mComboboxFrame->UpdateSelection(PR_TRUE,
PR_TRUE,
selectedIndex);
} else {
mComboboxFrame->RedisplaySelectedText();
}
// Fire the event (unless it's a dropped down combobox)
if (wasChanged) {
UpdateSelection(); // dispatch event
}
break;
@ -3274,11 +3258,13 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
mEndSelectionIndex = newIndex;
ScrollToIndex(newIndex);
} else {
PerformSelection(newIndex, isShift, isControl);
// Dispatch event
PRBool wasChanged = PerformSelection(newIndex, isShift, isControl);
// If combobox, redisplay selected text.
if (mComboboxFrame && mIsAllFramesHere) {
mComboboxFrame->UpdateSelection(PR_TRUE, PR_FALSE, newIndex);
} else {
mComboboxFrame->RedisplaySelectedText();
}
// Fire the event (unless it's a dropped down combobox)
if (wasChanged) {
UpdateSelection();
}
}

View File

@ -274,7 +274,6 @@ public:
NS_IMETHOD AboutToDropDown();
NS_IMETHOD AboutToRollup();
NS_IMETHOD UpdateSelection();
NS_IMETHOD ComboboxUpdateSelection(PRBool aForceUpdate, PRBool aSendEvent);
NS_IMETHOD SetOverrideReflowOptimization(PRBool aValue) { mOverrideReflowOpt = aValue; return NS_OK; }
NS_IMETHOD GetOptionsContainer(nsIPresContext* aPresContext, nsIFrame** aFrame);
@ -286,6 +285,7 @@ public:
NS_IMETHOD OnOptionSelected(nsIPresContext* aPresContext,
PRInt32 aIndex,
PRBool aSelected);
NS_IMETHOD OnOptionTextChanged(nsIDOMHTMLOptionElement* option);
NS_IMETHOD GetDummyFrame(nsIFrame** aFrame);
NS_IMETHOD SetDummyFrame(nsIFrame* aFrame);
@ -352,41 +352,46 @@ protected:
PRBool IsContentSelected(nsIContent* aContent);
PRBool IsContentSelectedByIndex(PRInt32 aIndex);
void GetViewOffset(nsIViewManager* aManager, nsIView* aView, nsPoint& aPoint);
PRBool IsInDropDownMode();
PRBool IsOptionElement(nsIContent* aContent);
void SingleSelection(PRInt32 aSelectedIndex, PRBool aDoToggle);
void ExtendedSelection(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aClearAll);
void PerformSelection(PRInt32 aSelectedIndex,
PRBool aIsShift,
PRBool aIsControl);
void ResetSelectedItem();
PRBool CheckIfAllFramesHere();
PRInt32 GetIndexFromContent(nsIContent *aContent);
void HandleListSelection(nsIDOMEvent * aDOMEvent, PRInt32 selectedIndex);
PRBool IsLeftButton(nsIDOMEvent* aMouseEvent);
nsresult SetOptionsSelectedFromFrame(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aValue,
PRBool aClearAll);
nsresult ToggleOptionSelectedFromFrame(PRInt32 aIndex);
void GetScrollableView(nsIScrollableView*& aScrollableView);
// Dropped down stuff
void SetComboboxItem(PRInt32 aIndex);
void ComboboxFinish(PRInt32 aIndex);
PRBool IsInDropDownMode();
// Selection
PRBool SetOptionsSelectedFromFrame(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aValue,
PRBool aClearAll);
PRBool ToggleOptionSelectedFromFrame(PRInt32 aIndex);
PRBool SingleSelection(PRInt32 aSelectedIndex, PRBool aDoToggle);
PRBool ExtendedSelection(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aClearAll);
PRBool PerformSelection(PRInt32 aSelectedIndex,
PRBool aIsShift,
PRBool aIsControl);
PRBool HandleListSelection(nsIDOMEvent * aDOMEvent, PRInt32 selectedIndex);
// Timer Methods
nsresult StartUpdateTimer(nsIPresContext * aPresContext);
void StopUpdateTimer();
void ItemsHaveBeenRemoved(nsIPresContext * aPresContext);
// onChange detection
nsresult SelectionChanged();
// fire onChange
nsresult FireOnChange();
// Data Members
nsFormFrame* mFormFrame;
PRInt32 mStartSelectionIndex;
PRInt32 mEndSelectionIndex;
PRBool mChangesNotNotified;
PRPackedBool mChangesSinceDragStart;
PRInt32 mSelectedIndexWhenPoppedDown;
nsIComboboxControlFrame *mComboboxFrame;

View File

@ -89,26 +89,15 @@ public:
NS_IMETHOD SetDropDown(nsIFrame* aDropDownFrame) = 0;
/**
* Notifies the Combobox the List was selected
* Tells the combobox to roll up
*
*/
NS_IMETHOD ListWasSelected(nsIPresContext* aPresContext, PRBool aForceUpdate, PRBool aSendEvent) = 0;
NS_IMETHOD RollupFromList(nsIPresContext* aPresContext) = 0;
/**
* Asks the Combobox to update the display frame
* aDoDispatchEvent - indicates whether an event should be dispatched to the DOM
* aForceUpdate - indicates whether the indexx and the text value should both be
* whether the index has changed or not.
*
* Redisplay the selected text (will do nothing if text has not changed)
*/
NS_IMETHOD UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate, PRInt32 aNewIndex) = 0;
/**
* Asks the Combobox to update the display frame when the selection has
* changed
*
*/
NS_IMETHOD SelectionChanged() = 0;
NS_IMETHOD RedisplaySelectedText() = 0;
/**
*

View File

@ -46,6 +46,8 @@
{ 0x96, 0xea, 0x0, 0x60, 0xb0, 0xfb, 0x99, 0x56 } }
// {162A2AE3-5A79-11d3-96EA-0060B0FB9956}
class nsIDOMHTMLOptionElement;
/**
* nsISelectControlFrame is the interface for combo boxes and listboxes
*/
@ -83,6 +85,13 @@ public:
PRInt32 aIndex,
PRBool aSelected) = 0;
/**
* Notify the frame when an option's text changes
* (We don't pass in the index because it would be expensive for
* the option to figure that out)
*/
NS_IMETHOD OnOptionTextChanged(nsIDOMHTMLOptionElement* option) = 0;
/**
* For the content model to tell if there's a dummy frame or not
*/

View File

@ -291,7 +291,6 @@ nsComboboxControlFrame::nsComboboxControlFrame()
mDisplayFrame = nsnull;
mButtonFrame = nsnull;
mDropdownFrame = nsnull;
mSelectedIndex = -1;
mCacheSize.width = kSizeNotSet;
mCacheSize.height = kSizeNotSet;
@ -412,17 +411,15 @@ nsComboboxControlFrame::Init(nsIPresContext* aPresContext,
void
nsComboboxControlFrame::InitTextStr()
{
nsAutoString textToDisplay;
PRInt32 selectedIndex;
mListControlFrame->GetSelectedIndex(&selectedIndex);
// Update the selected text string
if (selectedIndex == -1) {
mListControlFrame->GetOptionText(0, mTextStr);
} else {
mListControlFrame->GetOptionText(selectedIndex, mTextStr);
if (selectedIndex != -1) {
mListControlFrame->GetOptionText(selectedIndex, textToDisplay);
}
// Update the display by setting the value attribute
mDisplayContent->SetText(mTextStr.get(), mTextStr.Length(), PR_FALSE);
mDisplayedIndex = selectedIndex;
ActuallyDisplayText(textToDisplay, PR_FALSE);
}
@ -1848,22 +1845,6 @@ nsComboboxControlFrame::GetDropDown(nsIFrame** aDropDownFrame)
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::ListWasSelected(nsIPresContext* aPresContext, PRBool aForceUpdate, PRBool aSendEvent) // Added "aForceUpdate" for Bug 42661
{
if (aPresContext == nsnull) {
aPresContext = mPresContext;
}
ShowList(aPresContext, PR_FALSE);
mListControlFrame->CaptureMouseEvents(aPresContext, PR_FALSE);
PRInt32 indx;
mListControlFrame->GetSelectedIndex(&indx);
UpdateSelection(aSendEvent, aForceUpdate, indx); // Added "aForceUpdate" for Bug 42661
return NS_OK;
}
// Toggle dropdown list.
NS_IMETHODIMP
@ -1875,29 +1856,6 @@ nsComboboxControlFrame::ToggleList(nsIPresContext* aPresContext)
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate, PRInt32 aNewIndex)
{
if (mListControlFrame) {
// Check to see if the selection changed
if (mSelectedIndex != aNewIndex || aForceUpdate) {
mListControlFrame->GetOptionText(aNewIndex, mTextStr);
SelectionChanged();
// Fix for Bug 42661 (remove comment later)
#ifdef DO_REFLOW_DEBUG
char * str = ToNewCString(mTextStr);
REFLOW_DEBUG_MSG2("UpdateSelection %s\n", str);
delete [] str;
#endif
mSelectedIndex = aNewIndex;
mListControlFrame->UpdateSelection();
}
}
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::AbsolutelyPositionDropDown()
{
@ -1926,8 +1884,30 @@ nsComboboxControlFrame::GetAbsoluteRect(nsRect* aRect)
///////////////////////////////////////////////////////////////
NS_IMETHODIMP
nsComboboxControlFrame::SelectionChanged()
nsComboboxControlFrame::RedisplaySelectedText()
{
PRInt32 selectedIndex;
mListControlFrame->GetSelectedIndex(&selectedIndex);
return RedisplayText(selectedIndex);
}
nsresult
nsComboboxControlFrame::RedisplayText(PRInt32 aIndex)
{
// Get the text to display
nsAutoString textToDisplay;
if (aIndex != -1) {
mListControlFrame->GetOptionText(aIndex, textToDisplay);
}
mDisplayedIndex = aIndex;
#ifdef DO_REFLOW_DEBUG
char * str = ToNewCString(textToDisplay);
REFLOW_DEBUG_MSG2("RedisplayText %s\n", str);
delete [] str;
#endif
// Send reflow command because the new text maybe larger
nsresult rv = NS_OK;
if (mDisplayContent) {
@ -1941,16 +1921,13 @@ nsComboboxControlFrame::SelectionChanged()
if (NS_FAILED(result) || value.Length() == 0) {
shouldSetValue = PR_TRUE;
} else {
shouldSetValue = value != mTextStr;
REFLOW_DEBUG_MSG3("**** CBX::SelectionChanged Old[%s] New[%s]\n", NS_LossyConvertUCS2toASCII(value).get(), NS_LossyConvertUCS2toASCII(mTextStr).get());
shouldSetValue = value != textToDisplay;
REFLOW_DEBUG_MSG3("**** CBX::RedisplayText Old[%s] New[%s]\n",
NS_LossyConvertUCS2toASCII(value).get(),
NS_LossyConvertUCS2toASCII(textToDisplay).get());
}
if (shouldSetValue) {
if (mTextStr.Length() == 0) {
nsAutoString space(NS_LITERAL_STRING(" "));
rv = mDisplayContent->SetText(space.get(), space.Length(), PR_TRUE);
} else {
rv = mDisplayContent->SetText(mTextStr.get(), mTextStr.Length(), PR_TRUE);
}
rv = ActuallyDisplayText(textToDisplay, PR_TRUE);
nsFrameState state;
//mTextFrame->GetFrameState(&state);
//state |= NS_FRAME_IS_DIRTY;
@ -1970,11 +1947,26 @@ nsComboboxControlFrame::SelectionChanged()
return rv;
}
nsresult
nsComboboxControlFrame::ActuallyDisplayText(nsAString& aText, PRBool aNotify)
{
nsresult rv = NS_OK;
if (aText.IsEmpty()) {
nsAutoString space(PRUnichar(' '));
rv = mDisplayContent->SetText(space.get(), space.Length(), aNotify);
} else {
const nsAFlatString& flat = PromiseFlatString(aText);
rv = mDisplayContent->SetText(flat.get(), flat.Length(), aNotify);
}
return rv;
}
NS_IMETHODIMP
nsComboboxControlFrame::GetIndexOfDisplayArea(PRInt32* aSelectedIndex)
{
NS_ENSURE_ARG_POINTER(aSelectedIndex);
*aSelectedIndex = mSelectedIndex;
*aSelectedIndex = mDisplayedIndex;
return NS_OK;
}
@ -2473,6 +2465,15 @@ nsComboboxControlFrame::Rollup()
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::RollupFromList(nsIPresContext* aPresContext)
{
ShowList(aPresContext, PR_FALSE);
mListControlFrame->CaptureMouseEvents(aPresContext, PR_FALSE);
return NS_OK;
}
NS_METHOD
nsComboboxControlFrame::Paint(nsIPresContext* aPresContext,
nsIRenderingContext& aRenderingContext,
@ -2579,13 +2580,36 @@ nsComboboxControlFrame::OnOptionSelected(nsIPresContext* aPresContext,
PRInt32 aIndex,
PRBool aSelected)
{
if (aSelected && !mDroppedDown) {
mListControlFrame->GetOptionText(aIndex, mTextStr);
SelectionChanged();
if (aSelected) {
if (!mDroppedDown) {
RedisplayText(aIndex);
} else {
nsCOMPtr<nsISelectControlFrame> selectFrame
= do_QueryInterface(mListControlFrame);
if (selectFrame) {
selectFrame->OnOptionSelected(aPresContext, aIndex, aSelected);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::OnOptionTextChanged(nsIDOMHTMLOptionElement* option)
{
RedisplaySelectedText();
if (mDroppedDown) {
nsCOMPtr<nsISelectControlFrame> selectFrame
= do_QueryInterface(mListControlFrame);
if (selectFrame) {
selectFrame->OnOptionTextChanged(option);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsComboboxControlFrame::OnContentReset()
{

View File

@ -169,10 +169,6 @@ public:
nscoord aCharWidth) const;
virtual nsresult RequiresWidget(PRBool &aRequiresWidget);
NS_IMETHOD SelectionChanged();// Called when the selection has changed.
// If the the same item in the list is selected
// it is NOT called.
// nsIFormMouseListener
virtual void MouseClicked(nsIPresContext* aPresContext);
@ -181,11 +177,11 @@ public:
NS_IMETHOD ShowDropDown(PRBool aDoDropDown);
NS_IMETHOD GetDropDown(nsIFrame** aDropDownFrame);
NS_IMETHOD SetDropDown(nsIFrame* aDropDownFrame);
NS_IMETHOD ListWasSelected(nsIPresContext* aPresContext, PRBool aForceUpdate, PRBool aSendEvent);
NS_IMETHOD UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate, PRInt32 aNewIndex);
NS_IMETHOD RollupFromList(nsIPresContext* aPresContext);
NS_IMETHOD AbsolutelyPositionDropDown();
NS_IMETHOD GetAbsoluteRect(nsRect* aRect);
NS_IMETHOD GetIndexOfDisplayArea(PRInt32* aSelectedIndex);
NS_IMETHOD RedisplaySelectedText();
// nsISelectControlFrame
NS_IMETHOD AddOption(nsIPresContext* aPresContext, PRInt32 index);
@ -195,6 +191,7 @@ public:
NS_IMETHOD OnOptionSelected(nsIPresContext* aPresContext,
PRInt32 aIndex,
PRBool aSelected);
NS_IMETHOD OnOptionTextChanged(nsIDOMHTMLOptionElement* option);
NS_IMETHOD GetDummyFrame(nsIFrame** aFrame);
NS_IMETHOD SetDummyFrame(nsIFrame* aFrame);
@ -223,6 +220,7 @@ public:
NS_IMETHOD RestoreState(nsIPresContext* aPresContext, nsIPresState* aState);
protected:
NS_IMETHOD CreateDisplayFrame(nsIPresContext* aPresContext);
#ifdef DO_NEW_REFLOW
@ -253,6 +251,8 @@ protected:
void ShowList(nsIPresContext* aPresContext, PRBool aShowList);
void SetChildFrameSize(nsIFrame* aFrame, nscoord aWidth, nscoord aHeight);
void InitTextStr();
nsresult RedisplayText(PRInt32 aIndex);
nsresult ActuallyDisplayText(nsAString& aText, PRBool aNotify);
nsresult GetPrimaryComboFrame(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame** aFrame);
NS_IMETHOD ToggleList(nsIPresContext* aPresContext);
@ -271,8 +271,6 @@ protected:
nsFrameList mPopupFrames; // additional named child list
nsIPresContext* mPresContext; // XXX: Remove the need to cache the pres context.
nsFormFrame* mFormFrame; // Parent Form Frame
nsString mTextStr; // Current Combo box selection
PRInt32 mSelectedIndex; // current selected index
nsCOMPtr<nsITextContent> mDisplayContent; // Anonymous content used to display the current selection
PRPackedBool mDroppedDown; // Current state of the dropdown list, PR_TRUE is dropped down
nsIFrame* mDisplayFrame; // frame to display selection
@ -295,6 +293,8 @@ protected:
PRPackedBool mGoodToGo;
PRInt32 mDisplayedIndex;
// make someone to listen to the button. If its programmatically pressed by someone like Accessibility
// then open or close the combo box.
nsCOMPtr<nsIDOMMouseListener> mButtonListener;

View File

@ -1234,37 +1234,41 @@ nsListControlFrame::GetIndexFromContent(nsIContent *aContent)
}
//---------------------------------------------------------
void
PRBool
nsListControlFrame::ExtendedSelection(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aClearAll)
{
SetOptionsSelectedFromFrame(aStartIndex, aEndIndex, PR_TRUE, aClearAll);
return SetOptionsSelectedFromFrame(aStartIndex, aEndIndex,
PR_TRUE, aClearAll);
}
//---------------------------------------------------------
void
PRBool
nsListControlFrame::SingleSelection(PRInt32 aSelectedIndex, PRBool aDoToggle)
{
PRBool wasChanged = PR_FALSE;
// Get Current selection
if (aDoToggle) {
ToggleOptionSelectedFromFrame(aSelectedIndex);
wasChanged = ToggleOptionSelectedFromFrame(aSelectedIndex);
} else {
SetOptionsSelectedFromFrame(aSelectedIndex, aSelectedIndex,
wasChanged = SetOptionsSelectedFromFrame(aSelectedIndex, aSelectedIndex,
PR_TRUE, PR_TRUE);
}
ScrollToIndex(aSelectedIndex);
mStartSelectionIndex = aSelectedIndex;
mEndSelectionIndex = aSelectedIndex;
mChangesNotNotified = PR_TRUE;
return wasChanged;
}
//---------------------------------------------------------
void
PRBool
nsListControlFrame::PerformSelection(PRInt32 aSelectedIndex,
PRBool aIsShift,
PRBool aIsControl)
{
PRBool wasChanged = PR_FALSE;
PRBool isMultiple;
GetMultiple(&isMultiple);
@ -1286,7 +1290,7 @@ nsListControlFrame::PerformSelection(PRInt32 aSelectedIndex,
endIndex = mStartSelectionIndex;
}
ExtendedSelection(startIndex, endIndex, PR_TRUE);
wasChanged = ExtendedSelection(startIndex, endIndex, PR_TRUE);
ScrollToIndex(aSelectedIndex);
if (mStartSelectionIndex == kNothingSelected) {
@ -1295,14 +1299,13 @@ nsListControlFrame::PerformSelection(PRInt32 aSelectedIndex,
} else {
mEndSelectionIndex = aSelectedIndex;
}
mChangesNotNotified = PR_TRUE;
} else if (aIsControl) {
SingleSelection(aSelectedIndex, PR_TRUE);
wasChanged = SingleSelection(aSelectedIndex, PR_TRUE);
} else {
SingleSelection(aSelectedIndex, PR_FALSE);
wasChanged = SingleSelection(aSelectedIndex, PR_FALSE);
}
} else {
SingleSelection(aSelectedIndex, PR_FALSE);
wasChanged = SingleSelection(aSelectedIndex, PR_FALSE);
}
#ifdef DEBUG_rodsX
@ -1311,10 +1314,12 @@ nsListControlFrame::PerformSelection(PRInt32 aSelectedIndex,
printf("mLastStartIndex: %d\n", mLastStartIndex);
printf("mLastEndIndex: %d\n", mLastEndIndex);
#endif
return wasChanged;
}
//---------------------------------------------------------
void
PRBool
nsListControlFrame::HandleListSelection(nsIDOMEvent* aEvent,
PRInt32 aSelectedIndex)
{
@ -1327,7 +1332,7 @@ nsListControlFrame::HandleListSelection(nsIDOMEvent* aEvent,
mouseEvent->GetCtrlKey(&isControl);
#endif
mouseEvent->GetShiftKey(&isShift);
PerformSelection(aSelectedIndex, isShift, isControl);
return PerformSelection(aSelectedIndex, isShift, isControl);
}
//---------------------------------------------------------
@ -1518,7 +1523,6 @@ nsListControlFrame::Init(nsIPresContext* aPresContext,
mStartSelectionIndex = kNothingSelected;
mEndSelectionIndex = kNothingSelected;
mChangesNotNotified = PR_FALSE;
return result;
}
@ -1711,6 +1715,12 @@ nsListControlFrame::OnOptionSelected(nsIPresContext* aPresContext,
return NS_OK;
}
NS_IMETHODIMP
nsListControlFrame::OnOptionTextChanged(nsIDOMHTMLOptionElement* option)
{
return NS_OK;
}
//---------------------------------------------------------
PRIntn
@ -1777,13 +1787,8 @@ nsListControlFrame::ResetList(nsIPresContext* aPresContext, nsVoidArray * aInxLi
mStartSelectionIndex = kNothingSelected;
mEndSelectionIndex = kNothingSelected;
mChangesNotNotified = PR_FALSE;
if (mComboboxFrame != nsnull) {
// don't dispatch event
mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, indexToSelect);
}
// Combobox will redisplay itself with the OnOptionSelected event
}
//---------------------------------------------------------
@ -2030,7 +2035,7 @@ nsListControlFrame::RemoveOption(nsIPresContext* aPresContext, PRInt32 aIndex)
// Set the option selected in the DOM. This method is named
// as it is because it indicates that the frame is the source
// of this event rather than the receiver.
nsresult
PRBool
nsListControlFrame::SetOptionsSelectedFromFrame(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aValue,
@ -2046,25 +2051,22 @@ nsListControlFrame::SetOptionsSelectedFromFrame(PRInt32 aStartIndex,
PR_TRUE,
&wasChanged);
NS_ASSERTION(NS_SUCCEEDED(rv), "SetSelected failed");
if (NS_SUCCEEDED(rv) && !mChangesNotNotified && wasChanged) {
mChangesNotNotified = PR_TRUE;
}
return rv;
return wasChanged;
}
nsresult
PRBool
nsListControlFrame::ToggleOptionSelectedFromFrame(PRInt32 aIndex)
{
nsCOMPtr<nsIDOMHTMLCollection> options(getter_AddRefs(GetOptions(mContent)));
NS_ASSERTION(options, "No options");
if (!options) {
return NS_ERROR_FAILURE;
return PR_FALSE;
}
nsCOMPtr<nsIDOMHTMLOptionElement> option(
getter_AddRefs(GetOption(*options, aIndex)));
NS_ASSERTION(option, "No option");
if (!option) {
return NS_ERROR_FAILURE;
return PR_FALSE;
}
PRBool value = PR_FALSE;
@ -2082,11 +2084,8 @@ nsListControlFrame::ToggleOptionSelectedFromFrame(PRInt32 aIndex)
&wasChanged);
NS_ASSERTION(NS_SUCCEEDED(rv), "SetSelected failed");
if (wasChanged) {
mChangesNotNotified = PR_TRUE;
}
return rv;
return wasChanged;
}
@ -2094,7 +2093,7 @@ nsListControlFrame::ToggleOptionSelectedFromFrame(PRInt32 aIndex)
NS_IMETHODIMP
nsListControlFrame::UpdateSelection()
{
if (!mIsAllFramesHere || !mIsAllContentHere || !mChangesNotNotified) {
if (!mIsAllFramesHere || !mIsAllContentHere) {
return NS_OK;
}
nsresult rv = NS_OK;
@ -2104,23 +2103,30 @@ nsListControlFrame::UpdateSelection()
mComboboxFrame->IsDroppedDown(&isDroppedDown);
}
if (!isDroppedDown) {
rv = SelectionChanged(); // Dispatch event
mChangesNotNotified = PR_FALSE;
rv = FireOnChange(); // Dispatch event
}
return rv;
}
NS_IMETHODIMP
nsListControlFrame::ComboboxUpdateSelection(PRBool aForceUpdate,
PRBool aSendEvent)
void
nsListControlFrame::ComboboxFinish(PRInt32 aIndex)
{
if (mComboboxFrame) {
mComboboxFrame->ListWasSelected(mPresContext, aForceUpdate, aSendEvent);
}
mChangesNotNotified = PR_FALSE;
PerformSelection(aIndex, PR_FALSE, PR_FALSE);
return NS_OK;
PRInt32 displayIndex;
mComboboxFrame->GetIndexOfDisplayArea(&displayIndex);
if (displayIndex != aIndex) {
mComboboxFrame->RedisplaySelectedText();
}
mComboboxFrame->RollupFromList(mPresContext);
if (aIndex != mSelectedIndexWhenPoppedDown) {
FireOnChange();
}
}
}
NS_IMETHODIMP
@ -2132,7 +2138,7 @@ nsListControlFrame::GetOptionsContainer(nsIPresContext* aPresContext,
// Send out an onchange notification.
nsresult
nsListControlFrame::SelectionChanged()
nsListControlFrame::FireOnChange()
{
nsresult ret = NS_ERROR_FAILURE;
@ -2353,31 +2359,21 @@ nsListControlFrame::AboutToDropDown()
NS_IMETHODIMP
nsListControlFrame::AboutToRollup()
{
// XXX To have clicking outside the combobox ALWAYS reset the contents to the
// state before it was dropped, remove the all the code in the "if" below and replace it
// with just the call to ResetSelectedItem()
// We've been updating the combobox with the keyboard up until now, but not
// with the mouse. The problem is, even with mouse selection, we are
// updating the <select>. So if the mouse goes over an option just before
// he leaves the box and clicks, that's what the <select> will show.
//
//
// When the dropdown is dropped down via a mouse click and the user moves the mouse
// up and down without clicking, the currently selected item is being tracking inside
// the dropdown, but the combobox is not being updated. When the user selects items
// with the arrow keys, the combobox is being updated. So when the user clicks outside
// the dropdown and it needs to roll up it has to decide whether to keep the current
// selection or not. The GetIndexOfDisplayArea method is used to get the current index
// in the combobox to compare it to the current index in the dropdown to see if the combox
// has been updated and that way it knows whether to "cancel" the the current selection
// residing in the dropdown. Or whether to leave the selection alone.
// To deal with this we say "whatever is in the combobox is canonical."
// - IF the combobox is different from the current selected index, we
// reset the index.
// - IF the combobox is different from the index when it was popped down,
// we fire onChange() since it has changed.
if (IsInDropDownMode() == PR_TRUE) {
PRInt32 index;
mComboboxFrame->GetIndexOfDisplayArea(&index);
// if the indexes do NOT match then the selection in the combobox
// was never updated, and therefore we should reset the the selection back to
// whatever it was before it was dropped down.
if (index != mStartSelectionIndex) {
ResetSelectedItem();
} else {
ComboboxUpdateSelection(PR_FALSE, PR_TRUE);
}
ComboboxFinish(index);
}
return NS_OK;
}
@ -2500,21 +2496,6 @@ nsListControlFrame::IsOptionDisabled(PRInt32 anIndex, PRBool &aIsDisabled)
return NS_ERROR_FAILURE;
}
//----------------------------------------------------------------------
// This is used to reset the the list and it's selection because the
// selection was cancelled and the list rolled up.
void nsListControlFrame::ResetSelectedItem()
{
if (mIsAllFramesHere) {
SingleSelection(mSelectedIndexWhenPoppedDown, PR_FALSE);
// We're resetting ... don't send OnChange
mChangesNotNotified = PR_FALSE;
if (IsInDropDownMode() == PR_TRUE) {
ComboboxUpdateSelection(PR_TRUE, PR_FALSE);
}
}
}
//----------------------------------------------------------------------
// helper
//----------------------------------------------------------------------
@ -2619,11 +2600,9 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent)
}
if (kNothingSelected != selectedIndex) {
if (!mChangesNotNotified) {
ResetSelectedItem();
}
ComboboxFinish(selectedIndex);
}
ComboboxUpdateSelection(PR_TRUE, PR_TRUE);
mouseEvent->clickCount = 1;
} else {
// the click was out side of the select or its dropdown
@ -2633,7 +2612,9 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent)
REFLOW_DEBUG_MSG(">>>>>> Didn't find");
CaptureMouseEvents(mPresContext, PR_FALSE);
// Notify
UpdateSelection();
if (mChangesSinceDragStart) {
UpdateSelection();
}
#if 0 // XXX - this is a partial fix for Bug 29990
if (mSelectedIndex != mStartExtendedIndex) {
mEndExtendedIndex = mSelectedIndex;
@ -2761,7 +2742,7 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent)
if (!IsInDropDownMode()) {
// Handle Like List
CaptureMouseEvents(mPresContext, PR_TRUE);
HandleListSelection(aMouseEvent, selectedIndex);
mChangesSinceDragStart = HandleListSelection(aMouseEvent, selectedIndex);
}
} else {
// NOTE: the combo box is responsible for dropping it down
@ -2792,7 +2773,6 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent)
if (isDroppedDown) {
CaptureMouseEvents(mPresContext, PR_FALSE);
}
return NS_OK;
}
}
}
@ -2848,7 +2828,9 @@ nsListControlFrame::DragMove(nsIDOMEvent* aMouseEvent)
mouseEvent->GetCtrlKey(&isControl);
#endif
// Turn SHIFT on when you are dragging, unless control is on.
PerformSelection(selectedIndex, !isControl, isControl);
PRBool wasChanged = PerformSelection(selectedIndex,
!isControl, isControl);
mChangesSinceDragStart = mChangesSinceDragStart || wasChanged;
}
}
return NS_OK;
@ -3172,20 +3154,21 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
case nsIDOMKeyEvent::DOM_VK_RETURN: {
if (mComboboxFrame != nsnull) {
if (IsInDropDownMode() == PR_TRUE) {
PRBool isDroppedDown;
mComboboxFrame->IsDroppedDown(&isDroppedDown);
ComboboxUpdateSelection(isDroppedDown, PR_TRUE);
} else {
UpdateSelection();
PRBool droppedDown = PR_FALSE;
mComboboxFrame->IsDroppedDown(&droppedDown);
if (droppedDown) {
ComboboxFinish(mEndSelectionIndex);
}
return NS_OK;
} else {
newIndex = mEndSelectionIndex;
}
} break;
case nsIDOMKeyEvent::DOM_VK_ESCAPE: {
if (IsInDropDownMode() == PR_TRUE) {
ResetSelectedItem();
}
ComboboxFinish(mSelectedIndexWhenPoppedDown);
}
} break;
case nsIDOMKeyEvent::DOM_VK_PAGE_UP: {
@ -3242,13 +3225,14 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
ToLowerCase(text);
PRUnichar firstChar = text.CharAt(0);
if (firstChar == (PRUnichar)code) {
PerformSelection(selectedIndex, isShift, isControl);
PRBool wasChanged = PerformSelection(selectedIndex,
isShift, isControl);
// If it's a combobox, redisplay the text
if (mComboboxFrame && mIsAllFramesHere) {
// dispatch event
mComboboxFrame->UpdateSelection(PR_TRUE,
PR_TRUE,
selectedIndex);
} else {
mComboboxFrame->RedisplaySelectedText();
}
// Fire the event (unless it's a dropped down combobox)
if (wasChanged) {
UpdateSelection(); // dispatch event
}
break;
@ -3274,11 +3258,13 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
mEndSelectionIndex = newIndex;
ScrollToIndex(newIndex);
} else {
PerformSelection(newIndex, isShift, isControl);
// Dispatch event
PRBool wasChanged = PerformSelection(newIndex, isShift, isControl);
// If combobox, redisplay selected text.
if (mComboboxFrame && mIsAllFramesHere) {
mComboboxFrame->UpdateSelection(PR_TRUE, PR_FALSE, newIndex);
} else {
mComboboxFrame->RedisplaySelectedText();
}
// Fire the event (unless it's a dropped down combobox)
if (wasChanged) {
UpdateSelection();
}
}

View File

@ -274,7 +274,6 @@ public:
NS_IMETHOD AboutToDropDown();
NS_IMETHOD AboutToRollup();
NS_IMETHOD UpdateSelection();
NS_IMETHOD ComboboxUpdateSelection(PRBool aForceUpdate, PRBool aSendEvent);
NS_IMETHOD SetOverrideReflowOptimization(PRBool aValue) { mOverrideReflowOpt = aValue; return NS_OK; }
NS_IMETHOD GetOptionsContainer(nsIPresContext* aPresContext, nsIFrame** aFrame);
@ -286,6 +285,7 @@ public:
NS_IMETHOD OnOptionSelected(nsIPresContext* aPresContext,
PRInt32 aIndex,
PRBool aSelected);
NS_IMETHOD OnOptionTextChanged(nsIDOMHTMLOptionElement* option);
NS_IMETHOD GetDummyFrame(nsIFrame** aFrame);
NS_IMETHOD SetDummyFrame(nsIFrame* aFrame);
@ -352,41 +352,46 @@ protected:
PRBool IsContentSelected(nsIContent* aContent);
PRBool IsContentSelectedByIndex(PRInt32 aIndex);
void GetViewOffset(nsIViewManager* aManager, nsIView* aView, nsPoint& aPoint);
PRBool IsInDropDownMode();
PRBool IsOptionElement(nsIContent* aContent);
void SingleSelection(PRInt32 aSelectedIndex, PRBool aDoToggle);
void ExtendedSelection(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aClearAll);
void PerformSelection(PRInt32 aSelectedIndex,
PRBool aIsShift,
PRBool aIsControl);
void ResetSelectedItem();
PRBool CheckIfAllFramesHere();
PRInt32 GetIndexFromContent(nsIContent *aContent);
void HandleListSelection(nsIDOMEvent * aDOMEvent, PRInt32 selectedIndex);
PRBool IsLeftButton(nsIDOMEvent* aMouseEvent);
nsresult SetOptionsSelectedFromFrame(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aValue,
PRBool aClearAll);
nsresult ToggleOptionSelectedFromFrame(PRInt32 aIndex);
void GetScrollableView(nsIScrollableView*& aScrollableView);
// Dropped down stuff
void SetComboboxItem(PRInt32 aIndex);
void ComboboxFinish(PRInt32 aIndex);
PRBool IsInDropDownMode();
// Selection
PRBool SetOptionsSelectedFromFrame(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aValue,
PRBool aClearAll);
PRBool ToggleOptionSelectedFromFrame(PRInt32 aIndex);
PRBool SingleSelection(PRInt32 aSelectedIndex, PRBool aDoToggle);
PRBool ExtendedSelection(PRInt32 aStartIndex,
PRInt32 aEndIndex,
PRBool aClearAll);
PRBool PerformSelection(PRInt32 aSelectedIndex,
PRBool aIsShift,
PRBool aIsControl);
PRBool HandleListSelection(nsIDOMEvent * aDOMEvent, PRInt32 selectedIndex);
// Timer Methods
nsresult StartUpdateTimer(nsIPresContext * aPresContext);
void StopUpdateTimer();
void ItemsHaveBeenRemoved(nsIPresContext * aPresContext);
// onChange detection
nsresult SelectionChanged();
// fire onChange
nsresult FireOnChange();
// Data Members
nsFormFrame* mFormFrame;
PRInt32 mStartSelectionIndex;
PRInt32 mEndSelectionIndex;
PRBool mChangesNotNotified;
PRPackedBool mChangesSinceDragStart;
PRInt32 mSelectedIndexWhenPoppedDown;
nsIComboboxControlFrame *mComboboxFrame;