KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > gjt > sp > jedit > gui > VariableGridLayout


1 /*
2  * VariableGridLayout.java - a grid layout manager with variable cell sizes
3  * :tabSize=8:indentSize=8:noTabs=false:
4  *
5  * Originally written by Dirk Moebius for the jEdit project. This work has been
6  * placed into the public domain. You may use this work in any way and for any
7  * purpose you wish.
8  *
9  * THIS SOFTWARE IS PROVIDED AS-IS WITHOUT WARRANTY OF ANY KIND, NOT EVEN THE
10  * IMPLIED WARRANTY OF MERCHANTABILITY. THE AUTHOR OF THIS SOFTWARE, ASSUMES
11  * _NO_ RESPONSIBILITY FOR ANY CONSEQUENCE RESULTING FROM THE USE, MODIFICATION,
12  * OR REDISTRIBUTION OF THIS SOFTWARE.
13  */

14
15 package org.gjt.sp.jedit.gui;
16
17 import java.awt.Component JavaDoc;
18 import java.awt.Container JavaDoc;
19 import java.awt.Dimension JavaDoc;
20 import java.awt.Insets JavaDoc;
21 import java.awt.LayoutManager2 JavaDoc;
22
23 import java.util.Arrays JavaDoc;
24
25 /**
26  * The <code>VariableGridLayout</code> class is a layout manager
27  * that lays out a container's components in a rectangular grid
28  * with variable cell sizes.<p>
29  *
30  * The container is divided into rectangles, and one component is placed
31  * in each rectangle. Each row is as large as the largest component in
32  * that row, and each column is as wide as the widest component in
33  * that column.<p>
34  *
35  * This behavior is basically the same as in
36  * <code>java.awt.GridLayout</code>, but with different row heights and
37  * column widths for each row/column.<p>
38  *
39  * For example, the following is an applet that lays out six buttons
40  * into three rows and two columns:<p>
41  *
42  * <blockquote><pre>
43  * import java.awt.*;
44  * import java.applet.Applet;
45  * public class ButtonGrid extends Applet {
46  * public void init() {
47  * setLayout(new VariableGridLayout(VariableGridLayout.FIXED_NUM_COLUMNS, 2));
48  * add(new Button("1"));
49  * add(new Button("2"));
50  * add(new Button("3"));
51  * add(new Button("4"));
52  * add(new Button("5"));
53  * add(new Button("6"));
54  * }
55  * }
56  * </pre></blockquote><p>
57  *
58  * <b>Programmer's remark:</b> VariableGridLayout could be faster, if it would
59  * reside in the package java.awt, because then it could access some
60  * package private fields of <code>Container</code> or
61  * <code>Component</code>. Instead, it has to call
62  * <code>Component.getSize()</code>,
63  * which allocates memory on the heap.<p>
64  *
65  * <b>Todo:</b>
66  * <ul>
67  * <li>Ability to span components over more than one cell horizontally and vertically.
68  * </ul>
69  *
70  * @author Dirk Moebius, Björn "Vampire" Kautler
71  * @version 1.5
72  * @see java.awt.GridLayout
73  */

74 public class VariableGridLayout implements LayoutManager2 JavaDoc, java.io.Serializable JavaDoc
75 {
76     public static final int FIXED_NUM_ROWS = 1;
77     public static final int FIXED_NUM_COLUMNS = 2;
78
79     private static enum LayoutSize { MINIMUM, MAXIMUM, PREFERRED }
80
81     /**
82      * Creates a variable grid layout manager with the specified mode,
83      * size, horizontal and vertical gap, eventually taking minimum and maximum
84      * sizes into account when distributing free space, depending on takeSizesIntoAccount
85      * and the specified distance to the borders.
86      *
87      * @param mode The mode in which to operate. Either FIXED_NUM_ROWS or FIXED_NUM_COLUMNS
88      * @param size The amount of rows for mode FIXED_NUM_ROWS or the amount of columns for mode FIXED_NUM_COLUMNS (>0)
89      * @param hgap The horizontal space between cells (>=0)
90      * @param vgap The vertical space between cells (>=0)
91      * @param takeSizesIntoAccount Whether to take minimum and maximum sizes into account when distributing free space
92      * @param distanceToBorders The distances to the borders
93      * @throws IllegalArgumentException if mode is not either FIXED_NUM_ROWS or FIXED_NUM_COLUMNS or size is <= 0 or hgap or vgap is < 0
94      */

95     public VariableGridLayout(int mode, int size, int hgap, int vgap, boolean takeSizesIntoAccount, Insets JavaDoc distanceToBorders)
96     {
97         if (mode != FIXED_NUM_ROWS && mode != FIXED_NUM_COLUMNS)
98         {
99             throw new IllegalArgumentException JavaDoc("illegal mode; value is " + mode);
100         }
101         if (size <= 0)
102         {
103             throw new IllegalArgumentException JavaDoc("size cannot be zero or less; value is " + size);
104         }
105         if (hgap < 0)
106         {
107             throw new IllegalArgumentException JavaDoc("hgap cannot be negative; value is " + hgap);
108         }
109         if (vgap < 0)
110         {
111             throw new IllegalArgumentException JavaDoc("vgap cannot be negative; value is " + vgap);
112         }
113         this.mode = mode;
114         this.size = size;
115         this.hgap = hgap;
116         this.vgap = vgap;
117         this.takeSizesIntoAccount = takeSizesIntoAccount;
118         this.distanceToBorders = (Insets JavaDoc)distanceToBorders.clone();
119     }
120
121     /**
122      * Creates a variable grid layout manager with the specified mode,
123      * size, horizontal and vertical gap, eventually taking minimum and maximum
124      * sizes into account when distributing free space, depending on takeSizesIntoAccount
125      * and zero distance to borders.
126      *
127      * @param mode The mode in which to operate. Either FIXED_NUM_ROWS or FIXED_NUM_COLUMNS
128      * @param size The amount of rows for mode FIXED_NUM_ROWS or the amount of columns for mode FIXED_NUM_COLUMNS (>0)
129      * @param hgap The horizontal space between cells (>=0)
130      * @param vgap The vertical space between cells (>=0)
131      * @param takeSizesIntoAccount Whether to take minimum and maximum sizes into account when distributing free space
132      * @throws IllegalArgumentException if mode is not either FIXED_NUM_ROWS or FIXED_NUM_COLUMNS or size is <= 0 or hgap or vgap is < 0
133      */

134     public VariableGridLayout(int mode, int size, int hgap, int vgap, boolean takeSizesIntoAccount)
135     {
136         this(mode, size, hgap, vgap, takeSizesIntoAccount, new Insets JavaDoc(0,0,0,0));
137     }
138
139     /**
140      * Creates a variable grid layout manager with the specified mode,
141      * size, horizontal and vertical gap, and zero distance to borders.
142      * The minimum and maximum Component sizes are not taken into account
143      * when distributing free space.
144      *
145      * @param mode The mode in which to operate. Either FIXED_NUM_ROWS or FIXED_NUM_COLUMNS
146      * @param size The amount of rows for mode FIXED_NUM_ROWS or the amount of columns for mode FIXED_NUM_COLUMNS
147      * @param hgap The horizontal space between cells
148      * @param vgap The vertical space between cells
149      * @throws IllegalArgumentException if mode is not either FIXED_NUM_ROWS or FIXED_NUM_COLUMNS or size is <= 0 or hgap or vgap is < 0
150      */

151     public VariableGridLayout(int mode, int size, int hgap, int vgap)
152     {
153         this(mode, size, hgap, vgap, false, new Insets JavaDoc(0,0,0,0));
154     }
155
156     /**
157      * Creates a variable grid layout manager with the specified mode
158      * and size, zero horizontal and vertical gap, and zero distance to borders.
159      * Does not take minimum and maximum Component sizes into account when distributing
160      * free space.
161      *
162      * @param mode The mode in which to operate. Either FIXED_NUM_ROWS or FIXED_NUM_COLUMNS
163      * @param size The amount of rows for mode FIXED_NUM_ROWS or the amount of columns for mode FIXED_NUM_COLUMNS
164      * @throws IllegalArgumentException if mode is not either FIXED_NUM_ROWS or FIXED_NUM_COLUMNS or size is <= 0
165      */

166     public VariableGridLayout(int mode, int size)
167     {
168         this(mode, size, 0, 0, false, new Insets JavaDoc(0,0,0,0));
169     }
170
171     /**
172      * Creates a variable grid layout manager with mode FIXED_NUM_ROWS,
173      * number of rows == 1, zero horizontal and vertical gap, and zero distance to borders.
174      * Does not take minimum and maximum Component sizes into account when
175      * distributing free space.
176      */

177     public VariableGridLayout()
178     {
179         this(FIXED_NUM_ROWS, 1, 0, 0, false, new Insets JavaDoc(0,0,0,0));
180     }
181
182     /**
183      * Not used in this class.
184      */

185     public void addLayoutComponent(String JavaDoc name, Component JavaDoc component)
186     {
187     }
188
189     /**
190      * Not used in this class.
191      */

192     public void addLayoutComponent(Component JavaDoc component, Object JavaDoc constraints)
193     {
194     }
195
196     /**
197      * Not used in this class.
198      */

199     public void removeLayoutComponent(Component JavaDoc component)
200     {
201     }
202
203     /**
204      * Always returns 0.5.
205      */

206     public float getLayoutAlignmentX(Container JavaDoc container)
207     {
208         return 0.5f;
209     }
210
211     /**
212      * Always returns 0.5.
213      */

214     public float getLayoutAlignmentY(Container JavaDoc container)
215     {
216         return 0.5f;
217     }
218
219     public Dimension JavaDoc preferredLayoutSize(Container JavaDoc parent)
220     {
221         return getLayoutSize(parent,LayoutSize.PREFERRED);
222     }
223
224     public Dimension JavaDoc minimumLayoutSize(Container JavaDoc parent)
225     {
226         return getLayoutSize(parent,LayoutSize.MINIMUM);
227     }
228
229     public Dimension JavaDoc maximumLayoutSize(Container JavaDoc parent)
230     {
231         return getLayoutSize(parent,LayoutSize.MAXIMUM);
232     }
233
234     public void layoutContainer(Container JavaDoc parent)
235     {
236         synchronized (parent.getTreeLock())
237         {
238             update(parent);
239
240             int ncomponents = parent.getComponentCount();
241
242             if (ncomponents == 0)
243             {
244                 return;
245             }
246
247             // Pass 1: compute minimum, preferred and maximum row heights / column widths
248
int total_height = 0;
249             Arrays.fill(row_heights,0);
250             Arrays.fill(col_widths,0);
251             if (takeSizesIntoAccount)
252             {
253                 Arrays.fill(minimum_row_heights,0);
254                 Arrays.fill(minimum_col_widths,0);
255                 Arrays.fill(maximum_row_heights,Integer.MAX_VALUE);
256                 Arrays.fill(maximum_col_widths,Integer.MAX_VALUE);
257             }
258             for (int r = 0, i = 0; r < nrows; r++)
259             {
260                 for (int c = 0; c < ncols; c++, i++)
261                 {
262                     if (i < ncomponents)
263                     {
264                         Component JavaDoc comp = parent.getComponent(i);
265                         Dimension JavaDoc d = comp.getPreferredSize();
266                         row_heights[r] = Math.max(row_heights[r], d.height);
267                         col_widths[c] = Math.max(col_widths[c], d.width);
268                         if (takeSizesIntoAccount)
269                         {
270                             d = comp.getMinimumSize();
271                             minimum_row_heights[r] = Math.max(minimum_row_heights[r], d.height);
272                             minimum_col_widths[c] = Math.max(minimum_col_widths[c], d.width);
273                             d = comp.getMaximumSize();
274                             maximum_row_heights[r] = Math.min(maximum_row_heights[r], d.height);
275                             maximum_col_widths[c] = Math.min(maximum_col_widths[c], d.width);
276                         }
277                     }
278                     else
279                     {
280                         break;
281                     }
282                 }
283                 if (takeSizesIntoAccount)
284                 {
285                     if (minimum_row_heights[r] >= maximum_row_heights[r])
286                     {
287                         maximum_row_heights[r] = minimum_row_heights[r];
288                         row_heights[r] = minimum_row_heights[r];
289                     }
290                     else if (row_heights[r] < minimum_row_heights[r])
291                     {
292                         row_heights[r] = minimum_row_heights[r];
293                     }
294                     else if (row_heights[r] > maximum_row_heights[r])
295                     {
296                         row_heights[r] = maximum_row_heights[r];
297                     }
298                 }
299                 total_height += row_heights[r];
300             }
301
302             int total_width = 0;
303             for (int c = 0; c < ncols; c++)
304             {
305                 if (takeSizesIntoAccount)
306                 {
307                     if (minimum_col_widths[c] >= maximum_col_widths[c])
308                     {
309                         maximum_col_widths[c] = minimum_col_widths[c];
310                         col_widths[c] = minimum_col_widths[c];
311                     }
312                     else if (col_widths[c] < minimum_col_widths[c])
313                     {
314                         col_widths[c] = minimum_col_widths[c];
315                     }
316                     else if (col_widths[c] > maximum_col_widths[c])
317                     {
318                         col_widths[c] = maximum_col_widths[c];
319                     }
320                 }
321                 total_width += col_widths[c];
322             }
323
324             // Pass 2: redistribute free space
325
Dimension JavaDoc parent_size = parent.getSize();
326             Insets JavaDoc insets = parent.getInsets();
327             int free_height = parent_size.height
328                       - insets.top - insets.bottom
329                       - (nrows - 1) * vgap
330                       - distanceToBorders.top - distanceToBorders.bottom;
331             int free_width = parent_size.width
332                      - insets.left - insets.right
333                      - (ncols - 1) * hgap
334                      - distanceToBorders.left - distanceToBorders.right;
335
336             redistributeSpace(total_height,free_height,
337                       takeSizesIntoAccount,
338                       nrows,row_heights,
339                       minimum_row_heights,
340                       maximum_row_heights);
341
342             redistributeSpace(total_width,free_width,
343                       takeSizesIntoAccount,
344                       ncols,col_widths,
345                       minimum_col_widths,
346                       maximum_col_widths);
347
348             // Pass 3: layout components
349
for (int r = 0, y = insets.top + distanceToBorders.top, i = 0; r < nrows; y += row_heights[r] + vgap, r++)
350             {
351                 for (int c = 0, x = insets.left + distanceToBorders.left; c < ncols; x += col_widths[c] + hgap, c++, i++)
352                 {
353                     if (i < ncomponents)
354                     {
355                         Component JavaDoc comp = parent.getComponent(i);
356                         Dimension JavaDoc d = comp.getMaximumSize();
357                         int width = col_widths[c];
358                         int height = row_heights[r];
359                         int xCorrection = 0;
360                         int yCorrection = 0;
361                         if (width > d.width)
362                         {
363                             xCorrection = (int)((width - d.width) * comp.getAlignmentX());
364                             width = d.width;
365                         }
366                         if (height > d.height)
367                         {
368                             yCorrection = (int)((height-d.height) * comp.getAlignmentY());
369                             height = d.height;
370                         }
371                         
372                         comp.setBounds(x + xCorrection, y + yCorrection, width, height);
373                     }
374                 }
375             }
376         } // synchronized
377
}
378
379     public void invalidateLayout(Container JavaDoc container)
380     {
381     }
382
383     /**
384      * Returns the string representation of this variable grid layout's values.
385      * @return a string representation of this variable grid layout.
386      */

387     public String JavaDoc toString()
388     {
389         return getClass().getName() + "[mode="
390             + ((FIXED_NUM_ROWS == mode) ? "FIXED_NUM_ROWS"
391                : ((FIXED_NUM_COLUMNS == mode) ? "FIXED_NUM_COLUMNS"
392                   : "UNKNOWN(" + mode + ")")) + ",size=" + size
393             + ",hgap=" + hgap + ",vgap=" + vgap
394             + ",takeSizesIntoAccount=" + takeSizesIntoAccount
395             + ",distanceToBorders=" + distanceToBorders + "]";
396     }
397
398     /**
399      * @param which if LayoutSize.MINIMUM compute minimum layout size,
400      * if LayoutSize.MAXIMUM compute maximum layout size,
401      * if LayoutSize.PREFERRED compute preferred layout size.
402      */

403     private Dimension JavaDoc getLayoutSize(Container JavaDoc parent, LayoutSize which)
404     {
405         synchronized (parent.getTreeLock())
406         {
407             update(parent);
408
409             int ncomponents = parent.getComponentCount();
410             long h = 0;
411             long w = 0;
412
413             for (int r = 0, i = 0; r < nrows; r++)
414             {
415                 int row_height = 0;
416                 for (int c = 0; c < ncols; c++, i++)
417                 {
418                     if (i < ncomponents)
419                     {
420                         switch (which)
421                         {
422                             case MINIMUM:
423                                 row_height = Math.max(row_height, parent.getComponent(i).getMinimumSize().height);
424                                 break;
425                             case MAXIMUM:
426                                 row_height = Math.max(row_height, parent.getComponent(i).getMaximumSize().height);
427                                 break;
428                             case PREFERRED:
429                                 row_height = Math.max(row_height, parent.getComponent(i).getPreferredSize().height);
430                                 break;
431                             default:
432                                 throw new InternalError JavaDoc("missing case branch for LayoutSize " + which);
433                         }
434                     }
435                     else
436                     {
437                         break;
438                     }
439                 }
440                 h += row_height;
441             }
442
443             for (int c = 0; c < ncols; c++)
444             {
445                 int col_width = 0;
446                 for (int r = 0; r < nrows; r++)
447                 {
448                     int i = r * ncols + c;
449                     if (i < ncomponents)
450                     {
451                         switch (which)
452                         {
453                             case MINIMUM:
454                                 col_width = Math.max(col_width, parent.getComponent(i).getMinimumSize().width);
455                                 break;
456                             case MAXIMUM:
457                                 col_width = Math.max(col_width, parent.getComponent(i).getMaximumSize().width);
458                                 break;
459                             case PREFERRED:
460                                 col_width = Math.max(col_width, parent.getComponent(i).getPreferredSize().width);
461                                 break;
462                             default:
463                                 throw new InternalError JavaDoc("missing case branch for LayoutSize " + which);
464                         }
465                     }
466                     else
467                     {
468                         break;
469                     }
470                 }
471                 w += col_width;
472             }
473
474             Insets JavaDoc insets = parent.getInsets();
475             w += insets.left + insets.right + ((ncols - 1) * hgap) + distanceToBorders.left + distanceToBorders.right;
476             h += insets.top + insets.bottom + ((nrows - 1) * vgap) + distanceToBorders.top + distanceToBorders.bottom;
477             if (w > Integer.MAX_VALUE) {
478                 w = Integer.MAX_VALUE;
479             }
480             if (h > Integer.MAX_VALUE) {
481                 h = Integer.MAX_VALUE;
482             }
483             return new Dimension JavaDoc((int)w,(int)h);
484         }
485     }
486
487     private void update(Container JavaDoc container)
488     {
489         int ncomponents = container.getComponentCount();
490         int old_nrows = nrows;
491         int old_ncols = ncols;
492         if (this.mode == FIXED_NUM_ROWS)
493         {
494             nrows = this.size;
495             ncols = (ncomponents + nrows - 1) / nrows;
496         }
497         else
498         {
499             ncols = this.size;
500             nrows = (ncomponents + ncols - 1) / ncols;
501         }
502         if (old_nrows != nrows)
503         {
504             row_heights = new int[nrows];
505             if (takeSizesIntoAccount)
506             {
507                 minimum_row_heights = new int[nrows];
508                 maximum_row_heights = new int[nrows];
509             }
510         }
511         if (old_ncols != ncols)
512         {
513             col_widths = new int[ncols];
514             if (takeSizesIntoAccount)
515             {
516                 minimum_col_widths = new int[ncols];
517                 maximum_col_widths = new int[ncols];
518             }
519         }
520     }
521
522     private void redistributeSpace(int total_size, int free_size, boolean takeSizesIntoAccount,
523                        int nelements, int[] element_sizes,
524                        int[] minimum_element_sizes, int[] maximum_element_sizes)
525     {
526         if (total_size != free_size)
527         {
528             if (takeSizesIntoAccount)
529             {
530                 boolean grow = total_size < free_size;
531                 free_size = (free_size - total_size) * (grow ? 1 : -1);
532                 while (free_size != 0)
533                 {
534                     int modifyableAmount = 0;
535                     int modifySize = 0;
536                     for (int i = 0 ; i < nelements ; i++)
537                     {
538                         if ((grow && (element_sizes[i] < maximum_element_sizes[i])) ||
539                             (!grow && (element_sizes[i] > minimum_element_sizes[i])))
540                         {
541                             modifyableAmount++;
542                             modifySize += element_sizes[i];
543                         }
544                     }
545                     boolean checkBounds = true;
546                     if (0 == modifyableAmount)
547                     {
548                         for (int i = 0 ; i < nelements ; i++)
549                         {
550                             modifySize += element_sizes[i];
551                         }
552                         checkBounds = false;
553                         modifyableAmount = nelements;
554                     }
555                     if (free_size < modifyableAmount)
556                     {
557                         for (int i = 0 ; i < nelements ; i++)
558                         {
559                             if ((free_size != 0) &&
560                                 (!checkBounds ||
561                                  (checkBounds &&
562                                   (grow && (element_sizes[i] < maximum_element_sizes[i])) ||
563                                   (!grow && (element_sizes[i] > minimum_element_sizes[i])))))
564                             {
565                                 element_sizes[i] += (grow ? 1 : -1);
566                                 if (0 > element_sizes[i])
567                                 {
568                                     element_sizes[i] = 0;
569                                 }
570                                 free_size--;
571                             }
572                         }
573                     }
574                     else
575                     {
576                         int modifySizeAddition = 0;
577                         for (int i = 0 ; i < nelements ; i++)
578                         {
579                             int modifyableSize = (checkBounds ? (grow ? maximum_element_sizes[i] - element_sizes[i] : element_sizes[i] - minimum_element_sizes[i]) : Integer.MAX_VALUE - element_sizes[i]);
580                             int elementModifySize = (int)((double)free_size / (double)modifySize * (double)element_sizes[i]);
581                             if (elementModifySize <= modifyableSize)
582                             {
583                                 element_sizes[i] += (grow ? elementModifySize : -elementModifySize);
584                                 modifySizeAddition += (grow ? elementModifySize : -elementModifySize);
585                                 free_size -= elementModifySize;
586                             }
587                             else
588                             {
589                                 element_sizes[i] += (grow ? modifyableSize : -modifyableSize);
590                                 modifySizeAddition += (grow ? modifyableSize : -modifyableSize);
591                                 free_size -= modifyableSize;
592                             }
593                             if (0 > element_sizes[i])
594                             {
595                                 element_sizes[i] = 0;
596                             }
597                         }
598                         modifySize += modifySizeAddition;
599                     }
600                 }
601             }
602             else
603             {
604                 double d = (double)free_size / (double)total_size;
605                 for (int i = 0; i < nelements; i++)
606                 {
607                     element_sizes[i] = (int)(element_sizes[i] * d);
608                 }
609             }
610         }
611     }
612
613     private int mode;
614     private int size;
615     private int hgap;
616     private int vgap;
617     private boolean takeSizesIntoAccount;
618     private Insets JavaDoc distanceToBorders;
619     private transient int nrows = -1;
620     private transient int ncols = -1;
621     private transient int[] minimum_row_heights = null;
622     private transient int[] minimum_col_widths = null;
623     private transient int[] row_heights = null;
624     private transient int[] col_widths = null;
625     private transient int[] maximum_row_heights = null;
626     private transient int[] maximum_col_widths = null;
627 }
628
Popular Tags