1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Communicator client code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : //
39 : // Eric Vaughan
40 : // Netscape Communications
41 : //
42 : // See documentation in associated header file
43 : //
44 :
45 : #include "nsGridRowLeafLayout.h"
46 : #include "nsGridRowGroupLayout.h"
47 : #include "nsGridRow.h"
48 : #include "nsBoxLayoutState.h"
49 : #include "nsBox.h"
50 : #include "nsIScrollableFrame.h"
51 : #include "nsBoxFrame.h"
52 : #include "nsGridLayout2.h"
53 :
54 0 : already_AddRefed<nsBoxLayout> NS_NewGridRowLeafLayout()
55 : {
56 0 : nsBoxLayout* layout = new nsGridRowLeafLayout();
57 0 : NS_IF_ADDREF(layout);
58 0 : return layout;
59 : }
60 :
61 0 : nsGridRowLeafLayout::nsGridRowLeafLayout():nsGridRowLayout()
62 : {
63 0 : }
64 :
65 0 : nsGridRowLeafLayout::~nsGridRowLeafLayout()
66 : {
67 0 : }
68 :
69 : nsSize
70 0 : nsGridRowLeafLayout::GetPrefSize(nsIBox* aBox, nsBoxLayoutState& aState)
71 : {
72 0 : PRInt32 index = 0;
73 0 : nsGrid* grid = GetGrid(aBox, &index);
74 0 : bool isHorizontal = IsHorizontal(aBox);
75 :
76 : // If we are not in a grid. Then we just work like a box. But if we are in a grid
77 : // ask the grid for our size.
78 0 : if (!grid) {
79 0 : return nsGridRowLayout::GetPrefSize(aBox, aState);
80 : }
81 : else {
82 0 : return grid->GetPrefRowSize(aState, index, isHorizontal);
83 : //AddBorderAndPadding(aBox, pref);
84 : }
85 : }
86 :
87 : nsSize
88 0 : nsGridRowLeafLayout::GetMinSize(nsIBox* aBox, nsBoxLayoutState& aState)
89 : {
90 0 : PRInt32 index = 0;
91 0 : nsGrid* grid = GetGrid(aBox, &index);
92 0 : bool isHorizontal = IsHorizontal(aBox);
93 :
94 0 : if (!grid)
95 0 : return nsGridRowLayout::GetMinSize(aBox, aState);
96 : else {
97 0 : nsSize minSize = grid->GetMinRowSize(aState, index, isHorizontal);
98 0 : AddBorderAndPadding(aBox, minSize);
99 0 : return minSize;
100 : }
101 : }
102 :
103 : nsSize
104 0 : nsGridRowLeafLayout::GetMaxSize(nsIBox* aBox, nsBoxLayoutState& aState)
105 : {
106 0 : PRInt32 index = 0;
107 0 : nsGrid* grid = GetGrid(aBox, &index);
108 0 : bool isHorizontal = IsHorizontal(aBox);
109 :
110 0 : if (!grid)
111 0 : return nsGridRowLayout::GetMaxSize(aBox, aState);
112 : else {
113 0 : nsSize maxSize;
114 0 : maxSize = grid->GetMaxRowSize(aState, index, isHorizontal);
115 0 : AddBorderAndPadding(aBox, maxSize);
116 0 : return maxSize;
117 : }
118 : }
119 :
120 : /** If a child is added or removed or changes size
121 : */
122 : void
123 0 : nsGridRowLeafLayout::ChildAddedOrRemoved(nsIBox* aBox, nsBoxLayoutState& aState)
124 : {
125 0 : PRInt32 index = 0;
126 0 : nsGrid* grid = GetGrid(aBox, &index);
127 0 : bool isHorizontal = IsHorizontal(aBox);
128 :
129 0 : if (grid)
130 0 : grid->CellAddedOrRemoved(aState, index, isHorizontal);
131 0 : }
132 :
133 : void
134 0 : nsGridRowLeafLayout::PopulateBoxSizes(nsIBox* aBox, nsBoxLayoutState& aState, nsBoxSize*& aBoxSizes, nscoord& aMinSize, nscoord& aMaxSize, PRInt32& aFlexes)
135 : {
136 0 : PRInt32 index = 0;
137 0 : nsGrid* grid = GetGrid(aBox, &index);
138 0 : bool isHorizontal = IsHorizontal(aBox);
139 :
140 : // Our base class SprocketLayout is giving us a chance to change the box sizes before layout
141 : // If we are a row lets change the sizes to match our columns. If we are a column then do the opposite
142 : // and make them match or rows.
143 0 : if (grid) {
144 : nsGridRow* column;
145 0 : PRInt32 count = grid->GetColumnCount(isHorizontal);
146 0 : nsBoxSize* start = nsnull;
147 0 : nsBoxSize* last = nsnull;
148 0 : nsBoxSize* current = nsnull;
149 0 : nsIBox* child = aBox->GetChildBox();
150 0 : for (int i=0; i < count; i++)
151 : {
152 0 : column = grid->GetColumnAt(i,isHorizontal);
153 :
154 : // make sure the value was computed before we use it.
155 : // !isHorizontal is passed in to invert the behavior of these methods.
156 : nscoord pref =
157 0 : grid->GetPrefRowHeight(aState, i, !isHorizontal); // GetPrefColumnWidth
158 : nscoord min =
159 0 : grid->GetMinRowHeight(aState, i, !isHorizontal); // GetMinColumnWidth
160 : nscoord max =
161 0 : grid->GetMaxRowHeight(aState, i, !isHorizontal); // GetMaxColumnWidth
162 : nscoord flex =
163 0 : grid->GetRowFlex(aState, i, !isHorizontal); // GetColumnFlex
164 0 : nscoord left = 0;
165 0 : nscoord right = 0;
166 0 : grid->GetRowOffsets(aState, i, left, right, !isHorizontal); // GetColumnOffsets
167 0 : nsIBox* box = column->GetBox();
168 0 : bool collapsed = false;
169 0 : nscoord topMargin = column->mTopMargin;
170 0 : nscoord bottomMargin = column->mBottomMargin;
171 :
172 0 : if (box)
173 0 : collapsed = box->IsCollapsed();
174 :
175 0 : pref = pref - (left + right);
176 0 : if (pref < 0)
177 0 : pref = 0;
178 :
179 : // if this is the first or last column. Take into account that
180 : // our row could have a border that could affect our left or right
181 : // padding from our columns. If the row has padding subtract it.
182 : // would should always be able to garentee that our margin is smaller
183 : // or equal to our left or right
184 0 : PRInt32 firstIndex = 0;
185 0 : PRInt32 lastIndex = 0;
186 0 : nsGridRow* firstRow = nsnull;
187 0 : nsGridRow* lastRow = nsnull;
188 0 : grid->GetFirstAndLastRow(aState, firstIndex, lastIndex, firstRow, lastRow, !isHorizontal);
189 :
190 0 : if (i == firstIndex || i == lastIndex) {
191 0 : nsMargin offset = GetTotalMargin(aBox, isHorizontal);
192 :
193 0 : nsMargin border(0,0,0,0);
194 : // can't call GetBorderPadding we will get into recursion
195 0 : aBox->GetBorder(border);
196 0 : offset += border;
197 0 : aBox->GetPadding(border);
198 0 : offset += border;
199 :
200 : // subtract from out left and right
201 0 : if (i == firstIndex)
202 : {
203 0 : if (isHorizontal)
204 0 : left -= offset.left;
205 : else
206 0 : left -= offset.top;
207 : }
208 :
209 0 : if (i == lastIndex)
210 : {
211 0 : if (isHorizontal)
212 0 : right -= offset.right;
213 : else
214 0 : right -= offset.bottom;
215 : }
216 : }
217 :
218 : // initialize the box size here
219 0 : max = NS_MAX(min, max);
220 0 : pref = nsBox::BoundsCheck(min, pref, max);
221 :
222 0 : current = new (aState) nsBoxSize();
223 0 : current->pref = pref;
224 0 : current->min = min;
225 0 : current->max = max;
226 0 : current->flex = flex;
227 0 : current->bogus = column->mIsBogus;
228 0 : current->left = left + topMargin;
229 0 : current->right = right + bottomMargin;
230 0 : current->collapsed = collapsed;
231 :
232 0 : if (!start) {
233 0 : start = current;
234 0 : last = start;
235 : } else {
236 0 : last->next = current;
237 0 : last = current;
238 : }
239 :
240 0 : if (child && !column->mIsBogus)
241 0 : child = child->GetNextBox();
242 :
243 : }
244 0 : aBoxSizes = start;
245 : }
246 :
247 0 : nsSprocketLayout::PopulateBoxSizes(aBox, aState, aBoxSizes, aMinSize, aMaxSize, aFlexes);
248 0 : }
249 :
250 : void
251 0 : nsGridRowLeafLayout::ComputeChildSizes(nsIBox* aBox,
252 : nsBoxLayoutState& aState,
253 : nscoord& aGivenSize,
254 : nsBoxSize* aBoxSizes,
255 : nsComputedBoxSize*& aComputedBoxSizes)
256 : {
257 : // see if we are in a scrollable frame. If we are then there could be scrollbars present
258 : // if so we need to subtract them out to make sure our columns line up.
259 0 : if (aBox) {
260 0 : bool isHorizontal = aBox->IsHorizontal();
261 :
262 : // go up the parent chain looking for scrollframes
263 0 : nscoord diff = 0;
264 : nsIBox* parentBox;
265 0 : (void)GetParentGridPart(aBox, &parentBox);
266 0 : while (parentBox) {
267 0 : nsIBox* scrollbox = nsGrid::GetScrollBox(parentBox);
268 0 : nsIScrollableFrame *scrollable = do_QueryFrame(scrollbox);
269 0 : if (scrollable) {
270 : // Don't call GetActualScrollbarSizes here because it's not safe
271 : // to call that while we're reflowing the contents of the scrollframe,
272 : // which we are here.
273 0 : nsMargin scrollbarSizes = scrollable->GetDesiredScrollbarSizes(&aState);
274 0 : PRUint32 visible = scrollable->GetScrollbarVisibility();
275 :
276 0 : if (isHorizontal && (visible & nsIScrollableFrame::VERTICAL)) {
277 0 : diff += scrollbarSizes.left + scrollbarSizes.right;
278 0 : } else if (!isHorizontal && (visible & nsIScrollableFrame::HORIZONTAL)) {
279 0 : diff += scrollbarSizes.top + scrollbarSizes.bottom;
280 : }
281 : }
282 :
283 0 : (void)GetParentGridPart(parentBox, &parentBox);
284 : }
285 :
286 0 : if (diff > 0) {
287 0 : aGivenSize += diff;
288 :
289 0 : nsSprocketLayout::ComputeChildSizes(aBox, aState, aGivenSize, aBoxSizes, aComputedBoxSizes);
290 :
291 0 : aGivenSize -= diff;
292 :
293 0 : nsComputedBoxSize* s = aComputedBoxSizes;
294 0 : nsComputedBoxSize* last = aComputedBoxSizes;
295 0 : while(s)
296 : {
297 0 : last = s;
298 0 : s = s->next;
299 : }
300 :
301 0 : if (last)
302 0 : last->size -= diff;
303 :
304 0 : return;
305 : }
306 : }
307 :
308 0 : nsSprocketLayout::ComputeChildSizes(aBox, aState, aGivenSize, aBoxSizes, aComputedBoxSizes);
309 :
310 : }
311 :
312 : NS_IMETHODIMP
313 0 : nsGridRowLeafLayout::Layout(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState)
314 : {
315 0 : return nsGridRowLayout::Layout(aBox, aBoxLayoutState);
316 : }
317 :
318 : void
319 0 : nsGridRowLeafLayout::DirtyRows(nsIBox* aBox, nsBoxLayoutState& aState)
320 : {
321 0 : if (aBox) {
322 : // mark us dirty
323 : // XXXldb We probably don't want to walk up the ancestor chain
324 : // calling MarkIntrinsicWidthsDirty for every row.
325 0 : aState.PresShell()->FrameNeedsReflow(aBox, nsIPresShell::eTreeChange,
326 0 : NS_FRAME_IS_DIRTY);
327 : }
328 0 : }
329 :
330 : void
331 0 : nsGridRowLeafLayout::CountRowsColumns(nsIBox* aBox, PRInt32& aRowCount, PRInt32& aComputedColumnCount)
332 : {
333 0 : if (aBox) {
334 0 : nsIBox* child = aBox->GetChildBox();
335 :
336 : // count the children
337 0 : PRInt32 columnCount = 0;
338 0 : while(child) {
339 0 : child = child->GetNextBox();
340 0 : columnCount++;
341 : }
342 :
343 : // if our count is greater than the current column count
344 0 : if (columnCount > aComputedColumnCount)
345 0 : aComputedColumnCount = columnCount;
346 :
347 0 : aRowCount++;
348 : }
349 0 : }
350 :
351 : PRInt32
352 0 : nsGridRowLeafLayout::BuildRows(nsIBox* aBox, nsGridRow* aRows)
353 : {
354 0 : if (aBox) {
355 0 : aRows[0].Init(aBox, false);
356 0 : return 1;
357 : }
358 :
359 0 : return 0;
360 : }
361 :
|