/* * Copyright 2013 original Randori IntelliJ Plugin authors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package randori.plugin.ui; import com.intellij.ide.DataManager; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.projectRoots.*; import com.intellij.openapi.projectRoots.ui.ProjectJdksEditor; import com.intellij.openapi.roots.ui.configuration.ProjectJdksConfigurable; import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable; import com.intellij.openapi.roots.ui.configuration.projectRoot.JdkListConfigurable; import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel; import com.intellij.openapi.ui.MasterDetailsComponent; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.ComboboxWithBrowseButton; import com.intellij.ui.ListCellRendererWrapper; import com.intellij.util.ArrayUtil; import com.intellij.util.Consumer; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JList; import org.jetbrains.annotations.Nullable; import randori.plugin.roots.RandoriSdkType; /** * @author Frédéric THOMAS * Date: 20/04/13 * Time: 08:39 */ public class RandoriSdkComboBoxWithBrowseButton extends ComboboxWithBrowseButton { private static final Condition<Sdk> RANDORI_SDK = new Condition<Sdk>() { @Override public boolean value(Sdk sdk) { return (sdk != null) && ((sdk.getSdkType() instanceof RandoriSdkType)); } }; public static final String BC_SDK_KEY = "BC SDK"; private final Condition<Sdk> sdkFilter; private final BCSdk bcSdk = new BCSdk(); private boolean showBCSdk = false; public RandoriSdkComboBoxWithBrowseButton() { this.sdkFilter = RandoriSdkComboBoxWithBrowseButton.RANDORI_SDK; rebuildSdkListAndSelectSdk(null); final JComboBox sdkCombo = getComboBox(); sdkCombo.setRenderer(new ListCellRendererWrapper() { public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) { if ((value instanceof RandoriSdkComboBoxWithBrowseButton.BCSdk)) { Sdk sdk = ((RandoriSdkComboBoxWithBrowseButton.BCSdk)value).mySdk; if (sdk == null) { if (sdkCombo.isEnabled()) { setText("<html>SDK set for the build configuration <font color='red'>[not set]</font></html>"); setIcon(null); } else { setText("SDK set for the build configuration [not set]"); setIcon(null); } } else { setText("SDK set for the build configuration [" + sdk.getName() + "]"); setIcon(((SdkType)((RandoriSdkComboBoxWithBrowseButton.BCSdk)value).mySdk.getSdkType()).getIcon()); } } else if ((value instanceof String)) { if (sdkCombo.isEnabled()) { setText("<html><font color='red'>" + value + " [Invalid]</font></html>"); setIcon(null); } else { setText(value + " [Invalid]"); setIcon(null); } } else if ((value instanceof Sdk)) { setText(((Sdk)value).getName()); setIcon(((SdkType)((Sdk)value).getSdkType()).getIcon()); } else if (sdkCombo.isEnabled()) { setText("<html><font color='red'>[none]</font></html>"); } else { setText("[none]"); } } }); addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Project project = PlatformDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()); if (project == null) { project = ProjectManager.getInstance().getDefaultProject(); } ProjectSdksModel sdksModel = ProjectStructureConfigurable.getInstance(project).getProjectJdksModel(); sdksModel = new RandoriSdkComboBoxWithBrowseButton.NonCommittingWrapper(sdksModel, JdkListConfigurable.getInstance(project)); ProjectJdksEditor editor = new ProjectJdksEditor(null, RandoriSdkComboBoxWithBrowseButton.this, new ProjectJdksConfigurable(project, sdksModel)); editor.show(); if (editor.isOK()) { Sdk selectedSdk = editor.getSelectedJdk(); if (RandoriSdkComboBoxWithBrowseButton.this.sdkFilter.value(selectedSdk)) { RandoriSdkComboBoxWithBrowseButton.this.rebuildSdkListAndSelectSdk(selectedSdk); } else { RandoriSdkComboBoxWithBrowseButton.this.rebuildSdkListAndSelectSdk(null); if (selectedSdk != null) Messages.showErrorDialog(RandoriSdkComboBoxWithBrowseButton.this, "SDK '" + selectedSdk.getName() + "' can not be selected here.\\nPlease select a Randori SDK.", "Select a Randori SDK"); } } } }); } private void rebuildSdkListAndSelectSdk(@Nullable Sdk selectedSdk) { String previousSelectedSdkName = getSelectedSdkRaw(); List<Object> sdkList = new ArrayList<Object>(); if (this.showBCSdk) { sdkList.add(this.bcSdk); } Sdk[] sdks = ProjectJdkTable.getInstance().getAllJdks(); for (Sdk sdk : sdks) { if (this.sdkFilter.value(sdk)) { sdkList.add(sdk); } } if (!sdkList.isEmpty()) { Collections.sort(sdkList, new Comparator<Object>() { public int compare(Object sdk1, Object sdk2) { if ((sdk1 == RandoriSdkComboBoxWithBrowseButton.this.bcSdk) && (sdk2 != RandoriSdkComboBoxWithBrowseButton.this.bcSdk)) return -1; if ((sdk1 != RandoriSdkComboBoxWithBrowseButton.this.bcSdk) && (sdk2 == RandoriSdkComboBoxWithBrowseButton.this.bcSdk)) return 1; if (((sdk1 instanceof Sdk)) && ((sdk2 instanceof Sdk))) { SdkTypeId type1 = ((Sdk)sdk1).getSdkType(); SdkTypeId type2 = ((Sdk)sdk2).getSdkType(); if (type1 == type2) return -StringUtil.compareVersionNumbers(((Sdk)sdk1).getVersionString(), ((Sdk)sdk2).getVersionString()); if (type1 == RandoriSdkType.getInstance()) return -1; if (type2 == RandoriSdkType.getInstance()) return 1; } return 0; } }); getComboBox().setModel(new DefaultComboBoxModel(ArrayUtil.toObjectArray(sdkList))); if (selectedSdk != null) { setSelectedSdkRaw(selectedSdk.getName(), false); } else if (previousSelectedSdkName != null) setSelectedSdkRaw(previousSelectedSdkName, false); } else { getComboBox().setModel(new DefaultComboBoxModel(new Object[] { null })); } } public void addComboboxListener(final Listener listener) { getComboBox().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { listener.stateChanged(); } }); getComboBox().addPropertyChangeListener("model", new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { listener.stateChanged(); } }); } @Nullable public Sdk getSelectedSdk() { Object selectedItem = getComboBox().getSelectedItem(); if ((selectedItem instanceof BCSdk)) { return ((BCSdk)selectedItem).mySdk; } if ((selectedItem instanceof Sdk)) { return (Sdk)selectedItem; } return null; } String getSelectedSdkRaw() { Object selectedItem = getComboBox().getSelectedItem(); if ((selectedItem instanceof BCSdk)) { return "BC SDK"; } if ((selectedItem instanceof Sdk)) { return ((Sdk)selectedItem).getName(); } if ((selectedItem instanceof String)) { return (String)selectedItem; } return ""; } public void setSelectedSdkRaw(String sdkName) { setSelectedSdkRaw(sdkName, true); } private void setSelectedSdkRaw(String sdkName, boolean addErrorItemIfSdkNotFound) { JComboBox combo = getComboBox(); if ("BC SDK".equals(sdkName)) { combo.setSelectedItem(this.bcSdk); return; } for (int i = 0; i < combo.getItemCount(); i++) { Object item = combo.getItemAt(i); if (((item instanceof Sdk)) && (((Sdk)item).getName().equals(sdkName))) { combo.setSelectedItem(item); return; } } if (addErrorItemIfSdkNotFound) { List<Object> items = new ArrayList<Object>(); items.add(sdkName); for (int i = 0; i < combo.getItemCount(); i++) { Object item = combo.getItemAt(i); if (!(item instanceof String)) { items.add(item); } } combo.setModel(new DefaultComboBoxModel(ArrayUtil.toObjectArray(items))); } } public void showBCSdk(boolean showBCSdk) { if (this.showBCSdk != showBCSdk) { this.showBCSdk = showBCSdk; Object selectedItem = getComboBox().getSelectedItem(); rebuildSdkListAndSelectSdk(null); if ((selectedItem instanceof String)) setSelectedSdkRaw((String)selectedItem, true); } } public void setBCSdk(Sdk sdk) { if (sdk != this.bcSdk.mySdk) this.bcSdk.mySdk = sdk; } private static class NonCommittingWrapper extends ProjectSdksModel { private final ProjectSdksModel myOriginal; private final JdkListConfigurable myConfigurable; public NonCommittingWrapper(ProjectSdksModel original, JdkListConfigurable configurable) { this.myOriginal = original; this.myConfigurable = configurable; } public void apply() throws ConfigurationException { apply(null); } public void apply(@Nullable MasterDetailsComponent configurable) throws ConfigurationException { this.myConfigurable.reset(); } public void reset(@Nullable Project project) { } public void addListener(SdkModel.Listener listener) { this.myOriginal.addListener(listener); } public void removeListener(SdkModel.Listener listener) { this.myOriginal.removeListener(listener); } public SdkModel.Listener getMulticaster() { return this.myOriginal.getMulticaster(); } public Sdk[] getSdks() { return this.myOriginal.getSdks(); } public Sdk findSdk(String sdkName) { return this.myOriginal.findSdk(sdkName); } public void disposeUIResources() { } public HashMap<Sdk, Sdk> getProjectSdks() { return this.myOriginal.getProjectSdks(); } public boolean isModified() { return this.myOriginal.isModified(); } public void removeSdk(Sdk editableObject) { this.myOriginal.removeSdk(editableObject); } public void createAddActions(DefaultActionGroup group, JComponent parent, Consumer<Sdk> updateTree, @Nullable Condition<SdkTypeId> filter) { this.myOriginal.createAddActions(group, parent, updateTree, filter); } public void doAdd(JComponent parent, SdkType type, Consumer<Sdk> updateTree) { this.myOriginal.doAdd(parent, type, updateTree); } public void addSdk(Sdk sdk) { this.myOriginal.addSdk(sdk); } public void doAdd(Sdk newSdk, @Nullable Consumer<Sdk> updateTree) { this.myOriginal.doAdd(newSdk, updateTree); } public Sdk findSdk(@Nullable Sdk modelJdk) { return this.myOriginal.findSdk(modelJdk); } public Sdk getProjectSdk() { return this.myOriginal.getProjectSdk(); } public void setProjectSdk(Sdk projectSdk) { this.myOriginal.setProjectSdk(projectSdk); } public boolean isInitialized() { return this.myOriginal.isInitialized(); } } private static class BCSdk { private Sdk mySdk; } public static abstract interface Listener { public abstract void stateChanged(); } }