/*
* Copyright 2003-2016 JetBrains s.r.o.
*
* 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 jetbrains.mps.ide.java.ui;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.AnActionButtonRunnable;
import com.intellij.ui.ColoredTableCellRenderer;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.TableUtil;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.table.JBTable;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.core.GridLayoutManager;
import com.intellij.util.ui.ItemRemovable;
import jetbrains.mps.ide.icons.IdeIcons;
import jetbrains.mps.ide.ui.dialogs.properties.MPSPropertiesConfigurable;
import jetbrains.mps.ide.ui.dialogs.properties.PropertiesBundle;
import jetbrains.mps.ide.ui.dialogs.properties.creators.StubRootChooser;
import jetbrains.mps.ide.ui.dialogs.properties.tabs.BaseTab;
import jetbrains.mps.ide.ui.filechoosers.treefilechooser.TreeFileChooser;
import jetbrains.mps.project.Solution;
import jetbrains.mps.project.facets.JavaModuleFacetImpl;
import jetbrains.mps.project.structure.model.ModelRootDescriptor;
import jetbrains.mps.project.structure.modules.SolutionDescriptor;
import jetbrains.mps.project.structure.modules.SolutionKind;
import jetbrains.mps.smodel.Language;
import jetbrains.mps.util.Computable;
import jetbrains.mps.util.FileUtil;
import jetbrains.mps.vfs.IFile;
import org.jetbrains.mps.openapi.module.SModuleFacet;
import org.jetbrains.mps.openapi.ui.persistence.FacetTab;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import java.awt.Dimension;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
// FIXME #apply() shall not deal with ModuleDescriptor directly, instead, JavaModuleFacet.save() shall put that there (better yet,
// to memento, not to be different from other facets, provided we don't use isCompileInMPS and getKind directly from descriptor)
public class JavaModuleFacetTab extends BaseTab implements FacetTab {
private PathsTableModel myPathsTableModel;
private LibraryTableModel myLibraryTableModel;
private JBCheckBox myCheckBox;
private ComboBox myComboBox;
private JavaModuleFacetImpl myJavaModuleFacet;
public JavaModuleFacetTab(JavaModuleFacetImpl javaModuleFacet) {
super(javaModuleFacet.getFacetPresentation(), IdeIcons.DEFAULT_ICON, PropertiesBundle.message("facet.java.tip"));
myJavaModuleFacet = javaModuleFacet;
}
@Override
public void init() {
JPanel advancedTab = new JPanel();
advancedTab.setLayout(new GridLayoutManager((myJavaModuleFacet.getModule() instanceof Solution ? 5 : 3), 2, MPSPropertiesConfigurable.INSETS, -1, -1));
int row = 0;
if (myJavaModuleFacet.getModule() instanceof Solution) {
SolutionDescriptor descriptor = ((Solution) myJavaModuleFacet.getModule()).getModuleDescriptor();
assert descriptor != null;
JBLabel solutionKindLabel = new JBLabel(PropertiesBundle.message("facet.java.solutionkind"));
myComboBox = new ComboBox(new DefaultComboBoxModel<SolutionKind>(SolutionKind.values()));
myComboBox.setSelectedItem(descriptor.getKind());
advancedTab.add(solutionKindLabel, new GridConstraints(row, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
advancedTab.add(myComboBox, new GridConstraints(row++, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
myCheckBox = new JBCheckBox(PropertiesBundle.message("facet.java.compileinmps"), descriptor.getCompileInMPS());
advancedTab.add(myCheckBox, new GridConstraints(row++, 0, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
}
advancedTab.add(getSourcePathsTable(), new GridConstraints(row++, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
advancedTab.add(getLibrariesTable(), new GridConstraints(row, 0, 1, 2, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
setTabComponent(advancedTab);
}
private JComponent getSourcePathsTable() {
myPathsTableModel = new PathsTableModel();
final JBTable sourcePathTable = new JBTable(myPathsTableModel);
sourcePathTable.setTableHeader(null);
sourcePathTable.setDefaultRenderer(String.class, new MyPathRenderer());
sourcePathTable.setShowHorizontalLines(false);
sourcePathTable.setShowVerticalLines(false);
sourcePathTable.setAutoCreateRowSorter(false);
sourcePathTable.setAutoscrolls(true);
ToolbarDecorator decorator = ToolbarDecorator.createDecorator(sourcePathTable);
decorator.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
myPathsTableModel.add((new PathChooser()).compute());
}
}).setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
TableUtil.removeSelectedItems(sourcePathTable);
myPathsTableModel.fireTableDataChanged();
}
});
decorator.setToolbarBorder(IdeBorderFactory.createBorder());
decorator.setPreferredSize(new Dimension(500, 100));
JPanel table = decorator.createPanel();
table.setBorder(IdeBorderFactory.createTitledBorder(PropertiesBundle.message("facet.java.sourcepath"), false));
return table;
}
private JComponent getLibrariesTable() {
myLibraryTableModel = new LibraryTableModel();
final JBTable librariesTable = new JBTable(myLibraryTableModel);
librariesTable.setTableHeader(null);
librariesTable.setDefaultRenderer(String.class, new MyPathRenderer());
librariesTable.setShowHorizontalLines(false);
librariesTable.setShowVerticalLines(false);
librariesTable.setAutoCreateRowSorter(false);
librariesTable.setAutoscrolls(true);
ToolbarDecorator decorator = ToolbarDecorator.createDecorator(librariesTable);
decorator.setAddAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
List<ModelRootDescriptor> modelRoots = new ArrayList<ModelRootDescriptor>(myJavaModuleFacet.getModule().getModuleDescriptor().getModelRootDescriptors());
StubRootChooser stubRootChooser = new StubRootChooser(librariesTable, modelRoots, myJavaModuleFacet.getModule() instanceof Language);
myLibraryTableModel.addAll(stubRootChooser.compute());
}
}).setRemoveAction(new AnActionButtonRunnable() {
@Override
public void run(AnActionButton anActionButton) {
TableUtil.removeSelectedItems(librariesTable);
myLibraryTableModel.fireTableDataChanged();
}
});
decorator.setToolbarBorder(IdeBorderFactory.createBorder());
decorator.setPreferredSize(new Dimension(500, 100));
JPanel table = decorator.createPanel();
table.setBorder(IdeBorderFactory.createTitledBorder(PropertiesBundle.message("facet.java.libraries"), false));
return table;
}
@Override
public boolean isModified() {
boolean solutionCheck = false;
if (myJavaModuleFacet.getModule() instanceof Solution) {
SolutionDescriptor descriptor = (SolutionDescriptor) myJavaModuleFacet.getModule().getModuleDescriptor();
assert descriptor != null;
solutionCheck = descriptor.getCompileInMPS() != myCheckBox.isSelected() || descriptor.getKind() != myComboBox.getSelectedItem();
}
return myPathsTableModel.isModified()
|| myLibraryTableModel.isModified()
|| solutionCheck;
}
@Override
public void apply() {
if (myJavaModuleFacet.getModule() instanceof Solution) {
SolutionDescriptor descriptor = (SolutionDescriptor) myJavaModuleFacet.getModule().getModuleDescriptor();
assert descriptor != null;
descriptor.setCompileInMPS(myCheckBox.isSelected());
descriptor.setKind((SolutionKind) myComboBox.getSelectedItem());
}
myPathsTableModel.apply();
myLibraryTableModel.apply();
}
@Override
public SModuleFacet getFacet() {
return myJavaModuleFacet;
}
private class PathsTableModel extends AbstractTableModel implements ItemRemovable {
public PathsTableModel() {
for(String s : myJavaModuleFacet.getAdditionalSourcePaths()) {
myPaths.add(s);
}
}
List<String> myPaths = new ArrayList<String>();
public void add(String path) {
if (path != null && !myPaths.contains(path)) {
myPaths.add(path);
fireTableDataChanged();
}
}
@Override
public int getRowCount() {
return myPaths.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return myPaths.get(rowIndex);
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public String getColumnName(int columnIndex) {
return "";
}
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) {
return String.class;
}
return super.getColumnClass(columnIndex);
}
@Override
public void removeRow(int idx) {
myPaths.remove(idx);
}
public boolean isModified() {
return !(myJavaModuleFacet.getAdditionalSourcePaths().containsAll(myPaths) && myPaths.containsAll(myJavaModuleFacet.getAdditionalSourcePaths()));
}
public void apply() {
myJavaModuleFacet.getModule().getModuleDescriptor().getSourcePaths().clear();
if (!myPaths.isEmpty()) {
myJavaModuleFacet.getModule().getModuleDescriptor().getSourcePaths().addAll(myPaths);
}
}
}
private class PathChooser implements Computable<String> {
@Override
public String compute() {
TreeFileChooser chooser = new TreeFileChooser();
chooser.setMode(TreeFileChooser.MODE_DIRECTORIES);
IFile result = chooser.showDialog();
if (result == null) {
return null;
}
return result.getPath();
}
}
private class LibraryTableModel extends AbstractTableModel implements ItemRemovable {
private List<String> myStubModelEntries = new ArrayList<String>();
public LibraryTableModel() {
for (String javaStubPath : myJavaModuleFacet.getModule().getModuleDescriptor().getAdditionalJavaStubPaths()) {
myStubModelEntries.add(javaStubPath);
}
}
public void addAll(Collection<String> javaStubPaths) {
if (myStubModelEntries.addAll(javaStubPaths)) {
fireTableDataChanged();
}
}
@Override
public int getRowCount() {
return myStubModelEntries.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return myStubModelEntries.get(rowIndex);
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public String getColumnName(int columnIndex) {
return "";
}
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) {
return String.class;
}
return super.getColumnClass(columnIndex);
}
@Override
public void removeRow(int idx) {
myStubModelEntries.remove(idx);
}
public boolean isModified() {
return !(myJavaModuleFacet.getModule().getModuleDescriptor().getAdditionalJavaStubPaths().containsAll(myStubModelEntries) &&
myStubModelEntries.containsAll(myJavaModuleFacet.getLibraryClassPath()));
}
public void apply() {
myJavaModuleFacet.getModule().getModuleDescriptor().getAdditionalJavaStubPaths().clear();
if (!myStubModelEntries.isEmpty()) {
myJavaModuleFacet.getModule().getModuleDescriptor().getAdditionalJavaStubPaths().addAll(myStubModelEntries);
}
}
}
private class MyPathRenderer extends ColoredTableCellRenderer {
@Override
protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) {
setPaintFocusBorder(false);
setFocusBorderAroundIcon(true);
setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
if (value != null) {
String path = FileUtil.getCanonicalPath((String) value);
if (!(new File(path)).exists()) {
append(path, SimpleTextAttributes.ERROR_ATTRIBUTES);
} else {
append(path);
}
}
}
}
}