Merge pull request #10 from bjornpijnacker/auto-position
Implemented Automatic Alignment
This commit is contained in:
		
						commit
						a8966d6051
					
				| 
						 | 
				
			
			@ -0,0 +1,103 @@
 | 
			
		|||
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;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
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 {
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public void actionPerformed(ActionEvent actionEvent) {
 | 
			
		||||
		if (model.getRelations().size() == 0) {
 | 
			
		||||
			JOptionPane.showMessageDialog(
 | 
			
		||||
					this.diagramPanel,
 | 
			
		||||
					"Cannot position all relations when there are no relations present",
 | 
			
		||||
					"Relations Required",
 | 
			
		||||
					JOptionPane.WARNING_MESSAGE
 | 
			
		||||
			);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		String[] choices = new String[]{"Alphabeticaly", "Custom Order"};
 | 
			
		||||
		String choice = (String) JOptionPane.showInputDialog(
 | 
			
		||||
				this.diagramPanel,
 | 
			
		||||
				"Select how to sort the relations",
 | 
			
		||||
				"Position Relations",
 | 
			
		||||
				JOptionPane.PLAIN_MESSAGE,
 | 
			
		||||
				null,
 | 
			
		||||
				choices,
 | 
			
		||||
				0);
 | 
			
		||||
		if (choice == null) return;
 | 
			
		||||
		if (choice.equals(choices[0])) {
 | 
			
		||||
			positionRelations(getAlphabeticRelationList());
 | 
			
		||||
		} else if (choice.equals(choices[1])) {
 | 
			
		||||
			JOptionPane.showConfirmDialog(
 | 
			
		||||
					this.diagramPanel,
 | 
			
		||||
					OrderableListPanel.getInstance(),
 | 
			
		||||
					"teststring",
 | 
			
		||||
					JOptionPane.OK_CANCEL_OPTION,
 | 
			
		||||
					JOptionPane.PLAIN_MESSAGE);
 | 
			
		||||
			this.positionRelations(OrderableListPanel.getInstance().getOrderList());
 | 
			
		||||
		}
 | 
			
		||||
		diagramPanel.repaint();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Sets the location on all relations in the orderList to be vertically aligned
 | 
			
		||||
	 * @param orderList The ordered list of relations to be aligned
 | 
			
		||||
	 */
 | 
			
		||||
	private void positionRelations(ArrayList<Relation> orderList) {
 | 
			
		||||
		if (orderList.isEmpty()) return;
 | 
			
		||||
		AtomicInteger vertSpace = new AtomicInteger(0);
 | 
			
		||||
		orderList.forEach(r -> {
 | 
			
		||||
			int height = (int) r.getViewModel().getBounds(diagramPanel.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();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Creates an orderList by grabbing all relations and sorting them
 | 
			
		||||
	 */
 | 
			
		||||
	private ArrayList<Relation> getAlphabeticRelationList() {
 | 
			
		||||
		ArrayList<Relation> relationList = new ArrayList<>(model.getRelations());
 | 
			
		||||
		Collections.sort(relationList);
 | 
			
		||||
		return relationList;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
package nl.andrewlalis.erme.model;
 | 
			
		||||
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import nl.andrewlalis.erme.view.OrderableListPanel;
 | 
			
		||||
import nl.andrewlalis.erme.view.view_models.MappingModelViewModel;
 | 
			
		||||
import nl.andrewlalis.erme.view.view_models.ViewModel;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ import java.util.stream.Collectors;
 | 
			
		|||
 * Represents a single "relation" or table in the diagram.
 | 
			
		||||
 */
 | 
			
		||||
@Getter
 | 
			
		||||
public class Relation implements Serializable, Viewable {
 | 
			
		||||
public class Relation implements Serializable, Viewable, Comparable<Relation> {
 | 
			
		||||
	private final MappingModel model;
 | 
			
		||||
	private Point position;
 | 
			
		||||
	private String name;
 | 
			
		||||
| 
						 | 
				
			
			@ -97,4 +97,9 @@ public class Relation implements Serializable, Viewable {
 | 
			
		|||
		this.getAttributes().forEach(a -> c.addAttribute(a.copy(c)));
 | 
			
		||||
		return c;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public int compareTo(Relation relation) {
 | 
			
		||||
		return this.name.compareTo(relation.name);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,6 +64,7 @@ public class DiagramPanel extends JPanel implements ModelChangeListener {
 | 
			
		|||
		this.addMouseListener(listener);
 | 
			
		||||
		this.addMouseMotionListener(listener);
 | 
			
		||||
		this.updateActionModels();
 | 
			
		||||
		newModel.addChangeListener(OrderableListPanel.getInstance());
 | 
			
		||||
		this.centerModel();
 | 
			
		||||
		this.repaint();
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +134,10 @@ public class DiagramPanel extends JPanel implements ModelChangeListener {
 | 
			
		|||
		RemoveAttributeAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		LoadSampleModelAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		LolcatAction.getInstance().setDiagramPanel(this);
 | 
			
		||||
		AboutAction.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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,6 +40,7 @@ public class EditorMenuBar extends JMenuBar {
 | 
			
		|||
		menu.add(AddAttributeAction.getInstance());
 | 
			
		||||
		menu.add(RemoveAttributeAction.getInstance());
 | 
			
		||||
		menu.add(new JCheckBoxMenuItem(LolcatAction.getInstance()));
 | 
			
		||||
		menu.add(AutoPositionAction.getInstance());
 | 
			
		||||
		menu.addSeparator();
 | 
			
		||||
		menu.add(UndoAction.getInstance());
 | 
			
		||||
		menu.add(RedoAction.getInstance());
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
package nl.andrewlalis.erme.view;
 | 
			
		||||
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
import nl.andrewlalis.erme.model.Relation;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A custom model for a ListModel that supports ordering by publically accessable methods
 | 
			
		||||
 */
 | 
			
		||||
public class OrderableListModel extends AbstractListModel<Relation> {
 | 
			
		||||
	@Getter
 | 
			
		||||
	private final ArrayList<Relation> list = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
	public OrderableListModel() {
 | 
			
		||||
		super();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void addAll(Collection<Relation> relations) {
 | 
			
		||||
		list.addAll(relations);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void removeAll(Collection<Relation> relations) {
 | 
			
		||||
		list.removeAll(relations);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void add(Relation relation) {
 | 
			
		||||
		list.add(relation);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void remove(Relation relation) {
 | 
			
		||||
		list.remove(relation);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void moveUp(int index) {
 | 
			
		||||
		if (index > 0) {
 | 
			
		||||
			Collections.swap(list, index, index - 1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		this.fireContentsChanged(this, index, index - 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void moveDown(int index) {
 | 
			
		||||
        if (index < this.getSize() - 1) {
 | 
			
		||||
            Collections.swap(list, index, index + 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.fireContentsChanged(this, index, index + 1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public int getSize() {
 | 
			
		||||
		return list.size();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Relation getElementAt(int i) {
 | 
			
		||||
		return list.get(i);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
package nl.andrewlalis.erme.view;
 | 
			
		||||
 | 
			
		||||
import lombok.Setter;
 | 
			
		||||
import nl.andrewlalis.erme.model.MappingModel;
 | 
			
		||||
import nl.andrewlalis.erme.model.ModelChangeListener;
 | 
			
		||||
import nl.andrewlalis.erme.model.Relation;
 | 
			
		||||
 | 
			
		||||
import javax.swing.*;
 | 
			
		||||
import java.awt.event.ActionEvent;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
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.
 | 
			
		||||
 */
 | 
			
		||||
public class OrderableListPanel extends JPanel implements ModelChangeListener {
 | 
			
		||||
	private static OrderableListPanel instance;
 | 
			
		||||
	private final JList<Relation> list;
 | 
			
		||||
	private final OrderableListModel listModel;
 | 
			
		||||
	@Setter
 | 
			
		||||
	private MappingModel model;
 | 
			
		||||
 | 
			
		||||
	private OrderableListPanel() {
 | 
			
		||||
		list = new JList<>();
 | 
			
		||||
		listModel = new OrderableListModel();
 | 
			
		||||
		list.setModel(listModel);
 | 
			
		||||
 | 
			
		||||
		JButton up = new JButton(new AbstractAction() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void actionPerformed(ActionEvent actionEvent) {
 | 
			
		||||
				if (list.isSelectionEmpty()) return;
 | 
			
		||||
				listModel.moveUp(list.getSelectedIndex());
 | 
			
		||||
				list.setSelectedIndex(list.getSelectedIndex() - 1);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		up.setText("\u2227");
 | 
			
		||||
		JButton down = new JButton(new AbstractAction() {
 | 
			
		||||
			@Override
 | 
			
		||||
			public void actionPerformed(ActionEvent actionEvent) {
 | 
			
		||||
				if (list.isSelectionEmpty()) return;
 | 
			
		||||
				listModel.moveDown(list.getSelectedIndex());
 | 
			
		||||
				list.setSelectedIndex(list.getSelectedIndex() + 1);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
		down.setText("\u2228");
 | 
			
		||||
 | 
			
		||||
		this.add(list);
 | 
			
		||||
		this.add(up);
 | 
			
		||||
		this.add(down);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static OrderableListPanel getInstance() {
 | 
			
		||||
		if (instance == null) {
 | 
			
		||||
			instance = new OrderableListPanel();
 | 
			
		||||
		}
 | 
			
		||||
		return instance;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Updates removed and new relations in the listModel. Does it in a special way to preserve existing ordering in the
 | 
			
		||||
	 * list, so user has to do minimal re-sorting.
 | 
			
		||||
	 */
 | 
			
		||||
	@Override
 | 
			
		||||
	public void onModelChanged() {
 | 
			
		||||
		if (this.model == null) return;
 | 
			
		||||
		Set<Relation> newRelations = new HashSet<>(model.getRelations());
 | 
			
		||||
		newRelations.removeAll(listModel.getList());
 | 
			
		||||
 | 
			
		||||
		Set<Relation> removedRelations = new HashSet<>(listModel.getList());
 | 
			
		||||
		removedRelations.removeAll(model.getRelations());
 | 
			
		||||
 | 
			
		||||
		listModel.removeAll(removedRelations);
 | 
			
		||||
		listModel.addAll(newRelations);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public ArrayList<Relation> getOrderList() {
 | 
			
		||||
		return new ArrayList<>(listModel.getList());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue