/*
* #%L
* org.gitools.ui.app
* %%
* Copyright (C) 2013 - 2016 Universitat Pompeu Fabra - Biomedical Genomics group
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
package org.gitools.ui.app.actions.data.transform;
import com.jgoodies.binding.PresentationModel;
import com.jgoodies.binding.beans.PropertyAdapter;
import com.jgoodies.binding.list.SelectionInList;
import org.gitools.api.matrix.ConfigurableTransformFunction;
import org.gitools.api.matrix.IFunctionParameter;
import org.gitools.api.matrix.IMatrixLayer;
import org.gitools.heatmap.Heatmap;
import org.gitools.matrix.model.MatrixLayer;
import org.gitools.matrix.transform.FoldChangeFunction;
import org.gitools.matrix.transform.LogFunction;
import org.gitools.matrix.transform.TransformFunctionFactory;
import org.gitools.matrix.transform.parameters.AbstractFunctionParameter;
import org.gitools.matrix.transform.parameters.DoubleParameter;
import org.gitools.ui.core.utils.DocumentChangeListener;
import org.gitools.ui.platform.IconUtils;
import org.gitools.ui.platform.dialog.MessageStatus;
import org.gitools.ui.platform.icons.IconNames;
import org.gitools.ui.platform.wizard.AbstractWizardPage;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static com.jgoodies.binding.adapter.Bindings.bind;
public class SelectTransformationsPage extends AbstractWizardPage {
private final MatrixLayer<Double> resultLayer;
private JPanel rootPanel;
private JTable transformationTable;
private JButton removeButton;
private JPanel editPanel;
private JButton upButton;
private JButton downButton;
private JButton addButton;
private JTextField resultLayerName;
private JPanel addnewPanel;
private JPanel tableButtonPanel;
private JPanel transformationPanel;
private JList<ConfigurableTransformFunction> transformFunctionJList;
private JLabel transformFunctionDescription;
private JButton cancelNewButton;
private JButton acceptNewButton;
private TransformationFunctionTableModel tableModel;
private Heatmap heatmap;
public SelectTransformationsPage(Heatmap heatmap) {
super();
this.heatmap = heatmap;
resultLayer = createResultLayer();
init();
}
private MatrixLayer<Double> createResultLayer() {
MatrixLayer<Double> resultLayer = new MatrixLayer<>(getNewLayerName(), Double.class);
return resultLayer;
}
public void init() {
setTitle("Transformation selection");
setLogo(IconUtils.getImageIconResourceScaledByHeight(IconNames.LOGO_METHOD, 96));
switchToSelectedFunctionPanel();
tableModel = new TransformationFunctionTableModel();
final ArrayList<ConfigurableTransformFunction> funcs = new ArrayList<>();
funcs.add(new LogFunction());
funcs.add(new FoldChangeFunction(resultLayer));
tableModel.setFunctions(funcs);
transformationTable.setModel(tableModel);
transformationTable.getColumnModel().getColumn(0).setMaxWidth(100);
transformationTable.getColumnModel().getColumn(0).setMinWidth(80);
transformationTable.getColumnModel().getColumn(1).setMaxWidth(200);
transformationTable.getColumnModel().getColumn(1).setMinWidth(150);
transformationTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
updateControls();
}
});
List<ConfigurableTransformFunction> selectFuncs = TransformFunctionFactory.get(resultLayer);
final ConfigurableTransformFunction[] transformFunctions = selectFuncs.toArray(new ConfigurableTransformFunction[selectFuncs.size()]);
/*transformationsCB.setModel(new DefaultComboBoxModel(transformFunctions));
transformationsCB.setSelectedIndex(0);*/
transformFunctionJList.setListData(transformFunctions);
transformFunctionJList.setSelectedIndex(0);
transformFunctionJList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
ConfigurableTransformFunction selectedValue = transformFunctionJList.getSelectedValue();
String labelText = String.format("<html><div style=\"width:%dpx;\">%s", 500, selectedValue.getDescription());
if (selectedValue.getParamterCount() > 0) {
labelText += "<br/> Configurable parameters:<ul>";
for (String key : selectedValue.getParameters().keySet()) {
labelText += "<li><b>" + key + ":</b> " + selectedValue.getParameters().get(key).getDescription() + "</li>";
}
}
labelText += "</div><html>";
transformFunctionDescription.setText(labelText);
}
});
PresentationModel<MatrixLayer<Double>> layerModel = new PresentationModel<>(resultLayer);
bind(resultLayerName, layerModel.getModel(MatrixLayer.PROPERTY_ID));
resultLayerName.setText("transform-" + UUID.randomUUID().toString().substring(0,4));
resultLayerName.getDocument().addDocumentListener(new DocumentChangeListener() {
@Override
protected void update(DocumentEvent e) {
updateControls();
}
});
removeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow = transformationTable.getSelectedRow();
if (selectedRow == funcs.size()-1) {
transformationTable.clearSelection();
if (funcs.size() != 1) {
transformationTable.addRowSelectionInterval(funcs.size()-2, funcs.size()-2);
}
}
tableModel.remove(selectedRow);
updateControls();
}
});
upButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tableModel.moveUp(transformationTable.getSelectedRow());
updateControls();
}
});
downButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tableModel.moveDown(transformationTable.getSelectedRow());
updateControls();
}
});
addButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
switchToAddFunctionPanel();
}
});
acceptNewButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
tableModel.add(transformFunctionJList.getSelectedValue());
switchToSelectedFunctionPanel();
updateControls();
}
});
cancelNewButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
switchToSelectedFunctionPanel();
}
});
updateControls();
}
private void switchToSelectedFunctionPanel() {
addnewPanel.setEnabled(false);
addnewPanel.setVisible(false);
transformationPanel.setEnabled(true);
transformationPanel.setVisible(true);
updateControls();
}
private void switchToAddFunctionPanel() {
transformationPanel.setEnabled(false);
transformationPanel.setVisible(false);
addnewPanel.setVisible(true);
addnewPanel.setEnabled(true);
setComplete(false);
}
@Override
public JComponent createControls() {
return rootPanel;
}
public List<ConfigurableTransformFunction> getTransformationFunctions() {
return tableModel.getAll();
}
@Override
public void updateControls() {
super.updateControls();
removeButton.setEnabled(transformationTable.getSelectedRowCount() == 1);
upButton.setEnabled(transformationTable.getSelectedRowCount() == 1 && transformationTable.getSelectedRow() != 0);
downButton.setEnabled(transformationTable.getSelectedRowCount() == 1 && transformationTable.getSelectedRow() != transformationTable.getRowCount()-1);
keepTableUpdated();
addParameterComponents();
setComplete(tableModel != null && tableModel.getRowCount() > 0
&& validateFunctionParameters()
&& validLayerName());
}
private boolean validLayerName() {
String newLayerName = getNewLayerName();
if(newLayerName.equals("")) {
setMessage("Define a layer name and choose the transformation(s)");
return false;
}
for (String existingLayer : heatmap.getLayers().getIds()) {
if (existingLayer.toLowerCase().equals(newLayerName.toLowerCase()) ||
newLayerName.toLowerCase().equals(heatmap.getLayers().get(existingLayer).getName().toLowerCase())) {
setMessage(MessageStatus.ERROR, "Chosen layer name already exists");
return false;
}
}
setMessage("Choose and edit the transformation(s)");
return true;
}
private void keepTableUpdated() {
if (tableModel != null && tableModel.get() != null) {
transformationTable.setEnabled(tableModel.get().validate());
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// updateUI();
transformationTable.updateUI();
}
});
}
private boolean validateFunctionParameters() {
for (int i = 0; i < tableModel.getRowCount(); i++) {
if (!tableModel.get(i).validate()) {
return false;
}
}
return true;
}
private void addParameterComponents() {
if (transformationPanel == null || transformationTable.getSelectedRow() < 0) {
return;
}
ConfigurableTransformFunction func = tableModel.get(transformationTable.getSelectedRow());
if (func == null) {
return;
}
editPanel.removeAll();
editPanel.setLayout(new GridLayout(0,2));
if (func.getParamterCount() > 0) {
addParameterComponent(new JLabel("<html><body><b>" + func.getName() + "</b></body></html>"));
addParameterComponent(new JLabel("Parameters"));
for (String key : func.getParameters().keySet()) {
IFunctionParameter param = func.getParameters().get(key);
Component c = getParamInput((AbstractFunctionParameter) param);
String labelText = String.format("<html><div style=\"width:%dpx;\">%s</div><html>", 300, param.getDescription());
addParameterComponent(new JLabel(labelText));
addParameterComponent(c);
}
} else {
addParameterComponent(new JLabel("<html><body><b>" + func.getName() + "</b></body></html>"));
addParameterComponent(new JLabel("No configurable parameters"));
}
}
private Component getParamInput(AbstractFunctionParameter param) {
Component c = null;
if (param.hasChoices()) {
JComboBox jComboBox = new JComboBox();
bind(jComboBox,
new SelectionInList<>(param.getChoices(), new PropertyAdapter<>(param, param.PROPERTY_PARAMETER_VALUE)));
jComboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
keepTableUpdated();
}
});
c = jComboBox;
} else if (param.getParameterValue().getClass() == Integer.class) {
JFormattedTextField textField = new JFormattedTextField();
/*bind (textField,
createStringConverter(
new PropertyAdapter<>(param, param.PROPERTY_PARAMETER_VALUE),
NumberFormat.getIntegerInstance()
));*/
bind(textField, new PresentationModel<>(param).getModel(DoubleParameter.PROPERTY_PARAMETER_VALUE));
textField.getDocument().addDocumentListener(new DocumentChangeListener() {
@Override
protected void update(DocumentEvent e) {
keepTableUpdated();
}
});
c = textField;
} else if (param.getParameterValue().getClass() == Double.class) {
JFormattedTextField textField = new JFormattedTextField();
bind(textField, new PresentationModel<>(param).getModel(DoubleParameter.PROPERTY_PARAMETER_VALUE));
textField.getDocument().addDocumentListener(new DocumentChangeListener() {
@Override
protected void update(DocumentEvent e) {
keepTableUpdated();
}
});
c = textField;
}
return c;
}
private void addParameterComponent(Component c) {
if (c == null) {
return;
}
editPanel.add(c);
editPanel.revalidate();
editPanel.repaint();
}
public IMatrixLayer getLayer() {
return heatmap.getLayers().getTopLayer();
}
public String getNewLayerName() {
return resultLayerName.getText();
}
public MatrixLayer<Double> getNewLayer() {
String desc = "Transformed from " + getLayer().getName() + ": ";
for (ConfigurableTransformFunction function : getTransformationFunctions()) {
desc += function.getName() + "; ";
}
resultLayer.setDescription(desc);
return resultLayer;
}
private class TransformationFunctionTableModel extends AbstractTableModel {
private List<ConfigurableTransformFunction> funcs;
private String[] columnName = {"Order", "Transformation", "Explanation"};
@Override
public int getRowCount() {
return funcs.size();
}
@Override
public int getColumnCount() {
return 3;
}
@Override
public String getColumnName(int columnIndex) {
return columnName[columnIndex];
}
public ConfigurableTransformFunction get(int i) {
return (ConfigurableTransformFunction) getValueAt(i, 1);
}
public ConfigurableTransformFunction get() {
return (ConfigurableTransformFunction) getValueAt(transformationTable.getSelectedRow(), 1);
}
public List<ConfigurableTransformFunction> getAll() {
return funcs;
}
public void remove(int i) {
funcs.remove(i);
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex < 0 || columnIndex < 0) {
return null;
}
if (columnIndex == 0) {
return rowIndex+1 + ". ";
}
else if (columnIndex == 1 && funcs.size() > 0) {
return funcs.get(rowIndex);
} else if (columnIndex == 2 && funcs.size() > 0) {
return funcs.get(rowIndex).getDescription();
}
return null;
}
public void setFunctions(List<ConfigurableTransformFunction> funcs) {
this.funcs = funcs;
}
public void add(ConfigurableTransformFunction func) {
this.funcs.add(TransformFunctionFactory.createFromTemplate(func));
}
public void moveUp(int selectedRow) {
int newPos = selectedRow - 1;
ConfigurableTransformFunction f = get(selectedRow);
remove(selectedRow);
funcs.add(newPos, f);
transformationTable.getSelectionModel().setSelectionInterval(newPos, newPos);
}
public void moveDown(int selectedRow) {
int newPos = selectedRow + 1;
ConfigurableTransformFunction f = get(selectedRow);
remove(selectedRow);
funcs.add(newPos, f);
transformationTable.getSelectionModel().setSelectionInterval(newPos, newPos);
}
}
}