2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.LinkedHashMap;
26 import java.util.List;
28 import java.util.Map.Entry;
29 import jalview.api.structures.JalviewStructureDisplayI;
30 import jalview.bin.Cache;
31 import jalview.datamodel.PDBEntry;
32 import jalview.datamodel.SequenceI;
33 import jalview.datamodel.StructureViewerModel;
34 import jalview.structure.StructureSelectionManager;
38 * A proxy for handling structure viewers, that orchestrates adding selected
39 * structures, associated with sequences in Jalview, to an existing viewer, or
40 * opening a new one. Currently supports either Jmol or Chimera as the structure
45 public class StructureViewer
50 Platform.loadStaticResource("core/core_jvjmol.z.js",
51 "org.jmol.viewer.Viewer");
57 private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type ";
59 StructureSelectionManager ssm;
62 * decide if new structures are aligned to existing ones
64 private boolean superposeAdded = true;
66 public enum ViewerType
68 JMOL, CHIMERA, CHIMERAX, PYMOL
74 * @param structureSelectionManager
76 public StructureViewer(StructureSelectionManager structureSelectionManager)
78 ssm = structureSelectionManager;
82 * Factory to create a proxy for modifying existing structure viewer
85 public static StructureViewer reconfigure(
86 JalviewStructureDisplayI display)
88 StructureViewer sv = new StructureViewer(display.getBinding().getSsm());
95 public String toString()
99 return sview.toString();
105 * @return ViewerType for currently configured structure viewer
107 public static ViewerType getViewerType()
109 String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY,
110 ViewerType.JMOL.name());
111 return ViewerType.valueOf(viewType);
114 public void setViewerType(ViewerType type)
116 Cache.setProperty(Preferences.STRUCTURE_DISPLAY, type.name());
120 * View multiple PDB entries, each with associated sequences
127 public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs,
128 SequenceI[] seqs, AlignmentPanel ap)
130 JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqs, ap);
134 * user added structure to an existing viewer - all done
139 ViewerType viewerType = getViewerType();
141 Map<PDBEntry, SequenceI[]> seqsForPdbs = getSequencesForPdbs(pdbs,
143 PDBEntry[] pdbsForFile = seqsForPdbs.keySet().toArray(
144 new PDBEntry[seqsForPdbs.size()]);
145 SequenceI[][] theSeqs = seqsForPdbs.values().toArray(
146 new SequenceI[seqsForPdbs.size()][]);
149 sview.setAlignAddedStructures(superposeAdded);
150 new Thread(new Runnable()
156 for (int pdbep = 0; pdbep < pdbsForFile.length; pdbep++)
158 PDBEntry pdb = pdbsForFile[pdbep];
159 if (!sview.addAlreadyLoadedFile(theSeqs[pdbep], null, ap,
162 sview.addToExistingViewer(pdb, theSeqs[pdbep], null, ap,
167 sview.updateTitleAndMenus();
173 if (viewerType.equals(ViewerType.JMOL))
175 sview = new AppJmol(ap, superposeAdded, pdbsForFile, theSeqs);
177 else if (viewerType.equals(ViewerType.CHIMERA))
179 sview = new ChimeraViewFrame(pdbsForFile, superposeAdded, theSeqs,
182 else if (viewerType.equals(ViewerType.CHIMERAX))
184 sview = new ChimeraXViewFrame(pdbsForFile, superposeAdded, theSeqs,
187 else if (viewerType.equals(ViewerType.PYMOL))
189 sview = new PymolViewer(pdbsForFile, superposeAdded, theSeqs, ap);
193 Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
199 * Converts the list of selected PDB entries (possibly including duplicates
200 * for multiple chains), and corresponding sequences, into a map of sequences
201 * for each distinct PDB file. Returns null if either argument is null, or
202 * their lengths do not match.
208 Map<PDBEntry, SequenceI[]> getSequencesForPdbs(PDBEntry[] pdbs,
211 if (pdbs == null || seqs == null || pdbs.length != seqs.length)
217 * we want only one 'representative' PDBEntry per distinct file name
218 * (there may be entries for distinct chains)
220 Map<String, PDBEntry> pdbsSeen = new HashMap<>();
223 * LinkedHashMap preserves order of PDB entries (significant if they
224 * will get superimposed to the first structure)
226 Map<PDBEntry, List<SequenceI>> pdbSeqs = new LinkedHashMap<>();
227 for (int i = 0; i < pdbs.length; i++)
229 PDBEntry pdb = pdbs[i];
230 SequenceI seq = seqs[i];
231 String pdbFile = pdb.getFile();
232 if (pdbFile == null || pdbFile.length() == 0)
234 pdbFile = pdb.getId();
236 if (!pdbsSeen.containsKey(pdbFile))
238 pdbsSeen.put(pdbFile, pdb);
239 pdbSeqs.put(pdb, new ArrayList<SequenceI>());
243 pdb = pdbsSeen.get(pdbFile);
245 List<SequenceI> seqsForPdb = pdbSeqs.get(pdb);
246 if (!seqsForPdb.contains(seq))
253 * convert to Map<PDBEntry, SequenceI[]>
255 Map<PDBEntry, SequenceI[]> result = new LinkedHashMap<>();
256 for (Entry<PDBEntry, List<SequenceI>> entry : pdbSeqs.entrySet())
258 List<SequenceI> theSeqs = entry.getValue();
259 result.put(entry.getKey(),
260 theSeqs.toArray(new SequenceI[theSeqs.size()]));
267 * A strictly temporary method pending JAL-1761 refactoring. Determines if all
268 * the passed PDB entries are the same (this is the case if selected sequences
269 * to view structure for are chains of the same structure). If so, calls the
270 * single-pdb version of viewStructures and returns the viewer, else returns
278 private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs,
279 SequenceI[] seqsForPdbs, AlignmentPanel ap)
281 List<SequenceI> seqs = new ArrayList<>();
282 if (pdbs == null || pdbs.length == 0)
287 String firstFile = pdbs[0].getFile();
288 for (PDBEntry pdb : pdbs)
290 String pdbFile = pdb.getFile();
291 if (pdbFile == null || !pdbFile.equals(firstFile))
295 SequenceI pdbseq = seqsForPdbs[i++];
301 return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]),
305 JalviewStructureDisplayI sview = null;
307 public JalviewStructureDisplayI viewStructures(PDBEntry pdb,
308 SequenceI[] seqsForPdb, AlignmentPanel ap)
312 sview.setAlignAddedStructures(superposeAdded);
313 String pdbId = pdb.getId();
314 if (!sview.addAlreadyLoadedFile(seqsForPdb, null, ap, pdbId))
316 sview.addToExistingViewer(pdb, seqsForPdb, null, ap, pdbId);
318 sview.updateTitleAndMenus();
322 ViewerType viewerType = getViewerType();
323 if (viewerType.equals(ViewerType.JMOL))
325 sview = new AppJmol(pdb, seqsForPdb, null, ap);
327 else if (viewerType.equals(ViewerType.CHIMERA))
329 sview = new ChimeraViewFrame(pdb, seqsForPdb, null, ap);
331 else if (viewerType.equals(ViewerType.CHIMERAX))
333 sview = new ChimeraXViewFrame(pdb, seqsForPdb, null, ap);
335 else if (viewerType.equals(ViewerType.PYMOL))
337 sview = new PymolViewer(pdb, seqsForPdb, null, ap);
341 Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
347 * Creates a new panel controlling a structure viewer
359 public static JalviewStructureDisplayI createView(ViewerType type,
360 AlignmentPanel alignPanel, StructureViewerModel viewerData,
361 String sessionFile, String vid)
363 JalviewStructureDisplayI viewer = null;
368 viewer = new AppJmol(viewerData, alignPanel, sessionFile, vid);
369 // todo or construct and then openSession(sessionFile)?
372 viewer = new ChimeraViewFrame(viewerData, alignPanel, sessionFile,
376 viewer = new ChimeraXViewFrame(viewerData, alignPanel, sessionFile,
380 viewer = new PymolViewer(viewerData, alignPanel, sessionFile, vid);
383 Cache.log.error(UNKNOWN_VIEWER_TYPE + type.toString());
388 public boolean isBusy()
392 if (!sview.hasMapping())
403 * @return true if view is already showing PDBid
405 public boolean hasPdbId(String pDBid)
412 return sview.getBinding().hasPdbId(pDBid);
415 public boolean isVisible()
417 return sview != null && sview.isVisible();
420 public void setSuperpose(boolean alignAddedStructures)
422 superposeAdded = alignAddedStructures;