JAL-4290 new CommandsTest.headlessOrGuiImageOutputTest - uses CommandLineOperations...
[jalview.git] / src / jalview / project / Jalview2XML.java
index f8be544..983b512 100644 (file)
@@ -26,7 +26,6 @@ import static jalview.math.RotatableMatrix.Axis.Z;
 
 import java.awt.Color;
 import java.awt.Font;
-import java.awt.FontMetrics;
 import java.awt.Rectangle;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
@@ -87,6 +86,7 @@ import jalview.api.analysis.SimilarityParamsI;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.bin.Console;
+import jalview.bin.Jalview;
 import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
@@ -272,7 +272,7 @@ public class Jalview2XML
 
   Map<String, SequenceI> incompleteSeqs = null;
 
-  List<SeqFref> frefedSequence = null;
+  List<forwardRef> frefedSequence = null;
 
   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
 
@@ -287,13 +287,16 @@ public class Jalview2XML
    * entry names
    */
   private Map<RnaModel, String> rnaSessions = new HashMap<>();
-  
+
   /**
    * map from contact matrices to their XML ids
    */
-  private Map<ContactMatrixI,String> contactMatrices = new HashMap<>();
+  private Map<ContactMatrixI, String> contactMatrices = new HashMap<>();
+
   private Map<String, ContactMatrixI> contactMatrixRefs = new HashMap<>();
 
+  private List<jalview.xml.binding.jalview.MatrixType> xmlMatrices = new ArrayList<>();
+
   /**
    * A helper method for safely using the value of an optional attribute that
    * may be null if not present in the XML. Answers the boolean value, or false
@@ -390,18 +393,19 @@ public class Jalview2XML
   }
 
   /**
-   * base class for resolving forward references to sequences by their ID
+   * base class for resolving forward references to an as-yet unmarshalled
+   * object referenced by already unmarshalled objects
    * 
    * @author jprocter
    *
    */
-  abstract class SeqFref
+  abstract class forwardRef
   {
     String sref;
 
     String type;
 
-    public SeqFref(String _sref, String type)
+    public forwardRef(String _sref, String type)
     {
       sref = _sref;
       this.type = type;
@@ -412,6 +416,32 @@ public class Jalview2XML
       return sref;
     }
 
+    public abstract boolean isResolvable();
+
+    /**
+     * @return true if the forward reference was fully resolved
+     */
+    abstract boolean resolve();
+
+    @Override
+    public String toString()
+    {
+      return type + " reference to " + sref;
+    }
+  }
+
+  /**
+   * resolve forward references to sequences by their ID
+   * 
+   * @author jprocter
+   */
+  abstract class SeqFref extends forwardRef
+  {
+    public SeqFref(String _sref, String type)
+    {
+      super(_sref, type);
+    }
+
     public SequenceI getSrefSeq()
     {
       return seqRefIds.get(sref);
@@ -434,17 +464,6 @@ public class Jalview2XML
       }
       return sq;
     }
-
-    /**
-     * @return true if the forward reference was fully resolved
-     */
-    abstract boolean resolve();
-
-    @Override
-    public String toString()
-    {
-      return type + " reference to " + sref;
-    }
   }
 
   /**
@@ -508,14 +527,41 @@ public class Jalview2XML
     return fref;
   }
 
+  public forwardRef newMatrixFref(final String matRef,
+          final jalview.util.MapList mapping, final AlignmentAnnotation jaa)
+  {
+    forwardRef fref = new forwardRef(matRef,
+            "Matrix Reference for sequence and annotation")
+    {
+
+      @Override
+      boolean resolve()
+      {
+        ContactMatrixI cm = contactMatrixRefs.get(matRef);
+        PAEContactMatrix newpae = new PAEContactMatrix(jaa.sequenceRef,
+                mapping, cm);
+
+        jaa.sequenceRef.addContactListFor(jaa, newpae);
+        return true;
+      }
+
+      @Override
+      public boolean isResolvable()
+      {
+        return (contactMatrixRefs.get(matRef) != null);
+      }
+    };
+    return fref;
+  }
+
   public void resolveFrefedSequences()
   {
-    Iterator<SeqFref> nextFref = frefedSequence.iterator();
+    Iterator<forwardRef> nextFref = frefedSequence.iterator();
     int toresolve = frefedSequence.size();
     int unresolved = 0, failedtoresolve = 0;
     while (nextFref.hasNext())
     {
-      SeqFref ref = nextFref.next();
+      forwardRef ref = nextFref.next();
       if (ref.isResolvable())
       {
         try
@@ -530,7 +576,7 @@ public class Jalview2XML
           }
         } catch (Exception x)
         {
-          System.err.println(
+          jalview.bin.Console.errPrintln(
                   "IMPLEMENTATION ERROR: Failed to resolve forward reference for sequence "
                           + ref.getSref());
           x.printStackTrace();
@@ -544,29 +590,30 @@ public class Jalview2XML
     }
     if (unresolved > 0)
     {
-      System.err.println("Jalview Project Import: There were " + unresolved
+      jalview.bin.Console.errPrintln("Jalview Project Import: There were "
+              + unresolved
               + " forward references left unresolved on the stack.");
     }
     if (failedtoresolve > 0)
     {
-      System.err.println("SERIOUS! " + failedtoresolve
+      jalview.bin.Console.errPrintln("SERIOUS! " + failedtoresolve
               + " resolvable forward references failed to resolve.");
     }
     if (incompleteSeqs != null && incompleteSeqs.size() > 0)
     {
-      System.err.println(
+      jalview.bin.Console.errPrintln(
               "Jalview Project Import: There are " + incompleteSeqs.size()
                       + " sequences which may have incomplete metadata.");
       if (incompleteSeqs.size() < 10)
       {
         for (SequenceI s : incompleteSeqs.values())
         {
-          System.err.println(s.toString());
+          jalview.bin.Console.errPrintln(s.toString());
         }
       }
       else
       {
-        System.err.println(
+        jalview.bin.Console.errPrintln(
                 "Too many to report. Skipping output of incomplete sequences.");
       }
     }
@@ -640,7 +687,7 @@ public class Jalview2XML
    */
   public void saveState(JarOutputStream jout)
   {
-    AlignFrame[] frames = Desktop.getAlignFrames();
+    AlignFrame[] frames = Desktop.getDesktopAlignFrames();
 
     setStateSavedUpToDate(true);
 
@@ -946,7 +993,7 @@ public class Jalview2XML
       object.setCreationDate(now);
     } catch (DatatypeConfigurationException e)
     {
-      System.err.println("error writing date: " + e.toString());
+      jalview.bin.Console.errPrintln("error writing date: " + e.toString());
     }
     object.setVersion(Cache.getDefault("VERSION", "Development Build"));
 
@@ -1013,14 +1060,14 @@ public class Jalview2XML
           // HAPPEN! (PF00072.15.stk does this)
           // JBPNote: Uncomment to debug writing out of files that do not read
           // back in due to ArrayOutOfBoundExceptions.
-          // System.err.println("vamsasSeq backref: "+id+"");
-          // System.err.println(jds.getName()+"
+          // jalview.bin.Console.errPrintln("vamsasSeq backref: "+id+"");
+          // jalview.bin.Console.errPrintln(jds.getName()+"
           // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
-          // System.err.println("Hashcode: "+seqHash(jds));
+          // jalview.bin.Console.errPrintln("Hashcode: "+seqHash(jds));
           // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
-          // System.err.println(rsq.getName()+"
+          // jalview.bin.Console.errPrintln(rsq.getName()+"
           // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
-          // System.err.println("Hashcode: "+seqHash(rsq));
+          // jalview.bin.Console.errPrintln("Hashcode: "+seqHash(rsq));
         }
         else
         {
@@ -1149,38 +1196,55 @@ public class Jalview2XML
            * only view *should* be coped with sensibly.
            */
           // This must have been loaded, is it still visible?
-          JInternalFrame[] frames = Desktop.desktop.getAllFrames();
-          String matchedFile = null;
-          for (int f = frames.length - 1; f > -1; f--)
+          List<JalviewStructureDisplayI> viewFrames = new ArrayList<>();
+          if (Desktop.desktop != null)
           {
-            if (frames[f] instanceof StructureViewerBase)
+            JInternalFrame[] jifs = Desktop.desktop.getAllFrames();
+            if (jifs != null)
             {
-              StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
-              matchedFile = saveStructureViewer(ap, jds, pdb, entry,
-                      viewIds, matchedFile, viewFrame);
-              /*
-               * Only store each structure viewer's state once in the project
-               * jar. First time through only (storeDS==false)
-               */
-              String viewId = viewFrame.getViewId();
-              String viewerType = viewFrame.getViewerType().toString();
-              if (!storeDS && !viewIds.contains(viewId))
+              for (JInternalFrame jif : jifs)
               {
-                viewIds.add(viewId);
-                File viewerState = viewFrame.saveSession();
-                if (viewerState != null)
-                {
-                  copyFileToJar(jout, viewerState.getPath(),
-                          getViewerJarEntryName(viewId), viewerType);
-                }
-                else
+                if (jif instanceof JalviewStructureDisplayI)
                 {
-                  Console.error(
-                          "Failed to save viewer state for " + viewerType);
+                  viewFrames.add((JalviewStructureDisplayI) jif);
                 }
               }
             }
           }
+          else if (Jalview.isHeadlessMode()
+                  && Jalview.getInstance().getCommands() != null)
+          {
+            viewFrames.addAll(
+                    StructureViewerBase.getAllStructureViewerBases());
+          }
+
+          String matchedFile = null;
+          for (JalviewStructureDisplayI viewFrame : viewFrames)
+          {
+            matchedFile = saveStructureViewer(ap, jds, pdb, entry, viewIds,
+                    matchedFile, viewFrame);
+            /*
+             * Only store each structure viewer's state once in the project
+             * jar. First time through only (storeDS==false)
+             */
+            String viewId = viewFrame.getViewId();
+            String viewerType = viewFrame.getViewerType().toString();
+            if (!storeDS && !viewIds.contains(viewId))
+            {
+              viewIds.add(viewId);
+              File viewerState = viewFrame.saveSession();
+              if (viewerState != null)
+              {
+                copyFileToJar(jout, viewerState.getPath(),
+                        getViewerJarEntryName(viewId), viewerType);
+              }
+              else
+              {
+                Console.error(
+                        "Failed to save viewer state for " + viewerType);
+              }
+            }
+          }
 
           if (matchedFile != null || entry.getFile() != null)
           {
@@ -1590,6 +1654,10 @@ public class Jalview2XML
       view.setShowColourText(av.getColourText());
       view.setShowFullId(av.getShowJVSuffix());
       view.setRightAlignIds(av.isRightAlignIds());
+      view.setIdWidth(av.getIdWidth());
+      view.setIdWidthManuallyAdjusted(
+              ap.getIdPanel().getIdCanvas().isManuallyAdjusted());
+
       view.setShowSequenceFeatures(av.isShowSequenceFeatures());
       view.setShowText(av.getShowText());
       view.setShowUnconserved(av.getShowUnconserved());
@@ -1761,6 +1829,19 @@ public class Jalview2XML
       // jms.addViewport(view);
       object.getViewport().add(view);
     }
+
+    if (storeDS)
+    {
+      // store matrices referenced by any views or annotation in this dataset
+      if (xmlMatrices != null && xmlMatrices.size() > 0)
+      {
+        Console.debug(
+                "Adding " + xmlMatrices.size() + " matrices to dataset.");
+        vamsasSet.getMatrix().addAll(xmlMatrices);
+        xmlMatrices.clear();
+      }
+    }
+
     // object.setJalviewModelSequence(jms);
     // object.getVamsasModel().addSequenceSet(vamsasSet);
     object.getVamsasModel().getSequenceSet().add(vamsasSet);
@@ -1773,7 +1854,7 @@ public class Jalview2XML
       try
       {
         fileName = fileName.replace('\\', '/');
-        System.out.println("Writing jar entry " + fileName);
+        jalview.bin.Console.outPrintln("Writing jar entry " + fileName);
         JarEntry entry = new JarEntry(fileName);
         jout.putNextEntry(entry);
         PrintWriter pout = new PrintWriter(
@@ -1794,7 +1875,7 @@ public class Jalview2XML
       } catch (Exception ex)
       {
         // TODO: raise error in GUI if marshalling failed.
-        System.err.println("Error writing Jalview project");
+        jalview.bin.Console.errPrintln("Error writing Jalview project");
         ex.printStackTrace();
       }
     }
@@ -2108,7 +2189,7 @@ public class Jalview2XML
       File file = new File(infilePath);
       if (file.exists() && jout != null)
       {
-        System.out.println(
+        jalview.bin.Console.outPrintln(
                 "Writing jar entry " + jarEntryName + " (" + msg + ")");
         jout.putNextEntry(new JarEntry(jarEntryName));
         copyAll(is, jout);
@@ -2157,7 +2238,7 @@ public class Jalview2XML
    */
   protected String saveStructureViewer(AlignmentPanel ap, SequenceI jds,
           Pdbids pdb, PDBEntry entry, List<String> viewIds,
-          String matchedFile, StructureViewerBase viewFrame)
+          String matchedFile, JalviewStructureDisplayI viewFrame)
   {
     final AAStructureBindingModel bindingModel = viewFrame.getBinding();
 
@@ -2202,7 +2283,7 @@ public class Jalview2XML
         {
           StructureState state = new StructureState();
           state.setVisible(true);
-          state.setXpos(viewFrame.getX());
+          state.setXpos(viewFrame.getY());
           state.setYpos(viewFrame.getY());
           state.setWidth(viewFrame.getWidth());
           state.setHeight(viewFrame.getHeight());
@@ -2325,7 +2406,7 @@ public class Jalview2XML
                     .getContactMatrixFor(annotation);
             if (cm != null)
             {
-              storeMatrixFor(vamsasSet, an,annotation, cm);
+              storeMatrixFor(vamsasSet, an, annotation, cm);
             }
           }
         }
@@ -2432,11 +2513,12 @@ public class Jalview2XML
 
   }
 
-  private void storeMatrixFor(SequenceSet root, Annotation an, AlignmentAnnotation annotation, ContactMatrixI cm)
+  private void storeMatrixFor(SequenceSet root, Annotation an,
+          AlignmentAnnotation annotation, ContactMatrixI cm)
   {
     String cmId = contactMatrices.get(cm);
-    MatrixType xmlmat=null;
-    
+    MatrixType xmlmat = null;
+
     // first create an xml ref for the matrix data, if none exist
     if (cmId == null)
     {
@@ -2464,17 +2546,23 @@ public class Jalview2XML
       {
         xmlmat.setCutHeight(cm.getCutHeight());
       }
-      xmlmat.setId(cmId = makeHashCode(cm, null));
+      xmlmat.setId(cmId = "m" + contactMatrices.size()
+              + System.currentTimeMillis());
+      Console.trace("Matrix data stored :" + cmId);
       contactMatrices.put(cm, cmId);
       contactMatrixRefs.put(cmId, cm);
-      root.getMatrices().add(xmlmat);
+      xmlMatrices.add(xmlmat);
+    }
+    else
+    {
+      Console.trace("Existing Matrix stored :" + cmId);
     }
 
     // now store mapping
 
     MapOnAMatrixType xmlmatmapping = new MapOnAMatrixType();
     xmlmatmapping.setMatrix(cmId);
-    
+
     // Pretty much all matrices currently managed in this way are
     // mappableContactMatrixI implementations - but check anyway
     if (cm instanceof MappableContactMatrixI)
@@ -2548,6 +2636,7 @@ public class Jalview2XML
     return BitSet.valueOf(newlongvals);
 
   }
+
   private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
   {
     AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
@@ -2991,7 +3080,8 @@ public class Jalview2XML
         });
       } catch (Exception x)
       {
-        System.err.println("Error loading alignment: " + x.getMessage());
+        jalview.bin.Console
+                .errPrintln("Error loading alignment: " + x.getMessage());
       }
     }
     return af;
@@ -3030,19 +3120,22 @@ public class Jalview2XML
         {
           if (bytes != null)
           {
-            // System.out.println("Jalview2XML: opening byte jarInputStream for
+            // jalview.bin.Console.outPrintln("Jalview2XML: opening byte
+            // jarInputStream for
             // bytes.length=" + bytes.length);
             return new JarInputStream(new ByteArrayInputStream(bytes));
           }
           if (_url != null)
           {
-            // System.out.println("Jalview2XML: opening url jarInputStream for "
+            // jalview.bin.Console.outPrintln("Jalview2XML: opening url
+            // jarInputStream for "
             // + _url);
             return new JarInputStream(_url.openStream());
           }
           else
           {
-            // System.out.println("Jalview2XML: opening file jarInputStream for
+            // jalview.bin.Console.outPrintln("Jalview2XML: opening file
+            // jarInputStream for
             // " + file);
             return new JarInputStream(new FileInputStream(file));
           }
@@ -3082,6 +3175,7 @@ public class Jalview2XML
       initSeqRefs();
     }
     AlignFrame af = null, _af = null;
+    List<AlignFrame> toRepaint = new ArrayList<AlignFrame>();
     IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<>();
     Map<String, AlignFrame> gatherToThisFrame = new HashMap<>();
     final String file = jprovider.getFilename();
@@ -3116,6 +3210,7 @@ public class Jalview2XML
             if (_af != null && object.getViewport().size() > 0)
             // getJalviewModelSequence().getViewportCount() > 0)
             {
+              toRepaint.add(_af);
               if (af == null)
               {
                 // store a reference to the first view
@@ -3145,15 +3240,20 @@ public class Jalview2XML
       } while (jarentry != null);
       jin.close();
       resolveFrefedSequences();
+      for (AlignFrame alignFrame : toRepaint)
+      {
+        alignFrame.repaint();
+      }
     } catch (IOException ex)
     {
       ex.printStackTrace();
       errorMessage = "Couldn't locate Jalview XML file : " + file;
-      System.err.println(
+      jalview.bin.Console.errPrintln(
               "Exception whilst loading jalview XML file : " + ex + "\n");
     } catch (Exception ex)
     {
-      System.err.println("Parsing as Jalview Version 2 file failed.");
+      jalview.bin.Console
+              .errPrintln("Parsing as Jalview Version 2 file failed.");
       ex.printStackTrace(System.err);
       if (attemptversion1parse)
       {
@@ -3165,18 +3265,19 @@ public class Jalview2XML
       }
       if (af != null)
       {
-        System.out.println("Successfully loaded archive file");
+        jalview.bin.Console.outPrintln("Successfully loaded archive file");
         return af;
       }
       ex.printStackTrace();
 
-      System.err.println(
+      jalview.bin.Console.errPrintln(
               "Exception whilst loading jalview XML file : " + ex + "\n");
     } catch (OutOfMemoryError e)
     {
       // Don't use the OOM Window here
       errorMessage = "Out of memory loading jalview XML file";
-      System.err.println("Out of memory whilst loading jalview XML file");
+      jalview.bin.Console
+              .errPrintln("Out of memory whilst loading jalview XML file");
       e.printStackTrace();
     }
 
@@ -3280,8 +3381,8 @@ public class Jalview2XML
         Desktop.addInternalFrame(af, view.getTitle(),
                 safeInt(view.getWidth()), safeInt(view.getHeight()));
         af.setMenusForViewport();
-        System.err.println("Failed to restore view " + view.getTitle()
-                + " to split frame");
+        jalview.bin.Console.errPrintln("Failed to restore view "
+                + view.getTitle() + " to split frame");
       }
     }
 
@@ -3359,7 +3460,8 @@ public class Jalview2XML
       }
       else
       {
-        System.err.println("Problem loading Jalview file: " + errorMessage);
+        jalview.bin.Console.errPrintln(
+                "Problem loading Jalview file: " + errorMessage);
       }
     }
     errorMessage = null;
@@ -3533,12 +3635,12 @@ public class Jalview2XML
 
     // ////////////////////////////////
     // LOAD MATRICES (IF ANY)
-    
-    if (vamsasSet.getMatrices()!=null && vamsasSet.getMatrices().size()>0)
+
+    if (vamsasSet.getMatrix() != null && vamsasSet.getMatrix().size() > 0)
     {
-      importMatrixData(vamsasSet.getMatrices());
+      importMatrixData(vamsasSet.getMatrix());
     }
-    
+
     // ////////////////////////////////
     // LOAD SEQUENCES
 
@@ -3565,7 +3667,7 @@ public class Jalview2XML
           if (tmpSeq.getStart() != jseq.getStart()
                   || tmpSeq.getEnd() != jseq.getEnd())
           {
-            System.err.println(String.format(
+            jalview.bin.Console.errPrintln(String.format(
                     "Warning JAL-2154 regression: updating start/end for sequence %s from %d/%d to %d/%d",
                     tmpSeq.getName(), tmpSeq.getStart(), tmpSeq.getEnd(),
                     jseq.getStart(), jseq.getEnd()));
@@ -4074,7 +4176,7 @@ public class Jalview2XML
             for (MapOnAMatrixType xmlmat : annotation.getContactmatrix())
             {
               restoreMatrixFor(jaa.sequenceRef, jaa, xmlmat);
-            } 
+            }
           }
         }
 
@@ -4226,7 +4328,7 @@ public class Jalview2XML
       // XML.
       // and then recover its containing af to allow the settings to be applied.
       // TODO: fix for vamsas demo
-      System.err.println(
+      jalview.bin.Console.errPrintln(
               "About to recover a viewport for existing alignment: Sequence set ID is "
                       + uniqueSeqSetId);
       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
@@ -4235,13 +4337,13 @@ public class Jalview2XML
         if (seqsetobj instanceof String)
         {
           uniqueSeqSetId = (String) seqsetobj;
-          System.err.println(
+          jalview.bin.Console.errPrintln(
                   "Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
                           + uniqueSeqSetId);
         }
         else
         {
-          System.err.println(
+          jalview.bin.Console.errPrintln(
                   "Warning : Collision between sequence set ID string and existing jalview object mapping.");
         }
 
@@ -4306,11 +4408,12 @@ public class Jalview2XML
 
   private void importMatrixData(List<MatrixType> xmlmatrices)
   {
-    for (MatrixType xmlmat:xmlmatrices)
+    for (MatrixType xmlmat : xmlmatrices)
     {
       if (!PAEContactMatrix.PAEMATRIX.equals(xmlmat.getType()))
       {
-        Console.error("Ignoring matrix '"+xmlmat.getId()+"' of type '"+xmlmat.getType());
+        Console.error("Ignoring matrix '" + xmlmat.getId() + "' of type '"
+                + xmlmat.getType());
         continue;
       }
 
@@ -4320,11 +4423,10 @@ public class Jalview2XML
         continue;
       }
 
-      float[][] elements = ContactMatrix
-              .fromFloatStringToContacts(xmlmat.getElements(),
-                      xmlmat.getCols().intValue(),
-                      xmlmat.getRows().intValue());
-      
+      float[][] elements = ContactMatrix.fromFloatStringToContacts(
+              xmlmat.getElements(), xmlmat.getCols().intValue(),
+              xmlmat.getRows().intValue());
+
       List<BitSet> newgroups = new ArrayList<BitSet>();
       if (xmlmat.getGroups().size() > 0)
       {
@@ -4333,40 +4435,28 @@ public class Jalview2XML
           newgroups.add(deStringifyBitset(sgroup));
         }
       }
-      String nwk = xmlmat.getNewick().size() > 0
-              ? xmlmat.getNewick().get(0)
+      String nwk = xmlmat.getNewick().size() > 0 ? xmlmat.getNewick().get(0)
               : null;
       if (xmlmat.getNewick().size() > 1)
       {
-        Console.log.info(
-                "Ignoring additional clusterings for contact matrix");
+        Console.log
+                .info("Ignoring additional clusterings for contact matrix");
       }
       String treeMethod = xmlmat.getTreeMethod();
-      double thresh = xmlmat.getCutHeight() != null
-              ? xmlmat.getCutHeight()
+      double thresh = xmlmat.getCutHeight() != null ? xmlmat.getCutHeight()
               : 0;
       GroupSet grpset = new GroupSet();
       grpset.restoreGroups(newgroups, treeMethod, nwk, thresh);
-      
+
       FloatContactMatrix newcm = new FloatContactMatrix(elements, grpset);
       contactMatrixRefs.put(xmlmat.getId(), newcm);
-      Console.trace("Restored base contact matrix "+xmlmat.getId());
+      Console.trace("Restored base contact matrix " + xmlmat.getId());
     }
   }
 
   private void restoreMatrixFor(SequenceI sequenceRef,
           AlignmentAnnotation jaa, MapOnAMatrixType xmlmatmapping)
   {
-    MatrixType xmlmat;
-
-    // locate matrix data in project XML and import
-    ContactMatrixI cm = contactMatrixRefs.get(xmlmatmapping.getMatrix());
-    if (cm == null)
-    {
-      Console.error("Cannot restore mapping to matrix "
-              + xmlmatmapping.getMatrix() + " - not found in project.");
-    }
-
     // restore mapping data to matrix data
     jalview.util.MapList mapping = null;
     if (xmlmatmapping.getMapping() != null)
@@ -4393,10 +4483,22 @@ public class Jalview2XML
       mapping = new jalview.util.MapList(fr, fto,
               m.getMapFromUnit().intValue(), m.getMapToUnit().intValue());
     }
-    PAEContactMatrix newpae = new PAEContactMatrix(jaa.sequenceRef, mapping,
-            cm);
 
-    jaa.sequenceRef.addContactListFor(jaa, newpae);
+    // locate matrix data in project XML and import
+    ContactMatrixI cm = contactMatrixRefs.get(xmlmatmapping.getMatrix());
+    if (cm == null)
+    {
+      frefedSequence
+              .add(newMatrixFref(xmlmatmapping.getMatrix(), mapping, jaa));
+    }
+    else
+    {
+      // create the PAEMatrix now
+      PAEContactMatrix newpae = new PAEContactMatrix(jaa.sequenceRef,
+              mapping, cm);
+
+      jaa.sequenceRef.addContactListFor(jaa, newpae);
+    }
 
     return;
   }
@@ -4558,7 +4660,7 @@ public class Jalview2XML
         {
           if (tree.isColumnWise())
           {
-            AlignmentAnnotation aa = (AlignmentAnnotation) annotationIds
+            AlignmentAnnotation aa = annotationIds
                     .get(tree.getColumnReference());
             if (aa == null)
             {
@@ -4778,7 +4880,7 @@ public class Jalview2XML
         createOrLinkStructureViewer(entry, af, ap, jprovider);
       } catch (Exception e)
       {
-        System.err.println(
+        jalview.bin.Console.errPrintln(
                 "Error loading structure viewer: " + e.getMessage());
         // failed - try the next one
       }
@@ -4982,7 +5084,7 @@ public class Jalview2XML
             || version.equalsIgnoreCase("Test")
             || version.equalsIgnoreCase("AUTOMATED BUILD"))
     {
-      System.err.println("Assuming project file with "
+      jalview.bin.Console.errPrintln("Assuming project file with "
               + (version == null ? "null" : version)
               + " is compatible with Jalview version " + supported);
       return true;
@@ -5029,7 +5131,7 @@ public class Jalview2XML
     //
     // @Override
     // protected void processKeyEvent(java.awt.event.KeyEvent e) {
-    // System.out.println("Jalview2XML AF " + e);
+    // jalview.bin.Console.outPrintln("Jalview2XML AF " + e);
     // super.processKeyEvent(e);
     //
     // }
@@ -5152,6 +5254,26 @@ public class Jalview2XML
     }
     af.setBounds(safeInt(view.getXpos()), safeInt(view.getYpos()),
             safeInt(view.getWidth()), safeInt(view.getHeight()));
+
+    af.alignPanel.fontChanged(); // make sure font is updated *before* we set ID
+                                 // width
+    if (view.getIdWidth() == null)
+    {
+      if (!isVersionStringLaterThan("2.11.3", jm.getVersion()))
+      {
+        // Pre 2.11.3 jalview projects do not store the id width
+        // idWidth was also calculated in a different way.
+        viewport.setIdWidth(af.alignPanel.getLegacyIdWidth());
+        af.alignPanel.getIdPanel().getIdCanvas().setManuallyAdjusted(true);
+      }
+    }
+    else
+    {
+      viewport.setIdWidth(view.getIdWidth());
+      af.alignPanel.getIdPanel().getIdCanvas()
+              .setManuallyAdjusted(view.isIdWidthManuallyAdjusted());
+    }
+
     // startSeq set in af.alignPanel.updateLayout below
     af.alignPanel.updateLayout();
     ColourSchemeI cs = null;
@@ -5444,8 +5566,9 @@ public class Jalview2XML
     }
     if (matchedAnnotation == null)
     {
-      System.err.println("Failed to match annotation colour scheme for "
-              + annotationId);
+      jalview.bin.Console
+              .errPrintln("Failed to match annotation colour scheme for "
+                      + annotationId);
       return null;
     }
     // belt-and-braces create a threshold line if the
@@ -5903,7 +6026,7 @@ public class Jalview2XML
         }
         // TODO: merges will never happen if we 'know' we have the real dataset
         // sequence - this should be detected when id==dssid
-        System.err.println(
+        jalview.bin.Console.errPrintln(
                 "DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
         // + (pre ? "prepended" : "") + " "
         // + (post ? "appended" : ""));
@@ -6096,7 +6219,7 @@ public class Jalview2XML
       }
       else
       {
-        System.err.println(
+        jalview.bin.Console.errPrintln(
                 "Warning - making up dataset sequence id for DbRef sequence map reference");
         sqid = ((Object) ms).toString(); // make up a new hascode for
         // undefined dataset sequence hash
@@ -6777,7 +6900,7 @@ public class Jalview2XML
     } catch (IllegalStateException e)
     {
       // mixing AND and OR conditions perhaps
-      System.err.println(
+      jalview.bin.Console.errPrintln(
               String.format("Error reading filter conditions for '%s': %s",
                       featureType, e.getMessage()));
       // return as much as was parsed up to the error
@@ -6858,7 +6981,8 @@ public class Jalview2XML
       }
       else
       {
-        System.err.println("Malformed compound filter condition");
+        jalview.bin.Console
+                .errPrintln("Malformed compound filter condition");
       }
     }
   }
@@ -6958,7 +7082,7 @@ public class Jalview2XML
     if (stateSavedUpToDate()) // nothing happened since last project save
       return true;
 
-    AlignFrame[] frames = Desktop.getAlignFrames();
+    AlignFrame[] frames = Desktop.getDesktopAlignFrames();
     if (frames != null)
     {
       for (int i = 0; i < frames.length; i++)