Added image export.
This commit is contained in:
parent
cf2a670e0b
commit
f47b2449d3
|
@ -1,9 +1,23 @@
|
||||||
package nl.andrewlalis.erme.control.actions;
|
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.*;
|
||||||
|
import javax.swing.filechooser.FileNameExtensionFilter;
|
||||||
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.InputEvent;
|
import java.awt.event.InputEvent;
|
||||||
import java.awt.event.KeyEvent;
|
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 {
|
public class ExportToImageAction extends AbstractAction {
|
||||||
private static ExportToImageAction instance;
|
private static ExportToImageAction instance;
|
||||||
|
@ -15,6 +29,11 @@ public class ExportToImageAction extends AbstractAction {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private File lastSelectedFile;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private MappingModel model;
|
||||||
|
|
||||||
public ExportToImageAction() {
|
public ExportToImageAction() {
|
||||||
super("Export to Image");
|
super("Export to Image");
|
||||||
this.putValue(Action.SHORT_DESCRIPTION, "Export the current diagram to an image.");
|
this.putValue(Action.SHORT_DESCRIPTION, "Export the current diagram to an image.");
|
||||||
|
@ -23,6 +42,66 @@ public class ExportToImageAction extends AbstractAction {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
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<Relation> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ public class RedoAction extends AbstractAction {
|
||||||
super("Redo");
|
super("Redo");
|
||||||
this.putValue(Action.SHORT_DESCRIPTION, "Redoes a previously undone action.");
|
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.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK));
|
||||||
|
this.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -40,6 +40,9 @@ public class SaveAction extends AbstractAction {
|
||||||
"erme"
|
"erme"
|
||||||
);
|
);
|
||||||
fileChooser.setFileFilter(filter);
|
fileChooser.setFileFilter(filter);
|
||||||
|
if (this.lastSelectedFile != null) {
|
||||||
|
fileChooser.setSelectedFile(this.lastSelectedFile);
|
||||||
|
}
|
||||||
int choice = fileChooser.showSaveDialog((Component) e.getSource());
|
int choice = fileChooser.showSaveDialog((Component) e.getSource());
|
||||||
if (choice == JFileChooser.APPROVE_OPTION) {
|
if (choice == JFileChooser.APPROVE_OPTION) {
|
||||||
File chosenFile = fileChooser.getSelectedFile();
|
File chosenFile = fileChooser.getSelectedFile();
|
||||||
|
@ -56,6 +59,7 @@ public class SaveAction extends AbstractAction {
|
||||||
this.lastSelectedFile = chosenFile;
|
this.lastSelectedFile = chosenFile;
|
||||||
JOptionPane.showMessageDialog(fileChooser, "File saved successfully.", "Success", JOptionPane.INFORMATION_MESSAGE);
|
JOptionPane.showMessageDialog(fileChooser, "File saved successfully.", "Success", JOptionPane.INFORMATION_MESSAGE);
|
||||||
} catch (IOException ex) {
|
} 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);
|
JOptionPane.showMessageDialog(fileChooser, "An error occurred and the file could not be saved:\n" + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ public class UndoAction extends AbstractAction {
|
||||||
super("Undo");
|
super("Undo");
|
||||||
this.putValue(Action.SHORT_DESCRIPTION, "Undo the last action.");
|
this.putValue(Action.SHORT_DESCRIPTION, "Undo the last action.");
|
||||||
this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK));
|
this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK));
|
||||||
|
this.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -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<Relation> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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<Relation> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package nl.andrewlalis.erme.control.diagram;
|
||||||
import nl.andrewlalis.erme.model.MappingModel;
|
import nl.andrewlalis.erme.model.MappingModel;
|
||||||
import nl.andrewlalis.erme.model.Relation;
|
import nl.andrewlalis.erme.model.Relation;
|
||||||
import nl.andrewlalis.erme.view.DiagramPanel;
|
import nl.andrewlalis.erme.view.DiagramPanel;
|
||||||
|
import nl.andrewlalis.erme.view.DiagramPopupMenu;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
@ -24,6 +25,8 @@ public class DiagramMouseListener extends MouseAdapter {
|
||||||
* relations, if CTRL is not held down.
|
* relations, if CTRL is not held down.
|
||||||
* - If the click occurs within at least one relation, select the first one,
|
* - If the click occurs within at least one relation, select the first one,
|
||||||
* and deselect all others if CTRL is not held down.
|
* 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.
|
* @param e The mouse event.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,6 +46,12 @@ public class DiagramMouseListener extends MouseAdapter {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.getButton() == MouseEvent.BUTTON3) {
|
||||||
|
DiagramPopupMenu popupMenu = new DiagramPopupMenu(this.model);
|
||||||
|
popupMenu.show(panel, e.getX(), e.getY());
|
||||||
|
}
|
||||||
|
|
||||||
this.model.fireChangedEvent();
|
this.model.fireChangedEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,4 +43,9 @@ public class Attribute implements Serializable {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(type, name);
|
return Objects.hash(type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.getName() + ":" + this.getType().name();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@ import lombok.Getter;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This model contains all the information about a single mapping diagram,
|
* This model contains all the information about a single mapping diagram,
|
||||||
|
@ -15,7 +17,7 @@ public class MappingModel implements Serializable {
|
||||||
@Getter
|
@Getter
|
||||||
private final Set<Relation> relations;
|
private final Set<Relation> relations;
|
||||||
|
|
||||||
private transient final Set<ModelChangeListener> changeListeners;
|
private transient Set<ModelChangeListener> changeListeners;
|
||||||
|
|
||||||
public MappingModel() {
|
public MappingModel() {
|
||||||
this.relations = new HashSet<>();
|
this.relations = new HashSet<>();
|
||||||
|
@ -34,7 +36,14 @@ public class MappingModel implements Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Relation> getSelectedRelations() {
|
||||||
|
return this.relations.stream().filter(Relation::isSelected).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
public void addChangeListener(ModelChangeListener listener) {
|
public void addChangeListener(ModelChangeListener listener) {
|
||||||
|
if (this.changeListeners == null) {
|
||||||
|
this.changeListeners = new HashSet<>();
|
||||||
|
}
|
||||||
this.changeListeners.add(listener);
|
this.changeListeners.add(listener);
|
||||||
listener.onModelChanged();
|
listener.onModelChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,13 @@ public class Relation implements Serializable {
|
||||||
private final List<Attribute> attributes;
|
private final List<Attribute> attributes;
|
||||||
|
|
||||||
private transient boolean selected;
|
private transient boolean selected;
|
||||||
private final transient RelationViewModel viewModel;
|
private transient RelationViewModel viewModel;
|
||||||
|
|
||||||
public Relation(MappingModel model, Point position, String name) {
|
public Relation(MappingModel model, Point position, String name) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.attributes = new ArrayList<>();
|
this.attributes = new ArrayList<>();
|
||||||
this.viewModel = new RelationViewModel(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPosition(Point position) {
|
public void setPosition(Point position) {
|
||||||
|
@ -47,12 +46,24 @@ public class Relation implements Serializable {
|
||||||
this.model.fireChangedEvent();
|
this.model.fireChangedEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addAttribute(Attribute attribute, int index) {
|
||||||
|
this.attributes.add(index, attribute);
|
||||||
|
this.model.fireChangedEvent();
|
||||||
|
}
|
||||||
|
|
||||||
public void removeAttribute(Attribute attribute) {
|
public void removeAttribute(Attribute attribute) {
|
||||||
if (this.attributes.remove(attribute)) {
|
if (this.attributes.remove(attribute)) {
|
||||||
this.model.fireChangedEvent();
|
this.model.fireChangedEvent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RelationViewModel getViewModel() {
|
||||||
|
if (this.viewModel == null) {
|
||||||
|
this.viewModel = new RelationViewModel(this);
|
||||||
|
}
|
||||||
|
return this.viewModel;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
package nl.andrewlalis.erme.view;
|
package nl.andrewlalis.erme.view;
|
||||||
|
|
||||||
import lombok.Getter;
|
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.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.control.diagram.DiagramMouseListener;
|
||||||
import nl.andrewlalis.erme.model.MappingModel;
|
import nl.andrewlalis.erme.model.MappingModel;
|
||||||
import nl.andrewlalis.erme.model.ModelChangeListener;
|
import nl.andrewlalis.erme.model.ModelChangeListener;
|
||||||
|
@ -36,7 +42,7 @@ public class DiagramPanel extends JPanel implements ModelChangeListener {
|
||||||
DiagramMouseListener listener = new DiagramMouseListener(newModel);
|
DiagramMouseListener listener = new DiagramMouseListener(newModel);
|
||||||
this.addMouseListener(listener);
|
this.addMouseListener(listener);
|
||||||
this.addMouseMotionListener(listener);
|
this.addMouseMotionListener(listener);
|
||||||
SaveAction.getInstance().setModel(newModel);
|
this.updateActionModels();
|
||||||
this.repaint();
|
this.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,4 +69,17 @@ public class DiagramPanel extends JPanel implements ModelChangeListener {
|
||||||
this.revalidate();
|
this.revalidate();
|
||||||
this.repaint();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Relation> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
package nl.andrewlalis.erme.view;
|
package nl.andrewlalis.erme.view;
|
||||||
|
|
||||||
import nl.andrewlalis.erme.control.actions.*;
|
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.*;
|
import javax.swing.*;
|
||||||
|
|
||||||
|
@ -15,21 +19,23 @@ public class EditorMenuBar extends JMenuBar {
|
||||||
|
|
||||||
private JMenu buildFileMenu() {
|
private JMenu buildFileMenu() {
|
||||||
JMenu menu = new JMenu("File");
|
JMenu menu = new JMenu("File");
|
||||||
JMenuItem saveItem = new JMenuItem(SaveAction.getInstance());
|
menu.add(SaveAction.getInstance());
|
||||||
menu.add(saveItem);
|
menu.add(LoadAction.getInstance());
|
||||||
JMenuItem exportAsImageItem = new JMenuItem(ExportToImageAction.getInstance());
|
menu.add(ExportToImageAction.getInstance());
|
||||||
menu.add(exportAsImageItem);
|
menu.addSeparator();
|
||||||
JMenuItem exitItem = new JMenuItem(ExitAction.getInstance());
|
menu.add(ExitAction.getInstance());
|
||||||
menu.add(exitItem);
|
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JMenu buildEditMenu() {
|
private JMenu buildEditMenu() {
|
||||||
JMenu menu = new JMenu("Edit");
|
JMenu menu = new JMenu("Edit");
|
||||||
JMenuItem undoItem = new JMenuItem(UndoAction.getInstance());
|
menu.add(AddRelationAction.getInstance());
|
||||||
menu.add(undoItem);
|
menu.add(RemoveRelationAction.getInstance());
|
||||||
JMenuItem redoItem = new JMenuItem(RedoAction.getInstance());
|
menu.add(AddAttributeAction.getInstance());
|
||||||
menu.add(redoItem);
|
menu.add(RemoveAttributeAction.getInstance());
|
||||||
|
menu.addSeparator();
|
||||||
|
menu.add(UndoAction.getInstance());
|
||||||
|
menu.add(RedoAction.getInstance());
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue