/* * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.plaf.metal; import javax.swing.*; import javax.swing.event.*; import java.awt.*; import java.awt.event.*; import java.beans.*; import java.io.*; import java.util.*; import javax.swing.plaf.*; import javax.swing.tree.*; import javax.swing.plaf.basic.*; /** * The metal look and feel implementation of TreeUI. *

* MetalTreeUI allows for configuring how to * visually render the spacing and delineation between nodes. The following * hints are supported: * * * * * * * * * * * * * * * *
Descriptions of supported hints: Angled, * Horizontal, and None
AngledA line is drawn connecting the child to the parent. For handling * of the root node refer to * {@link javax.swing.JTree#setRootVisible} and * {@link javax.swing.JTree#setShowsRootHandles}. *
HorizontalA horizontal line is drawn dividing the children of the root node.
NoneDo not draw any visual indication between nodes.
* *

* As it is typically impractical to obtain the TreeUI from * the JTree and cast to an instance of MetalTreeUI * you enable this property via the client property * JTree.lineStyle. For example, to switch to * Horizontal style you would do: * tree.putClientProperty("JTree.lineStyle", "Horizontal"); *

* The default is Angled. * * @author Tom Santos * @author Steve Wilson (value add stuff) */ public class MetalTreeUI extends BasicTreeUI { private static Color lineColor; private static final String LINE_STYLE = "JTree.lineStyle"; private static final String LEG_LINE_STYLE_STRING = "Angled"; private static final String HORIZ_STYLE_STRING = "Horizontal"; private static final String NO_STYLE_STRING = "None"; private static final int LEG_LINE_STYLE = 2; private static final int HORIZ_LINE_STYLE = 1; private static final int NO_LINE_STYLE = 0; private int lineStyle = LEG_LINE_STYLE; private PropertyChangeListener lineStyleListener = new LineListener(); /** * Constructs the {@code MetalTreeUI}. * * @param x a component * @return the instance of the {@code MetalTreeUI} */ public static ComponentUI createUI(JComponent x) { return new MetalTreeUI(); } /** * Constructs the {@code MetalTreeUI}. */ public MetalTreeUI() { super(); } protected int getHorizontalLegBuffer() { return 3; } public void installUI( JComponent c ) { super.installUI( c ); lineColor = UIManager.getColor( "Tree.line" ); Object lineStyleFlag = c.getClientProperty( LINE_STYLE ); decodeLineStyle(lineStyleFlag); c.addPropertyChangeListener(lineStyleListener); } public void uninstallUI( JComponent c) { c.removePropertyChangeListener(lineStyleListener); super.uninstallUI(c); } /** * Converts between the string passed into the client property * and the internal representation (currently and int) * * @param lineStyleFlag a flag */ protected void decodeLineStyle(Object lineStyleFlag) { if ( lineStyleFlag == null || lineStyleFlag.equals(LEG_LINE_STYLE_STRING)) { lineStyle = LEG_LINE_STYLE; // default case } else { if ( lineStyleFlag.equals(NO_STYLE_STRING) ) { lineStyle = NO_LINE_STYLE; } else if ( lineStyleFlag.equals(HORIZ_STYLE_STRING) ) { lineStyle = HORIZ_LINE_STYLE; } } } /** * Returns {@code true} if a point with X coordinate {@code mouseX} * and Y coordinate {@code mouseY} is in expanded control. * * @param row a row * @param rowLevel a row level * @param mouseX X coordinate * @param mouseY Y coordinate * @return {@code true} if a point with X coordinate {@code mouseX} * and Y coordinate {@code mouseY} is in expanded control. */ protected boolean isLocationInExpandControl(int row, int rowLevel, int mouseX, int mouseY) { if(tree != null && !isLeaf(row)) { int boxWidth; if(getExpandedIcon() != null) boxWidth = getExpandedIcon().getIconWidth() + 6; else boxWidth = 8; Insets i = tree.getInsets(); int boxLeftX = (i != null) ? i.left : 0; boxLeftX += (((rowLevel + depthOffset - 1) * totalChildIndent) + getLeftChildIndent()) - boxWidth/2; int boxRightX = boxLeftX + boxWidth; return mouseX >= boxLeftX && mouseX <= boxRightX; } return false; } public void paint(Graphics g, JComponent c) { super.paint( g, c ); // Paint the lines if (lineStyle == HORIZ_LINE_STYLE && !largeModel) { paintHorizontalSeparators(g,c); } } /** * Paints the horizontal separators. * * @param g an instance of {@code Graphics} * @param c a component */ protected void paintHorizontalSeparators(Graphics g, JComponent c) { g.setColor( lineColor ); Rectangle clipBounds = g.getClipBounds(); int beginRow = getRowForPath(tree, getClosestPathForLocation (tree, 0, clipBounds.y)); int endRow = getRowForPath(tree, getClosestPathForLocation (tree, 0, clipBounds.y + clipBounds.height - 1)); if ( beginRow <= -1 || endRow <= -1 ) { return; } for ( int i = beginRow; i <= endRow; ++i ) { TreePath path = getPathForRow(tree, i); if(path != null && path.getPathCount() == 2) { Rectangle rowBounds = getPathBounds(tree,getPathForRow (tree, i)); // Draw a line at the top if(rowBounds != null) g.drawLine(clipBounds.x, rowBounds.y, clipBounds.x + clipBounds.width, rowBounds.y); } } } protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) { if (lineStyle == LEG_LINE_STYLE) { super.paintVerticalPartOfLeg(g, clipBounds, insets, path); } } protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf) { if (lineStyle == LEG_LINE_STYLE) { super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf); } } /** This class listens for changes in line style */ class LineListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { String name = e.getPropertyName(); if ( name.equals( LINE_STYLE ) ) { decodeLineStyle(e.getNewValue()); } } } // end class PaletteListener }