JXTree.java
上传用户:zhengdagz
上传日期:2014-03-06
资源大小:1956k
文件大小:26k
源码类别:

xml/soap/webservice

开发平台:

Java

  1. /*
  2.  * $Id: JXTree.java,v 1.15 2005/10/12 11:26:57 kleopatra Exp $
  3.  *
  4.  * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
  5.  * Santa Clara, California 95054, U.S.A. All rights reserved.
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2.1 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful,
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * Lesser General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the Free Software
  19.  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  20.  */
  21. package org.jdesktop.swingx;
  22. import java.awt.Component;
  23. import java.awt.Cursor;
  24. import java.awt.Point;
  25. import java.awt.Rectangle;
  26. import java.awt.event.ActionEvent;
  27. import java.awt.event.MouseEvent;
  28. import java.beans.PropertyChangeEvent;
  29. import java.beans.PropertyChangeListener;
  30. import java.lang.reflect.Method;
  31. import java.util.Hashtable;
  32. import java.util.Vector;
  33. import java.util.regex.Matcher;
  34. import java.util.regex.Pattern;
  35. import javax.swing.AbstractButton;
  36. import javax.swing.Action;
  37. import javax.swing.ActionMap;
  38. import javax.swing.Icon;
  39. import javax.swing.JComponent;
  40. import javax.swing.JTree;
  41. import javax.swing.KeyStroke;
  42. import javax.swing.ListCellRenderer;
  43. import javax.swing.event.ChangeEvent;
  44. import javax.swing.event.ChangeListener;
  45. import javax.swing.plaf.basic.BasicTreeUI;
  46. import javax.swing.tree.DefaultTreeCellRenderer;
  47. import javax.swing.tree.TreeCellRenderer;
  48. import javax.swing.tree.TreeModel;
  49. import javax.swing.tree.TreeNode;
  50. import javax.swing.tree.TreePath;
  51. import org.jdesktop.swingx.AbstractSearchable.SearchResult;
  52. import org.jdesktop.swingx.JXList.ListSearchable;
  53. import org.jdesktop.swingx.decorator.ComponentAdapter;
  54. import org.jdesktop.swingx.decorator.FilterPipeline;
  55. import org.jdesktop.swingx.decorator.HighlighterPipeline;
  56. /**
  57.  * JXTree.
  58.  *
  59.  * PENDING: support filtering/sorting.
  60.  * 
  61.  * @author Ramesh Gupta
  62.  * @author Jeanette Winzenburg
  63.  */
  64. public class JXTree extends JTree {
  65.     private Method conversionMethod = null;
  66.     private final static Class[] methodSignature = new Class[] {Object.class};
  67.     private final static Object[] methodArgs = new Object[] {null};
  68.     protected FilterPipeline filters;
  69.     protected HighlighterPipeline highlighters;
  70.     private ChangeListener highlighterChangeListener;
  71.     private DelegatingRenderer delegatingRenderer;
  72.     /**
  73.      * Mouse/Motion/Listener keeping track of mouse moved in
  74.      * cell coordinates.
  75.      */
  76.     private RolloverProducer rolloverProducer;
  77.     /**
  78.      * RolloverController: listens to cell over events and
  79.      * repaints entered/exited rows.
  80.      */
  81.     private LinkController linkController;
  82.     private boolean overwriteIcons;
  83.     private Searchable searchable;
  84.     
  85.     
  86.     
  87.     /**
  88.      * Constructs a <code>JXTree</code> with a sample model. The default model
  89.      * used by this tree defines a leaf node as any node without children.
  90.      */
  91.     public JXTree() {
  92. initActions();
  93.     }
  94.     /**
  95.      * Constructs a <code>JXTree</code> with each element of the specified array
  96.      * as the child of a new root node which is not displayed. By default, this
  97.      * tree defines a leaf node as any node without children.
  98.      *
  99.      * This version of the constructor simply invokes the super class version
  100.      * with the same arguments.
  101.      *
  102.      * @param value an array of objects that are children of the root.
  103.      */
  104.     public JXTree(Object[] value) {
  105.         super(value);
  106. initActions();
  107.     }
  108.     /**
  109.      * Constructs a <code>JXTree</code> with each element of the specified
  110.      * Vector as the child of a new root node which is not displayed.
  111.      * By default, this tree defines a leaf node as any node without children.
  112.      *
  113.      * This version of the constructor simply invokes the super class version
  114.      * with the same arguments.
  115.      *
  116.      * @param value an Vector of objects that are children of the root.
  117.      */
  118.     public JXTree(Vector value) {
  119.         super(value);
  120. initActions();
  121.     }
  122.     /**
  123.      * Constructs a <code>JXTree</code> created from a Hashtable which does not
  124.      * display with root. Each value-half of the key/value pairs in the HashTable
  125.      * becomes a child of the new root node. By default, the tree defines a leaf
  126.      * node as any node without children.
  127.      *
  128.      * This version of the constructor simply invokes the super class version
  129.      * with the same arguments.
  130.      *
  131.      * @param value a Hashtable containing objects that are children of the root.
  132.      */
  133.     public JXTree(Hashtable value) {
  134.         super(value);
  135. initActions();
  136.     }
  137.     /**
  138.      * Constructs a <code>JXTree</code> with the specified TreeNode as its root,
  139.      * which displays the root node. By default, the tree defines a leaf node as
  140.      * any node without children.
  141.      *
  142.      * This version of the constructor simply invokes the super class version
  143.      * with the same arguments.
  144.      *
  145.      * @param root root node of this tree
  146.      */
  147.     public JXTree(TreeNode root) {
  148.         super(root, false);
  149.     }
  150.     /**
  151.      * Constructs a <code>JXTree</code> with the specified TreeNode as its root,
  152.      * which displays the root node and which decides whether a node is a leaf
  153.      * node in the specified manner.
  154.      *
  155.      * This version of the constructor simply invokes the super class version
  156.      * with the same arguments.
  157.      *
  158.      * @param root root node of this tree
  159.      * @param asksAllowsChildren if true, only nodes that do not allow children
  160.      * are leaf nodes; otherwise, any node without children is a leaf node;
  161.      * @see javax.swing.tree.DefaultTreeModel#asksAllowsChildren
  162.      */
  163.     public JXTree(TreeNode root, boolean asksAllowsChildren) {
  164.         super(root, asksAllowsChildren);
  165. initActions();
  166.     }
  167.     /**
  168.      * Constructs an instance of <code>JXTree</code> which displays the root
  169.      * node -- the tree is created using the specified data model.
  170.      * 
  171.      * This version of the constructor simply invokes the super class version
  172.      * with the same arguments.
  173.      * 
  174.      * @param newModel
  175.      *            the <code>TreeModel</code> to use as the data model
  176.      */
  177.     public JXTree(TreeModel newModel) {
  178.         super(newModel);
  179.         initActions();
  180.         // To support delegation of convertValueToText() to the model...
  181.         conversionMethod = getValueConversionMethod(newModel);
  182.     }
  183.     public void setModel(TreeModel newModel) {
  184.         super.setModel(newModel);
  185.         // To support delegation of convertValueToText() to the model...
  186.         conversionMethod = getValueConversionMethod(newModel);
  187.     }
  188.     private Method getValueConversionMethod(TreeModel model) {
  189.         try {
  190.             return model == null ? null : model.getClass().getMethod(
  191.                     "convertValueToText", methodSignature);
  192.         } catch (NoSuchMethodException ex) {
  193.             // not an error
  194.         }
  195.         return null;
  196.     }
  197.     public String convertValueToText(Object value, boolean selected,
  198.             boolean expanded, boolean leaf, int row, boolean hasFocus) {
  199.         // Delegate to model, if possible. Otherwise fall back to superclass...
  200.         if (value != null) {
  201.             if (conversionMethod == null) {
  202.                 return value.toString();
  203.             } else {
  204.                 try {
  205.                     methodArgs[0] = value;
  206.                     return (String) conversionMethod.invoke(getModel(),
  207.                             methodArgs);
  208.                 } catch (Exception ex) {
  209.                     // fall through
  210.                 }
  211.             }
  212.         }
  213.         return "";
  214.     }
  215.     private void initActions() {
  216.         // Register the actions that this class can handle.
  217.         ActionMap map = getActionMap();
  218.         map.put("expand-all", new Actions("expand-all"));
  219.         map.put("collapse-all", new Actions("collapse-all"));
  220.         map.put("find", createFindAction());
  221.         // JW: this should be handled by the LF!
  222.         KeyStroke findStroke = KeyStroke.getKeyStroke("control F");
  223.         getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(findStroke, "find");
  224.     }
  225.     /**
  226.      * A small class which dispatches actions.
  227.      * TODO: Is there a way that we can make this static?
  228.      */
  229.     private class Actions extends UIAction {
  230.         Actions(String name) {
  231.             super(name);
  232.         }
  233.         public void actionPerformed(ActionEvent evt) {
  234.             if ("expand-all".equals(getName())) {
  235. expandAll();
  236.             }
  237.             else if ("collapse-all".equals(getName())) {
  238.                 collapseAll();
  239.             }
  240.         }
  241.     }
  242. //-------------------- search support
  243.     
  244.     private Action createFindAction() {
  245.         Action findAction = new UIAction("find") {
  246.             public void actionPerformed(ActionEvent e) {
  247.                 doFind();
  248.                 
  249.             }
  250.             
  251.         };
  252.         return findAction;
  253.     }
  254.     protected void doFind() {
  255.         SearchFactory.getInstance().showFindInput(this, getSearchable());
  256.     }
  257.     /**
  258.      * 
  259.      * @returns a not-null Searchable for this editor.  
  260.      */
  261.     public Searchable getSearchable() {
  262.         if (searchable == null) {
  263.             searchable = new TreeSearchable();
  264.         }
  265.         return searchable;
  266.     }
  267.     /**
  268.      * sets the Searchable for this editor. If null, a default 
  269.      * searchable will be used.
  270.      * 
  271.      * @param searchable
  272.      */
  273.     public void setSearchable(Searchable searchable) {
  274.         this.searchable = searchable;
  275.     }
  276.     
  277.  
  278.     /**
  279.      * A searchable targetting the visible rows of a JXTree.
  280.      * 
  281.      * PENDING: value to string conversion should behave as nextMatch (?) which
  282.      * uses the convertValueToString().
  283.      * 
  284.      */
  285.     public class TreeSearchable extends AbstractSearchable {
  286.         @Override
  287.         protected void findMatchAndUpdateState(Pattern pattern, int startRow,
  288.                 boolean backwards) {
  289.             SearchResult searchResult = null;
  290.             if (backwards) {
  291.                 for (int index = startRow; index >= 0 && searchResult == null; index--) {
  292.                     searchResult = findMatchAt(pattern, index);
  293.                 }
  294.             } else {
  295.                 for (int index = startRow; index < getSize()
  296.                         && searchResult == null; index++) {
  297.                     searchResult = findMatchAt(pattern, index);
  298.                 }
  299.             }
  300.             updateState(searchResult);
  301.         }
  302.         @Override
  303.         protected SearchResult findExtendedMatch(Pattern pattern, int row) {
  304.             return findMatchAt(pattern, row);
  305.         }
  306.         /**
  307.          * Matches the cell content at row/col against the given Pattern.
  308.          * Returns an appropriate SearchResult if matching or null if no
  309.          * matching
  310.          * 
  311.          * @param pattern
  312.          * @param row
  313.          *            a valid row index in view coordinates
  314.          * @param column
  315.          *            a valid column index in view coordinates
  316.          * @return
  317.          */
  318.         protected SearchResult findMatchAt(Pattern pattern, int row) {
  319.             TreePath path = getPathForRow(row);
  320.             Object value = null;
  321.             if (path != null) {
  322.                 value = path.getLastPathComponent();
  323.             }
  324.             if (value != null) {
  325.                 Matcher matcher = pattern.matcher(value.toString());
  326.                 if (matcher.find()) {
  327.                     return createSearchResult(matcher, row, -1);
  328.                 }
  329.             }
  330.             return null;
  331.         }
  332.         @Override
  333.         protected int getSize() {
  334.             return getRowCount();
  335.         }
  336.         @Override
  337.         protected void moveMatchMarker() {
  338.             int row = lastSearchResult.foundRow;
  339.             setSelectionRow(row);
  340.             if (row >= 0) {
  341.                 scrollRowToVisible(row);
  342.             }
  343.         }
  344.     }
  345.     
  346.     /**
  347.      * Collapses all nodes in the tree table.
  348.      */
  349.     public void collapseAll() {
  350.         for (int i = getRowCount() - 1; i >= 0 ; i--) {
  351.             collapseRow(i);
  352.         }
  353.     }
  354.     /**
  355.      * Expands all nodes in the tree table.
  356.      */
  357.     public void expandAll() {
  358.         for (int i = 0; i < getRowCount(); i++) {
  359.             expandRow(i);
  360.         }
  361.     }
  362.     public HighlighterPipeline getHighlighters() {
  363.         return highlighters;
  364.     }
  365.     /** Assigns a HighlighterPipeline to the table. */
  366.     public void setHighlighters(HighlighterPipeline pipeline) {
  367.         HighlighterPipeline old = getHighlighters();
  368.         if (old != null) {
  369.             old.removeChangeListener(getHighlighterChangeListener());
  370.         }
  371.         highlighters = pipeline;
  372.         if (highlighters != null) {
  373.             highlighters.addChangeListener(getHighlighterChangeListener());
  374.         }
  375.         firePropertyChange("highlighters", old, getHighlighters());
  376.     }
  377.     private ChangeListener getHighlighterChangeListener() {
  378.         if (highlighterChangeListener == null) {
  379.             highlighterChangeListener = new ChangeListener() {
  380.                 public void stateChanged(ChangeEvent e) {
  381.                     repaint();
  382.                     
  383.                 }
  384.                 
  385.             };
  386.         }
  387.         return highlighterChangeListener;
  388.     }
  389.     /**
  390.      * Property to enable/disable rollover support. This can be enabled
  391.      * to show "live" rollover behaviour, f.i. the cursor over LinkModel cells. 
  392.      * Default is disabled.
  393.      * @param rolloverEnabled
  394.      */
  395.     public void setRolloverEnabled(boolean rolloverEnabled) {
  396.         boolean old = isRolloverEnabled();
  397.         if (rolloverEnabled == old) return;
  398.         if (rolloverEnabled) {
  399.             rolloverProducer = createRolloverProducer();
  400.             addMouseListener(rolloverProducer);
  401.             addMouseMotionListener(rolloverProducer);
  402.             linkController = new LinkController();
  403.             addPropertyChangeListener(linkController);
  404.         } else {
  405.             removeMouseListener(rolloverProducer);
  406.             removeMouseMotionListener(rolloverProducer);
  407.             rolloverProducer = null;
  408.             removePropertyChangeListener(linkController);
  409.             linkController = null;
  410.         }
  411.         firePropertyChange("rolloverEnabled", old, isRolloverEnabled());
  412.     }
  413.     /**
  414.      * creates and returns the RolloverProducer to use with this tree.
  415.      * A "hit" for rollover is covering the total width of the tree.
  416.      * Additionally, a pressed to the right (but outside of the label bounds)
  417.      * is re-dispatched as a pressed just inside the label bounds. This 
  418.      * is a first go for #166-swingx.
  419.      * 
  420.      * @return
  421.      */
  422.     protected RolloverProducer createRolloverProducer() {
  423.         RolloverProducer r = new RolloverProducer() {
  424.             
  425.             @Override
  426.             public void mousePressed(MouseEvent e) {
  427.                 JXTree tree = (JXTree) e.getComponent();
  428.                 Point mousePoint = e.getPoint();
  429.               int labelRow = tree.getRowForLocation(mousePoint.x, mousePoint.y);
  430.               // default selection
  431.               if (labelRow >= 0) return;
  432.               int row = tree.getClosestRowForLocation(mousePoint.x, mousePoint.y);
  433.               Rectangle bounds = tree.getRowBounds(row);
  434.               if (bounds == null) {
  435.                   row = -1;
  436.               } else {
  437.                   if ((bounds.y + bounds.height < mousePoint.y) || 
  438.                        bounds.x > mousePoint.x)   {
  439.                       row = -1;
  440.                   }
  441.               }
  442.               // no hit
  443.               if (row < 0) return;
  444.               tree.dispatchEvent(new MouseEvent(tree, e.getID(), e.getWhen(), 
  445.                       e.getModifiers(), bounds.x + bounds.width - 2, mousePoint.y,
  446.                       e.getClickCount(), e.isPopupTrigger(), e.getButton()));
  447.             }
  448.             protected void updateRolloverPoint(JComponent component,
  449.                     Point mousePoint) {
  450.                 JXTree tree = (JXTree) component;
  451.                 int row = tree.getClosestRowForLocation(mousePoint.x, mousePoint.y);
  452.                 Rectangle bounds = tree.getRowBounds(row);
  453.                 if (bounds == null) {
  454.                     row = -1;
  455.                 } else {
  456.                     if ((bounds.y + bounds.height < mousePoint.y) || 
  457.                             bounds.x > mousePoint.x)   {
  458.                            row = -1;
  459.                        }
  460.                 }
  461.                 int col = row < 0 ? -1 : 0;
  462.                 rollover.x = col;
  463.                 rollover.y = row;
  464.             }
  465.         };
  466.         return r;
  467.     }
  468.   
  469.    
  470.     /**
  471.      * returns the rolloverEnabled property.
  472.      * @return
  473.      */
  474.     public boolean isRolloverEnabled() {
  475.         return rolloverProducer != null;
  476.     }
  477.     /**
  478.      * listens to rollover properties. 
  479.      * Repaints effected component regions.
  480.      * Updates link cursor.
  481.      * 
  482.      * @author Jeanette Winzenburg
  483.      */
  484.     public  class LinkController implements PropertyChangeListener {
  485.         private Cursor oldCursor;
  486.         public void propertyChange(PropertyChangeEvent evt) {
  487.             if (RolloverProducer.ROLLOVER_KEY.equals(evt.getPropertyName())) {
  488.                     rollover((JXTree) evt.getSource(), (Point) evt.getOldValue(),
  489.                             (Point) evt.getOldValue());
  490.             } 
  491.         }
  492.         
  493.         
  494. //    -------------------------------------JTree rollover
  495.         
  496.         private void rollover(JXTree tree, Point oldLocation, Point newLocation) {
  497.             //setLinkCursor(list, newLocation);
  498.             // JW: conditional repaint not working?
  499.             tree.repaint();
  500. //            if (oldLocation != null) {
  501. //                Rectangle r = tree.getRowBounds(oldLocation.y);
  502. ////                r.x = 0;
  503. ////                r.width = table.getWidth();
  504. //                if (r != null)
  505. //                tree.repaint(r);
  506. //            }
  507. //            if (newLocation != null) {
  508. //                Rectangle r = tree.getRowBounds(newLocation.y);
  509. ////                r.x = 0;
  510. ////                r.width = table.getWidth();
  511. //                if (r != null)
  512. //                tree.repaint(r);
  513. //            }
  514.         }
  515.     }
  516.     
  517.     private DelegatingRenderer getDelegatingRenderer() {
  518.         if (delegatingRenderer == null) {
  519.             // only called once... to get hold of the default?
  520.             delegatingRenderer = new DelegatingRenderer();
  521.             delegatingRenderer.setDelegateRenderer(super.getCellRenderer());
  522.         }
  523.         return delegatingRenderer;
  524.     }
  525.     public TreeCellRenderer getCellRenderer() {
  526.         return getDelegatingRenderer();
  527.     }
  528.     public void setCellRenderer(TreeCellRenderer renderer) {
  529.         // PENDING: do something against recursive setting
  530.         // == multiple delegation...
  531.         getDelegatingRenderer().setDelegateRenderer(renderer);
  532.         super.setCellRenderer(delegatingRenderer);
  533.     }
  534.     /**
  535.      * sets the icon for the handle of an expanded node.
  536.      * 
  537.      * Note: this will only succeed if the current ui delegate is
  538.      * a BasicTreeUI otherwise it will do nothing.
  539.      * 
  540.      * @param expanded
  541.      */
  542.     public void setExpandedIcon(Icon expanded) {
  543.         if (getUI() instanceof BasicTreeUI) {
  544.             ((BasicTreeUI) getUI()).setExpandedIcon(expanded);
  545.         }
  546.     }
  547.     
  548.     /**
  549.      * sets the icon for the handel of a collapsed node.
  550.      * 
  551.      * Note: this will only succeed if the current ui delegate is
  552.      * a BasicTreeUI otherwise it will do nothing.
  553.      *  
  554.      * @param collapsed
  555.      */
  556.     public void setCollapsedIcon(Icon collapsed) {
  557.         if (getUI() instanceof BasicTreeUI) {
  558.             ((BasicTreeUI) getUI()).setCollapsedIcon(collapsed);
  559.         }
  560.     }
  561.     
  562.     /**
  563.      * set the icon for a leaf node.
  564.      * 
  565.      * Note: this will only succeed if current renderer is a 
  566.      * DefaultTreeCellRenderer.
  567.      * 
  568.      * @param leafIcon
  569.      */
  570.     public void setLeafIcon(Icon leafIcon) {
  571.         getDelegatingRenderer().setLeafIcon(leafIcon);
  572.         
  573.     }
  574.     
  575.     /**
  576.      * set the icon for a open non-leaf node.
  577.      * 
  578.      * Note: this will only succeed if current renderer is a 
  579.      * DefaultTreeCellRenderer.
  580.      * 
  581.      * @param openIcon
  582.      */
  583.     public void setOpenIcon(Icon openIcon) {
  584.         getDelegatingRenderer().setOpenIcon(openIcon);
  585.     }
  586.     
  587.     /**
  588.      * set the icon for a closed non-leaf node.
  589.      * 
  590.      * Note: this will only succeed if current renderer is a 
  591.      * DefaultTreeCellRenderer.
  592.      * 
  593.      * @param closedIcon
  594.      */
  595.     public void setClosedIcon(Icon closedIcon) {
  596.         getDelegatingRenderer().setClosedIcon(closedIcon);
  597.     }
  598.     
  599.     /**
  600.      * Property to control whether per-tree icons should be 
  601.      * copied to the renderer on setCellRenderer.
  602.      * 
  603.      * the default is false for backward compatibility.
  604.      * 
  605.      * PENDING: should update the current renderer's icons when 
  606.      * setting to true?
  607.      * 
  608.      * @param overwrite
  609.      */
  610.     public void setOverwriteRendererIcons(boolean overwrite) {
  611.         if (overwriteIcons == overwrite) return;
  612.         boolean old = overwriteIcons;
  613.         this.overwriteIcons = overwrite;
  614.         firePropertyChange("overwriteRendererIcons", old, overwrite);
  615.     }
  616.     public boolean isOverwriteRendererIcons() {
  617.         return overwriteIcons;
  618.     }
  619.     
  620.     public class DelegatingRenderer implements TreeCellRenderer {
  621.         private Icon    closedIcon = null;
  622.         private Icon    openIcon = null;
  623.         private Icon    leafIcon = null;
  624.        
  625.         private TreeCellRenderer delegate;
  626.         
  627.         public DelegatingRenderer() {
  628.             initIcons(new DefaultTreeCellRenderer());
  629.         }
  630.         /**
  631.          * initially sets the icons to the defaults as given
  632.          * by a DefaultTreeCellRenderer.
  633.          * 
  634.          * @param renderer
  635.          */
  636.         private void initIcons(DefaultTreeCellRenderer renderer) {
  637.             closedIcon = renderer.getDefaultClosedIcon();
  638.             openIcon = renderer.getDefaultOpenIcon();
  639.             leafIcon = renderer.getDefaultLeafIcon();
  640.         }
  641.         /**
  642.          * Set the delegate renderer. 
  643.          * Updates the folder/leaf icons. 
  644.          * 
  645.          * THINK: how to update? always override with this.icons, only
  646.          * if renderer's icons are null, update this icons if they are not,
  647.          * update all if only one is != null.... ??
  648.          * 
  649.          * @param delegate
  650.          */
  651.         public void setDelegateRenderer(TreeCellRenderer delegate) {
  652.             if (delegate == null) {
  653.                 delegate = new DefaultTreeCellRenderer();
  654.             }
  655.             this.delegate = delegate;
  656.             updateIcons();
  657.         }
  658.         
  659.         /**
  660.          * tries to set the renderers icons. Can succeed only if the
  661.          * delegate is a DefaultTreeCellRenderer.
  662.          * THINK: how to update? always override with this.icons, only
  663.          * if renderer's icons are null, update this icons if they are not,
  664.          * update all if only one is != null.... ??
  665.          * 
  666.          */
  667.         private void updateIcons() {
  668.             if (!isOverwriteRendererIcons()) return;
  669.             setClosedIcon(closedIcon);
  670.             setOpenIcon(openIcon);
  671.             setLeafIcon(leafIcon);
  672.         }
  673.         public void setClosedIcon(Icon closedIcon) {
  674.             if (delegate instanceof DefaultTreeCellRenderer) {
  675.                 ((DefaultTreeCellRenderer) delegate).setClosedIcon(closedIcon);
  676.             }
  677.             this.closedIcon = closedIcon;
  678.         }
  679.         
  680.         public void setOpenIcon(Icon openIcon) {
  681.             if (delegate instanceof DefaultTreeCellRenderer) {
  682.                 ((DefaultTreeCellRenderer) delegate).setOpenIcon(openIcon);
  683.             }
  684.             this.openIcon = openIcon;
  685.         }
  686.         
  687.         public void setLeafIcon(Icon leafIcon) {
  688.             if (delegate instanceof DefaultTreeCellRenderer) {
  689.                 ((DefaultTreeCellRenderer) delegate).setLeafIcon(leafIcon);
  690.             }
  691.             this.leafIcon = leafIcon;
  692.         }
  693.         
  694.         public TreeCellRenderer getDelegateRenderer() {
  695.             return delegate;
  696.         }
  697.             public Component getTreeCellRendererComponent(JTree tree, Object value, 
  698.                     boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
  699.                 Component result = delegate.getTreeCellRendererComponent(tree, value, 
  700.                         selected, expanded, leaf, row, hasFocus);
  701.                     if (highlighters != null) {
  702.                         getComponentAdapter().row = row;
  703.                         result = highlighters.apply(result, getComponentAdapter());
  704.                     }
  705.                  return result;
  706.             }
  707.     }
  708.     
  709.     protected ComponentAdapter getComponentAdapter() {
  710.         return dataAdapter;
  711.     }
  712.     private final ComponentAdapter dataAdapter = new TreeAdapter(this);
  713.     protected static class TreeAdapter extends ComponentAdapter {
  714.         private final JXTree tree;
  715.         /**
  716.          * Constructs a <code>TableCellRenderContext</code> for the specified
  717.          * target component.
  718.          *
  719.          * @param component the target component
  720.          */
  721.         public TreeAdapter(JXTree component) {
  722.             super(component);
  723.             tree = component;
  724.         }
  725.         public JXTree getTree() {
  726.             return tree;
  727.         }
  728.         public boolean hasFocus() {
  729.             return tree.isFocusOwner() && (tree.getLeadSelectionRow() == row);
  730.         }
  731.         public Object getValueAt(int row, int column) {
  732.             TreePath path = tree.getPathForRow(row);
  733.             return path.getLastPathComponent();
  734.         }
  735.         public Object getFilteredValueAt(int row, int column) {
  736.             /** @todo Implement filtering */
  737.             return getValueAt(row, column);
  738.         }
  739.         public boolean isSelected() {
  740.             return tree.isRowSelected(row);
  741.         }
  742.         public boolean isExpanded() {
  743.             return tree.isExpanded(row);
  744.         }
  745.         public boolean isLeaf() {
  746.             return tree.getModel().isLeaf(getValue());
  747.         }
  748.         public boolean isCellEditable(int row, int column) {
  749.             return false; /** @todo  */
  750.         }
  751.         public void setValueAt(Object aValue, int row, int column) {
  752.             /** @todo  */
  753.         }
  754.         
  755.         public String getColumnName(int columnIndex) {
  756.             return "Column_" + columnIndex;
  757.         }
  758.         
  759.         public String getColumnIdentifier(int columnIndex) {
  760.             return null;
  761.         }
  762.     }
  763. }