1 package com.intellij.ui;
3 import com.intellij.ui.components.JBList;
4 import com.intellij.util.Function;
5 import com.intellij.util.containers.BidirectionalMap;
6 import com.intellij.util.ui.UIUtil;
7 import org.jetbrains.annotations.NotNull;
8 import org.jetbrains.annotations.Nullable;
11 import javax.swing.border.Border;
12 import javax.swing.border.EmptyBorder;
13 import javax.swing.plaf.basic.BasicRadioButtonUI;
15 import java.awt.event.KeyAdapter;
16 import java.awt.event.KeyEvent;
17 import java.awt.event.MouseEvent;
18 import java.util.List;
24 public class CheckBoxList<T> extends JBList {
25 private static final int DEFAULT_CHECK_BOX_WIDTH = 20;
26 private CheckBoxListListener checkBoxListListener;
27 private final BidirectionalMap<T, JCheckBox> myItemMap = new BidirectionalMap<T, JCheckBox>();
29 public CheckBoxList(final CheckBoxListListener checkBoxListListener) {
30 this(new DefaultListModel(), checkBoxListListener);
32 public CheckBoxList(final DefaultListModel dataModel, final CheckBoxListListener checkBoxListListener) {
34 setCheckBoxListListener(checkBoxListListener);
37 public CheckBoxList() {
38 this(new DefaultListModel());
41 public CheckBoxList(final DefaultListModel dataModel) {
43 //noinspection unchecked
45 setCellRenderer(new CellRenderer());
46 setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
47 setBorder(BorderFactory.createEtchedBorder());
48 addKeyListener(new KeyAdapter() {
50 public void keyTyped(KeyEvent e) {
51 if (e.getKeyChar() == ' ') {
52 for (int index : getSelectedIndices()) {
54 JCheckBox checkbox = (JCheckBox)getModel().getElementAt(index);
55 setSelected(checkbox, index);
63 public boolean onClick(@NotNull MouseEvent e, int clickCount) {
65 int index = locationToIndex(e.getPoint());
68 JCheckBox checkbox = (JCheckBox)getModel().getElementAt(index);
71 iconArea = checkbox.getMargin().left +
72 ((BasicRadioButtonUI)checkbox.getUI()).getDefaultIcon().getIconWidth() +
73 checkbox.getIconTextGap();
75 catch (ClassCastException c) {
76 iconArea = DEFAULT_CHECK_BOX_WIDTH;
78 if (e.getX() < iconArea + checkbox.getX()) {
79 setSelected(checkbox, index);
89 public void setStringItems(final Map<String, Boolean> items) {
91 for (Map.Entry<String, Boolean> entry : items.entrySet()) {
92 //noinspection unchecked
93 addItem((T)entry.getKey(), entry.getKey(), entry.getValue());
97 public void setItems(final List<T> items, @Nullable Function<T, String> converter) {
99 for (T item : items) {
100 String text = converter != null ? converter.fun(item) : item.toString();
101 addItem(item, text, false);
105 public void addItem(T item, String text, boolean selected) {
106 JCheckBox checkBox = new JCheckBox(text, selected);
107 myItemMap.put(item, checkBox);
108 //noinspection unchecked
109 ((DefaultListModel) getModel()).addElement(checkBox);
112 public void updateItem(@NotNull T oldItem, @NotNull T newItem, @NotNull String newText) {
113 JCheckBox checkBox = myItemMap.remove(oldItem);
114 myItemMap.put(newItem, checkBox);
115 checkBox.setText(newText);
116 DefaultListModel model = (DefaultListModel)getModel();
117 int ind = model.indexOf(checkBox);
119 model.set(ind, checkBox); // to fire contentsChanged event
124 public T getItemAt(int index) {
125 JCheckBox checkBox = (JCheckBox)getModel().getElementAt(index);
126 List<T> value = myItemMap.getKeysByValue(checkBox);
127 return value == null || value.isEmpty() ? null : value.get(0);
130 public void clear() {
131 ((DefaultListModel) getModel()).clear();
135 public boolean isItemSelected(int index) {
136 return ((JCheckBox)getModel().getElementAt(index)).isSelected();
139 public boolean isItemSelected(T item) {
140 JCheckBox checkBox = myItemMap.get(item);
141 return checkBox != null && checkBox.isSelected();
144 public void setItemSelected(T item, boolean selected) {
145 JCheckBox checkBox = myItemMap.get(item);
146 if (checkBox != null) {
147 checkBox.setSelected(selected);
151 private void setSelected(JCheckBox checkbox, int index) {
152 boolean value = !checkbox.isSelected();
153 checkbox.setSelected(value);
156 // fire change notification in case if we've already initialized model
157 final ListModel model = getModel();
158 if (model instanceof DefaultListModel) {
159 //noinspection unchecked
160 ((DefaultListModel)model).setElementAt(getModel().getElementAt(index), index);
163 if (checkBoxListListener != null) {
164 checkBoxListListener.checkBoxSelectionChanged(index, value);
168 public void setCheckBoxListListener(CheckBoxListListener checkBoxListListener) {
169 this.checkBoxListListener = checkBoxListListener;
172 protected JComponent adjustRendering(JComponent rootComponent,
173 final JCheckBox checkBox,
175 final boolean selected,
176 final boolean hasFocus) {
177 return rootComponent;
180 private class CellRenderer implements ListCellRenderer {
181 private final Border mySelectedBorder;
182 private final Border myBorder;
184 private CellRenderer() {
185 mySelectedBorder = UIManager.getBorder("List.focusCellHighlightBorder");
186 final Insets borderInsets = mySelectedBorder.getBorderInsets(new JCheckBox());
187 myBorder = new EmptyBorder(borderInsets);
191 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
192 JCheckBox checkbox = (JCheckBox)value;
194 Color textColor = getForeground(isSelected);
195 Color backgroundColor = getBackground(isSelected);
196 Font font = getFont();
198 boolean shouldAdjustColors = !UIUtil.isUnderNimbusLookAndFeel();
200 if (shouldAdjustColors) {
201 checkbox.setBackground(backgroundColor);
202 checkbox.setForeground(textColor);
205 checkbox.setEnabled(isEnabled());
206 checkbox.setFont(font);
207 checkbox.setFocusPainted(false);
209 String auxText = getSecondaryText(index);
211 JComponent rootComponent;
212 if (auxText != null) {
213 JPanel panel = new JPanel(new BorderLayout());
215 checkbox.setBorderPainted(false);
216 panel.add(checkbox, BorderLayout.LINE_START);
218 JLabel infoLabel = new JLabel(auxText, SwingConstants.RIGHT);
219 infoLabel.setBorder(new EmptyBorder(0, 0, 0, checkbox.getInsets().left));
220 infoLabel.setFont(font);
221 panel.add(infoLabel, BorderLayout.CENTER);
223 if (shouldAdjustColors) {
224 panel.setBackground(backgroundColor);
225 infoLabel.setForeground(isSelected ? textColor : JBColor.GRAY);
226 infoLabel.setBackground(backgroundColor);
229 rootComponent = panel;
232 checkbox.setBorderPainted(true);
233 rootComponent = checkbox;
236 rootComponent.setBorder(isSelected ? mySelectedBorder : myBorder);
238 rootComponent = adjustRendering(rootComponent, checkbox, index, isSelected, cellHasFocus);
240 return rootComponent;
245 protected String getSecondaryText(int index) {
249 protected Color getBackground(final boolean isSelected) {
250 return isSelected ? getSelectionBackground() : getBackground();
253 protected Color getForeground(final boolean isSelected) {
254 return isSelected ? getSelectionForeground() : getForeground();