Display updated tree table value from another tree table - java

I am trying to make a scene editor to go with my rendering engine. I am using swing to make the GUI and also swingx for its JXTreeTable component. Everything is working fine, except that the Scene tree table is not updating the names of the nodes automatically as I would like. For example, in the next image, I change the name of one of the nodes, and nothing seems to happen. However if I then move my mouse over the node in the Scene box (the one at the top) the name gets updated.
I have two JXTreeTable, and two models which extend AbstractTreeTableModel.
Here is the relevant code for the Properties model.
public class PropertiesModel extends AbstractTreeTableModel{
private EE_Property root;
private SceneModel mSceneModel;
private EE_SceneObject sceneSelection;
...
#Override
public void setValueAt(Object value, Object node, int column){
((EE_Property)node).setValue((String)value);
// Updates the values of the current scene selection
sceneSelection.setProperties(root);
TreePath path = new TreePath(sceneSelection.getParent());
int index = mSceneModel.getIndexOfChild(sceneSelection.getParent(), sceneSelection);
// This is where I thought the updating of the scene model would happen and thus redraw it correctly
mSceneModel.getTreeModelSupport().fireChildChanged(path, index, sceneSelection);
}
}
I thought that using fireChildChanged() would update the scene tree table as I wanted.
If I call fireChildChanged() with index=0, I can get the Root node to update when I rename it, but any other index I have to wait till I move the mouse over it to update.
Edit: problem solved
I tried the redraw method suggested by #Shakedown which partially worked but sometimes would leave "..." after the text if the new text was longer than the original.
I did however realize that the problem was coming from the TreePath not being generated properly. When using TreePath path = new TreePath(sceneSelection.getParent());, the path's parent was null, thus not allowing the tree to update. I now use this code which works :
// mTT is the scene tree table
TreePath nodePath = mSceneModel.mTT.getTreeSelectionModel().getSelectionPath();
int index = mSceneModel.getIndexOfChild(sceneSelection.getParent(), sceneSelection);
mSceneModel.getTreeModelSupport().fireChildChanged(nodePath.getParentPath(), index, sceneSelection);

You're notifying the listeners of the SceneModel which is not the tree-table that you want to update. Look for some similar fireXXX methods on the AbstractTreeTableModel class and call those, which will notify the JXTreeTable and it will redraw itself.
It looks like the one you want is fireTreeNodesChanged(...), so play around with that and figure out what parameters you need to pass in.

Related

Move neighboor components on JavaFx ScaleTransition

I have a GridPane containing some Nodes (of unknown types). There is some nodes on the middle of this GridPane who should disappear progressively when a method is called (500ms).
My first step was to simply make it disappear using (each node have this method, will be called when the event to show or hide nodes occurs)
public void expandNode(boolean isExpanded) {
node.setVisible(isExpanded);
node.setManaged(isExpanded);
}
My second step was to use ScaleTransition like this:
public void expandNode(boolean isExpanded) {
ScaleTransition transition = new ScaleTransition(Duration.millis(500), node);
transition.setToY(isExpanded?1:0);
transition.play();
}
But the problem (that was corrected using node.setManaged when not using timed transition) is that my grid doesn't resize like it does when adding or removing nodes from it.
Lets see an example:
Action expand should hidde/expand an entire line for my grid. I want when my line is hidden, the following lines to go up, but I want that all my columns have their sizes unchanged (like if line was always here).
If you have any idea of how to do something like this, I need your help !
Thank you !

Drag and Drop in JTree - can importData modify the tree?

I am experiencing some difficulty with a drag and drop in the JTree component. My drag and drop is to allow user rearrange nodes in the tree. My implementation works mostly fine, but in some situations there is a null pointer exception when moving a node in between of its children, like on the picture below I am dragging C under B (between B and A):
The direct cause of the exception seems to be that while performing cleanup of DnD framework, path to the item being dragged was invalidated by the changes I have performed on the tree to implement the required move.
More specifically, I get a callstack like this:
javax.swing.plaf.basic.BasicTreeUI#getDropLineRect
java.beans.PropertyChangeSupport#firePropertyChange
...
javax.swing.TransferHandler.DropHandler#cleanup
...
javax.swing.TransferHandler.SwingDropTarget#drop
The getDropLineRect is clearly attempting to evaluate a rectangle for a path in the tree which no longer exists, as the node was already deleted when the move was performed in my importData function.
My question is: is this expected? Is it considered unsafe to perform data changes while DnD has not completed yet, and should they be queued and performed later? I did not see any such requirement in any documentation, and I did not see such async implementation in any of the examples or tutorials.
IMHO it would not only be saver - as you stated yourself - to do any data modifications AFTER the drop occured, but also easier to implement.
If you already remove the node on the drag event, you need to have a rollback at hand to handle the situation of removing the node from you window or making an illegal drop.
On the other hand, if you use the drag event to just gain the needed information for a successful drop and don't change anything in your data yet, the previously stated problems dissapear. On drop, you would use the remembered information from the drag and complete the whole operation, or discard the drop, which in return rollbacks the visual actions (though I'm not 100% sure here), thus remaining a consistent state all the time.
Edit: "My question is: is this expected?": I think the Exception occurs from your logic of changing the tree model in between the drag and drop, making it inconsitent at a certain time for the event dispatching thread trying to redraw it from the model. Apologies for not being able to state "this is how it's done", maybe someone else finds a quote from the swing tree dnd doc.
I still can not give a general statement, but I think it's always a good idea to look at the standard implementations, in this case, at the TreeModel. You did not state what kind of TreeModel you use. If you a custom one, you have to fire the "tree changed event" yourself, to tell the tree something changed. I found this excerpt from an example for dnd, using the DefaultTreeModel. Its ready to run and I tried to build the error you described but it seemed to work fine. The line "model.insertNodeInto" fires the appropriate event in the DefaultTreeModel. http://www.coderanch.com/t/346509/GUI/java/JTree-drag-drop-tree-Java
Notice that the event is fired during the importData and before the clean up calling, where your exception occurs.
I remember that these events only differ in the value of the arguements passed to the same constructor. I also had problems getting to fire the right event for the tree to understand me. At some point I ended up copying the calling lines from the DefaultTreeModel source and used it in my code so it just worked. The docu on this is not very specific.
public boolean importData(TransferHandler.TransferSupport support) {
if(!canImport(support)) {
return false;
}
// Extract transfer data.
DefaultMutableTreeNode[] nodes = null;
try {
Transferable t = support.getTransferable();
nodes = (DefaultMutableTreeNode[])t.getTransferData(nodesFlavor);
} catch(UnsupportedFlavorException ufe) {
System.out.println("UnsupportedFlavor: " + ufe.getMessage());
} catch(java.io.IOException ioe) {
System.out.println("I/O error: " + ioe.getMessage());
}
// Get drop location info.
JTree.DropLocation dl =
(JTree.DropLocation)support.getDropLocation();
int childIndex = dl.getChildIndex();
TreePath dest = dl.getPath();
DefaultMutableTreeNode parent =
(DefaultMutableTreeNode)dest.getLastPathComponent();
JTree tree = (JTree)support.getComponent();
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
// Configure for drop mode.
int index = childIndex; // DropMode.INSERT
if(childIndex == -1) { // DropMode.ON
index = parent.getChildCount();
}
// Add data to model.
for(int i = 0; i < nodes.length; i++) {
model.insertNodeInto(nodes[i], parent, index++);
}
return true;
}
Default tree model source: http://developer.classpath.org/doc/javax/swing/tree/DefaultTreeModel-source.html
Edit: I think what also worked for me was firing a general "whole tree structure changed" event, which causes the tree to be built up from scratch from the model state. I think it was the Event constructor with all the specifics having a null value. Of course this might take some more time that just firing the specific event for the actual changes.

ResizeLayoutPanel inside DisclosurePanel using GWT

I'm having problems using ResizeLayoutPanel inside DisclosurePanel.
I have a Datagrid into ResizeLayoutPanel, and ResizeLayaoutPanel inside DisclosurePanel. The problem is when data is loaded, If DisclosurePanel was closed, when user opens disclosurePanel it finds the table empty. If user does this action when DisclosurePanel is open, works fine.
Any idea how can I solve this problem?
This is the code:
ResizeLayoutPanel resizeLayoutPanel = new ResizeLayoutPanel();
resizeLayoutPanel.setWidth("100%");
resizeLayoutPanel.setStyleName("gwt-resizeLayoutPanel-Example");
/****CREATE COLUMNS [BEGIN] ***************************************************************************/
// Create column.
TextColumn<Object> column = new TextColumn<Object>() {
#Override
public String getValue(Object object) {
return "Test";
}
};
/******CREATE COLUMNS [END] ***************************************************************************/
cellTable = new DataGrid<Object>();
cellTable.addColumn(column, "Example");
cellTable.setKeyboardSelectionPolicy(HasKeyboardSelectionPolicy.KeyboardSelectionPolicy.BOUND_TO_SELECTION);
cellTable.setWidth("100%");
cellTable.setEmptyTableWidget(new Label("Example table"));
resizeLayoutPanel.add(cellTable);
//Link dataProvider with CellTable
dataProvider.addDataDisplay(cellTable);
this.container = ModuleContainer.getAdvancedDisclosurePanel("Samples");
this.container.setWidth("100%");
this.container.setContent(resizeLayoutPanel);
return this.container;
ResizeLayoutPanel provides resize (implements ProvidesResize) but does not requires resize (does not implement RequiresResize).
Since ResizeLayoutPanel in its plain state does not listen to resize, you have to deliberately resize it, or write code to resize it. Perhaps, set width and height to 100%.
And then, why did you place the table inside a ResizeLayoutPanel ?
ResizeLayoutPanel provides resize to widgets that RequiresResize. You need to extend the celltable to implement RequiresResize, so that the ResizeLayoutPanel would be able to resize it.
The first concept anyone needs to learn when programming GWT is to understand why/how to create a chain/tree of unbroken RequiresResize-RequiresResize from a root resize panel. If your root of your resize chain/tree is not RootLayoutPanel, then you have to deliberately resize that root.
In this case, you have made ResizeLayoutPanel the root of resize non-existent resize chain. Use another panel that implmements RequiresResize so that it could be resized by a parent - as well as making sure the parent is resizable manually by user or by another resizable parent.
May I ask you to ask yourself, what is the reason for using ResizeLayoutPanel without providing a way to resize it and also without using it as a root of a resizing chain?

Java Swing JTree Expansion

This method works as expected - it creates a JTree with a root node and two child container nodes (each with a respective leaf node):
private JComponent createSideBar()
{
final DefaultMutableTreeNode top = new DefaultMutableTreeNode("Projects");
final JTree tree = new JTree(top);
DefaultMutableTreeNode project = new DefaultMutableTreeNode("project 1");
DefaultMutableTreeNode version = new DefaultMutableTreeNode("version 1");
project.add(version);
top.add(project);
TreePath treePath = new TreePath(project.getPath());
// tree.expandPath(treePath);
project = new DefaultMutableTreeNode("project 2");
version = new DefaultMutableTreeNode("version 2");
project.add(version);
top.add(project);
return tree;
}
In this case, the tree starts out closed. I'd like the application to start with all nodes fully expanded so I started by adding the following:
tree.expandPath(treePath);
but when I un-comment it from the code above, the second set of child nodes don't show up, ie: Project 2 and Version 2 do not show up. In fact, all subsequently added nodes never show up.
For what its worth, I'm using JDK 1.5. From the docs, I can't seem to see any restrictions or why this method would have such ill-effects ... I'm going to try to look at the source but was hoping someone might have a good idea what and why this is expected behavior. I'm wondering if each subsequent node 'add' is somehow disallowed somehow - but I can't imagine would work for most run-time use cases.
Thanks,
-Luther
Unfortunately, Swing is often "helpful". In this case, it is creating a model for you from the data supplied, much the same as a JList would create a model if you supplied a Vector.
JTree and accomplices (primarily the Pluggable Look & Feel) will add listeners to the model to keep informed of updates. If you just change the data behind the (implicit) model's back, nothing will get updated other than by chance.
So, what you should do is explicitly create a model. When the model data changes (always on the EDT, of course), cause the relevant event to be fired.
If nodes are added to a node which has already been expanded, you need to reload the model.
((DefaultTreeModel)tree.getModel()).reload();
or
((DefaultTreeModel)tree.getModel()).reload(top);
This second version is more useful if you want to reload only a small part of a large tree.
Ah ... the model.
Your answers are elaborated on a bit here and here .... and even here.
I ended up doing something like:
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
model.insertNodeInto(newNode, parent, index);
which keeps the model directly informed. In my case, that scales just fine.
Now, how to mark one of these as the answer!?!

Prefuse Toolkit: dynamically adding nodes and edges

Does anyone have experience with the prefuse graph toolkit? Is it possible to change an already displayed graph, ie. add/remove nodes and/or edges, and have the display correctly adapt?
For instance, prefuse comes with an example that visualizes a network of friends:
http://prefuse.org/doc/manual/introduction/example/Example.java
What I would like to do is something along the lines of this:
// -- 7. add new nodes on the fly -------------------------------------
new Timer(2000, new ActionListener() {
private Node oldNode = graph.nodes().next(); // init with random node
public void actionPerformed(ActionEvent e) {
// insert new node //
Node newNode = graph.addNode();
// insert new edge //
graph.addEdge(oldNode, newNode);
// remember node for next call //
oldNode = newNode;
}
}).start();
But it doesn't seem to work. Any hints?
You should be aware the several layers of prefuse:
Data
Visualization
Display
To be short, the three layers can be linked this way:
Graph graph = new Graph(eg. yourXML_file);
Visualization viz = new Visualization();
viz.add(GRAPH, graph);
Display disp = new Display();
disp.setVisualization(viz);
Display is a graphic component that you add to a panel as usual.
Here you only modify the data layer.
Node newNode = graph.addNode();
graph.addEdge(oldNode, newNode);
You need now to update the visual layer:
viz.run("repaint");
The repaint action has to be defined.
ActionList repaint = new ActionList();
repaint.add(new RepaintAction());
viz.putAction("repaint", repaint);
I really advise you to read the prefuse doc.
And you can find a lot a resources on the official forum
At least, I can say you that prefuse is for the moment not really efficient for live graph update.
But it should not be enough, as you modified the graph structure, you have to regenerate it in the visualization (ie. recalculate the node placements etc...). There are two actions already defined in your sample code. Run them at the end of your actionPerformed.
viz.run("color");
viz.run("layout");
This method is not very efficient, because it adds a lot of computation each time you add a node, but there are not any others for the moment with prefuse.
As pointed out in my other post, the reason new nodes and edges are not visible in the original example is that the colors etc. for the nodes are not set correctly. One way to fix this is to explicitly call vis.run("color"); whenever a node or edge was added.
Alternatively, we can ensure that the color action is always running, by initializing the ActionList to which we add it (called "color" in the original example) slightly differently:
instead of
ActionList color = new ActionList();
we could write
ActionList color = new ActionList(Activity.INFINITY);
This keeps the action list running indefinitely, so that new nodes/edges will automatically be initialized for their visual appearance.
However, it is unclear to me whether this would actually be the preferred method - for things like a dynamic layout action (e.g. ForceDirectedLayout), such a declaration makes perfect sense, but for colors it seems to me that a constantly running coloring action is mostly overhead.
So, perhaps the previously posted solution of just running the "color" action explicitly (but only once) whenever the graph gets extended, might be the better choice...
Okay, after digging a bit through the prefuse sources, I now have a better understanding of how things work under the hood. I found out that actually the new nodes I create with the code above are not only added correctly to the graph, the visualization also takes note of it!
So, unlike Jerome suggests, it is not necessary to call vis.run("layout") explicitly.
The reason I thought the nodes were not added correctly was the fact that they are drawn with white background-, border- and text color - on white background that is. Not astonishing that they are a bit difficult to spot.
To fix that one has to call the color action after a new node is inserted, like this:
// insert new edge //
graph.addEdge(oldNode, newNode);
vis.run("color"); // <- this is new
(Note that this action is defined further up in the code of Example.jar under //-- 4.)
One last thing I am unsure about now is whether calling this action will make prefuse go over all graph nodes again and set their color - for very large graphs that would be undesired, of course.
You need to tell the control container ('d', in example.java) do get redrawn. Calling invalidate() should be enough (not sure, though).
Anyway, this might help you.

Categories

Resources