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