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    }