Added better mouse functionality and save.

This commit is contained in:
Andrew Lalis 2021-02-07 18:22:14 +01:00
parent 8e8b8d3302
commit cf2a670e0b
8 changed files with 104 additions and 34 deletions

View File

@ -0,0 +1,63 @@
package nl.andrewlalis.erme.control.actions;
import lombok.Setter;
import nl.andrewlalis.erme.model.MappingModel;
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.*;
public class SaveAction extends AbstractAction {
private static SaveAction instance;
public static SaveAction getInstance() {
if (instance == null) {
instance = new SaveAction();
}
return instance;
}
private File lastSelectedFile;
@Setter
private MappingModel model;
public SaveAction() {
super("Save");
this.putValue(SHORT_DESCRIPTION, "Save the current diagram to a file.");
this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, 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);
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;
}
if (!chosenFile.exists() && !chosenFile.getName().endsWith(".erme")) {
chosenFile = new File(chosenFile.getParent(), chosenFile.getName() + ".erme");
}
// TODO: Check for confirm before overwriting.
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(chosenFile))) {
oos.writeObject(this.model);
this.lastSelectedFile = chosenFile;
JOptionPane.showMessageDialog(fileChooser, "File saved successfully.", "Success", JOptionPane.INFORMATION_MESSAGE);
} catch (IOException ex) {
JOptionPane.showMessageDialog(fileChooser, "An error occurred and the file could not be saved:\n" + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
}
}

View File

@ -18,23 +18,32 @@ public class DiagramMouseListener extends MouseAdapter {
this.model = model;
}
/**
* Handles mouse presses. This is what should happen:
* - If the click occurs outside of the bounds of any relation, deselect all
* 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.
* @param e The mouse event.
*/
@Override
public void mousePressed(MouseEvent e) {
DiagramPanel panel = (DiagramPanel) e.getSource();
final Graphics2D g2d = panel.getGraphics2D();
this.mouseDragStart = e.getPoint();
if ((e.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) {
boolean hit = false;
for (Relation r : this.model.getRelations()) {
if (r.getViewModel().getBounds(g2d).contains(e.getX(), e.getY())) {
r.setSelected(!r.isSelected());
hit = true;
}
}
if (!hit) {
this.model.getRelations().forEach(r -> r.setSelected(false));
boolean isCtrlDown = (e.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK;
if (!isCtrlDown) {
this.model.getRelations().forEach(r -> r.setSelected(false));
}
for (Relation r : this.model.getRelations()) {
if (r.getViewModel().getBounds(g2d).contains(e.getX(), e.getY())) {
r.setSelected(!r.isSelected());
break;
}
}
this.model.fireChangedEvent();
}
@Override
@ -46,11 +55,16 @@ public class DiagramMouseListener extends MouseAdapter {
public void mouseDragged(MouseEvent e) {
int dx = this.mouseDragStart.x - e.getX();
int dy = this.mouseDragStart.y - e.getY();
boolean changed = false;
for (Relation r : this.model.getRelations()) {
if (r.isSelected()) {
r.setPosition(new Point(r.getPosition().x - dx, r.getPosition().y - dy));
changed = true;
}
}
if (changed) {
this.model.fireChangedEvent();
}
this.mouseDragStart = e.getPoint();
}

View File

@ -2,13 +2,14 @@ package nl.andrewlalis.erme.model;
import lombok.Getter;
import java.io.Serializable;
import java.util.Objects;
/**
* A single value that belongs to a relation.
*/
@Getter
public class Attribute {
public class Attribute implements Serializable {
private final Relation relation;
private AttributeType type;
private String name;

View File

@ -2,6 +2,7 @@ package nl.andrewlalis.erme.model;
import lombok.Getter;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@ -10,11 +11,11 @@ import java.util.Set;
* This model contains all the information about a single mapping diagram,
* including each mapped table and the links between them.
*/
public class MappingModel {
public class MappingModel implements Serializable {
@Getter
private final Set<Relation> relations;
private final Set<ModelChangeListener> changeListeners;
private transient final Set<ModelChangeListener> changeListeners;
public MappingModel() {
this.relations = new HashSet<>();
@ -38,7 +39,7 @@ public class MappingModel {
listener.onModelChanged();
}
protected final void fireChangedEvent() {
public final void fireChangedEvent() {
this.changeListeners.forEach(ModelChangeListener::onModelChanged);
}

View File

@ -4,6 +4,7 @@ import lombok.Getter;
import nl.andrewlalis.erme.view.view_models.RelationViewModel;
import java.awt.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -12,7 +13,7 @@ import java.util.Objects;
* Represents a single "relation" or table in the diagram.
*/
@Getter
public class Relation {
public class Relation implements Serializable {
private final MappingModel model;
private Point position;
private String name;
@ -30,24 +31,15 @@ public class Relation {
}
public void setPosition(Point position) {
if (!this.position.equals(position)) {
this.position = position;
this.model.fireChangedEvent();
}
this.position = position;
}
public void setName(String name) {
if (!this.name.equals(name)) {
this.name = name;
this.model.fireChangedEvent();
}
this.name = name;
}
public void setSelected(boolean selected) {
if (selected != this.selected) {
this.selected = selected;
this.model.fireChangedEvent();
}
this.selected = selected;
}
public void addAttribute(Attribute attribute) {

View File

@ -1,6 +1,7 @@
package nl.andrewlalis.erme.view;
import lombok.Getter;
import nl.andrewlalis.erme.control.actions.SaveAction;
import nl.andrewlalis.erme.control.diagram.DiagramMouseListener;
import nl.andrewlalis.erme.model.MappingModel;
import nl.andrewlalis.erme.model.ModelChangeListener;
@ -35,6 +36,7 @@ public class DiagramPanel extends JPanel implements ModelChangeListener {
DiagramMouseListener listener = new DiagramMouseListener(newModel);
this.addMouseListener(listener);
this.addMouseMotionListener(listener);
SaveAction.getInstance().setModel(newModel);
this.repaint();
}

View File

@ -1,9 +1,6 @@
package nl.andrewlalis.erme.view;
import nl.andrewlalis.erme.control.actions.ExitAction;
import nl.andrewlalis.erme.control.actions.ExportToImageAction;
import nl.andrewlalis.erme.control.actions.RedoAction;
import nl.andrewlalis.erme.control.actions.UndoAction;
import nl.andrewlalis.erme.control.actions.*;
import javax.swing.*;
@ -18,6 +15,8 @@ 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());

View File

@ -29,10 +29,8 @@ public class RelationViewModel implements ViewModel {
}
if (this.relation.isSelected()) {
g.setColor(Color.BLUE);
} else {
g.setColor(Color.CYAN);
g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
public Rectangle getBounds(Graphics2D g) {