1 package net.logAnalyzer.utils.gui;
2
3 import java.awt.Component;
4 import java.awt.Point;
5 import java.awt.Rectangle;
6 import java.awt.event.MouseAdapter;
7 import java.awt.event.MouseEvent;
8
9 import javax.swing.JTable;
10 import javax.swing.event.TableModelEvent;
11 import javax.swing.event.TableModelListener;
12 import javax.swing.table.JTableHeader;
13 import javax.swing.table.TableCellRenderer;
14 import javax.swing.table.TableColumn;
15 import javax.swing.table.TableColumnModel;
16
17 /***
18 * This subclass of <code>JTableHeader</code> extends a
19 * <code>JTableHeader</code> with the ability to resize a column to fit all
20 * its content on double click on the right end of the column's header cell.
21 * <p>
22 * Note that you have to set the tableheader's columnmodel to the columnmodel of
23 * the table. See example for usage:
24 *
25 * <pre>
26 * JTable table;
27 * ResizeableTableHeader tableHeader;
28 *
29 * // create new table
30 * table = new JTable();
31 * // create new ResizableTableHeader with the table's ColumnModel
32 * tableHeader = new ResizeableTableHeader(table.getColumnModel());
33 * // set the new header for the table
34 * table.setTableHeader(tableHeader);
35 * </pre>
36 *
37 * Or for the one line fans
38 *
39 * <pre>
40 * // create new table
41 * JTable table = new JTable();
42 * table.setTableHeader(new ResizeableTableHeader(table.getColumnModel()));
43 * </pre>
44 *
45 */
46 public class ResizableTableHeader extends JTableHeader implements
47 TableModelListener {
48 private static final long serialVersionUID = 1L;
49
50 /***
51 * If true, auto resizing of columns when the TableModel invokes a
52 * tableChanged event is enabled. The default is false.
53 */
54 private boolean autoResizingEnabled;
55
56 /***
57 * If true, resizing of columns will also take the width of header cells
58 * into account. The default is false.
59 */
60 private boolean includeHeaderWidth;
61
62 /***
63 * Constructs a <code>ResizeableTableHeader</code> with a default
64 * <code>TableColumnModel</code>.
65 *
66 */
67 public ResizableTableHeader() {
68 this(null);
69 }
70
71 /***
72 * Constructs a <code>ResizeableTableHeader</code> which is initialized
73 * with <code>cm</code> as the column model. If <code>cm</code> is
74 * <code>null</code> this method will initialize the table header with a
75 * default <code>TableColumnModel</code>.
76 *
77 * @param cm
78 * the column model for the table
79 */
80 public ResizableTableHeader(TableColumnModel cm) {
81 super(cm);
82 autoResizingEnabled = false;
83 includeHeaderWidth = false;
84 addMouseListener(new ResizingMouseAdapter());
85 }
86
87 /***
88 * Sets the table associated with this header. Also adds a
89 * TableModelListener to the table's TableModel to allow resizing of columns
90 * if data of table changed.
91 *
92 * @param table
93 * the new table
94 */
95 public void setTable(JTable table) {
96 JTable old = this.table;
97 if (table != old) {
98 if (old != null && old.getModel() != null) {
99 old.getModel().removeTableModelListener(this);
100 }
101 if (table != null && table.getModel() != null) {
102 table.getModel().addTableModelListener(this);
103 }
104 }
105 this.table = table;
106 firePropertyChange("table", old, table);
107 }
108
109 /***
110 * Sets whether columns are resized on table model events.
111 *
112 * @param autoResizingEnabled
113 * true if columns are resized automatically
114 * @see #getAutoResizingEnabled
115 */
116 public void setAutoResizingEnabled(boolean autoResizingEnabled) {
117 boolean old = this.autoResizingEnabled;
118 this.autoResizingEnabled = autoResizingEnabled;
119 firePropertyChange("autoResizingEnabled", old, autoResizingEnabled);
120 }
121
122 /***
123 * Returns true if auto resizing is enabled.
124 *
125 * @return the <code>autoResizingEnabled</code> property
126 * @see #setAutoResizingEnabled
127 */
128 public boolean getAutoResizingEnabled() {
129 return autoResizingEnabled;
130 }
131
132 /***
133 * Sets whether the header's width are included on calculation.
134 *
135 * @param includeHeaderWidth
136 * true if the headers are included
137 * @see #getIncludeHeaderWidth
138 */
139 public void setIncludeHeaderWidth(boolean includeHeaderWidth) {
140 boolean old = this.includeHeaderWidth;
141 this.includeHeaderWidth = includeHeaderWidth;
142 firePropertyChange("includeHeaderWidth", old, autoResizingEnabled);
143 }
144
145 /***
146 * Returns true, if the header's width are.
147 *
148 * @return the <code>setIncludeHeaderWidth</code> property
149 * @see #setIncludeHeaderWidth
150 */
151 public boolean getIncludeHeaderWidth() {
152 return includeHeaderWidth;
153 }
154
155 /***
156 * Resizes the given column to fit all its content.
157 *
158 * @param column
159 * The <code> TableColumn</code> to resize.
160 */
161 public void resizeColumn(TableColumn column) {
162 if (column != null) {
163 adjustColumnWidth(column);
164 }
165 }
166
167 /***
168 * Resizes all columns to fit all their content.
169 */
170 public void resizeAllColumns() {
171 for (int col = 0; col < table.getColumnCount(); col++) {
172 TableColumn column = table.getColumnModel().getColumn(col);
173 adjustColumnWidth(column);
174 }
175 }
176
177 /***
178 * Sets preferred width, the minimum and maximum width for a given column.
179 * The minimum and maximum widths are the bounds for the preferred width. If
180 * the preferred width is not in the region of minmum and maximum, it will
181 * be adjusted. Also the user cannot resize columns out of this bounds by
182 * moving the edge or double clicking it. If a width is set to -1, no change
183 * is made to this value.
184 *
185 * @param column
186 * The <code>TableColumn</code> to change.
187 * @param preferredWidth
188 * The preferred width of the column.
189 * @param minWidth
190 * The minimum width of the column.
191 * @param maxWidth
192 * The maximum width of the column.
193 *
194 * @see #setAllColumnWidths(int preferredWidth, int minWidth, int maxWidth)
195 */
196 public void setColumnWidths(TableColumn column, int preferredWidth,
197 int minWidth, int maxWidth) {
198 if (column != null) {
199 if (preferredWidth != -1){
200 column.setPreferredWidth(preferredWidth);
201 }
202 if (minWidth != -1) {
203 column.setMinWidth(minWidth);
204 }
205 if (maxWidth != -1) {
206 column.setMaxWidth(maxWidth);
207 }
208 }
209 }
210
211 /***
212 * Sets preferred width, the minimum and maximum width for all columns. See
213 * setColumnWidth() for further details.
214 *
215 * @param preferredWidth
216 * The preferred width of the column.
217 * @param minWidth
218 * The minimum width of the column.
219 * @param maxWidth
220 * The maximum width of the column.
221 *
222 * @see #setColumnWidths(javax.swing.table.TableColumn column,int
223 * preferredWidth, int minWidth, int maxWidth)
224 */
225 public void setAllColumnWidths(int preferredWidth, int minWidth,
226 int maxWidth) {
227 for (int col = 0; col < table.getColumnCount(); col++) {
228 TableColumn column = table.getColumnModel().getColumn(col);
229 setColumnWidths(column, preferredWidth, minWidth, maxWidth);
230 }
231 }
232
233 /***
234 * Listen for table model events from tablemodel. If new data is inserted,
235 * only determine the width of new cell and adjust column width, if
236 * necessary. If data is deleted or updated, call
237 * <code>resizeAllColumns()</code>.
238 *
239 * @param e
240 * The <code>TableModelEvent</code>
241 */
242 public void tableChanged(TableModelEvent e) {
243 if (getAutoResizingEnabled()) {
244 if (e.getType() == TableModelEvent.DELETE) {
245 resizeAllColumns();
246 } else {
247
248
249 for (int col = 0; col < table.getColumnCount(); col++) {
250 TableColumn column = table.getColumnModel().getColumn(col);
251 if (canResize(column)) {
252 int width = column.getPreferredWidth();
253 for (int row = e.getFirstRow(); row <= e.getLastRow(); row++) {
254 TableCellRenderer renderer = table.getCellRenderer(
255 row, col);
256 Component comp = renderer
257 .getTableCellRendererComponent(table, table
258 .getValueAt(row, col), false,
259 false, row, col);
260 width = Math.max(width,
261 comp.getPreferredSize().width
262 + table.getColumnModel()
263 .getColumnMargin());
264 }
265 column.setPreferredWidth(width);
266 }
267 }
268 }
269
270 }
271
272 }
273
274 /***
275 * Adjust the width of a column to fit all cells.
276 *
277 * @param column
278 * The column to adjust.
279 */
280 private void adjustColumnWidth(TableColumn column) {
281
282 int width = 0;
283 int col = table.convertColumnIndexToView(column.getModelIndex());
284
285
286 if (includeHeaderWidth) {
287 TableCellRenderer headerRenderer = column.getHeaderRenderer();
288 if (headerRenderer == null) {
289 headerRenderer = table.getTableHeader().getDefaultRenderer();
290 }
291 Component headerComp = headerRenderer
292 .getTableCellRendererComponent(table, column
293 .getHeaderValue(), false, false, 0, col);
294 width = Math.max(width, headerComp.getPreferredSize().width);
295 }
296
297
298 Rectangle visibleRect = this.getTable().getVisibleRect();
299 int firstRow = this.getTable().rowAtPoint(
300 new Point((int) visibleRect.getMinX(), (int) visibleRect
301 .getMinY()));
302 int lastRow = this.getTable().rowAtPoint(
303 new Point((int) visibleRect.getMaxX(), (int) visibleRect
304 .getMaxY()));
305
306 for (int row = firstRow; row <= lastRow; row++) {
307 TableCellRenderer renderer = table.getCellRenderer(row, col);
308 Component comp = renderer.getTableCellRendererComponent(table,
309 table.getValueAt(row, col), false, false, row, col);
310 width = Math.max(width, comp.getPreferredSize().width);
311 }
312
313 width += table.getColumnModel().getColumnMargin();
314
315 column.setPreferredWidth(width);
316 }
317
318 /***
319 * Returns true, if resizing for the tableheader is allowed and if the
320 * column is resizable.
321 *
322 * @param column
323 * The column to check
324 * @return True, if the column is resizeable, otherwise false.
325 */
326 private boolean canResize(TableColumn column) {
327 return (column != null) && getResizingAllowed()
328 && column.getResizable();
329 }
330
331 /***
332 * Gets the the column that will be resized for a specific point. This
333 * method only return a column, if the point is within the last 3 pixels + 3
334 * pixels of next column. Otherwise it returns null.
335 *
336 * @param p
337 * The point to check, if we are in the resizing area.
338 *
339 * @return The resizing column, if the point is in the resizing area,
340 * otherwise null.
341 *
342 * @see #getResizingColumn(java.awt.Point p,int column)
343 */
344 private TableColumn getResizingColumn(Point p) {
345 return getResizingColumn(p, columnAtPoint(p));
346 }
347
348 /***
349 * @see #getResizingColumn(java.awt.Point p)
350 */
351 private TableColumn getResizingColumn(Point p, int column) {
352 if (column == -1) {
353 return null;
354 }
355 Rectangle r = getHeaderRect(column);
356 r.grow(-3, 0);
357 if (r.contains(p)) {
358 return null;
359 }
360 int midPoint = r.x + r.width / 2;
361 int columnIndex;
362 if (getComponentOrientation().isLeftToRight()) {
363 if (p.x < midPoint) {
364 columnIndex = column - 1;
365 } else {
366 columnIndex = column;
367 }
368 } else {
369 if (p.x < midPoint) {
370 columnIndex = column;
371 } else {
372 columnIndex = column - 1;
373 }
374 }
375 if (columnIndex == -1) {
376 return null;
377 }
378 return getColumnModel().getColumn(columnIndex);
379 }
380
381 /***
382 * Listener that will resize a column, if a double click in a resizing area
383 * is performed.
384 */
385 private class ResizingMouseAdapter extends MouseAdapter {
386 public void mouseClicked(MouseEvent me) {
387 if (me.getClickCount() == 2 && me.getButton() == MouseEvent.BUTTON1) {
388 TableColumn resizingColumn = getResizingColumn(me.getPoint());
389 if (canResize(resizingColumn)) {
390 adjustColumnWidth(resizingColumn);
391 }
392 }
393 }
394 }
395
396 public String getToolTipText(MouseEvent event) {
397 String tip = null;
398 Point p = event.getPoint();
399 int index = columnModel.getColumnIndexAtX(p.x);
400 Object value = columnModel.getColumn(index).getHeaderValue();
401 return value.toString();
402 }
403
404
405 }