001    /* 
002     * Copyright (c) Holger Pfaff - http://pfaff.ws
003     *
004     * This software maybe used for any purpose provided the
005     * above copyright notice is retained. It is supplied as is.
006     * No warranty expressed or implied - Use at your own risk.
007     */
008    
009    import java.awt.*;
010    
011    /**
012      * (#)ColumnLayout.java
013      * @author   Holger Pfaff
014      * @version  3.2 19-Mar-2004<br><br> 
015      *
016      * Implements a ColumnLayout for gui items which is very useful for forms.
017      * Has the ability to expand specific rows/cols.
018      */
019      
020    public class ColumnLayout implements LayoutManager {
021      
022      /**
023       * do not expand or no rubbercol/row
024       */
025      public final static int NONE = -1;
026      
027      /**
028       * expand horz and vert
029       */
030      public final static int ALL  = -2;
031    
032      /**
033       * expand horz
034       */
035      public final static int HORZ = -3;
036    
037      /**
038       * expand vert
039       */
040      public final static int VERT = -4;
041    
042      /**
043       * The border around each cell
044       */
045      protected Insets cellinset;
046      
047      /**   
048       *  North position
049       */
050      public final static int N  = 0x1 << 0;
051    
052      /**   
053       *  East position
054       */
055      public final static int E  = 0x1 << 1;
056    
057      /**   
058       *  South position
059       */
060      public final static int S  = 0x1 << 2;
061    
062      /**   
063       *  West position
064       */
065      public final static int W  = 0x1 << 3;
066      
067      /**   
068       *  NorthWest position
069       */
070      public final static int NW = N|W;
071    
072      /**   
073       *  SouthWest position
074       */
075      public final static int SW = S|W;
076    
077      /**   
078       *  NorthEast position
079       */
080      public final static int NE = N|E;
081    
082      /**   
083       *  SouthEast position
084       */
085      public final static int SE = S|E;
086    
087      /**   
088       *  Center position
089       */
090      public final static int CENTER = N|E|S|W;
091    
092      /**
093       * where should the component be psoitioned within each cell ?
094       */
095      protected int cellpos;        // NW, N, NE, E, SE, S, SW, W, C
096      
097      /**
098       * Should we expand each cell to fit exactly (as in GridLayout) ?
099       */
100      protected int cellexpn;       // NONE, ALL, HORZ, VERT
101      
102      /**
103       * which col to expand if parent larger than preferred size
104       */
105            protected int rubbercolumn;   // NONE, ALL or colnumber
106            
107      /**
108       * which row to expand if parent larger than preferred size.
109       */
110            protected int rubberrow;      // NONE, ALL or rownumber
111      
112      /**
113       * no of colums
114       */
115      protected int columns;        // no of colums (from constructor)
116      
117      /**
118       * Creates a column layout with default insets
119       *
120       * @param columns        the number of columns
121       * @param cellexpn       expand the component within cell (NONE, ALL, HORZ, VERT)
122       * @param cellpos        position of component within cell (NW, N, NE, E, SE, S, SW, W, C)
123       * @param rubbercolumn   specify column to expand if parent larger as needed (NONE, ALL, <col>)
124       * @param rubberrow      specify row to expand if parent larger as needed (NONE, ALL, <row>)
125       */
126      public ColumnLayout(int columns, int cellexpn, int cellpos, int rubbercolumn, int rubberrow) {
127        this(columns, cellexpn, cellpos, rubbercolumn, rubberrow, new Insets(0,0,0,0));
128      }
129      
130      /**
131       * Creates a column layout
132       *
133       * @param columns        the number of columns
134       * @param cellexpn       expand the component within cell (NONE, ALL, HORZ, VERT)
135       * @param cellpos        position of component within cell (NW, N, NE, E, SE, S, SW, W, C)
136       * @param rubbercolumn   specify column to expand if parent larger as needed (NONE, ALL, <col>)
137       * @param rubberrow      specify row to expand if parent larger as needed (NONE, ALL, <row>)
138       * @param cellinset      the border within each cell
139       */
140      public ColumnLayout(int columns, int cellexpn, int cellpos, int rubbercolumn, int rubberrow, Insets cellinset) {
141        this.columns      = columns;
142        this.cellexpn     = cellexpn;
143        this.cellpos      = cellpos;
144        this.rubbercolumn = rubbercolumn;
145        this.rubberrow    = rubberrow;
146                    this.cellinset    = cellinset;
147      }
148      
149      /**
150       * calculates the y-pos row r (including cellinset)
151       *
152       * @param rh        the rowheight array
153       * @param r        the row
154       */
155      private int ry(int[] rh, int r) {
156        int ry = 0;
157        for(int i = 0; i < r; ++i) {
158          ry += rh[i] + cellinset.top  + cellinset.bottom;
159        }
160        return ry + cellinset.top;
161      }
162      
163      /**
164       * calculates the x-pos for col c (including cellinset)
165       *
166       * @param cw        the columnwidth array
167       * @param c        the column
168       */
169      private int cx(int[] cw, int c) {
170        int cx = 0;
171        for(int i = 0; i < c; ++i) {
172          cx += cw[i] + cellinset.left + cellinset.right;
173        }
174        return cx + cellinset.left;
175      }
176      
177      /**
178       * Get the table size as an int array
179       *
180       * @param c        the component array
181       */
182      private int[][] tableSize(Component[] c) {
183        int[][] ts = new int[2][];  // 0=cw 1=th
184        
185        ts[0] = new int[columns];
186        ts[1] = new int[(c.length / columns) + (c.length % columns == 0 ? 0 : 1)];
187        
188        for(int i = 0; i < c.length; i++) {
189          Dimension d = c[i].getPreferredSize();
190          ts[0][i%columns] = Math.max(d.width,  ts[0][i%columns]);
191          ts[1][i/columns] = Math.max(d.height, ts[1][i/columns]);
192        }
193        
194        return ts;
195      }
196    
197      /**
198       * Position the children. Overridden from LayoutManager
199       *
200       * @param C        the parent container
201       */
202      public void layoutContainer(Container C) {
203        Component[] children = C.getComponents();
204        Insets            in = C.getInsets();
205                    int      parentwidth = C.getSize().width - in.left - in.right;
206                    int     parentheight = C.getSize().height - in.top - in.bottom;
207        int[][]           ts = tableSize(children);
208        int[]             cw = ts[0];
209        int[]             rh = ts[1];
210        Dimension          d = preferredLayoutSize(ts, in);
211        
212                    // rubbercol resizing?
213                    if(d.width < parentwidth) {
214                      if(rubbercolumn == ALL) {
215            for(int i = 0; i < cw.length; i++) {
216                                      cw[i] += (parentwidth - d.width) / cw.length;
217                                    }
218                            } else if(rubbercolumn > -1 && rubbercolumn < columns) {
219                              cw[rubbercolumn] += parentwidth - d.width;
220                            }
221                    }
222                    
223                    // rubberrow resizing?
224                    if(d.height < parentheight) {
225                      if(rubberrow == ALL) {
226            for(int i = 0; i < rh.length; i++) {
227                                      rh[i] += (parentheight - d.height) / rh.length;
228                                    }
229                            } else if(rubberrow > -1 && rubberrow < rh.length) {
230                              rh[rubberrow] += parentheight - d.height;
231                            }
232                    }
233            
234        for(int i = 0; i < children.length; i++) {
235    
236          // what row & column ?
237          int r = i / columns;
238          int c = i % columns;
239          
240          // top-left pos
241          int x = cx(cw, c) + in.left;
242          int y = ry(rh, r) + in.top;
243          
244          // size of this child
245          d = new Dimension(children[i].getPreferredSize());
246          
247          // horz/vert resize needed ?
248          switch(cellexpn) {
249            case ALL:
250            case VERT: d.height = rh[r]; if(cellexpn == VERT) break;
251            case HORZ: d.width = cw[c]; break;
252          }
253          
254          // horz aligment needed ?
255          if(d.width < cw[c]) {
256            int k = 1;
257            switch(cellpos) {
258              case N:
259              case CENTER:
260              case S: k = 2;
261    
262              case NE:
263              case E:
264              case SE: x += (cw[c] - d.width) / k;
265            }
266          }
267          
268          // vert aligment needed ?
269          if(d.height < rh[r]) {
270            int k = 1;
271            switch(cellpos) {
272              case E:
273              case CENTER:
274              case W: k = 2;
275    
276              case SW:
277              case S:
278              case SE: y += (rh[r] - d.height) / k;
279            }
280          }
281          
282          // set pos & size
283          children[i].setBounds(x, y, d.width, d.height);
284        }
285      }
286    
287      /**
288       * Calculate the minimum layout size. Same as preferredLayoutSize() for
289       * this layout. Overridden from LayoutManager
290       *
291       * @param C        the parent container
292       */
293      public Dimension minimumLayoutSize(Container C) {
294        return preferredLayoutSize(C);
295      }
296    
297      /**
298       * Calculate the preferred layout size.
299       *
300       * @param C        the parent container
301       */
302      public Dimension preferredLayoutSize(Container C) {
303        return preferredLayoutSize(tableSize(C.getComponents()), C.getInsets());
304      }
305      
306      /**
307       * Calculate the preferred layout size.
308       *
309       * @param ts        the tablesize array
310       * @param i        the insets of the container
311       */
312      private Dimension preferredLayoutSize(int[][] ts, Insets i) {
313        return new Dimension(cx(ts[0], ts[0].length) - cellinset.left + i.left + i.right,
314                             ry(ts[1], ts[1].length) - cellinset.top  + i.top  + i.bottom);
315      }
316    
317      /**
318       * Just here to satisfy the interface. Overridden from LayoutManager
319       *
320       * @param c        component being removed
321       */
322      public void removeLayoutComponent(Component c) {}
323      
324      /**
325       * Just here to satisfy the interface. Overridden from LayoutManager
326       *
327       * @param s        additional layout info
328       * @param c        component being added
329       */
330      public void addLayoutComponent(String s, Component c) {}
331    }