[#2456] Add dedicated warnings column in sim table

This commit is contained in:
SiboVG 2024-02-15 03:21:16 +01:00
parent af75857934
commit 7d23bb58ee
8 changed files with 205 additions and 79 deletions

View File

@ -614,6 +614,7 @@ simpanel.dlg.lbl.DeleteSim1 = Delete the selected simulations?
simpanel.dlg.lbl.DeleteSim2 = <html><i>This operation cannot be undone.</i>
simpanel.dlg.lbl.DeleteSim3 = Delete simulations
simpanel.col.Status = Status
simpanel.col.Warnings = Warnings
simpanel.col.Name = Name
simpanel.col.Motors = Motors
simpanel.col.Configuration = Configuration

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

View File

@ -116,6 +116,20 @@ public abstract class MessageSet<E extends Message> extends AbstractSet<E> imple
return messages.size();
}
/**
* Returns the number of messages with the specified priority.
* @param priority the priority
* @return the number of messages with the specified priority.
*/
public int getNrOfMessagesWithPriority(MessagePriority priority) {
int count = 0;
for (E m : messages) {
if (m.getPriority() == priority) {
count++;
}
}
return count;
}
public void immute() {
mutable.immute();

View File

@ -27,6 +27,18 @@ public class WarningSet extends MessageSet<Warning> {
return add(Warning.fromString(s));
}
public int getNrOfCriticalWarnings() {
return getNrOfMessagesWithPriority(MessagePriority.HIGH);
}
public int getNrOfNormalWarnings() {
return getNrOfMessagesWithPriority(MessagePriority.NORMAL);
}
public int getNrOfInformativeWarnings() {
return getNrOfMessagesWithPriority(MessagePriority.LOW);
}
@Override
public WarningSet clone() {
try {

View File

@ -1,7 +1,7 @@
package net.sf.openrocket.gui.main;
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
@ -21,13 +21,14 @@ import java.io.Serial;
import java.util.Comparator;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
@ -94,13 +95,6 @@ public class SimulationPanel extends JPanel {
private static final Translator trans = Application.getTranslator();
private static final Color WARNING_COLOR = Color.RED;
private static final String WARNING_TEXT = "\uFF01"; // Fullwidth exclamation mark
private static final Color OK_COLOR = new Color(60, 150, 0);
private static final String OK_TEXT = "\u2714"; // Heavy check mark
private RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class);
@ -195,6 +189,7 @@ public class SimulationPanel extends JPanel {
simulationTable.setRowSorter(simulationTableSorter);
simulationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer());
simulationTable.setDefaultRenderer(WarningsBox.class, new WarningsBoxRenderer());
simulationTableModel.setColumnWidths(simulationTable.getColumnModel());
simulationTable.setFillsViewportHeight(true);
@ -239,6 +234,7 @@ public class SimulationPanel extends JPanel {
if (e.getButton() == MouseEvent.BUTTON1) {
if (e.getClickCount() == 1) {
// Rerun the simulation
if (column == 0) {
int selected = simulationTable.convertRowIndexToModel(selectedRow);
Simulation sim = document.getSimulations().get(selected);
@ -248,14 +244,15 @@ public class SimulationPanel extends JPanel {
runSimulation();
}
}
} else if (e.getClickCount() == 2) {
int selected = simulationTable.convertRowIndexToModel(selectedRow);
// Show the warnings for the simulation
if (column == 0) {
else if (column == 1) {
int selected = simulationTable.convertRowIndexToModel(selectedRow);
SimulationWarningDialog.showWarningDialog(SimulationPanel.this, document.getSimulations().get(selected));
}
} else if (e.getClickCount() == 2) {
int selected = simulationTable.convertRowIndexToModel(selectedRow);
// Edit the simulation or plot/export
else {
if (column > 1) {
simulationTable.clearSelection();
simulationTable.addRowSelectionInterval(selectedRow, selectedRow);
@ -756,67 +753,83 @@ public class SimulationPanel extends JPanel {
return simulationTable.getSelectionModel();
}
private String getSimulationToolTip(Simulation sim, boolean includeSimName) {
String tip;
private static String getSimulationStatusToolTip(Simulation sim, boolean includeSimName) {
StringBuilder tip;
FlightData data = sim.getSimulatedData();
tip = "<html>";
tip = new StringBuilder("<html>");
if (includeSimName) {
tip += "<b>" + sim.getName() + "</b><br>";
tip.append("<b>").append(sim.getName()).append("</b><br>");
}
switch (sim.getStatus()) {
case CANT_RUN:
tip += trans.get("simpanel.ttip.noData")+"<br>";
tip.append(trans.get("simpanel.ttip.noData")).append("<br>");
break;
case LOADED:
tip += trans.get("simpanel.ttip.loaded") + "<br>";
tip.append(trans.get("simpanel.ttip.loaded")).append("<br>");
break;
case UPTODATE:
tip += trans.get("simpanel.ttip.uptodate") + "<br>";
tip.append(trans.get("simpanel.ttip.uptodate")).append("<br>");
break;
case OUTDATED:
tip += trans.get("simpanel.ttip.outdated") + "<br>";
tip.append(trans.get("simpanel.ttip.outdated")).append("<br>");
break;
case EXTERNAL:
tip += trans.get("simpanel.ttip.external") + "<br>";
return tip;
tip.append(trans.get("simpanel.ttip.external")).append("<br>");
return tip.toString();
case NOT_SIMULATED:
tip += trans.get("simpanel.ttip.notSimulated");
return tip;
tip.append(trans.get("simpanel.ttip.notSimulated"));
return tip.toString();
}
if (data == null) {
tip += trans.get("simpanel.ttip.noData");
return tip;
tip.append(trans.get("simpanel.ttip.noData"));
return tip.toString();
}
for (int b = 0; b < data.getBranchCount(); b++) {
FlightEvent abortEvent = data.getBranch(b).getFirstEvent(FlightEvent.Type.SIM_ABORT);
if ( abortEvent != null) {
tip += "<font color=\"red\"><i><b>" + trans.get("simpanel.ttip.simAbort") + ":</b></i> " +
((SimulationAbort)(abortEvent.getData())).toString() + "</font><br />";
if (abortEvent != null) {
tip.append("<font color=\"red\"><i><b>").append(trans.get("simpanel.ttip.simAbort")).append(":</b></i> ").append((abortEvent.getData()).toString()).append("</font><br />");
}
}
WarningSet warnings = data.getWarningSet();
if (warnings.isEmpty()) {
tip += trans.get("simpanel.ttip.noWarnings");
return tip;
}
tip += trans.get("simpanel.ttip.warnings");
for (Warning w : warnings) {
tip += "<br>" + w.toString();
}
return tip;
return tip.toString();
}
private String getSimulationToolTip(Simulation sim) {
return getSimulationToolTip(sim, true);
private static String getSimulationWarningsToolTip(Simulation sim, boolean includeSimName) {
StringBuilder tip;
FlightData data = sim.getSimulatedData();
tip = new StringBuilder("<html>");
if (includeSimName) {
tip.append("<b>").append(sim.getName()).append("</b><br>");
}
if (data == null) {
tip.append(trans.get("simpanel.ttip.noData"));
return tip.toString();
}
WarningSet warnings = data.getWarningSet();
if (warnings.isEmpty()) {
tip.append(trans.get("simpanel.ttip.noWarnings"));
return tip.toString();
}
tip.append(trans.get("simpanel.ttip.warnings"));
for (Warning w : warnings) {
tip.append("<br>").append(w.toString());
}
return tip.toString();
}
private String getSimulationStatusToolTip(Simulation sim) {
return getSimulationStatusToolTip(sim, true);
}
private void openDialog(boolean plotMode, boolean isNewSimulation, final Simulation... sims) {
@ -1138,7 +1151,7 @@ public class SimulationPanel extends JPanel {
label.setBackground(table.getBackground());
label.setOpaque(true);
label.setToolTipText(getSimulationToolTip(document.getSimulation(row)));
label.setToolTipText(getSimulationStatusToolTip(document.getSimulation(row)));
return label;
}
@ -1146,7 +1159,7 @@ public class SimulationPanel extends JPanel {
isSelected, hasFocus, row, column);
if (component instanceof JComponent) {
((JComponent) component).setToolTipText(getSimulationToolTip(
((JComponent) component).setToolTipText(getSimulationStatusToolTip(
document.getSimulation(row)));
}
return component;
@ -1167,17 +1180,97 @@ public class SimulationPanel extends JPanel {
@Override
public String toString() {
String text = getSimulationToolTip(simulation, false);
String text = getSimulationStatusToolTip(simulation, false);
return text.replace("<br>", "-").replaceAll("<[^>]*>","");
}
}
private static class WarningsBox extends Box {
private Simulation simulation;
public WarningsBox(Simulation simulation) {
super(BoxLayout.X_AXIS); // Horizontal box
this.simulation = simulation;
updateContent();
}
public void replaceSimulation(Simulation simulation) {
this.simulation = simulation;
updateContent();
}
private void updateContent() {
removeAll(); // Clear existing content before update
setToolTipText("");
if (simulation == null) {
revalidate();
return;
}
// Update the tooltip text
String ttip = getSimulationWarningsToolTip(simulation, true);
setToolTipText(ttip);
WarningSet warnings = simulation.getSimulatedWarnings();
if (warnings == null || warnings.isEmpty()) {
revalidate();
return;
}
int nrOfCriticalWarnings = warnings.getNrOfCriticalWarnings();
int nrOfNormalWarnings = warnings.getNrOfNormalWarnings();
int nrOfInfoWarnings = warnings.getNrOfInformativeWarnings();
if (nrOfCriticalWarnings > 0) {
add(new JLabel(nrOfCriticalWarnings + " "));
add(new JLabel(Icons.WARNING_HIGH));
}
if (nrOfCriticalWarnings > 0 && nrOfNormalWarnings > 0) {
add(new JLabel(", "));
}
if (nrOfNormalWarnings > 0) {
add(new JLabel(nrOfNormalWarnings + " "));
add(new JLabel(Icons.WARNING_NORMAL));
}
if ((nrOfCriticalWarnings > 0 || nrOfNormalWarnings > 0) && nrOfInfoWarnings > 0) {
add(new JLabel(", "));
}
if (nrOfInfoWarnings > 0) {
add(new JLabel(nrOfInfoWarnings + " "));
add(new JLabel(Icons.WARNING_LOW));
}
revalidate(); // Notify layout manager of changes
}
}
public static class WarningsBoxRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if (value instanceof WarningsBox box) {
// Wrap the box in a panel with BorderLayout to allow alignment
JPanel panel = new JPanel(new BorderLayout());
panel.setToolTipText(box.getToolTipText());
panel.add(box, BorderLayout.EAST); // Align to the right within the panel
return panel;
}
return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
private class SimulationTableModel extends ColumnTableModel {
private static final long serialVersionUID = 8686456963492628476L;
public SimulationTableModel() {
super(
//// Status and warning column
//// Status column
new Column("") {
private StatusLabel label = null;
@ -1201,31 +1294,12 @@ public class SimulationPanel extends JPanel {
Simulation.Status status = simulation.getStatus();
label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
// Set warning marker
if (status == Simulation.Status.NOT_SIMULATED ||
status == Simulation.Status.EXTERNAL) {
label.setText("");
} else {
WarningSet w = document.getSimulation(row).getSimulatedWarnings();
if (w == null) {
label.setText("");
} else if (w.isEmpty()) {
label.setForeground(OK_COLOR);
label.setText(OK_TEXT);
} else {
label.setForeground(WARNING_COLOR);
label.setText(WARNING_TEXT);
}
}
return label;
}
@Override
public int getExactWidth() {
return 36;
return 26;
}
@Override
@ -1234,6 +1308,38 @@ public class SimulationPanel extends JPanel {
}
},
//// Warnings column
new Column(trans.get("simpanel.col.Warnings")) {
private WarningsBox box = null;
@Override
public Object getValueAt(int row) {
if (row < 0 || row >= document.getSimulationCount())
return null;
Simulation simulation = document.getSimulation(row);
// Initialize the box
if (box == null) {
box = new WarningsBox(simulation);
} else {
box.replaceSimulation(simulation);
}
return box;
}
@Override
public int getDefaultWidth() {
return 70;
}
@Override
public Class<?> getColumnClass() {
return WarningsBox.class;
}
},
//// Simulation name
//// Name
new Column(trans.get("simpanel.col.Name")) {

View File

@ -99,6 +99,10 @@ public class Icons {
public static final Icon NOT_FAVORITE = loadImageIcon("pix/icons/star_silver.png", "Not favorite");
public static final Icon FAVORITE = loadImageIcon("pix/icons/star_gold.png", "Favorite");
public static final Icon WARNING_LOW = loadImageIcon("pix/icons/warning_low.png", "Informative Warning");
public static final Icon WARNING_NORMAL = loadImageIcon("pix/icons/warning_normal.png", "Warning");
public static final Icon WARNING_HIGH = loadImageIcon("pix/icons/warning_high.png", "Critical Warning");
public static final Icon MASS_OVERRIDE_LIGHT = loadImageIcon("pix/icons/mass-override_light.png", "Mass Override");
public static final Icon MASS_OVERRIDE_DARK = loadImageIcon("pix/icons/mass-override_dark.png", "Mass Override");
public static final Icon MASS_OVERRIDE_SUBCOMPONENT_LIGHT = loadImageIcon("pix/icons/mass-override-subcomponent_light.png", "Mass Override Subcomponent");
@ -143,17 +147,6 @@ public class Icons {
return new ImageIcon(url, name);
}
/**
* Loads an ImageIcon with a new name.
*
* @param icon the original ImageIcon to load.
* @param newName the new name for the ImageIcon.
* @return the loaded ImageIcon with the new name.
*/
public static ImageIcon loadImageIconWithNewName(ImageIcon icon, String newName) {
return new ImageIcon(icon.getImage(), newName);
}
/**
* Scales an ImageIcon to the specified scale.
* @param icon icon to scale