From ddbcf597c2dae10660415852d9ec490eadac0482 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 15 Jun 2022 12:48:23 +0200 Subject: [PATCH 01/10] Extend multi-select components to different-typed components --- core/resources/l10n/messages.properties | 5 +- core/resources/l10n/messages_cs.properties | 1 - core/resources/l10n/messages_de.properties | 1 - core/resources/l10n/messages_es.properties | 3 +- core/resources/l10n/messages_fr.properties | 1 - core/resources/l10n/messages_it.properties | 1 - core/resources/l10n/messages_ja.properties | 1 - core/resources/l10n/messages_nl.properties | 1 - core/resources/l10n/messages_pl.properties | 1 - core/resources/l10n/messages_ru.properties | 1 - core/resources/l10n/messages_uk_UA.properties | 1 - .../rocketcomponent/RocketComponent.java | 20 ++++- .../gui/components/StyledLabel.java | 9 +- .../configdialog/ComponentConfigDialog.java | 84 +++++++++--------- .../gui/configdialog/FinSetConfig.java | 9 +- .../configdialog/RocketComponentConfig.java | 86 +++++++++++++++---- .../sf/openrocket/gui/main/BasicFrame.java | 13 ++- .../sf/openrocket/gui/main/RocketActions.java | 34 ++------ 18 files changed, 152 insertions(+), 120 deletions(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 0a33eaaef..9a7f539f4 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -947,7 +947,10 @@ CenteringRingCfg.tab.Generalproperties = General properties !ComponentConfigDialog ComponentCfgDlg.configuration = configuration -ComponentCfgDlg.configuration1 = +ComponentCfgDlg.MultiComponent = Multi-component +ComponentCfgDlg.MultiComponentConfig = Multi-component configuration +ComponentCfgDlg.MultiComponentEdit = Multi-component edit +ComponentCfgDlg.MultiComponentEdit.ttip = You are editing the following components:
ComponentCfgDlg.Modify = Modify !StageConfig diff --git a/core/resources/l10n/messages_cs.properties b/core/resources/l10n/messages_cs.properties index c6354d4a8..f5b5933f7 100644 --- a/core/resources/l10n/messages_cs.properties +++ b/core/resources/l10n/messages_cs.properties @@ -678,7 +678,6 @@ CenteringRingCfg.tab.Generalproperties = Obecn !ComponentConfigDialog ComponentCfgDlg.configuration = konfigurace -ComponentCfgDlg.configuration1 = ComponentCfgDlg.Modify = Uprav !StageConfig diff --git a/core/resources/l10n/messages_de.properties b/core/resources/l10n/messages_de.properties index 442081510..45f91eda5 100644 --- a/core/resources/l10n/messages_de.properties +++ b/core/resources/l10n/messages_de.properties @@ -734,7 +734,6 @@ CenteringRingCfg.tab.Generalproperties = Allgemeine Eigenschaften !ComponentConfigDialog ComponentCfgDlg.configuration = Konfiguration -ComponentCfgDlg.configuration1 = ComponentCfgDlg.Modify = Verändern !StageConfig diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties index 9d0e5b2c3..255e26369 100644 --- a/core/resources/l10n/messages_es.properties +++ b/core/resources/l10n/messages_es.properties @@ -130,8 +130,7 @@ CompassSelectionButton.lbl.W = O ComponentCfgDlg.Modify = Modificar !ComponentConfigDialog -ComponentCfgDlg.configuration = -ComponentCfgDlg.configuration1 = Configuraci\u00f3n +ComponentCfgDlg.configuration = Configuraci\u00f3n ComponentIcons.Bodytube = Cuerpo tubular ComponentIcons.Bulkhead = Disco de enganche diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties index ab7fb5544..d4cee80d0 100644 --- a/core/resources/l10n/messages_fr.properties +++ b/core/resources/l10n/messages_fr.properties @@ -121,7 +121,6 @@ CompassSelectionButton.lbl.W = O ComponentCfgDlg.Modify = Modifier !ComponentConfigDialog ComponentCfgDlg.configuration = configuration -ComponentCfgDlg.configuration1 = configuration ComponentIcons.Bodytube = Tube ComponentIcons.Bulkhead = Cloison diff --git a/core/resources/l10n/messages_it.properties b/core/resources/l10n/messages_it.properties index ef7a5d226..f99e43eb3 100644 --- a/core/resources/l10n/messages_it.properties +++ b/core/resources/l10n/messages_it.properties @@ -736,7 +736,6 @@ CenteringRingCfg.tab.Generalproperties = Proprieta' generali !ComponentConfigDialog ComponentCfgDlg.configuration = (configurazione) -ComponentCfgDlg.configuration1 = ComponentCfgDlg.Modify = Modifica !StageConfig diff --git a/core/resources/l10n/messages_ja.properties b/core/resources/l10n/messages_ja.properties index 8cf8b6b03..2417dd0c0 100644 --- a/core/resources/l10n/messages_ja.properties +++ b/core/resources/l10n/messages_ja.properties @@ -766,7 +766,6 @@ CenteringRingCfg.tab.Generalproperties = \u4E00\u822C !ComponentConfigDialog ComponentCfgDlg.configuration = \u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3 -ComponentCfgDlg.configuration1 = ComponentCfgDlg.Modify = \u5909\u66F4 !StageConfig diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties index 0b7e783f8..b32a8d0a5 100644 --- a/core/resources/l10n/messages_nl.properties +++ b/core/resources/l10n/messages_nl.properties @@ -889,7 +889,6 @@ CenteringRingCfg.tab.Generalproperties = Algemene eigenschappen !ComponentConfigDialog ComponentCfgDlg.configuration = configuratie -ComponentCfgDlg.configuration1 = ComponentCfgDlg.Modify = Wijzigen !StageConfig diff --git a/core/resources/l10n/messages_pl.properties b/core/resources/l10n/messages_pl.properties index 05127de1c..08bcaec77 100644 --- a/core/resources/l10n/messages_pl.properties +++ b/core/resources/l10n/messages_pl.properties @@ -680,7 +680,6 @@ update.dlg.latestVersion = Korzystasz z najnowszej wersji OpenRocket: %s. !ComponentConfigDialog ComponentCfgDlg.configuration = konfiguracja - ComponentCfgDlg.configuration1 = ComponentCfgDlg.Modify = Zmodyfikuj !StageConfig diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties index 5fc2e1149..6f8657f59 100644 --- a/core/resources/l10n/messages_ru.properties +++ b/core/resources/l10n/messages_ru.properties @@ -949,7 +949,6 @@ CenteringRingCfg.tab.Generalproperties = \u041E\u0441\u043D\u043E\u0432\u043D\u0 !ComponentConfigDialog ComponentCfgDlg.configuration = \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B -ComponentCfgDlg.configuration1 = ComponentCfgDlg.Modify = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C !StageConfig diff --git a/core/resources/l10n/messages_uk_UA.properties b/core/resources/l10n/messages_uk_UA.properties index 8a06a77ef..5d7147134 100644 --- a/core/resources/l10n/messages_uk_UA.properties +++ b/core/resources/l10n/messages_uk_UA.properties @@ -838,7 +838,6 @@ CenteringRingCfg.tab.Generalproperties = General properties !ComponentConfigDialog ComponentCfgDlg.configuration = configuration -ComponentCfgDlg.configuration1 = ComponentCfgDlg.Modify = Modify !StageConfig diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index f03862f0a..19ee93526 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -1671,6 +1671,24 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } return false; } + + /** + * Checks whether all components in the list have the same class as this component. + * @param components list to check + * @return true if all components are of the same class, false if not + */ + public boolean checkAllClassesEqual(List components) { + if (components == null || components.size() == 0) { + return true; + } + Class myClass = this.getClass(); + for (RocketComponent c : components) { + if (!c.getClass().equals(myClass)) { + return false; + } + } + return true; + } /** * Get the root component of the component tree. @@ -1970,7 +1988,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * @return true if listener was successfully added, false if not */ public boolean addConfigListener(RocketComponent listener) { - if (listener == null || !this.getClass().equals(listener.getClass())) { + if (listener == null) { return false; } configListeners.add(listener); diff --git a/swing/src/net/sf/openrocket/gui/components/StyledLabel.java b/swing/src/net/sf/openrocket/gui/components/StyledLabel.java index 70328411b..bf2a29314 100644 --- a/swing/src/net/sf/openrocket/gui/components/StyledLabel.java +++ b/swing/src/net/sf/openrocket/gui/components/StyledLabel.java @@ -1,5 +1,6 @@ package net.sf.openrocket.gui.components; +import java.awt.Color; import java.awt.Font; import javax.swing.JLabel; @@ -87,11 +88,9 @@ public class StyledLabel extends JLabel { private void checkPreferredSize(float size, Style style) { String str = this.getText(); - if (str.startsWith("") && str.indexOf("") && !str.contains(" listeners) { + private ComponentConfigDialog(Window parent, OpenRocketDocument document, RocketComponent component) { super(parent); this.parent = parent; @@ -63,21 +64,15 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis configurator.invalidate(); document.getRocket().removeComponentChangeListener(ComponentConfigDialog.this); ComponentConfigDialog.this.dispose(); - component.clearConfigListeners(); + if (clearConfigListeners) { + component.clearConfigListeners(); + } } - - public void windowClosing(WindowEvent e){} @Override public void windowOpened(WindowEvent e) { super.windowOpened(e); - // Add config listeners - component.clearConfigListeners(); - if (listeners != null) { - for (RocketComponent listener : listeners) { - component.addConfigListener(listener); - } - } + clearConfigListeners = true; } }); } @@ -104,7 +99,17 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis configurator.updateFields(); //// configuration - setTitle(trans.get("ComponentCfgDlg.configuration1") + " " + component.getComponentName() + " " + trans.get("ComponentCfgDlg.configuration")); + List listeners = component.getConfigListeners(); + if (component.checkAllClassesEqual(listeners)) { + if (listeners != null && listeners.size() > 0) { + setTitle("(" + trans.get("ComponentCfgDlg.MultiComponent") + ") " + + component.getComponentName() + " " + trans.get("ComponentCfgDlg.configuration")); + } else { + setTitle(component.getComponentName() + " " + trans.get("ComponentCfgDlg.configuration")); + } + } else { + setTitle(trans.get("ComponentCfgDlg.MultiComponentConfig")); + } this.pack(); } @@ -118,14 +123,18 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis * Return the configurator panel of the current component. */ private RocketComponentConfig getDialogContents() { - Constructor c = + List listeners = component.getConfigListeners(); + boolean isSameClass = component.checkAllClassesEqual(listeners); + if (!isSameClass) { + return new RocketComponentConfig(document, component); + } + + Constructor constructor = findDialogContentsConstructor(component); - if (c != null) { + if (constructor != null) { try { - return c.newInstance(document, component); - } catch (InstantiationException e) { - throw new BugException("BUG in constructor reflection", e); - } catch (IllegalAccessException e) { + return constructor.newInstance(document, component); + } catch (InstantiationException | IllegalAccessException e) { throw new BugException("BUG in constructor reflection", e); } catch (InvocationTargetException e) { throw Reflection.handleWrappedException(e); @@ -196,48 +205,35 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis ////////// Static dialog ///////// - + /** * A singleton configuration dialog. Will create and show a new dialog if one has not * previously been used, or update the dialog and show it if a previous one exists. * * @param document the document to configure. * @param component the component to configure. - * @param listeners config listeners for the component */ public static void showDialog(Window parent, OpenRocketDocument document, - RocketComponent component, List listeners) { - if (dialog != null) + RocketComponent component) { + if (dialog != null) { + // If the component is the same as the ComponentConfigDialog component, and the dialog is still visible, + // that means that the user did a ctr/cmd click on a new component => don't remove the config listeners of component + if (component.equals(ComponentConfigDialog.component)) { + ComponentConfigDialog.clearConfigListeners = false; + } dialog.dispose(); + } - dialog = new ComponentConfigDialog(parent, document, component, listeners); + dialog = new ComponentConfigDialog(parent, document, component); dialog.setVisible(true); ////Modify document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName()); } - /** - * A singleton configuration dialog. Will create and show a new dialog if one has not - * previously been used, or update the dialog and show it if a previous one exists. - * - * @param document the document to configure. - * @param component the component to configure. - */ - public static void showDialog(Window parent, OpenRocketDocument document, - RocketComponent component) { - ComponentConfigDialog.showDialog(parent, document, component, null); - } - - - /* package */ - static void showDialog(RocketComponent component, List listeners) { - showDialog(dialog.parent, dialog.document, component, listeners); - } - /* package */ static void showDialog(RocketComponent component) { - ComponentConfigDialog.showDialog(component, null); + ComponentConfigDialog.showDialog(dialog.parent, dialog.document, component); } /** diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java index 06dee6dc5..e5470185e 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FinSetConfig.java @@ -82,17 +82,10 @@ public abstract class FinSetConfig extends RocketComponentConfig { //// Convert fin set document.addUndoPosition(trans.get("FinSetConfig.Convertfinset")); - List listeners = new ArrayList<>(); - for (RocketComponent listener : component.getConfigListeners()) { - if (listener instanceof FinSet) { - listeners.add(FreeformFinSet.convertFinSet((FinSet) listener)); - } - } - RocketComponent freeform = FreeformFinSet.convertFinSet((FinSet) component); - ComponentConfigDialog.showDialog(freeform, listeners); + ComponentConfigDialog.showDialog(freeform); } }); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index 12ce67e1f..8185cde58 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -1,6 +1,7 @@ package net.sf.openrocket.gui.configdialog; +import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.event.*; @@ -39,7 +40,6 @@ import net.sf.openrocket.gui.widgets.SelectColorButton; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.material.Material; import net.sf.openrocket.preset.ComponentPreset; -import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.*; import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; import net.sf.openrocket.rocketcomponent.position.AxialMethod; @@ -68,20 +68,39 @@ public class RocketComponentConfig extends JPanel { private JPanel buttonPanel; private JLabel infoLabel; - - + private StyledLabel multiCompEditLabel; + + private boolean allSameType; // Checks whether all listener components are of the same type as + private boolean allMassive; // Checks whether all listener components, and this component, are massive + public RocketComponentConfig(OpenRocketDocument document, RocketComponent component) { setLayout(new MigLayout("fill, gap 4!, ins panel", "[]:5[]", "[growprio 5]5![fill, grow, growprio 500]5![growprio 5]")); this.document = document; this.component = component; - + + // Check the listeners for the same type and massive status + allSameType = true; + allMassive = component.isMassive(); + List listeners = component.getConfigListeners(); + if (listeners != null && listeners.size() > 0) { + allSameType = component.checkAllClassesEqual(listeners); + if (allMassive) { // Only check if is already massive + for (RocketComponent listener : listeners) { + if (!listener.isMassive()) { + allMassive = false; + break; + } + } + } + } + //// Component name: JLabel label = new JLabel(trans.get("RocketCompCfg.lbl.Componentname")); //// The component name. label.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname")); this.add(label, "spanx, height 32!, split"); - + componentNameField = new JTextField(15); textFieldListener = new TextFieldListener(); componentNameField.addActionListener(textFieldListener); @@ -89,34 +108,38 @@ public class RocketComponentConfig extends JPanel { //// The component name. componentNameField.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname")); this.add(componentNameField, "growx"); - - if (component.getPresetType() != null) { + + if (allSameType && component.getPresetType() != null) { // If the component supports a preset, show the preset selection box. presetModel = new PresetModel(this, document, component); presetComboBox = new JComboBox(presetModel); presetComboBox.setEditable(false); this.add(presetComboBox, ""); } - - + tabbedPane = new JTabbedPane(); this.add(tabbedPane, "newline, span, growx, growy 100, wrap"); - + //// Override and Mass and CG override options tabbedPane.addTab(trans.get("RocketCompCfg.tab.Override"), null, overrideTab(), trans.get("RocketCompCfg.tab.MassandCGoverride")); - if (component.isMassive()) { + if (allMassive) { //// Appearance options tabbedPane.addTab(trans.get("RocketCompCfg.tab.Appearance"), null, new AppearancePanel(document, component), "Appearance Tool Tip"); } - + //// Comment and Specify a comment for the component tabbedPane.addTab(trans.get("RocketCompCfg.tab.Comment"), null, commentTab(), trans.get("RocketCompCfg.tab.Specifyacomment")); - + + // Set the default tab to 'Appearance' for a different-type multi-comp dialog (this is the most prominent use case) + if (listeners != null && listeners.size() > 0 && !allSameType) { + tabbedPane.setSelectedIndex(1); + } + addButtons(); - + updateFields(); } @@ -128,6 +151,12 @@ public class RocketComponentConfig extends JPanel { buttonPanel = new JPanel(new MigLayout("fillx, ins 5")); + //// Multi-comp edit label + multiCompEditLabel = new StyledLabel(" ", 0, Style.BOLD); + //multiCompEditLabel.setFontColor(new Color(0, 0, 239)); + multiCompEditLabel.setFontColor(new Color(170, 0, 100)); + buttonPanel.add(multiCompEditLabel, "split 2"); + //// Mass: infoLabel = new StyledLabel(" ", -1); buttonPanel.add(infoLabel, "growx"); @@ -159,16 +188,17 @@ public class RocketComponentConfig extends JPanel { public void updateFields() { // Component name componentNameField.setText(component.getName()); - + // Info label StringBuilder sb = new StringBuilder(); - - if (component.getPresetComponent() != null) { + + if (allSameType && component.getPresetComponent() != null) { ComponentPreset preset = component.getPresetComponent(); sb.append(preset.getManufacturer() + " " + preset.getPartNo() + " "); } - - if (component.isMassive()) { + + List listeners = component.getConfigListeners(); + if (allMassive && (listeners == null || listeners.size() == 0)) { // TODO: support aggregate mass display for current component and listeners? sb.append(trans.get("RocketCompCfg.lbl.Componentmass") + " "); sb.append(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit( component.getComponentMass())); @@ -193,6 +223,24 @@ public class RocketComponentConfig extends JPanel { } else { infoLabel.setText(""); } + + // Multi-comp edit label + if (listeners != null && listeners.size() > 0) { + multiCompEditLabel.setText(trans.get("ComponentCfgDlg.MultiComponentEdit")); + + StringBuilder components = new StringBuilder(trans.get("ComponentCfgDlg.MultiComponentEdit.ttip")); + components.append(component.getName()).append(", "); + for (int i = 0; i < listeners.size(); i++) { + if (i < listeners.size() - 1) { + components.append(listeners.get(i).getName()).append(", "); + } else { + components.append(listeners.get(i).getName()); + } + } + multiCompEditLabel.setToolTipText(components.toString()); + } else { + multiCompEditLabel.setText(""); + } } diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 880713cfa..f90605c34 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -333,16 +333,16 @@ public class BasicFrame extends JFrame { if (!ComponentConfigDialog.isDialogVisible()) return; + else + ComponentConfigDialog.disposeDialog(); + RocketComponent c = (RocketComponent) paths[0].getLastPathComponent(); - List listeners = new ArrayList<>(); + c.clearConfigListeners(); for (int i = 1; i < paths.length; i++) { RocketComponent listener = (RocketComponent) paths[i].getLastPathComponent(); - if (listener.getClass().equals(c.getClass())) { - listeners.add((RocketComponent) paths[i].getLastPathComponent()); - } + c.addConfigListener(listener); } - ComponentConfigDialog.showDialog(BasicFrame.this, - BasicFrame.this.document, c, listeners); + ComponentConfigDialog.showDialog(BasicFrame.this, BasicFrame.this.document, c); } }); @@ -1323,7 +1323,6 @@ public class BasicFrame extends JFrame { * * @param worker the OpenFileWorker that loads the file. * @param displayName the file name to display in dialogs. - * @param file the File to set the document to (may be null). * @param parent * @param openRocketConfigDialog if true, will open the configuration dialog of the rocket. This is useful for examples. * @return diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java index e966061ec..3292af843 100644 --- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java +++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java @@ -5,7 +5,6 @@ import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; @@ -879,16 +878,17 @@ public class RocketActions { List components = selectionModel.getSelectedComponents(); Simulation[] sims = selectionModel.getSelectedSimulations(); - if ((components != null) && (components.size() > 0) && checkAllClassesEqual(components)) { - // Do nothing if the config dialog is already visible + if ((components != null) && (components.size() > 0)) { if (ComponentConfigDialog.isDialogVisible()) - return; + ComponentConfigDialog.disposeDialog(); - List listeners = null; + RocketComponent component = components.get(0); if (components.size() > 1) { - listeners = components.subList(1, components.size()); + for (int i = 1; i < components.size(); i++) { + component.addConfigListener(components.get(i)); + } } - ComponentConfigDialog.showDialog(parentFrame, document, components.get(0), listeners); + ComponentConfigDialog.showDialog(parentFrame, document, component); } else if (sims != null && sims.length > 0 && (simulationPanel != null)) { simulationPanel.editSimulation(); } @@ -898,25 +898,7 @@ public class RocketActions { public void clipboardChanged() { List components = selectionModel.getSelectedComponents(); - this.setEnabled(checkAllClassesEqual(components) || isSimulationSelected()); - } - - /** - * Checks whether all components in the list have the same class - * @param components list to check - * @return true if all components are of the same class, false if not - */ - private boolean checkAllClassesEqual(List components) { - if (components == null || components.size() == 0) { - return false; - } - Class myClass = components.get(0).getClass(); - for (int i = 1; i < components.size(); i++) { - if (!components.get(i).getClass().equals(myClass)) { - return false; - } - } - return true; + this.setEnabled((components != null && components.size() > 0) || isSimulationSelected()); } } From 13fa275ff9012545bb1f465a4bfe6c7ff7586346 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 15 Jun 2022 14:33:07 +0200 Subject: [PATCH 02/10] Fix merge conflict --- .../configdialog/ComponentConfigDialog.java | 110 ++++++++++++------ 1 file changed, 73 insertions(+), 37 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java index 0aa9de71a..62217603d 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java @@ -12,6 +12,7 @@ import javax.swing.JDialog; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; @@ -24,7 +25,7 @@ import net.sf.openrocket.util.Reflection; * A dialog that contains the configuration elements of one component. * The contents of the dialog are instantiated from CONFIGDIALOGPACKAGE according * to the current component. - * + * * @author Sampo Niskanen */ @@ -32,25 +33,26 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis private static final long serialVersionUID = 1L; private static final String CONFIGDIALOGPACKAGE = "net.sf.openrocket.gui.configdialog"; private static final String CONFIGDIALOGPOSTFIX = "Config"; - + // Static Value -- This is a singleton value, and we should only have zero or one active at any time private static ComponentConfigDialog dialog = null; - + private OpenRocketDocument document = null; protected static RocketComponent component = null; private RocketComponentConfig configurator = null; - protected static boolean clearConfigListeners = true; - + private static String previousSelectedTab = null; // Name of the previous selected tab + + private final Window parent; private static final Translator trans = Application.getTranslator(); - + private ComponentConfigDialog(Window parent, OpenRocketDocument document, RocketComponent component) { super(parent); this.parent = parent; - + setComponent(document, component); - + GUIUtil.setDisposableDialogOptions(this, null); GUIUtil.rememberWindowPosition(this); @@ -76,12 +78,12 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis } }); } - - + + /** * Set the component being configured. The listening connections of the old configurator * will be removed and the new ones created. - * + * * @param component Component to configure. */ private void setComponent(OpenRocketDocument document, RocketComponent component) { @@ -89,15 +91,18 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis // Remove listeners by setting all applicable models to null GUIUtil.setNullModels(configurator); // null-safe } - + this.document = document; this.component = component; this.document.getRocket().addComponentChangeListener(this); - + configurator = getDialogContents(); this.setContentPane(configurator); configurator.updateFields(); - + + // Set the selected tab + configurator.setSelectedTab(previousSelectedTab); + //// configuration List listeners = component.getConfigListeners(); if (component.checkAllClassesEqual(listeners)) { @@ -110,7 +115,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis } else { setTitle(trans.get("ComponentCfgDlg.MultiComponentConfig")); } - + this.pack(); } @@ -140,19 +145,19 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis throw Reflection.handleWrappedException(e); } } - + // Should never be reached, since RocketComponentConfig should catch all // components without their own configurator. throw new BugException("Unable to find any configurator for " + component); } - + @Override public void componentChanged(ComponentChangeEvent e) { if (e.isTreeChange() || e.isUndoChange()) { - + // Hide dialog in case of tree or undo change disposeDialog(); - + } else { /* * TODO: HIGH: The line below has caused a NullPointerException (without null check) @@ -164,10 +169,10 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis configurator.updateFields(); } } - - + + /** - * Finds the Constructor of the given component's config dialog panel in + * Finds the Constructor of the given component's config dialog panel in * CONFIGDIALOGPACKAGE. */ @SuppressWarnings("unchecked") @@ -175,10 +180,10 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis Class currentclass; String currentclassname; String configclassname; - + Class configclass; Constructor c; - + currentclass = component.getClass(); while ((currentclass != null) && (currentclass != Object.class)) { currentclassname = currentclass.getCanonicalName(); @@ -187,7 +192,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis currentclassname = currentclassname.substring(index + 1); configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname + CONFIGDIALOGPOSTFIX; - + try { configclass = Class.forName(configclassname); c = (Constructor) @@ -195,27 +200,28 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis return c; } catch (Exception ignore) { } - + currentclass = currentclass.getSuperclass(); } return null; } - - + + ////////// Static dialog ///////// /** - * A singleton configuration dialog. Will create and show a new dialog if one has not + * A singleton configuration dialog. Will create and show a new dialog if one has not * previously been used, or update the dialog and show it if a previous one exists. - * + * * @param document the document to configure. * @param component the component to configure. + * @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog */ - public static void showDialog(Window parent, OpenRocketDocument document, - RocketComponent component) { + public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) { if (dialog != null) { + previousSelectedTab = dialog.getSelectedTabName(); // If the component is the same as the ComponentConfigDialog component, and the dialog is still visible, // that means that the user did a ctr/cmd click on a new component => don't remove the config listeners of component if (component.equals(ComponentConfigDialog.component)) { @@ -224,18 +230,40 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis dialog.dispose(); } + final SwingPreferences preferences = (SwingPreferences) Application.getPreferences(); + if (preferences.isAlwaysOpenLeftmostTab() || !rememberPreviousTab) { + previousSelectedTab = null; + } + dialog = new ComponentConfigDialog(parent, document, component); dialog.setVisible(true); - + ////Modify document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName()); } + /** + * A singleton configuration dialog. Will create and show a new dialog if one has not + * previously been used, or update the dialog and show it if a previous one exists. + * By default, the previous tab is remembered. + * + * @param document the document to configure. + * @param component the component to configure. + */ + public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component) { + ComponentConfigDialog.showDialog(parent, document, component, true); + } + + static void showDialog(RocketComponent component, boolean rememberPreviousTab) { + showDialog(dialog.parent, dialog.document, component, rememberPreviousTab); + } + + /* package */ static void showDialog(RocketComponent component) { - ComponentConfigDialog.showDialog(dialog.parent, dialog.document, component); + showDialog(dialog.parent, dialog.document, component, true); } - + /** * Disposes the configuration dialog. May be used even if not currently visible. */ @@ -244,13 +272,21 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis dialog.dispose(); } } - - + + /** * Returns whether the singleton configuration dialog is currently visible or not. */ public static boolean isDialogVisible() { return (dialog != null) && (dialog.isVisible()); } + + public String getSelectedTabName() { + if (configurator != null) { + return configurator.getSelectedTabName(); + } else { + return null; + } + } } From d398d48d5582e38638130f93b4bf32ec4f58c648 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 15 Jun 2022 14:43:06 +0200 Subject: [PATCH 03/10] Don't remember previous tab for stages --- .../configdialog/ComponentConfigDialog.java | 21 ++++++++++++++++--- .../configdialog/RocketComponentConfig.java | 4 ++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java index 62217603d..244675b15 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java @@ -14,6 +14,7 @@ import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -38,7 +39,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis private static ComponentConfigDialog dialog = null; private OpenRocketDocument document = null; - protected static RocketComponent component = null; + protected RocketComponent component = null; private RocketComponentConfig configurator = null; protected static boolean clearConfigListeners = true; private static String previousSelectedTab = null; // Name of the previous selected tab @@ -119,6 +120,10 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis this.pack(); } + public RocketComponent getComponent() { + return component; + } + public static ComponentConfigDialog getDialog() { return dialog; } @@ -221,10 +226,16 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis */ public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) { if (dialog != null) { - previousSelectedTab = dialog.getSelectedTabName(); + // Don't remember the previous tab for stages, because this will leave you in the override tab for + // the next component, which is generally not what you want. + if (dialog.getComponent() instanceof AxialStage && !(component instanceof AxialStage)) { + previousSelectedTab = null; + } else { + previousSelectedTab = dialog.getSelectedTabName(); + } // If the component is the same as the ComponentConfigDialog component, and the dialog is still visible, // that means that the user did a ctr/cmd click on a new component => don't remove the config listeners of component - if (component.equals(ComponentConfigDialog.component)) { + if (component == dialog.getComponent()) { ComponentConfigDialog.clearConfigListeners = false; } dialog.dispose(); @@ -281,6 +292,10 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis return (dialog != null) && (dialog.isVisible()); } + public int getSelectedTabIndex() { + return configurator.getSelectedTabIndex(); + } + public String getSelectedTabName() { if (configurator != null) { return configurator.getSelectedTabName(); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index 8a1900f5f..eaf8965a2 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -317,6 +317,10 @@ public class RocketComponentConfig extends JPanel { return subPanel; } + public int getSelectedTabIndex() { + return tabbedPane.getSelectedIndex(); + } + public String getSelectedTabName() { if (tabbedPane != null) { return tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()); From 4b614ff11a257aa74b2e3728df2e386ca6df714e Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 15 Jun 2022 17:50:46 +0200 Subject: [PATCH 04/10] Fix appearance multi-comp select --- .../appearance/AppearanceBuilder.java | 157 +++++++++++++++--- .../rocketcomponent/RocketComponent.java | 37 ++--- .../gui/configdialog/AppearancePanel.java | 108 ++++++++++-- .../configdialog/ComponentConfigDialog.java | 11 +- .../configdialog/RocketComponentConfig.java | 11 +- 5 files changed, 263 insertions(+), 61 deletions(-) diff --git a/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java index 230c80ebd..c77fea10c 100644 --- a/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java +++ b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java @@ -1,10 +1,14 @@ package net.sf.openrocket.appearance; import net.sf.openrocket.appearance.Decal.EdgeMode; +import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.AbstractChangeSource; import net.sf.openrocket.util.Color; import net.sf.openrocket.util.Coordinate; +import java.util.LinkedHashMap; +import java.util.Map; + /** * Use this class to build an immutable Appearance object in a friendly way. Set * the various values one at a time with the setter methods and then call @@ -28,6 +32,13 @@ public class AppearanceBuilder extends AbstractChangeSource { private Decal.EdgeMode edgeMode; private boolean batch; + + /** + * List of appearance builders that will set their appearance properties to the same as the current appearance + */ + private final Map configListeners = new LinkedHashMap<>(); + // If true, appearance change events will not be fired + private boolean bypassAppearanceChangeEvent = false; /** * Default constructor @@ -59,7 +70,9 @@ public class AppearanceBuilder extends AbstractChangeSource { rotation = 0; image = null; edgeMode = EdgeMode.REPEAT; - fireChangeEvent();//shouldn't this fire change event? + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -88,6 +101,9 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param d The decal */ public void setDecal(Decal d){ + for (AppearanceBuilder listener : configListeners.values()) { + listener.setDecal(d); + } if (d != null) { setOffset(d.getOffset().x, d.getOffset().y); setCenter(d.getCenter().x, d.getCenter().y); @@ -96,7 +112,9 @@ public class AppearanceBuilder extends AbstractChangeSource { setEdgeMode(d.getEdgeMode()); setImage(d.getImage()); } - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -137,9 +155,13 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param paint the new color */ public void setPaint(Color paint) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setPaint(paint); + } this.paint = paint; - fireChangeEvent(); - + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -158,8 +180,13 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param shine the new shine for template */ public void setShine(double shine) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setShine(shine); + } this.shine = shine; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -179,6 +206,9 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param opacity new opacity value expressed in a percentage, where 0 is fully transparent and 1 is fully opaque */ public void setOpacity(double opacity) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setOpacity(opacity); + } if (this.paint == null) { return; } @@ -187,7 +217,9 @@ public class AppearanceBuilder extends AbstractChangeSource { opacity = Math.max(0, Math.min(1, opacity)); this.paint.setAlpha((int) (opacity * 255)); - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -207,8 +239,13 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param offsetU the new offset to be used */ public void setOffsetU(double offsetU) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setOffsetU(offsetU); + } this.offsetU = offsetU; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -227,8 +264,13 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param offsetV the new offset to be used */ public void setOffsetV(double offsetV) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setOffsetV(offsetV); + } this.offsetV = offsetV; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -259,8 +301,13 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param centerU value of axis U for center */ public void setCenterU(double centerU) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setCenterU(centerU); + } this.centerU = centerU; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -276,11 +323,16 @@ public class AppearanceBuilder extends AbstractChangeSource { * set a new value for axis V for center in template * fires change event * - * @param centerU value of axis V for center + * @return value of axis V for center */ public void setCenterV(double centerV) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setCenterV(centerV); + } this.centerV = centerV; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -311,8 +363,13 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param scaleU new value of scalling in axis U */ public void setScaleU(double scaleU) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setScaleU(scaleU); + } this.scaleU = scaleU; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -331,8 +388,13 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param scaleV new value of scalling in axis V */ public void setScaleV(double scaleV) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setScaleV(scaleV); + } this.scaleV = scaleV; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -379,7 +441,7 @@ public class AppearanceBuilder extends AbstractChangeSource { * sets a new value of axis Y for scalling in template * fires change event * - * @param scaleX the new value for axis Y + * @param scaleY the new value for axis Y */ public void setScaleY(double scaleY) { setScaleV(1.0 / scaleY); @@ -401,14 +463,19 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param rotation the new value for rotation in template */ public void setRotation(double rotation) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setRotation(rotation); + } this.rotation = rotation; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** * gets the current image in template * - * @param the current image in template + * @return the current image in template */ public DecalImage getImage() { return image; @@ -421,8 +488,13 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param image the new image to be used as template */ public void setImage(DecalImage image) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setImage(image); + } this.image = image; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -441,8 +513,13 @@ public class AppearanceBuilder extends AbstractChangeSource { * @param edgeMode the new edgeMode to be used */ public void setEdgeMode(Decal.EdgeMode edgeMode) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setEdgeMode(edgeMode); + } this.edgeMode = edgeMode; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } } /** @@ -455,15 +532,55 @@ public class AppearanceBuilder extends AbstractChangeSource { } /** - * function that garantees that chenges event only occurs after all changes are made + * function that guarantees that changes event only occurs after all changes are made * * param r the functor to be executed */ public void batch(Runnable r) { + for (AppearanceBuilder listener : configListeners.values()) { + listener.batch(r); + } batch = true; r.run(); batch = false; - fireChangeEvent(); + if (!bypassAppearanceChangeEvent) { + fireChangeEvent(); + } + } + + /** + * Add a new config listener that will undergo the same configuration changes as this AppearanceBuilder. + * @param component the component to add as a config listener + * @param ab new AppearanceBuilder config listener + * @return true if listener was successfully added, false if not + */ + public boolean addConfigListener(RocketComponent component, AppearanceBuilder ab) { + if (component == null || ab == null) { + return false; + } + configListeners.put(component, ab); + ab.setBypassChangeEvent(true); + return true; + } + + public void removeConfigListener(RocketComponent listener) { + configListeners.remove(listener); + listener.setBypassChangeEvent(false); + } + + public void clearConfigListeners() { + for (AppearanceBuilder listener : configListeners.values()) { + listener.setBypassChangeEvent(false); + } + configListeners.clear(); + } + + public Map getConfigListeners() { + return configListeners; + } + + public void setBypassChangeEvent(boolean newValue) { + this.bypassAppearanceChangeEvent = newValue; } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 19ee93526..4971e1525 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -123,7 +123,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab private Appearance appearance = null; // If true, component change events will not be fired - private boolean ignoreComponentChange = false; + private boolean bypassComponentChangeEvent = false; /** @@ -464,10 +464,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * @param appearance */ public void setAppearance(Appearance appearance) { - for (RocketComponent listener : configListeners) { - listener.setAppearance(appearance); - } - this.appearance = appearance; if (this.appearance != null) { Decal d = this.appearance.getTexture(); @@ -581,9 +577,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ public final void setMassOverridden(boolean o) { for (RocketComponent listener : configListeners) { - listener.setIgnoreComponentChange(false); + listener.setBypassChangeEvent(false); listener.setMassOverridden(o); - listener.setIgnoreComponentChange(false); + listener.setBypassChangeEvent(false); } if (massOverridden == o) { @@ -655,9 +651,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ public final void setCGOverridden(boolean o) { for (RocketComponent listener : configListeners) { - listener.setIgnoreComponentChange(false); + listener.setBypassChangeEvent(false); listener.setCGOverridden(o); - listener.setIgnoreComponentChange(true); + listener.setBypassChangeEvent(true); } if (cgOverridden == o) { @@ -806,9 +802,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ public final void setName(String name) { for (RocketComponent listener : configListeners) { - listener.setIgnoreComponentChange(false); + listener.setBypassChangeEvent(false); listener.setName(name); - listener.setIgnoreComponentChange(true); + listener.setBypassChangeEvent(true); } if (this.name.equals(name)) { @@ -1954,7 +1950,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ protected void fireComponentChangeEvent(ComponentChangeEvent e) { checkState(); - if (parent == null || ignoreComponentChange) { + if (parent == null || bypassComponentChangeEvent) { /* Ignore if root invalid. */ return; } @@ -1973,17 +1969,16 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab fireComponentChangeEvent(new ComponentChangeEvent(this, type)); } - public void setIgnoreComponentChange(boolean newValue) { - this.ignoreComponentChange = newValue; + public void setBypassChangeEvent(boolean newValue) { + this.bypassComponentChangeEvent = newValue; } - public boolean getIgnoreComponentChange() { - return this.ignoreComponentChange; + public boolean getBypassComponentChangeEvent() { + return this.bypassComponentChangeEvent; } /** - * Add a new config listener that will undergo the same configuration changes as this.component. Listener must be - * of the same class as this.component. + * Add a new config listener that will undergo the same configuration changes as this.component. * @param listener new config listener * @return true if listener was successfully added, false if not */ @@ -1992,18 +1987,18 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return false; } configListeners.add(listener); - listener.setIgnoreComponentChange(true); + listener.setBypassChangeEvent(true); return true; } public void removeConfigListener(RocketComponent listener) { configListeners.remove(listener); - listener.setIgnoreComponentChange(false); + listener.setBypassChangeEvent(false); } public void clearConfigListeners() { for (RocketComponent listener : configListeners) { - listener.setIgnoreComponentChange(false); + listener.setBypassChangeEvent(false); } configListeners.clear(); } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java index 352bb55e1..669089883 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java @@ -3,6 +3,9 @@ package net.sf.openrocket.gui.configdialog; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; import java.lang.reflect.Method; import java.util.EventObject; @@ -58,7 +61,7 @@ import net.sf.openrocket.util.LineStyle; import net.sf.openrocket.util.StateChangeListener; import net.sf.openrocket.gui.widgets.SelectColorButton; -public class AppearancePanel extends JPanel { +public class AppearancePanel extends JPanel implements WindowListener { private static final long serialVersionUID = 2709187552673202019L; private static final Translator trans = Application.getTranslator(); @@ -107,6 +110,34 @@ public class AppearancePanel extends JPanel { private static final JColorChooser colorChooser = new JColorChooser(); + @Override + public void windowOpened(WindowEvent e) {} + + @Override + public void windowClosing(WindowEvent e) {} + + @Override + public void windowClosed(WindowEvent e) { + if (ab != null) { + ab.clearConfigListeners(); + } + if (insideAb != null) { + insideAb.clearConfigListeners(); + } + } + + @Override + public void windowIconified(WindowEvent e) {} + + @Override + public void windowDeiconified(WindowEvent e) {} + + @Override + public void windowActivated(WindowEvent e) {} + + @Override + public void windowDeactivated(WindowEvent e) {} + private class ColorActionListener implements ActionListener { private final String valueName; private final Object o; @@ -198,23 +229,44 @@ public class AppearancePanel extends JPanel { previousUserSelectedAppearance = c.getAppearance(); if (previousUserSelectedAppearance == null) { - previousUserSelectedAppearance = new AppearanceBuilder() - .getAppearance(); + previousUserSelectedAppearance = new AppearanceBuilder().getAppearance(); ab = new AppearanceBuilder(defaultAppearance); } else { ab = new AppearanceBuilder(previousUserSelectedAppearance); } + for (RocketComponent listener : c.getConfigListeners()) { + Appearance a = listener.getAppearance(); + AppearanceBuilder appearanceBuilder = new AppearanceBuilder(a); + ab.addConfigListener(listener, appearanceBuilder); + } - if (c instanceof InsideColorComponent) { + // Check if all InsideColorComponent + boolean allInsideColor = c instanceof InsideColorComponent; + if (allInsideColor) { + for (RocketComponent listener : c.getConfigListeners()) { + if (!(listener instanceof InsideColorComponent)) { + allInsideColor = false; + break; + } + } + } + + if (allInsideColor) { previousUserSelectedInsideAppearance = ((InsideColorComponent) c).getInsideColorComponentHandler() .getInsideAppearance(); if (previousUserSelectedInsideAppearance == null) { - previousUserSelectedInsideAppearance = new AppearanceBuilder() - .getAppearance(); + previousUserSelectedInsideAppearance = new AppearanceBuilder().getAppearance(); insideAb = new AppearanceBuilder(defaultAppearance); } else { insideAb = new AppearanceBuilder(previousUserSelectedInsideAppearance); } + + for (RocketComponent listener : c.getConfigListeners()) { + Appearance a = ((InsideColorComponent) listener).getInsideColorComponentHandler() + .getInsideAppearance(); + AppearanceBuilder appearanceBuilder = new AppearanceBuilder(a); + insideAb.addConfigListener(listener, appearanceBuilder); + } } net.sf.openrocket.util.Color figureColor = c.getColor(); @@ -317,7 +369,7 @@ public class AppearancePanel extends JPanel { add(new JSeparator(SwingConstants.HORIZONTAL), "span, wrap, growx"); // Display a tabbed panel for choosing the outside and inside appearance, if the object is of type InsideColorComponent - if (c instanceof InsideColorComponent) { + if (allInsideColor) { InsideColorComponentHandler handler = ((InsideColorComponent)c).getInsideColorComponentHandler(); // Get translator keys @@ -464,13 +516,31 @@ public class AppearancePanel extends JPanel { previousUserSelectedInsideAppearance = (builder == null) ? null : builder.getAppearance(); } + + // Set the listeners' appearance to the default appearance + for (RocketComponent listener : builder.getConfigListeners().keySet()) { + builder.getConfigListeners().get(listener).setAppearance(defaultAppearance); + listener.setAppearance(null); + } + + // Set this component's appearance to the default appearance builder.setAppearance(defaultAppearance); c.setAppearance(null); } else { - if (!insideBuilder) + if (!insideBuilder) { + // Set the listeners' appearance to the previous user selected appearance + for (AppearanceBuilder listener : builder.getConfigListeners().values()) { + listener.setAppearance(previousUserSelectedAppearance); + } builder.setAppearance(previousUserSelectedAppearance); - else + } + else { + // Set the listeners' inside appearance to the previous user selected appearance + for (AppearanceBuilder listener : builder.getConfigListeners().values()) { + listener.setAppearance(previousUserSelectedInsideAppearance); + } builder.setAppearance(previousUserSelectedInsideAppearance); + } } } }); @@ -622,10 +692,24 @@ public class AppearancePanel extends JPanel { opacityModel.stateChanged(null); lastOpacity = builder.getOpacity(); } - if (!insideBuilder) + if (!insideBuilder) { + // Set the listeners' outside appearance + for (RocketComponent listener : builder.getConfigListeners().keySet()) { + listener.setAppearance(builder.getConfigListeners().get(listener).getAppearance()); + } + // Set this component's outside appearance c.setAppearance(builder.getAppearance()); - else - ((InsideColorComponent)c).getInsideColorComponentHandler().setInsideAppearance(builder.getAppearance()); + } + else { + // Set the listeners' inside appearance + for (RocketComponent listener : builder.getConfigListeners().keySet()) { + if (!(listener instanceof InsideColorComponent)) continue; + ((InsideColorComponent) listener).getInsideColorComponentHandler() + .setInsideAppearance(builder.getConfigListeners().get(listener).getAppearance()); + } + // Set this component's inside appearance + ((InsideColorComponent) c).getInsideColorComponentHandler().setInsideAppearance(builder.getAppearance()); + } decalModel.refresh(); } }); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java index 244675b15..8b88185a3 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java @@ -101,11 +101,16 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis this.setContentPane(configurator); configurator.updateFields(); - // Set the selected tab - configurator.setSelectedTab(previousSelectedTab); + List listeners = component.getConfigListeners(); + + // Set the default tab to 'Appearance' for a different-type multi-comp dialog (this is the most prominent use case) + if (listeners != null && listeners.size() > 0 && !component.checkAllClassesEqual(listeners)) { + configurator.setSelectedTabIndex(1); + } else { + configurator.setSelectedTab(previousSelectedTab); + } //// configuration - List listeners = component.getConfigListeners(); if (component.checkAllClassesEqual(listeners)) { if (listeners != null && listeners.size() > 0) { setTitle("(" + trans.get("ComponentCfgDlg.MultiComponent") + ") " + diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index eaf8965a2..d21013064 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -133,11 +133,6 @@ public class RocketComponentConfig extends JPanel { tabbedPane.addTab(trans.get("RocketCompCfg.tab.Comment"), null, commentTab(), trans.get("RocketCompCfg.tab.Specifyacomment")); - // Set the default tab to 'Appearance' for a different-type multi-comp dialog (this is the most prominent use case) - if (listeners != null && listeners.size() > 0 && !allSameType) { - tabbedPane.setSelectedIndex(1); - } - addButtons(); updateFields(); @@ -329,6 +324,12 @@ public class RocketComponentConfig extends JPanel { } } + public void setSelectedTabIndex(int index) { + if (tabbedPane != null) { + tabbedPane.setSelectedIndex(index); + } + } + public void setSelectedTab(String tabName) { if (tabbedPane != null) { for (int i = 0; i < tabbedPane.getTabCount(); i++) { From 05a033dee425c4f940e33a5a6759a0790947c759 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 15 Jun 2022 18:08:06 +0200 Subject: [PATCH 05/10] Fix decal selection for multi-comp --- .../gui/configdialog/AppearancePanel.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java index 669089883..86632889a 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java @@ -496,6 +496,20 @@ public class AppearancePanel extends JPanel implements WindowListener { DecalModel decalModel = new DecalModel(panel, document, builder); JComboBox textureDropDown = new JComboBox(decalModel); + // We need to add this action listener that triggers a decalModel update when the same item is selected, because + // for multi-comp edits, the listeners' decals may not be updated otherwise + textureDropDown.addActionListener(new ActionListener() { + private DecalImage previousSelection = (DecalImage) decalModel.getSelectedItem(); + @Override + public void actionPerformed(ActionEvent e) { + DecalImage decal = (DecalImage) textureDropDown.getSelectedItem(); + if (decal == previousSelection) { + decalModel.setSelectedItem(decal); + } + previousSelection = decal; + } + }); + JButton colorButton = new SelectColorButton(new ColorIcon(builder.getPaint())); colorButton.addActionListener(new ColorActionListener(builder, "Paint")); From ab8fe16b62f5eaf8255e4ae5cd63478d78774062 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 15 Jun 2022 19:22:39 +0200 Subject: [PATCH 06/10] Fix AppearanceBuilder change listener removal --- .../gui/configdialog/AppearancePanel.java | 26 ++----------------- .../configdialog/ComponentConfigDialog.java | 7 +++-- .../configdialog/RocketComponentConfig.java | 11 ++++++-- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java index 86632889a..19361928a 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java @@ -3,9 +3,6 @@ package net.sf.openrocket.gui.configdialog; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.awt.event.WindowListener; import java.lang.reflect.Method; import java.util.EventObject; @@ -61,7 +58,7 @@ import net.sf.openrocket.util.LineStyle; import net.sf.openrocket.util.StateChangeListener; import net.sf.openrocket.gui.widgets.SelectColorButton; -public class AppearancePanel extends JPanel implements WindowListener { +public class AppearancePanel extends JPanel { private static final long serialVersionUID = 2709187552673202019L; private static final Translator trans = Application.getTranslator(); @@ -110,14 +107,7 @@ public class AppearancePanel extends JPanel implements WindowListener { private static final JColorChooser colorChooser = new JColorChooser(); - @Override - public void windowOpened(WindowEvent e) {} - - @Override - public void windowClosing(WindowEvent e) {} - - @Override - public void windowClosed(WindowEvent e) { + public void clearConfigListeners() { if (ab != null) { ab.clearConfigListeners(); } @@ -126,18 +116,6 @@ public class AppearancePanel extends JPanel implements WindowListener { } } - @Override - public void windowIconified(WindowEvent e) {} - - @Override - public void windowDeiconified(WindowEvent e) {} - - @Override - public void windowActivated(WindowEvent e) {} - - @Override - public void windowDeactivated(WindowEvent e) {} - private class ColorActionListener implements ActionListener { private final String valueName; private final Object o; diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java index 8b88185a3..4919c1ee3 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java @@ -17,6 +17,7 @@ import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; +import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.BugException; @@ -64,6 +65,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis * In fact, it should trigger for any method of closing the dialog. */ public void windowClosed(WindowEvent e){ + configurator.clearConfigListeners(); configurator.invalidate(); document.getRocket().removeComponentChangeListener(ComponentConfigDialog.this); ComponentConfigDialog.this.dispose(); @@ -231,9 +233,10 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis */ public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) { if (dialog != null) { - // Don't remember the previous tab for stages, because this will leave you in the override tab for + // Don't remember the previous tab for rockets or stages, because this will leave you in the override tab for // the next component, which is generally not what you want. - if (dialog.getComponent() instanceof AxialStage && !(component instanceof AxialStage)) { + if (dialog.getComponent() instanceof Rocket || + (dialog.getComponent() instanceof AxialStage && !(component instanceof AxialStage))) { previousSelectedTab = null; } else { previousSelectedTab = dialog.getSelectedTabName(); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index d21013064..1b7486292 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -66,6 +66,7 @@ public class RocketComponentConfig extends JPanel { private final TextFieldListener textFieldListener; private JPanel buttonPanel; + private AppearancePanel appearancePanel = null; private JLabel infoLabel; private StyledLabel multiCompEditLabel; @@ -125,7 +126,8 @@ public class RocketComponentConfig extends JPanel { trans.get("RocketCompCfg.tab.MassandCGoverride")); if (allMassive) { //// Appearance options - tabbedPane.addTab(trans.get("RocketCompCfg.tab.Appearance"), null, new AppearancePanel(document, component), + appearancePanel = new AppearancePanel(document, component); + tabbedPane.addTab(trans.get("RocketCompCfg.tab.Appearance"), null, appearancePanel, "Appearance Tool Tip"); } @@ -237,7 +239,12 @@ public class RocketComponentConfig extends JPanel { multiCompEditLabel.setText(""); } } - + + public void clearConfigListeners() { + if (appearancePanel != null) { + appearancePanel.clearConfigListeners(); + } + } protected JPanel materialPanel(Material.Type type) { ////Component material: and Component finish: From f39d744d6729c46e2f88af5f98a381c879b2369d Mon Sep 17 00:00:00 2001 From: SiboVG Date: Wed, 15 Jun 2022 23:26:10 +0200 Subject: [PATCH 07/10] Fix undo title --- core/resources/l10n/messages.properties | 1 + .../openrocket/gui/configdialog/ComponentConfigDialog.java | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index a10a1c412..57645d9c8 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -954,6 +954,7 @@ ComponentCfgDlg.MultiComponentConfig = Multi-component configuration ComponentCfgDlg.MultiComponentEdit = Multi-component edit ComponentCfgDlg.MultiComponentEdit.ttip = You are editing the following components:
ComponentCfgDlg.Modify = Modify +ComponentCfgDlg.ModifyComponents = Modify components !StageConfig StageConfig.tab.Separation = Separation diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java index 4919c1ee3..7defaca26 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java @@ -258,7 +258,11 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis dialog.setVisible(true); ////Modify - document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName()); + if (component.getConfigListeners().size() == 0) { + document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName()); + } else { + document.addUndoPosition(trans.get("ComponentCfgDlg.ModifyComponents")); + } } /** From 6590a76ff466a3adb517b7002b4c5e7b90639fa1 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 16 Jun 2022 00:25:48 +0200 Subject: [PATCH 08/10] Fix opacity change not undo-ing properly --- .../net/sf/openrocket/appearance/AppearanceBuilder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java index c77fea10c..3763eae0e 100644 --- a/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java +++ b/core/src/net/sf/openrocket/appearance/AppearanceBuilder.java @@ -216,10 +216,10 @@ public class AppearanceBuilder extends AbstractChangeSource { // Clamp opacity between 0 and 1 opacity = Math.max(0, Math.min(1, opacity)); - this.paint.setAlpha((int) (opacity * 255)); - if (!bypassAppearanceChangeEvent) { - fireChangeEvent(); - } + // Instead of simply setting the alpha, we need to create a new color with the new alpha value, otherwise undoing + // the setOpacity will not work correctly. (don't ask me why) + Color c = new Color(paint.getRed(), paint.getGreen(), paint.getBlue(), (int) (opacity * 255)); + setPaint(c); } /** From 93bf12ec39211e0b16b1dab2dfb8b0117e45f4e7 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 16 Jun 2022 00:27:10 +0200 Subject: [PATCH 09/10] Fix UI issue opacity slider The spinner was too small which caused the width of the spinner to dynamically change, based on what value the spinner had. So when changing the slider value, the slider would sometimes jump, thus changing the slider value unwanted. --- .../sf/openrocket/gui/configdialog/AppearancePanel.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java index 19361928a..2bea214f3 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/AppearancePanel.java @@ -613,9 +613,9 @@ public class AppearancePanel extends JPanel { mDefault.addEnableComponent(spinShine, false); mDefault.addEnableComponent(unitShine, false); - panel.add(spinShine, "split 3, w 50"); + panel.add(spinShine, "split 3, w 60"); panel.add(unitShine); - panel.add(slideShine, "w 50"); + panel.add(slideShine, "w 50, growx"); // Offset panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset"))); @@ -647,9 +647,9 @@ public class AppearancePanel extends JPanel { mDefault.addEnableComponent(spinOpacity, false); mDefault.addEnableComponent(unitOpacity, false); - panel.add(spinOpacity, "split 3, w 50"); + panel.add(spinOpacity, "split 3, w 60"); panel.add(unitOpacity); - panel.add(slideOpacity, "w 50"); + panel.add(slideOpacity, "w 50, growx"); // Rotation panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.rotation"))); From 93460be2e7565d4858c0234fe737545ed1656044 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 16 Jun 2022 00:48:44 +0200 Subject: [PATCH 10/10] Make multi-comp label a bit smaller --- .../sf/openrocket/gui/configdialog/RocketComponentConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index 1b7486292..283cdc52b 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -149,7 +149,7 @@ public class RocketComponentConfig extends JPanel { buttonPanel = new JPanel(new MigLayout("fillx, ins 5")); //// Multi-comp edit label - multiCompEditLabel = new StyledLabel(" ", 0, Style.BOLD); + multiCompEditLabel = new StyledLabel(" ", -1, Style.BOLD); //multiCompEditLabel.setFontColor(new Color(0, 0, 239)); multiCompEditLabel.setFontColor(new Color(170, 0, 100)); buttonPanel.add(multiCompEditLabel, "split 2");