JDK-4136681 : If the TableCellRenderer is JScrollPane, the scrollbar does not work
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.2.0
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_nt
  • CPU: x86
  • Submitted: 1998-05-08
  • Updated: 1998-05-28
  • Resolved: 1998-05-28
Description
nd") );
	         displayComponent.setBackground( UIManager.getColor("Table.focusCellBackground") );
	    }
	} 	
	
  if ( value == null)
   displayComponent.setText("");
  else
   displayComponent.setText(value.toString());
  
  return this;
}
    
}

2)

import java.awt.*;
import java.awt.event.*;
import java.awt.AWTEvent;
import java.lang.Boolean;
import java.awt.swing.table.*;
import java.awt.swing.event.*;
import java.util.EventObject;
import java.awt.swing.*;
import java.awt.swing.border.*;


public class ScrollableCellEditor  implements TableCellEditor
					  {
//
//  Instance Variables
//

    /** Event listeners */
    protected EventListenerList listenerList = new EventListenerList();
    transient protected ChangeEvent changeEvent = null;

    protected JComponent editorComponent;
    protected EditorDelegate delegate;
    protected int clickCountToStart;

		protected JScrollPane scrollpane;

//
//  Constructors
//

    public ScrollableCellEditor(JTextArea x) {
        this.editorComponent = x;
	      this.clickCountToStart = 2;
        this.delegate = new EditorDelegate() {
            public void setValue(Object x) {
                super.setValue(x);
		if (x != null)
		    ((JTextArea)editorComponent).setText(x.toString());
		else
		    ((JTextArea)editorComponent).setText("");
		    
		scrollpane = new JScrollPane((JTextArea)editorComponent);
	  EmptyBorder emptyBorder = new EmptyBorder(0,0,0,0);
		editorComponent.setBorder(emptyBorder);
		scrollpane.setBorder(emptyBorder);
	
  }

	    public Object getCellEditorValue() {
		return ((JTextArea)editorComponent).getText();
	    }

	    public boolean startCellEditing(EventObject anEvent) {
		if(anEvent == null)
		    editorComponent.requestFocus();
		return true;
	    }

	    public boolean stopCellEditing() {
		return true;
	    }
        };
    }

    

    public Component getComponent() {
	return editorComponent;
    }

//
//  Modifying
//

    public void setClickCountToStart(int count) {
	     clickCountToStart = count;
    }

    
    public int getClickCountToStart() {
	return clickCountToStart;
    }

//
//  Implementing the CellEditor Interface
//

    public Object getCellEditorValue() {
        return delegate.getCellEditorValue();
    }

    public boolean isCellEditable(EventObject anEvent) {
	if (anEvent instanceof MouseEvent) {
	 if (((MouseEvent)anEvent).getClickCount() < clickCountToStart)
					return false;
	}
	return delegate.isCellEditable(anEvent);
    }
    
    public boolean shouldSelectCell(EventObject anEvent) {
	boolean         retValue = true;

	if (this.isCellEditable(anEvent)) {
	    if (anEvent == null || ((MouseEvent)anEvent).getClickCount() >= 
		clickCountToStart)
		retValue = delegate.startCellEditing(anEvent);
	}

	// By default we want the cell the be selected so
	// we return true
	return retValue;
    }

    public boolean stopCellEditing() {
	boolean stopped = delegate.stopCellEditing();

	if (stopped) {
	    fireEditingStopped();
	}
	
	return stopped;
    }

    public void cancelCellEditing() {
	delegate.cancelCellEditing();
	fireEditingCanceled();
    }

//
//  Handle the event listener bookkeeping
//
    public void addCellEditorListener(CellEditorListener l) {
	listenerList.add(CellEditorListener.class, l);
    }

    public void removeCellEditorListener(CellEditorListener l) {
	listenerList.remove(CellEditorListener.class, l);
    }

    /*
     * 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 EventListenerList
     */
    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);
	    }	       
	}
    }

    
    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);
	    }	       
	}
    }


//
//  Implementing the CellEditor Interface
//

    public Component getTableCellEditorComponent(JTable table, Object value,
						 boolean isSelected,
						 int row, int column) {
        delegate.setValue(value);
    return scrollpane;
    }


//
//  Protected EditorDelegate class
//

    protected class EditorDelegate implements ActionListener, ItemListener{

        protected Object value;

        public Object getCellEditorValue() {
            return value;
        }

        public void setValue(Object x) {
            this.value = x;
        }

        public boolean isCellEditable(EventObject anEvent) {
	    return true;
	}

        public boolean startCellEditing(EventObject anEvent) {
	    return true;
	}

        public boolean stopCellEditing() {
	    return true;
	}

        public void cancelCellEditing() {
	}

	// Implementing ActionListener interface
        public void actionPerformed(ActionEvent e) {
	    fireEditingStopped();
	}

	// Implementing ItemListener interface
        public void itemStateChanged(ItemEvent e) {
	    fireEditingStopped();
	}
    }

} // End of class 

3)import java.awt.swing.table.*;
import java.awt.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MyTablePanel extends JPanel {
	
	JTable     tableContained;
	
	public MyTablePanel(String[] colName) {
        tableContained = new JTable  (new MyTableModel(colName));
        tableContained.setSelectionBackground(Color.lightGray);
        tableContained.setRowHeight(50);
        //Create the scroll pane and add the table to it. 
        JScrollPane scrollPane = JTable.createScrollPaneForTable(tableContained);
        scrollPane.setPreferredSize(new Dimension(400, 70));

        //Add the scroll pane to this panel.
        setLayout(new GridLayout(1, 0)); 
        setPreferredSize(new Dimension(400,50));
        add(scrollPane);   
    }
 		
 		public JTable     getTableContained() { return tableContained;}
    
    class MyTableModel extends AbstractTableModel 
    {
    	  String[] 	colNames;
    	  int rowLength=5;
    	  String[][] data;
    	  
    	 
    	  public MyTableModel(String[] cName)
    	  {
    	  	colNames = cName;
    	  	data = new String[rowLength][colNames.length];
    	  	for (int i=0; i<rowLength; i++)
    	  	{
    	  	  for (int j=0; j<colNames.length; j++)
    	  	  	data[i][j] ="";
    	 	  }                   
    	 	              
        };

        public int getColumnCount() {
        		 return colNames.length;
        }

        public int getRowCount() {
           return rowLength;
        }

        public String getColumnName(int col) {
          return colNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }
        
        public Class getColumnClass(int c)
        {
        	/*if (getColumnName(c).equals("Description") )
        		return JTextArea.class;
        	else*/
        	  return getValueAt(0, c).getClass();
        	}

        public boolean isCellEditable(int row, int col) {return true;} 
        
        public void setValueAt(Object aValue, int row, int col) 
        { 
                data[row][col] =(String) aValue; 
        }
    }
    
    public static void main(String[] args) {
        JFrame frame = new JFrame("SimpleTableDemo");

        frame.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            });
        String a[]={"Name","Description"};
        
        MyTablePanel table = new MyTablePanel(a);

		  	TableColumn descriptionColumn = table.getTableContained().getColumn("Description");   
      ScrollableCellEditor cellEditor = new ScrollableCellEditor(new JTextArea());
      descriptionColumn.setCellEditor(cellEditor);     
        
      ScrollableCellRenderer desRenderer = new ScrollableCellRenderer(); 
      descriptionColumn.setCellRenderer(desRenderer);
      
        
      frame.getContentPane().add("Center", table);
      frame.setSize(400, 300);
      frame.setVisible(true);
        
    }
}


Name: rk38400			Date: 05/08/98


1. Steps to reproduce the problem
   
   1) Compile three .java files attached. ( ScrollableCellEditor.java, ScrollableCellRenderer.java, MyTablePanel.java)
   2) run MyTablePanel using ( java MyTablePanel)
   3) After the Table shows up, you can see there are 2 column.( Name and Description)
      For the name column, you can only enter single line text. But you can enter multiple lines of text in Description column.
   4) Double click on one of the cell in Description column and enter more than 4 lines of text so that the scroll bar can show up on the screen.
   5) deselect this cell by click on any other cell. Since the tableCellRender is a scrollPane, you can see the scroll bar in the cell.
   6) SINGLE click on this cell, the background of the cell change to a different color to indicate that this cell has been selected.
      SINGLE click on the scrollbar of this cell. THERE IS NO RESPONSE! it is not scrolling.
      (if you DOUBLE click on this cell, the cellEditor takes over, and the scrollbar in the cellEditor works well. So in order to demonstrate 
       this problem, you have to SINGLE click on the scroll bar of this cell.)

   QUESTION: How to make the Scroll bar of the CellRenderer work?
   
   I think the problem is that the CellRenderer never gets this event When the user click on the scrollbar.

2. Java source code.
 
  1) ScrollableCellRenderer.java

import java.awt.swing.*;
import java.awt.swing.table.TableCellRenderer;
import java.awt.swing.border.*;

import java.awt.*;
import java.awt.Color;

public class ScrollableCellRenderer extends  JScrollPane
    implements TableCellRenderer
{    
    private Color unselectedForeground; 
    private Color unselectedBackground; 
    private JTextArea displayComponent;
          
    public ScrollableCellRenderer() {
    	displayComponent =new JTextArea();
    	EmptyBorder emptyBorder = new EmptyBorder(0,0,0,0);
    	setBorder(emptyBorder);
    	setViewportView(displayComponent);
    	displayComponent.setBorder(emptyBorder);
	    setOpaque(true);
    }

    public void setForeground(Color c) {
        super.setForeground(c); 
        unselectedForeground = c; 
    }
    
    public void setBackground(Color c) {
        super.setBackground(c); 
        unselectedBackground = c; 
    }

    public void updateUI() {
       super.updateUI(); 
	setForeground(null);
	setBackground(null);
    }
    
 
public Component getTableCellRendererComponent(JTable table, Object value,
                          boolean isSelected, boolean hasFocus, int row, int column) {

	if (isSelected) {
	    displayComponent.setForeground(table.getSelectionForeground());
	    displayComponent.setBackground(table.getSelectionBackground());
	}
	else {
	    displayComponent.setForeground((unselectedForeground != null) ? unselectedForeground 
	                                                       : table.getForeground());
	     displayComponent.setBackground((unselectedBackground != null) ? unselectedBackground 
	                                                       : table.getBackground());
	}
	
	setFont(table.getFont());

	if (hasFocus) 
	{
	   
	    if (table.isCellEditable(row, column)) { 
	         displayComponent.setForeground( UIManager.getColor("Table.focusCellForeground") );
	         displayComponent.setBackground( UIManager.getColor("Table.focusCellBackground") );
	    }
	} 	
	
  if ( value == null)
   displayComponent.setText("");
  else
   displayComponent.setText(value.toString());
  
  return this;
}
    
} // end of ScrollableCellRenderer class


  2) ScrollableCellEditor.java 

import java.awt.*;
import java.awt.event.*;
import java.awt.AWTEvent;
import java.lang.Boolean;
import java.awt.swing.table.*;
import java.awt.swing.event.*;
import java.util.EventObject;
import java.awt.swing.*;
import java.awt.swing.border.*;


public class ScrollableCellEditor  implements TableCellEditor
{
//
//  Instance Variables
//

    /** Event listeners */
    protected EventListenerList listenerList = new EventListenerList();
    transient protected ChangeEvent changeEvent = null;

    protected JComponent editorComponent;
    protected EditorDelegate delegate;
    protected int clickCountToStart;

		protected JScrollPane scrollpane;

//
//  Constructors
//

    public ScrollableCellEditor(JTextArea x) {
        this.editorComponent = x;
	      this.clickCountToStart = 2;
        this.delegate = new EditorDelegate() {
            public void setValue(Object x) {
                super.setValue(x);
		if (x != null)
		    ((JTextArea)editorComponent).setText(x.toString());
		else
		    ((JTextArea)editorComponent).setText("");
		    
		scrollpane = new JScrollPane((JTextArea)editorComponent);
	  EmptyBorder emptyBorder = new EmptyBorder(0,0,0,0);
		editorComponent.setBorder(emptyBorder);
		scrollpane.setBorder(emptyBorder);
	
  }

	    public Object getCellEditorValue() {
		return ((JTextArea)editorComponent).getText();
	    }

	    public boolean startCellEditing(EventObject anEvent) {
		if(anEvent == null)
		    editorComponent.requestFocus();
		return true;
	    }

	    public boolean stopCellEditing() {
		return true;
	    }
        };
    }

    

    public Component getComponent() {
	return editorComponent;
    }

//
//  Modifying
//

    public void setClickCountToStart(int count) {
	     clickCountToStart = count;
    }

    
    public int getClickCountToStart() {
	return clickCountToStart;
    }

//
//  Implementing the CellEditor Interface
//

    public Object getCellEditorValue() {
        return delegate.getCellEditorValue();
    }

    public boolean isCellEditable(EventObject anEvent) {
	if (anEvent instanceof MouseEvent) {
	 if (((MouseEvent)anEvent).getClickCount() < clickCountToStart)
					return false;
	}
	return delegate.isCellEditable(anEvent);
    }
    
    public boolean shouldSelectCell(EventObject anEvent) {
	boolean         retValue = true;

	if (this.isCellEditable(anEvent)) {
	    if (anEvent == null || ((MouseEvent)anEvent).getClickCount() >= 
		clickCountToStart)
		retValue = delegate.startCellEditing(anEvent);
	}

	// By default we want the cell the be selected so
	// we return true
	return retValue;
    }

    public boolean stopCellEditing() {
	boolean stopped = delegate.stopCellEditing();

	if (stopped) {
	    fireEditingStopped();
	}
	
	return stopped;
    }

    public void cancelCellEditing() {
	delegate.cancelCellEditing();
	fireEditingCanceled();
    }

//
//  Handle the event listener bookkeeping
//
    public void addCellEditorListener(CellEditorListener l) {
	listenerList.add(CellEditorListener.class, l);
    }

    public void removeCellEditorListener(CellEditorListener l) {
	listenerList.remove(CellEditorListener.class, l);
    }

    /*
     * 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 EventListenerList
     */
    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);
	    }	       
	}
    }

    
    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);
	    }	       
	}
    }


//
//  Implementing the CellEditor Interface
//

    public Component getTableCellEditorComponent(JTable table, Object value,
						 boolean isSelected,
						 int row, int column) {
        delegate.setValue(value);
    return scrollpane;
    }


//
//  Protected EditorDelegate class
//

    protected class EditorDelegate implements ActionListener, ItemListener{

        protected Object value;

        public Object getCellEditorValue() {
            return value;
        }

        public void setValue(Object x) {
            this.value = x;
        }

        public boolean isCellEditable(EventObject anEvent) {
	    return true;
	}

        public boolean startCellEditing(EventObject anEvent) {
	    return true;
	}

        public boolean stopCellEditing() {
	    return true;
	}

        public void cancelCellEditing() {
	}

	// Implementing ActionListener interface
        public void actionPerformed(ActionEvent e) {
	    fireEditingStopped();
	}

	// Implementing ItemListener interface
        public void itemStateChanged(ItemEvent e) {
	    fireEditingStopped();
	}
    }

} // End of class   

  3) MyTablePanel.java

import java.awt.swing.table.*;
import java.awt.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MyTablePanel extends JPanel {
	
	JTable     tableContained;
	
	public MyTablePanel(String[] colName) {
        tableContained = new JTable  (new MyTableModel(colName));
        tableContained.setSelectionBackground(Color.lightGray);
        tableContained.setRowHeight(50);
        //Create the scroll pane and add the table to it. 
        JScrollPane scrollPane = JTable.createScrollPaneForTable(tableContained);
        scrollPane.setPreferredSize(new Dimension(400, 70));

        //Add the scroll pane to this panel.
        setLayout(new GridLayout(1, 0)); 
        setPreferredSize(new Dimension(400,50));
        add(scrollPane);   
    }
 		
 		public JTable     getTableContained() { return tableContained;}
    
    class MyTableModel extends AbstractTableModel 
    {
    	  String[] 	colNames;
    	  int rowLength=5;
    	  String[][] data;
    	  
    	 
    	  public MyTableModel(String[] cName)
    	  {
    	  	colNames = cName;
    	  	data = new String[rowLength][colNames.length];
    	  	for (int i=0; i<rowLength; i++)
    	  	{
    	  	  for (int j=0; j<colNames.length; j++)
    	  	  	data[i][j] ="";
    	 	  }                   
    	 	              
        };

        public int getColumnCount() {
        		 return colNames.length;
        }

        public int getRowCount() {
           return rowLength;
        }

        public String getColumnName(int col) {
          return colNames[col];
        }

        public Object getValueAt(int row, int col) {
            return data[row][col];
        }
        
        public Class getColumnClass(int c)
        {
        	/*if (getColumnName(c).equals("Description") )
        		return JTextArea.class;
        	else*/
        	  return getValueAt(0, c).getClass();
        	}

        public boolean isCellEditable(int row, int col) {return true;} 
        
        public void setValueAt(Object aValue, int row, int col) 
        { 
                data[row][col] =(String) aValue; 
        }
    }
    
    public static void main(String[] args) {
        JFrame frame = new JFrame("SimpleTableDemo");

        frame.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    System.exit(0);
                }
            });
        String a[]={"Name","Description"};
        
        MyTablePanel table = new MyTablePanel(a);

		  	TableColumn descriptionColumn = table.getTableContained().getColumn("Description");   
      ScrollableCellEditor cellEditor = new ScrollableCellEditor(new JTextArea());
      descriptionColumn.setCellEditor(cellEditor);     
        
      ScrollableCellRenderer desRenderer = new ScrollableCellRenderer(); 
      descriptionColumn.setCellRenderer(desRenderer);
      
        
      frame.getContentPane().add("Center", table);
      frame.setSize(400, 300);
      frame.setVisible(true);
        
    }
}
(Review ID: 29995)
======================================================================

ralph.kar@Eng 1998-05-11
Customer came also up with an additional issue related to the same problem:

"If the TableCellRenderer is JScrollPane, horizontal scrollbar doesn't show up"

This bug is related to the bug # 4136681.
The source codea are the same as the source codes for bug #4136681.

1. Steps to reproduce the problem

   1)Run MyTablePanel using: java MyTablePanel

   2)There are 2 columns ( Name, Description) in the table. You can enter multiple lines in Description column. 

   3) Double click any cell in Description column. And enter some text long enough so that the horizontal scrollbar in the cellEditor will show up.

   4) Deselect this cell by click on any other cell. At this point TableCellRenderer takes over and you can see the horizontal scrollbar does not show up.


2. Source code.

1)
import java.awt.swing.*;
import java.awt.swing.table.TableCellRenderer;
import java.awt.swing.border.*;

import java.awt.*;
import java.awt.Color;

public class ScrollableCellRenderer extends  JScrollPane
    implements TableCellRenderer
{    
    private Color unselectedForeground; 
    private Color unselectedBackground; 
    private JTextArea displayComponent;
          
    public ScrollableCellRenderer() {
    	displayComponent =new JTextArea();
    	EmptyBorder emptyBorder = new EmptyBorder(0,0,0,0);
    	setBorder(emptyBorder);
    	setViewportView(displayComponent);
    	displayComponent.setBorder(emptyBorder);
	    setOpaque(true);
    }

    public void setForeground(Color c) {
        super.setForeground(c); 
        unselectedForeground = c; 
    }
    
    public void setBackground(Color c) {
        super.setBackground(c); 
        unselectedBackground = c; 
    }

    public void updateUI() {
       super.updateUI(); 
	setForeground(null);
	setBackground(null);
    }
    
 
public Component getTableCellRendererComponent(JTable table, Object value,
                          boolean isSelected, boolean hasFocus, int row, int column) {

	if (isSelected) {
	    displayComponent.setForeground(table.getSelectionForeground());
	    displayComponent.setBackground(table.getSelectionBackground());
	}
	else {
	    displayComponent.setForeground((unselectedForeground != null) ? unselectedForeground 
	                                                       : table.getForeground());
	     displayComponent.setBackground((unselectedBackground != null) ? unselectedBackground 
	                                                       : table.getBackground());
	}
	
	setFont(table.getFont());

	if (hasFocus) 
	{
	   
	    if (table.isCellEditable(row, column)) { 
	         displayComponent.setForeground( UIManager.getColor("Table.focusCellForegrou

Comments
EVALUATION I don't believe this is a bug. It is a misunderstanding about how renderers work. There is typically only one renderer component per table column. This allows the Table to be fast and not use much RAM. There isn't actually a component for each cell in the table, thus they are not actually there to handle events. steve.wilson@eng 1998-05-28
28-05-1998

WORK AROUND Name: rk38400 Date: 05/08/98 none ====================================================================== You might try making an editor instead of a renderer. Editors actually get events. steve.wilson@eng 1998-05-28
28-05-1998