package uk.ac.rhul.cs.cl1.ui.cytoscape3; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JPanel; import org.cytoscape.application.events.SetCurrentNetworkEvent; import org.cytoscape.application.events.SetCurrentNetworkListener; import org.cytoscape.application.swing.CySwingApplication; import org.cytoscape.application.swing.CytoPanel; import org.cytoscape.application.swing.CytoPanelComponent; import org.cytoscape.application.swing.CytoPanelName; import org.cytoscape.application.swing.CytoPanelState; import org.cytoscape.model.CyColumn; import org.cytoscape.model.CyNetwork; import org.cytoscape.model.CyTable; import org.cytoscape.model.events.NetworkAddedEvent; import org.cytoscape.model.events.NetworkAddedListener; import org.cytoscape.model.events.NetworkDestroyedEvent; import org.cytoscape.model.events.NetworkDestroyedListener; import uk.ac.rhul.cs.cl1.ClusterONE; import uk.ac.rhul.cs.cl1.ClusterONEAlgorithmParameters; import uk.ac.rhul.cs.cl1.ui.CollapsiblePanel; import uk.ac.rhul.cs.cl1.ui.ClusterONEAlgorithmParametersPanel; import uk.ac.rhul.cs.cl1.ui.ClusterONEAlgorithmParametersPanel.Section; import uk.ac.rhul.cs.cl1.ui.EmptyIcon; /** * Cytoscape control panel for ClusterONE * * @author tamas */ public class ControlPanel extends JPanel implements CytoPanelComponent, PropertyChangeListener, SetCurrentNetworkListener, NetworkAddedListener, NetworkDestroyedListener { /** Algorithm parameters panel embedded inside the control panel */ protected ClusterONEAlgorithmParametersPanel algorithmParametersPanel; /** Selection info panel embedded inside the control panel */ protected SelectionPropertiesPanel selectionInfoPanel; /** Combobox for selecting the appropriate weight attribute */ protected JComboBox weightAttributeCombo; /** Button for refreshing the list of weight attributes */ protected JButton weightAttributeRefreshButton; /** The ClusterONE Cytoscape app in which this control panel lives */ protected ClusterONECytoscapeApp app; public ControlPanel(ClusterONECytoscapeApp app) { super(); this.app = app; setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JPanel algorithmParametersPanel = constructAlgorithmParametersPanel(); Dimension d = algorithmParametersPanel.getPreferredSize(); d.width = Integer.MAX_VALUE; algorithmParametersPanel.setMaximumSize(d); this.add(algorithmParametersPanel); this.add(constructSelectionInfoPanel()); this.add(constructButtonPanel()); this.add(Box.createVerticalGlue()); updateCollapsiblePanelIcons(); } protected JPanel constructAlgorithmParametersPanel() { URL url = app.getResource(app.getResourcePathName() + "/refresh.png"); /* Algorithm parameters panel */ algorithmParametersPanel = new ClusterONEAlgorithmParametersPanel(); /* Weight attribute name method */ JPanel weightPanel = new JPanel(); weightPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); weightAttributeCombo = new JComboBox(); updateWeightAttributeCombo(); weightAttributeRefreshButton = new JButton(url == null ? new EmptyIcon() : new ImageIcon(url)); weightAttributeRefreshButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { updateWeightAttributeCombo(); } }); if (ClusterONE.isRunningOnMac()) { weightAttributeRefreshButton.putClientProperty("JButton.buttonType", "square"); } weightPanel.add(weightAttributeCombo); weightPanel.add(Box.createHorizontalStrut(3)); weightPanel.add(weightAttributeRefreshButton); algorithmParametersPanel.addComponent(Section.BASIC, "Edge weights:", weightPanel); try { algorithmParametersPanel.monitorComponent(weightAttributeCombo); } catch (Exception ex) { ex.printStackTrace(); } algorithmParametersPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); algorithmParametersPanel.addPropertyChangeListener( "parameters", this); return algorithmParametersPanel; } protected JPanel constructSelectionInfoPanel() { selectionInfoPanel = new SelectionPropertiesPanel(this); return new CollapsiblePanel(selectionInfoPanel, "Selection info"); } protected JPanel constructButtonPanel() { /* Button panel */ JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout()); JButton startButton = new JButton("Start"); startButton.addActionListener(new StartAction(app)); buttonPanel.add(startButton); JButton closeButton = new JButton("Close panel"); closeButton.addActionListener(new CloseControlPanelAction(this)); buttonPanel.add(closeButton); JButton helpButton = new JButton(new HelpAction(app, "control-panel")); if (ClusterONE.isRunningOnMac()) { helpButton.putClientProperty("JButton.buttonType", "help"); helpButton.setText(""); } buttonPanel.add(helpButton); return buttonPanel; } /** * Returns a {@link ClusterONEAlgorithmParameters} object from the current state * of the panel */ ClusterONEAlgorithmParameters getParameters() { return algorithmParametersPanel.getParameters(); } /** * Retrieves the {@link ControlPanel} instance that is shown on the Cytoscape * control panel * * @param app the Cytoscape desktop application instance * @return the {@link ControlPanel} instance or null if no control panel is open */ public static ControlPanel getShownInstance(CySwingApplication app) { CytoPanel panel = app.getCytoPanel(CytoPanelName.WEST); for (int i = 0; i < panel.getCytoPanelComponentCount(); i++) { Component c = panel.getComponentAt(i); if (c instanceof ControlPanel) return (ControlPanel)c; } return null; } /** * Returns the selected weight attribute name * * @return the selected weight attribute name or null if no weights should be used */ public String getWeightAttributeName() { if (weightAttributeCombo.getSelectedIndex() == 0) return null; if (weightAttributeCombo.getSelectedItem() == null) return null; return weightAttributeCombo.getSelectedItem().toString(); } /** Updates the weight attribute combo box to contain the suitable edge attribute * names from the current Cytoscape network */ public void updateWeightAttributeCombo() { Object currentItem = weightAttributeCombo.getSelectedItem(); weightAttributeCombo.removeAllItems(); weightAttributeCombo.addItem("[unweighted]"); CyNetwork network = app.getCurrentNetwork(); if (network == null) return; CyTable nodeTable = network.getDefaultEdgeTable(); ArrayList<String> names = new ArrayList<String>(); for (CyColumn column: nodeTable.getColumns()) { Class<?> type = column.getType(); // Ignore non-numeric columns if (type != Integer.class && type != Long.class && type != Float.class && type != Double.class) continue; // Ignore the SUID column if ("SUID".equals(column.getName())) continue; names.add(column.getName()); } Collections.sort(names); Collections.sort(names); for (String name: names) weightAttributeCombo.addItem(name); if (currentItem != null) weightAttributeCombo.setSelectedItem(currentItem); } /** * Called when the algorithm parameters have changed. * * This method will update the properties of the current selection in the * info panel accordingly. */ public void propertyChange(PropertyChangeEvent evt) { if (evt.getSource() == algorithmParametersPanel) { selectionInfoPanel.setQualityFunction( getParameters().getQualityFunction() ); selectionInfoPanel.updateNodeSetFromSelection(); } } /** * Activates and shows the control panel. */ public void activate() { app.registerService(this, CytoPanelComponent.class); app.registerService(this, NetworkAddedListener.class); app.registerService(this, NetworkDestroyedListener.class); app.registerService(this, SetCurrentNetworkListener.class); CytoPanel cytoPanel = app.getCySwingApplication().getCytoPanel(getCytoPanelName()); /* Ensure that the panel is visible */ if (cytoPanel.getState() == CytoPanelState.HIDE) { cytoPanel.setState(CytoPanelState.DOCK); } setVisible(true); /* Activate the panel */ cytoPanel.setSelectedIndex(cytoPanel.indexOfComponent(getComponent())); /* Update the weight attribute combo */ updateWeightAttributeCombo(); } /** * Deactivates and hides the control panel. */ public void deactivate() { app.unregisterService(this, CytoPanelComponent.class); app.unregisterService(this, NetworkAddedListener.class); app.unregisterService(this, NetworkDestroyedListener.class); app.unregisterService(this, SetCurrentNetworkListener.class); } /** * Updates the icons of the collapsible panels in the control panel. */ private void updateCollapsiblePanelIcons() { ArrayList<Component> components = new ArrayList<Component>(); components.add(this); URL url = app.getResource(app.getResourcePathName() + "/plus.gif"); Icon collapsedIcon = url != null ? new ImageIcon(url) : null; url = app.getResource(app.getResourcePathName() + "/minus.gif"); Icon expandedIcon = url != null ? new ImageIcon(url) : null; while (!components.isEmpty()) { Component component = components.remove(components.size() - 1); if (component instanceof CollapsiblePanel) { ((CollapsiblePanel)component).setCollapsedIcon(collapsedIcon); ((CollapsiblePanel)component).setExpandedIcon(expandedIcon); } if (component instanceof Container) { Container container = (Container)component; components.addAll(Arrays.asList(container.getComponents())); } } } // -------------------------------------------------------------------- // SetCurrentNetworkListener implementation // -------------------------------------------------------------------- /** * Called when a new network is added in the application. */ public void handleEvent(NetworkAddedEvent event) { updateWeightAttributeCombo(); } /** * Called when a network is destroyed in the application. */ public void handleEvent(NetworkDestroyedEvent event) { updateWeightAttributeCombo(); } /** * Called when the current network changes in the application. */ public void handleEvent(SetCurrentNetworkEvent event) { updateWeightAttributeCombo(); } // -------------------------------------------------------------------- // CytoPanelComponent implementation // -------------------------------------------------------------------- /** * Returns the component to be placed in Cytoscape. */ public Component getComponent() { return this; } /** * Return the name of the Cytoscape panel in which this component will be placed. */ public CytoPanelName getCytoPanelName() { return CytoPanelName.WEST; } /** * Returns the icon of the panel. */ public Icon getIcon() { // TODO: use the ClusterONE logo? return null; } /** * Returns the title of this panel. */ public String getTitle() { return ClusterONE.applicationName; } }