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