/* * This software copyright by various authors including the RPTools.net * development team, and licensed under the LGPL Version 3 or, at your option, * any later version. * * Portions of this software were originally covered under the Apache Software * License, Version 1.1 or Version 2.0. * * See the file LICENSE elsewhere in this distribution for license details. */ package net.rptools.maptool.client.ui.campaignproperties; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.swing.AbstractListModel; import javax.swing.JButton; import javax.swing.JList; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import net.rptools.CaseInsensitiveHashMap; import net.rptools.maptool.client.MapTool; import net.rptools.maptool.client.swing.AbeillePanel; import net.rptools.maptool.language.I18N; import net.rptools.maptool.model.Campaign; import net.rptools.maptool.model.CampaignProperties; import net.rptools.maptool.model.TokenProperty; public class TokenPropertiesManagementPanel extends AbeillePanel<CampaignProperties> { private Map<String, List<TokenProperty>> tokenTypeMap; private String editingType; public TokenPropertiesManagementPanel() { super("net/rptools/maptool/client/ui/forms/tokenPropertiesManagementPanel.xml"); panelInit(); } public void copyCampaignToUI(CampaignProperties campaignProperties) { tokenTypeMap = new HashMap<String, List<TokenProperty>>(campaignProperties.getTokenTypeMap()); updateTypeList(); } public void copyUIToCampaign(Campaign campaign) { campaign.getTokenTypeMap().clear(); campaign.getTokenTypeMap().putAll(tokenTypeMap); } public JList getTokenTypeList() { JList list = (JList) getComponent("tokenTypeList"); if (list == null) { list = new JList(); } return list; } public JTextField getTokenTypeName() { return (JTextField) getComponent("tokenTypeName"); } public JButton getNewButton() { return (JButton) getComponent("newButton"); } public JButton getUpdateButton() { return (JButton) getComponent("updateButton"); } public JButton getRevertButton() { return (JButton) getComponent("revertButton"); } public JTextArea getTokenPropertiesArea() { return (JTextArea) getComponent("tokenProperties"); } public void initUpdateButton() { getUpdateButton().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { update(); } }); } public void initNewButton() { getNewButton().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { EventQueue.invokeLater(new Runnable() { public void run() { // This will force a reset getTokenTypeList().getSelectionModel().clearSelection(); reset(); } }); } }); } public void initRevertButton() { getRevertButton().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { bind(editingType); } }); } public void initTypeList() { getTokenTypeList().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } if (getTokenTypeList().getSelectedValue() == null) { reset(); } else { bind((String) getTokenTypeList().getSelectedValue()); } } }); getTokenTypeList().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); } private void bind(String type) { editingType = type; getTokenTypeName().setText(type != null ? type : ""); getTokenTypeName().setEditable(!CampaignProperties.DEFAULT_TOKEN_PROPERTY_TYPE.equals(type)); getTokenPropertiesArea().setText(type != null ? compileTokenProperties(tokenTypeMap.get(type)) : ""); } private void update() { // Pull the old one out and put the new one in (rename) List<TokenProperty> current; try { // If an exception occurs here, the GUI goes back into editing of the text. current = parseTokenProperties(getTokenPropertiesArea().getText()); tokenTypeMap.remove(editingType); tokenTypeMap.put(getTokenTypeName().getText().trim(), current); reset(); updateTypeList(); } catch (IllegalArgumentException e) { // Don't need to do anything here... } } private void reset() { bind((String) null); } private void updateTypeList() { getTokenTypeList().setModel(new TypeListModel()); } private String compileTokenProperties(List<TokenProperty> propertyList) { // Sanity if (propertyList == null) { return ""; } StringBuilder builder = new StringBuilder(); for (TokenProperty property : propertyList) { if (property.isShowOnStatSheet()) { builder.append("*"); } if (property.isOwnerOnly()) { builder.append("@"); } if (property.isGMOnly()) { builder.append("#"); } builder.append(property.getName()); if (property.getShortName() != null) { builder.append(" (").append(property.getShortName()).append(")"); } if (property.getDefaultValue() != null) { builder.append(":").append(property.getDefaultValue()); } builder.append("\n"); } return builder.toString(); } /** * Given a string (normally from the JTextArea which holds the properties for a Property Type) * this method converts those lines into a List of EditTokenProperty objects. It checks for * duplicates along the way, ignoring any it finds. (Should produce a list of warnings to * indicate which ones are duplicates. See the Light/Sight code for examples.) * * @param propertyText * @return */ private List<TokenProperty> parseTokenProperties(String propertyText) throws IllegalArgumentException { List<TokenProperty> propertyList = new ArrayList<TokenProperty>(); BufferedReader reader = new BufferedReader(new StringReader(propertyText)); CaseInsensitiveHashMap<String> caseCheck = new CaseInsensitiveHashMap<String>(); List<String> errlog = new LinkedList<String>(); try { String original, line; while ((original = reader.readLine()) != null) { line = original = original.trim(); if (line.length() == 0) { continue; } TokenProperty property = new TokenProperty(); // Prefix while (true) { if (line.startsWith("*")) { property.setShowOnStatSheet(true); line = line.substring(1); continue; } if (line.startsWith("@")) { property.setOwnerOnly(true); line = line.substring(1); continue; } if (line.startsWith("#")) { property.setGMOnly(true); line = line.substring(1); continue; } // Ran out of special characters break; } // default value // had to do this here since the short name is not built // to take advantage of multiple opening/closing parenthesis // in a single property line int indexDefault = line.indexOf(":"); if (indexDefault > 0) { String defaultVal = line.substring(indexDefault + 1).trim(); if (defaultVal.length() > 0) { property.setDefaultValue(defaultVal); } //remove the default value from the end of the string... line = line.substring(0, indexDefault); } // Suffix // (Really should handle nested parens here) int index = line.indexOf("("); if (index > 0) { String shortName = line.substring(index + 1, line.lastIndexOf(")")).trim(); if (shortName.length() > 0) { property.setShortName(shortName); } line = line.substring(0, index).trim(); } property.setName(line); // Since property names are not case-sensitive, let's make sure that we don't // already have this name represented somewhere in the list. String old = caseCheck.get(line); if (old != null) { // Perhaps these properties should produce warnings at all, but what it someone // is actually <b>using them as property names!</b> if (old.startsWith("---")) errlog.add(I18N.getText("msg.error.mtprops.properties.duplicateComment", original, old)); else errlog.add(I18N.getText("msg.error.mtprops.properties.duplicate", original, old)); } else { propertyList.add(property); caseCheck.put(line, original); } } } catch (IOException ioe) { // If this happens, I'll check into the nearest insane asylum MapTool.showError("IOException during parsing of properties?!", ioe); } caseCheck.clear(); if (!errlog.isEmpty()) { errlog.add(0, I18N.getText("msg.error.mtprops.properties.title", editingType)); errlog.add(I18N.getText("msg.error.mtprops.properties.ending")); MapTool.showFeedback(errlog.toArray()); errlog.clear(); throw new IllegalArgumentException(); // Don't save the properties... } return propertyList; } private class TypeListModel extends AbstractListModel { public Object getElementAt(int index) { List<String> names = new ArrayList<String>(tokenTypeMap.keySet()); Collections.sort(names); return names.get(index); } public int getSize() { return tokenTypeMap.size(); } } }