package uk.ac.rhul.cs.cl1.ui.cytoscape;
import giny.model.Node;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JPopupMenu;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import cytoscape.CyNetwork;
import cytoscape.Cytoscape;
import cytoscape.view.CyNetworkView;
import cytoscape.view.cytopanels.CytoPanel;
import cytoscape.view.cytopanels.CytoPanelState;
import uk.ac.rhul.cs.cl1.NodeSet;
import uk.ac.rhul.cs.cl1.ui.NodeSetTableModel;
import uk.ac.rhul.cs.cl1.ui.PopupMenuTrigger;
import uk.ac.rhul.cs.cl1.ui.RemoveClusterFromResultAction;
import uk.ac.rhul.cs.cl1.ui.ResultViewerPanel;
/**
* Result viewer panel with some added functionality to ensure better integration
* with Cytoscape
*
* @author tamas
*/
public class CytoscapeResultViewerPanel extends ResultViewerPanel implements
ListSelectionListener {
/**
* Serial number of this result viewer panel.
*/
private Integer serialNumber = null;
/**
* Last used serial number of result viewer panels.
*
* This is used to assign unique numbers to each result panel in Cytoscape
*/
static private int lastUsedSerialNumber = 1;
/**
* Mapping from node IDs to real Cytoscape {@link Node} objects
*/
protected List<Node> nodeMapping;
/** Reference to the original Cytoscape network from which the results were calculated */
protected WeakReference<CyNetwork> networkRef;
/** Reference to a Cytoscape network view that will be used to highlight nodes in the selected nodeset */
protected WeakReference<CyNetworkView> networkViewRef;
/**
* The popup menu that comes up when right clicking on a cluster
*/
protected JPopupMenu clusterPopup;
/**
* The "Copy to clipboard" element of the popup menu
*/
protected AbstractAction copyToClipboardAction;
/**
* The "Extract selected cluster" element of the popup menu
*/
protected AbstractAction extractClusterAction;
/**
* The "Save selected cluster..." element of the popup menu
*/
protected AbstractAction saveClusterAction;
/**
* The "Remove" element of the popup menu
*/
protected AbstractAction removeClusterAction;
/**
* The "Convert to Cytoscape group..." element of the popup menu
*/
protected AbstractAction saveClusterAsCyGroupAction;
/**
* Creates a result viewer panel associated to the given {@link CyNetwork}
* and {@link CyNetworkView}
*
* It will be assumed that the results shown in this panel were generated
* from the given network, and the given view will be used to update the
* selection based on the current nodeset in the table.
*
* @param network a network from which the results were generated
* @param networkView a network view that will be used to show the clusters
*/
public CytoscapeResultViewerPanel(CyNetwork network, CyNetworkView networkView) {
super();
initializeClusterPopup();
this.networkRef = new WeakReference<CyNetwork>(network);
this.networkViewRef = new WeakReference<CyNetworkView>(networkView);
/* Listen to table selection changes */
this.table.getSelectionModel().addListSelectionListener(this);
/* Listen to double click events on the table */
this.table.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
extractClusterAction.actionPerformed(null);
}
}
});
/* Add popup menu to the cluster selection table */
this.table.addMouseListener(new PopupMenuTrigger(clusterPopup));
/* Add the bottom buttons */
/* JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
JButton closeButton = new JButton(new CloseAction(this));
buttonPanel.add(closeButton);
this.add(buttonPanel, BorderLayout.SOUTH); */
this.addAction(new FindAction(this));
this.addAction(new SaveClusteringAction(this));
this.addAction(new CloseAction(this));
}
/**
* Adds the result panel to Cytoscape's designated result panel area
*/
public void addToCytoscapeResultPanel() {
if (serialNumber == null) {
serialNumber = lastUsedSerialNumber;
lastUsedSerialNumber++;
}
CytoPanel cytoPanel = Cytoscape.getDesktop().getCytoPanel(SwingConstants.EAST);
String title = "ClusterONE result "+serialNumber;
cytoPanel.add(title, null, this, title);
cytoPanel.setSelectedIndex(cytoPanel.indexOfComponent(this));
/* Ensure that the panel is visible */
if (cytoPanel.getState() == CytoPanelState.HIDE)
cytoPanel.setState(CytoPanelState.DOCK);
}
/**
* Converts an integer iterable yielding node IDs to a list of Cytoscape nodes
*
* As {@link NodeSet}s are iterable, this method works with {@link NodeSet}s directly.
*/
protected List<Node> convertIterableToCytoscapeNodeList(Iterable<Integer> iterable) {
List<Node> result = new ArrayList<Node>();
for (int idx: iterable) {
Node node = this.nodeMapping.get(idx);
if (node == null)
continue;
result.add(node);
}
return result;
}
/**
* Retrieves the Cytoscape network associated to this panel
*/
public CyNetwork getNetwork() {
if (networkRef == null)
return null;
return networkRef.get();
}
/**
* Retrieves the Cytoscape network view associated to this panel
*/
public CyNetworkView getNetworkView() {
if (networkViewRef == null)
return null;
return networkViewRef.get();
}
/**
* Retrieves the mapping from integer node IDs to real Cytoscape {@link Node} objects
*/
public List<Node> getNodeMapping() {
return this.nodeMapping;
}
/**
* Retrieves the set of Cytoscape nodes associated to the selected {@link NodeSet}.
*
* If multiple {@link NodeSet}s are selected, the corresponding Cytoscape nodes will be
* merged into a single set.
*
* If nothing is selected in the table, an empty set will be returned.
*/
public List<Node> getSelectedCytoscapeNodeSet() {
Set<Integer> selectedIndices = new TreeSet<Integer>();
/* Take the union of all indices. This step is necessary because CyNodes are not hashable */
for (NodeSet selectedNodeSet: this.getSelectedNodeSets()) {
for (Integer idx: selectedNodeSet) {
selectedIndices.add(idx);
}
}
/* Convert indices to CyNodes */
return this.convertIterableToCytoscapeNodeList(selectedIndices);
}
/**
* Retrieves the set of Cytoscape nodes associated to the selected {@link NodeSet}.
*
* If multiple {@link NodeSet}s are selected, the corresponding Cytoscape nodes will be
* returned as individual lists.
*
* If nothing is selected in the table, an empty list will be returned.
*/
public List<List<Node>> getSelectedCytoscapeNodeSets() {
List<List<Node>> result = new ArrayList<List<Node>>();
for (NodeSet selectedNodeSet: this.getSelectedNodeSets()) {
result.add(this.convertIterableToCytoscapeNodeList(selectedNodeSet));
}
return result;
}
/**
* Retrieves the set of Cytoscape nodes associated to all {@link NodeSet} instances
* in this result viewer.
*/
public List<List<Node>> getAllCytoscapeNodeSets() {
NodeSetTableModel model = this.getTableModel();
int numRows = model.getRowCount();
List<List<Node>> result = new ArrayList<List<Node>>();
for (int i = 0; i < numRows; i++) {
result.add(this.convertIterableToCytoscapeNodeList(model.getNodeSetByIndex(i)));
}
return result;
}
/**
* Initializes the cluster popup menu
*/
private void initializeClusterPopup() {
clusterPopup = new JPopupMenu();
copyToClipboardAction = new CopyClusterToClipboardAction(this);
copyToClipboardAction.setEnabled(false);
clusterPopup.add(copyToClipboardAction);
extractClusterAction = new ExtractClusterAction(this);
extractClusterAction.setEnabled(false);
clusterPopup.add(extractClusterAction);
saveClusterAction = new SaveClusterAction(this);
saveClusterAction.setEnabled(false);
clusterPopup.add(saveClusterAction);
removeClusterAction = new RemoveClusterFromResultAction(this);
removeClusterAction.setEnabled(false);
clusterPopup.add(removeClusterAction);
/*
clusterPopup.addSeparator();
saveClusterAsCyGroupAction = new SaveClusterAsCyGroupAction(this);
saveClusterAsCyGroupAction.setEnabled(false);
clusterPopup.add(saveClusterAsCyGroupAction);
*/
}
/**
* Sets the mapping from integer node IDs to real Cytoscape {@link Node} objects
*/
public void setNodeMapping(List<Node> mapping) {
this.nodeMapping = mapping;
}
/**
* Method called when the table selection changes
* @param event event describing how the selection changed
*/
public void valueChanged(ListSelectionEvent event) {
CyNetwork network = this.getNetwork();
CyNetworkView networkView = this.getNetworkView();
if (network == null) {
copyToClipboardAction.setEnabled(false);
extractClusterAction.setEnabled(false);
saveClusterAction.setEnabled(false);
return;
}
List<Node> nodes = this.getSelectedCytoscapeNodeSet();
network.unselectAllNodes();
network.unselectAllEdges();
network.setSelectedNodeState(nodes, true);
network.setSelectedEdgeState(network.getConnectingEdges(nodes), true);
networkView.redrawGraph(false, true);
boolean enabled = nodes.size() > 0;
extractClusterAction.setEnabled(enabled);
copyToClipboardAction.setEnabled(enabled);
saveClusterAction.setEnabled(enabled);
removeClusterAction.setEnabled(enabled);
// saveClusterAsCyGroupAction.setEnabled(enabled);
}
class CloseAction extends AbstractAction {
CytoscapeResultViewerPanel panel;
public CloseAction(CytoscapeResultViewerPanel panel) {
super("Close");
this.panel = panel;
this.putValue(AbstractAction.SMALL_ICON,
new ImageIcon(this.getClass().getResource("../../resources/close.png"))
);
this.putValue(AbstractAction.SHORT_DESCRIPTION,
"Close this result panel");
}
public void actionPerformed(ActionEvent event) {
CytoPanel cytoPanel = Cytoscape.getDesktop().getCytoPanel(SwingConstants.EAST);
cytoPanel.remove(panel);
if (cytoPanel.getCytoPanelComponentCount() == 0) {
cytoPanel.setState(CytoPanelState.HIDE);
}
}
}
}