diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/ExportToImageAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/ExportToImageAction.java index 8fb316b..96c6fba 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/ExportToImageAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/ExportToImageAction.java @@ -18,11 +18,12 @@ import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.List; -import java.util.Set; +import java.util.prefs.Preferences; public class ExportToImageAction extends AbstractAction { - private static ExportToImageAction instance; + private static final String LAST_EXPORT_LOCATION_KEY = "lastExportLocation"; + private static ExportToImageAction instance; public static ExportToImageAction getInstance() { if (instance == null) { instance = new ExportToImageAction(); @@ -30,8 +31,6 @@ public class ExportToImageAction extends AbstractAction { return instance; } - private File lastSelectedFile; - @Setter private MappingModel model; @@ -52,12 +51,14 @@ public class ExportToImageAction extends AbstractAction { ); return; } - JFileChooser fileChooser = new JFileChooser(this.lastSelectedFile); + JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileFilter(new FileNameExtensionFilter( "Image files", ImageIO.getReaderFileSuffixes() )); - if (this.lastSelectedFile != null) { - fileChooser.setSelectedFile(this.lastSelectedFile); + Preferences prefs = Preferences.userNodeForPackage(ExportToImageAction.class); + String path = prefs.get(LAST_EXPORT_LOCATION_KEY, null); + if (path != null) { + fileChooser.setSelectedFile(new File(path)); } int choice = fileChooser.showSaveDialog((Component) e.getSource()); if (choice == JFileChooser.APPROVE_OPTION) { @@ -74,7 +75,18 @@ public class ExportToImageAction extends AbstractAction { chosenFile = new File(chosenFile.getParent(), chosenFile.getName() + '.' + extension); } try { - ImageIO.write(this.renderModel(), extension, chosenFile); + long start = System.currentTimeMillis(); + BufferedImage render = this.renderModel(); + double durationSeconds = (System.currentTimeMillis() - start) / 1000.0; + ImageIO.write(render, extension, chosenFile); + prefs.put(LAST_EXPORT_LOCATION_KEY, chosenFile.getAbsolutePath()); + JOptionPane.showMessageDialog( + fileChooser, + "Image export completed in " + String.format("%.4f", durationSeconds) + " seconds.\n" + + "Resolution: " + render.getWidth() + "x" + render.getHeight(), + "Image Export Complete", + JOptionPane.INFORMATION_MESSAGE + ); } catch (IOException ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(fileChooser, "An error occurred and the file could not be saved:\n" + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); @@ -83,23 +95,30 @@ public class ExportToImageAction extends AbstractAction { } private BufferedImage renderModel() { + // Prepare a tiny sample image that we can use to determine the bounds of the model in a graphics context. BufferedImage bufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); Graphics2D g2d = bufferedImage.createGraphics(); DiagramPanel.prepareGraphics(g2d); final Rectangle bounds = this.model.getViewModel().getBounds(g2d); + + // Prepare the output image. BufferedImage outputImage = new BufferedImage(bounds.width, bounds.height + 20, BufferedImage.TYPE_INT_RGB); g2d = outputImage.createGraphics(); g2d.setColor(Color.WHITE); g2d.fillRect(outputImage.getMinX(), outputImage.getMinY(), outputImage.getWidth(), outputImage.getHeight()); + + // Transform the graphics space to account for the model's offset from origin. AffineTransform originalTransform = g2d.getTransform(); g2d.setTransform(AffineTransform.getTranslateInstance(-bounds.x, -bounds.y)); DiagramPanel.prepareGraphics(g2d); + // Render the model. List selectedRelations = this.model.getSelectedRelations(); this.model.getSelectedRelations().forEach(r -> r.setSelected(false)); new MappingModelViewModel(this.model).draw(g2d); this.model.getRelations().forEach(r -> r.setSelected(selectedRelations.contains(r))); + // Revert back to the normal image space, and render a watermark. g2d.setTransform(originalTransform); g2d.setColor(Color.LIGHT_GRAY); g2d.setFont(g2d.getFont().deriveFont(10.0f)); diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/HtmlDocumentViewerAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/HtmlDocumentViewerAction.java index 22f3348..da5ad90 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/HtmlDocumentViewerAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/HtmlDocumentViewerAction.java @@ -12,10 +12,16 @@ import java.net.URISyntaxException; public abstract class HtmlDocumentViewerAction extends AbstractAction { private final String resourceFileName; + private final Dialog.ModalityType modalityType; public HtmlDocumentViewerAction(String name, String resourceFileName) { + this(name, resourceFileName, Dialog.ModalityType.APPLICATION_MODAL); + } + + public HtmlDocumentViewerAction(String name, String resourceFileName, Dialog.ModalityType modalityType) { super(name); this.resourceFileName = resourceFileName; + this.modalityType = modalityType; } @Override @@ -23,7 +29,7 @@ public abstract class HtmlDocumentViewerAction extends AbstractAction { JDialog dialog = new JDialog( SwingUtilities.getWindowAncestor((Component) e.getSource()), (String) this.getValue(NAME), - Dialog.ModalityType.APPLICATION_MODAL + this.modalityType ); JTextPane textPane = new JTextPane(); textPane.setEditable(false); diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/LoadAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/LoadAction.java index 370fba7..5cf2859 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/LoadAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/LoadAction.java @@ -14,10 +14,12 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; +import java.util.prefs.Preferences; public class LoadAction extends AbstractAction { - private static LoadAction instance; + private static final String LAST_LOAD_LOCATION_KEY = "lastLoadLocation"; + private static LoadAction instance; public static LoadAction getInstance() { if (instance == null) { instance = new LoadAction(); @@ -25,8 +27,6 @@ public class LoadAction extends AbstractAction { return instance; } - private File lastSelectedFile; - @Setter private DiagramPanel diagramPanel; @@ -38,14 +38,16 @@ public class LoadAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { - JFileChooser fileChooser = new JFileChooser(this.lastSelectedFile); + JFileChooser fileChooser = new JFileChooser(); FileNameExtensionFilter filter = new FileNameExtensionFilter( "ERME Serialized Files", "erme" ); fileChooser.setFileFilter(filter); - if (this.lastSelectedFile != null) { - fileChooser.setSelectedFile(this.lastSelectedFile); + Preferences prefs = Preferences.userNodeForPackage(LoadAction.class); + String path = prefs.get(LAST_LOAD_LOCATION_KEY, null); + if (path != null) { + fileChooser.setSelectedFile(new File(path)); } int choice = fileChooser.showOpenDialog((Component) e.getSource()); if (choice == JFileChooser.APPROVE_OPTION) { @@ -56,8 +58,8 @@ public class LoadAction extends AbstractAction { } try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(chosenFile))) { MappingModel loadedModel = (MappingModel) ois.readObject(); - this.lastSelectedFile = chosenFile; this.diagramPanel.setModel(loadedModel); + prefs.put(LAST_LOAD_LOCATION_KEY, chosenFile.getAbsolutePath()); } catch (IOException | ClassNotFoundException | ClassCastException ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(fileChooser, "An error occurred and the file could not be read:\n" + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/MappingAlgorithmHelpAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/MappingAlgorithmHelpAction.java index dea1d87..bd3f790 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/MappingAlgorithmHelpAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/MappingAlgorithmHelpAction.java @@ -1,5 +1,7 @@ package nl.andrewlalis.erme.control.actions; +import java.awt.*; + public class MappingAlgorithmHelpAction extends HtmlDocumentViewerAction { private static MappingAlgorithmHelpAction instance; @@ -11,7 +13,7 @@ public class MappingAlgorithmHelpAction extends HtmlDocumentViewerAction { } public MappingAlgorithmHelpAction() { - super("Mapping Algorithm Help", "html/er_mapping_algorithm.html"); + super("Mapping Algorithm Help", "html/er_mapping_algorithm.html", Dialog.ModalityType.DOCUMENT_MODAL); this.putValue(SHORT_DESCRIPTION, "Shows a quick guide on how to map from an ER model to a schema."); } } diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/SaveAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/SaveAction.java index afc728b..9bd0d44 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/SaveAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/SaveAction.java @@ -9,11 +9,16 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; -import java.io.*; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.prefs.Preferences; public class SaveAction extends AbstractAction { - private static SaveAction instance; + private static final String LAST_SAVE_LOCATION_KEY = "lastSaveLocation"; + private static SaveAction instance; public static SaveAction getInstance() { if (instance == null) { instance = new SaveAction(); @@ -21,8 +26,6 @@ public class SaveAction extends AbstractAction { return instance; } - private File lastSelectedFile; - @Setter private MappingModel model; @@ -34,14 +37,16 @@ public class SaveAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { - JFileChooser fileChooser = new JFileChooser(this.lastSelectedFile); + JFileChooser fileChooser = new JFileChooser(); FileNameExtensionFilter filter = new FileNameExtensionFilter( "ERME Serialized Files", "erme" ); fileChooser.setFileFilter(filter); - if (this.lastSelectedFile != null) { - fileChooser.setSelectedFile(this.lastSelectedFile); + Preferences prefs = Preferences.userNodeForPackage(SaveAction.class); + String path = prefs.get(LAST_SAVE_LOCATION_KEY, null); + if (path != null) { + fileChooser.setSelectedFile(new File(path)); } int choice = fileChooser.showSaveDialog((Component) e.getSource()); if (choice == JFileChooser.APPROVE_OPTION) { @@ -56,7 +61,7 @@ public class SaveAction extends AbstractAction { // TODO: Check for confirm before overwriting. try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(chosenFile))) { oos.writeObject(this.model); - this.lastSelectedFile = chosenFile; + prefs.put(LAST_SAVE_LOCATION_KEY, chosenFile.getAbsolutePath()); JOptionPane.showMessageDialog(fileChooser, "File saved successfully.", "Success", JOptionPane.INFORMATION_MESSAGE); } catch (IOException ex) { ex.printStackTrace();