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