Back to index

lightning-sunbird  0.9+nobinonly
nsMathMLmunderFrame.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 "nsMathMLmunderFrame.h"
00053 #include "nsMathMLmsubFrame.h"
00054 
00055 //
00056 // <munder> -- attach an underscript to a base - implementation
00057 //
00058 
00059 nsresult
00060 NS_NewMathMLmunderFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00061 {
00062   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00063   if (nsnull == aNewFrame) {
00064     return NS_ERROR_NULL_POINTER;
00065   }
00066   nsMathMLmunderFrame* it = new (aPresShell) nsMathMLmunderFrame;
00067   if (nsnull == it) {
00068     return NS_ERROR_OUT_OF_MEMORY;
00069   }
00070   *aNewFrame = it;
00071   return NS_OK;
00072 }
00073 
00074 nsMathMLmunderFrame::nsMathMLmunderFrame()
00075 {
00076 }
00077 
00078 nsMathMLmunderFrame::~nsMathMLmunderFrame()
00079 {
00080 }
00081 
00082 NS_IMETHODIMP
00083 nsMathMLmunderFrame::AttributeChanged(nsIContent*     aContent,
00084                                       PRInt32         aNameSpaceID,
00085                                       nsIAtom*        aAttribute,
00086                                       PRInt32         aModType)
00087 {
00088   if (nsMathMLAtoms::accentunder_ == 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 nsMathMLmunderFrame::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 subscript
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 nsMathMLmunderFrame::UpdatePresentationDataFromChildAt(PRInt32         aFirstIndex,
00119                                                        PRInt32         aLastIndex,
00120                                                        PRInt32         aScriptLevelIncrement,
00121                                                        PRUint32        aFlagsValues,
00122                                                        PRUint32        aFlagsToUpdate)
00123 {
00124   // munder is special... The REC says:
00125   // Within underscript, <munder> always sets displaystyle to "false", 
00126   // but increments scriptlevel by 1 only when accentunder is "false".
00127   // This means that
00128   // 1. don't allow displaystyle to change in the underscript
00129   // 2. if the value of the accent is changed, we need to recompute the
00130   //    scriptlevel of the underscript. 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 underscript
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,
00145         aScriptLevelIncrement, 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 nsMathMLmunderFrame::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 nsMathMLmunderFrame::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 accentunder is false, unless underscript
00176   is an <mo> element or an embellished operator.  If underscript is 
00177   an <mo> element, the value of its accent attribute is used as the
00178   default value of accentunder. If underscript 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 setting in conflicting settings like these:
00184 <munder accent='true'>
00185   <mi>...</mi>
00186   <mo accent='false'> ... </mo>
00187 </munder>
00188    */
00189 
00190   nsIFrame* underscriptFrame = nsnull;
00191   nsIFrame* baseFrame = mFrames.FirstChild();
00192   if (baseFrame)
00193     underscriptFrame = 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 accentunder is false, unless the underscript is embellished
00204   // and its core <mo> is an accent
00205   nsEmbellishData embellishData;
00206   GetEmbellishDataFrom(underscriptFrame, embellishData);
00207   if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags))
00208     mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
00209   else
00210     mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
00211 
00212   // if we have an accentunder attribute, it overrides what the underscript said
00213   if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_None, 
00214                    nsMathMLAtoms::accentunder_, value)) {
00215     if (value.EqualsLiteral("true"))
00216       mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER;
00217     else if (value.EqualsLiteral("false")) 
00218       mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER;
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 underscript, <munder> always sets displaystyle to "false", 
00232      but increments scriptlevel by 1 only when accentunder is "false".
00233 
00234      The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a 
00235      say it should be compressed
00236   */
00237   PRInt32 increment = NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)
00238     ? 0 : 1;
00239   PropagatePresentationDataFor(underscriptFrame, increment,
00240     ~NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED,
00241      NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED);
00242 
00243   return NS_OK;
00244 }
00245 
00246 /*
00247 The REC says:
00248 * If the base is an operator with movablelimits="true" (or
00249   an embellished operator whose <mo> element core has
00250   movablelimits="true"), and displaystyle="false", then
00251   underscript is drawn in a subscript position. In this case,
00252   the accentunder attribute is ignored. This is often used
00253   for limits on symbols such as &sum;. 
00254 
00255 i.e.,:
00256  if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
00257      !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
00258   // place like subscript
00259  }
00260  else {
00261   // place like underscript 
00262  }
00263 */
00264 
00265 NS_IMETHODIMP
00266 nsMathMLmunderFrame::Place(nsIRenderingContext& aRenderingContext,
00267                            PRBool               aPlaceOrigin,
00268                            nsHTMLReflowMetrics& aDesiredSize)
00269 {
00270   if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) &&
00271       !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) {
00272     // place like subscript
00273     return nsMathMLmsubFrame::PlaceSubScript(GetPresContext(),
00274                                              aRenderingContext,
00275                                              aPlaceOrigin,
00276                                              aDesiredSize,
00277                                              this);
00278   }
00279 
00281   // Get the children's desired sizes
00282 
00283   nsBoundingMetrics bmBase, bmUnder;
00284   nsHTMLReflowMetrics baseSize (nsnull);
00285   nsHTMLReflowMetrics underSize (nsnull);
00286   nsIFrame* underFrame = nsnull;
00287   nsIFrame* baseFrame = mFrames.FirstChild();
00288   if (baseFrame)
00289     underFrame = baseFrame->GetNextSibling();
00290   if (!baseFrame || !underFrame || underFrame->GetNextSibling()) {
00291     // report an error, encourage people to get their markups in order
00292     NS_WARNING("invalid markup");
00293     return ReflowError(aRenderingContext, aDesiredSize);
00294   }
00295   GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase);
00296   GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder);
00297 
00298   nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1);
00299 
00301   // Place Children
00302 
00303   aRenderingContext.SetFont(GetStyleFont()->mFont, nsnull);
00304   nsCOMPtr<nsIFontMetrics> fm;
00305   aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
00306 
00307   nscoord xHeight = 0;
00308   fm->GetXHeight (xHeight);
00309 
00310   nscoord ruleThickness;
00311   GetRuleThickness (aRenderingContext, fm, ruleThickness);
00312 
00313   // there are 2 different types of placement depending on 
00314   // whether we want an accented under or not
00315 
00316   nscoord correction = 0;
00317   GetItalicCorrection (bmBase, correction);
00318 
00319   nscoord delta1 = 0; // gap between base and underscript
00320   nscoord delta2 = 0; // extra space beneath underscript
00321   if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {    
00322     // Rule 13a, App. G, TeXbook
00323     nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy; 
00324     GetBigOpSpacings (fm, 
00325                       dummy, bigOpSpacing2, 
00326                       dummy, bigOpSpacing4, 
00327                       bigOpSpacing5);
00328     delta1 = PR_MAX(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent));
00329     delta2 = bigOpSpacing5;
00330   }
00331   else {
00332     // No corresponding rule in TeXbook - we are on our own here
00333     // XXX tune the gap delta between base and underscript 
00334 
00335     // Should we use Rule 10 like \underline does?
00336     delta1 = ruleThickness + onePixel/2;
00337     delta2 = ruleThickness;
00338   }
00339   // empty under?
00340   if (!(bmUnder.ascent + bmUnder.descent)) delta1 = 0;
00341 
00342   nscoord dxBase, dxUnder;
00343   nscoord maxWidth = PR_MAX(bmBase.width, bmUnder.width);
00344   if (NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) {    
00345     dxUnder = (maxWidth - bmUnder.width)/2;
00346   }
00347   else {
00348     dxUnder = -correction/2 + (maxWidth - bmUnder.width)/2;
00349   }
00350   dxBase = (maxWidth - bmBase.width)/2;
00351 
00352   mBoundingMetrics.width =
00353     PR_MAX(dxBase + bmBase.width, dxUnder + bmUnder.width);
00354   mBoundingMetrics.ascent = bmBase.ascent;
00355   mBoundingMetrics.descent = 
00356     bmBase.descent + delta1 + bmUnder.ascent + bmUnder.descent;
00357   mBoundingMetrics.leftBearing = 
00358     PR_MIN(dxBase + bmBase.leftBearing, dxUnder + bmUnder.leftBearing);
00359   mBoundingMetrics.rightBearing = 
00360     PR_MAX(dxBase + bmBase.rightBearing, dxUnder + bmUnder.rightBearing);
00361 
00362   aDesiredSize.ascent = baseSize.ascent;
00363   aDesiredSize.descent = 
00364     PR_MAX(mBoundingMetrics.descent + delta2,
00365            bmBase.descent + delta1 + bmUnder.ascent + underSize.descent);
00366   aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
00367   aDesiredSize.width = mBoundingMetrics.width;
00368   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
00369 
00370   mReference.x = 0;
00371   mReference.y = aDesiredSize.ascent;
00372 
00373   if (aPlaceOrigin) {
00374     nscoord dy = 0;
00375     // place base
00376     FinishReflowChild(baseFrame, GetPresContext(), nsnull, baseSize, dxBase, dy, 0);
00377     // place underscript
00378     dy = aDesiredSize.ascent + mBoundingMetrics.descent - bmUnder.descent - underSize.ascent;
00379     FinishReflowChild(underFrame, GetPresContext(), nsnull, underSize, dxUnder, dy, 0);
00380   }
00381 
00382   return NS_OK;
00383 }
00384