/*
Violet - A program for editing UML diagrams.
Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com)
Alexandre de Pellegrin (http://alexdp.free.fr);
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.horstmann.violet.application.menu;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Comparator;
import java.util.List;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import com.horstmann.violet.application.ApplicationStopper;
import com.horstmann.violet.application.gui.MainFrame;
import com.horstmann.violet.framework.dialog.DialogFactory;
import com.horstmann.violet.framework.file.GraphFile;
import com.horstmann.violet.framework.file.IFile;
import com.horstmann.violet.framework.file.IGraphFile;
import com.horstmann.violet.framework.file.chooser.IFileChooserService;
import com.horstmann.violet.framework.file.export.FileExportService;
import com.horstmann.violet.framework.file.naming.ExtensionFilter;
import com.horstmann.violet.framework.file.naming.FileNamingService;
import com.horstmann.violet.framework.file.persistence.IFilePersistenceService;
import com.horstmann.violet.framework.file.persistence.IFileReader;
import com.horstmann.violet.framework.file.persistence.IFileWriter;
import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector;
import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean;
import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector;
import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean;
import com.horstmann.violet.framework.plugin.IDiagramPlugin;
import com.horstmann.violet.framework.plugin.PluginRegistry;
import com.horstmann.violet.framework.userpreferences.UserPreferencesService;
import com.horstmann.violet.product.diagram.abstracts.IGraph;
import com.horstmann.violet.workspace.IWorkspace;
import com.horstmann.violet.workspace.Workspace;
import com.thoughtworks.xstream.io.StreamException;
/**
* Represents the file menu on the editor frame
*
* @author Alexandre de Pellegrin
*/
@ResourceBundleBean(resourceReference = MenuFactory.class)
public class FileMenu extends JMenu
{
/**
* Default constructor
*
* @param mainFrame
*/
@ResourceBundleBean(key = "file")
public FileMenu(MainFrame mainFrame)
{
ResourceBundleInjector.getInjector().inject(this);
BeanInjector.getInjector().inject(this);
this.mainFrame = mainFrame;
createMenu();
addWindowsClosingListener();
}
/**
* @return 'new file' menu
*/
public JMenu getFileNewMenu()
{
return this.fileNewMenu;
}
/**
* @return recently opened file menu
*/
public JMenu getFileRecentMenu()
{
return this.fileRecentMenu;
}
/**
* Initialize the menu
*/
private void createMenu()
{
initFileNewMenu();
initFileOpenItem();
initFileCloseItem();
initFileRecentMenu();
initFileSaveItem();
initFileSaveAsItem();
initFileExportMenu();
initFilePrintItem();
initFileExitItem();
this.add(this.fileNewMenu);
this.add(this.fileOpenItem);
this.add(this.fileCloseItem);
this.add(this.fileRecentMenu);
this.add(this.fileSaveItem);
this.add(this.fileSaveAsItem);
this.add(this.fileExportMenu);
this.add(this.filePrintItem);
this.add(this.fileExitItem);
}
/**
* Add frame listener to detect closing request
*/
private void addWindowsClosingListener()
{
this.mainFrame.addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent event)
{
stopper.exitProgram(mainFrame);
}
});
}
/**
* Init exit menu entry
*/
private void initFileExitItem()
{
this.fileExitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
stopper.exitProgram(mainFrame);
}
});
if (this.fileChooserService == null) this.fileExitItem.setEnabled(false);
}
/**
* Init export submenu
*/
private void initFileExportMenu()
{
initFileExportToImageItem();
initFileExportToClipboardItem();
initFileExportToPdfItem();
initFileExportToJavaItem();
initFileExportToPythonItem();
this.fileExportMenu.add(this.fileExportToImageItem);
this.fileExportMenu.add(this.fileExportToClipBoardItem);
this.fileExportMenu.add(this.fileExportToPdfItem);
// this.fileExportMenu.add(this.fileExportToJavaItem);
// this.fileExportMenu.add(this.fileExportToPythonItem);
if (this.fileChooserService == null) this.fileExportMenu.setEnabled(false);
}
/**
* Init export to python menu entry
*/
private void initFileExportToPythonItem()
{
this.fileExportToPythonItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
IWorkspace workspace = (Workspace) mainFrame.getActiveWorkspace();
if (workspace != null)
{
}
}
});
}
/**
* Init export to java menu entry
*/
private void initFileExportToJavaItem()
{
this.fileExportToJavaItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
IWorkspace workspace = (Workspace) mainFrame.getActiveWorkspace();
if (workspace != null)
{
}
}
});
}
/**
* Init export to clipboard menu entry
*/
private void initFileExportToClipboardItem()
{
this.fileExportToClipBoardItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
IWorkspace workspace = (Workspace) mainFrame.getActiveWorkspace();
if (workspace != null)
{
workspace.getGraphFile().exportToClipboard();
}
}
});
}
/**
* Init export to image menu entry
*/
private void initFileExportToImageItem()
{
this.fileExportToImageItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
IWorkspace workspace = (Workspace) mainFrame.getActiveWorkspace();
if (workspace != null)
{
try
{
ExtensionFilter[] exportFilters = fileNamingService.getImageExtensionFilters();
IFileWriter fileSaver = fileChooserService.chooseAndGetFileWriter(exportFilters);
OutputStream out = fileSaver.getOutputStream();
if (out != null)
{
String filename = fileSaver.getFileDefinition().getFilename();
for (ExtensionFilter exportFilter : exportFilters)
{
String extension = exportFilter.getExtension();
if (filename.toLowerCase().endsWith(extension.toLowerCase()))
{
String format = extension.replace(".", "");
workspace.getGraphFile().exportImage(out, format);
break;
}
}
}
}
catch (Exception e1)
{
throw new RuntimeException(e1);
}
}
}
});
}
private void initFileExportToPdfItem()
{
this.fileExportToPdfItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
IWorkspace workspace = (Workspace) mainFrame.getActiveWorkspace();
if (workspace != null)
{
try {
ExtensionFilter extensionFilter = fileNamingService.getPdfExtensionFilter();
IFileWriter fileSaver = fileChooserService.chooseAndGetFileWriter(extensionFilter);
OutputStream out = fileSaver.getOutputStream();
if(null == out)
{
throw new IOException("Unable to get output stream for extension "
+ extensionFilter.getExtension());
}
String filename = fileSaver.getFileDefinition().getFilename();
workspace.getGraphFile().exportToPdf(out);
}
catch (IOException e1)
{
String message = MessageFormat.format(fileExportErrorMessage, e1.getMessage());
JOptionPane.showMessageDialog(null, message, fileExportError, JOptionPane.ERROR_MESSAGE);
}
}
}
});
}
/**
* Init 'save as' menu entry
*/
private void initFileSaveAsItem()
{
this.fileSaveAsItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
IWorkspace workspace = (Workspace) mainFrame.getActiveWorkspace();
if (workspace != null)
{
IGraphFile graphFile = workspace.getGraphFile();
graphFile.saveToNewLocation();
userPreferencesService.addRecentFile(graphFile);
}
}
});
if (this.fileChooserService == null) this.fileSaveAsItem.setEnabled(false);
}
/**
* Init save menu entry
*/
private void initFileSaveItem()
{
this.fileSaveItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
IWorkspace workspace = mainFrame.getActiveWorkspace();
if (workspace != null)
{
IGraphFile graphFile = workspace.getGraphFile();
graphFile.save();
userPreferencesService.addRecentFile(graphFile);
}
}
});
if (this.fileChooserService == null || (this.fileChooserService != null && this.fileChooserService.isWebStart()))
{
this.fileSaveItem.setEnabled(false);
}
}
/**
* Init print menu entry
*/
private void initFilePrintItem()
{
this.filePrintItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
IWorkspace workspace = (Workspace) mainFrame.getActiveWorkspace();
if (workspace != null)
{
workspace.getGraphFile().exportToPrinter();
}
}
});
if (this.fileChooserService == null) this.filePrintItem.setEnabled(false);
}
/**
* Init close menu entry
*/
private void initFileCloseItem()
{
this.fileCloseItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
IWorkspace workspace = null;
try
{
workspace = (Workspace) mainFrame.getActiveWorkspace();
}
catch (RuntimeException e)
{
// If no diagram is opened, close app
stopper.exitProgram(mainFrame);
}
if (workspace != null)
{
IGraphFile graphFile = workspace.getGraphFile();
graphFile.removeBackup();
if (graphFile.isSaveRequired())
{
JOptionPane optionPane = new JOptionPane();
optionPane.setMessage(dialogCloseMessage);
optionPane.setOptionType(JOptionPane.YES_NO_CANCEL_OPTION);
optionPane.setIcon(dialogCloseIcon);
dialogFactory.showDialog(optionPane, dialogCloseTitle, true);
int result = JOptionPane.CANCEL_OPTION;
if (!JOptionPane.UNINITIALIZED_VALUE.equals(optionPane.getValue()))
{
result = ((Integer) optionPane.getValue()).intValue();
}
if (result == JOptionPane.YES_OPTION)
{
String filename = graphFile.getFilename();
if (filename == null)
{
graphFile.saveToNewLocation();
userPreferencesService.addRecentFile(graphFile);
}
if (filename != null)
{
graphFile.save();
}
if (!graphFile.isSaveRequired())
{
mainFrame.removeWorkspace(workspace);
userPreferencesService.removeOpenedFile(graphFile);
}
}
if (result == JOptionPane.NO_OPTION)
{
mainFrame.removeWorkspace(workspace);
userPreferencesService.removeOpenedFile(graphFile);
}
}
if (!graphFile.isSaveRequired())
{
mainFrame.removeWorkspace(workspace);
userPreferencesService.removeOpenedFile(graphFile);
}
List<IWorkspace> workspaceList = mainFrame.getWorkspaceList();
if (workspaceList.size() == 0)
{
mainFrame.requestFocus();
}
}
}
});
}
/**
* Init open menu entry
*/
private void initFileOpenItem()
{
this.fileOpenItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
IFile selectedFile = null;
try
{
ExtensionFilter[] filters = fileNamingService.getFileFilters();
IFileReader fileOpener = fileChooserService.chooseAndGetFileReader(filters);
if (fileOpener == null)
{
// Action cancelled by user
return;
}
selectedFile = fileOpener.getFileDefinition();
IGraphFile graphFile = new GraphFile(selectedFile);
IWorkspace workspace = new Workspace(graphFile);
mainFrame.addWorkspace(workspace);
userPreferencesService.addOpenedFile(graphFile);
userPreferencesService.addRecentFile(graphFile);
}
catch (StreamException se)
{
dialogFactory.showErrorDialog(dialogOpenFileIncompatibilityMessage);
}
catch (Exception e)
{
dialogFactory.showErrorDialog(dialogOpenFileErrorMessage + " : " + e.getMessage());
}
}
});
if (this.fileChooserService == null) this.fileOpenItem.setEnabled(false);
}
/**
* Init new menu entry
*/
public void initFileNewMenu()
{
List<IDiagramPlugin> diagramPlugins = this.pluginRegistry.getDiagramPlugins();
// Step 1 : sort diagram plugins by categories and names
SortedMap<String, SortedSet<IDiagramPlugin>> diagramPluginsSortedByCategory = new TreeMap<String, SortedSet<IDiagramPlugin>>();
for (final IDiagramPlugin aDiagramPlugin : diagramPlugins)
{
String category = aDiagramPlugin.getCategory();
if (!diagramPluginsSortedByCategory.containsKey(category))
{
SortedSet<IDiagramPlugin> newSortedSet = new TreeSet<IDiagramPlugin>(new Comparator<IDiagramPlugin>()
{
@Override
public int compare(IDiagramPlugin o1, IDiagramPlugin o2)
{
String n1 = o1.getName();
String n2 = o2.getName();
return n1.compareTo(n2);
}
});
diagramPluginsSortedByCategory.put(category, newSortedSet);
}
SortedSet<IDiagramPlugin> aSortedSet = diagramPluginsSortedByCategory.get(category);
aSortedSet.add(aDiagramPlugin);
}
// Step 2 : populate menu entry
for (String aCategory : diagramPluginsSortedByCategory.keySet())
{
String categoryName = aCategory.replaceFirst("[0-9]*\\.", "");
JMenu categorySubMenu = new JMenu(categoryName);
Dimension preferredSize = categorySubMenu.getPreferredSize();
preferredSize = new Dimension((int) preferredSize.getWidth() + 30, (int) preferredSize.getHeight());
categorySubMenu.setPreferredSize(preferredSize);
fileNewMenu.add(categorySubMenu);
SortedSet<IDiagramPlugin> diagramPluginsByCategory = diagramPluginsSortedByCategory.get(aCategory);
for (final IDiagramPlugin aDiagramPlugin : diagramPluginsByCategory)
{
String name = aDiagramPlugin.getName();
name = name.replaceFirst("[0-9]*\\.", "");
JMenuItem item = new JMenuItem(name);
ImageIcon sampleDiagramImage = getSampleDiagramImage(aDiagramPlugin);
if (sampleDiagramImage != null)
{
item.setRolloverIcon(sampleDiagramImage);
}
item.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
Class<? extends IGraph> graphClass = aDiagramPlugin.getGraphClass();
IGraphFile graphFile = new GraphFile(graphClass);
IWorkspace diagramPanel = new Workspace(graphFile);
String name = aDiagramPlugin.getName();
name = name.replaceFirst("[0-9]*\\.", "");
name = unsavedPrefix + " " + name.toLowerCase();
diagramPanel.setTitle(name);
mainFrame.addWorkspace(diagramPanel);
}
});
categorySubMenu.add( item );
}
}
}
/**
* Init recent menu entry
*/
public void initFileRecentMenu()
{
// Set entries on startup
refreshFileRecentMenu();
// Refresh recent files list each time the global file menu gets the focus
this.addFocusListener(new FocusListener()
{
public void focusGained(FocusEvent e)
{
refreshFileRecentMenu();
}
public void focusLost(FocusEvent e)
{
// Nothing to do
}
});
if (this.fileChooserService == null || (this.fileChooserService != null && this.fileChooserService.isWebStart()))
{
this.fileRecentMenu.setEnabled(false);
}
}
/**
* Updates file recent menu
*/
private void refreshFileRecentMenu()
{
fileRecentMenu.removeAll();
for (final IFile aFile : userPreferencesService.getRecentFiles())
{
String name = aFile.getFilename();
JMenuItem item = new JMenuItem(name);
fileRecentMenu.add(item);
item.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
try
{
IGraphFile graphFile = new GraphFile(aFile);
IWorkspace workspace = new Workspace(graphFile);
mainFrame.addWorkspace(workspace);
userPreferencesService.addOpenedFile(aFile);
userPreferencesService.addRecentFile(aFile);
}
catch (Exception e)
{
dialogFactory.showErrorDialog(dialogOpenFileErrorMessage + " : " + e.getMessage());
userPreferencesService.removeOpenedFile(aFile);
}
}
});
}
}
/**
* @param diagramPlugin
* @return an image exported from the sample diagram file attached to each plugin or null if no one exists
* @throws IOException
*/
private ImageIcon getSampleDiagramImage(IDiagramPlugin diagramPlugin)
{
try
{
String sampleFilePath = diagramPlugin.getSampleFilePath();
InputStream resourceAsStream = getClass().getResourceAsStream("/" + sampleFilePath);
if (resourceAsStream == null)
{
return null;
}
IGraph graph = this.filePersistenceService.read(resourceAsStream);
BufferedImage image = FileExportService.getImage(graph);
JLabel label = new JLabel();
label.setHorizontalAlignment(JLabel.CENTER);
label.setVerticalAlignment(JLabel.CENTER);
label.setIcon(new ImageIcon(image));
label.setSize(new Dimension(600, 550));
label.setBackground(Color.WHITE);
label.setOpaque(true);
Dimension size = label.getSize();
BufferedImage image2 = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image2.createGraphics();
label.paint(g2);
return new ImageIcon(image2);
}
catch (Exception e)
{
// Failed to load sample. It doesn"t matter.
return null;
}
}
/**
* The file chooser to use with with menu
*/
@InjectedBean
private IFileChooserService fileChooserService;
/**
* Application stopper
*/
private ApplicationStopper stopper = new ApplicationStopper();
/**
* Plugin registry
*/
@InjectedBean
private PluginRegistry pluginRegistry;
/**
* DialogBox handler
*/
@InjectedBean
private DialogFactory dialogFactory;
/**
* Access to user preferences
*/
@InjectedBean
private UserPreferencesService userPreferencesService;
/**
* File services
*/
@InjectedBean
private FileNamingService fileNamingService;
/**
* Service to convert IGraph to XML content (and XML to IGraph of course)
*/
@InjectedBean
private IFilePersistenceService filePersistenceService;
/**
* Application main frame
*/
private MainFrame mainFrame;
@ResourceBundleBean(key = "file.new")
private JMenu fileNewMenu;
@ResourceBundleBean(key = "file.open")
private JMenuItem fileOpenItem;
@ResourceBundleBean(key = "file.recent")
private JMenu fileRecentMenu;
@ResourceBundleBean(key = "file.close")
private JMenuItem fileCloseItem;
@ResourceBundleBean(key = "file.save")
private JMenuItem fileSaveItem;
@ResourceBundleBean(key = "file.save_as")
private JMenuItem fileSaveAsItem;
@ResourceBundleBean(key = "file.export_to_pdf")
private JMenuItem fileExportToPdfItem;
@ResourceBundleBean(key = "file.export_to_image")
private JMenuItem fileExportToImageItem;
@ResourceBundleBean(key = "file.export_to_clipboard")
private JMenuItem fileExportToClipBoardItem;
@ResourceBundleBean(key = "file.export_to_java")
private JMenuItem fileExportToJavaItem;
@ResourceBundleBean(key = "file.export_to_python")
private JMenuItem fileExportToPythonItem;
@ResourceBundleBean(key = "file.export")
private JMenu fileExportMenu;
@ResourceBundleBean(key = "file.print")
private JMenuItem filePrintItem;
@ResourceBundleBean(key = "file.exit")
private JMenuItem fileExitItem;
@ResourceBundleBean(key = "dialog.close.title")
private String dialogCloseTitle;
@ResourceBundleBean(key = "dialog.close.ok")
private String dialogCloseMessage;
@ResourceBundleBean(key = "dialog.close.icon")
private ImageIcon dialogCloseIcon;
@ResourceBundleBean(key = "dialog.open_file_failed.text")
private String dialogOpenFileErrorMessage;
@ResourceBundleBean(key = "dialog.open_file_content_incompatibility.text")
private String dialogOpenFileIncompatibilityMessage;
@ResourceBundleBean(key = "workspace.unsaved_prefix")
private String unsavedPrefix;
@ResourceBundleBean(key = "file.export.error")
private String fileExportError;
@ResourceBundleBean(key = "file.export.error.message")
private String fileExportErrorMessage;
}