com.lyonesgamer.propertygrid.properties.NumberProperty Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of JPropertyGrid Show documentation
Show all versions of JPropertyGrid Show documentation
A property editor component for Swing.
The newest version!
package com.lyonesgamer.propertygrid.properties;
import com.lyonesgamer.propertygrid.PGProperty;
import javax.swing.*;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import java.awt.*;
import java.math.BigDecimal;
import java.util.*;
import java.util.List;
/**
* A property representing floating-point numbers. Has double precision.
*
* The property can be unsigned, which disallows the value from being negative. However, because Java doesn't support
* unsigned numbers, the maximum value is still {@link Double#MAX_VALUE}.
*
* @author Tristan Patch
* @since 1.0
*/
public class NumberProperty extends PGProperty {
/**
* The value of the property.
*/
protected double value;
/**
* The minimum value of the spinner. Defaults to {@link Double#MIN_VALUE}.
*/
protected double minimum = Double.MIN_VALUE;
/**
* The maximum value of the spinner. Defaults to {@link Double#MAX_VALUE}.
*/
protected double maximum = Double.MAX_VALUE;
/**
* The number of significant digits the value should display when stringified.
*/
protected final int PRECISION = 10;
/**
* The editor to use.
*/
protected NumberEditor editor = new NumberEditor();
/**
* The renderer to use.
*/
protected NumberRenderer renderer = new NumberRenderer();
/**
* Creates a new property.
*
* @param name The name of the property in the grid.
* @param value The initial value of the property.
*/
public NumberProperty(String name, double value) {
super(name);
this.value = value;
}
/**
* The minimum value this property can hold.
* @return The minimum value.
*/
public double getMinimum() {
return minimum;
}
/**
* Sets the minimum value this property can hold. The spinner will go no lower than this, and values are clamped both
* during this call and on following setValue* calls.
* @param minimum The new minimum.
*/
public void setMinimum(double minimum) {
if (value < minimum)
value = minimum;
this.minimum = minimum;
}
/**
* The maximum value this property can hold.
* @return The maximum value.
*/
public double getMaximum() {
return maximum;
}
/**
* Sets the maximum value this property can hold. The spinner will go no higher than this, and values are clamped both
* during this call and on following setValue* calls.
* @param maximum The new maximum.
*/
public void setMaximum(double maximum) {
if (value > maximum)
value = maximum;
this.maximum = maximum;
}
@Override
protected void doAfterAdded() {}
/**
* Validates the passed value. If the property is signed, the value is valid. If the property is unsigned, the value
* is valid iff the value is positive or zero.
*
* @param value The value to be validated.
* @return If the value is valid.
*/
@Override
public boolean onValidateValue(Double value) {
return value >= minimum && value <= maximum;
}
@Override
public void onSetValue(Double value) {
if (parent != null)
parent.firePropertyChangeEvent(this, value);
this.value = value;
}
@Override
public void setValueFromString(String value) {
setValue(Double.parseDouble(value));
}
@Override
public void setValueFromLong(long value) {
setValue((double)value);
}
@Override
public Double getValue() {
return value;
}
@Override
public Double stringToValue(String value) {
try {
double value2 = Double.parseDouble(value);
if (!onValidateValue(value2))
value2 = 0.0;
return value2;
} catch (NumberFormatException e) {
return -1.0;
}
}
@Override
public Double longToValue(long value) {
if (!onValidateValue((double)value))
value = 0;
return (double)value;
}
/**
* Converts the value to a string. Rounds the value up to {@link #PRECISION} significant digits.
*
* @param value The value to convert.
* @return The value as a pretty-printed string.
*/
@Override
public String valueToString(Double value) {
BigDecimal decimal = new BigDecimal(value);
decimal = decimal.setScale(PRECISION, BigDecimal.ROUND_UP);
return decimal.toString();
}
@Override
public PGCellRenderer getRenderer() {
return renderer;
}
@Override
public PGCellEditor getEditor() {
return editor;
}
protected class NumberEditor extends JTextPane implements PGCellEditor {
//No need to use an EventListenerList if we only have one listener type
protected final List listeners = new ArrayList<>();
protected final Set listenersToRemove = new HashSet<>();
@Override
public Component getTableCellEditorComponent(JTable table, Object passedValue, boolean isSelected, int row, int column) {
this.setText(valueToString(getValue()));
this.setEnabled(!disabled);
return this;
}
@Override
public Object getCellEditorValue() {
return value;
}
@Override
public boolean isCellEditable(EventObject anEvent) {
return !disabled;
}
@Override
public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
@Override
public boolean stopCellEditing() {
onSetValue(Double.parseDouble(this.getText()));
fireEditingStopped();
return true;
}
@Override
public void cancelCellEditing() {
fireEditingCancelled();
}
public void fireEditingCancelled() {
ChangeEvent event = new ChangeEvent(this);
for (CellEditorListener l : listeners)
l.editingCanceled(event);
doClearListeners();
}
public void fireEditingStopped() {
ChangeEvent event = new ChangeEvent(this);
for (CellEditorListener l : listeners)
l.editingStopped(event);
doClearListeners();
}
@Override
public void addCellEditorListener(CellEditorListener l) {
listeners.add(l);
}
@Override
public void removeCellEditorListener(CellEditorListener l) {
listenersToRemove.add(l);
}
protected void doClearListeners() {
listeners.removeAll(listenersToRemove);
listenersToRemove.clear();
}
}
protected class NumberRenderer extends JLabel implements PGCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
setText(valueToString(getValue()));
setToolTipText(helpString);
return this;
}
}
}