/*
* @(#)CheckBoxTreeCellRenderer.java 8/11/2005
*
* Copyright 2002 - 2005 JIDE Software Inc. All rights reserved.
*/
package com.jidesoft.swing;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.io.Serializable;
/**
* Renderers an item in a tree using JCheckBox.
*/
public class CheckBoxTreeCellRenderer extends JPanel implements TreeCellRenderer, Serializable {
private static final long serialVersionUID = 30207434500313004L;
/**
* The checkbox that is used to paint the check box in cell renderer
*/
protected TristateCheckBox _checkBox = null;
protected JComponent _emptyBox = null;
protected JCheckBox _protoType;
/**
* The label which appears after the check box.
*/
protected TreeCellRenderer _actualTreeRenderer;
/**
* Constructs a default renderer object for an item in a list.
*/
public CheckBoxTreeCellRenderer() {
this(null);
}
public CheckBoxTreeCellRenderer(TreeCellRenderer renderer) {
this(renderer, null);
}
public CheckBoxTreeCellRenderer(TreeCellRenderer renderer, TristateCheckBox checkBox) {
_protoType = new TristateCheckBox();
if (checkBox == null) {
_checkBox = createCheckBox();
}
else {
_checkBox = checkBox;
}
_emptyBox = (JComponent) Box.createHorizontalStrut(_protoType.getPreferredSize().width);
setLayout(new BorderLayout(0, 0));
setOpaque(false);
_actualTreeRenderer = renderer;
}
/**
* Create the check box in the cell.
* <p/>
* By default, it creates a TristateCheckBox and set opaque to false.
*
* @return the check box instance.
*/
protected TristateCheckBox createCheckBox() {
TristateCheckBox checkBox = new NullTristateCheckBox();
checkBox.setOpaque(false);
return checkBox;
}
public TreeCellRenderer getActualTreeRenderer() {
return _actualTreeRenderer;
}
public void setActualTreeRenderer(TreeCellRenderer actualTreeRenderer) {
_actualTreeRenderer = actualTreeRenderer;
}
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
removeAll();
// _checkBox.setPreferredSize(new Dimension(_protoType.getPreferredSize().width, 0));
_emptyBox.setPreferredSize(new Dimension(_protoType.getPreferredSize().width, 0));
applyComponentOrientation(tree.getComponentOrientation());
TreePath path = tree.getPathForRow(row);
if (path != null && tree instanceof CheckBoxTree) {
CheckBoxTreeSelectionModel selectionModel = ((CheckBoxTree) tree).getCheckBoxTreeSelectionModel();
if (selectionModel != null) {
boolean enabled = tree.isEnabled() && ((CheckBoxTree) tree).isCheckBoxEnabled() && ((CheckBoxTree) tree).isCheckBoxEnabled(path);
if (!enabled && !selected) {
if (getBackground() != null) {
setForeground(getBackground().darker());
}
}
_checkBox.setEnabled(enabled);
updateCheckBoxState(_checkBox, path, selectionModel);
}
}
if (_actualTreeRenderer != null) {
JComponent treeCellRendererComponent = (JComponent) _actualTreeRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
Border border = treeCellRendererComponent.getBorder();
setBorder(border);
treeCellRendererComponent.setBorder(BorderFactory.createEmptyBorder());
if (path == null || !(tree instanceof CheckBoxTree) || ((CheckBoxTree) tree).isCheckBoxVisible(path)) {
remove(_emptyBox);
add(_checkBox, BorderLayout.BEFORE_LINE_BEGINS);
}
else {
remove(_checkBox);
add(_emptyBox, BorderLayout.AFTER_LINE_ENDS); // expand the tree node size to be the same as the one with check box.
}
add(treeCellRendererComponent);
// copy the background and foreground for the renderer component
setBackground(treeCellRendererComponent.getBackground());
treeCellRendererComponent.setBackground(null);
setForeground(treeCellRendererComponent.getForeground());
treeCellRendererComponent.setForeground(null);
}
return this;
}
/**
* Updates the check box state based on the selection in the selection model. By default, we check if the path is
* selected. If yes, we mark the check box as TristateCheckBox.SELECTED. If not, we will check if the path is
* partially selected, if yes, we set the check box as null or TristateCheckBox.DONT_CARE to indicate the path is
* partially selected. Otherwise, we set it to TristateCheckBox.NOT_SELECTED.
*
* @param checkBox the TristateCheckBox for the particular tree path.
* @param path the tree path.
* @param selectionModel the CheckBoxTreeSelectionModel.
*/
protected void updateCheckBoxState(TristateCheckBox checkBox, TreePath path, CheckBoxTreeSelectionModel selectionModel) {
if (selectionModel.isPathSelected(path, selectionModel.isDigIn()))
checkBox.setState(TristateCheckBox.STATE_SELECTED);
else
checkBox.setState(selectionModel.isDigIn() && selectionModel.isPartiallySelected(path) ? TristateCheckBox.STATE_MIXED : TristateCheckBox.STATE_UNSELECTED);
}
@Override
public String getToolTipText(MouseEvent event) {
if (_actualTreeRenderer instanceof JComponent) {
Point p = event.getPoint();
p.translate(-_checkBox.getWidth(), 0);
MouseEvent newEvent = new MouseEvent(((JComponent) _actualTreeRenderer), event.getID(),
event.getWhen(),
event.getModifiers(),
p.x, p.y, event.getClickCount(),
event.isPopupTrigger());
String tip = ((JComponent) _actualTreeRenderer).getToolTipText(
newEvent);
if (tip != null) {
return tip;
}
}
return super.getToolTipText(event);
}
}