2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.structure;
23 import jalview.analysis.AlignSeq;
24 import jalview.api.StructureSelectionManagerProvider;
25 import jalview.bin.ApplicationSingletonProvider;
26 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
27 import jalview.bin.Cache;
28 import jalview.commands.CommandI;
29 import jalview.commands.EditCommand;
30 import jalview.commands.OrderCommand;
31 import jalview.datamodel.AlignedCodonFrame;
32 import jalview.datamodel.AlignmentAnnotation;
33 import jalview.datamodel.AlignmentI;
34 import jalview.datamodel.Annotation;
35 import jalview.datamodel.HiddenColumns;
36 import jalview.datamodel.PDBEntry;
37 import jalview.datamodel.SearchResults;
38 import jalview.datamodel.SearchResultsI;
39 import jalview.datamodel.SequenceI;
40 import jalview.ext.jmol.JmolParser;
41 import jalview.gui.IProgressIndicator;
42 import jalview.io.AppletFormatAdapter;
43 import jalview.io.DataSourceType;
44 import jalview.io.StructureFile;
45 import jalview.util.MappingUtils;
46 import jalview.util.MessageManager;
47 import jalview.util.Platform;
48 import jalview.ws.sifts.SiftsClient;
49 import jalview.ws.sifts.SiftsException;
50 import jalview.ws.sifts.SiftsSettings;
52 import java.io.PrintStream;
53 import java.util.ArrayList;
54 import java.util.Arrays;
55 import java.util.Collections;
56 import java.util.Enumeration;
57 import java.util.HashMap;
58 import java.util.IdentityHashMap;
59 import java.util.List;
61 import java.util.Vector;
64 import mc_view.PDBChain;
65 import mc_view.PDBfile;
67 public class StructureSelectionManager implements ApplicationSingletonI
69 public final static String NEWLINE = System.lineSeparator();
71 private List<StructureMapping> mappings = new ArrayList<>();
73 private boolean processSecondaryStructure = false;
75 private boolean secStructServices = false;
77 private boolean addTempFacAnnot = false;
80 * Set of any registered mappings between (dataset) sequences.
82 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
84 private List<CommandListener> commandListeners = new ArrayList<>();
86 private List<SelectionListener> sel_listeners = new ArrayList<>();
89 * instances of this class scoped by some context class
91 private IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> selectionManagers;
94 * Answers an instance of this class for the current application (Java or JS
99 private static StructureSelectionManager getInstance()
101 return (StructureSelectionManager) ApplicationSingletonProvider
102 .getInstance(StructureSelectionManager.class);
106 * Private constructor as all 'singleton' instances are managed here or by
107 * ApplicationSingletonProvider
109 private StructureSelectionManager()
111 selectionManagers = new IdentityHashMap<>();
115 * Answers an instance of this class for the current application (Java or JS
116 * 'applet') scope, and scoped to the specified context
121 public static StructureSelectionManager getStructureSelectionManager(
122 StructureSelectionManagerProvider context)
124 return getInstance().getInstanceForContext(context);
128 * Answers an instance of this class scoped to the given context. The instance
129 * is created on the first request for the context, thereafter the same
130 * instance is returned. Note that the context may be null (this is the case
131 * when running headless without a Desktop).
136 StructureSelectionManager getInstanceForContext(
137 StructureSelectionManagerProvider context)
139 StructureSelectionManager instance = selectionManagers.get(context);
140 if (instance == null)
142 instance = new StructureSelectionManager();
143 selectionManagers.put(context, instance);
150 * @return true if will try to use external services for processing secondary
153 public boolean isSecStructServices()
155 return secStructServices;
159 * control use of external services for processing secondary structure
161 * @param secStructServices
163 public void setSecStructServices(boolean secStructServices)
165 this.secStructServices = secStructServices;
169 * flag controlling addition of any kind of structural annotation
171 * @return true if temperature factor annotation will be added
173 public boolean isAddTempFacAnnot()
175 return addTempFacAnnot;
179 * set flag controlling addition of structural annotation
181 * @param addTempFacAnnot
183 public void setAddTempFacAnnot(boolean addTempFacAnnot)
185 this.addTempFacAnnot = addTempFacAnnot;
190 * @return if true, the structure manager will attempt to add secondary
191 * structure lines for unannotated sequences
194 public boolean isProcessSecondaryStructure()
196 return processSecondaryStructure;
200 * Control whether structure manager will try to annotate mapped sequences
201 * with secondary structure from PDB data.
205 public void setProcessSecondaryStructure(boolean enable)
207 processSecondaryStructure = enable;
211 * debug function - write all mappings to stdout
213 public void reportMapping()
215 if (mappings.isEmpty())
217 System.err.println("reportMapping: No PDB/Sequence mappings.");
222 "reportMapping: There are " + mappings.size() + " mappings.");
224 for (StructureMapping sm : mappings)
226 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
232 * map between the PDB IDs (or structure identifiers) used by Jalview and the
233 * absolute filenames for PDB data that corresponds to it
235 Map<String, String> pdbIdFileName = new HashMap<>();
237 Map<String, String> pdbFileNameId = new HashMap<>();
239 public void registerPDBFile(String idForFile, String absoluteFile)
241 pdbIdFileName.put(idForFile, absoluteFile);
242 pdbFileNameId.put(absoluteFile, idForFile);
245 public String findIdForPDBFile(String idOrFile)
247 String id = pdbFileNameId.get(idOrFile);
251 public String findFileForPDBId(String idOrFile)
253 String id = pdbIdFileName.get(idOrFile);
257 public boolean isPDBFileRegistered(String idOrFile)
259 return pdbFileNameId.containsKey(idOrFile)
260 || pdbIdFileName.containsKey(idOrFile);
264 * flag controlling whether SeqMappings are relayed from received sequence
265 * mouse over events to other sequences
267 boolean relaySeqMappings = true;
270 * Enable or disable relay of seqMapping events to other sequences. You might
271 * want to do this if there are many sequence mappings and the host computer
276 public void setRelaySeqMappings(boolean relay)
278 relaySeqMappings = relay;
282 * get the state of the relay seqMappings flag.
284 * @return true if sequence mouse overs are being relayed to other mapped
287 public boolean isRelaySeqMappingsEnabled()
289 return relaySeqMappings;
292 Vector<Object> listeners = new Vector<>();
295 * register a listener for alignment sequence mouseover events
299 public void addStructureViewerListener(Object svl)
301 if (!listeners.contains(svl))
303 listeners.addElement(svl);
308 * Returns the filename the PDB id is already mapped to if known, or null if
314 public String alreadyMappedToFile(String pdbid)
316 for (StructureMapping sm : mappings)
318 if (sm.getPdbId().equalsIgnoreCase(pdbid))
327 * Import structure data and register a structure mapping for broadcasting
328 * colouring, mouseovers and selection events (convenience wrapper).
330 * This is the standard entry point.
333 * - one or more sequences to be mapped to pdbFile
334 * @param targetChains
335 * - optional chain specification for mapping each sequence to pdb
336 * (may be nill, individual elements may be nill)
338 * - structure data resource
340 * - how to resolve data from resource
341 * @return null or the structure data parsed as a pdb file
343 synchronized public StructureFile setMapping(SequenceI[] sequence,
344 String[] targetChains, String pdbFile, DataSourceType protocol,
345 IProgressIndicator progress)
347 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
352 * Import a single structure file and register sequence structure mappings for
353 * broadcasting colouring, mouseovers and selection events (convenience
358 * @param forStructureView
359 * when true (testng only), record the mapping for use in mouseOvers
362 * - one or more sequences to be mapped to pdbFile
363 * @param targetChains
364 * - optional chain specification for mapping each sequence to pdb
365 * (may be nill, individual elements may be nill)
367 * - structure data resource
369 * - how to resolve data from resource
370 * @return null or the structure data parsed as a pdb file
372 synchronized public StructureFile setMapping(boolean forStructureView,
373 SequenceI[] sequenceArray, String[] targetChainIds,
374 String pdbFile, DataSourceType sourceType)
376 return computeMapping(forStructureView, sequenceArray, targetChainIds,
377 pdbFile, sourceType, null);
381 * create sequence structure mappings between each sequence and the given
382 * pdbFile (retrieved via the given protocol). Either constructs a mapping
383 * using NW alignment or derives one from any available SIFTS mapping data.
385 * @param forStructureView
386 * when true, record the mapping for use in mouseOvers
388 * @param sequenceArray
389 * - one or more sequences to be mapped to pdbFile
390 * @param targetChainIds
391 * - optional chain specification for mapping each sequence to pdb
392 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
393 * - this should be List<List<String>>, empty lists indicate no
394 * predefined mappings
396 * - structure data resource
398 * - how to resolve data from resource
399 * @param IProgressIndicator
400 * reference to UI component that maintains a progress bar for the
402 * @return null or the structure data parsed as a pdb file
404 synchronized private StructureFile computeMapping(
405 boolean forStructureView, SequenceI[] sequenceArray,
406 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
407 IProgressIndicator progress)
409 long progressSessionId = System.currentTimeMillis() * 3;
412 * do we extract and transfer annotation from 3D data ?
414 // FIXME: possibly should just delete
416 boolean parseSecStr = processSecondaryStructure
417 ? isStructureFileProcessed(pdbFile, sequenceArray)
420 StructureFile pdb = null;
421 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
424 // FIXME if sourceType is not null, we've lost data here
425 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
426 pdb = new JmolParser(false, pdbFile, sourceType);
427 pdb.addSettings(parseSecStr && processSecondaryStructure,
428 parseSecStr && addTempFacAnnot,
429 parseSecStr && secStructServices);
431 if (pdb.getId() != null && pdb.getId().trim().length() > 0
432 && DataSourceType.FILE == sourceType)
434 registerPDBFile(pdb.getId().trim(), pdbFile);
436 // if PDBId is unavailable then skip SIFTS mapping execution path
437 // TODO: JAL-3868 need to know if structure is actually from
438 // PDB (has valid PDB ID and has provenance suggesting it
439 // actually came from PDB)
440 boolean isProtein = false;
441 for (SequenceI s:sequenceArray) {
447 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() && !pdb.getId().startsWith("AF-") && isProtein;
449 } catch (Exception ex)
451 ex.printStackTrace();
455 * sifts client - non null if SIFTS mappings are to be used
457 SiftsClient siftsClient = null;
462 siftsClient = new SiftsClient(pdb);
464 } catch (SiftsException e)
466 isMapUsingSIFTs = false;
467 Cache.log.error("SIFTS mapping failed", e);
468 Cache.log.error("Falling back on Needleman & Wunsch alignment");
472 String targetChainId;
473 for (int s = 0; s < sequenceArray.length; s++)
475 boolean infChain = true;
476 final SequenceI seq = sequenceArray[s];
478 while (ds.getDatasetSequence() != null)
480 ds = ds.getDatasetSequence();
483 if (targetChainIds != null && targetChainIds[s] != null)
486 targetChainId = targetChainIds[s];
488 else if (seq.getName().indexOf("|") > -1)
490 targetChainId = seq.getName()
491 .substring(seq.getName().lastIndexOf("|") + 1);
492 if (targetChainId.length() > 1)
494 if (targetChainId.trim().length() == 0)
500 // not a valid chain identifier
511 * Attempt pairwise alignment of the sequence with each chain in the PDB,
512 * and remember the highest scoring chain
515 AlignSeq maxAlignseq = null;
516 String maxChainId = " ";
517 PDBChain maxChain = null;
518 boolean first = true;
519 for (PDBChain chain : pdb.getChains())
521 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
524 continue; // don't try to map chains don't match.
526 // TODO: correctly determine sequence type for mixed na/peptide
528 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
529 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
532 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
533 // as.calcScoreMatrix();
534 // as.traceAlignment();
536 if (first || as.maxscore > max
537 || (as.maxscore == max && chain.id.equals(targetChainId)))
543 maxChainId = chain.id;
546 if (maxChain == null)
551 if (sourceType == DataSourceType.PASTE)
553 pdbFile = "INLINE" + pdb.getId();
556 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
557 if (isMapUsingSIFTs && seq.isProtein())
559 if (progress!=null) {
560 progress.setProgressBar(MessageManager
561 .getString("status.obtaining_mapping_with_sifts"),
564 jalview.datamodel.Mapping sqmpping = maxAlignseq
565 .getMappingFromS1(false);
566 if (targetChainId != null && !targetChainId.trim().isEmpty())
568 StructureMapping siftsMapping;
571 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
572 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
573 seqToStrucMapping.add(siftsMapping);
574 maxChain.makeExactMapping(siftsMapping, seq);
575 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");
576 maxChain.transferResidueAnnotation(siftsMapping, null);
577 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
579 } catch (SiftsException e)
581 // fall back to NW alignment
582 Cache.log.error(e.getMessage());
583 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
584 targetChainId, maxChain, pdb, maxAlignseq);
585 seqToStrucMapping.add(nwMapping);
586 maxChain.makeExactMapping(maxAlignseq, seq);
587 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
590 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
591 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
596 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
597 for (PDBChain chain : pdb.getChains())
599 StructureMapping siftsMapping = null;
602 siftsMapping = getStructureMapping(seq,
603 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
605 foundSiftsMappings.add(siftsMapping);
606 chain.makeExactMapping(siftsMapping, seq);
607 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
609 chain.transferResidueAnnotation(siftsMapping, null);
610 } catch (SiftsException e)
612 System.err.println(e.getMessage());
618 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
619 System.err.println(e.getMessage());
622 if (!foundSiftsMappings.isEmpty())
624 seqToStrucMapping.addAll(foundSiftsMappings);
625 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
629 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
630 maxChainId, maxChain, pdb, maxAlignseq);
631 seqToStrucMapping.add(nwMapping);
632 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
634 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
635 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
641 if (progress != null)
643 progress.setProgressBar(MessageManager
644 .getString("status.obtaining_mapping_with_nw_alignment"),
647 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
648 maxChain, pdb, maxAlignseq);
649 seqToStrucMapping.add(nwMapping);
650 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
652 if (forStructureView)
654 for (StructureMapping sm : seqToStrucMapping)
656 addStructureMapping(sm); // not addAll!
659 if (progress != null)
661 progress.setProgressBar(null, progressSessionId);
668 * check if we need to extract secondary structure from given pdbFile and
669 * transfer to sequences
672 * @param sequenceArray
675 private boolean isStructureFileProcessed(String pdbFile,
676 SequenceI[] sequenceArray)
678 boolean parseSecStr = true;
679 if (isPDBFileRegistered(pdbFile))
681 for (SequenceI sq : sequenceArray)
684 while (ds.getDatasetSequence() != null)
686 ds = ds.getDatasetSequence();
688 if (ds.getAnnotation() != null)
690 for (AlignmentAnnotation ala : ds.getAnnotation())
692 // false if any annotation present from this structure
693 // JBPNote this fails for jmol/chimera view because the *file* is
694 // passed, not the structure data ID -
695 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
706 public void addStructureMapping(StructureMapping sm)
708 if (!mappings.contains(sm))
715 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
720 * @param targetChainId
726 * client for retrieval of SIFTS mappings for this structure
728 * @throws SiftsException
730 private StructureMapping getStructureMapping(SequenceI seq,
731 String pdbFile, String targetChainId, StructureFile pdb,
732 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
733 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
735 StructureMapping curChainMapping = siftsClient
736 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
739 PDBChain chain = pdb.findChain(targetChainId);
742 chain.transferResidueAnnotation(curChainMapping, null);
744 } catch (Exception e)
748 return curChainMapping;
751 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
752 String maxChainId, PDBChain maxChain, StructureFile pdb,
753 AlignSeq maxAlignseq)
755 final StringBuilder mappingDetails = new StringBuilder(128);
756 mappingDetails.append(NEWLINE)
757 .append("Sequence \u27f7 Structure mapping details");
758 mappingDetails.append(NEWLINE);
760 .append("Method: inferred with Needleman & Wunsch alignment");
761 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
762 .append(NEWLINE).append("Sequence = ")
763 .append(maxChain.sequence.getSequenceAsString());
764 mappingDetails.append(NEWLINE).append("No of residues = ")
765 .append(maxChain.residues.size()).append(NEWLINE)
767 PrintStream ps = new PrintStream(System.out)
770 public void print(String x)
772 mappingDetails.append(x);
776 public void println()
778 mappingDetails.append(NEWLINE);
782 maxAlignseq.printAlignment(ps);
784 mappingDetails.append(NEWLINE).append("PDB start/end ");
785 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
787 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
788 mappingDetails.append(NEWLINE).append("SEQ start/end ");
791 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
793 mappingDetails.append(
794 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
795 mappingDetails.append(NEWLINE);
796 maxChain.makeExactMapping(maxAlignseq, seq);
797 jalview.datamodel.Mapping sqmpping = maxAlignseq
798 .getMappingFromS1(false);
799 maxChain.transferRESNUMFeatures(seq, null);
801 HashMap<Integer, int[]> mapping = new HashMap<>();
808 Atom tmp = maxChain.atoms.elementAt(index);
809 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
810 && tmp.alignmentMapping != -1)
812 resNum = tmp.resNumber;
813 insCode = tmp.insCode;
814 if (tmp.alignmentMapping >= -1)
816 mapping.put(tmp.alignmentMapping + 1,
818 { tmp.resNumber, tmp.atomIndex });
823 } while (index < maxChain.atoms.size());
825 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
826 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
827 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
831 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
833 listeners.removeElement(svl);
834 if (svl instanceof SequenceListener)
836 for (int i = 0; i < listeners.size(); i++)
838 if (listeners.elementAt(i) instanceof StructureListener)
840 ((StructureListener) listeners.elementAt(i))
841 .releaseReferences(svl);
846 if (pdbfiles == null)
852 * Remove mappings to the closed listener's PDB files, but first check if
853 * another listener is still interested
855 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
857 StructureListener sl;
858 for (int i = 0; i < listeners.size(); i++)
860 if (listeners.elementAt(i) instanceof StructureListener)
862 sl = (StructureListener) listeners.elementAt(i);
863 for (String pdbfile : sl.getStructureFiles())
865 pdbs.remove(pdbfile);
871 * Rebuild the mappings set, retaining only those which are for 'other' PDB
876 List<StructureMapping> tmp = new ArrayList<>();
877 for (StructureMapping sm : mappings)
879 if (!pdbs.contains(sm.pdbfile))
890 * Propagate mouseover of a single position in a structure
897 public String mouseOverStructure(int pdbResNum, String chain,
900 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
901 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
902 return mouseOverStructure(atoms);
906 * Propagate mouseover or selection of multiple positions in a structure
910 public String mouseOverStructure(List<AtomSpec> atoms)
912 if (listeners == null)
914 // old or prematurely sent event
917 boolean hasSequenceListener = false;
918 for (int i = 0; i < listeners.size(); i++)
920 if (listeners.elementAt(i) instanceof SequenceListener)
922 hasSequenceListener = true;
925 if (!hasSequenceListener)
930 SearchResultsI results = findAlignmentPositionsForStructurePositions(
932 String result = null;
933 for (Object li : listeners)
935 if (li instanceof SequenceListener)
937 String s = ((SequenceListener) li).highlightSequence(results);
948 * Constructs a SearchResults object holding regions (if any) in the Jalview
949 * alignment which have a mapping to the structure viewer positions in the
955 public SearchResultsI findAlignmentPositionsForStructurePositions(
956 List<AtomSpec> atoms)
958 SearchResultsI results = new SearchResults();
959 for (AtomSpec atom : atoms)
961 SequenceI lastseq = null;
963 for (StructureMapping sm : mappings)
965 if (sm.pdbfile.equals(atom.getPdbFile())
966 && sm.pdbchain.equals(atom.getChain()))
968 int indexpos = sm.getSeqPos(atom.getPdbResNum());
969 if (lastipos != indexpos || lastseq != sm.sequence)
971 results.addResult(sm.sequence, indexpos, indexpos);
973 lastseq = sm.sequence;
974 // construct highlighted sequence list
975 for (AlignedCodonFrame acf : seqmappings)
977 acf.markMappedRegion(sm.sequence, indexpos, results);
987 * highlight regions associated with a position (indexpos) in seq
990 * the sequence that the mouse over occurred on
992 * the absolute position being mouseovered in seq (0 to seq.length())
994 * the sequence position (if -1, seq.findPosition is called to
995 * resolve the residue number)
997 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1000 boolean hasSequenceListeners = handlingVamsasMo
1001 || !seqmappings.isEmpty();
1002 SearchResultsI results = null;
1005 seqPos = seq.findPosition(indexpos);
1007 for (int i = 0; i < listeners.size(); i++)
1009 Object listener = listeners.elementAt(i);
1010 if (listener == source)
1012 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1013 // Temporary fudge with SequenceListener.getVamsasSource()
1016 if (listener instanceof StructureListener)
1018 highlightStructure((StructureListener) listener, seq, seqPos);
1022 if (listener instanceof SequenceListener)
1024 final SequenceListener seqListener = (SequenceListener) listener;
1025 if (hasSequenceListeners
1026 && seqListener.getVamsasSource() != source)
1028 if (relaySeqMappings)
1030 if (results == null)
1032 results = MappingUtils.buildSearchResults(seq, seqPos,
1035 if (handlingVamsasMo)
1037 results.addResult(seq, seqPos, seqPos);
1040 if (!results.isEmpty())
1042 seqListener.highlightSequence(results);
1047 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1049 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1052 else if (listener instanceof SecondaryStructureListener)
1054 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1062 * Send suitable messages to a StructureListener to highlight atoms
1063 * corresponding to the given sequence position(s)
1069 public void highlightStructure(StructureListener sl, SequenceI seq,
1072 if (!sl.isListeningFor(seq))
1077 List<AtomSpec> atoms = new ArrayList<>();
1078 for (StructureMapping sm : mappings)
1080 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1081 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1082 .getDatasetSequence() == seq.getDatasetSequence()))
1084 for (int index : positions)
1086 atomNo = sm.getAtomNum(index);
1090 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1091 sm.getPDBResNum(index), atomNo));
1096 sl.highlightAtoms(atoms);
1100 * true if a mouse over event from an external (ie Vamsas) source is being
1103 boolean handlingVamsasMo = false;
1108 * as mouseOverSequence but only route event to SequenceListeners
1112 * in an alignment sequence
1114 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1115 VamsasSource source)
1117 handlingVamsasMo = true;
1118 long msg = sequenceI.hashCode() * (1 + position);
1122 mouseOverSequence(sequenceI, position, -1, source);
1124 handlingVamsasMo = false;
1127 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1131 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1132 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1134 * Annotation [] annotations = new Annotation[seq.getLength()];
1136 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1137 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1138 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1140 * for (int j = 0; j < mappings.length; j++) {
1142 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1143 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1144 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1145 * "+mappings[j].pdbfile);
1147 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1148 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1150 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1151 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1152 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1153 * mappings[j].pdbfile); }
1155 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1156 * annotations; } } } }
1158 * return annotations;
1162 public void structureSelectionChanged()
1166 public void sequenceSelectionChanged()
1170 public void sequenceColoursChanged(Object source)
1172 StructureListener sl;
1173 for (int i = 0; i < listeners.size(); i++)
1175 if (listeners.elementAt(i) instanceof StructureListener)
1177 sl = (StructureListener) listeners.elementAt(i);
1178 sl.updateColours(source);
1183 public StructureMapping[] getMapping(String pdbfile)
1185 List<StructureMapping> tmp = new ArrayList<>();
1186 for (StructureMapping sm : mappings)
1188 if (sm.pdbfile.equals(pdbfile))
1193 return tmp.toArray(new StructureMapping[tmp.size()]);
1197 * Returns a readable description of all mappings for the given pdbfile to any
1198 * of the given sequences
1204 public String printMappings(String pdbfile, List<SequenceI> seqs)
1206 if (pdbfile == null || seqs == null || seqs.isEmpty())
1211 StringBuilder sb = new StringBuilder(64);
1212 for (StructureMapping sm : mappings)
1214 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1215 && seqs.contains(sm.sequence))
1217 sb.append(sm.mappingDetails);
1219 // separator makes it easier to read multiple mappings
1220 sb.append("=====================");
1226 return sb.toString();
1230 * Remove the given mapping
1234 public void deregisterMapping(AlignedCodonFrame acf)
1238 boolean removed = seqmappings.remove(acf);
1239 if (removed && seqmappings.isEmpty())
1241 System.out.println("All mappings removed");
1247 * Add each of the given codonFrames to the stored set, if not aready present.
1251 public void registerMappings(List<AlignedCodonFrame> mappings)
1253 if (mappings != null)
1255 for (AlignedCodonFrame acf : mappings)
1257 registerMapping(acf);
1263 * Add the given mapping to the stored set, unless already stored.
1265 public void registerMapping(AlignedCodonFrame acf)
1269 if (!seqmappings.contains(acf))
1271 seqmappings.add(acf);
1277 * Reset this object to its initial state by removing all registered
1278 * listeners, codon mappings, PDB file mappings.
1280 * Called only by Desktop and testng.
1283 public void resetAll()
1285 if (mappings != null)
1289 if (seqmappings != null)
1291 seqmappings.clear();
1293 if (sel_listeners != null)
1295 sel_listeners.clear();
1297 if (listeners != null)
1301 if (commandListeners != null)
1303 commandListeners.clear();
1305 if (view_listeners != null)
1307 view_listeners.clear();
1309 if (pdbFileNameId != null)
1311 pdbFileNameId.clear();
1313 if (pdbIdFileName != null)
1315 pdbIdFileName.clear();
1319 public List<SelectionListener> getListeners() {
1320 return sel_listeners;
1323 public void addSelectionListener(SelectionListener selecter)
1325 if (!sel_listeners.contains(selecter))
1327 sel_listeners.add(selecter);
1331 public void removeSelectionListener(SelectionListener toremove)
1333 if (sel_listeners.contains(toremove))
1335 sel_listeners.remove(toremove);
1339 public synchronized void sendSelection(
1340 jalview.datamodel.SequenceGroup selection,
1341 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1342 SelectionSource source)
1344 for (SelectionListener slis : sel_listeners)
1348 slis.selection(selection, colsel, hidden, source);
1353 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1355 public synchronized void sendViewPosition(
1356 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1357 int startSeq, int endSeq)
1360 if (view_listeners != null && view_listeners.size() > 0)
1362 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1364 while (listeners.hasMoreElements())
1366 AlignmentViewPanelListener slis = listeners.nextElement();
1369 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1378 * Removes the instance associated with this provider
1383 public static void release(StructureSelectionManagerProvider provider)
1385 getInstance().selectionManagers.remove(provider);
1388 public void registerPDBEntry(PDBEntry pdbentry)
1390 if (pdbentry.getFile() != null
1391 && pdbentry.getFile().trim().length() > 0)
1393 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1397 public void addCommandListener(CommandListener cl)
1399 if (!commandListeners.contains(cl))
1401 commandListeners.add(cl);
1405 public boolean hasCommandListener(CommandListener cl)
1407 return this.commandListeners.contains(cl);
1410 public boolean removeCommandListener(CommandListener l)
1412 return commandListeners.remove(l);
1416 * Forward a command to any command listeners (except for the command's
1420 * the command to be broadcast (in its form after being performed)
1422 * if true, the command was being 'undone'
1425 public void commandPerformed(CommandI command, boolean undo,
1426 VamsasSource source)
1428 for (CommandListener listener : commandListeners)
1430 listener.mirrorCommand(command, undo, this, source);
1435 * Returns a new CommandI representing the given command as mapped to the
1436 * given sequences. If no mapping could be made, or the command is not of a
1437 * mappable kind, returns null.
1445 public CommandI mapCommand(CommandI command, boolean undo,
1446 final AlignmentI mapTo, char gapChar)
1448 if (command instanceof EditCommand)
1450 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1451 gapChar, seqmappings);
1453 else if (command instanceof OrderCommand)
1455 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1456 mapTo, seqmappings);
1461 public List<AlignedCodonFrame> getSequenceMappings()