JAL-3390 pull up of getShownResidues() to AAStructureBindingModel
[jalview.git] / src / jalview / structures / models / AAStructureBindingModel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
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.
11  *  
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.
16  * 
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.
20  */
21 package jalview.structures.models;
22
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.SequenceRenderer;
26 import jalview.api.StructureSelectionManagerProvider;
27 import jalview.api.structures.JalviewStructureDisplayI;
28 import jalview.datamodel.AlignmentI;
29 import jalview.datamodel.HiddenColumns;
30 import jalview.datamodel.PDBEntry;
31 import jalview.datamodel.SequenceI;
32 import jalview.ext.rbvi.chimera.AtomSpecModel;
33 import jalview.io.DataSourceType;
34 import jalview.schemes.ColourSchemeI;
35 import jalview.structure.AtomSpec;
36 import jalview.structure.StructureListener;
37 import jalview.structure.StructureMapping;
38 import jalview.structure.StructureMappingcommandSet;
39 import jalview.structure.StructureSelectionManager;
40 import jalview.util.Comparison;
41 import jalview.util.MessageManager;
42
43 import java.awt.Color;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.BitSet;
47 import java.util.Collections;
48 import java.util.Iterator;
49 import java.util.List;
50
51 /**
52  * 
53  * A base class to hold common function for protein structure model binding.
54  * Initial version created by refactoring JMol and Chimera binding models, but
55  * other structure viewers could in principle be accommodated in future.
56  * 
57  * @author gmcarstairs
58  *
59  */
60 public abstract class AAStructureBindingModel
61         extends SequenceStructureBindingModel
62         implements StructureListener, StructureSelectionManagerProvider
63 {
64
65   private StructureSelectionManager ssm;
66
67   /*
68    * distinct PDB entries (pdb files) associated
69    * with sequences
70    */
71   private PDBEntry[] pdbEntry;
72
73   /*
74    * sequences mapped to each pdbentry
75    */
76   private SequenceI[][] sequence;
77
78   /*
79    * array of target chains for sequences - tied to pdbentry and sequence[]
80    */
81   private String[][] chains;
82
83   /*
84    * datasource protocol for access to PDBEntrylatest
85    */
86   DataSourceType protocol = null;
87
88   protected boolean colourBySequence = true;
89
90   private boolean nucleotide;
91
92   private boolean finishedInit = false;
93
94   /**
95    * current set of model filenames loaded in the Jmol instance
96    */
97   protected String[] modelFileNames = null;
98
99   public String fileLoadingError;
100
101   private boolean showAlignmentOnly;
102
103   /*
104    * a list of chains "pdbid:chainid" to show in the viewer;
105    * empty means show all
106    */
107   // TODO make private once showStructures() deals with this
108   protected List<String> chainsToShow;
109
110   private boolean hideHiddenRegions;
111
112   /**
113    * Data bean class to simplify parameterisation in superposeStructures
114    */
115   protected class SuperposeData
116   {
117     /**
118      * Constructor with alignment width argument
119      * 
120      * @param width
121      */
122     public SuperposeData(int width)
123     {
124       pdbResNo = new int[width];
125     }
126
127     public String filename;
128
129     public String pdbId;
130
131     public String chain = "";
132
133     public boolean isRna;
134
135     /*
136      * The pdb residue number (if any) mapped to each column of the alignment
137      */
138     public int[] pdbResNo;
139   }
140
141   /**
142    * Constructor
143    * 
144    * @param ssm
145    * @param seqs
146    */
147   public AAStructureBindingModel(StructureSelectionManager ssm,
148           SequenceI[][] seqs)
149   {
150     this.ssm = ssm;
151     this.sequence = seqs;
152     chainsToShow = new ArrayList<>();
153   }
154
155   /**
156    * Constructor
157    * 
158    * @param ssm
159    * @param pdbentry
160    * @param sequenceIs
161    * @param protocol
162    */
163   public AAStructureBindingModel(StructureSelectionManager ssm,
164           PDBEntry[] pdbentry, SequenceI[][] sequenceIs,
165           DataSourceType protocol)
166   {
167     this.ssm = ssm;
168     this.sequence = sequenceIs;
169     this.nucleotide = Comparison.isNucleotide(sequenceIs);
170     this.pdbEntry = pdbentry;
171     this.protocol = protocol;
172     chainsToShow = new ArrayList<>();
173
174     resolveChains();
175   }
176
177   private boolean resolveChains()
178   {
179     /**
180      * final count of chain mappings discovered
181      */
182     int chainmaps = 0;
183     // JBPNote: JAL-2693 - this should be a list of chain mappings per
184     // [pdbentry][sequence]
185     String[][] newchains = new String[pdbEntry.length][];
186     int pe = 0;
187     for (PDBEntry pdb : pdbEntry)
188     {
189       SequenceI[] seqsForPdb = sequence[pe];
190       if (seqsForPdb != null)
191       {
192         newchains[pe] = new String[seqsForPdb.length];
193         int se = 0;
194         for (SequenceI asq : seqsForPdb)
195         {
196           String chain = (chains != null && chains[pe] != null)
197                   ? chains[pe][se]
198                   : null;
199           SequenceI sq = (asq.getDatasetSequence() == null) ? asq
200                   : asq.getDatasetSequence();
201           if (sq.getAllPDBEntries() != null)
202           {
203             for (PDBEntry pdbentry : sq.getAllPDBEntries())
204             {
205               if (pdb.getFile() != null && pdbentry.getFile() != null
206                       && pdb.getFile().equals(pdbentry.getFile()))
207               {
208                 String chaincode = pdbentry.getChainCode();
209                 if (chaincode != null && chaincode.length() > 0)
210                 {
211                   chain = chaincode;
212                   chainmaps++;
213                   break;
214                 }
215               }
216             }
217           }
218           newchains[pe][se] = chain;
219           se++;
220         }
221         pe++;
222       }
223     }
224
225     chains = newchains;
226     return chainmaps > 0;
227   }
228   public StructureSelectionManager getSsm()
229   {
230     return ssm;
231   }
232
233   /**
234    * Returns the i'th PDBEntry (or null)
235    * 
236    * @param i
237    * @return
238    */
239   public PDBEntry getPdbEntry(int i)
240   {
241     return (pdbEntry != null && pdbEntry.length > i) ? pdbEntry[i] : null;
242   }
243
244   /**
245    * Answers true if this binding includes the given PDB id, else false
246    * 
247    * @param pdbId
248    * @return
249    */
250   public boolean hasPdbId(String pdbId)
251   {
252     if (pdbEntry != null)
253     {
254       for (PDBEntry pdb : pdbEntry)
255       {
256         if (pdb.getId().equals(pdbId))
257         {
258           return true;
259         }
260       }
261     }
262     return false;
263   }
264
265   /**
266    * Returns the number of modelled PDB file entries.
267    * 
268    * @return
269    */
270   public int getPdbCount()
271   {
272     return pdbEntry == null ? 0 : pdbEntry.length;
273   }
274
275   public SequenceI[][] getSequence()
276   {
277     return sequence;
278   }
279
280   public String[][] getChains()
281   {
282     return chains;
283   }
284
285   public DataSourceType getProtocol()
286   {
287     return protocol;
288   }
289
290   // TODO may remove this if calling methods can be pulled up here
291   protected void setPdbentry(PDBEntry[] pdbentry)
292   {
293     this.pdbEntry = pdbentry;
294   }
295
296   protected void setSequence(SequenceI[][] sequence)
297   {
298     this.sequence = sequence;
299   }
300
301   protected void setChains(String[][] chains)
302   {
303     this.chains = chains;
304   }
305
306   /**
307    * Construct a title string for the viewer window based on the data Jalview
308    * knows about
309    * 
310    * @param viewerName
311    *          TODO
312    * @param verbose
313    * 
314    * @return
315    */
316   public String getViewerTitle(String viewerName, boolean verbose)
317   {
318     if (getSequence() == null || getSequence().length < 1
319             || getPdbCount() < 1 || getSequence()[0].length < 1)
320     {
321       return ("Jalview " + viewerName + " Window");
322     }
323     // TODO: give a more informative title when multiple structures are
324     // displayed.
325     StringBuilder title = new StringBuilder(64);
326     final PDBEntry pdbe = getPdbEntry(0);
327     title.append(viewerName + " view for " + getSequence()[0][0].getName()
328             + ":" + pdbe.getId());
329
330     if (verbose)
331     {
332       String method = (String) pdbe.getProperty("method");
333       if (method != null)
334       {
335         title.append(" Method: ").append(method);
336       }
337       String chain = (String) pdbe.getProperty("chains");
338       if (chain != null)
339       {
340         title.append(" Chain:").append(chain);
341       }
342     }
343     return title.toString();
344   }
345
346   /**
347    * Called by after closeViewer is called, to release any resources and
348    * references so they can be garbage collected. Override if needed.
349    */
350   protected void releaseUIResources()
351   {
352
353   }
354
355   public boolean isColourBySequence()
356   {
357     return colourBySequence;
358   }
359
360   public void setColourBySequence(boolean colourBySequence)
361   {
362     this.colourBySequence = colourBySequence;
363   }
364
365   protected void addSequenceAndChain(int pe, SequenceI[] seq,
366           String[] tchain)
367   {
368     if (pe < 0 || pe >= getPdbCount())
369     {
370       throw new Error(MessageManager.formatMessage(
371               "error.implementation_error_no_pdbentry_from_index",
372               new Object[]
373               { Integer.valueOf(pe).toString() }));
374     }
375     final String nullChain = "TheNullChain";
376     List<SequenceI> s = new ArrayList<>();
377     List<String> c = new ArrayList<>();
378     if (getChains() == null)
379     {
380       setChains(new String[getPdbCount()][]);
381     }
382     if (getSequence()[pe] != null)
383     {
384       for (int i = 0; i < getSequence()[pe].length; i++)
385       {
386         s.add(getSequence()[pe][i]);
387         if (getChains()[pe] != null)
388         {
389           if (i < getChains()[pe].length)
390           {
391             c.add(getChains()[pe][i]);
392           }
393           else
394           {
395             c.add(nullChain);
396           }
397         }
398         else
399         {
400           if (tchain != null && tchain.length > 0)
401           {
402             c.add(nullChain);
403           }
404         }
405       }
406     }
407     for (int i = 0; i < seq.length; i++)
408     {
409       if (!s.contains(seq[i]))
410       {
411         s.add(seq[i]);
412         if (tchain != null && i < tchain.length)
413         {
414           c.add(tchain[i] == null ? nullChain : tchain[i]);
415         }
416       }
417     }
418     SequenceI[] tmp = s.toArray(new SequenceI[s.size()]);
419     getSequence()[pe] = tmp;
420     if (c.size() > 0)
421     {
422       String[] tch = c.toArray(new String[c.size()]);
423       for (int i = 0; i < tch.length; i++)
424       {
425         if (tch[i] == nullChain)
426         {
427           tch[i] = null;
428         }
429       }
430       getChains()[pe] = tch;
431     }
432     else
433     {
434       getChains()[pe] = null;
435     }
436   }
437
438   /**
439    * add structures and any known sequence associations
440    * 
441    * @returns the pdb entries added to the current set.
442    */
443   public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe,
444           SequenceI[][] seq, String[][] chns)
445   {
446     List<PDBEntry> v = new ArrayList<>();
447     List<int[]> rtn = new ArrayList<>();
448     for (int i = 0; i < getPdbCount(); i++)
449     {
450       v.add(getPdbEntry(i));
451     }
452     for (int i = 0; i < pdbe.length; i++)
453     {
454       int r = v.indexOf(pdbe[i]);
455       if (r == -1 || r >= getPdbCount())
456       {
457         rtn.add(new int[] { v.size(), i });
458         v.add(pdbe[i]);
459       }
460       else
461       {
462         // just make sure the sequence/chain entries are all up to date
463         addSequenceAndChain(r, seq[i], chns[i]);
464       }
465     }
466     pdbe = v.toArray(new PDBEntry[v.size()]);
467     setPdbentry(pdbe);
468     if (rtn.size() > 0)
469     {
470       // expand the tied sequence[] and string[] arrays
471       SequenceI[][] sqs = new SequenceI[getPdbCount()][];
472       String[][] sch = new String[getPdbCount()][];
473       System.arraycopy(getSequence(), 0, sqs, 0, getSequence().length);
474       System.arraycopy(getChains(), 0, sch, 0, this.getChains().length);
475       setSequence(sqs);
476       setChains(sch);
477       pdbe = new PDBEntry[rtn.size()];
478       for (int r = 0; r < pdbe.length; r++)
479       {
480         int[] stri = (rtn.get(r));
481         // record the pdb file as a new addition
482         pdbe[r] = getPdbEntry(stri[0]);
483         // and add the new sequence/chain entries
484         addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]);
485       }
486     }
487     else
488     {
489       pdbe = null;
490     }
491     return pdbe;
492   }
493
494   /**
495    * Add sequences to the pe'th pdbentry's sequence set.
496    * 
497    * @param pe
498    * @param seq
499    */
500   public void addSequence(int pe, SequenceI[] seq)
501   {
502     addSequenceAndChain(pe, seq, null);
503   }
504
505   /**
506    * add the given sequences to the mapping scope for the given pdb file handle
507    * 
508    * @param pdbFile
509    *          - pdbFile identifier
510    * @param seq
511    *          - set of sequences it can be mapped to
512    */
513   public void addSequenceForStructFile(String pdbFile, SequenceI[] seq)
514   {
515     for (int pe = 0; pe < getPdbCount(); pe++)
516     {
517       if (getPdbEntry(pe).getFile().equals(pdbFile))
518       {
519         addSequence(pe, seq);
520       }
521     }
522   }
523
524   @Override
525   public abstract void highlightAtoms(List<AtomSpec> atoms);
526
527   protected boolean isNucleotide()
528   {
529     return this.nucleotide;
530   }
531
532   /**
533    * Returns a readable description of all mappings for the wrapped pdbfile to
534    * any mapped sequences
535    * 
536    * @param pdbfile
537    * @param seqs
538    * @return
539    */
540   public String printMappings()
541   {
542     if (pdbEntry == null)
543     {
544       return "";
545     }
546     StringBuilder sb = new StringBuilder(128);
547     for (int pdbe = 0; pdbe < getPdbCount(); pdbe++)
548     {
549       String pdbfile = getPdbEntry(pdbe).getFile();
550       List<SequenceI> seqs = Arrays.asList(getSequence()[pdbe]);
551       sb.append(getSsm().printMappings(pdbfile, seqs));
552     }
553     return sb.toString();
554   }
555
556   /**
557    * Returns the mapped structure position for a given aligned column of a given
558    * sequence, or -1 if the column is gapped, beyond the end of the sequence, or
559    * not mapped to structure.
560    * 
561    * @param seq
562    * @param alignedPos
563    * @param mapping
564    * @return
565    */
566   protected int getMappedPosition(SequenceI seq, int alignedPos,
567           StructureMapping mapping)
568   {
569     if (alignedPos >= seq.getLength())
570     {
571       return -1;
572     }
573
574     if (Comparison.isGap(seq.getCharAt(alignedPos)))
575     {
576       return -1;
577     }
578     int seqPos = seq.findPosition(alignedPos);
579     int pos = mapping.getPDBResNum(seqPos);
580     return pos;
581   }
582
583   /**
584    * Helper method to identify residues that can participate in a structure
585    * superposition command. For each structure, identify a sequence in the
586    * alignment which is mapped to the structure. Identify non-gapped columns in
587    * the sequence which have a mapping to a residue in the structure. Returns
588    * the index of the first structure that has a mapping to the alignment.
589    * 
590    * @param alignment
591    *          the sequence alignment which is the basis of structure
592    *          superposition
593    * @param matched
594    *          a BitSet, where bit j is set to indicate that every structure has
595    *          a mapped residue present in column j (so the column can
596    *          participate in structure alignment)
597    * @param structures
598    *          an array of data beans corresponding to pdb file index
599    * @return
600    */
601   protected int findSuperposableResidues(AlignmentI alignment,
602           BitSet matched, SuperposeData[] structures)
603   {
604     int refStructure = -1;
605     String[] files = getStructureFiles();
606     if (files == null)
607     {
608       return -1;
609     }
610     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
611     {
612       StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]);
613       int lastPos = -1;
614
615       /*
616        * Find the first mapped sequence (if any) for this PDB entry which is in
617        * the alignment
618        */
619       final int seqCountForPdbFile = getSequence()[pdbfnum].length;
620       for (int s = 0; s < seqCountForPdbFile; s++)
621       {
622         for (StructureMapping mapping : mappings)
623         {
624           final SequenceI theSequence = getSequence()[pdbfnum][s];
625           if (mapping.getSequence() == theSequence
626                   && alignment.findIndex(theSequence) > -1)
627           {
628             if (refStructure < 0)
629             {
630               refStructure = pdbfnum;
631             }
632             for (int r = 0; r < alignment.getWidth(); r++)
633             {
634               if (!matched.get(r))
635               {
636                 continue;
637               }
638               int pos = getMappedPosition(theSequence, r, mapping);
639               if (pos < 1 || pos == lastPos)
640               {
641                 matched.clear(r);
642                 continue;
643               }
644               lastPos = pos;
645               structures[pdbfnum].pdbResNo[r] = pos;
646             }
647             String chain = mapping.getChain();
648             if (chain != null && chain.trim().length() > 0)
649             {
650               structures[pdbfnum].chain = chain;
651             }
652             structures[pdbfnum].pdbId = mapping.getPdbId();
653             structures[pdbfnum].isRna = theSequence.getRNA() != null;
654
655             /*
656              * move on to next pdb file (ignore sequences for other chains
657              * for the same structure)
658              */
659             s = seqCountForPdbFile;
660             break;
661           }
662         }
663       }
664     }
665     return refStructure;
666   }
667
668   /**
669    * Returns true if the structure viewer has loaded all of the files of
670    * interest (identified by the file mapping having been set up), or false if
671    * any are still not loaded after a timeout interval.
672    * 
673    * @param files
674    */
675   protected boolean waitForFileLoad(String[] files)
676   {
677     /*
678      * give up after 10 secs plus 1 sec per file
679      */
680     long starttime = System.currentTimeMillis();
681     long endTime = 10000 + 1000 * files.length + starttime;
682     String notLoaded = null;
683
684     boolean waiting = true;
685     while (waiting && System.currentTimeMillis() < endTime)
686     {
687       waiting = false;
688       for (String file : files)
689       {
690         notLoaded = file;
691         if (file == null)
692         {
693           continue;
694         }
695         try
696         {
697           StructureMapping[] sm = getSsm().getMapping(file);
698           if (sm == null || sm.length == 0)
699           {
700             waiting = true;
701           }
702         } catch (Throwable x)
703         {
704           waiting = true;
705         }
706       }
707     }
708
709     if (waiting)
710     {
711       System.err.println(
712               "Timed out waiting for structure viewer to load file "
713                       + notLoaded);
714       return false;
715     }
716     return true;
717   }
718
719   @Override
720   public boolean isListeningFor(SequenceI seq)
721   {
722     if (sequence != null)
723     {
724       for (SequenceI[] seqs : sequence)
725       {
726         if (seqs != null)
727         {
728           for (SequenceI s : seqs)
729           {
730             if (s == seq || (s.getDatasetSequence() != null
731                     && s.getDatasetSequence() == seq.getDatasetSequence()))
732             {
733               return true;
734             }
735           }
736         }
737       }
738     }
739     return false;
740   }
741
742   public boolean isFinishedInit()
743   {
744     return finishedInit;
745   }
746
747   public void setFinishedInit(boolean fi)
748   {
749     this.finishedInit = fi;
750   }
751
752   /**
753    * Returns a list of chains mapped in this viewer.
754    * 
755    * @return
756    */
757   public abstract List<String> getChainNames();
758
759   /**
760    * Returns the Jalview panel hosting the structure viewer (if any)
761    * 
762    * @return
763    */
764   public JalviewStructureDisplayI getViewer()
765   {
766     return null;
767   }
768
769   public abstract void setJalviewColourScheme(ColourSchemeI cs);
770
771   /**
772    * Constructs and sends a command to align structures against a reference
773    * structure, based on one or more sequence alignments. May optionally return
774    * an error or warning message for the alignment command.
775    * 
776    * @param alignments
777    *          an array of alignments to process
778    * @param structureIndices
779    *          an array of corresponding reference structures (index into pdb
780    *          file array); if a negative value is passed, the first PDB file
781    *          mapped to an alignment sequence is used as the reference for
782    *          superposition
783    * @param hiddenCols
784    *          an array of corresponding hidden columns for each alignment
785    * @return
786    */
787   public abstract String superposeStructures(AlignmentI[] alignments,
788           int[] structureIndices, HiddenColumns[] hiddenCols);
789
790   public abstract void setBackgroundColour(Color col);
791
792   protected abstract StructureMappingcommandSet[] getColourBySequenceCommands(
793           String[] files, AlignmentViewPanel avp);
794
795   /**
796    * returns the current sequenceRenderer that should be used to colour the
797    * structures
798    * 
799    * @param alignment
800    * 
801    * @return
802    */
803   public abstract SequenceRenderer getSequenceRenderer(
804           AlignmentViewPanel alignment);
805
806   protected abstract void colourBySequence(
807           StructureMappingcommandSet[] colourBySequenceCommands);
808
809   public abstract void colourByChain();
810
811   public abstract void colourByCharge();
812
813   /**
814    * Recolours the displayed structures, if they are coloured by sequence, or
815    * 'show only visible alignment' is selected. This supports updating structure
816    * colours on either change of alignment colours, or change to the visible
817    * region of the alignment.
818    */
819   public void colourBySequence(AlignmentViewPanel alignmentv)
820   {
821     if (!isLoadingFinished())
822     {
823       return;
824     }
825
826     /*
827      * if structure is not coloured by sequence, but restricted to the alignment,
828      * then redraw it (but don't recolour it) in case hidden regions have changed
829      * (todo: specific messaging for change of hidden region only)
830      */
831     if (!colourBySequence)
832     {
833       if (isShowAlignmentOnly())
834       {
835         showStructures(alignmentv.getAlignViewport(), false);
836       }
837       return;
838     }
839     if (getSsm() == null)
840     {
841       return;
842     }
843     String[] files = getStructureFiles();
844
845     StructureMappingcommandSet[] colourBySequenceCommands = getColourBySequenceCommands(
846             files, alignmentv);
847     colourBySequence(colourBySequenceCommands);
848   }
849
850   public boolean hasFileLoadingError()
851   {
852     return fileLoadingError != null && fileLoadingError.length() > 0;
853   }
854
855   public abstract jalview.api.FeatureRenderer getFeatureRenderer(
856           AlignmentViewPanel alignment);
857
858   /**
859    * Sets the flag for whether only mapped visible residues in the alignment
860    * should be visible in the structure viewer
861    * 
862    * @param b
863    */
864   public void setShowAlignmentOnly(boolean b)
865   {
866     showAlignmentOnly = b;
867   }
868
869   /**
870    * Answers true if only residues mapped to the alignment should be shown in the
871    * structure viewer, else false
872    * 
873    * @return
874    */
875   public boolean isShowAlignmentOnly()
876   {
877     return showAlignmentOnly;
878   }
879
880   /**
881    * Sets the flag for hiding regions of structure which are hidden in the
882    * alignment (only applies when the structure viewer is restricted to the
883    * alignment only)
884    * 
885    * @param b
886    */
887   public void setHideHiddenRegions(boolean b)
888   {
889     hideHiddenRegions = b;
890   }
891
892   /**
893    * Answers true if regions hidden in the alignment should also be hidden in the
894    * structure viewer, else false (only applies when the structure viewer is
895    * restricted to the alignment only)
896    * 
897    * @return
898    */
899   public boolean isHideHiddenRegions()
900   {
901     return hideHiddenRegions;
902   }
903
904   /**
905    * Shows the structures in the viewer, without changing their colouring. This is
906    * to support toggling of whether the whole structure is shown, or only residues
907    * mapped to visible regions of the alignment.
908    * 
909    * @param alignViewportI
910    * @param refocus
911    *                         if true, refit the display to the viewer
912    */
913   public void showStructures(AlignViewportI alignViewportI, boolean refocus)
914   {
915     // override with implementation
916   }
917
918   @Override
919   public void updateColours(Object source)
920   {
921     AlignmentViewPanel ap = (AlignmentViewPanel) source;
922     // ignore events from panels not used to colour this view
923     if (!getViewer().isUsedforcolourby(ap))
924     {
925       return;
926     }
927     if (!isLoadingFromArchive())
928     {
929       colourBySequence(ap);
930     }
931   }
932
933   /**
934    * Sets the list of chains to display (as "pdbid:chain"), where an empty list
935    * means show all
936    * 
937    * @param chains
938    */
939   public void setChainsToShow(List<String> chains)
940   {
941     chainsToShow = chains;
942   }
943
944   /**
945    * Answers true if the specified structure and chain are selected to be shown in
946    * the viewer, else false
947    * 
948    * @param pdbId
949    * @param chainId
950    * @return
951    */
952   protected boolean isShowChain(String pdbId, String chainId)
953   {
954     if (chainsToShow.isEmpty())
955     {
956       return true;
957     }
958     return chainsToShow.contains(pdbId + ":" + chainId);
959   }
960
961   @Override
962   public abstract String[] getStructureFiles();
963
964   /**
965    * Builds a model of residues mapped from sequences to show on structure, taking
966    * into account user choices of
967    * <ul>
968    * <li>which chains are shown</li>
969    * <li>whether all structure is shown, or only that mapped to the alignment</li>
970    * <li>whether hidden regions of the alignment are hidden (excluded) or grayed
971    * out (included)</li>
972    * </ul>
973    * 
974    * @param av
975    * @return
976    */
977   protected AtomSpecModel getShownResidues(AlignViewportI av)
978   {
979     AlignmentI alignment = av.getAlignment();
980     final int width = alignment.getWidth();
981   
982     String[] files = getStructureFiles();
983   
984     AtomSpecModel model = new AtomSpecModel();
985   
986     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
987     {
988       StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]);
989   
990       /*
991        * Find the first mapped sequence (if any) for this PDB entry which is in
992        * the alignment
993        */
994       final int seqCountForPdbFile = getSequence()[pdbfnum].length;
995       for (int s = 0; s < seqCountForPdbFile; s++)
996       {
997         for (StructureMapping mapping : mappings)
998         {
999           final SequenceI theSequence = getSequence()[pdbfnum][s];
1000           if (mapping.getSequence() == theSequence
1001                   && alignment.findIndex(theSequence) > -1)
1002           {
1003             String chainCd = mapping.getChain();
1004             if (!isShowChain(mapping.getPdbId(), chainCd))
1005             {
1006               continue;
1007             }
1008             Iterator<int[]> visible;
1009             if (isShowAlignmentOnly() && isHideHiddenRegions())
1010             {
1011               visible = alignment.getHiddenColumns()
1012                     .getVisContigsIterator(0, width, true);
1013             }
1014             else
1015             {
1016               visible = Collections.singletonList(new int[] { 0, width })
1017                       .iterator();
1018             }
1019             while (visible.hasNext())
1020             {
1021               int[] visibleRegion = visible.next();
1022               int seqStartPos = theSequence.findPosition(visibleRegion[0]);
1023               int seqEndPos = theSequence.findPosition(visibleRegion[1]);
1024               List<int[]> residueRanges = mapping
1025                       .getPDBResNumRanges(seqStartPos, seqEndPos);
1026               if (!residueRanges.isEmpty())
1027               {
1028                 for (int[] range : residueRanges)
1029                 {
1030                   model.addRange(pdbfnum, range[0], range[1], chainCd);
1031                 }
1032               }
1033             }
1034           }
1035         }
1036       }
1037     }
1038   
1039     return model;
1040   }
1041
1042   /**
1043    * Answers a default structure model specification which is simply the string
1044    * form of the model number. Override if needed to specify submodels.
1045    * 
1046    * @param model
1047    * @return
1048    */
1049   public String getModelSpec(int model)
1050   {
1051     return String.valueOf(model);
1052   }
1053 }