package org.phenoscape.view; import java.awt.BorderLayout; import java.awt.Point; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; import java.io.IOException; import java.util.Arrays; import java.util.Comparator; import java.util.List; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JToolBar; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableCellEditor; import org.obo.annotation.base.TermSet; import org.obo.annotation.view.PostCompositionEditor; import org.obo.annotation.view.TermAutocompleteFieldFactory; import org.obo.annotation.view.TermRenderer; import org.obo.app.swing.BugWorkaroundTable; import org.obo.app.swing.PlaceholderRenderer; import org.obo.app.swing.PopupListener; import org.obo.app.swing.SortDisabler; import org.obo.app.swing.TableColumnPrefsSaver; import org.obo.datamodel.OBOClass; import org.obo.datamodel.OBOObject; import org.phenoscape.controller.PhenexController; import org.phenoscape.model.Phenotype; import org.phenoscape.model.State; import org.phenoscape.util.TermSelection; import org.phenoscape.util.TermTransferObject; import ca.odell.glazedlists.EventList; import ca.odell.glazedlists.GlazedLists; 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 PhenotypeTableComponent extends PhenoscapeGUIComponent { private JButton addPhenotypeButton; private JButton deletePhenotypeButton; private PopupListener tablePopup; private PhenotypesTableFormat tableFormat; private JTable phenotypesTable; private static final String COUNT = "PATO:0000070"; public PhenotypeTableComponent(String id, PhenexController controller) { super(id, controller); } @Override public void init() { super.init(); this.initializeInterface(); } private void initializeInterface() { this.setLayout(new BorderLayout()); this.tableFormat = new PhenotypesTableFormat(); final EventTableModel<Phenotype> phenotypesTableModel = new EventTableModel<Phenotype>(this.getController().getPhenotypesForCurrentStateSelection(), this.tableFormat); this.phenotypesTable = new BugWorkaroundTable(phenotypesTableModel); this.phenotypesTable.setSelectionModel(this.getController().getCurrentPhenotypesSelectionModel()); this.phenotypesTable.setDefaultRenderer(Object.class, new PlaceholderRenderer("None")); this.phenotypesTable.setDefaultRenderer(OBOObject.class, new TermRenderer("None")); for (int i = 0; i < this.phenotypesTable.getColumnCount(); i++) { final TableCellEditor editor = this.tableFormat.getColumnEditor(i); if (editor != null) { this.phenotypesTable.getColumnModel().getColumn(i).setCellEditor(editor); } } this.phenotypesTable.putClientProperty("Quaqua.Table.style", "striped"); new TableColumnPrefsSaver(this.phenotypesTable, this.getClass().getName()); this.tablePopup = new PopupListener(this.createTablePopupMenu()); this.phenotypesTable.addMouseListener(this.tablePopup); final TableComparatorChooser<Phenotype> sortChooser = new TableComparatorChooser<Phenotype>(this.phenotypesTable, this.getController().getPhenotypesForCurrentStateSelection(), false); sortChooser.addSortActionListener(new SortDisabler()); this.add(new JScrollPane(this.phenotypesTable), BorderLayout.CENTER); this.add(this.createToolBar(), BorderLayout.NORTH); this.getController().getCurrentStatesSelectionModel().addListSelectionListener(new StateSelectionListener()); this.getController().getCurrentPhenotypesSelectionModel().addListSelectionListener(new PhenotypeSelectionListener()); } private void addPhenotype() { final State state = this.getSelectedState(); if (state != null) { final Phenotype phenotype = state.newPhenotype(); phenotype.setEntity(this.getAutofillEntity()); final OBOClass possibleQuality = this.getAutofillQuality(); if ((possibleQuality != null) && (possibleQuality.getID().equals(COUNT))) { phenotype.setQuality(this.getAutofillQuality()); } } } private OBOClass getAutofillEntity() { //check current state for (State state : this.getController().getCurrentStatesSelectionModel().getSelected()) { for (Phenotype phenotype : state.getPhenotypes()) { if (phenotype.getEntity() != null) { return phenotype.getEntity(); } } } //then check all states for (State state : this.getController().getStatesForCurrentCharacterSelection()) { for (Phenotype phenotype : state.getPhenotypes()) { if (phenotype.getEntity() != null) { return phenotype.getEntity(); } } } return null; } private OBOClass getAutofillQuality() { //check current state for (State state : this.getController().getCurrentStatesSelectionModel().getSelected()) { for (Phenotype phenotype : state.getPhenotypes()) { if (phenotype.getQuality() != null) { return phenotype.getQuality(); } } } //then check all states for (State state : this.getController().getStatesForCurrentCharacterSelection()) { for (Phenotype phenotype : state.getPhenotypes()) { if (phenotype.getQuality() != null) { return phenotype.getQuality(); } } } return null; } private void deleteSelectedPhenotype() { final State state = this.getSelectedState(); if (state != null) { final Phenotype phenotype = this.getSelectedPhenotype(); if (phenotype != null) { state.removePhenotype(phenotype); } } } private State getSelectedState() { final EventList<State> selected = this.getController().getCurrentStatesSelectionModel().getSelected(); if (selected.size() == 1) { return selected.get(0); } else { return null; } } private Phenotype getSelectedPhenotype() { final EventList<Phenotype> selected = this.getController().getCurrentPhenotypesSelectionModel().getSelected(); if (selected.size() == 1) { return selected.get(0); } else { return null; } } private void stateSelectionDidChange() { final String unselectedTitle = "Phenotypes"; final String selectedPrefix = "Phenotypes for State: "; final List<State> states = this.getController().getCurrentStatesSelectionModel().getSelected(); if (states.isEmpty()) { this.updatePanelTitle(unselectedTitle); } else { this.updatePanelTitle(selectedPrefix + states.get(0)); } this.updateButtonStates(); } private void phenotypeSelectionDidChange() { this.updateButtonStates(); } private void updateButtonStates() { this.addPhenotypeButton.setEnabled(this.getSelectedState() != null); this.deletePhenotypeButton.setEnabled(this.getSelectedPhenotype() != null); } private void runPostCompositionForTermAtPoint(Point p) { final int column = this.phenotypesTable.getTableHeader().columnAtPoint(p); final int row = this.phenotypesTable.rowAtPoint(p); if (!this.tableFormat.getColumnClass(column).equals(OBOObject.class)) return; final Phenotype phenotype = this.getController().getPhenotypesForCurrentStateSelection().get(row); final OBOClass term = (OBOClass)(this.tableFormat.getColumnValue(phenotype, column)); final PostCompositionEditor pce = new PostCompositionEditor(this.tableFormat.getColumnTermSet(column), this.getController().getOntologyController().getRelationsTermSet(), this.getController().getOntologyController().getPostCompositionFillersTermSet(), this.getController().getOntologyCoordinator()); pce.setTerm(term); final int result = pce.runPostCompositionDialog(this); if (result == JOptionPane.OK_OPTION) { this.tableFormat.setColumnValue(phenotype, pce.getTerm(), column); } } private void copyTermAtPoint(Point p) { final int column = this.phenotypesTable.getTableHeader().columnAtPoint(p); final int row = this.phenotypesTable.rowAtPoint(p); if (!this.tableFormat.getColumnClass(column).equals(OBOObject.class)) return; final Phenotype phenotype = this.getController().getPhenotypesForCurrentStateSelection().get(row); final OBOClass term = (OBOClass)(this.tableFormat.getColumnValue(phenotype, column)); final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); final TermSelection termSelection = new TermSelection(term); log().debug("Putting term on clipboard: " + term); clipboard.setContents(termSelection, null); } private void pasteTermAtPoint(Point p) { final int column = this.phenotypesTable.getTableHeader().columnAtPoint(p); final int row = this.phenotypesTable.rowAtPoint(p); if (!this.tableFormat.getColumnClass(column).equals(OBOObject.class)) return; final Phenotype phenotype = this.getController().getPhenotypesForCurrentStateSelection().get(row); final Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); if (Arrays.asList(clipboard.getAvailableDataFlavors()).contains(TermSelection.termFlavor)) { try { clipboard.getData(DataFlavor.stringFlavor); log().debug("Made it past string flavor"); final Object data = clipboard.getData(TermSelection.termFlavor); //final IdentifiedObject obj = this.getController().getOntologyController().getOBOSession().getObject(data.toString()); if (data instanceof TermTransferObject) { final OBOClass term = ((TermTransferObject)data).getTerm(this.getController().getOntologyController().getOBOSession()); this.tableFormat.setColumnValue(phenotype, term, column); } else { log().error("The object on the clipboard was not an OBOClass"); } } catch (UnsupportedFlavorException e) { log().error("The clipboard didn't have the term flavor, although it said it did", e); } catch (IOException e) { log().error("Couldn't read from the clipboard", e); } } } private JToolBar createToolBar() { final JToolBar toolBar = new JToolBar(); this.addPhenotypeButton = new JButton(new AbstractAction(null, new ImageIcon(this.getClass().getResource("/org/phenoscape/view/images/list-add.png"))) { @Override public void actionPerformed(ActionEvent e) { addPhenotype(); } }); this.addPhenotypeButton.setToolTipText("Add Phenotype"); toolBar.add(this.addPhenotypeButton); this.deletePhenotypeButton = new JButton(new AbstractAction(null, new ImageIcon(this.getClass().getResource("/org/phenoscape/view/images/list-remove.png"))) { @Override public void actionPerformed(ActionEvent e) { deleteSelectedPhenotype(); } }); this.deletePhenotypeButton.setToolTipText("Delete Phenotype"); toolBar.add(this.deletePhenotypeButton); toolBar.setFloatable(false); return toolBar; } private JPopupMenu createTablePopupMenu() { final JPopupMenu menu = new JPopupMenu(); menu.add(new AbstractAction("Copy Term") { @Override public void actionPerformed(ActionEvent e) { copyTermAtPoint(tablePopup.getLocation()); } }); menu.add(new AbstractAction("Paste Term") { @Override public void actionPerformed(ActionEvent e) { pasteTermAtPoint(tablePopup.getLocation()); } }); menu.add(new AbstractAction("Edit Post-composed Term") { @Override public void actionPerformed(ActionEvent e) { runPostCompositionForTermAtPoint(tablePopup.getLocation()); } }); return menu; } private class PhenotypesTableFormat implements WritableTableFormat<Phenotype>, AdvancedTableFormat<Phenotype> { @Override public boolean isEditable(Phenotype phenotype, int column) { return true; } @Override public Phenotype setColumnValue(Phenotype phenotype, Object editedValue, int column) { switch (column) { case 0: phenotype.setEntity((OBOClass)editedValue); break; case 1: phenotype.setQuality((OBOClass)editedValue); break; case 2: phenotype.setRelatedEntity((OBOClass)editedValue); break; case 3: phenotype.setCount((Integer)editedValue); break; case 4: phenotype.setMeasurement((Float)editedValue); break; case 5: phenotype.setUnit((OBOClass)editedValue); break; case 6: phenotype.setComment(editedValue.toString()); break; } return phenotype; } @Override public int getColumnCount() { return 7; } @Override public String getColumnName(int column) { switch (column) { case 0: return "Entity"; case 1: return "Quality"; case 2: return "Related Entity"; case 3: return "Count"; case 4: return "Measurement"; case 5: return "Unit"; case 6: return "Comment"; default: return null; } } public TermSet getColumnTermSet(int column) { switch (column) { case 0: return getController().getOntologyController().getEntityTermSet(); case 1: return getController().getOntologyController().getQualityTermSet(); case 2: return getController().getOntologyController().getRelatedEntityTermSet(); case 3: return null; case 4: return null; case 5: return getController().getOntologyController().getUnitTermSet(); case 6: return null; default: return null; } } public TableCellEditor getColumnEditor(int column) { switch (column) { case 0: return TermAutocompleteFieldFactory.createAutocompleteEditor(this.getColumnTermSet(column), getController().getOntologyCoordinator()); case 1: return TermAutocompleteFieldFactory.createAutocompleteEditor(this.getColumnTermSet(column), getController().getOntologyCoordinator()); case 2: return TermAutocompleteFieldFactory.createAutocompleteEditor(this.getColumnTermSet(column), getController().getOntologyCoordinator()); case 3: return null; case 4: return null; case 5: return TermAutocompleteFieldFactory.createAutocompleteEditor(this.getColumnTermSet(column), getController().getOntologyCoordinator()); case 6: return null; default: return null; } } @Override public Object getColumnValue(Phenotype phenotype, int column) { switch (column) { case 0: return phenotype.getEntity(); case 1: return phenotype.getQuality(); case 2: return phenotype.getRelatedEntity(); case 3: return phenotype.getCount(); case 4: return phenotype.getMeasurement(); case 5: return phenotype.getUnit(); case 6: return phenotype.getComment(); default: return null; } } @Override public Class<?> getColumnClass(int column) { switch (column) { case 0: return OBOObject.class; case 1: return OBOObject.class; case 2: return OBOObject.class; case 3: return Integer.class; case 4: return Float.class; case 5: return OBOObject.class; case 6: return String.class; default: return null; } } @Override public Comparator<?> getColumnComparator(int column) { switch (column) { case 0: return GlazedLists.comparableComparator(); case 1: return GlazedLists.comparableComparator(); case 2: return GlazedLists.comparableComparator(); case 3: return GlazedLists.comparableComparator(); case 4: return GlazedLists.comparableComparator(); case 5: return GlazedLists.comparableComparator(); case 6: return Strings.getNaturalComparator(); default: return null; } } } private class StateSelectionListener implements ListSelectionListener { public StateSelectionListener() { stateSelectionDidChange(); } @Override public void valueChanged(ListSelectionEvent e) { stateSelectionDidChange(); } } private class PhenotypeSelectionListener implements ListSelectionListener { public PhenotypeSelectionListener() { phenotypeSelectionDidChange(); } @Override public void valueChanged(ListSelectionEvent e) { phenotypeSelectionDidChange(); } } }