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;
|
package nl.andrewlalis.erme.model;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import nl.andrewlalis.erme.view.OrderableListPanel;
|
||||||
import nl.andrewlalis.erme.view.view_models.MappingModelViewModel;
|
import nl.andrewlalis.erme.view.view_models.MappingModelViewModel;
|
||||||
import nl.andrewlalis.erme.view.view_models.ViewModel;
|
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.
|
* Represents a single "relation" or table in the diagram.
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public class Relation implements Serializable, Viewable {
|
public class Relation implements Serializable, Viewable, Comparable<Relation> {
|
||||||
private final MappingModel model;
|
private final MappingModel model;
|
||||||
private Point position;
|
private Point position;
|
||||||
private String name;
|
private String name;
|
||||||
|
@ -97,4 +97,9 @@ public class Relation implements Serializable, Viewable {
|
||||||
this.getAttributes().forEach(a -> c.addAttribute(a.copy(c)));
|
this.getAttributes().forEach(a -> c.addAttribute(a.copy(c)));
|
||||||
return 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.addMouseListener(listener);
|
||||||
this.addMouseMotionListener(listener);
|
this.addMouseMotionListener(listener);
|
||||||
this.updateActionModels();
|
this.updateActionModels();
|
||||||
|
newModel.addChangeListener(OrderableListPanel.getInstance());
|
||||||
this.centerModel();
|
this.centerModel();
|
||||||
this.repaint();
|
this.repaint();
|
||||||
}
|
}
|
||||||
|
@ -133,6 +134,9 @@ public class DiagramPanel extends JPanel implements ModelChangeListener {
|
||||||
RemoveAttributeAction.getInstance().setDiagramPanel(this);
|
RemoveAttributeAction.getInstance().setDiagramPanel(this);
|
||||||
LoadSampleModelAction.getInstance().setDiagramPanel(this);
|
LoadSampleModelAction.getInstance().setDiagramPanel(this);
|
||||||
LolcatAction.getInstance().setDiagramPanel(this);
|
LolcatAction.getInstance().setDiagramPanel(this);
|
||||||
|
AutoPositionAction.getInstance().setDiagramPanel(this);
|
||||||
|
AutoPositionAction.getInstance().setModel(this.model);
|
||||||
|
OrderableListPanel.getInstance().setModel(this.model);
|
||||||
AboutAction.getInstance().setDiagramPanel(this);
|
AboutAction.getInstance().setDiagramPanel(this);
|
||||||
ExitAction.getInstance().setDiagramPanel(this);
|
ExitAction.getInstance().setDiagramPanel(this);
|
||||||
InstructionsAction.getInstance().setDiagramPanel(this);
|
InstructionsAction.getInstance().setDiagramPanel(this);
|
||||||
|
|
|
@ -40,6 +40,7 @@ public class EditorMenuBar extends JMenuBar {
|
||||||
menu.add(AddAttributeAction.getInstance());
|
menu.add(AddAttributeAction.getInstance());
|
||||||
menu.add(RemoveAttributeAction.getInstance());
|
menu.add(RemoveAttributeAction.getInstance());
|
||||||
menu.add(new JCheckBoxMenuItem(LolcatAction.getInstance()));
|
menu.add(new JCheckBoxMenuItem(LolcatAction.getInstance()));
|
||||||
|
menu.add(AutoPositionAction.getInstance());
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
menu.add(UndoAction.getInstance());
|
menu.add(UndoAction.getInstance());
|
||||||
menu.add(RedoAction.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