Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/xul/grid/nsGrid.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
//
8
// Eric Vaughan
9
// Netscape Communications
10
//
11
// See documentation in associated header file
12
//
13
14
#include "nsGrid.h"
15
#include "nsGridRowGroupLayout.h"
16
#include "nsBox.h"
17
#include "nsIScrollableFrame.h"
18
#include "nsSprocketLayout.h"
19
#include "nsGridLayout2.h"
20
#include "nsGridRow.h"
21
#include "nsGridCell.h"
22
#include "mozilla/ReflowInput.h"
23
24
/*
25
The grid control expands the idea of boxes from 1 dimension to 2 dimensions.
26
It works by allowing the XUL to define a collection of rows and columns and then
27
stacking them on top of each other. Here is and example.
28
29
Example 1:
30
31
<grid>
32
   <columns>
33
      <column/>
34
      <column/>
35
   </columns>
36
37
   <rows>
38
      <row/>
39
      <row/>
40
   </rows>
41
</grid>
42
43
example 2:
44
45
<grid>
46
   <columns>
47
      <column flex="1"/>
48
      <column flex="1"/>
49
   </columns>
50
51
   <rows>
52
      <row>
53
         <text value="hello"/>
54
         <text value="there"/>
55
      </row>
56
   </rows>
57
</grid>
58
59
example 3:
60
61
<grid>
62
63
<rows>
64
      <row>
65
         <text value="hello"/>
66
         <text value="there"/>
67
      </row>
68
   </rows>
69
70
   <columns>
71
      <column>
72
         <text value="Hey I'm in the column and I'm on top!"/>
73
      </column>
74
      <column/>
75
   </columns>
76
77
</grid>
78
79
Usually the columns are first and the rows are second, so the rows will be drawn on top of the columns.
80
You can reverse this by defining the rows first.
81
Other tags are then placed in the <row> or <column> tags causing the grid to accommodate everyone.
82
It does this by creating 3 things: A cellmap, a row list, and a column list. The cellmap is a 2
83
dimensional array of nsGridCells. Each cell contains 2 boxes.  One cell from the column list
84
and one from the row list. When a cell is asked for its size it returns that smallest size it can
85
be to accommodate the 2 cells. Row lists and Column lists use the same data structure: nsGridRow.
86
Essentially a row and column are the same except a row goes alone the x axis and a column the y.
87
To make things easier and save code everything is written in terms of the x dimension. A flag is
88
passed in called "isHorizontal" that can flip the calculations to the y axis.
89
90
Usually the number of cells in a row match the number of columns, but not always.
91
It is possible to define 5 columns for a grid but have 10 cells in one of the rows.
92
In this case 5 extra columns will be added to the column list to handle the situation.
93
These are called extraColumns/Rows.
94
*/
95
96
using namespace mozilla;
97
98
nsGrid::nsGrid():mBox(nullptr),
99
                 mRowsBox(nullptr),
100
                 mColumnsBox(nullptr),
101
                 mNeedsRebuild(true),
102
                 mRowCount(0),
103
                 mColumnCount(0),
104
                 mExtraRowCount(0),
105
                 mExtraColumnCount(0),
106
                 mMarkingDirty(false)
107
0
{
108
0
    MOZ_COUNT_CTOR(nsGrid);
109
0
}
110
111
nsGrid::~nsGrid()
112
0
{
113
0
    FreeMap();
114
0
    MOZ_COUNT_DTOR(nsGrid);
115
0
}
116
117
/*
118
 * This is called whenever something major happens in the grid. And example
119
 * might be when many cells or row are added. It sets a flag signaling that
120
 * all the grids caches information should be recalculated.
121
 */
122
void
123
nsGrid::NeedsRebuild(nsBoxLayoutState& aState)
124
0
{
125
0
  if (mNeedsRebuild)
126
0
    return;
127
0
128
0
  // iterate through columns and rows and dirty them
129
0
  mNeedsRebuild = true;
130
0
131
0
  // find the new row and column box. They could have
132
0
  // been changed.
133
0
  mRowsBox = nullptr;
134
0
  mColumnsBox = nullptr;
135
0
  FindRowsAndColumns(&mRowsBox, &mColumnsBox);
136
0
137
0
  // tell all the rows and columns they are dirty
138
0
  DirtyRows(mRowsBox, aState);
139
0
  DirtyRows(mColumnsBox, aState);
140
0
}
141
142
143
144
/**
145
 * If we are marked for rebuild. Then build everything
146
 */
147
void
148
nsGrid::RebuildIfNeeded()
149
0
{
150
0
  if (!mNeedsRebuild)
151
0
    return;
152
0
153
0
  mNeedsRebuild = false;
154
0
155
0
  // find the row and columns frames
156
0
  FindRowsAndColumns(&mRowsBox, &mColumnsBox);
157
0
158
0
  // count the rows and columns
159
0
  int32_t computedRowCount = 0;
160
0
  int32_t computedColumnCount = 0;
161
0
  int32_t rowCount = 0;
162
0
  int32_t columnCount = 0;
163
0
164
0
  CountRowsColumns(mRowsBox, rowCount, computedColumnCount);
165
0
  CountRowsColumns(mColumnsBox, columnCount, computedRowCount);
166
0
167
0
  // computedRowCount are the actual number of rows as determined by the
168
0
  // columns children.
169
0
  // computedColumnCount are the number of columns as determined by the number
170
0
  // of rows children.
171
0
  // We can use this information to see how many extra columns or rows we need.
172
0
  // This can happen if there are are more children in a row that number of columns
173
0
  // defined. Example:
174
0
  //
175
0
  // <columns>
176
0
  //   <column/>
177
0
  // </columns>
178
0
  //
179
0
  // <rows>
180
0
  //   <row>
181
0
  //     <button/><button/>
182
0
  //   </row>
183
0
  // </rows>
184
0
  //
185
0
  // computedColumnCount = 2 // for the 2 buttons in the row tag
186
0
  // computedRowCount = 0 // there is nothing in the  column tag
187
0
  // mColumnCount = 1 // one column defined
188
0
  // mRowCount = 1 // one row defined
189
0
  //
190
0
  // So in this case we need to make 1 extra column.
191
0
  //
192
0
193
0
  // Make sure to update mExtraColumnCount no matter what, since it might
194
0
  // happen that we now have as many columns as are defined, and we wouldn't
195
0
  // want to have a positive mExtraColumnCount hanging about in that case!
196
0
  mExtraColumnCount = computedColumnCount - columnCount;
197
0
  if (computedColumnCount > columnCount) {
198
0
     columnCount = computedColumnCount;
199
0
  }
200
0
201
0
  // Same for rows.
202
0
  mExtraRowCount = computedRowCount - rowCount;
203
0
  if (computedRowCount > rowCount) {
204
0
     rowCount = computedRowCount;
205
0
  }
206
0
207
0
  // build and poplulate row and columns arrays
208
0
  mRows = BuildRows(mRowsBox, rowCount, true);
209
0
  mColumns = BuildRows(mColumnsBox, columnCount, false);
210
0
211
0
  // build and populate the cell map
212
0
  mCellMap = BuildCellMap(rowCount, columnCount);
213
0
214
0
  mRowCount = rowCount;
215
0
  mColumnCount = columnCount;
216
0
217
0
  // populate the cell map from column and row children
218
0
  PopulateCellMap(mRows.get(), mColumns.get(), mRowCount, mColumnCount, true);
219
0
  PopulateCellMap(mColumns.get(), mRows.get(), mColumnCount, mRowCount, false);
220
0
}
221
222
void
223
nsGrid::FreeMap()
224
0
{
225
0
  mRows = nullptr;
226
0
  mColumns = nullptr;
227
0
  mCellMap = nullptr;
228
0
  mColumnCount = 0;
229
0
  mRowCount = 0;
230
0
  mExtraColumnCount = 0;
231
0
  mExtraRowCount = 0;
232
0
  mRowsBox = nullptr;
233
0
  mColumnsBox = nullptr;
234
0
}
235
236
/**
237
 * finds the first <rows> and <columns> tags in the <grid> tag
238
 */
239
void
240
nsGrid::FindRowsAndColumns(nsIFrame** aRows, nsIFrame** aColumns)
241
0
{
242
0
  *aRows = nullptr;
243
0
  *aColumns = nullptr;
244
0
245
0
  // find the boxes that contain our rows and columns
246
0
  nsIFrame* child = nullptr;
247
0
  // if we have <grid></grid> then mBox will be null (bug 125689)
248
0
  if (mBox)
249
0
    child = nsBox::GetChildXULBox(mBox);
250
0
251
0
  while(child)
252
0
  {
253
0
    nsIFrame* oldBox = child;
254
0
    nsIScrollableFrame *scrollFrame = do_QueryFrame(child);
255
0
    if (scrollFrame) {
256
0
       nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
257
0
       NS_ASSERTION(scrolledFrame,"Error no scroll frame!!");
258
0
       child = do_QueryFrame(scrolledFrame);
259
0
    }
260
0
261
0
    nsCOMPtr<nsIGridPart> monument = GetPartFromBox(child);
262
0
    if (monument)
263
0
    {
264
0
      nsGridRowGroupLayout* rowGroup = monument->CastToRowGroupLayout();
265
0
      if (rowGroup) {
266
0
         bool isHorizontal = !nsSprocketLayout::IsXULHorizontal(child);
267
0
         if (isHorizontal)
268
0
           *aRows = child;
269
0
         else
270
0
           *aColumns = child;
271
0
272
0
         if (*aRows && *aColumns)
273
0
           return;
274
0
      }
275
0
    }
276
0
277
0
    if (scrollFrame) {
278
0
      child = oldBox;
279
0
    }
280
0
281
0
    child = nsBox::GetNextXULBox(child);
282
0
  }
283
0
}
284
285
/**
286
 * Count the number of rows and columns in the given box. aRowCount well become the actual number
287
 * rows defined in the xul. aComputedColumnCount will become the number of columns by counting the number
288
 * of cells in each row.
289
 */
290
void
291
nsGrid::CountRowsColumns(nsIFrame* aRowBox, int32_t& aRowCount, int32_t& aComputedColumnCount)
292
0
{
293
0
  aRowCount = 0;
294
0
  aComputedColumnCount = 0;
295
0
  // get the rowboxes layout manager. Then ask it to do the work for us
296
0
  if (aRowBox) {
297
0
    nsCOMPtr<nsIGridPart> monument = GetPartFromBox(aRowBox);
298
0
    if (monument)
299
0
       monument->CountRowsColumns(aRowBox, aRowCount, aComputedColumnCount);
300
0
  }
301
0
}
302
303
304
/**
305
 * Given the number of rows create nsGridRow objects for them and full them out.
306
 */
307
UniquePtr<nsGridRow[]>
308
nsGrid::BuildRows(nsIFrame* aBox, int32_t aRowCount, bool aIsHorizontal)
309
0
{
310
0
  // if no rows then return null
311
0
  if (aRowCount == 0) {
312
0
    return nullptr;
313
0
  }
314
0
315
0
  // create the array
316
0
  UniquePtr<nsGridRow[]> row;
317
0
318
0
  // only create new rows if we have to. Reuse old rows.
319
0
  if (aIsHorizontal)
320
0
  {
321
0
    if (aRowCount > mRowCount) {
322
0
       row = MakeUnique<nsGridRow[]>(aRowCount);
323
0
    } else {
324
0
      for (int32_t i=0; i < mRowCount; i++)
325
0
        mRows[i].Init(nullptr, false);
326
0
327
0
      row = std::move(mRows);
328
0
    }
329
0
  } else {
330
0
    if (aRowCount > mColumnCount) {
331
0
       row = MakeUnique<nsGridRow[]>(aRowCount);
332
0
    } else {
333
0
       for (int32_t i=0; i < mColumnCount; i++)
334
0
         mColumns[i].Init(nullptr, false);
335
0
336
0
       row = std::move(mColumns);
337
0
    }
338
0
  }
339
0
340
0
  // populate it if we can. If not it will contain only dynamic columns
341
0
  if (aBox)
342
0
  {
343
0
    nsCOMPtr<nsIGridPart> monument = GetPartFromBox(aBox);
344
0
    if (monument) {
345
0
       monument->BuildRows(aBox, row.get());
346
0
    }
347
0
  }
348
0
349
0
  return row;
350
0
}
351
352
353
/**
354
 * Given the number of rows and columns. Build a cellmap
355
 */
356
UniquePtr<nsGridCell[]>
357
nsGrid::BuildCellMap(int32_t aRows, int32_t aColumns)
358
0
{
359
0
  int32_t size = aRows*aColumns;
360
0
  int32_t oldsize = mRowCount*mColumnCount;
361
0
  if (size == 0) {
362
0
    return nullptr;
363
0
  }
364
0
365
0
  if (size > oldsize) {
366
0
    return MakeUnique<nsGridCell[]>(size);
367
0
  }
368
0
369
0
  // clear out cellmap
370
0
  for (int32_t i=0; i < oldsize; i++) {
371
0
    mCellMap[i].SetBoxInRow(nullptr);
372
0
    mCellMap[i].SetBoxInColumn(nullptr);
373
0
  }
374
0
  return std::move(mCellMap);
375
0
}
376
377
/**
378
 * Run through all the cells in the rows and columns and populate then with 2 cells. One from the row and one
379
 * from the column
380
 */
381
void
382
nsGrid::PopulateCellMap(nsGridRow* aRows, nsGridRow* aColumns, int32_t aRowCount, int32_t aColumnCount, bool aIsHorizontal)
383
0
{
384
0
  if (!aRows)
385
0
    return;
386
0
387
0
   // look through the columns
388
0
  int32_t j = 0;
389
0
390
0
  for(int32_t i=0; i < aRowCount; i++)
391
0
  {
392
0
     nsIFrame* child = nullptr;
393
0
     nsGridRow* row = &aRows[i];
394
0
395
0
     // skip bogus rows. They have no cells
396
0
     if (row->mIsBogus)
397
0
       continue;
398
0
399
0
     child = row->mBox;
400
0
     if (child) {
401
0
       child = nsBox::GetChildXULBox(child);
402
0
403
0
       j = 0;
404
0
405
0
       while(child && j < aColumnCount)
406
0
       {
407
0
         // skip bogus column. They have no cells
408
0
         nsGridRow* column = &aColumns[j];
409
0
         if (column->mIsBogus)
410
0
         {
411
0
           j++;
412
0
           continue;
413
0
         }
414
0
415
0
         if (aIsHorizontal)
416
0
           GetCellAt(j,i)->SetBoxInRow(child);
417
0
         else
418
0
           GetCellAt(i,j)->SetBoxInColumn(child);
419
0
420
0
         child = nsBox::GetNextXULBox(child);
421
0
422
0
         j++;
423
0
       }
424
0
     }
425
0
  }
426
0
}
427
428
/**
429
 * Run through the rows in the given box and mark them dirty so they
430
 * will get recalculated and get a layout.
431
 */
432
void
433
nsGrid::DirtyRows(nsIFrame* aRowBox, nsBoxLayoutState& aState)
434
0
{
435
0
  // make sure we prevent others from dirtying things.
436
0
  mMarkingDirty = true;
437
0
438
0
  // if the box is a grid part have it recursively hand it.
439
0
  if (aRowBox) {
440
0
    nsCOMPtr<nsIGridPart> part = GetPartFromBox(aRowBox);
441
0
    if (part)
442
0
       part->DirtyRows(aRowBox, aState);
443
0
  }
444
0
445
0
  mMarkingDirty = false;
446
0
}
447
448
nsGridRow*
449
nsGrid::GetColumnAt(int32_t aIndex, bool aIsHorizontal)
450
0
{
451
0
  return GetRowAt(aIndex, !aIsHorizontal);
452
0
}
453
454
nsGridRow*
455
nsGrid::GetRowAt(int32_t aIndex, bool aIsHorizontal)
456
0
{
457
0
  RebuildIfNeeded();
458
0
459
0
  if (aIsHorizontal) {
460
0
    NS_ASSERTION(aIndex < mRowCount && aIndex >= 0, "Index out of range");
461
0
    return &mRows[aIndex];
462
0
  } else {
463
0
    NS_ASSERTION(aIndex < mColumnCount && aIndex >= 0, "Index out of range");
464
0
    return &mColumns[aIndex];
465
0
  }
466
0
}
467
468
nsGridCell*
469
nsGrid::GetCellAt(int32_t aX, int32_t aY)
470
0
{
471
0
  RebuildIfNeeded();
472
0
473
0
  NS_ASSERTION(aY < mRowCount && aY >= 0, "Index out of range");
474
0
  NS_ASSERTION(aX < mColumnCount && aX >= 0, "Index out of range");
475
0
  return &mCellMap[aY*mColumnCount+aX];
476
0
}
477
478
int32_t
479
nsGrid::GetExtraColumnCount(bool aIsHorizontal)
480
0
{
481
0
  return GetExtraRowCount(!aIsHorizontal);
482
0
}
483
484
int32_t
485
nsGrid::GetExtraRowCount(bool aIsHorizontal)
486
0
{
487
0
  RebuildIfNeeded();
488
0
489
0
  if (aIsHorizontal)
490
0
    return mExtraRowCount;
491
0
  else
492
0
    return mExtraColumnCount;
493
0
}
494
495
496
/**
497
 * These methods return the preferred, min, max sizes for a given row index.
498
 * aIsHorizontal if aIsHorizontal is true. If you pass false you will get the inverse.
499
 * As if you called GetPrefColumnSize(aState, index, aPref)
500
 */
501
nsSize
502
nsGrid::GetPrefRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
503
0
{
504
0
  nsSize size(0,0);
505
0
  if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
506
0
    return size;
507
0
508
0
  nscoord height = GetPrefRowHeight(aState, aRowIndex, aIsHorizontal);
509
0
  SetLargestSize(size, height, aIsHorizontal);
510
0
511
0
  return size;
512
0
}
513
514
nsSize
515
nsGrid::GetMinRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
516
0
{
517
0
  nsSize size(0,0);
518
0
  if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
519
0
    return size;
520
0
521
0
  nscoord height = GetMinRowHeight(aState, aRowIndex, aIsHorizontal);
522
0
  SetLargestSize(size, height, aIsHorizontal);
523
0
524
0
  return size;
525
0
}
526
527
nsSize
528
nsGrid::GetMaxRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
529
0
{
530
0
  nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
531
0
  if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
532
0
    return size;
533
0
534
0
  nscoord height = GetMaxRowHeight(aState, aRowIndex, aIsHorizontal);
535
0
  SetSmallestSize(size, height, aIsHorizontal);
536
0
537
0
  return size;
538
0
}
539
540
// static
541
nsIGridPart*
542
nsGrid::GetPartFromBox(nsIFrame* aBox)
543
0
{
544
0
  if (!aBox)
545
0
    return nullptr;
546
0
547
0
  nsBoxLayout* layout = aBox->GetXULLayoutManager();
548
0
  return layout ? layout->AsGridPart() : nullptr;
549
0
}
550
551
nsMargin
552
nsGrid::GetBoxTotalMargin(nsIFrame* aBox, bool aIsHorizontal)
553
0
{
554
0
  nsMargin margin(0,0,0,0);
555
0
  // walk the boxes parent chain getting the border/padding/margin of our parent rows
556
0
557
0
  // first get the layour manager
558
0
  nsIGridPart* part = GetPartFromBox(aBox);
559
0
  if (part)
560
0
    margin = part->GetTotalMargin(aBox, aIsHorizontal);
561
0
562
0
  return margin;
563
0
}
564
565
/**
566
 * The first and last rows can be affected by <rows> tags with borders or margin
567
 * gets first and last rows and their indexes.
568
 * If it fails because there are no rows then:
569
 * FirstRow is nullptr
570
 * LastRow is nullptr
571
 * aFirstIndex = -1
572
 * aLastIndex = -1
573
 */
574
void
575
nsGrid::GetFirstAndLastRow(int32_t& aFirstIndex,
576
                           int32_t& aLastIndex,
577
                           nsGridRow*& aFirstRow,
578
                           nsGridRow*& aLastRow,
579
                           bool aIsHorizontal)
580
0
{
581
0
  aFirstRow = nullptr;
582
0
  aLastRow = nullptr;
583
0
  aFirstIndex = -1;
584
0
  aLastIndex = -1;
585
0
586
0
  int32_t count = GetRowCount(aIsHorizontal);
587
0
588
0
  if (count == 0)
589
0
    return;
590
0
591
0
592
0
  // We could have collapsed columns either before or after our index.
593
0
  // they should not count. So if we are the 5th row and the first 4 are
594
0
  // collaped we become the first row. Or if we are the 9th row and
595
0
  // 10 up to the last row are collapsed we then become the last.
596
0
597
0
  // see if we are first
598
0
  int32_t i;
599
0
  for (i=0; i < count; i++)
600
0
  {
601
0
     nsGridRow* row = GetRowAt(i,aIsHorizontal);
602
0
     if (!row->IsXULCollapsed()) {
603
0
       aFirstIndex = i;
604
0
       aFirstRow = row;
605
0
       break;
606
0
     }
607
0
  }
608
0
609
0
  // see if we are last
610
0
  for (i=count-1; i >= 0; i--)
611
0
  {
612
0
     nsGridRow* row = GetRowAt(i,aIsHorizontal);
613
0
     if (!row->IsXULCollapsed()) {
614
0
       aLastIndex = i;
615
0
       aLastRow = row;
616
0
       break;
617
0
     }
618
0
619
0
  }
620
0
}
621
622
/**
623
 * A row can have a top and bottom offset. Usually this is just the top and bottom border/padding.
624
 * However if the row is the first or last it could be affected by the fact a column or columns could
625
 * have a top or bottom margin.
626
 */
627
void
628
nsGrid::GetRowOffsets(int32_t aIndex, nscoord& aTop, nscoord& aBottom, bool aIsHorizontal)
629
0
{
630
0
631
0
  RebuildIfNeeded();
632
0
633
0
  nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
634
0
635
0
  if (row->IsOffsetSet())
636
0
  {
637
0
    aTop    = row->mTop;
638
0
    aBottom = row->mBottom;
639
0
    return;
640
0
  }
641
0
642
0
  // first get the rows top and bottom border and padding
643
0
  nsIFrame* box = row->GetBox();
644
0
645
0
  // add up all the padding
646
0
  nsMargin margin(0,0,0,0);
647
0
  nsMargin border(0,0,0,0);
648
0
  nsMargin padding(0,0,0,0);
649
0
  nsMargin totalBorderPadding(0,0,0,0);
650
0
  nsMargin totalMargin(0,0,0,0);
651
0
652
0
  // if there is a box and it's not bogus take its
653
0
  // borders padding into account
654
0
  if (box && !row->mIsBogus)
655
0
  {
656
0
    if (!box->IsXULCollapsed())
657
0
    {
658
0
       // get real border and padding. GetXULBorderAndPadding
659
0
       // is redefined on nsGridRowLeafFrame. If we called it here
660
0
       // we would be in finite recurson.
661
0
       box->GetXULBorder(border);
662
0
       box->GetXULPadding(padding);
663
0
664
0
       totalBorderPadding += border;
665
0
       totalBorderPadding += padding;
666
0
     }
667
0
668
0
     // if we are the first or last row
669
0
     // take into account <rows> tags around us
670
0
     // that could have borders or margins.
671
0
     // fortunately they only affect the first
672
0
     // and last row inside the <rows> tag
673
0
674
0
     totalMargin = GetBoxTotalMargin(box, aIsHorizontal);
675
0
  }
676
0
677
0
  if (aIsHorizontal) {
678
0
    row->mTop = totalBorderPadding.top;
679
0
    row->mBottom = totalBorderPadding.bottom;
680
0
    row->mTopMargin = totalMargin.top;
681
0
    row->mBottomMargin = totalMargin.bottom;
682
0
  } else {
683
0
    row->mTop = totalBorderPadding.left;
684
0
    row->mBottom = totalBorderPadding.right;
685
0
    row->mTopMargin = totalMargin.left;
686
0
    row->mBottomMargin = totalMargin.right;
687
0
  }
688
0
689
0
  // if we are the first or last row take into account the top and bottom borders
690
0
  // of each columns.
691
0
692
0
  // If we are the first row then get the largest top border/padding in
693
0
  // our columns. If that's larger than the rows top border/padding use it.
694
0
695
0
  // If we are the last row then get the largest bottom border/padding in
696
0
  // our columns. If that's larger than the rows bottom border/padding use it.
697
0
  int32_t firstIndex = 0;
698
0
  int32_t lastIndex = 0;
699
0
  nsGridRow* firstRow = nullptr;
700
0
  nsGridRow* lastRow = nullptr;
701
0
  GetFirstAndLastRow(firstIndex, lastIndex, firstRow, lastRow, aIsHorizontal);
702
0
703
0
  if (aIndex == firstIndex || aIndex == lastIndex) {
704
0
    nscoord maxTop = 0;
705
0
    nscoord maxBottom = 0;
706
0
707
0
    // run through the columns. Look at each column
708
0
    // pick the largest top border or bottom border
709
0
    int32_t count = GetColumnCount(aIsHorizontal);
710
0
711
0
    for (int32_t i=0; i < count; i++)
712
0
    {
713
0
      nsMargin totalChildBorderPadding(0,0,0,0);
714
0
715
0
      nsGridRow* column = GetColumnAt(i,aIsHorizontal);
716
0
      nsIFrame* box = column->GetBox();
717
0
718
0
      if (box)
719
0
      {
720
0
        // ignore collapsed children
721
0
        if (!box->IsXULCollapsed())
722
0
        {
723
0
           // include the margin of the columns. To the row
724
0
           // at this point border/padding and margins all added
725
0
           // up to more needed space.
726
0
           margin = GetBoxTotalMargin(box, !aIsHorizontal);
727
0
           // get real border and padding. GetXULBorderAndPadding
728
0
           // is redefined on nsGridRowLeafFrame. If we called it here
729
0
           // we would be in finite recurson.
730
0
           box->GetXULBorder(border);
731
0
           box->GetXULPadding(padding);
732
0
           totalChildBorderPadding += border;
733
0
           totalChildBorderPadding += padding;
734
0
           totalChildBorderPadding += margin;
735
0
        }
736
0
737
0
        nscoord top;
738
0
        nscoord bottom;
739
0
740
0
        // pick the largest top margin
741
0
        if (aIndex == firstIndex) {
742
0
          if (aIsHorizontal) {
743
0
            top = totalChildBorderPadding.top;
744
0
          } else {
745
0
            top = totalChildBorderPadding.left;
746
0
          }
747
0
          if (top > maxTop)
748
0
            maxTop = top;
749
0
        }
750
0
751
0
        // pick the largest bottom margin
752
0
        if (aIndex == lastIndex) {
753
0
          if (aIsHorizontal) {
754
0
            bottom = totalChildBorderPadding.bottom;
755
0
          } else {
756
0
            bottom = totalChildBorderPadding.right;
757
0
          }
758
0
          if (bottom > maxBottom)
759
0
             maxBottom = bottom;
760
0
        }
761
0
762
0
      }
763
0
764
0
      // If the biggest top border/padding the columns is larger than this rows top border/padding
765
0
      // the use it.
766
0
      if (aIndex == firstIndex) {
767
0
        if (maxTop > (row->mTop + row->mTopMargin))
768
0
          row->mTop = maxTop - row->mTopMargin;
769
0
      }
770
0
771
0
      // If the biggest bottom border/padding the columns is larger than this rows bottom border/padding
772
0
      // the use it.
773
0
      if (aIndex == lastIndex) {
774
0
        if (maxBottom > (row->mBottom + row->mBottomMargin))
775
0
          row->mBottom = maxBottom - row->mBottomMargin;
776
0
      }
777
0
    }
778
0
  }
779
0
780
0
  aTop    = row->mTop;
781
0
  aBottom = row->mBottom;
782
0
}
783
784
/**
785
 * These methods return the preferred, min, max coord for a given row index if
786
 * aIsHorizontal is true. If you pass false you will get the inverse.
787
 * As if you called GetPrefColumnHeight(aState, index, aPref).
788
 */
789
nscoord
790
nsGrid::GetPrefRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
791
0
{
792
0
  RebuildIfNeeded();
793
0
794
0
  nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
795
0
796
0
  if (row->IsXULCollapsed())
797
0
    return 0;
798
0
799
0
  if (row->IsPrefSet())
800
0
    return row->mPref;
801
0
802
0
  nsIFrame* box = row->mBox;
803
0
804
0
  // set in CSS?
805
0
  if (box)
806
0
  {
807
0
    bool widthSet, heightSet;
808
0
    nsSize cssSize(-1, -1);
809
0
    nsIFrame::AddXULPrefSize(box, cssSize, widthSet, heightSet);
810
0
811
0
    row->mPref = GET_HEIGHT(cssSize, aIsHorizontal);
812
0
813
0
    // yep do nothing.
814
0
    if (row->mPref != -1)
815
0
      return row->mPref;
816
0
  }
817
0
818
0
  // get the offsets so they are cached.
819
0
  nscoord top;
820
0
  nscoord bottom;
821
0
  GetRowOffsets(aIndex, top, bottom, aIsHorizontal);
822
0
823
0
  // is the row bogus? If so then just ask it for its size
824
0
  // it should not be affected by cells in the grid.
825
0
  if (row->mIsBogus)
826
0
  {
827
0
     nsSize size(0,0);
828
0
     if (box)
829
0
     {
830
0
       size = box->GetXULPrefSize(aState);
831
0
       nsBox::AddMargin(box, size);
832
0
       nsGridLayout2::AddOffset(box, size);
833
0
     }
834
0
835
0
     row->mPref = GET_HEIGHT(size, aIsHorizontal);
836
0
     return row->mPref;
837
0
  }
838
0
839
0
  nsSize size(0,0);
840
0
841
0
  nsGridCell* child;
842
0
843
0
  int32_t count = GetColumnCount(aIsHorizontal);
844
0
845
0
  for (int32_t i=0; i < count; i++)
846
0
  {
847
0
    if (aIsHorizontal)
848
0
     child = GetCellAt(i,aIndex);
849
0
    else
850
0
     child = GetCellAt(aIndex,i);
851
0
852
0
    // ignore collapsed children
853
0
    if (!child->IsXULCollapsed())
854
0
    {
855
0
      nsSize childSize = child->GetXULPrefSize(aState);
856
0
857
0
      nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
858
0
    }
859
0
  }
860
0
861
0
  row->mPref = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
862
0
863
0
  return row->mPref;
864
0
}
865
866
nscoord
867
nsGrid::GetMinRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
868
0
{
869
0
  RebuildIfNeeded();
870
0
871
0
  nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
872
0
873
0
  if (row->IsXULCollapsed())
874
0
    return 0;
875
0
876
0
  if (row->IsMinSet())
877
0
    return row->mMin;
878
0
879
0
  nsIFrame* box = row->mBox;
880
0
881
0
  // set in CSS?
882
0
  if (box) {
883
0
    bool widthSet, heightSet;
884
0
    nsSize cssSize(-1, -1);
885
0
    nsIFrame::AddXULMinSize(aState, box, cssSize, widthSet, heightSet);
886
0
887
0
    row->mMin = GET_HEIGHT(cssSize, aIsHorizontal);
888
0
889
0
    // yep do nothing.
890
0
    if (row->mMin != -1)
891
0
      return row->mMin;
892
0
  }
893
0
894
0
  // get the offsets so they are cached.
895
0
  nscoord top;
896
0
  nscoord bottom;
897
0
  GetRowOffsets(aIndex, top, bottom, aIsHorizontal);
898
0
899
0
  // is the row bogus? If so then just ask it for its size
900
0
  // it should not be affected by cells in the grid.
901
0
  if (row->mIsBogus)
902
0
  {
903
0
     nsSize size(0,0);
904
0
     if (box) {
905
0
       size = box->GetXULPrefSize(aState);
906
0
       nsBox::AddMargin(box, size);
907
0
       nsGridLayout2::AddOffset(box, size);
908
0
     }
909
0
910
0
     row->mMin = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
911
0
     return row->mMin;
912
0
  }
913
0
914
0
  nsSize size(0,0);
915
0
916
0
  nsGridCell* child;
917
0
918
0
  int32_t count = GetColumnCount(aIsHorizontal);
919
0
920
0
  for (int32_t i=0; i < count; i++)
921
0
  {
922
0
    if (aIsHorizontal)
923
0
     child = GetCellAt(i,aIndex);
924
0
    else
925
0
     child = GetCellAt(aIndex,i);
926
0
927
0
    // ignore collapsed children
928
0
    if (!child->IsXULCollapsed())
929
0
    {
930
0
      nsSize childSize = child->GetXULMinSize(aState);
931
0
932
0
      nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
933
0
    }
934
0
  }
935
0
936
0
  row->mMin = GET_HEIGHT(size, aIsHorizontal);
937
0
938
0
  return row->mMin;
939
0
}
940
941
nscoord
942
nsGrid::GetMaxRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
943
0
{
944
0
  RebuildIfNeeded();
945
0
946
0
  nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
947
0
948
0
  if (row->IsXULCollapsed())
949
0
    return 0;
950
0
951
0
  if (row->IsMaxSet())
952
0
    return row->mMax;
953
0
954
0
  nsIFrame* box = row->mBox;
955
0
956
0
  // set in CSS?
957
0
  if (box) {
958
0
    bool widthSet, heightSet;
959
0
    nsSize cssSize(-1, -1);
960
0
    nsIFrame::AddXULMaxSize(box, cssSize, widthSet, heightSet);
961
0
962
0
    row->mMax = GET_HEIGHT(cssSize, aIsHorizontal);
963
0
964
0
    // yep do nothing.
965
0
    if (row->mMax != -1)
966
0
      return row->mMax;
967
0
  }
968
0
969
0
  // get the offsets so they are cached.
970
0
  nscoord top;
971
0
  nscoord bottom;
972
0
  GetRowOffsets(aIndex, top, bottom, aIsHorizontal);
973
0
974
0
  // is the row bogus? If so then just ask it for its size
975
0
  // it should not be affected by cells in the grid.
976
0
  if (row->mIsBogus)
977
0
  {
978
0
     nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
979
0
     if (box) {
980
0
       size = box->GetXULPrefSize(aState);
981
0
       nsBox::AddMargin(box, size);
982
0
       nsGridLayout2::AddOffset(box, size);
983
0
     }
984
0
985
0
     row->mMax = GET_HEIGHT(size, aIsHorizontal);
986
0
     return row->mMax;
987
0
  }
988
0
989
0
  nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
990
0
991
0
  nsGridCell* child;
992
0
993
0
  int32_t count = GetColumnCount(aIsHorizontal);
994
0
995
0
  for (int32_t i=0; i < count; i++)
996
0
  {
997
0
    if (aIsHorizontal)
998
0
     child = GetCellAt(i,aIndex);
999
0
    else
1000
0
     child = GetCellAt(aIndex,i);
1001
0
1002
0
    // ignore collapsed children
1003
0
    if (!child->IsXULCollapsed())
1004
0
    {
1005
0
      nsSize min = child->GetXULMinSize(aState);
1006
0
      nsSize childSize = nsBox::BoundsCheckMinMax(min, child->GetXULMaxSize(aState));
1007
0
      nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
1008
0
    }
1009
0
  }
1010
0
1011
0
  row->mMax = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
1012
0
1013
0
  return row->mMax;
1014
0
}
1015
1016
bool
1017
nsGrid::IsGrid(nsIFrame* aBox)
1018
0
{
1019
0
  nsIGridPart* part = GetPartFromBox(aBox);
1020
0
  if (!part)
1021
0
    return false;
1022
0
1023
0
  nsGridLayout2* grid = part->CastToGridLayout();
1024
0
1025
0
  if (grid)
1026
0
    return true;
1027
0
1028
0
  return false;
1029
0
}
1030
1031
/**
1032
 * This get the flexibilty of the row at aIndex. It's not trivial. There are a few
1033
 * things we need to look at. Specifically we need to see if any <rows> or <columns>
1034
 * tags are around us. Their flexibilty will affect ours.
1035
 */
1036
nscoord
1037
nsGrid::GetRowFlex(int32_t aIndex, bool aIsHorizontal)
1038
0
{
1039
0
  RebuildIfNeeded();
1040
0
1041
0
  nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
1042
0
1043
0
  if (row->IsFlexSet())
1044
0
    return row->mFlex;
1045
0
1046
0
  nsIFrame* box = row->mBox;
1047
0
  row->mFlex = 0;
1048
0
1049
0
  if (box) {
1050
0
1051
0
    // We need our flex but a inflexible row could be around us. If so
1052
0
    // neither are we. However if its the row tag just inside the grid it won't
1053
0
    // affect us. We need to do this for this case:
1054
0
    // <grid>
1055
0
    //   <rows>
1056
0
    //     <rows> // this is not flexible. So our children should not be flexible
1057
0
    //        <row flex="1"/>
1058
0
    //        <row flex="1"/>
1059
0
    //     </rows>
1060
0
    //        <row/>
1061
0
    //   </rows>
1062
0
    // </grid>
1063
0
    //
1064
0
    // or..
1065
0
    //
1066
0
    // <grid>
1067
0
    //  <rows>
1068
0
    //   <rows> // this is not flexible. So our children should not be flexible
1069
0
    //     <rows flex="1">
1070
0
    //        <row flex="1"/>
1071
0
    //        <row flex="1"/>
1072
0
    //     </rows>
1073
0
    //        <row/>
1074
0
    //   </rows>
1075
0
    //  </row>
1076
0
    // </grid>
1077
0
1078
0
1079
0
    // So here is how it looks
1080
0
    //
1081
0
    // <grid>
1082
0
    //   <rows>   // parentsParent
1083
0
    //     <rows> // parent
1084
0
    //        <row flex="1"/>
1085
0
    //        <row flex="1"/>
1086
0
    //     </rows>
1087
0
    //        <row/>
1088
0
    //   </rows>
1089
0
    // </grid>
1090
0
1091
0
    // so the answer is simple: 1) Walk our parent chain. 2) If we find
1092
0
    // someone who is not flexible and they aren't the rows immediately in
1093
0
    // the grid. 3) Then we are not flexible
1094
0
1095
0
    box = GetScrollBox(box);
1096
0
    nsIFrame* parent = nsBox::GetParentXULBox(box);
1097
0
    nsIFrame* parentsParent=nullptr;
1098
0
1099
0
    while(parent)
1100
0
    {
1101
0
      parent = GetScrollBox(parent);
1102
0
      parentsParent = nsBox::GetParentXULBox(parent);
1103
0
1104
0
      // if our parents parent is not a grid
1105
0
      // the get its flex. If its 0 then we are
1106
0
      // not flexible.
1107
0
      if (parentsParent) {
1108
0
        if (!IsGrid(parentsParent)) {
1109
0
          nscoord flex = parent->GetXULFlex();
1110
0
          nsIFrame::AddXULFlex(parent, flex);
1111
0
          if (flex == 0) {
1112
0
            row->mFlex = 0;
1113
0
            return row->mFlex;
1114
0
          }
1115
0
        } else
1116
0
          break;
1117
0
      }
1118
0
1119
0
      parent = parentsParent;
1120
0
    }
1121
0
1122
0
    // get the row flex.
1123
0
    row->mFlex = box->GetXULFlex();
1124
0
    nsIFrame::AddXULFlex(box, row->mFlex);
1125
0
  }
1126
0
1127
0
  return row->mFlex;
1128
0
}
1129
1130
void
1131
nsGrid::SetLargestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
1132
0
{
1133
0
  if (aIsHorizontal) {
1134
0
    if (aSize.height < aHeight)
1135
0
      aSize.height = aHeight;
1136
0
  } else {
1137
0
    if (aSize.width < aHeight)
1138
0
      aSize.width = aHeight;
1139
0
  }
1140
0
}
1141
1142
void
1143
nsGrid::SetSmallestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
1144
0
{
1145
0
  if (aIsHorizontal) {
1146
0
    if (aSize.height > aHeight)
1147
0
      aSize.height = aHeight;
1148
0
  } else {
1149
0
    if (aSize.width < aHeight)
1150
0
      aSize.width = aHeight;
1151
0
  }
1152
0
}
1153
1154
int32_t
1155
nsGrid::GetRowCount(int32_t aIsHorizontal)
1156
0
{
1157
0
  RebuildIfNeeded();
1158
0
1159
0
  if (aIsHorizontal)
1160
0
    return mRowCount;
1161
0
  else
1162
0
    return mColumnCount;
1163
0
}
1164
1165
int32_t
1166
nsGrid::GetColumnCount(int32_t aIsHorizontal)
1167
0
{
1168
0
  return GetRowCount(!aIsHorizontal);
1169
0
}
1170
1171
/*
1172
 * A cell in the given row or columns at the given index has had a child added or removed
1173
 */
1174
void
1175
nsGrid::CellAddedOrRemoved(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
1176
0
{
1177
0
  // TBD see if the cell will fit in our current row. If it will
1178
0
  // just add it in.
1179
0
  // but for now rebuild everything.
1180
0
  if (mMarkingDirty)
1181
0
    return;
1182
0
1183
0
  NeedsRebuild(aState);
1184
0
}
1185
1186
/**
1187
 * A row or columns at the given index had been added or removed
1188
 */
1189
void
1190
nsGrid::RowAddedOrRemoved(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
1191
0
{
1192
0
  // TBD see if we have extra room in the table and just add the new row in
1193
0
  // for now rebuild the world
1194
0
  if (mMarkingDirty)
1195
0
    return;
1196
0
1197
0
  NeedsRebuild(aState);
1198
0
}
1199
1200
/*
1201
 * Scrollframes are tranparent. If this is given a scrollframe is will return the
1202
 * frame inside. If there is no scrollframe it does nothing.
1203
 */
1204
nsIFrame*
1205
nsGrid::GetScrolledBox(nsIFrame* aChild)
1206
0
{
1207
0
  // first see if it is a scrollframe. If so walk down into it and get the scrolled child
1208
0
      nsIScrollableFrame *scrollFrame = do_QueryFrame(aChild);
1209
0
      if (scrollFrame) {
1210
0
         nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
1211
0
         NS_ASSERTION(scrolledFrame,"Error no scroll frame!!");
1212
0
         return scrolledFrame;
1213
0
      }
1214
0
1215
0
      return aChild;
1216
0
}
1217
1218
/*
1219
 * Scrollframes are tranparent. If this is given a child in a scrollframe is will return the
1220
 * scrollframe ourside it. If there is no scrollframe it does nothing.
1221
 */
1222
nsIFrame*
1223
nsGrid::GetScrollBox(nsIFrame* aChild)
1224
0
{
1225
0
  if (!aChild)
1226
0
    return nullptr;
1227
0
1228
0
  // get parent
1229
0
  nsIFrame* parent = nsBox::GetParentXULBox(aChild);
1230
0
1231
0
  // walk up until we find a scrollframe or a part
1232
0
  // if it's a scrollframe return it.
1233
0
  // if it's a parent then the child passed does not
1234
0
  // have a scroll frame immediately wrapped around it.
1235
0
  while (parent) {
1236
0
    nsIScrollableFrame *scrollFrame = do_QueryFrame(parent);
1237
0
    // scrollframe? Yep return it.
1238
0
    if (scrollFrame)
1239
0
      return parent;
1240
0
1241
0
    nsCOMPtr<nsIGridPart> parentGridRow = GetPartFromBox(parent);
1242
0
    // if a part then just return the child
1243
0
    if (parentGridRow)
1244
0
      break;
1245
0
1246
0
    parent = nsBox::GetParentXULBox(parent);
1247
0
  }
1248
0
1249
0
  return aChild;
1250
0
}
1251
1252
1253
1254
#ifdef DEBUG_grid
1255
void
1256
nsGrid::PrintCellMap()
1257
{
1258
1259
  printf("-----Columns------\n");
1260
  for (int x=0; x < mColumnCount; x++)
1261
  {
1262
1263
    nsGridRow* column = GetColumnAt(x);
1264
    printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
1265
  }
1266
1267
  printf("\n-----Rows------\n");
1268
  for (x=0; x < mRowCount; x++)
1269
  {
1270
    nsGridRow* column = GetRowAt(x);
1271
    printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
1272
  }
1273
1274
  printf("\n");
1275
1276
}
1277
#endif