/*
* 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.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.EventObject;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumnModel;
import org.sosy_lab.ccvisu.graph.GraphData;
import org.sosy_lab.ccvisu.graph.GraphVertex;
import org.sosy_lab.ccvisu.graph.GraphVertex.Shape;
import org.sosy_lab.ccvisu.graph.Group;
import org.sosy_lab.ccvisu.graph.NameVisibility;
import org.sosy_lab.ccvisu.graph.interfaces.GraphEventListener;
import org.sosy_lab.ccvisu.readers.ReaderData;
import org.sosy_lab.ccvisu.readers.ReaderWriterGroup;
import org.sosy_lab.ccvisu.ui.DialogEditGroup;
import org.sosy_lab.ccvisu.ui.helper.ColorComboBox;
import org.sosy_lab.ccvisu.ui.helper.ShapeComboBox;
import org.sosy_lab.ccvisu.writers.WriterDataLayoutDISP;
import org.sosy_lab.util.Colors;
/**
* a GUI to manage the groups define new, remove, …
*/
@SuppressWarnings("serial")
public class ToolPanelGroups extends ControlPanel {
private static final String GROUPS_FILE_EXT = ".grp";
private final JButton saveButton = new JButton("Save...");
private final JButton loadButton = new JButton("Load...");
private final JButton upButton = new JButton("Up");
private final JButton downButton = new JButton("Down");
private final JButton deleteButton = new JButton("Delete");
private final JButton editButton = new JButton("Edit");
private final JButton newButton = new JButton("New");
private final JButton hideLabelsButton = new JButton("Hide labels");
private final JButton showLabelsButton = new JButton("Show labels");
private JCheckBox showGroupCheckBox;
private JCheckBox showGraphicInfoCheckBox;
private JLabel chosenGroups;
private ColorComboBox colorComboBox;
private ShapeComboBox shapeComboBox;
private JTabbedPane tabbedPane;
private JPanel groupDetailsPanel;
private JPanel groupMeasuresPanel;
private JList<Group> groupsList;
private DefaultListModel<Group> groupsListModel;
private final GraphData graph;
public ToolPanelGroups(WriterDataLayoutDISP writer, GraphData graph) {
this.graph = graph;
initComponents();
assignListeners();
refillGroupList();
enableAvailableControls();
}
private void initComponents() {
this.setLayout(new BorderLayout());
JPanel groupsPanel = new JPanel();
groupsPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
groupsPanel.setLayout(new BorderLayout(0, 0));
add(groupsPanel, BorderLayout.WEST);
groupsListModel = new DefaultListModel<Group>();
groupsList = new JList<>(groupsListModel);
groupsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
groupsList.setBorder(null);
groupsPanel.add(new JScrollPane(groupsList));
JPanel groupEditCtrlPanel = new JPanel();
groupsPanel.add(groupEditCtrlPanel, BorderLayout.SOUTH);
groupEditCtrlPanel.setLayout(new BorderLayout(0, 0));
JToolBar editToolBar = new JToolBar();
editToolBar.setFloatable(false);
groupEditCtrlPanel.add(editToolBar);
editToolBar.add(newButton);
editToolBar.add(editButton);
editToolBar.add(deleteButton);
JToolBar sortToolBar = new JToolBar();
sortToolBar.setFloatable(false);
groupEditCtrlPanel.add(sortToolBar, BorderLayout.EAST);
sortToolBar.add(upButton);
sortToolBar.add(downButton);
upButton.setHorizontalAlignment(SwingConstants.RIGHT);
downButton.setHorizontalAlignment(SwingConstants.RIGHT);
JToolBar storeToolBar = new JToolBar();
storeToolBar.setFloatable(false);
groupsPanel.add(storeToolBar, BorderLayout.NORTH);
storeToolBar.add(loadButton);
storeToolBar.add(saveButton);
// create tabbed pane and its tabs and add the pane to this, i.e., the
// control panel
tabbedPane = new JTabbedPane(SwingConstants.TOP);
groupDetailsPanel = createTabGroupPanel(tabbedPane);
groupMeasuresPanel = createTabMeasuresPanel(tabbedPane);
tabbedPane.addTab("Group(s)", null, groupDetailsPanel, null);
tabbedPane.addTab("Measures", null, groupMeasuresPanel, null);
createTabMeasuresPanel(tabbedPane);
add(tabbedPane, BorderLayout.CENTER);
}
private void refillGroupList() {
groupsList.setEnabled(false);
Group selectedGroup = groupsList.getSelectedValue();
groupsListModel.clear();
for (Group g : graph.getGroups()) {
groupsListModel.addElement(g);
}
if (selectedGroup != null) {
groupsList.setSelectedValue(selectedGroup, true);
}
groupsList.setEnabled(true);
}
private JPanel createTabGroupPanel(JTabbedPane tabbedPane) {
JPanel result = new JPanel(new BorderLayout(5, 5));
JPanel inner = new JPanel(new GridBagLayout());
chosenGroups = new JLabel("");
chosenGroups.setFont(chosenGroups.getFont().deriveFont(Font.BOLD));
addOptionControls(inner, "", chosenGroups);
addOptionControls(inner, "", new JLabel(""));
showGroupCheckBox = new JCheckBox("Show group");
addOptionControls(inner, "", showGroupCheckBox);
showGraphicInfoCheckBox = new JCheckBox("Graphic informations");
addOptionControls(inner, "", showGraphicInfoCheckBox);
shapeComboBox = new ShapeComboBox(Shape.DISC);
addOptionControls(inner, "Shape", shapeComboBox);
colorComboBox = new ColorComboBox(Colors.RED.get());
addOptionControls(inner, "Color", colorComboBox);
addOptionControls(inner, "", showLabelsButton);
addOptionControls(inner, "", hideLabelsButton);
result.add(inner, BorderLayout.NORTH);
return result;
}
private JPanel createTabMeasuresPanel(JTabbedPane tabbedPane) {
JPanel result = new JPanel();
result.setLayout(new BorderLayout());
return result;
}
protected void assignListeners() {
graph.addOnGroupChangeListener(new GraphEventListener() {
@Override
public void onGraphChangedEvent(EventObject event) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (groupsList.isEnabled()) {
refillGroupList();
}
}
});
}
});
groupsList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent event) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
enableAvailableControls();
if (groupsList.isEnabled()) {
updateGroupInfos();
}
}
});
}
});
// Adds ActionListener for action event "Save button pressed".
// Opens a dialog for choosing the file to which the group definition is saved.
saveButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
// Create a fileDialog and save the info in the selected file
JFileChooser fileDialog = new JFileChooser(".");
fileDialog.setFileFilter(ReaderData.mkExtensionFileFilter(GROUPS_FILE_EXT, "Group Files"));
int outcome = fileDialog.showSaveDialog((Frame) null);
if (outcome == JFileChooser.APPROVE_OPTION) {
assert (fileDialog.getCurrentDirectory() != null);
assert (fileDialog.getSelectedFile() != null);
String fileName = fileDialog.getCurrentDirectory().toString()
+ File.separator
+ fileDialog.getSelectedFile().getName();
if (!fileName.endsWith(GROUPS_FILE_EXT)) {
fileName += GROUPS_FILE_EXT;
}
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
ReaderWriterGroup.write(out, graph);
System.err.println("Wrote groups informations to output '" + fileName + "'.");
out.close();
} catch (IOException e) {
System.err.println("error while writing (GroupManager.saveClt):");
e.printStackTrace();
}
}
}
});
// Adds ActionListener for action event ''Load button pressed''.
// Opens a dialog for choosing the file from which the group definition is loaded.
loadButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent evt) {
// Create a fileDialog and try to load the info from the selected file
JFileChooser fileDialog = new JFileChooser(".");
fileDialog.setFileFilter(ReaderData.mkExtensionFileFilter(GROUPS_FILE_EXT, "Group Files"));
int outcome = fileDialog.showOpenDialog((Frame) null);
if (outcome == JFileChooser.APPROVE_OPTION) {
assert (fileDialog.getCurrentDirectory() != null);
assert (fileDialog.getSelectedFile() != null);
String fileName = fileDialog.getCurrentDirectory().toString()
+ File.separator
+ fileDialog.getSelectedFile().getName();
// Read group definition from file.
BufferedReader fileReader = null;
try {
fileReader = new BufferedReader(new FileReader(fileName));
ReaderWriterGroup.read(fileReader, graph);
// Close the input file.
fileReader.close();
} catch (Exception e) {
System.err.println("Exception while reading from file '" + fileName + "'.");
System.err.println(e);
}
}
}
});
abstract class SelectedGroupsModifier {
public final void run() {
if (groupDetailsPanel.isEnabled()) {
for (Group group : groupsList.getSelectedValuesList()) {
modifyGroup(group);
}
graph.notifyAboutGroupsChange(null);
}
}
public abstract void modifyGroup(Group group);
}
colorComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
(new SelectedGroupsModifier() {
@Override
public void modifyGroup(Group group) {
Color color = colorComboBox.getSelectedColor();
assert (color != null);
group.setColor(color);
shapeComboBox.setColor(color);
}
}).run();
}
});
shapeComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
(new SelectedGroupsModifier() {
@Override
public void modifyGroup(Group group) {
Shape shape = shapeComboBox.getSelectedShape();
assert (shape != null);
group.setShape(shape);
}
}).run();
}
});
showGroupCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
(new SelectedGroupsModifier() {
@Override
public void modifyGroup(Group group) {
group.setVisible(showGroupCheckBox.isSelected());
}
}).run();
}
});
showGraphicInfoCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
(new SelectedGroupsModifier() {
@Override
public void modifyGroup(Group group) {
group.setDrawInfo(showGraphicInfoCheckBox.isSelected());
}
}).run();
}
});
showLabelsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
(new SelectedGroupsModifier() {
@Override
public void modifyGroup(Group group) {
for (GraphVertex graphVertex : group.getNodes()) {
graphVertex.setShowName(NameVisibility.Priority.GROUPS, true);
}
}
}).run();
}
});
hideLabelsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
(new SelectedGroupsModifier() {
@Override
public void modifyGroup(Group group) {
for (GraphVertex graphVertex : group.getNodes()) {
graphVertex.setShowName(NameVisibility.Priority.GROUPS, false);
}
}
}).run();
}
});
// call the method in charge of creating a new group
newButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
String newGroupName = JOptionPane.showInputDialog(null, "Enter a name",
"Name of the new group", JOptionPane.QUESTION_MESSAGE);
if (newGroupName != null) {
if (graph.getGroup(newGroupName) == null) {
graph.addGroup(new Group(newGroupName, graph));
}
}
}
});
// open a dialog to edit the nodes of the selected group
editButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
Group group = groupsList.getSelectedValue();
if (group != null) {
DialogEditGroup dialog = new DialogEditGroup(graph, group);
dialog.setModal(true);
dialog.setVisible(true);
}
}
});
// remove the selected group
deleteButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
Group selectedGroup = groupsList.getSelectedValue();
if (selectedGroup != null) {
graph.removeGroup(selectedGroup);
}
}
});
// put the selected group one rank higher in the rendering list
upButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
Group group = groupsList.getSelectedValue();
if (group != null) {
groupsList.setEnabled(false);
graph.moveGroupUp(group);
groupsList.setEnabled(true);
}
}
});
// put the selected group one rank lower in the rendering list
downButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent event) {
Group group = groupsList.getSelectedValue();
if (group != null) {
groupsList.setEnabled(false);
graph.moveGroupDown(group);
groupsList.setEnabled(true);
}
}
});
}
/**
* refresh the information about the selected group
*/
protected void updateGroupInfos() {
Group group = groupsList.getSelectedValue();
if (group == null) {
return;
}
groupDetailsPanel.setEnabled(false);
try {
String chosenGroupDesc = group.getName();
chosenGroups.setText(chosenGroupDesc);
// refresh color
String colorString = Colors.toString(group.getColor());
if (colorString != null) {
Colors color = Colors.valueOfUpper(colorString);
colorComboBox.setSelectedColor(color);
}
showGraphicInfoCheckBox.setSelected(group.isDrawInfo());
showGroupCheckBox.setSelected(group.isVisible());
// refresh shape
shapeComboBox.setColor(colorComboBox.getSelectedColor());
shapeComboBox.setSelectedItem(group.getShape());
shapeComboBox.validate();
renderGroupMeasures(group);
} finally {
groupDetailsPanel.setEnabled(true);
}
}
protected void renderGroupMeasures(Group group) {
final Object[][] data = new Object[][]{
{"Average radius", Float.toString(group.getAverageRadius())},
{"Number of nodes", Integer.toString(group.getNodes().size())},
{"Barycenter X", Float.toString(group.getX())},
{"Barycenter Y", Float.toString(group.getY())},
{"Barycenter Z", Float.toString(group.getZ())}
};
final Object[] cols = new Object[] {"Measure", "Value"};
ReadOnlyTableModel measuresModel = new ReadOnlyTableModel(data, cols);
JTable measuresTable = new JTable(measuresModel);
measuresTable.setPreferredScrollableViewportSize(new Dimension(200, 200));
TableColumnModel colModel = measuresTable.getColumnModel();
colModel.getColumn(0).setWidth(150);
colModel.getColumn(0).setResizable(false);
colModel.getColumn(1).setWidth(100);
colModel.getColumn(1).setResizable(false);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(measuresTable);
groupMeasuresPanel.removeAll();
groupMeasuresPanel.add(scrollPane, BorderLayout.CENTER);
groupMeasuresPanel.validate();
}
private void enableAvailableControls() {
final int selectedGroups = groupsList.getSelectedIndices().length;
final int availGroups = groupsListModel.getSize();
final boolean graphHasNodes = graph.getVertices().size() > 0;
final boolean groupsSelected = selectedGroups > 0;
downButton.setEnabled(availGroups > 1 && selectedGroups == 1 && groupsList.getSelectedIndex() != availGroups-1);
upButton.setEnabled(availGroups > 1 && selectedGroups == 1 && groupsList.getSelectedIndex() != 0);
editButton.setEnabled(selectedGroups == 1);
deleteButton.setEnabled(selectedGroups > 0);
newButton.setEnabled(graphHasNodes);
saveButton.setEnabled(availGroups > 0);
tabbedPane.setEnabledAt(0, groupsSelected);
tabbedPane.setEnabledAt(1, selectedGroups == 1);
hideLabelsButton.setEnabled(groupsSelected);
showLabelsButton.setEnabled(groupsSelected);
shapeComboBox.setEnabled(groupsSelected);
colorComboBox.setEnabled(groupsSelected);
showGraphicInfoCheckBox.setEnabled(groupsSelected);
showGroupCheckBox.setEnabled(groupsSelected);
}
private static class ReadOnlyTableModel extends DefaultTableModel {
public ReadOnlyTableModel(Object[][] data, Object[] cols) {
super(data, cols);
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
}
}