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 /*
15  * The nsGridRowGroupLayout implements the <rows> or <columns> tag in a grid.
16  */
17 
18 #include "nsGridRowGroupLayout.h"
19 #include "nsCOMPtr.h"
20 #include "nsIScrollableFrame.h"
21 #include "nsBox.h"
22 #include "nsBoxLayoutState.h"
23 #include "nsGridLayout2.h"
24 #include "nsGridRow.h"
25 #include "mozilla/ReflowInput.h"
26 
NS_NewGridRowGroupLayout()27 already_AddRefed<nsBoxLayout> NS_NewGridRowGroupLayout() {
28   RefPtr<nsBoxLayout> layout = new nsGridRowGroupLayout();
29   return layout.forget();
30 }
31 
nsGridRowGroupLayout()32 nsGridRowGroupLayout::nsGridRowGroupLayout()
33     : nsGridRowLayout(), mRowCount(0) {}
34 
~nsGridRowGroupLayout()35 nsGridRowGroupLayout::~nsGridRowGroupLayout() {}
36 
ChildAddedOrRemoved(nsIFrame * aBox,nsBoxLayoutState & aState)37 void nsGridRowGroupLayout::ChildAddedOrRemoved(nsIFrame* aBox,
38                                                nsBoxLayoutState& aState) {
39   int32_t index = 0;
40   nsGrid* grid = GetGrid(aBox, &index);
41   bool isHorizontal = IsXULHorizontal(aBox);
42 
43   if (grid) grid->RowAddedOrRemoved(aState, index, isHorizontal);
44 }
45 
AddWidth(nsSize & aSize,nscoord aSize2,bool aIsHorizontal)46 void nsGridRowGroupLayout::AddWidth(nsSize& aSize, nscoord aSize2,
47                                     bool aIsHorizontal) {
48   nscoord& size = GET_WIDTH(aSize, aIsHorizontal);
49 
50   if (size == NS_INTRINSICSIZE || aSize2 == NS_INTRINSICSIZE)
51     size = NS_INTRINSICSIZE;
52   else
53     size += aSize2;
54 }
55 
GetXULPrefSize(nsIFrame * aBox,nsBoxLayoutState & aState)56 nsSize nsGridRowGroupLayout::GetXULPrefSize(nsIFrame* aBox,
57                                             nsBoxLayoutState& aState) {
58   nsSize vpref = nsGridRowLayout::GetXULPrefSize(aBox, aState);
59 
60   /* It is possible that we could have some extra columns. This is when less
61    * columns in XUL were defined that needed. And example might be a grid with 3
62    * defined columns but a row with 4 cells in it. We would need an extra column
63    * to make the grid work. But because that extra column does not have a box
64    * associated with it we must add its size in manually. Remember we could have
65    * extra rows as well.
66    */
67 
68   int32_t index = 0;
69   nsGrid* grid = GetGrid(aBox, &index);
70 
71   if (grid) {
72     // make sure we add in extra columns sizes as well
73     bool isHorizontal = IsXULHorizontal(aBox);
74     int32_t extraColumns = grid->GetExtraColumnCount(isHorizontal);
75     int32_t start = grid->GetColumnCount(isHorizontal) -
76                     grid->GetExtraColumnCount(isHorizontal);
77     for (int32_t i = 0; i < extraColumns; i++) {
78       nscoord pref = grid->GetPrefRowHeight(
79           aState, i + start, !isHorizontal);  // GetPrefColumnWidth
80 
81       AddWidth(vpref, pref, isHorizontal);
82     }
83   }
84 
85   return vpref;
86 }
87 
GetXULMaxSize(nsIFrame * aBox,nsBoxLayoutState & aState)88 nsSize nsGridRowGroupLayout::GetXULMaxSize(nsIFrame* aBox,
89                                            nsBoxLayoutState& aState) {
90   nsSize maxSize = nsGridRowLayout::GetXULMaxSize(aBox, aState);
91 
92   int32_t index = 0;
93   nsGrid* grid = GetGrid(aBox, &index);
94 
95   if (grid) {
96     // make sure we add in extra columns sizes as well
97     bool isHorizontal = IsXULHorizontal(aBox);
98     int32_t extraColumns = grid->GetExtraColumnCount(isHorizontal);
99     int32_t start = grid->GetColumnCount(isHorizontal) -
100                     grid->GetExtraColumnCount(isHorizontal);
101     for (int32_t i = 0; i < extraColumns; i++) {
102       nscoord max = grid->GetMaxRowHeight(aState, i + start,
103                                           !isHorizontal);  // GetMaxColumnWidth
104 
105       AddWidth(maxSize, max, isHorizontal);
106     }
107   }
108 
109   return maxSize;
110 }
111 
GetXULMinSize(nsIFrame * aBox,nsBoxLayoutState & aState)112 nsSize nsGridRowGroupLayout::GetXULMinSize(nsIFrame* aBox,
113                                            nsBoxLayoutState& aState) {
114   nsSize minSize = nsGridRowLayout::GetXULMinSize(aBox, aState);
115 
116   int32_t index = 0;
117   nsGrid* grid = GetGrid(aBox, &index);
118 
119   if (grid) {
120     // make sure we add in extra columns sizes as well
121     bool isHorizontal = IsXULHorizontal(aBox);
122     int32_t extraColumns = grid->GetExtraColumnCount(isHorizontal);
123     int32_t start = grid->GetColumnCount(isHorizontal) -
124                     grid->GetExtraColumnCount(isHorizontal);
125     for (int32_t i = 0; i < extraColumns; i++) {
126       nscoord min = grid->GetMinRowHeight(aState, i + start,
127                                           !isHorizontal);  // GetMinColumnWidth
128       AddWidth(minSize, min, isHorizontal);
129     }
130   }
131 
132   return minSize;
133 }
134 
135 /*
136  * Run down through our children dirtying them recursively.
137  */
DirtyRows(nsIFrame * aBox,nsBoxLayoutState & aState)138 void nsGridRowGroupLayout::DirtyRows(nsIFrame* aBox, nsBoxLayoutState& aState) {
139   if (aBox) {
140     // mark us dirty
141     // XXXldb We probably don't want to walk up the ancestor chain
142     // calling MarkIntrinsicISizesDirty for every row group.
143     aState.PresShell()->FrameNeedsReflow(aBox, nsIPresShell::eTreeChange,
144                                          NS_FRAME_IS_DIRTY);
145     nsIFrame* child = nsBox::GetChildXULBox(aBox);
146 
147     while (child) {
148       // walk into scrollframes
149       nsIFrame* deepChild = nsGrid::GetScrolledBox(child);
150 
151       // walk into other monuments
152       nsIGridPart* monument = nsGrid::GetPartFromBox(deepChild);
153       if (monument) monument->DirtyRows(deepChild, aState);
154 
155       child = nsBox::GetNextXULBox(child);
156     }
157   }
158 }
159 
CountRowsColumns(nsIFrame * aBox,int32_t & aRowCount,int32_t & aComputedColumnCount)160 void nsGridRowGroupLayout::CountRowsColumns(nsIFrame* aBox, int32_t& aRowCount,
161                                             int32_t& aComputedColumnCount) {
162   if (aBox) {
163     int32_t startCount = aRowCount;
164 
165     nsIFrame* child = nsBox::GetChildXULBox(aBox);
166 
167     while (child) {
168       // first see if it is a scrollframe. If so walk down into it and get the
169       // scrolled child
170       nsIFrame* deepChild = nsGrid::GetScrolledBox(child);
171 
172       nsIGridPart* monument = nsGrid::GetPartFromBox(deepChild);
173       if (monument) {
174         monument->CountRowsColumns(deepChild, aRowCount, aComputedColumnCount);
175         child = nsBox::GetNextXULBox(child);
176         deepChild = child;
177         continue;
178       }
179 
180       child = nsBox::GetNextXULBox(child);
181 
182       // if not a monument. Then count it. It will be a bogus row
183       aRowCount++;
184     }
185 
186     mRowCount = aRowCount - startCount;
187   }
188 }
189 
190 /**
191  * Fill out the given row structure recursively
192  */
BuildRows(nsIFrame * aBox,nsGridRow * aRows)193 int32_t nsGridRowGroupLayout::BuildRows(nsIFrame* aBox, nsGridRow* aRows) {
194   int32_t rowCount = 0;
195 
196   if (aBox) {
197     nsIFrame* child = nsBox::GetChildXULBox(aBox);
198 
199     while (child) {
200       // first see if it is a scrollframe. If so walk down into it and get the
201       // scrolled child
202       nsIFrame* deepChild = nsGrid::GetScrolledBox(child);
203 
204       nsIGridPart* monument = nsGrid::GetPartFromBox(deepChild);
205       if (monument) {
206         rowCount += monument->BuildRows(deepChild, &aRows[rowCount]);
207         child = nsBox::GetNextXULBox(child);
208         deepChild = child;
209         continue;
210       }
211 
212       aRows[rowCount].Init(child, true);
213 
214       child = nsBox::GetNextXULBox(child);
215 
216       // if not a monument. Then count it. It will be a bogus row
217       rowCount++;
218     }
219   }
220 
221   return rowCount;
222 }
223 
GetTotalMargin(nsIFrame * aBox,bool aIsHorizontal)224 nsMargin nsGridRowGroupLayout::GetTotalMargin(nsIFrame* aBox,
225                                               bool aIsHorizontal) {
226   // group have border and padding added to the total margin
227 
228   nsMargin margin = nsGridRowLayout::GetTotalMargin(aBox, aIsHorizontal);
229 
230   // make sure we have the scrollframe on the outside if it has one.
231   // that's where the border is.
232   aBox = nsGrid::GetScrollBox(aBox);
233 
234   // add our border/padding to it
235   nsMargin borderPadding(0, 0, 0, 0);
236   aBox->GetXULBorderAndPadding(borderPadding);
237   margin += borderPadding;
238 
239   return margin;
240 }
241