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.commands.CommandI;
28 import jalview.commands.EditCommand;
29 import jalview.commands.OrderCommand;
30 import jalview.datamodel.AlignedCodonFrame;
31 import jalview.datamodel.AlignmentAnnotation;
32 import jalview.datamodel.AlignmentI;
33 import jalview.datamodel.Annotation;
34 import jalview.datamodel.HiddenColumns;
35 import jalview.datamodel.PDBEntry;
36 import jalview.datamodel.SearchResults;
37 import jalview.datamodel.SearchResultsI;
38 import jalview.datamodel.SequenceI;
39 import jalview.ext.jmol.JmolParser;
40 import jalview.gui.IProgressIndicator;
41 import jalview.io.AppletFormatAdapter;
42 import jalview.io.DataSourceType;
43 import jalview.io.StructureFile;
44 import jalview.util.MappingUtils;
45 import jalview.util.MessageManager;
46 import jalview.util.Platform;
47 import jalview.ws.sifts.SiftsClient;
48 import jalview.ws.sifts.SiftsException;
49 import jalview.ws.sifts.SiftsSettings;
51 import java.io.PrintStream;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collections;
55 import java.util.Enumeration;
56 import java.util.HashMap;
57 import java.util.IdentityHashMap;
58 import java.util.List;
60 import java.util.Vector;
63 import mc_view.PDBChain;
64 import mc_view.PDBfile;
66 public class StructureSelectionManager implements ApplicationSingletonI
68 public final static String NEWLINE = System.lineSeparator();
70 private List<StructureMapping> mappings = new ArrayList<>();
72 private boolean processSecondaryStructure = false;
74 private boolean secStructServices = false;
76 private boolean addTempFacAnnot = false;
79 * Set of any registered mappings between (dataset) sequences.
81 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
83 private List<CommandListener> commandListeners = new ArrayList<>();
85 private List<SelectionListener> sel_listeners = new ArrayList<>();
88 * instances of this class scoped by some context class
90 private IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> selectionManagers;
93 * Answers an instance of this class for the current application (Java or JS
98 private static StructureSelectionManager getInstance()
100 return (StructureSelectionManager) ApplicationSingletonProvider
101 .getInstance(StructureSelectionManager.class);
105 * Answers an instance of this class for the current application (Java or JS
106 * 'applet') scope, and scoped to the specified context
111 public static StructureSelectionManager getStructureSelectionManager(
112 StructureSelectionManagerProvider context)
114 return getInstance().getInstanceForContext(context);
118 * Answers an instance of this class scoped to the given context. The instance
119 * is created on the first request for the context, thereafter the same
120 * instance is returned. Note that the context may be null (this is the case
121 * when running headless without a Desktop).
126 StructureSelectionManager getInstanceForContext(
127 StructureSelectionManagerProvider context)
129 StructureSelectionManager instance = selectionManagers.get(context);
130 if (instance == null)
132 instance = new StructureSelectionManager();
133 selectionManagers.put(context, instance);
139 * Removes the instance associated with this provider
144 public static void release(StructureSelectionManagerProvider provider)
146 getInstance().selectionManagers.remove(provider);
150 * Private constructor as all 'singleton' instances are managed here or by
151 * ApplicationSingletonProvider
153 private StructureSelectionManager()
155 selectionManagers = new IdentityHashMap<>();
159 * @return true if will try to use external services for processing secondary
162 public boolean isSecStructServices()
164 return secStructServices;
168 * control use of external services for processing secondary structure
170 * @param secStructServices
172 public void setSecStructServices(boolean secStructServices)
174 this.secStructServices = secStructServices;
178 * flag controlling addition of any kind of structural annotation
180 * @return true if temperature factor annotation will be added
182 public boolean isAddTempFacAnnot()
184 return addTempFacAnnot;
188 * set flag controlling addition of structural annotation
190 * @param addTempFacAnnot
192 public void setAddTempFacAnnot(boolean addTempFacAnnot)
194 this.addTempFacAnnot = addTempFacAnnot;
199 * @return if true, the structure manager will attempt to add secondary
200 * structure lines for unannotated sequences
203 public boolean isProcessSecondaryStructure()
205 return processSecondaryStructure;
209 * Control whether structure manager will try to annotate mapped sequences
210 * with secondary structure from PDB data.
214 public void setProcessSecondaryStructure(boolean enable)
216 processSecondaryStructure = enable;
220 * debug function - write all mappings to stdout
222 public void reportMapping()
224 if (mappings.isEmpty())
226 System.err.println("reportMapping: No PDB/Sequence mappings.");
231 "reportMapping: There are " + mappings.size() + " mappings.");
233 for (StructureMapping sm : mappings)
235 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
241 * map between the PDB IDs (or structure identifiers) used by Jalview and the
242 * absolute filenames for PDB data that corresponds to it
244 Map<String, String> pdbIdFileName = new HashMap<>();
246 Map<String, String> pdbFileNameId = new HashMap<>();
248 public void registerPDBFile(String idForFile, String absoluteFile)
250 pdbIdFileName.put(idForFile, absoluteFile);
251 pdbFileNameId.put(absoluteFile, idForFile);
254 public String findIdForPDBFile(String idOrFile)
256 String id = pdbFileNameId.get(idOrFile);
260 public String findFileForPDBId(String idOrFile)
262 String id = pdbIdFileName.get(idOrFile);
266 public boolean isPDBFileRegistered(String idOrFile)
268 return pdbFileNameId.containsKey(idOrFile)
269 || pdbIdFileName.containsKey(idOrFile);
273 * flag controlling whether SeqMappings are relayed from received sequence
274 * mouse over events to other sequences
276 boolean relaySeqMappings = true;
279 * Enable or disable relay of seqMapping events to other sequences. You might
280 * want to do this if there are many sequence mappings and the host computer
285 public void setRelaySeqMappings(boolean relay)
287 relaySeqMappings = relay;
291 * get the state of the relay seqMappings flag.
293 * @return true if sequence mouse overs are being relayed to other mapped
296 public boolean isRelaySeqMappingsEnabled()
298 return relaySeqMappings;
301 Vector<Object> listeners = new Vector<>();
304 * register a listener for alignment sequence mouseover events
308 public void addStructureViewerListener(Object svl)
310 if (!listeners.contains(svl))
312 listeners.addElement(svl);
317 * Returns the filename the PDB id is already mapped to if known, or null if
323 public String alreadyMappedToFile(String pdbid)
325 for (StructureMapping sm : mappings)
327 if (sm.getPdbId().equalsIgnoreCase(pdbid))
336 * Import structure data and register a structure mapping for broadcasting
337 * colouring, mouseovers and selection events (convenience wrapper).
339 * This is the standard entry point.
342 * - one or more sequences to be mapped to pdbFile
343 * @param targetChains
344 * - optional chain specification for mapping each sequence to pdb
345 * (may be nill, individual elements may be nill)
347 * - structure data resource
349 * - how to resolve data from resource
350 * @return null or the structure data parsed as a pdb file
352 synchronized public StructureFile setMapping(SequenceI[] sequence,
353 String[] targetChains, String pdbFile, DataSourceType protocol,
354 IProgressIndicator progress)
356 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
361 * Import a single structure file and register sequence structure mappings for
362 * broadcasting colouring, mouseovers and selection events (convenience
367 * @param forStructureView
368 * when true (testng only), record the mapping for use in mouseOvers
371 * - one or more sequences to be mapped to pdbFile
372 * @param targetChains
373 * - optional chain specification for mapping each sequence to pdb
374 * (may be nill, individual elements may be nill)
376 * - structure data resource
378 * - how to resolve data from resource
379 * @return null or the structure data parsed as a pdb file
381 synchronized public StructureFile setMapping(boolean forStructureView,
382 SequenceI[] sequenceArray, String[] targetChainIds,
383 String pdbFile, DataSourceType sourceType)
385 return computeMapping(forStructureView, sequenceArray, targetChainIds,
386 pdbFile, sourceType, null);
390 * create sequence structure mappings between each sequence and the given
391 * pdbFile (retrieved via the given protocol). Either constructs a mapping
392 * using NW alignment or derives one from any available SIFTS mapping data.
394 * @param forStructureView
395 * when true, record the mapping for use in mouseOvers
397 * @param sequenceArray
398 * - one or more sequences to be mapped to pdbFile
399 * @param targetChainIds
400 * - optional chain specification for mapping each sequence to pdb
401 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
402 * - this should be List<List<String>>, empty lists indicate no
403 * predefined mappings
405 * - structure data resource
407 * - how to resolve data from resource
408 * @param IProgressIndicator
409 * reference to UI component that maintains a progress bar for the
411 * @return null or the structure data parsed as a pdb file
413 synchronized private StructureFile computeMapping(
414 boolean forStructureView, SequenceI[] sequenceArray,
415 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
416 IProgressIndicator progress)
418 long progressSessionId = System.currentTimeMillis() * 3;
421 * do we extract and transfer annotation from 3D data ?
423 // FIXME: possibly should just delete
425 boolean parseSecStr = processSecondaryStructure
426 ? isStructureFileProcessed(pdbFile, sequenceArray)
429 StructureFile pdb = null;
430 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
433 // FIXME if sourceType is not null, we've lost data here
434 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
435 pdb = new JmolParser(false, pdbFile, sourceType);
436 pdb.addSettings(parseSecStr && processSecondaryStructure,
437 parseSecStr && addTempFacAnnot,
438 parseSecStr && secStructServices);
440 if (pdb.getId() != null && pdb.getId().trim().length() > 0
441 && DataSourceType.FILE == sourceType)
443 registerPDBFile(pdb.getId().trim(), pdbFile);
445 // if PDBId is unavailable then skip SIFTS mapping execution path
446 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
448 } catch (Exception ex)
450 ex.printStackTrace();
454 * sifts client - non null if SIFTS mappings are to be used
456 SiftsClient siftsClient = null;
461 siftsClient = new SiftsClient(pdb);
463 } catch (SiftsException e)
465 isMapUsingSIFTs = false;
470 String targetChainId;
471 for (int s = 0; s < sequenceArray.length; s++)
473 boolean infChain = true;
474 final SequenceI seq = sequenceArray[s];
476 while (ds.getDatasetSequence() != null)
478 ds = ds.getDatasetSequence();
481 if (targetChainIds != null && targetChainIds[s] != null)
484 targetChainId = targetChainIds[s];
486 else if (seq.getName().indexOf("|") > -1)
488 targetChainId = seq.getName()
489 .substring(seq.getName().lastIndexOf("|") + 1);
490 if (targetChainId.length() > 1)
492 if (targetChainId.trim().length() == 0)
498 // not a valid chain identifier
509 * Attempt pairwise alignment of the sequence with each chain in the PDB,
510 * and remember the highest scoring chain
513 AlignSeq maxAlignseq = null;
514 String maxChainId = " ";
515 PDBChain maxChain = null;
516 boolean first = true;
517 for (PDBChain chain : pdb.getChains())
519 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
522 continue; // don't try to map chains don't match.
524 // TODO: correctly determine sequence type for mixed na/peptide
526 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
527 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
530 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
531 // as.calcScoreMatrix();
532 // as.traceAlignment();
534 if (first || as.maxscore > max
535 || (as.maxscore == max && chain.id.equals(targetChainId)))
541 maxChainId = chain.id;
544 if (maxChain == null)
549 if (sourceType == DataSourceType.PASTE)
551 pdbFile = "INLINE" + pdb.getId();
554 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
555 if (isMapUsingSIFTs && seq.isProtein())
557 if (progress!=null) {
558 progress.setProgressBar(MessageManager
559 .getString("status.obtaining_mapping_with_sifts"),
562 jalview.datamodel.Mapping sqmpping = maxAlignseq
563 .getMappingFromS1(false);
564 if (targetChainId != null && !targetChainId.trim().isEmpty())
566 StructureMapping siftsMapping;
569 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
570 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
571 seqToStrucMapping.add(siftsMapping);
572 maxChain.makeExactMapping(siftsMapping, seq);
573 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
575 maxChain.transferResidueAnnotation(siftsMapping, null);
576 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
578 } catch (SiftsException e)
580 // fall back to NW alignment
581 System.err.println(e.getMessage());
582 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
583 targetChainId, maxChain, pdb, maxAlignseq);
584 seqToStrucMapping.add(nwMapping);
585 maxChain.makeExactMapping(maxAlignseq, seq);
586 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
589 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
590 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
595 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
596 for (PDBChain chain : pdb.getChains())
598 StructureMapping siftsMapping = null;
601 siftsMapping = getStructureMapping(seq,
602 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
604 foundSiftsMappings.add(siftsMapping);
605 chain.makeExactMapping(siftsMapping, seq);
606 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
608 chain.transferResidueAnnotation(siftsMapping, null);
609 } catch (SiftsException e)
611 System.err.println(e.getMessage());
617 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
618 System.err.println(e.getMessage());
621 if (!foundSiftsMappings.isEmpty())
623 seqToStrucMapping.addAll(foundSiftsMappings);
624 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
628 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
629 maxChainId, maxChain, pdb, maxAlignseq);
630 seqToStrucMapping.add(nwMapping);
631 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
633 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
634 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
640 if (progress != null)
642 progress.setProgressBar(MessageManager
643 .getString("status.obtaining_mapping_with_nw_alignment"),
646 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
647 maxChain, pdb, maxAlignseq);
648 seqToStrucMapping.add(nwMapping);
649 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
651 if (forStructureView)
653 for (StructureMapping sm : seqToStrucMapping)
655 addStructureMapping(sm); // not addAll!
658 if (progress != null)
660 progress.setProgressBar(null, progressSessionId);
667 * check if we need to extract secondary structure from given pdbFile and
668 * transfer to sequences
671 * @param sequenceArray
674 private boolean isStructureFileProcessed(String pdbFile,
675 SequenceI[] sequenceArray)
677 boolean parseSecStr = true;
678 if (isPDBFileRegistered(pdbFile))
680 for (SequenceI sq : sequenceArray)
683 while (ds.getDatasetSequence() != null)
685 ds = ds.getDatasetSequence();
687 if (ds.getAnnotation() != null)
689 for (AlignmentAnnotation ala : ds.getAnnotation())
691 // false if any annotation present from this structure
692 // JBPNote this fails for jmol/chimera view because the *file* is
693 // passed, not the structure data ID -
694 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
705 public void addStructureMapping(StructureMapping sm)
707 if (!mappings.contains(sm))
714 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
719 * @param targetChainId
725 * client for retrieval of SIFTS mappings for this structure
727 * @throws SiftsException
729 private StructureMapping getStructureMapping(SequenceI seq,
730 String pdbFile, String targetChainId, StructureFile pdb,
731 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
732 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
734 StructureMapping curChainMapping = siftsClient
735 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
738 PDBChain chain = pdb.findChain(targetChainId);
741 chain.transferResidueAnnotation(curChainMapping, null);
743 } catch (Exception e)
747 return curChainMapping;
750 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
751 String maxChainId, PDBChain maxChain, StructureFile pdb,
752 AlignSeq maxAlignseq)
754 final StringBuilder mappingDetails = new StringBuilder(128);
755 mappingDetails.append(NEWLINE)
756 .append("Sequence \u27f7 Structure mapping details");
757 mappingDetails.append(NEWLINE);
759 .append("Method: inferred with Needleman & Wunsch alignment");
760 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
761 .append(NEWLINE).append("Sequence = ")
762 .append(maxChain.sequence.getSequenceAsString());
763 mappingDetails.append(NEWLINE).append("No of residues = ")
764 .append(maxChain.residues.size()).append(NEWLINE)
766 PrintStream ps = new PrintStream(System.out)
769 public void print(String x)
771 mappingDetails.append(x);
775 public void println()
777 mappingDetails.append(NEWLINE);
781 maxAlignseq.printAlignment(ps);
783 mappingDetails.append(NEWLINE).append("PDB start/end ");
784 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
786 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
787 mappingDetails.append(NEWLINE).append("SEQ start/end ");
790 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
792 mappingDetails.append(
793 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
794 mappingDetails.append(NEWLINE);
795 maxChain.makeExactMapping(maxAlignseq, seq);
796 jalview.datamodel.Mapping sqmpping = maxAlignseq
797 .getMappingFromS1(false);
798 maxChain.transferRESNUMFeatures(seq, null);
800 HashMap<Integer, int[]> mapping = new HashMap<>();
807 Atom tmp = maxChain.atoms.elementAt(index);
808 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
809 && tmp.alignmentMapping != -1)
811 resNum = tmp.resNumber;
812 insCode = tmp.insCode;
813 if (tmp.alignmentMapping >= -1)
815 mapping.put(tmp.alignmentMapping + 1,
817 { tmp.resNumber, tmp.atomIndex });
822 } while (index < maxChain.atoms.size());
824 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
825 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
826 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
830 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
832 listeners.removeElement(svl);
833 if (svl instanceof SequenceListener)
835 for (int i = 0; i < listeners.size(); i++)
837 if (listeners.elementAt(i) instanceof StructureListener)
839 ((StructureListener) listeners.elementAt(i))
840 .releaseReferences(svl);
845 if (pdbfiles == null)
851 * Remove mappings to the closed listener's PDB files, but first check if
852 * another listener is still interested
854 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
856 StructureListener sl;
857 for (int i = 0; i < listeners.size(); i++)
859 if (listeners.elementAt(i) instanceof StructureListener)
861 sl = (StructureListener) listeners.elementAt(i);
862 for (String pdbfile : sl.getStructureFiles())
864 pdbs.remove(pdbfile);
870 * Rebuild the mappings set, retaining only those which are for 'other' PDB
875 List<StructureMapping> tmp = new ArrayList<>();
876 for (StructureMapping sm : mappings)
878 if (!pdbs.contains(sm.pdbfile))
889 * Propagate mouseover of a single position in a structure
895 public void mouseOverStructure(int pdbResNum, String chain,
898 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
899 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
900 mouseOverStructure(atoms);
904 * Propagate mouseover or selection of multiple positions in a structure
908 public void mouseOverStructure(List<AtomSpec> atoms)
910 if (listeners == null)
912 // old or prematurely sent event
915 boolean hasSequenceListener = false;
916 for (int i = 0; i < listeners.size(); i++)
918 if (listeners.elementAt(i) instanceof SequenceListener)
920 hasSequenceListener = true;
923 if (!hasSequenceListener)
928 SearchResultsI results = findAlignmentPositionsForStructurePositions(
930 for (Object li : listeners)
932 if (li instanceof SequenceListener)
934 ((SequenceListener) li).highlightSequence(results);
940 * Constructs a SearchResults object holding regions (if any) in the Jalview
941 * alignment which have a mapping to the structure viewer positions in the
947 public SearchResultsI findAlignmentPositionsForStructurePositions(
948 List<AtomSpec> atoms)
950 SearchResultsI results = new SearchResults();
951 for (AtomSpec atom : atoms)
953 SequenceI lastseq = null;
955 for (StructureMapping sm : mappings)
957 if (sm.pdbfile.equals(atom.getPdbFile())
958 && sm.pdbchain.equals(atom.getChain()))
960 int indexpos = sm.getSeqPos(atom.getPdbResNum());
961 if (lastipos != indexpos || lastseq != sm.sequence)
963 results.addResult(sm.sequence, indexpos, indexpos);
965 lastseq = sm.sequence;
966 // construct highlighted sequence list
967 for (AlignedCodonFrame acf : seqmappings)
969 acf.markMappedRegion(sm.sequence, indexpos, results);
979 * highlight regions associated with a position (indexpos) in seq
982 * the sequence that the mouse over occurred on
984 * the absolute position being mouseovered in seq (0 to seq.length())
986 * the sequence position (if -1, seq.findPosition is called to
987 * resolve the residue number)
989 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
992 boolean hasSequenceListeners = handlingVamsasMo
993 || !seqmappings.isEmpty();
994 SearchResultsI results = null;
997 seqPos = seq.findPosition(indexpos);
999 for (int i = 0; i < listeners.size(); i++)
1001 Object listener = listeners.elementAt(i);
1002 if (listener == source)
1004 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1005 // Temporary fudge with SequenceListener.getVamsasSource()
1008 if (listener instanceof StructureListener)
1010 highlightStructure((StructureListener) listener, seq, seqPos);
1014 if (listener instanceof SequenceListener)
1016 final SequenceListener seqListener = (SequenceListener) listener;
1017 if (hasSequenceListeners
1018 && seqListener.getVamsasSource() != source)
1020 if (relaySeqMappings)
1022 if (results == null)
1024 results = MappingUtils.buildSearchResults(seq, seqPos,
1027 if (handlingVamsasMo)
1029 results.addResult(seq, seqPos, seqPos);
1032 if (!results.isEmpty())
1034 seqListener.highlightSequence(results);
1039 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1041 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1044 else if (listener instanceof SecondaryStructureListener)
1046 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1054 * Send suitable messages to a StructureListener to highlight atoms
1055 * corresponding to the given sequence position(s)
1061 public void highlightStructure(StructureListener sl, SequenceI seq,
1064 if (!sl.isListeningFor(seq))
1069 List<AtomSpec> atoms = new ArrayList<>();
1070 for (StructureMapping sm : mappings)
1072 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1073 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1074 .getDatasetSequence() == seq.getDatasetSequence()))
1076 for (int index : positions)
1078 atomNo = sm.getAtomNum(index);
1082 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1083 sm.getPDBResNum(index), atomNo));
1088 sl.highlightAtoms(atoms);
1092 * true if a mouse over event from an external (ie Vamsas) source is being
1095 boolean handlingVamsasMo = false;
1100 * as mouseOverSequence but only route event to SequenceListeners
1104 * in an alignment sequence
1106 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1107 VamsasSource source)
1109 handlingVamsasMo = true;
1110 long msg = sequenceI.hashCode() * (1 + position);
1114 mouseOverSequence(sequenceI, position, -1, source);
1116 handlingVamsasMo = false;
1119 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1123 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1124 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1126 * Annotation [] annotations = new Annotation[seq.getLength()];
1128 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1129 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1130 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1132 * for (int j = 0; j < mappings.length; j++) {
1134 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1135 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1136 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1137 * "+mappings[j].pdbfile);
1139 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1140 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1142 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1143 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1144 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1145 * mappings[j].pdbfile); }
1147 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1148 * annotations; } } } }
1150 * return annotations;
1154 public void structureSelectionChanged()
1158 public void sequenceSelectionChanged()
1162 public void sequenceColoursChanged(Object source)
1164 StructureListener sl;
1165 for (int i = 0; i < listeners.size(); i++)
1167 if (listeners.elementAt(i) instanceof StructureListener)
1169 sl = (StructureListener) listeners.elementAt(i);
1170 sl.updateColours(source);
1175 public StructureMapping[] getMapping(String pdbfile)
1177 List<StructureMapping> tmp = new ArrayList<>();
1178 for (StructureMapping sm : mappings)
1180 if (sm.pdbfile.equals(pdbfile))
1185 return tmp.toArray(new StructureMapping[tmp.size()]);
1189 * Returns a readable description of all mappings for the given pdbfile to any
1190 * of the given sequences
1196 public String printMappings(String pdbfile, List<SequenceI> seqs)
1198 if (pdbfile == null || seqs == null || seqs.isEmpty())
1203 StringBuilder sb = new StringBuilder(64);
1204 for (StructureMapping sm : mappings)
1206 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1207 && seqs.contains(sm.sequence))
1209 sb.append(sm.mappingDetails);
1211 // separator makes it easier to read multiple mappings
1212 sb.append("=====================");
1218 return sb.toString();
1222 * Remove the given mapping
1226 public void deregisterMapping(AlignedCodonFrame acf)
1230 boolean removed = seqmappings.remove(acf);
1231 if (removed && seqmappings.isEmpty())
1233 System.out.println("All mappings removed");
1239 * Add each of the given codonFrames to the stored set, if not aready present.
1243 public void registerMappings(List<AlignedCodonFrame> mappings)
1245 if (mappings != null)
1247 for (AlignedCodonFrame acf : mappings)
1249 registerMapping(acf);
1255 * Add the given mapping to the stored set, unless already stored.
1257 public void registerMapping(AlignedCodonFrame acf)
1261 if (!seqmappings.contains(acf))
1263 seqmappings.add(acf);
1269 * Reset this object to its initial state by removing all registered
1270 * listeners, codon mappings, PDB file mappings.
1272 * Called only by Desktop and testng.
1275 public void resetAll()
1278 seqmappings.clear();
1279 sel_listeners.clear();
1281 commandListeners.clear();
1282 view_listeners.clear();
1283 pdbFileNameId.clear();
1284 pdbIdFileName.clear();
1287 public void addSelectionListener(SelectionListener selecter)
1289 if (!sel_listeners.contains(selecter))
1291 sel_listeners.add(selecter);
1295 public void removeSelectionListener(SelectionListener toremove)
1297 if (sel_listeners.contains(toremove))
1299 sel_listeners.remove(toremove);
1303 public synchronized void sendSelection(
1304 jalview.datamodel.SequenceGroup selection,
1305 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1306 SelectionSource source)
1308 for (SelectionListener slis : sel_listeners)
1312 slis.selection(selection, colsel, hidden, source);
1317 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1319 public synchronized void sendViewPosition(
1320 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1321 int startSeq, int endSeq)
1324 if (view_listeners != null && view_listeners.size() > 0)
1326 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1328 while (listeners.hasMoreElements())
1330 AlignmentViewPanelListener slis = listeners.nextElement();
1333 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1339 public void registerPDBEntry(PDBEntry pdbentry)
1341 if (pdbentry.getFile() != null
1342 && pdbentry.getFile().trim().length() > 0)
1344 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1348 public void addCommandListener(CommandListener cl)
1350 if (!commandListeners.contains(cl))
1352 commandListeners.add(cl);
1356 public boolean hasCommandListener(CommandListener cl)
1358 return this.commandListeners.contains(cl);
1361 public boolean removeCommandListener(CommandListener l)
1363 return commandListeners.remove(l);
1367 * Forward a command to any command listeners (except for the command's
1371 * the command to be broadcast (in its form after being performed)
1373 * if true, the command was being 'undone'
1376 public void commandPerformed(CommandI command, boolean undo,
1377 VamsasSource source)
1379 for (CommandListener listener : commandListeners)
1381 listener.mirrorCommand(command, undo, this, source);
1386 * Returns a new CommandI representing the given command as mapped to the
1387 * given sequences. If no mapping could be made, or the command is not of a
1388 * mappable kind, returns null.
1396 public CommandI mapCommand(CommandI command, boolean undo,
1397 final AlignmentI mapTo, char gapChar)
1399 if (command instanceof EditCommand)
1401 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1402 gapChar, seqmappings);
1404 else if (command instanceof OrderCommand)
1406 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1407 mapTo, seqmappings);
1412 public List<AlignedCodonFrame> getSequenceMappings()