/* Copyright (c) 1999 Computer Engineering and Communication Networks Lab (TIK) Swiss Federal Institute of Technology (ETH) Zurich, Switzerland All rights reserved. Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this software and its documentation for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software. IN NO EVENT SHALL THE TIK OR THE ETH ZURICH BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE TIK OR THE ETH ZURICH HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE TIK AND THE ETH ZURICH SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND TIK AND THE ETH ZURICH HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. */ package vijay; import java.awt.Component; import java.awt.Font; import java.awt.FontMetrics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.io.Serializable; import java.util.EventObject; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextArea; import javax.swing.JTree; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.CellEditorListener; import javax.swing.event.ChangeEvent; import javax.swing.event.EventListenerList; import javax.swing.table.TableCellEditor; import javax.swing.tree.TreeCellEditor; /** * The class for editing individual cells in a JTable as multiline text. * * It contains a hack where the JTextArea is rebuilt upon every edit. * This was necessary as it would not otherwise display properly * *

* * * * * * * * * * *
Modification History
AuthorDateComments
Vijay Aravamudhan12/16/2001 *
    *
  • Modified so as to update the row height during editing and then reset * it back to font height after editing is over.
  • *
  • Added javadoc comments
  • *
*/ public class TextCellEditor implements TableCellEditor, TreeCellEditor, Serializable { /** * An EventListenerList used to keep track of events. */ private EventListenerList listenerList = new EventListenerList(); protected transient ChangeEvent changeEvent = null; /** * A placeholder for the component used while editing. */ private JTextArea editorComponent; /** * An instance of the inner class to which all edting is delegated. */ private EditorDelegate delegate; /** * An integer that controls the number of clicks required to start * editing. */ private int clickCountToStart = 1; /** * The scroll pane used to wrap up the text area so that * horizontal/vertical scrolls are possible. */ private JScrollPane scroller; /** * The table on which the editing is occurring * We need to keep track of this so that the row height can be adjusted. */ private JTable table; /** * The number of lines to be shown while editing a cell. */ private final int numLinesToShow = 4; /** * Constructs a TextCellEditor that uses a text area. * * @param textArea a JTextArea object */ public TextCellEditor(final JTextArea textArea) { setScroller(new JScrollPane(textArea)); setEditorComponent(textArea); delegate = new EditorDelegate() { public void setValue(Object value) { editorComponent.setText((value != null) ? value.toString() : ""); } public Object getCellEditorValue() { return editorComponent.getText(); } }; textArea.addFocusListener(delegate); textArea.addCaretListener(delegate); } /** * Sets the 'listenerList' attribute with the value passed in. * * @param list The new event listener list. */ protected void setListenerList(EventListenerList list) { listenerList = list; } /** * Gets the 'listenerList' attribute. * * @return The event listener list. */ protected EventListenerList getListenerList() { return listenerList; } /** * Sets the 'editorComponent' attribute with the value passed in. * * @param area The new editor component. */ protected void setEditorComponent(JTextArea area) { editorComponent = area; } /** * Gets the 'editorComponent' attribute. * * @return The editor component. */ protected JTextArea getEditorComponent() { return editorComponent; } /** * Sets the 'clickCountToStart' attribute with the value passed in. * * @param count an int specifying the number of clicks needed to start editing */ public void setClickCountToStart(int count) { clickCountToStart = count; } /** * Get the 'clickCountToStart' value. * * @return The number of clicks needed to start editing. */ public int getClickCountToStart() { return clickCountToStart; } /** * Sets the 'scroller' attribute with the value passed in. * * @param pane the new pane that is to be used for scrolling. */ public void setScroller(JScrollPane pane) { scroller = pane; } /** * Get the 'scroller' value. * * @return The scroll pane that is to be used for scrolling. */ public JScrollPane getScroller() { return scroller; } /** * Sets the 'table' attribute with the value passed in. * * @param table the new table on which the events are occurring. */ public void setTable(JTable table) { this.table = table; } /** * Get the 'table' value. * * @return The table on which the events are occurring. */ public JTable getTable() { return table; } /** * Implements javax.swing.CellEditor * Delegates the work to the delegator instance. * * @return The value stored in the editor component */ public Object getCellEditorValue() { return delegate.getCellEditorValue(); } /** * Implements javax.swing.CellEditor * Used while checking if the cell is editable or not * Delegates the work to the delegator instance * * @param anEvent User input event which triggered this method call. * @return true if the cell is editable, false otherwise. */ public boolean isCellEditable(EventObject anEvent) { if (anEvent instanceof MouseEvent) { return ((MouseEvent) anEvent).getClickCount() >= clickCountToStart; } return true; } /** * Implements javax.swing.CellEditor * Used while checking if the cell should be selected or not. * Delegates the work to the delegator instance. * * @param anEvent User input event which triggered this method call. * @return true if the cell should be selected, false otherwise. */ public boolean shouldSelectCell(EventObject anEvent) { return delegate.shouldSelectCell(anEvent); } /** * Implements javax.swing.CellEditor * Fires the "editingStopped" event and returns true. * * @return true always. */ public boolean stopCellEditing() { fireEditingStopped(); return true; } /** * Implements javax.swing.CellEditor * Fires the "editingCancelled" event. */ public void cancelCellEditing() { fireEditingCanceled(); } /** * Implements javax.swing.CellEditor * Adds the instance of CellEditorListener to the listener list. * * @param listener The instance of CellEditorListener which is to be notified of any changes to this component. * @see #removeCellEditorListener */ public void addCellEditorListener(CellEditorListener listener) { listenerList.add(CellEditorListener.class, listener); } /** * Implements javax.swing.CellEditor * Removes the instance of CellEditorListener from the listener list. * * @param listener The instance of CellEditorListener which is to be removed from the listener list. * @see #addCellEditorListener */ public void removeCellEditorListener(CellEditorListener listener) { listenerList.remove(CellEditorListener.class, listener); } /** * Notify all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. * @see #listenerList */ protected void fireEditingStopped() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == CellEditorListener.class) { // Lazily create the event: if (changeEvent == null) { changeEvent = new ChangeEvent(this); } ((CellEditorListener) listeners[i + 1]).editingStopped(changeEvent); } } } /** * Notify all listeners that have registered interest for * notification on this event type. The event instance * is lazily created using the parameters passed into * the fire method. * * @see #listenerList */ protected void fireEditingCanceled() { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == CellEditorListener.class) { // Lazily create the event: if (changeEvent == null) { changeEvent = new ChangeEvent(this); } ((CellEditorListener) listeners[i + 1]).editingCanceled(changeEvent); } } } /** * Implements javax.swing.tree.TreeCellEditor * Sets the equivalent string value on the delegator instance and rebuilds the screen. * * @param tree The JTree that is asking the editor to edit * This parameter can be null. * @param value The value of the cell to be edited. * @param isSelected true is the cell is to be renderer with * selection highlighting * @param expanded true if the node is expanded * @param leaf true if the node is a leaf node * @param row The row index of the node being edited * @return The component for editing */ public Component getTreeCellEditorComponent( JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { delegate.setValue(tree.convertValueToText(value, isSelected, expanded, leaf, row, false)); rebuildViews((String) value); return scroller; } /** * Rebuilds the 'editorComponent' with a new JTextArea, * populates it with the text and sets the font from the previous value * Note: This should not really be necessary but the JTextArea does not behave * properly if we do not instantiate a new one * * @param s The text in the current instance of the 'editorComponent' */ private void rebuildViews(String s) { //disconnect listeners editorComponent.removeFocusListener(delegate); editorComponent.removeCaretListener(delegate); //save the current font Font f = editorComponent.getFont(); //create a new JTextArea on the current value setEditorComponent(new JTextArea(s)); //set the font editorComponent.setFont(f); //add the listeners editorComponent.addFocusListener(delegate); editorComponent.addCaretListener(delegate); //and plonk into the scroller scroller.setViewportView(editorComponent); } /** * Implements javax.swing.table.TableCellEditor * Sets the equivalent string value on the delegator instance, resets the table instance and rebuilds the screen. * * @param table the JTable that is asking the * editor to edit; can be null * @param value the value of the cell to be edited; it is * up to the specific editor to interpret * and draw the value. For example, if value is * the string "true", it could be rendered as a * string or it could be rendered as a check * box that is checked. null * is a valid value * @param isSelected true if the cell is to be rendered with * highlighting * @param row the row of the cell being edited * @param column the column of the cell being edited * @return the component for editing */ public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { delegate.setValue(value); setTable(table); editorComponent.setFont(table.getFont()); rebuildViews((String) value); return scroller; } /** * Sets the table's row height based on whether any cell is being currently edited or not. * * @param isEditing boolean flag depicting whether any cell is being currently edited or not. * @param horizontalScrollBarHeight The integer height */ private void setTableRowHeight(boolean isEditing) { if (table != null) { FontMetrics fontMetrics = table.getGraphics().getFontMetrics(table.getFont()); if (isEditing) { int newHeight = (fontMetrics.getHeight() * numLinesToShow) + scroller.getHorizontalScrollBar().getHeight(); if (table.getRowHeight() < newHeight) { table.setRowHeight(newHeight); } } else { table.setRowHeight(fontMetrics.getHeight()); } } } /** * */ protected class EditorDelegate implements FocusListener, CaretListener, ActionListener, ItemListener, Serializable { /** Not implemented. */ protected Object value; /** Not implemented. */ public Object getCellEditorValue() { return null; } /** Not implemented. */ public void setValue(Object x) { } /** Not implemented. */ public boolean isCellEditable(EventObject anEvent) { return true; } /** * Unfortunately, restrictions on API changes force us to * declare this method package private. */ boolean shouldSelectCell(EventObject anEvent) { return true; } /** Not implemented. */ public boolean startCellEditing(EventObject anEvent) { return true; } /** Not implemented. */ public boolean stopCellEditing() { return true; } /** Not implemented. */ public void cancelCellEditing() { } // Implementing ActionListener interface public void actionPerformed(ActionEvent e) { fireEditingStopped(); } // Implementing ItemListener interface public void itemStateChanged(ItemEvent e) { fireEditingStopped(); } // Implementing FocusListener interface public void focusGained(FocusEvent e) { setTableRowHeight(true); } // Implementing FocusListener interface public void focusLost(FocusEvent e) { setTableRowHeight(false); fireEditingStopped(); } // Implementing CaretListener interface public void caretUpdate(CaretEvent e) { setTableRowHeight(true); } } }