I'm working with a TreeTable (from wicket-extensions) and I'd like to be able to select a row by clicking anywhere within it instead of the usual behavior of clicking the link in one cell to select the row. I understand this should be possible by adding an AjaxEventBehavior("onclick") to the component representing the row, but I can't seem to find any methods where the row component is exposed.
I figured out a solution after. The row element is available in the populateTreeItem method from TreeTable. When you're creating your treetable, override this method like so:
#Override
protected void populateTreeItem(final WebMarkupContainer item, final int level) {
super.populateTreeItem(item, level);
item.add(new AjaxEventBehavior("onclick") {
#Override
protected void onEvent(final AjaxRequestTarget target) {
final TreeNode node = ((TreeNode) item.getDefaultModelObject());
rowClickSelect(node);
});
}
};
Generally useful in adding behaviors to rows. In my case, I'll have to do some more overriding to reconcile this toggle-on-click behavior with the clicks that are supposed to expand/contract nodes as well as link clicks.
Just toggling selection again in these cases has the unfortunate effect of briefly toggling the node in and out of the unwanted state, which is not ideal. Instead, override the onJunctionLinkClicked and onNodeLinkClicked methods, which will be touched by a click event before it gets to the onClick behavior we just set-up in populateTreeItem:
#Override
protected void onJunctionLinkClicked(final AjaxRequestTarget target, final TreeNode node) {
super.onJunctionLinkClicked(target, node);
skipNextRowClick();
}
#Override
protected void onNodeLinkClicked(final AjaxRequestTarget target, final TreeNode node) {
super.onNodeLinkClicked(target, node);
skipNextRowClick();
}
Finally, add the methods skipNextRowClick and rowClickSelect:
/**
* Ensure the next call to rowClickSelect() will have no effect.
*/
private void skipNextRowClick() {
this.skipNextClickSelect = true;
}
private void rowClickSelect(final TreeNode node) {
if (this.skipNextClickSelect) {
this.skipNextClickSelect = false;
return;
}
// select on click row
final boolean isSelected = Log4jPanel.this.treeTable.getTreeState().isNodeSelected(node);
treeTable.getTreeState().selectNode(node, !isSelected);
}
Related
I have a TreeViewer which has elements with children which I want to fetch from a rest api, so initially there are no children for the nodes. What I currently did is I added a doubleClickListener to the nodes and fetch the children for the selected node, then use the treeviewer's method "expandToLevel" to show them. I want to be able to do that by clicking on the expand arrow too.
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
Module m = (Module) selection.getFirstElement();
//----> theChildren are fetched here <---
m.setStrings(theChildren);
treeViewer.expandToLevel(m, 1);
}
Unfortunately when I try to do it with the treeListener the getChildren method from the content provider is called first and therefore returns that there are no children. Is it ok to make the api call in the getChildren method?
treeViewer.addTreeListener(new ITreeViewerListener() {
#Override
public void treeExpanded(TreeExpansionEvent event) {
Module m = (Module) event.getElement();
//----> theChildren are fetched here <---
m.setStrings(theChildren);
}
#Override
public void treeCollapsed(TreeExpansionEvent event) {
// TODO Auto-generated method stub
}
});
I found a solution. I don't know if its the best one out there but it works.
In treeExpanded I added async call to the expand of treeViewer so the children are shown now.
Display.getCurrent().asyncExec(new Runnable() {
#Override
public void run() {
treeViewer.expandToLevel(m, 1);
}
});
After I drag and drop a node, the new parent of the node expands. I want to disable this.
I extended the class TransferHandler and overrode the method importData.
In this method I do the actual move. After the move of the node I check if the parent node isExpanded and I get false as an answer.
So where or how can I cancel the expansion?
Maybe you can use addTreeWillExpandListener and add a listener like this:
public class DisableExpansionListener implements TreeWillExpandListener {
#Override
public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException {
throw new ExpandVetoException(event, "Disallow all expansions!");
}
#Override
public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException
// allow collapsing!
}
}
The lack of documentation for this really simple feature is disturbing. I have a TreeViewer and want to select a node. And the only way this makes sense is if the tree expands all elements up to the selection, else the user can't see it.
public class TreeWindow extends ApplicationWindow {
public static void main(String[] args) {
new TreeWindow().open();
}
public TreeWindow() {
super(null);
setBlockOnOpen(true);
}
#Override
protected Control createContents(Composite parent) {
final TreeViewer treeViewer = new TreeViewer(parent);
treeViewer.setContentProvider(new FileTreeContentProvider());
treeViewer.setLabelProvider(new LabelProvider() {
#Override
public String getText(Object element) {
String result = ((File) element).getName();
if (result.isEmpty()) {
result = ((File) element).getPath(); // root like C:\
}
return result;
}
});
treeViewer.setInput(File.listRoots());
// expand
final File fileToExpand = new File("src");
System.out.println("Expand to file: " + fileToExpand.getAbsolutePath());
return treeViewer.getControl();
}
static class FileTreeContentProvider extends ArrayContentProvider implements ITreeContentProvider {
#Override
public Object[] getChildren(Object parentElement) {
return ((File) parentElement).listFiles();
}
#Override
public Object getParent(Object element) {
return ((File) element).getParentFile();
}
#Override
public boolean hasChildren(Object element) {
return ((File) element).isDirectory();
}
}
}
What I tried:
treeViewer.setSelection(new StructuredSelection(fileToExpand));
System.out.println("Selection: " + treeViewer.getSelection());
The selection doesn't get set. (I saw multiple times that TreeViewer#setSelection(ISelection, boolean) was used, but the JavaDoc states "Currently the reveal parameter is not honored because Tree does not provide an API to only select an item without scrolling it into view").
treeViewer.expandToLevel(fileToExpand, AbstractTreeViewer.ALL_LEVELS);
This method... does nothing?
final Tree tree = treeViewer.getTree();
final TreeItem[] items = tree.getSelection();
for (int i = 0; i < items.length; ++i) {
final TreeItem item = items[i];
TreeItem treeParent = item.getParentItem();
while (treeParent != null) {
treeParent.setExpanded(true);
treeParent = treeParent.getParentItem();
}
}
Might work maybe? But the selection does not get set, so...
treeViewer.expandAll();
This method normally works, but I don't think it's a good idea to try it in the above example. It does not do what I want, so it's a moot point anyways.
The problem seems to be that the TreeItem is created lazily. To check that you can try this:
for (final TreeItem item : this.treeViewer.getTree().getItems()) {
System.out.println(item.getData() + " " + item.getItemCount());
}
This method outputs either 1 if the item has children or 0 if not, but not the actual item count. Also, if you try to get the children's data, it's null.
How do I select a node that is not expanded? How do I expand the tree to the selection / a specified node?
Possible duplicate:
How to expand a specific node in TreeViewer(org.eclipse.jface) (I'm not sure if this is the same problem, but there is no solution either way)
I was able to get this to work on JFace 3.13.2:
// element is any object of your tree content provider data model,
// in your case a File.
treeViewer.expandToLevel(element, 0);
treeViewer.setSelection(new StructuredSelection(element));
The above code will make all the nodes down to the level of the selected element expanded, scroll the relevant part of the tree into view and mark the element as selected.
In my case the tree was four levels deep and the selected element was a leaf node. If you wanted to expand levels below the selected node, you can provide a level higher than 0 as second parameter to expandToLevel, or TreeViewer.ALL_LEVELS to expand all levels in the subtree.
I have a problem with my app which uses JavaFX... In one view I have a tableview which contains list of people and I want change row style one person. Here is my code:
personTable.setRowFactory(new Callback<TableView<Person>, TableRow<Person>>() {
#Override
public TableRow<Person> call(TableView<Person> personTableView) {
return new TableRowRightFormat();
}
});
...
private class TableRowRightFormat extends TableRow {
#Override
protected void updateItem(Object o, boolean b) {
super.updateItem(o, b);
if(o == null) {
return;
}
getStyleClass().remove("headPerson");
if(((Person)o).getId()==2) {
getStyleClass().add("headPerson");
}
}
}
And it is working for one person(id=2) but when I scroll up my table and when person with id=2 disappears another person with id!=2 gets style called 'headPerson' (always one Person on visible elements in tableview has style 'headPerson', but above code is executing once time). What is the problem?
Update: I tested your code on both JavaFX 2.2 and JDK 8 and it seemed to work fine for my simple test case. The logic seems right; the one thing you have to be really careful of in these types of style-class based cell and row implementations is making sure you don't add multiple copies of a given string to the list of style classes - in your implementation this looks right. Double check and make sure you have the strings exactly the same in the add(...) and remove(...) methods.
I like to completely bullet-proof these at a slight cost to performance:
private final String headPersonStyleClass = "headPerson" ;
private class TableRowRightFormat extends TableRow<Person> {
#Override
protected void updateItem(Person p, boolean b) {
super.updateItem(p, b);
ObservableList<String> styleClass = getStyleClass();
if (p != null && p.getId()==2 && (! styleClass.contains(headPersonStyleClass))) {
styleClass.add(headPersonStyleClass);
} else {
// remove all occurrences:
styleClass.removeAll(Collections.singleton(headPersonStyleClass));
}
}
}
If you are using JavaFX 8, a better approach is to use a PseudoClass for this.
I'm working in Swing and I would like to disable the expand (plus [+]) sign on a certain type of nodes.
Not sure how to do it because my nodes aren't leaves and I also cannot use setShowsRootHandles (which is only for the root).
I'm referring to to JTree: suppose i got this structure:
Root
--[+] node1
--[+] node2
when I load this structure i would like not to see the [+] sign on node2 (because it a special type node). But I also would like to expand it by using a special command.
I've overridden isLeaf() (method from DefaultMutableTreeNode) so it would set to to TRUE when i'm in the special type node, but then when I'm trying to expand it, it wouldn't expand because isLeaf() == TRUE...
Hope this will make things more clear.
While it is not possible to remove the handles, it is possible to restrict the expansion of nodes. The way to go is a TreeWillExpandListener combined with a custom treeNode that has state to restrict expansion:
the custom node below has an expandable property that's false by default
when detecting custom nodes, the listener allows/vetoes expansion based on that expandable property
for programmatic expansion, the expandable property is set to true temporarily to pass the listener
Example code:
// mixed tree of normal/restricted noded
DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
DefaultMutableTreeNode normalSubTree = new DefaultMutableTreeNode("normal");
normalSubTree.add(new DefaultMutableTreeNode("normalChild"));
MyNode restrictedSubTree = new MyNode("restrictedSubtree");
restrictedSubTree.add(new DefaultMutableTreeNode("restrictedChild"));
root.add(normalSubTree);
root.add(restrictedSubTree);
final JTree tree = new JTree(root);
// the listener which vetos expansion of MyNodes that are not expandable
TreeWillExpandListener l = new TreeWillExpandListener() {
#Override
public void treeWillExpand(TreeExpansionEvent event)
throws ExpandVetoException {
TreePath path = event.getPath();
if (path.getLastPathComponent() instanceof MyNode) {
if (!((MyNode) path.getLastPathComponent()).isExpandable()) {
throw new ExpandVetoException(event, "node not expandable");
}
}
}
#Override
public void treeWillCollapse(TreeExpansionEvent event)
throws ExpandVetoException {
}
};
tree.addTreeWillExpandListener(l);
Action expand = new AbstractAction("Expand") {
#Override
public void actionPerformed(ActionEvent e) {
TreePath selected = tree.getSelectionPath();
if (selected == null) return;
if (selected.getLastPathComponent() instanceof MyNode) {
MyNode last = (MyNode) selected.getLastPathComponent();
boolean old = last.isExpandable();
last.setExpandable(true);
tree.expandPath(selected);
last.setExpandable(old);
}
}
};
JXFrame frame = wrapWithScrollingInFrame(tree, "veto expand");
addAction(frame, expand);
show(frame);
}
// custom node which has an expandable property
public static class MyNode extends DefaultMutableTreeNode {
private boolean expandable;
public MyNode() {
this(null);
}
public MyNode(Object userObject) {
super(userObject);
}
public void setExpandable(boolean expandable) {
this.expandable = expandable;
}
public boolean isExpandable() {
return expandable;
}
}
It's possible to remove the handles - despite what others have mentioned.
I've attached a snippet on how to do this below. The key thing is to override shouldPaintExpandControl in BasicTreeUI.
jtree.setUI(new BasicTreeUI() {
#Override
protected boolean shouldPaintExpandControl(final TreePath path, final int row
, final boolean isExpanded, final boolean hasBeenExpanded, final boolean isLeaf)
{
boolean shouldDisplayExpandControl = false;
return shouldDisplayExpandControl;
}
This should really be documented in the JTree API but that's another issue.
Another approach to consider:
If you call DefaultTreeModel(TreeNode root, boolean asksAllowsChildren) the model will "ask" the nodes you insert if they are allowed to have children. If they cannot, it should not display the expand icon.
Be sure to override javax.swing.tree.TreeNode.getAllowsChildren() in your class.