Merge pull request #1450 from SiboVG/extend-multi-select

Extend multi-component selection for different-typed components
This commit is contained in:
SiboVG 2022-06-16 21:17:45 +02:00 committed by GitHub
commit f1a203ea6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 481 additions and 238 deletions

View File

@ -949,8 +949,12 @@ CenteringRingCfg.tab.Generalproperties = General properties
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = configuration ComponentCfgDlg.configuration = configuration
ComponentCfgDlg.configuration1 = ComponentCfgDlg.MultiComponent = Multi-component
ComponentCfgDlg.MultiComponentConfig = Multi-component configuration
ComponentCfgDlg.MultiComponentEdit = Multi-component edit
ComponentCfgDlg.MultiComponentEdit.ttip = <html>You are editing the following components:<br>
ComponentCfgDlg.Modify = Modify ComponentCfgDlg.Modify = Modify
ComponentCfgDlg.ModifyComponents = Modify components
!StageConfig !StageConfig
StageConfig.tab.Separation = Separation StageConfig.tab.Separation = Separation

View File

@ -678,7 +678,6 @@ CenteringRingCfg.tab.Generalproperties = Obecn
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = konfigurace ComponentCfgDlg.configuration = konfigurace
ComponentCfgDlg.configuration1 =
ComponentCfgDlg.Modify = Uprav ComponentCfgDlg.Modify = Uprav
!StageConfig !StageConfig

View File

@ -734,7 +734,6 @@ CenteringRingCfg.tab.Generalproperties = Allgemeine Eigenschaften
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = Konfiguration ComponentCfgDlg.configuration = Konfiguration
ComponentCfgDlg.configuration1 =
ComponentCfgDlg.Modify = Verändern ComponentCfgDlg.Modify = Verändern
!StageConfig !StageConfig

View File

@ -130,8 +130,7 @@ CompassSelectionButton.lbl.W = O
ComponentCfgDlg.Modify = Modificar ComponentCfgDlg.Modify = Modificar
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = ComponentCfgDlg.configuration = Configuraci\u00f3n
ComponentCfgDlg.configuration1 = Configuraci\u00f3n
ComponentIcons.Bodytube = Cuerpo tubular ComponentIcons.Bodytube = Cuerpo tubular
ComponentIcons.Bulkhead = Disco de enganche ComponentIcons.Bulkhead = Disco de enganche

View File

@ -121,7 +121,6 @@ CompassSelectionButton.lbl.W = O
ComponentCfgDlg.Modify = Modifier ComponentCfgDlg.Modify = Modifier
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = configuration ComponentCfgDlg.configuration = configuration
ComponentCfgDlg.configuration1 = configuration
ComponentIcons.Bodytube = Tube ComponentIcons.Bodytube = Tube
ComponentIcons.Bulkhead = Cloison ComponentIcons.Bulkhead = Cloison

View File

@ -736,7 +736,6 @@ CenteringRingCfg.tab.Generalproperties = Proprieta' generali
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = (configurazione) ComponentCfgDlg.configuration = (configurazione)
ComponentCfgDlg.configuration1 =
ComponentCfgDlg.Modify = Modifica ComponentCfgDlg.Modify = Modifica
!StageConfig !StageConfig

View File

@ -766,7 +766,6 @@ CenteringRingCfg.tab.Generalproperties = \u4E00\u822C
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = \u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3 ComponentCfgDlg.configuration = \u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30FC\u30B7\u30E7\u30F3
ComponentCfgDlg.configuration1 =
ComponentCfgDlg.Modify = \u5909\u66F4 ComponentCfgDlg.Modify = \u5909\u66F4
!StageConfig !StageConfig

View File

@ -889,7 +889,6 @@ CenteringRingCfg.tab.Generalproperties = Algemene eigenschappen
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = configuratie ComponentCfgDlg.configuration = configuratie
ComponentCfgDlg.configuration1 =
ComponentCfgDlg.Modify = Wijzigen ComponentCfgDlg.Modify = Wijzigen
!StageConfig !StageConfig

View File

@ -680,7 +680,6 @@ update.dlg.latestVersion = Korzystasz z najnowszej wersji OpenRocket: %s.
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = konfiguracja ComponentCfgDlg.configuration = konfiguracja
ComponentCfgDlg.configuration1 =
ComponentCfgDlg.Modify = Zmodyfikuj ComponentCfgDlg.Modify = Zmodyfikuj
!StageConfig !StageConfig

View File

@ -951,7 +951,6 @@ CenteringRingCfg.tab.Generalproperties = \u041E\u0441\u043D\u043E\u0432\u043D\u0
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B ComponentCfgDlg.configuration = \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B
ComponentCfgDlg.configuration1 =
ComponentCfgDlg.Modify = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C ComponentCfgDlg.Modify = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C
!StageConfig !StageConfig

View File

@ -838,7 +838,6 @@ CenteringRingCfg.tab.Generalproperties = General properties
!ComponentConfigDialog !ComponentConfigDialog
ComponentCfgDlg.configuration = configuration ComponentCfgDlg.configuration = configuration
ComponentCfgDlg.configuration1 =
ComponentCfgDlg.Modify = Modify ComponentCfgDlg.Modify = Modify
!StageConfig !StageConfig

View File

@ -1,10 +1,14 @@
package net.sf.openrocket.appearance; package net.sf.openrocket.appearance;
import net.sf.openrocket.appearance.Decal.EdgeMode; import net.sf.openrocket.appearance.Decal.EdgeMode;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.util.AbstractChangeSource; import net.sf.openrocket.util.AbstractChangeSource;
import net.sf.openrocket.util.Color; import net.sf.openrocket.util.Color;
import net.sf.openrocket.util.Coordinate; 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 * 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 * 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 Decal.EdgeMode edgeMode;
private boolean batch; private boolean batch;
/**
* List of appearance builders that will set their appearance properties to the same as the current appearance
*/
private final Map<RocketComponent, AppearanceBuilder> configListeners = new LinkedHashMap<>();
// If true, appearance change events will not be fired
private boolean bypassAppearanceChangeEvent = false;
/** /**
* Default constructor * Default constructor
@ -59,7 +70,9 @@ public class AppearanceBuilder extends AbstractChangeSource {
rotation = 0; rotation = 0;
image = null; image = null;
edgeMode = EdgeMode.REPEAT; 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 * @param d The decal
*/ */
public void setDecal(Decal d){ public void setDecal(Decal d){
for (AppearanceBuilder listener : configListeners.values()) {
listener.setDecal(d);
}
if (d != null) { if (d != null) {
setOffset(d.getOffset().x, d.getOffset().y); setOffset(d.getOffset().x, d.getOffset().y);
setCenter(d.getCenter().x, d.getCenter().y); setCenter(d.getCenter().x, d.getCenter().y);
@ -96,7 +112,9 @@ public class AppearanceBuilder extends AbstractChangeSource {
setEdgeMode(d.getEdgeMode()); setEdgeMode(d.getEdgeMode());
setImage(d.getImage()); setImage(d.getImage());
} }
fireChangeEvent(); if (!bypassAppearanceChangeEvent) {
fireChangeEvent();
}
} }
/** /**
@ -137,9 +155,13 @@ public class AppearanceBuilder extends AbstractChangeSource {
* @param paint the new color * @param paint the new color
*/ */
public void setPaint(Color paint) { public void setPaint(Color paint) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setPaint(paint);
}
this.paint = paint; this.paint = paint;
fireChangeEvent(); if (!bypassAppearanceChangeEvent) {
fireChangeEvent();
}
} }
/** /**
@ -158,8 +180,13 @@ public class AppearanceBuilder extends AbstractChangeSource {
* @param shine the new shine for template * @param shine the new shine for template
*/ */
public void setShine(double shine) { public void setShine(double shine) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setShine(shine);
}
this.shine = 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 * @param opacity new opacity value expressed in a percentage, where 0 is fully transparent and 1 is fully opaque
*/ */
public void setOpacity(double opacity) { public void setOpacity(double opacity) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setOpacity(opacity);
}
if (this.paint == null) { if (this.paint == null) {
return; return;
} }
@ -186,8 +216,10 @@ public class AppearanceBuilder extends AbstractChangeSource {
// Clamp opacity between 0 and 1 // Clamp opacity between 0 and 1
opacity = Math.max(0, Math.min(1, opacity)); opacity = Math.max(0, Math.min(1, opacity));
this.paint.setAlpha((int) (opacity * 255)); // Instead of simply setting the alpha, we need to create a new color with the new alpha value, otherwise undoing
fireChangeEvent(); // 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);
} }
/** /**
@ -207,8 +239,13 @@ public class AppearanceBuilder extends AbstractChangeSource {
* @param offsetU the new offset to be used * @param offsetU the new offset to be used
*/ */
public void setOffsetU(double offsetU) { public void setOffsetU(double offsetU) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setOffsetU(offsetU);
}
this.offsetU = 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 * @param offsetV the new offset to be used
*/ */
public void setOffsetV(double offsetV) { public void setOffsetV(double offsetV) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setOffsetV(offsetV);
}
this.offsetV = 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 * @param centerU value of axis U for center
*/ */
public void setCenterU(double centerU) { public void setCenterU(double centerU) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setCenterU(centerU);
}
this.centerU = 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 * set a new value for axis V for center in template
* fires change event * fires change event
* *
* @param centerU value of axis V for center * @return value of axis V for center
*/ */
public void setCenterV(double centerV) { public void setCenterV(double centerV) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setCenterV(centerV);
}
this.centerV = 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 * @param scaleU new value of scalling in axis U
*/ */
public void setScaleU(double scaleU) { public void setScaleU(double scaleU) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setScaleU(scaleU);
}
this.scaleU = 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 * @param scaleV new value of scalling in axis V
*/ */
public void setScaleV(double scaleV) { public void setScaleV(double scaleV) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setScaleV(scaleV);
}
this.scaleV = 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 * sets a new value of axis Y for scalling in template
* fires change event * fires change event
* *
* @param scaleX the new value for axis Y * @param scaleY the new value for axis Y
*/ */
public void setScaleY(double scaleY) { public void setScaleY(double scaleY) {
setScaleV(1.0 / scaleY); setScaleV(1.0 / scaleY);
@ -401,14 +463,19 @@ public class AppearanceBuilder extends AbstractChangeSource {
* @param rotation the new value for rotation in template * @param rotation the new value for rotation in template
*/ */
public void setRotation(double rotation) { public void setRotation(double rotation) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setRotation(rotation);
}
this.rotation = rotation; this.rotation = rotation;
fireChangeEvent(); if (!bypassAppearanceChangeEvent) {
fireChangeEvent();
}
} }
/** /**
* gets the current image in template * gets the current image in template
* *
* @param the current image in template * @return the current image in template
*/ */
public DecalImage getImage() { public DecalImage getImage() {
return image; return image;
@ -421,8 +488,13 @@ public class AppearanceBuilder extends AbstractChangeSource {
* @param image the new image to be used as template * @param image the new image to be used as template
*/ */
public void setImage(DecalImage image) { public void setImage(DecalImage image) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setImage(image);
}
this.image = 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 * @param edgeMode the new edgeMode to be used
*/ */
public void setEdgeMode(Decal.EdgeMode edgeMode) { public void setEdgeMode(Decal.EdgeMode edgeMode) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.setEdgeMode(edgeMode);
}
this.edgeMode = 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 * param r the functor to be executed
*/ */
public void batch(Runnable r) { public void batch(Runnable r) {
for (AppearanceBuilder listener : configListeners.values()) {
listener.batch(r);
}
batch = true; batch = true;
r.run(); r.run();
batch = false; 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<RocketComponent, AppearanceBuilder> getConfigListeners() {
return configListeners;
}
public void setBypassChangeEvent(boolean newValue) {
this.bypassAppearanceChangeEvent = newValue;
} }
} }

View File

@ -123,7 +123,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
private Appearance appearance = null; private Appearance appearance = null;
// If true, component change events will not be fired // 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 * @param appearance
*/ */
public void setAppearance(Appearance appearance) { public void setAppearance(Appearance appearance) {
for (RocketComponent listener : configListeners) {
listener.setAppearance(appearance);
}
this.appearance = appearance; this.appearance = appearance;
if (this.appearance != null) { if (this.appearance != null) {
Decal d = this.appearance.getTexture(); Decal d = this.appearance.getTexture();
@ -581,9 +577,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/ */
public final void setMassOverridden(boolean o) { public final void setMassOverridden(boolean o) {
for (RocketComponent listener : configListeners) { for (RocketComponent listener : configListeners) {
listener.setIgnoreComponentChange(false); listener.setBypassChangeEvent(false);
listener.setMassOverridden(o); listener.setMassOverridden(o);
listener.setIgnoreComponentChange(false); listener.setBypassChangeEvent(false);
} }
if (massOverridden == o) { if (massOverridden == o) {
@ -655,9 +651,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/ */
public final void setCGOverridden(boolean o) { public final void setCGOverridden(boolean o) {
for (RocketComponent listener : configListeners) { for (RocketComponent listener : configListeners) {
listener.setIgnoreComponentChange(false); listener.setBypassChangeEvent(false);
listener.setCGOverridden(o); listener.setCGOverridden(o);
listener.setIgnoreComponentChange(true); listener.setBypassChangeEvent(true);
} }
if (cgOverridden == o) { if (cgOverridden == o) {
@ -806,9 +802,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/ */
public final void setName(String name) { public final void setName(String name) {
for (RocketComponent listener : configListeners) { for (RocketComponent listener : configListeners) {
listener.setIgnoreComponentChange(false); listener.setBypassChangeEvent(false);
listener.setName(name); listener.setName(name);
listener.setIgnoreComponentChange(true); listener.setBypassChangeEvent(true);
} }
if (this.name.equals(name)) { if (this.name.equals(name)) {
@ -1671,6 +1667,24 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
} }
return false; 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<RocketComponent> components) {
if (components == null || components.size() == 0) {
return true;
}
Class<? extends RocketComponent> myClass = this.getClass();
for (RocketComponent c : components) {
if (!c.getClass().equals(myClass)) {
return false;
}
}
return true;
}
/** /**
* Get the root component of the component tree. * Get the root component of the component tree.
@ -1936,7 +1950,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
*/ */
protected void fireComponentChangeEvent(ComponentChangeEvent e) { protected void fireComponentChangeEvent(ComponentChangeEvent e) {
checkState(); checkState();
if (parent == null || ignoreComponentChange) { if (parent == null || bypassComponentChangeEvent) {
/* Ignore if root invalid. */ /* Ignore if root invalid. */
return; return;
} }
@ -1955,37 +1969,36 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
fireComponentChangeEvent(new ComponentChangeEvent(this, type)); fireComponentChangeEvent(new ComponentChangeEvent(this, type));
} }
public void setIgnoreComponentChange(boolean newValue) { public void setBypassChangeEvent(boolean newValue) {
this.ignoreComponentChange = newValue; this.bypassComponentChangeEvent = newValue;
} }
public boolean getIgnoreComponentChange() { public boolean getBypassComponentChangeEvent() {
return this.ignoreComponentChange; return this.bypassComponentChangeEvent;
} }
/** /**
* Add a new config listener that will undergo the same configuration changes as this.component. Listener must be * Add a new config listener that will undergo the same configuration changes as this.component.
* of the same class as this.component.
* @param listener new config listener * @param listener new config listener
* @return true if listener was successfully added, false if not * @return true if listener was successfully added, false if not
*/ */
public boolean addConfigListener(RocketComponent listener) { public boolean addConfigListener(RocketComponent listener) {
if (listener == null || !this.getClass().equals(listener.getClass())) { if (listener == null) {
return false; return false;
} }
configListeners.add(listener); configListeners.add(listener);
listener.setIgnoreComponentChange(true); listener.setBypassChangeEvent(true);
return true; return true;
} }
public void removeConfigListener(RocketComponent listener) { public void removeConfigListener(RocketComponent listener) {
configListeners.remove(listener); configListeners.remove(listener);
listener.setIgnoreComponentChange(false); listener.setBypassChangeEvent(false);
} }
public void clearConfigListeners() { public void clearConfigListeners() {
for (RocketComponent listener : configListeners) { for (RocketComponent listener : configListeners) {
listener.setIgnoreComponentChange(false); listener.setBypassChangeEvent(false);
} }
configListeners.clear(); configListeners.clear();
} }

View File

@ -1,5 +1,6 @@
package net.sf.openrocket.gui.components; package net.sf.openrocket.gui.components;
import java.awt.Color;
import java.awt.Font; import java.awt.Font;
import javax.swing.JLabel; import javax.swing.JLabel;
@ -87,11 +88,9 @@ public class StyledLabel extends JLabel {
private void checkPreferredSize(float size, Style style) { private void checkPreferredSize(float size, Style style) {
String str = this.getText(); String str = this.getText();
if (str.startsWith("<html>") && str.indexOf("<br") < 0) { if (str.startsWith("<html>") && !str.contains("<br")) {
StyledLabel label = new StyledLabel("plaintext", size, style); StyledLabel label = new StyledLabel("plaintext", size, style);
label.validate(); label.validate();
//System.out.println("Plain-text label: " + label.getPreferredSize());
//System.out.println("HTML label: " + this.getPreferredSize());
} }
} }
@ -108,4 +107,8 @@ public class StyledLabel extends JLabel {
font = font.deriveFont(style.getFontStyle()); font = font.deriveFont(style.getFontStyle());
this.setFont(font); this.setFont(font);
} }
public void setFontColor(Color color) {
this.setForeground(color);
}
} }

View File

@ -107,6 +107,15 @@ public class AppearancePanel extends JPanel {
private static final JColorChooser colorChooser = new JColorChooser(); private static final JColorChooser colorChooser = new JColorChooser();
public void clearConfigListeners() {
if (ab != null) {
ab.clearConfigListeners();
}
if (insideAb != null) {
insideAb.clearConfigListeners();
}
}
private class ColorActionListener implements ActionListener { private class ColorActionListener implements ActionListener {
private final String valueName; private final String valueName;
private final Object o; private final Object o;
@ -198,23 +207,44 @@ public class AppearancePanel extends JPanel {
previousUserSelectedAppearance = c.getAppearance(); previousUserSelectedAppearance = c.getAppearance();
if (previousUserSelectedAppearance == null) { if (previousUserSelectedAppearance == null) {
previousUserSelectedAppearance = new AppearanceBuilder() previousUserSelectedAppearance = new AppearanceBuilder().getAppearance();
.getAppearance();
ab = new AppearanceBuilder(defaultAppearance); ab = new AppearanceBuilder(defaultAppearance);
} else { } else {
ab = new AppearanceBuilder(previousUserSelectedAppearance); 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() previousUserSelectedInsideAppearance = ((InsideColorComponent) c).getInsideColorComponentHandler()
.getInsideAppearance(); .getInsideAppearance();
if (previousUserSelectedInsideAppearance == null) { if (previousUserSelectedInsideAppearance == null) {
previousUserSelectedInsideAppearance = new AppearanceBuilder() previousUserSelectedInsideAppearance = new AppearanceBuilder().getAppearance();
.getAppearance();
insideAb = new AppearanceBuilder(defaultAppearance); insideAb = new AppearanceBuilder(defaultAppearance);
} else { } else {
insideAb = new AppearanceBuilder(previousUserSelectedInsideAppearance); 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(); net.sf.openrocket.util.Color figureColor = c.getColor();
@ -317,7 +347,7 @@ public class AppearancePanel extends JPanel {
add(new JSeparator(SwingConstants.HORIZONTAL), "span, wrap, growx"); 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 // 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(); InsideColorComponentHandler handler = ((InsideColorComponent)c).getInsideColorComponentHandler();
// Get translator keys // Get translator keys
@ -444,6 +474,20 @@ public class AppearancePanel extends JPanel {
DecalModel decalModel = new DecalModel(panel, document, builder); DecalModel decalModel = new DecalModel(panel, document, builder);
JComboBox<DecalImage> textureDropDown = new JComboBox<DecalImage>(decalModel); JComboBox<DecalImage> textureDropDown = new JComboBox<DecalImage>(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())); JButton colorButton = new SelectColorButton(new ColorIcon(builder.getPaint()));
colorButton.addActionListener(new ColorActionListener(builder, "Paint")); colorButton.addActionListener(new ColorActionListener(builder, "Paint"));
@ -464,13 +508,31 @@ public class AppearancePanel extends JPanel {
previousUserSelectedInsideAppearance = (builder == null) ? null previousUserSelectedInsideAppearance = (builder == null) ? null
: builder.getAppearance(); : 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); builder.setAppearance(defaultAppearance);
c.setAppearance(null); c.setAppearance(null);
} else { } 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); 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); builder.setAppearance(previousUserSelectedInsideAppearance);
}
} }
} }
}); });
@ -551,9 +613,9 @@ public class AppearancePanel extends JPanel {
mDefault.addEnableComponent(spinShine, false); mDefault.addEnableComponent(spinShine, false);
mDefault.addEnableComponent(unitShine, false); mDefault.addEnableComponent(unitShine, false);
panel.add(spinShine, "split 3, w 50"); panel.add(spinShine, "split 3, w 60");
panel.add(unitShine); panel.add(unitShine);
panel.add(slideShine, "w 50"); panel.add(slideShine, "w 50, growx");
// Offset // Offset
panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.offset")));
@ -585,9 +647,9 @@ public class AppearancePanel extends JPanel {
mDefault.addEnableComponent(spinOpacity, false); mDefault.addEnableComponent(spinOpacity, false);
mDefault.addEnableComponent(unitOpacity, false); mDefault.addEnableComponent(unitOpacity, false);
panel.add(spinOpacity, "split 3, w 50"); panel.add(spinOpacity, "split 3, w 60");
panel.add(unitOpacity); panel.add(unitOpacity);
panel.add(slideOpacity, "w 50"); panel.add(slideOpacity, "w 50, growx");
// Rotation // Rotation
panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.rotation"))); panel.add(new JLabel(trans.get("AppearanceCfg.lbl.texture.rotation")));
@ -622,10 +684,24 @@ public class AppearancePanel extends JPanel {
opacityModel.stateChanged(null); opacityModel.stateChanged(null);
lastOpacity = builder.getOpacity(); 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()); 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(); decalModel.refresh();
} }
}); });

View File

@ -14,8 +14,10 @@ import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.GUIUtil;
import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.rocketcomponent.AxialStage;
import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
import net.sf.openrocket.rocketcomponent.ComponentChangeListener; import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.BugException; import net.sf.openrocket.util.BugException;
@ -25,7 +27,7 @@ import net.sf.openrocket.util.Reflection;
* A dialog that contains the configuration elements of one component. * A dialog that contains the configuration elements of one component.
* The contents of the dialog are instantiated from CONFIGDIALOGPACKAGE according * The contents of the dialog are instantiated from CONFIGDIALOGPACKAGE according
* to the current component. * to the current component.
* *
* @author Sampo Niskanen <sampo.niskanen@iki.fi> * @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/ */
@ -33,26 +35,26 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String CONFIGDIALOGPACKAGE = "net.sf.openrocket.gui.configdialog"; private static final String CONFIGDIALOGPACKAGE = "net.sf.openrocket.gui.configdialog";
private static final String CONFIGDIALOGPOSTFIX = "Config"; 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 // 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 static ComponentConfigDialog dialog = null;
private OpenRocketDocument document = null;
private RocketComponent component = null;
private RocketComponentConfig configurator = null;
private OpenRocketDocument document = 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 private static String previousSelectedTab = null; // Name of the previous selected tab
private final Window parent; private final Window parent;
private static final Translator trans = Application.getTranslator(); private static final Translator trans = Application.getTranslator();
private ComponentConfigDialog(Window parent, OpenRocketDocument document, RocketComponent component, private ComponentConfigDialog(Window parent, OpenRocketDocument document, RocketComponent component) {
List<RocketComponent> listeners) {
super(parent); super(parent);
this.parent = parent; this.parent = parent;
setComponent(document, component); setComponent(document, component);
GUIUtil.setDisposableDialogOptions(this, null); GUIUtil.setDisposableDialogOptions(this, null);
GUIUtil.rememberWindowPosition(this); GUIUtil.rememberWindowPosition(this);
@ -63,33 +65,28 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
* In fact, it should trigger for any method of closing the dialog. * In fact, it should trigger for any method of closing the dialog.
*/ */
public void windowClosed(WindowEvent e){ public void windowClosed(WindowEvent e){
configurator.clearConfigListeners();
configurator.invalidate(); configurator.invalidate();
document.getRocket().removeComponentChangeListener(ComponentConfigDialog.this); document.getRocket().removeComponentChangeListener(ComponentConfigDialog.this);
ComponentConfigDialog.this.dispose(); ComponentConfigDialog.this.dispose();
component.clearConfigListeners(); if (clearConfigListeners) {
component.clearConfigListeners();
}
} }
public void windowClosing(WindowEvent e){}
@Override @Override
public void windowOpened(WindowEvent e) { public void windowOpened(WindowEvent e) {
super.windowOpened(e); super.windowOpened(e);
// Add config listeners clearConfigListeners = true;
component.clearConfigListeners();
if (listeners != null) {
for (RocketComponent listener : listeners) {
component.addConfigListener(listener);
}
}
} }
}); });
} }
/** /**
* Set the component being configured. The listening connections of the old configurator * Set the component being configured. The listening connections of the old configurator
* will be removed and the new ones created. * will be removed and the new ones created.
* *
* @param component Component to configure. * @param component Component to configure.
*/ */
private void setComponent(OpenRocketDocument document, RocketComponent component) { private void setComponent(OpenRocketDocument document, RocketComponent component) {
@ -97,24 +94,43 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
// Remove listeners by setting all applicable models to null // Remove listeners by setting all applicable models to null
GUIUtil.setNullModels(configurator); // null-safe GUIUtil.setNullModels(configurator); // null-safe
} }
this.document = document; this.document = document;
this.component = component; this.component = component;
this.document.getRocket().addComponentChangeListener(this); this.document.getRocket().addComponentChangeListener(this);
configurator = getDialogContents(); configurator = getDialogContents();
this.setContentPane(configurator); this.setContentPane(configurator);
configurator.updateFields(); configurator.updateFields();
// Set the selected tab List<RocketComponent> listeners = component.getConfigListeners();
configurator.setSelectedTab(previousSelectedTab);
// 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 //// configuration
setTitle(trans.get("ComponentCfgDlg.configuration1") + " " + component.getComponentName() + " " + trans.get("ComponentCfgDlg.configuration")); 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(); this.pack();
} }
public RocketComponent getComponent() {
return component;
}
public static ComponentConfigDialog getDialog() { public static ComponentConfigDialog getDialog() {
return dialog; return dialog;
} }
@ -124,32 +140,36 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
* Return the configurator panel of the current component. * Return the configurator panel of the current component.
*/ */
private RocketComponentConfig getDialogContents() { private RocketComponentConfig getDialogContents() {
Constructor<? extends RocketComponentConfig> c = List<RocketComponent> listeners = component.getConfigListeners();
boolean isSameClass = component.checkAllClassesEqual(listeners);
if (!isSameClass) {
return new RocketComponentConfig(document, component);
}
Constructor<? extends RocketComponentConfig> constructor =
findDialogContentsConstructor(component); findDialogContentsConstructor(component);
if (c != null) { if (constructor != null) {
try { try {
return c.newInstance(document, component); return constructor.newInstance(document, component);
} catch (InstantiationException e) { } catch (InstantiationException | IllegalAccessException e) {
throw new BugException("BUG in constructor reflection", e);
} catch (IllegalAccessException e) {
throw new BugException("BUG in constructor reflection", e); throw new BugException("BUG in constructor reflection", e);
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
throw Reflection.handleWrappedException(e); throw Reflection.handleWrappedException(e);
} }
} }
// Should never be reached, since RocketComponentConfig should catch all // Should never be reached, since RocketComponentConfig should catch all
// components without their own configurator. // components without their own configurator.
throw new BugException("Unable to find any configurator for " + component); throw new BugException("Unable to find any configurator for " + component);
} }
@Override @Override
public void componentChanged(ComponentChangeEvent e) { public void componentChanged(ComponentChangeEvent e) {
if (e.isTreeChange() || e.isUndoChange()) { if (e.isTreeChange() || e.isUndoChange()) {
// Hide dialog in case of tree or undo change // Hide dialog in case of tree or undo change
disposeDialog(); disposeDialog();
} else { } else {
/* /*
* TODO: HIGH: The line below has caused a NullPointerException (without null check) * TODO: HIGH: The line below has caused a NullPointerException (without null check)
@ -161,10 +181,10 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
configurator.updateFields(); 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. * CONFIGDIALOGPACKAGE.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -172,10 +192,10 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
Class<?> currentclass; Class<?> currentclass;
String currentclassname; String currentclassname;
String configclassname; String configclassname;
Class<?> configclass; Class<?> configclass;
Constructor<? extends RocketComponentConfig> c; Constructor<? extends RocketComponentConfig> c;
currentclass = component.getClass(); currentclass = component.getClass();
while ((currentclass != null) && (currentclass != Object.class)) { while ((currentclass != null) && (currentclass != Object.class)) {
currentclassname = currentclass.getCanonicalName(); currentclassname = currentclass.getCanonicalName();
@ -184,7 +204,7 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
currentclassname = currentclassname.substring(index + 1); currentclassname = currentclassname.substring(index + 1);
configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname + configclassname = CONFIGDIALOGPACKAGE + "." + currentclassname +
CONFIGDIALOGPOSTFIX; CONFIGDIALOGPOSTFIX;
try { try {
configclass = Class.forName(configclassname); configclass = Class.forName(configclassname);
c = (Constructor<? extends RocketComponentConfig>) c = (Constructor<? extends RocketComponentConfig>)
@ -192,69 +212,57 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
return c; return c;
} catch (Exception ignore) { } catch (Exception ignore) {
} }
currentclass = currentclass.getSuperclass(); currentclass = currentclass.getSuperclass();
} }
return null; return null;
} }
////////// Static dialog ///////// ////////// 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. * previously been used, or update the dialog and show it if a previous one exists.
* *
* @param document the document to configure. * @param document the document to configure.
* @param component the component to configure. * @param component the component to configure.
* @param listeners config listeners for the component
* @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog * @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog
*/ */
public static void showDialog(Window parent, OpenRocketDocument document, public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) {
RocketComponent component, List<RocketComponent> listeners, boolean rememberPreviousTab) {
if (dialog != null) { if (dialog != null) {
previousSelectedTab = dialog.getSelectedTabName(); // 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 Rocket ||
(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 == dialog.getComponent()) {
ComponentConfigDialog.clearConfigListeners = false;
}
dialog.dispose(); dialog.dispose();
} }
final SwingPreferences preferences = (SwingPreferences) Application.getPreferences(); final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
if (preferences.isAlwaysOpenLeftmostTab() || !rememberPreviousTab) { if (preferences.isAlwaysOpenLeftmostTab() || !rememberPreviousTab) {
previousSelectedTab = null; previousSelectedTab = null;
} }
dialog = new ComponentConfigDialog(parent, document, component, listeners); dialog = new ComponentConfigDialog(parent, document, component);
dialog.setVisible(true); dialog.setVisible(true);
////Modify ////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"));
* 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.
* @param listeners config listeners for the component
*/
public static void showDialog(Window parent, OpenRocketDocument document,
RocketComponent component, List<RocketComponent> listeners) {
ComponentConfigDialog.showDialog(parent, document, component, listeners, true);
}
/**
* 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, boolean rememberPreviousTab) {
ComponentConfigDialog.showDialog(parent, document, component, null, rememberPreviousTab);
} }
/** /**
@ -265,26 +273,20 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
* @param document the document to configure. * @param document the document to configure.
* @param component the component to configure. * @param component the component to configure.
*/ */
public static void showDialog(Window parent, OpenRocketDocument document, public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component) {
RocketComponent component) { ComponentConfigDialog.showDialog(parent, document, component, true);
ComponentConfigDialog.showDialog(parent, document, component, null, true);
} }
static void showDialog(RocketComponent component, List<RocketComponent> listeners, boolean rememberPreviousTab) { static void showDialog(RocketComponent component, boolean rememberPreviousTab) {
showDialog(dialog.parent, dialog.document, component, listeners, rememberPreviousTab); showDialog(dialog.parent, dialog.document, component, rememberPreviousTab);
}
/* package */
static void showDialog(RocketComponent component, List<RocketComponent> listeners) {
showDialog(dialog.parent, dialog.document, component, listeners, true);
} }
/* package */ /* package */
static void showDialog(RocketComponent component) { static void showDialog(RocketComponent component) {
ComponentConfigDialog.showDialog(component, null); showDialog(dialog.parent, dialog.document, component, true);
} }
/** /**
* Disposes the configuration dialog. May be used even if not currently visible. * Disposes the configuration dialog. May be used even if not currently visible.
*/ */
@ -293,8 +295,8 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
dialog.dispose(); dialog.dispose();
} }
} }
/** /**
* Returns whether the singleton configuration dialog is currently visible or not. * Returns whether the singleton configuration dialog is currently visible or not.
*/ */
@ -302,6 +304,10 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis
return (dialog != null) && (dialog.isVisible()); return (dialog != null) && (dialog.isVisible());
} }
public int getSelectedTabIndex() {
return configurator.getSelectedTabIndex();
}
public String getSelectedTabName() { public String getSelectedTabName() {
if (configurator != null) { if (configurator != null) {
return configurator.getSelectedTabName(); return configurator.getSelectedTabName();

View File

@ -82,17 +82,10 @@ public abstract class FinSetConfig extends RocketComponentConfig {
//// Convert fin set //// Convert fin set
document.addUndoPosition(trans.get("FinSetConfig.Convertfinset")); document.addUndoPosition(trans.get("FinSetConfig.Convertfinset"));
List<RocketComponent> listeners = new ArrayList<>();
for (RocketComponent listener : component.getConfigListeners()) {
if (listener instanceof FinSet) {
listeners.add(FreeformFinSet.convertFinSet((FinSet) listener));
}
}
RocketComponent freeform = RocketComponent freeform =
FreeformFinSet.convertFinSet((FinSet) component); FreeformFinSet.convertFinSet((FinSet) component);
ComponentConfigDialog.showDialog(freeform, listeners); ComponentConfigDialog.showDialog(freeform);
} }
}); });

View File

@ -1,6 +1,7 @@
package net.sf.openrocket.gui.configdialog; package net.sf.openrocket.gui.configdialog;
import java.awt.Color;
import java.awt.Component; import java.awt.Component;
import java.awt.Container; import java.awt.Container;
import java.awt.event.*; 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.l10n.Translator;
import net.sf.openrocket.material.Material; import net.sf.openrocket.material.Material;
import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.rocketcomponent.ComponentAssembly;
import net.sf.openrocket.rocketcomponent.*; import net.sf.openrocket.rocketcomponent.*;
import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish; import net.sf.openrocket.rocketcomponent.ExternalComponent.Finish;
import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.rocketcomponent.position.AxialMethod;
@ -66,22 +66,42 @@ public class RocketComponentConfig extends JPanel {
private final TextFieldListener textFieldListener; private final TextFieldListener textFieldListener;
private JPanel buttonPanel; private JPanel buttonPanel;
private AppearancePanel appearancePanel = null;
private JLabel infoLabel; private JLabel infoLabel;
private StyledLabel multiCompEditLabel;
private boolean allSameType; // Checks whether all listener components are of the same type as <component>
private boolean allMassive; // Checks whether all listener components, and this component, are massive
public RocketComponentConfig(OpenRocketDocument document, RocketComponent component) { 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]")); setLayout(new MigLayout("fill, gap 4!, ins panel", "[]:5[]", "[growprio 5]5![fill, grow, growprio 500]5![growprio 5]"));
this.document = document; this.document = document;
this.component = component; this.component = component;
// Check the listeners for the same type and massive status
allSameType = true;
allMassive = component.isMassive();
List<RocketComponent> listeners = component.getConfigListeners();
if (listeners != null && listeners.size() > 0) {
allSameType = component.checkAllClassesEqual(listeners);
if (allMassive) { // Only check if <component> is already massive
for (RocketComponent listener : listeners) {
if (!listener.isMassive()) {
allMassive = false;
break;
}
}
}
}
//// Component name: //// Component name:
JLabel label = new JLabel(trans.get("RocketCompCfg.lbl.Componentname")); JLabel label = new JLabel(trans.get("RocketCompCfg.lbl.Componentname"));
//// The component name. //// The component name.
label.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname")); label.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname"));
this.add(label, "spanx, height 32!, split"); this.add(label, "spanx, height 32!, split");
componentNameField = new JTextField(15); componentNameField = new JTextField(15);
textFieldListener = new TextFieldListener(); textFieldListener = new TextFieldListener();
componentNameField.addActionListener(textFieldListener); componentNameField.addActionListener(textFieldListener);
@ -89,34 +109,34 @@ public class RocketComponentConfig extends JPanel {
//// The component name. //// The component name.
componentNameField.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname")); componentNameField.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname"));
this.add(componentNameField, "growx"); this.add(componentNameField, "growx");
if (component.getPresetType() != null) { if (allSameType && component.getPresetType() != null) {
// If the component supports a preset, show the preset selection box. // If the component supports a preset, show the preset selection box.
presetModel = new PresetModel(this, document, component); presetModel = new PresetModel(this, document, component);
presetComboBox = new JComboBox(presetModel); presetComboBox = new JComboBox(presetModel);
presetComboBox.setEditable(false); presetComboBox.setEditable(false);
this.add(presetComboBox, ""); this.add(presetComboBox, "");
} }
tabbedPane = new JTabbedPane(); tabbedPane = new JTabbedPane();
this.add(tabbedPane, "newline, span, growx, growy 100, wrap"); this.add(tabbedPane, "newline, span, growx, growy 100, wrap");
//// Override and Mass and CG override options //// Override and Mass and CG override options
tabbedPane.addTab(trans.get("RocketCompCfg.tab.Override"), null, overrideTab(), tabbedPane.addTab(trans.get("RocketCompCfg.tab.Override"), null, overrideTab(),
trans.get("RocketCompCfg.tab.MassandCGoverride")); trans.get("RocketCompCfg.tab.MassandCGoverride"));
if (component.isMassive()) { if (allMassive) {
//// Appearance options //// 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"); "Appearance Tool Tip");
} }
//// Comment and Specify a comment for the component //// Comment and Specify a comment for the component
tabbedPane.addTab(trans.get("RocketCompCfg.tab.Comment"), null, commentTab(), tabbedPane.addTab(trans.get("RocketCompCfg.tab.Comment"), null, commentTab(),
trans.get("RocketCompCfg.tab.Specifyacomment")); trans.get("RocketCompCfg.tab.Specifyacomment"));
addButtons(); addButtons();
updateFields(); updateFields();
} }
@ -128,6 +148,12 @@ public class RocketComponentConfig extends JPanel {
buttonPanel = new JPanel(new MigLayout("fillx, ins 5")); buttonPanel = new JPanel(new MigLayout("fillx, ins 5"));
//// Multi-comp edit label
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");
//// Mass: //// Mass:
infoLabel = new StyledLabel(" ", -1); infoLabel = new StyledLabel(" ", -1);
buttonPanel.add(infoLabel, "growx"); buttonPanel.add(infoLabel, "growx");
@ -159,16 +185,17 @@ public class RocketComponentConfig extends JPanel {
public void updateFields() { public void updateFields() {
// Component name // Component name
componentNameField.setText(component.getName()); componentNameField.setText(component.getName());
// Info label // Info label
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
if (component.getPresetComponent() != null) { if (allSameType && component.getPresetComponent() != null) {
ComponentPreset preset = component.getPresetComponent(); ComponentPreset preset = component.getPresetComponent();
sb.append(preset.getManufacturer() + " " + preset.getPartNo() + " "); sb.append(preset.getManufacturer() + " " + preset.getPartNo() + " ");
} }
if (component.isMassive()) { List<RocketComponent> 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(trans.get("RocketCompCfg.lbl.Componentmass") + " ");
sb.append(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit( sb.append(UnitGroup.UNITS_MASS.getDefaultUnit().toStringUnit(
component.getComponentMass())); component.getComponentMass()));
@ -193,8 +220,31 @@ public class RocketComponentConfig extends JPanel {
} else { } else {
infoLabel.setText(""); 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("");
}
}
public void clearConfigListeners() {
if (appearancePanel != null) {
appearancePanel.clearConfigListeners();
}
} }
protected JPanel materialPanel(Material.Type type) { protected JPanel materialPanel(Material.Type type) {
////Component material: and Component finish: ////Component material: and Component finish:
@ -269,6 +319,10 @@ public class RocketComponentConfig extends JPanel {
return subPanel; return subPanel;
} }
public int getSelectedTabIndex() {
return tabbedPane.getSelectedIndex();
}
public String getSelectedTabName() { public String getSelectedTabName() {
if (tabbedPane != null) { if (tabbedPane != null) {
return tabbedPane.getTitleAt(tabbedPane.getSelectedIndex()); return tabbedPane.getTitleAt(tabbedPane.getSelectedIndex());
@ -277,6 +331,12 @@ public class RocketComponentConfig extends JPanel {
} }
} }
public void setSelectedTabIndex(int index) {
if (tabbedPane != null) {
tabbedPane.setSelectedIndex(index);
}
}
public void setSelectedTab(String tabName) { public void setSelectedTab(String tabName) {
if (tabbedPane != null) { if (tabbedPane != null) {
for (int i = 0; i < tabbedPane.getTabCount(); i++) { for (int i = 0; i < tabbedPane.getTabCount(); i++) {

View File

@ -338,16 +338,16 @@ public class BasicFrame extends JFrame {
if (!ComponentConfigDialog.isDialogVisible()) if (!ComponentConfigDialog.isDialogVisible())
return; return;
else
ComponentConfigDialog.disposeDialog();
RocketComponent c = (RocketComponent) paths[0].getLastPathComponent(); RocketComponent c = (RocketComponent) paths[0].getLastPathComponent();
List<RocketComponent> listeners = new ArrayList<>(); c.clearConfigListeners();
for (int i = 1; i < paths.length; i++) { for (int i = 1; i < paths.length; i++) {
RocketComponent listener = (RocketComponent) paths[i].getLastPathComponent(); RocketComponent listener = (RocketComponent) paths[i].getLastPathComponent();
if (listener.getClass().equals(c.getClass())) { c.addConfigListener(listener);
listeners.add((RocketComponent) paths[i].getLastPathComponent());
}
} }
ComponentConfigDialog.showDialog(BasicFrame.this, ComponentConfigDialog.showDialog(BasicFrame.this, BasicFrame.this.document, c);
BasicFrame.this.document, c, listeners);
} }
}); });
@ -1332,7 +1332,6 @@ public class BasicFrame extends JFrame {
* *
* @param worker the OpenFileWorker that loads the file. * @param worker the OpenFileWorker that loads the file.
* @param displayName the file name to display in dialogs. * @param displayName the file name to display in dialogs.
* @param file the File to set the document to (may be null).
* @param parent * @param parent
* @param openRocketConfigDialog if true, will open the configuration dialog of the rocket. This is useful for examples. * @param openRocketConfigDialog if true, will open the configuration dialog of the rocket. This is useful for examples.
* @return * @return

View File

@ -5,7 +5,6 @@ import java.awt.Toolkit;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -879,16 +878,17 @@ public class RocketActions {
List<RocketComponent> components = selectionModel.getSelectedComponents(); List<RocketComponent> components = selectionModel.getSelectedComponents();
Simulation[] sims = selectionModel.getSelectedSimulations(); Simulation[] sims = selectionModel.getSelectedSimulations();
if ((components != null) && (components.size() > 0) && checkAllClassesEqual(components)) { if ((components != null) && (components.size() > 0)) {
// Do nothing if the config dialog is already visible
if (ComponentConfigDialog.isDialogVisible()) if (ComponentConfigDialog.isDialogVisible())
return; ComponentConfigDialog.disposeDialog();
List<RocketComponent> listeners = null; RocketComponent component = components.get(0);
if (components.size() > 1) { 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)) { } else if (sims != null && sims.length > 0 && (simulationPanel != null)) {
simulationPanel.editSimulation(); simulationPanel.editSimulation();
} }
@ -898,25 +898,7 @@ public class RocketActions {
public void clipboardChanged() { public void clipboardChanged() {
List<RocketComponent> components = selectionModel.getSelectedComponents(); List<RocketComponent> components = selectionModel.getSelectedComponents();
this.setEnabled(checkAllClassesEqual(components) || isSimulationSelected()); this.setEnabled((components != null && components.size() > 0) || 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<RocketComponent> components) {
if (components == null || components.size() == 0) {
return false;
}
Class<? extends RocketComponent> myClass = components.get(0).getClass();
for (int i = 1; i < components.size(); i++) {
if (!components.get(i).getClass().equals(myClass)) {
return false;
}
}
return true;
} }
} }