1 /* |
|
2 * CDDL HEADER START |
|
3 * |
|
4 * The contents of this file are subject to the terms of the |
|
5 * Common Development and Distribution License (the "License"). |
|
6 * You may not use this file except in compliance with the License. |
|
7 * |
|
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 * or http://www.opensolaris.org/os/licensing. |
|
10 * See the License for the specific language governing permissions |
|
11 * and limitations under the License. |
|
12 * |
|
13 * When distributing Covered Code, include this CDDL HEADER in each |
|
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 * If applicable, add the following below this CDDL HEADER, with the |
|
16 * fields enclosed by brackets "[]" replaced with your own identifying |
|
17 * information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 * |
|
19 * CDDL HEADER END |
|
20 */ |
|
21 |
|
22 /* |
|
23 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. |
|
24 */ |
|
25 |
|
26 package com.oracle.solaris.vp.util.swing.layout; |
|
27 |
|
28 import java.awt.*; |
|
29 import java.util.ArrayList; |
|
30 |
|
31 public abstract class AbstractLayout implements LayoutManager { |
|
32 // |
|
33 // Inner classes |
|
34 // |
|
35 |
|
36 public interface SizedSet { |
|
37 /** |
|
38 * Gets the number of elements in the set. |
|
39 */ |
|
40 int getCount(); |
|
41 |
|
42 /** |
|
43 * Gets the size of the {@code i}th element. |
|
44 */ |
|
45 int getSize(int i); |
|
46 |
|
47 /** |
|
48 * Gets the weight of the {@code i}th element. |
|
49 */ |
|
50 float getWeight(int i); |
|
51 |
|
52 /** |
|
53 * Sets the size of the {@code i}th element. |
|
54 */ |
|
55 void setSize(int i, int size); |
|
56 |
|
57 /** |
|
58 * Sets the weight of the {@code i}th element. |
|
59 */ |
|
60 void setWeight(int i, float weight); |
|
61 } |
|
62 |
|
63 protected static class ArrayBasedSizedSet implements SizedSet { |
|
64 // |
|
65 // Instance data |
|
66 // |
|
67 |
|
68 private int[] sizes; |
|
69 private float[] weights; |
|
70 |
|
71 // |
|
72 // Constructors |
|
73 // |
|
74 |
|
75 public ArrayBasedSizedSet(int[] sizes, float[] weights) { |
|
76 if (weights == null) { |
|
77 weights = new float[sizes.length]; |
|
78 } else |
|
79 |
|
80 // Check for differing array lengths |
|
81 if (sizes.length != weights.length) { |
|
82 throw new IllegalArgumentException(String.format( |
|
83 "sizes (%d) and weights (%d) arrays differ in length", |
|
84 sizes.length, weights.length)); |
|
85 } |
|
86 |
|
87 this.sizes = sizes; |
|
88 this.weights = weights; |
|
89 } |
|
90 |
|
91 // |
|
92 // SizedSet methods |
|
93 // |
|
94 |
|
95 @Override |
|
96 public int getCount() { |
|
97 return sizes.length; |
|
98 } |
|
99 |
|
100 @Override |
|
101 public int getSize(int i) { |
|
102 return sizes[i]; |
|
103 } |
|
104 |
|
105 @Override |
|
106 public float getWeight(int i) { |
|
107 return weights[i]; |
|
108 } |
|
109 |
|
110 @Override |
|
111 public void setSize(int i, int size) { |
|
112 sizes[i] = size; |
|
113 } |
|
114 |
|
115 @Override |
|
116 public void setWeight(int i, float weight) { |
|
117 weights[i] = weight; |
|
118 } |
|
119 } |
|
120 |
|
121 // |
|
122 // LayoutManager methods |
|
123 // |
|
124 |
|
125 @Override |
|
126 public void addLayoutComponent(String name, Component comp) { |
|
127 } |
|
128 |
|
129 @Override |
|
130 public Dimension minimumLayoutSize(Container container) { |
|
131 return getLayoutSize(container, false); |
|
132 } |
|
133 |
|
134 @Override |
|
135 public Dimension preferredLayoutSize(Container container) { |
|
136 return getLayoutSize(container, true); |
|
137 } |
|
138 |
|
139 @Override |
|
140 public void removeLayoutComponent(Component comp) { |
|
141 } |
|
142 |
|
143 // |
|
144 // AbstractLayout methods |
|
145 // |
|
146 |
|
147 /** |
|
148 * Distribute the given extra space among the given sizes, subject to the |
|
149 * given weights. |
|
150 * |
|
151 * @param space |
|
152 * the excess space (may be negative) to distribute |
|
153 * |
|
154 * @param set |
|
155 * encapsulates the sizes/weights of the elements to distribute |
|
156 * {@code space} to |
|
157 * |
|
158 * @return the amount of space that could not be distributed |
|
159 */ |
|
160 protected int distributeSpace(int space, SizedSet set) { |
|
161 int length = set.getCount(); |
|
162 |
|
163 // Check for negative weights |
|
164 for (int i = 0; i < length; i++) { |
|
165 float weight = set.getWeight(i); |
|
166 if (weight < 0) { |
|
167 throw new IllegalArgumentException( |
|
168 "negative weight: " + weight); |
|
169 } |
|
170 } |
|
171 |
|
172 // Check for negative sizes |
|
173 for (int i = 0; i < length; i++) { |
|
174 int size = set.getSize(i); |
|
175 if (size < 0) { |
|
176 space -= size; |
|
177 set.setSize(i, 0); |
|
178 } |
|
179 } |
|
180 |
|
181 if (space == 0) { |
|
182 return space; |
|
183 } |
|
184 |
|
185 // If weights are all zeros, weight each size equally |
|
186 for (int i = 0; i < length; i++) { |
|
187 if (set.getWeight(i) != 0) { |
|
188 break; |
|
189 } |
|
190 |
|
191 if (i == length - 1) { |
|
192 for (int j = 0; j < length; j++) { |
|
193 set.setWeight(j, 1); |
|
194 } |
|
195 } |
|
196 } |
|
197 |
|
198 // Verify that no non-zero weight is associated with a zero size |
|
199 boolean zeroedWeight = false; |
|
200 for (int i = 0; i < length; i++) { |
|
201 if (set.getWeight(i) != 0 && set.getSize(i) == 0) { |
|
202 if (space <= 0) { |
|
203 // Zero out weights corresponding to zero sizes (resulting |
|
204 // sizes should not be negative) |
|
205 set.setWeight(i, 0); |
|
206 zeroedWeight = true; |
|
207 } else { |
|
208 set.setSize(i, 1); |
|
209 space--; |
|
210 } |
|
211 } |
|
212 } |
|
213 |
|
214 // If we zeroed out a weight... |
|
215 if (zeroedWeight) { |
|
216 for (int i = 0; i < length; i++) { |
|
217 if (set.getWeight(i) != 0) { |
|
218 break; |
|
219 } |
|
220 |
|
221 // If all weights are now zeros |
|
222 if (i == length - 1) { |
|
223 // Weight each non-zero size equally |
|
224 for (int k = 0; k < length; k++) { |
|
225 if (set.getSize(k) != 0) { |
|
226 set.setWeight(k, 1); |
|
227 } |
|
228 } |
|
229 } |
|
230 } |
|
231 } |
|
232 |
|
233 float sum = 0; |
|
234 for (int i = 0; i < length; i++) { |
|
235 float weight = set.getWeight(i); |
|
236 weight *= (float)set.getSize(i); |
|
237 set.setWeight(i, weight); |
|
238 sum += weight; |
|
239 } |
|
240 |
|
241 int notDistributed = 0; |
|
242 |
|
243 // Avoid divide-by-zero |
|
244 if (sum != 0) { |
|
245 float remainder = 0; |
|
246 int total = 0; |
|
247 |
|
248 for (int i = 0; i < length; i++) { |
|
249 float weight = set.getWeight(i); |
|
250 weight /= sum; |
|
251 set.setWeight(i, weight); |
|
252 |
|
253 float delta = weight * (float)space; |
|
254 int trunc = (int)delta; |
|
255 |
|
256 // Keep track of truncated portion |
|
257 remainder += delta - trunc; |
|
258 |
|
259 int round = Math.round(remainder); |
|
260 if (round != 0) { |
|
261 trunc += round; |
|
262 remainder -= round; |
|
263 } |
|
264 |
|
265 total += trunc; |
|
266 |
|
267 int newSize = set.getSize(i) + trunc; |
|
268 if (newSize < 0) { |
|
269 notDistributed += newSize; |
|
270 newSize = 0; |
|
271 } |
|
272 |
|
273 set.setSize(i, newSize); |
|
274 } |
|
275 } |
|
276 |
|
277 if (notDistributed != 0) { |
|
278 for (int i = 0; i < length; i++) { |
|
279 if (set.getSize(i) != 0) { |
|
280 notDistributed = distributeSpace(notDistributed, set); |
|
281 break; |
|
282 } |
|
283 } |
|
284 } |
|
285 |
|
286 return notDistributed; |
|
287 } |
|
288 |
|
289 /** |
|
290 * Distribute the given extra space among the given sizes, subject to the |
|
291 * given weights. |
|
292 * |
|
293 * @param space |
|
294 * the excess space (may be negative) to distribute |
|
295 * |
|
296 * @param sizes |
|
297 * the sizes to modify -- if any are negative, they will be |
|
298 * initially set to zero and the excess will be added to the |
|
299 * {@code space} |
|
300 * |
|
301 * @param weights |
|
302 * the weighting of each size, or {@code null} to apply |
|
303 * weightings equally across all sizes |
|
304 * |
|
305 * @exception IllegalArgumentException |
|
306 * if the lengths of the two arrays are not equal, or a weight |
|
307 * is negative |
|
308 * |
|
309 * @return the amount of space that could not be distributed |
|
310 */ |
|
311 protected int distributeSpace(int space, int[] sizes, float[] weights) { |
|
312 SizedSet set = new ArrayBasedSizedSet(sizes, weights); |
|
313 return distributeSpace(space, set); |
|
314 } |
|
315 |
|
316 public Component[] getLayoutComponents(Component[] comps) { |
|
317 // Weed out Components that don't need to be layed out |
|
318 ArrayList<Component> list = new ArrayList<Component>(); |
|
319 for (Component comp : comps) { |
|
320 if (needsLayout(comp)) { |
|
321 list.add(comp); |
|
322 } |
|
323 } |
|
324 return list.toArray(new Component[list.size()]); |
|
325 } |
|
326 |
|
327 /** |
|
328 * Convenience method called by {@link |
|
329 * #minimumLayoutSize(java.awt.Container)} and {@link |
|
330 * #preferredLayoutSize(java.awt.Container)}. This default implementation |
|
331 * returns {@code null}, in case subclasses choose to override those methods |
|
332 * directly. |
|
333 */ |
|
334 protected Dimension getLayoutSize(Container container, boolean preferred) { |
|
335 return null; |
|
336 } |
|
337 |
|
338 /** |
|
339 * Called by {@code #getLayoutComponents(Component[])}, determines whether |
|
340 * the given {@code Component} should be layed out. This default |
|
341 * implmentation returns {@code true} iff the {@code Component} is |
|
342 * non-{@code null}. |
|
343 */ |
|
344 protected boolean needsLayout(Component c) { |
|
345 return c != null; |
|
346 } |
|
347 } |
|