001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.table;
017
018 import java.awt.Component;
019 import java.awt.Dimension;
020 import java.awt.Point;
021 import java.awt.Rectangle;
022 import java.util.Date;
023
024 import javax.swing.JLabel;
025 import javax.swing.JTable;
026 import javax.swing.JViewport;
027 import javax.swing.table.TableCellRenderer;
028 import javax.swing.table.TableColumn;
029 import javax.swing.table.TableModel;
030
031 import org.springframework.core.enums.LetterCodedLabeledEnum;
032 import org.springframework.core.enums.ShortCodedLabeledEnum;
033 import org.springframework.core.enums.StringCodedLabeledEnum;
034 import org.springframework.richclient.core.UIConstants;
035 import org.springframework.richclient.table.renderer.BeanTableCellRenderer;
036 import org.springframework.richclient.table.renderer.BooleanTableCellRenderer;
037 import org.springframework.richclient.table.renderer.DateTimeTableCellRenderer;
038 import org.springframework.richclient.table.renderer.LabeledEnumTableCellRenderer;
039 import org.springframework.richclient.util.WindowUtils;
040
041 /**
042 * @author Keith Donald
043 */
044 public class TableUtils {
045
046 public static void scrollToRow(JTable table, int row) {
047 if (!(table.getParent() instanceof JViewport)) {
048 return;
049 }
050 JViewport viewport = (JViewport)table.getParent();
051 // This rectangle is relative to the table where the
052 // northwest corner of cell (0,0) is always (0,0).
053 Rectangle rect = table.getCellRect(row, 0, true);
054 // The location of the viewport relative to the table
055 Point pt = viewport.getViewPosition();
056 // Translate the cell location so that it is relative
057 // to the view, assuming the northwest corner of the
058 // view is (0,0)
059 rect.setLocation(rect.x - pt.x, rect.y - pt.y);
060 // Scroll the area into view
061 viewport.scrollRectToVisible(rect);
062 }
063
064 public static JTable createStandardSortableTable(TableModel tableModel) {
065 JTable table = new JTable(tableModel);
066 table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
067 installDefaultRenderers(table);
068 attachSorter(table);
069 sizeColumnsToFitRowData(table);
070 return table;
071 }
072
073 public static JTable attachSorter(JTable table) {
074 TableModel tableModel = table.getModel();
075 ShuttleSortableTableModel sortedModel = new ShuttleSortableTableModel(tableModel);
076 table.setAutoCreateColumnsFromModel(true);
077 table.setModel(sortedModel);
078 TableSortIndicator sortIndicator = new TableSortIndicator(table);
079 new SortTableCommand(table, sortIndicator.getColumnSortList());
080 return table;
081 }
082
083 public static void installDefaultRenderers(JTable table) {
084 BeanTableCellRenderer beanRenderer = new BeanTableCellRenderer();
085 table.setDefaultRenderer(Object.class, beanRenderer);
086 LabeledEnumTableCellRenderer er = new LabeledEnumTableCellRenderer();
087 table.setDefaultRenderer(ShortCodedLabeledEnum.class, er);
088 table.setDefaultRenderer(StringCodedLabeledEnum.class, er);
089 table.setDefaultRenderer(LetterCodedLabeledEnum.class, er);
090 table.setDefaultRenderer(Date.class, new DateTimeTableCellRenderer());
091 table.setDefaultRenderer(Boolean.class, new BooleanTableCellRenderer());
092 }
093
094 public static void setPreferredColumnWidths(JTable table) {
095 for (int i = 0; i < table.getColumnCount(); i++) {
096 TableColumn col = table.getColumnModel().getColumn(i);
097 int w = calculatePreferredColumnWidth(table, col);
098 col.setPreferredWidth(w);
099 col.setWidth(w);
100 }
101 }
102
103 /**
104 * Calculates the preferred width of a table column based on the header.
105 *
106 * @param table
107 * @return the preferred table width
108 */
109 public static int calculatePreferredColumnWidth(JTable table, TableColumn col) {
110 TableModel model = table.getModel();
111 String colName = model.getColumnName(col.getModelIndex());
112 return new JLabel(colName).getPreferredSize().width + UIConstants.THREE_SPACES + UIConstants.TWO_SPACES;
113 }
114
115 /**
116 * Returns the innermost table model associated with this table; if layers
117 * of table model filters are wrapping it.
118 */
119 public static TableModel getUnfilteredTableModel(JTable table) {
120 return getUnfilteredTableModel(table.getModel());
121 }
122
123 /**
124 * resizes the column widths to optimally fit the row data.
125 * <p>
126 * this method only tests the first row (if it exists)
127 * @param table the table whose columns should be resized, not null
128 */
129 public static void sizeColumnsToFitRowData(JTable table) {
130 sizeColumnsToFitRowData(table, 1);
131 }
132
133 /**
134 * resizes the column widths to optimally fit the row data.
135 *
136 * @param table
137 * the table whose columns should be resized, not null
138 * @param maxNumberOfRows
139 * specifies the maximum number of rows to evaluate the column widths. If it is lower or equals 0 all rows
140 * will be evaluated
141 */
142 public static void sizeColumnsToFitRowData(JTable table, int maxNumberOfRows) {
143 if (table.getRowCount() > 0) {
144 int rowSize = maxNumberOfRows <= 0 ? table.getRowCount() : Math
145 .min(maxNumberOfRows, table.getRowCount());
146 for (int col = 0, colSize = table.getColumnCount(); col < colSize; col++) {
147 int width = 0;
148 TableColumn column = table.getColumnModel().getColumn(col);
149 TableCellRenderer r = table.getColumnModel().getColumn(col).getCellRenderer();
150 for (int row = 0; row < rowSize; row++) {
151 Object val = table.getValueAt(row, col);
152 if (r == null) {
153 if (val != null) {
154 r = table.getDefaultRenderer(val.getClass());
155 }
156 }
157 if (r != null) {
158 Component c = r
159 .getTableCellRendererComponent(table, val, false, false, row, col);
160 int cWidth = c.getPreferredSize().width;
161 if(cWidth > width) {
162 width = cWidth;
163 }
164 }
165 }
166 column.setPreferredWidth(width + UIConstants.ONE_SPACE);
167 column.setWidth(column.getPreferredWidth());
168 }
169 }
170 int width = Math.min(table.getColumnModel().getTotalColumnWidth(), (int)(WindowUtils.getScreenWidth() * .75));
171 table.setPreferredScrollableViewportSize(new Dimension(width, 300));
172 }
173
174 public static TableModel getUnfilteredTableModel(TableModel tableModel) {
175 if (tableModel instanceof AbstractTableModelFilter) {
176 return getUnfilteredTableModel(((AbstractTableModelFilter)tableModel).getFilteredModel());
177 }
178 return tableModel;
179 }
180
181 /**
182 * Workaround for a very annoying bug in jtable where an editing cell value
183 * does not get committed on focus lost.
184 *
185 * @param table
186 */
187 public static void stopCellEditing(JTable table) {
188 int row = table.getEditingRow();
189 int col = table.getEditingColumn();
190 if (table.isEditing()) {
191 if (row < table.getRowCount()) {
192 table.getCellEditor(row, col).stopCellEditing();
193 }
194 }
195 }
196 }