diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp new file mode 100644 index 000000000000..2f44428145a4 --- /dev/null +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -0,0 +1,901 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nsComboboxControlFrame.h" +#include "nsFormFrame.h" +#include "nsButtonControlFrame.h" +#include "nsTextControlFrame.h" +#include "nsIContent.h" +#include "prtypes.h" +#include "nsIAtom.h" +#include "nsIPresContext.h" +#include "nsIHTMLContent.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLAtoms.h" +#include "nsIFileWidget.h" +#include "nsITextWidget.h" +#include "nsWidgetsCID.h" +#include "nsRepository.h" +#include "nsIView.h" +#include "nsHTMLParts.h" +#include "nsIDOMHTMLInputElement.h" +#include "nsIFormControl.h" +#include "nsINameSpaceManager.h" +#include "nsITextContent.h" + +// Used for Paint +#include "nsCSSRendering.h" +#include "nsIDeviceContext.h" + +#include "nsIPresShell.h" +#include "nsIView.h" +#include "nsIViewManager.h" +#include "nsViewsCID.h" + +#include "nsIDOMElement.h" +#include "nsListControlFrame.h" +#include "nsIListControlFrame.h" + +#include "nsIDOMHTMLCollection.h" //rods +#include "nsIDOMHTMLSelectElement.h" //rods +#include "nsIDOMHTMLOptionElement.h" //rods + +// XXX make this pixels +#define CONTROL_SPACING 40 + +static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); +static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID); +static NS_DEFINE_IID(kCFileWidgetCID, NS_FILEWIDGET_CID); +static NS_DEFINE_IID(kIFileWidgetIID, NS_IFILEWIDGET_IID); +static NS_DEFINE_IID(kITextWidgetIID, NS_ITEXTWIDGET_IID); +static NS_DEFINE_IID(kIFormControlFrameIID, NS_IFORMCONTROLFRAME_IID); +static NS_DEFINE_IID(kIComboboxControlFrameIID, NS_ICOMBOBOXCONTROLFRAME_IID); + +static NS_DEFINE_IID(kIDOMHTMLSelectElementIID, NS_IDOMHTMLSELECTELEMENT_IID); +static NS_DEFINE_IID(kIDOMHTMLOptionElementIID, NS_IDOMHTMLOPTIONELEMENT_IID); +static NS_DEFINE_IID(kIPluggableEventListenerIID, NS_IPLUGGABLEEVENTLISTENER_IID); +static NS_DEFINE_IID(kIListControlFrameIID, NS_ILISTCONTROLFRAME_IID); +static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID); + +extern nsresult NS_NewListControlFrame(nsIFrame*& aNewFrame); + +//-------------------------------------------------------------- +nsresult +NS_NewComboboxControlFrame(nsIFrame*& aResult) +{ + aResult = new nsComboboxControlFrame; + if (nsnull == aResult) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +//-------------------------------------------------------------- +nsComboboxControlFrame::nsComboboxControlFrame() + : nsHTMLContainerFrame() +{ + mFormFrame = nsnull; + mListFrame = nsnull; + mListControlFrame = nsnull; + mPlaceHolderFrame = nsnull; + + mVisibleStyleContext = nsnull; + mHiddenStyleContext = nsnull; + mCurrentStyleContext = nsnull; + mBlockTextStyle = nsnull; + mBlockTextSelectedStyle = nsnull; + mBlockTextSelectedFocusStyle = nsnull; + mFirstTime = PR_TRUE; + mGotFocus = PR_FALSE; +} + +//-------------------------------------------------------------- +nsComboboxControlFrame::~nsComboboxControlFrame() +{ + + NS_IF_RELEASE(mVisibleStyleContext); + NS_IF_RELEASE(mHiddenStyleContext); + NS_IF_RELEASE(mBlockTextStyle); + //NS_IF_RELEASE(mListControlFrame); + //NS_IF_RELEASE(mListFrame); + //NS_IF_RELEASE(mPlaceHolderFrame); + +} + +//-------------------------------------------------------------- +nsresult +nsComboboxControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) +{ + NS_PRECONDITION(0 != aInstancePtr, "null ptr"); + if (NULL == aInstancePtr) { + return NS_ERROR_NULL_POINTER; + } + if (aIID.Equals(kIComboboxControlFrameIID)) { + *aInstancePtr = (void*) ((nsIComboboxControlFrame*) this); + return NS_OK; + } + if (aIID.Equals(kIFormControlFrameIID)) { + *aInstancePtr = (void*) ((nsIFormControlFrame*) this); + return NS_OK; + } + if (aIID.Equals(kIPluggableEventListenerIID)) { + NS_ADDREF_THIS(); // Increase reference count for caller + *aInstancePtr = (void *)((nsIPluggableEventListener*)this); + return NS_OK; + } + return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); +} + +//-------------------------------------------------------------- +PRBool +nsComboboxControlFrame::IsSuccessful(nsIFormControlFrame* aSubmitter) +{ + nsAutoString name; + return (NS_CONTENT_ATTR_HAS_VALUE == GetName(&name)); +} + +//-------------------------------------------------------------- +void +nsComboboxControlFrame::Reset() +{ + SetFocus(PR_TRUE, PR_TRUE); +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetType(PRInt32* aType) const +{ + *aType = NS_FORM_SELECT; + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetFormContent(nsIContent*& aContent) const +{ + return GetContent(aContent); +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetFont(nsIPresContext* aPresContext, + nsFont& aFont) +{ + nsFormControlHelper::GetFont(this, aPresContext, mStyleContext, aFont); + return NS_OK; +} + + +//-------------------------------------------------------------- +nscoord +nsComboboxControlFrame::GetVerticalBorderWidth(float aPixToTwip) const +{ + return NSIntPixelsToTwips(3, aPixToTwip); +} + + +//-------------------------------------------------------------- +nscoord +nsComboboxControlFrame::GetHorizontalBorderWidth(float aPixToTwip) const +{ + return GetVerticalBorderWidth(aPixToTwip); +} + + +//-------------------------------------------------------------- +nscoord +nsComboboxControlFrame::GetVerticalInsidePadding(float aPixToTwip, + nscoord aInnerHeight) const +{ + return 0; +} + +//-------------------------------------------------------------- +nscoord +nsComboboxControlFrame::GetHorizontalInsidePadding(nsIPresContext& aPresContext, + float aPixToTwip, + nscoord aInnerWidth, + nscoord aCharWidth) const +{ + return 0; +} + +//-------------------------------------------------------------- +// XXX this should be removed when nsView exposes it +nsIWidget* +nsComboboxControlFrame::GetWindowTemp(nsIView *aView) +{ + nsIWidget *window = nsnull; + + nsIView *ancestor = aView; + while (nsnull != ancestor) { + ancestor->GetWidget(window); + if (nsnull != window) { + return window; + } + ancestor->GetParent(ancestor); + } + return nsnull; +} + +/** + * + */ +static void ForceDrawFrame(nsIFrame * aFrame)//, PRBool) +{ + if (aFrame == nsnull) { + return; + } + nsRect rect; + nsIView * view; + nsPoint pnt; + aFrame->GetOffsetFromView(pnt, view); + aFrame->GetRect(rect); + rect.x = pnt.x; + rect.y = pnt.y; + if (view != nsnull) { + nsIViewManager * viewMgr; + view->GetViewManager(viewMgr); + if (viewMgr != nsnull) { + viewMgr->UpdateView(view, rect, 0); + NS_RELEASE(viewMgr); + } + } + +} + +//-------------------------------------------------------------- +void +nsComboboxControlFrame::SetFocus(PRBool aOn, PRBool aRepaint) +{ + //mContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, "SELECTED", PR_TRUE); + mGotFocus = aOn; + if (aRepaint) { + ForceDrawFrame(this); + } +} + + +//-------------------------------------------------------------- +// this is in response to the MouseClick from the containing browse button +// XXX still need to get filters from accept attribute +void nsComboboxControlFrame::MouseClicked(nsIPresContext* aPresContext) +{ + + if (nsnull != mListControlFrame) { + SetFocus(PR_FALSE, PR_TRUE); + mCurrentStyleContext = (mCurrentStyleContext == mHiddenStyleContext ? mVisibleStyleContext : mHiddenStyleContext); + if (mCurrentStyleContext == mVisibleStyleContext) { + mListControlFrame->AboutToDropDown(); + nsIFormControlFrame* fcFrame = nsnull; + nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame); + if ((NS_OK == result) && (nsnull != fcFrame)) { + fcFrame->SetFocus(PR_TRUE, PR_FALSE); + } + } else { + SetFocus(PR_TRUE, PR_TRUE); + } + + mListFrame->ReResolveStyleContext(aPresContext, mCurrentStyleContext); + ForceDrawFrame(mListFrame); + } + +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList) +{ + nsFormFrame::AddFormControlFrame(aPresContext, *this); + + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP nsComboboxControlFrame::Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + if (mFirstTime) { + ReResolveStyleContext(&aPresContext, mStyleContext); // XXX This temporary + mListFrame->ReResolveStyleContext(&aPresContext, mCurrentStyleContext); + mFirstTime = PR_FALSE; + } + + PRInt32 numChildren = LengthOf(mFirstChild); + + //nsIFrame* childFrame; + if (1 == numChildren) { + nsIAtom * textBlockContentPseudo = NS_NewAtom(":COMBOBOX-TEXT"); + mBlockTextStyle = aPresContext.ResolvePseudoStyleContextFor(mContent, textBlockContentPseudo, mStyleContext); + NS_RELEASE(textBlockContentPseudo); + + // XXX This code should move to Init(), someday when the frame construction + // changes are all done and Init() is always getting called... + PRBool disabled = nsFormFrame::GetDisabled(this); + } + + nsSize maxSize(aReflowState.availableWidth, aReflowState.availableHeight); + nsHTMLReflowMetrics desiredSize = aDesiredSize; + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + { + nsIDOMHTMLSelectElement* select = nsListControlFrame::GetSelect(mContent); + if (!select) { + return NS_OK; + } + nsIDOMHTMLCollection* options = nsListControlFrame::GetOptions(mContent, select); + if (!options) { + NS_RELEASE(select); + return NS_OK; + } + + // get the css size + nsSize styleSize; + nsFormControlFrame::GetStyleSize(aPresContext, aReflowState, styleSize); + + // get the size of the longest option + PRInt32 maxWidth = 1; + PRUint32 numOptions; + options->GetLength(&numOptions); + for (PRUint32 i = 0; i < numOptions; i++) { + nsIDOMHTMLOptionElement* option = nsListControlFrame::GetOption(*options, i); + if (option) { + //option->CompressContent(); + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE != option->GetText(text)) { + continue; + } + nsSize textSize; + // use the style for the select rather that the option, since widgets don't support it + nsFormControlHelper::GetTextSize(aPresContext, this, text, textSize, aReflowState.rendContext); + //nsFormControlHelper::GetTextSize(aPresContext, this, 1, textSize, aReflowState.rendContext); + if (textSize.width > maxWidth) { + maxWidth = textSize.width; + } + NS_RELEASE(option); + } + } + + PRInt32 rowHeight = 0; + nsSize calcSize, charSize; + PRBool widthExplicit, heightExplicit; + nsInputDimensionSpec textSpec(nsnull, PR_FALSE, nsnull, nsnull, + maxWidth, PR_TRUE, nsHTMLAtoms::size, 1); + // XXX fix CalculateSize to return PRUint32 + PRUint32 numRows = (PRUint32)nsFormControlHelper::CalculateSize(&aPresContext, this, styleSize, textSpec, + calcSize, widthExplicit, heightExplicit, rowHeight, + aReflowState.rendContext); + + float sp2t; + float p2t = aPresContext.GetPixelsToTwips(); + + aPresContext.GetScaledPixelsToTwips(sp2t); + nscoord onePixel = NSIntPixelsToTwips(1, sp2t); + + nscoord scrollbarWidth = 0; + nscoord scrollbarHeight = 0; + nsListControlFrame::GetScrollBarDimensions(aPresContext, scrollbarWidth, scrollbarHeight); + + nscoord extra = calcSize.height - (rowHeight * numRows); + + numRows = (numOptions > 20 ? 20 : numOptions); + calcSize.height = (numRows * rowHeight) + extra; + if (numRows < 21) { + //calcSize.width += scrollbarWidth; + } + + /*aDesiredSize.width = calcSize.width; + // account for vertical scrollbar, if present + if (!widthExplicit && (numRows < (PRInt32)numOptions)) { + aDesiredSize.width += scrollbarWidth; + } +*/ + aDesiredSize.descent = 0; + + + nsMargin border; + const nsStyleSpacing* mySpacing = (const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing); + mySpacing->CalcBorderFor(this, border); + + float scale; + float sbWidth; + float sbHeight; + nsIDeviceContext * context = aPresContext.GetDeviceContext(); + context->GetCanonicalPixelScale(scale); + context->GetScrollBarDimensions(sbWidth, sbHeight); + PRInt32 scrollbarScaledWidth = PRInt32(sbWidth * scale); + PRInt32 scrollbarScaledHeight = PRInt32(sbWidth * scale); + + nscoord adjustment = NSIntPixelsToTwips(14, p2t); + + aDesiredSize.width = calcSize.width + scrollbarScaledWidth + border.left + border.right + adjustment; + aDesiredSize.height = rowHeight + onePixel; + aDesiredSize.height = rowHeight + onePixel; + + mButtonRect.SetRect(aDesiredSize.width-scrollbarScaledWidth-border.right, border.top, + scrollbarScaledWidth, aDesiredSize.height); + + desiredSize = aDesiredSize; + aDesiredSize.height += border.top + border.bottom; + + if (nsnull != aDesiredSize.maxElementSize) { + aDesiredSize.maxElementSize->width = aDesiredSize.width; + aDesiredSize.maxElementSize->height = aDesiredSize.height; + } + + + nsRect frameRect; + GetRect(frameRect); + nsRect curRect; + mPlaceHolderFrame->GetRect(curRect); + curRect.x = frameRect.x; + curRect.y = frameRect.y + aDesiredSize.height; + mPlaceHolderFrame->SetRect(curRect); + + mListFrame->GetRect(frameRect); + + NS_RELEASE(context); + + } + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + + aStatus = NS_FRAME_COMPLETE; + + return NS_OK; +} + +//------------------------------------------------------------------------------ +void +nsComboboxControlFrame::PaintComboboxControl(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer) +{ + const nsStyleDisplay* disp = (const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display); + if (!disp->mVisible) { + return; + } + aRenderingContext.PushState(); + + + const nsStyleColor* myColor = (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color); + const nsStyleSpacing* mySpacing = (const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing); + const nsStyleFont* myFont = (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font); + + + nsIStyleContext * blkStyle; + if (mGotFocus) { + blkStyle = mTextStr.Length() > 0?mBlockTextSelectedFocusStyle:mBlockTextStyle; + } else { + blkStyle = mTextStr.Length() > 0?mBlockTextSelectedStyle:mBlockTextStyle; + } + const nsStyleColor* blkColor = (const nsStyleColor*)blkStyle->GetStyleData(eStyleStruct_Color); + const nsStyleSpacing* blkSpacing = (const nsStyleSpacing*)blkStyle->GetStyleData(eStyleStruct_Spacing); + const nsStyleFont* blkFont = (const nsStyleFont*)blkStyle->GetStyleData(eStyleStruct_Font); + + nsRect rect(0, 0, mRect.width, mRect.height); + nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this, + aDirtyRect, rect, *myColor, 0, 0); + + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, + aDirtyRect, rect, *mySpacing, 0); + + nsHTMLContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer); + + const nsStyleSpacing* spacing =(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing); + nsMargin border; + spacing->CalcBorderFor(this, border); + + float p2t; + aPresContext.GetScaledPixelsToTwips(p2t); + nscoord onePixel = NSIntPixelsToTwips(1, p2t); + + nsRect outside(0, 0, mRect.width, mRect.height); + outside.Deflate(border); + + nsRect inside(outside); + outside.Deflate(onePixel, onePixel); + + aRenderingContext.SetColor(blkColor->mBackgroundColor); + aRenderingContext.FillRect(inside.x, inside.y, inside.width, inside.height); + + float appUnits; + float devUnits; + float scale; + nsIDeviceContext * context; + aRenderingContext.GetDeviceContext(context); + + context->GetCanonicalPixelScale(scale); + context->GetAppUnitsToDevUnits(devUnits); + context->GetDevUnitsToAppUnits(appUnits); + + float sbWidth; + float sbHeight; + context->GetScrollBarDimensions(sbWidth, sbHeight); + PRInt32 scrollbarScaledWidth = PRInt32(sbWidth * scale); + PRInt32 scrollbarScaledHeight = PRInt32(sbWidth * scale); + + nsFont font(aPresContext.GetDefaultFixedFont()); + GetFont(&aPresContext, font); + + aRenderingContext.SetFont(myFont->mFont); + + inside.width -= scrollbarScaledWidth; + PRBool clipEmpty; + aRenderingContext.PushState(); + aRenderingContext.SetClipRect(inside, nsClipCombine_kReplace, clipEmpty); + + nscoord x = inside.x + (onePixel * 4); + nscoord y = inside.y; + + aRenderingContext.SetColor(blkColor->mColor); + + aRenderingContext.DrawString(mTextStr, x, y, 0); + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, + aDirtyRect, inside, *blkSpacing, 0); + + aRenderingContext.PopState(clipEmpty); + +//////////////////////////////// + + inside.width -= scrollbarScaledWidth; + inside.height -= scrollbarScaledHeight; + + // Scrollbars + //const nsStyleColor* myColor = (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color); + //nsIAtom * sbAtom = NS_NewAtom(":SCROLLBAR-ARROW-LOOK"); + //nsIStyleContext* arrowStyle = aPresContext.ResolvePseudoStyleContextFor(mContent, sbAtom, mStyleContext); + //NS_RELEASE(sbAtom); + + const nsStyleSpacing* arrowSpacing = (const nsStyleSpacing*)mArrowStyle->GetStyleData(eStyleStruct_Spacing); + const nsStyleColor* arrowColor = (const nsStyleColor*)mArrowStyle->GetStyleData(eStyleStruct_Color); + + nsRect srect(0,0,0,0);//mRect.width-scrollbarWidth-onePixel, onePixel, scrollbarWidth, mRect.height-(onePixel*2)); + srect = mButtonRect; + nsFormControlHelper::PaintArrow(nsFormControlHelper::eArrowDirection_Down, aRenderingContext,aPresContext, + aDirtyRect, srect, onePixel, *arrowColor, *arrowSpacing, this, mRect); + //} + + + NS_RELEASE(context); + + PRBool status; + aRenderingContext.PopState(status); +} + +NS_METHOD +nsComboboxControlFrame::Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer) +{ + if (eFramePaintLayer_Content == aWhichLayer) { + PaintComboboxControl(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer); + } + return NS_OK; +} + +//-------------------------------------------------------------- +PRIntn +nsComboboxControlFrame::GetSkipSides() const +{ + return 0; +} + + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetName(nsString* aResult) +{ + nsresult result = NS_FORM_NOTOK; + if (mContent) { + nsIHTMLContent* formControl = nsnull; + result = mContent->QueryInterface(kIHTMLContentIID, (void**)&formControl); + if ((NS_OK == result) && formControl) { + nsHTMLValue value; + result = formControl->GetHTMLAttribute(nsHTMLAtoms::name, value); + if (NS_CONTENT_ATTR_HAS_VALUE == result) { + if (eHTMLUnit_String == value.GetUnit()) { + value.GetStringValue(*aResult); + } + } + NS_RELEASE(formControl); + } + } + return result; +} + +//-------------------------------------------------------------- +PRInt32 +nsComboboxControlFrame::GetMaxNumValues() +{ + return 1; +} + +//-------------------------------------------------------------- +PRBool +nsComboboxControlFrame::GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames) +{ + nsAutoString name; + nsresult result = GetName(&name); + if ((aMaxNumValues <= 0) || (NS_CONTENT_ATTR_HAS_VALUE != result)) { + return PR_FALSE; + } + + // use our name and the text widgets value + aNames[0] = name; + nsresult status = PR_FALSE; + /*nsIWidget* widget; + nsITextWidget* textWidget; + mTextFrame->GetWidget(&widget); + if (widget && (NS_OK == widget->QueryInterface(kITextWidgetIID, (void**)&textWidget))) { + PRUint32 actualSize; + textWidget->GetText(aValues[0], 0, actualSize); + aNumValues = 1; + NS_RELEASE(textWidget); + status = PR_TRUE; + } + NS_IF_RELEASE(widget);*/ + return status; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetFrameName(nsString& aResult) const +{ + return MakeFrameName("ComboboxControl", aResult); +} + + +void +nsComboboxControlFrame::RefreshStyleContext(nsIPresContext* aPresContext, + nsIAtom * aNewContentPseudo, + nsIStyleContext*& aCurrentStyle, + nsIContent * aContent, + nsIStyleContext* aParentStyle) + +{ + nsIStyleContext* newStyleContext = aPresContext->ProbePseudoStyleContextFor(aContent, + aNewContentPseudo, + aParentStyle); + if (newStyleContext != aCurrentStyle) { + NS_IF_RELEASE(aCurrentStyle); + aCurrentStyle = newStyleContext; + } else { + NS_IF_RELEASE(newStyleContext); + } +} + +NS_IMETHODIMP +nsComboboxControlFrame::ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext) +{ + nsIStyleContext* oldContext = mStyleContext; + + // NOTE: using nsFrame's ReResolveStyleContext method to avoid + // useless version in base classes. + nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext); + if (NS_FAILED(rv)) { + return rv; + } + + if (oldContext != mStyleContext) { + + PRBool currentIsVisible = (mCurrentStyleContext == mVisibleStyleContext?PR_TRUE:PR_FALSE); + + nsIAtom * visibleContentPseudo = NS_NewAtom(":DROPDOWN-VISIBLE"); + RefreshStyleContext(aPresContext, visibleContentPseudo, mVisibleStyleContext, mContent, mStyleContext); + NS_IF_RELEASE(visibleContentPseudo); + + nsIAtom * hiddenContentPseudo = NS_NewAtom(":DROPDOWN-HIDDEN"); + RefreshStyleContext(aPresContext, hiddenContentPseudo, mHiddenStyleContext, mContent, mStyleContext); + NS_IF_RELEASE(hiddenContentPseudo); + mCurrentStyleContext = (currentIsVisible?mVisibleStyleContext:mHiddenStyleContext); + + mListFrame->ReResolveStyleContext(aPresContext, + (nsnull != mCurrentStyleContext? mCurrentStyleContext : mStyleContext)); + + // Button Style + nsIAtom * btnOutContentPseudo = NS_NewAtom(":DROPDOWN-BTN-OUT"); + RefreshStyleContext(aPresContext, btnOutContentPseudo, mBtnOutStyleContext, mContent, mStyleContext); + NS_IF_RELEASE(btnOutContentPseudo); + + nsIAtom * btnPressContentPseudo = NS_NewAtom(":DROPDOWN-BTN-PRESSED"); + RefreshStyleContext(aPresContext, btnPressContentPseudo, mBtnPressedStyleContext, mContent, mStyleContext); + NS_IF_RELEASE(btnPressContentPseudo); + + nsIAtom * txtBlkContentPseudo = NS_NewAtom(":COMBOBOX-TEXT"); + RefreshStyleContext(aPresContext, txtBlkContentPseudo, mBlockTextStyle, mContent, mStyleContext); + NS_IF_RELEASE(txtBlkContentPseudo); + + nsIAtom * txtBlkSelContentPseudo = NS_NewAtom(":COMBOBOX-TEXTSELECTED"); + RefreshStyleContext(aPresContext, txtBlkSelContentPseudo, mBlockTextSelectedStyle, mContent, mStyleContext); + NS_IF_RELEASE(txtBlkSelContentPseudo); + + nsIAtom * txtBlkSelFocContentPseudo = NS_NewAtom(":COMBOBOX-TEXTSELECTEDFOCUS"); + RefreshStyleContext(aPresContext, txtBlkSelFocContentPseudo, mBlockTextSelectedFocusStyle, mContent, mStyleContext); + NS_IF_RELEASE(txtBlkSelFocContentPseudo); + + + } + + return rv; +} +//---------------------------------------------------------------------- +NS_IMETHODIMP nsComboboxControlFrame::HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + if (nsEventStatus_eConsumeNoDefault == aEventStatus) { + return NS_OK; + } + + if(nsEventStatus_eConsumeNoDefault != aEventStatus) { + if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { + } else if (aEvent->message == NS_MOUSE_MOVE && mDoingSelection || + aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { + // no-op + } else { + return NS_OK; + } + + aEventStatus = nsEventStatus_eConsumeNoDefault; + + if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { + mArrowStyle = mBtnOutStyleContext; + ForceDrawFrame(this); + //MouseClicked(&aPresContext); + + } else if (aEvent->message == NS_MOUSE_MOVE) { + + } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { + mArrowStyle = mBtnPressedStyleContext; + ForceDrawFrame(this); + MouseClicked(&aPresContext); + } + } + + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame) +{ + //nsresult rv = nsScrollFrame::GetFrameForPoint(aPoint, aFrame); + nsresult rv = GetFrameForPointUsing(aPoint, nsnull, aFrame); + if (NS_OK == rv) { + if (*aFrame != this) { + //mHitFrame = *aFrame; + *aFrame = this; + } + return NS_OK; + } + *aFrame = this; + return NS_OK; +} + +nsresult +nsComboboxControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, + nsIAtom* aList, + nsIFrame** aFrame) +{ + *aFrame = this; + return NS_OK; +} + + +//---------------------------------------------------------------------- +// nsIPluggableEventListener +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +NS_IMETHODIMP nsComboboxControlFrame::PluggableEventHandler(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + HandleEvent(aPresContext, aEvent, aEventStatus); + //aEventStatus = nsEventStatus_eConsumeNoDefault; + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsComboboxControlFrame::PluggableGetFrameForPoint(const nsPoint& aPoint, + nsIFrame** aFrame) +{ + return GetFrameForPoint(aPoint, aFrame); +} + + + +//---------------------------------------------------------------------- +// nsIComboboxControlFrame +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::SetDropDown(nsIFrame* aPlaceHolderFrame, nsIFrame* aDropDownFrame) +{ + mPlaceHolderFrame = aPlaceHolderFrame; + mListFrame = aDropDownFrame; + + if (NS_OK != mListFrame->QueryInterface(kIListControlFrameIID, (void**)&mListControlFrame)) { + return NS_ERROR_FAILURE; + } + + // Ok, since we now know we have the ListFrame, and we are assuming at this point it has been initialized + // Let's get the currently selected item, but we make the call using the Interface + mListControlFrame->GetSelectedItem(mTextStr); + + AppendChildren(mPlaceHolderFrame, PR_FALSE); + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::SetDropDownStyleContexts(nsIStyleContext * aVisible, nsIStyleContext * aHidden) +{ + mVisibleStyleContext = aVisible; + mHiddenStyleContext = aHidden; + mCurrentStyleContext = mHiddenStyleContext; + + NS_ADDREF(mVisibleStyleContext); + NS_ADDREF(mHiddenStyleContext); + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::SetButtonStyleContexts(nsIStyleContext * aOut, nsIStyleContext * aPressed) +{ + mBtnOutStyleContext = aOut; + mBtnPressedStyleContext = aPressed; + mArrowStyle = aOut; + + NS_ADDREF(mBtnOutStyleContext); + NS_ADDREF(mBtnPressedStyleContext); + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::ListWasSelected(nsIPresContext* aPresContext) +{ + mArrowStyle = mBtnOutStyleContext; + MouseClicked(aPresContext); + + nsString str; + if (nsnull != mListControlFrame) { + mListControlFrame->GetSelectedItem(mTextStr); + nsIFormControlFrame* fcFrame = nsnull; + nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame); + if ((NS_OK == result) && (nsnull != fcFrame)) { + fcFrame->SetFocus(PR_FALSE, PR_FALSE); + } + SetFocus(PR_TRUE, PR_TRUE); + } + + + return NS_OK; +} + diff --git a/layout/forms/nsComboboxControlFrame.h b/layout/forms/nsComboboxControlFrame.h new file mode 100644 index 000000000000..7fda170523f4 --- /dev/null +++ b/layout/forms/nsComboboxControlFrame.h @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#ifndef nsComboboxControlFrame_h___ +#define nsComboboxControlFrame_h___ + +#include "nsHTMLContainerFrame.h" +#include "nsIFormControlFrame.h" +#include "nsIComboboxControlFrame.h" +#include "nsIPluggableEventListener.h" + +class nsButtonControlFrame; +class nsTextControlFrame; +class nsFormFrame; +class nsIView; +class nsStyleContext; +class nsIHTMLContent; +class nsIListControlFrame; + +class nsComboboxControlFrame : public nsHTMLContainerFrame, + public nsIFormControlFrame, + public nsIPluggableEventListener, + public nsIComboboxControlFrame +{ +public: + nsComboboxControlFrame(); + ~nsComboboxControlFrame(); + + NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); + + // nsIFrame + NS_IMETHOD SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList); + + NS_IMETHOD Reflow(nsIPresContext& aCX, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus); + + NS_IMETHOD ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext); + + NS_IMETHOD Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer); + + NS_IMETHOD HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + + NS_IMETHOD GetFrameName(nsString& aResult) const; + + + virtual PRInt32 GetMaxNumValues(); + + virtual PRBool GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames); + + //nsTextControlFrame* GetTextFrame() { return mTextFrame; } + + //void SetTextFrame(nsTextControlFrame* aFrame) { mTextFrame = aFrame; } + + //nsButtonControlFrame* GetBrowseFrame() { return mBrowseFrame; } + //void SetBrowseFrame(nsButtonControlFrame* aFrame) { mBrowseFrame = aFrame; } + void SetFocus(PRBool aOn, PRBool aRepaint); + virtual PRBool IsSuccessful(nsIFormControlFrame* aSubmitter); + virtual void SetFormFrame(nsFormFrame* aFormFrame) { mFormFrame = aFormFrame; } + virtual void Reset(); + NS_IMETHOD GetName(nsString* aName); + NS_IMETHOD GetType(PRInt32* aType) const; + + NS_IMETHOD GetFont(nsIPresContext* aPresContext, + nsFont& aFont); + NS_IMETHOD GetFormContent(nsIContent*& aContent) const; + virtual nscoord GetVerticalBorderWidth(float aPixToTwip) const; + virtual nscoord GetHorizontalBorderWidth(float aPixToTwip) const; + virtual nscoord GetVerticalInsidePadding(float aPixToTwip, + nscoord aInnerHeight) const; + virtual nscoord GetHorizontalInsidePadding(nsIPresContext& aPresContext, + float aPixToTwip, + nscoord aInnerWidth, + nscoord aCharWidth) const; + + + NS_IMETHOD GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame); + + nsresult GetFrameForPointUsing(const nsPoint& aPoint, + nsIAtom* aList, + nsIFrame** aFrame); + // A Static Helper Functions + + // This refreshes a particular pseudo style content when a "ReResolveStyleContent" happens + static void RefreshStyleContext(nsIPresContext* aPresContext, + nsIAtom * aNewContentPseudo, + nsIStyleContext*& aCurrentStyle, + nsIContent * aContent, + nsIStyleContext* aParentStyle); + + // nsIFormMouseListener + virtual void MouseClicked(nsIPresContext* aPresContext); + + // nsIPluggableEventListener + NS_IMETHOD PluggableEventHandler(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + + NS_IMETHOD PluggableGetFrameForPoint(const nsPoint& aPoint, + nsIFrame** aFrame); + + //static PRInt32 gSpacing; + + //nsIComboboxControlFrame + NS_IMETHOD SetDropDown(nsIFrame* aPlaceHolderFrame, nsIFrame* aDropDownFrame); + NS_IMETHOD SetDropDownStyleContexts(nsIStyleContext * aVisible, nsIStyleContext * aHidden); + NS_IMETHOD SetButtonStyleContexts(nsIStyleContext * aOut, nsIStyleContext * aPressed); + NS_IMETHOD ListWasSelected(nsIPresContext* aPresContext); + + +protected: + virtual void PaintComboboxControl(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer); + nsIWidget* GetWindowTemp(nsIView *aView); // XXX temporary + virtual PRIntn GetSkipSides() const; + + + nsFormFrame* mFormFrame; // Parent Form Frame + + nsIFrame * mPlaceHolderFrame; + nsIFrame * mListFrame; // Generic nsIFrame + nsIListControlFrame * mListControlFrame; // Specific ListControl Interface + nsRect mButtonRect; // The Arrow Button's Rect cached for Painting + + // DropDown List Visibility Styles + nsIStyleContext * mVisibleStyleContext; // Style for the DropDown List to make it visible + nsIStyleContext * mHiddenStyleContext; // Style for the DropDown List to make it hidden + nsIStyleContext * mCurrentStyleContext; // The current Style state of the DropDown List + + // Arrow Button Styles + nsIStyleContext * mBtnOutStyleContext; // Style when not pressed + nsIStyleContext * mBtnPressedStyleContext; // Style When Pressed + nsIStyleContext * mArrowStyle; // Arrows currrent style (for painting) + + // Combobox Text Styles + nsIStyleContext * mBlockTextStyle; // Style when there is no selection and it doesn't have focus + nsIStyleContext * mBlockTextSelectedStyle; // Style when selected and it doesn't have focus + nsIStyleContext * mBlockTextSelectedFocusStyle; // Style when selected and it has focus + + + PRBool mFirstTime; + + // State data members + nsString mTextStr; + PRBool mGotFocus; + +private: + NS_IMETHOD_(nsrefcnt) AddRef() { return NS_OK; } + NS_IMETHOD_(nsrefcnt) Release() { return NS_OK; } +}; + +#endif + + diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp new file mode 100644 index 000000000000..5b679548eb39 --- /dev/null +++ b/layout/forms/nsListControlFrame.cpp @@ -0,0 +1,1634 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#include "nsListControlFrame.h" +#include "nsFormControlHelper.h" +#include "nsFormControlFrame.h" +#include "nsBlockReflowContext.h" +#include "nsBlockBandData.h" +#include "nsBulletFrame.h" +#include "nsLineBox.h" + +#include "nsFrameReflowState.h" +#include "nsLineLayout.h" +#include "nsInlineReflow.h" +#include "nsPlaceholderFrame.h" +#include "nsStyleConsts.h" +#include "nsHTMLIIDs.h" +#include "nsCSSRendering.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIReflowCommand.h" +#include "nsISpaceManager.h" +#include "nsIStyleContext.h" +#include "nsIView.h" +#include "nsIFontMetrics.h" +#include "nsHTMLParts.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLValue.h" +#include "nsDOMEvent.h" +#include "nsIHTMLContent.h" +#include "prprf.h" +#include "nsLayoutAtoms.h" + +#include "nsIFormControl.h" +#include "nsStyleUtil.h" +#include "nsINameSpaceManager.h" +#include "nsIDeviceContext.h" //rods +#include "nsIHTMLContent.h" //rods +#include "nsIDOMHTMLCollection.h" //rods +#include "nsIDOMHTMLSelectElement.h" //rods +//#include "nsIDOMHTMLOListElement.h" //rods +#include "nsIDOMHTMLOptionElement.h" //rods +#include "nsIDOMNode.h" //rods +#include "nsHTMLAtoms.h" //rods +#include "nsIAtom.h" //rods +#include "nsIDOMEventReceiver.h" +#include "nsIComboboxControlFrame.h" +#include "nsComboboxControlFrame.h" // for the static helper function +#include "nsIListControlFrame.h" +#include "nsIViewManager.h" +#include "nsFormFrame.h" + +#define CSS_NOTSET -1 +#define ATTR_NOTSET -1 + +// Constants +const char * kNormal = ""; +const char * kSelected = "SELECTED"; +const char * kSelectedFocus = "SELECTEDFOCUS"; + + +static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID); +static NS_DEFINE_IID(kIFormControlFrameIID, NS_IFORMCONTROLFRAME_IID); + +static NS_DEFINE_IID(kIPluggableEventListenerIID, NS_IPLUGGABLEEVENTLISTENER_IID); +static NS_DEFINE_IID(kIListControlFrameIID, NS_ILISTCONTROLFRAME_IID); + +static NS_DEFINE_IID(kIDOMHTMLSelectElementIID, NS_IDOMHTMLSELECTELEMENT_IID); +static NS_DEFINE_IID(kIDOMHTMLOptionElementIID, NS_IDOMHTMLOPTIONELEMENT_IID); +static NS_DEFINE_IID(kIDOMNodeIID, NS_IDOMNODE_IID); +static NS_DEFINE_IID(kIDOMElementIID, NS_IDOMELEMENT_IID); + +//---------------------------------------------------------------------- + +nsresult +NS_NewListControlFrame(nsIFrame*& aNewFrame) +{ + nsListControlFrame* it = new nsListControlFrame; + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + aNewFrame = it; + return NS_OK; +} + +//---------------------------------------------------------------------- +nsListControlFrame::nsListControlFrame() +{ + mHitFrame = nsnull; + mCurrentHitFrame = nsnull; + mSelectedFrame = nsnull; + mSelectedContent = nsnull; + mSelectedIndex = -1; + mNumRows = 0; + mMaxNumSelections = 64; + mIsInitializedFromContent = PR_FALSE; + + PRInt32 i; + for (i=0;iFirstChild(aList, kid); + while (nsnull != kid) { + kid->GetRect(kidRect); + if (kidRect.Contains(aPoint)) { + tmp.MoveTo(aPoint.x - kidRect.x, aPoint.y - kidRect.y); + + nsIContent * content; + kid->GetContent(content); + static NS_DEFINE_IID(kIDOMHTMLOptionElementIID, NS_IDOMHTMLOPTIONELEMENT_IID); + nsIDOMHTMLOptionElement* oe; + if (content && (NS_OK == content->QueryInterface(kIDOMHTMLOptionElementIID, (void**) &oe))) { + *aFrame = kid; + NS_RELEASE(content); + return NS_OK; + } + NS_RELEASE(content); + return kid->GetFrameForPoint(tmp, aFrame); + //*aFrame = kid; + //return NS_OK; + } + kid->GetNextSibling(kid); + } + + mContentFrame->FirstChild(aList, kid); + while (nsnull != kid) { + nsFrameState state; + kid->GetFrameState(state); + if (NS_FRAME_OUTSIDE_CHILDREN & state) { + kid->GetRect(kidRect); + tmp.MoveTo(aPoint.x - kidRect.x, aPoint.y - kidRect.y); + if (NS_OK == kid->GetFrameForPoint(tmp, aFrame)) { + return NS_OK; + } + } + kid->GetNextSibling(kid); + } + *aFrame = this; + return NS_ERROR_FAILURE; +} + +//---------------------------------------------------------------------- +void +nsListControlFrame::PaintChildren(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer) +{ + + nsScrollFrame::PaintChildren(aPresContext, + aRenderingContext, + aDirtyRect, aWhichLayer); +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + if (!mIsInitializedFromContent) { + mIsInitializedFromContent = PR_TRUE; + InitializeFromContent(PR_TRUE); + } + + nsIFrame* childFrame; + + nscoord scrollbarWidth = 0; + nscoord scrollbarHeight = 0; + GetScrollBarDimensions(aPresContext, scrollbarWidth, scrollbarHeight); + + nsFont font(aPresContext.GetDefaultFixedFont()); + GetFont(&aPresContext, font); + + nsSize maxSize(aReflowState.availableWidth, aReflowState.availableHeight); + nsHTMLReflowMetrics desiredSize = aDesiredSize; + aDesiredSize.width = 0; + aDesiredSize.height = 0; + + // Calculate the amount of space needed for borders + const nsStyleSpacing* spacing = (const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing); + nsMargin border; + if (!spacing->GetBorder(border)) { + border.SizeTo(0, 0, 0, 0); + } + + nsSize desiredLineSize; + GetDesiredSize(&aPresContext, aReflowState, aDesiredSize, desiredLineSize); + + float p2t; + aPresContext.GetScaledPixelsToTwips(p2t); + nscoord lineEndPadding = NSIntPixelsToTwips(10, p2t);//GetHorizontalInsidePadding(aPresContext, p2t, 0, 0); + aDesiredSize.width += lineEndPadding; + + desiredSize = aDesiredSize; + nsHTMLReflowState reflowState(aPresContext, mFirstChild, aReflowState, maxSize); + nsIHTMLReflow* htmlReflow; + if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) { + htmlReflow->WillReflow(aPresContext); + nsresult result = htmlReflow->Reflow(aPresContext, desiredSize, reflowState, aStatus); + NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); + } + + mContentFrame->FirstChild(nsnull, childFrame); + PRInt32 numChildren = LengthOf(childFrame); + + aDesiredSize.width += border.left+border.right; + aDesiredSize.height += border.top+border.bottom; + + nscoord insideWidth = aDesiredSize.width-border.left-border.right-1; + if (mInDropDownMode) { + aDesiredSize.width += scrollbarWidth; + insideWidth += scrollbarWidth; + } else { + if (mNumRows < numChildren) { + aDesiredSize.width += scrollbarWidth; + } + } + + nsRect rect(0,0, insideWidth, desiredLineSize.height*numChildren); + mContentFrame->SetRect(rect); + + + mContentFrame->FirstChild(nsnull, childFrame); + nsPoint offset(0,0); + while (nsnull != childFrame) { // reflow, place, size the children + nsHTMLReflowState reflowState(aPresContext, childFrame, aReflowState, maxSize); + nsIHTMLReflow* htmlReflow; + + if (NS_OK == childFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) { + htmlReflow->WillReflow(aPresContext); + nsresult result = htmlReflow->Reflow(aPresContext, desiredSize, reflowState, aStatus); + NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); + //??nsRect rect(offset.x, offset.y, desiredLineSize.width+lineEndPadding, desiredLineSize.height); + nsRect rect(offset.x, offset.y, insideWidth, desiredLineSize.height); + childFrame->SetRect(rect); + childFrame->GetNextSibling(childFrame); + offset.x = 0; + offset.y += desiredLineSize.height; + } + } + + if (nsnull != aDesiredSize.maxElementSize) { + //XXX aDesiredSize.AddBorderPaddingToMaxElementSize(borderPadding); + aDesiredSize.maxElementSize->width = aDesiredSize.width; + aDesiredSize.maxElementSize->height = aDesiredSize.height; + } + + aStatus = NS_FRAME_COMPLETE; + + return NS_OK; +} + +NS_IMETHODIMP nsListControlFrame::SetRect(const nsRect& aRect) +{ + nsScrollFrame::SetRect(aRect); + return NS_OK; +} +NS_IMETHODIMP nsListControlFrame::SizeTo(nscoord aWidth, nscoord aHeight) +{ + nsScrollFrame::SizeTo(aWidth, aHeight); + return NS_OK; +} + +//---------------------------------------------------------------------- +nsIFrame * +nsListControlFrame::GetOptionFromChild(nsIFrame* aParentFrame) +{ + + nsIFrame* kid; + nsIFrame * frame = nsnull; + + aParentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + static NS_DEFINE_IID(kIDOMHTMLOptionElementIID, NS_IDOMHTMLOPTIONELEMENT_IID); + nsIDOMHTMLOptionElement* element; + if (content && (NS_OK == content->QueryInterface(kIDOMHTMLOptionElementIID, (void**) &element))) { + NS_RELEASE(content); + NS_RELEASE(element); + return kid; + } + NS_RELEASE(content); + nsIFrame * frame = GetOptionFromChild(kid); + if (nsnull != frame) { + return frame; + } + kid->GetNextSibling(kid); + } + return nsnull; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetFormContent(nsIContent*& aContent) const +{ + return GetContent(aContent); +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetFont(nsIPresContext* aPresContext, + nsFont& aFont) +{ + nsFormControlHelper::GetFont(this, aPresContext, mStyleContext, aFont); + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::PluggableEventHandler(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + HandleEvent(aPresContext, aEvent, aEventStatus); + //aEventStatus = nsEventStatus_eConsumeNoDefault; + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::PluggableGetFrameForPoint(const nsPoint& aPoint, + nsIFrame** aFrame) +{ + return GetFrameForPoint(aPoint, aFrame); +} + +//---------------------------------------------------------------------- +PRInt32 nsListControlFrame::SetContentSelected(nsIFrame * aHitFrame, + nsIContent *& aHitContent, + PRBool aIsSelected) +{ + PRInt32 index = 0; + nsIFrame* kid; + mContentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + if (kid == aHitFrame) { + NS_IF_RELEASE(aHitContent); + kid->GetContent(aHitContent); + aHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (aIsSelected?kSelectedFocus:kNormal), PR_TRUE); + return index; + } + kid->GetNextSibling(kid); + index++; + } + return -1; +} + +//---------------------------------------------------------------------- +void nsListControlFrame::ClearSelection() +{ + PRInt32 i = 0; + nsIFrame* kid; + mContentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + if (mIsFrameSelected[i]) { + if (i != mSelectedIndex) { + content->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + mIsFrameSelected[i] = PR_FALSE; + } + } + NS_RELEASE(content); + kid->GetNextSibling(kid); + i++; + } +} + +//---------------------------------------------------------------------- +void nsListControlFrame::ExtendedSelection(PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aDoInvert, PRBool aSetValue) +{ + PRInt32 startInx; + PRInt32 endInx; + + if (aStartIndex < aEndIndex) { + startInx = aStartIndex; + endInx = aEndIndex; + } else { + startInx = aEndIndex; + endInx = aStartIndex; + } + + PRInt32 i = 0; + nsIFrame* kid; + PRBool startInverting = PR_FALSE; + mContentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + if (i == startInx) { + startInverting = PR_TRUE; + } + if (startInverting && ((i != mStartExtendedIndex && aDoInvert) || !aDoInvert)) { + if (aDoInvert) { + mIsFrameSelected[i] = !mIsFrameSelected[i]; + } else { + mIsFrameSelected[i] = aSetValue; + } + if (i != mSelectedIndex) { + content->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (mIsFrameSelected[i]?kSelected:kNormal), PR_TRUE); + } + } + if (i == endInx) { + startInverting = PR_FALSE; + } + NS_RELEASE(content); + kid->GetNextSibling(kid); + i++; + } +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::HandleLikeListEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { + PRInt32 oldSelectedIndex = mSelectedIndex; + mSelectedIndex = (PRInt32)SetContentSelected(mHitFrame, mHitContent, PR_TRUE); + if (-1 < mSelectedIndex) { + PRBool selected = mIsFrameSelected[mSelectedIndex]; + if (mMultipleSelections) { + + if (((nsMouseEvent *)aEvent)->isShift) { + selected = PR_TRUE; + if (mEndExtendedIndex == -1) { + mEndExtendedIndex = mSelectedIndex; + ExtendedSelection(mStartExtendedIndex, mEndExtendedIndex, PR_FALSE, PR_TRUE); + } else { + if (mStartExtendedIndex < mEndExtendedIndex) { + if (mSelectedIndex < mStartExtendedIndex) { + ExtendedSelection(mSelectedIndex, mEndExtendedIndex, PR_TRUE, PR_TRUE); + mEndExtendedIndex = mSelectedIndex; + } else if (mSelectedIndex > mEndExtendedIndex) { + ExtendedSelection(mEndExtendedIndex+1, mSelectedIndex, PR_FALSE, PR_TRUE); + mEndExtendedIndex = mSelectedIndex; + } else if (mSelectedIndex < mEndExtendedIndex) { + ExtendedSelection(mSelectedIndex+1, mEndExtendedIndex, PR_FALSE, PR_FALSE); + mEndExtendedIndex = mSelectedIndex; + } + } else if (mStartExtendedIndex > mEndExtendedIndex) { + if (mSelectedIndex > mStartExtendedIndex) { + ExtendedSelection(mEndExtendedIndex, mSelectedIndex, PR_TRUE, PR_TRUE); + mEndExtendedIndex = mSelectedIndex; + } else if (mSelectedIndex < mEndExtendedIndex) { + ExtendedSelection(mStartExtendedIndex+1, mEndExtendedIndex-1, PR_FALSE, PR_TRUE); + mEndExtendedIndex = mSelectedIndex; + } else if (mSelectedIndex > mEndExtendedIndex) { + ExtendedSelection(mEndExtendedIndex, mSelectedIndex-1, PR_FALSE, PR_FALSE); + mEndExtendedIndex = mSelectedIndex; + } + } + } + } else if (((nsMouseEvent *)aEvent)->isControl) { + selected = !selected; + if (nsnull != mSelectedContent) { + if (mHitContent != mSelectedContent) { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (mIsFrameSelected[oldSelectedIndex]?kSelected:kNormal), PR_TRUE); + } else { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); + } + } + } else { + mStartExtendedIndex = mSelectedIndex; + mEndExtendedIndex = -1; + ClearSelection(); + selected = PR_TRUE; + } + } else { + if (nsnull != mSelectedContent) { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mSelectedContent); + } + } + mIsFrameSelected[mSelectedIndex] = selected; + //mHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); + mSelectedContent = mHitContent; + mSelectedFrame = mHitFrame; + } + } + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::HandleLikeDropDownListEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + // Mouse Move behavior is as follows: + // When the DropDown occurs, if an item is selected it displayed as being selected. + // It may or may not be currently visible, when the mouse is moved across any item + // in the the list it is displayed as the currently selected item. + // (Selection doesn't actually occur until the mouse is released, or lciked and released) + if (aEvent->message == NS_MOUSE_MOVE) { + + // If the DropDown is currently has a selected item + // then clear the selected item + if (nsnull != mSelectedContent) { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mSelectedContent); + } + + // Now check to see if the newly hit frame is different + // from the currently hit frame + if (nsnull != mHitFrame && mHitFrame != mCurrentHitFrame) { + + // Since the new frame is different then clear the selection on the old frame (the current frame) + if (nsnull != mCurrentHitContent) { + mCurrentHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mCurrentHitContent); + } + + // Find the new hit "content" from the hit frame and select it + SetContentSelected(mHitFrame, mHitContent, PR_TRUE); + + // Now cache the the newly hit frame and content as the "current" values + mCurrentHitFrame = mHitFrame; + mCurrentHitContent = mHitContent; + + } + + } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { + + // Start by finding the newly "hit" content from the hit frame + PRInt32 index = SetContentSelected(mHitFrame, mHitContent, PR_FALSE); + if (-1 < index) { + + nsIDOMHTMLOptionElement* option = nsnull; + nsresult rv = mHitContent->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); + if (NS_OK == rv) { + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE == option->GetText(text)) { + mSelectionStr = text; + } + + NS_IF_RELEASE(mSelectedContent); + mSelectedIndex = index; + + // Clean up frames before disappearing + if (nsnull != mSelectedContent) { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mSelectedContent); + } + if (nsnull != mHitContent) { + mHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mHitContent); + } + if (nsnull != mCurrentHitContent) { + mCurrentHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mCurrentHitContent); + } + NS_RELEASE(option); + } + + if (mComboboxFrame) { + mComboboxFrame->ListWasSelected(&aPresContext); + } + + } + + } + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ +/* const char * desc[] = {"NS_MOUSE_MOVE", + "NS_MOUSE_LEFT_BUTTON_UP", + "NS_MOUSE_LEFT_BUTTON_DOWN", + "","","","","","","", + "NS_MOUSE_MIDDLE_BUTTON_UP", + "NS_MOUSE_MIDDLE_BUTTON_DOWN", + "","","","","","","","", + "NS_MOUSE_RIGHT_BUTTON_UP", + "NS_MOUSE_RIGHT_BUTTON_DOWN", + "NS_MOUSE_ENTER", + "NS_MOUSE_EXIT", + "NS_MOUSE_LEFT_DOUBLECLICK", + "NS_MOUSE_MIDDLE_DOUBLECLICK", + "NS_MOUSE_RIGHT_DOUBLECLICK", + "NS_MOUSE_LEFT_CLICK", + "NS_MOUSE_MIDDLE_CLICK", + "NS_MOUSE_RIGHT_CLICK"}; + int inx = aEvent->message-NS_MOUSE_MESSAGE_START; + if (inx >= 0 && inx <= (NS_MOUSE_RIGHT_CLICK-NS_MOUSE_MESSAGE_START)) { + printf("Mouse in ListFrame %s [%d]\n", desc[inx], aEvent->message); + } else { + printf("Mouse in ListFrame [%d]\n", aEvent->message); + }*/ + if (nsEventStatus_eConsumeNoDefault == aEventStatus) { + return NS_OK; + } + + const nsStyleDisplay* disp = (const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display); + if (!disp->mVisible) { + return NS_OK; + } + + if(nsEventStatus_eConsumeNoDefault != aEventStatus) { + + aEventStatus = nsEventStatus_eConsumeNoDefault; + if (mInDropDownMode) { + HandleLikeDropDownListEvent(aPresContext, aEvent, aEventStatus); + } else { + HandleLikeListEvent(aPresContext, aEvent, aEventStatus); + } + } + + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList) +{ + mContentFrame = aChildList; + mContentFrame->SetPluggableEventListener(this); + + if (!mInDropDownMode) { + nsFormFrame::AddFormControlFrame(aPresContext, *this); + } + //aChildList->FirstChild(nsnull, mContentFrame); + + if (!mIsInitializedFromContent) { + InitializeFromContent(); + } + + return nsScrollFrame::SetInitialChildList(aPresContext, aListName, aChildList); + +} +//---------------------------------------------------------------------- +/*nsresult +nsListControlFrame::AppendNewFrames(nsIPresContext& aPresContext, + nsIFrame* aNewFrame) +{ + return nsScrollFrame::AppendNewFrames(aPresContext, aNewFrame); +} + +//---------------------------------------------------------------------- +nsresult +nsListControlFrame::InsertNewFrame(nsIPresContext& aPresContext, + nsBaseIBFrame* aParentFrame, + nsIFrame* aNewFrame, + nsIFrame* aPrevSibling) +{ + return nsScrollFrame::InsertNewFrame(aPresContext, aParentFrame, aNewFrame, aPrevSibling); +} + +//---------------------------------------------------------------------- +nsresult +nsListControlFrame::DoRemoveFrame(nsBlockReflowState& aState, + nsBaseIBFrame* aParentFrame, + nsIFrame* aDeletedFrame, + nsIFrame* aPrevSibling) +{ + return nsScrollFrame::DoRemoveFrame(aState, aParentFrame, aDeletedFrame, aPrevSibling); +}*/ + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::Init(nsIPresContext& aPresContext, + nsIContent* aContent, + nsIFrame* aGeometricParent, + nsIFrame* aContentParent, + nsIStyleContext* aContext) +{ + nsresult result = nsScrollFrame::Init(aPresContext, aContent, aGeometricParent, aContentParent, aContext); + /*if (NS_OK == result) { + nsIDOMNode* node; + if (mContent && (NS_OK == mContent->QueryInterface(kIDOMNodeIID, (void**) &node))) { + AddEventListener(node); + NS_RELEASE(node); + } + }*/ + if (NS_OK == result) { + nsIDOMHTMLSelectElement* select; + if (mContent && (NS_OK == mContent->QueryInterface(kIDOMHTMLSelectElementIID, (void**) &select))) { + select->GetMultiple(&mMultipleSelections); + select->GetSize(&mNumRows); + + NS_RELEASE(select); + } + } + + return result; + +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetMaxLength(PRInt32* aSize) +{ + *aSize = -1; + nsresult result = NS_CONTENT_ATTR_NOT_THERE; + nsIHTMLContent* content = nsnull; + mContent->QueryInterface(kIHTMLContentIID, (void**) &content); + if (nsnull != content) { + nsHTMLValue value; + result = content->GetHTMLAttribute(nsHTMLAtoms::maxlength, value); + if (eHTMLUnit_Integer == value.GetUnit()) { + *aSize = value.GetIntValue(); + } + NS_RELEASE(content); + } + return result; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetSize(PRInt32* aSize) const +{ + *aSize = -1; + nsresult result = NS_CONTENT_ATTR_NOT_THERE; + nsIHTMLContent* content = nsnull; + mContent->QueryInterface(kIHTMLContentIID, (void**) &content); + if (nsnull != content) { + nsHTMLValue value; + result = content->GetHTMLAttribute(nsHTMLAtoms::size, value); + if (eHTMLUnit_Integer == value.GetUnit()) { + *aSize = value.GetIntValue(); + } + NS_RELEASE(content); + } + return result; +} +//---------------------------------------------------------------------- +void +nsListControlFrame::GetScrollBarDimensions(nsIPresContext& aPresContext, + nscoord &aWidth, nscoord &aHeight) +{ + + aWidth = 0; + aHeight = 0; + float scale; + nsIDeviceContext* dx = nsnull; + dx = aPresContext.GetDeviceContext(); + if (nsnull != dx) { + float sbWidth; + float sbHeight; + dx->GetCanonicalPixelScale(scale); + dx->GetScrollBarDimensions(sbWidth, sbHeight); + aWidth = PRInt32(sbWidth * scale); + aHeight = PRInt32(sbHeight * scale); + NS_RELEASE(dx); + } //else { + //aWidth = 19 * sp2t; + //aHeight = scrollbarWidth; + //} + + +} + +//---------------------------------------------------------------------- +nscoord +nsListControlFrame::GetVerticalBorderWidth(float aPixToTwip) const +{ + return NSIntPixelsToTwips(3, aPixToTwip); +} + +//---------------------------------------------------------------------- +nscoord +nsListControlFrame::GetHorizontalBorderWidth(float aPixToTwip) const +{ + return GetVerticalBorderWidth(aPixToTwip); +} + +//---------------------------------------------------------------------- +nscoord +nsListControlFrame::GetVerticalInsidePadding(float aPixToTwip, + nscoord aInnerHeight) const +{ + return NSIntPixelsToTwips(0, aPixToTwip); // XXX this is really +} + +//---------------------------------------------------------------------- +nscoord +nsListControlFrame::GetHorizontalInsidePadding(nsIPresContext& aPresContext, + float aPixToTwip, + nscoord aInnerWidth, + nscoord aCharWidth) const +{ + return GetVerticalInsidePadding(aPixToTwip, aInnerWidth); +} + + +//---------------------------------------------------------------------- +void +nsListControlFrame::GetDesiredSize(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredLayoutSize, + nsSize& aDesiredWidgetSize) +{ + nsIDOMHTMLSelectElement* select = GetSelect(mContent); + if (!select) { + return; + } + nsIDOMHTMLCollection* options = GetOptions(mContent, select); + if (!options) { + NS_RELEASE(select); + return; + } + + // get the css size + nsSize styleSize; + nsFormControlFrame::GetStyleSize(*aPresContext, aReflowState, styleSize); + + // get the size of the longest option + PRInt32 maxWidth = 1; + PRUint32 numOptions; + options->GetLength(&numOptions); + for (PRUint32 i = 0; i < numOptions; i++) { + nsIDOMHTMLOptionElement* option = GetOption(*options, i); + if (option) { + //option->CompressContent(); + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE != option->GetText(text)) { + continue; + } + nsSize textSize; + // use the style for the select rather that the option, since widgets don't support it + nsFormControlHelper::GetTextSize(*aPresContext, this, text, textSize, aReflowState.rendContext); + if (textSize.width > maxWidth) { + maxWidth = textSize.width; + } + NS_RELEASE(option); + } + } + + PRInt32 rowHeight = 0; + nsSize calcSize, charSize; + PRBool widthExplicit, heightExplicit; + nsInputDimensionSpec textSpec(nsnull, PR_FALSE, nsnull, nsnull, + maxWidth, PR_TRUE, nsHTMLAtoms::size, 1); + // XXX fix CalculateSize to return PRUint32 + mNumRows = (PRUint32)nsFormControlHelper::CalculateSize(aPresContext, this, styleSize, textSpec, + calcSize, widthExplicit, heightExplicit, rowHeight, + aReflowState.rendContext); + + float sp2t; + float p2t = aPresContext->GetPixelsToTwips(); + + aPresContext->GetScaledPixelsToTwips(sp2t); + + //nscoord scrollbarWidth = 0; + //nscoord scrollbarHeight = 0; + //GetScrollBarDimensions(*aPresContext, scrollbarWidth, scrollbarHeight); + + if (mInDropDownMode) { + //PRUint32 numOptions; + //options->GetLength(&numOptions); + nscoord extra = calcSize.height - (rowHeight * mNumRows); + + mNumRows = (numOptions > 20 ? 20 : numOptions); + calcSize.height = (mNumRows * rowHeight) + extra; + if (mNumRows < 21) { + //calcSize.width += scrollbarWidth; + } + } + + aDesiredLayoutSize.width = calcSize.width; + // account for vertical scrollbar, if present + //if (!widthExplicit && ((mNumRows < numOptions) || mIsComboBox)) { + //if (!widthExplicit && (mNumRows < (PRInt32)numOptions)) { + // aDesiredLayoutSize.width += scrollbarWidth; + // } + + // XXX put this in widget library, combo boxes are fixed height (visible part) + //aDesiredLayoutSize.height = (mIsComboBox) + // ? rowHeight + (2 * GetVerticalInsidePadding(p2t, rowHeight)) + // : calcSize.height; + aDesiredLayoutSize.height = calcSize.height; + aDesiredLayoutSize.ascent = aDesiredLayoutSize.height; + aDesiredLayoutSize.descent = 0; + + aDesiredWidgetSize.width = maxWidth; + aDesiredWidgetSize.height = rowHeight; + //if (mIsComboBox) { // add in pull down size + // PRInt32 extra = NSIntPixelsToTwips(10, p2t*scale); + // aDesiredWidgetSize.height += (rowHeight * (numOptions > 20 ? 20 : numOptions)) + extra; + //} + + // override the width and height for a combo box that has already got a widget + //if (mWidget && mIsComboBox) { + // nscoord ignore; + // GetWidgetSize(*aPresContext, ignore, aDesiredLayoutSize.height); + // aDesiredLayoutSize.ascent = aDesiredLayoutSize.height; + //} + + NS_RELEASE(select); + NS_RELEASE(options); + +} + + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetMultiple(PRBool* aMultiple, nsIDOMHTMLSelectElement* aSelect) +{ + if (!aSelect) { + nsIDOMHTMLSelectElement* select = nsnull; + nsresult result = mContent->QueryInterface(kIDOMHTMLSelectElementIID, (void**)&select); + if ((NS_OK == result) && select) { + result = select->GetMultiple(aMultiple); + NS_RELEASE(select); + } + return result; + } else { + return aSelect->GetMultiple(aMultiple); + } +} + +//---------------------------------------------------------------------- +nsIDOMHTMLSelectElement* +nsListControlFrame::GetSelect(nsIContent * aContent) +{ + nsIDOMHTMLSelectElement* select = nsnull; + nsresult result = aContent->QueryInterface(kIDOMHTMLSelectElementIID, (void**)&select); + if ((NS_OK == result) && select) { + return select; + } else { + return nsnull; + } +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::AboutToDropDown() +{ + PRInt32 i = 0; + nsIFrame* kid; + mContentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + if (i == mSelectedIndex) { + mSelectedContent = content; + mSelectedFrame = kid; + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kSelected, PR_TRUE); + return NS_OK; + } + NS_RELEASE(content); + kid->GetNextSibling(kid); + i++; + } + return NS_OK; +} + +//---------------------------------------------------------------------- +void +nsListControlFrame::InitializeFromContent(PRBool aDoDisplay) +{ + PRInt32 i; + for (i=0;iFirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + + nsIDOMHTMLOptionElement* option = nsnull; + nsresult result = content->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); + if (result == NS_OK) { + PRBool selected; + option->GetDefaultSelected(&selected); + option->GetSelected(&selected); + mIsFrameSelected[i] = selected; + + if (mInDropDownMode) { + if (selected) { + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE == option->GetText(text)) { + mSelectionStr = text; + } + mSelectedIndex = i; + NS_RELEASE(option); + mSelectedContent = content; + mSelectedFrame = kid; + return; + } + } else { + if (selected && aDoDisplay) { + content->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelected:kNormal), PR_TRUE); + } + } + NS_RELEASE(option); + } + NS_RELEASE(content); + kid->GetNextSibling(kid); + i++; + } + +} + +//---------------------------------------------------------------------- +nsIDOMHTMLCollection* +nsListControlFrame::GetOptions(nsIContent * aContent, nsIDOMHTMLSelectElement* aSelect) +{ + nsIDOMHTMLCollection* options = nsnull; + if (!aSelect) { + nsIDOMHTMLSelectElement* select = GetSelect(aContent); + if (select) { + select->GetOptions(&options); + NS_RELEASE(select); + return options; + } else { + return nsnull; + } + } else { + aSelect->GetOptions(&options); + return options; + } +} + +//---------------------------------------------------------------------- +nsIDOMHTMLOptionElement* +nsListControlFrame::GetOption(nsIDOMHTMLCollection& aCollection, PRUint32 aIndex) +{ + nsIDOMNode* node = nsnull; + PRBool status = PR_FALSE; + if ((NS_OK == aCollection.Item(aIndex, &node)) && node) { + nsIDOMHTMLOptionElement* option = nsnull; + nsresult result = node->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); + NS_RELEASE(node); + return option; + } + return nsnull; +} + + +//---------------------------------------------------------------------- +PRBool +nsListControlFrame::GetOptionValue(nsIDOMHTMLCollection& aCollection, PRUint32 aIndex, nsString& aValue) +{ + PRBool status = PR_FALSE; + nsIDOMHTMLOptionElement* option = GetOption(aCollection, aIndex); + if (option) { + nsresult result = option->GetValue(aValue); + if (aValue.Length() > 0) { + status = PR_TRUE; + } else { + result = option->GetText(aValue); + if (aValue.Length() > 0) { + status = PR_TRUE; + } + } + NS_RELEASE(option); + } + return status; +} + + + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::Deselect() +{ + PRInt32 i; + for (i=0;iGetFont(&aPresContext, font); + nsIDeviceContext* deviceContext = aPresContext.GetDeviceContext(); + + nsIFontMetrics* fontMet; + deviceContext->GetMetricsFor(font, fontMet); + aRendContext->SetFont(fontMet); + aRendContext->GetWidth(aString, aSize.width); + fontMet->GetHeight(aSize.height); + + char char1, char2; + nsCompatibility mode = nsFormControlHelper::GetRepChars(aPresContext, char1, char2); + nscoord char1Width, char2Width; + aRendContext->GetWidth(char1, char1Width); + aRendContext->GetWidth(char2, char2Width); + + NS_RELEASE(fontMet); + NS_RELEASE(deviceContext); + + if (eCompatibility_Standard == mode) { + return ((char1Width + char2Width) / 2) + 1; + } else { + return char1Width; + } +} + +//------------------------------------------------------------------ +nscoord +nsListControlFrame::GetTextSize(nsIPresContext& aPresContext, nsListControlFrame* aFrame, + PRInt32 aNumChars, nsSize& aSize, + nsIRenderingContext *aRendContext) +{ + nsAutoString val; + char char1, char2; + nsFormControlHelper::GetRepChars(aPresContext, char1, char2); + int i; + for (i = 0; i < aNumChars; i+=2) { + val += char1; + } + for (i = 1; i < aNumChars; i+=2) { + val += char2; + } + return GetTextSize(aPresContext, aFrame, val, aSize, aRendContext); +} + +//------------------------------------------------------------------ +PRInt32 +nsListControlFrame::CalculateSize (nsIPresContext* aPresContext, nsListControlFrame* aFrame, + const nsSize& aCSSSize, nsInputDimensionSpec& aSpec, + nsSize& aBounds, PRBool& aWidthExplicit, + PRBool& aHeightExplicit, nscoord& aRowHeight, + nsIRenderingContext *aRendContext) +{ + nscoord charWidth = 0; + PRInt32 numRows = ATTR_NOTSET; + aWidthExplicit = PR_FALSE; + aHeightExplicit = PR_FALSE; + + aBounds.width = CSS_NOTSET; + aBounds.height = CSS_NOTSET; + nsSize textSize(0,0); + + nsIContent* iContent = nsnull; + aFrame->GetContent((nsIContent*&) iContent); + if (!iContent) { + return 0; + } + nsIHTMLContent* hContent = nsnull; + nsresult result = iContent->QueryInterface(kIHTMLContentIID, (void**)&hContent); + if ((NS_OK != result) || !hContent) { + NS_RELEASE(iContent); + return 0; + } + nsAutoString valAttr; + nsresult valStatus = NS_CONTENT_ATTR_NOT_THERE; + if (nsnull != aSpec.mColValueAttr) { + valStatus = hContent->GetAttribute(kNameSpaceID_HTML, aSpec.mColValueAttr, valAttr); + } + nsHTMLValue colAttr; + nsresult colStatus = NS_CONTENT_ATTR_NOT_THERE; + if (nsnull != aSpec.mColSizeAttr) { + colStatus = hContent->GetHTMLAttribute(aSpec.mColSizeAttr, colAttr); + } + float p2t; + aPresContext->GetScaledPixelsToTwips(p2t); + + // determine the width + nscoord adjSize; + if (NS_CONTENT_ATTR_HAS_VALUE == colStatus) { // col attr will provide width + PRInt32 col = ((colAttr.GetUnit() == eHTMLUnit_Pixel) ? colAttr.GetPixelValue() : colAttr.GetIntValue()); + if (aSpec.mColSizeAttrInPixels) { + col = (col <= 0) ? 15 : col; + aBounds.width = NSIntPixelsToTwips(col, p2t); + } + else { + col = (col <= 0) ? 1 : col; + charWidth = GetTextSize(*aPresContext, aFrame, col, aBounds, aRendContext); + aRowHeight = aBounds.height; // XXX aBounds.height has CSS_NOTSET + } + if (aSpec.mColSizeAttrInPixels) { + aWidthExplicit = PR_TRUE; + } + } + else { + if (CSS_NOTSET != aCSSSize.width) { // css provides width + aBounds.width = (aCSSSize.width > 0) ? aCSSSize.width : 1; + aWidthExplicit = PR_TRUE; + } + else { + if (NS_CONTENT_ATTR_HAS_VALUE == valStatus) { // use width of initial value + charWidth = GetTextSize(*aPresContext, aFrame, valAttr, aBounds, aRendContext); + } + else if (aSpec.mColDefaultValue) { // use default value + charWidth = GetTextSize(*aPresContext, aFrame, *aSpec.mColDefaultValue, aBounds, aRendContext); + } + else if (aSpec.mColDefaultSizeInPixels) { // use default width in pixels + charWidth = GetTextSize(*aPresContext, aFrame, 1, aBounds, aRendContext); + aBounds.width = aSpec.mColDefaultSize; + } + else { // use default width in num characters + charWidth = GetTextSize(*aPresContext, aFrame, aSpec.mColDefaultSize, aBounds, aRendContext); + } + aRowHeight = aBounds.height; // XXX aBounds.height has CSS_NOTSET + } + } + + // determine the height + nsHTMLValue rowAttr; + nsresult rowStatus = NS_CONTENT_ATTR_NOT_THERE; + if (nsnull != aSpec.mRowSizeAttr) { + rowStatus = hContent->GetHTMLAttribute(aSpec.mRowSizeAttr, rowAttr); + } + if (NS_CONTENT_ATTR_HAS_VALUE == rowStatus) { // row attr will provide height + PRInt32 rowAttrInt = ((rowAttr.GetUnit() == eHTMLUnit_Pixel) ? rowAttr.GetPixelValue() : rowAttr.GetIntValue()); + adjSize = (rowAttrInt > 0) ? rowAttrInt : 1; + if (0 == charWidth) { + charWidth = GetTextSize(*aPresContext, aFrame, 1, textSize, aRendContext); + aBounds.height = textSize.height * adjSize; + aRowHeight = textSize.height; + numRows = adjSize; + } + else { + aBounds.height = aBounds.height * adjSize; + } + } + else if (CSS_NOTSET != aCSSSize.height) { // css provides height + aBounds.height = (aCSSSize.height > 0) ? aCSSSize.height : 1; + aHeightExplicit = PR_TRUE; + } + else { // use default height in num lines + if (0 == charWidth) { + charWidth = GetTextSize(*aPresContext, aFrame, 1, textSize, aRendContext); + aBounds.height = textSize.height * aSpec.mRowDefaultSize; + aRowHeight = textSize.height; + } + else { + aBounds.height = aBounds.height * aSpec.mRowDefaultSize; + } + } + + if ((0 == charWidth) || (0 == textSize.width)) { + charWidth = GetTextSize(*aPresContext, aFrame, 1, textSize, aRendContext); + aRowHeight = textSize.height; + } + + // add inside padding if necessary + if (!aWidthExplicit) { + aBounds.width += (2 * aFrame->GetHorizontalInsidePadding(*aPresContext, p2t, aBounds.width, charWidth)); + } + if (!aHeightExplicit) { + aBounds.height += (2 * aFrame->GetVerticalInsidePadding(p2t, textSize.height)); + } + + NS_RELEASE(hContent); + if (ATTR_NOTSET == numRows) { + numRows = aBounds.height / aRowHeight; + } + + NS_RELEASE(iContent); + return numRows; +} + +#endif + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// nsHTMLContainerFrame +//---------------------------------------------------------------------- +PRIntn +nsListControlFrame::GetSkipSides() const +{ + return 0; +} + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// nsIFormControlFrame +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetType(PRInt32* aType) const +{ + /*nsresult result = NS_FORM_NOTOK; + if (mContent) { + nsIFormControl* formControl = nsnull; + result = mContent->QueryInterface(kIFormControlIID, (void**)&formControl); + if ((NS_OK == result) && formControl) { + result = formControl->GetType(aType); + NS_RELEASE(formControl); + } + } + return result; + */ + *aType = NS_FORM_SELECT; + return NS_OK; +} + +void +nsListControlFrame::SetFormFrame(nsFormFrame* aFormFrame) +{ + mFormFrame = aFormFrame; +} + +//---------------------------------------------------------------------- +PRBool +nsListControlFrame::IsSuccessful(nsIFormControlFrame* aSubmitter) +{ + nsAutoString name; + return (NS_CONTENT_ATTR_HAS_VALUE == GetName(&name)); +} + +//---------------------------------------------------------------------- +void +nsListControlFrame::MouseClicked(nsIPresContext* aPresContext) +{ + /*nsIRadioButton* radioWidget; + if (mWidget && (NS_OK == mWidget->QueryInterface(kIRadioIID, (void**)&radioWidget))) { + radioWidget->SetState(PR_TRUE); + NS_RELEASE(radioWidget); + if (mFormFrame) { + mFormFrame->OnRadioChecked(*this); + } + }*/ +} +//---------------------------------------------------------------------- +PRInt32 +nsListControlFrame::GetMaxNumValues() +{ + PRBool multiple; + GetMultiple(&multiple); + if (multiple) { + PRUint32 length = 0; + nsIDOMHTMLCollection* options = GetOptions(mContent); + if (options) { + options->GetLength(&length); + } + return (PRInt32)length; // XXX fix return on GetMaxNumValues + } else { + return 1; + } +} + +//---------------------------------------------------------------------- +void +nsListControlFrame::Reset() +{ + SetFocus(PR_TRUE, PR_TRUE); + + nsIDOMHTMLCollection* options = GetOptions(mContent); + if (!options) { + return; + } + + PRUint32 numOptions; + options->GetLength(&numOptions); + + Deselect(); + for (PRUint32 i = 0; i < numOptions; i++) { + nsIDOMHTMLOptionElement* option = GetOption(*options, i); + if (option) { + PRBool selected = PR_FALSE; + option->GetSelected(&selected); + if (selected) { + mIsFrameSelected[i] = PR_TRUE; + } + NS_RELEASE(option); + } + } + + // update visual here + NS_RELEASE(options); +} +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetName(nsString* aResult) +{ + nsresult result = NS_FORM_NOTOK; + if (mContent) { + nsIHTMLContent* formControl = nsnull; + result = mContent->QueryInterface(kIHTMLContentIID, (void**)&formControl); + if ((NS_OK == result) && formControl) { + nsHTMLValue value; + result = formControl->GetHTMLAttribute(nsHTMLAtoms::name, value); + if (NS_CONTENT_ATTR_HAS_VALUE == result) { + if (eHTMLUnit_String == value.GetUnit()) { + value.GetStringValue(*aResult); + } + } + NS_RELEASE(formControl); + } + } + return result; +} + + +//---------------------------------------------------------------------- +/*PRBool +nsListControlFrame::GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames) +{ + aNumValues = 0; + return PR_FALSE; +}*/ + +//---------------------------------------------------------------------- +PRBool +nsListControlFrame::GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames) +{ + PRBool status = PR_FALSE; + + aNumValues = 0; + nsAutoString name; + nsresult result = GetName(&name); + if ((aMaxNumValues <= 0) || (NS_CONTENT_ATTR_NOT_THERE == result)) { + return PR_FALSE; + } + + nsIDOMHTMLCollection* options = GetOptions(mContent); + if (!options) { + return PR_FALSE; + } + + NS_ASSERTION(aMaxNumValues >= mNumSelections, "invalid max num values"); + if (mNumSelections >= 0) { + PRInt32* selections = new PRInt32[mNumSelections]; + PRInt32 i = 0; + PRInt32 inx; + for (inx=0;iSetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (aOn?kSelectedFocus:kNormal), PR_TRUE); + } + +} + +//---------------------------------------------------------------------- +// nsIListControlFrame +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::SetComboboxFrame(nsIFrame* aComboboxFrame) +{ + static NS_DEFINE_IID(kIComboboxIID, NS_ICOMBOBOXCONTROLFRAME_IID); + if (aComboboxFrame && (NS_OK == aComboboxFrame->QueryInterface(kIComboboxIID, (void**) &mComboboxFrame))) { + mInDropDownMode = PR_TRUE; + } + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetSelectedItem(nsString & aStr) +{ + aStr = mSelectionStr; + return NS_OK; +} + +#if 0 +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::DeleteFrame(nsIPresContext& aPresContext) +{ + nsIDOMNode* node; + if (mContent && (NS_OK == mContent->QueryInterface(kIDOMNodeIID, (void**) &node))) { + RemoveEventListener(node); + NS_RELEASE(node); + } + return nsScrollFrame::DeleteFrame(aPresContext); + +} +//----------------------------------------------------------------- +nsresult nsListControlFrame::Focus(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +//----------------------------------------------------------------- +nsresult nsListControlFrame::Blur(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +//----------------------------------------------------------------- +nsresult nsListControlFrame::ProcessEvent(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +//----------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::AddEventListener(nsIDOMNode * aNode) +{ + nsIDOMEventReceiver * receiver; + + if (NS_OK == aNode->QueryInterface(kIDOMEventReceiverIID, (void**) &receiver)) { + receiver->AddEventListener((nsIDOMFocusListener*)this, kIDOMFocusListenerIID); + NS_RELEASE(receiver); + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +//----------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::RemoveEventListener(nsIDOMNode * aNode) +{ + nsIDOMEventReceiver * receiver; + + if (NS_OK == aNode->QueryInterface(kIDOMEventReceiverIID, (void**) &receiver)) { + receiver->RemoveEventListener(this, kIDOMFocusListenerIID); + NS_RELEASE(receiver); + return NS_OK; + } + return NS_ERROR_FAILURE; +} +#endif diff --git a/layout/forms/nsListControlFrame.h b/layout/forms/nsListControlFrame.h new file mode 100644 index 000000000000..3e908e9fbb0d --- /dev/null +++ b/layout/forms/nsListControlFrame.h @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#ifndef nsListControlFrame_h___ +#define nsListControlFrame_h___ + +//#include "nsHTMLContainerFrame.h" +#include "nsScrollFrame.h" +#include "nsIDOMFocusListener.h" +#include "nsIPresContext.h" +#include "nsIFormControlFrame.h" +#include "nsIPluggableEventListener.h" +#include "nsIListControlFrame.h" + +class nsIDOMHTMLSelectElement; +class nsIDOMHTMLCollection; +class nsIDOMHTMLOptionElement; +class nsFormFrame; +class nsScrollFrame; +class nsIComboboxControlFrame; + +#if 0 +struct nsInputDimSpec +{ + nsIAtom* mColSizeAttr; // attribute used to determine width + PRBool mColSizeAttrInPixels; // is attribute value in pixels (otherwise num chars) + nsIAtom* mColValueAttr; // attribute used to get value to determine size + // if not determined above + nsString* mColDefaultValue; // default value if not determined above + nscoord mColDefaultSize; // default width if not determined above + PRBool mColDefaultSizeInPixels; // is default width in pixels (otherswise num chars) + nsIAtom* mRowSizeAttr; // attribute used to determine height + nscoord mRowDefaultSize; // default height if not determined above + + nsInputDimSpec(nsIAtom* aColSizeAttr, PRBool aColSizeAttrInPixels, + nsIAtom* aColValueAttr, nsString* aColDefaultValue, + nscoord aColDefaultSize, PRBool aColDefaultSizeInPixels, + nsIAtom* aRowSizeAttr, nscoord aRowDefaultSize) + : mColSizeAttr(aColSizeAttr), mColSizeAttrInPixels(aColSizeAttrInPixels), + mColValueAttr(aColValueAttr), + mColDefaultValue(aColDefaultValue), mColDefaultSize(aColDefaultSize), + mColDefaultSizeInPixels(aColDefaultSizeInPixels), + mRowSizeAttr(aRowSizeAttr), mRowDefaultSize(aRowDefaultSize) + { + } + +}; +#endif +/** + * The block frame has two additional named child lists: + * - "Floater-list" which contains the floated frames + * - "Bullet-list" which contains the bullet frame + * + * @see nsLayoutAtoms::bulletList + * @see nsLayoutAtoms::floaterList + */ +class nsListControlFrame : public nsScrollFrame, + public nsIFormControlFrame, + public nsIPluggableEventListener, + public nsIListControlFrame +{ +public: + friend nsresult NS_NewListControlFrame(nsIFrame*& aNewFrame); + + // nsISupports + NS_DECL_ISUPPORTS + + // nsISupports overrides + // NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); + + NS_IMETHOD GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame); + + NS_IMETHOD HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + + // nsIFrame + NS_IMETHOD SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList); + NS_IMETHOD Reflow(nsIPresContext& aCX, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus); + + // +/* virtual nsresult AppendNewFrames(nsIPresContext& aPresContext, nsIFrame*); + + virtual nsresult InsertNewFrame(nsIPresContext& aPresContext, + nsBaseIBFrame* aParentFrame, + nsIFrame* aNewFrame, + nsIFrame* aPrevSibling); + + virtual nsresult DoRemoveFrame(nsBlockReflowState& aState, + nsBaseIBFrame* aParentFrame, + nsIFrame* aDeletedFrame, + nsIFrame* aPrevSibling); + */ + + NS_IMETHOD Init(nsIPresContext& aPresContext, + nsIContent* aContent, + nsIFrame* aGeometricParent, + nsIFrame* aContentParent, + nsIStyleContext* aContext); + + NS_IMETHOD Deselect(); + +#if 0 + virtual void GetStyleSize(nsIPresContext& aContext, + const nsHTMLReflowState& aReflowState, + nsSize& aSize); +#endif + + virtual void GetDesiredSize(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredLayoutSize, + nsSize& aDesiredWidgetSize); +#if 0 + nscoord CalculateSize (nsIPresContext* aPresContext, nsListControlFrame* aFrame, + const nsSize& aCSSSize, nsInputDimSpec& aDimensionSpec, + nsSize& aBounds, PRBool& aWidthExplicit, + PRBool& aHeightExplicit, nscoord& aRowSize, + nsIRenderingContext *aRendContext); +#endif + + /*virtual nsresult Focus(nsIDOMEvent* aEvent); + virtual nsresult Blur(nsIDOMEvent* aEvent); + virtual nsresult ProcessEvent(nsIDOMEvent* aEvent); + NS_IMETHOD AddEventListener(nsIDOMNode * aNode); + NS_IMETHOD RemoveEventListener(nsIDOMNode * aNode); + + NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext);*/ + + NS_METHOD GetMultiple(PRBool* aResult, nsIDOMHTMLSelectElement* aSelect = nsnull); + + ///XXX: End o the temporary methods +#if 0 + nscoord GetTextSize(nsIPresContext& aContext, nsListControlFrame* aFrame, + const nsString& aString, nsSize& aSize, + nsIRenderingContext *aRendContext); + + nscoord GetTextSize(nsIPresContext& aContext, nsListControlFrame* aFrame, + PRInt32 aNumChars, nsSize& aSize, + nsIRenderingContext *aRendContext); +#endif + NS_IMETHOD GetSize(PRInt32* aSize) const; + NS_IMETHOD GetMaxLength(PRInt32* aSize); + + static void GetScrollBarDimensions(nsIPresContext& aPresContext, + nscoord &aWidth, nscoord &aHeight); + virtual nscoord GetVerticalBorderWidth(float aPixToTwip) const; + virtual nscoord GetHorizontalBorderWidth(float aPixToTwip) const; + virtual nscoord GetVerticalInsidePadding(float aPixToTwip, + nscoord aInnerHeight) const; + virtual nscoord GetHorizontalInsidePadding(nsIPresContext& aPresContext, + float aPixToTwip, + nscoord aInnerWidth, + nscoord aCharWidth) const; + + NS_IMETHOD GetFont(nsIPresContext* aPresContext, + nsFont& aFont); + + NS_IMETHOD GetFormContent(nsIContent*& aContent) const; + + // nsIFrame + NS_IMETHOD SetRect(const nsRect& aRect); + NS_IMETHOD SizeTo(nscoord aWidth, nscoord aHeight); + + ///////////////////////// + // nsHTMLContainerFrame + ///////////////////////// + virtual PRIntn GetSkipSides() const; + + ///////////////////////// + // nsIFormControlFrame + ///////////////////////// + NS_IMETHOD GetType(PRInt32* aType) const; + + NS_IMETHOD GetName(nsString* aName); + + virtual void SetFocus(PRBool aOn = PR_TRUE, PRBool aRepaint = PR_FALSE); + + virtual void MouseClicked(nsIPresContext* aPresContext); + + virtual void Reset(); + + virtual PRBool IsSuccessful(nsIFormControlFrame* aSubmitter); + + virtual PRInt32 GetMaxNumValues(); + + virtual PRBool GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames); + + virtual void SetFormFrame(nsFormFrame* aFrame); + + // nsIPluggableEventListener + NS_IMETHOD PluggableEventHandler(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + + NS_IMETHOD PluggableGetFrameForPoint(const nsPoint& aPoint, + nsIFrame** aFrame); + + // nsIListControlFrame + NS_IMETHOD SetComboboxFrame(nsIFrame* aComboboxFrame); + NS_IMETHOD GetSelectedItem(nsString & aStr); + NS_IMETHOD AboutToDropDown(); + + + // Static Methods + static nsIDOMHTMLSelectElement* GetSelect(nsIContent * aContent); + static nsIDOMHTMLCollection* GetOptions(nsIContent * aContent, nsIDOMHTMLSelectElement* aSelect = nsnull); + static nsIDOMHTMLOptionElement* GetOption(nsIDOMHTMLCollection& aOptions, PRUint32 aIndex); + static PRBool GetOptionValue(nsIDOMHTMLCollection& aCollecton, PRUint32 aIndex, nsString& aValue); + +protected: + nsListControlFrame(); + virtual ~nsListControlFrame(); + + nsIFrame * GetOptionFromChild(nsIFrame* aParentFrame); + + nsresult GetFrameForPointUsing(const nsPoint& aPoint, + nsIAtom* aList, + nsIFrame** aFrame); + + // nsHTMLContainerFrame overrides + virtual void PaintChildren(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer); + + void ClearSelection(); + void InitializeFromContent(PRBool aDoDisplay = PR_FALSE); + + void ExtendedSelection(PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aDoInvert, PRBool aSetValue); + + NS_IMETHOD HandleLikeDropDownListEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + NS_IMETHOD HandleLikeListEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + PRInt32 SetContentSelected(nsIFrame * aHitFrame, + nsIContent *& aHitContent, + PRBool aIsSelected); + + // Data Members + nsFormFrame* mFormFrame; + + PRInt32 mNumRows; + + PRBool mIsFrameSelected[64]; + PRInt32 mNumSelections; + PRInt32 mMaxNumSelections; + PRBool mMultipleSelections; + + + //nsIContent * mSelectedContent; + PRInt32 mSelectedIndex; + PRInt32 mStartExtendedIndex; + PRInt32 mEndExtendedIndex; + + nsIFrame * mHitFrame; + nsIContent * mHitContent; + + nsIFrame * mCurrentHitFrame; + nsIContent * mCurrentHitContent; + + nsIFrame * mSelectedFrame; + nsIContent * mSelectedContent; + + PRBool mIsInitializedFromContent; + + nsIFrame * mContentFrame; + PRBool mInDropDownMode; + nsIComboboxControlFrame * mComboboxFrame; + nsString mSelectionStr; + +}; + +#endif /* nsListControlFrame_h___ */ + diff --git a/layout/html/forms/src/nsComboboxControlFrame.cpp b/layout/html/forms/src/nsComboboxControlFrame.cpp new file mode 100644 index 000000000000..2f44428145a4 --- /dev/null +++ b/layout/html/forms/src/nsComboboxControlFrame.cpp @@ -0,0 +1,901 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#include "nsComboboxControlFrame.h" +#include "nsFormFrame.h" +#include "nsButtonControlFrame.h" +#include "nsTextControlFrame.h" +#include "nsIContent.h" +#include "prtypes.h" +#include "nsIAtom.h" +#include "nsIPresContext.h" +#include "nsIHTMLContent.h" +#include "nsHTMLIIDs.h" +#include "nsHTMLAtoms.h" +#include "nsIFileWidget.h" +#include "nsITextWidget.h" +#include "nsWidgetsCID.h" +#include "nsRepository.h" +#include "nsIView.h" +#include "nsHTMLParts.h" +#include "nsIDOMHTMLInputElement.h" +#include "nsIFormControl.h" +#include "nsINameSpaceManager.h" +#include "nsITextContent.h" + +// Used for Paint +#include "nsCSSRendering.h" +#include "nsIDeviceContext.h" + +#include "nsIPresShell.h" +#include "nsIView.h" +#include "nsIViewManager.h" +#include "nsViewsCID.h" + +#include "nsIDOMElement.h" +#include "nsListControlFrame.h" +#include "nsIListControlFrame.h" + +#include "nsIDOMHTMLCollection.h" //rods +#include "nsIDOMHTMLSelectElement.h" //rods +#include "nsIDOMHTMLOptionElement.h" //rods + +// XXX make this pixels +#define CONTROL_SPACING 40 + +static NS_DEFINE_IID(kViewCID, NS_VIEW_CID); +static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID); +static NS_DEFINE_IID(kCFileWidgetCID, NS_FILEWIDGET_CID); +static NS_DEFINE_IID(kIFileWidgetIID, NS_IFILEWIDGET_IID); +static NS_DEFINE_IID(kITextWidgetIID, NS_ITEXTWIDGET_IID); +static NS_DEFINE_IID(kIFormControlFrameIID, NS_IFORMCONTROLFRAME_IID); +static NS_DEFINE_IID(kIComboboxControlFrameIID, NS_ICOMBOBOXCONTROLFRAME_IID); + +static NS_DEFINE_IID(kIDOMHTMLSelectElementIID, NS_IDOMHTMLSELECTELEMENT_IID); +static NS_DEFINE_IID(kIDOMHTMLOptionElementIID, NS_IDOMHTMLOPTIONELEMENT_IID); +static NS_DEFINE_IID(kIPluggableEventListenerIID, NS_IPLUGGABLEEVENTLISTENER_IID); +static NS_DEFINE_IID(kIListControlFrameIID, NS_ILISTCONTROLFRAME_IID); +static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID); + +extern nsresult NS_NewListControlFrame(nsIFrame*& aNewFrame); + +//-------------------------------------------------------------- +nsresult +NS_NewComboboxControlFrame(nsIFrame*& aResult) +{ + aResult = new nsComboboxControlFrame; + if (nsnull == aResult) { + return NS_ERROR_OUT_OF_MEMORY; + } + return NS_OK; +} + +//-------------------------------------------------------------- +nsComboboxControlFrame::nsComboboxControlFrame() + : nsHTMLContainerFrame() +{ + mFormFrame = nsnull; + mListFrame = nsnull; + mListControlFrame = nsnull; + mPlaceHolderFrame = nsnull; + + mVisibleStyleContext = nsnull; + mHiddenStyleContext = nsnull; + mCurrentStyleContext = nsnull; + mBlockTextStyle = nsnull; + mBlockTextSelectedStyle = nsnull; + mBlockTextSelectedFocusStyle = nsnull; + mFirstTime = PR_TRUE; + mGotFocus = PR_FALSE; +} + +//-------------------------------------------------------------- +nsComboboxControlFrame::~nsComboboxControlFrame() +{ + + NS_IF_RELEASE(mVisibleStyleContext); + NS_IF_RELEASE(mHiddenStyleContext); + NS_IF_RELEASE(mBlockTextStyle); + //NS_IF_RELEASE(mListControlFrame); + //NS_IF_RELEASE(mListFrame); + //NS_IF_RELEASE(mPlaceHolderFrame); + +} + +//-------------------------------------------------------------- +nsresult +nsComboboxControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) +{ + NS_PRECONDITION(0 != aInstancePtr, "null ptr"); + if (NULL == aInstancePtr) { + return NS_ERROR_NULL_POINTER; + } + if (aIID.Equals(kIComboboxControlFrameIID)) { + *aInstancePtr = (void*) ((nsIComboboxControlFrame*) this); + return NS_OK; + } + if (aIID.Equals(kIFormControlFrameIID)) { + *aInstancePtr = (void*) ((nsIFormControlFrame*) this); + return NS_OK; + } + if (aIID.Equals(kIPluggableEventListenerIID)) { + NS_ADDREF_THIS(); // Increase reference count for caller + *aInstancePtr = (void *)((nsIPluggableEventListener*)this); + return NS_OK; + } + return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr); +} + +//-------------------------------------------------------------- +PRBool +nsComboboxControlFrame::IsSuccessful(nsIFormControlFrame* aSubmitter) +{ + nsAutoString name; + return (NS_CONTENT_ATTR_HAS_VALUE == GetName(&name)); +} + +//-------------------------------------------------------------- +void +nsComboboxControlFrame::Reset() +{ + SetFocus(PR_TRUE, PR_TRUE); +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetType(PRInt32* aType) const +{ + *aType = NS_FORM_SELECT; + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetFormContent(nsIContent*& aContent) const +{ + return GetContent(aContent); +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetFont(nsIPresContext* aPresContext, + nsFont& aFont) +{ + nsFormControlHelper::GetFont(this, aPresContext, mStyleContext, aFont); + return NS_OK; +} + + +//-------------------------------------------------------------- +nscoord +nsComboboxControlFrame::GetVerticalBorderWidth(float aPixToTwip) const +{ + return NSIntPixelsToTwips(3, aPixToTwip); +} + + +//-------------------------------------------------------------- +nscoord +nsComboboxControlFrame::GetHorizontalBorderWidth(float aPixToTwip) const +{ + return GetVerticalBorderWidth(aPixToTwip); +} + + +//-------------------------------------------------------------- +nscoord +nsComboboxControlFrame::GetVerticalInsidePadding(float aPixToTwip, + nscoord aInnerHeight) const +{ + return 0; +} + +//-------------------------------------------------------------- +nscoord +nsComboboxControlFrame::GetHorizontalInsidePadding(nsIPresContext& aPresContext, + float aPixToTwip, + nscoord aInnerWidth, + nscoord aCharWidth) const +{ + return 0; +} + +//-------------------------------------------------------------- +// XXX this should be removed when nsView exposes it +nsIWidget* +nsComboboxControlFrame::GetWindowTemp(nsIView *aView) +{ + nsIWidget *window = nsnull; + + nsIView *ancestor = aView; + while (nsnull != ancestor) { + ancestor->GetWidget(window); + if (nsnull != window) { + return window; + } + ancestor->GetParent(ancestor); + } + return nsnull; +} + +/** + * + */ +static void ForceDrawFrame(nsIFrame * aFrame)//, PRBool) +{ + if (aFrame == nsnull) { + return; + } + nsRect rect; + nsIView * view; + nsPoint pnt; + aFrame->GetOffsetFromView(pnt, view); + aFrame->GetRect(rect); + rect.x = pnt.x; + rect.y = pnt.y; + if (view != nsnull) { + nsIViewManager * viewMgr; + view->GetViewManager(viewMgr); + if (viewMgr != nsnull) { + viewMgr->UpdateView(view, rect, 0); + NS_RELEASE(viewMgr); + } + } + +} + +//-------------------------------------------------------------- +void +nsComboboxControlFrame::SetFocus(PRBool aOn, PRBool aRepaint) +{ + //mContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, "SELECTED", PR_TRUE); + mGotFocus = aOn; + if (aRepaint) { + ForceDrawFrame(this); + } +} + + +//-------------------------------------------------------------- +// this is in response to the MouseClick from the containing browse button +// XXX still need to get filters from accept attribute +void nsComboboxControlFrame::MouseClicked(nsIPresContext* aPresContext) +{ + + if (nsnull != mListControlFrame) { + SetFocus(PR_FALSE, PR_TRUE); + mCurrentStyleContext = (mCurrentStyleContext == mHiddenStyleContext ? mVisibleStyleContext : mHiddenStyleContext); + if (mCurrentStyleContext == mVisibleStyleContext) { + mListControlFrame->AboutToDropDown(); + nsIFormControlFrame* fcFrame = nsnull; + nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame); + if ((NS_OK == result) && (nsnull != fcFrame)) { + fcFrame->SetFocus(PR_TRUE, PR_FALSE); + } + } else { + SetFocus(PR_TRUE, PR_TRUE); + } + + mListFrame->ReResolveStyleContext(aPresContext, mCurrentStyleContext); + ForceDrawFrame(mListFrame); + } + +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList) +{ + nsFormFrame::AddFormControlFrame(aPresContext, *this); + + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP nsComboboxControlFrame::Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + if (mFirstTime) { + ReResolveStyleContext(&aPresContext, mStyleContext); // XXX This temporary + mListFrame->ReResolveStyleContext(&aPresContext, mCurrentStyleContext); + mFirstTime = PR_FALSE; + } + + PRInt32 numChildren = LengthOf(mFirstChild); + + //nsIFrame* childFrame; + if (1 == numChildren) { + nsIAtom * textBlockContentPseudo = NS_NewAtom(":COMBOBOX-TEXT"); + mBlockTextStyle = aPresContext.ResolvePseudoStyleContextFor(mContent, textBlockContentPseudo, mStyleContext); + NS_RELEASE(textBlockContentPseudo); + + // XXX This code should move to Init(), someday when the frame construction + // changes are all done and Init() is always getting called... + PRBool disabled = nsFormFrame::GetDisabled(this); + } + + nsSize maxSize(aReflowState.availableWidth, aReflowState.availableHeight); + nsHTMLReflowMetrics desiredSize = aDesiredSize; + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + { + nsIDOMHTMLSelectElement* select = nsListControlFrame::GetSelect(mContent); + if (!select) { + return NS_OK; + } + nsIDOMHTMLCollection* options = nsListControlFrame::GetOptions(mContent, select); + if (!options) { + NS_RELEASE(select); + return NS_OK; + } + + // get the css size + nsSize styleSize; + nsFormControlFrame::GetStyleSize(aPresContext, aReflowState, styleSize); + + // get the size of the longest option + PRInt32 maxWidth = 1; + PRUint32 numOptions; + options->GetLength(&numOptions); + for (PRUint32 i = 0; i < numOptions; i++) { + nsIDOMHTMLOptionElement* option = nsListControlFrame::GetOption(*options, i); + if (option) { + //option->CompressContent(); + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE != option->GetText(text)) { + continue; + } + nsSize textSize; + // use the style for the select rather that the option, since widgets don't support it + nsFormControlHelper::GetTextSize(aPresContext, this, text, textSize, aReflowState.rendContext); + //nsFormControlHelper::GetTextSize(aPresContext, this, 1, textSize, aReflowState.rendContext); + if (textSize.width > maxWidth) { + maxWidth = textSize.width; + } + NS_RELEASE(option); + } + } + + PRInt32 rowHeight = 0; + nsSize calcSize, charSize; + PRBool widthExplicit, heightExplicit; + nsInputDimensionSpec textSpec(nsnull, PR_FALSE, nsnull, nsnull, + maxWidth, PR_TRUE, nsHTMLAtoms::size, 1); + // XXX fix CalculateSize to return PRUint32 + PRUint32 numRows = (PRUint32)nsFormControlHelper::CalculateSize(&aPresContext, this, styleSize, textSpec, + calcSize, widthExplicit, heightExplicit, rowHeight, + aReflowState.rendContext); + + float sp2t; + float p2t = aPresContext.GetPixelsToTwips(); + + aPresContext.GetScaledPixelsToTwips(sp2t); + nscoord onePixel = NSIntPixelsToTwips(1, sp2t); + + nscoord scrollbarWidth = 0; + nscoord scrollbarHeight = 0; + nsListControlFrame::GetScrollBarDimensions(aPresContext, scrollbarWidth, scrollbarHeight); + + nscoord extra = calcSize.height - (rowHeight * numRows); + + numRows = (numOptions > 20 ? 20 : numOptions); + calcSize.height = (numRows * rowHeight) + extra; + if (numRows < 21) { + //calcSize.width += scrollbarWidth; + } + + /*aDesiredSize.width = calcSize.width; + // account for vertical scrollbar, if present + if (!widthExplicit && (numRows < (PRInt32)numOptions)) { + aDesiredSize.width += scrollbarWidth; + } +*/ + aDesiredSize.descent = 0; + + + nsMargin border; + const nsStyleSpacing* mySpacing = (const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing); + mySpacing->CalcBorderFor(this, border); + + float scale; + float sbWidth; + float sbHeight; + nsIDeviceContext * context = aPresContext.GetDeviceContext(); + context->GetCanonicalPixelScale(scale); + context->GetScrollBarDimensions(sbWidth, sbHeight); + PRInt32 scrollbarScaledWidth = PRInt32(sbWidth * scale); + PRInt32 scrollbarScaledHeight = PRInt32(sbWidth * scale); + + nscoord adjustment = NSIntPixelsToTwips(14, p2t); + + aDesiredSize.width = calcSize.width + scrollbarScaledWidth + border.left + border.right + adjustment; + aDesiredSize.height = rowHeight + onePixel; + aDesiredSize.height = rowHeight + onePixel; + + mButtonRect.SetRect(aDesiredSize.width-scrollbarScaledWidth-border.right, border.top, + scrollbarScaledWidth, aDesiredSize.height); + + desiredSize = aDesiredSize; + aDesiredSize.height += border.top + border.bottom; + + if (nsnull != aDesiredSize.maxElementSize) { + aDesiredSize.maxElementSize->width = aDesiredSize.width; + aDesiredSize.maxElementSize->height = aDesiredSize.height; + } + + + nsRect frameRect; + GetRect(frameRect); + nsRect curRect; + mPlaceHolderFrame->GetRect(curRect); + curRect.x = frameRect.x; + curRect.y = frameRect.y + aDesiredSize.height; + mPlaceHolderFrame->SetRect(curRect); + + mListFrame->GetRect(frameRect); + + NS_RELEASE(context); + + } + //////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + + aStatus = NS_FRAME_COMPLETE; + + return NS_OK; +} + +//------------------------------------------------------------------------------ +void +nsComboboxControlFrame::PaintComboboxControl(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer) +{ + const nsStyleDisplay* disp = (const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display); + if (!disp->mVisible) { + return; + } + aRenderingContext.PushState(); + + + const nsStyleColor* myColor = (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color); + const nsStyleSpacing* mySpacing = (const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing); + const nsStyleFont* myFont = (const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font); + + + nsIStyleContext * blkStyle; + if (mGotFocus) { + blkStyle = mTextStr.Length() > 0?mBlockTextSelectedFocusStyle:mBlockTextStyle; + } else { + blkStyle = mTextStr.Length() > 0?mBlockTextSelectedStyle:mBlockTextStyle; + } + const nsStyleColor* blkColor = (const nsStyleColor*)blkStyle->GetStyleData(eStyleStruct_Color); + const nsStyleSpacing* blkSpacing = (const nsStyleSpacing*)blkStyle->GetStyleData(eStyleStruct_Spacing); + const nsStyleFont* blkFont = (const nsStyleFont*)blkStyle->GetStyleData(eStyleStruct_Font); + + nsRect rect(0, 0, mRect.width, mRect.height); + nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this, + aDirtyRect, rect, *myColor, 0, 0); + + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, + aDirtyRect, rect, *mySpacing, 0); + + nsHTMLContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer); + + const nsStyleSpacing* spacing =(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing); + nsMargin border; + spacing->CalcBorderFor(this, border); + + float p2t; + aPresContext.GetScaledPixelsToTwips(p2t); + nscoord onePixel = NSIntPixelsToTwips(1, p2t); + + nsRect outside(0, 0, mRect.width, mRect.height); + outside.Deflate(border); + + nsRect inside(outside); + outside.Deflate(onePixel, onePixel); + + aRenderingContext.SetColor(blkColor->mBackgroundColor); + aRenderingContext.FillRect(inside.x, inside.y, inside.width, inside.height); + + float appUnits; + float devUnits; + float scale; + nsIDeviceContext * context; + aRenderingContext.GetDeviceContext(context); + + context->GetCanonicalPixelScale(scale); + context->GetAppUnitsToDevUnits(devUnits); + context->GetDevUnitsToAppUnits(appUnits); + + float sbWidth; + float sbHeight; + context->GetScrollBarDimensions(sbWidth, sbHeight); + PRInt32 scrollbarScaledWidth = PRInt32(sbWidth * scale); + PRInt32 scrollbarScaledHeight = PRInt32(sbWidth * scale); + + nsFont font(aPresContext.GetDefaultFixedFont()); + GetFont(&aPresContext, font); + + aRenderingContext.SetFont(myFont->mFont); + + inside.width -= scrollbarScaledWidth; + PRBool clipEmpty; + aRenderingContext.PushState(); + aRenderingContext.SetClipRect(inside, nsClipCombine_kReplace, clipEmpty); + + nscoord x = inside.x + (onePixel * 4); + nscoord y = inside.y; + + aRenderingContext.SetColor(blkColor->mColor); + + aRenderingContext.DrawString(mTextStr, x, y, 0); + nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, + aDirtyRect, inside, *blkSpacing, 0); + + aRenderingContext.PopState(clipEmpty); + +//////////////////////////////// + + inside.width -= scrollbarScaledWidth; + inside.height -= scrollbarScaledHeight; + + // Scrollbars + //const nsStyleColor* myColor = (const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color); + //nsIAtom * sbAtom = NS_NewAtom(":SCROLLBAR-ARROW-LOOK"); + //nsIStyleContext* arrowStyle = aPresContext.ResolvePseudoStyleContextFor(mContent, sbAtom, mStyleContext); + //NS_RELEASE(sbAtom); + + const nsStyleSpacing* arrowSpacing = (const nsStyleSpacing*)mArrowStyle->GetStyleData(eStyleStruct_Spacing); + const nsStyleColor* arrowColor = (const nsStyleColor*)mArrowStyle->GetStyleData(eStyleStruct_Color); + + nsRect srect(0,0,0,0);//mRect.width-scrollbarWidth-onePixel, onePixel, scrollbarWidth, mRect.height-(onePixel*2)); + srect = mButtonRect; + nsFormControlHelper::PaintArrow(nsFormControlHelper::eArrowDirection_Down, aRenderingContext,aPresContext, + aDirtyRect, srect, onePixel, *arrowColor, *arrowSpacing, this, mRect); + //} + + + NS_RELEASE(context); + + PRBool status; + aRenderingContext.PopState(status); +} + +NS_METHOD +nsComboboxControlFrame::Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer) +{ + if (eFramePaintLayer_Content == aWhichLayer) { + PaintComboboxControl(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer); + } + return NS_OK; +} + +//-------------------------------------------------------------- +PRIntn +nsComboboxControlFrame::GetSkipSides() const +{ + return 0; +} + + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetName(nsString* aResult) +{ + nsresult result = NS_FORM_NOTOK; + if (mContent) { + nsIHTMLContent* formControl = nsnull; + result = mContent->QueryInterface(kIHTMLContentIID, (void**)&formControl); + if ((NS_OK == result) && formControl) { + nsHTMLValue value; + result = formControl->GetHTMLAttribute(nsHTMLAtoms::name, value); + if (NS_CONTENT_ATTR_HAS_VALUE == result) { + if (eHTMLUnit_String == value.GetUnit()) { + value.GetStringValue(*aResult); + } + } + NS_RELEASE(formControl); + } + } + return result; +} + +//-------------------------------------------------------------- +PRInt32 +nsComboboxControlFrame::GetMaxNumValues() +{ + return 1; +} + +//-------------------------------------------------------------- +PRBool +nsComboboxControlFrame::GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames) +{ + nsAutoString name; + nsresult result = GetName(&name); + if ((aMaxNumValues <= 0) || (NS_CONTENT_ATTR_HAS_VALUE != result)) { + return PR_FALSE; + } + + // use our name and the text widgets value + aNames[0] = name; + nsresult status = PR_FALSE; + /*nsIWidget* widget; + nsITextWidget* textWidget; + mTextFrame->GetWidget(&widget); + if (widget && (NS_OK == widget->QueryInterface(kITextWidgetIID, (void**)&textWidget))) { + PRUint32 actualSize; + textWidget->GetText(aValues[0], 0, actualSize); + aNumValues = 1; + NS_RELEASE(textWidget); + status = PR_TRUE; + } + NS_IF_RELEASE(widget);*/ + return status; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetFrameName(nsString& aResult) const +{ + return MakeFrameName("ComboboxControl", aResult); +} + + +void +nsComboboxControlFrame::RefreshStyleContext(nsIPresContext* aPresContext, + nsIAtom * aNewContentPseudo, + nsIStyleContext*& aCurrentStyle, + nsIContent * aContent, + nsIStyleContext* aParentStyle) + +{ + nsIStyleContext* newStyleContext = aPresContext->ProbePseudoStyleContextFor(aContent, + aNewContentPseudo, + aParentStyle); + if (newStyleContext != aCurrentStyle) { + NS_IF_RELEASE(aCurrentStyle); + aCurrentStyle = newStyleContext; + } else { + NS_IF_RELEASE(newStyleContext); + } +} + +NS_IMETHODIMP +nsComboboxControlFrame::ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext) +{ + nsIStyleContext* oldContext = mStyleContext; + + // NOTE: using nsFrame's ReResolveStyleContext method to avoid + // useless version in base classes. + nsresult rv = nsFrame::ReResolveStyleContext(aPresContext, aParentContext); + if (NS_FAILED(rv)) { + return rv; + } + + if (oldContext != mStyleContext) { + + PRBool currentIsVisible = (mCurrentStyleContext == mVisibleStyleContext?PR_TRUE:PR_FALSE); + + nsIAtom * visibleContentPseudo = NS_NewAtom(":DROPDOWN-VISIBLE"); + RefreshStyleContext(aPresContext, visibleContentPseudo, mVisibleStyleContext, mContent, mStyleContext); + NS_IF_RELEASE(visibleContentPseudo); + + nsIAtom * hiddenContentPseudo = NS_NewAtom(":DROPDOWN-HIDDEN"); + RefreshStyleContext(aPresContext, hiddenContentPseudo, mHiddenStyleContext, mContent, mStyleContext); + NS_IF_RELEASE(hiddenContentPseudo); + mCurrentStyleContext = (currentIsVisible?mVisibleStyleContext:mHiddenStyleContext); + + mListFrame->ReResolveStyleContext(aPresContext, + (nsnull != mCurrentStyleContext? mCurrentStyleContext : mStyleContext)); + + // Button Style + nsIAtom * btnOutContentPseudo = NS_NewAtom(":DROPDOWN-BTN-OUT"); + RefreshStyleContext(aPresContext, btnOutContentPseudo, mBtnOutStyleContext, mContent, mStyleContext); + NS_IF_RELEASE(btnOutContentPseudo); + + nsIAtom * btnPressContentPseudo = NS_NewAtom(":DROPDOWN-BTN-PRESSED"); + RefreshStyleContext(aPresContext, btnPressContentPseudo, mBtnPressedStyleContext, mContent, mStyleContext); + NS_IF_RELEASE(btnPressContentPseudo); + + nsIAtom * txtBlkContentPseudo = NS_NewAtom(":COMBOBOX-TEXT"); + RefreshStyleContext(aPresContext, txtBlkContentPseudo, mBlockTextStyle, mContent, mStyleContext); + NS_IF_RELEASE(txtBlkContentPseudo); + + nsIAtom * txtBlkSelContentPseudo = NS_NewAtom(":COMBOBOX-TEXTSELECTED"); + RefreshStyleContext(aPresContext, txtBlkSelContentPseudo, mBlockTextSelectedStyle, mContent, mStyleContext); + NS_IF_RELEASE(txtBlkSelContentPseudo); + + nsIAtom * txtBlkSelFocContentPseudo = NS_NewAtom(":COMBOBOX-TEXTSELECTEDFOCUS"); + RefreshStyleContext(aPresContext, txtBlkSelFocContentPseudo, mBlockTextSelectedFocusStyle, mContent, mStyleContext); + NS_IF_RELEASE(txtBlkSelFocContentPseudo); + + + } + + return rv; +} +//---------------------------------------------------------------------- +NS_IMETHODIMP nsComboboxControlFrame::HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + if (nsEventStatus_eConsumeNoDefault == aEventStatus) { + return NS_OK; + } + + if(nsEventStatus_eConsumeNoDefault != aEventStatus) { + if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { + } else if (aEvent->message == NS_MOUSE_MOVE && mDoingSelection || + aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { + // no-op + } else { + return NS_OK; + } + + aEventStatus = nsEventStatus_eConsumeNoDefault; + + if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { + mArrowStyle = mBtnOutStyleContext; + ForceDrawFrame(this); + //MouseClicked(&aPresContext); + + } else if (aEvent->message == NS_MOUSE_MOVE) { + + } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { + mArrowStyle = mBtnPressedStyleContext; + ForceDrawFrame(this); + MouseClicked(&aPresContext); + } + } + + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame) +{ + //nsresult rv = nsScrollFrame::GetFrameForPoint(aPoint, aFrame); + nsresult rv = GetFrameForPointUsing(aPoint, nsnull, aFrame); + if (NS_OK == rv) { + if (*aFrame != this) { + //mHitFrame = *aFrame; + *aFrame = this; + } + return NS_OK; + } + *aFrame = this; + return NS_OK; +} + +nsresult +nsComboboxControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, + nsIAtom* aList, + nsIFrame** aFrame) +{ + *aFrame = this; + return NS_OK; +} + + +//---------------------------------------------------------------------- +// nsIPluggableEventListener +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +NS_IMETHODIMP nsComboboxControlFrame::PluggableEventHandler(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + HandleEvent(aPresContext, aEvent, aEventStatus); + //aEventStatus = nsEventStatus_eConsumeNoDefault; + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsComboboxControlFrame::PluggableGetFrameForPoint(const nsPoint& aPoint, + nsIFrame** aFrame) +{ + return GetFrameForPoint(aPoint, aFrame); +} + + + +//---------------------------------------------------------------------- +// nsIComboboxControlFrame +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::SetDropDown(nsIFrame* aPlaceHolderFrame, nsIFrame* aDropDownFrame) +{ + mPlaceHolderFrame = aPlaceHolderFrame; + mListFrame = aDropDownFrame; + + if (NS_OK != mListFrame->QueryInterface(kIListControlFrameIID, (void**)&mListControlFrame)) { + return NS_ERROR_FAILURE; + } + + // Ok, since we now know we have the ListFrame, and we are assuming at this point it has been initialized + // Let's get the currently selected item, but we make the call using the Interface + mListControlFrame->GetSelectedItem(mTextStr); + + AppendChildren(mPlaceHolderFrame, PR_FALSE); + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::SetDropDownStyleContexts(nsIStyleContext * aVisible, nsIStyleContext * aHidden) +{ + mVisibleStyleContext = aVisible; + mHiddenStyleContext = aHidden; + mCurrentStyleContext = mHiddenStyleContext; + + NS_ADDREF(mVisibleStyleContext); + NS_ADDREF(mHiddenStyleContext); + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::SetButtonStyleContexts(nsIStyleContext * aOut, nsIStyleContext * aPressed) +{ + mBtnOutStyleContext = aOut; + mBtnPressedStyleContext = aPressed; + mArrowStyle = aOut; + + NS_ADDREF(mBtnOutStyleContext); + NS_ADDREF(mBtnPressedStyleContext); + return NS_OK; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsComboboxControlFrame::ListWasSelected(nsIPresContext* aPresContext) +{ + mArrowStyle = mBtnOutStyleContext; + MouseClicked(aPresContext); + + nsString str; + if (nsnull != mListControlFrame) { + mListControlFrame->GetSelectedItem(mTextStr); + nsIFormControlFrame* fcFrame = nsnull; + nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame); + if ((NS_OK == result) && (nsnull != fcFrame)) { + fcFrame->SetFocus(PR_FALSE, PR_FALSE); + } + SetFocus(PR_TRUE, PR_TRUE); + } + + + return NS_OK; +} + diff --git a/layout/html/forms/src/nsComboboxControlFrame.h b/layout/html/forms/src/nsComboboxControlFrame.h new file mode 100644 index 000000000000..7fda170523f4 --- /dev/null +++ b/layout/html/forms/src/nsComboboxControlFrame.h @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "NPL"); you may not use this file except in + * compliance with the NPL. You may obtain a copy of the NPL at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the NPL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL + * for the specific language governing rights and limitations under the + * NPL. + * + * The Initial Developer of this code under the NPL is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 1998 Netscape Communications Corporation. All Rights + * Reserved. + */ + +#ifndef nsComboboxControlFrame_h___ +#define nsComboboxControlFrame_h___ + +#include "nsHTMLContainerFrame.h" +#include "nsIFormControlFrame.h" +#include "nsIComboboxControlFrame.h" +#include "nsIPluggableEventListener.h" + +class nsButtonControlFrame; +class nsTextControlFrame; +class nsFormFrame; +class nsIView; +class nsStyleContext; +class nsIHTMLContent; +class nsIListControlFrame; + +class nsComboboxControlFrame : public nsHTMLContainerFrame, + public nsIFormControlFrame, + public nsIPluggableEventListener, + public nsIComboboxControlFrame +{ +public: + nsComboboxControlFrame(); + ~nsComboboxControlFrame(); + + NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); + + // nsIFrame + NS_IMETHOD SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList); + + NS_IMETHOD Reflow(nsIPresContext& aCX, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus); + + NS_IMETHOD ReResolveStyleContext(nsIPresContext* aPresContext, + nsIStyleContext* aParentContext); + + NS_IMETHOD Paint(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer); + + NS_IMETHOD HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + + NS_IMETHOD GetFrameName(nsString& aResult) const; + + + virtual PRInt32 GetMaxNumValues(); + + virtual PRBool GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames); + + //nsTextControlFrame* GetTextFrame() { return mTextFrame; } + + //void SetTextFrame(nsTextControlFrame* aFrame) { mTextFrame = aFrame; } + + //nsButtonControlFrame* GetBrowseFrame() { return mBrowseFrame; } + //void SetBrowseFrame(nsButtonControlFrame* aFrame) { mBrowseFrame = aFrame; } + void SetFocus(PRBool aOn, PRBool aRepaint); + virtual PRBool IsSuccessful(nsIFormControlFrame* aSubmitter); + virtual void SetFormFrame(nsFormFrame* aFormFrame) { mFormFrame = aFormFrame; } + virtual void Reset(); + NS_IMETHOD GetName(nsString* aName); + NS_IMETHOD GetType(PRInt32* aType) const; + + NS_IMETHOD GetFont(nsIPresContext* aPresContext, + nsFont& aFont); + NS_IMETHOD GetFormContent(nsIContent*& aContent) const; + virtual nscoord GetVerticalBorderWidth(float aPixToTwip) const; + virtual nscoord GetHorizontalBorderWidth(float aPixToTwip) const; + virtual nscoord GetVerticalInsidePadding(float aPixToTwip, + nscoord aInnerHeight) const; + virtual nscoord GetHorizontalInsidePadding(nsIPresContext& aPresContext, + float aPixToTwip, + nscoord aInnerWidth, + nscoord aCharWidth) const; + + + NS_IMETHOD GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame); + + nsresult GetFrameForPointUsing(const nsPoint& aPoint, + nsIAtom* aList, + nsIFrame** aFrame); + // A Static Helper Functions + + // This refreshes a particular pseudo style content when a "ReResolveStyleContent" happens + static void RefreshStyleContext(nsIPresContext* aPresContext, + nsIAtom * aNewContentPseudo, + nsIStyleContext*& aCurrentStyle, + nsIContent * aContent, + nsIStyleContext* aParentStyle); + + // nsIFormMouseListener + virtual void MouseClicked(nsIPresContext* aPresContext); + + // nsIPluggableEventListener + NS_IMETHOD PluggableEventHandler(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + + NS_IMETHOD PluggableGetFrameForPoint(const nsPoint& aPoint, + nsIFrame** aFrame); + + //static PRInt32 gSpacing; + + //nsIComboboxControlFrame + NS_IMETHOD SetDropDown(nsIFrame* aPlaceHolderFrame, nsIFrame* aDropDownFrame); + NS_IMETHOD SetDropDownStyleContexts(nsIStyleContext * aVisible, nsIStyleContext * aHidden); + NS_IMETHOD SetButtonStyleContexts(nsIStyleContext * aOut, nsIStyleContext * aPressed); + NS_IMETHOD ListWasSelected(nsIPresContext* aPresContext); + + +protected: + virtual void PaintComboboxControl(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer); + nsIWidget* GetWindowTemp(nsIView *aView); // XXX temporary + virtual PRIntn GetSkipSides() const; + + + nsFormFrame* mFormFrame; // Parent Form Frame + + nsIFrame * mPlaceHolderFrame; + nsIFrame * mListFrame; // Generic nsIFrame + nsIListControlFrame * mListControlFrame; // Specific ListControl Interface + nsRect mButtonRect; // The Arrow Button's Rect cached for Painting + + // DropDown List Visibility Styles + nsIStyleContext * mVisibleStyleContext; // Style for the DropDown List to make it visible + nsIStyleContext * mHiddenStyleContext; // Style for the DropDown List to make it hidden + nsIStyleContext * mCurrentStyleContext; // The current Style state of the DropDown List + + // Arrow Button Styles + nsIStyleContext * mBtnOutStyleContext; // Style when not pressed + nsIStyleContext * mBtnPressedStyleContext; // Style When Pressed + nsIStyleContext * mArrowStyle; // Arrows currrent style (for painting) + + // Combobox Text Styles + nsIStyleContext * mBlockTextStyle; // Style when there is no selection and it doesn't have focus + nsIStyleContext * mBlockTextSelectedStyle; // Style when selected and it doesn't have focus + nsIStyleContext * mBlockTextSelectedFocusStyle; // Style when selected and it has focus + + + PRBool mFirstTime; + + // State data members + nsString mTextStr; + PRBool mGotFocus; + +private: + NS_IMETHOD_(nsrefcnt) AddRef() { return NS_OK; } + NS_IMETHOD_(nsrefcnt) Release() { return NS_OK; } +}; + +#endif + + diff --git a/layout/html/forms/src/nsListControlFrame.cpp b/layout/html/forms/src/nsListControlFrame.cpp new file mode 100644 index 000000000000..5b679548eb39 --- /dev/null +++ b/layout/html/forms/src/nsListControlFrame.cpp @@ -0,0 +1,1634 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#include "nsListControlFrame.h" +#include "nsFormControlHelper.h" +#include "nsFormControlFrame.h" +#include "nsBlockReflowContext.h" +#include "nsBlockBandData.h" +#include "nsBulletFrame.h" +#include "nsLineBox.h" + +#include "nsFrameReflowState.h" +#include "nsLineLayout.h" +#include "nsInlineReflow.h" +#include "nsPlaceholderFrame.h" +#include "nsStyleConsts.h" +#include "nsHTMLIIDs.h" +#include "nsCSSRendering.h" +#include "nsIPresContext.h" +#include "nsIPresShell.h" +#include "nsIReflowCommand.h" +#include "nsISpaceManager.h" +#include "nsIStyleContext.h" +#include "nsIView.h" +#include "nsIFontMetrics.h" +#include "nsHTMLParts.h" +#include "nsHTMLAtoms.h" +#include "nsHTMLValue.h" +#include "nsDOMEvent.h" +#include "nsIHTMLContent.h" +#include "prprf.h" +#include "nsLayoutAtoms.h" + +#include "nsIFormControl.h" +#include "nsStyleUtil.h" +#include "nsINameSpaceManager.h" +#include "nsIDeviceContext.h" //rods +#include "nsIHTMLContent.h" //rods +#include "nsIDOMHTMLCollection.h" //rods +#include "nsIDOMHTMLSelectElement.h" //rods +//#include "nsIDOMHTMLOListElement.h" //rods +#include "nsIDOMHTMLOptionElement.h" //rods +#include "nsIDOMNode.h" //rods +#include "nsHTMLAtoms.h" //rods +#include "nsIAtom.h" //rods +#include "nsIDOMEventReceiver.h" +#include "nsIComboboxControlFrame.h" +#include "nsComboboxControlFrame.h" // for the static helper function +#include "nsIListControlFrame.h" +#include "nsIViewManager.h" +#include "nsFormFrame.h" + +#define CSS_NOTSET -1 +#define ATTR_NOTSET -1 + +// Constants +const char * kNormal = ""; +const char * kSelected = "SELECTED"; +const char * kSelectedFocus = "SELECTEDFOCUS"; + + +static NS_DEFINE_IID(kIContentIID, NS_ICONTENT_IID); +static NS_DEFINE_IID(kIFormControlFrameIID, NS_IFORMCONTROLFRAME_IID); + +static NS_DEFINE_IID(kIPluggableEventListenerIID, NS_IPLUGGABLEEVENTLISTENER_IID); +static NS_DEFINE_IID(kIListControlFrameIID, NS_ILISTCONTROLFRAME_IID); + +static NS_DEFINE_IID(kIDOMHTMLSelectElementIID, NS_IDOMHTMLSELECTELEMENT_IID); +static NS_DEFINE_IID(kIDOMHTMLOptionElementIID, NS_IDOMHTMLOPTIONELEMENT_IID); +static NS_DEFINE_IID(kIDOMNodeIID, NS_IDOMNODE_IID); +static NS_DEFINE_IID(kIDOMElementIID, NS_IDOMELEMENT_IID); + +//---------------------------------------------------------------------- + +nsresult +NS_NewListControlFrame(nsIFrame*& aNewFrame) +{ + nsListControlFrame* it = new nsListControlFrame; + if (nsnull == it) { + return NS_ERROR_OUT_OF_MEMORY; + } + aNewFrame = it; + return NS_OK; +} + +//---------------------------------------------------------------------- +nsListControlFrame::nsListControlFrame() +{ + mHitFrame = nsnull; + mCurrentHitFrame = nsnull; + mSelectedFrame = nsnull; + mSelectedContent = nsnull; + mSelectedIndex = -1; + mNumRows = 0; + mMaxNumSelections = 64; + mIsInitializedFromContent = PR_FALSE; + + PRInt32 i; + for (i=0;iFirstChild(aList, kid); + while (nsnull != kid) { + kid->GetRect(kidRect); + if (kidRect.Contains(aPoint)) { + tmp.MoveTo(aPoint.x - kidRect.x, aPoint.y - kidRect.y); + + nsIContent * content; + kid->GetContent(content); + static NS_DEFINE_IID(kIDOMHTMLOptionElementIID, NS_IDOMHTMLOPTIONELEMENT_IID); + nsIDOMHTMLOptionElement* oe; + if (content && (NS_OK == content->QueryInterface(kIDOMHTMLOptionElementIID, (void**) &oe))) { + *aFrame = kid; + NS_RELEASE(content); + return NS_OK; + } + NS_RELEASE(content); + return kid->GetFrameForPoint(tmp, aFrame); + //*aFrame = kid; + //return NS_OK; + } + kid->GetNextSibling(kid); + } + + mContentFrame->FirstChild(aList, kid); + while (nsnull != kid) { + nsFrameState state; + kid->GetFrameState(state); + if (NS_FRAME_OUTSIDE_CHILDREN & state) { + kid->GetRect(kidRect); + tmp.MoveTo(aPoint.x - kidRect.x, aPoint.y - kidRect.y); + if (NS_OK == kid->GetFrameForPoint(tmp, aFrame)) { + return NS_OK; + } + } + kid->GetNextSibling(kid); + } + *aFrame = this; + return NS_ERROR_FAILURE; +} + +//---------------------------------------------------------------------- +void +nsListControlFrame::PaintChildren(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer) +{ + + nsScrollFrame::PaintChildren(aPresContext, + aRenderingContext, + aDirtyRect, aWhichLayer); +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::Reflow(nsIPresContext& aPresContext, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus) +{ + if (!mIsInitializedFromContent) { + mIsInitializedFromContent = PR_TRUE; + InitializeFromContent(PR_TRUE); + } + + nsIFrame* childFrame; + + nscoord scrollbarWidth = 0; + nscoord scrollbarHeight = 0; + GetScrollBarDimensions(aPresContext, scrollbarWidth, scrollbarHeight); + + nsFont font(aPresContext.GetDefaultFixedFont()); + GetFont(&aPresContext, font); + + nsSize maxSize(aReflowState.availableWidth, aReflowState.availableHeight); + nsHTMLReflowMetrics desiredSize = aDesiredSize; + aDesiredSize.width = 0; + aDesiredSize.height = 0; + + // Calculate the amount of space needed for borders + const nsStyleSpacing* spacing = (const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing); + nsMargin border; + if (!spacing->GetBorder(border)) { + border.SizeTo(0, 0, 0, 0); + } + + nsSize desiredLineSize; + GetDesiredSize(&aPresContext, aReflowState, aDesiredSize, desiredLineSize); + + float p2t; + aPresContext.GetScaledPixelsToTwips(p2t); + nscoord lineEndPadding = NSIntPixelsToTwips(10, p2t);//GetHorizontalInsidePadding(aPresContext, p2t, 0, 0); + aDesiredSize.width += lineEndPadding; + + desiredSize = aDesiredSize; + nsHTMLReflowState reflowState(aPresContext, mFirstChild, aReflowState, maxSize); + nsIHTMLReflow* htmlReflow; + if (NS_OK == mFirstChild->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) { + htmlReflow->WillReflow(aPresContext); + nsresult result = htmlReflow->Reflow(aPresContext, desiredSize, reflowState, aStatus); + NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); + } + + mContentFrame->FirstChild(nsnull, childFrame); + PRInt32 numChildren = LengthOf(childFrame); + + aDesiredSize.width += border.left+border.right; + aDesiredSize.height += border.top+border.bottom; + + nscoord insideWidth = aDesiredSize.width-border.left-border.right-1; + if (mInDropDownMode) { + aDesiredSize.width += scrollbarWidth; + insideWidth += scrollbarWidth; + } else { + if (mNumRows < numChildren) { + aDesiredSize.width += scrollbarWidth; + } + } + + nsRect rect(0,0, insideWidth, desiredLineSize.height*numChildren); + mContentFrame->SetRect(rect); + + + mContentFrame->FirstChild(nsnull, childFrame); + nsPoint offset(0,0); + while (nsnull != childFrame) { // reflow, place, size the children + nsHTMLReflowState reflowState(aPresContext, childFrame, aReflowState, maxSize); + nsIHTMLReflow* htmlReflow; + + if (NS_OK == childFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow)) { + htmlReflow->WillReflow(aPresContext); + nsresult result = htmlReflow->Reflow(aPresContext, desiredSize, reflowState, aStatus); + NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); + //??nsRect rect(offset.x, offset.y, desiredLineSize.width+lineEndPadding, desiredLineSize.height); + nsRect rect(offset.x, offset.y, insideWidth, desiredLineSize.height); + childFrame->SetRect(rect); + childFrame->GetNextSibling(childFrame); + offset.x = 0; + offset.y += desiredLineSize.height; + } + } + + if (nsnull != aDesiredSize.maxElementSize) { + //XXX aDesiredSize.AddBorderPaddingToMaxElementSize(borderPadding); + aDesiredSize.maxElementSize->width = aDesiredSize.width; + aDesiredSize.maxElementSize->height = aDesiredSize.height; + } + + aStatus = NS_FRAME_COMPLETE; + + return NS_OK; +} + +NS_IMETHODIMP nsListControlFrame::SetRect(const nsRect& aRect) +{ + nsScrollFrame::SetRect(aRect); + return NS_OK; +} +NS_IMETHODIMP nsListControlFrame::SizeTo(nscoord aWidth, nscoord aHeight) +{ + nsScrollFrame::SizeTo(aWidth, aHeight); + return NS_OK; +} + +//---------------------------------------------------------------------- +nsIFrame * +nsListControlFrame::GetOptionFromChild(nsIFrame* aParentFrame) +{ + + nsIFrame* kid; + nsIFrame * frame = nsnull; + + aParentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + static NS_DEFINE_IID(kIDOMHTMLOptionElementIID, NS_IDOMHTMLOPTIONELEMENT_IID); + nsIDOMHTMLOptionElement* element; + if (content && (NS_OK == content->QueryInterface(kIDOMHTMLOptionElementIID, (void**) &element))) { + NS_RELEASE(content); + NS_RELEASE(element); + return kid; + } + NS_RELEASE(content); + nsIFrame * frame = GetOptionFromChild(kid); + if (nsnull != frame) { + return frame; + } + kid->GetNextSibling(kid); + } + return nsnull; +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetFormContent(nsIContent*& aContent) const +{ + return GetContent(aContent); +} + +//-------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetFont(nsIPresContext* aPresContext, + nsFont& aFont) +{ + nsFormControlHelper::GetFont(this, aPresContext, mStyleContext, aFont); + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::PluggableEventHandler(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + HandleEvent(aPresContext, aEvent, aEventStatus); + //aEventStatus = nsEventStatus_eConsumeNoDefault; + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::PluggableGetFrameForPoint(const nsPoint& aPoint, + nsIFrame** aFrame) +{ + return GetFrameForPoint(aPoint, aFrame); +} + +//---------------------------------------------------------------------- +PRInt32 nsListControlFrame::SetContentSelected(nsIFrame * aHitFrame, + nsIContent *& aHitContent, + PRBool aIsSelected) +{ + PRInt32 index = 0; + nsIFrame* kid; + mContentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + if (kid == aHitFrame) { + NS_IF_RELEASE(aHitContent); + kid->GetContent(aHitContent); + aHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (aIsSelected?kSelectedFocus:kNormal), PR_TRUE); + return index; + } + kid->GetNextSibling(kid); + index++; + } + return -1; +} + +//---------------------------------------------------------------------- +void nsListControlFrame::ClearSelection() +{ + PRInt32 i = 0; + nsIFrame* kid; + mContentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + if (mIsFrameSelected[i]) { + if (i != mSelectedIndex) { + content->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + mIsFrameSelected[i] = PR_FALSE; + } + } + NS_RELEASE(content); + kid->GetNextSibling(kid); + i++; + } +} + +//---------------------------------------------------------------------- +void nsListControlFrame::ExtendedSelection(PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aDoInvert, PRBool aSetValue) +{ + PRInt32 startInx; + PRInt32 endInx; + + if (aStartIndex < aEndIndex) { + startInx = aStartIndex; + endInx = aEndIndex; + } else { + startInx = aEndIndex; + endInx = aStartIndex; + } + + PRInt32 i = 0; + nsIFrame* kid; + PRBool startInverting = PR_FALSE; + mContentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + if (i == startInx) { + startInverting = PR_TRUE; + } + if (startInverting && ((i != mStartExtendedIndex && aDoInvert) || !aDoInvert)) { + if (aDoInvert) { + mIsFrameSelected[i] = !mIsFrameSelected[i]; + } else { + mIsFrameSelected[i] = aSetValue; + } + if (i != mSelectedIndex) { + content->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (mIsFrameSelected[i]?kSelected:kNormal), PR_TRUE); + } + } + if (i == endInx) { + startInverting = PR_FALSE; + } + NS_RELEASE(content); + kid->GetNextSibling(kid); + i++; + } +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::HandleLikeListEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN) { + PRInt32 oldSelectedIndex = mSelectedIndex; + mSelectedIndex = (PRInt32)SetContentSelected(mHitFrame, mHitContent, PR_TRUE); + if (-1 < mSelectedIndex) { + PRBool selected = mIsFrameSelected[mSelectedIndex]; + if (mMultipleSelections) { + + if (((nsMouseEvent *)aEvent)->isShift) { + selected = PR_TRUE; + if (mEndExtendedIndex == -1) { + mEndExtendedIndex = mSelectedIndex; + ExtendedSelection(mStartExtendedIndex, mEndExtendedIndex, PR_FALSE, PR_TRUE); + } else { + if (mStartExtendedIndex < mEndExtendedIndex) { + if (mSelectedIndex < mStartExtendedIndex) { + ExtendedSelection(mSelectedIndex, mEndExtendedIndex, PR_TRUE, PR_TRUE); + mEndExtendedIndex = mSelectedIndex; + } else if (mSelectedIndex > mEndExtendedIndex) { + ExtendedSelection(mEndExtendedIndex+1, mSelectedIndex, PR_FALSE, PR_TRUE); + mEndExtendedIndex = mSelectedIndex; + } else if (mSelectedIndex < mEndExtendedIndex) { + ExtendedSelection(mSelectedIndex+1, mEndExtendedIndex, PR_FALSE, PR_FALSE); + mEndExtendedIndex = mSelectedIndex; + } + } else if (mStartExtendedIndex > mEndExtendedIndex) { + if (mSelectedIndex > mStartExtendedIndex) { + ExtendedSelection(mEndExtendedIndex, mSelectedIndex, PR_TRUE, PR_TRUE); + mEndExtendedIndex = mSelectedIndex; + } else if (mSelectedIndex < mEndExtendedIndex) { + ExtendedSelection(mStartExtendedIndex+1, mEndExtendedIndex-1, PR_FALSE, PR_TRUE); + mEndExtendedIndex = mSelectedIndex; + } else if (mSelectedIndex > mEndExtendedIndex) { + ExtendedSelection(mEndExtendedIndex, mSelectedIndex-1, PR_FALSE, PR_FALSE); + mEndExtendedIndex = mSelectedIndex; + } + } + } + } else if (((nsMouseEvent *)aEvent)->isControl) { + selected = !selected; + if (nsnull != mSelectedContent) { + if (mHitContent != mSelectedContent) { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (mIsFrameSelected[oldSelectedIndex]?kSelected:kNormal), PR_TRUE); + } else { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); + } + } + } else { + mStartExtendedIndex = mSelectedIndex; + mEndExtendedIndex = -1; + ClearSelection(); + selected = PR_TRUE; + } + } else { + if (nsnull != mSelectedContent) { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mSelectedContent); + } + } + mIsFrameSelected[mSelectedIndex] = selected; + //mHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); + mSelectedContent = mHitContent; + mSelectedFrame = mHitFrame; + } + } + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::HandleLikeDropDownListEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ + // Mouse Move behavior is as follows: + // When the DropDown occurs, if an item is selected it displayed as being selected. + // It may or may not be currently visible, when the mouse is moved across any item + // in the the list it is displayed as the currently selected item. + // (Selection doesn't actually occur until the mouse is released, or lciked and released) + if (aEvent->message == NS_MOUSE_MOVE) { + + // If the DropDown is currently has a selected item + // then clear the selected item + if (nsnull != mSelectedContent) { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mSelectedContent); + } + + // Now check to see if the newly hit frame is different + // from the currently hit frame + if (nsnull != mHitFrame && mHitFrame != mCurrentHitFrame) { + + // Since the new frame is different then clear the selection on the old frame (the current frame) + if (nsnull != mCurrentHitContent) { + mCurrentHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mCurrentHitContent); + } + + // Find the new hit "content" from the hit frame and select it + SetContentSelected(mHitFrame, mHitContent, PR_TRUE); + + // Now cache the the newly hit frame and content as the "current" values + mCurrentHitFrame = mHitFrame; + mCurrentHitContent = mHitContent; + + } + + } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { + + // Start by finding the newly "hit" content from the hit frame + PRInt32 index = SetContentSelected(mHitFrame, mHitContent, PR_FALSE); + if (-1 < index) { + + nsIDOMHTMLOptionElement* option = nsnull; + nsresult rv = mHitContent->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); + if (NS_OK == rv) { + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE == option->GetText(text)) { + mSelectionStr = text; + } + + NS_IF_RELEASE(mSelectedContent); + mSelectedIndex = index; + + // Clean up frames before disappearing + if (nsnull != mSelectedContent) { + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mSelectedContent); + } + if (nsnull != mHitContent) { + mHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mHitContent); + } + if (nsnull != mCurrentHitContent) { + mCurrentHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kNormal, PR_TRUE); + NS_RELEASE(mCurrentHitContent); + } + NS_RELEASE(option); + } + + if (mComboboxFrame) { + mComboboxFrame->ListWasSelected(&aPresContext); + } + + } + + } + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus) +{ +/* const char * desc[] = {"NS_MOUSE_MOVE", + "NS_MOUSE_LEFT_BUTTON_UP", + "NS_MOUSE_LEFT_BUTTON_DOWN", + "","","","","","","", + "NS_MOUSE_MIDDLE_BUTTON_UP", + "NS_MOUSE_MIDDLE_BUTTON_DOWN", + "","","","","","","","", + "NS_MOUSE_RIGHT_BUTTON_UP", + "NS_MOUSE_RIGHT_BUTTON_DOWN", + "NS_MOUSE_ENTER", + "NS_MOUSE_EXIT", + "NS_MOUSE_LEFT_DOUBLECLICK", + "NS_MOUSE_MIDDLE_DOUBLECLICK", + "NS_MOUSE_RIGHT_DOUBLECLICK", + "NS_MOUSE_LEFT_CLICK", + "NS_MOUSE_MIDDLE_CLICK", + "NS_MOUSE_RIGHT_CLICK"}; + int inx = aEvent->message-NS_MOUSE_MESSAGE_START; + if (inx >= 0 && inx <= (NS_MOUSE_RIGHT_CLICK-NS_MOUSE_MESSAGE_START)) { + printf("Mouse in ListFrame %s [%d]\n", desc[inx], aEvent->message); + } else { + printf("Mouse in ListFrame [%d]\n", aEvent->message); + }*/ + if (nsEventStatus_eConsumeNoDefault == aEventStatus) { + return NS_OK; + } + + const nsStyleDisplay* disp = (const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display); + if (!disp->mVisible) { + return NS_OK; + } + + if(nsEventStatus_eConsumeNoDefault != aEventStatus) { + + aEventStatus = nsEventStatus_eConsumeNoDefault; + if (mInDropDownMode) { + HandleLikeDropDownListEvent(aPresContext, aEvent, aEventStatus); + } else { + HandleLikeListEvent(aPresContext, aEvent, aEventStatus); + } + } + + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList) +{ + mContentFrame = aChildList; + mContentFrame->SetPluggableEventListener(this); + + if (!mInDropDownMode) { + nsFormFrame::AddFormControlFrame(aPresContext, *this); + } + //aChildList->FirstChild(nsnull, mContentFrame); + + if (!mIsInitializedFromContent) { + InitializeFromContent(); + } + + return nsScrollFrame::SetInitialChildList(aPresContext, aListName, aChildList); + +} +//---------------------------------------------------------------------- +/*nsresult +nsListControlFrame::AppendNewFrames(nsIPresContext& aPresContext, + nsIFrame* aNewFrame) +{ + return nsScrollFrame::AppendNewFrames(aPresContext, aNewFrame); +} + +//---------------------------------------------------------------------- +nsresult +nsListControlFrame::InsertNewFrame(nsIPresContext& aPresContext, + nsBaseIBFrame* aParentFrame, + nsIFrame* aNewFrame, + nsIFrame* aPrevSibling) +{ + return nsScrollFrame::InsertNewFrame(aPresContext, aParentFrame, aNewFrame, aPrevSibling); +} + +//---------------------------------------------------------------------- +nsresult +nsListControlFrame::DoRemoveFrame(nsBlockReflowState& aState, + nsBaseIBFrame* aParentFrame, + nsIFrame* aDeletedFrame, + nsIFrame* aPrevSibling) +{ + return nsScrollFrame::DoRemoveFrame(aState, aParentFrame, aDeletedFrame, aPrevSibling); +}*/ + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::Init(nsIPresContext& aPresContext, + nsIContent* aContent, + nsIFrame* aGeometricParent, + nsIFrame* aContentParent, + nsIStyleContext* aContext) +{ + nsresult result = nsScrollFrame::Init(aPresContext, aContent, aGeometricParent, aContentParent, aContext); + /*if (NS_OK == result) { + nsIDOMNode* node; + if (mContent && (NS_OK == mContent->QueryInterface(kIDOMNodeIID, (void**) &node))) { + AddEventListener(node); + NS_RELEASE(node); + } + }*/ + if (NS_OK == result) { + nsIDOMHTMLSelectElement* select; + if (mContent && (NS_OK == mContent->QueryInterface(kIDOMHTMLSelectElementIID, (void**) &select))) { + select->GetMultiple(&mMultipleSelections); + select->GetSize(&mNumRows); + + NS_RELEASE(select); + } + } + + return result; + +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetMaxLength(PRInt32* aSize) +{ + *aSize = -1; + nsresult result = NS_CONTENT_ATTR_NOT_THERE; + nsIHTMLContent* content = nsnull; + mContent->QueryInterface(kIHTMLContentIID, (void**) &content); + if (nsnull != content) { + nsHTMLValue value; + result = content->GetHTMLAttribute(nsHTMLAtoms::maxlength, value); + if (eHTMLUnit_Integer == value.GetUnit()) { + *aSize = value.GetIntValue(); + } + NS_RELEASE(content); + } + return result; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetSize(PRInt32* aSize) const +{ + *aSize = -1; + nsresult result = NS_CONTENT_ATTR_NOT_THERE; + nsIHTMLContent* content = nsnull; + mContent->QueryInterface(kIHTMLContentIID, (void**) &content); + if (nsnull != content) { + nsHTMLValue value; + result = content->GetHTMLAttribute(nsHTMLAtoms::size, value); + if (eHTMLUnit_Integer == value.GetUnit()) { + *aSize = value.GetIntValue(); + } + NS_RELEASE(content); + } + return result; +} +//---------------------------------------------------------------------- +void +nsListControlFrame::GetScrollBarDimensions(nsIPresContext& aPresContext, + nscoord &aWidth, nscoord &aHeight) +{ + + aWidth = 0; + aHeight = 0; + float scale; + nsIDeviceContext* dx = nsnull; + dx = aPresContext.GetDeviceContext(); + if (nsnull != dx) { + float sbWidth; + float sbHeight; + dx->GetCanonicalPixelScale(scale); + dx->GetScrollBarDimensions(sbWidth, sbHeight); + aWidth = PRInt32(sbWidth * scale); + aHeight = PRInt32(sbHeight * scale); + NS_RELEASE(dx); + } //else { + //aWidth = 19 * sp2t; + //aHeight = scrollbarWidth; + //} + + +} + +//---------------------------------------------------------------------- +nscoord +nsListControlFrame::GetVerticalBorderWidth(float aPixToTwip) const +{ + return NSIntPixelsToTwips(3, aPixToTwip); +} + +//---------------------------------------------------------------------- +nscoord +nsListControlFrame::GetHorizontalBorderWidth(float aPixToTwip) const +{ + return GetVerticalBorderWidth(aPixToTwip); +} + +//---------------------------------------------------------------------- +nscoord +nsListControlFrame::GetVerticalInsidePadding(float aPixToTwip, + nscoord aInnerHeight) const +{ + return NSIntPixelsToTwips(0, aPixToTwip); // XXX this is really +} + +//---------------------------------------------------------------------- +nscoord +nsListControlFrame::GetHorizontalInsidePadding(nsIPresContext& aPresContext, + float aPixToTwip, + nscoord aInnerWidth, + nscoord aCharWidth) const +{ + return GetVerticalInsidePadding(aPixToTwip, aInnerWidth); +} + + +//---------------------------------------------------------------------- +void +nsListControlFrame::GetDesiredSize(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredLayoutSize, + nsSize& aDesiredWidgetSize) +{ + nsIDOMHTMLSelectElement* select = GetSelect(mContent); + if (!select) { + return; + } + nsIDOMHTMLCollection* options = GetOptions(mContent, select); + if (!options) { + NS_RELEASE(select); + return; + } + + // get the css size + nsSize styleSize; + nsFormControlFrame::GetStyleSize(*aPresContext, aReflowState, styleSize); + + // get the size of the longest option + PRInt32 maxWidth = 1; + PRUint32 numOptions; + options->GetLength(&numOptions); + for (PRUint32 i = 0; i < numOptions; i++) { + nsIDOMHTMLOptionElement* option = GetOption(*options, i); + if (option) { + //option->CompressContent(); + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE != option->GetText(text)) { + continue; + } + nsSize textSize; + // use the style for the select rather that the option, since widgets don't support it + nsFormControlHelper::GetTextSize(*aPresContext, this, text, textSize, aReflowState.rendContext); + if (textSize.width > maxWidth) { + maxWidth = textSize.width; + } + NS_RELEASE(option); + } + } + + PRInt32 rowHeight = 0; + nsSize calcSize, charSize; + PRBool widthExplicit, heightExplicit; + nsInputDimensionSpec textSpec(nsnull, PR_FALSE, nsnull, nsnull, + maxWidth, PR_TRUE, nsHTMLAtoms::size, 1); + // XXX fix CalculateSize to return PRUint32 + mNumRows = (PRUint32)nsFormControlHelper::CalculateSize(aPresContext, this, styleSize, textSpec, + calcSize, widthExplicit, heightExplicit, rowHeight, + aReflowState.rendContext); + + float sp2t; + float p2t = aPresContext->GetPixelsToTwips(); + + aPresContext->GetScaledPixelsToTwips(sp2t); + + //nscoord scrollbarWidth = 0; + //nscoord scrollbarHeight = 0; + //GetScrollBarDimensions(*aPresContext, scrollbarWidth, scrollbarHeight); + + if (mInDropDownMode) { + //PRUint32 numOptions; + //options->GetLength(&numOptions); + nscoord extra = calcSize.height - (rowHeight * mNumRows); + + mNumRows = (numOptions > 20 ? 20 : numOptions); + calcSize.height = (mNumRows * rowHeight) + extra; + if (mNumRows < 21) { + //calcSize.width += scrollbarWidth; + } + } + + aDesiredLayoutSize.width = calcSize.width; + // account for vertical scrollbar, if present + //if (!widthExplicit && ((mNumRows < numOptions) || mIsComboBox)) { + //if (!widthExplicit && (mNumRows < (PRInt32)numOptions)) { + // aDesiredLayoutSize.width += scrollbarWidth; + // } + + // XXX put this in widget library, combo boxes are fixed height (visible part) + //aDesiredLayoutSize.height = (mIsComboBox) + // ? rowHeight + (2 * GetVerticalInsidePadding(p2t, rowHeight)) + // : calcSize.height; + aDesiredLayoutSize.height = calcSize.height; + aDesiredLayoutSize.ascent = aDesiredLayoutSize.height; + aDesiredLayoutSize.descent = 0; + + aDesiredWidgetSize.width = maxWidth; + aDesiredWidgetSize.height = rowHeight; + //if (mIsComboBox) { // add in pull down size + // PRInt32 extra = NSIntPixelsToTwips(10, p2t*scale); + // aDesiredWidgetSize.height += (rowHeight * (numOptions > 20 ? 20 : numOptions)) + extra; + //} + + // override the width and height for a combo box that has already got a widget + //if (mWidget && mIsComboBox) { + // nscoord ignore; + // GetWidgetSize(*aPresContext, ignore, aDesiredLayoutSize.height); + // aDesiredLayoutSize.ascent = aDesiredLayoutSize.height; + //} + + NS_RELEASE(select); + NS_RELEASE(options); + +} + + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetMultiple(PRBool* aMultiple, nsIDOMHTMLSelectElement* aSelect) +{ + if (!aSelect) { + nsIDOMHTMLSelectElement* select = nsnull; + nsresult result = mContent->QueryInterface(kIDOMHTMLSelectElementIID, (void**)&select); + if ((NS_OK == result) && select) { + result = select->GetMultiple(aMultiple); + NS_RELEASE(select); + } + return result; + } else { + return aSelect->GetMultiple(aMultiple); + } +} + +//---------------------------------------------------------------------- +nsIDOMHTMLSelectElement* +nsListControlFrame::GetSelect(nsIContent * aContent) +{ + nsIDOMHTMLSelectElement* select = nsnull; + nsresult result = aContent->QueryInterface(kIDOMHTMLSelectElementIID, (void**)&select); + if ((NS_OK == result) && select) { + return select; + } else { + return nsnull; + } +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::AboutToDropDown() +{ + PRInt32 i = 0; + nsIFrame* kid; + mContentFrame->FirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + if (i == mSelectedIndex) { + mSelectedContent = content; + mSelectedFrame = kid; + mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, kSelected, PR_TRUE); + return NS_OK; + } + NS_RELEASE(content); + kid->GetNextSibling(kid); + i++; + } + return NS_OK; +} + +//---------------------------------------------------------------------- +void +nsListControlFrame::InitializeFromContent(PRBool aDoDisplay) +{ + PRInt32 i; + for (i=0;iFirstChild(nsnull, kid); + while (nsnull != kid) { + nsIContent * content; + kid->GetContent(content); + + nsIDOMHTMLOptionElement* option = nsnull; + nsresult result = content->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); + if (result == NS_OK) { + PRBool selected; + option->GetDefaultSelected(&selected); + option->GetSelected(&selected); + mIsFrameSelected[i] = selected; + + if (mInDropDownMode) { + if (selected) { + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE == option->GetText(text)) { + mSelectionStr = text; + } + mSelectedIndex = i; + NS_RELEASE(option); + mSelectedContent = content; + mSelectedFrame = kid; + return; + } + } else { + if (selected && aDoDisplay) { + content->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelected:kNormal), PR_TRUE); + } + } + NS_RELEASE(option); + } + NS_RELEASE(content); + kid->GetNextSibling(kid); + i++; + } + +} + +//---------------------------------------------------------------------- +nsIDOMHTMLCollection* +nsListControlFrame::GetOptions(nsIContent * aContent, nsIDOMHTMLSelectElement* aSelect) +{ + nsIDOMHTMLCollection* options = nsnull; + if (!aSelect) { + nsIDOMHTMLSelectElement* select = GetSelect(aContent); + if (select) { + select->GetOptions(&options); + NS_RELEASE(select); + return options; + } else { + return nsnull; + } + } else { + aSelect->GetOptions(&options); + return options; + } +} + +//---------------------------------------------------------------------- +nsIDOMHTMLOptionElement* +nsListControlFrame::GetOption(nsIDOMHTMLCollection& aCollection, PRUint32 aIndex) +{ + nsIDOMNode* node = nsnull; + PRBool status = PR_FALSE; + if ((NS_OK == aCollection.Item(aIndex, &node)) && node) { + nsIDOMHTMLOptionElement* option = nsnull; + nsresult result = node->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); + NS_RELEASE(node); + return option; + } + return nsnull; +} + + +//---------------------------------------------------------------------- +PRBool +nsListControlFrame::GetOptionValue(nsIDOMHTMLCollection& aCollection, PRUint32 aIndex, nsString& aValue) +{ + PRBool status = PR_FALSE; + nsIDOMHTMLOptionElement* option = GetOption(aCollection, aIndex); + if (option) { + nsresult result = option->GetValue(aValue); + if (aValue.Length() > 0) { + status = PR_TRUE; + } else { + result = option->GetText(aValue); + if (aValue.Length() > 0) { + status = PR_TRUE; + } + } + NS_RELEASE(option); + } + return status; +} + + + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::Deselect() +{ + PRInt32 i; + for (i=0;iGetFont(&aPresContext, font); + nsIDeviceContext* deviceContext = aPresContext.GetDeviceContext(); + + nsIFontMetrics* fontMet; + deviceContext->GetMetricsFor(font, fontMet); + aRendContext->SetFont(fontMet); + aRendContext->GetWidth(aString, aSize.width); + fontMet->GetHeight(aSize.height); + + char char1, char2; + nsCompatibility mode = nsFormControlHelper::GetRepChars(aPresContext, char1, char2); + nscoord char1Width, char2Width; + aRendContext->GetWidth(char1, char1Width); + aRendContext->GetWidth(char2, char2Width); + + NS_RELEASE(fontMet); + NS_RELEASE(deviceContext); + + if (eCompatibility_Standard == mode) { + return ((char1Width + char2Width) / 2) + 1; + } else { + return char1Width; + } +} + +//------------------------------------------------------------------ +nscoord +nsListControlFrame::GetTextSize(nsIPresContext& aPresContext, nsListControlFrame* aFrame, + PRInt32 aNumChars, nsSize& aSize, + nsIRenderingContext *aRendContext) +{ + nsAutoString val; + char char1, char2; + nsFormControlHelper::GetRepChars(aPresContext, char1, char2); + int i; + for (i = 0; i < aNumChars; i+=2) { + val += char1; + } + for (i = 1; i < aNumChars; i+=2) { + val += char2; + } + return GetTextSize(aPresContext, aFrame, val, aSize, aRendContext); +} + +//------------------------------------------------------------------ +PRInt32 +nsListControlFrame::CalculateSize (nsIPresContext* aPresContext, nsListControlFrame* aFrame, + const nsSize& aCSSSize, nsInputDimensionSpec& aSpec, + nsSize& aBounds, PRBool& aWidthExplicit, + PRBool& aHeightExplicit, nscoord& aRowHeight, + nsIRenderingContext *aRendContext) +{ + nscoord charWidth = 0; + PRInt32 numRows = ATTR_NOTSET; + aWidthExplicit = PR_FALSE; + aHeightExplicit = PR_FALSE; + + aBounds.width = CSS_NOTSET; + aBounds.height = CSS_NOTSET; + nsSize textSize(0,0); + + nsIContent* iContent = nsnull; + aFrame->GetContent((nsIContent*&) iContent); + if (!iContent) { + return 0; + } + nsIHTMLContent* hContent = nsnull; + nsresult result = iContent->QueryInterface(kIHTMLContentIID, (void**)&hContent); + if ((NS_OK != result) || !hContent) { + NS_RELEASE(iContent); + return 0; + } + nsAutoString valAttr; + nsresult valStatus = NS_CONTENT_ATTR_NOT_THERE; + if (nsnull != aSpec.mColValueAttr) { + valStatus = hContent->GetAttribute(kNameSpaceID_HTML, aSpec.mColValueAttr, valAttr); + } + nsHTMLValue colAttr; + nsresult colStatus = NS_CONTENT_ATTR_NOT_THERE; + if (nsnull != aSpec.mColSizeAttr) { + colStatus = hContent->GetHTMLAttribute(aSpec.mColSizeAttr, colAttr); + } + float p2t; + aPresContext->GetScaledPixelsToTwips(p2t); + + // determine the width + nscoord adjSize; + if (NS_CONTENT_ATTR_HAS_VALUE == colStatus) { // col attr will provide width + PRInt32 col = ((colAttr.GetUnit() == eHTMLUnit_Pixel) ? colAttr.GetPixelValue() : colAttr.GetIntValue()); + if (aSpec.mColSizeAttrInPixels) { + col = (col <= 0) ? 15 : col; + aBounds.width = NSIntPixelsToTwips(col, p2t); + } + else { + col = (col <= 0) ? 1 : col; + charWidth = GetTextSize(*aPresContext, aFrame, col, aBounds, aRendContext); + aRowHeight = aBounds.height; // XXX aBounds.height has CSS_NOTSET + } + if (aSpec.mColSizeAttrInPixels) { + aWidthExplicit = PR_TRUE; + } + } + else { + if (CSS_NOTSET != aCSSSize.width) { // css provides width + aBounds.width = (aCSSSize.width > 0) ? aCSSSize.width : 1; + aWidthExplicit = PR_TRUE; + } + else { + if (NS_CONTENT_ATTR_HAS_VALUE == valStatus) { // use width of initial value + charWidth = GetTextSize(*aPresContext, aFrame, valAttr, aBounds, aRendContext); + } + else if (aSpec.mColDefaultValue) { // use default value + charWidth = GetTextSize(*aPresContext, aFrame, *aSpec.mColDefaultValue, aBounds, aRendContext); + } + else if (aSpec.mColDefaultSizeInPixels) { // use default width in pixels + charWidth = GetTextSize(*aPresContext, aFrame, 1, aBounds, aRendContext); + aBounds.width = aSpec.mColDefaultSize; + } + else { // use default width in num characters + charWidth = GetTextSize(*aPresContext, aFrame, aSpec.mColDefaultSize, aBounds, aRendContext); + } + aRowHeight = aBounds.height; // XXX aBounds.height has CSS_NOTSET + } + } + + // determine the height + nsHTMLValue rowAttr; + nsresult rowStatus = NS_CONTENT_ATTR_NOT_THERE; + if (nsnull != aSpec.mRowSizeAttr) { + rowStatus = hContent->GetHTMLAttribute(aSpec.mRowSizeAttr, rowAttr); + } + if (NS_CONTENT_ATTR_HAS_VALUE == rowStatus) { // row attr will provide height + PRInt32 rowAttrInt = ((rowAttr.GetUnit() == eHTMLUnit_Pixel) ? rowAttr.GetPixelValue() : rowAttr.GetIntValue()); + adjSize = (rowAttrInt > 0) ? rowAttrInt : 1; + if (0 == charWidth) { + charWidth = GetTextSize(*aPresContext, aFrame, 1, textSize, aRendContext); + aBounds.height = textSize.height * adjSize; + aRowHeight = textSize.height; + numRows = adjSize; + } + else { + aBounds.height = aBounds.height * adjSize; + } + } + else if (CSS_NOTSET != aCSSSize.height) { // css provides height + aBounds.height = (aCSSSize.height > 0) ? aCSSSize.height : 1; + aHeightExplicit = PR_TRUE; + } + else { // use default height in num lines + if (0 == charWidth) { + charWidth = GetTextSize(*aPresContext, aFrame, 1, textSize, aRendContext); + aBounds.height = textSize.height * aSpec.mRowDefaultSize; + aRowHeight = textSize.height; + } + else { + aBounds.height = aBounds.height * aSpec.mRowDefaultSize; + } + } + + if ((0 == charWidth) || (0 == textSize.width)) { + charWidth = GetTextSize(*aPresContext, aFrame, 1, textSize, aRendContext); + aRowHeight = textSize.height; + } + + // add inside padding if necessary + if (!aWidthExplicit) { + aBounds.width += (2 * aFrame->GetHorizontalInsidePadding(*aPresContext, p2t, aBounds.width, charWidth)); + } + if (!aHeightExplicit) { + aBounds.height += (2 * aFrame->GetVerticalInsidePadding(p2t, textSize.height)); + } + + NS_RELEASE(hContent); + if (ATTR_NOTSET == numRows) { + numRows = aBounds.height / aRowHeight; + } + + NS_RELEASE(iContent); + return numRows; +} + +#endif + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// nsHTMLContainerFrame +//---------------------------------------------------------------------- +PRIntn +nsListControlFrame::GetSkipSides() const +{ + return 0; +} + +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +//---------------------------------------------------------------------- +// nsIFormControlFrame +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetType(PRInt32* aType) const +{ + /*nsresult result = NS_FORM_NOTOK; + if (mContent) { + nsIFormControl* formControl = nsnull; + result = mContent->QueryInterface(kIFormControlIID, (void**)&formControl); + if ((NS_OK == result) && formControl) { + result = formControl->GetType(aType); + NS_RELEASE(formControl); + } + } + return result; + */ + *aType = NS_FORM_SELECT; + return NS_OK; +} + +void +nsListControlFrame::SetFormFrame(nsFormFrame* aFormFrame) +{ + mFormFrame = aFormFrame; +} + +//---------------------------------------------------------------------- +PRBool +nsListControlFrame::IsSuccessful(nsIFormControlFrame* aSubmitter) +{ + nsAutoString name; + return (NS_CONTENT_ATTR_HAS_VALUE == GetName(&name)); +} + +//---------------------------------------------------------------------- +void +nsListControlFrame::MouseClicked(nsIPresContext* aPresContext) +{ + /*nsIRadioButton* radioWidget; + if (mWidget && (NS_OK == mWidget->QueryInterface(kIRadioIID, (void**)&radioWidget))) { + radioWidget->SetState(PR_TRUE); + NS_RELEASE(radioWidget); + if (mFormFrame) { + mFormFrame->OnRadioChecked(*this); + } + }*/ +} +//---------------------------------------------------------------------- +PRInt32 +nsListControlFrame::GetMaxNumValues() +{ + PRBool multiple; + GetMultiple(&multiple); + if (multiple) { + PRUint32 length = 0; + nsIDOMHTMLCollection* options = GetOptions(mContent); + if (options) { + options->GetLength(&length); + } + return (PRInt32)length; // XXX fix return on GetMaxNumValues + } else { + return 1; + } +} + +//---------------------------------------------------------------------- +void +nsListControlFrame::Reset() +{ + SetFocus(PR_TRUE, PR_TRUE); + + nsIDOMHTMLCollection* options = GetOptions(mContent); + if (!options) { + return; + } + + PRUint32 numOptions; + options->GetLength(&numOptions); + + Deselect(); + for (PRUint32 i = 0; i < numOptions; i++) { + nsIDOMHTMLOptionElement* option = GetOption(*options, i); + if (option) { + PRBool selected = PR_FALSE; + option->GetSelected(&selected); + if (selected) { + mIsFrameSelected[i] = PR_TRUE; + } + NS_RELEASE(option); + } + } + + // update visual here + NS_RELEASE(options); +} +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetName(nsString* aResult) +{ + nsresult result = NS_FORM_NOTOK; + if (mContent) { + nsIHTMLContent* formControl = nsnull; + result = mContent->QueryInterface(kIHTMLContentIID, (void**)&formControl); + if ((NS_OK == result) && formControl) { + nsHTMLValue value; + result = formControl->GetHTMLAttribute(nsHTMLAtoms::name, value); + if (NS_CONTENT_ATTR_HAS_VALUE == result) { + if (eHTMLUnit_String == value.GetUnit()) { + value.GetStringValue(*aResult); + } + } + NS_RELEASE(formControl); + } + } + return result; +} + + +//---------------------------------------------------------------------- +/*PRBool +nsListControlFrame::GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames) +{ + aNumValues = 0; + return PR_FALSE; +}*/ + +//---------------------------------------------------------------------- +PRBool +nsListControlFrame::GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames) +{ + PRBool status = PR_FALSE; + + aNumValues = 0; + nsAutoString name; + nsresult result = GetName(&name); + if ((aMaxNumValues <= 0) || (NS_CONTENT_ATTR_NOT_THERE == result)) { + return PR_FALSE; + } + + nsIDOMHTMLCollection* options = GetOptions(mContent); + if (!options) { + return PR_FALSE; + } + + NS_ASSERTION(aMaxNumValues >= mNumSelections, "invalid max num values"); + if (mNumSelections >= 0) { + PRInt32* selections = new PRInt32[mNumSelections]; + PRInt32 i = 0; + PRInt32 inx; + for (inx=0;iSetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (aOn?kSelectedFocus:kNormal), PR_TRUE); + } + +} + +//---------------------------------------------------------------------- +// nsIListControlFrame +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::SetComboboxFrame(nsIFrame* aComboboxFrame) +{ + static NS_DEFINE_IID(kIComboboxIID, NS_ICOMBOBOXCONTROLFRAME_IID); + if (aComboboxFrame && (NS_OK == aComboboxFrame->QueryInterface(kIComboboxIID, (void**) &mComboboxFrame))) { + mInDropDownMode = PR_TRUE; + } + return NS_OK; +} + +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::GetSelectedItem(nsString & aStr) +{ + aStr = mSelectionStr; + return NS_OK; +} + +#if 0 +//---------------------------------------------------------------------- +NS_IMETHODIMP +nsListControlFrame::DeleteFrame(nsIPresContext& aPresContext) +{ + nsIDOMNode* node; + if (mContent && (NS_OK == mContent->QueryInterface(kIDOMNodeIID, (void**) &node))) { + RemoveEventListener(node); + NS_RELEASE(node); + } + return nsScrollFrame::DeleteFrame(aPresContext); + +} +//----------------------------------------------------------------- +nsresult nsListControlFrame::Focus(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +//----------------------------------------------------------------- +nsresult nsListControlFrame::Blur(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +//----------------------------------------------------------------- +nsresult nsListControlFrame::ProcessEvent(nsIDOMEvent* aEvent) +{ + return NS_OK; +} + +//----------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::AddEventListener(nsIDOMNode * aNode) +{ + nsIDOMEventReceiver * receiver; + + if (NS_OK == aNode->QueryInterface(kIDOMEventReceiverIID, (void**) &receiver)) { + receiver->AddEventListener((nsIDOMFocusListener*)this, kIDOMFocusListenerIID); + NS_RELEASE(receiver); + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +//----------------------------------------------------------------- +NS_IMETHODIMP nsListControlFrame::RemoveEventListener(nsIDOMNode * aNode) +{ + nsIDOMEventReceiver * receiver; + + if (NS_OK == aNode->QueryInterface(kIDOMEventReceiverIID, (void**) &receiver)) { + receiver->RemoveEventListener(this, kIDOMFocusListenerIID); + NS_RELEASE(receiver); + return NS_OK; + } + return NS_ERROR_FAILURE; +} +#endif diff --git a/layout/html/forms/src/nsListControlFrame.h b/layout/html/forms/src/nsListControlFrame.h new file mode 100644 index 000000000000..3e908e9fbb0d --- /dev/null +++ b/layout/html/forms/src/nsListControlFrame.h @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ +#ifndef nsListControlFrame_h___ +#define nsListControlFrame_h___ + +//#include "nsHTMLContainerFrame.h" +#include "nsScrollFrame.h" +#include "nsIDOMFocusListener.h" +#include "nsIPresContext.h" +#include "nsIFormControlFrame.h" +#include "nsIPluggableEventListener.h" +#include "nsIListControlFrame.h" + +class nsIDOMHTMLSelectElement; +class nsIDOMHTMLCollection; +class nsIDOMHTMLOptionElement; +class nsFormFrame; +class nsScrollFrame; +class nsIComboboxControlFrame; + +#if 0 +struct nsInputDimSpec +{ + nsIAtom* mColSizeAttr; // attribute used to determine width + PRBool mColSizeAttrInPixels; // is attribute value in pixels (otherwise num chars) + nsIAtom* mColValueAttr; // attribute used to get value to determine size + // if not determined above + nsString* mColDefaultValue; // default value if not determined above + nscoord mColDefaultSize; // default width if not determined above + PRBool mColDefaultSizeInPixels; // is default width in pixels (otherswise num chars) + nsIAtom* mRowSizeAttr; // attribute used to determine height + nscoord mRowDefaultSize; // default height if not determined above + + nsInputDimSpec(nsIAtom* aColSizeAttr, PRBool aColSizeAttrInPixels, + nsIAtom* aColValueAttr, nsString* aColDefaultValue, + nscoord aColDefaultSize, PRBool aColDefaultSizeInPixels, + nsIAtom* aRowSizeAttr, nscoord aRowDefaultSize) + : mColSizeAttr(aColSizeAttr), mColSizeAttrInPixels(aColSizeAttrInPixels), + mColValueAttr(aColValueAttr), + mColDefaultValue(aColDefaultValue), mColDefaultSize(aColDefaultSize), + mColDefaultSizeInPixels(aColDefaultSizeInPixels), + mRowSizeAttr(aRowSizeAttr), mRowDefaultSize(aRowDefaultSize) + { + } + +}; +#endif +/** + * The block frame has two additional named child lists: + * - "Floater-list" which contains the floated frames + * - "Bullet-list" which contains the bullet frame + * + * @see nsLayoutAtoms::bulletList + * @see nsLayoutAtoms::floaterList + */ +class nsListControlFrame : public nsScrollFrame, + public nsIFormControlFrame, + public nsIPluggableEventListener, + public nsIListControlFrame +{ +public: + friend nsresult NS_NewListControlFrame(nsIFrame*& aNewFrame); + + // nsISupports + NS_DECL_ISUPPORTS + + // nsISupports overrides + // NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); + + NS_IMETHOD GetFrameForPoint(const nsPoint& aPoint, nsIFrame** aFrame); + + NS_IMETHOD HandleEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + + // nsIFrame + NS_IMETHOD SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList); + NS_IMETHOD Reflow(nsIPresContext& aCX, + nsHTMLReflowMetrics& aDesiredSize, + const nsHTMLReflowState& aReflowState, + nsReflowStatus& aStatus); + + // +/* virtual nsresult AppendNewFrames(nsIPresContext& aPresContext, nsIFrame*); + + virtual nsresult InsertNewFrame(nsIPresContext& aPresContext, + nsBaseIBFrame* aParentFrame, + nsIFrame* aNewFrame, + nsIFrame* aPrevSibling); + + virtual nsresult DoRemoveFrame(nsBlockReflowState& aState, + nsBaseIBFrame* aParentFrame, + nsIFrame* aDeletedFrame, + nsIFrame* aPrevSibling); + */ + + NS_IMETHOD Init(nsIPresContext& aPresContext, + nsIContent* aContent, + nsIFrame* aGeometricParent, + nsIFrame* aContentParent, + nsIStyleContext* aContext); + + NS_IMETHOD Deselect(); + +#if 0 + virtual void GetStyleSize(nsIPresContext& aContext, + const nsHTMLReflowState& aReflowState, + nsSize& aSize); +#endif + + virtual void GetDesiredSize(nsIPresContext* aPresContext, + const nsHTMLReflowState& aReflowState, + nsHTMLReflowMetrics& aDesiredLayoutSize, + nsSize& aDesiredWidgetSize); +#if 0 + nscoord CalculateSize (nsIPresContext* aPresContext, nsListControlFrame* aFrame, + const nsSize& aCSSSize, nsInputDimSpec& aDimensionSpec, + nsSize& aBounds, PRBool& aWidthExplicit, + PRBool& aHeightExplicit, nscoord& aRowSize, + nsIRenderingContext *aRendContext); +#endif + + /*virtual nsresult Focus(nsIDOMEvent* aEvent); + virtual nsresult Blur(nsIDOMEvent* aEvent); + virtual nsresult ProcessEvent(nsIDOMEvent* aEvent); + NS_IMETHOD AddEventListener(nsIDOMNode * aNode); + NS_IMETHOD RemoveEventListener(nsIDOMNode * aNode); + + NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext);*/ + + NS_METHOD GetMultiple(PRBool* aResult, nsIDOMHTMLSelectElement* aSelect = nsnull); + + ///XXX: End o the temporary methods +#if 0 + nscoord GetTextSize(nsIPresContext& aContext, nsListControlFrame* aFrame, + const nsString& aString, nsSize& aSize, + nsIRenderingContext *aRendContext); + + nscoord GetTextSize(nsIPresContext& aContext, nsListControlFrame* aFrame, + PRInt32 aNumChars, nsSize& aSize, + nsIRenderingContext *aRendContext); +#endif + NS_IMETHOD GetSize(PRInt32* aSize) const; + NS_IMETHOD GetMaxLength(PRInt32* aSize); + + static void GetScrollBarDimensions(nsIPresContext& aPresContext, + nscoord &aWidth, nscoord &aHeight); + virtual nscoord GetVerticalBorderWidth(float aPixToTwip) const; + virtual nscoord GetHorizontalBorderWidth(float aPixToTwip) const; + virtual nscoord GetVerticalInsidePadding(float aPixToTwip, + nscoord aInnerHeight) const; + virtual nscoord GetHorizontalInsidePadding(nsIPresContext& aPresContext, + float aPixToTwip, + nscoord aInnerWidth, + nscoord aCharWidth) const; + + NS_IMETHOD GetFont(nsIPresContext* aPresContext, + nsFont& aFont); + + NS_IMETHOD GetFormContent(nsIContent*& aContent) const; + + // nsIFrame + NS_IMETHOD SetRect(const nsRect& aRect); + NS_IMETHOD SizeTo(nscoord aWidth, nscoord aHeight); + + ///////////////////////// + // nsHTMLContainerFrame + ///////////////////////// + virtual PRIntn GetSkipSides() const; + + ///////////////////////// + // nsIFormControlFrame + ///////////////////////// + NS_IMETHOD GetType(PRInt32* aType) const; + + NS_IMETHOD GetName(nsString* aName); + + virtual void SetFocus(PRBool aOn = PR_TRUE, PRBool aRepaint = PR_FALSE); + + virtual void MouseClicked(nsIPresContext* aPresContext); + + virtual void Reset(); + + virtual PRBool IsSuccessful(nsIFormControlFrame* aSubmitter); + + virtual PRInt32 GetMaxNumValues(); + + virtual PRBool GetNamesValues(PRInt32 aMaxNumValues, PRInt32& aNumValues, + nsString* aValues, nsString* aNames); + + virtual void SetFormFrame(nsFormFrame* aFrame); + + // nsIPluggableEventListener + NS_IMETHOD PluggableEventHandler(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + + NS_IMETHOD PluggableGetFrameForPoint(const nsPoint& aPoint, + nsIFrame** aFrame); + + // nsIListControlFrame + NS_IMETHOD SetComboboxFrame(nsIFrame* aComboboxFrame); + NS_IMETHOD GetSelectedItem(nsString & aStr); + NS_IMETHOD AboutToDropDown(); + + + // Static Methods + static nsIDOMHTMLSelectElement* GetSelect(nsIContent * aContent); + static nsIDOMHTMLCollection* GetOptions(nsIContent * aContent, nsIDOMHTMLSelectElement* aSelect = nsnull); + static nsIDOMHTMLOptionElement* GetOption(nsIDOMHTMLCollection& aOptions, PRUint32 aIndex); + static PRBool GetOptionValue(nsIDOMHTMLCollection& aCollecton, PRUint32 aIndex, nsString& aValue); + +protected: + nsListControlFrame(); + virtual ~nsListControlFrame(); + + nsIFrame * GetOptionFromChild(nsIFrame* aParentFrame); + + nsresult GetFrameForPointUsing(const nsPoint& aPoint, + nsIAtom* aList, + nsIFrame** aFrame); + + // nsHTMLContainerFrame overrides + virtual void PaintChildren(nsIPresContext& aPresContext, + nsIRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsFramePaintLayer aWhichLayer); + + void ClearSelection(); + void InitializeFromContent(PRBool aDoDisplay = PR_FALSE); + + void ExtendedSelection(PRInt32 aStartIndex, PRInt32 aEndIndex, PRBool aDoInvert, PRBool aSetValue); + + NS_IMETHOD HandleLikeDropDownListEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + NS_IMETHOD HandleLikeListEvent(nsIPresContext& aPresContext, + nsGUIEvent* aEvent, + nsEventStatus& aEventStatus); + PRInt32 SetContentSelected(nsIFrame * aHitFrame, + nsIContent *& aHitContent, + PRBool aIsSelected); + + // Data Members + nsFormFrame* mFormFrame; + + PRInt32 mNumRows; + + PRBool mIsFrameSelected[64]; + PRInt32 mNumSelections; + PRInt32 mMaxNumSelections; + PRBool mMultipleSelections; + + + //nsIContent * mSelectedContent; + PRInt32 mSelectedIndex; + PRInt32 mStartExtendedIndex; + PRInt32 mEndExtendedIndex; + + nsIFrame * mHitFrame; + nsIContent * mHitContent; + + nsIFrame * mCurrentHitFrame; + nsIContent * mCurrentHitContent; + + nsIFrame * mSelectedFrame; + nsIContent * mSelectedContent; + + PRBool mIsInitializedFromContent; + + nsIFrame * mContentFrame; + PRBool mInDropDownMode; + nsIComboboxControlFrame * mComboboxFrame; + nsString mSelectionStr; + +}; + +#endif /* nsListControlFrame_h___ */ +