summaryrefslogtreecommitdiffstats
path: root/layout/xul/grid/nsGrid.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/xul/grid/nsGrid.cpp')
-rw-r--r--layout/xul/grid/nsGrid.cpp1276
1 files changed, 1276 insertions, 0 deletions
diff --git a/layout/xul/grid/nsGrid.cpp b/layout/xul/grid/nsGrid.cpp
new file mode 100644
index 000000000..762bbfcd7
--- /dev/null
+++ b/layout/xul/grid/nsGrid.cpp
@@ -0,0 +1,1276 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// Eric Vaughan
+// Netscape Communications
+//
+// See documentation in associated header file
+//
+
+#include "nsGrid.h"
+#include "nsGridRowGroupLayout.h"
+#include "nsBox.h"
+#include "nsIScrollableFrame.h"
+#include "nsSprocketLayout.h"
+#include "nsGridLayout2.h"
+#include "nsGridRow.h"
+#include "nsGridCell.h"
+#include "mozilla/ReflowInput.h"
+
+/*
+The grid control expands the idea of boxes from 1 dimension to 2 dimensions.
+It works by allowing the XUL to define a collection of rows and columns and then
+stacking them on top of each other. Here is and example.
+
+Example 1:
+
+<grid>
+ <columns>
+ <column/>
+ <column/>
+ </columns>
+
+ <rows>
+ <row/>
+ <row/>
+ </rows>
+</grid>
+
+example 2:
+
+<grid>
+ <columns>
+ <column flex="1"/>
+ <column flex="1"/>
+ </columns>
+
+ <rows>
+ <row>
+ <text value="hello"/>
+ <text value="there"/>
+ </row>
+ </rows>
+</grid>
+
+example 3:
+
+<grid>
+
+<rows>
+ <row>
+ <text value="hello"/>
+ <text value="there"/>
+ </row>
+ </rows>
+
+ <columns>
+ <column>
+ <text value="Hey I'm in the column and I'm on top!"/>
+ </column>
+ <column/>
+ </columns>
+
+</grid>
+
+Usually the columns are first and the rows are second, so the rows will be drawn on top of the columns.
+You can reverse this by defining the rows first.
+Other tags are then placed in the <row> or <column> tags causing the grid to accommodate everyone.
+It does this by creating 3 things: A cellmap, a row list, and a column list. The cellmap is a 2
+dimensional array of nsGridCells. Each cell contains 2 boxes. One cell from the column list
+and one from the row list. When a cell is asked for its size it returns that smallest size it can
+be to accommodate the 2 cells. Row lists and Column lists use the same data structure: nsGridRow.
+Essentially a row and column are the same except a row goes alone the x axis and a column the y.
+To make things easier and save code everything is written in terms of the x dimension. A flag is
+passed in called "isHorizontal" that can flip the calculations to the y axis.
+
+Usually the number of cells in a row match the number of columns, but not always.
+It is possible to define 5 columns for a grid but have 10 cells in one of the rows.
+In this case 5 extra columns will be added to the column list to handle the situation.
+These are called extraColumns/Rows.
+*/
+
+using namespace mozilla;
+
+nsGrid::nsGrid():mBox(nullptr),
+ mRowsBox(nullptr),
+ mColumnsBox(nullptr),
+ mNeedsRebuild(true),
+ mRowCount(0),
+ mColumnCount(0),
+ mExtraRowCount(0),
+ mExtraColumnCount(0),
+ mMarkingDirty(false)
+{
+ MOZ_COUNT_CTOR(nsGrid);
+}
+
+nsGrid::~nsGrid()
+{
+ FreeMap();
+ MOZ_COUNT_DTOR(nsGrid);
+}
+
+/*
+ * This is called whenever something major happens in the grid. And example
+ * might be when many cells or row are added. It sets a flag signaling that
+ * all the grids caches information should be recalculated.
+ */
+void
+nsGrid::NeedsRebuild(nsBoxLayoutState& aState)
+{
+ if (mNeedsRebuild)
+ return;
+
+ // iterate through columns and rows and dirty them
+ mNeedsRebuild = true;
+
+ // find the new row and column box. They could have
+ // been changed.
+ mRowsBox = nullptr;
+ mColumnsBox = nullptr;
+ FindRowsAndColumns(&mRowsBox, &mColumnsBox);
+
+ // tell all the rows and columns they are dirty
+ DirtyRows(mRowsBox, aState);
+ DirtyRows(mColumnsBox, aState);
+}
+
+
+
+/**
+ * If we are marked for rebuild. Then build everything
+ */
+void
+nsGrid::RebuildIfNeeded()
+{
+ if (!mNeedsRebuild)
+ return;
+
+ mNeedsRebuild = false;
+
+ // find the row and columns frames
+ FindRowsAndColumns(&mRowsBox, &mColumnsBox);
+
+ // count the rows and columns
+ int32_t computedRowCount = 0;
+ int32_t computedColumnCount = 0;
+ int32_t rowCount = 0;
+ int32_t columnCount = 0;
+
+ CountRowsColumns(mRowsBox, rowCount, computedColumnCount);
+ CountRowsColumns(mColumnsBox, columnCount, computedRowCount);
+
+ // computedRowCount are the actual number of rows as determined by the
+ // columns children.
+ // computedColumnCount are the number of columns as determined by the number
+ // of rows children.
+ // We can use this information to see how many extra columns or rows we need.
+ // This can happen if there are are more children in a row that number of columns
+ // defined. Example:
+ //
+ // <columns>
+ // <column/>
+ // </columns>
+ //
+ // <rows>
+ // <row>
+ // <button/><button/>
+ // </row>
+ // </rows>
+ //
+ // computedColumnCount = 2 // for the 2 buttons in the row tag
+ // computedRowCount = 0 // there is nothing in the column tag
+ // mColumnCount = 1 // one column defined
+ // mRowCount = 1 // one row defined
+ //
+ // So in this case we need to make 1 extra column.
+ //
+
+ // Make sure to update mExtraColumnCount no matter what, since it might
+ // happen that we now have as many columns as are defined, and we wouldn't
+ // want to have a positive mExtraColumnCount hanging about in that case!
+ mExtraColumnCount = computedColumnCount - columnCount;
+ if (computedColumnCount > columnCount) {
+ columnCount = computedColumnCount;
+ }
+
+ // Same for rows.
+ mExtraRowCount = computedRowCount - rowCount;
+ if (computedRowCount > rowCount) {
+ rowCount = computedRowCount;
+ }
+
+ // build and poplulate row and columns arrays
+ mRows = BuildRows(mRowsBox, rowCount, true);
+ mColumns = BuildRows(mColumnsBox, columnCount, false);
+
+ // build and populate the cell map
+ mCellMap = BuildCellMap(rowCount, columnCount);
+
+ mRowCount = rowCount;
+ mColumnCount = columnCount;
+
+ // populate the cell map from column and row children
+ PopulateCellMap(mRows.get(), mColumns.get(), mRowCount, mColumnCount, true);
+ PopulateCellMap(mColumns.get(), mRows.get(), mColumnCount, mRowCount, false);
+}
+
+void
+nsGrid::FreeMap()
+{
+ mRows = nullptr;
+ mColumns = nullptr;
+ mCellMap = nullptr;
+ mColumnCount = 0;
+ mRowCount = 0;
+ mExtraColumnCount = 0;
+ mExtraRowCount = 0;
+ mRowsBox = nullptr;
+ mColumnsBox = nullptr;
+}
+
+/**
+ * finds the first <rows> and <columns> tags in the <grid> tag
+ */
+void
+nsGrid::FindRowsAndColumns(nsIFrame** aRows, nsIFrame** aColumns)
+{
+ *aRows = nullptr;
+ *aColumns = nullptr;
+
+ // find the boxes that contain our rows and columns
+ nsIFrame* child = nullptr;
+ // if we have <grid></grid> then mBox will be null (bug 125689)
+ if (mBox)
+ child = nsBox::GetChildXULBox(mBox);
+
+ while(child)
+ {
+ nsIFrame* oldBox = child;
+ nsIScrollableFrame *scrollFrame = do_QueryFrame(child);
+ if (scrollFrame) {
+ nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
+ NS_ASSERTION(scrolledFrame,"Error no scroll frame!!");
+ child = do_QueryFrame(scrolledFrame);
+ }
+
+ nsCOMPtr<nsIGridPart> monument = GetPartFromBox(child);
+ if (monument)
+ {
+ nsGridRowGroupLayout* rowGroup = monument->CastToRowGroupLayout();
+ if (rowGroup) {
+ bool isHorizontal = !nsSprocketLayout::IsXULHorizontal(child);
+ if (isHorizontal)
+ *aRows = child;
+ else
+ *aColumns = child;
+
+ if (*aRows && *aColumns)
+ return;
+ }
+ }
+
+ if (scrollFrame) {
+ child = oldBox;
+ }
+
+ child = nsBox::GetNextXULBox(child);
+ }
+}
+
+/**
+ * Count the number of rows and columns in the given box. aRowCount well become the actual number
+ * rows defined in the xul. aComputedColumnCount will become the number of columns by counting the number
+ * of cells in each row.
+ */
+void
+nsGrid::CountRowsColumns(nsIFrame* aRowBox, int32_t& aRowCount, int32_t& aComputedColumnCount)
+{
+ aRowCount = 0;
+ aComputedColumnCount = 0;
+ // get the rowboxes layout manager. Then ask it to do the work for us
+ if (aRowBox) {
+ nsCOMPtr<nsIGridPart> monument = GetPartFromBox(aRowBox);
+ if (monument)
+ monument->CountRowsColumns(aRowBox, aRowCount, aComputedColumnCount);
+ }
+}
+
+
+/**
+ * Given the number of rows create nsGridRow objects for them and full them out.
+ */
+UniquePtr<nsGridRow[]>
+nsGrid::BuildRows(nsIFrame* aBox, int32_t aRowCount, bool aIsHorizontal)
+{
+ // if no rows then return null
+ if (aRowCount == 0) {
+ return nullptr;
+ }
+
+ // create the array
+ UniquePtr<nsGridRow[]> row;
+
+ // only create new rows if we have to. Reuse old rows.
+ if (aIsHorizontal)
+ {
+ if (aRowCount > mRowCount) {
+ row = MakeUnique<nsGridRow[]>(aRowCount);
+ } else {
+ for (int32_t i=0; i < mRowCount; i++)
+ mRows[i].Init(nullptr, false);
+
+ row = Move(mRows);
+ }
+ } else {
+ if (aRowCount > mColumnCount) {
+ row = MakeUnique<nsGridRow[]>(aRowCount);
+ } else {
+ for (int32_t i=0; i < mColumnCount; i++)
+ mColumns[i].Init(nullptr, false);
+
+ row = Move(mColumns);
+ }
+ }
+
+ // populate it if we can. If not it will contain only dynamic columns
+ if (aBox)
+ {
+ nsCOMPtr<nsIGridPart> monument = GetPartFromBox(aBox);
+ if (monument) {
+ monument->BuildRows(aBox, row.get());
+ }
+ }
+
+ return row;
+}
+
+
+/**
+ * Given the number of rows and columns. Build a cellmap
+ */
+UniquePtr<nsGridCell[]>
+nsGrid::BuildCellMap(int32_t aRows, int32_t aColumns)
+{
+ int32_t size = aRows*aColumns;
+ int32_t oldsize = mRowCount*mColumnCount;
+ if (size == 0) {
+ return nullptr;
+ }
+
+ if (size > oldsize) {
+ return MakeUnique<nsGridCell[]>(size);
+ }
+
+ // clear out cellmap
+ for (int32_t i=0; i < oldsize; i++) {
+ mCellMap[i].SetBoxInRow(nullptr);
+ mCellMap[i].SetBoxInColumn(nullptr);
+ }
+ return Move(mCellMap);
+}
+
+/**
+ * Run through all the cells in the rows and columns and populate then with 2 cells. One from the row and one
+ * from the column
+ */
+void
+nsGrid::PopulateCellMap(nsGridRow* aRows, nsGridRow* aColumns, int32_t aRowCount, int32_t aColumnCount, bool aIsHorizontal)
+{
+ if (!aRows)
+ return;
+
+ // look through the columns
+ int32_t j = 0;
+
+ for(int32_t i=0; i < aRowCount; i++)
+ {
+ nsIFrame* child = nullptr;
+ nsGridRow* row = &aRows[i];
+
+ // skip bogus rows. They have no cells
+ if (row->mIsBogus)
+ continue;
+
+ child = row->mBox;
+ if (child) {
+ child = nsBox::GetChildXULBox(child);
+
+ j = 0;
+
+ while(child && j < aColumnCount)
+ {
+ // skip bogus column. They have no cells
+ nsGridRow* column = &aColumns[j];
+ if (column->mIsBogus)
+ {
+ j++;
+ continue;
+ }
+
+ if (aIsHorizontal)
+ GetCellAt(j,i)->SetBoxInRow(child);
+ else
+ GetCellAt(i,j)->SetBoxInColumn(child);
+
+ child = nsBox::GetNextXULBox(child);
+
+ j++;
+ }
+ }
+ }
+}
+
+/**
+ * Run through the rows in the given box and mark them dirty so they
+ * will get recalculated and get a layout.
+ */
+void
+nsGrid::DirtyRows(nsIFrame* aRowBox, nsBoxLayoutState& aState)
+{
+ // make sure we prevent others from dirtying things.
+ mMarkingDirty = true;
+
+ // if the box is a grid part have it recursively hand it.
+ if (aRowBox) {
+ nsCOMPtr<nsIGridPart> part = GetPartFromBox(aRowBox);
+ if (part)
+ part->DirtyRows(aRowBox, aState);
+ }
+
+ mMarkingDirty = false;
+}
+
+nsGridRow*
+nsGrid::GetColumnAt(int32_t aIndex, bool aIsHorizontal)
+{
+ return GetRowAt(aIndex, !aIsHorizontal);
+}
+
+nsGridRow*
+nsGrid::GetRowAt(int32_t aIndex, bool aIsHorizontal)
+{
+ RebuildIfNeeded();
+
+ if (aIsHorizontal) {
+ NS_ASSERTION(aIndex < mRowCount && aIndex >= 0, "Index out of range");
+ return &mRows[aIndex];
+ } else {
+ NS_ASSERTION(aIndex < mColumnCount && aIndex >= 0, "Index out of range");
+ return &mColumns[aIndex];
+ }
+}
+
+nsGridCell*
+nsGrid::GetCellAt(int32_t aX, int32_t aY)
+{
+ RebuildIfNeeded();
+
+ NS_ASSERTION(aY < mRowCount && aY >= 0, "Index out of range");
+ NS_ASSERTION(aX < mColumnCount && aX >= 0, "Index out of range");
+ return &mCellMap[aY*mColumnCount+aX];
+}
+
+int32_t
+nsGrid::GetExtraColumnCount(bool aIsHorizontal)
+{
+ return GetExtraRowCount(!aIsHorizontal);
+}
+
+int32_t
+nsGrid::GetExtraRowCount(bool aIsHorizontal)
+{
+ RebuildIfNeeded();
+
+ if (aIsHorizontal)
+ return mExtraRowCount;
+ else
+ return mExtraColumnCount;
+}
+
+
+/**
+ * These methods return the preferred, min, max sizes for a given row index.
+ * aIsHorizontal if aIsHorizontal is true. If you pass false you will get the inverse.
+ * As if you called GetPrefColumnSize(aState, index, aPref)
+ */
+nsSize
+nsGrid::GetPrefRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
+{
+ nsSize size(0,0);
+ if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
+ return size;
+
+ nscoord height = GetPrefRowHeight(aState, aRowIndex, aIsHorizontal);
+ SetLargestSize(size, height, aIsHorizontal);
+
+ return size;
+}
+
+nsSize
+nsGrid::GetMinRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
+{
+ nsSize size(0,0);
+ if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
+ return size;
+
+ nscoord height = GetMinRowHeight(aState, aRowIndex, aIsHorizontal);
+ SetLargestSize(size, height, aIsHorizontal);
+
+ return size;
+}
+
+nsSize
+nsGrid::GetMaxRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
+{
+ nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
+ if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
+ return size;
+
+ nscoord height = GetMaxRowHeight(aState, aRowIndex, aIsHorizontal);
+ SetSmallestSize(size, height, aIsHorizontal);
+
+ return size;
+}
+
+// static
+nsIGridPart*
+nsGrid::GetPartFromBox(nsIFrame* aBox)
+{
+ if (!aBox)
+ return nullptr;
+
+ nsBoxLayout* layout = aBox->GetXULLayoutManager();
+ return layout ? layout->AsGridPart() : nullptr;
+}
+
+nsMargin
+nsGrid::GetBoxTotalMargin(nsIFrame* aBox, bool aIsHorizontal)
+{
+ nsMargin margin(0,0,0,0);
+ // walk the boxes parent chain getting the border/padding/margin of our parent rows
+
+ // first get the layour manager
+ nsIGridPart* part = GetPartFromBox(aBox);
+ if (part)
+ margin = part->GetTotalMargin(aBox, aIsHorizontal);
+
+ return margin;
+}
+
+/**
+ * The first and last rows can be affected by <rows> tags with borders or margin
+ * gets first and last rows and their indexes.
+ * If it fails because there are no rows then:
+ * FirstRow is nullptr
+ * LastRow is nullptr
+ * aFirstIndex = -1
+ * aLastIndex = -1
+ */
+void
+nsGrid::GetFirstAndLastRow(int32_t& aFirstIndex,
+ int32_t& aLastIndex,
+ nsGridRow*& aFirstRow,
+ nsGridRow*& aLastRow,
+ bool aIsHorizontal)
+{
+ aFirstRow = nullptr;
+ aLastRow = nullptr;
+ aFirstIndex = -1;
+ aLastIndex = -1;
+
+ int32_t count = GetRowCount(aIsHorizontal);
+
+ if (count == 0)
+ return;
+
+
+ // We could have collapsed columns either before or after our index.
+ // they should not count. So if we are the 5th row and the first 4 are
+ // collaped we become the first row. Or if we are the 9th row and
+ // 10 up to the last row are collapsed we then become the last.
+
+ // see if we are first
+ int32_t i;
+ for (i=0; i < count; i++)
+ {
+ nsGridRow* row = GetRowAt(i,aIsHorizontal);
+ if (!row->IsXULCollapsed()) {
+ aFirstIndex = i;
+ aFirstRow = row;
+ break;
+ }
+ }
+
+ // see if we are last
+ for (i=count-1; i >= 0; i--)
+ {
+ nsGridRow* row = GetRowAt(i,aIsHorizontal);
+ if (!row->IsXULCollapsed()) {
+ aLastIndex = i;
+ aLastRow = row;
+ break;
+ }
+
+ }
+}
+
+/**
+ * A row can have a top and bottom offset. Usually this is just the top and bottom border/padding.
+ * However if the row is the first or last it could be affected by the fact a column or columns could
+ * have a top or bottom margin.
+ */
+void
+nsGrid::GetRowOffsets(int32_t aIndex, nscoord& aTop, nscoord& aBottom, bool aIsHorizontal)
+{
+
+ RebuildIfNeeded();
+
+ nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
+
+ if (row->IsOffsetSet())
+ {
+ aTop = row->mTop;
+ aBottom = row->mBottom;
+ return;
+ }
+
+ // first get the rows top and bottom border and padding
+ nsIFrame* box = row->GetBox();
+
+ // add up all the padding
+ nsMargin margin(0,0,0,0);
+ nsMargin border(0,0,0,0);
+ nsMargin padding(0,0,0,0);
+ nsMargin totalBorderPadding(0,0,0,0);
+ nsMargin totalMargin(0,0,0,0);
+
+ // if there is a box and it's not bogus take its
+ // borders padding into account
+ if (box && !row->mIsBogus)
+ {
+ if (!box->IsXULCollapsed())
+ {
+ // get real border and padding. GetXULBorderAndPadding
+ // is redefined on nsGridRowLeafFrame. If we called it here
+ // we would be in finite recurson.
+ box->GetXULBorder(border);
+ box->GetXULPadding(padding);
+
+ totalBorderPadding += border;
+ totalBorderPadding += padding;
+ }
+
+ // if we are the first or last row
+ // take into account <rows> tags around us
+ // that could have borders or margins.
+ // fortunately they only affect the first
+ // and last row inside the <rows> tag
+
+ totalMargin = GetBoxTotalMargin(box, aIsHorizontal);
+ }
+
+ if (aIsHorizontal) {
+ row->mTop = totalBorderPadding.top;
+ row->mBottom = totalBorderPadding.bottom;
+ row->mTopMargin = totalMargin.top;
+ row->mBottomMargin = totalMargin.bottom;
+ } else {
+ row->mTop = totalBorderPadding.left;
+ row->mBottom = totalBorderPadding.right;
+ row->mTopMargin = totalMargin.left;
+ row->mBottomMargin = totalMargin.right;
+ }
+
+ // if we are the first or last row take into account the top and bottom borders
+ // of each columns.
+
+ // If we are the first row then get the largest top border/padding in
+ // our columns. If that's larger than the rows top border/padding use it.
+
+ // If we are the last row then get the largest bottom border/padding in
+ // our columns. If that's larger than the rows bottom border/padding use it.
+ int32_t firstIndex = 0;
+ int32_t lastIndex = 0;
+ nsGridRow* firstRow = nullptr;
+ nsGridRow* lastRow = nullptr;
+ GetFirstAndLastRow(firstIndex, lastIndex, firstRow, lastRow, aIsHorizontal);
+
+ if (aIndex == firstIndex || aIndex == lastIndex) {
+ nscoord maxTop = 0;
+ nscoord maxBottom = 0;
+
+ // run through the columns. Look at each column
+ // pick the largest top border or bottom border
+ int32_t count = GetColumnCount(aIsHorizontal);
+
+ for (int32_t i=0; i < count; i++)
+ {
+ nsMargin totalChildBorderPadding(0,0,0,0);
+
+ nsGridRow* column = GetColumnAt(i,aIsHorizontal);
+ nsIFrame* box = column->GetBox();
+
+ if (box)
+ {
+ // ignore collapsed children
+ if (!box->IsXULCollapsed())
+ {
+ // include the margin of the columns. To the row
+ // at this point border/padding and margins all added
+ // up to more needed space.
+ margin = GetBoxTotalMargin(box, !aIsHorizontal);
+ // get real border and padding. GetXULBorderAndPadding
+ // is redefined on nsGridRowLeafFrame. If we called it here
+ // we would be in finite recurson.
+ box->GetXULBorder(border);
+ box->GetXULPadding(padding);
+ totalChildBorderPadding += border;
+ totalChildBorderPadding += padding;
+ totalChildBorderPadding += margin;
+ }
+
+ nscoord top;
+ nscoord bottom;
+
+ // pick the largest top margin
+ if (aIndex == firstIndex) {
+ if (aIsHorizontal) {
+ top = totalChildBorderPadding.top;
+ } else {
+ top = totalChildBorderPadding.left;
+ }
+ if (top > maxTop)
+ maxTop = top;
+ }
+
+ // pick the largest bottom margin
+ if (aIndex == lastIndex) {
+ if (aIsHorizontal) {
+ bottom = totalChildBorderPadding.bottom;
+ } else {
+ bottom = totalChildBorderPadding.right;
+ }
+ if (bottom > maxBottom)
+ maxBottom = bottom;
+ }
+
+ }
+
+ // If the biggest top border/padding the columns is larger than this rows top border/padding
+ // the use it.
+ if (aIndex == firstIndex) {
+ if (maxTop > (row->mTop + row->mTopMargin))
+ row->mTop = maxTop - row->mTopMargin;
+ }
+
+ // If the biggest bottom border/padding the columns is larger than this rows bottom border/padding
+ // the use it.
+ if (aIndex == lastIndex) {
+ if (maxBottom > (row->mBottom + row->mBottomMargin))
+ row->mBottom = maxBottom - row->mBottomMargin;
+ }
+ }
+ }
+
+ aTop = row->mTop;
+ aBottom = row->mBottom;
+}
+
+/**
+ * These methods return the preferred, min, max coord for a given row index if
+ * aIsHorizontal is true. If you pass false you will get the inverse.
+ * As if you called GetPrefColumnHeight(aState, index, aPref).
+ */
+nscoord
+nsGrid::GetPrefRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
+{
+ RebuildIfNeeded();
+
+ nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
+
+ if (row->IsXULCollapsed())
+ return 0;
+
+ if (row->IsPrefSet())
+ return row->mPref;
+
+ nsIFrame* box = row->mBox;
+
+ // set in CSS?
+ if (box)
+ {
+ bool widthSet, heightSet;
+ nsSize cssSize(-1, -1);
+ nsIFrame::AddXULPrefSize(box, cssSize, widthSet, heightSet);
+
+ row->mPref = GET_HEIGHT(cssSize, aIsHorizontal);
+
+ // yep do nothing.
+ if (row->mPref != -1)
+ return row->mPref;
+ }
+
+ // get the offsets so they are cached.
+ nscoord top;
+ nscoord bottom;
+ GetRowOffsets(aIndex, top, bottom, aIsHorizontal);
+
+ // is the row bogus? If so then just ask it for its size
+ // it should not be affected by cells in the grid.
+ if (row->mIsBogus)
+ {
+ nsSize size(0,0);
+ if (box)
+ {
+ size = box->GetXULPrefSize(aState);
+ nsBox::AddMargin(box, size);
+ nsGridLayout2::AddOffset(box, size);
+ }
+
+ row->mPref = GET_HEIGHT(size, aIsHorizontal);
+ return row->mPref;
+ }
+
+ nsSize size(0,0);
+
+ nsGridCell* child;
+
+ int32_t count = GetColumnCount(aIsHorizontal);
+
+ for (int32_t i=0; i < count; i++)
+ {
+ if (aIsHorizontal)
+ child = GetCellAt(i,aIndex);
+ else
+ child = GetCellAt(aIndex,i);
+
+ // ignore collapsed children
+ if (!child->IsXULCollapsed())
+ {
+ nsSize childSize = child->GetXULPrefSize(aState);
+
+ nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
+ }
+ }
+
+ row->mPref = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
+
+ return row->mPref;
+}
+
+nscoord
+nsGrid::GetMinRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
+{
+ RebuildIfNeeded();
+
+ nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
+
+ if (row->IsXULCollapsed())
+ return 0;
+
+ if (row->IsMinSet())
+ return row->mMin;
+
+ nsIFrame* box = row->mBox;
+
+ // set in CSS?
+ if (box) {
+ bool widthSet, heightSet;
+ nsSize cssSize(-1, -1);
+ nsIFrame::AddXULMinSize(aState, box, cssSize, widthSet, heightSet);
+
+ row->mMin = GET_HEIGHT(cssSize, aIsHorizontal);
+
+ // yep do nothing.
+ if (row->mMin != -1)
+ return row->mMin;
+ }
+
+ // get the offsets so they are cached.
+ nscoord top;
+ nscoord bottom;
+ GetRowOffsets(aIndex, top, bottom, aIsHorizontal);
+
+ // is the row bogus? If so then just ask it for its size
+ // it should not be affected by cells in the grid.
+ if (row->mIsBogus)
+ {
+ nsSize size(0,0);
+ if (box) {
+ size = box->GetXULPrefSize(aState);
+ nsBox::AddMargin(box, size);
+ nsGridLayout2::AddOffset(box, size);
+ }
+
+ row->mMin = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
+ return row->mMin;
+ }
+
+ nsSize size(0,0);
+
+ nsGridCell* child;
+
+ int32_t count = GetColumnCount(aIsHorizontal);
+
+ for (int32_t i=0; i < count; i++)
+ {
+ if (aIsHorizontal)
+ child = GetCellAt(i,aIndex);
+ else
+ child = GetCellAt(aIndex,i);
+
+ // ignore collapsed children
+ if (!child->IsXULCollapsed())
+ {
+ nsSize childSize = child->GetXULMinSize(aState);
+
+ nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
+ }
+ }
+
+ row->mMin = GET_HEIGHT(size, aIsHorizontal);
+
+ return row->mMin;
+}
+
+nscoord
+nsGrid::GetMaxRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
+{
+ RebuildIfNeeded();
+
+ nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
+
+ if (row->IsXULCollapsed())
+ return 0;
+
+ if (row->IsMaxSet())
+ return row->mMax;
+
+ nsIFrame* box = row->mBox;
+
+ // set in CSS?
+ if (box) {
+ bool widthSet, heightSet;
+ nsSize cssSize(-1, -1);
+ nsIFrame::AddXULMaxSize(box, cssSize, widthSet, heightSet);
+
+ row->mMax = GET_HEIGHT(cssSize, aIsHorizontal);
+
+ // yep do nothing.
+ if (row->mMax != -1)
+ return row->mMax;
+ }
+
+ // get the offsets so they are cached.
+ nscoord top;
+ nscoord bottom;
+ GetRowOffsets(aIndex, top, bottom, aIsHorizontal);
+
+ // is the row bogus? If so then just ask it for its size
+ // it should not be affected by cells in the grid.
+ if (row->mIsBogus)
+ {
+ nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
+ if (box) {
+ size = box->GetXULPrefSize(aState);
+ nsBox::AddMargin(box, size);
+ nsGridLayout2::AddOffset(box, size);
+ }
+
+ row->mMax = GET_HEIGHT(size, aIsHorizontal);
+ return row->mMax;
+ }
+
+ nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
+
+ nsGridCell* child;
+
+ int32_t count = GetColumnCount(aIsHorizontal);
+
+ for (int32_t i=0; i < count; i++)
+ {
+ if (aIsHorizontal)
+ child = GetCellAt(i,aIndex);
+ else
+ child = GetCellAt(aIndex,i);
+
+ // ignore collapsed children
+ if (!child->IsXULCollapsed())
+ {
+ nsSize min = child->GetXULMinSize(aState);
+ nsSize childSize = nsBox::BoundsCheckMinMax(min, child->GetXULMaxSize(aState));
+ nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
+ }
+ }
+
+ row->mMax = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
+
+ return row->mMax;
+}
+
+bool
+nsGrid::IsGrid(nsIFrame* aBox)
+{
+ nsIGridPart* part = GetPartFromBox(aBox);
+ if (!part)
+ return false;
+
+ nsGridLayout2* grid = part->CastToGridLayout();
+
+ if (grid)
+ return true;
+
+ return false;
+}
+
+/**
+ * This get the flexibilty of the row at aIndex. It's not trivial. There are a few
+ * things we need to look at. Specifically we need to see if any <rows> or <columns>
+ * tags are around us. Their flexibilty will affect ours.
+ */
+nscoord
+nsGrid::GetRowFlex(int32_t aIndex, bool aIsHorizontal)
+{
+ RebuildIfNeeded();
+
+ nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
+
+ if (row->IsFlexSet())
+ return row->mFlex;
+
+ nsIFrame* box = row->mBox;
+ row->mFlex = 0;
+
+ if (box) {
+
+ // We need our flex but a inflexible row could be around us. If so
+ // neither are we. However if its the row tag just inside the grid it won't
+ // affect us. We need to do this for this case:
+ // <grid>
+ // <rows>
+ // <rows> // this is not flexible. So our children should not be flexible
+ // <row flex="1"/>
+ // <row flex="1"/>
+ // </rows>
+ // <row/>
+ // </rows>
+ // </grid>
+ //
+ // or..
+ //
+ // <grid>
+ // <rows>
+ // <rows> // this is not flexible. So our children should not be flexible
+ // <rows flex="1">
+ // <row flex="1"/>
+ // <row flex="1"/>
+ // </rows>
+ // <row/>
+ // </rows>
+ // </row>
+ // </grid>
+
+
+ // So here is how it looks
+ //
+ // <grid>
+ // <rows> // parentsParent
+ // <rows> // parent
+ // <row flex="1"/>
+ // <row flex="1"/>
+ // </rows>
+ // <row/>
+ // </rows>
+ // </grid>
+
+ // so the answer is simple: 1) Walk our parent chain. 2) If we find
+ // someone who is not flexible and they aren't the rows immediately in
+ // the grid. 3) Then we are not flexible
+
+ box = GetScrollBox(box);
+ nsIFrame* parent = nsBox::GetParentXULBox(box);
+ nsIFrame* parentsParent=nullptr;
+
+ while(parent)
+ {
+ parent = GetScrollBox(parent);
+ parentsParent = nsBox::GetParentXULBox(parent);
+
+ // if our parents parent is not a grid
+ // the get its flex. If its 0 then we are
+ // not flexible.
+ if (parentsParent) {
+ if (!IsGrid(parentsParent)) {
+ nscoord flex = parent->GetXULFlex();
+ nsIFrame::AddXULFlex(parent, flex);
+ if (flex == 0) {
+ row->mFlex = 0;
+ return row->mFlex;
+ }
+ } else
+ break;
+ }
+
+ parent = parentsParent;
+ }
+
+ // get the row flex.
+ row->mFlex = box->GetXULFlex();
+ nsIFrame::AddXULFlex(box, row->mFlex);
+ }
+
+ return row->mFlex;
+}
+
+void
+nsGrid::SetLargestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
+{
+ if (aIsHorizontal) {
+ if (aSize.height < aHeight)
+ aSize.height = aHeight;
+ } else {
+ if (aSize.width < aHeight)
+ aSize.width = aHeight;
+ }
+}
+
+void
+nsGrid::SetSmallestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
+{
+ if (aIsHorizontal) {
+ if (aSize.height > aHeight)
+ aSize.height = aHeight;
+ } else {
+ if (aSize.width < aHeight)
+ aSize.width = aHeight;
+ }
+}
+
+int32_t
+nsGrid::GetRowCount(int32_t aIsHorizontal)
+{
+ RebuildIfNeeded();
+
+ if (aIsHorizontal)
+ return mRowCount;
+ else
+ return mColumnCount;
+}
+
+int32_t
+nsGrid::GetColumnCount(int32_t aIsHorizontal)
+{
+ return GetRowCount(!aIsHorizontal);
+}
+
+/*
+ * A cell in the given row or columns at the given index has had a child added or removed
+ */
+void
+nsGrid::CellAddedOrRemoved(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
+{
+ // TBD see if the cell will fit in our current row. If it will
+ // just add it in.
+ // but for now rebuild everything.
+ if (mMarkingDirty)
+ return;
+
+ NeedsRebuild(aState);
+}
+
+/**
+ * A row or columns at the given index had been added or removed
+ */
+void
+nsGrid::RowAddedOrRemoved(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
+{
+ // TBD see if we have extra room in the table and just add the new row in
+ // for now rebuild the world
+ if (mMarkingDirty)
+ return;
+
+ NeedsRebuild(aState);
+}
+
+/*
+ * Scrollframes are tranparent. If this is given a scrollframe is will return the
+ * frame inside. If there is no scrollframe it does nothing.
+ */
+nsIFrame*
+nsGrid::GetScrolledBox(nsIFrame* aChild)
+{
+ // first see if it is a scrollframe. If so walk down into it and get the scrolled child
+ nsIScrollableFrame *scrollFrame = do_QueryFrame(aChild);
+ if (scrollFrame) {
+ nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
+ NS_ASSERTION(scrolledFrame,"Error no scroll frame!!");
+ return scrolledFrame;
+ }
+
+ return aChild;
+}
+
+/*
+ * Scrollframes are tranparent. If this is given a child in a scrollframe is will return the
+ * scrollframe ourside it. If there is no scrollframe it does nothing.
+ */
+nsIFrame*
+nsGrid::GetScrollBox(nsIFrame* aChild)
+{
+ if (!aChild)
+ return nullptr;
+
+ // get parent
+ nsIFrame* parent = nsBox::GetParentXULBox(aChild);
+
+ // walk up until we find a scrollframe or a part
+ // if it's a scrollframe return it.
+ // if it's a parent then the child passed does not
+ // have a scroll frame immediately wrapped around it.
+ while (parent) {
+ nsIScrollableFrame *scrollFrame = do_QueryFrame(parent);
+ // scrollframe? Yep return it.
+ if (scrollFrame)
+ return parent;
+
+ nsCOMPtr<nsIGridPart> parentGridRow = GetPartFromBox(parent);
+ // if a part then just return the child
+ if (parentGridRow)
+ break;
+
+ parent = nsBox::GetParentXULBox(parent);
+ }
+
+ return aChild;
+}
+
+
+
+#ifdef DEBUG_grid
+void
+nsGrid::PrintCellMap()
+{
+
+ printf("-----Columns------\n");
+ for (int x=0; x < mColumnCount; x++)
+ {
+
+ nsGridRow* column = GetColumnAt(x);
+ printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
+ }
+
+ printf("\n-----Rows------\n");
+ for (x=0; x < mRowCount; x++)
+ {
+ nsGridRow* column = GetRowAt(x);
+ printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
+ }
+
+ printf("\n");
+
+}
+#endif