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