From 36b13cb2baf0a1e26b9d6ed5366774a328f0a5f4 Mon Sep 17 00:00:00 2001 From: "rods%netscape.com" Date: Fri, 8 Jun 2001 02:34:09 +0000 Subject: [PATCH] Major rework of how items are added, removed and selected (while being added and removed) with script Now all the changes are performed, but no reflows are issued until the timer fires, which would be after the script is done executing. The selection that take place during the script execution are also cached and done afterward. Bug 53165 r=pollmann sr=attinasi a=asa git-svn-id: svn://10.0.0.236/trunk@96675 18797224-902f-48f8-a5cc-f745e15eee43 --- .../html/content/src/nsHTMLOptionElement.cpp | 6 +- .../html/content/src/nsHTMLSelectElement.cpp | 4 +- .../layout/forms/nsComboboxControlFrame.cpp | 35 +- mozilla/layout/forms/nsComboboxControlFrame.h | 2 +- mozilla/layout/forms/nsISelectControlFrame.h | 5 + mozilla/layout/forms/nsListControlFrame.cpp | 571 +++++++++++------- mozilla/layout/forms/nsListControlFrame.h | 30 +- .../html/forms/public/nsISelectControlFrame.h | 5 + .../html/forms/src/nsComboboxControlFrame.cpp | 35 +- .../html/forms/src/nsComboboxControlFrame.h | 2 +- .../html/forms/src/nsListControlFrame.cpp | 571 +++++++++++------- .../html/forms/src/nsListControlFrame.h | 30 +- 12 files changed, 811 insertions(+), 485 deletions(-) diff --git a/mozilla/content/html/content/src/nsHTMLOptionElement.cpp b/mozilla/content/html/content/src/nsHTMLOptionElement.cpp index d5343e25671..ceeabed20b8 100644 --- a/mozilla/content/html/content/src/nsHTMLOptionElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLOptionElement.cpp @@ -227,7 +227,9 @@ nsHTMLOptionElement::GetSelected(PRBool* aValue) *aValue = PR_FALSE; nsIFormControlFrame* formControlFrame = nsnull; - GetPrimaryFrame(formControlFrame); + + // DO NOT flush pending reflows here + GetPrimaryFrame(formControlFrame, PR_FALSE); if (formControlFrame) { PRInt32 indx; @@ -265,6 +267,7 @@ nsHTMLOptionElement::SetSelected(PRBool aValue) { nsIFormControlFrame* fcFrame = nsnull; + // DO NOT flush pending reflows here nsresult result = GetPrimaryFrame(fcFrame, PR_FALSE); if (NS_SUCCEEDED(result) && fcFrame) { @@ -278,6 +281,7 @@ nsHTMLOptionElement::SetSelected(PRBool aValue) GetIndex(&indx); if (indx >= 0) { + // this will flush pending reflows return selectFrame->SetOptionSelected(indx, aValue); } } diff --git a/mozilla/content/html/content/src/nsHTMLSelectElement.cpp b/mozilla/content/html/content/src/nsHTMLSelectElement.cpp index 5410802a0fe..5e29257c743 100644 --- a/mozilla/content/html/content/src/nsHTMLSelectElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLSelectElement.cpp @@ -514,7 +514,7 @@ nsHTMLSelectElement::GetSelectedIndex(PRInt32* aValue) { nsIFormControlFrame* formControlFrame = nsnull; - nsresult rv = GetPrimaryFrame(this, formControlFrame); + nsresult rv = GetPrimaryFrame(this, formControlFrame, PR_FALSE); if (NS_SUCCEEDED(rv)) { nsAutoString value; @@ -637,7 +637,7 @@ nsHTMLSelectElement::SetSelectedIndex(PRInt32 aIndex) { nsIFormControlFrame* formControlFrame = nsnull; - GetPrimaryFrame(this, formControlFrame); + GetPrimaryFrame(this, formControlFrame, PR_FALSE); if (formControlFrame) { nsAutoString value; diff --git a/mozilla/layout/forms/nsComboboxControlFrame.cpp b/mozilla/layout/forms/nsComboboxControlFrame.cpp index f1008bfe19c..4d5513310f9 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.cpp +++ b/mozilla/layout/forms/nsComboboxControlFrame.cpp @@ -2030,24 +2030,14 @@ nsComboboxControlFrame::DoneAddingContent(PRBool aIsDone) NS_IMETHODIMP nsComboboxControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) { -#ifdef DO_REFLOW_DEBUG - printf("**********\n*********AddOption: %d\n", aIndex); +#ifdef DO_REFLOW_DEBUGXX + printf("*********AddOption: %d\n", aIndex); #endif - nsISelectControlFrame* listFrame = nsnull; - nsresult rv = mDropdownFrame->QueryInterface(NS_GET_IID(nsISelectControlFrame), - (void**)&listFrame); - if (NS_SUCCEEDED(rv) && listFrame) { - rv = listFrame->AddOption(aPresContext, aIndex); - //PRInt32 index; - //mListControlFrame->GetSelectedIndex(&index); - //UpdateSelection(PR_FALSE, PR_TRUE, index); - NS_RELEASE(listFrame); + nsresult rv = NS_ERROR_FAILURE; + if (mDropdownFrame) { + nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame); + rv = lcf->AddOption(aPresContext, aIndex); } - // If we added the first option, we might need to select it. - // We should call MakeSureSomethingIsSelected here, but since it - // it changes selection, which currently causes a reframe, and thus - // deletes the frame out from under the caller, causing a crash. (Bug 17995) - // XXX MakeSureSomethingIsSelected(aPresContext); return rv; } @@ -2055,16 +2045,11 @@ nsComboboxControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) NS_IMETHODIMP nsComboboxControlFrame::RemoveOption(nsIPresContext* aPresContext, PRInt32 aIndex) { - nsISelectControlFrame* listFrame = nsnull; - nsresult rv = mDropdownFrame->QueryInterface(NS_GET_IID(nsISelectControlFrame), - (void**)&listFrame); - if (NS_SUCCEEDED(rv) && listFrame) { - rv = listFrame->RemoveOption(aPresContext, aIndex); - NS_RELEASE(listFrame); + nsresult rv = NS_ERROR_FAILURE; + if (mDropdownFrame != nsnull) { + nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame); + rv = lcf->RemoveOption(aPresContext, aIndex); } - // If we removed the selected option, nothing is selected any more. - // Restore selection to option 0 if there are options left. - MakeSureSomethingIsSelected(aPresContext); return rv; } diff --git a/mozilla/layout/forms/nsComboboxControlFrame.h b/mozilla/layout/forms/nsComboboxControlFrame.h index 0b2414106a6..0f28755755a 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.h +++ b/mozilla/layout/forms/nsComboboxControlFrame.h @@ -177,6 +177,7 @@ public: NS_IMETHOD GetOptionSelected(PRInt32 aIndex, PRBool* aValue); NS_IMETHOD DoneAddingContent(PRBool aIsDone); NS_IMETHOD OptionDisabled(nsIContent * aContent); + NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext); // Default to option 0 //nsIStatefulFrame NS_IMETHOD SaveState(nsIPresContext* aPresContext, nsIPresState** aState); @@ -232,7 +233,6 @@ protected: void InitTextStr(nsIPresContext* aPresContext, PRBool aUpdate); nsresult GetPrimaryComboFrame(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame** aFrame); NS_IMETHOD ToggleList(nsIPresContext* aPresContext); - NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext); // Default to option 0 void ReflowCombobox(nsIPresContext * aPresContext, const nsHTMLReflowState& aReflowState, diff --git a/mozilla/layout/forms/nsISelectControlFrame.h b/mozilla/layout/forms/nsISelectControlFrame.h index 55f2b25e7b1..0ea67706f16 100644 --- a/mozilla/layout/forms/nsISelectControlFrame.h +++ b/mozilla/layout/forms/nsISelectControlFrame.h @@ -72,6 +72,11 @@ public: NS_IMETHOD OptionDisabled(nsIContent * aContent) = 0; + /** + * This only applies to Comboboxes, no-op for ListBoxes + */ + NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext) = 0; + }; #endif diff --git a/mozilla/layout/forms/nsListControlFrame.cpp b/mozilla/layout/forms/nsListControlFrame.cpp index 37e3151a2f2..9a4edfb9ef0 100644 --- a/mozilla/layout/forms/nsListControlFrame.cpp +++ b/mozilla/layout/forms/nsListControlFrame.cpp @@ -62,7 +62,9 @@ #include "nsISelectElement.h" #include "nsIPrivateDOMEvent.h" -//static NS_DEFINE_IID(kBlockFrameCID, NS_BLOCK_FRAME_CID); +// Timer Includes +#include "nsITimer.h" +#include "nsITimerCallback.h" // Constants const nscoord kMaxDropDownRows = 20; // This matches the setting for 4.x browsers @@ -207,6 +209,160 @@ if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) { \ //-- Done with macros //------------------------------------------------------ +//--------------------------------------------------- +//-- Update Timer Stuff +//--------------------------------------------------- +class nsSelectUpdateTimer : public nsITimerCallback +{ +public: + + NS_DECL_ISUPPORTS + + nsSelectUpdateTimer() + : mPresContext(nsnull), mDelay(0), mHasBeenNotified(PR_FALSE), + mItemsAdded(PR_FALSE), mItemsRemoved(PR_FALSE), mItemsInxSet(PR_FALSE), + mRemovedSelectedIndex(PR_FALSE) + { + NS_INIT_ISUPPORTS(); + } + + virtual ~nsSelectUpdateTimer(); + + NS_IMETHOD_(void) Notify(nsITimer *timer); + + // Additional Methods + nsresult Start(nsIPresContext *aPresContext) + { + mPresContext = aPresContext; + //mHasBeenNotified = PR_FALSE; + + if (!mTimer) { + nsresult result; + mTimer = do_CreateInstance("@mozilla.org/timer;1", &result); + + if (NS_FAILED(result)) + return result; + + } + + if (mHasBeenNotified) { + mItemsAdded = PR_FALSE; + mItemsRemoved = PR_FALSE; + mItemsInxSet = PR_FALSE; + mHasBeenNotified = PR_FALSE; + mRemovedSelectedIndex = PR_FALSE; + mInxArray.Clear(); + } + + return mTimer->Init(this, mDelay); + } + + void Init(nsListControlFrame *aList, PRUint32 aDelay) { mListControl = aList; mDelay = aDelay; } + void Stop() { if (mTimer) mTimer->Cancel(); } + + void AdjustIndexes(PRBool aInserted, PRInt32 aInx) + { + // remove the index from the list + if (!aInserted) { + PRInt32 inx = (PRInt32)mInxArray.IndexOf((void*)aInx); + if (inx > -1) { + mInxArray.RemoveElementAt(inx); + } + } + + PRInt32 count = mInxArray.Count(); + for (PRInt32 i=0;i aInx) { + mInxArray.ReplaceElementAt((void*)(inx+(aInserted?1:-1)), i); + } + } + } + + void ItemAdded(PRInt32 aInx, PRInt32 aNumItems) + { + mItemsAdded = PR_TRUE; + if (mInxArray.Count() > 0 && aInx <= aNumItems-1) { + AdjustIndexes(PR_TRUE, aInx); + } + } + + void ItemRemoved(PRInt32 aInx, PRInt32 aNumItems) + { + mItemsRemoved = PR_TRUE; + if (mInxArray.Count() > 0 && aInx <= aNumItems) { + AdjustIndexes(PR_FALSE, aInx); + } + } + + void ItemIndexSet(PRInt32 aInx) + { + mItemsInxSet = PR_TRUE; + mInxArray.AppendElement((void*)aInx); + } + + PRPackedBool HasBeenNotified() { return mHasBeenNotified; } + void SetRemovedSelectedIndex() { mRemovedSelectedIndex = PR_TRUE; } + PRPackedBool RemovedSelectedIndex() { return mRemovedSelectedIndex; } + +private: + nsListControlFrame * mListControl; + nsCOMPtr mTimer; + nsIPresContext* mPresContext; + PRUint32 mDelay; + PRPackedBool mHasBeenNotified; + + PRPackedBool mItemsAdded; + PRPackedBool mItemsRemoved; + PRPackedBool mItemsInxSet; + PRPackedBool mRemovedSelectedIndex; + nsVoidArray mInxArray; + +}; + +NS_IMPL_ADDREF(nsSelectUpdateTimer) +NS_IMPL_RELEASE(nsSelectUpdateTimer) +NS_IMPL_QUERY_INTERFACE1(nsSelectUpdateTimer, nsITimerCallback) + +nsresult NS_NewUpdateTimer(nsSelectUpdateTimer **aResult) +{ + if (!aResult) + return NS_ERROR_NULL_POINTER; + + *aResult = (nsSelectUpdateTimer*) new nsSelectUpdateTimer; + + if (!aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + + return NS_OK; +} + +// nsITimerCallback +NS_IMETHODIMP_(void) nsSelectUpdateTimer::Notify(nsITimer *timer) +{ + if (mPresContext && mListControl && !mHasBeenNotified) { + mHasBeenNotified = PR_TRUE; + if (mItemsAdded || mItemsInxSet) { + mListControl->ResetList(mPresContext, &mInxArray); + } else { + mListControl->ItemsHaveBeenRemoved(mPresContext); + } + } +} + +nsSelectUpdateTimer::~nsSelectUpdateTimer() +{ + if (mTimer) { + mTimer->Cancel(); + } +} + +//--------------------------------------------------- +//-- DONE: Update Timer Stuff +//--------------------------------------------------- + //--------------------------------------------------------- nsListControlFrame::nsListControlFrame() @@ -225,9 +381,6 @@ nsListControlFrame::nsListControlFrame() mDelayedIndexSetting = kNothingSelected; mDelayedValueSetting = PR_FALSE; - mSelectionCache = new nsVoidArray(); - mSelectionCacheLength = 0; - mIsAllContentHere = PR_FALSE; mIsAllFramesHere = PR_FALSE; mHasBeenInitialized = PR_FALSE; @@ -245,6 +398,8 @@ nsListControlFrame::nsListControlFrame() mOverrideReflowOpt = PR_FALSE; mPassId = 0; + mUpdateTimer = nsnull; + REFLOW_COUNTER_INIT() } @@ -252,6 +407,10 @@ nsListControlFrame::nsListControlFrame() nsListControlFrame::~nsListControlFrame() { REFLOW_COUNTER_DUMP("nsLCF"); + if (mUpdateTimer != nsnull) { + StopUpdateTimer(); + delete mUpdateTimer; + } mComboboxFrame = nsnull; if (mFormFrame) { @@ -259,9 +418,6 @@ nsListControlFrame::~nsListControlFrame() mFormFrame = nsnull; } NS_IF_RELEASE(mPresContext); - if (mSelectionCache) { - delete mSelectionCache; - } } // for Bug 47302 (remove this comment later) @@ -531,7 +687,7 @@ nsListControlFrame::Reflow(nsIPresContext* aPresContext, } if (mIsAllFramesHere && !mHasBeenInitialized) { mHasBeenInitialized = PR_TRUE; - Reset(aPresContext); + ResetList(aPresContext); } } @@ -1068,41 +1224,16 @@ nsListControlFrame::ForceRedraw(nsIPresContext* aPresContext) void nsListControlFrame::DisplaySelected(nsIContent* aContent) { - //XXX: This is temporary. It simulates psuedo states by using a attribute selector on - // -moz-option-selected in the ua.css style sheet. This will not be needed when - // The event state manager supports selected states. KMM - nsAutoString attr; - nsresult rv = aContent->GetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, attr); - if (rv != NS_CONTENT_ATTR_NOT_THERE) - return; - - if (PR_TRUE == mIsAllFramesHere) { - aContent->SetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, nsAutoString(), PR_TRUE); - //ForceRedraw(); - } else { - aContent->SetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, nsAutoString(), PR_FALSE); - } + // ignore return value, not much we can do if it fails + aContent->SetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, nsAutoString(), mIsAllFramesHere); } //--------------------------------------------------------- void nsListControlFrame::DisplayDeselected(nsIContent* aContent) { - //XXX: This is temporary. It simulates psuedo states by using a attribute selector on - // -moz-option-selected in the ua.css style sheet. This will not be needed when - // The event state manager is functional. KMM - nsAutoString attr; - nsresult rv = aContent->GetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, attr); - if (rv == NS_CONTENT_ATTR_NOT_THERE) - return; - - if (PR_TRUE == mIsAllFramesHere) { - aContent->UnsetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, PR_TRUE); - //ForceRedraw(); - } else { - aContent->UnsetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, PR_FALSE); - } - + // ignore return value, not much we can do if it fails + aContent->UnsetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, mIsAllFramesHere); } @@ -1339,7 +1470,7 @@ nsListControlFrame::MultipleSelection(PRBool aIsShift, PRBool aIsControl) ClearSelection(); } } -#ifdef DEBUG_rods +#ifdef DEBUG_rodsX printf("mSelectedIndex: %d\n", mSelectedIndex); printf("mOldSelectedIndex: %d\n", mOldSelectedIndex); printf("mStartExtendedIndex: %d\n", mStartExtendedIndex); @@ -1555,7 +1686,7 @@ nsListControlFrame::SetInitialChildList(nsIPresContext* aPresContext, // If all content and frames are here // the reset/initialize if (CheckIfAllFramesHere()) { - Reset(aPresContext); + ResetList(aPresContext); mHasBeenInitialized = PR_TRUE; } }*/ @@ -1801,7 +1932,7 @@ nsListControlFrame::GetOptionValue(nsIDOMHTMLCollection& aCollection, return status; } - +static int cnt = 0; //--------------------------------------------------------- // For a given piece of content, it determines whether the // content (an option) is selected or not @@ -1837,7 +1968,10 @@ nsListControlFrame::IsContentSelectedByIndex(PRInt32 aIndex) // being selected or not selected //--------------------------------------------------------- void -nsListControlFrame::SetContentSelected(PRInt32 aIndex, PRBool aSelected) +nsListControlFrame::SetContentSelected(PRInt32 aIndex, + PRBool aSelected, + PRBool aDoScrollTo, + nsIPresShell * aPresShell) { if (aIndex == kNothingSelected) { return; @@ -1858,26 +1992,47 @@ nsListControlFrame::SetContentSelected(PRInt32 aIndex, PRBool aSelected) nsIContent* content = GetOptionContent(aIndex); NS_ASSERTION(content != nsnull, "Content should not be null!"); - if (content != nsnull) { - nsCOMPtr presShell; - mPresContext->GetShell(getter_AddRefs(presShell)); - if (presShell) { - nsIFrame * childframe; - nsresult result = presShell->GetPrimaryFrameFor(content, &childframe); - if (NS_SUCCEEDED(result) && childframe != nsnull) { - if (aSelected) { - DisplaySelected(content); - // Now that it is selected scroll to it - ScrollToFrame(content); + nsIPresShell* presShell; + if (aPresShell == nsnull) { + mPresContext->GetShell(&presShell); + } else { + presShell = aPresShell; + NS_ADDREF(presShell); + } + SetContentSelected(aIndex, content, aSelected, aDoScrollTo, presShell); + NS_RELEASE(content); + NS_RELEASE(presShell); +} + +void +nsListControlFrame::SetContentSelected(PRInt32 aIndex, + nsIContent * aContent, + PRBool aSelected, + PRBool aDoScrollTo, + nsIPresShell * aPresShell) +{ + if (aContent != nsnull) { + PRBool isSelected = IsContentSelected(aContent); + //if (aSelected != isSelected) { + if (aPresShell) { + nsIFrame * childframe; + nsresult result = aPresShell->GetPrimaryFrameFor(aContent, &childframe); + if (NS_SUCCEEDED(result) && childframe != nsnull) { + if (aSelected) { + DisplaySelected(aContent); + // Now that it is selected scroll to it + if (aDoScrollTo) { + ScrollToFrame(aContent); + } + } else { + DisplayDeselected(aContent); + } } else { - DisplayDeselected(content); + mDelayedIndexSetting = aIndex; + mDelayedValueSetting = aSelected; } - } else { - mDelayedIndexSetting = aIndex; - mDelayedValueSetting = aSelected; } - } - NS_RELEASE(content); + //} } } @@ -1888,6 +2043,9 @@ nsListControlFrame::SetContentSelected(PRInt32 aIndex, PRBool aSelected) nsresult nsListControlFrame::Deselect() { + + /* This code works fine but it + * isn't quite as efficient as the code below PRInt32 i; PRInt32 max = 0; if (NS_SUCCEEDED(GetNumberOfOptions(&max))) { @@ -1895,6 +2053,24 @@ nsListControlFrame::Deselect() SetContentSelected(i, PR_FALSE); } } + */ + + nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); + if (options) { + PRUint32 length = 0; + options->GetLength(&length); + PRUint32 i; + for (i=0;i node; + if (NS_SUCCEEDED(options->Item(i, getter_AddRefs(node)))) { + nsCOMPtr content(do_QueryInterface(node)); + if (content && IsContentSelected(content)) { + DisplayDeselected(content); + } + } + } + } + mSelectedIndex = kNothingSelected; return NS_OK; @@ -1966,9 +2142,10 @@ nsListControlFrame::GetMaxNumValues() // those values as determined by the original HTML //--------------------------------------------------------- void -nsListControlFrame::Reset(nsIPresContext* aPresContext) +nsListControlFrame::ResetList(nsIPresContext* aPresContext, nsVoidArray * aInxList) { - REFLOW_DEBUG_MSG("LBX::Reset\n"); + + REFLOW_DEBUG_MSG("LBX::ResetList\n"); // if all the frames aren't here // don't bother reseting @@ -1995,12 +2172,16 @@ nsListControlFrame::Reset(nsIPresContext* aPresContext) PRBool multiple; GetMultiple(&multiple); - Deselect(); - // Clear the cache and set the default selections - // if we havn't been restored - mSelectionCache->Clear(); - mSelectionCacheLength = 0; + // if we haven't been restored + nsCOMPtr presShell; + aPresContext->GetShell(getter_AddRefs(presShell)); + + // This code has been reworked to make it as efficient as possible. + // In the case, where it is a single select, we do not select any items + // while they are being reset, we wait until the end and set the selected + // item once. This also means that when it is a multiple select + // we set the selected item twice, but the cost at that point is very low. PRUint32 i; for (i = 0; i < numOptions; i++) { nsCOMPtr option = getter_AddRefs(GetOption(*options, i)); @@ -2009,31 +2190,39 @@ nsListControlFrame::Reset(nsIPresContext* aPresContext) if (!hasBeenRestored) { option->GetDefaultSelected(&selected); } + if (aInxList) { + selected = aInxList->IndexOf((void*)i) > -1; + } - mSelectionCache->AppendElement((void*)selected); - mSelectionCacheLength++; - + nsCOMPtr content(do_QueryInterface(option)); if (selected) { - if (mSelectedIndex == kNothingSelected || - (mSelectedIndex != kNothingSelected && multiple)) { - mSelectedIndex = i; - SetContentSelected(i, PR_TRUE); - if (multiple) { - mStartExtendedIndex = i; - if (mEndExtendedIndex == kNothingSelected) { - mEndExtendedIndex = i; - } + NS_ASSERTION(content, "There is no way this could be nsnull"); + + mSelectedIndex = i; + + if (multiple) { + SetContentSelected(i, content, PR_TRUE, PR_FALSE, presShell); + mStartExtendedIndex = i; + if (mEndExtendedIndex == kNothingSelected) { + mEndExtendedIndex = i; } - } else { - SetContentSelected(mSelectedIndex, PR_FALSE); - mSelectionCache->ReplaceElementAt((void*)PR_FALSE, mSelectedIndex); - mSelectedIndex = i; - SetContentSelected(i, PR_TRUE); } + } else if (IsContentSelected(content)){ + SetContentSelected(i, content, PR_FALSE, PR_FALSE, presShell); } } } + // To make the final adjustments + // set the selected item to true and scroll to it + PRUint32 indexToSelect; + if (multiple) { + indexToSelect = mStartExtendedIndex; + } else { + indexToSelect = mSelectedIndex; + } + SetContentSelected(indexToSelect, PR_TRUE, PR_TRUE, presShell); + // Ok, so we were restored, now set the last known selections from the restore state. if (hasBeenRestored) { nsCOMPtr supp; @@ -2060,7 +2249,6 @@ nsListControlFrame::Reset(nsIPresContext* aPresContext) if (NS_SUCCEEDED(res)) { mSelectedIndex = j; SetContentSelected(j, PR_TRUE);// might want to use ToggleSelection - mSelectionCache->ReplaceElementAt((void*)PR_TRUE, j); if (multiple) { mStartExtendedIndex = j; if (mEndExtendedIndex == kNothingSelected) { @@ -2376,7 +2564,7 @@ nsListControlFrame::DoneAddingContent(PRBool aIsDone) // if all the frames are now present we can initalize if (CheckIfAllFramesHere() && mPresContext) { mHasBeenInitialized = PR_TRUE; - Reset(mPresContext); + ResetList(mPresContext); } } } @@ -2387,10 +2575,15 @@ nsListControlFrame::DoneAddingContent(PRBool aIsDone) NS_IMETHODIMP nsListControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) { + StopUpdateTimer(); + #ifdef DO_REFLOW_DEBUG printf("---- Id: %d nsLCF %p Added Option %d\n", mReflowId, this, aIndex); #endif + PRInt32 numOptions; + GetNumberOfOptions(&numOptions); + if (!mIsAllContentHere) { nsCOMPtr element(do_QueryInterface(mContent)); if (element) { @@ -2399,8 +2592,6 @@ nsListControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) mIsAllFramesHere = PR_FALSE; mHasBeenInitialized = PR_FALSE; } else { - PRInt32 numOptions; - GetNumberOfOptions(&numOptions); mIsAllFramesHere = aIndex == numOptions-1; } } @@ -2410,56 +2601,10 @@ nsListControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) return NS_OK; } - PRInt32 oldSelection = mSelectedIndex; - - // Adding an option to the select can cause a change in selection - // if the new option has it's selected attribute set. - // this code checks to see if it does - // if so then it resets the entire selection of listbox - PRBool wasReset = PR_FALSE; - nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); - if (options) { - nsCOMPtr option = getter_AddRefs(GetOption(*options, aIndex)); - if (option) { - PRBool selected = PR_FALSE; - option->GetDefaultSelected(&selected); - - mSelectionCache->InsertElementAt((void*)selected, aIndex); - mSelectionCacheLength++; - - if (selected) { - Reset(aPresContext); // this sets mSelectedIndex to the defaulted selection - wasReset = PR_TRUE; - } - -#ifdef DEBUG_rods - { - nsAutoString text; - text.AssignWithConversion("No Value"); - nsresult rv = option->GetLabel(text); - if (NS_CONTENT_ATTR_NOT_THERE == rv || 0 == text.Length()) { - option->GetText(text); - } - printf("|||||this %p Index: %d [%s] CB: %p\n", this, aIndex, text.ToNewCString(), mComboboxFrame); //leaks - } -#endif - } + nsresult rv = StartUpdateTimer(aPresContext); + if (NS_SUCCEEDED(rv) && mUpdateTimer != nsnull) { + mUpdateTimer->ItemAdded(aIndex, numOptions); } - - if (!wasReset) { - GetSelectedIndexFromDOM(&mSelectedIndex); // comes from the DOM - } - - // if selection changed because of the new option being added - // notify the combox if necessary - if (mComboboxFrame != nsnull) { - if (mSelectedIndex == kNothingSelected) { - mComboboxFrame->MakeSureSomethingIsSelected(mPresContext); - } else if (oldSelection != mSelectedIndex) { - mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event - } - } - return NS_OK; } @@ -2467,25 +2612,29 @@ nsListControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) NS_IMETHODIMP nsListControlFrame::RemoveOption(nsIPresContext* aPresContext, PRInt32 aIndex) { - PRInt32 numOptions; - GetNumberOfOptions(&numOptions); - -// PRInt32 oldSelectedIndex = mSelectedIndex; - GetSelectedIndexFromDOM(&mSelectedIndex); // comes from the DOM - - // Select the new selectedIndex - // Don't need to deselect option as it is being removed anyway. - if (mSelectedIndex >= 0) { - SetContentSelected(mSelectedIndex, PR_TRUE); + StopUpdateTimer(); + nsresult rv = StartUpdateTimer(aPresContext); + if (NS_SUCCEEDED(rv) && mUpdateTimer != nsnull) { + PRInt32 numOptions; + GetNumberOfOptions(&numOptions); + mUpdateTimer->ItemRemoved(aIndex, numOptions); } - mSelectionCache->RemoveElementAt(aIndex); - mSelectionCacheLength--; + // Check to see which index is being removed + // if the current index is being removed, remember that + // if the index is less then the current index + // then decrement the current selected index + if (aIndex == mSelectedIndex) { + mUpdateTimer->SetRemovedSelectedIndex(); + mSelectedIndex = kNothingSelected; + + } if (aIndex < mSelectedIndex) { + mSelectedIndex--; + } return NS_OK; } -#ifdef FIX_FOR_BUG_50376 //------------------------------------------------------------------- nsresult nsListControlFrame::GetPresStateAndValueArray(nsISupportsArray ** aSuppArray) { @@ -2596,8 +2745,6 @@ nsresult nsListControlFrame::SetOptionIntoPresState(nsISupportsArray * aSuppArra return res; } -#endif // FIX_FOR_BUG_50376 - //--------------------------------------------------------- // Select the specified item in the listbox using control logic. // If it a single selection listbox the previous selection will be @@ -2605,11 +2752,9 @@ nsresult nsListControlFrame::SetOptionIntoPresState(nsISupportsArray * aSuppArra NS_IMETHODIMP nsListControlFrame::SetOptionSelected(PRInt32 aIndex, PRBool aValue) { -#ifdef FIX_FOR_BUG_50376 if (!mIsAllFramesHere && !mHasBeenInitialized) { return SetSelectionInPresState(aIndex, aValue); } -#endif // FIX_FOR_BUG_50376 PRBool multiple; nsresult rv = GetMultiple(&multiple); @@ -2646,49 +2791,16 @@ nsListControlFrame::UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate nsresult rv = NS_OK; PRBool changed = PR_FALSE; - // Paranoia: check if cache is up to date with content - PRInt32 length = 0; - GetNumberOfOptions(&length); - if (mSelectionCacheLength != length) { - //NS_ASSERTION(0,"nsListControlFrame: Cache sync'd with content!\n"); - changed = PR_TRUE; // Assume the worst, there was a change. + + PRBool isDroppedDown = PR_FALSE; + if (mComboboxFrame != nsnull) { + mComboboxFrame->IsDroppedDown(&isDroppedDown); + } + if (aDoDispatchEvent && !isDroppedDown) { + rv = SelectionChanged(aContent); // Dispatch event } - // Step through content looking for change in selection - if (NS_SUCCEEDED(rv)) { - if (!changed) { - PRBool selected; - // the content array of options is actually - // out of sync with the array - // so until bug 38825 is fixed. - if (mSelectionCacheLength != length) { // this shouldn't happend - for (PRInt32 i = 0; i < length; i++) { - selected = IsContentSelectedByIndex(i); - if (selected != (PRBool)mSelectionCache->ElementAt(i)) { - mSelectionCache->ReplaceElementAt((void*)selected, i); - changed = PR_TRUE; - } - } - } else { - mSelectionCache->Clear(); - for (PRInt32 i = 0; i < length; i++) { - selected = IsContentSelectedByIndex(i); - mSelectionCache->InsertElementAt((void*)selected, i); - changed = PR_TRUE; - } - } - } - - PRBool isDroppedDown = PR_FALSE; - if (mComboboxFrame != nsnull) { - mComboboxFrame->IsDroppedDown(&isDroppedDown); - } - if (changed && aDoDispatchEvent && !isDroppedDown) { - rv = SelectionChanged(aContent); // Dispatch event - } - } - - if ((changed || aForceUpdate) && mComboboxFrame) { + if (aForceUpdate && mComboboxFrame) { rv = mComboboxFrame->SelectionChanged(); // Update view } return rv; @@ -2779,6 +2891,17 @@ nsListControlFrame::SetProperty(nsIPresContext* aPresContext, nsIAtom* aName, if (NS_FAILED(GetMultiple(&multiple, selectElement))) { multiple = PR_FALSE; } + if (mUpdateTimer != nsnull) { + if (!mUpdateTimer->HasBeenNotified()) { + StopUpdateTimer(); + ToggleSelected(selectedIndex); // sets mSelectedIndex + nsresult rv = StartUpdateTimer(aPresContext); + if (NS_SUCCEEDED(rv) && mUpdateTimer != nsnull) { + mUpdateTimer->ItemIndexSet(selectedIndex); + } + return NS_OK; + } + } // if it is a multiple, select the new item if (multiple) { Deselect(); @@ -2991,7 +3114,7 @@ nsListControlFrame::AboutToRollup() ResetSelectedItem(); } else { if (IsInDropDownMode() == PR_TRUE) { - mComboboxFrame->ListWasSelected(mPresContext, PR_TRUE, PR_TRUE); + mComboboxFrame->ListWasSelected(mPresContext, PR_FALSE, PR_TRUE); } } } @@ -3251,7 +3374,7 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) SetContentSelected(mSelectedIndex, PR_TRUE); } if (mComboboxFrame) { - mComboboxFrame->ListWasSelected(mPresContext, PR_FALSE, PR_TRUE); + mComboboxFrame->ListWasSelected(mPresContext, PR_TRUE, PR_TRUE); } mouseEvent->clickCount = 1; } else { @@ -3335,11 +3458,9 @@ nsListControlFrame::GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent, if (optionContent) { aOldIndex = aCurIndex; aCurIndex = GetSelectedIndexFromContent(optionContent); - //printf("--> Old: %d New: %d\n", aOldIndex, aCurIndex); rv = NS_OK; } } - //printf("--> bailing\n"); return rv; } @@ -4024,6 +4145,7 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) //---------------------------------------------------------------------- // nsIStatefulFrame //---------------------------------------------------------------------- +//---------------------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::SaveStateInternal(nsIPresContext* aPresContext, nsIPresState** aState) { @@ -4062,18 +4184,7 @@ nsListControlFrame::SaveStateInternal(nsIPresContext* aPresContext, nsIPresState PRInt32 j = 0; for (i = 0; i < numOptions; i++) { if (IsContentSelectedByIndex(i)) { -#ifdef FIX_FOR_BUG_50376 res = SetOptionIntoPresState(value, i, j++); -#else - nsCOMPtr thisVal(do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID)); - NS_ENSURE_TRUE(thisVal, res); - - res = thisVal->SetData(i); - NS_ENSURE_SUCCEEDED(res, res); - - PRBool okay = value->InsertElementAt((nsISupports *)thisVal, j++); - NS_ENSURE_TRUE(okay, NS_ERROR_OUT_OF_MEMORY); -#endif } } @@ -4107,7 +4218,7 @@ nsListControlFrame::RestoreStateInternal(nsIPresContext* aPresContext, mPresState = aState; if (mHasBeenInitialized) { // Already called Reset, call again to update selection - Reset(aPresContext); + ResetList(aPresContext); } return NS_OK; } @@ -4352,3 +4463,45 @@ nsListEventListener::DragMove(nsIDOMEvent* aMouseEvent) return NS_OK; } +/*=============== Timer Related Code ======================*/ +nsresult +nsListControlFrame::StartUpdateTimer(nsIPresContext * aPresContext) +{ + + if (mUpdateTimer == nsnull) { + nsresult result = NS_NewUpdateTimer(&mUpdateTimer); + if (NS_FAILED(result)) + return result; + + mUpdateTimer->Init(this, 0); // delay "0" + } + + if (mUpdateTimer != nsnull) { + return mUpdateTimer->Start(aPresContext); + } + + return NS_ERROR_FAILURE; +} + +inline void +nsListControlFrame::StopUpdateTimer() +{ + if (mUpdateTimer != nsnull) { + mUpdateTimer->Stop(); + } +} + +void +nsListControlFrame::ItemsHaveBeenRemoved(nsIPresContext * aPresContext) +{ + // Only adjust things if it is a combobox + // removing items on a listbox should effect anything + if (IsInDropDownMode()) { + // if items were removed ahead of the selected item the + // selected index is moved down + // if the selected item was removed then we should reset the list + if (mUpdateTimer->RemovedSelectedIndex()) { + ResetList(aPresContext); + } + } +} diff --git a/mozilla/layout/forms/nsListControlFrame.h b/mozilla/layout/forms/nsListControlFrame.h index 2f8748fe687..d6a965775fd 100644 --- a/mozilla/layout/forms/nsListControlFrame.h +++ b/mozilla/layout/forms/nsListControlFrame.h @@ -55,6 +55,8 @@ class nsVoidArray; class nsIScrollableView; class nsListControlFrame; +class nsSelectUpdateTimer; +class nsVoidArray; #define NS_ILIST_EVENT_LISTENER_IID \ {/* 45BC6821-6EFB-11d4-B1EE-000064657374*/ \ @@ -152,8 +154,6 @@ protected: /** * Frame-based listbox. */ -#define FIX_FOR_BUG_50376 - class nsListControlFrame : public nsScrollFrame, public nsIFormControlFrame, @@ -165,6 +165,7 @@ class nsListControlFrame : public nsScrollFrame, { public: friend nsresult NS_NewListControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame); + friend class nsSelectUpdateTimer; // nsISupports NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); @@ -220,7 +221,7 @@ public: virtual void SetFocus(PRBool aOn = PR_TRUE, PRBool aRepaint = PR_FALSE); virtual void ScrollIntoView(nsIPresContext* aPresContext); virtual void MouseClicked(nsIPresContext* aPresContext); - virtual void Reset(nsIPresContext* aPresContext); + virtual void Reset(nsIPresContext* aPresContext) { ResetList(aPresContext); } virtual PRBool IsSuccessful(nsIFormControlFrame* aSubmitter); virtual PRInt32 GetMaxNumValues(); virtual PRBool GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, @@ -264,6 +265,7 @@ public: NS_IMETHOD GetOptionSelected(PRInt32 aIndex, PRBool* aValue); NS_IMETHOD DoneAddingContent(PRBool aIsDone); NS_IMETHOD OptionDisabled(nsIContent * aContent); + NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext) { return NS_OK; } //nsIStatefulFrame NS_IMETHOD SaveState(nsIPresContext* aPresContext, nsIPresState** aState); @@ -311,7 +313,8 @@ protected: void AdjustIndexForDisabledOpt(PRInt32 &anNewIndex, PRInt32 &anOldIndex, PRBool &aDoSetNewIndex, PRBool &aWasDisabled, PRInt32 aNumOptions, PRInt32 aDoAdjustInc, PRInt32 aDoAdjustIncNext); -#ifdef FIX_FOR_BUG_50376 + virtual void ResetList(nsIPresContext* aPresContext, nsVoidArray * aInxList = nsnull); + // PresState Helper Methods nsresult GetPresStateAndValueArray(nsISupportsArray ** aSuppArray); nsresult SetOptionIntoPresState(nsISupportsArray * aSuppArray, @@ -320,7 +323,6 @@ protected: nsresult SetSelectionInPresState(PRInt32 aIndex, PRBool aValue); nsresult RemoveOptionFromPresState(nsISupportsArray * aSuppArray, PRInt32 aIndex); -#endif nsListControlFrame(); virtual ~nsListControlFrame(); @@ -343,7 +345,15 @@ protected: nsIContent* GetOptionContent(PRInt32 aIndex); PRBool IsContentSelected(nsIContent* aContent); PRBool IsContentSelectedByIndex(PRInt32 aIndex); - void SetContentSelected(PRInt32 aIndex, PRBool aSelected); + void SetContentSelected(PRInt32 aIndex, + PRBool aSelected, + PRBool aDoScrollTo = PR_TRUE, + nsIPresShell * aPresShell = nsnull); + void SetContentSelected(PRInt32 aIndex, + nsIContent * aContent, + PRBool aSelected, + PRBool aDoScrollTo = PR_TRUE, + nsIPresShell * aPresShell = nsnull); void GetViewOffset(nsIViewManager* aManager, nsIView* aView, nsPoint& aPoint); nsresult Deselect(); nsIFrame *GetOptionFromChild(nsIFrame* aParentFrame); @@ -373,6 +383,11 @@ protected: void GetScrollableView(nsIScrollableView*& aScrollableView); + // Timer Methods + nsresult StartUpdateTimer(nsIPresContext * aPresContext); + void StopUpdateTimer(); + void ItemsHaveBeenRemoved(nsIPresContext * aPresContext); + // onChange detection nsresult SelectionChanged(nsIContent* aContent); @@ -417,6 +432,9 @@ protected: PRInt16 mPassId; nsSize mCachedDesiredMaxSize; + // Update timer + nsSelectUpdateTimer * mUpdateTimer; + //Resize Reflow OpitmizationSize; nsSize mCacheSize; nsSize mCachedMaxElementSize; diff --git a/mozilla/layout/html/forms/public/nsISelectControlFrame.h b/mozilla/layout/html/forms/public/nsISelectControlFrame.h index 55f2b25e7b1..0ea67706f16 100644 --- a/mozilla/layout/html/forms/public/nsISelectControlFrame.h +++ b/mozilla/layout/html/forms/public/nsISelectControlFrame.h @@ -72,6 +72,11 @@ public: NS_IMETHOD OptionDisabled(nsIContent * aContent) = 0; + /** + * This only applies to Comboboxes, no-op for ListBoxes + */ + NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext) = 0; + }; #endif diff --git a/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp b/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp index f1008bfe19c..4d5513310f9 100644 --- a/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp @@ -2030,24 +2030,14 @@ nsComboboxControlFrame::DoneAddingContent(PRBool aIsDone) NS_IMETHODIMP nsComboboxControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) { -#ifdef DO_REFLOW_DEBUG - printf("**********\n*********AddOption: %d\n", aIndex); +#ifdef DO_REFLOW_DEBUGXX + printf("*********AddOption: %d\n", aIndex); #endif - nsISelectControlFrame* listFrame = nsnull; - nsresult rv = mDropdownFrame->QueryInterface(NS_GET_IID(nsISelectControlFrame), - (void**)&listFrame); - if (NS_SUCCEEDED(rv) && listFrame) { - rv = listFrame->AddOption(aPresContext, aIndex); - //PRInt32 index; - //mListControlFrame->GetSelectedIndex(&index); - //UpdateSelection(PR_FALSE, PR_TRUE, index); - NS_RELEASE(listFrame); + nsresult rv = NS_ERROR_FAILURE; + if (mDropdownFrame) { + nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame); + rv = lcf->AddOption(aPresContext, aIndex); } - // If we added the first option, we might need to select it. - // We should call MakeSureSomethingIsSelected here, but since it - // it changes selection, which currently causes a reframe, and thus - // deletes the frame out from under the caller, causing a crash. (Bug 17995) - // XXX MakeSureSomethingIsSelected(aPresContext); return rv; } @@ -2055,16 +2045,11 @@ nsComboboxControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) NS_IMETHODIMP nsComboboxControlFrame::RemoveOption(nsIPresContext* aPresContext, PRInt32 aIndex) { - nsISelectControlFrame* listFrame = nsnull; - nsresult rv = mDropdownFrame->QueryInterface(NS_GET_IID(nsISelectControlFrame), - (void**)&listFrame); - if (NS_SUCCEEDED(rv) && listFrame) { - rv = listFrame->RemoveOption(aPresContext, aIndex); - NS_RELEASE(listFrame); + nsresult rv = NS_ERROR_FAILURE; + if (mDropdownFrame != nsnull) { + nsListControlFrame * lcf = NS_STATIC_CAST(nsListControlFrame*, mDropdownFrame); + rv = lcf->RemoveOption(aPresContext, aIndex); } - // If we removed the selected option, nothing is selected any more. - // Restore selection to option 0 if there are options left. - MakeSureSomethingIsSelected(aPresContext); return rv; } diff --git a/mozilla/layout/html/forms/src/nsComboboxControlFrame.h b/mozilla/layout/html/forms/src/nsComboboxControlFrame.h index 0b2414106a6..0f28755755a 100644 --- a/mozilla/layout/html/forms/src/nsComboboxControlFrame.h +++ b/mozilla/layout/html/forms/src/nsComboboxControlFrame.h @@ -177,6 +177,7 @@ public: NS_IMETHOD GetOptionSelected(PRInt32 aIndex, PRBool* aValue); NS_IMETHOD DoneAddingContent(PRBool aIsDone); NS_IMETHOD OptionDisabled(nsIContent * aContent); + NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext); // Default to option 0 //nsIStatefulFrame NS_IMETHOD SaveState(nsIPresContext* aPresContext, nsIPresState** aState); @@ -232,7 +233,6 @@ protected: void InitTextStr(nsIPresContext* aPresContext, PRBool aUpdate); nsresult GetPrimaryComboFrame(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame** aFrame); NS_IMETHOD ToggleList(nsIPresContext* aPresContext); - NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext); // Default to option 0 void ReflowCombobox(nsIPresContext * aPresContext, const nsHTMLReflowState& aReflowState, diff --git a/mozilla/layout/html/forms/src/nsListControlFrame.cpp b/mozilla/layout/html/forms/src/nsListControlFrame.cpp index 37e3151a2f2..9a4edfb9ef0 100644 --- a/mozilla/layout/html/forms/src/nsListControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsListControlFrame.cpp @@ -62,7 +62,9 @@ #include "nsISelectElement.h" #include "nsIPrivateDOMEvent.h" -//static NS_DEFINE_IID(kBlockFrameCID, NS_BLOCK_FRAME_CID); +// Timer Includes +#include "nsITimer.h" +#include "nsITimerCallback.h" // Constants const nscoord kMaxDropDownRows = 20; // This matches the setting for 4.x browsers @@ -207,6 +209,160 @@ if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) { \ //-- Done with macros //------------------------------------------------------ +//--------------------------------------------------- +//-- Update Timer Stuff +//--------------------------------------------------- +class nsSelectUpdateTimer : public nsITimerCallback +{ +public: + + NS_DECL_ISUPPORTS + + nsSelectUpdateTimer() + : mPresContext(nsnull), mDelay(0), mHasBeenNotified(PR_FALSE), + mItemsAdded(PR_FALSE), mItemsRemoved(PR_FALSE), mItemsInxSet(PR_FALSE), + mRemovedSelectedIndex(PR_FALSE) + { + NS_INIT_ISUPPORTS(); + } + + virtual ~nsSelectUpdateTimer(); + + NS_IMETHOD_(void) Notify(nsITimer *timer); + + // Additional Methods + nsresult Start(nsIPresContext *aPresContext) + { + mPresContext = aPresContext; + //mHasBeenNotified = PR_FALSE; + + if (!mTimer) { + nsresult result; + mTimer = do_CreateInstance("@mozilla.org/timer;1", &result); + + if (NS_FAILED(result)) + return result; + + } + + if (mHasBeenNotified) { + mItemsAdded = PR_FALSE; + mItemsRemoved = PR_FALSE; + mItemsInxSet = PR_FALSE; + mHasBeenNotified = PR_FALSE; + mRemovedSelectedIndex = PR_FALSE; + mInxArray.Clear(); + } + + return mTimer->Init(this, mDelay); + } + + void Init(nsListControlFrame *aList, PRUint32 aDelay) { mListControl = aList; mDelay = aDelay; } + void Stop() { if (mTimer) mTimer->Cancel(); } + + void AdjustIndexes(PRBool aInserted, PRInt32 aInx) + { + // remove the index from the list + if (!aInserted) { + PRInt32 inx = (PRInt32)mInxArray.IndexOf((void*)aInx); + if (inx > -1) { + mInxArray.RemoveElementAt(inx); + } + } + + PRInt32 count = mInxArray.Count(); + for (PRInt32 i=0;i aInx) { + mInxArray.ReplaceElementAt((void*)(inx+(aInserted?1:-1)), i); + } + } + } + + void ItemAdded(PRInt32 aInx, PRInt32 aNumItems) + { + mItemsAdded = PR_TRUE; + if (mInxArray.Count() > 0 && aInx <= aNumItems-1) { + AdjustIndexes(PR_TRUE, aInx); + } + } + + void ItemRemoved(PRInt32 aInx, PRInt32 aNumItems) + { + mItemsRemoved = PR_TRUE; + if (mInxArray.Count() > 0 && aInx <= aNumItems) { + AdjustIndexes(PR_FALSE, aInx); + } + } + + void ItemIndexSet(PRInt32 aInx) + { + mItemsInxSet = PR_TRUE; + mInxArray.AppendElement((void*)aInx); + } + + PRPackedBool HasBeenNotified() { return mHasBeenNotified; } + void SetRemovedSelectedIndex() { mRemovedSelectedIndex = PR_TRUE; } + PRPackedBool RemovedSelectedIndex() { return mRemovedSelectedIndex; } + +private: + nsListControlFrame * mListControl; + nsCOMPtr mTimer; + nsIPresContext* mPresContext; + PRUint32 mDelay; + PRPackedBool mHasBeenNotified; + + PRPackedBool mItemsAdded; + PRPackedBool mItemsRemoved; + PRPackedBool mItemsInxSet; + PRPackedBool mRemovedSelectedIndex; + nsVoidArray mInxArray; + +}; + +NS_IMPL_ADDREF(nsSelectUpdateTimer) +NS_IMPL_RELEASE(nsSelectUpdateTimer) +NS_IMPL_QUERY_INTERFACE1(nsSelectUpdateTimer, nsITimerCallback) + +nsresult NS_NewUpdateTimer(nsSelectUpdateTimer **aResult) +{ + if (!aResult) + return NS_ERROR_NULL_POINTER; + + *aResult = (nsSelectUpdateTimer*) new nsSelectUpdateTimer; + + if (!aResult) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*aResult); + + return NS_OK; +} + +// nsITimerCallback +NS_IMETHODIMP_(void) nsSelectUpdateTimer::Notify(nsITimer *timer) +{ + if (mPresContext && mListControl && !mHasBeenNotified) { + mHasBeenNotified = PR_TRUE; + if (mItemsAdded || mItemsInxSet) { + mListControl->ResetList(mPresContext, &mInxArray); + } else { + mListControl->ItemsHaveBeenRemoved(mPresContext); + } + } +} + +nsSelectUpdateTimer::~nsSelectUpdateTimer() +{ + if (mTimer) { + mTimer->Cancel(); + } +} + +//--------------------------------------------------- +//-- DONE: Update Timer Stuff +//--------------------------------------------------- + //--------------------------------------------------------- nsListControlFrame::nsListControlFrame() @@ -225,9 +381,6 @@ nsListControlFrame::nsListControlFrame() mDelayedIndexSetting = kNothingSelected; mDelayedValueSetting = PR_FALSE; - mSelectionCache = new nsVoidArray(); - mSelectionCacheLength = 0; - mIsAllContentHere = PR_FALSE; mIsAllFramesHere = PR_FALSE; mHasBeenInitialized = PR_FALSE; @@ -245,6 +398,8 @@ nsListControlFrame::nsListControlFrame() mOverrideReflowOpt = PR_FALSE; mPassId = 0; + mUpdateTimer = nsnull; + REFLOW_COUNTER_INIT() } @@ -252,6 +407,10 @@ nsListControlFrame::nsListControlFrame() nsListControlFrame::~nsListControlFrame() { REFLOW_COUNTER_DUMP("nsLCF"); + if (mUpdateTimer != nsnull) { + StopUpdateTimer(); + delete mUpdateTimer; + } mComboboxFrame = nsnull; if (mFormFrame) { @@ -259,9 +418,6 @@ nsListControlFrame::~nsListControlFrame() mFormFrame = nsnull; } NS_IF_RELEASE(mPresContext); - if (mSelectionCache) { - delete mSelectionCache; - } } // for Bug 47302 (remove this comment later) @@ -531,7 +687,7 @@ nsListControlFrame::Reflow(nsIPresContext* aPresContext, } if (mIsAllFramesHere && !mHasBeenInitialized) { mHasBeenInitialized = PR_TRUE; - Reset(aPresContext); + ResetList(aPresContext); } } @@ -1068,41 +1224,16 @@ nsListControlFrame::ForceRedraw(nsIPresContext* aPresContext) void nsListControlFrame::DisplaySelected(nsIContent* aContent) { - //XXX: This is temporary. It simulates psuedo states by using a attribute selector on - // -moz-option-selected in the ua.css style sheet. This will not be needed when - // The event state manager supports selected states. KMM - nsAutoString attr; - nsresult rv = aContent->GetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, attr); - if (rv != NS_CONTENT_ATTR_NOT_THERE) - return; - - if (PR_TRUE == mIsAllFramesHere) { - aContent->SetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, nsAutoString(), PR_TRUE); - //ForceRedraw(); - } else { - aContent->SetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, nsAutoString(), PR_FALSE); - } + // ignore return value, not much we can do if it fails + aContent->SetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, nsAutoString(), mIsAllFramesHere); } //--------------------------------------------------------- void nsListControlFrame::DisplayDeselected(nsIContent* aContent) { - //XXX: This is temporary. It simulates psuedo states by using a attribute selector on - // -moz-option-selected in the ua.css style sheet. This will not be needed when - // The event state manager is functional. KMM - nsAutoString attr; - nsresult rv = aContent->GetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, attr); - if (rv == NS_CONTENT_ATTR_NOT_THERE) - return; - - if (PR_TRUE == mIsAllFramesHere) { - aContent->UnsetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, PR_TRUE); - //ForceRedraw(); - } else { - aContent->UnsetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, PR_FALSE); - } - + // ignore return value, not much we can do if it fails + aContent->UnsetAttribute(kNameSpaceID_None, nsLayoutAtoms::optionSelectedPseudo, mIsAllFramesHere); } @@ -1339,7 +1470,7 @@ nsListControlFrame::MultipleSelection(PRBool aIsShift, PRBool aIsControl) ClearSelection(); } } -#ifdef DEBUG_rods +#ifdef DEBUG_rodsX printf("mSelectedIndex: %d\n", mSelectedIndex); printf("mOldSelectedIndex: %d\n", mOldSelectedIndex); printf("mStartExtendedIndex: %d\n", mStartExtendedIndex); @@ -1555,7 +1686,7 @@ nsListControlFrame::SetInitialChildList(nsIPresContext* aPresContext, // If all content and frames are here // the reset/initialize if (CheckIfAllFramesHere()) { - Reset(aPresContext); + ResetList(aPresContext); mHasBeenInitialized = PR_TRUE; } }*/ @@ -1801,7 +1932,7 @@ nsListControlFrame::GetOptionValue(nsIDOMHTMLCollection& aCollection, return status; } - +static int cnt = 0; //--------------------------------------------------------- // For a given piece of content, it determines whether the // content (an option) is selected or not @@ -1837,7 +1968,10 @@ nsListControlFrame::IsContentSelectedByIndex(PRInt32 aIndex) // being selected or not selected //--------------------------------------------------------- void -nsListControlFrame::SetContentSelected(PRInt32 aIndex, PRBool aSelected) +nsListControlFrame::SetContentSelected(PRInt32 aIndex, + PRBool aSelected, + PRBool aDoScrollTo, + nsIPresShell * aPresShell) { if (aIndex == kNothingSelected) { return; @@ -1858,26 +1992,47 @@ nsListControlFrame::SetContentSelected(PRInt32 aIndex, PRBool aSelected) nsIContent* content = GetOptionContent(aIndex); NS_ASSERTION(content != nsnull, "Content should not be null!"); - if (content != nsnull) { - nsCOMPtr presShell; - mPresContext->GetShell(getter_AddRefs(presShell)); - if (presShell) { - nsIFrame * childframe; - nsresult result = presShell->GetPrimaryFrameFor(content, &childframe); - if (NS_SUCCEEDED(result) && childframe != nsnull) { - if (aSelected) { - DisplaySelected(content); - // Now that it is selected scroll to it - ScrollToFrame(content); + nsIPresShell* presShell; + if (aPresShell == nsnull) { + mPresContext->GetShell(&presShell); + } else { + presShell = aPresShell; + NS_ADDREF(presShell); + } + SetContentSelected(aIndex, content, aSelected, aDoScrollTo, presShell); + NS_RELEASE(content); + NS_RELEASE(presShell); +} + +void +nsListControlFrame::SetContentSelected(PRInt32 aIndex, + nsIContent * aContent, + PRBool aSelected, + PRBool aDoScrollTo, + nsIPresShell * aPresShell) +{ + if (aContent != nsnull) { + PRBool isSelected = IsContentSelected(aContent); + //if (aSelected != isSelected) { + if (aPresShell) { + nsIFrame * childframe; + nsresult result = aPresShell->GetPrimaryFrameFor(aContent, &childframe); + if (NS_SUCCEEDED(result) && childframe != nsnull) { + if (aSelected) { + DisplaySelected(aContent); + // Now that it is selected scroll to it + if (aDoScrollTo) { + ScrollToFrame(aContent); + } + } else { + DisplayDeselected(aContent); + } } else { - DisplayDeselected(content); + mDelayedIndexSetting = aIndex; + mDelayedValueSetting = aSelected; } - } else { - mDelayedIndexSetting = aIndex; - mDelayedValueSetting = aSelected; } - } - NS_RELEASE(content); + //} } } @@ -1888,6 +2043,9 @@ nsListControlFrame::SetContentSelected(PRInt32 aIndex, PRBool aSelected) nsresult nsListControlFrame::Deselect() { + + /* This code works fine but it + * isn't quite as efficient as the code below PRInt32 i; PRInt32 max = 0; if (NS_SUCCEEDED(GetNumberOfOptions(&max))) { @@ -1895,6 +2053,24 @@ nsListControlFrame::Deselect() SetContentSelected(i, PR_FALSE); } } + */ + + nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); + if (options) { + PRUint32 length = 0; + options->GetLength(&length); + PRUint32 i; + for (i=0;i node; + if (NS_SUCCEEDED(options->Item(i, getter_AddRefs(node)))) { + nsCOMPtr content(do_QueryInterface(node)); + if (content && IsContentSelected(content)) { + DisplayDeselected(content); + } + } + } + } + mSelectedIndex = kNothingSelected; return NS_OK; @@ -1966,9 +2142,10 @@ nsListControlFrame::GetMaxNumValues() // those values as determined by the original HTML //--------------------------------------------------------- void -nsListControlFrame::Reset(nsIPresContext* aPresContext) +nsListControlFrame::ResetList(nsIPresContext* aPresContext, nsVoidArray * aInxList) { - REFLOW_DEBUG_MSG("LBX::Reset\n"); + + REFLOW_DEBUG_MSG("LBX::ResetList\n"); // if all the frames aren't here // don't bother reseting @@ -1995,12 +2172,16 @@ nsListControlFrame::Reset(nsIPresContext* aPresContext) PRBool multiple; GetMultiple(&multiple); - Deselect(); - // Clear the cache and set the default selections - // if we havn't been restored - mSelectionCache->Clear(); - mSelectionCacheLength = 0; + // if we haven't been restored + nsCOMPtr presShell; + aPresContext->GetShell(getter_AddRefs(presShell)); + + // This code has been reworked to make it as efficient as possible. + // In the case, where it is a single select, we do not select any items + // while they are being reset, we wait until the end and set the selected + // item once. This also means that when it is a multiple select + // we set the selected item twice, but the cost at that point is very low. PRUint32 i; for (i = 0; i < numOptions; i++) { nsCOMPtr option = getter_AddRefs(GetOption(*options, i)); @@ -2009,31 +2190,39 @@ nsListControlFrame::Reset(nsIPresContext* aPresContext) if (!hasBeenRestored) { option->GetDefaultSelected(&selected); } + if (aInxList) { + selected = aInxList->IndexOf((void*)i) > -1; + } - mSelectionCache->AppendElement((void*)selected); - mSelectionCacheLength++; - + nsCOMPtr content(do_QueryInterface(option)); if (selected) { - if (mSelectedIndex == kNothingSelected || - (mSelectedIndex != kNothingSelected && multiple)) { - mSelectedIndex = i; - SetContentSelected(i, PR_TRUE); - if (multiple) { - mStartExtendedIndex = i; - if (mEndExtendedIndex == kNothingSelected) { - mEndExtendedIndex = i; - } + NS_ASSERTION(content, "There is no way this could be nsnull"); + + mSelectedIndex = i; + + if (multiple) { + SetContentSelected(i, content, PR_TRUE, PR_FALSE, presShell); + mStartExtendedIndex = i; + if (mEndExtendedIndex == kNothingSelected) { + mEndExtendedIndex = i; } - } else { - SetContentSelected(mSelectedIndex, PR_FALSE); - mSelectionCache->ReplaceElementAt((void*)PR_FALSE, mSelectedIndex); - mSelectedIndex = i; - SetContentSelected(i, PR_TRUE); } + } else if (IsContentSelected(content)){ + SetContentSelected(i, content, PR_FALSE, PR_FALSE, presShell); } } } + // To make the final adjustments + // set the selected item to true and scroll to it + PRUint32 indexToSelect; + if (multiple) { + indexToSelect = mStartExtendedIndex; + } else { + indexToSelect = mSelectedIndex; + } + SetContentSelected(indexToSelect, PR_TRUE, PR_TRUE, presShell); + // Ok, so we were restored, now set the last known selections from the restore state. if (hasBeenRestored) { nsCOMPtr supp; @@ -2060,7 +2249,6 @@ nsListControlFrame::Reset(nsIPresContext* aPresContext) if (NS_SUCCEEDED(res)) { mSelectedIndex = j; SetContentSelected(j, PR_TRUE);// might want to use ToggleSelection - mSelectionCache->ReplaceElementAt((void*)PR_TRUE, j); if (multiple) { mStartExtendedIndex = j; if (mEndExtendedIndex == kNothingSelected) { @@ -2376,7 +2564,7 @@ nsListControlFrame::DoneAddingContent(PRBool aIsDone) // if all the frames are now present we can initalize if (CheckIfAllFramesHere() && mPresContext) { mHasBeenInitialized = PR_TRUE; - Reset(mPresContext); + ResetList(mPresContext); } } } @@ -2387,10 +2575,15 @@ nsListControlFrame::DoneAddingContent(PRBool aIsDone) NS_IMETHODIMP nsListControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) { + StopUpdateTimer(); + #ifdef DO_REFLOW_DEBUG printf("---- Id: %d nsLCF %p Added Option %d\n", mReflowId, this, aIndex); #endif + PRInt32 numOptions; + GetNumberOfOptions(&numOptions); + if (!mIsAllContentHere) { nsCOMPtr element(do_QueryInterface(mContent)); if (element) { @@ -2399,8 +2592,6 @@ nsListControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) mIsAllFramesHere = PR_FALSE; mHasBeenInitialized = PR_FALSE; } else { - PRInt32 numOptions; - GetNumberOfOptions(&numOptions); mIsAllFramesHere = aIndex == numOptions-1; } } @@ -2410,56 +2601,10 @@ nsListControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) return NS_OK; } - PRInt32 oldSelection = mSelectedIndex; - - // Adding an option to the select can cause a change in selection - // if the new option has it's selected attribute set. - // this code checks to see if it does - // if so then it resets the entire selection of listbox - PRBool wasReset = PR_FALSE; - nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); - if (options) { - nsCOMPtr option = getter_AddRefs(GetOption(*options, aIndex)); - if (option) { - PRBool selected = PR_FALSE; - option->GetDefaultSelected(&selected); - - mSelectionCache->InsertElementAt((void*)selected, aIndex); - mSelectionCacheLength++; - - if (selected) { - Reset(aPresContext); // this sets mSelectedIndex to the defaulted selection - wasReset = PR_TRUE; - } - -#ifdef DEBUG_rods - { - nsAutoString text; - text.AssignWithConversion("No Value"); - nsresult rv = option->GetLabel(text); - if (NS_CONTENT_ATTR_NOT_THERE == rv || 0 == text.Length()) { - option->GetText(text); - } - printf("|||||this %p Index: %d [%s] CB: %p\n", this, aIndex, text.ToNewCString(), mComboboxFrame); //leaks - } -#endif - } + nsresult rv = StartUpdateTimer(aPresContext); + if (NS_SUCCEEDED(rv) && mUpdateTimer != nsnull) { + mUpdateTimer->ItemAdded(aIndex, numOptions); } - - if (!wasReset) { - GetSelectedIndexFromDOM(&mSelectedIndex); // comes from the DOM - } - - // if selection changed because of the new option being added - // notify the combox if necessary - if (mComboboxFrame != nsnull) { - if (mSelectedIndex == kNothingSelected) { - mComboboxFrame->MakeSureSomethingIsSelected(mPresContext); - } else if (oldSelection != mSelectedIndex) { - mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event - } - } - return NS_OK; } @@ -2467,25 +2612,29 @@ nsListControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) NS_IMETHODIMP nsListControlFrame::RemoveOption(nsIPresContext* aPresContext, PRInt32 aIndex) { - PRInt32 numOptions; - GetNumberOfOptions(&numOptions); - -// PRInt32 oldSelectedIndex = mSelectedIndex; - GetSelectedIndexFromDOM(&mSelectedIndex); // comes from the DOM - - // Select the new selectedIndex - // Don't need to deselect option as it is being removed anyway. - if (mSelectedIndex >= 0) { - SetContentSelected(mSelectedIndex, PR_TRUE); + StopUpdateTimer(); + nsresult rv = StartUpdateTimer(aPresContext); + if (NS_SUCCEEDED(rv) && mUpdateTimer != nsnull) { + PRInt32 numOptions; + GetNumberOfOptions(&numOptions); + mUpdateTimer->ItemRemoved(aIndex, numOptions); } - mSelectionCache->RemoveElementAt(aIndex); - mSelectionCacheLength--; + // Check to see which index is being removed + // if the current index is being removed, remember that + // if the index is less then the current index + // then decrement the current selected index + if (aIndex == mSelectedIndex) { + mUpdateTimer->SetRemovedSelectedIndex(); + mSelectedIndex = kNothingSelected; + + } if (aIndex < mSelectedIndex) { + mSelectedIndex--; + } return NS_OK; } -#ifdef FIX_FOR_BUG_50376 //------------------------------------------------------------------- nsresult nsListControlFrame::GetPresStateAndValueArray(nsISupportsArray ** aSuppArray) { @@ -2596,8 +2745,6 @@ nsresult nsListControlFrame::SetOptionIntoPresState(nsISupportsArray * aSuppArra return res; } -#endif // FIX_FOR_BUG_50376 - //--------------------------------------------------------- // Select the specified item in the listbox using control logic. // If it a single selection listbox the previous selection will be @@ -2605,11 +2752,9 @@ nsresult nsListControlFrame::SetOptionIntoPresState(nsISupportsArray * aSuppArra NS_IMETHODIMP nsListControlFrame::SetOptionSelected(PRInt32 aIndex, PRBool aValue) { -#ifdef FIX_FOR_BUG_50376 if (!mIsAllFramesHere && !mHasBeenInitialized) { return SetSelectionInPresState(aIndex, aValue); } -#endif // FIX_FOR_BUG_50376 PRBool multiple; nsresult rv = GetMultiple(&multiple); @@ -2646,49 +2791,16 @@ nsListControlFrame::UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate nsresult rv = NS_OK; PRBool changed = PR_FALSE; - // Paranoia: check if cache is up to date with content - PRInt32 length = 0; - GetNumberOfOptions(&length); - if (mSelectionCacheLength != length) { - //NS_ASSERTION(0,"nsListControlFrame: Cache sync'd with content!\n"); - changed = PR_TRUE; // Assume the worst, there was a change. + + PRBool isDroppedDown = PR_FALSE; + if (mComboboxFrame != nsnull) { + mComboboxFrame->IsDroppedDown(&isDroppedDown); + } + if (aDoDispatchEvent && !isDroppedDown) { + rv = SelectionChanged(aContent); // Dispatch event } - // Step through content looking for change in selection - if (NS_SUCCEEDED(rv)) { - if (!changed) { - PRBool selected; - // the content array of options is actually - // out of sync with the array - // so until bug 38825 is fixed. - if (mSelectionCacheLength != length) { // this shouldn't happend - for (PRInt32 i = 0; i < length; i++) { - selected = IsContentSelectedByIndex(i); - if (selected != (PRBool)mSelectionCache->ElementAt(i)) { - mSelectionCache->ReplaceElementAt((void*)selected, i); - changed = PR_TRUE; - } - } - } else { - mSelectionCache->Clear(); - for (PRInt32 i = 0; i < length; i++) { - selected = IsContentSelectedByIndex(i); - mSelectionCache->InsertElementAt((void*)selected, i); - changed = PR_TRUE; - } - } - } - - PRBool isDroppedDown = PR_FALSE; - if (mComboboxFrame != nsnull) { - mComboboxFrame->IsDroppedDown(&isDroppedDown); - } - if (changed && aDoDispatchEvent && !isDroppedDown) { - rv = SelectionChanged(aContent); // Dispatch event - } - } - - if ((changed || aForceUpdate) && mComboboxFrame) { + if (aForceUpdate && mComboboxFrame) { rv = mComboboxFrame->SelectionChanged(); // Update view } return rv; @@ -2779,6 +2891,17 @@ nsListControlFrame::SetProperty(nsIPresContext* aPresContext, nsIAtom* aName, if (NS_FAILED(GetMultiple(&multiple, selectElement))) { multiple = PR_FALSE; } + if (mUpdateTimer != nsnull) { + if (!mUpdateTimer->HasBeenNotified()) { + StopUpdateTimer(); + ToggleSelected(selectedIndex); // sets mSelectedIndex + nsresult rv = StartUpdateTimer(aPresContext); + if (NS_SUCCEEDED(rv) && mUpdateTimer != nsnull) { + mUpdateTimer->ItemIndexSet(selectedIndex); + } + return NS_OK; + } + } // if it is a multiple, select the new item if (multiple) { Deselect(); @@ -2991,7 +3114,7 @@ nsListControlFrame::AboutToRollup() ResetSelectedItem(); } else { if (IsInDropDownMode() == PR_TRUE) { - mComboboxFrame->ListWasSelected(mPresContext, PR_TRUE, PR_TRUE); + mComboboxFrame->ListWasSelected(mPresContext, PR_FALSE, PR_TRUE); } } } @@ -3251,7 +3374,7 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) SetContentSelected(mSelectedIndex, PR_TRUE); } if (mComboboxFrame) { - mComboboxFrame->ListWasSelected(mPresContext, PR_FALSE, PR_TRUE); + mComboboxFrame->ListWasSelected(mPresContext, PR_TRUE, PR_TRUE); } mouseEvent->clickCount = 1; } else { @@ -3335,11 +3458,9 @@ nsListControlFrame::GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent, if (optionContent) { aOldIndex = aCurIndex; aCurIndex = GetSelectedIndexFromContent(optionContent); - //printf("--> Old: %d New: %d\n", aOldIndex, aCurIndex); rv = NS_OK; } } - //printf("--> bailing\n"); return rv; } @@ -4024,6 +4145,7 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent) //---------------------------------------------------------------------- // nsIStatefulFrame //---------------------------------------------------------------------- +//---------------------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::SaveStateInternal(nsIPresContext* aPresContext, nsIPresState** aState) { @@ -4062,18 +4184,7 @@ nsListControlFrame::SaveStateInternal(nsIPresContext* aPresContext, nsIPresState PRInt32 j = 0; for (i = 0; i < numOptions; i++) { if (IsContentSelectedByIndex(i)) { -#ifdef FIX_FOR_BUG_50376 res = SetOptionIntoPresState(value, i, j++); -#else - nsCOMPtr thisVal(do_CreateInstance(NS_SUPPORTS_PRINT32_CONTRACTID)); - NS_ENSURE_TRUE(thisVal, res); - - res = thisVal->SetData(i); - NS_ENSURE_SUCCEEDED(res, res); - - PRBool okay = value->InsertElementAt((nsISupports *)thisVal, j++); - NS_ENSURE_TRUE(okay, NS_ERROR_OUT_OF_MEMORY); -#endif } } @@ -4107,7 +4218,7 @@ nsListControlFrame::RestoreStateInternal(nsIPresContext* aPresContext, mPresState = aState; if (mHasBeenInitialized) { // Already called Reset, call again to update selection - Reset(aPresContext); + ResetList(aPresContext); } return NS_OK; } @@ -4352,3 +4463,45 @@ nsListEventListener::DragMove(nsIDOMEvent* aMouseEvent) return NS_OK; } +/*=============== Timer Related Code ======================*/ +nsresult +nsListControlFrame::StartUpdateTimer(nsIPresContext * aPresContext) +{ + + if (mUpdateTimer == nsnull) { + nsresult result = NS_NewUpdateTimer(&mUpdateTimer); + if (NS_FAILED(result)) + return result; + + mUpdateTimer->Init(this, 0); // delay "0" + } + + if (mUpdateTimer != nsnull) { + return mUpdateTimer->Start(aPresContext); + } + + return NS_ERROR_FAILURE; +} + +inline void +nsListControlFrame::StopUpdateTimer() +{ + if (mUpdateTimer != nsnull) { + mUpdateTimer->Stop(); + } +} + +void +nsListControlFrame::ItemsHaveBeenRemoved(nsIPresContext * aPresContext) +{ + // Only adjust things if it is a combobox + // removing items on a listbox should effect anything + if (IsInDropDownMode()) { + // if items were removed ahead of the selected item the + // selected index is moved down + // if the selected item was removed then we should reset the list + if (mUpdateTimer->RemovedSelectedIndex()) { + ResetList(aPresContext); + } + } +} diff --git a/mozilla/layout/html/forms/src/nsListControlFrame.h b/mozilla/layout/html/forms/src/nsListControlFrame.h index 2f8748fe687..d6a965775fd 100644 --- a/mozilla/layout/html/forms/src/nsListControlFrame.h +++ b/mozilla/layout/html/forms/src/nsListControlFrame.h @@ -55,6 +55,8 @@ class nsVoidArray; class nsIScrollableView; class nsListControlFrame; +class nsSelectUpdateTimer; +class nsVoidArray; #define NS_ILIST_EVENT_LISTENER_IID \ {/* 45BC6821-6EFB-11d4-B1EE-000064657374*/ \ @@ -152,8 +154,6 @@ protected: /** * Frame-based listbox. */ -#define FIX_FOR_BUG_50376 - class nsListControlFrame : public nsScrollFrame, public nsIFormControlFrame, @@ -165,6 +165,7 @@ class nsListControlFrame : public nsScrollFrame, { public: friend nsresult NS_NewListControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame); + friend class nsSelectUpdateTimer; // nsISupports NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); @@ -220,7 +221,7 @@ public: virtual void SetFocus(PRBool aOn = PR_TRUE, PRBool aRepaint = PR_FALSE); virtual void ScrollIntoView(nsIPresContext* aPresContext); virtual void MouseClicked(nsIPresContext* aPresContext); - virtual void Reset(nsIPresContext* aPresContext); + virtual void Reset(nsIPresContext* aPresContext) { ResetList(aPresContext); } virtual PRBool IsSuccessful(nsIFormControlFrame* aSubmitter); virtual PRInt32 GetMaxNumValues(); virtual PRBool GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, @@ -264,6 +265,7 @@ public: NS_IMETHOD GetOptionSelected(PRInt32 aIndex, PRBool* aValue); NS_IMETHOD DoneAddingContent(PRBool aIsDone); NS_IMETHOD OptionDisabled(nsIContent * aContent); + NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext) { return NS_OK; } //nsIStatefulFrame NS_IMETHOD SaveState(nsIPresContext* aPresContext, nsIPresState** aState); @@ -311,7 +313,8 @@ protected: void AdjustIndexForDisabledOpt(PRInt32 &anNewIndex, PRInt32 &anOldIndex, PRBool &aDoSetNewIndex, PRBool &aWasDisabled, PRInt32 aNumOptions, PRInt32 aDoAdjustInc, PRInt32 aDoAdjustIncNext); -#ifdef FIX_FOR_BUG_50376 + virtual void ResetList(nsIPresContext* aPresContext, nsVoidArray * aInxList = nsnull); + // PresState Helper Methods nsresult GetPresStateAndValueArray(nsISupportsArray ** aSuppArray); nsresult SetOptionIntoPresState(nsISupportsArray * aSuppArray, @@ -320,7 +323,6 @@ protected: nsresult SetSelectionInPresState(PRInt32 aIndex, PRBool aValue); nsresult RemoveOptionFromPresState(nsISupportsArray * aSuppArray, PRInt32 aIndex); -#endif nsListControlFrame(); virtual ~nsListControlFrame(); @@ -343,7 +345,15 @@ protected: nsIContent* GetOptionContent(PRInt32 aIndex); PRBool IsContentSelected(nsIContent* aContent); PRBool IsContentSelectedByIndex(PRInt32 aIndex); - void SetContentSelected(PRInt32 aIndex, PRBool aSelected); + void SetContentSelected(PRInt32 aIndex, + PRBool aSelected, + PRBool aDoScrollTo = PR_TRUE, + nsIPresShell * aPresShell = nsnull); + void SetContentSelected(PRInt32 aIndex, + nsIContent * aContent, + PRBool aSelected, + PRBool aDoScrollTo = PR_TRUE, + nsIPresShell * aPresShell = nsnull); void GetViewOffset(nsIViewManager* aManager, nsIView* aView, nsPoint& aPoint); nsresult Deselect(); nsIFrame *GetOptionFromChild(nsIFrame* aParentFrame); @@ -373,6 +383,11 @@ protected: void GetScrollableView(nsIScrollableView*& aScrollableView); + // Timer Methods + nsresult StartUpdateTimer(nsIPresContext * aPresContext); + void StopUpdateTimer(); + void ItemsHaveBeenRemoved(nsIPresContext * aPresContext); + // onChange detection nsresult SelectionChanged(nsIContent* aContent); @@ -417,6 +432,9 @@ protected: PRInt16 mPassId; nsSize mCachedDesiredMaxSize; + // Update timer + nsSelectUpdateTimer * mUpdateTimer; + //Resize Reflow OpitmizationSize; nsSize mCacheSize; nsSize mCachedMaxElementSize;