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.ws.sifts.SiftsClient;
47 import jalview.ws.sifts.SiftsException;
48 import jalview.ws.sifts.SiftsSettings;
50 import java.io.PrintStream;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Collections;
54 import java.util.Enumeration;
55 import java.util.HashMap;
56 import java.util.IdentityHashMap;
57 import java.util.List;
59 import java.util.Vector;
62 import mc_view.PDBChain;
63 import mc_view.PDBfile;
65 public class StructureSelectionManager implements ApplicationSingletonI
67 public final static String NEWLINE = System.lineSeparator();
69 private List<StructureMapping> mappings = new ArrayList<>();
71 private boolean processSecondaryStructure = false;
73 private boolean secStructServices = false;
75 private boolean addTempFacAnnot = false;
78 * Set of any registered mappings between (dataset) sequences.
80 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
82 private List<CommandListener> commandListeners = new ArrayList<>();
84 private List<SelectionListener> sel_listeners = new ArrayList<>();
87 * instances of this class scoped by some context class
89 private IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> selectionManagers;
92 * Answers an instance of this class for the current application (Java or JS
97 private static StructureSelectionManager getInstance()
99 return (StructureSelectionManager) ApplicationSingletonProvider
100 .getInstance(StructureSelectionManager.class);
104 * Answers an instance of this class for the current application (Java or JS
105 * 'applet') scope, and scoped to the specified context
110 public static StructureSelectionManager getStructureSelectionManager(
111 StructureSelectionManagerProvider context)
113 return getInstance().getInstanceForContext(context);
117 * Answers an instance of this class scoped to the given context. The instance
118 * is created on the first request for the context, thereafter the same
119 * instance is returned. Note that the context may be null (this is the case
120 * when running headless without a Desktop).
125 StructureSelectionManager getInstanceForContext(
126 StructureSelectionManagerProvider context)
128 StructureSelectionManager instance = selectionManagers.get(context);
129 if (instance == null)
131 instance = new StructureSelectionManager();
132 selectionManagers.put(context, instance);
138 * Removes the instance associated with this provider
143 public static void release(StructureSelectionManagerProvider provider)
145 getInstance().selectionManagers.remove(provider);
149 * Private constructor as all 'singleton' instances are managed here or by
150 * ApplicationSingletonProvider
152 private StructureSelectionManager()
154 selectionManagers = new IdentityHashMap<>();
158 * @return true if will try to use external services for processing secondary
161 public boolean isSecStructServices()
163 return secStructServices;
167 * control use of external services for processing secondary structure
169 * @param secStructServices
171 public void setSecStructServices(boolean secStructServices)
173 this.secStructServices = secStructServices;
177 * flag controlling addition of any kind of structural annotation
179 * @return true if temperature factor annotation will be added
181 public boolean isAddTempFacAnnot()
183 return addTempFacAnnot;
187 * set flag controlling addition of structural annotation
189 * @param addTempFacAnnot
191 public void setAddTempFacAnnot(boolean addTempFacAnnot)
193 this.addTempFacAnnot = addTempFacAnnot;
198 * @return if true, the structure manager will attempt to add secondary
199 * structure lines for unannotated sequences
202 public boolean isProcessSecondaryStructure()
204 return processSecondaryStructure;
208 * Control whether structure manager will try to annotate mapped sequences
209 * with secondary structure from PDB data.
213 public void setProcessSecondaryStructure(boolean enable)
215 processSecondaryStructure = enable;
219 * debug function - write all mappings to stdout
221 public void reportMapping()
223 if (mappings.isEmpty())
225 System.err.println("reportMapping: No PDB/Sequence mappings.");
230 "reportMapping: There are " + mappings.size() + " mappings.");
232 for (StructureMapping sm : mappings)
234 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
240 * map between the PDB IDs (or structure identifiers) used by Jalview and the
241 * absolute filenames for PDB data that corresponds to it
243 Map<String, String> pdbIdFileName = new HashMap<>();
245 Map<String, String> pdbFileNameId = new HashMap<>();
247 public void registerPDBFile(String idForFile, String absoluteFile)
249 pdbIdFileName.put(idForFile, absoluteFile);
250 pdbFileNameId.put(absoluteFile, idForFile);
253 public String findIdForPDBFile(String idOrFile)
255 String id = pdbFileNameId.get(idOrFile);
259 public String findFileForPDBId(String idOrFile)
261 String id = pdbIdFileName.get(idOrFile);
265 public boolean isPDBFileRegistered(String idOrFile)
267 return pdbFileNameId.containsKey(idOrFile)
268 || pdbIdFileName.containsKey(idOrFile);
272 * flag controlling whether SeqMappings are relayed from received sequence
273 * mouse over events to other sequences
275 boolean relaySeqMappings = true;
278 * Enable or disable relay of seqMapping events to other sequences. You might
279 * want to do this if there are many sequence mappings and the host computer
284 public void setRelaySeqMappings(boolean relay)
286 relaySeqMappings = relay;
290 * get the state of the relay seqMappings flag.
292 * @return true if sequence mouse overs are being relayed to other mapped
295 public boolean isRelaySeqMappingsEnabled()
297 return relaySeqMappings;
300 Vector<Object> listeners = new Vector<>();
303 * register a listener for alignment sequence mouseover events
307 public void addStructureViewerListener(Object svl)
309 if (!listeners.contains(svl))
311 listeners.addElement(svl);
316 * Returns the filename the PDB id is already mapped to if known, or null if
322 public String alreadyMappedToFile(String pdbid)
324 for (StructureMapping sm : mappings)
326 if (sm.getPdbId().equalsIgnoreCase(pdbid))
335 * Import structure data and register a structure mapping for broadcasting
336 * colouring, mouseovers and selection events (convenience wrapper).
338 * This is the standard entry point.
341 * - one or more sequences to be mapped to pdbFile
342 * @param targetChains
343 * - optional chain specification for mapping each sequence to pdb
344 * (may be nill, individual elements may be nill)
346 * - structure data resource
348 * - how to resolve data from resource
349 * @return null or the structure data parsed as a pdb file
351 synchronized public StructureFile setMapping(SequenceI[] sequence,
352 String[] targetChains, String pdbFile, DataSourceType protocol,
353 IProgressIndicator progress)
355 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
360 * Import a single structure file and register sequence structure mappings for
361 * broadcasting colouring, mouseovers and selection events (convenience
366 * @param forStructureView
367 * when true (testng only), record the mapping for use in mouseOvers
370 * - one or more sequences to be mapped to pdbFile
371 * @param targetChains
372 * - optional chain specification for mapping each sequence to pdb
373 * (may be nill, individual elements may be nill)
375 * - structure data resource
377 * - how to resolve data from resource
378 * @return null or the structure data parsed as a pdb file
380 synchronized public StructureFile setMapping(boolean forStructureView,
381 SequenceI[] sequenceArray, String[] targetChainIds,
382 String pdbFile, DataSourceType sourceType)
384 return computeMapping(forStructureView, sequenceArray, targetChainIds,
385 pdbFile, sourceType, null);
389 * create sequence structure mappings between each sequence and the given
390 * pdbFile (retrieved via the given protocol). Either constructs a mapping
391 * using NW alignment or derives one from any available SIFTS mapping data.
393 * @param forStructureView
394 * when true, record the mapping for use in mouseOvers
396 * @param sequenceArray
397 * - one or more sequences to be mapped to pdbFile
398 * @param targetChainIds
399 * - optional chain specification for mapping each sequence to pdb
400 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
401 * - this should be List<List<String>>, empty lists indicate no
402 * predefined mappings
404 * - structure data resource
406 * - how to resolve data from resource
407 * @param IProgressIndicator
408 * reference to UI component that maintains a progress bar for the
410 * @return null or the structure data parsed as a pdb file
412 synchronized private StructureFile computeMapping(
413 boolean forStructureView, SequenceI[] sequenceArray,
414 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
415 IProgressIndicator progress)
417 long progressSessionId = System.currentTimeMillis() * 3;
420 * do we extract and transfer annotation from 3D data ?
422 // FIXME: possibly should just delete
424 boolean parseSecStr = processSecondaryStructure
425 ? isStructureFileProcessed(pdbFile, sequenceArray)
428 StructureFile pdb = null;
429 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
432 // FIXME if sourceType is not null, we've lost data here
433 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
434 pdb = new JmolParser(false, pdbFile, sourceType);
435 pdb.addSettings(parseSecStr && processSecondaryStructure,
436 parseSecStr && addTempFacAnnot,
437 parseSecStr && secStructServices);
439 if (pdb.getId() != null && pdb.getId().trim().length() > 0
440 && DataSourceType.FILE == sourceType)
442 registerPDBFile(pdb.getId().trim(), pdbFile);
444 // if PDBId is unavailable then skip SIFTS mapping execution path
445 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
447 } catch (Exception ex)
449 ex.printStackTrace();
453 * sifts client - non null if SIFTS mappings are to be used
455 SiftsClient siftsClient = null;
460 siftsClient = new SiftsClient(pdb);
462 } catch (SiftsException e)
464 isMapUsingSIFTs = false;
469 String targetChainId;
470 for (int s = 0; s < sequenceArray.length; s++)
472 boolean infChain = true;
473 final SequenceI seq = sequenceArray[s];
475 while (ds.getDatasetSequence() != null)
477 ds = ds.getDatasetSequence();
480 if (targetChainIds != null && targetChainIds[s] != null)
483 targetChainId = targetChainIds[s];
485 else if (seq.getName().indexOf("|") > -1)
487 targetChainId = seq.getName()
488 .substring(seq.getName().lastIndexOf("|") + 1);
489 if (targetChainId.length() > 1)
491 if (targetChainId.trim().length() == 0)
497 // not a valid chain identifier
508 * Attempt pairwise alignment of the sequence with each chain in the PDB,
509 * and remember the highest scoring chain
512 AlignSeq maxAlignseq = null;
513 String maxChainId = " ";
514 PDBChain maxChain = null;
515 boolean first = true;
516 for (PDBChain chain : pdb.getChains())
518 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
521 continue; // don't try to map chains don't match.
523 // TODO: correctly determine sequence type for mixed na/peptide
525 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
526 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
529 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
530 // as.calcScoreMatrix();
531 // as.traceAlignment();
533 if (first || as.maxscore > max
534 || (as.maxscore == max && chain.id.equals(targetChainId)))
540 maxChainId = chain.id;
543 if (maxChain == null)
548 if (sourceType == DataSourceType.PASTE)
550 pdbFile = "INLINE" + pdb.getId();
553 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
554 if (isMapUsingSIFTs && seq.isProtein())
556 if (progress!=null) {
557 progress.setProgressBar(MessageManager
558 .getString("status.obtaining_mapping_with_sifts"),
561 jalview.datamodel.Mapping sqmpping = maxAlignseq
562 .getMappingFromS1(false);
563 if (targetChainId != null && !targetChainId.trim().isEmpty())
565 StructureMapping siftsMapping;
568 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
569 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
570 seqToStrucMapping.add(siftsMapping);
571 maxChain.makeExactMapping(siftsMapping, seq);
572 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
574 maxChain.transferResidueAnnotation(siftsMapping, null);
575 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
577 } catch (SiftsException e)
579 // fall back to NW alignment
580 System.err.println(e.getMessage());
581 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
582 targetChainId, maxChain, pdb, maxAlignseq);
583 seqToStrucMapping.add(nwMapping);
584 maxChain.makeExactMapping(maxAlignseq, seq);
585 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
588 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
589 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
594 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
595 for (PDBChain chain : pdb.getChains())
597 StructureMapping siftsMapping = null;
600 siftsMapping = getStructureMapping(seq,
601 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
603 foundSiftsMappings.add(siftsMapping);
604 chain.makeExactMapping(siftsMapping, seq);
605 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
607 chain.transferResidueAnnotation(siftsMapping, null);
608 } catch (SiftsException e)
610 System.err.println(e.getMessage());
616 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
617 System.err.println(e.getMessage());
620 if (!foundSiftsMappings.isEmpty())
622 seqToStrucMapping.addAll(foundSiftsMappings);
623 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
627 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
628 maxChainId, maxChain, pdb, maxAlignseq);
629 seqToStrucMapping.add(nwMapping);
630 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
632 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
633 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
639 if (progress != null)
641 progress.setProgressBar(MessageManager
642 .getString("status.obtaining_mapping_with_nw_alignment"),
645 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
646 maxChain, pdb, maxAlignseq);
647 seqToStrucMapping.add(nwMapping);
648 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
650 if (forStructureView)
652 for (StructureMapping sm : seqToStrucMapping)
654 addStructureMapping(sm); // not addAll!
657 if (progress != null)
659 progress.setProgressBar(null, progressSessionId);
666 * check if we need to extract secondary structure from given pdbFile and
667 * transfer to sequences
670 * @param sequenceArray
673 private boolean isStructureFileProcessed(String pdbFile,
674 SequenceI[] sequenceArray)
676 boolean parseSecStr = true;
677 if (isPDBFileRegistered(pdbFile))
679 for (SequenceI sq : sequenceArray)
682 while (ds.getDatasetSequence() != null)
684 ds = ds.getDatasetSequence();
686 if (ds.getAnnotation() != null)
688 for (AlignmentAnnotation ala : ds.getAnnotation())
690 // false if any annotation present from this structure
691 // JBPNote this fails for jmol/chimera view because the *file* is
692 // passed, not the structure data ID -
693 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
704 public void addStructureMapping(StructureMapping sm)
706 if (!mappings.contains(sm))
713 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
718 * @param targetChainId
724 * client for retrieval of SIFTS mappings for this structure
726 * @throws SiftsException
728 private StructureMapping getStructureMapping(SequenceI seq,
729 String pdbFile, String targetChainId, StructureFile pdb,
730 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
731 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
733 StructureMapping curChainMapping = siftsClient
734 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
737 PDBChain chain = pdb.findChain(targetChainId);
740 chain.transferResidueAnnotation(curChainMapping, null);
742 } catch (Exception e)
746 return curChainMapping;
749 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
750 String maxChainId, PDBChain maxChain, StructureFile pdb,
751 AlignSeq maxAlignseq)
753 final StringBuilder mappingDetails = new StringBuilder(128);
754 mappingDetails.append(NEWLINE)
755 .append("Sequence \u27f7 Structure mapping details");
756 mappingDetails.append(NEWLINE);
758 .append("Method: inferred with Needleman & Wunsch alignment");
759 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
760 .append(NEWLINE).append("Sequence = ")
761 .append(maxChain.sequence.getSequenceAsString());
762 mappingDetails.append(NEWLINE).append("No of residues = ")
763 .append(maxChain.residues.size()).append(NEWLINE)
765 PrintStream ps = new PrintStream(System.out)
768 public void print(String x)
770 mappingDetails.append(x);
774 public void println()
776 mappingDetails.append(NEWLINE);
780 maxAlignseq.printAlignment(ps);
782 mappingDetails.append(NEWLINE).append("PDB start/end ");
783 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
785 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
786 mappingDetails.append(NEWLINE).append("SEQ start/end ");
789 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
791 mappingDetails.append(
792 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
793 mappingDetails.append(NEWLINE);
794 maxChain.makeExactMapping(maxAlignseq, seq);
795 jalview.datamodel.Mapping sqmpping = maxAlignseq
796 .getMappingFromS1(false);
797 maxChain.transferRESNUMFeatures(seq, null);
799 HashMap<Integer, int[]> mapping = new HashMap<>();
806 Atom tmp = maxChain.atoms.elementAt(index);
807 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
808 && tmp.alignmentMapping != -1)
810 resNum = tmp.resNumber;
811 insCode = tmp.insCode;
812 if (tmp.alignmentMapping >= -1)
814 mapping.put(tmp.alignmentMapping + 1,
816 { tmp.resNumber, tmp.atomIndex });
821 } while (index < maxChain.atoms.size());
823 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
824 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
825 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
829 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
831 listeners.removeElement(svl);
832 if (svl instanceof SequenceListener)
834 for (int i = 0; i < listeners.size(); i++)
836 if (listeners.elementAt(i) instanceof StructureListener)
838 ((StructureListener) listeners.elementAt(i))
839 .releaseReferences(svl);
844 if (pdbfiles == null)
850 * Remove mappings to the closed listener's PDB files, but first check if
851 * another listener is still interested
853 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
855 StructureListener sl;
856 for (int i = 0; i < listeners.size(); i++)
858 if (listeners.elementAt(i) instanceof StructureListener)
860 sl = (StructureListener) listeners.elementAt(i);
861 for (String pdbfile : sl.getStructureFiles())
863 pdbs.remove(pdbfile);
869 * Rebuild the mappings set, retaining only those which are for 'other' PDB
874 List<StructureMapping> tmp = new ArrayList<>();
875 for (StructureMapping sm : mappings)
877 if (!pdbs.contains(sm.pdbfile))
888 * Propagate mouseover of a single position in a structure
894 public void mouseOverStructure(int pdbResNum, String chain,
897 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
898 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
899 mouseOverStructure(atoms);
903 * Propagate mouseover or selection of multiple positions in a structure
907 public void mouseOverStructure(List<AtomSpec> atoms)
909 if (listeners == null)
911 // old or prematurely sent event
914 boolean hasSequenceListener = false;
915 for (int i = 0; i < listeners.size(); i++)
917 if (listeners.elementAt(i) instanceof SequenceListener)
919 hasSequenceListener = true;
922 if (!hasSequenceListener)
927 SearchResultsI results = findAlignmentPositionsForStructurePositions(
929 for (Object li : listeners)
931 if (li instanceof SequenceListener)
933 ((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 (sm.pdbfile.equals(pdbfile) && 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()
1276 seqmappings.clear();
1277 sel_listeners.clear();
1279 commandListeners.clear();
1280 view_listeners.clear();
1281 pdbFileNameId.clear();
1282 pdbIdFileName.clear();
1285 public void addSelectionListener(SelectionListener selecter)
1287 if (!sel_listeners.contains(selecter))
1289 sel_listeners.add(selecter);
1293 public void removeSelectionListener(SelectionListener toremove)
1295 if (sel_listeners.contains(toremove))
1297 sel_listeners.remove(toremove);
1301 public synchronized void sendSelection(
1302 jalview.datamodel.SequenceGroup selection,
1303 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1304 SelectionSource source)
1306 for (SelectionListener slis : sel_listeners)
1310 slis.selection(selection, colsel, hidden, source);
1315 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1317 public synchronized void sendViewPosition(
1318 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1319 int startSeq, int endSeq)
1322 if (view_listeners != null && view_listeners.size() > 0)
1324 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1326 while (listeners.hasMoreElements())
1328 AlignmentViewPanelListener slis = listeners.nextElement();
1331 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1337 public void registerPDBEntry(PDBEntry pdbentry)
1339 if (pdbentry.getFile() != null
1340 && pdbentry.getFile().trim().length() > 0)
1342 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1346 public void addCommandListener(CommandListener cl)
1348 if (!commandListeners.contains(cl))
1350 commandListeners.add(cl);
1354 public boolean hasCommandListener(CommandListener cl)
1356 return this.commandListeners.contains(cl);
1359 public boolean removeCommandListener(CommandListener l)
1361 return commandListeners.remove(l);
1365 * Forward a command to any command listeners (except for the command's
1369 * the command to be broadcast (in its form after being performed)
1371 * if true, the command was being 'undone'
1374 public void commandPerformed(CommandI command, boolean undo,
1375 VamsasSource source)
1377 for (CommandListener listener : commandListeners)
1379 listener.mirrorCommand(command, undo, this, source);
1384 * Returns a new CommandI representing the given command as mapped to the
1385 * given sequences. If no mapping could be made, or the command is not of a
1386 * mappable kind, returns null.
1394 public CommandI mapCommand(CommandI command, boolean undo,
1395 final AlignmentI mapTo, char gapChar)
1397 if (command instanceof EditCommand)
1399 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1400 gapChar, seqmappings);
1402 else if (command instanceof OrderCommand)
1404 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1405 mapTo, seqmappings);
1410 public List<AlignedCodonFrame> getSequenceMappings()