blob: 810c4c94ca89540772726bd662ff93bdc69301a9 [file] [log] [blame]
package com.intellij.ui;
import com.intellij.ui.components.JBList;
import com.intellij.util.Function;
import com.intellij.util.containers.BidirectionalMap;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicRadioButtonUI;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Map;
/**
* @author oleg
*/
public class CheckBoxList<T> extends JBList {
private static final int DEFAULT_CHECK_BOX_WIDTH = 20;
private CheckBoxListListener checkBoxListListener;
private final BidirectionalMap<T, JCheckBox> myItemMap = new BidirectionalMap<T, JCheckBox>();
public CheckBoxList(final CheckBoxListListener checkBoxListListener) {
this(new DefaultListModel(), checkBoxListListener);
}
public CheckBoxList(final DefaultListModel dataModel, final CheckBoxListListener checkBoxListListener) {
this(dataModel);
setCheckBoxListListener(checkBoxListListener);
}
public CheckBoxList() {
this(new DefaultListModel());
}
public CheckBoxList(final DefaultListModel dataModel) {
super();
//noinspection unchecked
setModel(dataModel);
setCellRenderer(new CellRenderer());
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
setBorder(BorderFactory.createEtchedBorder());
addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
if (e.getKeyChar() == ' ') {
for (int index : getSelectedIndices()) {
if (index >= 0) {
JCheckBox checkbox = (JCheckBox)getModel().getElementAt(index);
setSelected(checkbox, index);
}
}
}
}
});
new ClickListener() {
@Override
public boolean onClick(@NotNull MouseEvent e, int clickCount) {
if (isEnabled()) {
int index = locationToIndex(e.getPoint());
if (index != -1) {
JCheckBox checkbox = (JCheckBox)getModel().getElementAt(index);
int iconArea;
try {
iconArea = checkbox.getMargin().left +
((BasicRadioButtonUI)checkbox.getUI()).getDefaultIcon().getIconWidth() +
checkbox.getIconTextGap();
}
catch (ClassCastException c) {
iconArea = DEFAULT_CHECK_BOX_WIDTH;
}
if (e.getX() < iconArea) {
setSelected(checkbox, index);
return true;
}
}
}
return false;
}
}.installOn(this);
}
public void setStringItems(final Map<String, Boolean> items) {
clear();
for (Map.Entry<String, Boolean> entry : items.entrySet()) {
//noinspection unchecked
addItem((T)entry.getKey(), entry.getKey(), entry.getValue());
}
}
public void setItems(final List<T> items, @Nullable Function<T, String> converter) {
clear();
for (T item : items) {
String text = converter != null ? converter.fun(item) : item.toString();
addItem(item, text, false);
}
}
public void addItem(T item, String text, boolean selected) {
JCheckBox checkBox = new JCheckBox(text, selected);
myItemMap.put(item, checkBox);
//noinspection unchecked
((DefaultListModel) getModel()).addElement(checkBox);
}
public void updateItem(@NotNull T oldItem, @NotNull T newItem) {
JCheckBox checkBox = myItemMap.remove(oldItem);
myItemMap.put(newItem, checkBox);
}
@Nullable
public T getItemAt(int index) {
JCheckBox checkBox = (JCheckBox)getModel().getElementAt(index);
List<T> value = myItemMap.getKeysByValue(checkBox);
return value == null || value.isEmpty() ? null : value.get(0);
}
public void clear() {
((DefaultListModel) getModel()).clear();
myItemMap.clear();
}
public boolean isItemSelected(int index) {
return ((JCheckBox)getModel().getElementAt(index)).isSelected();
}
public boolean isItemSelected(T item) {
JCheckBox checkBox = myItemMap.get(item);
return checkBox != null && checkBox.isSelected();
}
public void setItemSelected(T item, boolean selected) {
JCheckBox checkBox = myItemMap.get(item);
if (checkBox != null) {
checkBox.setSelected(selected);
}
}
private void setSelected(JCheckBox checkbox, int index) {
boolean value = !checkbox.isSelected();
checkbox.setSelected(value);
repaint();
// fire change notification in case if we've already initialized model
final ListModel model = getModel();
if (model instanceof DefaultListModel) {
//noinspection unchecked
((DefaultListModel)model).setElementAt(getModel().getElementAt(index), index);
}
if (checkBoxListListener != null) {
checkBoxListListener.checkBoxSelectionChanged(index, value);
}
}
public void setCheckBoxListListener(CheckBoxListListener checkBoxListListener) {
this.checkBoxListListener = checkBoxListListener;
}
protected void adjustRendering(final JCheckBox checkBox, final boolean selected, final boolean hasFocus) {
}
private class CellRenderer implements ListCellRenderer {
private final Border mySelectedBorder;
private final Border myBorder;
private CellRenderer() {
mySelectedBorder = UIManager.getBorder("List.focusCellHighlightBorder");
final Insets borderInsets = mySelectedBorder.getBorderInsets(new JCheckBox());
myBorder = new EmptyBorder(borderInsets);
}
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JCheckBox checkbox = (JCheckBox)value;
if (!UIUtil.isUnderNimbusLookAndFeel()) {
checkbox.setBackground(getBackground(isSelected, checkbox));
checkbox.setForeground(getForeground(isSelected, checkbox));
}
checkbox.setEnabled(isEnabled());
checkbox.setFont(getFont(checkbox));
checkbox.setFocusPainted(false);
checkbox.setBorderPainted(true);
checkbox.setBorder(isSelected ? mySelectedBorder : myBorder);
adjustRendering(checkbox, isSelected, cellHasFocus);
return checkbox;
}
}
protected Font getFont(final JCheckBox checkbox) {
return getFont();
}
protected Color getBackground(final boolean isSelected, final JCheckBox checkbox) {
return isSelected ? getSelectionBackground() : getBackground();
}
protected Color getForeground(final boolean isSelected, final JCheckBox checkbox) {
return isSelected ? getSelectionForeground() : getForeground();
}
}