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.
21 package jalview.structure;
23 import jalview.analysis.AlignSeq;
24 import jalview.api.StructureSelectionManagerProvider;
25 import jalview.bin.ApplicationSingletonProvider;
26 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
27 import jalview.bin.Cache;
28 import jalview.commands.CommandI;
29 import jalview.commands.EditCommand;
30 import jalview.commands.OrderCommand;
31 import jalview.datamodel.AlignedCodonFrame;
32 import jalview.datamodel.AlignmentAnnotation;
33 import jalview.datamodel.AlignmentI;
34 import jalview.datamodel.Annotation;
35 import jalview.datamodel.HiddenColumns;
36 import jalview.datamodel.PDBEntry;
37 import jalview.datamodel.SearchResults;
38 import jalview.datamodel.SearchResultsI;
39 import jalview.datamodel.SequenceI;
40 import jalview.ext.jmol.JmolParser;
41 import jalview.gui.IProgressIndicator;
42 import jalview.io.AppletFormatAdapter;
43 import jalview.io.DataSourceType;
44 import jalview.io.StructureFile;
45 import jalview.util.MappingUtils;
46 import jalview.util.MessageManager;
47 import jalview.util.Platform;
48 import jalview.ws.sifts.SiftsClient;
49 import jalview.ws.sifts.SiftsException;
50 import jalview.ws.sifts.SiftsSettings;
52 import java.io.PrintStream;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Collections;
56 import java.util.Enumeration;
57 import java.util.HashMap;
58 import java.util.IdentityHashMap;
59 import java.util.List;
61 import java.util.Vector;
64 import mc_view.PDBChain;
65 import mc_view.PDBfile;
67 public class StructureSelectionManager implements ApplicationSingletonI
69 public final static String NEWLINE = System.lineSeparator();
71 private List<StructureMapping> mappings = new ArrayList<>();
73 private boolean processSecondaryStructure = false;
75 private boolean secStructServices = false;
77 private boolean addTempFacAnnot = false;
80 * Set of any registered mappings between (dataset) sequences.
82 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
84 private List<CommandListener> commandListeners = new ArrayList<>();
86 private List<SelectionListener> sel_listeners = new ArrayList<>();
89 * instances of this class scoped by some context class
91 private IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> selectionManagers;
94 * Answers an instance of this class for the current application (Java or JS
99 private static StructureSelectionManager getInstance()
101 return (StructureSelectionManager) ApplicationSingletonProvider
102 .getInstance(StructureSelectionManager.class);
106 * Answers an instance of this class for the current application (Java or JS
107 * 'applet') scope, and scoped to the specified context
112 public static StructureSelectionManager getStructureSelectionManager(
113 StructureSelectionManagerProvider context)
115 return getInstance().getInstanceForContext(context);
119 * Answers an instance of this class scoped to the given context. The instance
120 * is created on the first request for the context, thereafter the same
121 * instance is returned. Note that the context may be null (this is the case
122 * when running headless without a Desktop).
127 StructureSelectionManager getInstanceForContext(
128 StructureSelectionManagerProvider context)
130 StructureSelectionManager instance = selectionManagers.get(context);
131 if (instance == null)
133 instance = new StructureSelectionManager();
134 selectionManagers.put(context, instance);
140 * Private constructor as all 'singleton' instances are managed here or by
141 * ApplicationSingletonProvider
143 private StructureSelectionManager()
145 selectionManagers = new IdentityHashMap<>();
149 * @return true if will try to use external services for processing secondary
152 public boolean isSecStructServices()
154 return secStructServices;
158 * control use of external services for processing secondary structure
160 * @param secStructServices
162 public void setSecStructServices(boolean secStructServices)
164 this.secStructServices = secStructServices;
168 * flag controlling addition of any kind of structural annotation
170 * @return true if temperature factor annotation will be added
172 public boolean isAddTempFacAnnot()
174 return addTempFacAnnot;
178 * set flag controlling addition of structural annotation
180 * @param addTempFacAnnot
182 public void setAddTempFacAnnot(boolean addTempFacAnnot)
184 this.addTempFacAnnot = addTempFacAnnot;
189 * @return if true, the structure manager will attempt to add secondary
190 * structure lines for unannotated sequences
193 public boolean isProcessSecondaryStructure()
195 return processSecondaryStructure;
199 * Control whether structure manager will try to annotate mapped sequences
200 * with secondary structure from PDB data.
204 public void setProcessSecondaryStructure(boolean enable)
206 processSecondaryStructure = enable;
210 * debug function - write all mappings to stdout
212 public void reportMapping()
214 if (mappings.isEmpty())
216 System.err.println("reportMapping: No PDB/Sequence mappings.");
221 "reportMapping: There are " + mappings.size() + " mappings.");
223 for (StructureMapping sm : mappings)
225 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
231 * map between the PDB IDs (or structure identifiers) used by Jalview and the
232 * absolute filenames for PDB data that corresponds to it
234 Map<String, String> pdbIdFileName = new HashMap<>();
236 Map<String, String> pdbFileNameId = new HashMap<>();
238 public void registerPDBFile(String idForFile, String absoluteFile)
240 pdbIdFileName.put(idForFile, absoluteFile);
241 pdbFileNameId.put(absoluteFile, idForFile);
244 public String findIdForPDBFile(String idOrFile)
246 String id = pdbFileNameId.get(idOrFile);
250 public String findFileForPDBId(String idOrFile)
252 String id = pdbIdFileName.get(idOrFile);
256 public boolean isPDBFileRegistered(String idOrFile)
258 return pdbFileNameId.containsKey(idOrFile)
259 || pdbIdFileName.containsKey(idOrFile);
263 * flag controlling whether SeqMappings are relayed from received sequence
264 * mouse over events to other sequences
266 boolean relaySeqMappings = true;
269 * Enable or disable relay of seqMapping events to other sequences. You might
270 * want to do this if there are many sequence mappings and the host computer
275 public void setRelaySeqMappings(boolean relay)
277 relaySeqMappings = relay;
281 * get the state of the relay seqMappings flag.
283 * @return true if sequence mouse overs are being relayed to other mapped
286 public boolean isRelaySeqMappingsEnabled()
288 return relaySeqMappings;
291 Vector<Object> listeners = new Vector<>();
294 * register a listener for alignment sequence mouseover events
298 public void addStructureViewerListener(Object svl)
300 if (!listeners.contains(svl))
302 listeners.addElement(svl);
307 * Returns the filename the PDB id is already mapped to if known, or null if
313 public String alreadyMappedToFile(String pdbid)
315 for (StructureMapping sm : mappings)
317 if (sm.getPdbId().equalsIgnoreCase(pdbid))
326 * Import structure data and register a structure mapping for broadcasting
327 * colouring, mouseovers and selection events (convenience wrapper).
329 * This is the standard entry point.
332 * - one or more sequences to be mapped to pdbFile
333 * @param targetChains
334 * - optional chain specification for mapping each sequence to pdb
335 * (may be nill, individual elements may be nill)
337 * - structure data resource
339 * - how to resolve data from resource
340 * @return null or the structure data parsed as a pdb file
342 synchronized public StructureFile setMapping(SequenceI[] sequence,
343 String[] targetChains, String pdbFile, DataSourceType protocol,
344 IProgressIndicator progress)
346 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
351 * Import a single structure file and register sequence structure mappings for
352 * broadcasting colouring, mouseovers and selection events (convenience
357 * @param forStructureView
358 * when true (testng only), record the mapping for use in mouseOvers
361 * - one or more sequences to be mapped to pdbFile
362 * @param targetChains
363 * - optional chain specification for mapping each sequence to pdb
364 * (may be nill, individual elements may be nill)
366 * - structure data resource
368 * - how to resolve data from resource
369 * @return null or the structure data parsed as a pdb file
371 synchronized public StructureFile setMapping(boolean forStructureView,
372 SequenceI[] sequenceArray, String[] targetChainIds,
373 String pdbFile, DataSourceType sourceType)
375 return computeMapping(forStructureView, sequenceArray, targetChainIds,
376 pdbFile, sourceType, null);
380 * create sequence structure mappings between each sequence and the given
381 * pdbFile (retrieved via the given protocol). Either constructs a mapping
382 * using NW alignment or derives one from any available SIFTS mapping data.
384 * @param forStructureView
385 * when true, record the mapping for use in mouseOvers
387 * @param sequenceArray
388 * - one or more sequences to be mapped to pdbFile
389 * @param targetChainIds
390 * - optional chain specification for mapping each sequence to pdb
391 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
392 * - this should be List<List<String>>, empty lists indicate no
393 * predefined mappings
395 * - structure data resource
397 * - how to resolve data from resource
398 * @param IProgressIndicator
399 * reference to UI component that maintains a progress bar for the
401 * @return null or the structure data parsed as a pdb file
403 synchronized private StructureFile computeMapping(
404 boolean forStructureView, SequenceI[] sequenceArray,
405 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
406 IProgressIndicator progress)
408 long progressSessionId = System.currentTimeMillis() * 3;
411 * do we extract and transfer annotation from 3D data ?
413 // FIXME: possibly should just delete
415 boolean parseSecStr = processSecondaryStructure
416 ? isStructureFileProcessed(pdbFile, sequenceArray)
419 StructureFile pdb = null;
420 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
423 // FIXME if sourceType is not null, we've lost data here
424 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
425 pdb = new JmolParser(false, pdbFile, sourceType);
426 pdb.addSettings(parseSecStr && processSecondaryStructure,
427 parseSecStr && addTempFacAnnot,
428 parseSecStr && secStructServices);
430 if (pdb.getId() != null && pdb.getId().trim().length() > 0
431 && DataSourceType.FILE == sourceType)
433 registerPDBFile(pdb.getId().trim(), pdbFile);
435 // if PDBId is unavailable then skip SIFTS mapping execution path
436 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
438 } catch (Exception ex)
440 ex.printStackTrace();
444 * sifts client - non null if SIFTS mappings are to be used
446 SiftsClient siftsClient = null;
451 siftsClient = new SiftsClient(pdb);
453 } catch (SiftsException e)
455 isMapUsingSIFTs = false;
456 Cache.log.error("SIFTS mapping failed", e);
457 Cache.log.error("Falling back on Needleman & Wunsch alignment");
461 String targetChainId;
462 for (int s = 0; s < sequenceArray.length; s++)
464 boolean infChain = true;
465 final SequenceI seq = sequenceArray[s];
467 while (ds.getDatasetSequence() != null)
469 ds = ds.getDatasetSequence();
472 if (targetChainIds != null && targetChainIds[s] != null)
475 targetChainId = targetChainIds[s];
477 else if (seq.getName().indexOf("|") > -1)
479 targetChainId = seq.getName()
480 .substring(seq.getName().lastIndexOf("|") + 1);
481 if (targetChainId.length() > 1)
483 if (targetChainId.trim().length() == 0)
489 // not a valid chain identifier
500 * Attempt pairwise alignment of the sequence with each chain in the PDB,
501 * and remember the highest scoring chain
504 AlignSeq maxAlignseq = null;
505 String maxChainId = " ";
506 PDBChain maxChain = null;
507 boolean first = true;
508 for (PDBChain chain : pdb.getChains())
510 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
513 continue; // don't try to map chains don't match.
515 // TODO: correctly determine sequence type for mixed na/peptide
517 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
518 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
521 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
522 // as.calcScoreMatrix();
523 // as.traceAlignment();
525 if (first || as.maxscore > max
526 || (as.maxscore == max && chain.id.equals(targetChainId)))
532 maxChainId = chain.id;
535 if (maxChain == null)
540 if (sourceType == DataSourceType.PASTE)
542 pdbFile = "INLINE" + pdb.getId();
545 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
546 if (isMapUsingSIFTs && seq.isProtein())
548 if (progress!=null) {
549 progress.setProgressBar(MessageManager
550 .getString("status.obtaining_mapping_with_sifts"),
553 jalview.datamodel.Mapping sqmpping = maxAlignseq
554 .getMappingFromS1(false);
555 if (targetChainId != null && !targetChainId.trim().isEmpty())
557 StructureMapping siftsMapping;
560 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
561 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
562 seqToStrucMapping.add(siftsMapping);
563 maxChain.makeExactMapping(siftsMapping, seq);
564 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
566 maxChain.transferResidueAnnotation(siftsMapping, null);
567 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
569 } catch (SiftsException e)
571 // fall back to NW alignment
572 System.err.println(e.getMessage());
573 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
574 targetChainId, maxChain, pdb, maxAlignseq);
575 seqToStrucMapping.add(nwMapping);
576 maxChain.makeExactMapping(maxAlignseq, seq);
577 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
580 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
581 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
586 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
587 for (PDBChain chain : pdb.getChains())
589 StructureMapping siftsMapping = null;
592 siftsMapping = getStructureMapping(seq,
593 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
595 foundSiftsMappings.add(siftsMapping);
596 chain.makeExactMapping(siftsMapping, seq);
597 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
599 chain.transferResidueAnnotation(siftsMapping, null);
600 } catch (SiftsException e)
602 System.err.println(e.getMessage());
608 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
609 System.err.println(e.getMessage());
612 if (!foundSiftsMappings.isEmpty())
614 seqToStrucMapping.addAll(foundSiftsMappings);
615 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
619 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
620 maxChainId, maxChain, pdb, maxAlignseq);
621 seqToStrucMapping.add(nwMapping);
622 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
624 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
625 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
631 if (progress != null)
633 progress.setProgressBar(MessageManager
634 .getString("status.obtaining_mapping_with_nw_alignment"),
637 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
638 maxChain, pdb, maxAlignseq);
639 seqToStrucMapping.add(nwMapping);
640 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
642 if (forStructureView)
644 for (StructureMapping sm : seqToStrucMapping)
646 addStructureMapping(sm); // not addAll!
649 if (progress != null)
651 progress.setProgressBar(null, progressSessionId);
658 * check if we need to extract secondary structure from given pdbFile and
659 * transfer to sequences
662 * @param sequenceArray
665 private boolean isStructureFileProcessed(String pdbFile,
666 SequenceI[] sequenceArray)
668 boolean parseSecStr = true;
669 if (isPDBFileRegistered(pdbFile))
671 for (SequenceI sq : sequenceArray)
674 while (ds.getDatasetSequence() != null)
676 ds = ds.getDatasetSequence();
678 if (ds.getAnnotation() != null)
680 for (AlignmentAnnotation ala : ds.getAnnotation())
682 // false if any annotation present from this structure
683 // JBPNote this fails for jmol/chimera view because the *file* is
684 // passed, not the structure data ID -
685 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
696 public void addStructureMapping(StructureMapping sm)
698 if (!mappings.contains(sm))
705 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
710 * @param targetChainId
716 * client for retrieval of SIFTS mappings for this structure
718 * @throws SiftsException
720 private StructureMapping getStructureMapping(SequenceI seq,
721 String pdbFile, String targetChainId, StructureFile pdb,
722 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
723 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
725 StructureMapping curChainMapping = siftsClient
726 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
729 PDBChain chain = pdb.findChain(targetChainId);
732 chain.transferResidueAnnotation(curChainMapping, null);
734 } catch (Exception e)
738 return curChainMapping;
741 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
742 String maxChainId, PDBChain maxChain, StructureFile pdb,
743 AlignSeq maxAlignseq)
745 final StringBuilder mappingDetails = new StringBuilder(128);
746 mappingDetails.append(NEWLINE)
747 .append("Sequence \u27f7 Structure mapping details");
748 mappingDetails.append(NEWLINE);
750 .append("Method: inferred with Needleman & Wunsch alignment");
751 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
752 .append(NEWLINE).append("Sequence = ")
753 .append(maxChain.sequence.getSequenceAsString());
754 mappingDetails.append(NEWLINE).append("No of residues = ")
755 .append(maxChain.residues.size()).append(NEWLINE)
757 PrintStream ps = new PrintStream(System.out)
760 public void print(String x)
762 mappingDetails.append(x);
766 public void println()
768 mappingDetails.append(NEWLINE);
772 maxAlignseq.printAlignment(ps);
774 mappingDetails.append(NEWLINE).append("PDB start/end ");
775 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
777 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
778 mappingDetails.append(NEWLINE).append("SEQ start/end ");
781 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
783 mappingDetails.append(
784 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
785 mappingDetails.append(NEWLINE);
786 maxChain.makeExactMapping(maxAlignseq, seq);
787 jalview.datamodel.Mapping sqmpping = maxAlignseq
788 .getMappingFromS1(false);
789 maxChain.transferRESNUMFeatures(seq, null);
791 HashMap<Integer, int[]> mapping = new HashMap<>();
798 Atom tmp = maxChain.atoms.elementAt(index);
799 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
800 && tmp.alignmentMapping != -1)
802 resNum = tmp.resNumber;
803 insCode = tmp.insCode;
804 if (tmp.alignmentMapping >= -1)
806 mapping.put(tmp.alignmentMapping + 1,
808 { tmp.resNumber, tmp.atomIndex });
813 } while (index < maxChain.atoms.size());
815 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
816 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
817 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
821 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
823 listeners.removeElement(svl);
824 if (svl instanceof SequenceListener)
826 for (int i = 0; i < listeners.size(); i++)
828 if (listeners.elementAt(i) instanceof StructureListener)
830 ((StructureListener) listeners.elementAt(i))
831 .releaseReferences(svl);
836 if (pdbfiles == null)
842 * Remove mappings to the closed listener's PDB files, but first check if
843 * another listener is still interested
845 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
847 StructureListener sl;
848 for (int i = 0; i < listeners.size(); i++)
850 if (listeners.elementAt(i) instanceof StructureListener)
852 sl = (StructureListener) listeners.elementAt(i);
853 for (String pdbfile : sl.getStructureFiles())
855 pdbs.remove(pdbfile);
861 * Rebuild the mappings set, retaining only those which are for 'other' PDB
866 List<StructureMapping> tmp = new ArrayList<>();
867 for (StructureMapping sm : mappings)
869 if (!pdbs.contains(sm.pdbfile))
880 * Propagate mouseover of a single position in a structure
887 public String mouseOverStructure(int pdbResNum, String chain,
890 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
891 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
892 return mouseOverStructure(atoms);
896 * Propagate mouseover or selection of multiple positions in a structure
900 public String mouseOverStructure(List<AtomSpec> atoms)
902 if (listeners == null)
904 // old or prematurely sent event
907 boolean hasSequenceListener = false;
908 for (int i = 0; i < listeners.size(); i++)
910 if (listeners.elementAt(i) instanceof SequenceListener)
912 hasSequenceListener = true;
915 if (!hasSequenceListener)
920 SearchResultsI results = findAlignmentPositionsForStructurePositions(
922 String result = null;
923 for (Object li : listeners)
925 if (li instanceof SequenceListener)
927 String s = ((SequenceListener) li).highlightSequence(results);
938 * Constructs a SearchResults object holding regions (if any) in the Jalview
939 * alignment which have a mapping to the structure viewer positions in the
945 public SearchResultsI findAlignmentPositionsForStructurePositions(
946 List<AtomSpec> atoms)
948 SearchResultsI results = new SearchResults();
949 for (AtomSpec atom : atoms)
951 SequenceI lastseq = null;
953 for (StructureMapping sm : mappings)
955 if (sm.pdbfile.equals(atom.getPdbFile())
956 && sm.pdbchain.equals(atom.getChain()))
958 int indexpos = sm.getSeqPos(atom.getPdbResNum());
959 if (lastipos != indexpos || lastseq != sm.sequence)
961 results.addResult(sm.sequence, indexpos, indexpos);
963 lastseq = sm.sequence;
964 // construct highlighted sequence list
965 for (AlignedCodonFrame acf : seqmappings)
967 acf.markMappedRegion(sm.sequence, indexpos, results);
977 * highlight regions associated with a position (indexpos) in seq
980 * the sequence that the mouse over occurred on
982 * the absolute position being mouseovered in seq (0 to seq.length())
984 * the sequence position (if -1, seq.findPosition is called to
985 * resolve the residue number)
987 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
990 boolean hasSequenceListeners = handlingVamsasMo
991 || !seqmappings.isEmpty();
992 SearchResultsI results = null;
995 seqPos = seq.findPosition(indexpos);
997 for (int i = 0; i < listeners.size(); i++)
999 Object listener = listeners.elementAt(i);
1000 if (listener == source)
1002 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1003 // Temporary fudge with SequenceListener.getVamsasSource()
1006 if (listener instanceof StructureListener)
1008 highlightStructure((StructureListener) listener, seq, seqPos);
1012 if (listener instanceof SequenceListener)
1014 final SequenceListener seqListener = (SequenceListener) listener;
1015 if (hasSequenceListeners
1016 && seqListener.getVamsasSource() != source)
1018 if (relaySeqMappings)
1020 if (results == null)
1022 results = MappingUtils.buildSearchResults(seq, seqPos,
1025 if (handlingVamsasMo)
1027 results.addResult(seq, seqPos, seqPos);
1030 if (!results.isEmpty())
1032 seqListener.highlightSequence(results);
1037 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1039 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1042 else if (listener instanceof SecondaryStructureListener)
1044 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1052 * Send suitable messages to a StructureListener to highlight atoms
1053 * corresponding to the given sequence position(s)
1059 public void highlightStructure(StructureListener sl, SequenceI seq,
1062 if (!sl.isListeningFor(seq))
1067 List<AtomSpec> atoms = new ArrayList<>();
1068 for (StructureMapping sm : mappings)
1070 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1071 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1072 .getDatasetSequence() == seq.getDatasetSequence()))
1074 for (int index : positions)
1076 atomNo = sm.getAtomNum(index);
1080 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1081 sm.getPDBResNum(index), atomNo));
1086 sl.highlightAtoms(atoms);
1090 * true if a mouse over event from an external (ie Vamsas) source is being
1093 boolean handlingVamsasMo = false;
1098 * as mouseOverSequence but only route event to SequenceListeners
1102 * in an alignment sequence
1104 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1105 VamsasSource source)
1107 handlingVamsasMo = true;
1108 long msg = sequenceI.hashCode() * (1 + position);
1112 mouseOverSequence(sequenceI, position, -1, source);
1114 handlingVamsasMo = false;
1117 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1121 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1122 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1124 * Annotation [] annotations = new Annotation[seq.getLength()];
1126 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1127 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1128 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1130 * for (int j = 0; j < mappings.length; j++) {
1132 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1133 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1134 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1135 * "+mappings[j].pdbfile);
1137 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1138 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1140 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1141 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1142 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1143 * mappings[j].pdbfile); }
1145 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1146 * annotations; } } } }
1148 * return annotations;
1152 public void structureSelectionChanged()
1156 public void sequenceSelectionChanged()
1160 public void sequenceColoursChanged(Object source)
1162 StructureListener sl;
1163 for (int i = 0; i < listeners.size(); i++)
1165 if (listeners.elementAt(i) instanceof StructureListener)
1167 sl = (StructureListener) listeners.elementAt(i);
1168 sl.updateColours(source);
1173 public StructureMapping[] getMapping(String pdbfile)
1175 List<StructureMapping> tmp = new ArrayList<>();
1176 for (StructureMapping sm : mappings)
1178 if (sm.pdbfile.equals(pdbfile))
1183 return tmp.toArray(new StructureMapping[tmp.size()]);
1187 * Returns a readable description of all mappings for the given pdbfile to any
1188 * of the given sequences
1194 public String printMappings(String pdbfile, List<SequenceI> seqs)
1196 if (pdbfile == null || seqs == null || seqs.isEmpty())
1201 StringBuilder sb = new StringBuilder(64);
1202 for (StructureMapping sm : mappings)
1204 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1205 && seqs.contains(sm.sequence))
1207 sb.append(sm.mappingDetails);
1209 // separator makes it easier to read multiple mappings
1210 sb.append("=====================");
1216 return sb.toString();
1220 * Remove the given mapping
1224 public void deregisterMapping(AlignedCodonFrame acf)
1228 boolean removed = seqmappings.remove(acf);
1229 if (removed && seqmappings.isEmpty())
1231 System.out.println("All mappings removed");
1237 * Add each of the given codonFrames to the stored set, if not aready present.
1241 public void registerMappings(List<AlignedCodonFrame> mappings)
1243 if (mappings != null)
1245 for (AlignedCodonFrame acf : mappings)
1247 registerMapping(acf);
1253 * Add the given mapping to the stored set, unless already stored.
1255 public void registerMapping(AlignedCodonFrame acf)
1259 if (!seqmappings.contains(acf))
1261 seqmappings.add(acf);
1267 * Reset this object to its initial state by removing all registered
1268 * listeners, codon mappings, PDB file mappings.
1270 * Called only by Desktop and testng.
1273 public void resetAll()
1275 if (mappings != null)
1279 if (seqmappings != null)
1281 seqmappings.clear();
1283 if (sel_listeners != null)
1285 sel_listeners.clear();
1287 if (listeners != null)
1291 if (commandListeners != null)
1293 commandListeners.clear();
1295 if (view_listeners != null)
1297 view_listeners.clear();
1299 if (pdbFileNameId != null)
1301 pdbFileNameId.clear();
1303 if (pdbIdFileName != null)
1305 pdbIdFileName.clear();
1309 public void addSelectionListener(SelectionListener selecter)
1311 if (!sel_listeners.contains(selecter))
1313 sel_listeners.add(selecter);
1317 public void removeSelectionListener(SelectionListener toremove)
1319 if (sel_listeners.contains(toremove))
1321 sel_listeners.remove(toremove);
1325 public synchronized void sendSelection(
1326 jalview.datamodel.SequenceGroup selection,
1327 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1328 SelectionSource source)
1330 for (SelectionListener slis : sel_listeners)
1334 slis.selection(selection, colsel, hidden, source);
1339 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1341 public synchronized void sendViewPosition(
1342 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1343 int startSeq, int endSeq)
1346 if (view_listeners != null && view_listeners.size() > 0)
1348 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1350 while (listeners.hasMoreElements())
1352 AlignmentViewPanelListener slis = listeners.nextElement();
1355 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1364 * Removes the instance associated with this provider
1369 public static void release(StructureSelectionManagerProvider provider)
1371 getInstance().selectionManagers.remove(provider);
1374 public void registerPDBEntry(PDBEntry pdbentry)
1376 if (pdbentry.getFile() != null
1377 && pdbentry.getFile().trim().length() > 0)
1379 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1383 public void addCommandListener(CommandListener cl)
1385 if (!commandListeners.contains(cl))
1387 commandListeners.add(cl);
1391 public boolean hasCommandListener(CommandListener cl)
1393 return this.commandListeners.contains(cl);
1396 public boolean removeCommandListener(CommandListener l)
1398 return commandListeners.remove(l);
1402 * Forward a command to any command listeners (except for the command's
1406 * the command to be broadcast (in its form after being performed)
1408 * if true, the command was being 'undone'
1411 public void commandPerformed(CommandI command, boolean undo,
1412 VamsasSource source)
1414 for (CommandListener listener : commandListeners)
1416 listener.mirrorCommand(command, undo, this, source);
1421 * Returns a new CommandI representing the given command as mapped to the
1422 * given sequences. If no mapping could be made, or the command is not of a
1423 * mappable kind, returns null.
1431 public CommandI mapCommand(CommandI command, boolean undo,
1432 final AlignmentI mapTo, char gapChar)
1434 if (command instanceof EditCommand)
1436 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1437 gapChar, seqmappings);
1439 else if (command instanceof OrderCommand)
1441 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1442 mapTo, seqmappings);
1447 public List<AlignedCodonFrame> getSequenceMappings()