ConfigDialog.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /**
  2. *
  3. */
  4. package jme3test.blender.config;
  5. import java.awt.event.ActionEvent;
  6. import java.awt.event.ActionListener;
  7. import java.io.File;
  8. import java.io.FileFilter;
  9. import java.io.IOException;
  10. import java.lang.reflect.Field;
  11. import java.lang.reflect.Modifier;
  12. import java.util.HashMap;
  13. import java.util.Map;
  14. import java.util.Map.Entry;
  15. import java.util.logging.Level;
  16. import java.util.logging.Logger;
  17. import javax.swing.DefaultComboBoxModel;
  18. import javax.swing.DefaultListModel;
  19. import javax.swing.JOptionPane;
  20. import javax.swing.SwingUtilities;
  21. import javax.swing.event.ListSelectionEvent;
  22. import javax.swing.event.ListSelectionListener;
  23. import javax.swing.event.TableModelEvent;
  24. import javax.swing.event.TableModelListener;
  25. import javax.swing.table.DefaultTableModel;
  26. import com.jme3.asset.BlenderKey;
  27. import com.jme3.asset.ModelKey;
  28. import com.jme3.export.InputCapsule;
  29. import com.jme3.export.JmeExporter;
  30. import com.jme3.export.JmeImporter;
  31. import com.jme3.export.OutputCapsule;
  32. import com.jme3.export.Savable;
  33. import com.jme3.export.binary.BinaryExporter;
  34. import com.jme3.export.binary.BinaryImporter;
  35. /**
  36. * A class that shows a dialog box for blender testing configuration.
  37. * @author Marcin Roguski (Kaelthas)
  38. */
  39. public class ConfigDialog extends AbstractConfigDialog {
  40. private static final long serialVersionUID = 2863364888664674247L;
  41. private static final Logger LOGGER = Logger.getLogger(ConfigDialog.class.getName());
  42. private String baseFolderName;
  43. private File configFile; //the config file
  44. private Map<String, BlenderKeyConfiguration> configMap; //the blender key configuration map
  45. private BlenderKeyConfiguration blenderKeyConfiguration;//the configuration for the files
  46. private IConfigExecutable configExecutable; //this is called after clicking the 'OK' button
  47. /**
  48. * Constructor. Builds the whole window and stores its data.
  49. * @param testAssetsFolderName the path to test files folder
  50. */
  51. public ConfigDialog(String baseFolderName, IConfigExecutable configExecutable) {
  52. if(baseFolderName==null) {
  53. throw new IllegalArgumentException("No test asset folder given!");
  54. }
  55. if(configExecutable==null) {
  56. throw new IllegalArgumentException("No config executable given!");
  57. }
  58. this.baseFolderName = baseFolderName;
  59. this.configExecutable = configExecutable;
  60. this.configMap = new HashMap<String, ConfigDialog.BlenderKeyConfiguration>();
  61. //setting up version selection (as a folder list in a compo box)
  62. File baseFolder = new File(baseFolderName);
  63. if(!baseFolder.exists() || !baseFolder.isDirectory()) {
  64. throw new IllegalArgumentException("The given base folder path either does not exists or does not point to a directory!");
  65. }
  66. File[] folders = baseFolder.listFiles(new FileFilter() {
  67. @Override
  68. public boolean accept(File file) {
  69. return file.isDirectory() && file.getName().charAt(0)!='.';
  70. }
  71. });
  72. for(File folder : folders) {
  73. ((DefaultComboBoxModel)jComboBoxVersionSelection.getModel()).addElement(folder.getName());
  74. configMap.put(folder.getName(), null);
  75. }
  76. this.initListeners();
  77. jComboBoxVersionSelection.setSelectedIndex(0);
  78. }
  79. /**
  80. * This method returns the selected blender key.
  81. * @return the selected blender key
  82. */
  83. public BlenderKey getSelectedBlenderKey() {
  84. return blenderKeyConfiguration.lastUsedKey;
  85. }
  86. /**
  87. * This method prepares the blender files' list.
  88. * @param testAssetsFolderName the path to test files folder
  89. * @return array of blender files
  90. */
  91. private File[] prepareFilesList(String testAssetsFolderName) {
  92. File testAssetsFolder = new File(testAssetsFolderName);
  93. //loading blender files
  94. File[] blenderFiles = testAssetsFolder.listFiles(new FileFilter() {
  95. @Override
  96. public boolean accept(File file) {
  97. return file.isFile() && file.canRead() && file.getName().endsWith(".blend");
  98. }
  99. });
  100. //loading the blender files configuration
  101. File[] files = testAssetsFolder.listFiles(new FileFilter() {
  102. @Override
  103. public boolean accept(File file) {
  104. return file.isFile() && file.canRead() && file.getName().endsWith(".conf");
  105. }
  106. });
  107. if (files == null || files.length == 0) {
  108. blenderKeyConfiguration = new BlenderKeyConfiguration(blenderFiles.length);
  109. } else {
  110. BinaryImporter jmeImporter = new BinaryImporter();
  111. String instructionToUser = files.length==1 ?
  112. "No other config file to load! No configuration set!" :
  113. "Please choose different config file!";
  114. do {
  115. if (files.length > 1) {
  116. configFile = (File) JOptionPane.showInputDialog(null, "Choose the config file!", "Config file selection",
  117. JOptionPane.INFORMATION_MESSAGE, null, files, files[0]);
  118. } else {
  119. configFile = files[0];
  120. }
  121. if(configFile==null) {
  122. JOptionPane.showMessageDialog(this, "No config file selected!\nEmpty configuration will be created!",
  123. "No configuration selected", JOptionPane.INFORMATION_MESSAGE);
  124. blenderKeyConfiguration = new BlenderKeyConfiguration(blenderFiles.length);
  125. } else {
  126. try {
  127. Savable loadedData = jmeImporter.load(configFile);
  128. if (loadedData instanceof BlenderKeyConfiguration) {
  129. blenderKeyConfiguration = (BlenderKeyConfiguration) loadedData;
  130. } else {
  131. LOGGER.warning("Cannot load data drom the given file!");
  132. JOptionPane.showMessageDialog(this, "The data stored in the config file is of invalid type!\n"
  133. + instructionToUser, "Config data error", JOptionPane.ERROR_MESSAGE);
  134. }
  135. } catch (IOException e) {
  136. JOptionPane.showMessageDialog(this, "Unable to load configuration! Reason: " + e.getLocalizedMessage(),
  137. "Loading data error", JOptionPane.ERROR_MESSAGE);
  138. LOGGER.severe("Unable to load configuration");
  139. } catch (Exception e) {
  140. JOptionPane.showMessageDialog(this, "Unable to load configuration!",
  141. "Loading data error", JOptionPane.ERROR_MESSAGE);
  142. LOGGER.log(Level.SEVERE, "Unable to load configuration due to unpredicted error!", e);
  143. }
  144. }
  145. } while (blenderKeyConfiguration == null && files.length>1);
  146. }
  147. configFile = new File(testAssetsFolder, "test.conf");
  148. jCheckBoxUseModelKey.setSelected(blenderKeyConfiguration.useModelKey);
  149. //enlisting the files in the list
  150. DefaultListModel defaultListModel = (DefaultListModel) jListBlenderFiles.getModel();
  151. defaultListModel.removeAllElements();
  152. for(int i=0; i<blenderFiles.length; ++i) {
  153. defaultListModel.addElement(new FileListItem(blenderFiles[i]));
  154. }
  155. return blenderFiles;
  156. }
  157. /**
  158. * This method fills the properties panel with blender key data.
  159. * @param blenderKey the belnder key data
  160. */
  161. private void setBlenderKey(BlenderKey blenderKey) {
  162. //setting properties
  163. BlenderTableModel propertiesModel = (BlenderTableModel) jTableProperties.getModel();
  164. int rowCount = propertiesModel.getRowCount();
  165. for(int i=0;i<rowCount;++i) {
  166. propertiesModel.removeRow(0);
  167. }
  168. Field[] fields = blenderKey.getClass().getDeclaredFields();
  169. for(Field field : fields) {
  170. field.setAccessible(true);
  171. if(!"animations".equalsIgnoreCase(field.getName()) &&
  172. (field.getModifiers() & Modifier.STATIC)==0) {
  173. try {
  174. propertiesModel.addRow(new Object[] {field.getName(), field.get(blenderKey)});
  175. } catch (IllegalArgumentException e) {
  176. LOGGER.log(Level.SEVERE, e.getMessage(), e);
  177. } catch (IllegalAccessException e) {
  178. LOGGER.log(Level.SEVERE, e.getMessage(), e);
  179. }
  180. }
  181. }
  182. //setting animations
  183. DefaultTableModel animationsModel = (DefaultTableModel) jTableAnimations.getModel();
  184. rowCount = animationsModel.getRowCount();
  185. for(int i=0;i<rowCount;++i) {
  186. animationsModel.removeRow(0);
  187. }
  188. Map<String, Map<String, int[]>> animations = blenderKey.getAnimations();
  189. if(animations!=null) {
  190. for(Entry<String, Map<String, int[]>> animationEntry : animations.entrySet()) {
  191. for(Entry<String, int[]> animDataEntry : animationEntry.getValue().entrySet()) {
  192. int[] frames = animDataEntry.getValue();
  193. animationsModel.addRow(new Object[] {animationEntry.getKey(), animDataEntry.getKey(),
  194. Integer.valueOf(frames[0]), Integer.valueOf(frames[1])});
  195. }
  196. }
  197. }
  198. this.jButtonOK.setEnabled(true);
  199. this.jButtonOK.requestFocusInWindow();
  200. this.jButtonAddAnimation.setEnabled(true);
  201. }
  202. /**
  203. * This method stores the current blender config.
  204. * @param configuration the blender config to store
  205. */
  206. private void storeConfig(BlenderKeyConfiguration configuration) {
  207. if(configuration.lastUsedKey!=null) {//reading animations
  208. DefaultTableModel animationsTableModel = (DefaultTableModel)jTableAnimations.getModel();
  209. if(configuration.lastUsedKey.getAnimations()!=null) {
  210. configuration.lastUsedKey.getAnimations().clear();
  211. }
  212. int animCounter = 0;
  213. for(int i=0;i<animationsTableModel.getRowCount();++i) {
  214. String objectName = (String)animationsTableModel.getValueAt(i, 0);
  215. String animName = (String)animationsTableModel.getValueAt(i, 1);
  216. Number startFrame = (Number)animationsTableModel.getValueAt(i, 2);
  217. Number stopFrame = (Number)animationsTableModel.getValueAt(i, 3);
  218. if(objectName!=null && animName!=null && startFrame.intValue()<=stopFrame.intValue()) {
  219. configuration.lastUsedKey.addAnimation(objectName, animName, startFrame.intValue(), stopFrame.intValue());
  220. ++animCounter;
  221. }
  222. }
  223. if(animCounter<animationsTableModel.getRowCount()) {
  224. JOptionPane.showMessageDialog(ConfigDialog.this, "Some animations had errors!\nThey had not been added!",
  225. "Invalid animations definitions", JOptionPane.WARNING_MESSAGE);
  226. }
  227. }
  228. //getting the key type
  229. configuration.useModelKey = jCheckBoxUseModelKey.isSelected();
  230. configuration.logLevel = JRadioButtonLevel.getSelectedLevel();
  231. //storing the config
  232. JmeExporter jmeExporter = new BinaryExporter();
  233. try {
  234. if(!jmeExporter.save(configuration, configFile)) {
  235. JOptionPane.showMessageDialog(ConfigDialog.this, "Unable to save the config data!", "Config save problem", JOptionPane.ERROR_MESSAGE);
  236. }
  237. } catch (IOException e) {
  238. JOptionPane.showMessageDialog(ConfigDialog.this, "Error occured during config saving!\nReason: " + e.getLocalizedMessage(),
  239. "Config save problem", JOptionPane.ERROR_MESSAGE);
  240. }
  241. }
  242. /**
  243. * This method initiates components listeners.
  244. */
  245. private void initListeners() {
  246. //selection of blender version
  247. jComboBoxVersionSelection.addActionListener(new ActionListener() {
  248. @Override
  249. public void actionPerformed(ActionEvent evt) {
  250. //save the previous congifuration
  251. if(blenderKeyConfiguration!=null) {
  252. ConfigDialog.this.storeConfig(blenderKeyConfiguration);
  253. blenderKeyConfiguration = null;
  254. }
  255. //load new configuration
  256. File[] blenderFiles = ConfigDialog.this.prepareFilesList(baseFolderName+'/'+jComboBoxVersionSelection.getSelectedItem().toString());
  257. if(blenderKeyConfiguration.lastUsedKey!=null) {
  258. for(int i=0;i<blenderFiles.length; ++i) {
  259. if(blenderFiles[i].getPath().equalsIgnoreCase(blenderKeyConfiguration.lastUsedKey.getName())) {
  260. jListBlenderFiles.setSelectedIndex(i);
  261. break;
  262. }
  263. }
  264. }
  265. if(blenderKeyConfiguration.logLevel==null) {
  266. blenderKeyConfiguration.logLevel = Level.INFO;
  267. }
  268. JRadioButtonLevel.setSelectedLevel(blenderKeyConfiguration.logLevel);
  269. }
  270. });
  271. //selection of the file changes the config on the right
  272. jListBlenderFiles.addListSelectionListener(new ListSelectionListener() {
  273. @Override
  274. public void valueChanged(ListSelectionEvent evt) {
  275. BlenderKeyConfiguration config = ConfigDialog.this.blenderKeyConfiguration;
  276. FileListItem selectedItem = (FileListItem) ConfigDialog.this.jListBlenderFiles.getSelectedValue();
  277. if(selectedItem != null) {
  278. String fileName = selectedItem.getFile().getName();
  279. config.lastUsedKey = config.blenderKeys.get(fileName);
  280. if(config.lastUsedKey==null) {
  281. config.lastUsedKey = new BlenderKey(selectedItem.getFile().getPath());
  282. config.blenderKeys.put(fileName, config.lastUsedKey);
  283. }
  284. ConfigDialog.this.setBlenderKey(config.lastUsedKey);
  285. } else {
  286. config.lastUsedKey = null;
  287. }
  288. }
  289. });
  290. jTableProperties.getModel().addTableModelListener(new TableModelListener() {
  291. @Override
  292. public void tableChanged(TableModelEvent evt) {
  293. if(evt.getType()==TableModelEvent.UPDATE) {
  294. BlenderKeyConfiguration config = ConfigDialog.this.blenderKeyConfiguration;
  295. int row = evt.getFirstRow();
  296. String name = (String)jTableProperties.getModel().getValueAt(row, 0);
  297. Object value = jTableProperties.getModel().getValueAt(row, 1);
  298. try {
  299. Field field = config.lastUsedKey.getClass().getDeclaredField(name);
  300. field.setAccessible(true);
  301. field.set(config.lastUsedKey, value);
  302. } catch (IllegalArgumentException e) {
  303. LOGGER.log(Level.SEVERE, e.getMessage(), e);
  304. } catch (SecurityException e) {
  305. LOGGER.log(Level.SEVERE, e.getMessage(), e);
  306. } catch (IllegalAccessException e) {
  307. LOGGER.log(Level.SEVERE, e.getMessage(), e);
  308. } catch (NoSuchFieldException e) {
  309. LOGGER.log(Level.SEVERE, e.getMessage(), e);
  310. }
  311. }
  312. }
  313. });
  314. jTableAnimations.getModel().addTableModelListener(new TableModelListener() {
  315. @Override
  316. public void tableChanged(TableModelEvent evt) {
  317. if(evt.getType()==TableModelEvent.INSERT) {
  318. jButtonRemoveAnimation.setEnabled(true);
  319. } else if(evt.getType()==TableModelEvent.DELETE && jTableAnimations.getModel().getRowCount()==0) {
  320. jButtonRemoveAnimation.setEnabled(false);
  321. }
  322. }
  323. });
  324. jButtonAddAnimation.addActionListener(new ActionListener() {
  325. @Override
  326. public void actionPerformed(ActionEvent evt) {
  327. ((DefaultTableModel)jTableAnimations.getModel()).addRow(new Object[] {"", "", Integer.valueOf(-1), Integer.valueOf(-1)});
  328. }
  329. });
  330. jButtonRemoveAnimation.addActionListener(new ActionListener() {
  331. @Override
  332. public void actionPerformed(ActionEvent evt) {
  333. int row = jTableAnimations.getSelectedRow();
  334. if(row>=0) {
  335. ((DefaultTableModel)jTableAnimations.getModel()).removeRow(row);
  336. }
  337. }
  338. });
  339. //button listeners
  340. jButtonOK.addActionListener(new ActionListener() {
  341. @Override
  342. public void actionPerformed(ActionEvent evt) {
  343. ConfigDialog.this.storeConfig(blenderKeyConfiguration);
  344. //running the test
  345. SwingUtilities.invokeLater(new Runnable() {
  346. @Override
  347. public void run() {
  348. configExecutable.execute(ConfigDialog.this.blenderKeyConfiguration.getKeyToUse(),
  349. ConfigDialog.this.blenderKeyConfiguration.logLevel);
  350. }
  351. });
  352. //disposing the config window
  353. ConfigDialog.this.dispose();
  354. }
  355. });
  356. jButtonCancel.addActionListener(new ActionListener() {
  357. @Override
  358. public void actionPerformed(ActionEvent evt) {
  359. ConfigDialog.this.dispose();
  360. }
  361. });
  362. }
  363. /**
  364. * This class holds the configuration for all the files.
  365. * It can be saved and loaded using jme mechanisms.
  366. * @author Marcin Roguski (Kaelthas)
  367. */
  368. public static class BlenderKeyConfiguration implements Savable {
  369. private Map<String, BlenderKey> blenderKeys;
  370. private BlenderKey lastUsedKey;
  371. private Level logLevel;
  372. private boolean useModelKey;
  373. /**
  374. * Constructor for jme serialization.
  375. */
  376. public BlenderKeyConfiguration() {}
  377. /**
  378. * Constructor that creates new empty configuration for every blender file.
  379. * @param blenderFilesAmount the amount of blender files
  380. */
  381. public BlenderKeyConfiguration(int blenderFilesAmount) {
  382. blenderKeys = new HashMap<String, BlenderKey>(blenderFilesAmount);
  383. }
  384. /**
  385. * This method returns the key that will be used during the test.
  386. * @return the key that will be used during the test
  387. */
  388. public ModelKey getKeyToUse() {
  389. return useModelKey ? new ModelKey(lastUsedKey.getName()) : lastUsedKey;
  390. }
  391. @Override
  392. public void write(JmeExporter ex) throws IOException {
  393. OutputCapsule oc = ex.getCapsule(this);
  394. oc.writeStringSavableMap(blenderKeys, "keys", null);
  395. oc.write(lastUsedKey, "last-key", null);
  396. oc.write(useModelKey, "use-model-key", false);
  397. oc.write(logLevel==null ? null : logLevel.getName(), "log-level", Level.INFO.getName());
  398. }
  399. @Override
  400. @SuppressWarnings("unchecked")
  401. public void read(JmeImporter im) throws IOException {
  402. InputCapsule ic = im.getCapsule(this);
  403. blenderKeys = (Map<String, BlenderKey>) ic.readStringSavableMap("keys", null);
  404. lastUsedKey = (BlenderKey) ic.readSavable("last-key", null);
  405. useModelKey = ic.readBoolean("use-model-key", false);
  406. String logLevelName = ic.readString("log-level", Level.INFO.getName());
  407. logLevel = logLevelName==null ? Level.INFO : Level.parse(logLevelName);
  408. }
  409. }
  410. }