1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla MathML Project.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * The University Of Queensland.
19 : * Portions created by the Initial Developer are Copyright (C) 1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Roger B. Sidje <rbs@maths.uq.edu.au>
24 : * David J. Fiddes <D.J.Fiddes@hw.ac.uk>
25 : * Pierre Phaneuf <pp@ludusdesign.com>
26 : * Frederic Wang <fred.wang@free.fr>
27 : * Florian Scholz <elchi3@elchi3.de>
28 : *
29 : * Alternatively, the contents of this file may be used under the terms of
30 : * either of the GNU General Public License Version 2 or later (the "GPL"),
31 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32 : * in which case the provisions of the GPL or the LGPL are applicable instead
33 : * of those above. If you wish to allow use of your version of this file only
34 : * under the terms of either the GPL or the LGPL, and not to allow others to
35 : * use your version of this file under the terms of the MPL, indicate your
36 : * decision by deleting the provisions above and replace them with the notice
37 : * and other provisions required by the GPL or the LGPL. If you do not delete
38 : * the provisions above, a recipient may use your version of this file under
39 : * the terms of any one of the MPL, the GPL or the LGPL.
40 : *
41 : * ***** END LICENSE BLOCK ***** */
42 :
43 :
44 : #include "nsCOMPtr.h"
45 : #include "nsFrame.h"
46 : #include "nsPresContext.h"
47 : #include "nsStyleContext.h"
48 : #include "nsStyleConsts.h"
49 : #include "nsRenderingContext.h"
50 :
51 : #include "nsMathMLmfencedFrame.h"
52 :
53 : //
54 : // <mfenced> -- surround content with a pair of fences
55 : //
56 :
57 : nsIFrame*
58 0 : NS_NewMathMLmfencedFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
59 : {
60 0 : return new (aPresShell) nsMathMLmfencedFrame(aContext);
61 : }
62 :
63 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmfencedFrame)
64 :
65 0 : nsMathMLmfencedFrame::~nsMathMLmfencedFrame()
66 : {
67 0 : RemoveFencesAndSeparators();
68 0 : }
69 :
70 : NS_IMETHODIMP
71 0 : nsMathMLmfencedFrame::InheritAutomaticData(nsIFrame* aParent)
72 : {
73 : // let the base class get the default from our parent
74 0 : nsMathMLContainerFrame::InheritAutomaticData(aParent);
75 :
76 0 : mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
77 :
78 0 : RemoveFencesAndSeparators();
79 0 : CreateFencesAndSeparators(PresContext());
80 :
81 0 : return NS_OK;
82 : }
83 :
84 : NS_IMETHODIMP
85 0 : nsMathMLmfencedFrame::SetInitialChildList(ChildListID aListID,
86 : nsFrameList& aChildList)
87 : {
88 : // First, let the base class do its work
89 0 : nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList);
90 0 : if (NS_FAILED(rv)) return rv;
91 :
92 : // InheritAutomaticData will not get called if our parent is not a mathml
93 : // frame, so initialize NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY for
94 : // GetPreferredStretchSize() from Reflow().
95 0 : mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
96 : // No need to track the style contexts given to our MathML chars.
97 : // The Style System will use Get/SetAdditionalStyleContext() to keep them
98 : // up-to-date if dynamic changes arise.
99 0 : CreateFencesAndSeparators(PresContext());
100 0 : return NS_OK;
101 : }
102 :
103 : NS_IMETHODIMP
104 0 : nsMathMLmfencedFrame::AttributeChanged(PRInt32 aNameSpaceID,
105 : nsIAtom* aAttribute,
106 : PRInt32 aModType)
107 : {
108 0 : RemoveFencesAndSeparators();
109 0 : CreateFencesAndSeparators(PresContext());
110 :
111 : return nsMathMLContainerFrame::
112 0 : AttributeChanged(aNameSpaceID, aAttribute, aModType);
113 : }
114 :
115 : nsresult
116 0 : nsMathMLmfencedFrame::ChildListChanged(PRInt32 aModType)
117 : {
118 0 : RemoveFencesAndSeparators();
119 0 : CreateFencesAndSeparators(PresContext());
120 :
121 0 : return nsMathMLContainerFrame::ChildListChanged(aModType);
122 : }
123 :
124 : void
125 0 : nsMathMLmfencedFrame::RemoveFencesAndSeparators()
126 : {
127 0 : delete mOpenChar;
128 0 : delete mCloseChar;
129 0 : if (mSeparatorsChar) delete[] mSeparatorsChar;
130 :
131 0 : mOpenChar = nsnull;
132 0 : mCloseChar = nsnull;
133 0 : mSeparatorsChar = nsnull;
134 0 : mSeparatorsCount = 0;
135 0 : }
136 :
137 : void
138 0 : nsMathMLmfencedFrame::CreateFencesAndSeparators(nsPresContext* aPresContext)
139 : {
140 0 : nsAutoString value;
141 0 : bool isMutable = false;
142 :
143 : //////////////
144 : // see if the opening fence is there ...
145 0 : if (!GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::open,
146 0 : value)) {
147 0 : value = PRUnichar('('); // default as per the MathML REC
148 : } else {
149 0 : value.CompressWhitespace();
150 : }
151 :
152 0 : if (!value.IsEmpty()) {
153 0 : mOpenChar = new nsMathMLChar;
154 0 : mOpenChar->SetData(aPresContext, value);
155 0 : isMutable = nsMathMLOperators::IsMutableOperator(value);
156 0 : ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, mOpenChar, isMutable);
157 : }
158 :
159 : //////////////
160 : // see if the closing fence is there ...
161 0 : if(!GetAttribute(mContent, mPresentationData.mstyle,
162 0 : nsGkAtoms::close, value)) {
163 0 : value = PRUnichar(')'); // default as per the MathML REC
164 : } else {
165 0 : value.CompressWhitespace();
166 : }
167 :
168 0 : if (!value.IsEmpty()) {
169 0 : mCloseChar = new nsMathMLChar;
170 0 : mCloseChar->SetData(aPresContext, value);
171 0 : isMutable = nsMathMLOperators::IsMutableOperator(value);
172 0 : ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, mCloseChar, isMutable);
173 : }
174 :
175 : //////////////
176 : // see if separators are there ...
177 0 : if (!GetAttribute(mContent, mPresentationData.mstyle,
178 0 : nsGkAtoms::separators_, value)) {
179 0 : value = PRUnichar(','); // default as per the MathML REC
180 : } else {
181 0 : value.StripWhitespace();
182 : }
183 :
184 0 : mSeparatorsCount = value.Length();
185 0 : if (0 < mSeparatorsCount) {
186 0 : PRInt32 sepCount = mFrames.GetLength() - 1;
187 0 : if (0 < sepCount) {
188 0 : mSeparatorsChar = new nsMathMLChar[sepCount];
189 0 : nsAutoString sepChar;
190 0 : for (PRInt32 i = 0; i < sepCount; i++) {
191 0 : if (i < mSeparatorsCount) {
192 0 : sepChar = value[i];
193 0 : isMutable = nsMathMLOperators::IsMutableOperator(sepChar);
194 : }
195 : else {
196 0 : sepChar = value[mSeparatorsCount-1];
197 : // keep the value of isMutable that was set earlier
198 : }
199 0 : mSeparatorsChar[i].SetData(aPresContext, sepChar);
200 0 : ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, &mSeparatorsChar[i], isMutable);
201 : }
202 0 : mSeparatorsCount = sepCount;
203 : } else {
204 : // No separators. Note that sepCount can be -1 here, so don't
205 : // set mSeparatorsCount to it.
206 0 : mSeparatorsCount = 0;
207 : }
208 : }
209 0 : }
210 :
211 : NS_IMETHODIMP
212 0 : nsMathMLmfencedFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
213 : const nsRect& aDirtyRect,
214 : const nsDisplayListSet& aLists)
215 : {
216 : /////////////
217 : // display the content
218 0 : nsresult rv = nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
219 0 : NS_ENSURE_SUCCESS(rv, rv);
220 :
221 : ////////////
222 : // display fences and separators
223 0 : if (mOpenChar) {
224 0 : rv = mOpenChar->Display(aBuilder, this, aLists);
225 0 : NS_ENSURE_SUCCESS(rv, rv);
226 : }
227 :
228 0 : if (mCloseChar) {
229 0 : rv = mCloseChar->Display(aBuilder, this, aLists);
230 0 : NS_ENSURE_SUCCESS(rv, rv);
231 : }
232 :
233 0 : for (PRInt32 i = 0; i < mSeparatorsCount; i++) {
234 0 : rv = mSeparatorsChar[i].Display(aBuilder, this, aLists);
235 0 : NS_ENSURE_SUCCESS(rv, rv);
236 : }
237 0 : return NS_OK;
238 : }
239 :
240 : NS_IMETHODIMP
241 0 : nsMathMLmfencedFrame::Reflow(nsPresContext* aPresContext,
242 : nsHTMLReflowMetrics& aDesiredSize,
243 : const nsHTMLReflowState& aReflowState,
244 : nsReflowStatus& aStatus)
245 : {
246 : nsresult rv;
247 0 : aDesiredSize.width = aDesiredSize.height = 0;
248 0 : aDesiredSize.ascent = 0;
249 0 : aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
250 :
251 : PRInt32 i;
252 0 : const nsStyleFont* font = GetStyleFont();
253 0 : nsRefPtr<nsFontMetrics> fm;
254 0 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
255 0 : aReflowState.rendContext->SetFont(fm);
256 : nscoord axisHeight, em;
257 0 : GetAxisHeight(*aReflowState.rendContext, fm, axisHeight);
258 0 : GetEmHeight(fm, em);
259 : // leading to be left at the top and the bottom of stretched chars
260 0 : nscoord leading = NSToCoordRound(0.2f * em);
261 :
262 : /////////////
263 : // Reflow children
264 : // Asking each child to cache its bounding metrics
265 :
266 : // Note that we don't use the base method nsMathMLContainerFrame::Reflow()
267 : // because we want to stretch our fences, separators and stretchy frames using
268 : // the *same* initial aDesiredSize.mBoundingMetrics. If we were to use the base
269 : // method here, our stretchy frames will be stretched and placed, and we may
270 : // end up stretching our fences/separators with a different aDesiredSize.
271 : // XXX The above decision was revisited in bug 121748 and this code can be
272 : // refactored to use nsMathMLContainerFrame::Reflow() at some stage.
273 :
274 : nsReflowStatus childStatus;
275 0 : nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
276 0 : nsIFrame* firstChild = GetFirstPrincipalChild();
277 0 : nsIFrame* childFrame = firstChild;
278 0 : nscoord ascent = 0, descent = 0;
279 0 : if (firstChild || mOpenChar || mCloseChar || mSeparatorsCount > 0) {
280 : // We use the ASCII metrics to get our minimum height. This way,
281 : // if we have borders or a background, they will fit better with
282 : // other elements on the line.
283 0 : ascent = fm->MaxAscent();
284 0 : descent = fm->MaxDescent();
285 : }
286 0 : while (childFrame) {
287 : nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags
288 0 : | NS_REFLOW_CALC_BOUNDING_METRICS);
289 : nsHTMLReflowState childReflowState(aPresContext, aReflowState,
290 0 : childFrame, availSize);
291 : rv = ReflowChild(childFrame, aPresContext, childDesiredSize,
292 0 : childReflowState, childStatus);
293 : //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
294 0 : if (NS_FAILED(rv)) {
295 : // Call DidReflow() for the child frames we successfully did reflow.
296 0 : DidReflowChildren(firstChild, childFrame);
297 0 : return rv;
298 : }
299 :
300 : SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
301 0 : childDesiredSize.mBoundingMetrics);
302 :
303 0 : nscoord childDescent = childDesiredSize.height - childDesiredSize.ascent;
304 0 : if (descent < childDescent)
305 0 : descent = childDescent;
306 0 : if (ascent < childDesiredSize.ascent)
307 0 : ascent = childDesiredSize.ascent;
308 :
309 0 : childFrame = childFrame->GetNextSibling();
310 : }
311 :
312 : /////////////
313 : // Ask stretchy children to stretch themselves
314 :
315 0 : nsBoundingMetrics containerSize;
316 0 : nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL;
317 :
318 : GetPreferredStretchSize(*aReflowState.rendContext,
319 : 0, /* i.e., without embellishments */
320 0 : stretchDir, containerSize);
321 0 : childFrame = firstChild;
322 0 : while (childFrame) {
323 0 : nsIMathMLFrame* mathmlChild = do_QueryFrame(childFrame);
324 0 : if (mathmlChild) {
325 0 : nsHTMLReflowMetrics childDesiredSize;
326 : // retrieve the metrics that was stored at the previous pass
327 : GetReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
328 0 : childDesiredSize.mBoundingMetrics);
329 :
330 : mathmlChild->Stretch(*aReflowState.rendContext,
331 0 : stretchDir, containerSize, childDesiredSize);
332 : // store the updated metrics
333 : SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
334 0 : childDesiredSize.mBoundingMetrics);
335 :
336 0 : nscoord childDescent = childDesiredSize.height - childDesiredSize.ascent;
337 0 : if (descent < childDescent)
338 0 : descent = childDescent;
339 0 : if (ascent < childDesiredSize.ascent)
340 0 : ascent = childDesiredSize.ascent;
341 : }
342 0 : childFrame = childFrame->GetNextSibling();
343 : }
344 :
345 : // bug 121748: for surrounding fences & separators, use a size that covers everything
346 : GetPreferredStretchSize(*aReflowState.rendContext,
347 : STRETCH_CONSIDER_EMBELLISHMENTS,
348 0 : stretchDir, containerSize);
349 :
350 : //////////////////////////////////////////
351 : // Prepare the opening fence, separators, and closing fence, and
352 : // adjust the origin of children.
353 :
354 : // we need to center around the axis
355 : nscoord delta = NS_MAX(containerSize.ascent - axisHeight,
356 0 : containerSize.descent + axisHeight);
357 0 : containerSize.ascent = delta + axisHeight;
358 0 : containerSize.descent = delta - axisHeight;
359 :
360 0 : bool isRTL = NS_MATHML_IS_RTL(mPresentationData.flags);
361 :
362 : /////////////////
363 : // opening fence ...
364 : ReflowChar(aPresContext, *aReflowState.rendContext, mOpenChar,
365 : NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel,
366 0 : axisHeight, leading, em, containerSize, ascent, descent, isRTL);
367 : /////////////////
368 : // separators ...
369 0 : for (i = 0; i < mSeparatorsCount; i++) {
370 : ReflowChar(aPresContext, *aReflowState.rendContext, &mSeparatorsChar[i],
371 : NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel,
372 0 : axisHeight, leading, em, containerSize, ascent, descent, isRTL);
373 : }
374 : /////////////////
375 : // closing fence ...
376 : ReflowChar(aPresContext, *aReflowState.rendContext, mCloseChar,
377 : NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel,
378 0 : axisHeight, leading, em, containerSize, ascent, descent, isRTL);
379 :
380 : //////////////////
381 : // Adjust the origins of each child.
382 : // and update our bounding metrics
383 :
384 0 : i = 0;
385 0 : nscoord dx = 0;
386 0 : nsBoundingMetrics bm;
387 0 : bool firstTime = true;
388 : nsMathMLChar *leftChar, *rightChar;
389 0 : if (isRTL) {
390 0 : leftChar = mCloseChar;
391 0 : rightChar = mOpenChar;
392 : } else {
393 0 : leftChar = mOpenChar;
394 0 : rightChar = mCloseChar;
395 : }
396 :
397 0 : if (leftChar) {
398 0 : PlaceChar(leftChar, ascent, bm, dx);
399 0 : aDesiredSize.mBoundingMetrics = bm;
400 0 : firstTime = false;
401 : }
402 :
403 0 : if (isRTL) {
404 0 : childFrame = this->GetLastChild(nsIFrame::kPrincipalList);
405 : } else {
406 0 : childFrame = firstChild;
407 : }
408 0 : while (childFrame) {
409 0 : nsHTMLReflowMetrics childSize;
410 0 : GetReflowAndBoundingMetricsFor(childFrame, childSize, bm);
411 0 : if (firstTime) {
412 0 : firstTime = false;
413 0 : aDesiredSize.mBoundingMetrics = bm;
414 : }
415 : else
416 0 : aDesiredSize.mBoundingMetrics += bm;
417 :
418 : FinishReflowChild(childFrame, aPresContext, nsnull, childSize,
419 0 : dx, ascent - childSize.ascent, 0);
420 0 : dx += childSize.width;
421 :
422 0 : if (i < mSeparatorsCount) {
423 : PlaceChar(&mSeparatorsChar[isRTL ? mSeparatorsCount - 1 - i : i],
424 0 : ascent, bm, dx);
425 0 : aDesiredSize.mBoundingMetrics += bm;
426 : }
427 0 : i++;
428 :
429 0 : if (isRTL) {
430 0 : childFrame = childFrame->GetPrevSibling();
431 : } else {
432 0 : childFrame = childFrame->GetNextSibling();
433 : }
434 : }
435 :
436 0 : if (rightChar) {
437 0 : PlaceChar(rightChar, ascent, bm, dx);
438 0 : if (firstTime)
439 0 : aDesiredSize.mBoundingMetrics = bm;
440 : else
441 0 : aDesiredSize.mBoundingMetrics += bm;
442 : }
443 :
444 0 : aDesiredSize.width = aDesiredSize.mBoundingMetrics.width;
445 0 : aDesiredSize.height = ascent + descent;
446 0 : aDesiredSize.ascent = ascent;
447 :
448 0 : SetBoundingMetrics(aDesiredSize.mBoundingMetrics);
449 0 : SetReference(nsPoint(0, aDesiredSize.ascent));
450 :
451 : // see if we should fix the spacing
452 0 : FixInterFrameSpacing(aDesiredSize);
453 :
454 : // Finished with these:
455 0 : ClearSavedChildMetrics();
456 :
457 : // Set our overflow area
458 0 : GatherAndStoreOverflow(&aDesiredSize);
459 :
460 0 : aStatus = NS_FRAME_COMPLETE;
461 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
462 0 : return NS_OK;
463 : }
464 :
465 : static void
466 0 : GetCharSpacing(nsMathMLChar* aMathMLChar,
467 : nsOperatorFlags aForm,
468 : PRInt32 aScriptLevel,
469 : nscoord em,
470 : nscoord& aLeftSpace,
471 : nscoord& aRightSpace)
472 : {
473 0 : nsAutoString data;
474 0 : aMathMLChar->GetData(data);
475 0 : nsOperatorFlags flags = 0;
476 0 : float lspace = 0.0f;
477 0 : float rspace = 0.0f;
478 : bool found = nsMathMLOperators::LookupOperator(data, aForm,
479 0 : &flags, &lspace, &rspace);
480 :
481 : // We don't want extra space when we are a script
482 0 : if (found && aScriptLevel > 0) {
483 0 : lspace /= 2.0f;
484 0 : rspace /= 2.0f;
485 : }
486 :
487 0 : aLeftSpace = NSToCoordRound(lspace * em);
488 0 : aRightSpace = NSToCoordRound(rspace * em);
489 0 : }
490 :
491 : // helper functions to perform the common task of formatting our chars
492 : /*static*/ nsresult
493 0 : nsMathMLmfencedFrame::ReflowChar(nsPresContext* aPresContext,
494 : nsRenderingContext& aRenderingContext,
495 : nsMathMLChar* aMathMLChar,
496 : nsOperatorFlags aForm,
497 : PRInt32 aScriptLevel,
498 : nscoord axisHeight,
499 : nscoord leading,
500 : nscoord em,
501 : nsBoundingMetrics& aContainerSize,
502 : nscoord& aAscent,
503 : nscoord& aDescent,
504 : bool aRTL)
505 : {
506 0 : if (aMathMLChar && 0 < aMathMLChar->Length()) {
507 : nscoord leftSpace;
508 : nscoord rightSpace;
509 0 : GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace);
510 :
511 : // stretch the char to the appropriate height if it is not big enough.
512 0 : nsBoundingMetrics charSize;
513 : nsresult res = aMathMLChar->Stretch(aPresContext, aRenderingContext,
514 : NS_STRETCH_DIRECTION_VERTICAL,
515 : aContainerSize, charSize,
516 0 : NS_STRETCH_NORMAL, aRTL);
517 :
518 0 : if (NS_STRETCH_DIRECTION_UNSUPPORTED != aMathMLChar->GetStretchDirection()) {
519 : // has changed... so center the char around the axis
520 0 : nscoord height = charSize.ascent + charSize.descent;
521 0 : charSize.ascent = height/2 + axisHeight;
522 0 : charSize.descent = height - charSize.ascent;
523 : }
524 : else {
525 : // either it hasn't changed or stretching the char failed (i.e.,
526 : // GetBoundingMetrics failed)
527 0 : leading = 0;
528 0 : if (NS_FAILED(res)) {
529 0 : nsAutoString data;
530 0 : aMathMLChar->GetData(data);
531 : nsBoundingMetrics metrics =
532 0 : aRenderingContext.GetBoundingMetrics(data.get(), data.Length());
533 0 : charSize.ascent = metrics.ascent;
534 0 : charSize.descent = metrics.descent;
535 0 : charSize.width = metrics.width;
536 : // Set this as the bounding metrics of the MathMLChar to leave
537 : // the necessary room to paint the char.
538 0 : aMathMLChar->SetBoundingMetrics(charSize);
539 : }
540 : }
541 :
542 0 : if (aAscent < charSize.ascent + leading)
543 0 : aAscent = charSize.ascent + leading;
544 0 : if (aDescent < charSize.descent + leading)
545 0 : aDescent = charSize.descent + leading;
546 :
547 : // account the spacing
548 0 : charSize.width += leftSpace + rightSpace;
549 :
550 : // x-origin is used to store lspace ...
551 : // y-origin is used to stored the ascent ...
552 : aMathMLChar->SetRect(nsRect(leftSpace,
553 : charSize.ascent, charSize.width,
554 0 : charSize.ascent + charSize.descent));
555 : }
556 0 : return NS_OK;
557 : }
558 :
559 : /*static*/ void
560 0 : nsMathMLmfencedFrame::PlaceChar(nsMathMLChar* aMathMLChar,
561 : nscoord aDesiredAscent,
562 : nsBoundingMetrics& bm,
563 : nscoord& dx)
564 : {
565 0 : aMathMLChar->GetBoundingMetrics(bm);
566 :
567 : // the char's x-origin was used to store lspace ...
568 : // the char's y-origin was used to store the ascent ...
569 : // the char's width was used to store the advance with (with spacing) ...
570 0 : nsRect rect;
571 0 : aMathMLChar->GetRect(rect);
572 :
573 0 : nscoord dy = aDesiredAscent - rect.y;
574 0 : if (aMathMLChar->GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED) {
575 : // the stretchy char will be centered around the axis
576 : // so we adjust the returned bounding metrics accordingly
577 0 : bm.descent = (bm.ascent + bm.descent) - rect.y;
578 0 : bm.ascent = rect.y;
579 : }
580 :
581 0 : aMathMLChar->SetRect(nsRect(dx + rect.x, dy, bm.width, rect.height));
582 :
583 0 : bm.leftBearing += rect.x;
584 0 : bm.rightBearing += rect.x;
585 :
586 : // return rect.width since it includes lspace and rspace
587 0 : bm.width = rect.width;
588 0 : dx += rect.width;
589 0 : }
590 :
591 : static nscoord
592 0 : GetMaxCharWidth(nsPresContext* aPresContext,
593 : nsRenderingContext* aRenderingContext,
594 : nsMathMLChar* aMathMLChar,
595 : nsOperatorFlags aForm,
596 : PRInt32 aScriptLevel,
597 : nscoord em)
598 : {
599 0 : nscoord width = aMathMLChar->GetMaxWidth(aPresContext, *aRenderingContext);
600 :
601 0 : if (0 < aMathMLChar->Length()) {
602 : nscoord leftSpace;
603 : nscoord rightSpace;
604 0 : GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace);
605 :
606 0 : width += leftSpace + rightSpace;
607 : }
608 :
609 0 : return width;
610 : }
611 :
612 : /* virtual */ nscoord
613 0 : nsMathMLmfencedFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext)
614 : {
615 0 : nscoord width = 0;
616 :
617 0 : nsPresContext* presContext = PresContext();
618 0 : const nsStyleFont* font = GetStyleFont();
619 0 : nsRefPtr<nsFontMetrics> fm;
620 0 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
621 : nscoord em;
622 0 : GetEmHeight(fm, em);
623 :
624 0 : if (mOpenChar) {
625 : width +=
626 : GetMaxCharWidth(presContext, aRenderingContext, mOpenChar,
627 0 : NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, em);
628 : }
629 :
630 0 : PRInt32 i = 0;
631 0 : nsIFrame* childFrame = GetFirstPrincipalChild();
632 0 : while (childFrame) {
633 : // XXX This includes margin while Reflow currently doesn't consider
634 : // margin, so we may end up with too much space, but, with stretchy
635 : // characters, this is an approximation anyway.
636 : width += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
637 0 : nsLayoutUtils::PREF_WIDTH);
638 :
639 0 : if (i < mSeparatorsCount) {
640 : width +=
641 : GetMaxCharWidth(presContext, aRenderingContext, &mSeparatorsChar[i],
642 0 : NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel, em);
643 : }
644 0 : i++;
645 :
646 0 : childFrame = childFrame->GetNextSibling();
647 : }
648 :
649 0 : if (mCloseChar) {
650 : width +=
651 : GetMaxCharWidth(presContext, aRenderingContext, mCloseChar,
652 0 : NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, em);
653 : }
654 :
655 0 : return width;
656 : }
657 :
658 : nscoord
659 0 : nsMathMLmfencedFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
660 : {
661 0 : nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
662 0 : if (!gap) return 0;
663 :
664 0 : nsRect rect;
665 0 : if (mOpenChar) {
666 0 : mOpenChar->GetRect(rect);
667 0 : rect.MoveBy(gap, 0);
668 0 : mOpenChar->SetRect(rect);
669 : }
670 0 : if (mCloseChar) {
671 0 : mCloseChar->GetRect(rect);
672 0 : rect.MoveBy(gap, 0);
673 0 : mCloseChar->SetRect(rect);
674 : }
675 0 : for (PRInt32 i = 0; i < mSeparatorsCount; i++) {
676 0 : mSeparatorsChar[i].GetRect(rect);
677 0 : rect.MoveBy(gap, 0);
678 0 : mSeparatorsChar[i].SetRect(rect);
679 : }
680 0 : return gap;
681 : }
682 :
683 : // ----------------------
684 : // the Style System will use these to pass the proper style context to our MathMLChar
685 : nsStyleContext*
686 0 : nsMathMLmfencedFrame::GetAdditionalStyleContext(PRInt32 aIndex) const
687 : {
688 0 : PRInt32 openIndex = -1;
689 0 : PRInt32 closeIndex = -1;
690 0 : PRInt32 lastIndex = mSeparatorsCount-1;
691 :
692 0 : if (mOpenChar) {
693 0 : lastIndex++;
694 0 : openIndex = lastIndex;
695 : }
696 0 : if (mCloseChar) {
697 0 : lastIndex++;
698 0 : closeIndex = lastIndex;
699 : }
700 0 : if (aIndex < 0 || aIndex > lastIndex) {
701 0 : return nsnull;
702 : }
703 :
704 0 : if (aIndex < mSeparatorsCount) {
705 0 : return mSeparatorsChar[aIndex].GetStyleContext();
706 : }
707 0 : else if (aIndex == openIndex) {
708 0 : return mOpenChar->GetStyleContext();
709 : }
710 0 : else if (aIndex == closeIndex) {
711 0 : return mCloseChar->GetStyleContext();
712 : }
713 0 : return nsnull;
714 : }
715 :
716 : void
717 0 : nsMathMLmfencedFrame::SetAdditionalStyleContext(PRInt32 aIndex,
718 : nsStyleContext* aStyleContext)
719 : {
720 0 : PRInt32 openIndex = -1;
721 0 : PRInt32 closeIndex = -1;
722 0 : PRInt32 lastIndex = mSeparatorsCount-1;
723 :
724 0 : if (mOpenChar) {
725 0 : lastIndex++;
726 0 : openIndex = lastIndex;
727 : }
728 0 : if (mCloseChar) {
729 0 : lastIndex++;
730 0 : closeIndex = lastIndex;
731 : }
732 0 : if (aIndex < 0 || aIndex > lastIndex) {
733 0 : return;
734 : }
735 :
736 0 : if (aIndex < mSeparatorsCount) {
737 0 : mSeparatorsChar[aIndex].SetStyleContext(aStyleContext);
738 : }
739 0 : else if (aIndex == openIndex) {
740 0 : mOpenChar->SetStyleContext(aStyleContext);
741 : }
742 0 : else if (aIndex == closeIndex) {
743 0 : mCloseChar->SetStyleContext(aStyleContext);
744 : }
745 : }
|