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 : * David W. Hyatt (hyatt@netscape.com) (Original Author)
24 : * Joe Hewitt (hewitt@netscape.com)
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsListBoxLayout.h"
41 :
42 : #include "nsListBoxBodyFrame.h"
43 : #include "nsIFrame.h"
44 : #include "nsBox.h"
45 : #include "nsBoxLayoutState.h"
46 : #include "nsIScrollableFrame.h"
47 : #include "nsIReflowCallback.h"
48 : #include "nsINameSpaceManager.h"
49 : #include "nsGkAtoms.h"
50 : #include "nsContentUtils.h"
51 :
52 0 : nsListBoxLayout::nsListBoxLayout() : nsGridRowGroupLayout()
53 : {
54 0 : }
55 :
56 : ////////// nsBoxLayout //////////////
57 :
58 : nsSize
59 0 : nsListBoxLayout::GetPrefSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState)
60 : {
61 0 : nsSize pref = nsGridRowGroupLayout::GetPrefSize(aBox, aBoxLayoutState);
62 :
63 0 : nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
64 0 : if (frame) {
65 0 : nscoord rowheight = frame->GetRowHeightAppUnits();
66 0 : pref.height = frame->GetRowCount() * rowheight;
67 : // Pad the height.
68 0 : nscoord y = frame->GetAvailableHeight();
69 0 : if (pref.height > y && y > 0 && rowheight > 0) {
70 0 : nscoord m = (pref.height-y)%rowheight;
71 0 : nscoord remainder = m == 0 ? 0 : rowheight - m;
72 0 : pref.height += remainder;
73 : }
74 0 : if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
75 0 : nsGkAtoms::sizemode)) {
76 0 : nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState);
77 0 : if (width > pref.width)
78 0 : pref.width = width;
79 : }
80 : }
81 : return pref;
82 : }
83 :
84 : nsSize
85 0 : nsListBoxLayout::GetMinSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState)
86 : {
87 0 : nsSize minSize = nsGridRowGroupLayout::GetMinSize(aBox, aBoxLayoutState);
88 :
89 0 : nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
90 0 : if (frame) {
91 0 : nscoord rowheight = frame->GetRowHeightAppUnits();
92 0 : minSize.height = frame->GetRowCount() * rowheight;
93 : // Pad the height.
94 0 : nscoord y = frame->GetAvailableHeight();
95 0 : if (minSize.height > y && y > 0 && rowheight > 0) {
96 0 : nscoord m = (minSize.height-y)%rowheight;
97 0 : nscoord remainder = m == 0 ? 0 : rowheight - m;
98 0 : minSize.height += remainder;
99 : }
100 0 : if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
101 0 : nsGkAtoms::sizemode)) {
102 0 : nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState);
103 0 : if (width > minSize.width)
104 0 : minSize.width = width;
105 : }
106 : }
107 : return minSize;
108 : }
109 :
110 : nsSize
111 0 : nsListBoxLayout::GetMaxSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState)
112 : {
113 0 : nsSize maxSize = nsGridRowGroupLayout::GetMaxSize(aBox, aBoxLayoutState);
114 :
115 0 : nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
116 0 : if (frame) {
117 0 : nscoord rowheight = frame->GetRowHeightAppUnits();
118 0 : maxSize.height = frame->GetRowCount() * rowheight;
119 : // Pad the height.
120 0 : nscoord y = frame->GetAvailableHeight();
121 0 : if (maxSize.height > y && y > 0 && rowheight > 0) {
122 0 : nscoord m = (maxSize.height-y)%rowheight;
123 0 : nscoord remainder = m == 0 ? 0 : rowheight - m;
124 0 : maxSize.height += remainder;
125 : }
126 : }
127 : return maxSize;
128 : }
129 :
130 : NS_IMETHODIMP
131 0 : nsListBoxLayout::Layout(nsIBox* aBox, nsBoxLayoutState& aState)
132 : {
133 0 : return LayoutInternal(aBox, aState);
134 : }
135 :
136 :
137 : /////////// nsListBoxLayout /////////////////////////
138 :
139 : /**
140 : * Called to layout our our children. Does no frame construction
141 : */
142 : NS_IMETHODIMP
143 0 : nsListBoxLayout::LayoutInternal(nsIBox* aBox, nsBoxLayoutState& aState)
144 : {
145 0 : PRInt32 redrawStart = -1;
146 :
147 : // Get the start y position.
148 0 : nsListBoxBodyFrame* body = static_cast<nsListBoxBodyFrame*>(aBox);
149 0 : if (!body) {
150 0 : NS_ERROR("Frame encountered that isn't a listboxbody!");
151 0 : return NS_ERROR_FAILURE;
152 : }
153 :
154 0 : nsMargin margin;
155 :
156 : // Get our client rect.
157 0 : nsRect clientRect;
158 0 : aBox->GetClientRect(clientRect);
159 :
160 : // Get the starting y position and the remaining available
161 : // height.
162 0 : nscoord availableHeight = body->GetAvailableHeight();
163 0 : nscoord yOffset = body->GetYPosition();
164 :
165 0 : if (availableHeight <= 0) {
166 0 : bool fixed = (body->GetFixedRowSize() != -1);
167 0 : if (fixed)
168 0 : availableHeight = 10;
169 : else
170 0 : return NS_OK;
171 : }
172 :
173 : // run through all our currently created children
174 0 : nsIBox* box = body->GetChildBox();
175 :
176 : // if the reason is resize or initial we must relayout.
177 0 : nscoord rowHeight = body->GetRowHeightAppUnits();
178 :
179 0 : while (box) {
180 : // If this box is dirty or if it has dirty children, we
181 : // call layout on it.
182 0 : nsRect childRect(box->GetRect());
183 0 : box->GetMargin(margin);
184 :
185 : // relayout if we must or we are dirty or some of our children are dirty
186 : // or the client area is wider than us
187 : // XXXldb There should probably be a resize check here too!
188 0 : if (NS_SUBTREE_DIRTY(box) || childRect.width < clientRect.width) {
189 0 : childRect.x = 0;
190 0 : childRect.y = yOffset;
191 0 : childRect.width = clientRect.width;
192 :
193 0 : nsSize size = box->GetPrefSize(aState);
194 0 : body->SetRowHeight(size.height);
195 :
196 0 : childRect.height = rowHeight;
197 :
198 0 : childRect.Deflate(margin);
199 0 : box->SetBounds(aState, childRect);
200 0 : box->Layout(aState);
201 : } else {
202 : // if the child did not need to be relayed out. Then its easy.
203 : // Place the child by just grabbing its rect and adjusting the y.
204 0 : PRInt32 newPos = yOffset+margin.top;
205 :
206 : // are we pushing down or pulling up any rows?
207 : // Then we may have to redraw everything below the moved
208 : // rows.
209 0 : if (redrawStart == -1 && childRect.y != newPos)
210 0 : redrawStart = newPos;
211 :
212 0 : childRect.y = newPos;
213 0 : box->SetBounds(aState, childRect);
214 : }
215 :
216 : // Ok now the available size gets smaller and we move the
217 : // starting position of the next child down some.
218 0 : nscoord size = childRect.height + margin.top + margin.bottom;
219 :
220 0 : yOffset += size;
221 0 : availableHeight -= size;
222 :
223 0 : box = box->GetNextBox();
224 : }
225 :
226 : // We have enough available height left to add some more rows
227 : // Since we can't do this during layout, we post a callback
228 : // that will be processed after the reflow completes.
229 0 : body->PostReflowCallback();
230 :
231 : // if rows were pushed down or pulled up because some rows were added
232 : // before them then redraw everything under the inserted rows. The inserted
233 : // rows will automatically be redrawn because the were marked dirty on insertion.
234 0 : if (redrawStart > -1) {
235 0 : nsRect bounds(aBox->GetRect());
236 0 : nsRect tempRect(0,redrawStart,bounds.width, bounds.height - redrawStart);
237 0 : aBox->Redraw(aState, &tempRect);
238 : }
239 :
240 0 : return NS_OK;
241 : }
242 :
243 : // Creation Routines ///////////////////////////////////////////////////////////////////////
244 :
245 0 : already_AddRefed<nsBoxLayout> NS_NewListBoxLayout()
246 : {
247 0 : nsBoxLayout* layout = new nsListBoxLayout();
248 0 : NS_IF_ADDREF(layout);
249 0 : return layout;
250 : }
|