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 jalview.api.structures.JalviewStructureDisplayI;
24 import jalview.bin.Cache;
25 import jalview.datamodel.PDBEntry;
26 import jalview.datamodel.SequenceI;
27 import jalview.datamodel.StructureViewerModel;
28 import jalview.structure.StructureMapping;
29 import jalview.structure.StructureSelectionManager;
30 import jalview.util.MessageManager;
31 import jalview.ws.DBRefFetcher;
32 import jalview.ws.sifts.SiftsSettings;
34 import java.awt.Rectangle;
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.LinkedHashMap;
38 import java.util.List;
40 import java.util.Map.Entry;
43 * A proxy for handling structure viewers, that orchestrates adding selected
44 * structures, associated with sequences in Jalview, to an existing viewer, or
45 * opening a new one. Currently supports either Jmol or Chimera as the structure
50 public class StructureViewer
56 * This is the entry class. Load the core file directly, if it exists. See
62 * swingjs.JSUtil.loadStaticResource$S("core/core_jvjmol.z.js");
66 private static final String UNKNOWN_VIEWER_TYPE = "Unknown structure viewer type ";
68 StructureSelectionManager ssm;
71 * decide if new structures are aligned to existing ones
73 private boolean superposeAdded = true;
75 public enum ViewerType
83 * @param structureSelectionManager
85 public StructureViewer(StructureSelectionManager structureSelectionManager)
87 ssm = structureSelectionManager;
91 * Factory to create a proxy for modifying existing structure viewer
94 public static StructureViewer reconfigure(
95 JalviewStructureDisplayI display)
97 StructureViewer sv = new StructureViewer(display.getBinding().getSsm());
103 public String toString()
107 return sview.toString();
111 public ViewerType getViewerType()
113 String viewType = Cache.getDefault(Preferences.STRUCTURE_DISPLAY,
114 ViewerType.JMOL.name());
115 return ViewerType.valueOf(viewType);
118 public void setViewerType(ViewerType type)
120 Cache.setProperty(Preferences.STRUCTURE_DISPLAY, type.name());
124 * View multiple PDB entries, each with associated sequences
131 public JalviewStructureDisplayI viewStructures(PDBEntry[] pdbs,
132 SequenceI[] seqs, AlignmentPanel ap)
134 JalviewStructureDisplayI viewer = onlyOnePdb(pdbs, seqs, ap);
138 * user added structure to an existing viewer - all done
143 ViewerType viewerType = getViewerType();
145 Map<PDBEntry, SequenceI[]> seqsForPdbs = getSequencesForPdbs(pdbs,
147 PDBEntry[] pdbsForFile = seqsForPdbs.keySet().toArray(
148 new PDBEntry[seqsForPdbs.size()]);
149 SequenceI[][] theSeqs = seqsForPdbs.values().toArray(
150 new SequenceI[seqsForPdbs.size()][]);
153 sview.setAlignAddedStructures(superposeAdded);
154 new Thread(new Runnable()
160 for (int pdbep = 0; pdbep < pdbsForFile.length; pdbep++)
162 PDBEntry pdb = pdbsForFile[pdbep];
163 if (!sview.addAlreadyLoadedFile(theSeqs[pdbep], null, ap,
166 sview.addToExistingViewer(pdb, theSeqs[pdbep], null, ap,
171 sview.updateTitleAndMenus();
177 if (viewerType.equals(ViewerType.JMOL))
179 sview = new AppJmol(ap, superposeAdded, pdbsForFile, theSeqs);
181 else if (viewerType.equals(ViewerType.CHIMERA))
183 sview = new ChimeraViewFrame(pdbsForFile, superposeAdded, theSeqs,
188 Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
194 * Converts the list of selected PDB entries (possibly including duplicates
195 * for multiple chains), and corresponding sequences, into a map of sequences
196 * for each distinct PDB file. Returns null if either argument is null, or
197 * their lengths do not match.
203 Map<PDBEntry, SequenceI[]> getSequencesForPdbs(PDBEntry[] pdbs,
206 if (pdbs == null || seqs == null || pdbs.length != seqs.length)
212 * we want only one 'representative' PDBEntry per distinct file name
213 * (there may be entries for distinct chains)
215 Map<String, PDBEntry> pdbsSeen = new HashMap<>();
218 * LinkedHashMap preserves order of PDB entries (significant if they
219 * will get superimposed to the first structure)
221 Map<PDBEntry, List<SequenceI>> pdbSeqs = new LinkedHashMap<>();
222 for (int i = 0; i < pdbs.length; i++)
224 PDBEntry pdb = pdbs[i];
225 SequenceI seq = seqs[i];
226 String pdbFile = pdb.getFile();
227 if (pdbFile == null || pdbFile.length() == 0)
229 pdbFile = pdb.getId();
231 if (!pdbsSeen.containsKey(pdbFile))
233 pdbsSeen.put(pdbFile, pdb);
234 pdbSeqs.put(pdb, new ArrayList<SequenceI>());
238 pdb = pdbsSeen.get(pdbFile);
240 List<SequenceI> seqsForPdb = pdbSeqs.get(pdb);
241 if (!seqsForPdb.contains(seq))
248 * convert to Map<PDBEntry, SequenceI[]>
250 Map<PDBEntry, SequenceI[]> result = new LinkedHashMap<>();
251 for (Entry<PDBEntry, List<SequenceI>> entry : pdbSeqs.entrySet())
253 List<SequenceI> theSeqs = entry.getValue();
254 result.put(entry.getKey(),
255 theSeqs.toArray(new SequenceI[theSeqs.size()]));
262 * A strictly temporary method pending JAL-1761 refactoring. Determines if all
263 * the passed PDB entries are the same (this is the case if selected sequences
264 * to view structure for are chains of the same structure). If so, calls the
265 * single-pdb version of viewStructures and returns the viewer, else returns
273 private JalviewStructureDisplayI onlyOnePdb(PDBEntry[] pdbs,
274 SequenceI[] seqsForPdbs, AlignmentPanel ap)
276 List<SequenceI> seqs = new ArrayList<>();
277 if (pdbs == null || pdbs.length == 0)
282 String firstFile = pdbs[0].getFile();
283 for (PDBEntry pdb : pdbs)
285 String pdbFile = pdb.getFile();
286 if (pdbFile == null || !pdbFile.equals(firstFile))
290 SequenceI pdbseq = seqsForPdbs[i++];
296 return viewStructures(pdbs[0], seqs.toArray(new SequenceI[seqs.size()]),
300 JalviewStructureDisplayI sview = null;
302 public JalviewStructureDisplayI viewStructures(PDBEntry pdb,
303 SequenceI[] seqsForPdb, AlignmentPanel ap)
307 sview.setAlignAddedStructures(superposeAdded);
308 String pdbId = pdb.getId();
309 if (!sview.addAlreadyLoadedFile(seqsForPdb, null, ap, pdbId))
311 sview.addToExistingViewer(pdb, seqsForPdb, null, ap, pdbId);
313 sview.updateTitleAndMenus();
317 ViewerType viewerType = getViewerType();
318 if (viewerType.equals(ViewerType.JMOL))
320 sview = new AppJmol(pdb, seqsForPdb, null, ap);
322 else if (viewerType.equals(ViewerType.CHIMERA))
324 sview = new ChimeraViewFrame(pdb, seqsForPdb, null, ap);
328 Cache.log.error(UNKNOWN_VIEWER_TYPE + getViewerType().toString());
334 * Create a new panel controlling a structure viewer.
347 public JalviewStructureDisplayI createView(ViewerType type, String[] pdbf,
348 String[] id, SequenceI[][] sq, AlignmentPanel alignPanel,
349 StructureViewerModel viewerData, String fileloc, Rectangle rect,
352 final boolean useinViewerSuperpos = viewerData.isAlignWithPanel();
353 final boolean usetoColourbyseq = viewerData.isColourWithAlignPanel();
354 final boolean viewerColouring = viewerData.isColourByViewer();
359 sview = new AppJmol(pdbf, id, sq, alignPanel, usetoColourbyseq,
360 useinViewerSuperpos, viewerColouring, fileloc, rect, vid);
364 "Unsupported structure viewer type " + type.toString());
367 Cache.log.error(UNKNOWN_VIEWER_TYPE + type.toString());
372 public boolean isBusy()
376 if (!sview.hasMapping())
387 * @return true if view is already showing PDBid
389 public boolean hasPdbId(String pDBid)
396 return sview.getBinding().hasPdbId(pDBid);
399 public boolean isVisible()
401 return sview != null && sview.isVisible();
404 public void setSuperpose(boolean alignAddedStructures)
406 superposeAdded = alignAddedStructures;
410 * Launch a minimal implementation of a StructureViewer.
417 public static StructureViewer launchStructureViewer(
418 AlignmentPanel alignPanel, PDBEntry pdb, SequenceI[] seqs)
420 return launchStructureViewer(alignPanel, new PDBEntry[] { pdb }, seqs,
425 * Launch a structure viewer with or without an open StructureChooser.
427 * Moved from StructureChooser to enable JalviewJS startup with structure
431 * @param pdbEntriesToView
438 protected static StructureViewer launchStructureViewer(
439 final AlignmentPanel ap,
440 final PDBEntry[] pdbEntriesToView, SequenceI[] sequences,
441 boolean superimpose, StructureViewer theViewer,
442 IProgressIndicator pb)
444 final StructureSelectionManager ssm = ap.getStructureSelectionManager();
445 long progressId = sequences.hashCode();
448 pb.setProgressBar(MessageManager
449 .getString("status.launching_3d_structure_viewer"), progressId);
451 if (theViewer == null)
453 theViewer = new StructureViewer(ssm);
455 theViewer.setSuperpose(superimpose);
459 pb.setProgressBar(null, progressId);
461 if (SiftsSettings.isMapWithSifts())
463 List<SequenceI> seqsWithoutSourceDBRef = new ArrayList<>();
465 // TODO: skip PDBEntry:Sequence pairs where PDBEntry doesn't look like a
466 // real PDB ID. For moment, we can also safely do this if there is already
467 // a known mapping between the PDBEntry and the sequence.
468 for (SequenceI seq : sequences)
470 PDBEntry pdbe = pdbEntriesToView[p++];
471 if (pdbe != null && pdbe.getFile() != null)
473 StructureMapping[] smm = ssm.getMapping(pdbe.getFile());
474 if (smm != null && smm.length > 0)
476 for (StructureMapping sm : smm)
478 if (sm.getSequence() == seq)
485 if (seq.getPrimaryDBRefs().isEmpty())
487 seqsWithoutSourceDBRef.add(seq);
491 if (!seqsWithoutSourceDBRef.isEmpty())
493 int y = seqsWithoutSourceDBRef.size();
496 pb.setProgressBar(MessageManager.formatMessage(
497 "status.fetching_dbrefs_for_sequences_without_valid_refs",
500 SequenceI[] seqWithoutSrcDBRef = seqsWithoutSourceDBRef
501 .toArray(new SequenceI[y]);
502 DBRefFetcher dbRefFetcher = new DBRefFetcher(seqWithoutSrcDBRef);
503 dbRefFetcher.fetchDBRefs(true);
507 pb.setProgressBar("Fetch complete.", progressId); // todo i18n
511 if (pdbEntriesToView.length > 1)
515 pb.setProgressBar(MessageManager.getString(
516 "status.fetching_3d_structures_for_selected_entries"),
519 theViewer.viewStructures(pdbEntriesToView, sequences, ap);
525 pb.setProgressBar(MessageManager.formatMessage(
526 "status.fetching_3d_structures_for",
527 pdbEntriesToView[0].getId()),progressId);
529 theViewer.viewStructures(pdbEntriesToView[0], sequences, ap);
533 pb.setProgressBar(null, progressId);
535 // remember the last viewer we used...
536 Desktop.getInstance().lastTargetedView = theViewer;