Merge pull request #2413 from SiboVG/issue-2338
[#2338] Organize axis plot types selector in categories + add search function
This commit is contained in:
commit
5938f08145
@ -811,6 +811,9 @@ simplotpanel.MarkerStyle.btn.VerticalMarker = Vertical line
|
||||
simplotpanel.MarkerStyle.btn.Icon = Icon
|
||||
simplotpanel.MarkerStyle.OnlyInTime = Only available for time domain, other domains only support icon markers
|
||||
|
||||
! FlightDataComboBox
|
||||
FlightDataComboBox.placeholder = Enter the data type
|
||||
|
||||
! Component add buttons
|
||||
compaddbuttons.AxialStage = Stage
|
||||
compaddbuttons.Bodycompandfinsets = Body Components and Fin Sets
|
||||
@ -2062,6 +2065,20 @@ FlightDataType.TYPE_LONGITUDE = Longitude
|
||||
FlightDataType.TYPE_CORIOLIS_ACCELERATION = Coriolis acceleration
|
||||
FlightDataType.TYPE_GRAVITY = Gravitational acceleration
|
||||
|
||||
! FlightDataTypeGroup
|
||||
FlightDataTypeGroup.GROUP_TIME = Time
|
||||
FlightDataTypeGroup.GROUP_POSITION_AND_MOTION = Position and Motion
|
||||
FlightDataTypeGroup.GROUP_ORIENTATION = Orientation
|
||||
FlightDataTypeGroup.GROUP_MASS_AND_INERTIA = Mass and Inertia
|
||||
FlightDataTypeGroup.GROUP_STABILITY = Stability
|
||||
FlightDataTypeGroup.GROUP_THRUST_AND_DRAG = Thrust and Drag
|
||||
FlightDataTypeGroup.GROUP_COEFFICIENTS = Coefficients
|
||||
FlightDataTypeGroup.GROUP_ATMOSPHERIC_CONDITIONS = Atmospheric Conditions
|
||||
FlightDataTypeGroup.GROUP_CHARACTERISTIC_NUMBERS = Characteristic Numbers
|
||||
FlightDataTypeGroup.GROUP_REFERENCE_VALUES = Reference Values
|
||||
FlightDataTypeGroup.GROUP_SIMULATION_INFORMATION = Simulation Information
|
||||
FlightDataTypeGroup.GROUP_CUSTOM = Custom
|
||||
|
||||
! PlotConfiguration
|
||||
PlotConfiguration.Verticalmotion = Vertical motion vs. time
|
||||
PlotConfiguration.Totalmotion = Total motion vs. time
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* Each type has a type name (description), a unit group and a priority. The type is identified
|
||||
@ -38,156 +38,203 @@ public class FlightDataType implements Comparable<FlightDataType> {
|
||||
|
||||
|
||||
//// Time
|
||||
public static final FlightDataType TYPE_TIME = newType(trans.get("FlightDataType.TYPE_TIME"), "t", UnitGroup.UNITS_FLIGHT_TIME, 1);
|
||||
public static final FlightDataType TYPE_TIME = newType(trans.get("FlightDataType.TYPE_TIME"), "t", UnitGroup.UNITS_FLIGHT_TIME,
|
||||
FlightDataTypeGroup.TIME, 0);
|
||||
|
||||
//// Vertical position and motion
|
||||
//// 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);
|
||||
//// Vertical acceleration
|
||||
public static final FlightDataType TYPE_ACCELERATION_Z = newType(trans.get("FlightDataType.TYPE_ACCELERATION_Z"), "Az", UnitGroup.UNITS_ACCELERATION, 12);
|
||||
|
||||
|
||||
//// Total motion
|
||||
public static final FlightDataType TYPE_VELOCITY_Z = newType(trans.get("FlightDataType.TYPE_VELOCITY_Z"), "Vz", UnitGroup.UNITS_VELOCITY,
|
||||
FlightDataTypeGroup.POSITION_AND_MOTION, 1);
|
||||
//// 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, 2);
|
||||
//// Vertical acceleration
|
||||
public static final FlightDataType TYPE_ACCELERATION_Z = newType(trans.get("FlightDataType.TYPE_ACCELERATION_Z"), "Az", UnitGroup.UNITS_ACCELERATION,
|
||||
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, 0);
|
||||
//// 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, 1);
|
||||
//// 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, 2);
|
||||
//// 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, 3);
|
||||
//// 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, 4);
|
||||
//// 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, 5);
|
||||
//// 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, 6);
|
||||
//// Longitude
|
||||
public static final FlightDataType TYPE_LONGITUDE = newType(trans.get("FlightDataType.TYPE_LONGITUDE"), "\u03bb", UnitGroup.UNITS_ANGLE, 37);
|
||||
|
||||
//// Gravity
|
||||
public static final FlightDataType TYPE_GRAVITY = newType(trans.get("FlightDataType.TYPE_GRAVITY"), "g", UnitGroup.UNITS_ACCELERATION, 38);
|
||||
|
||||
//// Angular motion
|
||||
public static final FlightDataType TYPE_LONGITUDE = newType(trans.get("FlightDataType.TYPE_LONGITUDE"), "\u03bb", UnitGroup.UNITS_ANGLE,
|
||||
FlightDataTypeGroup.POSITION_AND_MOTION, 7);
|
||||
|
||||
|
||||
// Orientation
|
||||
//// Angle of attack
|
||||
public static final FlightDataType TYPE_AOA = newType(trans.get("FlightDataType.TYPE_AOA"), "\u03b1", UnitGroup.UNITS_ANGLE, 40);
|
||||
public static final FlightDataType TYPE_AOA = newType(trans.get("FlightDataType.TYPE_AOA"), "\u03b1", UnitGroup.UNITS_ANGLE,
|
||||
FlightDataTypeGroup.ORIENTATION, 0);
|
||||
//// Roll rate
|
||||
public static final FlightDataType TYPE_ROLL_RATE = newType(trans.get("FlightDataType.TYPE_ROLL_RATE"), "d\u03a6", UnitGroup.UNITS_ROLL, 41);
|
||||
public static final FlightDataType TYPE_ROLL_RATE = newType(trans.get("FlightDataType.TYPE_ROLL_RATE"), "d\u03a6", UnitGroup.UNITS_ROLL,
|
||||
FlightDataTypeGroup.ORIENTATION, 1);
|
||||
//// Pitch rate
|
||||
public static final FlightDataType TYPE_PITCH_RATE = newType(trans.get("FlightDataType.TYPE_PITCH_RATE"), "d\u03b8", UnitGroup.UNITS_ROLL, 42);
|
||||
public static final FlightDataType TYPE_PITCH_RATE = newType(trans.get("FlightDataType.TYPE_PITCH_RATE"), "d\u03b8", UnitGroup.UNITS_ROLL,
|
||||
FlightDataTypeGroup.ORIENTATION, 2);
|
||||
//// Yaw rate
|
||||
public static final FlightDataType TYPE_YAW_RATE = newType(trans.get("FlightDataType.TYPE_YAW_RATE"), "d\u03a8", UnitGroup.UNITS_ROLL, 43);
|
||||
public static final FlightDataType TYPE_YAW_RATE = newType(trans.get("FlightDataType.TYPE_YAW_RATE"), "d\u03a8", UnitGroup.UNITS_ROLL,
|
||||
FlightDataTypeGroup.ORIENTATION, 3);
|
||||
//// Vertical orientation (zenith)
|
||||
public static final FlightDataType TYPE_ORIENTATION_THETA = newType(trans.get("FlightDataType.TYPE_ORIENTATION_THETA"), "\u0398", UnitGroup.UNITS_ANGLE,
|
||||
FlightDataTypeGroup.ORIENTATION, 4);
|
||||
//// Lateral orientation (azimuth)
|
||||
public static final FlightDataType TYPE_ORIENTATION_PHI = newType(trans.get("FlightDataType.TYPE_ORIENTATION_PHI"), "\u03a6", UnitGroup.UNITS_ANGLE,
|
||||
FlightDataTypeGroup.ORIENTATION, 5);
|
||||
|
||||
|
||||
|
||||
//// Stability information
|
||||
// Mass and inertia
|
||||
//// Mass
|
||||
public static final FlightDataType TYPE_MASS = newType(trans.get("FlightDataType.TYPE_MASS"), "m", UnitGroup.UNITS_MASS, 50);
|
||||
public static final FlightDataType TYPE_MASS = newType(trans.get("FlightDataType.TYPE_MASS"), "m", UnitGroup.UNITS_MASS,
|
||||
FlightDataTypeGroup.MASS_AND_INERTIA, 0);
|
||||
//// Motor mass
|
||||
public static final FlightDataType TYPE_MOTOR_MASS = newType(trans.get("FlightDataType.TYPE_MOTOR_MASS"), "mp", UnitGroup.UNITS_MASS, 51);
|
||||
public static final FlightDataType TYPE_MOTOR_MASS = newType(trans.get("FlightDataType.TYPE_MOTOR_MASS"), "mp", UnitGroup.UNITS_MASS,
|
||||
FlightDataTypeGroup.MASS_AND_INERTIA, 1);
|
||||
//// Longitudinal moment of inertia
|
||||
public static final FlightDataType TYPE_LONGITUDINAL_INERTIA = newType(trans.get("FlightDataType.TYPE_LONGITUDINAL_INERTIA"), "Il", UnitGroup.UNITS_INERTIA, 52);
|
||||
public static final FlightDataType TYPE_LONGITUDINAL_INERTIA = newType(trans.get("FlightDataType.TYPE_LONGITUDINAL_INERTIA"), "Il", UnitGroup.UNITS_INERTIA,
|
||||
FlightDataTypeGroup.MASS_AND_INERTIA, 2);
|
||||
//// Rotational moment of inertia
|
||||
public static final FlightDataType TYPE_ROTATIONAL_INERTIA = newType(trans.get("FlightDataType.TYPE_ROTATIONAL_INERTIA"), "Ir", UnitGroup.UNITS_INERTIA, 53);
|
||||
public static final FlightDataType TYPE_ROTATIONAL_INERTIA = newType(trans.get("FlightDataType.TYPE_ROTATIONAL_INERTIA"), "Ir", UnitGroup.UNITS_INERTIA,
|
||||
FlightDataTypeGroup.MASS_AND_INERTIA, 3);
|
||||
//// Gravity
|
||||
public static final FlightDataType TYPE_GRAVITY = newType(trans.get("FlightDataType.TYPE_GRAVITY"), "g", UnitGroup.UNITS_ACCELERATION,
|
||||
FlightDataTypeGroup.MASS_AND_INERTIA, 4);
|
||||
|
||||
// Stability
|
||||
//// CP location
|
||||
public static final FlightDataType TYPE_CP_LOCATION = newType(trans.get("FlightDataType.TYPE_CP_LOCATION"), "Cp", UnitGroup.UNITS_LENGTH, 54);
|
||||
public static final FlightDataType TYPE_CP_LOCATION = newType(trans.get("FlightDataType.TYPE_CP_LOCATION"), "Cp", UnitGroup.UNITS_LENGTH,
|
||||
FlightDataTypeGroup.STABILITY, 0);
|
||||
//// CG location
|
||||
public static final FlightDataType TYPE_CG_LOCATION = newType(trans.get("FlightDataType.TYPE_CG_LOCATION"), "Cg", UnitGroup.UNITS_LENGTH, 55);
|
||||
public static final FlightDataType TYPE_CG_LOCATION = newType(trans.get("FlightDataType.TYPE_CG_LOCATION"), "Cg", UnitGroup.UNITS_LENGTH,
|
||||
FlightDataTypeGroup.STABILITY, 1);
|
||||
//// Stability margin calibers
|
||||
public static final FlightDataType TYPE_STABILITY = newType(trans.get("FlightDataType.TYPE_STABILITY"), "S", UnitGroup.UNITS_COEFFICIENT, 56);
|
||||
public static final FlightDataType TYPE_STABILITY = newType(trans.get("FlightDataType.TYPE_STABILITY"), "S", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.STABILITY, 2);
|
||||
|
||||
|
||||
//// Characteristic numbers
|
||||
// Characteristic numbers
|
||||
//// Mach number
|
||||
public static final FlightDataType TYPE_MACH_NUMBER = newType(trans.get("FlightDataType.TYPE_MACH_NUMBER"), "M", UnitGroup.UNITS_COEFFICIENT, 60);
|
||||
public static final FlightDataType TYPE_MACH_NUMBER = newType(trans.get("FlightDataType.TYPE_MACH_NUMBER"), "M", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.CHARACTERISTIC_NUMBERS, 0);
|
||||
//// Reynolds number
|
||||
public static final FlightDataType TYPE_REYNOLDS_NUMBER = newType(trans.get("FlightDataType.TYPE_REYNOLDS_NUMBER"), "R", UnitGroup.UNITS_COEFFICIENT, 61);
|
||||
public static final FlightDataType TYPE_REYNOLDS_NUMBER = newType(trans.get("FlightDataType.TYPE_REYNOLDS_NUMBER"), "R", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.CHARACTERISTIC_NUMBERS, 1);
|
||||
|
||||
|
||||
//// Thrust and drag
|
||||
// Thrust and drag
|
||||
//// Thrust
|
||||
public static final FlightDataType TYPE_THRUST_FORCE = newType(trans.get("FlightDataType.TYPE_THRUST_FORCE"), "Ft", UnitGroup.UNITS_FORCE, 70);
|
||||
public static final FlightDataType TYPE_THRUST_FORCE = newType(trans.get("FlightDataType.TYPE_THRUST_FORCE"), "Ft", UnitGroup.UNITS_FORCE,
|
||||
FlightDataTypeGroup.THRUST_AND_DRAG, 0);
|
||||
//// Thrust-to-weight ratio
|
||||
public static final FlightDataType TYPE_THRUST_WEIGHT_RATIO = newType(trans.get("FlightDataType.TYPE_THRUST_WEIGHT_RATIO"), "Twr", UnitGroup.UNITS_COEFFICIENT, 71);
|
||||
public static final FlightDataType TYPE_THRUST_WEIGHT_RATIO = newType(trans.get("FlightDataType.TYPE_THRUST_WEIGHT_RATIO"), "Twr", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.THRUST_AND_DRAG, 1);
|
||||
//// Drag force
|
||||
public static final FlightDataType TYPE_DRAG_FORCE = newType(trans.get("FlightDataType.TYPE_DRAG_FORCE"), "Fd", UnitGroup.UNITS_FORCE, 72);
|
||||
public static final FlightDataType TYPE_DRAG_FORCE = newType(trans.get("FlightDataType.TYPE_DRAG_FORCE"), "Fd", UnitGroup.UNITS_FORCE,
|
||||
FlightDataTypeGroup.THRUST_AND_DRAG, 2);
|
||||
//// Drag coefficient
|
||||
public static final FlightDataType TYPE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_DRAG_COEFF"), "Cd", UnitGroup.UNITS_COEFFICIENT, 73);
|
||||
//// Axial drag coefficient
|
||||
public static final FlightDataType TYPE_AXIAL_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_AXIAL_DRAG_COEFF"), "Cda", UnitGroup.UNITS_COEFFICIENT, 74);
|
||||
|
||||
|
||||
//// Component drag coefficients
|
||||
public static final FlightDataType TYPE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_DRAG_COEFF"), "Cd", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.THRUST_AND_DRAG, 3);
|
||||
//// Friction drag coefficient
|
||||
public static final FlightDataType TYPE_FRICTION_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_FRICTION_DRAG_COEFF"), "Cdf", UnitGroup.UNITS_COEFFICIENT, 80);
|
||||
public static final FlightDataType TYPE_FRICTION_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_FRICTION_DRAG_COEFF"), "Cdf", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.THRUST_AND_DRAG, 4);
|
||||
//// Pressure drag coefficient
|
||||
public static final FlightDataType TYPE_PRESSURE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_PRESSURE_DRAG_COEFF"), "Cdp", UnitGroup.UNITS_COEFFICIENT, 81);
|
||||
public static final FlightDataType TYPE_PRESSURE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_PRESSURE_DRAG_COEFF"), "Cdp", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.THRUST_AND_DRAG, 5);
|
||||
//// Base drag coefficient
|
||||
public static final FlightDataType TYPE_BASE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_BASE_DRAG_COEFF"), "Cdb", UnitGroup.UNITS_COEFFICIENT, 82);
|
||||
public static final FlightDataType TYPE_BASE_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_BASE_DRAG_COEFF"), "Cdb", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.THRUST_AND_DRAG, 6);
|
||||
//// Axial drag coefficient
|
||||
public static final FlightDataType TYPE_AXIAL_DRAG_COEFF = newType(trans.get("FlightDataType.TYPE_AXIAL_DRAG_COEFF"), "Cda", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.THRUST_AND_DRAG, 7);
|
||||
|
||||
|
||||
//// Other coefficients
|
||||
// Coefficients
|
||||
//// Normal force coefficient
|
||||
public static final FlightDataType TYPE_NORMAL_FORCE_COEFF = newType(trans.get("FlightDataType.TYPE_NORMAL_FORCE_COEFF"), "Cn", UnitGroup.UNITS_COEFFICIENT, 90);
|
||||
public static final FlightDataType TYPE_NORMAL_FORCE_COEFF = newType(trans.get("FlightDataType.TYPE_NORMAL_FORCE_COEFF"), "Cn", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.COEFFICIENTS, 0);
|
||||
//// Pitch moment coefficient
|
||||
public static final FlightDataType TYPE_PITCH_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_PITCH_MOMENT_COEFF"), "C\u03b8", UnitGroup.UNITS_COEFFICIENT, 91);
|
||||
public static final FlightDataType TYPE_PITCH_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_PITCH_MOMENT_COEFF"), "C\u03b8", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.COEFFICIENTS, 1);
|
||||
//// Yaw moment coefficient
|
||||
public static final FlightDataType TYPE_YAW_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_YAW_MOMENT_COEFF"), "C\u03c4\u03a8", UnitGroup.UNITS_COEFFICIENT, 92);
|
||||
public static final FlightDataType TYPE_YAW_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_YAW_MOMENT_COEFF"), "C\u03c4\u03a8", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.COEFFICIENTS, 2);
|
||||
//// Side force coefficient
|
||||
public static final FlightDataType TYPE_SIDE_FORCE_COEFF = newType(trans.get("FlightDataType.TYPE_SIDE_FORCE_COEFF"), "C\u03c4s", UnitGroup.UNITS_COEFFICIENT, 93);
|
||||
public static final FlightDataType TYPE_SIDE_FORCE_COEFF = newType(trans.get("FlightDataType.TYPE_SIDE_FORCE_COEFF"), "C\u03c4s", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.COEFFICIENTS, 3);
|
||||
//// Roll moment coefficient
|
||||
public static final FlightDataType TYPE_ROLL_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_MOMENT_COEFF"), "C\u03c4\u03a6", UnitGroup.UNITS_COEFFICIENT, 94);
|
||||
public static final FlightDataType TYPE_ROLL_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_MOMENT_COEFF"), "C\u03c4\u03a6", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.COEFFICIENTS, 4);
|
||||
//// Roll forcing coefficient
|
||||
public static final FlightDataType TYPE_ROLL_FORCING_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_FORCING_COEFF"), "Cf\u03a6", UnitGroup.UNITS_COEFFICIENT, 95);
|
||||
public static final FlightDataType TYPE_ROLL_FORCING_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_FORCING_COEFF"), "Cf\u03a6", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.COEFFICIENTS, 5);
|
||||
//// Roll damping coefficient
|
||||
public static final FlightDataType TYPE_ROLL_DAMPING_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_DAMPING_COEFF"), "C\u03b6\u03a6", UnitGroup.UNITS_COEFFICIENT, 96);
|
||||
|
||||
public static final FlightDataType TYPE_ROLL_DAMPING_COEFF = newType(trans.get("FlightDataType.TYPE_ROLL_DAMPING_COEFF"), "C\u03b6\u03a6", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.COEFFICIENTS, 6);
|
||||
//// Pitch damping coefficient
|
||||
public static final FlightDataType TYPE_PITCH_DAMPING_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_PITCH_DAMPING_MOMENT_COEFF"), "C\u03b6\u03b8", UnitGroup.UNITS_COEFFICIENT, 97);
|
||||
public static final FlightDataType TYPE_PITCH_DAMPING_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_PITCH_DAMPING_MOMENT_COEFF"), "C\u03b6\u03b8", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.COEFFICIENTS, 7);
|
||||
//// Yaw damping coefficient
|
||||
public static final FlightDataType TYPE_YAW_DAMPING_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_YAW_DAMPING_MOMENT_COEFF"), "C\u03b6\u03a8", UnitGroup.UNITS_COEFFICIENT, 98);
|
||||
public static final FlightDataType TYPE_YAW_DAMPING_MOMENT_COEFF = newType(trans.get("FlightDataType.TYPE_YAW_DAMPING_MOMENT_COEFF"), "C\u03b6\u03a8", UnitGroup.UNITS_COEFFICIENT,
|
||||
FlightDataTypeGroup.COEFFICIENTS, 8);
|
||||
|
||||
//// Coriolis acceleration
|
||||
public static final FlightDataType TYPE_CORIOLIS_ACCELERATION = newType(trans.get("FlightDataType.TYPE_CORIOLIS_ACCELERATION"), "Ac", UnitGroup.UNITS_ACCELERATION, 99);
|
||||
|
||||
|
||||
//// Reference length + area
|
||||
// Reference values
|
||||
//// Reference length
|
||||
public static final FlightDataType TYPE_REFERENCE_LENGTH = newType(trans.get("FlightDataType.TYPE_REFERENCE_LENGTH"), "Lr", UnitGroup.UNITS_LENGTH, 100);
|
||||
public static final FlightDataType TYPE_REFERENCE_LENGTH = newType(trans.get("FlightDataType.TYPE_REFERENCE_LENGTH"), "Lr", UnitGroup.UNITS_LENGTH,
|
||||
FlightDataTypeGroup.REFERENCE_VALUES, 0);
|
||||
//// Reference area
|
||||
public static final FlightDataType TYPE_REFERENCE_AREA = newType(trans.get("FlightDataType.TYPE_REFERENCE_AREA"), "Ar", UnitGroup.UNITS_AREA, 101);
|
||||
public static final FlightDataType TYPE_REFERENCE_AREA = newType(trans.get("FlightDataType.TYPE_REFERENCE_AREA"), "Ar", UnitGroup.UNITS_AREA,
|
||||
FlightDataTypeGroup.REFERENCE_VALUES, 1);
|
||||
|
||||
|
||||
//// Orientation
|
||||
//// Vertical orientation (zenith)
|
||||
public static final FlightDataType TYPE_ORIENTATION_THETA = newType(trans.get("FlightDataType.TYPE_ORIENTATION_THETA"), "\u0398", UnitGroup.UNITS_ANGLE, 106);
|
||||
//// Lateral orientation (azimuth)
|
||||
public static final FlightDataType TYPE_ORIENTATION_PHI = newType(trans.get("FlightDataType.TYPE_ORIENTATION_PHI"), "\u03a6", UnitGroup.UNITS_ANGLE, 107);
|
||||
|
||||
|
||||
//// Atmospheric conditions
|
||||
// Atmospheric conditions
|
||||
//// Wind velocity
|
||||
public static final FlightDataType TYPE_WIND_VELOCITY = newType(trans.get("FlightDataType.TYPE_WIND_VELOCITY"), "Vw", UnitGroup.UNITS_VELOCITY, 110);
|
||||
public static final FlightDataType TYPE_WIND_VELOCITY = newType(trans.get("FlightDataType.TYPE_WIND_VELOCITY"), "Vw", UnitGroup.UNITS_VELOCITY,
|
||||
FlightDataTypeGroup.ATMOSPHERIC_CONDITIONS, 0);
|
||||
//// Air temperature
|
||||
public static final FlightDataType TYPE_AIR_TEMPERATURE = newType(trans.get("FlightDataType.TYPE_AIR_TEMPERATURE"), "T", UnitGroup.UNITS_TEMPERATURE, 111);
|
||||
public static final FlightDataType TYPE_AIR_TEMPERATURE = newType(trans.get("FlightDataType.TYPE_AIR_TEMPERATURE"), "T", UnitGroup.UNITS_TEMPERATURE,
|
||||
FlightDataTypeGroup.ATMOSPHERIC_CONDITIONS, 1);
|
||||
//// Air pressure
|
||||
public static final FlightDataType TYPE_AIR_PRESSURE = newType(trans.get("FlightDataType.TYPE_AIR_PRESSURE"), "P", UnitGroup.UNITS_PRESSURE, 112);
|
||||
public static final FlightDataType TYPE_AIR_PRESSURE = newType(trans.get("FlightDataType.TYPE_AIR_PRESSURE"), "P", UnitGroup.UNITS_PRESSURE,
|
||||
FlightDataTypeGroup.ATMOSPHERIC_CONDITIONS, 2);
|
||||
//// Speed of sound
|
||||
public static final FlightDataType TYPE_SPEED_OF_SOUND = newType(trans.get("FlightDataType.TYPE_SPEED_OF_SOUND"), "Vs", UnitGroup.UNITS_VELOCITY, 113);
|
||||
public static final FlightDataType TYPE_SPEED_OF_SOUND = newType(trans.get("FlightDataType.TYPE_SPEED_OF_SOUND"), "Vs", UnitGroup.UNITS_VELOCITY,
|
||||
FlightDataTypeGroup.ATMOSPHERIC_CONDITIONS, 3);
|
||||
|
||||
//// Simulation information
|
||||
// Simulation information
|
||||
//// Simulation time step
|
||||
public static final FlightDataType TYPE_TIME_STEP = newType(trans.get("FlightDataType.TYPE_TIME_STEP"), "dt", UnitGroup.UNITS_TIME_STEP, 200);
|
||||
public static final FlightDataType TYPE_TIME_STEP = newType(trans.get("FlightDataType.TYPE_TIME_STEP"), "dt", UnitGroup.UNITS_TIME_STEP,
|
||||
FlightDataTypeGroup.SIMULATION_INFORMATION, 0);
|
||||
//// Computation time
|
||||
public static final FlightDataType TYPE_COMPUTATION_TIME = newType(trans.get("FlightDataType.TYPE_COMPUTATION_TIME"), "tc", UnitGroup.UNITS_SHORT_TIME, 201);
|
||||
public static final FlightDataType TYPE_COMPUTATION_TIME = newType(trans.get("FlightDataType.TYPE_COMPUTATION_TIME"), "tc", UnitGroup.UNITS_SHORT_TIME,
|
||||
FlightDataTypeGroup.SIMULATION_INFORMATION, 1);
|
||||
|
||||
// An array of all the built in types
|
||||
public static final FlightDataType[] ALL_TYPES = {
|
||||
@ -286,7 +333,7 @@ public class FlightDataType implements Comparable<FlightDataType> {
|
||||
// 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() +
|
||||
@ -294,7 +341,7 @@ public class FlightDataType implements Comparable<FlightDataType> {
|
||||
". 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.");
|
||||
}
|
||||
@ -329,9 +376,20 @@ public class FlightDataType implements Comparable<FlightDataType> {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@ -341,11 +399,12 @@ public class FlightDataType implements Comparable<FlightDataType> {
|
||||
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)
|
||||
@ -353,16 +412,11 @@ public class FlightDataType implements Comparable<FlightDataType> {
|
||||
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;
|
||||
}
|
||||
@ -374,6 +428,14 @@ public class FlightDataType implements Comparable<FlightDataType> {
|
||||
public UnitGroup getUnitGroup() {
|
||||
return units;
|
||||
}
|
||||
|
||||
public FlightDataTypeGroup getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public int getGroupPriority() {
|
||||
return groupPriority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -394,8 +456,8 @@ public class FlightDataType implements Comparable<FlightDataType> {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
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 TIME = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_TIME"), 0);
|
||||
public static final FlightDataTypeGroup POSITION_AND_MOTION = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_POSITION_AND_MOTION"), 10);
|
||||
public static final FlightDataTypeGroup ORIENTATION = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_ORIENTATION"), 20);
|
||||
public static final FlightDataTypeGroup MASS_AND_INERTIA = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_MASS_AND_INERTIA"), 30);
|
||||
public static final FlightDataTypeGroup STABILITY = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_STABILITY"), 40);
|
||||
public static final FlightDataTypeGroup THRUST_AND_DRAG = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_THRUST_AND_DRAG"), 50);
|
||||
public static final FlightDataTypeGroup COEFFICIENTS = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_COEFFICIENTS"), 60);
|
||||
public static final FlightDataTypeGroup ATMOSPHERIC_CONDITIONS = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_ATMOSPHERIC_CONDITIONS"), 70);
|
||||
public static final FlightDataTypeGroup CHARACTERISTIC_NUMBERS = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_CHARACTERISTIC_NUMBERS"), 80);
|
||||
public static final FlightDataTypeGroup REFERENCE_VALUES = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_REFERENCE_VALUES"), 90);
|
||||
public static final FlightDataTypeGroup SIMULATION_INFORMATION = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_SIMULATION_INFORMATION"), 100);
|
||||
|
||||
public static final FlightDataTypeGroup CUSTOM = new FlightDataTypeGroup(trans.get("FlightDataTypeGroup.GROUP_CUSTOM"), 200);
|
||||
|
||||
// An array of all the built-in groups
|
||||
public static final FlightDataTypeGroup[] ALL_GROUPS = {
|
||||
TIME,
|
||||
POSITION_AND_MOTION,
|
||||
ORIENTATION,
|
||||
MASS_AND_INERTIA,
|
||||
STABILITY,
|
||||
THRUST_AND_DRAG,
|
||||
COEFFICIENTS,
|
||||
ATMOSPHERIC_CONDITIONS,
|
||||
CHARACTERISTIC_NUMBERS,
|
||||
REFERENCE_VALUES,
|
||||
SIMULATION_INFORMATION,
|
||||
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 getName();
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package net.sf.openrocket.gui.simulation;
|
||||
|
||||
import net.sf.openrocket.gui.widgets.SearchableAndCategorizableComboBox;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.FlightDataTypeGroup;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
|
||||
import javax.swing.JComboBox;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class FlightDataComboBox extends JComboBox<FlightDataType> {
|
||||
private static final Translator trans = Application.getTranslator();
|
||||
|
||||
public static SearchableAndCategorizableComboBox<FlightDataTypeGroup, FlightDataType> createComboBox(FlightDataTypeGroup[] allGroups, FlightDataType[] types) {
|
||||
final Map<FlightDataTypeGroup, FlightDataType[]> typeGroupMap = createFlightDataGroupMap(allGroups, types);
|
||||
return new SearchableAndCategorizableComboBox<>(typeGroupMap, trans.get("FlightDataComboBox.placeholder"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a map of flight data group and corresponding flight data types.
|
||||
* @param groups the groups
|
||||
* @param types the types
|
||||
* @return the map linking the types to their groups
|
||||
*/
|
||||
private static Map<FlightDataTypeGroup, FlightDataType[]> createFlightDataGroupMap(
|
||||
FlightDataTypeGroup[] groups, FlightDataType[] types) {
|
||||
// Sort the groups based on priority (lower number = higher priority)
|
||||
FlightDataTypeGroup[] sortedGroups = groups.clone();
|
||||
Arrays.sort(sortedGroups, Comparator.comparingInt(FlightDataTypeGroup::getPriority));
|
||||
|
||||
Map<FlightDataTypeGroup, FlightDataType[]> map = new LinkedHashMap<>();
|
||||
for (FlightDataTypeGroup group : sortedGroups) {
|
||||
List<FlightDataType> itemsForGroup = new ArrayList<>();
|
||||
for (FlightDataType type : types) {
|
||||
if (type.getGroup().equals(group)) {
|
||||
itemsForGroup.add(type);
|
||||
}
|
||||
}
|
||||
// Sort the types within each group based on priority
|
||||
itemsForGroup.sort(Comparator.comparingInt(FlightDataType::getGroupPriority));
|
||||
|
||||
map.put(group, itemsForGroup.toArray(new FlightDataType[0]));
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ import net.sf.openrocket.gui.util.UITheme;
|
||||
import net.sf.openrocket.l10n.Translator;
|
||||
import net.sf.openrocket.simulation.FlightDataBranch;
|
||||
import net.sf.openrocket.simulation.FlightDataType;
|
||||
import net.sf.openrocket.simulation.FlightDataTypeGroup;
|
||||
import net.sf.openrocket.simulation.FlightEvent;
|
||||
import net.sf.openrocket.startup.Application;
|
||||
import net.sf.openrocket.startup.Preferences;
|
||||
@ -171,7 +172,7 @@ public class SimulationPlotPanel extends JPanel {
|
||||
|
||||
//// X axis type:
|
||||
this.add(new JLabel(trans.get("simplotpanel.lbl.Xaxistype")), "spanx, split");
|
||||
domainTypeSelector = new JComboBox<FlightDataType>(types);
|
||||
domainTypeSelector = FlightDataComboBox.createComboBox(FlightDataTypeGroup.ALL_GROUPS, types);
|
||||
domainTypeSelector.setSelectedItem(configuration.getDomainAxisType());
|
||||
domainTypeSelector.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
@ -487,7 +488,7 @@ public class SimulationPlotPanel extends JPanel {
|
||||
private final String[] POSITIONS = { AUTO_NAME, LEFT_NAME, RIGHT_NAME };
|
||||
|
||||
private final int index;
|
||||
private JComboBox<FlightDataType> typeSelector;
|
||||
private final JComboBox<FlightDataType> typeSelector;
|
||||
private UnitSelector unitSelector;
|
||||
private JComboBox<String> axisSelector;
|
||||
|
||||
@ -497,7 +498,7 @@ public class SimulationPlotPanel extends JPanel {
|
||||
|
||||
this.index = plotIndex;
|
||||
|
||||
typeSelector = new JComboBox<FlightDataType>(types);
|
||||
typeSelector = FlightDataComboBox.createComboBox(FlightDataTypeGroup.ALL_GROUPS, types);
|
||||
typeSelector.setSelectedItem(type);
|
||||
typeSelector.addItemListener(new ItemListener() {
|
||||
@Override
|
||||
|
@ -0,0 +1,55 @@
|
||||
package net.sf.openrocket.gui.widgets;
|
||||
|
||||
import javax.swing.JTextField;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
|
||||
/**
|
||||
* A text field that can display a placeholder when empty.
|
||||
*
|
||||
* @author Sibo Van Gool <sibo.vangool@hotmail.com>
|
||||
*/
|
||||
public class PlaceholderTextField extends JTextField {
|
||||
private String placeholder;
|
||||
|
||||
public PlaceholderTextField() { }
|
||||
|
||||
public PlaceholderTextField(final int pColumns) {
|
||||
super(pColumns);
|
||||
}
|
||||
|
||||
public PlaceholderTextField(final String pText) {
|
||||
super(pText);
|
||||
}
|
||||
|
||||
public PlaceholderTextField(final String pText, final int pColumns) {
|
||||
super(pText, pColumns);
|
||||
}
|
||||
|
||||
public String getPlaceholder() {
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics pG) {
|
||||
super.paintComponent(pG);
|
||||
|
||||
if (placeholder == null || placeholder.isEmpty() || !getText().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Graphics2D g = (Graphics2D) pG;
|
||||
g.setRenderingHint(
|
||||
RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g.setColor(getDisabledTextColor());
|
||||
g.drawString(placeholder, getInsets().left, pG.getFontMetrics()
|
||||
.getMaxAscent() + getInsets().top);
|
||||
}
|
||||
|
||||
public void setPlaceholder(final String s) {
|
||||
placeholder = s;
|
||||
}
|
||||
}
|
@ -0,0 +1,481 @@
|
||||
package net.sf.openrocket.gui.widgets;
|
||||
|
||||
import net.sf.openrocket.gui.util.GUIUtil;
|
||||
import net.sf.openrocket.gui.util.UITheme;
|
||||
|
||||
import javax.swing.AbstractListModel;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.DefaultListCellRenderer;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.plaf.basic.BasicArrowButton;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* A combo box that has a search box for searching the items in the combobox.
|
||||
* If no text is entered, the combobox items are displayed in a categorized popup menu, grouped according to their groups.
|
||||
* @param <E> The type of the group
|
||||
* @param <T> The type of the items
|
||||
*/
|
||||
public class SearchableAndCategorizableComboBox<E, T> extends JComboBox<T> {
|
||||
|
||||
private final JPopupMenu categoryPopup;
|
||||
private final JPopupMenu searchPopup;
|
||||
private final PlaceholderTextField searchFieldCategory;
|
||||
private final PlaceholderTextField searchFieldSearch;
|
||||
private final JList<T> filteredList;
|
||||
|
||||
private final T[] allItems;
|
||||
private final Map<E, T[]> itemGroupMap;
|
||||
|
||||
private int highlightedListIdx = -1;
|
||||
|
||||
private static Color textSelectionBackground;
|
||||
|
||||
static {
|
||||
initColors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a searchable and categorizable combo box.
|
||||
* @param itemGroupMap the map of items and their corresponding groups
|
||||
* @param placeHolderText the placeholder text for the search field (when no text is entered)
|
||||
*/
|
||||
public SearchableAndCategorizableComboBox(Map<E, T[]> itemGroupMap, String placeHolderText) {
|
||||
super();
|
||||
setEditable(false);
|
||||
|
||||
this.itemGroupMap = itemGroupMap;
|
||||
this.allItems = extractItemsFromMap(itemGroupMap);
|
||||
setModel(new DefaultComboBoxModel<>(allItems));
|
||||
|
||||
initColors();
|
||||
|
||||
// Create the search field widget
|
||||
searchFieldCategory = new PlaceholderTextField();
|
||||
searchFieldCategory.setPlaceholder(placeHolderText);
|
||||
searchFieldSearch = new PlaceholderTextField();
|
||||
|
||||
// Create the filtered list
|
||||
filteredList = createFilteredList();
|
||||
|
||||
// Create the different popups
|
||||
categoryPopup = createCategoryPopup();
|
||||
searchPopup = createSearchPopup();
|
||||
searchPopup.setPreferredSize(categoryPopup.getPreferredSize());
|
||||
|
||||
// Add key listener for the search fields
|
||||
searchFieldCategory.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
overrideActionKeys(e);
|
||||
}
|
||||
|
||||
public void keyTyped(KeyEvent e) {
|
||||
EventQueue.invokeLater(() -> {
|
||||
String text = searchFieldCategory.getText();
|
||||
highlightedListIdx = 0; // Start with the first item selected
|
||||
searchFieldSearch.setText(text);
|
||||
if (!text.isEmpty() && !searchPopup.isVisible()) {
|
||||
hideCategoryPopup();
|
||||
showSearchPopup();
|
||||
filter(text);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
searchFieldSearch.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
overrideActionKeys(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
EventQueue.invokeLater(() -> {
|
||||
String text = searchFieldSearch.getText();
|
||||
highlightedListIdx = 0; // Start with the first item selected
|
||||
searchFieldCategory.setText(text);
|
||||
if (text.isEmpty() && !categoryPopup.isVisible()) {
|
||||
hideSearchPopup();
|
||||
showCategoryPopup();
|
||||
}
|
||||
filter(text);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Override the mouse listeners to use our custom popup
|
||||
for (MouseListener mouseListener : getMouseListeners()) {
|
||||
removeMouseListener(mouseListener);
|
||||
}
|
||||
|
||||
addMouseListeners();
|
||||
}
|
||||
|
||||
private static void initColors() {
|
||||
updateColors();
|
||||
UITheme.Theme.addUIThemeChangeListener(SearchableAndCategorizableComboBox::updateColors);
|
||||
}
|
||||
|
||||
private static void updateColors() {
|
||||
textSelectionBackground = GUIUtil.getUITheme().getTextSelectionBackgroundColor();
|
||||
}
|
||||
|
||||
private T[] extractItemsFromMap(Map<E, T[]> itemGroupMap) {
|
||||
Set<T> uniqueItems = new HashSet<>(); // Use a Set to ensure uniqueness
|
||||
for (E group : itemGroupMap.keySet()) {
|
||||
uniqueItems.addAll(Arrays.asList(itemGroupMap.get(group)));
|
||||
}
|
||||
ArrayList<T> items = new ArrayList<>(uniqueItems);
|
||||
return items.toArray((T[]) new Object[0]);
|
||||
}
|
||||
|
||||
private JPopupMenu createCategoryPopup() {
|
||||
final JPopupMenu menu = new JPopupMenu();
|
||||
|
||||
// Add the search field at the top
|
||||
menu.add(searchFieldCategory);
|
||||
menu.addSeparator(); // Separator between search field and menu items
|
||||
|
||||
// Fill the menu with the groups
|
||||
for (E group : itemGroupMap.keySet()) {
|
||||
JMenu groupList = new JMenu(group.toString());
|
||||
T[] itemsForGroup = itemGroupMap.get(group);
|
||||
|
||||
if (itemsForGroup != null) {
|
||||
for (T item : itemsForGroup) {
|
||||
JMenuItem itemMenu = new JMenuItem(item.toString());
|
||||
itemMenu.addActionListener(e -> {
|
||||
setSelectedItem(item);
|
||||
});
|
||||
groupList.add(itemMenu);
|
||||
}
|
||||
}
|
||||
|
||||
menu.add(groupList);
|
||||
}
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
private JPopupMenu createSearchPopup() {
|
||||
final JPopupMenu menu = new JPopupMenu();
|
||||
menu.setLayout(new BorderLayout());
|
||||
|
||||
// Add the search field at the top
|
||||
menu.add(searchFieldSearch, BorderLayout.NORTH);
|
||||
menu.addSeparator();
|
||||
|
||||
menu.add(new JScrollPane(filteredList));
|
||||
|
||||
return menu;
|
||||
}
|
||||
|
||||
private JList<T> createFilteredList() {
|
||||
JList<T> list = new JList<>(); // Don't fill the list with the items yet, this will be done during filtering
|
||||
|
||||
list.setCellRenderer(new DefaultListCellRenderer() {
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
T item = (T) value;
|
||||
String itemName = item.toString();
|
||||
|
||||
if (itemName.toLowerCase().contains(searchFieldSearch.getText().toLowerCase())) {
|
||||
// Use HTML to underline matching text
|
||||
itemName = itemName.replaceAll("(?i)(" + searchFieldSearch.getText() + ")", "<u>$1</u>");
|
||||
label.setText("<html>" + itemName + "</html>");
|
||||
}
|
||||
|
||||
// Set the hover color
|
||||
if (highlightedListIdx == index || isSelected) {
|
||||
label.setBackground(textSelectionBackground);
|
||||
label.setOpaque(true);
|
||||
} else {
|
||||
label.setOpaque(false);
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
});
|
||||
|
||||
list.addMouseMotionListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
Point p = new Point(e.getX(),e.getY());
|
||||
int index = list.locationToIndex(p);
|
||||
if (index != highlightedListIdx) {
|
||||
highlightedListIdx = index;
|
||||
list.repaint();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
list.addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
// Check if the event is in the final phase of change
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
selectComboBoxItemFromFilteredList();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private void selectComboBoxItemFromFilteredList() {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
T selectedItem = filteredList.getSelectedValue();
|
||||
if (selectedItem != null) {
|
||||
SearchableAndCategorizableComboBox.this.setSelectedItem(selectedItem);
|
||||
// Hide the popups after selection
|
||||
hideCategoryPopup();
|
||||
hideSearchPopup();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showCategoryPopup() {
|
||||
categoryPopup.show(this, 0, getHeight());
|
||||
searchFieldSearch.setText("");
|
||||
searchFieldCategory.setText("");
|
||||
}
|
||||
|
||||
private void hideCategoryPopup() {
|
||||
categoryPopup.setVisible(false);
|
||||
}
|
||||
|
||||
private void showSearchPopup() {
|
||||
searchPopup.show(this, 0, getHeight());
|
||||
}
|
||||
|
||||
private void hideSearchPopup() {
|
||||
searchPopup.setVisible(false);
|
||||
}
|
||||
|
||||
private void filter(String text) {
|
||||
filteredList.removeAll();
|
||||
String searchText = text.toLowerCase();
|
||||
SortedListModel<T> filteredModel = new SortedListModel<>();
|
||||
|
||||
for (T item : this.allItems) {
|
||||
if (item.toString().toLowerCase().contains(searchText)) {
|
||||
filteredModel.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
filteredList.setModel(filteredModel);
|
||||
filteredList.revalidate();
|
||||
filteredList.repaint();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPopupVisible() {
|
||||
return categoryPopup.isVisible() || searchPopup.isVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default action keys (escape, enter, arrow keys) to do our own actions.
|
||||
* @param e the key event
|
||||
*/
|
||||
private void overrideActionKeys(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
|
||||
hideCategoryPopup();
|
||||
hideSearchPopup();
|
||||
} else if (e.getKeyCode() == KeyEvent.VK_ENTER) {
|
||||
selectHighlightedItemInFilteredList();
|
||||
} else if (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_RIGHT) {
|
||||
highlightNextItemInFilteredList();
|
||||
} else if (e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_LEFT) {
|
||||
highlightPreviousItemInFilteredList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the highlighted item in the filtered list and hide the popups.
|
||||
*/
|
||||
private void selectHighlightedItemInFilteredList() {
|
||||
if (highlightedListIdx >= filteredList.getModel().getSize() || highlightedListIdx < 0 || !searchPopup.isVisible()) {
|
||||
return;
|
||||
}
|
||||
filteredList.setSelectedIndex(highlightedListIdx);
|
||||
selectComboBoxItemFromFilteredList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight the next item in the filtered list.
|
||||
*/
|
||||
private void highlightNextItemInFilteredList() {
|
||||
if (highlightedListIdx + 1 >= filteredList.getModel().getSize() || !searchPopup.isVisible()) {
|
||||
return;
|
||||
}
|
||||
highlightedListIdx++;
|
||||
filteredList.ensureIndexIsVisible(highlightedListIdx);
|
||||
filteredList.repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight the previous item in the filtered list.
|
||||
*/
|
||||
private void highlightPreviousItemInFilteredList() {
|
||||
if (highlightedListIdx <= 0 || !searchPopup.isVisible()) {
|
||||
return;
|
||||
}
|
||||
highlightedListIdx--;
|
||||
filteredList.ensureIndexIsVisible(highlightedListIdx);
|
||||
filteredList.repaint();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add mouse listener to widgets of the combobox to open our custom popup menu.
|
||||
*/
|
||||
private void addMouseListeners() {
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isPopupVisible()) {
|
||||
showCategoryPopup();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Component arrowButton = getArrowButton();
|
||||
if (arrowButton != null) {
|
||||
for (MouseListener mouseListener : arrowButton.getMouseListeners()) {
|
||||
arrowButton.removeMouseListener(mouseListener);
|
||||
}
|
||||
arrowButton.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isPopupVisible()) {
|
||||
showCategoryPopup();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static class SortedListModel<T> extends AbstractListModel<T> {
|
||||
private final SortedSet<T> model;
|
||||
|
||||
public SortedListModel() {
|
||||
Comparator<T> alphabeticalComparator = new Comparator<T>() {
|
||||
@Override
|
||||
public int compare(T o1, T o2) {
|
||||
return o1.toString().compareToIgnoreCase(o2.toString());
|
||||
}
|
||||
};
|
||||
|
||||
model = new TreeSet<>(alphabeticalComparator);
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return model.size();
|
||||
}
|
||||
|
||||
public T getElementAt(int index) {
|
||||
return (T) model.toArray()[index];
|
||||
}
|
||||
|
||||
public void add(T element) {
|
||||
if (model.add(element)) {
|
||||
fireContentsChanged(this, 0, getSize());
|
||||
}
|
||||
}
|
||||
public void addAll(T[] elements) {
|
||||
Collection<T> c = Arrays.asList(elements);
|
||||
model.addAll(c);
|
||||
fireContentsChanged(this, 0, getSize());
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
model.clear();
|
||||
fireContentsChanged(this, 0, getSize());
|
||||
}
|
||||
|
||||
public boolean contains(T element) {
|
||||
return model.contains(element);
|
||||
}
|
||||
|
||||
public T firstElement() {
|
||||
return model.first();
|
||||
}
|
||||
|
||||
public Iterator<T> iterator() {
|
||||
return model.iterator();
|
||||
}
|
||||
|
||||
public T lastElement() {
|
||||
return model.last();
|
||||
}
|
||||
|
||||
public boolean removeElement(T element) {
|
||||
boolean removed = model.remove(element);
|
||||
if (removed) {
|
||||
fireContentsChanged(this, 0, getSize());
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user