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