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