/* * 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; } } }