diff --git a/src/main/java/org/gecko/actions/ActionFactory.java b/src/main/java/org/gecko/actions/ActionFactory.java index 49d74f71..9fdf8842 100644 --- a/src/main/java/org/gecko/actions/ActionFactory.java +++ b/src/main/java/org/gecko/actions/ActionFactory.java @@ -172,8 +172,8 @@ public MoveSystemConnectionViewModelElementAction createMoveSystemConnectionView elementScalerBlock, portViewModel, isVariableBlock); } - public PastePositionableViewModelElementAction createPastePositionableViewModelElementAction() { - return new PastePositionableViewModelElementAction(geckoViewModel); + public PastePositionableViewModelElementAction createPastePositionableViewModelElementAction(Point2D center) { + return new PastePositionableViewModelElementAction(geckoViewModel, center); } public RenameViewModelElementAction createRenameViewModelElementAction(Renamable renamable, String name) { diff --git a/src/main/java/org/gecko/actions/CopyPositionableViewModelElementAction.java b/src/main/java/org/gecko/actions/CopyPositionableViewModelElementAction.java index cb40edc6..00784c29 100644 --- a/src/main/java/org/gecko/actions/CopyPositionableViewModelElementAction.java +++ b/src/main/java/org/gecko/actions/CopyPositionableViewModelElementAction.java @@ -1,7 +1,15 @@ package org.gecko.actions; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; import org.gecko.exceptions.GeckoException; +import org.gecko.model.Edge; +import org.gecko.model.Element; +import org.gecko.model.System; +import org.gecko.model.SystemConnection; import org.gecko.viewmodel.GeckoViewModel; +import org.gecko.viewmodel.PositionableViewModelElement; public class CopyPositionableViewModelElementAction extends Action { GeckoViewModel geckoViewModel; @@ -14,10 +22,31 @@ public class CopyPositionableViewModelElementAction extends Action { boolean run() throws GeckoException { CopyPositionableViewModelElementVisitor visitor = new CopyPositionableViewModelElementVisitor(geckoViewModel); - geckoViewModel.getCurrentEditor() - .getSelectionManager() - .getCurrentSelection() - .forEach(element -> element.accept(visitor)); + Set> copyQueue = + geckoViewModel.getCurrentEditor().getSelectionManager().getCurrentSelection(); + Set elementToCopy = + copyQueue.stream().map(PositionableViewModelElement::getTarget).collect(Collectors.toSet()); + for (Edge edge : geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().getAutomaton().getEdges()) { + if (elementToCopy.contains(edge.getSource()) && elementToCopy.contains(edge.getDestination())) { + copyQueue.add(geckoViewModel.getViewModelElement(edge)); + } + } + for (SystemConnection connection : geckoViewModel.getCurrentEditor() + .getCurrentSystem() + .getTarget() + .getConnections()) { + System sourceSystem = geckoViewModel.getGeckoModel().getSystemWithVariable(connection.getSource()); + System destinationSystem = + geckoViewModel.getGeckoModel().getSystemWithVariable(connection.getDestination()); + if (elementToCopy.contains(sourceSystem) && elementToCopy.contains(destinationSystem)) { + copyQueue.add(geckoViewModel.getViewModelElement(connection)); + } + } + do { + visitor.getFailedCopies().clear(); + copyQueue.forEach(element -> element.accept(visitor)); + copyQueue = new HashSet<>(visitor.getFailedCopies()); + } while (!copyQueue.isEmpty()); geckoViewModel.getActionManager().setCopyVisitor(visitor); return true; diff --git a/src/main/java/org/gecko/actions/CopyPositionableViewModelElementVisitor.java b/src/main/java/org/gecko/actions/CopyPositionableViewModelElementVisitor.java index e52b2c66..c5c448b2 100644 --- a/src/main/java/org/gecko/actions/CopyPositionableViewModelElementVisitor.java +++ b/src/main/java/org/gecko/actions/CopyPositionableViewModelElementVisitor.java @@ -1,11 +1,15 @@ package org.gecko.actions; import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import javafx.geometry.Point2D; import javafx.util.Pair; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; +import org.gecko.exceptions.ModelException; import org.gecko.model.Contract; import org.gecko.model.Edge; import org.gecko.model.Element; @@ -17,6 +21,7 @@ import org.gecko.viewmodel.EdgeViewModel; import org.gecko.viewmodel.GeckoViewModel; import org.gecko.viewmodel.PortViewModel; +import org.gecko.viewmodel.PositionableViewModelElement; import org.gecko.viewmodel.PositionableViewModelElementVisitor; import org.gecko.viewmodel.RegionViewModel; import org.gecko.viewmodel.StateViewModel; @@ -29,90 +34,141 @@ public class CopyPositionableViewModelElementVisitor implements PositionableView @Getter(AccessLevel.NONE) private GeckoViewModel geckoViewModel; private boolean isAutomatonCopy; - private HashMap copiedStates; - private HashMap copiedSystems; - private HashMap copiedRegions; - private HashMap copiedEdges; - private HashMap copiedSystemConnections; - private HashMap copiedPorts; - private HashMap copiedContracts; - private HashMap> copiedPosAndSize; + private HashMap originalToClipboard; + private HashMap> elementToPosAndSize; + @Getter + private Set> failedCopies; + private final Set copiedElements; public CopyPositionableViewModelElementVisitor(GeckoViewModel geckoViewModel) { this.geckoViewModel = geckoViewModel; isAutomatonCopy = geckoViewModel.getCurrentEditor().isAutomatonEditor(); - copiedStates = new HashMap<>(); - copiedSystems = new HashMap<>(); - copiedRegions = new HashMap<>(); - copiedEdges = new HashMap<>(); - copiedSystemConnections = new HashMap<>(); - copiedPorts = new HashMap<>(); - copiedContracts = new HashMap<>(); - copiedPosAndSize = new HashMap<>(); + originalToClipboard = new HashMap<>(); + elementToPosAndSize = new HashMap<>(); + copiedElements = new HashSet<>(); + failedCopies = new HashSet<>(); } @Override public Void visit(SystemViewModel systemViewModel) { - copiedSystems.put(systemViewModel.getTarget(), - geckoViewModel.getGeckoModel().getModelFactory().copySystem(systemViewModel.getTarget())); - copiedPosAndSize.put(systemViewModel.getTarget(), - new Pair<>(systemViewModel.getPosition(), systemViewModel.getSize())); - for (PortViewModel p : systemViewModel.getPorts()) { - if (copiedPorts.get(p.getTarget()) == null) { - continue; - } - copiedPorts.put(p.getTarget(), - geckoViewModel.getGeckoModel().getModelFactory().copyVariable(p.getTarget())); - copiedPosAndSize.put(p.getTarget(), new Pair<>(p.getPosition(), p.getSize())); + System original = systemViewModel.getTarget(); + Pair> copyResult; + System copy; + try { + copyResult = geckoViewModel.getGeckoModel().getModelFactory().copySystem(systemViewModel.getTarget()); + } catch (ModelException e) { + failedCopies.add(systemViewModel); + return null; } + copy = copyResult.getKey(); + originalToClipboard.putAll(copyResult.getValue()); + originalToClipboard.put(original, copy); + savePositionRecursively(original); + copiedElements.add(copy); return null; } @Override public Void visit(RegionViewModel regionViewModel) { - copiedRegions.put(regionViewModel.getTarget(), - geckoViewModel.getGeckoModel().getModelFactory().copyRegion(regionViewModel.getTarget())); - copiedPosAndSize.put(regionViewModel.getTarget(), - new Pair<>(regionViewModel.getPosition(), regionViewModel.getSize())); + Region original = regionViewModel.getTarget(); + Region copy = geckoViewModel.getGeckoModel().getModelFactory().copyRegion(original); + originalToClipboard.put(original, copy); + savePositionAndSize(copy, regionViewModel); + copiedElements.add(copy); return null; } @Override public Void visit(EdgeViewModel edgeViewModel) { - var selection = geckoViewModel.getCurrentEditor().getSelectionManager().getCurrentSelection(); + Set> selection = + geckoViewModel.getCurrentEditor().getSelectionManager().getCurrentSelection(); if (selection.contains(edgeViewModel.getSource()) && selection.contains(edgeViewModel.getDestination())) { - copiedEdges.put(edgeViewModel.getTarget(), - geckoViewModel.getGeckoModel().getModelFactory().copyEdge(edgeViewModel.getTarget())); + Edge original = edgeViewModel.getTarget(); + Edge copy = geckoViewModel.getGeckoModel().getModelFactory().copyEdge(original); + State sourceOnClipboard = (State) originalToClipboard.get(original.getSource()); + State destinationOnClipboard = (State) originalToClipboard.get(original.getDestination()); + Contract contractOnClipboard = (Contract) originalToClipboard.get(original.getContract()); + if (sourceOnClipboard == null || destinationOnClipboard == null) { + failedCopies.add(edgeViewModel); + return null; + } + copy.setSource(sourceOnClipboard); + copy.setDestination(destinationOnClipboard); + copy.setContract(contractOnClipboard); + originalToClipboard.put(original, copy); + copiedElements.add(copy); } return null; } @Override public Void visit(StateViewModel stateViewModel) { - copiedStates.put(stateViewModel.getTarget(), - geckoViewModel.getGeckoModel().getModelFactory().copyState(stateViewModel.getTarget())); - copiedPosAndSize.put(stateViewModel.getTarget(), - new Pair<>(stateViewModel.getPosition(), stateViewModel.getSize())); - for (Contract c : stateViewModel.getTarget().getContracts()) { - copiedContracts.put(c, geckoViewModel.getGeckoModel().getModelFactory().copyContract(c)); - } - for (EdgeViewModel evm : stateViewModel.getOutgoingEdges()) { - evm.accept(this); - } + State original = stateViewModel.getTarget(); + Pair> copyResult = + geckoViewModel.getGeckoModel().getModelFactory().copyState(original); + State copy = copyResult.getKey(); + originalToClipboard.putAll(copyResult.getValue()); + originalToClipboard.put(original, copy); + savePositionAndSize(copy, stateViewModel); + copiedElements.add(copy); return null; } @Override public Void visit(PortViewModel portViewModel) { - copiedPorts.put(portViewModel.getTarget(), - geckoViewModel.getGeckoModel().getModelFactory().copyVariable(portViewModel.getTarget())); - copiedPosAndSize.put(portViewModel.getTarget(), - new Pair<>(portViewModel.getPosition(), portViewModel.getSize())); return null; } @Override public Void visit(SystemConnectionViewModel systemConnectionViewModel) { + Set> selection = + geckoViewModel.getCurrentEditor().getSelectionManager().getCurrentSelection(); + SystemViewModel sourceSystemViewModel = (SystemViewModel) geckoViewModel.getViewModelElement( + geckoViewModel.getCurrentEditor() + .getCurrentSystem() + .getTarget() + .getChildSystemWithVariable(systemConnectionViewModel.getTarget().getSource())); + SystemViewModel destinationSystemViewModel = (SystemViewModel) geckoViewModel.getViewModelElement( + geckoViewModel.getCurrentEditor() + .getCurrentSystem() + .getTarget() + .getChildSystemWithVariable(systemConnectionViewModel.getTarget().getDestination())); + if (selection.contains(sourceSystemViewModel) && selection.contains(destinationSystemViewModel)) { + SystemConnection original = systemConnectionViewModel.getTarget(); + SystemConnection copy = geckoViewModel.getGeckoModel().getModelFactory().copySystemConnection(original); + Variable sourceOnClipboard = (Variable) originalToClipboard.get(original.getSource()); + Variable destinationOnClipboard = (Variable) originalToClipboard.get(original.getDestination()); + if (sourceOnClipboard == null || destinationOnClipboard == null) { + failedCopies.add(systemConnectionViewModel); + return null; + } + try { + copy.setSource(sourceOnClipboard); + copy.setDestination(destinationOnClipboard); + } catch (ModelException e) { + throw new RuntimeException(e); + } + originalToClipboard.put(original, copy); + copiedElements.add(copy); + } return null; } + + private void savePositionRecursively(System original) { + System copy = (System) originalToClipboard.get(original); + savePositionAndSize(copy, geckoViewModel.getViewModelElement(original)); + for (State state : original.getAutomaton().getStates()) { + savePositionAndSize(originalToClipboard.get(state), geckoViewModel.getViewModelElement(state)); + } + for (Region region : original.getAutomaton().getRegions()) { + savePositionAndSize(originalToClipboard.get(region), geckoViewModel.getViewModelElement(region)); + } + for (System child : original.getChildren()) { + savePositionRecursively(child); + } + } + + private void savePositionAndSize(Element key, PositionableViewModelElement positionSource) { + elementToPosAndSize.put(key, new Pair<>(positionSource.getPosition(), positionSource.getSize())); + } } diff --git a/src/main/java/org/gecko/actions/PastePositionableViewModelElementAction.java b/src/main/java/org/gecko/actions/PastePositionableViewModelElementAction.java index fdafaeed..e9ab4389 100644 --- a/src/main/java/org/gecko/actions/PastePositionableViewModelElementAction.java +++ b/src/main/java/org/gecko/actions/PastePositionableViewModelElementAction.java @@ -4,35 +4,24 @@ import java.util.Set; import javafx.geometry.Point2D; import org.gecko.exceptions.GeckoException; -import org.gecko.exceptions.MissingViewModelElementException; -import org.gecko.model.Contract; -import org.gecko.model.Edge; -import org.gecko.model.Region; -import org.gecko.model.State; -import org.gecko.model.System; -import org.gecko.model.Variable; -import org.gecko.viewmodel.EdgeViewModel; +import org.gecko.model.Element; import org.gecko.viewmodel.GeckoViewModel; -import org.gecko.viewmodel.PortViewModel; import org.gecko.viewmodel.PositionableViewModelElement; -import org.gecko.viewmodel.RegionViewModel; -import org.gecko.viewmodel.StateViewModel; -import org.gecko.viewmodel.SystemViewModel; public class PastePositionableViewModelElementAction extends Action { private final GeckoViewModel geckoViewModel; private final Set> pastedElements; - private static final Point2D PASTE_OFFSET = new Point2D(50, 50); - private CopyPositionableViewModelElementVisitor copyVisitor; + private final Point2D pasteOffset; - PastePositionableViewModelElementAction(GeckoViewModel geckoViewModel) { + PastePositionableViewModelElementAction(GeckoViewModel geckoViewModel, Point2D center) { this.geckoViewModel = geckoViewModel; - pastedElements = new HashSet<>(); + this.pastedElements = new HashSet<>(); + this.pasteOffset = center; } @Override boolean run() throws GeckoException { - copyVisitor = geckoViewModel.getActionManager().getCopyVisitor(); + CopyPositionableViewModelElementVisitor copyVisitor = geckoViewModel.getActionManager().getCopyVisitor(); if (copyVisitor == null) { throw new GeckoException("Invalid Clipboard. Nothing to paste."); } @@ -40,81 +29,24 @@ boolean run() throws GeckoException { return false; } - if (copyVisitor.isAutomatonCopy()) { - return pasteAutomaton(); - } else { - return pasteSystem(); + PastePositionableViewModelElementVisitor pasteVisitor = + new PastePositionableViewModelElementVisitor(geckoViewModel, copyVisitor, pasteOffset); + for (Element element : copyVisitor.getCopiedElements()) { + element.accept(pasteVisitor); } - } - - private boolean pasteSystem() { - for (Variable v : copyVisitor.getCopiedPorts().keySet()) { - if (!geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().getVariables().contains(v)) { - continue; - } - Variable copy = - geckoViewModel.getGeckoModel().getModelFactory().copyVariable(copyVisitor.getCopiedPorts().get(v)); - geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().addVariable(copy); - copyVisitor.getCopiedPorts().put(v, copy); - PortViewModel portViewModel = geckoViewModel.getViewModelFactory().createPortViewModelFrom(copy); - portViewModel.setPosition(copyVisitor.getCopiedPosAndSize().get(v).getKey().add(PASTE_OFFSET)); - pastedElements.add(portViewModel); - } - for (System s : copyVisitor.getCopiedSystems().keySet()) { - System copy = - geckoViewModel.getGeckoModel().getModelFactory().copySystem(copyVisitor.getCopiedSystems().get(s)); - geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().addChild(copy); - copy.getVariables().clear(); - for (Variable v : copyVisitor.getCopiedSystems().get(s).getVariables()) { - Variable newVariable = geckoViewModel.getGeckoModel().getModelFactory().copyVariable(v); - copy.addVariable(newVariable); - copyVisitor.getCopiedPorts().put(v, newVariable); - } - copyVisitor.getCopiedSystems().put(s, copy); - SystemViewModel systemViewModel = geckoViewModel.getViewModelFactory().createSystemViewModelFrom(copy); - systemViewModel.setPosition(copyVisitor.getCopiedPosAndSize().get(s).getKey().add(PASTE_OFFSET)); - pastedElements.add(systemViewModel); - } - return false; - } - - private boolean pasteAutomaton() throws MissingViewModelElementException { - for (State state : copyVisitor.getCopiedStates().keySet()) { - State copy = - geckoViewModel.getGeckoModel().getModelFactory().copyState(copyVisitor.getCopiedStates().get(state)); - geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().getAutomaton().addState(copy); - copy.getContracts().clear(); - for (Contract c : copyVisitor.getCopiedStates().get(state).getContracts()) { - Contract newContract = geckoViewModel.getGeckoModel().getModelFactory().copyContract(c); - copy.addContract(newContract); - copyVisitor.getCopiedContracts().put(c, newContract); + while (!pasteVisitor.getUnsuccessfulPastes().isEmpty()) { + Set unsuccessfulPastes = new HashSet<>(pasteVisitor.getUnsuccessfulPastes()); + pasteVisitor.getUnsuccessfulPastes().clear(); + for (Element element : unsuccessfulPastes) { + element.accept(pasteVisitor); } - copyVisitor.getCopiedStates().put(state, copy); - StateViewModel stateViewModel = geckoViewModel.getViewModelFactory().createStateViewModelFrom(copy); - stateViewModel.setPosition(copyVisitor.getCopiedPosAndSize().get(state).getKey().add(PASTE_OFFSET)); - stateViewModel.setSize(copyVisitor.getCopiedPosAndSize().get(state).getValue()); - pastedElements.add(stateViewModel); - } - for (Region region : copyVisitor.getCopiedRegions().keySet()) { - Region copy = - geckoViewModel.getGeckoModel().getModelFactory().copyRegion(copyVisitor.getCopiedRegions().get(region)); - geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().getAutomaton().addRegion(copy); - RegionViewModel regionViewModel = geckoViewModel.getViewModelFactory().createRegionViewModelFrom(copy); - regionViewModel.setPosition(copyVisitor.getCopiedPosAndSize().get(region).getKey().add(PASTE_OFFSET)); - regionViewModel.setSize(copyVisitor.getCopiedPosAndSize().get(region).getValue()); - pastedElements.add(regionViewModel); - } - for (Edge edge : copyVisitor.getCopiedEdges().keySet()) { - Edge copy = - geckoViewModel.getGeckoModel().getModelFactory().copyEdge(copyVisitor.getCopiedEdges().get(edge)); - geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().getAutomaton().addEdge(copy); - copy.setSource(copyVisitor.getCopiedStates().get(edge.getSource())); - copy.setDestination(copyVisitor.getCopiedStates().get(edge.getDestination())); - copy.setContract(copyVisitor.getCopiedContracts().get(edge.getContract())); - EdgeViewModel edgeViewModel = geckoViewModel.getViewModelFactory().createEdgeViewModelFrom(copy); - pastedElements.add(edgeViewModel); } - return false; + pasteVisitor.updatePositions(); + pastedElements.addAll(pasteVisitor.getPastedElements()); + /*Action selectAction = + geckoViewModel.getActionManager().getActionFactory().createSelectAction(pastedElements, true); + geckoViewModel.getActionManager().run(selectAction);*/ + return true; } @Override diff --git a/src/main/java/org/gecko/actions/PastePositionableViewModelElementVisitor.java b/src/main/java/org/gecko/actions/PastePositionableViewModelElementVisitor.java new file mode 100644 index 00000000..70388a5c --- /dev/null +++ b/src/main/java/org/gecko/actions/PastePositionableViewModelElementVisitor.java @@ -0,0 +1,220 @@ +package org.gecko.actions; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import javafx.geometry.Point2D; +import javafx.util.Pair; +import lombok.Getter; +import org.gecko.exceptions.MissingViewModelElementException; +import org.gecko.exceptions.ModelException; +import org.gecko.model.Automaton; +import org.gecko.model.Contract; +import org.gecko.model.Edge; +import org.gecko.model.Element; +import org.gecko.model.ElementVisitor; +import org.gecko.model.Region; +import org.gecko.model.State; +import org.gecko.model.System; +import org.gecko.model.SystemConnection; +import org.gecko.model.Variable; +import org.gecko.viewmodel.EdgeViewModel; +import org.gecko.viewmodel.GeckoViewModel; +import org.gecko.viewmodel.PortViewModel; +import org.gecko.viewmodel.PositionableViewModelElement; +import org.gecko.viewmodel.RegionViewModel; +import org.gecko.viewmodel.StateViewModel; +import org.gecko.viewmodel.SystemConnectionViewModel; +import org.gecko.viewmodel.SystemViewModel; + +public class PastePositionableViewModelElementVisitor implements ElementVisitor { + private final GeckoViewModel geckoViewModel; + private final CopyPositionableViewModelElementVisitor copyVisitor; + private final Point2D pasteOffset; + @Getter + private final Set> pastedElements; + private final BiMap clipboardToPasted; + @Getter + private final Set unsuccessfulPastes; + + PastePositionableViewModelElementVisitor( + GeckoViewModel geckoViewModel, CopyPositionableViewModelElementVisitor copyVisitor, Point2D pasteOffset) { + this.geckoViewModel = geckoViewModel; + this.copyVisitor = copyVisitor; + pastedElements = new HashSet<>(); + clipboardToPasted = HashBiMap.create(); + unsuccessfulPastes = new HashSet<>(); + this.pasteOffset = pasteOffset; + } + + @Override + public void visit(State stateFromClipboard) throws ModelException { + Pair> copyResult = + geckoViewModel.getGeckoModel().getModelFactory().copyState(stateFromClipboard); + State stateToPaste = copyResult.getKey(); + clipboardToPasted.putAll(copyResult.getValue()); + Automaton automaton = geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().getAutomaton(); + automaton.addState(stateToPaste); + if (automaton.getStartState() == null) { + automaton.setStartState(stateToPaste); + } + StateViewModel stateViewModel = geckoViewModel.getViewModelFactory().createStateViewModelFrom(stateToPaste); + stateViewModel.setPosition(copyVisitor.getElementToPosAndSize().get(stateFromClipboard).getKey()); + stateViewModel.setSize(copyVisitor.getElementToPosAndSize().get(stateFromClipboard).getValue()); + clipboardToPasted.put(stateFromClipboard, stateToPaste); + pastedElements.add(stateViewModel); + } + + @Override + public void visit(Contract contract) { + } + + @Override + public void visit(SystemConnection connectionFromClipboard) throws MissingViewModelElementException { + SystemConnection connectionToPaste = + geckoViewModel.getGeckoModel().getModelFactory().copySystemConnection(connectionFromClipboard); + Variable pastedSource = (Variable) clipboardToPasted.get(connectionFromClipboard.getSource()); + Variable pastedDestination = (Variable) clipboardToPasted.get(connectionFromClipboard.getDestination()); + if (pastedSource == null || pastedDestination == null) { + unsuccessfulPastes.add(connectionFromClipboard); + return; + } + try { + connectionToPaste.setSource(pastedSource); + connectionToPaste.setDestination(pastedDestination); + } catch (ModelException e) { + throw new RuntimeException(e); + } + geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().addConnection(connectionToPaste); + SystemConnectionViewModel systemConnectionViewModel = + geckoViewModel.getViewModelFactory().createSystemConnectionViewModelFrom(connectionToPaste); + pastedElements.add(systemConnectionViewModel); + } + + @Override + public void visit(Variable variableFromClipboard) throws ModelException { + if (!geckoViewModel.getCurrentEditor() + .getCurrentSystem() + .getTarget() + .getVariables() + .contains(variableFromClipboard)) { + return; + } + Variable variableToPaste = geckoViewModel.getGeckoModel().getModelFactory().copyVariable(variableFromClipboard); + geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().addVariable(variableToPaste); + PortViewModel portViewModel = geckoViewModel.getViewModelFactory().createPortViewModelFrom(variableToPaste); + clipboardToPasted.put(variableFromClipboard, variableToPaste); + pastedElements.add(portViewModel); + } + + @Override + public void visit(System systemFromClipboard) throws ModelException { + if (systemFromClipboard.getParent() != null) { + return; + } + Pair> copyResult = + geckoViewModel.getGeckoModel().getModelFactory().copySystem(systemFromClipboard); + System systemToPaste = copyResult.getKey(); + clipboardToPasted.putAll(copyResult.getValue()); + geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().addChild(systemToPaste); + systemToPaste.setParent(geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget()); + createRecursiveSystemViewModels(systemToPaste); + clipboardToPasted.put(systemFromClipboard, systemToPaste); + } + + @Override + public void visit(Region regionFromClipboard) throws ModelException, MissingViewModelElementException { + Region regionToPaste = geckoViewModel.getGeckoModel().getModelFactory().copyRegion(regionFromClipboard); + clipboardToPasted.put(regionFromClipboard, regionToPaste); + geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().getAutomaton().addRegion(regionToPaste); + RegionViewModel regionViewModel = geckoViewModel.getViewModelFactory().createRegionViewModelFrom(regionToPaste); + regionViewModel.setPosition( + copyVisitor.getElementToPosAndSize().get(regionFromClipboard).getKey().add(pasteOffset)); + regionViewModel.setSize(copyVisitor.getElementToPosAndSize().get(regionFromClipboard).getValue()); + pastedElements.add(regionViewModel); + } + + @Override + public void visit(Edge edge) throws ModelException, MissingViewModelElementException { + Edge copy = geckoViewModel.getGeckoModel().getModelFactory().copyEdge(edge); + State pastedSource = (State) clipboardToPasted.get(edge.getSource()); + State pastedDestination = (State) clipboardToPasted.get(edge.getDestination()); + Contract pastedContract = (Contract) clipboardToPasted.get(edge.getContract()); + if (pastedSource == null || pastedDestination == null) { + unsuccessfulPastes.add(edge); + return; + } + geckoViewModel.getCurrentEditor().getCurrentSystem().getTarget().getAutomaton().addEdge(copy); + copy.setSource(pastedSource); + copy.setDestination(pastedDestination); + copy.setContract(pastedContract); + EdgeViewModel edgeViewModel = geckoViewModel.getViewModelFactory().createEdgeViewModelFrom(copy); + pastedElements.add(edgeViewModel); + } + + private void createRecursiveSystemViewModels(System systemToPaste) { + System systemFromClipboard = (System) clipboardToPasted.inverse().get(systemToPaste); + SystemViewModel systemViewModel = geckoViewModel.getViewModelFactory().createSystemViewModelFrom(systemToPaste); + systemViewModel.setPosition(copyVisitor.getElementToPosAndSize().get(systemFromClipboard).getKey()); + pastedElements.add(systemViewModel); + for (Variable variable : systemToPaste.getVariables()) { + geckoViewModel.getViewModelFactory().createPortViewModelFrom(variable); + } + for (State state : systemToPaste.getAutomaton().getStates()) { + StateViewModel stateViewModel = geckoViewModel.getViewModelFactory().createStateViewModelFrom(state); + stateViewModel.setPosition( + copyVisitor.getElementToPosAndSize().get(clipboardToPasted.inverse().get(state)).getKey()); + } + for (Region region : systemToPaste.getAutomaton().getRegions()) { + try { + RegionViewModel regionViewModel = + geckoViewModel.getViewModelFactory().createRegionViewModelFrom(region); + regionViewModel.setPosition( + copyVisitor.getElementToPosAndSize().get(clipboardToPasted.inverse().get(region)).getKey()); + regionViewModel.setSize( + copyVisitor.getElementToPosAndSize().get(clipboardToPasted.inverse().get(region)).getValue()); + } catch (MissingViewModelElementException e) { + throw new RuntimeException(e); + } + } + for (Edge edge : systemToPaste.getAutomaton().getEdges()) { + try { + geckoViewModel.getViewModelFactory().createEdgeViewModelFrom(edge); + } catch (MissingViewModelElementException e) { + throw new RuntimeException(e); + } + } + for (System child : systemToPaste.getChildren()) { + createRecursiveSystemViewModels(child); + } + for (SystemConnection connection : systemToPaste.getConnections()) { + try { + geckoViewModel.getViewModelFactory().createSystemConnectionViewModelFrom(connection); + } catch (MissingViewModelElementException e) { + throw new RuntimeException(e); + } + } + } + + public void updatePositions() { + Point2D minPos = new Point2D(Double.MAX_VALUE, Double.MAX_VALUE); + Point2D maxPos = new Point2D(-Double.MAX_VALUE, -Double.MAX_VALUE); + for (PositionableViewModelElement element : pastedElements) { + if (element.getSize().equals(Point2D.ZERO)) { + continue; + } + double x = element.getPosition().getX(); + double y = element.getPosition().getY(); + minPos = new Point2D(Math.min(minPos.getX(), x), Math.min(minPos.getY(), y)); + maxPos = new Point2D(Math.max(maxPos.getX(), x + element.getSize().getX()), + Math.max(maxPos.getY(), y + element.getSize().getY())); + } + Point2D center = minPos.midpoint(maxPos); + for (PositionableViewModelElement element : pastedElements) { + Point2D pos = element.getCenter(); + element.setCenter(pos.subtract(center).add(pasteOffset)); + } + } +} diff --git a/src/main/java/org/gecko/model/Contract.java b/src/main/java/org/gecko/model/Contract.java index 860b8029..645619f7 100644 --- a/src/main/java/org/gecko/model/Contract.java +++ b/src/main/java/org/gecko/model/Contract.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import org.gecko.exceptions.MissingViewModelElementException; import org.gecko.exceptions.ModelException; /** @@ -44,4 +45,9 @@ public void setName(@NonNull String name) throws ModelException { public String toString() { return name; } + + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + visitor.visit(this); + } } diff --git a/src/main/java/org/gecko/model/Edge.java b/src/main/java/org/gecko/model/Edge.java index 96d89bce..aeff1223 100644 --- a/src/main/java/org/gecko/model/Edge.java +++ b/src/main/java/org/gecko/model/Edge.java @@ -5,6 +5,7 @@ import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import org.gecko.exceptions.MissingViewModelElementException; import org.gecko.exceptions.ModelException; /** @@ -43,4 +44,9 @@ public void setPriority(int priority) throws ModelException { } this.priority = priority; } + + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + visitor.visit(this); + } } diff --git a/src/main/java/org/gecko/model/Element.java b/src/main/java/org/gecko/model/Element.java index 6b4a4c14..89075527 100644 --- a/src/main/java/org/gecko/model/Element.java +++ b/src/main/java/org/gecko/model/Element.java @@ -2,6 +2,7 @@ import java.util.Objects; import lombok.Getter; +import org.gecko.exceptions.MissingViewModelElementException; import org.gecko.exceptions.ModelException; /** @@ -18,6 +19,8 @@ protected Element(int id) throws ModelException { this.id = id; } + public abstract void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException; + @Override public int hashCode() { return Objects.hash(id); diff --git a/src/main/java/org/gecko/model/ElementVisitor.java b/src/main/java/org/gecko/model/ElementVisitor.java new file mode 100644 index 00000000..3797d019 --- /dev/null +++ b/src/main/java/org/gecko/model/ElementVisitor.java @@ -0,0 +1,24 @@ +package org.gecko.model; + +import org.gecko.exceptions.MissingViewModelElementException; +import org.gecko.exceptions.ModelException; + +/** + * Represents a visitor pattern for performing operations on {@link Element}s. Concrete visitors must implement this + * interface to define specific behavior for each {@link Element}. + */ +public interface ElementVisitor { + void visit(State state) throws ModelException; + + void visit(Contract contract); + + void visit(SystemConnection systemConnection) throws ModelException, MissingViewModelElementException; + + void visit(Variable variable) throws ModelException; + + void visit(System system) throws ModelException; + + void visit(Region region) throws ModelException, MissingViewModelElementException; + + void visit(Edge edge) throws ModelException, MissingViewModelElementException; +} diff --git a/src/main/java/org/gecko/model/ModelFactory.java b/src/main/java/org/gecko/model/ModelFactory.java index 8280c98b..8cf88b3f 100644 --- a/src/main/java/org/gecko/model/ModelFactory.java +++ b/src/main/java/org/gecko/model/ModelFactory.java @@ -1,5 +1,8 @@ package org.gecko.model; +import java.util.HashMap; +import java.util.Map; +import javafx.util.Pair; import lombok.NonNull; import lombok.Setter; import org.gecko.exceptions.ModelException; @@ -49,18 +52,21 @@ public State createState(@NonNull Automaton automaton) throws ModelException { return state; } - public State copyState(@NonNull State state) { + public Pair> copyState(@NonNull State state) { int id = getNewElementId(); + Map contractToCopy = new HashMap<>(); State copy; try { copy = new State(id, getDefaultName(id)); - for (Contract contract : state.getContracts()) { - copy.addContract(contract); - } } catch (ModelException e) { throw new RuntimeException("Failed to create a copy of the state", e); } - return copy; + for (Contract contract : state.getContracts()) { + Contract copiedContract = copyContract(contract); + copy.addContract(copiedContract); + contractToCopy.put(contract, copiedContract); + } + return new Pair<>(copy, contractToCopy); } public Edge createEdge(@NonNull Automaton automaton, @NonNull State source, @NonNull State destination) @@ -95,18 +101,65 @@ public System createSystem(@NonNull System parentSystem) throws ModelException { return system; } - public System copySystem(@NonNull System system) { + public Pair> copySystem(@NonNull System system) throws ModelException { + return copySystem(system, new HashMap<>()); + } + + public Pair> copySystem(@NonNull System system, Map originalToCopy) + throws ModelException { int id = getNewElementId(); System copy; try { - copy = new System(id, getDefaultName(id), DEFAULT_CODE, system.getAutomaton()); - for (Variable variable : system.getVariables()) { - copy.addVariable(variable); - } + copy = new System(id, getDefaultName(id), DEFAULT_CODE, new Automaton()); } catch (ModelException e) { throw new RuntimeException("Failed to create a copy of the system", e); } - return copy; + for (State state : system.getAutomaton().getStates()) { + Pair> copyResult = copyState(state); + State copiedState = copyResult.getKey(); + originalToCopy.putAll(copyResult.getValue()); + copy.getAutomaton().addState(copiedState); + if (system.getAutomaton().getStartState().equals(state)) { + copy.getAutomaton().setStartState(copiedState); + } + originalToCopy.put(state, copiedState); + } + for (Edge edge : system.getAutomaton().getEdges()) { + State copiedSource = (State) originalToCopy.get(edge.getSource()); + State copiedDestination = (State) originalToCopy.get(edge.getDestination()); + Edge copiedEdge = createEdge(copy.getAutomaton(), copiedSource, copiedDestination); + copiedEdge.setContract((Contract) originalToCopy.get(edge.getContract())); + copiedEdge.setKind(edge.getKind()); + } + for (Region region : system.getAutomaton().getRegions()) { + Region copiedRegion = copyRegion(region); + copy.getAutomaton().addRegion(copiedRegion); + originalToCopy.put(region, copiedRegion); + } + for (Variable variable : system.getVariables()) { + Variable copiedVariable = copyVariable(variable); + originalToCopy.put(variable, copiedVariable); + copy.addVariable(copiedVariable); + } + for (System childSystem : system.getChildren()) { + System copiedChildSystem = copySystem(childSystem, originalToCopy).getKey(); + copy.addChild(copiedChildSystem); + originalToCopy.put(childSystem, copiedChildSystem); + + } + for (System childSystem : copy.getChildren()) { + childSystem.setParent(copy); + } + for (SystemConnection connection : system.getConnections()) { + SystemConnection copiedConnection = + copySystemConnection(connection, (Variable) originalToCopy.get(connection.getSource()), + (Variable) originalToCopy.get(connection.getDestination())); + copy.addConnection(copiedConnection); + originalToCopy.put(connection, copiedConnection); + } + copy.setCode(system.getCode()); + originalToCopy.put(system, copy); + return new Pair<>(copy, originalToCopy); } public System createRoot() throws ModelException { @@ -180,6 +233,18 @@ public SystemConnection copySystemConnection(SystemConnection connection) { } } + public SystemConnection copySystemConnection( + SystemConnection connection, Variable copiedSource, Variable copiedDestination) { + SystemConnection result = copySystemConnection(connection); + try { + result.setSource(copiedSource); + result.setDestination(copiedDestination); + } catch (ModelException e) { + throw new RuntimeException(e); + } + return result; + } + public Contract createContract(@NonNull State state) throws ModelException { int id = getNewElementId(); Contract contract = diff --git a/src/main/java/org/gecko/model/Region.java b/src/main/java/org/gecko/model/Region.java index 81f0b068..0f53ca37 100644 --- a/src/main/java/org/gecko/model/Region.java +++ b/src/main/java/org/gecko/model/Region.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import org.gecko.exceptions.MissingViewModelElementException; import org.gecko.exceptions.ModelException; /** @@ -42,6 +43,11 @@ public void setName(@NonNull String name) throws ModelException { this.name = name; } + @Override + public void accept(ElementVisitor visitor) throws MatchException, ModelException, MissingViewModelElementException { + visitor.visit(this); + } + public void addState(@NonNull State state) { states.add(state); } diff --git a/src/main/java/org/gecko/model/State.java b/src/main/java/org/gecko/model/State.java index af5704d7..ef30bbd3 100644 --- a/src/main/java/org/gecko/model/State.java +++ b/src/main/java/org/gecko/model/State.java @@ -6,6 +6,7 @@ import java.util.Set; import lombok.Getter; import lombok.NonNull; +import org.gecko.exceptions.MissingViewModelElementException; import org.gecko.exceptions.ModelException; /** @@ -52,4 +53,9 @@ public void removeContracts(@NonNull Set contracts) { removeContract(contract); } } + + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + visitor.visit(this); + } } diff --git a/src/main/java/org/gecko/model/System.java b/src/main/java/org/gecko/model/System.java index b56f1f78..e8d9fca9 100644 --- a/src/main/java/org/gecko/model/System.java +++ b/src/main/java/org/gecko/model/System.java @@ -10,6 +10,7 @@ import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import org.gecko.exceptions.MissingViewModelElementException; import org.gecko.exceptions.ModelException; /** @@ -19,6 +20,7 @@ * afferent data. */ @Getter +@SuppressWarnings("JavaLangClash") public class System extends Element implements Renamable { private final Set children; private final Set connections; @@ -153,4 +155,9 @@ public Set getAllElements() { public System getChildSystemWithVariable(Variable variable) { return children.stream().filter(child -> child.getVariables().contains(variable)).findFirst().orElse(null); } + + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + visitor.visit(this); + } } diff --git a/src/main/java/org/gecko/model/SystemConnection.java b/src/main/java/org/gecko/model/SystemConnection.java index d19ea18a..316d732c 100644 --- a/src/main/java/org/gecko/model/SystemConnection.java +++ b/src/main/java/org/gecko/model/SystemConnection.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.NonNull; +import org.gecko.exceptions.MissingViewModelElementException; import org.gecko.exceptions.ModelException; /** @@ -48,4 +49,9 @@ public void setDestination(@NonNull Variable destination) throws ModelException this.destination = destination; this.destination.setHasIncomingConnection(true); } + + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + visitor.visit(this); + } } diff --git a/src/main/java/org/gecko/model/Variable.java b/src/main/java/org/gecko/model/Variable.java index abed6fce..3cc015f7 100644 --- a/src/main/java/org/gecko/model/Variable.java +++ b/src/main/java/org/gecko/model/Variable.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import org.gecko.exceptions.MissingViewModelElementException; import org.gecko.exceptions.ModelException; /** @@ -14,11 +15,12 @@ * {@link Visibility}. */ @Getter -@Setter(onParam_ = {@NonNull}) +@Setter public class Variable extends Element implements Renamable { private String name; private String type; private String value; + @Setter(onParam_ = @NonNull) private Visibility visibility; private boolean hasIncomingConnection; @@ -62,4 +64,9 @@ public void setValue(String value) throws ModelException { } this.value = value; } + + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + visitor.visit(this); + } } diff --git a/src/main/java/org/gecko/view/contextmenu/EdgeViewElementContextMenuBuilder.java b/src/main/java/org/gecko/view/contextmenu/EdgeViewElementContextMenuBuilder.java index 3eef7d33..e5080df1 100644 --- a/src/main/java/org/gecko/view/contextmenu/EdgeViewElementContextMenuBuilder.java +++ b/src/main/java/org/gecko/view/contextmenu/EdgeViewElementContextMenuBuilder.java @@ -6,6 +6,7 @@ import javafx.scene.control.SeparatorMenuItem; import org.gecko.actions.ActionManager; import org.gecko.model.Kind; +import org.gecko.view.GeckoView; import org.gecko.view.ResourceHandler; import org.gecko.viewmodel.EdgeViewModel; @@ -19,8 +20,8 @@ public class EdgeViewElementContextMenuBuilder extends ViewContextMenuBuilder { private final EdgeViewModel edgeViewModel; public EdgeViewElementContextMenuBuilder( - ActionManager actionManager, EdgeViewModel edgeViewModel) { - super(actionManager); + ActionManager actionManager, EdgeViewModel edgeViewModel, GeckoView geckoView) { + super(actionManager, geckoView); this.edgeViewModel = edgeViewModel; } diff --git a/src/main/java/org/gecko/view/contextmenu/RegionViewElementContextMenuBuilder.java b/src/main/java/org/gecko/view/contextmenu/RegionViewElementContextMenuBuilder.java index bdbe6373..f2d1a896 100644 --- a/src/main/java/org/gecko/view/contextmenu/RegionViewElementContextMenuBuilder.java +++ b/src/main/java/org/gecko/view/contextmenu/RegionViewElementContextMenuBuilder.java @@ -4,6 +4,7 @@ import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; import org.gecko.actions.ActionManager; +import org.gecko.view.GeckoView; import org.gecko.view.ResourceHandler; import org.gecko.viewmodel.RegionViewModel; @@ -17,8 +18,8 @@ public class RegionViewElementContextMenuBuilder extends ViewContextMenuBuilder private final RegionViewModel regionViewModel; public RegionViewElementContextMenuBuilder( - ActionManager actionManager, RegionViewModel regionViewModel) { - super(actionManager); + ActionManager actionManager, RegionViewModel regionViewModel, GeckoView geckoView) { + super(actionManager, geckoView); this.regionViewModel = regionViewModel; } diff --git a/src/main/java/org/gecko/view/contextmenu/StateViewElementContextMenuBuilder.java b/src/main/java/org/gecko/view/contextmenu/StateViewElementContextMenuBuilder.java index 5fe8f523..9fc3895e 100644 --- a/src/main/java/org/gecko/view/contextmenu/StateViewElementContextMenuBuilder.java +++ b/src/main/java/org/gecko/view/contextmenu/StateViewElementContextMenuBuilder.java @@ -4,6 +4,7 @@ import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; import org.gecko.actions.ActionManager; +import org.gecko.view.GeckoView; import org.gecko.view.ResourceHandler; import org.gecko.viewmodel.StateViewModel; @@ -17,8 +18,8 @@ public class StateViewElementContextMenuBuilder extends ViewContextMenuBuilder { private final StateViewModel stateViewModel; public StateViewElementContextMenuBuilder( - ActionManager actionManager, StateViewModel stateViewModel) { - super(actionManager); + ActionManager actionManager, StateViewModel stateViewModel, GeckoView geckoView) { + super(actionManager, geckoView); this.stateViewModel = stateViewModel; } diff --git a/src/main/java/org/gecko/view/contextmenu/SystemConnectionViewElementContextMenuBuilder.java b/src/main/java/org/gecko/view/contextmenu/SystemConnectionViewElementContextMenuBuilder.java index 6bd236c0..4ea5f0a5 100644 --- a/src/main/java/org/gecko/view/contextmenu/SystemConnectionViewElementContextMenuBuilder.java +++ b/src/main/java/org/gecko/view/contextmenu/SystemConnectionViewElementContextMenuBuilder.java @@ -4,6 +4,7 @@ import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; import org.gecko.actions.ActionManager; +import org.gecko.view.GeckoView; import org.gecko.view.ResourceHandler; import org.gecko.viewmodel.SystemConnectionViewModel; @@ -17,8 +18,8 @@ public class SystemConnectionViewElementContextMenuBuilder extends ViewContextMe private final SystemConnectionViewModel systemConnectionViewModel; public SystemConnectionViewElementContextMenuBuilder( - ActionManager actionManager, SystemConnectionViewModel systemConnectionViewModel) { - super(actionManager); + ActionManager actionManager, SystemConnectionViewModel systemConnectionViewModel, GeckoView geckoView) { + super(actionManager, geckoView); this.systemConnectionViewModel = systemConnectionViewModel; } diff --git a/src/main/java/org/gecko/view/contextmenu/SystemViewElementContextMenuBuilder.java b/src/main/java/org/gecko/view/contextmenu/SystemViewElementContextMenuBuilder.java index 9c011a2b..ace005bc 100644 --- a/src/main/java/org/gecko/view/contextmenu/SystemViewElementContextMenuBuilder.java +++ b/src/main/java/org/gecko/view/contextmenu/SystemViewElementContextMenuBuilder.java @@ -4,6 +4,7 @@ import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; import org.gecko.actions.ActionManager; +import org.gecko.view.GeckoView; import org.gecko.view.ResourceHandler; import org.gecko.view.views.shortcuts.Shortcuts; import org.gecko.viewmodel.SystemViewModel; @@ -18,8 +19,8 @@ public class SystemViewElementContextMenuBuilder extends ViewContextMenuBuilder private final SystemViewModel systemViewModel; public SystemViewElementContextMenuBuilder( - ActionManager actionManager, SystemViewModel systemViewModel) { - super(actionManager); + ActionManager actionManager, SystemViewModel systemViewModel, GeckoView geckoView) { + super(actionManager, geckoView); this.systemViewModel = systemViewModel; } diff --git a/src/main/java/org/gecko/view/contextmenu/VariableBlockViewElementContextMenuBuilder.java b/src/main/java/org/gecko/view/contextmenu/VariableBlockViewElementContextMenuBuilder.java index c320a358..61e87fd4 100644 --- a/src/main/java/org/gecko/view/contextmenu/VariableBlockViewElementContextMenuBuilder.java +++ b/src/main/java/org/gecko/view/contextmenu/VariableBlockViewElementContextMenuBuilder.java @@ -4,6 +4,7 @@ import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; import org.gecko.actions.ActionManager; +import org.gecko.view.GeckoView; import org.gecko.view.ResourceHandler; import org.gecko.viewmodel.PortViewModel; @@ -17,8 +18,8 @@ public class VariableBlockViewElementContextMenuBuilder extends ViewContextMenuB private final PortViewModel portViewModel; public VariableBlockViewElementContextMenuBuilder( - ActionManager actionManager, PortViewModel portViewModel) { - super(actionManager); + ActionManager actionManager, PortViewModel portViewModel, GeckoView geckoView) { + super(actionManager, geckoView); this.portViewModel = portViewModel; } diff --git a/src/main/java/org/gecko/view/contextmenu/ViewContextMenuBuilder.java b/src/main/java/org/gecko/view/contextmenu/ViewContextMenuBuilder.java index 2d3696a1..00f8f6b2 100644 --- a/src/main/java/org/gecko/view/contextmenu/ViewContextMenuBuilder.java +++ b/src/main/java/org/gecko/view/contextmenu/ViewContextMenuBuilder.java @@ -1,13 +1,16 @@ package org.gecko.view.contextmenu; import javafx.beans.binding.Bindings; +import javafx.geometry.Point2D; import javafx.scene.control.ContextMenu; import javafx.scene.control.MenuItem; import javafx.scene.control.SeparatorMenuItem; import lombok.Getter; import lombok.Setter; import org.gecko.actions.ActionManager; +import org.gecko.view.GeckoView; import org.gecko.view.ResourceHandler; +import org.gecko.view.views.EditorView; import org.gecko.view.views.shortcuts.Shortcuts; import org.gecko.viewmodel.EditorViewModel; @@ -22,15 +25,18 @@ public class ViewContextMenuBuilder { protected EditorViewModel editorViewModel; @Getter protected ContextMenu contextMenu; + private final EditorView editorView; - public ViewContextMenuBuilder(ActionManager actionManager) { + public ViewContextMenuBuilder(ActionManager actionManager, GeckoView geckoView) { this.actionManager = actionManager; this.editorViewModel = null; + this.editorView = geckoView.getCurrentView(); } - public ViewContextMenuBuilder(ActionManager actionManager, EditorViewModel editorViewModel) { + public ViewContextMenuBuilder(ActionManager actionManager, EditorViewModel editorViewModel, EditorView editorView) { this.actionManager = actionManager; this.editorViewModel = editorViewModel; + this.editorView = editorView; } public ContextMenu build() { @@ -62,8 +68,10 @@ public ContextMenu build() { } MenuItem pasteMenuItem = new MenuItem(ResourceHandler.getString("Buttons", "paste")); - pasteMenuItem.setOnAction( - e -> actionManager.run(actionManager.getActionFactory().createPastePositionableViewModelElementAction())); + pasteMenuItem.setOnAction(e -> { + Point2D center = editorView.getViewElementPane().screenCenterWorldCoords(); + actionManager.run(actionManager.getActionFactory().createPastePositionableViewModelElementAction(center)); + }); pasteMenuItem.setAccelerator(Shortcuts.PASTE.get()); SeparatorMenuItem separatorMenuItem = new SeparatorMenuItem(); diff --git a/src/main/java/org/gecko/view/menubar/MenuBarBuilder.java b/src/main/java/org/gecko/view/menubar/MenuBarBuilder.java index feff1a60..6a188c07 100644 --- a/src/main/java/org/gecko/view/menubar/MenuBarBuilder.java +++ b/src/main/java/org/gecko/view/menubar/MenuBarBuilder.java @@ -3,6 +3,7 @@ import java.io.File; import java.util.Set; import javafx.beans.binding.Bindings; +import javafx.geometry.Point2D; import javafx.scene.control.CustomMenuItem; import javafx.scene.control.Label; import javafx.scene.control.Menu; @@ -142,8 +143,10 @@ private Menu setupEditMenu() { copyMenuItem.setAccelerator(Shortcuts.COPY.get()); MenuItem pasteMenuItem = new MenuItem(ResourceHandler.getString("Buttons", "paste")); - pasteMenuItem.setOnAction( - e -> actionManager.run(actionManager.getActionFactory().createPastePositionableViewModelElementAction())); + pasteMenuItem.setOnAction(e -> { + Point2D center = view.getCurrentView().getViewElementPane().screenCenterWorldCoords(); + actionManager.run(actionManager.getActionFactory().createPastePositionableViewModelElementAction(center)); + }); pasteMenuItem.setAccelerator(Shortcuts.PASTE.get()); // General selection commands: diff --git a/src/main/java/org/gecko/view/views/EditorView.java b/src/main/java/org/gecko/view/views/EditorView.java index 2c8af1df..b2214865 100644 --- a/src/main/java/org/gecko/view/views/EditorView.java +++ b/src/main/java/org/gecko/view/views/EditorView.java @@ -49,11 +49,10 @@ public class EditorView { private final StackPane currentViewPane; @Getter private final ToolBar toolBar; - private final ToolBarBuilder toolBarBuilder; private final InspectorFactory inspectorFactory; private final Inspector emptyInspector; private final Node searchWindow; - + @Getter private final ViewElementPane viewElementPane; @Getter @@ -70,7 +69,7 @@ public EditorView( ViewFactory viewFactory, ActionManager actionManager, EditorViewModel viewModel) { this.viewFactory = viewFactory; this.viewModel = viewModel; - this.toolBarBuilder = new ToolBarBuilder(actionManager, this, viewModel); + ToolBarBuilder toolBarBuilder = new ToolBarBuilder(actionManager, this, viewModel); this.toolBar = toolBarBuilder.build(); this.inspectorFactory = new InspectorFactory(actionManager, viewModel); @@ -86,8 +85,8 @@ public EditorView( StringProperty tabName = new SimpleStringProperty("Error_Name"); tabName.bind(Bindings.createStringBinding(() -> { String name = viewModel.getCurrentSystem().getName(); - return name + (viewModel.isAutomatonEditor() ? " (" + ResourceHandler.getString("View", "automaton") + ")" - : " (" + ResourceHandler.getString("View", "system") + ")"); + return name + (viewModel.isAutomatonEditor() ? " (" + ResourceHandler.getString("View", "automaton") + ")" : + " (" + ResourceHandler.getString("View", "system") + ")"); }, viewModel.getCurrentSystem().getNameProperty())); Label tabLabel = new Label(); @@ -129,7 +128,8 @@ public EditorView( // Set current tool viewModel.getCurrentToolProperty().addListener(this::onToolChanged); - ViewContextMenuBuilder contextMenuBuilder = new ViewContextMenuBuilder(viewModel.getActionManager(), viewModel); + ViewContextMenuBuilder contextMenuBuilder = + new ViewContextMenuBuilder(viewModel.getActionManager(), viewModel, this); this.contextMenu = contextMenuBuilder.build(); currentViewPane.setOnContextMenuRequested(event -> { changeContextMenu(contextMenuBuilder.getContextMenu()); @@ -236,7 +236,7 @@ private void initializeViewElements() { } private void addElement(PositionableViewModelElement element) { - PositionableViewModelElementVisitor visitor = new ViewElementCreatorVisitor(viewFactory); + PositionableViewModelElementVisitor visitor = new ViewElementCreatorVisitor(viewFactory); ViewElement viewElement = (ViewElement) element.accept(visitor); // Add view element to current view elements diff --git a/src/main/java/org/gecko/view/views/ViewElementPane.java b/src/main/java/org/gecko/view/views/ViewElementPane.java index 00e92a48..ca2dbd7a 100644 --- a/src/main/java/org/gecko/view/views/ViewElementPane.java +++ b/src/main/java/org/gecko/view/views/ViewElementPane.java @@ -185,7 +185,7 @@ private Point2D localViewPortPosition() { return new Point2D(x, y); } - private Point2D screenCenterWorldCoords() { + public Point2D screenCenterWorldCoords() { //Can't use screenToLocal because we don't want the pane.localToScreen() offset Point2D screenCenter = new Point2D(pane.getViewportBounds().getWidth() / 2, pane.getViewportBounds().getHeight() / 2); diff --git a/src/main/java/org/gecko/view/views/ViewFactory.java b/src/main/java/org/gecko/view/views/ViewFactory.java index 78c169c8..d549cb36 100644 --- a/src/main/java/org/gecko/view/views/ViewFactory.java +++ b/src/main/java/org/gecko/view/views/ViewFactory.java @@ -52,7 +52,7 @@ public ViewElement createViewElementFrom(StateViewModel stateViewModel) { StateViewElement newStateViewElement = new StateViewElement(stateViewModel); ViewContextMenuBuilder contextMenuBuilder = - new StateViewElementContextMenuBuilder(actionManager, stateViewModel); + new StateViewElementContextMenuBuilder(actionManager, stateViewModel, geckoView); setContextMenu(newStateViewElement, contextMenuBuilder); return new SelectableViewElementDecorator(newStateViewElement); } @@ -61,7 +61,7 @@ public ViewElement createViewElementFrom(RegionViewModel regionViewModel) { RegionViewElement newRegionViewElement = new RegionViewElement(regionViewModel); ViewContextMenuBuilder contextMenuBuilder = - new RegionViewElementContextMenuBuilder(actionManager, regionViewModel); + new RegionViewElementContextMenuBuilder(actionManager, regionViewModel, geckoView); setContextMenu(newRegionViewElement, contextMenuBuilder); return new BlockElementScalerViewElementDecorator(new SelectableViewElementDecorator(newRegionViewElement)); } @@ -70,7 +70,7 @@ public ViewElement createViewElementFrom(PortViewModel portViewModel) { VariableBlockViewElement newVariableBlockViewElement = new VariableBlockViewElement(portViewModel); ViewContextMenuBuilder contextMenuBuilder = - new VariableBlockViewElementContextMenuBuilder(actionManager, portViewModel); + new VariableBlockViewElementContextMenuBuilder(actionManager, portViewModel, geckoView); setContextMenu(newVariableBlockViewElement, contextMenuBuilder); return new SelectableViewElementDecorator(newVariableBlockViewElement); @@ -79,7 +79,8 @@ public ViewElement createViewElementFrom(PortViewModel portViewModel) { public ViewElement createViewElementFrom(EdgeViewModel edgeViewModel) { EdgeViewElement newEdgeViewElement = new EdgeViewElement(edgeViewModel); - ViewContextMenuBuilder contextMenuBuilder = new EdgeViewElementContextMenuBuilder(actionManager, edgeViewModel); + ViewContextMenuBuilder contextMenuBuilder = + new EdgeViewElementContextMenuBuilder(actionManager, edgeViewModel, geckoView); setContextMenu(newEdgeViewElement, contextMenuBuilder); return new ConnectionElementScalerViewElementDecorator(newEdgeViewElement); @@ -90,7 +91,7 @@ public ViewElement createViewElementFrom(SystemConnectionViewModel systemConn new SystemConnectionViewElement(systemConnectionViewModel); ViewContextMenuBuilder contextMenuBuilder = - new SystemConnectionViewElementContextMenuBuilder(actionManager, systemConnectionViewModel); + new SystemConnectionViewElementContextMenuBuilder(actionManager, systemConnectionViewModel, geckoView); setContextMenu(newSystemConnectionViewElement, contextMenuBuilder); return new ConnectionElementScalerViewElementDecorator(newSystemConnectionViewElement); @@ -100,7 +101,7 @@ public ViewElement createViewElementFrom(SystemViewModel systemViewModel) { SystemViewElement newSystemViewElement = new SystemViewElement(systemViewModel); ViewContextMenuBuilder contextMenuBuilder = - new SystemViewElementContextMenuBuilder(actionManager, systemViewModel); + new SystemViewElementContextMenuBuilder(actionManager, systemViewModel, geckoView); setContextMenu(newSystemViewElement, contextMenuBuilder); return new SelectableViewElementDecorator(newSystemViewElement); diff --git a/src/test/java/org/gecko/model/ElementTest.java b/src/test/java/org/gecko/model/ElementTest.java index 13d142a8..a87f596f 100644 --- a/src/test/java/org/gecko/model/ElementTest.java +++ b/src/test/java/org/gecko/model/ElementTest.java @@ -6,6 +6,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import org.gecko.exceptions.MissingViewModelElementException; import org.gecko.exceptions.ModelException; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -16,8 +17,16 @@ public class ElementTest { @BeforeAll static void setUp() { assertThrows(ModelException.class, () -> element = new Element(-1) { + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + + } }); assertDoesNotThrow(() -> element = new Element(0) { + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + + } }); } @@ -32,8 +41,16 @@ void testEquals() { final Element[] other = new Element[4]; assertDoesNotThrow(() -> other[0] = new Element(1) { + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + + } }); assertDoesNotThrow(() -> other[1] = new Element(0) { + @Override + public void accept(ElementVisitor visitor) throws ModelException, MissingViewModelElementException { + + } }); assertDoesNotThrow(() -> other[2] = new State(2, "state"));