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

org.jdesktop.swingx.tree.TreeModelSupport Maven / Gradle / Ivy

There is a newer version: 1.6.8
Show newest version
/*
 * $Id$
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jdesktop.swingx.tree;

import javax.swing.event.EventListenerList;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import org.jdesktop.swingx.util.Contract;

/**
 * Support for change notification, usable by {@code TreeModel}s.
 * 
 * The changed/inserted/removed is expressed in terms of a {@code TreePath},
 * it's up to the client model to build it as appropriate.
 * 
 * This is inspired by {@code AbstractTreeModel} from Christian Kaufhold,
 * www.chka.de.
 * 
 * TODO - implement and test precondition failure of added/removed notification
 * 
 * @author JW
 */
public final class TreeModelSupport {
    protected EventListenerList listeners;

    private TreeModel treeModel;

    /**
     * Creates the support class for the given {@code TreeModel}.
     * 
     * @param model the model to support
     * @throws NullPointerException if {@code model} is {@code null}
     */
    public TreeModelSupport(TreeModel model) {
        if (model == null)
            throw new NullPointerException("model must not be null");
        listeners = new EventListenerList();
        this.treeModel = model;
    }

//---------------------- structural changes on subtree
    
    /** 
     * Notifies registered TreeModelListeners that the tree's root has
     * been replaced. Can cope with a null root.
     */
    public void fireNewRoot() {

        Object root = treeModel.getRoot();

        /*
         * Undocumented. I think it is the only reasonable/possible solution to
         * use use null as path if there is no root. TreeModels without root
         * aren't important anyway, since JTree doesn't support them (yet).
         */
        TreePath path = (root != null) ? new TreePath(root) : null;
        fireTreeStructureChanged(path);
    }

    /**
     * Call when a node has changed its leaf state.

* * PENDING: rename? Do we need it? * @param path the path to the node with changed leaf state. */ public void firePathLeafStateChanged(TreePath path) { fireTreeStructureChanged(path); } /** * Notifies registered TreeModelListeners that the structure * below the node identified by the given path has been * completely changed. *

* NOTE: the subtree path maybe null if the root is null. * If not null, it must contain at least one element (the root). * * @param subTreePath the path to the root of the subtree * whose structure was changed. * @throws NullPointerException if the path is not null but empty * or contains null elements. */ public void fireTreeStructureChanged(TreePath subTreePath) { if (subTreePath != null) { Contract.asNotNull(subTreePath.getPath(), "path must not contain null elements"); } Object[] pairs = listeners.getListenerList(); TreeModelEvent e = null; for (int i = pairs.length - 2; i >= 0; i -= 2) { if (pairs[i] == TreeModelListener.class) { if (e == null) e = createStructureChangedEvent(subTreePath); ((TreeModelListener) pairs[i + 1]).treeStructureChanged(e); } } } //----------------------- node modifications, no mutations /** * Notifies registered TreeModelListeners that the * the node identified by the given path has been modified. * * @param path the path to the node that has been modified, * must not be null and must not contain null path elements. * */ public void firePathChanged(TreePath path) { Object node = path.getLastPathComponent(); TreePath parentPath = path.getParentPath(); if (parentPath == null) fireChildrenChanged(path, null, null); else { Object parent = parentPath.getLastPathComponent(); fireChildChanged(parentPath, treeModel .getIndexOfChild(parent, node), node); } } /** * Notifies registered TreeModelListeners that the given child of * the node identified by the given parent path has been modified. * The parent path must not be null, nor empty nor contain null * elements. * * @param parentPath the path to the parent of the modified children. * @param index the position of the child * @param child child node that has been modified, must not be null */ public void fireChildChanged(TreePath parentPath, int index, Object child) { fireChildrenChanged(parentPath, new int[] { index }, new Object[] { child }); } /** * Notifies registered TreeModelListeners that the given children of * the node identified by the given parent path have been modified. * The parent path must not be null, nor empty nor contain null * elements. Note that the index array must contain the position of the * corresponding child in the the children array. The indices must be in * ascending order.

* * The exception to these rules is if the root itself has been * modified (which has no parent by definition). In this case * the path must be the path to the root and both indices and children * arrays must be null. * * @param parentPath the path to the parent of the modified children. * @param indices the positions of the modified children * @param children the modified children */ public void fireChildrenChanged(TreePath parentPath, int[] indices, Object[] children) { Contract.asNotNull(parentPath.getPath(), "path must not be null and must not contain null elements"); Object[] pairs = listeners.getListenerList(); TreeModelEvent e = null; for (int i = pairs.length - 2; i >= 0; i -= 2) { if (pairs[i] == TreeModelListener.class) { if (e == null) e = createTreeModelEvent(parentPath, indices, children); ((TreeModelListener) pairs[i + 1]).treeNodesChanged(e); } } } //------------------------ mutations (insert/remove nodes) /** * Notifies registered TreeModelListeners that the child has been added to * the the node identified by the given parent path at the given position. * The parent path must not be null, nor empty nor contain null elements. * * @param parentPath the path to the parent of added child. * @param index the position of the added children * @param child the added child */ public void fireChildAdded(TreePath parentPath, int index, Object child) { fireChildrenAdded(parentPath, new int[] { index }, new Object[] { child }); } /** * Notifies registered TreeModelListeners that the child has been removed * from the node identified by the given parent path from the given position. * The parent path must not be null, nor empty nor contain null elements. * * @param parentPath the path to the parent of removed child. * @param index the position of the removed children before the removal * @param child the removed child */ public void fireChildRemoved(TreePath parentPath, int index, Object child) { fireChildrenRemoved(parentPath, new int[] { index }, new Object[] { child }); } /** * Notifies registered TreeModelListeners that the given children have been * added to the the node identified by the given parent path at the given * locations. The parent path and the child array must not be null, nor * empty nor contain null elements. Note that the index array must contain * the position of the corresponding child in the the children array. The * indices must be in ascending order. *

* * @param parentPath the path to the parent of the added children. * @param indices the positions of the added children. * @param children the added children. */ public void fireChildrenAdded(TreePath parentPath, int[] indices, Object[] children) { Object[] pairs = listeners.getListenerList(); TreeModelEvent e = null; for (int i = pairs.length - 2; i >= 0; i -= 2) { if (pairs[i] == TreeModelListener.class) { if (e == null) e = createTreeModelEvent(parentPath, indices, children); ((TreeModelListener) pairs[i + 1]).treeNodesInserted(e); } } } /** * Notifies registered TreeModelListeners that the given children have been * removed to the the node identified by the given parent path from the * given locations. The parent path and the child array must not be null, * nor empty nor contain null elements. Note that the index array must * contain the position of the corresponding child in the the children * array. The indices must be in ascending order. *

* * @param parentPath the path to the parent of the removed children. * @param indices the positions of the removed children before the removal * @param children the removed children */ public void fireChildrenRemoved(TreePath parentPath, int[] indices, Object[] children) { Object[] pairs = listeners.getListenerList(); TreeModelEvent e = null; for (int i = pairs.length - 2; i >= 0; i -= 2) { if (pairs[i] == TreeModelListener.class) { if (e == null) e = createTreeModelEvent(parentPath, indices, children); ((TreeModelListener) pairs[i + 1]).treeNodesRemoved(e); } } } //------------------- factory methods of TreeModelEvents /** * Creates and returns a TreeModelEvent for structureChanged * event notification. The given path may be null to indicate * setting a null root. In all other cases, the first path element * must contain the root and the last path element the rootNode of the * structural change. Specifically, a TreePath with a single element * (which is the root) denotes a structural change of the complete tree. * * @param parentPath the path to the root of the changed structure, * may be null to indicate setting a null root. * @return a TreeModelEvent for structureChanged notification. * * @see javax.swing.event.TreeModelEvent * @see javax.swing.event.TreeModelListener */ private TreeModelEvent createStructureChangedEvent(TreePath parentPath) { return createTreeModelEvent(parentPath, null, null); } /** * Creates and returns a TreeModelEvent for changed/inserted/removed * event notification. * * @param parentPath path to parent of modified node * @param indices the indices of the modified children (before the change) * @param children the array of modified children * @return a TreeModelEvent for changed/inserted/removed notification * * @see javax.swing.event.TreeModelEvent * @see javax.swing.event.TreeModelListener */ private TreeModelEvent createTreeModelEvent(TreePath parentPath, int[] indices, Object[] children) { return new TreeModelEvent(treeModel, parentPath, indices, children); } //------------------------ handling listeners public void addTreeModelListener(TreeModelListener l) { listeners.add(TreeModelListener.class, l); } public TreeModelListener[] getTreeModelListeners() { return listeners.getListeners(TreeModelListener.class); } public void removeTreeModelListener(TreeModelListener l) { listeners.remove(TreeModelListener.class, l); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy