+ }
+ // Instantiate the associated structure views
+ for (Entry<String, StructureViewerModel> entry : structureViewers
+ .entrySet())
+ {
+ try
+ {
+ createOrLinkStructureViewer(entry, af, ap, jprovider);
+ } catch (Exception e)
+ {
+ System.err.println("Error loading structure viewer: "
+ + e.getMessage());
+ // failed - try the next one
+ }
+ }
+ }
+
+ /**
+ *
+ * @param viewerData
+ * @param af
+ * @param ap
+ * @param jprovider
+ */
+ protected void createOrLinkStructureViewer(
+ Entry<String, StructureViewerModel> viewerData, AlignFrame af,
+ AlignmentPanel ap, jarInputStreamProvider jprovider)
+ {
+ final StructureViewerModel stateData = viewerData.getValue();
+
+ /*
+ * Search for any viewer windows already open from other alignment views
+ * that exactly match the stored structure state
+ */
+ StructureViewerBase comp = findMatchingViewer(viewerData);
+
+ if (comp != null)
+ {
+ linkStructureViewer(ap, comp, stateData);
+ return;
+ }
+
+ /*
+ * From 2.9: stateData.type contains JMOL or CHIMERA, data is in jar entry
+ * "viewer_"+stateData.viewId
+ */
+ if (ViewerType.CHIMERA.toString().equals(stateData.getType()))
+ {
+ createChimeraViewer(viewerData, af, jprovider);
+ }
+ else
+ {
+ /*
+ * else Jmol (if pre-2.9, stateData contains JMOL state string)
+ */
+ createJmolViewer(viewerData, af, jprovider);
+ }
+ }
+
+ /**
+ * Create a new Chimera viewer.
+ *
+ * @param data
+ * @param af
+ * @param jprovider
+ */
+ protected void createChimeraViewer(
+ Entry<String, StructureViewerModel> viewerData, AlignFrame af,
+ jarInputStreamProvider jprovider)
+ {
+ StructureViewerModel data = viewerData.getValue();
+ String chimeraSessionFile = data.getStateData();
+
+ /*
+ * Copy Chimera session from jar entry "viewer_"+viewId to a temporary file
+ *
+ * NB this is the 'saved' viewId as in the project file XML, _not_ the
+ * 'uniquified' sviewid used to reconstruct the viewer here
+ */
+ String viewerJarEntryName = getViewerJarEntryName(data.getViewId());
+ chimeraSessionFile = copyJarEntry(jprovider, viewerJarEntryName,
+ "chimera");
+
+ Set<Entry<File, StructureData>> fileData = data.getFileData()
+ .entrySet();
+ List<PDBEntry> pdbs = new ArrayList<PDBEntry>();
+ List<SequenceI[]> allseqs = new ArrayList<SequenceI[]>();
+ for (Entry<File, StructureData> pdb : fileData)
+ {
+ String filePath = pdb.getValue().getFilePath();
+ String pdbId = pdb.getValue().getPdbId();
+ // pdbs.add(new PDBEntry(filePath, pdbId));
+ pdbs.add(new PDBEntry(pdbId, null, PDBEntry.Type.PDB, filePath));
+ final List<SequenceI> seqList = pdb.getValue().getSeqList();
+ SequenceI[] seqs = seqList.toArray(new SequenceI[seqList.size()]);
+ allseqs.add(seqs);
+ }
+
+ boolean colourByChimera = data.isColourByViewer();
+ boolean colourBySequence = data.isColourWithAlignPanel();
+
+ // TODO use StructureViewer as a factory here, see JAL-1761
+ final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]);
+ final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs
+ .size()][]);
+ String newViewId = viewerData.getKey();
+
+ ChimeraViewFrame cvf = new ChimeraViewFrame(chimeraSessionFile,
+ af.alignPanel, pdbArray, seqsArray, colourByChimera,
+ colourBySequence, newViewId);
+ cvf.setSize(data.getWidth(), data.getHeight());
+ cvf.setLocation(data.getX(), data.getY());
+ }
+
+ /**
+ * Create a new Jmol window. First parse the Jmol state to translate filenames
+ * loaded into the view, and record the order in which files are shown in the
+ * Jmol view, so we can add the sequence mappings in same order.
+ *
+ * @param viewerData
+ * @param af
+ * @param jprovider
+ */
+ protected void createJmolViewer(
+ final Entry<String, StructureViewerModel> viewerData,
+ AlignFrame af, jarInputStreamProvider jprovider)
+ {
+ final StructureViewerModel svattrib = viewerData.getValue();
+ String state = svattrib.getStateData();
+
+ /*
+ * Pre-2.9: state element value is the Jmol state string
+ *
+ * 2.9+: @type is "JMOL", state data is in a Jar file member named "viewer_"
+ * + viewId
+ */
+ if (ViewerType.JMOL.toString().equals(svattrib.getType()))
+ {
+ state = readJarEntry(jprovider,
+ getViewerJarEntryName(svattrib.getViewId()));
+ }
+
+ List<String> pdbfilenames = new ArrayList<String>();
+ List<SequenceI[]> seqmaps = new ArrayList<SequenceI[]>();
+ List<String> pdbids = new ArrayList<String>();
+ StringBuilder newFileLoc = new StringBuilder(64);
+ int cp = 0, ncp, ecp;
+ Map<File, StructureData> oldFiles = svattrib.getFileData();
+ while ((ncp = state.indexOf("load ", cp)) > -1)
+ {
+ do
+ {
+ // look for next filename in load statement
+ newFileLoc.append(state.substring(cp,
+ ncp = (state.indexOf("\"", ncp + 1) + 1)));
+ String oldfilenam = state.substring(ncp,
+ ecp = state.indexOf("\"", ncp));
+ // recover the new mapping data for this old filename
+ // have to normalize filename - since Jmol and jalview do
+ // filename
+ // translation differently.
+ StructureData filedat = oldFiles.get(new File(oldfilenam));
+ newFileLoc.append(Platform.escapeString(filedat.getFilePath()));
+ pdbfilenames.add(filedat.getFilePath());
+ pdbids.add(filedat.getPdbId());
+ seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
+ newFileLoc.append("\"");
+ cp = ecp + 1; // advance beyond last \" and set cursor so we can
+ // look for next file statement.
+ } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
+ }
+ if (cp > 0)
+ {
+ // just append rest of state
+ newFileLoc.append(state.substring(cp));
+ }
+ else
+ {
+ System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
+ newFileLoc = new StringBuilder(state);
+ newFileLoc.append("; load append ");
+ for (File id : oldFiles.keySet())
+ {
+ // add this and any other pdb files that should be present in
+ // the viewer
+ StructureData filedat = oldFiles.get(id);
+ newFileLoc.append(filedat.getFilePath());
+ pdbfilenames.add(filedat.getFilePath());
+ pdbids.add(filedat.getPdbId());
+ seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
+ newFileLoc.append(" \"");
+ newFileLoc.append(filedat.getFilePath());
+ newFileLoc.append("\"");
+
+ }
+ newFileLoc.append(";");
+ }
+
+ if (newFileLoc.length() == 0)
+ {
+ return;
+ }
+ int histbug = newFileLoc.indexOf("history = ");
+ if (histbug > -1)
+ {
+ /*
+ * change "history = [true|false];" to "history = [1|0];"
+ */
+ histbug += 10;
+ int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug);
+ String val = (diff == -1) ? null : newFileLoc
+ .substring(histbug, diff);
+ if (val != null && val.length() >= 4)
+ {
+ if (val.contains("e")) // eh? what can it be?
+ {
+ if (val.trim().equals("true"))
+ {
+ val = "1";
+ }
+ else
+ {
+ val = "0";
+ }
+ newFileLoc.replace(histbug, diff, val);
+ }
+ }
+ }
+
+ final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
+ .size()]);
+ final String[] id = pdbids.toArray(new String[pdbids.size()]);
+ final SequenceI[][] sq = seqmaps
+ .toArray(new SequenceI[seqmaps.size()][]);
+ final String fileloc = newFileLoc.toString();
+ final String sviewid = viewerData.getKey();
+ final AlignFrame alf = af;
+ final Rectangle rect = new Rectangle(svattrib.getX(), svattrib.getY(),
+ svattrib.getWidth(), svattrib.getHeight());
+ try
+ {
+ javax.swing.SwingUtilities.invokeAndWait(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ JalviewStructureDisplayI sview = null;
+ try
+ {
+ sview = new StructureViewer(alf.alignPanel
+ .getStructureSelectionManager()).createView(
+ StructureViewer.ViewerType.JMOL, pdbf, id, sq,
+ alf.alignPanel, svattrib, fileloc, rect, sviewid);
+ addNewStructureViewer(sview);
+ } catch (OutOfMemoryError ex)
+ {
+ new OOMWarning("restoring structure view for PDB id " + id,
+ (OutOfMemoryError) ex.getCause());
+ if (sview != null && sview.isVisible())
+ {
+ sview.closeViewer(false);
+ sview.setVisible(false);
+ sview.dispose();
+ }
+ }
+ }
+ });
+ } catch (InvocationTargetException ex)
+ {
+ warn("Unexpected error when opening Jmol view.", ex);
+
+ } catch (InterruptedException e)
+ {
+ // e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * Generates a name for the entry in the project jar file to hold state
+ * information for a structure viewer
+ *
+ * @param viewId
+ * @return
+ */
+ protected String getViewerJarEntryName(String viewId)
+ {
+ return VIEWER_PREFIX + viewId;
+ }
+
+ /**
+ * Returns any open frame that matches given structure viewer data. The match
+ * is based on the unique viewId, or (for older project versions) the frame's
+ * geometry.
+ *
+ * @param viewerData
+ * @return
+ */
+ protected StructureViewerBase findMatchingViewer(
+ Entry<String, StructureViewerModel> viewerData)
+ {
+ final String sviewid = viewerData.getKey();
+ final StructureViewerModel svattrib = viewerData.getValue();
+ StructureViewerBase comp = null;
+ JInternalFrame[] frames = getAllFrames();
+ for (JInternalFrame frame : frames)
+ {
+ if (frame instanceof StructureViewerBase)
+ {
+ /*
+ * Post jalview 2.4 schema includes structure view id
+ */
+ if (sviewid != null
+ && ((StructureViewerBase) frame).getViewId()
+ .equals(sviewid))
+ {
+ comp = (StructureViewerBase) frame;
+ break; // break added in 2.9
+ }
+ /*
+ * Otherwise test for matching position and size of viewer frame
+ */
+ else if (frame.getX() == svattrib.getX()
+ && frame.getY() == svattrib.getY()
+ && frame.getHeight() == svattrib.getHeight()
+ && frame.getWidth() == svattrib.getWidth())
+ {
+ comp = (StructureViewerBase) frame;
+ // no break in faint hope of an exact match on viewId
+ }
+ }
+ }
+ return comp;
+ }
+
+ /**
+ * Link an AlignmentPanel to an existing structure viewer.
+ *
+ * @param ap
+ * @param viewer
+ * @param oldFiles
+ * @param useinViewerSuperpos
+ * @param usetoColourbyseq
+ * @param viewerColouring
+ */
+ protected void linkStructureViewer(AlignmentPanel ap,
+ StructureViewerBase viewer, StructureViewerModel stateData)
+ {
+ // NOTE: if the jalview project is part of a shared session then
+ // view synchronization should/could be done here.
+
+ final boolean useinViewerSuperpos = stateData.isAlignWithPanel();
+ final boolean usetoColourbyseq = stateData.isColourWithAlignPanel();
+ final boolean viewerColouring = stateData.isColourByViewer();
+ Map<File, StructureData> oldFiles = stateData.getFileData();
+
+ /*
+ * Add mapping for sequences in this view to an already open viewer
+ */
+ final AAStructureBindingModel binding = viewer.getBinding();
+ for (File id : oldFiles.keySet())
+ {
+ // add this and any other pdb files that should be present in the
+ // viewer
+ StructureData filedat = oldFiles.get(id);
+ String pdbFile = filedat.getFilePath();
+ SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
+ binding.getSsm().setMapping(seq, null, pdbFile,
+ jalview.io.AppletFormatAdapter.FILE);
+ binding.addSequenceForStructFile(pdbFile, seq);
+ }
+ // and add the AlignmentPanel's reference to the view panel
+ viewer.addAlignmentPanel(ap);
+ if (useinViewerSuperpos)
+ {
+ viewer.useAlignmentPanelForSuperposition(ap);
+ }
+ else
+ {
+ viewer.excludeAlignmentPanelForSuperposition(ap);
+ }
+ if (usetoColourbyseq)
+ {
+ viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
+ }
+ else
+ {
+ viewer.excludeAlignmentPanelForColourbyseq(ap);
+ }
+ }
+
+ /**
+ * Get all frames within the Desktop.
+ *
+ * @return
+ */
+ protected JInternalFrame[] getAllFrames()
+ {
+ JInternalFrame[] frames = null;
+ // TODO is this necessary - is it safe - risk of hanging?
+ do
+ {
+ try
+ {
+ frames = Desktop.desktop.getAllFrames();
+ } catch (ArrayIndexOutOfBoundsException e)
+ {
+ // occasional No such child exceptions are thrown here...
+ try
+ {
+ Thread.sleep(10);
+ } catch (InterruptedException f)
+ {
+ }
+ }
+ } while (frames == null);
+ return frames;
+ }