2

What is the best way to display a range of signed HexBinary numbers in a JSpinner?

e.g. from 0x8000 to 0x7ffe

I have tried the following solutions, without much luck:

  1. Use a JSpinnerNumberModel with a default formatter to convert from int to Hexbinary.[Cannot display negative part of the range]
  2. Use a JSpinnerListModel and pass it a constructed list of HexBinary values that fall in the range (contrived solution with unnecessary code. doesn't work perfectly).

Is there a better, generic solution?

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
schinoy
  • 115
  • 1
  • 1
  • 7
  • 2
    possible duplicate of [Some hints for hex Jspinner ? I s the approach correct](http://stackoverflow.com/questions/9758469/some-hints-for-hex-jspinner-i-s-the-approach-correct) – trashgod Oct 29 '12 at 02:34
  • I tried the approach as detailed above in trial solution no. 1. The above link only solves the formatting to hex problem and cannot display negative values. I am facing a different problem, which involves starting the JSpinner at a negative hexBinary representation and rolling upwards till an upper positive limit. – schinoy Oct 29 '12 at 10:06
  • I managed to eventually accomplish the task by using a JSpinnerListModel, and passing it a list of hexbinary strings of values in range, constructed by a utility class. Of course, I do not like this solution, and think there may be a way to do it without constructing an actual list of the entire range. – schinoy Oct 29 '12 at 10:08

1 Answers1

4

I … think there may be a way to do it without constructing an actual list of the entire range.

One approach would be to extend AbstractSpinnerModel to create a LongNumberModel, as shown below. See also this related example.

Hex Spinner Test image

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.HeadlessException;
import java.text.ParseException;
import javax.swing.AbstractSpinnerModel;
import javax.swing.JFormattedTextField;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.JFrame;
import javax.swing.JSpinner;
import javax.swing.text.DefaultFormatter;
import javax.swing.text.DefaultFormatterFactory;

/**
 * @see https://stackoverflow.com/a/13121724/230513
 * @see https://stackoverflow.com/a/9758714/230513
 */
public class HexSpinnerTest {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                display();
            }
        });
    }

    private static void display() throws HeadlessException {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JSpinner sp = new JSpinner(new LongNumberModel(0x8000L, 0x8000L, 0xFFFFL, 1L));
        JSpinner.DefaultEditor editor = (JSpinner.DefaultEditor) sp.getEditor();
        JFormattedTextField tf = editor.getTextField();
        tf.setFormatterFactory(new MyFormatterFactory());
        f.getContentPane().add(sp, BorderLayout.NORTH);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static class LongNumberModel extends AbstractSpinnerModel {

        private Long value, stepSize;
        private Comparable<Long> minimum, maximum;

        public LongNumberModel(Long value, Long minimum, Long maximum, Long stepSize) {
            this.value = value;
            this.minimum = minimum;
            this.maximum = maximum;
            this.stepSize = stepSize;
        }

        @Override
        public Object getValue() {
            return value;
        }

        @Override
        public void setValue(Object value) {
            this.value = (Long) value;
            fireStateChanged();
        }

        @Override
        public Object getNextValue() {
            long v = value.longValue() + stepSize.longValue();
            return bounded(v);
        }

        @Override
        public Object getPreviousValue() {
            long v = value.longValue() - stepSize.longValue();
            return bounded(v);
        }

        private Object bounded(long v) {
            if ((maximum != null) && (maximum.compareTo(v) < 0)) {
                return null;
            }
            if ((minimum != null) && (minimum.compareTo(v) > 0)) {
                return null;
            }
            return Long.valueOf(v);
        }
    }

    private static class MyFormatterFactory extends DefaultFormatterFactory {

        @Override
        public AbstractFormatter getDefaultFormatter() {
            return new HexFormatter();
        }
    }

    private static class HexFormatter extends DefaultFormatter {

        @Override
        public Object stringToValue(String text) throws ParseException {
            try {
                return Long.valueOf(text, 16);
            } catch (NumberFormatException nfe) {
                throw new ParseException(text, 0);
            }
        }

        @Override
        public String valueToString(Object value) throws ParseException {
            return Long.toHexString(
                ((Long) value).intValue()).toUpperCase();
        }
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • You don't need to subclass DefaultFormatterFactory, though. Just use the setDefaultFormatter method of the textfield's formatterFactory to set the HexFormatter instance as a default. – phobic Aug 02 '14 at 10:33