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
896 public String mouseOverStructure(int pdbResNum, String chain,
899 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
900 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
901 return mouseOverStructure(atoms);
905 * Propagate mouseover or selection of multiple positions in a structure
909 public String mouseOverStructure(List<AtomSpec> atoms)
911 if (listeners == null)
913 // old or prematurely sent event
916 boolean hasSequenceListener = false;
917 for (int i = 0; i < listeners.size(); i++)
919 if (listeners.elementAt(i) instanceof SequenceListener)
921 hasSequenceListener = true;
924 if (!hasSequenceListener)
929 SearchResultsI results = findAlignmentPositionsForStructurePositions(
931 String result = null;
932 for (Object li : listeners)
934 if (li instanceof SequenceListener)
936 String s = ((SequenceListener) li).highlightSequence(results);
947 * Constructs a SearchResults object holding regions (if any) in the Jalview
948 * alignment which have a mapping to the structure viewer positions in the
954 public SearchResultsI findAlignmentPositionsForStructurePositions(
955 List<AtomSpec> atoms)
957 SearchResultsI results = new SearchResults();
958 for (AtomSpec atom : atoms)
960 SequenceI lastseq = null;
962 for (StructureMapping sm : mappings)
964 if (sm.pdbfile.equals(atom.getPdbFile())
965 && sm.pdbchain.equals(atom.getChain()))
967 int indexpos = sm.getSeqPos(atom.getPdbResNum());
968 if (lastipos != indexpos || lastseq != sm.sequence)
970 results.addResult(sm.sequence, indexpos, indexpos);
972 lastseq = sm.sequence;
973 // construct highlighted sequence list
974 for (AlignedCodonFrame acf : seqmappings)
976 acf.markMappedRegion(sm.sequence, indexpos, results);
986 * highlight regions associated with a position (indexpos) in seq
989 * the sequence that the mouse over occurred on
991 * the absolute position being mouseovered in seq (0 to seq.length())
993 * the sequence position (if -1, seq.findPosition is called to
994 * resolve the residue number)
996 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
999 boolean hasSequenceListeners = handlingVamsasMo
1000 || !seqmappings.isEmpty();
1001 SearchResultsI results = null;
1004 seqPos = seq.findPosition(indexpos);
1006 for (int i = 0; i < listeners.size(); i++)
1008 Object listener = listeners.elementAt(i);
1009 if (listener == source)
1011 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1012 // Temporary fudge with SequenceListener.getVamsasSource()
1015 if (listener instanceof StructureListener)
1017 highlightStructure((StructureListener) listener, seq, seqPos);
1021 if (listener instanceof SequenceListener)
1023 final SequenceListener seqListener = (SequenceListener) listener;
1024 if (hasSequenceListeners
1025 && seqListener.getVamsasSource() != source)
1027 if (relaySeqMappings)
1029 if (results == null)
1031 results = MappingUtils.buildSearchResults(seq, seqPos,
1034 if (handlingVamsasMo)
1036 results.addResult(seq, seqPos, seqPos);
1039 if (!results.isEmpty())
1041 seqListener.highlightSequence(results);
1046 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1048 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1051 else if (listener instanceof SecondaryStructureListener)
1053 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1061 * Send suitable messages to a StructureListener to highlight atoms
1062 * corresponding to the given sequence position(s)
1068 public void highlightStructure(StructureListener sl, SequenceI seq,
1071 if (!sl.isListeningFor(seq))
1076 List<AtomSpec> atoms = new ArrayList<>();
1077 for (StructureMapping sm : mappings)
1079 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1080 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1081 .getDatasetSequence() == seq.getDatasetSequence()))
1083 for (int index : positions)
1085 atomNo = sm.getAtomNum(index);
1089 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1090 sm.getPDBResNum(index), atomNo));
1095 sl.highlightAtoms(atoms);
1099 * true if a mouse over event from an external (ie Vamsas) source is being
1102 boolean handlingVamsasMo = false;
1107 * as mouseOverSequence but only route event to SequenceListeners
1111 * in an alignment sequence
1113 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1114 VamsasSource source)
1116 handlingVamsasMo = true;
1117 long msg = sequenceI.hashCode() * (1 + position);
1121 mouseOverSequence(sequenceI, position, -1, source);
1123 handlingVamsasMo = false;
1126 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1130 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1131 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1133 * Annotation [] annotations = new Annotation[seq.getLength()];
1135 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1136 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1137 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1139 * for (int j = 0; j < mappings.length; j++) {
1141 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1142 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1143 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1144 * "+mappings[j].pdbfile);
1146 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1147 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1149 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1150 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1151 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1152 * mappings[j].pdbfile); }
1154 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1155 * annotations; } } } }
1157 * return annotations;
1161 public void structureSelectionChanged()
1165 public void sequenceSelectionChanged()
1169 public void sequenceColoursChanged(Object source)
1171 StructureListener sl;
1172 for (int i = 0; i < listeners.size(); i++)
1174 if (listeners.elementAt(i) instanceof StructureListener)
1176 sl = (StructureListener) listeners.elementAt(i);
1177 sl.updateColours(source);
1182 public StructureMapping[] getMapping(String pdbfile)
1184 List<StructureMapping> tmp = new ArrayList<>();
1185 for (StructureMapping sm : mappings)
1187 if (sm.pdbfile.equals(pdbfile))
1192 return tmp.toArray(new StructureMapping[tmp.size()]);
1196 * Returns a readable description of all mappings for the given pdbfile to any
1197 * of the given sequences
1203 public String printMappings(String pdbfile, List<SequenceI> seqs)
1205 if (pdbfile == null || seqs == null || seqs.isEmpty())
1210 StringBuilder sb = new StringBuilder(64);
1211 for (StructureMapping sm : mappings)
1213 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1214 && seqs.contains(sm.sequence))
1216 sb.append(sm.mappingDetails);
1218 // separator makes it easier to read multiple mappings
1219 sb.append("=====================");
1225 return sb.toString();
1229 * Remove the given mapping
1233 public void deregisterMapping(AlignedCodonFrame acf)
1237 boolean removed = seqmappings.remove(acf);
1238 if (removed && seqmappings.isEmpty())
1240 System.out.println("All mappings removed");
1246 * Add each of the given codonFrames to the stored set, if not aready present.
1250 public void registerMappings(List<AlignedCodonFrame> mappings)
1252 if (mappings != null)
1254 for (AlignedCodonFrame acf : mappings)
1256 registerMapping(acf);
1262 * Add the given mapping to the stored set, unless already stored.
1264 public void registerMapping(AlignedCodonFrame acf)
1268 if (!seqmappings.contains(acf))
1270 seqmappings.add(acf);
1276 * Resets this object to its initial state by removing all registered
1277 * listeners, codon mappings, PDB file mappings
1279 public void resetAll()
1281 if (mappings != null)
1285 if (seqmappings != null)
1287 seqmappings.clear();
1289 if (sel_listeners != null)
1291 sel_listeners.clear();
1293 if (listeners != null)
1297 if (commandListeners != null)
1299 commandListeners.clear();
1301 if (view_listeners != null)
1303 view_listeners.clear();
1305 if (pdbFileNameId != null)
1307 pdbFileNameId.clear();
1309 if (pdbIdFileName != null)
1311 pdbIdFileName.clear();
1315 public void addSelectionListener(SelectionListener selecter)
1317 if (!sel_listeners.contains(selecter))
1319 sel_listeners.add(selecter);
1323 public void removeSelectionListener(SelectionListener toremove)
1325 if (sel_listeners.contains(toremove))
1327 sel_listeners.remove(toremove);
1331 public synchronized void sendSelection(
1332 jalview.datamodel.SequenceGroup selection,
1333 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1334 SelectionSource source)
1336 for (SelectionListener slis : sel_listeners)
1340 slis.selection(selection, colsel, hidden, source);
1345 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1347 public synchronized void sendViewPosition(
1348 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1349 int startSeq, int endSeq)
1352 if (view_listeners != null && view_listeners.size() > 0)
1354 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1356 while (listeners.hasMoreElements())
1358 AlignmentViewPanelListener slis = listeners.nextElement();
1361 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1367 public void registerPDBEntry(PDBEntry pdbentry)
1369 if (pdbentry.getFile() != null
1370 && pdbentry.getFile().trim().length() > 0)
1372 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1376 public void addCommandListener(CommandListener cl)
1378 if (!commandListeners.contains(cl))
1380 commandListeners.add(cl);
1384 public boolean hasCommandListener(CommandListener cl)
1386 return this.commandListeners.contains(cl);
1389 public boolean removeCommandListener(CommandListener l)
1391 return commandListeners.remove(l);
1395 * Forward a command to any command listeners (except for the command's
1399 * the command to be broadcast (in its form after being performed)
1401 * if true, the command was being 'undone'
1404 public void commandPerformed(CommandI command, boolean undo,
1405 VamsasSource source)
1407 for (CommandListener listener : commandListeners)
1409 listener.mirrorCommand(command, undo, this, source);
1414 * Returns a new CommandI representing the given command as mapped to the
1415 * given sequences. If no mapping could be made, or the command is not of a
1416 * mappable kind, returns null.
1424 public CommandI mapCommand(CommandI command, boolean undo,
1425 final AlignmentI mapTo, char gapChar)
1427 if (command instanceof EditCommand)
1429 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1430 gapChar, seqmappings);
1432 else if (command instanceof OrderCommand)
1434 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1435 mapTo, seqmappings);
1440 public List<AlignedCodonFrame> getSequenceMappings()