Reputation: 775
I have an issue with the interoperability between Scala and the Swing JTree component (Java).
The JTree does not update correctly, except when I stop the display of the JOptionPane to prompt the user for the name of the new entity. This line is marked with [* * *]. As you can see, I have provided static text "xxx" instead, commenting out the call to the JOptionPane method. The JTree updates correctly as expected in this case.
I thought it might be something to do with the Swing threading model, but wrapping the update text in a Runnable class is not fixing the issue. See also Why isn't my JTree updating when the TreeModel adds new nodes?
Why would the JOptionPane prevent the JTree from updating the nodes correctly?
I am doing it this way because Scala does not yet have a Swing Tree implementation that allows dynamic updates. See http://github.com/kenbot/ScalaSwingTreeWrapper
Any tips or pointers would be greatly appreciated.
Cheers,
Nigel
import scala.swing._
import javax.swing.{JOptionPane,JTree,SwingUtilities}
import javax.swing.tree.{DefaultTreeModel,DefaultMutableTreeNode,TreePath}
object XApp extends SimpleSwingApplication {
val APP_NAME: String = "AppName"
def getNameDialog(q: String): String =
{
JOptionPane.showInputDialog(top.self, q, APP_NAME, JOptionPane.PLAIN_MESSAGE);
}
def menuProjectNewX = {
// Get the name of the X
var name = "xxx"; // getNameDialog ("Enter the name of the X:") [***]
def doUpdate = new Runnable() {
def run()
{
pl ("Running");
// Get the root
var root = treeModel.getRoot().asInstanceOf[DefaultMutableTreeNode]
// Insert new object
var newNode = new DefaultMutableTreeNode(name)
treeModel.insertNodeInto(newNode, root, root.getChildCount())
// Expand the tree
var tp = new TreePath(newNode.getPath().asInstanceOf[Array[Object]])
tree.scrollPathToVisible(tp)
}
}
SwingUtilities.invokeLater(doUpdate);
}
var tree: JTree = null
var treeModel: DefaultTreeModel = null
var flow: FlowPanel = null
def top = new MainFrame {
// Create the menu bar
menuBar = new MenuBar() {
contents += new Menu("Project") {
contents += new MenuItem(Action("New X...") { menuProjectNewX })
}
}
title = APP_NAME
preferredSize = new Dimension (1000, 800)
location = new Point(50,50)
treeModel = new DefaultTreeModel(new DefaultMutableTreeNode("(root)"))
tree = new JTree(treeModel)
//flow = new FlowPanel
var splitPane = new SplitPane (Orientation.Vertical, new Component {
override lazy val peer = tree
}, new FlowPanel)
splitPane.dividerLocation = 250
contents = splitPane
}
}
Upvotes: 1
Views: 423
Reputation: 654
The problem is that every time the JOptionPane
is shown, you're creating a new Frame, instead of reusing the MainFrame. Take in mind that top
is a method, and when you refer to 'top' to show the JOptionPane
you're creating a new MainFrame
. So, at the end, you're adding the node to a tree that is in a MainFrame different from the one is being displayed.
One way to solve this is to simply store the MainFrame in a variable:
var mainFrame: MainFrame = null
def top =
{
mainFrame = new MainFrame {
// Rest of the code
}
mainFrame
}
}
// To show the JOptionPane
JOptionPane.showInputDialog(mainFrame.self, q, APP_NAME, JOptionPane.PLAIN_MESSAGE);
Upvotes: 1