WEB-17222 File watcher is shown with a new name only after re-open Settings
[idea/community.git] / platform / platform-api / src / com / intellij / ui / CheckBoxList.java
1 package com.intellij.ui;
2
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;
9
10 import javax.swing.*;
11 import javax.swing.border.Border;
12 import javax.swing.border.EmptyBorder;
13 import javax.swing.plaf.basic.BasicRadioButtonUI;
14 import java.awt.*;
15 import java.awt.event.KeyAdapter;
16 import java.awt.event.KeyEvent;
17 import java.awt.event.MouseEvent;
18 import java.util.List;
19 import java.util.Map;
20
21 /**
22  * @author oleg
23  */
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>();
28
29   public CheckBoxList(final CheckBoxListListener checkBoxListListener) {
30     this(new DefaultListModel(), checkBoxListListener);
31   }
32   public CheckBoxList(final DefaultListModel dataModel, final CheckBoxListListener checkBoxListListener) {
33     this(dataModel);
34     setCheckBoxListListener(checkBoxListListener);
35   }
36
37   public CheckBoxList() {
38     this(new DefaultListModel());
39   }
40
41   public CheckBoxList(final DefaultListModel dataModel) {
42     super();
43     //noinspection unchecked
44     setModel(dataModel);
45     setCellRenderer(new CellRenderer());
46     setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
47     setBorder(BorderFactory.createEtchedBorder());
48     addKeyListener(new KeyAdapter() {
49       @Override
50       public void keyTyped(KeyEvent e) {
51         if (e.getKeyChar() == ' ') {
52           for (int index : getSelectedIndices()) {
53             if (index >= 0) {
54               JCheckBox checkbox = (JCheckBox)getModel().getElementAt(index);
55               setSelected(checkbox, index);
56             }
57           }
58         }
59       }
60     });
61     new ClickListener() {
62       @Override
63       public boolean onClick(@NotNull MouseEvent e, int clickCount) {
64         if (isEnabled()) {
65           int index = locationToIndex(e.getPoint());
66
67           if (index != -1) {
68             JCheckBox checkbox = (JCheckBox)getModel().getElementAt(index);
69             int iconArea;
70             try {
71               iconArea = checkbox.getMargin().left +
72                          ((BasicRadioButtonUI)checkbox.getUI()).getDefaultIcon().getIconWidth() +
73                          checkbox.getIconTextGap();
74             }
75             catch (ClassCastException c) {
76               iconArea = DEFAULT_CHECK_BOX_WIDTH;
77             }
78             if (e.getX() < iconArea) {
79               setSelected(checkbox, index);
80               return true;
81             }
82           }
83         }
84         return false;
85       }
86     }.installOn(this);
87   }
88
89   public void setStringItems(final Map<String, Boolean> items) {
90     clear();
91     for (Map.Entry<String, Boolean> entry : items.entrySet()) {
92       //noinspection unchecked
93       addItem((T)entry.getKey(), entry.getKey(), entry.getValue());
94     }
95   }
96
97   public void setItems(final List<T> items, @Nullable Function<T, String> converter) {
98     clear();
99     for (T item : items) {
100       String text = converter != null ? converter.fun(item) : item.toString();
101       addItem(item, text, false);
102     }
103   }
104
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);
110   }
111
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);
118     if (ind >= 0) {
119       model.set(ind, checkBox); // to fire contentsChanged event
120     }
121   }
122
123   @Nullable
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);
128   }
129
130   public void clear() {
131     ((DefaultListModel) getModel()).clear();
132     myItemMap.clear();
133   }
134
135   public boolean isItemSelected(int index) {
136     return ((JCheckBox)getModel().getElementAt(index)).isSelected();
137   }
138
139   public boolean isItemSelected(T item) {
140     JCheckBox checkBox = myItemMap.get(item);
141     return checkBox != null && checkBox.isSelected();
142   }
143
144   public void setItemSelected(T item, boolean selected) {
145     JCheckBox checkBox = myItemMap.get(item);
146     if (checkBox != null) {
147       checkBox.setSelected(selected);
148     }
149   }
150
151   private void setSelected(JCheckBox checkbox, int index) {
152     boolean value = !checkbox.isSelected();
153     checkbox.setSelected(value);
154     repaint();
155
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);
161     }
162
163     if (checkBoxListListener != null) {
164       checkBoxListListener.checkBoxSelectionChanged(index, value);
165     }
166   }
167
168   public void setCheckBoxListListener(CheckBoxListListener checkBoxListListener) {
169     this.checkBoxListListener = checkBoxListListener;
170   }
171
172   protected void adjustRendering(final JCheckBox checkBox, int index, final boolean selected, final boolean hasFocus) {
173   }
174
175   private class CellRenderer implements ListCellRenderer {
176     private final Border mySelectedBorder;
177     private final Border myBorder;
178
179     private CellRenderer() {
180       mySelectedBorder = UIManager.getBorder("List.focusCellHighlightBorder");
181       final Insets borderInsets = mySelectedBorder.getBorderInsets(new JCheckBox());
182       myBorder = new EmptyBorder(borderInsets);
183     }
184
185     @Override
186     public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
187       JCheckBox checkbox = (JCheckBox)value;
188
189       Color textColor = getForeground(isSelected);
190       Color backgroundColor = getBackground(isSelected);
191       Font font = getFont();
192
193       boolean shouldAdjustColors = !UIUtil.isUnderNimbusLookAndFeel();
194
195       if (shouldAdjustColors) {
196         checkbox.setBackground(backgroundColor);
197         checkbox.setForeground(textColor);
198       }
199
200       checkbox.setEnabled(isEnabled());
201       checkbox.setFont(font);
202       checkbox.setFocusPainted(false);
203
204       String auxText = getSecondaryText(index);
205
206       JComponent rootComponent;
207       if (auxText != null) {
208         JPanel panel = new JPanel(new BorderLayout());
209
210         checkbox.setBorderPainted(false);
211         panel.add(checkbox, BorderLayout.LINE_START);
212
213         JLabel infoLabel = new JLabel(auxText, SwingConstants.RIGHT);
214         infoLabel.setBorder(new EmptyBorder(0, 0, 0, checkbox.getInsets().left));
215         infoLabel.setFont(font);
216         panel.add(infoLabel, BorderLayout.CENTER);
217
218         if (shouldAdjustColors) {
219           panel.setBackground(backgroundColor);
220           infoLabel.setForeground(isSelected ? textColor : JBColor.GRAY);
221           infoLabel.setBackground(backgroundColor);
222         }
223
224         rootComponent = panel;
225       }
226       else {
227         checkbox.setBorderPainted(true);
228         rootComponent = checkbox;
229       }
230
231       rootComponent.setBorder(isSelected ? mySelectedBorder : myBorder);
232
233       adjustRendering(checkbox, index, isSelected, cellHasFocus);
234       return rootComponent;
235     }
236   }
237
238   @Nullable
239   protected String getSecondaryText(int index) {
240     return null;
241   }
242
243   protected Color getBackground(final boolean isSelected) {
244       return isSelected ? getSelectionBackground() : getBackground();
245     }
246
247   protected Color getForeground(final boolean isSelected) {
248     return isSelected ? getSelectionForeground() : getForeground();
249   }
250 }