This patch implements DefaultCellEditor and some methods in JTable in
order to make editing JTable cells possible.
A demo will be added shortly, for now I am attaching a small test app
that you can use to test the editing functionality. With a table cell
selected, pressing F2 starts editing and then pressing ENTER stops
editing.
2 notes:
- the test app appears to draw incorrectly at the top, this is due to
some trouble in BasicViewportUI
- if you edit more than one cell, you will notice that text from
previously edited cells appears in the cell you are currently editing,
this is due to bugs in JTextField/JTextComponent
Patch is attached.
2005-08-09 Anthony Balkissoon <[EMAIL PROTECTED]>
* javax/swing/DefaultCellEditor.java:
(EditorDelegate.setValue): Implemented.
(EditorDelegate.getCellEditorValue): Implemented.
(EditorDelegate.isCellEditable): Implemented.
(EditorDelegate.shouldSelectCell): Implemented.
(EditorDelegate.stopCellEditing): Implemented.
(EditorDelegate.cancelCellEditing): Implemented.
(EditorDelegate.startCellEditing): Implemented.
(EditorDelegate.actionPerformed): Implemented.
(EditorDelegate.itemStateChanged): Implemented.
(EditorDelegate.fireEditingStopped): New implementation method.
(EditorDelegate.fireEditingCancelled): New implementation method.
(DefaultCellEditor): Implemented 3 constructors.
(getComponent): Implemented.
(getClickCountToStart): Implemented.
(setClickCountToStart): Implemented.
(getCellEditorValue): Implemented.
(isCellEditable): Implemented.
(shouldSelectCell): Implemented.
(cancelCellEditing): Implemented.
(getTableCellEditorComponent): Implemented.
* javax/swing/JTable.java:
(EditorUpdateTimer): New private class.
(editingStopped): Implemented.
(setValueAt): If the Object value is a Component, add it to the JTable
so it can obtain focus.
(editCellAt): Implemented.
(removeEditor): Implemented.
(prepareEditor): Implemented.
* javax/swing/plaf/basic/BasicTableUI.java:
(KeyHandler.keyPressed): Added F2 "start editing" key action.
(MouseHandler.mousePressed): Added check to see if a new cell was
selected and we need to stop editing.
(paint): If the cell is a JTextField, paint its Caret as well.
* javax/swing/table/DefaultTableCellRenderer.java:
(getTableCellRendererComponent): If a JTextField is passed in, return
one. This is used for editing JTable cells.
Index: javax/swing/DefaultCellEditor.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/DefaultCellEditor.java,v
retrieving revision 1.10
diff -u -r1.10 DefaultCellEditor.java
--- javax/swing/DefaultCellEditor.java 27 Jul 2005 12:41:33 -0000 1.10
+++ javax/swing/DefaultCellEditor.java 9 Aug 2005 15:52:59 -0000
@@ -43,9 +43,13 @@
import java.awt.event.ActionListener;
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.JTable;
+import javax.swing.JTextField;
+import javax.swing.event.CellEditorListener;
import javax.swing.table.TableCellEditor;
import javax.swing.tree.TreeCellEditor;
@@ -91,8 +95,10 @@
*
* @param event TODO
*/
- public void setValue(Object event)
+ public void setValue(Object value)
{
+ // TODO: should be setting the value in the editorComp
+ this.value = value;
}
/**
@@ -102,7 +108,8 @@
*/
public Object getCellEditorValue()
{
- return null; // TODO
+ // TODO: should be getting the updated value from the editorComp
+ return value;
} // getCellEditorValue()
/**
@@ -114,7 +121,11 @@
*/
public boolean isCellEditable(EventObject event)
{
- return false; // TODO
+ if (!(event instanceof MouseEvent))
+ return true;
+
+ //Todo: if the right number of clicks has occured, return true;
+ return false;
} // isCellEditable()
/**
@@ -126,7 +137,8 @@
*/
public boolean shouldSelectCell(EventObject event)
{
- return false; // TODO
+ // return true to indicate that the editing cell may be selected
+ return true;
} // shouldSelectCell()
/**
@@ -136,7 +148,8 @@
*/
public boolean stopCellEditing()
{
- return false; // TODO
+ fireEditingStopped();
+ return true;
} // stopCellEditing()
/**
@@ -144,7 +157,7 @@
*/
public void cancelCellEditing()
{
- // TODO
+ fireEditingCanceled();
} // cancelCellEditing()
/**
@@ -156,7 +169,8 @@
*/
public boolean startCellEditing(EventObject event)
{
- return false; // TODO
+ // return true to indicate that editing has begun
+ return true;
} // startCellEditing()
/**
@@ -166,7 +180,7 @@
*/
public void actionPerformed(ActionEvent event)
{
- // TODO
+ stopCellEditing();
} // actionPerformed()
/**
@@ -176,9 +190,23 @@
*/
public void itemStateChanged(ItemEvent event)
{
- // TODO
+ stopCellEditing();
} // itemStateChanged()
+ void fireEditingStopped()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].editingStopped(changeEvent);
+
+ }
+
+ void fireEditingCanceled()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].editingCanceled(changeEvent);
+ }
} // EditorDelegate
/**
@@ -203,7 +231,8 @@
*/
public DefaultCellEditor(JTextField textfield)
{
- // TODO
+ editorComponent = textfield;
+ clickCountToStart = 2;
} // DefaultCellEditor()
/**
@@ -213,7 +242,8 @@
*/
public DefaultCellEditor(JCheckBox checkbox)
{
- // TODO
+ editorComponent = checkbox;
+ clickCountToStart = 1;
} // DefaultCellEditor()
/**
@@ -223,7 +253,8 @@
*/
public DefaultCellEditor(JComboBox combobox)
{
- // TODO
+ editorComponent = combobox;
+ clickCountToStart = 1;
} // DefaultCellEditor()
/**
@@ -233,7 +264,7 @@
*/
public Component getComponent()
{
- return null; // TODO
+ return editorComponent;
} // getComponent()
/**
@@ -243,7 +274,7 @@
*/
public int getClickCountToStart()
{
- return 0; // TODO
+ return clickCountToStart;
} // getClickCountToStart()
/**
@@ -253,7 +284,7 @@
*/
public void setClickCountToStart(int count)
{
- // TODO
+ clickCountToStart = count;
} // setClickCountToStart()
/**
@@ -263,7 +294,7 @@
*/
public Object getCellEditorValue()
{
- return null; // TODO
+ return delegate.getCellEditorValue();
} // getCellEditorValue()
/**
@@ -275,7 +306,7 @@
*/
public boolean isCellEditable(EventObject event)
{
- return false; // TODO
+ return delegate.isCellEditable(event);
} // isCellEditable()
/**
@@ -287,7 +318,7 @@
*/
public boolean shouldSelectCell(EventObject event)
{
- return false; // TODO
+ return delegate.shouldSelectCell(event);
} // shouldSelectCell()
/**
@@ -297,7 +328,7 @@
*/
public boolean stopCellEditing()
{
- return false; // TODO
+ return delegate.stopCellEditing();
} // stopCellEditing()
/**
@@ -305,7 +336,7 @@
*/
public void cancelCellEditing()
{
- // TODO
+ delegate.cancelCellEditing();
} // cancelCellEditing()
/**
@@ -339,10 +370,30 @@
*
* @returns Component
*/
- public Component getTableCellEditorComponent(JTable tree, Object value,
+ public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row,
int column)
{
- return null; // TODO
+ // NOTE: as specified by Sun, we don't call new() everytime, we return
+ // editorComponent on each call to getTableCellEditorComponent or
+ // getTreeCellEditorComponent. However, currently JTextFields have a
+ // problem with getting rid of old text, so without calling new() there
+ // are some strange results. If you edit more than one cell in the table
+ // text from previously edited cells may unexpectedly show up in the
+ // cell you are currently editing. This will be fixed automatically
+ // when JTextField is fixed.
+ if (editorComponent instanceof JTextField)
+ {
+ ((JTextField)editorComponent).setText(value.toString());
+ delegate = new EditorDelegate();
+ ((JTextField)editorComponent).addActionListener(delegate);
+ }
+ else
+ {
+ // TODO
+ }
+ return editorComponent;
} // getTableCellEditorComponent()
+
+
}
Index: javax/swing/JTable.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JTable.java,v
retrieving revision 1.38
diff -u -r1.38 JTable.java
--- javax/swing/JTable.java 2 Aug 2005 19:25:34 -0000 1.38
+++ javax/swing/JTable.java 9 Aug 2005 15:52:59 -0000
@@ -43,9 +43,12 @@
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Date;
+import java.util.EventObject;
import java.util.Hashtable;
import java.util.Vector;
@@ -69,6 +72,7 @@
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
+import javax.swing.text.Caret;
public class JTable extends JComponent
implements TableModelListener, Scrollable, TableColumnModelListener,
@@ -351,6 +355,7 @@
*/
protected transient Component editorComp;
+
/**
* Whether or not the table should automatically compute a matching
* [EMAIL PROTECTED] TableColumnModel} and assign it to the [EMAIL PROTECTED] #columnModel}
@@ -555,7 +560,21 @@
*/
protected JTableHeader tableHeader;
-
+ /**
+ * The row of the cell being edited.
+ */
+ int rowBeingEdited = -1;
+
+ /**
+ * The column of the cell being edited.
+ */
+ int columnBeingEdited = -1;
+
+ /**
+ * The action listener for the editor's Timer.
+ */
+ Timer editorTimer = new EditorUpdateTimer();
+
/**
* Creates a new <code>JTable</code> instance.
*/
@@ -673,6 +692,51 @@
this(new DefaultTableModel(data, columnNames));
}
+ /**
+ * The timer that updates the editor component.
+ */
+ private class EditorUpdateTimer
+ extends Timer
+ implements ActionListener
+ {
+ /**
+ * Creates a new EditorUpdateTimer object with a default delay of 0.5 seconds.
+ */
+ public EditorUpdateTimer()
+ {
+ super(500, null);
+ addActionListener(this);
+ }
+
+ /**
+ * Lets the caret blink and repaints the table.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ Caret c = ((JTextField)JTable.this.editorComp).getCaret();
+ if (c != null)
+ c.setVisible(!c.isVisible());
+ JTable.this.repaint();
+ }
+
+ /**
+ * Updates the blink delay according to the current caret.
+ */
+ public void update()
+ {
+ stop();
+ Caret c = ((JTextField)JTable.this.editorComp).getCaret();
+ if (c != null)
+ {
+ setDelay(c.getBlinkRate());
+ if (((JTextField)JTable.this.editorComp).isEditable())
+ start();
+ else
+ c.setVisible(false);
+ }
+ }
+ }
+
public void addColumn(TableColumn column)
{
if (column.getHeaderValue() == null)
@@ -779,6 +843,21 @@
public void editingStopped (ChangeEvent event)
{
+ if (rowBeingEdited > -1 && columnBeingEdited > -1)
+ {
+ if (getValueAt(rowBeingEdited, columnBeingEdited) instanceof JTextField)
+ {
+ remove((Component)getValueAt(rowBeingEdited, columnBeingEdited));
+ setValueAt(((JTextField)editorComp).getText(),
+ rowBeingEdited, columnBeingEdited);
+ }
+ rowBeingEdited = -1;
+ columnBeingEdited = -1;
+ }
+ editorTimer.stop();
+ editorComp = null;
+ cellEditor = null;
+ requestFocusInWindow(false);
repaint();
}
@@ -980,7 +1059,7 @@
if (editor == null)
editor = getDefaultEditor(dataModel.getColumnClass(column));
-
+
return editor;
}
@@ -2116,6 +2195,8 @@
public void setValueAt(Object value, int row, int column)
{
+ if (value instanceof Component)
+ add((Component)value);
dataModel.setValueAt(value, row, convertColumnIndexToModel(column));
}
@@ -2206,5 +2287,58 @@
}
+ }
+
+ /**
+ * Programmatically starts editing the specified cell.
+ *
+ * @param row the row of the cell to edit.
+ * @param column the column of the cell to edit.
+ */
+ public boolean editCellAt (int row, int column)
+ {
+ setCellEditor(getCellEditor(row, column));
+ editorComp = prepareEditor(cellEditor, row, column);
+ cellEditor.addCellEditorListener(this);
+ rowBeingEdited = row;
+ columnBeingEdited = column;
+ setValueAt(editorComp, row, column);
+ ((JTextField)editorComp).requestFocusInWindow(false);
+ editorTimer.start();
+ return true;
+ }
+
+ /**
+ * Programmatically starts editing the specified cell.
+ *
+ * @param row the row of the cell to edit.
+ * @param column the column of the cell to edit.
+ */
+ public boolean editCellAt (int row, int column, EventObject e)
+ {
+ return editCellAt(row, column);
+ }
+
+ /**
+ * Discards the editor object.
+ */
+ public void removeEditor()
+ {
+ editingStopped(new ChangeEvent(this));
+ }
+
+ /**
+ * Prepares the editor by querying for the value and selection state of the
+ * cell at (row, column).
+ *
+ * @param editor the TableCellEditor to set up
+ * @param row the row of the cell to edit
+ * @param column the column of the cell to edit
+ * @return the Component being edited
+ */
+ public Component prepareEditor (TableCellEditor editor, int row, int column)
+ {
+ return editor.getTableCellEditorComponent
+ (this, getValueAt(row, column), isCellSelected(row, column), row, column);
}
}
Index: javax/swing/plaf/basic/BasicTableUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTableUI.java,v
retrieving revision 1.19
diff -u -r1.19 BasicTableUI.java
--- javax/swing/plaf/basic/BasicTableUI.java 2 Aug 2005 19:25:34 -0000 1.19
+++ javax/swing/plaf/basic/BasicTableUI.java 9 Aug 2005 15:52:59 -0000
@@ -56,10 +56,12 @@
import javax.swing.CellRendererPane;
import javax.swing.JComponent;
import javax.swing.JTable;
+import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.TableUI;
@@ -393,7 +395,7 @@
}
else if (evt.getKeyCode() == KeyEvent.VK_F2)
{
- // FIXME: Implement "start editing"
+ table.editCellAt(rowLead,colLead);
}
else if (evt.getKeyCode() == KeyEvent.VK_PAGE_UP)
{
@@ -592,15 +594,15 @@
(table.getCellRect(rowModel.getLeadSelectionIndex(),
colModel.getLeadSelectionIndex(), false));
}
-
+
public void keyReleased(KeyEvent e)
{
}
-
+
public void keyTyped(KeyEvent e)
{
}
-
+
/**
* Returns the column index of the first visible column.
*
@@ -718,6 +720,11 @@
}
public void mousePressed(MouseEvent e)
{
+ ListSelectionModel rowModel = table.getSelectionModel();
+ ListSelectionModel colModel = table.getColumnModel().getSelectionModel();
+ int rowLead = rowModel.getLeadSelectionIndex();
+ int colLead = colModel.getLeadSelectionIndex();
+
begin = new Point(e.getX(), e.getY());
curr = new Point(e.getX(), e.getY());
//if control is pressed and the cell is already selected, deselect it
@@ -733,7 +740,12 @@
}
else
updateSelection(e.isControlDown());
-
+
+ // If we were editing, but the moved to another cell, stop editing
+ if (rowLead != rowModel.getLeadSelectionIndex() ||
+ colLead != colModel.getLeadSelectionIndex())
+ if (table.isEditing())
+ table.editingStopped(new ChangeEvent(e));
}
public void mouseReleased(MouseEvent e)
{
@@ -896,6 +908,8 @@
((JComponent) comp).setBorder(cellBorder);
}
comp.paint(gfx);
+ if (comp instanceof JTextField)
+ ((JTextField)comp).getCaret().paint(gfx);
gfx.translate(-x, -y);
}
y += height;
@@ -948,6 +962,5 @@
}
gfx.setColor(save);
}
-
}
}
Index: javax/swing/table/DefaultTableCellRenderer.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/table/DefaultTableCellRenderer.java,v
retrieving revision 1.13
diff -u -r1.13 DefaultTableCellRenderer.java
--- javax/swing/table/DefaultTableCellRenderer.java 2 Jul 2005 20:32:51 -0000 1.13
+++ javax/swing/table/DefaultTableCellRenderer.java 9 Aug 2005 15:52:59 -0000
@@ -47,6 +47,7 @@
import javax.swing.JTable;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
+import javax.swing.JTextField;
/**
* Class to display every cells.
@@ -123,7 +124,11 @@
int row, int column)
{
if (value != null)
- super.setText(value.toString());
+ {
+ if (value instanceof JTextField)
+ return new JTextField(((JTextField)value).getText());
+ super.setText(value.toString());
+ }
setOpaque(true);
import java.awt.*;
import javax.swing.*;
import java.io.*;
class Test
{
public static void main(String args[]) throws IOException
{
JFrame jf = new JFrame();
JPanel topPanel;
JTable table;
JScrollPane scrollPane;
// Set the frame characteristics
jf.setSize( 300, 200 );
// Create a panel to hold all other components
topPanel = new JPanel();
topPanel.setLayout( new BorderLayout() );
jf.getContentPane().add( topPanel );
// Create columns names
String columnNames[] = { "Column 1", "Column 2", "Column 3" };
// Create some data
String dataValues[][] =
{
{ "12", "234", "67" },
{ "-123", "43", "853" },
{ "93", "89.2", "109" },
{ "279", "9033", "3092" }
};
// Create a new table instance
table = new JTable( dataValues, columnNames );
table.setColumnSelectionAllowed(true);
table.setRowSelectionAllowed(true);
table.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.getColumnModel().getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
// Add the table to a scrolling pane
scrollPane = new JScrollPane( table );
topPanel.add( scrollPane, BorderLayout.CENTER );
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.show();
}
}
_______________________________________________
Classpath-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/classpath-patches