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