Nigel Sheridan-Smith
Nigel Sheridan-Smith

Reputation: 775

JTree nodes not updated (in Scala)

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

Answers (1)

Luismahou
Luismahou

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

Related Questions