package com.intellij.lang.javascript.flex.projectStructure.ui;
import com.intellij.flex.FlexCommonUtils;
import com.intellij.flex.model.bc.BuildConfigurationNature;
import com.intellij.flex.model.bc.ComponentSet;
import com.intellij.flex.model.bc.LinkageType;
import com.intellij.flex.model.bc.OutputType;
import com.intellij.lang.javascript.flex.FlexBundle;
import com.intellij.lang.javascript.flex.library.FlexLibraryProperties;
import com.intellij.lang.javascript.flex.library.FlexLibraryType;
import com.intellij.lang.javascript.flex.projectStructure.FlexBCConfigurator;
import com.intellij.lang.javascript.flex.projectStructure.FlexBuildConfigurationsExtension;
import com.intellij.lang.javascript.flex.projectStructure.model.*;
import com.intellij.lang.javascript.flex.projectStructure.model.impl.Factory;
import com.intellij.lang.javascript.flex.projectStructure.model.impl.FlexLibraryIdGenerator;
import com.intellij.lang.javascript.flex.projectStructure.model.impl.FlexProjectConfigurationEditor;
import com.intellij.lang.javascript.flex.projectStructure.options.BCUtils;
import com.intellij.lang.javascript.flex.projectStructure.options.FlexProjectRootsUtil;
import com.intellij.lang.javascript.flex.sdk.FlexSdkType2;
import com.intellij.lang.javascript.flex.sdk.FlexSdkUtils;
import com.intellij.lang.javascript.flex.sdk.FlexmojosSdkType;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectBundle;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.SdkModel;
import com.intellij.openapi.projectRoots.SdkType;
import com.intellij.openapi.projectRoots.SdkTypeId;
import com.intellij.openapi.roots.LibraryOrderEntry;
import com.intellij.openapi.roots.OrderRootType;
import com.intellij.openapi.roots.ProjectModelExternalSource;
import com.intellij.openapi.roots.impl.libraries.LibraryEx;
import com.intellij.openapi.roots.libraries.*;
import com.intellij.openapi.roots.ui.OrderEntryAppearanceService;
import com.intellij.openapi.roots.ui.configuration.JdkComboBox;
import com.intellij.openapi.roots.ui.configuration.LibraryTableModifiableModelProvider;
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
import com.intellij.openapi.roots.ui.configuration.UIRootConfigurationAccessor;
import com.intellij.openapi.roots.ui.configuration.classpath.CreateModuleLibraryChooser;
import com.intellij.openapi.roots.ui.configuration.libraryEditor.EditExistingLibraryDialog;
import com.intellij.openapi.roots.ui.configuration.projectRoot.*;
import com.intellij.openapi.ui.ComboBoxTableRenderer;
import com.intellij.openapi.ui.MasterDetailsComponent;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.NamedConfigurable;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.ui.*;
import com.intellij.ui.components.editors.JBComboBoxTableCellEditorComponent;
import com.intellij.ui.navigation.History;
import com.intellij.ui.navigation.Place;
import com.intellij.util.EventDispatcher;
import com.intellij.util.PlatformIcons;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.FilteringIterator;
import com.intellij.util.ui.AbstractTableCellEditor;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.classpath.ChooseLibrariesFromTablesDialog;
import icons.FlexIcons;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.awt.event.*;
import java.text.MessageFormat;
import java.util.*;
import java.util.List;
public class DependenciesConfigurable extends NamedConfigurable<Dependencies> implements Place.Navigator {
private static final Logger LOG = Logger.getInstance(DependenciesConfigurable.class.getName());
public static final String TAB_NAME = FlexBundle.message("bc.tab.dependencies.display.name");
private static final Icon MISSING_BC_ICON = null;
public static abstract class Location {
public final String errorId;
protected Location(final String id) {
errorId = id;
}
public static final Location SDK = new Location("sdk") {
};
public static class TableEntry extends Location {
private final String locationString;
private TableEntry(final String locationString) {
super(locationString);
this.locationString = locationString;
}
public static TableEntry forSdkRoot(final String url) {
return new TableEntry("sdkroot\t" + url);
}
public static TableEntry forSharedLibrary(final String libraryLevel, final String libraryName) {
return new TableEntry("sharedlib\t" + libraryLevel + "\t" + libraryName);
}
public static TableEntry forSharedLibrary(final Library liveLibrary) {
return new TableEntry("sharedlib\t" + liveLibrary.getTable().getTableLevel() + "\t" + liveLibrary.getName());
}
public static TableEntry forModuleLibrary(final String libraryId) {
return new TableEntry("modulelib\t" + libraryId);
}
public static TableEntry forBc(FlexBCConfigurable configurable) {
return new TableEntry("bc\t" + configurable.getModuleName() + "\t" + configurable.getDisplayName());
}
public static TableEntry forBc(String moduleName, String bcName) {
return new TableEntry("bc\t" + moduleName + "\t" + bcName);
}
public static final TableEntry SDK_ENTRY = new TableEntry("sdk_entry");
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final TableEntry that = (TableEntry)o;
if (!locationString.equals(that.locationString)) return false;
return true;
}
@Override
public int hashCode() {
return locationString.hashCode();
}
}
}
private JPanel myMainPanel;
private JdkComboBox mySdkCombo;
private JLabel myTargetPlayerLabel;
private JComboBox myTargetPlayerCombo;
private JLabel myTargetPlayerWarning;
private JLabel myComponentSetLabel;
private JComboBox myComponentSetCombo;
private JLabel myFrameworkLinkageLabel;
private JComboBox myFrameworkLinkageCombo;
private JLabel myWarning;
private JPanel myTablePanel;
private JButton myNewButton;
private JButton myEditButton;
private JLabel mySdkLabel;
private final EditableTreeTable<MyTableItem> myTable;
private final Project myProject;
private final ModifiableDependencies myDependencies;
private AddItemPopupAction[] myPopupActions;
private final Disposable myDisposable;
private final BuildConfigurationNature myNature;
private final FlexProjectConfigurationEditor myConfigEditor;
private final ProjectSdksModel mySkdsModel;
private boolean myFreeze;
private final EventDispatcher<ChangeListener> mySdkChangeDispatcher;
private final EventDispatcher<UserActivityListener> myUserActivityDispatcher;
private boolean myReset;
private abstract static class MyTableItem {
@Nullable
public Icon getIcon() {
return PlatformIcons.LIBRARY_ICON;
}
public boolean showLinkage() {
return true;
}
public abstract boolean isLinkageEditable();
public boolean isANE() {
return false;
}
public abstract LinkageType getLinkageType();
public abstract void setLinkageType(LinkageType linkageType);
public void onDoubleClick() {
}
@Nullable
public ModifiableDependencyEntry apply(ModifiableDependencies dependencies) {
return null;
}
public boolean isModified(DependencyEntry entry) {
return false;
}
public boolean canEdit() {
return false;
}
public abstract Location.TableEntry getLocation();
public abstract SimpleColoredText getPresentableText();
}
private class BCItem extends MyTableItem {
public final ModifiableDependencyType dependencyType = Factory.createDependencyTypeInstance();
public final FlexBCConfigurable configurable;
public final String moduleName;
public final String bcName;
public BCItem(@NotNull String moduleName, @NotNull String bcName) {
this.moduleName = moduleName;
this.bcName = bcName;
this.configurable = null;
}
public BCItem(@NotNull FlexBCConfigurable configurable) {
this.moduleName = null;
this.bcName = null;
this.configurable = configurable;
if (configurable.getOutputType() != OutputType.Library) {
dependencyType.setLinkageType(LinkageType.LoadInRuntime);
}
}
@Override
public SimpleColoredText getPresentableText() {
if (configurable != null) {
return BCUtils.renderBuildConfiguration(configurable.getEditableObject(), configurable.getModuleName());
}
else {
return BCUtils.renderMissingBuildConfiguration(bcName, moduleName);
}
}
@Nullable
@Override
public Icon getIcon() {
return configurable != null ? configurable.getIcon() : MISSING_BC_ICON;
}
@Override
public boolean showLinkage() {
return configurable != null;
}
@Override
public boolean isLinkageEditable() {
return configurable != null && configurable.getOutputType() == OutputType.Library;
}
@Override
public LinkageType getLinkageType() {
return dependencyType.getLinkageType();
}
@Override
public void setLinkageType(LinkageType linkageType) {
dependencyType.setLinkageType(linkageType);
}
public void onDoubleClick() {
if (configurable != null) {
Project project = configurable.getModule().getProject();
ProjectStructureConfigurable.getInstance(project).navigateTo(FlexProjectStructureUtil.createPlace(configurable, null), true);
}
}
public ModifiableDependencyEntry apply(ModifiableDependencies dependencies) {
ModifiableDependencyEntry entry;
if (configurable != null) {
// configurable may be not yet applied at the moment
entry = myConfigEditor.createBcEntry(dependencies, configurable.getEditableObject(), configurable.getDisplayName());
}
else {
entry = myConfigEditor.createBcEntry(dependencies, moduleName, bcName);
}
entry.getDependencyType().copyFrom(dependencyType);
return entry;
}
public boolean isModified(final DependencyEntry entry) {
if (!(entry instanceof BuildConfigurationEntry)) {
return true;
}
BuildConfigurationEntry bcEntry = (BuildConfigurationEntry)entry;
if (configurable != null) {
if (configurable.getModule() != bcEntry.findModule()) return true;
if (!configurable.getDisplayName().equals(bcEntry.getBcName())) return true;
}
else {
if (!moduleName.equals(bcEntry.getModuleName())) return true;
if (!bcName.equals(bcEntry.getBcName())) return true;
}
if (!dependencyType.isEqual(entry.getDependencyType())) return true;
return false;
}
public boolean canEdit() {
return false;
}
@Override
public Location.TableEntry getLocation() {
return configurable != null ? Location.TableEntry.forBc(configurable) : Location.TableEntry.forBc(moduleName, bcName);
}
}
private class ModuleLibraryItem extends MyTableItem {
public final ModifiableDependencyType dependencyType = Factory.createDependencyTypeInstance();
public final String libraryId;
@Nullable
public final LibraryOrderEntry orderEntry;
private final Project project;
public ModuleLibraryItem(@NotNull String libraryId, @Nullable LibraryOrderEntry orderEntry, @NotNull Project project) {
this.libraryId = libraryId;
this.orderEntry = orderEntry;
this.project = project;
}
@Override
public SimpleColoredText getPresentableText() {
if (orderEntry != null) {
Library library = orderEntry.getLibrary();
if (library != null) {
if (((LibraryEx)library).isDisposed()) {
Pair<String, String> moduleAndBcName = getModuleAndBcName();
LOG.error("Module library '" +
library.getName() +
"' is disposed, used in BC: " +
moduleAndBcName.second +
" of module " +
moduleAndBcName.first);
return new SimpleColoredText("<unknown>", SimpleTextAttributes.ERROR_ATTRIBUTES);
}
boolean hasInvalidRoots = !((LibraryEx)library).getInvalidRootUrls(OrderRootType.CLASSES).isEmpty();
String text = OrderEntryAppearanceService.getInstance().forLibrary(project, library, hasInvalidRoots).getText();
return new SimpleColoredText(text, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
}
return new SimpleColoredText("<unknown>", SimpleTextAttributes.ERROR_ATTRIBUTES);
}
@Override
public boolean isLinkageEditable() {
return !isANE();
}
public boolean isANE() {
final Library library = orderEntry == null ? null : orderEntry.getLibrary();
final VirtualFile[] files = library == null ? VirtualFile.EMPTY_ARRAY : library.getFiles(OrderRootType.CLASSES);
for (VirtualFile file : files) {
if ("ane".equalsIgnoreCase(file.getExtension())) return true;
}
return false;
}
@Override
public LinkageType getLinkageType() {
return dependencyType.getLinkageType();
}
@Override
public void setLinkageType(LinkageType linkageType) {
dependencyType.setLinkageType(linkageType);
}
public void onDoubleClick() {
if (canEdit()) {
editLibrary(this);
}
}
public ModifiableDependencyEntry apply(final ModifiableDependencies dependencies) {
ModifiableDependencyEntry entry = myConfigEditor.createModuleLibraryEntry(dependencies, libraryId);
entry.getDependencyType().copyFrom(dependencyType);
return entry;
}
public boolean isModified(final DependencyEntry entry) {
if (!(entry instanceof ModuleLibraryEntry)) {
return true;
}
ModuleLibraryEntry libraryEntry = (ModuleLibraryEntry)entry;
if (!libraryEntry.getLibraryId().equals(libraryId)) {
return true;
}
if (!dependencyType.isEqual(entry.getDependencyType())) return true;
return false;
}
public boolean canEdit() {
return orderEntry != null;
}
@Override
public Location.TableEntry getLocation() {
return Location.TableEntry.forModuleLibrary(libraryId);
}
}
private class SharedLibraryItem extends MyTableItem {
public final ModifiableDependencyType dependencyType = Factory.createDependencyTypeInstance();
public final String libraryName;
public final String libraryLevel;
@Nullable
public final Library liveLibrary;
private final Project project;
public SharedLibraryItem(@NotNull String libraryName,
@NotNull String libraryLevel,
@Nullable Library liveLibrary,
@NotNull Project project) {
this.libraryName = libraryName;
this.libraryLevel = libraryLevel;
this.liveLibrary = liveLibrary;
this.project = project;
}
@Override
public SimpleColoredText getPresentableText() {
Library liveLibrary = findLiveLibrary();
if (liveLibrary != null) {
String text = OrderEntryAppearanceService.getInstance().forLibrary(project, liveLibrary, false).getText();
return new SimpleColoredText(text, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
else {
return new SimpleColoredText(libraryName, SimpleTextAttributes.ERROR_ATTRIBUTES);
}
}
@Nullable
public Library findLiveLibrary() {
// TODO call myConfigEditor.findLiveLibrary(library, libraryName, libraryLevel);
return new UIRootConfigurationAccessor(project).getLibrary(liveLibrary, libraryName, libraryLevel);
}
@Override
public boolean isLinkageEditable() {
return !isANE();
}
public boolean isANE() {
final VirtualFile[] files = liveLibrary == null ? VirtualFile.EMPTY_ARRAY : liveLibrary.getFiles(OrderRootType.CLASSES);
for (VirtualFile file : files) {
if ("ane".equalsIgnoreCase(file.getExtension())) return true;
}
return false;
}
@Override
public LinkageType getLinkageType() {
return dependencyType.getLinkageType();
}
@Override
public void setLinkageType(LinkageType linkageType) {
dependencyType.setLinkageType(linkageType);
}
public void onDoubleClick() {
editLibrary(this);
}
public ModifiableDependencyEntry apply(final ModifiableDependencies dependencies) {
ModifiableDependencyEntry entry;
Library liveLibrary = findLiveLibrary();
if (liveLibrary != null) {
entry = myConfigEditor.createSharedLibraryEntry(dependencies, liveLibrary.getName(), liveLibrary.getTable().getTableLevel());
}
else {
entry = myConfigEditor.createSharedLibraryEntry(dependencies, libraryName, libraryLevel);
}
entry.getDependencyType().copyFrom(dependencyType);
return entry;
}
public boolean isModified(final DependencyEntry entry) {
if (!(entry instanceof ModifiableSharedLibraryEntry)) {
return true;
}
ModifiableSharedLibraryEntry libraryEntry = (ModifiableSharedLibraryEntry)entry;
Library liveLibrary = findLiveLibrary();
if (liveLibrary != null) {
if (!liveLibrary.getName().equals(libraryEntry.getLibraryName())) return true;
if (!liveLibrary.getTable().getTableLevel().equals(libraryEntry.getLibraryLevel())) return true;
}
else {
if (!libraryName.equals(libraryEntry.getLibraryName())) return true;
if (!libraryLevel.equals(libraryEntry.getLibraryLevel())) return true;
}
if (!dependencyType.isEqual(entry.getDependencyType())) return true;
return false;
}
public boolean canEdit() {
return true;
}
@Override
public Location.TableEntry getLocation() {
Library liveLibrary = findLiveLibrary();
return liveLibrary != null
? Location.TableEntry.forSharedLibrary(liveLibrary)
: Location.TableEntry.forSharedLibrary(libraryLevel, libraryName);
}
}
private static class SdkItem extends MyTableItem {
private final Sdk mySdk;
private final SdkType mySdkType;
public SdkItem(Sdk sdk) {
mySdk = sdk;
mySdkType = (SdkType)sdk.getSdkType();
}
@Override
public SimpleColoredText getPresentableText() {
SimpleColoredText text = new SimpleColoredText();
final String sdkVersion = StringUtil.notNullize(mySdk.getVersionString(), FlexBundle.message("flex.sdk.version.unknown"));
if (sdkVersion.startsWith(FlexCommonUtils.AIR_SDK_VERSION_PREFIX)) {
text.append(sdkVersion, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
else {
text.append("Flex SDK " + sdkVersion, SimpleTextAttributes.REGULAR_ATTRIBUTES);
final String airSdkVersion = FlexCommonUtils.getVersionOfAirSdkIncludedInFlexSdk(mySdk.getHomePath());
if (airSdkVersion != null) {
text.append(", AIR SDK " + airSdkVersion, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
}
return text;
}
@Override
public Icon getIcon() {
return mySdkType.getIcon();
}
@Override
public boolean showLinkage() {
return false;
}
@Override
public LinkageType getLinkageType() {
return null;
}
@Override
public boolean isLinkageEditable() {
return false;
}
@Override
public void setLinkageType(LinkageType linkageType) {
throw new UnsupportedOperationException();
}
@Override
public Location.TableEntry getLocation() {
return Location.TableEntry.SDK_ENTRY;
}
}
private static class SdkEntryItem extends MyTableItem {
private final String url;
private final LinkageType linkageType;
private SdkEntryItem(String url, LinkageType linkageType) {
this.url = url;
this.linkageType = linkageType;
}
@Override
public SimpleColoredText getPresentableText() {
return new SimpleColoredText(url, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
@Override
public boolean isLinkageEditable() {
return false;
}
@Override
public LinkageType getLinkageType() {
return linkageType;
}
@Override
public void setLinkageType(LinkageType linkageType) {
throw new UnsupportedOperationException();
}
@Override
public Location.TableEntry getLocation() {
return Location.TableEntry.forSdkRoot(url);
}
}
private static final TableCellRenderer LINKAGE_TYPE_RENDERER = new DefaultTableCellRenderer() {
private ComboBoxTableRenderer<LinkageType> myComboBoxTableRenderer = new ComboBoxTableRenderer<>(null);
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
final Object tableItem = ((EditableTreeTable)table).getItemAt(row);
if (tableItem instanceof MyTableItem && ((MyTableItem)tableItem).isLinkageEditable()) {
myComboBoxTableRenderer.setFont(table.getFont());
return myComboBoxTableRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
else {
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
};
private static final DefaultTableCellRenderer ANE_RENDERER = new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
final JLabel component = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
component.setText("ANE");
return component;
}
};
private static final DefaultTableCellRenderer EMPTY_RENDERER = new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
final JLabel component = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
component.setText("");
return component;
}
};
private static final AbstractTableCellEditor LINKAGE_TYPE_EDITOR = new AbstractTableCellEditor() {
private JBComboBoxTableCellEditorComponent myCombo;
public Object getCellEditorValue() {
return myCombo.getEditorValue();
}
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
myCombo = new JBComboBoxTableCellEditorComponent(table);
myCombo.setCell(table, row, column);
myCombo.setOptions(LinkageType.getSwcLinkageValues());
myCombo.setDefaultValue(value);
myCombo.setToString(o -> ((LinkageType)o).getShortText());
return myCombo;
}
};
private static final ColumnInfo<MyTableItem, LinkageType> DEPENDENCY_TYPE_COLUMN = new ColumnInfo<MyTableItem, LinkageType>("Linkage") {
@Override
public LinkageType valueOf(MyTableItem item) {
return item.getLinkageType();
}
@Override
public void setValue(MyTableItem item, LinkageType linkageType) {
item.setLinkageType(linkageType);
}
@Override
public TableCellRenderer getRenderer(MyTableItem item) {
return item.showLinkage() ? item.isANE() ? ANE_RENDERER
: LINKAGE_TYPE_RENDERER
: EMPTY_RENDERER;
}
@Override
public TableCellEditor getEditor(MyTableItem item) {
return LINKAGE_TYPE_EDITOR;
}
@Override
public boolean isCellEditable(MyTableItem item) {
return item.isLinkageEditable();
}
@Override
public int getWidth(JTable table) {
return new JLabel(LinkageType.External.getShortText()).getPreferredSize().width + 20;
}
};
public DependenciesConfigurable(final ModifiableFlexBuildConfiguration bc,
Project project,
@NotNull FlexProjectConfigurationEditor configEditor,
final ProjectSdksModel sdksModel) {
mySkdsModel = sdksModel;
myConfigEditor = configEditor;
myDependencies = bc.getDependencies();
myProject = project;
myNature = bc.getNature();
mySdkChangeDispatcher = EventDispatcher.create(ChangeListener.class);
myDisposable = Disposer.newDisposable();
final SdkModel.Listener listener = new SdkModel.Listener() {
public void sdkAdded(final Sdk sdk) {
rebuildSdksModel();
}
public void beforeSdkRemove(final Sdk sdk) {
rebuildSdksModel();
}
public void sdkChanged(final Sdk sdk, final String previousName) {
rebuildSdksModel();
}
public void sdkHomeSelected(final Sdk sdk, final String newSdkHome) {
rebuildSdksModel();
}
};
sdksModel.addListener(listener);
Disposer.register(myDisposable, new Disposable() {
public void dispose() {
sdksModel.removeListener(listener);
}
});
mySdkCombo.setSetupButton(myNewButton, myProject, sdksModel, new JdkComboBox.NoneJdkComboBoxItem(), null,
FlexBundle.message("set.up.sdk.title"));
mySdkCombo.setEditButton(myEditButton, myProject, (NullableComputable<Sdk>)() -> mySdkCombo.getSelectedJdk());
mySdkLabel.setLabelFor(mySdkCombo);
mySdkCombo.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
if (myFreeze) {
return;
}
updateOnSelectedSdkChange();
}
});
myComponentSetCombo.setModel(new DefaultComboBoxModel(ComponentSet.values()));
myComponentSetCombo.setRenderer(new ListCellRendererWrapper<ComponentSet>() {
public void customize(JList list, ComponentSet value, int index, boolean selected, boolean hasFocus) {
setText(value.getPresentableText());
}
});
myFrameworkLinkageCombo
.setRenderer(new ListCellRendererWrapper<LinkageType>() {
public void customize(JList list, LinkageType value, int index, boolean selected, boolean hasFocus) {
if (value == LinkageType.Default) {
final Sdk sdk = mySdkCombo.getSelectedJdk();
final String sdkVersion = sdk != null ? sdk.getVersionString() : null;
setText(sdkVersion == null
? "Default"
: MessageFormat
.format("Default ({0})", FlexCommonUtils.getDefaultFrameworkLinkage(sdkVersion, myNature).getLongText()));
}
else {
setText(value.getLongText());
}
}
});
myFrameworkLinkageCombo.setModel(new DefaultComboBoxModel(BCUtils.getSuitableFrameworkLinkages(myNature)));
ItemListener updateSdkItemsListener = new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (myFreeze) {
return;
}
DefaultMutableTreeNode sdkNode = findSdkNode();
Sdk currentSdk = mySdkCombo.getSelectedJdk();
if (sdkNode != null && currentSdk != null) {
updateSdkEntries(sdkNode, currentSdk);
myTable.refresh();
}
}
};
myTargetPlayerCombo.addItemListener(updateSdkItemsListener);
myComponentSetCombo.addItemListener(updateSdkItemsListener);
myFrameworkLinkageCombo.addItemListener(updateSdkItemsListener);
myTargetPlayerWarning.setIcon(FlexIcons.Flex.SmallWarning);
myWarning.setIcon(UIUtil.getBalloonWarningIcon());
myTable = new EditableTreeTable<MyTableItem>("", DEPENDENCY_TYPE_COLUMN) {
@Override
protected void render(SimpleColoredComponent c, MyTableItem item) {
if (item != null) {
item.getPresentableText().appendToComponent(c);
c.setIcon(item.getIcon());
}
}
};
myTable.setRootVisible(false);
myTable.getTree().setShowsRootHandles(true);
myTable.getTree().setLineStyleAngled();
myTablePanel.add(
ToolbarDecorator.createDecorator(myTable)
.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
addItem(button);
}
}).setAddActionName(FlexBundle.message("add.dependency.action.name"))
.setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
removeSelection();
}
}).setEditAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton button) {
MyTableItem item = myTable.getItemAt(myTable.getSelectedRow());
if (item instanceof SharedLibraryItem) {
editLibrary((SharedLibraryItem)item);
}
if (item instanceof ModuleLibraryItem) {
editLibrary(((ModuleLibraryItem)item));
}
}
}).setRemoveActionUpdater(new AnActionButtonUpdater() {
@Override
public boolean isEnabled(AnActionEvent e) {
if (myTable.getSelectedRowCount() == 0) return false;
for (int row : myTable.getSelectedRows()) {
MyTableItem item = myTable.getItemAt(row);
if (item instanceof SdkItem || item instanceof SdkEntryItem) return false;
}
return true;
}
}).setEditActionUpdater(new AnActionButtonUpdater() {
@Override
public boolean isEnabled(AnActionEvent e) {
MyTableItem item = myTable.getItemAt(myTable.getSelectedRow());
return item != null && item.canEdit();
}
}).disableUpDownActions().createPanel(), BorderLayout.CENTER);
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent e) {
if (myTable.getSelectedRowCount() == 1) {
myTable.getItemAt(myTable.getSelectedRow()).onDoubleClick();
return true;
}
return false;
}
}.installOn(myTable);
FlexBuildConfigurationsExtension.getInstance().getConfigurator().addListener(new FlexBCConfigurator.Listener() {
@Override
public void moduleRemoved(Module module) {
// TODO return if module == this module
Set<MyTableItem> itemsToRemove = new HashSet<>();
// 1st-level nodes are always visible
// 2nd-level nodes cannot refer to BC
for (int row = 0; row < myTable.getRowCount(); row++) {
MyTableItem item = myTable.getItemAt(row);
if (item instanceof BCItem) {
FlexBCConfigurable configurable = ((BCItem)item).configurable;
if (configurable != null && configurable.getModule() == module) {
itemsToRemove.add(item);
}
}
}
removeItems(itemsToRemove, true);
}
@Override
public void buildConfigurationRemoved(FlexBCConfigurable configurable) {
Pair<BCItem, Integer> item = findDependencyItem(configurable);
if (item != null) {
removeItems(Collections.singleton(item.first), true);
}
}
@Override
public void buildConfigurationRenamed(final FlexBCConfigurable configurable) {
Pair<BCItem, Integer> item = findDependencyItem(configurable);
if (item != null) {
myTable.refreshItemAt(item.second);
}
}
public void natureChanged(final FlexBCConfigurable configurable) {
Pair<BCItem, Integer> item = findDependencyItem(configurable);
if (item != null) {
final BuildConfigurationNature dependencyNature = item.first.configurable.getEditableObject().getNature();
if (!FlexCommonUtils.checkDependencyType(myNature.outputType, dependencyNature.outputType, item.first.getLinkageType())) {
removeItems(Collections.singleton(item.first), true);
}
}
}
@Nullable
private Pair<BCItem, Integer> findDependencyItem(FlexBCConfigurable configurable) {
if (configurable.isParentFor(DependenciesConfigurable.this)) {
return null;
}
// 1st-level nodes are always visible
// 2nd-level nodes cannot refer to BC
for (int row = 0; row < myTable.getRowCount(); row++) {
final MyTableItem item = myTable.getItemAt(row);
if (item instanceof BCItem && ((BCItem)item).configurable == configurable) {
// there may be only one dependency on a BC
return Pair.create((BCItem)item, row);
}
}
return null;
}
}, myDisposable);
myConfigEditor.addModulesModelChangeListener(new FlexProjectConfigurationEditor.ModulesModelChangeListener() {
@Override
public void modulesModelsChanged(Collection<Module> modules) {
FlexBCConfigurator configurator = FlexBuildConfigurationsExtension.getInstance().getConfigurator();
for (Module module : modules) {
for (CompositeConfigurable configurable : configurator.getBCConfigurables(module)) {
FlexBCConfigurable flexBCConfigurable = FlexBCConfigurable.unwrap(configurable);
if (flexBCConfigurable.isParentFor(DependenciesConfigurable.this)) {
resetTable(myDependencies.getSdkEntry(), true);
}
}
}
}
}, myDisposable);
UserActivityWatcher watcher = new UserActivityWatcher();
watcher.register(myMainPanel);
myUserActivityDispatcher = EventDispatcher.create(UserActivityListener.class);
watcher.addUserActivityListener(new UserActivityListener() {
@Override
public void stateChanged() {
if (myFreeze) {
return;
}
myUserActivityDispatcher.getMulticaster().stateChanged();
}
}, myDisposable);
}
private void rebuildSdksModel() {
final JdkComboBox.JdkComboBoxItem selectedItem = mySdkCombo.getSelectedItem();
JdkComboBox.NoneJdkComboBoxItem noneSdkItem = new JdkComboBox.NoneJdkComboBoxItem();
myFreeze = true;
try {
mySdkCombo.reloadModel(noneSdkItem, myProject);
}
finally {
myFreeze = false;
}
if (selectedItem instanceof JdkComboBox.NoneJdkComboBoxItem) {
mySdkCombo.setSelectedItem(noneSdkItem);
}
else {
String selectedSdkName = selectedItem.getSdkName();
if (selectedSdkName != null) {
Sdk sdk = mySkdsModel.findSdk(selectedSdkName);
if (sdk != null) {
mySdkCombo.setSelectedJdk(sdk);
}
else {
mySdkCombo.setInvalidJdk(selectedSdkName);
}
}
else {
mySdkCombo.setSelectedItem(noneSdkItem);
}
}
if (mySdkCombo.getSelectedJdk() != selectedItem.getJdk()) {
updateOnSelectedSdkChange();
}
mySdkChangeDispatcher.getMulticaster().stateChanged(new ChangeEvent(this));
}
@Nullable
Sdk getCurrentSdk() {
return mySdkCombo.getSelectedJdk();
}
ComponentSet getCurrentComponentSet() {
return (ComponentSet)myComponentSetCombo.getSelectedItem();
}
void addSdkChangeListener(final ChangeListener changeListener) {
mySdkChangeDispatcher.addListener(changeListener);
}
@Nullable
private DefaultMutableTreeNode findSdkNode() {
for (int row = 0; row < myTable.getRowCount(); row++) {
MyTableItem item = myTable.getItemAt(row);
if (myTable.getItemAt(row) instanceof SdkItem) {
if (item instanceof SdkItem) {
return (DefaultMutableTreeNode)myTable.getRoot().getChildAt(row);
}
}
}
return null;
}
private void editLibrary(final SharedLibraryItem item) {
final Library liveLibrary = item.findLiveLibrary();
if (liveLibrary != null) {
final BaseLibrariesConfigurable librariesConfigurable =
LibraryTablesRegistrar.APPLICATION_LEVEL.equals(liveLibrary.getTable().getTableLevel())
? GlobalLibrariesConfigurable.getInstance(myProject)
: ProjectLibrariesConfigurable.getInstance(myProject);
final Place place = new Place()
.putPath(ProjectStructureConfigurable.CATEGORY, librariesConfigurable)
.putPath(MasterDetailsComponent.TREE_OBJECT, liveLibrary);
ProjectStructureConfigurable.getInstance(myProject).navigateTo(place, true);
}
}
private void editLibrary(ModuleLibraryItem item) {
if (!item.canEdit()) return;
final LibraryOrderEntry entry = item.orderEntry;
assert entry != null;
Library library = entry.getLibrary();
if (library == null) {
return;
}
LibraryTablePresentation presentation = new LibraryTablePresentation() {
@Override
public String getDisplayName(boolean plural) {
return FlexBundle.message(plural ? "library.editor.title.plural" : "library.editor.title.singular");
}
@Override
public String getDescription() {
return ProjectBundle.message("libraries.node.text.module");
}
@Override
public String getLibraryTableEditorTitle() {
return "Configure Library"; // not used as far as I see
}
};
LibraryTableModifiableModelProvider provider = new LibraryTableModifiableModelProvider() {
public LibraryTable.ModifiableModel getModifiableModel() {
return myConfigEditor.getLibraryModel(myDependencies);
}
};
StructureConfigurableContext context = ModuleStructureConfigurable.getInstance(myProject).getContext();
EditExistingLibraryDialog dialog =
EditExistingLibraryDialog.createDialog(myMainPanel, provider, library, myProject, presentation, context);
dialog.setContextModule(myConfigEditor.getModule(myDependencies));
dialog.show();
myTable.refresh();
}
private void addItem(AnActionButton button) {
initPopupActions();
final JBPopup popup = JBPopupFactory.getInstance().createListPopup(
new BaseListPopupStep<AddItemPopupAction>(FlexBundle.message("add.dependency.popup.title"), myPopupActions) {
@Override
public Icon getIconFor(AddItemPopupAction aValue) {
return aValue.getIcon();
}
@Override
public boolean hasSubstep(AddItemPopupAction selectedValue) {
return selectedValue.hasSubStep();
}
public boolean isMnemonicsNavigationEnabled() {
return true;
}
public PopupStep onChosen(final AddItemPopupAction selectedValue, final boolean finalChoice) {
if (selectedValue.hasSubStep()) {
return selectedValue.createSubStep();
}
return doFinalStep(() -> selectedValue.run());
}
@NotNull
public String getTextFor(AddItemPopupAction value) {
return "&" + value.getIndex() + " " + value.getTitle();
}
});
popup.show(button.getPreferredPopupPoint());
}
private void removeSelection() {
TableUtil.stopEditing(myTable);
int[] selectedRows = myTable.getSelectedRows();
Set<MyTableItem> itemsToRemove = new HashSet<>(selectedRows.length);
for (int row : selectedRows) {
itemsToRemove.add(myTable.getItemAt(row));
}
removeItems(itemsToRemove, true);
if (myTable.getRowCount() > 0) {
int toSelect = Math.min(myTable.getRowCount() - 1, selectedRows[0]);
myTable.clearSelection();
myTable.getSelectionModel().addSelectionInterval(toSelect, toSelect);
}
}
private void removeItems(Set<? extends MyTableItem> itemsToDelete, boolean refresh) {
if (itemsToDelete.isEmpty()) return;
DefaultMutableTreeNode root = myTable.getRoot();
for (int i = 0; i < root.getChildCount(); ) {
Object item = ((DefaultMutableTreeNode)root.getChildAt(i)).getUserObject();
if (itemsToDelete.contains(((MyTableItem)item))) {
root.remove(i);
}
else {
i++;
}
}
if (refresh) {
myTable.refresh();
}
}
private void moveSelection(int delta) {
// TODO check the case when SDK item is expanded!
int[] selectedRows = myTable.getSelectedRows();
Arrays.sort(selectedRows);
DefaultMutableTreeNode root = myTable.getRoot();
if (delta < 0) {
for (int i = 0; i < selectedRows.length; i++) {
int row = selectedRows[i];
DefaultMutableTreeNode child = (DefaultMutableTreeNode)root.getChildAt(row);
root.remove(row);
root.insert(child, row + delta);
}
}
else {
for (int i = selectedRows.length - 1; i >= 0; i--) {
int row = selectedRows[i];
DefaultMutableTreeNode child = (DefaultMutableTreeNode)root.getChildAt(row);
root.remove(row);
root.insert(child, row + delta);
}
}
myTable.refresh();
myTable.clearSelection();
for (int selectedRow : selectedRows) {
myTable.getSelectionModel().addSelectionInterval(selectedRow + delta, selectedRow + delta);
}
}
@Nls
public String getDisplayName() {
return TAB_NAME;
}
public void setDisplayName(final String name) {
}
public String getBannerSlogan() {
return getDisplayName();
}
public Dependencies getEditableObject() {
return myDependencies;
}
public String getHelpTopic() {
return "BuildConfigurationPage.Dependencies";
}
public JComponent createOptionsPanel() {
return myMainPanel;
}
public boolean isModified() {
final JdkComboBox.JdkComboBoxItem selectedItem = mySdkCombo.getSelectedItem();
String currentSdkName = selectedItem.getSdkName();
SdkEntry sdkEntry = myDependencies.getSdkEntry();
if (currentSdkName != null) {
if (sdkEntry == null) {
return mySdkCombo.getSelectedJdk() != null;
}
else if (!currentSdkName.equals(sdkEntry.getName())) {
return true;
}
}
else {
if (sdkEntry != null) return true;
}
final String targetPlayer = (String)myTargetPlayerCombo.getSelectedItem();
if (myTargetPlayerCombo.isVisible() && targetPlayer != null && !myDependencies.getTargetPlayer().equals(targetPlayer)) return true;
if (myComponentSetCombo.isVisible() && myDependencies.getComponentSet() != myComponentSetCombo.getSelectedItem()) return true;
if (myFrameworkLinkageCombo.isVisible() && myDependencies.getFrameworkLinkage() != myFrameworkLinkageCombo.getSelectedItem()) {
return true;
}
List<MyTableItem> items = myTable.getItems();
items = ContainerUtil.filter(items, item -> !(item instanceof SdkItem || item instanceof SdkEntryItem));
DependencyEntry[] entries = myDependencies.getEntries();
if (items.size() != entries.length) return true;
for (int i = 0; i < items.size(); i++) {
MyTableItem item = items.get(i);
DependencyEntry entry = entries[i];
if (item.isModified(entry)) return true;
}
return false;
}
public void apply() throws ConfigurationException {
final Object targetPlayer = myTargetPlayerCombo.getSelectedItem();
if (myTargetPlayerCombo.isVisible() && targetPlayer != null) {
myDependencies.setTargetPlayer((String)targetPlayer);
}
if (myComponentSetCombo.isVisible()) {
myDependencies.setComponentSet((ComponentSet)myComponentSetCombo.getSelectedItem());
}
if (myFrameworkLinkageCombo.isVisible()) {
myDependencies.setFrameworkLinkage((LinkageType)myFrameworkLinkageCombo.getSelectedItem());
}
List<MyTableItem> items = myTable.getItems();
List<ModifiableDependencyEntry> newEntries = new ArrayList<>();
for (MyTableItem item : items) {
ModifiableDependencyEntry entry = item.apply(myDependencies);
if (entry != null) {
newEntries.add(entry);
}
}
myConfigEditor.setEntries(myDependencies, newEntries);
JdkComboBox.JdkComboBoxItem currentSdk = mySdkCombo.getSelectedItem();
if (currentSdk != null) {
final String sdkName = currentSdk.getSdkName();
if (sdkName != null) {
SdkEntry sdkEntry = Factory.createSdkEntry(sdkName);
myDependencies.setSdkEntry(sdkEntry);
}
else {
myDependencies.setSdkEntry(null);
}
}
else {
myDependencies.setSdkEntry(null);
}
}
public void reset() {
myReset = true;
SdkEntry sdkEntry = myDependencies.getSdkEntry();
myFreeze = true;
try {
mySdkCombo.reloadModel(new JdkComboBox.NoneJdkComboBoxItem(), myProject);
if (sdkEntry != null) {
final Sdk sdk = mySkdsModel.findSdk(sdkEntry.getName());
if (sdk != null && (sdk.getSdkType() == FlexSdkType2.getInstance() || sdk.getSdkType() == FlexmojosSdkType.getInstance())) {
// technically, non-flex item won't appear in SDK combo model anyway
mySdkCombo.setSelectedJdk(sdk);
}
else {
mySdkCombo.setInvalidJdk(sdkEntry.getName());
}
}
else {
mySdkCombo.setSelectedJdk(null);
}
BCUtils.updateAvailableTargetPlayers(mySdkCombo.getSelectedJdk(), myTargetPlayerCombo);
myTargetPlayerCombo.setSelectedItem(myDependencies.getTargetPlayer());
overriddenTargetPlayerChanged(null); // no warning initially
updateComponentSetCombo();
myComponentSetCombo.setSelectedItem(myDependencies.getComponentSet());
myFrameworkLinkageCombo.setSelectedItem(myDependencies.getFrameworkLinkage());
resetTable(sdkEntry, false);
updateControls();
}
finally {
myFreeze = false;
}
}
private void updateControls() {
final Sdk sdk = mySdkCombo.getSelectedJdk();
final boolean flexmojos = sdk != null && sdk.getSdkType() instanceof FlexmojosSdkType;
myTargetPlayerLabel.setVisible(myNature.isWebPlatform() && !flexmojos);
myTargetPlayerCombo.setVisible(myNature.isWebPlatform() && !flexmojos);
if (!myTargetPlayerCombo.isVisible()) {
myTargetPlayerWarning.setVisible(false);
myWarning.setVisible(false);
}
final boolean airSdk = FlexSdkUtils.isAirSdkWithoutFlex(sdk);
final boolean visible = sdk != null && !flexmojos && !myNature.isMobilePlatform() && !myNature.pureAS && !airSdk &&
StringUtil.compareVersionNumbers(sdk.getVersionString(), "4") >= 0;
myComponentSetLabel.setVisible(visible);
myComponentSetCombo.setVisible(visible);
myFrameworkLinkageLabel.setVisible(!myNature.pureAS && !flexmojos && !airSdk);
myFrameworkLinkageCombo.setVisible(!myNature.pureAS && !flexmojos && !airSdk);
}
private void resetTable(SdkEntry sdkEntry, boolean keepSelection) {
int[] selectedRows = keepSelection ? myTable.getSelectedRows() : new int[0];
DefaultMutableTreeNode root = myTable.getRoot();
root.removeAllChildren();
if (sdkEntry != null) {
Sdk flexSdk = FlexSdkUtils.findFlexOrFlexmojosSdk(sdkEntry.getName());
if (flexSdk != null) {
DefaultMutableTreeNode sdkNode = new DefaultMutableTreeNode(new SdkItem(flexSdk), true);
myTable.getRoot().insert(sdkNode, 0);
updateSdkEntries(sdkNode, flexSdk);
}
}
FlexBCConfigurator configurator = FlexBuildConfigurationsExtension.getInstance().getConfigurator();
for (DependencyEntry entry : myDependencies.getEntries()) {
MyTableItem item = null;
if (entry instanceof BuildConfigurationEntry) {
final BuildConfigurationEntry bcEntry = (BuildConfigurationEntry)entry;
Module module = bcEntry.findModule();
CompositeConfigurable configurable =
module != null ? ContainerUtil.find(configurator.getBCConfigurables(module),
configurable1 -> configurable1.getDisplayName().equals(bcEntry.getBcName())) : null;
if (configurable == null) {
item = new BCItem(bcEntry.getModuleName(), bcEntry.getBcName());
}
else {
item = new BCItem(FlexBCConfigurable.unwrap(configurable));
}
((BCItem)item).dependencyType.copyFrom(entry.getDependencyType());
}
else if (entry instanceof ModuleLibraryEntry) {
ModuleLibraryEntry moduleLibraryEntry = (ModuleLibraryEntry)entry;
item = new ModuleLibraryItem(moduleLibraryEntry.getLibraryId(),
myConfigEditor.findLibraryOrderEntry(myDependencies, moduleLibraryEntry),
myProject);
((ModuleLibraryItem)item).dependencyType.copyFrom(entry.getDependencyType());
}
else if (entry instanceof SharedLibraryEntry) {
SharedLibraryEntry sharedLibraryEntry = (SharedLibraryEntry)entry;
LibrariesModifiableModel model = ProjectStructureConfigurable.getInstance(myProject).getContext()
.createModifiableModelProvider(sharedLibraryEntry.getLibraryLevel()).getModifiableModel();
LibraryEx library = (LibraryEx)model.getLibraryByName(sharedLibraryEntry.getLibraryName());
item = new SharedLibraryItem(sharedLibraryEntry.getLibraryName(), sharedLibraryEntry.getLibraryLevel(), library, myProject);
((SharedLibraryItem)item).dependencyType.copyFrom(entry.getDependencyType());
}
if (item != null) {
root.add(new DefaultMutableTreeNode(item, false));
}
}
myTable.refresh();
myTable.clearSelection();
for (int row : selectedRows) {
myTable.getSelectionModel().addSelectionInterval(row, row);
}
}
/**
* Called when {@link CompilerOptionsConfigurable} is initialized and when path to additional config file is changed
*
* @param targetPlayer <code>null</code> means that the value is not overridden in additional config file
*/
public void overriddenTargetPlayerChanged(final @Nullable String targetPlayer) {
myTargetPlayerWarning.setToolTipText(FlexBundle.message("actual.value.from.config.file.0", targetPlayer));
myWarning.setText(FlexBundle.message("overridden.in.config.file", "Target player", targetPlayer));
final boolean visible = myTargetPlayerCombo.isVisible() && targetPlayer != null;
myTargetPlayerWarning.setVisible(visible);
myWarning.setVisible(visible);
}
private void updateComponentSetCombo() {
updateControls();
final Sdk sdk = mySdkCombo.getSelectedJdk();
if (sdk != null && myComponentSetCombo.isVisible()) {
final Object selectedItem = myComponentSetCombo.getSelectedItem();
final ComponentSet[] values = StringUtil.compareVersionNumbers(sdk.getVersionString(), "4.5") >= 0
? ComponentSet.values()
: new ComponentSet[]{ComponentSet.SparkAndMx, ComponentSet.MxOnly};
myComponentSetCombo.setModel(new DefaultComboBoxModel(values));
myComponentSetCombo.setSelectedItem(selectedItem);
}
}
private void updateSdkTableItem(@Nullable Sdk sdk) {
DefaultMutableTreeNode sdkNode = findSdkNode();
if (sdk != null) {
SdkItem sdkItem = new SdkItem(sdk);
if (sdkNode != null) {
sdkNode.setUserObject(sdkItem);
}
else {
sdkNode = new DefaultMutableTreeNode(sdkItem, true);
myTable.getRoot().insert(sdkNode, 0);
}
updateSdkEntries(sdkNode, sdk);
}
else if (sdkNode != null) {
myTable.getRoot().remove(sdkNode);
}
}
private void updateSdkEntries(DefaultMutableTreeNode sdkNode, Sdk sdk) {
sdkNode.removeAllChildren();
ComponentSet componentSet = (ComponentSet)myComponentSetCombo.getSelectedItem();
String targetPlayer = (String)myTargetPlayerCombo.getSelectedItem();
for (String url : sdk.getRootProvider().getUrls(OrderRootType.CLASSES)) {
final String swcPath = VirtualFileManager.extractPath(StringUtil.trimEnd(url, JarFileSystem.JAR_SEPARATOR));
LinkageType linkageType = FlexCommonUtils.getSdkEntryLinkageType(sdk.getHomePath(), swcPath, myNature, targetPlayer, componentSet);
if (linkageType == null) {
// this swc is not applicable
continue;
}
if (linkageType == LinkageType.Default) {
linkageType = (LinkageType)myFrameworkLinkageCombo.getSelectedItem();
if (linkageType == LinkageType.Default) {
linkageType = FlexCommonUtils.getDefaultFrameworkLinkage(sdk.getVersionString(), myNature);
}
}
SdkEntryItem item = new SdkEntryItem(FileUtil.toSystemDependentName(swcPath), linkageType);
sdkNode.add(new DefaultMutableTreeNode(item, false));
}
}
public void disposeUIResources() {
Disposer.dispose(myDisposable);
}
private void createUIComponents() {
final Condition<SdkTypeId> sdkTypeFilter = Conditions.oneOf(FlexSdkType2.getInstance(), FlexmojosSdkType.getInstance());
Condition<Sdk> sdkCondition =
JdkComboBox.getSdkFilter(sdkTypeFilter);
mySdkCombo = new JdkComboBox(mySkdsModel, sdkTypeFilter, sdkCondition, Conditions.is(FlexSdkType2.getInstance()), false);
}
private void initPopupActions() {
if (myPopupActions == null) {
int actionIndex = 1;
final List<AddItemPopupAction> actions = new ArrayList<>();
actions.add(new AddBuildConfigurationDependencyAction(actionIndex++));
actions.add(new AddFilesAction(actionIndex++));
actions.add(new AddSharedLibraryAction(actionIndex++));
myPopupActions = actions.toArray(new AddItemPopupAction[actions.size()]);
}
}
private class AddBuildConfigurationDependencyAction extends AddItemPopupAction {
public AddBuildConfigurationDependencyAction(int index) {
super(index, "Build Configuration...", null);
}
@Override
public void run() {
final Collection<FlexBCConfigurable> dependencies = new ArrayList<>();
List<MyTableItem> items = myTable.getItems();
for (MyTableItem item : items) {
if (item instanceof BCItem) {
FlexBCConfigurable configurable = ((BCItem)item).configurable;
if (configurable != null) {
dependencies.add(configurable);
}
}
}
ChooseBuildConfigurationDialog d = ChooseBuildConfigurationDialog.createForApplicableBCs(
FlexBundle.message("add.bc.dependency.dialog.title"), FlexBundle.message("add.dependency.bc.dialog.label"), myProject, false,
configurable -> {
if (dependencies.contains(configurable) || configurable.isParentFor(DependenciesConfigurable.this)) {
return false;
}
if (!BCUtils.isApplicableForDependency(myNature, configurable.getOutputType())) {
return false;
}
return true;
});
if (d == null) {
Messages.showInfoMessage(myProject, FlexBundle.message("no.applicable.bcs"), FlexBundle.message("add.bc.dependency.dialog.title"));
return;
}
if (!d.showAndGet()) {
return;
}
FlexBCConfigurable[] configurables = d.getSelectedConfigurables();
DefaultMutableTreeNode root = myTable.getRoot();
for (FlexBCConfigurable configurable : configurables) {
root.add(new DefaultMutableTreeNode(new BCItem(configurable), false));
}
myTable.refresh();
myTable.getSelectionModel().clearSelection();
int rowCount = myTable.getRowCount();
myTable.getSelectionModel().addSelectionInterval(rowCount - configurables.length, rowCount - 1);
}
}
private class AddFilesAction extends AddItemPopupAction {
public AddFilesAction(int index) {
super(index, FlexBundle.message("add.module.library.action.text"), null);
}
@Override
public void run() {
// TODO we can have problems if we add the same library twice for one module?
final Collection<Library> usedLibraries = new ArrayList<>();
List<MyTableItem> items = myTable.getItems();
for (MyTableItem item : items) {
if (item instanceof ModuleLibraryItem) {
LibraryOrderEntry orderEntry = ((ModuleLibraryItem)item).orderEntry;
if (orderEntry != null) {
ContainerUtil.addIfNotNull(usedLibraries, orderEntry.getLibrary());
}
}
}
Condition<Library> filter = library -> usedLibraries.contains(library);
LibraryTable.ModifiableModel modifiableModel = myConfigEditor.getLibraryModel(myDependencies);
LibraryTable.ModifiableModel librariesModelWrapper = new LibraryTableModifiableModelWrapper(modifiableModel, filter);
Module module = myConfigEditor.getModule(myDependencies);
List<? extends FlexLibraryType> libraryTypes = Collections.singletonList(FlexLibraryType.getInstance());
CreateModuleLibraryChooser c = new CreateModuleLibraryChooser(libraryTypes, myMainPanel, module, librariesModelWrapper,
type -> new FlexLibraryProperties(
FlexLibraryIdGenerator.generateId()));
final List<Library> libraries = c.chooseElements();
if (libraries.isEmpty()) {
return;
}
DefaultMutableTreeNode rootNode = myTable.getRoot();
for (Library library : libraries) {
String libraryId = FlexProjectRootsUtil.getLibraryId(library);
LibraryOrderEntry libraryEntry = myConfigEditor.findLibraryOrderEntry(myDependencies, library);
rootNode.add(new DefaultMutableTreeNode(new ModuleLibraryItem(libraryId, libraryEntry, myProject), false));
}
updateTableOnItemsAdded(libraries.size());
}
}
private class AddSharedLibraryAction extends AddItemPopupAction {
public AddSharedLibraryAction(int index) {
super(index, FlexBundle.message("add.shared.library.dependency.action.text"), null);
}
public void run() {
final Collection<Library> usedLibraries = new HashSet<>();
List<MyTableItem> items = myTable.getItems();
for (MyTableItem item : items) {
if (item instanceof SharedLibraryItem) {
LibraryEx library = (LibraryEx)((SharedLibraryItem)item).findLiveLibrary();
if (library != null) {
usedLibraries.add(library);
}
}
}
ChooseLibrariesDialog d = new ChooseLibrariesDialog(library -> !usedLibraries.contains(library));
if (!d.showAndGet()) {
return;
}
final List<Library> libraries = d.getSelectedLibraries();
addSharedLibraries(libraries);
}
}
public void addSharedLibraries(final List<Library> libraries) {
DefaultMutableTreeNode rootNode = myTable.getRoot();
for (Library library : libraries) {
SharedLibraryItem item = new SharedLibraryItem(library.getName(), library.getTable().getTableLevel(), library, myProject);
rootNode.add(new DefaultMutableTreeNode(item, false));
}
updateTableOnItemsAdded(libraries.size());
}
public void addBCDependency(final FlexBCConfigurable dependencyConfigurable, final LinkageType linkageType) {
final DefaultMutableTreeNode rootNode = myTable.getRoot();
final Enumeration children = rootNode.children();
while (children.hasMoreElements()) {
final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)children.nextElement();
final Object userObject = childNode.getUserObject();
if (userObject instanceof BCItem && ((BCItem)userObject).configurable == dependencyConfigurable) {
return;
}
}
final BCItem item = new BCItem(dependencyConfigurable);
item.setLinkageType(linkageType);
// todo let BC-on-BC dependency be before BC-on-lib dependencies. Need also to fix FlexProjectConfigurationEditor.setEntries()
rootNode.add(new DefaultMutableTreeNode(item, false));
updateTableOnItemsAdded(1);
}
public void removeDependency(final String moduleLibraryId) {
final DefaultMutableTreeNode rootNode = myTable.getRoot();
final Enumeration children = rootNode.children();
while (children.hasMoreElements()) {
final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)children.nextElement();
final Object userObject = childNode.getUserObject();
if (userObject instanceof ModuleLibraryItem && moduleLibraryId.equals(((ModuleLibraryItem)userObject).libraryId)) {
childNode.removeFromParent();
myTable.refresh();
return;
}
}
}
public void removeDependency(final String sharedLibraryLevel, final String sharedLibraryName) {
final DefaultMutableTreeNode rootNode = myTable.getRoot();
final Enumeration children = rootNode.children();
while (children.hasMoreElements()) {
final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)children.nextElement();
final Object userObject = childNode.getUserObject();
if (userObject instanceof SharedLibraryItem &&
sharedLibraryLevel.equals(((SharedLibraryItem)userObject).libraryLevel) &&
sharedLibraryName.equals(((SharedLibraryItem)userObject).libraryName)) {
childNode.removeFromParent();
myTable.refresh();
return;
}
}
}
private void updateTableOnItemsAdded(int count) {
myTable.refresh();
myTable.getSelectionModel().clearSelection();
int rowCount = myTable.getRowCount();
myTable.getSelectionModel().addSelectionInterval(rowCount - count, rowCount - 1);
}
private void updateOnSelectedSdkChange() {
Sdk sdk = mySdkCombo.getSelectedJdk();
if (sdk != null && (sdk.getSdkType() != FlexSdkType2.getInstance() && sdk.getSdkType() != FlexmojosSdkType.getInstance())) {
sdk = null; // TODO remove this when SDK filters out non-Flex items
}
BCUtils.updateAvailableTargetPlayers(sdk, myTargetPlayerCombo);
updateComponentSetCombo();
updateSdkTableItem(sdk);
myTable.refresh();
mySdkChangeDispatcher.getMulticaster().stateChanged(new ChangeEvent(this));
}
@Override
public void setHistory(final History history) {
}
@Override
public ActionCallback navigateTo(@Nullable final Place place, final boolean requestFocus) {
if (place != null) {
final Object location = place.getPath(FlexBCConfigurable.LOCATION_ON_TAB);
if (location == Location.SDK) {
if (requestFocus) {
return IdeFocusManager.findInstance().requestFocus(mySdkCombo, true);
}
}
else if (location instanceof Location.TableEntry) {
for (int row = 0; row < myTable.getRowCount(); row++) {
MyTableItem item = myTable.getItemAt(row);
Location.TableEntry loc = item.getLocation();
if (loc.equals(location)) {
myTable.clearSelection();
myTable.getSelectionModel().addSelectionInterval(row, row);
TableUtil.scrollSelectionToVisible(myTable);
break;
}
}
if (requestFocus) {
return IdeFocusManager.findInstance().requestFocus(myTable, true);
}
}
}
return ActionCallback.DONE;
}
@Override
public void queryPlace(@NotNull final Place place) {
}
public void libraryReplaced(@NotNull final Library library, @Nullable final Library replacement) {
assert myReset;
// look in UI as there is no way to find just-created-and-then-renamed library in model
List<MyTableItem> items = myTable.getItems();
for (int i = 0; i < items.size(); i++) {
MyTableItem item = items.get(i);
if (item instanceof SharedLibraryItem && ((SharedLibraryItem)item).findLiveLibrary() == library) {
removeItems(Collections.singleton(item), replacement == null);
break;
}
}
if (replacement != null) {
addSharedLibraries(Collections.singletonList(replacement));
}
}
public void addUserActivityListener(final UserActivityListener listener, final Disposable disposable) {
myUserActivityDispatcher.addListener(listener, disposable);
}
public void removeUserActivityListeners() {
for (UserActivityListener listener : myUserActivityDispatcher.getListeners()) {
myUserActivityDispatcher.removeListener(listener);
}
}
private static class LibraryTableModifiableModelWrapper implements LibraryTable.ModifiableModel {
private final LibraryTable.ModifiableModel myDelegate;
private final Condition<Library> myLibraryFilter;
private LibraryTableModifiableModelWrapper(LibraryTable.ModifiableModel delegate, Condition<Library> libraryFilter) {
myDelegate = delegate;
myLibraryFilter = libraryFilter;
}
@Override
public Library createLibrary(String name) {
return myDelegate.createLibrary(name);
}
@Override
public void removeLibrary(@NotNull Library library) {
myDelegate.removeLibrary(library);
}
@Override
public void commit() {
myDelegate.commit();
}
@Override
@NotNull
public Iterator<Library> getLibraryIterator() {
return new FilteringIterator<>(myDelegate.getLibraryIterator(), myLibraryFilter);
}
@Override
@Nullable
public Library getLibraryByName(@NotNull String name) {
Library library = myDelegate.getLibraryByName(name);
return myLibraryFilter.value(library) ? library : null;
}
@Override
@NotNull
public Library[] getLibraries() {
List<Library> filtered = ContainerUtil.filter(myDelegate.getLibraries(), myLibraryFilter);
return filtered.toArray(new Library[filtered.size()]);
}
@Override
public boolean isChanged() {
return myDelegate.isChanged();
}
@Override
public Library createLibrary(String name, @Nullable PersistentLibraryKind kind) {
return myDelegate.createLibrary(name, kind);
}
@Override
public Library createLibrary(String name, @Nullable PersistentLibraryKind type, @Nullable ProjectModelExternalSource externalSource) {
return myDelegate.createLibrary(name, type, externalSource);
}
@Override
public void dispose() {
Disposer.dispose(myDelegate);
}
}
private class ChooseLibrariesDialog extends ChooseLibrariesFromTablesDialog {
private final Condition<Library> myFilter;
public ChooseLibrariesDialog(Condition<Library> liveLibraryFilter) {
super(myMainPanel, "Choose Libraries", myProject, false);
myFilter = liveLibraryFilter;
init();
}
public void show() {
if (isEmpty()) {
Disposer.dispose(getDisposable());
dispose();
Messages.showInfoMessage(myProject, "No applicable libraries found", "Add Dependency");
}
else {
super.show();
}
}
@NotNull
protected Library[] getLibraries(@NotNull final LibraryTable table) {
final StructureConfigurableContext context = ProjectStructureConfigurable.getInstance(myProject).getContext();
final Library[] libraries = context.createModifiableModelProvider(table.getTableLevel()).getModifiableModel().getLibraries();
final List<Library> filtered = ContainerUtil.mapNotNull(libraries, library -> {
Library liveLibrary = context.getLibraryModel(library);
if (liveLibrary == null || !FlexProjectRootsUtil.isFlexLibrary(liveLibrary) || !myFilter.value(liveLibrary)) {
return null;
}
return liveLibrary;
});
return filtered.toArray(new Library[filtered.size()]);
}
}
private Pair<String, String> getModuleAndBcName() {
FlexBCConfigurator configurator = FlexBuildConfigurationsExtension.getInstance().getConfigurator();
for (Module module : ProjectStructureConfigurable.getInstance(myProject).getModulesConfig().getModules()) {
for (CompositeConfigurable configurable : configurator.getBCConfigurables(module)) {
FlexBCConfigurable flexBCConfigurable = FlexBCConfigurable.unwrap(configurable);
if (flexBCConfigurable.isParentFor(this)) {
return Pair.create(module.getName(), flexBCConfigurable.getDisplayName());
}
}
}
return Pair.create("?", "?");
}
}