JAL-3451 JalviewJS embedded mode not resizing
[jalview.git] / src / jalview / project / Jalview2XML.java
index 1797fb7..186f786 100644 (file)
@@ -28,6 +28,7 @@ import jalview.analysis.Conservation;
 import jalview.analysis.PCA;
 import jalview.analysis.scoremodels.ScoreModels;
 import jalview.analysis.scoremodels.SimilarityParams;
+import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureColourI;
 import jalview.api.ViewStyleI;
 import jalview.api.analysis.ScoreModelI;
@@ -38,6 +39,7 @@ import jalview.datamodel.AlignedCodonFrame;
 import jalview.datamodel.Alignment;
 import jalview.datamodel.AlignmentAnnotation;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.DBRefEntry;
 import jalview.datamodel.GraphLine;
 import jalview.datamodel.PDBEntry;
 import jalview.datamodel.Point;
@@ -81,7 +83,6 @@ import jalview.schemes.ColourSchemeProperty;
 import jalview.schemes.FeatureColour;
 import jalview.schemes.ResidueProperties;
 import jalview.schemes.UserColourScheme;
-import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
 import jalview.util.Format;
 import jalview.util.MessageManager;
@@ -149,9 +150,11 @@ import jalview.xml.binding.jalview.ThresholdType;
 import jalview.xml.binding.jalview.VAMSAS;
 
 import java.awt.Color;
+import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Rectangle;
 import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
@@ -161,7 +164,6 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
 import java.math.BigInteger;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -208,6 +210,15 @@ import javax.xml.stream.XMLStreamReader;
  */
 public class Jalview2XML
 {
+
+  // BH 2018 we add the .jvp binary extension to J2S so that
+  // it will declare that binary when we do the file save from the browser
+
+  static
+  {
+    Platform.addJ2SBinaryType(".jvp?");
+  }
+
   private static final String VIEWER_PREFIX = "viewer_";
 
   private static final String RNA_PREFIX = "rna_";
@@ -451,7 +462,7 @@ public class Jalview2XML
       public boolean isResolvable()
       {
         return super.isResolvable() && mp.getTo() != null;
-      };
+      }
 
       @Override
       boolean resolve()
@@ -613,13 +624,14 @@ public class Jalview2XML
    * core method for storing state for a set of AlignFrames.
    * 
    * @param frames
-   *          - frames involving all data to be exported (including containing
-   *          splitframes)
+   *          - frames involving all data to be exported (including those
+   *          contained in splitframes, though not the split frames themselves)
    * @param jout
    *          - project output stream
    */
   private void saveAllFrames(List<AlignFrame> frames, JarOutputStream jout)
   {
+
     Hashtable<String, AlignFrame> dsses = new Hashtable<>();
 
     /*
@@ -642,21 +654,22 @@ public class Jalview2XML
       for (int i = frames.size() - 1; i > -1; i--)
       {
         AlignFrame af = frames.get(i);
+        AlignViewport vp = af.getViewport();
         // skip ?
         if (skipList != null && skipList
-                .containsKey(af.getViewport().getSequenceSetId()))
+                .containsKey(vp.getSequenceSetId()))
         {
           continue;
         }
 
         String shortName = makeFilename(af, shortNames);
 
-        int apSize = af.getAlignPanels().size();
-
+        AlignmentI alignment = vp.getAlignment();
+        List<? extends AlignmentViewPanel> panels = af.getAlignPanels();
+        int apSize = panels.size();
         for (int ap = 0; ap < apSize; ap++)
-        {
-          AlignmentPanel apanel = (AlignmentPanel) af.getAlignPanels()
-                  .get(ap);
+          {
+          AlignmentPanel apanel = (AlignmentPanel) panels.get(ap);
           String fileName = apSize == 1 ? shortName : ap + shortName;
           if (!fileName.endsWith(".xml"))
           {
@@ -664,11 +677,17 @@ public class Jalview2XML
           }
 
           saveState(apanel, fileName, jout, viewIds);
-
-          String dssid = getDatasetIdRef(
-                  af.getViewport().getAlignment().getDataset());
+        }
+        if (apSize > 0)
+        {
+          // BH moved next bit out of inner loop, not that it really matters.
+          // so we are testing to make sure we actually have an alignment,
+          // apparently.
+          String dssid = getDatasetIdRef(alignment.getDataset());
           if (!dsses.containsKey(dssid))
           {
+            // We have not already covered this data by reference from another
+            // frame.
             dsses.put(dssid, af);
           }
         }
@@ -683,7 +702,6 @@ public class Jalview2XML
       } catch (Exception foo)
       {
       }
-      ;
       jout.close();
     } catch (Exception ex)
     {
@@ -745,9 +763,10 @@ public class Jalview2XML
     try
     {
       // create backupfiles object and get new temp filename destination
-      BackupFiles backupfiles = new BackupFiles(jarFile);
-      FileOutputStream fos = new FileOutputStream(
-              backupfiles.getTempFilePath());
+      boolean doBackup = BackupFiles.getEnabled();
+      BackupFiles backupfiles = doBackup ? new BackupFiles(jarFile) : null;
+      FileOutputStream fos = new FileOutputStream(doBackup ? 
+              backupfiles.getTempFilePath() : jarFile);
 
       JarOutputStream jout = new JarOutputStream(fos);
       List<AlignFrame> frames = new ArrayList<>();
@@ -768,12 +787,14 @@ public class Jalview2XML
       } catch (Exception foo)
       {
       }
-      ;
       jout.close();
       boolean success = true;
 
-      backupfiles.setWriteSuccess(success);
-      success = backupfiles.rollBackupsAndRenameTempFile();
+      if (doBackup)
+      {
+        backupfiles.setWriteSuccess(success);
+        success = backupfiles.rollBackupsAndRenameTempFile();
+      }
 
       return success;
     } catch (Exception ex)
@@ -784,10 +805,22 @@ public class Jalview2XML
     }
   }
 
+  /**
+   * Each AlignFrame has a single data set associated with it. Note that none of
+   * these frames are split frames, because Desktop.getAlignFrames() collects
+   * top and bottom separately here.
+   * 
+   * @param dsses
+   * @param fileName
+   * @param jout
+   */
   private void writeDatasetFor(Hashtable<String, AlignFrame> dsses,
           String fileName, JarOutputStream jout)
   {
 
+    // Note that in saveAllFrames we have associated each specific dataset to
+    // ONE of its associated frames.
+
     for (String dssids : dsses.keySet())
     {
       AlignFrame _af = dsses.get(dssids);
@@ -1068,7 +1101,7 @@ public class Jalview2XML
            * only view *should* be coped with sensibly.
            */
           // This must have been loaded, is it still visible?
-          JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+          JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
           String matchedFile = null;
           for (int f = frames.length - 1; f > -1; f--)
           {
@@ -1220,9 +1253,9 @@ public class Jalview2XML
     {
       // FIND ANY ASSOCIATED TREES
       // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
-      if (Desktop.desktop != null)
+      if (Desktop.getDesktopPane() != null)
       {
-        JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+        JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
 
         for (int t = 0; t < frames.length; t++)
         {
@@ -1266,9 +1299,9 @@ public class Jalview2XML
     /*
      * save PCA viewers
      */
-    if (!storeDS && Desktop.desktop != null)
+    if (!storeDS && Desktop.getDesktopPane() != null)
     {
-      for (JInternalFrame frame : Desktop.desktop.getAllFrames())
+      for (JInternalFrame frame : Desktop.getDesktopPane().getAllFrames())
       {
         if (frame instanceof PCAPanel)
         {
@@ -1659,6 +1692,7 @@ public class Jalview2XML
       // using save and then load
       try
       {
+       fileName = fileName.replace('\\', '/');
         System.out.println("Writing jar entry " + fileName);
         JarEntry entry = new JarEntry(fileName);
         jout.putNextEntry(entry);
@@ -1905,11 +1939,11 @@ public class Jalview2XML
           final SequenceI jds, List<String> viewIds, AlignmentPanel ap,
           boolean storeDataset)
   {
-    if (Desktop.desktop == null)
+    if (Desktop.getDesktopPane() == null)
     {
       return;
     }
-    JInternalFrame[] frames = Desktop.desktop.getAllFrames();
+    JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
     for (int f = frames.length - 1; f > -1; f--)
     {
       if (frames[f] instanceof AppVarna)
@@ -2029,6 +2063,7 @@ public class Jalview2XML
   {
     if (jout != null)
     {
+      jarEntryName = jarEntryName.replace('\\','/');
       System.out.println("Writing jar entry " + jarEntryName);
       jout.putNextEntry(new JarEntry(jarEntryName));
       DataOutputStream dout = new DataOutputStream(jout);
@@ -2364,7 +2399,7 @@ public class Jalview2XML
     if (calcIdParam.getVersion().equals("1.0"))
     {
       final String[] calcIds = calcIdParam.getServiceURL().toArray(new String[0]);
-      Jws2Instance service = Jws2Discoverer.getDiscoverer()
+      Jws2Instance service = Jws2Discoverer.getInstance()
               .getPreferredServiceFor(calcIds);
       if (service != null)
       {
@@ -2489,7 +2524,7 @@ public class Jalview2XML
     vamsasSeq.setName(jds.getName());
     vamsasSeq.setSequence(jds.getSequenceAsString());
     vamsasSeq.setDescription(jds.getDescription());
-    jalview.datamodel.DBRefEntry[] dbrefs = null;
+    List<DBRefEntry> dbrefs = null;
     if (jds.getDatasetSequence() != null)
     {
       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
@@ -2507,15 +2542,16 @@ public class Jalview2XML
     }
     if (dbrefs != null)
     {
-      for (int d = 0; d < dbrefs.length; d++)
+      for (int d = 0, nd = dbrefs.size(); d < nd; d++)
       {
         DBRef dbref = new DBRef();
-        dbref.setSource(dbrefs[d].getSource());
-        dbref.setVersion(dbrefs[d].getVersion());
-        dbref.setAccessionId(dbrefs[d].getAccessionId());
-        if (dbrefs[d].hasMap())
+        DBRefEntry ref = dbrefs.get(d);
+        dbref.setSource(ref.getSource());
+        dbref.setVersion(ref.getVersion());
+        dbref.setAccessionId(ref.getAccessionId());
+        if (ref.hasMap())
         {
-          Mapping mp = createVamsasMapping(dbrefs[d].getMap(), parentseq,
+          Mapping mp = createVamsasMapping(ref.getMap(), parentseq,
                   jds, recurse);
           dbref.setMapping(mp);
         }
@@ -2711,7 +2747,7 @@ public class Jalview2XML
    * @param file
    *          - HTTP URL or filename
    */
-  public AlignFrame loadJalviewAlign(final String file)
+  public AlignFrame loadJalviewAlign(final Object file)
   {
 
     jalview.gui.AlignFrame af = null;
@@ -2739,13 +2775,14 @@ public class Jalview2XML
     {
       try
       {
-        SwingUtilities.invokeAndWait(new Runnable()
+         // BH 2019 -- can't wait
+        SwingUtilities.invokeLater(new Runnable()
         {
           @Override
           public void run()
           {
             setLoadingFinishedForNewStructureViewers();
-          };
+          }
         });
       } catch (Exception x)
       {
@@ -2755,44 +2792,52 @@ public class Jalview2XML
     return af;
   }
 
-  private jarInputStreamProvider createjarInputStreamProvider(
-          final String file) throws MalformedURLException
-  {
-    URL url = null;
-    errorMessage = null;
-    uniqueSetSuffix = null;
-    seqRefIds = null;
-    viewportsAdded.clear();
-    frefedSequence = null;
-
-    if (file.startsWith("http://"))
-    {
-      url = new URL(file);
-    }
-    final URL _url = url;
-    return new jarInputStreamProvider()
-    {
-
-      @Override
-      public JarInputStream getJarInputStream() throws IOException
-      {
-        if (_url != null)
-        {
-          return new JarInputStream(_url.openStream());
-        }
-        else
-        {
-          return new JarInputStream(new FileInputStream(file));
-        }
-      }
-
-      @Override
-      public String getFilename()
-      {
-        return file;
-      }
-    };
-  }
+       @SuppressWarnings("unused")
+       private jarInputStreamProvider createjarInputStreamProvider(final Object ofile) throws MalformedURLException {
+
+               // BH 2018 allow for bytes already attached to File object
+               try {
+                       String file = (ofile instanceof File ? ((File) ofile).getCanonicalPath() : ofile.toString());
+      byte[] bytes = Platform.isJS() ? Platform.getFileBytes((File) ofile)
+              : null;
+                       URL url = null;
+                       errorMessage = null;
+                       uniqueSetSuffix = null;
+                       seqRefIds = null;
+                       viewportsAdded.clear();
+                       frefedSequence = null;
+
+                       if (file.startsWith("http://")) {
+                               url = new URL(file);
+                       }
+                       final URL _url = url;
+                       return new jarInputStreamProvider() {
+
+                               @Override
+                               public JarInputStream getJarInputStream() throws IOException {
+                                       if (bytes != null) {
+//                                             System.out.println("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 " + _url);
+                                               return new JarInputStream(_url.openStream());
+                                       } else {
+//                                             System.out.println("Jalview2XML: opening file jarInputStream for " + file);
+                                               return new JarInputStream(new FileInputStream(file));
+                                       }
+                               }
+
+                               @Override
+                               public String getFilename() {
+                                       return file;
+                               }
+                       };
+               } catch (IOException e) {
+                       e.printStackTrace();
+                       return null;
+               }
+       }
 
   /**
    * Recover jalview session from a jalview project archive. Caller may
@@ -2818,12 +2863,19 @@ public class Jalview2XML
     IdentityHashMap<AlignmentI, AlignmentI> importedDatasets = new IdentityHashMap<>();
     Map<String, AlignFrame> gatherToThisFrame = new HashMap<>();
     final String file = jprovider.getFilename();
+
+    List<AlignFrame> alignFrames = new ArrayList<>();
+
     try
     {
       JarInputStream jin = null;
       JarEntry jarentry = null;
       int entryCount = 1;
 
+
+      // Look for all the entry names ending with ".xml"
+      // This includes all panels and at least one frame.
+//      Platform.timeCheck(null, Platform.TIME_MARK);
       do
       {
         jin = jprovider.getJarInputStream();
@@ -2831,12 +2883,27 @@ public class Jalview2XML
         {
           jarentry = jin.getNextJarEntry();
         }
+        String name = (jarentry == null ? null : jarentry.getName());
 
-        if (jarentry != null && jarentry.getName().endsWith(".xml"))
+//        System.out.println("Jalview2XML opening " + name);
+        if (name != null && name.endsWith(".xml"))
         {
-          InputStreamReader in = new InputStreamReader(jin, UTF_8);
-          // JalviewModel object = new JalviewModel();
 
+          // DataSet for.... is read last.
+          
+          
+          // The question here is what to do with the two
+          // .xml files in the jvp file.
+          // Some number of them, "...Dataset for...", will be the
+          // Only AlignPanels and will have Viewport.
+          // One or more will be the source data, with the DBRefs.
+          //
+          // JVP file writing (above) ensures tha the AlignPanels are written
+          // first, then all relevant datasets (which are
+          // Jalview.datamodel.Alignment).
+          //
+
+//          Platform.timeCheck("Jalview2XML JAXB " + name, Platform.TIME_MARK);
           JAXBContext jc = JAXBContext
                   .newInstance("jalview.xml.binding.jalview");
           XMLStreamReader streamReader = XMLInputFactory.newInstance()
@@ -2844,19 +2911,25 @@ public class Jalview2XML
           javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
           JAXBElement<JalviewModel> jbe = um
                   .unmarshal(streamReader, JalviewModel.class);
-          JalviewModel object = jbe.getValue();
+          JalviewModel model = jbe.getValue();
 
-          /*
-          Unmarshaller unmar = new Unmarshaller(object);
-          unmar.setValidation(false);
-          object = (JalviewModel) unmar.unmarshal(in);
-          */
           if (true) // !skipViewport(object))
           {
-            _af = loadFromObject(object, file, true, jprovider);
-            if (_af != null && object.getViewport().size() > 0)
-            // getJalviewModelSequence().getViewportCount() > 0)
+            // Q: Do we have to load from the model, even if it
+            // does not have a viewport, could we discover that early on?
+            // Q: Do we need to load this object?
+            _af = loadFromObject(model, file, true, jprovider);
+//            Platform.timeCheck("Jalview2XML.loadFromObject",
+            // Platform.TIME_MARK);
+
+            if (_af != null)
             {
+              alignFrames.add(_af);
+            }
+            if (_af != null && model.getViewport().size() > 0)
+            {
+
+              // That is, this is one of the AlignmentPanel models
               if (af == null)
               {
                 // store a reference to the first view
@@ -2876,6 +2949,7 @@ public class Jalview2XML
                       af.getViewport().getAlignment().getDataset());
             }
           }
+//          Platform.timeCheck("JAXB " + name, Platform.TIME_MARK);
           entryCount++;
         }
         else if (jarentry != null)
@@ -2884,7 +2958,10 @@ public class Jalview2XML
           entryCount++;
         }
       } while (jarentry != null);
+//      Platform.timeCheck("JAXB loop exit", Platform.TIME_MARK);
       resolveFrefedSequences();
+//      Platform.timeCheck("JAXB resolveFrefed", Platform.TIME_MARK);
+
     } catch (IOException ex)
     {
       ex.printStackTrace();
@@ -2899,9 +2976,9 @@ public class Jalview2XML
       {
         // used to attempt to parse as V1 castor-generated xml
       }
-      if (Desktop.instance != null)
+      if (Desktop.getInstance() != null)
       {
-        Desktop.instance.stopLoading();
+        Desktop.getInstance().stopLoading();
       }
       if (af != null)
       {
@@ -2918,6 +2995,13 @@ public class Jalview2XML
       errorMessage = "Out of memory loading jalview XML file";
       System.err.println("Out of memory whilst loading jalview XML file");
       e.printStackTrace();
+    } finally
+    {
+      for (AlignFrame alf : alignFrames)
+      {
+        alf.alignPanel.setHoldRepaint(false);
+      }
+
     }
 
     /*
@@ -2929,7 +3013,7 @@ public class Jalview2XML
      */
     for (AlignFrame fr : gatherToThisFrame.values())
     {
-      Desktop.instance.gatherViews(fr);
+      Desktop.getInstance().gatherViews(fr);
     }
 
     restoreSplitFrames();
@@ -2937,8 +3021,7 @@ public class Jalview2XML
     {
       if (ds.getCodonFrames() != null)
       {
-        StructureSelectionManager
-                .getStructureSelectionManager(Desktop.instance)
+        Desktop.getStructureSelectionManager()
                 .registerMappings(ds.getCodonFrames());
       }
     }
@@ -2947,9 +3030,9 @@ public class Jalview2XML
       reportErrors();
     }
 
-    if (Desktop.instance != null)
+    if (Desktop.getInstance() != null)
     {
-      Desktop.instance.stopLoading();
+      Desktop.getInstance().stopLoading();
     }
 
     return af;
@@ -3030,7 +3113,7 @@ public class Jalview2XML
      */
     for (SplitFrame sf : gatherTo)
     {
-      Desktop.instance.gatherViews(sf);
+      Desktop.getInstance().gatherViews(sf);
     }
 
     splitFrameCandidates.clear();
@@ -3089,7 +3172,7 @@ public class Jalview2XML
           @Override
           public void run()
           {
-            JvOptionPane.showInternalMessageDialog(Desktop.desktop,
+            JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
                     finalErrorMessage,
                     "Error " + (saving ? "saving" : "loading")
                             + " Jalview file",
@@ -3248,7 +3331,9 @@ public class Jalview2XML
   }
 
   /**
-   * Load alignment frame from jalview XML DOM object
+   * Load alignment frame from jalview XML DOM object. For a DOM object that
+   * includes one or more Viewport elements (one with a title that does NOT
+   * contain "Dataset for"), create the frame.
    * 
    * @param jalviewModel
    *          DOM
@@ -3263,9 +3348,13 @@ public class Jalview2XML
   AlignFrame loadFromObject(JalviewModel jalviewModel, String file,
           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
   {
+
+//    Platform.timeCheck("Jalview2XML.loadFromObject0", Platform.TIME_MARK);
+
     SequenceSet vamsasSet = jalviewModel.getVamsasModel().getSequenceSet().get(0);
     List<Sequence> vamsasSeqs = vamsasSet.getSequence();
 
+
     // JalviewModelSequence jms = object.getJalviewModelSequence();
 
     // Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
@@ -3296,6 +3385,7 @@ public class Jalview2XML
               : view.getId() + uniqueSetSuffix);
     }
 
+//    Platform.timeCheck("Jalview2XML.loadFromObject1", Platform.TIME_MARK);
     // ////////////////////////////////
     // LOAD SEQUENCES
 
@@ -3316,6 +3406,7 @@ public class Jalview2XML
       SequenceI tmpSeq = seqRefIds.get(seqId);
       if (tmpSeq != null)
       {
+        //
         if (!incompleteSeqs.containsKey(seqId))
         {
           // may not need this check, but keep it for at least 2.9,1 release
@@ -3380,6 +3471,8 @@ public class Jalview2XML
       }
     }
 
+//    Platform.timeCheck("Jalview2XML.loadFromObject-seq",
+//            Platform.TIME_MARK);
     // /
     // Create the alignment object from the sequence set
     // ///////////////////////////////
@@ -3420,6 +3513,8 @@ public class Jalview2XML
       recoverDatasetFor(vamsasSet, al, isdsal, uniqueSeqSetId);
     }
 
+//    Platform.timeCheck("Jalview2XML.loadFromObject-align",
+//            Platform.TIME_MARK);
     if (referenceseqForView != null)
     {
       al.setSeqrep(referenceseqForView);
@@ -3432,6 +3527,8 @@ public class Jalview2XML
       al.setProperty(ssp.getKey(), ssp.getValue());
     }
 
+//    Platform.timeCheck("Jalview2XML.loadFromObject-setseqprop",
+//            Platform.TIME_MARK);
     // ///////////////////////////////
 
     Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
@@ -3445,6 +3542,7 @@ public class Jalview2XML
       // now, for 2.10 projects, this is also done if the xml doc includes
       // dataset sequences not actually present in any particular view.
       //
+//      Platform.timeCheck("J2XML features0", Platform.TIME_RESET);
       for (int i = 0; i < vamsasSeqs.size(); i++)
       {
         JSeq jseq = jseqs.get(i);
@@ -3499,7 +3597,9 @@ public class Jalview2XML
             }
 
             // adds feature to datasequence's feature set (since Jalview 2.10)
+//            Platform.timeCheck(null, Platform.TIME_SET);
             al.getSequenceAt(i).addSequenceFeature(sf);
+//            Platform.timeCheck(null, Platform.TIME_MARK);
           }
         }
         if (vamsasSeqs.get(i).getDBRef().size() > 0)
@@ -3559,8 +3659,7 @@ public class Jalview2XML
             {
               entry.setProperty(prop.getName(), prop.getValue());
             }
-            StructureSelectionManager
-                    .getStructureSelectionManager(Desktop.instance)
+            Desktop.getStructureSelectionManager()
                     .registerPDBEntry(entry);
             // adds PDBEntry to datasequence's set (since Jalview 2.10)
             if (al.getSequenceAt(i).getDatasetSequence() != null)
@@ -3573,7 +3672,12 @@ public class Jalview2XML
             }
           }
         }
+
       }
+
+//      Platform.timeCheck("features done", Platform.TIME_GET);
+//      Platform.timeCheck("Jalview2XML.loadFromObject-endmultiview",
+//              Platform.TIME_MARK);
     } // end !multipleview
 
     // ///////////////////////////////
@@ -3615,6 +3719,8 @@ public class Jalview2XML
           al.addCodonFrame(cf);
         }
       }
+//      Platform.timeCheck("Jalview2XML.loadFromObject-seqmap",
+//              Platform.TIME_MARK);
     }
 
     // ////////////////////////////////
@@ -3675,6 +3781,9 @@ public class Jalview2XML
         }
         // Construct new annotation from model.
         List<AnnotationElement> ae = annotation.getAnnotationElement();
+//        System.err.println(
+//                "Jalview2XML processing " + ae.size() + " annotations");
+
         jalview.datamodel.Annotation[] anot = null;
         java.awt.Color firstColour = null;
         int anpos;
@@ -3710,6 +3819,7 @@ public class Jalview2XML
             }
           }
         }
+        // create the new AlignmentAnnotation
         jalview.datamodel.AlignmentAnnotation jaa = null;
 
         if (annotation.isGraph())
@@ -3746,6 +3856,7 @@ public class Jalview2XML
           jaa._linecolour = firstColour;
         }
         // register new annotation
+        // Annotation graphs such as Conservation will not have id.
         if (annotation.getId() != null)
         {
           annotationIds.put(annotation.getId(), jaa);
@@ -3834,6 +3945,8 @@ public class Jalview2XML
           al.addAnnotation(jaa);
         }
       }
+//      Platform.timeCheck("Jalview2XML.loadFromObject-annot",
+//              Platform.TIME_MARK);
     }
     // ///////////////////////
     // LOAD GROUPS
@@ -3859,7 +3972,7 @@ public class Jalview2XML
           }
           else
           {
-            cs = ColourSchemeProperty.getColourScheme(al,
+            cs = ColourSchemeProperty.getColourScheme(null, al,
                     jGroup.getColour());
           }
         }
@@ -3948,6 +4061,8 @@ public class Jalview2XML
                   jGroup.getAnnotationColours(), null, al, jalviewModel, false));
         }
       }
+//      Platform.timeCheck("Jalview2XML.loadFromObject-groups",
+//              Platform.TIME_MARK);
     }
     if (view == null)
     {
@@ -3957,8 +4072,6 @@ public class Jalview2XML
     // ///////////////////////////////
     // LOAD VIEWPORT
 
-    AlignFrame af = null;
-    AlignViewport av = null;
     // now check to see if we really need to create a new viewport.
     if (multipleView && viewportsAdded.size() == 0)
     {
@@ -3989,6 +4102,8 @@ public class Jalview2XML
         }
 
       }
+//      Platform.timeCheck("Jalview2XML.loadFromObject-viewport",
+//              Platform.TIME_MARK);
     }
     /**
      * indicate that annotation colours are applied across all groups (pre
@@ -3997,8 +4112,9 @@ public class Jalview2XML
     boolean doGroupAnnColour = Jalview2XML.isVersionStringLaterThan("2.8.1",
             jalviewModel.getVersion());
 
+    AlignFrame af = null;
     AlignmentPanel ap = null;
-    boolean isnewview = true;
+    AlignViewport av = null;
     if (viewId != null)
     {
       // Check to see if this alignment already has a view id == viewId
@@ -4008,25 +4124,27 @@ public class Jalview2XML
       {
         for (int v = 0; v < views.length; v++)
         {
-          if (views[v].av.getViewId().equalsIgnoreCase(viewId))
+          ap = views[v];
+          av = ap.av;
+          if (av.getViewId().equalsIgnoreCase(viewId))
           {
             // recover the existing alignpanel, alignframe, viewport
-            af = views[v].alignFrame;
-            av = views[v].av;
-            ap = views[v];
+            af = ap.alignFrame;
+            break;
             // TODO: could even skip resetting view settings if we don't want to
             // change the local settings from other jalview processes
-            isnewview = false;
           }
         }
       }
     }
 
-    if (isnewview)
+    if (af == null)
     {
       af = loadViewport(file, jseqs, hiddenSeqs, al, jalviewModel, view,
               uniqueSeqSetId, viewId, autoAlan);
       av = af.getViewport();
+      // note that this only retrieves the most recently accessed
+      // tab of an AlignFrame.
       ap = af.alignPanel;
     }
 
@@ -4035,14 +4153,61 @@ public class Jalview2XML
      * 
      * Not done if flag is false (when this method is used for New View)
      */
+    final AlignFrame af0 = af;
+    final AlignViewport av0 = av;
+    final AlignmentPanel ap0 = ap;
+//    Platform.timeCheck("Jalview2XML.loadFromObject-beforetree",
+//            Platform.TIME_MARK);
     if (loadTreesAndStructures)
     {
-      loadTrees(jalviewModel, view, af, av, ap);
-      loadPCAViewers(jalviewModel, ap);
-      loadPDBStructures(jprovider, jseqs, af, ap);
-      loadRnaViewers(jprovider, jseqs, ap);
+      if (!jalviewModel.getTree().isEmpty())
+      {
+        SwingUtilities.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+//            Platform.timeCheck(null, Platform.TIME_MARK);
+            loadTrees(jalviewModel, view, af0, av0, ap0);
+//            Platform.timeCheck("Jalview2XML.loadTrees", Platform.TIME_MARK);
+          }
+        });
+      }
+      if (!jalviewModel.getPcaViewer().isEmpty())
+      {
+        SwingUtilities.invokeLater(new Runnable()
+        {
+          @Override
+          public void run()
+          {
+//            Platform.timeCheck(null, Platform.TIME_MARK);
+            loadPCAViewers(jalviewModel, ap0);
+//            Platform.timeCheck("Jalview2XML.loadPCA", Platform.TIME_MARK);
+          }
+        });
+      }
+      SwingUtilities.invokeLater(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+//          Platform.timeCheck(null, Platform.TIME_MARK);
+          loadPDBStructures(jprovider, jseqs, af0, ap0);
+//          Platform.timeCheck("Jalview2XML.loadPDB", Platform.TIME_MARK);
+        }
+      });
+      SwingUtilities.invokeLater(new Runnable()
+      {
+        @Override
+        public void run()
+        {
+          loadRnaViewers(jprovider, jseqs, ap0);
+        }
+      });
     }
     // and finally return.
+    // but do not set holdRepaint true just yet, because this could be the
+    // initial frame with just its dataset.
     return af;
   }
 
@@ -4059,7 +4224,7 @@ public class Jalview2XML
    * @param jseqs
    * @param ap
    */
-  private void loadRnaViewers(jarInputStreamProvider jprovider,
+  protected void loadRnaViewers(jarInputStreamProvider jprovider,
           List<JSeq> jseqs, AlignmentPanel ap)
   {
     /*
@@ -4187,10 +4352,8 @@ public class Jalview2XML
           // TODO: verify 'associate with all views' works still
           tp.getTreeCanvas().setViewport(av); // af.viewport;
           tp.getTreeCanvas().setAssociatedPanel(ap); // af.alignPanel;
-          // FIXME: should we use safeBoolean here ?
-          tp.getTreeCanvas().setApplyToAllViews(tree.isLinkToAllViews());
-
         }
+        tp.getTreeCanvas().setApplyToAllViews(tree.isLinkToAllViews());
         if (tp == null)
         {
           warn("There was a problem recovering stored Newick tree: \n"
@@ -4280,7 +4443,7 @@ public class Jalview2XML
             int height = safeInt(structureState.getHeight());
 
             // Probably don't need to do this anymore...
-            // Desktop.desktop.getComponentAt(x, y);
+            // Desktop.getDesktop().getComponentAt(x, y);
             // TODO: NOW: check that this recovers the PDB file correctly.
             String pdbFile = loadPDBFile(jprovider, pdbid.getId(),
                     pdbid.getFile());
@@ -4599,9 +4762,9 @@ public class Jalview2XML
     final AlignFrame alf = af;
     final Rectangle rect = new Rectangle(svattrib.getX(), svattrib.getY(),
             svattrib.getWidth(), svattrib.getHeight());
-    try
-    {
-      javax.swing.SwingUtilities.invokeAndWait(new Runnable()
+    // try
+    // {
+      javax.swing.SwingUtilities.invokeLater(new Runnable()
       {
         @Override
         public void run()
@@ -4628,14 +4791,14 @@ public class Jalview2XML
           }
         }
       });
-    } catch (InvocationTargetException ex)
-    {
-      warn("Unexpected error when opening Jmol view.", ex);
-
-    } catch (InterruptedException e)
-    {
-      // e.printStackTrace();
-    }
+    // } catch (InvocationTargetException ex)
+    // {
+    // warn("Unexpected error when opening Jmol view.", ex);
+    //
+    // } catch (InterruptedException e)
+    // {
+    // // e.printStackTrace();
+    // }
 
   }
 
@@ -4764,7 +4927,7 @@ public class Jalview2XML
     {
       try
       {
-        frames = Desktop.desktop.getAllFrames();
+        frames = Desktop.getDesktopPane().getAllFrames();
       } catch (ArrayIndexOutOfBoundsException e)
       {
         // occasional No such child exceptions are thrown here...
@@ -4835,14 +4998,25 @@ public class Jalview2XML
   }
 
   AlignFrame loadViewport(String file, List<JSeq> JSEQ,
-          List<SequenceI> hiddenSeqs, AlignmentI al,
-          JalviewModel jm, Viewport view, String uniqueSeqSetId,
-          String viewId, List<JvAnnotRow> autoAlan)
+          List<SequenceI> hiddenSeqs, AlignmentI al, JalviewModel jm,
+          Viewport view, String uniqueSeqSetId, String viewId,
+          List<JvAnnotRow> autoAlan)
   {
     AlignFrame af = null;
     af = new AlignFrame(al, safeInt(view.getWidth()),
-            safeInt(view.getHeight()), uniqueSeqSetId, viewId);
-
+            safeInt(view.getHeight()), uniqueSeqSetId, viewId)
+    // {
+    //
+    // @Override
+    // protected void processKeyEvent(java.awt.event.KeyEvent e) {
+    // System.out.println("Jalview2XML AF " + e);
+    // super.processKeyEvent(e);
+    //
+    // }
+    //
+    // }
+    ;
+    af.alignPanel.setHoldRepaint(true);
     af.setFileName(file, FileFormat.Jalview);
 
     final AlignViewport viewport = af.getViewport();
@@ -4918,9 +5092,8 @@ public class Jalview2XML
 
     viewport.setColourText(safeBoolean(view.isShowColourText()));
 
-    viewport
-            .setConservationSelected(
-                    safeBoolean(view.isConservationSelected()));
+    viewport.setConservationSelected(
+            safeBoolean(view.isConservationSelected()));
     viewport.setIncrement(safeInt(view.getConsThreshold()));
     viewport.setShowJVSuffix(safeBoolean(view.isShowFullId()));
     viewport.setRightAlignIds(safeBoolean(view.isRightAlignIds()));
@@ -4951,8 +5124,18 @@ public class Jalview2XML
       viewport.setViewName(view.getViewName());
       af.setInitialTabVisible();
     }
-    af.setBounds(safeInt(view.getXpos()), safeInt(view.getYpos()),
-            safeInt(view.getWidth()), safeInt(view.getHeight()));
+    int x = safeInt(view.getXpos());
+    int y = safeInt(view.getYpos());
+    int w = safeInt(view.getWidth());
+    int h = safeInt(view.getHeight());
+    // // BH we cannot let the title bar go off the top
+    // if (Platform.isJS())
+    // {
+    // x = Math.max(50 - w, x);
+    // y = Math.max(0, y);
+    // }
+
+    af.setBounds(x, y, w, h);
     // startSeq set in af.alignPanel.updateLayout below
     af.alignPanel.updateLayout();
     ColourSchemeI cs = null;
@@ -4973,7 +5156,8 @@ public class Jalview2XML
       }
       else
       {
-        cs = ColourSchemeProperty.getColourScheme(al, view.getBgColour());
+        cs = ColourSchemeProperty.getColourScheme(af.getViewport(), al,
+                view.getBgColour());
       }
     }
 
@@ -4995,9 +5179,8 @@ public class Jalview2XML
     af.changeColour(cs);
     viewport.setColourAppliesToAllGroups(true);
 
-    viewport
-            .setShowSequenceFeatures(
-                    safeBoolean(view.isShowSequenceFeatures()));
+    viewport.setShowSequenceFeatures(
+            safeBoolean(view.isShowSequenceFeatures()));
 
     viewport.setCentreColumnLabels(view.isCentreColumnLabels());
     viewport.setIgnoreGapsConsensus(view.isIgnoreGapsinConsensus(), null);
@@ -5018,13 +5201,13 @@ public class Jalview2XML
               .getFeatureRenderer();
       FeaturesDisplayed fdi;
       viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
-      String[] renderOrder = new String[jm.getFeatureSettings()
-              .getSetting().size()];
+      String[] renderOrder = new String[jm.getFeatureSettings().getSetting()
+              .size()];
       Map<String, FeatureColourI> featureColours = new Hashtable<>();
       Map<String, Float> featureOrder = new Hashtable<>();
 
-      for (int fs = 0; fs < jm.getFeatureSettings()
-              .getSetting().size(); fs++)
+      for (int fs = 0; fs < jm.getFeatureSettings().getSetting()
+              .size(); fs++)
       {
         Setting setting = jm.getFeatureSettings().getSetting().get(fs);
         String featureType = setting.getType();
@@ -5036,8 +5219,8 @@ public class Jalview2XML
                 .getMatcherSet();
         if (filters != null)
         {
-          FeatureMatcherSetI filter = Jalview2XML
-                  .parseFilter(featureType, filters);
+          FeatureMatcherSetI filter = Jalview2XML.parseFilter(featureType,
+                  filters);
           if (!filter.isEmpty())
           {
             fr.setFeatureFilter(featureType, filter);
@@ -5069,8 +5252,7 @@ public class Jalview2XML
           float max = setting.getMax() == null ? 1f
                   : setting.getMax().floatValue();
           FeatureColourI gc = new FeatureColour(maxColour, minColour,
-                  maxColour,
-                  noValueColour, min, max);
+                  maxColour, noValueColour, min, max);
           if (setting.getAttributeName().size() > 0)
           {
             gc.setAttributeName(setting.getAttributeName().toArray(
@@ -5104,8 +5286,7 @@ public class Jalview2XML
         }
         else
         {
-          featureColours.put(featureType,
-                  new FeatureColour(maxColour));
+          featureColours.put(featureType, new FeatureColour(maxColour));
         }
         renderOrder[fs] = featureType;
         if (setting.getOrder() != null)
@@ -5173,8 +5354,9 @@ public class Jalview2XML
     String complementaryViewId = view.getComplementId();
     if (complementaryViewId == null)
     {
-      Desktop.addInternalFrame(af, view.getTitle(),
+      Dimension dim = Platform.getDimIfEmbedded(af,
               safeInt(view.getWidth()), safeInt(view.getHeight()));
+      Desktop.addInternalFrame(af, view.getTitle(), dim.width, dim.height);
       // recompute any autoannotation
       af.alignPanel.updateAnnotation(false, true);
       reorderAutoannotation(af, al, autoAlan);
@@ -5271,7 +5453,7 @@ public class Jalview2XML
     else
     {
       cs = new AnnotationColourGradient(matchedAnnotation,
-              ColourSchemeProperty.getColourScheme(al,
+              ColourSchemeProperty.getColourScheme(af.getViewport(), al,
                       viewAnnColour.getColourScheme()),
               safeInt(viewAnnColour.getAboveThreshold()));
     }
@@ -5519,8 +5701,8 @@ public class Jalview2XML
       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
       dseqs.copyInto(dsseqs);
       ds = new jalview.datamodel.Alignment(dsseqs);
-      debug("Created new dataset " + vamsasSet.getDatasetId()
-              + " for alignment " + System.identityHashCode(al));
+//      debug("Jalview2XML Created new dataset " + vamsasSet.getDatasetId()
+//              + " for alignment " + System.identityHashCode(al));
       addDatasetRef(vamsasSet.getDatasetId(), ds);
     }
     // set the dataset for the newly imported alignment.
@@ -5934,14 +6116,7 @@ public class Jalview2XML
     AlignFrame af = loadFromObject(jm, null, false, null);
     af.getAlignPanels().clear();
     af.closeMenuItem_actionPerformed(true);
-
-    /*
-     * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
-     * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
-     * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
-     * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
-     * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
-     */
+    af.alignPanel.setHoldRepaint(false);
 
     return af.alignPanel;
   }
@@ -6292,8 +6467,10 @@ public class Jalview2XML
                   axis.getXPos(), axis.getYPos(), axis.getZPos());
         }
 
+        Dimension dim = Platform.getDimIfEmbedded(panel, 475, 450);
         Desktop.addInternalFrame(panel, MessageManager.formatMessage(
-                "label.calc_title", "PCA", modelName), 475, 450);
+                "label.calc_title", "PCA", modelName), dim.width,
+                dim.height);
       }
     } catch (Exception ex)
     {