Source for javax.swing.plaf.metal.MetalTreeUI

   1: /* MetalTreeUI.java
   2:    Copyright (C) 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.metal;
  40: 
  41: import java.awt.Graphics;
  42: import java.awt.Insets;
  43: import java.awt.Rectangle;
  44: import java.beans.PropertyChangeEvent;
  45: import java.beans.PropertyChangeListener;
  46: 
  47: import javax.swing.JComponent;
  48: import javax.swing.JTree;
  49: import javax.swing.UIManager;
  50: import javax.swing.tree.TreePath;
  51: import javax.swing.plaf.ComponentUI;
  52: import javax.swing.plaf.basic.BasicTreeUI;
  53: 
  54: /**
  55:  * A UI delegate for the {@link JTree} component.
  56:  */
  57: public class MetalTreeUI extends BasicTreeUI
  58: {
  59:   /**
  60:    * Listens for property changes of the line style and updates the
  61:    * internal setting.
  62:    */
  63:   private class LineStyleListener
  64:     implements PropertyChangeListener
  65:   {
  66: 
  67:     public void propertyChange(PropertyChangeEvent e)
  68:     {
  69:       if (e.getPropertyName().equals(LINE_STYLE_PROPERTY))
  70:         decodeLineStyle(e.getNewValue());
  71:     }
  72:       
  73:   }
  74: 
  75:   /**
  76:    * The key to the lineStyle client property.
  77:    */
  78:   private static final String LINE_STYLE_PROPERTY = "JTree.lineStyle";
  79: 
  80:   /**
  81:    * The property value indicating no line style.
  82:    */
  83:   private static final String LINE_STYLE_VALUE_NONE = "None";
  84: 
  85:   /**
  86:    * The property value indicating angled line style.
  87:    */
  88:   private static final String LINE_STYLE_VALUE_ANGLED = "Angled";
  89: 
  90:   /**
  91:    * The property value indicating horizontal line style.
  92:    */
  93:   private static final String LINE_STYLE_VALUE_HORIZONTAL = "Horizontal";
  94: 
  95:   /**
  96:    * The line style for None.
  97:    */
  98:   private static final int LINE_STYLE_NONE = 0;
  99: 
 100:   /**
 101:    * The line style for Angled.
 102:    */
 103:   private static final int LINE_STYLE_ANGLED = 1;
 104: 
 105:   /**
 106:    * The line style for Horizontal.
 107:    */
 108:   private static final int LINE_STYLE_HORIZONTAL = 2;
 109: 
 110:   /**
 111:    * The current line style.
 112:    */
 113:   private int lineStyle;
 114: 
 115:   /**
 116:    * Listens for changes on the line style property and updates the
 117:    * internal settings.
 118:    */
 119:   private PropertyChangeListener lineStyleListener;
 120: 
 121:   /**
 122:    * Constructs a new instance of <code>MetalTreeUI</code>.
 123:    */
 124:   public MetalTreeUI()
 125:   {
 126:     super();
 127:   }
 128: 
 129:   /**
 130:    * Returns a new instance of <code>MetalTreeUI</code>.
 131:    *
 132:    * @param component the component for which we return an UI instance
 133:    *
 134:    * @return A new instance of <code>MetalTreeUI</code>.
 135:    */
 136:   public static ComponentUI createUI(JComponent component)
 137:   {
 138:     return new MetalTreeUI();
 139:   }
 140:   
 141:   /**
 142:    * The horizontal element of legs between nodes starts at the right of the
 143:    * left-hand side of the child node by default. This method makes the
 144:    * leg end before that.
 145:    */
 146:   protected int getHorizontalLegBuffer()
 147:   {
 148:     return super.getHorizontalLegBuffer();
 149:   }
 150: 
 151:   /**
 152:    * Configures the specified component appropriate for the look and feel.
 153:    * This method is invoked when the ComponentUI instance is being installed 
 154:    * as the UI delegate on the specified component. This method should completely 
 155:    * configure the component for the look and feel, including the following:
 156:    * 1. Install any default property values for color, fonts, borders, icons, 
 157:    *    opacity, etc. on the component. Whenever possible, property values
 158:    *    initialized by the client program should not be overridden.
 159:    * 2. Install a LayoutManager on the component if necessary.
 160:    * 3. Create/add any required sub-components to the component.
 161:    * 4. Create/install event listeners on the component.
 162:    * 5. Create/install a PropertyChangeListener on the component in order 
 163:    *    to detect and respond to component property changes appropriately.
 164:    * 6. Install keyboard UI (mnemonics, traversal, etc.) on the component.
 165:    * 7. Initialize any appropriate instance data. 
 166:    */
 167:   public void installUI(JComponent c)
 168:   {
 169:     super.installUI(c);
 170: 
 171:     Object lineStyleProp = c.getClientProperty(LINE_STYLE_PROPERTY);
 172:     decodeLineStyle(lineStyleProp);
 173:     if (lineStyleListener == null)
 174:       lineStyleListener = new LineStyleListener();
 175:     c.addPropertyChangeListener(lineStyleListener);
 176:   }
 177:   
 178:   /**
 179:    * Reverses configuration which was done on the specified component during 
 180:    * installUI. This method is invoked when this UIComponent instance is being 
 181:    * removed as the UI delegate for the specified component. This method should 
 182:    * undo the configuration performed in installUI, being careful to leave the 
 183:    * JComponent instance in a clean state (no extraneous listeners, 
 184:    * look-and-feel-specific property objects, etc.). This should include 
 185:    * the following:
 186:    * 1. Remove any UI-set borders from the component.
 187:    * 2. Remove any UI-set layout managers on the component.
 188:    * 3. Remove any UI-added sub-components from the component.
 189:    * 4. Remove any UI-added event/property listeners from the component.
 190:    * 5. Remove any UI-installed keyboard UI from the component.
 191:    * 6. Nullify any allocated instance data objects to allow for GC. 
 192:    */
 193:   public void uninstallUI(JComponent c)
 194:   {
 195:     super.uninstallUI(c);
 196:     if (lineStyleListener != null)
 197:       c.removePropertyChangeListener(lineStyleListener);
 198:     lineStyleListener = null;
 199:   }
 200:   
 201:   /**
 202:    * This function converts between the string passed into the client
 203:    * property and the internal representation (currently an int).
 204:    * 
 205:    * @param lineStyleFlag - String representation
 206:    */     
 207:   protected void decodeLineStyle(Object lineStyleFlag)
 208:   {
 209:     if (lineStyleFlag == null || lineStyleFlag.equals(LINE_STYLE_VALUE_ANGLED))
 210:       lineStyle = LINE_STYLE_ANGLED;
 211:     else if (lineStyleFlag.equals(LINE_STYLE_VALUE_HORIZONTAL))
 212:       lineStyle = LINE_STYLE_HORIZONTAL;
 213:     else if (lineStyleFlag.equals(LINE_STYLE_VALUE_NONE))
 214:       lineStyle = LINE_STYLE_NONE;
 215:     else
 216:       lineStyle = LINE_STYLE_ANGLED;
 217:   }
 218: 
 219:   /**
 220:    * Checks if the location is in expand control.
 221:    * 
 222:    * @param row - current row
 223:    * @param rowLevel - current level
 224:    * @param mouseX - current x location of the mouse click
 225:    * @param mouseY - current y location of the mouse click
 226:    */
 227:   protected boolean isLocationInExpandControl(int row, int rowLevel,
 228:                                           int mouseX, int mouseY)
 229:   {
 230:     return super.isLocationInExpandControl(tree.getPathForRow(row), 
 231:                                            mouseX, mouseY);
 232:   }
 233:   
 234:   /**
 235:    * Paints the specified component appropriate for the look and feel. 
 236:    * This method is invoked from the ComponentUI.update method when the 
 237:    * specified component is being painted. Subclasses should override this 
 238:    * method and use the specified Graphics object to render the content of 
 239:    * the component.
 240:    * 
 241:    * @param g - the current graphics configuration.
 242:    * @param c - the current component to draw
 243:    */
 244:   public void paint(Graphics g, JComponent c)
 245:   {
 246:     // Calls BasicTreeUI's paint since it takes care of painting all
 247:     // types of icons. 
 248:     super.paint(g, c);
 249: 
 250:     if (lineStyle == LINE_STYLE_HORIZONTAL)
 251:       paintHorizontalSeparators(g, c);
 252:   }
 253:   
 254:   /**
 255:    * Paints the horizontal separators.
 256:    * 
 257:    * @param g - the current graphics configuration.
 258:    * @param c - the current component to draw
 259:    */
 260:   protected void paintHorizontalSeparators(Graphics g, JComponent c)
 261:   {
 262:     g.setColor(UIManager.getColor("Tree.line"));
 263:     Rectangle clip = g.getClipBounds();
 264:     int row0 = getRowForPath(tree, getClosestPathForLocation(tree, 0, clip.y));
 265:     int row1 =
 266:       getRowForPath(tree, getClosestPathForLocation(tree, 0,
 267:                                                     clip.y + clip.height - 1));
 268:     if (row0 >= 0 && row1 >= 0)
 269:       {
 270:         for (int i = row0; i <= row1; i++)
 271:           {
 272:             TreePath p = getPathForRow(tree, i);
 273:             if (p != null && p.getPathCount() == 2)
 274:               {
 275:                 Rectangle r = getPathBounds(tree, getPathForRow(tree, i));
 276:                 if (r != null)
 277:                   {
 278:                     g.drawLine(clip.x, r.y, clip.x + clip.width, r.y);
 279:                   }
 280:               }
 281:           }
 282:       }
 283:   }
 284: 
 285:   
 286:   /**
 287:    * Paints the vertical part of the leg. The receiver should NOT modify 
 288:    * clipBounds, insets.
 289:    * 
 290:    * @param g - the current graphics configuration.
 291:    * @param clipBounds -
 292:    * @param insets - 
 293:    * @param path - the current path
 294:    */
 295:   protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
 296:                                     Insets insets, TreePath path)
 297:   {
 298:     if (lineStyle == LINE_STYLE_ANGLED)
 299:       super.paintVerticalPartOfLeg(g, clipBounds, insets, path);
 300:   }
 301: 
 302:   /**
 303:    * Paints the horizontal part of the leg. The receiver should NOT \
 304:    * modify clipBounds, or insets.
 305:    * NOTE: parentRow can be -1 if the root is not visible.
 306:    */
 307:   protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
 308:                                         Insets insets, Rectangle bounds,
 309:                                         TreePath path, int row,
 310:                                         boolean isExpanded, boolean hasBeenExpanded,
 311:                                         boolean isLeaf)
 312:   {
 313:     if (lineStyle == LINE_STYLE_ANGLED)
 314:       super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row, 
 315:                                      isExpanded, hasBeenExpanded, isLeaf);
 316:   }
 317: }