Coverage Report - net.wotonomy.ui.swing.components.KeyableCellEditor
 
Classes in this File Line Coverage Branch Coverage Complexity
KeyableCellEditor
0% 
0% 
2.238
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Blacksmith, Inc.
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 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  0
         this( (JTextField) null );
 78  0
     }
 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  0
     public KeyableCellEditor( JTextField aTextField )
 86  0
     {
 87  0
         listeners = new Vector();
 88  0
         lastValue = null;
 89  
 
 90  
         // default to stock JTextField
 91  0
         textField = aTextField;
 92  0
         if ( textField == null )
 93  
         {
 94  0
             textField = new JTextField();
 95  
         }
 96  
 
 97  0
         textField.setBorder(new LineBorder(Color.black));
 98  
 
 99  
         // handle arrow keys while caret is showing
 100  0
         textField.addKeyListener( this );
 101  
 
 102  
         // handle lost focus
 103  0
         textField.addFocusListener( this );
 104  0
     }
 105  
 
 106  
     public Component getTableCellEditorComponent(JTable table,
 107  
                                                  Object value,
 108  
                                                  boolean isSelected,
 109  
                                                  int row,
 110  
                                                  int column)
 111  
     {
 112  0
         this.table = table;
 113  0
         table.removeKeyListener( this ); // if any
 114  0
         table.addKeyListener( this );
 115  0
         return getEditorComponent( value );
 116  
     }
 117  
 
 118  
     protected Component getEditorComponent( Object value )
 119  
     {
 120  0
         if ( value != null )
 121  
         {
 122  0
             textField.setText( value.toString() );
 123  0
         }
 124  
         else
 125  
         {
 126  0
             textField.setText( "" );
 127  
         }
 128  
 
 129  0
         if ( value instanceof Number )
 130  
         {
 131  0
             textField.setHorizontalAlignment(JTextField.RIGHT);
 132  0
         }
 133  
         else
 134  
         {
 135  0
             textField.setHorizontalAlignment(JTextField.LEFT);
 136  
         }
 137  
 
 138  
         // remember original value
 139  0
         lastValue = value;
 140  
 
 141  
         // select all text and get focus
 142  0
         textField.selectAll();
 143  0
         textField.requestFocus();
 144  
 
 145  0
         return textField;
 146  
     }
 147  
 
 148  
     public Object getCellEditorValue()
 149  
     {
 150  0
         return lastValue;
 151  
     }
 152  
 
 153  
     public boolean isCellEditable(EventObject anEvent)
 154  
     {
 155  
         // key events should replace the selection
 156  
         // NOTE: For whatever reason, key events trigger result in a null parameter
 157  0
         if ( anEvent == null )
 158  
         {
 159  0
             textField.setText("");
 160  0
             textField.requestFocus();
 161  0
             return true;
 162  
         }
 163  
 
 164  0
         return true;
 165  
     }
 166  
 
 167  
     public boolean shouldSelectCell(EventObject anEvent)
 168  
     { // System.out.println( "KeyableCellEditor.shouldSelectCell: " + anEvent );
 169  
 
 170  
         // key events should replace the selection
 171  
         // NOTE: For whatever reason, key events are not generated
 172  0
         if ( anEvent instanceof KeyEvent )
 173  
         {
 174  0
             textField.setText("");
 175  0
             textField.requestFocus();
 176  0
             return true;
 177  
         }
 178  
 
 179  
         // otherwise, select all text and continue
 180  0
         textField.selectAll();
 181  0
         textField.requestFocus();
 182  
 
 183  0
         return true;
 184  
     }
 185  
 
 186  
     public boolean stopCellEditing()
 187  
     {
 188  0
         lastValue = textField.getText();
 189  0
         fireEditingStopped();
 190  0
         table.removeKeyListener( this ); // if any
 191  0
         return true;
 192  
     }
 193  
 
 194  
     public void cancelCellEditing()
 195  
     {
 196  0
         fireEditingCanceled();
 197  0
         table.removeKeyListener( this ); // if any
 198  0
     }
 199  
 
 200  
     public void addCellEditorListener(CellEditorListener l)
 201  
     {
 202  0
         listeners.add( l );
 203  0
     }
 204  
 
 205  
     public void removeCellEditorListener(CellEditorListener l)
 206  
     {
 207  0
         listeners.remove( l );
 208  0
     }
 209  
 
 210  
     protected void fireEditingCanceled()
 211  
     {
 212  0
         ChangeEvent event = new ChangeEvent( this );
 213  0
         Iterator it = new ArrayList( listeners ).iterator(); // copy to prevent modification exception
 214  0
         while ( it.hasNext() )
 215  
         {
 216  0
             ((CellEditorListener)it.next()).editingCanceled( event );
 217  0
         }
 218  0
     }
 219  
 
 220  
     protected void fireEditingStopped()
 221  
     {
 222  0
         ChangeEvent event = new ChangeEvent( this );
 223  0
         Iterator it = new ArrayList( listeners ).iterator(); // copy to prevent modification exception
 224  0
         while ( it.hasNext() )
 225  
         {
 226  0
             ((CellEditorListener)it.next()).editingStopped( event );
 227  0
         }
 228  0
     }
 229  
 
 230  
     protected void onEnterKey()
 231  
     {
 232  0
         stopCellEditing();
 233  0
     }
 234  
 
 235  
     protected void onEscapeKey()
 236  
     {
 237  0
         cancelCellEditing();
 238  0
     }
 239  
 
 240  
     protected void moveEditCell( int dRow, int dCol )
 241  
     {
 242  0
         if ( table == null ) return;
 243  0
         int row = table.getSelectedRow() + dRow;
 244  0
         int col = table.getSelectedColumn() + dCol;
 245  
 
 246  0
         row = Math.max( 0, row );
 247  0
         row = Math.min( row, table.getRowCount() - 1 );
 248  0
         col = Math.max( 0, col );
 249  0
         col = Math.min( col, table.getColumnCount() - 1 );
 250  
 
 251  0
         stopCellEditing();
 252  0
         table.setRowSelectionInterval( row, row );
 253  0
         table.setColumnSelectionInterval( col, col );
 254  0
         table.editCellAt( row, col );
 255  0
         textField.selectAll();
 256  0
         textField.requestFocus();
 257  0
     }
 258  
 
 259  
     // interface KeyListener
 260  
 
 261  
     public void keyTyped(KeyEvent e)
 262  
     { // System.out.println( "KeyableCellEditor.keyTyped: " + KeyEvent.getKeyText( e.getKeyCode() ) );
 263  0
     }
 264  
 
 265  
     public void keyPressed(KeyEvent e)
 266  
     { // System.out.println( "KeyableCellEditor.keyPressed: " + KeyEvent.getKeyText( e.getKeyCode() ) );
 267  
 
 268  
         // catch LEFT and RIGHT here before JTextField consumes them
 269  
 
 270  0
         int keyCode = e.getKeyCode();
 271  0
         if ( keyCode == KeyEvent.VK_LEFT )
 272  
         {
 273  0
             if ( textField.getSelectionStart() == 0 )
 274  
             {
 275  0
                 moveEditCell( 0, -1 );
 276  0
                 e.consume();
 277  0
                 return;
 278  
             }
 279  
         }
 280  0
         if ( keyCode == KeyEvent.VK_RIGHT )
 281  
         {
 282  0
             if ( textField.getSelectionEnd() == textField.getText().length() )
 283  
             {
 284  0
                 moveEditCell( 0, 1 );
 285  0
                 e.consume();
 286  0
                 return;
 287  
             }
 288  
         }
 289  0
         if ( keyCode == KeyEvent.VK_UP )
 290  
         {
 291  0
             moveEditCell( -1, 0 );
 292  0
             e.consume();
 293  0
             return;
 294  
         }
 295  0
         if ( keyCode == KeyEvent.VK_DOWN )
 296  
         {
 297  0
             moveEditCell( 1, 0 );
 298  0
             e.consume();
 299  0
             return;
 300  
         }
 301  0
     }
 302  
 
 303  
     public void keyReleased(KeyEvent e)
 304  
     { // System.out.println( "KeyableCellEditor.keyReleased: " + KeyEvent.getKeyText( e.getKeyCode() ) );
 305  
 
 306  
         // catch ENTER here to allow JTextField to process it as well
 307  
 
 308  0
         int keyCode = e.getKeyCode();
 309  0
         if ( keyCode == KeyEvent.VK_ENTER )
 310  
         {
 311  0
             onEnterKey();
 312  0
             return;
 313  
         }
 314  0
         if ( keyCode == KeyEvent.VK_ESCAPE )
 315  
         {
 316  0
             onEscapeKey();
 317  0
             return;
 318  
         }
 319  
 
 320  
         // tabs are apparently only received on key release
 321  0
         if ( keyCode == KeyEvent.VK_TAB )
 322  
         {
 323  0
             if ( e.isShiftDown() )
 324  
             {
 325  0
                 moveEditCell( 0, -1 );
 326  0
             }
 327  
             else
 328  
             {
 329  0
                 moveEditCell( 0, 1 );
 330  
             }
 331  0
             e.consume();
 332  0
             return;
 333  
         }
 334  
 
 335  0
     }
 336  
 
 337  
     // interface FocusListener
 338  
 
 339  
     public void focusGained(FocusEvent e)
 340  
     { // System.out.println( "focusGained: " );        
 341  0
     }
 342  
 
 343  
     public void focusLost(FocusEvent e)
 344  
     { // System.out.println( "focusLost: " );        
 345  0
         stopCellEditing();
 346  0
     }
 347  
 
 348  
 }
 349  
 
 350