Added a selection model, and attribute selection, and cleaned up foreign key referencing.

This commit is contained in:
Andrew Lalis 2021-10-31 10:47:07 +01:00
parent 1019654e76
commit cea15d872b
17 changed files with 347 additions and 191 deletions

View File

@ -124,10 +124,10 @@ public class ExportToImageAction extends DiagramPanelAction {
// Render the model.
boolean lolcat = model.isLolcatEnabled(); // save previous lolcat mode
model.setLolcatEnabled(false);
List<Relation> selectedRelations = model.getSelectedRelations();
model.getSelectedRelations().forEach(r -> r.setSelected(false));
List<Relation> selectedRelations = model.getSelectionModel().getSelectedRelations();
model.getSelectionModel().clearSelection();
new MappingModelViewModel(model).draw(g2d);
model.getRelations().forEach(r -> r.setSelected(selectedRelations.contains(r)));
model.getSelectionModel().selectAll(selectedRelations);
model.setLolcatEnabled(lolcat); // revert previous lolcat mode
// Revert to the normal image space, and render a watermark.

View File

@ -1,6 +1,9 @@
package nl.andrewlalis.erme.control.actions;
import nl.andrewlalis.erme.model.*;
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 nl.andrewlalis.erme.view.DiagramPanel;
import java.awt.*;
@ -22,7 +25,9 @@ public class LoadSampleModelAction extends DiagramPanelAction {
Relation r1 = new Relation(model, new Point(50, 100), "Airplane");
r1.addAttribute(new Attribute(r1, AttributeType.ID_KEY, "id"));
r1.addAttribute(new Attribute(r1, AttributeType.PLAIN, "purchasedAt"));
r1.addAttribute(new ForeignKeyAttribute(r1, AttributeType.PLAIN, "typeName", "AirplaneType", "name"));
Attribute fk = new Attribute(r1, AttributeType.PLAIN, "typeName");
fk.setReference(model.findAttribute("AirplaneType", "name"));
r1.addAttribute(fk);
model.addRelation(r1);
getDiagramPanel().setModel(model);
}

View File

@ -3,6 +3,7 @@ package nl.andrewlalis.erme.control.actions.edits;
import nl.andrewlalis.erme.control.actions.DiagramPanelAction;
import nl.andrewlalis.erme.model.*;
import nl.andrewlalis.erme.view.DiagramPanel;
import nl.andrewlalis.erme.view.EditAttributePopupDialog;
import javax.swing.*;
import java.awt.*;
@ -23,7 +24,7 @@ public class AddAttributeAction extends DiagramPanelAction {
public void actionPerformed(ActionEvent e) {
DiagramPanel dp = getDiagramPanel();
MappingModel model = dp.getModel();
List<Relation> selectedRelations = model.getSelectedRelations();
List<Relation> selectedRelations = model.getSelectionModel().getSelectedRelations();
if (selectedRelations.size() != 1) {
JOptionPane.showMessageDialog(
dp,
@ -34,72 +35,74 @@ public class AddAttributeAction extends DiagramPanelAction {
return;
}
Relation r = selectedRelations.get(0);
Attribute createdAttribute;
String name = JOptionPane.showInputDialog(dp, "Enter the name of the attribute.", "Attribute Name", JOptionPane.PLAIN_MESSAGE);
if (name == null) return;
Integer index = (Integer) JOptionPane.showInputDialog(
dp,
"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()
);
if (index == null) return;
AttributeType type = (AttributeType) JOptionPane.showInputDialog(
dp,
"Select the type this attribute is.",
"Attribute Type",
JOptionPane.PLAIN_MESSAGE,
null,
AttributeType.values(),
AttributeType.PLAIN
);
if (type == null) return;
boolean shouldUseForeignKey = ((String) JOptionPane.showInputDialog(
dp,
"Is this attribute a foreign key?",
"Foreign Key",
JOptionPane.PLAIN_MESSAGE,
null,
new String[]{"Yes", "No"},
"No"
)).equalsIgnoreCase("yes");
if (shouldUseForeignKey) {
if (model.getRelations().size() < 2) {
JOptionPane.showMessageDialog(dp, "There should be at least 2 relations present in the model.", "Not Enough Relations", JOptionPane.WARNING_MESSAGE);
return;
}
Relation fkRelation = (Relation) JOptionPane.showInputDialog(
dp,
"Select the relation that this foreign key references.",
"Foreign Key Relation Reference",
JOptionPane.PLAIN_MESSAGE,
null,
model.getRelations().toArray(new Relation[0]),
model.getRelations().stream().findFirst().orElse(null)
);
if (fkRelation == null) return;
List<Attribute> eligibleAttributes = fkRelation.getAttributes();
if (eligibleAttributes.isEmpty()) {
JOptionPane.showMessageDialog(dp, "There are no referencable attributes in the selected relation.", "No Referencable Attributes", JOptionPane.WARNING_MESSAGE);
return;
}
Attribute fkAttribute = (Attribute) JOptionPane.showInputDialog(
dp,
"Select the attribute that this foreign key references.",
"Foreign Key Attribute Reference",
JOptionPane.PLAIN_MESSAGE,
null,
eligibleAttributes.toArray(new Attribute[0]),
eligibleAttributes.get(0)
);
if (fkAttribute == null) return;
createdAttribute = new ForeignKeyAttribute(r, type, name, fkAttribute);
} else {
createdAttribute = new Attribute(r, type, name);
}
r.addAttribute(createdAttribute, index);
EditAttributePopupDialog popup = new EditAttributePopupDialog((JFrame) SwingUtilities.getWindowAncestor(dp), r, null);
popup.setVisible(true);
// Attribute createdAttribute;
// String name = JOptionPane.showInputDialog(dp, "Enter the name of the attribute.", "Attribute Name", JOptionPane.PLAIN_MESSAGE);
// if (name == null) return;
// Integer index = (Integer) JOptionPane.showInputDialog(
// dp,
// "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()
// );
// if (index == null) return;
// AttributeType type = (AttributeType) JOptionPane.showInputDialog(
// dp,
// "Select the type this attribute is.",
// "Attribute Type",
// JOptionPane.PLAIN_MESSAGE,
// null,
// AttributeType.values(),
// AttributeType.PLAIN
// );
// if (type == null) return;
// boolean shouldUseForeignKey = ((String) JOptionPane.showInputDialog(
// dp,
// "Is this attribute a foreign key?",
// "Foreign Key",
// JOptionPane.PLAIN_MESSAGE,
// null,
// new String[]{"Yes", "No"},
// "No"
// )).equalsIgnoreCase("yes");
// if (shouldUseForeignKey) {
// if (model.getRelations().size() < 2) {
// JOptionPane.showMessageDialog(dp, "There should be at least 2 relations present in the model.", "Not Enough Relations", JOptionPane.WARNING_MESSAGE);
// return;
// }
// Relation fkRelation = (Relation) JOptionPane.showInputDialog(
// dp,
// "Select the relation that this foreign key references.",
// "Foreign Key Relation Reference",
// JOptionPane.PLAIN_MESSAGE,
// null,
// model.getRelations().toArray(new Relation[0]),
// model.getRelations().stream().findFirst().orElse(null)
// );
// if (fkRelation == null) return;
// List<Attribute> eligibleAttributes = fkRelation.getAttributes();
// if (eligibleAttributes.isEmpty()) {
// JOptionPane.showMessageDialog(dp, "There are no referencable attributes in the selected relation.", "No Referencable Attributes", JOptionPane.WARNING_MESSAGE);
// return;
// }
// Attribute fkAttribute = (Attribute) JOptionPane.showInputDialog(
// dp,
// "Select the attribute that this foreign key references.",
// "Foreign Key Attribute Reference",
// JOptionPane.PLAIN_MESSAGE,
// null,
// eligibleAttributes.toArray(new Attribute[0]),
// eligibleAttributes.get(0)
// );
// if (fkAttribute == null) return;
// createdAttribute = new ForeignKeyAttribute(r, type, name, fkAttribute);
// } else {
// createdAttribute = new Attribute(r, type, name);
// }
// r.addAttribute(createdAttribute, index);
}
}

View File

@ -43,9 +43,9 @@ public class AddRelationAction extends DiagramPanelAction {
p = new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
}
Relation r = new Relation(model, p, name);
model.getSelectedRelations().forEach(rl -> rl.setSelected(false));
r.setSelected(true);
model.addRelation(r);
model.getSelectionModel().clearSelection();
model.getSelectionModel().select(r);
if (isFirstRelation) {
model.normalizeRelationPositions();
dp.centerModel();

View File

@ -2,7 +2,6 @@ package nl.andrewlalis.erme.control.actions.edits;
import nl.andrewlalis.erme.control.actions.DiagramPanelAction;
import nl.andrewlalis.erme.model.Attribute;
import nl.andrewlalis.erme.model.Relation;
import nl.andrewlalis.erme.view.DiagramPanel;
import javax.swing.*;
@ -20,28 +19,22 @@ public class RemoveAttributeAction extends DiagramPanelAction {
@Override
public void actionPerformed(ActionEvent e) {
List<Relation> selectedRelations = getDiagramPanel().getModel().getSelectedRelations();
if (selectedRelations.size() != 1 || selectedRelations.get(0).getAttributes().isEmpty()) {
List<Attribute> selectedAttributes = getDiagramPanel().getModel().getSelectionModel().getSelectedAttributes();
if (selectedAttributes.isEmpty()) {
JOptionPane.showMessageDialog(
getDiagramPanel(),
"A single relation with at least one attribute must be selected to remove an attribute.",
"Single Relation With Attribute Required",
"At least one attribute must be selected to remove.",
"Select Attributes to Remove Them",
JOptionPane.WARNING_MESSAGE
);
return;
}
Relation r = selectedRelations.get(0);
Attribute attribute = (Attribute) JOptionPane.showInputDialog(
getDiagramPanel(),
"Select the attribute to remove.",
"Select Attribute",
JOptionPane.PLAIN_MESSAGE,
null,
r.getAttributes().toArray(new Attribute[0]),
r.getAttributes().get(0)
);
if (attribute != null) {
r.removeAttribute(attribute);
int choice = JOptionPane.showConfirmDialog(getDiagramPanel(), "Are you sure you want to remove these attributes?", "Confirm", JOptionPane.OK_CANCEL_OPTION);
if (choice == JOptionPane.YES_OPTION) {
for (Attribute a : selectedAttributes) {
a.getRelation().removeAttribute(a);
}
getDiagramPanel().getModel().getSelectionModel().clearAttributes();
}
}
}

View File

@ -9,6 +9,7 @@ import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.List;
public class RemoveRelationAction extends DiagramPanelAction {
public RemoveRelationAction(DiagramPanel diagramPanel) {
@ -20,7 +21,8 @@ public class RemoveRelationAction extends DiagramPanelAction {
@Override
public void actionPerformed(ActionEvent e) {
MappingModel model = getDiagramPanel().getModel();
if (model.getSelectedRelations().isEmpty()) {
List<Relation> selectedRelations = model.getSelectionModel().getSelectedRelations();
if (selectedRelations.isEmpty()) {
JOptionPane.showMessageDialog(
getDiagramPanel(),
"No relations selected. Select at least one relation to remove.",
@ -29,8 +31,12 @@ public class RemoveRelationAction extends DiagramPanelAction {
);
return;
}
for (Relation r : model.getSelectedRelations()) {
model.removeRelation(r);
int choice = JOptionPane.showConfirmDialog(getDiagramPanel(), "Are you sure you want to remove these relations?", "Confirm", JOptionPane.YES_NO_OPTION);
if (choice == JOptionPane.YES_OPTION) {
for (Relation r : selectedRelations) {
model.removeRelation(r);
}
model.getSelectionModel().clearRelations();
}
}
}

View File

@ -1,5 +1,6 @@
package nl.andrewlalis.erme.control.diagram;
import nl.andrewlalis.erme.model.Attribute;
import nl.andrewlalis.erme.model.MappingModel;
import nl.andrewlalis.erme.model.Relation;
import nl.andrewlalis.erme.view.DiagramPanel;
@ -25,7 +26,7 @@ public class DiagramMouseListener extends MouseAdapter {
* - 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.
* possible actions, based on what entity we're over.
* @param e The mouse event.
*/
@Override
@ -40,15 +41,24 @@ public class DiagramMouseListener extends MouseAdapter {
final boolean isShiftDown = (e.getModifiers() & ActionEvent.SHIFT_MASK) == ActionEvent.SHIFT_MASK;
MappingModel model = this.diagramPanel.getModel();
if (!isShiftDown && !isCtrlDown) {// A simple click anywhere should reset selection.
model.getRelations().forEach(r -> r.setSelected(false));
if (!isShiftDown && !isCtrlDown) {// A simple left-click anywhere should reset selection.
model.getSelectionModel().clearSelection();
}
if (!isShiftDown) {// If the user clicked or CTRL+clicked, try and select the relation they clicked on.
for (Relation r : model.getRelations()) {
if (r.getViewModel().getBounds(g).contains(modelX, modelY)) {
r.setSelected(!r.isSelected());
break;
boolean anyAttributeSelected = false;
for (Attribute a : r.getAttributes()) {
if (a.getViewModel().getBounds(g).contains(modelX, modelY)) {
model.getSelectionModel().toggle(a);
}
anyAttributeSelected = anyAttributeSelected || a.isSelected();
}
if (!anyAttributeSelected) {
model.getSelectionModel().toggle(r);
break;
}
}
}
}
@ -77,12 +87,11 @@ public class DiagramMouseListener extends MouseAdapter {
MappingModel model = this.diagramPanel.getModel();
if (isShiftDown) {
System.out.println(e);
this.diagramPanel.translate(-dx, -dy);
this.diagramPanel.repaint();
} else {
for (Relation r : model.getRelations()) {
if (r.isSelected()) {
if (model.getSelectionModel().isSelected(r)) {
r.setPosition(new Point(r.getPosition().x - dx, r.getPosition().y - dy));
changed = true;
}

View File

@ -13,6 +13,7 @@ public class Attribute {
private final Relation relation;
private AttributeType type;
private String name;
private Attribute reference;
private transient AttributeViewModel viewModel;
@ -20,6 +21,7 @@ public class Attribute {
this.relation = relation;
this.type = type;
this.name = name;
this.reference = null;
}
public void setType(AttributeType type) {
@ -31,6 +33,14 @@ public class Attribute {
this.name = name;
this.relation.getModel().fireChangedEvent();
}
public boolean hasReference() {
return this.reference != null;
}
public void setReference(Attribute attribute) {
this.reference = attribute;
}
public AttributeViewModel getViewModel() {
if (this.viewModel == null) {
@ -39,6 +49,10 @@ public class Attribute {
return this.viewModel;
}
public boolean isSelected() {
return this.relation.getModel().getSelectionModel().isSelected(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -1,39 +0,0 @@
package nl.andrewlalis.erme.model;
import lombok.Getter;
@Getter
public class ForeignKeyAttribute extends Attribute {
private Attribute reference;
public ForeignKeyAttribute(Relation relation, AttributeType type, String name, Attribute reference) {
super(relation, type, name);
this.reference = reference;
}
public ForeignKeyAttribute(Relation relation, AttributeType type, String name, String referencedRelationName, String referencedAttributeName) {
this(relation, type, name, relation.getModel().findAttribute(referencedRelationName, referencedAttributeName));
if (this.getReference() == null) {
throw new IllegalArgumentException("Unknown attribute name.");
}
}
public void setReference(Attribute reference) {
this.reference = reference;
this.getRelation().getModel().fireChangedEvent();
}
public String getFullReferenceName() {
return this.getReference().getRelation().getName() + "." + this.getReference().getName();
}
@Override
public String toString() {
return super.toString() + "->" + this.getFullReferenceName();
}
@Override
public ForeignKeyAttribute copy(Relation newRelation) {
return new ForeignKeyAttribute(newRelation, this.getType(), this.getName(), this.getReference());
}
}

View File

@ -10,9 +10,7 @@ import nl.andrewlalis.erme.view.view_models.MappingModelViewModel;
import nl.andrewlalis.erme.view.view_models.ViewModel;
import java.awt.*;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
/**
* This model contains all the information about a single mapping diagram,
@ -33,10 +31,13 @@ public class MappingModel implements Viewable {
@Getter
@Setter
private transient boolean referenceVisualizationEnabled = false;
@Getter
private transient final SelectionModel selectionModel;
public MappingModel() {
this.relations = new HashSet<>();
this.changeListeners = new HashSet<>();
this.selectionModel = new SelectionModel(this);
}
public void addRelation(Relation r) {
@ -51,14 +52,6 @@ public class MappingModel implements Viewable {
}
}
/**
* Gets the list of relations which are currently selected.
* @return The list of relations which are selected.
*/
public List<Relation> getSelectedRelations() {
return this.relations.stream().filter(Relation::isSelected).collect(Collectors.toList());
}
/**
* Finds an attribute in this model, or returns null otherwise.
* @param relationName The name of the relation the attribute is in.
@ -86,11 +79,8 @@ public class MappingModel implements Viewable {
for (Relation r : this.getRelations()) {
Set<Attribute> removalSet = new HashSet<>();
for (Attribute a : r.getAttributes()) {
if (a instanceof ForeignKeyAttribute) {
ForeignKeyAttribute fkA = (ForeignKeyAttribute) a;
if (fkA.getReference().equals(referenced)) {
removalSet.add(fkA);
}
if (a.hasReference() && a.getReference().equals(referenced)) {
removalSet.add(a);
}
}
removalSet.forEach(r::removeAttribute);
@ -196,12 +186,11 @@ public class MappingModel implements Viewable {
ObjectNode attributeNode = mapper.createObjectNode()
.put("name", a.getName())
.put("type", a.getType().name());
if (a instanceof ForeignKeyAttribute) {
ForeignKeyAttribute fk = (ForeignKeyAttribute) a;
if (a.hasReference()) {
ObjectNode referenceNode = mapper.createObjectNode()
.put("relation", fk.getReference().getRelation().getName())
.put("attribute", fk.getReference().getName());
attributeNode.set("references", referenceNode);
.put("relation", a.getReference().getRelation().getName())
.put("attribute", a.getReference().getName());
attributeNode.set("reference", referenceNode);
}
attributesArray.add(attributeNode);
}
@ -245,7 +234,8 @@ public class MappingModel implements Viewable {
Attribute referencedAttribute = model.findAttribute(referencedRelation, referencedName);
if (referencedAttribute == null) throw new IllegalArgumentException("Foreign key referenced unknown attribute.");
if (!references.containsKey(referencedAttribute)) {
ForeignKeyAttribute fk = new ForeignKeyAttribute(attribute.getRelation(), attribute.getType(), attribute.getName(), referencedAttribute);
Attribute fk = new Attribute(attribute.getRelation(), attribute.getType(), attribute.getName());
fk.setReference(referencedAttribute);
attribute.getRelation().removeAttribute(attribute);
attribute.getRelation().addAttribute(fk);
references.remove(attribute);
@ -270,8 +260,8 @@ public class MappingModel implements Viewable {
Map<Attribute, ObjectNode> references = new HashMap<>();
for (JsonNode r : node.withArray("relations")) {
for (JsonNode a : r.withArray("attributes")) {
if (a.has("references") && a.get("references").isObject()) {
ObjectNode referenceNode = (ObjectNode) a.get("references");
if (a.has("reference") && a.get("reference").isObject()) {
ObjectNode referenceNode = (ObjectNode) a.get("reference");
String attributeName = a.get("name").asText();
String relationName = r.get("name").asText();
Attribute attribute = model.findAttribute(relationName, attributeName);

View File

@ -20,7 +20,6 @@ public class Relation implements Viewable, Comparable<Relation> {
private String name;
private final List<Attribute> attributes;
private transient boolean selected;
private transient RelationViewModel viewModel;
public Relation(MappingModel model, Point position, String name, List<Attribute> attributes) {
@ -42,10 +41,6 @@ public class Relation implements Viewable, Comparable<Relation> {
this.name = name;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public void addAttribute(Attribute attribute) {
this.attributes.add(attribute);
this.model.fireChangedEvent();
@ -63,6 +58,10 @@ public class Relation implements Viewable, Comparable<Relation> {
}
}
public boolean isSelected() {
return this.model.getSelectionModel().isSelected(this);
}
@Override
public ViewModel getViewModel() {
if (this.viewModel == null) {

View File

@ -0,0 +1,75 @@
package nl.andrewlalis.erme.model;
import java.util.*;
/**
* A model that keeps track of the application's set of selected elements.
*/
public class SelectionModel {
private final MappingModel model;
private final Set<Relation> selectedRelations;
private final Set<Attribute> selectedAttributes;
public SelectionModel(MappingModel model) {
this.model = model;
this.selectedRelations = new HashSet<>();
this.selectedAttributes = new HashSet<>();
}
public boolean isSelected(Relation relation) {
return this.selectedRelations.contains(relation);
}
public boolean isSelected(Attribute attribute) {
return this.selectedAttributes.contains(attribute);
}
public List<Relation> getSelectedRelations() {
return new ArrayList<>(this.selectedRelations);
}
public List<Attribute> getSelectedAttributes() {
return new ArrayList<>(this.selectedAttributes);
}
public void select(Relation relation) {
this.selectedRelations.add(relation);
}
public void selectAll(Collection<Relation> relations) {
this.selectedRelations.addAll(relations);
}
public void select(Attribute attribute) {
this.selectedAttributes.add(attribute);
}
public void toggle(Relation relation) {
if (selectedRelations.contains(relation)) {
selectedRelations.remove(relation);
} else {
selectedRelations.add(relation);
}
}
public void toggle(Attribute attribute) {
if (selectedAttributes.contains(attribute)) {
selectedAttributes.remove(attribute);
} else {
selectedAttributes.add(attribute);
}
}
public void clearSelection() {
this.selectedRelations.clear();
this.selectedAttributes.clear();
}
public void clearRelations() {
this.selectedRelations.clear();
}
public void clearAttributes() {
this.selectedAttributes.clear();
}
}

View File

@ -2,7 +2,9 @@ package nl.andrewlalis.erme.view;
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.Attribute;
import nl.andrewlalis.erme.model.Relation;
import javax.swing.*;
@ -10,19 +12,19 @@ import java.util.List;
public class DiagramPopupMenu extends JPopupMenu {
public DiagramPopupMenu(DiagramPanel diagramPanel) {
List<Relation> selectedRelations = diagramPanel.getModel().getSelectedRelations();
if (selectedRelations.size() == 0) {
List<Relation> selectedRelations = diagramPanel.getModel().getSelectionModel().getSelectedRelations();
List<Attribute> selectedAttributes = diagramPanel.getModel().getSelectionModel().getSelectedAttributes();
if (selectedRelations.isEmpty() && selectedAttributes.isEmpty()) {
this.add(new AddRelationAction(diagramPanel));
}
if (selectedRelations.size() > 0) {
if (selectedRelations.size() > 0 && selectedAttributes.isEmpty()) {
this.add(new RemoveRelationAction(diagramPanel));
}
if (selectedRelations.size() == 1) {
Relation relation = selectedRelations.get(0);
if (selectedRelations.size() == 1 && selectedAttributes.isEmpty()) {
this.add(new AddAttributeAction(diagramPanel));
if (!relation.getAttributes().isEmpty()) {
this.add(new RemoveRelationAction(diagramPanel));
}
}
if (!selectedAttributes.isEmpty() && selectedRelations.isEmpty()) {
this.add(new RemoveAttributeAction(diagramPanel));
}
}
}

View File

@ -0,0 +1,96 @@
package nl.andrewlalis.erme.view;
import nl.andrewlalis.erme.model.Attribute;
import nl.andrewlalis.erme.model.AttributeType;
import nl.andrewlalis.erme.model.Relation;
import javax.swing.*;
import java.awt.*;
/**
* A popup that's shown when creating or editing an attribute of a relation.
*/
public class EditAttributePopupDialog extends JDialog {
private final Relation relation;
private final Attribute attribute;
private final JTextField nameField;
private final JComboBox<AttributeType> attributeTypeComboBox;
private final JSpinner orderSpinner;
public EditAttributePopupDialog(Frame owner, Relation relation, Attribute attribute) {
super(owner, "Edit Attribute", true);
this.relation = relation;
this.attribute = attribute;
JPanel mainPanel = new JPanel(new GridBagLayout());
GridBagConstraints gc = new GridBagConstraints();
gc.ipadx = 2;
gc.ipady = 2;
gc.gridx = 0;
gc.gridy = 0;
gc.anchor = GridBagConstraints.LINE_START;
mainPanel.add(new JLabel("Name"), gc);
gc.gridx = 1;
gc.anchor = GridBagConstraints.LINE_END;
this.nameField = new JTextField(20);
mainPanel.add(nameField, gc);
gc.gridx = 0;
gc.gridy++;
gc.anchor = GridBagConstraints.LINE_START;
mainPanel.add(new JLabel("Type"), gc);
gc.gridx = 1;
gc.anchor = GridBagConstraints.LINE_END;
this.attributeTypeComboBox = new JComboBox<>(AttributeType.values());
mainPanel.add(attributeTypeComboBox, gc);
gc.gridx = 0;
gc.gridy++;
gc.anchor = GridBagConstraints.LINE_START;
mainPanel.add(new JLabel("Order"), gc);
gc.gridx = 1;
gc.anchor = GridBagConstraints.LINE_END;
this.orderSpinner = new JSpinner(new SpinnerNumberModel(1, 1, relation.getAttributes().size() + 1, 1));
mainPanel.add(orderSpinner, gc);
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
JButton okayButton = new JButton("Okay");
okayButton.addActionListener(e -> this.submit());
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(e -> this.dispose());
buttonPanel.add(okayButton);
buttonPanel.add(cancelButton);
gc.gridx = 0;
gc.gridy++;
gc.gridwidth = 2;
mainPanel.add(buttonPanel, gc);
if (this.attribute != null) {
this.nameField.setText(this.attribute.getName());
this.attributeTypeComboBox.setSelectedItem(this.attribute.getType());
this.orderSpinner.setValue(this.relation.getAttributes().indexOf(this.attribute));
} else {
this.orderSpinner.setValue(this.relation.getAttributes().size() + 1);
}
this.setContentPane(mainPanel);
this.pack();
this.setLocationRelativeTo(owner);
}
private void submit() {
String name = this.nameField.getText().trim();
AttributeType type = (AttributeType) this.attributeTypeComboBox.getSelectedItem();
int order = (int) this.orderSpinner.getValue();
if (name.isEmpty()) {
JOptionPane.showMessageDialog(this, "The attribute must have a name.", "Missing Name", JOptionPane.WARNING_MESSAGE);
return;
}
if (this.attribute != null) {
this.attribute.setName(name);
this.attribute.setType(type);
} else {
Attribute a = new Attribute(this.relation, type, name);
this.relation.addAttribute(a, order - 1);
}
this.dispose();
}
}

View File

@ -3,6 +3,7 @@ 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.*;
@ -35,7 +36,7 @@ public class EditorMenuBar extends JMenuBar {
menu.add(new AddRelationAction(diagramPanel));
menu.add(new RemoveRelationAction(diagramPanel));
menu.add(new AddAttributeAction(diagramPanel));
menu.add(new RemoveRelationAction(diagramPanel));
menu.add(new RemoveAttributeAction(diagramPanel));
menu.add(new AutoPositionAction(diagramPanel));
return menu;
}

View File

@ -2,7 +2,6 @@ package nl.andrewlalis.erme.view.view_models;
import nl.andrewlalis.erme.model.Attribute;
import nl.andrewlalis.erme.model.AttributeType;
import nl.andrewlalis.erme.model.ForeignKeyAttribute;
import java.awt.*;
import java.awt.font.TextAttribute;
@ -16,6 +15,7 @@ public class AttributeViewModel implements ViewModel {
public static final int PADDING_X = 5;
public static final int PADDING_Y = 5;
public static final Color BACKGROUND_COLOR = new Color(192, 192, 192, 127);
public static final Color SELECTED_COLOR = new Color(148, 255, 176, 127);
public static final Color FONT_COLOR = Color.BLACK;
public static final float FK_FONT_SIZE = 11.0f;
private static final float LOLCAT_SAT = 0.75f;
@ -35,16 +35,16 @@ public class AttributeViewModel implements ViewModel {
g.setColor(FONT_COLOR);
g.drawRect(r.x, r.y, r.width, r.height);
g.drawString(as.getIterator(), r.x + PADDING_X, r.y + (r.height - PADDING_Y));
if (this.attribute instanceof ForeignKeyAttribute) {
ForeignKeyAttribute fkAttribute = (ForeignKeyAttribute) this.attribute;
if (this.attribute.hasReference() && !this.attribute.getRelation().getModel().isReferenceVisualizationEnabled()) {
Font originalFont = g.getFont();
g.setFont(g.getFont().deriveFont(Font.ITALIC, FK_FONT_SIZE));
g.drawString(fkAttribute.getFullReferenceName(), r.x + PADDING_X, r.y - PADDING_Y);
g.drawString(getFullReferenceName(), r.x + PADDING_X, r.y - PADDING_Y);
g.setFont(originalFont);
}
}
private Color getBackgroundColor(int x, int y, Graphics2D g) {
if (attribute.isSelected()) return SELECTED_COLOR;
if (!attribute.getRelation().getModel().isLolcatEnabled()) return BACKGROUND_COLOR;
Point offset = g.getClipBounds().getLocation();
g.translate(offset.x, offset.y);
@ -82,11 +82,10 @@ public class AttributeViewModel implements ViewModel {
Rectangle2D nameRect = g.getFontMetrics().getStringBounds(as.getIterator(), 0, this.attribute.getName().length(), g);
int width = (int) nameRect.getWidth() + (2 * PADDING_X);
int height = (int) nameRect.getHeight() + (2 * PADDING_Y);
if (this.attribute instanceof ForeignKeyAttribute) {
ForeignKeyAttribute fkAttribute = (ForeignKeyAttribute) this.attribute;
if (this.attribute.hasReference()) {
Font originalFont = g.getFont();
g.setFont(g.getFont().deriveFont(Font.ITALIC, FK_FONT_SIZE));
Rectangle referenceNameBounds = g.getFontMetrics().getStringBounds(fkAttribute.getFullReferenceName(), g).getBounds();
Rectangle referenceNameBounds = g.getFontMetrics().getStringBounds(getFullReferenceName(), g).getBounds();
g.setFont(originalFont);
width = Math.max(width, referenceNameBounds.width + (2 * PADDING_X));
}
@ -103,4 +102,9 @@ public class AttributeViewModel implements ViewModel {
}
return as;
}
private String getFullReferenceName() {
if (!this.attribute.hasReference()) return "";
return this.attribute.getReference().getRelation().getName() + "." + this.attribute.getReference().getName();
}
}

View File

@ -1,7 +1,6 @@
package nl.andrewlalis.erme.view.view_models;
import nl.andrewlalis.erme.model.Attribute;
import nl.andrewlalis.erme.model.ForeignKeyAttribute;
import nl.andrewlalis.erme.model.MappingModel;
import nl.andrewlalis.erme.model.Relation;
@ -32,13 +31,12 @@ public class MappingModelViewModel implements ViewModel {
g2.setStroke(dashedStroke);
for (Relation r : this.model.getRelations()) {
for (Attribute a : r.getAttributes()) {
if (a instanceof ForeignKeyAttribute) {
ForeignKeyAttribute fk = (ForeignKeyAttribute) a;
if (a.hasReference()) {
// Generate a random HSB color for the line, seeded using the referenced attribute's hash code.
Random random = new Random(fk.getReference().hashCode());
Random random = new Random(a.getReference().hashCode());
g2.setColor(Color.getHSBColor(random.nextFloat(), 1.0f, 0.8f));
Rectangle sourceBounds = fk.getViewModel().getBounds(g);
Rectangle targetBounds = fk.getReference().getViewModel().getBounds(g);
Rectangle sourceBounds = a.getViewModel().getBounds(g);
Rectangle targetBounds = a.getReference().getViewModel().getBounds(g);
Point sourcePoint = new Point(sourceBounds.x + sourceBounds.width / 2, sourceBounds.y + 3 * targetBounds.height / 4);
Point targetPoint = new Point(targetBounds.x + targetBounds.width / 2, targetBounds.y + 3 * targetBounds.height / 4);
g2.drawLine(sourcePoint.x, sourcePoint.y, targetPoint.x, targetPoint.y);