diff --git a/release/common/VERSION HISTORY.txt b/release/common/VERSION HISTORY.txt
index aa7373658..19204264a 100644
--- a/release/common/VERSION HISTORY.txt
+++ b/release/common/VERSION HISTORY.txt
@@ -1,22 +1,22 @@
- BEAST v2.1.3 2014
- Beast 2 development team 2011-2014
+ BEAST v2.1.3 2015
+ Beast 2 development team 2011-2015
Version History
Last updated: April 2014
All issues can be viewed at https://github.com/CompEvol/beast2/issues
================================================================================
Version 2.2.0 January 2015
- Uses Java 8 to facilitate package development, prevent version clashes with packages
+ Uses Java 8 to facilitate package development, thus preventing version clashes with packages
BEAUti default settings preventing common errors, template fixes, layout enhancements, error logging
- Enhanced package manager, smaller install
+ Enhanced package manager
Better error reporting for finding common issues with XML files (such as *BEAST gene trees not covering all species)
Improved install for Mac
- Over 270 commits for small bug fixes and performance enhancements (more readable XML, better NEXUS parsing, TreeAnnotator fix, etc.)
+ Over 300 commits for small bug fixes and performance enhancements (more readable XML, better NEXUS parsing, TreeAnnotator fix, etc.)
Version 2.1.3 May 2014
Patch release
diff --git a/src/beast/app/BEASTVersion.java b/src/beast/app/BEASTVersion.java
index 37ce266c9..cbc5b7572 100644
--- a/src/beast/app/BEASTVersion.java
+++ b/src/beast/app/BEASTVersion.java
@@ -21,9 +21,9 @@ public class BEASTVersion extends Version {
*/
private static final String VERSION = "2.2.0";
- private static final String DATE_STRING = "2002-2014";
+ private static final String DATE_STRING = "2002-2015";
- private static final boolean IS_PRERELEASE = true;
+ private static final boolean IS_PRERELEASE = false;
private static final String BEAST2_WEBPAGE = "http://beast2.org/";
diff --git a/src/beast/app/draw/DoubleListInputEditor.java b/src/beast/app/draw/DoubleListInputEditor.java
new file mode 100644
index 000000000..974afc40d
--- /dev/null
+++ b/src/beast/app/draw/DoubleListInputEditor.java
@@ -0,0 +1,406 @@
+package beast.app.draw;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+import javax.swing.border.Border;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionListener;
+
+import beast.app.beauti.BeautiDoc;
+import beast.app.beauti.BeautiPanel;
+import beast.app.beauti.BeautiPanelConfig;
+import beast.core.BEASTInterface;
+import beast.core.Input;
+
+
+public class DoubleListInputEditor extends ListInputEditor {
+ private static final long serialVersionUID = 1L;
+
+ public DoubleListInputEditor(BeautiDoc doc) {
+ super(doc);
+ }
+
+ @Override
+ public Class> type() {
+ return List.class;
+ }
+
+ /**
+ * return type of the list *
+ */
+ public Class> baseType() {
+ return Double.class;
+ }
+
+
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void initEntry() {
+ if (m_input.get() != null) {
+ String str = "";
+ for (Double d : (List) m_input.get()) {
+ str += d + " ";
+ }
+ str = str.substring(0, str.length() - 1);
+ m_entry.setText(str);
+ }
+ }
+
+ /**
+ * the input to be edited *
+ */
+ protected Input> m_input;
+
+ /**
+ * parent plugin *
+ */
+ protected BEASTInterface m_plugin;
+
+ /**
+ * text field used for primitive input editors *
+ */
+ protected JTextField m_entry;
+
+ protected int itemNr;
+
+ public JTextField getEntry() {
+ return m_entry;
+ }
+
+ JLabel m_inputLabel;
+ protected static Dimension PREFERRED_SIZE = new Dimension(200, 25);
+ protected static Dimension MAX_SIZE = new Dimension(1024, 25);
+
+ /**
+ * flag to indicate label, edit and validate buttons/labels should be added *
+ */
+ protected boolean m_bAddButtons = true;
+
+ /**
+ * label that shows up when validation fails *
+ */
+ protected SmallLabel m_validateLabel;
+
+ /**
+ * document that we are editing *
+ */
+ protected BeautiDoc doc;
+
+ /**
+ * list of objects that want to be notified of the validation state when it changes *
+ */
+ List m_validateListeners;
+
+ public void addValidationListener(InputEditor validateListener) {
+ if (m_validateListeners == null) {
+ m_validateListeners = new ArrayList();
+ }
+ m_validateListeners.add(validateListener);
+ }
+
+ public void notifyValidationListeners(ValidationStatus state) {
+ if (m_validateListeners != null) {
+ for (InputEditor listener : m_validateListeners) {
+ listener.startValidating(state);
+ }
+ }
+ }
+
+ protected BeautiDoc getDoc() {
+ if (doc == null) {
+ Component c = this;
+ while (c.getParent() != null) {
+ c = c.getParent();
+ if (c instanceof BeautiPanel) {
+ doc = ((BeautiPanel) c).getDoc();
+ }
+ }
+ }
+ return doc;
+ }
+
+
+ public Class>[] types() {
+ Class>[] types = new Class>[1];
+ types[0] = type();
+ return types;
+ }
+
+ /**
+ * construct an editor consisting of a label and input entry *
+ */
+ public void init(Input> input, BEASTInterface plugin, int itemNr, ExpandOption bExpandOption, boolean bAddButtons) {
+ m_bAddButtons = bAddButtons;
+ m_input = input;
+ m_plugin = plugin;
+ this.itemNr= itemNr;
+
+ addInputLabel();
+
+ setUpEntry();
+
+ add(m_entry);
+ add(Box.createHorizontalGlue());
+ addValidationLabel();
+ } // init
+
+ void setUpEntry() {
+ m_entry = new JTextField();
+ m_entry.setName(m_input.getName());
+ m_entry.setMinimumSize(PREFERRED_SIZE);
+ m_entry.setPreferredSize(PREFERRED_SIZE);
+ m_entry.setSize(PREFERRED_SIZE);
+ initEntry();
+ m_entry.setToolTipText(m_input.getHTMLTipText());
+ m_entry.setMaximumSize(MAX_SIZE);
+
+ m_entry.getDocument().addDocumentListener(new DocumentListener() {
+ @Override
+ public void removeUpdate(DocumentEvent e) {
+ processEntry();
+ }
+
+ @Override
+ public void insertUpdate(DocumentEvent e) {
+ processEntry();
+ }
+
+ @Override
+ public void changedUpdate(DocumentEvent e) {
+ processEntry();
+ }
+ });
+ }
+
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ protected void setValue(Object o) throws Exception {
+ if (itemNr < 0) {
+ m_input.setValue(o, m_plugin);
+ } else {
+ // set value of an item in a list
+ List list = (List) m_input.get();
+ Object other = list.get(itemNr);
+ if (other != o) {
+ if (other instanceof BEASTInterface) {
+ BEASTInterface.getOutputs(other).remove(m_plugin);
+ }
+ list.set(itemNr, o);
+ if (o instanceof BEASTInterface) {
+ BEASTInterface.getOutputs(o).add(m_plugin);
+ }
+ }
+ }
+ }
+
+ protected void processEntry() {
+ try {
+ setValue(m_entry.getText());
+ validateInput();
+ m_entry.requestFocusInWindow();
+ } catch (Exception ex) {
+// JOptionPane.showMessageDialog(null, "Error while setting " + m_input.getName() + ": " + ex.getMessage() +
+// " Leaving value at " + m_input.get());
+// m_entry.setText(m_input.get() + "");
+ if (m_validateLabel != null) {
+ m_validateLabel.setVisible(true);
+ m_validateLabel.setToolTipText("Parsing error: " + ex.getMessage() + ". Value was left at " + m_input.get() + ".
");
+ m_validateLabel.m_circleColor = Color.orange;
+ }
+ repaint();
+ }
+ }
+
+ protected void addInputLabel() {
+ if (m_bAddButtons) {
+ String sName = formatName(m_input.getName());
+ addInputLabel(sName, m_input.getHTMLTipText());
+ }
+ }
+
+ protected String formatName(String sName) {
+ if (doc.beautiConfig.inputLabelMap.containsKey(m_plugin.getClass().getName() + "." + sName)) {
+ sName = doc.beautiConfig.inputLabelMap.get(m_plugin.getClass().getName() + "." + sName);
+ } else {
+ sName = sName.replaceAll("([a-z])([A-Z])", "$1 $2");
+ sName = sName.substring(0, 1).toUpperCase() + sName.substring(1);
+ }
+ return sName;
+ }
+
+ protected void addInputLabel(String sLabel, String sTipText) {
+ if (m_bAddButtons) {
+ m_inputLabel = new JLabel(sLabel);
+ m_inputLabel.setToolTipText(sTipText);
+ m_inputLabel.setHorizontalTextPosition(JLabel.RIGHT);
+ //Dimension size = new Dimension(g_nLabelWidth, 20);
+ Dimension size = new Dimension(200, 20);
+ m_inputLabel.setMaximumSize(size);
+ m_inputLabel.setMinimumSize(size);
+ m_inputLabel.setPreferredSize(size);
+ m_inputLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
+
+// m_inputLabel.setSize(size);
+// m_inputLabel.setBorder(BorderFactory.createLineBorder(Color.RED, 2));
+ // RRB: temporary
+ //m_inputLabel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
+ add(m_inputLabel);
+ }
+ }
+
+ protected void addValidationLabel() {
+ if (m_bAddButtons) {
+ m_validateLabel = new SmallLabel("x", new Color(200, 0, 0));
+ add(m_validateLabel);
+ m_validateLabel.setVisible(true);
+ validateInput();
+ }
+ }
+
+ /* check the input is valid, continue checking recursively */
+ protected void validateAllEditors() {
+ for (InputEditor editor : doc.currentInputEditors) {
+ editor.validateInput();
+ }
+ }
+
+ @Override
+ public void validateInput() {
+ try {
+ m_input.validate();
+ if (m_entry != null && !m_input.canSetValue(m_entry.getText(), m_plugin)) {
+ throw new Exception("invalid value");
+ }
+ // recurse
+ try {
+ validateRecursively(m_input, new HashSet>());
+ } catch (Exception e) {
+ notifyValidationListeners(ValidationStatus.HAS_INVALIDMEMBERS);
+ if (m_validateLabel != null) {
+ m_validateLabel.setVisible(true);
+ m_validateLabel.setToolTipText("Recursive error in " + e.getMessage() + "
");
+ m_validateLabel.m_circleColor = Color.orange;
+ }
+ repaint();
+ return;
+ }
+ if (m_validateLabel != null) {
+ m_validateLabel.setVisible(false);
+ }
+ notifyValidationListeners(ValidationStatus.IS_VALID);
+ } catch (Exception e) {
+ System.err.println("Validation message: " + e.getMessage());
+ if (m_validateLabel != null) {
+ m_validateLabel.setToolTipText(e.getMessage());
+ m_validateLabel.m_circleColor = Color.red;
+ m_validateLabel.setVisible(true);
+ }
+ notifyValidationListeners(ValidationStatus.IS_INVALID);
+ }
+ repaint();
+ }
+
+ /* Recurse in any of the input plugins
+ * and validate its inputs */
+ void validateRecursively(Input> input, Set> done) throws Exception {
+ if (done.contains(input)) {
+ // this prevent cycles to lock up validation
+ return;
+ } else {
+ done.add(input);
+ }
+ if (input.get() != null) {
+ if (input.get() instanceof BEASTInterface) {
+ BEASTInterface plugin = ((BEASTInterface) input.get());
+ for (Input> input2 : plugin.listInputs()) {
+ try {
+ input2.validate();
+ } catch (Exception e) {
+ throw new Exception(((BEASTInterface) input.get()).getID() + "
" + e.getMessage());
+ }
+ validateRecursively(input2, done);
+ }
+ }
+ if (input.get() instanceof List>) {
+ for (Object o : (List>) input.get()) {
+ if (o != null && o instanceof BEASTInterface) {
+ BEASTInterface plugin = (BEASTInterface) o;
+ for (Input> input2 : plugin.listInputs()) {
+ try {
+ input2.validate();
+ } catch (Exception e) {
+ throw new Exception(((BEASTInterface) o).getID() + " " + e.getMessage());
+ }
+ validateRecursively(input2, done);
+ }
+ }
+ }
+ }
+ }
+ } // validateRecursively
+
+ @Override
+ public void startValidating(ValidationStatus state) {
+ validateInput();
+ }
+
+
+ public void refreshPanel() {
+ Component c = this;
+ while (c.getParent() != null) {
+ c = c.getParent();
+ if (c instanceof ListSelectionListener) {
+ ((ListSelectionListener) c).valueChanged(null);
+ }
+ }
+ }
+
+ /**
+ * synchronise values in panel with current network *
+ */
+ protected void sync() {
+ Component c = this;
+ while (c.getParent() != null) {
+ c = c.getParent();
+ if (c instanceof BeautiPanel) {
+ BeautiPanel panel = (BeautiPanel) c;
+ BeautiPanelConfig cfgPanel = panel.config;
+ cfgPanel.sync(panel.iPartition);
+ }
+ }
+ }
+
+ // we should leave it to the component to set its own border
+ @Deprecated
+ public void setBorder(Border border) {
+ super.setBorder(border);
+ }
+
+ @Override
+ public void setDoc(BeautiDoc doc) {
+ this.doc = doc;
+ }
+
+ // what is this method for? We should leave repainting to the standard mechanism
+ @Deprecated
+ public void repaint() {
+ this.repaint(0);
+ super.repaint();
+ }
+
+ public Component getComponent() {
+ return this;
+ }
+
+}