diff --git a/core/src/net/sf/openrocket/simulation/FlightDataType.java b/core/src/net/sf/openrocket/simulation/FlightDataType.java index 950b0c728..083cf8cbf 100644 --- a/core/src/net/sf/openrocket/simulation/FlightDataType.java +++ b/core/src/net/sf/openrocket/simulation/FlightDataType.java @@ -15,7 +15,7 @@ import net.sf.openrocket.util.StringUtils; /** * A class defining a storable simulation variable type. This class defined numerous ready * types, and allows also creating new types with any name. When retrieving types based on - * a name, you should use {@link #getType(String, UnitGroup)} to return the default unit type, + * a name, you should use {@link #getType(String, String, UnitGroup)} to return the default unit type, * or a new type if the name does not currently exist. *

* Each type has a type name (description), a unit group and a priority. The type is identified @@ -42,37 +42,50 @@ public class FlightDataType implements Comparable { //// Vertical position and motion //// Altitude - public static final FlightDataType TYPE_ALTITUDE = newType(trans.get("FlightDataType.TYPE_ALTITUDE"), "h", UnitGroup.UNITS_DISTANCE, 10); + public static final FlightDataType TYPE_ALTITUDE = newType(trans.get("FlightDataType.TYPE_ALTITUDE"), "h", UnitGroup.UNITS_DISTANCE, + FlightDataTypeGroup.POSITION_AND_MOTION, 0); //// Vertical velocity - public static final FlightDataType TYPE_VELOCITY_Z = newType(trans.get("FlightDataType.TYPE_VELOCITY_Z"), "Vz", UnitGroup.UNITS_VELOCITY, 11); + public static final FlightDataType TYPE_VELOCITY_Z = newType(trans.get("FlightDataType.TYPE_VELOCITY_Z"), "Vz", UnitGroup.UNITS_VELOCITY, + FlightDataTypeGroup.POSITION_AND_MOTION, 1); //// Vertical acceleration - public static final FlightDataType TYPE_ACCELERATION_Z = newType(trans.get("FlightDataType.TYPE_ACCELERATION_Z"), "Az", UnitGroup.UNITS_ACCELERATION, 12); + public static final FlightDataType TYPE_ACCELERATION_Z = newType(trans.get("FlightDataType.TYPE_ACCELERATION_Z"), "Az", UnitGroup.UNITS_ACCELERATION, + FlightDataTypeGroup.POSITION_AND_MOTION, 2); //// Total motion //// Total velocity - public static final FlightDataType TYPE_VELOCITY_TOTAL = newType(trans.get("FlightDataType.TYPE_VELOCITY_TOTAL"), "Vt", UnitGroup.UNITS_VELOCITY, 20); + public static final FlightDataType TYPE_VELOCITY_TOTAL = newType(trans.get("FlightDataType.TYPE_VELOCITY_TOTAL"), "Vt", UnitGroup.UNITS_VELOCITY, + FlightDataTypeGroup.POSITION_AND_MOTION, 3); //// Total acceleration - public static final FlightDataType TYPE_ACCELERATION_TOTAL = newType(trans.get("FlightDataType.TYPE_ACCELERATION_TOTAL"), "At", UnitGroup.UNITS_ACCELERATION, 21); + public static final FlightDataType TYPE_ACCELERATION_TOTAL = newType(trans.get("FlightDataType.TYPE_ACCELERATION_TOTAL"), "At", UnitGroup.UNITS_ACCELERATION, + FlightDataTypeGroup.POSITION_AND_MOTION, 4); //// Lateral position and motion //// Position East of launch - public static final FlightDataType TYPE_POSITION_X = newType(trans.get("FlightDataType.TYPE_POSITION_X"), "Px", UnitGroup.UNITS_DISTANCE, 30); + public static final FlightDataType TYPE_POSITION_X = newType(trans.get("FlightDataType.TYPE_POSITION_X"), "Px", UnitGroup.UNITS_DISTANCE, + FlightDataTypeGroup.POSITION_AND_MOTION, 5); //// Position North of launch - public static final FlightDataType TYPE_POSITION_Y = newType(trans.get("FlightDataType.TYPE_POSITION_Y"), "Py", UnitGroup.UNITS_DISTANCE, 31); + public static final FlightDataType TYPE_POSITION_Y = newType(trans.get("FlightDataType.TYPE_POSITION_Y"), "Py", UnitGroup.UNITS_DISTANCE, + FlightDataTypeGroup.POSITION_AND_MOTION, 6); //// Lateral distance - public static final FlightDataType TYPE_POSITION_XY = newType(trans.get("FlightDataType.TYPE_POSITION_XY"), "Pl", UnitGroup.UNITS_DISTANCE, 32); + public static final FlightDataType TYPE_POSITION_XY = newType(trans.get("FlightDataType.TYPE_POSITION_XY"), "Pl", UnitGroup.UNITS_DISTANCE, + FlightDataTypeGroup.POSITION_AND_MOTION, 7); //// Lateral direction - public static final FlightDataType TYPE_POSITION_DIRECTION = newType(trans.get("FlightDataType.TYPE_POSITION_DIRECTION"), "\u03b8l", UnitGroup.UNITS_ANGLE, 33); + public static final FlightDataType TYPE_POSITION_DIRECTION = newType(trans.get("FlightDataType.TYPE_POSITION_DIRECTION"), "\u03b8l", UnitGroup.UNITS_ANGLE, + FlightDataTypeGroup.POSITION_AND_MOTION, 8); //// Lateral velocity - public static final FlightDataType TYPE_VELOCITY_XY = newType(trans.get("FlightDataType.TYPE_VELOCITY_XY"), "Vl", UnitGroup.UNITS_VELOCITY, 34); + public static final FlightDataType TYPE_VELOCITY_XY = newType(trans.get("FlightDataType.TYPE_VELOCITY_XY"), "Vl", UnitGroup.UNITS_VELOCITY, + FlightDataTypeGroup.POSITION_AND_MOTION, 9); //// Lateral acceleration - public static final FlightDataType TYPE_ACCELERATION_XY = newType(trans.get("FlightDataType.TYPE_ACCELERATION_XY"), "Al", UnitGroup.UNITS_ACCELERATION, 35); + public static final FlightDataType TYPE_ACCELERATION_XY = newType(trans.get("FlightDataType.TYPE_ACCELERATION_XY"), "Al", UnitGroup.UNITS_ACCELERATION, + FlightDataTypeGroup.POSITION_AND_MOTION, 10); //// Latitude - public static final FlightDataType TYPE_LATITUDE = newType(trans.get("FlightDataType.TYPE_LATITUDE"), "\u03c6", UnitGroup.UNITS_ANGLE, 36); + public static final FlightDataType TYPE_LATITUDE = newType(trans.get("FlightDataType.TYPE_LATITUDE"), "\u03c6", UnitGroup.UNITS_ANGLE, + FlightDataTypeGroup.POSITION_AND_MOTION, 11); //// Longitude - public static final FlightDataType TYPE_LONGITUDE = newType(trans.get("FlightDataType.TYPE_LONGITUDE"), "\u03bb", UnitGroup.UNITS_ANGLE, 37); + public static final FlightDataType TYPE_LONGITUDE = newType(trans.get("FlightDataType.TYPE_LONGITUDE"), "\u03bb", UnitGroup.UNITS_ANGLE, + FlightDataTypeGroup.POSITION_AND_MOTION, 12); //// Gravity public static final FlightDataType TYPE_GRAVITY = newType(trans.get("FlightDataType.TYPE_GRAVITY"), "g", UnitGroup.UNITS_ACCELERATION, 38); @@ -283,7 +296,7 @@ public class FlightDataType implements Comparable { // otherwise, just return what we found if ( !u.equals(type.getUnitGroup()) ) { - oldPriority = type.priority; + oldPriority = type.groupPriority; EXISTING_TYPES.remove(type); log.info("Unitgroup of type "+type.getName() + ", has changed from "+type.getUnitGroup().toString() + @@ -291,7 +304,7 @@ public class FlightDataType implements Comparable { ". Removing old version."); } else if (!s.equals(type.getName())) { - oldPriority = type.priority; + oldPriority = type.groupPriority; EXISTING_TYPES.remove(type); log.info("Name of type "+type.getName()+", has changed to "+s+". Removing old version."); } @@ -326,9 +339,20 @@ public class FlightDataType implements Comparable { /** * Used while initializing the class. + * @param s the name of the type. + * @param symbol the mathematical symbol of the type. + * @param u the unit group of the type. + * @param group the group of the type. + * @param priority the priority of the type within the group. */ + private static synchronized FlightDataType newType(String s, String symbol, UnitGroup u, FlightDataTypeGroup group, int priority) { + FlightDataType type = new FlightDataType(s, symbol, u, group, priority); + //EXISTING_TYPES.put(s.toLowerCase(Locale.ENGLISH), type); + EXISTING_TYPES.put(symbol, type); + return type; + } private static synchronized FlightDataType newType(String s, String symbol, UnitGroup u, int priority) { - FlightDataType type = new FlightDataType(s, symbol, u, priority); + FlightDataType type = new FlightDataType(s, symbol, u, FlightDataTypeGroup.CUSTOM, priority); //EXISTING_TYPES.put(s.toLowerCase(Locale.ENGLISH), type); EXISTING_TYPES.put(symbol, type); return type; @@ -338,11 +362,12 @@ public class FlightDataType implements Comparable { private final String name; private final String symbol; private final UnitGroup units; - private final int priority; + private final FlightDataTypeGroup group; + private final int groupPriority; private final int hashCode; - private FlightDataType(String typeName, String symbol, UnitGroup units, int priority) { + private FlightDataType(String typeName, String symbol, UnitGroup units, FlightDataTypeGroup group, int priority) { if (typeName == null) throw new IllegalArgumentException("typeName is null"); if (units == null) @@ -350,16 +375,11 @@ public class FlightDataType implements Comparable { this.name = typeName; this.symbol = symbol; this.units = units; - this.priority = priority; + this.group = group; + this.groupPriority = priority; this.hashCode = this.name.toLowerCase(Locale.ENGLISH).hashCode(); } - /* - public void setPriority(int p){ - this.priority = p; - } - */ - public String getName() { return name; } @@ -371,6 +391,14 @@ public class FlightDataType implements Comparable { public UnitGroup getUnitGroup() { return units; } + + public FlightDataTypeGroup getGroup() { + return group; + } + + public int getGroupPriority() { + return groupPriority; + } @Override public String toString() { @@ -391,8 +419,8 @@ public class FlightDataType implements Comparable { @Override public int compareTo(FlightDataType o) { - if (this.priority != o.priority) - return this.priority - o.priority; + if (this.groupPriority != o.groupPriority) + return this.groupPriority - o.groupPriority; return this.name.compareToIgnoreCase(o.name); } } diff --git a/core/src/net/sf/openrocket/simulation/FlightDataTypeGroup.java b/core/src/net/sf/openrocket/simulation/FlightDataTypeGroup.java new file mode 100644 index 000000000..296374031 --- /dev/null +++ b/core/src/net/sf/openrocket/simulation/FlightDataTypeGroup.java @@ -0,0 +1,39 @@ +package net.sf.openrocket.simulation; + +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.startup.Application; + +public class FlightDataTypeGroup { + private static final Translator trans = Application.getTranslator(); + + public static final FlightDataTypeGroup POSITION_AND_MOTION = new FlightDataTypeGroup("Position and motion", 0); + + public static final FlightDataTypeGroup CUSTOM = new FlightDataTypeGroup("Custom", 100); + + // An array of all the built-in groups + public static final FlightDataTypeGroup[] ALL_GROUPS = { + POSITION_AND_MOTION, + CUSTOM + }; + + private final String name; + private final int priority; + + private FlightDataTypeGroup(String groupName, int priority) { + this.name = groupName; + this.priority = priority; + } + + public String getName() { + return name; + } + + public int getPriority() { + return priority; + } + + @Override + public String toString() { + return name; + } +} diff --git a/swing/src/net/sf/openrocket/gui/simulation/FlightDataComboBox.java b/swing/src/net/sf/openrocket/gui/simulation/FlightDataComboBox.java new file mode 100644 index 000000000..80931952e --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/simulation/FlightDataComboBox.java @@ -0,0 +1,105 @@ +package net.sf.openrocket.gui.simulation; + +import net.sf.openrocket.simulation.FlightDataType; +import net.sf.openrocket.simulation.FlightDataTypeGroup; + +import javax.swing.JComboBox; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.plaf.basic.BasicArrowButton; +import java.awt.Component; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.Hashtable; + +public class FlightDataComboBox extends JComboBox { + private final JPopupMenu mainMenu; + private final Hashtable subItems = new Hashtable<>(); + + public FlightDataComboBox(FlightDataType[] types) { + super(types); + setEditable(false); + + for (FlightDataTypeGroup group : FlightDataTypeGroup.ALL_GROUPS) { + ArrayList listForGroup = new ArrayList<>(); + for (FlightDataType type : types) { + if (type.getGroup().equals(group)) { + listForGroup.add(type); + } + } + subItems.put(group, listForGroup.toArray(new FlightDataType[0])); + } + + mainMenu = createMainMenu(); + + // Override the mouse listeners to use our custom popup + for (MouseListener mouseListener : getMouseListeners()) { + removeMouseListener(mouseListener); + } + + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + showCustomPopup(); + } + }); + + Component arrowButton = getArrowButton(); + if (arrowButton != null) { + for (MouseListener mouseListener : arrowButton.getMouseListeners()) { + arrowButton.removeMouseListener(mouseListener); + } + arrowButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + showCustomPopup(); + } + }); + } + } + + private Component getArrowButton() { + for (Component child : getComponents()) { + if (child instanceof BasicArrowButton) { + return child; + } + } + return null; + } + + @Override + public void showPopup() { + // Override the default JComboBox showPopup() to do nothing + // Our custom popup will be shown by the MouseListener + } + + private JPopupMenu createMainMenu() { + JPopupMenu menu = new JPopupMenu(); + + for (FlightDataTypeGroup group : FlightDataTypeGroup.ALL_GROUPS) { + JMenu groupMenu = new JMenu(group.getName()); + FlightDataType[] typesForGroup = subItems.get(group); + + if (typesForGroup != null) { + for (FlightDataType type : typesForGroup) { + JMenuItem typeItem = new JMenuItem(type.getName()); + typeItem.addActionListener(e -> { + setSelectedItem(type); + }); + groupMenu.add(typeItem); + } + } + + menu.add(groupMenu); + } + + return menu; + } + + private void showCustomPopup() { + mainMenu.show(this, 0, getHeight()); + } +} diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java index 11fcbeb32..37e6620bb 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java +++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationPlotPanel.java @@ -497,7 +497,7 @@ public class SimulationPlotPanel extends JPanel { this.index = plotIndex; - typeSelector = new JComboBox(types); + typeSelector = new FlightDataComboBox(types); typeSelector.setSelectedItem(type); typeSelector.addItemListener(new ItemListener() { @Override