1 /*
   2  * Copyright (c) 2002, 2014, 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 package javax.swing.plaf.synth;
  26 
  27 import java.awt.Color;
  28 import java.awt.Component;
  29 import java.awt.Graphics;
  30 import java.awt.Insets;
  31 import java.awt.Rectangle;
  32 import java.beans.PropertyChangeEvent;
  33 import java.beans.PropertyChangeListener;
  34 import java.util.Enumeration;
  35 import javax.swing.DefaultCellEditor;
  36 import javax.swing.Icon;
  37 import javax.swing.JComponent;
  38 import javax.swing.JTextField;
  39 import javax.swing.JTree;
  40 import javax.swing.LookAndFeel;
  41 import javax.swing.plaf.ComponentUI;
  42 import javax.swing.plaf.UIResource;
  43 import javax.swing.plaf.basic.BasicTreeUI;
  44 import javax.swing.tree.DefaultTreeCellEditor;
  45 import javax.swing.tree.DefaultTreeCellRenderer;
  46 import javax.swing.tree.TreeCellEditor;
  47 import javax.swing.tree.TreeCellRenderer;
  48 import javax.swing.tree.TreeModel;
  49 import javax.swing.tree.TreePath;
  50 import sun.swing.plaf.synth.SynthIcon;
  51 
  52 /**
  53  * Provides the Synth L&F UI delegate for
  54  * {@link javax.swing.JTree}.
  55  *
  56  * @author Scott Violet
  57  * @since 1.7
  58  */
  59 public class SynthTreeUI extends BasicTreeUI
  60                          implements PropertyChangeListener, SynthUI {
  61     private SynthStyle style;
  62     private SynthStyle cellStyle;
  63 
  64     private SynthContext paintContext;
  65 
  66     private boolean drawHorizontalLines;
  67     private boolean drawVerticalLines;
  68 
  69     private Object linesStyle;
  70 
  71     private int padding;
  72 
  73     private boolean useTreeColors;
  74 
  75     private Icon expandedIconWrapper = new ExpandedIconWrapper();
  76 
  77     /**
  78      * Creates a new UI object for the given component.
  79      *
  80      * @param x component to create UI object for
  81      * @return the UI object
  82      */
  83     public static ComponentUI createUI(JComponent x) {
  84         return new SynthTreeUI();
  85     }
  86 
  87     /**
  88      * {@inheritDoc}
  89      */
  90     @Override
  91     public Icon getExpandedIcon() {
  92         return expandedIconWrapper;
  93     }
  94 
  95     /**
  96      * {@inheritDoc}
  97      */
  98     @Override
  99     protected void installDefaults() {
 100         updateStyle(tree);
 101     }
 102 
 103     private void updateStyle(JTree tree) {
 104         SynthContext context = getContext(tree, ENABLED);
 105         SynthStyle oldStyle = style;
 106 
 107         style = SynthLookAndFeel.updateStyle(context, this);
 108         if (style != oldStyle) {
 109             Object value;
 110 
 111             setExpandedIcon(style.getIcon(context, "Tree.expandedIcon"));
 112             setCollapsedIcon(style.getIcon(context, "Tree.collapsedIcon"));
 113 
 114             setLeftChildIndent(style.getInt(context, "Tree.leftChildIndent",
 115                                             0));
 116             setRightChildIndent(style.getInt(context, "Tree.rightChildIndent",
 117                                              0));
 118 
 119             drawHorizontalLines = style.getBoolean(
 120                           context, "Tree.drawHorizontalLines",true);
 121             drawVerticalLines = style.getBoolean(
 122                         context, "Tree.drawVerticalLines", true);
 123             linesStyle = style.get(context, "Tree.linesStyle");
 124 
 125                 value = style.get(context, "Tree.rowHeight");
 126                 if (value != null) {
 127                     LookAndFeel.installProperty(tree, "rowHeight", value);
 128                 }
 129 
 130                 value = style.get(context, "Tree.scrollsOnExpand");
 131                 LookAndFeel.installProperty(tree, "scrollsOnExpand",
 132                                                     value != null? value : Boolean.TRUE);
 133 
 134             padding = style.getInt(context, "Tree.padding", 0);
 135 
 136             largeModel = (tree.isLargeModel() && tree.getRowHeight() > 0);
 137 
 138             useTreeColors = style.getBoolean(context,
 139                                   "Tree.rendererUseTreeColors", true);
 140 
 141             Boolean showsRootHandles = style.getBoolean(
 142                     context, "Tree.showsRootHandles", Boolean.TRUE);
 143             LookAndFeel.installProperty(
 144                     tree, JTree.SHOWS_ROOT_HANDLES_PROPERTY, showsRootHandles);
 145 
 146             if (oldStyle != null) {
 147                 uninstallKeyboardActions();
 148                 installKeyboardActions();
 149             }
 150         }
 151         context.dispose();
 152 
 153         context = getContext(tree, Region.TREE_CELL, ENABLED);
 154         cellStyle = SynthLookAndFeel.updateStyle(context, this);
 155         context.dispose();
 156     }
 157 
 158     /**
 159      * {@inheritDoc}
 160      */
 161     @Override
 162     protected void installListeners() {
 163         super.installListeners();
 164         tree.addPropertyChangeListener(this);
 165     }
 166 
 167     /**
 168      * {@inheritDoc}
 169      */
 170     @Override
 171     public SynthContext getContext(JComponent c) {
 172         return getContext(c, SynthLookAndFeel.getComponentState(c));
 173     }
 174 
 175     private SynthContext getContext(JComponent c, int state) {
 176         return SynthContext.getContext(SynthContext.class, c,
 177                     SynthLookAndFeel.getRegion(c), style, state);
 178     }
 179 
 180     private SynthContext getContext(JComponent c, Region region) {
 181         return getContext(c, region, getComponentState(c, region));
 182     }
 183 
 184     private SynthContext getContext(JComponent c, Region region, int state) {
 185         return SynthContext.getContext(SynthContext.class, c,
 186                                        region, cellStyle, state);
 187     }
 188 
 189     private int getComponentState(JComponent c, Region region) {
 190         // Always treat the cell as selected, will be adjusted appropriately
 191         // when painted.
 192         return ENABLED | SELECTED;
 193     }
 194 
 195     /**
 196      * {@inheritDoc}
 197      */
 198     @Override
 199     protected TreeCellEditor createDefaultCellEditor() {
 200         TreeCellRenderer renderer = tree.getCellRenderer();
 201         DefaultTreeCellEditor editor;
 202 
 203         if(renderer != null && (renderer instanceof DefaultTreeCellRenderer)) {
 204             editor = new SynthTreeCellEditor(tree, (DefaultTreeCellRenderer)
 205                                              renderer);
 206         }
 207         else {
 208             editor = new SynthTreeCellEditor(tree, null);
 209         }
 210         return editor;
 211     }
 212 
 213     /**
 214      * {@inheritDoc}
 215      */
 216     @Override
 217     protected TreeCellRenderer createDefaultCellRenderer() {
 218         return new SynthTreeCellRenderer();
 219     }
 220 
 221     /**
 222      * {@inheritDoc}
 223      */
 224     @Override
 225     protected void uninstallDefaults() {
 226         SynthContext context = getContext(tree, ENABLED);
 227 
 228         style.uninstallDefaults(context);
 229         context.dispose();
 230         style = null;
 231 
 232         context = getContext(tree, Region.TREE_CELL, ENABLED);
 233         cellStyle.uninstallDefaults(context);
 234         context.dispose();
 235         cellStyle = null;
 236 
 237 
 238         if (tree.getTransferHandler() instanceof UIResource) {
 239             tree.setTransferHandler(null);
 240         }
 241     }
 242 
 243     /**
 244      * {@inheritDoc}
 245      */
 246     @Override
 247     protected void uninstallListeners() {
 248         super.uninstallListeners();
 249         tree.removePropertyChangeListener(this);
 250     }
 251 
 252     /**
 253      * Notifies this UI delegate to repaint the specified component.
 254      * This method paints the component background, then calls
 255      * the {@link #paint(SynthContext,Graphics)} method.
 256      *
 257      * <p>In general, this method does not need to be overridden by subclasses.
 258      * All Look and Feel rendering code should reside in the {@code paint} method.
 259      *
 260      * @param g the {@code Graphics} object used for painting
 261      * @param c the component being painted
 262      * @see #paint(SynthContext,Graphics)
 263      */
 264     @Override
 265     public void update(Graphics g, JComponent c) {
 266         SynthContext context = getContext(c);
 267 
 268         SynthLookAndFeel.update(context, g);
 269         context.getPainter().paintTreeBackground(context,
 270                           g, 0, 0, c.getWidth(), c.getHeight());
 271         paint(context, g);
 272         context.dispose();
 273     }
 274 
 275     /**
 276      * {@inheritDoc}
 277      */
 278     @Override
 279     public void paintBorder(SynthContext context, Graphics g, int x,
 280                             int y, int w, int h) {
 281         context.getPainter().paintTreeBorder(context, g, x, y, w, h);
 282     }
 283 
 284     /**
 285      * Paints the specified component according to the Look and Feel.
 286      * <p>This method is not used by Synth Look and Feel.
 287      * Painting is handled by the {@link #paint(SynthContext,Graphics)} method.
 288      *
 289      * @param g the {@code Graphics} object used for painting
 290      * @param c the component being painted
 291      * @see #paint(SynthContext,Graphics)
 292      */
 293     @Override
 294     public void paint(Graphics g, JComponent c) {
 295         SynthContext context = getContext(c);
 296 
 297         paint(context, g);
 298         context.dispose();
 299     }
 300 
 301     /**
 302      * Paints the specified component.
 303      *
 304      * @param context context for the component being painted
 305      * @param g the {@code Graphics} object used for painting
 306      * @see #update(Graphics,JComponent)
 307      */
 308     protected void paint(SynthContext context, Graphics g) {
 309         paintContext = context;
 310 
 311         updateLeadSelectionRow();
 312 
 313         Rectangle paintBounds = g.getClipBounds();
 314         Insets insets = tree.getInsets();
 315         TreePath initialPath = getClosestPathForLocation(tree, 0,
 316                                                          paintBounds.y);
 317         Enumeration paintingEnumerator = treeState.getVisiblePathsFrom
 318                                               (initialPath);
 319         int row = treeState.getRowForPath(initialPath);
 320         int endY = paintBounds.y + paintBounds.height;
 321         TreeModel treeModel = tree.getModel();
 322         SynthContext cellContext = getContext(tree, Region.TREE_CELL);
 323 
 324         drawingCache.clear();
 325 
 326         setHashColor(context.getStyle().getColor(context,
 327                                                 ColorType.FOREGROUND));
 328 
 329         if (paintingEnumerator != null) {
 330             // First pass, draw the rows
 331 
 332             boolean done = false;
 333             boolean isExpanded;
 334             boolean hasBeenExpanded;
 335             boolean isLeaf;
 336             Rectangle rowBounds = new Rectangle(0, 0, tree.getWidth(),0);
 337             Rectangle bounds;
 338             TreePath path;
 339             TreeCellRenderer renderer = tree.getCellRenderer();
 340             DefaultTreeCellRenderer dtcr = (renderer instanceof
 341                        DefaultTreeCellRenderer) ? (DefaultTreeCellRenderer)
 342                        renderer : null;
 343 
 344             configureRenderer(cellContext);
 345             while (!done && paintingEnumerator.hasMoreElements()) {
 346                 path = (TreePath)paintingEnumerator.nextElement();
 347                 bounds = getPathBounds(tree, path);
 348                 if ((path != null) && (bounds != null)) {
 349                     isLeaf = treeModel.isLeaf(path.getLastPathComponent());
 350                     if (isLeaf) {
 351                         isExpanded = hasBeenExpanded = false;
 352                     }
 353                     else {
 354                         isExpanded = treeState.getExpandedState(path);
 355                         hasBeenExpanded = tree.hasBeenExpanded(path);
 356                     }
 357                     rowBounds.y = bounds.y;
 358                     rowBounds.height = bounds.height;
 359                     paintRow(renderer, dtcr, context, cellContext, g,
 360                              paintBounds, insets, bounds, rowBounds, path,
 361                              row, isExpanded, hasBeenExpanded, isLeaf);
 362                     if ((bounds.y + bounds.height) >= endY) {
 363                         done = true;
 364                     }
 365                 }
 366                 else {
 367                     done = true;
 368                 }
 369                 row++;
 370             }
 371 
 372             // Draw the connecting lines and controls.
 373             // Find each parent and have them draw a line to their last child
 374             boolean rootVisible = tree.isRootVisible();
 375             TreePath parentPath = initialPath;
 376             parentPath = parentPath.getParentPath();
 377             while (parentPath != null) {
 378                 paintVerticalPartOfLeg(g, paintBounds, insets, parentPath);
 379                 drawingCache.put(parentPath, Boolean.TRUE);
 380                 parentPath = parentPath.getParentPath();
 381             }
 382             done = false;
 383             paintingEnumerator = treeState.getVisiblePathsFrom(initialPath);
 384             while (!done && paintingEnumerator.hasMoreElements()) {
 385                 path = (TreePath)paintingEnumerator.nextElement();
 386                 bounds = getPathBounds(tree, path);
 387                 if ((path != null) && (bounds != null)) {
 388                     isLeaf = treeModel.isLeaf(path.getLastPathComponent());
 389                     if (isLeaf) {
 390                         isExpanded = hasBeenExpanded = false;
 391                     }
 392                     else {
 393                         isExpanded = treeState.getExpandedState(path);
 394                         hasBeenExpanded = tree.hasBeenExpanded(path);
 395                     }
 396                     // See if the vertical line to the parent has been drawn.
 397                     parentPath = path.getParentPath();
 398                     if (parentPath != null) {
 399                         if (drawingCache.get(parentPath) == null) {
 400                             paintVerticalPartOfLeg(g, paintBounds, insets,
 401                                                    parentPath);
 402                             drawingCache.put(parentPath, Boolean.TRUE);
 403                         }
 404                         paintHorizontalPartOfLeg(g,
 405                                                  paintBounds, insets, bounds,
 406                                                  path, row, isExpanded,
 407                                                  hasBeenExpanded, isLeaf);
 408                     }
 409                     else if (rootVisible && row == 0) {
 410                         paintHorizontalPartOfLeg(g,
 411                                                  paintBounds, insets, bounds,
 412                                                  path, row, isExpanded,
 413                                                  hasBeenExpanded, isLeaf);
 414                     }
 415                     if (shouldPaintExpandControl(path, row, isExpanded,
 416                                                  hasBeenExpanded, isLeaf)) {
 417                         paintExpandControl(g, paintBounds,
 418                                            insets, bounds, path, row,
 419                                            isExpanded, hasBeenExpanded,isLeaf);
 420                     }
 421                     if ((bounds.y + bounds.height) >= endY) {
 422                         done = true;
 423                     }
 424                 }
 425                 else {
 426                     done = true;
 427                 }
 428                 row++;
 429             }
 430         }
 431         cellContext.dispose();
 432 
 433         paintDropLine(g);
 434 
 435         // Empty out the renderer pane, allowing renderers to be gc'ed.
 436         rendererPane.removeAll();
 437 
 438         paintContext = null;
 439     }
 440 
 441     private void configureRenderer(SynthContext context) {
 442         TreeCellRenderer renderer = tree.getCellRenderer();
 443 
 444         if (renderer instanceof DefaultTreeCellRenderer) {
 445             DefaultTreeCellRenderer r = (DefaultTreeCellRenderer)renderer;
 446             SynthStyle style = context.getStyle();
 447 
 448             context.setComponentState(ENABLED | SELECTED);
 449             Color color = r.getTextSelectionColor();
 450             if (color == null || (color instanceof UIResource)) {
 451                 r.setTextSelectionColor(style.getColor(
 452                                      context, ColorType.TEXT_FOREGROUND));
 453             }
 454             color = r.getBackgroundSelectionColor();
 455             if (color == null || (color instanceof UIResource)) {
 456                 r.setBackgroundSelectionColor(style.getColor(
 457                                         context, ColorType.TEXT_BACKGROUND));
 458             }
 459 
 460             context.setComponentState(ENABLED);
 461             color = r.getTextNonSelectionColor();
 462             if (color == null || color instanceof UIResource) {
 463                 r.setTextNonSelectionColor(style.getColorForState(
 464                                         context, ColorType.TEXT_FOREGROUND));
 465             }
 466             color = r.getBackgroundNonSelectionColor();
 467             if (color == null || color instanceof UIResource) {
 468                 r.setBackgroundNonSelectionColor(style.getColorForState(
 469                                   context, ColorType.TEXT_BACKGROUND));
 470             }
 471         }
 472     }
 473 
 474     /**
 475      * {@inheritDoc}
 476      */
 477     @Override
 478     protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
 479                                             Insets insets, Rectangle bounds,
 480                                             TreePath path, int row,
 481                                             boolean isExpanded,
 482                                             boolean hasBeenExpanded, boolean
 483                                             isLeaf) {
 484         if (drawHorizontalLines) {
 485             super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds,
 486                                            path, row, isExpanded,
 487                                            hasBeenExpanded, isLeaf);
 488         }
 489     }
 490 
 491     /**
 492      * {@inheritDoc}
 493      */
 494     @Override
 495     protected void paintHorizontalLine(Graphics g, JComponent c, int y,
 496                                       int left, int right) {
 497         paintContext.getStyle().getGraphicsUtils(paintContext).drawLine(
 498             paintContext, "Tree.horizontalLine", g, left, y, right, y, linesStyle);
 499     }
 500 
 501     /**
 502      * {@inheritDoc}
 503      */
 504     @Override
 505     protected void paintVerticalPartOfLeg(Graphics g,
 506                                           Rectangle clipBounds, Insets insets,
 507                                           TreePath path) {
 508         if (drawVerticalLines) {
 509             super.paintVerticalPartOfLeg(g, clipBounds, insets, path);
 510         }
 511     }
 512 
 513     /**
 514      * {@inheritDoc}
 515      */
 516     @Override
 517     protected void paintVerticalLine(Graphics g, JComponent c, int x, int top,
 518                                     int bottom) {
 519         paintContext.getStyle().getGraphicsUtils(paintContext).drawLine(
 520             paintContext, "Tree.verticalLine", g, x, top, x, bottom, linesStyle);
 521     }
 522 
 523     private void paintRow(TreeCellRenderer renderer,
 524                DefaultTreeCellRenderer dtcr, SynthContext treeContext,
 525                SynthContext cellContext, Graphics g, Rectangle clipBounds,
 526                Insets insets, Rectangle bounds, Rectangle rowBounds,
 527                TreePath path, int row, boolean isExpanded,
 528                boolean hasBeenExpanded, boolean isLeaf) {
 529         // Don't paint the renderer if editing this row.
 530         boolean selected = tree.isRowSelected(row);
 531 
 532         JTree.DropLocation dropLocation = tree.getDropLocation();
 533         boolean isDrop = dropLocation != null
 534                          && dropLocation.getChildIndex() == -1
 535                          && path == dropLocation.getPath();
 536 
 537         int state = ENABLED;
 538         if (selected || isDrop) {
 539             state |= SELECTED;
 540         }
 541 
 542         if (tree.isFocusOwner() && row == getLeadSelectionRow()) {
 543             state |= FOCUSED;
 544         }
 545 
 546         cellContext.setComponentState(state);
 547 
 548         if (dtcr != null && (dtcr.getBorderSelectionColor() instanceof
 549                              UIResource)) {
 550             dtcr.setBorderSelectionColor(style.getColor(
 551                                              cellContext, ColorType.FOCUS));
 552         }
 553         SynthLookAndFeel.updateSubregion(cellContext, g, rowBounds);
 554         cellContext.getPainter().paintTreeCellBackground(cellContext, g,
 555                     rowBounds.x, rowBounds.y, rowBounds.width,
 556                     rowBounds.height);
 557         cellContext.getPainter().paintTreeCellBorder(cellContext, g,
 558                     rowBounds.x, rowBounds.y, rowBounds.width,
 559                     rowBounds.height);
 560         if (editingComponent != null && editingRow == row) {
 561             return;
 562         }
 563 
 564         int leadIndex;
 565 
 566         if (tree.hasFocus()) {
 567             leadIndex = getLeadSelectionRow();
 568         }
 569         else {
 570             leadIndex = -1;
 571         }
 572 
 573         Component component = renderer.getTreeCellRendererComponent(
 574                          tree, path.getLastPathComponent(),
 575                          selected, isExpanded, isLeaf, row,
 576                          (leadIndex == row));
 577 
 578         rendererPane.paintComponent(g, component, tree, bounds.x, bounds.y,
 579                                     bounds.width, bounds.height, true);
 580     }
 581 
 582     private int findCenteredX(int x, int iconWidth) {
 583         return tree.getComponentOrientation().isLeftToRight()
 584                ? x - (int)Math.ceil(iconWidth / 2.0)
 585                : x - (int)Math.floor(iconWidth / 2.0);
 586     }
 587 
 588     /**
 589      * {@inheritDoc}
 590      */
 591     @Override
 592     protected void paintExpandControl(Graphics g, Rectangle clipBounds,
 593             Insets insets, Rectangle bounds, TreePath path, int row,
 594             boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf) {
 595         //modify the paintContext's state to match the state for the row
 596         //this is a hack in that it requires knowledge of the subsequent
 597         //method calls. The point is, the context used in drawCentered
 598         //should reflect the state of the row, not of the tree.
 599         boolean isSelected = tree.getSelectionModel().isPathSelected(path);
 600         int state = paintContext.getComponentState();
 601         if (isSelected) {
 602             paintContext.setComponentState(state | SynthConstants.SELECTED);
 603         }
 604         super.paintExpandControl(g, clipBounds, insets, bounds, path, row,
 605                 isExpanded, hasBeenExpanded, isLeaf);
 606         paintContext.setComponentState(state);
 607     }
 608 
 609     /**
 610      * {@inheritDoc}
 611      */
 612     @Override
 613     protected void drawCentered(Component c, Graphics graphics, Icon icon,
 614                                 int x, int y) {
 615         int w = SynthIcon.getIconWidth(icon, paintContext);
 616         int h = SynthIcon.getIconHeight(icon, paintContext);
 617 
 618         SynthIcon.paintIcon(icon, paintContext, graphics,
 619                             findCenteredX(x, w),
 620                             y - h/2, w, h);
 621     }
 622 
 623     /**
 624      * {@inheritDoc}
 625      */
 626     @Override
 627     public void propertyChange(PropertyChangeEvent event) {
 628         if (SynthLookAndFeel.shouldUpdateStyle(event)) {
 629             updateStyle((JTree)event.getSource());
 630         }
 631 
 632         if ("dropLocation" == event.getPropertyName()) {
 633             JTree.DropLocation oldValue = (JTree.DropLocation)event.getOldValue();
 634             repaintDropLocation(oldValue);
 635             repaintDropLocation(tree.getDropLocation());
 636         }
 637     }
 638 
 639     /**
 640      * {@inheritDoc}
 641      */
 642     @Override
 643     protected void paintDropLine(Graphics g) {
 644         JTree.DropLocation loc = tree.getDropLocation();
 645         if (!isDropLine(loc)) {
 646             return;
 647         }
 648 
 649         Color c = (Color)style.get(paintContext, "Tree.dropLineColor");
 650         if (c != null) {
 651             g.setColor(c);
 652             Rectangle rect = getDropLineRect(loc);
 653             g.fillRect(rect.x, rect.y, rect.width, rect.height);
 654         }
 655     }
 656 
 657     private void repaintDropLocation(JTree.DropLocation loc) {
 658         if (loc == null) {
 659             return;
 660         }
 661 
 662         Rectangle r;
 663 
 664         if (isDropLine(loc)) {
 665             r = getDropLineRect(loc);
 666         } else {
 667             r = tree.getPathBounds(loc.getPath());
 668             if (r != null) {
 669                 r.x = 0;
 670                 r.width = tree.getWidth();
 671             }
 672         }
 673 
 674         if (r != null) {
 675             tree.repaint(r);
 676         }
 677     }
 678 
 679     /**
 680      * {@inheritDoc}
 681      */
 682     @Override
 683     protected int getRowX(int row, int depth) {
 684         return super.getRowX(row, depth) + padding;
 685     }
 686 
 687     @SuppressWarnings("serial") // Superclass is not serializable across versions
 688     private class SynthTreeCellRenderer extends DefaultTreeCellRenderer
 689                                implements UIResource {
 690         SynthTreeCellRenderer() {
 691         }
 692 
 693         @Override
 694         public String getName() {
 695             return "Tree.cellRenderer";
 696         }
 697 
 698         @Override
 699         public Component getTreeCellRendererComponent(JTree tree, Object value,
 700                                                       boolean sel,
 701                                                       boolean expanded,
 702                                                       boolean leaf, int row,
 703                                                       boolean hasFocus) {
 704             if (!useTreeColors && (sel || hasFocus)) {
 705                 SynthLookAndFeel.setSelectedUI((SynthLabelUI)SynthLookAndFeel.
 706                              getUIOfType(getUI(), SynthLabelUI.class),
 707                                    sel, hasFocus, tree.isEnabled(), false);
 708             }
 709             else {
 710                 SynthLookAndFeel.resetSelectedUI();
 711             }
 712             return super.getTreeCellRendererComponent(tree, value, sel,
 713                                                       expanded, leaf, row, hasFocus);
 714         }
 715 
 716         @Override
 717         public void paint(Graphics g) {
 718             paintComponent(g);
 719             if (hasFocus) {
 720                 SynthContext context = getContext(tree, Region.TREE_CELL);
 721 
 722                 if (context.getStyle() == null) {
 723                     assert false: "SynthTreeCellRenderer is being used " +
 724                         "outside of UI that created it";
 725                     return;
 726                 }
 727                 int imageOffset = 0;
 728                 Icon currentI = getIcon();
 729 
 730                 if(currentI != null && getText() != null) {
 731                     imageOffset = currentI.getIconWidth() +
 732                                           Math.max(0, getIconTextGap() - 1);
 733                 }
 734                 if (selected) {
 735                     context.setComponentState(ENABLED | SELECTED);
 736                 }
 737                 else {
 738                     context.setComponentState(ENABLED);
 739                 }
 740                 if(getComponentOrientation().isLeftToRight()) {
 741                     context.getPainter().paintTreeCellFocus(context, g,
 742                             imageOffset, 0, getWidth() - imageOffset,
 743                             getHeight());
 744                 }
 745                 else {
 746                     context.getPainter().paintTreeCellFocus(context, g,
 747                             0, 0, getWidth() - imageOffset, getHeight());
 748                 }
 749                 context.dispose();
 750             }
 751             SynthLookAndFeel.resetSelectedUI();
 752         }
 753     }
 754 
 755 
 756     private static class SynthTreeCellEditor extends DefaultTreeCellEditor {
 757         public SynthTreeCellEditor(JTree tree,
 758                                    DefaultTreeCellRenderer renderer) {
 759             super(tree, renderer);
 760             setBorderSelectionColor(null);
 761         }
 762 
 763         @Override
 764         protected TreeCellEditor createTreeCellEditor() {
 765             @SuppressWarnings("serial") // anonymous class
 766             JTextField tf = new JTextField() {
 767                 @Override
 768                 public String getName() {
 769                     return "Tree.cellEditor";
 770                 }
 771             };
 772             DefaultCellEditor editor = new DefaultCellEditor(tf);
 773 
 774             // One click to edit.
 775             editor.setClickCountToStart(1);
 776             return editor;
 777         }
 778     }
 779 
 780     //
 781     // BasicTreeUI directly uses expandIcon outside of the Synth methods.
 782     // To get the correct context we return an instance of this that fetches
 783     // the SynthContext as needed.
 784     //
 785     private class ExpandedIconWrapper extends SynthIcon {
 786         public void paintIcon(SynthContext context, Graphics g, int x,
 787                               int y, int w, int h) {
 788             if (context == null) {
 789                 context = getContext(tree);
 790                 SynthIcon.paintIcon(expandedIcon, context, g, x, y, w, h);
 791                 context.dispose();
 792             }
 793             else {
 794                 SynthIcon.paintIcon(expandedIcon, context, g, x, y, w, h);
 795             }
 796         }
 797 
 798         public int getIconWidth(SynthContext context) {
 799             int width;
 800             if (context == null) {
 801                 context = getContext(tree);
 802                 width = SynthIcon.getIconWidth(expandedIcon, context);
 803                 context.dispose();
 804             }
 805             else {
 806                 width = SynthIcon.getIconWidth(expandedIcon, context);
 807             }
 808             return width;
 809         }
 810 
 811         public int getIconHeight(SynthContext context) {
 812             int height;
 813             if (context == null) {
 814                 context = getContext(tree);
 815                 height = SynthIcon.getIconHeight(expandedIcon, context);
 816                 context.dispose();
 817             }
 818             else {
 819                 height = SynthIcon.getIconHeight(expandedIcon, context);
 820             }
 821             return height;
 822         }
 823     }
 824 }