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

com.alee.extended.tree.AsyncTreeTransferHandler Maven / Gradle / Ivy

There is a newer version: 1.2.14
Show newest version
/*
 * This file is part of WebLookAndFeel library.
 *
 * WebLookAndFeel library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebLookAndFeel 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebLookAndFeel library.  If not, see .
 */

package com.alee.extended.tree;

import com.alee.api.annotations.NotNull;
import com.alee.managers.task.TaskManager;

import java.util.List;

/**
 * Custom {@link javax.swing.TransferHandler} implementation for {@link WebAsyncTree} that provides a quick and convenient way to implement
 * drag and drop for {@link AsyncUniqueNode}s within the tree or between different trees using the same type of {@link AsyncUniqueNode}s.
 *
 * @param  {@link AsyncUniqueNode} type
 * @param  {@link WebAsyncTree} type
 * @param  {@link AsyncTreeModel} type
 * @author Mikle Garin
 */
public abstract class AsyncTreeTransferHandler, M extends AsyncTreeModel>
        extends AbstractTreeTransferHandler
{
    /**
     * Whether should allow dropping nodes onto not-yet-loaded node or not.
     * Be aware that if this set to true and your tree might fail loading children - old nodes will still get removed on drop.
     * If set to false tree will try to load child nodes first and then perform the drop operation.
     */
    protected boolean allowUncheckedDrop = false;

    /**
     * Returns whether should allow dropping nodes onto not-yet-loaded node or not.
     *
     * @return true if should allow dropping nodes onto not-yet-loaded node, false otherwise
     */
    public boolean isAllowUncheckedDrop ()
    {
        return allowUncheckedDrop;
    }

    /**
     * Sets whether should allow dropping nodes onto not-yet-loaded node or not
     *
     * @param allowUncheckedDrop whether should allow dropping nodes onto not-yet-loaded node or not
     */
    public void setAllowUncheckedDrop ( final boolean allowUncheckedDrop )
    {
        this.allowUncheckedDrop = allowUncheckedDrop;
    }

    /**
     * In addition to {@link AbstractTreeTransferHandler} this method:
     * - Doesn't allow drop on busy nodes as that might break tree model
     * - Doesn't allow drop on a failed nodes as they are considered to have no chidren
     */
    @Override
    protected boolean canDropTo ( @NotNull final TransferSupport support, @NotNull final T tree, @NotNull final M model,
                                  @NotNull final N destination )
    {
        return !destination.isLoading () && !destination.isFailed ();
    }

    @Override
    protected boolean prepareDropOperation ( @NotNull final TransferSupport support, @NotNull final T tree, @NotNull final M model,
                                             @NotNull final N destination, final int dropIndex )
    {
        final boolean prepared;
        if ( allowUncheckedDrop )
        {
            // Acting differently in case parent node children are not yet loaded
            // We don't want to modify model (insert children) before existing children are actually loaded
            final AsyncTreeDataProvider dataProvider = tree.getDataProvider ();
            if ( dataProvider != null )
            {
                if ( destination.isLoaded () )
                {
                    // Expanding parent first
                    if ( !tree.isExpanded ( destination ) )
                    {
                        tree.expandNode ( destination );
                    }

                    // Adjust drop index after we ensure parent is loaded and expanded
                    final int adjustedDropIndex = getAdjustedDropIndex ( support, tree, model, destination, dropIndex );

                    // Adding data to model
                    // Performing it in a separate non-EDT thread
                    TaskManager.execute ( dataProvider.getThreadGroupId (), new Runnable ()
                    {
                        @Override
                        public void run ()
                        {
                            performDropOperation ( support, tree, model, destination, adjustedDropIndex );
                        }
                    } );
                }
                else
                {
                    // Loading children first
                    tree.addAsyncTreeListener ( new AsyncTreeAdapter ()
                    {
                        @Override
                        public void loadCompleted ( @NotNull final N parent, @NotNull final List children )
                        {
                            if ( parent == destination )
                            {
                                // Adding data to model
                                // Performing it in a separate non-EDT thread
                                TaskManager.execute ( dataProvider.getThreadGroupId (), new Runnable ()
                                {
                                    @Override
                                    public void run ()
                                    {
                                        performDropOperation ( support, tree, model, destination, destination.getChildCount () );
                                    }
                                } );

                                // Removing listener
                                tree.removeAsyncTreeListener ( this );
                            }
                        }

                        @Override
                        public void loadFailed ( @NotNull final N parent, @NotNull final Throwable cause )
                        {
                            if ( parent == destination )
                            {
                                // Removing listener
                                tree.removeAsyncTreeListener ( this );
                            }
                        }
                    } );
                    tree.reloadNode ( destination );
                }
                prepared = true;
            }
            else
            {
                // Cannot proceed when target tree doesn't have data provider installed
                prepared = false;
            }
        }
        else
        {
            // We have to load children synchronously, otherwise we cannot say for sure if drop succeed or not
            if ( !destination.isLoaded () )
            {
                tree.reloadNodeSync ( destination );
            }

            // If children were loaded right away with our attempt - perform the drop
            prepared = destination.isLoaded () && performDropOperation (
                    support,
                    tree,
                    model,
                    destination,
                    getAdjustedDropIndex ( support, tree, model, destination, dropIndex )
            );
        }
        return prepared;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy