Subversion Repository Public Repository

litesoft

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.gen2.table.client;

import java.util.*;

/**
 * <p>
 * A helper class that distributes available width across a set of columns.
 * </p>
 * <p/>
 * <h3>The following algorithm is used to distribute the available width:</h3>
 * <ol>
 * <li>Calculate the percent difference between the current and preferred width
 * of each column</li>
 * <li>Order the columns in descending order according to their percent
 * differences from preferred width</li>
 * <li>For each iteration, distribute width into the first n columns such that
 * their percent difference now equals that of the n+1 column</li>
 * <li>If a column hits its minimum or maximum size, remove it from the list of
 * columns</li>
 * <li>If there are no more columns that can accept more/less width (they are
 * all at their boundaries), return the undistributed width.</li>
 * </ol>
 */
class ColumnResizer
{
    /**
     * The resolution of the width of columns. The true target width of a column
     * is usually a decimal, but column widths can only be represented as integers
     * (ie. pixels). The resolution determines the maximum number of pixels that
     * the calculations can be off by.
     * <p/>
     * Increasing the resolution will increase the speed of the algorithm and
     * reduce the accuracy of the calculations. The resolution must be at least 1
     * or errors will occur (because we cannot get perfect "0" accuracy).
     */
    private static final int RESOLUTION = 1;

    /**
     * A class that contains the current and desired width of a column.
     */
    static class ColumnWidthInfo
    {
        private int minWidth;
        private int maxWidth;
        private int preferredWidth;
        private int curWidth;

        /**
         * The new column width.
         */
        private int newWidth = 0;

        /**
         * The required width to achieve the next level.
         */
        private int requiredWidth;

        /**
         * Construct a new {@link ColumnWidthInfo}.
         *
         * @param minWidth       the minimum width of the column
         * @param maxWidth       the maximum width of the column
         * @param preferredWidth the preferred width of the column
         * @param curWidth       the current width of the column
         */
        public ColumnWidthInfo( int minWidth, int maxWidth, int preferredWidth, int curWidth )
        {
            this.minWidth = minWidth;
            this.maxWidth = maxWidth;
            this.preferredWidth = preferredWidth;
            this.curWidth = curWidth;
        }

        public int getCurrentWidth()
        {
            return curWidth;
        }

        public int getMaximumWidth()
        {
            // For calculation purposes, ensure maxWidth >= minWidth
            if ( hasMaximumWidth() )
            {
                return Math.max( maxWidth, minWidth );
            }
            return maxWidth;
        }

        public int getMinimumWidth()
        {
            return minWidth;
        }

        public int getNewWidth()
        {
            return newWidth;
        }

        public int getPreferredWidth()
        {
            return preferredWidth;
        }

        public boolean hasMaximumWidth()
        {
            return maxWidth >= 0;
        }

        public boolean hasMinimumWidth()
        {
            return minWidth >= 0;
        }

        public void setCurrentWidth( int curWidth )
        {
            this.curWidth = curWidth;
        }

        public void setMaximumWidth( int maxWidth )
        {
            this.maxWidth = maxWidth;
        }

        public void setMinimumWidth( int minWidth )
        {
            this.minWidth = minWidth;
        }

        public void setPreferredWidth( int preferredWidth )
        {
            this.preferredWidth = preferredWidth;
        }

        /**
         * Get the percentage difference between the current column width and the
         * preferred column width. A negative value indicates that the current width
         * is less than the preferred width, while a positive value indicates that
         * the current width is above the preferred width.
         *
         * @return the percentage difference
         */
        double getPercentageDifference()
        {
            return (newWidth - preferredWidth) / (double) preferredWidth;
        }

        int getRequiredWidth()
        {
            return requiredWidth;
        }

        void setNewWidth( int newWidth )
        {
            this.newWidth = newWidth;
        }

        void setRequiredWidth( int requiredWidth )
        {
            this.requiredWidth = requiredWidth;
        }
    }

    /**
     * Distribute some width across a list of columns, respecting the minimum and
     * maximum widths of the columns. The return value is the remaining width that
     * could not be distributed due to constraints. If the return value is 0, all
     * of the width has been distributed.
     *
     * @param columns the list of column width info
     * @param width   the width to distribute
     *
     * @return the width that could not be distributed
     */
    public int distributeWidth( List<ColumnWidthInfo> columns, int width )
    {
        // The new width defaults to the current width, within min/max range
        for ( ColumnWidthInfo info : columns )
        {
            int curWidth = info.getCurrentWidth();
            if ( info.hasMinimumWidth() && curWidth < info.getMinimumWidth() )
            {
                curWidth = info.getMinimumWidth();
            }
            else if ( info.hasMaximumWidth() && curWidth > info.getMaximumWidth() )
            {
                curWidth = info.getMaximumWidth();
            }
            width -= (curWidth - info.getCurrentWidth());
            info.setNewWidth( curWidth );
        }

        // Do not modify widths if there is nothing to distribute
        if ( width == 0 )
        {
            return 0;
        }

        // Copy the list of columns
        List<ColumnWidthInfo> orderedColumns = new ArrayList<ColumnWidthInfo>( columns );

        // Sort the list of columns
        if ( width > 0 )
        {
            // Enlarge columns
            Comparator<ColumnWidthInfo> comparator = new Comparator<ColumnWidthInfo>()
            {
                public int compare( ColumnWidthInfo o1, ColumnWidthInfo o2 )
                {
                    double diff1 = o1.getPercentageDifference();
                    double diff2 = o2.getPercentageDifference();
                    if ( diff1 < diff2 )
                    {
                        return -1;
                    }
                    else if ( diff1 == diff2 )
                    {
                        return 0;
                    }
                    else
                    {
                        return 1;
                    }
                }
            };
            Collections.sort( orderedColumns, comparator );
        }
        else if ( width < 0 )
        {
            // Shrink columns
            Comparator<ColumnWidthInfo> comparator = new Comparator<ColumnWidthInfo>()
            {
                public int compare( ColumnWidthInfo o1, ColumnWidthInfo o2 )
                {
                    double diff1 = o1.getPercentageDifference();
                    double diff2 = o2.getPercentageDifference();
                    if ( diff1 > diff2 )
                    {
                        return -1;
                    }
                    else if ( diff1 == diff2 )
                    {
                        return 0;
                    }
                    else
                    {
                        return 1;
                    }
                }
            };
            Collections.sort( orderedColumns, comparator );
        }

        // Distribute the width
        return distributeWidthImpl( orderedColumns, width );
    }

    private int distributeWidthImpl( List<ColumnWidthInfo> columns, int width )
    {
        // Iterate until width can not longer be distributed
        boolean growing = (width > 0);
        boolean fullySynced = false;
        int syncedColumns = 1;
        while ( columns.size() > 0 && width != 0 )
        {
            // Calculate the target difference at the next level
            double targetDiff = getTargetDiff( columns, syncedColumns, width );

            // Calculate the total required width to achieve the target difference
            int totalRequired = 0;
            for ( int curIndex = 0; curIndex < syncedColumns; curIndex++ )
            {
                // Calculate the new width at the target diff
                ColumnWidthInfo curInfo = columns.get( curIndex );
                int preferredWidth = curInfo.getPreferredWidth();
                int newWidth = (int) (targetDiff * preferredWidth) + preferredWidth;

                // Compare the boundaries
                if ( growing )
                {
                    newWidth = Math.max( newWidth, curInfo.getCurrentWidth() );
                    if ( curInfo.hasMaximumWidth() )
                    {
                        newWidth = Math.min( newWidth, curInfo.getMaximumWidth() );
                    }
                }
                else
                {
                    newWidth = Math.min( newWidth, curInfo.getCurrentWidth() );
                    if ( curInfo.hasMinimumWidth() )
                    {
                        newWidth = Math.max( newWidth, curInfo.getMinimumWidth() );
                    }
                }

                // Calculate the width required to achieve the new width
                curInfo.setRequiredWidth( newWidth - curInfo.getNewWidth() );
                totalRequired += curInfo.getRequiredWidth();
            }

            // Calculate the percent of the required width that is available
            double percentAvailable = 1.0;
            if ( totalRequired != 0 )
            {
                percentAvailable = Math.min( 1.0, width / (double) totalRequired );
            }
            for ( int curIndex = 0; curIndex < syncedColumns; curIndex++ )
            {
                // Determine the true width to add to the column
                ColumnWidthInfo curInfo = columns.get( curIndex );
                int required = (int) (percentAvailable * curInfo.getRequiredWidth());

                // Make sure we get out of the loop by distributing at least 1
                if ( fullySynced )
                {
                    if ( growing )
                    {
                        required = Math.max( RESOLUTION, required );
                    }
                    else
                    {
                        required = Math.min( -RESOLUTION, required );
                    }
                }

                // Don't distribute more than the available width
                if ( growing && required > width )
                {
                    required = width;
                }
                else if ( !growing && required < width )
                {
                    required = width;
                }

                // Set the new width of the column
                curInfo.setNewWidth( curInfo.getNewWidth() + required );
                width -= required;

                // Remove the column if it has reached its maximum/minimum width
                boolean maxedOut = false;
                if ( growing && curInfo.hasMaximumWidth() )
                {
                    maxedOut = (curInfo.getNewWidth() >= curInfo.getMaximumWidth());
                }
                else if ( !growing && curInfo.hasMinimumWidth() )
                {
                    maxedOut = (curInfo.getNewWidth() <= curInfo.getMinimumWidth());
                }
                if ( maxedOut )
                {
                    columns.remove( curIndex );
                    curIndex--;
                    syncedColumns--;
                }
            }

            // Increment the number of synced column
            if ( !fullySynced && syncedColumns < columns.size() )
            {
                syncedColumns++;
            }
            else
            {
                fullySynced = true;
            }
        }

        // Return the undistributed width
        return width;
    }

    /**
     * Calculate the target percentage difference of the next level.
     *
     * @param columns       the column width info
     * @param syncedColumns the number of synced columns
     * @param width         the width to distribute
     */
    private double getTargetDiff( List<ColumnWidthInfo> columns, int syncedColumns, int width )
    {
        if ( syncedColumns < columns.size() )
        {
            // Use the diff of the next un-synced column as the target
            return columns.get( syncedColumns ).getPercentageDifference();
        }
        else
        {
            // Calculate the total diff after all width has been distributed
            int totalNewWidth = width;
            int totalPreferredWidth = 0;
            for ( ColumnWidthInfo info : columns )
            {
                totalNewWidth += info.getNewWidth();
                totalPreferredWidth += info.getPreferredWidth();
            }
            return (totalNewWidth - totalPreferredWidth) / (double) totalPreferredWidth;
        }
    }
}

Commits for litesoft/trunk/Java/GWT/Client/src/com/google/gwt/gen2/table/client/ColumnResizer.java

Diff revisions: vs.
Revision Author Commited Message
939 Diff Diff GeorgeS picture GeorgeS Mon 02 Jun, 2014 21:30:31 +0000

Extracting commonfoundation

475 Diff Diff GeorgeS picture GeorgeS Sat 03 Sep, 2011 13:54:51 +0000
282 GeorgeS picture GeorgeS Fri 17 Jun, 2011 13:54:39 +0000