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; 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 @Override
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
DiagramPanel panel = (DiagramPanel) e.getSource(); DiagramPanel panel = (DiagramPanel) e.getSource();
final Graphics2D g2d = panel.getGraphics2D(); final Graphics2D g2d = panel.getGraphics2D();
this.mouseDragStart = e.getPoint(); this.mouseDragStart = e.getPoint();
if ((e.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) {
boolean hit = false; boolean isCtrlDown = (e.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK;
for (Relation r : this.model.getRelations()) {
if (r.getViewModel().getBounds(g2d).contains(e.getX(), e.getY())) { if (!isCtrlDown) {
r.setSelected(!r.isSelected()); this.model.getRelations().forEach(r -> r.setSelected(false));
hit = true; }
} for (Relation r : this.model.getRelations()) {
} if (r.getViewModel().getBounds(g2d).contains(e.getX(), e.getY())) {
if (!hit) { r.setSelected(!r.isSelected());
this.model.getRelations().forEach(r -> r.setSelected(false)); break;
} }
} }
this.model.fireChangedEvent();
} }
@Override @Override
@ -46,11 +55,16 @@ public class DiagramMouseListener extends MouseAdapter {
public void mouseDragged(MouseEvent e) { public void mouseDragged(MouseEvent e) {
int dx = this.mouseDragStart.x - e.getX(); int dx = this.mouseDragStart.x - e.getX();
int dy = this.mouseDragStart.y - e.getY(); int dy = this.mouseDragStart.y - e.getY();
boolean changed = false;
for (Relation r : this.model.getRelations()) { for (Relation r : this.model.getRelations()) {
if (r.isSelected()) { if (r.isSelected()) {
r.setPosition(new Point(r.getPosition().x - dx, r.getPosition().y - dy)); r.setPosition(new Point(r.getPosition().x - dx, r.getPosition().y - dy));
changed = true;
} }
} }
if (changed) {
this.model.fireChangedEvent();
}
this.mouseDragStart = e.getPoint(); this.mouseDragStart = e.getPoint();
} }

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
package nl.andrewlalis.erme.view; package nl.andrewlalis.erme.view;
import lombok.Getter; import lombok.Getter;
import nl.andrewlalis.erme.control.actions.SaveAction;
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;
@ -35,6 +36,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.repaint(); this.repaint();
} }

View File

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

View File

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