1 // Copyright Hugh Perkins 2009
2 // hughperkins@gmail.com http://manageddreams.com
3 //
4 // This program is free software; you can redistribute it and/or modify it
5 // under the terms of the GNU General Public License as published by the
6 // Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 //  more details.
13 //
14 // You should have received a copy of the GNU General Public License along
15 // with this program in the file licence.txt; if not, write to the
16 // Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-
17 // 1307 USA
18 // You can find the licence also on the web at:
19 // http://www.opensource.org/licenses/gpl-license.php
20 //
21 // ======================================================================================
22 //
23 
24 package hughai.ui;
25 
26 import java.awt.*;
27 import java.awt.event.*;
28 
29 import java.lang.reflect.*;
30 
31 import javax.swing.*;
32 
33 import java.util.*;
34 import java.util.Map;
35 import java.lang.annotation.*;
36 
37 import com.springrts.ai.*;
38 import com.springrts.ai.oo.clb.*;
39 
40 import hughai.*;
41 import hughai.EnemyTracker.EnemyAdapter;
42 import hughai.basictypes.*;
43 import hughai.mapping.*;
44 import hughai.packcoordinators.*;
45 import hughai.unitdata.*;
46 import hughai.unitdata.UnitController.UnitAdapter;
47 import hughai.utils.*;
48 import hughai.utils.ConfigController.ConfigSource;
49 
50 // builds the configuration tab in the gui
51 // using reflection
52 // supports most primitive types
53 // todo: lists, custom classes, lists of custom classes..
54 public class ConfigDialog {
55    PlayerObjects playerObjects;
56 
57    JPanel configPanel;
58    GridLayout configGridLayout;
59 
ConfigDialog( PlayerObjects playerObjects )60    public ConfigDialog( PlayerObjects playerObjects ) {
61       this.playerObjects = playerObjects;
62       Init();
63    }
64 
Init()65    public void Init() {
66       configGridLayout = new GridLayout(0,2);
67       configPanel = new JPanel( configGridLayout );
68 
69       playerObjects.getMainUI().addPanelToTabbedPanel( "Config", configPanel );
70 
71       buildConfigPanel();
72    }
getGetMethod( Class<?> targetClass, Class<?> fieldType, String fieldName )73    Method getGetMethod( Class<?> targetClass, Class<?> fieldType, String fieldName ) {
74       fieldName = fieldName.substring( 0, 1 ).toUpperCase()
75       + fieldName.substring( 1 );
76       String methodname = "get" + fieldName;
77       if( fieldType == boolean.class || fieldType == Boolean.class ) {
78          methodname = "is" + fieldName;
79       }
80       Method method = null;
81       try {
82          method = targetClass.getMethod( methodname, new Class<?>[0] );
83       } catch( Exception e ) {
84          playerObjects.getLogFile().WriteLine( Formatting.exceptionToStackTrace( e ) );
85       }
86       return method;
87    }
88 
getSetMethod( Class<?> targetClass, Class<?> fieldType, String fieldName )89    Method getSetMethod( Class<?> targetClass, Class<?> fieldType, String fieldName ) {
90       fieldName = fieldName.substring( 0, 1 ).toUpperCase()
91       + fieldName.substring( 1 );
92       String methodname = "set" + fieldName;
93       Method method = null;
94       try {
95          method = targetClass.getMethod( methodname, new Class<?>[]{ fieldType } );
96       } catch( Exception e ) {
97          playerObjects.getLogFile().WriteLine( Formatting.exceptionToStackTrace( e ) );
98       }
99       return method;
100    }
101 
buildConfigPanel()102    void buildConfigPanel() {
103       try {
104          Config config = playerObjects.getConfig();
105          for( Field field : config.getClass().getDeclaredFields() ) {
106             Annotation excludeAnnotation = field.getAnnotation( ReflectionHelper.Exclude.class );
107             if( excludeAnnotation == null ) { // so, this field is not excluded
108                Class<?> fieldType = field.getType();
109                Method getMethod = getGetMethod( config.getClass(), field.getType(), field.getName() );
110                if( getMethod != null ) {
111                   Object value = getMethod.invoke( config );
112                   if( fieldType == String.class ) {
113                      addTextBox( field.getName(), (String)value );
114                   }
115                   if( fieldType == boolean.class || fieldType == Boolean.class ) {
116                      addBooleanComponent( field.getName(), (Boolean)value );
117                   }
118                   if( fieldType == float.class || fieldType == Float.class ) {
119                      addTextBox( field.getName(), "" + value );
120                   }
121                   if( fieldType == int.class || fieldType == Integer.class ) {
122                      addTextBox( field.getName(), "" + value );
123                   }
124                } else {
125                   playerObjects.getLogFile().WriteLine( "No get accessor method for config field " + field.getName() );
126                }
127             }
128          }
129       } catch( Exception e ) {
130          playerObjects.getLogFile().WriteLine( Formatting.exceptionToStackTrace( e ) );
131       }
132 
133       configGridLayout.setRows( configGridLayout.getRows() + 2 );
134 
135       configRevertButton = new JButton( "Revert" );
136       configReloadButton = new JButton( "Reload" );
137       configApplyButton = new JButton( "Apply" );
138       configSaveButton = new JButton( "Save" );
139 
140       configRevertButton.addActionListener( new ConfigRevert() );
141       configReloadButton.addActionListener( new ConfigReload() );
142       configApplyButton.addActionListener( new ConfigApply() );
143       configSaveButton.addActionListener( new ConfigSave() );
144 
145       configPanel.add( configRevertButton );
146       configPanel.add( configReloadButton );
147       configPanel.add( configApplyButton );
148       configPanel.add( configSaveButton );
149    }
150 
151    JButton configRevertButton;
152    JButton configReloadButton;
153    JButton configApplyButton;
154    JButton configSaveButton;
155    HashMap< String, JComponent > componentByName = new HashMap<String, JComponent>();
156 
addTextBox( String name, String currentValue )157    void addTextBox( String name, String currentValue ) {
158       configGridLayout.setRows( configGridLayout.getRows() + 1 );
159       addConfigLabel( name );
160 
161       JTextField textField = new JTextField();
162       textField.setText( currentValue );
163 
164       componentByName.put(  name, textField );
165       configPanel.add( textField );
166    }
167 
addConfigLabel( String labelName )168    void addConfigLabel( String labelName ) {
169       JLabel label = new JLabel( labelName );
170       configPanel.add( label );
171    }
172 
addBooleanComponent( String name, boolean currentValue )173    void addBooleanComponent( String name, boolean currentValue ) {
174       configGridLayout.setRows( configGridLayout.getRows() + 1 );
175       addConfigLabel( name );
176 
177       JCheckBox checkBox = new JCheckBox();
178       checkBox.setSelected( currentValue );
179 
180       componentByName.put(  name, checkBox );
181       configPanel.add( checkBox );
182    }
183 
debug( Object message )184    void debug( Object message ) {
185       playerObjects.getLogFile().WriteLine( "" + message );
186    }
187 
188    class ConfigRevert implements ActionListener {
189       @Override
actionPerformed( ActionEvent event )190       public void actionPerformed( ActionEvent event ){
191          revertConfig();
192       }
193    }
194 
revertConfig()195    void revertConfig() {
196       try {
197          debug("reverting config panel");
198          Config config = playerObjects.getConfig();
199          for( Field field : config.getClass().getDeclaredFields() ) {
200             Annotation excludeAnnotation = field.getAnnotation( ReflectionHelper.Exclude.class );
201             if( excludeAnnotation == null ) { // so, this field is not excluded
202                debug("field " + field.getName() );
203                Class<?> fieldType = field.getType();
204                Method getMethod = getGetMethod( config.getClass(), field.getType(), field.getName() );
205                if( getMethod != null ) {
206                   debug(" ... found accessor method" );
207                   Object value = getMethod.invoke( config );
208                   String fieldname = field.getName();
209                   Component component = componentByName.get( fieldname );
210                   if( component != null ) {
211                      debug(" ... found component" );
212                      if( fieldType == String.class ) {
213                         ((JTextField)component).setText( (String )value );
214                      }
215                      if( fieldType == boolean.class || fieldType == Boolean.class ) {
216                         ((JCheckBox)component).setSelected( (Boolean )value );
217                      }
218                      if( fieldType == float.class || fieldType == Float.class ) {
219                         ((JTextField)component).setText( "" + value );
220                      }
221                      if( fieldType == int.class || fieldType == Integer.class ) {
222                         ((JTextField)component).setText( "" + value );
223                      }
224                   }
225                } else {
226                   playerObjects.getLogFile().WriteLine( "No get accessor method for config field " + field.getName() );
227                }
228             }
229          }
230       } catch( Exception e ) {
231          playerObjects.getLogFile().WriteLine( Formatting.exceptionToStackTrace( e ) );
232       }
233    }
234 
235    class ConfigApply implements ActionListener {
236       @Override
actionPerformed( ActionEvent event )237       public void actionPerformed( ActionEvent event ){
238          applyConfig();
239       }
240    }
241 
applyConfig()242    void applyConfig() {
243       debug("applying config from panel");
244       Config config = playerObjects.getConfig();
245       for( Field field : config.getClass().getDeclaredFields() ) {
246          Annotation excludeAnnotation = field.getAnnotation( ReflectionHelper.Exclude.class );
247          if( excludeAnnotation == null ) { // so, this field is not excluded
248             debug("field " + field.getName() );
249             Class<?> fieldType = field.getType();
250             Method setMethod = getSetMethod( config.getClass(), field.getType(), field.getName() );
251             if( setMethod != null ) {
252                debug(" ... found accessor method" );
253                String fieldname = field.getName();
254                Component component = componentByName.get( fieldname );
255                if( component != null ) {
256                   debug(" ... found component" );
257                   Object value = null;
258                   if( fieldType == String.class ) {
259                      value = ((JTextField)component).getText();
260                   }
261                   if( fieldType == boolean.class || fieldType == Boolean.class ) {
262                      value = ((JCheckBox)component).isSelected();
263                   }
264                   if( fieldType == float.class || fieldType == Float.class ) {
265                      String stringvalue = (String)((JTextField)component).getText();
266                      try {
267                         value = Float.parseFloat( stringvalue );
268                      } catch( Exception e ) {
269                      }
270                   }
271                   if( fieldType == int.class || fieldType == Integer.class ) {
272                      String stringvalue = (String)((JTextField)component).getText();
273                      try {
274                         value = Integer.parseInt( stringvalue );
275                      } catch( Exception e ) {
276                      }
277                   }
278                   if( value != null ) {
279                      try {
280                         setMethod.invoke( config, value );
281                      } catch( Exception e ) {
282                         playerObjects.getLogFile().WriteLine( Formatting.exceptionToStackTrace( e ) );
283                      }
284                   }
285                }
286                if( fieldType == boolean.class || fieldType == Boolean.class ) {
287                   //addBooleanComponent( field.getName(), (Boolean)value );
288                }
289             } else {
290                playerObjects.getLogFile().WriteLine( "No get accessor method for config field " + field.getName() );
291             }
292          }
293       }
294       revertConfig(); // in case some parses and stuff didn't work, so
295                       // user can see what is actually being read.
296       playerObjects.getMainUI().showInfo( "Config updated.  Note that most changes require an AI restart.  You can click on 'reloadAI' in 'Actions' tab to do so." );
297       playerObjects.getConfig().configUpdated();
298    }
299 
300    class ConfigSave implements ActionListener {
301       @Override
actionPerformed( ActionEvent e )302       public void actionPerformed( ActionEvent e ){
303          applyConfig();
304          playerObjects.getConfigController().writeConfigBackToSource( ConfigSource.XmlFile );
305 //         playerObjects.getConfig().save();
306       }
307    }
308 
309    class ConfigReload implements ActionListener {
310       @Override
actionPerformed( ActionEvent e )311       public void actionPerformed( ActionEvent e ){
312          //playerObjects.getConfig().reload();
313          playerObjects.getConfigController().restoreFromSource( ConfigSource.WorkingCopy );
314          playerObjects.getConfig().configUpdated();
315          revertConfig();
316       }
317    }
318 }
319