package org.phenoscape.view;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.obo.app.swing.BugWorkaroundTable;
import org.obo.app.swing.PlaceholderRenderer;
import org.obo.app.swing.SortDisabler;
import org.obo.app.swing.TableColumnPrefsSaver;
import org.phenoscape.controller.PhenexController;
import org.phenoscape.model.Association;
import org.phenoscape.model.Character;
import org.phenoscape.model.DataSet;
import org.phenoscape.model.MultipleState;
import org.phenoscape.model.State;
import org.phenoscape.model.Taxon;
import org.phenoscape.util.ConsolidationUtil;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.gui.AdvancedTableFormat;
import ca.odell.glazedlists.gui.WritableTableFormat;
import ca.odell.glazedlists.swing.EventTableModel;
import ca.odell.glazedlists.swing.TableComparatorChooser;
import com.eekboom.utils.Strings;
public class StateTableComponent extends PhenoscapeGUIComponent {
private JButton addStateButton;
private JButton deleteStateButton;
public StateTableComponent(String id, PhenexController controller) {
super(id, controller);
}
@Override
public void init() {
super.init();
this.initializeInterface();
}
private void initializeInterface() {
this.setLayout(new BorderLayout());
final EventTableModel<State> statesTableModel = new EventTableModel<State>(this.getController().getStatesForCurrentCharacterSelection(), new StatesTableFormat());
final JTable statesTable = new BugWorkaroundTable(statesTableModel);
statesTable.setSelectionModel(this.getController().getCurrentStatesSelectionModel());
statesTable.setDefaultRenderer(Object.class, new PlaceholderRenderer("None"));
statesTable.putClientProperty("Quaqua.Table.style", "striped");
new TableColumnPrefsSaver(statesTable, this.getClass().getName());
final TableComparatorChooser<State> sortChooser = new TableComparatorChooser<State>(statesTable, this.getController().getStatesForCurrentCharacterSelection(), false);
sortChooser.addSortActionListener(new SortDisabler());
this.add(new JScrollPane(statesTable), BorderLayout.CENTER);
this.add(this.createToolBar(), BorderLayout.NORTH);
this.getController().getCharactersSelectionModel().addListSelectionListener(new CharacterSelectionListener());
this.getController().getCurrentStatesSelectionModel().addListSelectionListener(new StateSelectionListener());
}
public void createNewCharacterWithSelectedStates() {
this.getController().getUndoController().beginCoalescingEdits("New Character With States");
final List<State> states = Collections.unmodifiableList(this.getSelectedStates());
if (!states.isEmpty()) {
ConsolidationUtil.createNewCharacterWithStates(states, this.getSelectedCharacter(), this.getController().getDataSet());
}
this.getController().getUndoController().endCoalescingEdits();
}
public void consolidateSelectedStates() {
this.getController().getUndoController().beginCoalescingEdits("Consolidate States");
final List<State> states = Collections.unmodifiableList(this.getSelectedStates());
if (states.size() > 1) {
final Character selectedCharacter = this.getSelectedCharacter();
if (selectedCharacter == null) return; // don't consolidate states across different characters
ConsolidationUtil.consolidateStates(states, selectedCharacter, this.getController().getDataSet());
}
this.getController().getUndoController().endCoalescingEdits();
}
private void addState() {
final Character character = this.getSelectedCharacter();
if (character != null) { character.newState(); }
}
private void deleteSelectedStates() {
final List<State> states = Collections.unmodifiableList(this.getSelectedStates());
final Character selectedCharacter = this.getSelectedCharacter();
final DataSet data = this.getController().getDataSet();
for (Taxon taxon : data.getTaxa()) {
//FIXME some of this logic should be moved down into the model
final State stateValue = data.getStateForTaxon(taxon, selectedCharacter);
if (stateValue instanceof MultipleState) {
final MultipleState multiple = (MultipleState)stateValue;
if (!Collections.disjoint(states, multiple.getStates())) {
final Set<State> oldStates = new HashSet<State>(multiple.getStates());
oldStates.removeAll(states);
if (oldStates.size() > 1) {
data.setStateForTaxon(taxon, selectedCharacter, new MultipleState(oldStates, multiple.getMode()));
} else if (oldStates.size() == 1) {
data.setStateForTaxon(taxon, selectedCharacter, oldStates.iterator().next());
} else {
data.setStateForTaxon(taxon, selectedCharacter, null);
}
}
} else {
if (states.contains(stateValue)) {
data.setStateForTaxon(taxon, selectedCharacter, null);
}
}
}
if (selectedCharacter != null) {
selectedCharacter.removeStates(this.getSelectedStates());
}
}
private Character getSelectedCharacter() {
final EventList<Character> selected = this.getController().getCharactersSelectionModel().getSelected();
if (selected.size() == 1) {
return selected.get(0);
} else {
return null;
}
}
private List<State> getSelectedStates() {
return this.getController().getCurrentStatesSelectionModel().getSelected();
}
private void characterSelectionDidChange() {
final String unselectedTitle = "States";
final String selectedPrefix = "States for Character: ";
final List<Character> characters = this.getController().getCharactersSelectionModel().getSelected();
if (characters.isEmpty()) {
this.updatePanelTitle(unselectedTitle);
} else {
this.updatePanelTitle(selectedPrefix + characters.get(0));
}
this.updateButtonStates();
}
private void stateSelectionDidChange() {
this.updateButtonStates();
this.selectFirstPhenotype();
}
private void updateButtonStates() {
this.addStateButton.setEnabled(this.getSelectedCharacter() != null);
this.deleteStateButton.setEnabled(!this.getSelectedStates().isEmpty());
}
private void selectFirstPhenotype() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (!getController().getPhenotypesForCurrentStateSelection().isEmpty()) {
getController().getCurrentPhenotypesSelectionModel().setSelectionInterval(0, 0);
}
}
});
}
private JToolBar createToolBar() {
final JToolBar toolBar = new JToolBar();
this.addStateButton = new JButton(new AbstractAction(null, new ImageIcon(this.getClass().getResource("/org/phenoscape/view/images/list-add.png"))) {
@Override
public void actionPerformed(ActionEvent e) {
addState();
}
});
this.addStateButton.setToolTipText("Add State");
toolBar.add(this.addStateButton);
this.deleteStateButton = new JButton(new AbstractAction(null, new ImageIcon(this.getClass().getResource("/org/phenoscape/view/images/list-remove.png"))) {
@Override
public void actionPerformed(ActionEvent e) {
deleteSelectedStates();
}
});
this.deleteStateButton.setToolTipText("Delete State");
toolBar.add(this.deleteStateButton);
toolBar.setFloatable(false);
return toolBar;
}
private class StatesTableFormat implements WritableTableFormat<State>, AdvancedTableFormat<State> {
@Override
public boolean isEditable(State state, int column) {
return true;
}
@Override
public State setColumnValue(State state, Object editedValue, int column) {
switch(column) {
case 0: state.setSymbol(editedValue.toString()); break;
case 1: state.setLabel(editedValue.toString()); break;
case 2: state.setComment(editedValue.toString()); break;
case 3: state.setFigure(editedValue.toString()); break;
}
return state;
}
@Override
public int getColumnCount() {
return 4;
}
@Override
public String getColumnName(int column) {
switch(column) {
case 0: return "Symbol";
case 1: return "State Description";
case 2: return "Comment";
case 3: return "Figure";
default: return null;
}
}
@Override
public Object getColumnValue(State state, int column) {
switch(column) {
case 0: return state.getSymbol();
case 1: return state.getLabel();
case 2: return state.getComment();
case 3: return state.getFigure();
default: return null;
}
}
@Override
public Class<?> getColumnClass(int column) {
switch(column) {
case 0: return String.class;
case 1: return String.class;
case 2: return String.class;
case 3: return String.class;
default: return null;
}
}
@Override
public Comparator<?> getColumnComparator(int column) {
switch(column) {
case 0: return Strings.getNaturalComparator();
case 1: return Strings.getNaturalComparator();
case 2: return Strings.getNaturalComparator();
case 3: return Strings.getNaturalComparator();
default: return null;
}
}
}
private class CharacterSelectionListener implements ListSelectionListener {
public CharacterSelectionListener() {
characterSelectionDidChange();
}
@Override
public void valueChanged(ListSelectionEvent e) {
characterSelectionDidChange();
}
}
private class StateSelectionListener implements ListSelectionListener {
public StateSelectionListener() {
stateSelectionDidChange();
}
@Override
public void valueChanged(ListSelectionEvent e) {
stateSelectionDidChange();
}
}
}