1/* 2 * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package javax.swing.plaf.metal; 27 28import javax.swing.*; 29import javax.swing.event.*; 30import java.awt.*; 31import java.awt.event.*; 32import java.beans.*; 33import java.io.*; 34import java.util.*; 35import javax.swing.plaf.*; 36import javax.swing.tree.*; 37 38import javax.swing.plaf.basic.*; 39 40/** 41 * The metal look and feel implementation of <code>TreeUI</code>. 42 * <p> 43 * <code>MetalTreeUI</code> allows for configuring how to 44 * visually render the spacing and delineation between nodes. The following 45 * hints are supported: 46 * 47 * <table class="striped"> 48 * <caption>Descriptions of supported hints: Angled, Horizontal, and None 49 * </caption> 50 * <tr> 51 * <th>Angled</th> 52 * <td>A line is drawn connecting the child to the parent. For handling 53 * of the root node refer to 54 * {@link javax.swing.JTree#setRootVisible} and 55 * {@link javax.swing.JTree#setShowsRootHandles}. 56 * </td> 57 * </tr> 58 * <tr> 59 * <th>Horizontal</th> 60 * <td>A horizontal line is drawn dividing the children of the root node.</td> 61 * </tr> 62 * <tr> 63 * <th>None</th> 64 * <td>Do not draw any visual indication between nodes.</td> 65 * </tr> 66 * </table> 67 * 68 * <p> 69 * As it is typically impractical to obtain the <code>TreeUI</code> from 70 * the <code>JTree</code> and cast to an instance of <code>MetalTreeUI</code> 71 * you enable this property via the client property 72 * <code>JTree.lineStyle</code>. For example, to switch to 73 * <code>Horizontal</code> style you would do: 74 * <code>tree.putClientProperty("JTree.lineStyle", "Horizontal");</code> 75 * <p> 76 * The default is <code>Angled</code>. 77 * 78 * @author Tom Santos 79 * @author Steve Wilson (value add stuff) 80 */ 81public class MetalTreeUI extends BasicTreeUI { 82 83 private static Color lineColor; 84 85 private static final String LINE_STYLE = "JTree.lineStyle"; 86 87 private static final String LEG_LINE_STYLE_STRING = "Angled"; 88 private static final String HORIZ_STYLE_STRING = "Horizontal"; 89 private static final String NO_STYLE_STRING = "None"; 90 91 private static final int LEG_LINE_STYLE = 2; 92 private static final int HORIZ_LINE_STYLE = 1; 93 private static final int NO_LINE_STYLE = 0; 94 95 private int lineStyle = LEG_LINE_STYLE; 96 private PropertyChangeListener lineStyleListener = new LineListener(); 97 98 /** 99 * Constructs the {@code MetalTreeUI}. 100 * 101 * @param x a component 102 * @return the instance of the {@code MetalTreeUI} 103 */ 104 public static ComponentUI createUI(JComponent x) { 105 return new MetalTreeUI(); 106 } 107 108 /** 109 * Constructs the {@code MetalTreeUI}. 110 */ 111 public MetalTreeUI() { 112 super(); 113 } 114 115 protected int getHorizontalLegBuffer() { 116 return 3; 117 } 118 119 public void installUI( JComponent c ) { 120 super.installUI( c ); 121 lineColor = UIManager.getColor( "Tree.line" ); 122 123 Object lineStyleFlag = c.getClientProperty( LINE_STYLE ); 124 decodeLineStyle(lineStyleFlag); 125 c.addPropertyChangeListener(lineStyleListener); 126 127 } 128 129 public void uninstallUI( JComponent c) { 130 c.removePropertyChangeListener(lineStyleListener); 131 super.uninstallUI(c); 132 } 133 134 /** 135 * Converts between the string passed into the client property 136 * and the internal representation (currently and int) 137 * 138 * @param lineStyleFlag a flag 139 */ 140 protected void decodeLineStyle(Object lineStyleFlag) { 141 if ( lineStyleFlag == null || 142 lineStyleFlag.equals(LEG_LINE_STYLE_STRING)) { 143 lineStyle = LEG_LINE_STYLE; // default case 144 } else { 145 if ( lineStyleFlag.equals(NO_STYLE_STRING) ) { 146 lineStyle = NO_LINE_STYLE; 147 } else if ( lineStyleFlag.equals(HORIZ_STYLE_STRING) ) { 148 lineStyle = HORIZ_LINE_STYLE; 149 } 150 } 151 } 152 153 /** 154 * Returns {@code true} if a point with X coordinate {@code mouseX} 155 * and Y coordinate {@code mouseY} is in expanded control. 156 * 157 * @param row a row 158 * @param rowLevel a row level 159 * @param mouseX X coordinate 160 * @param mouseY Y coordinate 161 * @return {@code true} if a point with X coordinate {@code mouseX} 162 * and Y coordinate {@code mouseY} is in expanded control. 163 */ 164 protected boolean isLocationInExpandControl(int row, int rowLevel, 165 int mouseX, int mouseY) { 166 if(tree != null && !isLeaf(row)) { 167 int boxWidth; 168 169 if(getExpandedIcon() != null) 170 boxWidth = getExpandedIcon().getIconWidth() + 6; 171 else 172 boxWidth = 8; 173 174 Insets i = tree.getInsets(); 175 int boxLeftX = (i != null) ? i.left : 0; 176 177 178 boxLeftX += (((rowLevel + depthOffset - 1) * totalChildIndent) + 179 getLeftChildIndent()) - boxWidth/2; 180 181 int boxRightX = boxLeftX + boxWidth; 182 183 return mouseX >= boxLeftX && mouseX <= boxRightX; 184 } 185 return false; 186 } 187 188 public void paint(Graphics g, JComponent c) { 189 super.paint( g, c ); 190 191 192 // Paint the lines 193 if (lineStyle == HORIZ_LINE_STYLE && !largeModel) { 194 paintHorizontalSeparators(g,c); 195 } 196 } 197 198 /** 199 * Paints the horizontal separators. 200 * 201 * @param g an instance of {@code Graphics} 202 * @param c a component 203 */ 204 protected void paintHorizontalSeparators(Graphics g, JComponent c) { 205 g.setColor( lineColor ); 206 207 Rectangle clipBounds = g.getClipBounds(); 208 209 int beginRow = getRowForPath(tree, getClosestPathForLocation 210 (tree, 0, clipBounds.y)); 211 int endRow = getRowForPath(tree, getClosestPathForLocation 212 (tree, 0, clipBounds.y + clipBounds.height - 1)); 213 214 if ( beginRow <= -1 || endRow <= -1 ) { 215 return; 216 } 217 218 for ( int i = beginRow; i <= endRow; ++i ) { 219 TreePath path = getPathForRow(tree, i); 220 221 if(path != null && path.getPathCount() == 2) { 222 Rectangle rowBounds = getPathBounds(tree,getPathForRow 223 (tree, i)); 224 225 // Draw a line at the top 226 if(rowBounds != null) 227 g.drawLine(clipBounds.x, rowBounds.y, 228 clipBounds.x + clipBounds.width, rowBounds.y); 229 } 230 } 231 232 } 233 234 protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, 235 Insets insets, TreePath path) { 236 if (lineStyle == LEG_LINE_STYLE) { 237 super.paintVerticalPartOfLeg(g, clipBounds, insets, path); 238 } 239 } 240 241 protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, 242 Insets insets, Rectangle bounds, 243 TreePath path, int row, 244 boolean isExpanded, 245 boolean hasBeenExpanded, boolean 246 isLeaf) { 247 if (lineStyle == LEG_LINE_STYLE) { 248 super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, 249 path, row, isExpanded, 250 hasBeenExpanded, isLeaf); 251 } 252 } 253 254 /** This class listens for changes in line style */ 255 class LineListener implements PropertyChangeListener { 256 public void propertyChange(PropertyChangeEvent e) { 257 String name = e.getPropertyName(); 258 if ( name.equals( LINE_STYLE ) ) { 259 decodeLineStyle(e.getNewValue()); 260 } 261 } 262 } // end class PaletteListener 263 264} 265