[fixes #358] Implement multi-selection for component tree - Move up, down, Delete, Cut, Copy, Paste
This commit is contained in:
parent
4ba92905b4
commit
aacc101f3a
@ -181,7 +181,7 @@ public class BasicFrame extends JFrame {
|
|||||||
|
|
||||||
// Create the component tree selection model that will be used
|
// Create the component tree selection model that will be used
|
||||||
componentSelectionModel = new DefaultTreeSelectionModel();
|
componentSelectionModel = new DefaultTreeSelectionModel();
|
||||||
componentSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
|
componentSelectionModel.setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
|
||||||
|
|
||||||
// Obtain the simulation selection model that will be used
|
// Obtain the simulation selection model that will be used
|
||||||
simulationPanel = new SimulationPanel(document);
|
simulationPanel = new SimulationPanel(document);
|
||||||
|
@ -79,10 +79,21 @@ public class DocumentSelectionModel {
|
|||||||
* @return the currently selected rocket component, or <code>null</code>.
|
* @return the currently selected rocket component, or <code>null</code>.
|
||||||
*/
|
*/
|
||||||
public RocketComponent getSelectedComponent() {
|
public RocketComponent getSelectedComponent() {
|
||||||
if (componentSelection == null || componentSelection.size() == 0) return null;
|
if (componentSelection.size() == 0) return null;
|
||||||
return componentSelection.get(0);
|
return componentSelection.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the currently selected rocket components. Returns <code>null</code>
|
||||||
|
* if no rocket component is selected.
|
||||||
|
*
|
||||||
|
* @return the currently selected rocket components, or <code>null</code>.
|
||||||
|
*/
|
||||||
|
public List<RocketComponent> getSelectedComponents() {
|
||||||
|
if (componentSelection.size() == 0) return null;
|
||||||
|
return componentSelection;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSelectedComponent(RocketComponent component) {
|
public void setSelectedComponent(RocketComponent component) {
|
||||||
componentSelection.clear();
|
componentSelection.clear();
|
||||||
componentSelection.add(component);
|
componentSelection.add(component);
|
||||||
@ -142,7 +153,7 @@ public class DocumentSelectionModel {
|
|||||||
|
|
||||||
|
|
||||||
public void clearComponentSelection() {
|
public void clearComponentSelection() {
|
||||||
if (componentSelection == null || componentSelection.size() == 0)
|
if (componentSelection.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
componentSelection.clear();
|
componentSelection.clear();
|
||||||
@ -177,15 +188,17 @@ public class DocumentSelectionModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void valueChanged(TreeSelectionEvent e) {
|
public void valueChanged(TreeSelectionEvent e) {
|
||||||
TreePath path = componentTreeSelectionModel.getSelectionPath();
|
TreePath[] paths = componentTreeSelectionModel.getSelectionPaths();
|
||||||
if (path == null) {
|
if (paths == null || paths.length == 0) {
|
||||||
componentSelection.clear();
|
componentSelection.clear();
|
||||||
fireDocumentSelection(DocumentSelectionListener.COMPONENT_SELECTION_CHANGE);
|
fireDocumentSelection(DocumentSelectionListener.COMPONENT_SELECTION_CHANGE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentSelection.clear();
|
componentSelection.clear();
|
||||||
componentSelection.add((RocketComponent)path.getLastPathComponent());
|
for (TreePath path : paths) {
|
||||||
|
componentSelection.add((RocketComponent)path.getLastPathComponent());
|
||||||
|
}
|
||||||
|
|
||||||
clearSimulationSelection();
|
clearSimulationSelection();
|
||||||
fireDocumentSelection(DocumentSelectionListener.COMPONENT_SELECTION_CHANGE);
|
fireDocumentSelection(DocumentSelectionListener.COMPONENT_SELECTION_CHANGE);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package net.sf.openrocket.gui.main;
|
package net.sf.openrocket.gui.main;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import net.sf.openrocket.document.Simulation;
|
import net.sf.openrocket.document.Simulation;
|
||||||
@ -8,10 +10,10 @@ import net.sf.openrocket.rocketcomponent.RocketComponent;
|
|||||||
|
|
||||||
public final class OpenRocketClipboard {
|
public final class OpenRocketClipboard {
|
||||||
|
|
||||||
private static RocketComponent clipboardComponent = null;
|
private static final List<RocketComponent> clipboardComponents = new LinkedList<>();
|
||||||
private static Simulation[] clipboardSimulations = null;
|
private static Simulation[] clipboardSimulations = null;
|
||||||
|
|
||||||
private static List<ClipboardListener> listeners = new ArrayList<ClipboardListener>();
|
private static final List<ClipboardListener> listeners = new ArrayList<ClipboardListener>();
|
||||||
|
|
||||||
private OpenRocketClipboard() {
|
private OpenRocketClipboard() {
|
||||||
// Disallow instantiation
|
// Disallow instantiation
|
||||||
@ -26,17 +28,92 @@ public final class OpenRocketClipboard {
|
|||||||
* @return the rocket component contained in the clipboard, or <code>null</code>
|
* @return the rocket component contained in the clipboard, or <code>null</code>
|
||||||
* if the clipboard does not currently contain a rocket component.
|
* if the clipboard does not currently contain a rocket component.
|
||||||
*/
|
*/
|
||||||
public static RocketComponent getClipboardComponent() {
|
public static List<RocketComponent> getClipboardComponents() {
|
||||||
return clipboardComponent;
|
return clipboardComponents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void setClipboard(RocketComponent component) {
|
public static void setClipboard(List<RocketComponent> components) {
|
||||||
clipboardComponent = component;
|
clipboardComponents.clear();
|
||||||
|
components = RocketActions.copyComponentsMaintainParent(components);
|
||||||
|
if (components != null) {
|
||||||
|
clipboardComponents.addAll(components);
|
||||||
|
}
|
||||||
|
filterClipboardComponents(new LinkedList<>(clipboardComponents));
|
||||||
clipboardSimulations = null;
|
clipboardSimulations = null;
|
||||||
fireClipboardChanged();
|
fireClipboardChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filters out children from clipboardComponents and the children's list of the components, based on the selection
|
||||||
|
* (see {@link #filterClipboardComponent} to see how the actual filtering is done.
|
||||||
|
* @param components components to be filtered
|
||||||
|
*/
|
||||||
|
private static void filterClipboardComponents(List<RocketComponent> components) {
|
||||||
|
if (components == null || components.size() == 0) return;
|
||||||
|
List<RocketComponent> checkedComponents = new ArrayList<>();
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
// Make sure a parent component is processed before the child components
|
||||||
|
RocketComponent temp = component;
|
||||||
|
while (temp.getParent() != null && clipboardComponents.contains(temp.getParent())
|
||||||
|
&& !checkedComponents.contains(temp.getParent())) {
|
||||||
|
filterClipboardComponent(temp.getParent());
|
||||||
|
checkedComponents.add(temp.getParent());
|
||||||
|
temp = temp.getParent();
|
||||||
|
}
|
||||||
|
filterClipboardComponent(component);
|
||||||
|
checkedComponents.add(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iteratively (over all children of component and their children), filter clipboard components according to
|
||||||
|
* the following rules:
|
||||||
|
* - If all children of component are selected, then remove those children from clipboardComponents, but keep them
|
||||||
|
* as children of component
|
||||||
|
* - If one of the children of component is selected, but some are not, then remove the unselected children as child
|
||||||
|
* from component, but keep the selected children as children of component + remove the selected children from
|
||||||
|
* clipboardComponents
|
||||||
|
* - If no children are selected, then no children are removed from component + no children are removed from
|
||||||
|
* clipboardComponents
|
||||||
|
* @param component component to filter
|
||||||
|
*/
|
||||||
|
private static void filterClipboardComponent(RocketComponent component) {
|
||||||
|
if (component == null) return;
|
||||||
|
|
||||||
|
boolean allChildrenSelected = clipboardComponents.containsAll(component.getChildren());
|
||||||
|
boolean someChildrenSelected = false;
|
||||||
|
for (RocketComponent child : component.getChildren()) {
|
||||||
|
if (clipboardComponents.contains(child)) {
|
||||||
|
someChildrenSelected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allChildrenSelected) {
|
||||||
|
for (RocketComponent child : component.getChildren()) {
|
||||||
|
clipboardComponents.remove(child);
|
||||||
|
filterClipboardComponents(child.getChildren());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (someChildrenSelected) {
|
||||||
|
for (RocketComponent child : component.getChildren()) {
|
||||||
|
if (!clipboardComponents.contains(child)) {
|
||||||
|
component.removeChild(child);
|
||||||
|
} else {
|
||||||
|
clipboardComponents.remove(child);
|
||||||
|
filterClipboardComponents(child.getChildren());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (RocketComponent child : component.getChildren()) {
|
||||||
|
filterClipboardComponents(child.getChildren());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Simulation[] getClipboardSimulations() {
|
public static Simulation[] getClipboardSimulations() {
|
||||||
if (clipboardSimulations == null || clipboardSimulations.length == 0)
|
if (clipboardSimulations == null || clipboardSimulations.length == 0)
|
||||||
@ -46,7 +123,7 @@ public final class OpenRocketClipboard {
|
|||||||
|
|
||||||
public static void setClipboard(Simulation[] simulations) {
|
public static void setClipboard(Simulation[] simulations) {
|
||||||
clipboardSimulations = simulations.clone();
|
clipboardSimulations = simulations.clone();
|
||||||
clipboardComponent = null;
|
clipboardComponents.clear();
|
||||||
fireClipboardChanged();
|
fireClipboardChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ import java.awt.Toolkit;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
@ -181,6 +184,14 @@ public class RocketActions {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isDeletable(List<RocketComponent> components) {
|
||||||
|
if (components == null || components.size() == 0) return false;
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
if (!isDeletable(component)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void delete(RocketComponent c) {
|
private void delete(RocketComponent c) {
|
||||||
if (!isDeletable(c)) {
|
if (!isDeletable(c)) {
|
||||||
throw new IllegalArgumentException("Report bug! Component " + c +
|
throw new IllegalArgumentException("Report bug! Component " + c +
|
||||||
@ -191,6 +202,16 @@ public class RocketActions {
|
|||||||
parent.removeChild(c);
|
parent.removeChild(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void delete(List<RocketComponent> components) {
|
||||||
|
if (!isDeletable(components)) {
|
||||||
|
throw new IllegalArgumentException("Report bug! Components not deletable.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
delete(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isCopyable(RocketComponent c) {
|
private boolean isCopyable(RocketComponent c) {
|
||||||
if (c==null)
|
if (c==null)
|
||||||
return false;
|
return false;
|
||||||
@ -199,6 +220,119 @@ public class RocketActions {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCopyable(List<RocketComponent> components) {
|
||||||
|
if (components == null || components.size() == 0) return false;
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
if (!isCopyable(component)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies components, but with maintaining parent-relations with the newly copied components
|
||||||
|
* @param components components to copy
|
||||||
|
* @return copied components
|
||||||
|
*/
|
||||||
|
public static List<RocketComponent> copyComponentsMaintainParent(List<RocketComponent> components) {
|
||||||
|
if (components == null) return null;
|
||||||
|
List<RocketComponent> result = new LinkedList<>();
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
result.add(component.copy());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < components.size(); i++) {
|
||||||
|
if (components.contains(components.get(i).getParent())) {
|
||||||
|
RocketComponent oldChild = components.get(i);
|
||||||
|
RocketComponent oldParent = oldChild.getParent();
|
||||||
|
|
||||||
|
int index = components.indexOf(oldParent);
|
||||||
|
int childPos = oldParent.getChildPosition(oldChild);
|
||||||
|
|
||||||
|
RocketComponent newChild = result.get(i);
|
||||||
|
RocketComponent newParent = result.get(index);
|
||||||
|
|
||||||
|
// Add the newly copied child to the parent
|
||||||
|
newParent.addChild(newChild, childPos);
|
||||||
|
|
||||||
|
// Remove the old child from the parent
|
||||||
|
newParent.removeChild(oldChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iteratively checks whether the list of components contains the parent or super-parent (parent of parent of parent of...)
|
||||||
|
* of component.
|
||||||
|
* @param components list of components that may contain the parent
|
||||||
|
* @param component component to check the parent for
|
||||||
|
* @return true if the list contains the parent, false if not
|
||||||
|
*/
|
||||||
|
public static boolean listContainsParent(List<RocketComponent> components, RocketComponent component) {
|
||||||
|
RocketComponent c = component;
|
||||||
|
while (c.getParent() != null) {
|
||||||
|
if (components.contains(c.getParent())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
c = c.getParent();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the children of a parent are not selected, add them to the selection. Do this recursively for the children
|
||||||
|
* of the children as well.
|
||||||
|
* @param selections list of currently selected components
|
||||||
|
* @param parent parent component to parse the children of
|
||||||
|
*/
|
||||||
|
private void selectAllUnselectedInParent(List<RocketComponent> selections, RocketComponent parent) {
|
||||||
|
if (parent.getChildCount() == 0) return;
|
||||||
|
|
||||||
|
boolean noChildrenSelected = true;
|
||||||
|
for (RocketComponent child : parent.getChildren()) {
|
||||||
|
if (selections.contains(child)) {
|
||||||
|
noChildrenSelected = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add children to selection if none of them were selected
|
||||||
|
if (noChildrenSelected) {
|
||||||
|
selections.addAll(parent.getChildren());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively select all unselected children. Unselected children will not undergo this recursive updating.
|
||||||
|
for (RocketComponent child : parent.getChildren()) {
|
||||||
|
if (!noChildrenSelected && selections.contains(child)) {
|
||||||
|
selectAllUnselectedInParent(selections, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method fills in some selections in selections. If there is a parent where none of its children are selected,
|
||||||
|
* add all the children to the selection. If there is a component whose parent is not selected, but it does have
|
||||||
|
* a super-parent in the selection, add the parent to the selection.
|
||||||
|
* @param selections component selections to be filled up
|
||||||
|
*/
|
||||||
|
private void fillInMissingSelections(List<RocketComponent> selections) {
|
||||||
|
List<RocketComponent> initSelections = new LinkedList<>(selections);
|
||||||
|
for (RocketComponent component : initSelections) {
|
||||||
|
selectAllUnselectedInParent(selections, component);
|
||||||
|
|
||||||
|
// If there is a component in the selection, but its parent (or the parent of the parent) is still
|
||||||
|
// not selected, add it to the selection
|
||||||
|
RocketComponent temp = component;
|
||||||
|
if (listContainsParent(selections, temp) && !selections.contains(temp.getParent())) {
|
||||||
|
while (!selections.contains(temp.getParent())) {
|
||||||
|
selections.add(temp.getParent());
|
||||||
|
temp = temp.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean isSimulationSelected() {
|
private boolean isSimulationSelected() {
|
||||||
Simulation[] selection = selectionModel.getSelectedSimulations();
|
Simulation[] selection = selectionModel.getSelectedSimulations();
|
||||||
@ -270,6 +404,24 @@ public class RocketActions {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the component and position to which the current clipboard
|
||||||
|
* should be pasted. Returns null if the clipboard is empty or if the
|
||||||
|
* clipboard cannot be pasted to the current selection.
|
||||||
|
*
|
||||||
|
* @param clipboard the component on the clipboard.
|
||||||
|
* @return a Pair with both components defined, or null.
|
||||||
|
*/
|
||||||
|
private List<Pair<RocketComponent, Integer>> getPastePositions(List<RocketComponent> clipboard) {
|
||||||
|
List<Pair<RocketComponent, Integer>> result = new LinkedList<>();
|
||||||
|
for (RocketComponent component : clipboard) {
|
||||||
|
Pair<RocketComponent, Integer> position = getPastePosition(component);
|
||||||
|
if (position != null) {
|
||||||
|
result.add(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -306,15 +458,30 @@ public class RocketActions {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
RocketComponent c = selectionModel.getSelectedComponent();
|
List<RocketComponent> components = new ArrayList<>(selectionModel.getSelectedComponents());
|
||||||
|
if (components.size() == 0) return;
|
||||||
|
components.sort(Comparator.comparing(c -> c.getParent().getChildPosition(c)));
|
||||||
|
|
||||||
if (isDeletable(c)) {
|
if (components.size() == 1) {
|
||||||
|
document.addUndoPosition("Delete " + components.get(0).getComponentName());
|
||||||
|
} else {
|
||||||
|
document.addUndoPosition("Delete components");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
deleteComponent(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteComponent(RocketComponent component) {
|
||||||
|
if (isDeletable(component)) {
|
||||||
ComponentConfigDialog.hideDialog();
|
ComponentConfigDialog.hideDialog();
|
||||||
|
|
||||||
c.getRocket().removeComponentChangeListener(ComponentConfigDialog.getDialog());
|
try {
|
||||||
|
component.getRocket().removeComponentChangeListener(ComponentConfigDialog.getDialog());
|
||||||
|
|
||||||
document.addUndoPosition("Delete " + c.getComponentName());
|
delete(component);
|
||||||
delete(c);
|
} catch (IllegalStateException ignored) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,15 +586,28 @@ public class RocketActions {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
RocketComponent c = selectionModel.getSelectedComponent();
|
List<RocketComponent> components = selectionModel.getSelectedComponents();
|
||||||
|
if (components != null) {
|
||||||
|
components = new ArrayList<>(components);
|
||||||
|
fillInMissingSelections(components);
|
||||||
|
|
||||||
|
components.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0));
|
||||||
|
}
|
||||||
Simulation[] sims = selectionModel.getSelectedSimulations();
|
Simulation[] sims = selectionModel.getSelectedSimulations();
|
||||||
|
|
||||||
if (isDeletable(c) && isCopyable(c)) {
|
if (isDeletable(components) && isCopyable(components)) {
|
||||||
ComponentConfigDialog.hideDialog();
|
ComponentConfigDialog.hideDialog();
|
||||||
|
|
||||||
document.addUndoPosition("Cut " + c.getComponentName());
|
if (components.size() == 1) {
|
||||||
OpenRocketClipboard.setClipboard(c.copy());
|
document.addUndoPosition("Cut " + components.get(0).getComponentName());
|
||||||
delete(c);
|
} else {
|
||||||
|
document.addUndoPosition("Cut components");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<RocketComponent> copiedComponents = new LinkedList<>(copyComponentsMaintainParent(components));
|
||||||
|
|
||||||
|
OpenRocketClipboard.setClipboard(copiedComponents);
|
||||||
|
delete(components);
|
||||||
parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
|
parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
|
||||||
} else if (isSimulationSelected()) {
|
} else if (isSimulationSelected()) {
|
||||||
|
|
||||||
@ -446,8 +626,8 @@ public class RocketActions {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clipboardChanged() {
|
public void clipboardChanged() {
|
||||||
RocketComponent c = selectionModel.getSelectedComponent();
|
List<RocketComponent> components = selectionModel.getSelectedComponents();
|
||||||
this.setEnabled((isDeletable(c) && isCopyable(c)) || isSimulationSelected());
|
this.setEnabled((isDeletable(components) && isCopyable(components)) || isSimulationSelected());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,13 +652,21 @@ public class RocketActions {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
RocketComponent c = selectionModel.getSelectedComponent();
|
List<RocketComponent> components = selectionModel.getSelectedComponents();
|
||||||
|
if (components != null) {
|
||||||
|
components = new ArrayList<>(components);
|
||||||
|
fillInMissingSelections(components);
|
||||||
|
|
||||||
|
components.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0));
|
||||||
|
}
|
||||||
Simulation[] sims = selectionModel.getSelectedSimulations();
|
Simulation[] sims = selectionModel.getSelectedSimulations();
|
||||||
|
|
||||||
if (isCopyable(c)) {
|
if (isCopyable(components)) {
|
||||||
OpenRocketClipboard.setClipboard(c.copy());
|
List<RocketComponent> copiedComponents = new LinkedList<>(copyComponentsMaintainParent(components));
|
||||||
|
|
||||||
|
OpenRocketClipboard.setClipboard(copiedComponents);
|
||||||
parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
|
parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
|
||||||
} else if (sims.length >= 0) {
|
} else if (sims.length > 0) {
|
||||||
|
|
||||||
Simulation[] simsCopy = new Simulation[sims.length];
|
Simulation[] simsCopy = new Simulation[sims.length];
|
||||||
for (int i=0; i < sims.length; i++) {
|
for (int i=0; i < sims.length; i++) {
|
||||||
@ -520,17 +708,33 @@ public class RocketActions {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
RocketComponent clipboard = OpenRocketClipboard.getClipboardComponent();
|
List<RocketComponent> components = new LinkedList<>(OpenRocketClipboard.getClipboardComponents());
|
||||||
Simulation[] sims = OpenRocketClipboard.getClipboardSimulations();
|
Simulation[] sims = OpenRocketClipboard.getClipboardSimulations();
|
||||||
|
|
||||||
Pair<RocketComponent, Integer> position = getPastePosition(clipboard);
|
if (components.size() > 0) {
|
||||||
if (position != null) {
|
|
||||||
ComponentConfigDialog.hideDialog();
|
ComponentConfigDialog.hideDialog();
|
||||||
|
|
||||||
RocketComponent pasted = clipboard.copy();
|
List<RocketComponent> pasted = new LinkedList<>();
|
||||||
document.addUndoPosition("Paste " + pasted.getComponentName());
|
for (RocketComponent component : components) {
|
||||||
position.getU().addChild(pasted, position.getV());
|
pasted.add(component.copy());
|
||||||
selectionModel.setSelectedComponent(pasted);
|
}
|
||||||
|
|
||||||
|
List<Pair<RocketComponent, Integer>> positions = new LinkedList<>();
|
||||||
|
for (RocketComponent component : pasted) {
|
||||||
|
positions.add(getPastePosition(component));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pasted.size() == 1) {
|
||||||
|
document.addUndoPosition("Paste " + pasted.get(0).getComponentName());
|
||||||
|
} else {
|
||||||
|
document.addUndoPosition("Paste components");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < pasted.size(); i++) {
|
||||||
|
positions.get(i).getU().addChild(pasted.get(i), positions.get(i).getV());
|
||||||
|
}
|
||||||
|
|
||||||
|
selectionModel.setSelectedComponents(pasted);
|
||||||
|
|
||||||
parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
|
parentFrame.selectTab(BasicFrame.COMPONENT_TAB);
|
||||||
|
|
||||||
@ -556,7 +760,7 @@ public class RocketActions {
|
|||||||
@Override
|
@Override
|
||||||
public void clipboardChanged() {
|
public void clipboardChanged() {
|
||||||
this.setEnabled(
|
this.setEnabled(
|
||||||
(getPastePosition(OpenRocketClipboard.getClipboardComponent()) != null) ||
|
(getPastePositions(OpenRocketClipboard.getClipboardComponents()).size() > 0) ||
|
||||||
(OpenRocketClipboard.getClipboardSimulations() != null));
|
(OpenRocketClipboard.getClipboardSimulations() != null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -656,22 +860,40 @@ public class RocketActions {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
RocketComponent selected = selectionModel.getSelectedComponent();
|
List<RocketComponent> components = selectionModel.getSelectedComponents();
|
||||||
if (!canMove(selected))
|
if (components == null || components.size() == 0) return;
|
||||||
|
components = new ArrayList<>(components);
|
||||||
|
components.sort(Comparator.comparing(c -> c.getParent() != null ? c.getParent().getChildPosition(c) : 0));
|
||||||
|
|
||||||
|
if (components.size() == 1) {
|
||||||
|
document.addUndoPosition("Move " + components.get(0).getComponentName());
|
||||||
|
} else {
|
||||||
|
document.addUndoPosition("Move components");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
// Only move top components, don't move its children
|
||||||
|
if (!listContainsParent(components, component)) {
|
||||||
|
moveUp(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rocket.fireComponentChangeEvent(ComponentChangeEvent.TREE_CHANGE);
|
||||||
|
selectionModel.setSelectedComponents(components);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveUp(RocketComponent component) {
|
||||||
|
if (!canMove(component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ComponentConfigDialog.hideDialog();
|
ComponentConfigDialog.hideDialog();
|
||||||
|
|
||||||
RocketComponent parent = selected.getParent();
|
RocketComponent parent = component.getParent();
|
||||||
document.addUndoPosition("Move "+selected.getComponentName());
|
parent.moveChild(component, parent.getChildPosition(component) - 1);
|
||||||
parent.moveChild(selected, parent.getChildPosition(selected)-1);
|
|
||||||
rocket.fireComponentChangeEvent( ComponentChangeEvent.TREE_CHANGE );
|
|
||||||
selectionModel.setSelectedComponent(selected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clipboardChanged() {
|
public void clipboardChanged() {
|
||||||
this.setEnabled(canMove(selectionModel.getSelectedComponent()));
|
this.setEnabled(canMove(selectionModel.getSelectedComponents()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canMove(RocketComponent c) {
|
private boolean canMove(RocketComponent c) {
|
||||||
@ -682,6 +904,17 @@ public class RocketActions {
|
|||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canMove(List<RocketComponent> components) {
|
||||||
|
if (components == null || components.size() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
if (!listContainsParent(components, component) && !canMove(component))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -702,22 +935,40 @@ public class RocketActions {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
RocketComponent selected = selectionModel.getSelectedComponent();
|
List<RocketComponent> components = selectionModel.getSelectedComponents();
|
||||||
if (!canMove(selected))
|
if (components == null || components.size() == 0) return;
|
||||||
|
components = new ArrayList<>(components);
|
||||||
|
components.sort(Comparator.comparing(c -> c.getParent() != null ? -c.getParent().getChildPosition(c) : 0));
|
||||||
|
|
||||||
|
if (components.size() == 1) {
|
||||||
|
document.addUndoPosition("Move " + components.get(0).getComponentName());
|
||||||
|
} else {
|
||||||
|
document.addUndoPosition("Move components");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
// Only move top components, don't move its children
|
||||||
|
if (!listContainsParent(components, component)) {
|
||||||
|
moveDown(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rocket.fireComponentChangeEvent(ComponentChangeEvent.TREE_CHANGE);
|
||||||
|
selectionModel.setSelectedComponents(components);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void moveDown(RocketComponent component) {
|
||||||
|
if (!canMove(component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ComponentConfigDialog.hideDialog();
|
ComponentConfigDialog.hideDialog();
|
||||||
|
|
||||||
RocketComponent parent = selected.getParent();
|
RocketComponent parent = component.getParent();
|
||||||
document.addUndoPosition("Move "+selected.getComponentName());
|
parent.moveChild(component, parent.getChildPosition(component) + 1);
|
||||||
parent.moveChild(selected, parent.getChildPosition(selected)+1);
|
|
||||||
rocket.fireComponentChangeEvent( ComponentChangeEvent.TREE_CHANGE );
|
|
||||||
selectionModel.setSelectedComponent(selected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clipboardChanged() {
|
public void clipboardChanged() {
|
||||||
this.setEnabled(canMove(selectionModel.getSelectedComponent()));
|
this.setEnabled(canMove(selectionModel.getSelectedComponents()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canMove(RocketComponent c) {
|
private boolean canMove(RocketComponent c) {
|
||||||
@ -728,6 +979,17 @@ public class RocketActions {
|
|||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean canMove(List<RocketComponent> components) {
|
||||||
|
if (components == null || components.size() == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (RocketComponent component : components) {
|
||||||
|
if (!listContainsParent(components, component) && !canMove(component))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user