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 * Private constructor as all 'singleton' instances are managed here or by
107 * ApplicationSingletonProvider
109 private StructureSelectionManager()
111 selectionManagers = new IdentityHashMap<>();
115 * Answers an instance of this class for the current application (Java or JS
116 * 'applet') scope, and scoped to the specified context
121 public static StructureSelectionManager getStructureSelectionManager(
122 StructureSelectionManagerProvider context)
124 return getInstance().getInstanceForContext(context);
128 * Answers an instance of this class scoped to the given context. The instance
129 * is created on the first request for the context, thereafter the same
130 * instance is returned. Note that the context may be null (this is the case
131 * when running headless without a Desktop).
136 StructureSelectionManager getInstanceForContext(
137 StructureSelectionManagerProvider context)
139 StructureSelectionManager instance = selectionManagers.get(context);
140 if (instance == null)
142 instance = new StructureSelectionManager();
143 selectionManagers.put(context, instance);
150 * @return true if will try to use external services for processing secondary
153 public boolean isSecStructServices()
155 return secStructServices;
159 * control use of external services for processing secondary structure
161 * @param secStructServices
163 public void setSecStructServices(boolean secStructServices)
165 this.secStructServices = secStructServices;
169 * flag controlling addition of any kind of structural annotation
171 * @return true if temperature factor annotation will be added
173 public boolean isAddTempFacAnnot()
175 return addTempFacAnnot;
179 * set flag controlling addition of structural annotation
181 * @param addTempFacAnnot
183 public void setAddTempFacAnnot(boolean addTempFacAnnot)
185 this.addTempFacAnnot = addTempFacAnnot;
190 * @return if true, the structure manager will attempt to add secondary
191 * structure lines for unannotated sequences
194 public boolean isProcessSecondaryStructure()
196 return processSecondaryStructure;
200 * Control whether structure manager will try to annotate mapped sequences
201 * with secondary structure from PDB data.
205 public void setProcessSecondaryStructure(boolean enable)
207 processSecondaryStructure = enable;
211 * debug function - write all mappings to stdout
213 public void reportMapping()
215 if (mappings.isEmpty())
217 System.err.println("reportMapping: No PDB/Sequence mappings.");
222 "reportMapping: There are " + mappings.size() + " mappings.");
224 for (StructureMapping sm : mappings)
226 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
232 * map between the PDB IDs (or structure identifiers) used by Jalview and the
233 * absolute filenames for PDB data that corresponds to it
235 Map<String, String> pdbIdFileName = new HashMap<>();
237 Map<String, String> pdbFileNameId = new HashMap<>();
239 public void registerPDBFile(String idForFile, String absoluteFile)
241 pdbIdFileName.put(idForFile, absoluteFile);
242 pdbFileNameId.put(absoluteFile, idForFile);
245 public String findIdForPDBFile(String idOrFile)
247 String id = pdbFileNameId.get(idOrFile);
251 public String findFileForPDBId(String idOrFile)
253 String id = pdbIdFileName.get(idOrFile);
257 public boolean isPDBFileRegistered(String idOrFile)
259 return pdbFileNameId.containsKey(idOrFile)
260 || pdbIdFileName.containsKey(idOrFile);
264 * flag controlling whether SeqMappings are relayed from received sequence
265 * mouse over events to other sequences
267 boolean relaySeqMappings = true;
270 * Enable or disable relay of seqMapping events to other sequences. You might
271 * want to do this if there are many sequence mappings and the host computer
276 public void setRelaySeqMappings(boolean relay)
278 relaySeqMappings = relay;
282 * get the state of the relay seqMappings flag.
284 * @return true if sequence mouse overs are being relayed to other mapped
287 public boolean isRelaySeqMappingsEnabled()
289 return relaySeqMappings;
292 Vector<Object> listeners = new Vector<>();
295 * register a listener for alignment sequence mouseover events
299 public void addStructureViewerListener(Object svl)
301 if (!listeners.contains(svl))
303 listeners.addElement(svl);
308 * Returns the filename the PDB id is already mapped to if known, or null if
314 public String alreadyMappedToFile(String pdbid)
316 for (StructureMapping sm : mappings)
318 if (sm.getPdbId().equalsIgnoreCase(pdbid))
327 * Import structure data and register a structure mapping for broadcasting
328 * colouring, mouseovers and selection events (convenience wrapper).
330 * This is the standard entry point.
333 * - one or more sequences to be mapped to pdbFile
334 * @param targetChains
335 * - optional chain specification for mapping each sequence to pdb
336 * (may be nill, individual elements may be nill)
338 * - structure data resource
340 * - how to resolve data from resource
341 * @return null or the structure data parsed as a pdb file
343 synchronized public StructureFile setMapping(SequenceI[] sequence,
344 String[] targetChains, String pdbFile, DataSourceType protocol,
345 IProgressIndicator progress)
347 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
352 * Import a single structure file and register sequence structure mappings for
353 * broadcasting colouring, mouseovers and selection events (convenience
358 * @param forStructureView
359 * when true (testng only), record the mapping for use in mouseOvers
362 * - one or more sequences to be mapped to pdbFile
363 * @param targetChains
364 * - optional chain specification for mapping each sequence to pdb
365 * (may be nill, individual elements may be nill)
367 * - structure data resource
369 * - how to resolve data from resource
370 * @return null or the structure data parsed as a pdb file
372 synchronized public StructureFile setMapping(boolean forStructureView,
373 SequenceI[] sequenceArray, String[] targetChainIds,
374 String pdbFile, DataSourceType sourceType)
376 return computeMapping(forStructureView, sequenceArray, targetChainIds,
377 pdbFile, sourceType, null);
381 * create sequence structure mappings between each sequence and the given
382 * pdbFile (retrieved via the given protocol). Either constructs a mapping
383 * using NW alignment or derives one from any available SIFTS mapping data.
385 * @param forStructureView
386 * when true, record the mapping for use in mouseOvers
388 * @param sequenceArray
389 * - one or more sequences to be mapped to pdbFile
390 * @param targetChainIds
391 * - optional chain specification for mapping each sequence to pdb
392 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
393 * - this should be List<List<String>>, empty lists indicate no
394 * predefined mappings
396 * - structure data resource
398 * - how to resolve data from resource
399 * @param IProgressIndicator
400 * reference to UI component that maintains a progress bar for the
402 * @return null or the structure data parsed as a pdb file
404 synchronized private StructureFile computeMapping(
405 boolean forStructureView, SequenceI[] sequenceArray,
406 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
407 IProgressIndicator progress)
409 long progressSessionId = System.currentTimeMillis() * 3;
412 * do we extract and transfer annotation from 3D data ?
414 // FIXME: possibly should just delete
416 boolean parseSecStr = processSecondaryStructure
417 ? isStructureFileProcessed(pdbFile, sequenceArray)
420 StructureFile pdb = null;
421 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
424 // FIXME if sourceType is not null, we've lost data here
425 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
426 pdb = new JmolParser(false, pdbFile, sourceType);
427 pdb.addSettings(parseSecStr && processSecondaryStructure,
428 parseSecStr && addTempFacAnnot,
429 parseSecStr && secStructServices);
431 if (pdb.getId() != null && pdb.getId().trim().length() > 0
432 && DataSourceType.FILE == sourceType)
434 registerPDBFile(pdb.getId().trim(), pdbFile);
436 // if PDBId is unavailable then skip SIFTS mapping execution path
437 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
439 } catch (Exception ex)
441 ex.printStackTrace();
445 * sifts client - non null if SIFTS mappings are to be used
447 SiftsClient siftsClient = null;
452 siftsClient = new SiftsClient(pdb);
454 } catch (SiftsException e)
456 isMapUsingSIFTs = false;
457 Cache.log.error("SIFTS mapping failed", e);
458 Cache.log.error("Falling back on Needleman & Wunsch alignment");
462 String targetChainId;
463 for (int s = 0; s < sequenceArray.length; s++)
465 boolean infChain = true;
466 final SequenceI seq = sequenceArray[s];
468 while (ds.getDatasetSequence() != null)
470 ds = ds.getDatasetSequence();
473 if (targetChainIds != null && targetChainIds[s] != null)
476 targetChainId = targetChainIds[s];
478 else if (seq.getName().indexOf("|") > -1)
480 targetChainId = seq.getName()
481 .substring(seq.getName().lastIndexOf("|") + 1);
482 if (targetChainId.length() > 1)
484 if (targetChainId.trim().length() == 0)
490 // not a valid chain identifier
501 * Attempt pairwise alignment of the sequence with each chain in the PDB,
502 * and remember the highest scoring chain
505 AlignSeq maxAlignseq = null;
506 String maxChainId = " ";
507 PDBChain maxChain = null;
508 boolean first = true;
509 for (PDBChain chain : pdb.getChains())
511 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
514 continue; // don't try to map chains don't match.
516 // TODO: correctly determine sequence type for mixed na/peptide
518 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
519 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
522 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
523 // as.calcScoreMatrix();
524 // as.traceAlignment();
526 if (first || as.maxscore > max
527 || (as.maxscore == max && chain.id.equals(targetChainId)))
533 maxChainId = chain.id;
536 if (maxChain == null)
541 if (sourceType == DataSourceType.PASTE)
543 pdbFile = "INLINE" + pdb.getId();
546 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
547 if (isMapUsingSIFTs && seq.isProtein())
549 if (progress!=null) {
550 progress.setProgressBar(MessageManager
551 .getString("status.obtaining_mapping_with_sifts"),
554 jalview.datamodel.Mapping sqmpping = maxAlignseq
555 .getMappingFromS1(false);
556 if (targetChainId != null && !targetChainId.trim().isEmpty())
558 StructureMapping siftsMapping;
561 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
562 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
563 seqToStrucMapping.add(siftsMapping);
564 maxChain.makeExactMapping(siftsMapping, seq);
565 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
567 maxChain.transferResidueAnnotation(siftsMapping, null);
568 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
570 } catch (SiftsException e)
572 // fall back to NW alignment
573 System.err.println(e.getMessage());
574 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
575 targetChainId, maxChain, pdb, maxAlignseq);
576 seqToStrucMapping.add(nwMapping);
577 maxChain.makeExactMapping(maxAlignseq, seq);
578 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
581 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
582 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
587 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
588 for (PDBChain chain : pdb.getChains())
590 StructureMapping siftsMapping = null;
593 siftsMapping = getStructureMapping(seq,
594 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
596 foundSiftsMappings.add(siftsMapping);
597 chain.makeExactMapping(siftsMapping, seq);
598 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
600 chain.transferResidueAnnotation(siftsMapping, null);
601 } catch (SiftsException e)
603 System.err.println(e.getMessage());
609 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
610 System.err.println(e.getMessage());
613 if (!foundSiftsMappings.isEmpty())
615 seqToStrucMapping.addAll(foundSiftsMappings);
616 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
620 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
621 maxChainId, maxChain, pdb, maxAlignseq);
622 seqToStrucMapping.add(nwMapping);
623 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
625 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
626 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
632 if (progress != null)
634 progress.setProgressBar(MessageManager
635 .getString("status.obtaining_mapping_with_nw_alignment"),
638 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
639 maxChain, pdb, maxAlignseq);
640 seqToStrucMapping.add(nwMapping);
641 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
643 if (forStructureView)
645 for (StructureMapping sm : seqToStrucMapping)
647 addStructureMapping(sm); // not addAll!
650 if (progress != null)
652 progress.setProgressBar(null, progressSessionId);
659 * check if we need to extract secondary structure from given pdbFile and
660 * transfer to sequences
663 * @param sequenceArray
666 private boolean isStructureFileProcessed(String pdbFile,
667 SequenceI[] sequenceArray)
669 boolean parseSecStr = true;
670 if (isPDBFileRegistered(pdbFile))
672 for (SequenceI sq : sequenceArray)
675 while (ds.getDatasetSequence() != null)
677 ds = ds.getDatasetSequence();
679 if (ds.getAnnotation() != null)
681 for (AlignmentAnnotation ala : ds.getAnnotation())
683 // false if any annotation present from this structure
684 // JBPNote this fails for jmol/chimera view because the *file* is
685 // passed, not the structure data ID -
686 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
697 public void addStructureMapping(StructureMapping sm)
699 if (!mappings.contains(sm))
706 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
711 * @param targetChainId
717 * client for retrieval of SIFTS mappings for this structure
719 * @throws SiftsException
721 private StructureMapping getStructureMapping(SequenceI seq,
722 String pdbFile, String targetChainId, StructureFile pdb,
723 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
724 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
726 StructureMapping curChainMapping = siftsClient
727 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
730 PDBChain chain = pdb.findChain(targetChainId);
733 chain.transferResidueAnnotation(curChainMapping, null);
735 } catch (Exception e)
739 return curChainMapping;
742 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
743 String maxChainId, PDBChain maxChain, StructureFile pdb,
744 AlignSeq maxAlignseq)
746 final StringBuilder mappingDetails = new StringBuilder(128);
747 mappingDetails.append(NEWLINE)
748 .append("Sequence \u27f7 Structure mapping details");
749 mappingDetails.append(NEWLINE);
751 .append("Method: inferred with Needleman & Wunsch alignment");
752 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
753 .append(NEWLINE).append("Sequence = ")
754 .append(maxChain.sequence.getSequenceAsString());
755 mappingDetails.append(NEWLINE).append("No of residues = ")
756 .append(maxChain.residues.size()).append(NEWLINE)
758 PrintStream ps = new PrintStream(System.out)
761 public void print(String x)
763 mappingDetails.append(x);
767 public void println()
769 mappingDetails.append(NEWLINE);
773 maxAlignseq.printAlignment(ps);
775 mappingDetails.append(NEWLINE).append("PDB start/end ");
776 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
778 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
779 mappingDetails.append(NEWLINE).append("SEQ start/end ");
782 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
784 mappingDetails.append(
785 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
786 mappingDetails.append(NEWLINE);
787 maxChain.makeExactMapping(maxAlignseq, seq);
788 jalview.datamodel.Mapping sqmpping = maxAlignseq
789 .getMappingFromS1(false);
790 maxChain.transferRESNUMFeatures(seq, null);
792 HashMap<Integer, int[]> mapping = new HashMap<>();
799 Atom tmp = maxChain.atoms.elementAt(index);
800 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
801 && tmp.alignmentMapping != -1)
803 resNum = tmp.resNumber;
804 insCode = tmp.insCode;
805 if (tmp.alignmentMapping >= -1)
807 mapping.put(tmp.alignmentMapping + 1,
809 { tmp.resNumber, tmp.atomIndex });
814 } while (index < maxChain.atoms.size());
816 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
817 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
818 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
822 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
824 listeners.removeElement(svl);
825 if (svl instanceof SequenceListener)
827 for (int i = 0; i < listeners.size(); i++)
829 if (listeners.elementAt(i) instanceof StructureListener)
831 ((StructureListener) listeners.elementAt(i))
832 .releaseReferences(svl);
837 if (pdbfiles == null)
843 * Remove mappings to the closed listener's PDB files, but first check if
844 * another listener is still interested
846 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
848 StructureListener sl;
849 for (int i = 0; i < listeners.size(); i++)
851 if (listeners.elementAt(i) instanceof StructureListener)
853 sl = (StructureListener) listeners.elementAt(i);
854 for (String pdbfile : sl.getStructureFiles())
856 pdbs.remove(pdbfile);
862 * Rebuild the mappings set, retaining only those which are for 'other' PDB
867 List<StructureMapping> tmp = new ArrayList<>();
868 for (StructureMapping sm : mappings)
870 if (!pdbs.contains(sm.pdbfile))
881 * Propagate mouseover of a single position in a structure
888 public String mouseOverStructure(int pdbResNum, String chain,
891 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
892 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
893 return mouseOverStructure(atoms);
897 * Propagate mouseover or selection of multiple positions in a structure
901 public String mouseOverStructure(List<AtomSpec> atoms)
903 if (listeners == null)
905 // old or prematurely sent event
908 boolean hasSequenceListener = false;
909 for (int i = 0; i < listeners.size(); i++)
911 if (listeners.elementAt(i) instanceof SequenceListener)
913 hasSequenceListener = true;
916 if (!hasSequenceListener)
921 SearchResultsI results = findAlignmentPositionsForStructurePositions(
923 String result = null;
924 for (Object li : listeners)
926 if (li instanceof SequenceListener)
928 String s = ((SequenceListener) li).highlightSequence(results);
939 * Constructs a SearchResults object holding regions (if any) in the Jalview
940 * alignment which have a mapping to the structure viewer positions in the
946 public SearchResultsI findAlignmentPositionsForStructurePositions(
947 List<AtomSpec> atoms)
949 SearchResultsI results = new SearchResults();
950 for (AtomSpec atom : atoms)
952 SequenceI lastseq = null;
954 for (StructureMapping sm : mappings)
956 if (sm.pdbfile.equals(atom.getPdbFile())
957 && sm.pdbchain.equals(atom.getChain()))
959 int indexpos = sm.getSeqPos(atom.getPdbResNum());
960 if (lastipos != indexpos || lastseq != sm.sequence)
962 results.addResult(sm.sequence, indexpos, indexpos);
964 lastseq = sm.sequence;
965 // construct highlighted sequence list
966 for (AlignedCodonFrame acf : seqmappings)
968 acf.markMappedRegion(sm.sequence, indexpos, results);
978 * highlight regions associated with a position (indexpos) in seq
981 * the sequence that the mouse over occurred on
983 * the absolute position being mouseovered in seq (0 to seq.length())
985 * the sequence position (if -1, seq.findPosition is called to
986 * resolve the residue number)
988 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
991 boolean hasSequenceListeners = handlingVamsasMo
992 || !seqmappings.isEmpty();
993 SearchResultsI results = null;
996 seqPos = seq.findPosition(indexpos);
998 for (int i = 0; i < listeners.size(); i++)
1000 Object listener = listeners.elementAt(i);
1001 if (listener == source)
1003 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1004 // Temporary fudge with SequenceListener.getVamsasSource()
1007 if (listener instanceof StructureListener)
1009 highlightStructure((StructureListener) listener, seq, seqPos);
1013 if (listener instanceof SequenceListener)
1015 final SequenceListener seqListener = (SequenceListener) listener;
1016 if (hasSequenceListeners
1017 && seqListener.getVamsasSource() != source)
1019 if (relaySeqMappings)
1021 if (results == null)
1023 results = MappingUtils.buildSearchResults(seq, seqPos,
1026 if (handlingVamsasMo)
1028 results.addResult(seq, seqPos, seqPos);
1031 if (!results.isEmpty())
1033 seqListener.highlightSequence(results);
1038 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1040 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1043 else if (listener instanceof SecondaryStructureListener)
1045 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1053 * Send suitable messages to a StructureListener to highlight atoms
1054 * corresponding to the given sequence position(s)
1060 public void highlightStructure(StructureListener sl, SequenceI seq,
1063 if (!sl.isListeningFor(seq))
1068 List<AtomSpec> atoms = new ArrayList<>();
1069 for (StructureMapping sm : mappings)
1071 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1072 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1073 .getDatasetSequence() == seq.getDatasetSequence()))
1075 for (int index : positions)
1077 atomNo = sm.getAtomNum(index);
1081 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1082 sm.getPDBResNum(index), atomNo));
1087 sl.highlightAtoms(atoms);
1091 * true if a mouse over event from an external (ie Vamsas) source is being
1094 boolean handlingVamsasMo = false;
1099 * as mouseOverSequence but only route event to SequenceListeners
1103 * in an alignment sequence
1105 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1106 VamsasSource source)
1108 handlingVamsasMo = true;
1109 long msg = sequenceI.hashCode() * (1 + position);
1113 mouseOverSequence(sequenceI, position, -1, source);
1115 handlingVamsasMo = false;
1118 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1122 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1123 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1125 * Annotation [] annotations = new Annotation[seq.getLength()];
1127 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1128 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1129 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1131 * for (int j = 0; j < mappings.length; j++) {
1133 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1134 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1135 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1136 * "+mappings[j].pdbfile);
1138 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1139 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1141 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1142 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1143 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1144 * mappings[j].pdbfile); }
1146 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1147 * annotations; } } } }
1149 * return annotations;
1153 public void structureSelectionChanged()
1157 public void sequenceSelectionChanged()
1161 public void sequenceColoursChanged(Object source)
1163 StructureListener sl;
1164 for (int i = 0; i < listeners.size(); i++)
1166 if (listeners.elementAt(i) instanceof StructureListener)
1168 sl = (StructureListener) listeners.elementAt(i);
1169 sl.updateColours(source);
1174 public StructureMapping[] getMapping(String pdbfile)
1176 List<StructureMapping> tmp = new ArrayList<>();
1177 for (StructureMapping sm : mappings)
1179 if (sm.pdbfile.equals(pdbfile))
1184 return tmp.toArray(new StructureMapping[tmp.size()]);
1188 * Returns a readable description of all mappings for the given pdbfile to any
1189 * of the given sequences
1195 public String printMappings(String pdbfile, List<SequenceI> seqs)
1197 if (pdbfile == null || seqs == null || seqs.isEmpty())
1202 StringBuilder sb = new StringBuilder(64);
1203 for (StructureMapping sm : mappings)
1205 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1206 && seqs.contains(sm.sequence))
1208 sb.append(sm.mappingDetails);
1210 // separator makes it easier to read multiple mappings
1211 sb.append("=====================");
1217 return sb.toString();
1221 * Remove the given mapping
1225 public void deregisterMapping(AlignedCodonFrame acf)
1229 boolean removed = seqmappings.remove(acf);
1230 if (removed && seqmappings.isEmpty())
1232 System.out.println("All mappings removed");
1238 * Add each of the given codonFrames to the stored set, if not aready present.
1242 public void registerMappings(List<AlignedCodonFrame> mappings)
1244 if (mappings != null)
1246 for (AlignedCodonFrame acf : mappings)
1248 registerMapping(acf);
1254 * Add the given mapping to the stored set, unless already stored.
1256 public void registerMapping(AlignedCodonFrame acf)
1260 if (!seqmappings.contains(acf))
1262 seqmappings.add(acf);
1268 * Reset this object to its initial state by removing all registered
1269 * listeners, codon mappings, PDB file mappings.
1271 * Called only by Desktop and testng.
1274 public void resetAll()
1276 if (mappings != null)
1280 if (seqmappings != null)
1282 seqmappings.clear();
1284 if (sel_listeners != null)
1286 sel_listeners.clear();
1288 if (listeners != null)
1292 if (commandListeners != null)
1294 commandListeners.clear();
1296 if (view_listeners != null)
1298 view_listeners.clear();
1300 if (pdbFileNameId != null)
1302 pdbFileNameId.clear();
1304 if (pdbIdFileName != null)
1306 pdbIdFileName.clear();
1310 public List<SelectionListener> getListeners() {
1311 return sel_listeners;
1314 public void addSelectionListener(SelectionListener selecter)
1316 if (!sel_listeners.contains(selecter))
1318 sel_listeners.add(selecter);
1322 public void removeSelectionListener(SelectionListener toremove)
1324 if (sel_listeners.contains(toremove))
1326 sel_listeners.remove(toremove);
1330 public synchronized void sendSelection(
1331 jalview.datamodel.SequenceGroup selection,
1332 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1333 SelectionSource source)
1335 for (SelectionListener slis : sel_listeners)
1339 slis.selection(selection, colsel, hidden, source);
1344 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1346 public synchronized void sendViewPosition(
1347 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1348 int startSeq, int endSeq)
1351 if (view_listeners != null && view_listeners.size() > 0)
1353 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1355 while (listeners.hasMoreElements())
1357 AlignmentViewPanelListener slis = listeners.nextElement();
1360 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1369 * Removes the instance associated with this provider
1374 public static void release(StructureSelectionManagerProvider provider)
1376 getInstance().selectionManagers.remove(provider);
1379 public void registerPDBEntry(PDBEntry pdbentry)
1381 if (pdbentry.getFile() != null
1382 && pdbentry.getFile().trim().length() > 0)
1384 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1388 public void addCommandListener(CommandListener cl)
1390 if (!commandListeners.contains(cl))
1392 commandListeners.add(cl);
1396 public boolean hasCommandListener(CommandListener cl)
1398 return this.commandListeners.contains(cl);
1401 public boolean removeCommandListener(CommandListener l)
1403 return commandListeners.remove(l);
1407 * Forward a command to any command listeners (except for the command's
1411 * the command to be broadcast (in its form after being performed)
1413 * if true, the command was being 'undone'
1416 public void commandPerformed(CommandI command, boolean undo,
1417 VamsasSource source)
1419 for (CommandListener listener : commandListeners)
1421 listener.mirrorCommand(command, undo, this, source);
1426 * Returns a new CommandI representing the given command as mapped to the
1427 * given sequences. If no mapping could be made, or the command is not of a
1428 * mappable kind, returns null.
1436 public CommandI mapCommand(CommandI command, boolean undo,
1437 final AlignmentI mapTo, char gapChar)
1439 if (command instanceof EditCommand)
1441 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1442 gapChar, seqmappings);
1444 else if (command instanceof OrderCommand)
1446 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1447 mapTo, seqmappings);
1452 public List<AlignedCodonFrame> getSequenceMappings()