1 /*
   2  * Copyright (c) 2002, 2008, 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                 if (path != null) {
 348                     isLeaf = treeModel.isLeaf(path.getLastPathComponent());
 349                     if (isLeaf) {
 350                         isExpanded = hasBeenExpanded = false;
 351                     }
 352                     else {
 353                         isExpanded = treeState.getExpandedState(path);
 354                         hasBeenExpanded = tree.hasBeenExpanded(path);
 355                     }
 356                     bounds = getPathBounds(tree, path);
 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                 if (path != null) {
 387                     isLeaf = treeModel.isLeaf(path.getLastPathComponent());
 388                     if (isLeaf) {
 389                         isExpanded = hasBeenExpanded = false;
 390                     }
 391                     else {
 392                         isExpanded = treeState.getExpandedState(path);
 393                         hasBeenExpanded = tree.hasBeenExpanded(path);
 394                     }
 395                     bounds = getPathBounds(tree, path);
 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 
 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 
 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             JTextField tf = new JTextField() {
 764                 @Override
 765                 public String getName() {
 766                     return "Tree.cellEditor";
 767                 }
 768             };
 769             DefaultCellEditor editor = new DefaultCellEditor(tf);
 770 
 771             // One click to edit.
 772             editor.setClickCountToStart(1);
 773             return editor;
 774         }
 775     }
 776 
 777     //
 778     // BasicTreeUI directly uses expandIcon outside of the Synth methods.
 779     // To get the correct context we return an instance of this that fetches
 780     // the SynthContext as needed.
 781     //
 782     private class ExpandedIconWrapper extends SynthIcon {
 783         public void paintIcon(SynthContext context, Graphics g, int x,
 784                               int y, int w, int h) {
 785             if (context == null) {
 786                 context = getContext(tree);
 787                 SynthIcon.paintIcon(expandedIcon, context, g, x, y, w, h);
 788                 context.dispose();
 789             }
 790             else {
 791                 SynthIcon.paintIcon(expandedIcon, context, g, x, y, w, h);
 792             }
 793         }
 794 
 795         public int getIconWidth(SynthContext context) {
 796             int width;
 797             if (context == null) {
 798                 context = getContext(tree);
 799                 width = SynthIcon.getIconWidth(expandedIcon, context);
 800                 context.dispose();
 801             }
 802             else {
 803                 width = SynthIcon.getIconWidth(expandedIcon, context);
 804             }
 805             return width;
 806         }
 807 
 808         public int getIconHeight(SynthContext context) {
 809             int height;
 810             if (context == null) {
 811                 context = getContext(tree);
 812                 height = SynthIcon.getIconHeight(expandedIcon, context);
 813                 context.dispose();
 814             }
 815             else {
 816                 height = SynthIcon.getIconHeight(expandedIcon, context);
 817             }
 818             return height;
 819         }
 820     }
 821 }