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 "nsMathMLmunderFrame.h"
53 #include "nsMathMLmsubFrame.h"
56 // <munder> -- attach an underscript to a base - implementation
60 NS_NewMathMLmunderFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
62 return new (aPresShell
) nsMathMLmunderFrame(aContext
);
65 nsMathMLmunderFrame::~nsMathMLmunderFrame()
70 nsMathMLmunderFrame::AttributeChanged(PRInt32 aNameSpaceID
,
74 if (nsGkAtoms::accentunder_
== aAttribute
) {
75 // When we have automatic data to update within ourselves, we ask our
76 // parent to re-layout its children
77 return ReLayoutChildren(mParent
, NS_FRAME_IS_DIRTY
);
80 return nsMathMLContainerFrame::
81 AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
85 nsMathMLmunderFrame::UpdatePresentationData(PRUint32 aFlagsValues
,
86 PRUint32 aFlagsToUpdate
)
88 nsMathMLContainerFrame::UpdatePresentationData(aFlagsValues
, aFlagsToUpdate
);
89 // disable the stretch-all flag if we are going to act like a subscript
90 if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData
.flags
) &&
91 !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
)) {
92 mPresentationData
.flags
&= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY
;
95 mPresentationData
.flags
|= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY
;
101 nsMathMLmunderFrame::UpdatePresentationDataFromChildAt(PRInt32 aFirstIndex
,
103 PRUint32 aFlagsValues
,
104 PRUint32 aFlagsToUpdate
)
106 // munder is special... The REC says:
107 // Within underscript, <munder> always sets displaystyle to "false",
108 // but increments scriptlevel by 1 only when accentunder is "false".
110 // 1. don't allow displaystyle to change in the underscript
111 // 2. if the value of the accent is changed, we need to recompute the
112 // scriptlevel of the underscript. The problem is that the accent
113 // can change in the <mo> deep down the embellished hierarchy
115 // Do #1 here, never allow displaystyle to be changed in the underscript
117 nsIFrame
* childFrame
= mFrames
.FirstChild();
119 if ((index
>= aFirstIndex
) &&
120 ((aLastIndex
<= 0) || ((aLastIndex
> 0) && (index
<= aLastIndex
)))) {
123 aFlagsToUpdate
&= ~NS_MATHML_DISPLAYSTYLE
;
124 aFlagsValues
&= ~NS_MATHML_DISPLAYSTYLE
;
126 PropagatePresentationDataFor(childFrame
, aFlagsValues
, aFlagsToUpdate
);
129 childFrame
= childFrame
->GetNextSibling();
133 // For #2, changing the accent attribute will trigger a re-build of
134 // all automatic data in the embellished hierarchy
138 nsMathMLmunderFrame::InheritAutomaticData(nsIFrame
* aParent
)
140 // let the base class get the default from our parent
141 nsMathMLContainerFrame::InheritAutomaticData(aParent
);
143 mPresentationData
.flags
|= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY
;
149 nsMathMLmunderFrame::TransmitAutomaticData()
151 // At this stage, all our children are in sync and we can fully
152 // resolve our own mEmbellishData struct
153 //---------------------------------------------------------------------
156 The default value of accentunder is false, unless underscript
157 is an <mo> element or an embellished operator. If underscript is
158 an <mo> element, the value of its accent attribute is used as the
159 default value of accentunder. If underscript is an embellished
160 operator, the accent attribute of the <mo> element at its
161 core is used as the default value. As with all attributes, an
162 explicitly given value overrides the default.
164 XXX The winner is the outermost setting in conflicting settings like these:
165 <munder accent='true'>
167 <mo accent='false'> ... </mo>
171 nsIFrame
* underscriptFrame
= nsnull
;
172 nsIFrame
* baseFrame
= mFrames
.FirstChild();
174 underscriptFrame
= baseFrame
->GetNextSibling();
176 // if our base is an embellished operator, let its state bubble to us (in particular,
177 // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags
178 // are reset to the default values of false if the base frame isn't embellished.
179 mPresentationData
.baseFrame
= baseFrame
;
180 GetEmbellishDataFrom(baseFrame
, mEmbellishData
);
182 // The default value of accentunder is false, unless the underscript is embellished
183 // and its core <mo> is an accent
184 nsEmbellishData embellishData
;
185 GetEmbellishDataFrom(underscriptFrame
, embellishData
);
186 if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData
.flags
))
187 mEmbellishData
.flags
|= NS_MATHML_EMBELLISH_ACCENTUNDER
;
189 mEmbellishData
.flags
&= ~NS_MATHML_EMBELLISH_ACCENTUNDER
;
191 // if we have an accentunder attribute, it overrides what the underscript said
192 static nsIContent::AttrValuesArray strings
[] =
193 {&nsGkAtoms::_true
, &nsGkAtoms::_false
, nsnull
};
194 switch (mContent
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::accentunder_
,
195 strings
, eCaseMatters
)) {
196 case 0: mEmbellishData
.flags
|= NS_MATHML_EMBELLISH_ACCENTUNDER
; break;
197 case 1: mEmbellishData
.flags
&= ~NS_MATHML_EMBELLISH_ACCENTUNDER
; break;
200 // disable the stretch-all flag if we are going to act like a superscript
201 if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData
.flags
) &&
202 !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
))
203 mPresentationData
.flags
&= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY
;
205 // Now transmit any change that we want to our children so that they
206 // can update their mPresentationData structs
207 //---------------------------------------------------------------------
210 Within underscript, <munder> always sets displaystyle to "false",
211 but increments scriptlevel by 1 only when accentunder is "false".
213 The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a
214 say it should be compressed
216 SetIncrementScriptLevel(1, !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData
.flags
));
217 PropagatePresentationDataFor(underscriptFrame
,
218 ~NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
,
219 NS_MATHML_DISPLAYSTYLE
| NS_MATHML_COMPRESSED
);
226 * If the base is an operator with movablelimits="true" (or
227 an embellished operator whose <mo> element core has
228 movablelimits="true"), and displaystyle="false", then
229 underscript is drawn in a subscript position. In this case,
230 the accentunder attribute is ignored. This is often used
231 for limits on symbols such as ∑.
234 if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
235 !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
236 // place like subscript
239 // place like underscript
243 /* virtual */ nsresult
244 nsMathMLmunderFrame::Place(nsIRenderingContext
& aRenderingContext
,
246 nsHTMLReflowMetrics
& aDesiredSize
)
248 if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData
.flags
) &&
249 !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData
.flags
)) {
250 // place like subscript
251 return nsMathMLmsubFrame::PlaceSubScript(PresContext(),
255 this, 0, PresContext()->PointsToAppUnits(0.5f
));
258 ////////////////////////////////////
259 // Get the children's desired sizes
261 nsBoundingMetrics bmBase
, bmUnder
;
262 nsHTMLReflowMetrics baseSize
;
263 nsHTMLReflowMetrics underSize
;
264 nsIFrame
* underFrame
= nsnull
;
265 nsIFrame
* baseFrame
= mFrames
.FirstChild();
267 underFrame
= baseFrame
->GetNextSibling();
268 if (!baseFrame
|| !underFrame
|| underFrame
->GetNextSibling()) {
269 // report an error, encourage people to get their markups in order
270 return ReflowError(aRenderingContext
, aDesiredSize
);
272 GetReflowAndBoundingMetricsFor(baseFrame
, baseSize
, bmBase
);
273 GetReflowAndBoundingMetricsFor(underFrame
, underSize
, bmUnder
);
275 nscoord onePixel
= nsPresContext::CSSPixelsToAppUnits(1);
280 aRenderingContext
.SetFont(GetStyleFont()->mFont
, nsnull
,
281 PresContext()->GetUserFontSet());
282 nsCOMPtr
<nsIFontMetrics
> fm
;
283 aRenderingContext
.GetFontMetrics(*getter_AddRefs(fm
));
286 fm
->GetXHeight (xHeight
);
288 nscoord ruleThickness
;
289 GetRuleThickness (aRenderingContext
, fm
, ruleThickness
);
291 // there are 2 different types of placement depending on
292 // whether we want an accented under or not
294 nscoord correction
= 0;
295 GetItalicCorrection (bmBase
, correction
);
297 nscoord delta1
= 0; // gap between base and underscript
298 nscoord delta2
= 0; // extra space beneath underscript
299 if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData
.flags
)) {
300 // Rule 13a, App. G, TeXbook
301 nscoord bigOpSpacing2
, bigOpSpacing4
, bigOpSpacing5
, dummy
;
302 GetBigOpSpacings (fm
,
303 dummy
, bigOpSpacing2
,
304 dummy
, bigOpSpacing4
,
306 delta1
= PR_MAX(bigOpSpacing2
, (bigOpSpacing4
- bmUnder
.ascent
));
307 delta2
= bigOpSpacing5
;
310 // No corresponding rule in TeXbook - we are on our own here
311 // XXX tune the gap delta between base and underscript
313 // Should we use Rule 10 like \underline does?
314 delta1
= ruleThickness
+ onePixel
/2;
315 delta2
= ruleThickness
;
318 if (!(bmUnder
.ascent
+ bmUnder
.descent
)) delta1
= 0;
320 nscoord dxBase
, dxUnder
= 0;
322 // Width of non-spacing marks is zero so use left and right bearing.
323 nscoord underWidth
= bmUnder
.width
;
325 underWidth
= bmUnder
.rightBearing
- bmUnder
.leftBearing
;
326 dxUnder
= -bmUnder
.leftBearing
;
329 nscoord maxWidth
= PR_MAX(bmBase
.width
, underWidth
);
330 if (NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData
.flags
)) {
331 dxUnder
+= (maxWidth
- underWidth
)/2;
334 dxUnder
+= -correction
/2 + (maxWidth
- underWidth
)/2;
336 dxBase
= (maxWidth
- bmBase
.width
)/2;
338 mBoundingMetrics
.width
=
339 PR_MAX(dxBase
+ bmBase
.width
, dxUnder
+ bmUnder
.width
);
340 mBoundingMetrics
.ascent
= bmBase
.ascent
;
341 mBoundingMetrics
.descent
=
342 bmBase
.descent
+ delta1
+ bmUnder
.ascent
+ bmUnder
.descent
;
343 mBoundingMetrics
.leftBearing
=
344 PR_MIN(dxBase
+ bmBase
.leftBearing
, dxUnder
+ bmUnder
.leftBearing
);
345 mBoundingMetrics
.rightBearing
=
346 PR_MAX(dxBase
+ bmBase
.rightBearing
, dxUnder
+ bmUnder
.rightBearing
);
348 aDesiredSize
.ascent
= baseSize
.ascent
;
349 aDesiredSize
.height
= aDesiredSize
.ascent
+
350 PR_MAX(mBoundingMetrics
.descent
+ delta2
,
351 bmBase
.descent
+ delta1
+ bmUnder
.ascent
+
352 underSize
.height
- underSize
.ascent
);
353 aDesiredSize
.width
= mBoundingMetrics
.width
;
354 aDesiredSize
.mBoundingMetrics
= mBoundingMetrics
;
357 mReference
.y
= aDesiredSize
.ascent
;
362 FinishReflowChild(baseFrame
, PresContext(), nsnull
, baseSize
, dxBase
, dy
, 0);
364 dy
= aDesiredSize
.ascent
+ mBoundingMetrics
.descent
- bmUnder
.descent
- underSize
.ascent
;
365 FinishReflowChild(underFrame
, PresContext(), nsnull
, underSize
, dxUnder
, dy
, 0);