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