1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.wotonomy.ui.swing.components;
20
21 import java.awt.Color;
22 import java.awt.Component;
23 import java.awt.event.FocusEvent;
24 import java.awt.event.FocusListener;
25 import java.awt.event.KeyEvent;
26 import java.awt.event.KeyListener;
27 import java.io.Serializable;
28 import java.text.Format;
29 import java.util.ArrayList;
30 import java.util.EventObject;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Vector;
34
35 import javax.swing.JTable;
36 import javax.swing.JTextField;
37 import javax.swing.border.LineBorder;
38 import javax.swing.event.CellEditorListener;
39 import javax.swing.event.ChangeEvent;
40 import javax.swing.table.TableCellEditor;
41
42 /***
43 * A table cell editor customized for keyboard navigation, much like
44 * working with a spreadsheet. The default cell editor unfortunately
45 * does none of these things:
46 * <ul>
47 * <li> Selects text on start of editing.
48 * <li> Up and down keys move edit cell up and down.
49 * <li> Right and left keys move cell when selection caret is at end of text.
50 * <li> Escape cancels editing.
51 * <li> Enter commits edit.
52 * <li> Edits are properly committed on lost focus.
53 * <li> Tab and shift-tab work as expected.
54 * <li> Cell selection moves with the edit cell.
55 * </ul>
56 *
57 * @author michael@mpowers.net
58 * @author $Author: cgruber $
59 * @version $Revision: 904 $
60 * $Date: 2006-02-18 23:19:05 +0000 (Sat, 18 Feb 2006) $
61 */
62 public class KeyableCellEditor implements TableCellEditor, FocusListener,
63 KeyListener, Serializable
64 {
65 List listeners;
66 JTextField textField;
67 Object lastValue;
68 Format currentFormat;
69
70 JTable table;
71
72 /***
73 * Default constructor - a standard JTextField will be used for editing.
74 */
75 public KeyableCellEditor()
76 {
77 this( (JTextField) null );
78 }
79
80 /***
81 * Constructor specifying a type of JTextField to be used for editing.
82 * The JTextField will have its border replaced with a black line border.
83 * @param aTextField A JTextField or subclass for editing values.
84 */
85 public KeyableCellEditor( JTextField aTextField )
86 {
87 listeners = new Vector();
88 lastValue = null;
89
90
91 textField = aTextField;
92 if ( textField == null )
93 {
94 textField = new JTextField();
95 }
96
97 textField.setBorder(new LineBorder(Color.black));
98
99
100 textField.addKeyListener( this );
101
102
103 textField.addFocusListener( this );
104 }
105
106 public Component getTableCellEditorComponent(JTable table,
107 Object value,
108 boolean isSelected,
109 int row,
110 int column)
111 {
112 this.table = table;
113 table.removeKeyListener( this );
114 table.addKeyListener( this );
115 return getEditorComponent( value );
116 }
117
118 protected Component getEditorComponent( Object value )
119 {
120 if ( value != null )
121 {
122 textField.setText( value.toString() );
123 }
124 else
125 {
126 textField.setText( "" );
127 }
128
129 if ( value instanceof Number )
130 {
131 textField.setHorizontalAlignment(JTextField.RIGHT);
132 }
133 else
134 {
135 textField.setHorizontalAlignment(JTextField.LEFT);
136 }
137
138
139 lastValue = value;
140
141
142 textField.selectAll();
143 textField.requestFocus();
144
145 return textField;
146 }
147
148 public Object getCellEditorValue()
149 {
150 return lastValue;
151 }
152
153 public boolean isCellEditable(EventObject anEvent)
154 {
155
156
157 if ( anEvent == null )
158 {
159 textField.setText("");
160 textField.requestFocus();
161 return true;
162 }
163
164 return true;
165 }
166
167 public boolean shouldSelectCell(EventObject anEvent)
168 {
169
170
171
172 if ( anEvent instanceof KeyEvent )
173 {
174 textField.setText("");
175 textField.requestFocus();
176 return true;
177 }
178
179
180 textField.selectAll();
181 textField.requestFocus();
182
183 return true;
184 }
185
186 public boolean stopCellEditing()
187 {
188 lastValue = textField.getText();
189 fireEditingStopped();
190 table.removeKeyListener( this );
191 return true;
192 }
193
194 public void cancelCellEditing()
195 {
196 fireEditingCanceled();
197 table.removeKeyListener( this );
198 }
199
200 public void addCellEditorListener(CellEditorListener l)
201 {
202 listeners.add( l );
203 }
204
205 public void removeCellEditorListener(CellEditorListener l)
206 {
207 listeners.remove( l );
208 }
209
210 protected void fireEditingCanceled()
211 {
212 ChangeEvent event = new ChangeEvent( this );
213 Iterator it = new ArrayList( listeners ).iterator();
214 while ( it.hasNext() )
215 {
216 ((CellEditorListener)it.next()).editingCanceled( event );
217 }
218 }
219
220 protected void fireEditingStopped()
221 {
222 ChangeEvent event = new ChangeEvent( this );
223 Iterator it = new ArrayList( listeners ).iterator();
224 while ( it.hasNext() )
225 {
226 ((CellEditorListener)it.next()).editingStopped( event );
227 }
228 }
229
230 protected void onEnterKey()
231 {
232 stopCellEditing();
233 }
234
235 protected void onEscapeKey()
236 {
237 cancelCellEditing();
238 }
239
240 protected void moveEditCell( int dRow, int dCol )
241 {
242 if ( table == null ) return;
243 int row = table.getSelectedRow() + dRow;
244 int col = table.getSelectedColumn() + dCol;
245
246 row = Math.max( 0, row );
247 row = Math.min( row, table.getRowCount() - 1 );
248 col = Math.max( 0, col );
249 col = Math.min( col, table.getColumnCount() - 1 );
250
251 stopCellEditing();
252 table.setRowSelectionInterval( row, row );
253 table.setColumnSelectionInterval( col, col );
254 table.editCellAt( row, col );
255 textField.selectAll();
256 textField.requestFocus();
257 }
258
259
260
261 public void keyTyped(KeyEvent e)
262 {
263 }
264
265 public void keyPressed(KeyEvent e)
266 {
267
268
269
270 int keyCode = e.getKeyCode();
271 if ( keyCode == KeyEvent.VK_LEFT )
272 {
273 if ( textField.getSelectionStart() == 0 )
274 {
275 moveEditCell( 0, -1 );
276 e.consume();
277 return;
278 }
279 }
280 if ( keyCode == KeyEvent.VK_RIGHT )
281 {
282 if ( textField.getSelectionEnd() == textField.getText().length() )
283 {
284 moveEditCell( 0, 1 );
285 e.consume();
286 return;
287 }
288 }
289 if ( keyCode == KeyEvent.VK_UP )
290 {
291 moveEditCell( -1, 0 );
292 e.consume();
293 return;
294 }
295 if ( keyCode == KeyEvent.VK_DOWN )
296 {
297 moveEditCell( 1, 0 );
298 e.consume();
299 return;
300 }
301 }
302
303 public void keyReleased(KeyEvent e)
304 {
305
306
307
308 int keyCode = e.getKeyCode();
309 if ( keyCode == KeyEvent.VK_ENTER )
310 {
311 onEnterKey();
312 return;
313 }
314 if ( keyCode == KeyEvent.VK_ESCAPE )
315 {
316 onEscapeKey();
317 return;
318 }
319
320
321 if ( keyCode == KeyEvent.VK_TAB )
322 {
323 if ( e.isShiftDown() )
324 {
325 moveEditCell( 0, -1 );
326 }
327 else
328 {
329 moveEditCell( 0, 1 );
330 }
331 e.consume();
332 return;
333 }
334
335 }
336
337
338
339 public void focusGained(FocusEvent e)
340 {
341 }
342
343 public void focusLost(FocusEvent e)
344 {
345 stopCellEditing();
346 }
347
348 }
349
350