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