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

com.alee.laf.tree.WebTreeUI 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.laf.tree;

import com.alee.api.jdk.Consumer;
import com.alee.extended.tree.WebCheckBoxTree;
import com.alee.laf.WebLookAndFeel;
import com.alee.laf.tree.behavior.TreePathHoverBehavior;
import com.alee.managers.icon.Icons;
import com.alee.managers.style.*;
import com.alee.painter.DefaultPainter;
import com.alee.painter.Painter;
import com.alee.painter.PainterSupport;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.util.Enumeration;

/**
 * Custom UI for {@link JTree} component.
 *
 * @author Mikle Garin
 */
public class WebTreeUI extends WTreeUI implements ShapeSupport, MarginSupport, PaddingSupport
{
    /**
     * Style settings.
     */
    protected TreeSelectionStyle selectionStyle;

    /**
     * Component painter.
     */
    @DefaultPainter ( TreePainter.class )
    protected ITreePainter painter;

    /**
     * Listeners.
     */
    protected transient TreePathHoverBehavior hoverNodeTracker;

    /**
     * Runtime variables.
     */
    protected transient int hoverRow = -1;

    /**
     * Returns an instance of the {@link WebTreeUI} for the specified component.
     * This tricky method is used by {@link UIManager} to create component UIs when needed.
     *
     * @param c component that will use UI instance
     * @return instance of the {@link WebTreeUI}
     */
    public static ComponentUI createUI ( final JComponent c )
    {
        return new WebTreeUI ();
    }

    @Override
    public void installUI ( final JComponent c )
    {
        // Installing UI
        super.installUI ( c );

        // todo Probably completely remove this?
        // Overwrite indent in case WebLookAndFeel is not installed as LaF
        if ( !WebLookAndFeel.isInstalled () )
        {
            setRightChildIndent ( 12 );
            setLeftChildIndent ( 12 );
        }

        // Allow each cell to choose its own preferred height
        tree.setRowHeight ( -1 );

        // Modifying default drop mode
        // USE_SELECTION mode is not preferred since WebLaF provides a better visual drop representation
        tree.setDropMode ( DropMode.ON );

        // Use a moderate amount of visible rows by default
        // BasicTreeUI uses 20 rows by default which is too much for most of cases
        tree.setVisibleRowCount ( 10 );

        // Forces tree to save changes when another tree node is selected instead of cancelling them
        tree.setInvokesStopCellEditing ( true );

        // Hover behavior
        hoverNodeTracker = new TreePathHoverBehavior ( tree, true )
        {
            @Override
            public void hoverChanged ( final TreePath previous, final TreePath current )
            {
                // Updating hover row
                final int previousRow = hoverRow;
                hoverRow = current != null ? tree.getRowForPath ( current ) : -1;

                // Repainting nodes according to hover changes
                // This occurs only if hover highlight is enabled
                if ( painter != null && painter.isRowHoverDecorationSupported () )
                {
                    repaintRow ( previousRow );
                    repaintRow ( hoverRow );
                }

                // Updating custom WebLaF tooltip display state
                final TreeToolTipProvider provider = getToolTipProvider ();
                if ( provider != null )
                {
                    final TreeCellArea oldArea = previousRow != -1 ? new TreeCellArea ( previousRow ) : null;
                    final TreeCellArea newArea = hoverRow != -1 ? new TreeCellArea ( hoverRow ) : null;
                    provider.hoverAreaChanged ( tree, oldArea, newArea );
                }

                // Informing {@link com.alee.laf.tree.WebTree} about hover node change
                // This is performed here to avoid excessive listeners usage for the same purpose
                if ( tree instanceof WebTree )
                {
                    final MutableTreeNode p = previous != null ? ( MutableTreeNode ) previous.getLastPathComponent () : null;
                    final MutableTreeNode c = current != null ? ( MutableTreeNode ) current.getLastPathComponent () : null;
                    ( ( WebTree ) tree ).fireHoverChanged ( p, c );
                }
            }

            /**
             * Repaints specified row if it exists and it is visible.
             *
             * @param row row to repaint
             */
            private void repaintRow ( final int row )
            {
                if ( row != -1 )
                {
                    final Rectangle rowBounds = getRowBounds ( row, true );
                    if ( rowBounds != null )
                    {
                        tree.repaint ( rowBounds );
                    }
                }
            }
        };
        hoverNodeTracker.install ();

        // Applying skin
        StyleManager.installSkin ( tree );
    }

    @Override
    public void uninstallUI ( final JComponent c )
    {
        // Uninstalling applied skin
        StyleManager.uninstallSkin ( tree );

        // Removing custom listeners
        hoverNodeTracker.uninstall ();
        hoverNodeTracker = null;

        // Uninstalling UI
        super.uninstallUI ( c );
    }

    @Override
    public Shape getShape ()
    {
        return PainterSupport.getShape ( tree, painter );
    }

    @Override
    public boolean isShapeDetectionEnabled ()
    {
        return PainterSupport.isShapeDetectionEnabled ( tree, painter );
    }

    @Override
    public void setShapeDetectionEnabled ( final boolean enabled )
    {
        PainterSupport.setShapeDetectionEnabled ( tree, painter, enabled );
    }

    @Override
    public Insets getMargin ()
    {
        return PainterSupport.getMargin ( tree );
    }

    @Override
    public void setMargin ( final Insets margin )
    {
        PainterSupport.setMargin ( tree, margin );
    }

    @Override
    public Insets getPadding ()
    {
        return PainterSupport.getPadding ( tree );
    }

    @Override
    public void setPadding ( final Insets padding )
    {
        PainterSupport.setPadding ( tree, padding );
    }

    /**
     * Returns tree painter.
     *
     * @return tree painter
     */
    public Painter getPainter ()
    {
        return PainterSupport.getPainter ( painter );
    }

    /**
     * Sets tree painter.
     * Pass null to remove tree painter.
     *
     * @param painter new tree painter
     */
    public void setPainter ( final Painter painter )
    {
        PainterSupport.setPainter ( tree, new Consumer ()
        {
            @Override
            public void accept ( final ITreePainter newPainter )
            {
                WebTreeUI.this.painter = newPainter;
            }
        }, this.painter, painter, ITreePainter.class, AdaptiveTreePainter.class );
    }

    @Override
    public int getHoverRow ()
    {
        return hoverRow;
    }

    @Override
    public int getExactRowForLocation ( final Point location )
    {
        return getExactRowForLocation ( location, isFullLineSelection () );
    }

    @Override
    public int getExactRowForLocation ( final Point location, final boolean fullRow )
    {
        if ( tree != null )
        {
            final Enumeration visiblePaths = getVisiblePaths ();
            if ( visiblePaths != null )
            {
                while ( visiblePaths.hasMoreElements () )
                {
                    final TreePath treePath = visiblePaths.nextElement ();
                    final Rectangle bounds = getPathBounds ( treePath, fullRow );
                    if ( bounds != null && bounds.contains ( location ) )
                    {
                        return getRowForPath ( tree, treePath );
                    }
                }
            }
        }
        return -1;
    }

    @Override
    public Rectangle getRowBounds ( final int row )
    {
        return getRowBounds ( row, isFullLineSelection () );
    }

    @Override
    public Rectangle getRowBounds ( final int row, final boolean fullRow )
    {
        final TreePath path = getPathForRow ( tree, row );
        return fullRow ? getFullPathBounds ( path ) : getPathBounds ( tree, path );
    }

    /**
     * Returns full path bounds.
     *
     * @param path    tree path
     * @param fullRow whether take the whole row into account or just node renderer rect
     * @return full path bounds
     */
    public Rectangle getPathBounds ( final TreePath path, final boolean fullRow )
    {
        return fullRow ? getFullPathBounds ( path ) : getPathBounds ( tree, path );
    }

    /**
     * Returns full path bounds.
     *
     * @param path tree path
     * @return full path bounds
     */
    private Rectangle getFullPathBounds ( final TreePath path )
    {
        final Rectangle b = getPathBounds ( tree, path );
        if ( b != null )
        {
            final Insets insets = tree.getInsets ();
            b.x = insets.left;
            b.width = tree.getWidth () - insets.left - insets.right;
        }
        return b;
    }

    /**
     * Returns visible paths enumeration.
     * This is just a small method for convenient enumeration retrieval.
     *
     * @return visible paths enumeration
     */
    public Enumeration getVisiblePaths ()
    {
        if ( tree.isShowing () )
        {
            final Rectangle paintBounds = tree.getVisibleRect ();
            final TreePath initialPath = getClosestPathForLocation ( tree, 0, paintBounds.y );
            if ( initialPath != null )
            {
                return treeState.getVisiblePathsFrom ( initialPath );
            }
        }
        return null;
    }

    @Override
    protected TreeCellEditor createDefaultCellEditor ()
    {
        return new WebTreeCellEditor ();
    }

    @Override
    protected TreeCellRenderer createDefaultCellRenderer ()
    {
        return new WebTreeCellRenderer.UIResource> ();
    }

    @Override
    protected void selectPathForEvent ( final TreePath path, final MouseEvent e )
    {
        if ( !isLocationInCheckBoxControl ( path, e.getX (), e.getY () ) )
        {
            super.selectPathForEvent ( path, e );
        }
    }

    @Override
    public boolean isLocationInCheckBoxControl ( final TreePath path, final int x, final int y )
    {
        if ( tree instanceof WebCheckBoxTree )
        {
            final WebCheckBoxTree checkBoxTree = ( WebCheckBoxTree ) tree;
            if ( checkBoxTree.isCheckingByUserEnabled () )
            {
                final MutableTreeNode node = ( MutableTreeNode ) path.getLastPathComponent ();
                if ( checkBoxTree.isCheckBoxVisible ( node ) && checkBoxTree.isCheckBoxEnabled ( node ) )
                {
                    final Rectangle checkBoxBounds = checkBoxTree.getCheckBoxBounds ( path );
                    return checkBoxBounds != null && checkBoxBounds.contains ( x, y );
                }
                else
                {
                    return false;
                }
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    @Override
    public Icon getExpandedIcon ()
    {
        return tree.isEnabled () ? Icons.squareMinus : Icons.squareMinusDisabled;
    }

    @Override
    public Icon getCollapsedIcon ()
    {
        return tree.isEnabled () ? Icons.squarePlus : Icons.squarePlusDisabled;
    }

    @Override
    public CellRendererPane getCellRendererPane ()
    {
        return rendererPane;
    }

    @Override
    public AbstractLayoutCache getTreeLayoutCache ()
    {
        return treeState;
    }

    @Override
    public TreeSelectionStyle getSelectionStyle ()
    {
        return selectionStyle;
    }

    @Override
    public void setSelectionStyle ( final TreeSelectionStyle style )
    {
        this.selectionStyle = style;
    }

    /**
     * Returns whether tree selection style points that the whole line is a single cell or not.
     *
     * @return true if tree selection style points that the whole line is a single cell, false otherwise
     */
    protected boolean isFullLineSelection ()
    {
        return selectionStyle == TreeSelectionStyle.line;
    }

    /**
     * Returns {@link TreeToolTipProvider} for {@link JTree} that uses this {@link WebTreeUI}.
     *
     * @return {@link TreeToolTipProvider} for {@link JTree} that uses this {@link WebTreeUI}
     */
    protected TreeToolTipProvider getToolTipProvider ()
    {
        return tree instanceof WebTree ? ( ( WebTree ) tree ).getToolTipProvider () : null;
    }

    @Override
    public boolean contains ( final JComponent c, final int x, final int y )
    {
        return PainterSupport.contains ( c, this, painter, x, y );
    }

    @Override
    public void paint ( final Graphics g, final JComponent c )
    {
        if ( painter != null )
        {
            painter.prepareToPaint ( drawingCache, currentCellRenderer );
            painter.paint ( ( Graphics2D ) g, c, this, new Bounds ( c ) );
        }
    }

    @Override
    public Dimension getPreferredSize ( final JComponent c )
    {
        return PainterSupport.getPreferredSize ( c, super.getPreferredSize ( c ), painter );
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy