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.EnumSet;
25 import java.util.HashMap;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Locale;
30 import java.util.Map.Entry;
32 import jalview.api.structures.JalviewStructureDisplayI;
33 import jalview.bin.Cache;
34 import jalview.bin.Console;
35 import jalview.datamodel.PDBEntry;
36 import jalview.datamodel.SequenceI;
37 import jalview.datamodel.StructureViewerModel;
38 import jalview.structure.StructureSelectionManager;
41 * A proxy for handling structure viewers, that orchestrates adding selected
42 * structures, associated with sequences in Jalview, to an existing viewer, or
43 * opening a new one. Currently supports either Jmol or Chimera as the structure
48 public class StructureViewer
50 private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type ";
52 StructureSelectionManager ssm;
55 * decide if new structures are aligned to existing ones
57 private boolean superposeAdded = true;
60 * whether to open structures in their own thread or not
62 private boolean async = true;
64 public void setAsync(boolean b)
69 public enum ViewerType
71 JMOL, CHIMERA, CHIMERAX, PYMOL;
73 public static ViewerType getFromString(String viewerString)
75 ViewerType viewerType = null;
76 if (!"none".equals(viewerString))
78 for (ViewerType v : EnumSet.allOf(ViewerType.class))
80 String name = v.name().toLowerCase(Locale.ROOT).replaceAll(" ",
82 if (viewerString.equals(name))
97 * @param structureSelectionManager
99 public StructureViewer(
100 StructureSelectionManager structureSelectionManager)
102 ssm = structureSelectionManager;
106 * Factory to create a proxy for modifying existing structure viewer
109 public static StructureViewer reconfigure(
110 JalviewStructureDisplayI display)
112 StructureViewer sv = new StructureViewer(display.getBinding().getSsm());
118 public String toString()
122 return sview.toString();
129 * @return ViewerType for currently configured structure viewer
131 public static ViewerType getViewerType()
133 String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY,
134 ViewerType.JMOL.name());
135 return ViewerType.valueOf(viewType);
138 public void setViewerType(ViewerType type)
140 Cache.setProperty(Preferences.STRUCTURE_DISPLAY, type.name());
144 * View multiple PDB entries, each with associated sequences
151 public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs,
152 SequenceI[] seqs, AlignmentPanel ap)
154 return viewStructures(pdbs, seqs, ap, null);
157 public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs,
158 SequenceI[] seqs, AlignmentPanel ap, ViewerType viewerType)
160 JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqs, ap);
164 * user added structure to an existing viewer - all done
169 if (viewerType == null)
170 viewerType = getViewerType();
172 Map<PDBEntry, SequenceI[]> seqsForPdbs = getSequencesForPdbs(pdbs,
174 PDBEntry[] pdbsForFile = seqsForPdbs.keySet()
175 .toArray(new PDBEntry[seqsForPdbs.size()]);
176 SequenceI[][] theSeqs = seqsForPdbs.values()
177 .toArray(new SequenceI[seqsForPdbs.size()][]);
180 sview.setAlignAddedStructures(superposeAdded);
182 Runnable viewRunnable = new Runnable()
188 for (int pdbep = 0; pdbep < pdbsForFile.length; pdbep++)
190 PDBEntry pdb = pdbsForFile[pdbep];
191 if (!sview.addAlreadyLoadedFile(theSeqs[pdbep], null, ap,
194 sview.addToExistingViewer(pdb, theSeqs[pdbep], null, ap,
199 sview.updateTitleAndMenus();
204 new Thread(viewRunnable).start();
213 if (viewerType.equals(ViewerType.JMOL))
215 sview = new AppJmol(ap, superposeAdded, pdbsForFile, theSeqs);
217 else if (viewerType.equals(ViewerType.CHIMERA))
219 sview = new ChimeraViewFrame(pdbsForFile, superposeAdded, theSeqs,
222 else if (viewerType.equals(ViewerType.CHIMERAX))
224 sview = new ChimeraXViewFrame(pdbsForFile, superposeAdded, theSeqs,
227 else if (viewerType.equals(ViewerType.PYMOL))
229 sview = new PymolViewer(pdbsForFile, superposeAdded, theSeqs, ap);
233 Console.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
239 * Converts the list of selected PDB entries (possibly including duplicates
240 * for multiple chains), and corresponding sequences, into a map of sequences
241 * for each distinct PDB file. Returns null if either argument is null, or
242 * their lengths do not match.
248 Map<PDBEntry, SequenceI[]> getSequencesForPdbs(PDBEntry[] pdbs,
251 if (pdbs == null || seqs == null || pdbs.length != seqs.length)
257 * we want only one 'representative' PDBEntry per distinct file name
258 * (there may be entries for distinct chains)
260 Map<String, PDBEntry> pdbsSeen = new HashMap<>();
263 * LinkedHashMap preserves order of PDB entries (significant if they
264 * will get superimposed to the first structure)
266 Map<PDBEntry, List<SequenceI>> pdbSeqs = new LinkedHashMap<>();
267 for (int i = 0; i < pdbs.length; i++)
269 PDBEntry pdb = pdbs[i];
270 SequenceI seq = seqs[i];
271 String pdbFile = pdb.getFile();
272 if (pdbFile == null || pdbFile.length() == 0)
274 pdbFile = pdb.getId();
276 if (!pdbsSeen.containsKey(pdbFile))
278 pdbsSeen.put(pdbFile, pdb);
279 pdbSeqs.put(pdb, new ArrayList<SequenceI>());
283 pdb = pdbsSeen.get(pdbFile);
285 List<SequenceI> seqsForPdb = pdbSeqs.get(pdb);
286 if (!seqsForPdb.contains(seq))
293 * convert to Map<PDBEntry, SequenceI[]>
295 Map<PDBEntry, SequenceI[]> result = new LinkedHashMap<>();
296 for (Entry<PDBEntry, List<SequenceI>> entry : pdbSeqs.entrySet())
298 List<SequenceI> theSeqs = entry.getValue();
299 result.put(entry.getKey(),
300 theSeqs.toArray(new SequenceI[theSeqs.size()]));
307 * A strictly temporary method pending JAL-1761 refactoring. Determines if all
308 * the passed PDB entries are the same (this is the case if selected sequences
309 * to view structure for are chains of the same structure). If so, calls the
310 * single-pdb version of viewStructures and returns the viewer, else returns
318 private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs,
319 SequenceI[] seqsForPdbs, AlignmentPanel ap)
321 List<SequenceI> seqs = new ArrayList<>();
322 if (pdbs == null || pdbs.length == 0)
327 String firstFile = pdbs[0].getFile();
328 for (PDBEntry pdb : pdbs)
330 String pdbFile = pdb.getFile();
331 if (pdbFile == null || !pdbFile.equals(firstFile))
335 SequenceI pdbseq = seqsForPdbs[i++];
341 return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]),
345 JalviewStructureDisplayI sview = null;
347 public JalviewStructureDisplayI getJalviewStructureDisplay()
352 public JalviewStructureDisplayI viewStructures(PDBEntry pdb,
353 SequenceI[] seqsForPdb, AlignmentPanel ap)
355 return viewStructures(pdb, seqsForPdb, ap, null);
358 public JalviewStructureDisplayI viewStructures(PDBEntry pdb,
359 SequenceI[] seqsForPdb, AlignmentPanel ap, ViewerType viewerType)
363 sview.setAlignAddedStructures(superposeAdded);
364 String pdbId = pdb.getId();
365 if (!sview.addAlreadyLoadedFile(seqsForPdb, null, ap, pdbId))
367 sview.addToExistingViewer(pdb, seqsForPdb, null, ap, pdbId);
369 sview.updateTitleAndMenus();
373 if (viewerType == null)
374 viewerType = getViewerType();
375 if (viewerType.equals(ViewerType.JMOL))
377 sview = new AppJmol(pdb, seqsForPdb, null, ap);
379 else if (viewerType.equals(ViewerType.CHIMERA))
381 sview = new ChimeraViewFrame(pdb, seqsForPdb, null, ap);
383 else if (viewerType.equals(ViewerType.CHIMERAX))
385 sview = new ChimeraXViewFrame(pdb, seqsForPdb, null, ap);
387 else if (viewerType.equals(ViewerType.PYMOL))
389 sview = new PymolViewer(pdb, seqsForPdb, null, ap);
393 Console.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
399 * Creates a new panel controlling a structure viewer
408 public static JalviewStructureDisplayI createView(ViewerType type,
409 AlignmentPanel alignPanel, StructureViewerModel viewerData,
410 String sessionFile, String vid)
412 JalviewStructureDisplayI viewer = null;
416 viewer = new AppJmol(viewerData, alignPanel, sessionFile, vid);
417 // todo or construct and then openSession(sessionFile)?
420 viewer = new ChimeraViewFrame(viewerData, alignPanel, sessionFile,
424 viewer = new ChimeraXViewFrame(viewerData, alignPanel, sessionFile,
428 viewer = new PymolViewer(viewerData, alignPanel, sessionFile, vid);
431 Console.error(UNKNOWN_VIEWER_TYPE + type.toString());
436 public boolean isBusy()
440 if (!sview.hasMapping())
451 * @return true if view is already showing PDBid
453 public boolean hasPdbId(String pDBid)
460 return sview.getBinding().hasPdbId(pDBid);
463 public boolean isVisible()
465 return sview != null && sview.isVisible();
468 public void setSuperpose(boolean alignAddedStructures)
470 superposeAdded = alignAddedStructures;