Cleaned up singleton actions, updated instructions, etc.
This commit is contained in:
		
							parent
							
								
									0be31c84ac
								
							
						
					
					
						commit
						1019654e76
					
				
							
								
								
									
										2
									
								
								pom.xml
								
								
								
								
							
							
						
						
									
										2
									
								
								pom.xml
								
								
								
								
							| 
						 | 
				
			
			@ -6,7 +6,7 @@
 | 
			
		|||
 | 
			
		||||
    <groupId>nl.andrewlalis</groupId>
 | 
			
		||||
    <artifactId>EntityRelationMappingEditor</artifactId>
 | 
			
		||||
    <version>1.6.1</version>
 | 
			
		||||
    <version>1.6.2</version>
 | 
			
		||||
    <build>
 | 
			
		||||
        <plugins>
 | 
			
		||||
            <plugin>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,41 +0,0 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.EntityRelationMappingEditor;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
 | 
			
		||||
public class AboutAction extends AbstractAction {
 | 
			
		||||
	private static AboutAction instance;
 | 
			
		||||
 | 
			
		||||
	public static AboutAction getInstance() {
 | 
			
		||||
		if (instance == null) instance = new AboutAction();
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public AboutAction() {
 | 
			
		||||
		super("About");
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Show some information about this program.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void actionPerformed(ActionEvent e) {
 | 
			
		||||
		JOptionPane.showMessageDialog(
 | 
			
		||||
				this.diagramPanel,
 | 
			
		||||
				"Entity-Relation Mapping Editor\n" +
 | 
			
		||||
						"by Andrew Lalis\n" +
 | 
			
		||||
						"Version " + EntityRelationMappingEditor.VERSION + "\n" +
 | 
			
		||||
						"To report bugs or make suggestions, please visit the GitHub\n" +
 | 
			
		||||
						"repository for this application and create a new issue.\n\n" +
 | 
			
		||||
						"Thank you for using the ERME!",
 | 
			
		||||
				"About ERME",
 | 
			
		||||
				JOptionPane.INFORMATION_MESSAGE
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,5 @@
 | 
			
		|||
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.DiagramPanel;
 | 
			
		||||
import nl.andrewlalis.erme.view.OrderableListPanel;
 | 
			
		||||
| 
						 | 
				
			
			@ -17,31 +15,20 @@ import java.util.concurrent.atomic.AtomicInteger;
 | 
			
		|||
 * A class that implements an AutoPositionAction. This automatically (vertically) positions all relations in the model
 | 
			
		||||
 * based either on alphabetic ordering (by name) or an order that's set by the user
 | 
			
		||||
 */
 | 
			
		||||
public class AutoPositionAction extends AbstractAction {
 | 
			
		||||
public class AutoPositionAction extends DiagramPanelAction {
 | 
			
		||||
	private final static int MARGIN = 10;
 | 
			
		||||
	private final static int PADDING = 10;
 | 
			
		||||
	private static AutoPositionAction instance;
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
	@Setter
 | 
			
		||||
	private MappingModel model;
 | 
			
		||||
	public AutoPositionAction() {
 | 
			
		||||
		super("Align Relations");
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Automatically Align Relations");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static AutoPositionAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new AutoPositionAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	public AutoPositionAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Align Relations", diagramPanel);
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Automatically Align Relations");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void actionPerformed(ActionEvent actionEvent) {
 | 
			
		||||
		if (model.getRelations().size() == 0) {
 | 
			
		||||
		if (getDiagramPanel().getModel().getRelations().size() == 0) {
 | 
			
		||||
			JOptionPane.showMessageDialog(
 | 
			
		||||
					this.diagramPanel,
 | 
			
		||||
					getDiagramPanel(),
 | 
			
		||||
					"Cannot position all relations when there are no relations present",
 | 
			
		||||
					"Relations Required",
 | 
			
		||||
					JOptionPane.WARNING_MESSAGE
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +37,7 @@ public class AutoPositionAction extends AbstractAction {
 | 
			
		|||
		}
 | 
			
		||||
		String[] choices = new String[]{"Alphabeticaly", "Custom Order"};
 | 
			
		||||
		String choice = (String) JOptionPane.showInputDialog(
 | 
			
		||||
				this.diagramPanel,
 | 
			
		||||
				getDiagramPanel(),
 | 
			
		||||
				"Select how to sort the relations",
 | 
			
		||||
				"Position Relations",
 | 
			
		||||
				JOptionPane.PLAIN_MESSAGE,
 | 
			
		||||
| 
						 | 
				
			
			@ -62,14 +49,14 @@ public class AutoPositionAction extends AbstractAction {
 | 
			
		|||
			positionRelations(getAlphabeticRelationList());
 | 
			
		||||
		} else if (choice.equals(choices[1])) {
 | 
			
		||||
			JOptionPane.showConfirmDialog(
 | 
			
		||||
					this.diagramPanel,
 | 
			
		||||
					getDiagramPanel(),
 | 
			
		||||
					OrderableListPanel.getInstance(),
 | 
			
		||||
					"teststring",
 | 
			
		||||
					JOptionPane.OK_CANCEL_OPTION,
 | 
			
		||||
					JOptionPane.PLAIN_MESSAGE);
 | 
			
		||||
			this.positionRelations(OrderableListPanel.getInstance().getOrderList());
 | 
			
		||||
		}
 | 
			
		||||
		diagramPanel.repaint();
 | 
			
		||||
		getDiagramPanel().repaint();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -80,23 +67,20 @@ public class AutoPositionAction extends AbstractAction {
 | 
			
		|||
		if (orderList.isEmpty()) return;
 | 
			
		||||
		AtomicInteger vertSpace = new AtomicInteger(0);
 | 
			
		||||
		orderList.forEach(r -> {
 | 
			
		||||
			int height = (int) r.getViewModel().getBounds(diagramPanel.getGraphics2D()).getHeight();
 | 
			
		||||
			int height = (int) r.getViewModel().getBounds(getDiagramPanel().getGraphics2D()).getHeight();
 | 
			
		||||
			vertSpace.set(Math.max(vertSpace.get(), height));
 | 
			
		||||
		});
 | 
			
		||||
		vertSpace.addAndGet(PADDING);
 | 
			
		||||
		AtomicInteger vertPos = new AtomicInteger(MARGIN);
 | 
			
		||||
		orderList.forEach(r -> {
 | 
			
		||||
			r.setPosition(new Point(MARGIN, vertPos.getAndAdd(vertSpace.get())));
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		diagramPanel.centerModel();
 | 
			
		||||
		orderList.forEach(r -> r.setPosition(new Point(MARGIN, vertPos.getAndAdd(vertSpace.get()))));
 | 
			
		||||
		getDiagramPanel().centerModel();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates an orderList by grabbing all relations and sorting them
 | 
			
		||||
	 */
 | 
			
		||||
	private ArrayList<Relation> getAlphabeticRelationList() {
 | 
			
		||||
		ArrayList<Relation> relationList = new ArrayList<>(model.getRelations());
 | 
			
		||||
		ArrayList<Relation> relationList = new ArrayList<>(getDiagramPanel().getModel().getRelations());
 | 
			
		||||
		Collections.sort(relationList);
 | 
			
		||||
		return relationList;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import lombok.AccessLevel;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
 | 
			
		||||
public abstract class DiagramPanelAction extends AbstractAction {
 | 
			
		||||
	@Getter(AccessLevel.PROTECTED)
 | 
			
		||||
	private final DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public DiagramPanelAction(String name, DiagramPanel diagramPanel) {
 | 
			
		||||
		super(name);
 | 
			
		||||
		this.diagramPanel = diagramPanel;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,29 +1,15 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
import java.awt.event.InputEvent;
 | 
			
		||||
import java.awt.event.KeyEvent;
 | 
			
		||||
 | 
			
		||||
public class ExitAction extends AbstractAction {
 | 
			
		||||
	private static ExitAction instance;
 | 
			
		||||
 | 
			
		||||
	public static ExitAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new ExitAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public ExitAction() {
 | 
			
		||||
		super("Exit");
 | 
			
		||||
public class ExitAction extends DiagramPanelAction {
 | 
			
		||||
	public ExitAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Exit", diagramPanel);
 | 
			
		||||
		this.putValue(Action.SHORT_DESCRIPTION, "Exit the program.");
 | 
			
		||||
		this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Q, InputEvent.SHIFT_DOWN_MASK | InputEvent.CTRL_DOWN_MASK));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +17,7 @@ public class ExitAction extends AbstractAction {
 | 
			
		|||
	@Override
 | 
			
		||||
	public void actionPerformed(ActionEvent e) {
 | 
			
		||||
		int choice = JOptionPane.showConfirmDialog(
 | 
			
		||||
				this.diagramPanel,
 | 
			
		||||
				getDiagramPanel(),
 | 
			
		||||
				"Are you sure you want to quit?\nAll unsaved data will be lost.",
 | 
			
		||||
				"Confirm Exit",
 | 
			
		||||
				JOptionPane.OK_CANCEL_OPTION,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
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.DiagramPanel;
 | 
			
		||||
| 
						 | 
				
			
			@ -20,33 +19,21 @@ import java.io.IOException;
 | 
			
		|||
import java.util.List;
 | 
			
		||||
import java.util.prefs.Preferences;
 | 
			
		||||
 | 
			
		||||
public class ExportToImageAction extends AbstractAction {
 | 
			
		||||
public class ExportToImageAction extends DiagramPanelAction {
 | 
			
		||||
	private static final String LAST_EXPORT_LOCATION_KEY = "lastExportLocation";
 | 
			
		||||
 | 
			
		||||
	private static ExportToImageAction instance;
 | 
			
		||||
	public static ExportToImageAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new ExportToImageAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private MappingModel model;
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public ExportToImageAction() {
 | 
			
		||||
		super("Export to Image");
 | 
			
		||||
	public ExportToImageAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Export to Image", diagramPanel);
 | 
			
		||||
		this.putValue(Action.SHORT_DESCRIPTION, "Export the current diagram to an image.");
 | 
			
		||||
		this.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void actionPerformed(ActionEvent e) {
 | 
			
		||||
		if (this.model.getRelations().isEmpty()) {
 | 
			
		||||
		DiagramPanel dp = getDiagramPanel();
 | 
			
		||||
		if (dp.getModel().getRelations().isEmpty()) {
 | 
			
		||||
			JOptionPane.showMessageDialog(
 | 
			
		||||
					this.diagramPanel,
 | 
			
		||||
					dp,
 | 
			
		||||
					"Model is empty. Add some relations before exporting to an image.",
 | 
			
		||||
					"Model Empty",
 | 
			
		||||
					JOptionPane.WARNING_MESSAGE
 | 
			
		||||
| 
						 | 
				
			
			@ -62,11 +49,11 @@ public class ExportToImageAction extends AbstractAction {
 | 
			
		|||
		if (path != null) {
 | 
			
		||||
			fileChooser.setSelectedFile(new File(path));
 | 
			
		||||
		}
 | 
			
		||||
		int choice = fileChooser.showSaveDialog(this.diagramPanel);
 | 
			
		||||
		int choice = fileChooser.showSaveDialog(dp);
 | 
			
		||||
		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);
 | 
			
		||||
				JOptionPane.showMessageDialog(dp, "The selected file cannot be written to.", "Invalid File", JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			int i = chosenFile.getName().lastIndexOf('.');
 | 
			
		||||
| 
						 | 
				
			
			@ -76,13 +63,13 @@ public class ExportToImageAction extends AbstractAction {
 | 
			
		|||
			} else {
 | 
			
		||||
				chosenFile = new File(chosenFile.getParent(), chosenFile.getName() + '.' + extension);
 | 
			
		||||
			}
 | 
			
		||||
			String input = JOptionPane.showInputDialog(this.diagramPanel, "Choose a scale for the image.", "3.0");
 | 
			
		||||
			String input = JOptionPane.showInputDialog(dp, "Choose a scale for the image.", "3.0");
 | 
			
		||||
			float scale;
 | 
			
		||||
			try {
 | 
			
		||||
				scale = Float.parseFloat(input);
 | 
			
		||||
				if (scale <= 0.0f || scale > 64.0f) throw new IllegalArgumentException();
 | 
			
		||||
			} catch (Exception ex) {
 | 
			
		||||
				JOptionPane.showMessageDialog(this.diagramPanel, "Invalid scale value. Should be a positive number less than 64.", "Invalid Scale", JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
				JOptionPane.showMessageDialog(dp, "Invalid scale value. Should be a positive number less than 64.", "Invalid Scale", JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			try {
 | 
			
		||||
| 
						 | 
				
			
			@ -92,7 +79,7 @@ public class ExportToImageAction extends AbstractAction {
 | 
			
		|||
				ImageIO.write(render, extension, chosenFile);
 | 
			
		||||
				prefs.put(LAST_EXPORT_LOCATION_KEY, chosenFile.getAbsolutePath());
 | 
			
		||||
				JOptionPane.showMessageDialog(
 | 
			
		||||
						this.diagramPanel,
 | 
			
		||||
						dp,
 | 
			
		||||
						"Image export completed in " + String.format("%.4f", durationSeconds) + " seconds.\n" +
 | 
			
		||||
								"Resolution: " + render.getWidth() + "x" + render.getHeight(),
 | 
			
		||||
						"Image Export Complete",
 | 
			
		||||
| 
						 | 
				
			
			@ -111,11 +98,12 @@ public class ExportToImageAction extends AbstractAction {
 | 
			
		|||
	 * @return The image which was rendered.
 | 
			
		||||
	 */
 | 
			
		||||
	private BufferedImage renderModel(float scale) {
 | 
			
		||||
		MappingModel model = getDiagramPanel().getModel();
 | 
			
		||||
		// Prepare a tiny sample image that we can use to determine the bounds of the model in a graphics context.
 | 
			
		||||
		BufferedImage bufferedImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
 | 
			
		||||
		Graphics2D g2d = bufferedImage.createGraphics();
 | 
			
		||||
		DiagramPanel.prepareGraphics(g2d);
 | 
			
		||||
		final Rectangle bounds = this.model.getViewModel().getBounds(g2d);
 | 
			
		||||
		final Rectangle bounds = model.getViewModel().getBounds(g2d);
 | 
			
		||||
		bounds.width *= scale;
 | 
			
		||||
		bounds.height *= scale;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -134,13 +122,13 @@ public class ExportToImageAction extends AbstractAction {
 | 
			
		|||
		DiagramPanel.prepareGraphics(g2d);
 | 
			
		||||
 | 
			
		||||
		// Render the model.
 | 
			
		||||
		boolean lolcat = LolcatAction.getInstance().isLolcatEnabled(); // save previous lolcat mode
 | 
			
		||||
		LolcatAction.getInstance().setLolcatEnabled(false);
 | 
			
		||||
		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)));
 | 
			
		||||
		LolcatAction.getInstance().setLolcatEnabled(lolcat); // revert previous lolcat mode
 | 
			
		||||
		boolean lolcat = model.isLolcatEnabled(); // save previous lolcat mode
 | 
			
		||||
		model.setLolcatEnabled(false);
 | 
			
		||||
		List<Relation> selectedRelations = model.getSelectedRelations();
 | 
			
		||||
		model.getSelectedRelations().forEach(r -> r.setSelected(false));
 | 
			
		||||
		new MappingModelViewModel(model).draw(g2d);
 | 
			
		||||
		model.getRelations().forEach(r -> r.setSelected(selectedRelations.contains(r)));
 | 
			
		||||
		model.setLolcatEnabled(lolcat); // revert previous lolcat mode
 | 
			
		||||
 | 
			
		||||
		// Revert to the normal image space, and render a watermark.
 | 
			
		||||
		g2d.setTransform(originalTransform);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
| 
						 | 
				
			
			@ -16,27 +15,24 @@ import java.net.URISyntaxException;
 | 
			
		|||
/**
 | 
			
		||||
 * An action which, when performed, opens a view that displays an HTML document.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class HtmlDocumentViewerAction extends AbstractAction {
 | 
			
		||||
public abstract class HtmlDocumentViewerAction extends DiagramPanelAction {
 | 
			
		||||
	private final String resourceFileName;
 | 
			
		||||
	private final Dialog.ModalityType modalityType;
 | 
			
		||||
 | 
			
		||||
	public HtmlDocumentViewerAction(String name, String resourceFileName) {
 | 
			
		||||
		this(name, resourceFileName, Dialog.ModalityType.APPLICATION_MODAL);
 | 
			
		||||
	public HtmlDocumentViewerAction(String name, String resourceFileName, DiagramPanel diagramPanel) {
 | 
			
		||||
		this(name, resourceFileName, Dialog.ModalityType.APPLICATION_MODAL, diagramPanel);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public HtmlDocumentViewerAction(String name, String resourceFileName, Dialog.ModalityType modalityType) {
 | 
			
		||||
		super(name);
 | 
			
		||||
	public HtmlDocumentViewerAction(String name, String resourceFileName, Dialog.ModalityType modalityType, DiagramPanel diagramPanel) {
 | 
			
		||||
		super(name, diagramPanel);
 | 
			
		||||
		this.resourceFileName = resourceFileName;
 | 
			
		||||
		this.modalityType = modalityType;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void actionPerformed(ActionEvent e) {
 | 
			
		||||
		JDialog dialog = new JDialog(
 | 
			
		||||
				SwingUtilities.getWindowAncestor(this.diagramPanel),
 | 
			
		||||
				SwingUtilities.getWindowAncestor(getDiagramPanel()),
 | 
			
		||||
				(String) this.getValue(NAME),
 | 
			
		||||
				this.modalityType
 | 
			
		||||
		);
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +59,7 @@ public abstract class HtmlDocumentViewerAction extends AbstractAction {
 | 
			
		|||
		} catch (IOException ex) {
 | 
			
		||||
			ex.printStackTrace();
 | 
			
		||||
			JOptionPane.showMessageDialog(
 | 
			
		||||
					this.diagramPanel,
 | 
			
		||||
					getDiagramPanel(),
 | 
			
		||||
					"An error occured:\n" + ex.getMessage(),
 | 
			
		||||
					"Error",
 | 
			
		||||
					JOptionPane.ERROR_MESSAGE
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +73,7 @@ public abstract class HtmlDocumentViewerAction extends AbstractAction {
 | 
			
		|||
		dialog.setMaximumSize(new Dimension(600, 800));
 | 
			
		||||
		dialog.setPreferredSize(new Dimension(600, 800));
 | 
			
		||||
		dialog.pack();
 | 
			
		||||
		dialog.setLocationRelativeTo(this.diagramPanel);
 | 
			
		||||
		dialog.setLocationRelativeTo(getDiagramPanel());
 | 
			
		||||
		dialog.setVisible(true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,15 +1,10 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
public class InstructionsAction extends HtmlDocumentViewerAction {
 | 
			
		||||
	private static InstructionsAction instance;
 | 
			
		||||
 | 
			
		||||
	public static InstructionsAction getInstance() {
 | 
			
		||||
		if (instance == null) instance = new InstructionsAction();
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public InstructionsAction() {
 | 
			
		||||
		super("Instructions", "html/instructions.html");
 | 
			
		||||
	public InstructionsAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Instructions", "html/instructions.html", diagramPanel);
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Instructions for how to use this program.");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,6 @@ import com.fasterxml.jackson.databind.MapperFeature;
 | 
			
		|||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import com.fasterxml.jackson.databind.json.JsonMapper;
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.model.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,22 +18,11 @@ import java.io.File;
 | 
			
		|||
import java.io.FileInputStream;
 | 
			
		||||
import java.util.prefs.Preferences;
 | 
			
		||||
 | 
			
		||||
public class LoadAction extends AbstractAction {
 | 
			
		||||
public class LoadAction extends DiagramPanelAction {
 | 
			
		||||
	private static final String LAST_LOAD_LOCATION_KEY = "lastLoadLocation";
 | 
			
		||||
 | 
			
		||||
	private static LoadAction instance;
 | 
			
		||||
	public static LoadAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new LoadAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public LoadAction() {
 | 
			
		||||
		super("Load");
 | 
			
		||||
	public LoadAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Load", diagramPanel);
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Load a saved diagram.");
 | 
			
		||||
		this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +40,7 @@ public class LoadAction extends AbstractAction {
 | 
			
		|||
		if (path != null) {
 | 
			
		||||
			fileChooser.setSelectedFile(new File(path));
 | 
			
		||||
		}
 | 
			
		||||
		int choice = fileChooser.showOpenDialog(this.diagramPanel);
 | 
			
		||||
		int choice = fileChooser.showOpenDialog(getDiagramPanel());
 | 
			
		||||
		if (choice == JFileChooser.APPROVE_OPTION) {
 | 
			
		||||
			File chosenFile = fileChooser.getSelectedFile();
 | 
			
		||||
			if (chosenFile == null || chosenFile.isDirectory() || !chosenFile.exists() || !chosenFile.canRead()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +53,7 @@ public class LoadAction extends AbstractAction {
 | 
			
		|||
						.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true)
 | 
			
		||||
						.build();
 | 
			
		||||
				JsonNode data = mapper.readValue(fis, JsonNode.class);
 | 
			
		||||
				this.diagramPanel.setModel(MappingModel.fromJson((ObjectNode) data));
 | 
			
		||||
				getDiagramPanel().setModel(MappingModel.fromJson((ObjectNode) data));
 | 
			
		||||
				prefs.put(LAST_LOAD_LOCATION_KEY, chosenFile.getAbsolutePath());
 | 
			
		||||
			} catch (Exception ex) {
 | 
			
		||||
				ex.printStackTrace();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,28 +1,14 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.model.*;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
 | 
			
		||||
public class LoadSampleModelAction extends AbstractAction {
 | 
			
		||||
	private static LoadSampleModelAction instance;
 | 
			
		||||
 | 
			
		||||
	public static LoadSampleModelAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new LoadSampleModelAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public LoadSampleModelAction() {
 | 
			
		||||
		super("Load Sample Model");
 | 
			
		||||
public class LoadSampleModelAction extends DiagramPanelAction {
 | 
			
		||||
	public LoadSampleModelAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Load Sample Model", diagramPanel);
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Loads a sample ER-mapping model.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +24,6 @@ public class LoadSampleModelAction extends AbstractAction {
 | 
			
		|||
		r1.addAttribute(new Attribute(r1, AttributeType.PLAIN, "purchasedAt"));
 | 
			
		||||
		r1.addAttribute(new ForeignKeyAttribute(r1, AttributeType.PLAIN, "typeName", "AirplaneType", "name"));
 | 
			
		||||
		model.addRelation(r1);
 | 
			
		||||
		this.diagramPanel.setModel(model);
 | 
			
		||||
		getDiagramPanel().setModel(model);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,26 +0,0 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
 | 
			
		||||
public abstract class LocalAction extends AbstractAction {
 | 
			
		||||
	@Setter
 | 
			
		||||
	@Getter
 | 
			
		||||
	private Point location;
 | 
			
		||||
 | 
			
		||||
	public LocalAction(String name, Point location) {
 | 
			
		||||
		super(name);
 | 
			
		||||
		this.location = location;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public LocalAction(String name) {
 | 
			
		||||
		this(name, null);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public boolean hasLocation() {
 | 
			
		||||
		return this.location != null;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,37 +1,20 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
 | 
			
		||||
public class LolcatAction extends AbstractAction {
 | 
			
		||||
	private static LolcatAction instance;
 | 
			
		||||
 | 
			
		||||
	@Getter
 | 
			
		||||
	@Setter
 | 
			
		||||
	private boolean lolcatEnabled = false;
 | 
			
		||||
 | 
			
		||||
	public static LolcatAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new LolcatAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public LolcatAction() {
 | 
			
		||||
		super("Toggle Lolcat Mode");
 | 
			
		||||
public class LolcatAction extends DiagramPanelAction {
 | 
			
		||||
	public LolcatAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Toggle Lolcat Mode", diagramPanel);
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Does some wacky color stuff.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void actionPerformed(ActionEvent actionEvent) {
 | 
			
		||||
		lolcatEnabled = ((AbstractButton)actionEvent.getSource()).getModel().isSelected();
 | 
			
		||||
		diagramPanel.repaint();
 | 
			
		||||
		boolean lolcatEnabled = ((AbstractButton)actionEvent.getSource()).getModel().isSelected();
 | 
			
		||||
		getDiagramPanel().getModel().setLolcatEnabled(lolcatEnabled);
 | 
			
		||||
		getDiagramPanel().repaint();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,19 +1,12 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
 | 
			
		||||
public class MappingAlgorithmHelpAction extends HtmlDocumentViewerAction {
 | 
			
		||||
	private static MappingAlgorithmHelpAction instance;
 | 
			
		||||
 | 
			
		||||
	public static MappingAlgorithmHelpAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new MappingAlgorithmHelpAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public MappingAlgorithmHelpAction() {
 | 
			
		||||
		super("Mapping Algorithm Help", "html/er_mapping_algorithm.html", Dialog.ModalityType.DOCUMENT_MODAL);
 | 
			
		||||
	public MappingAlgorithmHelpAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Mapping Algorithm Help", "html/er_mapping_algorithm.html", Dialog.ModalityType.DOCUMENT_MODAL, diagramPanel);
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Shows a quick guide on how to map from an ER model to a schema.");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.model.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -9,27 +8,15 @@ import java.awt.event.ActionEvent;
 | 
			
		|||
import java.awt.event.InputEvent;
 | 
			
		||||
import java.awt.event.KeyEvent;
 | 
			
		||||
 | 
			
		||||
public class NewModelAction extends AbstractAction {
 | 
			
		||||
	private static NewModelAction instance;
 | 
			
		||||
 | 
			
		||||
	public static NewModelAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new NewModelAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public NewModelAction() {
 | 
			
		||||
		super("New Model");
 | 
			
		||||
public class NewModelAction extends DiagramPanelAction {
 | 
			
		||||
	public NewModelAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("New Model", diagramPanel);
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Create a new model.");
 | 
			
		||||
		this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void actionPerformed(ActionEvent e) {
 | 
			
		||||
		this.diagramPanel.setModel(new MappingModel());
 | 
			
		||||
		getDiagramPanel().setModel(new MappingModel());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,8 +4,6 @@ import com.fasterxml.jackson.databind.MapperFeature;
 | 
			
		|||
import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		||||
import com.fasterxml.jackson.databind.SerializationFeature;
 | 
			
		||||
import com.fasterxml.jackson.databind.json.JsonMapper;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.model.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
| 
						 | 
				
			
			@ -18,25 +16,11 @@ import java.io.FileOutputStream;
 | 
			
		|||
import java.io.IOException;
 | 
			
		||||
import java.util.prefs.Preferences;
 | 
			
		||||
 | 
			
		||||
public class SaveAction extends AbstractAction {
 | 
			
		||||
public class SaveAction extends DiagramPanelAction {
 | 
			
		||||
	private static final String LAST_SAVE_LOCATION_KEY = "lastSaveLocation";
 | 
			
		||||
 | 
			
		||||
	private static SaveAction instance;
 | 
			
		||||
	public static SaveAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new SaveAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private MappingModel model;
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public SaveAction() {
 | 
			
		||||
		super("Save");
 | 
			
		||||
	public SaveAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Save", diagramPanel);
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Save the current diagram to a file.");
 | 
			
		||||
		this.putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -54,17 +38,18 @@ public class SaveAction extends AbstractAction {
 | 
			
		|||
		if (path != null) {
 | 
			
		||||
			fileChooser.setSelectedFile(new File(path));
 | 
			
		||||
		}
 | 
			
		||||
		int choice = fileChooser.showSaveDialog(this.diagramPanel);
 | 
			
		||||
		DiagramPanel dp = getDiagramPanel();
 | 
			
		||||
		int choice = fileChooser.showSaveDialog(dp);
 | 
			
		||||
		if (choice == JFileChooser.APPROVE_OPTION) {
 | 
			
		||||
			File chosenFile = fileChooser.getSelectedFile();
 | 
			
		||||
			if (chosenFile == null || chosenFile.isDirectory()) {
 | 
			
		||||
				JOptionPane.showMessageDialog(this.diagramPanel, "The selected file cannot be written to.", "Invalid File", JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
				JOptionPane.showMessageDialog(dp, "The selected file cannot be written to.", "Invalid File", JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			if (!chosenFile.exists() && !chosenFile.getName().endsWith(".json")) {
 | 
			
		||||
				chosenFile = new File(chosenFile.getParent(), chosenFile.getName() + ".json");
 | 
			
		||||
			} else if (chosenFile.exists()) {
 | 
			
		||||
				int result = JOptionPane.showConfirmDialog(this.diagramPanel, "Are you sure you want overwrite this file?", "Overwrite", JOptionPane.YES_NO_OPTION);
 | 
			
		||||
				int result = JOptionPane.showConfirmDialog(dp, "Are you sure you want overwrite this file?", "Overwrite", JOptionPane.YES_NO_OPTION);
 | 
			
		||||
				if (result == JOptionPane.NO_OPTION) {
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -74,12 +59,12 @@ public class SaveAction extends AbstractAction {
 | 
			
		|||
						.configure(SerializationFeature.INDENT_OUTPUT, true)
 | 
			
		||||
						.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true)
 | 
			
		||||
						.build();
 | 
			
		||||
				mapper.writeValue(fos, this.model.toJson(mapper));
 | 
			
		||||
				mapper.writeValue(fos, dp.getModel().toJson(mapper));
 | 
			
		||||
				prefs.put(LAST_SAVE_LOCATION_KEY, chosenFile.getAbsolutePath());
 | 
			
		||||
				JOptionPane.showMessageDialog(fileChooser, "File saved successfully.", "Success", JOptionPane.INFORMATION_MESSAGE);
 | 
			
		||||
				JOptionPane.showMessageDialog(dp, "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);
 | 
			
		||||
				JOptionPane.showMessageDialog(dp, "An error occurred and the file could not be saved:\n" + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,37 +1,19 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions;
 | 
			
		||||
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
 | 
			
		||||
public class VisualizeReferencesAction extends AbstractAction {
 | 
			
		||||
	private static VisualizeReferencesAction instance;
 | 
			
		||||
 | 
			
		||||
	public static VisualizeReferencesAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new VisualizeReferencesAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Getter
 | 
			
		||||
	@Setter
 | 
			
		||||
	private boolean referenceVisualizationEnabled = false;
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public VisualizeReferencesAction() {
 | 
			
		||||
		super("Toggle Reference Visualization");
 | 
			
		||||
public class VisualizeReferencesAction extends DiagramPanelAction {
 | 
			
		||||
	public VisualizeReferencesAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Toggle Reference Visualization", diagramPanel);
 | 
			
		||||
		this.putValue(SHORT_DESCRIPTION, "Shows/hides visualization of the references between attributes.");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void actionPerformed(ActionEvent e) {
 | 
			
		||||
		referenceVisualizationEnabled = ((AbstractButton)e.getSource()).getModel().isSelected();
 | 
			
		||||
		diagramPanel.repaint();
 | 
			
		||||
		getDiagramPanel().getModel().setReferenceVisualizationEnabled(((AbstractButton)e.getSource()).getModel().isSelected());
 | 
			
		||||
		getDiagramPanel().repaint();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions.edits;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.control.actions.DiagramPanelAction;
 | 
			
		||||
import nl.andrewlalis.erme.model.*;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,36 +10,23 @@ 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;
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public AddAttributeAction() {
 | 
			
		||||
		super("Add Attribute");
 | 
			
		||||
public class AddAttributeAction extends DiagramPanelAction {
 | 
			
		||||
	public AddAttributeAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Add Attribute", diagramPanel);
 | 
			
		||||
		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();
 | 
			
		||||
		DiagramPanel dp = getDiagramPanel();
 | 
			
		||||
		MappingModel model = dp.getModel();
 | 
			
		||||
		List<Relation> selectedRelations = model.getSelectedRelations();
 | 
			
		||||
		if (selectedRelations.size() != 1) {
 | 
			
		||||
			JOptionPane.showMessageDialog(
 | 
			
		||||
					this.diagramPanel,
 | 
			
		||||
					dp,
 | 
			
		||||
					"A single relation must be selected to add an attribute.",
 | 
			
		||||
					"Single Relation Required",
 | 
			
		||||
					JOptionPane.WARNING_MESSAGE
 | 
			
		||||
| 
						 | 
				
			
			@ -48,11 +35,10 @@ public class AddAttributeAction extends AbstractAction {
 | 
			
		|||
		}
 | 
			
		||||
		Relation r = selectedRelations.get(0);
 | 
			
		||||
		Attribute createdAttribute;
 | 
			
		||||
		Component source = this.diagramPanel;
 | 
			
		||||
		String name = JOptionPane.showInputDialog(source, "Enter the name of the attribute.", "Attribute Name", JOptionPane.PLAIN_MESSAGE);
 | 
			
		||||
		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(
 | 
			
		||||
				source,
 | 
			
		||||
				dp,
 | 
			
		||||
				"Select the index to insert this attribute at.",
 | 
			
		||||
				"Attribute Index",
 | 
			
		||||
				JOptionPane.PLAIN_MESSAGE,
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +48,7 @@ public class AddAttributeAction extends AbstractAction {
 | 
			
		|||
		);
 | 
			
		||||
		if (index == null) return;
 | 
			
		||||
		AttributeType type = (AttributeType) JOptionPane.showInputDialog(
 | 
			
		||||
				source,
 | 
			
		||||
				dp,
 | 
			
		||||
				"Select the type this attribute is.",
 | 
			
		||||
				"Attribute Type",
 | 
			
		||||
				JOptionPane.PLAIN_MESSAGE,
 | 
			
		||||
| 
						 | 
				
			
			@ -72,7 +58,7 @@ public class AddAttributeAction extends AbstractAction {
 | 
			
		|||
		);
 | 
			
		||||
		if (type == null) return;
 | 
			
		||||
		boolean shouldUseForeignKey = ((String) JOptionPane.showInputDialog(
 | 
			
		||||
				source,
 | 
			
		||||
				dp,
 | 
			
		||||
				"Is this attribute a foreign key?",
 | 
			
		||||
				"Foreign Key",
 | 
			
		||||
				JOptionPane.PLAIN_MESSAGE,
 | 
			
		||||
| 
						 | 
				
			
			@ -81,27 +67,27 @@ public class AddAttributeAction extends AbstractAction {
 | 
			
		|||
				"No"
 | 
			
		||||
		)).equalsIgnoreCase("yes");
 | 
			
		||||
		if (shouldUseForeignKey) {
 | 
			
		||||
			if (this.model.getRelations().size() < 2) {
 | 
			
		||||
				JOptionPane.showMessageDialog(source, "There should be at least 2 relations present in the model.", "Not Enough Relations", JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
			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(
 | 
			
		||||
					source,
 | 
			
		||||
					dp,
 | 
			
		||||
					"Select the relation that this foreign key references.",
 | 
			
		||||
					"Foreign Key Relation Reference",
 | 
			
		||||
					JOptionPane.PLAIN_MESSAGE,
 | 
			
		||||
					null,
 | 
			
		||||
					this.model.getRelations().toArray(new Relation[0]),
 | 
			
		||||
					this.model.getRelations().stream().findFirst().orElse(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(source, "There are no referencable attributes in the selected relation.", "No Referencable Attributes", JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
				JOptionPane.showMessageDialog(dp, "There are no referencable attributes in the selected relation.", "No Referencable Attributes", JOptionPane.WARNING_MESSAGE);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			Attribute fkAttribute = (Attribute) JOptionPane.showInputDialog(
 | 
			
		||||
					source,
 | 
			
		||||
					dp,
 | 
			
		||||
					"Select the attribute that this foreign key references.",
 | 
			
		||||
					"Foreign Key Attribute Reference",
 | 
			
		||||
					JOptionPane.PLAIN_MESSAGE,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions.edits;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.control.actions.LocalAction;
 | 
			
		||||
import nl.andrewlalis.erme.control.actions.DiagramPanelAction;
 | 
			
		||||
import nl.andrewlalis.erme.model.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.model.Relation;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
| 
						 | 
				
			
			@ -12,57 +11,45 @@ import java.awt.event.ActionEvent;
 | 
			
		|||
import java.awt.event.InputEvent;
 | 
			
		||||
import java.awt.event.KeyEvent;
 | 
			
		||||
 | 
			
		||||
public class AddRelationAction extends LocalAction {
 | 
			
		||||
	private static AddRelationAction instance;
 | 
			
		||||
	public static AddRelationAction getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new AddRelationAction();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private MappingModel model;
 | 
			
		||||
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public AddRelationAction() {
 | 
			
		||||
		super("Add Relation");
 | 
			
		||||
public class AddRelationAction extends DiagramPanelAction {
 | 
			
		||||
	public AddRelationAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Add Relation", diagramPanel);
 | 
			
		||||
		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) {
 | 
			
		||||
		DiagramPanel dp = getDiagramPanel();
 | 
			
		||||
		MappingModel model = dp.getModel();
 | 
			
		||||
		String name = JOptionPane.showInputDialog(
 | 
			
		||||
				this.diagramPanel,
 | 
			
		||||
				dp,
 | 
			
		||||
				"Enter the name of the relation.",
 | 
			
		||||
				"Add Relation",
 | 
			
		||||
				JOptionPane.PLAIN_MESSAGE
 | 
			
		||||
		);
 | 
			
		||||
		if (name != null) {
 | 
			
		||||
			final boolean isFirstRelation = this.model.getRelations().isEmpty();
 | 
			
		||||
			final boolean isFirstRelation = model.getRelations().isEmpty();
 | 
			
		||||
			Point p;
 | 
			
		||||
			if (this.hasLocation()) {
 | 
			
		||||
			if (model.getLastInteractionPoint() != null) {
 | 
			
		||||
				p = new Point(
 | 
			
		||||
						this.getLocation().x - this.diagramPanel.getPanningTranslation().x,
 | 
			
		||||
						this.getLocation().y - this.diagramPanel.getPanningTranslation().y
 | 
			
		||||
						model.getLastInteractionPoint().x - dp.getPanningTranslation().x,
 | 
			
		||||
						model.getLastInteractionPoint().y - dp.getPanningTranslation().y
 | 
			
		||||
				);
 | 
			
		||||
			} else if (isFirstRelation) {
 | 
			
		||||
				p = new Point(100, 100);
 | 
			
		||||
			} else {
 | 
			
		||||
				Rectangle bounds = this.model.getRelationBounds();
 | 
			
		||||
				Rectangle bounds = model.getRelationBounds();
 | 
			
		||||
				p = new Point(bounds.x + bounds.width / 2, bounds.y + bounds.height / 2);
 | 
			
		||||
			}
 | 
			
		||||
			Relation r = new Relation(this.model, p, name);
 | 
			
		||||
			this.model.getSelectedRelations().forEach(rl -> rl.setSelected(false));
 | 
			
		||||
			Relation r = new Relation(model, p, name);
 | 
			
		||||
			model.getSelectedRelations().forEach(rl -> rl.setSelected(false));
 | 
			
		||||
			r.setSelected(true);
 | 
			
		||||
			this.model.addRelation(r);
 | 
			
		||||
			model.addRelation(r);
 | 
			
		||||
			if (isFirstRelation) {
 | 
			
		||||
				this.model.normalizeRelationPositions();
 | 
			
		||||
				this.diagramPanel.centerModel();
 | 
			
		||||
				this.diagramPanel.repaint();
 | 
			
		||||
				model.normalizeRelationPositions();
 | 
			
		||||
				dp.centerModel();
 | 
			
		||||
				dp.repaint();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,7 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions.edits;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.control.actions.DiagramPanelAction;
 | 
			
		||||
import nl.andrewlalis.erme.model.Attribute;
 | 
			
		||||
import nl.andrewlalis.erme.model.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.model.Relation;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -12,33 +11,19 @@ import java.awt.event.InputEvent;
 | 
			
		|||
import java.awt.event.KeyEvent;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public RemoveAttributeAction() {
 | 
			
		||||
		super("Remove Attribute");
 | 
			
		||||
public class RemoveAttributeAction extends DiagramPanelAction {
 | 
			
		||||
	public RemoveAttributeAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Remove Attribute", diagramPanel);
 | 
			
		||||
		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();
 | 
			
		||||
		List<Relation> selectedRelations = getDiagramPanel().getModel().getSelectedRelations();
 | 
			
		||||
		if (selectedRelations.size() != 1 || selectedRelations.get(0).getAttributes().isEmpty()) {
 | 
			
		||||
			JOptionPane.showMessageDialog(
 | 
			
		||||
					this.diagramPanel,
 | 
			
		||||
					getDiagramPanel(),
 | 
			
		||||
					"A single relation with at least one attribute must be selected to remove an attribute.",
 | 
			
		||||
					"Single Relation With Attribute Required",
 | 
			
		||||
					JOptionPane.WARNING_MESSAGE
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +32,7 @@ public class RemoveAttributeAction extends AbstractAction {
 | 
			
		|||
		}
 | 
			
		||||
		Relation r = selectedRelations.get(0);
 | 
			
		||||
		Attribute attribute = (Attribute) JOptionPane.showInputDialog(
 | 
			
		||||
				this.diagramPanel,
 | 
			
		||||
				getDiagramPanel(),
 | 
			
		||||
				"Select the attribute to remove.",
 | 
			
		||||
				"Select Attribute",
 | 
			
		||||
				JOptionPane.PLAIN_MESSAGE,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,50 +1,36 @@
 | 
			
		|||
package nl.andrewlalis.erme.control.actions.edits;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.control.actions.DiagramPanelAction;
 | 
			
		||||
import nl.andrewlalis.erme.model.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.model.Relation;
 | 
			
		||||
import nl.andrewlalis.erme.view.DiagramPanel;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	@Setter
 | 
			
		||||
	private DiagramPanel diagramPanel;
 | 
			
		||||
 | 
			
		||||
	public RemoveRelationAction() {
 | 
			
		||||
		super("Remove Relation");
 | 
			
		||||
public class RemoveRelationAction extends DiagramPanelAction {
 | 
			
		||||
	public RemoveRelationAction(DiagramPanel diagramPanel) {
 | 
			
		||||
		super("Remove Relation", diagramPanel);
 | 
			
		||||
		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()) {
 | 
			
		||||
		MappingModel model = getDiagramPanel().getModel();
 | 
			
		||||
		if (model.getSelectedRelations().isEmpty()) {
 | 
			
		||||
			JOptionPane.showMessageDialog(
 | 
			
		||||
					this.diagramPanel,
 | 
			
		||||
					getDiagramPanel(),
 | 
			
		||||
					"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);
 | 
			
		||||
		for (Relation r : model.getSelectedRelations()) {
 | 
			
		||||
			model.removeRelation(r);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,11 +11,11 @@ import java.awt.event.MouseAdapter;
 | 
			
		|||
import java.awt.event.MouseEvent;
 | 
			
		||||
 | 
			
		||||
public class DiagramMouseListener extends MouseAdapter {
 | 
			
		||||
	private final MappingModel model;
 | 
			
		||||
	private final DiagramPanel diagramPanel;
 | 
			
		||||
	private Point mouseDragStart;
 | 
			
		||||
 | 
			
		||||
	public DiagramMouseListener(MappingModel model) {
 | 
			
		||||
		this.model = model;
 | 
			
		||||
	public DiagramMouseListener(DiagramPanel diagramPanel) {
 | 
			
		||||
		this.diagramPanel = diagramPanel;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
| 
						 | 
				
			
			@ -38,13 +38,14 @@ public class DiagramMouseListener extends MouseAdapter {
 | 
			
		|||
 | 
			
		||||
		final boolean isCtrlDown = (e.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK;
 | 
			
		||||
		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.
 | 
			
		||||
			this.model.getRelations().forEach(r -> r.setSelected(false));
 | 
			
		||||
			model.getRelations().forEach(r -> r.setSelected(false));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!isShiftDown) {// If the user clicked or CTRL+clicked, try and select the relation they clicked on.
 | 
			
		||||
			for (Relation r : this.model.getRelations()) {
 | 
			
		||||
			for (Relation r : model.getRelations()) {
 | 
			
		||||
				if (r.getViewModel().getBounds(g).contains(modelX, modelY)) {
 | 
			
		||||
					r.setSelected(!r.isSelected());
 | 
			
		||||
					break;
 | 
			
		||||
| 
						 | 
				
			
			@ -54,11 +55,12 @@ public class DiagramMouseListener extends MouseAdapter {
 | 
			
		|||
 | 
			
		||||
		// If the user right-clicked, show a popup menu.
 | 
			
		||||
		if (e.getButton() == MouseEvent.BUTTON3) {
 | 
			
		||||
			DiagramPopupMenu popupMenu = new DiagramPopupMenu(this.model, e);
 | 
			
		||||
			model.setLastInteractionPoint(e.getPoint());
 | 
			
		||||
			DiagramPopupMenu popupMenu = new DiagramPopupMenu(this.diagramPanel);
 | 
			
		||||
			popupMenu.show(panel, e.getX(), e.getY());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this.model.fireChangedEvent();
 | 
			
		||||
		model.fireChangedEvent();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
| 
						 | 
				
			
			@ -72,13 +74,14 @@ public class DiagramMouseListener extends MouseAdapter {
 | 
			
		|||
		final int dy = this.mouseDragStart.y - e.getY();
 | 
			
		||||
		final boolean isShiftDown = (e.getModifiers() & ActionEvent.SHIFT_MASK) == ActionEvent.SHIFT_MASK;
 | 
			
		||||
		boolean changed = false;
 | 
			
		||||
		MappingModel model = this.diagramPanel.getModel();
 | 
			
		||||
 | 
			
		||||
		if (isShiftDown) {
 | 
			
		||||
			final DiagramPanel panel = (DiagramPanel) e.getSource();
 | 
			
		||||
			panel.translate(-dx, -dy);
 | 
			
		||||
			panel.repaint();
 | 
			
		||||
			System.out.println(e);
 | 
			
		||||
			this.diagramPanel.translate(-dx, -dy);
 | 
			
		||||
			this.diagramPanel.repaint();
 | 
			
		||||
		} else {
 | 
			
		||||
			for (Relation r : this.model.getRelations()) {
 | 
			
		||||
			for (Relation r : model.getRelations()) {
 | 
			
		||||
				if (r.isSelected()) {
 | 
			
		||||
					r.setPosition(new Point(r.getPosition().x - dx, r.getPosition().y - dy));
 | 
			
		||||
					changed = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +90,7 @@ public class DiagramMouseListener extends MouseAdapter {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		if (changed) {
 | 
			
		||||
			this.model.fireChangedEvent();
 | 
			
		||||
			model.fireChangedEvent();
 | 
			
		||||
		}
 | 
			
		||||
		this.mouseDragStart = e.getPoint();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 | 
			
		|||
import com.fasterxml.jackson.databind.node.ArrayNode;
 | 
			
		||||
import com.fasterxml.jackson.databind.node.ObjectNode;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.view.view_models.MappingModelViewModel;
 | 
			
		||||
import nl.andrewlalis.erme.view.view_models.ViewModel;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +24,16 @@ public class MappingModel implements Viewable {
 | 
			
		|||
 | 
			
		||||
	private transient final Set<ModelChangeListener> changeListeners;
 | 
			
		||||
 | 
			
		||||
	@Getter
 | 
			
		||||
	@Setter
 | 
			
		||||
	private transient Point lastInteractionPoint = null;
 | 
			
		||||
	@Getter
 | 
			
		||||
	@Setter
 | 
			
		||||
	private transient boolean lolcatEnabled = false;
 | 
			
		||||
	@Getter
 | 
			
		||||
	@Setter
 | 
			
		||||
	private transient boolean referenceVisualizationEnabled = false;
 | 
			
		||||
 | 
			
		||||
	public MappingModel() {
 | 
			
		||||
		this.relations = new HashSet<>();
 | 
			
		||||
		this.changeListeners = new HashSet<>();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,11 +1,6 @@
 | 
			
		|||
package nl.andrewlalis.erme.view;
 | 
			
		||||
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
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 nl.andrewlalis.erme.control.diagram.DiagramMouseListener;
 | 
			
		||||
import nl.andrewlalis.erme.model.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.model.ModelChangeListener;
 | 
			
		||||
| 
						 | 
				
			
			@ -21,6 +16,10 @@ import java.awt.event.MouseMotionListener;
 | 
			
		|||
 * The main panel in which the ER Mapping diagram is displayed.
 | 
			
		||||
 */
 | 
			
		||||
public class DiagramPanel extends JPanel implements ModelChangeListener {
 | 
			
		||||
	/**
 | 
			
		||||
	 * The model for the application. This is the main location from which to
 | 
			
		||||
	 * obtain the model for use in actions.
 | 
			
		||||
	 */
 | 
			
		||||
	@Getter
 | 
			
		||||
	private MappingModel model;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -60,10 +59,10 @@ public class DiagramPanel extends JPanel implements ModelChangeListener {
 | 
			
		|||
		for (MouseMotionListener listener : this.getMouseMotionListeners()) {
 | 
			
		||||
			this.removeMouseMotionListener(listener);
 | 
			
		||||
		}
 | 
			
		||||
		DiagramMouseListener listener = new DiagramMouseListener(newModel);
 | 
			
		||||
		DiagramMouseListener listener = new DiagramMouseListener(this);
 | 
			
		||||
		this.addMouseListener(listener);
 | 
			
		||||
		this.addMouseMotionListener(listener);
 | 
			
		||||
		this.updateActionModels();
 | 
			
		||||
		this.updateActionModels(); // TODO: remove this once OrderableListPanel is cleaned up.
 | 
			
		||||
		newModel.addChangeListener(OrderableListPanel.getInstance());
 | 
			
		||||
		this.centerModel();
 | 
			
		||||
		this.repaint();
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +78,9 @@ public class DiagramPanel extends JPanel implements ModelChangeListener {
 | 
			
		|||
		this.panningTranslation.y = 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Centers the model in the panel, by adjusting the panning translation.
 | 
			
		||||
	 */
 | 
			
		||||
	public void centerModel() {
 | 
			
		||||
		if (this.getGraphics() == null) {
 | 
			
		||||
			return;
 | 
			
		||||
| 
						 | 
				
			
			@ -120,30 +122,7 @@ public class DiagramPanel extends JPanel implements ModelChangeListener {
 | 
			
		|||
	 * TODO: Clean this up somehow!
 | 
			
		||||
	 */
 | 
			
		||||
	private void updateActionModels() {
 | 
			
		||||
		NewModelAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		SaveAction.getInstance().setModel(this.model);
 | 
			
		||||
		LoadAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		ExportToImageAction.getInstance().setModel(this.model);
 | 
			
		||||
		ExportToImageAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		AddRelationAction.getInstance().setModel(this.model);
 | 
			
		||||
		AddRelationAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		RemoveRelationAction.getInstance().setModel(this.model);
 | 
			
		||||
		RemoveRelationAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		AddAttributeAction.getInstance().setModel(this.model);
 | 
			
		||||
		AddAttributeAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		RemoveAttributeAction.getInstance().setModel(this.model);
 | 
			
		||||
		RemoveAttributeAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		LoadSampleModelAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		LolcatAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		VisualizeReferencesAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
    	AutoPositionAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		AutoPositionAction.getInstance().setModel(this.model);
 | 
			
		||||
		OrderableListPanel.getInstance().setModel(this.model);
 | 
			
		||||
    	AboutAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		ExitAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		InstructionsAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		MappingAlgorithmHelpAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		SaveAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static void prepareGraphics(Graphics2D g) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,28 +2,27 @@ 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.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.model.Relation;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.event.MouseEvent;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class DiagramPopupMenu extends JPopupMenu {
 | 
			
		||||
	public DiagramPopupMenu(MappingModel model, MouseEvent e) {
 | 
			
		||||
		List<Relation> selectedRelations = model.getSelectedRelations();
 | 
			
		||||
		AddRelationAction.getInstance().setLocation(e.getPoint());
 | 
			
		||||
	public DiagramPopupMenu(DiagramPanel diagramPanel) {
 | 
			
		||||
		List<Relation> selectedRelations = diagramPanel.getModel().getSelectedRelations();
 | 
			
		||||
		if (selectedRelations.size() == 0) {
 | 
			
		||||
			this.add(AddRelationAction.getInstance());
 | 
			
		||||
			this.add(new AddRelationAction(diagramPanel));
 | 
			
		||||
		}
 | 
			
		||||
		if (selectedRelations.size() > 0) {
 | 
			
		||||
			this.add(RemoveRelationAction.getInstance());
 | 
			
		||||
			this.add(new RemoveRelationAction(diagramPanel));
 | 
			
		||||
		}
 | 
			
		||||
		if (selectedRelations.size() == 1) {
 | 
			
		||||
			this.add(AddAttributeAction.getInstance());
 | 
			
		||||
			this.add(RemoveAttributeAction.getInstance());
 | 
			
		||||
			Relation relation = selectedRelations.get(0);
 | 
			
		||||
			this.add(new AddAttributeAction(diagramPanel));
 | 
			
		||||
			if (!relation.getAttributes().isEmpty()) {
 | 
			
		||||
				this.add(new RemoveRelationAction(diagramPanel));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,8 +14,9 @@ import java.io.InputStream;
 | 
			
		|||
public class EditorFrame extends JFrame {
 | 
			
		||||
	public EditorFrame() {
 | 
			
		||||
		super("ER-Mapping Editor");
 | 
			
		||||
		this.setContentPane(new DiagramPanel(new MappingModel()));
 | 
			
		||||
		this.setJMenuBar(new EditorMenuBar());
 | 
			
		||||
		DiagramPanel diagramPanel = new DiagramPanel(new MappingModel());
 | 
			
		||||
		this.setContentPane(diagramPanel);
 | 
			
		||||
		this.setJMenuBar(new EditorMenuBar(diagramPanel));
 | 
			
		||||
		try {
 | 
			
		||||
			InputStream is = getClass().getClassLoader().getResourceAsStream("icon.png");
 | 
			
		||||
			if (is == null) {
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +27,7 @@ public class EditorFrame extends JFrame {
 | 
			
		|||
		} catch (IOException e) {
 | 
			
		||||
			e.printStackTrace();
 | 
			
		||||
		}
 | 
			
		||||
		this.setMinimumSize(new Dimension(400, 400));
 | 
			
		||||
		this.setMinimumSize(new Dimension(300, 300));
 | 
			
		||||
		this.setPreferredSize(new Dimension(800, 800));
 | 
			
		||||
		this.pack();
 | 
			
		||||
		this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,6 @@ 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.*;
 | 
			
		||||
| 
						 | 
				
			
			@ -12,51 +11,47 @@ import javax.swing.*;
 | 
			
		|||
 * The menu bar that's visible atop the application.
 | 
			
		||||
 */
 | 
			
		||||
public class EditorMenuBar extends JMenuBar {
 | 
			
		||||
	public EditorMenuBar() {
 | 
			
		||||
		this.add(this.buildFileMenu());
 | 
			
		||||
		this.add(this.buildEditMenu());
 | 
			
		||||
		this.add(this.buildViewMenu());
 | 
			
		||||
		this.add(this.buildHelpMenu());
 | 
			
		||||
	public EditorMenuBar(DiagramPanel diagramPanel) {
 | 
			
		||||
		this.add(this.buildFileMenu(diagramPanel));
 | 
			
		||||
		this.add(this.buildEditMenu(diagramPanel));
 | 
			
		||||
		this.add(this.buildViewMenu(diagramPanel));
 | 
			
		||||
		this.add(this.buildHelpMenu(diagramPanel));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private JMenu buildFileMenu() {
 | 
			
		||||
	private JMenu buildFileMenu(DiagramPanel diagramPanel) {
 | 
			
		||||
		JMenu menu = new JMenu("File");
 | 
			
		||||
		menu.add(NewModelAction.getInstance());
 | 
			
		||||
		menu.add(SaveAction.getInstance());
 | 
			
		||||
		menu.add(LoadAction.getInstance());
 | 
			
		||||
		menu.add(new NewModelAction(diagramPanel));
 | 
			
		||||
		menu.add(new SaveAction(diagramPanel));
 | 
			
		||||
		menu.add(new LoadAction(diagramPanel));
 | 
			
		||||
		menu.addSeparator();
 | 
			
		||||
		menu.add(ExportToImageAction.getInstance());
 | 
			
		||||
		menu.add(new ExportToImageAction(diagramPanel));
 | 
			
		||||
		menu.addSeparator();
 | 
			
		||||
		menu.add(ExitAction.getInstance());
 | 
			
		||||
		menu.add(new ExitAction(diagramPanel));
 | 
			
		||||
		return menu;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private JMenu buildEditMenu() {
 | 
			
		||||
	private JMenu buildEditMenu(DiagramPanel diagramPanel) {
 | 
			
		||||
		JMenu menu = new JMenu("Edit");
 | 
			
		||||
		menu.add(AddRelationAction.getInstance());
 | 
			
		||||
		menu.add(RemoveRelationAction.getInstance());
 | 
			
		||||
		menu.add(AddAttributeAction.getInstance());
 | 
			
		||||
		menu.add(RemoveAttributeAction.getInstance());
 | 
			
		||||
		menu.add(AutoPositionAction.getInstance());
 | 
			
		||||
		menu.addSeparator();
 | 
			
		||||
		menu.add(UndoAction.getInstance());
 | 
			
		||||
		menu.add(RedoAction.getInstance());
 | 
			
		||||
		menu.add(new AddRelationAction(diagramPanel));
 | 
			
		||||
		menu.add(new RemoveRelationAction(diagramPanel));
 | 
			
		||||
		menu.add(new AddAttributeAction(diagramPanel));
 | 
			
		||||
		menu.add(new RemoveRelationAction(diagramPanel));
 | 
			
		||||
		menu.add(new AutoPositionAction(diagramPanel));
 | 
			
		||||
		return menu;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private JMenu buildViewMenu() {
 | 
			
		||||
	private JMenu buildViewMenu(DiagramPanel diagramPanel) {
 | 
			
		||||
		JMenu menu = new JMenu("View");
 | 
			
		||||
		menu.add(new JCheckBoxMenuItem(LolcatAction.getInstance()));
 | 
			
		||||
		menu.add(new JCheckBoxMenuItem(VisualizeReferencesAction.getInstance()));
 | 
			
		||||
		menu.add(new JCheckBoxMenuItem(new LolcatAction(diagramPanel)));
 | 
			
		||||
		menu.add(new JCheckBoxMenuItem(new VisualizeReferencesAction(diagramPanel)));
 | 
			
		||||
		return menu;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private JMenu buildHelpMenu() {
 | 
			
		||||
	private JMenu buildHelpMenu(DiagramPanel diagramPanel) {
 | 
			
		||||
		JMenu menu = new JMenu("Help");
 | 
			
		||||
		menu.add(InstructionsAction.getInstance());
 | 
			
		||||
		menu.add(MappingAlgorithmHelpAction.getInstance());
 | 
			
		||||
		menu.add(LoadSampleModelAction.getInstance());
 | 
			
		||||
		menu.add(AboutAction.getInstance());
 | 
			
		||||
		menu.add(new InstructionsAction(diagramPanel));
 | 
			
		||||
		menu.add(new MappingAlgorithmHelpAction(diagramPanel));
 | 
			
		||||
		menu.add(new LoadSampleModelAction(diagramPanel));
 | 
			
		||||
		return menu;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ import java.util.Set;
 | 
			
		|||
/**
 | 
			
		||||
 * A panel to be used in a popup that has a OrderableListModel implemented. Implements ModelChangeListener to be able
 | 
			
		||||
 * to update the list of relations when new ones are added or ones are removed.
 | 
			
		||||
 * TODO: Refactor this to not use static singleton instance.
 | 
			
		||||
 */
 | 
			
		||||
public class OrderableListPanel extends JPanel implements ModelChangeListener {
 | 
			
		||||
	private static OrderableListPanel instance;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,5 @@
 | 
			
		|||
package nl.andrewlalis.erme.view.view_models;
 | 
			
		||||
 | 
			
		||||
import nl.andrewlalis.erme.control.actions.LolcatAction;
 | 
			
		||||
import nl.andrewlalis.erme.model.Attribute;
 | 
			
		||||
import nl.andrewlalis.erme.model.AttributeType;
 | 
			
		||||
import nl.andrewlalis.erme.model.ForeignKeyAttribute;
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +45,7 @@ public class AttributeViewModel implements ViewModel {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	private Color getBackgroundColor(int x, int y, Graphics2D g) {
 | 
			
		||||
		if (!LolcatAction.getInstance().isLolcatEnabled()) return BACKGROUND_COLOR;
 | 
			
		||||
		if (!attribute.getRelation().getModel().isLolcatEnabled()) return BACKGROUND_COLOR;
 | 
			
		||||
		Point offset = g.getClipBounds().getLocation();
 | 
			
		||||
		g.translate(offset.x, offset.y);
 | 
			
		||||
		Dimension viewportSize = g.getClipBounds().getSize();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,11 @@
 | 
			
		|||
package nl.andrewlalis.erme.view.view_models;
 | 
			
		||||
 | 
			
		||||
import nl.andrewlalis.erme.control.actions.VisualizeReferencesAction;
 | 
			
		||||
import nl.andrewlalis.erme.model.Attribute;
 | 
			
		||||
import nl.andrewlalis.erme.model.ForeignKeyAttribute;
 | 
			
		||||
import nl.andrewlalis.erme.model.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.model.Relation;
 | 
			
		||||
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.color.ColorSpace;
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -22,9 +20,7 @@ public class MappingModelViewModel implements ViewModel {
 | 
			
		|||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void draw(Graphics2D g) {
 | 
			
		||||
		if (VisualizeReferencesAction.getInstance().isReferenceVisualizationEnabled()) {
 | 
			
		||||
			visualizeReferences(g);
 | 
			
		||||
		}
 | 
			
		||||
		if (model.isReferenceVisualizationEnabled()) visualizeReferences(g);
 | 
			
		||||
		for (Relation r : this.model.getRelations()) {
 | 
			
		||||
			r.getViewModel().draw(g);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,9 +7,13 @@
 | 
			
		|||
<body>
 | 
			
		||||
 | 
			
		||||
<h1>Entity-Relation Mapping Editor</h1>
 | 
			
		||||
<p>Created by <a href="https://github.com/andrewlalis">@andrewlalis</a></p>
 | 
			
		||||
 | 
			
		||||
<p><em>A simple UI for editing entity-relation mapping diagrams.</em></p>
 | 
			
		||||
<p>Created by <a href="https://github.com/andrewlalis">Andrew Lalis</a></p>
 | 
			
		||||
<p>And with generous contributions by</p>
 | 
			
		||||
<ul>
 | 
			
		||||
    <li><a href="https://github.com/bjornpijnacker">Bjorn Pijnacker</a></li>
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
<p>
 | 
			
		||||
    Have you noticed any unexpected behavior? Is there something you think would make a good addition to this application?
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +31,9 @@
 | 
			
		|||
    <li>
 | 
			
		||||
        The <em>Edit</em> menu contains options for making changes to the current model, such as adding or removing relations and attributes, or undoing/redoing actions.
 | 
			
		||||
    </li>
 | 
			
		||||
    <li>
 | 
			
		||||
        The <em>View</em> menu contains options for changing how the model is viewed.
 | 
			
		||||
    </li>
 | 
			
		||||
    <li>
 | 
			
		||||
        The <em>Help</em> menu contains some items with additional information about the application, like this help page and a simple <em>About</em> popup with version information. There's also a <em>Load Sample Model</em> option, which will load a very basic sample model into the application that you are free to mess around with.
 | 
			
		||||
    </li>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue