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> structureSelections;
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.
124 StructureSelectionManager getInstanceForContext(
125 StructureSelectionManagerProvider context)
127 StructureSelectionManager instance = structureSelections.get(context);
128 if (instance == null)
130 instance = new StructureSelectionManager();
131 structureSelections.put(context, instance);
137 * Removes the instance associated with this provider
142 public static void release(StructureSelectionManagerProvider provider)
144 getInstance().structureSelections.remove(provider);
148 * Private constructor as all 'singleton' instances are managed here or by
149 * ApplicationSingletonProvider
151 private StructureSelectionManager()
153 structureSelections = new IdentityHashMap<>();
157 * @return true if will try to use external services for processing secondary
160 public boolean isSecStructServices()
162 return secStructServices;
166 * control use of external services for processing secondary structure
168 * @param secStructServices
170 public void setSecStructServices(boolean secStructServices)
172 this.secStructServices = secStructServices;
176 * flag controlling addition of any kind of structural annotation
178 * @return true if temperature factor annotation will be added
180 public boolean isAddTempFacAnnot()
182 return addTempFacAnnot;
186 * set flag controlling addition of structural annotation
188 * @param addTempFacAnnot
190 public void setAddTempFacAnnot(boolean addTempFacAnnot)
192 this.addTempFacAnnot = addTempFacAnnot;
197 * @return if true, the structure manager will attempt to add secondary
198 * structure lines for unannotated sequences
201 public boolean isProcessSecondaryStructure()
203 return processSecondaryStructure;
207 * Control whether structure manager will try to annotate mapped sequences
208 * with secondary structure from PDB data.
212 public void setProcessSecondaryStructure(boolean enable)
214 processSecondaryStructure = enable;
218 * debug function - write all mappings to stdout
220 public void reportMapping()
222 if (mappings.isEmpty())
224 System.err.println("reportMapping: No PDB/Sequence mappings.");
229 "reportMapping: There are " + mappings.size() + " mappings.");
231 for (StructureMapping sm : mappings)
233 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
239 * map between the PDB IDs (or structure identifiers) used by Jalview and the
240 * absolute filenames for PDB data that corresponds to it
242 Map<String, String> pdbIdFileName = new HashMap<>();
244 Map<String, String> pdbFileNameId = new HashMap<>();
246 public void registerPDBFile(String idForFile, String absoluteFile)
248 pdbIdFileName.put(idForFile, absoluteFile);
249 pdbFileNameId.put(absoluteFile, idForFile);
252 public String findIdForPDBFile(String idOrFile)
254 String id = pdbFileNameId.get(idOrFile);
258 public String findFileForPDBId(String idOrFile)
260 String id = pdbIdFileName.get(idOrFile);
264 public boolean isPDBFileRegistered(String idOrFile)
266 return pdbFileNameId.containsKey(idOrFile)
267 || pdbIdFileName.containsKey(idOrFile);
271 * flag controlling whether SeqMappings are relayed from received sequence
272 * mouse over events to other sequences
274 boolean relaySeqMappings = true;
277 * Enable or disable relay of seqMapping events to other sequences. You might
278 * want to do this if there are many sequence mappings and the host computer
283 public void setRelaySeqMappings(boolean relay)
285 relaySeqMappings = relay;
289 * get the state of the relay seqMappings flag.
291 * @return true if sequence mouse overs are being relayed to other mapped
294 public boolean isRelaySeqMappingsEnabled()
296 return relaySeqMappings;
299 Vector<Object> listeners = new Vector<>();
302 * register a listener for alignment sequence mouseover events
306 public void addStructureViewerListener(Object svl)
308 if (!listeners.contains(svl))
310 listeners.addElement(svl);
315 * Returns the filename the PDB id is already mapped to if known, or null if
321 public String alreadyMappedToFile(String pdbid)
323 for (StructureMapping sm : mappings)
325 if (sm.getPdbId().equalsIgnoreCase(pdbid))
334 * Import structure data and register a structure mapping for broadcasting
335 * colouring, mouseovers and selection events (convenience wrapper).
337 * This is the standard entry point.
340 * - one or more sequences to be mapped to pdbFile
341 * @param targetChains
342 * - optional chain specification for mapping each sequence to pdb
343 * (may be nill, individual elements may be nill)
345 * - structure data resource
347 * - how to resolve data from resource
348 * @return null or the structure data parsed as a pdb file
350 synchronized public StructureFile setMapping(SequenceI[] sequence,
351 String[] targetChains, String pdbFile, DataSourceType protocol,
352 IProgressIndicator progress)
354 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
359 * Import a single structure file and register sequence structure mappings for
360 * broadcasting colouring, mouseovers and selection events (convenience
365 * @param forStructureView
366 * when true (testng only), record the mapping for use in mouseOvers
369 * - one or more sequences to be mapped to pdbFile
370 * @param targetChains
371 * - optional chain specification for mapping each sequence to pdb
372 * (may be nill, individual elements may be nill)
374 * - structure data resource
376 * - how to resolve data from resource
377 * @return null or the structure data parsed as a pdb file
379 synchronized public StructureFile setMapping(boolean forStructureView,
380 SequenceI[] sequenceArray, String[] targetChainIds,
381 String pdbFile, DataSourceType sourceType)
383 return computeMapping(forStructureView, sequenceArray, targetChainIds,
384 pdbFile, sourceType, null);
388 * create sequence structure mappings between each sequence and the given
389 * pdbFile (retrieved via the given protocol). Either constructs a mapping
390 * using NW alignment or derives one from any available SIFTS mapping data.
392 * @param forStructureView
393 * when true, record the mapping for use in mouseOvers
395 * @param sequenceArray
396 * - one or more sequences to be mapped to pdbFile
397 * @param targetChainIds
398 * - optional chain specification for mapping each sequence to pdb
399 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
400 * - this should be List<List<String>>, empty lists indicate no
401 * predefined mappings
403 * - structure data resource
405 * - how to resolve data from resource
406 * @param IProgressIndicator
407 * reference to UI component that maintains a progress bar for the
409 * @return null or the structure data parsed as a pdb file
411 synchronized private StructureFile computeMapping(
412 boolean forStructureView, SequenceI[] sequenceArray,
413 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
414 IProgressIndicator progress)
416 long progressSessionId = System.currentTimeMillis() * 3;
419 * do we extract and transfer annotation from 3D data ?
421 // FIXME: possibly should just delete
423 boolean parseSecStr = processSecondaryStructure
424 ? isStructureFileProcessed(pdbFile, sequenceArray)
427 StructureFile pdb = null;
428 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
431 // FIXME if sourceType is not null, we've lost data here
432 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
433 pdb = new JmolParser(false, pdbFile, sourceType);
434 pdb.addSettings(parseSecStr && processSecondaryStructure,
435 parseSecStr && addTempFacAnnot,
436 parseSecStr && secStructServices);
438 if (pdb.getId() != null && pdb.getId().trim().length() > 0
439 && DataSourceType.FILE == sourceType)
441 registerPDBFile(pdb.getId().trim(), pdbFile);
443 // if PDBId is unavailable then skip SIFTS mapping execution path
444 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
446 } catch (Exception ex)
448 ex.printStackTrace();
452 * sifts client - non null if SIFTS mappings are to be used
454 SiftsClient siftsClient = null;
459 siftsClient = new SiftsClient(pdb);
461 } catch (SiftsException e)
463 isMapUsingSIFTs = false;
468 String targetChainId;
469 for (int s = 0; s < sequenceArray.length; s++)
471 boolean infChain = true;
472 final SequenceI seq = sequenceArray[s];
474 while (ds.getDatasetSequence() != null)
476 ds = ds.getDatasetSequence();
479 if (targetChainIds != null && targetChainIds[s] != null)
482 targetChainId = targetChainIds[s];
484 else if (seq.getName().indexOf("|") > -1)
486 targetChainId = seq.getName()
487 .substring(seq.getName().lastIndexOf("|") + 1);
488 if (targetChainId.length() > 1)
490 if (targetChainId.trim().length() == 0)
496 // not a valid chain identifier
507 * Attempt pairwise alignment of the sequence with each chain in the PDB,
508 * and remember the highest scoring chain
511 AlignSeq maxAlignseq = null;
512 String maxChainId = " ";
513 PDBChain maxChain = null;
514 boolean first = true;
515 for (PDBChain chain : pdb.getChains())
517 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
520 continue; // don't try to map chains don't match.
522 // TODO: correctly determine sequence type for mixed na/peptide
524 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
525 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
528 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
529 // as.calcScoreMatrix();
530 // as.traceAlignment();
532 if (first || as.maxscore > max
533 || (as.maxscore == max && chain.id.equals(targetChainId)))
539 maxChainId = chain.id;
542 if (maxChain == null)
547 if (sourceType == DataSourceType.PASTE)
549 pdbFile = "INLINE" + pdb.getId();
552 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
553 if (isMapUsingSIFTs && seq.isProtein())
555 if (progress!=null) {
556 progress.setProgressBar(MessageManager
557 .getString("status.obtaining_mapping_with_sifts"),
560 jalview.datamodel.Mapping sqmpping = maxAlignseq
561 .getMappingFromS1(false);
562 if (targetChainId != null && !targetChainId.trim().isEmpty())
564 StructureMapping siftsMapping;
567 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
568 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
569 seqToStrucMapping.add(siftsMapping);
570 maxChain.makeExactMapping(siftsMapping, seq);
571 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
573 maxChain.transferResidueAnnotation(siftsMapping, null);
574 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
576 } catch (SiftsException e)
578 // fall back to NW alignment
579 System.err.println(e.getMessage());
580 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
581 targetChainId, maxChain, pdb, maxAlignseq);
582 seqToStrucMapping.add(nwMapping);
583 maxChain.makeExactMapping(maxAlignseq, seq);
584 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
587 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
588 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
593 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
594 for (PDBChain chain : pdb.getChains())
596 StructureMapping siftsMapping = null;
599 siftsMapping = getStructureMapping(seq,
600 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
602 foundSiftsMappings.add(siftsMapping);
603 chain.makeExactMapping(siftsMapping, seq);
604 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
606 chain.transferResidueAnnotation(siftsMapping, null);
607 } catch (SiftsException e)
609 System.err.println(e.getMessage());
615 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
616 System.err.println(e.getMessage());
619 if (!foundSiftsMappings.isEmpty())
621 seqToStrucMapping.addAll(foundSiftsMappings);
622 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
626 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
627 maxChainId, maxChain, pdb, maxAlignseq);
628 seqToStrucMapping.add(nwMapping);
629 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
631 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
632 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
638 if (progress != null)
640 progress.setProgressBar(MessageManager
641 .getString("status.obtaining_mapping_with_nw_alignment"),
644 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
645 maxChain, pdb, maxAlignseq);
646 seqToStrucMapping.add(nwMapping);
647 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
649 if (forStructureView)
651 for (StructureMapping sm : seqToStrucMapping)
653 addStructureMapping(sm); // not addAll!
656 if (progress != null)
658 progress.setProgressBar(null, progressSessionId);
665 * check if we need to extract secondary structure from given pdbFile and
666 * transfer to sequences
669 * @param sequenceArray
672 private boolean isStructureFileProcessed(String pdbFile,
673 SequenceI[] sequenceArray)
675 boolean parseSecStr = true;
676 if (isPDBFileRegistered(pdbFile))
678 for (SequenceI sq : sequenceArray)
681 while (ds.getDatasetSequence() != null)
683 ds = ds.getDatasetSequence();
685 if (ds.getAnnotation() != null)
687 for (AlignmentAnnotation ala : ds.getAnnotation())
689 // false if any annotation present from this structure
690 // JBPNote this fails for jmol/chimera view because the *file* is
691 // passed, not the structure data ID -
692 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
703 public void addStructureMapping(StructureMapping sm)
705 if (!mappings.contains(sm))
712 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
717 * @param targetChainId
723 * client for retrieval of SIFTS mappings for this structure
725 * @throws SiftsException
727 private StructureMapping getStructureMapping(SequenceI seq,
728 String pdbFile, String targetChainId, StructureFile pdb,
729 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
730 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
732 StructureMapping curChainMapping = siftsClient
733 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
736 PDBChain chain = pdb.findChain(targetChainId);
739 chain.transferResidueAnnotation(curChainMapping, null);
741 } catch (Exception e)
745 return curChainMapping;
748 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
749 String maxChainId, PDBChain maxChain, StructureFile pdb,
750 AlignSeq maxAlignseq)
752 final StringBuilder mappingDetails = new StringBuilder(128);
753 mappingDetails.append(NEWLINE)
754 .append("Sequence \u27f7 Structure mapping details");
755 mappingDetails.append(NEWLINE);
757 .append("Method: inferred with Needleman & Wunsch alignment");
758 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
759 .append(NEWLINE).append("Sequence = ")
760 .append(maxChain.sequence.getSequenceAsString());
761 mappingDetails.append(NEWLINE).append("No of residues = ")
762 .append(maxChain.residues.size()).append(NEWLINE)
764 PrintStream ps = new PrintStream(System.out)
767 public void print(String x)
769 mappingDetails.append(x);
773 public void println()
775 mappingDetails.append(NEWLINE);
779 maxAlignseq.printAlignment(ps);
781 mappingDetails.append(NEWLINE).append("PDB start/end ");
782 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
784 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
785 mappingDetails.append(NEWLINE).append("SEQ start/end ");
788 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
790 mappingDetails.append(
791 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
792 mappingDetails.append(NEWLINE);
793 maxChain.makeExactMapping(maxAlignseq, seq);
794 jalview.datamodel.Mapping sqmpping = maxAlignseq
795 .getMappingFromS1(false);
796 maxChain.transferRESNUMFeatures(seq, null);
798 HashMap<Integer, int[]> mapping = new HashMap<>();
805 Atom tmp = maxChain.atoms.elementAt(index);
806 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
807 && tmp.alignmentMapping != -1)
809 resNum = tmp.resNumber;
810 insCode = tmp.insCode;
811 if (tmp.alignmentMapping >= -1)
813 mapping.put(tmp.alignmentMapping + 1,
815 { tmp.resNumber, tmp.atomIndex });
820 } while (index < maxChain.atoms.size());
822 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
823 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
824 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
828 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
830 listeners.removeElement(svl);
831 if (svl instanceof SequenceListener)
833 for (int i = 0; i < listeners.size(); i++)
835 if (listeners.elementAt(i) instanceof StructureListener)
837 ((StructureListener) listeners.elementAt(i))
838 .releaseReferences(svl);
843 if (pdbfiles == null)
849 * Remove mappings to the closed listener's PDB files, but first check if
850 * another listener is still interested
852 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
854 StructureListener sl;
855 for (int i = 0; i < listeners.size(); i++)
857 if (listeners.elementAt(i) instanceof StructureListener)
859 sl = (StructureListener) listeners.elementAt(i);
860 for (String pdbfile : sl.getStructureFiles())
862 pdbs.remove(pdbfile);
868 * Rebuild the mappings set, retaining only those which are for 'other' PDB
873 List<StructureMapping> tmp = new ArrayList<>();
874 for (StructureMapping sm : mappings)
876 if (!pdbs.contains(sm.pdbfile))
887 * Propagate mouseover of a single position in a structure
893 public void mouseOverStructure(int pdbResNum, String chain,
896 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
897 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
898 mouseOverStructure(atoms);
902 * Propagate mouseover or selection of multiple positions in a structure
906 public void mouseOverStructure(List<AtomSpec> atoms)
908 if (listeners == null)
910 // old or prematurely sent event
913 boolean hasSequenceListener = false;
914 for (int i = 0; i < listeners.size(); i++)
916 if (listeners.elementAt(i) instanceof SequenceListener)
918 hasSequenceListener = true;
921 if (!hasSequenceListener)
926 SearchResultsI results = findAlignmentPositionsForStructurePositions(
928 for (Object li : listeners)
930 if (li instanceof SequenceListener)
932 ((SequenceListener) li).highlightSequence(results);
938 * Constructs a SearchResults object holding regions (if any) in the Jalview
939 * alignment which have a mapping to the structure viewer positions in the
945 public SearchResultsI findAlignmentPositionsForStructurePositions(
946 List<AtomSpec> atoms)
948 SearchResultsI results = new SearchResults();
949 for (AtomSpec atom : atoms)
951 SequenceI lastseq = null;
953 for (StructureMapping sm : mappings)
955 if (sm.pdbfile.equals(atom.getPdbFile())
956 && sm.pdbchain.equals(atom.getChain()))
958 int indexpos = sm.getSeqPos(atom.getPdbResNum());
959 if (lastipos != indexpos || lastseq != sm.sequence)
961 results.addResult(sm.sequence, indexpos, indexpos);
963 lastseq = sm.sequence;
964 // construct highlighted sequence list
965 for (AlignedCodonFrame acf : seqmappings)
967 acf.markMappedRegion(sm.sequence, indexpos, results);
977 * highlight regions associated with a position (indexpos) in seq
980 * the sequence that the mouse over occurred on
982 * the absolute position being mouseovered in seq (0 to seq.length())
984 * the sequence position (if -1, seq.findPosition is called to
985 * resolve the residue number)
987 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
990 boolean hasSequenceListeners = handlingVamsasMo
991 || !seqmappings.isEmpty();
992 SearchResultsI results = null;
995 seqPos = seq.findPosition(indexpos);
997 for (int i = 0; i < listeners.size(); i++)
999 Object listener = listeners.elementAt(i);
1000 if (listener == source)
1002 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1003 // Temporary fudge with SequenceListener.getVamsasSource()
1006 if (listener instanceof StructureListener)
1008 highlightStructure((StructureListener) listener, seq, seqPos);
1012 if (listener instanceof SequenceListener)
1014 final SequenceListener seqListener = (SequenceListener) listener;
1015 if (hasSequenceListeners
1016 && seqListener.getVamsasSource() != source)
1018 if (relaySeqMappings)
1020 if (results == null)
1022 results = MappingUtils.buildSearchResults(seq, seqPos,
1025 if (handlingVamsasMo)
1027 results.addResult(seq, seqPos, seqPos);
1030 if (!results.isEmpty())
1032 seqListener.highlightSequence(results);
1037 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1039 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1042 else if (listener instanceof SecondaryStructureListener)
1044 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1052 * Send suitable messages to a StructureListener to highlight atoms
1053 * corresponding to the given sequence position(s)
1059 public void highlightStructure(StructureListener sl, SequenceI seq,
1062 if (!sl.isListeningFor(seq))
1067 List<AtomSpec> atoms = new ArrayList<>();
1068 for (StructureMapping sm : mappings)
1070 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1071 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1072 .getDatasetSequence() == seq.getDatasetSequence()))
1074 for (int index : positions)
1076 atomNo = sm.getAtomNum(index);
1080 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1081 sm.getPDBResNum(index), atomNo));
1086 sl.highlightAtoms(atoms);
1090 * true if a mouse over event from an external (ie Vamsas) source is being
1093 boolean handlingVamsasMo = false;
1098 * as mouseOverSequence but only route event to SequenceListeners
1102 * in an alignment sequence
1104 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1105 VamsasSource source)
1107 handlingVamsasMo = true;
1108 long msg = sequenceI.hashCode() * (1 + position);
1112 mouseOverSequence(sequenceI, position, -1, source);
1114 handlingVamsasMo = false;
1117 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1121 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1122 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1124 * Annotation [] annotations = new Annotation[seq.getLength()];
1126 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1127 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1128 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1130 * for (int j = 0; j < mappings.length; j++) {
1132 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1133 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1134 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1135 * "+mappings[j].pdbfile);
1137 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1138 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1140 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1141 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1142 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1143 * mappings[j].pdbfile); }
1145 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1146 * annotations; } } } }
1148 * return annotations;
1152 public void structureSelectionChanged()
1156 public void sequenceSelectionChanged()
1160 public void sequenceColoursChanged(Object source)
1162 StructureListener sl;
1163 for (int i = 0; i < listeners.size(); i++)
1165 if (listeners.elementAt(i) instanceof StructureListener)
1167 sl = (StructureListener) listeners.elementAt(i);
1168 sl.updateColours(source);
1173 public StructureMapping[] getMapping(String pdbfile)
1175 List<StructureMapping> tmp = new ArrayList<>();
1176 for (StructureMapping sm : mappings)
1178 if (sm.pdbfile.equals(pdbfile))
1183 return tmp.toArray(new StructureMapping[tmp.size()]);
1187 * Returns a readable description of all mappings for the given pdbfile to any
1188 * of the given sequences
1194 public String printMappings(String pdbfile, List<SequenceI> seqs)
1196 if (pdbfile == null || seqs == null || seqs.isEmpty())
1201 StringBuilder sb = new StringBuilder(64);
1202 for (StructureMapping sm : mappings)
1204 if (sm.pdbfile.equals(pdbfile) && seqs.contains(sm.sequence))
1206 sb.append(sm.mappingDetails);
1208 // separator makes it easier to read multiple mappings
1209 sb.append("=====================");
1215 return sb.toString();
1219 * Remove the given mapping
1223 public void deregisterMapping(AlignedCodonFrame acf)
1227 boolean removed = seqmappings.remove(acf);
1228 if (removed && seqmappings.isEmpty())
1230 System.out.println("All mappings removed");
1236 * Add each of the given codonFrames to the stored set, if not aready present.
1240 public void registerMappings(List<AlignedCodonFrame> mappings)
1242 if (mappings != null)
1244 for (AlignedCodonFrame acf : mappings)
1246 registerMapping(acf);
1252 * Add the given mapping to the stored set, unless already stored.
1254 public void registerMapping(AlignedCodonFrame acf)
1258 if (!seqmappings.contains(acf))
1260 seqmappings.add(acf);
1266 * Reset this object to its initial state by removing all registered
1267 * listeners, codon mappings, PDB file mappings.
1269 * Called only by Desktop and testng.
1272 public void resetAll()
1275 seqmappings.clear();
1276 sel_listeners.clear();
1278 commandListeners.clear();
1279 view_listeners.clear();
1280 pdbFileNameId.clear();
1281 pdbIdFileName.clear();
1284 public void addSelectionListener(SelectionListener selecter)
1286 if (!sel_listeners.contains(selecter))
1288 sel_listeners.add(selecter);
1292 public void removeSelectionListener(SelectionListener toremove)
1294 if (sel_listeners.contains(toremove))
1296 sel_listeners.remove(toremove);
1300 public synchronized void sendSelection(
1301 jalview.datamodel.SequenceGroup selection,
1302 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1303 SelectionSource source)
1305 for (SelectionListener slis : sel_listeners)
1309 slis.selection(selection, colsel, hidden, source);
1314 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1316 public synchronized void sendViewPosition(
1317 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1318 int startSeq, int endSeq)
1321 if (view_listeners != null && view_listeners.size() > 0)
1323 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1325 while (listeners.hasMoreElements())
1327 AlignmentViewPanelListener slis = listeners.nextElement();
1330 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1336 public void registerPDBEntry(PDBEntry pdbentry)
1338 if (pdbentry.getFile() != null
1339 && pdbentry.getFile().trim().length() > 0)
1341 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1345 public void addCommandListener(CommandListener cl)
1347 if (!commandListeners.contains(cl))
1349 commandListeners.add(cl);
1353 public boolean hasCommandListener(CommandListener cl)
1355 return this.commandListeners.contains(cl);
1358 public boolean removeCommandListener(CommandListener l)
1360 return commandListeners.remove(l);
1364 * Forward a command to any command listeners (except for the command's
1368 * the command to be broadcast (in its form after being performed)
1370 * if true, the command was being 'undone'
1373 public void commandPerformed(CommandI command, boolean undo,
1374 VamsasSource source)
1376 for (CommandListener listener : commandListeners)
1378 listener.mirrorCommand(command, undo, this, source);
1383 * Returns a new CommandI representing the given command as mapped to the
1384 * given sequences. If no mapping could be made, or the command is not of a
1385 * mappable kind, returns null.
1393 public CommandI mapCommand(CommandI command, boolean undo,
1394 final AlignmentI mapTo, char gapChar)
1396 if (command instanceof EditCommand)
1398 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1399 gapChar, seqmappings);
1401 else if (command instanceof OrderCommand)
1403 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1404 mapTo, seqmappings);
1409 public List<AlignedCodonFrame> getSequenceMappings()