All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.jaxx.runtime.swing.JAXXTree Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * JAXX :: Runtime
 * %%
 * Copyright (C) 2008 - 2024 Code Lutin, Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
package org.nuiton.jaxx.runtime.swing;

import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.awt.Component;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;

public class JAXXTree extends JTree {

    private static final long serialVersionUID = 1L;

    private static final String SYNTHETIC = "";

    public class JAXXTreeModel implements TreeModel {

        private final Item root;

        private final List listeners = new ArrayList<>();

        public JAXXTreeModel(List items) {
            if (items.size() == 1) {
                root = items.get(0);
            } else {
                root = new Item(null, null, SYNTHETIC, false);
                for (Item item : items) {
                    root.addChild(item);
                }
            }

            PropertyChangeListener listener = e -> {
                if (e.getPropertyName().equals(Item.SELECTED_PROPERTY)) {
                    Item item = (Item) e.getSource();
                    if (item.isSelected()) {
                        addSelectionPath(getTreePath(item));
                    } else {
                        removeSelectionPath(getTreePath(item));
                    }
                } else {
                    Item item = (Item) e.getSource();
                    boolean root = item.getParent() == null;
                    TreePath path = !root ? getTreePath(item.getParent()) : null;
                    fireTreeNodesChanged(new TreeModelEvent(JAXXTreeModel.this, path,
                                                            !root ? new int[]{item.getParent().getChildren().indexOf(item)} : null,
                                                            new Object[]{item.getValue()}));
                }
            };
            addPropertyChangeListener(root, listener);
        }

        private void addPropertyChangeListener(Item item, PropertyChangeListener listener) {
            item.addPropertyChangeListener(listener);
            List children = item.getChildren();
            for (Item aChildren : children) {
                addPropertyChangeListener(aChildren, listener);
            }
        }

        @Override
        public void addTreeModelListener(TreeModelListener listener) {
            listeners.add(listener);
        }


        /* This is an inefficient implementation, but hand-coded tree structures are unlikely to contain
        enough nodes for that to really matter.  This could be sped up with caching. */
        public Item findItem(Object value) {
            return findItem(root, value);
        }

        private Item findItem(Item node, Object value) {
            if (node.getValue() == value) {
                return node;
            } else {
                List children = node.getChildren();
                for (Item aChildren : children) {
                    Item result = findItem(aChildren, value);
                    if (result != null) {
                        return result;
                    }
                }
                return null;
            }
        }

        private TreePath getTreePath(Item node) {
            List path = new ArrayList<>();
            while (node != null) {
                path.add(0, node.getValue());
                node = node.getParent();
            }
            return new TreePath(path.toArray());
        }

        @Override
        public Object getChild(Object parent, int index) {
            Item node = findItem(parent);
            return node.getChildren().get(index).getValue();
        }

        @Override
        public int getChildCount(Object parent) {
            Item node = findItem(parent);
            if (node == null) {
                return 0;
            }
            return node.getChildren().size();
        }

        @Override
        public int getIndexOfChild(Object parent, Object child) {
            Item node = findItem(parent);
            List children = node.getChildren();
            for (int i = 0, j = children.size(); i < j; i++) {
                if (children.get(i).getValue() == child) {
                    return i;
                }
            }
            return -1;
        }

        @Override
        public Object getRoot() {
            return root.getValue();
        }

        public Item getRootItem() {
            return root;
        }

        @Override
        public boolean isLeaf(Object node) {
            Item item = findItem(node);
            return item != null && item.getChildren().size() == 0;
        }

        @Override
        public void removeTreeModelListener(TreeModelListener listener) {
            listeners.remove(listener);
        }

        public void fireTreeNodesChanged(TreeModelEvent e) {
            for (TreeModelListener listener : listeners) {
                listener.treeNodesChanged(e);
            }
        }

        @Override
        public void valueForPathChanged(TreePath path, Object newValue) {
        }
    }

    public JAXXTree(TreeModel model) {
        super(model);
    }

    public JAXXTree() {
        setCellRenderer(new DefaultTreeCellRenderer() {

            private static final long serialVersionUID = 1L;

            @Override
            public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
                TreeModel model = tree.getModel();
                if (model instanceof JAXXTreeModel) {
                    Item item = ((JAXXTreeModel) model).findItem(value);
                    if (item != null) {
                        String label = item.getLabel();
                        if (label != null) {
                            value = label;
                        }
                    }
                }
                return super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            }
        });

        addTreeSelectionListener(new TreeSelectionListener() {

            @Override
            public void valueChanged(TreeSelectionEvent e) {
                TreeModel model = getModel();
                if (model instanceof JAXXTreeModel) {
                    scan((JAXXTreeModel) model, ((JAXXTreeModel) model).root);
                }
            }

            private void scan(JAXXTreeModel model, Item item) {
                TreePath path = model.getTreePath(item);
                if (item.isSelected() != isPathSelected(path)) {
                    item.setSelected(!item.isSelected());
                }
                List children = item.getChildren();
                for (Item aChildren : children) {
                    scan(model, aChildren);
                }
            }
        });
    }

    public void setItem(Item items) {
        List newItems = new ArrayList<>();
        newItems.add(items);
        setItems(newItems);
    }

    public void setItems(List items) {
        // Create model
        JAXXTreeModel model = new JAXXTreeModel(items);
        if (model.getRoot() != null) {
            setRootVisible(model.getRoot() != SYNTHETIC);
        }
        // Atach model
        setModel(model);

        // Appli selected items
        if (items != null) {
            List treePathSelected = new ArrayList<>();
            for (Item i : items) {
                if (i.isSelected()) {
                    treePathSelected.add(model.getTreePath(i));
                }
            }
            setSelectionPaths(convertToTreePathArray(treePathSelected.toArray()));
        }
    }

    public Object getSelectionValue() {
        TreePath selectionPath = getSelectionPath();
        return selectionPath != null ? selectionPath.getLastPathComponent() : null;
    }

    public Item getRootItem() {
        if (getModel() instanceof JAXXTreeModel) {
            return ((JAXXTreeModel) getModel()).getRootItem();
        }
        return null;
    }

    protected TreePath[] convertToTreePathArray(Object[] datas) {
        TreePath[] params = new TreePath[datas.length];
        System.arraycopy(datas, 0, params, 0, datas.length);
        return params;
    }
}