From f47b2449d3926876be5b1e3715d0a5439f495d0f Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sun, 7 Feb 2021 20:51:42 +0100 Subject: [PATCH] Added image export. --- .../control/actions/ExportToImageAction.java | 81 ++++++++++++++++++- .../erme/control/actions/LoadAction.java | 67 +++++++++++++++ .../erme/control/actions/RedoAction.java | 1 + .../erme/control/actions/SaveAction.java | 4 + .../erme/control/actions/UndoAction.java | 1 + .../actions/edits/AddAttributeAction.java | 74 +++++++++++++++++ .../actions/edits/AddRelationAction.java | 44 ++++++++++ .../actions/edits/RemoveAttributeAction.java | 61 ++++++++++++++ .../actions/edits/RemoveRelationAction.java | 47 +++++++++++ .../control/diagram/DiagramMouseListener.java | 9 +++ .../nl/andrewlalis/erme/model/Attribute.java | 5 ++ .../andrewlalis/erme/model/MappingModel.java | 11 ++- .../nl/andrewlalis/erme/model/Relation.java | 15 +++- .../andrewlalis/erme/view/DiagramPanel.java | 21 ++++- .../erme/view/DiagramPopupMenu.java | 29 +++++++ .../andrewlalis/erme/view/EditorMenuBar.java | 26 +++--- 16 files changed, 481 insertions(+), 15 deletions(-) create mode 100644 src/main/java/nl/andrewlalis/erme/control/actions/LoadAction.java create mode 100644 src/main/java/nl/andrewlalis/erme/control/actions/edits/AddAttributeAction.java create mode 100644 src/main/java/nl/andrewlalis/erme/control/actions/edits/AddRelationAction.java create mode 100644 src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveAttributeAction.java create mode 100644 src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveRelationAction.java create mode 100644 src/main/java/nl/andrewlalis/erme/view/DiagramPopupMenu.java 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 0408c05..74803b7 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/ExportToImageAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/ExportToImageAction.java @@ -1,9 +1,23 @@ package nl.andrewlalis.erme.control.actions; +import lombok.Setter; +import nl.andrewlalis.erme.model.MappingModel; +import nl.andrewlalis.erme.model.Relation; +import nl.andrewlalis.erme.view.view_models.MappingModelViewModel; + +import javax.imageio.ImageIO; import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; +import java.awt.geom.AffineTransform; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Set; public class ExportToImageAction extends AbstractAction { private static ExportToImageAction instance; @@ -15,6 +29,11 @@ public class ExportToImageAction extends AbstractAction { return instance; } + private File lastSelectedFile; + + @Setter + private MappingModel model; + public ExportToImageAction() { super("Export to Image"); this.putValue(Action.SHORT_DESCRIPTION, "Export the current diagram to an image."); @@ -23,6 +42,66 @@ public class ExportToImageAction extends AbstractAction { @Override public void actionPerformed(ActionEvent e) { - System.out.println("Export to image."); + JFileChooser fileChooser = new JFileChooser(this.lastSelectedFile); + fileChooser.setFileFilter(new FileNameExtensionFilter( + "Image files", ImageIO.getReaderFileSuffixes() + )); + if (this.lastSelectedFile != null) { + fileChooser.setSelectedFile(this.lastSelectedFile); + } + int choice = fileChooser.showSaveDialog((Component) e.getSource()); + if (choice == JFileChooser.APPROVE_OPTION) { + File chosenFile = fileChooser.getSelectedFile(); + if (chosenFile == null || chosenFile.isDirectory()) { + JOptionPane.showMessageDialog(fileChooser, "The selected file cannot be written to.", "Invalid File", JOptionPane.WARNING_MESSAGE); + return; + } + int i = chosenFile.getName().lastIndexOf('.'); + String extension = "png"; + if (i > 0) { + extension = chosenFile.getName().substring(i + 1); + } else { + chosenFile = new File(chosenFile.getParent(), chosenFile.getName() + '.' + extension); + } + try { + ImageIO.write(this.renderModel(), extension, chosenFile); + } 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); + } + } + } + + private BufferedImage renderModel() { + BufferedImage bufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = bufferedImage.createGraphics(); + int minX = Integer.MAX_VALUE; + int minY = Integer.MAX_VALUE; + int maxX = Integer.MIN_VALUE; + int maxY = Integer.MIN_VALUE; + for (Relation r : model.getRelations()) { + Rectangle bounds = r.getViewModel().getBounds(g2d); + minX = Math.min(minX, bounds.x); + minY = Math.min(minY, bounds.y); + maxX = Math.max(maxX, bounds.x + bounds.width); + maxY = Math.max(maxY, bounds.y + bounds.height); + } + BufferedImage outputImage = new BufferedImage((maxX - minX), (maxY - minY) + 20, BufferedImage.TYPE_INT_RGB); + g2d = outputImage.createGraphics(); + g2d.setColor(Color.WHITE); + g2d.fillRect(outputImage.getMinX(), outputImage.getMinY(), outputImage.getWidth(), outputImage.getHeight()); + AffineTransform originalTransform = g2d.getTransform(); + g2d.setTransform(AffineTransform.getTranslateInstance(-minX, -minY)); + + 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))); + + g2d.setTransform(originalTransform); + g2d.setColor(Color.LIGHT_GRAY); + g2d.setFont(g2d.getFont().deriveFont(10.0f)); + g2d.drawString("Created by EntityRelationMappingEditor", 0, outputImage.getHeight() - 3); + return outputImage; } } diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/LoadAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/LoadAction.java new file mode 100644 index 0000000..370fba7 --- /dev/null +++ b/src/main/java/nl/andrewlalis/erme/control/actions/LoadAction.java @@ -0,0 +1,67 @@ +package nl.andrewlalis.erme.control.actions; + +import lombok.Setter; +import nl.andrewlalis.erme.model.MappingModel; +import nl.andrewlalis.erme.view.DiagramPanel; + +import javax.swing.*; +import javax.swing.filechooser.FileNameExtensionFilter; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; + +public class LoadAction extends AbstractAction { + private static LoadAction instance; + + public static LoadAction getInstance() { + if (instance == null) { + instance = new LoadAction(); + } + return instance; + } + + private File lastSelectedFile; + + @Setter + private DiagramPanel diagramPanel; + + public LoadAction() { + super("Load"); + this.putValue(SHORT_DESCRIPTION, "Load a saved diagram."); + this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + JFileChooser fileChooser = new JFileChooser(this.lastSelectedFile); + FileNameExtensionFilter filter = new FileNameExtensionFilter( + "ERME Serialized Files", + "erme" + ); + fileChooser.setFileFilter(filter); + if (this.lastSelectedFile != null) { + fileChooser.setSelectedFile(this.lastSelectedFile); + } + int choice = fileChooser.showOpenDialog((Component) e.getSource()); + if (choice == JFileChooser.APPROVE_OPTION) { + File chosenFile = fileChooser.getSelectedFile(); + if (chosenFile == null || chosenFile.isDirectory() || !chosenFile.exists() || !chosenFile.canRead()) { + JOptionPane.showMessageDialog(fileChooser, "The selected file cannot be read.", "Invalid File", JOptionPane.WARNING_MESSAGE); + return; + } + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(chosenFile))) { + MappingModel loadedModel = (MappingModel) ois.readObject(); + this.lastSelectedFile = chosenFile; + this.diagramPanel.setModel(loadedModel); + } 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/RedoAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/RedoAction.java index 3038057..ab59821 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/RedoAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/RedoAction.java @@ -19,6 +19,7 @@ public class RedoAction extends AbstractAction { super("Redo"); this.putValue(Action.SHORT_DESCRIPTION, "Redoes a previously undone action."); this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); + this.setEnabled(false); } @Override 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 d9cc06b..afc728b 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/SaveAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/SaveAction.java @@ -40,6 +40,9 @@ public class SaveAction extends AbstractAction { "erme" ); fileChooser.setFileFilter(filter); + if (this.lastSelectedFile != null) { + fileChooser.setSelectedFile(this.lastSelectedFile); + } int choice = fileChooser.showSaveDialog((Component) e.getSource()); if (choice == JFileChooser.APPROVE_OPTION) { File chosenFile = fileChooser.getSelectedFile(); @@ -56,6 +59,7 @@ public class SaveAction extends AbstractAction { this.lastSelectedFile = chosenFile; JOptionPane.showMessageDialog(fileChooser, "File saved successfully.", "Success", 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); } } diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/UndoAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/UndoAction.java index 2c9581f..727403d 100644 --- a/src/main/java/nl/andrewlalis/erme/control/actions/UndoAction.java +++ b/src/main/java/nl/andrewlalis/erme/control/actions/UndoAction.java @@ -19,6 +19,7 @@ public class UndoAction extends AbstractAction { super("Undo"); this.putValue(Action.SHORT_DESCRIPTION, "Undo the last action."); this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK)); + this.setEnabled(false); } @Override diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddAttributeAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddAttributeAction.java new file mode 100644 index 0000000..1cea555 --- /dev/null +++ b/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddAttributeAction.java @@ -0,0 +1,74 @@ +package nl.andrewlalis.erme.control.actions.edits; + +import lombok.Setter; +import nl.andrewlalis.erme.model.Attribute; +import nl.andrewlalis.erme.model.AttributeType; +import nl.andrewlalis.erme.model.MappingModel; +import nl.andrewlalis.erme.model.Relation; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +public class AddAttributeAction extends AbstractAction { + private static AddAttributeAction instance; + + public static AddAttributeAction getInstance() { + if (instance == null) { + instance = new AddAttributeAction(); + } + return instance; + } + + @Setter + private MappingModel model; + + public AddAttributeAction() { + super("Add Attribute"); + this.putValue(SHORT_DESCRIPTION, "Add an attribute to the selected relation."); + this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + List selectedRelations = this.model.getSelectedRelations(); + if (selectedRelations.size() != 1) { + JOptionPane.showMessageDialog( + (Component) e.getSource(), + "A single relation must be selected to add an attribute.", + "Single Relation Required", + JOptionPane.WARNING_MESSAGE + ); + return; + } + Relation r = selectedRelations.get(0); + Component c = (Component) e.getSource(); + String name = JOptionPane.showInputDialog(c, "Enter the name of the attribute.", "Attribute Name", JOptionPane.PLAIN_MESSAGE); + Integer index = (Integer) JOptionPane.showInputDialog( + c, + "Select the index to insert this attribute at.", + "Attribute Index", + JOptionPane.PLAIN_MESSAGE, + null, + Stream.iterate(0, n -> n + 1).limit(r.getAttributes().size() + 1).toArray(), + r.getAttributes().size() + ); + AttributeType type = (AttributeType) JOptionPane.showInputDialog( + c, + "Select the type this attribute is.", + "Attribute Type", + JOptionPane.PLAIN_MESSAGE, + null, + AttributeType.values(), + AttributeType.PLAIN + ); + if (name != null && index != null && type != null) { + r.addAttribute(new Attribute(r, type, name), index); + } + } +} diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddRelationAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddRelationAction.java new file mode 100644 index 0000000..a945557 --- /dev/null +++ b/src/main/java/nl/andrewlalis/erme/control/actions/edits/AddRelationAction.java @@ -0,0 +1,44 @@ +package nl.andrewlalis.erme.control.actions.edits; + +import lombok.Setter; +import nl.andrewlalis.erme.model.MappingModel; +import nl.andrewlalis.erme.model.Relation; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +public class AddRelationAction extends AbstractAction { + private static AddRelationAction instance; + + public static AddRelationAction getInstance() { + if (instance == null) { + instance = new AddRelationAction(); + } + return instance; + } + + @Setter + private MappingModel model; + + public AddRelationAction() { + super("Add Relation"); + this.putValue(SHORT_DESCRIPTION, "Add a new relation to the diagram."); + this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + String name = JOptionPane.showInputDialog( + (Component) e.getSource(), + "Enter the name of the relation.", + "Add Relation", + JOptionPane.PLAIN_MESSAGE + ); + if (name != null) { + this.model.addRelation(new Relation(this.model, new Point(0, 0), name)); + } + } +} diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveAttributeAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveAttributeAction.java new file mode 100644 index 0000000..a5ad92b --- /dev/null +++ b/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveAttributeAction.java @@ -0,0 +1,61 @@ +package nl.andrewlalis.erme.control.actions.edits; + +import lombok.Setter; +import nl.andrewlalis.erme.model.Attribute; +import nl.andrewlalis.erme.model.MappingModel; +import nl.andrewlalis.erme.model.Relation; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.List; +import java.util.stream.Stream; + +public class RemoveAttributeAction extends AbstractAction { + private static RemoveAttributeAction instance; + + public static RemoveAttributeAction getInstance() { + if (instance == null) { + instance = new RemoveAttributeAction(); + } + return instance; + } + + @Setter + private MappingModel model; + + public RemoveAttributeAction() { + super("Remove Attribute"); + this.putValue(SHORT_DESCRIPTION, "Remove an attribute from a relation."); + this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + List selectedRelations = this.model.getSelectedRelations(); + if (selectedRelations.size() != 1 || selectedRelations.get(0).getAttributes().isEmpty()) { + JOptionPane.showMessageDialog( + (Component) e.getSource(), + "A single relation with at least one attribute must be selected to remove an attribute.", + "Single Relation With Attribute Required", + JOptionPane.WARNING_MESSAGE + ); + return; + } + Relation r = selectedRelations.get(0); + Attribute attribute = (Attribute) JOptionPane.showInputDialog( + (Component) e.getSource(), + "Select the index to insert this attribute at.", + "Attribute Index", + JOptionPane.PLAIN_MESSAGE, + null, + r.getAttributes().toArray(new Attribute[0]), + r.getAttributes().get(0) + ); + if (attribute != null) { + r.removeAttribute(attribute); + } + } +} diff --git a/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveRelationAction.java b/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveRelationAction.java new file mode 100644 index 0000000..5a24136 --- /dev/null +++ b/src/main/java/nl/andrewlalis/erme/control/actions/edits/RemoveRelationAction.java @@ -0,0 +1,47 @@ +package nl.andrewlalis.erme.control.actions.edits; + +import lombok.Setter; +import nl.andrewlalis.erme.model.MappingModel; +import nl.andrewlalis.erme.model.Relation; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; + +public class RemoveRelationAction extends AbstractAction { + private static RemoveRelationAction instance; + + public static RemoveRelationAction getInstance() { + if (instance == null) { + instance = new RemoveRelationAction(); + } + return instance; + } + + @Setter + private MappingModel model; + + public RemoveRelationAction() { + super("Remove Relation"); + this.putValue(SHORT_DESCRIPTION, "Remove a relation from the diagram."); + this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK)); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (this.model.getSelectedRelations().isEmpty()) { + JOptionPane.showMessageDialog( + (Component) e.getSource(), + "No relations selected. Select at least one relation to remove.", + "No Relations Selected", + JOptionPane.WARNING_MESSAGE + ); + return; + } + for (Relation r : this.model.getSelectedRelations()) { + this.model.removeRelation(r); + } + } +} diff --git a/src/main/java/nl/andrewlalis/erme/control/diagram/DiagramMouseListener.java b/src/main/java/nl/andrewlalis/erme/control/diagram/DiagramMouseListener.java index 45ab1f4..113f072 100644 --- a/src/main/java/nl/andrewlalis/erme/control/diagram/DiagramMouseListener.java +++ b/src/main/java/nl/andrewlalis/erme/control/diagram/DiagramMouseListener.java @@ -3,6 +3,7 @@ package nl.andrewlalis.erme.control.diagram; import nl.andrewlalis.erme.model.MappingModel; import nl.andrewlalis.erme.model.Relation; import nl.andrewlalis.erme.view.DiagramPanel; +import nl.andrewlalis.erme.view.DiagramPopupMenu; import java.awt.*; import java.awt.event.ActionEvent; @@ -24,6 +25,8 @@ public class DiagramMouseListener extends MouseAdapter { * relations, if CTRL is not held down. * - If the click occurs within at least one relation, select the first one, * and deselect all others if CTRL is not held down. + * - If the user did a right-click, try to open a popup menu with some + * possible actions. * @param e The mouse event. */ @Override @@ -43,6 +46,12 @@ public class DiagramMouseListener extends MouseAdapter { break; } } + + if (e.getButton() == MouseEvent.BUTTON3) { + DiagramPopupMenu popupMenu = new DiagramPopupMenu(this.model); + popupMenu.show(panel, e.getX(), e.getY()); + } + this.model.fireChangedEvent(); } diff --git a/src/main/java/nl/andrewlalis/erme/model/Attribute.java b/src/main/java/nl/andrewlalis/erme/model/Attribute.java index 9f4e84e..b1929f8 100644 --- a/src/main/java/nl/andrewlalis/erme/model/Attribute.java +++ b/src/main/java/nl/andrewlalis/erme/model/Attribute.java @@ -43,4 +43,9 @@ public class Attribute implements Serializable { public int hashCode() { return Objects.hash(type, name); } + + @Override + public String toString() { + return this.getName() + ":" + this.getType().name(); + } } diff --git a/src/main/java/nl/andrewlalis/erme/model/MappingModel.java b/src/main/java/nl/andrewlalis/erme/model/MappingModel.java index 331128e..b765687 100644 --- a/src/main/java/nl/andrewlalis/erme/model/MappingModel.java +++ b/src/main/java/nl/andrewlalis/erme/model/MappingModel.java @@ -4,8 +4,10 @@ import lombok.Getter; import java.io.Serializable; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; /** * This model contains all the information about a single mapping diagram, @@ -15,7 +17,7 @@ public class MappingModel implements Serializable { @Getter private final Set relations; - private transient final Set changeListeners; + private transient Set changeListeners; public MappingModel() { this.relations = new HashSet<>(); @@ -34,7 +36,14 @@ public class MappingModel implements Serializable { } } + public List getSelectedRelations() { + return this.relations.stream().filter(Relation::isSelected).collect(Collectors.toList()); + } + public void addChangeListener(ModelChangeListener listener) { + if (this.changeListeners == null) { + this.changeListeners = new HashSet<>(); + } this.changeListeners.add(listener); listener.onModelChanged(); } diff --git a/src/main/java/nl/andrewlalis/erme/model/Relation.java b/src/main/java/nl/andrewlalis/erme/model/Relation.java index 37b95bd..7cbdbbe 100644 --- a/src/main/java/nl/andrewlalis/erme/model/Relation.java +++ b/src/main/java/nl/andrewlalis/erme/model/Relation.java @@ -20,14 +20,13 @@ public class Relation implements Serializable { private final List attributes; private transient boolean selected; - private final transient RelationViewModel viewModel; + private transient RelationViewModel viewModel; public Relation(MappingModel model, Point position, String name) { this.model = model; this.position = position; this.name = name; this.attributes = new ArrayList<>(); - this.viewModel = new RelationViewModel(this); } public void setPosition(Point position) { @@ -47,12 +46,24 @@ public class Relation implements Serializable { this.model.fireChangedEvent(); } + public void addAttribute(Attribute attribute, int index) { + this.attributes.add(index, attribute); + this.model.fireChangedEvent(); + } + public void removeAttribute(Attribute attribute) { if (this.attributes.remove(attribute)) { this.model.fireChangedEvent(); } } + public RelationViewModel getViewModel() { + if (this.viewModel == null) { + this.viewModel = new RelationViewModel(this); + } + return this.viewModel; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/nl/andrewlalis/erme/view/DiagramPanel.java b/src/main/java/nl/andrewlalis/erme/view/DiagramPanel.java index 2a8a617..4fc20bf 100644 --- a/src/main/java/nl/andrewlalis/erme/view/DiagramPanel.java +++ b/src/main/java/nl/andrewlalis/erme/view/DiagramPanel.java @@ -1,7 +1,13 @@ package nl.andrewlalis.erme.view; import lombok.Getter; +import nl.andrewlalis.erme.control.actions.ExportToImageAction; +import nl.andrewlalis.erme.control.actions.LoadAction; import nl.andrewlalis.erme.control.actions.SaveAction; +import nl.andrewlalis.erme.control.actions.edits.AddAttributeAction; +import nl.andrewlalis.erme.control.actions.edits.AddRelationAction; +import nl.andrewlalis.erme.control.actions.edits.RemoveAttributeAction; +import nl.andrewlalis.erme.control.actions.edits.RemoveRelationAction; import nl.andrewlalis.erme.control.diagram.DiagramMouseListener; import nl.andrewlalis.erme.model.MappingModel; import nl.andrewlalis.erme.model.ModelChangeListener; @@ -36,7 +42,7 @@ public class DiagramPanel extends JPanel implements ModelChangeListener { DiagramMouseListener listener = new DiagramMouseListener(newModel); this.addMouseListener(listener); this.addMouseMotionListener(listener); - SaveAction.getInstance().setModel(newModel); + this.updateActionModels(); this.repaint(); } @@ -63,4 +69,17 @@ public class DiagramPanel extends JPanel implements ModelChangeListener { this.revalidate(); this.repaint(); } + + /** + * Updates all the action singletons with the latest model information. + */ + private void updateActionModels() { + SaveAction.getInstance().setModel(this.model); + LoadAction.getInstance().setDiagramPanel(this); + ExportToImageAction.getInstance().setModel(this.model); + AddRelationAction.getInstance().setModel(this.model); + RemoveRelationAction.getInstance().setModel(this.model); + AddAttributeAction.getInstance().setModel(this.model); + RemoveAttributeAction.getInstance().setModel(this.model); + } } diff --git a/src/main/java/nl/andrewlalis/erme/view/DiagramPopupMenu.java b/src/main/java/nl/andrewlalis/erme/view/DiagramPopupMenu.java new file mode 100644 index 0000000..e98276c --- /dev/null +++ b/src/main/java/nl/andrewlalis/erme/view/DiagramPopupMenu.java @@ -0,0 +1,29 @@ +package nl.andrewlalis.erme.view; + +import nl.andrewlalis.erme.control.actions.ExportToImageAction; +import nl.andrewlalis.erme.control.actions.edits.AddAttributeAction; +import nl.andrewlalis.erme.control.actions.edits.AddRelationAction; +import nl.andrewlalis.erme.control.actions.edits.RemoveAttributeAction; +import nl.andrewlalis.erme.control.actions.edits.RemoveRelationAction; +import nl.andrewlalis.erme.model.MappingModel; +import nl.andrewlalis.erme.model.Relation; + +import javax.swing.*; +import java.util.List; + +public class DiagramPopupMenu extends JPopupMenu { + public DiagramPopupMenu(MappingModel model) { + List selectedRelations = model.getSelectedRelations(); + if (selectedRelations.size() == 0) { + this.add(AddRelationAction.getInstance()); + this.add(ExportToImageAction.getInstance()); + } + if (selectedRelations.size() > 0) { + this.add(RemoveRelationAction.getInstance()); + } + if (selectedRelations.size() == 1) { + this.add(AddAttributeAction.getInstance()); + this.add(RemoveAttributeAction.getInstance()); + } + } +} diff --git a/src/main/java/nl/andrewlalis/erme/view/EditorMenuBar.java b/src/main/java/nl/andrewlalis/erme/view/EditorMenuBar.java index ed02ffa..8ed10d0 100644 --- a/src/main/java/nl/andrewlalis/erme/view/EditorMenuBar.java +++ b/src/main/java/nl/andrewlalis/erme/view/EditorMenuBar.java @@ -1,6 +1,10 @@ package nl.andrewlalis.erme.view; import nl.andrewlalis.erme.control.actions.*; +import nl.andrewlalis.erme.control.actions.edits.AddAttributeAction; +import nl.andrewlalis.erme.control.actions.edits.AddRelationAction; +import nl.andrewlalis.erme.control.actions.edits.RemoveAttributeAction; +import nl.andrewlalis.erme.control.actions.edits.RemoveRelationAction; import javax.swing.*; @@ -15,21 +19,23 @@ public class EditorMenuBar extends JMenuBar { private JMenu buildFileMenu() { JMenu menu = new JMenu("File"); - JMenuItem saveItem = new JMenuItem(SaveAction.getInstance()); - menu.add(saveItem); - JMenuItem exportAsImageItem = new JMenuItem(ExportToImageAction.getInstance()); - menu.add(exportAsImageItem); - JMenuItem exitItem = new JMenuItem(ExitAction.getInstance()); - menu.add(exitItem); + menu.add(SaveAction.getInstance()); + menu.add(LoadAction.getInstance()); + menu.add(ExportToImageAction.getInstance()); + menu.addSeparator(); + menu.add(ExitAction.getInstance()); return menu; } private JMenu buildEditMenu() { JMenu menu = new JMenu("Edit"); - JMenuItem undoItem = new JMenuItem(UndoAction.getInstance()); - menu.add(undoItem); - JMenuItem redoItem = new JMenuItem(RedoAction.getInstance()); - menu.add(redoItem); + menu.add(AddRelationAction.getInstance()); + menu.add(RemoveRelationAction.getInstance()); + menu.add(AddAttributeAction.getInstance()); + menu.add(RemoveAttributeAction.getInstance()); + menu.addSeparator(); + menu.add(UndoAction.getInstance()); + menu.add(RedoAction.getInstance()); return menu; } }