Back to index

lightning-sunbird  0.9+nobinonly
nsMathMLmoverFrame.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla MathML Project.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * The University Of Queensland.
00018  * Portions created by the Initial Developer are Copyright (C) 1999
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Roger B. Sidje <rbs@maths.uq.edu.au>
00023  *   David J. Fiddes <D.J.Fiddes@hw.ac.uk>
00024  *   Shyjan Mahamud <mahamud@cs.cmu.edu>
00025  *   Pierre Phaneuf <pp@ludusdesign.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 
00042 #include "nsCOMPtr.h"
00043 #include "nsFrame.h"
00044 #include "nsPresContext.h"
00045 #include "nsUnitConversion.h"
00046 #include "nsStyleContext.h"
00047 #include "nsStyleConsts.h"
00048 #include "nsINameSpaceManager.h"
00049 #include "nsIRenderingContext.h"
00050 #include "nsIFontMetrics.h"
00051 
00052 #include "nsMathMLmoverFrame.h"
00053 #include "nsMathMLmsupFrame.h"
00054 
00055 //
00056 // <mover> -- attach an overscript to a base - implementation
00057 //
00058 
00059 nsresult
00060 NS_NewMathMLmoverFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00061 {
00062   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00063   if (nsnull == aNewFrame) {
00064     return NS_ERROR_NULL_POINTER;
00065   }
00066   nsMathMLmoverFrame* it = new (aPresShell) nsMathMLmoverFrame;
00067   if (nsnull == it) {
00068     return NS_ERROR_OUT_OF_MEMORY;
00069   }
00070   *aNewFrame = it;
00071   return NS_OK;
00072 }
00073 
00074 nsMathMLmoverFrame::nsMathMLmoverFrame()
00075 {
00076 }
00077 
00078 nsMathMLmoverFrame::~nsMathMLmoverFrame()
00079 {
00080 }
00081 
00082 NS_IMETHODIMP
00083 nsMathMLmoverFrame::AttributeChanged(nsIContent*     aContent,
00084                                      PRInt32         aNameSpaceID,
00085                                      nsIAtom*        aAttribute,
00086                                      PRInt32         aModType)
00087 {
00088   if (nsMathMLAtoms::accent_ == aAttribute) {
00089     // When we have automatic data to update within ourselves, we ask our
00090     // parent to re-layout its children
00091     return ReLayoutChildren(mParent);
00092   }
00093 
00094   return nsMathMLContainerFrame::
00095          AttributeChanged(aContent, aNameSpaceID,
00096                           aAttribute, aModType);
00097 }
00098 
00099 NS_IMETHODIMP
00100 nsMathMLmoverFrame::UpdatePresentationData(PRInt32         aScriptLevelIncrement,
00101                                            PRUint32        aFlagsValues,
00102                                            PRUint32        aFlagsToUpdate)
00103 {
00104   nsMathMLContainerFrame::UpdatePresentationData(
00105     aScriptLevelIncrement, aFlagsValues, aFlagsToUpdate);
00106   // disable the stretch-all flag if we are going to act like a superscript
00107   if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
00108       !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
00109     mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
00110   }
00111   else {
00112     mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
00113   }
00114   return NS_OK;
00115 }
00116 
00117 NS_IMETHODIMP
00118 nsMathMLmoverFrame::UpdatePresentationDataFromChildAt(PRInt32         aFirstIndex,
00119                                                       PRInt32         aLastIndex,
00120                                                       PRInt32         aScriptLevelIncrement,
00121                                                       PRUint32        aFlagsValues,
00122                                                       PRUint32        aFlagsToUpdate)
00123 {
00124   // mover is special... The REC says:
00125   // Within overscript, <mover> always sets displaystyle to "false", 
00126   // but increments scriptlevel by 1 only when accent is "false".
00127   // This means that
00128   // 1. don't allow displaystyle to change in the overscript
00129   // 2. if the value of the accent is changed, we need to recompute the
00130   //    scriptlevel of the overscript. The problem is that the accent
00131   //    can change in the <mo> deep down the embellished hierarchy
00132 
00133   // Do #1 here, never allow displaystyle to be changed in the overscript
00134   PRInt32 index = 0;
00135   nsIFrame* childFrame = mFrames.FirstChild();
00136   while (childFrame) {
00137     if ((index >= aFirstIndex) &&
00138         ((aLastIndex <= 0) || ((aLastIndex > 0) && (index <= aLastIndex)))) {
00139       if (index > 0) {
00140         // disable the flag
00141         aFlagsToUpdate &= ~NS_MATHML_DISPLAYSTYLE;
00142         aFlagsValues &= ~NS_MATHML_DISPLAYSTYLE;
00143       }
00144       PropagatePresentationDataFor(childFrame, aScriptLevelIncrement,
00145                                    aFlagsValues, aFlagsToUpdate);
00146     }
00147     index++;
00148     childFrame = childFrame->GetNextSibling();
00149   }
00150   return NS_OK;
00151 
00152   // For #2, changing the accent attribute will trigger a re-build of
00153   // all automatic data in the embellished hierarchy
00154 }
00155 
00156 NS_IMETHODIMP
00157 nsMathMLmoverFrame::InheritAutomaticData(nsIFrame* aParent)
00158 {
00159   // let the base class get the default from our parent
00160   nsMathMLContainerFrame::InheritAutomaticData(aParent);
00161 
00162   mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
00163 
00164   return NS_OK;
00165 }
00166 
00167 NS_IMETHODIMP
00168 nsMathMLmoverFrame::TransmitAutomaticData()
00169 {
00170   // At this stage, all our children are in sync and we can fully
00171   // resolve our own mEmbellishData struct
00172   //---------------------------------------------------------------------
00173 
00174   /* The REC says:
00175   The default value of accent is false, unless overscript
00176   is an <mo> element or an embellished operator. If overscript is
00177   an <mo> element, the value of its accent attribute is used as
00178   the default value of accent. If overscript is an embellished 
00179   operator, the accent attribute of the <mo> element at its
00180   core is used as the default value. As with all attributes, an
00181   explicitly given value overrides the default.
00182 
00183 XXX The winner is the outermost in conflicting settings like these:
00184 <mover accent='true'>
00185   <mi>...</mi>
00186   <mo accent='false'> ... </mo>
00187 </mover>
00188    */
00189 
00190   nsIFrame* overscriptFrame = nsnull;
00191   nsIFrame* baseFrame = mFrames.FirstChild();
00192   if (baseFrame)
00193     overscriptFrame = baseFrame->GetNextSibling();
00194 
00195   // if our base is an embellished operator, let its state bubble to us (in particular,
00196   // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags
00197   // are reset to the default values of false if the base frame isn't embellished.
00198   mPresentationData.baseFrame = baseFrame;
00199   GetEmbellishDataFrom(baseFrame, mEmbellishData);
00200 
00201   nsAutoString value;
00202 
00203   // The default value of accent is false, unless the overscript is embellished
00204   // and its core <mo> is an accent
00205   nsEmbellishData embellishData;
00206   GetEmbellishDataFrom(overscriptFrame, embellishData);
00207   if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags))
00208     mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
00209   else
00210     mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
00211 
00212   // if we have an accent attribute, it overrides what the overscript said
00213   if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_None, 
00214                    nsMathMLAtoms::accent_, value)) {
00215     if (value.EqualsLiteral("true"))
00216       mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER;
00217     else if (value.EqualsLiteral("false")) 
00218       mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER;
00219   }
00220 
00221   // disable the stretch-all flag if we are going to act like a superscript
00222   if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
00223       !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags))
00224     mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY;
00225 
00226   // Now transmit any change that we want to our children so that they
00227   // can update their mPresentationData structs
00228   //---------------------------------------------------------------------
00229 
00230   /* The REC says:
00231      Within overscript, <mover> always sets displaystyle to "false", 
00232      but increments scriptlevel by 1 only when accent is "false".
00233 
00234      The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a
00235      say it shouldn't be compressed. However, The TeXBook says
00236      that math accents and \overline change uncramped styles to their
00237      cramped counterparts.
00238   */
00239   PRInt32 increment = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)
00240     ? 0 : 1;
00241   PRUint32 compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)
00242     ? NS_MATHML_COMPRESSED : 0;
00243   PropagatePresentationDataFor(overscriptFrame, increment,
00244     ~NS_MATHML_DISPLAYSTYLE | compress,
00245      NS_MATHML_DISPLAYSTYLE | compress);
00246 
00247   return NS_OK;
00248 }
00249 
00250 /*
00251 The REC says:
00252 * If the base is an operator with movablelimits="true" (or an embellished
00253   operator whose <mo> element core has movablelimits="true"), and
00254   displaystyle="false", then overscript is drawn in a superscript
00255   position. In this case, the accent attribute is ignored. This is
00256   often used for limits on symbols such as &sum;. 
00257 
00258 i.e.:
00259  if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
00260      !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
00261   // place like superscript
00262  }
00263  else {
00264   // place like overscript 
00265  }
00266 */
00267 
00268 NS_IMETHODIMP
00269 nsMathMLmoverFrame::Place(nsIRenderingContext& aRenderingContext,
00270                           PRBool               aPlaceOrigin,
00271                           nsHTMLReflowMetrics& aDesiredSize)
00272 { 
00273   if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
00274       !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
00275     // place like superscript
00276     return nsMathMLmsupFrame::PlaceSuperScript(GetPresContext(),
00277                                                aRenderingContext,
00278                                                aPlaceOrigin,
00279                                                aDesiredSize,
00280                                                this);
00281   }
00282 
00284   // Get the children's desired sizes
00285 
00286   nsBoundingMetrics bmBase, bmOver;
00287   nsHTMLReflowMetrics baseSize(nsnull);
00288   nsHTMLReflowMetrics overSize(nsnull);
00289   nsIFrame* overFrame = nsnull;
00290   nsIFrame* baseFrame = mFrames.FirstChild();
00291   if (baseFrame)
00292     overFrame = baseFrame->GetNextSibling();
00293   if (!baseFrame || !overFrame || overFrame->GetNextSibling()) {
00294     // report an error, encourage people to get their markups in order
00295     NS_WARNING("invalid markup");
00296     return ReflowError(aRenderingContext, aDesiredSize);
00297   }
00298   GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
00299   GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver);
00300 
00301   nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1);
00302 
00304   // Place Children
00305 
00306   aRenderingContext.SetFont(GetStyleFont()->mFont, nsnull);
00307   nsCOMPtr<nsIFontMetrics> fm;
00308   aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
00309 
00310   nscoord xHeight = 0;
00311   fm->GetXHeight (xHeight);
00312 
00313   nscoord ruleThickness;
00314   GetRuleThickness (aRenderingContext, fm, ruleThickness);
00315 
00316   // there are 2 different types of placement depending on 
00317   // whether we want an accented overscript or not
00318 
00319   nscoord correction = 0;
00320   GetItalicCorrection (bmBase, correction);
00321 
00322   nscoord delta1 = 0; // gap between base and overscript
00323   nscoord delta2 = 0; // extra space above overscript
00324   if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {    
00325     // Rule 13a, App. G, TeXbook
00326     nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy; 
00327     GetBigOpSpacings (fm, 
00328                       bigOpSpacing1, dummy, 
00329                       bigOpSpacing3, dummy, 
00330                       bigOpSpacing5);
00331     delta1 = PR_MAX(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent));
00332     delta2 = bigOpSpacing5;
00333 
00334     // XXX This is not a TeX rule... 
00335     // delta1 (as computed above) can become really big when bmOver.descent is
00336     // negative,  e.g., if the content is &OverBar. In such case, we use the height
00337     if (bmOver.descent < 0)    
00338       delta1 = PR_MAX(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent)));
00339   }
00340   else {
00341     // Rule 12, App. G, TeXbook
00342     // We are going to modify this rule to make it more general.
00343     // The idea behind Rule 12 in the TeXBook is to keep the accent
00344     // as close to the base as possible, while ensuring that the
00345     // distance between the *baseline* of the accent char and 
00346     // the *baseline* of the base is atleast x-height. 
00347     // The idea is that for normal use, we would like all the accents
00348     // on a line to line up atleast x-height above the baseline 
00349     // if possible. 
00350     // When the ascent of the base is >= x-height, 
00351     // the baseline of the accent char is placed just above the base
00352     // (specifically, the baseline of the accent char is placed 
00353     // above the baseline of the base by the ascent of the base).
00354     // For ease of implementation, 
00355     // this assumes that the font-designer designs accents 
00356     // in such a way that the bottom of the accent is atleast x-height
00357     // above its baseline, otherwise there will be collisions
00358     // with the base. Also there should be proper padding between
00359     // the bottom of the accent char and its baseline.
00360     // The above rule may not be obvious from a first
00361     // reading of rule 12 in the TeXBook !!!
00362     // The mathml <mover> tag can use accent chars that
00363     // do not follow this convention. So we modify TeX's rule 
00364     // so that TeX's rule gets subsumed for accents that follow 
00365     // TeX's convention,
00366     // while also allowing accents that do not follow the convention :
00367     // we try to keep the *bottom* of the accent char atleast x-height 
00368     // from the baseline of the base char. we also slap on an extra
00369     // padding between the accent and base chars.
00370     delta1 = ruleThickness + onePixel/2; // we have at least the padding
00371     if (bmBase.ascent < xHeight) { 
00372       // also ensure at least x-height above the baseline of the base
00373       delta1 += xHeight - bmBase.ascent;
00374     }
00375     delta2 = ruleThickness;
00376   }
00377   // empty over?
00378   if (!(bmOver.ascent + bmOver.descent)) delta1 = 0;
00379 
00380   nscoord dxBase, dxOver = 0;
00381 
00382   // Ad-hoc - This is to override fonts which have ready-made _accent_
00383   // glyphs with negative lbearing and rbearing. We want to position
00384   // the overscript ourselves
00385   nscoord overWidth = bmOver.width;
00386   if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) {
00387     overWidth = bmOver.rightBearing - bmOver.leftBearing;
00388     dxOver = -bmOver.leftBearing;
00389   }
00390 
00391   if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) {
00392     mBoundingMetrics.width = bmBase.width; 
00393     dxOver += correction + (mBoundingMetrics.width - overWidth)/2;
00394   }
00395   else {
00396     mBoundingMetrics.width = PR_MAX(bmBase.width, overWidth);
00397     dxOver += correction/2 + (mBoundingMetrics.width - overWidth)/2;
00398   }
00399   dxBase = (mBoundingMetrics.width - bmBase.width) / 2;
00400 
00401   mBoundingMetrics.ascent = 
00402     bmOver.ascent + bmOver.descent + delta1 + bmBase.ascent;
00403   mBoundingMetrics.descent = bmBase.descent;
00404   mBoundingMetrics.leftBearing = 
00405     PR_MIN(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing);
00406   mBoundingMetrics.rightBearing = 
00407     PR_MAX(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing);
00408 
00409   aDesiredSize.descent = baseSize.descent;
00410   aDesiredSize.ascent = 
00411     PR_MAX(mBoundingMetrics.ascent + delta2,
00412            overSize.ascent + bmOver.descent + delta1 + bmBase.ascent);
00413   aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
00414   aDesiredSize.width = mBoundingMetrics.width;
00415   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
00416 
00417   mReference.x = 0;
00418   mReference.y = aDesiredSize.ascent;
00419 
00420   if (aPlaceOrigin) {
00421     // place base
00422     nscoord dy = aDesiredSize.ascent - baseSize.ascent;
00423     FinishReflowChild (baseFrame, GetPresContext(), nsnull, baseSize, dxBase, dy, 0);
00424     // place overscript
00425     dy = aDesiredSize.ascent - 
00426       mBoundingMetrics.ascent + bmOver.ascent - overSize.ascent;
00427     FinishReflowChild (overFrame, GetPresContext(), nsnull, overSize, dxOver, dy, 0);
00428   }
00429   return NS_OK;
00430 }