/*
* CCVisu is a tool for visual graph clustering
* and general force-directed graph layout.
* This file is part of CCVisu.
*
* Copyright (C) 2005-2012 Dirk Beyer
*
* CCVisu is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* CCVisu 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CCVisu; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please find the GNU Lesser General Public License in file
* license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
*
* Dirk Beyer (firstname.lastname@uni-passau.de)
* University of Passau, Bavaria, Germany
*/
package org.sosy_lab.ccvisu.ui.controlpanel;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.ListCellRenderer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.sosy_lab.ccvisu.Options;
import org.sosy_lab.ccvisu.Options.OptionsEnum;
import org.sosy_lab.ccvisu.clustering.ClustererBase;
import org.sosy_lab.ccvisu.clustering.ClustererMinDist;
import org.sosy_lab.ccvisu.clustering.ClustererMinDistPerc;
import org.sosy_lab.ccvisu.clustering.interfaces.Clusterer;
import org.sosy_lab.ccvisu.graph.GraphData;
import org.sosy_lab.ccvisu.ui.StopTaskButton;
import org.sosy_lab.util.interfaces.WorkerManager;
import com.google.common.base.Preconditions;
/**
* User interface for the graph clustering.
*/
@SuppressWarnings("serial")
public class ToolPanelClusterer extends ControlPanel {
private static final String TOOLTIP_RUN_CLUSTERING = "<html>Agglomerative clustering: initially, every node is in its own cluster; successive merge clusters "
+ "<br />until there is no other cluster to merge with within a certain distance (measured from the barycenters) "
+ "<br />or the least number of partitions has already been reached.</html>";
private static final char STOP_MNEMONIC = 'S';
private static final char RUN_MNEMONIC = 'R';
private static final String CLUSTERER_THREAD_NAME = "Clusterer";
private static final int DEFAULT_MIN_DIST = 40;
private JComboBox<Class<? extends Clusterer>> clustererClassComboBox = new JComboBox<>();
private JSpinner numOfClustersSpinner = new JSpinner();
private JSlider minDistanceSlider = new JSlider(0, 100);
private JLabel minDistanceValueLabel = new JLabel();
private JButton runButton = new JButton("Run clusterer");
private StopTaskButton stopTaskButton;
private final Options options;
private final GraphData graph;
private final WorkerManager workerManager;
public ToolPanelClusterer(Options options, WorkerManager workerManager) {
Preconditions.checkNotNull(options);
Preconditions.checkNotNull(options.graph);
Preconditions.checkNotNull(workerManager);
this.options = options;
this.graph = options.graph;
this.workerManager = workerManager;
initComponents();
}
private void initComponents() {
setLayout(new BorderLayout());
setMinimumSize(new Dimension(500, 150));
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new GridBagLayout());
add(mainPanel, BorderLayout.CENTER);
JPanel minDistancePanel = new JPanel(new BorderLayout());
minDistanceSlider.setValue(DEFAULT_MIN_DIST);
minDistanceSlider.setPaintTicks(false);
minDistanceSlider.setPaintLabels(false);
minDistancePanel.add(minDistanceSlider, BorderLayout.CENTER);
minDistancePanel.add(minDistanceValueLabel, BorderLayout.EAST);
addOptionControls(mainPanel, "Clusterer class:", clustererClassComboBox);
addOptionControls(mainPanel, "Minimal cluster distance (%):", minDistancePanel);
addOptionControls(mainPanel, "Number of partitions (at least):", numOfClustersSpinner);
JPanel controlPanel = new JPanel();
stopTaskButton = new StopTaskButton(workerManager);
stopTaskButton.setMnemonic(STOP_MNEMONIC);
runButton.setMnemonic(RUN_MNEMONIC);
controlPanel.add(runButton);
controlPanel.add(stopTaskButton);
add(controlPanel, BorderLayout.SOUTH);
registerAvailableClusterer();
registerListeners();
runButton.setToolTipText(TOOLTIP_RUN_CLUSTERING);
}
/**
* Register available clusterer.
*/
private void registerAvailableClusterer() {
clustererClassComboBox.addItem(ClustererMinDistPerc.class);
clustererClassComboBox.addItem(ClustererMinDist.class);
clustererClassComboBox.setRenderer(new ClustererCellRenderer());
clustererClassComboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
minDistanceSlider.setEnabled(clustererClassComboBox.getSelectedItem() == ClustererMinDistPerc.class);
}
});
}
/**
* Register action handlers / event listeners.
*/
private void registerListeners() {
minDistanceSlider.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent pE) {
minDistanceValueLabel.setText(String.format("%d", minDistanceSlider.getValue()));
}
});
runButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent pE) {
ClustererBase clusterer = createParameterizedClusterer();
graph.clearGroups();
workerManager.addAndRunTask(clusterer, CLUSTERER_THREAD_NAME);
}
});
}
/**
* @return Instance of the chosen clusterer.
*/
protected ClustererBase createParameterizedClusterer() {
int numOfclusters = (Integer) numOfClustersSpinner.getValue();
double minDistanceFract = minDistanceSlider.getValue() / 100.0;
double mergeDistanceFract = options.getOption(OptionsEnum.clusterMergeDistancePercent).getFloat();
Class<?> clustererClass = (Class<?>) clustererClassComboBox.getSelectedItem();
if (clustererClass == ClustererMinDist.class) {
return new ClustererMinDist(graph, numOfclusters);
} else if (clustererClass == ClustererMinDistPerc.class) {
return new ClustererMinDistPerc(graph, numOfclusters, mergeDistanceFract, minDistanceFract);
} else {
throw new RuntimeException("No valid clusterer selected!");
}
}
/**
* Render the class name of a clusterer.
*/
private static class ClustererCellRenderer extends JLabel implements ListCellRenderer<Class<? extends Clusterer>> {
@Override
public Component getListCellRendererComponent(JList<? extends Class<? extends Clusterer>> list,
Class<? extends Clusterer> value, int index, boolean isSelected, boolean cellHasFocus) {
setText(value.getSimpleName());
return this;
}
}
}