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 final CellRenderer cellRenderer = new CellRenderer();
46 setCellRenderer(cellRenderer);
47 setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
48 setBorder(BorderFactory.createEtchedBorder());
49 addKeyListener(new KeyAdapter() {
51 public void keyTyped(KeyEvent e) {
52 if (e.getKeyChar() == ' ') {
53 for (int index : getSelectedIndices()) {
55 JCheckBox checkbox = (JCheckBox)getModel().getElementAt(index);
56 setSelected(checkbox, index);
64 public boolean onClick(@NotNull MouseEvent e, int clickCount) {
66 int index = locationToIndex(e.getPoint());
69 JCheckBox checkbox = (JCheckBox)getModel().getElementAt(index);
72 iconArea = checkbox.getMargin().left +
73 ((BasicRadioButtonUI)checkbox.getUI()).getDefaultIcon().getIconWidth() +
74 checkbox.getIconTextGap();
76 catch (ClassCastException c) {
77 iconArea = DEFAULT_CHECK_BOX_WIDTH;
79 int checkboxX = e.getX() - cellRenderer.getLeftBorderThickness();
80 if (checkboxX >= 0 && checkboxX < iconArea) {
81 setSelected(checkbox, index);
91 public void setStringItems(final Map<String, Boolean> items) {
93 for (Map.Entry<String, Boolean> entry : items.entrySet()) {
94 //noinspection unchecked
95 addItem((T)entry.getKey(), entry.getKey(), entry.getValue());
99 public void setItems(final List<T> items, @Nullable Function<T, String> converter) {
101 for (T item : items) {
102 String text = converter != null ? converter.fun(item) : item.toString();
103 addItem(item, text, false);
107 public void addItem(T item, String text, boolean selected) {
108 JCheckBox checkBox = new JCheckBox(text, selected);
109 myItemMap.put(item, checkBox);
110 //noinspection unchecked
111 ((DefaultListModel) getModel()).addElement(checkBox);
114 public void updateItem(@NotNull T oldItem, @NotNull T newItem, @NotNull String newText) {
115 JCheckBox checkBox = myItemMap.remove(oldItem);
116 myItemMap.put(newItem, checkBox);
117 checkBox.setText(newText);
118 DefaultListModel model = (DefaultListModel)getModel();
119 int ind = model.indexOf(checkBox);
121 model.set(ind, checkBox); // to fire contentsChanged event
126 public T getItemAt(int index) {
127 JCheckBox checkBox = (JCheckBox)getModel().getElementAt(index);
128 List<T> value = myItemMap.getKeysByValue(checkBox);
129 return value == null || value.isEmpty() ? null : value.get(0);
132 public void clear() {
133 ((DefaultListModel) getModel()).clear();
137 public boolean isItemSelected(int index) {
138 return ((JCheckBox)getModel().getElementAt(index)).isSelected();
141 public boolean isItemSelected(T item) {
142 JCheckBox checkBox = myItemMap.get(item);
143 return checkBox != null && checkBox.isSelected();
146 public void setItemSelected(T item, boolean selected) {
147 JCheckBox checkBox = myItemMap.get(item);
148 if (checkBox != null) {
149 checkBox.setSelected(selected);
153 private void setSelected(JCheckBox checkbox, int index) {
154 boolean value = !checkbox.isSelected();
155 checkbox.setSelected(value);
158 // fire change notification in case if we've already initialized model
159 final ListModel model = getModel();
160 if (model instanceof DefaultListModel) {
161 //noinspection unchecked
162 ((DefaultListModel)model).setElementAt(getModel().getElementAt(index), index);
165 if (checkBoxListListener != null) {
166 checkBoxListListener.checkBoxSelectionChanged(index, value);
170 public void setCheckBoxListListener(CheckBoxListListener checkBoxListListener) {
171 this.checkBoxListListener = checkBoxListListener;
174 protected JComponent adjustRendering(JComponent rootComponent,
175 final JCheckBox checkBox,
177 final boolean selected,
178 final boolean hasFocus) {
179 return rootComponent;
182 private class CellRenderer implements ListCellRenderer {
183 private final Border mySelectedBorder;
184 private final Border myBorder;
185 private final int myLeftBorderThickness;
187 private CellRenderer() {
188 mySelectedBorder = UIManager.getBorder("List.focusCellHighlightBorder");
189 final Insets borderInsets = mySelectedBorder.getBorderInsets(new JCheckBox());
190 myBorder = new EmptyBorder(borderInsets);
191 myLeftBorderThickness = borderInsets.left;
195 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
196 JCheckBox checkbox = (JCheckBox)value;
198 Color textColor = getForeground(isSelected);
199 Color backgroundColor = getBackground(isSelected);
200 Font font = getFont();
202 boolean shouldAdjustColors = !UIUtil.isUnderNimbusLookAndFeel();
204 if (shouldAdjustColors) {
205 checkbox.setBackground(backgroundColor);
206 checkbox.setForeground(textColor);
209 checkbox.setEnabled(isEnabled());
210 checkbox.setFont(font);
211 checkbox.setFocusPainted(false);
213 String auxText = getSecondaryText(index);
215 JComponent rootComponent;
216 if (auxText != null) {
217 JPanel panel = new JPanel(new BorderLayout());
219 checkbox.setBorderPainted(false);
220 panel.add(checkbox, BorderLayout.LINE_START);
222 JLabel infoLabel = new JLabel(auxText, SwingConstants.RIGHT);
223 infoLabel.setBorder(new EmptyBorder(0, 0, 0, checkbox.getInsets().left));
224 infoLabel.setFont(font);
225 panel.add(infoLabel, BorderLayout.CENTER);
227 if (shouldAdjustColors) {
228 panel.setBackground(backgroundColor);
229 infoLabel.setForeground(isSelected ? textColor : JBColor.GRAY);
230 infoLabel.setBackground(backgroundColor);
233 rootComponent = panel;
236 checkbox.setBorderPainted(true);
237 rootComponent = checkbox;
240 rootComponent.setBorder(isSelected ? mySelectedBorder : myBorder);
242 rootComponent = adjustRendering(rootComponent, checkbox, index, isSelected, cellHasFocus);
244 return rootComponent;
247 private int getLeftBorderThickness() {
248 return myLeftBorderThickness;
253 protected String getSecondaryText(int index) {
257 protected Color getBackground(final boolean isSelected) {
258 return isSelected ? getSelectionBackground() : getBackground();
261 protected Color getForeground(final boolean isSelected) {
262 return isSelected ? getSelectionForeground() : getForeground();