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 }