View Javadoc

1   /*
2    * Copyright (c) 2007 Creative Sphere Limited.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *
10   *   Creative Sphere - initial API and implementation
11   *
12   */
13  package org.abstracthorizon.aequo.gui;
14  
15  import java.awt.LayoutManager;
16  
17  import javax.swing.JTable;
18  import javax.swing.table.TableColumn;
19  import javax.swing.table.TableColumnModel;
20  import javax.swing.table.TableModel;
21  
22  /**
23   * Extension of {@link JTable} that provides different way of resizing of columns.
24   *
25   * @author Daniel Sendula
26   */
27  public class ResizingTable extends JTable {
28  
29      public ResizingTable(TableModel tableModel) {
30          super(tableModel);
31      }
32  
33      public void createDefaultColumnsFromModel() {
34          TableModel m = getModel();
35          if (m != null) {
36              // Remove any current columns
37              TableColumnModel cm = getColumnModel();
38              while (cm.getColumnCount() > 0) {
39                  cm.removeColumn(cm.getColumn(0));
40              }
41  
42              // Create new columns from the data model info
43              for (int i = 0; i < m.getColumnCount(); i++) {
44                  TableColumn newColumn = new TableColumn(i) {
45                      
46                      protected int preferredWidth;
47                      
48                      public void setPreferredWidth(int width) {
49                          preferredWidth = width;
50                      }
51                      
52                      public int getPreferredWidth() {
53                          return preferredWidth;
54                      }
55                  };
56                  addColumn(newColumn);
57              }
58          }
59      }
60  
61      public void doLayout() {
62          TableColumn resizingColumn = getResizingColumn();
63          TableColumnModel columnModel = getColumnModel();
64          
65          int width = getWidth();
66          int columns = getColumnCount();
67          int[] widths = new int[columns];
68          int[] minWidths = new int[columns];
69          int[] maxWidths = new int[columns];
70          
71          int notDefined = 0;
72          int tWidth = 0;
73          if (resizingColumn != null) {
74              for (int i = 0; i < columns; i++) {
75                  TableColumn col = columnModel.getColumn(i);
76                  if (resizingColumn == col) {
77                      widths[i] = col.getWidth();
78                  } else {
79                      if (col.getPreferredWidth() >= 0) {
80                          widths[i] = col.getWidth();
81                      } else {
82                          widths[i] = -1;
83                          notDefined = notDefined + 1;
84                      }
85                  }
86                  minWidths[i] = col.getMinWidth();
87                  maxWidths[i] = col.getMaxWidth();
88                  tWidth = tWidth + widths[i];
89                  if (minWidths[i] < 0) {
90                      minWidths[i] = 15;
91                  }
92              }
93          } else {
94              for (int i = 0; i < columns; i++) {
95                  TableColumn col = columnModel.getColumn(i);
96                  widths[i] = col.getPreferredWidth();
97                  minWidths[i] = col.getMinWidth();
98                  maxWidths[i] = col.getMaxWidth();
99                  if (widths[i] > 0) {
100                     tWidth = tWidth + widths[i];
101                 } else {
102                     notDefined = notDefined + 1;
103                 }
104                 if (minWidths[i] < 0) {
105                     minWidths[i] = 15;
106                 }
107             }
108         }
109         if (notDefined > 0) {
110             if (tWidth < width) {
111                 int nWidth = 0;
112                 for (int i = 0; i < columns; i++) {
113                     if (widths[i] < 0) {
114                         int calc = (width - tWidth) / notDefined;
115                         if (calc < minWidths[i]) {
116                             calc = minWidths[i];
117                         }
118                         widths[i] = calc;
119                         nWidth = nWidth + calc;
120                         notDefined = notDefined - 1;
121                     } else {
122                         nWidth = nWidth + widths[i];
123                     }
124                 }
125                 tWidth = nWidth;
126             } else {
127                 tWidth = 0;
128                 for (int i = 0; i < columns; i++) {
129                     if (widths[i] < 0) {
130                         widths[i] = minWidths[i];
131                     }
132                     tWidth = tWidth + widths[i];
133                 }
134             }
135         }
136         if (tWidth < width) {
137             // size up
138             int add = width - tWidth;
139             int i = 0;
140             while ((i < columns) && (add > 0)) {
141                 int calc = add / (columns - i);
142                 if (calc == 0) {
143                     calc = 1;
144                 }
145                 if (maxWidths[i] >= widths[i] + calc) {
146                     widths[i] = widths[i] + calc;
147                     add = add - calc;
148                 }
149                 i++;
150             }
151         } else if (tWidth > width) {
152             // size down
153             int del = tWidth - width;
154             int i = 0;
155             while ((i < columns) && (del > 0)) {
156                 int calc = del / (columns - i);
157                 if (calc == 0) {
158                     calc = 1;
159                 }
160                 if (minWidths[i] <= widths[i] - calc) {
161                     widths[i] = widths[i] - calc;
162                     del = del - calc;
163                 }
164                 i++;
165             }
166         }
167         for (int i = 0; i < columns; i++) {
168             columnModel.getColumn(i).setWidth(widths[i]);
169         }
170     }
171     
172     
173     
174     
175     
176     public void doLayout2() {
177         TableColumn resizingColumn = getResizingColumn();
178         if (resizingColumn == null) {
179             setWidthsFromPreferredWidths(false);
180         } else {
181             // JTable behaves like a layout manger - but one in which the
182             // user can come along and dictate how big one of the children
183             // (columns) is supposed to be.
184 
185             // A column has been resized and JTable may need to distribute
186             // any overall delta to other columns, according to the resize mode.
187             int columnIndex = viewIndexForColumn(resizingColumn);
188             int delta = getWidth() - getColumnModel().getTotalColumnWidth();
189             accommodateDelta(columnIndex, delta);
190             delta = getWidth() - getColumnModel().getTotalColumnWidth();
191 
192             // If the delta cannot be completely accomodated, then the
193             // resizing column will have to take any remainder. This means
194             // that the column is not being allowed to take the requested
195             // width. This happens under many circumstances: For example,
196             // AUTO_RESIZE_NEXT_COLUMN specifies that any delta be distributed
197             // to the column after the resizing column. If one were to attempt
198             // to resize the last column of the table, there would be no
199             // columns after it, and hence nowhere to distribute the delta.
200             // It would then be given entirely back to the resizing column,
201             // preventing it from changing size.
202             if (delta != 0) {
203                 resizingColumn.setWidth(resizingColumn.getWidth() + delta);
204             }
205 
206             // At this point the JTable has to work out what preferred sizes
207             // would have resulted in the layout the user has chosen.
208             // Thereafter, during window resizing etc. it has to work off
209             // the preferred sizes as usual - the idea being that, whatever
210             // the user does, everything stays in synch and things don't jump
211             // around.
212             setWidthsFromPreferredWidths(true);
213         }
214 
215         LayoutManager layoutMgr = getLayout();
216         if (layoutMgr != null) {
217             layoutMgr.layoutContainer(this);
218         }
219     }
220 
221 
222     
223     
224     
225     
226     
227     
228     
229     private TableColumn getResizingColumn() {
230         return (tableHeader == null) ? null : tableHeader.getResizingColumn();
231     }
232 
233     private void setWidthsFromPreferredWidths(final boolean inverse) {
234         int totalWidth = getWidth();
235         int totalPreferred = getPreferredSize().width;
236         int target = !inverse ? totalWidth : totalPreferred;
237 
238         final TableColumnModel cm = columnModel;
239         Resizable3 r = new Resizable3() {
240             public int getElementCount() {
241                 return cm.getColumnCount();
242             }
243 
244             public int getLowerBoundAt(int i) {
245                 return cm.getColumn(i).getMinWidth();
246             }
247 
248             public int getUpperBoundAt(int i) {
249                 return cm.getColumn(i).getMaxWidth();
250             }
251 
252             public int getMidPointAt(int i) {
253                 if (!inverse) {
254                     return cm.getColumn(i).getPreferredWidth();
255                 } else {
256                     return cm.getColumn(i).getWidth();
257                 }
258             }
259 
260             public void setSizeAt(int s, int i) {
261                 if (!inverse) {
262                     cm.getColumn(i).setWidth(s);
263                 } else {
264                     cm.getColumn(i).setPreferredWidth(s);
265                 }
266             }
267         };
268 
269         adjustSizes(target, r, inverse);
270     }
271 
272     private int viewIndexForColumn(TableColumn aColumn) {
273         TableColumnModel cm = getColumnModel();
274         for (int column = 0; column < cm.getColumnCount(); column++) {
275             if (cm.getColumn(column) == aColumn) {
276                 return column;
277             }
278         }
279         return -1;
280     }
281 
282     private void accommodateDelta(int resizingColumnIndex, int delta) {
283         int columnCount = getColumnCount();
284         int from = resizingColumnIndex;
285         int to = columnCount;
286 
287         // Use the mode to determine how to absorb the changes.
288         switch (autoResizeMode) {
289         case AUTO_RESIZE_NEXT_COLUMN:
290             from = from + 1;
291             to = Math.min(from + 1, columnCount);
292             break;
293         case AUTO_RESIZE_SUBSEQUENT_COLUMNS:
294             from = from + 1;
295             to = columnCount;
296             break;
297         case AUTO_RESIZE_LAST_COLUMN:
298             from = columnCount - 1;
299             to = from + 1;
300             break;
301         case AUTO_RESIZE_ALL_COLUMNS:
302             from = 0;
303             to = columnCount;
304             break;
305         default:
306             return;
307         }
308 
309         final int start = from;
310         final int end = to;
311         final TableColumnModel cm = columnModel;
312         Resizable3 r = new Resizable3() {
313             public int getElementCount() {
314                 return end - start;
315             }
316 
317             public int getLowerBoundAt(int i) {
318                 return cm.getColumn(i + start).getMinWidth();
319             }
320 
321             public int getUpperBoundAt(int i) {
322                 return cm.getColumn(i + start).getMaxWidth();
323             }
324 
325             public int getMidPointAt(int i) {
326                 return cm.getColumn(i + start).getWidth();
327             }
328 
329             public void setSizeAt(int s, int i) {
330                 cm.getColumn(i + start).setWidth(s);
331             }
332         };
333 
334         int totalWidth = 0;
335         for (int i = from; i < to; i++) {
336             TableColumn aColumn = columnModel.getColumn(i);
337             int input = aColumn.getWidth();
338             totalWidth = totalWidth + input;
339         }
340 
341         adjustSizes(totalWidth + delta, r, false);
342 
343         return;
344     }
345 
346     private void adjustSizes(long target, final Resizable3 r, boolean inverse) {
347         int N = r.getElementCount();
348         long totalPreferred = 0;
349         for (int i = 0; i < N; i++) {
350             totalPreferred += r.getMidPointAt(i);
351         }
352         Resizable2 s;
353         if ((target < totalPreferred) == !inverse) {
354             s = new Resizable2() {
355                 public int getElementCount() {
356                     return r.getElementCount();
357                 }
358 
359                 public int getLowerBoundAt(int i) {
360                     return r.getLowerBoundAt(i);
361                 }
362 
363                 public int getUpperBoundAt(int i) {
364                     return r.getMidPointAt(i);
365                 }
366 
367                 public void setSizeAt(int newSize, int i) {
368                     r.setSizeAt(newSize, i);
369                 }
370 
371             };
372         } else {
373             s = new Resizable2() {
374                 public int getElementCount() {
375                     return r.getElementCount();
376                 }
377 
378                 public int getLowerBoundAt(int i) {
379                     return r.getMidPointAt(i);
380                 }
381 
382                 public int getUpperBoundAt(int i) {
383                     return r.getUpperBoundAt(i);
384                 }
385 
386                 public void setSizeAt(int newSize, int i) {
387                     r.setSizeAt(newSize, i);
388                 }
389 
390             };
391         }
392         adjustSizes(target, s, !inverse);
393     }
394 
395     private void adjustSizes(long target, Resizable2 r, boolean limitToRange) {
396         long totalLowerBound = 0;
397         long totalUpperBound = 0;
398         for (int i = 0; i < r.getElementCount(); i++) {
399             totalLowerBound += r.getLowerBoundAt(i);
400             totalUpperBound += r.getUpperBoundAt(i);
401         }
402 
403         if (limitToRange) {
404             target = Math.min(Math.max(totalLowerBound, target),
405                     totalUpperBound);
406         }
407 
408         for (int i = 0; i < r.getElementCount(); i++) {
409             int lowerBound = r.getLowerBoundAt(i);
410             int upperBound = r.getUpperBoundAt(i);
411             // Check for zero. This happens when the distribution of the delta
412             // finishes early due to a series of "fixed" entries at the end.
413             // In this case, lowerBound == upperBound, for all subsequent terms.
414             int newSize;
415             if (totalLowerBound == totalUpperBound) {
416                 newSize = lowerBound;
417             } else {
418                 double f = (double) (target - totalLowerBound)
419                         / (totalUpperBound - totalLowerBound);
420                 newSize = (int) Math.round(lowerBound + f
421                         * (upperBound - lowerBound));
422                 // We'd need to round manually in an all integer version.
423                 // size[i] = (int)(((totalUpperBound - target) * lowerBound +
424                 // (target - totalLowerBound) *
425                 // upperBound)/(totalUpperBound-totalLowerBound));
426             }
427             r.setSizeAt(newSize, i);
428             target -= newSize;
429             totalLowerBound -= lowerBound;
430             totalUpperBound -= upperBound;
431         }
432     }
433 
434     private interface Resizable2 {
435         public int getElementCount();
436 
437         public int getLowerBoundAt(int i);
438 
439         public int getUpperBoundAt(int i);
440 
441         public void setSizeAt(int newSize, int i);
442     }
443 
444     private interface Resizable3 extends Resizable2 {
445         public int getMidPointAt(int i);
446     }
447 
448 }