-
Bug
-
Resolution: Fixed
-
P3
-
1.2.0, 1.2.1
-
1.2.2
-
generic, x86
-
generic, windows_95, windows_nt
changed from the default.
if (s.equals(CompanyPage.defaultContactName))
{
removeNodeFromParent(node);
return;
}
DefaultMutableTreeNode addressNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject addressObject = (CompanyTreeUserObject) addressNode.getUserObject();
}
else
{
// Cannot allow the description to be changed to
// the default name.
if (s.equals(CompanyPage.defaultContactName))
return;
DefaultMutableTreeNode addressNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject addressObject = (CompanyTreeUserObject) addressNode.getUserObject();
}
}
else if (userObject.getType() == CompanyTreeUserObject.METHOD)
{
// Check to see if this is a new method, if not then
// just change the name of the referenced method.
if (userObject.isNew())
{
// Only add a new method if the description
// has been changed from the default.
if (s.equals(CompanyPage.defaultMethodName))
{
removeNodeFromParent(node);
return;
}
DefaultMutableTreeNode contactNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject contactObject = (CompanyTreeUserObject) contactNode.getUserObject();
DefaultMutableTreeNode addressNode = (DefaultMutableTreeNode) contactNode.getParent();
CompanyTreeUserObject addressObject = (CompanyTreeUserObject) addressNode.getUserObject();
}
else
{
// Cannot allow the description to be changed to
// the default name.
if (s.equals(CompanyPage.defaultMethodName))
return;
DefaultMutableTreeNode contactNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject contactObject = (CompanyTreeUserObject) contactNode.getUserObject();
DefaultMutableTreeNode addressNode = (DefaultMutableTreeNode) contactNode.getParent();
CompanyTreeUserObject addressObject = (CompanyTreeUserObject) addressNode.getUserObject();
}
}
userObject.setName(s);
userObject.setNew(false);
nodeChanged(node);
}
// CellEditorListener interfaces.
public void editingCanceled(ChangeEvent event)
{
// Remove the node if it is temporary.
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent();
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
// Check to see if this is a new address, if not then
// just change the description of the referenced address.
if (userObject.isNew())
{
// Remove the address node if the description has
// not been changed from the default.
if (userObject.getName().equals(CompanyPage.defaultAddressName))
removeNodeFromParent(node);
}
}
}
public void editingStopped(ChangeEvent event)
{
// Just ignore this case.
}
}
class CompanyTreeUserObject
{
public static final int COMPANY = 0;
public static final int ADDRESS = 1;
public static final int CONTACT = 2;
public static final int METHOD = 3;
private String name = null;
private int type = -1;
private boolean isNew = false;
public CompanyTreeUserObject(String name, int type, boolean isNew)
{
this.name = name;
this.type = type;
this.isNew = isNew;
}
public String getName()
{
return name;
}
public void setName(String name)
{
if (name == null)
this.name = "";
else
this.name = name;
}
public int getType()
{
return type;
}
public boolean isNew()
{
return isNew;
}
public void setNew(boolean isNew)
{
this.isNew = isNew;
}
public String toString()
{
return name;
}
}
class CompanyTreeCellEditor implements TreeCellEditor, ActionListener, FocusListener, Serializable
{
protected EventListenerList listenerList = new EventListenerList();
transient protected ChangeEvent changeEvent = null;
protected JTextField editorComponent;
protected int clickCountToStart = 1;
public CompanyTreeCellEditor()
{
this.editorComponent = new JTextField();
this.clickCountToStart = 2;
editorComponent.addActionListener(this);
editorComponent.addFocusListener(this);
}
public Component getComponent()
{
return editorComponent;
}
public void setClickCountToStart(int count)
{
clickCountToStart = count;
}
/**
* clickCountToStart controls the number of clicks required to start
* editing if the event passed to isCellEditable() or startCellEditing() is
* a MouseEvent. For example, by default the clickCountToStart for
* a JTextField is set to 2, so in a JTable the user will need to
* double click to begin editing a cell.
*/
public int getClickCountToStart()
{
return clickCountToStart;
}
//
// Implementing the CellEditor Interface
//
public Object getCellEditorValue()
{
return editorComponent.getText();
}
public boolean isCellEditable(EventObject anEvent)
{
if (anEvent instanceof MouseEvent)
{
if (((MouseEvent)anEvent).getClickCount() < clickCountToStart)
return false;
}
return true;
}
public boolean shouldSelectCell(EventObject anEvent)
{
if (this.isCellEditable(anEvent))
{
if (anEvent == null)
{
editorComponent.requestFocus();
editorComponent.selectAll();
}
}
// By default we want the cell the be selected so
// we return true
return true;
}
public boolean stopCellEditing()
{
fireEditingStopped();
return true;
}
public void cancelCellEditing()
{
fireEditingCanceled();
}
//
// Implementing the TreeCellEditor Interface
//
public Component getTreeCellEditorComponent(JTree tree, Object value,
boolean isSelected, boolean expanded, boolean leaf, int row)
{
String stringValue = tree.convertValueToText(value, isSelected, expanded, leaf, row, false);
editorComponent.setText(stringValue);
return editorComponent;
}
// Implementing ActionListener interface
public void actionPerformed(ActionEvent e)
{
fireEditingStopped();
}
//Implements FocusListener interface
public void focusGained(FocusEvent event)
{
}
public void focusLost(FocusEvent event)
{
fireEditingStopped();
}
//
// 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);
}
}
}
/*
* 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 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);
}
}
}
}
(Review ID: 41968)
======================================================================
Name: rk38400 Date: 11/10/98
I am using jdk1.2rc1 and Win95.
The example I am including is somewhat large, but the
simplest example that I created did not exhibit the
problem. Since the example code is hacked out of my
code it should be considered a fragment, since its
functionality is gutted.
At any rate, there are two files below that create a
window that has a JTree and two buttons to add and remove
nodes from the tree. To view the problem run the program
and then select the root node in the tree. Click on the
"add" button to add a child; the child node will automatically
become editable, so type in "Test" and press return. In most
cases, by double-clicking on the child node to edit it again
a null exception will be thrown. It may require the
editing process to be repeated.
I noticed this bug in swing-1.1beta3 too.
FILE: TreeFrame.java
--------------------
package Tree;
import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;
public class TreeFrame extends JFrame
{
private JTree tree = null;
public TreeFrame()
{
super("Tree Test");
setSize(400, 400);
getContentPane().add(new CompanyPage(), BorderLayout.CENTER);
setVisible(true);
}
public static void main(String[] argv)
{
TreeFrame obj = new TreeFrame();
}
}
FILE: CompanyPage.java
======================
package Tree;
import java.lang.Boolean;
import java.util.EventObject;
import java.io.Serializable;
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
public class CompanyPage extends JPanel
{
protected static final String defaultAddressName = "[new address]";
protected static final String defaultContactName = "[new contact]";
protected static final String defaultMethodName = "[new method]";
private CompanyTree tree = null;
private CompanyTreeModel treeModel = null;
private JButton addButton = null;
private JButton removeButton = null;
public CompanyPage()
{
super();
// Create and set layout.
GridBagLayout grid = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(grid);
// Create controls, labels, and fields.
JLabel label = null;
c.insets = new Insets(2, 2, 2, 2);
// Tree control
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 3;
c.gridheight = 5;
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.BOTH;
c.weightx = 0.5;
c.weighty = 1.0;
JScrollPane scroll = null;
grid.setConstraints(scroll = new JScrollPane(tree = new CompanyTree()), c);
add(scroll);
treeModel = (CompanyTreeModel) tree.getModel();
tree.setEditable(true);
c.fill = GridBagConstraints.NONE;
c.weightx = 0.0;
c.weighty = 0.0;
// Add button.
c.gridx = 3;
c.gridy = 2;
c.gridwidth = 3;
c.gridheight = 1;
c.anchor = GridBagConstraints.CENTER;
grid.setConstraints(addButton = new JButton("Add"), c);
add(addButton);
// Remove button.
c.gridx = 6;
c.gridy = 2;
c.gridwidth = 3;
c.gridheight = 1;
c.anchor = GridBagConstraints.CENTER;
grid.setConstraints(removeButton = new JButton("Remove"), c);
add(removeButton);
// Create necessary event listeners.
createEventListeners();
}
private void createEventListeners()
{
// Create action listeners.
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event)
{
// When the add button is clicked we don't actually
// want to modify the company object. First we want
// to put a place holder in the tree control and let
// the user edit it. The follow code does this. If
// the user edits the code then the CompanyTree member
// valueForPathChanged() where the company object is
// actually modified.
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null)
return;
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
// We can't add anything to a node that is itself
// yet added to the company object.
if (userObject.isNew())
return;
if (userObject.getType() == CompanyTreeUserObject.COMPANY)
{
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new CompanyTreeUserObject(defaultAddressName, CompanyTreeUserObject.ADDRESS, true));
treeModel.insertNodeInto(newNode, node, node.getChildCount());
TreePath path = new TreePath(newNode.getPath());
tree.grabFocus();
tree.setSelectionPath(path);
tree.startEditingAtPath(path);
}
else if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new CompanyTreeUserObject(defaultContactName, CompanyTreeUserObject.CONTACT, true));
treeModel.insertNodeInto(newNode, node, node.getChildCount());
TreePath path = new TreePath(newNode.getPath());
tree.grabFocus();
tree.setSelectionPath(path);
tree.startEditingAtPath(path);
}
else if (userObject.getType() == CompanyTreeUserObject.CONTACT)
{
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new CompanyTreeUserObject(defaultMethodName, CompanyTreeUserObject.METHOD, true));
treeModel.insertNodeInto(newNode, node, node.getChildCount());
TreePath path = new TreePath(newNode.getPath());
tree.grabFocus();
tree.setSelectionPath(path);
tree.startEditingAtPath(path);
}
else if (userObject.getType() == CompanyTreeUserObject.METHOD)
{
node = (DefaultMutableTreeNode) node.getParent();
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new CompanyTreeUserObject(defaultMethodName, CompanyTreeUserObject.METHOD, true));
treeModel.insertNodeInto(newNode, node, node.getChildCount());
TreePath path = new TreePath(newNode.getPath());
tree.grabFocus();
tree.setSelectionPath(path);
tree.startEditingAtPath(path);
}
}
});
removeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event)
{
// When the remove button is clicked we actually
// want to remove the selected node from the company
// object. We should also remove it from the tree
// control as well.
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null)
return;
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
treeModel.removeNodeFromParent(node);
}
else if (userObject.getType() == CompanyTreeUserObject.CONTACT)
{
DefaultMutableTreeNode addrNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject addrObject = (CompanyTreeUserObject) addrNode.getUserObject();
treeModel.removeNodeFromParent(node);
}
else if (userObject.getType() == CompanyTreeUserObject.METHOD)
{
DefaultMutableTreeNode contactNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject contactObject = (CompanyTreeUserObject) contactNode.getUserObject();
DefaultMutableTreeNode addrNode = (DefaultMutableTreeNode) contactNode.getParent();
CompanyTreeUserObject addrObject = (CompanyTreeUserObject) addrNode.getUserObject();
treeModel.removeNodeFromParent(node);
}
}
});
// Create tree selection listener.
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent event)
{
updateState();
}
});
}
private void updateState()
{
// Start with buttons disabled.
addButton.setEnabled(false);
removeButton.setEnabled(false);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null)
return;
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
if (userObject.getType() == CompanyTreeUserObject.COMPANY)
{
addButton.setText("Add address");
removeButton.setText("Remove");
addButton.setEnabled(true);
}
else if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
addButton.setText("Add contact");
removeButton.setText("Remove address");
addButton.setEnabled(true);
removeButton.setEnabled(true);
}
else if (userObject.getType() == CompanyTreeUserObject.CONTACT)
{
addButton.setText("Add method");
removeButton.setText("Remove contact");
addButton.setEnabled(true);
removeButton.setEnabled(true);
}
else if (userObject.getType() == CompanyTreeUserObject.METHOD)
{
addButton.setText("Add method");
removeButton.setText("Remove method");
addButton.setEnabled(true);
removeButton.setEnabled(true);
}
validate();
}
}
class CompanyTree extends JTree
{
private boolean currentlyEditable = true;
public CompanyTree()
{
CompanyTreeModel treeModel = new CompanyTreeModel(new DefaultMutableTreeNode(new CompanyTreeUserObject("[no name]", CompanyTreeUserObject.COMPANY, false)), this);
setModel(treeModel);
CompanyTreeCellEditor editor = new CompanyTreeCellEditor();
setCellEditor(editor);
editor.addCellEditorListener(treeModel);
}
/**
* Needed to create this property since the
* tree component already has an implementation
* for isEditable() that is used to determine
* whether a cell editor is necessary. Since
* we want a cell editor we cannot set that to
* false or else we don't get one.
*/
public boolean isCurrentlyEditable()
{
return currentlyEditable;
}
/**
* Needed to create this property since the
* tree component already has an implementation
* for setEditable() that is used to determine
* whether a cell editor is necessary. Since
* we want a cell editor we cannot set that to
* false or else we don't get one.
*/
public void setCurrentlyEditable(boolean b)
{
currentlyEditable = b;
}
public boolean isPathEditable(TreePath path)
{
if (!isCurrentlyEditable())
return false;
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) getLastSelectedPathComponent();
if (treeNode == null)
return false;
CompanyTreeUserObject userObject = (CompanyTreeUserObject) treeNode.getUserObject();
if (userObject == null)
return false;
if (userObject.getType() == CompanyTreeUserObject.COMPANY)
return false;
return isEditable();
}
}
/**
* CompanyTreeModel extends DefaultTreeModel to override valueForPathChanged.
* This method is called as a result of the user editing a value in
* the tree. If you allow editing in your tree, are using TreeNodes
* and the user object of the TreeNodes is not a String, then you're going
* to have to subclass JTreeModel as this example does.
*/
class CompanyTreeModel extends DefaultTreeModel implements CellEditorListener
{
private JTree tree = null;
public CompanyTreeModel(DefaultMutableTreeNode root, JTree tree)
{
super(root);
this.tree = tree;
}
public void valueForPathChanged(TreePath path, Object newValue)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
// If the new name is an empty string, then
// just keep the old name.
String s = (String) newValue;
if (s.length() == 0)
s = userObject.getName();
// Change the value of the user object and update
// the company object to reflect the new value
// depending on the type of the user object.
if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
// Check to see if this is a new address, if not then
// just change the description of the referenced address.
if (userObject.isNew())
{
// Only add a new address if the description
// has been changed from the default.
if (s.equals(CompanyPage.defaultAddressName))
{
removeNodeFromParent(node);
return;
}
}
else
{
// Cannot allow the description to be changed to
// the default name.
if (s.equals(CompanyPage.defaultAddressName))
return;
}
}
else if (userObject.getType() == CompanyTreeUserObject.CONTACT)
{
// Check to see if this is a new contact, if not then
// just change the name of the referenced contact.
if (userObject.isNew())
{
// Only add a new contact if the name
// has been
if (s.equals(CompanyPage.defaultContactName))
{
removeNodeFromParent(node);
return;
}
DefaultMutableTreeNode addressNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject addressObject = (CompanyTreeUserObject) addressNode.getUserObject();
}
else
{
// Cannot allow the description to be changed to
// the default name.
if (s.equals(CompanyPage.defaultContactName))
return;
DefaultMutableTreeNode addressNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject addressObject = (CompanyTreeUserObject) addressNode.getUserObject();
}
}
else if (userObject.getType() == CompanyTreeUserObject.METHOD)
{
// Check to see if this is a new method, if not then
// just change the name of the referenced method.
if (userObject.isNew())
{
// Only add a new method if the description
// has been changed from the default.
if (s.equals(CompanyPage.defaultMethodName))
{
removeNodeFromParent(node);
return;
}
DefaultMutableTreeNode contactNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject contactObject = (CompanyTreeUserObject) contactNode.getUserObject();
DefaultMutableTreeNode addressNode = (DefaultMutableTreeNode) contactNode.getParent();
CompanyTreeUserObject addressObject = (CompanyTreeUserObject) addressNode.getUserObject();
}
else
{
// Cannot allow the description to be changed to
// the default name.
if (s.equals(CompanyPage.defaultMethodName))
return;
DefaultMutableTreeNode contactNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject contactObject = (CompanyTreeUserObject) contactNode.getUserObject();
DefaultMutableTreeNode addressNode = (DefaultMutableTreeNode) contactNode.getParent();
CompanyTreeUserObject addressObject = (CompanyTreeUserObject) addressNode.getUserObject();
}
}
userObject.setName(s);
userObject.setNew(false);
nodeChanged(node);
}
// CellEditorListener interfaces.
public void editingCanceled(ChangeEvent event)
{
// Remove the node if it is temporary.
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent();
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
// Check to see if this is a new address, if not then
// just change the description of the referenced address.
if (userObject.isNew())
{
// Remove the address node if the description has
// not been changed from the default.
if (userObject.getName().equals(CompanyPage.defaultAddressName))
removeNodeFromParent(node);
}
}
}
public void editingStopped(ChangeEvent event)
{
// Just ignore this case.
}
}
class CompanyTreeUserObject
{
public static final int COMPANY = 0;
public static final int ADDRESS = 1;
public static final int CONTACT = 2;
public static final int METHOD = 3;
private String name = null;
private int type = -1;
private boolean isNew = false;
public CompanyTreeUserObject(String name, int type, boolean isNew)
{
this.name = name;
this.type = type;
this.isNew = isNew;
}
public String getName()
{
return name;
}
public void setName(String name)
{
if (name == null)
this.name = "";
else
this.name = name;
}
public int getType()
{
return type;
}
public boolean isNew()
{
return isNew;
}
public void setNew(boolean isNew)
{
this.isNew = isNew;
}
public String toString()
{
return name;
}
}
class CompanyTreeCellEditor implements TreeCellEditor, ActionListener, FocusListener, Serializable
{
protected EventListenerList listenerList = new EventListenerList();
transient protected ChangeEvent changeEvent = null;
protected JTextField editorComponent;
protected int clickCountToStart = 1;
public CompanyTreeCellEditor()
{
this.editorComponent = new JTextField();
this.clickCountToStart = 2;
editorComponent.addActionListener(this);
editorComponent.addFocusListener(this);
}
public Component getComponent()
{
return editorComponent;
}
public void setClickCountToStart(int count)
{
clickCountToStart = count;
}
/**
* clickCountToStart controls the number of clicks required to start
* editing if the event passed to isCellEditable() or startCellEditing() is
* a MouseEvent. For example, by default the clickCountToStart for
* a JTextField is set to 2, so in a JTable the user will need to
* double click to begin editing a cell.
*/
public int getClickCountToStart()
{
return clickCountToStart;
}
//
// Implementing the CellEditor Interface
//
public Object getCellEditorValue()
{
return editorComponent.getText();
}
public boolean isCellEditable(EventObject anEvent)
{
if (anEvent instanceof MouseEvent)
{
if (((MouseEvent)anEvent).getClickCount() < clickCountToStart)
return false;
}
return true;
}
public boolean shouldSelectCell(EventObject anEvent)
{
if (this.isCellEditable(anEvent))
{
if (anEvent == null)
{
editorComponent.requestFocus();
editorComponent.selectAll();
}
}
// By default we want the cell the be selected so
// we return true
return true;
}
public boolean stopCellEditing()
{
fireEditingStopped();
return true;
}
public void cancelCellEditing()
{
fireEditingCanceled();
}
//
// Implementing the TreeCellEditor Interface
//
public Component getTreeCellEditorComponent(JTree tree, Object value,
boolean isSelected, boolean expanded, boolean leaf, int row)
{
String stringValue = tree.convertValueToText(value, isSelected, expanded, leaf, row, false);
editorComponent.setText(stringValue);
return editorComponent;
}
// Implementing ActionListener interface
public void actionPerformed(ActionEvent e)
{
fireEditingStopped();
}
//Implements FocusListener interface
public void focusGained(FocusEvent event)
{
}
public void focusLost(FocusEvent event)
{
fireEditingStopped();
}
//
// 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);
}
}
}
/*
* 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 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);
}
}
}
}
(Review ID: 41968)
======================================================================
Name: rk38400 Date: 11/10/98
I am using jdk1.2rc1 and Win95.
The example I am including is somewhat large, but the
simplest example that I created did not exhibit the
problem. Since the example code is hacked out of my
code it should be considered a fragment, since its
functionality is gutted.
At any rate, there are two files below that create a
window that has a JTree and two buttons to add and remove
nodes from the tree. To view the problem run the program
and then select the root node in the tree. Click on the
"add" button to add a child; the child node will automatically
become editable, so type in "Test" and press return. In most
cases, by double-clicking on the child node to edit it again
a null exception will be thrown. It may require the
editing process to be repeated.
I noticed this bug in swing-1.1beta3 too.
FILE: TreeFrame.java
--------------------
package Tree;
import java.awt.*;
import javax.swing.*;
import javax.swing.tree.*;
public class TreeFrame extends JFrame
{
private JTree tree = null;
public TreeFrame()
{
super("Tree Test");
setSize(400, 400);
getContentPane().add(new CompanyPage(), BorderLayout.CENTER);
setVisible(true);
}
public static void main(String[] argv)
{
TreeFrame obj = new TreeFrame();
}
}
FILE: CompanyPage.java
======================
package Tree;
import java.lang.Boolean;
import java.util.EventObject;
import java.io.Serializable;
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;
public class CompanyPage extends JPanel
{
protected static final String defaultAddressName = "[new address]";
protected static final String defaultContactName = "[new contact]";
protected static final String defaultMethodName = "[new method]";
private CompanyTree tree = null;
private CompanyTreeModel treeModel = null;
private JButton addButton = null;
private JButton removeButton = null;
public CompanyPage()
{
super();
// Create and set layout.
GridBagLayout grid = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
setLayout(grid);
// Create controls, labels, and fields.
JLabel label = null;
c.insets = new Insets(2, 2, 2, 2);
// Tree control
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 3;
c.gridheight = 5;
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.BOTH;
c.weightx = 0.5;
c.weighty = 1.0;
JScrollPane scroll = null;
grid.setConstraints(scroll = new JScrollPane(tree = new CompanyTree()), c);
add(scroll);
treeModel = (CompanyTreeModel) tree.getModel();
tree.setEditable(true);
c.fill = GridBagConstraints.NONE;
c.weightx = 0.0;
c.weighty = 0.0;
// Add button.
c.gridx = 3;
c.gridy = 2;
c.gridwidth = 3;
c.gridheight = 1;
c.anchor = GridBagConstraints.CENTER;
grid.setConstraints(addButton = new JButton("Add"), c);
add(addButton);
// Remove button.
c.gridx = 6;
c.gridy = 2;
c.gridwidth = 3;
c.gridheight = 1;
c.anchor = GridBagConstraints.CENTER;
grid.setConstraints(removeButton = new JButton("Remove"), c);
add(removeButton);
// Create necessary event listeners.
createEventListeners();
}
private void createEventListeners()
{
// Create action listeners.
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event)
{
// When the add button is clicked we don't actually
// want to modify the company object. First we want
// to put a place holder in the tree control and let
// the user edit it. The follow code does this. If
// the user edits the code then the CompanyTree member
// valueForPathChanged() where the company object is
// actually modified.
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null)
return;
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
// We can't add anything to a node that is itself
// yet added to the company object.
if (userObject.isNew())
return;
if (userObject.getType() == CompanyTreeUserObject.COMPANY)
{
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new CompanyTreeUserObject(defaultAddressName, CompanyTreeUserObject.ADDRESS, true));
treeModel.insertNodeInto(newNode, node, node.getChildCount());
TreePath path = new TreePath(newNode.getPath());
tree.grabFocus();
tree.setSelectionPath(path);
tree.startEditingAtPath(path);
}
else if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new CompanyTreeUserObject(defaultContactName, CompanyTreeUserObject.CONTACT, true));
treeModel.insertNodeInto(newNode, node, node.getChildCount());
TreePath path = new TreePath(newNode.getPath());
tree.grabFocus();
tree.setSelectionPath(path);
tree.startEditingAtPath(path);
}
else if (userObject.getType() == CompanyTreeUserObject.CONTACT)
{
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new CompanyTreeUserObject(defaultMethodName, CompanyTreeUserObject.METHOD, true));
treeModel.insertNodeInto(newNode, node, node.getChildCount());
TreePath path = new TreePath(newNode.getPath());
tree.grabFocus();
tree.setSelectionPath(path);
tree.startEditingAtPath(path);
}
else if (userObject.getType() == CompanyTreeUserObject.METHOD)
{
node = (DefaultMutableTreeNode) node.getParent();
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(new CompanyTreeUserObject(defaultMethodName, CompanyTreeUserObject.METHOD, true));
treeModel.insertNodeInto(newNode, node, node.getChildCount());
TreePath path = new TreePath(newNode.getPath());
tree.grabFocus();
tree.setSelectionPath(path);
tree.startEditingAtPath(path);
}
}
});
removeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event)
{
// When the remove button is clicked we actually
// want to remove the selected node from the company
// object. We should also remove it from the tree
// control as well.
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null)
return;
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
treeModel.removeNodeFromParent(node);
}
else if (userObject.getType() == CompanyTreeUserObject.CONTACT)
{
DefaultMutableTreeNode addrNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject addrObject = (CompanyTreeUserObject) addrNode.getUserObject();
treeModel.removeNodeFromParent(node);
}
else if (userObject.getType() == CompanyTreeUserObject.METHOD)
{
DefaultMutableTreeNode contactNode = (DefaultMutableTreeNode) node.getParent();
CompanyTreeUserObject contactObject = (CompanyTreeUserObject) contactNode.getUserObject();
DefaultMutableTreeNode addrNode = (DefaultMutableTreeNode) contactNode.getParent();
CompanyTreeUserObject addrObject = (CompanyTreeUserObject) addrNode.getUserObject();
treeModel.removeNodeFromParent(node);
}
}
});
// Create tree selection listener.
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent event)
{
updateState();
}
});
}
private void updateState()
{
// Start with buttons disabled.
addButton.setEnabled(false);
removeButton.setEnabled(false);
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null)
return;
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
if (userObject.getType() == CompanyTreeUserObject.COMPANY)
{
addButton.setText("Add address");
removeButton.setText("Remove");
addButton.setEnabled(true);
}
else if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
addButton.setText("Add contact");
removeButton.setText("Remove address");
addButton.setEnabled(true);
removeButton.setEnabled(true);
}
else if (userObject.getType() == CompanyTreeUserObject.CONTACT)
{
addButton.setText("Add method");
removeButton.setText("Remove contact");
addButton.setEnabled(true);
removeButton.setEnabled(true);
}
else if (userObject.getType() == CompanyTreeUserObject.METHOD)
{
addButton.setText("Add method");
removeButton.setText("Remove method");
addButton.setEnabled(true);
removeButton.setEnabled(true);
}
validate();
}
}
class CompanyTree extends JTree
{
private boolean currentlyEditable = true;
public CompanyTree()
{
CompanyTreeModel treeModel = new CompanyTreeModel(new DefaultMutableTreeNode(new CompanyTreeUserObject("[no name]", CompanyTreeUserObject.COMPANY, false)), this);
setModel(treeModel);
CompanyTreeCellEditor editor = new CompanyTreeCellEditor();
setCellEditor(editor);
editor.addCellEditorListener(treeModel);
}
/**
* Needed to create this property since the
* tree component already has an implementation
* for isEditable() that is used to determine
* whether a cell editor is necessary. Since
* we want a cell editor we cannot set that to
* false or else we don't get one.
*/
public boolean isCurrentlyEditable()
{
return currentlyEditable;
}
/**
* Needed to create this property since the
* tree component already has an implementation
* for setEditable() that is used to determine
* whether a cell editor is necessary. Since
* we want a cell editor we cannot set that to
* false or else we don't get one.
*/
public void setCurrentlyEditable(boolean b)
{
currentlyEditable = b;
}
public boolean isPathEditable(TreePath path)
{
if (!isCurrentlyEditable())
return false;
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) getLastSelectedPathComponent();
if (treeNode == null)
return false;
CompanyTreeUserObject userObject = (CompanyTreeUserObject) treeNode.getUserObject();
if (userObject == null)
return false;
if (userObject.getType() == CompanyTreeUserObject.COMPANY)
return false;
return isEditable();
}
}
/**
* CompanyTreeModel extends DefaultTreeModel to override valueForPathChanged.
* This method is called as a result of the user editing a value in
* the tree. If you allow editing in your tree, are using TreeNodes
* and the user object of the TreeNodes is not a String, then you're going
* to have to subclass JTreeModel as this example does.
*/
class CompanyTreeModel extends DefaultTreeModel implements CellEditorListener
{
private JTree tree = null;
public CompanyTreeModel(DefaultMutableTreeNode root, JTree tree)
{
super(root);
this.tree = tree;
}
public void valueForPathChanged(TreePath path, Object newValue)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
CompanyTreeUserObject userObject = (CompanyTreeUserObject) node.getUserObject();
// If the new name is an empty string, then
// just keep the old name.
String s = (String) newValue;
if (s.length() == 0)
s = userObject.getName();
// Change the value of the user object and update
// the company object to reflect the new value
// depending on the type of the user object.
if (userObject.getType() == CompanyTreeUserObject.ADDRESS)
{
// Check to see if this is a new address, if not then
// just change the description of the referenced address.
if (userObject.isNew())
{
// Only add a new address if the description
// has been changed from the default.
if (s.equals(CompanyPage.defaultAddressName))
{
removeNodeFromParent(node);
return;
}
}
else
{
// Cannot allow the description to be changed to
// the default name.
if (s.equals(CompanyPage.defaultAddressName))
return;
}
}
else if (userObject.getType() == CompanyTreeUserObject.CONTACT)
{
// Check to see if this is a new contact, if not then
// just change the name of the referenced contact.
if (userObject.isNew())
{
// Only add a new contact if the name
// has been
- duplicates
-
JDK-4234727 BasicTreeUI's MouseInputHandler does not forward mouse events properly
-
- Closed
-