/*
* 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;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Arrays;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.filechooser.FileFilter;
import org.sosy_lab.ccvisu.CCVisuController;
import org.sosy_lab.ccvisu.Options;
import org.sosy_lab.ccvisu.Options.InFormat;
import org.sosy_lab.ccvisu.Options.OptionsEnum;
import org.sosy_lab.ccvisu.Options.OutFormat;
import org.sosy_lab.ccvisu.readers.ReaderData;
import org.sosy_lab.ccvisu.readers.ReaderDataLayoutLAY;
import org.sosy_lab.ccvisu.ui.FrameDisplay.GraphCanvas;
import org.sosy_lab.ccvisu.ui.FrameDisplay.LoadDirection;
import org.sosy_lab.ccvisu.ui.controlpanel.OptionsPanelVisualization;
import org.sosy_lab.ccvisu.writers.Writer;
import org.sosy_lab.ccvisu.writers.WriterDataLayoutDISP;
import org.sosy_lab.ccvisu.writers.WriterDataLayoutLAY;
import org.sosy_lab.ccvisu.writers.WriterDataLayoutSVG;
import org.sosy_lab.ccvisu.writers.WriterDataLayoutVRML;
import org.sosy_lab.util.CCVisuWorkerManager;
import org.sosy_lab.util.interfaces.ProgressReceiver;
import org.sosy_lab.util.interfaces.WorkerManager;
/**
* The application's main frame.
*/
public class FrameMain extends JFrame {
private static final long serialVersionUID = -4778809967193305287L;
private JPanel mainContainer = new JPanel();
private WorkerManager workerManager;
private final Options options;
private JDialog optionsDialog;
private PrintWriter printer;
private CCVisuController ccvisuController;
private FrameTools propertiesWindow;
private FrameMenuBar menuBar;
private SearchPanel searchPanel;
private StatusBar statusBar;
private JInternalFrame graphLayoutFrame;
private WriterDataLayoutDISP writer;
private GraphCanvas canvas;
private FrameDisplay display;
public FrameMain(CCVisuController ccvisuController, Options options,
JInternalFrame graphLayoutFrame) {
// set fields defined by parametes
this.options = options;
this.graphLayoutFrame = graphLayoutFrame;
this.ccvisuController = ccvisuController;
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setSize(1280, 768);
// create printer and writer
printer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
writer = new WriterDataLayoutDISP(printer, options.graph, options.frame, options);
workerManager = new CCVisuWorkerManager();
// set display and canvas
display = writer.getDisplay();
canvas = display.getCanvas();
initComponents();
workerManager.addReceiver(statusBar);
updateWindow();
}
private void initComponents() {
searchPanel = new SearchPanel();
searchPanel.setVisible(false);
statusBar = new StatusBar();
mainContainer.setBorder(javax.swing.BorderFactory.createEmptyBorder(4, 4, 4, 4));
mainContainer.setLayout(new BorderLayout());
mainContainer.add(searchPanel, BorderLayout.NORTH);
mainContainer.add(graphLayoutFrame, BorderLayout.CENTER);
mainContainer.add(statusBar, BorderLayout.SOUTH);
menuBar = new FrameMenuBar(this, options);
setLayout(new BorderLayout());
add(mainContainer, BorderLayout.CENTER);
setJMenuBar(menuBar);
propertiesWindow = new FrameTools(this.options, writer, workerManager);
propertiesWindow.setVisible(true);
add(propertiesWindow, BorderLayout.EAST);
menuBar.updateMenuStatus();
}
/**
* Set the window title. Include the name of the active file.
*/
private void updateWindow() {
File inputFile = new File(options.getOption(OptionsEnum.inputName).getString());
setTitle("CCVisu | www.sosy-lab.org | ".concat(inputFile.getName()));
if (canvas != null) {
canvas.updateAndPaint();
}
}
public boolean isSearchPanelShown() {
return searchPanel.isVisible();
}
public void setShowSearchPanel(boolean showSearchPanel) {
searchPanel.setVisible(showSearchPanel);
}
public JComponent getCanvasContainer() {
return mainContainer;
}
void resetZoom() {
canvas.zoomOut();
}
void showOrHideControlPanel() {
propertiesWindow.setVisible(!propertiesWindow.isVisible());
}
boolean isControlPanelShown() {
if (propertiesWindow == null) {
return false;
} else {
return propertiesWindow.isVisible();
}
}
void openSaveLayoutDialog() {
JFileChooser fileDialog = new JFileChooser(".");
fileDialog.setFileFilter(ReaderData.mkExtensionFileFilter(OutFormat.LAY));
int outcome = fileDialog.showSaveDialog(canvas.getParentFrame());
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(OutFormat.LAY.getFileExtension())) {
fileName += OutFormat.LAY.getFileExtension();
}
// write file layout
writeFileLayout(fileName);
}
}
void openLoadLayoutDialog() {
JFileChooser fileDialog = new JFileChooser(".");
fileDialog.setFileFilter(ReaderData.mkExtensionFileFilter(InFormat.LAY));
int outcome = fileDialog.showOpenDialog(canvas.getParentFrame());
if (outcome == JFileChooser.APPROVE_OPTION) {
assert (fileDialog.getCurrentDirectory() != null);
assert (fileDialog.getSelectedFile() != null);
loadFile(fileDialog.getCurrentDirectory().toString()
+ File.separator + fileDialog.getSelectedFile().getName());
}
}
/**
* Open a dialog to edit various visual options not supported via the menu.
*/
void openVisualOptionsDialog() {
if (this.optionsDialog == null) {
// create the dialog
optionsDialog = new JDialog(this, "Options");
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.add(new OptionsPanelVisualization(options, writer));
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
buttonPane.add(Box.createHorizontalGlue());
JButton okButton = new JButton("Ok");
okButton.setMnemonic('o');
okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
optionsDialog.setVisible(false);
}
});
buttonPane.add(okButton);
buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
panel.add(buttonPane);
optionsDialog.add(panel);
optionsDialog.pack();
optionsDialog.setResizable(false);
okButton.requestFocusInWindow();
}
optionsDialog.setVisible(true);
}
/**
* Open a dialog to load a graph from a supported source and do the actual loading.
*/
void openLoadDialog() {
// Setup filechooser.
JFileChooser fileChooser = new JFileChooser(".");
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
FileFilter defaultFilter = null;
for (final Options.InFormat inFormat : Options.InFormat.values()) {
if (inFormat.isDefaultGuiLoad()) {
FileFilter filter = new FileFilter() {
@Override
public String getDescription() {
return inFormat.getDescription();
}
@Override
public boolean accept(File file) {
return (file.isDirectory()
|| file.getAbsoluteFile().getName().toLowerCase().endsWith(inFormat.getFileExtension()));
}
};
fileChooser.addChoosableFileFilter(filter);
if (defaultFilter == null || inFormat == Options.InFormat.RSF) {
defaultFilter = filter;
}
}
}
fileChooser.setFileFilter(defaultFilter);
// Show filechooser.
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
String fileName = fileChooser.getSelectedFile().getAbsoluteFile().getAbsolutePath();
options.inFormat = Options.getInFormatOfFile(fileName);
options.getOption(OptionsEnum.inputName).set(fileName);
try {
// Load the choosen file.
ccvisuController.processVisual(options, writer.getDisplay());
this.updateWindow();
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "Opening file failed: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
}
}
/**
* Open a dialog to save a graph in a supported format and do the actual saving.
*/
void openSaveDialog() {
JFileChooser fileChooser = new JFileChooser(".");
fileChooser.setFileSelectionMode(JFileChooser.SAVE_DIALOG);
for (final Options.OutFormat outFormat : Options.OutFormat.values()) {
switch (outFormat) {
case DISP:
case NULL:
break;
default:
fileChooser.addChoosableFileFilter(new FileFilter() {
@Override
public String getDescription() {
return outFormat.getDescription();
}
@Override
public boolean accept(File file) {
return (file.isDirectory()
|| file.getAbsoluteFile().getName().toLowerCase().endsWith(outFormat.getFileExtension()));
}
@Override
public String toString() {
return outFormat.getFileExtension();
}
});
}
}
if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
String fileName = fileChooser.getSelectedFile().getAbsoluteFile().getAbsolutePath();
OutFormat outFormat = Options.getOutFormatByExtension(fileChooser.getFileFilter().toString());
try {
ccvisuController.writeOutput(options.graph, fileName, outFormat, options);
JOptionPane.showMessageDialog(this, "File successfully written!",
"Done", JOptionPane.INFORMATION_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "Writing file failed: " + e.getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
e.printStackTrace();
}
}
}
/**
* Show an About-Dialog.
*/
void openAboutDialog() {
JOptionPane.showMessageDialog(this, options.versionMessage(), "About CCVisu",
JOptionPane.INFORMATION_MESSAGE);
}
/**
* Load next/previous layout from file,
* to leaf through the directory file by file.
* @param loadDirection determines whether next or previous file
* should be loaded.
*/
void loadOtherFile(LoadDirection loadDirection) {
final String fileName = options.getOption(OptionsEnum.inputName).getString();
String path = ".";
if (fileName.lastIndexOf(File.separator) != -1) {
path = fileName.substring(0, fileName.lastIndexOf(File.separator));
}
String[] fileList = (new File(path)).list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".lay");
}
});
if (fileList.length == 0) {
System.err.println("No layout (.lay) file available in current directory.");
return;
}
Arrays.sort(fileList);
int fileCurrent = Arrays.binarySearch(fileList, new File(fileName).getName());
if (fileCurrent < 0) { // File not found in current directory.
fileCurrent = 0;
} else if (loadDirection == LoadDirection.NEXT && (fileCurrent < fileList.length - 1)) {
fileCurrent++;
} else if (loadDirection == LoadDirection.PREV && (fileCurrent > 0)) {
fileCurrent--;
}
// Load next layout.
options.getOption(OptionsEnum.inputName).set(path + File.separator + fileList[fileCurrent]);
loadFile(options.getOption(OptionsEnum.inputName).getString());
}
private void loadFile(String fileName) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader(fileName));
} catch (Exception e) {
System.err.println("Exception while opening file '" + fileName
+ "' for reading: ");
System.err.println(e);
}
// Read layout from file.
options.graph.reset();
new ReaderDataLayoutLAY(reader, options.verbosity).read(options.graph);
updateWindow();
// Close the input file.
try {
reader.close();
} catch (Exception e) {
System.err.println("Exception while closing input file: ");
System.err.println(e);
}
}
/**
* Writes layout to file using an implementation of class <code>WriterData</code>.
* Call-back method, invoked from within ScreenDisplay.
* @param fileName Name of the output file to write the layout to.
*/
public void writeFileLayout(String fileName) {
try {
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
Writer dataWriter = new WriterDataLayoutLAY(out, options.graph, options); // Default, also .lay.
if (fileName.endsWith(".svg")) {
dataWriter = new WriterDataLayoutSVG(out, options.graph, options);
} else if (fileName.endsWith(".wrl")) {
dataWriter = new WriterDataLayoutVRML(out, options.graph, options);
}
dataWriter.write();
out.flush();
out.close();
options.infoCollector.addMessage("Wrote layout to output file '" + fileName + "'.");
} catch (Exception e) {
System.err.println("Exception while writing file '" + fileName + "': ");
System.err.println(e);
}
}
/**
* This panel provides a search field to filter (and mark) vertices by a given name.
*/
private class SearchPanel extends JPanel {
private static final long serialVersionUID = -2212083044165367854L;
private JTextField searchTextField = new JTextField();
public SearchPanel() {
setLayout(new BorderLayout());
setBorder(javax.swing.BorderFactory.createEmptyBorder(3, 0, 3, 0));
add(new JLabel("Search for node: "), BorderLayout.WEST);
add(searchTextField, BorderLayout.CENTER);
searchTextField.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
if (searchTextField.getText().isEmpty()) {
writer.resetMarkedVertices();
} else {
writer.markVertices(".*" + searchTextField.getText() + ".*");
}
canvas.updateAndPaint();
}
}
});
}
}
private class StatusBar extends JPanel implements ProgressReceiver {
private static final long serialVersionUID = -3044688812187823110L;
private JProgressBar progressBar = new JProgressBar();
private JLabel statusInfo = new JLabel();
public StatusBar() {
setLayout(new BorderLayout(3, 3));
setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
add(statusInfo, BorderLayout.WEST);
add(progressBar, BorderLayout.CENTER);
setEnabled(false);
}
@Override
public void processChange(final Object source, final int value, final int max,
final String message) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// update progress bar
progressBar.setMaximum(max);
progressBar.setValue(value);
progressBar.setToolTipText(message);
setVisible(progressBar.isEnabled());
if (value == max) {
progressBar.setValue(0);
progressBar.setToolTipText(null);
}
// update label
statusInfo.setText(message);
}
});
}
}
}