FileDocCategorySizeDatePackage
SynthTreeUI.javaAPI DocJava SE 6 API25787Tue Jun 10 00:26:56 BST 2008javax.swing.plaf.synth

SynthTreeUI.java

/*
 * @(#)SynthTreeUI.java	1.34 07/01/11
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package javax.swing.plaf.synth;

import javax.swing.*;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.beans.*;
import java.io.*;
import java.util.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.tree.*;
import javax.swing.text.Position;
import sun.swing.plaf.synth.*;

/**
 * Skinnable TreeUI.
 *
 * @version 1.34, 01/11/07
 * @author Scott Violet
 */
class SynthTreeUI extends BasicTreeUI implements PropertyChangeListener,
                               SynthUI {
    private SynthStyle style;
    private SynthStyle cellStyle;

    private SynthContext paintContext;

    private boolean drawHorizontalLines;
    private boolean drawVerticalLines;
    
    private Object linesStyle;

    private int leadRow;

    private int padding;

    private boolean useTreeColors;

    private Icon expandedIconWrapper;

    public static ComponentUI createUI(JComponent x) {
	return new SynthTreeUI();
    }

    SynthTreeUI() {
        expandedIconWrapper = new ExpandedIconWrapper();
    }

    public Icon getExpandedIcon() {
	return expandedIconWrapper;
    }

    protected void installDefaults() {
        updateStyle(tree);
    }

    private void updateStyle(JTree tree) {
        SynthContext context = getContext(tree, ENABLED);
        SynthStyle oldStyle = style;

        style = SynthLookAndFeel.updateStyle(context, this);
        if (style != oldStyle) {
            Object value;

            setExpandedIcon(style.getIcon(context, "Tree.expandedIcon"));
            setCollapsedIcon(style.getIcon(context, "Tree.collapsedIcon"));

            setLeftChildIndent(style.getInt(context, "Tree.leftChildIndent",
                                            0));
            setRightChildIndent(style.getInt(context, "Tree.rightChildIndent",
                                             0));

            drawHorizontalLines = style.getBoolean(
                          context, "Tree.drawHorizontalLines",true);
            drawVerticalLines = style.getBoolean(
                        context, "Tree.drawVerticalLines", true);
            linesStyle = style.get(context, "Tree.linesStyle");

	        value = style.get(context, "Tree.rowHeight");
	        if (value != null) {
	            LookAndFeel.installProperty(tree, "rowHeight", value);
	        }

	        value = style.get(context, "Tree.scrollsOnExpand");
	        LookAndFeel.installProperty(tree, "scrollsOnExpand",
			                            value != null? value : Boolean.TRUE);

            padding = style.getInt(context, "Tree.padding", 0);

            largeModel = (tree.isLargeModel() && tree.getRowHeight() > 0);

            useTreeColors = style.getBoolean(context,
                                  "Tree.rendererUseTreeColors", true);
            
            Boolean showsRootHandles = style.getBoolean(
                    context, "Tree.showsRootHandles", Boolean.TRUE);
            LookAndFeel.installProperty(
                    tree, JTree.SHOWS_ROOT_HANDLES_PROPERTY, showsRootHandles);

            if (oldStyle != null) {
                uninstallKeyboardActions();
                installKeyboardActions();
            }
        }
        context.dispose();

        context = getContext(tree, Region.TREE_CELL, ENABLED);
        cellStyle = SynthLookAndFeel.updateStyle(context, this);
        context.dispose();
    }

    protected void installListeners() {
        super.installListeners();
        tree.addPropertyChangeListener(this);
    }

    public SynthContext getContext(JComponent c) {
        return getContext(c, getComponentState(c));
    }

    private SynthContext getContext(JComponent c, int state) {
        return SynthContext.getContext(SynthContext.class, c,
                    SynthLookAndFeel.getRegion(c), style, state);
    }

    private Region getRegion(JTree c) {
        return SynthLookAndFeel.getRegion(c);
    }

    private int getComponentState(JComponent c) {
        return SynthLookAndFeel.getComponentState(c);
    }

    private SynthContext getContext(JComponent c, Region region) {
        return getContext(c, region, getComponentState(c, region));
    }

    private SynthContext getContext(JComponent c, Region region, int state) {
        return SynthContext.getContext(SynthContext.class, c,
                                       region, cellStyle, state);
    }

    private int getComponentState(JComponent c, Region region) {
        // Always treat the cell as selected, will be adjusted appropriately
        // when painted.
        return ENABLED | SELECTED;
    }

    protected TreeCellEditor createDefaultCellEditor() {
        TreeCellRenderer renderer = tree.getCellRenderer();
        DefaultTreeCellEditor editor;

	if(renderer != null && (renderer instanceof DefaultTreeCellRenderer)) {
	    editor = new SynthTreeCellEditor(tree, (DefaultTreeCellRenderer)
                                             renderer);
	}
        else {
            editor = new SynthTreeCellEditor(tree, null);
        }
        return editor;
    }

    protected TreeCellRenderer createDefaultCellRenderer() {
        return new SynthTreeCellRenderer();
    }

    protected void uninstallDefaults() {
        SynthContext context = getContext(tree, ENABLED);

        style.uninstallDefaults(context);
        context.dispose();
        style = null;

        context = getContext(tree, Region.TREE_CELL, ENABLED);
        cellStyle.uninstallDefaults(context);
        context.dispose();
        cellStyle = null;


	if (tree.getTransferHandler() instanceof UIResource) {
	    tree.setTransferHandler(null);
	}
    }

    protected void uninstallListeners() {
        super.uninstallListeners();
        tree.removePropertyChangeListener(this);
    }

    public void update(Graphics g, JComponent c) {
        SynthContext context = getContext(c);

        SynthLookAndFeel.update(context, g);
        context.getPainter().paintTreeBackground(context,
                          g, 0, 0, c.getWidth(), c.getHeight());
        paint(context, g);
        context.dispose();
    }

    public void paintBorder(SynthContext context, Graphics g, int x,
                            int y, int w, int h) {
        context.getPainter().paintTreeBorder(context, g, x, y, w, h);
    }

    public void paint(Graphics g, JComponent c) {
        SynthContext context = getContext(c);

        paint(context, g);
        context.dispose();
    }

    private void updateLeadRow() {
	leadRow = getRowForPath(tree, tree.getLeadSelectionPath());
    }

    protected void paint(SynthContext context, Graphics g) {
        paintContext = context;

        updateLeadRow();

	Rectangle paintBounds = g.getClipBounds();
	Insets insets = tree.getInsets();
	TreePath initialPath = getClosestPathForLocation(tree, 0,
                                                         paintBounds.y);
	Enumeration paintingEnumerator = treeState.getVisiblePathsFrom
	                                      (initialPath);
	int row = treeState.getRowForPath(initialPath);
	int endY = paintBounds.y + paintBounds.height;
        TreeModel treeModel = tree.getModel();
        SynthContext cellContext = getContext(tree, Region.TREE_CELL);

	drawingCache.clear();

        setHashColor(context.getStyle().getColor(context,
                                                ColorType.FOREGROUND));

	if (paintingEnumerator != null) {
            // First pass, draw the rows

	    boolean done = false;
	    boolean isExpanded;
	    boolean hasBeenExpanded;
	    boolean isLeaf;
	    Rectangle boundsBuffer = new Rectangle();
            Rectangle rowBounds = new Rectangle(0, 0, tree.getWidth(),0);
	    Rectangle bounds;
	    TreePath path;
            TreeCellRenderer renderer = tree.getCellRenderer();
            DefaultTreeCellRenderer dtcr = (renderer instanceof
                       DefaultTreeCellRenderer) ? (DefaultTreeCellRenderer)
                       renderer : null;

            configureRenderer(cellContext);
	    while (!done && paintingEnumerator.hasMoreElements()) {
		path = (TreePath)paintingEnumerator.nextElement();
		if (path != null) {
		    isLeaf = treeModel.isLeaf(path.getLastPathComponent());
		    if (isLeaf) {
			isExpanded = hasBeenExpanded = false;
                    }
		    else {
			isExpanded = treeState.getExpandedState(path);
			hasBeenExpanded = tree.hasBeenExpanded(path);
		    }
                    bounds = getPathBounds(tree, path);
                    rowBounds.y = bounds.y;
                    rowBounds.height = bounds.height;
		    paintRow(renderer, dtcr, context, cellContext, g,
                             paintBounds, insets, bounds, rowBounds, path,
                             row, isExpanded, hasBeenExpanded, isLeaf);
		    if ((bounds.y + bounds.height) >= endY) {
			done = true;
                    }
		}
		else {
		    done = true;
		}
		row++;
	    }

	    // Draw the connecting lines and controls.
	    // Find each parent and have them draw a line to their last child
            boolean rootVisible = tree.isRootVisible();
            TreePath parentPath = initialPath;
	    parentPath = parentPath.getParentPath();
	    while (parentPath != null) {
                paintVerticalPartOfLeg(g, paintBounds, insets, parentPath);
		drawingCache.put(parentPath, Boolean.TRUE);
		parentPath = parentPath.getParentPath();
	    }
	    done = false;
            paintingEnumerator = treeState.getVisiblePathsFrom(initialPath);
	    while (!done && paintingEnumerator.hasMoreElements()) {
		path = (TreePath)paintingEnumerator.nextElement();
		if (path != null) {
		    isLeaf = treeModel.isLeaf(path.getLastPathComponent());
		    if (isLeaf) {
			isExpanded = hasBeenExpanded = false;
                    }
		    else {
			isExpanded = treeState.getExpandedState(path);
			hasBeenExpanded = tree.hasBeenExpanded(path);
		    }
                    bounds = getPathBounds(tree, path);
		    // See if the vertical line to the parent has been drawn.
		    parentPath = path.getParentPath();
		    if (parentPath != null) {
			if (drawingCache.get(parentPath) == null) {
                            paintVerticalPartOfLeg(g, paintBounds, insets,
                                                   parentPath);
			    drawingCache.put(parentPath, Boolean.TRUE);
			}
			paintHorizontalPartOfLeg(g,
                                                 paintBounds, insets, bounds,
                                                 path, row, isExpanded,
						 hasBeenExpanded, isLeaf);
		    }
		    else if (rootVisible && row == 0) {
			paintHorizontalPartOfLeg(g,
                                                 paintBounds, insets, bounds,
                                                 path, row, isExpanded,
						 hasBeenExpanded, isLeaf);
		    }
		    if (shouldPaintExpandControl(path, row, isExpanded,
                                                 hasBeenExpanded, isLeaf)) {
			paintExpandControl(g, paintBounds,
                                           insets, bounds, path, row,
                                           isExpanded, hasBeenExpanded,isLeaf);
		    }
		    if ((bounds.y + bounds.height) >= endY) {
			done = true;
                    }
		}
		else {
		    done = true;
		}
		row++;
	    }
	}
        cellContext.dispose();
        
        paintDropLine(g);
        
	// Empty out the renderer pane, allowing renderers to be gc'ed.
	rendererPane.removeAll();
    }

    private boolean isDropLine(JTree.DropLocation loc) {
        return loc != null && loc.getPath() != null && loc.getChildIndex() != -1;
    }

    private void paintDropLine(Graphics g) {
        JTree.DropLocation loc = tree.getDropLocation();
        if (!isDropLine(loc)) {
            return;
        }

        Color c = (Color)style.get(paintContext, "Tree.dropLineColor");
        if (c != null) {
            g.setColor(c);
            Rectangle rect = getDropLineRect(loc);
            g.fillRect(rect.x, rect.y, rect.width, rect.height);
        }
    }

    private Rectangle getDropLineRect(JTree.DropLocation loc) {
        Rectangle rect = null;
        TreePath path = loc.getPath();
        int index = loc.getChildIndex();
        boolean ltr = tree.getComponentOrientation().isLeftToRight();

        Insets insets = tree.getInsets();

        if (tree.getRowCount() == 0) {
            rect = new Rectangle(insets.left,
                                 insets.top,
                                 tree.getWidth() - insets.left - insets.right,
                                 0);
        } else {
            int row = tree.getRowForPath(path);
            TreeModel model = getModel();
            Object root = model.getRoot();

            if (path.getLastPathComponent() == root
                    && index >= model.getChildCount(root)) {

                rect = tree.getRowBounds(tree.getRowCount() - 1);
                rect.y = rect.y + rect.height;
                Rectangle xRect;

                if (!tree.isRootVisible()) {
                    xRect = tree.getRowBounds(0);
                } else if (model.getChildCount(root) == 0){
                    xRect = tree.getRowBounds(0);
                    xRect.x += totalChildIndent;
                    xRect.width -= totalChildIndent + totalChildIndent;
                } else {
                    TreePath lastChildPath = path.pathByAddingChild(
                        model.getChild(root, model.getChildCount(root) - 1));
                    xRect = tree.getPathBounds(lastChildPath);
                }

                rect.x = xRect.x;
                rect.width = xRect.width;
            } else {
                rect = tree.getPathBounds(path.pathByAddingChild(
                    model.getChild(path.getLastPathComponent(), index)));
            }
        }

        if (rect.y != 0) {
            rect.y--;
        }

        if (!ltr) {
            rect.x = rect.x + rect.width - 100;
        }

        rect.width = 100;
        rect.height = 2;

        return rect;
    }

    private void configureRenderer(SynthContext context) {
        TreeCellRenderer renderer = tree.getCellRenderer();

        if (renderer instanceof DefaultTreeCellRenderer) {
            DefaultTreeCellRenderer r = (DefaultTreeCellRenderer)renderer;
            SynthStyle style = context.getStyle();

            context.setComponentState(ENABLED | SELECTED);
            Color color = r.getTextSelectionColor();
            if (color == null || (color instanceof UIResource)) {
                r.setTextSelectionColor(style.getColor(
                                     context, ColorType.TEXT_FOREGROUND));
            }
            color = r.getBackgroundSelectionColor();
            if (color == null || (color instanceof UIResource)) {
                r.setBackgroundSelectionColor(style.getColor(
                                        context, ColorType.TEXT_BACKGROUND));
            }

            context.setComponentState(ENABLED);
            color = r.getTextNonSelectionColor();
            if (color == null || color instanceof UIResource) {
                r.setTextNonSelectionColor(style.getColorForState(
                                        context, ColorType.TEXT_FOREGROUND));
            }
            color = r.getBackgroundNonSelectionColor();
            if (color == null || color instanceof UIResource) {
                r.setBackgroundNonSelectionColor(style.getColorForState(
                                  context, ColorType.TEXT_BACKGROUND));
            }
        }
    }

    protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
					    Insets insets, Rectangle bounds,
					    TreePath path, int row,
					    boolean isExpanded,
					    boolean hasBeenExpanded, boolean
					    isLeaf) {
        if (drawHorizontalLines) {
            super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds,
                                           path, row, isExpanded,
                                           hasBeenExpanded, isLeaf);
        }
    }

    protected void paintHorizontalLine(Graphics g, JComponent c, int y,
				      int left, int right) {
        paintContext.getStyle().getGraphicsUtils(paintContext).drawLine(
            paintContext, "Tree.horizontalLine", g, left, y, right, y, linesStyle);
    }

    protected void paintVerticalPartOfLeg(Graphics g,
                                          Rectangle clipBounds, Insets insets,
                                          TreePath path) {
        if (drawVerticalLines) {
            super.paintVerticalPartOfLeg(g, clipBounds, insets, path);
        }
    }

    protected void paintVerticalLine(Graphics g, JComponent c, int x, int top,
				    int bottom) {
        paintContext.getStyle().getGraphicsUtils(paintContext).drawLine(
            paintContext, "Tree.verticalLine", g, x, top, x, bottom, linesStyle);
    }

    protected void paintRow(TreeCellRenderer renderer,
               DefaultTreeCellRenderer dtcr, SynthContext treeContext,
               SynthContext cellContext, Graphics g, Rectangle clipBounds,
               Insets insets, Rectangle bounds, Rectangle rowBounds,
               TreePath path, int row, boolean isExpanded,
               boolean hasBeenExpanded, boolean isLeaf) {
	// Don't paint the renderer if editing this row.
        boolean selected = tree.isRowSelected(row);

        JTree.DropLocation dropLocation = (JTree.DropLocation)tree.getDropLocation();
        boolean isDrop = dropLocation != null
                         && dropLocation.getChildIndex() == -1
                         && path == dropLocation.getPath();

        if (selected || isDrop) {
            cellContext.setComponentState(ENABLED | SELECTED);
        }
        else {
            cellContext.setComponentState(ENABLED);
        }
        if (dtcr != null && (dtcr.getBorderSelectionColor() instanceof
                             UIResource)) {
            dtcr.setBorderSelectionColor(style.getColor(
                                             cellContext, ColorType.FOCUS));
        }
        SynthLookAndFeel.updateSubregion(cellContext, g, rowBounds);
        cellContext.getPainter().paintTreeCellBackground(cellContext, g,
                    rowBounds.x, rowBounds.y, rowBounds.width,
                    rowBounds.height);
        cellContext.getPainter().paintTreeCellBorder(cellContext, g,
                    rowBounds.x, rowBounds.y, rowBounds.width,
                    rowBounds.height);
	if (editingComponent != null && editingRow == row) {
	    return;
        }

	int leadIndex;

	if (tree.hasFocus()) {
	    leadIndex = leadRow;
	}
	else {
	    leadIndex = -1;
        }

	Component component = renderer.getTreeCellRendererComponent(
                         tree, path.getLastPathComponent(),
                         selected, isExpanded, isLeaf, row,
                         (leadIndex == row));

	rendererPane.paintComponent(g, component, tree, bounds.x, bounds.y,
				    bounds.width, bounds.height, true);
    }

    private int findCenteredX(int x, int iconWidth) {
        return tree.getComponentOrientation().isLeftToRight()
               ? x - (int)Math.ceil(iconWidth / 2.0)
               : x - (int)Math.floor(iconWidth / 2.0);
    }

    protected void drawCentered(Component c, Graphics graphics, Icon icon,
				int x, int y) {
        int w = SynthIcon.getIconWidth(icon, paintContext);
        int h = SynthIcon.getIconHeight(icon, paintContext);

	SynthIcon.paintIcon(icon, paintContext, graphics,
                            findCenteredX(x, w),
                            y - h/2, w, h);
    }

    public void propertyChange(PropertyChangeEvent event) {
        if (SynthLookAndFeel.shouldUpdateStyle(event)) {
            updateStyle((JTree)event.getSource());
        }

        if ("dropLocation" == event.getPropertyName()) {
            JTree.DropLocation oldValue = (JTree.DropLocation)event.getOldValue();
            repaintDropLocation(oldValue);
            repaintDropLocation(tree.getDropLocation());
        }
    }

    private void repaintDropLocation(JTree.DropLocation loc) {
        if (loc == null) {
            return;
        }

        Rectangle r;

        if (isDropLine(loc)) {
            r = getDropLineRect(loc);
        } else {
            r = tree.getPathBounds(loc.getPath());
            if (r != null) {
                r.x = 0;
                r.width = tree.getWidth();
            }
        }

        if (r != null) {
            tree.repaint(r);
        }
    }

    protected int getRowX(int row, int depth) {
        return super.getRowX(row, depth) + padding;
    }


    private class SynthTreeCellRenderer extends DefaultTreeCellRenderer
                               implements UIResource {
        SynthTreeCellRenderer() {
        }
        public String getName() {
            return "Tree.cellRenderer";
        }
        public Component getTreeCellRendererComponent(JTree tree, Object value,
                                                      boolean sel,
                                                      boolean expanded,
                                                      boolean leaf, int row,
                                                      boolean hasFocus) {
            if (!useTreeColors && (sel || hasFocus)) {
                SynthLookAndFeel.setSelectedUI((SynthLabelUI)SynthLookAndFeel.
                             getUIOfType(getUI(), SynthLabelUI.class),
                                   sel, hasFocus, tree.isEnabled(), false);
            }
            else {
                SynthLookAndFeel.resetSelectedUI();
            }
            return super.getTreeCellRendererComponent(tree, value, sel,
                                                      expanded, leaf, row, hasFocus);
        }
        public void paint(Graphics g) {
            paintComponent(g);
            if (hasFocus) {
                SynthContext context = getContext(tree, Region.TREE_CELL);

                if (context.getStyle() == null) {
                    assert false: "SynthTreeCellRenderer is being used " +
                        "outside of UI that created it";
                    return;
                }
                int imageOffset = 0;
                Icon currentI = getIcon();

                if(currentI != null && getText() != null) {
                    imageOffset = currentI.getIconWidth() +
                                          Math.max(0, getIconTextGap() - 1);
                }
                if (selected) {
                    context.setComponentState(ENABLED | SELECTED);
                }
                else {
                    context.setComponentState(ENABLED);
                }
                if(getComponentOrientation().isLeftToRight()) {
                    context.getPainter().paintTreeCellFocus(context, g,
                            imageOffset, 0, getWidth() - imageOffset,
                            getHeight());
                }
                else {
                    context.getPainter().paintTreeCellFocus(context, g,
                            0, 0, getWidth() - imageOffset, getHeight());
                }
                context.dispose();
            }
            SynthLookAndFeel.resetSelectedUI();
        }
    }


    private static class SynthTreeCellEditor extends DefaultTreeCellEditor {
        public SynthTreeCellEditor(JTree tree,
                                   DefaultTreeCellRenderer renderer) {
            super(tree, renderer);
            setBorderSelectionColor(null);
        }

        protected TreeCellEditor createTreeCellEditor() {
            JTextField tf = new JTextField() {
                public String getName() {
                    return "Tree.cellEditor";
                }
            };
            DefaultCellEditor editor = new DefaultCellEditor(tf);

            // One click to edit.
            editor.setClickCountToStart(1);
            return editor;
        }
    }


    //
    // BasicTreeUI directly uses expandIcon outside of the Synth methods.
    // To get the correct context we return an instance of this that fetches
    // the SynthContext as needed.
    //
    private class ExpandedIconWrapper extends SynthIcon {
        public void paintIcon(SynthContext context, Graphics g, int x,
                              int y, int w, int h) {
            if (context == null) {
                context = getContext(tree);
                SynthIcon.paintIcon(expandedIcon, context, g, x, y, w, h);
                context.dispose();
            }
            else {
                SynthIcon.paintIcon(expandedIcon, context, g, x, y, w, h);
            }
        }

        public int getIconWidth(SynthContext context) {
            int width;
            if (context == null) {
                context = getContext(tree);
                width = SynthIcon.getIconWidth(expandedIcon, context);
                context.dispose();
            }
            else {
                width = SynthIcon.getIconWidth(expandedIcon, context);
            }
            return width;
        }

        public int getIconHeight(SynthContext context) {
            int height;
            if (context == null) {
                context = getContext(tree);
                height = SynthIcon.getIconHeight(expandedIcon, context);
                context.dispose();
            }
            else {
                height = SynthIcon.getIconHeight(expandedIcon, context);
            }
            return height;
        }
    }
}