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