X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FStructureViewer.java;h=40f9ae1cca5bbd9dc444011c808a4a474a18aeee;hb=a1984b1c8c273ed33c7ce9283039f4027dcae2de;hp=6b6fde40fae9f200d0b029c1f54bf06d0d0b9596;hpb=ab43013b7e357b84b4abade0dba949668dfb2a0e;p=jalview.git diff --git a/src/jalview/gui/StructureViewer.java b/src/jalview/gui/StructureViewer.java index 6b6fde4..40f9ae1 100644 --- a/src/jalview/gui/StructureViewer.java +++ b/src/jalview/gui/StructureViewer.java @@ -1,6 +1,6 @@ /* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1) - * Copyright (C) 2014 The Jalview Authors + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * @@ -20,121 +20,393 @@ */ package jalview.gui; +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; + import jalview.api.structures.JalviewStructureDisplayI; import jalview.bin.Cache; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; +import jalview.datamodel.StructureViewerModel; import jalview.structure.StructureSelectionManager; -import java.awt.Rectangle; - /** - * 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 { + private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type "; + StructureSelectionManager ssm; - public enum Viewer + /** + * decide if new structures are aligned to existing ones + */ + private boolean superposeAdded = true; + + public enum ViewerType { - JMOL, CHIMERA + JMOL, CHIMERA, CHIMERAX, PYMOL }; - public Viewer getViewerType() + /** + * Constructor + * + * @param structureSelectionManager + */ + public StructureViewer(StructureSelectionManager structureSelectionManager) { - String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY, - Viewer.JMOL.name()); - return Viewer.valueOf(viewType); + ssm = structureSelectionManager; } - public void setViewerType(Viewer type) + /** + * Factory to create a proxy for modifying existing structure viewer + * + */ + public static StructureViewer reconfigure( + JalviewStructureDisplayI display) { - Cache.setProperty(Preferences.STRUCTURE_DISPLAY, type.name()); + StructureViewer sv = new StructureViewer(display.getBinding().getSsm()); + sv.sview = display; + return sv; } - public StructureViewer(StructureSelectionManager structureSelectionManager) + + @Override + public String toString() { - ssm = structureSelectionManager; + if (sview != null) + { + return sview.toString(); + } + return "New View"; + } + /** + * + * @return ViewerType for currently configured structure viewer + */ + public static ViewerType getViewerType() + { + String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY, + ViewerType.JMOL.name()); + return ViewerType.valueOf(viewType); } - public JalviewStructureDisplayI viewStructures(AlignmentPanel ap, - PDBEntry[] pr, SequenceI[][] collateForPDB) + public void setViewerType(ViewerType type) { - return viewStructures(getViewerType(), ap, pr, collateForPDB); + Cache.setProperty(Preferences.STRUCTURE_DISPLAY, type.name()); } - public JalviewStructureDisplayI viewStructures(Viewer viewerType, - AlignmentPanel ap, PDBEntry[] pr, SequenceI[][] collateForPDB) + /** + * View multiple PDB entries, each with associated sequences + * + * @param pdbs + * @param seqs + * @param ap + * @return + */ + public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs, + SequenceI[] seqs, AlignmentPanel ap) { - JalviewStructureDisplayI sview = null; - if (viewerType.equals(Viewer.JMOL)) + JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqs, ap); + if (viewer != null) + { + /* + * user added structure to an existing viewer - all done + */ + return viewer; + } + + 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 if (viewerType.equals(ViewerType.CHIMERAX)) { - sview = new AppJmol(ap, pr, ap.av.collateForPDB(pr)); + sview = new ChimeraXViewFrame(pdbsForFile, superposeAdded, theSeqs, + ap); } - else if (viewerType.equals(Viewer.CHIMERA)) + else if (viewerType.equals(ViewerType.PYMOL)) { - sview = new ChimeraViewFrame(ap, pr, ap.av.collateForPDB(pr)); + sview = new PymolViewer(pdbsForFile, superposeAdded, theSeqs, ap); } else { - Cache.log.error("Unknown structure viewer type " - + getViewerType().toString()); + Cache.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString()); } return sview; } - public JalviewStructureDisplayI viewStructures(Viewer viewerType, - AlignmentPanel ap, PDBEntry pr, SequenceI[] collateForPDB) + /** + * 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 seqs + * @return + */ + Map getSequencesForPdbs(PDBEntry[] pdbs, + SequenceI[] seqs) { - JalviewStructureDisplayI sview = null; - if (viewerType.equals(Viewer.JMOL)) + if (pdbs == null || seqs == null || pdbs.length != seqs.length) { - sview = new AppJmol(pr, collateForPDB, null, ap); + return null; } - else if (viewerType.equals(Viewer.CHIMERA)) + + /* + * 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++) { - sview = new ChimeraViewFrame(pr, collateForPDB, null, ap); + PDBEntry pdb = pdbs[i]; + SequenceI seq = seqs[i]; + String pdbFile = pdb.getFile(); + if (pdbFile == null || pdbFile.length() == 0) + { + pdbFile = pdb.getId(); + } + if (!pdbsSeen.containsKey(pdbFile)) + { + 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); + } } - else + + /* + * convert to Map + */ + Map result = new LinkedHashMap<>(); + for (Entry> entry : pdbSeqs.entrySet()) { - Cache.log.error("Unknown structure viewer type " - + getViewerType().toString()); + List theSeqs = entry.getValue(); + result.put(entry.getKey(), + theSeqs.toArray(new SequenceI[theSeqs.size()])); } - return sview; + + return result; } + /** + * 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 + */ + private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs, + SequenceI[] seqsForPdbs, AlignmentPanel ap) + { + List seqs = new ArrayList<>(); + if (pdbs == null || pdbs.length == 0) + { + return null; + } + int i = 0; + String firstFile = pdbs[0].getFile(); + for (PDBEntry pdb : pdbs) + { + String pdbFile = pdb.getFile(); + if (pdbFile == null || !pdbFile.equals(firstFile)) + { + return null; + } + SequenceI pdbseq = seqsForPdbs[i++]; + if (pdbseq != null) + { + seqs.add(pdbseq); + } + } + return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]), + ap); + } + + JalviewStructureDisplayI sview = null; + public JalviewStructureDisplayI viewStructures(PDBEntry pdb, - SequenceI[] sequenceIs, Object object, AlignmentPanel ap) + SequenceI[] seqsForPdb, AlignmentPanel ap) { - return viewStructures(getViewerType(), ap, pdb, sequenceIs); + 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); + } + else if (viewerType.equals(ViewerType.CHIMERA)) + { + sview = new ChimeraViewFrame(pdb, seqsForPdb, null, ap); + } + else if (viewerType.equals(ViewerType.CHIMERAX)) + { + sview = new ChimeraXViewFrame(pdb, seqsForPdb, null, ap); + } + else if (viewerType.equals(ViewerType.PYMOL)) + { + sview = new PymolViewer(pdb, seqsForPdb, null, ap); + } + else + { + Cache.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString()); + } + return sview; } - public JalviewStructureDisplayI createView(Viewer viewer, String[] pdbf, - String[] id, SequenceI[][] sq, AlignmentPanel alignPanel, - boolean useinJmolsuperpos, boolean usetoColourbyseq, - boolean jmolColouring, String fileloc, Rectangle rect, String vid) + /** + * Creates a new panel controlling a structure viewer + * + * @param type + * @param alignPanel + * @param viewerData + * @param sessionFile + * @param vid + * @return + */ + public static JalviewStructureDisplayI createView(ViewerType type, + AlignmentPanel alignPanel, StructureViewerModel viewerData, + String sessionFile, String vid) { - JalviewStructureDisplayI sview = null; - switch (viewer) + JalviewStructureDisplayI viewer = null; + switch (type) { case JMOL: - sview = new AppJmol(pdbf, id, sq, alignPanel, useinJmolsuperpos, - usetoColourbyseq, jmolColouring, fileloc, rect, vid); + viewer = new AppJmol(viewerData, alignPanel, sessionFile, vid); + // todo or construct and then openSession(sessionFile)? break; case CHIMERA: - Cache.log.error("Unsupported structure viewer type " - + viewer.toString()); + viewer = new ChimeraViewFrame(viewerData, alignPanel, sessionFile, + vid); + break; + case CHIMERAX: + viewer = new ChimeraXViewFrame(viewerData, alignPanel, sessionFile, + vid); + break; + case PYMOL: + viewer = new PymolViewer(viewerData, alignPanel, sessionFile, vid); break; default: - Cache.log.error("Unknown structure viewer type " + viewer.toString()); + Cache.error(UNKNOWN_VIEWER_TYPE + type.toString()); } - return sview; + return viewer; + } + + + 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; } }