michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: michael@0: #include "nsMathMLmfencedFrame.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsMathMLChar.h" michael@0: #include michael@0: michael@0: // michael@0: // -- surround content with a pair of fences michael@0: // michael@0: michael@0: nsIFrame* michael@0: NS_NewMathMLmfencedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsMathMLmfencedFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfencedFrame) michael@0: michael@0: nsMathMLmfencedFrame::~nsMathMLmfencedFrame() michael@0: { michael@0: RemoveFencesAndSeparators(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMathMLmfencedFrame::InheritAutomaticData(nsIFrame* aParent) michael@0: { michael@0: // let the base class get the default from our parent michael@0: nsMathMLContainerFrame::InheritAutomaticData(aParent); michael@0: michael@0: mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; michael@0: michael@0: RemoveFencesAndSeparators(); michael@0: CreateFencesAndSeparators(PresContext()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLmfencedFrame::SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) michael@0: { michael@0: // First, let the base class do its work michael@0: nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // InheritAutomaticData will not get called if our parent is not a mathml michael@0: // frame, so initialize NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY for michael@0: // GetPreferredStretchSize() from Reflow(). michael@0: mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; michael@0: // No need to track the style contexts given to our MathML chars. michael@0: // The Style System will use Get/SetAdditionalStyleContext() to keep them michael@0: // up-to-date if dynamic changes arise. michael@0: CreateFencesAndSeparators(PresContext()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLmfencedFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: RemoveFencesAndSeparators(); michael@0: CreateFencesAndSeparators(PresContext()); michael@0: michael@0: return nsMathMLContainerFrame:: michael@0: AttributeChanged(aNameSpaceID, aAttribute, aModType); michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLmfencedFrame::ChildListChanged(int32_t aModType) michael@0: { michael@0: RemoveFencesAndSeparators(); michael@0: CreateFencesAndSeparators(PresContext()); michael@0: michael@0: return nsMathMLContainerFrame::ChildListChanged(aModType); michael@0: } michael@0: michael@0: void michael@0: nsMathMLmfencedFrame::RemoveFencesAndSeparators() michael@0: { michael@0: delete mOpenChar; michael@0: delete mCloseChar; michael@0: if (mSeparatorsChar) delete[] mSeparatorsChar; michael@0: michael@0: mOpenChar = nullptr; michael@0: mCloseChar = nullptr; michael@0: mSeparatorsChar = nullptr; michael@0: mSeparatorsCount = 0; michael@0: } michael@0: michael@0: void michael@0: nsMathMLmfencedFrame::CreateFencesAndSeparators(nsPresContext* aPresContext) michael@0: { michael@0: nsAutoString value; michael@0: michael@0: ////////////// michael@0: // see if the opening fence is there ... michael@0: if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::open, value)) { michael@0: value = char16_t('('); // default as per the MathML REC michael@0: } else { michael@0: value.CompressWhitespace(); michael@0: } michael@0: michael@0: if (!value.IsEmpty()) { michael@0: mOpenChar = new nsMathMLChar; michael@0: mOpenChar->SetData(aPresContext, value); michael@0: ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, mOpenChar); michael@0: } michael@0: michael@0: ////////////// michael@0: // see if the closing fence is there ... michael@0: if(!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::close, value)) { michael@0: value = char16_t(')'); // default as per the MathML REC michael@0: } else { michael@0: value.CompressWhitespace(); michael@0: } michael@0: michael@0: if (!value.IsEmpty()) { michael@0: mCloseChar = new nsMathMLChar; michael@0: mCloseChar->SetData(aPresContext, value); michael@0: ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, mCloseChar); michael@0: } michael@0: michael@0: ////////////// michael@0: // see if separators are there ... michael@0: if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::separators_, value)) { michael@0: value = char16_t(','); // default as per the MathML REC michael@0: } else { michael@0: value.StripWhitespace(); michael@0: } michael@0: michael@0: mSeparatorsCount = value.Length(); michael@0: if (0 < mSeparatorsCount) { michael@0: int32_t sepCount = mFrames.GetLength() - 1; michael@0: if (0 < sepCount) { michael@0: mSeparatorsChar = new nsMathMLChar[sepCount]; michael@0: nsAutoString sepChar; michael@0: for (int32_t i = 0; i < sepCount; i++) { michael@0: if (i < mSeparatorsCount) { michael@0: sepChar = value[i]; michael@0: } michael@0: else { michael@0: sepChar = value[mSeparatorsCount-1]; michael@0: } michael@0: mSeparatorsChar[i].SetData(aPresContext, sepChar); michael@0: ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, &mSeparatorsChar[i]); michael@0: } michael@0: mSeparatorsCount = sepCount; michael@0: } else { michael@0: // No separators. Note that sepCount can be -1 here, so don't michael@0: // set mSeparatorsCount to it. michael@0: mSeparatorsCount = 0; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsMathMLmfencedFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: ///////////// michael@0: // display the content michael@0: nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); michael@0: michael@0: //////////// michael@0: // display fences and separators michael@0: uint32_t count = 0; michael@0: if (mOpenChar) { michael@0: mOpenChar->Display(aBuilder, this, aLists, count++); michael@0: } michael@0: michael@0: if (mCloseChar) { michael@0: mCloseChar->Display(aBuilder, this, aLists, count++); michael@0: } michael@0: michael@0: for (int32_t i = 0; i < mSeparatorsCount; i++) { michael@0: mSeparatorsChar[i].Display(aBuilder, this, aLists, count++); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLmfencedFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: nsresult rv; michael@0: aDesiredSize.Width() = aDesiredSize.Height() = 0; michael@0: aDesiredSize.SetTopAscent(0); michael@0: aDesiredSize.mBoundingMetrics = nsBoundingMetrics(); michael@0: michael@0: int32_t i; michael@0: const nsStyleFont* font = StyleFont(); michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); michael@0: aReflowState.rendContext->SetFont(fm); michael@0: nscoord axisHeight, em; michael@0: GetAxisHeight(*aReflowState.rendContext, fm, axisHeight); michael@0: GetEmHeight(fm, em); michael@0: // leading to be left at the top and the bottom of stretched chars michael@0: nscoord leading = NSToCoordRound(0.2f * em); michael@0: michael@0: ///////////// michael@0: // Reflow children michael@0: // Asking each child to cache its bounding metrics michael@0: michael@0: // Note that we don't use the base method nsMathMLContainerFrame::Reflow() michael@0: // because we want to stretch our fences, separators and stretchy frames using michael@0: // the *same* initial aDesiredSize.mBoundingMetrics. If we were to use the base michael@0: // method here, our stretchy frames will be stretched and placed, and we may michael@0: // end up stretching our fences/separators with a different aDesiredSize. michael@0: // XXX The above decision was revisited in bug 121748 and this code can be michael@0: // refactored to use nsMathMLContainerFrame::Reflow() at some stage. michael@0: michael@0: nsReflowStatus childStatus; michael@0: nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); michael@0: nsIFrame* firstChild = GetFirstPrincipalChild(); michael@0: nsIFrame* childFrame = firstChild; michael@0: nscoord ascent = 0, descent = 0; michael@0: if (firstChild || mOpenChar || mCloseChar || mSeparatorsCount > 0) { michael@0: // We use the ASCII metrics to get our minimum height. This way, michael@0: // if we have borders or a background, they will fit better with michael@0: // other elements on the line. michael@0: ascent = fm->MaxAscent(); michael@0: descent = fm->MaxDescent(); michael@0: } michael@0: while (childFrame) { michael@0: nsHTMLReflowMetrics childDesiredSize(aReflowState, michael@0: aDesiredSize.mFlags michael@0: | NS_REFLOW_CALC_BOUNDING_METRICS); michael@0: nsHTMLReflowState childReflowState(aPresContext, aReflowState, michael@0: childFrame, availSize); michael@0: rv = ReflowChild(childFrame, aPresContext, childDesiredSize, michael@0: childReflowState, childStatus); michael@0: //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status"); michael@0: if (NS_FAILED(rv)) { michael@0: // Call DidReflow() for the child frames we successfully did reflow. michael@0: DidReflowChildren(firstChild, childFrame); michael@0: return rv; michael@0: } michael@0: michael@0: SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, michael@0: childDesiredSize.mBoundingMetrics); michael@0: michael@0: nscoord childDescent = childDesiredSize.Height() - childDesiredSize.TopAscent(); michael@0: if (descent < childDescent) michael@0: descent = childDescent; michael@0: if (ascent < childDesiredSize.TopAscent()) michael@0: ascent = childDesiredSize.TopAscent(); michael@0: michael@0: childFrame = childFrame->GetNextSibling(); michael@0: } michael@0: michael@0: ///////////// michael@0: // Ask stretchy children to stretch themselves michael@0: michael@0: nsBoundingMetrics containerSize; michael@0: nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL; michael@0: michael@0: GetPreferredStretchSize(*aReflowState.rendContext, michael@0: 0, /* i.e., without embellishments */ michael@0: stretchDir, containerSize); michael@0: childFrame = firstChild; michael@0: while (childFrame) { michael@0: nsIMathMLFrame* mathmlChild = do_QueryFrame(childFrame); michael@0: if (mathmlChild) { michael@0: nsHTMLReflowMetrics childDesiredSize(aReflowState); michael@0: // retrieve the metrics that was stored at the previous pass michael@0: GetReflowAndBoundingMetricsFor(childFrame, childDesiredSize, michael@0: childDesiredSize.mBoundingMetrics); michael@0: michael@0: mathmlChild->Stretch(*aReflowState.rendContext, michael@0: stretchDir, containerSize, childDesiredSize); michael@0: // store the updated metrics michael@0: SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, michael@0: childDesiredSize.mBoundingMetrics); michael@0: michael@0: nscoord childDescent = childDesiredSize.Height() - childDesiredSize.TopAscent(); michael@0: if (descent < childDescent) michael@0: descent = childDescent; michael@0: if (ascent < childDesiredSize.TopAscent()) michael@0: ascent = childDesiredSize.TopAscent(); michael@0: } michael@0: childFrame = childFrame->GetNextSibling(); michael@0: } michael@0: michael@0: // bug 121748: for surrounding fences & separators, use a size that covers everything michael@0: GetPreferredStretchSize(*aReflowState.rendContext, michael@0: STRETCH_CONSIDER_EMBELLISHMENTS, michael@0: stretchDir, containerSize); michael@0: michael@0: ////////////////////////////////////////// michael@0: // Prepare the opening fence, separators, and closing fence, and michael@0: // adjust the origin of children. michael@0: michael@0: // we need to center around the axis michael@0: nscoord delta = std::max(containerSize.ascent - axisHeight, michael@0: containerSize.descent + axisHeight); michael@0: containerSize.ascent = delta + axisHeight; michael@0: containerSize.descent = delta - axisHeight; michael@0: michael@0: bool isRTL = StyleVisibility()->mDirection; michael@0: michael@0: ///////////////// michael@0: // opening fence ... michael@0: ReflowChar(aPresContext, *aReflowState.rendContext, mOpenChar, michael@0: NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, michael@0: axisHeight, leading, em, containerSize, ascent, descent, isRTL); michael@0: ///////////////// michael@0: // separators ... michael@0: for (i = 0; i < mSeparatorsCount; i++) { michael@0: ReflowChar(aPresContext, *aReflowState.rendContext, &mSeparatorsChar[i], michael@0: NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel, michael@0: axisHeight, leading, em, containerSize, ascent, descent, isRTL); michael@0: } michael@0: ///////////////// michael@0: // closing fence ... michael@0: ReflowChar(aPresContext, *aReflowState.rendContext, mCloseChar, michael@0: NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, michael@0: axisHeight, leading, em, containerSize, ascent, descent, isRTL); michael@0: michael@0: ////////////////// michael@0: // Adjust the origins of each child. michael@0: // and update our bounding metrics michael@0: michael@0: i = 0; michael@0: nscoord dx = 0; michael@0: nsBoundingMetrics bm; michael@0: bool firstTime = true; michael@0: nsMathMLChar *leftChar, *rightChar; michael@0: if (isRTL) { michael@0: leftChar = mCloseChar; michael@0: rightChar = mOpenChar; michael@0: } else { michael@0: leftChar = mOpenChar; michael@0: rightChar = mCloseChar; michael@0: } michael@0: michael@0: if (leftChar) { michael@0: PlaceChar(leftChar, ascent, bm, dx); michael@0: aDesiredSize.mBoundingMetrics = bm; michael@0: firstTime = false; michael@0: } michael@0: michael@0: if (isRTL) { michael@0: childFrame = this->GetLastChild(nsIFrame::kPrincipalList); michael@0: } else { michael@0: childFrame = firstChild; michael@0: } michael@0: while (childFrame) { michael@0: nsHTMLReflowMetrics childSize(aReflowState); michael@0: GetReflowAndBoundingMetricsFor(childFrame, childSize, bm); michael@0: if (firstTime) { michael@0: firstTime = false; michael@0: aDesiredSize.mBoundingMetrics = bm; michael@0: } michael@0: else michael@0: aDesiredSize.mBoundingMetrics += bm; michael@0: michael@0: FinishReflowChild(childFrame, aPresContext, childSize, nullptr, michael@0: dx, ascent - childSize.TopAscent(), 0); michael@0: dx += childSize.Width(); michael@0: michael@0: if (i < mSeparatorsCount) { michael@0: PlaceChar(&mSeparatorsChar[isRTL ? mSeparatorsCount - 1 - i : i], michael@0: ascent, bm, dx); michael@0: aDesiredSize.mBoundingMetrics += bm; michael@0: } michael@0: i++; michael@0: michael@0: if (isRTL) { michael@0: childFrame = childFrame->GetPrevSibling(); michael@0: } else { michael@0: childFrame = childFrame->GetNextSibling(); michael@0: } michael@0: } michael@0: michael@0: if (rightChar) { michael@0: PlaceChar(rightChar, ascent, bm, dx); michael@0: if (firstTime) michael@0: aDesiredSize.mBoundingMetrics = bm; michael@0: else michael@0: aDesiredSize.mBoundingMetrics += bm; michael@0: } michael@0: michael@0: aDesiredSize.Width() = aDesiredSize.mBoundingMetrics.width; michael@0: aDesiredSize.Height() = ascent + descent; michael@0: aDesiredSize.SetTopAscent(ascent); michael@0: michael@0: SetBoundingMetrics(aDesiredSize.mBoundingMetrics); michael@0: SetReference(nsPoint(0, aDesiredSize.TopAscent())); michael@0: michael@0: // see if we should fix the spacing michael@0: FixInterFrameSpacing(aDesiredSize); michael@0: michael@0: // Finished with these: michael@0: ClearSavedChildMetrics(); michael@0: michael@0: // Set our overflow area michael@0: GatherAndStoreOverflow(&aDesiredSize); michael@0: michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static void michael@0: GetCharSpacing(nsMathMLChar* aMathMLChar, michael@0: nsOperatorFlags aForm, michael@0: int32_t aScriptLevel, michael@0: nscoord em, michael@0: nscoord& aLeftSpace, michael@0: nscoord& aRightSpace) michael@0: { michael@0: nsAutoString data; michael@0: aMathMLChar->GetData(data); michael@0: nsOperatorFlags flags = 0; michael@0: float lspace = 0.0f; michael@0: float rspace = 0.0f; michael@0: bool found = nsMathMLOperators::LookupOperator(data, aForm, michael@0: &flags, &lspace, &rspace); michael@0: michael@0: // We don't want extra space when we are a script michael@0: if (found && aScriptLevel > 0) { michael@0: lspace /= 2.0f; michael@0: rspace /= 2.0f; michael@0: } michael@0: michael@0: aLeftSpace = NSToCoordRound(lspace * em); michael@0: aRightSpace = NSToCoordRound(rspace * em); michael@0: } michael@0: michael@0: // helper functions to perform the common task of formatting our chars michael@0: /*static*/ nsresult michael@0: nsMathMLmfencedFrame::ReflowChar(nsPresContext* aPresContext, michael@0: nsRenderingContext& aRenderingContext, michael@0: nsMathMLChar* aMathMLChar, michael@0: nsOperatorFlags aForm, michael@0: int32_t aScriptLevel, michael@0: nscoord axisHeight, michael@0: nscoord leading, michael@0: nscoord em, michael@0: nsBoundingMetrics& aContainerSize, michael@0: nscoord& aAscent, michael@0: nscoord& aDescent, michael@0: bool aRTL) michael@0: { michael@0: if (aMathMLChar && 0 < aMathMLChar->Length()) { michael@0: nscoord leftSpace; michael@0: nscoord rightSpace; michael@0: GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace); michael@0: michael@0: // stretch the char to the appropriate height if it is not big enough. michael@0: nsBoundingMetrics charSize; michael@0: nsresult res = aMathMLChar->Stretch(aPresContext, aRenderingContext, michael@0: NS_STRETCH_DIRECTION_VERTICAL, michael@0: aContainerSize, charSize, michael@0: NS_STRETCH_NORMAL, aRTL); michael@0: michael@0: if (NS_STRETCH_DIRECTION_UNSUPPORTED != aMathMLChar->GetStretchDirection()) { michael@0: // has changed... so center the char around the axis michael@0: nscoord height = charSize.ascent + charSize.descent; michael@0: charSize.ascent = height/2 + axisHeight; michael@0: charSize.descent = height - charSize.ascent; michael@0: } michael@0: else { michael@0: // either it hasn't changed or stretching the char failed (i.e., michael@0: // GetBoundingMetrics failed) michael@0: leading = 0; michael@0: if (NS_FAILED(res)) { michael@0: nsAutoString data; michael@0: aMathMLChar->GetData(data); michael@0: nsBoundingMetrics metrics = michael@0: aRenderingContext.GetBoundingMetrics(data.get(), data.Length()); michael@0: charSize.ascent = metrics.ascent; michael@0: charSize.descent = metrics.descent; michael@0: charSize.width = metrics.width; michael@0: // Set this as the bounding metrics of the MathMLChar to leave michael@0: // the necessary room to paint the char. michael@0: aMathMLChar->SetBoundingMetrics(charSize); michael@0: } michael@0: } michael@0: michael@0: if (aAscent < charSize.ascent + leading) michael@0: aAscent = charSize.ascent + leading; michael@0: if (aDescent < charSize.descent + leading) michael@0: aDescent = charSize.descent + leading; michael@0: michael@0: // account the spacing michael@0: charSize.width += leftSpace + rightSpace; michael@0: michael@0: // x-origin is used to store lspace ... michael@0: // y-origin is used to stored the ascent ... michael@0: aMathMLChar->SetRect(nsRect(leftSpace, michael@0: charSize.ascent, charSize.width, michael@0: charSize.ascent + charSize.descent)); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /*static*/ void michael@0: nsMathMLmfencedFrame::PlaceChar(nsMathMLChar* aMathMLChar, michael@0: nscoord aDesiredAscent, michael@0: nsBoundingMetrics& bm, michael@0: nscoord& dx) michael@0: { michael@0: aMathMLChar->GetBoundingMetrics(bm); michael@0: michael@0: // the char's x-origin was used to store lspace ... michael@0: // the char's y-origin was used to store the ascent ... michael@0: // the char's width was used to store the advance with (with spacing) ... michael@0: nsRect rect; michael@0: aMathMLChar->GetRect(rect); michael@0: michael@0: nscoord dy = aDesiredAscent - rect.y; michael@0: if (aMathMLChar->GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED) { michael@0: // the stretchy char will be centered around the axis michael@0: // so we adjust the returned bounding metrics accordingly michael@0: bm.descent = (bm.ascent + bm.descent) - rect.y; michael@0: bm.ascent = rect.y; michael@0: } michael@0: michael@0: aMathMLChar->SetRect(nsRect(dx + rect.x, dy, bm.width, rect.height)); michael@0: michael@0: bm.leftBearing += rect.x; michael@0: bm.rightBearing += rect.x; michael@0: michael@0: // return rect.width since it includes lspace and rspace michael@0: bm.width = rect.width; michael@0: dx += rect.width; michael@0: } michael@0: michael@0: static nscoord michael@0: GetMaxCharWidth(nsPresContext* aPresContext, michael@0: nsRenderingContext* aRenderingContext, michael@0: nsMathMLChar* aMathMLChar, michael@0: nsOperatorFlags aForm, michael@0: int32_t aScriptLevel, michael@0: nscoord em) michael@0: { michael@0: nscoord width = aMathMLChar->GetMaxWidth(aPresContext, *aRenderingContext); michael@0: michael@0: if (0 < aMathMLChar->Length()) { michael@0: nscoord leftSpace; michael@0: nscoord rightSpace; michael@0: GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace); michael@0: michael@0: width += leftSpace + rightSpace; michael@0: } michael@0: michael@0: return width; michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsMathMLmfencedFrame::GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: nscoord width = 0; michael@0: michael@0: nsPresContext* presContext = PresContext(); michael@0: const nsStyleFont* font = StyleFont(); michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); michael@0: nscoord em; michael@0: GetEmHeight(fm, em); michael@0: michael@0: if (mOpenChar) { michael@0: width += michael@0: GetMaxCharWidth(presContext, aRenderingContext, mOpenChar, michael@0: NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, em); michael@0: } michael@0: michael@0: int32_t i = 0; michael@0: nsIFrame* childFrame = GetFirstPrincipalChild(); michael@0: while (childFrame) { michael@0: // XXX This includes margin while Reflow currently doesn't consider michael@0: // margin, so we may end up with too much space, but, with stretchy michael@0: // characters, this is an approximation anyway. michael@0: width += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame, michael@0: nsLayoutUtils::PREF_WIDTH); michael@0: michael@0: if (i < mSeparatorsCount) { michael@0: width += michael@0: GetMaxCharWidth(presContext, aRenderingContext, &mSeparatorsChar[i], michael@0: NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel, em); michael@0: } michael@0: i++; michael@0: michael@0: childFrame = childFrame->GetNextSibling(); michael@0: } michael@0: michael@0: if (mCloseChar) { michael@0: width += michael@0: GetMaxCharWidth(presContext, aRenderingContext, mCloseChar, michael@0: NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, em); michael@0: } michael@0: michael@0: aDesiredSize.Width() = width; michael@0: aDesiredSize.mBoundingMetrics.width = width; michael@0: aDesiredSize.mBoundingMetrics.leftBearing = 0; michael@0: aDesiredSize.mBoundingMetrics.rightBearing = width; michael@0: } michael@0: michael@0: nscoord michael@0: nsMathMLmfencedFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize); michael@0: if (!gap) return 0; michael@0: michael@0: nsRect rect; michael@0: if (mOpenChar) { michael@0: mOpenChar->GetRect(rect); michael@0: rect.MoveBy(gap, 0); michael@0: mOpenChar->SetRect(rect); michael@0: } michael@0: if (mCloseChar) { michael@0: mCloseChar->GetRect(rect); michael@0: rect.MoveBy(gap, 0); michael@0: mCloseChar->SetRect(rect); michael@0: } michael@0: for (int32_t i = 0; i < mSeparatorsCount; i++) { michael@0: mSeparatorsChar[i].GetRect(rect); michael@0: rect.MoveBy(gap, 0); michael@0: mSeparatorsChar[i].SetRect(rect); michael@0: } michael@0: return gap; michael@0: } michael@0: michael@0: // ---------------------- michael@0: // the Style System will use these to pass the proper style context to our MathMLChar michael@0: nsStyleContext* michael@0: nsMathMLmfencedFrame::GetAdditionalStyleContext(int32_t aIndex) const michael@0: { michael@0: int32_t openIndex = -1; michael@0: int32_t closeIndex = -1; michael@0: int32_t lastIndex = mSeparatorsCount-1; michael@0: michael@0: if (mOpenChar) { michael@0: lastIndex++; michael@0: openIndex = lastIndex; michael@0: } michael@0: if (mCloseChar) { michael@0: lastIndex++; michael@0: closeIndex = lastIndex; michael@0: } michael@0: if (aIndex < 0 || aIndex > lastIndex) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aIndex < mSeparatorsCount) { michael@0: return mSeparatorsChar[aIndex].GetStyleContext(); michael@0: } michael@0: else if (aIndex == openIndex) { michael@0: return mOpenChar->GetStyleContext(); michael@0: } michael@0: else if (aIndex == closeIndex) { michael@0: return mCloseChar->GetStyleContext(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsMathMLmfencedFrame::SetAdditionalStyleContext(int32_t aIndex, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: int32_t openIndex = -1; michael@0: int32_t closeIndex = -1; michael@0: int32_t lastIndex = mSeparatorsCount-1; michael@0: michael@0: if (mOpenChar) { michael@0: lastIndex++; michael@0: openIndex = lastIndex; michael@0: } michael@0: if (mCloseChar) { michael@0: lastIndex++; michael@0: closeIndex = lastIndex; michael@0: } michael@0: if (aIndex < 0 || aIndex > lastIndex) { michael@0: return; michael@0: } michael@0: michael@0: if (aIndex < mSeparatorsCount) { michael@0: mSeparatorsChar[aIndex].SetStyleContext(aStyleContext); michael@0: } michael@0: else if (aIndex == openIndex) { michael@0: mOpenChar->SetStyleContext(aStyleContext); michael@0: } michael@0: else if (aIndex == closeIndex) { michael@0: mCloseChar->SetStyleContext(aStyleContext); michael@0: } michael@0: }