Added 'Value Notation' to the Config menu

This commit is contained in:
Bane Stojkovic
2026-11-09 21:44:17 +01:00
parent bff9e05843
commit b2f99c7022
26 changed files with 1174 additions and 118 deletions
@@ -26,6 +26,8 @@ import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -34,12 +36,19 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import org.apache.log4j.Logger;
import org.diylc.swingframework.DoubleArrayTextField;
import org.diylc.common.PropertyWrapper;
import org.diylc.core.measures.AbstractMeasure;
import org.diylc.core.measures.Unit;
import org.diylc.core.measures.ValueNotation;
import org.diylc.core.measures.ValueNotation.ParsedMeasure;
import org.diylc.utils.Constants;
public class MeasureArrayEditor extends Container {
@@ -50,52 +59,59 @@ public class MeasureArrayEditor extends Container {
.getLogger(MeasureArrayEditor.class);
private Color oldBg;
private DoubleArrayTextField valueField;
private JTextField valueField;
private JComboBox<Object> unitBox;
private boolean ignoreUnitChanges = false;
@SuppressWarnings("unchecked")
public MeasureArrayEditor(final PropertyWrapper property) {
setLayout(new BorderLayout());
final AbstractMeasure<?>[] measure = ((AbstractMeasure<?>[]) property
.getValue());
Double[] values = new Double[measure.length];
for (int i = 0; i < measure.length; i++) {
values[i] = measure[i] == null ? null : measure[i].getValue();
}
valueField = new DoubleArrayTextField(measure == null ? null : values);
if (property.isReadOnly())
valueField.setEnabled(false);
oldBg = valueField.getBackground();
valueField.addPropertyChangeListener(
DoubleArrayTextField.VALUE_PROPERTY,
new PropertyChangeListener() {
final Function<String, Unit> unitResolver = MeasureEditor
.getUnitResolver(property.getType().getComponentType());
if (unitResolver == null) {
valueField = createDoubleField(property, measure);
} else {
final MeasureTextField<List<ParsedMeasure<Unit>>> measureField = new MeasureTextField<List<ParsedMeasure<Unit>>>(
formatForField(measure), text -> parseSections(text, unitResolver));
measureField.addPropertyChangeListener(MeasureTextField.VALUE_PROPERTY,
new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
try {
Constructor<?> ctor = property.getType().getComponentType()
.getConstructors()[0];
Double[] newValues = (Double[]) evt.getNewValue();
AbstractMeasure<?>[] newMeasure;
if (newValues == null)
newMeasure = null;
else {
newMeasure = (AbstractMeasure<?>[]) Array.newInstance(property.getType().getComponentType(), newValues.length);
for (int i = 0; i < newValues.length; i++) {
newMeasure[i] = (AbstractMeasure<?>) ctor
.newInstance(newValues[i],
unitBox.getSelectedItem());
@Override
public void propertyChange(PropertyChangeEvent evt) {
List<ParsedMeasure<Unit>> parsed = (List<ParsedMeasure<Unit>>) evt
.getNewValue();
// the unit box shows the unit of the first section
if (parsed != null && !parsed.isEmpty() && parsed.get(0) != null
&& parsed.get(0).unit() != null
&& parsed.get(0).unit() != unitBox.getSelectedItem()) {
ignoreUnitChanges = true;
try {
unitBox.setSelectedItem(parsed.get(0).unit());
} finally {
ignoreUnitChanges = false;
}
}
property.setValue(newMeasure);
property.setChanged(true);
valueField.setBackground(oldBg);
unitBox.setBackground(oldBg);
} catch (Exception e) {
LOG.error("Error while updating property value", e);
applySections(property, parsed, false);
}
});
measureField.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
if (measureField.getValue() != null) {
measureField.setDisplayText(
formatForField((AbstractMeasure<?>[]) property.getValue()));
}
});
}
});
valueField = measureField;
}
if (property.isReadOnly()) {
valueField.setEnabled(false);
}
oldBg = valueField.getBackground();
add(valueField, BorderLayout.CENTER);
try {
Type type = ((ParameterizedType) property.getType().getComponentType()
@@ -108,29 +124,18 @@ public class MeasureArrayEditor extends Container {
@Override
public void actionPerformed(ActionEvent evt) {
try {
Constructor<?> ctor = property.getType().getComponentType()
.getConstructors()[0];
AbstractMeasure<?>[] newMeasure;
Double[] newValues = valueField.getValue();
if (newValues == null)
newMeasure = null;
else {
newMeasure = (AbstractMeasure<?>[]) Array.newInstance(property.getType().getComponentType(), newValues.length);
for (int i = 0; i < newValues.length; i++) {
newMeasure[i] = (AbstractMeasure<?>) ctor
.newInstance(newValues[i],
unitBox.getSelectedItem());
}
if (ignoreUnitChanges) {
return;
}
if (valueField instanceof DoubleArrayTextField doubleField) {
applyValues(property, doubleField.getValue());
} else {
MeasureTextField<List<ParsedMeasure<Unit>>> measureField = (MeasureTextField<List<ParsedMeasure<Unit>>>) valueField;
if (measureField.getValue() != null) {
applySections(property, measureField.getValue(), true);
measureField.setDisplayText(
formatForField((AbstractMeasure<?>[]) property.getValue()));
}
property.setValue(newMeasure);
property.setChanged(true);
valueField.setBackground(oldBg);
unitBox.setBackground(oldBg);
} catch (Exception e) {
LOG.error("Error while updating property units", e);
}
}
});
@@ -145,6 +150,101 @@ public class MeasureArrayEditor extends Container {
}
}
private DoubleArrayTextField createDoubleField(final PropertyWrapper property,
AbstractMeasure<?>[] measure) {
Double[] values = new Double[measure.length];
for (int i = 0; i < measure.length; i++) {
values[i] = measure[i] == null ? null : measure[i].getValue();
}
DoubleArrayTextField valueField = new DoubleArrayTextField(measure == null ? null : values);
valueField.addPropertyChangeListener(
DoubleArrayTextField.VALUE_PROPERTY,
new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
applyValues(property, (Double[]) evt.getNewValue());
}
});
return valueField;
}
private void applyValues(PropertyWrapper property, Double[] newValues) {
try {
Constructor<?> ctor = property.getType().getComponentType()
.getConstructors()[0];
AbstractMeasure<?>[] newMeasure;
if (newValues == null)
newMeasure = null;
else {
newMeasure = (AbstractMeasure<?>[]) Array.newInstance(property.getType().getComponentType(), newValues.length);
for (int i = 0; i < newValues.length; i++) {
newMeasure[i] = (AbstractMeasure<?>) ctor
.newInstance(newValues[i],
unitBox.getSelectedItem());
}
}
property.setValue(newMeasure);
property.setChanged(true);
valueField.setBackground(oldBg);
unitBox.setBackground(oldBg);
} catch (Exception e) {
LOG.error("Error while updating property value", e);
}
}
/**
* Sections typed with their own unit keep it, the others take the unit selected in the box.
* When the box itself changed, its unit is applied to every section.
*/
private void applySections(PropertyWrapper property, List<ParsedMeasure<Unit>> sections,
boolean forceBoxUnit) {
try {
Constructor<?> ctor = property.getType().getComponentType()
.getConstructors()[0];
AbstractMeasure<?>[] newMeasure = null;
if (sections != null) {
newMeasure = (AbstractMeasure<?>[]) Array.newInstance(property.getType().getComponentType(), sections.size());
for (int i = 0; i < sections.size(); i++) {
ParsedMeasure<Unit> section = sections.get(i);
Object unit = section == null || section.unit() == null || forceBoxUnit
? unitBox.getSelectedItem() : section.unit();
newMeasure[i] = (AbstractMeasure<?>) ctor
.newInstance(section == null ? null : section.value(), unit);
}
}
property.setValue(newMeasure);
property.setChanged(true);
valueField.setBackground(oldBg);
unitBox.setBackground(oldBg);
} catch (Exception e) {
LOG.error("Error while updating property value", e);
}
}
private static List<ParsedMeasure<Unit>> parseSections(String text,
Function<String, Unit> unitResolver) {
List<ParsedMeasure<Unit>> sections = new ArrayList<ParsedMeasure<Unit>>();
for (String part : text.split("/", -1)) {
sections.add(part.trim().isEmpty() ? null : ValueNotation.parse(part, unitResolver));
}
return sections;
}
private static String formatForField(AbstractMeasure<?>[] measure) {
if (measure == null) {
return "";
}
Object boxUnit = measure.length == 0 || measure[0] == null ? null : measure[0].getUnit();
List<String> parts = new ArrayList<String>();
for (AbstractMeasure<?> section : measure) {
parts.add(MeasureEditor.formatForField(section,
section == null || section.getUnit() == boxUnit));
}
return String.join(" / ", parts);
}
@Override
public void requestFocus() {
this.valueField.requestFocus();
@@ -26,6 +26,8 @@ import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -33,12 +35,22 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.DecimalFormat;
import java.text.Format;
import java.util.function.Function;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import org.apache.log4j.Logger;
import org.diylc.appframework.miscutils.ConfigurationManager;
import org.diylc.common.IPlugInPort;
import org.diylc.core.measures.Capacitance;
import org.diylc.core.measures.CapacitanceUnit;
import org.diylc.core.measures.Resistance;
import org.diylc.core.measures.ResistanceUnit;
import org.diylc.core.measures.Size;
import org.diylc.core.measures.SizeUnit;
import org.diylc.core.measures.ValueNotation;
import org.diylc.core.measures.ValueNotation.ParsedMeasure;
import org.diylc.swingframework.DoubleTextField;
import org.diylc.common.PropertyWrapper;
@@ -52,39 +64,70 @@ public class MeasureEditor extends Container {
private final static Logger LOG = Logger.getLogger(MeasureEditor.class);
private static final Format DECIMAL_FORMAT = new DecimalFormat("0.#####");
private Color oldBg;
private DoubleTextField valueField;
private JTextField valueField;
private JComboBox<Object> unitBox;
private boolean ignoreUnitChanges = false;
@SuppressWarnings("unchecked")
public MeasureEditor(final PropertyWrapper property) {
setLayout(new BorderLayout());
AbstractMeasure<?> measure = ((AbstractMeasure<?>) property.getValue());
measure = convertUnitsIfNeeded(measure);
valueField = new DoubleTextField(measure == null ? null : measure.getValue());
oldBg = valueField.getBackground();
valueField.addPropertyChangeListener(DoubleTextField.VALUE_PROPERTY, new PropertyChangeListener() {
final Function<String, Unit> unitResolver = getUnitResolver(property.getType());
if (unitResolver == null) {
final DoubleTextField doubleField =
new DoubleTextField(measure == null ? null : measure.getValue());
doubleField.addPropertyChangeListener(DoubleTextField.VALUE_PROPERTY,
new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
try {
Double newValue = (Double) evt.getNewValue();
if (newValue == null) {
property.setValue(null);
} else {
Constructor<?> ctor = property.getType().getConstructors()[0];
AbstractMeasure<?> newMeasure =
(AbstractMeasure<?>) ctor.newInstance(newValue, unitBox.getSelectedItem());
property.setValue(newMeasure);
@Override
public void propertyChange(PropertyChangeEvent evt) {
applyValue(property, (Double) evt.getNewValue(), unitBox.getSelectedItem());
}
});
valueField = doubleField;
} else {
final MeasureTextField<ParsedMeasure<Unit>> measureField =
new MeasureTextField<ParsedMeasure<Unit>>(formatForField(measure, true),
text -> ValueNotation.parse(text, unitResolver));
measureField.addPropertyChangeListener(MeasureTextField.VALUE_PROPERTY,
new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
ParsedMeasure<Unit> parsed = (ParsedMeasure<Unit>) evt.getNewValue();
if (parsed == null) {
applyValue(property, null, unitBox.getSelectedItem());
return;
}
// a unit typed together with the value, e.g. 4K7, also switches the unit box
if (parsed.unit() != null && parsed.unit() != unitBox.getSelectedItem()) {
ignoreUnitChanges = true;
try {
unitBox.setSelectedItem(parsed.unit());
} finally {
ignoreUnitChanges = false;
}
}
applyValue(property, parsed.value(), unitBox.getSelectedItem());
}
});
measureField.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent e) {
if (measureField.getValue() != null) {
measureField.setDisplayText(
formatForField((AbstractMeasure<?>) property.getValue(), true));
}
property.setChanged(true);
valueField.setBackground(oldBg);
unitBox.setBackground(oldBg);
} catch (Exception e) {
LOG.error("Error while updating property value", e);
}
}
});
});
valueField = measureField;
}
oldBg = valueField.getBackground();
add(valueField, BorderLayout.CENTER);
try {
Type type = ((ParameterizedType) property.getType().getGenericSuperclass()).getActualTypeArguments()[0];
@@ -95,33 +138,27 @@ public class MeasureEditor extends Container {
@Override
public void actionPerformed(ActionEvent evt) {
if (ignoreUnitChanges) {
return;
}
try {
Double newValue = valueField.getValue();
if (newValue == null) {
property.setValue(null);
} else {
Constructor<?> ctor = property.getType().getConstructors()[0];
if (property.isReadOnly()) {
double oldFactor = ((Unit)((AbstractMeasure<?>)property.getValue()).getUnit()).getFactor();
double newFactor = ((Unit)unitBox.getSelectedItem()).getFactor();
newValue = newValue * oldFactor / newFactor;
valueField.setValue(newValue);
}
AbstractMeasure<?> newMeasure =
(AbstractMeasure<?>) ctor.newInstance(newValue,
(Enum<? extends Unit>) unitBox.getSelectedItem());
property.setValue(newMeasure);
Double newValue = getFieldValue();
if (newValue != null && property.isReadOnly()) {
double oldFactor = ((Unit)((AbstractMeasure<?>)property.getValue()).getUnit()).getFactor();
double newFactor = ((Unit)unitBox.getSelectedItem()).getFactor();
newValue = newValue * oldFactor / newFactor;
}
applyValue(property, newValue, unitBox.getSelectedItem());
if (newValue != null && (property.isReadOnly() || unitResolver != null)) {
setFieldValue((AbstractMeasure<?>) property.getValue());
}
property.setChanged(true);
valueField.setBackground(oldBg);
unitBox.setBackground(oldBg);
} catch (Exception e) {
LOG.error("Error while updating property units", e);
}
}
});
add(unitBox, BorderLayout.EAST);
if (property.isReadOnly()) {
valueField.setEnabled(false);
unitBox.setEnabled(true);
@@ -136,6 +173,70 @@ public class MeasureEditor extends Container {
}
}
/**
* Returns the function that resolves unit symbols typed together with the value, or null when
* the measure only accepts a plain number.
*/
static Function<String, Unit> getUnitResolver(Class<?> measureType) {
if (Resistance.class.equals(measureType)) {
return ResistanceUnit::fromSymbol;
}
if (Capacitance.class.equals(measureType)) {
return CapacitanceUnit::fromSymbol;
}
return null;
}
/**
* Text shown in the value field. It is always decimal, whatever the selected value notation,
* because the unit box beside it already shows the unit; the unit is written out only when it
* differs from the one in the box.
*/
static String formatForField(AbstractMeasure<?> measure, boolean unitShownInBox) {
if (measure == null || measure.getValue() == null) {
return "";
}
String number;
synchronized (DECIMAL_FORMAT) {
number = DECIMAL_FORMAT.format(measure.getValue());
}
return unitShownInBox || measure.getUnit() == null ? number : number + measure.getUnit();
}
private void applyValue(PropertyWrapper property, Double newValue, Object unit) {
try {
if (newValue == null) {
property.setValue(null);
} else {
Constructor<?> ctor = property.getType().getConstructors()[0];
property.setValue((AbstractMeasure<?>) ctor.newInstance(newValue, unit));
}
property.setChanged(true);
valueField.setBackground(oldBg);
unitBox.setBackground(oldBg);
} catch (Exception e) {
LOG.error("Error while updating property value", e);
}
}
@SuppressWarnings("unchecked")
private Double getFieldValue() {
if (valueField instanceof DoubleTextField doubleField) {
return doubleField.getValue();
}
ParsedMeasure<Unit> parsed = ((MeasureTextField<ParsedMeasure<Unit>>) valueField).getValue();
return parsed == null ? null : parsed.value();
}
@SuppressWarnings("unchecked")
private void setFieldValue(AbstractMeasure<?> measure) {
if (valueField instanceof DoubleTextField doubleField) {
doubleField.setValue(measure == null ? null : measure.getValue());
} else {
((MeasureTextField<ParsedMeasure<Unit>>) valueField).setDisplayText(formatForField(measure, true));
}
}
private AbstractMeasure<?> convertUnitsIfNeeded(AbstractMeasure<?> value) {
if (ConfigurationManager.getInstance().readBoolean(IPlugInPort.AUTO_UNIT_CONVERSION_KEY, true)
&& value instanceof Size sizeValue) {
@@ -0,0 +1,131 @@
/*
DIY Layout Creator (DIYLC).
Copyright (c) 2009-2025 held jointly by the individual authors.
This file is part of DIYLC.
DIYLC is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DIYLC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with DIYLC. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diylc.swing.gui.editor;
import java.awt.BorderLayout;
import java.util.function.Function;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.diylc.utils.IconLoader;
/**
* {@link JTextField} that parses its text on every change and fires {@link #VALUE_PROPERTY} with
* the result, showing a warning icon while the text cannot be parsed. Unlike
* {@code DoubleTextField} the text is never rewritten while the user types; call
* {@link #setDisplayText(String)} to reformat it.
*
* @author Branislav Stojkovic
*
* @param <T> type of the parsed value
*/
public class MeasureTextField<T> extends JTextField {
private static final long serialVersionUID = 1L;
public static final String VALUE_PROPERTY = "MeasureValue";
private final Function<String, T> parser;
private final JLabel errorLabel;
private T value;
private boolean ignoreChanges = false;
/**
* @param text initial text
* @param parser turns non-empty text into a value, throwing {@link IllegalArgumentException} when
* it cannot
*/
public MeasureTextField(String text, Function<String, T> parser) {
super();
this.parser = parser;
setLayout(new BorderLayout());
errorLabel = new JLabel(IconLoader.Warning.getIcon());
errorLabel.setBorder(BorderFactory.createEmptyBorder(3, 0, 0, 0));
add(errorLabel, BorderLayout.EAST);
setDisplayText(text);
getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
textChanged();
}
@Override
public void insertUpdate(DocumentEvent e) {
textChanged();
}
@Override
public void removeUpdate(DocumentEvent e) {
textChanged();
}
});
}
public T getValue() {
return value;
}
/**
* Replaces the text without firing {@link #VALUE_PROPERTY}.
*/
public void setDisplayText(String text) {
ignoreChanges = true;
try {
setText(text);
value = parseQuietly(text);
errorLabel.setVisible(value == null);
} finally {
ignoreChanges = false;
}
}
private T parseQuietly(String text) {
try {
return text == null || text.trim().isEmpty() ? null : parser.apply(text);
} catch (IllegalArgumentException e) {
return null;
}
}
private void textChanged() {
if (ignoreChanges) {
return;
}
T newValue;
try {
newValue = getText().trim().isEmpty() ? null : parser.apply(getText());
} catch (IllegalArgumentException e) {
value = null;
errorLabel.setVisible(true);
return;
}
T oldValue = value;
value = newValue;
errorLabel.setVisible(false);
firePropertyChange(VALUE_PROPERTY, oldValue, newValue);
}
}
@@ -40,6 +40,7 @@ import org.diylc.core.Theme;
import org.diylc.utils.FlagLoader;
import org.diylc.lang.LangUtil;
import org.diylc.lang.Language;
import org.diylc.presenter.ValueNotationBinder;
import org.diylc.swing.ActionFactory;
import org.diylc.swing.ISwingUI;
import org.diylc.utils.IconLoader;
@@ -58,6 +59,7 @@ public class ConfigPlugin implements IPlugIn {
private static final String THEME_MENU = "Theme";
private static final String RULER_MENU = "Ruler Inch Subdivision...";
private static final String SNAP_MENU = "Snap To";
private static final String VALUE_NOTATION_MENU = "Value Notation";
private static final String CATEGORIES_MENU = "Component Categories";
private static final String COMPONENT_BROWSER_MENU = "Toolbox";
public static final String COMPONENT_BROWSER = "componentBrowser";
@@ -196,6 +198,17 @@ public class ConfigPlugin implements IPlugIn {
ActionFactory.getInstance()
.createConfigAction(plugInPort, "Sticky Points", IPlugInPort.STICKY_POINTS_KEY, true), CONFIG_MENU);
swingUI.injectSubmenu(VALUE_NOTATION_MENU, IconLoader.Decimal.getIcon(), CONFIG_MENU);
swingUI.injectMenuAction(
ActionFactory.getInstance().createToggleAction(IPlugInPort.VALUE_NOTATION_DECIMAL,
IPlugInPort.VALUE_NOTATION_KEY, VALUE_NOTATION_MENU, IPlugInPort.VALUE_NOTATION_DEFAULT),
VALUE_NOTATION_MENU);
swingUI.injectMenuAction(
ActionFactory.getInstance().createToggleAction(IPlugInPort.VALUE_NOTATION_RKM,
IPlugInPort.VALUE_NOTATION_KEY, VALUE_NOTATION_MENU, IPlugInPort.VALUE_NOTATION_DEFAULT),
VALUE_NOTATION_MENU);
ValueNotationBinder.bind(ConfigurationManager.getInstance(), plugInPort::refresh);
Map<String, String> themeContents = ResourceUtils.getResourceContents(THEMES_DIR);
if (!themeContents.isEmpty()) {
XStream xStream = new XStream(new DomDriver());
@@ -51,8 +51,8 @@ public enum IconLoader {
"coffeebean_edit.png"), Component("component.png"), ComponentAdd(
"component_add.png"), ComponentLarge("component_large.png"), ComponentPreferences(
"component_preferences.png"), ComponentReplace("component_replace.png"), Copy(
"copy.png"), Cut("cut.png"), Dashboard("dashboard.png"), DataFind("data_find.png"), Delete(
"delete.png"), DiskBlue("disk_blue.png"), DistributeCentersHorizontal(
"copy.png"), Cut("cut.png"), Dashboard("dashboard.png"), DataFind("data_find.png"), Decimal(
"decimal.png"), Delete("delete.png"), DiskBlue("disk_blue.png"), DistributeCentersHorizontal(
"distribute_centers_horizontal.png"), DistributeCentersVertical(
"distribute_centers_vertical.png"), DistributeSpacingHorizontal(
"distribute_spacing_horizontal.png"), DistributeSpacingVertical(
Binary file not shown.

After

Width:  |  Height:  |  Size: 769 B

@@ -729,3 +729,6 @@ Are you sure you want to delete variant "%s"?|Möchten Sie die Variante "%s" wir
Delete|Löschen
Set default model|Als Standardmodell festlegen
Remove default model|Standardmodell entfernen
Value Notation|Wertnotation
Decimal (4.7K, 0.22uF)|Dezimal (4.7K, 0.22uF)
RKM (4K7, 0u22)|RKM (4K7, 0u22)
@@ -722,3 +722,6 @@ Are you sure you want to delete variant "%s"?
Delete
Set default model
Remove default model
Value Notation
Decimal (4.7K, 0.22uF)
RKM (4K7, 0u22)
@@ -725,3 +725,6 @@ Are you sure you want to delete variant "%s"?|¿Seguro que desea eliminar la var
Delete|Eliminar
Set default model|Establecer como modelo predeterminado
Remove default model|Quitar modelo predeterminado
Value Notation|Notación de valores
Decimal (4.7K, 0.22uF)|Decimal (4.7K, 0.22uF)
RKM (4K7, 0u22)|RKM (4K7, 0u22)
@@ -724,3 +724,6 @@ Are you sure you want to delete variant "%s"?|Voulez-vous vraiment supprimer la
Delete|Supprimer
Set default model|Définir comme modèle par défaut
Remove default model|Retirer le modèle par défaut
Value Notation|Notation des valeurs
Decimal (4.7K, 0.22uF)|Décimale (4.7K, 0.22uF)
RKM (4K7, 0u22)|RKM (4K7, 0u22)
@@ -726,3 +726,6 @@ Are you sure you want to delete variant "%s"?|Eliminare davvero la variante "%s"
Delete|Elimina
Set default model|Imposta come modello predefinito
Remove default model|Rimuovi modello predefinito
Value Notation|Notazione dei valori
Decimal (4.7K, 0.22uF)|Decimale (4.7K, 0.22uF)
RKM (4K7, 0u22)|RKM (4K7, 0u22)
@@ -730,3 +730,6 @@ Are you sure you want to delete variant "%s"?|Weet u zeker dat u variant "%s" wi
Delete|Verwijderen
Set default model|Instellen als standaardmodel
Remove default model|Standaardmodel verwijderen
Value Notation|Waardenotatie
Decimal (4.7K, 0.22uF)|Decimaal (4.7K, 0.22uF)
RKM (4K7, 0u22)|RKM (4K7, 0u22)
@@ -725,3 +725,6 @@ Are you sure you want to delete variant "%s"?|Tem certeza de que deseja excluir
Delete|Excluir
Set default model|Definir como modelo padrão
Remove default model|Remover modelo padrão
Value Notation|Notação de valores
Decimal (4.7K, 0.22uF)|Decimal (4.7K, 0.22uF)
RKM (4K7, 0u22)|RKM (4K7, 0u22)
@@ -721,3 +721,6 @@ Are you sure you want to delete variant "%s"?|Da li ste sigurni da želite da ob
Delete|Obriši
Set default model|Postavi kao podrazumevani model
Remove default model|Ukloni podrazumevani model
Value Notation|Zapis vrednosti
Decimal (4.7K, 0.22uF)|Decimalni (4.7K, 0.22uF)
RKM (4K7, 0u22)|RKM (4K7, 0u22)
@@ -723,3 +723,6 @@ Are you sure you want to delete variant "%s"?|क्या आप वाकई
Delete|हटाएँ
Set default model|डिफ़ॉल्ट मॉडल सेट करें
Remove default model|डिफ़ॉल्ट मॉडल हटाएँ
Value Notation|मान संकेतन
Decimal (4.7K, 0.22uF)|दशमलव (4.7K, 0.22uF)
RKM (4K7, 0u22)|RKM (4K7, 0u22)
@@ -723,3 +723,6 @@ Are you sure you want to delete variant "%s"?|确定要删除变体“%s”吗
Delete|删除
Set default model|设为默认型号
Remove default model|取消默认型号
Value Notation|数值表示法
Decimal (4.7K, 0.22uF)|小数 (4.7K, 0.22uF)
RKM (4K7, 0u22)|RKM (4K7, 0u22)