1 | // Copyright 2004-2007 Jean-Francois Poilpret |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | package net.sourceforge.hivegui.table; |
16 | |
17 | import java.awt.Dimension; |
18 | import java.awt.event.KeyEvent; |
19 | |
20 | import javax.swing.Action; |
21 | import javax.swing.InputMap; |
22 | import javax.swing.JTable; |
23 | import javax.swing.KeyStroke; |
24 | import javax.swing.table.TableCellRenderer; |
25 | import javax.swing.table.TableColumn; |
26 | import javax.swing.table.TableColumnModel; |
27 | |
28 | import org.jdesktop.application.ApplicationContext; |
29 | import org.jdesktop.application.Resource; |
30 | import org.jdesktop.application.ResourceMap; |
31 | |
32 | import net.sourceforge.hiveevents.Consumer; |
33 | import net.sourceforge.hivegui.application.ApplicationContextHolder; |
34 | import net.sourceforge.hivegui.component.RendererInformation; |
35 | |
36 | /** |
37 | * @author Jean-Francois Poilpret |
38 | */ |
39 | public class BeanTable<T> extends JTable |
40 | { |
41 | public BeanTable( DataListModel<T> listModel, |
42 | TableContribution contrib, |
43 | ApplicationContextHolder holder) |
44 | { |
45 | this(contrib.getName(), listModel, contrib, holder); |
46 | } |
47 | |
48 | public BeanTable( String name, |
49 | DataListModel<T> listModel, |
50 | TableContribution contrib, |
51 | ApplicationContextHolder holder) |
52 | { |
53 | setName(name); |
54 | |
55 | // Build BeanTableModel + ColumnModel |
56 | ColumnContribution[] columns = contrib.getColumns(); |
57 | BeanDataModel<T> dataModel = new BeanDataModel<T>(columns); |
58 | _columnNames = new String[dataModel.getColumnCount()]; |
59 | |
60 | // The order in which the listeners are added is highly important: |
61 | // 1- Listener called first: remember current selection |
62 | listModel.addDataListModelListener(new DataListModelAdapter() |
63 | { |
64 | @Override public void rowModified(int source, int destination) |
65 | { |
66 | int[] selection = getSelectedRows(); |
67 | _reselectRow = -1; |
68 | for (int i = 0; i < selection.length; i++) |
69 | { |
70 | if (selection[i] == source) |
71 | { |
72 | _reselectRow = destination; |
73 | break; |
74 | } |
75 | } |
76 | } |
77 | }); |
78 | // 2- BeanTableModel Inner listener called then (update Swing models) |
79 | // this listener will actually fire the change to the table |
80 | setModel(new BeanTableModel<T>(listModel, dataModel)); |
81 | // 3- Listener called last: restore previous selection |
82 | listModel.addDataListModelListener(new DataListModelAdapter() |
83 | { |
84 | @Override public void rowModified(int source, int destination) |
85 | { |
86 | if (_reselectRow != -1) |
87 | { |
88 | addRowSelectionInterval(_reselectRow, _reselectRow); |
89 | } |
90 | } |
91 | }); |
92 | |
93 | if (contrib.isAutoSelectNewRow()) |
94 | { |
95 | listModel.addDataListModelListener(new DataListModelAdapter() |
96 | { |
97 | @Override public void rowsAdded(int begin, int end) |
98 | { |
99 | addRowSelectionInterval(begin, end); |
100 | } |
101 | }); |
102 | } |
103 | |
104 | _selectHandler = new TableSelectionHandler<T>(this, contrib); |
105 | _popupHandler = new TablePopupHandler<T>(this, contrib); |
106 | |
107 | setupColumns(columns, holder.getContext()); |
108 | |
109 | // Remove mapping of Escape key |
110 | InputMap map = getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); |
111 | map.getParent().remove(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0)); |
112 | } |
113 | |
114 | public void setVisibleRowCount(int count) |
115 | { |
116 | Dimension size = getPreferredScrollableViewportSize(); |
117 | setPreferredScrollableViewportSize( |
118 | new Dimension(size.width, count * getRowHeight())); |
119 | } |
120 | |
121 | private void setupColumns( ColumnContribution[] columns, |
122 | ApplicationContext context) |
123 | { |
124 | // First get the i18n column names |
125 | ResourceMap map = context.getResourceMap(getClass(), BeanTable.class); |
126 | map.injectFields(this, getName() + "-header"); |
127 | |
128 | // Then setup each column settings |
129 | int height = 0; |
130 | TableColumnModel colModel = getColumnModel(); |
131 | for (int i = 0; i < columns.length; i++) |
132 | { |
133 | ColumnContribution col = columns[i]; |
134 | TableColumn column = colModel.getColumn(i); |
135 | if (col.getMinWidth() >= 0) |
136 | { |
137 | column.setMinWidth(col.getMinWidth()); |
138 | } |
139 | if (col.getMaxWidth() >= 0) |
140 | { |
141 | column.setMaxWidth(col.getMaxWidth()); |
142 | } |
143 | if (col.getPreferredWidth() >= 0) |
144 | { |
145 | column.setPreferredWidth(col.getPreferredWidth()); |
146 | } |
147 | if (col.getRenderer() != null) |
148 | { |
149 | TableCellRenderer renderer = col.getRenderer(); |
150 | column.setCellRenderer(renderer); |
151 | if (renderer instanceof RendererInformation) |
152 | { |
153 | int cellHeight = ((RendererInformation) renderer).getCellHeight(); |
154 | if (cellHeight > height) |
155 | { |
156 | height = cellHeight; |
157 | } |
158 | } |
159 | } |
160 | if (_columnNames[i] != null) |
161 | { |
162 | column.setHeaderValue(_columnNames[i]); |
163 | } |
164 | } |
165 | if (height > 0) |
166 | { |
167 | // Add 2+2 for extra space up and below |
168 | setRowHeight(height + EXTRA_HEIGHT); |
169 | } |
170 | } |
171 | |
172 | public void addSelectionConsumer(int priority, Consumer consumer) |
173 | { |
174 | _selectHandler.addSelectionConsumer(priority, consumer); |
175 | } |
176 | |
177 | public void setDoubleClickCommand(Action command) |
178 | { |
179 | _popupHandler.setDoubleClickCommand(command); |
180 | } |
181 | |
182 | public Action getDoubleClickCommand() |
183 | { |
184 | return _popupHandler.getDoubleClickCommand(); |
185 | } |
186 | |
187 | // Overridden to check if put inside a JScrollPane and put a MouseListener to it |
188 | @Override public void addNotify() |
189 | { |
190 | super.addNotify(); |
191 | _popupHandler.addNotify(getParent()); |
192 | } |
193 | |
194 | // Overridden to remove MouseListener from it |
195 | @Override public void removeNotify() |
196 | { |
197 | _popupHandler.removeNotify(getParent()); |
198 | super.removeNotify(); |
199 | } |
200 | |
201 | @SuppressWarnings("unchecked") |
202 | public DataListModel<T> getDataListModel() |
203 | { |
204 | return ((BeanTableModel<T>) getModel()).getModel(); |
205 | } |
206 | |
207 | @SuppressWarnings("unchecked") |
208 | public void setDataListModel(DataListModel<T> model) |
209 | { |
210 | ((BeanTableModel<T>) getModel()).setModel(model); |
211 | } |
212 | |
213 | private final TableSelectionHandler<T> _selectHandler; |
214 | private final TablePopupHandler<T> _popupHandler; |
215 | private int _reselectRow; |
216 | |
217 | @Resource(key = "column") |
218 | final private String[] _columnNames; |
219 | |
220 | static final private int EXTRA_HEIGHT = 4; |
221 | } |