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
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/
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
15 * The Original Code is Mozilla MathML Project.
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.
23 * Roger B. Sidje <rbs@maths.uq.edu.au>
24 * David J. Fiddes <D.J.Fiddes@hw.ac.uk>
25 * Shyjan Mahamud <mahamud@cs.cmu.edu>
26 * Pierre Phaneuf <pp@ludusdesign.com>
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
40 * ***** END LICENSE BLOCK ***** */
45 #include "nsPresContext.h"
46 #include "nsStyleContext.h"
47 #include "nsStyleConsts.h"
48 #include "nsINameSpaceManager.h"
49 #include "nsIRenderingContext.h"
50 #include "nsIFontMetrics.h"
52 #include "nsMathMLmunderoverFrame.h"
53 #include "nsMathMLmsubsupFrame.h"
56 // <munderover> -- attach an underscript-overscript pair to a base - implementation
60 NS_NewMathMLmunderoverFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
62 return new (aPresShell
) nsMathMLmunderoverFrame(aContext
);
65 nsMathMLmunderoverFrame::~nsMathMLmunderoverFrame()
70 nsMathMLmunderoverFrame::AttributeChanged(PRInt32 aNameSpaceID
,
74 if (nsGkAtoms::accent_
== aAttribute
||
75 nsGkAtoms::accentunder_
== aAttribute
) {
76 // When we have automatic data to update within ourselves, we ask our
77 // parent to re-layout its children
78 return ReLayoutChildren(mParent
, NS_FRAME_IS_DIRTY
);
81 return nsMathMLContainerFrame::
82 AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
86 nsMathMLmunderoverFrame::UpdatePresentationData(PRUint32 aFlagsValues
,
87 PRUint32 aFlagsToUpdate
)
89 nsMathMLContainerFrame::UpdatePresentationData(aFlagsValues
, aFlagsToUpdate
);
90 // disable the stretch-all flag if we are going to act like a subscript-superscript pair
91 if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData
.flags
) &&
92 !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
)) {
93 mPresentationData
.flags
&= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY
;
96 mPresentationData
.flags
|= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY
;
102 nsMathMLmunderoverFrame::UpdatePresentationDataFromChildAt(PRInt32 aFirstIndex
,
104 PRUint32 aFlagsValues
,
105 PRUint32 aFlagsToUpdate
)
107 // munderover is special... The REC says:
108 // Within underscript, <munder> always sets displaystyle to "false",
109 // but increments scriptlevel by 1 only when accentunder is "false".
110 // Within underscript, <munderover> always sets displaystyle to "false",
111 // but increments scriptlevel by 1 only when accentunder is "false".
113 // 1. don't allow displaystyle to change in the underscript & overscript
114 // 2a if the value of the accent is changed, we need to recompute the
115 // scriptlevel of the underscript. The problem is that the accent
116 // can change in the <mo> deep down the embellished hierarchy
117 // 2b if the value of the accent is changed, we need to recompute the
118 // scriptlevel of the overscript. The problem is that the accent
119 // can change in the <mo> deep down the embellished hierarchy
121 // Do #1 here, prevent displaystyle to be changed in the underscript & overscript
123 nsIFrame
* childFrame
= mFrames
.FirstChild();
125 if ((index
>= aFirstIndex
) &&
126 ((aLastIndex
<= 0) || ((aLastIndex
> 0) && (index
<= aLastIndex
)))) {
129 aFlagsToUpdate
&= ~NS_MATHML_DISPLAYSTYLE
;
130 aFlagsValues
&= ~NS_MATHML_DISPLAYSTYLE
;
132 PropagatePresentationDataFor(childFrame
, aFlagsValues
, aFlagsToUpdate
);
135 childFrame
= childFrame
->GetNextSibling();
139 // For #2, changing the accent attribute will trigger a re-build of
140 // all automatic data in the embellished hierarchy
144 nsMathMLmunderoverFrame::InheritAutomaticData(nsIFrame
* aParent
)
146 // let the base class get the default from our parent
147 nsMathMLContainerFrame::InheritAutomaticData(aParent
);
149 mPresentationData
.flags
|= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY
;
155 nsMathMLmunderoverFrame::TransmitAutomaticData()
157 // At this stage, all our children are in sync and we can fully
158 // resolve our own mEmbellishData struct
159 //---------------------------------------------------------------------
164 The accent and accentunder attributes have the same effect as
165 the attributes with the same names on <mover> and <munder>,
166 respectively. Their default values are also computed in the
167 same manner as described for those elements, with the default
168 value of accent depending on overscript and the default value
169 of accentunder depending on underscript.
172 nsIFrame
* overscriptFrame
= nsnull
;
173 nsIFrame
* underscriptFrame
= nsnull
;
174 nsIFrame
* baseFrame
= mFrames
.FirstChild();
176 underscriptFrame
= baseFrame
->GetNextSibling();
177 if (underscriptFrame
)
178 overscriptFrame
= underscriptFrame
->GetNextSibling();
180 // if our base is an embellished operator, let its state bubble to us (in particular,
181 // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags
182 // are reset to the default values of false if the base frame isn't embellished.
183 mPresentationData
.baseFrame
= baseFrame
;
184 GetEmbellishDataFrom(baseFrame
, mEmbellishData
);
186 // The default value of accentunder is false, unless the underscript is embellished
187 // and its core <mo> is an accent
188 nsEmbellishData embellishData
;
189 GetEmbellishDataFrom(underscriptFrame
, embellishData
);
190 if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData
.flags
))
191 mEmbellishData
.flags
|= NS_MATHML_EMBELLISH_ACCENTUNDER
;
193 mEmbellishData
.flags
&= ~NS_MATHML_EMBELLISH_ACCENTUNDER
;
195 static nsIContent::AttrValuesArray strings
[] =
196 {&nsGkAtoms::_true
, &nsGkAtoms::_false
, nsnull
};
198 // if we have an accentunder attribute, it overrides what the underscript said
199 switch (mContent
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::accentunder_
,
200 strings
, eCaseMatters
)) {
201 case 0: mEmbellishData
.flags
|= NS_MATHML_EMBELLISH_ACCENTUNDER
; break;
202 case 1: mEmbellishData
.flags
&= ~NS_MATHML_EMBELLISH_ACCENTUNDER
; break;
205 // The default value of accent is false, unless the overscript is embellished
206 // and its core <mo> is an accent
207 GetEmbellishDataFrom(overscriptFrame
, embellishData
);
208 if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData
.flags
))
209 mEmbellishData
.flags
|= NS_MATHML_EMBELLISH_ACCENTOVER
;
211 mEmbellishData
.flags
&= ~NS_MATHML_EMBELLISH_ACCENTOVER
;
213 // if we have an accent attribute, it overrides what the overscript said
214 switch (mContent
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::accent_
,
215 strings
, eCaseMatters
)) {
216 case 0: mEmbellishData
.flags
|= NS_MATHML_EMBELLISH_ACCENTOVER
; break;
217 case 1: mEmbellishData
.flags
&= ~NS_MATHML_EMBELLISH_ACCENTOVER
; break;
220 // disable the stretch-all flag if we are going to act like a superscript
221 if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData
.flags
) &&
222 !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
))
223 mPresentationData
.flags
&= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY
;
225 // Now transmit any change that we want to our children so that they
226 // can update their mPresentationData structs
227 //---------------------------------------------------------------------
230 Within underscript, <munderover> always sets displaystyle to "false",
231 but increments scriptlevel by 1 only when accentunder is "false".
233 Within overscript, <munderover> always sets displaystyle to "false",
234 but increments scriptlevel by 1 only when accent is "false".
236 The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a
237 say it shouldn't be compressed. However, The TeXBook says
238 that math accents and \overline change uncramped styles to their
239 cramped counterparts.
241 PRUint32 compress
= NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData
.flags
)
242 ? NS_MATHML_COMPRESSED
: 0;
243 SetIncrementScriptLevel(2, !NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData
.flags
));
244 PropagatePresentationDataFor(overscriptFrame
,
245 ~NS_MATHML_DISPLAYSTYLE
| compress
,
246 NS_MATHML_DISPLAYSTYLE
| compress
);
249 The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a
250 say it should be compressed
252 SetIncrementScriptLevel(1, !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData
.flags
));
253 PropagatePresentationDataFor(underscriptFrame
,
254 ~NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
,
255 NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
);
262 * If the base is an operator with movablelimits="true" (or an embellished
263 operator whose <mo> element core has movablelimits="true"), and
264 displaystyle="false", then underscript and overscript are drawn in
265 a subscript and superscript position, respectively. In this case,
266 the accent and accentunder attributes are ignored. This is often
267 used for limits on symbols such as ∑.
270 if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishDataflags) &&
271 !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
272 // place like subscript-superscript pair
275 // place like underscript-overscript pair
279 /* virtual */ nsresult
280 nsMathMLmunderoverFrame::Place(nsIRenderingContext
& aRenderingContext
,
282 nsHTMLReflowMetrics
& aDesiredSize
)
284 if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData
.flags
) &&
285 !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
)) {
286 // place like sub-superscript pair
287 return nsMathMLmsubsupFrame::PlaceSubSupScript(PresContext(),
291 this, 0, 0, PresContext()->PointsToAppUnits(0.5f
));
294 ////////////////////////////////////
295 // Get the children's desired sizes
297 nsBoundingMetrics bmBase
, bmUnder
, bmOver
;
298 nsHTMLReflowMetrics baseSize
;
299 nsHTMLReflowMetrics underSize
;
300 nsHTMLReflowMetrics overSize
;
301 nsIFrame
* overFrame
= nsnull
;
302 nsIFrame
* underFrame
= nsnull
;
303 nsIFrame
* baseFrame
= mFrames
.FirstChild();
305 underFrame
= baseFrame
->GetNextSibling();
307 overFrame
= underFrame
->GetNextSibling();
308 if (!baseFrame
|| !underFrame
|| !overFrame
|| overFrame
->GetNextSibling()) {
309 // report an error, encourage people to get their markups in order
310 return ReflowError(aRenderingContext
, aDesiredSize
);
312 GetReflowAndBoundingMetricsFor(baseFrame
, baseSize
, bmBase
);
313 GetReflowAndBoundingMetricsFor(underFrame
, underSize
, bmUnder
);
314 GetReflowAndBoundingMetricsFor(overFrame
, overSize
, bmOver
);
316 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
321 aRenderingContext
.SetFont(GetStyleFont()->mFont
, nsnull
,
322 PresContext()->GetUserFontSet());
323 nsCOMPtr
<nsIFontMetrics
> fm
;
324 aRenderingContext
.GetFontMetrics(*getter_AddRefs(fm
));
327 fm
->GetXHeight (xHeight
);
329 nscoord ruleThickness
;
330 GetRuleThickness (aRenderingContext
, fm
, ruleThickness
);
332 nscoord correction
= 0;
333 GetItalicCorrection (bmBase
, correction
);
335 // there are 2 different types of placement depending on
336 // whether we want an accented under or not
338 nscoord underDelta1
= 0; // gap between base and underscript
339 nscoord underDelta2
= 0; // extra space beneath underscript
341 if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData
.flags
)) {
342 // Rule 13a, App. G, TeXbook
343 nscoord bigOpSpacing2
, bigOpSpacing4
, bigOpSpacing5
, dummy
;
344 GetBigOpSpacings (fm
,
345 dummy
, bigOpSpacing2
,
346 dummy
, bigOpSpacing4
,
348 underDelta1
= PR_MAX(bigOpSpacing2
, (bigOpSpacing4
- bmUnder
.ascent
));
349 underDelta2
= bigOpSpacing5
;
352 // No corresponding rule in TeXbook - we are on our own here
353 // XXX tune the gap delta between base and underscript
355 // Should we use Rule 10 like \underline does?
356 underDelta1
= ruleThickness
+ onePixel
/2;
357 underDelta2
= ruleThickness
;
360 if (!(bmUnder
.ascent
+ bmUnder
.descent
)) underDelta1
= 0;
362 nscoord overDelta1
= 0; // gap between base and overscript
363 nscoord overDelta2
= 0; // extra space above overscript
365 if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData
.flags
)) {
366 // Rule 13a, App. G, TeXbook
367 nscoord bigOpSpacing1
, bigOpSpacing3
, bigOpSpacing5
, dummy
;
368 GetBigOpSpacings (fm
,
369 bigOpSpacing1
, dummy
,
370 bigOpSpacing3
, dummy
,
372 overDelta1
= PR_MAX(bigOpSpacing1
, (bigOpSpacing3
- bmOver
.descent
));
373 overDelta2
= bigOpSpacing5
;
375 // XXX This is not a TeX rule...
376 // delta1 (as computed abvove) can become really big when bmOver.descent is
377 // negative, e.g., if the content is &OverBar. In such case, we use the height
378 if (bmOver
.descent
< 0)
379 overDelta1
= PR_MAX(bigOpSpacing1
, (bigOpSpacing3
- (bmOver
.ascent
+ bmOver
.descent
)));
382 // Rule 12, App. G, TeXbook
383 overDelta1
= ruleThickness
+ onePixel
/2;
384 if (bmBase
.ascent
< xHeight
) {
385 overDelta1
+= xHeight
- bmBase
.ascent
;
387 overDelta2
= ruleThickness
;
390 if (!(bmOver
.ascent
+ bmOver
.descent
)) overDelta1
= 0;
392 nscoord dxBase
, dxOver
= 0, dxUnder
= 0;
395 // pass 1, do what <mover> does: attach the overscript on the base
397 // Ad-hoc - This is to override fonts which have ready-made _accent_
398 // glyphs with negative lbearing and rbearing. We want to position
399 // the overscript ourselves
400 nscoord overWidth
= bmOver
.width
;
401 if (!overWidth
&& (bmOver
.rightBearing
- bmOver
.leftBearing
> 0)) {
402 overWidth
= bmOver
.rightBearing
- bmOver
.leftBearing
;
403 dxOver
= -bmOver
.leftBearing
;
406 if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData
.flags
)) {
407 mBoundingMetrics
.width
= bmBase
.width
;
408 dxOver
+= correction
+ (mBoundingMetrics
.width
- overWidth
)/2;
411 mBoundingMetrics
.width
= PR_MAX(bmBase
.width
, overWidth
);
412 dxOver
+= correction
/2 + (mBoundingMetrics
.width
- overWidth
)/2;
414 dxBase
= (mBoundingMetrics
.width
- bmBase
.width
)/2;
416 mBoundingMetrics
.ascent
=
417 bmBase
.ascent
+ overDelta1
+ bmOver
.ascent
+ bmOver
.descent
;
418 mBoundingMetrics
.descent
=
419 bmBase
.descent
+ underDelta1
+ bmUnder
.ascent
+ bmUnder
.descent
;
420 mBoundingMetrics
.leftBearing
=
421 PR_MIN(dxBase
+ bmBase
.leftBearing
, dxOver
+ bmOver
.leftBearing
);
422 mBoundingMetrics
.rightBearing
=
423 PR_MAX(dxBase
+ bmBase
.rightBearing
, dxOver
+ bmOver
.rightBearing
);
426 // pass 2, do what <munder> does: attach the underscript on the previous
427 // result. We conceptually view the previous result as an "anynomous base"
428 // from where to attach the underscript. Hence if the underscript is empty,
429 // we should end up like <mover>. If the overscript is empty, we should
430 // end up like <munder>.
432 nsBoundingMetrics bmAnonymousBase
= mBoundingMetrics
;
433 nscoord ascentAnonymousBase
=
434 PR_MAX(mBoundingMetrics
.ascent
+ overDelta2
,
435 overSize
.ascent
+ bmOver
.descent
+ overDelta1
+ bmBase
.ascent
);
437 GetItalicCorrection(bmAnonymousBase
, correction
);
439 // Width of non-spacing marks is zero so use left and right bearing.
440 nscoord underWidth
= bmUnder
.width
;
442 underWidth
= bmUnder
.rightBearing
- bmUnder
.leftBearing
;
443 dxUnder
= -bmUnder
.leftBearing
;
446 nscoord maxWidth
= PR_MAX(bmAnonymousBase
.width
, underWidth
);
447 if (NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData
.flags
)) {
448 dxUnder
+= (maxWidth
- underWidth
)/2;;
451 dxUnder
+= -correction
/2 + (maxWidth
- underWidth
)/2;
453 nscoord dxAnonymousBase
= (maxWidth
- bmAnonymousBase
.width
)/2;
455 // adjust the offsets of the real base and overscript since their
456 // final offsets should be relative to us...
457 dxOver
+= dxAnonymousBase
;
458 dxBase
+= dxAnonymousBase
;
460 mBoundingMetrics
.width
=
461 PR_MAX(dxAnonymousBase
+ bmAnonymousBase
.width
, dxUnder
+ bmUnder
.width
);
462 mBoundingMetrics
.leftBearing
=
463 PR_MIN(dxAnonymousBase
+ bmAnonymousBase
.leftBearing
, dxUnder
+ bmUnder
.leftBearing
);
464 mBoundingMetrics
.rightBearing
=
465 PR_MAX(dxAnonymousBase
+ bmAnonymousBase
.rightBearing
, dxUnder
+ bmUnder
.rightBearing
);
467 aDesiredSize
.ascent
= ascentAnonymousBase
;
468 aDesiredSize
.height
= aDesiredSize
.ascent
+
469 PR_MAX(mBoundingMetrics
.descent
+ underDelta2
,
470 bmAnonymousBase
.descent
+ underDelta1
+ bmUnder
.ascent
+
471 underSize
.height
- underSize
.ascent
);
472 aDesiredSize
.width
= mBoundingMetrics
.width
;
473 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
476 mReference
.y
= aDesiredSize
.ascent
;
481 dy
= aDesiredSize
.ascent
- mBoundingMetrics
.ascent
+ bmOver
.ascent
- overSize
.ascent
;
482 FinishReflowChild (overFrame
, PresContext(), nsnull
, overSize
, dxOver
, dy
, 0);
484 dy
= aDesiredSize
.ascent
- baseSize
.ascent
;
485 FinishReflowChild (baseFrame
, PresContext(), nsnull
, baseSize
, dxBase
, dy
, 0);
487 dy
= aDesiredSize
.ascent
+ mBoundingMetrics
.descent
- bmUnder
.descent
- underSize
.ascent
;
488 FinishReflowChild (underFrame
, PresContext(), nsnull
, underSize
, dxUnder
, dy
, 0);