001    package util.swing;
002    
003    import javax.swing.*;
004    import javax.swing.event.*;
005    import javax.swing.text.*;
006    
007    /**
008     * A {@link JTextField} that will allow only int values within a certain range to be entered.
009     *
010     * <p><b>Note:</b> This class is not meant to be serialized!</p>
011     *
012     * @author Steffen Zschaler
013     * @version 2.0 28/07/1999
014     * @since v2.0
015     */
016    public class JIntInput extends JTextField {
017    
018        /**
019         * The current value observer. The current value can always be found as the first element of the array.
020         *
021         * @serial This class is not meant to be serialized!
022         */
023        protected int[] m_anValue;
024    
025        /**
026         * The default value of the input field.
027         *
028         * @serial This class is not meant to be serialized!
029         */
030        protected int m_nDefault = 0;
031    
032        /**
033         * The minimum value.
034         *
035         * @serial This class is not meant to be serialized!
036         */
037        protected int m_nMinimum;
038    
039        /**
040         * The maximum value.
041         *
042         * @serial This class is not meant to be serialized!
043         */
044        protected int m_nMaximum;
045    
046        /**
047         * Create a new JIntInput. The minimum and maximum values default to {@link java.lang.Integer#MIN_VALUE} and
048         * {@link java.lang.Integer#MAX_VALUE}, resp.
049         *
050         * @param anValue the value observer the current value of the input field can be found as the first element
051         * of the array at any time.
052         * @param nDefault the default value of the input line.
053         */
054        public JIntInput(int[] anValue, int nDefault) {
055            this(anValue, nDefault, Integer.MIN_VALUE, Integer.MAX_VALUE);
056        }
057    
058        /**
059         * Create a new JIntInput.
060         *
061         * @param anValue the value observer the current value of the input field can be found as the first element
062         * of the array at any time.
063         * @param nDefault the default value of the input line.
064         * @param nMinimum the minimum input value.
065         * @param nMaximum the maximum input value.
066         */
067        public JIntInput(int[] anValue, int nDefault, int nMinimum, int nMaximum) {
068    
069            super("0");
070            // The value set here can be anything, because we set the actual value at the end of this constructor
071            // This is done, so that everything is properly initialized when the setText method is called.
072    
073            m_anValue = anValue;
074            m_nMinimum = nMinimum;
075            m_nMaximum = nMaximum;
076            m_nDefault = ((nDefault >= m_nMinimum) ? ((nDefault <= m_nMaximum) ? (nDefault) : (m_nMaximum)) :
077                    (m_nMinimum));
078            m_anValue[0] = m_nDefault;
079    
080            getDocument().addDocumentListener(new DocumentListener() {
081                public void changedUpdate(DocumentEvent e) {
082                    performUpdate();
083                }
084    
085                public void insertUpdate(DocumentEvent e) {
086                    performUpdate();
087                }
088    
089                public void removeUpdate(DocumentEvent e) {
090                    performUpdate();
091                }
092    
093                private void performUpdate() {
094                    String sText = getText();
095                    if (sText == "") {
096                        sText = Integer.toString(m_nDefault);
097                    }
098    
099                    try {
100                        m_anValue[0] = Integer.parseInt(sText);
101                    }
102                    catch (NumberFormatException nfe) {}
103                }
104            });
105    
106            setHorizontalAlignment(RIGHT);
107    
108            // we set the text again, because when this is done in the super class constructor, the minimum and maximum values
109            // have not yet been set correctly.
110            setText(Integer.toString(m_nDefault));
111        }
112    
113        /**
114         * Create and return the input fields document. This will create a document that allows only int values
115         * within the current range to be entered.
116         */
117        protected Document createDefaultModel() {
118            return new PlainDocument() {
119                private boolean m_fDidSpecialRemove = false;
120    
121                public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
122    
123                    if (m_fDidSpecialRemove) {
124                        if (getLength() > 0) {
125                            super.remove(0, getLength());
126                        }
127    
128                        offs = 0;
129                        m_fDidSpecialRemove = false;
130                    }
131    
132                    if (str == null) {
133                        return;
134                    }
135    
136                    String sText = getText(0, getLength());
137    
138                    if (sText == null) {
139                        sText = "";
140                        offs = 0;
141                    }
142    
143                    sText = new StringBuffer(sText).insert(offs, str).toString();
144    
145                    try {
146                        int nCounter = Integer.parseInt(sText);
147    
148                        if (nCounter < m_nMinimum) {
149                            return;
150                        }
151    
152                        if (nCounter > m_nMaximum) {
153                            return;
154                        }
155    
156                        super.insertString(offs, str, a);
157                    }
158                    catch (NumberFormatException nfe) {
159                        if (getLength() == 0) {
160                            super.insertString(0, Integer.toString(m_nDefault), a);
161                        }
162                    }
163                }
164    
165                public void remove(int offs, int len) throws BadLocationException {
166    
167                    String sText = getText(0, getLength());
168    
169                    if (sText == null) {
170                        return;
171                    }
172    
173                    sText = new StringBuffer(sText).delete(offs, offs + len).toString();
174    
175                    if (sText.equals("")) {
176                        sText = null;
177                    }
178    
179                    try {
180                        int nCounter = 0;
181    
182                        if (sText != null) {
183                            nCounter = Integer.parseInt(sText);
184                        }
185    
186                        if ((nCounter < m_nMinimum) || (nCounter > m_nMaximum) || (sText == null)) {
187                            super.remove(0, getLength());
188                            super.insertString(0, Integer.toString(m_nDefault), null);
189    
190                            m_fDidSpecialRemove = true;
191                        } else {
192                            super.remove(offs, len);
193    
194                            m_fDidSpecialRemove = false;
195                        }
196                    }
197                    catch (NumberFormatException nfe) {
198                        m_fDidSpecialRemove = false;
199                    }
200                }
201            };
202        }
203    }