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

Reply via email to