Subversion Repository Public Repository

litesoft

Diff Revisions 282 vs 475 for /trunk/Java/GWT/Client/src/com/google/gwt/gen2/table/client/ColumnResizer.java

Diff revisions: vs.
  @@ -15,16 +15,13 @@
15 15 */
16 16 package com.google.gwt.gen2.table.client;
17 17
18 - import java.util.ArrayList;
19 - import java.util.Collections;
20 - import java.util.Comparator;
21 - import java.util.List;
18 + import java.util.*;
22 19
23 20 /**
24 21 * <p>
25 22 * A helper class that distributes available width across a set of columns.
26 23 * </p>
27 - *
24 + * <p/>
28 25 * <h3>The following algorithm is used to distribute the available width:</h3>
29 26 * <ol>
30 27 * <li>Calculate the percent difference between the current and preferred width
  @@ -39,311 +36,379 @@
39 36 * all at their boundaries), return the undistributed width.</li>
40 37 * </ol>
41 38 */
42 - class ColumnResizer {
43 - /**
44 - * The resolution of the width of columns. The true target width of a column
45 - * is usually a decimal, but column widths can only be represented as integers
46 - * (ie. pixels). The resolution determines the maximum number of pixels that
47 - * the calculations can be off by.
48 - *
49 - * Increasing the resolution will increase the speed of the algorithm and
50 - * reduce the accuracy of the calculations. The resolution must be at least 1
51 - * or errors will occur (because we cannot get perfect "0" accuracy).
52 - */
53 - private static final int RESOLUTION = 1;
54 -
55 - /**
56 - * A class that contains the current and desired width of a column.
57 - */
58 - static class ColumnWidthInfo {
59 - private int minWidth;
60 - private int maxWidth;
61 - private int preferredWidth;
62 - private int curWidth;
63 -
39 + class ColumnResizer
40 + {
64 41 /**
65 - * The new column width.
42 + * The resolution of the width of columns. The true target width of a column
43 + * is usually a decimal, but column widths can only be represented as integers
44 + * (ie. pixels). The resolution determines the maximum number of pixels that
45 + * the calculations can be off by.
46 + * <p/>
47 + * Increasing the resolution will increase the speed of the algorithm and
48 + * reduce the accuracy of the calculations. The resolution must be at least 1
49 + * or errors will occur (because we cannot get perfect "0" accuracy).
66 50 */
67 - private int newWidth = 0;
51 + private static final int RESOLUTION = 1;
68 52
69 53 /**
70 - * The required width to achieve the next level.
54 + * A class that contains the current and desired width of a column.
71 55 */
72 - private int requiredWidth;
56 + static class ColumnWidthInfo
57 + {
58 + private int minWidth;
59 + private int maxWidth;
60 + private int preferredWidth;
61 + private int curWidth;
62 +
63 + /**
64 + * The new column width.
65 + */
66 + private int newWidth = 0;
67 +
68 + /**
69 + * The required width to achieve the next level.
70 + */
71 + private int requiredWidth;
72 +
73 + /**
74 + * Construct a new {@link ColumnWidthInfo}.
75 + *
76 + * @param minWidth the minimum width of the column
77 + * @param maxWidth the maximum width of the column
78 + * @param preferredWidth the preferred width of the column
79 + * @param curWidth the current width of the column
80 + */
81 + public ColumnWidthInfo( int minWidth, int maxWidth, int preferredWidth, int curWidth )
82 + {
83 + this.minWidth = minWidth;
84 + this.maxWidth = maxWidth;
85 + this.preferredWidth = preferredWidth;
86 + this.curWidth = curWidth;
87 + }
73 88
74 - /**
75 - * Construct a new {@link ColumnWidthInfo}.
76 - *
77 - * @param minWidth the minimum width of the column
78 - * @param maxWidth the maximum width of the column
79 - * @param preferredWidth the preferred width of the column
80 - * @param curWidth the current width of the column
81 - */
82 - public ColumnWidthInfo(int minWidth, int maxWidth, int preferredWidth,
83 - int curWidth) {
84 - this.minWidth = minWidth;
85 - this.maxWidth = maxWidth;
86 - this.preferredWidth = preferredWidth;
87 - this.curWidth = curWidth;
88 - }
89 + public int getCurrentWidth()
90 + {
91 + return curWidth;
92 + }
89 93
90 - public int getCurrentWidth() {
91 - return curWidth;
92 - }
94 + public int getMaximumWidth()
95 + {
96 + // For calculation purposes, ensure maxWidth >= minWidth
97 + if ( hasMaximumWidth() )
98 + {
99 + return Math.max( maxWidth, minWidth );
100 + }
101 + return maxWidth;
102 + }
93 103
94 - public int getMaximumWidth() {
95 - // For calculation purposes, ensure maxWidth >= minWidth
96 - if (hasMaximumWidth()) {
97 - return Math.max(maxWidth, minWidth);
98 - }
99 - return maxWidth;
100 - }
104 + public int getMinimumWidth()
105 + {
106 + return minWidth;
107 + }
101 108
102 - public int getMinimumWidth() {
103 - return minWidth;
104 - }
109 + public int getNewWidth()
110 + {
111 + return newWidth;
112 + }
105 113
106 - public int getNewWidth() {
107 - return newWidth;
108 - }
114 + public int getPreferredWidth()
115 + {
116 + return preferredWidth;
117 + }
109 118
110 - public int getPreferredWidth() {
111 - return preferredWidth;
112 - }
119 + public boolean hasMaximumWidth()
120 + {
121 + return maxWidth >= 0;
122 + }
113 123
114 - public boolean hasMaximumWidth() {
115 - return maxWidth >= 0;
116 - }
124 + public boolean hasMinimumWidth()
125 + {
126 + return minWidth >= 0;
127 + }
117 128
118 - public boolean hasMinimumWidth() {
119 - return minWidth >= 0;
120 - }
129 + public void setCurrentWidth( int curWidth )
130 + {
131 + this.curWidth = curWidth;
132 + }
121 133
122 - public void setCurrentWidth(int curWidth) {
123 - this.curWidth = curWidth;
124 - }
134 + public void setMaximumWidth( int maxWidth )
135 + {
136 + this.maxWidth = maxWidth;
137 + }
125 138
126 - public void setMaximumWidth(int maxWidth) {
127 - this.maxWidth = maxWidth;
128 - }
139 + public void setMinimumWidth( int minWidth )
140 + {
141 + this.minWidth = minWidth;
142 + }
129 143
130 - public void setMinimumWidth(int minWidth) {
131 - this.minWidth = minWidth;
132 - }
144 + public void setPreferredWidth( int preferredWidth )
145 + {
146 + this.preferredWidth = preferredWidth;
147 + }
133 148
134 - public void setPreferredWidth(int preferredWidth) {
135 - this.preferredWidth = preferredWidth;
149 + /**
150 + * Get the percentage difference between the current column width and the
151 + * preferred column width. A negative value indicates that the current width
152 + * is less than the preferred width, while a positive value indicates that
153 + * the current width is above the preferred width.
154 + *
155 + * @return the percentage difference
156 + */
157 + double getPercentageDifference()
158 + {
159 + return (newWidth - preferredWidth) / (double) preferredWidth;
160 + }
161 +
162 + int getRequiredWidth()
163 + {
164 + return requiredWidth;
165 + }
166 +
167 + void setNewWidth( int newWidth )
168 + {
169 + this.newWidth = newWidth;
170 + }
171 +
172 + void setRequiredWidth( int requiredWidth )
173 + {
174 + this.requiredWidth = requiredWidth;
175 + }
136 176 }
137 177
138 178 /**
139 - * Get the percentage difference between the current column width and the
140 - * preferred column width. A negative value indicates that the current width
141 - * is less than the preferred width, while a positive value indicates that
142 - * the current width is above the preferred width.
143 - *
144 - * @return the percentage difference
179 + * Distribute some width across a list of columns, respecting the minimum and
180 + * maximum widths of the columns. The return value is the remaining width that
181 + * could not be distributed due to constraints. If the return value is 0, all
182 + * of the width has been distributed.
183 + *
184 + * @param columns the list of column width info
185 + * @param width the width to distribute
186 + *
187 + * @return the width that could not be distributed
145 188 */
146 - double getPercentageDifference() {
147 - return (newWidth - preferredWidth) / (double) preferredWidth;
148 - }
149 -
150 - int getRequiredWidth() {
151 - return requiredWidth;
152 - }
189 + public int distributeWidth( List<ColumnWidthInfo> columns, int width )
190 + {
191 + // The new width defaults to the current width, within min/max range
192 + for ( ColumnWidthInfo info : columns )
193 + {
194 + int curWidth = info.getCurrentWidth();
195 + if ( info.hasMinimumWidth() && curWidth < info.getMinimumWidth() )
196 + {
197 + curWidth = info.getMinimumWidth();
198 + }
199 + else if ( info.hasMaximumWidth() && curWidth > info.getMaximumWidth() )
200 + {
201 + curWidth = info.getMaximumWidth();
202 + }
203 + width -= (curWidth - info.getCurrentWidth());
204 + info.setNewWidth( curWidth );
205 + }
153 206
154 - void setNewWidth(int newWidth) {
155 - this.newWidth = newWidth;
156 - }
207 + // Do not modify widths if there is nothing to distribute
208 + if ( width == 0 )
209 + {
210 + return 0;
211 + }
157 212
158 - void setRequiredWidth(int requiredWidth) {
159 - this.requiredWidth = requiredWidth;
160 - }
161 - }
213 + // Copy the list of columns
214 + List<ColumnWidthInfo> orderedColumns = new ArrayList<ColumnWidthInfo>( columns );
162 215
163 - /**
164 - * Distribute some width across a list of columns, respecting the minimum and
165 - * maximum widths of the columns. The return value is the remaining width that
166 - * could not be distributed due to constraints. If the return value is 0, all
167 - * of the width has been distributed.
168 - *
169 - * @param columns the list of column width info
170 - * @param width the width to distribute
171 - * @return the width that could not be distributed
172 - */
173 - public int distributeWidth(List<ColumnWidthInfo> columns, int width) {
174 - // The new width defaults to the current width, within min/max range
175 - for (ColumnWidthInfo info : columns) {
176 - int curWidth = info.getCurrentWidth();
177 - if (info.hasMinimumWidth() && curWidth < info.getMinimumWidth()) {
178 - curWidth = info.getMinimumWidth();
179 - } else if (info.hasMaximumWidth() && curWidth > info.getMaximumWidth()) {
180 - curWidth = info.getMaximumWidth();
181 - }
182 - width -= (curWidth - info.getCurrentWidth());
183 - info.setNewWidth(curWidth);
184 - }
216 + // Sort the list of columns
217 + if ( width > 0 )
218 + {
219 + // Enlarge columns
220 + Comparator<ColumnWidthInfo> comparator = new Comparator<ColumnWidthInfo>()
221 + {
222 + public int compare( ColumnWidthInfo o1, ColumnWidthInfo o2 )
223 + {
224 + double diff1 = o1.getPercentageDifference();
225 + double diff2 = o2.getPercentageDifference();
226 + if ( diff1 < diff2 )
227 + {
228 + return -1;
229 + }
230 + else if ( diff1 == diff2 )
231 + {
232 + return 0;
233 + }
234 + else
235 + {
236 + return 1;
237 + }
238 + }
239 + };
240 + Collections.sort( orderedColumns, comparator );
241 + }
242 + else if ( width < 0 )
243 + {
244 + // Shrink columns
245 + Comparator<ColumnWidthInfo> comparator = new Comparator<ColumnWidthInfo>()
246 + {
247 + public int compare( ColumnWidthInfo o1, ColumnWidthInfo o2 )
248 + {
249 + double diff1 = o1.getPercentageDifference();
250 + double diff2 = o2.getPercentageDifference();
251 + if ( diff1 > diff2 )
252 + {
253 + return -1;
254 + }
255 + else if ( diff1 == diff2 )
256 + {
257 + return 0;
258 + }
259 + else
260 + {
261 + return 1;
262 + }
263 + }
264 + };
265 + Collections.sort( orderedColumns, comparator );
266 + }
185 267
186 - // Do not modify widths if there is nothing to distribute
187 - if (width == 0) {
188 - return 0;
268 + // Distribute the width
269 + return distributeWidthImpl( orderedColumns, width );
189 270 }
190 271
191 - // Copy the list of columns
192 - List<ColumnWidthInfo> orderedColumns = new ArrayList<ColumnWidthInfo>(
193 - columns);
194 -
195 - // Sort the list of columns
196 - if (width > 0) {
197 - // Enlarge columns
198 - Comparator<ColumnWidthInfo> comparator = new Comparator<ColumnWidthInfo>() {
199 - public int compare(ColumnWidthInfo o1, ColumnWidthInfo o2) {
200 - double diff1 = o1.getPercentageDifference();
201 - double diff2 = o2.getPercentageDifference();
202 - if (diff1 < diff2) {
203 - return -1;
204 - } else if (diff1 == diff2) {
205 - return 0;
206 - } else {
207 - return 1;
208 - }
209 - }
210 - };
211 - Collections.sort(orderedColumns, comparator);
212 - } else if (width < 0) {
213 - // Shrink columns
214 - Comparator<ColumnWidthInfo> comparator = new Comparator<ColumnWidthInfo>() {
215 - public int compare(ColumnWidthInfo o1, ColumnWidthInfo o2) {
216 - double diff1 = o1.getPercentageDifference();
217 - double diff2 = o2.getPercentageDifference();
218 - if (diff1 > diff2) {
219 - return -1;
220 - } else if (diff1 == diff2) {
221 - return 0;
222 - } else {
223 - return 1;
224 - }
272 + private int distributeWidthImpl( List<ColumnWidthInfo> columns, int width )
273 + {
274 + // Iterate until width can not longer be distributed
275 + boolean growing = (width > 0);
276 + boolean fullySynced = false;
277 + int syncedColumns = 1;
278 + while ( columns.size() > 0 && width != 0 )
279 + {
280 + // Calculate the target difference at the next level
281 + double targetDiff = getTargetDiff( columns, syncedColumns, width );
282 +
283 + // Calculate the total required width to achieve the target difference
284 + int totalRequired = 0;
285 + for ( int curIndex = 0; curIndex < syncedColumns; curIndex++ )
286 + {
287 + // Calculate the new width at the target diff
288 + ColumnWidthInfo curInfo = columns.get( curIndex );
289 + int preferredWidth = curInfo.getPreferredWidth();
290 + int newWidth = (int) (targetDiff * preferredWidth) + preferredWidth;
291 +
292 + // Compare the boundaries
293 + if ( growing )
294 + {
295 + newWidth = Math.max( newWidth, curInfo.getCurrentWidth() );
296 + if ( curInfo.hasMaximumWidth() )
297 + {
298 + newWidth = Math.min( newWidth, curInfo.getMaximumWidth() );
299 + }
300 + }
301 + else
302 + {
303 + newWidth = Math.min( newWidth, curInfo.getCurrentWidth() );
304 + if ( curInfo.hasMinimumWidth() )
305 + {
306 + newWidth = Math.max( newWidth, curInfo.getMinimumWidth() );
307 + }
308 + }
309 +
310 + // Calculate the width required to achieve the new width
311 + curInfo.setRequiredWidth( newWidth - curInfo.getNewWidth() );
312 + totalRequired += curInfo.getRequiredWidth();
313 + }
314 +
315 + // Calculate the percent of the required width that is available
316 + double percentAvailable = 1.0;
317 + if ( totalRequired != 0 )
318 + {
319 + percentAvailable = Math.min( 1.0, width / (double) totalRequired );
320 + }
321 + for ( int curIndex = 0; curIndex < syncedColumns; curIndex++ )
322 + {
323 + // Determine the true width to add to the column
324 + ColumnWidthInfo curInfo = columns.get( curIndex );
325 + int required = (int) (percentAvailable * curInfo.getRequiredWidth());
326 +
327 + // Make sure we get out of the loop by distributing at least 1
328 + if ( fullySynced )
329 + {
330 + if ( growing )
331 + {
332 + required = Math.max( RESOLUTION, required );
333 + }
334 + else
335 + {
336 + required = Math.min( -RESOLUTION, required );
337 + }
338 + }
339 +
340 + // Don't distribute more than the available width
341 + if ( growing && required > width )
342 + {
343 + required = width;
344 + }
345 + else if ( !growing && required < width )
346 + {
347 + required = width;
348 + }
349 +
350 + // Set the new width of the column
351 + curInfo.setNewWidth( curInfo.getNewWidth() + required );
352 + width -= required;
353 +
354 + // Remove the column if it has reached its maximum/minimum width
355 + boolean maxedOut = false;
356 + if ( growing && curInfo.hasMaximumWidth() )
357 + {
358 + maxedOut = (curInfo.getNewWidth() >= curInfo.getMaximumWidth());
359 + }
360 + else if ( !growing && curInfo.hasMinimumWidth() )
361 + {
362 + maxedOut = (curInfo.getNewWidth() <= curInfo.getMinimumWidth());
363 + }
364 + if ( maxedOut )
365 + {
366 + columns.remove( curIndex );
367 + curIndex--;
368 + syncedColumns--;
369 + }
370 + }
371 +
372 + // Increment the number of synced column
373 + if ( !fullySynced && syncedColumns < columns.size() )
374 + {
375 + syncedColumns++;
376 + }
377 + else
378 + {
379 + fullySynced = true;
380 + }
225 381 }
226 - };
227 - Collections.sort(orderedColumns, comparator);
228 - }
229 382
230 - // Distribute the width
231 - return distributeWidthImpl(orderedColumns, width);
232 - }
233 -
234 - private int distributeWidthImpl(List<ColumnWidthInfo> columns, int width) {
235 - // Iterate until width can not longer be distributed
236 - boolean growing = (width > 0);
237 - boolean fullySynced = false;
238 - int syncedColumns = 1;
239 - while (columns.size() > 0 && width != 0) {
240 - // Calculate the target difference at the next level
241 - double targetDiff = getTargetDiff(columns, syncedColumns, width);
242 -
243 - // Calculate the total required width to achieve the target difference
244 - int totalRequired = 0;
245 - for (int curIndex = 0; curIndex < syncedColumns; curIndex++) {
246 - // Calculate the new width at the target diff
247 - ColumnWidthInfo curInfo = columns.get(curIndex);
248 - int preferredWidth = curInfo.getPreferredWidth();
249 - int newWidth = (int) (targetDiff * preferredWidth) + preferredWidth;
250 -
251 - // Compare the boundaries
252 - if (growing) {
253 - newWidth = Math.max(newWidth, curInfo.getCurrentWidth());
254 - if (curInfo.hasMaximumWidth()) {
255 - newWidth = Math.min(newWidth, curInfo.getMaximumWidth());
256 - }
257 - } else {
258 - newWidth = Math.min(newWidth, curInfo.getCurrentWidth());
259 - if (curInfo.hasMinimumWidth()) {
260 - newWidth = Math.max(newWidth, curInfo.getMinimumWidth());
261 - }
262 - }
263 -
264 - // Calculate the width required to achieve the new width
265 - curInfo.setRequiredWidth(newWidth - curInfo.getNewWidth());
266 - totalRequired += curInfo.getRequiredWidth();
267 - }
268 -
269 - // Calculate the percent of the required width that is available
270 - double percentAvailable = 1.0;
271 - if (totalRequired != 0) {
272 - percentAvailable = Math.min(1.0, width / (double) totalRequired);
273 - }
274 - for (int curIndex = 0; curIndex < syncedColumns; curIndex++) {
275 - // Determine the true width to add to the column
276 - ColumnWidthInfo curInfo = columns.get(curIndex);
277 - int required = (int) (percentAvailable * curInfo.getRequiredWidth());
278 -
279 - // Make sure we get out of the loop by distributing at least 1
280 - if (fullySynced) {
281 - if (growing) {
282 - required = Math.max(RESOLUTION, required);
283 - } else {
284 - required = Math.min(-RESOLUTION, required);
285 - }
286 - }
287 -
288 - // Don't distribute more than the available width
289 - if (growing && required > width) {
290 - required = width;
291 - } else if (!growing && required < width) {
292 - required = width;
293 - }
294 -
295 - // Set the new width of the column
296 - curInfo.setNewWidth(curInfo.getNewWidth() + required);
297 - width -= required;
298 -
299 - // Remove the column if it has reached its maximum/minimum width
300 - boolean maxedOut = false;
301 - if (growing && curInfo.hasMaximumWidth()) {
302 - maxedOut = (curInfo.getNewWidth() >= curInfo.getMaximumWidth());
303 - } else if (!growing && curInfo.hasMinimumWidth()) {
304 - maxedOut = (curInfo.getNewWidth() <= curInfo.getMinimumWidth());
305 - }
306 - if (maxedOut) {
307 - columns.remove(curIndex);
308 - curIndex--;
309 - syncedColumns--;
310 - }
311 - }
312 -
313 - // Increment the number of synced column
314 - if (!fullySynced && syncedColumns < columns.size()) {
315 - syncedColumns++;
316 - } else {
317 - fullySynced = true;
318 - }
383 + // Return the undistributed width
384 + return width;
319 385 }
320 386
321 - // Return the undistributed width
322 - return width;
323 - }
324 -
325 - /**
326 - * Calculate the target percentage difference of the next level.
327 - *
328 - * @param columns the column width info
329 - * @param syncedColumns the number of synced columns
330 - * @param width the width to distribute
331 - */
332 - private double getTargetDiff(List<ColumnWidthInfo> columns,
333 - int syncedColumns, int width) {
334 - if (syncedColumns < columns.size()) {
335 - // Use the diff of the next un-synced column as the target
336 - return columns.get(syncedColumns).getPercentageDifference();
337 - } else {
338 - // Calculate the total diff after all width has been distributed
339 - int totalNewWidth = width;
340 - int totalPreferredWidth = 0;
341 - for (ColumnWidthInfo info : columns) {
342 - totalNewWidth += info.getNewWidth();
343 - totalPreferredWidth += info.getPreferredWidth();
344 - }
345 - return (totalNewWidth - totalPreferredWidth)
346 - / (double) totalPreferredWidth;
387 + /**
388 + * Calculate the target percentage difference of the next level.
389 + *
390 + * @param columns the column width info
391 + * @param syncedColumns the number of synced columns
392 + * @param width the width to distribute
393 + */
394 + private double getTargetDiff( List<ColumnWidthInfo> columns, int syncedColumns, int width )
395 + {
396 + if ( syncedColumns < columns.size() )
397 + {
398 + // Use the diff of the next un-synced column as the target
399 + return columns.get( syncedColumns ).getPercentageDifference();
400 + }
401 + else
402 + {
403 + // Calculate the total diff after all width has been distributed
404 + int totalNewWidth = width;
405 + int totalPreferredWidth = 0;
406 + for ( ColumnWidthInfo info : columns )
407 + {
408 + totalNewWidth += info.getNewWidth();
409 + totalPreferredWidth += info.getPreferredWidth();
410 + }
411 + return (totalNewWidth - totalPreferredWidth) / (double) totalPreferredWidth;
412 + }
347 413 }
348 - }
349 414 }