X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FStructureViewer.java;h=17b786d5c02e9fc62481f81f9c49a1f815888610;hb=14bfc6fb57f123b815f08dbf5b35544abd33b3af;hp=189d490880ba8c563fe46180be6fa21db93e727b;hpb=a5be53df96dabea00429121a742ae476872640eb;p=jalview.git diff --git a/src/jalview/gui/StructureViewer.java b/src/jalview/gui/StructureViewer.java index 189d490..17b786d 100644 --- a/src/jalview/gui/StructureViewer.java +++ b/src/jalview/gui/StructureViewer.java @@ -25,30 +25,82 @@ import jalview.bin.Cache; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.datamodel.StructureViewerModel; +import jalview.structure.StructureMapping; import jalview.structure.StructureSelectionManager; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.ws.DBRefFetcher; +import jalview.ws.sifts.SiftsSettings; import java.awt.Rectangle; import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; /** - * proxy for handling structure viewers. - * - * this allows new views to be created with the currently configured viewer, the - * preferred viewer to be set/read and existing views created previously with a - * particular viewer to be recovered + * A proxy for handling structure viewers, that orchestrates adding selected + * structures, associated with sequences in Jalview, to an existing viewer, or + * opening a new one. Currently supports either Jmol or Chimera as the structure + * viewer. * * @author jprocter */ public class StructureViewer { + + static + { + Platform.ensureJmol(); + } + + private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type "; + StructureSelectionManager ssm; + /** + * decide if new structures are aligned to existing ones + */ + private boolean superposeAdded = true; + public enum ViewerType { JMOL, CHIMERA - }; + } + + /** + * Constructor + * + * @param structureSelectionManager + */ + public StructureViewer(StructureSelectionManager structureSelectionManager) + { + ssm = structureSelectionManager; + } + + /** + * Factory to create a proxy for modifying existing structure viewer + * + */ + public static StructureViewer reconfigure( + JalviewStructureDisplayI display) + { + StructureViewer sv = new StructureViewer(display.getBinding().getSsm()); + sv.sview = display; + return sv; + } + @Override + public String toString() + { + if (sview != null) + { + return sview.toString(); + } + return "New View"; + } public ViewerType getViewerType() { String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY, @@ -61,134 +113,201 @@ public class StructureViewer Cache.setProperty(Preferences.STRUCTURE_DISPLAY, type.name()); } - public StructureViewer(StructureSelectionManager structureSelectionManager) - { - ssm = structureSelectionManager; - } - /** * View multiple PDB entries, each with associated sequences * * @param pdbs - * @param seqsForPdbs + * @param seqs * @param ap * @return */ public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs, - SequenceI[][] seqsForPdbs, AlignmentPanel ap) + SequenceI[] seqs, AlignmentPanel ap) { - JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqsForPdbs, ap); + JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqs, ap); if (viewer != null) { + /* + * user added structure to an existing viewer - all done + */ return viewer; } - return viewStructures(getViewerType(), pdbs, seqsForPdbs, ap); + + ViewerType viewerType = getViewerType(); + + Map seqsForPdbs = getSequencesForPdbs(pdbs, + seqs); + PDBEntry[] pdbsForFile = seqsForPdbs.keySet().toArray( + new PDBEntry[seqsForPdbs.size()]); + SequenceI[][] theSeqs = seqsForPdbs.values().toArray( + new SequenceI[seqsForPdbs.size()][]); + if (sview != null) + { + sview.setAlignAddedStructures(superposeAdded); + new Thread(new Runnable() + { + @Override + public void run() + { + + for (int pdbep = 0; pdbep < pdbsForFile.length; pdbep++) + { + PDBEntry pdb = pdbsForFile[pdbep]; + if (!sview.addAlreadyLoadedFile(theSeqs[pdbep], null, ap, + pdb.getId())) + { + sview.addToExistingViewer(pdb, theSeqs[pdbep], null, ap, + pdb.getId()); + } + } + + sview.updateTitleAndMenus(); + } + }).start(); + return sview; + } + + if (viewerType.equals(ViewerType.JMOL)) + { + sview = new AppJmol(ap, superposeAdded, pdbsForFile, theSeqs); + } + else if (viewerType.equals(ViewerType.CHIMERA)) + { + sview = new ChimeraViewFrame(pdbsForFile, superposeAdded, theSeqs, + ap); + } + else + { + Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString()); + } + return sview; } /** - * A strictly temporary method pending JAL-1761 refactoring. Determines if all - * the passed PDB entries are the same (this is the case if selected sequences - * to view structure for are chains of the same structure). If so, calls the - * single-pdb version of viewStructures and returns the viewer, else returns - * null. + * Converts the list of selected PDB entries (possibly including duplicates + * for multiple chains), and corresponding sequences, into a map of sequences + * for each distinct PDB file. Returns null if either argument is null, or + * their lengths do not match. * * @param pdbs - * @param seqsForPdbs - * @param ap + * @param seqs * @return */ - private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs, - SequenceI[][] seqsForPdbs, AlignmentPanel ap) + Map getSequencesForPdbs(PDBEntry[] pdbs, + SequenceI[] seqs) { - List seqs = new ArrayList(); - if (pdbs == null || pdbs.length == 0) + if (pdbs == null || seqs == null || pdbs.length != seqs.length) { return null; } - int i = 0; - String firstFile = pdbs[0].getFile(); - for (PDBEntry pdb : pdbs) + + /* + * we want only one 'representative' PDBEntry per distinct file name + * (there may be entries for distinct chains) + */ + Map pdbsSeen = new HashMap<>(); + + /* + * LinkedHashMap preserves order of PDB entries (significant if they + * will get superimposed to the first structure) + */ + Map> pdbSeqs = new LinkedHashMap<>(); + for (int i = 0; i < pdbs.length; i++) { + PDBEntry pdb = pdbs[i]; + SequenceI seq = seqs[i]; String pdbFile = pdb.getFile(); - if (pdbFile == null || !pdbFile.equals(firstFile)) + if (pdbFile == null || pdbFile.length() == 0) { - return null; + pdbFile = pdb.getId(); } - SequenceI[] pdbseqs = seqsForPdbs[i++]; - if (pdbseqs != null) + if (!pdbsSeen.containsKey(pdbFile)) { - for (SequenceI sq : pdbseqs) - { - seqs.add(sq); - } + pdbsSeen.put(pdbFile, pdb); + pdbSeqs.put(pdb, new ArrayList()); + } + else + { + pdb = pdbsSeen.get(pdbFile); + } + List seqsForPdb = pdbSeqs.get(pdb); + if (!seqsForPdb.contains(seq)) + { + seqsForPdb.add(seq); } } - return viewStructures(pdbs[0], - seqs.toArray(new SequenceI[seqs.size()]), ap); - } - public JalviewStructureDisplayI viewStructures(PDBEntry pdb, - SequenceI[] seqsForPdb, AlignmentPanel ap) - { - return viewStructures(getViewerType(), pdb, seqsForPdb, ap); - } - - protected JalviewStructureDisplayI viewStructures(ViewerType viewerType, - PDBEntry[] pdbs, SequenceI[][] seqsForPdbs, AlignmentPanel ap) - { - PDBEntry[] pdbsForFile = getUniquePdbFiles(pdbs); - JalviewStructureDisplayI sview = null; - if (viewerType.equals(ViewerType.JMOL)) - { - sview = new AppJmol(ap, pdbsForFile, ap.av.collateForPDB(pdbsForFile)); - } - else if (viewerType.equals(ViewerType.CHIMERA)) + /* + * convert to Map + */ + Map result = new LinkedHashMap<>(); + for (Entry> entry : pdbSeqs.entrySet()) { - sview = new ChimeraViewFrame(pdbsForFile, - ap.av.collateForPDB(pdbsForFile), ap); + List theSeqs = entry.getValue(); + result.put(entry.getKey(), + theSeqs.toArray(new SequenceI[theSeqs.size()])); } - else - { - Cache.log.error("Unknown structure viewer type " - + getViewerType().toString()); - } - return sview; + + return result; } /** - * Convert the array of PDBEntry into an array with no filename repeated + * A strictly temporary method pending JAL-1761 refactoring. Determines if all + * the passed PDB entries are the same (this is the case if selected sequences + * to view structure for are chains of the same structure). If so, calls the + * single-pdb version of viewStructures and returns the viewer, else returns + * null. * * @param pdbs + * @param seqsForPdbs + * @param ap * @return */ - static PDBEntry[] getUniquePdbFiles(PDBEntry[] pdbs) + private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs, + SequenceI[] seqsForPdbs, AlignmentPanel ap) { - if (pdbs == null) + List seqs = new ArrayList<>(); + if (pdbs == null || pdbs.length == 0) { return null; } - List uniques = new ArrayList(); - List filesSeen = new ArrayList(); - for (PDBEntry entry : pdbs) + int i = 0; + String firstFile = pdbs[0].getFile(); + for (PDBEntry pdb : pdbs) { - String file = entry.getFile(); - if (file == null) + String pdbFile = pdb.getFile(); + if (pdbFile == null || !pdbFile.equals(firstFile)) { - uniques.add(entry); + return null; } - else if (!filesSeen.contains(file)) + SequenceI pdbseq = seqsForPdbs[i++]; + if (pdbseq != null) { - uniques.add(entry); - filesSeen.add(file); + seqs.add(pdbseq); } } - return uniques.toArray(new PDBEntry[uniques.size()]); + return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]), + ap); } - protected JalviewStructureDisplayI viewStructures(ViewerType viewerType, - PDBEntry pdb, SequenceI[] seqsForPdb, AlignmentPanel ap) + JalviewStructureDisplayI sview = null; + + public JalviewStructureDisplayI viewStructures(PDBEntry pdb, + SequenceI[] seqsForPdb, AlignmentPanel ap) { - JalviewStructureDisplayI sview = null; + if (sview != null) + { + sview.setAlignAddedStructures(superposeAdded); + String pdbId = pdb.getId(); + if (!sview.addAlreadyLoadedFile(seqsForPdb, null, ap, pdbId)) + { + sview.addToExistingViewer(pdb, seqsForPdb, null, ap, pdbId); + } + sview.updateTitleAndMenus(); + sview.raiseViewer(); + return sview; + } + ViewerType viewerType = getViewerType(); if (viewerType.equals(ViewerType.JMOL)) { sview = new AppJmol(pdb, seqsForPdb, null, ap); @@ -199,8 +318,7 @@ public class StructureViewer } else { - Cache.log.error("Unknown structure viewer type " - + getViewerType().toString()); + Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString()); } return sview; } @@ -219,16 +337,15 @@ public class StructureViewer * @param vid * @return */ - public JalviewStructureDisplayI createView(ViewerType type, - String[] pdbf, String[] id, SequenceI[][] sq, - AlignmentPanel alignPanel, StructureViewerModel viewerData, - String fileloc, Rectangle rect, String vid) + public JalviewStructureDisplayI createView(ViewerType type, String[] pdbf, + String[] id, SequenceI[][] sq, AlignmentPanel alignPanel, + StructureViewerModel viewerData, String fileloc, Rectangle rect, + String vid) { final boolean useinViewerSuperpos = viewerData.isAlignWithPanel(); final boolean usetoColourbyseq = viewerData.isColourWithAlignPanel(); final boolean viewerColouring = viewerData.isColourByViewer(); - JalviewStructureDisplayI sview = null; switch (type) { case JMOL: @@ -236,13 +353,181 @@ public class StructureViewer useinViewerSuperpos, viewerColouring, fileloc, rect, vid); break; case CHIMERA: - Cache.log.error("Unsupported structure viewer type " - + type.toString()); + Cache.log.error( + "Unsupported structure viewer type " + type.toString()); break; default: - Cache.log.error("Unknown structure viewer type " + type.toString()); + Cache.log.error(UNKNOWN_VIEWER_TYPE + type.toString()); } return sview; } + public boolean isBusy() + { + if (sview != null) + { + if (!sview.hasMapping()) + { + return true; + } + } + return false; + } + + /** + * + * @param pDBid + * @return true if view is already showing PDBid + */ + public boolean hasPdbId(String pDBid) + { + if (sview == null) + { + return false; + } + + return sview.getBinding().hasPdbId(pDBid); + } + + public boolean isVisible() + { + return sview != null && sview.isVisible(); + } + + public void setSuperpose(boolean alignAddedStructures) + { + superposeAdded = alignAddedStructures; + } + + /** + * Launch a minimal implementation of a StructureViewer. + * + * @param alignPanel + * @param pdb + * @param seqs + * @return + */ + public static StructureViewer launchStructureViewer( + AlignmentPanel alignPanel, PDBEntry pdb, SequenceI[] seqs) + { + return launchStructureViewer(alignPanel, new PDBEntry[] { pdb }, seqs, + false, null, null); + } + + /** + * Launch a structure viewer with or without an open StructureChooser. + * + * Moved from StructureChooser to enable JalviewJS startup with structure + * display. + * + * @param ap + * @param pdbEntriesToView + * @param sequences + * @param superimpose + * @param theViewer + * @param pb + * @return + */ + protected static StructureViewer launchStructureViewer( + final AlignmentPanel ap, + final PDBEntry[] pdbEntriesToView, SequenceI[] sequences, + boolean superimpose, StructureViewer theViewer, + IProgressIndicator pb) + { + final StructureSelectionManager ssm = ap.getStructureSelectionManager(); + long progressId = sequences.hashCode(); + if (pb != null) + { + pb.setProgressBar(MessageManager + .getString("status.launching_3d_structure_viewer"), progressId); + } + if (theViewer == null) + { + theViewer = new StructureViewer(ssm); + } + theViewer.setSuperpose(superimpose); + + if (pb != null) + { + pb.setProgressBar(null, progressId); + } + if (SiftsSettings.isMapWithSifts()) + { + List seqsWithoutSourceDBRef = new ArrayList<>(); + int p = 0; + // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a + // real PDB ID. For moment, we can also safely do this if there is already + // a known mapping between the PDBEntry and the sequence. + for (SequenceI seq : sequences) + { + PDBEntry pdbe = pdbEntriesToView[p++]; + if (pdbe != null && pdbe.getFile() != null) + { + StructureMapping[] smm = ssm.getMapping(pdbe.getFile()); + if (smm != null && smm.length > 0) + { + for (StructureMapping sm : smm) + { + if (sm.getSequence() == seq) + { + continue; + } + } + } + } + if (seq.getPrimaryDBRefs().isEmpty()) + { + seqsWithoutSourceDBRef.add(seq); + continue; + } + } + if (!seqsWithoutSourceDBRef.isEmpty()) + { + int y = seqsWithoutSourceDBRef.size(); + if (pb != null) + { + pb.setProgressBar(MessageManager.formatMessage( + "status.fetching_dbrefs_for_sequences_without_valid_refs", + y), progressId); + } + SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef + .toArray(new SequenceI[y]); + DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef); + dbRefFetcher.fetchDBRefs(true); + + if (pb != null) + { + pb.setProgressBar("Fetch complete.", progressId); // todo i18n + } + } + } + if (pdbEntriesToView.length > 1) + { + if (pb != null) + { + pb.setProgressBar(MessageManager.getString( + "status.fetching_3d_structures_for_selected_entries"), + progressId); + } + theViewer.viewStructures(pdbEntriesToView, sequences, ap); + } + else + { + if (pb != null) + { + pb.setProgressBar(MessageManager.formatMessage( + "status.fetching_3d_structures_for", + pdbEntriesToView[0].getId()),progressId); + } + theViewer.viewStructures(pdbEntriesToView[0], sequences, ap); + } + if (pb != null) + { + pb.setProgressBar(null, progressId); + } + // remember the last viewer we used... + Desktop.getInstance().lastTargetedView = theViewer; + return theViewer; + } + }