diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index af2e046c9..8697363ed 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -1640,6 +1640,24 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab 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 components, RocketComponent component) { + RocketComponent c = component; + while (c.getParent() != null) { + if (components.contains(c.getParent())) { + return true; + } + c = c.getParent(); + } + return false; + } /** * Get the root component of the component tree. diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java index ca3e49a5e..e966061ec 100644 --- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java +++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java @@ -278,24 +278,6 @@ public class RocketActions { 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 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. @@ -340,7 +322,7 @@ public class RocketActions { // 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())) { + if (RocketComponent.listContainsParent(selections, temp) && !selections.contains(temp.getParent())) { while (!selections.contains(temp.getParent())) { selections.add(temp.getParent()); temp = temp.getParent(); @@ -1038,7 +1020,7 @@ public class RocketActions { for (RocketComponent component : components) { // Only move top components, don't move its children - if (!listContainsParent(components, component)) { + if (!RocketComponent.listContainsParent(components, component)) { moveUp(component); } } @@ -1075,7 +1057,7 @@ public class RocketActions { return false; for (RocketComponent component : components) { - if (!listContainsParent(components, component) && !canMove(component)) + if (!RocketComponent.listContainsParent(components, component) && !canMove(component)) return false; } return true; @@ -1113,7 +1095,7 @@ public class RocketActions { for (RocketComponent component : components) { // Only move top components, don't move its children - if (!listContainsParent(components, component)) { + if (!RocketComponent.listContainsParent(components, component)) { moveDown(component); } } @@ -1150,7 +1132,7 @@ public class RocketActions { return false; for (RocketComponent component : components) { - if (!listContainsParent(components, component) && !canMove(component)) + if (!RocketComponent.listContainsParent(components, component) && !canMove(component)) return false; } return true; diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java index 8c66f25c7..75f369d26 100644 --- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java +++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeRenderer.java @@ -2,12 +2,14 @@ package net.sf.openrocket.gui.main.componenttree; import java.awt.Component; import java.awt.FlowLayout; +import java.util.List; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTree; import javax.swing.UIManager; import javax.swing.tree.DefaultTreeCellRenderer; +import javax.swing.tree.TreePath; import net.sf.openrocket.gui.main.ComponentIcons; import net.sf.openrocket.gui.util.Icons; @@ -17,6 +19,7 @@ import net.sf.openrocket.rocketcomponent.MassComponent.MassComponentType; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; +import net.sf.openrocket.util.ArrayList; import net.sf.openrocket.util.TextUtil; @SuppressWarnings("serial") @@ -31,11 +34,15 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer { Component comp = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus1); + if (tree == null) return comp; + TreePath[] paths = tree.getSelectionPaths(); + List components = null; + if (paths != null && paths.length > 0) { + components = new ArrayList<>(ComponentTreeModel.componentsFromPaths(paths)); + } // Set icon - RocketComponent c = (RocketComponent) value; - if (c.getClass().isAssignableFrom(MassComponent.class)) { MassComponentType t = ((MassComponent) c).getMassComponentType(); setIcon(ComponentIcons.getSmallMassTypeIcon(t)); @@ -54,17 +61,26 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer { if (c.isCGOverridden()) { p.add(new JLabel(Icons.CG_OVERRIDE)); } - p.setToolTipText(getToolTip(c)); + + if (components != null && components.size() > 1 && components.contains(c)) { + p.setToolTipText(getToolTipMultipleComponents(components)); + } else { + p.setToolTipText(getToolTipSingleComponent(c)); + } + comp = p; } - // Set tooltip - this.setToolTipText(getToolTip(c)); + if (components != null && components.size() > 1 && components.contains(c)) { + this.setToolTipText(getToolTipMultipleComponents(components)); + } else { + this.setToolTipText(getToolTipSingleComponent(c)); + } return comp; } - private static String getToolTip(RocketComponent c) { + private static String getToolTipSingleComponent(RocketComponent c) { StringBuilder sb = new StringBuilder(); sb.append(""); @@ -104,4 +120,55 @@ public class ComponentTreeRenderer extends DefaultTreeCellRenderer { return sb.toString(); } + private static String getToolTipMultipleComponents(List components) { + if (components == null || components.size() == 0) { + return null; + } + + StringBuilder sb = new StringBuilder(); + sb.append(""); + + sb.append("Components"); + double totalMass = 0; + double totalSectionMass = 0; + boolean containsSectionMass = false; + for (RocketComponent c : components) { + if (c.isMassive() || c.isMassOverridden()) { + totalMass += c.getMass(); + // Don't add this component's mass to the section mass if its parent is in the list, otherwise you add up duplicate mass + if (!RocketComponent.listContainsParent(components, c)) { + if (c.getChildCount() > 0 && c.getSectionMass() > 0) { + totalSectionMass += c.getSectionMass(); + containsSectionMass = true; + } else { + totalSectionMass += c.getMass(); + } + } + } else if ((c.getChildCount() > 0) && (c.getSectionMass() > 0)) { + totalMass = c.getSectionMass(); + containsSectionMass = false; + break; + } + } + + sb.append(" (").append(UnitGroup.UNITS_MASS.toStringUnit(totalMass)); + if (containsSectionMass) { + sb.append(" of ").append(UnitGroup.UNITS_MASS.toStringUnit(totalSectionMass)).append(" total)"); + } else { + sb.append(")"); + } + + // Set the component names as description + sb.append("
"); + for (int i = 0; i < components.size(); i++) { + if (i < components.size() - 1) { + sb.append(components.get(i).getName()).append(", "); + } else { + sb.append(components.get(i).getName()); + } + } + + return sb.toString(); + } + } diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeTransferHandler.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeTransferHandler.java index 6750b98b2..54e68a6af 100644 --- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeTransferHandler.java +++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTreeTransferHandler.java @@ -16,7 +16,6 @@ import javax.swing.TransferHandler; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; -import net.sf.openrocket.gui.main.RocketActions; import net.sf.openrocket.util.ArrayList; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -73,7 +72,7 @@ public class ComponentTreeTransferHandler extends TransferHandler { // When the parent of a child is in the selection, don't include the child in components for (RocketComponent component : new ArrayList<>(components)) { - if (RocketActions.listContainsParent(components, component)) { + if (RocketComponent.listContainsParent(components, component)) { components.remove(component); } }