6800e0de670682c20a20df1c51b89bc786967706
[debian/openrocket] / src / net / sf / openrocket / gui / adaptors / IntegerModel.java
1 package net.sf.openrocket.gui.adaptors;
2
3 import java.lang.reflect.InvocationTargetException;
4 import java.lang.reflect.Method;
5 import java.util.ArrayList;
6
7 import javax.swing.SpinnerModel;
8 import javax.swing.SpinnerNumberModel;
9 import javax.swing.event.ChangeEvent;
10 import javax.swing.event.ChangeListener;
11
12 import net.sf.openrocket.util.ChangeSource;
13
14
15 public class IntegerModel implements ChangeListener {
16
17
18         //////////// JSpinner Model ////////////
19         
20         private class IntegerSpinnerModel extends SpinnerNumberModel {
21                 @Override
22                 public Object getValue() {
23                         return IntegerModel.this.getValue();
24                 }
25
26                 @Override
27                 public void setValue(Object value) {
28                         if (firing > 0)   // Ignore, if called when model is sending events
29                                 return;
30                         Number num = (Number)value;
31                         int newValue = num.intValue();
32                         IntegerModel.this.setValue(newValue);
33                         
34 //                      try {
35 //                              int newValue = Integer.parseInt((String)value);
36 //                              IntegerModel.this.setValue(newValue);
37 //                      } catch (NumberFormatException e) { 
38 //                              IntegerModel.this.fireStateChanged();
39 //                      };
40                 }
41                         
42                 @Override
43                 public Object getNextValue() {
44                         int d = IntegerModel.this.getValue();
45                         if (d >= maxValue)
46                                 return null;
47                         return (d+1);
48                 }
49
50                 @Override
51                 public Object getPreviousValue() {
52                         int d = IntegerModel.this.getValue();
53                         if (d <= minValue)
54                                 return null;
55                         return (d-1);
56                 }
57                 
58                 @Override
59                 public void addChangeListener(ChangeListener l) {
60                         IntegerModel.this.addChangeListener(l);
61                 }
62
63                 @Override
64                 public void removeChangeListener(ChangeListener l) {
65                         IntegerModel.this.removeChangeListener(l);
66                 }
67         }
68         
69         /**
70          * Returns a new SpinnerModel with the same base as the DoubleModel.
71          * The values given to the JSpinner are in the currently selected units.
72          * 
73          * @return  A compatibility layer for a SpinnerModel.
74          */
75         public SpinnerModel getSpinnerModel() {
76                 return new IntegerSpinnerModel();
77         }
78         
79         
80
81
82         ////////////  Main model  /////////////
83
84         /*
85          * The main model handles all values in SI units, i.e. no conversion is made within the model.
86          */
87         
88         private final ChangeSource source;
89         private final String valueName;
90         
91         private final Method getMethod;
92         private final Method setMethod;
93         
94         private final ArrayList<ChangeListener> listeners = new ArrayList<ChangeListener>();
95
96         private final int minValue;
97         private final int maxValue;
98
99         
100         private int firing = 0;  //  >0 when model itself is sending events
101         
102         
103         // Used to differentiate changes in valueName and other changes in the source:
104         private int lastValue = 0;
105                 
106
107         
108         /**
109          * Generates a new DoubleModel that changes the values of the specified source.
110          * The double value is read and written using the methods "get"/"set" + valueName.
111          *  
112          * @param source Component whose parameter to use.
113          * @param valueName Name of metods used to get/set the parameter.
114          * @param multiplier Value shown by the model is the value from source.getXXX * multiplier
115          * @param min Minimum value allowed (in SI units)
116          * @param max Maximum value allowed (in SI units)
117          */
118         public IntegerModel(ChangeSource source, String valueName, int min, int max) {
119                 this.source = source;
120                 this.valueName = valueName;
121                 
122                 this.minValue = min;
123                 this.maxValue = max;
124                 
125                 try {
126                         getMethod = source.getClass().getMethod("get" + valueName);
127                         setMethod = source.getClass().getMethod("set" + valueName,int.class);
128                 } catch (NoSuchMethodException e) {
129                         throw new IllegalArgumentException("get/set methods for value '"+valueName+
130                                         "' not present in class "+source.getClass().getCanonicalName());
131                 }
132         }
133
134         public IntegerModel(ChangeSource source, String valueName, int min) {
135                 this(source,valueName,min,Integer.MAX_VALUE);
136         }
137         
138         public IntegerModel(ChangeSource source, String valueName) {
139                 this(source,valueName,Integer.MIN_VALUE,Integer.MAX_VALUE);
140         }
141         
142
143         
144         
145         /**
146          * Returns the value of the variable.
147          */
148         public int getValue() {
149                 try {
150                         return (Integer)getMethod.invoke(source);
151                 } catch (IllegalArgumentException e) {
152                         e.printStackTrace();
153                 } catch (IllegalAccessException e) {
154                         e.printStackTrace();
155                 } catch (InvocationTargetException e) {
156                         e.printStackTrace();
157                 }
158                 return lastValue;  // Should not occur
159         }
160         
161         /**
162          * Sets the value of the variable.
163          */
164         public void setValue(int v) {
165                 try {
166                         setMethod.invoke(source, v);
167                         return;
168                 } catch (IllegalArgumentException e) {
169                         e.printStackTrace();
170                 } catch (IllegalAccessException e) {
171                         e.printStackTrace();
172                 } catch (InvocationTargetException e) {
173                         e.printStackTrace();
174                 }
175                 fireStateChanged();  // Should not occur
176         }
177
178         
179         /**
180          * Add a listener to the model.  Adds the model as a listener to the Component if this
181          * is the first listener.
182          * @param l Listener to add.
183          */
184         public void addChangeListener(ChangeListener l) {
185                 if (listeners.isEmpty()) {
186                         source.addChangeListener(this);
187                         lastValue = getValue();
188                 }
189
190                 listeners.add(l);
191         }
192
193         /**
194          * Remove a listener from the model.  Removes the model from being a listener to the Component
195          * if this was the last listener of the model.
196          * @param l Listener to remove.
197          */
198         public void removeChangeListener(ChangeListener l) {
199                 listeners.remove(l);
200                 if (listeners.isEmpty()) {
201                         source.removeChangeListener(this);
202                 }
203         }
204         
205         public void fireStateChanged() {
206                 Object[] l = listeners.toArray();
207                 ChangeEvent event = new ChangeEvent(this);
208                 firing++;
209                 for (int i=0; i<l.length; i++)
210                         ((ChangeListener)l[i]).stateChanged(event);
211                 firing--;
212         }
213
214         /**
215          * Called when the source changes.  Checks whether the modeled value has changed, and if
216          * it has, updates lastValue and generates ChangeEvents for all listeners of the model.
217          */
218         public void stateChanged(ChangeEvent e) {
219                 int v = getValue();
220                 if (lastValue == v)
221                         return;
222                 lastValue = v;
223                 fireStateChanged();
224         }
225
226         /**
227          * Explain the DoubleModel as a String.
228          */
229         @Override
230         public String toString() {
231                 return "IntegerModel["+source.getClass().getCanonicalName()+":"+valueName+"]";
232         }
233         
234 }