/*
* Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of Substance Kirill Grouchnikov nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.pushingpixels.substance.api.renderers;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.TreeUI;
import javax.swing.tree.TreeCellRenderer;
import org.pushingpixels.substance.api.ColorSchemeAssociationKind;
import org.pushingpixels.substance.api.ComponentState;
import org.pushingpixels.substance.api.SubstanceColorScheme;
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
import org.pushingpixels.substance.internal.animation.StateTransitionTracker.StateContributionInfo;
import org.pushingpixels.substance.internal.ui.SubstanceTreeUI;
import org.pushingpixels.substance.internal.ui.SubstanceTreeUI.TreePathId;
import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceImageCreator;
import org.pushingpixels.substance.internal.utils.SubstanceStripingUtils;
/**
* Default renderer for tree cells.
*
* @author Kirill Grouchnikov
*/
@SubstanceRenderer
public class SubstanceDefaultTreeCellRenderer extends JLabel implements
TreeCellRenderer {
/** Last tree the renderer was painted in. */
private JTree tree;
/** Is the value currently selected. */
protected boolean selected;
/** True if has focus. */
protected boolean hasFocus;
/**
* Returns a new instance of SubstanceDefaultTreeCellRenderer. Alignment is
* set to left aligned. Icons and text color are determined from the
* UIManager.
*/
public SubstanceDefaultTreeCellRenderer() {
this.setHorizontalAlignment(SwingConstants.LEFT);
this.putClientProperty(SubstanceLookAndFeel.COLORIZATION_FACTOR, 1.0);
}
/**
* Returns the default icon that is used to represent non-leaf nodes that
* are expanded.
*
* @return The default icon for non-leaf expanded nodes.
*/
public Icon getDefaultOpenIcon() {
return UIManager.getIcon("Tree.openIcon");
}
/**
* Returns the default icon that is used to represent non-leaf nodes that
* are not expanded.
*
* @return The default icon for non-leaf non-expanded nodes.
*/
public Icon getDefaultClosedIcon() {
return UIManager.getIcon("Tree.closedIcon");
}
/**
* Returns the default icon that is used to represent leaf nodes.
*
* @return The default icon for leaf nodes.
*/
public Icon getDefaultLeafIcon() {
return UIManager.getIcon("Tree.leafIcon");
}
/**
* Subclassed to map <code>FontUIResource</code>s to null. If
* <code>font</code> is null, or a <code>FontUIResource</code>, this has the
* effect of letting the font of the JTree show through. On the other hand,
* if <code>font</code> is non-null, and not a <code>FontUIResource</code>,
* the font becomes <code>font</code>.
*/
@Override
public void setFont(Font font) {
if (font instanceof FontUIResource)
font = null;
super.setFont(font);
}
/**
* Gets the font of this component.
*
* @return this component's font; if a font has not been set for this
* component, the font of its parent is returned
*/
@Override
public Font getFont() {
Font font = super.getFont();
if ((font == null) && (this.tree != null)) {
// Strive to return a non-null value, otherwise the html support
// will typically pick up the wrong font in certain situations.
font = this.tree.getFont();
}
return font;
}
/**
* Configures the renderer based on the passed in components. The value is
* set from messaging the tree with <code>convertValueToText</code>, which
* ultimately invokes <code>toString</code> on <code>value</code>. The
* foreground color is set based on the selection and the icon is set based
* on on leaf and expanded.
*/
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
String stringValue = tree.convertValueToText(value, sel, expanded,
leaf, row, hasFocus);
this.tree = tree;
this.hasFocus = hasFocus;
this.setText(stringValue);
TreeUI treeUI = tree.getUI();
if (treeUI instanceof SubstanceTreeUI) {
SubstanceTreeUI ui = (SubstanceTreeUI) treeUI;
TreePathId pathId = new TreePathId(tree.getPathForRow(row));
StateTransitionTracker.ModelStateInfo modelStateInfo = ui
.getModelStateInfo(pathId);
ComponentState currState = ui.getPathState(pathId);
// special case for drop location
JTree.DropLocation dropLocation = tree.getDropLocation();
boolean isDropLocation = (dropLocation != null
&& dropLocation.getChildIndex() == -1 && tree
.getRowForPath(dropLocation.getPath()) == row);
if (!isDropLocation && (modelStateInfo != null)) {
Map<ComponentState, StateContributionInfo> activeStates = modelStateInfo
.getStateContributionMap();
SubstanceColorScheme colorScheme = getColorSchemeForState(tree,
ui, currState);
if (currState.isDisabled() || (activeStates == null)
|| (activeStates.size() == 1)) {
super.setForeground(new ColorUIResource(colorScheme
.getForegroundColor()));
} else {
float aggrRed = 0;
float aggrGreen = 0;
float aggrBlue = 0;
for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : modelStateInfo
.getStateContributionMap().entrySet()) {
ComponentState activeState = activeEntry.getKey();
SubstanceColorScheme scheme = getColorSchemeForState(
tree, ui, activeState);
Color schemeFg = scheme.getForegroundColor();
float contribution = activeEntry.getValue()
.getContribution();
aggrRed += schemeFg.getRed() * contribution;
aggrGreen += schemeFg.getGreen() * contribution;
aggrBlue += schemeFg.getBlue() * contribution;
}
super.setForeground(new ColorUIResource(new Color(
(int) aggrRed, (int) aggrGreen, (int) aggrBlue)));
}
} else {
SubstanceColorScheme scheme = getColorSchemeForState(tree, ui,
currState);
if (isDropLocation) {
scheme = SubstanceColorSchemeUtilities.getColorScheme(tree,
ColorSchemeAssociationKind.TEXT_HIGHLIGHT,
currState);
}
super.setForeground(new ColorUIResource(scheme
.getForegroundColor()));
}
} else {
if (sel)
this.setForeground(UIManager
.getColor("Tree.selectionForeground"));
else
this.setForeground(UIManager.getColor("Tree.textForeground"));
}
if (SubstanceLookAndFeel.isCurrentLookAndFeel())
SubstanceStripingUtils.applyStripedBackground(tree, row, this);
// There needs to be a way to specify disabled icons.
if (!tree.isEnabled()) {
this.setEnabled(false);
if (leaf) {
this.setDisabledIcon(SubstanceImageCreator
.toGreyscale(SubstanceImageCreator.makeTransparent(
tree, this.getDefaultLeafIcon(), 0.5)));
} else if (expanded) {
this.setDisabledIcon(SubstanceImageCreator
.toGreyscale(SubstanceImageCreator.makeTransparent(
tree, this.getDefaultOpenIcon(), 0.5)));
// setIcon(SubstanceImageCreator.toGreyscale(
// SubstanceImageCreator
// .makeTransparent(getDefaultOpenIcon(), 0.5)));
} else {
this.setDisabledIcon(SubstanceImageCreator
.toGreyscale(SubstanceImageCreator.makeTransparent(
tree, this.getDefaultClosedIcon(), 0.5)));
// setIcon(SubstanceImageCreator.toGreyscale(
// SubstanceImageCreator
// .makeTransparent(getDefaultClosedIcon(), 0.5)));
}
} else {
this.setEnabled(true);
if (leaf) {
this.setIcon(this.getDefaultLeafIcon());
} else if (expanded) {
this.setIcon(this.getDefaultOpenIcon());
} else {
this.setIcon(this.getDefaultClosedIcon());
}
}
this.setComponentOrientation(tree.getComponentOrientation());
this.setOpaque(false);
this.selected = sel;
if (treeUI instanceof SubstanceTreeUI) {
SubstanceTreeUI ui = (SubstanceTreeUI) treeUI;
Insets regInsets = ui.getCellRendererInsets();
this
.setBorder(new BorderUIResource.EmptyBorderUIResource(
regInsets));
}
return this;
}
private SubstanceColorScheme getColorSchemeForState(JTree tree,
SubstanceTreeUI ui, ComponentState activeState) {
SubstanceColorScheme scheme = (activeState == ComponentState.ENABLED) ? ui
.getDefaultColorScheme()
: SubstanceColorSchemeUtilities.getColorScheme(tree,
ColorSchemeAssociationKind.HIGHLIGHT, activeState);
if (scheme == null) {
scheme = SubstanceColorSchemeUtilities.getColorScheme(tree,
ColorSchemeAssociationKind.HIGHLIGHT, activeState);
}
return scheme;
}
/**
* Overrides <code>JComponent.getPreferredSize</code> to return slightly
* wider preferred size value.
*/
@Override
public Dimension getPreferredSize() {
Dimension retDimension = super.getPreferredSize();
if (retDimension != null)
retDimension = new Dimension(retDimension.width + 3,
retDimension.height);
return retDimension;
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void validate() {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*
* @since 1.5
*/
@Override
public void invalidate() {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void revalidate() {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void repaint(long tm, int x, int y, int width, int height) {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void repaint(Rectangle r) {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*
* @since 1.5
*/
@Override
public void repaint() {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
protected void firePropertyChange(String propertyName, Object oldValue,
Object newValue) {
if ("text".equals(propertyName))
super.firePropertyChange(propertyName, oldValue, newValue);
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void firePropertyChange(String propertyName, byte oldValue,
byte newValue) {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void firePropertyChange(String propertyName, char oldValue,
char newValue) {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void firePropertyChange(String propertyName, short oldValue,
short newValue) {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void firePropertyChange(String propertyName, int oldValue,
int newValue) {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void firePropertyChange(String propertyName, long oldValue,
long newValue) {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void firePropertyChange(String propertyName, float oldValue,
float newValue) {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void firePropertyChange(String propertyName, double oldValue,
double newValue) {
}
/**
* Overridden for performance reasons. See the <a
* href="#override">Implementation Note</a> for more information.
*/
@Override
public void firePropertyChange(String propertyName, boolean oldValue,
boolean newValue) {
}
/*
* (non-Javadoc)
*
* @see javax.swing.JComponent#paint(java.awt.Graphics)
*/
@Override
public final void paint(Graphics g) {
super.paint(g);
}
/*
* (non-Javadoc)
*
* @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
@Override
protected final void paintComponent(Graphics g) {
super.paintComponent(g);
}
}