~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/layout/mathml/base/src/nsMathMLmtableFrame.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/*
 
3
 * The contents of this file are subject to the Mozilla Public
 
4
 * License Version 1.1 (the "License"); you may not use this file
 
5
 * except in compliance with the License. You may obtain a copy of
 
6
 * the License at http://www.mozilla.org/MPL/
 
7
 *
 
8
 * Software distributed under the License is distributed on an "AS
 
9
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 
10
 * implied. See the License for the specific language governing
 
11
 * rights and limitations under the License.
 
12
 *
 
13
 * The Original Code is Mozilla MathML Project.
 
14
 *
 
15
 * The Initial Developer of the Original Code is The University Of
 
16
 * Queensland.  Portions created by The University Of Queensland are
 
17
 * Copyright (C) 1999 The University Of Queensland.  All Rights Reserved.
 
18
 *
 
19
 * Contributor(s):
 
20
 *   Roger B. Sidje <rbs@maths.uq.edu.au>
 
21
 */
 
22
 
 
23
#include "nsCOMPtr.h"
 
24
#include "nsFrame.h"
 
25
#include "nsAreaFrame.h"
 
26
#include "nsIPresContext.h"
 
27
#include "nsUnitConversion.h"
 
28
#include "nsStyleContext.h"
 
29
#include "nsStyleConsts.h"
 
30
#include "nsINameSpaceManager.h"
 
31
#include "nsIRenderingContext.h"
 
32
#include "nsIFontMetrics.h"
 
33
 
 
34
#include "nsVoidArray.h"
 
35
#include "nsFrameManager.h"
 
36
#include "nsStyleChangeList.h"
 
37
#include "nsTableOuterFrame.h"
 
38
#include "nsTableFrame.h"
 
39
#include "nsTableCellFrame.h"
 
40
 
 
41
#include "nsMathMLmtableFrame.h"
 
42
 
 
43
//
 
44
// <mtable> -- table or matrix - implementation
 
45
//
 
46
 
 
47
// helper function to perform an in-place split of a space-delimited string,
 
48
// and return an array of pointers for the beginning of each segment, i.e.,
 
49
// aOffset[0] is the first string, aOffset[1] is the second string, etc.
 
50
// Used to parse attributes like columnalign='left right', rowalign='top bottom'
 
51
static void
 
52
SplitString(nsString&    aString, // [IN/OUT]
 
53
            nsVoidArray& aOffset) // [OUT]
 
54
{
 
55
  static const PRUnichar kNullCh = PRUnichar('\0');
 
56
 
 
57
  aString.Append(kNullCh);  // put an extra null at the end
 
58
 
 
59
  PRUnichar* start = aString.BeginWriting();
 
60
  PRUnichar* end   = start;
 
61
 
 
62
  while (kNullCh != *start) {
 
63
    while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) {  // skip leading space
 
64
      start++;
 
65
    }
 
66
    end = start;
 
67
 
 
68
    while ((kNullCh != *end) && (PR_FALSE == nsCRT::IsAsciiSpace(*end))) { // look for space or end
 
69
      end++;
 
70
    }
 
71
    *end = kNullCh; // end string here
 
72
 
 
73
    if (start < end) {
 
74
      aOffset.AppendElement(start); // record the beginning of this segment
 
75
    }
 
76
 
 
77
    start = ++end;
 
78
  }
 
79
}
 
80
 
 
81
struct nsValueList
 
82
{
 
83
  nsString    mData;
 
84
  nsVoidArray mArray;
 
85
 
 
86
  nsValueList(nsString& aData) {
 
87
    mData.Assign(aData);
 
88
    SplitString(mData, mArray);
 
89
  }
 
90
};
 
91
 
 
92
// Each rowalign='top bottom' or columnalign='left right center' (from
 
93
// <mtable> or <mtr>) is split once (lazily) into a nsValueList which is
 
94
// stored in the frame manager. Cell frames query the frame manager
 
95
// to see what values apply to them.
 
96
 
 
97
// XXX See bug 69409 - MathML attributes are not mapped to style.
 
98
// This code is not suitable for dynamic updates, for example when the
 
99
// rowalign and columalign attributes are changed with JavaScript.
 
100
// The code doesn't include hooks for AttributeChanged() notifications.
 
101
 
 
102
static void
 
103
DestroyValueListFunc(nsIPresContext* aPresContext,
 
104
                     nsIFrame*       aFrame,
 
105
                     nsIAtom*        aPropertyName,
 
106
                     void*           aPropertyValue)
 
107
{
 
108
  delete NS_STATIC_CAST(nsValueList*, aPropertyValue);
 
109
}
 
110
 
 
111
static PRUnichar*
 
112
GetValueAt(nsIPresContext* aPresContext,
 
113
           nsIFrame*       aTableOrRowFrame,
 
114
           nsIAtom*        aAttributeAtom,
 
115
           PRInt32         aRowOrColIndex)
 
116
{
 
117
  PRUnichar* result = nsnull;
 
118
  nsValueList* valueList;
 
119
 
 
120
  nsFrameManager *frameManager = aPresContext->FrameManager();
 
121
  valueList = NS_STATIC_CAST(nsValueList*,
 
122
          frameManager->GetFrameProperty(aTableOrRowFrame, aAttributeAtom, 0));
 
123
 
 
124
  if (!valueList) {
 
125
    // The property isn't there yet, so set it
 
126
    nsAutoString values;
 
127
    if (NS_CONTENT_ATTR_HAS_VALUE ==
 
128
        aTableOrRowFrame->GetContent()->GetAttr(kNameSpaceID_None, aAttributeAtom, values)) {
 
129
      valueList = new nsValueList(values);
 
130
      if (valueList) {
 
131
        frameManager->SetFrameProperty(aTableOrRowFrame, aAttributeAtom,
 
132
                                       valueList, DestroyValueListFunc);
 
133
      }
 
134
    }
 
135
  }
 
136
 
 
137
  if (valueList) {
 
138
    PRInt32 count = valueList->mArray.Count();
 
139
    result = (aRowOrColIndex < count)
 
140
           ? (PRUnichar*)(valueList->mArray[aRowOrColIndex])
 
141
           : (PRUnichar*)(valueList->mArray[count-1]);
 
142
  }
 
143
  return result;
 
144
}
 
145
 
 
146
#ifdef NS_DEBUG
 
147
#define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
 
148
  NS_ASSERTION(NS_STYLE_DISPLAY_##_expected == _frame->GetStyleDisplay()->mDisplay, "internal error");
 
149
#else
 
150
#define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
 
151
#endif
 
152
 
 
153
static void
 
154
MapAttributesInto(nsIPresContext* aPresContext,
 
155
                  nsIContent*     aCellContent,
 
156
                  nsIFrame*       aCellFrame,
 
157
                  nsIFrame*       aCellInnerFrame)
 
158
{
 
159
  nsTableCellFrame* cellFrame = NS_STATIC_CAST(nsTableCellFrame*, aCellFrame);
 
160
  nsTableCellFrame* sibling;
 
161
 
 
162
  PRInt32 rowIndex, colIndex;
 
163
  nsresult rv = cellFrame->GetCellIndexes(rowIndex, colIndex);
 
164
  NS_ASSERTION(NS_SUCCEEDED(rv), "cannot find the position of the cell frame");
 
165
  if (NS_FAILED(rv)) return;
 
166
 
 
167
  nsIFrame* rowFrame = cellFrame->GetParent();
 
168
  nsIFrame* rowgroupFrame = rowFrame->GetParent();
 
169
  nsIFrame* tableFrame = rowgroupFrame->GetParent();
 
170
  DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
 
171
  DEBUG_VERIFY_THAT_FRAME_IS(rowgroupFrame, TABLE_ROW_GROUP);
 
172
  DEBUG_VERIFY_THAT_FRAME_IS(tableFrame, TABLE);
 
173
#ifdef NS_DEBUG
 
174
  PRBool originates;
 
175
  ((nsTableFrame*)tableFrame)->GetCellInfoAt(rowIndex, colIndex, &originates);
 
176
  NS_ASSERTION(originates, "internal error");
 
177
#endif
 
178
 
 
179
  nsIAtom* atom;
 
180
  PRUnichar* attr;
 
181
  nsAutoString value;
 
182
  PRBool hasChanged = PR_FALSE;
 
183
  NS_NAMED_LITERAL_STRING(trueStr, "true");
 
184
 
 
185
  //////////////////////////////////////
 
186
  // process attributes that depend on the index of the row:
 
187
  // rowalign, rowlines
 
188
 
 
189
  // see if the rowalign attribute is not already set
 
190
  atom = nsMathMLAtoms::rowalign_;
 
191
  rv = aCellContent->GetAttr(kNameSpaceID_None, atom, value);
 
192
  if (NS_CONTENT_ATTR_NOT_THERE == rv) {
 
193
    // see if the rowalign attribute was specified on the row
 
194
    attr = GetValueAt(aPresContext, rowFrame, atom, rowIndex);
 
195
    if (!attr) {
 
196
        // see if the rowalign attribute was specified on the table
 
197
      attr = GetValueAt(aPresContext, tableFrame, atom, rowIndex);
 
198
    }
 
199
    // set the attribute without notifying that we want a reflow
 
200
    if (attr) {
 
201
      hasChanged = PR_TRUE;
 
202
      aCellContent->SetAttr(kNameSpaceID_None, atom, nsDependentString(attr), PR_FALSE);
 
203
    }
 
204
  }
 
205
  // if we are not on the first row, see if |rowlines| was specified on the table.
 
206
  // Note that we pass 'rowIndex-1' because the CSS rule in mathml.css is associated
 
207
  // to 'border-top', and it is as if we draw the line on behalf of the previous cell.
 
208
  // This way of doing so allows us to handle selective lines, [row]\hline[row][row]',
 
209
  // and cases of spanning cells without further complications.
 
210
  if (rowIndex > 0) {
 
211
    attr = GetValueAt(aPresContext, tableFrame, nsMathMLAtoms::rowlines_, rowIndex-1);
 
212
    // set the special -moz-math-rowline without notifying that we want a reflow
 
213
    if (attr) {
 
214
      hasChanged = PR_TRUE;
 
215
      aCellContent->SetAttr(kNameSpaceID_None, nsMathMLAtoms::rowline, nsDependentString(attr), PR_FALSE);
 
216
    }
 
217
  }
 
218
  else {
 
219
    // set the special -moz-math-firstrow to annotate that we are on the first row
 
220
    hasChanged = PR_TRUE;
 
221
    aCellContent->SetAttr(kNameSpaceID_None, nsMathMLAtoms::firstrow, trueStr, PR_FALSE);
 
222
  }
 
223
  // if we are on the last row, set the special -moz-math-lastrow
 
224
  PRInt32 rowSpan = ((nsTableFrame*)tableFrame)->GetEffectiveRowSpan(*cellFrame);
 
225
  sibling = ((nsTableFrame*)tableFrame)->GetCellFrameAt(rowIndex+rowSpan, colIndex);
 
226
  if (!sibling) {
 
227
    hasChanged = PR_TRUE;
 
228
    aCellContent->SetAttr(kNameSpaceID_None, nsMathMLAtoms::lastrow, trueStr, PR_FALSE);
 
229
  }
 
230
 
 
231
  //////////////////////////////////////
 
232
  // process attributes that depend on the index of the column:
 
233
  // columnalign, columnlines, XXX need columnwidth too
 
234
 
 
235
  // see if the columnalign attribute is not already set
 
236
  atom = nsMathMLAtoms::columnalign_;
 
237
  rv = aCellContent->GetAttr(kNameSpaceID_None, atom, value);
 
238
  if (NS_CONTENT_ATTR_NOT_THERE == rv) {
 
239
    // see if the columnalign attribute was specified on the row
 
240
    attr = GetValueAt(aPresContext, rowFrame, atom, colIndex);
 
241
    if (!attr) {
 
242
        // see if the columnalign attribute was specified on the table
 
243
      attr = GetValueAt(aPresContext, tableFrame, atom, colIndex);
 
244
    }
 
245
    if (attr) {
 
246
      hasChanged = PR_TRUE;
 
247
      aCellContent->SetAttr(kNameSpaceID_None, atom, nsDependentString(attr), PR_FALSE);
 
248
    }
 
249
  }
 
250
  // if we are not on the first column, see if |columnlines| was specified on
 
251
  // the table. Note that we pass 'colIndex-1' because the CSS rule in mathml.css
 
252
  // is associated to 'border-left', and it is as if we draw the line on behalf
 
253
  // of the previous cell. This way of doing so allows us to handle selective lines,
 
254
  // e.g., 'r|cl', and cases of spanning cells without further complications.
 
255
  if (colIndex > 0) {
 
256
    attr = GetValueAt(aPresContext, tableFrame, nsMathMLAtoms::columnlines_, colIndex-1);
 
257
    // set the special -moz-math-columnline without notifying that we want a reflow
 
258
    if (attr) {
 
259
      hasChanged = PR_TRUE;
 
260
      aCellContent->SetAttr(kNameSpaceID_None, nsMathMLAtoms::columnline, nsDependentString(attr), PR_FALSE);
 
261
    }
 
262
  }
 
263
  else {
 
264
    // set the special -moz-math-firstcolumn to annotate that we are on the first column
 
265
    hasChanged = PR_TRUE;
 
266
    aCellContent->SetAttr(kNameSpaceID_None, nsMathMLAtoms::firstcolumn, trueStr, PR_FALSE);
 
267
  }
 
268
  // if we are on the last column, set the special -moz-math-lastcolumn
 
269
  PRInt32 colSpan = ((nsTableFrame*)tableFrame)->GetEffectiveColSpan(*cellFrame);
 
270
  sibling = ((nsTableFrame*)tableFrame)->GetCellFrameAt(rowIndex, colIndex+colSpan);
 
271
  if (!sibling) {
 
272
    hasChanged = PR_TRUE;
 
273
    aCellContent->SetAttr(kNameSpaceID_None, nsMathMLAtoms::lastcolumn, trueStr, PR_FALSE);
 
274
  }
 
275
 
 
276
  // now, re-resolve the style contexts in our subtree to pick up any changes
 
277
  if (hasChanged) {
 
278
    nsFrameManager *fm = aPresContext->FrameManager();
 
279
    nsStyleChangeList changeList;
 
280
    nsChangeHint maxChange = fm->ComputeStyleChangeFor(aCellFrame, &changeList,
 
281
                                                       NS_STYLE_HINT_NONE);
 
282
#ifdef DEBUG
 
283
    // Use the parent frame to make sure we catch in-flows and such
 
284
    nsIFrame* parentFrame = aCellFrame->GetParent();
 
285
    fm->DebugVerifyStyleTree(parentFrame ? parentFrame : aCellFrame);
 
286
#endif
 
287
  }
 
288
}
 
289
 
 
290
// the align attribute of mtable can have a row number which indicates
 
291
// from where to anchor the table, e.g., top5 means anchor the table at
 
292
// the top of the 5th row, axis-1 means anchor the table on the axis of
 
293
// the last row (could have been nicer if the REC used the '#' separator,
 
294
// e.g., top#5, or axis#-1)
 
295
 
 
296
enum eAlign {
 
297
  eAlign_top,
 
298
  eAlign_bottom,
 
299
  eAlign_center,
 
300
  eAlign_baseline,
 
301
  eAlign_axis
 
302
};
 
303
 
 
304
static void
 
305
ParseAlignAttribute(nsString& aValue, eAlign& aAlign, PRInt32& aRowIndex)
 
306
{
 
307
  // by default, the table is centered about the axis
 
308
  aRowIndex = 0;
 
309
  aAlign = eAlign_axis;
 
310
  PRInt32 len = 0;
 
311
  if (0 == aValue.Find("top")) {
 
312
    len = 3; // 3 is the length of 'top'
 
313
    aAlign = eAlign_top;
 
314
  }
 
315
  else if (0 == aValue.Find("bottom")) {
 
316
    len = 6; // 6 is the length of 'bottom'
 
317
    aAlign = eAlign_bottom;
 
318
  }
 
319
  else if (0 == aValue.Find("center")) {
 
320
    len = 6; // 6 is the length of 'center'
 
321
    aAlign = eAlign_center;
 
322
  }
 
323
  else if (0 == aValue.Find("baseline")) {
 
324
    len = 8; // 8 is the length of 'baseline'
 
325
    aAlign = eAlign_baseline;
 
326
  }
 
327
  else if (0 == aValue.Find("axis")) {
 
328
    len = 4; // 4 is the length of 'axis'
 
329
    aAlign = eAlign_axis;
 
330
  }
 
331
  if (len) {
 
332
    PRInt32 error;
 
333
    aValue.Cut(0, len); // aValue is not a const here
 
334
    aRowIndex = aValue.ToInteger(&error);
 
335
    if (error)
 
336
      aRowIndex = 0;
 
337
  }
 
338
}
 
339
 
 
340
// --------
 
341
// implementation of nsMathMLmtableOuterFrame
 
342
 
 
343
NS_IMPL_ADDREF_INHERITED(nsMathMLmtableOuterFrame, nsMathMLFrame)
 
344
NS_IMPL_RELEASE_INHERITED(nsMathMLmtableOuterFrame, nsMathMLFrame)
 
345
NS_IMPL_QUERY_INTERFACE_INHERITED1(nsMathMLmtableOuterFrame, nsTableOuterFrame, nsMathMLFrame)
 
346
 
 
347
nsresult
 
348
NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsIFrame** aNewFrame)
 
349
{
 
350
  NS_PRECONDITION(aNewFrame, "null OUT ptr");
 
351
  if (nsnull == aNewFrame) {
 
352
    return NS_ERROR_NULL_POINTER;
 
353
  }
 
354
  nsMathMLmtableOuterFrame* it = new (aPresShell) nsMathMLmtableOuterFrame;
 
355
  if (!it)
 
356
    return NS_ERROR_OUT_OF_MEMORY;
 
357
 
 
358
  *aNewFrame = it;
 
359
  return NS_OK;
 
360
}
 
361
 
 
362
nsMathMLmtableOuterFrame::nsMathMLmtableOuterFrame()
 
363
  :nsTableOuterFrame()
 
364
{
 
365
}
 
366
 
 
367
nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame()
 
368
{
 
369
}
 
370
 
 
371
NS_IMETHODIMP
 
372
nsMathMLmtableOuterFrame::InheritAutomaticData(nsIPresContext* aPresContext,
 
373
                                               nsIFrame*       aParent)
 
374
{
 
375
  // XXX the REC says that by default, displaystyle=false in <mtable>
 
376
 
 
377
  // let the base class inherit the scriptlevel and displaystyle from our parent
 
378
  nsMathMLFrame::InheritAutomaticData(aPresContext, aParent);
 
379
 
 
380
  // see if the displaystyle attribute is there and let it override what we inherited
 
381
  nsAutoString value;
 
382
  if (NS_CONTENT_ATTR_HAS_VALUE ==
 
383
      GetAttribute(mContent, nsnull, nsMathMLAtoms::displaystyle_, value)) {
 
384
    if (value.Equals(NS_LITERAL_STRING("true"))) {
 
385
      mPresentationData.flags |= NS_MATHML_DISPLAYSTYLE;
 
386
    }
 
387
    else if (value.Equals(NS_LITERAL_STRING("false"))) {
 
388
      mPresentationData.flags &= ~NS_MATHML_DISPLAYSTYLE;
 
389
    }
 
390
  }
 
391
 
 
392
  return NS_OK;
 
393
}
 
394
 
 
395
NS_IMETHODIMP
 
396
nsMathMLmtableOuterFrame::Init(nsIPresContext*  aPresContext,
 
397
                               nsIContent*      aContent,
 
398
                               nsIFrame*        aParent,
 
399
                               nsStyleContext*  aContext,
 
400
                               nsIFrame*        aPrevInFlow)
 
401
{
 
402
  MapAttributesIntoCSS(aPresContext, aContent);
 
403
 
 
404
  return nsTableOuterFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
 
405
}
 
406
 
 
407
nsIFrame*
 
408
nsMathMLmtableOuterFrame::GetRowFrameAt(nsIPresContext* aPresContext,
 
409
                                        PRInt32         aRowIndex)
 
410
{
 
411
  // To find the row at the given index, we will iterate downwards or
 
412
  // upwards depending on the sign of the index
 
413
  nsTableIteration dir = eTableLTR;
 
414
  if (aRowIndex < 0) {
 
415
    aRowIndex = -aRowIndex;
 
416
    dir = eTableRTL;
 
417
  }
 
418
  // if our inner table says that the index is valid, find the row now
 
419
  PRInt32 rowCount, colCount;
 
420
  GetTableSize(rowCount, colCount);
 
421
  if (aRowIndex <= rowCount) {
 
422
    nsIFrame* innerTableFrame = mFrames.FirstChild();
 
423
    nsTableIterator rowgroupIter(*innerTableFrame, dir);
 
424
    nsIFrame* rowgroupFrame = rowgroupIter.First();
 
425
    while (rowgroupFrame) {
 
426
      nsTableIterator rowIter(*rowgroupFrame, dir);
 
427
      nsIFrame* rowFrame = rowIter.First();
 
428
      while (rowFrame) {
 
429
        if (--aRowIndex == 0) {
 
430
          DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
 
431
          return rowFrame;
 
432
        }
 
433
        rowFrame = rowIter.Next();
 
434
      }
 
435
      rowgroupFrame = rowgroupIter.Next();
 
436
    }
 
437
  }
 
438
  return nsnull;
 
439
}
 
440
 
 
441
NS_IMETHODIMP
 
442
nsMathMLmtableOuterFrame::Reflow(nsIPresContext*          aPresContext,
 
443
                                 nsHTMLReflowMetrics&     aDesiredSize,
 
444
                                 const nsHTMLReflowState& aReflowState,
 
445
                                 nsReflowStatus&          aStatus)
 
446
{
 
447
  nsresult rv;
 
448
  nsAutoString value;
 
449
  // we want to return a table that is anchored according to the align attribute
 
450
 
 
451
  nsHTMLReflowState reflowState(aReflowState);
 
452
  if ((NS_FRAME_FIRST_REFLOW & mState) &&
 
453
      (NS_UNCONSTRAINEDSIZE == reflowState.availableWidth)) {
 
454
    // We are going to reflow twice because the table frame code is
 
455
    // skipping the Pass 2 reflow when, at the Pass 1 reflow, the available
 
456
    // size is unconstrained. Skipping the Pass2 messes the MathML vertical
 
457
    // alignments that are resolved during the reflow of cell frames.
 
458
 
 
459
    nscoord oldComputedWidth = reflowState.mComputedWidth;
 
460
    reflowState.mComputedWidth = NS_UNCONSTRAINEDSIZE;
 
461
    reflowState.reason = eReflowReason_Initial;
 
462
 
 
463
    rv = nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, reflowState, aStatus);
 
464
 
 
465
    // The second reflow will just be a reflow with a constrained width
 
466
    reflowState.availableWidth = aDesiredSize.width;
 
467
    reflowState.mComputedWidth = oldComputedWidth;
 
468
    reflowState.reason = eReflowReason_StyleChange;
 
469
 
 
470
    mState &= ~NS_FRAME_FIRST_REFLOW;
 
471
  }
 
472
  else if (mRect.width) {
 
473
    reflowState.availableWidth = mRect.width;
 
474
  }
 
475
 
 
476
  rv = nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, reflowState, aStatus);
 
477
  NS_ASSERTION(aDesiredSize.height >= 0, "illegal height for mtable");
 
478
  NS_ASSERTION(aDesiredSize.width >= 0, "illegal width for mtable");
 
479
 
 
480
  // see if the user has set the align attribute on the <mtable>
 
481
  // XXX should we also check <mstyle> ?
 
482
  PRInt32 rowIndex = 0;
 
483
  eAlign tableAlign = eAlign_axis;
 
484
  if (NS_CONTENT_ATTR_HAS_VALUE ==
 
485
      GetAttribute(mContent, nsnull, nsMathMLAtoms::align_, value)) {
 
486
    ParseAlignAttribute(value, tableAlign, rowIndex);
 
487
  }
 
488
 
 
489
  // adjustments if there is a specified row from where to anchor the table
 
490
  // (conceptually: when there is no row of reference, picture the table as if
 
491
  // it is wrapped in a single big fictional row at dy = 0, this way of
 
492
  // doing so allows us to have a single code path for all cases).
 
493
  nscoord dy = 0;
 
494
  nscoord height = aDesiredSize.height;
 
495
  nsIFrame* rowFrame = nsnull;
 
496
  if (rowIndex) {
 
497
    rowFrame = GetRowFrameAt(aPresContext, rowIndex);
 
498
    if (rowFrame) {
 
499
      // translate the coordinates to be relative to us
 
500
      nsIFrame* frame = rowFrame;
 
501
      height = frame->GetSize().height;
 
502
      do {
 
503
        dy += frame->GetPosition().y;
 
504
        frame = frame->GetParent();
 
505
      } while (frame != this);
 
506
    }
 
507
  }
 
508
  switch (tableAlign) {
 
509
    case eAlign_top:
 
510
      aDesiredSize.ascent = dy;
 
511
      break;
 
512
    case eAlign_bottom:
 
513
      aDesiredSize.ascent = dy + height;
 
514
      break;
 
515
    case eAlign_center:
 
516
      aDesiredSize.ascent = dy + height/2;
 
517
      break;
 
518
    case eAlign_baseline:
 
519
      if (rowFrame) {
 
520
        // anchor the table on the baseline of the row of reference
 
521
        nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
 
522
        if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
 
523
          aDesiredSize.ascent = dy + rowAscent;
 
524
          break;
 
525
        }
 
526
      }
 
527
      // in other situations, fallback to center
 
528
      aDesiredSize.ascent = dy + height/2;
 
529
      break;
 
530
    case eAlign_axis:
 
531
    default: {
 
532
      // XXX should instead use style data from the row of reference here ?
 
533
      aReflowState.rendContext->SetFont(GetStyleFont()->mFont, nsnull);
 
534
      nsCOMPtr<nsIFontMetrics> fm;
 
535
      aReflowState.rendContext->GetFontMetrics(*getter_AddRefs(fm));
 
536
      nscoord axisHeight;
 
537
      GetAxisHeight(*aReflowState.rendContext, fm, axisHeight);
 
538
      if (rowFrame) {
 
539
        // anchor the table on the axis of the row of reference
 
540
        // XXX fallback to baseline because it is a hard problem
 
541
        // XXX need to fetch the axis of the row; would need rowalign=axis to work better
 
542
        nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
 
543
        if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
 
544
          aDesiredSize.ascent = dy + rowAscent;
 
545
          break;
 
546
        }
 
547
      }
 
548
      // in other situations, fallback to using half of the height
 
549
      aDesiredSize.ascent = dy + height/2 + axisHeight;
 
550
    }
 
551
  }
 
552
  aDesiredSize.descent = aDesiredSize.height - aDesiredSize.ascent;
 
553
 
 
554
  mReference.x = 0;
 
555
  mReference.y = aDesiredSize.ascent;
 
556
 
 
557
  // just make-up a bounding metrics
 
558
  mBoundingMetrics.Clear();
 
559
  mBoundingMetrics.ascent = aDesiredSize.ascent;
 
560
  mBoundingMetrics.descent = aDesiredSize.descent;
 
561
  mBoundingMetrics.width = aDesiredSize.width;
 
562
  mBoundingMetrics.leftBearing = 0;
 
563
  mBoundingMetrics.rightBearing = aDesiredSize.width;
 
564
 
 
565
  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
 
566
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
 
567
 
 
568
  return rv;
 
569
}
 
570
 
 
571
// --------
 
572
// implementation of nsMathMLmtdFrame
 
573
 
 
574
NS_IMPL_ADDREF_INHERITED(nsMathMLmtdFrame, nsTableCellFrame)
 
575
NS_IMPL_RELEASE_INHERITED(nsMathMLmtdFrame, nsTableCellFrame)
 
576
NS_IMPL_QUERY_INTERFACE_INHERITED0(nsMathMLmtdFrame, nsTableCellFrame)
 
577
 
 
578
nsresult
 
579
NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
 
580
{
 
581
  NS_PRECONDITION(aNewFrame, "null OUT ptr");
 
582
  if (nsnull == aNewFrame) {
 
583
    return NS_ERROR_NULL_POINTER;
 
584
  }
 
585
  nsMathMLmtdFrame* it = new (aPresShell) nsMathMLmtdFrame;
 
586
  if (nsnull == it) {
 
587
    return NS_ERROR_OUT_OF_MEMORY;
 
588
  }
 
589
  *aNewFrame = it;
 
590
  return NS_OK;
 
591
}
 
592
 
 
593
nsMathMLmtdFrame::nsMathMLmtdFrame()
 
594
{
 
595
}
 
596
 
 
597
nsMathMLmtdFrame::~nsMathMLmtdFrame()
 
598
{
 
599
}
 
600
 
 
601
PRInt32
 
602
nsMathMLmtdFrame::GetRowSpan()
 
603
{
 
604
  PRInt32 rowspan = 1;
 
605
  nsAutoString value;
 
606
  if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_None,
 
607
                   nsMathMLAtoms::rowspan_, value)) {
 
608
    PRInt32 error;
 
609
    rowspan = value.ToInteger(&error);
 
610
    if (error)
 
611
      rowspan = 1;
 
612
  }
 
613
  return rowspan;
 
614
}
 
615
 
 
616
PRInt32
 
617
nsMathMLmtdFrame::GetColSpan()
 
618
{
 
619
  PRInt32 colspan = 1;
 
620
  nsAutoString value;
 
621
  if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_None,
 
622
                   nsMathMLAtoms::columnspan_, value)) {
 
623
    PRInt32 error;
 
624
    colspan = value.ToInteger(&error);
 
625
    if (error)
 
626
      colspan = 1;
 
627
  }
 
628
  return colspan;
 
629
}
 
630
 
 
631
// --------
 
632
// implementation of nsMathMLmtdInnerFrame
 
633
 
 
634
NS_IMPL_ADDREF_INHERITED(nsMathMLmtdInnerFrame, nsMathMLFrame)
 
635
NS_IMPL_RELEASE_INHERITED(nsMathMLmtdInnerFrame, nsMathMLFrame)
 
636
NS_IMPL_QUERY_INTERFACE_INHERITED1(nsMathMLmtdInnerFrame, nsBlockFrame, nsMathMLFrame)
 
637
 
 
638
nsresult
 
639
NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
 
640
{
 
641
  NS_PRECONDITION(aNewFrame, "null OUT ptr");
 
642
  if (nsnull == aNewFrame) {
 
643
    return NS_ERROR_NULL_POINTER;
 
644
  }
 
645
  nsMathMLmtdInnerFrame* it = new (aPresShell) nsMathMLmtdInnerFrame;
 
646
  if (nsnull == it) {
 
647
    return NS_ERROR_OUT_OF_MEMORY;
 
648
  }
 
649
  *aNewFrame = it;
 
650
  return NS_OK;
 
651
}
 
652
 
 
653
nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame()
 
654
{
 
655
}
 
656
 
 
657
nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame()
 
658
{
 
659
}
 
660
 
 
661
NS_IMETHODIMP
 
662
nsMathMLmtdInnerFrame::Init(nsIPresContext*  aPresContext,
 
663
                            nsIContent*      aContent,
 
664
                            nsIFrame*        aParent,
 
665
                            nsStyleContext*  aContext,
 
666
                            nsIFrame*        aPrevInFlow)
 
667
{
 
668
  nsresult rv = nsBlockFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
 
669
 
 
670
  // record that children that are ignorable whitespace should be excluded
 
671
  mState |= NS_FRAME_EXCLUDE_IGNORABLE_WHITESPACE;
 
672
 
 
673
  return rv;
 
674
}
 
675
 
 
676
NS_IMETHODIMP
 
677
nsMathMLmtdInnerFrame::Reflow(nsIPresContext*          aPresContext,
 
678
                              nsHTMLReflowMetrics&     aDesiredSize,
 
679
                              const nsHTMLReflowState& aReflowState,
 
680
                              nsReflowStatus&          aStatus)
 
681
{
 
682
  // Map attributes to style (hopefully, bug 69409 will eventually help here).
 
683
  if (NS_FRAME_FIRST_REFLOW & mState) {
 
684
    MapAttributesInto(aPresContext, mContent, mParent, this);
 
685
  }
 
686
 
 
687
  // Let the base class do the reflow
 
688
  nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
 
689
 
 
690
  // more about <maligngroup/> and <malignmark/> later
 
691
  // ...
 
692
  return rv;
 
693
}