Back to index

lightning-sunbird  0.9+nobinonly
nsMathMLContainerFrame.h
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> (added TeX rendering rules)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #ifndef nsMathMLContainerFrame_h___
00041 #define nsMathMLContainerFrame_h___
00042 
00043 #include "nsCOMPtr.h"
00044 #include "nsHTMLContainerFrame.h"
00045 #include "nsBlockFrame.h"
00046 #include "nsInlineFrame.h"
00047 #include "nsMathMLAtoms.h"
00048 #include "nsMathMLOperators.h"
00049 #include "nsMathMLChar.h"
00050 #include "nsMathMLFrame.h"
00051 #include "nsMathMLParts.h"
00052 
00053 /*
00054  * Base class for MathML container frames. It acts like an inferred 
00055  * mrow. By default, this frame uses its Reflow() method to lay its 
00056  * children horizontally and ensure that their baselines are aligned.
00057  * The Reflow() method relies upon Place() to position children.
00058  * By overloading Place() in derived classes, it is therefore possible
00059  * to position children in various customized ways.
00060  */
00061 
00062 // Parameters to handle the change of font-size induced by changing the
00063 // scriptlevel. These are hard-coded values that match with the rules in
00064 // mathml.css. Note that mScriptLevel can exceed these bounds, but the
00065 // scaling effect on the font-size will be bounded. The following bounds can
00066 // be expanded provided the new corresponding rules are added in mathml.css.
00067 #define NS_MATHML_CSS_POSITIVE_SCRIPTLEVEL_LIMIT  +5
00068 #define NS_MATHML_CSS_NEGATIVE_SCRIPTLEVEL_LIMIT  -5
00069 #define NS_MATHML_SCRIPTSIZEMULTIPLIER             0.71f
00070 #define NS_MATHML_SCRIPTMINSIZE                    8
00071 
00072 // Options for the preferred size at which to stretch our stretchy children 
00073 #define STRETCH_CONSIDER_ACTUAL_SIZE    0x00000001 // just use our current size
00074 #define STRETCH_CONSIDER_EMBELLISHMENTS 0x00000002 // size calculations include embellishments
00075 
00076 class nsMathMLContainerFrame : public nsHTMLContainerFrame,
00077                                public nsMathMLFrame {
00078 public:
00079 
00080   NS_DECL_ISUPPORTS_INHERITED
00081 
00082   // --------------------------------------------------------------------------
00083   // Overloaded nsMathMLFrame methods -- see documentation in nsIMathMLFrame.h
00084 
00085   NS_IMETHOD
00086   Stretch(nsIRenderingContext& aRenderingContext,
00087           nsStretchDirection   aStretchDirection,
00088           nsBoundingMetrics&   aContainerSize,
00089           nsHTMLReflowMetrics& aDesiredStretchSize);
00090 
00091   NS_IMETHOD
00092   Place(nsIRenderingContext& aRenderingContext,
00093         PRBool               aPlaceOrigin,
00094         nsHTMLReflowMetrics& aDesiredSize);
00095 
00096   NS_IMETHOD
00097   UpdatePresentationDataFromChildAt(PRInt32         aFirstIndex,
00098                                     PRInt32         aLastIndex,
00099                                     PRInt32         aScriptLevelIncrement,
00100                                     PRUint32        aFlagsValues,
00101                                     PRUint32        aFlagsToUpdate)
00102   {
00103     PropagatePresentationDataFromChildAt(this, aFirstIndex, aLastIndex,
00104       aScriptLevelIncrement, aFlagsValues, aFlagsToUpdate);
00105     return NS_OK;
00106   }
00107 
00108   NS_IMETHOD
00109   ReResolveScriptStyle(PRInt32 aParentScriptLevel)
00110   {
00111     PropagateScriptStyleFor(this, aParentScriptLevel);
00112     return NS_OK;
00113   }
00114 
00115   // --------------------------------------------------------------------------
00116   // Overloaded nsHTMLContainerFrame methods -- see documentation in nsIFrame.h
00117 
00118   virtual PRBool IsFrameOfType(PRUint32 aFlags) const;
00119 
00120   NS_IMETHOD
00121   Init(nsPresContext*  aPresContext,
00122        nsIContent*      aContent,
00123        nsIFrame*        aParent,
00124        nsStyleContext*  aContext,
00125        nsIFrame*        aPrevInFlow);
00126 
00127   NS_IMETHOD
00128   SetInitialChildList(nsPresContext* aPresContext,
00129                       nsIAtom*        aListName,
00130                       nsIFrame*       aChildList);
00131 
00132   NS_IMETHOD
00133   AppendFrames(nsIAtom*        aListName,
00134                nsIFrame*       aFrameList);
00135 
00136   NS_IMETHOD
00137   InsertFrames(nsIAtom*        aListName,
00138                nsIFrame*       aPrevFrame,
00139                nsIFrame*       aFrameList);
00140 
00141   NS_IMETHOD
00142   RemoveFrame(nsIAtom*        aListName,
00143               nsIFrame*       aOldFrame);
00144 
00145   NS_IMETHOD
00146   ReplaceFrame(nsIAtom*        aListName,
00147                nsIFrame*       aOldFrame,
00148                nsIFrame*       aNewFrame);
00149 
00150   NS_IMETHODIMP
00151   ReflowDirtyChild(nsIPresShell* aPresShell, 
00152                    nsIFrame*     aChild);
00153 
00154   NS_IMETHOD
00155   Reflow(nsPresContext*          aPresContext,
00156          nsHTMLReflowMetrics&     aDesiredSize,
00157          const nsHTMLReflowState& aReflowState,
00158          nsReflowStatus&          aStatus);
00159 
00160   NS_IMETHOD
00161   DidReflow(nsPresContext*           aPresContext,
00162             const nsHTMLReflowState*  aReflowState,
00163             nsDidReflowStatus         aStatus)
00164 
00165   {
00166     mPresentationData.flags &= ~NS_MATHML_STRETCH_DONE;
00167     return nsHTMLContainerFrame::DidReflow(aPresContext, aReflowState, aStatus);
00168   }
00169 
00170   NS_IMETHOD 
00171   Paint(nsPresContext*      aPresContext,
00172         nsIRenderingContext& aRenderingContext,
00173         const nsRect&        aDirtyRect,
00174         nsFramePaintLayer    aWhichLayer,
00175         PRUint32             aFlags = 0);
00176 
00177   // Notification when an attribute is changed. The MathML module uses the
00178   // following paradigm:
00179   //
00180   // 1. If the MathML frame class doesn't have any cached automatic data that
00181   //    depends on the attribute: we just reflow (e.g., this happens with <msub>,
00182   //    <msup>, <mmultiscripts>, etc). This is the default behavior implemented
00183   //    by this base class.
00184   //
00185   // 2. If the MathML frame class has cached automatic data that depends on
00186   //    the attribute:
00187   //    2a. If the automatic data to update resides only within the descendants,
00188   //        we just re-layout them using ReLayoutChildren(aPresContext, this);
00189   //        (e.g., this happens with <ms>).
00190   //    2b. If the automatic data to update affects us in some way, we ask our parent
00191   //        to re-layout its children using ReLayoutChildren(aPresContext, mParent);
00192   //        Therefore, there is an overhead here in that our siblings are re-laid
00193   //        too (e.g., this happens with <mstyle>, <munder>, <mover>, <munderover>). 
00194   NS_IMETHOD
00195   AttributeChanged(nsIContent*     aChild,
00196                    PRInt32         aNameSpaceID,
00197                    nsIAtom*        aAttribute,
00198                    PRInt32         aModType);
00199 
00200   // --------------------------------------------------------------------------
00201   // Additional methods 
00202 
00203   // helper to re-sync the automatic data in our children and notify our parent to
00204   // reflow us when changes (e.g., append/insert/remove) happen in our child list
00205   virtual nsresult
00206   ChildListChanged(PRInt32 aModType);
00207 
00208   // helper to get the preferred size that a container frame should use to fire
00209   // the stretch on its stretchy child frames.
00210   virtual void
00211   GetPreferredStretchSize(nsIRenderingContext& aRenderingContext,
00212                           PRUint32             aOptions,
00213                           nsStretchDirection   aStretchDirection,
00214                           nsBoundingMetrics&   aPreferredStretchSize);
00215 
00216   // error handlers to provide a visual feedback to the user when an error
00217   // (typically invalid markup) was encountered during reflow.
00218   virtual nsresult
00219   ReflowError(nsIRenderingContext& aRenderingContext,
00220               nsHTMLReflowMetrics& aDesiredSize);
00221   virtual nsresult
00222   PaintError(nsIRenderingContext& aRenderingContext,
00223              const nsRect&        aDirtyRect,
00224              nsFramePaintLayer    aWhichLayer);
00225 
00226   // helper method to reflow a child frame. We are inline frames, and we don't
00227   // know our positions until reflow is finished. That's why we ask the
00228   // base method not to worry about our position.
00229   nsresult 
00230   ReflowChild(nsIFrame*                aKidFrame,
00231               nsPresContext*          aPresContext,
00232               nsHTMLReflowMetrics&     aDesiredSize,
00233               const nsHTMLReflowState& aReflowState,
00234               nsReflowStatus&          aStatus);
00235 
00236   nsresult 
00237   ReflowForeignChild(nsIFrame*                aKidFrame,
00238                      nsPresContext*           aPresContext,
00239                      nsHTMLReflowMetrics&     aDesiredSize,
00240                      const nsHTMLReflowState& aReflowState,
00241                      nsReflowStatus&          aStatus);
00242 
00243   // helper to add the inter-spacing when <math> is the immediate parent.
00244   // Since we don't (yet) handle the root <math> element ourselves, we need to
00245   // take special care of the inter-frame spacing on elements for which <math>
00246   // is the direct xml parent. This function will be repeatedly called from
00247   // left to right on the childframes of <math>, and by so doing it will
00248   // emulate the spacing that would have been done by a <mrow> container.
00249   // e.g., it fixes <math> <mi>f</mi> <mo>q</mo> <mi>f</mi> <mo>I</mo> </math>
00250   virtual nscoord
00251   FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize);
00252 
00253   // helper method to complete the post-reflow hook and ensure that embellished
00254   // operators don't terminate their Reflow without receiving a Stretch command.
00255   virtual nsresult
00256   FinalizeReflow(nsIRenderingContext& aRenderingContext,
00257                  nsHTMLReflowMetrics& aDesiredSize);
00258 
00259   // helper method to facilitate getting the reflow and bounding metrics
00260   // IMPORTANT: This function is only meant to be called in Place() methods 
00261   // where it is assumed that the frame's rect is still acting as place holder
00262   // for the frame's ascent and descent information
00263   static void
00264   GetReflowAndBoundingMetricsFor(nsIFrame*            aFrame,
00265                                  nsHTMLReflowMetrics& aReflowMetrics,
00266                                  nsBoundingMetrics&   aBoundingMetrics,
00267                                  eMathMLFrameType*    aMathMLFrameType = nsnull);
00268 
00269   // helper to let the scriptstyle re-resolution pass through
00270   // a subtree that may contain non-MathML container frames
00271   static void
00272   PropagateScriptStyleFor(nsIFrame*       aFrame,
00273                           PRInt32         aParentScriptLevel);
00274 
00275   // helper to let the update of presentation data pass through
00276   // a subtree that may contain non-MathML container frames
00277   static void
00278   PropagatePresentationDataFor(nsIFrame*       aFrame,
00279                                PRInt32         aScriptLevelIncrement,
00280                                PRUint32        aFlagsValues,
00281                                PRUint32        aFlagsToUpdate);
00282 
00283   static void
00284   PropagatePresentationDataFromChildAt(nsIFrame*       aParentFrame,
00285                                        PRInt32         aFirstChildIndex,
00286                                        PRInt32         aLastChildIndex,
00287                                        PRInt32         aScriptLevelIncrement,
00288                                        PRUint32        aFlagsValues,
00289                                        PRUint32        aFlagsToUpdate);
00290 
00291   // helper to let the rebuild of automatic data (presentation data
00292   // and embellishement data) walk through a subtree that may contain
00293   // non-MathML container frames. Note that this method re-builds the
00294   // automatic data in the children -- not in aParentFrame itself (except
00295   // for those particular operations that the parent frame may do in its
00296   // TransmitAutomaticData()). The reason it works this way is because
00297   // a container frame knows what it wants for its children, whereas children
00298   // have no clue who their parent is. For example, it is <mfrac> who knows
00299   // that its children have to be in scriptsizes, and has to transmit this
00300   // information to them. Hence, when changes occur in a child frame, the child
00301   // has to request the re-build from its parent. Unfortunately, the extra cost
00302   // for this is that it will re-sync in the siblings of the child as well.
00303   static void
00304   RebuildAutomaticDataForChildren(nsIFrame* aParentFrame);
00305 
00306   // helper to blow away the automatic data cached in a frame's subtree and
00307   // re-layout its subtree to reflect changes that may have happen. In the
00308   // event where aParentFrame isn't a MathML frame, it will first walk up to
00309   // the ancestor that is a MathML frame, and re-layout from there -- this is
00310   // to guarantee that automatic data will be rebuilt properly. Note that this
00311   // method re-builds the automatic data in the children -- not in the parent
00312   // frame itself (except for those particular operations that the parent frame
00313   // may do do its TransmitAutomaticData()). @see RebuildAutomaticDataForChildren
00314   static nsresult
00315   ReLayoutChildren(nsIFrame* aParentFrame);
00316 
00317 protected:
00318   virtual PRIntn GetSkipSides() const { return 0; }
00319 };
00320 
00321 
00322 // --------------------------------------------------------------------------
00323 // Currently, to benefit from line-breaking inside the <math> element, <math> is
00324 // simply mapping to nsBlockFrame or nsInlineFrame.
00325 // A separate implemention needs to provide:
00326 // 1) line-breaking
00327 // 2) proper inter-frame spacing
00328 // 3) firing of Stretch() (in which case FinalizeReflow() would have to be cleaned)
00329 // Issues: If/when mathml becomes a pluggable component, the separation will be needed.
00330 class nsMathMLmathBlockFrame : public nsBlockFrame {
00331 public:
00332   friend nsresult NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame);
00333 
00334   // beware, mFrames is not set by nsBlockFrame
00335   // cannot use mFrames{.FirstChild()|.etc} since the block code doesn't set mFrames
00336   NS_IMETHOD
00337   SetInitialChildList(nsPresContext* aPresContext,
00338                       nsIAtom*        aListName,
00339                       nsIFrame*       aChildList)
00340   {
00341     nsresult rv = nsBlockFrame::SetInitialChildList(aPresContext, aListName, aChildList);
00342     // re-resolve our subtree to set any mathml-expected data
00343     nsMathMLContainerFrame::MapAttributesIntoCSS(aPresContext, this);
00344     nsMathMLContainerFrame::RebuildAutomaticDataForChildren(this);
00345     return rv;
00346   }
00347 
00348   NS_IMETHOD
00349   Reflow(nsPresContext*          aPresContext,
00350          nsHTMLReflowMetrics&     aDesiredSize,
00351          const nsHTMLReflowState& aReflowState,
00352          nsReflowStatus&          aStatus)
00353   {
00354     if (mScriptStyleChanged) {
00355       mScriptStyleChanged = PR_FALSE;
00356       nsMathMLContainerFrame::PropagateScriptStyleFor(this, 0);
00357     }
00358     return nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
00359   }
00360 
00361   NS_IMETHOD
00362   AppendFrames(nsIAtom*        aListName,
00363                nsIFrame*       aFrameList)
00364   {
00365     NS_ASSERTION(!aListName, "internal error");
00366     nsresult rv = nsBlockFrame::AppendFrames(aListName, aFrameList);
00367     nsMathMLContainerFrame::ReLayoutChildren(this);
00368     return rv;
00369   }
00370 
00371   NS_IMETHOD
00372   InsertFrames(nsIAtom*        aListName,
00373                nsIFrame*       aPrevFrame,
00374                nsIFrame*       aFrameList)
00375   {
00376     NS_ASSERTION(!aListName, "internal error");
00377     nsresult rv = nsBlockFrame::InsertFrames(aListName, aPrevFrame, aFrameList);
00378     nsMathMLContainerFrame::ReLayoutChildren(this);
00379     return rv;
00380   }
00381 
00382   NS_IMETHOD
00383   ReplaceFrame(nsIAtom*        aListName,
00384                nsIFrame*       aOldFrame,
00385                nsIFrame*       aNewFrame)
00386   {
00387     NS_ASSERTION(!aListName, "internal error");
00388     nsresult rv = nsBlockFrame::ReplaceFrame(aListName, aOldFrame, aNewFrame);
00389     nsMathMLContainerFrame::ReLayoutChildren(this);
00390     return rv;
00391   }
00392 
00393   NS_IMETHOD
00394   RemoveFrame(nsIAtom*        aListName,
00395               nsIFrame*       aOldFrame)
00396   {
00397     NS_ASSERTION(!aListName, "internal error");
00398     nsresult rv = nsBlockFrame::RemoveFrame(aListName, aOldFrame);
00399     nsMathMLContainerFrame::ReLayoutChildren(this);
00400     return rv;
00401   }
00402 
00403   virtual PRBool IsFrameOfType(PRUint32 aFlags) const {
00404     return !(aFlags & ~nsIFrame::eMathML);
00405   }
00406 
00407 protected:
00408   nsMathMLmathBlockFrame() {
00409     // We should always have a space manager.  Not that things can really try
00410     // to float out of us anyway, but we need one for line layout.
00411     AddStateBits(NS_BLOCK_SPACE_MGR);
00412   }
00413   virtual ~nsMathMLmathBlockFrame() {}
00414 
00415   NS_IMETHOD
00416   DidSetStyleContext(nsPresContext* aPresContext)
00417   {
00418     mScriptStyleChanged = PR_TRUE;
00419     return nsBlockFrame::DidSetStyleContext(aPresContext);
00420   }
00421 
00422   PRBool mScriptStyleChanged;
00423 };
00424 
00425 // --------------
00426 
00427 class nsMathMLmathInlineFrame : public nsInlineFrame {
00428 public:
00429   friend nsresult NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame);
00430 
00431   NS_IMETHOD
00432   SetInitialChildList(nsPresContext* aPresContext,
00433                       nsIAtom*        aListName,
00434                       nsIFrame*       aChildList)
00435   {
00436     nsresult rv = nsInlineFrame::SetInitialChildList(aPresContext, aListName, aChildList);
00437     // re-resolve our subtree to set any mathml-expected data
00438     nsMathMLContainerFrame::MapAttributesIntoCSS(aPresContext, this);
00439     nsMathMLContainerFrame::RebuildAutomaticDataForChildren(this);
00440     return rv;
00441   }
00442 
00443   NS_IMETHOD
00444   Reflow(nsPresContext*          aPresContext,
00445          nsHTMLReflowMetrics&     aDesiredSize,
00446          const nsHTMLReflowState& aReflowState,
00447          nsReflowStatus&          aStatus)
00448   {
00449     if (mScriptStyleChanged) {
00450       mScriptStyleChanged = PR_FALSE;
00451       nsMathMLContainerFrame::PropagateScriptStyleFor(this, 0);
00452     }
00453     return nsInlineFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
00454   }
00455 
00456   NS_IMETHOD
00457   AppendFrames(nsIAtom*        aListName,
00458                nsIFrame*       aFrameList)
00459   {
00460     NS_ASSERTION(!aListName, "internal error");
00461     nsresult rv = nsInlineFrame::AppendFrames(aListName, aFrameList);
00462     nsMathMLContainerFrame::ReLayoutChildren(this);
00463     return rv;
00464   }
00465 
00466   NS_IMETHOD
00467   InsertFrames(nsIAtom*        aListName,
00468                nsIFrame*       aPrevFrame,
00469                nsIFrame*       aFrameList)
00470   {
00471     NS_ASSERTION(!aListName, "internal error");
00472     nsresult rv = nsInlineFrame::InsertFrames(aListName, aPrevFrame, aFrameList);
00473     nsMathMLContainerFrame::ReLayoutChildren(this);
00474     return rv;
00475   }
00476 
00477   NS_IMETHOD
00478   ReplaceFrame(nsIAtom*        aListName,
00479                nsIFrame*       aOldFrame,
00480                nsIFrame*       aNewFrame)
00481   {
00482     NS_ASSERTION(!aListName, "internal error");
00483     nsresult rv = nsInlineFrame::ReplaceFrame(aListName, aOldFrame, aNewFrame);
00484     nsMathMLContainerFrame::ReLayoutChildren(this);
00485     return rv;
00486   }
00487 
00488   NS_IMETHOD
00489   RemoveFrame(nsIAtom*        aListName,
00490               nsIFrame*       aOldFrame)
00491   {
00492     NS_ASSERTION(!aListName, "internal error");
00493     nsresult rv = nsInlineFrame::RemoveFrame(aListName, aOldFrame);
00494     nsMathMLContainerFrame::ReLayoutChildren(this);
00495     return rv;
00496   }
00497 
00498   virtual PRBool IsFrameOfType(PRUint32 aFlags) const {
00499     return !(aFlags & ~nsIFrame::eMathML);
00500   }
00501 
00502 protected:
00503   nsMathMLmathInlineFrame() {}
00504   virtual ~nsMathMLmathInlineFrame() {}
00505 
00506   NS_IMETHOD
00507   DidSetStyleContext(nsPresContext* aPresContext)
00508   {
00509     mScriptStyleChanged = PR_TRUE;
00510     return nsInlineFrame::DidSetStyleContext(aPresContext);
00511   }
00512 
00513   PRBool mScriptStyleChanged;
00514 };
00515 
00516 #endif /* nsMathMLContainerFrame_h___ */