diff --git a/mozilla/layout/forms/nsComboboxControlFrame.cpp b/mozilla/layout/forms/nsComboboxControlFrame.cpp index 56dedbe5143..b2eefeff432 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.cpp +++ b/mozilla/layout/forms/nsComboboxControlFrame.cpp @@ -22,6 +22,7 @@ */ #include "nsCOMPtr.h" #include "nsComboboxControlFrame.h" +#include "nsIFrameManager.h" #include "nsFormFrame.h" #include "nsFormControlFrame.h" #include "nsIHTMLContent.h" @@ -32,11 +33,11 @@ #include "nsINameSpaceManager.h" #include "nsLayoutAtoms.h" #include "nsIDOMElement.h" -#include "nsListControlFrame.h" #include "nsIListControlFrame.h" #include "nsIDOMHTMLCollection.h" #include "nsIDOMHTMLSelectElement.h" #include "nsIDOMHTMLOptionElement.h" +#include "nsIDOMNSHTMLOptionCollection.h" #include "nsIPresShell.h" #include "nsIPresState.h" #include "nsISupportsArray.h" @@ -51,6 +52,16 @@ #include "nsISelectControlFrame.h" #include "nsISupportsPrimitives.h" #include "nsIComponentManager.h" +#include "nsIDOMMouseListener.h" +#include "nsITextContent.h" +#include "nsTextFragment.h" +#include "nsCSSFrameConstructor.h" +#include "nsIDocument.h" + +#ifdef DO_NEW_REFLOW +#include "nsIFontMetrics.h" +#endif + static NS_DEFINE_IID(kIFormControlFrameIID, NS_IFORMCONTROLFRAME_IID); static NS_DEFINE_IID(kIComboboxControlFrameIID, NS_ICOMBOBOXCONTROLFRAME_IID); @@ -62,8 +73,7 @@ static NS_DEFINE_IID(kIDOMNodeIID, NS_IDOMNODE_IID); static NS_DEFINE_IID(kIPrivateDOMEventIID, NS_IPRIVATEDOMEVENT_IID); // Drop down list event management. -// The combo box uses the following strategy for managing the -// drop-down list. +// The combo box uses the following strategy for managing the drop-down list. // If the combo box or it's arrow button is clicked on the drop-down list is displayed // If mouse exit's the combo box with the drop-down list displayed the drop-down list // is asked to capture events @@ -78,6 +88,8 @@ static NS_DEFINE_IID(kIPrivateDOMEventIID, NS_IPRIVATEDOMEVENT_IID); const char * kMozDropdownActive = "-moz-dropdown-active"; +const PRInt32 kSizeNotSet = -1; + nsresult NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aStateFlags) { @@ -98,6 +110,113 @@ NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUin return NS_OK; } +//----------------------------------------------------------- +// Reflow Debugging Macros +// These let us "see" how many reflow counts are happening +//----------------------------------------------------------- +#ifdef DO_REFLOW_COUNTER + +#define MAX_REFLOW_CNT 1024 +static PRInt32 gTotalReqs = 0;; +static PRInt32 gTotalReflows = 0;; +static PRInt32 gReflowControlCntRQ[MAX_REFLOW_CNT]; +static PRInt32 gReflowControlCnt[MAX_REFLOW_CNT]; +static PRInt32 gReflowInx = -1; + +#define REFLOW_COUNTER() \ + if (mReflowId > -1) \ + gReflowControlCnt[mReflowId]++; + +#define REFLOW_COUNTER_REQUEST() \ + if (mReflowId > -1) \ + gReflowControlCntRQ[mReflowId]++; + +#define REFLOW_COUNTER_DUMP(__desc) \ + if (mReflowId > -1) {\ + gTotalReqs += gReflowControlCntRQ[mReflowId];\ + gTotalReflows += gReflowControlCnt[mReflowId];\ + printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", \ + mReflowId, (__desc), \ + gReflowControlCnt[mReflowId], \ + gReflowControlCntRQ[mReflowId],\ + gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\ + } + +#define REFLOW_COUNTER_INIT() \ + if (gReflowInx < MAX_REFLOW_CNT) { \ + gReflowInx++; \ + mReflowId = gReflowInx; \ + gReflowControlCnt[mReflowId] = 0; \ + gReflowControlCntRQ[mReflowId] = 0; \ + } else { \ + mReflowId = -1; \ + } + +// reflow messages +#define REFLOW_DEBUG_MSG(_msg1) printf((_msg1)) +#define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) +#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) +#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) + +#else //------------- + +#define REFLOW_COUNTER_REQUEST() +#define REFLOW_COUNTER() +#define REFLOW_COUNTER_DUMP(__desc) +#define REFLOW_COUNTER_INIT() + +#define REFLOW_DEBUG_MSG(_msg) +#define REFLOW_DEBUG_MSG2(_msg1, _msg2) +#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) +#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) + + +#endif + +//------------------------------------------ +// This is for being VERY noisy +//------------------------------------------ +#ifdef DO_VERY_NOISY +#define REFLOW_NOISY_MSG(_msg1) printf((_msg1)) +#define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) +#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) +#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) +#else +#define REFLOW_NOISY_MSG(_msg) +#define REFLOW_NOISY_MSG2(_msg1, _msg2) +#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) +#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) +#endif + +//------------------------------------------ +// Displays value in pixels or twips +//------------------------------------------ +#ifdef DO_PIXELS +#define PX(__v) __v / 15 +#else +#define PX(__v) __v +#endif + +//------------------------------------------ +// Asserts if we return a desired size that +// doesn't correctly match the mComputedWidth +//------------------------------------------ +#ifdef DO_UNCONSTRAINED_CHECK +#define UNCONSTRAINED_CHECK() \ +if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) { \ + nscoord width = aDesiredSize.width - borderPadding.left - borderPadding.right; \ + if (width != aReflowState.mComputedWidth) { \ + printf("aDesiredSize.width %d %d != aReflowState.mComputedWidth %d\n", aDesiredSize.width, width, aReflowState.mComputedWidth); \ + } \ + NS_ASSERTION(width == aReflowState.mComputedWidth, "Returning bad value when constrained!"); \ +} +#else +#define UNCONSTRAINED_CHECK() +#endif +//------------------------------------------------------ +//-- Done with macros +//------------------------------------------------------ + nsComboboxControlFrame::nsComboboxControlFrame() : nsAreaFrame() { @@ -105,7 +224,6 @@ nsComboboxControlFrame::nsComboboxControlFrame() mFormFrame = nsnull; mListControlFrame = nsnull; mTextStr = ""; - mDisplayContent = nsnull; mButtonContent = nsnull; mDroppedDown = PR_FALSE; mDisplayFrame = nsnull; @@ -114,23 +232,33 @@ nsComboboxControlFrame::nsComboboxControlFrame() mIgnoreFocus = PR_FALSE; mSelectedIndex = -1; - mCacheSize.width = -1; - mCacheSize.height = -1; - mCachedMaxElementSize.width = -1; - mCachedMaxElementSize.height = -1; + mCacheSize.width = kSizeNotSet; + mCacheSize.height = kSizeNotSet; + mCachedMaxElementSize.width = kSizeNotSet; + mCachedMaxElementSize.height = kSizeNotSet; + mCachedAvailableSize.width = kSizeNotSet; + mCachedAvailableSize.height = kSizeNotSet; + mCachedUncDropdownSize.width = kSizeNotSet; + mCachedUncDropdownSize.height = kSizeNotSet; + mCachedUncComboSize.width = kSizeNotSet; + mCachedUncComboSize.height = kSizeNotSet; + mItemDisplayWidth = 0; //Shrink the area around it's contents //SetFlags(NS_BLOCK_SHRINK_WRAP); + + REFLOW_COUNTER_INIT() } //-------------------------------------------------------------- nsComboboxControlFrame::~nsComboboxControlFrame() { + REFLOW_COUNTER_DUMP("nsCCF"); + if (mFormFrame) { mFormFrame->RemoveFormControlFrame(*this); mFormFrame = nsnull; } NS_IF_RELEASE(mPresContext); - NS_IF_RELEASE(mDisplayContent); NS_IF_RELEASE(mButtonContent); nsFormControlFrame::RegUnRegAccessKey(mPresContext, NS_STATIC_CAST(nsIFrame*, this), PR_FALSE); @@ -211,8 +339,9 @@ nsComboboxControlFrame::MakeSureSomethingIsSelected(nsIPresContext* aPresContext // Set listbox selection to first item in the list box rv = fcFrame->SetProperty(aPresContext, nsHTMLAtoms::selectedindex, "0"); mSelectedIndex = 0; + } else { + UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // Needed to reflow when removing last option } - UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // Needed to reflow when removing last option } // Don't NS_RELEASE fcFrame here as it isn't addRef'd in the QI (???) @@ -231,9 +360,7 @@ nsComboboxControlFrame::InitTextStr(nsIPresContext* aPresContext, PRBool aUpdate mListControlFrame->GetSelectedItem(mTextStr); // Update the display by setting the value attribute - mDisplayContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, mTextStr, aUpdate); - //nsIFrame* buttonFrame = GetDisplayFrame(aPresContext); - //nsFormControlHelper::ForceDrawFrame(buttonFrame); + mDisplayContent->SetText(mTextStr.GetUnicode(), mTextStr.Length(), aUpdate); } //-------------------------------------------------------------- @@ -427,12 +554,12 @@ nsComboboxControlFrame::MouseClicked(nsIPresContext* aPresContext) nsresult nsComboboxControlFrame::ReflowComboChildFrame(nsIFrame* aFrame, - nsIPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus, - nscoord aAvailableWidth, - nscoord aAvailableHeight) + nsIPresContext* aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus, + nscoord aAvailableWidth, + nscoord aAvailableHeight) { // Constrain the child's width and height to aAvailableWidth and aAvailableHeight nsSize availSize(aAvailableWidth, aAvailableHeight); @@ -490,17 +617,6 @@ nsComboboxControlFrame::GetButtonFrame(nsIPresContext* aPresContext) return mButtonFrame; } -nsIFrame* -nsComboboxControlFrame::GetDisplayFrame(nsIPresContext* aPresContext) -{ - if (mDisplayFrame == nsnull) { - NS_ASSERTION(mDisplayContent != nsnull, "nsComboboxControlFrame mDisplayContent is null"); - GetPrimaryComboFrame(aPresContext, mDisplayContent, &mDisplayFrame); - } - - return mDisplayFrame; -} - nsIFrame* nsComboboxControlFrame::GetDropdownFrame() { @@ -578,12 +694,10 @@ nsComboboxControlFrame::PositionDropdown(nsIPresContext* aPresContext, nsRect currentRect; dropdownFrame->GetRect(currentRect); - //if (currentRect != dropdownRect) { - dropdownFrame->SetRect(aPresContext, dropdownRect); + dropdownFrame->SetRect(aPresContext, dropdownRect); #ifdef DEBUG_rodsXXXXXX - printf("%d Position Dropdown at: %d %d %d %d\n", counter++, dropdownRect.x, dropdownRect.y, dropdownRect.width, dropdownRect.height); + printf("%d Position Dropdown at: %d %d %d %d\n", counter++, dropdownRect.x, dropdownRect.y, dropdownRect.width, dropdownRect.height); #endif - //} return rv; } @@ -667,55 +781,435 @@ nsComboboxControlFrame::GetAbsoluteFramePosition(nsIPresContext* aPresContext, return rv; } -//------------------------------------------------------------------------------- -//------------------------------------------------------------------------------- -// Old/Current Way of Doing Reflow -//------------------------------------------------------------------------------- -//------------------------------------------------------------------------------- -#define DO_OLD_REFLOW +//////////////////////////////////////////////////////////////// +// Experimental Reflow +//////////////////////////////////////////////////////////////// +#if defined(DO_NEW_REFLOW) || defined(DO_REFLOW_COUNTER) +//--------------------------------------------------------- +// Returns the nsIDOMHTMLOptionElement for a given index +// in the select's collection +//--------------------------------------------------------- +static nsIDOMHTMLOptionElement* +GetOption(nsIDOMHTMLCollection& aCollection, PRInt32 aIndex) +{ + nsIDOMNode* node = nsnull; + if (NS_SUCCEEDED(aCollection.Item(aIndex, &node))) { + if (nsnull != node) { + nsIDOMHTMLOptionElement* option = nsnull; + node->QueryInterface(NS_GET_IID(nsIDOMHTMLOptionElement), (void**)&option); + NS_RELEASE(node); + return option; + } + } + return nsnull; +} +//--------------------------------------------------------- +// for a given piece of content it returns nsIDOMHTMLSelectElement object +// or null +//--------------------------------------------------------- +static nsIDOMHTMLSelectElement* +GetSelect(nsIContent * aContent) +{ + nsIDOMHTMLSelectElement* selectElement = nsnull; + nsresult result = aContent->QueryInterface(NS_GET_IID(nsIDOMHTMLSelectElement), + (void**)&selectElement); + if (NS_SUCCEEDED(result) && selectElement) { + return selectElement; + } else { + return nsnull; + } +} +//--------------------------------------------------------- +//--------------------------------------------------------- +// This returns the collection for nsIDOMHTMLSelectElement or +// the nsIContent object is the select is null (AddRefs) +//--------------------------------------------------------- +static nsIDOMHTMLCollection* +GetOptions(nsIContent * aContent, nsIDOMHTMLSelectElement* aSelect = nsnull) +{ + nsIDOMNSHTMLOptionCollection* optCol = nsnull; + nsIDOMHTMLCollection* options = nsnull; + if (!aSelect) { + nsCOMPtr selectElement = getter_AddRefs(GetSelect(aContent)); + if (selectElement) { + selectElement->GetOptions(&optCol); // AddRefs (1) + } + } else { + aSelect->GetOptions(&optCol); // AddRefs (1) + } + if (optCol) { + nsresult res = optCol->QueryInterface(NS_GET_IID(nsIDOMHTMLCollection), (void **)&options); // AddRefs (2) + NS_RELEASE(optCol); // Release (1) + } + return options; +} -#ifdef DO_OLD_REFLOW +#ifdef DO_NEW_REFLOW +NS_IMETHODIMP +nsComboboxControlFrame::ReflowItems(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize) +{ + //printf("*****************\n"); + const nsStyleFont* dspFont; + mDisplayFrame->GetStyleData(eStyleStruct_Font, (const nsStyleStruct *&)dspFont); + nsCOMPtr deviceContext; + aPresContext->GetDeviceContext(getter_AddRefs(deviceContext)); + NS_ASSERTION(deviceContext, "Couldn't get the device context"); + nsIFontMetrics * fontMet; + deviceContext->GetMetricsFor(dspFont->mFont, fontMet); -#ifdef DEBUG_rodsXXX -static int myCounter = 0; + nscoord visibleHeight; + //nsCOMPtr fontMet; + //nsresult res = nsFormControlHelper::GetFrameFontFM(aPresContext, this, getter_AddRefs(fontMet)); + if (fontMet) { + fontMet->GetHeight(visibleHeight); + } + + nsAutoString maxStr; + nscoord maxWidth = 0; + //nsIRenderingContext * rc = aReflowState.rendContext; + nsresult rv = NS_ERROR_FAILURE; + nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); + if (options) { + PRUint32 numOptions; + options->GetLength(&numOptions); + //printf("--- Num of Items %d ---\n", numOptions); + for (PRUint32 i=0;i optionElement = getter_AddRefs(GetOption(*options, i)); + if (optionElement) { + nsAutoString text; + rv = optionElement->GetLabel(text); + if (NS_CONTENT_ATTR_HAS_VALUE != rv || 0 == text.Length()) { + if (NS_OK == optionElement->GetText(text)) { + nscoord width; + aReflowState.rendContext->GetWidth(text, width); + if (width > maxWidth) { + maxStr = text; + maxWidth = width; + } + //maxWidth = PR_MAX(width, maxWidth); + //printf("[%d] - %d %s \n", i, width, text.ToNewCString()); + } + } + } + } + } + if (maxWidth == 0) { + maxWidth = 11 * 15; + } + char * str = maxStr.ToNewCString(); + printf("id: %d maxWidth %d [%s]\n", mReflowId, maxWidth, str); + delete [] str; + + // get the borderPadding for the display area + const nsStyleSpacing* dspSpacing; + mDisplayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); + nsMargin dspBorderPadding; + dspBorderPadding.SizeTo(0, 0, 0, 0); + dspSpacing->CalcBorderPaddingFor(mDisplayFrame, dspBorderPadding); + + nscoord frmWidth = maxWidth+dspBorderPadding.left+dspBorderPadding.right+ + aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; + nscoord frmHeight = visibleHeight+dspBorderPadding.top+dspBorderPadding.bottom+ + aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; + +#if 1 + aDesiredSize.width = frmWidth; + aDesiredSize.height = frmHeight; +#else + printf("Size %d,%d %d,%d %d,%d %d,%d\n", + frmWidth, frmHeight, + aDesiredSize.width, aDesiredSize.height, + frmWidth-aDesiredSize.width, frmHeight-aDesiredSize.height, + (frmWidth-aDesiredSize.width)/15, (frmHeight-aDesiredSize.height)/15); +#endif + NS_RELEASE(fontMet); + return NS_OK; +} #endif +#endif + +//------------------------------------------------------------------ +// This Method reflow just the contents of the ComboBox +// The contents are a Block frame containing a Text Frame - This is the display area +// and then the GfxButton - The dropdown button +//-------------------------------------------------------------------------- +void +nsComboboxControlFrame::ReflowCombobox(nsIPresContext * aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize, + nsReflowStatus& aStatus, + nsIFrame * aDisplayFrame, + nsIFrame * aDropDownBtn, + nscoord& aDisplayWidth, + nscoord aBtnWidth, + const nsMargin& aBorderPadding, + nscoord aFallBackHgt, + PRBool aCheckHeight) +{ + // start out by using the cached height + // XXX later this will change when we better handle constrained height + nscoord dispHeight = mCacheSize.height - aBorderPadding.top - aBorderPadding.bottom; + nscoord dispWidth = aDisplayWidth; + + REFLOW_NOISY_MSG3("+++1 AdjustCombo DW:%d DH:%d ", PX(dispWidth), PX(dispHeight)); + REFLOW_NOISY_MSG3("BW:%d BH:%d ", PX(aBtnWidth), PX(dispHeight)); + REFLOW_NOISY_MSG3("mCacheSize.height:%d - %d\n", PX(mCacheSize.height), PX((aBorderPadding.top + aBorderPadding.bottom))); + + // get the border and padding for the DisplayArea (block frame & textframe) + const nsStyleSpacing* dspSpacing; + aDisplayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); + nsMargin dspBorderPadding; + dspBorderPadding.SizeTo(0, 0, 0, 0); + dspSpacing->CalcBorderPaddingFor(aDisplayFrame, dspBorderPadding); + + // adjust the height + if (mCacheSize.height == kSizeNotSet) { + if (aFallBackHgt == kSizeNotSet) { + NS_ASSERTION(aFallBackHgt != kSizeNotSet, "Fallback can't be kSizeNotSet when mCacheSize.height == kSizeNotSet"); + } else { + dispHeight = aFallBackHgt; + REFLOW_NOISY_MSG2("+++3 Adding (dspBorderPadding.top + dspBorderPadding.bottom): %d\n", (dspBorderPadding.top + dspBorderPadding.bottom)); + dispHeight += (dspBorderPadding.top + dspBorderPadding.bottom); + } + } + + //////////////////////////////////////////////////////////////////////////// + // XXX - This is really bad, but at the moment I am NOT adding in the left padding + // we need to be able to find out what the left padding is on an option + // and subtract that out before we can add in our padding. + REFLOW_NOISY_MSG2("+++3 Adding: %d\n", dspBorderPadding.right); + dispWidth += dspBorderPadding.left + dspBorderPadding.right; + + REFLOW_NOISY_MSG3("+++2 AdjustCombo DW:%d DH:%d ", PX(dispWidth), PX(dispHeight)); + REFLOW_NOISY_MSG3(" BW:%d BH:%d\n", PX(aBtnWidth), PX(dispHeight)); + + // This sets the button to be a specific size + // so no matter what it reflows at these values + SetChildFrameSize(aDropDownBtn, aBtnWidth, dispHeight); + + // now that we know what the overall display width & height will be + // set up a new reflow state and reflow the area frame at that size + nsSize availSize(dispWidth + aBorderPadding.left + aBorderPadding.right, + dispHeight + aBorderPadding.top + aBorderPadding.bottom); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState, this, availSize); + kidReflowState.mComputedWidth = dispWidth; + kidReflowState.mComputedHeight = dispHeight; + + // do reflow + nsAreaFrame::Reflow(aPresContext, aDesiredSize, kidReflowState, aStatus); + + ///////////////////////////////////////////////////////// + // The DisplayFrame is a Block frame containing a TextFrame + // and it is completely anonymous, so we must manually reflow it + nsSize txtAvailSize(dispWidth - aBtnWidth, dispHeight); + nsHTMLReflowMetrics txtKidSize(&txtAvailSize); + nsHTMLReflowState txtKidReflowState(aPresContext, aReflowState, aDisplayFrame, txtAvailSize); + + aDisplayFrame->WillReflow(aPresContext); + aDisplayFrame->MoveTo(aPresContext, aBorderPadding.left, aBorderPadding.top); + nsIView* view; + aDisplayFrame->GetView(aPresContext, &view); + if (view) { + nsAreaFrame::PositionFrameView(aPresContext, aDisplayFrame, view); + } + nsReflowStatus status; + nsresult rv = aDisplayFrame->Reflow(aPresContext, txtKidSize, txtKidReflowState, status); + + ///////////////////////////////////////////////////////// + // If we are Constrained then the AreaFrame Reflow is the correct size + // if we are unconstrained then + //if (aReflowState.mComputedWidth == NS_UNCONSTRAINEDSIZE) { + // aDesiredSize.width += txtKidSize.width; + //} + + // Apparently, XUL lays out differently than HTML + // (the code above works for HTML and not XUL), + // so instead of using the above calculation + // I just set it to what it should be. + aDesiredSize.width = availSize.width; + //aDesiredSize.height = availSize.height; + + // now we need to adjust layout, because the AreaFrame + // doesn't position things exactly where we want them + nscoord insideWidth = dispWidth; + nscoord insideHeight = aDesiredSize.height - aBorderPadding.top - aBorderPadding.bottom; + + // the gets for the Display "block" frame and for the button + // make adjustments + nsRect buttonRect; + nsRect displayRect; + aDisplayFrame->GetRect(displayRect); + aDropDownBtn->GetRect(buttonRect); + // set the display rect to be left justifed and + // fills the entire area except the button + nscoord x = aBorderPadding.left; + displayRect.x = x; + displayRect.y = aBorderPadding.top; + displayRect.height = insideHeight; + displayRect.width = dispWidth - aBtnWidth; + aDisplayFrame->SetRect(aPresContext, displayRect); + x += displayRect.width; + + // right justify the button + buttonRect.x = x; + buttonRect.y = aBorderPadding.top; + buttonRect.height = insideHeight; + buttonRect.width = aBtnWidth; + aDropDownBtn->SetRect(aPresContext, buttonRect); + + // since we have changed the height of the button + // make sure it has these new values + // XXX this may not be needed anymore + SetChildFrameSize(aDropDownBtn, aBtnWidth, aDesiredSize.height); + + + REFLOW_NOISY_MSG3("**AdjustCombobox - Reflow: WW: %d HH: %d\n", aDesiredSize.width, aDesiredSize.height); + + // Now cache the available height as our height without border and padding + // This sets up the optimization for if a new available width comes in and we are equal or + // less than it we can bail + if (aDesiredSize.width != mCacheSize.width || aDesiredSize.height != mCacheSize.height) { + if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) { + mCachedAvailableSize.width = aDesiredSize.width - (aBorderPadding.left + aBorderPadding.right); + } + if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { + mCachedAvailableSize.height = aDesiredSize.height - (aBorderPadding.top + aBorderPadding.bottom); + } + nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedMaxElementSize, aDesiredSize); + } + + /////////////////////////////////////////////////////////////////// + // This is an experimental reflow that is turned off in the build +#ifdef DO_NEW_REFLOW_X + ReflowItems(aPresContext, aReflowState, aDesiredSize); +#endif + /////////////////////////////////////////////////////////////////// +} + +//---------------------------------------------------------- +// +//---------------------------------------------------------- +#ifdef DO_REFLOW_DEBUG +static int myCounter = 0; + +static void printSize(char * aDesc, nscoord aSize) +{ + printf(" %s: ", aDesc); + if (aSize == NS_UNCONSTRAINEDSIZE) { + printf("UC"); + } else { + printf("%d", PX(aSize)); + } +} +#endif + +//------------------------------------------------------------------- +//-- Main Reflow for the Combobox +//------------------------------------------------------------------- NS_IMETHODIMP nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { -#ifdef DEBUG_rodsXXX - printf("****** nsComboboxControlFrame::Reflow %d Reason: ", myCounter++); + aStatus = NS_FRAME_COMPLETE; + + REFLOW_COUNTER_REQUEST(); + +#ifdef DO_REFLOW_DEBUG + printf("-------------Starting Combobox Reflow ----------------------------\n"); + printf("%p ** Id: %d nsCCF::Reflow %d R: ", this, mReflowId, myCounter++); switch (aReflowState.reason) { case eReflowReason_Initial: - printf("eReflowReason_Initial\n");break; + printf("Ini");break; case eReflowReason_Incremental: - printf("eReflowReason_Incremental\n");break; + printf("Inc");break; case eReflowReason_Resize: - printf("eReflowReason_Resize\n"); + printf("Rsz");break; + case eReflowReason_StyleChange: + printf("Sty");break; + case eReflowReason_Dirty: + printf("Drt "); break; - case eReflowReason_StyleChange:printf("eReflowReason_StyleChange\n");break; + default:printf("%d", aReflowState.reason);break; } + + printSize("AW", aReflowState.availableWidth); + printSize("AH", aReflowState.availableHeight); + printSize("CW", aReflowState.mComputedWidth); + printSize("CH", aReflowState.mComputedHeight); + + nsCOMPtr optionsTemp = getter_AddRefs(GetOptions(mContent)); + PRUint32 numOptions; + optionsTemp->GetLength(&numOptions); + printSize("NO", (nscoord)numOptions); + + printf(" *\n"); + #endif -#if 0 - nsresult skiprv = nsFormControlFrame::SkipResizeReflow(mCacheSize, mCachedMaxElementSize, aPresContext, - aDesiredSize, aReflowState, aStatus); - if (NS_SUCCEEDED(skiprv)) { - return skiprv; - } -#endif + PRBool bailOnWidth; + PRBool bailOnHeight; + + // Do initial check to see if we can bail out + // If it is an Initial or Incremental Reflow we never bail out here + // XXX right now we only bail if the width meets the criteria + // + // We bail: + // if mComputedWidth == NS_UNCONSTRAINEDSIZE and + // availableWidth == NS_UNCONSTRAINEDSIZE and + // we have cached an available size + // + // We bail: + // if mComputedWidth == NS_UNCONSTRAINEDSIZE and + // availableWidth != NS_UNCONSTRAINEDSIZE and + // availableWidth minus its border equals our cached available size + // + // We bail: + // if mComputedWidth != NS_UNCONSTRAINEDSIZE and + // cached availableSize.width == aReflowState.mComputedWidth and + // cached AvailableSize.width == aCacheSize.width + // + // NOTE: this returns whether we are doing an Incremental reflow + nsFormControlFrame::SkipResizeReflow(mCacheSize, + mCachedMaxElementSize, + mCachedAvailableSize, + aDesiredSize, aReflowState, + aStatus, + bailOnWidth, bailOnHeight); + if (bailOnWidth) { +#ifdef DO_REFLOW_DEBUG // check or size + const nsStyleSpacing* spacing; + GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)spacing); + nsMargin borderPadding; + borderPadding.SizeTo(0, 0, 0, 0); + spacing->CalcBorderPaddingFor(this, borderPadding); + UNCONSTRAINED_CHECK(); +#endif + REFLOW_DEBUG_MSG3("^** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + return NS_OK; + } + + // add ourself to the form control + if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { + nsFormFrame::AddFormControlFrame(aPresContext, *NS_STATIC_CAST(nsIFrame*,this)); + if (NS_FAILED(CreateDisplayFrame(aPresContext))) { + return NS_ERROR_FAILURE; + } + } + + // Go get all of the important frame nsresult rv = NS_OK; nsIFrame* buttonFrame = GetButtonFrame(aPresContext); - nsIFrame* displayFrame = GetDisplayFrame(aPresContext); nsIFrame* dropdownFrame = GetDropdownFrame(); // Don't try to do any special sizing and positioning unless all of the frames // have been created. - if ((nsnull == displayFrame) || + if ((nsnull == mDisplayFrame) || (nsnull == buttonFrame) || (nsnull == dropdownFrame)) { @@ -728,12 +1222,9 @@ nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, nsRect buttonRect; nsRect dropdownRect; - - if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { - nsFormControlFrame::RegUnRegAccessKey(aPresContext, NS_STATIC_CAST(nsIFrame*, this), PR_TRUE); - nsFormFrame::AddFormControlFrame(aPresContext, *NS_STATIC_CAST(nsIFrame*,this)); - } - + // get our border and padding, + // XXX - should be the same mComputedBorderPadding? + // maybe we should use that? const nsStyleSpacing* spacing; GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)spacing); nsMargin borderPadding; @@ -741,39 +1232,11 @@ nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, spacing->CalcBorderPaddingFor(this, borderPadding); // Get the current sizes of the combo box child frames - displayFrame->GetRect(displayRect); + mDisplayFrame->GetRect(displayRect); buttonFrame->GetRect(buttonRect); dropdownFrame->GetRect(dropdownRect); - nsHTMLReflowState firstPassState(aReflowState); - - // Only reflow the display and button if they are the target of - // the incremental reflow, unless they change size. If they do - // then everything needs to be reflowed. - if (eReflowReason_Incremental == firstPassState.reason) { - nsIFrame* targetFrame; - firstPassState.reflowCommand->GetTarget(targetFrame); - if ((targetFrame == buttonFrame) || (targetFrame == displayFrame)) { - nsRect oldDisplayRect = displayRect; - nsRect oldButtonRect = buttonRect; - rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - if (NS_UNCONSTRAINEDSIZE != firstPassState.mComputedWidth) { - aDesiredSize.width -= borderPadding.left + borderPadding.right; - } - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - if ((oldDisplayRect == displayRect) && (oldButtonRect == buttonRect)) { - aStatus = NS_FRAME_COMPLETE; - return rv; - } - } - nsIReflowCommand::ReflowType type; - aReflowState.reflowCommand->GetType(type); - firstPassState.reason = eReflowReason_StyleChange; - firstPassState.reflowCommand = nsnull; - } - + // We should cache this instead getting it everytime // the default size of the of scrollbar // that will be the default width of the dropdown button // the height will be the height of the text @@ -794,416 +1257,382 @@ nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, float scale; dx->GetCanonicalPixelScale(scale); scrollbarWidth = NSIntPixelsToTwips(info.mSize, p2t*scale); - } - - //Set the desired size for the button and display frame - if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) { - // A width has not been specified for the select so size the display area to - // match the width of the longest item in the drop-down list. The dropdown - // list has already been reflowed and sized to shrink around its contents above. - - // Reflow the dropdown shrink-wrapped. - PRBool saveMES = aDesiredSize.maxElementSize != nsnull; - nsSize * maxElementSize = nsnull; - if (saveMES) { - maxElementSize = new nsSize(); - //maxElementSize = new nsSize(*aDesiredSize.maxElementSize); - } - nsHTMLReflowMetrics dropdownDesiredSize(maxElementSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - - nsSize size; - PRInt32 length = 0; - mListControlFrame->GetNumberOfOptions(&length); - dropdownFrame->GetRect(dropdownRect); - - const nsStyleSpacing* dropSpacing; - dropdownFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dropSpacing); - nsMargin dropBorderPadding; - dropBorderPadding.SizeTo(0, 0, 0, 0); - dropSpacing->CalcBorderPaddingFor(dropdownFrame, dropBorderPadding); - dropdownRect.width -= (dropBorderPadding.left + dropBorderPadding.right); - - // Get maximum size and height of a option in the dropdown - mListControlFrame->GetMaximumSize(size); - - if (scrollbarWidth > 0) { - size.width = scrollbarWidth; - } - const nsStyleSpacing* dspSpacing; - displayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); - nsMargin dspBorderPadding; - dspBorderPadding.SizeTo(0, 0, 0, 0); - dspSpacing->CalcBorderPaddingFor(displayFrame, dspBorderPadding); - - // Set width of display to match width of the drop down - SetChildFrameSize(displayFrame, dropdownRect.width-size.width+dspBorderPadding.left+dspBorderPadding.right, - size.height+dspBorderPadding.top+dspBorderPadding.bottom); - - // Size the button - SetChildFrameSize(buttonFrame, size.width, size.height); - - // Reflow display + button - nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus); - - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - buttonRect.y = displayRect.y; - buttonRect.height = displayRect.height; - buttonFrame->SetRect(aPresContext, buttonRect); - - // Reflow the dropdown list to match the width of the display + button - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, aDesiredSize.width, NS_UNCONSTRAINEDSIZE); - dropdownFrame->GetRect(dropdownRect); - - if (maxElementSize) { - delete maxElementSize; - } - - } else { - // A width has been specified for the select. - // Make the display frame's width + button frame width = the width specified. - nsHTMLReflowMetrics dropdownDesiredSize(aDesiredSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - // Get unconstrained size of the dropdown list. - nsSize size; - mListControlFrame->GetMaximumSize(size); - - if (scrollbarWidth > 0) { - size.width = scrollbarWidth; - } - // Size the button to be the same as the scrollbar width - SetChildFrameSize(buttonFrame, size.width, size.height); - - // Compute display width - nscoord displayWidth = firstPassState.mComputedWidth - size.width; - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - //displayWidth -= borderPadding.left + borderPadding.right; - - // Set the displayFrame to match the displayWidth computed above - SetChildFrameSize(displayFrame, displayWidth, size.height); - - // Reflow again with the width of the display frame set. - nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus); - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - // XXX rods - this hould not be subtracted in - //aDesiredSize.width += borderPadding.left + borderPadding.right; - - // Reflow the dropdown list to match the width of the display + button - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, aDesiredSize.width, NS_UNCONSTRAINEDSIZE); - - } - - // Set the max element size to be the same as the desired element size. - if (nsnull != aDesiredSize.maxElementSize) { - aDesiredSize.maxElementSize->width = aDesiredSize.width; - aDesiredSize.maxElementSize->height = aDesiredSize.height; - } - - aStatus = NS_FRAME_COMPLETE; -#if 0 - COMPARE_QUIRK_SIZE("nsComboboxControlFrame", 127, 22) -#endif - - nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedMaxElementSize, aDesiredSize); - return rv; - -} - - -//------------------------------------------------------------------------------- -//------------------------------------------------------------------------------- -// New Reflow -//------------------------------------------------------------------------------- -//------------------------------------------------------------------------------- -#else // DO_OLD_REFLOW - -#ifdef DEBUG_rods -nsComboboxControlFrame * pCB = nsnull; -static int myCounter = 0; -#endif - -NS_IMETHODIMP -nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) -{ -#ifdef DEBUG_rods - //if (pCB) { - printf("%p ****** nsComboboxControlFrame::Reflow %d Reason: ", this, myCounter++); - switch (aReflowState.reason) { - case eReflowReason_Initial: - printf("eReflowReason_Initial\n");break; - case eReflowReason_Incremental: - printf("eReflowReason_Incremental\n");break; - case eReflowReason_Resize: - printf("eReflowReason_Resize\n"); - break; - case eReflowReason_StyleChange:printf("eReflowReason_StyleChange\n");break; - } - //} -#endif - -#if 0 - nsresult skiprv = nsFormControlFrame::SkipResizeReflow(mCacheSize, mCachedMaxElementSize, aPresContext, - aDesiredSize, aReflowState, aStatus); - if (NS_SUCCEEDED(skiprv)) { - return skiprv; - } -#endif - - nsresult rv = NS_OK; - nsIFrame* buttonFrame = GetButtonFrame(aPresContext); - nsIFrame* displayFrame = GetDisplayFrame(aPresContext); - nsIFrame* dropdownFrame = GetDropdownFrame(); - - // Don't try to do any special sizing and positioning unless all of the frames - // have been created. - if ((nsnull == displayFrame) || - (nsnull == buttonFrame) || - (nsnull == dropdownFrame)) - { - // Since combobox frames are missing just do a normal area frame reflow - return nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - } - - // size of each part of the combo box - nsRect displayRect; - nsRect buttonRect; - nsRect dropdownRect; - - - if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { - nsFormFrame::AddFormControlFrame(aPresContext, *NS_STATIC_CAST(nsIFrame*,this)); - } - - const nsStyleSpacing* spacing; - GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)spacing); - nsMargin borderPadding; - borderPadding.SizeTo(0, 0, 0, 0); - spacing->CalcBorderPaddingFor(this, borderPadding); - - // Get the current sizes of the combo box child frames - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - dropdownFrame->GetRect(dropdownRect); - + } + + // set up a new reflow state for use throughout nsHTMLReflowState firstPassState(aReflowState); + nsHTMLReflowMetrics dropdownDesiredSize(nsnull); - // Only reflow the display and button if they are the target of - // the incremental reflow, unless they change size. If they do - // then everything needs to be reflowed. - if (eReflowReason_Incremental == firstPassState.reason) { + // Check to see if this a fully unconstrained reflow + PRBool fullyUnconstrained = firstPassState.availableWidth == NS_UNCONSTRAINEDSIZE && + firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE && + firstPassState.availableHeight == NS_UNCONSTRAINEDSIZE && + firstPassState.mComputedHeight == NS_UNCONSTRAINEDSIZE; + + PRBool forceReflow = PR_FALSE; + + // Only reflow the display and button + // if they are the target of the incremental reflow, unless they change size. + if (eReflowReason_Incremental == aReflowState.reason) { nsIFrame* targetFrame; firstPassState.reflowCommand->GetTarget(targetFrame); + // Check to see if we are the target of the Incremental Reflow if (targetFrame == this) { - // not sure what I should be doing here for an incremental reflow - // so I will let it just reflow as normal - // but it does seems to go into this code. - } else { - if (targetFrame != dropdownFrame) { -#ifdef DEBUG_rods - printf("+++++++++++++++++++++++\n"); -#endif - rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - buttonRect.y = displayRect.y; - buttonRect.height = displayRect.height; - buttonFrame->SetRect(aPresContext, buttonRect); - aStatus = NS_FRAME_COMPLETE; -#ifdef DEBUG_rods - printf("--> W: %d H: %d\n", aDesiredSize.width, aDesiredSize.height); -#endif + // We need to check here to see if we can get away with just reflowing + // the combobox and not the dropdown + REFLOW_DEBUG_MSG("-----------------Target is Combobox------------\n"); + + // If the mComputedWidth matches our cached display width + // then we get away with bailing out + PRBool doFullReflow = firstPassState.mComputedWidth != NS_UNCONSTRAINEDSIZE && + firstPassState.mComputedWidth != mItemDisplayWidth; + if (!doFullReflow) { + // OK, so we got lucky and the size didn't change + // so do a simple reflow and bail out + REFLOW_DEBUG_MSG("------------Reflowing AreaFrame and bailing----\n\n"); + ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, + mDisplayFrame, buttonFrame, mItemDisplayWidth, + scrollbarWidth, borderPadding); + REFLOW_COUNTER(); + UNCONSTRAINED_CHECK(); + REFLOW_DEBUG_MSG3("&** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); return rv; - } else { -#if 0 - nsRect dropdownRect; - dropdownFrame->GetRect(dropdownRect); - nsHTMLReflowMetrics dropdownDesiredSize(aDesiredSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, - aStatus, dropdownRect.width, dropdownRect.height); - nsCOMPtr context; - targetFrame->GetStyleContext(getter_AddRefs(context)); - const nsStyleDisplay* disp = (const nsStyleDisplay*)context->GetStyleData(eStyleStruct_Display); - printf("IsVisible %s\n", disp->mVisible == NS_STYLE_VISIBILITY_VISIBLE?"yes":"No"); - //if (disp->mVisible == NS_STYLE_VISIBILITY_VISIBLE) { - nsRect rect; - GetRect(rect); - aDesiredSize.width = rect.width;// + borderPadding.left + borderPadding.right; - aDesiredSize.height = rect.height;// + borderPadding.top + borderPadding.bottom; + } + // Nope, something changed that affected our size + // so we need to do a full reflow and resize ourself + REFLOW_DEBUG_MSG("------------Do Full Reflow----\n\n"); + firstPassState.reason = eReflowReason_StyleChange; + firstPassState.reflowCommand = nsnull; + forceReflow = PR_TRUE; + + } else { + // Now, see if our target is the dropdown + // If so, maybe an items was added or some style changed etc. + // OR + // We get an Incremental reflow on the dropdown when it is being + // shown or hidden. + if (targetFrame == dropdownFrame) { + REFLOW_DEBUG_MSG("---------Target is Dropdown (Clearing Unc DD Size)---\n"); + PRBool bail = PR_FALSE; + + // Let's start by checking to see if this is a reflow ro showing or hiding. + nsIView * view; + dropdownFrame->GetView(aPresContext, &view); + nsViewVisibility vis = nsViewVisibility_kHide; + NS_ASSERTION(view != nsnull, "The dropdown frame doesn't have a view!"); + if (view != nsnull) { + view->GetVisibility(vis); + } + // If the view is hidden, but the combobox thinks the it is dropped down + // then it look like we are suppose to be showing the dropdown + // XXX - It is really to bad we have to reflow at all + // just to get it to show up or be hidden + if (nsViewVisibility_kHide == vis && mDroppedDown) { + bail = PR_TRUE; + // Do a constrained reflow at a predetermined size + // to have the dropdown shown + firstPassState.reason = eReflowReason_StyleChange; + firstPassState.reflowCommand = nsnull; + mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); + nscoord width = mCacheSize.width > kSizeNotSet?PR_MAX(mCacheSize.width, dropdownRect.width):NS_UNCONSTRAINEDSIZE; + ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, width, NS_UNCONSTRAINEDSIZE); + + } else if (nsViewVisibility_kShow == vis && !mDroppedDown) { + bail = PR_TRUE; + // Do a Unconstrained reflow to "roll" it up + // then cache that unconstrained size + firstPassState.reason = eReflowReason_StyleChange; + firstPassState.reflowCommand = nsnull; + mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); + ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + // cache the values here also, + // since we just did an unconstrained reflow + mCachedUncDropdownSize.width = dropdownDesiredSize.width; + mCachedUncDropdownSize.height = dropdownDesiredSize.height; + REFLOW_DEBUG_MSG3("---2 Caching mCachedUncDropdownSize %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + } + + // Ok, we get to bail because it was either being shown or hidden + if (bail) { + if (fullyUnconstrained) { + aDesiredSize.width = mCachedUncComboSize.width; + aDesiredSize.height = mCachedUncComboSize.height; + } else { + aDesiredSize.width = mCacheSize.width; + aDesiredSize.height = mCacheSize.height; + } + + if (aDesiredSize.maxElementSize != nsnull) { + aDesiredSize.maxElementSize->width = mCachedMaxElementSize.width; + aDesiredSize.maxElementSize->height = mCachedMaxElementSize.height; + } aDesiredSize.ascent = aDesiredSize.height; aDesiredSize.descent = 0; - dropdownRect.width = rect.width; - dropdownFrame->SetRect(aPresContext, dropdownRect); - printf("Skipping out.........................."); - aStatus = NS_FRAME_COMPLETE; + REFLOW_DEBUG_MSG3("|** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + REFLOW_DEBUG_MSG("---- Setting Cached values and then bailing out\n\n"); + UNCONSTRAINED_CHECK(); return NS_OK; - //} -#endif + } + // Nope, we were unlucky so now we do a full reflow + mCachedUncDropdownSize.width = kSizeNotSet; + mCachedUncDropdownSize.height = kSizeNotSet; + REFLOW_DEBUG_MSG("---- Doing Full Reflow\n"); + // This is an incremental reflow targeted at the dropdown list + // and it didn't have anything to do with being show or hidden. + // + // The incremental reflow will not get to the dropdown list + // because it is in the "popup" list + // when this flow of control drops out of this if it will do a reflow + // on the AreaFrame which SHOULD make it get tothe drop down + // except that it is in the popup list, so we have it reflowed as + // a StyleChange, this is not as effecient as doing an Incremental + // + // At this point we want to by pass the reflow optimization in the dropdown + // because we aren't why it is getting an incremental reflow, but we do + // know that it needs to be resized or restyled + //mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); + + } else if (targetFrame == mDisplayFrame || targetFrame == buttonFrame) { + /*if (targetFrame == mDisplayFrame) { + nsresult rv = ReflowComboChildFrame(mDisplayFrame, aPresContext, aDesiredSize, + aReflowState, aStatus, + aReflowState.availableWidth, + aReflowState.availableHeight); + return rv; + }*/ + + // The incremental reflow is targeted at either the block or the button + REFLOW_DEBUG_MSG2("-----------------Target is %s------------\n", (targetFrame == mDisplayFrame?"DisplayItem Frame":"DropDown Btn Frame")); + REFLOW_DEBUG_MSG("---- Doing AreaFrame Reflow and then bailing out\n"); + // Do simple reflow and bail out + ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, + mDisplayFrame, buttonFrame, + mItemDisplayWidth, scrollbarWidth, borderPadding, kSizeNotSet, PR_TRUE); + REFLOW_DEBUG_MSG3("+** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + REFLOW_COUNTER(); + UNCONSTRAINED_CHECK(); + return rv; + } else { + // Here the target of the reflow was a child of the dropdown list + // so we must do a full reflow + REFLOW_DEBUG_MSG("-----------------Target is Dropdown's Child (Option Item)------------\n"); + REFLOW_DEBUG_MSG("---- Doing Reflow as StyleChange\n"); } + firstPassState.reason = eReflowReason_StyleChange; + firstPassState.reflowCommand = nsnull; + mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); + forceReflow = PR_TRUE; } } - // the default size of the of scrollbar - // that will be the default width of the dropdown button - // the height will be the height of the text - nscoord scrollbarWidth = -1; - nsCOMPtr dx; - aPresContext->GetDeviceContext(getter_AddRefs(dx)); - if (dx) { - // Get the width in Device pixels (in this case screen) - SystemAttrStruct info; - dx->GetSystemAttribute(eSystemAttr_Size_ScrollbarWidth, &info); - // Get the pixels to twips conversion for the current device (screen or printer) - float p2t; - aPresContext->GetPixelsToTwips(&p2t); - // Get the scale factor for mapping from one device (screen) - // to another device (screen or printer) - // Typically when it is a screen the scale 1.0 - // when it is a printer is could be anything - float scale; - dx->GetCanonicalPixelScale(scale); - scrollbarWidth = NSIntPixelsToTwips(info.mSize, p2t*scale); - } -#ifdef DEBUG_rods - printf("UNC %d AV %d \n", firstPassState.mComputedWidth,firstPassState.availableWidth); -#endif + // This ifdef is for the new approach to reflow + // where we don't reflow the dropdown + // we just figure out or width from the list of items + // + // This next section is the Current implementation + // the else contains the new reflow code +#ifndef DO_NEW_REFLOW + + // Here is another special optimization + // Only reflow the dropdown if it has never been reflowed unconstrained + // + // Or someone up above here may want to force it to be reflowed + // by setting one or both of these to kSizeNotSet + if ((mCachedUncDropdownSize.width == kSizeNotSet && + mCachedUncDropdownSize.height == kSizeNotSet) || forceReflow) { + REFLOW_DEBUG_MSG3("---Re %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + // A width has not been specified for the select so size the display area + // to match the width of the longest item in the drop-down list. The dropdown + // list has already been reflowed and sized to shrink around its contents above. + ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, + aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + if (forceReflow) { + mCachedUncDropdownSize.width = dropdownDesiredSize.width; + mCachedUncDropdownSize.height = dropdownDesiredSize.height; + } + } else { + // Here we pretended we did an unconstrained reflow + // so we set the cached values and continue on + REFLOW_DEBUG_MSG3("--- Using Cached ListBox Size %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + dropdownDesiredSize.width = mCachedUncDropdownSize.width; + dropdownDesiredSize.height = mCachedUncDropdownSize.height; + } + + ///////////////////////////////////////////////////////////////////////// + // XXX - I need to clean this nect part up a little it is very redundant + + // Check here to if this is a mComputed unconstrained reflow + PRBool computedUnconstrained = firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE && + firstPassState.mComputedHeight == NS_UNCONSTRAINEDSIZE; + if (computedUnconstrained && !forceReflow) { + // Because Incremental reflows aren't actually getting to the dropdown + // we cache the size from when it did a fully unconstrained reflow + // we then check to see if the size changed at all, + // if not then bail out we don't need to worry + if (mCachedUncDropdownSize.width == kSizeNotSet && mCachedUncDropdownSize.height == kSizeNotSet) { + mCachedUncDropdownSize.width = dropdownDesiredSize.width; + mCachedUncDropdownSize.height = dropdownDesiredSize.height; + REFLOW_DEBUG_MSG3("---1 Caching mCachedUncDropdownSize %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + + } else if (mCachedUncDropdownSize.width == dropdownDesiredSize.width && + mCachedUncDropdownSize.height == dropdownDesiredSize.height) { + + if (mCachedUncComboSize.width != kSizeNotSet && mCachedUncComboSize.height != kSizeNotSet) { + REFLOW_DEBUG_MSG3("--- Bailing because of mCachedUncDropdownSize %d,%d\n\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + aDesiredSize.width = mCachedUncComboSize.width; + aDesiredSize.height = mCachedUncComboSize.height; + + if (aDesiredSize.maxElementSize != nsnull) { + aDesiredSize.maxElementSize->width = mCachedMaxElementSize.width; + aDesiredSize.maxElementSize->height = mCachedMaxElementSize.height; + } + aDesiredSize.ascent = aDesiredSize.height; + aDesiredSize.descent = 0; + UNCONSTRAINED_CHECK(); + REFLOW_DEBUG_MSG3("#** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + return NS_OK; + } + } else { + mCachedUncDropdownSize.width = dropdownDesiredSize.width; + mCachedUncDropdownSize.height = dropdownDesiredSize.height; + } + } + // clean up stops here + ///////////////////////////////////////////////////////////////////////// + + // So this point we know we flowed the dropdown unconstrained + // now we get to figure out how big we need to be and + // + // We don't reflow the combobox here at the new size + // we cache its new size and reflow it on the dropdown + nsSize size; + PRInt32 length = 0; + mListControlFrame->GetNumberOfOptions(&length); + + // dropdownRect will hold the content size (minus border padding) + // for the display area + dropdownFrame->GetRect(dropdownRect); + + // Get maximum size of the largest item in the dropdown + // The height of the display frame will be that height + // the width will be the same as + // the dropdown width (minus its borderPadding) OR + // a caculation off the mComputedWidth from reflow + mListControlFrame->GetMaximumSize(size); + + // the variable "size" will now be + // the default size of the dropdown btn + if (scrollbarWidth > 0) { + size.width = scrollbarWidth; + } + + // Get the border and padding for the dropdown + const nsStyleSpacing* dropSpacing; + dropdownFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dropSpacing); + nsMargin dropBorderPadding; + dropBorderPadding.SizeTo(0, 0, 0, 0); + dropSpacing->CalcBorderPaddingFor(dropdownFrame, dropBorderPadding); + + // get the borderPadding for the display area + const nsStyleSpacing* dspSpacing; + mDisplayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); + nsMargin dspBorderPadding; + dspBorderPadding.SizeTo(0, 0, 0, 0); + dspSpacing->CalcBorderPaddingFor(mDisplayFrame, dspBorderPadding); + + // Substract dropdown's borderPadding from the width of the dropdown rect + // to get the size of the content area + // + // the height will come from the mDisplayFrame's height + // declare a size for the item display frame + //Set the desired size for the button and display frame if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) { -#ifdef DEBUG_rods - printf("=====================================\n"); -#endif + REFLOW_DEBUG_MSG("Unconstrained.....\n"); + REFLOW_DEBUG_MSG4("*B mItemDisplayWidth %d dropdownRect.width:%d dropdownRect.w+h %d\n", PX(mItemDisplayWidth), PX(dropdownRect.width), PX((dropBorderPadding.left + dropBorderPadding.right))); - // A width has not been specified for the select so size the display area to - // match the width of the longest item in the drop-down list. The dropdown - // list has already been reflowed and sized to shrink around its contents above. + // Start with the dropdown rect's width + mItemDisplayWidth = dropdownRect.width; - // Reflow the dropdown shrink-wrapped. - PRBool saveMES = aDesiredSize.maxElementSize != nsnull; - nsSize * maxElementSize = nsnull; - if (saveMES) { - maxElementSize = new nsSize(); - //maxElementSize = new nsSize(*aDesiredSize.maxElementSize); - } - nsHTMLReflowMetrics dropdownDesiredSize(maxElementSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - - nsSize size; - PRInt32 length = 0; - mListControlFrame->GetNumberOfOptions(&length); - dropdownFrame->GetRect(dropdownRect); + REFLOW_DEBUG_MSG2("* mItemDisplayWidth %d\n", PX(mItemDisplayWidth)); - const nsStyleSpacing* dropSpacing; - dropdownFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dropSpacing); - nsMargin dropBorderPadding; - dropBorderPadding.SizeTo(0, 0, 0, 0); - dropSpacing->CalcBorderPaddingFor(dropdownFrame, dropBorderPadding); - dropdownRect.width -= (dropBorderPadding.left + dropBorderPadding.right); + // subtract off the display's BorderPadding + mItemDisplayWidth -= dspBorderPadding.left + dspBorderPadding.right; - // Get maximum size and height of a option in the dropdown - mListControlFrame->GetMaximumSize(size); - - if (scrollbarWidth > 0) { - size.width = scrollbarWidth; - } - const nsStyleSpacing* dspSpacing; - displayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); - nsMargin dspBorderPadding; - dspBorderPadding.SizeTo(0, 0, 0, 0); - dspSpacing->CalcBorderPaddingFor(displayFrame, dspBorderPadding); - -#ifdef DEBUG_rods - printf("W: %d\n", dropdownRect.width-size.width+dspBorderPadding.left+dspBorderPadding.right); -#endif - // Set width of display to match width of the drop down - SetChildFrameSize(displayFrame, dropdownRect.width-size.width+dspBorderPadding.left+dspBorderPadding.right, - size.height+dspBorderPadding.top+dspBorderPadding.bottom); - - // Size the button - SetChildFrameSize(buttonFrame, size.width, size.height); - - // Reflow display + button - nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus); - - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - buttonRect.y = displayRect.y; - buttonRect.height = displayRect.height; - buttonFrame->SetRect(aPresContext, buttonRect); - - // Reflow the dropdown list to match the width of the display + button - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, aDesiredSize.width, NS_UNCONSTRAINEDSIZE); - dropdownFrame->GetRect(dropdownRect); - - if (maxElementSize) { - delete maxElementSize; - } + REFLOW_DEBUG_MSG2("*A mItemDisplayWidth %d\n", PX(mItemDisplayWidth)); } else { -#ifdef DEBUG_rods - printf("*************************************\n"); -#endif - // for debug ------- - PRInt32 length = 0; - mListControlFrame->GetNumberOfOptions(&length); - //------ - - // A width has been specified for the select. - // Make the display frame's width + button frame width = the width specified. - nsHTMLReflowMetrics dropdownDesiredSize(aDesiredSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - // Get unconstrained size of the dropdown list. - nsSize size; - mListControlFrame->GetMaximumSize(size); - - if (scrollbarWidth > 0) { - size.width = scrollbarWidth; - } - // Size the button to be the same as the scrollbar width - SetChildFrameSize(buttonFrame, size.width, size.height); - + REFLOW_DEBUG_MSG("Constrained.....\n"); if (firstPassState.mComputedWidth > 0) { - // Compute display width - nscoord displayWidth = firstPassState.mComputedWidth - size.width; - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - //displayWidth -= borderPadding.left + borderPadding.right; - -#ifdef DEBUG_rods - printf("WW: %d\n", displayWidth); -#endif - // Set the displayFrame to match the displayWidth computed above - if (displayWidth >= 0) { - SetChildFrameSize(displayFrame, displayWidth, size.height); - } - - // Reflow again with the width of the display frame set. - nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus); - - displayFrame->GetRect(displayRect); - displayRect.width = displayWidth; - buttonFrame->GetRect(buttonRect); - buttonRect.x = displayWidth+borderPadding.left; - buttonRect.y = displayRect.y; - buttonRect.height = displayRect.height; - buttonFrame->SetRect(aPresContext, buttonRect); - displayFrame->SetRect(aPresContext, displayRect); -#ifdef DEBUG_rods - printf("DW: %d\n", aDesiredSize.width); -#endif - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - // XXX rods - this hould not be subtracted in - //aDesiredSize.width += borderPadding.left + borderPadding.right; - - // Reflow the dropdown list to match the width of the display + button - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, aDesiredSize.width, NS_UNCONSTRAINEDSIZE); + // Compute the display item's width from reflow's mComputedWidth + // mComputedWidth has already excluded border and padding + // so subtract off the button's size + REFLOW_DEBUG_MSG3("B mItemDisplayWidth %d %d\n", PX(mItemDisplayWidth), PX(dspBorderPadding.right)); + mItemDisplayWidth = firstPassState.mComputedWidth - dspBorderPadding.left - dspBorderPadding.right; + REFLOW_DEBUG_MSG2("A mItemDisplayWidth %d\n", PX(mItemDisplayWidth)); + REFLOW_DEBUG_MSG4("firstPassState.mComputedWidth %d - size.width %d dspBorderPadding.right %d\n", PX(firstPassState.mComputedWidth), PX(size.width), PX(dspBorderPadding.right)); } - aDesiredSize.width = firstPassState.mComputedWidth + borderPadding.left + borderPadding.right; - aDesiredSize.height = firstPassState.mComputedHeight + borderPadding.top + borderPadding.bottom; - aDesiredSize.ascent = aDesiredSize.height; - aDesiredSize.descent = 0; } + // this reflows and makes and last minute adjustments + ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, + mDisplayFrame, buttonFrame, mItemDisplayWidth, scrollbarWidth, + borderPadding, size.height); + + // The dropdown was reflowed UNCONSTRAINED before, now we need to check to see + // if it needs to be resized. + // + // Optimization - The style (font, etc.) maybe different for the display item + // than for any particular item in the dropdown. So, if the new size of combobox + // is smaller than the dropdown, that is OK, The dropdown MUST always be either the same + //size as the combo or larger if necessary +#if 0 + if (aDesiredSize.width > dropdownDesiredSize.width) { + if (eReflowReason_Initial == firstPassState.reason) { + firstPassState.reason = eReflowReason_Resize; + } + REFLOW_DEBUG_MSG3("*** Reflowing ListBox to width: %d it was %d\n", PX(aDesiredSize.width), PX(dropdownDesiredSize.width)); + // Reflow the dropdown list to match the width of the display + button + ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, + aDesiredSize.width, NS_UNCONSTRAINEDSIZE); + } +#endif + +#else // DO_NEW_REFLOW + + if (mCacheSize.width == kSizeNotSet) { + ReflowItems(aPresContext, aReflowState, aDesiredSize); + } else { + aDesiredSize.width = mCacheSize.width; + aDesiredSize.height = mCacheSize.height; + } + + // get the borderPadding for the display area + const nsStyleSpacing* dspSpacing; + mDisplayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); + nsMargin dspBorderPadding; + dspBorderPadding.SizeTo(0, 0, 0, 0); + + dspSpacing->CalcBorderPaddingFor(mDisplayFrame, dspBorderPadding); + + if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) { + mItemDisplayWidth = aDesiredSize.width - (dspBorderPadding.left + dspBorderPadding.right); + mItemDisplayWidth -= borderPadding.left + borderPadding.right; + } else { + if (firstPassState.mComputedWidth > 0) { + // Compute the display item's width from reflow's mComputedWidth + // mComputedWidth has already excluded border and padding + // so subtract off the button's size + mItemDisplayWidth = firstPassState.mComputedWidth - dspBorderPadding.left - dspBorderPadding.right; + } + } + + // this reflows and makes and last minute adjustments + ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, + mDisplayFrame, buttonFrame, mItemDisplayWidth, scrollbarWidth, + borderPadding, + aDesiredSize.height- borderPadding.top - borderPadding.bottom - + dspBorderPadding.top - dspBorderPadding.bottom); +#endif // DO_NEW_REFLOW // Set the max element size to be the same as the desired element size. if (nsnull != aDesiredSize.maxElementSize) { @@ -1211,19 +1640,37 @@ nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, aDesiredSize.maxElementSize->height = aDesiredSize.height; } - aStatus = NS_FRAME_COMPLETE; #if 0 COMPARE_QUIRK_SIZE("nsComboboxControlFrame", 127, 22) #endif + // cache the availabe size to be our desired size minus the borders + // this is so if our cached avilable size is ever equal to or less + // than the real avilable size we can bail out + if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) { + mCachedAvailableSize.width = aDesiredSize.width - (borderPadding.left + borderPadding.right); + } + if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { + mCachedAvailableSize.height = aDesiredSize.height - (borderPadding.top + borderPadding.bottom); + } + nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedMaxElementSize, aDesiredSize); -#ifdef DEBUG_rods - printf("--> W: %d H: %d\n", aDesiredSize.width, aDesiredSize.height); -#endif + + REFLOW_DEBUG_MSG3("** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + REFLOW_COUNTER(); + UNCONSTRAINED_CHECK(); + + // If this was a fully unconstrained reflow we cache + // the combobox's unconstrained size + if (fullyUnconstrained) { + mCachedUncComboSize.width = aDesiredSize.width; + mCachedUncComboSize.height = aDesiredSize.height; + } + return rv; } -#endif // DO_OLD_REFLOW + //------------------------------------------------------------------------------- //------------------------------------------------------------------------------- // Done with New Reflow @@ -1361,10 +1808,15 @@ nsComboboxControlFrame::SetDropDown(nsIFrame* aDropDownFrame) return NS_ERROR_FAILURE; } + + // The ListControlFrame was just created and added to the comboxbox + // so provide it with a PresState so it can restore itself + // when it does its first "Reset" if (mPresState) { mListControlFrame->SetPresState(mPresState); mPresState = do_QueryInterface(nsnull); } + return NS_OK; } @@ -1413,7 +1865,13 @@ nsComboboxControlFrame::UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUp if (mListControlFrame) { // Check to see if the selection changed if (mSelectedIndex != aNewIndex || aForceUpdate) { + //???if (mSelectedIndex != aNewIndex || (aForceUpdate && aNewIndex != kSizeNotSet)) { mListControlFrame->GetSelectedItem(mTextStr); // Update text box +#ifdef DO_REFLOW_DEBUG + char * str = mTextStr.ToNewCString(); + REFLOW_DEBUG_MSG2("UpdateSelection %s\n", str); + delete [] str; +#endif mListControlFrame->UpdateSelection(aDoDispatchEvent, aForceUpdate, mContent); } mSelectedIndex = aNewIndex; @@ -1427,7 +1885,7 @@ nsComboboxControlFrame::AbsolutelyPositionDropDown() { nsRect absoluteTwips; nsRect absolutePixels; -// XXX Not used nsIFrame* displayFrame = GetDisplayFrame(mPresContext); + nsRect rect; this->GetRect(rect); GetAbsoluteFramePosition(mPresContext, this, absoluteTwips, absolutePixels); @@ -1454,49 +1912,31 @@ nsComboboxControlFrame::SelectionChanged() // Send reflow command because the new text maybe larger nsresult rv = NS_OK; if (mDisplayContent) { - nsCOMPtr htmlContent(do_QueryInterface(mDisplayContent, &rv)); - if (NS_SUCCEEDED(rv) && htmlContent) { - nsHTMLValue value; - nsresult result = htmlContent->GetHTMLAttribute(nsHTMLAtoms::value, value); - - PRBool shouldSetValue = PR_FALSE; - if (NS_FAILED(result) || value.GetUnit() == eHTMLUnit_Empty) { - shouldSetValue = PR_TRUE; - } else if (NS_SUCCEEDED(result) && value.GetUnit() == eHTMLUnit_String) { - nsAutoString str; - value.GetStringValue(str); - shouldSetValue = str != mTextStr; - } - if (shouldSetValue) { - rv = htmlContent->SetHTMLAttribute(nsHTMLAtoms::value, mTextStr, PR_TRUE); -#if 1 - if (NS_SUCCEEDED(rv)) { - nsIFrame* displayFrame = GetDisplayFrame(mPresContext); - - nsIReflowCommand* cmd; - rv = NS_NewHTMLReflowCommand(&cmd, displayFrame, nsIReflowCommand::StyleChanged); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr shell; - rv = mPresContext->GetShell(getter_AddRefs(shell)); - if (NS_SUCCEEDED(rv) && shell) { - if (NS_SUCCEEDED(shell->EnterReflowLock())) { - shell->AppendReflowCommand(cmd); - shell->ExitReflowLock(PR_TRUE); - } - } - NS_RELEASE(cmd); - } - } -#else - if (mParent) { - nsCOMPtr shell; - rv = mPresContext->GetShell(getter_AddRefs(shell)); - mState |= NS_FRAME_IS_DIRTY; - mParent->ReflowDirtyChild(shell, (nsIFrame*) this); - } - -#endif - } + nsAutoString value; + const nsTextFragment* fragment; + nsresult result = mDisplayContent->GetText(&fragment); + if (NS_SUCCEEDED(result)) { + fragment->AppendTo(value); + } + PRBool shouldSetValue = PR_FALSE; + if (NS_FAILED(result) || value.Length() == 0) { + shouldSetValue = PR_TRUE; + } else { + shouldSetValue = value != mTextStr; + REFLOW_DEBUG_MSG3("**** SelectionChanged Old[%s] New[%s]\n", value.ToNewCString(), mTextStr.ToNewCString()); + } + if (shouldSetValue) { + rv = mDisplayContent->SetText(mTextStr.GetUnicode(), mTextStr.Length(), PR_TRUE); + nsFrameState state; + mTextFrame->GetFrameState(&state); + state |= NS_FRAME_IS_DIRTY; + mTextFrame->SetFrameState(state); + mDisplayFrame->GetFrameState(&state); + state |= NS_FRAME_IS_DIRTY; + mDisplayFrame->SetFrameState(state); + nsCOMPtr shell; + rv = mPresContext->GetShell(getter_AddRefs(shell)); + ReflowDirtyChild(shell, (nsIFrame*) mDisplayFrame); } } return rv; @@ -1526,19 +1966,18 @@ nsComboboxControlFrame::DoneAddingContent(PRBool aIsDone) NS_IMETHODIMP nsComboboxControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) { -#ifdef DEBUG_rodsXXX - if (!pCB) pCB = this; +#ifdef DO_REFLOW_DEBUG + printf("**********\n*********AddOption: %d\n", aIndex); #endif - nsISelectControlFrame* listFrame = nsnull; nsIFrame* dropdownFrame = GetDropdownFrame(); nsresult rv = dropdownFrame->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); + //PRInt32 index; + //mListControlFrame->GetSelectedIndex(&index); + //UpdateSelection(PR_FALSE, PR_TRUE, index); NS_RELEASE(listFrame); } // If we added the first option, we might need to select it. @@ -1645,6 +2084,60 @@ nsComboboxControlFrame::GetProperty(nsIAtom* aName, nsString& aValue) } +NS_IMETHODIMP +nsComboboxControlFrame::CreateDisplayFrame(nsIPresContext* aPresContext) +{ + + nsCOMPtr shell; + aPresContext->GetShell(getter_AddRefs(shell)); + nsresult rv = NS_NewBlockFrame(shell, (nsIFrame**)&mDisplayFrame, NS_BLOCK_SPACE_MGR); + if (NS_FAILED(rv)) { return rv; } + if (!mDisplayFrame) { return NS_ERROR_NULL_POINTER; } + + // create the style context for the anonymous frame + nsCOMPtr styleContext; + rv = aPresContext->ResolvePseudoStyleContextFor(mContent, + nsHTMLAtoms::mozDisplayComboboxControlFrame, + mStyleContext, + PR_FALSE, + getter_AddRefs(styleContext)); + if (NS_FAILED(rv)) { return rv; } + if (!styleContext) { return NS_ERROR_NULL_POINTER; } + + // create a text frame and put it inside the block frame + rv = NS_NewTextFrame(shell, &mTextFrame); + if (NS_FAILED(rv)) { return rv; } + if (!mTextFrame) { return NS_ERROR_NULL_POINTER; } + nsCOMPtr textStyleContext; + rv = aPresContext->ResolvePseudoStyleContextFor(mContent, + nsHTMLAtoms::mozDisplayComboboxControlFrame, + styleContext, + PR_FALSE, + getter_AddRefs(textStyleContext)); + if (NS_FAILED(rv)) { return rv; } + if (!textStyleContext) { return NS_ERROR_NULL_POINTER; } + nsCOMPtr content(do_QueryInterface(mDisplayContent)); + mTextFrame->Init(aPresContext, content, mDisplayFrame, textStyleContext, nsnull); + mTextFrame->SetInitialChildList(aPresContext, nsnull, nsnull); + nsCOMPtr presShell; + rv = aPresContext->GetShell(getter_AddRefs(presShell)); + if (NS_FAILED(rv)) { return rv; } + if (!presShell) { return NS_ERROR_NULL_POINTER; } + nsCOMPtr frameManager; + rv = presShell->GetFrameManager(getter_AddRefs(frameManager)); + if (NS_FAILED(rv)) { return rv; } + if (!frameManager) { return NS_ERROR_NULL_POINTER; } + frameManager->SetPrimaryFrameFor(content, mTextFrame); + + rv = mDisplayFrame->Init(aPresContext, content, this, styleContext, nsnull); + if (NS_FAILED(rv)) { return rv; } + + mDisplayFrame->SetInitialChildList(aPresContext, nsnull, mTextFrame); + + return NS_OK; +} + + NS_IMETHODIMP nsComboboxControlFrame::CreateAnonymousContent(nsIPresContext* aPresContext, nsISupportsArray& aChildList) @@ -1669,22 +2162,29 @@ nsComboboxControlFrame::CreateAnonymousContent(nsIPresContext* aPresContext, // create content used for display //nsIAtom* tag = NS_NewAtom("mozcombodisplay"); - NS_NewHTMLInputElement(&mDisplayContent, nsHTMLAtoms::input); + // Add a child text content node for the label + nsCOMPtr labelContent; + nsresult result = NS_NewTextNode(getter_AddRefs(labelContent)); + nsAutoString value="X"; + if (NS_SUCCEEDED(result) && labelContent) { + // set the value of the text node + mDisplayContent = do_QueryInterface(labelContent); + mDisplayContent->SetText(value.GetUnicode(), value.Length(), PR_TRUE); - mDisplayContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, nsAutoString("button"), PR_FALSE); + nsIDocument* doc; + mContent->GetDocument(doc); + labelContent->SetDocument(doc, PR_FALSE); + NS_RELEASE(doc); + mContent->AppendChildTo(labelContent, PR_FALSE); - //XXX: Do not use nsHTMLAtoms::id use nsHTMLAtoms::kClass instead. There will end up being multiple - //ids set to the same value which is illegal. - mDisplayContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, nsAutoString("-moz-display"), PR_FALSE); - // This is - mDisplayContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, nsAutoString("X"), PR_TRUE); - aChildList.AppendElement(mDisplayContent); - - // create button which drops the list down - NS_NewHTMLInputElement(&mButtonContent, nsHTMLAtoms::input); - //NS_ADDREF(mButtonContent); - mButtonContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, nsAutoString("button"), PR_FALSE); - aChildList.AppendElement(mButtonContent); + // create button which drops the list down + result = NS_NewHTMLInputElement(&mButtonContent, nsHTMLAtoms::input); + //NS_ADDREF(mButtonContent); + if (NS_SUCCEEDED(result) && mButtonContent) { + mButtonContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, nsAutoString("button"), PR_FALSE); + aChildList.AppendElement(mButtonContent); + } + } return NS_OK; } @@ -1702,6 +2202,22 @@ nsComboboxControlFrame::Destroy(nsIPresContext* aPresContext) { // Cleanup frames in popup child list mPopupFrames.DestroyFrames(aPresContext); + if (mDisplayFrame) { + mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, mDisplayFrame, nsnull); + mDisplayFrame->Destroy(aPresContext); + mDisplayFrame=nsnull; + } + + if (mDisplayContent) { + nsCOMPtr content(do_QueryInterface(mDisplayContent)); + if (content) { + PRInt32 index; + if (NS_SUCCEEDED(mContent->IndexOf(content, index))) { + mContent->RemoveChildAt(index, PR_FALSE); + } + } + } + return nsAreaFrame::Destroy(aPresContext); } @@ -1729,7 +2245,7 @@ nsComboboxControlFrame::SetInitialChildList(nsIPresContext* aPresContext, mPopupFrames.SetFrames(aChildList); } else { rv = nsAreaFrame::SetInitialChildList(aPresContext, aListName, aChildList); - InitTextStr(aPresContext, PR_TRUE); + InitTextStr(aPresContext, PR_FALSE); } return rv; } @@ -1818,3 +2334,39 @@ nsComboboxControlFrame::RestoreState(nsIPresContext* aPresContext, nsIPresState* return mListControlFrame->RestoreStateInternal(aPresContext, aState); } + +NS_METHOD +nsComboboxControlFrame::Paint(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer) +{ +#ifdef NOISY + printf("%p paint layer %d at (%d, %d, %d, %d)\n", this, aWhichLayer, + aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); +#endif + nsAreaFrame::Paint(aPresContext,aRenderingContext,aDirtyRect,aWhichLayer); + + if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) { + nsRect rect(0, 0, mRect.width, mRect.height); + if (mDisplayFrame) { + aRenderingContext.PushState(); + PRBool clipEmpty; + nsRect clipRect; + mDisplayFrame->GetRect(clipRect); + aRenderingContext.SetClipRect(clipRect, nsClipCombine_kReplace, clipEmpty); + PaintChild(aPresContext, aRenderingContext, aDirtyRect, + mDisplayFrame, NS_FRAME_PAINT_LAYER_BACKGROUND); + PaintChild(aPresContext, aRenderingContext, aDirtyRect, + mDisplayFrame, NS_FRAME_PAINT_LAYER_FOREGROUND); + aRenderingContext.PopState(clipEmpty); + } + } + //nsRect rect(0, 0, 50000, 50000); + //aRenderingContext.SetColor(NS_RGB(255, 0, 0)); + //aRenderingContext.FillRect(rect); + + return NS_OK; +} + + diff --git a/mozilla/layout/forms/nsComboboxControlFrame.h b/mozilla/layout/forms/nsComboboxControlFrame.h index 1813dab4fe5..9546915ef8f 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.h +++ b/mozilla/layout/forms/nsComboboxControlFrame.h @@ -23,7 +23,17 @@ #ifndef nsComboboxControlFrame_h___ #define nsComboboxControlFrame_h___ -#define OPTIMIZE_RESIZE_RELOW +#ifdef DEBUG_evaughan +//#define DEBUG_rods +#endif + +#ifdef DEBUG_rods +//#define DO_REFLOW_DEBUG +//#define DO_REFLOW_COUNTER +//#define DO_UNCONSTRAINED_CHECK +//#define DO_PIXELS +//#define DO_NEW_REFLOW +#endif #include "nsAreaFrame.h" #include "nsIFormControlFrame.h" @@ -34,12 +44,14 @@ #include "nsIStatefulFrame.h" #include "nsIRollupListener.h" #include "nsIPresState.h" +#include "nsCSSFrameConstructor.h" class nsFormFrame; class nsIView; class nsStyleContext; class nsIHTMLContent; class nsIListControlFrame; +class nsITextContent; /** * Child list name indices @@ -84,6 +96,10 @@ public: nsGUIEvent* aEvent, nsEventStatus* aEventStatus); + NS_IMETHOD Paint(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer); #ifdef NS_DEBUG NS_IMETHOD GetFrameName(nsString& aResult) const; #endif @@ -144,8 +160,6 @@ public: NS_IMETHOD UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate, PRInt32 aNewIndex); NS_IMETHOD AbsolutelyPositionDropDown(); NS_IMETHOD GetAbsoluteRect(nsRect* aRect); - NS_IMETHOD SetFrameConstructor(nsCSSFrameConstructor *aConstructor) - { return NS_OK;} // not owner - do not addref! // nsISelectControlFrame NS_IMETHOD AddOption(nsIPresContext* aPresContext, PRInt32 index); @@ -162,8 +176,17 @@ public: //nsIRollupListener NS_IMETHOD Rollup(); + NS_IMETHOD SetFrameConstructor(nsCSSFrameConstructor *aConstructor) + { mFrameConstructor = aConstructor; return NS_OK;} // not owner - do not addref! protected: + NS_IMETHOD CreateDisplayFrame(nsIPresContext* aPresContext); + +#ifdef DO_NEW_REFLOW + NS_IMETHOD ReflowItems(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize); +#endif // nsHTMLContainerFrame virtual PRIntn GetSkipSides() const; @@ -187,7 +210,6 @@ public: nsIFrame *aFrame, nsRect& aAbsoluteTwipsRect, nsRect& aAbsolutePixelRect); - nsIFrame* GetDisplayFrame(nsIPresContext* aPresContext); protected: void ShowPopup(PRBool aShowPopup); void ShowList(nsIPresContext* aPresContext, PRBool aShowList); @@ -199,19 +221,32 @@ protected: NS_IMETHOD ToggleList(nsIPresContext* aPresContext); NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext); // Default to option 0 - 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 - nsIHTMLContent* mDisplayContent; // Anonymous content used to display the current selection - nsIHTMLContent* mButtonContent; // Anonymous content used to popup the dropdown list - PRBool mDroppedDown; // Current state of the dropdown list, PR_TRUE is dropped down - nsIFrame* mDisplayFrame; // frame to display selection - nsIFrame* mButtonFrame; // button frame - nsIFrame* mDropdownFrame; // dropdown list frame - nsIListControlFrame * mListControlFrame; // ListControl Interface for the dropdown frame - PRBool mIgnoreFocus; // Tells the combo to ignore all focus notifications + void ReflowCombobox(nsIPresContext * aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize, + nsReflowStatus& aStatus, + nsIFrame * aDisplayFrame, + nsIFrame * aDropDownBtn, + nscoord& aDisplayWidth, + nscoord aBtnWidth, + const nsMargin& aBorderPadding, + nscoord aFallBackHgt = -1, + PRBool aCheckHeight = PR_FALSE); + + 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 mDisplayContent; // Anonymous content used to display the current selection + nsIHTMLContent* mButtonContent; // Anonymous content used to popup the dropdown list + PRBool mDroppedDown; // Current state of the dropdown list, PR_TRUE is dropped down + nsIFrame* mDisplayFrame; // frame to display selection + nsIFrame* mButtonFrame; // button frame + nsIFrame* mDropdownFrame; // dropdown list frame + nsIFrame* mTextFrame; // display area frame + nsIListControlFrame * mListControlFrame; // ListControl Interface for the dropdown frame + PRBool mIgnoreFocus; // Tells the combo to ignore all focus notifications nsCOMPtr mPresState; // Need cache state when list is null @@ -219,6 +254,18 @@ protected: // Resize Reflow Optimization nsSize mCacheSize; nsSize mCachedMaxElementSize; + nsSize mCachedAvailableSize; + + nsSize mCachedUncDropdownSize; + nsSize mCachedUncComboSize; + + nscoord mItemDisplayWidth; + //nscoord mItemDisplayHeight; + nsCSSFrameConstructor* mFrameConstructor; + +#ifdef DO_REFLOW_COUNTER + PRInt32 mReflowId; +#endif private: NS_IMETHOD_(nsrefcnt) AddRef() { return NS_OK; } diff --git a/mozilla/layout/forms/nsGfxButtonControlFrame.cpp b/mozilla/layout/forms/nsGfxButtonControlFrame.cpp index 5ecbee68658..6a8407fcb91 100644 --- a/mozilla/layout/forms/nsGfxButtonControlFrame.cpp +++ b/mozilla/layout/forms/nsGfxButtonControlFrame.cpp @@ -612,7 +612,7 @@ nsGfxButtonControlFrame::SetSuggestedSize(nscoord aWidth, nscoord aHeight) { mSuggestedWidth = aWidth; mSuggestedHeight = aHeight; - mState |= NS_FRAME_IS_DIRTY; + //mState |= NS_FRAME_IS_DIRTY; return NS_OK; } diff --git a/mozilla/layout/forms/nsListControlFrame.cpp b/mozilla/layout/forms/nsListControlFrame.cpp index 7866ab948a7..8924f47e44f 100644 --- a/mozilla/layout/forms/nsListControlFrame.cpp +++ b/mozilla/layout/forms/nsListControlFrame.cpp @@ -354,6 +354,8 @@ nsListControlFrame::Reflow(nsIPresContext* aPresContext, { REFLOW_COUNTER_REQUEST(); + aStatus = NS_FRAME_COMPLETE; + #ifdef DO_REFLOW_DEBUG printf("%p ** Id: %d nsLCF::Reflow %d R: ", this, mReflowId, myCounter++); switch (aReflowState.reason) { @@ -381,7 +383,7 @@ nsListControlFrame::Reflow(nsIPresContext* aPresContext, PRBool bailOnHeight; // This ifdef is for turning off the optimization // so we can check timings against the old version -#if 0 +#if 1 nsFormControlFrame::SkipResizeReflow(mCacheSize, mCachedMaxElementSize, @@ -2637,9 +2639,9 @@ nsListControlFrame::IsLeftButton(nsIDOMEvent* aMouseEvent) nsresult nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) { -#ifdef DO_REFLOW_DEBUG - printf("--------------------------- MouseUp ----------------------------\n"); -#endif + NS_ASSERTION(aMouseEvent != nsnull, "aMouseEvent is null."); + + REFLOW_DEBUG_MSG("--------------------------- MouseUp ----------------------------\n"); if (nsFormFrame::GetDisabled(this)) { return NS_OK; @@ -2681,13 +2683,20 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) mButtonDown = PR_FALSE; CaptureMouseEvents(mPresContext, PR_FALSE); UpdateSelection(PR_TRUE, PR_FALSE, mContent); +#if 0 // XXX - this is a partial fix for Bug 29990 + if (mSelectedIndex != mStartExtendedIndex) { + mEndExtendedIndex = mSelectedIndex; + } +#endif } - aMouseEvent->PreventDefault(); - aMouseEvent->PreventCapture(); - aMouseEvent->PreventBubble(); - return NS_ERROR_FAILURE; - //return NS_OK; + if (IsInDropDownMode() == PR_TRUE) { + aMouseEvent->PreventDefault(); + aMouseEvent->PreventCapture(); + aMouseEvent->PreventBubble(); + return NS_ERROR_FAILURE; //consumes event + } + return NS_OK; } //--------------------------------------------------------- @@ -2747,9 +2756,10 @@ nsListControlFrame::GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent, nsresult nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent) { -#ifdef DO_REFLOW_DEBUG - printf("--------------------------- MouseDown ----------------------------\n"); -#endif + NS_ASSERTION(aMouseEvent != nsnull, "aMouseEvent is null."); + + REFLOW_DEBUG_MSG("--------------------------- MouseDown ----------------------------\n"); + if (nsFormFrame::GetDisabled(this)) { return NS_OK; } @@ -2865,6 +2875,8 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent) nsresult nsListControlFrame::MouseMove(nsIDOMEvent* aMouseEvent) { + NS_ASSERTION(aMouseEvent != nsnull, "aMouseEvent is null."); + if (mComboboxFrame) { // Synonym for IsInDropDownMode() PRBool isDroppedDown = PR_FALSE; mComboboxFrame->IsDroppedDown(&isDroppedDown); @@ -2884,7 +2896,26 @@ nsListControlFrame::MouseMove(nsIDOMEvent* aMouseEvent) } } } + } else +#if 0 // XXX - this is a partial fix for Bug 29990 + if (mButtonDown) { + PRBool multipleSelections = PR_FALSE; + GetMultiple(&multipleSelections); + if (multipleSelections) { + PRInt32 oldIndex; + PRInt32 curIndex = mSelectedIndex; + if (NS_SUCCEEDED(GetIndexFromDOMEvent(aMouseEvent, oldIndex, curIndex))) { + PRBool optionIsDisabled; + if (NS_OK == IsTargetOptionDisabled(optionIsDisabled)) { + if (!optionIsDisabled) { + mSelectedIndex = curIndex; + SetContentSelected(mSelectedIndex, PR_TRUE); + } + } + } + } } +#endif return NS_OK; } @@ -2966,6 +2997,8 @@ nsListControlFrame::ScrollToFrame(nsIContent* aOptElement) nsresult nsListControlFrame::KeyDown(nsIDOMEvent* aKeyEvent) { + NS_ASSERTION(aKeyEvent != nsnull, "keyEvent is null."); + if (nsFormFrame::GetDisabled(this)) return NS_OK; @@ -2973,9 +3006,9 @@ nsListControlFrame::KeyDown(nsIDOMEvent* aKeyEvent) if (keyEvent) { PRUint32 code; //uiEvent->GetCharCode(&code); - //printf("%c %d ", code, code); + //REFLOW_DEBUG_MSG3("%c %d ", code, code); keyEvent->GetKeyCode(&code); - //printf("%c %d\n", code, code); + REFLOW_DEBUG_MSG3("KeyCode: %c %d\n", code, code); nsresult rv = NS_ERROR_FAILURE; nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); @@ -2988,83 +3021,93 @@ nsListControlFrame::KeyDown(nsIDOMEvent* aKeyEvent) rv = NS_OK; } else { - if (code == nsIDOMKeyEvent::DOM_VK_UP || code == nsIDOMKeyEvent::DOM_VK_LEFT) { -#ifdef DEBUG_rodsXXX - printf("DOM_VK_UP mSelectedIndex: %d ", mSelectedIndex); -#endif - if (mSelectedIndex > 0) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex--; - SingleSelection(); - if (nsnull != mComboboxFrame && mIsAllFramesHere) { - mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event - } - } -#ifdef DEBUG_rodsXXX - printf(" After: %d\n", mSelectedIndex); -#endif - } if (code == nsIDOMKeyEvent::DOM_VK_DOWN || code == nsIDOMKeyEvent::DOM_VK_RIGHT) { -#ifdef DEBUG_rodsXXX - printf("DOM_VK_DOWN mSelectedIndex: %d ", mSelectedIndex); -#endif - if ((mSelectedIndex+1) < (PRInt32)numOptions) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex++; - SingleSelection(); - if (nsnull != mComboboxFrame && mIsAllFramesHere) { - mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event - } - } -#ifdef DEBUG_rodsXXX - printf(" After: %d\n", mSelectedIndex); -#endif - } if (code == nsIDOMKeyEvent::DOM_VK_RETURN) { - if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { - mComboboxFrame->ListWasSelected(mPresContext); - } else { - UpdateSelection(PR_TRUE, PR_FALSE, mContent); - } - } if (code == nsIDOMKeyEvent::DOM_VK_ESCAPE) { - if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { - ResetSelectedItem(); - mComboboxFrame->ListWasSelected(mPresContext); - } - } else { // Select option with this as the first character - // XXX Not I18N compliant - PRInt32 selectedIndex = (mSelectedIndex == kNothingSelected ? 0 : mSelectedIndex+1) % numOptions; - PRInt32 startedAtIndex = selectedIndex; - PRBool loopedAround = PR_FALSE; - while ((selectedIndex < startedAtIndex && loopedAround) || !loopedAround) { - nsCOMPtroptionElement = getter_AddRefs(GetOption(*options, selectedIndex)); - if (optionElement) { - nsAutoString text; - if (NS_OK == optionElement->GetText(text)) { - char * buf = text.ToNewCString(); - char c = buf[0]; - delete [] buf; - if (c == (char)code) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex = selectedIndex; - SingleSelection(); - if (nsnull != mComboboxFrame && mIsAllFramesHere) { - mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event - } - break; - } + switch (code) { + + case nsIDOMKeyEvent::DOM_VK_UP: + case nsIDOMKeyEvent::DOM_VK_LEFT: { + REFLOW_DEBUG_MSG2("DOM_VK_UP mSelectedIndex: %d ", mSelectedIndex); + if (mSelectedIndex > 0) { + mOldSelectedIndex = mSelectedIndex; + mSelectedIndex--; + SingleSelection(); + if (nsnull != mComboboxFrame && mIsAllFramesHere) { + mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event + } else { + UpdateSelection(PR_TRUE, PR_FALSE, GetOptionContent(mSelectedIndex)); // dispatch event } } - selectedIndex++; - if (selectedIndex == (PRInt32)numOptions) { - selectedIndex = 0; - loopedAround = PR_TRUE; + REFLOW_DEBUG_MSG2(" After: %d\n", mSelectedIndex); + } break; + + case nsIDOMKeyEvent::DOM_VK_DOWN: + case nsIDOMKeyEvent::DOM_VK_RIGHT: { + REFLOW_DEBUG_MSG2("DOM_VK_DOWN mSelectedIndex: %d ", mSelectedIndex); + if ((mSelectedIndex+1) < (PRInt32)numOptions) { + mOldSelectedIndex = mSelectedIndex; + mSelectedIndex++; + SingleSelection(); + if (nsnull != mComboboxFrame && mIsAllFramesHere) { + mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event + } else { + UpdateSelection(PR_TRUE, PR_FALSE, GetOptionContent(mSelectedIndex)); // dispatch event + } } + REFLOW_DEBUG_MSG2(" After: %d\n", mSelectedIndex); + } break; - } // while - } - } + case nsIDOMKeyEvent::DOM_VK_RETURN: { + if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { + mComboboxFrame->ListWasSelected(mPresContext); + } else { + UpdateSelection(PR_TRUE, PR_FALSE, mContent); + } + } break; + + case nsIDOMKeyEvent::DOM_VK_ESCAPE: { + if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { + ResetSelectedItem(); + mComboboxFrame->ListWasSelected(mPresContext); + } + } break; + + default: { // Select option with this as the first character + // XXX Not I18N compliant + PRInt32 selectedIndex = (mSelectedIndex == kNothingSelected ? 0 : mSelectedIndex+1) % numOptions; + PRInt32 startedAtIndex = selectedIndex; + PRBool loopedAround = PR_FALSE; + while ((selectedIndex < startedAtIndex && loopedAround) || !loopedAround) { + nsCOMPtroptionElement = getter_AddRefs(GetOption(*options, selectedIndex)); + if (optionElement) { + nsAutoString text; + if (NS_OK == optionElement->GetText(text)) { + char * buf = text.ToNewCString(); + char c = buf[0]; + delete [] buf; + if (c == (char)code) { + mOldSelectedIndex = mSelectedIndex; + mSelectedIndex = selectedIndex; + SingleSelection(); + if (nsnull != mComboboxFrame && mIsAllFramesHere) { + mComboboxFrame->UpdateSelection(PR_TRUE, PR_TRUE, mSelectedIndex); // don't dispatch event + } else { + UpdateSelection(PR_TRUE, PR_FALSE, GetOptionContent(mSelectedIndex)); // dispatch event + } + break; + } + } + } + selectedIndex++; + if (selectedIndex == (PRInt32)numOptions) { + selectedIndex = 0; + loopedAround = PR_TRUE; + } + + } // while + } break;//case + } // switch + } // if } } - return NS_OK; } diff --git a/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp b/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp index 56dedbe5143..b2eefeff432 100644 --- a/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp @@ -22,6 +22,7 @@ */ #include "nsCOMPtr.h" #include "nsComboboxControlFrame.h" +#include "nsIFrameManager.h" #include "nsFormFrame.h" #include "nsFormControlFrame.h" #include "nsIHTMLContent.h" @@ -32,11 +33,11 @@ #include "nsINameSpaceManager.h" #include "nsLayoutAtoms.h" #include "nsIDOMElement.h" -#include "nsListControlFrame.h" #include "nsIListControlFrame.h" #include "nsIDOMHTMLCollection.h" #include "nsIDOMHTMLSelectElement.h" #include "nsIDOMHTMLOptionElement.h" +#include "nsIDOMNSHTMLOptionCollection.h" #include "nsIPresShell.h" #include "nsIPresState.h" #include "nsISupportsArray.h" @@ -51,6 +52,16 @@ #include "nsISelectControlFrame.h" #include "nsISupportsPrimitives.h" #include "nsIComponentManager.h" +#include "nsIDOMMouseListener.h" +#include "nsITextContent.h" +#include "nsTextFragment.h" +#include "nsCSSFrameConstructor.h" +#include "nsIDocument.h" + +#ifdef DO_NEW_REFLOW +#include "nsIFontMetrics.h" +#endif + static NS_DEFINE_IID(kIFormControlFrameIID, NS_IFORMCONTROLFRAME_IID); static NS_DEFINE_IID(kIComboboxControlFrameIID, NS_ICOMBOBOXCONTROLFRAME_IID); @@ -62,8 +73,7 @@ static NS_DEFINE_IID(kIDOMNodeIID, NS_IDOMNODE_IID); static NS_DEFINE_IID(kIPrivateDOMEventIID, NS_IPRIVATEDOMEVENT_IID); // Drop down list event management. -// The combo box uses the following strategy for managing the -// drop-down list. +// The combo box uses the following strategy for managing the drop-down list. // If the combo box or it's arrow button is clicked on the drop-down list is displayed // If mouse exit's the combo box with the drop-down list displayed the drop-down list // is asked to capture events @@ -78,6 +88,8 @@ static NS_DEFINE_IID(kIPrivateDOMEventIID, NS_IPRIVATEDOMEVENT_IID); const char * kMozDropdownActive = "-moz-dropdown-active"; +const PRInt32 kSizeNotSet = -1; + nsresult NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aStateFlags) { @@ -98,6 +110,113 @@ NS_NewComboboxControlFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUin return NS_OK; } +//----------------------------------------------------------- +// Reflow Debugging Macros +// These let us "see" how many reflow counts are happening +//----------------------------------------------------------- +#ifdef DO_REFLOW_COUNTER + +#define MAX_REFLOW_CNT 1024 +static PRInt32 gTotalReqs = 0;; +static PRInt32 gTotalReflows = 0;; +static PRInt32 gReflowControlCntRQ[MAX_REFLOW_CNT]; +static PRInt32 gReflowControlCnt[MAX_REFLOW_CNT]; +static PRInt32 gReflowInx = -1; + +#define REFLOW_COUNTER() \ + if (mReflowId > -1) \ + gReflowControlCnt[mReflowId]++; + +#define REFLOW_COUNTER_REQUEST() \ + if (mReflowId > -1) \ + gReflowControlCntRQ[mReflowId]++; + +#define REFLOW_COUNTER_DUMP(__desc) \ + if (mReflowId > -1) {\ + gTotalReqs += gReflowControlCntRQ[mReflowId];\ + gTotalReflows += gReflowControlCnt[mReflowId];\ + printf("** Id:%5d %s RF: %d RQ: %d %d/%d %5.2f\n", \ + mReflowId, (__desc), \ + gReflowControlCnt[mReflowId], \ + gReflowControlCntRQ[mReflowId],\ + gTotalReflows, gTotalReqs, float(gTotalReflows)/float(gTotalReqs)*100.0f);\ + } + +#define REFLOW_COUNTER_INIT() \ + if (gReflowInx < MAX_REFLOW_CNT) { \ + gReflowInx++; \ + mReflowId = gReflowInx; \ + gReflowControlCnt[mReflowId] = 0; \ + gReflowControlCntRQ[mReflowId] = 0; \ + } else { \ + mReflowId = -1; \ + } + +// reflow messages +#define REFLOW_DEBUG_MSG(_msg1) printf((_msg1)) +#define REFLOW_DEBUG_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) +#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) +#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) + +#else //------------- + +#define REFLOW_COUNTER_REQUEST() +#define REFLOW_COUNTER() +#define REFLOW_COUNTER_DUMP(__desc) +#define REFLOW_COUNTER_INIT() + +#define REFLOW_DEBUG_MSG(_msg) +#define REFLOW_DEBUG_MSG2(_msg1, _msg2) +#define REFLOW_DEBUG_MSG3(_msg1, _msg2, _msg3) +#define REFLOW_DEBUG_MSG4(_msg1, _msg2, _msg3, _msg4) + + +#endif + +//------------------------------------------ +// This is for being VERY noisy +//------------------------------------------ +#ifdef DO_VERY_NOISY +#define REFLOW_NOISY_MSG(_msg1) printf((_msg1)) +#define REFLOW_NOISY_MSG2(_msg1, _msg2) printf((_msg1), (_msg2)) +#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) printf((_msg1), (_msg2), (_msg3)) +#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) printf((_msg1), (_msg2), (_msg3), (_msg4)) +#else +#define REFLOW_NOISY_MSG(_msg) +#define REFLOW_NOISY_MSG2(_msg1, _msg2) +#define REFLOW_NOISY_MSG3(_msg1, _msg2, _msg3) +#define REFLOW_NOISY_MSG4(_msg1, _msg2, _msg3, _msg4) +#endif + +//------------------------------------------ +// Displays value in pixels or twips +//------------------------------------------ +#ifdef DO_PIXELS +#define PX(__v) __v / 15 +#else +#define PX(__v) __v +#endif + +//------------------------------------------ +// Asserts if we return a desired size that +// doesn't correctly match the mComputedWidth +//------------------------------------------ +#ifdef DO_UNCONSTRAINED_CHECK +#define UNCONSTRAINED_CHECK() \ +if (aReflowState.mComputedWidth != NS_UNCONSTRAINEDSIZE) { \ + nscoord width = aDesiredSize.width - borderPadding.left - borderPadding.right; \ + if (width != aReflowState.mComputedWidth) { \ + printf("aDesiredSize.width %d %d != aReflowState.mComputedWidth %d\n", aDesiredSize.width, width, aReflowState.mComputedWidth); \ + } \ + NS_ASSERTION(width == aReflowState.mComputedWidth, "Returning bad value when constrained!"); \ +} +#else +#define UNCONSTRAINED_CHECK() +#endif +//------------------------------------------------------ +//-- Done with macros +//------------------------------------------------------ + nsComboboxControlFrame::nsComboboxControlFrame() : nsAreaFrame() { @@ -105,7 +224,6 @@ nsComboboxControlFrame::nsComboboxControlFrame() mFormFrame = nsnull; mListControlFrame = nsnull; mTextStr = ""; - mDisplayContent = nsnull; mButtonContent = nsnull; mDroppedDown = PR_FALSE; mDisplayFrame = nsnull; @@ -114,23 +232,33 @@ nsComboboxControlFrame::nsComboboxControlFrame() mIgnoreFocus = PR_FALSE; mSelectedIndex = -1; - mCacheSize.width = -1; - mCacheSize.height = -1; - mCachedMaxElementSize.width = -1; - mCachedMaxElementSize.height = -1; + mCacheSize.width = kSizeNotSet; + mCacheSize.height = kSizeNotSet; + mCachedMaxElementSize.width = kSizeNotSet; + mCachedMaxElementSize.height = kSizeNotSet; + mCachedAvailableSize.width = kSizeNotSet; + mCachedAvailableSize.height = kSizeNotSet; + mCachedUncDropdownSize.width = kSizeNotSet; + mCachedUncDropdownSize.height = kSizeNotSet; + mCachedUncComboSize.width = kSizeNotSet; + mCachedUncComboSize.height = kSizeNotSet; + mItemDisplayWidth = 0; //Shrink the area around it's contents //SetFlags(NS_BLOCK_SHRINK_WRAP); + + REFLOW_COUNTER_INIT() } //-------------------------------------------------------------- nsComboboxControlFrame::~nsComboboxControlFrame() { + REFLOW_COUNTER_DUMP("nsCCF"); + if (mFormFrame) { mFormFrame->RemoveFormControlFrame(*this); mFormFrame = nsnull; } NS_IF_RELEASE(mPresContext); - NS_IF_RELEASE(mDisplayContent); NS_IF_RELEASE(mButtonContent); nsFormControlFrame::RegUnRegAccessKey(mPresContext, NS_STATIC_CAST(nsIFrame*, this), PR_FALSE); @@ -211,8 +339,9 @@ nsComboboxControlFrame::MakeSureSomethingIsSelected(nsIPresContext* aPresContext // Set listbox selection to first item in the list box rv = fcFrame->SetProperty(aPresContext, nsHTMLAtoms::selectedindex, "0"); mSelectedIndex = 0; + } else { + UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // Needed to reflow when removing last option } - UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // Needed to reflow when removing last option } // Don't NS_RELEASE fcFrame here as it isn't addRef'd in the QI (???) @@ -231,9 +360,7 @@ nsComboboxControlFrame::InitTextStr(nsIPresContext* aPresContext, PRBool aUpdate mListControlFrame->GetSelectedItem(mTextStr); // Update the display by setting the value attribute - mDisplayContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, mTextStr, aUpdate); - //nsIFrame* buttonFrame = GetDisplayFrame(aPresContext); - //nsFormControlHelper::ForceDrawFrame(buttonFrame); + mDisplayContent->SetText(mTextStr.GetUnicode(), mTextStr.Length(), aUpdate); } //-------------------------------------------------------------- @@ -427,12 +554,12 @@ nsComboboxControlFrame::MouseClicked(nsIPresContext* aPresContext) nsresult nsComboboxControlFrame::ReflowComboChildFrame(nsIFrame* aFrame, - nsIPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus, - nscoord aAvailableWidth, - nscoord aAvailableHeight) + nsIPresContext* aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus, + nscoord aAvailableWidth, + nscoord aAvailableHeight) { // Constrain the child's width and height to aAvailableWidth and aAvailableHeight nsSize availSize(aAvailableWidth, aAvailableHeight); @@ -490,17 +617,6 @@ nsComboboxControlFrame::GetButtonFrame(nsIPresContext* aPresContext) return mButtonFrame; } -nsIFrame* -nsComboboxControlFrame::GetDisplayFrame(nsIPresContext* aPresContext) -{ - if (mDisplayFrame == nsnull) { - NS_ASSERTION(mDisplayContent != nsnull, "nsComboboxControlFrame mDisplayContent is null"); - GetPrimaryComboFrame(aPresContext, mDisplayContent, &mDisplayFrame); - } - - return mDisplayFrame; -} - nsIFrame* nsComboboxControlFrame::GetDropdownFrame() { @@ -578,12 +694,10 @@ nsComboboxControlFrame::PositionDropdown(nsIPresContext* aPresContext, nsRect currentRect; dropdownFrame->GetRect(currentRect); - //if (currentRect != dropdownRect) { - dropdownFrame->SetRect(aPresContext, dropdownRect); + dropdownFrame->SetRect(aPresContext, dropdownRect); #ifdef DEBUG_rodsXXXXXX - printf("%d Position Dropdown at: %d %d %d %d\n", counter++, dropdownRect.x, dropdownRect.y, dropdownRect.width, dropdownRect.height); + printf("%d Position Dropdown at: %d %d %d %d\n", counter++, dropdownRect.x, dropdownRect.y, dropdownRect.width, dropdownRect.height); #endif - //} return rv; } @@ -667,55 +781,435 @@ nsComboboxControlFrame::GetAbsoluteFramePosition(nsIPresContext* aPresContext, return rv; } -//------------------------------------------------------------------------------- -//------------------------------------------------------------------------------- -// Old/Current Way of Doing Reflow -//------------------------------------------------------------------------------- -//------------------------------------------------------------------------------- -#define DO_OLD_REFLOW +//////////////////////////////////////////////////////////////// +// Experimental Reflow +//////////////////////////////////////////////////////////////// +#if defined(DO_NEW_REFLOW) || defined(DO_REFLOW_COUNTER) +//--------------------------------------------------------- +// Returns the nsIDOMHTMLOptionElement for a given index +// in the select's collection +//--------------------------------------------------------- +static nsIDOMHTMLOptionElement* +GetOption(nsIDOMHTMLCollection& aCollection, PRInt32 aIndex) +{ + nsIDOMNode* node = nsnull; + if (NS_SUCCEEDED(aCollection.Item(aIndex, &node))) { + if (nsnull != node) { + nsIDOMHTMLOptionElement* option = nsnull; + node->QueryInterface(NS_GET_IID(nsIDOMHTMLOptionElement), (void**)&option); + NS_RELEASE(node); + return option; + } + } + return nsnull; +} +//--------------------------------------------------------- +// for a given piece of content it returns nsIDOMHTMLSelectElement object +// or null +//--------------------------------------------------------- +static nsIDOMHTMLSelectElement* +GetSelect(nsIContent * aContent) +{ + nsIDOMHTMLSelectElement* selectElement = nsnull; + nsresult result = aContent->QueryInterface(NS_GET_IID(nsIDOMHTMLSelectElement), + (void**)&selectElement); + if (NS_SUCCEEDED(result) && selectElement) { + return selectElement; + } else { + return nsnull; + } +} +//--------------------------------------------------------- +//--------------------------------------------------------- +// This returns the collection for nsIDOMHTMLSelectElement or +// the nsIContent object is the select is null (AddRefs) +//--------------------------------------------------------- +static nsIDOMHTMLCollection* +GetOptions(nsIContent * aContent, nsIDOMHTMLSelectElement* aSelect = nsnull) +{ + nsIDOMNSHTMLOptionCollection* optCol = nsnull; + nsIDOMHTMLCollection* options = nsnull; + if (!aSelect) { + nsCOMPtr selectElement = getter_AddRefs(GetSelect(aContent)); + if (selectElement) { + selectElement->GetOptions(&optCol); // AddRefs (1) + } + } else { + aSelect->GetOptions(&optCol); // AddRefs (1) + } + if (optCol) { + nsresult res = optCol->QueryInterface(NS_GET_IID(nsIDOMHTMLCollection), (void **)&options); // AddRefs (2) + NS_RELEASE(optCol); // Release (1) + } + return options; +} -#ifdef DO_OLD_REFLOW +#ifdef DO_NEW_REFLOW +NS_IMETHODIMP +nsComboboxControlFrame::ReflowItems(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize) +{ + //printf("*****************\n"); + const nsStyleFont* dspFont; + mDisplayFrame->GetStyleData(eStyleStruct_Font, (const nsStyleStruct *&)dspFont); + nsCOMPtr deviceContext; + aPresContext->GetDeviceContext(getter_AddRefs(deviceContext)); + NS_ASSERTION(deviceContext, "Couldn't get the device context"); + nsIFontMetrics * fontMet; + deviceContext->GetMetricsFor(dspFont->mFont, fontMet); -#ifdef DEBUG_rodsXXX -static int myCounter = 0; + nscoord visibleHeight; + //nsCOMPtr fontMet; + //nsresult res = nsFormControlHelper::GetFrameFontFM(aPresContext, this, getter_AddRefs(fontMet)); + if (fontMet) { + fontMet->GetHeight(visibleHeight); + } + + nsAutoString maxStr; + nscoord maxWidth = 0; + //nsIRenderingContext * rc = aReflowState.rendContext; + nsresult rv = NS_ERROR_FAILURE; + nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); + if (options) { + PRUint32 numOptions; + options->GetLength(&numOptions); + //printf("--- Num of Items %d ---\n", numOptions); + for (PRUint32 i=0;i optionElement = getter_AddRefs(GetOption(*options, i)); + if (optionElement) { + nsAutoString text; + rv = optionElement->GetLabel(text); + if (NS_CONTENT_ATTR_HAS_VALUE != rv || 0 == text.Length()) { + if (NS_OK == optionElement->GetText(text)) { + nscoord width; + aReflowState.rendContext->GetWidth(text, width); + if (width > maxWidth) { + maxStr = text; + maxWidth = width; + } + //maxWidth = PR_MAX(width, maxWidth); + //printf("[%d] - %d %s \n", i, width, text.ToNewCString()); + } + } + } + } + } + if (maxWidth == 0) { + maxWidth = 11 * 15; + } + char * str = maxStr.ToNewCString(); + printf("id: %d maxWidth %d [%s]\n", mReflowId, maxWidth, str); + delete [] str; + + // get the borderPadding for the display area + const nsStyleSpacing* dspSpacing; + mDisplayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); + nsMargin dspBorderPadding; + dspBorderPadding.SizeTo(0, 0, 0, 0); + dspSpacing->CalcBorderPaddingFor(mDisplayFrame, dspBorderPadding); + + nscoord frmWidth = maxWidth+dspBorderPadding.left+dspBorderPadding.right+ + aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; + nscoord frmHeight = visibleHeight+dspBorderPadding.top+dspBorderPadding.bottom+ + aReflowState.mComputedBorderPadding.top + aReflowState.mComputedBorderPadding.bottom; + +#if 1 + aDesiredSize.width = frmWidth; + aDesiredSize.height = frmHeight; +#else + printf("Size %d,%d %d,%d %d,%d %d,%d\n", + frmWidth, frmHeight, + aDesiredSize.width, aDesiredSize.height, + frmWidth-aDesiredSize.width, frmHeight-aDesiredSize.height, + (frmWidth-aDesiredSize.width)/15, (frmHeight-aDesiredSize.height)/15); +#endif + NS_RELEASE(fontMet); + return NS_OK; +} #endif +#endif + +//------------------------------------------------------------------ +// This Method reflow just the contents of the ComboBox +// The contents are a Block frame containing a Text Frame - This is the display area +// and then the GfxButton - The dropdown button +//-------------------------------------------------------------------------- +void +nsComboboxControlFrame::ReflowCombobox(nsIPresContext * aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize, + nsReflowStatus& aStatus, + nsIFrame * aDisplayFrame, + nsIFrame * aDropDownBtn, + nscoord& aDisplayWidth, + nscoord aBtnWidth, + const nsMargin& aBorderPadding, + nscoord aFallBackHgt, + PRBool aCheckHeight) +{ + // start out by using the cached height + // XXX later this will change when we better handle constrained height + nscoord dispHeight = mCacheSize.height - aBorderPadding.top - aBorderPadding.bottom; + nscoord dispWidth = aDisplayWidth; + + REFLOW_NOISY_MSG3("+++1 AdjustCombo DW:%d DH:%d ", PX(dispWidth), PX(dispHeight)); + REFLOW_NOISY_MSG3("BW:%d BH:%d ", PX(aBtnWidth), PX(dispHeight)); + REFLOW_NOISY_MSG3("mCacheSize.height:%d - %d\n", PX(mCacheSize.height), PX((aBorderPadding.top + aBorderPadding.bottom))); + + // get the border and padding for the DisplayArea (block frame & textframe) + const nsStyleSpacing* dspSpacing; + aDisplayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); + nsMargin dspBorderPadding; + dspBorderPadding.SizeTo(0, 0, 0, 0); + dspSpacing->CalcBorderPaddingFor(aDisplayFrame, dspBorderPadding); + + // adjust the height + if (mCacheSize.height == kSizeNotSet) { + if (aFallBackHgt == kSizeNotSet) { + NS_ASSERTION(aFallBackHgt != kSizeNotSet, "Fallback can't be kSizeNotSet when mCacheSize.height == kSizeNotSet"); + } else { + dispHeight = aFallBackHgt; + REFLOW_NOISY_MSG2("+++3 Adding (dspBorderPadding.top + dspBorderPadding.bottom): %d\n", (dspBorderPadding.top + dspBorderPadding.bottom)); + dispHeight += (dspBorderPadding.top + dspBorderPadding.bottom); + } + } + + //////////////////////////////////////////////////////////////////////////// + // XXX - This is really bad, but at the moment I am NOT adding in the left padding + // we need to be able to find out what the left padding is on an option + // and subtract that out before we can add in our padding. + REFLOW_NOISY_MSG2("+++3 Adding: %d\n", dspBorderPadding.right); + dispWidth += dspBorderPadding.left + dspBorderPadding.right; + + REFLOW_NOISY_MSG3("+++2 AdjustCombo DW:%d DH:%d ", PX(dispWidth), PX(dispHeight)); + REFLOW_NOISY_MSG3(" BW:%d BH:%d\n", PX(aBtnWidth), PX(dispHeight)); + + // This sets the button to be a specific size + // so no matter what it reflows at these values + SetChildFrameSize(aDropDownBtn, aBtnWidth, dispHeight); + + // now that we know what the overall display width & height will be + // set up a new reflow state and reflow the area frame at that size + nsSize availSize(dispWidth + aBorderPadding.left + aBorderPadding.right, + dispHeight + aBorderPadding.top + aBorderPadding.bottom); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState, this, availSize); + kidReflowState.mComputedWidth = dispWidth; + kidReflowState.mComputedHeight = dispHeight; + + // do reflow + nsAreaFrame::Reflow(aPresContext, aDesiredSize, kidReflowState, aStatus); + + ///////////////////////////////////////////////////////// + // The DisplayFrame is a Block frame containing a TextFrame + // and it is completely anonymous, so we must manually reflow it + nsSize txtAvailSize(dispWidth - aBtnWidth, dispHeight); + nsHTMLReflowMetrics txtKidSize(&txtAvailSize); + nsHTMLReflowState txtKidReflowState(aPresContext, aReflowState, aDisplayFrame, txtAvailSize); + + aDisplayFrame->WillReflow(aPresContext); + aDisplayFrame->MoveTo(aPresContext, aBorderPadding.left, aBorderPadding.top); + nsIView* view; + aDisplayFrame->GetView(aPresContext, &view); + if (view) { + nsAreaFrame::PositionFrameView(aPresContext, aDisplayFrame, view); + } + nsReflowStatus status; + nsresult rv = aDisplayFrame->Reflow(aPresContext, txtKidSize, txtKidReflowState, status); + + ///////////////////////////////////////////////////////// + // If we are Constrained then the AreaFrame Reflow is the correct size + // if we are unconstrained then + //if (aReflowState.mComputedWidth == NS_UNCONSTRAINEDSIZE) { + // aDesiredSize.width += txtKidSize.width; + //} + + // Apparently, XUL lays out differently than HTML + // (the code above works for HTML and not XUL), + // so instead of using the above calculation + // I just set it to what it should be. + aDesiredSize.width = availSize.width; + //aDesiredSize.height = availSize.height; + + // now we need to adjust layout, because the AreaFrame + // doesn't position things exactly where we want them + nscoord insideWidth = dispWidth; + nscoord insideHeight = aDesiredSize.height - aBorderPadding.top - aBorderPadding.bottom; + + // the gets for the Display "block" frame and for the button + // make adjustments + nsRect buttonRect; + nsRect displayRect; + aDisplayFrame->GetRect(displayRect); + aDropDownBtn->GetRect(buttonRect); + // set the display rect to be left justifed and + // fills the entire area except the button + nscoord x = aBorderPadding.left; + displayRect.x = x; + displayRect.y = aBorderPadding.top; + displayRect.height = insideHeight; + displayRect.width = dispWidth - aBtnWidth; + aDisplayFrame->SetRect(aPresContext, displayRect); + x += displayRect.width; + + // right justify the button + buttonRect.x = x; + buttonRect.y = aBorderPadding.top; + buttonRect.height = insideHeight; + buttonRect.width = aBtnWidth; + aDropDownBtn->SetRect(aPresContext, buttonRect); + + // since we have changed the height of the button + // make sure it has these new values + // XXX this may not be needed anymore + SetChildFrameSize(aDropDownBtn, aBtnWidth, aDesiredSize.height); + + + REFLOW_NOISY_MSG3("**AdjustCombobox - Reflow: WW: %d HH: %d\n", aDesiredSize.width, aDesiredSize.height); + + // Now cache the available height as our height without border and padding + // This sets up the optimization for if a new available width comes in and we are equal or + // less than it we can bail + if (aDesiredSize.width != mCacheSize.width || aDesiredSize.height != mCacheSize.height) { + if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) { + mCachedAvailableSize.width = aDesiredSize.width - (aBorderPadding.left + aBorderPadding.right); + } + if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { + mCachedAvailableSize.height = aDesiredSize.height - (aBorderPadding.top + aBorderPadding.bottom); + } + nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedMaxElementSize, aDesiredSize); + } + + /////////////////////////////////////////////////////////////////// + // This is an experimental reflow that is turned off in the build +#ifdef DO_NEW_REFLOW_X + ReflowItems(aPresContext, aReflowState, aDesiredSize); +#endif + /////////////////////////////////////////////////////////////////// +} + +//---------------------------------------------------------- +// +//---------------------------------------------------------- +#ifdef DO_REFLOW_DEBUG +static int myCounter = 0; + +static void printSize(char * aDesc, nscoord aSize) +{ + printf(" %s: ", aDesc); + if (aSize == NS_UNCONSTRAINEDSIZE) { + printf("UC"); + } else { + printf("%d", PX(aSize)); + } +} +#endif + +//------------------------------------------------------------------- +//-- Main Reflow for the Combobox +//------------------------------------------------------------------- NS_IMETHODIMP nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { -#ifdef DEBUG_rodsXXX - printf("****** nsComboboxControlFrame::Reflow %d Reason: ", myCounter++); + aStatus = NS_FRAME_COMPLETE; + + REFLOW_COUNTER_REQUEST(); + +#ifdef DO_REFLOW_DEBUG + printf("-------------Starting Combobox Reflow ----------------------------\n"); + printf("%p ** Id: %d nsCCF::Reflow %d R: ", this, mReflowId, myCounter++); switch (aReflowState.reason) { case eReflowReason_Initial: - printf("eReflowReason_Initial\n");break; + printf("Ini");break; case eReflowReason_Incremental: - printf("eReflowReason_Incremental\n");break; + printf("Inc");break; case eReflowReason_Resize: - printf("eReflowReason_Resize\n"); + printf("Rsz");break; + case eReflowReason_StyleChange: + printf("Sty");break; + case eReflowReason_Dirty: + printf("Drt "); break; - case eReflowReason_StyleChange:printf("eReflowReason_StyleChange\n");break; + default:printf("%d", aReflowState.reason);break; } + + printSize("AW", aReflowState.availableWidth); + printSize("AH", aReflowState.availableHeight); + printSize("CW", aReflowState.mComputedWidth); + printSize("CH", aReflowState.mComputedHeight); + + nsCOMPtr optionsTemp = getter_AddRefs(GetOptions(mContent)); + PRUint32 numOptions; + optionsTemp->GetLength(&numOptions); + printSize("NO", (nscoord)numOptions); + + printf(" *\n"); + #endif -#if 0 - nsresult skiprv = nsFormControlFrame::SkipResizeReflow(mCacheSize, mCachedMaxElementSize, aPresContext, - aDesiredSize, aReflowState, aStatus); - if (NS_SUCCEEDED(skiprv)) { - return skiprv; - } -#endif + PRBool bailOnWidth; + PRBool bailOnHeight; + + // Do initial check to see if we can bail out + // If it is an Initial or Incremental Reflow we never bail out here + // XXX right now we only bail if the width meets the criteria + // + // We bail: + // if mComputedWidth == NS_UNCONSTRAINEDSIZE and + // availableWidth == NS_UNCONSTRAINEDSIZE and + // we have cached an available size + // + // We bail: + // if mComputedWidth == NS_UNCONSTRAINEDSIZE and + // availableWidth != NS_UNCONSTRAINEDSIZE and + // availableWidth minus its border equals our cached available size + // + // We bail: + // if mComputedWidth != NS_UNCONSTRAINEDSIZE and + // cached availableSize.width == aReflowState.mComputedWidth and + // cached AvailableSize.width == aCacheSize.width + // + // NOTE: this returns whether we are doing an Incremental reflow + nsFormControlFrame::SkipResizeReflow(mCacheSize, + mCachedMaxElementSize, + mCachedAvailableSize, + aDesiredSize, aReflowState, + aStatus, + bailOnWidth, bailOnHeight); + if (bailOnWidth) { +#ifdef DO_REFLOW_DEBUG // check or size + const nsStyleSpacing* spacing; + GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)spacing); + nsMargin borderPadding; + borderPadding.SizeTo(0, 0, 0, 0); + spacing->CalcBorderPaddingFor(this, borderPadding); + UNCONSTRAINED_CHECK(); +#endif + REFLOW_DEBUG_MSG3("^** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + return NS_OK; + } + + // add ourself to the form control + if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { + nsFormFrame::AddFormControlFrame(aPresContext, *NS_STATIC_CAST(nsIFrame*,this)); + if (NS_FAILED(CreateDisplayFrame(aPresContext))) { + return NS_ERROR_FAILURE; + } + } + + // Go get all of the important frame nsresult rv = NS_OK; nsIFrame* buttonFrame = GetButtonFrame(aPresContext); - nsIFrame* displayFrame = GetDisplayFrame(aPresContext); nsIFrame* dropdownFrame = GetDropdownFrame(); // Don't try to do any special sizing and positioning unless all of the frames // have been created. - if ((nsnull == displayFrame) || + if ((nsnull == mDisplayFrame) || (nsnull == buttonFrame) || (nsnull == dropdownFrame)) { @@ -728,12 +1222,9 @@ nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, nsRect buttonRect; nsRect dropdownRect; - - if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { - nsFormControlFrame::RegUnRegAccessKey(aPresContext, NS_STATIC_CAST(nsIFrame*, this), PR_TRUE); - nsFormFrame::AddFormControlFrame(aPresContext, *NS_STATIC_CAST(nsIFrame*,this)); - } - + // get our border and padding, + // XXX - should be the same mComputedBorderPadding? + // maybe we should use that? const nsStyleSpacing* spacing; GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)spacing); nsMargin borderPadding; @@ -741,39 +1232,11 @@ nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, spacing->CalcBorderPaddingFor(this, borderPadding); // Get the current sizes of the combo box child frames - displayFrame->GetRect(displayRect); + mDisplayFrame->GetRect(displayRect); buttonFrame->GetRect(buttonRect); dropdownFrame->GetRect(dropdownRect); - nsHTMLReflowState firstPassState(aReflowState); - - // Only reflow the display and button if they are the target of - // the incremental reflow, unless they change size. If they do - // then everything needs to be reflowed. - if (eReflowReason_Incremental == firstPassState.reason) { - nsIFrame* targetFrame; - firstPassState.reflowCommand->GetTarget(targetFrame); - if ((targetFrame == buttonFrame) || (targetFrame == displayFrame)) { - nsRect oldDisplayRect = displayRect; - nsRect oldButtonRect = buttonRect; - rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - if (NS_UNCONSTRAINEDSIZE != firstPassState.mComputedWidth) { - aDesiredSize.width -= borderPadding.left + borderPadding.right; - } - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - if ((oldDisplayRect == displayRect) && (oldButtonRect == buttonRect)) { - aStatus = NS_FRAME_COMPLETE; - return rv; - } - } - nsIReflowCommand::ReflowType type; - aReflowState.reflowCommand->GetType(type); - firstPassState.reason = eReflowReason_StyleChange; - firstPassState.reflowCommand = nsnull; - } - + // We should cache this instead getting it everytime // the default size of the of scrollbar // that will be the default width of the dropdown button // the height will be the height of the text @@ -794,416 +1257,382 @@ nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, float scale; dx->GetCanonicalPixelScale(scale); scrollbarWidth = NSIntPixelsToTwips(info.mSize, p2t*scale); - } - - //Set the desired size for the button and display frame - if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) { - // A width has not been specified for the select so size the display area to - // match the width of the longest item in the drop-down list. The dropdown - // list has already been reflowed and sized to shrink around its contents above. - - // Reflow the dropdown shrink-wrapped. - PRBool saveMES = aDesiredSize.maxElementSize != nsnull; - nsSize * maxElementSize = nsnull; - if (saveMES) { - maxElementSize = new nsSize(); - //maxElementSize = new nsSize(*aDesiredSize.maxElementSize); - } - nsHTMLReflowMetrics dropdownDesiredSize(maxElementSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - - nsSize size; - PRInt32 length = 0; - mListControlFrame->GetNumberOfOptions(&length); - dropdownFrame->GetRect(dropdownRect); - - const nsStyleSpacing* dropSpacing; - dropdownFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dropSpacing); - nsMargin dropBorderPadding; - dropBorderPadding.SizeTo(0, 0, 0, 0); - dropSpacing->CalcBorderPaddingFor(dropdownFrame, dropBorderPadding); - dropdownRect.width -= (dropBorderPadding.left + dropBorderPadding.right); - - // Get maximum size and height of a option in the dropdown - mListControlFrame->GetMaximumSize(size); - - if (scrollbarWidth > 0) { - size.width = scrollbarWidth; - } - const nsStyleSpacing* dspSpacing; - displayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); - nsMargin dspBorderPadding; - dspBorderPadding.SizeTo(0, 0, 0, 0); - dspSpacing->CalcBorderPaddingFor(displayFrame, dspBorderPadding); - - // Set width of display to match width of the drop down - SetChildFrameSize(displayFrame, dropdownRect.width-size.width+dspBorderPadding.left+dspBorderPadding.right, - size.height+dspBorderPadding.top+dspBorderPadding.bottom); - - // Size the button - SetChildFrameSize(buttonFrame, size.width, size.height); - - // Reflow display + button - nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus); - - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - buttonRect.y = displayRect.y; - buttonRect.height = displayRect.height; - buttonFrame->SetRect(aPresContext, buttonRect); - - // Reflow the dropdown list to match the width of the display + button - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, aDesiredSize.width, NS_UNCONSTRAINEDSIZE); - dropdownFrame->GetRect(dropdownRect); - - if (maxElementSize) { - delete maxElementSize; - } - - } else { - // A width has been specified for the select. - // Make the display frame's width + button frame width = the width specified. - nsHTMLReflowMetrics dropdownDesiredSize(aDesiredSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - // Get unconstrained size of the dropdown list. - nsSize size; - mListControlFrame->GetMaximumSize(size); - - if (scrollbarWidth > 0) { - size.width = scrollbarWidth; - } - // Size the button to be the same as the scrollbar width - SetChildFrameSize(buttonFrame, size.width, size.height); - - // Compute display width - nscoord displayWidth = firstPassState.mComputedWidth - size.width; - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - //displayWidth -= borderPadding.left + borderPadding.right; - - // Set the displayFrame to match the displayWidth computed above - SetChildFrameSize(displayFrame, displayWidth, size.height); - - // Reflow again with the width of the display frame set. - nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus); - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - // XXX rods - this hould not be subtracted in - //aDesiredSize.width += borderPadding.left + borderPadding.right; - - // Reflow the dropdown list to match the width of the display + button - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, aDesiredSize.width, NS_UNCONSTRAINEDSIZE); - - } - - // Set the max element size to be the same as the desired element size. - if (nsnull != aDesiredSize.maxElementSize) { - aDesiredSize.maxElementSize->width = aDesiredSize.width; - aDesiredSize.maxElementSize->height = aDesiredSize.height; - } - - aStatus = NS_FRAME_COMPLETE; -#if 0 - COMPARE_QUIRK_SIZE("nsComboboxControlFrame", 127, 22) -#endif - - nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedMaxElementSize, aDesiredSize); - return rv; - -} - - -//------------------------------------------------------------------------------- -//------------------------------------------------------------------------------- -// New Reflow -//------------------------------------------------------------------------------- -//------------------------------------------------------------------------------- -#else // DO_OLD_REFLOW - -#ifdef DEBUG_rods -nsComboboxControlFrame * pCB = nsnull; -static int myCounter = 0; -#endif - -NS_IMETHODIMP -nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, - nsHTMLReflowMetrics& aDesiredSize, - const nsHTMLReflowState& aReflowState, - nsReflowStatus& aStatus) -{ -#ifdef DEBUG_rods - //if (pCB) { - printf("%p ****** nsComboboxControlFrame::Reflow %d Reason: ", this, myCounter++); - switch (aReflowState.reason) { - case eReflowReason_Initial: - printf("eReflowReason_Initial\n");break; - case eReflowReason_Incremental: - printf("eReflowReason_Incremental\n");break; - case eReflowReason_Resize: - printf("eReflowReason_Resize\n"); - break; - case eReflowReason_StyleChange:printf("eReflowReason_StyleChange\n");break; - } - //} -#endif - -#if 0 - nsresult skiprv = nsFormControlFrame::SkipResizeReflow(mCacheSize, mCachedMaxElementSize, aPresContext, - aDesiredSize, aReflowState, aStatus); - if (NS_SUCCEEDED(skiprv)) { - return skiprv; - } -#endif - - nsresult rv = NS_OK; - nsIFrame* buttonFrame = GetButtonFrame(aPresContext); - nsIFrame* displayFrame = GetDisplayFrame(aPresContext); - nsIFrame* dropdownFrame = GetDropdownFrame(); - - // Don't try to do any special sizing and positioning unless all of the frames - // have been created. - if ((nsnull == displayFrame) || - (nsnull == buttonFrame) || - (nsnull == dropdownFrame)) - { - // Since combobox frames are missing just do a normal area frame reflow - return nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - } - - // size of each part of the combo box - nsRect displayRect; - nsRect buttonRect; - nsRect dropdownRect; - - - if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { - nsFormFrame::AddFormControlFrame(aPresContext, *NS_STATIC_CAST(nsIFrame*,this)); - } - - const nsStyleSpacing* spacing; - GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)spacing); - nsMargin borderPadding; - borderPadding.SizeTo(0, 0, 0, 0); - spacing->CalcBorderPaddingFor(this, borderPadding); - - // Get the current sizes of the combo box child frames - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - dropdownFrame->GetRect(dropdownRect); - + } + + // set up a new reflow state for use throughout nsHTMLReflowState firstPassState(aReflowState); + nsHTMLReflowMetrics dropdownDesiredSize(nsnull); - // Only reflow the display and button if they are the target of - // the incremental reflow, unless they change size. If they do - // then everything needs to be reflowed. - if (eReflowReason_Incremental == firstPassState.reason) { + // Check to see if this a fully unconstrained reflow + PRBool fullyUnconstrained = firstPassState.availableWidth == NS_UNCONSTRAINEDSIZE && + firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE && + firstPassState.availableHeight == NS_UNCONSTRAINEDSIZE && + firstPassState.mComputedHeight == NS_UNCONSTRAINEDSIZE; + + PRBool forceReflow = PR_FALSE; + + // Only reflow the display and button + // if they are the target of the incremental reflow, unless they change size. + if (eReflowReason_Incremental == aReflowState.reason) { nsIFrame* targetFrame; firstPassState.reflowCommand->GetTarget(targetFrame); + // Check to see if we are the target of the Incremental Reflow if (targetFrame == this) { - // not sure what I should be doing here for an incremental reflow - // so I will let it just reflow as normal - // but it does seems to go into this code. - } else { - if (targetFrame != dropdownFrame) { -#ifdef DEBUG_rods - printf("+++++++++++++++++++++++\n"); -#endif - rv = nsAreaFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - buttonRect.y = displayRect.y; - buttonRect.height = displayRect.height; - buttonFrame->SetRect(aPresContext, buttonRect); - aStatus = NS_FRAME_COMPLETE; -#ifdef DEBUG_rods - printf("--> W: %d H: %d\n", aDesiredSize.width, aDesiredSize.height); -#endif + // We need to check here to see if we can get away with just reflowing + // the combobox and not the dropdown + REFLOW_DEBUG_MSG("-----------------Target is Combobox------------\n"); + + // If the mComputedWidth matches our cached display width + // then we get away with bailing out + PRBool doFullReflow = firstPassState.mComputedWidth != NS_UNCONSTRAINEDSIZE && + firstPassState.mComputedWidth != mItemDisplayWidth; + if (!doFullReflow) { + // OK, so we got lucky and the size didn't change + // so do a simple reflow and bail out + REFLOW_DEBUG_MSG("------------Reflowing AreaFrame and bailing----\n\n"); + ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, + mDisplayFrame, buttonFrame, mItemDisplayWidth, + scrollbarWidth, borderPadding); + REFLOW_COUNTER(); + UNCONSTRAINED_CHECK(); + REFLOW_DEBUG_MSG3("&** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); return rv; - } else { -#if 0 - nsRect dropdownRect; - dropdownFrame->GetRect(dropdownRect); - nsHTMLReflowMetrics dropdownDesiredSize(aDesiredSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, - aStatus, dropdownRect.width, dropdownRect.height); - nsCOMPtr context; - targetFrame->GetStyleContext(getter_AddRefs(context)); - const nsStyleDisplay* disp = (const nsStyleDisplay*)context->GetStyleData(eStyleStruct_Display); - printf("IsVisible %s\n", disp->mVisible == NS_STYLE_VISIBILITY_VISIBLE?"yes":"No"); - //if (disp->mVisible == NS_STYLE_VISIBILITY_VISIBLE) { - nsRect rect; - GetRect(rect); - aDesiredSize.width = rect.width;// + borderPadding.left + borderPadding.right; - aDesiredSize.height = rect.height;// + borderPadding.top + borderPadding.bottom; + } + // Nope, something changed that affected our size + // so we need to do a full reflow and resize ourself + REFLOW_DEBUG_MSG("------------Do Full Reflow----\n\n"); + firstPassState.reason = eReflowReason_StyleChange; + firstPassState.reflowCommand = nsnull; + forceReflow = PR_TRUE; + + } else { + // Now, see if our target is the dropdown + // If so, maybe an items was added or some style changed etc. + // OR + // We get an Incremental reflow on the dropdown when it is being + // shown or hidden. + if (targetFrame == dropdownFrame) { + REFLOW_DEBUG_MSG("---------Target is Dropdown (Clearing Unc DD Size)---\n"); + PRBool bail = PR_FALSE; + + // Let's start by checking to see if this is a reflow ro showing or hiding. + nsIView * view; + dropdownFrame->GetView(aPresContext, &view); + nsViewVisibility vis = nsViewVisibility_kHide; + NS_ASSERTION(view != nsnull, "The dropdown frame doesn't have a view!"); + if (view != nsnull) { + view->GetVisibility(vis); + } + // If the view is hidden, but the combobox thinks the it is dropped down + // then it look like we are suppose to be showing the dropdown + // XXX - It is really to bad we have to reflow at all + // just to get it to show up or be hidden + if (nsViewVisibility_kHide == vis && mDroppedDown) { + bail = PR_TRUE; + // Do a constrained reflow at a predetermined size + // to have the dropdown shown + firstPassState.reason = eReflowReason_StyleChange; + firstPassState.reflowCommand = nsnull; + mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); + nscoord width = mCacheSize.width > kSizeNotSet?PR_MAX(mCacheSize.width, dropdownRect.width):NS_UNCONSTRAINEDSIZE; + ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, width, NS_UNCONSTRAINEDSIZE); + + } else if (nsViewVisibility_kShow == vis && !mDroppedDown) { + bail = PR_TRUE; + // Do a Unconstrained reflow to "roll" it up + // then cache that unconstrained size + firstPassState.reason = eReflowReason_StyleChange; + firstPassState.reflowCommand = nsnull; + mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); + ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + // cache the values here also, + // since we just did an unconstrained reflow + mCachedUncDropdownSize.width = dropdownDesiredSize.width; + mCachedUncDropdownSize.height = dropdownDesiredSize.height; + REFLOW_DEBUG_MSG3("---2 Caching mCachedUncDropdownSize %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + } + + // Ok, we get to bail because it was either being shown or hidden + if (bail) { + if (fullyUnconstrained) { + aDesiredSize.width = mCachedUncComboSize.width; + aDesiredSize.height = mCachedUncComboSize.height; + } else { + aDesiredSize.width = mCacheSize.width; + aDesiredSize.height = mCacheSize.height; + } + + if (aDesiredSize.maxElementSize != nsnull) { + aDesiredSize.maxElementSize->width = mCachedMaxElementSize.width; + aDesiredSize.maxElementSize->height = mCachedMaxElementSize.height; + } aDesiredSize.ascent = aDesiredSize.height; aDesiredSize.descent = 0; - dropdownRect.width = rect.width; - dropdownFrame->SetRect(aPresContext, dropdownRect); - printf("Skipping out.........................."); - aStatus = NS_FRAME_COMPLETE; + REFLOW_DEBUG_MSG3("|** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + REFLOW_DEBUG_MSG("---- Setting Cached values and then bailing out\n\n"); + UNCONSTRAINED_CHECK(); return NS_OK; - //} -#endif + } + // Nope, we were unlucky so now we do a full reflow + mCachedUncDropdownSize.width = kSizeNotSet; + mCachedUncDropdownSize.height = kSizeNotSet; + REFLOW_DEBUG_MSG("---- Doing Full Reflow\n"); + // This is an incremental reflow targeted at the dropdown list + // and it didn't have anything to do with being show or hidden. + // + // The incremental reflow will not get to the dropdown list + // because it is in the "popup" list + // when this flow of control drops out of this if it will do a reflow + // on the AreaFrame which SHOULD make it get tothe drop down + // except that it is in the popup list, so we have it reflowed as + // a StyleChange, this is not as effecient as doing an Incremental + // + // At this point we want to by pass the reflow optimization in the dropdown + // because we aren't why it is getting an incremental reflow, but we do + // know that it needs to be resized or restyled + //mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); + + } else if (targetFrame == mDisplayFrame || targetFrame == buttonFrame) { + /*if (targetFrame == mDisplayFrame) { + nsresult rv = ReflowComboChildFrame(mDisplayFrame, aPresContext, aDesiredSize, + aReflowState, aStatus, + aReflowState.availableWidth, + aReflowState.availableHeight); + return rv; + }*/ + + // The incremental reflow is targeted at either the block or the button + REFLOW_DEBUG_MSG2("-----------------Target is %s------------\n", (targetFrame == mDisplayFrame?"DisplayItem Frame":"DropDown Btn Frame")); + REFLOW_DEBUG_MSG("---- Doing AreaFrame Reflow and then bailing out\n"); + // Do simple reflow and bail out + ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, + mDisplayFrame, buttonFrame, + mItemDisplayWidth, scrollbarWidth, borderPadding, kSizeNotSet, PR_TRUE); + REFLOW_DEBUG_MSG3("+** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + REFLOW_COUNTER(); + UNCONSTRAINED_CHECK(); + return rv; + } else { + // Here the target of the reflow was a child of the dropdown list + // so we must do a full reflow + REFLOW_DEBUG_MSG("-----------------Target is Dropdown's Child (Option Item)------------\n"); + REFLOW_DEBUG_MSG("---- Doing Reflow as StyleChange\n"); } + firstPassState.reason = eReflowReason_StyleChange; + firstPassState.reflowCommand = nsnull; + mListControlFrame->SetOverrideReflowOptimization(PR_TRUE); + forceReflow = PR_TRUE; } } - // the default size of the of scrollbar - // that will be the default width of the dropdown button - // the height will be the height of the text - nscoord scrollbarWidth = -1; - nsCOMPtr dx; - aPresContext->GetDeviceContext(getter_AddRefs(dx)); - if (dx) { - // Get the width in Device pixels (in this case screen) - SystemAttrStruct info; - dx->GetSystemAttribute(eSystemAttr_Size_ScrollbarWidth, &info); - // Get the pixels to twips conversion for the current device (screen or printer) - float p2t; - aPresContext->GetPixelsToTwips(&p2t); - // Get the scale factor for mapping from one device (screen) - // to another device (screen or printer) - // Typically when it is a screen the scale 1.0 - // when it is a printer is could be anything - float scale; - dx->GetCanonicalPixelScale(scale); - scrollbarWidth = NSIntPixelsToTwips(info.mSize, p2t*scale); - } -#ifdef DEBUG_rods - printf("UNC %d AV %d \n", firstPassState.mComputedWidth,firstPassState.availableWidth); -#endif + // This ifdef is for the new approach to reflow + // where we don't reflow the dropdown + // we just figure out or width from the list of items + // + // This next section is the Current implementation + // the else contains the new reflow code +#ifndef DO_NEW_REFLOW + + // Here is another special optimization + // Only reflow the dropdown if it has never been reflowed unconstrained + // + // Or someone up above here may want to force it to be reflowed + // by setting one or both of these to kSizeNotSet + if ((mCachedUncDropdownSize.width == kSizeNotSet && + mCachedUncDropdownSize.height == kSizeNotSet) || forceReflow) { + REFLOW_DEBUG_MSG3("---Re %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + // A width has not been specified for the select so size the display area + // to match the width of the longest item in the drop-down list. The dropdown + // list has already been reflowed and sized to shrink around its contents above. + ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, + aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); + if (forceReflow) { + mCachedUncDropdownSize.width = dropdownDesiredSize.width; + mCachedUncDropdownSize.height = dropdownDesiredSize.height; + } + } else { + // Here we pretended we did an unconstrained reflow + // so we set the cached values and continue on + REFLOW_DEBUG_MSG3("--- Using Cached ListBox Size %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + dropdownDesiredSize.width = mCachedUncDropdownSize.width; + dropdownDesiredSize.height = mCachedUncDropdownSize.height; + } + + ///////////////////////////////////////////////////////////////////////// + // XXX - I need to clean this nect part up a little it is very redundant + + // Check here to if this is a mComputed unconstrained reflow + PRBool computedUnconstrained = firstPassState.mComputedWidth == NS_UNCONSTRAINEDSIZE && + firstPassState.mComputedHeight == NS_UNCONSTRAINEDSIZE; + if (computedUnconstrained && !forceReflow) { + // Because Incremental reflows aren't actually getting to the dropdown + // we cache the size from when it did a fully unconstrained reflow + // we then check to see if the size changed at all, + // if not then bail out we don't need to worry + if (mCachedUncDropdownSize.width == kSizeNotSet && mCachedUncDropdownSize.height == kSizeNotSet) { + mCachedUncDropdownSize.width = dropdownDesiredSize.width; + mCachedUncDropdownSize.height = dropdownDesiredSize.height; + REFLOW_DEBUG_MSG3("---1 Caching mCachedUncDropdownSize %d,%d\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + + } else if (mCachedUncDropdownSize.width == dropdownDesiredSize.width && + mCachedUncDropdownSize.height == dropdownDesiredSize.height) { + + if (mCachedUncComboSize.width != kSizeNotSet && mCachedUncComboSize.height != kSizeNotSet) { + REFLOW_DEBUG_MSG3("--- Bailing because of mCachedUncDropdownSize %d,%d\n\n", PX(mCachedUncDropdownSize.width), PX(mCachedUncDropdownSize.height)); + aDesiredSize.width = mCachedUncComboSize.width; + aDesiredSize.height = mCachedUncComboSize.height; + + if (aDesiredSize.maxElementSize != nsnull) { + aDesiredSize.maxElementSize->width = mCachedMaxElementSize.width; + aDesiredSize.maxElementSize->height = mCachedMaxElementSize.height; + } + aDesiredSize.ascent = aDesiredSize.height; + aDesiredSize.descent = 0; + UNCONSTRAINED_CHECK(); + REFLOW_DEBUG_MSG3("#** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + return NS_OK; + } + } else { + mCachedUncDropdownSize.width = dropdownDesiredSize.width; + mCachedUncDropdownSize.height = dropdownDesiredSize.height; + } + } + // clean up stops here + ///////////////////////////////////////////////////////////////////////// + + // So this point we know we flowed the dropdown unconstrained + // now we get to figure out how big we need to be and + // + // We don't reflow the combobox here at the new size + // we cache its new size and reflow it on the dropdown + nsSize size; + PRInt32 length = 0; + mListControlFrame->GetNumberOfOptions(&length); + + // dropdownRect will hold the content size (minus border padding) + // for the display area + dropdownFrame->GetRect(dropdownRect); + + // Get maximum size of the largest item in the dropdown + // The height of the display frame will be that height + // the width will be the same as + // the dropdown width (minus its borderPadding) OR + // a caculation off the mComputedWidth from reflow + mListControlFrame->GetMaximumSize(size); + + // the variable "size" will now be + // the default size of the dropdown btn + if (scrollbarWidth > 0) { + size.width = scrollbarWidth; + } + + // Get the border and padding for the dropdown + const nsStyleSpacing* dropSpacing; + dropdownFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dropSpacing); + nsMargin dropBorderPadding; + dropBorderPadding.SizeTo(0, 0, 0, 0); + dropSpacing->CalcBorderPaddingFor(dropdownFrame, dropBorderPadding); + + // get the borderPadding for the display area + const nsStyleSpacing* dspSpacing; + mDisplayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); + nsMargin dspBorderPadding; + dspBorderPadding.SizeTo(0, 0, 0, 0); + dspSpacing->CalcBorderPaddingFor(mDisplayFrame, dspBorderPadding); + + // Substract dropdown's borderPadding from the width of the dropdown rect + // to get the size of the content area + // + // the height will come from the mDisplayFrame's height + // declare a size for the item display frame + //Set the desired size for the button and display frame if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) { -#ifdef DEBUG_rods - printf("=====================================\n"); -#endif + REFLOW_DEBUG_MSG("Unconstrained.....\n"); + REFLOW_DEBUG_MSG4("*B mItemDisplayWidth %d dropdownRect.width:%d dropdownRect.w+h %d\n", PX(mItemDisplayWidth), PX(dropdownRect.width), PX((dropBorderPadding.left + dropBorderPadding.right))); - // A width has not been specified for the select so size the display area to - // match the width of the longest item in the drop-down list. The dropdown - // list has already been reflowed and sized to shrink around its contents above. + // Start with the dropdown rect's width + mItemDisplayWidth = dropdownRect.width; - // Reflow the dropdown shrink-wrapped. - PRBool saveMES = aDesiredSize.maxElementSize != nsnull; - nsSize * maxElementSize = nsnull; - if (saveMES) { - maxElementSize = new nsSize(); - //maxElementSize = new nsSize(*aDesiredSize.maxElementSize); - } - nsHTMLReflowMetrics dropdownDesiredSize(maxElementSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - - nsSize size; - PRInt32 length = 0; - mListControlFrame->GetNumberOfOptions(&length); - dropdownFrame->GetRect(dropdownRect); + REFLOW_DEBUG_MSG2("* mItemDisplayWidth %d\n", PX(mItemDisplayWidth)); - const nsStyleSpacing* dropSpacing; - dropdownFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dropSpacing); - nsMargin dropBorderPadding; - dropBorderPadding.SizeTo(0, 0, 0, 0); - dropSpacing->CalcBorderPaddingFor(dropdownFrame, dropBorderPadding); - dropdownRect.width -= (dropBorderPadding.left + dropBorderPadding.right); + // subtract off the display's BorderPadding + mItemDisplayWidth -= dspBorderPadding.left + dspBorderPadding.right; - // Get maximum size and height of a option in the dropdown - mListControlFrame->GetMaximumSize(size); - - if (scrollbarWidth > 0) { - size.width = scrollbarWidth; - } - const nsStyleSpacing* dspSpacing; - displayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); - nsMargin dspBorderPadding; - dspBorderPadding.SizeTo(0, 0, 0, 0); - dspSpacing->CalcBorderPaddingFor(displayFrame, dspBorderPadding); - -#ifdef DEBUG_rods - printf("W: %d\n", dropdownRect.width-size.width+dspBorderPadding.left+dspBorderPadding.right); -#endif - // Set width of display to match width of the drop down - SetChildFrameSize(displayFrame, dropdownRect.width-size.width+dspBorderPadding.left+dspBorderPadding.right, - size.height+dspBorderPadding.top+dspBorderPadding.bottom); - - // Size the button - SetChildFrameSize(buttonFrame, size.width, size.height); - - // Reflow display + button - nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus); - - displayFrame->GetRect(displayRect); - buttonFrame->GetRect(buttonRect); - buttonRect.y = displayRect.y; - buttonRect.height = displayRect.height; - buttonFrame->SetRect(aPresContext, buttonRect); - - // Reflow the dropdown list to match the width of the display + button - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, aDesiredSize.width, NS_UNCONSTRAINEDSIZE); - dropdownFrame->GetRect(dropdownRect); - - if (maxElementSize) { - delete maxElementSize; - } + REFLOW_DEBUG_MSG2("*A mItemDisplayWidth %d\n", PX(mItemDisplayWidth)); } else { -#ifdef DEBUG_rods - printf("*************************************\n"); -#endif - // for debug ------- - PRInt32 length = 0; - mListControlFrame->GetNumberOfOptions(&length); - //------ - - // A width has been specified for the select. - // Make the display frame's width + button frame width = the width specified. - nsHTMLReflowMetrics dropdownDesiredSize(aDesiredSize); - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - // Get unconstrained size of the dropdown list. - nsSize size; - mListControlFrame->GetMaximumSize(size); - - if (scrollbarWidth > 0) { - size.width = scrollbarWidth; - } - // Size the button to be the same as the scrollbar width - SetChildFrameSize(buttonFrame, size.width, size.height); - + REFLOW_DEBUG_MSG("Constrained.....\n"); if (firstPassState.mComputedWidth > 0) { - // Compute display width - nscoord displayWidth = firstPassState.mComputedWidth - size.width; - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - //displayWidth -= borderPadding.left + borderPadding.right; - -#ifdef DEBUG_rods - printf("WW: %d\n", displayWidth); -#endif - // Set the displayFrame to match the displayWidth computed above - if (displayWidth >= 0) { - SetChildFrameSize(displayFrame, displayWidth, size.height); - } - - // Reflow again with the width of the display frame set. - nsAreaFrame::Reflow(aPresContext, aDesiredSize, firstPassState, aStatus); - - displayFrame->GetRect(displayRect); - displayRect.width = displayWidth; - buttonFrame->GetRect(buttonRect); - buttonRect.x = displayWidth+borderPadding.left; - buttonRect.y = displayRect.y; - buttonRect.height = displayRect.height; - buttonFrame->SetRect(aPresContext, buttonRect); - displayFrame->SetRect(aPresContext, displayRect); -#ifdef DEBUG_rods - printf("DW: %d\n", aDesiredSize.width); -#endif - // nsAreaFrame::Reflow adds in the border and padding so we need to remove it - // XXX rods - this hould not be subtracted in - //aDesiredSize.width += borderPadding.left + borderPadding.right; - - // Reflow the dropdown list to match the width of the display + button - ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, aDesiredSize.width, NS_UNCONSTRAINEDSIZE); + // Compute the display item's width from reflow's mComputedWidth + // mComputedWidth has already excluded border and padding + // so subtract off the button's size + REFLOW_DEBUG_MSG3("B mItemDisplayWidth %d %d\n", PX(mItemDisplayWidth), PX(dspBorderPadding.right)); + mItemDisplayWidth = firstPassState.mComputedWidth - dspBorderPadding.left - dspBorderPadding.right; + REFLOW_DEBUG_MSG2("A mItemDisplayWidth %d\n", PX(mItemDisplayWidth)); + REFLOW_DEBUG_MSG4("firstPassState.mComputedWidth %d - size.width %d dspBorderPadding.right %d\n", PX(firstPassState.mComputedWidth), PX(size.width), PX(dspBorderPadding.right)); } - aDesiredSize.width = firstPassState.mComputedWidth + borderPadding.left + borderPadding.right; - aDesiredSize.height = firstPassState.mComputedHeight + borderPadding.top + borderPadding.bottom; - aDesiredSize.ascent = aDesiredSize.height; - aDesiredSize.descent = 0; } + // this reflows and makes and last minute adjustments + ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, + mDisplayFrame, buttonFrame, mItemDisplayWidth, scrollbarWidth, + borderPadding, size.height); + + // The dropdown was reflowed UNCONSTRAINED before, now we need to check to see + // if it needs to be resized. + // + // Optimization - The style (font, etc.) maybe different for the display item + // than for any particular item in the dropdown. So, if the new size of combobox + // is smaller than the dropdown, that is OK, The dropdown MUST always be either the same + //size as the combo or larger if necessary +#if 0 + if (aDesiredSize.width > dropdownDesiredSize.width) { + if (eReflowReason_Initial == firstPassState.reason) { + firstPassState.reason = eReflowReason_Resize; + } + REFLOW_DEBUG_MSG3("*** Reflowing ListBox to width: %d it was %d\n", PX(aDesiredSize.width), PX(dropdownDesiredSize.width)); + // Reflow the dropdown list to match the width of the display + button + ReflowComboChildFrame(dropdownFrame, aPresContext, dropdownDesiredSize, firstPassState, aStatus, + aDesiredSize.width, NS_UNCONSTRAINEDSIZE); + } +#endif + +#else // DO_NEW_REFLOW + + if (mCacheSize.width == kSizeNotSet) { + ReflowItems(aPresContext, aReflowState, aDesiredSize); + } else { + aDesiredSize.width = mCacheSize.width; + aDesiredSize.height = mCacheSize.height; + } + + // get the borderPadding for the display area + const nsStyleSpacing* dspSpacing; + mDisplayFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)dspSpacing); + nsMargin dspBorderPadding; + dspBorderPadding.SizeTo(0, 0, 0, 0); + + dspSpacing->CalcBorderPaddingFor(mDisplayFrame, dspBorderPadding); + + if (NS_UNCONSTRAINEDSIZE == firstPassState.mComputedWidth) { + mItemDisplayWidth = aDesiredSize.width - (dspBorderPadding.left + dspBorderPadding.right); + mItemDisplayWidth -= borderPadding.left + borderPadding.right; + } else { + if (firstPassState.mComputedWidth > 0) { + // Compute the display item's width from reflow's mComputedWidth + // mComputedWidth has already excluded border and padding + // so subtract off the button's size + mItemDisplayWidth = firstPassState.mComputedWidth - dspBorderPadding.left - dspBorderPadding.right; + } + } + + // this reflows and makes and last minute adjustments + ReflowCombobox(aPresContext, firstPassState, aDesiredSize, aStatus, + mDisplayFrame, buttonFrame, mItemDisplayWidth, scrollbarWidth, + borderPadding, + aDesiredSize.height- borderPadding.top - borderPadding.bottom - + dspBorderPadding.top - dspBorderPadding.bottom); +#endif // DO_NEW_REFLOW // Set the max element size to be the same as the desired element size. if (nsnull != aDesiredSize.maxElementSize) { @@ -1211,19 +1640,37 @@ nsComboboxControlFrame::Reflow(nsIPresContext* aPresContext, aDesiredSize.maxElementSize->height = aDesiredSize.height; } - aStatus = NS_FRAME_COMPLETE; #if 0 COMPARE_QUIRK_SIZE("nsComboboxControlFrame", 127, 22) #endif + // cache the availabe size to be our desired size minus the borders + // this is so if our cached avilable size is ever equal to or less + // than the real avilable size we can bail out + if (aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE) { + mCachedAvailableSize.width = aDesiredSize.width - (borderPadding.left + borderPadding.right); + } + if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { + mCachedAvailableSize.height = aDesiredSize.height - (borderPadding.top + borderPadding.bottom); + } + nsFormControlFrame::SetupCachedSizes(mCacheSize, mCachedMaxElementSize, aDesiredSize); -#ifdef DEBUG_rods - printf("--> W: %d H: %d\n", aDesiredSize.width, aDesiredSize.height); -#endif + + REFLOW_DEBUG_MSG3("** Done nsCCF DW: %d DH: %d\n\n", PX(aDesiredSize.width), PX(aDesiredSize.height)); + REFLOW_COUNTER(); + UNCONSTRAINED_CHECK(); + + // If this was a fully unconstrained reflow we cache + // the combobox's unconstrained size + if (fullyUnconstrained) { + mCachedUncComboSize.width = aDesiredSize.width; + mCachedUncComboSize.height = aDesiredSize.height; + } + return rv; } -#endif // DO_OLD_REFLOW + //------------------------------------------------------------------------------- //------------------------------------------------------------------------------- // Done with New Reflow @@ -1361,10 +1808,15 @@ nsComboboxControlFrame::SetDropDown(nsIFrame* aDropDownFrame) return NS_ERROR_FAILURE; } + + // The ListControlFrame was just created and added to the comboxbox + // so provide it with a PresState so it can restore itself + // when it does its first "Reset" if (mPresState) { mListControlFrame->SetPresState(mPresState); mPresState = do_QueryInterface(nsnull); } + return NS_OK; } @@ -1413,7 +1865,13 @@ nsComboboxControlFrame::UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUp if (mListControlFrame) { // Check to see if the selection changed if (mSelectedIndex != aNewIndex || aForceUpdate) { + //???if (mSelectedIndex != aNewIndex || (aForceUpdate && aNewIndex != kSizeNotSet)) { mListControlFrame->GetSelectedItem(mTextStr); // Update text box +#ifdef DO_REFLOW_DEBUG + char * str = mTextStr.ToNewCString(); + REFLOW_DEBUG_MSG2("UpdateSelection %s\n", str); + delete [] str; +#endif mListControlFrame->UpdateSelection(aDoDispatchEvent, aForceUpdate, mContent); } mSelectedIndex = aNewIndex; @@ -1427,7 +1885,7 @@ nsComboboxControlFrame::AbsolutelyPositionDropDown() { nsRect absoluteTwips; nsRect absolutePixels; -// XXX Not used nsIFrame* displayFrame = GetDisplayFrame(mPresContext); + nsRect rect; this->GetRect(rect); GetAbsoluteFramePosition(mPresContext, this, absoluteTwips, absolutePixels); @@ -1454,49 +1912,31 @@ nsComboboxControlFrame::SelectionChanged() // Send reflow command because the new text maybe larger nsresult rv = NS_OK; if (mDisplayContent) { - nsCOMPtr htmlContent(do_QueryInterface(mDisplayContent, &rv)); - if (NS_SUCCEEDED(rv) && htmlContent) { - nsHTMLValue value; - nsresult result = htmlContent->GetHTMLAttribute(nsHTMLAtoms::value, value); - - PRBool shouldSetValue = PR_FALSE; - if (NS_FAILED(result) || value.GetUnit() == eHTMLUnit_Empty) { - shouldSetValue = PR_TRUE; - } else if (NS_SUCCEEDED(result) && value.GetUnit() == eHTMLUnit_String) { - nsAutoString str; - value.GetStringValue(str); - shouldSetValue = str != mTextStr; - } - if (shouldSetValue) { - rv = htmlContent->SetHTMLAttribute(nsHTMLAtoms::value, mTextStr, PR_TRUE); -#if 1 - if (NS_SUCCEEDED(rv)) { - nsIFrame* displayFrame = GetDisplayFrame(mPresContext); - - nsIReflowCommand* cmd; - rv = NS_NewHTMLReflowCommand(&cmd, displayFrame, nsIReflowCommand::StyleChanged); - if (NS_SUCCEEDED(rv)) { - nsCOMPtr shell; - rv = mPresContext->GetShell(getter_AddRefs(shell)); - if (NS_SUCCEEDED(rv) && shell) { - if (NS_SUCCEEDED(shell->EnterReflowLock())) { - shell->AppendReflowCommand(cmd); - shell->ExitReflowLock(PR_TRUE); - } - } - NS_RELEASE(cmd); - } - } -#else - if (mParent) { - nsCOMPtr shell; - rv = mPresContext->GetShell(getter_AddRefs(shell)); - mState |= NS_FRAME_IS_DIRTY; - mParent->ReflowDirtyChild(shell, (nsIFrame*) this); - } - -#endif - } + nsAutoString value; + const nsTextFragment* fragment; + nsresult result = mDisplayContent->GetText(&fragment); + if (NS_SUCCEEDED(result)) { + fragment->AppendTo(value); + } + PRBool shouldSetValue = PR_FALSE; + if (NS_FAILED(result) || value.Length() == 0) { + shouldSetValue = PR_TRUE; + } else { + shouldSetValue = value != mTextStr; + REFLOW_DEBUG_MSG3("**** SelectionChanged Old[%s] New[%s]\n", value.ToNewCString(), mTextStr.ToNewCString()); + } + if (shouldSetValue) { + rv = mDisplayContent->SetText(mTextStr.GetUnicode(), mTextStr.Length(), PR_TRUE); + nsFrameState state; + mTextFrame->GetFrameState(&state); + state |= NS_FRAME_IS_DIRTY; + mTextFrame->SetFrameState(state); + mDisplayFrame->GetFrameState(&state); + state |= NS_FRAME_IS_DIRTY; + mDisplayFrame->SetFrameState(state); + nsCOMPtr shell; + rv = mPresContext->GetShell(getter_AddRefs(shell)); + ReflowDirtyChild(shell, (nsIFrame*) mDisplayFrame); } } return rv; @@ -1526,19 +1966,18 @@ nsComboboxControlFrame::DoneAddingContent(PRBool aIsDone) NS_IMETHODIMP nsComboboxControlFrame::AddOption(nsIPresContext* aPresContext, PRInt32 aIndex) { -#ifdef DEBUG_rodsXXX - if (!pCB) pCB = this; +#ifdef DO_REFLOW_DEBUG + printf("**********\n*********AddOption: %d\n", aIndex); #endif - nsISelectControlFrame* listFrame = nsnull; nsIFrame* dropdownFrame = GetDropdownFrame(); nsresult rv = dropdownFrame->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); + //PRInt32 index; + //mListControlFrame->GetSelectedIndex(&index); + //UpdateSelection(PR_FALSE, PR_TRUE, index); NS_RELEASE(listFrame); } // If we added the first option, we might need to select it. @@ -1645,6 +2084,60 @@ nsComboboxControlFrame::GetProperty(nsIAtom* aName, nsString& aValue) } +NS_IMETHODIMP +nsComboboxControlFrame::CreateDisplayFrame(nsIPresContext* aPresContext) +{ + + nsCOMPtr shell; + aPresContext->GetShell(getter_AddRefs(shell)); + nsresult rv = NS_NewBlockFrame(shell, (nsIFrame**)&mDisplayFrame, NS_BLOCK_SPACE_MGR); + if (NS_FAILED(rv)) { return rv; } + if (!mDisplayFrame) { return NS_ERROR_NULL_POINTER; } + + // create the style context for the anonymous frame + nsCOMPtr styleContext; + rv = aPresContext->ResolvePseudoStyleContextFor(mContent, + nsHTMLAtoms::mozDisplayComboboxControlFrame, + mStyleContext, + PR_FALSE, + getter_AddRefs(styleContext)); + if (NS_FAILED(rv)) { return rv; } + if (!styleContext) { return NS_ERROR_NULL_POINTER; } + + // create a text frame and put it inside the block frame + rv = NS_NewTextFrame(shell, &mTextFrame); + if (NS_FAILED(rv)) { return rv; } + if (!mTextFrame) { return NS_ERROR_NULL_POINTER; } + nsCOMPtr textStyleContext; + rv = aPresContext->ResolvePseudoStyleContextFor(mContent, + nsHTMLAtoms::mozDisplayComboboxControlFrame, + styleContext, + PR_FALSE, + getter_AddRefs(textStyleContext)); + if (NS_FAILED(rv)) { return rv; } + if (!textStyleContext) { return NS_ERROR_NULL_POINTER; } + nsCOMPtr content(do_QueryInterface(mDisplayContent)); + mTextFrame->Init(aPresContext, content, mDisplayFrame, textStyleContext, nsnull); + mTextFrame->SetInitialChildList(aPresContext, nsnull, nsnull); + nsCOMPtr presShell; + rv = aPresContext->GetShell(getter_AddRefs(presShell)); + if (NS_FAILED(rv)) { return rv; } + if (!presShell) { return NS_ERROR_NULL_POINTER; } + nsCOMPtr frameManager; + rv = presShell->GetFrameManager(getter_AddRefs(frameManager)); + if (NS_FAILED(rv)) { return rv; } + if (!frameManager) { return NS_ERROR_NULL_POINTER; } + frameManager->SetPrimaryFrameFor(content, mTextFrame); + + rv = mDisplayFrame->Init(aPresContext, content, this, styleContext, nsnull); + if (NS_FAILED(rv)) { return rv; } + + mDisplayFrame->SetInitialChildList(aPresContext, nsnull, mTextFrame); + + return NS_OK; +} + + NS_IMETHODIMP nsComboboxControlFrame::CreateAnonymousContent(nsIPresContext* aPresContext, nsISupportsArray& aChildList) @@ -1669,22 +2162,29 @@ nsComboboxControlFrame::CreateAnonymousContent(nsIPresContext* aPresContext, // create content used for display //nsIAtom* tag = NS_NewAtom("mozcombodisplay"); - NS_NewHTMLInputElement(&mDisplayContent, nsHTMLAtoms::input); + // Add a child text content node for the label + nsCOMPtr labelContent; + nsresult result = NS_NewTextNode(getter_AddRefs(labelContent)); + nsAutoString value="X"; + if (NS_SUCCEEDED(result) && labelContent) { + // set the value of the text node + mDisplayContent = do_QueryInterface(labelContent); + mDisplayContent->SetText(value.GetUnicode(), value.Length(), PR_TRUE); - mDisplayContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, nsAutoString("button"), PR_FALSE); + nsIDocument* doc; + mContent->GetDocument(doc); + labelContent->SetDocument(doc, PR_FALSE); + NS_RELEASE(doc); + mContent->AppendChildTo(labelContent, PR_FALSE); - //XXX: Do not use nsHTMLAtoms::id use nsHTMLAtoms::kClass instead. There will end up being multiple - //ids set to the same value which is illegal. - mDisplayContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, nsAutoString("-moz-display"), PR_FALSE); - // This is - mDisplayContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::value, nsAutoString("X"), PR_TRUE); - aChildList.AppendElement(mDisplayContent); - - // create button which drops the list down - NS_NewHTMLInputElement(&mButtonContent, nsHTMLAtoms::input); - //NS_ADDREF(mButtonContent); - mButtonContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, nsAutoString("button"), PR_FALSE); - aChildList.AppendElement(mButtonContent); + // create button which drops the list down + result = NS_NewHTMLInputElement(&mButtonContent, nsHTMLAtoms::input); + //NS_ADDREF(mButtonContent); + if (NS_SUCCEEDED(result) && mButtonContent) { + mButtonContent->SetAttribute(kNameSpaceID_None, nsHTMLAtoms::type, nsAutoString("button"), PR_FALSE); + aChildList.AppendElement(mButtonContent); + } + } return NS_OK; } @@ -1702,6 +2202,22 @@ nsComboboxControlFrame::Destroy(nsIPresContext* aPresContext) { // Cleanup frames in popup child list mPopupFrames.DestroyFrames(aPresContext); + if (mDisplayFrame) { + mFrameConstructor->RemoveMappingsForFrameSubtree(aPresContext, mDisplayFrame, nsnull); + mDisplayFrame->Destroy(aPresContext); + mDisplayFrame=nsnull; + } + + if (mDisplayContent) { + nsCOMPtr content(do_QueryInterface(mDisplayContent)); + if (content) { + PRInt32 index; + if (NS_SUCCEEDED(mContent->IndexOf(content, index))) { + mContent->RemoveChildAt(index, PR_FALSE); + } + } + } + return nsAreaFrame::Destroy(aPresContext); } @@ -1729,7 +2245,7 @@ nsComboboxControlFrame::SetInitialChildList(nsIPresContext* aPresContext, mPopupFrames.SetFrames(aChildList); } else { rv = nsAreaFrame::SetInitialChildList(aPresContext, aListName, aChildList); - InitTextStr(aPresContext, PR_TRUE); + InitTextStr(aPresContext, PR_FALSE); } return rv; } @@ -1818,3 +2334,39 @@ nsComboboxControlFrame::RestoreState(nsIPresContext* aPresContext, nsIPresState* return mListControlFrame->RestoreStateInternal(aPresContext, aState); } + +NS_METHOD +nsComboboxControlFrame::Paint(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer) +{ +#ifdef NOISY + printf("%p paint layer %d at (%d, %d, %d, %d)\n", this, aWhichLayer, + aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height); +#endif + nsAreaFrame::Paint(aPresContext,aRenderingContext,aDirtyRect,aWhichLayer); + + if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) { + nsRect rect(0, 0, mRect.width, mRect.height); + if (mDisplayFrame) { + aRenderingContext.PushState(); + PRBool clipEmpty; + nsRect clipRect; + mDisplayFrame->GetRect(clipRect); + aRenderingContext.SetClipRect(clipRect, nsClipCombine_kReplace, clipEmpty); + PaintChild(aPresContext, aRenderingContext, aDirtyRect, + mDisplayFrame, NS_FRAME_PAINT_LAYER_BACKGROUND); + PaintChild(aPresContext, aRenderingContext, aDirtyRect, + mDisplayFrame, NS_FRAME_PAINT_LAYER_FOREGROUND); + aRenderingContext.PopState(clipEmpty); + } + } + //nsRect rect(0, 0, 50000, 50000); + //aRenderingContext.SetColor(NS_RGB(255, 0, 0)); + //aRenderingContext.FillRect(rect); + + return NS_OK; +} + + diff --git a/mozilla/layout/html/forms/src/nsComboboxControlFrame.h b/mozilla/layout/html/forms/src/nsComboboxControlFrame.h index 1813dab4fe5..9546915ef8f 100644 --- a/mozilla/layout/html/forms/src/nsComboboxControlFrame.h +++ b/mozilla/layout/html/forms/src/nsComboboxControlFrame.h @@ -23,7 +23,17 @@ #ifndef nsComboboxControlFrame_h___ #define nsComboboxControlFrame_h___ -#define OPTIMIZE_RESIZE_RELOW +#ifdef DEBUG_evaughan +//#define DEBUG_rods +#endif + +#ifdef DEBUG_rods +//#define DO_REFLOW_DEBUG +//#define DO_REFLOW_COUNTER +//#define DO_UNCONSTRAINED_CHECK +//#define DO_PIXELS +//#define DO_NEW_REFLOW +#endif #include "nsAreaFrame.h" #include "nsIFormControlFrame.h" @@ -34,12 +44,14 @@ #include "nsIStatefulFrame.h" #include "nsIRollupListener.h" #include "nsIPresState.h" +#include "nsCSSFrameConstructor.h" class nsFormFrame; class nsIView; class nsStyleContext; class nsIHTMLContent; class nsIListControlFrame; +class nsITextContent; /** * Child list name indices @@ -84,6 +96,10 @@ public: nsGUIEvent* aEvent, nsEventStatus* aEventStatus); + NS_IMETHOD Paint(nsIPresContext* aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer); #ifdef NS_DEBUG NS_IMETHOD GetFrameName(nsString& aResult) const; #endif @@ -144,8 +160,6 @@ public: NS_IMETHOD UpdateSelection(PRBool aDoDispatchEvent, PRBool aForceUpdate, PRInt32 aNewIndex); NS_IMETHOD AbsolutelyPositionDropDown(); NS_IMETHOD GetAbsoluteRect(nsRect* aRect); - NS_IMETHOD SetFrameConstructor(nsCSSFrameConstructor *aConstructor) - { return NS_OK;} // not owner - do not addref! // nsISelectControlFrame NS_IMETHOD AddOption(nsIPresContext* aPresContext, PRInt32 index); @@ -162,8 +176,17 @@ public: //nsIRollupListener NS_IMETHOD Rollup(); + NS_IMETHOD SetFrameConstructor(nsCSSFrameConstructor *aConstructor) + { mFrameConstructor = aConstructor; return NS_OK;} // not owner - do not addref! protected: + NS_IMETHOD CreateDisplayFrame(nsIPresContext* aPresContext); + +#ifdef DO_NEW_REFLOW + NS_IMETHOD ReflowItems(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize); +#endif // nsHTMLContainerFrame virtual PRIntn GetSkipSides() const; @@ -187,7 +210,6 @@ public: nsIFrame *aFrame, nsRect& aAbsoluteTwipsRect, nsRect& aAbsolutePixelRect); - nsIFrame* GetDisplayFrame(nsIPresContext* aPresContext); protected: void ShowPopup(PRBool aShowPopup); void ShowList(nsIPresContext* aPresContext, PRBool aShowList); @@ -199,19 +221,32 @@ protected: NS_IMETHOD ToggleList(nsIPresContext* aPresContext); NS_IMETHOD MakeSureSomethingIsSelected(nsIPresContext* aPresContext); // Default to option 0 - 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 - nsIHTMLContent* mDisplayContent; // Anonymous content used to display the current selection - nsIHTMLContent* mButtonContent; // Anonymous content used to popup the dropdown list - PRBool mDroppedDown; // Current state of the dropdown list, PR_TRUE is dropped down - nsIFrame* mDisplayFrame; // frame to display selection - nsIFrame* mButtonFrame; // button frame - nsIFrame* mDropdownFrame; // dropdown list frame - nsIListControlFrame * mListControlFrame; // ListControl Interface for the dropdown frame - PRBool mIgnoreFocus; // Tells the combo to ignore all focus notifications + void ReflowCombobox(nsIPresContext * aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredSize, + nsReflowStatus& aStatus, + nsIFrame * aDisplayFrame, + nsIFrame * aDropDownBtn, + nscoord& aDisplayWidth, + nscoord aBtnWidth, + const nsMargin& aBorderPadding, + nscoord aFallBackHgt = -1, + PRBool aCheckHeight = PR_FALSE); + + 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 mDisplayContent; // Anonymous content used to display the current selection + nsIHTMLContent* mButtonContent; // Anonymous content used to popup the dropdown list + PRBool mDroppedDown; // Current state of the dropdown list, PR_TRUE is dropped down + nsIFrame* mDisplayFrame; // frame to display selection + nsIFrame* mButtonFrame; // button frame + nsIFrame* mDropdownFrame; // dropdown list frame + nsIFrame* mTextFrame; // display area frame + nsIListControlFrame * mListControlFrame; // ListControl Interface for the dropdown frame + PRBool mIgnoreFocus; // Tells the combo to ignore all focus notifications nsCOMPtr mPresState; // Need cache state when list is null @@ -219,6 +254,18 @@ protected: // Resize Reflow Optimization nsSize mCacheSize; nsSize mCachedMaxElementSize; + nsSize mCachedAvailableSize; + + nsSize mCachedUncDropdownSize; + nsSize mCachedUncComboSize; + + nscoord mItemDisplayWidth; + //nscoord mItemDisplayHeight; + nsCSSFrameConstructor* mFrameConstructor; + +#ifdef DO_REFLOW_COUNTER + PRInt32 mReflowId; +#endif private: NS_IMETHOD_(nsrefcnt) AddRef() { return NS_OK; } diff --git a/mozilla/layout/html/forms/src/nsGfxButtonControlFrame.cpp b/mozilla/layout/html/forms/src/nsGfxButtonControlFrame.cpp index 5ecbee68658..6a8407fcb91 100644 --- a/mozilla/layout/html/forms/src/nsGfxButtonControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsGfxButtonControlFrame.cpp @@ -612,7 +612,7 @@ nsGfxButtonControlFrame::SetSuggestedSize(nscoord aWidth, nscoord aHeight) { mSuggestedWidth = aWidth; mSuggestedHeight = aHeight; - mState |= NS_FRAME_IS_DIRTY; + //mState |= NS_FRAME_IS_DIRTY; return NS_OK; } diff --git a/mozilla/layout/html/forms/src/nsListControlFrame.cpp b/mozilla/layout/html/forms/src/nsListControlFrame.cpp index 7866ab948a7..8924f47e44f 100644 --- a/mozilla/layout/html/forms/src/nsListControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsListControlFrame.cpp @@ -354,6 +354,8 @@ nsListControlFrame::Reflow(nsIPresContext* aPresContext, { REFLOW_COUNTER_REQUEST(); + aStatus = NS_FRAME_COMPLETE; + #ifdef DO_REFLOW_DEBUG printf("%p ** Id: %d nsLCF::Reflow %d R: ", this, mReflowId, myCounter++); switch (aReflowState.reason) { @@ -381,7 +383,7 @@ nsListControlFrame::Reflow(nsIPresContext* aPresContext, PRBool bailOnHeight; // This ifdef is for turning off the optimization // so we can check timings against the old version -#if 0 +#if 1 nsFormControlFrame::SkipResizeReflow(mCacheSize, mCachedMaxElementSize, @@ -2637,9 +2639,9 @@ nsListControlFrame::IsLeftButton(nsIDOMEvent* aMouseEvent) nsresult nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) { -#ifdef DO_REFLOW_DEBUG - printf("--------------------------- MouseUp ----------------------------\n"); -#endif + NS_ASSERTION(aMouseEvent != nsnull, "aMouseEvent is null."); + + REFLOW_DEBUG_MSG("--------------------------- MouseUp ----------------------------\n"); if (nsFormFrame::GetDisabled(this)) { return NS_OK; @@ -2681,13 +2683,20 @@ nsListControlFrame::MouseUp(nsIDOMEvent* aMouseEvent) mButtonDown = PR_FALSE; CaptureMouseEvents(mPresContext, PR_FALSE); UpdateSelection(PR_TRUE, PR_FALSE, mContent); +#if 0 // XXX - this is a partial fix for Bug 29990 + if (mSelectedIndex != mStartExtendedIndex) { + mEndExtendedIndex = mSelectedIndex; + } +#endif } - aMouseEvent->PreventDefault(); - aMouseEvent->PreventCapture(); - aMouseEvent->PreventBubble(); - return NS_ERROR_FAILURE; - //return NS_OK; + if (IsInDropDownMode() == PR_TRUE) { + aMouseEvent->PreventDefault(); + aMouseEvent->PreventCapture(); + aMouseEvent->PreventBubble(); + return NS_ERROR_FAILURE; //consumes event + } + return NS_OK; } //--------------------------------------------------------- @@ -2747,9 +2756,10 @@ nsListControlFrame::GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent, nsresult nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent) { -#ifdef DO_REFLOW_DEBUG - printf("--------------------------- MouseDown ----------------------------\n"); -#endif + NS_ASSERTION(aMouseEvent != nsnull, "aMouseEvent is null."); + + REFLOW_DEBUG_MSG("--------------------------- MouseDown ----------------------------\n"); + if (nsFormFrame::GetDisabled(this)) { return NS_OK; } @@ -2865,6 +2875,8 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent) nsresult nsListControlFrame::MouseMove(nsIDOMEvent* aMouseEvent) { + NS_ASSERTION(aMouseEvent != nsnull, "aMouseEvent is null."); + if (mComboboxFrame) { // Synonym for IsInDropDownMode() PRBool isDroppedDown = PR_FALSE; mComboboxFrame->IsDroppedDown(&isDroppedDown); @@ -2884,7 +2896,26 @@ nsListControlFrame::MouseMove(nsIDOMEvent* aMouseEvent) } } } + } else +#if 0 // XXX - this is a partial fix for Bug 29990 + if (mButtonDown) { + PRBool multipleSelections = PR_FALSE; + GetMultiple(&multipleSelections); + if (multipleSelections) { + PRInt32 oldIndex; + PRInt32 curIndex = mSelectedIndex; + if (NS_SUCCEEDED(GetIndexFromDOMEvent(aMouseEvent, oldIndex, curIndex))) { + PRBool optionIsDisabled; + if (NS_OK == IsTargetOptionDisabled(optionIsDisabled)) { + if (!optionIsDisabled) { + mSelectedIndex = curIndex; + SetContentSelected(mSelectedIndex, PR_TRUE); + } + } + } + } } +#endif return NS_OK; } @@ -2966,6 +2997,8 @@ nsListControlFrame::ScrollToFrame(nsIContent* aOptElement) nsresult nsListControlFrame::KeyDown(nsIDOMEvent* aKeyEvent) { + NS_ASSERTION(aKeyEvent != nsnull, "keyEvent is null."); + if (nsFormFrame::GetDisabled(this)) return NS_OK; @@ -2973,9 +3006,9 @@ nsListControlFrame::KeyDown(nsIDOMEvent* aKeyEvent) if (keyEvent) { PRUint32 code; //uiEvent->GetCharCode(&code); - //printf("%c %d ", code, code); + //REFLOW_DEBUG_MSG3("%c %d ", code, code); keyEvent->GetKeyCode(&code); - //printf("%c %d\n", code, code); + REFLOW_DEBUG_MSG3("KeyCode: %c %d\n", code, code); nsresult rv = NS_ERROR_FAILURE; nsCOMPtr options = getter_AddRefs(GetOptions(mContent)); @@ -2988,83 +3021,93 @@ nsListControlFrame::KeyDown(nsIDOMEvent* aKeyEvent) rv = NS_OK; } else { - if (code == nsIDOMKeyEvent::DOM_VK_UP || code == nsIDOMKeyEvent::DOM_VK_LEFT) { -#ifdef DEBUG_rodsXXX - printf("DOM_VK_UP mSelectedIndex: %d ", mSelectedIndex); -#endif - if (mSelectedIndex > 0) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex--; - SingleSelection(); - if (nsnull != mComboboxFrame && mIsAllFramesHere) { - mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event - } - } -#ifdef DEBUG_rodsXXX - printf(" After: %d\n", mSelectedIndex); -#endif - } if (code == nsIDOMKeyEvent::DOM_VK_DOWN || code == nsIDOMKeyEvent::DOM_VK_RIGHT) { -#ifdef DEBUG_rodsXXX - printf("DOM_VK_DOWN mSelectedIndex: %d ", mSelectedIndex); -#endif - if ((mSelectedIndex+1) < (PRInt32)numOptions) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex++; - SingleSelection(); - if (nsnull != mComboboxFrame && mIsAllFramesHere) { - mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event - } - } -#ifdef DEBUG_rodsXXX - printf(" After: %d\n", mSelectedIndex); -#endif - } if (code == nsIDOMKeyEvent::DOM_VK_RETURN) { - if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { - mComboboxFrame->ListWasSelected(mPresContext); - } else { - UpdateSelection(PR_TRUE, PR_FALSE, mContent); - } - } if (code == nsIDOMKeyEvent::DOM_VK_ESCAPE) { - if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { - ResetSelectedItem(); - mComboboxFrame->ListWasSelected(mPresContext); - } - } else { // Select option with this as the first character - // XXX Not I18N compliant - PRInt32 selectedIndex = (mSelectedIndex == kNothingSelected ? 0 : mSelectedIndex+1) % numOptions; - PRInt32 startedAtIndex = selectedIndex; - PRBool loopedAround = PR_FALSE; - while ((selectedIndex < startedAtIndex && loopedAround) || !loopedAround) { - nsCOMPtroptionElement = getter_AddRefs(GetOption(*options, selectedIndex)); - if (optionElement) { - nsAutoString text; - if (NS_OK == optionElement->GetText(text)) { - char * buf = text.ToNewCString(); - char c = buf[0]; - delete [] buf; - if (c == (char)code) { - mOldSelectedIndex = mSelectedIndex; - mSelectedIndex = selectedIndex; - SingleSelection(); - if (nsnull != mComboboxFrame && mIsAllFramesHere) { - mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event - } - break; - } + switch (code) { + + case nsIDOMKeyEvent::DOM_VK_UP: + case nsIDOMKeyEvent::DOM_VK_LEFT: { + REFLOW_DEBUG_MSG2("DOM_VK_UP mSelectedIndex: %d ", mSelectedIndex); + if (mSelectedIndex > 0) { + mOldSelectedIndex = mSelectedIndex; + mSelectedIndex--; + SingleSelection(); + if (nsnull != mComboboxFrame && mIsAllFramesHere) { + mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event + } else { + UpdateSelection(PR_TRUE, PR_FALSE, GetOptionContent(mSelectedIndex)); // dispatch event } } - selectedIndex++; - if (selectedIndex == (PRInt32)numOptions) { - selectedIndex = 0; - loopedAround = PR_TRUE; + REFLOW_DEBUG_MSG2(" After: %d\n", mSelectedIndex); + } break; + + case nsIDOMKeyEvent::DOM_VK_DOWN: + case nsIDOMKeyEvent::DOM_VK_RIGHT: { + REFLOW_DEBUG_MSG2("DOM_VK_DOWN mSelectedIndex: %d ", mSelectedIndex); + if ((mSelectedIndex+1) < (PRInt32)numOptions) { + mOldSelectedIndex = mSelectedIndex; + mSelectedIndex++; + SingleSelection(); + if (nsnull != mComboboxFrame && mIsAllFramesHere) { + mComboboxFrame->UpdateSelection(PR_FALSE, PR_TRUE, mSelectedIndex); // don't dispatch event + } else { + UpdateSelection(PR_TRUE, PR_FALSE, GetOptionContent(mSelectedIndex)); // dispatch event + } } + REFLOW_DEBUG_MSG2(" After: %d\n", mSelectedIndex); + } break; - } // while - } - } + case nsIDOMKeyEvent::DOM_VK_RETURN: { + if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { + mComboboxFrame->ListWasSelected(mPresContext); + } else { + UpdateSelection(PR_TRUE, PR_FALSE, mContent); + } + } break; + + case nsIDOMKeyEvent::DOM_VK_ESCAPE: { + if (IsInDropDownMode() == PR_TRUE && mComboboxFrame) { + ResetSelectedItem(); + mComboboxFrame->ListWasSelected(mPresContext); + } + } break; + + default: { // Select option with this as the first character + // XXX Not I18N compliant + PRInt32 selectedIndex = (mSelectedIndex == kNothingSelected ? 0 : mSelectedIndex+1) % numOptions; + PRInt32 startedAtIndex = selectedIndex; + PRBool loopedAround = PR_FALSE; + while ((selectedIndex < startedAtIndex && loopedAround) || !loopedAround) { + nsCOMPtroptionElement = getter_AddRefs(GetOption(*options, selectedIndex)); + if (optionElement) { + nsAutoString text; + if (NS_OK == optionElement->GetText(text)) { + char * buf = text.ToNewCString(); + char c = buf[0]; + delete [] buf; + if (c == (char)code) { + mOldSelectedIndex = mSelectedIndex; + mSelectedIndex = selectedIndex; + SingleSelection(); + if (nsnull != mComboboxFrame && mIsAllFramesHere) { + mComboboxFrame->UpdateSelection(PR_TRUE, PR_TRUE, mSelectedIndex); // don't dispatch event + } else { + UpdateSelection(PR_TRUE, PR_FALSE, GetOptionContent(mSelectedIndex)); // dispatch event + } + break; + } + } + } + selectedIndex++; + if (selectedIndex == (PRInt32)numOptions) { + selectedIndex = 0; + loopedAround = PR_TRUE; + } + + } // while + } break;//case + } // switch + } // if } } - return NS_OK; }