All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lyonesgamer.propertygrid.properties.NumberProperty Maven / Gradle / Ivy

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;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy