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 26 package javax.swing.plaf.metal; 27 28 import javax.swing.*; 29 import javax.swing.event.*; 30 import java.awt.*; 31 import java.awt.event.*; 32 import java.beans.*; 33 import java.io.*; 34 import java.util.*; 35 import javax.swing.plaf.*; 36 import javax.swing.tree.*; 37 38 import 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 * <thead> 51 * <tr> 52 * <th scope="col">Hint 53 * <th scope="col">Description 54 * </thead> 55 * <tbody> 56 * <tr> 57 * <th scope="row">Angled 58 * <td>A line is drawn connecting the child to the parent. For handling of 59 * the root node refer to {@link JTree#setRootVisible} and 60 * {@link JTree#setShowsRootHandles}. 61 * <tr> 62 * <th scope="row">Horizontal 63 * <td>A horizontal line is drawn dividing the children of the root node. 64 * <tr> 65 * <th scope="row">None 66 * <td>Do not draw any visual indication between nodes. 67 * </tbody> 68 * </table> 69 * <p> 70 * As it is typically impractical to obtain the <code>TreeUI</code> from 71 * the <code>JTree</code> and cast to an instance of <code>MetalTreeUI</code> 72 * you enable this property via the client property 73 * <code>JTree.lineStyle</code>. For example, to switch to 74 * <code>Horizontal</code> style you would do: 75 * <code>tree.putClientProperty("JTree.lineStyle", "Horizontal");</code> 76 * <p> 77 * The default is <code>Angled</code>. 78 * 79 * @author Tom Santos 80 * @author Steve Wilson (value add stuff) 81 */ 82 public class MetalTreeUI extends BasicTreeUI { 83 84 private static Color lineColor; 85 86 private static final String LINE_STYLE = "JTree.lineStyle"; 87 88 private static final String LEG_LINE_STYLE_STRING = "Angled"; 89 private static final String HORIZ_STYLE_STRING = "Horizontal"; 90 private static final String NO_STYLE_STRING = "None"; 91 92 private static final int LEG_LINE_STYLE = 2; 93 private static final int HORIZ_LINE_STYLE = 1; 94 private static final int NO_LINE_STYLE = 0; 95 96 private int lineStyle = LEG_LINE_STYLE; 97 private PropertyChangeListener lineStyleListener = new LineListener(); 98 99 /** 100 * Constructs the {@code MetalTreeUI}. 101 * 102 * @param x a component 103 * @return the instance of the {@code MetalTreeUI} 104 */ createUI(JComponent x)105 public static ComponentUI createUI(JComponent x) { 106 return new MetalTreeUI(); 107 } 108 109 /** 110 * Constructs the {@code MetalTreeUI}. 111 */ MetalTreeUI()112 public MetalTreeUI() { 113 super(); 114 } 115 getHorizontalLegBuffer()116 protected int getHorizontalLegBuffer() { 117 return 3; 118 } 119 installUI( JComponent c )120 public void installUI( JComponent c ) { 121 super.installUI( c ); 122 lineColor = UIManager.getColor( "Tree.line" ); 123 124 Object lineStyleFlag = c.getClientProperty( LINE_STYLE ); 125 decodeLineStyle(lineStyleFlag); 126 c.addPropertyChangeListener(lineStyleListener); 127 128 } 129 uninstallUI( JComponent c)130 public void uninstallUI( JComponent c) { 131 c.removePropertyChangeListener(lineStyleListener); 132 super.uninstallUI(c); 133 } 134 135 /** 136 * Converts between the string passed into the client property 137 * and the internal representation (currently and int) 138 * 139 * @param lineStyleFlag a flag 140 */ decodeLineStyle(Object lineStyleFlag)141 protected void decodeLineStyle(Object lineStyleFlag) { 142 if ( lineStyleFlag == null || 143 lineStyleFlag.equals(LEG_LINE_STYLE_STRING)) { 144 lineStyle = LEG_LINE_STYLE; // default case 145 } else { 146 if ( lineStyleFlag.equals(NO_STYLE_STRING) ) { 147 lineStyle = NO_LINE_STYLE; 148 } else if ( lineStyleFlag.equals(HORIZ_STYLE_STRING) ) { 149 lineStyle = HORIZ_LINE_STYLE; 150 } 151 } 152 } 153 154 /** 155 * Returns {@code true} if a point with X coordinate {@code mouseX} 156 * and Y coordinate {@code mouseY} is in expanded control. 157 * 158 * @param row a row 159 * @param rowLevel a row level 160 * @param mouseX X coordinate 161 * @param mouseY Y coordinate 162 * @return {@code true} if a point with X coordinate {@code mouseX} 163 * and Y coordinate {@code mouseY} is in expanded control. 164 */ isLocationInExpandControl(int row, int rowLevel, int mouseX, int mouseY)165 protected boolean isLocationInExpandControl(int row, int rowLevel, 166 int mouseX, int mouseY) { 167 if(tree != null && !isLeaf(row)) { 168 int boxWidth; 169 170 if(getExpandedIcon() != null) 171 boxWidth = getExpandedIcon().getIconWidth() + 6; 172 else 173 boxWidth = 8; 174 175 Insets i = tree.getInsets(); 176 int boxLeftX = (i != null) ? i.left : 0; 177 178 179 boxLeftX += (((rowLevel + depthOffset - 1) * totalChildIndent) + 180 getLeftChildIndent()) - boxWidth/2; 181 182 int boxRightX = boxLeftX + boxWidth; 183 184 return mouseX >= boxLeftX && mouseX <= boxRightX; 185 } 186 return false; 187 } 188 paint(Graphics g, JComponent c)189 public void paint(Graphics g, JComponent c) { 190 super.paint( g, c ); 191 192 193 // Paint the lines 194 if (lineStyle == HORIZ_LINE_STYLE && !largeModel) { 195 paintHorizontalSeparators(g,c); 196 } 197 } 198 199 /** 200 * Paints the horizontal separators. 201 * 202 * @param g an instance of {@code Graphics} 203 * @param c a component 204 */ paintHorizontalSeparators(Graphics g, JComponent c)205 protected void paintHorizontalSeparators(Graphics g, JComponent c) { 206 g.setColor( lineColor ); 207 208 Rectangle clipBounds = g.getClipBounds(); 209 210 int beginRow = getRowForPath(tree, getClosestPathForLocation 211 (tree, 0, clipBounds.y)); 212 int endRow = getRowForPath(tree, getClosestPathForLocation 213 (tree, 0, clipBounds.y + clipBounds.height - 1)); 214 215 if ( beginRow <= -1 || endRow <= -1 ) { 216 return; 217 } 218 219 for ( int i = beginRow; i <= endRow; ++i ) { 220 TreePath path = getPathForRow(tree, i); 221 222 if(path != null && path.getPathCount() == 2) { 223 Rectangle rowBounds = getPathBounds(tree,getPathForRow 224 (tree, i)); 225 226 // Draw a line at the top 227 if(rowBounds != null) 228 g.drawLine(clipBounds.x, rowBounds.y, 229 clipBounds.x + clipBounds.width, rowBounds.y); 230 } 231 } 232 233 } 234 paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path)235 protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, 236 Insets insets, TreePath path) { 237 if (lineStyle == LEG_LINE_STYLE) { 238 super.paintVerticalPartOfLeg(g, clipBounds, insets, path); 239 } 240 } 241 paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf)242 protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds, 243 Insets insets, Rectangle bounds, 244 TreePath path, int row, 245 boolean isExpanded, 246 boolean hasBeenExpanded, boolean 247 isLeaf) { 248 if (lineStyle == LEG_LINE_STYLE) { 249 super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, 250 path, row, isExpanded, 251 hasBeenExpanded, isLeaf); 252 } 253 } 254 255 /** This class listens for changes in line style */ 256 class LineListener implements PropertyChangeListener { propertyChange(PropertyChangeEvent e)257 public void propertyChange(PropertyChangeEvent e) { 258 String name = e.getPropertyName(); 259 if ( name.equals( LINE_STYLE ) ) { 260 decodeLineStyle(e.getNewValue()); 261 } 262 } 263 } // end class PaletteListener 264 265 } 266