allow to filter JdkComboBox items
[idea/community.git] / java / idea-ui / src / com / intellij / openapi / roots / ui / configuration / JdkComboBox.java
1 /*
2  * Copyright 2000-2011 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.roots.ui.configuration;
17
18 import com.intellij.ide.DataManager;
19 import com.intellij.ide.util.projectWizard.ProjectJdkListRenderer;
20 import com.intellij.openapi.actionSystem.DefaultActionGroup;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.project.ProjectBundle;
23 import com.intellij.openapi.projectRoots.Sdk;
24 import com.intellij.openapi.roots.ui.configuration.projectRoot.JdkListConfigurable;
25 import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
26 import com.intellij.openapi.ui.ComboBoxWithWidePopup;
27 import com.intellij.openapi.ui.popup.JBPopupFactory;
28 import com.intellij.openapi.util.Computable;
29 import com.intellij.openapi.util.Condition;
30 import com.intellij.ui.ScreenUtil;
31 import com.intellij.ui.SimpleTextAttributes;
32 import com.intellij.util.Consumer;
33 import com.intellij.util.containers.ContainerUtil;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36
37 import javax.swing.*;
38 import java.awt.*;
39 import java.awt.event.ActionEvent;
40 import java.awt.event.ActionListener;
41 import java.util.*;
42 import java.util.List;
43
44 /**
45  * @author Eugene Zhuravlev
46  * @since May 18, 2005
47  */
48 public class JdkComboBox extends ComboBoxWithWidePopup {
49
50   @Nullable
51   private Condition<Sdk> myFilter;
52   
53   public JdkComboBox(@NotNull final ProjectSdksModel jdkModel) {
54     this(jdkModel, null);
55   }
56
57   public JdkComboBox(@NotNull final ProjectSdksModel jdkModel, @Nullable Condition<Sdk> filter) {
58     super(new JdkComboBoxModel(jdkModel, filter));
59     myFilter = filter;
60     setRenderer(new ProjectJdkListRenderer(getRenderer()) {
61       @Override
62       public void doCustomize(JList list, Object value, int index, boolean selected, boolean hasFocus) {
63         if (JdkComboBox.this.isEnabled()) {
64           if (value instanceof InvalidJdkComboBoxItem) {
65             final String str = value.toString();
66             append(str, SimpleTextAttributes.ERROR_ATTRIBUTES);
67           }
68           else if (value instanceof ProjectJdkComboBoxItem){
69             final Sdk jdk = jdkModel.getProjectSdk();
70             if (jdk != null){
71               setIcon(jdk.getSdkType().getIcon());
72               append(ProjectBundle.message("project.roots.project.jdk.inherited"), SimpleTextAttributes.REGULAR_ATTRIBUTES);
73               append(" (" + jdk.getName() + ")", SimpleTextAttributes.GRAYED_ATTRIBUTES);
74             } else {
75               final String str = value.toString();
76               append(str, SimpleTextAttributes.ERROR_ATTRIBUTES);
77             }
78           }
79           else {
80             super.doCustomize(list, value != null ? ((JdkComboBoxItem)value).getJdk() : new NoneJdkComboBoxItem(), index, selected,
81                               hasFocus);
82           }
83         }
84       }
85     });
86   }
87
88   @Override
89   public Dimension getPreferredSize() {
90     final Rectangle rec = ScreenUtil.getScreenRectangle(0, 0);
91     final Dimension size = super.getPreferredSize();
92     final int maxWidth = rec.width / 4;
93     if (size.width > maxWidth) {
94       size.width = maxWidth; 
95     }
96     return size;
97   }
98
99   @Override
100   public Dimension getMinimumSize() {
101     final Dimension minSize = super.getMinimumSize();
102     final Dimension prefSize = getPreferredSize();
103     if (minSize.width > prefSize.width) {
104       minSize.width = prefSize.width;
105     }
106     return minSize;
107   }
108
109   public void setSetupButton(final JButton setUpButton,
110                                 final Project project,
111                                 final ProjectSdksModel jdksModel,
112                                 final JdkComboBoxItem firstItem,
113                                 @Nullable final Condition<Sdk> additionalSetup,
114                                 final boolean moduleJdkSetup) {
115     setSetupButton(setUpButton, project, jdksModel, firstItem, additionalSetup,
116                    ProjectBundle.message("project.roots.set.up.jdk.title", moduleJdkSetup ? 1 : 2));
117   }
118
119   public void setSetupButton(final JButton setUpButton,
120                                 final Project project,
121                                 final ProjectSdksModel jdksModel,
122                                 final JdkComboBoxItem firstItem,
123                                 @Nullable final Condition<Sdk> additionalSetup,
124                                 final String actionGroupTitle) {
125     setUpButton.addActionListener(new ActionListener() {
126       public void actionPerformed(ActionEvent e) {
127         final JdkListConfigurable configurable = JdkListConfigurable.getInstance(project);
128         DefaultActionGroup group = new DefaultActionGroup();
129         jdksModel.createAddActions(group, JdkComboBox.this, new Consumer<Sdk>() {
130           public void consume(final Sdk jdk) {
131             configurable.addJdkNode(jdk, false);
132             reloadModel(firstItem, project);
133             setSelectedJdk(jdk); //restore selection
134             if (additionalSetup != null) {
135               if (additionalSetup.value(jdk)) { //leave old selection
136                 setSelectedJdk(firstItem.getJdk());
137               }
138             }
139           }
140         });
141         JBPopupFactory.getInstance()
142           .createActionGroupPopup(actionGroupTitle, group,
143                                   DataManager.getInstance().getDataContext(JdkComboBox.this), JBPopupFactory.ActionSelectionAid.MNEMONICS,
144                                   false)
145           .showUnderneathOf(setUpButton);
146       }
147     });
148   }
149
150   public void setEditButton(final JButton editButton, final Project project, final Computable<Sdk> retrieveJDK){
151     editButton.addActionListener(new ActionListener() {
152       public void actionPerformed(ActionEvent e) {
153         final Sdk projectJdk = retrieveJDK.compute();
154         if (projectJdk != null) {
155           ProjectStructureConfigurable.getInstance(project).select(projectJdk, true);
156         }
157       }
158     });
159     addActionListener(new ActionListener() {
160       public void actionPerformed(ActionEvent e) {
161         final JdkComboBoxItem selectedItem = getSelectedItem();
162         if (selectedItem instanceof ProjectJdkComboBoxItem) {
163           editButton.setEnabled(ProjectStructureConfigurable.getInstance(project).getProjectJdksModel().getProjectSdk() != null);
164         }
165         else {
166           editButton.setEnabled(!(selectedItem instanceof InvalidJdkComboBoxItem) && selectedItem != null && selectedItem.getJdk() != null);
167         }
168       }
169     });
170   }
171
172   public JdkComboBoxItem getSelectedItem() {
173     return (JdkComboBoxItem)super.getSelectedItem();
174   }
175
176   @Nullable
177   public Sdk getSelectedJdk() {
178     final JdkComboBoxItem selectedItem = (JdkComboBoxItem)super.getSelectedItem();
179     return selectedItem != null? selectedItem.getJdk() : null;
180   }
181
182   public void setSelectedJdk(Sdk jdk) {
183     final int index = indexOf(jdk);
184     if (index >= 0) {
185       setSelectedIndex(index);
186     }
187   }
188
189   public void setInvalidJdk(String name) {
190     removeInvalidElement();
191     addItem(new InvalidJdkComboBoxItem(name));
192     setSelectedIndex(getModel().getSize() - 1);
193   }
194   
195   private int indexOf(Sdk jdk) {
196     final JdkComboBoxModel model = (JdkComboBoxModel)getModel();
197     final int count = model.getSize();
198     for (int idx = 0; idx < count; idx++) {
199       final JdkComboBoxItem elementAt = model.getElementAt(idx);
200       if (jdk == null) {
201         if (elementAt instanceof NoneJdkComboBoxItem || elementAt instanceof ProjectJdkComboBoxItem) {
202           return idx;
203         }
204       }
205       else {
206         if (jdk.equals(elementAt.getJdk())) {
207           return idx;
208         }
209       }
210     }
211     return -1;
212   }
213   
214   private void removeInvalidElement() {
215     final JdkComboBoxModel model = (JdkComboBoxModel)getModel();
216     final int count = model.getSize();
217     for (int idx = 0; idx < count; idx++) {
218       final JdkComboBoxItem elementAt = model.getElementAt(idx);
219       if (elementAt instanceof InvalidJdkComboBoxItem) {
220         removeItemAt(idx);
221         break;
222       }
223     }
224   }
225
226   public void reloadModel(JdkComboBoxItem firstItem, Project project) {
227     final DefaultComboBoxModel model = ((DefaultComboBoxModel)getModel());
228     model.removeAllElements();
229     model.addElement(firstItem);
230     final ProjectSdksModel projectJdksModel = ProjectStructureConfigurable.getInstance(project).getProjectJdksModel();
231     List<Sdk> projectJdks = new ArrayList<Sdk>(projectJdksModel.getProjectSdks().values());
232     if (myFilter != null) {
233       projectJdks = ContainerUtil.filter(projectJdks, myFilter);
234     }
235     Collections.sort(projectJdks, new Comparator<Sdk>() {
236       public int compare(final Sdk o1, final Sdk o2) {
237         return o1.getName().compareToIgnoreCase(o2.getName());
238       }
239     });
240     for (Sdk projectJdk : projectJdks) {
241       model.addElement(new JdkComboBox.JdkComboBoxItem(projectJdk));
242     }
243   }
244
245   private static class JdkComboBoxModel extends DefaultComboBoxModel {
246     public JdkComboBoxModel(final ProjectSdksModel jdksModel, final @Nullable Condition<Sdk> filter) {
247       Sdk[] jdks = jdksModel.getSdks();
248       if (filter != null) {
249         final List<Sdk> filtered = ContainerUtil.filter(jdks, filter);
250         jdks = filtered.toArray(new Sdk[filtered.size()]); 
251       }
252       Arrays.sort(jdks, new Comparator<Sdk>() {
253         public int compare(final Sdk s1, final Sdk s2) {
254           return s1.getName().compareToIgnoreCase(s2.getName());
255         }
256       });
257       for (Sdk jdk : jdks) {
258         addElement(new JdkComboBoxItem(jdk));
259       }
260     }
261
262     // implements javax.swing.ListModel
263     public JdkComboBoxItem getElementAt(int index) {
264       return (JdkComboBoxItem)super.getElementAt(index);
265     }
266   }
267   
268   public static class JdkComboBoxItem {
269     private final Sdk myJdk;
270
271     public JdkComboBoxItem(@Nullable Sdk jdk) {
272       myJdk = jdk;
273     }
274
275     public Sdk getJdk() {
276       return myJdk;
277     }
278
279     @Nullable
280     public String getSdkName() {
281       return myJdk != null ? myJdk.getName() : null;
282     }
283     
284     public String toString() {
285       return myJdk.getName();
286     }
287   }
288
289   public static class ProjectJdkComboBoxItem extends JdkComboBoxItem {
290     public ProjectJdkComboBoxItem() {
291       super(null);
292     }
293
294     public String toString() {
295       return ProjectBundle.message("jdk.combo.box.project.item");
296     }
297   }
298
299   public static class NoneJdkComboBoxItem extends JdkComboBoxItem {
300     public NoneJdkComboBoxItem() {
301       super(null);
302     }
303
304     public String toString() {
305       return ProjectBundle.message("jdk.combo.box.none.item");
306     }
307   }
308
309   private static class InvalidJdkComboBoxItem extends JdkComboBoxItem {
310     private final String mySdkName;
311
312     public InvalidJdkComboBoxItem(String name) {
313       super(null);
314       mySdkName = name;
315     }
316
317     public String getSdkName() {
318       return mySdkName;
319     }
320
321     public String toString() {
322       return ProjectBundle.message("jdk.combo.box.invalid.item", mySdkName);
323     }
324   }
325 }