Package com.alee.extended.tree

Source Code of com.alee.extended.tree.WebTreeFilterField

/*
* 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 <http://www.gnu.org/licenses/>.
*/

package com.alee.extended.tree;

import com.alee.extended.image.WebImage;
import com.alee.laf.WebLookAndFeel;
import com.alee.laf.menu.WebCheckBoxMenuItem;
import com.alee.laf.menu.WebPopupMenu;
import com.alee.laf.text.WebTextField;
import com.alee.laf.tree.TreeState;
import com.alee.laf.tree.UniqueNode;
import com.alee.laf.tree.WebTree;
import com.alee.managers.hotkey.Hotkey;
import com.alee.utils.compare.Filter;
import com.alee.utils.swing.StringDocumentChangeListener;
import com.alee.utils.text.TextProvider;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;

/**
* Special filter field that can be attached to any WebAsyncTree.
*
* @param <E> filtered node type
* @author Mikle Garin
*/

public class WebTreeFilterField<E extends UniqueNode> extends WebTextField
{
    /**
     * Used icons.
     */
    public static final ImageIcon settingsIcon = new ImageIcon ( WebTreeFilterField.class.getResource ( "icons/filter/settings.png" ) );
    public static final ImageIcon matchCaseIcon = new ImageIcon ( WebTreeFilterField.class.getResource ( "icons/filter/matchCase.png" ) );
    public static final ImageIcon useSpaceAsSeparatorIcon =
            new ImageIcon ( WebTreeFilterField.class.getResource ( "icons/filter/useSpaceAsSeparator.png" ) );
    public static final ImageIcon searchFromStartIcon =
            new ImageIcon ( WebTreeFilterField.class.getResource ( "icons/filter/searchFromStart.png" ) );

    /**
     * Async tree to which this field should apply filtering.
     */
    protected WeakReference<WebTree<E>> tree;

    /**
     * Nodes filter used by this field.
     */
    protected StructuredTreeNodesFilter<E> filter;

    /**
     * Currently listened field document.
     */
    protected Document document;

    /**
     * Special document listener that notifies about filter changes.
     */
    protected DocumentListener documentListener;

    /**
     * Data provider change listener.
     */
    protected PropertyChangeListener dataProviderChangeListener;

    /**
     * Tree filter change listener.
     */
    protected PropertyChangeListener filterChangeListener;

    /**
     * Whether should automatically handle tree state on filter changes or not.
     */
    protected boolean defaultTreeStateBehavior = true;

    /**
     * Last saved tree state.
     */
    protected TreeState treeState = null;
    protected Rectangle visibleRect = null;

    /**
     * UI elements.
     */
    protected WebImage filterIcon;
    protected WebPopupMenu settingsMenu;
    protected WebCheckBoxMenuItem matchCaseItem;
    protected WebCheckBoxMenuItem useSpaceAsSeparatorItem;
    protected WebCheckBoxMenuItem searchFromStartItem;

    /**
     * Constructs new tree filter field.
     */
    public WebTreeFilterField ()
    {
        this ( null, null );
    }

    /**
     * Constructs new tree filter field.
     *
     * @param tree tree to which this field applies filtering
     */
    public WebTreeFilterField ( final WebTree<E> tree )
    {
        this ( tree, null );
    }

    /**
     * Constructs new tree filter field.
     *
     * @param textProvider node text provider
     */
    public WebTreeFilterField ( final TextProvider<E> textProvider )
    {
        this ( null, textProvider );
    }

    /**
     * Constructs new tree filter field.
     *
     * @param tree         tree to which this field applies filtering
     * @param textProvider node text provider
     */
    public WebTreeFilterField ( final WebTree<E> tree, final TextProvider<E> textProvider )
    {
        super ();
        checkTree ( tree );
        initDefaultFilter ();
        setTree ( tree );
        setTextProvider ( textProvider );
        initField ();
    }

    /**
     * Checks whether provided tree type is correct or not.
     *
     * @param tree tree to check
     */
    protected void checkTree ( final WebTree<E> tree )
    {
        if ( !( tree instanceof WebAsyncTree || tree instanceof WebExTree ) )
        {
            throw new RuntimeException ( "WebTreeFilterField is only usable with WebAsyncTree and WebExTree" );
        }
    }

    /**
     * Initializes default field tree filter.
     */
    protected void initDefaultFilter ()
    {
        this.filter = new StructuredTreeNodesFilter ();
    }

    /**
     * Initializes filter field.
     */
    protected void initField ()
    {
        setLanguage ( "weblaf.ex.treefilter.inputprompt" );
        setHideInputPromptOnFocus ( false );

        initFilterIcon ();
        initSettingsMenu ();
        initListeners ();
    }

    /**
     * Initializes filter icon.
     */
    protected void initFilterIcon ()
    {
        filterIcon = new WebImage ( WebTreeFilterField.class, "icons/filter/settings.png" );
        filterIcon.setMargin ( 0, 2, 0, 2 );
        filterIcon.setCursor ( Cursor.getDefaultCursor () );
        filterIcon.addMouseListener ( new MouseAdapter ()
        {
            @Override
            public void mousePressed ( final MouseEvent e )
            {
                settingsMenu.showBelowMiddle ( filterIcon );
            }
        } );
        setLeadingComponent ( filterIcon );
    }

    /**
     * Initializes settings menu.
     */
    protected void initSettingsMenu ()
    {
        settingsMenu = new WebPopupMenu ();

        matchCaseItem = new WebCheckBoxMenuItem ( matchCaseIcon );
        matchCaseItem.setLanguage ( "weblaf.filter.matchcase" );
        matchCaseItem.addActionListener ( new ActionListener ()
        {
            @Override
            public void actionPerformed ( final ActionEvent e )
            {
                filter.setMatchCase ( matchCaseItem.isSelected () );
                updateFiltering ();
            }
        } );
        settingsMenu.add ( matchCaseItem );

        useSpaceAsSeparatorItem = new WebCheckBoxMenuItem ( useSpaceAsSeparatorIcon );
        useSpaceAsSeparatorItem.setLanguage ( "weblaf.filter.spaceseparator" );
        useSpaceAsSeparatorItem.addActionListener ( new ActionListener ()
        {
            @Override
            public void actionPerformed ( final ActionEvent e )
            {
                filter.setUseSpaceAsSeparator ( useSpaceAsSeparatorItem.isSelected () );
                updateFiltering ();
            }
        } );
        settingsMenu.add ( useSpaceAsSeparatorItem );

        searchFromStartItem = new WebCheckBoxMenuItem ( searchFromStartIcon );
        searchFromStartItem.setLanguage ( "weblaf.filter.frombeginning" );
        searchFromStartItem.addActionListener ( new ActionListener ()
        {
            @Override
            public void actionPerformed ( final ActionEvent e )
            {
                filter.setSearchFromStart ( searchFromStartItem.isSelected () );
                updateFiltering ();
            }
        } );
        settingsMenu.add ( searchFromStartItem );
    }

    /**
     * Initializes listeners.
     */
    protected void initListeners ()
    {
        // Field changes listener
        documentListener = new StringDocumentChangeListener ()
        {
            @Override
            public void documentChanged ( final String newValue, final DocumentEvent e )
            {
                filter.setSearchText ( newValue );
                updateFiltering ();
            }
        };
        updateDocumentListener ();

        // Field document change listener
        addPropertyChangeListener ( WebLookAndFeel.DOCUMENT_PROPERTY, new PropertyChangeListener ()
        {
            @Override
            public void propertyChange ( final PropertyChangeEvent e )
            {
                updateDocumentListener ();
            }
        } );

        // Field clear listener
        addKeyListener ( new KeyAdapter ()
        {
            @Override
            public void keyPressed ( final KeyEvent e )
            {
                if ( Hotkey.ESCAPE.isTriggered ( e ) )
                {
                    // Clearing filter field on ESCAPE press
                    clear ();
                }
            }
        } );

        // Model change listener to properly update field filter
        dataProviderChangeListener = new PropertyChangeListener ()
        {
            @Override
            public void propertyChange ( final PropertyChangeEvent evt )
            {
                // Reapplying field filter on model change
                applyFieldFilter ();
            }
        };
        getTree ().addPropertyChangeListener ( WebTree.TREE_DATA_PROVIDER_PROPERTY, dataProviderChangeListener );

        // Filter change listener to properly update field filter
        filterChangeListener = new PropertyChangeListener ()
        {
            @Override
            public void propertyChange ( final PropertyChangeEvent evt )
            {
                // Reapplying field filter on filter change
                applyFieldFilter ();
            }
        };
        getTree ().addPropertyChangeListener ( WebTree.TREE_FILTER_PROPERTY, dataProviderChangeListener );
    }

    /**
     * Updates field document listener.
     */
    protected void updateDocumentListener ()
    {
        // Removing listener from old document
        if ( document != null )
        {
            document.removeDocumentListener ( documentListener );
        }

        // Adding listener to new document
        document = getDocument ();
        document.addDocumentListener ( documentListener );
    }

    /**
     * Sets tree to which this field applies filtering.
     *
     * @param tree tree to which this field applies filtering
     */
    public void setTree ( final WebTree<E> tree )
    {
        // Cleanup the mess we made in previous tree
        final WebTree<E> previousTree = getTree ();
        if ( previousTree != null )
        {
            // Removing listener from previous tree
            previousTree.removePropertyChangeListener ( WebTree.TREE_MODEL_PROPERTY, dataProviderChangeListener );

            // Removing filter from previous tree
            removeFieldFilter ();
        }

        // Installing filtering into new tree
        {
            // Saving reference to new tree
            this.tree = new WeakReference<WebTree<E>> ( tree );

            // Updating filter in current tree
            applyFieldFilter ();

            // Adding listener into current tree
            tree.addPropertyChangeListener ( WebTree.TREE_MODEL_PROPERTY, dataProviderChangeListener );
        }
    }

    /**
     * Returns nodes filter.
     *
     * @return nodes filter
     */
    public StructuredTreeNodesFilter<E> getFilter ()
    {
        return filter;
    }

    /**
     * Sets nodes filter.
     *
     * @param filter new nodes filter
     */
    public void setFilter ( final StructuredTreeNodesFilter<E> filter )
    {
        removeFieldFilter ();
        this.filter = filter;
        applyFieldFilter ();
    }

    /**
     * Applies field tree filter.
     */
    protected void applyFieldFilter ()
    {
        // Updating tree filter if possible
        final WebTree<E> tree = getTree ();
        if ( tree != null )
        {
            if ( tree instanceof WebAsyncTree )
            {
                final WebAsyncTree asyncTree = ( WebAsyncTree ) tree;

                // Cleaning up filter cache
                filter.clearCache ();

                // Saving original filter
                // Note that we have to check whether field filter is already installed or not here
                final Filter originalFilter = asyncTree.getFilter ();
                filter.setOriginalFilter ( originalFilter instanceof StructuredTreeNodesFilter ?
                        ( ( StructuredTreeNodesFilter ) originalFilter ).getOriginalFilter () : originalFilter );

                // Updating field tree filter
                asyncTree.setFilter ( filter );
            }
            else if ( tree instanceof WebExTree )
            {
                final WebExTree exTree = ( WebExTree ) tree;

                // Cleaning up filter cache
                filter.clearCache ();

                // Saving original filter
                // Note that we have to check whether field filter is already installed or not here
                final Filter originalFilter = exTree.getFilter ();
                filter.setOriginalFilter ( originalFilter instanceof StructuredTreeNodesFilter ?
                        ( ( StructuredTreeNodesFilter ) originalFilter ).getOriginalFilter () : originalFilter );

                // Updating field tree filter
                exTree.setFilter ( filter );
            }
        }
    }

    /**
     * Removes field tree filter.
     */
    protected void removeFieldFilter ()
    {
        final WebTree<E> tree = getTree ();
        if ( tree != null )
        {
            final Filter<E> originalFilter = filter.getOriginalFilter ();
            if ( tree instanceof WebAsyncTree )
            {
                ( ( WebAsyncTree ) tree ).setFilter ( originalFilter );
            }
            else if ( tree instanceof WebExTree )
            {
                ( ( WebExTree ) tree ).setFilter ( originalFilter );
            }
            filter.setOriginalFilter ( null );
        }
    }

    /**
     * Returns node text provider.
     *
     * @return node text provider
     */
    public TextProvider<E> getTextProvider ()
    {
        return filter.getTextProvider ();
    }

    /**
     * Sets node text provider.
     *
     * @param textProvider new node text provider
     */
    public void setTextProvider ( final TextProvider<E> textProvider )
    {
        filter.setTextProvider ( textProvider );
        updateFiltering ();
    }

    /**
     * Returns whether should automatically handle tree state on filter changes or not.
     *
     * @return true if should automatically handle tree state on filter changes, false otherwise
     */
    public boolean isDefaultTreeStateBehavior ()
    {
        return defaultTreeStateBehavior;
    }

    /**
     * Sets whether should automatically handle tree state on filter changes or not.
     *
     * @param defaultTreeStateBehavior whether should automatically handle tree state on filter changes or not
     */
    public void setDefaultTreeStateBehavior ( final boolean defaultTreeStateBehavior )
    {
        this.defaultTreeStateBehavior = defaultTreeStateBehavior;
    }

    /**
     * Updates tree filtering.
     */
    public void updateFiltering ()
    {
        // Updating tree filtering if possible
        final WebTree<E> tree = getTree ();
        if ( tree != null )
        {
            if ( tree instanceof WebAsyncTree )
            {
                // todo Restore/expand behavior

                // Cleaning up filter cache
                filter.clearCache ();

                // Updating tree filtering
                ( ( WebAsyncTree ) tree ).updateSortingAndFiltering ();
            }
            else if ( tree instanceof WebExTree )
            {
                // Save tree state before filtering
                if ( defaultTreeStateBehavior )
                {
                    if ( !isEmpty () && treeState == null )
                    {
                        treeState = tree.getTreeState ();
                        visibleRect = tree.getVisibleRect ();
                    }
                }

                // Cleaning up filter cache
                filter.clearCache ();

                // Updating tree filtering
                ( ( WebExTree ) tree ).updateSortingAndFiltering ();

                // Restore tree state or expand tree
                if ( defaultTreeStateBehavior )
                {
                    if ( isEmpty () )
                    {
                        // Restore tree state
                        if ( treeState != null )
                        {
                            tree.setTreeState ( treeState );
                            tree.scrollRectToVisible ( visibleRect );
                            treeState = null;
                            visibleRect = null;
                        }
                    }
                    else
                    {
                        // Expand all
                        tree.expandAll ();
                    }
                }
            }
        }
    }

    /**
     * Performs node acceptance re-check.
     * Might be useful if external tree updates are applied.
     *
     * @param node node that should be re-checked
     */
    public void updateNodeAcceptance ( final E node )
    {
        // Updating tree filtering
        final WebTree<E> tree = getTree ();
        if ( tree != null )
        {
            if ( tree instanceof WebAsyncTree )
            {
                // Cleaning up filter cache
                filter.clearCache ( node );

                // Updating tree node filtering
                ( ( WebAsyncTree ) tree ).updateSortingAndFiltering ( ( AsyncUniqueNode ) node.getParent () );
            }
            else if ( tree instanceof WebExTree )
            {
                // Cleaning up filter cache
                filter.clearCache ( node );

                // Updating tree node filtering
                ( ( WebExTree ) tree ).updateSortingAndFiltering ( node.getParent () );
            }
        }
    }

    /**
     * Returns tree to which this field applies filtering.
     *
     * @return tree to which this field applies filtering
     */
    public WebTree<E> getTree ()
    {
        return tree != null ? tree.get () : null;
    }

    /**
     * Returns whether this tree filter field is empty or not.
     *
     * @return true if this tree filter field is empty, false otherwise
     */
    public boolean isEmpty ()
    {
        final String text = getText ();
        return text == null || text.equals ( "" );
    }
}
TOP

Related Classes of com.alee.extended.tree.WebTreeFilterField

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.