release 0.9.6
[debian/openrocket] / src / net / sf / openrocket / gui / adaptors / BooleanModel.java
1 package net.sf.openrocket.gui.adaptors;
2
3 import java.awt.Component;
4 import java.awt.event.ActionEvent;
5 import java.lang.reflect.InvocationTargetException;
6 import java.lang.reflect.Method;
7 import java.util.ArrayList;
8 import java.util.List;
9
10 import javax.swing.AbstractAction;
11 import javax.swing.event.ChangeEvent;
12 import javax.swing.event.ChangeListener;
13
14 import net.sf.openrocket.util.BugException;
15 import net.sf.openrocket.util.ChangeSource;
16 import net.sf.openrocket.util.Reflection;
17
18
19 /**
20  * A class that adapts an isXXX/setXXX boolean variable.  It functions as an Action suitable
21  * for usage in JCheckBox or JToggleButton.  You can create a suitable button with
22  * <code>
23  *   check = new JCheckBox(new BooleanModel(component,"Value"))
24  *   check.setText("Label");
25  * </code>
26  * This will produce a button that uses isValue() and setValue(boolean) of the corresponding
27  * component.
28  * <p>
29  * Additionally a number of component enabled states may be controlled by this class using
30  * the method {@link #addEnableComponent(Component, boolean)}.
31  * 
32  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
33  */
34
35 public class BooleanModel extends AbstractAction implements ChangeListener {
36
37         private final ChangeSource source;
38         private final String valueName;
39         
40         private final Method getMethod;
41         private final Method setMethod;
42         private final Method getEnabled;
43         
44         private final List<Component> components = new ArrayList<Component>();
45         private final List<Boolean> componentEnableState = new ArrayList<Boolean>();
46         
47         private int firing = 0;
48         
49         private boolean oldValue;
50         private boolean oldEnabled;
51         
52         public BooleanModel(ChangeSource source, String valueName) {
53                 this.source = source;
54                 this.valueName = valueName;
55                 
56                 Method getter=null, setter=null;
57                 
58                 
59                 // Try get/is and set
60                 try {
61                         getter = source.getClass().getMethod("is" + valueName);
62                 } catch (NoSuchMethodException ignore) { }
63                 if (getter == null) {
64                         try {
65                                 getter = source.getClass().getMethod("get" + valueName);
66                         } catch (NoSuchMethodException ignore) { }
67                 }
68                 try {
69                         setter = source.getClass().getMethod("set" + valueName,boolean.class);
70                 } catch (NoSuchMethodException ignore) { }
71                 
72                 if (getter==null || setter==null) {
73                         throw new IllegalArgumentException("get/is methods for boolean '"+valueName+
74                                         "' not present in class "+source.getClass().getCanonicalName());
75                 }
76
77                 getMethod = getter;
78                 setMethod = setter;
79                 
80                 Method e = null;
81                 try {
82                         e = source.getClass().getMethod("is" + valueName + "Enabled");
83                 } catch (NoSuchMethodException ignore) { }
84                 getEnabled = e;
85                 
86                 oldValue = getValue();
87                 oldEnabled = getIsEnabled();
88                 
89                 this.setEnabled(oldEnabled);
90                 this.putValue(SELECTED_KEY, oldValue);
91                 
92                 source.addChangeListener(this);
93         }
94         
95         public boolean getValue() {
96                 try {
97                         return (Boolean)getMethod.invoke(source);
98                 } catch (IllegalAccessException e) {
99                         throw new BugException("getMethod execution error for source "+source,e);
100                 } catch (InvocationTargetException e) {
101                         throw Reflection.handleWrappedException(e);
102                 }
103         }
104         
105         public void setValue(boolean b) {
106                 try {
107                         setMethod.invoke(source, new Object[] { (Boolean)b });
108                 } catch (IllegalAccessException e) {
109                         throw new BugException("setMethod execution error for source "+source,e);
110                 } catch (InvocationTargetException e) {
111                         throw Reflection.handleWrappedException(e);
112                 }
113         }
114         
115         
116         /**
117          * Add a component the enabled status of which will be controlled by the value
118          * of this boolean.  The <code>component</code> will be enabled exactly when
119          * the state of this model is equal to that of <code>enableState</code>.
120          * 
121          * @param component             the component to control.
122          * @param enableState   the state in which the component should be enabled.
123          */
124         public void addEnableComponent(Component component, boolean enableState) {
125                 components.add(component);
126                 componentEnableState.add(enableState);
127                 updateEnableStatus();
128         }
129         
130         /**
131          * Add a component which will be enabled when this boolean is <code>true</code>.
132          * This is equivalent to <code>booleanModel.addEnableComponent(component, true)</code>.
133          * 
134          * @param component             the component to control.
135          * @see #addEnableComponent(Component, boolean)
136          */
137         public void addEnableComponent(Component component) {
138                 addEnableComponent(component, true);
139         }
140         
141         private void updateEnableStatus() {
142                 boolean state = getValue();
143                 
144                 for (int i=0; i < components.size(); i++) {
145                         Component c = components.get(i);
146                         boolean b = componentEnableState.get(i);
147                         c.setEnabled(state == b);
148                 }
149         }
150         
151         
152 //      @Override
153 //      public boolean isEnabled() {
154 //              if (getEnabled == null)
155 //                      return true;
156 //              try {
157 //                      return (Boolean)getEnabled.invoke(source);
158 //              } catch (IllegalAccessException e) {
159 //                      throw new RuntimeException("getEnabled execution error for source "+source,e);
160 //              } catch (InvocationTargetException e) {
161 //                      throw new RuntimeException("getEnabled execution error for source "+source,e);
162 //              }
163 //      }
164
165
166         private boolean getIsEnabled() {
167                 if (getEnabled == null)
168                         return true;
169                 try {
170                         return (Boolean)getEnabled.invoke(source);
171                 } catch (IllegalAccessException e) {
172                         throw new BugException("getEnabled execution error for source "+source,e);
173                 } catch (InvocationTargetException e) {
174                         throw Reflection.handleWrappedException(e);
175                 }
176         }
177         
178 //      @Override
179 //      public Object getValue(String key) {
180 //              if (key.equals(SELECTED_KEY)) {
181 //                      return getValue();
182 //              }
183 //              return super.getValue(key);
184 //      }
185 //
186 //      @Override
187 //      public void putValue(String key, Object value) {
188 //              if (firing > 0)  // Ignore if currently firing event
189 //                      return;
190 //              if (key.equals(SELECTED_KEY) && (value instanceof Boolean)) {
191 //                      setValue((Boolean)value);
192 //              } else {
193 //                      super.putValue(key, value);
194 //              }
195 //              updateEnableStatus();
196 //      }
197         
198         
199         @Override
200         public void stateChanged(ChangeEvent event) {
201                 if (firing > 0)
202                         return;
203                 
204                 boolean v = getValue();
205                 boolean e = getIsEnabled();
206                 if (oldValue != v) {
207                         oldValue = v;
208                         firing++;
209                         this.putValue(SELECTED_KEY, getValue());
210 //                      this.firePropertyChange(SELECTED_KEY, !v, v);
211                         updateEnableStatus();
212                         firing--;
213                 }
214                 if (oldEnabled != e) {
215                         oldEnabled = e;
216                         setEnabled(e);
217                 }
218         }
219
220
221         @Override
222         public void actionPerformed(ActionEvent e) {
223                 if (firing > 0)
224                         return;
225                 
226                 boolean v = (Boolean)this.getValue(SELECTED_KEY);
227                 if (v != oldValue) {
228                         firing++;
229                         setValue(v);
230                         oldValue = getValue();
231                         // Update all states
232                         this.putValue(SELECTED_KEY, oldValue);
233                         this.setEnabled(getIsEnabled());
234                         updateEnableStatus();
235                         firing--;
236                 }
237         }
238         
239         @Override
240         public String toString() {
241                 return "BooleanModel["+source.getClass().getCanonicalName()+":"+valueName+"]";
242         }
243 }