diff --git a/mozilla/layout/forms/nsComboboxControlFrame.cpp b/mozilla/layout/forms/nsComboboxControlFrame.cpp index 595d1c43ac5..1a8f5d8e416 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.cpp +++ b/mozilla/layout/forms/nsComboboxControlFrame.cpp @@ -128,6 +128,28 @@ nsComboboxControlFrame::IsSuccessful(nsIFormControlFrame* aSubmitter) return (NS_CONTENT_ATTR_HAS_VALUE == GetName(&name)); } + +// Initialize the text string in the combobox using either the current +// selection in the list box or the first item item in the list box. + +void nsComboboxControlFrame::InitTextStr() +{ + // Update the selected text string + mListControlFrame->GetSelectedItem(mTextStr); + + if (mTextStr == "") { + // No selection so use the first item in the list box + nsIFormControlFrame* fcFrame = nsnull; + nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame); + if ((NS_OK == result) && (nsnull != fcFrame)) { + // Set listbox selection to first item in the list box + fcFrame->SetProperty(nsHTMLAtoms::selectedindex, "0"); + // Get the listbox selection as a string + mListControlFrame->GetSelectedItem(mTextStr); + } + } +} + //-------------------------------------------------------------- void @@ -139,6 +161,8 @@ nsComboboxControlFrame::Reset() if ((NS_OK == result) && (nsnull != fcFrame)) { fcFrame->Reset(); } + + InitTextStr(); } void @@ -279,13 +303,19 @@ NS_IMETHODIMP nsComboboxControlFrame::Reflow(nsIPresContext& aPresConte // XXX Combo box should be changed to be implemented as a label and button frame. // This would eliminate all of this Reflow code. KMM + // add ourself as an nsIFormControlFrame + if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { + nsFormFrame::AddFormControlFrame(aPresContext, *this); + } + if (mFirstTime) { ReResolveStyleContext(&aPresContext, mStyleContext, NS_STYLE_HINT_REFLOW, nsnull, nsnull); // XXX This temporary mListFrame->ReResolveStyleContext(&aPresContext, mCurrentStyleContext, NS_STYLE_HINT_REFLOW, nsnull, nsnull); - nsFormFrame::AddFormControlFrame(aPresContext, *this); mFirstTime = PR_FALSE; + InitTextStr(); } + PRInt32 numChildren = mFrames.GetLength(); if (1 == numChildren) { diff --git a/mozilla/layout/forms/nsComboboxControlFrame.h b/mozilla/layout/forms/nsComboboxControlFrame.h index f7f7b1e53cc..ff243d029dc 100644 --- a/mozilla/layout/forms/nsComboboxControlFrame.h +++ b/mozilla/layout/forms/nsComboboxControlFrame.h @@ -138,6 +138,7 @@ protected: const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer); virtual PRIntn GetSkipSides() const; + void InitTextStr(); nsFormFrame* mFormFrame; // Parent Form Frame nsIFrame * mPlaceHolderFrame; diff --git a/mozilla/layout/forms/nsFileControlFrame.cpp b/mozilla/layout/forms/nsFileControlFrame.cpp index 5cab8483af1..11b1e50a95d 100644 --- a/mozilla/layout/forms/nsFileControlFrame.cpp +++ b/mozilla/layout/forms/nsFileControlFrame.cpp @@ -187,6 +187,11 @@ NS_IMETHODIMP nsFileControlFrame::Reflow(nsIPresContext& aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { + // add ourself as an nsIFormControlFrame + if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { + nsFormFrame::AddFormControlFrame(aPresContext, *this); + } + PRInt32 numChildren = mFrames.GetLength(); nsIFrame* childFrame; diff --git a/mozilla/layout/forms/nsListControlFrame.cpp b/mozilla/layout/forms/nsListControlFrame.cpp index 6c6c22fd0e9..63f6157857f 100644 --- a/mozilla/layout/forms/nsListControlFrame.cpp +++ b/mozilla/layout/forms/nsListControlFrame.cpp @@ -61,11 +61,18 @@ #include "nsIViewManager.h" #include "nsFormFrame.h" #include "nsIScrollableView.h" +#include "nsVoidArray.h" +#include "nsIDOMHTMLOptGroupElement.h" + + // Constants const char * kNormal = ""; const char * kSelected = "SELECTED"; const char * kSelectedFocus = "SELECTEDFOCUS"; +const nscoord kMaxDropDownRows = 3; +const PRInt32 kNothingSelected = -1; + //XXX: This is temporary. It simulates psuedo states by using a attribute selector on // -moz-option-selected in the ua.css style sheet. This will not be needed when //The event state manager is functional. KMM @@ -79,6 +86,8 @@ 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); +static NS_DEFINE_IID(kIFrameIID, NS_IFRAME_IID); +static NS_DEFINE_IID(kIDOMHTMLOptGroupElementIID, NS_IDOMHTMLOPTGROUPELEMENT_IID); //---------------------------------------------------------------------- @@ -98,22 +107,24 @@ nsListControlFrame::nsListControlFrame() { mHitFrame = nsnull; mCurrentHitFrame = nsnull; - mSelectedFrame = nsnull; mSelectedContent = nsnull; - mSelectedIndex = -1; + mSelectedIndex = kNothingSelected; mNumRows = 0; mIsInitializedFromContent = PR_FALSE; mNumSelections = 0; mInDropDownMode = PR_FALSE; mComboboxFrame = nsnull; mFormFrame = nsnull; + mSelectableFrames = new nsVoidArray; } //---------------------------------------------------------------------- nsListControlFrame::~nsListControlFrame() { - NS_IF_RELEASE(mComboboxFrame); + mComboboxFrame = nsnull; mFormFrame = nsnull; + delete mSelectableFrames; + mSelectableFrames = nsnull; } //---------------------------------------------------------------------- @@ -140,6 +151,48 @@ nsListControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsScrollFrame::QueryInterface(aIID, aInstancePtr); } +PRBool nsListControlFrame::IsOptionGroup(nsIFrame* aFrame) +{ + PRBool result = PR_FALSE; + nsIContent*content = nsnull; + aFrame->GetContent(&content); + nsIDOMHTMLOptGroupElement* og = nsnull; + if (content && (NS_OK == content->QueryInterface(kIDOMHTMLOptGroupElementIID, (void**) &og))) { + if (og != nsnull) { + result = PR_TRUE; + NS_RELEASE(og); + } + NS_RELEASE(content); + } + + return result; +} + +void nsListControlFrame::ConstructSelectableList(nsIFrame* aFrame, nsVoidArray* aList) +{ + nsIFrame* kid = nsnull; + aFrame->FirstChild(nsnull, &kid); + while (nsnull != kid) { + if (IsOptionGroup(kid)) { + ConstructSelectableList(kid, aList); + } else { + aList->AppendElement(kid); + kid->GetNextSibling(&kid); + } + } +} + +nsIFrame* nsListControlFrame::GetFirstSelectableFrame(nsVoidArray *aList, PRInt32& aPos) +{ + aPos = 1; + return((nsIFrame*)aList->ElementAt(0)); +} + +nsIFrame* nsListControlFrame::GetNextSelectableFrame(nsVoidArray *aList, PRInt32& aPos) +{ + return((nsIFrame*)aList->ElementAt(aPos++)); +} + //---------------------------------------------------------------------- NS_IMETHODIMP @@ -177,7 +230,6 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, nsRect kidRect; nsPoint tmp; nsRect offset(0, 0, 0, 0); - nsPoint absPoint = aPoint; *aFrame = nsnull; nsIFrame* firstKid = nsnull; @@ -204,7 +256,8 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, // As a hack we adjust for missing view offset by adding code to the nsListControlFrame::GetFrameForPoint which first // gets the ScrollableView associated with the nsListControlFrame and asks for it's GetScrollPosition offsets. These are // added to the point that is passed in so the correct frame is returned from nsListControlFrame::GetFrameForPoint. - + + tmp.MoveTo(aPoint.x, aPoint.y); nsIView *view = nsnull; GetView(&view); if (nsnull != view) { @@ -213,17 +266,57 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, nsIScrollableView* scrollableView; if (NS_SUCCEEDED(view->QueryInterface(kIScrollableViewIID, (void **)&scrollableView))) { scrollableView->GetScrollPosition(offsetx, offsety); - absPoint.x += offsetx; - absPoint.y += offsety; + tmp.x += offsetx; + tmp.y += offsety; // Note: scrollableView is not ref counted so it's interface is not NS_RELEASE'd } } - + +#ifdef NS_OPTGROUPSUPPORT + nsIFrame* selectedFrame = nsnull; + nsresult rv = nsScrollFrame::GetFrameForPoint(tmp, &selectedFrame); + + + // Walk up the frames looking for a selectable frame. + // A selectable frame is defined as a frame sitting under the ScrollArea's + // Area Frame or the first optiongroup ancestor. + + if (nsnull != selectedFrame) { + PRBool done = PR_FALSE; + while (! done) { + nsIFrame* parent = nsnull; + selectedFrame->GetParent(&parent); + if (parent != nsnull) { + if ((parent == mContentFrame) || (IsOptionGroup(parent))) { + done = PR_TRUE; + } else { + // Continue looking at ancestors + selectedFrame = parent; + } + } else { + // Parent is null so stop looking + done = PR_TRUE; + } + } + } + + *aFrame = selectedFrame; + + return rv; + +#else + +//XXX: Remove the following code when the code above works + + // Remove the top border from the offset. + nsMargin border; + tmp.y -= mBorderOffsetY; + mContentFrame->FirstChild(aList, &kid); while (nsnull != kid) { kid->GetRect(kidRect); - if (kidRect.Contains(absPoint)) { - tmp.MoveTo(absPoint.x - kidRect.x, absPoint.y - kidRect.y); + if (kidRect.Contains(tmp)) { + tmp.MoveTo(tmp.x - kidRect.x, tmp.y - kidRect.y); nsIContent * content; kid->GetContent(&content); @@ -246,7 +339,7 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, kid->GetFrameState(&state); if (NS_FRAME_OUTSIDE_CHILDREN & state) { kid->GetRect(kidRect); - tmp.MoveTo(absPoint.x - kidRect.x, absPoint.y - kidRect.y); + tmp.MoveTo(tmp.x - kidRect.x, tmp.y - kidRect.y); if (NS_OK == kid->GetFrameForPoint(tmp, aFrame)) { return NS_OK; } @@ -255,6 +348,7 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, } *aFrame = this; return NS_ERROR_FAILURE; +#endif } @@ -290,40 +384,38 @@ nsListControlFrame::Reflow(nsIPresContext& aPresContext, InitializeFromContent(PR_TRUE); } - nsIFrame* childFrame = nsnull; - mContentFrame->FirstChild(nsnull, &childFrame); - PRInt32 numChildren = LengthOf(childFrame); - - PRBool needsVerticalScrollbar = PR_FALSE; - if (PR_FALSE == mInDropDownMode) { - PRBool needsVerticalScrollbar = (numChildren > mNumRows); - } //--Calculate a width just big enough for the scrollframe to shrink around the //longest element in the list - //XXX: This is usually dangerous to do this type of coercion. Should it set the - //State back to the original when done??? - nsHTMLReflowState& tempReflowState = (nsHTMLReflowState&)aReflowState; - - // Get the size of option elements inside the listbox - nsSize desiredLineSize; - nsHTMLReflowMetrics desiredSize = aDesiredSize; - // Save original values which will be modified for the reflow state. - // They are set back to their original values at the end of reflow. - nscoord saveComputedWidth = tempReflowState.computedWidth; - nscoord saveAvailableWidth = tempReflowState.availableWidth; - + nsHTMLReflowState secondPassState(aReflowState); + nsHTMLReflowState firstPassState(aReflowState); + + // Get the size of option elements inside the listbox // Compute the width based on the longest line in the listbox. - // XXX: This should be changed to do the inherited reflow unconstrained for - // the width to get the real desired width. GetDesiredSize is limited to - // text strings. - //XXX: TODO: Use the width and height of an the first reflow to determine how high and wide - //to make the listbox, instead of calculating it. The second reflow can be done actually position - //the frames correctly using: tempReflowState.reason = eReflowReason_Resize; - GetDesiredSize(&aPresContext, aReflowState, desiredSize, desiredLineSize); + firstPassState.computedWidth = NS_UNCONSTRAINEDSIZE; + firstPassState.computedHeight = NS_UNCONSTRAINEDSIZE; + firstPassState.availableWidth = NS_UNCONSTRAINEDSIZE; + firstPassState.availableHeight = NS_UNCONSTRAINEDSIZE; - // Retrieve the scrollbar's width and height + + nsSize scrolledAreaSize(0,0); + nsHTMLReflowMetrics scrolledAreaDesiredSize(&scrolledAreaSize); + nsScrollFrame::Reflow(aPresContext, + scrolledAreaDesiredSize, + firstPassState, + aStatus); + + // Compute the bounding box of the contents of the list using the area + // calculated by the first reflow as a starting point. + + nscoord scrolledAreaWidth = scrolledAreaDesiredSize.maxElementSize->width; + nscoord scrolledAreaHeight = scrolledAreaDesiredSize.height; + + // The first reflow produces a box with the scrollbar width and borders + // added in so we need to subtract them out. + + // Retrieve the scrollbar's width and height float sbWidth = 0.0; float sbHeight = 0.0;; nsCOMPtr dc; @@ -333,88 +425,116 @@ nsListControlFrame::Reflow(nsIPresContext& aPresContext, nscoord scrollbarWidth = NSToCoordRound(sbWidth); nscoord scrollbarHeight = NSToCoordRound(sbHeight); - if (mInDropDownMode) { - // For drop-down lists, always size the width of the list based on - // the desired lineSize. - tempReflowState.computedWidth = desiredLineSize.width + scrollbarWidth; - } else if (NS_UNCONSTRAINEDSIZE == tempReflowState.computedWidth) { - // Only set to the desired width if it hasn't been explicitly defined through CSS. - // GetDesiredSize must still be done above to calculate the desired height. - tempReflowState.computedWidth = desiredLineSize.width; + // Subtract out the scrollbar width + scrolledAreaWidth -= scrollbarWidth; + + // Subtract out the borders + nsMargin border; + if (!aReflowState.mStyleSpacing->GetBorder(border)) { + border.SizeTo(0, 0, 0, 0); } + //XXX: Should just get the border.top when needed instead of + //caching it. + mBorderOffsetY = border.top; - // Add vertical scrollbar, Always add it in, - // even though the vertical scrollbar is not needed all the time - // the inherited Reflow: allways leave space for the scrollbar by - // shrinking the scroll area by the scrollbar width + scrolledAreaWidth -= (border.left + border.right); + scrolledAreaHeight -= (border.top + border.bottom); - tempReflowState.computedWidth += scrollbarWidth; - - // Now that we have calculated the width required to just shrink around the longest line. - // Go ahead and reflow - // Do the inherited Reflow. This reflow uses the computed widths which - // are setup above. - // Don't set the height. Let the height be unconstrained + // Now the scrolledAreaWidth and scrolledAreaHeight are exactly + // wide and high enough to enclose their contents + nscoord visibleWidth = 0; if (mInDropDownMode) { - // The dropdown list height is the smaller of it's height setting or the height - // of the smallest box that can drawn around it's contents. - if (tempReflowState.computedHeight > (desiredSize.height)) { - tempReflowState.computedHeight = desiredSize.height ; + // Always set the width to be + // XXX: TODO The width of the list should be passed in so it can + // be set to the same as the text box + dropdown arrow. + visibleWidth = scrolledAreaWidth; + if (nsnull != mComboboxFrame) { + nsRect comboRect; + nsIFrame *comboFrame = nsnull; + if (NS_SUCCEEDED(mComboboxFrame->QueryInterface(kIFrameIID, (void **)&comboFrame))) { + comboFrame->GetRect(comboRect); + visibleWidth = comboRect.width; + visibleWidth -= (border.left + border.right); + } + } + + } else { + if (NS_UNCONSTRAINEDSIZE == aReflowState.computedWidth) { + visibleWidth = scrolledAreaWidth; + } else { + visibleWidth = aReflowState.computedWidth; } } - + + // Determine if a scrollbar will be needed, If so we need to add + // enough the width to allow for the scrollbar. + // The scrollbar will be needed under two conditions: + // (size * heightOfaRow) < scrolledAreaHeight or + // the height set through Style < scrolledAreaHeight. + + // Calculate the height of a row. + // XXX: Fix this for optgroup's. + nsIFrame* childFrame = nsnull; + mContentFrame->FirstChild(nsnull, &childFrame); + PRInt32 numChildren = LengthOf(childFrame); + PRInt32 heightOfARow = scrolledAreaHeight / numChildren; + + nscoord visibleHeight = 0; + if (mInDropDownMode) { + // Compute the visible height of the drop-down list + // The dropdown list height is the smaller of it's height setting or the height + // of the smallest box that can drawn around it's contents. + visibleHeight = scrolledAreaHeight; + + if (visibleHeight > (kMaxDropDownRows * heightOfARow)) + visibleHeight = (kMaxDropDownRows * heightOfARow); + + } else { + // Calculate the visible height of the listbox + if (NS_UNCONSTRAINEDSIZE != aReflowState.computedHeight) { + visibleHeight = aReflowState.computedHeight; + } else { + if (numChildren == mNumRows) { + // Make sure scrollbars go away when the number of rows + // matchs the number of items to scroll. + visibleHeight = aReflowState.computedHeight; + } else { + visibleHeight = mNumRows * heightOfARow; + } + } + } + + PRBool needsVerticalScrollbar = PR_FALSE; + if (visibleHeight < scrolledAreaHeight) { + needsVerticalScrollbar = PR_TRUE; + } + + if ((needsVerticalScrollbar) && (! mInDropDownMode)) { + visibleWidth += scrollbarWidth; + } + + // Do a second reflow with the adjusted width and height settings + // This sets up all of the frames with the correct width and height. + secondPassState.computedWidth = visibleWidth; + secondPassState.computedHeight = visibleHeight; + secondPassState.reason = eReflowReason_Resize; nsScrollFrame::Reflow(aPresContext, aDesiredSize, - tempReflowState, + secondPassState, aStatus); - // Now that we have reflowed, we may need to adjust the width and height returned - // for the listbox. - - if (PR_FALSE == mInDropDownMode) { - // Set the desired height to the computed height above only if it has not - // been explicitly set by CSS. - if (NS_UNCONSTRAINEDSIZE == tempReflowState.computedHeight) { - aDesiredSize.height = desiredSize.height; - } - } - - //Need to addback the scrollbar because the inherited reflow - //subtracts the scrollbar width when computing the desired size. - //We don't subtract when there isn't a vertical scrollbar. This may - //seem odd, becuase above we always add in the scrollbar width before calling - //reflow. The reason is that inherited reflow always leaves blank space for - //the scrollbar, but we don't whant that behavior for the listbox'es scrollbars. - if (needsVerticalScrollbar) { - aDesiredSize.width += scrollbarWidth; - } // Set the max element size to be the same as the desired element size. if (nsnull != aDesiredSize.maxElementSize) { aDesiredSize.maxElementSize->width = aDesiredSize.width; aDesiredSize.maxElementSize->height = aDesiredSize.height; } - // XXX: Set the min element size here to the desired element size here?? - // Restore to original reflow state. - tempReflowState.computedWidth = saveComputedWidth; - tempReflowState.availableWidth = saveAvailableWidth; - 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 * @@ -444,6 +564,7 @@ nsListControlFrame::GetOptionFromChild(nsIFrame* aParentFrame) return nsnull; } + //-------------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::GetFormContent(nsIContent*& aContent) const @@ -466,6 +587,17 @@ nsListControlFrame::GetFont(nsIPresContext* aPresContext, } +void nsListControlFrame::ForceRedraw(nsIContent* aContent) +{ + //XXX: Hack. This should not be needed. The problem is DisplaySelected + //and DisplayDeselected set and unset an attribute on generic HTML content + //which does not know that it should repaint as the result of the attribute + //being set. This should not be needed once the event state manager handles + //selection. + nsFormControlHelper::ForceDrawFrame(this); +} + + // XXX: Here we introduce a new -moz-option-selected attribute so a attribute // selecitor n the ua.css can change the style when the option is selected. @@ -474,8 +606,11 @@ void nsListControlFrame::DisplaySelected(nsIContent* aContent) //XXX: This is temporary. It simulates psuedo states by using a attribute selector on // -moz-option-selected in the ua.css style sheet. This will not be needed when // The event state manager is functional. KMM + nsCOMPtr selectedAtom ( dont_QueryInterface(NS_NewAtom(kMozSelected))); aContent->SetAttribute(kNameSpaceID_None, selectedAtom, "", PR_TRUE); + ForceRedraw(aContent); + } void nsListControlFrame::DisplayDeselected(nsIContent* aContent) @@ -483,8 +618,11 @@ void nsListControlFrame::DisplayDeselected(nsIContent* aContent) //XXX: This is temporary. It simulates psuedo states by using a attribute selector on // -moz-option-selected in the ua.css style sheet. This will not be needed when // The event state manager is functional. KMM + nsCOMPtr selectedAtom ( dont_QueryInterface(NS_NewAtom(kMozSelected))); aContent->UnsetAttribute(kNameSpaceID_None, selectedAtom, PR_TRUE); + ForceRedraw(aContent); + } void nsListControlFrame::UpdateItem(nsIContent* aContent, PRBool aSelected) @@ -517,7 +655,7 @@ PRInt32 nsListControlFrame::SetContentSelected(nsIFrame * aHitFrame, kid->GetNextSibling(&kid); index++; } - return -1; + return kNothingSelected; } //---------------------------------------------------------------------- @@ -584,82 +722,104 @@ void nsListControlFrame::ExtendedSelection(PRInt32 aStartIndex, PRInt32 aEndInde } } +void nsListControlFrame::SingleSelection() +{ + // Store previous selection + PRInt32 oldSelectedIndex = mSelectedIndex; + // Get Current selection + mSelectedIndex = (PRInt32)SetContentSelected(mHitFrame, mHitContent, PR_FALSE); + if (mSelectedIndex != kNothingSelected) { + if (oldSelectedIndex != mSelectedIndex) { + // Deselect the previous selection if there is one + if (oldSelectedIndex != kNothingSelected) { + SetFrameSelected(oldSelectedIndex, PR_FALSE); + } + // Dipslay the new selection + SetFrameSelected(mSelectedIndex, PR_TRUE); + mSelectedContent = mHitContent; + } else { + // Selecting the currently selected item so do nothing. + } + } +} + +void nsListControlFrame::MultipleSelection(PRBool aIsShift, PRBool aIsControl) +{ + PRInt32 oldSelectedIndex = mSelectedIndex; + + mSelectedIndex = (PRInt32)SetContentSelected(mHitFrame, mHitContent, PR_FALSE); + if (kNothingSelected != mSelectedIndex) { + PRBool wasSelected = IsFrameSelected(mSelectedIndex); + SetFrameSelected(mSelectedIndex, PR_TRUE); + PRBool selected = wasSelected; + + if (aIsShift) { + selected = PR_TRUE; + if (mEndExtendedIndex == kNothingSelected) { + 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 (aIsControl) { + selected = !selected; + if (nsnull != mSelectedContent) { + if (mHitContent != mSelectedContent) { + // Toggle the selection + UpdateItem(mHitContent, !wasSelected); + } else { +//XXX: Fix this mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); + } + } + } else { + mStartExtendedIndex = mSelectedIndex; + mEndExtendedIndex = kNothingSelected; + ClearSelection(); + selected = PR_TRUE; + } + + //XXX Fix this mHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); + mSelectedContent = mHitContent; + } + +} + //---------------------------------------------------------------------- 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_FALSE); - if (-1 < mSelectedIndex) { - PRBool wasSelected = IsFrameSelected(mSelectedIndex); - SetFrameSelected(mSelectedIndex, PR_TRUE); - PRBool selected = wasSelected; - 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) { - // Toggle the selection - UpdateItem(mHitContent, !wasSelected); - } else { - //XXX: Fix this mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); - } - } - } else { - mStartExtendedIndex = mSelectedIndex; - mEndExtendedIndex = -1; - ClearSelection(); - selected = PR_TRUE; - } - } else { - if (nsnull != mSelectedContent) { - DisplayDeselected(mSelectedContent); - NS_RELEASE(mSelectedContent); - } - } - //XXX: Remove SetFrameSelected(mSelectedIndex, selected); - //XXX mHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); - mSelectedContent = mHitContent; - mSelectedFrame = mHitFrame; - aEventStatus = nsEventStatus_eConsumeNoDefault; + if (mMultipleSelections) { + MultipleSelection(((nsMouseEvent *)aEvent)->isShift, ((nsMouseEvent *)aEvent)->isControl); + } else { + SingleSelection(); } - } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { - aEventStatus = nsEventStatus_eConsumeNoDefault; } + + aEventStatus = nsEventStatus_eConsumeNoDefault; return NS_OK; } @@ -697,7 +857,7 @@ NS_IMETHODIMP nsListControlFrame::HandleLikeDropDownListEvent(nsIPresContext& aP // 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) + // (Selection doesn't actually occur until the mouse is released, or clicked and released) if (aEvent->message == NS_MOUSE_MOVE) { // If the DropDown is currently has a selected item @@ -735,7 +895,7 @@ NS_IMETHODIMP nsListControlFrame::HandleLikeDropDownListEvent(nsIPresContext& aP // Start by finding the newly "hit" content from the hit frame PRInt32 index = SetContentSelected(mHitFrame, mHitContent, PR_TRUE); - if (-1 < index) { + if (kNothingSelected != index) { nsIDOMHTMLOptionElement* option = nsnull; nsresult rv = mHitContent->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); @@ -868,6 +1028,8 @@ nsListControlFrame::Init(nsIPresContext& aPresContext, return result; } +// XXX: This following methods are not referenced. They should be removed if they +// are not needed //---------------------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::GetMaxLength(PRInt32* aSize) @@ -906,19 +1068,7 @@ nsListControlFrame::GetSizeFromContent(PRInt32* aSize) const return result; } -//---------------------------------------------------------------------- -nscoord -nsListControlFrame::GetVerticalBorderWidth(float aPixToTwip) const -{ - return NSIntPixelsToTwips(3, aPixToTwip); -} - -//---------------------------------------------------------------------- -nscoord -nsListControlFrame::GetHorizontalBorderWidth(float aPixToTwip) const -{ - return GetVerticalBorderWidth(aPixToTwip); -} +//XXX: End of the un-referenced methods. //---------------------------------------------------------------------- nscoord @@ -939,101 +1089,6 @@ nsListControlFrame::GetHorizontalInsidePadding(nsIPresContext& aPresContext, } -//---------------------------------------------------------------------- - -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) { - 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 desiredSize; - nsSize minSize; - 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, aReflowState.rendContext, this, styleSize, - textSpec, desiredSize, minSize, widthExplicit, - heightExplicit, rowHeight); - - float sp2t; - float p2t; - aPresContext->GetPixelsToTwips(&p2t); - aPresContext->GetScaledPixelsToTwips(&sp2t); - - if (mInDropDownMode) { - nscoord extra = desiredSize.height - (rowHeight * mNumRows); - - mNumRows = (numOptions > 20 ? 20 : numOptions); - desiredSize.height = (mNumRows * rowHeight) + extra; - } - - aDesiredLayoutSize.width = desiredSize.width; - - // 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 = desiredSize.height; - aDesiredLayoutSize.ascent = aDesiredLayoutSize.height; - aDesiredLayoutSize.descent = 0; - - // XXX When this code is working, max element size needs to be set properly from CalculateSize - // above. look at some of the other form control to see how to do it. - - aDesiredWidgetSize.width = maxWidth; - aDesiredWidgetSize.height = rowHeight; - - if (aDesiredLayoutSize.maxElementSize) { - aDesiredLayoutSize.maxElementSize->width = minSize.width; - aDesiredLayoutSize.maxElementSize->height = minSize.height; - } - - NS_RELEASE(select); - NS_RELEASE(options); - -} - - //---------------------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::GetMultiple(PRBool* aMultiple, nsIDOMHTMLSelectElement* aSelect) @@ -1076,7 +1131,6 @@ nsListControlFrame::AboutToDropDown() kid->GetContent(&content); if (i == mSelectedIndex) { mSelectedContent = content; - mSelectedFrame = kid; DisplaySelected(mSelectedContent); return NS_OK; } @@ -1158,6 +1212,10 @@ nsListControlFrame::InitializeFromContent(PRBool aDoDisplay) PRBool selected; option->GetDefaultSelected(&selected); SetFrameSelected(i, selected); + // Set the selected index for single selection list boxes. + if (selected) { + mSelectedIndex = i; + } if (mInDropDownMode) { if (selected) { @@ -1168,7 +1226,6 @@ nsListControlFrame::InitializeFromContent(PRBool aDoDisplay) mSelectedIndex = i; NS_RELEASE(option); mSelectedContent = content; - mSelectedFrame = kid; return; } } else { @@ -1434,8 +1491,8 @@ nsListControlFrame::SetFocus(PRBool aOn, PRBool aRepaint) // in this call? if (mSelectedContent) { //XXX: Need correct attribute here - mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (aOn?kSelectedFocus:kNormal), PR_TRUE); - } +// mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (aOn?kSelectedFocus:kNormal), PR_TRUE); + } } @@ -1479,8 +1536,92 @@ PRInt32 nsListControlFrame::GetNumberOfOptions() return(numOptions); } +// Set the selected attribute for an item in the listbox. If the listbox has +// been initialized the result of the setting the attribute is displayed otherwise +// it is not. -NS_IMETHODIMP nsListControlFrame::SetProperty(nsIAtom* aName, const nsString& aValue) +void +nsListControlFrame::SetContentSelectedAttribute(PRUint32 aIndex, PRBool aSelected) +{ + // Only display the result of the attribute changed if the listbox has been + // initialized. + if (mIsInitializedFromContent) { + // Update the attribute and display it + SetFrameSelected(aIndex, aSelected); + } else { + // Update the attribute without displaying it + nsIContent* content = GetOptionContent(aIndex); + NS_ASSERTION(nsnull != content, "Failed to retrieve option content"); + nsCOMPtr selectedAtom ( dont_QueryInterface(NS_NewAtom(kMozSelected))); + if (aSelected) { + content->SetAttribute(kNameSpaceID_None, selectedAtom, "", PR_FALSE); + } else { + content->UnsetAttribute(kNameSpaceID_None, selectedAtom, PR_FALSE); + } + NS_IF_RELEASE(content); + } +} + +// Set a single item in the listbox to selected. + +void +nsListControlFrame::SetSelectedIndex(PRInt32 aIndex, nsIContent *aContent) +{ + SetContentSelectedAttribute(aIndex, PR_TRUE); + mSelectedContent = aContent; + mSelectedIndex = aIndex; + + // Get the selected string from the content + nsIDOMHTMLOptionElement* option = nsnull; + nsresult rv = aContent->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); + if (NS_SUCCEEDED(rv)) { + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE == option->GetText(text)) { + mSelectionStr = text; + } + NS_RELEASE(option); + } +} + + +// Select the specified item in the listbox using control logic. +// If it a single selection listbox the previous selection will be +// de-selected. + +void +nsListControlFrame::ToggleSelected(PRInt32 aIndex, nsIContent *aContent) +{ + PRBool multiple; + GetMultiple(&multiple); + + if (PR_TRUE == multiple) { + SetSelectedIndex(aIndex, aContent); + } else { + if (mSelectedIndex != kNothingSelected) { + SetContentSelectedAttribute(aIndex, PR_FALSE); + } + SetSelectedIndex(aIndex, aContent); + } +} + +// Select the specified item in the listbox using + +void +nsListControlFrame::SelectIndex(PRInt32 aIndex) +{ + nsIDOMHTMLCollection* options = GetOptions(mContent); + nsIDOMHTMLOptionElement* optionElement = GetOption(*options, aIndex); + nsIContent *content = nsnull; + nsresult result = optionElement->QueryInterface(kIContentIID, (void**) &content); + ToggleSelected(aIndex, content); + NS_RELEASE(content); + NS_RELEASE(optionElement); + NS_RELEASE(options); +} + + +NS_IMETHODIMP +nsListControlFrame::SetProperty(nsIAtom* aName, const nsString& aValue) { if (nsHTMLAtoms::selected == aName) { return NS_ERROR_INVALID_ARG; // Selected is readonly according to spec. @@ -1490,7 +1631,8 @@ NS_IMETHODIMP nsListControlFrame::SetProperty(nsIAtom* aName, const nsString& aV if (error) { return NS_ERROR_INVALID_ARG; // Couldn't convert to integer } else { - SetFrameSelected(selectedIndex, PR_TRUE); + // Select the specified item in the list box. + SelectIndex(selectedIndex); } } else { //XXX: TODO What about all othe other properties that form elements can set here? @@ -1499,7 +1641,8 @@ NS_IMETHODIMP nsListControlFrame::SetProperty(nsIAtom* aName, const nsString& aV return NS_OK; } -NS_IMETHODIMP nsListControlFrame::GetProperty(nsIAtom* aName, nsString& aValue) +NS_IMETHODIMP +nsListControlFrame::GetProperty(nsIAtom* aName, nsString& aValue) { // Get the selected value of option from local cache (optimization vs. widget) if (nsHTMLAtoms::selected == aName) { @@ -1516,7 +1659,7 @@ NS_IMETHODIMP nsListControlFrame::GetProperty(nsIAtom* aName, nsString& aValue) // Spin through loooking for the first selection in the list PRInt32 index = 0; PRInt32 maxOptions = GetNumberOfOptions(); - PRInt32 selectedIndex = -1; + PRInt32 selectedIndex = kNothingSelected; for (index = 0; index < maxOptions; index++) { PRBool isSelected = PR_FALSE; diff --git a/mozilla/layout/forms/nsListControlFrame.h b/mozilla/layout/forms/nsListControlFrame.h index c9c90552897..4af053bc51e 100644 --- a/mozilla/layout/forms/nsListControlFrame.h +++ b/mozilla/layout/forms/nsListControlFrame.h @@ -32,6 +32,7 @@ class nsIDOMHTMLOptionElement; class nsFormFrame; class nsScrollFrame; class nsIComboboxControlFrame; +class nsVoidArray; /** @@ -83,26 +84,17 @@ public: NS_IMETHOD SetProperty(nsIAtom* aName, const nsString& aValue); NS_IMETHOD GetProperty(nsIAtom* aName, nsString& aValue); - virtual void GetDesiredSize(nsIPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsHTMLReflowMetrics& aDesiredLayoutSize, - nsSize& aDesiredWidgetSize); - /*virtual nsresult Focus(nsIDOMEvent* aEvent); - virtual nsresult Blur(nsIDOMEvent* aEvent); - virtual nsresult HandleEvent(nsIDOMEvent* aEvent); - NS_IMETHOD AddEventListener(nsIDOMNode * aNode); - NS_IMETHOD RemoveEventListener(nsIDOMNode * aNode); - - NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext);*/ - + virtual nsresult Blur(nsIDOMEvent* aEvent); */ + NS_METHOD GetMultiple(PRBool* aResult, nsIDOMHTMLSelectElement* aSelect = nsnull); +// XXX the following methods are not referenced. They should be removed when it is +// confirmed that they are not needed virtual nsresult GetSizeFromContent(PRInt32* aSize) const; NS_IMETHOD GetMaxLength(PRInt32* aSize); +// XXX: End of the unreferenced methods - 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, @@ -110,17 +102,18 @@ public: nscoord aInnerWidth, nscoord aCharWidth) const; + virtual nsresult RequiresWidget(PRBool &aRequiresWidget); + 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 @@ -183,6 +176,17 @@ protected: void DisplaySelected(nsIContent* aContent); void DisplayDeselected(nsIContent* aContent); void UpdateItem(nsIContent* aContent, PRBool aSelected); + void ForceRedraw(nsIContent* aContent); + PRBool IsOptionGroup(nsIFrame* aFrame); + void ConstructSelectableList(nsIFrame* aFrame, nsVoidArray *aList); + nsIFrame* GetFirstSelectableFrame(nsVoidArray *aList, PRInt32& aPos); + nsIFrame* GetNextSelectableFrame(nsVoidArray *aList, PRInt32& aPos); + void SingleSelection(); + void MultipleSelection(PRBool aIsShift, PRBool aIsControl); + void SelectIndex(PRInt32 aIndex); + void ToggleSelected(PRInt32 aIndex, nsIContent *aContent); + void SetSelectedIndex(PRInt32 aIndex, nsIContent *aContent); + void SetContentSelectedAttribute(PRUint32 aIndex, PRBool aSelected); // nsHTMLContainerFrame overrides @@ -202,10 +206,9 @@ protected: PRBool aDisplaySelected); // Data Members + nscoord mBorderOffsetY; nsFormFrame* mFormFrame; PRInt32 mNumRows; -//XXX: TODO: This should not be hardcoded to 64 -//ZZZ PRBool mIsFrameSelected[64]; PRInt32 mNumSelections; PRInt32 mMaxNumSelections; PRBool mMultipleSelections; @@ -216,13 +219,13 @@ protected: nsIContent * mHitContent; nsIFrame * mCurrentHitFrame; nsIContent * mCurrentHitContent; - nsIFrame * mSelectedFrame; nsIContent * mSelectedContent; PRBool mIsInitializedFromContent; nsIFrame * mContentFrame; PRBool mInDropDownMode; nsIComboboxControlFrame * mComboboxFrame; nsString mSelectionStr; + nsVoidArray* mSelectableFrames; }; diff --git a/mozilla/layout/html/document/src/ua.css b/mozilla/layout/html/document/src/ua.css index c2e7e2859f1..3b867eef998 100644 --- a/mozilla/layout/html/document/src/ua.css +++ b/mozilla/layout/html/document/src/ua.css @@ -686,6 +686,7 @@ select { border: 1px inset #c0c0c0; background-color: white; color:black; + white-space:nowrap; } option { display:block; @@ -707,6 +708,19 @@ option.selectedfocus { background-color: rgb(0,0,128); color: rgb(255,255,255); } + +optgroup { + font-style:italic; + font-weight:bold; +} + +optgroup > option { + position:relative; + left:20px; + font-style:normal; + font-weight:normal; +} + textarea { vertical-align: bottom; border: 2px inset #c0c0c0; @@ -915,8 +929,6 @@ sourcetext { :-moz-dropdown-list { position:absolute; - height:200px; - width:100px; background-color:white; border: 1px solid blue; } diff --git a/mozilla/layout/html/forms/src/nsButtonControlFrame.cpp b/mozilla/layout/html/forms/src/nsButtonControlFrame.cpp index 07f71c22730..160e1a18e0b 100644 --- a/mozilla/layout/html/forms/src/nsButtonControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsButtonControlFrame.cpp @@ -363,6 +363,11 @@ nsButtonControlFrame::Reflow(nsIPresContext& aPresContext, aPresContext.GetWidgetRenderingMode(&mode); if (eWidgetRendering_Gfx == mode || NS_FORM_INPUT_IMAGE == type) { + // add ourself as an nsIFormControlFrame + if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { + nsFormFrame::AddFormControlFrame(aPresContext, *this); + } + nsSize ignore; GetDesiredSize(&aPresContext, aReflowState, aDesiredSize, ignore); nsMargin bp(0,0,0,0); diff --git a/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp b/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp index 595d1c43ac5..1a8f5d8e416 100644 --- a/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsComboboxControlFrame.cpp @@ -128,6 +128,28 @@ nsComboboxControlFrame::IsSuccessful(nsIFormControlFrame* aSubmitter) return (NS_CONTENT_ATTR_HAS_VALUE == GetName(&name)); } + +// Initialize the text string in the combobox using either the current +// selection in the list box or the first item item in the list box. + +void nsComboboxControlFrame::InitTextStr() +{ + // Update the selected text string + mListControlFrame->GetSelectedItem(mTextStr); + + if (mTextStr == "") { + // No selection so use the first item in the list box + nsIFormControlFrame* fcFrame = nsnull; + nsresult result = mListFrame->QueryInterface(kIFormControlFrameIID, (void**)&fcFrame); + if ((NS_OK == result) && (nsnull != fcFrame)) { + // Set listbox selection to first item in the list box + fcFrame->SetProperty(nsHTMLAtoms::selectedindex, "0"); + // Get the listbox selection as a string + mListControlFrame->GetSelectedItem(mTextStr); + } + } +} + //-------------------------------------------------------------- void @@ -139,6 +161,8 @@ nsComboboxControlFrame::Reset() if ((NS_OK == result) && (nsnull != fcFrame)) { fcFrame->Reset(); } + + InitTextStr(); } void @@ -279,13 +303,19 @@ NS_IMETHODIMP nsComboboxControlFrame::Reflow(nsIPresContext& aPresConte // XXX Combo box should be changed to be implemented as a label and button frame. // This would eliminate all of this Reflow code. KMM + // add ourself as an nsIFormControlFrame + if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { + nsFormFrame::AddFormControlFrame(aPresContext, *this); + } + if (mFirstTime) { ReResolveStyleContext(&aPresContext, mStyleContext, NS_STYLE_HINT_REFLOW, nsnull, nsnull); // XXX This temporary mListFrame->ReResolveStyleContext(&aPresContext, mCurrentStyleContext, NS_STYLE_HINT_REFLOW, nsnull, nsnull); - nsFormFrame::AddFormControlFrame(aPresContext, *this); mFirstTime = PR_FALSE; + InitTextStr(); } + PRInt32 numChildren = mFrames.GetLength(); if (1 == numChildren) { diff --git a/mozilla/layout/html/forms/src/nsComboboxControlFrame.h b/mozilla/layout/html/forms/src/nsComboboxControlFrame.h index f7f7b1e53cc..ff243d029dc 100644 --- a/mozilla/layout/html/forms/src/nsComboboxControlFrame.h +++ b/mozilla/layout/html/forms/src/nsComboboxControlFrame.h @@ -138,6 +138,7 @@ protected: const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer); virtual PRIntn GetSkipSides() const; + void InitTextStr(); nsFormFrame* mFormFrame; // Parent Form Frame nsIFrame * mPlaceHolderFrame; diff --git a/mozilla/layout/html/forms/src/nsFileControlFrame.cpp b/mozilla/layout/html/forms/src/nsFileControlFrame.cpp index 5cab8483af1..11b1e50a95d 100644 --- a/mozilla/layout/html/forms/src/nsFileControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsFileControlFrame.cpp @@ -187,6 +187,11 @@ NS_IMETHODIMP nsFileControlFrame::Reflow(nsIPresContext& aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { + // add ourself as an nsIFormControlFrame + if (!mFormFrame && (eReflowReason_Initial == aReflowState.reason)) { + nsFormFrame::AddFormControlFrame(aPresContext, *this); + } + PRInt32 numChildren = mFrames.GetLength(); nsIFrame* childFrame; diff --git a/mozilla/layout/html/forms/src/nsListControlFrame.cpp b/mozilla/layout/html/forms/src/nsListControlFrame.cpp index 6c6c22fd0e9..63f6157857f 100644 --- a/mozilla/layout/html/forms/src/nsListControlFrame.cpp +++ b/mozilla/layout/html/forms/src/nsListControlFrame.cpp @@ -61,11 +61,18 @@ #include "nsIViewManager.h" #include "nsFormFrame.h" #include "nsIScrollableView.h" +#include "nsVoidArray.h" +#include "nsIDOMHTMLOptGroupElement.h" + + // Constants const char * kNormal = ""; const char * kSelected = "SELECTED"; const char * kSelectedFocus = "SELECTEDFOCUS"; +const nscoord kMaxDropDownRows = 3; +const PRInt32 kNothingSelected = -1; + //XXX: This is temporary. It simulates psuedo states by using a attribute selector on // -moz-option-selected in the ua.css style sheet. This will not be needed when //The event state manager is functional. KMM @@ -79,6 +86,8 @@ 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); +static NS_DEFINE_IID(kIFrameIID, NS_IFRAME_IID); +static NS_DEFINE_IID(kIDOMHTMLOptGroupElementIID, NS_IDOMHTMLOPTGROUPELEMENT_IID); //---------------------------------------------------------------------- @@ -98,22 +107,24 @@ nsListControlFrame::nsListControlFrame() { mHitFrame = nsnull; mCurrentHitFrame = nsnull; - mSelectedFrame = nsnull; mSelectedContent = nsnull; - mSelectedIndex = -1; + mSelectedIndex = kNothingSelected; mNumRows = 0; mIsInitializedFromContent = PR_FALSE; mNumSelections = 0; mInDropDownMode = PR_FALSE; mComboboxFrame = nsnull; mFormFrame = nsnull; + mSelectableFrames = new nsVoidArray; } //---------------------------------------------------------------------- nsListControlFrame::~nsListControlFrame() { - NS_IF_RELEASE(mComboboxFrame); + mComboboxFrame = nsnull; mFormFrame = nsnull; + delete mSelectableFrames; + mSelectableFrames = nsnull; } //---------------------------------------------------------------------- @@ -140,6 +151,48 @@ nsListControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr) return nsScrollFrame::QueryInterface(aIID, aInstancePtr); } +PRBool nsListControlFrame::IsOptionGroup(nsIFrame* aFrame) +{ + PRBool result = PR_FALSE; + nsIContent*content = nsnull; + aFrame->GetContent(&content); + nsIDOMHTMLOptGroupElement* og = nsnull; + if (content && (NS_OK == content->QueryInterface(kIDOMHTMLOptGroupElementIID, (void**) &og))) { + if (og != nsnull) { + result = PR_TRUE; + NS_RELEASE(og); + } + NS_RELEASE(content); + } + + return result; +} + +void nsListControlFrame::ConstructSelectableList(nsIFrame* aFrame, nsVoidArray* aList) +{ + nsIFrame* kid = nsnull; + aFrame->FirstChild(nsnull, &kid); + while (nsnull != kid) { + if (IsOptionGroup(kid)) { + ConstructSelectableList(kid, aList); + } else { + aList->AppendElement(kid); + kid->GetNextSibling(&kid); + } + } +} + +nsIFrame* nsListControlFrame::GetFirstSelectableFrame(nsVoidArray *aList, PRInt32& aPos) +{ + aPos = 1; + return((nsIFrame*)aList->ElementAt(0)); +} + +nsIFrame* nsListControlFrame::GetNextSelectableFrame(nsVoidArray *aList, PRInt32& aPos) +{ + return((nsIFrame*)aList->ElementAt(aPos++)); +} + //---------------------------------------------------------------------- NS_IMETHODIMP @@ -177,7 +230,6 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, nsRect kidRect; nsPoint tmp; nsRect offset(0, 0, 0, 0); - nsPoint absPoint = aPoint; *aFrame = nsnull; nsIFrame* firstKid = nsnull; @@ -204,7 +256,8 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, // As a hack we adjust for missing view offset by adding code to the nsListControlFrame::GetFrameForPoint which first // gets the ScrollableView associated with the nsListControlFrame and asks for it's GetScrollPosition offsets. These are // added to the point that is passed in so the correct frame is returned from nsListControlFrame::GetFrameForPoint. - + + tmp.MoveTo(aPoint.x, aPoint.y); nsIView *view = nsnull; GetView(&view); if (nsnull != view) { @@ -213,17 +266,57 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, nsIScrollableView* scrollableView; if (NS_SUCCEEDED(view->QueryInterface(kIScrollableViewIID, (void **)&scrollableView))) { scrollableView->GetScrollPosition(offsetx, offsety); - absPoint.x += offsetx; - absPoint.y += offsety; + tmp.x += offsetx; + tmp.y += offsety; // Note: scrollableView is not ref counted so it's interface is not NS_RELEASE'd } } - + +#ifdef NS_OPTGROUPSUPPORT + nsIFrame* selectedFrame = nsnull; + nsresult rv = nsScrollFrame::GetFrameForPoint(tmp, &selectedFrame); + + + // Walk up the frames looking for a selectable frame. + // A selectable frame is defined as a frame sitting under the ScrollArea's + // Area Frame or the first optiongroup ancestor. + + if (nsnull != selectedFrame) { + PRBool done = PR_FALSE; + while (! done) { + nsIFrame* parent = nsnull; + selectedFrame->GetParent(&parent); + if (parent != nsnull) { + if ((parent == mContentFrame) || (IsOptionGroup(parent))) { + done = PR_TRUE; + } else { + // Continue looking at ancestors + selectedFrame = parent; + } + } else { + // Parent is null so stop looking + done = PR_TRUE; + } + } + } + + *aFrame = selectedFrame; + + return rv; + +#else + +//XXX: Remove the following code when the code above works + + // Remove the top border from the offset. + nsMargin border; + tmp.y -= mBorderOffsetY; + mContentFrame->FirstChild(aList, &kid); while (nsnull != kid) { kid->GetRect(kidRect); - if (kidRect.Contains(absPoint)) { - tmp.MoveTo(absPoint.x - kidRect.x, absPoint.y - kidRect.y); + if (kidRect.Contains(tmp)) { + tmp.MoveTo(tmp.x - kidRect.x, tmp.y - kidRect.y); nsIContent * content; kid->GetContent(&content); @@ -246,7 +339,7 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, kid->GetFrameState(&state); if (NS_FRAME_OUTSIDE_CHILDREN & state) { kid->GetRect(kidRect); - tmp.MoveTo(absPoint.x - kidRect.x, absPoint.y - kidRect.y); + tmp.MoveTo(tmp.x - kidRect.x, tmp.y - kidRect.y); if (NS_OK == kid->GetFrameForPoint(tmp, aFrame)) { return NS_OK; } @@ -255,6 +348,7 @@ nsListControlFrame::GetFrameForPointUsing(const nsPoint& aPoint, } *aFrame = this; return NS_ERROR_FAILURE; +#endif } @@ -290,40 +384,38 @@ nsListControlFrame::Reflow(nsIPresContext& aPresContext, InitializeFromContent(PR_TRUE); } - nsIFrame* childFrame = nsnull; - mContentFrame->FirstChild(nsnull, &childFrame); - PRInt32 numChildren = LengthOf(childFrame); - - PRBool needsVerticalScrollbar = PR_FALSE; - if (PR_FALSE == mInDropDownMode) { - PRBool needsVerticalScrollbar = (numChildren > mNumRows); - } //--Calculate a width just big enough for the scrollframe to shrink around the //longest element in the list - //XXX: This is usually dangerous to do this type of coercion. Should it set the - //State back to the original when done??? - nsHTMLReflowState& tempReflowState = (nsHTMLReflowState&)aReflowState; - - // Get the size of option elements inside the listbox - nsSize desiredLineSize; - nsHTMLReflowMetrics desiredSize = aDesiredSize; - // Save original values which will be modified for the reflow state. - // They are set back to their original values at the end of reflow. - nscoord saveComputedWidth = tempReflowState.computedWidth; - nscoord saveAvailableWidth = tempReflowState.availableWidth; - + nsHTMLReflowState secondPassState(aReflowState); + nsHTMLReflowState firstPassState(aReflowState); + + // Get the size of option elements inside the listbox // Compute the width based on the longest line in the listbox. - // XXX: This should be changed to do the inherited reflow unconstrained for - // the width to get the real desired width. GetDesiredSize is limited to - // text strings. - //XXX: TODO: Use the width and height of an the first reflow to determine how high and wide - //to make the listbox, instead of calculating it. The second reflow can be done actually position - //the frames correctly using: tempReflowState.reason = eReflowReason_Resize; - GetDesiredSize(&aPresContext, aReflowState, desiredSize, desiredLineSize); + firstPassState.computedWidth = NS_UNCONSTRAINEDSIZE; + firstPassState.computedHeight = NS_UNCONSTRAINEDSIZE; + firstPassState.availableWidth = NS_UNCONSTRAINEDSIZE; + firstPassState.availableHeight = NS_UNCONSTRAINEDSIZE; - // Retrieve the scrollbar's width and height + + nsSize scrolledAreaSize(0,0); + nsHTMLReflowMetrics scrolledAreaDesiredSize(&scrolledAreaSize); + nsScrollFrame::Reflow(aPresContext, + scrolledAreaDesiredSize, + firstPassState, + aStatus); + + // Compute the bounding box of the contents of the list using the area + // calculated by the first reflow as a starting point. + + nscoord scrolledAreaWidth = scrolledAreaDesiredSize.maxElementSize->width; + nscoord scrolledAreaHeight = scrolledAreaDesiredSize.height; + + // The first reflow produces a box with the scrollbar width and borders + // added in so we need to subtract them out. + + // Retrieve the scrollbar's width and height float sbWidth = 0.0; float sbHeight = 0.0;; nsCOMPtr dc; @@ -333,88 +425,116 @@ nsListControlFrame::Reflow(nsIPresContext& aPresContext, nscoord scrollbarWidth = NSToCoordRound(sbWidth); nscoord scrollbarHeight = NSToCoordRound(sbHeight); - if (mInDropDownMode) { - // For drop-down lists, always size the width of the list based on - // the desired lineSize. - tempReflowState.computedWidth = desiredLineSize.width + scrollbarWidth; - } else if (NS_UNCONSTRAINEDSIZE == tempReflowState.computedWidth) { - // Only set to the desired width if it hasn't been explicitly defined through CSS. - // GetDesiredSize must still be done above to calculate the desired height. - tempReflowState.computedWidth = desiredLineSize.width; + // Subtract out the scrollbar width + scrolledAreaWidth -= scrollbarWidth; + + // Subtract out the borders + nsMargin border; + if (!aReflowState.mStyleSpacing->GetBorder(border)) { + border.SizeTo(0, 0, 0, 0); } + //XXX: Should just get the border.top when needed instead of + //caching it. + mBorderOffsetY = border.top; - // Add vertical scrollbar, Always add it in, - // even though the vertical scrollbar is not needed all the time - // the inherited Reflow: allways leave space for the scrollbar by - // shrinking the scroll area by the scrollbar width + scrolledAreaWidth -= (border.left + border.right); + scrolledAreaHeight -= (border.top + border.bottom); - tempReflowState.computedWidth += scrollbarWidth; - - // Now that we have calculated the width required to just shrink around the longest line. - // Go ahead and reflow - // Do the inherited Reflow. This reflow uses the computed widths which - // are setup above. - // Don't set the height. Let the height be unconstrained + // Now the scrolledAreaWidth and scrolledAreaHeight are exactly + // wide and high enough to enclose their contents + nscoord visibleWidth = 0; if (mInDropDownMode) { - // The dropdown list height is the smaller of it's height setting or the height - // of the smallest box that can drawn around it's contents. - if (tempReflowState.computedHeight > (desiredSize.height)) { - tempReflowState.computedHeight = desiredSize.height ; + // Always set the width to be + // XXX: TODO The width of the list should be passed in so it can + // be set to the same as the text box + dropdown arrow. + visibleWidth = scrolledAreaWidth; + if (nsnull != mComboboxFrame) { + nsRect comboRect; + nsIFrame *comboFrame = nsnull; + if (NS_SUCCEEDED(mComboboxFrame->QueryInterface(kIFrameIID, (void **)&comboFrame))) { + comboFrame->GetRect(comboRect); + visibleWidth = comboRect.width; + visibleWidth -= (border.left + border.right); + } + } + + } else { + if (NS_UNCONSTRAINEDSIZE == aReflowState.computedWidth) { + visibleWidth = scrolledAreaWidth; + } else { + visibleWidth = aReflowState.computedWidth; } } - + + // Determine if a scrollbar will be needed, If so we need to add + // enough the width to allow for the scrollbar. + // The scrollbar will be needed under two conditions: + // (size * heightOfaRow) < scrolledAreaHeight or + // the height set through Style < scrolledAreaHeight. + + // Calculate the height of a row. + // XXX: Fix this for optgroup's. + nsIFrame* childFrame = nsnull; + mContentFrame->FirstChild(nsnull, &childFrame); + PRInt32 numChildren = LengthOf(childFrame); + PRInt32 heightOfARow = scrolledAreaHeight / numChildren; + + nscoord visibleHeight = 0; + if (mInDropDownMode) { + // Compute the visible height of the drop-down list + // The dropdown list height is the smaller of it's height setting or the height + // of the smallest box that can drawn around it's contents. + visibleHeight = scrolledAreaHeight; + + if (visibleHeight > (kMaxDropDownRows * heightOfARow)) + visibleHeight = (kMaxDropDownRows * heightOfARow); + + } else { + // Calculate the visible height of the listbox + if (NS_UNCONSTRAINEDSIZE != aReflowState.computedHeight) { + visibleHeight = aReflowState.computedHeight; + } else { + if (numChildren == mNumRows) { + // Make sure scrollbars go away when the number of rows + // matchs the number of items to scroll. + visibleHeight = aReflowState.computedHeight; + } else { + visibleHeight = mNumRows * heightOfARow; + } + } + } + + PRBool needsVerticalScrollbar = PR_FALSE; + if (visibleHeight < scrolledAreaHeight) { + needsVerticalScrollbar = PR_TRUE; + } + + if ((needsVerticalScrollbar) && (! mInDropDownMode)) { + visibleWidth += scrollbarWidth; + } + + // Do a second reflow with the adjusted width and height settings + // This sets up all of the frames with the correct width and height. + secondPassState.computedWidth = visibleWidth; + secondPassState.computedHeight = visibleHeight; + secondPassState.reason = eReflowReason_Resize; nsScrollFrame::Reflow(aPresContext, aDesiredSize, - tempReflowState, + secondPassState, aStatus); - // Now that we have reflowed, we may need to adjust the width and height returned - // for the listbox. - - if (PR_FALSE == mInDropDownMode) { - // Set the desired height to the computed height above only if it has not - // been explicitly set by CSS. - if (NS_UNCONSTRAINEDSIZE == tempReflowState.computedHeight) { - aDesiredSize.height = desiredSize.height; - } - } - - //Need to addback the scrollbar because the inherited reflow - //subtracts the scrollbar width when computing the desired size. - //We don't subtract when there isn't a vertical scrollbar. This may - //seem odd, becuase above we always add in the scrollbar width before calling - //reflow. The reason is that inherited reflow always leaves blank space for - //the scrollbar, but we don't whant that behavior for the listbox'es scrollbars. - if (needsVerticalScrollbar) { - aDesiredSize.width += scrollbarWidth; - } // Set the max element size to be the same as the desired element size. if (nsnull != aDesiredSize.maxElementSize) { aDesiredSize.maxElementSize->width = aDesiredSize.width; aDesiredSize.maxElementSize->height = aDesiredSize.height; } - // XXX: Set the min element size here to the desired element size here?? - // Restore to original reflow state. - tempReflowState.computedWidth = saveComputedWidth; - tempReflowState.availableWidth = saveAvailableWidth; - 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 * @@ -444,6 +564,7 @@ nsListControlFrame::GetOptionFromChild(nsIFrame* aParentFrame) return nsnull; } + //-------------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::GetFormContent(nsIContent*& aContent) const @@ -466,6 +587,17 @@ nsListControlFrame::GetFont(nsIPresContext* aPresContext, } +void nsListControlFrame::ForceRedraw(nsIContent* aContent) +{ + //XXX: Hack. This should not be needed. The problem is DisplaySelected + //and DisplayDeselected set and unset an attribute on generic HTML content + //which does not know that it should repaint as the result of the attribute + //being set. This should not be needed once the event state manager handles + //selection. + nsFormControlHelper::ForceDrawFrame(this); +} + + // XXX: Here we introduce a new -moz-option-selected attribute so a attribute // selecitor n the ua.css can change the style when the option is selected. @@ -474,8 +606,11 @@ void nsListControlFrame::DisplaySelected(nsIContent* aContent) //XXX: This is temporary. It simulates psuedo states by using a attribute selector on // -moz-option-selected in the ua.css style sheet. This will not be needed when // The event state manager is functional. KMM + nsCOMPtr selectedAtom ( dont_QueryInterface(NS_NewAtom(kMozSelected))); aContent->SetAttribute(kNameSpaceID_None, selectedAtom, "", PR_TRUE); + ForceRedraw(aContent); + } void nsListControlFrame::DisplayDeselected(nsIContent* aContent) @@ -483,8 +618,11 @@ void nsListControlFrame::DisplayDeselected(nsIContent* aContent) //XXX: This is temporary. It simulates psuedo states by using a attribute selector on // -moz-option-selected in the ua.css style sheet. This will not be needed when // The event state manager is functional. KMM + nsCOMPtr selectedAtom ( dont_QueryInterface(NS_NewAtom(kMozSelected))); aContent->UnsetAttribute(kNameSpaceID_None, selectedAtom, PR_TRUE); + ForceRedraw(aContent); + } void nsListControlFrame::UpdateItem(nsIContent* aContent, PRBool aSelected) @@ -517,7 +655,7 @@ PRInt32 nsListControlFrame::SetContentSelected(nsIFrame * aHitFrame, kid->GetNextSibling(&kid); index++; } - return -1; + return kNothingSelected; } //---------------------------------------------------------------------- @@ -584,82 +722,104 @@ void nsListControlFrame::ExtendedSelection(PRInt32 aStartIndex, PRInt32 aEndInde } } +void nsListControlFrame::SingleSelection() +{ + // Store previous selection + PRInt32 oldSelectedIndex = mSelectedIndex; + // Get Current selection + mSelectedIndex = (PRInt32)SetContentSelected(mHitFrame, mHitContent, PR_FALSE); + if (mSelectedIndex != kNothingSelected) { + if (oldSelectedIndex != mSelectedIndex) { + // Deselect the previous selection if there is one + if (oldSelectedIndex != kNothingSelected) { + SetFrameSelected(oldSelectedIndex, PR_FALSE); + } + // Dipslay the new selection + SetFrameSelected(mSelectedIndex, PR_TRUE); + mSelectedContent = mHitContent; + } else { + // Selecting the currently selected item so do nothing. + } + } +} + +void nsListControlFrame::MultipleSelection(PRBool aIsShift, PRBool aIsControl) +{ + PRInt32 oldSelectedIndex = mSelectedIndex; + + mSelectedIndex = (PRInt32)SetContentSelected(mHitFrame, mHitContent, PR_FALSE); + if (kNothingSelected != mSelectedIndex) { + PRBool wasSelected = IsFrameSelected(mSelectedIndex); + SetFrameSelected(mSelectedIndex, PR_TRUE); + PRBool selected = wasSelected; + + if (aIsShift) { + selected = PR_TRUE; + if (mEndExtendedIndex == kNothingSelected) { + 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 (aIsControl) { + selected = !selected; + if (nsnull != mSelectedContent) { + if (mHitContent != mSelectedContent) { + // Toggle the selection + UpdateItem(mHitContent, !wasSelected); + } else { +//XXX: Fix this mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); + } + } + } else { + mStartExtendedIndex = mSelectedIndex; + mEndExtendedIndex = kNothingSelected; + ClearSelection(); + selected = PR_TRUE; + } + + //XXX Fix this mHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); + mSelectedContent = mHitContent; + } + +} + //---------------------------------------------------------------------- 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_FALSE); - if (-1 < mSelectedIndex) { - PRBool wasSelected = IsFrameSelected(mSelectedIndex); - SetFrameSelected(mSelectedIndex, PR_TRUE); - PRBool selected = wasSelected; - 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) { - // Toggle the selection - UpdateItem(mHitContent, !wasSelected); - } else { - //XXX: Fix this mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); - } - } - } else { - mStartExtendedIndex = mSelectedIndex; - mEndExtendedIndex = -1; - ClearSelection(); - selected = PR_TRUE; - } - } else { - if (nsnull != mSelectedContent) { - DisplayDeselected(mSelectedContent); - NS_RELEASE(mSelectedContent); - } - } - //XXX: Remove SetFrameSelected(mSelectedIndex, selected); - //XXX mHitContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (selected?kSelectedFocus:kNormal), PR_TRUE); - mSelectedContent = mHitContent; - mSelectedFrame = mHitFrame; - aEventStatus = nsEventStatus_eConsumeNoDefault; + if (mMultipleSelections) { + MultipleSelection(((nsMouseEvent *)aEvent)->isShift, ((nsMouseEvent *)aEvent)->isControl); + } else { + SingleSelection(); } - } else if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) { - aEventStatus = nsEventStatus_eConsumeNoDefault; } + + aEventStatus = nsEventStatus_eConsumeNoDefault; return NS_OK; } @@ -697,7 +857,7 @@ NS_IMETHODIMP nsListControlFrame::HandleLikeDropDownListEvent(nsIPresContext& aP // 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) + // (Selection doesn't actually occur until the mouse is released, or clicked and released) if (aEvent->message == NS_MOUSE_MOVE) { // If the DropDown is currently has a selected item @@ -735,7 +895,7 @@ NS_IMETHODIMP nsListControlFrame::HandleLikeDropDownListEvent(nsIPresContext& aP // Start by finding the newly "hit" content from the hit frame PRInt32 index = SetContentSelected(mHitFrame, mHitContent, PR_TRUE); - if (-1 < index) { + if (kNothingSelected != index) { nsIDOMHTMLOptionElement* option = nsnull; nsresult rv = mHitContent->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); @@ -868,6 +1028,8 @@ nsListControlFrame::Init(nsIPresContext& aPresContext, return result; } +// XXX: This following methods are not referenced. They should be removed if they +// are not needed //---------------------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::GetMaxLength(PRInt32* aSize) @@ -906,19 +1068,7 @@ nsListControlFrame::GetSizeFromContent(PRInt32* aSize) const return result; } -//---------------------------------------------------------------------- -nscoord -nsListControlFrame::GetVerticalBorderWidth(float aPixToTwip) const -{ - return NSIntPixelsToTwips(3, aPixToTwip); -} - -//---------------------------------------------------------------------- -nscoord -nsListControlFrame::GetHorizontalBorderWidth(float aPixToTwip) const -{ - return GetVerticalBorderWidth(aPixToTwip); -} +//XXX: End of the un-referenced methods. //---------------------------------------------------------------------- nscoord @@ -939,101 +1089,6 @@ nsListControlFrame::GetHorizontalInsidePadding(nsIPresContext& aPresContext, } -//---------------------------------------------------------------------- - -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) { - 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 desiredSize; - nsSize minSize; - 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, aReflowState.rendContext, this, styleSize, - textSpec, desiredSize, minSize, widthExplicit, - heightExplicit, rowHeight); - - float sp2t; - float p2t; - aPresContext->GetPixelsToTwips(&p2t); - aPresContext->GetScaledPixelsToTwips(&sp2t); - - if (mInDropDownMode) { - nscoord extra = desiredSize.height - (rowHeight * mNumRows); - - mNumRows = (numOptions > 20 ? 20 : numOptions); - desiredSize.height = (mNumRows * rowHeight) + extra; - } - - aDesiredLayoutSize.width = desiredSize.width; - - // 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 = desiredSize.height; - aDesiredLayoutSize.ascent = aDesiredLayoutSize.height; - aDesiredLayoutSize.descent = 0; - - // XXX When this code is working, max element size needs to be set properly from CalculateSize - // above. look at some of the other form control to see how to do it. - - aDesiredWidgetSize.width = maxWidth; - aDesiredWidgetSize.height = rowHeight; - - if (aDesiredLayoutSize.maxElementSize) { - aDesiredLayoutSize.maxElementSize->width = minSize.width; - aDesiredLayoutSize.maxElementSize->height = minSize.height; - } - - NS_RELEASE(select); - NS_RELEASE(options); - -} - - //---------------------------------------------------------------------- NS_IMETHODIMP nsListControlFrame::GetMultiple(PRBool* aMultiple, nsIDOMHTMLSelectElement* aSelect) @@ -1076,7 +1131,6 @@ nsListControlFrame::AboutToDropDown() kid->GetContent(&content); if (i == mSelectedIndex) { mSelectedContent = content; - mSelectedFrame = kid; DisplaySelected(mSelectedContent); return NS_OK; } @@ -1158,6 +1212,10 @@ nsListControlFrame::InitializeFromContent(PRBool aDoDisplay) PRBool selected; option->GetDefaultSelected(&selected); SetFrameSelected(i, selected); + // Set the selected index for single selection list boxes. + if (selected) { + mSelectedIndex = i; + } if (mInDropDownMode) { if (selected) { @@ -1168,7 +1226,6 @@ nsListControlFrame::InitializeFromContent(PRBool aDoDisplay) mSelectedIndex = i; NS_RELEASE(option); mSelectedContent = content; - mSelectedFrame = kid; return; } } else { @@ -1434,8 +1491,8 @@ nsListControlFrame::SetFocus(PRBool aOn, PRBool aRepaint) // in this call? if (mSelectedContent) { //XXX: Need correct attribute here - mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (aOn?kSelectedFocus:kNormal), PR_TRUE); - } +// mSelectedContent->SetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::kClass, (aOn?kSelectedFocus:kNormal), PR_TRUE); + } } @@ -1479,8 +1536,92 @@ PRInt32 nsListControlFrame::GetNumberOfOptions() return(numOptions); } +// Set the selected attribute for an item in the listbox. If the listbox has +// been initialized the result of the setting the attribute is displayed otherwise +// it is not. -NS_IMETHODIMP nsListControlFrame::SetProperty(nsIAtom* aName, const nsString& aValue) +void +nsListControlFrame::SetContentSelectedAttribute(PRUint32 aIndex, PRBool aSelected) +{ + // Only display the result of the attribute changed if the listbox has been + // initialized. + if (mIsInitializedFromContent) { + // Update the attribute and display it + SetFrameSelected(aIndex, aSelected); + } else { + // Update the attribute without displaying it + nsIContent* content = GetOptionContent(aIndex); + NS_ASSERTION(nsnull != content, "Failed to retrieve option content"); + nsCOMPtr selectedAtom ( dont_QueryInterface(NS_NewAtom(kMozSelected))); + if (aSelected) { + content->SetAttribute(kNameSpaceID_None, selectedAtom, "", PR_FALSE); + } else { + content->UnsetAttribute(kNameSpaceID_None, selectedAtom, PR_FALSE); + } + NS_IF_RELEASE(content); + } +} + +// Set a single item in the listbox to selected. + +void +nsListControlFrame::SetSelectedIndex(PRInt32 aIndex, nsIContent *aContent) +{ + SetContentSelectedAttribute(aIndex, PR_TRUE); + mSelectedContent = aContent; + mSelectedIndex = aIndex; + + // Get the selected string from the content + nsIDOMHTMLOptionElement* option = nsnull; + nsresult rv = aContent->QueryInterface(kIDOMHTMLOptionElementIID, (void**)&option); + if (NS_SUCCEEDED(rv)) { + nsAutoString text; + if (NS_CONTENT_ATTR_HAS_VALUE == option->GetText(text)) { + mSelectionStr = text; + } + NS_RELEASE(option); + } +} + + +// Select the specified item in the listbox using control logic. +// If it a single selection listbox the previous selection will be +// de-selected. + +void +nsListControlFrame::ToggleSelected(PRInt32 aIndex, nsIContent *aContent) +{ + PRBool multiple; + GetMultiple(&multiple); + + if (PR_TRUE == multiple) { + SetSelectedIndex(aIndex, aContent); + } else { + if (mSelectedIndex != kNothingSelected) { + SetContentSelectedAttribute(aIndex, PR_FALSE); + } + SetSelectedIndex(aIndex, aContent); + } +} + +// Select the specified item in the listbox using + +void +nsListControlFrame::SelectIndex(PRInt32 aIndex) +{ + nsIDOMHTMLCollection* options = GetOptions(mContent); + nsIDOMHTMLOptionElement* optionElement = GetOption(*options, aIndex); + nsIContent *content = nsnull; + nsresult result = optionElement->QueryInterface(kIContentIID, (void**) &content); + ToggleSelected(aIndex, content); + NS_RELEASE(content); + NS_RELEASE(optionElement); + NS_RELEASE(options); +} + + +NS_IMETHODIMP +nsListControlFrame::SetProperty(nsIAtom* aName, const nsString& aValue) { if (nsHTMLAtoms::selected == aName) { return NS_ERROR_INVALID_ARG; // Selected is readonly according to spec. @@ -1490,7 +1631,8 @@ NS_IMETHODIMP nsListControlFrame::SetProperty(nsIAtom* aName, const nsString& aV if (error) { return NS_ERROR_INVALID_ARG; // Couldn't convert to integer } else { - SetFrameSelected(selectedIndex, PR_TRUE); + // Select the specified item in the list box. + SelectIndex(selectedIndex); } } else { //XXX: TODO What about all othe other properties that form elements can set here? @@ -1499,7 +1641,8 @@ NS_IMETHODIMP nsListControlFrame::SetProperty(nsIAtom* aName, const nsString& aV return NS_OK; } -NS_IMETHODIMP nsListControlFrame::GetProperty(nsIAtom* aName, nsString& aValue) +NS_IMETHODIMP +nsListControlFrame::GetProperty(nsIAtom* aName, nsString& aValue) { // Get the selected value of option from local cache (optimization vs. widget) if (nsHTMLAtoms::selected == aName) { @@ -1516,7 +1659,7 @@ NS_IMETHODIMP nsListControlFrame::GetProperty(nsIAtom* aName, nsString& aValue) // Spin through loooking for the first selection in the list PRInt32 index = 0; PRInt32 maxOptions = GetNumberOfOptions(); - PRInt32 selectedIndex = -1; + PRInt32 selectedIndex = kNothingSelected; for (index = 0; index < maxOptions; index++) { PRBool isSelected = PR_FALSE; diff --git a/mozilla/layout/html/forms/src/nsListControlFrame.h b/mozilla/layout/html/forms/src/nsListControlFrame.h index c9c90552897..4af053bc51e 100644 --- a/mozilla/layout/html/forms/src/nsListControlFrame.h +++ b/mozilla/layout/html/forms/src/nsListControlFrame.h @@ -32,6 +32,7 @@ class nsIDOMHTMLOptionElement; class nsFormFrame; class nsScrollFrame; class nsIComboboxControlFrame; +class nsVoidArray; /** @@ -83,26 +84,17 @@ public: NS_IMETHOD SetProperty(nsIAtom* aName, const nsString& aValue); NS_IMETHOD GetProperty(nsIAtom* aName, nsString& aValue); - virtual void GetDesiredSize(nsIPresContext* aPresContext, - const nsHTMLReflowState& aReflowState, - nsHTMLReflowMetrics& aDesiredLayoutSize, - nsSize& aDesiredWidgetSize); - /*virtual nsresult Focus(nsIDOMEvent* aEvent); - virtual nsresult Blur(nsIDOMEvent* aEvent); - virtual nsresult HandleEvent(nsIDOMEvent* aEvent); - NS_IMETHOD AddEventListener(nsIDOMNode * aNode); - NS_IMETHOD RemoveEventListener(nsIDOMNode * aNode); - - NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext);*/ - + virtual nsresult Blur(nsIDOMEvent* aEvent); */ + NS_METHOD GetMultiple(PRBool* aResult, nsIDOMHTMLSelectElement* aSelect = nsnull); +// XXX the following methods are not referenced. They should be removed when it is +// confirmed that they are not needed virtual nsresult GetSizeFromContent(PRInt32* aSize) const; NS_IMETHOD GetMaxLength(PRInt32* aSize); +// XXX: End of the unreferenced methods - 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, @@ -110,17 +102,18 @@ public: nscoord aInnerWidth, nscoord aCharWidth) const; + virtual nsresult RequiresWidget(PRBool &aRequiresWidget); + 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 @@ -183,6 +176,17 @@ protected: void DisplaySelected(nsIContent* aContent); void DisplayDeselected(nsIContent* aContent); void UpdateItem(nsIContent* aContent, PRBool aSelected); + void ForceRedraw(nsIContent* aContent); + PRBool IsOptionGroup(nsIFrame* aFrame); + void ConstructSelectableList(nsIFrame* aFrame, nsVoidArray *aList); + nsIFrame* GetFirstSelectableFrame(nsVoidArray *aList, PRInt32& aPos); + nsIFrame* GetNextSelectableFrame(nsVoidArray *aList, PRInt32& aPos); + void SingleSelection(); + void MultipleSelection(PRBool aIsShift, PRBool aIsControl); + void SelectIndex(PRInt32 aIndex); + void ToggleSelected(PRInt32 aIndex, nsIContent *aContent); + void SetSelectedIndex(PRInt32 aIndex, nsIContent *aContent); + void SetContentSelectedAttribute(PRUint32 aIndex, PRBool aSelected); // nsHTMLContainerFrame overrides @@ -202,10 +206,9 @@ protected: PRBool aDisplaySelected); // Data Members + nscoord mBorderOffsetY; nsFormFrame* mFormFrame; PRInt32 mNumRows; -//XXX: TODO: This should not be hardcoded to 64 -//ZZZ PRBool mIsFrameSelected[64]; PRInt32 mNumSelections; PRInt32 mMaxNumSelections; PRBool mMultipleSelections; @@ -216,13 +219,13 @@ protected: nsIContent * mHitContent; nsIFrame * mCurrentHitFrame; nsIContent * mCurrentHitContent; - nsIFrame * mSelectedFrame; nsIContent * mSelectedContent; PRBool mIsInitializedFromContent; nsIFrame * mContentFrame; PRBool mInDropDownMode; nsIComboboxControlFrame * mComboboxFrame; nsString mSelectionStr; + nsVoidArray* mSelectableFrames; }; diff --git a/mozilla/layout/style/ua.css b/mozilla/layout/style/ua.css index c2e7e2859f1..3b867eef998 100644 --- a/mozilla/layout/style/ua.css +++ b/mozilla/layout/style/ua.css @@ -686,6 +686,7 @@ select { border: 1px inset #c0c0c0; background-color: white; color:black; + white-space:nowrap; } option { display:block; @@ -707,6 +708,19 @@ option.selectedfocus { background-color: rgb(0,0,128); color: rgb(255,255,255); } + +optgroup { + font-style:italic; + font-weight:bold; +} + +optgroup > option { + position:relative; + left:20px; + font-style:normal; + font-weight:normal; +} + textarea { vertical-align: bottom; border: 2px inset #c0c0c0; @@ -915,8 +929,6 @@ sourcetext { :-moz-dropdown-list { position:absolute; - height:200px; - width:100px; background-color:white; border: 1px solid blue; }