Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "nsMathMLmencloseFrame.h" |
michael@0 | 7 | #include "nsPresContext.h" |
michael@0 | 8 | #include "nsRenderingContext.h" |
michael@0 | 9 | #include "nsWhitespaceTokenizer.h" |
michael@0 | 10 | |
michael@0 | 11 | #include "nsDisplayList.h" |
michael@0 | 12 | #include "gfxContext.h" |
michael@0 | 13 | #include "nsMathMLChar.h" |
michael@0 | 14 | #include <algorithm> |
michael@0 | 15 | |
michael@0 | 16 | // |
michael@0 | 17 | // <menclose> -- enclose content with a stretching symbol such |
michael@0 | 18 | // as a long division sign. - implementation |
michael@0 | 19 | |
michael@0 | 20 | // longdiv: |
michael@0 | 21 | // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis |
michael@0 | 22 | // renders better with current font support. |
michael@0 | 23 | static const char16_t kLongDivChar = ')'; |
michael@0 | 24 | |
michael@0 | 25 | // radical: 'SQUARE ROOT' |
michael@0 | 26 | static const char16_t kRadicalChar = 0x221A; |
michael@0 | 27 | |
michael@0 | 28 | // updiagonalstrike |
michael@0 | 29 | static const uint8_t kArrowHeadSize = 10; |
michael@0 | 30 | |
michael@0 | 31 | nsIFrame* |
michael@0 | 32 | NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
michael@0 | 33 | { |
michael@0 | 34 | return new (aPresShell) nsMathMLmencloseFrame(aContext); |
michael@0 | 35 | } |
michael@0 | 36 | |
michael@0 | 37 | NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame) |
michael@0 | 38 | |
michael@0 | 39 | nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) : |
michael@0 | 40 | nsMathMLContainerFrame(aContext), mNotationsToDraw(0), |
michael@0 | 41 | mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0) |
michael@0 | 42 | { |
michael@0 | 43 | } |
michael@0 | 44 | |
michael@0 | 45 | nsMathMLmencloseFrame::~nsMathMLmencloseFrame() |
michael@0 | 46 | { |
michael@0 | 47 | } |
michael@0 | 48 | |
michael@0 | 49 | nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask) |
michael@0 | 50 | { |
michael@0 | 51 | // Is the char already allocated? |
michael@0 | 52 | if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) || |
michael@0 | 53 | (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0)) |
michael@0 | 54 | return NS_OK; |
michael@0 | 55 | |
michael@0 | 56 | // No need to track the style context given to our MathML chars. |
michael@0 | 57 | // The Style System will use Get/SetAdditionalStyleContext() to keep it |
michael@0 | 58 | // up-to-date if dynamic changes arise. |
michael@0 | 59 | uint32_t i = mMathMLChar.Length(); |
michael@0 | 60 | nsAutoString Char; |
michael@0 | 61 | |
michael@0 | 62 | if (!mMathMLChar.AppendElement()) |
michael@0 | 63 | return NS_ERROR_OUT_OF_MEMORY; |
michael@0 | 64 | |
michael@0 | 65 | if (mask == NOTATION_LONGDIV) { |
michael@0 | 66 | Char.Assign(kLongDivChar); |
michael@0 | 67 | mLongDivCharIndex = i; |
michael@0 | 68 | } else if (mask == NOTATION_RADICAL) { |
michael@0 | 69 | Char.Assign(kRadicalChar); |
michael@0 | 70 | mRadicalCharIndex = i; |
michael@0 | 71 | } |
michael@0 | 72 | |
michael@0 | 73 | nsPresContext *presContext = PresContext(); |
michael@0 | 74 | mMathMLChar[i].SetData(presContext, Char); |
michael@0 | 75 | ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]); |
michael@0 | 76 | |
michael@0 | 77 | return NS_OK; |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | /* |
michael@0 | 81 | * Add a notation to draw, if the argument is the name of a known notation. |
michael@0 | 82 | * @param aNotation string name of a notation |
michael@0 | 83 | */ |
michael@0 | 84 | nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation) |
michael@0 | 85 | { |
michael@0 | 86 | nsresult rv; |
michael@0 | 87 | |
michael@0 | 88 | if (aNotation.EqualsLiteral("longdiv")) { |
michael@0 | 89 | rv = AllocateMathMLChar(NOTATION_LONGDIV); |
michael@0 | 90 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 91 | mNotationsToDraw |= NOTATION_LONGDIV; |
michael@0 | 92 | } else if (aNotation.EqualsLiteral("actuarial")) { |
michael@0 | 93 | mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP); |
michael@0 | 94 | } else if (aNotation.EqualsLiteral("radical")) { |
michael@0 | 95 | rv = AllocateMathMLChar(NOTATION_RADICAL); |
michael@0 | 96 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 97 | mNotationsToDraw |= NOTATION_RADICAL; |
michael@0 | 98 | } else if (aNotation.EqualsLiteral("box")) { |
michael@0 | 99 | mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT | |
michael@0 | 100 | NOTATION_TOP | NOTATION_BOTTOM); |
michael@0 | 101 | } else if (aNotation.EqualsLiteral("roundedbox")) { |
michael@0 | 102 | mNotationsToDraw |= NOTATION_ROUNDEDBOX; |
michael@0 | 103 | } else if (aNotation.EqualsLiteral("circle")) { |
michael@0 | 104 | mNotationsToDraw |= NOTATION_CIRCLE; |
michael@0 | 105 | } else if (aNotation.EqualsLiteral("left")) { |
michael@0 | 106 | mNotationsToDraw |= NOTATION_LEFT; |
michael@0 | 107 | } else if (aNotation.EqualsLiteral("right")) { |
michael@0 | 108 | mNotationsToDraw |= NOTATION_RIGHT; |
michael@0 | 109 | } else if (aNotation.EqualsLiteral("top")) { |
michael@0 | 110 | mNotationsToDraw |= NOTATION_TOP; |
michael@0 | 111 | } else if (aNotation.EqualsLiteral("bottom")) { |
michael@0 | 112 | mNotationsToDraw |= NOTATION_BOTTOM; |
michael@0 | 113 | } else if (aNotation.EqualsLiteral("updiagonalstrike")) { |
michael@0 | 114 | mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE; |
michael@0 | 115 | } else if (aNotation.EqualsLiteral("updiagonalarrow")) { |
michael@0 | 116 | mNotationsToDraw |= NOTATION_UPDIAGONALARROW; |
michael@0 | 117 | } else if (aNotation.EqualsLiteral("downdiagonalstrike")) { |
michael@0 | 118 | mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE; |
michael@0 | 119 | } else if (aNotation.EqualsLiteral("verticalstrike")) { |
michael@0 | 120 | mNotationsToDraw |= NOTATION_VERTICALSTRIKE; |
michael@0 | 121 | } else if (aNotation.EqualsLiteral("horizontalstrike")) { |
michael@0 | 122 | mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE; |
michael@0 | 123 | } else if (aNotation.EqualsLiteral("madruwb")) { |
michael@0 | 124 | mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM); |
michael@0 | 125 | } |
michael@0 | 126 | |
michael@0 | 127 | return NS_OK; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | /* |
michael@0 | 131 | * Initialize the list of notations to draw |
michael@0 | 132 | */ |
michael@0 | 133 | void nsMathMLmencloseFrame::InitNotations() |
michael@0 | 134 | { |
michael@0 | 135 | mNotationsToDraw = 0; |
michael@0 | 136 | mLongDivCharIndex = mRadicalCharIndex = -1; |
michael@0 | 137 | mMathMLChar.Clear(); |
michael@0 | 138 | |
michael@0 | 139 | nsAutoString value; |
michael@0 | 140 | |
michael@0 | 141 | if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) { |
michael@0 | 142 | // parse the notation attribute |
michael@0 | 143 | nsWhitespaceTokenizer tokenizer(value); |
michael@0 | 144 | |
michael@0 | 145 | while (tokenizer.hasMoreTokens()) |
michael@0 | 146 | AddNotation(tokenizer.nextToken()); |
michael@0 | 147 | |
michael@0 | 148 | if (IsToDraw(NOTATION_UPDIAGONALARROW)) { |
michael@0 | 149 | // For <menclose notation="updiagonalstrike updiagonalarrow">, if |
michael@0 | 150 | // the two notations are drawn then the strike line may cause the point of |
michael@0 | 151 | // the arrow to be too wide. Hence we will only draw the updiagonalarrow |
michael@0 | 152 | // and the arrow shaft may be thought to be the updiagonalstrike. |
michael@0 | 153 | mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE; |
michael@0 | 154 | } |
michael@0 | 155 | } else { |
michael@0 | 156 | // default: longdiv |
michael@0 | 157 | if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV))) |
michael@0 | 158 | return; |
michael@0 | 159 | mNotationsToDraw = NOTATION_LONGDIV; |
michael@0 | 160 | } |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | NS_IMETHODIMP |
michael@0 | 164 | nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent) |
michael@0 | 165 | { |
michael@0 | 166 | // let the base class get the default from our parent |
michael@0 | 167 | nsMathMLContainerFrame::InheritAutomaticData(aParent); |
michael@0 | 168 | |
michael@0 | 169 | mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; |
michael@0 | 170 | |
michael@0 | 171 | InitNotations(); |
michael@0 | 172 | |
michael@0 | 173 | return NS_OK; |
michael@0 | 174 | } |
michael@0 | 175 | |
michael@0 | 176 | NS_IMETHODIMP |
michael@0 | 177 | nsMathMLmencloseFrame::TransmitAutomaticData() |
michael@0 | 178 | { |
michael@0 | 179 | if (IsToDraw(NOTATION_RADICAL)) { |
michael@0 | 180 | // The TeXBook (Ch 17. p.141) says that \sqrt is cramped |
michael@0 | 181 | UpdatePresentationDataFromChildAt(0, -1, |
michael@0 | 182 | NS_MATHML_COMPRESSED, |
michael@0 | 183 | NS_MATHML_COMPRESSED); |
michael@0 | 184 | } |
michael@0 | 185 | |
michael@0 | 186 | return NS_OK; |
michael@0 | 187 | } |
michael@0 | 188 | |
michael@0 | 189 | void |
michael@0 | 190 | nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
michael@0 | 191 | const nsRect& aDirtyRect, |
michael@0 | 192 | const nsDisplayListSet& aLists) |
michael@0 | 193 | { |
michael@0 | 194 | ///////////// |
michael@0 | 195 | // paint the menclosed content |
michael@0 | 196 | nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); |
michael@0 | 197 | |
michael@0 | 198 | if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) |
michael@0 | 199 | return; |
michael@0 | 200 | |
michael@0 | 201 | nsRect mencloseRect = nsIFrame::GetRect(); |
michael@0 | 202 | mencloseRect.x = mencloseRect.y = 0; |
michael@0 | 203 | |
michael@0 | 204 | if (IsToDraw(NOTATION_RADICAL)) { |
michael@0 | 205 | mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0); |
michael@0 | 206 | |
michael@0 | 207 | nsRect rect; |
michael@0 | 208 | mMathMLChar[mRadicalCharIndex].GetRect(rect); |
michael@0 | 209 | rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0); |
michael@0 | 210 | rect.SizeTo(mContentWidth, mRuleThickness); |
michael@0 | 211 | DisplayBar(aBuilder, this, rect, aLists); |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | if (IsToDraw(NOTATION_LONGDIV)) { |
michael@0 | 215 | mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1); |
michael@0 | 216 | |
michael@0 | 217 | nsRect rect; |
michael@0 | 218 | mMathMLChar[mLongDivCharIndex].GetRect(rect); |
michael@0 | 219 | rect.SizeTo(rect.width + mContentWidth, mRuleThickness); |
michael@0 | 220 | DisplayBar(aBuilder, this, rect, aLists); |
michael@0 | 221 | } |
michael@0 | 222 | |
michael@0 | 223 | if (IsToDraw(NOTATION_TOP)) { |
michael@0 | 224 | nsRect rect(0, 0, mencloseRect.width, mRuleThickness); |
michael@0 | 225 | DisplayBar(aBuilder, this, rect, aLists); |
michael@0 | 226 | } |
michael@0 | 227 | |
michael@0 | 228 | if (IsToDraw(NOTATION_BOTTOM)) { |
michael@0 | 229 | nsRect rect(0, mencloseRect.height - mRuleThickness, |
michael@0 | 230 | mencloseRect.width, mRuleThickness); |
michael@0 | 231 | DisplayBar(aBuilder, this, rect, aLists); |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | if (IsToDraw(NOTATION_LEFT)) { |
michael@0 | 235 | nsRect rect(0, 0, mRuleThickness, mencloseRect.height); |
michael@0 | 236 | DisplayBar(aBuilder, this, rect, aLists); |
michael@0 | 237 | } |
michael@0 | 238 | |
michael@0 | 239 | if (IsToDraw(NOTATION_RIGHT)) { |
michael@0 | 240 | nsRect rect(mencloseRect.width - mRuleThickness, 0, |
michael@0 | 241 | mRuleThickness, mencloseRect.height); |
michael@0 | 242 | DisplayBar(aBuilder, this, rect, aLists); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | if (IsToDraw(NOTATION_ROUNDEDBOX)) { |
michael@0 | 246 | DisplayNotation(aBuilder, this, mencloseRect, aLists, |
michael@0 | 247 | mRuleThickness, NOTATION_ROUNDEDBOX); |
michael@0 | 248 | } |
michael@0 | 249 | |
michael@0 | 250 | if (IsToDraw(NOTATION_CIRCLE)) { |
michael@0 | 251 | DisplayNotation(aBuilder, this, mencloseRect, aLists, |
michael@0 | 252 | mRuleThickness, NOTATION_CIRCLE); |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) { |
michael@0 | 256 | DisplayNotation(aBuilder, this, mencloseRect, aLists, |
michael@0 | 257 | mRuleThickness, NOTATION_UPDIAGONALSTRIKE); |
michael@0 | 258 | } |
michael@0 | 259 | |
michael@0 | 260 | if (IsToDraw(NOTATION_UPDIAGONALARROW)) { |
michael@0 | 261 | DisplayNotation(aBuilder, this, mencloseRect, aLists, |
michael@0 | 262 | mRuleThickness, NOTATION_UPDIAGONALARROW); |
michael@0 | 263 | } |
michael@0 | 264 | |
michael@0 | 265 | if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) { |
michael@0 | 266 | DisplayNotation(aBuilder, this, mencloseRect, aLists, |
michael@0 | 267 | mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE); |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) { |
michael@0 | 271 | nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2, |
michael@0 | 272 | mencloseRect.width, mRuleThickness); |
michael@0 | 273 | DisplayBar(aBuilder, this, rect, aLists); |
michael@0 | 274 | } |
michael@0 | 275 | |
michael@0 | 276 | if (IsToDraw(NOTATION_VERTICALSTRIKE)) { |
michael@0 | 277 | nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0, |
michael@0 | 278 | mRuleThickness, mencloseRect.height); |
michael@0 | 279 | DisplayBar(aBuilder, this, rect, aLists); |
michael@0 | 280 | } |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | /* virtual */ nsresult |
michael@0 | 284 | nsMathMLmencloseFrame::MeasureForWidth(nsRenderingContext& aRenderingContext, |
michael@0 | 285 | nsHTMLReflowMetrics& aDesiredSize) |
michael@0 | 286 | { |
michael@0 | 287 | return PlaceInternal(aRenderingContext, false, aDesiredSize, true); |
michael@0 | 288 | } |
michael@0 | 289 | |
michael@0 | 290 | /* virtual */ nsresult |
michael@0 | 291 | nsMathMLmencloseFrame::Place(nsRenderingContext& aRenderingContext, |
michael@0 | 292 | bool aPlaceOrigin, |
michael@0 | 293 | nsHTMLReflowMetrics& aDesiredSize) |
michael@0 | 294 | { |
michael@0 | 295 | return PlaceInternal(aRenderingContext, aPlaceOrigin, aDesiredSize, false); |
michael@0 | 296 | } |
michael@0 | 297 | |
michael@0 | 298 | /* virtual */ nsresult |
michael@0 | 299 | nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext, |
michael@0 | 300 | bool aPlaceOrigin, |
michael@0 | 301 | nsHTMLReflowMetrics& aDesiredSize, |
michael@0 | 302 | bool aWidthOnly) |
michael@0 | 303 | { |
michael@0 | 304 | /////////////// |
michael@0 | 305 | // Measure the size of our content using the base class to format like an |
michael@0 | 306 | // inferred mrow. |
michael@0 | 307 | nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode()); |
michael@0 | 308 | nsresult rv = |
michael@0 | 309 | nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize); |
michael@0 | 310 | |
michael@0 | 311 | if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { |
michael@0 | 312 | DidReflowChildren(GetFirstPrincipalChild()); |
michael@0 | 313 | return rv; |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | nsBoundingMetrics bmBase = baseSize.mBoundingMetrics; |
michael@0 | 317 | nscoord dx_left = 0, dx_right = 0; |
michael@0 | 318 | nsBoundingMetrics bmLongdivChar, bmRadicalChar; |
michael@0 | 319 | nscoord radicalAscent = 0, radicalDescent = 0; |
michael@0 | 320 | nscoord longdivAscent = 0, longdivDescent = 0; |
michael@0 | 321 | nscoord psi = 0; |
michael@0 | 322 | |
michael@0 | 323 | /////////////// |
michael@0 | 324 | // Thickness of bars and font metrics |
michael@0 | 325 | nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); |
michael@0 | 326 | |
michael@0 | 327 | nscoord mEmHeight; |
michael@0 | 328 | nsRefPtr<nsFontMetrics> fm; |
michael@0 | 329 | nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); |
michael@0 | 330 | aRenderingContext.SetFont(fm); |
michael@0 | 331 | GetRuleThickness(aRenderingContext, fm, mRuleThickness); |
michael@0 | 332 | GetEmHeight(fm, mEmHeight); |
michael@0 | 333 | |
michael@0 | 334 | char16_t one = '1'; |
michael@0 | 335 | nsBoundingMetrics bmOne = aRenderingContext.GetBoundingMetrics(&one, 1); |
michael@0 | 336 | |
michael@0 | 337 | /////////////// |
michael@0 | 338 | // General rules: the menclose element takes the size of the enclosed content. |
michael@0 | 339 | // We add a padding when needed. |
michael@0 | 340 | |
michael@0 | 341 | // determine padding & psi |
michael@0 | 342 | nscoord padding = 3 * mRuleThickness; |
michael@0 | 343 | nscoord delta = padding % onePixel; |
michael@0 | 344 | if (delta) |
michael@0 | 345 | padding += onePixel - delta; // round up |
michael@0 | 346 | |
michael@0 | 347 | if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { |
michael@0 | 348 | nscoord phi; |
michael@0 | 349 | // Rule 11, App. G, TeXbook |
michael@0 | 350 | // psi = clearance between rule and content |
michael@0 | 351 | if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) |
michael@0 | 352 | phi = fm->XHeight(); |
michael@0 | 353 | else |
michael@0 | 354 | phi = mRuleThickness; |
michael@0 | 355 | psi = mRuleThickness + phi / 4; |
michael@0 | 356 | |
michael@0 | 357 | delta = psi % onePixel; |
michael@0 | 358 | if (delta) |
michael@0 | 359 | psi += onePixel - delta; // round up |
michael@0 | 360 | } |
michael@0 | 361 | |
michael@0 | 362 | if (mRuleThickness < onePixel) |
michael@0 | 363 | mRuleThickness = onePixel; |
michael@0 | 364 | |
michael@0 | 365 | // Set horizontal parameters |
michael@0 | 366 | if (IsToDraw(NOTATION_ROUNDEDBOX) || |
michael@0 | 367 | IsToDraw(NOTATION_TOP) || |
michael@0 | 368 | IsToDraw(NOTATION_LEFT) || |
michael@0 | 369 | IsToDraw(NOTATION_BOTTOM) || |
michael@0 | 370 | IsToDraw(NOTATION_CIRCLE)) |
michael@0 | 371 | dx_left = padding; |
michael@0 | 372 | |
michael@0 | 373 | if (IsToDraw(NOTATION_ROUNDEDBOX) || |
michael@0 | 374 | IsToDraw(NOTATION_TOP) || |
michael@0 | 375 | IsToDraw(NOTATION_RIGHT) || |
michael@0 | 376 | IsToDraw(NOTATION_BOTTOM) || |
michael@0 | 377 | IsToDraw(NOTATION_CIRCLE)) |
michael@0 | 378 | dx_right = padding; |
michael@0 | 379 | |
michael@0 | 380 | // Set vertical parameters |
michael@0 | 381 | if (IsToDraw(NOTATION_RIGHT) || |
michael@0 | 382 | IsToDraw(NOTATION_LEFT) || |
michael@0 | 383 | IsToDraw(NOTATION_UPDIAGONALSTRIKE) || |
michael@0 | 384 | IsToDraw(NOTATION_UPDIAGONALARROW) || |
michael@0 | 385 | IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || |
michael@0 | 386 | IsToDraw(NOTATION_VERTICALSTRIKE) || |
michael@0 | 387 | IsToDraw(NOTATION_CIRCLE) || |
michael@0 | 388 | IsToDraw(NOTATION_ROUNDEDBOX) || |
michael@0 | 389 | IsToDraw(NOTATION_RADICAL) || |
michael@0 | 390 | IsToDraw(NOTATION_LONGDIV)) { |
michael@0 | 391 | // set a minimal value for the base height |
michael@0 | 392 | bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent); |
michael@0 | 393 | bmBase.descent = std::max(0, bmBase.descent); |
michael@0 | 394 | } |
michael@0 | 395 | |
michael@0 | 396 | mBoundingMetrics.ascent = bmBase.ascent; |
michael@0 | 397 | mBoundingMetrics.descent = bmBase.descent; |
michael@0 | 398 | |
michael@0 | 399 | if (IsToDraw(NOTATION_ROUNDEDBOX) || |
michael@0 | 400 | IsToDraw(NOTATION_TOP) || |
michael@0 | 401 | IsToDraw(NOTATION_LEFT) || |
michael@0 | 402 | IsToDraw(NOTATION_RIGHT) || |
michael@0 | 403 | IsToDraw(NOTATION_CIRCLE)) |
michael@0 | 404 | mBoundingMetrics.ascent += padding; |
michael@0 | 405 | |
michael@0 | 406 | if (IsToDraw(NOTATION_ROUNDEDBOX) || |
michael@0 | 407 | IsToDraw(NOTATION_LEFT) || |
michael@0 | 408 | IsToDraw(NOTATION_RIGHT) || |
michael@0 | 409 | IsToDraw(NOTATION_BOTTOM) || |
michael@0 | 410 | IsToDraw(NOTATION_CIRCLE)) |
michael@0 | 411 | mBoundingMetrics.descent += padding; |
michael@0 | 412 | |
michael@0 | 413 | /////////////// |
michael@0 | 414 | // updiagonal arrow notation. We need enough space at the top right corner to |
michael@0 | 415 | // draw the arrow head. |
michael@0 | 416 | if (IsToDraw(NOTATION_UPDIAGONALARROW)) { |
michael@0 | 417 | // This is an estimate, see nsDisplayNotation::Paint for the exact head size |
michael@0 | 418 | nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness; |
michael@0 | 419 | |
michael@0 | 420 | // We want that the arrow shaft strikes the menclose content and that the |
michael@0 | 421 | // arrow head does not overlap with that content. Hence we add some space |
michael@0 | 422 | // on the right. We don't add space on the top but only ensure that the |
michael@0 | 423 | // ascent is large enough. |
michael@0 | 424 | dx_right = std::max(dx_right, arrowHeadSize); |
michael@0 | 425 | mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize); |
michael@0 | 426 | } |
michael@0 | 427 | |
michael@0 | 428 | /////////////// |
michael@0 | 429 | // circle notation: we don't want the ellipse to overlap the enclosed |
michael@0 | 430 | // content. Hence, we need to increase the size of the bounding box by a |
michael@0 | 431 | // factor of at least sqrt(2). |
michael@0 | 432 | if (IsToDraw(NOTATION_CIRCLE)) { |
michael@0 | 433 | double ratio = (sqrt(2.0) - 1.0) / 2.0; |
michael@0 | 434 | nscoord padding2; |
michael@0 | 435 | |
michael@0 | 436 | // Update horizontal parameters |
michael@0 | 437 | padding2 = ratio * bmBase.width; |
michael@0 | 438 | |
michael@0 | 439 | dx_left = std::max(dx_left, padding2); |
michael@0 | 440 | dx_right = std::max(dx_right, padding2); |
michael@0 | 441 | |
michael@0 | 442 | // Update vertical parameters |
michael@0 | 443 | padding2 = ratio * (bmBase.ascent + bmBase.descent); |
michael@0 | 444 | |
michael@0 | 445 | mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, |
michael@0 | 446 | bmBase.ascent + padding2); |
michael@0 | 447 | mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, |
michael@0 | 448 | bmBase.descent + padding2); |
michael@0 | 449 | } |
michael@0 | 450 | |
michael@0 | 451 | /////////////// |
michael@0 | 452 | // longdiv notation: |
michael@0 | 453 | if (IsToDraw(NOTATION_LONGDIV)) { |
michael@0 | 454 | if (aWidthOnly) { |
michael@0 | 455 | nscoord longdiv_width = mMathMLChar[mLongDivCharIndex]. |
michael@0 | 456 | GetMaxWidth(PresContext(), aRenderingContext); |
michael@0 | 457 | |
michael@0 | 458 | // Update horizontal parameters |
michael@0 | 459 | dx_left = std::max(dx_left, longdiv_width); |
michael@0 | 460 | } else { |
michael@0 | 461 | // Stretch the parenthesis to the appropriate height if it is not |
michael@0 | 462 | // big enough. |
michael@0 | 463 | nsBoundingMetrics contSize = bmBase; |
michael@0 | 464 | contSize.ascent = mRuleThickness; |
michael@0 | 465 | contSize.descent = bmBase.ascent + bmBase.descent + psi; |
michael@0 | 466 | |
michael@0 | 467 | // height(longdiv) should be >= height(base) + psi + mRuleThickness |
michael@0 | 468 | mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext, |
michael@0 | 469 | NS_STRETCH_DIRECTION_VERTICAL, |
michael@0 | 470 | contSize, bmLongdivChar, |
michael@0 | 471 | NS_STRETCH_LARGER, false); |
michael@0 | 472 | mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar); |
michael@0 | 473 | |
michael@0 | 474 | // Update horizontal parameters |
michael@0 | 475 | dx_left = std::max(dx_left, bmLongdivChar.width); |
michael@0 | 476 | |
michael@0 | 477 | // Update vertical parameters |
michael@0 | 478 | longdivAscent = bmBase.ascent + psi + mRuleThickness; |
michael@0 | 479 | longdivDescent = std::max(bmBase.descent, |
michael@0 | 480 | (bmLongdivChar.ascent + bmLongdivChar.descent - |
michael@0 | 481 | longdivAscent)); |
michael@0 | 482 | |
michael@0 | 483 | mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, |
michael@0 | 484 | longdivAscent); |
michael@0 | 485 | mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, |
michael@0 | 486 | longdivDescent); |
michael@0 | 487 | } |
michael@0 | 488 | } |
michael@0 | 489 | |
michael@0 | 490 | /////////////// |
michael@0 | 491 | // radical notation: |
michael@0 | 492 | if (IsToDraw(NOTATION_RADICAL)) { |
michael@0 | 493 | nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left; |
michael@0 | 494 | |
michael@0 | 495 | if (aWidthOnly) { |
michael@0 | 496 | nscoord radical_width = mMathMLChar[mRadicalCharIndex]. |
michael@0 | 497 | GetMaxWidth(PresContext(), aRenderingContext); |
michael@0 | 498 | |
michael@0 | 499 | // Update horizontal parameters |
michael@0 | 500 | *dx_leading = std::max(*dx_leading, radical_width); |
michael@0 | 501 | } else { |
michael@0 | 502 | // Stretch the radical symbol to the appropriate height if it is not |
michael@0 | 503 | // big enough. |
michael@0 | 504 | nsBoundingMetrics contSize = bmBase; |
michael@0 | 505 | contSize.ascent = mRuleThickness; |
michael@0 | 506 | contSize.descent = bmBase.ascent + bmBase.descent + psi; |
michael@0 | 507 | |
michael@0 | 508 | // height(radical) should be >= height(base) + psi + mRuleThickness |
michael@0 | 509 | mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext, |
michael@0 | 510 | NS_STRETCH_DIRECTION_VERTICAL, |
michael@0 | 511 | contSize, bmRadicalChar, |
michael@0 | 512 | NS_STRETCH_LARGER, |
michael@0 | 513 | StyleVisibility()->mDirection); |
michael@0 | 514 | mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar); |
michael@0 | 515 | |
michael@0 | 516 | // Update horizontal parameters |
michael@0 | 517 | *dx_leading = std::max(*dx_leading, bmRadicalChar.width); |
michael@0 | 518 | |
michael@0 | 519 | // Update vertical parameters |
michael@0 | 520 | radicalAscent = bmBase.ascent + psi + mRuleThickness; |
michael@0 | 521 | radicalDescent = std::max(bmBase.descent, |
michael@0 | 522 | (bmRadicalChar.ascent + bmRadicalChar.descent - |
michael@0 | 523 | radicalAscent)); |
michael@0 | 524 | |
michael@0 | 525 | mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, |
michael@0 | 526 | radicalAscent); |
michael@0 | 527 | mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, |
michael@0 | 528 | radicalDescent); |
michael@0 | 529 | } |
michael@0 | 530 | } |
michael@0 | 531 | |
michael@0 | 532 | /////////////// |
michael@0 | 533 | // |
michael@0 | 534 | if (IsToDraw(NOTATION_CIRCLE) || |
michael@0 | 535 | IsToDraw(NOTATION_ROUNDEDBOX) || |
michael@0 | 536 | (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) { |
michael@0 | 537 | // center the menclose around the content (horizontally) |
michael@0 | 538 | dx_left = dx_right = std::max(dx_left, dx_right); |
michael@0 | 539 | } |
michael@0 | 540 | |
michael@0 | 541 | /////////////// |
michael@0 | 542 | // The maximum size is now computed: set the remaining parameters |
michael@0 | 543 | mBoundingMetrics.width = dx_left + bmBase.width + dx_right; |
michael@0 | 544 | |
michael@0 | 545 | mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing); |
michael@0 | 546 | mBoundingMetrics.rightBearing = |
michael@0 | 547 | std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing); |
michael@0 | 548 | |
michael@0 | 549 | aDesiredSize.Width() = mBoundingMetrics.width; |
michael@0 | 550 | |
michael@0 | 551 | aDesiredSize.SetTopAscent(std::max(mBoundingMetrics.ascent, baseSize.TopAscent())); |
michael@0 | 552 | aDesiredSize.Height() = aDesiredSize.TopAscent() + |
michael@0 | 553 | std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.TopAscent()); |
michael@0 | 554 | |
michael@0 | 555 | if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { |
michael@0 | 556 | // get the leading to be left at the top of the resulting frame |
michael@0 | 557 | // this seems more reliable than using fm->GetLeading() on suspicious |
michael@0 | 558 | // fonts |
michael@0 | 559 | nscoord leading = nscoord(0.2f * mEmHeight); |
michael@0 | 560 | nscoord desiredSizeAscent = aDesiredSize.TopAscent(); |
michael@0 | 561 | nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.TopAscent(); |
michael@0 | 562 | |
michael@0 | 563 | if (IsToDraw(NOTATION_LONGDIV)) { |
michael@0 | 564 | desiredSizeAscent = std::max(desiredSizeAscent, |
michael@0 | 565 | longdivAscent + leading); |
michael@0 | 566 | desiredSizeDescent = std::max(desiredSizeDescent, |
michael@0 | 567 | longdivDescent + mRuleThickness); |
michael@0 | 568 | } |
michael@0 | 569 | |
michael@0 | 570 | if (IsToDraw(NOTATION_RADICAL)) { |
michael@0 | 571 | desiredSizeAscent = std::max(desiredSizeAscent, |
michael@0 | 572 | radicalAscent + leading); |
michael@0 | 573 | desiredSizeDescent = std::max(desiredSizeDescent, |
michael@0 | 574 | radicalDescent + mRuleThickness); |
michael@0 | 575 | } |
michael@0 | 576 | |
michael@0 | 577 | aDesiredSize.SetTopAscent(desiredSizeAscent); |
michael@0 | 578 | aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent; |
michael@0 | 579 | } |
michael@0 | 580 | |
michael@0 | 581 | if (IsToDraw(NOTATION_CIRCLE) || |
michael@0 | 582 | IsToDraw(NOTATION_ROUNDEDBOX) || |
michael@0 | 583 | (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) { |
michael@0 | 584 | // center the menclose around the content (vertically) |
michael@0 | 585 | nscoord dy = std::max(aDesiredSize.TopAscent() - bmBase.ascent, |
michael@0 | 586 | aDesiredSize.Height() - aDesiredSize.TopAscent() - |
michael@0 | 587 | bmBase.descent); |
michael@0 | 588 | |
michael@0 | 589 | aDesiredSize.SetTopAscent(bmBase.ascent + dy); |
michael@0 | 590 | aDesiredSize.Height() = aDesiredSize.TopAscent() + bmBase.descent + dy; |
michael@0 | 591 | } |
michael@0 | 592 | |
michael@0 | 593 | // Update mBoundingMetrics ascent/descent |
michael@0 | 594 | if (IsToDraw(NOTATION_TOP) || |
michael@0 | 595 | IsToDraw(NOTATION_RIGHT) || |
michael@0 | 596 | IsToDraw(NOTATION_LEFT) || |
michael@0 | 597 | IsToDraw(NOTATION_UPDIAGONALSTRIKE) || |
michael@0 | 598 | IsToDraw(NOTATION_UPDIAGONALARROW) || |
michael@0 | 599 | IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || |
michael@0 | 600 | IsToDraw(NOTATION_VERTICALSTRIKE) || |
michael@0 | 601 | IsToDraw(NOTATION_CIRCLE) || |
michael@0 | 602 | IsToDraw(NOTATION_ROUNDEDBOX)) |
michael@0 | 603 | mBoundingMetrics.ascent = aDesiredSize.TopAscent(); |
michael@0 | 604 | |
michael@0 | 605 | if (IsToDraw(NOTATION_BOTTOM) || |
michael@0 | 606 | IsToDraw(NOTATION_RIGHT) || |
michael@0 | 607 | IsToDraw(NOTATION_LEFT) || |
michael@0 | 608 | IsToDraw(NOTATION_UPDIAGONALSTRIKE) || |
michael@0 | 609 | IsToDraw(NOTATION_UPDIAGONALARROW) || |
michael@0 | 610 | IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || |
michael@0 | 611 | IsToDraw(NOTATION_VERTICALSTRIKE) || |
michael@0 | 612 | IsToDraw(NOTATION_CIRCLE) || |
michael@0 | 613 | IsToDraw(NOTATION_ROUNDEDBOX)) |
michael@0 | 614 | mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent(); |
michael@0 | 615 | |
michael@0 | 616 | aDesiredSize.mBoundingMetrics = mBoundingMetrics; |
michael@0 | 617 | |
michael@0 | 618 | mReference.x = 0; |
michael@0 | 619 | mReference.y = aDesiredSize.TopAscent(); |
michael@0 | 620 | |
michael@0 | 621 | if (aPlaceOrigin) { |
michael@0 | 622 | ////////////////// |
michael@0 | 623 | // Set position and size of MathMLChars |
michael@0 | 624 | if (IsToDraw(NOTATION_LONGDIV)) |
michael@0 | 625 | mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left - |
michael@0 | 626 | bmLongdivChar.width, |
michael@0 | 627 | aDesiredSize.TopAscent() - |
michael@0 | 628 | longdivAscent, |
michael@0 | 629 | bmLongdivChar.width, |
michael@0 | 630 | bmLongdivChar.ascent + |
michael@0 | 631 | bmLongdivChar.descent)); |
michael@0 | 632 | |
michael@0 | 633 | if (IsToDraw(NOTATION_RADICAL)) { |
michael@0 | 634 | nscoord dx = (StyleVisibility()->mDirection ? |
michael@0 | 635 | dx_left + bmBase.width : dx_left - bmRadicalChar.width); |
michael@0 | 636 | |
michael@0 | 637 | mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx, |
michael@0 | 638 | aDesiredSize.TopAscent() - |
michael@0 | 639 | radicalAscent, |
michael@0 | 640 | bmRadicalChar.width, |
michael@0 | 641 | bmRadicalChar.ascent + |
michael@0 | 642 | bmRadicalChar.descent)); |
michael@0 | 643 | } |
michael@0 | 644 | |
michael@0 | 645 | mContentWidth = bmBase.width; |
michael@0 | 646 | |
michael@0 | 647 | ////////////////// |
michael@0 | 648 | // Finish reflowing child frames |
michael@0 | 649 | PositionRowChildFrames(dx_left, aDesiredSize.TopAscent()); |
michael@0 | 650 | } |
michael@0 | 651 | |
michael@0 | 652 | return NS_OK; |
michael@0 | 653 | } |
michael@0 | 654 | |
michael@0 | 655 | nscoord |
michael@0 | 656 | nsMathMLmencloseFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize) |
michael@0 | 657 | { |
michael@0 | 658 | nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize); |
michael@0 | 659 | if (!gap) |
michael@0 | 660 | return 0; |
michael@0 | 661 | |
michael@0 | 662 | // Move the MathML characters |
michael@0 | 663 | nsRect rect; |
michael@0 | 664 | for (uint32_t i = 0; i < mMathMLChar.Length(); i++) { |
michael@0 | 665 | mMathMLChar[i].GetRect(rect); |
michael@0 | 666 | rect.MoveBy(gap, 0); |
michael@0 | 667 | mMathMLChar[i].SetRect(rect); |
michael@0 | 668 | } |
michael@0 | 669 | |
michael@0 | 670 | return gap; |
michael@0 | 671 | } |
michael@0 | 672 | |
michael@0 | 673 | nsresult |
michael@0 | 674 | nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID, |
michael@0 | 675 | nsIAtom* aAttribute, |
michael@0 | 676 | int32_t aModType) |
michael@0 | 677 | { |
michael@0 | 678 | if (aAttribute == nsGkAtoms::notation_) { |
michael@0 | 679 | InitNotations(); |
michael@0 | 680 | } |
michael@0 | 681 | |
michael@0 | 682 | return nsMathMLContainerFrame:: |
michael@0 | 683 | AttributeChanged(aNameSpaceID, aAttribute, aModType); |
michael@0 | 684 | } |
michael@0 | 685 | |
michael@0 | 686 | ////////////////// |
michael@0 | 687 | // the Style System will use these to pass the proper style context to our |
michael@0 | 688 | // MathMLChar |
michael@0 | 689 | nsStyleContext* |
michael@0 | 690 | nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const |
michael@0 | 691 | { |
michael@0 | 692 | int32_t len = mMathMLChar.Length(); |
michael@0 | 693 | if (aIndex >= 0 && aIndex < len) |
michael@0 | 694 | return mMathMLChar[aIndex].GetStyleContext(); |
michael@0 | 695 | else |
michael@0 | 696 | return nullptr; |
michael@0 | 697 | } |
michael@0 | 698 | |
michael@0 | 699 | void |
michael@0 | 700 | nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex, |
michael@0 | 701 | nsStyleContext* aStyleContext) |
michael@0 | 702 | { |
michael@0 | 703 | int32_t len = mMathMLChar.Length(); |
michael@0 | 704 | if (aIndex >= 0 && aIndex < len) |
michael@0 | 705 | mMathMLChar[aIndex].SetStyleContext(aStyleContext); |
michael@0 | 706 | } |
michael@0 | 707 | |
michael@0 | 708 | class nsDisplayNotation : public nsDisplayItem |
michael@0 | 709 | { |
michael@0 | 710 | public: |
michael@0 | 711 | nsDisplayNotation(nsDisplayListBuilder* aBuilder, |
michael@0 | 712 | nsIFrame* aFrame, const nsRect& aRect, |
michael@0 | 713 | nscoord aThickness, nsMencloseNotation aType) |
michael@0 | 714 | : nsDisplayItem(aBuilder, aFrame), mRect(aRect), |
michael@0 | 715 | mThickness(aThickness), mType(aType) { |
michael@0 | 716 | MOZ_COUNT_CTOR(nsDisplayNotation); |
michael@0 | 717 | } |
michael@0 | 718 | #ifdef NS_BUILD_REFCNT_LOGGING |
michael@0 | 719 | virtual ~nsDisplayNotation() { |
michael@0 | 720 | MOZ_COUNT_DTOR(nsDisplayNotation); |
michael@0 | 721 | } |
michael@0 | 722 | #endif |
michael@0 | 723 | |
michael@0 | 724 | virtual void Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 725 | nsRenderingContext* aCtx) MOZ_OVERRIDE; |
michael@0 | 726 | NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION) |
michael@0 | 727 | |
michael@0 | 728 | private: |
michael@0 | 729 | nsRect mRect; |
michael@0 | 730 | nscoord mThickness; |
michael@0 | 731 | nsMencloseNotation mType; |
michael@0 | 732 | }; |
michael@0 | 733 | |
michael@0 | 734 | void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder, |
michael@0 | 735 | nsRenderingContext* aCtx) |
michael@0 | 736 | { |
michael@0 | 737 | // get the gfxRect |
michael@0 | 738 | nsPresContext* presContext = mFrame->PresContext(); |
michael@0 | 739 | gfxRect rect = presContext->AppUnitsToGfxUnits(mRect + ToReferenceFrame()); |
michael@0 | 740 | |
michael@0 | 741 | // paint the frame with the current text color |
michael@0 | 742 | aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color)); |
michael@0 | 743 | |
michael@0 | 744 | // change line width to mThickness |
michael@0 | 745 | gfxContext *gfxCtx = aCtx->ThebesContext(); |
michael@0 | 746 | gfxFloat e = presContext->AppUnitsToGfxUnits(mThickness); |
michael@0 | 747 | gfxCtx->Save(); |
michael@0 | 748 | gfxCtx->SetLineWidth(e); |
michael@0 | 749 | |
michael@0 | 750 | rect.Deflate(e / 2.0); |
michael@0 | 751 | |
michael@0 | 752 | switch(mType) |
michael@0 | 753 | { |
michael@0 | 754 | case NOTATION_CIRCLE: |
michael@0 | 755 | gfxCtx->NewPath(); |
michael@0 | 756 | gfxCtx->Ellipse(rect.Center(), rect.Size()); |
michael@0 | 757 | gfxCtx->Stroke(); |
michael@0 | 758 | break; |
michael@0 | 759 | |
michael@0 | 760 | case NOTATION_ROUNDEDBOX: |
michael@0 | 761 | gfxCtx->NewPath(); |
michael@0 | 762 | gfxCtx->RoundedRectangle(rect, gfxCornerSizes(3 * e), true); |
michael@0 | 763 | gfxCtx->Stroke(); |
michael@0 | 764 | break; |
michael@0 | 765 | |
michael@0 | 766 | case NOTATION_UPDIAGONALSTRIKE: |
michael@0 | 767 | gfxCtx->NewPath(); |
michael@0 | 768 | gfxCtx->Line(rect.BottomLeft(), rect.TopRight()); |
michael@0 | 769 | gfxCtx->Stroke(); |
michael@0 | 770 | break; |
michael@0 | 771 | |
michael@0 | 772 | case NOTATION_DOWNDIAGONALSTRIKE: |
michael@0 | 773 | gfxCtx->NewPath(); |
michael@0 | 774 | gfxCtx->Line(rect.TopLeft(), rect.BottomRight()); |
michael@0 | 775 | gfxCtx->Stroke(); |
michael@0 | 776 | break; |
michael@0 | 777 | |
michael@0 | 778 | case NOTATION_UPDIAGONALARROW: { |
michael@0 | 779 | // Compute some parameters to draw the updiagonalarrow. The values below |
michael@0 | 780 | // are taken from MathJax's HTML-CSS output. |
michael@0 | 781 | gfxFloat W = rect.Width(); gfxFloat H = rect.Height(); |
michael@0 | 782 | gfxFloat l = sqrt(W*W + H*H); |
michael@0 | 783 | gfxFloat f = gfxFloat(kArrowHeadSize) * e / l; |
michael@0 | 784 | gfxFloat w = W * f; gfxFloat h = H * f; |
michael@0 | 785 | |
michael@0 | 786 | // Draw the arrow shaft |
michael@0 | 787 | gfxCtx->NewPath(); |
michael@0 | 788 | gfxCtx->Line(rect.BottomLeft(), rect.TopRight() + gfxPoint(-.7*w, .7*h)); |
michael@0 | 789 | gfxCtx->Stroke(); |
michael@0 | 790 | |
michael@0 | 791 | // Draw the arrow head |
michael@0 | 792 | gfxCtx->NewPath(); |
michael@0 | 793 | gfxPoint p[] = { |
michael@0 | 794 | rect.TopRight(), |
michael@0 | 795 | rect.TopRight() + gfxPoint(-w -.4*h, std::max(-e / 2.0, h - .4*w)), |
michael@0 | 796 | rect.TopRight() + gfxPoint(-.7*w, .7*h), |
michael@0 | 797 | rect.TopRight() + gfxPoint(std::min(e / 2.0, -w + .4*h), h + .4*w), |
michael@0 | 798 | rect.TopRight() |
michael@0 | 799 | }; |
michael@0 | 800 | gfxCtx->Polygon(p, MOZ_ARRAY_LENGTH(p)); |
michael@0 | 801 | gfxCtx->Fill(); |
michael@0 | 802 | } |
michael@0 | 803 | break; |
michael@0 | 804 | |
michael@0 | 805 | default: |
michael@0 | 806 | NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation"); |
michael@0 | 807 | break; |
michael@0 | 808 | } |
michael@0 | 809 | |
michael@0 | 810 | gfxCtx->Restore(); |
michael@0 | 811 | } |
michael@0 | 812 | |
michael@0 | 813 | void |
michael@0 | 814 | nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder, |
michael@0 | 815 | nsIFrame* aFrame, const nsRect& aRect, |
michael@0 | 816 | const nsDisplayListSet& aLists, |
michael@0 | 817 | nscoord aThickness, |
michael@0 | 818 | nsMencloseNotation aType) |
michael@0 | 819 | { |
michael@0 | 820 | if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() || |
michael@0 | 821 | aThickness <= 0) |
michael@0 | 822 | return; |
michael@0 | 823 | |
michael@0 | 824 | aLists.Content()->AppendNewToTop(new (aBuilder) |
michael@0 | 825 | nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType)); |
michael@0 | 826 | } |